Solving models

In this notebook, we will solve a simple duopoly model using python. This illustrates how to optimize in python and how to solve equations. We do this both symbolically and numerically. There are a lot of mathematical functions in python, but for economists the two most important ones are: optimization (utility, profit etc. maximization) and fixed points (equilibrium). So we focus on these in this notebook.

1 Model

The set up is the following standard Cournot model. We work with a linear demand curve \(p_i(q_i,q_j)=1-a q_i - b q_j\) and costs \(c_i(q)=c_i q\), where \(q_i\) denotes firm \(i\)'s output on the market.

Firm \(i\)'s profit is written as \(\pi(q_i,q_j)=(1-a q_i-b q_j)q_i-c_i q_i\) with \(j \neq i\). The way we usually solve this analytically is as follows.

The two first order conditions for firms 1 and 2 can be written as:

\[ 1-2a q_1-b q_2-c_1 = 0 \]

and

\[ 1-2 a q_2-b q_1-c_2 = 0 \]

Then we can solve these two equations in the unknowns \(q_1,q_2\). However, we usually take a step in between and define the reaction functions. That is, we solve the optimal \(q_i\) as a function of \(q_j\):

\[ q_i = \frac{1}{2a}(1-c_i-b q_j) \]

Or, equivalently, we define \(i\)'s reaction function as

\[ R_i(q_j) = \frac{1}{2a}(1-c_i-b q_j) \]

We have an equilibrium if \(q_1 = R_1(q_2)\) and \(q_2 = R_2(q_1)\). Or in matrix notation, the equilibrium is given by \(q_1,q_2\) such that

$$

\begin{pmatrix} q_1\\ q_2\end{pmatrix} = \begin{pmatrix} R_1(q_2)\\ R_2(q_1)\end{pmatrix}

$$

If both firms have the same costs \(c_1=c_2\), there is a symmetric equilibrium given by \(q=R(q)\).

To calculate the equilibrium, we need to find a so called fixed point of the vector function \(R(q)\). This can be seen as follows.

Define the function (or mapping) \(q \rightarrow R(q)\) with \(q \in \Re^2\) and

\[ R(q) = \begin{pmatrix} R_1(q_2)\\ R_2(q_1)\end{pmatrix} \]

A fixed point of this function is defined as a vector \(q\) such that \(q=R(q)\). In words, \(q_1\) is the optimal reaction to \(q_2\) and \(q_2\) is the optimal reaction to \(q_1\). That is, the vector \(q\) is a Nash equilibrium. Each firm \(i\) chooses its optimal output level \(q_i\), given the choice by the other firm \(q_j\).

2 solve model symbolically

One way to solve such a duopoly model in python is to use the sympy library which allows us to do symbolic math. We import this library.

from sympy import *

We define variables that we want to use as symbols.

q1, q2 = symbols('q1 q2')
a,b = symbols('a b')
c1, c2 = symbols('c1 c2')

We define the (inverse) demand function and the cost function. Although, these look like "normal" python functions, python actually "understands" these functions as symbolic functions. The reason is that they are defined using the symbols that we just defined.

def p(q1,q2):
    return 1-a* q1 - b* q2

def cost(q,c):
    return c*q

With these functions, we can define the profit function of a firm with cost level \(c\) that produces output \(q_1\) while its competitor produces \(q_2\).

Once the profit function is defined, we derive the first order conditions. That is, we differentiate the profit of firm \(i\) with respect to the output level of firm \(i\), \(q_i\).

def profit(q1,q2,c):
    return p(q1,q2)*q1 - cost(q1,c)

foc1 = diff(profit(q1,q2,c1),q1)
foc2 = diff(profit(q2,q1,c2),q2)


Exercise

Use the function diff to show that profits of firm \(i\) are concave in \(q_i\).


In equilibrium the first order conditions for both firms should equal 0. Hence, we can solve the system of equation foc1, foc2 with respect to \(q_1,q_2\).

solve([foc1, foc2], [q1, q2])

{q1: (-2*a*(c1 - 1) + b*(c2 - 1))/(4*a**2 - b**2), q2: (-2*a*(c2 - 1) + b*(c1 - 1))/(4*a**2 - b**2)}


Exercise

Define a different demand and/or cost function and calculate the equilibrium again. Note that for some demand and cost functions, an analytical solution is no longer available.


With sympy you can differentiate and integrate your favourite functions.

