Chapter 7 Producer

import requests

response = requests.get("https://www.dropbox.com/s/cyp43ve6wyqnof1/consumers.py?dl=1")
output_file_object = open("consumers.py", "w")
output_file_object.writelines(response.text)
output_file_object.close()

from pathlib import Path
currentPath = Path()
if currentPath.cwd() not in sys.path:
    sys.path.append(currentPath.cwd())

import consumers as cs
from numpy.random import default_rng
rng_alpha = default_rng(2020)
rng_beta = default_rng(3892)

size = 500
consumers500 = [
    cs.Consumer(alpha, beta) for alpha, beta in zip(rng_alpha.uniform(300,500,size), rng_beta.uniform(5,30,size))
]
  • It is wise to close a file object when necessary operations are finished.

7.1 Monopoly

Suppose a monopoly has a constant marginal cost (mc) with no fixed cost. The producer sees market demand (i.e. aggregate demand, Qd), and decides market price. The quantity he can sell at a given price level is Qd(p)

class Monopoly:
    def __init__(self, mc, Qd):
        self.mc=mc
        self.Qd=Qd
    
    def cost(self, p):
        return self.mc*self.Qd(p)
    def revenue(self, p):
        return p*self.Qd(p)
    def profit(self, p):
        return p*self.Qd(p) - self.mc *self.Qd(p)
  • A monopoly not only knows his own cost of production, but also knows exactly market demend (Qd).

7.1.1 Market demand

import numpy as np
def Qd(p):
    return np.maximum(np.zeros(len(p)),100-2*p)
    # np.zeros: generate array of zeros
    # np.maximum: compare two arrays elementwise and choose the maximal one

prices = np.arange(1,101)
Qd(prices)
  • Qd maps 1-D array to
from scipy import optimize

monopoly=Monopoly(2, Qd)
monopoly.cost(np.array([3]))
monopoly.revenue(np.array([3]))
monopoly.profit(np.array([3]))

7.1.2 Profit maximization

def negative_conversion(fn):
    def neg_fun(p):
        return -1*fn(p)
    return neg_fun
objective_negative=negative_conversion(monopoly.profit)
optimize.minimize(
  fun=negative_conversion(monopoly.profit),
  x0=np.array([3])
)
def monopoly_optim(x0=3):
    optim = optimize.minimize(
      fun=negative_conversion(monopoly.profit),
      x0=np.array([x0])
    )
    return optim
optim = monopoly_optim()
optim.x
monopoly.Qd(optim.x)

7.1.3 Class

class Monopoly:
    def __init__(self, mc, Qd):
        self.mc=mc
        self.Qd=Qd
    
    def cost(self, p):
        return self.mc*self.Qd(p)
    def revenue(self, p):
        return p*self.Qd(p)
    def profit(self, p):
        return p*self.Qd(p) - self.mc *self.Qd(p)
    def optimize(self, x0=3):
        optim = optimize.minimize(
          fun=negative_conversion(self.profit),
          x0=np.array([x0])
        )
        self.optim_p = optim.x
        self.optim_q = self.Qd(self.optim_p)
        return optim
monopoly = Monopoly(5, Qd)
monopoly.Qd
monopoly.mc
monopoly.optimize()
monopoly.optim_p
monopoly.optim_q
monopoly.profit(monopoly.optim_p)

7.2 Class variable

7.2.1 Class: Market Demand

class MarketDemand:
    def __init__(self, intercept, slope):
        self.intercept = intercept
        self.slope = slope
    def Qd(self, p):
        return self.intercept - self.slope * p
marketDemand = MarketDemand(500, 7)
Qd = marketDemand.Qd
monopoly = Monopoly(10, Qd)
monopoly.optimize()
monopoly.optim_p
monopoly.optim_q

7.2.2 Change of demand

marketDemand2 = MarketDemand(380, 5)
Qd = marketDemand2.Qd

# optimization will give the same result
monopoly.optimize()
monopoly.optim_p
monopoly.optim_q

7.2.3 Class variable

import numpy as np 
from scipy import optimize

class Monopoly:
    intercept=100
    slope=2
    
    def __init__(self, mc):
        self.mc=mc
    
    def cost(self, p):
        return self.mc*self.__class__.Qd(p)
    def revenue(self, p):
        return p*self.__class__.Qd(p)
    def profit(self, p):
        return p*self.__class__.Qd(p) - self.mc *self.__class__.Qd(p)
    def optimize(self, x0=3):
        optim_res = optimize.minimize(
          fun=negative_conversion(self.profit),
          x0=np.array([x0])
        )
        self.optim_p = optim_res.x
        self.optim_q = self.__class__.Qd(self.optim_p)
        return optim_res
    
    @classmethod
    def Qd(cls, p):
        return cls.intercept - cls.slope*p

# helper
def negative_conversion(fn):
    def neg_fun(p):
        return -1*fn(p)
    return neg_fun
Instance and class: variables and methods

\[Qd(p)=100-2p\]

monopoly = Monopoly(5)
monopoly.Qd(3)

\[Qd(p)=300-7p\]

Monopoly.intercept = 300
Monopoly.slope = 7
monopoly.Qd(3)

Note that once class variables are changed. It stay that way until next change

monopoly2 = Monopoly(5) # will not go back to original Monopoly definition
monopoly2.Qd(3)
Monopoly.intercept = 300
Monopoly.slope = 7
monopoly.optimize()
monopoly.optim_p
monopoly.optim_q
Monopoly.intercept = 100
Monopoly.slope = 3
monopoly.optimize()
monopoly.optim_p
monopoly.optim_q

7.3 Ex-Post Manipulation

Class variables and instance variables can be manipulated ex post – including adding and deletion.

class Apple:
    pass # do nothing 
apple1 = Apple()
apple2 = Apple()
# An instance variable
apple1.color = "red"
apple2.color = "green"

# A class variable
Apple.type = "fruit"
# type is a class variable to all instances
apple1.__class__.type
apple2.__class__.type
# class variable can be accessed as it is an instance variables
#  except all instances share the same value (common knowledge)
apple1.type
apple2.type

# instance variables
apple1.color
apple2.color

If there is also an instance variable called type such as:

apple1.type = "animal" # instance variable creation

Then apple1.type represents the instance variable instead of the class variable:

apple1.type
apple1.__class__.type # you still can access class variable (type) this way

Deletion:

del Apple.type
del apple1.color
del apple2.color
del apple1

del can be used to delete any object in the environment:

del Apple

7.4 Common knowledge

7.4.1 Market structure narrative

In a country where demand is always linear, any demand is an instance of LinearDemand class via:

class LinearDemand:
    def __init__(self, intercept, slope):
      self.intercept=intercept
      self.slope=slope

demand = LinearDemand(intercept=300, slope=0.2)
demand.intercept
demand.slope

There are three producers to serve the market: mono, duo, and trio. They are instances of the same Producer class.

How can we model the common knowledge demand for these three instances so that:

mono.demand
duo.demand
trio.demand
# all are aware of the SAME demand

In addition, whenever something changes to demand, such as:

demand.intercept=500

all instance.demands changes as well.

7.4.2 Common in id

=: identical assignment

The first approach is to feed demand as instance variable when each instance is created. Consider the following class constructor:

class Producer:
    def __init__(self, demand):
      self.demand = demand

mono = Producer(demand)
duo = Producer(demand)
  • The design ensures that demand is in mono and duo’s knowledge domain (as an instance variable).

The above construction has:

print(id(demand))
print(id(mono.demand))
print(id(duo.demand))

the same.

Change one will change all:

demand.slope += 2
demand.slope
mono.demand.slope
duo.demand.slope

However, the above common knowledge structure works only under the same ID source of instance variable. It can be breached easily when users accidentally create another new instance source and feed it into a new instance, such as:

import copy
demand = copy.deepcopy(demand)
trio = Producer(demand)

print(id(mono.demand))
print(id(duo.demand))
print(id(trio.demand)) # different from the other two
print(id(demand)) # same as trio, different from the other two

# change to demand
demand.intercept += 50
demand.intercept
# changes
trio.demand.intercept

# but not
mono.demand.intercept
duo.demand.intercept

7.4.3 Common as class variables

If you want to ensure mono, duo, trio share the common knowledge of demand, you ought to make demand a class variable.

class Producer:
    pass

mono = Producer()
duo = Producer()
demand = LinearDemand(intercept=300, slope=0.2)
Producer.demand = demand

print(id(mono.demand))
print(id(duo.demand))

# create a new copy
demand = copy.deepcopy(demand)
print(id(demand))

# Attach new copy to Producer
Producer.demand = demand
# will immediately change each instance's knowledge
print(id(mono.demand))
print(id(duo.demand))
  • This is because the common knowledge ID comes from the class Producer – not from its own instance variable. This ensures each instance access the same source of demand knowledge.

Always model common knowledge across instances as their class variable.

7.5 Examples

7.5.1 Consumers

In an economy, there are many consumers with Cobb-Douglass utility: \[U(x_1, x_2)=x_1^\alpha x_2^{1-\alpha},\] with given prices \((p_1,p_2)\) and income \(I\). Optimal consumptions will be: \[x_1^*=\alpha*I/p_1,\ x_2^*=(1-\alpha)*I/p_2.\] Suppose there are 30 consumers randomly drawn from \(\alpha\sim uniform(0.2, 0.8)\) and \(I\sim uniform(500,1000)\)

from numpy.random import default_rng
alpha_s = default_rng(2038).uniform(0.2,0.8, size=30)
I_s = default_rng(2033).uniform(500,100, size=30)


class Consumer:
    def __init__(self, alpha, I):
      self.alpha = alpha
      self.I = I
    def optimise(self):
      p1, p2 = self.__class__.p
      self.x1 = self.alpha * self.I/p1
      self.x2 = (1-self.alpha) * self.I/p2


consumers = [
  Consumer(alpha, I) for alpha, I in zip(alpha_s, I_s)
]
  • note that class variable p is not assigned yet.
# Given p1=1, p2=1
p = (1, 1) 
# Assign it as common knowledge for all consumers
Consumer.p = p

Once consumers share the common knowledge, they know how much they want to consume:

for i in range(len(consumers)):
    consumers[i].optimise()

# check their decisions
consumers[0].x1
consumers[0].x2
consumers[22].x1
consumers[22].x2

You can also:

for consumer_i in consumers:
    consumer_i.optimise()

# check their decisions
consumers[0].x1
consumers[0].x2
consumers[22].x1
consumers[22].x2
  • The iterates from the iterable object are representing its element identically (i.e. sharing the same memory address).

7.5.2 Producers

An economy has many log production function producers. Each has production function: \[q(x)=\sqrt x,\] where \(x\) is some intermediate input quantity (such as labor). Assume intermediate input has a constant unit price \(w\) so that hiring \(x\) units of intermediate input will incur \(wx\) total cost while generating \(q(x)\) units of output and \(p q(x)\) total revenue. This means the producer’s total profit function is: \[\pi(x)=p\sqrt x-wx.\]

There are 10 producers in the economy whose \(w\) is randomly drawn from \(w\sim uniform(2, 5)\). They take market price \(p\) as their common knowledge. They are price taker. Optimal production of output will have: \[x^*=(p/w)^2/4,\ q^*=(p/w)/2\]

rng_w = default_rng(2033).uniform(2,5, size=10)

class Producer:
    def __init__(self, w):
      self.w=w
    def optimise(self):
      p = self.__class__.p
      self.x = (p/self.w)**2 /4
      self.q = (p/self.w)/2

producers = [
  Producer(w) for w in rng_w
]
  • note that class variable p is not assignment yet.
# Given
p = 1 
# Assign it as common knowledge for all producers
Producer.p = p

Once producers share the common knowledge, they know how many they want to produce:

for i in range(len(producers)):
    producers[i].optimise()

# check their decisions
producers[0].x
producers[0].q
producers[7].x
producers[7].q

7.5.3 Competitive markets

A (perfect) competition market consists of market demand, marke supply and an equilibrium condition that requires demand equal to supply to determine equilibrium price and quantity.

import numpy as np
from scipy.optimize import root


class Competitive_market:
  def equilibrium(self):
    excess_demand = lambda x: self.Qd(x) - self.Qs(x)
    result = root(excess_demand, x0=np.array([1]))
    self.P = result.x
    self.Q = self.Qd(self.P)
def excess_demand(x):
  return Qd(x) - Qs(x)
market = Competitive_market()

def Qd(p):
  return max(0, 3-p)

def Qs(p):
  return max(0, 0+2*p)

# supply Qd and Qs 
market.Qd = Qd
market.Qs = Qs
market.equilibrium()

market.P
market.Q

We can change Qd and Qs instance variables, and reuse optimise instance method:

def Qd(p):
  Consumer.p = (p, 1)
  total = 0
  for consumer_i in consumers:
    consumer_i.optimise()
    total += consumer_i.x1
  return total

def Qs(p):
  Producer.p = p
  total = 0
  for producer_i in producers:
    producer_i.optimise()
    total += producer_i.q
  return total

market.Qd = Qd
market.Qs = Qs
market.equilibrium()

market.P
market.Q

7.6 綜合練習

1. Consumers

In an economy, there are many Cobb-Douglas preference consumers. Each faces a utility function \(U(\bf{x})\): \[U({\bf x})=x_1^{\alpha}x_2^{1-\alpha},\] where \({\bf x}=[x_1, x_2]\) is a vector representing the consumption of \((x_1, x_2)\) bundle.

For any consumer, say consumer \(i\), he faces a budget constraint: \[p_1x_1+p_2x_2\leq I_i,\] Assume that the market prices are \((p_1=2, p_2=5)\) at the current moment. We can give birth to a \((\alpha=0.3, I_i=200)\) consumer via:

alpha = 0.3
I = 200
consumer_i = Consumer(alpha, I)

Once he is born, he:

# is aware of his budget constraint
consumer_i.describe_budget_constraint() # will generate his budget constraint at the following instance variable
consumer_i.budget_constraint # shows a dictionary object that represents the nonnegative budget constraint description as restriction optimization procedure requires
# knows his preference
consumer_i.U([2,3]) # shows utility of x1=2, x2=3 consumption
# is able to optimize
consumer_i.optimize() # generate consumer_i.x1 consumer_i.x2 to show the optimized x1, x2 consumption bundle.

We describe the market prices change to \((p_1=2.5, p_2=4)\) via:

Consumer.p1=2.5
Consumer.p2=4

Once the change is done, consumer_i can:

# re-optimize 
consumer_i.optimize()
# which gives you a new optimal consumption bundle
consumer_i.x1
consumer_i.x2

We can play God to give birth to 300 consumers via

from numpy.random import default_rng
rng_alpha = default_rng(2020)
rng_I = default_rng(2020)
world_alpha = rng_alpha.uniform(0.01, 0.99, size=300)
world_I = rng_I.uniform(500, 1000, size=300)
world_consumers = [Consumer(alpha, I) for alpha, I in zip(world_alpha, world_I)]

Not only does world_consumers keep track of these 300 consumers, so does the class object Consumer keep the record as well, which can be checked via:

Consumer.world_consumers

In addition, whenever God is not happy, it can destroy all consumers via:

Consumer.destroy()

which will turn Consumer.world_consumers into an empty list.

Design the Consumer class.

2. Market demand function

Given the Consumer from question 1, run the following code to generate 30 consumers:

from numpy.random import default_rng
rng_alpha = default_rng(2020)
rng_I = default_rng(2020)
world_alpha = rng_alpha.uniform(0.2, 0.8, size=30)
world_I = rng_I.uniform(500, 1000, size=30)
Consumer.destroy()
for alpha, I in zip(world_alpha, world_I):
    Consumer(alpha, I)

Complete the design of the following market demand function for \(x1\)

def Qd(class_consumer, p1):
    ....

Given the construction of 30 consumers earlier,

Qd(Consumer, 3) # You can use a **class** as an input argument.

will return the quantity of market demand at a price of p1=3 (which is around 4718), given the default value of Consumer.p2=5.

3. Cocky producer

Continue from the setting of question 1. There is only one producer Cocky who produces \(x_1\) (think it as chocolate cake). Cocky thinks his product is extremely unique so that the other commodity \(x_2\) (think it as bubble tea) is completely different and won’t affect Cocky’s profit – at least Cocky thinks so. At the current moment \(p_2=5\), Cocky think this price will continue to be that way no matter what price \(p_1\) he sets for \(x_1\).

Cocky is generated by a Producer1 class via:

cocky = Producer1(5, Consumer)

where 5 is his marginal cost of production, and Consumer is the class Consumer you design earlier.

Cocky:

# knows his market quantity demanded at price p1=3 via:
cocky.Qd(3)
# Other than that, he can calculate his cost of production at price =3
cocky.cost(3)
# his revenue of production at price=3
cocky.revenue(3)
# his profit of production at price=3
cocky.profit(3)

Cocky think the bubble tea will continue to charge at \(p_2=5\) as fixed via:

Consumer.p2 = 5

Given that perception, Cocky chooses a price \(p_1\) to maximize his profit via:

cocky.optimize()

# his optimal price and output will show up at
cocky.p1 # around 9.99
cocky.x1 # around 1415

You can play around with your simulated economy at different \(p_2\) price scenarios and see how Cocky would respond with his optimal \(p_1\). From the exercise, you can think about the following question:

Is there truly a pure monopoly in reality ?

同場加映(中間會有一首非常有名的曲子):

3. Prudent producer

Prue is the unique producer of \(x_2\). She grows up with Cocky; therefore, she know everything about Cocky. To model such an information structure, assume Prue is a instance generated by:

Prue = Producer2(2, Consumer, cocky)

Different from Cocky, whenever Prue thinks of her market demand, she thinks of the ability of the influence of her price \(p_2\) on \(p_1\) as an instance method, prue.p1, which tells Prue what optimal p1 will be charge by Cocky. For example, to know how much Cocky will charge when Prue charges \(p_1=5\), he can:

prue.p1(5) # tell us what p1 will be when Prue set a price of 5

Prue has two different types of market demands. One that take \(p_1\) as constant: \[\text{Qd_typeA} = Qd(p_2, p_1),\] the other takes \(p_1\) as an influced price of Prue’s \(p_2\), i.e. \[\text{Qd_typeB} = Qd(p_2, p_1^*(p_2)),\] where \(p_1^*(p_2)\) means the optimized price setting of \(p_1\) from Cocky given \(p_2\).

knitr::opts_chunk$set(message=F, eval=F)
klippy::klippy(lang=c("r","python"))