Florian Oswald, Sciences Po, 2019
Payoffs over time are $$U=\sum_{t=1}^{\infty}\beta^{t}u\left(s_{t},c_{t}\right) $$ where $\beta<1$ is a discount factor, $s_{t}$ is the state, $c_{t}$ is the control.
The state (vector) evolves as $s_{t+1}=h(s_{t},c_{t})$.
Theorem. Assume that $u(s,s')$ is real-valued, continuous, and bounded, that $\beta\in(0,1)$, and that the constraint set $\Gamma(s)$ is nonempty, compact, and continuous. Then there exists a unique function $v(s)$ that solves the above functional equation.
Proof. [@stokeylucas] theoreom 4.6.
and we employ the followign numerical approximation: $$ V(k_i) = \max_{i'=1,2,\dots,n} u(f(k_i) - k_{i'}) + \beta V(i') $$
The iteration is then on successive iterates of $V$: The LHS gets updated in each iteration!
alpha = 0.65
beta = 0.95
grid_max = 2 # upper bound of capital grid
n = 150 # number of grid points
N_iter = 3000 # number of iterations
kgrid = 1e-2:(grid_max-1e-2)/(n-1):grid_max # equispaced grid
f(x) = x^alpha # defines the production function f(k)
tol = 1e-9
1.0e-9
ab = alpha * beta
c1 = (log(1 - ab) + log(ab) * ab / (1 - ab)) / (1 - beta)
c2 = alpha / (1 - ab)
# optimal analytical values
v_star(k) = c1 .+ c2 .* log.(k)
k_star(k) = ab * k.^alpha
c_star(k) = (1-ab) * k.^alpha
ufun(x) = log.(x)
ufun (generic function with 1 method)
kgrid[4]
0.04026943624161074
# Bellman Operator
# inputs
# `grid`: grid of values of state variable
# `v0`: current guess of value function
# output
# `v1`: next guess of value function
# `pol`: corresponding policy function
#takes a grid of state variables and computes the next iterate of the value function.
function bellman_operator(grid,v0)
v1 = zeros(n) # next guess
pol = zeros(Int,n) # policy function
w = zeros(n) # temporary vector
# loop over current states
# current capital
for (i,k) in enumerate(grid)
# loop over all possible kprime choices
for (iprime,kprime) in enumerate(grid)
if f(k) - kprime < 0 #check for negative consumption
w[iprime] = -Inf
else
w[iprime] = ufun(f(k) - kprime) + beta * v0[iprime]
end
end
# find maximal choice
v1[i], pol[i] = findmax(w) # stores Value und policy (index of optimal choice)
end
return (v1,pol) # return both value and policy function
end
# VFI iterator
#
## input
# `n`: number of grid points
# output
# `v_next`: tuple with value and policy functions after `n` iterations.
function VFI()
v_init = zeros(n) # initial guess
for iter in 1:N_iter
v_next = bellman_operator(kgrid,v_init) # returns a tuple: (v1,pol)
# check convergence
if maximum(abs,v_init.-v_next[1]) < tol
verrors = maximum(abs,v_next[1].-v_star(kgrid))
perrors = maximum(abs,kgrid[v_next[2]].-k_star(kgrid))
println("Found solution after $iter iterations")
println("maximal value function error = $verrors")
println("maximal policy function error = $perrors")
return v_next
elseif iter==N_iter
warn("No solution found after $iter iterations")
return v_next
end
v_init = v_next[1] # update guess
end
end
# plot
using Plots
function plotVFI()
v = VFI()
p = Any[]
# value and policy functions
push!(p,plot(kgrid,v[1],
lab="V",
ylim=(-50,-30),legend=:bottomright),
plot(kgrid,kgrid[v[2]],
lab="policy",legend=:bottomright))
# errors of both
push!(p,plot(kgrid,v[1].-v_star(kgrid),
lab="V error",legend=:bottomright),
plot(kgrid,kgrid[v[2]].-k_star(kgrid),
lab="policy error",legend=:bottomright))
plot(p...,layout=grid(2,2) )
end
plotVFI()
┌ Info: Recompiling stale cache file /Users/florian.oswald/.julia/compiled/v1.1/Plots/ld3vC.ji for Plots [91a5bcdd-55d7-5caf-9e0b-520d859cae80] └ @ Base loading.jl:1184
Found solution after 418 iterations maximal value function error = 0.09528625737115703 maximal policy function error = 0.011773635481976297
kgrid
.Interpolations.jl
to linearly interpolate V.interpolate((grid,),v,Gridded(Linear()))
Optim::optimize()
to perform the maximization.optimize(objective, lb,ub)
kgrid
0.01:0.013355704697986578:2.0
using Interpolations
using Optim
function bellman_operator2(grid,v0)
v1 = zeros(n) # next guess
pol = zeros(n) # consumption policy function
Interp = interpolate((collect(grid),), v0, Gridded(Linear()) )
Interp = extrapolate(Interp,Interpolations.Flat())
# loop over current states
# of current capital
for (i,k) in enumerate(grid)
objective(c) = - (log.(c) + beta * Interp(f(k) - c))
# find max of ojbective between [0,k^alpha]
res = optimize(objective, 1e-6, f(k)) # Optim.jl
pol[i] = f(k) - res.minimizer # k'
v1[i] = -res.minimum
end
return (v1,pol) # return both value and policy function
end
function VFI2()
v_init = zeros(n) # initial guess
for iter in 1:N_iter
v_next = bellman_operator2(kgrid,v_init) # returns a tuple: (v1,pol)
# check convergence
if maximum(abs,v_init.-v_next[1]) < tol
verrors = maximum(abs,v_next[1].-v_star(kgrid))
perrors = maximum(abs,v_next[2].-k_star(kgrid))
println("continuous VFI:")
println("Found solution after $iter iterations")
println("maximal value function error = $verrors")
println("maximal policy function error = $perrors")
return v_next
elseif iter==N_iter
warn("No solution found after $iter iterations")
return v_next
end
v_init = v_next[1] # update guess
end
return nothing
end
function plotVFI2()
v = VFI2()
p = Any[]
# value and policy functions
push!(p,plot(kgrid,v[1],
lab="V",
ylim=(-50,-30),legend=:bottomright),
plot(kgrid,v[2],
lab="policy",legend=:bottomright))
# errors of both
push!(p,plot(kgrid,v[1].-v_star(kgrid),
lab="V error",legend=:bottomright),
plot(kgrid,v[2].-k_star(kgrid),
lab="policy error",legend=:bottomright))
plot(p...,layout=grid(2,2) )
end
plotVFI2()
continuous VFI: Found solution after 418 iterations maximal value function error = 0.04828453368161689 maximal policy function error = 0.004602693711777683
# Your turn!
using Roots
function policy_iter(grid,c0,u_prime,f_prime)
c1 = zeros(length(grid)) # next guess
pol_fun = extrapolate(interpolate((collect(grid),), c0, Gridded(Linear()) ) , Interpolations.Flat())
# loop over current states
# of current capital
for (i,k) in enumerate(grid)
objective(c) = u_prime(c) - beta * u_prime(pol_fun(f(k)-c)) * f_prime(f(k)-c)
c1[i] = fzero(objective, 1e-10, f(k)-1e-10)
end
return c1
end
uprime(x) = 1.0 ./ x
fprime(x) = alpha * x.^(alpha-1)
function PFI()
c_init = kgrid
for iter in 1:N_iter
c_next = policy_iter(kgrid,c_init,uprime,fprime)
# check convergence
if maximum(abs,c_init.-c_next) < tol
perrors = maximum(abs,c_next.-c_star(kgrid))
println("PFI:")
println("Found solution after $iter iterations")
println("max policy function error = $perrors")
return c_next
elseif iter==N_iter
warn("No solution found after $iter iterations")
return c_next
end
c_init = c_next # update guess
end
end
function plotPFI()
v = PFI()
plot(kgrid,[v v.-c_star(kgrid)],
lab=["policy" "error"],
legend=:bottomright,
layout = 2)
end
plotPFI()
PFI: Found solution after 39 iterations max policy function error = 7.301895796647112e-5
create an approximation to $c^*$: find $$ \bar{c} \equiv \sum_{i=0}^n a_i k^i $$
which nearly solves
$$\mathcal{N}(c^*)=0 $$
What's small in some sense?
using CompEcon
using Plots
using NLsolve
function proj(n=25)
alpha = 1.0
eta = 1.5
a = 0.1
b = 3.0
basis = fundefn(:cheb,n,a,b)
p = funnode(basis)[1] # collocation points
c0 = ones(n)*0.3
function resid!(c::Vector,result::Vector,p,basis,alpha,eta)
# your turn!
q = funeval(c,basis,p)[1]
q2 = similar(q)
for i in eachindex(q2)
if q[i] < 0
q2[i] = -20.0
else
q2[i] = sqrt(q[i])
end
end
result[:] = p.+ q .*((-1/eta)*p.^(eta+1)) .- alpha*q2 .- q.^2
end
f_closure(r::Vector,x::Vector) = resid!(x,r,p,basis,alpha,eta)
res = nlsolve(f_closure,c0)
println(res)
# plot residual function
x = collect(range(a, stop = b, length = 501))
y = similar(x)
resid!(res.zero,y,x,basis,alpha,eta);
y = funeval(res.zero,basis,x)[1]
pl = Any[]
push!(pl,plot(x,y,title="residual function"))
# plot supply functions at levels 1,10,20
# plot demand function
y = funeval(res.zero,basis,x)[1]
p2 = plot(y,x,label="supply 1")
plot!(10*y,x,label="supply 10")
plot!(20*y,x,label="supply 20")
d = x.^(-eta)
plot!(d,x,label="Demand")
push!(pl,p2)
plot(pl...,layout=2)
end
proj()
Results of Nonlinear Solver Algorithm * Algorithm: Trust-region with dogleg and autoscaling * Starting Point: [0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3] * Zero: [0.248768, 0.0838916, -0.13965, 0.0447411, 0.00701804, -0.0135233, 0.00715223, -0.00229524, 0.000329096, 0.000224355, -0.000290968, 0.000210507, -0.000107876, 3.32945e-5, 4.04856e-6, -1.48411e-5, 1.28265e-5, -7.34842e-6, 2.74595e-6, -1.36709e-7, -8.28018e-7, 8.69542e-7, -5.79429e-7, 2.87391e-7, -1.02456e-7] * Inf-norm of residuals: 0.000000 * Iterations: 9 * Convergence: true * |x - x'| < 0.0e+00: false * |f(x)| < 1.0e-08: true * Function Calls (f): 8 * Jacobian Calls (df/dx): 7
Starts in period $T$ with $c_T^* = M_T$. For all preceding periods:
# minimal EGM implementation, go here: https://github.com/floswald/DCEGM.jl/blob/master/src/dc_algo.jl#L4
#Â try out:
# ] dev https://github.com/floswald/DCEGM.jl
using DCEGM
DCEGM.minimal_EGM(dplot = true);
Optimal consumption in 6 period model:
where the value for retirees stays the same.