from sympy import *

x = symbols('x')
def f(x):
    return x**2

print(diff(f(x),x))
print(integrate(f(x),x))

2*x x**3/3


Exercise

Find the derivative and integral of \(e^x\) (use exp(x)), \(cos(x)\) and \(ln(x)\).


3 solve model numerically

Sometimes you are not interested in the analytical solution, but just want to know how the equilibrium varies with a parameter. We will make a graph where the equilibrium output levels vary with one of the firm's cost level. We start by importing some libraries.

# First lets clear all previous python imports and variables by resetting the python kernel.
%reset -f

from scipy import optimize,arange
from numpy import array
import matplotlib.pyplot as plt
%matplotlib inline

We define the (inverse) demand and cost functions as above. But now \(q1,q2,c\) are normal python variables and not symbols.

def p(q1,q2):
    return 1-a*q1-b*q2

def costs(q,c):
    return c*q

def profits(demand_function,cost_function,c,q1,q2):
    return demand_function(q1,q2)*q1-costs(q1,c)

Note that the function profits above takes the functions demand_function and cost_function as arguments, besides the variables c,q1,q2.


Exercice

Try to take the derivative of \(costs(q,c)\) with respect to \(q\) using diff (that is, after importing sympy).


As we are doing a numerical analysis here, we need to choose values for the demand parameters \(a,b\).

a = 1
b = 1

Each firm maximizes its profits. However, python does not feature maximization routines; there are only minimization routines. Hence, if we want to maximize profits, we need to minimize minus profits. In the following code block, we minimize \(-profits\) for firm 1 given an output level \(q_2\) for the other firm. This is, in fact, firm 1's reaction function. From optimize we use the function fminbound to find the minimum of \(-profits\). We specify the function profits, with functions p and costs as defined above. The function call to reaction specifies q2 and c1. The only variable left to vary is \(q_1\) which we denote by \(x\) in the "lambda"-function. Further, we specify the interval where the optimal \(q_1\) has to be found; \([0,1]\) in this case. Google to see what full_output does in this case.

Then we define the function fixed_point which is the equivalent of \(q-R(q)\) above. Note that the function reaction can be used for both firm 1 and firm 2; only the arguments differ for the firms, but the function itself is the same.

The function fixed_point takes two (vector) arguments: a vector \(q\) of output levels and \(c\) of cost levels. With the indexing conventions in python, \(q[0]\) is the first element of \(q\) and hence a natural choice for \(q_1\); similarly, \(q[1]\) corresponds to \(q_2\). The same logic applies to \(c\).

From optimize (which was imported above from scipy), we use fsolve to find the \(q\) where fixed_point(q,c) equals 0. We provide an initial guess for this vector \(q\). We solve the model for the case where \(c_1=c_2=0\) (with \(a=b=1\) you should be able to dream the answer…)

def reaction(q2,c1):
    q1 =  optimize.fminbound(lambda x: -profits(p,costs,c1,x,q2),0,1,full_output=1)
    return q1[0]

def fixed_point(q,c):
    return [q[0]-reaction(q[1],c[0]),q[1]-reaction(q[0],c[1])]

initial_guess = [0,0]

optimize.fsolve(lambda q: fixed_point(q,[0,0]), initial_guess)

array([ 0.33333333, 0.33333333])


Exercise

Solve the model for \(c_1 = 0.1, c_2 = 0.2\).


Now that we have solved the model, we can plot the output level of firms' 1 and 2 as a function of \(c_1\).

range_c = arange(0,0.51,0.01)
range_q = [optimize.fsolve(lambda q: fixed_point(q,[c,0]), [0,0]) for c in range_c]
plt.style.use('seaborn')
plt.clf()
plt.plot(range_c,array(range_q)[:,0], label = '$q_1$')
plt.plot(range_c,array(range_q)[:,1], label = '$q_2$')
plt.xlabel('$c_1$')
plt.ylabel('$q$')
plt.legend()
plt.savefig('Cournot.png')


ob-ipython-9a71e7398fd8de0eb04f71d2cd14ee21.png <matplotlib.figure.Figure at 0x10efc8b00>

Cournot.png


Exercise

Copy the code above in a new cell and adjust it such that you can plot \(q_1,q_2\) as a function of \(b \in [0,a]\). First, do this for the symmetric case with \(c_1=c_2=0\). Then make a new plot where \(c_1 \neq c_2\).