This notebook is a slight modification of the one found in QuantEcon: The Overlapping Generations Model.
In this lecture we study the famous overlapping generations (OLG) model, which is used by policy makers and researchers to examine
- fiscal policy
- monetary policy
- long-run growth
and many other topics.
The first rigorous version of the OLG model was developed by Paul Samuelson [Samuelson, 1958].
Our aim is to gain a good understanding of a simple version of the OLG model.
Overview¶
The dynamics of the OLG model are quite similar to those of the Solow-Swan growth model.
At the same time, the OLG model adds an important new feature: the choice of how much to save is endogenous.
To see why this is important, suppose, for example, that we are interested in predicting the effect of a new tax on long-run growth.
We could add a tax to the Solow-Swan model and look at the change in the steady state.
But this ignores the fact that households will change their savings and consumption behavior when they face the new tax rate.
Such changes can substantially alter the predictions of the model.
Hence, if we care about accurate predictions, we should model the decision problems of the agents.
In particular, households in the model should decide how much to save and how much to consume, given the environment that they face (technology, taxes, prices, etc.)
The OLG model takes up this challenge.
We will present a simple version of the OLG model that clarifies the decision problem of households and studies the implications for long-run growth.
Let’s start with some imports.
from collections import namedtuple
import matplotlib.pyplot as plt
import numpy as np
from scipy import optimize
Environment¶
We assume that time is discrete, so that .
An individual born at time lives for two periods, and .
We call an agent
- “young” during the first period of their lives and
- “old” during the second period of their lives.
Young agents work, supplying labor and earning labor income.
They also decide how much to save.
Old agents do not work, so all income is financial.
Their financial income is from interest on their savings from wage income, which is then combined with the labor of the new young generation at .
The wage and interest rates are determined in equilibrium by supply and demand.
To make the algebra slightly easier, we are going to assume a constant population size.
We normalize the constant population size in each period to 1.
We also suppose that each agent supplies one “unit” of labor hours, so total labor supply is 1.
Supply of capital¶
First let’s consider the household side.
Consumer’s problem¶
Suppose that utility for individuals born at time takes the form
Here
- is called the “flow” utility function
- is the discount factor
- is time consumption of the individual born at time
- is time consumption of the same individual
We assume that is strictly increasing.
Savings behavior is determined by the optimization problem
subject to
Here
- is savings by an individual born at time
- is the wage rate at time
- is the gross interest rate on savings invested at time , paid at time
Since is strictly increasing, both of these constraints will hold as equalities at the maximum.
Using this fact and substituting from the first constraint into the second we get .
The first-order condition for a maximum can be obtained by plugging into the objective function, taking the derivative with respect to , and setting it to zero.
This leads to the Euler equation of the OLG model, which describes the optimal intertemporal consumption dynamics:
From the first constraint we get , so the Euler equation can also be expressed as
Suppose that, for each and , there is exactly one that solves (26.4).
Then savings can be written as a fixed function of and .
We write this as
The precise form of the function will depend on the choice of flow utility function .
Together, and represent the prices in the economy (price of labor and rental rate of capital).
Thus, (26.5) states the quantity of savings given prices.
Example: log preferences¶
In the special case , the Euler equation simplifies to .
Solving for saving, we get
In this special case, savings does not depend on the interest rate.
Savings and investment¶
Since the population size is normalized to 1, is also total savings in the economy at time .
In our closed economy, there is no foreign investment, so net savings equals total investment, which can be understood as supply of capital to firms.
In the next section we investigate demand for capital.
Equating supply and demand will allow us to determine equilibrium in the OLG economy.
Demand for capital¶
First we describe the firm’s problem and then we write down an equation describing demand for capital given prices.
Firm’s problem¶
For each integer , output in period is given by the Cobb-Douglas production function
Here is capital, is labor, and is a parameter (sometimes called the “output elasticity of capital”).
The profit maximization problem of the firm is
The first-order conditions are obtained by taking the derivative of the objective function with respect to capital and labor respectively and setting them to zero:
Demand¶
Using our assumption allows us to write
and
Rearranging (26.10) gives the aggregate demand for capital at time
In Python code this is
def capital_demand(R, α):
return (α / R) ** (1 / (1 - α))
def capital_supply(R, β, w):
R = np.ones_like(R)
return R * (β / (1 + β)) * w
The next figure plots the supply of capital, as in (26.6), as well as the demand for capital, as in (26.11), as functions of the interest rate .
(For the special case of log utility, supply does not depend on the interest rate, so we have a constant function.)
Equilibrium¶
In this section we derive equilibrium conditions and investigate an example.
Equilibrium conditions¶
In equilibrium, savings at time equals investment at time , which equals capital supply at time .
Equilibrium is computed by equating these quantities, setting
In principle, we can now solve for the equilibrium price given .
(In practice, we first need to specify the function and hence .)
When we solve this equation, which concerns time outcomes, time quantities are already determined, so we can treat as a constant.
From equilibrium and (26.11), we can obtain the equilibrium quantity .
Example: log utility¶
In the case of log utility, we can use (26.12) and (26.6) to obtain
Solving for the equilibrium interest rate gives
In Python we can compute this via
def equilibrium_R_log_utility(α, β, w):
R = α * ((β * w) / (1 + β)) ** (α - 1)
return R
In the case of log utility, since capital supply does not depend on the interest rate, the equilibrium quantity is fixed by supply.
That is,
Let’s redo our plot above but now inserting the equilibrium quantity and price.
R_vals = np.linspace(0.3, 1)
α, β = 0.5, 0.9
w = 2.0
def plot_equilibrium(α, β, w):
fig, ax = plt.subplots()
ax.plot(R_vals, capital_demand(R_vals, α), label="aggregate demand")
ax.plot(R_vals, capital_supply(R_vals, β, w), label="aggregate supply")
R_e = equilibrium_R_log_utility(α, β, w)
k_e = (β / (1 + β)) * w
ax.plot(R_e, k_e, "o", label="equilibrium")
ax.set_xlabel("$R_{t+1}$")
ax.set_ylabel("$k_{t+1}$")
ax.legend()
plt.show()
plot_equilibrium(α, β, w)
from ipywidgets import FloatSlider, interact
interact(
plot_equilibrium,
α=FloatSlider(α, min=0, max=1, step=0.01),
β=FloatSlider(β, min=0, max=1, step=0.01),
w=FloatSlider(w, min=0, max=10, step=0.01),
)
Dynamics¶
In this section we discuss dynamics.
For now we will focus on the case of log utility, so that the equilibrium is determined by (26.15).
Evolution of capital¶
The discussion above shows how equilibrium is obtained given .
From (26.9) we can translate this into as a function of
In particular, since , we have
If we iterate on this equation, we get a sequence for capital stock.
Let’s plot the 45-degree diagram of these dynamics, which we write as
def k_update(k, α, β):
return β * (1 - α) * k**α / (1 + β)
α, β = 0.5, 0.9
kmin, kmax = 0, 0.1
n = 1000
k_grid = np.linspace(kmin, kmax, n)
def plot45(α, β):
k_grid_next = k_update(k_grid, α, β)
fig, ax = plt.subplots(figsize=(6, 6))
ymin, ymax = np.min(k_grid_next), np.max(k_grid_next)
ax.plot(k_grid, k_grid_next, lw=2, alpha=0.6, label="$g$")
ax.plot(k_grid, k_grid, "k-", lw=1, alpha=0.7, label=r"$45^{\circ}$")
ax.legend(loc="upper left", frameon=False, fontsize=12)
ax.set_xlabel("$k_t$", fontsize=12)
ax.set_ylabel("$k_{t+1}$", fontsize=12)
plt.show()
plot45(α, β)
interact(
plot45,
α=FloatSlider(α, min=0, max=1, step=0.01),
β=FloatSlider(β, min=0, max=1, step=0.01),
)
Steady state (log case)¶
The diagram shows that the model has a unique positive steady state, which we denote by .
We can solve for by setting , or
Solving this equation yields
We can get the steady state interest rate from (26.10), which yields
In Python we have
k_star = ((β * (1 - α)) / (1 + β)) ** (1 / (1 - α))
R_star = (α / (1 - α)) * ((1 + β) / β)
Time series¶
The 45-degree diagram above shows that time series of capital with positive initial conditions converge to this steady state.
Let’s plot some time series that visualize this.
ts_length = 25
k_series = np.empty(ts_length)
k_series[0] = 0.02
for t in range(ts_length - 1):
k_series[t + 1] = k_update(k_series[t], α, β)
fig, ax = plt.subplots()
ax.plot(k_series, label="capital series")
ax.plot(range(ts_length), np.full(ts_length, k_star), "k--", label="$k^*$")
ax.set_ylim(0, 0.1)
ax.set_ylabel("capital")
ax.set_xlabel("$t$")
ax.legend()
plt.show()
If you experiment with different positive initial conditions, you will see that the series always converges to .
Below we also plot the gross interest rate over time.
R_series = α * k_series ** (α - 1)
fig, ax = plt.subplots()
ax.plot(R_series, label="gross interest rate")
ax.plot(range(ts_length), np.full(ts_length, R_star), "k--", label="$R^*$")
ax.set_ylim(0, 4)
ax.set_ylabel("gross interest rate")
ax.set_xlabel("$t$")
ax.legend()
plt.show()
The interest rate reflects the marginal product of capital, which is high when capital stock is low.
CRRA preferences¶
Previously, in our examples, we looked at the case of log utility.
Log utility is a rather special case of CRRA utility with .
In this section, we are going to assume that , where .
This function is called the CRRA utility function.
In other respects, the model is the same.
Below we define the utility function in Python and construct a namedtuple
to store the parameters.
def crra(c, γ):
return c ** (1 - γ) / (1 - γ)
Model = namedtuple(
"Model",
[
"α", # Cobb-Douglas parameter
"β", # discount factor
"γ",
], # parameter in CRRA utility
)
def create_olg_model(α=0.4, β=0.9, γ=0.5):
return Model(α=α, β=β, γ=γ)
Let’s also redefine the capital demand function to work with this namedtuple
.
def capital_demand_model(R, model):
return (model.α / R) ** (1 / (1 - model.α))
Supply¶
For households, the Euler equation becomes
Solving for savings, we have
Notice how, unlike the log case, savings now depends on the interest rate.
def savings_crra(w, R, model):
α, β, γ = model
return w / (1 + β ** (-1 / γ) * R ** ((γ - 1) / γ))
model = create_olg_model()
w = 2.0
fig, ax = plt.subplots()
ax.plot(R_vals, capital_demand_model(R_vals, model), label="aggregate demand")
ax.plot(R_vals, savings_crra(w, R_vals, model), label="aggregate supply")
ax.set_xlabel("$R_{t+1}$")
ax.set_ylabel("$k_{t+1}$")
ax.legend()
plt.show()
Equilibrium¶
Equating aggregate demand for capital (see (26.11)) with our new aggregate supply function yields equilibrium capital.
Thus, we set
This expression is quite complex and we cannot solve for analytically.
Combining (26.10) and (26.21) yields
Again, with this equation and as given, we cannot solve for by pencil and paper.
In the exercise below, you will be asked to solve these equations numerically.
Exercises¶
Exercise 26.1¶
Solve for the dynamics of equilibrium capital stock in the CRRA case numerically using (26.22).
Visualize the dynamics using a 45-degree diagram.
Solution toExercise 26.1¶
To solve for given we use Newton’s method.
Let
If is given then is a function of unknown .
Then we can use scipy.optimize.newton
to solve for .
First let’s define .
def f(k_prime, k, model):
α, β, γ = model.α, model.β, model.γ
z = (1 - α) * k**α
a = α ** (1 - 1 / γ)
b = k_prime ** ((α * γ - α + 1) / γ)
p = k_prime + k_prime * β ** (-1 / γ) * a * b
return p - z
Now let’s define a function that finds the value of .
def k_update_opt(k, model):
return optimize.newton(lambda k_prime: f(k_prime, k, model), 0.1)
Finally, here is the 45-degree diagram.
kmin, kmax = 0, 0.5
n = 1000
k_grid = np.linspace(kmin, kmax, n)
k_grid_next = np.empty_like(k_grid)
for i in range(n):
k_grid_next[i] = k_update_opt(k_grid[i], model)
fig, ax = plt.subplots(figsize=(6, 6))
ymin, ymax = np.min(k_grid_next), np.max(k_grid_next)
ax.plot(k_grid, k_grid_next, lw=2, alpha=0.6, label="$g$")
ax.plot(k_grid, k_grid, "k-", lw=1, alpha=0.7, label=r"$45^{\circ}$")
ax.legend(loc="upper left", frameon=False, fontsize=12)
ax.set_xlabel("$k_t$", fontsize=12)
ax.set_ylabel("$k_{t+1}$", fontsize=12)
plt.show()
Exercise 26.2¶
The 45-degree diagram from the last exercise shows that there is a unique positive steady state.
The positive steady state can be obtained by setting in (26.22), which yields
Unlike the log preference case, the CRRA utility steady state cannot be obtained analytically.
Instead, we solve for using Newton’s method.
Solution toExercise 26.2¶
We introduce a function such that positive steady state is the root of .
Here it is in Python
def h(k_star, model):
α, β, γ = model.α, model.β, model.γ
z = (1 - α) * k_star**α
R1 = α ** (1 - 1 / γ)
R2 = k_star ** ((α * γ - α + 1) / γ)
p = k_star + k_star * β ** (-1 / γ) * R1 * R2
return p - z
Let’s apply Newton’s method to find the root:
k_star = optimize.newton(h, 0.2, args=(model,))
print(f"k_star = {k_star}")
k_star = 0.25788950250843484
Exercise 26.3¶
Generate three time paths for capital, from three distinct initial conditions, under the parameterization listed above.
Use initial conditions for of and time series length 10.
Solution toExercise 26.3¶
Let’s define the constants and three distinct intital conditions
ts_length = 10
k0 = np.array([0.001, 1.2, 2.6])
def simulate_ts(model, k0_values, ts_length):
fig, ax = plt.subplots()
ts = np.zeros(ts_length)
# simulate and plot time series
for k_init in k0_values:
ts[0] = k_init
for t in range(1, ts_length):
ts[t] = k_update_opt(ts[t - 1], model)
ax.plot(
np.arange(ts_length),
ts,
"-o",
ms=4,
alpha=0.6,
label=r"$k_0=%g$" % k_init,
)
ax.plot(
np.arange(ts_length),
np.full(ts_length, k_star),
alpha=0.6,
color="red",
label=r"$k^*$",
)
ax.legend(fontsize=10)
ax.set_xlabel(r"$t$", fontsize=14)
ax.set_ylabel(r"$k_t$", fontsize=14)
plt.show()
simulate_ts(model, k0, ts_length)