├── .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 |
--------------------------------------------------------------------------------