using InstantiateFromURL github_project("QuantEcon/quantecon-notebooks-julia", version = "0.2.0") using LinearAlgebra, Statistics, BenchmarkTools, Plots, QuantEcon using SparseArrays using BenchmarkTools, Plots, QuantEcon, Parameters gr(fmt = :png); SimpleOG = @with_kw (B = 10, M = 5, α = 0.5, β = 0.9) function transition_matrices(g) @unpack B, M, α, β = g u(c) = c^α n = B + M + 1 m = M + 1 R = zeros(n, m) Q = zeros(n, m, n) for a in 0:M Q[:, a + 1, (a:(a + B)) .+ 1] .= 1 / (B + 1) for s in 0:(B + M) R[s + 1, a + 1] = (a≤s ? u(s - a) : -Inf) end end return (Q = Q, R = R) end g = SimpleOG(); Q, R = transition_matrices(g); function verbose_matrices(g) @unpack B, M, α, β = g u(c) = c^α #Matrix dimensions. The +1 is due to the 0 state. n = B + M + 1 m = M + 1 R = fill(-Inf, n, m) #Start assuming nothing is feasible Q = zeros(n,m,n) #Assume 0 by default #Create the R matrix #Note: indexing into matrix complicated since Julia starts indexing at 1 instead of 0 #but the state s and choice a can be 0 for a in 0:M for s in 0:(B + M) if a <= s #i.e. if feasible R[s + 1, a + 1] = u(s - a) end end end #Create the Q multi-array for s in 0:(B+M) #For each state for a in 0:M #For each action for sp in 0:(B+M) #For each state next period if( sp >= a && sp <= a + B) # The support of all realizations Q[s + 1, a + 1, sp + 1] = 1 / (B + 1) # Same prob of all end end @assert sum(Q[s + 1, a + 1, :]) ≈ 1 #Optional check that matrix is stochastic end end return (Q = Q, R = R) end ddp = DiscreteDP(R, Q, g.β); results = solve(ddp, PFI) fieldnames(typeof(results)) results.v results.sigma .- 1 results.num_iter stationary_distributions(results.mc)[1] g_2 = SimpleOG(β=0.99); Q_2, R_2 = transition_matrices(g_2); ddp_2 = DiscreteDP(R_2, Q_2, g_2.β) results_2 = solve(ddp_2, PFI) std_2 = stationary_distributions(results_2.mc)[1] bar(std_2, label = "stationary dist") B = 10 M = 5 α = 0.5 β = 0.9 u(c) = c^α n = B + M + 1 m = M + 1 s_indices = Int64[] a_indices = Int64[] Q = zeros(0, n) R = zeros(0) b = 1 / (B + 1) for s in 0:(M + B) for a in 0:min(M, s) s_indices = [s_indices; s + 1] a_indices = [a_indices; a + 1] q = zeros(1, n) q[(a + 1):((a + B) + 1)] .= b Q = [Q; q] R = [R; u(s-a)] end end ddp = DiscreteDP(R, Q, β, s_indices, a_indices); results = solve(ddp, PFI) α = 0.65 f(k) = k.^α u_log(x) = log(x) β = 0.95 grid_max = 2 grid_size = 500 grid = range(1e-6, grid_max, length = grid_size) C = f.(grid) .- grid' coord = repeat(collect(1:grid_size), 1, grid_size) #coordinate matrix s_indices = coord[C .> 0] a_indices = transpose(coord)[C .> 0] L = length(a_indices) R = u_log.(C[C.>0]); using SparseArrays Q = spzeros(L, grid_size) # Formerly spzeros for i in 1:L Q[i, a_indices[i]] = 1 end ddp = DiscreteDP(R, Q, β, s_indices, a_indices); results = solve(ddp, PFI) v, σ, num_iter = results.v, results.sigma, results.num_iter num_iter c = f(grid) - grid[σ] ab = α * β c1 = (log(1 - α * β) + log(α * β) * α * β / (1 - α * β)) / (1 - β) c2 = α / (1 - α * β) v_star(k) = c1 + c2 * log(k) c_star(k) = (1 - α * β) * k.^α plot(grid, [v v_star.(grid)], ylim = (-40, -32), lw = 2, label = ["discrete" "continuous"]) plot(grid, [c c_star.(grid)], lw = 2, label = ["discrete" "continuous"], legend = :topleft) maximum(abs(x - v_star(y)) for (x, y) in zip(v, grid)) maximum(abs(v[idx] - v_star(grid[idx])) for idx in 2:lastindex(v)) all(x -> x ≥ 0, diff(v)) @benchmark results = solve(ddp, PFI) results = solve(ddp, PFI); @benchmark res1 = solve(ddp, VFI, max_iter = 500, epsilon = 1e-4) res1 = solve(ddp, VFI, max_iter = 500, epsilon = 1e-4); res1.num_iter σ == res1.sigma @benchmark res2 = solve(ddp, MPFI, max_iter = 500, epsilon = 1e-4) res2 = solve(ddp, MPFI, max_iter = 500, epsilon = 1e-4); res2.num_iter σ == res2.sigma w_init = 5log.(grid) .- 25 # Initial condition n = 50 ws = [] colors = [] w = w_init for i in 0:n-1 w = bellman_operator(ddp, w) push!(ws, w) push!(colors, RGBA(0, 0, 0, i/n)) end plot(grid, w_init, ylims = (-40, -20), lw = 2, xlims = extrema(grid), label = "initial condition") plot!(grid, ws, label = "", color = reshape(colors, 1, length(colors)), lw = 2) plot!(grid, v_star.(grid), label = "true value function", color = :red, lw = 2) function compute_policies(n_vals...) c_policies = [] w = w_init for n in 1:maximum(n_vals) w = bellman_operator(ddp, w) if n in n_vals σ = compute_greedy(ddp, w) c_policy = f(grid) - grid[σ] push!(c_policies, c_policy) end end return c_policies end true_c = c_star.(grid) c_policies = compute_policies(2, 4, 6) plot_vecs = [c_policies[1] c_policies[2] c_policies[3] true_c true_c true_c] l1 = "approximate optimal policy" l2 = "optimal consumption policy" labels = [l1 l1 l1 l2 l2 l2] plot(grid, plot_vecs, xlim = (0, 2), ylim = (0, 1), layout = (3, 1), lw = 2, label = labels, size = (600, 800), title = ["2 iterations" "4 iterations" "6 iterations"]) discount_factors = (0.9, 0.94, 0.98) k_init = 0.1 k_init_ind = findfirst(collect(grid) .≥ k_init) sample_size = 25 ddp0 = DiscreteDP(R, Q, β, s_indices, a_indices) k_paths = [] labels = [] for β in discount_factors ddp0.beta = β res0 = solve(ddp0, PFI) k_path_ind = simulate(res0.mc, sample_size, init=k_init_ind) k_path = grid[k_path_ind.+1] push!(k_paths, k_path) push!(labels, "β = $β") end plot(k_paths, xlabel = "time", ylabel = "capital", ylim = (0.1, 0.3), lw = 2, markershape = :circle, label = reshape(labels, 1, length(labels)))