├── .gitignore ├── Slides ├── EslamiPhelan.pdf ├── PhD_Macro_Comp_1.pdf ├── PhD_Macro_Comp_2.pdf ├── PhD_Macro_Comp_3.pdf ├── PhD_Macro_Comp_4.pdf ├── PhD_Macro_Comp_5.pdf ├── PhD_Macro_Comp_6.pdf ├── PhD_Macro_Comp_7.pdf ├── PhD_Macro_Comp_8.pdf ├── PhD_Macro_Comp_9.pdf ├── PhD_Macro_Comp_10.pdf ├── EslamiPhelan_Slides.pdf ├── PhD_Macro_Comp_10_Handout.pdf ├── PhD_Macro_Comp_1_Handout.pdf ├── PhD_Macro_Comp_2_Handout.pdf ├── PhD_Macro_Comp_3_Handout.pdf ├── PhD_Macro_Comp_4_Handout.pdf ├── PhD_Macro_Comp_5_Handout.pdf ├── PhD_Macro_Comp_6_Handout.pdf ├── PhD_Macro_Comp_7_Handout.pdf ├── PhD_Macro_Comp_8_Handout.pdf └── PhD_Macro_Comp_9_Handout.pdf ├── Problem_Sets ├── Problem_Set_1.pdf ├── Problem_Set_10.pdf ├── Problem_Set_2.pdf ├── Problem_Set_3.pdf ├── Problem_Set_4.pdf ├── Problem_Set_5.pdf ├── Problem_Set_6.pdf ├── Problem_Set_7.pdf ├── Problem_Set_8.pdf └── Problem_Set_9.pdf ├── Grad Course Outline - 9614 - Ocampo.docx ├── Julia_Code ├── Lecture_1_Julia │ ├── My_first_fig.pdf │ ├── Outside_Script.jl │ └── Julia_Test.jl ├── Lecture_7_RCE │ └── Tax_Graphs.jl ├── Lecture_4_Optimization │ ├── Sobol_Test_Lecture_4_Code.jl │ └── Optimization_Lecture_4_Code.jl ├── Lecture_2_VFI │ ├── VFI_Graphs.jl │ └── VFI_Code_Lecture_2.jl ├── Lecture_3_Interpolation │ ├── Scaled_Interpolation_Functions.jl │ └── Interpolation_Lecture_3_Code.jl ├── Lecture_5_Integration │ └── Integration_Lecture_5.jl ├── Lecture_6_EGM │ └── EGM_Lecture_6.jl ├── VFI_Toolbox.jl └── Lecture_8_Aiyagari │ └── Aiyagari_Lecture_8.jl └── Readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | Materials 2 | *Figures 3 | \#* 4 | *.lyx 5 | *.lyx~ 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /Slides/EslamiPhelan.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/EslamiPhelan.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_1.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_2.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_3.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_4.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_5.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_6.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_7.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_7.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_8.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_8.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_9.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_9.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_10.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_10.pdf -------------------------------------------------------------------------------- /Problem_Sets/Problem_Set_1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Problem_Sets/Problem_Set_1.pdf -------------------------------------------------------------------------------- /Problem_Sets/Problem_Set_10.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Problem_Sets/Problem_Set_10.pdf -------------------------------------------------------------------------------- /Problem_Sets/Problem_Set_2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Problem_Sets/Problem_Set_2.pdf -------------------------------------------------------------------------------- /Problem_Sets/Problem_Set_3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Problem_Sets/Problem_Set_3.pdf -------------------------------------------------------------------------------- /Problem_Sets/Problem_Set_4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Problem_Sets/Problem_Set_4.pdf -------------------------------------------------------------------------------- /Problem_Sets/Problem_Set_5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Problem_Sets/Problem_Set_5.pdf -------------------------------------------------------------------------------- /Problem_Sets/Problem_Set_6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Problem_Sets/Problem_Set_6.pdf -------------------------------------------------------------------------------- /Problem_Sets/Problem_Set_7.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Problem_Sets/Problem_Set_7.pdf -------------------------------------------------------------------------------- /Problem_Sets/Problem_Set_8.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Problem_Sets/Problem_Set_8.pdf -------------------------------------------------------------------------------- /Problem_Sets/Problem_Set_9.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Problem_Sets/Problem_Set_9.pdf -------------------------------------------------------------------------------- /Slides/EslamiPhelan_Slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/EslamiPhelan_Slides.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_10_Handout.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_10_Handout.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_1_Handout.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_1_Handout.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_2_Handout.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_2_Handout.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_3_Handout.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_3_Handout.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_4_Handout.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_4_Handout.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_5_Handout.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_5_Handout.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_6_Handout.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_6_Handout.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_7_Handout.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_7_Handout.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_8_Handout.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_8_Handout.pdf -------------------------------------------------------------------------------- /Slides/PhD_Macro_Comp_9_Handout.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Slides/PhD_Macro_Comp_9_Handout.pdf -------------------------------------------------------------------------------- /Grad Course Outline - 9614 - Ocampo.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Grad Course Outline - 9614 - Ocampo.docx -------------------------------------------------------------------------------- /Julia_Code/Lecture_1_Julia/My_first_fig.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocamp020/PhD_Macro_Course_Western/HEAD/Julia_Code/Lecture_1_Julia/My_first_fig.pdf -------------------------------------------------------------------------------- /Julia_Code/Lecture_1_Julia/Outside_Script.jl: -------------------------------------------------------------------------------- 1 | # Test Program for Julia 2 | # Sergio Ocampo 3 | # August 2020 4 | # This script is intended to be called from another script 5 | 6 | x = 1 7 | y = 2 8 | -------------------------------------------------------------------------------- /Julia_Code/Lecture_7_RCE/Tax_Graphs.jl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | using Plots 5 | cd("./Dropbox/Teaching/PhD_Macro_Comp/Julia_Code/Lecture_7_RCE/") 6 | mkpath("Figures") 7 | 8 | 9 | y_bt=range(1,100,length=1000) 10 | 11 | y_at(τ,θ,y_bar)=(1-τ)*y_bt.^(1-θ).+y_bar 12 | dy_at(τ,θ,y_bar)=(1-θ)*(1-τ)*y_bt.^(-θ) 13 | 14 | y_at_vec = y_at(0.25,0.1,0) 15 | dy_at_vec = dy_at(0.25,0.1,0) 16 | 17 | gr() 18 | plot(title="Progressive Taxes: τ=0.25, θ=0.1",legend=:bottomright,foreground_color_legend = nothing,background_color_legend = nothing) 19 | plot!(y_bt,y_bt.-y_at_vec ,linewidth=2.5,label="Tax Level") 20 | plot!(y_bt,100*(1 .- dy_at_vec) ,linewidth=2.5,label="Marginal Tax (in pp)") 21 | plot!(y_bt,100*(1 .- y_at_vec./y_bt) ,linewidth=2.5,label="Average Tax (in pp)") 22 | xlabel!("Before Tax Income") 23 | savefig("./Figures/Progressive_Tax.pdf") 24 | 25 | y_at_vec = y_at(0.25,-0.05,0) 26 | dy_at_vec = dy_at(0.25,-0.05,0) 27 | 28 | gr() 29 | plot(title="Regressive Taxes: τ=0.25, θ=-0.05",legend=:topright,foreground_color_legend = nothing,background_color_legend = nothing) 30 | plot!(y_bt,y_bt.-y_at_vec ,linewidth=2.5,label="Tax Level") 31 | plot!(y_bt,100*(1 .- dy_at_vec) ,linewidth=2.5,label="Marginal Tax (in pp)") 32 | plot!(y_bt,100*(1 .- y_at_vec./y_bt) ,linewidth=2.5,label="Average Tax (in pp)") 33 | xlabel!("Before Tax Income") 34 | savefig("./Figures/Regressive_Tax_Transfer.pdf") 35 | 36 | 37 | y_at_vec = y_at(0.25,0,0) 38 | dy_at_vec = dy_at(0.25,0,0) 39 | 40 | gr() 41 | plot(title="Linear Taxes: τ=0.25, θ=0",legend=:topright,foreground_color_legend = nothing,background_color_legend = nothing) 42 | plot!(y_bt,y_bt.-y_at_vec ,linewidth=2.5,label="Tax Level") 43 | plot!(y_bt,100*(1 .- dy_at_vec) ,linewidth=2.5,label="Marginal Tax (in pp)") 44 | plot!(y_bt,100*(1 .- y_at_vec./y_bt) ,linewidth=2.5,label="Average Tax (in pp)") 45 | xlabel!("Before Tax Income") 46 | savefig("./Figures/Linear_Tax_Transfer.pdf") 47 | 48 | y_at_vec = y_at(0.25,0.1,2) 49 | dy_at_vec = dy_at(0.25,0.1,2) 50 | 51 | gr() 52 | plot(title="Progressive Taxes with Transfer: τ=0.25, θ=0.1, y_bar=2",legend=:bottomright,foreground_color_legend = nothing,background_color_legend = nothing) 53 | plot!(y_bt,y_bt.-y_at_vec ,linewidth=2.5,label="Tax Level") 54 | plot!(y_bt,100*(1 .- dy_at_vec) ,linewidth=2.5,label="Marginal Tax (in pp)") 55 | plot!(y_bt,100*(1 .- y_at_vec./y_bt) ,linewidth=2.5,label="Average Tax (in pp)") 56 | xlabel!("Before Tax Income") 57 | savefig("./Figures/Progressive_Tax_Transfer.pdf") 58 | -------------------------------------------------------------------------------- /Julia_Code/Lecture_1_Julia/Julia_Test.jl: -------------------------------------------------------------------------------- 1 | # Test Program for Julia 2 | # Sergio Ocampo 3 | # August 2020 4 | 5 | cd("./Dropbox/Teaching/PhD_Macro_Comp/Julia_Code/Lecture_1_Julia") 6 | 7 | #----------------------------------------------------------- 8 | # Start with a print statement 9 | println(" ") 10 | println("------------------------") 11 | println("Hello World") 12 | println(" I can type latex in Julia: α β ϵ ♢ ±") 13 | 14 | # We can also print and evaluate 15 | println(" I will print some additions") 16 | println(" 3+2=",3+2) 17 | println(" 3+2.1=",3+2.1) 18 | println(" I can merge strings using *") 19 | println(" String number 1 "*"and "*"String number 2") 20 | println(" I can also include evaluations inside strings with dollar sign") 21 | a, b, c = 2, 3, 2+3 22 | println(" Sum $a+$b=$c or 2+3=$(a+b) and also its type $(typeof(a))") 23 | println(" I can also use the '_' to separate digits, it is ignored:") 24 | println(" 1_000_123=",1_000_123) 25 | println("------------------------") 26 | println(" ") 27 | 28 | #----------------------------------------------------------- 29 | # We can define logical expressions 30 | println(" ") 31 | println("------------------------") 32 | println("Logicals 'true' and 'false' are recognized by Julia") 33 | println(" ",true) 34 | println(" ",false) 35 | println("The '!' operator is a not") 36 | println(" !true = ",!true) 37 | println(" !false = ",!false) 38 | println("The '&&' operator is an and") 39 | println(" true && true = ",true && true) 40 | println(" true && false = ",true && false) 41 | println("The '||' operator is an or") 42 | println(" true || true = ",true || true) 43 | println(" true || false = ",true || false) 44 | println("Comparing numbers and logicals") 45 | println(" 2> 1 = ",2>1) 46 | println(" 2< 1 = ",2<1) 47 | println(" 2==1 = ",2==1) 48 | println("Julia can compare across types") 49 | println(" 2.0== 2 = ",2.0==2) 50 | println(" 2.0===2 = ",2.0===2) 51 | println(" The '===' is a strict comparisson operator") 52 | println("Careful not to use bitwise and (&) or (|)") 53 | println("------------------------") 54 | println(" ") 55 | 56 | 57 | #----------------------------------------------------------- 58 | # Defining arrays (matrices) 59 | println(" ") 60 | println("------------------------") 61 | α = 1 62 | β = 2 63 | A = [α,β] 64 | println("Array A=",A) 65 | B = Array{Float64,2}(undef,2,3) 66 | println("Undefined array B:",B) 67 | B = [1 2 3; 4 5.3 6.1] 68 | println("Assigned array B:",B) 69 | println("------------------------") 70 | println(" ") 71 | 72 | 73 | 74 | #----------------------------------------------------------- 75 | # Plots 76 | using Plots 77 | x = 0:0.5:6 78 | y = cos.(x) 79 | gr() 80 | plot(x,y,linewidth=2,marker=(:diamond,9),markercolor=RGB(0.1,0.1,0.1)) 81 | plot(x,y,linetype=:scatter) 82 | savefig("./My_first_fig.pdf") 83 | # See list of attributes with plotattr(:Series), plotattr(:Plot), plotattr(:Axis) 84 | 85 | 86 | #----------------------------------------------------------- 87 | # Call Outside Script 88 | include("./Outside_Script.jl") 89 | println(" ") 90 | println("------------------------") 91 | println("From Ouside_Script.jl: X=",x," and Y=",y) 92 | println("------------------------") 93 | println(" ") 94 | 95 | 96 | #= 97 | I can also comment multiple lines 98 | All these lines are commented 99 | Look at this! They are all commented! 100 | =# 101 | -------------------------------------------------------------------------------- /Julia_Code/Lecture_4_Optimization/Sobol_Test_Lecture_4_Code.jl: -------------------------------------------------------------------------------- 1 | # Sobol numbers in julia 2 | # Sergio Ocampo 3 | # September 2020 4 | # Draw Sobol numbers and compare with uniform random 5 | cd() # Go to root directory 6 | cd("./Dropbox/Teaching/PhD_Macro_Comp/Julia_Code/Lecture_4_Optimization/") 7 | mkpath("Figures/Sobol") 8 | using Plots 9 | using Sobol # Pkg.add("Sobol") # https://github.com/stevengj/Sobol.jl 10 | using Random 11 | println(" ") 12 | println("------------------------") 13 | println("Sobol numbers in Julia") 14 | println("PWD: ",pwd()) 15 | println("This code uses Plots, Sobol, Random") 16 | println("------------------------") 17 | println(" ") 18 | 19 | 20 | #----------------------------------------------------------- 21 | #----------------------------------------------------------- 22 | # Set random seed 23 | Random.seed!(3486); 24 | 25 | #----------------------------------------------------------- 26 | #----------------------------------------------------------- 27 | # One Dimension 28 | 29 | # Get Sobol sequence 30 | s = SobolSeq(1) 31 | s_vec = Float64[] 32 | # Get plots comparing numbers in one dimension 33 | for n=(10,20,50) 34 | # Get frist n elements of sobol sequence 35 | global s_vec = [s_vec ; hcat([next!(s) for i = 1:(n-length(s_vec))]...)'] 36 | # Get n random numbers 37 | r_vec = rand(n,1) 38 | # Plot results 39 | gr() 40 | plt = plot([0;1],[1 2;1 2],linewidth=2,linecolor=RGB(0.6,0.6,0.6),label=nothing,title="Grid Points - n=$n") 41 | scatter!(r_vec,1*ones(n),marker=(:diamond,6),markercolor=1,label=nothing) 42 | scatter!(s_vec,2*ones(n),marker=(:star4,6) ,markercolor=2,label=nothing) 43 | xlims!(0,1); ylims!(0,3); yticks!([1,2], ["Uniform", "Sobol"]) 44 | savefig("./Figures/Sobol/Sobol_vs_Unifrom_1D_n$n.pdf") 45 | end 46 | 47 | #----------------------------------------------------------- 48 | #----------------------------------------------------------- 49 | # Two Dimensions 50 | 51 | # Get Sobol sequence 52 | s = SobolSeq(2) 53 | s_vec = zeros(0,2) 54 | # Get plots comparing numbers in one dimension 55 | for n=(10,20,50,1024) 56 | # Get frist n elements of sobol sequence 57 | global s_vec = [s_vec ; hcat([next!(s) for i = 1:(n-length(s_vec[:,1]))]...)'] 58 | # Plot results 59 | gr() 60 | plt1 = plot(title="Sobol Points - n=$n",legend=:outerright,foreground_color_legend = nothing,background_color_legend = nothing) 61 | s_scatter = scatter!(s_vec[:,1],s_vec[:,2],marker=(:star4,6),label=nothing,color=2) 62 | xlims!(0,1); ylims!(0,1); 63 | savefig("./Figures/Sobol/Sobol_2D_n$n.pdf") 64 | # Get n random numbers 65 | r_vec = rand(n,2) 66 | # Plot results 67 | gr() 68 | plt2 = plot(title="Uniform Points - n=$n",legend=:outerright,foreground_color_legend = nothing,background_color_legend = nothing) 69 | r_scatter = scatter!(r_vec[:,1],r_vec[:,2],marker=(:diamond,6),label=nothing,color=1) 70 | xlims!(0,1); ylims!(0,1); 71 | savefig("./Figures/Sobol/Unifrom_2D_n$n.pdf") 72 | # Joint Figure 73 | gr() 74 | plt2 = plot(legend=:outerbottom,foreground_color_legend = nothing,background_color_legend = nothing) 75 | r_scatter = scatter!(r_vec[:,1],r_vec[:,2],marker=(:diamond,6),label="Uniform",color=1) 76 | s_scatter = scatter!(s_vec[:,1],s_vec[:,2],marker=(:star4,6),label="Sobol",color=2) 77 | xlims!(0,1); ylims!(0,1); 78 | savefig("./Figures/Sobol/Sobol_vs_Unifrom_2D_n$n.pdf") 79 | end 80 | -------------------------------------------------------------------------------- /Julia_Code/Lecture_2_VFI/VFI_Graphs.jl: -------------------------------------------------------------------------------- 1 | # VFI Program for Julia 2 | # Sergio Ocampo 3 | # September 2020 4 | # NGM with log utility and full depreciation 5 | # Solve: V(k) = max{ log(zk^alpha-k') +beta*V(k') } 6 | # Solution by grid search: k_grid = {k_1,...,k_I} 7 | # Graphing 8 | gr() 9 | # Value Function Analytical vs 200 10 | plot(k_grid_200,V_200a,linewidth=3,label = "Analytical Solution",title="Value Function",legend=(0.75,0.2),foreground_color_legend = nothing,background_color_legend = nothing) 11 | plot!(k_grid_50,V_50,linetype=:scatter,marker=(:diamond,6),markercolor=RGB(0.5,0.1,0.1),label="VFI - n_k=50") 12 | plot!(k_grid_200,V_200,linetype=:scatter,marker=(:diamond,3),markercolor=RGB(0.1,0.1,0.1),label = "VFI - n_k=200") 13 | xlabel!("Capital") 14 | ylabel!("Value") 15 | savefig("./Figures/VFI_"*VFI_Type*"_V.pdf") 16 | # Capital Policy Function Analytical vs 200 17 | plot(k_grid_200,k_grid_200,lw=1,linecolor=RGB(0.6,0.6,0.6),label=nothing) 18 | plot!(k_grid_200,G_kp_200a,linewidth=3,label = "Analytical Solution",title="Policy Function - K",legend=(0.75,0.2),foreground_color_legend = nothing,background_color_legend = nothing) 19 | plot!(k_grid_50,k_grid_50[G_kp_50],linetype=:scatter,marker=(:diamond,6),markercolor=RGB(0.5,0.1,0.1),label="VFI - n_k=50") 20 | plot!(k_grid_200,k_grid_200[G_kp_200],linetype=:scatter,marker=(:diamond,3),markercolor=RGB(0.1,0.1,0.1),label = "VFI - n_k=200") 21 | xlabel!("Capital") 22 | ylabel!("Capital") 23 | savefig("./Figures/VFI_"*VFI_Type*"_G_kp.pdf") 24 | # Euler Error 200 25 | plot([0,2*k_ss],[0,0],lw=1,linecolor=RGB(0.6,0.6,0.6),label=nothing,title = "Euler Equation Error (%)") 26 | plot!(k_grid_50,Euler_50,linetype=:scatter,marker=(:diamond,6),markercolor=RGB(0.5,0.1,0.1),label="VFI - n_k=50") 27 | plot!(k_grid_200,Euler_200,linetype=:scatter,marker=(:diamond,3),markercolor=RGB(0.1,0.1,0.1),label="VFI - n_k=200") 28 | xlabel!("Capital") 29 | ylabel!("Percentage Points") 30 | savefig("./Figures/VFI_"*VFI_Type*"_Euler.pdf") 31 | 32 | 33 | 34 | #----------------------------------------------------------- 35 | #----------------------------------------------------------- 36 | # Caution with Euler Errors 37 | 38 | # Possible scenarios 39 | x_vec = range(-1,1,1000) 40 | y_vec = x_vec.^3 41 | gr() 42 | plot([-1,1],[0,0],lw=1,linecolor=RGB(0.6,0.6,0.6),label=nothing) 43 | plot!(x_vec,y_vec,lw=2,label=nothing) 44 | ylims!(-1,1) 45 | xlabel!("Deviations from Policy") 46 | ylabel!("Euler Error (%)") 47 | savefig("./Figures/Euler_Error_Warning_1.pdf") 48 | y_vec = tan.(1.5*x_vec) 49 | gr() 50 | plot([-1,1],[0,0],lw=1,linecolor=RGB(0.6,0.6,0.6),label=nothing) 51 | plot!(x_vec,y_vec,lw=2,label=nothing) 52 | ylims!(-1,1) 53 | xlabel!("Deviations from Policy") 54 | ylabel!("Euler Error (%)") 55 | savefig("./Figures/Euler_Error_Warning_2.pdf") 56 | y_vec = x_vec.^2 57 | gr() 58 | plot([-1,1],[0,0],lw=1,linecolor=RGB(0.6,0.6,0.6),label=nothing) 59 | plot!(x_vec,y_vec,lw=2,label=nothing) 60 | ylims!(-1,1) 61 | xlabel!("Deviations from Policy") 62 | ylabel!("Euler Error (%)") 63 | savefig("./Figures/Euler_Error_Warning_3.pdf") 64 | y_vec = x_vec.^4 65 | gr() 66 | plot([-1,1],[0,0],lw=1,linecolor=RGB(0.6,0.6,0.6),label=nothing) 67 | plot!(x_vec,y_vec,lw=2,label=nothing) 68 | ylims!(-1,1) 69 | xlabel!("Deviations from Policy") 70 | ylabel!("Euler Error (%)") 71 | savefig("./Figures/Euler_Error_Warning_4.pdf") 72 | 73 | # Low capital 74 | k_0 = 1e-3 75 | kp_0,c_0 = G_analytical(k_0,p) 76 | kp_vec = range(kp_0-1e-3,kp_0+1e-3,1000) 77 | kpp_vec, cp_vec = G_analytical(kp_vec,p) 78 | Euler_vec = [Euler_Error(k_0,kp_vec[i],kpp_vec[i],p) for i in 1:1000] 79 | gr() 80 | plot([-1e-3,1e-3],[0,0],lw=1,linecolor=RGB(0.6,0.6,0.6),label=nothing,title = "Sensitivity of Euler Error for Low K (%)") 81 | plot!(kp_vec.-kp_0,Euler_vec,lw=2,label=nothing) 82 | xlims!(-1e-3,1e-3) 83 | xlabel!("Deviations from Policy (in levels)") 84 | ylabel!("Euler Error (%)") 85 | savefig("./Figures/Euler_Error_Test_Low_K.pdf") 86 | # Steady State capital 87 | k_0 = k_ss 88 | kp_0,c_0 = G_analytical(k_0,p) 89 | kp_vec = range(kp_0-1e-3,kp_0+1e-3,1000) 90 | kpp_vec, cp_vec = G_analytical(kp_vec,p) 91 | Euler_vec = [Euler_Error(k_0,kp_vec[i],kpp_vec[i],p) for i in 1:1000] 92 | gr() 93 | plot([-1e-3,1e-3],[0,0],lw=1,linecolor=RGB(0.6,0.6,0.6),label=nothing,title = "Sensitivity of Euler Error for SS K (%)") 94 | plot!(kp_vec.-kp_0,Euler_vec,lw=2,label=nothing) 95 | xlims!(-1e-3,1e-3) 96 | xlabel!("Deviations from Policy (in levels)") 97 | ylabel!("Euler Error (%)") 98 | savefig("./Figures/Euler_Error_Test_Med_K.pdf") 99 | # High capital 100 | k_0 = 1.5*k_ss 101 | kp_0,c_0 = G_analytical(k_0,p) 102 | kp_vec = range(kp_0-1e-3,kp_0+1e-3,1000) 103 | kpp_vec, cp_vec = G_analytical(kp_vec,p) 104 | Euler_vec = [Euler_Error(k_0,kp_vec[i],kpp_vec[i],p) for i in 1:1000] 105 | gr() 106 | plot([-1e-3,1e-3],[0,0],lw=1,linecolor=RGB(0.6,0.6,0.6),label=nothing,title = "Sensitivity of Euler Error for High K (%)") 107 | plot!(kp_vec.-kp_0,Euler_vec,lw=2,label=nothing) 108 | xlims!(-1e-3,1e-3) 109 | xlabel!("Deviations from Policy (in levels)") 110 | ylabel!("Euler Error (%)") 111 | savefig("./Figures/Euler_Error_Test_High_K.pdf") 112 | 113 | 114 | println("\n Graphs Completed for VFI_$VFI_Type \n") 115 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Git Repository for Advanced Macroeconomics II 2 | 3 | #### **Instructor:** Sergio Ocampo 4 | 5 | #### **Institution:** Western University 6 | 7 | #### **Contact:** socampod@uwo.ca 8 | 9 | This repository contains the material for the advanced macroeconomics course at Western University.
10 | This is a second year PhD course focused on computational tools for quantitative Macroeconomics.
11 | The course syllabus is located in the main folder.
12 | The lecture handouts are located in the "Slides" folder.
13 | Accompanying code is located in the "Julia_Code" folder and its subfolders. All code is in Julia and runs in version 1.5.1.
14 | Problem sets and other assignments are located in the "Problem_Sets" folder.
15 | 16 | ## Lecture List 17 | 18 | 1. Course overview and best practices [Slides](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Slides/PhD_Macro_Comp_1_Handout.pdf)//[Problem Set](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Problem_Sets/Problem_Set_1.pdf) 19 | - Problem set on dynamic programming with the neoclassical growth model 20 | 2. Value function iteration [Slides](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Slides/PhD_Macro_Comp_2_Handout.pdf)//[Problem Set](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Problem_Sets/Problem_Set_2.pdf) 21 | - Grid search algorithm 22 | - Howard's policy iteration 23 | - MacQueen-Porteus bounds 24 | - Problem set on coding VFI for the neoclassical growth model 25 | 3. Interpolation [Slides](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Slides/PhD_Macro_Comp_3_Handout.pdf)//[Problem Set](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Problem_Sets/Problem_Set_3.pdf) 26 | - Polynomial interpolation 27 | - Spline interpolation (linear, cubic and shape preserving splines) 28 | - Curved grids 29 | - Problem set with applications of interpolation methods 30 | 4. Optimization [Slides](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Slides/PhD_Macro_Comp_4_Handout.pdf)//[Problem Set](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Problem_Sets/Problem_Set_4.pdf) 31 | - Local optimizers 32 | - Global optimizers 33 | - Sobol numbers 34 | - Root finding 35 | - VFI with continuous choice variables and continuous states 36 | 5. Integration [Slides](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Slides/PhD_Macro_Comp_5_Handout.pdf)//[Problem Set](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Problem_Sets/Problem_Set_5.pdf) 37 | - Monte Carlo Integration (just the concept) 38 | - Gaussian quadrature methods 39 | - Discretizing stochastic processes 40 | * Tauchen (1986) 41 | * Tauchen & Hussey (1991) 42 | * Rouwenhorst (1995) 43 | * Gaussian mixtures (the basics) 44 | * Extensions (Galindev & Lkhagvasuren, 2010; Fella, Gallipoli & Pan, 2019) 45 | 6. The Endogenous Grid Method (EGM) [Slides](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Slides/PhD_Macro_Comp_6_Handout.pdf)//[Problem Set](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Problem_Sets/Problem_Set_6.pdf) 46 | - Basic EGM: Carroll (2006) 47 | - EGM with labor supply: Barillas & Fernandez-Villaverde (2007) 48 | - EGM for non-smooth, non-concave problems: Fella (2014) 49 | - Envelope condition method (ECM): Maliar & Maliar (2013) 50 | 7. Recursive Competitive Equilibria (RCE) [Slides](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Slides/PhD_Macro_Comp_7_Handout.pdf)//[Problem Set](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Problem_Sets/Problem_Set_7.pdf) 51 | - Decentralized solution to NGM 52 | - Definition of RCE 53 | - Applications to economies with distortions (taxes/wedges) 54 | - Overview of Chari, Kehoe & McGrattan (2007) 55 | - Overview of Arellano (2008) 56 | 8. Heterogeneous Agent Models (RCE) [Slides](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Slides/PhD_Macro_Comp_8_Handout.pdf)//[Problem Set](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Problem_Sets/Problem_Set_8.pdf) 57 | - Hugget (1993) 58 | - Aiyagari (1994) 59 | - OLG - Aiyagari 60 | - Histogram method of Young (2010) 61 | - Fast Kernel method of Tan (2020) 62 | 9. Computational methods for continuous time models 63 | - Heterogeneous Agent Continuous Time model 64 | - Finite Difference Method 65 | - We will follow Ben Moll's lecture notes [Slides 1](https://benjaminmoll.com/wp-content/uploads/2019/07/Lecture1_Rochester.pdf)//[Slides 2](https://benjaminmoll.com/wp-content/uploads/2019/07/Lecture2_Rochester.pdf)//[Paper](https://benjaminmoll.com/wp-content/uploads/2019/07/HACT.pdf)//[Other resources](https://benjaminmoll.com/lectures/) 66 | - Markov Chain Approximation Method 67 | - We will follow Eslami & Phelan (2020) [Slides](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Slides/EslamiPhelan_Slides.pdf)//[Paper](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Slides/EslamiPhelan.pdf) 68 | 9. Search models I [Slides](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Slides/PhD_Macro_Comp_9_Handout.pdf)//[Problem Set](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Problem_Sets/Problem_Set_9.pdf) 69 | - Basic McCall (1970) model 70 | - Extensions to job separations, on the job search, human capital, savings 71 | 10. Search models II [Slides](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Slides/PhD_Macro_Comp_10_Handout.pdf)//[Problem Set](https://github.com/ocamp020/PhD_Macro_Course_Western/blob/master/Problem_Sets/Problem_Set_10.pdf) 72 | - Basic DMP model 73 | - Shimer (2005), overview of main results 74 | - How to solve the DMP model 75 | - How to simulate models in continuous time with Poisson shocks 76 | -------------------------------------------------------------------------------- /Julia_Code/Lecture_3_Interpolation/Scaled_Interpolation_Functions.jl: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | #################### Scaled Grid Interpolation ################################# 3 | ################################################################################ 4 | # The following code generates three objects: 5 | # 1) A "TypeScale" type that transforms ranges into a type of scaled ranges 6 | # 2) A "TypeRange" type that foroms scaled ranges of a given type. 7 | #  These ranges behave "just as" normal ranges 8 | # 3) A ScaledInterpolations function that wraps "interpolations.jl" to be used 9 | #  with TypeRange ranges 10 | # Types of Scales: parameters bounds for grid (a,b) and curvature (θ) 11 | # p = PolyScale(a,b,θ) 12 | # Maps a number x∈[0,1] to a polynomial grid with curvature θ: p(x)∈[a,b] 13 | # Usage: p(x), where x is a number, or p.(x) where x is vector/range 14 | # p(x) = a + (b - a) * x^θ 15 | # p = InvPolyScale(a,b,θ) 16 | # Maps a number y∈[a,b] from a polynomial grid with curvature θ to the unit interval: p(x)∈[0,1] 17 | # Usage: p(y), where x is a number, or p.(y) where y is vector/range 18 | # p(y) = ((y-a) / (b-a) )^(1/θ) 19 | # p = ExpScale(a,b,θ) 20 | # Maps a number x∈[0,1] to a exponential grid with curvature θ: p(x)∈[a,b] 21 | # Usage: p(x), where x is a number, or p.(x) where x is vector/range 22 | # p(x) = a + (b - a) * ( (exp(θ*x) - 1)/(exp(θ) - 1) ) 23 | # p = LogScale(a,b,θ) 24 | # Maps a number y∈[a,b] from an exponential grid with curvature θ to the unit interval: p(x)∈[0,1] 25 | # Usage: p(y), where x is a number, or p.(y) where y is vector/range 26 | # p(y) = log( (y-a)*(exp(θ) - 1)/(b - a) + 1 ) / θ 27 | # Types of Scaled Ranges 28 | # grid = PolyRange(a,b,θ=t,N=n) = PolyScale(a,b,t)(range(0,1,length=n)) 29 | # grid = ExpRange(a,b,θ=t,N=n) = ExpScale(a,b,t)(range(0,1,length=n)) 30 | # ScaledInterpolations 31 | # itp = ScaledInterpolations(grid,f_grid,Itp_Type) 32 | # grid is either a PolyRange or ExpRange type 33 | # f_grid is a vector with the values of the function at the grid nodes 34 | # Itp_Type is a type from the interpolations.jl package 35 | # Examples: BSpline(Cubic(Line(OnGrid()))) // shortcut: CubicSpline 36 | #  BSpline(Cubic(Flat(OnGrid()))) 37 | # FritschButlandMonotonicInterpolation() // shortcut: MonotoneSpline 38 | #  BSpline(Linear()) // shortcut: LinearInterp 39 | # ScaledInterpolations with multiple dimensions 40 | # itp_md = ScaledInterpolations( (grid_1,...,grid_N) , (f_grid_1,...,f_grid_N) , (Type_1,...,Type_N) )  41 | # Jacob Adenbaum, 2020 42 | 43 | 44 | #------------------------------------------------------------------------------- 45 | #------------------------------------------------------------------------------- 46 | #------------------------------------------------------------------------------- 47 | # Define Scaled types 48 | #------------------------------------------------------------------------------- 49 | # Polynomial Scaling 50 | abstract type Scale{T} end 51 | abstract type ScaledRange{T} end 52 | 53 | for Foo in [:PolyScale, :InvPolyScale] 54 | 55 | @eval struct $Foo{T,TF} <: Scale{T} 56 | a::T 57 | b::T 58 | θ::TF 59 | end 60 | 61 | @eval function $Foo(a, b, θ = 1) 62 | T = reduce(promote_type, map(typeof, (a,b))) 63 | $Foo(convert(T, a), convert(T, b), θ) 64 | end 65 | end 66 | 67 | 68 | (p::PolyScale)(x) = p.a + (p.b - p.a) * x^p.θ 69 | (p::InvPolyScale)(y) = ((y-p.a) / (p.b-p.a) )^(1/p.θ) 70 | Base.inv(p::PolyScale) = InvPolyScale(p.a, p.b, p.θ) 71 | Base.inv(p::InvPolyScale) = PolyScale(p.a, p.b, p.θ) 72 | 73 | #------------------------------------------------------------------------------- 74 | # Exponential Scaling 75 | for Foo in [:ExpScale, :LogScale] 76 | 77 | @eval struct $Foo{T, TF} <: Scale{T} 78 | a::T 79 | b::T 80 | θ::TF 81 | et::T 82 | s::T 83 | end 84 | 85 | @eval function $Foo(a, b, θ = 1) 86 | s = (exp(θ) - 1)/(b - a) 87 | et= exp(θ) 88 | T = reduce(promote_type, map(typeof, (a,b,θ, et,s))) 89 | $Foo(convert(T, a), convert(T, b), θ, et,s) 90 | end 91 | end 92 | 93 | function (p::ExpScale)(x) 94 | t = (exp(p.θ * x) - 1)/(p.et - 1) 95 | return p.a + (p.b - p.a) * t 96 | end 97 | 98 | function (p::LogScale)(y) 99 | θ = p.θ 100 | log( (y-p.a)*p.s + 1 ) / p.θ 101 | end 102 | 103 | Base.inv(p::ExpScale) = LogScale(p.a, p.b, p.θ) 104 | Base.inv(p::LogScale) = ExpScale(p.a, p.b, p.θ) 105 | 106 | #------------------------------------------------------------------------------- 107 | # Finalize Types 108 | scales = Dict(:PolyRange => :PolyScale, :ExpRange => :ExpScale) 109 | 110 | 111 | Base.inv(::Type{ExpScale}) = LogScale 112 | Base.inv(::Type{LogScale}) = ExpScale 113 | Base.inv(::Type{PolyScale})= InvPolyScale 114 | Base.inv(::Type{InvPolyScale}) = PolyScale 115 | 116 | #------------------------------------------------------------------------------- 117 | #------------------------------------------------------------------------------- 118 | #------------------------------------------------------------------------------- 119 | # Define Scaled Ranges types 120 | #------------------------------------------------------------------------------- 121 | # Polynomial Range 122 | for Typ in [:PolyRange] @eval begin 123 | struct $Typ{T,TF} <: AbstractRange{T} 124 | a::T 125 | b::T 126 | θ::TF 127 | N::Int 128 | vals::Vector{Float64} 129 | scale::$(scales[Typ]){T,TF} 130 | invscale::inv($(scales[Typ])){T,TF} 131 | end 132 | 133 | function $Typ(a,b; θ = 1, N = 100) 134 | T = reduce(promote_type, map(typeof, (a,b,θ, 1.0))) 135 | Ta = convert(T, a) 136 | Tb = convert(T, b) 137 | 138 | vals = domscale($Typ)(Ta,Tb,θ).(LinRange(0, 1, N)) 139 | scale = $(scales[Typ])(Ta, Tb, θ) 140 | invscale= inv($(scales[Typ])(Ta, Tb, θ)) 141 | $Typ(convert(T, a), convert(T, b), θ, N, vals, scale, invscale) 142 | end 143 | 144 | domscale(p::$Typ) = p.scale 145 | domscale(::Type{$Typ}) = $(scales[Typ]) 146 | 147 | function rangescale(r::$Typ, x) 148 | r.invscale(x)*(length(r)-1) + 1 149 | end 150 | 151 | Base.getindex(p::$Typ, i::Int) = p.vals[i] 152 | Base.length(p::$Typ) = p.N 153 | Base.show(io::IO, p::$Typ) = begin 154 | Typ = $Typ 155 | print(io, "$Typ($(p.a), $(p.b), $(p.θ), $(p.N))") 156 | end 157 | 158 | function Base.searchsortedfirst(p::$Typ, x, args...) 159 | searchsortedfirst(p.vals, x, args...) 160 | end 161 | 162 | 163 | end end 164 | 165 | #------------------------------------------------------------------------------- 166 | # Exponential Range 167 | for Typ in [:ExpRange] @eval begin 168 | struct $Typ{T,TF} <: AbstractRange{T} 169 | a::T 170 | b::T 171 | θ::TF 172 | N::Int 173 | vals::Vector{Float64} 174 | scale::$(scales[Typ]){T,TF} 175 | invscale::inv($(scales[Typ])){T,TF} 176 | end 177 | 178 | function $Typ(a,b; θ = 1, N = 100) 179 | vals = domscale($Typ)(a,b,θ).(LinRange(0, 1, N)) 180 | scale = $(scales[Typ])(a, b, θ) 181 | invscale= inv($(scales[Typ])(a, b, θ)) 182 | T = reduce(promote_type, map(typeof, (a,b,θ,scale.s))) 183 | $Typ(convert(T, a), convert(T, b), θ, N, vals, scale, invscale) 184 | end 185 | 186 | domscale(p::$Typ) = p.scale 187 | domscale(::Type{$Typ}) = $(scales[Typ]) 188 | 189 | function rangescale(r::$Typ, x) 190 | r.invscale(x)*(length(r)-1) + 1 191 | end 192 | 193 | Base.getindex(p::$Typ, i::Int) = p.vals[i] 194 | Base.length(p::$Typ) = p.N 195 | Base.show(io::IO, p::$Typ) = begin 196 | Typ = $Typ 197 | print(io, "$Typ($(p.a), $(p.b), $(p.θ), $(p.N))") 198 | end 199 | 200 | function Base.searchsortedfirst(p::$Typ, x, args...) 201 | searchsortedfirst(p.vals, x, args...) 202 | end 203 | end end 204 | 205 | #------------------------------------------------------------------------------- 206 | # Finalize Ranges 207 | function rangescale(r::AbstractRange, x) 208 | ((x - first(r)))/(last(r) - first(r)) * (length(r) -1) + 1 209 | end 210 | 211 | rangescale(r::UnitRange, x::Int) = x 212 | 213 | 214 | #------------------------------------------------------------------------------- 215 | #------------------------------------------------------------------------------- 216 | #------------------------------------------------------------------------------- 217 | # Scaled Interpolation Wraper 218 | mutable struct ScaledInterpolations{T, N, IT <: Interpolations.AbstractInterpolation, 219 | RT <: NTuple{N, AbstractRange}, 220 | VT <: AbstractArray{T, N}, AT} 221 | r::RT # Interpolation grid (ranges) 222 | v::VT # Interpolation values 223 | itp::IT # Interpolation object 224 | args::AT # Arguments to pass to construct interpolant 225 | end 226 | 227 | function ScaledInterpolations(r, v, args...) 228 | itp = extrapolate(interpolate(v, args...), Interpolations.Flat()) 229 | return ScaledInterpolations(tuplefy(r), v, itp, args) 230 | end 231 | 232 | tuplefy(x::Tuple) = x 233 | tuplefy(x) = tuple(x) 234 | 235 | """ 236 | ``` 237 | fit!(s::ScaledInterpolations, [v]) 238 | ``` 239 | Re-fit the interpolation after the underlying data has updated. Works well if 240 | you use a view into the original array. If an array of values is passed, copies 241 | the values to the scaled interpolations value array and then refits the 242 | interpolation. 243 | """ 244 | function fit!(s::ScaledInterpolations) 245 | s.itp = extrapolate(interpolate(s.v, s.args...), Interpolations.Flat()) 246 | end 247 | 248 | function fit!(s::Array{T,N}) where {T <: ScaledInterpolations, N} 249 | for v in s 250 | fit!(v) 251 | end 252 | end 253 | 254 | dim(::Type{ScaledInterpolations{T, N, IT, RT, VT, AT}}) where {T, N, IT, RT, VT, AT} = N 255 | Base.eltype(::Type{ScaledInterpolations{T, N, IT, RT, VT, AT}}) where {T, N, IT, RT, VT, AT} = T 256 | dim(itp::ScaledInterpolations) = dim(typeof(itp)) 257 | Base.eltype(itp::ScaledInterpolations) = eltype(typeof(itp)) 258 | 259 | @generated function (s::ScaledInterpolations)(x::Vararg) 260 | Ns = dim(s) 261 | Nx = length(x) 262 | Ns == Nx || begin 263 | return quote 264 | Ns = $Ns 265 | Nx = $Nx 266 | throw(ArgumentError("Must have $Ns arguments. You passed $Nx")) 267 | end 268 | end 269 | 270 | ex = Expr(:call, :(s.itp)) 271 | for i = 1:Ns 272 | push!(ex.args, :(rangescale(s.r[$i], x[$i]))) 273 | end 274 | ex 275 | end 276 | 277 | #------------------------------------------------------------------------------- 278 | #------------------------------------------------------------------------------- 279 | #------------------------------------------------------------------------------- 280 | # Scaled Interpolation Shortcuts for Itp_Type 281 | const CubicSpline = BSpline(Cubic(Natural(OnGrid()))) 282 | const MonotoneSpline = FritschButlandMonotonicInterpolation() 283 | const LinearInterp = BSpline(Linear()) 284 | const LinearInterp2 = BSpline(Linear()) 285 | 286 | function ScaledInterpolations(grid, values, type::FritschButlandMonotonicInterpolation) 287 | return interpolate(collect(grid), values, type) 288 | end 289 | -------------------------------------------------------------------------------- /Julia_Code/Lecture_5_Integration/Integration_Lecture_5.jl: -------------------------------------------------------------------------------- 1 | # Integration for Julia 2 | # Sergio Ocampo 3 | # September 2020 4 | # NGM with log utility and full depreciation 5 | # Solve: V(k) = max{ log(zk^alpha-k') +beta*V(k') } 6 | cd() # Go to root directory 7 | cd("./Dropbox/Teaching/PhD_Macro_Comp/Julia_Code/Lecture_5_Integration/") 8 | mkpath("Figures") 9 | using Plots 10 | # using LateXStrings # Pkg.add("LaTeXStrings") # https://github.com/stevengj/LaTeXStrings.jl 11 | using Dierckx # Pkg.add("Dierckx") # https://github.com/kbarbary/Dierckx.jl 12 | using Interpolations # Pkg.add("Interpolations") # https://github.com/JuliaMath/Interpolations.jl 13 | using ForwardDiff # Pkg.add("ForwardDiff") # https://github.com/JuliaDiff/ForwardDiff.jl 14 | using Optim # Pkg.add("Optim") # https://julianlsolvers.github.io/Optim.jl/stable/ 15 | using Optim: converged, maximum, maximizer, minimizer, iterations 16 | using Roots # Pkg.add("Roots") # https://github.com/JuliaMath/Roots.jl 17 | using Parameters # Pkg.add("Parameters") # https://github.com/mauro3/Parameters.jl 18 | using Distributions #Pkg.add("Distributions") 19 | using QuadGK # Pkg.add("QuadGK") # https://juliamath.github.io/QuadGK.jl/latest/ 20 | using LinearAlgebra 21 | using Random 22 | using Statistics 23 | # Call Scaled Interpolation Functions 24 | include("../Lecture_3_Interpolation/Scaled_Interpolation_Functions.jl") 25 | println(" ") 26 | println("------------------------") 27 | println("Integration in Julia") 28 | println("PWD: ",pwd()) 29 | println("This code uses Plots, Interpolations, Dierckx, ForwardDiff, Optim, ") 30 | println(" Roots, Parameters, ScaledInterpolation, Distributions, QuadGK, LinearAlgebra, Random") 31 | println("Optimization in the context of the neoclassical growth model") 32 | println("------------------------") 33 | println(" ") 34 | 35 | #----------------------------------------------------------- 36 | #----------------------------------------------------------- 37 | # Set random seed 38 | Random.seed!(3486); 39 | 40 | #----------------------------------------------------------- 41 | # Defien a markov process struct 42 | # Generate structure for markov processes using Parameters module 43 | @with_kw struct MP 44 | # Model Parameters 45 | N::Int64 # Number of states 46 | grid # Grid of discrete markov process 47 | Π # Transition matrix 48 | PDF # Stationary distribution 49 | CDF # Stationary distribution 50 | end 51 | 52 | 53 | #----------------------------------------------------------- 54 | #----------------------------------------------------------- 55 | # Tauchen (1986) 56 | # Objective is to discretize AR(1) process: z'=ρz+η, η~N(0,σ) 57 | # Code from Kopecky & Suen (2010) 58 | # Inputs: 59 | # ρ - Process persisntence 60 | # σ - Innovation standard deviation 61 | # N - Size of the grid 62 | # Ω - Grid expansion in number of standard devaitions (Optional) 63 | # Outputs: 64 | # z - Grid of N equally spaced points covering [-Ωσ,Ωσ] 65 | # Π - Transition matrix, a stochastic matrix (sums to 1 across columns) 66 | # PDF_z, CDF_z - Stationary distribution of z 67 | function Tauchen86(ρ,σ,N,Ω::Any=3) 68 | # Create z grid 69 | z = range(-Ω*σ/sqrt(1-ρ^2),Ω*σ/sqrt(1-ρ^2),length=N) 70 | # Define intermediate step length 71 | h = (z[2]-z[1])/2 72 | # Define auxiliary matrices 73 | z_0 = repeat(z ,1,N) # Matrix of today's z each row is a value, columns are equal 74 | z_1 = repeat(z',N,1) # Matrix of tomorrow's z each column is a value, rows are equal 75 | # Define intervals 76 | z_lim = zeros(N,N,2) # First matrix is lower bounds. Second matrix is uppor bounds. 77 | z_lim[:,1 ,1] .= -Inf 78 | z_lim[:,2:end ,1] .= ( z_1[:,2:end ] - ρ*z_0[:,2:end ] .- h )./σ 79 | z_lim[:,1:end-1,2] .= ( z_1[:,1:end-1] - ρ*z_0[:,1:end-1] .+ h )./σ 80 | z_lim[:,end ,2] .= Inf 81 | # Define reference distribution 82 | # This line uses "Distributions" 83 | F(x) = cdf.(Normal(),x) 84 | # Fill in transition matrix 85 | Π_z = F.(z_lim[:,:,2]) - F.(z_lim[:,:,1]) 86 | Π_z = Π_z./repeat(sum(Π_z,dims=2),1,N) 87 | # Get stationary distribution of markov chain 88 | PDF_z = real(eigvecs(Π_z')[:,end]); PDF_z = PDF_z/sum(PDF_z) ; 89 | CDF_z = cumsum(PDF_z) 90 | # Return 91 | return MP(N=N,grid=z,Π=Π_z,PDF=PDF_z,CDF=CDF_z) 92 | end 93 | 94 | 95 | #----------------------------------------------------------- 96 | #----------------------------------------------------------- 97 | # Rouwenhorst (1995) 98 | # Objective is to discretize AR(1) process: z'=ρz+η, η~N(0,σ) 99 | # Code from Kopecky & Suen (2010) 100 | # Inputs: 101 | # ρ - Process persisntence 102 | # σ - Innovation standard deviation 103 | # N - Size of the grid 104 | # Outputs: 105 | # z - Grid of N equally spaced points covering [-ψ,ψ] 106 | # Π - Transition matrix, a stochastic matrix (sums to 1 across columns) 107 | # PDF_z, CDF_z - Stationary distribution of z 108 | function Rouwenhorst95(ρ,σ,N) 109 | # Define paramters for Rouwenhorst's approximation 110 | p = (1+ρ)/2 111 | q = p # Note: I am leaving q here for comparability with source 112 | ψ = σ*sqrt((N-1)/(1-ρ^2)) 113 | s = (1-q)/(2-(p+q)) # Note: s=0.5, I leave it for comparability with source 114 | # Fill in transition matrix 115 | if N==2 116 | Π_z = [p 1-p ; 1-q q] 117 | else 118 | MP_aux = Rouwenhorst95(ρ,σ,N-1) 119 | o = zeros(N-1) 120 | Π_z = p*[MP_aux.Π o ; o' 0] + (1-p)*[o MP_aux.Π ; 0 o'] + (1-q)*[o' 0 ; MP_aux.Π o] + q*[0 o' ; o MP_aux.Π] 121 | # Adjust scale for double counting 122 | Π_z = Π_z./repeat(sum(Π_z,dims=2),1,N) 123 | end 124 | # Distribution 125 | PDF_z = pdf.(Binomial(N-1,1-s),(0:N-1)) 126 | CDF_z = cumsum(PDF_z) 127 | # Create z grid 128 | z = range(-ψ,ψ,length=N) 129 | # Return 130 | return MP(N=N,grid=z,Π=Π_z,PDF=PDF_z,CDF=CDF_z) 131 | end 132 | 133 | 134 | #----------------------------------------------------------- 135 | #----------------------------------------------------------- 136 | # Simulation of Markov processes 137 | 138 | # Simulation function for discrete Markov process 139 | # The result of the simulation is a Markov chain 140 | # Inputs: 141 | # Ns - Number of simulated periods 142 | # z - Grid with levels of Markov process 143 | # Π - Transition matrix of Markov process 144 | # N_0- Number of periods to throw before reporting simulation (Optional) 145 | # Output: 146 | # z_MC - Vector of Ns elemenrts with Markov Chain 147 | function Simulation_MC(Ns,MP::MP,N_0=1000) 148 | # Compute conditional CDF 149 | Γ = cumsum(MP.Π,dims=2) 150 | # Allocate simulated vector 151 | z_ind = zeros(Int64,N_0+Ns) 152 | z_MC = zeros(N_0+Ns) 153 | # Starting value for simulation 154 | z_ind[1] = Int(ceil(length(MP.grid)/2)) 155 | z_MC[1] = MP.grid[z_ind[1]] 156 | # Simulate 157 | for i=2:Ns+N_0 158 | #= Option 1 159 | # Draw a uniform random number (r). Compare it with conditional CDF. 160 | # Conditional CDF given by cumsum of current row of Π, call it Γ 161 | # Γ[i,j] gives the probability z'0 164 | z_ind[i] = findmax(sign.(Γ[z_ind[i-1],:] .- rand()))[2] 165 | =# 166 | #= Option 2 167 | # Alternatively we can draw directly from conditional distributional 168 | # Distributional is categorical with P[z'=z_j] = Π[i,j] 169 | =# 170 | z_ind[i] = rand(Categorical(MP.Π[z_ind[i-1],:])) 171 | z_MC[i] = MP.grid[z_ind[i]] 172 | end 173 | # Throw out first N_0 elements 174 | z_MC = z_MC[N_0+1:end] 175 | # Return result 176 | return z_MC 177 | end 178 | 179 | # Moments function for sample 180 | function Moments_MC(z_MC) 181 | mean_MC = mean(z_MC) 182 | std_MC = std(z_MC) 183 | auto_corr_MC = cor(z_MC[1:end-1],z_MC[2:end]) 184 | return mean_MC, std_MC, auto_corr_MC 185 | end 186 | 187 | #----------------------------------------------------------- 188 | #----------------------------------------------------------- 189 | # Simulation examples 190 | # Set parameters 191 | ρ = 0.95 192 | σ = 0.2 193 | Ns = 10000 194 | # Get Markov processes with various sizes 195 | MP_T5 = Tauchen86(ρ,σ,5) 196 | MP_T11 = Tauchen86(ρ,σ,11) 197 | MP_T21 = Tauchen86(ρ,σ,21) 198 | 199 | MP_R5 = Rouwenhorst95(ρ,σ,5) 200 | MP_R11 = Rouwenhorst95(ρ,σ,11) 201 | MP_R21 = Rouwenhorst95(ρ,σ,21) 202 | 203 | #------------------------------------------------------- 204 | # Simulate 205 | T_s_5 = Simulation_MC(Ns,MP_T5 ,0) 206 | T_s_11 = Simulation_MC(Ns,MP_T11,0) 207 | T_s_21 = Simulation_MC(Ns,MP_T21,0) 208 | 209 | R_s_5 = Simulation_MC(Ns,MP_R5 ,0) 210 | R_s_11 = Simulation_MC(Ns,MP_R11,0) 211 | R_s_21 = Simulation_MC(Ns,MP_R21,0) 212 | 213 | #------------------------------------------------------- 214 | # Plot paths for simulations, Ns=100 215 | gr() 216 | plt = plot(title="Tauchen Simulation Paths",legend=:outerright,foreground_color_legend = nothing,background_color_legend = nothing) 217 | plot!(1:100,T_s_5[1:100] ,linewidth=3,label="N=5" ) 218 | plot!(1:100,T_s_11[1:100],linewidth=3,label="N=11",linestyle=(:dash)) 219 | plot!(1:100,T_s_21[1:100],linewidth=3,label="N=21",linestyle=(:dot)) 220 | xlabel!("Time") 221 | savefig("./Figures/Tauchen_Simulation_Paths.pdf") 222 | 223 | gr() 224 | plt = plot(title="Rouwenhorst Simulation Paths",legend=:outerright,foreground_color_legend = nothing,background_color_legend = nothing) 225 | plot!(1:100,R_s_5[1:100] ,linewidth=3,label="N=5" ) 226 | plot!(1:100,R_s_11[1:100],linewidth=3,label="N=11",linestyle=(:dash)) 227 | plot!(1:100,R_s_21[1:100],linewidth=3,label="N=21",linestyle=(:dot)) 228 | xlabel!("Time") 229 | savefig("./Figures/Rouwenhorst_Simulation_Paths.pdf") 230 | 231 | #------------------------------------------------------- 232 | # Report moments for simulations, Ns=100 233 | mean_T_5 , std_T_5 , acorr_T_5 = Moments_MC(T_s_5) 234 | mean_T_11, std_T_11, acorr_T_11 = Moments_MC(T_s_11) 235 | mean_T_21, std_T_21, acorr_T_21 = Moments_MC(T_s_21) 236 | 237 | mean_R_5 , std_R_5 , acorr_R_5 = Moments_MC(R_s_5) 238 | mean_R_11, std_R_11, acorr_R_11 = Moments_MC(R_s_11) 239 | mean_R_21, std_R_21, acorr_R_21 = Moments_MC(R_s_21) 240 | 241 | Moments_Mat = [" " "Data" "T" "T" "T" " " "R" "R" "R"; 242 | " " " " "N=5" "N=11" "N=21" " " "N=5" "N=11" "N=21"; 243 | "mean" 0 mean_T_5 mean_T_11 mean_T_21 " " mean_R_5 mean_R_11 mean_R_21; 244 | "std" σ/sqrt(1-ρ^2) std_T_5 std_T_11 std_T_21 " " std_R_5 std_R_11 std_R_21; 245 | "acorr" ρ acorr_T_5 acorr_T_11 acorr_T_21 " " acorr_R_5 acorr_R_11 acorr_R_21]; 246 | 247 | println("\n Moments from simulated paths: \n") 248 | display(Moments_Mat) 249 | 250 | 251 | 252 | 253 | #----------------------------------------------------------- 254 | #----------------------------------------------------------- 255 | #----------------------------------------------------------- 256 | # Constrained entrepreneur problem - Using integrals to solve VFI 257 | # V(z,k) = max{c,k'}{ c^(1-γ)/(1-γ) + βE[V(z',k')|z]} 258 | # s.t. c+k'=z(k^α)(l^(1-α)) - w*l + (1-δ)k 259 | #  log(z') = ρlog(z)+η; η~N(0,σ_η) 260 | 261 | #----------------------------------------------------------- 262 | # Paramters 263 | # Generate structure for parameters using Parameters module 264 | # We can set default values for our parameters 265 | @with_kw struct Par 266 | # Model Parameters 267 | α::Float64 = 1/3 ; # Production function 268 | β::Float64 = 0.98 ; # Discount factor 269 | γ::Float64 = 2 ; # Relative risk aversion parameter 270 | δ::Float64 = 0.05 ; # Depreciation rate 271 | z_bar::Float64 = 1; # Reference level for productivity 272 | ρ::Float64 = 0.95 ; # Persistence of productivity process 273 | σ::Float64 = 0.10 ; # Standard devaiation of productivity innovation 274 | # Wage level: set as wage in SS of equivalent NGM 275 | w_bar::Float64 = (1-α)*z_bar*(β*α*z_bar)^(α/(1-α)) 276 | # VFI Paramters 277 | max_iter::Int64 = 2000 ; # Maximum number of iterations 278 | dist_tol::Float64 = 1E-9 ; # Tolerance for distance 279 | # Howard's Policy Iterations 280 | H_tol::Float64 = 1E-9 ; # Tolerance for policy function iteration 281 | # Grid points 282 | N::Int64 = 31 ; 283 | end 284 | #----------------------------------------------------------- 285 | 286 | #----------------------------------------------------------- 287 | # Guess and verify: V(z,k)=v(z)*(k^(1-γ)/(1-γ)) 288 | # v(z) = [ 1 + (βE[v(z')|z])^(1/γ) ]^γ * Γ(z)^(1-γ) 289 | # Γ(z) = α((1-α)/w_bar)^((1-α)/α)*z^(1/α) + (1-δ) 290 | 291 | #------------------------------------------------------- 292 | # Define function for profits 293 | function Γ(z,p::Par=Par()) 294 | @unpack α, δ, w_bar = p 295 | return α*((1-α)/w_bar)^((1-α)/α)*z.^(1/α) .+ (1-δ) 296 | end 297 | #------------------------------------------------------- 298 | 299 | #------------------------------------------------------- 300 | # Define Bellman operator with Markov Process 301 | function T_MP(v,MP::MP,p::Par) 302 | # Check that f has same dimension as the Markov Process 303 | if length(v)!=MP.N 304 | error("Expectation requires that dimensiosn agree, v vs MP") 305 | end 306 | # Unpack parameters 307 | @unpack β, γ = p 308 | # Calculate Bellman operator (done in one step for all states) 309 | Tv = ( 1 .+ (β* (MP.Π*v) ).^(1/γ) ).^(γ).*(Γ(exp.(MP.grid),p)).^(1-γ) 310 | # Return 311 | return Tv 312 | end 313 | #------------------------------------------------------- 314 | 315 | #------------------------------------------------------- 316 | # Define Bellman operator with Gauss-Kronrod Quadrature 317 | function T_GK(v,log_z,p::Par) 318 | # Unpack parameters 319 | @unpack β, γ, ρ, σ = p 320 | # Define function for interpolation and evaluation out of bounds 321 | # Note: function evaluated in log(z) bc log(z) is the one with normal innovations 322 | vp = interpolate(v, BSpline(Cubic(Line(OnGrid())))) 323 | vp = Interpolations.scale(vp,log_z) 324 | vp_extrapolate = extrapolate(vp,Line()) 325 | 326 | # Define integral limits as 4 std around mean (of zero) 327 | x_min, x_max = -5*σ, 5*σ 328 | # Calculate Bellman operator (done in one step for all states) 329 | Tv = zeros(p.N) 330 | for i=1:p.N 331 | # Define integrand 332 | function F(x) 333 | zp = ρ*log_z[i].+x 334 | if zplog_z[end] 335 | return pdf.(Normal(0,σ),x) .* vp_extrapolate.(zp) 336 | else 337 | return pdf.(Normal(0,σ),x) .* vp.(zp) 338 | end 339 | end 340 | Tv[i] = ( 1 .+ (β* quadgk(F,x_min,x_max)[1] ).^(1/γ) ).^(γ).*(Γ(exp.(log_z[i]),p)).^(1-γ) 341 | end 342 | # Return 343 | return Tv 344 | end 345 | #------------------------------------------------------- 346 | 347 | 348 | #------------------------------------------------------- 349 | # Value Function Iteration 350 | function VFI_Fixed_Point(T::Function,p::Par) 351 | # VFI paramters 352 | @unpack max_iter, dist_tol, N = p 353 | # Initialize variables for loop 354 | v_old = zeros(N) ; # Initialize value function, here I just do 0, not the best option 355 | V_dist = 1 ; # Initialize distance 356 | println(" ") 357 | println("------------------------") 358 | println("VFI - N=$N ") 359 | for iter=1:max_iter 360 | # Update value function 361 | v_new = T(v_old) 362 | # println("T(V) = $V_new") 363 | # println(" V = $V_old") 364 | # Update distance and iterations 365 | V_dist = maximum(abs.(v_new./v_old.-1)) 366 | # Update old function 367 | v_old = v_new 368 | # Report progress 369 | if mod(iter,100)==0 370 | println(" VFI Loop: iter=$iter, dist=",100*V_dist,"%") 371 | end 372 | # Check convergence and return results 373 | if V_dist<=dist_tol 374 | println("VFI - N=$N ") 375 | println("Iterations = $iter and Distance = ",100*V_dist,"%") 376 | println("------------------------") 377 | println(" ") 378 | # Return results 379 | return v_old 380 | end 381 | end 382 | # If loop ends there was no convergence -> Error! 383 | error("Error in VFI - Solution not found") 384 | end 385 | 386 | 387 | #----------------------------------------------------------- 388 | #----------------------------------------------------------- 389 | # Expectations on value function 390 | 391 | for rho in [0.5,0.8,0.9,0.95] 392 | # Set parameters 393 | p = Par(ρ=rho) 394 | log_z_grid = range(-4*p.σ,4*p.σ,length=p.N) 395 | 396 | # Solve with Tauchen 397 | println("\n Solving value with Tauchen approximation \n") 398 | MP_T = Tauchen86(p.ρ,p.σ,p.N,4) 399 | @time v_T = VFI_Fixed_Point(x-> T_MP(x,MP_T,p),p) 400 | v_T_ip = ScaledInterpolations(MP_T.grid,v_T, BSpline(Cubic(Line(OnGrid())))).(log_z_grid) 401 | 402 | # Solve with Rouwenhorst 403 | println("\n Solving value with Rouwenhorst approximation \n") 404 | MP_R = Rouwenhorst95(p.ρ,p.σ,p.N)  405 | @time v_R = VFI_Fixed_Point(x-> T_MP(x,MP_R,p),p) 406 | v_R_ip = ScaledInterpolations(MP_R.grid,v_R, BSpline(Cubic(Line(OnGrid())))).(log_z_grid) 407 | 408 | # Solve with Quadrature 409 | println("\n Solving value with Gauss-Kronrod approximation \n") 410 | @time v_GK = VFI_Fixed_Point(x-> T_GK(x,log_z_grid,p),p) 411 | 412 | # Plot results 413 | gr() 414 | plt = plot(title="Value Function: υ(z)/(1-γ) - ρ=$rho, N=$(p.N)",legend=:bottomright,foreground_color_legend = nothing,background_color_legend = nothing) 415 | plot!(log_z_grid,v_T_ip/(1-p.γ),linewidth=3,label="Tauchen" ) 416 | plot!(log_z_grid,v_R_ip/(1-p.γ),linewidth=3,label="Rouwenhorst",linestyle=(:dash)) 417 | plot!(log_z_grid,v_GK /(1-p.γ),linewidth=3,label="Gauss-Kronrod",linestyle=(:dot)) 418 | xlabel!("log(z)") 419 | savefig("./Figures/Value_Functions_rho_$rho.pdf") 420 | 421 | # Test with ρ=0, all three columns should be the same 422 | a = ((v_T_ip./((Γ(exp.(log_z_grid),p)).^(1-p.γ))).^(1/p.γ) .- 1).^(p.γ) 423 | b = ((v_R_ip./((Γ(exp.(log_z_grid),p)).^(1-p.γ))).^(1/p.γ) .- 1).^(p.γ) 424 | c = (( v_GK./((Γ(exp.(log_z_grid),p)).^(1-p.γ))).^(1/p.γ) .- 1).^(p.γ) 425 | gr() 426 | plt = plot(title="Expectation: βE[υ(z')|z] - ρ=$rho, N=$(p.N)",legend=:topright,foreground_color_legend = nothing,background_color_legend = nothing) 427 | plot!(log_z_grid,a,linewidth=3,label="Tauchen" ) 428 | plot!(log_z_grid,b,linewidth=3,label="Rouwenhorst",linestyle=(:dash)) 429 | plot!(log_z_grid,c,linewidth=3,label="Gauss-Kronrod",linestyle=(:dot)) 430 | xlabel!("log(z)") 431 | savefig("./Figures/Expectations_rho_$rho.pdf") 432 | 433 | end 434 | -------------------------------------------------------------------------------- /Julia_Code/Lecture_3_Interpolation/Interpolation_Lecture_3_Code.jl: -------------------------------------------------------------------------------- 1 | # Interpolation for Julia 2 | # Sergio Ocampo 3 | # September 2020 4 | cd() # Go to root directory 5 | cd("./Dropbox/Teaching/PhD_Macro_Comp/Julia_Code/Lecture_3_Interpolation/") 6 | mkpath("Figures") 7 | using Plots 8 | # using LateXStrings # Pkg.add("LaTeXStrings") # https://github.com/stevengj/LaTeXStrings.jl 9 | using Dierckx # Pkg.add("Dierckx") # https://github.com/kbarbary/Dierckx.jl 10 | using Interpolations # Pkg.add("Interpolations") # https://github.com/JuliaMath/Interpolations.jl 11 | using ForwardDiff # Pkg.add("ForwardDiff") # https://github.com/JuliaDiff/ForwardDiff.jl 12 | println(" ") 13 | println("------------------------") 14 | println("Interpolation in Julia") 15 | println("PWD: ",pwd()) 16 | println("This code uses Plots, Interpolations, Dierckx and ForwardDiff") 17 | println("------------------------") 18 | println(" ") 19 | 20 | 21 | #----------------------------------------------------------- 22 | #----------------------------------------------------------- 23 | # Runge's Function 24 | # Define Runge's function 25 | f(x) =1/(1+x^2) 26 | df(x) = -2*x*(1+x^2)^-2 27 | # Fine Grid 28 | xaxis = range(-5,5;length=1000) ; 29 | # Exact evaluation of Runge's function 30 | runge = map(f,xaxis) # Runge's function values 31 | runge_derivative = map(df,xaxis) # Runge's function derivative values 32 | 33 | 34 | #----------------------------------------------------------- 35 | #----------------------------------------------------------- 36 | # Polynomial Interpolation: Newton Basis 37 | # Code from Giray Otken, First semester in numerical analysis with Julia 38 | 39 | function diff(x::Array,y::Array) 40 | m = length(x) #here m is the number of data points. #the degree of the polynomial is m-1 a=Array{Float64}(undef,m) 41 | a = zeros(m) 42 | for i in 1:m 43 | a[i]=y[i] 44 | end 45 | for j in 2:m 46 | for i in reverse(collect(j:m)) 47 | a[i]=(a[i]-a[i-1])/(x[i]-x[i-(j-1)]) 48 | end 49 | end 50 | return(a) 51 | end 52 | 53 | function newton(x::Array,y::Array,z) 54 | m=length(x) #here m is the number of data points, not the degree # of the polynomial 55 | a=diff(x,y) 56 | sum=a[1] 57 | pr=1.0 58 | for j in 1:(m-1) 59 | pr=pr*(z-x[j]) 60 | sum=sum+a[j+1]*pr 61 | end 62 | return sum 63 | end 64 | 65 | 66 | #----------------------------------------------------------- 67 | #----------------------------------------------------------- 68 | # Runge's Example with Newton Polynomial for different n 69 | for n = (4,6,11,21) 70 | # Grid of nodes for interpolation 71 | xi = collect(range(-5,5;length=n)) ; # Collect makes it an array instead of a collection 72 | yi = map(f,xi) # the corresponding y-coordinates 73 | # Interpolation 74 | interp=map(z->newton(xi,yi,z),xaxis) # Interpolating poly for the data 75 | 76 | # Plot 77 | gr() 78 | plt = plot(title="Interpolation n=$n - Newton Polynomial") 79 | plot!(xaxis,runge,linewidth=3,label = "Runge's Function",legend=(0.75,0.75),foreground_color_legend = nothing,background_color_legend = nothing) 80 | plot!(xaxis,interp,linewidth=3,label="Interpolation") 81 | plot!(xi,yi,linetype=:scatter,marker=(:diamond,9),markercolor=RGB(0.5,0.1,0.1),label = "Data") 82 | savefig("./Figures/Runge_Newton_n_$n.pdf") 83 | end 84 | 85 | 86 | #----------------------------------------------------------- 87 | #----------------------------------------------------------- 88 | # Runge's Example with Linear Spline 89 | for n = (4,6,11,21) 90 | # Grid of nodes for interpolation 91 | xi = range(-5,5;length=n) ; 92 | yi = map(f,xi) # the corresponding y-coordinates 93 | # Interpolation 94 | interp = interpolate((xi,),yi, Gridded(Linear())) 95 | # interp = Spline1D(xi, yi; k=1, bc="nearest") 96 | # Plot 97 | gr() 98 | plt = plot(title="Interpolation n=$n - Linear Spline") 99 | plot!(xaxis,runge,linewidth=3,label = "Runge's Function",legend=(0.75,0.75),foreground_color_legend = nothing,background_color_legend = nothing) 100 | plot!(xaxis,interp(xaxis),linewidth=3,label="Interpolation") 101 | plot!(xi,yi,linetype=:scatter,marker=(:diamond,9),markercolor=RGB(0.5,0.1,0.1),label = "Data") 102 | savefig("./Figures/Runge_LS_n_$n.pdf") 103 | end 104 | 105 | 106 | 107 | #----------------------------------------------------------- 108 | #----------------------------------------------------------- 109 | # Runge's Example with Cubic Spline 110 | for n = (4,6,11,21) 111 | # Grid of nodes for interpolation 112 | xi = range(-5,5;length=n) ; 113 | yi = map(f,xi) # the corresponding y-coordinates 114 | d_yi = map(df,xi) # The corresponding derivatives 115 | # Interpolation (interpolates.jl) 116 | interp = interpolate(yi, BSpline(Cubic(Line(OnGrid())))) 117 | # interpolate assumes grid is equally spaced in [0,1] 118 | interp = scale(interp, xi) 119 | # Derivative (gradient computes one value at a time) 120 | deriv_ip(x) = Interpolations.gradient(interp,x)[1] 121 | 122 | # Interpolation (Dierckx.jl) 123 | # interp = Spline1D(xi, yi; k=3, bc="nearest") 124 | # # Derivative 125 | # deriv_ip(x) = derivative(interp, x) 126 | 127 | # Derivative using ForwardDiff 128 | deriv_ip_FD(x) = ForwardDiff.derivative(interp,x) 129 | 130 | # Plot 131 | gr() 132 | plt = plot(title="Interpolation n=$n - Cubic Spline") 133 | plot!(xaxis,runge,linewidth=3,label = "Runge's Function",legend=(0.75,0.75),foreground_color_legend = nothing,background_color_legend = nothing) 134 | plot!(xaxis,interp(xaxis),linewidth=3,label="Interpolation") 135 | plot!(xi,yi,linetype=:scatter,marker=(:diamond,9),markercolor=RGB(0.5,0.1,0.1),label = "Data") 136 | savefig("./Figures/Runge_CS_n_$n.pdf") 137 | 138 | gr() 139 | plt = plot(title="Derivative Interpolation n=$n - Cubic Spline") 140 | plot!(xaxis,runge_derivative,linewidth=3,label = "Runge's Derivative",legend=(0.75,0.75),foreground_color_legend = nothing,background_color_legend = nothing) 141 | plot!(xaxis,deriv_ip.(xaxis),linewidth=3,label="Interpolation") 142 | plot!(xaxis,deriv_ip_FD.(xaxis),linewidth=2.5,linestyle=(:dash),label="Interpolation FD") 143 | plot!(xi,d_yi,linetype=:scatter,marker=(:diamond,9),markercolor=RGB(0.5,0.1,0.1),label = "Data") 144 | savefig("./Figures/Runge_CS_Derivative_n_$n.pdf") 145 | end 146 | 147 | 148 | #----------------------------------------------------------- 149 | #----------------------------------------------------------- 150 | # Runge's Example with Monotone Perserving Cubic Spline 151 | for n = (4,6,11,21) 152 | # Grid of nodes for interpolation 153 | xi = range(-5,5;length=n) ; 154 | # xi_u = (xi .- xi[1])./(xi[end]-xi[1]) 155 | yi = map(f,xi) # the corresponding y-coordinates 156 | d_yi = map(df,xi) # The corresponding derivatives 157 | # Interpolation 158 | # interp = interpolate(xi, yi, FritschCarlsonMonotonicInterpolation()) 159 |  interp = interpolate(xi, yi, FritschButlandMonotonicInterpolation()) 160 | # Monotone interpolation does apply to vectors, only to numbers 161 | # Derivative (gradient computes one value at a time) 162 | deriv_ip(x) = Interpolations.gradient(interp,x)[1] 163 | # Derivative using ForwardDiff 164 | deriv_ip_FD(x) = ForwardDiff.derivative(interp,x) 165 | 166 | # Plot 167 | gr() 168 | plt = plot(title="Interpolation n=$n - Monotone Cubic Spline") 169 | plot!(xaxis,runge,linewidth=3,label = "Runge's Function",legend=(0.75,0.75),foreground_color_legend = nothing,background_color_legend = nothing) 170 | plot!(xaxis,interp.(xaxis),linewidth=3,label="Interpolation") 171 | plot!(xi,yi,linetype=:scatter,marker=(:diamond,9),markercolor=RGB(0.5,0.1,0.1),label = "Data") 172 | savefig("./Figures/Runge_MS_n_$n.pdf") 173 | 174 | gr() 175 | plt = plot(title="Derivative Interpolation n=$n - Monotone Cubic Spline") 176 | plot!(xaxis,runge_derivative,linewidth=3,label = "Runge's Derivative",legend=(0.75,0.75),foreground_color_legend = nothing,background_color_legend = nothing) 177 | plot!(xaxis,deriv_ip.(xaxis),linewidth=3,label="Interpolation") 178 | plot!(xaxis,deriv_ip_FD.(xaxis),linewidth=2.5,linestyle=(:dash),label="Interpolation FD") 179 | plot!(xi,d_yi,linetype=:scatter,marker=(:diamond,9),markercolor=RGB(0.5,0.1,0.1),label = "Data") 180 | savefig("./Figures/Runge_MS_Derivative_n_$n.pdf") 181 | end 182 | 183 | 184 | 185 | #----------------------------------------------------------- 186 | #----------------------------------------------------------- 187 | # CRRA Example U(c)=c^(1-γ)/(1-γ) 188 | γ = 10 189 | U(x) = x^(1-γ)/(1-γ) 190 | dU(x) = x^-γ 191 | 192 | # Fine Grid 193 | xaxis = range(0.01,0.50;length=1000) ; 194 | # Exact evaluation of Runge's function 195 | Utility = map(U,xaxis) # Utility function values 196 | Utility_derivative = map(dU,xaxis) # Utility derivative values 197 | 198 | #----------------------------------------------------------- 199 | #----------------------------------------------------------- 200 | # CRRA Example U(c)=c^(1-γ)/(1-γ) 201 | 202 | for n = (5,10,50,100) 203 | # Grid of nodes for interpolation 204 | ci = range(0.01,0.50;length=n) ; 205 | ui = map(U,ci) # the corresponding y-coordinates 206 | d_ui = map(dU,ci) # The corresponding derivatives 207 | # Interpolation Cubic Splines (y(x_0)''=0) 208 | U_ip_CS_nat = interpolate(ui, BSpline(Cubic(Line(OnGrid())))) 209 | # interpolate assumes grid is equally spaced in [0,1] 210 | U_ip_CS_nat = scale(U_ip_CS_nat, ci) 211 | # Derivative 212 | dU_ip_CS_nat(x) = Interpolations.gradient(U_ip_CS_nat,x)[1] 213 | # Interpolation Cubic Splines (y(x_0)'=0) 214 | U_ip_CS_flat = interpolate(ui, BSpline(Cubic(Flat(OnGrid())))) 215 | # interpolate assumes grid is equally spaced in [0,1] 216 | U_ip_CS_flat = scale(U_ip_CS_flat, ci) 217 | # Derivative 218 | dU_ip_CS_flat(x) = Interpolations.gradient(U_ip_CS_flat,x)[1] 219 | # Interpolation Monotone Spline 220 | U_ip_MS = interpolate(ci, ui, FritschButlandMonotonicInterpolation()) 221 | dU_ip_MS(x) = Interpolations.gradient(U_ip_MS,x)[1] 222 | # Interpolation Newton 223 | U_ip_NP=map(z->newton(collect(ci),ui,z),collect(xaxis)) # Interpolating poly for the data 224 | 225 | # Plot 226 | gr() 227 | plt = plot(title="Interpolation n=$n - Splines") 228 | plot!(log.(xaxis),Utility,linewidth=3,label = "Utility Function",legend=(0.75,0.75),foreground_color_legend = nothing,background_color_legend = nothing) 229 | plot!(log.(xaxis),U_ip_CS_nat(xaxis),linewidth=3,label="Interpolation CS-Natural") 230 | plot!(log.(xaxis),U_ip_CS_flat(xaxis),linewidth=3,linestyle=(:dot),label="Interpolation CS-Flat") 231 | plot!(log.(xaxis),U_ip_MS.(xaxis),linewidth=3,linestyle=(:dash),label="Interpolation MS") 232 | # plot!(log.(xaxis),U_ip_NP,linewidth=3,linestyle=(:dashdotdot),label="Interpolation NP") 233 | plot!(log.(ci),ui,linetype=:scatter,marker=(:diamond,9),markercolor=RGB(0.5,0.1,0.1),label = "Data") 234 | xlabel!("Log(Consumption)") 235 | savefig("./Figures/CRRA_n_$n.pdf") 236 | 237 | plot!(title="Interpolation n=$n - Cubic Spline",xlims = (log(0.01),log(0.15))) 238 | savefig("./Figures/CRRA_zoom_n_$n.pdf") 239 | 240 | gr() 241 | plt = plot(title="Interpolation n=$n - Splines") 242 | plot!(log.(xaxis),Utility_derivative,linewidth=3,label = "Derivative of Utility",legend=(0.75,0.75),foreground_color_legend = nothing,background_color_legend = nothing) 243 | plot!(log.(xaxis),dU_ip_CS_nat.(xaxis),linewidth=3,label="Interpolation CS-Natural") 244 | plot!(log.(xaxis),dU_ip_CS_flat.(xaxis),linewidth=3,linestyle=(:dot),label="Interpolation CS-Flat") 245 | plot!(log.(xaxis),dU_ip_MS.(xaxis),linewidth=3,linestyle=(:dash),label="Interpolation MS") 246 | # plot!(log.(xaxis),U_ip_NP,linewidth=3,linestyle=(:dashdotdot),label="Interpolation NP") 247 | plot!(log.(ci),d_ui,linetype=:scatter,marker=(:diamond,9),markercolor=RGB(0.5,0.1,0.1),label = "Data") 248 | xlabel!("Log(Consumption)") 249 | savefig("./Figures/CRRA_Derivative_n_$n.pdf") 250 | 251 | plot!(title="Interpolation n=$n - Cubic Spline",xlims = (log(0.01),log(0.15))) 252 | savefig("./Figures/CRRA_Derivative_zoom_n_$n.pdf") 253 | end 254 | 255 | 256 | #----------------------------------------------------------- 257 | #----------------------------------------------------------- 258 | # CRRA Example U(c)=c^(1-γ)/(1-γ) - Curved Grid 259 | 260 | # Call Scaled Interpolation Functions 261 | include("./Scaled_Interpolation_Functions.jl") 262 | 263 | for n = (5,10,50,100) 264 | # Grid of nodes for interpolation 265 | ci = PolyRange(0.01,0.50;θ=2,N=n) 266 | ui = map(U,ci) # the corresponding y-coordinates 267 | d_ui = map(dU,ci) # The corresponding derivatives 268 | # Interpolation Cubic Splines (y(x_0)''=0) 269 | U_ip_CS_nat = ScaledInterpolations(ci,ui, BSpline(Cubic(Line(OnGrid())))) 270 | dU_ip_CS_nat(x) = ForwardDiff.derivative(U_ip_CS_nat,x) 271 | # Interpolation Cubic Splines (y(x_0)'=0) 272 | U_ip_CS_flat = ScaledInterpolations(ci,ui, BSpline(Cubic(Flat(OnGrid())))) 273 | dU_ip_CS_flat(x) = ForwardDiff.derivative(U_ip_CS_flat,x) 274 | # Interpolation Monotone Splines 275 | U_ip_MS = ScaledInterpolations(ci, ui, FritschButlandMonotonicInterpolation()) 276 | dU_ip_MS(x) = ForwardDiff.derivative(U_ip_MS,x) 277 | # # No need for ScaledInterpolations if not using PolyRange 278 | # ci = 0.01 .+ (0.50-0.01).*range(0,1;length=n).^2 279 | # U_ip_MS = interpolations(ci, ui, FritschButlandMonotonicInterpolation()) 280 | # Interpolation Newton 281 | U_ip_NP=map(z->newton(collect(ci),ui,z),collect(xaxis)) # Interpolating poly for the data 282 | 283 | # Plot 284 | gr() 285 | plt = plot(title="Interpolation n=$n - Splines") 286 | plot!(log.(xaxis),Utility,linewidth=3,label = "Utility Function",legend=(0.75,0.75),foreground_color_legend = nothing,background_color_legend = nothing) 287 | plot!(log.(xaxis),U_ip_CS_nat.(xaxis),linewidth=3,label="Interpolation CS-Natural") 288 | plot!(log.(xaxis),U_ip_CS_flat.(xaxis),linewidth=3,linestyle=(:dot),label="Interpolation CS-Flat") 289 | plot!(log.(xaxis),U_ip_MS.(xaxis),linewidth=3,linestyle=(:dash),label="Interpolation MS") 290 | #plot!(log.(xaxis),U_ip_NP,linewidth=3,linestyle=(:dashdotdot),label="Interpolation NP") 291 | plot!(log.(ci),ui,linetype=:scatter,marker=(:diamond,9),markercolor=RGB(0.5,0.1,0.1),label = "Data") 292 | xlabel!("Log(Consumption)") 293 | savefig("./Figures/Curved_CRRA_n_$n.pdf") 294 | 295 | plot!(title="Interpolation n=$n - Cubic Spline",xlims = (log(0.01),log(0.15))) 296 | savefig("./Figures/Curved_CRRA_Derivative_zoom_n_$n.pdf") 297 | 298 | gr() 299 | plt = plot(title="Interpolation n=$n - Splines") 300 | plot!(log.(xaxis),Utility_derivative,linewidth=3,label = "Derivative of Utility",legend=(0.75,0.75),foreground_color_legend = nothing,background_color_legend = nothing) 301 | plot!(log.(xaxis),dU_ip_CS_nat.(xaxis),linewidth=3,label="Interpolation CS-Natural") 302 | plot!(log.(xaxis),dU_ip_CS_flat.(xaxis),linewidth=3,linestyle=(:dot),label="Interpolation CS-Flat") 303 | plot!(log.(xaxis),dU_ip_MS.(xaxis),linewidth=3,linestyle=(:dash),label="Interpolation MS") 304 | #plot!(log.(xaxis),U_ip_NP,linewidth=3,linestyle=(:dashdotdot),label="Interpolation NP") 305 | plot!(log.(ci),d_ui,linetype=:scatter,marker=(:diamond,9),markercolor=RGB(0.5,0.1,0.1),label = "Data") 306 | xlabel!("Log(Consumption)") 307 | savefig("./Figures/Curved_CRRA_Derivative_n_$n.pdf") 308 | 309 | plot!(title="Interpolation n=$n - Cubic Spline",xlims = (log(0.01),log(0.15))) 310 | savefig("./Figures/Curved_CRRA_Derivative_zoom_n_$n.pdf") 311 | end 312 | 313 | for t = (1,1.5,2,3) 314 | # Grid size 315 | n = 10 316 | # Grid of nodes for interpolation 317 | ci =PolyRange(0.01,0.50;θ=t,N=n) 318 | ui = map(U,ci) # the corresponding y-coordinates 319 | d_ui = map(dU,ci) # The corresponding derivatives 320 | # Interpolation Cubic Splines (y(x_0)''=0) 321 | U_ip_CS_nat = ScaledInterpolations(ci,ui, BSpline(Cubic(Line(OnGrid())))) 322 | dU_ip_CS_nat(x) = ForwardDiff.derivative(U_ip_CS_nat,x) 323 | # Interpolation Cubic Splines (y(x_0)'=0) 324 | U_ip_CS_flat = ScaledInterpolations(ci,ui, BSpline(Cubic(Flat(OnGrid())))) 325 | dU_ip_CS_flat(x) = ForwardDiff.derivative(U_ip_CS_flat,x) 326 | # Interpolation Monotone Splines 327 | U_ip_MS = ScaledInterpolations(ci, ui, FritschButlandMonotonicInterpolation()) 328 | dU_ip_MS(x) = ForwardDiff.derivative(U_ip_MS,x) 329 | # # No need for ScaledInterpolations if not using PolyRange 330 | # ci = 0.01 .+ (0.50-0.01).*range(0,1;length=n).^2 331 | # U_ip_MS = interpolations(ci, ui, FritschButlandMonotonicInterpolation()) 332 | # Interpolation Newton 333 | U_ip_NP=map(z->newton(collect(ci),ui,z),collect(xaxis)) # Interpolating poly for the data 334 | 335 | # Plot 336 | gr() 337 | plt = plot(title="Interpolation n=$n - θ=$t") 338 | plot!(log.(xaxis),Utility,linewidth=3,label = "Utility Function",legend=(0.75,0.75),foreground_color_legend = nothing,background_color_legend = nothing) 339 | plot!(log.(xaxis),U_ip_CS_nat.(xaxis),linewidth=3,label="Interpolation CS-Natural") 340 | plot!(log.(xaxis),U_ip_CS_flat.(xaxis),linewidth=3,linestyle=(:dot),label="Interpolation CS-Flat") 341 | plot!(log.(xaxis),U_ip_MS.(xaxis),linewidth=3,linestyle=(:dash),label="Interpolation MS") 342 | #plot!(log.(xaxis),U_ip_NP,linewidth=3,linestyle=(:dashdotdot),label="Interpolation NP") 343 | plot!(log.(ci),ui,linetype=:scatter,marker=(:diamond,9),markercolor=RGB(0.5,0.1,0.1),label = "Data") 344 | xlabel!("Log(Consumption)") 345 | savefig("./Figures/Curved_CRRA_t_$t.pdf") 346 | 347 | plot!(title="Interpolation n=$n - Cubic Spline",xlims = (log(0.01),log(0.15))) 348 | savefig("./Figures/Curved_CRRA_zoom_t_$t.pdf") 349 | 350 | gr() 351 | plt = plot(title="Interpolation n=$n - θ=$t") 352 | plot!(log.(xaxis),Utility_derivative,linewidth=3,label = "Derivative of Utility",legend=(0.75,0.75),foreground_color_legend = nothing,background_color_legend = nothing) 353 | plot!(log.(xaxis),dU_ip_CS_nat.(xaxis),linewidth=3,label="Interpolation CS-Natural") 354 | plot!(log.(xaxis),dU_ip_CS_flat.(xaxis),linewidth=3,linestyle=(:dot),label="Interpolation CS-Flat") 355 | plot!(log.(xaxis),dU_ip_MS.(xaxis),linewidth=3,linestyle=(:dash),label="Interpolation MS") 356 | #plot!(log.(xaxis),U_ip_NP,linewidth=3,linestyle=(:dashdotdot),label="Interpolation NP") 357 | plot!(log.(ci),d_ui,linetype=:scatter,marker=(:diamond,9),markercolor=RGB(0.5,0.1,0.1),label = "Data") 358 | xlabel!("Log(Consumption)") 359 | savefig("./Figures/Curved_CRRA_Derivative_t_$t.pdf") 360 | 361 | plot!(title="Interpolation n=$n - Cubic Spline",xlims = (log(0.01),log(0.15))) 362 | savefig("./Figures/Curved_CRRA_Derivative_zoom_t_$t.pdf") 363 | end 364 | 365 | # Plot Grid 366 | marker_list = (:diamond,:circle,:star4) 367 | gr() 368 | p = plot(legend=(0.75,0.95),foreground_color_legend = nothing,background_color_legend = nothing) 369 | for t=(1,2,3) 370 | n = 10 371 | grid = PolyRange(0.0,1.00;θ=t,N=n) 372 | plot!(grid,t.*ones(n),linewidth=2,label = "Grid, θ=$t",marker=(marker_list[t],7),markercolor=RGB(0.3,0.3,0.3),linecolor=RGB(0.6,0.6,0.6)) 373 | end 374 | xlabel!("Grid"); ylabel!("Curvature θ"); ylims!(0,4) 375 | savefig("./Figures/Curved_Grid.pdf") 376 | -------------------------------------------------------------------------------- /Julia_Code/Lecture_6_EGM/EGM_Lecture_6.jl: -------------------------------------------------------------------------------- 1 | # EGM in Julia 2 | # Sergio Ocampo 3 | # September 2020 4 | # EGM on the NGM with CRRA utility and AR(1) productivity 5 | # Solve: V(z,k) = max{ (zk^alpha-k')^(1-γ)/(1-γ) +beta*E[V(z',k')|z] } 6 | # log(z') = ρ*log(z) + η; η~N(0,σ) 7 | cd() # Go to root directory 8 | cd("./Dropbox/Teaching/PhD_Macro_Comp/Julia_Code/Lecture_6_EGM/") 9 | mkpath("Figures") 10 | using Plots 11 | using Interpolations # Pkg.add("Interpolations") # https://github.com/JuliaMath/Interpolations.jl 12 | using Dierckx # Pkg.add("Dierckx") # https://github.com/kbarbary/Dierckx.jl 13 | using ForwardDiff # Pkg.add("ForwardDiff") # https://github.com/JuliaDiff/ForwardDiff.jl 14 | using Optim # Pkg.add("Optim") # https://julianlsolvers.github.io/Optim.jl/stable/ 15 | using Optim: converged, maximum, maximizer, minimizer, iterations 16 | using Roots # Pkg.add("Roots") # https://github.com/JuliaMath/Roots.jl 17 | using Parameters # Pkg.add("Parameters") # https://github.com/mauro3/Parameters.jl 18 | include("../VFI_Toolbox.jl") 19 | # using .VFI_Toolbox 20 | println(" ") 21 | println("------------------------") 22 | println("EGM in Julia") 23 | println("PWD: ",pwd()) 24 | println("This code uses Plots, Interpolations, Dierckx, ForwardDiff, Optim, Roots, Parameters, ScaledInterpolation") 25 | println("EGM in the context of the neoclassical growth model") 26 | println("------------------------") 27 | println(" ") 28 | 29 | 30 | 31 | #----------------------------------------------------------- 32 | #----------------------------------------------------------- 33 | # Paramters and Model Structure 34 | # Generate structure for parameters using Parameters module 35 | # We can set default values for our parameters 36 | @with_kw struct Par 37 | # Model Parameters 38 | α::Float64 = 1/3 ; # Production function 39 | β::Float64 = 0.96 ; # Discount factor 40 | γ::Float64 = 2.0 ; # Relative risk aversion parameter 41 | δ::Float64 = 0.05 ; # Depreciation rate 42 | ρ::Float64 = 0.90 ; # Persistence of productivity process 43 | σ::Float64 = 0.05 ; # Standard devaiation of productivity innovation 44 | z_bar::Float64 = 1; # Reference level for productivity 45 | # VFI Paramters 46 | max_iter::Int64 = 2000 ; # Maximum number of iterations 47 | dist_tol::Float64 = 1E-12 ; # Tolerance for distance 48 | # Howard's Policy Iterations 49 | H_tol::Float64 = 1E-9 ; # Tolerance for policy function iteration 50 | N_H::Int64 = 20 ; # Maximum number of policy iterations 51 | # Minimum consumption for numerical optimization 52 | c_min::Float64 = 1E-16 53 | end 54 | 55 | # Allocate paramters to object p for future calling 56 | p = Par() 57 | 58 | 59 | # Generate structure of model objects 60 | @with_kw struct Model 61 | # Parameters 62 | p::Par = Par() # Model paramters in their own structure 63 | # Steady State Values 64 | k_ss = (p.β*p.α*p.z_bar/(1-p.β*(1-p.δ)))^(1/(1-p.α)) 65 | # Capital Grid 66 | θ_k::Float64 = 1.5 # Curvature of k_grid 67 | n_k::Int64 = 500 # Size of k_grid 68 | n_k_fine::Int64 = 1000 # Size of fine grid for interpolation 69 | k_grid = Make_Grid(n_k ,θ_k,1E-5,2*k_ss) # k_grid for model solution 70 | k_grid_fine = Make_Grid(n_k_fine,1 ,1E-5,2*k_ss) # Fine grid for interpolation 71 | # Productivity process 72 | n_z = 5 # Size of z_grid 73 | MP_z = Rouwenhorst95(p.ρ,p.σ,n_z) # Markov Process for z 74 | # State matrices 75 | k_mat = repeat(k_grid',n_z,1) 76 | z_mat = exp.(repeat(MP_z.grid,1,n_k)) 77 | # Y_grid and Marginal Product of Capital 78 | Y_grid = p.z_bar*z_mat.*(k_mat.^(p.α) ) .+ (1-p.δ).*k_mat 79 | MPk_mat = p.α*p.z_bar*z_mat.*(k_mat.^(p.α-1)) .+ (1-p.δ) 80 | # Value and policy functions 81 | V = Array{Float64}(undef,n_z,n_k) # Value Function 82 | G_kp = Array{Float64}(undef,n_z,n_k) # Policy Function 83 | G_c = Array{Float64}(undef,n_z,n_k) # Policy Function 84 | V_fine = Array{Float64}(undef,n_z,n_k_fine) # Value Function on fine grid 85 | G_kp_fine = Array{Float64}(undef,n_z,n_k_fine) # Policy Function on fine grid 86 | G_c_fine = Array{Float64}(undef,n_z,n_k_fine) # Policy Function on fine grid 87 | # Error in Euler equation 88 | Euler = Array{Float64}(undef,n_z,n_k_fine) # Errors in Euler equation 89 | end 90 | 91 | # Allocate model to object M for future calling 92 | M = Model() 93 | 94 | 95 | #----------------------------------------------------------- 96 | #----------------------------------------------------------- 97 | # Utility function 98 | function utility(z,k,kp,p::Par) 99 | @unpack z_bar, α, γ, δ, c_min = p 100 | c = max.(z_bar.*z.*k.^α .+ (1-δ).*k .- kp,c_min) 101 | if γ>1 102 | return (c).^(1-γ)/(1-γ) 103 | else 104 | return log.(c) 105 | end 106 | end 107 | 108 | function utility(c,p::Par) 109 | if p.γ>1 110 | return (c).^(1-p.γ)/(1-p.γ) 111 | else 112 | return log.(c) 113 | end 114 | end 115 | 116 | function d_utility(z,k,kp,p::Par) 117 | @unpack z_bar, α, γ, δ, c_min = p 118 | c = max.(z_bar.*z.*k.^α .+ (1-δ).*k .- kp,c_min) 119 | return (c).^(-γ) 120 | end 121 | 122 | function d_utility(c,p::Par) 123 | return (c).^(-p.γ) 124 | end 125 | 126 | function d_utility_inv(x,p::Par) 127 | return x.^(-1/p.γ) 128 | end 129 | 130 | #----------------------------------------------------------- 131 | #----------------------------------------------------------- 132 | # Steady state values (funciton) 133 | function SS_values(p::Par) 134 | # This function takes in parameters and provides steady state values 135 | # Parameters: productivity (z), returns to scale (a) and discount factor (b) 136 | # Output: values for capital, production, consumption, rental rate, wage 137 | @unpack z_bar, α, β, δ = p 138 | k_ss = (β*α*z_bar/(1-β*(1-δ)))^(1/(1-α)) 139 | y_ss = z_bar*k_ss^α 140 | c_ss = y_ss - δ*k_ss 141 | r_ss = α*y_ss/k_ss 142 | w_ss = (1-α)*y_ss 143 | return k_ss,y_ss,c_ss,r_ss,w_ss 144 | end 145 | 146 | # Test steady state function 147 | k_ss,y_ss,c_ss,r_ss,w_ss = SS_values(p) 148 | println(" ") 149 | println("------------------------") 150 | println(" Steady State variables") 151 | println(" Quantities: k = $k_ss; y = $y_ss; c = $c_ss;") 152 | println(" Prices: r = $r_ss; w = $w_ss;") 153 | println("------------------------") 154 | println(" ") 155 | 156 | # Value function for non-stochastic case with full depreciation and log-utility 157 | function V_analytical(k,p::Par) 158 | # This function takes in paramters and a value of capital 159 | # Output is the value of the value function at that level of capital 160 | # Parameters: productivity (z), returns to scale (a) and discount factor (b) 161 | @unpack z_bar, α, β = p 162 | a_1 = α/(1-α*β) 163 | a_0 = (1/(1-β))*(log(z_bar)+log(1/(1+β*a_1))+β*a_1*log(β*a_1*z_bar/(1+β*a_1*z))) 164 | V = a_0 .+ a_1*log.(k) 165 | return V 166 | end 167 | 168 | #----------------------------------------------------------- 169 | #----------------------------------------------------------- 170 | # Euler Error Function 171 | 172 | # G_k interpolation 173 | function G_kp_zk(i_z::Int64,k,M::Model) 174 | itp = ScaledInterpolations(M.k_grid,M.G_kp[i_z,:], BSpline(Cubic(Line(OnGrid())))) 175 | return itp(k) 176 | end 177 | 178 | # Euler Equation 179 | function Euler_Error(i_z::Int64,k,M::Model) 180 | # Return percentage error in Euler equation 181 | @unpack p, MP_z, n_z = M 182 | @unpack z_bar, α, β, δ = p 183 | # Iterpolate G_k at current z 184 | kp = min(M.k_grid[end],max(M.k_grid[1],G_kp_zk(i_z,k,M))) 185 | 186 | # Compute left hand side of Euler equation 187 | LHS = d_utility(exp(MP_z.grid[i_z]),k,kp,p) 188 | # Compute right hand side of Euler equation 189 | # Marginal product at kp for all z 190 | MPkp = α*z_bar*exp.(MP_z.grid).*kp^(α-1) .+ (1-δ) 191 | # Marginal utility at z',k',G_k(z',k') 192 | up = zeros(n_z) 193 | for i_zp=1:n_z 194 | up[i_zp] = d_utility(exp(MP_z.grid[i_zp]),kp,G_kp_zk(i_zp,kp,M),p) 195 | end 196 | RHS = β*(MP_z.Π[i_z,:])'*(MPkp.*up) 197 | # Return percentage errror in Euler equation 198 | return (RHS/LHS-1)*100 199 | end 200 | 201 | # Euler Equation for Optimization 202 | function Euler_Eq(kp,i_z::Int64,i_k::Int64,Vk,M::Model) 203 | # Return percentage error in Euler equation 204 | @unpack p, MP_z, n_z, k_grid = M 205 | @unpack β = p 206 | # Compute left hand side of Euler equation 207 | LHS = d_utility(exp(MP_z.grid[i_z]),k_grid[i_k],kp,p) 208 | # Compute right hand side of Euler equation 209 | # Expected value of derivative β*E[Vk[z',kp]|k] 210 | Vkp = zeros(n_z) 211 | for i_zp=1:n_z 212 | Vk_ip = ScaledInterpolations(k_grid,Vk[i_zp,:], FritschButlandMonotonicInterpolation()) 213 | Vkp[i_zp] = Vk_ip(kp) 214 | end 215 | RHS = β*(MP_z.Π[i_z,:])'*Vkp 216 | # Return squared errror in Euler equation 217 | return (RHS/LHS-1)^2 218 | end 219 | 220 | #----------------------------------------------------------- 221 | #----------------------------------------------------------- 222 | # VFI Fixed Point 223 | function VFI_Fixed_Point(T::Function,M::Model,V_old=nothing) 224 | # Unpack model structure 225 | @unpack p, n_z, n_k, θ_k, k_grid, n_k_fine, k_grid_fine = M 226 | # VFI paramters 227 | @unpack max_iter, dist_tol = p 228 | # Initialize variables for loop 229 | if V_old==nothing 230 | # V_old = zeros(n_z,n_k) ; # Initialize value function, here I just do 0, not the best option 231 | V_old = utility(M.z_mat,M.k_mat,zeros(n_z,n_k),p) ; # Start at utility with zero savings 232 | # V_old = utility((1-p.δ)*(M.k_mat),p) ; # Start at utility with c=(1-δ)k 233 | end 234 | # println("V_0="); display(V_old) 235 | G_kp_old = copy(M.k_mat) 236 | V_dist = 1 ; # Initialize distance 237 | println(" ") 238 | println("------------------------") 239 | println("VFI - n_z=$n_z, n_k=$n_k - θ_k=$θ_k") 240 | for iter=1:max_iter 241 | # println("\n Iteration $iter") 242 | # Update value function 243 | V_new, G_kp, G_c = T(Model(M,V=copy(V_old))) 244 | # println("T(V) = $V_new") 245 | # println(" V = $V_old") 246 | # Update distance and iterations 247 | V_dist = maximum(abs.(V_new./V_old.-1)) 248 | # V_dist = maximum(abs.(G_kp./G_kp_old.-1)) 249 | # Update old function 250 | V_old = V_new 251 | # G_kp_old = copy(G_kp) 252 | # Report progress 253 | if mod(iter,100)==0 254 | println(" VFI Loop: iter=$iter, dist=",100*V_dist,"%") 255 | end 256 | # Check convergence and return results 257 | if V_dist<=dist_tol 258 | println("VFI - n_z=$n_z, n_k=$n_k - θ_k=$θ_k") 259 | println("Iterations = $iter and Distance = ",100*V_dist,"%") 260 | println("------------------------") 261 | println(" ") 262 | # Interpolate to fine grid 263 | V_fine = zeros(n_z,n_k_fine) 264 | G_kp_fine = zeros(n_z,n_k_fine) 265 | G_c_fine = zeros(n_z,n_k_fine) 266 | for i_z=1:n_z 267 | V_ip = ScaledInterpolations(k_grid,V_new[i_z,:], BSpline(Cubic(Line(OnGrid())))) 268 | V_fine[i_z,:] .= V_ip.(collect(k_grid_fine)) 269 | G_kp_ip = ScaledInterpolations(k_grid,G_kp[i_z,:] , BSpline(Cubic(Line(OnGrid())))) 270 | G_kp_fine[i_z,:].= G_kp_ip.(collect(k_grid_fine)) 271 | G_c_ip = ScaledInterpolations(k_grid,G_c[i_z,:] , BSpline(Cubic(Line(OnGrid())))) 272 | G_c_fine[i_z,:] .= G_c_ip.(collect(k_grid_fine)) 273 | end 274 | # Update model 275 | M = Model(M; V=V_new,G_kp=G_kp,G_c=G_c,V_fine=V_fine,G_kp_fine=G_kp_fine,G_c_fine=G_c_fine) 276 | # Euler Equation Errors 277 | Euler = [Euler_Error(i_z,k_grid_fine[i_k],M) for i_z=1:n_z, i_k in 1:n_k_fine] 278 | # Update model 279 | M = Model(M; Euler=Euler) 280 | # Return results 281 | return M 282 | end 283 | end 284 | # If loop ends there was no convergence -> Error! 285 | error("Error in VFI - Solution not found") 286 | end 287 | 288 | 289 | #----------------------------------------------------------- 290 | #----------------------------------------------------------- 291 | # Graphs 292 | function VFI_Graphs(M::Model,VFI_Type) 293 | gr() 294 | # Value Function 295 | plt = plot(title="Value Function - n_k=$(M.n_k) - θ_k=$(M.θ_k)",legend=:bottomright,foreground_color_legend = nothing,background_color_legend = nothing) 296 | for i_z=1:M.n_z 297 | plot!(M.k_grid,M.V[i_z,:],linetype=:scatter,label="V(z_$i_z)") 298 | plot!(M.k_grid_fine,M.V_fine[i_z,:],linewidth=2.5,linestyle=(:dash),linecolor=RGB(0.4,0.4,0.4),label=nothing) 299 | end 300 | xlabel!("Capital") 301 | ylabel!("Value") 302 | savefig("./Figures/VFI_"*VFI_Type*"_V_$(M.n_k)_$(M.θ_k).pdf") 303 | # Capital Policy Function Analytical vs 200 304 | plt = plot(title="Policy Function - K - n_k=$(M.n_k) - θ_k=$(M.θ_k)",legend=:bottomright,foreground_color_legend = nothing,background_color_legend = nothing) 305 | plot!(M.k_grid_fine,M.k_grid_fine,lw=1,linecolor=RGB(0.6,0.6,0.6),label=nothing) 306 | for i_z=1:M.n_z 307 | plot!(M.k_grid,M.G_kp[i_z,:],linetype=:scatter,label="G_kp(z_$i_z)") 308 | plot!(M.k_grid_fine,M.G_kp_fine[i_z,:],linewidth=2.5,linestyle=(:dash),linecolor=RGB(0.4,0.4,0.4),label=nothing) 309 | end 310 | xlabel!("Capital") 311 | ylabel!("Capital") 312 | savefig("./Figures/VFI_"*VFI_Type*"_G_kp_$(M.n_k)_$(M.θ_k).pdf") 313 | # Euler Error 200 314 | plt = plot(title="Euler Equation Error (%) - n_k=$(M.n_k) - θ_k=$(M.θ_k)",legend=:bottomright,foreground_color_legend = nothing,background_color_legend = nothing) 315 | plot!(M.k_grid_fine,zeros(M.n_k_fine),lw=1,linecolor=RGB(0.6,0.6,0.6)) 316 | for i_z=1:M.n_z 317 | plot!(M.k_grid_fine,M.Euler[i_z,:],linetype=:scatter,label="z_$i_z") 318 | end 319 | xlabel!("Capital") 320 | ylabel!("Percentage Points") 321 | savefig("./Figures/VFI_"*VFI_Type*"_Euler_$(M.n_k)_$(M.θ_k).pdf") 322 | 323 | println("\n Graphs Completed for VFI_$VFI_Type - n_k=$(M.n_k) - θ_k=$(M.θ_k)\n") 324 | end 325 | 326 | 327 | #----------------------------------------------------------- 328 | #----------------------------------------------------------- 329 | # Bellman operator - EGM 330 | function T_EGM(M::Model) 331 | @unpack p, n_z, MP_z, n_k, k_grid, Y_grid, V, G_kp, G_c = M 332 | @unpack β = p 333 | # println("Initial V = $V \n ") 334 | # Check Monotonicity 335 | if any( diff(V,dims=2).<0 ) 336 | error("V must be monotone for EGM to work") 337 | end 338 | # Define expectation of value function for each (z,k') 339 | EV = β*MP_z.Π*V # Rows are present z and columns are tomorrow's k in fixed grid 340 | # println("Initial EV size=$(size(EV)), and EV=$EV") 341 | # Check Monotonicity 342 | if any( diff(EV,dims=2).<0 ) 343 | error("EV must be monotone for EGM to work") 344 | end 345 | # Define the derivative of EV for each value 346 | # I will be lazy and interpolate to then get the derivative with ForwardDiff 347 | dEV = zeros(size(EV)) 348 | for i_z=1:n_z 349 | # EV_ip = ScaledInterpolations(k_grid,EV[i_z,:], BSpline(Cubic(Line(OnGrid())))) 350 | # dEV_ip(x) = ForwardDiff.derivative(EV_ip,x) 351 | # dEV[i_z,:].= dEV_ip.(k_grid) 352 | # EV_ip = Spline1D(k_grid,EV[i_z,:]) 353 | # dEV[i_z,:].= derivative(EV_ip, k_grid ) 354 | EV_ip = ScaledInterpolations(k_grid,EV[i_z,:], FritschButlandMonotonicInterpolation()) 355 | dEV_ip(x) = ForwardDiff.derivative(EV_ip,x) 356 | dEV[i_z,:].= dEV_ip.(k_grid) 357 | # println("\n i_z=$i_z, dEV=$(dEV[i_z,:]) \n") 358 | end 359 | # Check Monotonicity 360 | if any( dEV.<0 ) 361 | for i_z=1:n_z 362 | println("\n i_z=$i_z, [min,max]=$([minimum(dEV[i_z,:]),maximum(dEV[i_z,:])]) \n") 363 | end 364 | error("dEV must be monotone for EGM to work") 365 | end 366 | # gr() 367 | # plot(title="EV Test") 368 | # plot!(k_grid,EV') 369 | # plot(title="dEV Test") 370 | # plot!(k_grid,dEV') 371 | # Define Consumption from Euler Equation 372 | C_endo = d_utility_inv(dEV,p) 373 | # Define endogenous grid on cash on hand 374 | Y_endo = C_endo .+ M.k_mat 375 | # Sort Y_endo for interpolation 376 | for i_z=1:n_z 377 | sort_ind = sortperm(Y_endo[i_z,:]) 378 | Y_endo[i_z,:] .= Y_endo[i_z,:][sort_ind] 379 | C_endo[i_z,:] .= C_endo[i_z,:][sort_ind] 380 | end 381 | # Define value function on endogenous grid 382 | V_endo = utility(C_endo,p) .+ EV 383 | # Interpolate functions on exogenous grid 384 | for i_z=1:n_z 385 | # V_ip = ScaledInterpolations(Y_endo[i_z,:],V_endo[i_z,:], BSpline(Cubic(Line(OnGrid())))) 386 | V_ip = Spline1D(Y_endo[i_z,:],V_endo[i_z,:]) 387 | V[i_z,:] .= V_ip.(Y_grid[i_z,:]) 388 | # C_ip = ScaledInterpolations(Y_endo[i_z,:],C_endo[i_z,:], BSpline(Cubic(Line(OnGrid())))) 389 | C_ip = Spline1D(Y_endo[i_z,:],C_endo[i_z,:]) 390 | G_c[i_z,:] .= C_ip.(Y_grid[i_z,:]) 391 | G_kp[i_z,:].= Y_grid[i_z,:] .- G_c[i_z,:] 392 | end 393 | # Solve manually if capital is out of bounds 394 | for ind = findall(<=(k_grid[1]),G_kp) 395 | # Minimize square of Euler Error 396 | k_max = Y_grid[ind] - p.c_min 397 | min_result = optimize(x->Euler_Eq(x,ind[1],ind[2],Vk,M),k_grid[1],k_max) 398 | # Check result 399 | converged(min_result) || error("Failed to solve Euler Equation in $(iterations(min_result)) iterations") 400 | # Upddate policy function 401 | G_kp[ind] = min_result.minimizer 402 | G_c[ind] = Y_grid[ind] - G_kp[ind] 403 | EV_ip = ScaledInterpolations(k_grid,EV[ind[1],:], FritschButlandMonotonicInterpolation()) 404 | V[ind] = utility(G_c[ind],p) + EV_ip(G_kp[ind]) 405 | end 406 | # Return Results 407 | # println("T(V) = $V") 408 | # stop 409 | return V, G_kp, G_c 410 | end 411 | 412 | 413 | 414 | 415 | #----------------------------------------------------------- 416 | #----------------------------------------------------------- 417 | # Bellman operator - ECM 418 | function T_ECM(M::Model) 419 | @unpack p, n_z, MP_z, n_k, k_grid, Y_grid, V, G_kp, G_c, k_mat, z_mat, MPk_mat = M 420 | @unpack β, α, δ, z_bar = p 421 | # println("Initial V = \n "); display(V) 422 | # Option 1: Get Vk from interpolation 423 | Vk = zeros(n_z,n_k) 424 | for i_z=1:n_z 425 | V_ip = ScaledInterpolations(k_grid,V[i_z,:], FritschButlandMonotonicInterpolation()) 426 | Vk_ip(x) = ForwardDiff.derivative(V_ip,x) 427 | Vk[i_z,:].= Vk_ip.(k_grid) 428 | # V_ip = Spline1D(k_grid,V[i_z,:]) 429 | # Vk[i_z,:].= derivative(V_ip, k_grid ) 430 | # println("\n i_z=$i_z, Vk=$(Vk[i_z,:]) \n") 431 | # V_ip, dV_ip = spline_NR(collect(k_grid),V[i_z,:]) 432 | # Vk[i_z,:] .= dV_ip.(collect(k_grid)) 433 | 434 | end 435 | # Optioin 2: Get Vk from V (the program actually receives Vk) 436 | # Vk = V 437 | # Check Monotonicity 438 | if any( Vk.<0 ) 439 | for i_z=1:n_z 440 | println("\n i_z=$i_z, [min,max]=$([minimum(Vk[i_z,:]),maximum(Vk[i_z,:])]) \n") 441 | end 442 | error("Vk must be positive for ECM to work") 443 | end 444 | # # Check for maximum allowable consumption 445 | # Vk .= max.(d_utility(Y_grid.-k_grid[1],p).*MPk_mat,Vk) 446 | # Define consumption from envelope condition 447 | G_c = d_utility_inv(Vk./MPk_mat,p) 448 | # Define savings from budget constraint 449 | G_kp.= Y_grid .- G_c 450 | # Solve manually if capital is out of bounds 451 | for ind = findall(<=(k_grid[1]),G_kp) 452 | # Minimize square of Euler Error 453 | k_max = Y_grid[ind] - p.c_min 454 | min_result = optimize(x->Euler_Eq(x,ind[1],ind[2],Vk,M),k_grid[1],k_max) 455 | # Check result 456 | converged(min_result) || error("Failed to solve Euler Equation in $(iterations(min_result)) iterations") 457 | # Upddate policy function 458 | G_kp[ind] = min_result.minimizer 459 | end 460 | # Check for non-negativity (actually bounded by min grid point to avoid extrapolations) 461 | G_kp.= min.(max.(k_grid[1],G_kp),k_grid[end]) 462 | G_c .= Y_grid .- G_kp 463 | # Vk = d_utility(G_c,p).*MPk_mat 464 | # Define expectation of value function for each (z,k') 465 | EV = zeros(n_z,n_k) # E[V(z',k'(z,k))|z] 466 | for i_z=1:n_z 467 | kp = G_kp[i_z,:] 468 | Vp = zeros(n_z,n_k) # rows are zp and columns are k'(z,k) varying over k, z is fixed 469 | for i_zp=1:n_z 470 | # Define interpolated value at (z',G_kp(z,k)) 471 | V_ip = ScaledInterpolations(k_grid,V[i_zp,:], FritschButlandMonotonicInterpolation()) 472 | Vp[i_zp,:].= V_ip.(kp) 473 | end 474 | EV[i_z,:] = MP_z.Π[i_z,:]'*Vp # Rows are present z and columns are future k as in k'=G_kp(z,k) 475 | end 476 | # Update value 477 | # Option 1: Update value function 478 | V.= utility(G_c,p) .+ β*EV 479 | # Option 2: Update derivative of value function 480 | # V = β*MPk_mat.*EV 481 | # Return Results 482 | # println("T(V) = "); display(V) 483 | # stop 484 | return V, G_kp, G_c 485 | end 486 | 487 | 488 | 489 | 490 | 491 | #----------------------------------------------------------- 492 | #----------------------------------------------------------- 493 | # Execute VFI and plot graphs 494 | 495 | # Execute Numerical VFI - EGM 496 | println("===============================================\n Solving VFI with EGM") 497 | @time M_EGM = VFI_Fixed_Point(T_EGM,Model()) 498 | # Graphs 499 | VFI_Graphs(M_EGM,"EGM") 500 | 501 | # Execute Numerical VFI - ECM 502 | println("===============================================\n Solving VFI with ECM") 503 | # Initial value 504 | s_0 = p.β*p.α 505 | C_0 = (1-s_0)*M.Y_grid 506 | # V_0 = d_utility(C_0,p).*M.MPk_mat 507 | V_0 = (1/(1-p.β))*utility(C_0,p) 508 | # V_0 = copy(M_EGM.V) 509 | # V_0 = d_utility(M_EGM.G_c,p).*M.MPk_mat 510 | # println("V_0="); display(V_0) 511 | @time M_ECM = VFI_Fixed_Point(T_ECM,Model(),V_0) 512 | # Graphs 513 | VFI_Graphs(M_ECM,"ECM") 514 | -------------------------------------------------------------------------------- /Julia_Code/Lecture_2_VFI/VFI_Code_Lecture_2.jl: -------------------------------------------------------------------------------- 1 | # VFI Program for Julia 2 | # Sergio Ocampo 3 | # September 2020 4 | # NGM with log utility and full depreciation 5 | # Solve: V(k) = max{ log(zk^alpha-k') +beta*V(k') } 6 | # Solution by grid search: k_grid = {k_1,...,k_I} 7 | cd() # Go to root directory 8 | cd("./Dropbox/Teaching/PhD_Macro_Comp/Julia_Code/Lecture_2_VFI/") 9 | mkpath("Figures") 10 | using Plots 11 | using Parameters # Pkg.add("Parameters") # https://github.com/mauro3/Parameters.jl 12 | println(" ") 13 | println("------------------------") 14 | println("VFI code for NGM with log-utility and full depreciation") 15 | println("PWD: ",pwd()) 16 | println("This code uses Plots and Parameters") 17 | println("------------------------") 18 | println(" ") 19 | 20 | #----------------------------------------------------------- 21 | #----------------------------------------------------------- 22 | # Paramters 23 | # Generate structure for parameters using Parameters module 24 | # We can set default values for our parameters 25 | @with_kw struct Par 26 | # Model Parameters 27 | z::Float64 = 1 ; # Productivity 28 | α::Float64 = 1/3 ; # Production function 29 | β::Float64 = 0.98 ; # Discount factor 30 | # VFI Paramters 31 | max_iter::Int64 = 2000 ; # Maximum number of iterations 32 | dist_tol::Float64 = 1E-9 ; # Tolerance for distance 33 | # Howard's Policy Iterations 34 | H_tol::Float64 = 1E-9 ; # Tolerance for policy function iteration 35 | end 36 | 37 | # Allocate paramters to object p for future calling 38 | p = Par() 39 | 40 | #----------------------------------------------------------- 41 | #----------------------------------------------------------- 42 | # Utility function 43 | function utility(k,kp,p::Par) 44 | @unpack z, α = p 45 | c = z*k.^α - kp 46 | if c>0 47 | return log(c) 48 | else 49 | return -Inf 50 | end 51 | end 52 | 53 | 54 | 55 | #----------------------------------------------------------- 56 | #----------------------------------------------------------- 57 | # Steady state values (funciton) 58 | function SS_values(p::Par) 59 | # This function takes in parameters and provides steady state values 60 | # Parameters: productivity (z), returns to scale (a) and discount factor (b) 61 | # Output: values for capital, production, consumption, rental rate, wage 62 | @unpack z, α, β = p 63 | k_ss = (β*α*z)^(1/(1-α)) 64 | y_ss = z*k_ss^α 65 | c_ss = y_ss - k_ss 66 | r_ss = α*y_ss/k_ss 67 | w_ss = (1-α)*y_ss 68 | return k_ss,y_ss,c_ss,r_ss,w_ss 69 | end 70 | 71 | # Test steady state function 72 | k_ss,y_ss,c_ss,r_ss,w_ss = SS_values(p) 73 | println(" ") 74 | println("------------------------") 75 | println(" Steady State variables") 76 | println(" Quantities: k = $k_ss; y = $y_ss; c = $c_ss;") 77 | println(" Prices: r = $r_ss; w = $w_ss;") 78 | println("------------------------") 79 | println(" ") 80 | 81 | 82 | #----------------------------------------------------------- 83 | #----------------------------------------------------------- 84 | # Analytical solution 85 | 86 | # Value function 87 | function V_analytical(k,p::Par) 88 | # This function takes in paramters and a value of capital 89 | # Output is the value of the value function at that level of capital 90 | # Parameters: productivity (z), returns to scale (a) and discount factor (b) 91 | @unpack z, α, β = p 92 | a_1 = α/(1-α*β) 93 | a_0 = (1/(1-β))*(log(z)+log(1/(1+β*a_1))+β*a_1*log(β*a_1*z/(1+β*a_1*z))) 94 | V = a_0 .+ a_1*log.(k) 95 | return V 96 | end 97 | 98 | # Policy functions 99 | function G_analytical(k,p::Par) 100 | # This function takes in paramters and a value of capital 101 | # Output is the a tuple of optimal capital (kp) and consumption (c) 102 | # Parameters: productivity (z), returns to scale (a) and discount factor (b) 103 | @unpack z, α, β = p 104 | a_1 = α/(1-α*β) 105 | kp = β*α*z*k.^α 106 | c = z*k.^α .- kp 107 | return kp, c 108 | end 109 | 110 | # Euler Equation 111 | function Euler_Error(k,kp,kpp,p::Par) 112 | # Return percentage error in Euler equation 113 | @unpack z, α, β = p 114 | LHS = 1/(z*k^α-kp) 115 | RHS = β*α*z*kp^(α-1)/(z*kp^α-kpp) 116 | return (RHS/LHS-1)*100 117 | end 118 | 119 | # Analytical Suite 120 | function VFI_Analytical_Results(n_k::Int64,G_kp,p::Par) 121 | # Get Grid 122 | k_grid = Make_K_Grid(n_k,p) 123 | # Analytical value function 124 | V_a = V_analytical(k_grid,p) 125 | # Analytical policy function 126 | G_kp_a, G_c_a = G_analytical(k_grid,p) 127 | # Euler error of numerical policy function on grid 128 | Euler = zeros(n_k) 129 | for i=1:n_k 130 | k = k_grid[i] 131 | kp = k_grid[G_kp[i]] 132 | kpp = k_grid[G_kp[G_kp[i]]] 133 | Euler[i] = Euler_Error(k,kp,kpp,p) 134 | end 135 | # Return 136 | return V_a, G_kp_a, G_c_a, Euler 137 | end 138 | 139 | #----------------------------------------------------------- 140 | #----------------------------------------------------------- 141 | # VFI with Grid 142 | # Grid 143 | function Make_K_Grid(n_k,p::Par) 144 | # Get SS 145 | k_ss,y_ss,c_ss,r_ss,w_ss = SS_values(p) 146 | # Get k_grid 147 | k_grid = range(1E-5,2*k_ss;length=n_k) ; # Equally spaced grid between 0 and 2*k_ss 148 | # Return 149 | return k_grid 150 | end 151 | 152 | # Solve VFI with grid search and loops 153 | function VFI_grid(T::Function,k_grid,p::Par) 154 | # VFI paramters 155 | @unpack max_iter, dist_tol = p 156 | # Initialize variables for loop 157 | n_k = length(k_grid) ; # Number of grid nodes 158 | V_old = zeros(n_k) ; # Initial value, a vector of zeros 159 | V_dist = 1 ; # Initialize distance 160 | println(" ") 161 | println("------------------------") 162 | println("VFI - Grid Search - n_k=$n_k") 163 | for iter=1:max_iter 164 | # Update value function 165 | V_new, G_kp, G_c = T(V_old) 166 | # Update distance and iterations 167 | V_dist = maximum(abs.(V_new./V_old.-1)) 168 | # Update old function 169 | V_old = V_new 170 | # Report progress 171 | if mod(iter,100)==0 172 | println(" VFI Loop: iter=$iter, dist=",100*V_dist,"%") 173 | end 174 | # Check convergence and return results 175 | if V_dist<=dist_tol 176 | println("VFI - Grid Search - n_k=$n_k") 177 | println("Iterations = $iter and Distance = ",100*V_dist,"%") 178 | println("------------------------") 179 | println(" ") 180 | return V_new, G_kp, G_c 181 | end 182 | end 183 | # If loop ends there was no convergence -> Error! 184 | error("Error in VFI - Grid Search - Solution not found") 185 | end 186 | 187 | #----------------------------------------------------------- 188 | #----------------------------------------------------------- 189 | # VFI - Grid Search - Loops 190 | 191 | println(" "); println(" "); println(" ") 192 | println("----------------------------------------------------") 193 | println("----------------------------------------------------") 194 | println("Value Function Iteration - Grid Search with Loops") 195 | 196 | 197 | 198 | # Define function for Value update and policy functions 199 | function T_grid_loop(V_old,k_grid,p::Par) 200 | @unpack z, α, β = p 201 | n_k = length(k_grid) 202 | V = zeros(n_k) 203 | G_kp = fill(0,n_k) 204 | G_c = zeros(n_k) 205 | for i = 1:n_k 206 | V_aux = zeros(n_k) ; # Empty vector for auxiliary value of V(i,j) 207 | for j = 1:n_k 208 | # Evaluate potential value function for combinations of 209 | # current capital k_i and future capital k_j 210 | V_aux[j] = utility(k_grid[i],k_grid[j],p) + β*V_old[j] 211 | #println(V_aux[j]," ",k_grid[i]," ",k_grid[j]," ",utility(k_grid[i],k_grid[j],z,a,b) + b*V_old[j]) 212 | end 213 | # Choose maximum value given current capital k_i 214 | V[i], G_kp[i] = findmax(V_aux) 215 | G_c[i] = z*k_grid[i]^α - k_grid[G_kp[i]] 216 | end 217 | return V, G_kp, G_c 218 | end 219 | 220 | 221 | # Solve VFI with grid search and loops 222 | function Solve_VFI_loop(n_k,p::Par) 223 | # Get Grid 224 | k_grid = Make_K_Grid(n_k,p) 225 | # Solve VFI 226 | V, G_kp, G_c = VFI_grid(x->T_grid_loop(x,k_grid,p),k_grid,p) 227 | # Return Solution 228 | return V,G_kp, G_c, k_grid 229 | end 230 | #= I am leaving these lines here as an example of how to do VFI with a single funciton 231 | function VFI_grid_loop(n_k,z,a,b) 232 | println(" ") 233 | println("------------------------") 234 | println("VFI with grid search and loops - n_k=$n_k") 235 | # Get SS 236 | k_ss,y_ss,c_ss,r_ss,w_ss = SS_values(z,a,b) 237 | # Get k_grid 238 | k_grid = range(1E-5,2*k_ss;length=n_k) ; # Equally spaced grid between 0 and 2*k_ss 239 | # Initialize variables for loop 240 | V_old = zeros(n_k) ; # Initial value, a vector of zeros 241 | iter = 0 ; # Iteration index 242 | V_dist = 1 ; # Initialize distance 243 | while iter<=max_iter && V_dist>dist_tol 244 | # Update value function 245 | V_new, G_kp, G_c = T_grid_loop(V_old,k_grid,z,a,b) 246 | # Update distance and iterations 247 | V_dist = maximum(abs.(V_new./V_old.-1)) 248 | iter += 1 249 | # Update old function 250 | V_old = V_new 251 | # Report progress 252 | if mod(iter,50)==0 253 | println(" VFI Loop: iter=$iter, dist=",200*V_dist,"%") 254 | end 255 | end 256 | # Check solution 257 | if (V_dist<=dist_tol) 258 | # Recover value and policy functions 259 | V, G_kp, G_c = T_grid_loop(V_old,k_grid,z,a,b) 260 | # Return 261 | println("VFI with grid search and loops - Completed - n_k=$n_k") 262 | println("Iterations = $iter and Distance = ",200*V_dist,"%") 263 | println("------------------------") 264 | println(" ") 265 | return V, G_kp, G_c, k_grid 266 | else 267 | error("Error in VFI with loops - Solution not found") 268 | end 269 | end 270 | =# 271 | 272 | # Execute Numerical VFI 273 | @time V_20, G_kp_20, G_c_20, k_grid_20 = Solve_VFI_loop(20,p) 274 | @time V_50, G_kp_50, G_c_50, k_grid_50 = Solve_VFI_loop(50,p) 275 | @time V_200, G_kp_200, G_c_200, k_grid_200 = Solve_VFI_loop(200,p) 276 | @time V_1000, G_kp_1000, G_c_1000, k_grid_1000 = Solve_VFI_loop(1000,p) 277 | 278 | # Analytical Solutions 279 | V_20a, G_kp_20a, G_c_20a, Euler_20 = VFI_Analytical_Results(20,G_kp_20,p) 280 | V_50a, G_kp_50a, G_c_50a, Euler_50 = VFI_Analytical_Results(50,G_kp_50,p) 281 | V_200a, G_kp_200a, G_c_200a, Euler_200 = VFI_Analytical_Results(200,G_kp_200,p) 282 | V_1000a, G_kp_1000a, G_c_1000a, Euler_1000 = VFI_Analytical_Results(1000,G_kp_1000,p) 283 | 284 | # Graphs 285 | VFI_Type = "grid_loop" 286 | include("./VFI_Graphs.jl") 287 | 288 | 289 | println("----------------------------------------------------") 290 | println("----------------------------------------------------") 291 | println(" ") 292 | 293 | 294 | #----------------------------------------------------------- 295 | #----------------------------------------------------------- 296 | # VFI - Grid Search - Matrices 297 | println(" "); println(" "); println(" ") 298 | println("----------------------------------------------------") 299 | println("----------------------------------------------------") 300 | println("Value Function Iteration - Grid Search with Matrices") 301 | 302 | 303 | # Define function for Value update and policy functions 304 | function T_grid_mat(V_old,U_mat,k_grid,p::Par) 305 | @unpack z, α, β = p 306 | n_k = length(V_old) 307 | V,G_kp = findmax( U_mat .+ β*repeat(V_old',n_k,1) , dims=2 ) 308 | G_kp = [G_kp[i][2] for i in 1:n_k] # G_kp is a Cartesian index 309 | G_c = z*k_grid.^α .- k_grid[G_kp] 310 | return V, G_kp, G_c 311 | end 312 | 313 | 314 | # Solve VFI with grid search and loops 315 | function Solve_VFI_mat(n_k,p::Par) 316 | # Get Grid 317 | k_grid = Make_K_Grid(n_k,p) 318 | # Utility matrix 319 | U_mat = [utility(k_grid[i],k_grid[j],p) for i in 1:n_k, j in 1:n_k] 320 | # Solve VFI 321 | V, G_kp, G_c = VFI_grid(x->T_grid_mat(x,U_mat,k_grid,p),k_grid,p) 322 | # Return Solution 323 | return V,G_kp, G_c, k_grid 324 | end 325 | #= 326 | function VFI_grid_mat(n_k,z,a,b) 327 | println(" ") 328 | println("------------------------") 329 | println("VFI with grid search and matrices - n_k=$n_k") 330 | # Get SS 331 | k_ss,y_ss,c_ss,r_ss,w_ss = SS_values(z,a,b) 332 | # Get k_grid 333 | k_grid = range(1E-5,2*k_ss;length=n_k) ; # Equally spaced grid between 0 and 2*k_ss 334 | # Utility matrix 335 | U_mat = [utility(k_grid[i],k_grid[j],z,a,b) for i in 1:n_k, j in 1:n_k] 336 | # Initialize variables for loop 337 | V_old = zeros(n_k) ; # Initial value, a vector of zeros 338 | iter = 0 ; # Iteration index 339 | V_dist = 1 ; # Initialize distance 340 | while iter<=max_iter && V_dist>dist_tol 341 | # Update value function 342 | V_new, G_kp, G_c = T_grid_mat(V_old,U_mat,k_grid,z,a,b) 343 | # Update distance and iterations 344 | V_dist = maximum(abs.(V_new./V_old.-1)) 345 | iter += 1 346 | # Update old function 347 | V_old = V_new 348 | # Report progress 349 | if mod(iter,50)==0 350 | println(" VFI Loop: iter=$iter, dist=",200*V_dist,"%") 351 | end 352 | end 353 | # Check solution 354 | if (V_dist<=dist_tol) 355 | # Recover value and policy functions 356 | V, G_kp, G_c = T_grid_loop(V_old,k_grid,z,a,b) 357 | # Return 358 | println("VFI with grid search and matrices - Completed - n_k=$n_k") 359 | println("Iterations = $iter and Distance = ",200*V_dist,"%") 360 | println("------------------------") 361 | println(" ") 362 | return V, G_kp, G_c, k_grid 363 | else 364 | error("Error in VFI with matrices - Solution not found") 365 | end 366 | end 367 | =# 368 | 369 | # Execute Numerical VFI 370 | @time V_20, G_kp_20, G_c_20, k_grid_20 = Solve_VFI_mat(20,p) 371 | @time V_50, G_kp_50, G_c_50, k_grid_50 = Solve_VFI_mat(50,p) 372 | @time V_200, G_kp_200, G_c_200, k_grid_200 = Solve_VFI_mat(200,p) 373 | @time V_1000, G_kp_1000, G_c_1000, k_grid_1000 = Solve_VFI_mat(1000,p) 374 | 375 | # Analytical Solutions 376 | V_20a, G_kp_20a, G_c_20a, Euler_20 = VFI_Analytical_Results(20,G_kp_20,p) 377 | V_50a, G_kp_50a, G_c_50a, Euler_50 = VFI_Analytical_Results(50,G_kp_50,p) 378 | V_200a, G_kp_200a, G_c_200a, Euler_200 = VFI_Analytical_Results(200,G_kp_200,p) 379 | V_1000a, G_kp_1000a, G_c_1000a, Euler_1000 = VFI_Analytical_Results(1000,G_kp_1000,p) 380 | 381 | # Graphs 382 | VFI_Type = "grid_mat" 383 | include("./VFI_Graphs.jl") 384 | 385 | 386 | println("----------------------------------------------------") 387 | println("----------------------------------------------------") 388 | println(" ") 389 | 390 | 391 | #----------------------------------------------------------- 392 | #----------------------------------------------------------- 393 | # VFI - Grid Search - Matrices - Howard's Policy Iteration 394 | println(" "); println(" "); println(" ") 395 | println("----------------------------------------------------") 396 | println("----------------------------------------------------") 397 | println("Value Function Iteration - Howard's Policy Iteration") 398 | 399 | 400 | # Define function for Value update and policy functions 401 | function HT_grid_mat(V_old,U_mat,k_grid,p::Par,n_H) 402 | @unpack z, α, β, H_tol = p 403 | # Get Policy Function 404 | n_k = length(V_old) 405 | V,G_kp = findmax( U_mat .+ β*repeat(V_old',n_k,1) , dims=2 ) 406 | V_old = V 407 | # "Optimal" U for Howard's iteration 408 | U_vec = U_mat[G_kp] 409 | # Howard's policy iteration 410 | # G_kp is a Cartesian Index 411 | for i=1:n_H 412 | V = U_vec .+ β*repeat(V_old',n_k,1)[G_kp] 413 | if maximum(abs.(V./V_old.-1))<=H_tol 414 | break 415 | end 416 | V_old = V 417 | end 418 | # Recover Policy Functions 419 | G_kp = [G_kp[i][2] for i in 1:n_k] # G_kp is a Cartesian index 420 | G_c = z*k_grid.^α .- k_grid[G_kp] 421 | # Return output 422 | return V, G_kp, G_c 423 | end 424 | 425 | 426 | # Solve VFI with Howard's policy iteration 427 | function Solve_VFI_HPI(n_H,n_k,p::Par) 428 | # Get Grid 429 | k_grid = Make_K_Grid(n_k,p) 430 | # Utility matrix 431 | U_mat = [utility(k_grid[i],k_grid[j],p) for i in 1:n_k, j in 1:n_k] 432 | # Solve VFI 433 | V, G_kp, G_c = VFI_grid(x->HT_grid_mat(x,U_mat,k_grid,p,n_H),k_grid,p) 434 | # Return Solution 435 | return V,G_kp, G_c, k_grid 436 | end 437 | #= 438 | function VFI_HPI_grid_mat(n_H,n_k,z,a,b) 439 | println(" ") 440 | println("------------------------") 441 | println("VFI with Howard's Policy Iteration - n_k=$n_k") 442 | # Get SS 443 | k_ss,y_ss,c_ss,r_ss,w_ss = SS_values(z,a,b) 444 | # Get k_grid 445 | k_grid = range(1E-5,2*k_ss;length=n_k) ; # Equally spaced grid between 0 and 2*k_ss 446 | # Utility matrix 447 | U_mat = [utility(k_grid[i],k_grid[j],z,a,b) for i in 1:n_k, j in 1:n_k] 448 | # Initialize variables for loop 449 | V_old = zeros(n_k) ; # Initial value, a vector of zeros 450 | iter = 0 ; # Iteration index 451 | V_dist = 1 ; # Initialize distance 452 | while iter<=max_iter && V_dist>dist_tol 453 | # Update value function 454 | V_new, G_kp, G_c = HT_grid_mat(V_old,U_mat,k_grid,z,a,b,n_H) 455 | # Update distance and iterations 456 | V_dist = maximum(abs.(V_new./V_old.-1)) 457 | iter += 1 458 | # Update old function 459 | V_old = V_new 460 | # Report progress 461 | if mod(iter,50)==0 462 | println(" VFI Loop: iter=$iter, dist=",200*V_dist,"%") 463 | end 464 | end 465 | # Check solution 466 | if (V_dist<=dist_tol) 467 | # Recover value and policy functions 468 | V, G_kp, G_c = T_grid_loop(V_old,k_grid,z,a,b) 469 | # Return 470 | println("VFI with Howard's Policy Iteration - Completed - n_k=$n_k") 471 | println("Iterations = $iter and Distance = ",200*V_dist,"%") 472 | println("------------------------") 473 | println(" ") 474 | return V, G_kp, G_c, k_grid 475 | else 476 | error("Error in VFI with Howard's Policy Iteration - Solution not found") 477 | end 478 | end 479 | =# 480 | 481 | # Execute Numerical VFI 482 | @time V_20, G_kp_20, G_c_20, k_grid_20 = Solve_VFI_HPI(20,20,p) 483 | @time V_50, G_kp_50, G_c_50, k_grid_50 = Solve_VFI_HPI(20,50,p) 484 | @time V_200, G_kp_200, G_c_200, k_grid_200 = Solve_VFI_HPI(20,200,p) 485 | @time V_1000, G_kp_1000, G_c_1000, k_grid_1000 = Solve_VFI_HPI(20,1000,p) 486 | 487 | 488 | # Analytical Solutions 489 | V_20a, G_kp_20a, G_c_20a, Euler_20 = VFI_Analytical_Results(20,G_kp_20,p) 490 | V_50a, G_kp_50a, G_c_50a, Euler_50 = VFI_Analytical_Results(50,G_kp_50,p) 491 | V_200a, G_kp_200a, G_c_200a, Euler_200 = VFI_Analytical_Results(200,G_kp_200,p) 492 | V_1000a, G_kp_1000a, G_c_1000a, Euler_1000 = VFI_Analytical_Results(1000,G_kp_1000,p) 493 | 494 | # Graphs 495 | VFI_Type = "HPI" 496 | include("./VFI_Graphs.jl") 497 | 498 | println("----------------------------------------------------") 499 | println("----------------------------------------------------") 500 | println(" ") 501 | 502 | 503 | 504 | 505 | 506 | #----------------------------------------------------------- 507 | #----------------------------------------------------------- 508 | # VFI - Grid Search - Matrices - MacQueen Porteus Bounds 509 | println(" "); println(" "); println(" ") 510 | println("----------------------------------------------------") 511 | println("----------------------------------------------------") 512 | println("Value Function Iteration - MacQueen-Porteus Bounds") 513 | 514 | # Fixed point with MPB 515 | function VFI_grid_MPB(T::Function,k_grid,p::Par) 516 | @unpack β, max_iter, dist_tol = p 517 | # Initialize variables for loop 518 | n_k = length(k_grid) ; # Number of grid nodes 519 | V_old = zeros(n_k) ; # Initial value, a vector of zeros 520 | iter = 0 ; # Iteration index 521 | V_dist = 1 ; # Initialize distance 522 | println(" ") 523 | println("------------------------") 524 | println("VFI - Grid Search - MPB - n_k=$n_k") 525 | for iter=1:max_iter 526 | # Update value function 527 | V_new, G_kp, G_c = T(V_old) 528 | # MPB and Distance 529 | MPB_l = β/(1-β)*minimum(V_new-V_old) 530 | MPB_h = β/(1-β)*maximum(V_new-V_old) 531 | V_dist = MPB_h - MPB_l 532 | # Update old function 533 | V_old = V_new 534 | # Report progress 535 | if mod(iter,100)==0 536 | println(" VFI Loop: iter=$iter, dist=",100*V_dist,"%") 537 | end 538 | # Check Convergence 539 | if (V_dist<=dist_tol) 540 | # Recover value and policy functions 541 | V = V_old .+ (MPB_l+MPB_h)/2 542 | # Return 543 | println("VFI - Grid Search - MPB - n_k=$n_k") 544 | println("Iterations = $iter and Distance = ",100*V_dist) 545 | println("------------------------") 546 | println(" ") 547 | return V, G_kp, G_c 548 | end 549 | end 550 | # Report error for non-convergence 551 | error("Error in VFI - Grid Search - MPB - Solution not found") 552 | end 553 | 554 | 555 | 556 | # Solve VFI with MPB 557 | function Solve_VFI_MPB(n_k,p::Par) 558 | # Get Grid 559 | k_grid = Make_K_Grid(n_k,p) 560 | # Utility matrix 561 | U_mat = [utility(k_grid[i],k_grid[j],p) for i in 1:n_k, j in 1:n_k] 562 | # Solve VFI 563 | V, G_kp, G_c = VFI_grid_MPB(x->T_grid_mat(x,U_mat,k_grid,p),k_grid,p) 564 | # Return Solution 565 | return V,G_kp, G_c, k_grid 566 | end 567 | 568 | # Execute Numerical VFI 569 | @time V_20, G_kp_20, G_c_20, k_grid_20 = Solve_VFI_MPB(20,p) 570 | @time V_50, G_kp_50, G_c_50, k_grid_50 = Solve_VFI_MPB(50,p) 571 | @time V_200, G_kp_200, G_c_200, k_grid_200 = Solve_VFI_MPB(200,p) 572 | @time V_1000, G_kp_1000, G_c_1000, k_grid_1000 = Solve_VFI_MPB(1000,p) 573 | 574 | 575 | # Analytical Solutions 576 | V_20a, G_kp_20a, G_c_20a, Euler_20 = VFI_Analytical_Results(20,G_kp_20,p) 577 | V_50a, G_kp_50a, G_c_50a, Euler_50 = VFI_Analytical_Results(50,G_kp_50,p) 578 | V_200a, G_kp_200a, G_c_200a, Euler_200 = VFI_Analytical_Results(200,G_kp_200,p) 579 | V_1000a, G_kp_1000a, G_c_1000a, Euler_1000 = VFI_Analytical_Results(1000,G_kp_1000,p) 580 | 581 | # Graphs 582 | VFI_Type = "MPB" 583 | include("./VFI_Graphs.jl") 584 | 585 | 586 | 587 | 588 | 589 | #----------------------------------------------------------- 590 | #----------------------------------------------------------- 591 | # Caution with Euler Errors 592 | # Possible scenarios 593 | x_vec = range(-1,1,1000) 594 | gr() 595 | plot( ) 596 | 597 | gr() 598 | G_kp_a, G_c_a = G_analytical(k_grid,p) 599 | # Euler error of numerical policy function on grid 600 | Euler = zeros(n_k) 601 | for i=1:n_k 602 | k = k_grid[i] 603 | kp = k_grid[G_kp[i]] 604 | kpp = k_grid[G_kp[G_kp[i]]] 605 | Euler[i] = Euler_Error(k,kp,kpp,p) 606 | end -------------------------------------------------------------------------------- /Julia_Code/Lecture_4_Optimization/Optimization_Lecture_4_Code.jl: -------------------------------------------------------------------------------- 1 | # Optimization and Root Finding for Julia 2 | # Sergio Ocampo 3 | # September 2020 4 | # NGM with log utility and full depreciation 5 | # Solve: V(k) = max{ log(zk^alpha-k') +beta*V(k') } 6 | cd() # Go to root directory 7 | cd("./Dropbox/Teaching/PhD_Macro_Comp/Julia_Code/Lecture_4_Optimization/") 8 | mkpath("Figures") 9 | using Plots 10 | # using LateXStrings # Pkg.add("LaTeXStrings") # https://github.com/stevengj/LaTeXStrings.jl 11 | using Dierckx # Pkg.add("Dierckx") # https://github.com/kbarbary/Dierckx.jl 12 | using Interpolations # Pkg.add("Interpolations") # https://github.com/JuliaMath/Interpolations.jl 13 | using ForwardDiff # Pkg.add("ForwardDiff") # https://github.com/JuliaDiff/ForwardDiff.jl 14 | using Optim # Pkg.add("Optim") # https://julianlsolvers.github.io/Optim.jl/stable/ 15 | using Optim: converged, maximum, maximizer, minimizer, iterations 16 | using Roots # Pkg.add("Roots") # https://github.com/JuliaMath/Roots.jl 17 | using Parameters # Pkg.add("Parameters") # https://github.com/mauro3/Parameters.jl 18 | # Call Scaled Interpolation Functions 19 | include("../Lecture_3_Interpolation/Scaled_Interpolation_Functions.jl") 20 | println(" ") 21 | println("------------------------") 22 | println("Optimization in Julia") 23 | println("PWD: ",pwd()) 24 | println("This code uses Plots, Interpolations, Dierckx, ForwardDiff, Optim, Roots, Parameters, ScaledInterpolation") 25 | println("Optimization in the context of the neoclassical growth model") 26 | println("------------------------") 27 | println(" ") 28 | 29 | 30 | #----------------------------------------------------------- 31 | #----------------------------------------------------------- 32 | # Minimum Bracketing Function 33 | # This function comes from Numerical Recipes, Section 10.1 34 | # Input: numbers a,b that initiliaze bracket and objective function F 35 | # Optional: Bounds for the brackeet so that a,b,c ∈ [x_min,x_max] 36 | # Output: numbers (a,b,c) and images (fa,fb,fc) such that afa 48 | a , b = b , a 49 | fa,fb = fb, fa 50 | end 51 | # Guess for value c expanding bracket away from b -> (a,b,c) or (c,b,a) 52 | # Check bounds 53 | c = max( min( b + γ*(b-a) , x_max ) , x_min) 54 | fc = F(c) 55 | # While fb>fc we need to keep on bracketing 56 | while fb>fc 57 | # Compute u (new candidate) by parabolic extrapolation from a, b, c. 58 | # TINY is used to prevent any possible division by zero. 59 | r = (b-a)*(fb-fc) 60 | q = (b-c)*(fb-fa) 61 | u = min(max(b-((b-c)*q-(b-a)*r)/(2*sign(q-r)*max(abs(q-r),Tiny)),x_min),x_max) 62 | u_lim = min(max(b+G_limit*(c-b),x_min),x_max) 63 | # Test various cases for new candidate 64 | if ((b-u)*(u-c) > 0) # Parabolic u is between b and c 65 | fu=F(u) 66 | if (fu < fc) # Got a minimum between b and c so bounds are (b,u,c) 67 | a, fa, b, fb = b, fb, u, fu 68 | break 69 | elseif (fu > fb) # Got a minimum between a and u so bounds are (a,b,u) 70 | c, fc = u, fu 71 | break 72 | end 73 | # If you got here is because candidate u failed 74 | # Get new candidate by expanding interval with golden ratio 75 | # Check bounds 76 | u = max(min( c+γ*(c-b) , x_max ),x_min) 77 | fu = F(u) 78 | elseif ((c-u)*(u-u_lim) > 0.0) # Parabolic u is between c and its limit (ulim) 79 | fu=F(u) 80 | if (fu < fc) # Drop c and replace it with u, get new u with golden expansion 81 | b, c, fb, fc = c, u, fc, fu 82 | u = max(min( c+γ*(c-b) , x_max ),x_min) 83 | fu = F(u) 84 | end 85 | elseif ((u-u_lim)*(u_lim-c) >= 0.0) # U is above its limit, reign it in! 86 | u = u_lim 87 | fu = F(u) 88 | else # Nothing worked, use golden expansion 89 | u = max(min( c+γ*(c-b) , x_max ),x_min) 90 | fu = F(u) 91 | end 92 | # If no break then forget the oldest point and go onto next iteration 93 | # This means assigning b->a, c->b, u-> and forgetting about a 94 | a, b, c, fa, fb, fc = b, c, u, fb, fc, fu 95 | end 96 | # Return solution once out of the loop 97 | # Swap a and c so that ac 99 | a , c = c, a 100 | fa, fc = fc, fa 101 | end 102 | # Minimum bracketed in [a,c] with intermediate point b so that fbc_min 140 | return log(c) 141 | else 142 | return log(c_min) 143 | end 144 | end 145 | 146 | function d_utility(k,kp,p::Par) 147 | @unpack z, α = p 148 | c = z*k.^α - kp 149 | if c>0 150 | return -1/c 151 | else 152 | return -Inf 153 | end 154 | end 155 | 156 | #----------------------------------------------------------- 157 | #----------------------------------------------------------- 158 | # Steady state values (funciton) 159 | function SS_values(p::Par) 160 | # This function takes in parameters and provides steady state values 161 | # Parameters: productivity (z), returns to scale (a) and discount factor (b) 162 | # Output: values for capital, production, consumption, rental rate, wage 163 | @unpack z, α, β = p 164 | k_ss = (β*α*z)^(1/(1-α)) 165 | y_ss = z*k_ss^α 166 | c_ss = y_ss - k_ss 167 | r_ss = α*y_ss/k_ss 168 | w_ss = (1-α)*y_ss 169 | return k_ss,y_ss,c_ss,r_ss,w_ss 170 | end 171 | 172 | # Test steady state function 173 | k_ss,y_ss,c_ss,r_ss,w_ss = SS_values(p) 174 | println(" ") 175 | println("------------------------") 176 | println(" Steady State variables") 177 | println(" Quantities: k = $k_ss; y = $y_ss; c = $c_ss;") 178 | println(" Prices: r = $r_ss; w = $w_ss;") 179 | println("------------------------") 180 | println(" ") 181 | 182 | #----------------------------------------------------------- 183 | #----------------------------------------------------------- 184 | # Grid 185 | function Make_K_Grid(n_k,θ_k,p::Par,scale_type="Poly") 186 | # Get SS 187 | k_ss,y_ss,c_ss,r_ss,w_ss = SS_values(p) 188 | # Get k_grid 189 | if θ_k≠1 190 | if scale_type=="Poly" 191 | k_grid = PolyRange(1E-5,2*k_ss;θ=θ_k,N=n_k) ; # Curved grid between 0 and 2*k_ss 192 | elseif scale_type=="Exp" 193 | # k_grid = ExpRange(1E-5,2*k_ss;θ=θ_k,N=n_k) ; # Curved grid between 0 and 2*k_ss 194 | else 195 | error("scale_type must be either Poly or Exp") 196 | end 197 | else 198 | k_grid = range(1E-5,2*k_ss,length=n_k) 199 | end 200 | # Return 201 | return k_grid 202 | end 203 | 204 | 205 | # Generate structure of model objects 206 | @with_kw struct Model 207 | # Parameters 208 | p::Par = Par() # Model paramters in their own structure 209 | # Grids 210 | θ_k::Float64 = 1 # Curvature of k_grid 211 | n_k::Int64 = 20 # Size of k_grid 212 | n_k_fine::Int64 = 1000 # Size of fine grid for interpolation 213 | k_grid = Make_K_Grid(n_k,θ_k,p) # k_grid for model solution 214 | k_grid_fine = Make_K_Grid(n_k_fine,1,p) # Fine grid for interpolation 215 | # Value and policy functions 216 | V = Array{Float64}(undef,n_k) # Value Function 217 | G_kp = Array{Float64}(undef,n_k) # Policy Function 218 | G_c = Array{Float64}(undef,n_k) # Policy Function 219 | V_fine = Array{Float64}(undef,n_k_fine) # Value Function on fine grid 220 | G_kp_fine = Array{Float64}(undef,n_k_fine) # Policy Function on fine grid 221 | G_c_fine = Array{Float64}(undef,n_k_fine) # Policy Function on fine grid 222 | # Anaytical Solutions 223 | V_a = Array{Float64}(undef,n_k_fine) # Analytical Value Function on fine grid 224 | G_kp_a = Array{Float64}(undef,n_k_fine) # Analytical Policy Function on fine grid 225 | G_c_a = Array{Float64}(undef,n_k_fine) # Analytical Policy Function on fine grid 226 | Euler = Array{Float64}(undef,n_k_fine) # Errors in Euler equation 227 | end 228 | 229 | M = Model() 230 | 231 | 232 | #----------------------------------------------------------- 233 | #----------------------------------------------------------- 234 | # Analytical solution 235 | 236 | # Value function 237 | function V_analytical(k,p::Par) 238 | # This function takes in paramters and a value of capital 239 | # Output is the value of the value function at that level of capital 240 | # Parameters: productivity (z), returns to scale (a) and discount factor (b) 241 | @unpack z, α, β = p 242 | a_1 = α/(1-α*β) 243 | a_0 = (1/(1-β))*(log(z)+log(1/(1+β*a_1))+β*a_1*log(β*a_1*z/(1+β*a_1*z))) 244 | V = a_0 .+ a_1*log.(k) 245 | return V 246 | end 247 | 248 | # Policy functions 249 | function G_analytical(k,p::Par) 250 | # This function takes in paramters and a value of capital 251 | # Output is the a tuple of optimal capital (kp) and consumption (c) 252 | # Parameters: productivity (z), returns to scale (a) and discount factor (b) 253 | @unpack z, α, β = p 254 | a_1 = α/(1-α*β) 255 | kp = β*α*z*k.^α 256 | c = z*k.^α .- kp 257 | return kp, c 258 | end 259 | 260 | # Euler Equation 261 | function Euler_Error(k,kp,kpp,p::Par) 262 | # Return percentage error in Euler equation 263 | @unpack z, α, β = p 264 | LHS = 1/(z*k^α-kp) 265 | RHS = β*α*z*kp^(α-1)/(z*kp^α-kpp) 266 | return (RHS/LHS-1)*100 267 | end 268 | 269 | # Analytical Suite 270 | function VFI_Analytical_Results(M::Model) 271 | @unpack n_k_fine, k_grid_fine, p = M 272 | # Analytical value function 273 | V_a = V_analytical(k_grid_fine,p) 274 | # Analytical policy function 275 | G_kp_a, G_c_a = G_analytical(k_grid_fine,p) 276 | # Interpolation of G_kp 277 | G_kp_ip = ScaledInterpolations(M.k_grid,M.G_kp, BSpline(Cubic(Line(OnGrid())))) 278 | # Euler error of numerical policy function on grid 279 | Euler = zeros(n_k_fine) 280 | for i=1:n_k_fine 281 | k = k_grid_fine[i] 282 | kp = G_kp_ip(k) 283 | kpp = G_kp_ip(kp) 284 | Euler[i] = Euler_Error(k,kp,kpp,p) 285 | end 286 | # Return 287 | return V_a, G_kp_a, G_c_a, Euler 288 | end 289 | 290 | #----------------------------------------------------------- 291 | #----------------------------------------------------------- 292 | # VFI Fixed Point 293 | function VFI_Fixed_Point(T::Function,M::Model) 294 | # Unpack model structure 295 | @unpack p, n_k, θ_k, k_grid = M 296 | # VFI paramters 297 | @unpack max_iter, dist_tol = p 298 | # Initialize variables for loop 299 | V_old = zeros(n_k) ; # Initialize value function, here I just do 0, not the best option 300 | # V_old = utility.(collect(k_grid),zeros(n_k),p) ; # Start at utility with zero savings 301 | V_dist = 1 ; # Initialize distance 302 | println(" ") 303 | println("------------------------") 304 | println("VFI - n_k=$n_k - θ_k=$θ_k") 305 | for iter=1:max_iter 306 | # Update value function 307 | V_new, G_kp, G_c = T(Model(M,V=copy(V_old))) 308 | # println("T(V) = $V_new") 309 | # println(" V = $V_old") 310 | # Update distance and iterations 311 | V_dist = maximum(abs.(V_new./V_old.-1)) 312 | # Update old function 313 | V_old = V_new 314 | # Report progress 315 | if mod(iter,100)==0 316 | println(" VFI Loop: iter=$iter, dist=",100*V_dist,"%") 317 | end 318 | # Check convergence and return results 319 | if V_dist<=dist_tol 320 | println("VFI - n_k=$n_k - θ_k=$θ_k") 321 | println("Iterations = $iter and Distance = ",100*V_dist,"%") 322 | println("------------------------") 323 | println(" ") 324 | # Interpolate to fine grid 325 | V_ip = ScaledInterpolations(M.k_grid,V_new, BSpline(Cubic(Line(OnGrid())))) 326 | V_fine = V_ip.(collect(M.k_grid_fine)) 327 | G_kp_ip = ScaledInterpolations(M.k_grid,G_kp, BSpline(Cubic(Line(OnGrid())))) 328 | G_kp_fine = G_kp_ip.(collect(M.k_grid_fine)) 329 | G_c_ip = ScaledInterpolations(M.k_grid,G_c, BSpline(Cubic(Line(OnGrid())))) 330 | G_c_fine = G_c_ip.(collect(M.k_grid_fine)) 331 | # Update model 332 | M = Model(M; V=V_new,G_kp=G_kp,G_c=G_c,V_fine=V_fine,G_kp_fine=G_kp_fine,G_c_fine=G_c_fine) 333 | # Analytical results 334 | V_a, G_kp_a, G_c_a, Euler = VFI_Analytical_Results(M) 335 | # Update model 336 | M = Model(M; V_a=V_a,G_kp_a=G_kp_a,G_c_a=G_c_a,Euler=Euler) 337 | # Return results 338 | return M 339 | end 340 | end 341 | # If loop ends there was no convergence -> Error! 342 | error("Error in VFI - Solution not found") 343 | end 344 | 345 | 346 | #----------------------------------------------------------- 347 | #----------------------------------------------------------- 348 | # Graphs 349 | function VFI_Graphs(M::Model,VFI_Type) 350 | gr() 351 | # Value Function Analytical vs 200 352 | plot(M.k_grid_fine,M.V_a,linewidth=4,label = "Analytical Solution",title="Value Function",legend=(0.75,0.2),foreground_color_legend = nothing,background_color_legend = nothing) 353 | plot!(M.k_grid,M.V,linetype=:scatter,marker=(:diamond,4),markercolor=RGB(0.5,0.1,0.1),label="VFI - n_k=$(M.n_k) - θ_k=$(M.θ_k)") 354 | plot!(M.k_grid_fine,M.V_fine,linewidth=2.5,linestyle=(:dash),linecolor=RGB(0.4,0.4,0.4),label=nothing) 355 | xlabel!("Capital") 356 | ylabel!("Value") 357 | savefig("./Figures/VFI_"*VFI_Type*"_V_$(M.n_k)_$(M.θ_k).pdf") 358 | # Capital Policy Function Analytical vs 200 359 | plot(M.k_grid_fine,M.k_grid_fine,lw=1,linecolor=RGB(0.6,0.6,0.6),label=nothing) 360 | plot!(M.k_grid_fine,M.G_kp_a,linewidth=3,label = "Analytical Solution",title="Policy Function - K",legend=(0.75,0.2),foreground_color_legend = nothing,background_color_legend = nothing) 361 | plot!(M.k_grid,M.G_kp,linetype=:scatter,marker=(:diamond,4),markercolor=RGB(0.5,0.1,0.1),label="VFI - n_k=$(M.n_k) - θ_k=$(M.θ_k)") 362 | plot!(M.k_grid_fine,M.G_kp_fine,linewidth=2.5,linestyle=(:dash),linecolor=RGB(0.4,0.4,0.4),label=nothing) 363 | xlabel!("Capital") 364 | ylabel!("Capital") 365 | savefig("./Figures/VFI_"*VFI_Type*"_G_kp_$(M.n_k)_$(M.θ_k).pdf") 366 | # Euler Error 200 367 | plot(M.k_grid_fine,zeros(M.n_k_fine),lw=1,linecolor=RGB(0.6,0.6,0.6),label=nothing,title = "Euler Equation Error (%)",legend=(0.75,0.2),foreground_color_legend = nothing,background_color_legend = nothing) 368 | plot!(M.k_grid_fine,M.Euler,linetype=:scatter,marker=(:diamond,2),linecolor=RGB(0.5,0.1,0.1),label="VFI - n_k=$(M.n_k) - θ_k=$(M.θ_k)") 369 | xlabel!("Capital") 370 | ylabel!("Percentage Points") 371 | savefig("./Figures/VFI_"*VFI_Type*"_Euler_$(M.n_k)_$(M.θ_k).pdf") 372 | 373 | println("\n Graphs Completed for VFI_$VFI_Type - n_k=$(M.n_k) - θ_k=$(M.θ_k)\n") 374 | end 375 | 376 | 377 | #----------------------------------------------------------- 378 | #----------------------------------------------------------- 379 | # Bellman operator - Continuous Choice 380 | function T_cts_max(M::Model) 381 | @unpack p, n_k, k_grid, V, G_kp, G_c = M 382 | @unpack z, α, β, c_min = p 383 | # println("Initial V = $V") 384 | # Define the interpolation of the current value function (V) for values of Vp 385 | Vp = ScaledInterpolations(k_grid,V, BSpline(Cubic(Line(OnGrid())))) 386 | # println("Interpolation test: Vp(k_min)=",Vp(k_grid[1])," Vp(k/2)=",Vp((k_grid[1]+k_grid[end])/2)) 387 | # Define the derivative (might not be used, depends on algorithm) 388 | # For many methods we don't need to use ForwardDiff, we have direct measures, 389 | # for example for cubic splines. I am using ForwardDiff as an example 390 | # dVp(x) = ForwardDiff.derivative(Vp,x) 391 | for i = 1:n_k 392 | # Define objective function: Right hand side of Bellman operator 393 | # Optimizer will minimize so we need the negative of the function 394 | Obj_Fun = x->(-utility(k_grid[i],x,p) - β*Vp.(x)) 395 | # println("Interpolation test: Obj_Fun(k_min)=",Obj_Fun(k_grid[1])," Obj_Fun(k/2)=",Obj_Fun((k_grid[1]+k_grid[end])/2)) 396 | # Min and max kp given current k 397 | kp_min = k_grid[1] 398 | kp_max = z*k_grid[i]^α - c_min 399 | # Note: we can be smarter here. 400 | # We know that if k_grid[i]k_ss then kp_min = k_ss 402 | # Check if lower bound binds 403 | # No need in this problem because without capital there is no production 404 | # also no consumption. So value would be -Inf. 405 | # I am leaving two ways of getting the derivative 406 | # dObj_min = ForwardDiff.derivative(Obj_Fun,kp_min) 407 | # dObj_min = -d_utility(k_grid[i],kp_min,p) - β*dVp(kp_min) 408 | dObj_min = -1 409 | if dObj_min>0 410 | V[i] = -Obj_Fun(kp_min) 411 | G_kp[i] = kp_min 412 | else 413 | # Check if upper bound binds 414 | # This can bind but shouldn't I will leave code to check but comment it 415 | # I am leaving two ways of getting the derivative 416 | # dObj_max = ForwardDiff.derivative(Obj_Fun,kp_max) 417 | # dObj_max = -d_utility(k_grid[i],kp_max,p) - β*dVp(kp_max) 418 | dObj_max = 1 419 | if dObj_max<0 420 | V[i] = -bj_Fun(kp_max) 421 | G_kp[i] = kp_max 422 | else 423 | # Bracket the solution for k=k_grid[i] 424 | # In this case this is not necessary, we know the min is bracketed by [kp_min and kp_max] 425 | # We can still try to use mnbrak to get a better bracket, but in this case the algorithm 426 | # stops in the initial evaluation, it just verifies that we do have a bracket 427 |   kp_min, kp_max = mnbrak(kp_min,(kp_max+kp_min)/2,Obj_Fun,kp_min,kp_max) 428 | # Maximize 429 | min_result = optimize(Obj_Fun,kp_min,kp_max) 430 | # Check result 431 | converged(min_result) || error("Failed to solve Bellman max in $(iterations(min_result)) iterations") 432 | # Record results 433 | V[i] = -min_result.minimum 434 | G_kp[i] = min_result.minimizer 435 | # println(" Maximization result k_grid[$i] - kp=",min_result.minimizer," V(k)=",min_result.minimum," in $(iterations(min_result)) iterations") 436 | end # Upper bound if   437 | end # Lower bound if 438 | end # loop of k_grid[i] 439 | # Fill in policy for consumption 440 | G_c = z.*collect(k_grid).^α .- G_kp 441 | # Return Results 442 | # println("T(V) = $V") 443 | return V, G_kp, G_c 444 | end 445 | 446 | 447 | #----------------------------------------------------------- 448 | #----------------------------------------------------------- 449 | # Bellman operator - Continuous Choice 450 | function T_cts_root(M::Model) 451 | @unpack p, n_k, k_grid, V, G_kp, G_c = M 452 | @unpack z, α, β, c_min = p 453 | # println("Initial V = $V") 454 | # Define the interpolation of the current value function (V) for values of Vp 455 | Vp = ScaledInterpolations(k_grid,V, BSpline(Cubic(Line(OnGrid())))) 456 | # Define the derivative 457 | # For many methods we don't need to use ForwardDiff, we have direct measures, 458 | # for example for cubic splines. I am using ForwardDiff as an example 459 | dVp(x) = ForwardDiff.derivative(Vp,x) 460 | for i = 1:n_k 461 | # Define objective function: Right hand side of Bellman operator 462 | # Optimizer will minimize so we need the negative of the function 463 | Obj_Fun = x->( -utility(k_grid[i],x,p) - β*Vp.(x)) # Note the negative sign 464 | d_Obj_Fun = x->(d_utility(k_grid[i],x,p) + β*dVp.(x)) # Note there is no negative sign 465 | # d_Obj_Fun(x) = ForwardDiff.derivative(Obj_Fun,x) 466 | # println("Interpolation test: Obj_Fun(k_min)=",Obj_Fun(k_grid[1])," Obj_Fun(k/2)=",Obj_Fun((k_grid[1]+k_grid[end])/2)," Vp(k_min)=",Vp(k_grid[1])) 467 | # println("Interpolation test: dObj_Fun(k_min)=",d_Obj_Fun(k_grid[1])," dObj_Fun(k/2)=",d_Obj_Fun((k_grid[1]+k_grid[end])/2)," dVp(k_min)=",dVp(k_grid[1])) 468 | # Min and max kp given current k 469 | kp_min = 1.001*k_grid[1] # We set the lowest point just above k_grid[1] to allow numerical derivative of Vp. dVp(k_grid[1])=NaN 470 | kp_max = min( z*k_grid[i]^α - c_min , k_grid[end] ) 471 | # println("\n dObj_Fun=$(d_Obj_Fun.(range(kp_min,kp_max,length=15)))") 472 | # Note: we can be smarter here. 473 | # We know that if k_grid[i]k_ss then kp_min = k_ss 475 | # Check if lower bound binds 476 | # No need in this problem because without capital there is no production 477 | # also no consumption. So value would be -Inf. 478 | dObj_min = d_Obj_Fun(kp_min) 479 | if dObj_min<0 480 | V[i] = -Obj_Fun(kp_min) 481 | G_kp[i] = kp_min 482 | elseif isnan(dObj_min) 483 | error("dObj_min=NaN") 484 | else 485 | # Check if upper bound binds 486 | # This can bind but shouldn't I will leave code to check but comment it 487 | dObj_max = d_Obj_Fun(kp_max) 488 | if dObj_max>0 489 | V[i] = -Obj_Fun(kp_max) 490 | G_kp[i] = kp_max 491 | elseif isnan(dObj_max) 492 | errlr("dObj_max=NaN") 493 | else 494 | # Find root (also check for bracketing) 495 | if dObj_min*dObj_max<0 496 | # println("kp_min=$kp_min, c=$(z*k_grid[1]^α-kp_min) dObj_min=$dObj_min, dVp=$(dVp(kp_min)), dU=$(d_utility(k_grid[1],kp_min,p))") 497 | # println("kp_max=$kp_max, c=$(z*k_grid[1]^α-kp_max) dObj_max=$dObj_max, dVp=$(dVp(kp_max)), dU=$(d_utility(k_grid[1],kp_max,p))") 498 | G_kp[i] = find_zero(d_Obj_Fun,(kp_min,kp_max),Roots.Brent()) 499 | else 500 | error("Zero not bracketed by kp_min=$kp_min and kp_max=$kp_max, DF_min=$dObj_min, DF_max=$dObj_max") 501 | end 502 | # Check result 503 | if abs(d_Obj_Fun(G_kp[i]))>1E-6 504 | error("Failed to solve Bellman max") 505 | end 506 | # Record results 507 | V[i] = -Obj_Fun(G_kp[i]) 508 | # println(" Maximization result k_grid[$i] - kp=",min_result.minimizer," V(k)=",min_result.minimum," in $(iterations(min_result)) iterations") 509 | end # Upper bound if   510 | end # Lower bound if 511 | end # loop of k_grid[i] 512 | # Fill in policy for consumption 513 | G_c = z.*collect(k_grid).^α .- G_kp 514 | # Return Results 515 | # println("T(V) = $V") 516 | return V, G_kp, G_c 517 | end 518 | 519 | 520 | 521 | 522 | #----------------------------------------------------------- 523 | #----------------------------------------------------------- 524 | # Execute VFI and plot graphs 525 | 526 | # Execute Numerical VFI - equally spaced grid 527 | @time M_20 = VFI_Fixed_Point(T_cts_max,Model(n_k=20)) 528 | @time M_50 = VFI_Fixed_Point(T_cts_max,Model(n_k=50)) 529 | @time M_100 = VFI_Fixed_Point(T_cts_max,Model(n_k=100)) 530 | # Graphs 531 | VFI_Graphs(M_20,"cts_max") 532 | VFI_Graphs(M_50,"cts_max") 533 | VFI_Graphs(M_100,"cts_max") 534 | # Execute Numerical VFI - curved spaced grid 535 | @time M_20c = VFI_Fixed_Point(T_cts_max,Model(n_k=20,θ_k=2.5)) 536 | @time M_50c = VFI_Fixed_Point(T_cts_max,Model(n_k=50,θ_k=2.5)) 537 | @time M_100c = VFI_Fixed_Point(T_cts_max,Model(n_k=100,θ_k=2.5)) 538 | # Graphs 539 | VFI_Graphs(M_20c,"cts_max") 540 | VFI_Graphs(M_50c,"cts_max") 541 | VFI_Graphs(M_100c,"cts_max") 542 | 543 | 544 | # Execute Numerical VFI - equally spaced grid 545 | # @time M_20 = VFI_Fixed_Point(T_cts_root,Model(n_k=20)) 546 | # @time M_50 = VFI_Fixed_Point(T_cts_root,Model(n_k=50)) 547 | @time M_100 = VFI_Fixed_Point(T_cts_root,Model(n_k=100)) 548 |  # Graphs 549 | # VFI_Graphs(M_20,"cts_root") 550 | # VFI_Graphs(M_50,"cts_root") 551 | VFI_Graphs(M_100,"cts_root") 552 | 553 | # Execute Numerical VFI - curved spaced grid 554 | @time M_20c = VFI_Fixed_Point(T_cts_root,Model(n_k=20,θ_k=2.5)) 555 | @time M_50c = VFI_Fixed_Point(T_cts_root,Model(n_k=50,θ_k=2.5)) 556 | @time M_100c = VFI_Fixed_Point(T_cts_root,Model(n_k=100,θ_k=2.5)) 557 | # Graphs 558 | VFI_Graphs(M_20c,"cts_root") 559 | VFI_Graphs(M_50c,"cts_root") 560 | VFI_Graphs(M_100c,"cts_root") 561 | -------------------------------------------------------------------------------- /Julia_Code/VFI_Toolbox.jl: -------------------------------------------------------------------------------- 1 | # Toolbox for VFI in Julia 2 | # Sergio Ocampo 3 | # September 2020 4 | 5 | # module VFI_Toolbox 6 | 7 | #----------------------------------------------------------- 8 | #----------------------------------------------------------- 9 | using Parameters # Pkg.add("Parameters") # https://github.com/mauro3/Parameters.jl 10 | using Distributions #Pkg.add("Distributions") 11 | using LinearAlgebra 12 | using Random 13 | using Interpolations 14 | 15 | #----------------------------------------------------------- 16 | #----------------------------------------------------------- 17 | 18 | 19 | #----------------------------------------------------------- 20 | #----------------------------------------------------------- 21 | # Cubic spline interpolation 22 | #----------------------------------------------------------- 23 | #----------------------------------------------------------- 24 | 25 | #----------------------------------------------------------- 26 | # Solve tri-diagonal system 27 | # Solves for a vector u of size N the tridiagonal linear set given by equation (2.4.1) in NR 28 | # using a serial algorithm. 29 | # Input vectors b (diagonal elements) and r (right-hand sides) have size N, 30 | # while a and c (off-diagonal elements) are size N − 1. 31 | function tridag(a,b,c,r) 32 | # Check that sizes agree 33 | n = length(a)+1 # Lenght of vectors 34 | if any( [length(b) length(c)+1 length(r)].!=n) 35 | error("Interpolation requires length(x)==length(y)") 36 | end 37 | # Check boundary conditions 38 | bet = b[1] 39 | if (bet == 0) 40 | error("tridag: Error at code stage 1") 41 | # If this happens then you should rewrite your equations as a set of order N − 1, 42 | # with u2 trivially eliminated. 43 | end 44 | # Solution of system 45 | u = zeros(n) 46 | g = zeros(n) 47 | u[1]=r[1]/bet 48 | for j=2:n 49 | g[j] = c[j-1]/bet 50 | bet = b[j]-a[j-1]*g[j] 51 | if (bet == 0) 52 | error("tridag_ser: Error at code stage 2") 53 | # Decomposition and forward substitution. 54 | # Algorithm fails; see below routine in Vol. 1. of NR 55 | end 56 | u[j]=(r[j]-a[j-1]*u[j-1])/bet 57 | end 58 | for j=n-1:-1:1 59 | u[j]=u[j]-g[j+1]*u[j+1] 60 | end 61 | return u 62 | end 63 | 64 | 65 | #----------------------------------------------------------- 66 | # Get second derivatives 67 | function spline_ypp(x,y,yp1=nothing,ypn=nothing) 68 | # Check that x grid is ordered 69 | if any(diff(x).<0) 70 | error("Grid for x must be increasing") 71 | end 72 | # Check that sizes agree 73 | if length(x)!=length(y) 74 | error("Interpolation requires length(x)==length(y)") 75 | end 76 | n = length(x) # Lenght of vectors 77 | # Set up tri-diagonal system 78 | a = Array{Float64}(undef,n) 79 | b = Array{Float64}(undef,n) 80 | c = Array{Float64}(undef,n) 81 | r = Array{Float64}(undef,n) 82 | # Fill in elements for tri-diagonal system 83 | c[1:n-1].= x[2:n].-x[1:n-1] 84 | r[1:n-1].= 6*((y[2:n].-y[1:n-1])./c[1:n-1]) 85 | r[2:n-1].= r[2:n-1].-r[1:n-2] 86 | a[2:n-1].= c[1:n-2] 87 | b[2:n-1].= 2*(c[2:n-1].+a[2:n-1]) 88 | b[1] = 1 89 | b[n] = 1 90 | # Lower Boundary 91 | if yp1==nothing # Use natural spline 92 | r[1] = 0 93 | a[1] = 0 94 | else # User supplied a derivative 95 | r[1] = (3/(x[2]-x[1]))*((y[2]-y[1])/(x[2]-x[1])-yp1) 96 | c[1] = 0.5 97 | end 98 | # Upper Boundary 99 | if ypn==nothing # Use natural spline 100 | r[n] = 0 101 | c[n] = 0 102 | else # User supplied a derivative 103 | r[n] = (-3/(x[n]-x[n-1]))*((y[n]-y[n-1])/(x[n]-x[n-1])-ypn) 104 | a[n] = 0.5 105 | end 106 | # Compute second derivatives from tri-diagonal system 107 | ypp = tridag(a[2:n],b[1:n],c[1:n-1],r[1:n]) 108 | return ypp 109 | end 110 | #----------------------------------------------------------- 111 | 112 | #----------------------------------------------------------- 113 | # Get interpolation 114 | # Given the arrays x and y, which tabulate a function 115 | # (with the xi’s in increasing order), 116 | # and given the array ypp, which is the output from spline above, 117 | # and given a value of x, this routine returns a cubic-spline interpolated value. 118 | # The arrays x, y and ypp are all of the same size. 119 | function spline_itp(x,y,ypp,z) 120 | # Check that z∈[x_min,x_max] 121 | if zmaximum(x) 122 | error("Interpolation point z must be inside the grid bounds") 123 | end 124 | # Defien grid size and bracket z in grid 125 | n = length(x) # Lenght of vectors 126 | khi = findmax(sign.(x .- z))[2] # Index of higher brakcet 127 | klo = khi-1 # Index of lower braket 128 | h = x[khi]-x[klo] # Size of bracket 129 | # Define convexx weights 130 | a = (x[khi]-z)/h 131 | b = (z-x[klo])/h 132 | # Evaluate cubic spline 133 | itp = a*y[klo]+b*y[khi]+((a^3-a)*ypp[klo]+(b^3-b)*ypp[khi])*(h^2)/6 134 | return itp 135 | end 136 | #----------------------------------------------------------- 137 | 138 | #----------------------------------------------------------- 139 | # Get first derivative of interpolation 140 | function spline_ditp(x,y,ypp,z) 141 | # Check that z∈[x_min,x_max] 142 | if zmaximum(x) 143 | error("Interpolation point z must be inside the grid bounds") 144 | end 145 | # Defien grid size and bracket z in grid 146 | n = length(x) # Lenght of vectors 147 | khi = findmax(sign.(x .- z))[2] # Index of higher brakcet 148 | klo = khi-1 # Index of lower braket 149 | h = x[khi]-x[klo] # Size of bracket 150 | # Define convexx weights 151 | a = (x[khi]-z)/h 152 | b = (z-x[klo])/h 153 | # Evaluate cubic spline 154 | ditp = (y[khi]-y[klo])/(x[khi]-x[klo]) - ((3*a^2-1)*ypp[klo] + (3*b^2-1)*ypp[khi])*(x[khi]-x[klo])/6 155 | return ditp 156 | end 157 | #----------------------------------------------------------- 158 | 159 | #----------------------------------------------------------- 160 | # Wraper to get function 161 | function spline_NR(x,y,yp1=nothing,ypn=nothing) 162 | # Get second derivatives 163 | ypp = spline_ypp(x,y,yp1,ypn) 164 | # Define interpolation function 165 | F(z) = spline_itp(x,y,ypp,z) 166 | # Define derivative interpolation function 167 | dF(z) = spline_ditp(x,y,ypp,z) 168 | return F,dF 169 | end 170 | #----------------------------------------------------------- 171 | 172 | 173 | 174 | #----------------------------------------------------------- 175 | #----------------------------------------------------------- 176 | # Markov Processes Block 177 | #----------------------------------------------------------- 178 | #----------------------------------------------------------- 179 | # MP: struct to hold Markov Processes 180 | # Tauchen86: Discretize AR(1) with Tauchen 1986 method 181 | # Rouwenhorst95: Discretize AR(1) with Rouwenhorst 1995 method 182 | # Simulation_MC: Simulate a Markov Chain from a Markov Process MP 183 | 184 | #----------------------------------------------------------- 185 | #----------------------------------------------------------- 186 | # Defien a markov process struct 187 | # Generate structure for markov processes using Parameters module 188 | @with_kw struct MP 189 | # Model Parameters 190 | N::Int64 # Number of states 191 | grid # Grid of discrete markov process 192 | Π # Transition matrix 193 | PDF # Stationary distribution 194 | CDF # Stationary distribution 195 | end 196 | 197 | #----------------------------------------------------------- 198 | #----------------------------------------------------------- 199 | # Tauchen (1986) 200 | # Objective is to discretize AR(1) process: z'=ρz+η, η~N(0,σ) 201 | # Code from Kopecky & Suen (2010) 202 | # Inputs: 203 | # ρ - Process persisntence 204 | # σ - Innovation standard deviation 205 | # N - Size of the grid 206 | # Ω - Grid expansion in number of standard devaitions (Optional) 207 | # Outputs: 208 | # z - Grid of N equally spaced points covering [-Ωσ,Ωσ] 209 | # Π - Transition matrix, a stochastic matrix (sums to 1 across columns) 210 | # PDF_z, CDF_z - Stationary distribution of z 211 | function Tauchen86(ρ,σ,N,Ω::Any=3) 212 | # Check if N>1 213 | if N==1 214 | z = 0; Π = 1; PDF_z = 1; 215 | return MP(N=N,grid=0,Π=1,PDF=1,CDF=1) 216 | end 217 | # Create z grid 218 | z = range(-Ω*σ/sqrt(1-ρ^2),Ω*σ/sqrt(1-ρ^2),length=N) 219 | # Define intermediate step length 220 | h = (z[2]-z[1])/2 221 | # Define auxiliary matrices 222 | z_0 = repeat(z ,1,N) # Matrix of today's z each row is a value, columns are equal 223 | z_1 = repeat(z',N,1) # Matrix of tomorrow's z each column is a value, rows are equal 224 | # Define intervals 225 | z_lim = zeros(N,N,2) # First matrix is lower bounds. Second matrix is uppor bounds. 226 | z_lim[:,1 ,1] .= -Inf 227 | z_lim[:,2:end ,1] .= ( z_1[:,2:end ] - ρ*z_0[:,2:end ] .- h )./σ 228 | z_lim[:,1:end-1,2] .= ( z_1[:,1:end-1] - ρ*z_0[:,1:end-1] .+ h )./σ 229 | z_lim[:,end ,2] .= Inf 230 | # Define reference distribution 231 | # This line uses "Distributions" 232 | F(x) = cdf.(Normal(),x) 233 | # Fill in transition matrix 234 | Π_z = F.(z_lim[:,:,2]) - F.(z_lim[:,:,1]) 235 | Π_z = Π_z./repeat(sum(Π_z,dims=2),1,N) 236 | # Get stationary distribution of markov chain 237 | PDF_z = real(eigvecs(Π_z')[:,end]); PDF_z = PDF_z/sum(PDF_z) ; 238 | CDF_z = cumsum(PDF_z) 239 | # Return 240 | return MP(N=N,grid=z,Π=Π_z,PDF=PDF_z,CDF=CDF_z) 241 | end 242 | 243 | 244 | #----------------------------------------------------------- 245 | #----------------------------------------------------------- 246 | # Rouwenhorst (1995) 247 | # Objective is to discretize AR(1) process: z'=ρz+η, η~N(0,σ) 248 | # Code from Kopecky & Suen (2010) 249 | # Inputs: 250 | # ρ - Process persisntence 251 | # σ - Innovation standard deviation 252 | # N - Size of the grid 253 | # Outputs: 254 | # z - Grid of N equally spaced points covering [-ψ,ψ] 255 | # Π - Transition matrix, a stochastic matrix (sums to 1 across columns) 256 | # PDF_z, CDF_z - Stationary distribution of z 257 | function Rouwenhorst95(ρ,σ,N) 258 | # Check if N>1 259 | if N==1 260 | z = 0; Π = 1; PDF_z = 1; 261 | return MP(N=N,grid=0,Π=1,PDF=1,CDF=1) 262 | end 263 | # Define paramters for Rouwenhorst's approximation 264 | p = (1+ρ)/2 265 | q = p # Note: I am leaving q here for comparability with source 266 | ψ = σ*sqrt((N-1)/(1-ρ^2)) 267 | s = (1-q)/(2-(p+q)) # Note: s=0.5, I leave it for comparability with source 268 | # Fill in transition matrix 269 | if N==2 270 | Π_z = [p 1-p ; 1-q q] 271 | else 272 | MP_aux = Rouwenhorst95(ρ,σ,N-1) 273 | o = zeros(N-1) 274 | Π_z = p*[MP_aux.Π o ; o' 0] + (1-p)*[o MP_aux.Π ; 0 o'] + (1-q)*[o' 0 ; MP_aux.Π o] + q*[0 o' ; o MP_aux.Π] 275 | # Adjust scale for double counting 276 | Π_z = Π_z./repeat(sum(Π_z,dims=2),1,N) 277 | end 278 | # Distribution 279 | PDF_z = pdf.(Binomial(N-1,1-s),(0:N-1)) 280 | CDF_z = cumsum(PDF_z) 281 | # Create z grid 282 | z = range(-ψ,ψ,length=N) 283 | # Return 284 | return MP(N=N,grid=z,Π=Π_z,PDF=PDF_z,CDF=CDF_z) 285 | end 286 | 287 | 288 | #----------------------------------------------------------- 289 | #----------------------------------------------------------- 290 | # Simulation of Markov processes 291 | 292 | # Simulation function for discrete Markov process 293 | # The result of the simulation is a Markov chain 294 | # Inputs: 295 | # Ns - Number of simulated periods 296 | # z - Grid with levels of Markov process 297 | # Π - Transition matrix of Markov process 298 | # N_0- Number of periods to throw before reporting simulation (Optional) 299 | # Output: 300 | # z_MC - Vector of Ns elemenrts with Markov Chain 301 | function Simulation_MC(Ns,MP::MP,N_0=1000) 302 | # Compute conditional CDF 303 | Γ = cumsum(MP.Π,dims=2) 304 | # Allocate simulated vector 305 | z_ind = zeros(Int64,N_0+Ns) 306 | z_MC = zeros(N_0+Ns) 307 | # Starting value for simulation 308 | z_ind[1] = Int(ceil(length(MP.grid)/2)) 309 | z_MC[1] = MP.grid[z_ind[1]] 310 | # Simulate 311 | for i=2:Ns+N_0 312 | #= Option 1 313 | # Draw a uniform random number (r). Compare it with conditional CDF. 314 | # Conditional CDF given by cumsum of current row of Π, call it Γ 315 | # Γ[i,j] gives the probability z'0 318 | z_ind[i] = findmax(sign.(Γ[z_ind[i-1],:] .- rand()))[2] 319 | =# 320 | #= Option 2 321 | # Alternatively we can draw directly from conditional distributional 322 | # Distributional is categorical with P[z'=z_j] = Π[i,j] 323 | =# 324 | z_ind[i] = rand(Categorical(MP.Π[z_ind[i-1],:])) 325 | z_MC[i] = MP.grid[z_ind[i]] 326 | end 327 | # Throw out first N_0 elements 328 | z_MC = z_MC[N_0+1:end] 329 | # Return result 330 | return z_MC 331 | end 332 | #----------------------------------------------------------- 333 | #----------------------------------------------------------- 334 | # End of Markov Processes Block 335 | #----------------------------------------------------------- 336 | #----------------------------------------------------------- 337 | 338 | 339 | ################################################################################ 340 | #################### Scaled Grid Interpolation ################################# 341 | ################################################################################ 342 | # The following code generates three objects: 343 | # 1) A "TypeScale" type that transforms ranges into a type of scaled ranges 344 | # 2) A "TypeRange" type that foroms scaled ranges of a given type. 345 | #  These ranges behave "just as" normal ranges 346 | # 3) A ScaledInterpolations function that wraps "interpolations.jl" to be used 347 | #  with TypeRange ranges 348 | # Types of Scales: parameters bounds for grid (a,b) and curvature (θ) 349 | # p = PolyScale(a,b,θ) 350 | # Maps a number x∈[0,1] to a polynomial grid with curvature θ: p(x)∈[a,b] 351 | # Usage: p(x), where x is a number, or p.(x) where x is vector/range 352 | # p(x) = a + (b - a) * x^θ 353 | # p = InvPolyScale(a,b,θ) 354 | # Maps a number y∈[a,b] from a polynomial grid with curvature θ to the unit interval: p(x)∈[0,1] 355 | # Usage: p(y), where x is a number, or p.(y) where y is vector/range 356 | # p(y) = ((y-a) / (b-a) )^(1/θ) 357 | # p = ExpScale(a,b,θ) 358 | # Maps a number x∈[0,1] to a exponential grid with curvature θ: p(x)∈[a,b] 359 | # Usage: p(x), where x is a number, or p.(x) where x is vector/range 360 | # p(x) = a + (b - a) * ( (exp(θ*x) - 1)/(exp(θ) - 1) ) 361 | # p = LogScale(a,b,θ) 362 | # Maps a number y∈[a,b] from an exponential grid with curvature θ to the unit interval: p(x)∈[0,1] 363 | # Usage: p(y), where x is a number, or p.(y) where y is vector/range 364 | # p(y) = log( (y-a)*(exp(θ) - 1)/(b - a) + 1 ) / θ 365 | # Types of Scaled Ranges 366 | # grid = PolyRange(a,b,θ=t,N=n) = PolyScale(a,b,t)(range(0,1,length=n)) 367 | # grid = ExpRange(a,b,θ=t,N=n) = ExpScale(a,b,t)(range(0,1,length=n)) 368 | # ScaledInterpolations 369 | # itp = ScaledInterpolations(grid,f_grid,Itp_Type) 370 | # grid is either a PolyRange or ExpRange type 371 | # f_grid is a vector with the values of the function at the grid nodes 372 | # Itp_Type is a type from the interpolations.jl package 373 | # Examples: BSpline(Cubic(Line(OnGrid()))) // shortcut: CubicSpline 374 | #  BSpline(Cubic(Flat(OnGrid()))) 375 | # FritschButlandMonotonicInterpolation() // shortcut: MonotoneSpline 376 | #  BSpline(Linear()) // shortcut: LinearInterp 377 | # ScaledInterpolations with multiple dimensions 378 | # itp_md = ScaledInterpolations( (grid_1,...,grid_N) , (f_grid_1,...,f_grid_N) , (Type_1,...,Type_N) )  379 | # Jacob Adenbaum, 2020 380 | 381 | 382 | #------------------------------------------------------------------------------- 383 | #------------------------------------------------------------------------------- 384 | #------------------------------------------------------------------------------- 385 | # Define Scaled types 386 | #------------------------------------------------------------------------------- 387 | # Polynomial Scaling 388 | abstract type Scale{T} end 389 | abstract type ScaledRange{T} end 390 | 391 | for Foo in [:PolyScale, :InvPolyScale] 392 | 393 | @eval struct $Foo{T,TF} <: Scale{T} 394 | a::T 395 | b::T 396 | θ::TF 397 | end 398 | 399 | @eval function $Foo(a, b, θ = 1) 400 | T = reduce(promote_type, map(typeof, (a,b))) 401 | $Foo(convert(T, a), convert(T, b), θ) 402 | end 403 | end 404 | 405 | 406 | (p::PolyScale)(x) = p.a + (p.b - p.a) * x^p.θ 407 | (p::InvPolyScale)(y) = ((y-p.a) / (p.b-p.a) )^(1/p.θ) 408 | Base.inv(p::PolyScale) = InvPolyScale(p.a, p.b, p.θ) 409 | Base.inv(p::InvPolyScale) = PolyScale(p.a, p.b, p.θ) 410 | 411 | #------------------------------------------------------------------------------- 412 | # Exponential Scaling 413 | for Foo in [:ExpScale, :LogScale] 414 | 415 | @eval struct $Foo{T, TF} <: Scale{T} 416 | a::T 417 | b::T 418 | θ::TF 419 | et::T 420 | s::T 421 | end 422 | 423 | @eval function $Foo(a, b, θ = 1) 424 | s = (exp(θ) - 1)/(b - a) 425 | et= exp(θ) 426 | T = reduce(promote_type, map(typeof, (a,b,θ, et,s))) 427 | $Foo(convert(T, a), convert(T, b), θ, et,s) 428 | end 429 | end 430 | 431 | function (p::ExpScale)(x) 432 | t = (exp(p.θ * x) - 1)/(p.et - 1) 433 | return p.a + (p.b - p.a) * t 434 | end 435 | 436 | function (p::LogScale)(y) 437 | θ = p.θ 438 | log( (y-p.a)*p.s + 1 ) / p.θ 439 | end 440 | 441 | Base.inv(p::ExpScale) = LogScale(p.a, p.b, p.θ) 442 | Base.inv(p::LogScale) = ExpScale(p.a, p.b, p.θ) 443 | 444 | #------------------------------------------------------------------------------- 445 | # Finalize Types 446 | scales = Dict(:PolyRange => :PolyScale, :ExpRange => :ExpScale) 447 | 448 | 449 | Base.inv(::Type{ExpScale}) = LogScale 450 | Base.inv(::Type{LogScale}) = ExpScale 451 | Base.inv(::Type{PolyScale})= InvPolyScale 452 | Base.inv(::Type{InvPolyScale}) = PolyScale 453 | 454 | #------------------------------------------------------------------------------- 455 | #------------------------------------------------------------------------------- 456 | #------------------------------------------------------------------------------- 457 | # Define Scaled Ranges types 458 | #------------------------------------------------------------------------------- 459 | # Polynomial Range 460 | for Typ in [:PolyRange] @eval begin 461 | struct $Typ{T,TF} <: AbstractRange{T} 462 | a::T 463 | b::T 464 | θ::TF 465 | N::Int 466 | vals::Vector{Float64} 467 | scale::$(scales[Typ]){T,TF} 468 | invscale::inv($(scales[Typ])){T,TF} 469 | end 470 | 471 | function $Typ(a,b; θ = 1, N = 100) 472 | T = reduce(promote_type, map(typeof, (a,b,θ, 1.0))) 473 | Ta = convert(T, a) 474 | Tb = convert(T, b) 475 | 476 | vals = domscale($Typ)(Ta,Tb,θ).(LinRange(0, 1, N)) 477 | scale = $(scales[Typ])(Ta, Tb, θ) 478 | invscale= inv($(scales[Typ])(Ta, Tb, θ)) 479 | $Typ(convert(T, a), convert(T, b), θ, N, vals, scale, invscale) 480 | end 481 | 482 | domscale(p::$Typ) = p.scale 483 | domscale(::Type{$Typ}) = $(scales[Typ]) 484 | 485 | function rangescale(r::$Typ, x) 486 | r.invscale(x)*(length(r)-1) + 1 487 | end 488 | 489 | Base.getindex(p::$Typ, i::Int) = p.vals[i] 490 | Base.length(p::$Typ) = p.N 491 | Base.show(io::IO, p::$Typ) = begin 492 | Typ = $Typ 493 | print(io, "$Typ($(p.a), $(p.b), $(p.θ), $(p.N))") 494 | end 495 | 496 | function Base.searchsortedfirst(p::$Typ, x, args...) 497 | searchsortedfirst(p.vals, x, args...) 498 | end 499 | 500 | 501 | end end 502 | 503 | #------------------------------------------------------------------------------- 504 | # Exponential Range 505 | for Typ in [:ExpRange] @eval begin 506 | struct $Typ{T,TF} <: AbstractRange{T} 507 | a::T 508 | b::T 509 | θ::TF 510 | N::Int 511 | vals::Vector{Float64} 512 | scale::$(scales[Typ]){T,TF} 513 | invscale::inv($(scales[Typ])){T,TF} 514 | end 515 | 516 | function $Typ(a,b; θ = 1, N = 100) 517 | vals = domscale($Typ)(a,b,θ).(LinRange(0, 1, N)) 518 | scale = $(scales[Typ])(a, b, θ) 519 | invscale= inv($(scales[Typ])(a, b, θ)) 520 | T = reduce(promote_type, map(typeof, (a,b,θ,scale.s))) 521 | $Typ(convert(T, a), convert(T, b), θ, N, vals, scale, invscale) 522 | end 523 | 524 | domscale(p::$Typ) = p.scale 525 | domscale(::Type{$Typ}) = $(scales[Typ]) 526 | 527 | function rangescale(r::$Typ, x) 528 | r.invscale(x)*(length(r)-1) + 1 529 | end 530 | 531 | Base.getindex(p::$Typ, i::Int) = p.vals[i] 532 | Base.length(p::$Typ) = p.N 533 | Base.show(io::IO, p::$Typ) = begin 534 | Typ = $Typ 535 | print(io, "$Typ($(p.a), $(p.b), $(p.θ), $(p.N))") 536 | end 537 | 538 | function Base.searchsortedfirst(p::$Typ, x, args...) 539 | searchsortedfirst(p.vals, x, args...) 540 | end 541 | end end 542 | 543 | #------------------------------------------------------------------------------- 544 | # Finalize Ranges 545 | function rangescale(r::AbstractRange, x) 546 | ((x - first(r)))/(last(r) - first(r)) * (length(r) -1) + 1 547 | end 548 | 549 | rangescale(r::UnitRange, x::Int) = x 550 | 551 | 552 | #------------------------------------------------------------------------------- 553 | #------------------------------------------------------------------------------- 554 | #------------------------------------------------------------------------------- 555 | # Scaled Interpolation Wraper 556 | mutable struct ScaledInterpolations{T, N, IT <: Interpolations.AbstractInterpolation, 557 | RT <: NTuple{N, AbstractRange}, 558 | VT <: AbstractArray{T, N}, AT} 559 | r::RT # Interpolation grid (ranges) 560 | v::VT # Interpolation values 561 | itp::IT # Interpolation object 562 | args::AT # Arguments to pass to construct interpolant 563 | end 564 | 565 | function ScaledInterpolations(r, v, args...) 566 | itp = extrapolate(interpolate(v, args...), Interpolations.Flat()) 567 | return ScaledInterpolations(tuplefy(r), v, itp, args) 568 | end 569 | 570 | tuplefy(x::Tuple) = x 571 | tuplefy(x) = tuple(x) 572 | 573 | """ 574 | ``` 575 | fit!(s::ScaledInterpolations, [v]) 576 | ``` 577 | Re-fit the interpolation after the underlying data has updated. Works well if 578 | you use a view into the original array. If an array of values is passed, copies 579 | the values to the scaled interpolations value array and then refits the 580 | interpolation. 581 | """ 582 | function fit!(s::ScaledInterpolations) 583 | s.itp = extrapolate(interpolate(s.v, s.args...), Interpolations.Flat()) 584 | end 585 | 586 | function fit!(s::Array{T,N}) where {T <: ScaledInterpolations, N} 587 | for v in s 588 | fit!(v) 589 | end 590 | end 591 | 592 | dim(::Type{ScaledInterpolations{T, N, IT, RT, VT, AT}}) where {T, N, IT, RT, VT, AT} = N 593 | Base.eltype(::Type{ScaledInterpolations{T, N, IT, RT, VT, AT}}) where {T, N, IT, RT, VT, AT} = T 594 | dim(itp::ScaledInterpolations) = dim(typeof(itp)) 595 | Base.eltype(itp::ScaledInterpolations) = eltype(typeof(itp)) 596 | 597 | @generated function (s::ScaledInterpolations)(x::Vararg) 598 | Ns = dim(s) 599 | Nx = length(x) 600 | Ns == Nx || begin 601 | return quote 602 | Ns = $Ns 603 | Nx = $Nx 604 | throw(ArgumentError("Must have $Ns arguments. You passed $Nx")) 605 | end 606 | end 607 | 608 | ex = Expr(:call, :(s.itp)) 609 | for i = 1:Ns 610 | push!(ex.args, :(rangescale(s.r[$i], x[$i]))) 611 | end 612 | ex 613 | end 614 | 615 | #------------------------------------------------------------------------------- 616 | #------------------------------------------------------------------------------- 617 | #------------------------------------------------------------------------------- 618 | # Scaled Interpolation Shortcuts for Itp_Type 619 | const CubicSpline = BSpline(Cubic(Natural(OnGrid()))) 620 | const MonotoneSpline = FritschButlandMonotonicInterpolation() 621 | const LinearInterp = BSpline(Linear()) 622 | const LinearInterp2 = BSpline(Linear()) 623 | 624 | function ScaledInterpolations(grid, values, type::FritschButlandMonotonicInterpolation) 625 | return interpolate(collect(grid), values, type) 626 | end 627 | ################################################################################ 628 | ################ End of Scaled Grid Interpolation ############################## 629 | ################################################################################ 630 | 631 | 632 | #----------------------------------------------------------- 633 | #----------------------------------------------------------- 634 | # Miscelaneous Functions 635 | #----------------------------------------------------------- 636 | #----------------------------------------------------------- 637 | # Make_Grid: makes curved grid, uses ScaledInterpolation 638 | # Grid_Inv: gets index of closest node in grid 639 | # mnbrak: Bracket a minimum for a one-diimensional function expanding an orignal interval [a,b] 640 | 641 | #----------------------------------------------------------- 642 | #----------------------------------------------------------- 643 | # Grid 644 | function Make_Grid(n,θ_x,x_min,x_max,scale_type="Poly") 645 | # Get x_grid 646 | if θ_x≠1 647 | if scale_type=="Poly" 648 | x_grid = PolyRange(x_min,x_max;θ=θ_x,N=n) ; # Curved grid between x_min and x_max 649 | elseif scale_type=="Exp" 650 | x_grid = ExpRange(x_min,x_max;θ=θ_x,N=n) ; # Curved grid between x_min and x_max 651 | else 652 | error("scale_type must be either Poly or Exp") 653 | end 654 | else 655 | x_grid = range(x_min,x_max,length=n) 656 | end 657 | # Return 658 | return x_grid 659 | end 660 | 661 | 662 | #----------------------------------------------------------- 663 | #----------------------------------------------------------- 664 | # Grid_Inv 665 | function Grid_Inv(x,n,θ_x,x_min,x_max,scale_type="Poly") 666 | 667 | # Check corner solution 668 | if xx_max 671 | x_ind = n 672 | else 673 | # Get index of x in grid if x∈[x_min,x_max] 674 | if θ_x≠1 675 | if scale_type=="Poly" 676 | x_ind = Int(floor(((x.-x_min)/(x_max-x_min)).^(1/θ_x) *(n-1)+1)) 677 | elseif scale_type=="Exp" 678 | x_ind = Int(floor( (1/θ_x).*log.(((x-x_min)*(exp(θ_x)-1)/(x_max-x_min)).+1) *(n-1)+1)) 679 | else 680 | error("scale_type must be either Poly or Exp") 681 | end 682 | else 683 | x_ind = Int(floor(((x-x_min)/(x_max-x_min))*(n-1)+1)) 684 | end 685 | end 686 | # Return 687 | return x_ind 688 | end 689 | 690 | 691 | #----------------------------------------------------------- 692 | #----------------------------------------------------------- 693 | # Minimum Bracketing Function 694 | # This function comes from Numerical Recipes, Section 10.1 695 | # Input: numbers a,b that initiliaze bracket and objective function F 696 | # Optional: Bounds for the brackeet so that a,b,c ∈ [x_min,x_max] 697 | # Output: numbers (a,b,c) and images (fa,fb,fc) such that afa 709 | a , b = b , a 710 | fa,fb = fb, fa 711 | end 712 | # Guess for value c expanding bracket away from b -> (a,b,c) or (c,b,a) 713 | # Check bounds 714 | c = max( min( b + γ*(b-a) , x_max ) , x_min) 715 | fc = F(c) 716 | # While fb>fc we need to keep on bracketing 717 | while fb>fc 718 | # Compute u (new candidate) by parabolic extrapolation from a, b, c. 719 | # TINY is used to prevent any possible division by zero. 720 | r = (b-a)*(fb-fc) 721 | q = (b-c)*(fb-fa) 722 | u = min(max(b-((b-c)*q-(b-a)*r)/(2*sign(q-r)*max(abs(q-r),Tiny)),x_min),x_max) 723 | u_lim = min(max(b+G_limit*(c-b),x_min),x_max) 724 | # Test various cases for new candidate 725 | if ((b-u)*(u-c) > 0) # Parabolic u is between b and c 726 | fu=F(u) 727 | if (fu < fc) # Got a minimum between b and c so bounds are (b,u,c) 728 | a, fa, b, fb = b, fb, u, fu 729 | break 730 | elseif (fu > fb) # Got a minimum between a and u so bounds are (a,b,u) 731 | c, fc = u, fu 732 | break 733 | end 734 | # If you got here is because candidate u failed 735 | # Get new candidate by expanding interval with golden ratio 736 | # Check bounds 737 | u = max(min( c+γ*(c-b) , x_max ),x_min) 738 | fu = F(u) 739 | elseif ((c-u)*(u-u_lim) > 0.0) # Parabolic u is between c and its limit (ulim) 740 | fu=F(u) 741 | if (fu < fc) # Drop c and replace it with u, get new u with golden expansion 742 | b, c, fb, fc = c, u, fc, fu 743 | u = max(min( c+γ*(c-b) , x_max ),x_min) 744 | fu = F(u) 745 | end 746 | elseif ((u-u_lim)*(u_lim-c) >= 0.0) # U is above its limit, reign it in! 747 | u = u_lim 748 | fu = F(u) 749 | else # Nothing worked, use golden expansion 750 | u = max(min( c+γ*(c-b) , x_max ),x_min) 751 | fu = F(u) 752 | end 753 | # If no break then forget the oldest point and go onto next iteration 754 | # This means assigning b->a, c->b, u-> and forgetting about a 755 | a, b, c, fa, fb, fc = b, c, u, fb, fc, fu 756 | end 757 | # Return solution once out of the loop 758 | # Swap a and c so that ac 760 | a , c = c, a 761 | fa, fc = fc, fa 762 | end 763 | # Minimum bracketed in [a,c] with intermediate point b so that fb1 121 | return (c).^(1-p.γ)/(1-p.γ) 122 | else 123 | return log.(c) 124 | end 125 | end 126 | 127 | function d_utility(c,p::Par) 128 | return (c).^(-p.γ) 129 | end 130 | 131 | function d_utility_inv(x,p::Par) 132 | return x.^(-1/p.γ) 133 | end 134 | 135 | 136 | #----------------------------------------------------------- 137 | #----------------------------------------------------------- 138 | # Euler Error Function 139 | 140 | # G_ap interpolation 141 | function G_ap_ϵa(i_ϵ::Int64,a,M::Model) 142 | itp = ScaledInterpolations(M.a_grid,M.G_ap[i_ϵ,:], BSpline(Cubic(Line(OnGrid())))) 143 | return itp(a) 144 | end 145 | 146 | # Euler Equation 147 | function Euler_Error(i_ϵ::Int64,a,M::Model) 148 | # Return percentage error in Euler equation 149 | @unpack p, MP_ϵ, n_ϵ, ϵ_grid, r, w = M 150 | @unpack β, ϵ̄ = p 151 | 152 | # Iterpolate G_ap at current ϵ 153 | ap = min(M.a_grid[end],max(M.a_grid[1],G_ap_ϵa(i_ϵ,a,M))) 154 | 155 | # Current consumption 156 | c = (1+r)*a + w*ϵ_grid[i_ϵ] - ap 157 | 158 | # Compute left hand side of Euler equation 159 | LHS = d_utility(c,p) 160 | 161 | # Compute right hand side of Euler equation 162 | # Marginal utility at ϵ',a',G_ap(ϵ',a') 163 | up = zeros(n_ϵ) 164 | for i_ϵp=1:n_ϵ 165 | cp = (1+r)*ap + w*ϵ_grid[i_ϵp] - G_ap_ϵa(i_ϵp,ap,M) 166 | up[i_ϵp] = d_utility(cp,p) 167 | end 168 | RHS = β*(MP_ϵ.Π[i_ϵ,:])'*((1+r).*up) 169 | # Return percentage errror in Euler equation 170 | return (RHS/LHS-1)*100 171 | end 172 | 173 | # Euler Equation for Optimization 174 | function Euler_Eq(ap,i_ϵ::Int64,i_a::Int64,Va,M::Model) 175 | # Return percentage error in Euler equation 176 | @unpack p, MP_ϵ, n_ϵ, a_grid = M 177 | @unpack β = p 178 | # Compute left hand side of Euler equation 179 | c = (1+r)*a_grid[i_a] + w*ϵ_grid[i_ϵ] - ap 180 | LHS = d_utility(c,p) 181 | # Compute right hand side of Euler equation 182 | # Expected value of derivative β*E[Vk[z',kp]|k] 183 | Vap = zeros(n_ϵ) 184 | for i_ϵp=1:n_ϵ 185 | Va_ip = ScaledInterpolations(a_grid,Va[i_ϵp,:], FritschButlandMonotonicInterpolation()) 186 | Vap[i_ϵp] = Va_ip(kp) 187 | end 188 | RHS = β*(MP_ϵ.Π[i_ϵ,:])'*Vap 189 | # Return squared errror in Euler equation 190 | return (RHS/LHS-1)^2 191 | end 192 | 193 | #----------------------------------------------------------- 194 | #----------------------------------------------------------- 195 | # PFI Fixed Point 196 | function PFI_Fixed_Point(T::Function,M::Model,G_ap_old=nothing) 197 | # Unpack model structure 198 | @unpack p, n_ϵ, n_a, n_a_fine, θ_a, a_grid, a_grid_fine, r, w = M 199 | # PFI paramters 200 | @unpack max_iter, dist_tol = p 201 | # Initialize variables for loop 202 | if G_ap_old==nothing 203 | G_ap_old = (1+r)*M.a_mat # p.a_min*ones(n_ϵ,n_a) #max.(p.β*((1+r)*M.a_mat+w*M.ϵ_mat),p.a_min) ; 204 | end 205 | # println("V_0="); display(V_old) 206 | G_dist = 1 ; # Initialize distance 207 | println(" ") 208 | println("------------------------") 209 | println("PFI - n_ϵ=$n_ϵ, n_a=$n_a - θ_a=$θ_a - r=$r") 210 | for iter=1:max_iter 211 | # println("\n Iteration $iter") 212 | # Update value function 213 | G_ap_new, G_c = T(Model(M,G_ap=copy(G_ap_old))) 214 | # println("T(G_ap) = $G_ap_new") 215 | # println(" G_ap = $G_ap_old") 216 | # Update distance and iterations 217 | G_dist = sqrt(norm(G_ap_new-G_ap_old,2)) 218 | # Update old function 219 | G_ap_old = G_ap_new 220 | # Report progress 221 | if mod(iter,250)==0 222 | println(" PFI Loop: iter=$iter, dist=",G_dist) 223 | end 224 | # Check convergence and return results 225 | if G_dist<=dist_tol 226 | println("PFI - n_ϵ=$n_ϵ, n_a=$n_a - θ_a=$θ_a - r=$r") 227 | println("Iterations = $iter and Distance = ",G_dist) 228 | println("------------------------") 229 | println(" ") 230 | # Interpolate to fine grid 231 | G_ap_fine = zeros(n_ϵ,n_a_fine) 232 | G_c_fine = zeros(n_ϵ,n_a_fine) 233 | for i_ϵ=1:n_ϵ 234 | G_ap_ip = ScaledInterpolations(a_grid,G_ap_new[i_ϵ,:] , BSpline(Cubic(Line(OnGrid())))) 235 | G_ap_fine[i_ϵ,:].= G_ap_ip.(collect(a_grid_fine)) 236 | G_c_ip = ScaledInterpolations(a_grid,G_c[i_ϵ,:] , BSpline(Cubic(Line(OnGrid())))) 237 | G_c_fine[i_ϵ,:] .= G_c_ip.(collect(a_grid_fine)) 238 | end 239 | # Update model 240 | M = Model(M; G_ap=G_ap_new,G_c=G_c,G_ap_fine=G_ap_fine,G_c_fine=G_c_fine) 241 | # # Euler Equation Errors 242 | # Euler = [Euler_Error(i_ϵ,a_grid_fine[i_a],M) for i_ϵ=1:n_ϵ, i_a in 1:n_a_fine] 243 | # # Update model 244 | # M = Model(M; Euler=Euler) 245 | # Return results 246 | return M 247 | end 248 | end 249 | # If loop ends there was no convergence -> Error! 250 | error("Error in PFI - Solution not found") 251 | end 252 | 253 | 254 | # Recover Value Function 255 | function Value_Function(M::Model) 256 | # Unpack model structure 257 | @unpack p, n_ϵ, n_a, n_a_fine, a_grid, a_grid_fine, r, w, MP_ϵ, G_ap, G_c = M 258 | @unpack β, dist_tol = p 259 | 260 | # Compute value function with policy function iteration 261 | V = zeros(n_ϵ,n_a) 262 | V_new = zeros(n_ϵ,n_a) 263 | V_dist = 1 264 | U_mat = utility(G_c,p) 265 | while V_dist>dist_tol 266 | for i_ϵ=1:n_ϵ 267 | Pr = MP_ϵ.Π[i_ϵ,:]' 268 | for i_a=1:n_a 269 | ap = G_ap[i_ϵ,i_a] 270 | Vp = zeros(n_ϵ,1) 271 | for i_ϵp=1:n_ϵ 272 | Vp_ip = ScaledInterpolations(a_grid,V[i_ϵp,:], BSpline(Cubic(Line(OnGrid())))) 273 | Vp[i_ϵp] = Vp_ip(ap) 274 | end 275 | V_new[i_ϵ,i_a] = U_mat[i_ϵ,i_a] + β*Pr⋅Vp 276 | end 277 | end 278 | V_dist = maximum(abs.(V_new./V.-1)) 279 | V = V_new 280 | end 281 | # Interpolate to fine grid 282 | V_fine = zeros(n_ϵ,n_a_fine) 283 | for i_ϵ=1:n_ϵ 284 | V_ip = ScaledInterpolations(a_grid,V[i_ϵ,:], BSpline(Cubic(Line(OnGrid())))) 285 | V_fine[i_ϵ,:] .= V_ip.(collect(a_grid_fine)) 286 | end 287 | # Update model 288 | M = Model(M; V=V,V_fine=V_fine) 289 | return M 290 | end 291 | 292 | 293 | #----------------------------------------------------------- 294 | #----------------------------------------------------------- 295 | # VFI Fixed Point 296 | function VFI_Fixed_Point(T::Function,M::Model,V_old=nothing) 297 | # Unpack model structure 298 | @unpack p, n_ϵ, n_a, θ_a, a_grid, n_a_fine, a_grid_fine, r, w = M 299 | # VFI paramters 300 | @unpack max_iter, dist_tol = p 301 | # Initialize variables for loop 302 | if V_old==nothing 303 | V_old = utility(M.a_mat+w*M.ϵ_mat,p) ; # Start at utility with zero savings 304 | end 305 | # println("V_0="); display(V_old) 306 | 307 | V_dist = 1 ; # Initialize distance 308 | println(" ") 309 | println("------------------------") 310 | println("VFI - n_ϵ=$n_ϵ, n_a=$n_a - θ_a=$θ_a - r=$r") 311 | for iter=1:max_iter 312 | # println("\n Iteration $iter") 313 | # Update value function 314 | V_new, G_ap, G_c = T(Model(M,V=copy(V_old))) 315 | # println("T(V) = $V_new") 316 | # println(" V = $V_old") 317 | # Update distance and iterations 318 | V_dist = maximum(abs.(V_new./V_old.-1)) 319 | # V_dist = maximum(abs.(G_ap./G_ap_old.-1)) 320 | # Update old function 321 | V_old = V_new 322 | # G_ap_old = copy(G_ap) 323 | # Report progress 324 | if mod(iter,100)==0 325 | println(" VFI Loop: iter=$iter, dist=",100*V_dist,"%") 326 | end 327 | # Check convergence and return results 328 | if V_dist<=dist_tol 329 | println("VFI - n_ϵ=$n_ϵ, n_a=$n_a - θ_a=$θ_a - r=$r") 330 | println("Iterations = $iter and Distance = ",100*V_dist,"%") 331 | println("------------------------") 332 | println(" ") 333 | # Interpolate to fine grid 334 | V_fine = zeros(n_ϵ,n_a_fine) 335 | G_ap_fine = zeros(n_ϵ,n_a_fine) 336 | G_c_fine = zeros(n_ϵ,n_a_fine) 337 | for i_z=1:n_ϵ 338 | V_ip = ScaledInterpolations(a_grid,V_new[i_ϵ,:], BSpline(Cubic(Line(OnGrid())))) 339 | V_fine[i_ϵ,:] .= V_ip.(collect(a_grid_fine)) 340 | G_ap_ip = ScaledInterpolations(a_grid,G_ap[i_ϵ,:] , BSpline(Cubic(Line(OnGrid())))) 341 | G_ap_fine[i_ϵ,:].= G_ap_ip.(collect(a_grid_fine)) 342 | G_c_ip = ScaledInterpolations(a_grid,G_c[i_ϵ,:] , BSpline(Cubic(Line(OnGrid())))) 343 | G_c_fine[i_ϵ,:] .= G_c_ip.(collect(a_grid_fine)) 344 | end 345 | # Update model 346 | M = Model(M; V=V_new,G_ap=G_ap,G_c=G_c,V_fine=V_fine,G_ap_fine=G_ap_fine,G_c_fine=G_c_fine) 347 | # Return results 348 | return M 349 | end 350 | end 351 | # If loop ends there was no convergence -> Error! 352 | error("Error in VFI - Solution not found") 353 | end 354 | 355 | 356 | 357 | #----------------------------------------------------------- 358 | #----------------------------------------------------------- 359 | # Graphs and Stats 360 | function Aiyagari_Graph(M::Model,H_Type) 361 | gr() 362 | # Value Function 363 | plt = plot(title="Value Function - n_a=$(M.n_a) - θ_a=$(M.θ_a)",legend=:bottomright,foreground_color_legend = nothing,background_color_legend = nothing) 364 | for i_ϵ=1:M.n_ϵ 365 | plot!(M.a_grid,M.V[i_ϵ,:],linetype=:scatter,ms=1.5,label="V(ϵ_$i_ϵ)") 366 | plot!(M.a_grid_fine,M.V_fine[i_ϵ,:],linewidth=1,linestyle=(:dash),linecolor=RGB(0.4,0.4,0.4),label=nothing) 367 | end 368 | xlabel!("Capital") 369 | ylabel!("Value") 370 | savefig("./Figures/Aiyagari_V_"*H_Type*".pdf") 371 | # Assets Policy Function 372 | plt = plot(title="Policy Function - a' - n_a=$(M.n_a) - θ_a=$(M.θ_a)",legend=:bottomright,foreground_color_legend = nothing,background_color_legend = nothing) 373 | plot!(M.a_grid_fine,M.a_grid_fine,lw=1,linecolor=RGB(0.6,0.6,0.6),label=nothing) 374 | for i_ϵ=1:M.n_ϵ 375 | plot!(M.a_grid,M.G_ap[i_ϵ,:],linetype=:scatter,ms=1.5,label="G_ap(ϵ_$i_ϵ)") 376 | plot!(M.a_grid_fine,M.G_ap_fine[i_ϵ,:],linewidth=1,linestyle=(:dash),linecolor=RGB(0.4,0.4,0.4),label=nothing) 377 | end 378 | xlabel!("Assets") 379 | ylabel!("Assets") 380 | savefig("./Figures/Aiyagari_G_ap_"*H_Type*".pdf") 381 | # Disstribution 382 | Γ_a = sum(M.Γ,dims=1)' 383 | plt = plot(title="Distribution of Assets - n_a=$(M.n_a) - θ_a=$(M.θ_a)",legend=:bottomright,foreground_color_legend = nothing,background_color_legend = nothing) 384 | plot!(M.a_grid_fine,Γ_a,lw=2.5,label=nothing) 385 | xlabel!("Assets") 386 | ylabel!("Frequencies") 387 | savefig("./Figures/Aiyagari_Dist_"*H_Type*".pdf") 388 | # Disstribution 389 | plt = plot(title="Distribution of Assets by ϵ - n_a=$(M.n_a) - θ_a=$(M.θ_a)",legend=:bottomright,foreground_color_legend = nothing,background_color_legend = nothing) 390 | plot!(M.a_grid_fine,Γ_a,lw=2.5,linecolor=RGB(0.2,0.2,0.2),label="Γ_a") 391 | for i_ϵ=1:M.n_ϵ 392 | plot!(M.a_grid_fine,M.Γ[i_ϵ,:]/sum(M.Γ[i_ϵ,:]),linewidth=1.5,label="Γ_a(ϵ_$i_ϵ)") 393 | end 394 | xlabel!("Assets") 395 | ylabel!("Frequencies") 396 | savefig("./Figures/Aiyagari_Dist_by_eps_"*H_Type*".pdf") 397 | println("\n Graphs Completed for Aiyagari_$Type - n_a=$(M.n_a) - θ_a=$(M.θ_a)\n") 398 | end 399 | 400 | 401 | #----------------------------------------------------------- 402 | #----------------------------------------------------------- 403 | # Bellman operator - EGM - Iterate on Policy Functions 404 | function T_EGM_G(M::Model) 405 | @unpack p, n_ϵ, MP_ϵ, n_a, G_ap, r, w = M 406 | @unpack β, a_min = p 407 | # Define RHS of Euler equation for each (ϵ,a') 408 | # Rows are present ϵ and columns are tomorrow's a in fixed grid 409 | Euler_RHS = β*(1+r)*MP_ϵ.Π*d_utility( (1+r)*M.a_mat + w*M.ϵ_mat - G_ap , p ) 410 | # Check Monotonicity 411 | if any( Euler_RHS.<0 ) 412 | error("RHS must be monotone for EGM to work") 413 | end 414 | # Define consumption from Euler equation 415 | C_endo = d_utility_inv(Euler_RHS,p) 416 | # Define endogenous grid on assets 417 | A_endo = (C_endo .+ M.a_mat - w*M.ϵ_mat)/(1+r) 418 | # Interpolate functions on exogenous grid 419 | G_c = Array{Float64}(undef,n_ϵ,n_a) 420 | for i_ϵ=1:n_ϵ 421 | # Sort A_endo for interpolation 422 | sort_ind = sortperm(A_endo[i_ϵ,:]) 423 | A_aux = A_endo[i_ϵ,:][sort_ind] 424 | C_aux = C_endo[i_ϵ,:][sort_ind] 425 | # Check boundary condition 426 | # Ap(ϵ,a)=a_min for all aa_min 429 | a_vec = M.a_grid[M.a_grid.a_min 519 | a_vec = M.a_grid[a_grid.=a_max 655 | i_ap = n_a_fine - 1 656 | ω_ap = 0 657 | else 658 | i_ap = Grid_Inv(ap,n_a_fine,1,a_min,a_max) 659 | ω_ap = min.(1,max.(0, (ap-a_grid_fine[i_ap])/(a_max-a_min) )) 660 | end 661 | # Save index in transition matrix and weight 662 | H_ind[n_ϵ*(i_a-1)+i_ϵ] = n_ϵ*(i_ap-1)+i_ϵ 663 | H_weight[n_ϵ*(i_a-1)+i_ϵ] = ω_ap 664 | end 665 | end 666 | # Assemble transition matrix 667 | T_a = sparse( repeat(collect(1:n_ϵ*n_a_fine),2) , [H_ind;H_ind.+n_ϵ] , [H_weight; 1 .- H_weight] ) 668 | 669 | # Loop for updating histogram 670 | H_dist = 1 671 | for i_H=1:N_H 672 | # Update histogram only for a->a' 673 | Γ = T_a'*vec(Γ_0) 674 | # Reshape into n_ϵ by n_a and update ϵ->ϵ' 675 | Γ = MP_ϵ.Π'*reshape(Γ,n_ϵ,n_a) 676 | # Update distance 677 | H_dist = maximum(abs.(Γ-Γ_0)) 678 | # Update initial distribution 679 | Γ_0 .= Γ 680 | # Report progress 681 | if mod(i_H,250)==0 682 | println(" Histogram Loop: iter=$i_H, dist=$H_dist") 683 | end 684 | # Check convergence 685 | if H_distExcess_Demand_K(x,M),0.50*r_ss,0.99*r_ss) 815 | # Check result 816 | converged(min_result) || error("Failed to clear capital market in $(iterations(min_result)) iterations") 817 | # Upddate policy function 818 | r = min_result.minimizer 819 | println("Equilibrium found in $(iterations(min_result)) iterations: r=$r") 820 | 821 | # Compute Aggregates 822 | K = ((r+δ)/(α*z_bar))^(-1/(1-α)) 823 | w = (1-α)*z_bar*K^α 824 | Y = z_bar*K^α 825 | 826 | # Load aggregates into model 827 | M = Model(M; r=r, w=w, K=K, Y=Y) 828 | 829 | # Compute Policy Functions 830 | if Solver=="PFI" 831 | M = PFI_Fixed_Point(T_EGM_G,M) 832 | # Compute Value Function 833 | M = Value_Function(M) 834 | elseif Solver=="VFI" 835 | M = VFI_Fixed_Point(T_EGM_V,M) 836 | end 837 | 838 | # Compute Distribution 839 | M = Histogram_Method(M) 840 | 841 | # Return Model 842 | return M 843 | 844 | # No convergence, Display error 845 | error("No convervence to equilibrium after $N_eq iterations. Current distance of capital: $(100*K_dist)%") 846 | end 847 | 848 | 849 | #----------------------------------------------------------- 850 | #----------------------------------------------------------- 851 | # Execute Equilibrium and plot graphs 852 | 853 | println("===============================================\n Solving Aiyagari with EGM-Histogram(loop)") 854 | @time M_Aiyagari = Aiyagari_Equilibrium(Model(Solver="PFI")) 855 | # Graphs 856 | Aiyagari_Graph(M_Aiyagari,"Loop") 857 | # Aiyagari_Stats(M_Aiyagari,"Loop") 858 | --------------------------------------------------------------------------------