Recitation 5: Simulating from the life-cycle model
Part 1
Review the details of the simple life-cycle savings model. Today we’re only going to be thinking about the income process, but for the sake of completeness I include below all of the code to parameterize and solve that model.
usingDistributions,RandomusingLinearAlgebraΦ(x) =cdf(Normal(),x)functiontauchen(ρ,ση,Kϵ) sd = ση/sqrt(1-ρ^2) grid =range(-3sd,stop=3sd,length=Kϵ) Π =zeros(Kϵ,Kϵ) Δ = grid[2]-grid[1]for j=1:Kϵ Π[1,j] =Φ((grid[1] + Δ/2- ρ*grid[j])/ση) Π[end,j] =1-Φ((grid[end] - Δ/2- ρ*grid[j])/ση)for k=2:(Kϵ-1) Π[k,j] =Φ((grid[k] + Δ/2- ρ*grid[j])/ση) -Φ((grid[k] - Δ/2- ρ*grid[j])/ση)endendreturn Π,gridendu(c,σ) = c^(1-σ) / (1-σ)functionsolve_max(V,t,iϵ,ia,pars) (;agrid,ϵgrid,Π,σ,Ka,r,β) = pars cash =exp(pars.μ[t] + ϵgrid[iϵ]) + agrid[ia] amax =0 vmax =-Inf loop =true a =1while loop && a<Ka c = cash - agrid[a] / (1+r)if c>0#@views v = u(c,σ) + β * dot(Π[:,iϵ],V[:,a,t+1]) v =u(c,σ)for iϵ′ inaxes(V,1) v += β * Π[iϵ′,iϵ] * V[iϵ′,a,t+1]endif v>vmax vmax = v amax = aendelse loop =falseend a +=1#<- move one up the grid spaceendreturn amax,vmaxendfunctioniterate!(V,A,t,pars)for ia inaxes(V,2), iϵ inaxes(V,1) A[iϵ,ia,t],V[iϵ,ia,t] =solve_max(V,t,iϵ,ia,pars)endendfunctionterminal_values!(V,pars) (;σ,ψ,agrid) = parsfor ia inaxes(V,2), iϵ inaxes(V,1) V[iϵ,ia] = ψ *u(agrid[ia],σ)endendfunctionbackward_induction!(V,A,pars) (;ψ,σ,T,agrid) = pars# set the values at T+1 (bequest motives)@viewsterminal_values!(V[:,:,T+1],pars)for t inreverse(1:T)iterate!(V,A,t,pars)endendpars = (; T =45, β =0.95, σ =2,ρ =0.9,ση =0.1, μ =fill(2.,45), ψ =5., r =0.05)Ka =100Kϵ =5agrid =LinRange(0,pars.μ[1] * pars.T,Ka) #Π,ϵgrid =tauchen(pars.ρ,pars.ση,Kϵ)pars = (;pars...,Ka,agrid,Π,ϵgrid,Kϵ)V =zeros(pars.Kϵ,pars.Ka,pars.T+1)A =zeros(Int64,pars.Kϵ,pars.Ka,pars.T)backward_induction!(V,A,pars)@timebackward_induction!(V,A,pars)
0.011214 seconds
Part 2: The Stationary Distribution of \(\varepsilon\)
Suppose that \(\pi^*\) is the stationary distribution of \(\varepsilon\). It must obey:
\[ \pi^* = \Pi \pi^* \]
where the \(k\)th column of \(\Pi\) gives the transition probabilities given that the current value is \(\varepsilon_{k}\). This is equivalent to \(\pi^*\) being the eigenvector of \(\mathbf{I} - \Pi\) that corresponds to an eigenvalue of 0.
In principle, one can simulate draws from any distribution by simulating draws from the uniform distribution using rand() and using the inverse cdf. To see this, note that for any random variable:
\[ F_{X}(X) \sim \text{unif}[0,1] \]
and hence if \(U\sim\text{unif}[0,1]\):
\[ F_{X}^{-1}(U) \sim X \]
Here we will make use of the Distributions package and the Categorical distribution for simulation.
# this function simulates on pathfunctionsimulate_shocks!(ε,π_stat,Π) ε_current =rand(π_stat)for t ineachindex(ε) ε[t] = ε_current ε_current =rand(Π[ε_current])endendfunctionsimulate_shocks!(ε,pars) (;Π,π_stat) = pars π_stat_dist =Categorical(π_stat) Π_dist = [Categorical(Π[:,k]) for k inaxes(Π,2)]for n inaxes(ε,2)@viewssimulate_shocks!(ε[:,n],π_stat_dist,Π_dist)endendnsim =500ε_sim =zeros(Int64,pars.T,nsim)simulate_shocks!(ε_sim,pars)
Part 4: Holding Simulation Error Fixed
Now suppose we plan to use the simulated draws of \(\varepsilon\) to calculate some moments to match to the data. For example, we could try to match the lagged covariance to lagged covariance in the data. In practice we won’t have to simulation to do this, but for the sake of argument, let’s pretend we do. We could write the function to calculate moments as:
The moments keep jumping around! This is going to be a problem if we want to match the simulated moments to ones we have calculated in the data. The simulation error is moving the goalposts around in ways that we can’t predict. The solution is to fix simulation error by fixing the seed of our pseudorandom number generator. To see how, let’s rewrite the function: