from dataclasses import dataclass
from math import exp, sqrt
import numpy as np
from typing import Any, Tuple
import numpy.typing as npt
= npt.NDArray[1]
Vector = npt.NDArray[2] Matrix
Neoclassical model with time iteration
Our goal consists in this tutorial will be to solve numerically the neoclassical model using the time-iteration algorithms in two ways: - with a naive iterative algorithm - with a vectorized one (using numpy)
Remark: this tutorial uses typehints as a way to help structure the code. They can safely be ignored.
The neoclassical model
The representative agent maximizes intertemporal discounted utility of consumption
Production is
The law of motion for the capital depends on investment
The first order condition corresponding to the optimization problem is:
Exercise 1 What are the states of the problems? the controls? the exogenous shocks?
# states: (z,k)
# controls: (i,)
# everything else can be computed from it
# auxiliary: (y,c)
# shocks: (epsilon,)
# paramaters: alpha,beta,gamma,delta,sigma,rho
Exercise 2 Define an object to represent the model calibration (you can use a dataclass).
@dataclass
class Neoclassical:
= 0.3
alpha = 0.96
beta = 4.0
gamma = 0.1
delta = 0.9
rho = 0.01
sigma
= Neoclassical() m
Exercise 3 Define a function to compute the steady-state controls and states.
def steady_state(m: Neoclassical) -> Tuple[ Tuple[float, float], Tuple[float]]:
float = 0.0
z:float = ((1/m.beta-(1-m.delta))/m.alpha)**(1/(m.alpha-1))
k:= k*m.delta
i = (z,k) # tuple of states
s = (i,) #tuple of controls
x return (s,x)
Naive solution
Exercise 4 Define a cartesian grid on productivity and capital:
def get_grid(m: Neoclassical, size: Tuple[int, int]) -> Tuple[ Vector, Vector]:
= steady_state(m)
s,x
= m.sigma/sqrt(1-m.rho**2)
sigma_e = s[1] # steady state value of capital
kbar
= np.linspace( -2*sigma_e, 2*sigma_e, size[0])
zvec = np.linspace(0.5*kbar,kbar*1.5,size[1])
kvec return (zvec, kvec)
= get_grid(m, (10,10)) grid
Exercise 5 Define an initial guess representing the values of the controls on the grid
= (10,10)
N_1, N_2
= np.zeros( (N_1, N_2, 1) )
x0 0] = x
x0[:, :, ; x0
def initial_guess(m: Neoclassical, grid)->npt.NDArray[3]
# x0 = 3-dimensional array of size
= initial_guess(m, grid) x0
Exercise 6 Define a decision rule which interpolates the initial guess on any state
def phi(m: Neoclassical, s:: Tuple[float, float], x0::npt.NDArray[3])-> Tuple[float]
pass
Exercise 7 Compute the euler residual, for a given realization of the exogenous shocks.
def f(m: RBC, s: Tuple[float,float], x:Tuple[float], E: Tuple[float], grid, theta):
## grid is the discretized grid from before
## theta contains the values of the controls on the grid
pass
Exercise 8 Compute the expected euler residuals, integrating over all possible realizations of the exogenous shocks.
def F(m: RBC, s: Tuple[float,float], x:Tuple[float], grid, theta, discr:Tuple[Vector, Vector]):
## grid is the discretized grid from before
## theta contains the values of the controls on the grid
## discr contains the weights w,x of the gaussian quadrature
pass
Exercise 9 At the steady-state, find the optimal control, assuming future decisions are taken according to the initial guess.
# find x such that F(m,s,x,grid,theta_0,discr) = 0
Exercise 10 Solve for the optimal controls over the whole grid, still assuming future decisions are taken according to the initial guess.
# find x such that F(m,s,x,grid,theta_0,discr) = 0
Exercise 11 Implement the time iteration algorithm.
def time_iteration_0(m: Neoclassical, grid, discr, x0):
pass
Exercise 12 Time the result (and profile).
def time_iteration_0(m: Neoclassical, grid, discr, x0):
pass
Vectorization
There are at least two approaches to speed up the code, with the same algorithm: - avoid the interpretation cost by compiling the code (for instance using numba) - vectorize the operations over the whole grid
Exercise 13 Given
# grid_v =
Exercise 14 Rewrite the decision rule so that it can operate on a vector of states.
def phi_v(m: Neoclassical, s:: Matrix, x0::npt.NDArray[3])-> Matrix
pass
Exercise 15 Rewrite the euler residual function so that it can operate on a vector of states with the corresponding vector of controls.
def f_v(m: RBC, s: Matrix, x:Matrix, E: Tuple[float], grid, theta)->Matrix:
## grid is the discretized grid from before
## theta contains the values of the controls on the grid
pass
Exercise 16 Rewrite the integrated euler residual function so that it can operate on a vector of states with the corresponding vector of controls.
def F_v(m: RBC, s: Matrix, x:Matrix, grid, theta)->Matrix:
## grid is the discretized grid from before
## theta contains the values of the controls on the grid
pass
Exercise 17 Compute the jacobian of
#
Exercise 18 Solve for the matrix x
such that F_v(m, s, x:Matrix, grid, theta)=0
#
Exercise 19 Implement the time iteration algorithm.
#