diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9709451 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,19 @@ +name: Build Project [using jupyter-book] +on: [push] +jobs: + preview: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + # Push website files to netlify + - name: Preview Deploy to Netlify + uses: nwtgck/actions-netlify@v2 + with: + publish-dir: website + production-branch: main + github-token: ${{ secrets.GITHUB_TOKEN }} + deploy-message: "Preview Deploy from GitHub Actions" + env: + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..121be3a --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,18 @@ +name: Assemble & Publish to GH-PAGES +on: + push: + tags: + - 'publish*' +jobs: + publish: + if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Deploy website to gh-pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: website + cname: dp.quantecon.org diff --git a/.gitignore b/.gitignore index fd253a1..61e115b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .DS_Store -*./DS_Store \ No newline at end of file +*./DS_Store +.ipynb_checkpoints +__pycache__ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..acddd64 --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2022, QuantEcon +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index 0f6fc9e..f85cbed 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,15 @@ Public repo for the textbook **Dynamic Programming Volume 1** by [Thomas J. Sargent](http://www.tomsargent.com/) and [John Stachurski](https://johnstachurski.net/). -## Website +| Folder | Description | +|--------|-------------| +| website | HTML files that build the website | +| pdf | PDF of the book | +| code | Public source of `py` and `jl` code | -The website is currently being built with `jekyll` +## PDF + +The PDF is available in `pdf/dp.pdf` + +Comments and feedback are very welcome. +The easiest way to provide feedback is to open an issue above. \ No newline at end of file diff --git a/code/jl/README.md b/code/jl/README.md new file mode 100644 index 0000000..64593c5 --- /dev/null +++ b/code/jl/README.md @@ -0,0 +1,4 @@ +The source code here is read into the private repo via a symbolic link. + +If you edit a jl file here and rerun to produce a PDF, it needs to be shifted to +code_book/figures, which is also connected to the private repo via a sym link. diff --git a/code/jl/american_option.jl b/code/jl/american_option.jl new file mode 100644 index 0000000..9e4f73a --- /dev/null +++ b/code/jl/american_option.jl @@ -0,0 +1,153 @@ +""" +Valuation for finite-horizon American call options in discrete time. + +""" + +include("s_approx.jl") +using QuantEcon, LinearAlgebra, IterTools + +"Creates an instance of the option model with log S_t = Z_t + W_t." +function create_american_option_model(; + n=100, μ=10.0, # Markov state grid size and mean value + ρ=0.98, ν=0.2, # persistence and volatility for Markov state + s=0.3, # volatility parameter for W_t + r=0.01, # interest rate + K=10.0, T=200) # strike price and expiration date + t_vals = collect(1:T+1) + mc = tauchen(n, ρ, ν) + z_vals, Q = mc.state_values .+ μ, mc.p + w_vals, φ, β = [-s, s], [0.5, 0.5], 1 / (1 + r) + e(t, i_w, i_z) = (t ≤ T) * (z_vals[i_z] + w_vals[i_w] - K) + return (; t_vals, z_vals, w_vals, Q, φ, T, β, K, e) +end + +"The continuation value operator." +function C(h, model) + (; t_vals, z_vals, w_vals, Q, φ, T, β, K, e) = model + Ch = similar(h) + z_idx, w_idx = eachindex(z_vals), eachindex(w_vals) + for (t, i_z) in product(t_vals, z_idx) + out = 0.0 + for (i_w′, i_z′) in product(w_idx, z_idx) + t′ = min(t + 1, T + 1) + out += max(e(t′, i_w′, i_z′), h[t′, i_z′]) * + Q[i_z, i_z′] * φ[i_w′] + end + Ch[t, i_z] = β * out + end + return Ch +end + +"Compute the continuation value function by successive approx." +function compute_cvf(model) + h_init = zeros(length(model.t_vals), length(model.z_vals)) + h_star = successive_approx(h -> C(h, model), h_init) + return h_star +end + + +# Plots + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=16 + + +function plot_contours(; savefig=false, + figname="./figures/american_option_1.pdf") + + model = create_american_option_model() + (; t_vals, z_vals, w_vals, Q, φ, T, β, K, e) = model + h_star = compute_cvf(model) + + fig, axes = plt.subplots(3, 1, figsize=(7, 11)) + z_idx, w_idx = eachindex(z_vals), eachindex(w_vals) + H = zeros(length(w_vals), length(z_vals)) + + for (ax_index, t) in zip(1:3, (1, 195, 199)) + + ax = axes[ax_index, 1] + + for (i_w, i_z) in product(w_idx, z_idx) + H[i_w, i_z] = e(t, i_w, i_z) - h_star[t, i_z] + end + + cs1 = ax.contourf(w_vals, z_vals, transpose(H), alpha=0.5) + ctr1 = ax.contour(w_vals, z_vals, transpose(H), levels=[0.0]) + plt.clabel(ctr1, inline=1, fontsize=13) + plt.colorbar(cs1, ax=ax) #, format="%.6f") + + ax.set_title(L"t=" * "$t", fontsize=fontsize) + ax.set_xlabel(L"w", fontsize=fontsize) + ax.set_ylabel(L"z", fontsize=fontsize) + + end + + fig.tight_layout() + if savefig + fig.savefig(figname) + end + plt.show() +end + + +function plot_strike(; savefig=false, + fontsize=12, + figname="./figures/american_option_2.pdf") + + model = create_american_option_model() + (; t_vals, z_vals, w_vals, Q, φ, T, β, K, e) = model + h_star = compute_cvf(model) + + # Built Markov chains for simulation + z_mc = MarkovChain(Q, z_vals) + P_φ = zeros(length(w_vals), length(w_vals)) + for i in eachindex(w_vals) # Build IID chain + P_φ[i, :] = φ + end + w_mc = MarkovChain(P_φ, w_vals) + + + y_min = minimum(z_vals) + minimum(w_vals) + y_max = maximum(z_vals) + maximum(w_vals) + fig, axes = plt.subplots(3, 1, figsize=(7, 12)) + + for ax in axes + + # Generate price series + z_draws = simulate_indices(z_mc, T, init=Int(length(z_vals) / 2 - 10)) + w_draws = simulate_indices(w_mc, T) + s_vals = z_vals[z_draws] + w_vals[w_draws] + + # Find the exercise date, if any. + exercise_date = T + 1 + for t in 1:T + if e(t, w_draws[t], z_draws[t]) ≥ h_star[w_draws[t], z_draws[t]] + exercise_date = t + end + end + + @assert exercise_date ≤ T "Option not exercised." + + # Plot + ax.set_ylim(y_min, y_max) + ax.set_xlim(1, T) + ax.fill_between(1:T, ones(T) * K, ones(T) * y_max, alpha=0.2) + ax.plot(1:T, s_vals, label=L"S_t") + ax.plot((exercise_date,), (s_vals[exercise_date]), "ko") + ax.vlines((exercise_date,), 0, (s_vals[exercise_date]), ls="--", colors="black") + ax.legend(loc="upper left", fontsize=fontsize) + ax.text(-10, 11, "in the money", fontsize=fontsize, rotation=90) + ax.text(-10, 7.2, "out of the money", fontsize=fontsize, rotation=90) + ax.text(exercise_date-20, 6, #s_vals[exercise_date]+0.8, + "exercise date", fontsize=fontsize) + ax.set_xticks((1, T)) + ax.set_yticks((y_min, y_max)) + end + + if savefig + fig.savefig(figname) + end + plt.show() +end diff --git a/code/jl/ar1_spec_rad.jl b/code/jl/ar1_spec_rad.jl new file mode 100644 index 0000000..a107d7a --- /dev/null +++ b/code/jl/ar1_spec_rad.jl @@ -0,0 +1,97 @@ +""" + +Compute r(L) for model + + Zₜ = μ (1 - ρ) + ρ Zₜ₋₁ + σ εₜ + β_t = b(Z_t) + +The process is discretized using the Tauchen method with n states. +""" + +using LinearAlgebra, QuantEcon + +function compute_mc_spec_rad(n, ρ, σ, μ, m, b) + mc = tauchen(n, ρ, σ, μ * (1 - ρ), m) + state_values, P = mc.state_values, mc.p + + L = zeros(n, n) + for i in 1:n + for j in 1:n + L[i, j] = b(state_values[i]) * P[i, j] + end + end + r = maximum(abs.(eigvals(L))) + return r +end + +# Hubmer et al parameter values, p. 24 of May 17 2020 version. + +n = 15 +ρ = 0.992 +σ = 0.0006 +μ = 0.944 +m = 4 +b(z) = z + +println("Spectral radius of L in Hubmer et al.:") +println(compute_mc_spec_rad(n, ρ, σ, μ, m, b)) + +# ## Hills et al 2019 EER + +# For the empirical model, +# +# $$ +# Z_{t+1} = 1 - \rho + \rho Z_t + \sigma \epsilon_{t+1}, +# \quad \beta_t = \beta Z_t +# $$ +# +# with +# +# $$ +# \beta = 0.99875, \; \rho = 0.85, \; \sigma = 0.0062 +# $$ +# +# They use 15 grid points on $[1-4.5\sigma_\delta, 1+4.5\sigma_\delta]$. + +n = 15 +ρ = 0.85 +σ = 0.0062 +μ = 1 +m = 4.5 +beta = 0.99875 +b(z) = beta * z + +println("Spectral radius of L in Hills et al.:") +println(compute_mc_spec_rad(n, ρ, σ, μ, m, b)) + +# Let's run a simulation of the discount process. +# Plots + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fs=14 + +function plot_beta_sim(; T=80, + savefig=false, + figname="./figures/ar1_spec_rad.pdf") + β_vals = zeros(T) + Z = 1 + for t in 1:T + β_vals[t] = beta * Z + Z = 1 - ρ + ρ * Z + σ * randn() + end + + fig, ax = plt.subplots(figsize=(6, 3.8)) + + ax.plot(β_vals, label=L"\beta_t") + ax.plot(1:T, ones(T), "k--", alpha=0.5, label=L"\beta=1") + ax.set_yticks((0.97, 1.0, 1.03)) + ax.set_xlabel("time") + ax.legend(frameon=false, fontsize=fs) + + if savefig + fig.savefig(figname) + end + plt.show() +end diff --git a/code/jl/bellman_envelope.jl b/code/jl/bellman_envelope.jl new file mode 100644 index 0000000..a97e196 --- /dev/null +++ b/code/jl/bellman_envelope.jl @@ -0,0 +1,57 @@ +using PyPlot, LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +fs = 18 + +xmin=-0.5 +xmax=2.0 + +xgrid = LinRange(xmin, xmax, 1000) + +a1, b1 = 0.15, 0.5 # first T_σ +a2, b2 = 0.5, 0.4 # second T_σ +a3, b3 = 0.75, 0.2 # third T_σ + +v1 = b1/(1-a1) +v2 = b2/(1-a2) +v3 = b3/(1-a3) + +T1 = a1 * xgrid .+ b1 +T2 = a2 * xgrid .+ b2 +T3 = a3 * xgrid .+ b3 +T = max.(T1, T2, T3) + +fig, ax = plt.subplots() +for spine in ["left", "bottom"] + ax.spines[spine].set_position("zero") +end +for spine in ["right", "top"] + ax.spines[spine].set_color("none") +end + +ax.plot(xgrid, T1, "k-", lw=1) +ax.plot(xgrid, T2, "k-", lw=1) +ax.plot(xgrid, T3, "k-", lw=1) + +ax.plot(xgrid, T, lw=6, alpha=0.3, color="blue", label=L"T = \bigvee_{\sigma \in \Sigma} T_\sigma") + + +ax.text(2.1, 0.6, L"T_{\sigma'}", fontsize=fs) +ax.text(2.1, 1.4, L"T_{\sigma''}", fontsize=fs) +ax.text(2.1, 1.9, L"T_{\sigma'''}", fontsize=fs) + +ax.legend(frameon=false, loc="upper center", fontsize=fs) + + +ax.set_xlim(xmin, xmax+0.5) +ax.set_ylim(-0.2, 2) +ax.text(2.4, -0.15, L"v", fontsize=22) + +ax.set_xticks([]) +ax.set_yticks([]) + +plt.show() + +file_name = "./figures/bellman_envelope.pdf" +fig.savefig(file_name) + diff --git a/code/jl/binom_stoch_dom.jl b/code/jl/binom_stoch_dom.jl new file mode 100644 index 0000000..7938ff9 --- /dev/null +++ b/code/jl/binom_stoch_dom.jl @@ -0,0 +1,30 @@ +using Distributions + +n, m, p = 10, 18, 0.5 +ϕ = Binomial(n, p) +ψ = Binomial(m, p) + + +x = 0:m + + +# == Plots == # + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +fig, ax = plt.subplots(figsize=(9, 5.2)) +lb = latexstring("\\phi = B($n, $p)") +ax.plot(x, vcat(pdf(ϕ), zeros(m-n)), "-o", alpha=0.6, label=lb) +lb = latexstring("\\psi = B($m, $p)") +ax.plot(x, pdf(ψ), "-o", alpha=0.6, label=lb) + +ax.legend(fontsize=16, frameon=false) + +if false + fig.savefig("./figures/binom_stoch_dom.pdf") +end + +plt.show() + diff --git a/code/jl/compute_spec_rad.jl b/code/jl/compute_spec_rad.jl new file mode 100644 index 0000000..905a939 --- /dev/null +++ b/code/jl/compute_spec_rad.jl @@ -0,0 +1,5 @@ +using LinearAlgebra +ρ(A) = maximum(abs(λ) for λ in eigvals(A)) # Spectral radius +A = [0.4 0.1; # Test with arbitrary A + 0.7 0.2] +print(ρ(A)) diff --git a/code/jl/concave_map_fp.jl b/code/jl/concave_map_fp.jl new file mode 100644 index 0000000..49cdd71 --- /dev/null +++ b/code/jl/concave_map_fp.jl @@ -0,0 +1,40 @@ +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +x0 = 0.25 +xmin, xmax = 0, 3 +fs = 18 + +x_grid = LinRange(xmin, xmax, 1200) + +g(x) = 1 + 0.5 * x^0.5 +xstar = 1.64 + +fig, ax = plt.subplots(figsize=(10, 5.5)) +# Plot the functions +lb = L"g" +ax.plot(x_grid, g.(x_grid), lw=2, alpha=0.6, label=lb) +ax.plot(x_grid, x_grid, "k--", lw=1, alpha=0.7, label=L"45") + +# Show and annotate the fixed point +fps = (xstar,) +ax.plot(fps, fps, "go", ms=10, alpha=0.6) +ax.annotate(L"x^*", + xy=(xstar, xstar), + xycoords="data", + xytext=(-20, 20), + textcoords="offset points", + fontsize=fs) + #arrowstyle="->") + +ax.legend(loc="upper left", frameon=false, fontsize=fs) +ax.set_xticks((0, 1, 2, 3)) +ax.set_yticks((0, 1, 2, 3)) +ax.set_ylim(0, 3) +ax.set_xlim(0, 3) + +plt.show() +#fig.savefig("./figures/concave_map_fp.pdf") + + diff --git a/code/jl/cont_time_js.jl b/code/jl/cont_time_js.jl new file mode 100644 index 0000000..0497fda --- /dev/null +++ b/code/jl/cont_time_js.jl @@ -0,0 +1,243 @@ +""" + +Continuous time job search. + +Since Julia uses 1-based indexing, job status is + + s = 1 for unemployed and s = 2 for employed + +The policy function has the form + + σ[j] = optimal choice in when s = 1 + +We use σ[j] = 1 for reject and 2 for accept (1-based indexing) + +""" + + +using QuantEcon, Distributions, LinearAlgebra, IterTools + +function create_js_model(; α=0.1, # separation rate + κ=1.0, # offer rate + δ=0.1, # discount rate + n=100, # wage grid size + ρ=0.9, # wage persistence + ν=0.2, # wage volatility + c=1.0) # unemployment compensation + mc = tauchen(n, ρ, ν) + w_vals, P = exp.(mc.state_values), mc.p + + function Π(s, j, a, s′, j′) + a -= 1 # set a to 0:1 (reject:accept) + if s == 1 && s′ == 1 + out = P[j, j′] * (1 - a) + elseif s == 1 && s′ == 2 + out = P[j, j′] * a + elseif s == 2 && s′ == 1 + out = P[j, j′] + else + out = 0.0 + end + return out + end + + Q = Array{Float64}(undef, 2, n, 2, 2, n) + indices = product(1:2, 1:n, 1:2, 1:2, 1:n) + for (s, j, a, s′, j′) in indices + λ = (s == 1) ? κ : α + Q[s, j, a, s′, j′] = λ * + (Π(s, j, a, s′, j′) - (s == s′ && j == j′)) + end + + return (; n, w_vals, P, Q, δ, κ, c, α) +end + +function B(s, j, a, v, model) + (; n, w_vals, P, Q, δ, κ, c, α) = model + r = (s == 1) ? c : w_vals[j] + indices = product(1:2, 1:n) + continuation_value = 0.0 + for (s′, j′) in indices + continuation_value += v[s′, j′] * Q[s, j, a, s′, j′] + end + return r + continuation_value +end + + +"Compute a v-greedy policy." +function get_greedy(v, model) + (; n, w_vals, P, Q, δ, κ, c, α) = model + σ = Array{Int8}(undef, n) + for j in 1:n + _, σ[j] = findmax(B(1, j, a, v, model) for a in 1:2) + end + return σ +end + + +"Approximate lifetime value of policy σ." +function get_value(σ, model) + (; n, w_vals, P, Q, δ, κ, c, α) = model + # Set up matrices + A = Array{Float64}(undef, 2, n, 2, n) # A = I δ - Q_σ + r_σ = Array{Float64}(undef, 2, n) + indices = product(1:2, 1:n) + for (s, j) in indices + r_σ[s, j] = (s == 1) ? c : w_vals[j] + end + indices = product(1:2, 1:n, 1:2, 1:n) + for (s, j, s′, j′) in indices + A[s, j, s′, j′] = δ * (s == s′ && j == j′) - Q[s, j, σ[j], s′, j′] + end + # Reshape for matrix algebra + A = reshape(A, 2 * n, 2 * n) + r_σ = reshape(r_σ, 2 * n) + # Solve for v_σ = (I δ - Q_σ)^{-1} r_σ + v_σ = A \ r_σ + # Convert to shape (2, n) and return + v_σ = reshape(v_σ, 2, n) + return v_σ +end + +"Howard policy iteration routine." +function policy_iteration(v_init, + model; + tolerance=1e-9, + max_iter=1_000) + v = v_init + error = tolerance + 1 + k = 1 + while error > tolerance && k < max_iter + last_v = v + σ = get_greedy(v, model) + v = get_value(σ, model) + error = maximum(abs.(v - last_v)) + println("Completed iteration $k with error $error.") + k += 1 + end + return v, get_greedy(v, model) +end + + +# == Figures == # + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=14 + + +function plot_policy(; savefig=false, figname="cont_time_js_pol.pdf") + model = create_js_model() + (; n, w_vals, P, Q, δ, κ, c, α) = model + + v_init = ones(2, model.n) + @time v_star, σ_star = policy_iteration(v_init, model) + σ_star = σ_star .- 1 # Convert to 0:1 choices + + fig, ax = plt.subplots(figsize=(9,5)) + ax.plot(w_vals, σ_star) + ax.set_xlabel("wage offer", fontsize=fontsize) + ax.set_yticks((0, 1)) + ax.set_ylabel("action (reject/accept)", fontsize=fontsize) + if savefig + plt.savefig(figname) + end + plt.show() +end + + +function plot_reswage(; savefig=false, figname="cont_time_js_res.pdf") + α_vals = LinRange(0.05, 1.0, 100) + res_wages_alpha = [] + for α in α_vals + model = create_js_model(α=α) + local (; n, w_vals, P, Q, δ, κ, c, α) = model + + v_init = ones(2, model.n) + local v_star, σ_star = policy_iteration(v_init, model) + σ_star = σ_star .- 1 # Convert to 0:1 choices + + w_idx = searchsortedfirst(σ_star, 1) + w_bar = w_vals[w_idx] + push!(res_wages_alpha, w_bar) + + end + + κ_vals = LinRange(0.5, 1.5, 100) + res_wages_kappa = [] + for κ in κ_vals + model = create_js_model(κ=κ) + local (; n, w_vals, P, Q, δ, κ, c, α) = model + + v_init = ones(2, model.n) + local v_star, σ_star = policy_iteration(v_init, model) + σ_star = σ_star .- 1 # Convert to 0:1 choices + + w_idx = searchsortedfirst(σ_star, 1) + w_bar = w_vals[w_idx] + push!(res_wages_kappa, w_bar) + end + + + δ_vals = LinRange(0.05, 1.0, 100) + res_wages_delta = [] + for δ in δ_vals + model = create_js_model(δ=δ) + local (; n, w_vals, P, Q, δ, κ, c, α) = model + + v_init = ones(2, model.n) + local v_star, σ_star = policy_iteration(v_init, model) + σ_star = σ_star .- 1 # Convert to 0:1 choices + + w_idx = searchsortedfirst(σ_star, 1) + w_bar = w_vals[w_idx] + push!(res_wages_delta, w_bar) + + end + + + c_vals = LinRange(0.5, 1.5, 100) + res_wages_c = [] + for c in c_vals + model = create_js_model(c=c) + local (; n, w_vals, P, Q, δ, κ, c, α) = model + + v_init = ones(2, model.n) + local v_star, σ_star = policy_iteration(v_init, model) + σ_star = σ_star .- 1 # Convert to 0:1 choices + + w_idx = searchsortedfirst(σ_star, 1) + w_bar = w_vals[w_idx] + push!(res_wages_c, w_bar) + end + + multi_fig, axes = plt.subplots(2, 2, figsize=(9, 5)) + + ax = axes[1, 1] + ax.plot(α_vals, res_wages_alpha) + ax.set_xlabel("separation rate", fontsize=fontsize) + ax.set_ylabel("res. wage", fontsize=fontsize) + + ax = axes[1, 2] + ax.plot(κ_vals, res_wages_kappa) + ax.set_xlabel("offer rate", fontsize=fontsize) + ax.set_ylabel("res. wage", fontsize=fontsize) + + ax = axes[2, 1] + ax.plot(δ_vals, res_wages_delta) + ax.set_xlabel("discount rate", fontsize=fontsize) + ax.set_ylabel("res. wage", fontsize=fontsize) + + ax = axes[2, 2] + ax.plot(c_vals, res_wages_c) + ax.set_xlabel("unempl. compensation", fontsize=fontsize) + ax.set_ylabel("res. wage", fontsize=fontsize) + + multi_fig.tight_layout() + + if savefig + plt.savefig(figname) + end + plt.show() +end diff --git a/code/jl/data/GS1.csv b/code/jl/data/GS1.csv new file mode 100644 index 0000000..3d1d51f --- /dev/null +++ b/code/jl/data/GS1.csv @@ -0,0 +1,829 @@ +DATE,GS1 +1953-04-01,2.36 +1953-05-01,2.48 +1953-06-01,2.45 +1953-07-01,2.38 +1953-08-01,2.28 +1953-09-01,2.20 +1953-10-01,1.79 +1953-11-01,1.67 +1953-12-01,1.66 +1954-01-01,1.41 +1954-02-01,1.14 +1954-03-01,1.13 +1954-04-01,0.96 +1954-05-01,0.85 +1954-06-01,0.82 +1954-07-01,0.84 +1954-08-01,0.88 +1954-09-01,1.03 +1954-10-01,1.17 +1954-11-01,1.14 +1954-12-01,1.21 +1955-01-01,1.39 +1955-02-01,1.57 +1955-03-01,1.59 +1955-04-01,1.75 +1955-05-01,1.90 +1955-06-01,1.91 +1955-07-01,2.02 +1955-08-01,2.37 +1955-09-01,2.36 +1955-10-01,2.39 +1955-11-01,2.48 +1955-12-01,2.73 +1956-01-01,2.58 +1956-02-01,2.49 +1956-03-01,2.61 +1956-04-01,2.92 +1956-05-01,2.94 +1956-06-01,2.74 +1956-07-01,2.76 +1956-08-01,3.10 +1956-09-01,3.35 +1956-10-01,3.28 +1956-11-01,3.44 +1956-12-01,3.68 +1957-01-01,3.37 +1957-02-01,3.38 +1957-03-01,3.42 +1957-04-01,3.49 +1957-05-01,3.48 +1957-06-01,3.65 +1957-07-01,3.81 +1957-08-01,4.01 +1957-09-01,4.07 +1957-10-01,4.01 +1957-11-01,3.57 +1957-12-01,3.18 +1958-01-01,2.65 +1958-02-01,1.99 +1958-03-01,1.84 +1958-04-01,1.45 +1958-05-01,1.37 +1958-06-01,1.23 +1958-07-01,1.61 +1958-08-01,2.50 +1958-09-01,3.05 +1958-10-01,3.19 +1958-11-01,3.10 +1958-12-01,3.29 +1959-01-01,3.36 +1959-02-01,3.54 +1959-03-01,3.61 +1959-04-01,3.72 +1959-05-01,3.96 +1959-06-01,4.07 +1959-07-01,4.39 +1959-08-01,4.42 +1959-09-01,5.00 +1959-10-01,4.80 +1959-11-01,4.81 +1959-12-01,5.14 +1960-01-01,5.03 +1960-02-01,4.66 +1960-03-01,4.02 +1960-04-01,4.04 +1960-05-01,4.21 +1960-06-01,3.36 +1960-07-01,3.20 +1960-08-01,2.95 +1960-09-01,3.07 +1960-10-01,3.04 +1960-11-01,3.08 +1960-12-01,2.86 +1961-01-01,2.81 +1961-02-01,2.93 +1961-03-01,2.88 +1961-04-01,2.88 +1961-05-01,2.87 +1961-06-01,3.06 +1961-07-01,2.92 +1961-08-01,3.06 +1961-09-01,3.06 +1961-10-01,3.05 +1961-11-01,3.07 +1961-12-01,3.18 +1962-01-01,3.28 +1962-02-01,3.28 +1962-03-01,3.06 +1962-04-01,2.99 +1962-05-01,3.03 +1962-06-01,3.03 +1962-07-01,3.29 +1962-08-01,3.20 +1962-09-01,3.06 +1962-10-01,2.98 +1962-11-01,3.00 +1962-12-01,3.01 +1963-01-01,3.04 +1963-02-01,3.01 +1963-03-01,3.03 +1963-04-01,3.11 +1963-05-01,3.12 +1963-06-01,3.20 +1963-07-01,3.48 +1963-08-01,3.53 +1963-09-01,3.57 +1963-10-01,3.64 +1963-11-01,3.74 +1963-12-01,3.81 +1964-01-01,3.79 +1964-02-01,3.78 +1964-03-01,3.91 +1964-04-01,3.91 +1964-05-01,3.84 +1964-06-01,3.83 +1964-07-01,3.72 +1964-08-01,3.74 +1964-09-01,3.84 +1964-10-01,3.86 +1964-11-01,3.91 +1964-12-01,4.02 +1965-01-01,3.94 +1965-02-01,4.03 +1965-03-01,4.06 +1965-04-01,4.04 +1965-05-01,4.03 +1965-06-01,3.99 +1965-07-01,3.98 +1965-08-01,4.07 +1965-09-01,4.20 +1965-10-01,4.30 +1965-11-01,4.37 +1965-12-01,4.72 +1966-01-01,4.88 +1966-02-01,4.94 +1966-03-01,4.97 +1966-04-01,4.90 +1966-05-01,4.93 +1966-06-01,4.97 +1966-07-01,5.17 +1966-08-01,5.54 +1966-09-01,5.82 +1966-10-01,5.58 +1966-11-01,5.54 +1966-12-01,5.20 +1967-01-01,4.75 +1967-02-01,4.71 +1967-03-01,4.35 +1967-04-01,4.11 +1967-05-01,4.15 +1967-06-01,4.48 +1967-07-01,5.01 +1967-08-01,5.13 +1967-09-01,5.24 +1967-10-01,5.37 +1967-11-01,5.61 +1967-12-01,5.71 +1968-01-01,5.43 +1968-02-01,5.41 +1968-03-01,5.58 +1968-04-01,5.71 +1968-05-01,6.14 +1968-06-01,5.98 +1968-07-01,5.65 +1968-08-01,5.43 +1968-09-01,5.45 +1968-10-01,5.57 +1968-11-01,5.75 +1968-12-01,6.19 +1969-01-01,6.34 +1969-02-01,6.41 +1969-03-01,6.34 +1969-04-01,6.26 +1969-05-01,6.42 +1969-06-01,7.04 +1969-07-01,7.60 +1969-08-01,7.54 +1969-09-01,7.82 +1969-10-01,7.64 +1969-11-01,7.89 +1969-12-01,8.17 +1970-01-01,8.10 +1970-02-01,7.59 +1970-03-01,6.97 +1970-04-01,7.06 +1970-05-01,7.75 +1970-06-01,7.55 +1970-07-01,7.10 +1970-08-01,6.98 +1970-09-01,6.73 +1970-10-01,6.43 +1970-11-01,5.51 +1970-12-01,5.00 +1971-01-01,4.57 +1971-02-01,3.89 +1971-03-01,3.69 +1971-04-01,4.30 +1971-05-01,5.04 +1971-06-01,5.64 +1971-07-01,6.04 +1971-08-01,5.80 +1971-09-01,5.41 +1971-10-01,4.91 +1971-11-01,4.67 +1971-12-01,4.60 +1972-01-01,4.28 +1972-02-01,4.27 +1972-03-01,4.67 +1972-04-01,4.96 +1972-05-01,4.64 +1972-06-01,4.93 +1972-07-01,4.96 +1972-08-01,4.98 +1972-09-01,5.52 +1972-10-01,5.52 +1972-11-01,5.27 +1972-12-01,5.52 +1973-01-01,5.89 +1973-02-01,6.19 +1973-03-01,6.85 +1973-04-01,6.85 +1973-05-01,6.89 +1973-06-01,7.31 +1973-07-01,8.39 +1973-08-01,8.82 +1973-09-01,8.31 +1973-10-01,7.40 +1973-11-01,7.57 +1973-12-01,7.27 +1974-01-01,7.42 +1974-02-01,6.88 +1974-03-01,7.76 +1974-04-01,8.62 +1974-05-01,8.78 +1974-06-01,8.67 +1974-07-01,8.80 +1974-08-01,9.36 +1974-09-01,8.87 +1974-10-01,8.05 +1974-11-01,7.66 +1974-12-01,7.31 +1975-01-01,6.83 +1975-02-01,5.98 +1975-03-01,6.11 +1975-04-01,6.90 +1975-05-01,6.39 +1975-06-01,6.29 +1975-07-01,7.11 +1975-08-01,7.70 +1975-09-01,7.75 +1975-10-01,6.95 +1975-11-01,6.49 +1975-12-01,6.60 +1976-01-01,5.81 +1976-02-01,5.91 +1976-03-01,6.21 +1976-04-01,5.92 +1976-05-01,6.40 +1976-06-01,6.52 +1976-07-01,6.20 +1976-08-01,6.00 +1976-09-01,5.84 +1976-10-01,5.50 +1976-11-01,5.29 +1976-12-01,4.89 +1977-01-01,5.29 +1977-02-01,5.47 +1977-03-01,5.50 +1977-04-01,5.44 +1977-05-01,5.84 +1977-06-01,5.80 +1977-07-01,5.94 +1977-08-01,6.37 +1977-09-01,6.53 +1977-10-01,6.97 +1977-11-01,6.95 +1977-12-01,6.96 +1978-01-01,7.28 +1978-02-01,7.34 +1978-03-01,7.31 +1978-04-01,7.45 +1978-05-01,7.82 +1978-06-01,8.09 +1978-07-01,8.39 +1978-08-01,8.31 +1978-09-01,8.64 +1978-10-01,9.14 +1978-11-01,10.01 +1978-12-01,10.30 +1979-01-01,10.41 +1979-02-01,10.24 +1979-03-01,10.25 +1979-04-01,10.12 +1979-05-01,10.12 +1979-06-01,9.57 +1979-07-01,9.64 +1979-08-01,9.98 +1979-09-01,10.84 +1979-10-01,12.44 +1979-11-01,12.39 +1979-12-01,11.98 +1980-01-01,12.06 +1980-02-01,13.92 +1980-03-01,15.82 +1980-04-01,13.30 +1980-05-01,9.39 +1980-06-01,8.16 +1980-07-01,8.65 +1980-08-01,10.24 +1980-09-01,11.52 +1980-10-01,12.49 +1980-11-01,14.15 +1980-12-01,14.88 +1981-01-01,14.08 +1981-02-01,14.57 +1981-03-01,13.71 +1981-04-01,14.32 +1981-05-01,16.20 +1981-06-01,14.86 +1981-07-01,15.72 +1981-08-01,16.72 +1981-09-01,16.52 +1981-10-01,15.38 +1981-11-01,12.41 +1981-12-01,12.85 +1982-01-01,14.32 +1982-02-01,14.73 +1982-03-01,13.95 +1982-04-01,13.98 +1982-05-01,13.34 +1982-06-01,14.07 +1982-07-01,13.24 +1982-08-01,11.43 +1982-09-01,10.85 +1982-10-01,9.32 +1982-11-01,9.16 +1982-12-01,8.91 +1983-01-01,8.62 +1983-02-01,8.92 +1983-03-01,9.04 +1983-04-01,8.98 +1983-05-01,8.90 +1983-06-01,9.66 +1983-07-01,10.20 +1983-08-01,10.53 +1983-09-01,10.16 +1983-10-01,9.81 +1983-11-01,9.94 +1983-12-01,10.11 +1984-01-01,9.90 +1984-02-01,10.04 +1984-03-01,10.59 +1984-04-01,10.90 +1984-05-01,11.66 +1984-06-01,12.08 +1984-07-01,12.03 +1984-08-01,11.82 +1984-09-01,11.58 +1984-10-01,10.90 +1984-11-01,9.82 +1984-12-01,9.33 +1985-01-01,9.02 +1985-02-01,9.29 +1985-03-01,9.86 +1985-04-01,9.14 +1985-05-01,8.46 +1985-06-01,7.80 +1985-07-01,7.86 +1985-08-01,8.05 +1985-09-01,8.07 +1985-10-01,8.01 +1985-11-01,7.88 +1985-12-01,7.67 +1986-01-01,7.73 +1986-02-01,7.61 +1986-03-01,7.03 +1986-04-01,6.44 +1986-05-01,6.65 +1986-06-01,6.73 +1986-07-01,6.27 +1986-08-01,5.93 +1986-09-01,5.77 +1986-10-01,5.72 +1986-11-01,5.80 +1986-12-01,5.87 +1987-01-01,5.78 +1987-02-01,5.96 +1987-03-01,6.03 +1987-04-01,6.50 +1987-05-01,7.00 +1987-06-01,6.80 +1987-07-01,6.68 +1987-08-01,7.03 +1987-09-01,7.67 +1987-10-01,7.59 +1987-11-01,6.96 +1987-12-01,7.17 +1988-01-01,6.99 +1988-02-01,6.64 +1988-03-01,6.71 +1988-04-01,7.01 +1988-05-01,7.40 +1988-06-01,7.49 +1988-07-01,7.75 +1988-08-01,8.17 +1988-09-01,8.09 +1988-10-01,8.11 +1988-11-01,8.48 +1988-12-01,8.99 +1989-01-01,9.05 +1989-02-01,9.25 +1989-03-01,9.57 +1989-04-01,9.36 +1989-05-01,8.98 +1989-06-01,8.44 +1989-07-01,7.89 +1989-08-01,8.18 +1989-09-01,8.22 +1989-10-01,7.99 +1989-11-01,7.77 +1989-12-01,7.72 +1990-01-01,7.92 +1990-02-01,8.11 +1990-03-01,8.35 +1990-04-01,8.40 +1990-05-01,8.32 +1990-06-01,8.10 +1990-07-01,7.94 +1990-08-01,7.78 +1990-09-01,7.76 +1990-10-01,7.55 +1990-11-01,7.31 +1990-12-01,7.05 +1991-01-01,6.64 +1991-02-01,6.27 +1991-03-01,6.40 +1991-04-01,6.24 +1991-05-01,6.13 +1991-06-01,6.36 +1991-07-01,6.31 +1991-08-01,5.78 +1991-09-01,5.57 +1991-10-01,5.33 +1991-11-01,4.89 +1991-12-01,4.38 +1992-01-01,4.15 +1992-02-01,4.29 +1992-03-01,4.63 +1992-04-01,4.30 +1992-05-01,4.19 +1992-06-01,4.17 +1992-07-01,3.60 +1992-08-01,3.47 +1992-09-01,3.18 +1992-10-01,3.30 +1992-11-01,3.68 +1992-12-01,3.71 +1993-01-01,3.50 +1993-02-01,3.39 +1993-03-01,3.33 +1993-04-01,3.24 +1993-05-01,3.36 +1993-06-01,3.54 +1993-07-01,3.47 +1993-08-01,3.44 +1993-09-01,3.36 +1993-10-01,3.39 +1993-11-01,3.58 +1993-12-01,3.61 +1994-01-01,3.54 +1994-02-01,3.87 +1994-03-01,4.32 +1994-04-01,4.82 +1994-05-01,5.31 +1994-06-01,5.27 +1994-07-01,5.48 +1994-08-01,5.56 +1994-09-01,5.76 +1994-10-01,6.11 +1994-11-01,6.54 +1994-12-01,7.14 +1995-01-01,7.05 +1995-02-01,6.70 +1995-03-01,6.43 +1995-04-01,6.27 +1995-05-01,6.00 +1995-06-01,5.64 +1995-07-01,5.59 +1995-08-01,5.75 +1995-09-01,5.62 +1995-10-01,5.59 +1995-11-01,5.43 +1995-12-01,5.31 +1996-01-01,5.09 +1996-02-01,4.94 +1996-03-01,5.34 +1996-04-01,5.54 +1996-05-01,5.64 +1996-06-01,5.81 +1996-07-01,5.85 +1996-08-01,5.67 +1996-09-01,5.83 +1996-10-01,5.55 +1996-11-01,5.42 +1996-12-01,5.47 +1997-01-01,5.61 +1997-02-01,5.53 +1997-03-01,5.80 +1997-04-01,5.99 +1997-05-01,5.87 +1997-06-01,5.69 +1997-07-01,5.54 +1997-08-01,5.56 +1997-09-01,5.52 +1997-10-01,5.46 +1997-11-01,5.46 +1997-12-01,5.53 +1998-01-01,5.24 +1998-02-01,5.31 +1998-03-01,5.39 +1998-04-01,5.38 +1998-05-01,5.44 +1998-06-01,5.41 +1998-07-01,5.36 +1998-08-01,5.21 +1998-09-01,4.71 +1998-10-01,4.12 +1998-11-01,4.53 +1998-12-01,4.52 +1999-01-01,4.51 +1999-02-01,4.70 +1999-03-01,4.78 +1999-04-01,4.69 +1999-05-01,4.85 +1999-06-01,5.10 +1999-07-01,5.03 +1999-08-01,5.20 +1999-09-01,5.25 +1999-10-01,5.43 +1999-11-01,5.55 +1999-12-01,5.84 +2000-01-01,6.12 +2000-02-01,6.22 +2000-03-01,6.22 +2000-04-01,6.15 +2000-05-01,6.33 +2000-06-01,6.17 +2000-07-01,6.08 +2000-08-01,6.18 +2000-09-01,6.13 +2000-10-01,6.01 +2000-11-01,6.09 +2000-12-01,5.60 +2001-01-01,4.81 +2001-02-01,4.68 +2001-03-01,4.30 +2001-04-01,3.98 +2001-05-01,3.78 +2001-06-01,3.58 +2001-07-01,3.62 +2001-08-01,3.47 +2001-09-01,2.82 +2001-10-01,2.33 +2001-11-01,2.18 +2001-12-01,2.22 +2002-01-01,2.16 +2002-02-01,2.23 +2002-03-01,2.57 +2002-04-01,2.48 +2002-05-01,2.35 +2002-06-01,2.20 +2002-07-01,1.96 +2002-08-01,1.76 +2002-09-01,1.72 +2002-10-01,1.65 +2002-11-01,1.49 +2002-12-01,1.45 +2003-01-01,1.36 +2003-02-01,1.30 +2003-03-01,1.24 +2003-04-01,1.27 +2003-05-01,1.18 +2003-06-01,1.01 +2003-07-01,1.12 +2003-08-01,1.31 +2003-09-01,1.24 +2003-10-01,1.25 +2003-11-01,1.34 +2003-12-01,1.31 +2004-01-01,1.24 +2004-02-01,1.24 +2004-03-01,1.19 +2004-04-01,1.43 +2004-05-01,1.78 +2004-06-01,2.12 +2004-07-01,2.10 +2004-08-01,2.02 +2004-09-01,2.12 +2004-10-01,2.23 +2004-11-01,2.50 +2004-12-01,2.67 +2005-01-01,2.86 +2005-02-01,3.03 +2005-03-01,3.30 +2005-04-01,3.32 +2005-05-01,3.33 +2005-06-01,3.36 +2005-07-01,3.64 +2005-08-01,3.87 +2005-09-01,3.85 +2005-10-01,4.18 +2005-11-01,4.33 +2005-12-01,4.35 +2006-01-01,4.45 +2006-02-01,4.68 +2006-03-01,4.77 +2006-04-01,4.90 +2006-05-01,5.00 +2006-06-01,5.16 +2006-07-01,5.22 +2006-08-01,5.08 +2006-09-01,4.97 +2006-10-01,5.01 +2006-11-01,5.01 +2006-12-01,4.94 +2007-01-01,5.06 +2007-02-01,5.05 +2007-03-01,4.92 +2007-04-01,4.93 +2007-05-01,4.91 +2007-06-01,4.96 +2007-07-01,4.96 +2007-08-01,4.47 +2007-09-01,4.14 +2007-10-01,4.10 +2007-11-01,3.50 +2007-12-01,3.26 +2008-01-01,2.71 +2008-02-01,2.05 +2008-03-01,1.54 +2008-04-01,1.74 +2008-05-01,2.06 +2008-06-01,2.42 +2008-07-01,2.28 +2008-08-01,2.18 +2008-09-01,1.91 +2008-10-01,1.42 +2008-11-01,1.07 +2008-12-01,0.49 +2009-01-01,0.44 +2009-02-01,0.62 +2009-03-01,0.64 +2009-04-01,0.55 +2009-05-01,0.50 +2009-06-01,0.51 +2009-07-01,0.48 +2009-08-01,0.46 +2009-09-01,0.40 +2009-10-01,0.37 +2009-11-01,0.31 +2009-12-01,0.37 +2010-01-01,0.35 +2010-02-01,0.35 +2010-03-01,0.40 +2010-04-01,0.45 +2010-05-01,0.37 +2010-06-01,0.32 +2010-07-01,0.29 +2010-08-01,0.26 +2010-09-01,0.26 +2010-10-01,0.23 +2010-11-01,0.25 +2010-12-01,0.29 +2011-01-01,0.27 +2011-02-01,0.29 +2011-03-01,0.26 +2011-04-01,0.25 +2011-05-01,0.19 +2011-06-01,0.18 +2011-07-01,0.19 +2011-08-01,0.11 +2011-09-01,0.10 +2011-10-01,0.11 +2011-11-01,0.11 +2011-12-01,0.12 +2012-01-01,0.12 +2012-02-01,0.16 +2012-03-01,0.19 +2012-04-01,0.18 +2012-05-01,0.19 +2012-06-01,0.19 +2012-07-01,0.19 +2012-08-01,0.18 +2012-09-01,0.18 +2012-10-01,0.18 +2012-11-01,0.18 +2012-12-01,0.16 +2013-01-01,0.15 +2013-02-01,0.16 +2013-03-01,0.15 +2013-04-01,0.12 +2013-05-01,0.12 +2013-06-01,0.14 +2013-07-01,0.12 +2013-08-01,0.13 +2013-09-01,0.12 +2013-10-01,0.12 +2013-11-01,0.12 +2013-12-01,0.13 +2014-01-01,0.12 +2014-02-01,0.12 +2014-03-01,0.13 +2014-04-01,0.11 +2014-05-01,0.10 +2014-06-01,0.10 +2014-07-01,0.11 +2014-08-01,0.11 +2014-09-01,0.11 +2014-10-01,0.10 +2014-11-01,0.13 +2014-12-01,0.21 +2015-01-01,0.20 +2015-02-01,0.22 +2015-03-01,0.25 +2015-04-01,0.23 +2015-05-01,0.24 +2015-06-01,0.28 +2015-07-01,0.30 +2015-08-01,0.38 +2015-09-01,0.37 +2015-10-01,0.26 +2015-11-01,0.48 +2015-12-01,0.65 +2016-01-01,0.54 +2016-02-01,0.53 +2016-03-01,0.66 +2016-04-01,0.56 +2016-05-01,0.59 +2016-06-01,0.55 +2016-07-01,0.51 +2016-08-01,0.57 +2016-09-01,0.59 +2016-10-01,0.66 +2016-11-01,0.74 +2016-12-01,0.87 +2017-01-01,0.83 +2017-02-01,0.82 +2017-03-01,1.01 +2017-04-01,1.04 +2017-05-01,1.12 +2017-06-01,1.20 +2017-07-01,1.22 +2017-08-01,1.23 +2017-09-01,1.28 +2017-10-01,1.40 +2017-11-01,1.56 +2017-12-01,1.70 +2018-01-01,1.80 +2018-02-01,1.96 +2018-03-01,2.06 +2018-04-01,2.15 +2018-05-01,2.27 +2018-06-01,2.33 +2018-07-01,2.39 +2018-08-01,2.45 +2018-09-01,2.56 +2018-10-01,2.65 +2018-11-01,2.70 +2018-12-01,2.66 +2019-01-01,2.58 +2019-02-01,2.55 +2019-03-01,2.49 +2019-04-01,2.42 +2019-05-01,2.34 +2019-06-01,2.00 +2019-07-01,1.96 +2019-08-01,1.77 +2019-09-01,1.80 +2019-10-01,1.61 +2019-11-01,1.57 +2019-12-01,1.55 +2020-01-01,1.53 +2020-02-01,1.41 +2020-03-01,0.33 +2020-04-01,0.18 +2020-05-01,0.16 +2020-06-01,0.18 +2020-07-01,0.15 +2020-08-01,0.13 +2020-09-01,0.13 +2020-10-01,0.13 +2020-11-01,0.12 +2020-12-01,0.10 +2021-01-01,0.10 +2021-02-01,0.07 +2021-03-01,0.08 +2021-04-01,0.06 +2021-05-01,0.05 +2021-06-01,0.07 +2021-07-01,0.08 +2021-08-01,0.07 +2021-09-01,0.08 +2021-10-01,0.11 +2021-11-01,0.18 +2021-12-01,0.30 +2022-01-01,0.55 +2022-02-01,1.00 +2022-03-01,1.34 diff --git a/code/jl/data/WFII10.csv b/code/jl/data/WFII10.csv new file mode 100644 index 0000000..d405522 --- /dev/null +++ b/code/jl/data/WFII10.csv @@ -0,0 +1,523 @@ +DATE,WFII10 +2012-04-13,-0.23 +2012-04-20,-0.25 +2012-04-27,-0.27 +2012-05-04,-0.29 +2012-05-11,-0.27 +2012-05-18,-0.35 +2012-05-25,-0.39 +2012-06-01,-0.48 +2012-06-08,-0.52 +2012-06-15,-0.51 +2012-06-22,-0.49 +2012-06-29,-0.46 +2012-07-06,-0.51 +2012-07-13,-0.58 +2012-07-20,-0.62 +2012-07-27,-0.66 +2012-08-03,-0.67 +2012-08-10,-0.62 +2012-08-17,-0.48 +2012-08-24,-0.53 +2012-08-31,-0.65 +2012-09-07,-0.67 +2012-09-14,-0.69 +2012-09-21,-0.73 +2012-09-28,-0.76 +2012-10-05,-0.82 +2012-10-12,-0.79 +2012-10-19,-0.70 +2012-10-26,-0.68 +2012-11-02,-0.75 +2012-11-09,-0.79 +2012-11-16,-0.82 +2012-11-23,-0.74 +2012-11-30,-0.76 +2012-12-07,-0.84 +2012-12-14,-0.79 +2012-12-21,-0.69 +2012-12-28,-0.72 +2013-01-04,-0.60 +2013-01-11,-0.62 +2013-01-18,-0.65 +2013-01-25,-0.63 +2013-02-01,-0.55 +2013-02-08,-0.57 +2013-02-15,-0.56 +2013-02-22,-0.54 +2013-03-01,-0.63 +2013-03-08,-0.59 +2013-03-15,-0.52 +2013-03-22,-0.59 +2013-03-29,-0.62 +2013-04-05,-0.67 +2013-04-12,-0.66 +2013-04-19,-0.62 +2013-04-26,-0.65 +2013-05-03,-0.62 +2013-05-10,-0.49 +2013-05-17,-0.37 +2013-05-24,-0.28 +2013-05-31,-0.09 +2013-06-07,-0.04 +2013-06-14,0.14 +2013-06-21,0.33 +2013-06-28,0.58 +2013-07-05,0.52 +2013-07-12,0.58 +2013-07-19,0.39 +2013-07-26,0.38 +2013-08-02,0.44 +2013-08-09,0.37 +2013-08-16,0.53 +2013-08-23,0.73 +2013-08-30,0.63 +2013-09-06,0.85 +2013-09-13,0.82 +2013-09-20,0.60 +2013-09-27,0.46 +2013-10-04,0.45 +2013-10-11,0.48 +2013-10-18,0.48 +2013-10-25,0.37 +2013-11-01,0.39 +2013-11-08,0.52 +2013-11-15,0.56 +2013-11-22,0.56 +2013-11-29,0.57 +2013-12-06,0.71 +2013-12-13,0.72 +2013-12-20,0.72 +2013-12-27,0.79 +2014-01-03,0.76 +2014-01-10,0.68 +2014-01-17,0.60 +2014-01-24,0.62 +2014-01-31,0.58 +2014-02-07,0.53 +2014-02-14,0.56 +2014-02-21,0.60 +2014-02-28,0.53 +2014-03-07,0.52 +2014-03-14,0.53 +2014-03-21,0.58 +2014-03-28,0.59 +2014-04-04,0.64 +2014-04-11,0.55 +2014-04-18,0.51 +2014-04-25,0.50 +2014-05-02,0.48 +2014-05-09,0.44 +2014-05-16,0.39 +2014-05-23,0.35 +2014-05-30,0.25 +2014-06-06,0.40 +2014-06-13,0.41 +2014-06-20,0.39 +2014-06-27,0.30 +2014-07-04,0.34 +2014-07-11,0.29 +2014-07-18,0.28 +2014-07-25,0.24 +2014-08-01,0.25 +2014-08-08,0.23 +2014-08-15,0.19 +2014-08-22,0.24 +2014-08-29,0.23 +2014-09-05,0.28 +2014-09-12,0.42 +2014-09-19,0.54 +2014-09-26,0.54 +2014-10-03,0.51 +2014-10-10,0.41 +2014-10-17,0.29 +2014-10-24,0.35 +2014-10-31,0.41 +2014-11-07,0.42 +2014-11-14,0.45 +2014-11-21,0.49 +2014-11-28,0.42 +2014-12-05,0.50 +2014-12-12,0.49 +2014-12-19,0.49 +2014-12-26,0.55 +2015-01-02,0.51 +2015-01-09,0.39 +2015-01-16,0.29 +2015-01-23,0.25 +2015-01-30,0.14 +2015-02-06,0.12 +2015-02-13,0.31 +2015-02-20,0.39 +2015-02-27,0.25 +2015-03-06,0.29 +2015-03-13,0.41 +2015-03-20,0.29 +2015-03-27,0.16 +2015-04-03,0.12 +2015-04-10,0.09 +2015-04-17,0.07 +2015-04-24,0.06 +2015-05-01,0.11 +2015-05-08,0.28 +2015-05-15,0.38 +2015-05-22,0.37 +2015-05-29,0.33 +2015-06-05,0.49 +2015-06-12,0.57 +2015-06-19,0.45 +2015-06-26,0.50 +2015-07-03,0.49 +2015-07-10,0.45 +2015-07-17,0.54 +2015-07-24,0.52 +2015-07-31,0.50 +2015-08-07,0.53 +2015-08-14,0.54 +2015-08-21,0.56 +2015-08-28,0.59 +2015-09-04,0.62 +2015-09-11,0.64 +2015-09-18,0.66 +2015-09-25,0.66 +2015-10-02,0.62 +2015-10-09,0.55 +2015-10-16,0.54 +2015-10-23,0.59 +2015-10-30,0.62 +2015-11-06,0.69 +2015-11-13,0.77 +2015-11-20,0.71 +2015-11-27,0.63 +2015-12-04,0.64 +2015-12-11,0.69 +2015-12-18,0.78 +2015-12-25,0.76 +2016-01-01,0.76 +2016-01-08,0.67 +2016-01-15,0.68 +2016-01-22,0.71 +2016-01-29,0.61 +2016-02-05,0.52 +2016-02-12,0.50 +2016-02-19,0.52 +2016-02-26,0.39 +2016-03-04,0.33 +2016-03-11,0.42 +2016-03-18,0.38 +2016-03-25,0.31 +2016-04-01,0.21 +2016-04-08,0.14 +2016-04-15,0.21 +2016-04-22,0.22 +2016-04-29,0.19 +2016-05-06,0.17 +2016-05-13,0.15 +2016-05-20,0.23 +2016-05-27,0.28 +2016-06-03,0.27 +2016-06-10,0.14 +2016-06-17,0.16 +2016-06-24,0.23 +2016-07-01,0.09 +2016-07-08,-0.04 +2016-07-15,0.06 +2016-07-22,0.09 +2016-07-29,0.03 +2016-08-05,0.08 +2016-08-12,0.07 +2016-08-19,0.10 +2016-08-26,0.09 +2016-09-02,0.12 +2016-09-09,0.09 +2016-09-16,0.21 +2016-09-23,0.14 +2016-09-30,0.02 +2016-10-07,0.08 +2016-10-14,0.13 +2016-10-21,0.08 +2016-10-28,0.10 +2016-11-04,0.12 +2016-11-11,0.20 +2016-11-18,0.41 +2016-11-25,0.44 +2016-12-02,0.46 +2016-12-09,0.45 +2016-12-16,0.62 +2016-12-23,0.63 +2016-12-30,0.55 +2017-01-06,0.46 +2017-01-13,0.41 +2017-01-20,0.41 +2017-01-27,0.42 +2017-02-03,0.42 +2017-02-10,0.40 +2017-02-17,0.44 +2017-02-24,0.36 +2017-03-03,0.41 +2017-03-10,0.54 +2017-03-17,0.54 +2017-03-24,0.45 +2017-03-31,0.43 +2017-04-07,0.40 +2017-04-14,0.39 +2017-04-21,0.37 +2017-04-28,0.41 +2017-05-05,0.46 +2017-05-12,0.53 +2017-05-19,0.46 +2017-05-26,0.45 +2017-06-02,0.39 +2017-06-09,0.40 +2017-06-16,0.47 +2017-06-23,0.49 +2017-06-30,0.51 +2017-07-07,0.61 +2017-07-14,0.60 +2017-07-21,0.51 +2017-07-28,0.49 +2017-08-04,0.47 +2017-08-11,0.43 +2017-08-18,0.45 +2017-08-25,0.43 +2017-09-01,0.38 +2017-09-08,0.28 +2017-09-15,0.34 +2017-09-22,0.40 +2017-09-29,0.43 +2017-10-06,0.49 +2017-10-13,0.45 +2017-10-20,0.49 +2017-10-27,0.55 +2017-11-03,0.49 +2017-11-10,0.47 +2017-11-17,0.51 +2017-11-24,0.51 +2017-12-01,0.52 +2017-12-08,0.49 +2017-12-15,0.49 +2017-12-22,0.54 +2017-12-29,0.48 +2018-01-05,0.46 +2018-01-12,0.52 +2018-01-19,0.55 +2018-01-26,0.58 +2018-02-02,0.64 +2018-02-09,0.73 +2018-02-16,0.79 +2018-02-23,0.79 +2018-03-02,0.74 +2018-03-09,0.76 +2018-03-16,0.75 +2018-03-23,0.78 +2018-03-30,0.72 +2018-04-06,0.71 +2018-04-13,0.70 +2018-04-20,0.73 +2018-04-27,0.82 +2018-05-04,0.79 +2018-05-11,0.81 +2018-05-18,0.91 +2018-05-25,0.88 +2018-06-01,0.77 +2018-06-08,0.81 +2018-06-15,0.83 +2018-06-22,0.79 +2018-06-29,0.75 +2018-07-06,0.71 +2018-07-13,0.74 +2018-07-20,0.77 +2018-07-27,0.84 +2018-08-03,0.85 +2018-08-10,0.83 +2018-08-17,0.79 +2018-08-24,0.74 +2018-08-31,0.76 +2018-09-07,0.81 +2018-09-14,0.86 +2018-09-21,0.92 +2018-09-28,0.92 +2018-10-05,0.99 +2018-10-12,1.04 +2018-10-19,1.06 +2018-10-26,1.06 +2018-11-02,1.09 +2018-11-09,1.15 +2018-11-16,1.10 +2018-11-23,1.09 +2018-11-30,1.09 +2018-12-07,0.98 +2018-12-14,1.06 +2018-12-21,1.02 +2018-12-28,1.01 +2019-01-04,0.93 +2019-01-11,0.91 +2019-01-18,0.93 +2019-01-25,0.96 +2019-02-01,0.87 +2019-02-08,0.83 +2019-02-15,0.83 +2019-02-22,0.77 +2019-03-01,0.76 +2019-03-08,0.76 +2019-03-15,0.69 +2019-03-22,0.60 +2019-03-29,0.55 +2019-04-05,0.60 +2019-04-12,0.59 +2019-04-19,0.63 +2019-04-26,0.59 +2019-05-03,0.60 +2019-05-10,0.60 +2019-05-17,0.56 +2019-05-24,0.59 +2019-05-31,0.48 +2019-06-07,0.37 +2019-06-14,0.43 +2019-06-21,0.37 +2019-06-28,0.33 +2019-07-05,0.33 +2019-07-12,0.35 +2019-07-19,0.29 +2019-07-26,0.27 +2019-08-02,0.25 +2019-08-09,0.10 +2019-08-16,0.02 +2019-08-23,0.03 +2019-08-30,-0.06 +2019-09-06,-0.01 +2019-09-13,0.14 +2019-09-20,0.17 +2019-09-27,0.12 +2019-10-04,0.09 +2019-10-11,0.11 +2019-10-18,0.18 +2019-10-25,0.16 +2019-11-01,0.19 +2019-11-08,0.19 +2019-11-15,0.21 +2019-11-22,0.15 +2019-11-29,0.14 +2019-12-06,0.13 +2019-12-13,0.14 +2019-12-20,0.15 +2019-12-27,0.16 +2020-01-03,0.10 +2020-01-10,0.09 +2020-01-17,0.07 +2020-01-24,0.04 +2020-01-31,-0.05 +2020-02-07,-0.04 +2020-02-14,-0.07 +2020-02-21,-0.11 +2020-02-28,-0.23 +2020-03-06,-0.45 +2020-03-13,-0.17 +2020-03-20,0.35 +2020-03-27,-0.16 +2020-04-03,-0.31 +2020-04-10,-0.45 +2020-04-17,-0.47 +2020-04-24,-0.41 +2020-05-01,-0.48 +2020-05-08,-0.43 +2020-05-15,-0.42 +2020-05-22,-0.46 +2020-05-29,-0.47 +2020-06-05,-0.44 +2020-06-12,-0.48 +2020-06-19,-0.55 +2020-06-26,-0.65 +2020-07-03,-0.70 +2020-07-10,-0.76 +2020-07-17,-0.79 +2020-07-24,-0.88 +2020-07-31,-0.95 +2020-08-07,-1.05 +2020-08-14,-0.98 +2020-08-21,-0.99 +2020-08-28,-1.02 +2020-09-04,-1.05 +2020-09-11,-1.00 +2020-09-18,-0.98 +2020-09-25,-0.93 +2020-10-02,-0.95 +2020-10-09,-0.92 +2020-10-16,-0.96 +2020-10-23,-0.91 +2020-10-30,-0.88 +2020-11-06,-0.84 +2020-11-13,-0.80 +2020-11-20,-0.83 +2020-11-27,-0.87 +2020-12-04,-0.92 +2020-12-11,-0.96 +2020-12-18,-0.99 +2020-12-25,-1.01 +2021-01-01,-1.04 +2021-01-08,-1.02 +2021-01-15,-0.95 +2021-01-22,-1.00 +2021-01-29,-1.04 +2021-02-05,-1.03 +2021-02-12,-1.04 +2021-02-19,-0.88 +2021-02-26,-0.74 +2021-03-05,-0.71 +2021-03-12,-0.67 +2021-03-19,-0.63 +2021-03-26,-0.67 +2021-04-02,-0.64 +2021-04-09,-0.65 +2021-04-16,-0.71 +2021-04-23,-0.75 +2021-04-30,-0.77 +2021-05-07,-0.85 +2021-05-14,-0.88 +2021-05-21,-0.83 +2021-05-28,-0.83 +2021-06-04,-0.83 +2021-06-11,-0.84 +2021-06-18,-0.80 +2021-06-25,-0.81 +2021-07-02,-0.86 +2021-07-09,-0.93 +2021-07-16,-0.98 +2021-07-23,-1.02 +2021-07-30,-1.14 +2021-08-06,-1.14 +2021-08-13,-1.06 +2021-08-20,-1.05 +2021-08-27,-1.02 +2021-09-03,-1.04 +2021-09-10,-1.04 +2021-09-17,-1.02 +2021-09-24,-0.93 +2021-10-01,-0.86 +2021-10-08,-0.89 +2021-10-15,-0.96 +2021-10-22,-0.94 +2021-10-29,-1.03 +2021-11-05,-0.98 +2021-11-12,-1.14 +2021-11-19,-1.12 +2021-11-26,-0.99 +2021-12-03,-1.04 +2021-12-10,-0.99 +2021-12-17,-0.96 +2021-12-24,-0.98 +2021-12-31,-1.02 +2022-01-07,-0.83 +2022-01-14,-0.72 +2022-01-21,-0.56 +2022-01-28,-0.61 +2022-02-04,-0.59 +2022-02-11,-0.47 +2022-02-18,-0.46 +2022-02-25,-0.55 +2022-03-04,-0.86 +2022-03-11,-0.95 +2022-03-18,-0.70 +2022-03-25,-0.56 +2022-04-01,-0.48 +2022-04-08,-0.24 diff --git a/code/jl/expo_curve.py b/code/jl/expo_curve.py new file mode 100644 index 0000000..39c0b4b --- /dev/null +++ b/code/jl/expo_curve.py @@ -0,0 +1,74 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: light +# format_version: '1.5' +# jupytext_version: 1.14.6 +# kernelspec: +# display_name: Python 3 +# language: python +# name: python3 +# --- + +import numpy as np +import matplotlib.pyplot as plt +import matplotlib + +matplotlib.rc("text", usetex=True) # allow tex rendering +fontsize=16 +from scipy.linalg import expm, eigvals + +# %matplotlib inline + +# + +A = ((-2, -0.4, 0), + (-1.4, -1, 2.2), + (0, -2, -0.6)) + +A = np.array(A) +# - + +ev = eigvals(A) + +np.imag(ev) + +np.max(np.real(ev)) + +h = 0.01 +s0 = 0.01 * np.array((1, 1, 1)) + +x, y, z = [], [], [] +s = s0 +for i in range(6000): + s = expm(h * A) @ s + a, b, c = s + x.append(a) + y.append(b) + z.append(c) + +# + +ax = plt.figure().add_subplot(projection='3d') + +ax.plot(x, y, z, label='$t \mapsto \mathrm{e}^{t A} u_0$') +ax.legend() + +ax.view_init(23, -132) + +ax.set_xticks((-0.002, 0.002, 0.006, 0.01)) +ax.set_yticks((-0.002, 0.002, 0.006, 0.01)) +ax.set_zticks((-0.002, 0.002, 0.006, 0.01)) + +ax.set_ylim((-0.002, 0.014)) + +ax.text(s0[0]-0.001, s0[1]+0.002, s0[2], "$u_0$", fontsize=14) +ax.scatter(s0[0], s0[1], s0[2], color='k') + +plt.savefig("./figures/expo_curve_1.pdf") +plt.show() +# - + + + + diff --git a/code/jl/ez_dp_code.jl b/code/jl/ez_dp_code.jl new file mode 100644 index 0000000..cd53e1e --- /dev/null +++ b/code/jl/ez_dp_code.jl @@ -0,0 +1,74 @@ +include("ez_model.jl") + +"The policy operator for the original model." +function T_σ(v::Matrix, σ, model) + w_n, e_n = size(v) + w_idx, e_idx = 1:w_n, 1:e_n + v_new = similar(v) + for (i, j) in product(w_idx, e_idx) + v_new[i, j] = B(i, j, σ[i, j], v, model) + end + return v_new +end + +"The policy operator for the subordinate model." +function T_σ(h::Vector, σ, model) + w_n = length(h) + h_new = similar(h) + for i in 1:w_n + h_new[i] = B(i, σ[i], h, model) + end + return h_new +end + +"Compute a greedy policy for the original model." +function get_greedy(v::Matrix, model) + w_n, e_n = size(v) + w_idx, e_idx = 1:w_n, 1:e_n + σ = Matrix{Int32}(undef, w_n, e_n) + for (i, j) in product(w_idx, e_idx) + _, σ[i, j] = findmax(B(i, j, k, v, model) for k in w_idx) + end + return σ +end + +"Compute a greedy policy for the subordinate model." +function get_greedy(h::Vector, model) + w_n = length(h) + σ = Array{Int32}(undef, w_n) + for i in 1:w_n + _, σ[i] = findmax(B(i, k, h, model) for k in 1:w_n) + end + return σ +end + + +"Approximate lifetime value of policy σ." +function get_value(v_init, σ, m, model) + v = v_init + for i in 1:m + v = T_σ(v, σ, model) + end + return v +end + +"Optimistic policy iteration routine." +function optimistic_policy_iteration(v_init, + model; + tolerance=1e-9, + max_iter=1_000, + m=100) + v = v_init + error = tolerance + 1 + k = 1 + while error > tolerance && k < max_iter + last_v = v + σ = get_greedy(v, model) + v = get_value(v, σ, m, model) + error = maximum(abs.(v - last_v)) + println("Completed iteration $k with error $error.") + k += 1 + end + return v, get_greedy(v, model) +end + diff --git a/code/jl/ez_f_shapes.jl b/code/jl/ez_f_shapes.jl new file mode 100644 index 0000000..8a63a3b --- /dev/null +++ b/code/jl/ez_f_shapes.jl @@ -0,0 +1,28 @@ + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +function F(w; r=1, β=0.5, θ=5) + return (r + β * w^(1/θ))^θ +end + +w_grid = LinRange(0.1, 2.0, 200) + +fig, axes = plt.subplots(2, 2) + +θ_vals = -2, -0.5, 0.5, 2 + +for (θ, ax) in zip(θ_vals, axes) + + f(w) = F(w; θ=θ) + ax.plot(w_grid, w_grid, "k--", alpha=0.6, label=L"45") + ax.plot(w_grid, f.(w_grid), label=L"U") + ax.legend() + title = latexstring("\$\\theta = $θ\$") + ax.set_title(title) +end + +plt.show() + + diff --git a/code/jl/ez_model.jl b/code/jl/ez_model.jl new file mode 100644 index 0000000..62d3644 --- /dev/null +++ b/code/jl/ez_model.jl @@ -0,0 +1,97 @@ +""" + +The base model has Bellman equation + + (Tv)(w, e) = max_{0 <= s <= w} B(w, e, s, v) + +where + + B(w, e, s, v) = { r(w, e, s)^α + β [Σ_e' v^γ(s, e') φ(e')]^{α/γ} }^{1/γ} + + +with α = 1 - 1/ψ and r(w, e, s) = (w - s + e). + +We take φ to be the Binomial distribution on e_grid = (e_1, ..., e_n}) with e_1 > 0. + +In particular, φ(k) = Prob{E = e_k} and φ is Bin(n-1, p) + +Let α = 1 - 1 / ψ and γ = 1 - σ + + +Basu and Bundick use ψ = 0.8, β = 0.994 and σ = 30. + +SSY use ψ = 1.97, β = 0.999 and σ = -7.89. + + +We also study the subordinate model + + (B_σ h)(w) = { Σ_e (r(w, e, σ(w))^α + β * h(σ(w))^α)^(γ/α) φ(e) }^(1/γ) + +The optimal policy is found by solving the G_max step, with h as the fixed point +of B_σ and + + σ(w, e) = argmax_s { r(w, e, s)^α + β * h(s)^α }^(1/α) + + +""" + +using QuantEcon, Distributions, LinearAlgebra, IterTools + +function create_ez_model(; ψ=1.97, # elasticity of intertemp. substitution + β=0.96, # discount factor + γ=-7.89, # risk aversion parameter + n=80, # size of range(e) + p=0.5, + e_max=0.5, + w_size=50, w_max=2) + α = 1 - 1/ψ + θ = γ / α + b = Binomial(n - 1, p) + φ = [pdf(b, k) for k in 0:(n-1)] + e_grid = LinRange(1e-5, e_max, n) + w_grid = LinRange(0, w_max, w_size) + return (; α, β, γ, θ, φ, e_grid, w_grid) +end + +"Action-value aggregator for the original model." +function B(i, j, k, v, model) + (; α, β, γ, θ, φ, e_grid, w_grid) = model + w, e, s = w_grid[i], e_grid[j], w_grid[k] + value = -Inf + if s <= w + Rv = @views dot(v[k, :].^γ, φ)^(1/γ) + value = ((w - s + e)^α + β * Rv^α)^(1/α) + end + return value +end + +"Action-value aggregator for the subordinate model." +function B(i, k, h, model) + (; α, β, γ, θ, φ, e_grid, w_grid) = model + w, s = w_grid[i], w_grid[k] + G(e) = ((w - s + e)^α + β * h[k]^α)^(1/α) + value = s <= w ? dot(G.(e_grid).^γ, φ)^(1/γ) : -Inf + return value +end + +"G maximization step to find the optimal policy of the original ADP." +function G_max(h, model) + + w_n, e_n = length(model.w_grid), length(model.e_grid) + function G_obj(i, j, k) + w, e, s = w_grid[i], e_grid[j], w_grid[k] + value = -Inf + if s <= w + value = ((w - s + e)^α + β * h[k]^α)^(1/α) + end + return value + end + σ_star_mod = Array{Int32}(undef, w_n, e_n) + for i in 1:w_n + for j in 1:e_n + _, σ_star_mod[i, j] = findmax(G_obj(i, j, k) for k in 1:w_n) + end + end + return σ_star_mod +end + diff --git a/code/jl/ez_noncontraction.jl b/code/jl/ez_noncontraction.jl new file mode 100644 index 0000000..92c9d10 --- /dev/null +++ b/code/jl/ez_noncontraction.jl @@ -0,0 +1,33 @@ + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +function F(w; r=0.5, β=0.5, θ=5) + return (r + β * w^(1/θ))^θ +end + +w_grid = LinRange(0.001, 2.0, 200) + + +function plot_F(; savefig=false, + figname="./figures/ez_noncontraction.pdf", + fs=16) + + fig, ax = plt.subplots(figsize=(9, 5.2)) + f(w) = F(w; θ=-10) + ax.plot(w_grid, w_grid, "k--", alpha=0.6, label=L"45") + ax.plot(w_grid, f.(w_grid), label=L"\hat K = F") + ax.set_xticks((0, 1, 2)) + ax.set_yticks((0, 1, 2)) + ax.legend(fontsize=fs, frameon=false) + + plt.show() + + if savefig + fig.savefig(figname) + end +end + + + diff --git a/code/jl/ez_plot_functions.jl b/code/jl/ez_plot_functions.jl new file mode 100644 index 0000000..23a26c4 --- /dev/null +++ b/code/jl/ez_plot_functions.jl @@ -0,0 +1,37 @@ + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=16 + + +function plot_policy(σ, model; title, savefig=false, figname="policies.pdf") + w_grid = model.w_grid + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(w_grid, w_grid, "k--", label=L"45") + ax.plot(w_grid, w_grid[σ[:, 1]], label=L"\sigma^*(\cdot, e_1)") + ax.plot(w_grid, w_grid[σ[:, end]], label=L"\sigma^*(\cdot, e_N)") + #ax.set_title(title, fontsize=16) + ax.legend(fontsize=fontsize) + if savefig + plt.savefig(figname) + end + plt.show() +end + +function plot_value_orig(v, model) + w_grid = model.w_grid + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(w_grid, v[:, 1], label=L"v^*(\cdot, e_1)") + ax.plot(w_grid, v[:, end], label=L"v^*(\cdot, e_N)") + ax.legend(fontsize=fontsize) + plt.show() +end + +function plot_value_mod(h, model) + w_grid = model.w_grid + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(w_grid, h, label=L"h^*") + ax.legend(fontsize=fontsize) + plt.show() +end diff --git a/code/jl/ez_policy_plot.jl b/code/jl/ez_policy_plot.jl new file mode 100644 index 0000000..cfc26f9 --- /dev/null +++ b/code/jl/ez_policy_plot.jl @@ -0,0 +1,21 @@ + +""" +Optimal policy plot + +""" + +include("ez_model.jl") +include("ez_dp_code.jl") +include("ez_plot_functions.jl") + + +model = create_ez_model() +(; α, β, γ, θ, φ, e_grid, w_grid) = model + + +println("Solving modified model.") +h_init = ones(length(model.w_grid)) +@time h_star, _ = optimistic_policy_iteration(h_init, model) + +σ_star_mod = G_max(h_star, model) +plot_policy(σ_star_mod, model, title="optimal savings", savefig=true) diff --git a/code/jl/ez_sub_test.jl b/code/jl/ez_sub_test.jl new file mode 100644 index 0000000..46a1060 --- /dev/null +++ b/code/jl/ez_sub_test.jl @@ -0,0 +1,27 @@ +""" +Quick test and plots + +""" + +include("ez_model.jl") +include("ez_dp_code.jl") +include("ez_plot_functions.jl") + + +model = create_ez_model() +(; α, β, γ, θ, φ, e_grid, w_grid) = model + + +println("Solving unmodified model.") +v_init = ones(length(model.w_grid), length(model.e_grid)) +@time v_star, σ_star = optimistic_policy_iteration(v_init, model) + +println("Solving modified model.") + +h_init = ones(length(model.w_grid)) +@time h_star, _ = optimistic_policy_iteration(h_init, model) + +σ_star_mod = G_max(h_star, model) + +plot_policy(σ_star, model, title="original") +plot_policy(σ_star_mod, model, title="transformed") diff --git a/code/jl/ez_timings.jl b/code/jl/ez_timings.jl new file mode 100644 index 0000000..ec4f18c --- /dev/null +++ b/code/jl/ez_timings.jl @@ -0,0 +1,53 @@ +""" + +Timing figure + +""" + +include("ez_model.jl") +include("ez_dp_code.jl") +include("ez_plot_functions.jl") + + +n_vals = [i * 10 for i in 2:10] +β_vals = [0.96, 0.98] +gains = zeros(length(β_vals), length(n_vals)) + +for (β_i, β) in enumerate(β_vals) + for (n_i, n) in enumerate(n_vals) + + model = create_ez_model(n=n, β=β) + (; α, β, γ, θ, φ, e_grid, w_grid) = model + + println("Solving unmodified model at n = $n.") + v_init = ones(length(model.w_grid), length(model.e_grid)) + unmod_time = @elapsed v_star, σ_star = + optimistic_policy_iteration(v_init, model) + + println("Solving modified model at n = $n.") + h_init = ones(length(model.w_grid)) + mod_time = @elapsed h_star, _ = optimistic_policy_iteration(h_init, model) + + gains[β_i, n_i] = unmod_time / mod_time + end +end + + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=16 + +fig, ax = plt.subplots(figsize=(9,5)) +b = β_vals[1] +lb = "speed gain with " * L"\beta" * " = $b" +ax.plot(n_vals, gains[1, :], "-o", label=lb) +b = β_vals[2] +lb = "speed gain with " * L"\beta" * " = $b" +ax.plot(n_vals, gains[2, :], "-o", label=lb) +ax.legend(loc="lower right", fontsize=fontsize) +ax.set_xticks(n_vals) +ax.set_xlabel("size of " * L"\mathsf E", fontsize=fontsize) +plt.savefig("rel_timing.pdf") +plt.show() + diff --git a/code/jl/ez_utility.jl b/code/jl/ez_utility.jl new file mode 100644 index 0000000..95dfdd9 --- /dev/null +++ b/code/jl/ez_utility.jl @@ -0,0 +1,145 @@ +""" +Epstein--Zin utility: solving the recursion for a given consumption +path. + +""" + +include("s_approx.jl") +using LinearAlgebra, QuantEcon + +function create_ez_utility_model(; + n=200, # size of state space + ρ=0.96, # correlation coef in AR(1) + σ=0.1, # volatility + β=0.99, # time discount factor + α=0.75, # EIS parameter + γ=-2.0) # risk aversion parameter + + mc = tauchen(n, ρ, σ, 0, 5) + x_vals, P = mc.state_values, mc.p + c = exp.(x_vals) + + return (; β, ρ, σ, α, γ, c, x_vals, P) +end + +function K(v, model) + (; β, ρ, σ, α, γ, c, x_vals, P) = model + + R = (P * (v.^γ)).^(1/γ) + return ((1 - β) * c.^α + β * R.^α).^(1/α) +end + +function compute_ez_utility(model) + v_init = ones(length(model.x_vals)) + v_star = successive_approx(v -> K(v, model), + v_init, + tolerance=1e-10) + return v_star +end + + +# Plots + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=16 + +function plot_convergence(; savefig=false, + num_iter=100, + figname="./figures/ez_utility_c.pdf") + + fig, ax = plt.subplots(figsize=(10, 5.2)) + model = create_ez_utility_model() + (; β, ρ, σ, α, γ, c, x_vals, P) = model + + + v_star = compute_ez_utility(model) + v = 0.1 * v_star + ax.plot(x_vals, v, lw=3, "k-", alpha=0.7, label=L"v_0") + + greys = [string(g) for g in LinRange(0.0, 0.4, num_iter)] + greys = reverse(greys) + + for (i, g) in enumerate(greys) + ax.plot(x_vals, v, "k-", color=g, lw=1, alpha=0.7) + for t in 1:20 + v = K(v, model) + end + end + + v_star = compute_ez_utility(model) + ax.plot(x_vals, v_star, lw=3, alpha=0.7, label=L"v^*") + ax.set_xlabel(L"x", fontsize=fontsize) + + ax.legend(frameon=false, fontsize=fontsize, loc="upper left") + plt.show() + if savefig + fig.savefig(figname) + end +end + + +function plot_v(; savefig=false, + figname="./figures/ez_utility_1.pdf") + + fig, ax = plt.subplots(figsize=(10, 5.2)) + model = create_ez_utility_model() + (; β, ρ, σ, α, γ, c, x_vals, P) = model + v_star = compute_ez_utility(model) + ax.plot(x_vals, v_star, lw=2, alpha=0.7, label=L"v^*") + ax.set_xlabel(L"x", fontsize=fontsize) + + ax.legend(frameon=false, fontsize=fontsize, loc="upper left") + plt.show() + if savefig + fig.savefig(figname) + end +end + + +function vary_gamma(; gamma_vals=[1.0, -8.0], + savefig=false, + figname="./figures/ez_utility_2.pdf") + + fig, ax = plt.subplots(figsize=(10, 5.2)) + + for γ in gamma_vals + model = create_ez_utility_model(γ=γ) + (; β, ρ, σ, α, γ, c, x_vals, P) = model + v_star = compute_ez_utility(model) + ax.plot(x_vals, v_star, lw=2, alpha=0.7, label=L"\gamma="*"$γ") + ax.set_xlabel(L"x", fontsize=fontsize) + ax.set_ylabel(L"v(x)", fontsize=fontsize) + end + + ax.legend(frameon=false, fontsize=fontsize, loc="upper left") + plt.show() + if savefig + fig.savefig(figname) + end +end + + +function vary_alpha(; alpha_vals=[0.5, 0.6], + savefig=false, + figname="./figures/ez_utility_3.pdf") + + fig, ax = plt.subplots(figsize=(10, 5.2)) + + for α in alpha_vals + model = create_ez_utility_model(α=α) + (; β, ρ, σ, α, γ, c, x_vals, P) = model + v_star = compute_ez_utility(model) + ax.plot(x_vals, v_star, lw=2, alpha=0.7, label=L"\alpha="*"$α") + ax.set_xlabel(L"x", fontsize=fontsize) + ax.set_ylabel(L"v(x)", fontsize=fontsize) + end + + ax.legend(frameon=false, fontsize=fontsize, loc="upper left") + plt.show() + if savefig + fig.savefig(figname) + end +end + diff --git a/code/jl/finite_lq.jl b/code/jl/finite_lq.jl new file mode 100644 index 0000000..0166866 --- /dev/null +++ b/code/jl/finite_lq.jl @@ -0,0 +1,228 @@ +using QuantEcon, LinearAlgebra, IterTools +include("s_approx.jl") + +function create_investment_model(; + r=0.04, # Interest rate + a_0=10.0, a_1=1.0, # Demand parameters + γ=25.0, c=1.0, # Adjustment and unit cost + y_min=0.0, y_max=20.0, y_size=100, # Grid for output + ρ=0.9, ν=1.0, # AR(1) parameters + z_size=25) # Grid size for shock + β = 1/(1+r) + y_grid = LinRange(y_min, y_max, y_size) + mc = tauchen(y_size, ρ, ν) + z_grid, Q = mc.state_values, mc.p + return (; β, a_0, a_1, γ, c, y_grid, z_grid, Q) +end + +""" +The aggregator B is given by + + B(y, z, y′) = r(y, z, y′) + β Σ_z′ v(y′, z′) Q(z, z′)." + +where + + r(y, z, y′) := (a_0 - a_1 * y + z - c) y - γ * (y′ - y)^2 + +""" +function B(i, j, k, v, model) + (; β, a_0, a_1, γ, c, y_grid, z_grid, Q) = model + y, z, y′ = y_grid[i], z_grid[j], y_grid[k] + r = (a_0 - a_1 * y + z - c) * y - γ * (y′ - y)^2 + return @views r + β * dot(v[k, :], Q[j, :]) +end + +"The policy operator." +function T_σ(v, σ, model) + y_idx, z_idx = (eachindex(g) for g in (model.y_grid, model.z_grid)) + v_new = similar(v) + for (i, j) in product(y_idx, z_idx) + v_new[i, j] = B(i, j, σ[i, j], v, model) + end + return v_new +end + +"The Bellman operator." +function T(v, model) + y_idx, z_idx = (eachindex(g) for g in (model.y_grid, model.z_grid)) + v_new = similar(v) + for (i, j) in product(y_idx, z_idx) + v_new[i, j] = maximum(B(i, j, k, v, model) for k in y_idx) + end + return v_new +end + +"Compute a v-greedy policy." +function get_greedy(v, model) + y_idx, z_idx = (eachindex(g) for g in (model.y_grid, model.z_grid)) + σ = Matrix{Int32}(undef, length(y_idx), length(z_idx)) + for (i, j) in product(y_idx, z_idx) + _, σ[i, j] = findmax(B(i, j, k, v, model) for k in y_idx) + end + return σ +end + +"Value function iteration routine." +function value_iteration(model; tol=1e-5) + vz = zeros(length(model.y_grid), length(model.z_grid)) + v_star = successive_approx(v -> T(v, model), vz, tolerance=tol) + return get_greedy(v_star, model) +end + + + +"Get the value v_σ of policy σ." +function get_value(σ, model) + # Unpack and set up + (; β, a_0, a_1, γ, c, y_grid, z_grid, Q) = model + yn, zn = length(y_grid), length(z_grid) + n = yn * zn + # Function to extract (i, j) from m = i + (j-1)*yn" + single_to_multi(m) = (m-1)%yn + 1, div(m-1, yn) + 1 + # Allocate and create single index versions of P_σ and r_σ + P_σ = zeros(n, n) + r_σ = zeros(n) + for m in 1:n + i, j = single_to_multi(m) + y, z, y′ = y_grid[i], z_grid[j], y_grid[σ[i, j]] + r_σ[m] = (a_0 - a_1 * y + z - c) * y - γ * (y′ - y)^2 + for m′ in 1:n + i′, j′ = single_to_multi(m′) + if i′ == σ[i, j] + P_σ[m, m′] = Q[j, j′] + end + end + end + # Solve for the value of σ + v_σ = (I - β * P_σ) \ r_σ + # Return as multi-index array + return reshape(v_σ, yn, zn) +end + + +"Howard policy iteration routine." +function policy_iteration(model) + yn, zn = length(model.y_grid), length(model.z_grid) + σ = ones(Int32, yn, zn) + i, error = 0, 1.0 + while error > 0 + v_σ = get_value(σ, model) + σ_new = get_greedy(v_σ, model) + error = maximum(abs.(σ_new - σ)) + σ = σ_new + i = i + 1 + println("Concluded loop $i with error $error.") + end + return σ +end + +"Optimistic policy iteration routine." +function optimistic_policy_iteration(model; tol=1e-5, m=100) + v = zeros(length(model.y_grid), length(model.z_grid)) + error = tol + 1 + while error > tol + last_v = v + σ = get_greedy(v, model) + for i in 1:m + v = T_σ(v, σ, model) + end + error = maximum(abs.(v - last_v)) + end + return get_greedy(v, model) +end + + +# Plots + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=12 + +function plot_policy() + model = create_investment_model() + (; β, a_0, a_1, γ, c, y_grid, z_grid, Q) = model + σ_star = optimistic_policy_iteration(model) + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(y_grid, y_grid, "k--", label=L"45") + ax.plot(y_grid, y_grid[σ_star[:, 1]], label=L"\sigma^*(\cdot, z_1)") + ax.plot(y_grid, y_grid[σ_star[:, end]], label=L"\sigma^*(\cdot, z_N)") + ax.legend(fontsize=fontsize) + plt.show() +end + +function plot_sim(; savefig=false, figname="./figures/finite_lq_1.pdf") + ts_length = 200 + + fig, axes = plt.subplots(4, 1, figsize=(9, 11.2)) + + for (ax, γ) in zip(axes, (1, 10, 20, 30)) + model = create_investment_model(γ=γ) + (; β, a_0, a_1, γ, c, y_grid, z_grid, Q) = model + σ_star = optimistic_policy_iteration(model) + mc = MarkovChain(Q, z_grid) + + z_sim_idx = simulate_indices(mc, ts_length) + z_sim = z_grid[z_sim_idx] + y_sim_idx = Vector{Int32}(undef, ts_length) + y_1 = (a_0 - c + z_sim[1]) / (2 * a_1) + y_sim_idx[1] = searchsortedfirst(y_grid, y_1) + for t in 1:(ts_length-1) + y_sim_idx[t+1] = σ_star[y_sim_idx[t], z_sim_idx[t]] + end + y_sim = y_grid[y_sim_idx] + y_bar_sim = (a_0 .- c .+ z_sim) ./ (2 * a_1) + + ax.plot(1:ts_length, y_sim, label=L"Y_t") + ax.plot(1:ts_length, y_bar_sim, label=L"\bar Y_t") + ax.legend(fontsize=fontsize, frameon=false, loc="upper right") + ax.set_ylabel("output", fontsize=fontsize) + ax.set_ylim(1, 9) + ax.set_title(L"\gamma = " * "$γ", fontsize=fontsize) + end + + fig.tight_layout() + plt.show() + if savefig + fig.savefig(figname) + end +end + + +function plot_timing(; m_vals=collect(range(1, 600, step=10)), + savefig=false, + figname="./figures/finite_lq_time.pdf" + ) + model = create_investment_model() + #println("Running Howard policy iteration.") + #pi_time = @elapsed σ_pi = policy_iteration(model) + #println("PI completed in $pi_time seconds.") + println("Running value function iteration.") + vfi_time = @elapsed σ_vfi = value_iteration(model, tol=1e-5) + println("VFI completed in $vfi_time seconds.") + #@assert σ_vfi == σ_pi "Warning: policies deviated." + opi_times = [] + for m in m_vals + println("Running optimistic policy iteration with m=$m.") + opi_time = @elapsed σ_opi = + optimistic_policy_iteration(model, m=m, tol=1e-5) + println("OPI with m=$m completed in $opi_time seconds.") + #@assert σ_opi == σ_pi "Warning: policies deviated." + push!(opi_times, opi_time) + end + fig, ax = plt.subplots(figsize=(9, 5.2)) + #ax.plot(m_vals, fill(pi_time, length(m_vals)), + # lw=2, label="Howard policy iteration") + ax.plot(m_vals, fill(vfi_time, length(m_vals)), + lw=2, label="value function iteration") + ax.plot(m_vals, opi_times, lw=2, label="optimistic policy iteration") + ax.legend(fontsize=fontsize, frameon=false) + ax.set_xlabel(L"m", fontsize=fontsize) + ax.set_ylabel("time", fontsize=fontsize) + plt.show() + if savefig + fig.savefig(figname) + end + return (vfi_time, opi_times) + #return (pi_time, vfi_time, opi_times) +end diff --git a/code/jl/finite_opt_saving_0.jl b/code/jl/finite_opt_saving_0.jl new file mode 100644 index 0000000..79ce653 --- /dev/null +++ b/code/jl/finite_opt_saving_0.jl @@ -0,0 +1,43 @@ +using QuantEcon, LinearAlgebra, IterTools + +function create_savings_model(; R=1.01, β=0.98, γ=2.5, + w_min=0.01, w_max=20.0, w_size=200, + ρ=0.9, ν=0.1, y_size=5) + w_grid = LinRange(w_min, w_max, w_size) + mc = tauchen(y_size, ρ, ν) + y_grid, Q = exp.(mc.state_values), mc.p + return (; β, R, γ, w_grid, y_grid, Q) +end + +"B(w, y, w′, v) = u(R*w + y - w′) + β Σ_y′ v(w′, y′) Q(y, y′)." +function B(i, j, k, v, model) + (; β, R, γ, w_grid, y_grid, Q) = model + w, y, w′ = w_grid[i], y_grid[j], w_grid[k] + u(c) = c^(1-γ) / (1-γ) + c = w + y - (w′ / R) + @views value = c > 0 ? u(c) + β * dot(v[k, :], Q[j, :]) : -Inf + return value +end + +"The Bellman operator." +function T(v, model) + w_idx, y_idx = (eachindex(g) for g in (model.w_grid, model.y_grid)) + v_new = similar(v) + for (i, j) in product(w_idx, y_idx) + v_new[i, j] = maximum(B(i, j, k, v, model) for k in w_idx) + end + return v_new +end + +"The policy operator." +function T_σ(v, σ, model) + w_idx, y_idx = (eachindex(g) for g in (model.w_grid, model.y_grid)) + v_new = similar(v) + for (i, j) in product(w_idx, y_idx) + v_new[i, j] = B(i, j, σ[i, j], v, model) + end + return v_new +end + + + diff --git a/code/jl/finite_opt_saving_1.jl b/code/jl/finite_opt_saving_1.jl new file mode 100644 index 0000000..b0f467a --- /dev/null +++ b/code/jl/finite_opt_saving_1.jl @@ -0,0 +1,43 @@ +include("finite_opt_saving_0.jl") + +"Compute a v-greedy policy." +function get_greedy(v, model) + w_idx, y_idx = (eachindex(g) for g in (model.w_grid, model.y_grid)) + σ = Matrix{Int32}(undef, length(w_idx), length(y_idx)) + for (i, j) in product(w_idx, y_idx) + _, σ[i, j] = findmax(B(i, j, k, v, model) for k in w_idx) + end + return σ +end + +"Get the value v_σ of policy σ." +function get_value(σ, model) + # Unpack and set up + (; β, R, γ, w_grid, y_grid, Q) = model + w_idx, y_idx = (eachindex(g) for g in (w_grid, y_grid)) + wn, yn = length(w_idx), length(y_idx) + n = wn * yn + u(c) = c^(1-γ) / (1-γ) + # Build P_σ and r_σ as multi-index arrays + P_σ = zeros(wn, yn, wn, yn) + r_σ = zeros(wn, yn) + for (i, j) in product(w_idx, y_idx) + w, y, w′ = w_grid[i], y_grid[j], w_grid[σ[i, j]] + r_σ[i, j] = u(w + y - w′/R) + for (i′, j′) in product(w_idx, y_idx) + if i′ == σ[i, j] + P_σ[i, j, i′, j′] = Q[j, j′] + end + end + end + # Reshape for matrix algebra + P_σ = reshape(P_σ, n, n) + r_σ = reshape(r_σ, n) + # Apply matrix operations --- solve for the value of σ + v_σ = (I - β * P_σ) \ r_σ + # Return as multi-index array + return reshape(v_σ, wn, yn) +end + + + diff --git a/code/jl/finite_opt_saving_2.jl b/code/jl/finite_opt_saving_2.jl new file mode 100644 index 0000000..c6b0256 --- /dev/null +++ b/code/jl/finite_opt_saving_2.jl @@ -0,0 +1,186 @@ +include("s_approx.jl") +include("finite_opt_saving_1.jl") + +"Value function iteration routine." +function value_iteration(model, tol=1e-5) + vz = zeros(length(model.w_grid), length(model.y_grid)) + v_star = successive_approx(v -> T(v, model), vz, tolerance=tol) + return get_greedy(v_star, model) +end + +"Howard policy iteration routine." +function policy_iteration(model) + wn, yn = length(model.w_grid), length(model.y_grid) + σ = ones(Int32, wn, yn) + i, error = 0, 1.0 + while error > 0 + v_σ = get_value(σ, model) + σ_new = get_greedy(v_σ, model) + error = maximum(abs.(σ_new - σ)) + σ = σ_new + i = i + 1 + println("Concluded loop $i with error $error.") + end + return σ +end + +"Optimistic policy iteration routine." +function optimistic_policy_iteration(model; tolerance=1e-5, m=100) + v = zeros(length(model.w_grid), length(model.y_grid)) + error = tolerance + 1 + while error > tolerance + last_v = v + σ = get_greedy(v, model) + for i in 1:m + v = T_σ(v, σ, model) + end + error = maximum(abs.(v - last_v)) + end + return get_greedy(v, model) +end + +# == Simulations and inequality measures == # + +function simulate_wealth(m) + + model = create_savings_model() + σ_star = optimistic_policy_iteration(model) + (; β, R, γ, w_grid, y_grid, Q) = model + + # Simulate labor income (indices rather than grid values) + mc = MarkovChain(Q) + y_idx_series = simulate(mc, m) + + # Compute corresponding wealth time series + w_idx_series = similar(y_idx_series) + w_idx_series[1] = 1 # initial condition + for t in 1:(m-1) + i, j = w_idx_series[t], y_idx_series[t] + w_idx_series[t+1] = σ_star[i, j] + end + w_series = w_grid[w_idx_series] + + return w_series +end + +function lorenz(v) # assumed sorted vector + S = cumsum(v) # cumulative sums: [v[1], v[1] + v[2], ... ] + F = (1:length(v)) / length(v) + L = S ./ S[end] + return (; F, L) # returns named tuple +end + +gini(v) = (2 * sum(i * y for (i,y) in enumerate(v))/sum(v) + - (length(v) + 1))/length(v) + + + +# == Plots == # + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=16 + +function plot_timing(; m_vals=collect(range(1, 600, step=10)), + savefig=false) + model = create_savings_model(y_size=5) + println("Running Howard policy iteration.") + pi_time = @elapsed σ_pi = policy_iteration(model) + println("PI completed in $pi_time seconds.") + println("Running value function iteration.") + vfi_time = @elapsed σ_vfi = value_iteration(model) + println("VFI completed in $vfi_time seconds.") + @assert σ_vfi == σ_pi "Warning: policies deviated." + opi_times = [] + for m in m_vals + println("Running optimistic policy iteration with m=$m.") + opi_time = @elapsed σ_opi = optimistic_policy_iteration(model, m=m) + @assert σ_opi == σ_pi "Warning: policies deviated." + println("OPI with m=$m completed in $opi_time seconds.") + push!(opi_times, opi_time) + end + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(m_vals, fill(vfi_time, length(m_vals)), + lw=2, label="value function iteration") + ax.plot(m_vals, fill(pi_time, length(m_vals)), + lw=2, label="Howard policy iteration") + ax.plot(m_vals, opi_times, lw=2, label="optimistic policy iteration") + ax.legend(fontsize=fontsize, frameon=false) + ax.set_xlabel(L"m", fontsize=fontsize) + ax.set_ylabel("time", fontsize=fontsize) + plt.show() + if savefig + fig.savefig("./figures/finite_opt_saving_2_1.pdf") + end + return (pi_time, vfi_time, opi_times) +end + +function plot_policy(; method="pi") + model = create_savings_model() + (; β, R, γ, w_grid, y_grid, Q) = model + if method == "vfi" + σ_star = value_iteration(model) + elseif method == "pi" + σ_star = policy_iteration(model) + else + σ_star = optimistic_policy_iteration(model) + end + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(w_grid, w_grid, "k--", label=L"45") + ax.plot(w_grid, w_grid[σ_star[:, 1]], label=L"\sigma^*(\cdot, y_1)") + ax.plot(w_grid, w_grid[σ_star[:, end]], label=L"\sigma^*(\cdot, y_N)") + ax.legend(fontsize=fontsize) + plt.show() +end + + +function plot_time_series(; m=2_000, + savefig=false, + figname="./figures/finite_opt_saving_ts.pdf") + + w_series = simulate_wealth(m) + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(w_series, label=L"w_t") + ax.set_xlabel("time", fontsize=fontsize) + ax.legend(fontsize=fontsize) + plt.show() + if savefig + fig.savefig(figname) + end +end + +function plot_histogram(; m=1_000_000, + savefig=false, + figname="./figures/finite_opt_saving_hist.pdf") + + w_series = simulate_wealth(m) + g = round(gini(sort(w_series)), digits=2) + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.hist(w_series, bins=40, density=true) + ax.set_xlabel("wealth", fontsize=fontsize) + ax.text(15, 0.4, "Gini = $g", fontsize=fontsize) + plt.show() + + if savefig + fig.savefig(figname) + end +end + +function plot_lorenz(; m=1_000_000, + savefig=false, + figname="./figures/finite_opt_saving_lorenz.pdf") + + w_series = simulate_wealth(m) + (; F, L) = lorenz(sort(w_series)) + + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(F, F, label="Lorenz curve, equality") + ax.plot(F, L, label="Lorenz curve, wealth distribution") + ax.legend() + plt.show() + + if savefig + fig.savefig(figname) + end +end diff --git a/code/jl/firm_exit.jl b/code/jl/firm_exit.jl new file mode 100644 index 0000000..0139b7d --- /dev/null +++ b/code/jl/firm_exit.jl @@ -0,0 +1,106 @@ +""" +Firm valuation with exit option. + +""" + +using QuantEcon, LinearAlgebra +include("s_approx.jl") + +"Creates an instance of the firm exit model." +function create_exit_model(; + n=200, # productivity grid size + ρ=0.95, μ=0.1, ν=0.1, # persistence, mean and volatility + β=0.98, s=100.0 # discount factor and scrap value + ) + mc = tauchen(n, ρ, ν, μ) + z_vals, Q = mc.state_values, mc.p + return (; n, z_vals, Q, β, s) +end + + +"Compute value of firm without exit option." +function no_exit_value(model) + (; n, z_vals, Q, β, s) = model + return (I - β * Q) \ z_vals +end + +" The Bellman operator Tv = max{s, π + β Q v}." +function T(v, model) + (; n, z_vals, Q, β, s) = model + h = z_vals .+ β * Q * v + return max.(s, h) +end + +" Get a v-greedy policy." +function get_greedy(v, model) + (; n, z_vals, Q, β, s) = model + σ = s .>= z_vals .+ β * Q * v + return σ +end + +"Solve by VFI." +function vfi(model) + v_init = no_exit_value(model) + v_star = successive_approx(v -> T(v, model), v_init) + σ_star = get_greedy(v_star, model) + return v_star, σ_star +end + + +# Plots + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=16 + + +function plot_val(; savefig=false, + figname="./figures/firm_exit_1.pdf") + + fig, ax = plt.subplots(figsize=(9, 5.2)) + + model = create_exit_model() + (; n, z_vals, Q, β, s) = model + + v_star, σ_star = vfi(model) + h = z_vals + β * Q * v_star + + ax.plot(z_vals, h, "-", lw=3, alpha=0.6, label=L"h^*") + ax.plot(z_vals, s * ones(n), "-", lw=3, alpha=0.6, label=L"s") + ax.plot(z_vals, v_star, "k--", lw=1.5, alpha=0.8, label=L"v^*") + + ax.legend(frameon=false, fontsize=fontsize) + ax.set_xlabel(L"z", fontsize=fontsize) + + plt.show() + if savefig + fig.savefig(figname) + end +end + + +function plot_comparison(; savefig=false, + figname="./figures/firm_exit_2.pdf") + + fig, ax = plt.subplots(figsize=(9, 5.2)) + + model = create_exit_model() + (; n, z_vals, Q, β, s) = model + + v_star, σ_star = vfi(model) + w = no_exit_value(model) + + ax.plot(z_vals, v_star, "k-", lw=2, alpha=0.6, label=L"v^*") + ax.plot(z_vals, w, lw=2, alpha=0.6, label="no-exit value") + + ax.legend(frameon=false, fontsize=fontsize) + ax.set_xlabel(L"z", fontsize=fontsize) + + plt.show() + if savefig + fig.savefig(figname) + end +end + + diff --git a/code/jl/firm_hiring.jl b/code/jl/firm_hiring.jl new file mode 100644 index 0000000..c309f40 --- /dev/null +++ b/code/jl/firm_hiring.jl @@ -0,0 +1,175 @@ +using QuantEcon, LinearAlgebra, IterTools + +function create_hiring_model(; + r=0.04, # Interest rate + κ=1.0, # Adjustment cost + α=0.4, # Production parameter + p=1.0, w=1.0, # Price and wage + l_min=0.0, l_max=30.0, l_size=100, # Grid for labor + ρ=0.9, ν=0.4, b=1.0, # AR(1) parameters + z_size=100) # Grid size for shock + β = 1/(1+r) + l_grid = LinRange(l_min, l_max, l_size) + mc = tauchen(z_size, ρ, ν, b, 6) + z_grid, Q = mc.state_values, mc.p + return (; β, κ, α, p, w, l_grid, z_grid, Q) +end + +""" +The aggregator B is given by + + B(l, z, l′) = r(l, z, l′) + β Σ_z′ v(l′, z′) Q(z, z′)." + +where + + r(l, z, l′) := p * z * f(l) - w * l - κ 1{l != l′} + +""" +function B(i, j, k, v, model) + (; β, κ, α, p, w, l_grid, z_grid, Q) = model + l, z, l′ = l_grid[i], z_grid[j], l_grid[k] + r = p * z * l^α - w * l - κ * (l != l′) + return @views r + β * dot(v[k, :], Q[j, :]) +end + + +"The policy operator." +function T_σ(v, σ, model) + l_idx, z_idx = (eachindex(g) for g in (model.l_grid, model.z_grid)) + v_new = similar(v) + for (i, j) in product(l_idx, z_idx) + v_new[i, j] = B(i, j, σ[i, j], v, model) + end + return v_new +end + +"Compute a v-greedy policy." +function get_greedy(v, model) + (; β, κ, α, p, w, l_grid, z_grid, Q) = model + l_idx, z_idx = (eachindex(g) for g in (model.l_grid, model.z_grid)) + σ = Matrix{Int32}(undef, length(l_idx), length(z_idx)) + for (i, j) in product(l_idx, z_idx) + _, σ[i, j] = findmax(B(i, j, k, v, model) for k in l_idx) + end + return σ +end + +"Optimistic policy iteration routine." +function optimistic_policy_iteration(model; tolerance=1e-5, m=100) + v = zeros(length(model.l_grid), length(model.z_grid)) + error = tolerance + 1 + while error > tolerance + last_v = v + σ = get_greedy(v, model) + for i in 1:m + v = T_σ(v, σ, model) + end + error = maximum(abs.(v - last_v)) + end + return get_greedy(v, model) +end + + +# Plots + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=14 + +function plot_policy(; savefig=false, + figname="./figures/firm_hiring_pol.pdf") + model = create_hiring_model() + (; β, κ, α, p, w, l_grid, z_grid, Q) = model + σ_star = optimistic_policy_iteration(model) + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(l_grid, l_grid, "k--", label=L"45") + ax.plot(l_grid, l_grid[σ_star[:, 1]], label=L"\sigma^*(\cdot, z_1)") + ax.plot(l_grid, l_grid[σ_star[:, end]], label=L"\sigma^*(\cdot, z_N)") + ax.legend(fontsize=fontsize) + plt.show() +end + + +function sim_dynamics(model, ts_length) + + (; β, κ, α, p, w, l_grid, z_grid, Q) = model + σ_star = optimistic_policy_iteration(model) + mc = MarkovChain(Q, z_grid) + z_sim_idx = simulate_indices(mc, ts_length) + z_sim = z_grid[z_sim_idx] + l_sim_idx = Vector{Int32}(undef, ts_length) + l_sim_idx[1] = 32 + for t in 1:(ts_length-1) + l_sim_idx[t+1] = σ_star[l_sim_idx[t], z_sim_idx[t]] + end + l_sim = l_grid[l_sim_idx] + + y_sim = similar(l_sim) + for (i, l) in enumerate(l_sim) + y_sim[i] = p * z_sim[i] * l_sim[i]^α + end + + t = ts_length - 1 + l_g, y_g, z_g = zeros(t), zeros(t), zeros(t) + + for i in 1:t + l_g[i] = (l_sim[i+1] - l_sim[i]) / l_sim[i] + y_g[i] = (y_sim[i+1] - y_sim[i]) / y_sim[i] + z_g[i] = (z_sim[i+1] - z_sim[i]) / z_sim[i] + end + + return l_sim, y_sim, z_sim, l_g, y_g, z_g + +end + + + +function plot_sim(; savefig=false, + figname="./figures/firm_hiring_ts.pdf", + ts_length = 250) + + model = create_hiring_model() + (; β, κ, α, p, w, l_grid, z_grid, Q) = model + l_sim, y_sim, z_sim, l_g, y_g, z_g = sim_dynamics(model, ts_length) + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(1:ts_length, l_sim, label=L"\ell_t") + ax.plot(1:ts_length, z_sim, alpha=0.6, label=L"Z_t") + ax.legend(fontsize=fontsize, frameon=false) + ax.set_ylabel("employment", fontsize=fontsize) + ax.set_xlabel("time", fontsize=fontsize) + + plt.show() + if savefig + fig.savefig(figname) + end +end + + +function plot_growth(; savefig=false, + figname="./figures/firm_hiring_g.pdf", + ts_length = 10_000_000) + + model = create_hiring_model() + (; β, κ, α, p, w, l_grid, z_grid, Q) = model + l_sim, y_sim, z_sim, l_g, y_g, z_g = sim_dynamics(model, ts_length) + + fig, ax = plt.subplots() + ax.hist(l_g, alpha=0.6, bins=100) + ax.set_xlabel("growth", fontsize=fontsize) + + #fig, axes = plt.subplots(2, 1) + #series = y_g, z_g + #for (ax, g) in zip(axes, series) + # ax.hist(g, alpha=0.6, bins=100) + # ax.set_xlabel("growth", fontsize=fontsize) + #end + + plt.tight_layout() + plt.show() + if savefig + fig.savefig(figname) + end +end + + diff --git a/code/jl/fosd_tauchen.jl b/code/jl/fosd_tauchen.jl new file mode 100644 index 0000000..553de97 --- /dev/null +++ b/code/jl/fosd_tauchen.jl @@ -0,0 +1,32 @@ +using Distributions +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +n = 25 +ν = 1.0 +a = 0.5 + +mc = tauchen(n, a, ν) +i, j = 8, 12 + + +fig, axes = plt.subplots(2, 1, figsize=(10, 5.2)) +fontsize=16 +ax = axes[1] + +ax.plot(mc.state_values, mc.p[i, :], "b-o", alpha=0.4, lw=2, label=L"\varphi") +ax.plot(mc.state_values, mc.p[j, :], "g-o", alpha=0.4, lw=2, label=L"\psi") +ax.legend(frameon=false, fontsize=fontsize) + +ax = axes[2] +F = [sum(mc.p[i, k:end]) for k in 1:n] +G = [sum(mc.p[j, k:end]) for k in 1:n] +ax.plot(mc.state_values, F, "b-o", alpha=0.4, lw=2, label=L"G^\varphi") +ax.plot(mc.state_values, G, "g-o", alpha=0.4, lw=2, label=L"G^\psi") +ax.legend(frameon=false, fontsize=fontsize) + +plt.show() + +fig.savefig("./figures/fosd_tauchen_1.pdf") + diff --git a/code/jl/howard_newton.jl b/code/jl/howard_newton.jl new file mode 100644 index 0000000..accb331 --- /dev/null +++ b/code/jl/howard_newton.jl @@ -0,0 +1,56 @@ +using PyPlot, LaTeXStrings +using ForwardDiff, Roots +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +v0 = 0.5 +T(x) = 1 + 0.2 * x^2 +DT = v -> ForwardDiff.derivative(T, float(v)) +Tsp(v; v0=v0) = T(v0) + DT(v0) * (v - v0) # T_\sigma' + +vs = find_zero(v -> T(v)-v, 0.5) # find fixed point of T +v1 = (T(v0) - DT(v0) * v0) / (1 - DT(v0)) # v_\sigma' + +fs = 18 + +xmin, xmax = -0.2, 2.5 +xgrid = LinRange(xmin, xmax, 1000) + +fig, ax = plt.subplots() + +lb_T = L"T" +ax.plot(xgrid, T.(xgrid), lw=2, alpha=0.6, label=lb_T) + +lb_Tsp = L"T_{\sigma'}" +ax.plot(xgrid, Tsp.(xgrid), lw=2, alpha=0.6, label=lb_Tsp) + +ax.plot(xgrid, xgrid, "k--", lw=1, alpha=0.7, label=L"45^{\circ}") + +fp1 = (v1,) +ax.plot(fp1, fp1, "go", ms=5, alpha=0.6) + +ax.plot((v0,), (Tsp(v0),) , "go", ms=5, alpha=0.6) + +ax.plot((vs,), (vs,) , "go", ms=5, alpha=0.6) + +ax.vlines((v0, vs, v1), (0, 0, 0), (Tsp(v0), vs, v1), + color="k", linestyle="-.", lw=0.4) + +ax.legend(frameon=false, fontsize=fs) + +ax.set_xticks((v0, vs, v1)) +ax.set_xticklabels((L"v_\sigma", L"v^*", L"v_{\sigma'}"), fontsize=fs) +ax.set_yticks((0, )) + +ax.set_xlim(0, 2.6) +ax.set_ylim(0, 2.6) +#ax.set_xlabel(L"x", fontsize=14) + +plt.show() + +filename = "./figures/howard_newton_1.pdf" + +if true + fig.savefig(filename) +end + + diff --git a/code/jl/iid_job_search.jl b/code/jl/iid_job_search.jl new file mode 100644 index 0000000..cf7309e --- /dev/null +++ b/code/jl/iid_job_search.jl @@ -0,0 +1,108 @@ +""" +VFI approach to job search in the infinite-horizon IID case. + +""" + +include("two_period_job_search.jl") +include("s_approx.jl") + +" The Bellman operator. " +function T(v, model) + (; n, w_vals, ϕ, β, c) = model + return [max(w / (1 - β), c + β * v'ϕ) for w in w_vals] +end + +" Get a v-greedy policy. " +function get_greedy(v, model) + (; n, w_vals, ϕ, β, c) = model + σ = w_vals ./ (1 - β) .>= c .+ β * v'ϕ # Boolean policy vector + return σ +end + +" Solve the infinite-horizon IID job search model by VFI. " +function vfi(model=default_model) + (; n, w_vals, ϕ, β, c) = model + v_init = zero(model.w_vals) + v_star = successive_approx(v -> T(v, model), v_init) + σ_star = get_greedy(v_star, model) + return v_star, σ_star +end + + +# == Plots == # + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +# A model with default parameters +default_model = create_job_search_model() + + +" Plot a sequence of approximations. " +function fig_vseq(model=default_model; + k=3, + savefig=false, + figname="./figures/iid_job_search_1.pdf", + fs=16) + + v = zero(model.w_vals) + fig, ax = plt.subplots(figsize=(9, 5.5)) + for i in 1:k + ax.plot(model.w_vals, v, lw=3, alpha=0.6, label="iterate $i") + v = T(v, model) + end + + for i in 1:1000 + v = T(v, model) + end + ax.plot(model.w_vals, v, "k-", lw=3.0, label="iterate 1000", alpha=0.7) + + #ax.set_ylim((0, 140)) + ax.set_xlabel("wage offer", fontsize=fs) + ax.set_ylabel("lifetime value", fontsize=fs) + + ax.legend(fontsize=fs, frameon=false) + + if savefig + fig.savefig(figname) + end + plt.show() +end + + +" Plot the fixed point. " +function fig_vstar(model=default_model; + savefig=false, fs=18, + figname="./figures/iid_job_search_3.pdf") + + (; n, w_vals, ϕ, β, c) = model + v_star, σ_star = vfi(model) + + fig, ax = plt.subplots(figsize=(9, 5.5)) + ax.plot(w_vals, v_star, "k-", lw=1.5, label="value function") + cont_val = c + β * v_star'ϕ + ax.plot(w_vals, fill(cont_val, n+1), + "--", + lw=5, + alpha=0.5, + label="continuation value") + + ax.plot(w_vals, + w_vals / (1 - β), + "--", + lw=5, + alpha=0.5, + label=L"w/(1 - \beta)") + + #ax.set_ylim(0, v_star.max()) + ax.legend(frameon=false, fontsize=fs, loc="lower right") + + if savefig + fig.savefig(figname) + end + plt.show() +end + + + diff --git a/code/jl/iid_job_search_cv.jl b/code/jl/iid_job_search_cv.jl new file mode 100644 index 0000000..046d794 --- /dev/null +++ b/code/jl/iid_job_search_cv.jl @@ -0,0 +1,145 @@ +""" +Continuation value function approach to job search in the IID case. + +""" + +include("iid_job_search.jl") +include("s_approx.jl") + +function g(h, model) + (; n, w_vals, ϕ, β, c) = model + return c + β * max.(w_vals / (1 - β), h)'ϕ +end + +function compute_hstar_wstar(model, h_init=0.0) + (; n, w_vals, ϕ, β, c) = model + h_star = successive_approx(h -> g(h, model), h_init) + return h_star, (1 - β) * h_star +end + + +# == Plots == # + +" Plot the function g. " +function fig_g(model=default_model; + savefig=false, fs=18, + figname="./figures/iid_job_search_g.pdf") + + (; n, w_vals, ϕ, β, c) = model + h_grid = collect(LinRange(600, max(c, n) / (1 - β), 100)) + g_vals = [g(h, model) for h in h_grid] + + fig, ax = plt.subplots(figsize=(9, 5.5)) + ax.plot(h_grid, g_vals, lw=2.0, label=L"g") + ax.plot(h_grid, h_grid, "k--", lw=1.0, label="45") + + ax.legend(frameon=false, fontsize=fs, loc="lower right") + + h_star, w_star = compute_hstar_wstar(model) + ax.plot(h_star, h_star, "go", ms=10, alpha=0.6) + + ax.annotate(L"$h^*$", + xy=(h_star, h_star), + xycoords="data", + xytext=(40, -40), + textcoords="offset points", + fontsize=fs) + + if savefig + fig.savefig(figname) + end + + plt.show() + +end + + +" Plot the two ordered instances of function g. " +function fig_tg(betas=[0.95, 0.96]; + savefig=false, fs=18, + figname="./figures/iid_job_search_tg.pdf") + + h_grid = collect(LinRange(600, 1200, 100)) + fig, ax = plt.subplots(figsize=(9, 5.5)) + ax.plot(h_grid, h_grid, "k--", lw=1.0, label="45") + + for (i, β) in enumerate(betas) + model = create_job_search_model(β=β) + (; n, w_vals, ϕ, β, c) = model + b = maximum(betas) + g_vals = [g(h, model) for h in h_grid] + + lb = latexstring("g_$i \\; (\\beta_$i = $β)") + ax.plot(h_grid, g_vals, lw=2.0, label=lb) + + ax.legend(frameon=false, fontsize=fs, loc="lower right") + + h_star, w_star = compute_hstar_wstar(model) + ax.plot(h_star, h_star, "go", ms=10, alpha=0.6) + + lb = latexstring("h^*_$i") + ax.annotate(lb, + xy=(h_star, h_star), + xycoords="data", + xytext=(40, -40), + textcoords="offset points", + fontsize=fs) + end + + if savefig + fig.savefig(figname) + end + + plt.show() + +end + + +" Plot continuation value and the fixed point. " +function fig_cv(model=default_model; + fs=18, + savefig=false, + figname="./figures/iid_job_search_4.pdf") + + (; n, w_vals, ϕ, β, c) = model + h_star, w_star = compute_hstar_wstar(model) + vhat = max.(w_vals / (1 - β), h_star) + + fig, ax = plt.subplots() + ax.plot(w_vals, vhat, "k-", lw=2.0, label="value function") + ax.legend(fontsize=fs) + ax.set_ylim(0, maximum(vhat)) + + plt.show() + if savefig + fig.savefig(figname) + end +end + + +" Plot the fixed point as a function of β. " +function fig_bf(betas=LinRange(0.9, 0.99, 20); + savefig=false, + fs=16, + figname="./figures/iid_job_search_bf.pdf") + + h_vals = similar(betas) + for (i, β) in enumerate(betas) + model = create_job_search_model(β=β) + h, w = compute_hstar_wstar(model) + h_vals[i] = h + end + + fig, ax = plt.subplots() + ax.plot(betas, h_vals, lw=2.0, alpha=0.7, label=L"h^*(\beta)") + ax.legend(frameon=false, fontsize=fs) + ax.set_xlabel(L"\beta", fontsize=fs) + ax.set_ylabel("continuation value", fontsize=fs) + + if savefig + fig.savefig(figname) + end + + plt.show() + +end diff --git a/code/jl/inventory_cont_time.jl b/code/jl/inventory_cont_time.jl new file mode 100644 index 0000000..90c2f17 --- /dev/null +++ b/code/jl/inventory_cont_time.jl @@ -0,0 +1,64 @@ +using Random, Distributions + +""" +Generate a path for inventory starting at b, up to time T. + +Return the path as a function X(t) constructed from (J_k) and (Y_k). +""" +function sim_path(; T=10, seed=123, λ=0.5, α=0.7, b=10) + + J, Y = 0.0, b + J_vals, Y_vals = [J], [Y] + Random.seed!(seed) + φ = Exponential(1/λ) # Wait times are exponential + G = Geometric(α) # Orders are geometric + + while true + W = rand(φ) + J += W + push!(J_vals, J) + if Y == 0 + Y = b + else + U = rand(G) + 1 # Geometric on 1, 2,... + Y = Y - min(Y, U) + end + push!(Y_vals, Y) + if J > T + break + end + end + + function X(t) + k = searchsortedlast(J_vals, t) + return Y_vals[k+1] + end + + return X +end + + + +T = 50 +X = sim_path(T=T) + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +grid = LinRange(0, T - 0.001, 100) + +fig, ax = plt.subplots(figsize=(9, 5.2)) +ax.step(grid, [X(t) for t in grid], label=L"X_t", alpha=0.7) + +ax.set(xticks=(0, 10, 20, 30, 40, 50)) + +ax.set_xlabel("time", fontsize=12) +ax.set_ylabel("inventory", fontsize=12) +ax.legend(fontsize=12) + +plt.savefig("./figures/inventory_cont_time_1.pdf") +plt.show() + + + diff --git a/code/jl/inventory_dp.jl b/code/jl/inventory_dp.jl new file mode 100644 index 0000000..c75a9f8 --- /dev/null +++ b/code/jl/inventory_dp.jl @@ -0,0 +1,116 @@ +include("s_approx.jl") +using Distributions +m(x) = max(x, 0) # Convenience function + +function create_inventory_model(; β=0.98, # discount factor + K=40, # maximum inventory + c=0.2, κ=2, # cost paramters + p=0.6) # demand parameter + ϕ(d) = (1 - p)^d * p # demand pdf + x_vals = collect(0:K) # set of inventory levels + return (; β, K, c, κ, p, ϕ, x_vals) +end + +"The function B(x, a, v) = r(x, a) + β Σ_x′ v(x′) P(x, a, x′)." +function B(x, a, v, model; d_max=100) + (; β, K, c, κ, p, ϕ, x_vals) = model + revenue = sum(min(x, d) * ϕ(d) for d in 0:d_max) + current_profit = revenue - c * a - κ * (a > 0) + next_value = sum(v[m(x - d) + a + 1] * ϕ(d) for d in 0:d_max) + return current_profit + β * next_value +end + +"The Bellman operator." +function T(v, model) + (; β, K, c, κ, p, ϕ, x_vals) = model + new_v = similar(v) + for (x_idx, x) in enumerate(x_vals) + Γx = 0:(K - x) + new_v[x_idx], _ = findmax(B(x, a, v, model) for a in Γx) + end + return new_v +end + +"Get a v-greedy policy. Returns a zero-based array." +function get_greedy(v, model) + (; β, K, c, κ, p, ϕ, x_vals) = model + σ_star = zero(x_vals) + for (x_idx, x) in enumerate(x_vals) + Γx = 0:(K - x) + _, a_idx = findmax(B(x, a, v, model) for a in Γx) + σ_star[x_idx] = Γx[a_idx] + end + return σ_star +end + +"Use successive_approx to get v_star and then compute greedy." +function solve_inventory_model(v_init, model) + (; β, K, c, κ, p, ϕ, x_vals) = model + v_star = successive_approx(v -> T(v, model), v_init) + σ_star = get_greedy(v_star, model) + return v_star, σ_star +end + +# == Plots == # + +using PyPlot +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +# Create an instance of the model and solve it +model = create_inventory_model() +(; β, K, c, κ, p, ϕ, x_vals) = model +v_init = zeros(length(x_vals)) +v_star, σ_star = solve_inventory_model(v_init, model) + +"Simulate given the optimal policy." +function sim_inventories(ts_length=400, X_init=0) + G = Geometric(p) + X = zeros(Int32, ts_length) + X[1] = X_init + for t in 1:(ts_length-1) + D = rand(G) + X[t+1] = m(X[t] - D) + σ_star[X[t] + 1] + end + return X +end + + +function plot_vstar_and_opt_policy(; fontsize=16, + figname="./figures/inventory_dp_vs.pdf", + savefig=false) + fig, axes = plt.subplots(2, 1, figsize=(8, 6.5)) + + ax = axes[1] + ax.plot(0:K, v_star, label=L"v^*") + ax.set_ylabel("value", fontsize=fontsize) + ax.legend(fontsize=fontsize, frameon=false) + + ax = axes[2] + ax.plot(0:K, σ_star, label=L"\sigma^*") + ax.set_xlabel("inventory", fontsize=fontsize) + ax.set_ylabel("optimal choice", fontsize=fontsize) + ax.legend(fontsize=fontsize, frameon=false) + plt.show() + if savefig == true + fig.savefig(figname) + end +end + +function plot_ts(; fontsize=16, + figname="./figures/inventory_dp_ts.pdf", + savefig=false) + X = sim_inventories() + fig, ax = plt.subplots(figsize=(9, 5.5)) + ax.plot(X, label=L"X_t", alpha=0.7) + ax.set_xlabel(L"t", fontsize=fontsize) + ax.set_ylabel("inventory", fontsize=fontsize) + ax.legend(fontsize=fontsize, frameon=false) + ax.set_ylim(0, maximum(X)+4) + plt.show() + if savefig == true + fig.savefig(figname) + end +end + diff --git a/code/jl/inventory_sdd.jl b/code/jl/inventory_sdd.jl new file mode 100644 index 0000000..e98585e --- /dev/null +++ b/code/jl/inventory_sdd.jl @@ -0,0 +1,137 @@ +""" +Inventory management model with state-dependent discounting. +The discount factor takes the form β_t = Z_t, where (Z_t) is +a discretization of the Gaussian AR(1) process + + X_t = ρ X_{t-1} + b + ν W_t. + +""" + +include("s_approx.jl") +using LinearAlgebra, Distributions, QuantEcon + +function create_sdd_inventory_model(; + ρ=0.98, ν=0.002, n_z=20, b=0.97, # Z state parameters + K=40, c=0.2, κ=0.8, p=0.6) # firm and demand parameters + ϕ(d) = (1 - p)^d * p # demand pdf + y_vals = collect(0:K) # inventory levels + mc = tauchen(n_z, ρ, ν) + z_vals, Q = mc.state_values .+ b, mc.p + ρL = maximum(abs.(eigvals(z_vals .* Q))) + @assert ρL < 1 "Error: ρ(L) ≥ 1." # check ρ(L) < 1 + return (; K, c, κ, p, ϕ, y_vals, z_vals, Q) +end + +m(y) = max(y, 0) # Convenience function + +"The function B(x, a, v) = r(x, a) + β(x) Σ_x′ v(x′) P(x, a, x′)." +function B(x, i_z, a, v, model; d_max=100) + (; K, c, κ, p, ϕ, y_vals, z_vals, Q) = model + z = z_vals[i_z] + revenue = sum(min(x, d)*ϕ(d) for d in 0:d_max) + current_profit = revenue - c * a - κ * (a > 0) + cv = 0.0 + for i_z′ in eachindex(z_vals) + for d in 0:d_max + cv += v[m(x - d) + a + 1, i_z′] * ϕ(d) * Q[i_z, i_z′] + end + end + return current_profit + z * cv +end + +"The Bellman operator." +function T(v, model) + (; K, c, κ, p, ϕ, y_vals, z_vals, Q) = model + new_v = similar(v) + for (i_z, z) in enumerate(z_vals) + for (i_y, y) in enumerate(y_vals) + Γy = 0:(K - y) + new_v[i_y, i_z], _ = findmax(B(y, i_z, a, v, model) for a in Γy) + end + end + return new_v +end + +"Get a v-greedy policy. Returns a zero-based array." +function get_greedy(v, model) + (; K, c, κ, p, ϕ, y_vals, z_vals, Q) = model + n_z = length(z_vals) + σ_star = zeros(Int32, K+1, n_z) + for (i_z, z) in enumerate(z_vals) + for (i_y, y) in enumerate(y_vals) + Γy = 0:(K - y) + _, i_a = findmax(B(y, i_z, a, v, model) for a in Γy) + σ_star[i_y, i_z] = Γy[i_a] + end + end + return σ_star +end + + +"Use successive_approx to get v_star and then compute greedy." +function solve_inventory_model(v_init, model) + (; K, c, κ, p, ϕ, y_vals, z_vals, Q) = model + v_star = successive_approx(v -> T(v, model), v_init) + σ_star = get_greedy(v_star, model) + return v_star, σ_star +end + + +# == Plots == # + +using PyPlot +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +# Create an instance of the model and solve it +model = create_sdd_inventory_model() +(; K, c, κ, p, ϕ, y_vals, z_vals, Q) = model +n_z = length(z_vals) +v_init = zeros(Float64, K+1, n_z) +println("Solving model.") +v_star, σ_star = solve_inventory_model(v_init, model) +z_mc = MarkovChain(Q, z_vals) + +"Simulate given the optimal policy." +function sim_inventories(ts_length; X_init=0) + i_z = simulate_indices(z_mc, ts_length, init=1) + G = Geometric(p) + X = zeros(Int32, ts_length) + X[1] = X_init + for t in 1:(ts_length-1) + D′ = rand(G) + X[t+1] = m(X[t] - D′) + σ_star[X[t] + 1, i_z[t]] + end + return X, z_vals[i_z] +end + +function plot_ts(; ts_length=400, + fontsize=16, + figname="./figures/inventory_sdd_ts.pdf", + savefig=false) + X, Z = sim_inventories(ts_length) + fig, axes = plt.subplots(2, 1, figsize=(9, 5.5)) + + ax = axes[1] + ax.plot(X, label="inventory", alpha=0.7) + ax.set_xlabel(L"t", fontsize=fontsize) + ax.legend(fontsize=fontsize, frameon=false) + ax.set_ylim(0, maximum(X)+3) + + # calculate interest rate from discount factors + r = (1 ./ Z) .- 1 + + ax = axes[2] + ax.plot(r, label=L"r_t", alpha=0.7) + ax.set_xlabel(L"t", fontsize=fontsize) + ax.legend(fontsize=fontsize, frameon=false) + #ax.set_ylim(0, maximum(X)+8) + + plt.tight_layout() + plt.show() + if savefig == true + fig.savefig(figname) + end +end + diff --git a/code/jl/inventory_sim.jl b/code/jl/inventory_sim.jl new file mode 100644 index 0000000..144d2df --- /dev/null +++ b/code/jl/inventory_sim.jl @@ -0,0 +1,88 @@ +using Distributions, IterTools, QuantEcon + +function create_inventory_model(; S=100, # Order size + s=10, # Order threshold + p=0.4) # Demand parameter + ϕ = Geometric(p) + h(x, d) = max(x - d, 0) + S*(x <= s) + return (; S, s, p, ϕ, h) +end + +"Simulate the inventory process." +function sim_inventories(model; ts_length=200) + (; S, s, p, ϕ, h) = model + X = Vector{Int32}(undef, ts_length) + X[1] = S # Initial condition + for t in 1:(ts_length-1) + X[t+1] = h(X[t], rand(ϕ)) + end + return X +end + +"Compute the transition probabilities and state." +function compute_mc(model; d_max=100) + (; S, s, p, ϕ, h) = model + n = S + s + 1 # Size of state space + state_vals = collect(0:(S + s)) + P = Matrix{Float64}(undef, n, n) + for (i, j) in product(1:n, 1:n) + P[i, j] = sum((h(i-1, d) == j-1)*pdf(ϕ, d) for d in 0:d_max) + end + return MarkovChain(P, state_vals) +end + +"Compute the stationary distribution of the model." +function compute_stationary_dist(model) + mc = compute_mc(model) + return mc.state_values, stationary_distributions(mc)[1] +end + + +# Plots + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + + +function plot_ts(model; fontsize=16, + figname="./figures/inventory_sim_1.pdf", + savefig=false) + (; S, s, p, ϕ, h) = model + X = sim_inventories(model) + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(X, label=L"X_t", lw=3, alpha=0.6) + ax.set_xlabel(L"t", fontsize=fontsize) + ax.set_ylabel("inventory", fontsize=fontsize) + ax.legend(fontsize=fontsize, frameon=false) + ax.set_ylim(0, S + s + 20) + + plt.show() + if savefig == true + fig.savefig(figname) + end +end + + +function plot_hist(model; fontsize=16, + figname="./figures/inventory_sim_2.pdf", + savefig=false) + (; S, s, p, ϕ, h) = model + state_values, ψ_star = compute_stationary_dist(model) + X = sim_inventories(model; ts_length=1_000_000) + histogram = [mean(X .== i) for i in state_values] + + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(state_values, ψ_star, "k-", lw=3, alpha=0.7, label=L"\psi^*") + ax.bar(state_values, histogram, alpha=0.7, label="frequency") + ax.set_xlabel("state", fontsize=fontsize) + + ax.legend(fontsize=fontsize, frameon=false) + ax.set_ylim(0, 0.015) + + plt.show() + if savefig == true + fig.savefig(figname) + end +end + diff --git a/code/jl/is_irreducible.jl b/code/jl/is_irreducible.jl new file mode 100644 index 0000000..a96bb9a --- /dev/null +++ b/code/jl/is_irreducible.jl @@ -0,0 +1,5 @@ +using QuantEcon +P = [0.1 0.9; + 0.0 1.0] +mc = MarkovChain(P) +print(is_irreducible(mc)) diff --git a/code/jl/js_with_sep_sim.jl b/code/jl/js_with_sep_sim.jl new file mode 100644 index 0000000..afd289a --- /dev/null +++ b/code/jl/js_with_sep_sim.jl @@ -0,0 +1,80 @@ +""" +Simulation of the job search with Markov wage draws and separation. + +""" + +include("markov_js_with_sep.jl") # Code to solve model +using Distributions + +# Create and solve model +model = create_js_with_sep_model() +(; n, w_vals, P, β, c, α) = model +v_star, σ_star = vfi(model) + +# Create Markov distributions to draw from +P_dists = [DiscreteRV(P[i, :]) for i in 1:n] + +function update_wages_idx(w_idx) + return rand(P_dists[w_idx]) +end + +function sim_wages(ts_length=100) + w_idx = rand(DiscreteUniform(1, n)) + W = zeros(ts_length) + for t in 1:ts_length + W[t] = w_vals[w_idx] + w_idx = update_wages_idx(w_idx) + end + return W +end + +function sim_outcomes(; ts_length=100) + status = 0 + E, W = [], [] + w_idx = rand(DiscreteUniform(1, n)) + ts_length = 100 + for t in 1:ts_length + if status == 0 + status = σ_star[w_idx] ? 1 : 0 + else + status = rand() < α ? 0 : 1 + end + push!(W, w_vals[w_idx]) + push!(E, status) + w_idx = update_wages_idx(w_idx) + end + return W, E +end + + +# == Plots == # + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=16 + +function plot_status(; ts_length=200, + savefig=false, + figname="./figures/js_with_sep_sim_1.pdf") + + W, E = sim_outcomes() + fs = 16 + fig, axes = plt.subplots(2, 1) + + ax = axes[1] + ax.plot(W, label="wage offers") + ax.legend(fontsize=fs, frameon=false) + + ax = axes[2] + ax.set_yticks((0, 1)) + ax.set_yticklabels(("unempl.", "employed")) + ax.plot(E, label="status") + ax.legend(fontsize=fs, frameon=false) + + plt.show() + if savefig + fig.savefig(figname) + end + +end diff --git a/code/jl/laborer_sim.jl b/code/jl/laborer_sim.jl new file mode 100644 index 0000000..1f4e7eb --- /dev/null +++ b/code/jl/laborer_sim.jl @@ -0,0 +1,36 @@ +function create_laborer_model(; α=0.3, β=0.2) + return (; α, β) +end + +function laborer_update(x, model) # update X from t to t+1 + (; α, β) = model + if x == 1 + x′ = rand() < α ? 2 : 1 + else + x′ = rand() < β ? 1 : 2 + end + return x′ +end + +function sim_chain(k, p, model) + X = Array{Int32}(undef, k) + X[1] = rand() < p ? 1 : 2 + for t in 1:(k-1) + X[t+1] = laborer_update(X[t], model) + end + return X +end + +function test_convergence(; k=10_000_000, p=0.5) + model = create_laborer_model() + (; α, β) = model + ψ_star = (1/(α + β)) * [β α] + + X = sim_chain(k, p, model) + ψ_e = (1/k) * [sum(X .== 1) sum(X .== 2)] + error = maximum(abs.(ψ_star - ψ_e)) + approx_equal = isapprox(ψ_star, ψ_e, rtol=0.01) + println("Sup norm deviation is $error") + println("Approximate equality is $approx_equal") + end + diff --git a/code/jl/lake.jl b/code/jl/lake.jl new file mode 100644 index 0000000..5fd7dc3 --- /dev/null +++ b/code/jl/lake.jl @@ -0,0 +1,132 @@ +using LinearAlgebra + +α, λ, d, b = 0.01, 0.1, 0.02, 0.025 +g = b - d +A = [(1 - d) * (1 - λ) + b (1 - d) * α + b; + (1 - d) * λ (1 - d) * (1 - α)] + +ū = (1 + g - (1 - d) * (1 - α)) / + (1 + g - (1 - d) * (1 - α) + (1 - d) * λ) + +ē = 1 - ū +x̄ = [ū; ē] + +println(isapprox(A * x̄, (1 + g) * x̄)) # prints true + +# == Plots == # +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=14 + + + +function plot_paths(; figname="./figures/lake_1.pdf", + savefig=false) + path_length = 100 + x_path_1 = zeros(2, path_length) + x_path_2 = zeros(2, path_length) + x_0_1 = 5.0, 0.1 + x_0_2 = 0.1, 4.0 + x_path_1[1, 1] = x_0_1[1] + x_path_1[2, 1] = x_0_1[2] + x_path_2[1, 1] = x_0_2[1] + x_path_2[2, 1] = x_0_2[2] + + + for t in 1:(path_length-1) + x_path_1[:, t+1] = A * x_path_1[:, t] + x_path_2[:, t+1] = A * x_path_2[:, t] + end + + fig, ax = plt.subplots() + + # Set the axes through the origin + for spine in ["left", "bottom"] + ax.spines[spine].set_position("zero") + end + for spine in ["right", "top"] + ax.spines[spine].set_color("none") + end + + ax.set_xlim(0, 6) + ax.set_ylim(0, 6) + ax.set_xlabel("unemployed workforce", fontsize=fontsize) + ax.set_ylabel("employed workforce", fontsize=fontsize) + ax.set_xticks((0, 6)) + ax.set_yticks((0, 6)) + s = 10 + ax.plot([0, s * ū], [0, s * ē], "k--", lw=1) + ax.scatter(x_path_1[1, :], x_path_1[2, :], s=4, c="blue") + ax.scatter(x_path_2[1, :], x_path_2[2, :], s=4, c="green") + + ax.plot([ū], [ē], "ko", ms=4, alpha=0.6) + ax.annotate(L"\bar x", + xy=(ū, ē), + xycoords="data", + xytext=(20, -20), + textcoords="offset points", + fontsize=fontsize, + arrowprops=Dict("arrowstyle" => "->")) + + x, y = x_0_1[1], x_0_1[2] + lb = latexstring("\$x_0 = ($(x), $(y))\$") + ax.plot([x], [y], "ko", ms=2, alpha=0.6) + ax.annotate(lb, + xy=(x, y), + xycoords="data", + xytext=(0, 20), + textcoords="offset points", + fontsize=fontsize, + arrowprops=Dict("arrowstyle" => "->")) + + x, y = x_0_2[1], x_0_2[2] + lb = latexstring("\$x_0 = ($(x), $(y))\$") + ax.plot([x], [y], "ko", ms=2, alpha=0.6) + ax.annotate(lb, + xy=(x, y), + xycoords="data", + xytext=(0, 20), + textcoords="offset points", + fontsize=fontsize, + arrowprops=Dict("arrowstyle" => "->")) + + plt.show() + if savefig + fig.savefig(figname) + end +end + + +function plot_growth(; savefig=false, figname="./figures/lake_2.pdf") + + + path_length = 100 + x_0 = 2.1, 1.2 + x = zeros(2, path_length) + x[1, 1] = 0.6 + x[2, 1] = 1.2 + + for t in 1:(path_length-1) + x[:, t+1] = A * x[:, t] + end + + fig, axes = plt.subplots(3, 1) + u = x[1, :] + e = x[2, :] + n = x[1, :] .+ x[2, :] + paths = u, e, n + labels = L"u_t", L"e_t", L"n_t" + for (ax, path, label) in zip(axes, paths, labels) + ax.plot(path, label=label) + ax.legend(frameon=false, fontsize=14) + ax.set_xlabel(L"t") + end + + plt.tight_layout() + plt.show() + if savefig + fig.savefig(figname) + end + +end diff --git a/code/jl/linear_iter.jl b/code/jl/linear_iter.jl new file mode 100644 index 0000000..4774a88 --- /dev/null +++ b/code/jl/linear_iter.jl @@ -0,0 +1,15 @@ +include("s_approx.jl") +using LinearAlgebra + +# Compute the fixed point of Tu = Au + b via linear algebra +A, b = [0.4 0.1; 0.7 0.2], [1.0; 2.0] +u_star = (I - A) \ b # compute (I - A)^{-1} * b + +# Compute the fixed point via successive approximation +T(u) = A * u + b +u_0 = [1.0; 1.0] +u_star_approx = successive_approx(T, u_0) + +# Test for approximate equality (prints "true") +print(isapprox(u_star, u_star_approx, rtol=1e-5)) + diff --git a/code/jl/linear_iter_fig.jl b/code/jl/linear_iter_fig.jl new file mode 100644 index 0000000..3d5dd6d --- /dev/null +++ b/code/jl/linear_iter_fig.jl @@ -0,0 +1,43 @@ +include("linear_iter.jl") +using PyPlot + +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +fig, ax = plt.subplots() + +e = 0.02 + +marker_size = 60 +fs=14 + +colors = ("red", "blue", "orange", "green") +u_0_vecs = ([2.0; 3.0], [3.0; 5.2], [2.4; 3.6], [2.6, 5.6]) +iter_range = 8 + +for (u_0, color) in zip(u_0_vecs, colors) + u = u_0 + s, t = u + ax.text(s+e, t-4e, L"u_0", fontsize=fs) + + for i in 1:iter_range + s, t = u + ax.scatter((s,), (t,), c=color, alpha=0.3, s=marker_size) + u_new = T(u) + s_new, t_new = u_new + ax.plot((s, s_new), (t, t_new), lw=0.5, alpha=0.5, c=color) + u = u_new + end +end + +s_star, t_star = u_star +ax.scatter((s_star,), (t_star,), c="k", s=marker_size * 1.2) +ax.text(s_star-4e, t_star+4e, L"u^*", fontsize=fs) + +ax.set_xticks((2.0, 2.5, 3.0)) +ax.set_yticks((3.0, 4.0, 5.0, 6.0)) +ax.set_xlim(1.8, 3.2) +ax.set_ylim(2.8, 6.1) + +plt.show() +fig.savefig("./figures/linear_iter_fig_1.pdf") + diff --git a/code/jl/lqcontrol.py b/code/jl/lqcontrol.py new file mode 100644 index 0000000..b69cd45 --- /dev/null +++ b/code/jl/lqcontrol.py @@ -0,0 +1,142 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: light +# format_version: '1.5' +# jupytext_version: 1.13.7 +# kernelspec: +# display_name: Python 3 +# language: python +# name: python3 +# --- + +# ## A Life Cycle Model + +from initialize import * +from quantecon import LQ + + +# + +class LifeCycle: + + def __init__(self, + r=0.04, + β=1/(1+r), + T=45, + c_bar=2, + σ = 0.05, + μ = 1, + q = 1e6): + + print("market discount rate = ", 1/(1 + r)) + + self.r = r + self.β = β + self.T = T + self.c_bar = c_bar + self.σ = σ + self.μ = μ + self.q = q + + # Formulate as an LQ problem + Q = 1 + R = np.zeros((2, 2)) + Rf = np.zeros((2, 2)) + Rf[0, 0] = q + A = [[1 + r, -c_bar + μ], + [0, 1]] + B = [[-1], [ 0]] + C = [[σ], [0]] + + self.lq = LQ(Q, R, A, B, C, beta=self.β, T=self.T, Rf=Rf) + + self.x0 = (0, 1) + self.xp, self.up, self.wp = \ + self.lq.compute_sequence(self.x0, random_state=1234) + + # Convert back to assets, consumption and income + self.assets = self.xp[0, :] # a_t + self.c = self.up.flatten() + self.c_bar # c_t + self.income = self.σ * self.wp[0, 1:] + self.μ # y_t + + def plot(self, ax0, ax1): + + p_args = {'lw': 2, 'alpha': 0.7} + + ax0.plot(list(range(1, T+1)), + self.income, + label="non-financial income", + **p_args) + ax0.plot(list(range(T)), + self.c, + label="consumption", + **p_args) + ax0.plot(list(range(T)), + self.μ * np.ones(T), + 'k--', + lw=0.75) + + ax1.plot(list(range(1, T+1)), + np.cumsum(self.income - self.μ), + label="cumulative unanticipated income", + **p_args) + ax1.plot(list(range(T+1)), + self.assets, + label="assets", + **p_args) + ax1.plot(list(range(T)), + np.zeros(T), + 'k--', + lw=0.5) + + yl, yu = μ-3*σ, μ+3*σ + ax0.set_ylim((yl, yu)) + ax0.set_yticks((yl, μ, yu)) + + yl, yu = -9*σ, 9*σ + ax1.set_ylim((yl, yu)) + ax1.set_yticks((yl, 0.0, yu)) + + for ax in (ax0, ax1): + ax.set_xlabel('time') + ax.legend(fontsize=12, loc='upper left', frameon=False) + + + + +lc1 = LifeCycle() + +fig, axes = plt.subplots(1, 2, figsize=(10, 3.25)) +lc1.plot(axes[0], axes[1]) + +plt.savefig("./figures/lqcontrol_1.pdf") +plt.show() + + + +# + +lc2 = LifeCycle(β=0.962) +fig, axes = plt.subplots(1, 2, figsize=(10, 3.25)) +lc2.plot(axes[0], axes[1]) + +plt.savefig("./figures/lqcontrol_2.pdf") +plt.show() + + + +# + +lc3 = LifeCycle(β=0.96) +fig, axes = plt.subplots(1, 2, figsize=(10, 3.25)) +lc3.plot(axes[0], axes[1]) + +plt.savefig("./figures/lqcontrol_3.pdf") +plt.show() + + +# - + + + + diff --git a/code/jl/markov_js.jl b/code/jl/markov_js.jl new file mode 100644 index 0000000..fe89352 --- /dev/null +++ b/code/jl/markov_js.jl @@ -0,0 +1,114 @@ +""" +Infinite-horizon job search with Markov wage draws. + +""" + +using QuantEcon, LinearAlgebra +include("s_approx.jl") + +"Creates an instance of the job search model with Markov wages." +function create_markov_js_model(; + n=200, # wage grid size + ρ=0.9, # wage persistence + ν=0.2, # wage volatility + β=0.98, # discount factor + c=1.0 # unemployment compensation + ) + mc = tauchen(n, ρ, ν) + w_vals, P = exp.(mc.state_values), mc.p + return (; n, w_vals, P, β, c) +end + +" The Bellman operator Tv = max{e, c + β P v} with e(w) = w / (1-β)." +function T(v, model) + (; n, w_vals, P, β, c) = model + h = c .+ β * P * v + e = w_vals ./ (1 - β) + return max.(e, h) +end + +" Get a v-greedy policy." +function get_greedy(v, model) + (; n, w_vals, P, β, c) = model + σ = w_vals / (1 - β) .>= c .+ β * P * v + return σ +end + +"Solve the infinite-horizon Markov job search model by VFI." +function vfi(model) + v_init = zero(model.w_vals) + v_star = successive_approx(v -> T(v, model), v_init) + σ_star = get_greedy(v_star, model) + return v_star, σ_star +end + + + +# == Policy iteration == # + +"Get the value of policy σ." +function get_value(σ, model) + (; n, w_vals, P, β, c) = model + e = w_vals ./ (1 - β) + K_σ = β .* (1 .- σ) .* P + r_σ = σ .* e .+ (1 .- σ) .* c + return (I - K_σ) \ r_σ +end + + + +"Howard policy iteration routine." +function policy_iteration(model) + σ = Vector{Bool}(undef, model.n) + i, error = 0, 1.0 + while error > 0 + v_σ = get_value(σ, model) + σ_new = get_greedy(v_σ, model) + error = maximum(abs.(σ_new - σ)) + σ = σ_new + i = i + 1 + println("Concluded loop $i with error $error.") + end + return σ +end + + +# == Plots == # + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=16 + +default_model = create_markov_js_model() + + +function plot_main(; model=default_model, + method="vfi", + savefig=false, + figname="./figures/markov_js_1.pdf") + (; n, w_vals, P, β, c) = model + + + if method == "vfi" + v_star, σ_star = vfi(model) + else + σ_star = policy_iteration(model) + v_star = get_value(σ_star, model) + end + + h_star = c .+ β * P * v_star + e = w_vals / (1 - β) + + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(w_vals, h_star, lw=4, ls="--", alpha=0.4, label=L"h^*(w)") + ax.plot(w_vals, e, lw=4, ls="--", alpha=0.4, label=L"w/(1-\beta)") + ax.plot(w_vals, max.(e, h_star), "k-", alpha=0.7, label=L"v^*(w)") + ax.legend(frameon=false, fontsize=fontsize) + ax.set_xlabel(L"w", fontsize=fontsize) + plt.show() + if savefig + fig.savefig(figname) + end +end + diff --git a/code/jl/markov_js_with_sep.jl b/code/jl/markov_js_with_sep.jl new file mode 100644 index 0000000..7bf63c1 --- /dev/null +++ b/code/jl/markov_js_with_sep.jl @@ -0,0 +1,127 @@ +""" +Infinite-horizon job search with Markov wage draws and separation. + +""" + +include("s_approx.jl") +using QuantEcon, LinearAlgebra + +"Creates an instance of the job search model with separation." +function create_js_with_sep_model(; + n=200, # wage grid size + ρ=0.9, ν=0.2, # wage persistence and volatility + β=0.98, α=0.1, # discount factor and separation rate + c=1.0) # unemployment compensation + mc = tauchen(n, ρ, ν) + w_vals, P = exp.(mc.state_values), mc.p + return (; n, w_vals, P, β, c, α) +end + +" The Bellman operator for the value of being unemployed." +function T(v, model) + (; n, w_vals, P, β, c, α) = model + d = 1 / (1 - β * (1 - α)) + accept = d * (w_vals + α * β * P * v) + reject = c .+ β * P * v + return max.(accept, reject) +end + +" Get a v-greedy policy." +function get_greedy(v, model) + (; n, w_vals, P, β, c, α) = model + d = 1 / (1 - β * (1 - α)) + accept = d * (w_vals + α * β * P * v) + reject = c .+ β * P * v + σ = accept .>= reject + return σ +end + +"Solve by VFI." +function vfi(model) + v_init = zero(model.w_vals) + v_star = successive_approx(v -> T(v, model), v_init) + σ_star = get_greedy(v_star, model) + return v_star, σ_star +end + + +# == Plots == # + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=16 + +default_model = create_js_with_sep_model() + + +function plot_main(; model=default_model, + method="vfi", + savefig=false, + figname="./figures/markov_js_with_sep_1.pdf") + (; n, w_vals, P, β, c, α) = model + v_star, σ_star = vfi(model) + + d = 1 / (1 - β * (1 - α)) + accept = d * (w_vals + α * β * P * v_star) + h_star = c .+ β * P * v_star + + w_star = Inf + for (i, w) in enumerate(w_vals) + if accept[i] ≥ h_star[i] + w_star = w + break + end + end + @assert w_star < Inf "Agent never accepts" + + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(w_vals, h_star, lw=4, ls="--", alpha=0.4, label="continuation value") + ax.plot(w_vals, accept, lw=4, ls="--", alpha=0.4, label="stopping value") + ax.plot(w_vals, v_star, "k-", alpha=0.7, label=L"v_u^*(w)") + ax.legend(frameon=false, fontsize=fontsize) + ax.set_xlabel(L"w", fontsize=fontsize) + plt.show() + if savefig + fig.savefig(figname) + end +end + +function plot_w_stars(; α_vals=LinRange(0.0, 1.0, 10), + savefig=false, + figname="./figures/markov_js_with_sep_2.pdf") + + w_star_vec = similar(α_vals) + for (i_α, α) in enumerate(α_vals) + print(i_α, α) + model = create_js_with_sep_model(α=α) + (; n, w_vals, P, β, c, α) = model + v_star, σ_star = vfi(model) + + d = 1 / (1 - β * (1 - α)) + accept = d * (w_vals + α * β * P * v_star) + h_star = c .+ β * P * v_star + + w_star = Inf + for (i_w, w) in enumerate(w_vals) + if accept[i_w] ≥ h_star[i_w] + w_star = w + break + end + end + @assert w_star < Inf "Agent never accepts" + w_star_vec[i_α] = w_star + end + + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(α_vals, w_star_vec, lw=2, alpha=0.6, label="reservation wage") + ax.legend(frameon=false, fontsize=fontsize) + ax.set_xlabel(L"\alpha", fontsize=fontsize) + ax.set_xlabel(L"w", fontsize=fontsize) + plt.show() + if savefig + fig.savefig(figname) + end +end + + diff --git a/code/jl/modified_opt_savings.jl b/code/jl/modified_opt_savings.jl new file mode 100644 index 0000000..e074e32 --- /dev/null +++ b/code/jl/modified_opt_savings.jl @@ -0,0 +1,301 @@ +using QuantEcon, LinearAlgebra, IterTools + +function create_savings_model(; β=0.98, γ=2.5, + w_min=0.01, w_max=20.0, w_size=100, + ρ=0.9, ν=0.1, y_size=20, + η_min=0.75, η_max=1.25, η_size=2) + η_grid = LinRange(η_min, η_max, η_size) + ϕ = ones(η_size) * (1 / η_size) # Uniform distributoin + w_grid = LinRange(w_min, w_max, w_size) + mc = tauchen(y_size, ρ, ν) + y_grid, Q = exp.(mc.state_values), mc.p + return (; β, γ, η_grid, ϕ, w_grid, y_grid, Q) +end + + + +## == Functions for regular OPI == ## + +""" +B(w, y, η, w′) = u(w + y - w′/η)) + β Σ v(w′, y′, η′) Q(y, y′) ϕ(η′) +""" +function B(i, j, k, l, v, model) + (; β, γ, η_grid, ϕ, w_grid, y_grid, Q) = model + w, y, η, w′ = w_grid[i], y_grid[j], η_grid[k], w_grid[l] + u(c) = c^(1-γ)/(1-γ) + c = w + y - (w′/ η) + exp_value = 0.0 + for m in eachindex(y_grid) + for n in eachindex(η_grid) + exp_value += v[l, m, n] * Q[j, m] * ϕ[n] + end + end + return c > 0 ? u(c) + β * exp_value : -Inf +end + +"The policy operator." +function T_σ(v, σ, model) + (; β, γ, η_grid, ϕ, w_grid, y_grid, Q) = model + grids = w_grid, y_grid, η_grid + w_idx, y_idx, η_idx = (eachindex(g) for g in grids) + v_new = similar(v) + for (i, j, k) in product(w_idx, y_idx, η_idx) + v_new[i, j, k] = B(i, j, k, σ[i, j, k], v, model) + end + return v_new +end + +"Compute a v-greedy policy." +function get_greedy(v, model) + (; β, γ, η_grid, ϕ, w_grid, y_grid, Q) = model + w_idx, y_idx, η_idx = (eachindex(g) for g in (w_grid, y_grid, η_grid)) + σ = Array{Int32}(undef, length(w_idx), length(y_idx), length(η_idx)) + for (i, j, k) in product(w_idx, y_idx, η_idx) + _, σ[i, j, k] = findmax(B(i, j, k, l, v, model) for l in w_idx) + end + return σ +end + + +"Optimistic policy iteration routine." +function optimistic_policy_iteration(model; tolerance=1e-5, m=100) + (; β, γ, η_grid, ϕ, w_grid, y_grid, Q) = model + v = zeros(length(w_grid), length(y_grid), length(η_grid)) + error = tolerance + 1 + while error > tolerance + last_v = v + σ = get_greedy(v, model) + for i in 1:m + v = T_σ(v, σ, model) + end + error = maximum(abs.(v - last_v)) + println("OPI current error = $error") + end + return get_greedy(v, model) +end + + +## == Functions for modified OPI == ## + +"D(w, y, η, w′, g) = u(w + y - w′/η) + β g(y, w′)." +@inline function D(i, j, k, l, g, model) + (; β, γ, η_grid, ϕ, w_grid, y_grid, Q) = model + w, y, η, w′ = w_grid[i], y_grid[j], η_grid[k], w_grid[l] + u(c) = c^(1-γ)/(1-γ) + c = w + y - (w′/η) + return c > 0 ? u(c) + β * g[j, l] : -Inf +end + + +"Compute a g-greedy policy." +function get_g_greedy(g, model) + (; β, γ, η_grid, ϕ, w_grid, y_grid, Q) = model + w_idx, y_idx, η_idx = (eachindex(g) for g in (w_grid, y_grid, η_grid)) + σ = Array{Int32}(undef, length(w_idx), length(y_idx), length(η_idx)) + for (i, j, k) in product(w_idx, y_idx, η_idx) + _, σ[i, j, k] = findmax(D(i, j, k, l, g, model) for l in w_idx) + end + return σ +end + + +"The modified policy operator." +function R_σ(g, σ, model) + (; β, γ, η_grid, ϕ, w_grid, y_grid, Q) = model + w_idx, y_idx, η_idx = (eachindex(g) for g in (w_grid, y_grid, η_grid)) + g_new = similar(g) + for (j, i′) in product(y_idx, w_idx) # j indexes y, i′ indexes w′ + out = 0.0 + for j′ in y_idx # j′ indexes y′ + for k′ in η_idx # k′ indexes η′ + out += D(i′, j′, k′, σ[i′, j′, k′], g, model) * + Q[j, j′] * ϕ[k′] + end + end + g_new[j, i′] = out + end + return g_new +end + + +"Modified optimistic policy iteration routine." +function mod_opi(model; tolerance=1e-5, m=100) + (; β, γ, η_grid, ϕ, w_grid, y_grid, Q) = model + g = zeros(length(y_grid), length(w_grid)) + error = tolerance + 1 + while error > tolerance + last_g = g + σ = get_g_greedy(g, model) + for i in 1:m + g = R_σ(g, σ, model) + end + error = maximum(abs.(g - last_g)) + println("OPI current error = $error") + end + return get_g_greedy(g, model) +end + + +# == Simulations and inequality measures == # + +function simulate_wealth(m) + + model = create_savings_model() + (; β, γ, η_grid, ϕ, w_grid, y_grid, Q) = model + σ_star = mod_opi(model) + + # Simulate labor income + mc = MarkovChain(Q) + y_idx_series = simulate(mc, m) + + # IID Markov chain with uniform draws + l = length(η_grid) + mc = MarkovChain(ones(l, l) * (1/l)) + η_idx_series = simulate(mc, m) + + w_idx_series = similar(y_idx_series) + w_idx_series[1] = 1 + for t in 1:(m-1) + i, j, k = w_idx_series[t], y_idx_series[t], η_idx_series[t] + w_idx_series[t+1] = σ_star[i, j, k] + end + + w_series = w_grid[w_idx_series] + return w_series +end + + +function lorenz(v) # assumed sorted vector + S = cumsum(v) # cumulative sums: [v[1], v[1] + v[2], ... ] + F = (1:length(v)) / length(v) + L = S ./ S[end] + return (; F, L) # returns named tuple +end + + +gini(v) = (2 * sum(i * y for (i,y) in enumerate(v))/sum(v) + - (length(v) + 1))/length(v) + + +# == Plots == # + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=16 + + + +function plot_contours(; savefig=false, + figname="./figures/modified_opt_savings_1.pdf") + + model = create_savings_model() + (; β, γ, η_grid, ϕ, w_grid, y_grid, Q) = model + σ_star = optimistic_policy_iteration(model) + + fig, axes = plt.subplots(2, 1, figsize=(10, 8)) + y_idx, η_idx = eachindex(y_grid), eachindex(η_grid) + H = zeros(length(y_grid), length(η_grid)) + + w_indices = (1, length(w_grid)) + titles = "low wealth", "high wealth" + for (ax, w_idx, title) in zip(axes, w_indices, titles) + + for (i_y, i_ϵ) in product(y_idx, η_idx) + w, y, η = w_grid[w_idx], y_grid[i_y], η_grid[i_ϵ] + H[i_y, i_ϵ] = w_grid[σ_star[w_idx, i_y, i_ϵ]] / (w+y) + end + + cs1 = ax.contourf(y_grid, η_grid, transpose(H), alpha=0.5) + plt.colorbar(cs1, ax=ax) #, format="%.6f") + + ax.set_title(title, fontsize=fontsize) + ax.set_xlabel(L"y", fontsize=fontsize) + ax.set_ylabel(L"\varepsilon", fontsize=fontsize) + end + + plt.tight_layout() + if savefig + fig.savefig(figname) + end + plt.show() +end + + +function plot_policies(; savefig=false, + figname="./figures/modified_opt_savings_2.pdf") + + model = create_savings_model() + (; β, γ, η_grid, ϕ, w_grid, y_grid, Q) = model + σ_star = mod_opi(model) + y_bar = floor(Int, length(y_grid) / 2) # Index of mid-point of y_grid + + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(w_grid, w_grid, "k--", label=L"45") + + for (i, η) in enumerate(η_grid) + label = L"\sigma^*" * " at " * L"\eta = " * "$η" + ax.plot(w_grid, w_grid[σ_star[:, y_bar, i]], label=label) + end + ax.legend(fontsize=fontsize) + plt.show() + + plt.tight_layout() + if savefig + fig.savefig(figname) + end + plt.show() +end + + +function plot_time_series(; m=2_000, + savefig=false, + figname="./figures/modified_opt_savings_ts.pdf") + + w_series = simulate_wealth(m) + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(w_series, label=L"w_t") + ax.legend(fontsize=fontsize) + ax.set_xlabel("time", fontsize=fontsize) + plt.show() + if savefig + fig.savefig(figname) + end + +end + +function plot_histogram(; m=1_000_000, + savefig=false, + figname="./figures/modified_opt_savings_hist.pdf") + + w_series = simulate_wealth(m) + g = round(gini(sort(w_series)), digits=2) + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.hist(w_series, bins=40, density=true) + ax.set_xlabel("wealth", fontsize=fontsize) + ax.text(15, 0.7, "Gini = $g", fontsize=fontsize) + plt.show() + if savefig + fig.savefig(figname) + end + +end + + +function plot_lorenz(; m=1_000_000, + savefig=false, + figname="./figures/modified_opt_savings_lorenz.pdf") + + w_series = simulate_wealth(m) + (; F, L) = lorenz(sort(w_series)) + + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(F, F, label="Lorenz curve, equality") + ax.plot(F, L, label="Lorenz curve, wealth distribution") + ax.legend() + plt.show() + if savefig + fig.savefig(figname) + end + +end diff --git a/code/jl/monopolist_adj_costs.py b/code/jl/monopolist_adj_costs.py new file mode 100644 index 0000000..2762814 --- /dev/null +++ b/code/jl/monopolist_adj_costs.py @@ -0,0 +1,84 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: light +# format_version: '1.5' +# jupytext_version: 1.13.7 +# kernelspec: +# display_name: Python 3 +# language: python +# name: python3 +# --- + +# ## Monopolist with adjustment costs + +# + +from initialize import * + +import control + +# + +# == Model parameters == # +a0 = 5 +a1 = 0.5 +σ = 0.15 +ρ = 0.9 +γ = 10 +β = 0.95 +c = 2 +T = 120 + +# == Useful constants == # +m0 = (a0-c)/(2 * a1) +m1 = 1/(2 * a1) + +# == Formulate LQ problem == # +Q = γ +R = [[ a1, -a1, 0], + [-a1, a1, 0], + [ 0, 0, 0]] +A = [[ρ, 0, m0 * (1 - ρ)], + [0, 1, 0], + [0, 0, 1]] + +B = [[0], + [1], + [0]] +C = [[m1 * σ], + [ 0], + [ 0]] +# - + +A, R = np.array(A), np.array(R) +np.linalg.matrix_rank(control.ctrb(A.T, R.T)) + +# + +lq = qe.LQ(Q, R, A, B, C=C, beta=β) + +# == Simulate state / control paths == # +x0 = (m0, 2, 1) +xp, up, wp = lq.compute_sequence(x0, ts_length=150, random_state=123) +q_bar = xp[0, :] +q = xp[1, :] + +# == Plot simulation results == # +fig, ax = plt.subplots(figsize=(10, 6.5)) + +# == Some fancy plotting stuff -- simplify if you prefer == # +bbox = (0., 1.01, 1., .101) +legend_args = {'bbox_to_anchor': bbox, 'loc': 3, 'mode': 'expand'} +p_args = {'lw': 2, 'alpha': 0.6} + +time = range(len(q)) +ax.set(xlabel='Time', xlim=(0, max(time))) +ax.plot(time, q_bar, 'k-', lw=2, alpha=0.6, label=r'$\bar q_t$') +ax.plot(time, q, 'b-', lw=2, alpha=0.6, label='$q_t$') +ax.legend(ncol=2, **legend_args) +s = f'dynamics with $\gamma = {γ}$' +ax.text(max(time) * 0.6, 1 * q_bar.max(), s, fontsize=14) +plt.show() +# - + + diff --git a/code/jl/newton.jl b/code/jl/newton.jl new file mode 100644 index 0000000..26ba00f --- /dev/null +++ b/code/jl/newton.jl @@ -0,0 +1,66 @@ +using PyPlot, LaTeXStrings +using ForwardDiff, Roots +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +x0 = 0.5 +T(x) = 1 + (x/(x + 1)) +DT = x -> ForwardDiff.derivative(T, float(x)) +T_hat(x; x0=x0) = T(x0) + DT(x0) * (x - x0) + +xs = find_zero(x -> T(x)-x, 0.5) # find fixed point of T +x1 = (T(x0) - DT(x0) * x0) / (1 - DT(x0)) + +fs = 16 + +function plot_45(; file_name="./figures/newton_1.pdf", + xmin=0.0, xmax=2.6, + savefig=false) + + xgrid = LinRange(xmin, xmax, 1000) + + fig, ax = plt.subplots() + + lb_T = L"T" + ax.plot(xgrid, T.(xgrid), lw=2, alpha=0.6, label=lb_T) + + lb_T_hat = L"\hat T" + ax.plot(xgrid, T_hat.(xgrid), lw=2, alpha=0.6, label=lb_T_hat) + + ax.plot(xgrid, xgrid, "k--", lw=1, alpha=0.7, label=L"45") + + fp1 = (x1,) + ax.plot(fp1, fp1, "go", ms=5, alpha=0.6) + + ax.plot((x0,), (T_hat(x0),) , "go", ms=5, alpha=0.6) + + ax.plot((xs,), (xs,) , "go", ms=5, alpha=0.6) + + ax.vlines((x0, xs, x1), (0, 0, 0), (T_hat(x0), xs, x1), + color="k", linestyle="-.", lw=0.4) + + + + # ax.annotate(L"k^* = (sA / \delta)^{\frac{1}{1-\alpha}}", + # xy=(kstar, kstar), + # xycoords="data", + # xytext=(20, -20), + # textcoords="offset points", + # fontsize=14) + # #arrowstyle="->") + + ax.legend(frameon=false, fontsize=fs) + + ax.set_xticks((x0, xs, x1)) + ax.set_xticklabels((L"u_0", L"u^*", L"u_1"), fontsize=fs) + ax.set_yticks((0, )) + + ax.set_xlim(0, 2.6) + ax.set_ylim(0, 2.6) + #ax.set_xlabel(L"x", fontsize=14) + + plt.show() + if savefig + fig.savefig(file_name) + end +end + diff --git a/code/jl/newton_solow.jl b/code/jl/newton_solow.jl new file mode 100644 index 0000000..274f03c --- /dev/null +++ b/code/jl/newton_solow.jl @@ -0,0 +1,120 @@ +using PyPlot +using LaTeXStrings +using ForwardDiff +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +A, s, alpha, delta = 2, 0.3, 0.3, 0.4 +x0 = 0.25 +n = 14 + +g(k) = A * s * k^alpha + (1 - delta) * k +Dg = x -> ForwardDiff.derivative(g, float(x)) +q(x) = (g(x) - Dg(x) * x) / (1 - Dg(x)) + +fs = 14 +kstar = ((s * A) / delta)^(1/(1 - alpha)) + +function plot_45(; file_name="./figures/newton_solow_45.pdf", + xmin=0.0, xmax=4, + save_fig=false) + + xgrid = LinRange(xmin, xmax, 1200) + + fig, ax = plt.subplots() + + lb_g = L"g" + ax.plot(xgrid, g.(xgrid), lw=2, alpha=0.6, label=lb_g) + + lb_q = L"Q" + ax.plot(xgrid, q.(xgrid), lw=2, alpha=0.6, label=lb_q) + + ax.plot(xgrid, xgrid, "k--", lw=1, alpha=0.7, label=L"45") + + fps = (kstar,) + ax.plot(fps, fps, "go", ms=10, alpha=0.6) + + # ax.annotate(L"k^* = (sA / \delta)^{\frac{1}{1-\alpha}}", + # xy=(kstar, kstar), + # xycoords="data", + # xytext=(20, -20), + # textcoords="offset points", + # fontsize=14) + # #arrowstyle="->") + + ax.legend(frameon=false, fontsize=14) + + #ax.set_xticks((0, 1, 2, 3)) + #ax.set_yticks((0, 1, 2, 3)) + + ax.set_xlabel(L"k_t", fontsize=14) + ax.set_ylabel(L"k_{t+1}", fontsize=14) + + ax.set_ylim(-3, 4) + ax.set_xlim(0, 4) + + plt.show() + if save_fig + fig.savefig(file_name) + end +end + + +function compute_iterates(k0, f) + k = k0 + k_iterates = [] + for t in 1:n + push!(k_iterates, k) + k = f(k) + end + return k_iterates +end + + + +function plot_trajectories(; file_name="./figures/newton_solow_traj.pdf", + save_fig=false) + + x_grid = collect(1:n) + + fig, axes = plt.subplots(2, 1) + ax1, ax2 = axes + + k0_a, k0_b = 0.8, 3.1 + + ks1 = compute_iterates(k0_a, g) + ax1.plot(x_grid, ks1, "-o", label="successive approximation") + + ks2 = compute_iterates(k0_b, g) + ax2.plot(x_grid, ks2, "-o", label="successive approximation") + + ks3 = compute_iterates(k0_a, q) + ax1.plot(x_grid, ks3, "-o", label="newton steps") + + ks4 = compute_iterates(k0_b, q) + ax2.plot(x_grid, ks4, "-o", label="newton steps") + + + for ax in axes + ax.plot(x_grid, kstar * ones(n), "k--") + ax.legend(fontsize=fs, frameon=false) + ax.set_ylim(0.6, 3.2) + xticks = (2, 4, 6, 8, 10, 12) + ax.set_xticks(xticks) + ax.set_xticklabels([string(s) for s in xticks], fontsize=fs) + ax.set_yticks((kstar,)) + ax.set_yticklabels((L"k^*",), fontsize=fs) + end + + + plt.show() + if save_fig + fig.savefig(file_name) + end +end + + + + + + + diff --git a/code/jl/optimality_illustration.jl b/code/jl/optimality_illustration.jl new file mode 100644 index 0000000..98ec806 --- /dev/null +++ b/code/jl/optimality_illustration.jl @@ -0,0 +1,74 @@ +using PyPlot, LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +fs = 18 + +xmin=-0.5 +xmax=2.0 + +xgrid = LinRange(xmin, xmax, 1000) + +a1, b1 = 0.1, 0.5 # first T_σ +a2, b2 = 0.65, 0.4 # second T_σ +#a3, b3 = 0.85, 0.2 # third T_σ + +v1 = b1/(1-a1) +v2 = b2/(1-a2) +#v3 = b3/(1-a3) + +T1 = a1 * xgrid .+ b1 +T2 = a2 * xgrid .+ b2 +#T3 = a3 * xgrid .+ b3 +#T = max.(T1, T2, T3) +T = max.(T1, T2) + +fig, ax = plt.subplots() +for spine in ["left", "bottom"] + ax.spines[spine].set_position("zero") +end +for spine in ["right", "top"] + ax.spines[spine].set_color("none") +end + +ax.plot(xgrid, xgrid, "k--", lw=1, alpha=0.7, label=L"45^{\circ}") + +ax.plot(xgrid, T1, "k-", lw=1) +ax.plot(xgrid, T2, "k-", lw=1) +#ax.plot(xgrid, T3, "k-", lw=1) + +ax.plot(xgrid, T, lw=6, alpha=0.3, color="blue", label=L"T") + +ax.plot((v1,), (v1,) , "go", ms=5, alpha=0.8) +ax.plot((v2,), (v2,) , "go", ms=5, alpha=0.8) +#ax.plot((v3,), (v3,) , "go", ms=5, alpha=0.8) +#ax.vlines((v1, v2, v3), (0, 0, 0), (v1, v2, v3), +# color="k", linestyle="-.", lw=0.4) +ax.vlines((v1, v2), (0, 0), (v1, v2), + color="k", linestyle="-.", lw=0.4) + + +ax.text(2.1, 0.6, L"T_{\sigma'}", fontsize=fs) +ax.text(2.1, 1.4, L"T_{\sigma''}", fontsize=fs) +#ax.text(2.1, 1.9, L"T_{\sigma''}", fontsize=fs) + +ax.legend(frameon=false, loc="upper center", fontsize=fs) + +#ax.set_xticks((v1, v2, v3)) +ax.set_xticks((v1, v2)) +#ax.set_xticklabels((L"v_{\sigma}", +# L"v_{\sigma'}", +# L"v_{\sigma''} = v^*"), fontsize=fs) +ax.set_xticklabels((L"v_{\sigma'}", + L"v_{\sigma''} = v^*"), fontsize=fs) +ax.set_yticks([]) + +ax.set_xlim(xmin, xmax+0.5) +ax.set_ylim(-0.2, 2) +#ax.set_xlabel(L"v", fontsize=20) +ax.text(2.4, -0.15, L"v", fontsize=22) + +plt.show() + +file_name = "./figures/optimality_illustration_1.pdf" +fig.savefig(file_name) + diff --git a/code/jl/parallel_in_julia.ipynb b/code/jl/parallel_in_julia.ipynb new file mode 100644 index 0000000..f582fa5 --- /dev/null +++ b/code/jl/parallel_in_julia.ipynb @@ -0,0 +1,288 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO: Precompiling module QuantEcon...\n" + ] + } + ], + "source": [ + "using QuantEcon" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4-element Array{Int64,1}:\n", + " 2\n", + " 3\n", + " 4\n", + " 5" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "addprocs(4)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "@everywhere using QuantEcon" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nprocs()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4-element Array{Int64,1}:\n", + " 2\n", + " 3\n", + " 4\n", + " 5" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "workers()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "@everywhere function lucas_tree_spec_rad(;beta=0.96,\n", + " gamma=2.0,\n", + " sigma=0.1,\n", + " b=0.0,\n", + " rho=0.9,\n", + " n=200)\n", + "\n", + " mc = tauchen(n, rho, sigma, b) \n", + " s = mc.state_values\n", + " J = mc.p .* exp((1 - gamma) * s)\n", + " return beta * maximum(abs(eigvals(J)))\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1.2174480318956242" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "lucas_tree_spec_rad(b=0.01)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "b_vals = collect(linspace(0.0, 0.5, 500));" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 13.756786 seconds (46.00 k allocations: 674.538 MB, 0.19% gc time)\n" + ] + } + ], + "source": [ + "out = Array(Float64, length(b_vals))\n", + "@time for i in eachindex(b_vals)\n", + " out[i] = lucas_tree_spec_rad(b=b_vals[i])\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 3.740271 seconds (6.96 k allocations: 526.687 KB)\n" + ] + }, + { + "data": { + "text/plain": [ + "4-element Array{Any,1}:\n", + " RemoteRef{Channel{Any}}(2,1,69)\n", + " RemoteRef{Channel{Any}}(3,1,70)\n", + " RemoteRef{Channel{Any}}(4,1,71)\n", + " RemoteRef{Channel{Any}}(5,1,72)" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "out_shared = SharedArray(Float64, length(b_vals))\n", + "@time @sync @parallel for i in eachindex(b_vals)\n", + " out_shared[i] = lucas_tree_spec_rad(b=b_vals[i])\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "20-element BitArray{1}:\n", + " true\n", + " true\n", + " true\n", + " true\n", + " true\n", + " true\n", + " true\n", + " true\n", + " true\n", + " true\n", + " true\n", + " true\n", + " true\n", + " true\n", + " true\n", + " true\n", + " true\n", + " true\n", + " true\n", + " true" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "out .== out_shared" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Julia 0.4.5", + "language": "julia", + "name": "julia-0.4" + }, + "language_info": { + "file_extension": ".jl", + "mimetype": "application/julia", + "name": "julia", + "version": "0.4.5" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/code/jl/pd_ratio.jl b/code/jl/pd_ratio.jl new file mode 100644 index 0000000..d1830b8 --- /dev/null +++ b/code/jl/pd_ratio.jl @@ -0,0 +1,66 @@ +""" +Price-dividend ratio in a model with dividend and consumption growth. + +""" + +using QuantEcon, LinearAlgebra + +"Creates an instance of the asset pricing model with Markov state." +function create_asset_pricing_model(; + n=200, # state grid size + ρ=0.9, ν=0.2, # state persistence and volatility + β=0.99, γ=2.5, # discount and preference parameter + μ_c=0.01, σ_c=0.02, # consumption growth mean and volatility + μ_d=0.02, σ_d=0.1) # dividend growth mean and volatility + mc = tauchen(n, ρ, ν) + x_vals, P = exp.(mc.state_values), mc.p + return (; x_vals, P, β, γ, μ_c, σ_c, μ_d, σ_d) +end + +" Build the discount matrix A. " +function build_discount_matrix(model) + (; x_vals, P, β, γ, μ_c, σ_c, μ_d, σ_d) = model + e = exp.(μ_d - γ*μ_c + (γ^2*σ_c^2 + σ_d^2)/2 .+ (1-γ)*x_vals) + return β * e .* P +end + +"Compute the price-dividend ratio associated with the model." +function pd_ratio(model) + (; x_vals, P, β, γ, μ_c, σ_c, μ_d, σ_d) = model + A = build_discount_matrix(model) + @assert maximum(abs.(eigvals(A))) < 1 "Requires r(A) < 1." + n = length(x_vals) + return (I - A) \ (A * ones(n)) +end + + +# == Plots == # + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=16 + +default_model = create_asset_pricing_model() + + +function plot_main(; μ_d_vals = (0.02, 0.08), + savefig=false, + figname="./figures/pd_ratio_1.pdf") + fig, ax = plt.subplots(figsize=(9, 5.2)) + + for μ_d in μ_d_vals + model = create_asset_pricing_model(μ_d=μ_d) + (; x_vals, P, β, γ, μ_c, σ_c, μ_d, σ_d) = model + v_star = pd_ratio(model) + ax.plot(x_vals, v_star, lw=2, alpha=0.6, label=L"\mu_d="*"$μ_d") + end + + ax.legend(frameon=false, fontsize=fontsize) + ax.set_xlabel(L"x", fontsize=fontsize) + plt.show() + if savefig + fig.savefig(figname) + end +end + diff --git a/code/jl/plot_interest_rates.jl b/code/jl/plot_interest_rates.jl new file mode 100644 index 0000000..70d698f --- /dev/null +++ b/code/jl/plot_interest_rates.jl @@ -0,0 +1,23 @@ +# Nominal interest rate from https://fred.stlouisfed.org/series/GS1 +# Real interest rate from https://fred.stlouisfed.org/series/WFII10 +# +# Download as CSV files +# + +using DataFrames, CSV, PyPlot + +df_nominal = DataFrame(CSV.File("data/GS1.csv")) +df_real = DataFrame(CSV.File("data/WFII10.csv")) + +function plot_rates(df; fontsize=16, savefig=true) + r_type = df == df_nominal ? "nominal" : "real" + fig, ax = plt.subplots(figsize=(9, 5)) + ax.plot(df[!, 1], df[!, 2], label=r_type*" interest rate") + ax.plot(df[!, 1], zero(df[!, 2]), c="k", ls="--") + ax.set_xlim(df[1, 1], df[end, 1]) + ax.legend(fontsize=fontsize, frameon=false) + plt.show() + if savefig + fig.savefig("./figures/plot_interest_rates_"*r_type*".pdf") + end +end diff --git a/code/jl/power_series.jl b/code/jl/power_series.jl new file mode 100644 index 0000000..5752575 --- /dev/null +++ b/code/jl/power_series.jl @@ -0,0 +1,22 @@ +using LinearAlgebra + +# Primitives +A = [0.4 0.1; + 0.7 0.2] + +# Method one: direct inverse +B_inverse = inv(I - A) + +# Method two: power series +function power_series(A) + B_sum = zeros((2, 2)) + A_power = I + for k in 1:50 + B_sum += A_power + A_power = A_power * A + end + return B_sum +end + +# Print maximal error +print(maximum(abs.(B_inverse - power_series(A)))) diff --git a/code/jl/quantile_function.jl b/code/jl/quantile_function.jl new file mode 100644 index 0000000..a51823d --- /dev/null +++ b/code/jl/quantile_function.jl @@ -0,0 +1,29 @@ +import Distributions.quantile, Distributions.DiscreteNonParametric + +"Compute the τ-th quantile of v(X) when X ∼ ϕ and v = sort(v)." +function quantile(τ, v, ϕ) + for (i, v_value) in enumerate(v) + p = sum(ϕ[1:i]) # sum all ϕ[j] s.t. v[j] ≤ v_value + if p ≥ τ # exit and return v_value if prob ≥ τ + return v_value + end + end + +end + +"For each i, compute the τ-th quantile of v(Y) when Y ∼ P(i, ⋅)" +function R(τ, v, P) + return [quantile(τ, v, P[i, :]) for i in eachindex(v)] +end + + +function quantile_test(τ) + ϕ = [0.1, 0.2, 0.7] + v = [10, 20, 30] + + d = DiscreteNonParametric(v, ϕ) + return quantile(τ, v, ϕ), quantile(d, τ) +end + + + diff --git a/code/jl/quantile_js.jl b/code/jl/quantile_js.jl new file mode 100644 index 0000000..bd1ec47 --- /dev/null +++ b/code/jl/quantile_js.jl @@ -0,0 +1,106 @@ +""" +Job search with Markov wage draws and quantile preferences. + +""" + +using QuantEcon +include("quantile_function.jl") + +"Creates an instance of the job search model." +function create_markov_js_model(; + n=100, # wage grid size + ρ=0.9, # wage persistence + ν=0.2, # wage volatility + β=0.98, # discount factor + c=1.0, # unemployment compensation + τ=0.5 # quantile parameter + ) + mc = tauchen(n, ρ, ν) + w_vals, P = exp.(mc.state_values), mc.p + return (; n, w_vals, P, β, c, τ) +end + +""" +The policy operator + + (T_σ v)(w) = σ(w) (w / (1-β)) + (1 - σ(w))(c + β (R_τ v)(w)) + +""" +function T_σ(v, σ, model) + (; n, w_vals, P, β, c, τ) = model + h = c .+ β * R(τ, v, P) + e = w_vals ./ (1 - β) + return σ .* e + (1 .- σ) .* h +end + +" Get a v-greedy policy." +function get_greedy(v, model) + (; n, w_vals, P, β, c, τ) = model + σ = w_vals / (1 - β) .≥ c .+ β * R(τ, v, P) + return σ +end + + +"Optimistic policy iteration routine." +function optimistic_policy_iteration(model; tolerance=1e-5, m=100) + (; n, w_vals, P, β, c, τ) = model + v = ones(n) + error = tolerance + 1 + while error > tolerance + last_v = v + σ = get_greedy(v, model) + for i in 1:m + v = T_σ(v, σ, model) + end + error = maximum(abs.(v - last_v)) + println("OPI current error = $error") + end + return v, get_greedy(v, model) +end + + + +# == Plots == # + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=16 + + +function plot_main(; tau_vals=(0.1, 0.25, 0.5, 0.6, 0.7, 0.8), + savefig=false, + figname="./figures/quantile_js.pdf") + + w_star_vals = zeros(length(tau_vals)) + + for (τ_idx, τ) in enumerate(tau_vals) + model = create_markov_js_model(τ=τ) + (; n, w_vals, P, β, c, τ) = model + v_star, σ_star = optimistic_policy_iteration(model) + for i in 1:n + if σ_star[i] > 0 + w_star_vals[τ_idx] = w_vals[i] + break + end + end + end + + model = create_markov_js_model() + (; n, w_vals, P, β, c, τ) = model + mc = MarkovChain(model.P) + s = stationary_distributions(mc)[1] + + fig, ax = plt.subplots() + ax.plot(tau_vals, w_star_vals, "k--", lw=2, alpha=0.7, label="reservation wage") + ax.barh(w_vals, 32 * s, alpha=0.05, align="center") + ax.legend(frameon=false, fontsize=fontsize, loc="upper center") + ax.set_xlabel("quantile", fontsize=fontsize) + ax.set_ylabel("wages", fontsize=fontsize) + + plt.show() + if savefig + fig.savefig(figname) + end +end + diff --git a/code/jl/random_walk.jl b/code/jl/random_walk.jl new file mode 100644 index 0000000..6f84511 --- /dev/null +++ b/code/jl/random_walk.jl @@ -0,0 +1,23 @@ +using StatsBase + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=16 + +fig, ax = plt.subplots(figsize=(9, 5.2)) + +n, m = 100, 12 +cols = ("k-", "b-", "g-") + +for i in 1:m + s = cols[rand(1:3)] + ax.plot(cumsum(randn(n)), s, alpha=0.5) +end + +ax.set_xlabel("time") +plt.show() + +fig.savefig("./figures/random_walk_1.pdf") + + diff --git a/code/jl/risk_sensitive_js.jl b/code/jl/risk_sensitive_js.jl new file mode 100644 index 0000000..e03b0ad --- /dev/null +++ b/code/jl/risk_sensitive_js.jl @@ -0,0 +1,93 @@ +""" +Infinite-horizon job search with Markov wage draws and risk-sensitive preferences. + +""" + +using QuantEcon, LinearAlgebra +include("s_approx.jl") + +"Creates an instance of the job search model with Markov wages." +function create_markov_js_model(; + n=200, # wage grid size + ρ=0.9, # wage persistence + ν=0.2, # wage volatility + β=0.98, # discount factor + c=1.0, # unemployment compensation + θ=-0.01 # risk parameter + ) + mc = tauchen(n, ρ, ν) + w_vals, P = exp.(mc.state_values), mc.p + return (; n, w_vals, P, β, c, θ) +end + +""" +The Bellman operator Tv = max{e, c + β R v} with + + e(w) = w / (1-β) and + + (Rv)(w) = (1/θ) ln{E_w[ exp(θ v(W'))]} + +""" + +function T(v, model) + (; n, w_vals, P, β, c, θ) = model + h = c .+ (β / θ) * log.(P * (exp.(θ * v))) + e = w_vals ./ (1 - β) + return max.(e, h) +end + +" Get a v-greedy policy." +function get_greedy(v, model) + (; n, w_vals, P, β, c, θ) = model + σ = w_vals / (1 - β) .>= c .+ (β / θ) * log.(P * (exp.(θ * v))) + return σ +end + +"Solve the infinite-horizon Markov job search model by VFI." +function vfi(model) + v_init = zero(model.w_vals) + v_star = successive_approx(v -> T(v, model), v_init) + σ_star = get_greedy(v_star, model) + return v_star, σ_star +end + + + +# == Plots == # + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=16 + + +function plot_main(; theta_vals=(-10, 0.0001, 0.1), + savefig=false, + figname="./figures/risk_sensitive_js.pdf") + + fig, axes = plt.subplots(length(theta_vals), 1, figsize=(9, 22)) + + for (θ, ax) in zip(theta_vals, axes) + model = create_markov_js_model(θ=θ) + (; n, w_vals, P, β, c, θ) = model + v_star, σ_star = vfi(model) + + h_star = c .+ (β / θ) * log.(P * (exp.(θ * v_star))) + e = w_vals / (1 - β) + + ax.plot(w_vals, h_star, lw=4, ls="--", alpha=0.4, label=L"h^*(w)") + ax.plot(w_vals, e, lw=4, ls="--", alpha=0.4, label=L"w/(1-\beta)") + ax.plot(w_vals, max.(e, h_star), "k-", alpha=0.7, label=L"v^*(w)") + ax.set_title(L"\theta = " * "$θ", fontsize=fontsize) + ax.legend(frameon=false, fontsize=fontsize) + ax.set_xlabel(L"w", fontsize=fontsize) + end + + fig.tight_layout() + plt.show() + + if savefig + fig.savefig(figname) + end +end + diff --git a/code/jl/rs_utility.jl b/code/jl/rs_utility.jl new file mode 100644 index 0000000..e06dced --- /dev/null +++ b/code/jl/rs_utility.jl @@ -0,0 +1,84 @@ +include("s_approx.jl") +using LinearAlgebra, QuantEcon + +function create_rs_utility_model(; + n=180, # size of state space + β=0.95, # time discount factor + ρ=0.96, # correlation coef in AR(1) + σ=0.1, # volatility + θ=-1.0) # risk aversion + mc = tauchen(n, ρ, σ, 0, 10) # n_std = 10 + x_vals, P = mc.state_values, mc.p + r = x_vals # special case u(c(x)) = x + return (; β, θ, ρ, σ, r, x_vals, P) +end + +function K(v, model) + (; β, θ, ρ, σ, r, x_vals, P) = model + return r + (β/θ) * log.(P * (exp.(θ*v))) +end + +function compute_rs_utility(model) + (; β, θ, ρ, σ, r, x_vals, P) = model + v_init = zeros(length(x_vals)) + v_star = successive_approx(v -> K(v, model), + v_init, tolerance=1e-10) + return v_star +end + + +# Plots + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=16 + +function plot_v(; savefig=false, + figname="./figures/rs_utility_1.pdf") + + fig, ax = plt.subplots(figsize=(10, 5.2)) + model = create_rs_utility_model() + (; β, θ, ρ, σ, r, x_vals, P) = model + + a = 1/(1 - (ρ*β)) + b = (β /(1 - β)) * (θ/2) * (a*σ)^2 + + v_star = compute_rs_utility(model) + v_star_a = a * x_vals .+ b + ax.plot(x_vals, v_star, lw=2, alpha=0.7, label="approximate fixed point") + ax.plot(x_vals, v_star_a, "k--", lw=2, alpha=0.7, label=L"v(x)=ax + b") + ax.set_xlabel(L"x", fontsize=fontsize) + + ax.legend(frameon=false, fontsize=fontsize, loc="upper left") + plt.show() + if savefig + fig.savefig(figname) + end +end + + + + +function plot_multiple_v(; savefig=false, + figname="./figures/rs_utility_2.pdf") + + fig, ax = plt.subplots(figsize=(10, 5.2)) + σ_vals = 0.05, 0.1 + + for σ in σ_vals + model = create_rs_utility_model(σ=σ) + (; β, θ, r, x_vals, P) = model + v_star = compute_rs_utility(model) + ax.plot(x_vals, v_star, lw=2, alpha=0.7, label=L"\sigma="*"$σ") + ax.set_xlabel(L"x", fontsize=fontsize) + ax.set_ylabel(L"v(x)", fontsize=fontsize) + end + + ax.legend(frameon=false, fontsize=fontsize, loc="upper left") + plt.show() + if savefig + fig.savefig(figname) + end +end + diff --git a/code/jl/s_approx.jl b/code/jl/s_approx.jl new file mode 100644 index 0000000..a9c8595 --- /dev/null +++ b/code/jl/s_approx.jl @@ -0,0 +1,35 @@ +""" +Computes an approximate fixed point of a given operator T +via successive approximation. + +""" +function successive_approx(T, # operator (callable) + u_0; # initial condition + tolerance=1e-6, # error tolerance + max_iter=10_000, # max iteration bound + print_step=25) # print at multiples + u = u_0 + error = Inf + k = 1 + + while (error > tolerance) & (k <= max_iter) + + u_new = T(u) + error = maximum(abs.(u_new - u)) + + if k % print_step == 0 + println("Completed iteration $k with error $error.") + end + + u = u_new + k += 1 + end + + if error <= tolerance + println("Terminated successfully in $k iterations.") + else + println("Warning: hit iteration bound.") + end + + return u +end diff --git a/code/jl/solow_fp.jl b/code/jl/solow_fp.jl new file mode 100644 index 0000000..a123731 --- /dev/null +++ b/code/jl/solow_fp.jl @@ -0,0 +1,72 @@ +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +x0 = 0.25 +xmin, xmax = 0, 3 + + +k_grid = LinRange(xmin, xmax, 1200) + +function plot_45(ax; k0=0.5, + A=2.0, s=0.3, alpha=0.3, delta=0.4, + fs=16, # font size + num_arrows=8) + + # Define the function and the fixed point + g(k) = A * s * k^alpha + (1 - delta) * k + kstar = ((s * A) / delta)^(1/(1 - alpha)) + + # Plot the functions + lb = L"g(k) = sAk^{\alpha} + (1 - \delta)k" + ax.plot(k_grid, g.(k_grid), lw=2, alpha=0.6, label=lb) + ax.plot(k_grid, k_grid, "k--", lw=1, alpha=0.7, label=L"45") + + # Show and annotate the fixed point + fps = (kstar,) + ax.plot(fps, fps, "go", ms=10, alpha=0.6) + ax.annotate(L"k^* = (sA / \delta)^{\frac{1}{1-\alpha}}", + xy=(kstar, kstar), + xycoords="data", + xytext=(20, -20), + textcoords="offset points", + fontsize=fs) + #arrowstyle="->") + + # Draw the arrow sequence + + arrow_args = Dict(:fc=>"k", :ec=>"k", :head_width=>0.03, + :length_includes_head=>true, :lw=>1, + :alpha=>0.6, :head_length=>0.03) + + k = k0 + for i in 1:num_arrows + ax.arrow(k, k, 0.0, g(k)-k; arrow_args...) # x, y, dx, dy + ax.arrow(k, g(k), g(k) - k, 0; arrow_args...) + k = g(k) + end + + + ax.legend(loc="upper left", frameon=false, fontsize=fs) + + ax.set_xticks((0, k0, 3)) + ax.set_xticklabels((0, L"k_0", 3), fontsize=fs) + ax.set_yticks((0, 1, 2, 3)) + ax.set_yticklabels((0, 1, 2, 3), fontsize=fs) + ax.set_ylim(0, 3) + ax.set_xlabel(L"k_t", fontsize=fs) + ax.set_ylabel(L"k_{t+1}", fontsize=fs) + +end + + + +fig, ax = plt.subplots() + +plot_45(ax; A=2.0, s=0.3, alpha=0.4, delta=0.4) +#plot_45(ax2; A=3.0, s=0.4, alpha=0.05, delta=0.6) +fig.tight_layout() +plt.show() +#fig.savefig("./figures/solow_fp.pdf") + + diff --git a/code/jl/solow_fp_adjust.jl b/code/jl/solow_fp_adjust.jl new file mode 100644 index 0000000..7dd3cd8 --- /dev/null +++ b/code/jl/solow_fp_adjust.jl @@ -0,0 +1,65 @@ +using PyPlot, LinearAlgebra +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +function subplots(fs) + "Custom subplots with axes throught the origin" + fig, ax = plt.subplots(2, 2, figsize=fs) + + for i in 1:2 + for j in 1:2 + # Set the axes through the origin + for spine in ["left", "bottom"] + ax[i,j].spines[spine].set_position("zero") + ax[i,j].spines[spine].set_color("black") + end + for spine in ["right", "top"] + ax[i,j].spines[spine].set_color("none") + end + end + end + + return fig, ax +end + +# Create parameter sets where we adjust A, s, and delta +A = [2, 2.5, 2, 2] +s = [.3, .3, .2, .3] +alpha = [.3, .3, .3, .3] +delta = [.4, .4, .4, .6] +x0 = 0.25 +num_arrows = 8 +ts_length = 12 +xmin, xmax = 0, 3 +g(k, s, A, delta, alpha) = A * s * k^alpha + (1 - delta) * k +kstar(s, A, delta, alpha) = ((s * A) / delta)^(1/(1 - alpha)) + +xgrid = LinRange(xmin, xmax, 120) + +fig, ax = subplots((10, 7)) +# (0,0) is the default parameters +# (0,1) increases A +# (1,0) decreases s +# (1,1) increases delta + +lb = ["default", L"$A=2.5$", L"$s=.2$", L"$\delta=.6$"] +count = 1 +for i in 1:2 + for j in 1:2 + ax[i,j].set_xlim(xmin, xmax) + ax[i,j].set_ylim(xmin, xmax) + ax[i,j].plot(xgrid, g.(xgrid, s[count], A[count], delta[count], alpha[count]), + "b-", lw=2, alpha=0.6, label=lb[count]) + ks = kstar(s[count], A[count], delta[count], alpha[count]) + ax[i,j].plot(ks, ks, "go") + ax[i,j].plot(xgrid, xgrid, "k-", lw=1, alpha=0.7) + global count += 1 + ax[i,j].legend(loc="lower right", frameon=false, fontsize=14) + end +end + +plt.show() + +fig.savefig("./figures/solow_fp_adjust.pdf") + + diff --git a/code/jl/stoch_dom_finite.jl b/code/jl/stoch_dom_finite.jl new file mode 100644 index 0000000..3e5060d --- /dev/null +++ b/code/jl/stoch_dom_finite.jl @@ -0,0 +1,17 @@ +using Distributions +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +p, q = 0.75, 0.25 +fig, axes = plt.subplots(1, 2, figsize=(10, 5.2)) +ax = axes[1] +ax.bar(1:2, (p, 1-p), label=L"\phi") + +ax = axes[2] +ax.bar(1:2, (q, 1-q), label=L"\psi") + +ax.legend() +plt.show() + + diff --git a/code/jl/tauchen.jl b/code/jl/tauchen.jl new file mode 100644 index 0000000..5a22a32 --- /dev/null +++ b/code/jl/tauchen.jl @@ -0,0 +1,37 @@ +using QuantEcon + +ρ, b, ν = 0.9, 0.0, 1.0 +μ_x = b/(1-ρ) +σ_x = sqrt(ν^2 / (1-ρ^2)) + +n = 15 +mc = tauchen(n, ρ, ν) +approx_sd = stationary_distributions(mc)[1] + +function psi_star(y) + c = 1/(sqrt(2 * pi) * σ_x) + return c * exp(-(y - μ_x)^2 / (2 * σ_x^2)) +end + +# == Plots == # +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=14 + +fig, ax = plt.subplots() +ax.bar(mc.state_values, approx_sd, + fill=true, width=0.6, alpha=0.6, + label="approximation") + +x_grid = LinRange(minimum(mc.state_values) - 2, + maximum(mc.state_values) + 2, 100) +ax.plot(x_grid, psi_star.(x_grid), + "-k", lw=2, alpha=0.6, label=L"\psi^*") +ax.legend(fontsize=fontsize) +if false + plt.savefig("./figures/tauchen_1.pdf") +end +plt.show() + + diff --git a/code/jl/three_fixed_points.jl b/code/jl/three_fixed_points.jl new file mode 100644 index 0000000..0f11e64 --- /dev/null +++ b/code/jl/three_fixed_points.jl @@ -0,0 +1,42 @@ +using PyPlot, LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +fs = 18 + +xmin, xmax = 0.0000001, 2.0 + +g(x) = 2.125 / (1 + x^(-4)) + +xgrid = LinRange(xmin, xmax, 200) + +fig, ax = plt.subplots(figsize=(6.5, 6)) + +ax.set_xlim(xmin, xmax) +ax.set_ylim(xmin, xmax) + +ax.plot(xgrid, g.(xgrid), "b-", lw=2, alpha=0.6, label=L"G") +ax.plot(xgrid, xgrid, "k-", lw=1, alpha=0.7, label=L"45^o") + +ax.legend(fontsize=14) + +fps = (0.01, 0.94, 1.98) +fps_labels = (L"x_\ell", L"x_m", L"x_h" ) +coords = ((40, 80), (40, -40), (-40, -80)) + +ax.plot(fps, fps, "ro", ms=8, alpha=0.6) + +for (fp, lb, coord) in zip(fps, fps_labels, coords) + ax.annotate(lb, + xy=(fp, fp), + xycoords="data", + xytext=coord, + textcoords="offset points", + fontsize=16, + arrowprops=Dict("arrowstyle"=>"->")) +end + +#plt.savefig("./figures/three_fixed_points.pdf") + +plt.show() + + diff --git a/code/jl/two_period_job_search.jl b/code/jl/two_period_job_search.jl new file mode 100644 index 0000000..df1cc2b --- /dev/null +++ b/code/jl/two_period_job_search.jl @@ -0,0 +1,85 @@ +""" +Two period job search in the IID case. +""" + +using Distributions + +"Creates an instance of the job search model, stored as a NamedTuple." +function create_job_search_model(; + n=50, # wage grid size + w_min=10.0, # lowest wage + w_max=60.0, # highest wage + a=200, # wage distribution parameter + b=100, # wage distribution parameter + β=0.96, # discount factor + c=10.0 # unemployment compensation + ) + w_vals = collect(LinRange(w_min, w_max, n+1)) + ϕ = pdf(BetaBinomial(n, a, b)) + return (; n, w_vals, ϕ, β, c) +end + +" Computes lifetime value at t=1 given current wage w_1 = w. " +function v_1(w, model) + (; n, w_vals, ϕ, β, c) = model + h_1 = c + β * max.(c, w_vals)'ϕ + return max(w + β * w, h_1) +end + +" Computes reservation wage at t=1. " +function res_wage(model) + (; n, w_vals, ϕ, β, c) = model + h_1 = c + β * max.(c, w_vals)'ϕ + return h_1 / (1 + β) +end + + +# == Plots == # + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +default_model = create_job_search_model() + +" Plot the distribution of wages. " +function fig_dist(model=default_model, fs=14) + fig, ax = plt.subplots() + ax.plot(model.w_vals, model.ϕ, "-o", alpha=0.5, label="wage distribution") + ax.legend(loc="upper left", fontsize=fs) + plt.show() +end + + +" Plot two-period value function and res wage. " +function fig_v1(model=default_model; savefig=false, + figname="./figures/iid_job_search_0.pdf", fs=18) + + (; n, w_vals, ϕ, β, c) = model + + v = [v_1(w, model) for w in model.w_vals] + w_star = res_wage(model) + continuation_val = c + β * max.(c, w_vals)'ϕ + min_w, max_w = minimum(w_vals), maximum(w_vals) + + fig, ax = plt.subplots(figsize=(9, 5.5)) + ax.set_ylim(0, 120) + ax.set_xlim(min_w, max_w) + ax.vlines((w_star,), (0,), (continuation_val,), lw=0.5) + ax.set_yticks((0, 50, 100)) + ax.set_yticklabels((0, 50, 100), fontsize=12) + ax.set_xticks((min_w, w_star, max_w)) + ax.set_xticklabels((min_w, L"$w^*_1$", max_w), fontsize=12) + ax.plot(w_vals, w_vals + β * w_vals, "-", alpha=0.8, lw=3, + label=L"$w_1 + \beta w_1$") + ax.plot(w_vals, fill(continuation_val, n+1), lw=3, alpha=0.8, + label=L"$c + \beta \sum_{w'} \max\{c, w'\} \varphi(w')$" ) + ax.plot(w_vals, v, "k--", ms=2, alpha=1.0, lw=2, label=L"$v_1(w_1)$") + ax.legend(frameon=false, fontsize=fs, loc="upper left") + if savefig + fig.savefig(figname) + end + plt.show() +end + + diff --git a/code/jl/up_down_stable.jl b/code/jl/up_down_stable.jl new file mode 100644 index 0000000..3dc459e --- /dev/null +++ b/code/jl/up_down_stable.jl @@ -0,0 +1,52 @@ +using PyPlot, LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +fs = 20 + +xmin, xmax = 0., 1.0 + +g(x) = 0.2 + 0.6 * x^(1.2) + +xgrid = LinRange(xmin, xmax, 200) + +fig, ax = plt.subplots(figsize=(8.0, 6)) +for spine in ["left", "bottom"] + ax.spines[spine].set_position("zero") +end +for spine in ["right", "top"] + ax.spines[spine].set_color("none") +end + +ax.set_xlim(xmin, xmax) +ax.set_ylim(xmin, xmax) + +ax.plot(xgrid, g.(xgrid), "b-", lw=2, alpha=0.6, label=L"T") +ax.plot(xgrid, xgrid, "k-", lw=1, alpha=0.7, label=L"45^o") + +ax.legend(frameon=false, fontsize=fs) + +fp = (0.4,) +fps_label = L"\bar v" +coords = (40, -20) + +ax.plot(fp, fp, "ro", ms=8, alpha=0.6) + +ax.set_xticks([0, 1]) +ax.set_xticklabels([L"0", L"1"], fontsize=fs) +ax.set_yticks([]) + +ax.set_xlabel(L"V", fontsize=fs) + +ax.annotate(fps_label, + xy=(fp[1], fp[1]), + xycoords="data", + xytext=coords, + textcoords="offset points", + fontsize=fs, + arrowprops=Dict("arrowstyle"=>"->")) + +plt.savefig("./figures/up_down_stable.pdf") + +plt.show() + + diff --git a/code/jl/v_star_illus.jl b/code/jl/v_star_illus.jl new file mode 100644 index 0000000..d952f3b --- /dev/null +++ b/code/jl/v_star_illus.jl @@ -0,0 +1,49 @@ +using PyPlot, LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering + +fs = 18 + +xmin=0.01 +xmax=2.0 + +xgrid = LinRange(xmin, xmax, 1000) + + +v1 = xgrid.^0.7 +v2 = xgrid.^0.1 .+ .05 +v = max.(v1, v2) + +fig, ax = plt.subplots() + +for spine in ["left", "bottom"] + ax.spines[spine].set_position("zero") +end +for spine in ["right", "top"] + ax.spines[spine].set_color("none") +end + +ax.plot(xgrid, v1, "k-", lw=1) +ax.plot(xgrid, v2, "k-", lw=1) + +ax.plot(xgrid, v, lw=6, alpha=0.3, color="blue", + label=L"v^* = \bigvee_{\sigma \in \Sigma} v_\sigma") + +ax.text(2.1, 1.1, L"v_{\sigma'}", fontsize=fs) +ax.text(2.1, 1.6, L"v_{\sigma''}", fontsize=fs) + +ax.text(1.2, 0.3, L"\Sigma = \{\sigma', \sigma''\}", fontsize=fs) + +ax.legend(frameon=false, loc="upper left", fontsize=fs) + +ax.set_xlim(xmin, xmax+0.5) +ax.set_ylim(0.0, 2) +ax.text(2.4, -0.15, L"x", fontsize=20) + +ax.set_xticks([]) +ax.set_yticks([]) + +plt.show() + +file_name = "./figures/v_star_illus.pdf" +fig.savefig(file_name) + diff --git a/code/jl/val_consumption.jl b/code/jl/val_consumption.jl new file mode 100644 index 0000000..a8e172a --- /dev/null +++ b/code/jl/val_consumption.jl @@ -0,0 +1,36 @@ +using LinearAlgebra, QuantEcon + +function compute_v(; n=25, # Size of state space + β=0.98, # Time discount factor + ρ=0.96, # Correlation coef in AR(1) + ν=0.05, # Volatility + γ=2.0) # Preference parameter + mc = tauchen(n, ρ, ν) + x_vals = mc.state_values + P = mc.p + r = exp.((1 - γ) * x_vals) / (1 - γ) # r(x) = u(exp(x)) + v = (I - β*P) \ r + return x_vals, v +end + +# Plots + +using PyPlot +using LaTeXStrings +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering +fontsize=16 + +function plot_v(; savefig=false, + figname="./figures/val_consumption_1.pdf") + fig, ax = plt.subplots(figsize=(10, 5.2)) + x_vals, v = compute_v() + ax.plot(x_vals, v, lw=2, alpha=0.7, label=L"v") + ax.set_xlabel(L"x", fontsize=fontsize) + ax.legend(frameon=false, fontsize=fontsize, loc="upper left") + ax.set_ylim(-65, -40) + plt.show() + if savefig + fig.savefig(figname) + end +end + diff --git a/code/py/american_option.py b/code/py/american_option.py new file mode 100644 index 0000000..5859cac --- /dev/null +++ b/code/py/american_option.py @@ -0,0 +1,163 @@ +""" +Valuation for finite-horizon American call options in discrete time. + +""" + +from quantecon.markov import tauchen, MarkovChain +from quantecon import compute_fixed_point + +import numpy as np +from collections import namedtuple +from numba import njit, prange + + +# NamedTuple Model +Model = namedtuple("Model", ("t_vals", "z_vals","w_vals", "Q", + "φ", "T", "β", "K")) + + +@njit +def exit_reward(t, i_w, i_z, T, K): + """Payoff to exercising the option at time t.""" + return (t < T) * (i_z + i_w - K) + + +def create_american_option_model( + n=100, μ=10.0, # Markov state grid size and mean value + ρ=0.98, ν=0.2, # persistence and volatility for Markov state + s=0.3, # volatility parameter for W_t + r=0.01, # interest rate + K=10.0, T=200): # strike price and expiration date + """ + Creates an instance of the option model with log S_t = Z_t + W_t. + """ + t_vals = np.arange(T+1) + mc = tauchen(n, ρ, ν) + z_vals, Q = mc.state_values + μ, mc.P + w_vals, φ, β = np.array([-s, s]), np.array([0.5, 0.5]), 1 / (1 + r) + return Model(t_vals=t_vals, z_vals=z_vals, w_vals=w_vals, Q=Q, + φ=φ, T=T, β=β, K=K) + + +@njit(parallel=True) +def C(h, model): + """The continuation value operator.""" + t_vals, z_vals, w_vals, Q, φ, T, β, K = model + Ch = np.empty_like(h) + z_idx, w_idx = np.arange(len(z_vals)), np.arange(len(w_vals)) + for i in prange(len(t_vals)): + t = t_vals[i] + for i_z in prange(len(z_vals)): + out = 0.0 + for i_w_1 in prange(len(w_vals)): + for i_z_1 in prange(len(z_vals)): + t_1 = min(t + 1, T) + out += max(exit_reward(t_1, w_vals[i_w_1], z_vals[i_z_1], T, K), + h[t_1, i_z_1]) * Q[i_z, i_z_1] * φ[i_w_1] + Ch[t, i_z] = β * out + + return Ch + + +def compute_cvf(model): + """ + Compute the continuation value function by successive approx. + """ + h_init = np.zeros((len(model.t_vals), len(model.z_vals))) + h_star = compute_fixed_point(lambda h: C(h, model), h_init, + error_tol=1e-6, max_iter=1000, + print_skip=25) + return h_star + + +# Plots + + +import matplotlib.pyplot as plt + + +def plot_contours(savefig=False, + figname="./figures/american_option_1.pdf"): + + model = create_american_option_model() + t_vals, z_vals, w_vals, Q, φ, T, β, K = model + h_star = compute_cvf(model) + fig, axes = plt.subplots(3, 1, figsize=(7, 11)) + z_idx, w_idx = np.arange(len(z_vals)), np.arange(len(w_vals)) + H = np.zeros((len(w_vals), len(z_vals))) + for (ax_index, t) in zip(range(3), (1, 195, 198)): + + ax = axes[ax_index] + for i_w in prange(len(w_vals)): + for i_z in prange(len(z_vals)): + H[i_w, i_z] = exit_reward(t, w_vals[i_w], z_vals[i_z], T, + K) - h_star[t, i_z] + + cs1 = ax.contourf(w_vals, z_vals, np.transpose(H), alpha=0.5) + ctr1 = ax.contour(w_vals, z_vals, np.transpose(H), levels=[0.0]) + plt.clabel(ctr1, inline=1, fontsize=13) + plt.colorbar(cs1, ax=ax) + + ax.set_title(f"$t={t}$") + ax.set_xlabel(r"$w$") + ax.set_ylabel(r"$z$") + + fig.tight_layout() + if savefig: + fig.savefig(figname) + plt.show() + + +def plot_strike(savefig=False, + fontsize=9, + figname="./figures/american_option_2.pdf"): + model = create_american_option_model() + t_vals, z_vals, w_vals, Q, φ, T, β, K = model + h_star = compute_cvf(model) + + # Built Markov chains for simulation + z_mc = MarkovChain(Q, z_vals) + P_φ = np.zeros((len(w_vals), len(w_vals))) + for i in range(len(w_vals)): # Build IID chain + P_φ[i, :] = φ + w_mc = MarkovChain(P_φ, w_vals) + y_min = np.min(z_vals) + np.min(w_vals) + y_max = np.max(z_vals) + np.max(w_vals) + fig, axes = plt.subplots(3, 1, figsize=(7, 12)) + + for ax in axes: + + # Generate price series + z_draws = z_mc.simulate_indices(T, init=int(len(z_vals) / 2 - 10)) + w_draws = w_mc.simulate_indices(T) + s_vals = z_vals[z_draws] + w_vals[w_draws] + + # Find the exercise date, if any. + exercise_date = T + 1 + for t in range(T): + k = exit_reward(t, w_vals[w_draws[t]], z_vals[z_draws[t]], T, K) - \ + h_star[w_draws[t], z_draws[t]] + if k >= 0: + exercise_date = t + + if exercise_date >= T: + print("Warning: Option not exercised.") + else: + # Plot + ax.set_ylim(y_min, y_max) + ax.set_xlim(0, T+1) + ax.fill_between(range(T), np.ones(T) * K, np.ones(T) * y_max, alpha=0.2) + ax.plot(range(T), s_vals, label=r"$S_t$") + ax.plot((exercise_date,), (s_vals[exercise_date]), "ko") + ax.vlines((exercise_date,), 0, (s_vals[exercise_date]), ls="--", colors="black") + ax.legend(loc="upper left", fontsize=fontsize) + ax.text(-10, 11, "in the money", fontsize=fontsize, rotation=90) + ax.text(-10, 7.2, "out of the money", fontsize=fontsize, rotation=90) + ax.text(exercise_date-20, 6, + "exercise date", fontsize=fontsize) + ax.set_xticks((1, T)) + ax.set_yticks((y_min, y_max)) + + if savefig: + fig.savefig(figname) + plt.show() diff --git a/code/py/ar1_spec_rad.py b/code/py/ar1_spec_rad.py new file mode 100644 index 0000000..37fb9c3 --- /dev/null +++ b/code/py/ar1_spec_rad.py @@ -0,0 +1,96 @@ +""" + +Compute r(L) for model + + Zₜ = μ (1 - ρ) + ρ Zₜ₋₁ + σ εₜ + β_t = b(Z_t) + +The process is discretized using the Tauchen method with n states. +""" + +import numpy as np + +from quantecon.markov import tauchen + + +def compute_mc_spec_rad(n, ρ, σ, μ, m, b): + mc = tauchen(n, ρ, σ, μ * (1 - ρ), m) + state_values, P = mc.state_values, mc.P + + L = np.zeros((n, n)) + for i in range(n): + for j in range(n): + L[i, j] = b(state_values[i]) * P[i, j] + r = np.max(np.abs(np.linalg.eigvals(L))) + return r + + +# Hubmer et al parameter values, p. 24 of May 17 2020 version. + +n = 15 +ρ = 0.992 +σ = 0.0006 +μ = 0.944 +m = 4 +b = lambda z: z + +print("Spectral radius of L in Hubmer et al.:") +print(compute_mc_spec_rad(n, ρ, σ, μ, m, b)) + +# ## Hills et al 2019 EER + +# For the empirical model, +# +# $$ +# Z_{t+1} = 1 - \rho + \rho Z_t + \sigma \epsilon_{t+1}, +# \quad \beta_t = \beta Z_t +# $$ +# +# with +# +# $$ +# \beta = 0.99875, \; \rho = 0.85, \; \sigma = 0.0062 +# $$ +# +# They use 15 grid points on $[1-4.5\sigma_\delta, 1+4.5\sigma_\delta]$. + +n = 15 +ρ = 0.85 +σ = 0.0062 +μ = 1 +m = 4.5 +beta = 0.99875 +b = lambda z: beta*z + +print("Spectral radius of L in Hills et al.:") +print(compute_mc_spec_rad(n, ρ, σ, μ, m, b)) + +# Let's run a simulation of the discount process. +# Plots + + +import matplotlib.pyplot as plt + + +def plot_beta_sim(T=80, + savefig=True, + figname="./figures/ar1_spec_rad.png"): + β_vals = np.zeros(T) + Z = 1 + for t in range(T): + β_vals[t] = beta * Z + Z = 1 - ρ + ρ * Z + σ * np.random.default_rng().normal() + + fig, ax = plt.subplots(figsize=(6, 3.8)) + + ax.plot(β_vals, label=r"$\beta_t$") + ax.plot(np.arange(T), np.ones(T), "k--", alpha=0.5, label=r"$\beta=1$") + ax.set_yticks((0.97, 1.0, 1.03)) + ax.set_xlabel("time") + ax.legend(frameon=False) + + if savefig: + fig.savefig(figname) + plt.show() + +plot_beta_sim() diff --git a/code/py/compute_spec_rad.py b/code/py/compute_spec_rad.py new file mode 100644 index 0000000..fc82bb0 --- /dev/null +++ b/code/py/compute_spec_rad.py @@ -0,0 +1,11 @@ +import numpy as np + +# Spectral radius +ρ = lambda A: np.max(np.abs(np.linalg.eigvals(A))) + +# Test with arbitrary A +A = np.array([ + [0.4, 0.1], + [0.7, 0.2] +]) +print(ρ(A)) diff --git a/code/py/data/GS1.csv b/code/py/data/GS1.csv new file mode 100644 index 0000000..3d1d51f --- /dev/null +++ b/code/py/data/GS1.csv @@ -0,0 +1,829 @@ +DATE,GS1 +1953-04-01,2.36 +1953-05-01,2.48 +1953-06-01,2.45 +1953-07-01,2.38 +1953-08-01,2.28 +1953-09-01,2.20 +1953-10-01,1.79 +1953-11-01,1.67 +1953-12-01,1.66 +1954-01-01,1.41 +1954-02-01,1.14 +1954-03-01,1.13 +1954-04-01,0.96 +1954-05-01,0.85 +1954-06-01,0.82 +1954-07-01,0.84 +1954-08-01,0.88 +1954-09-01,1.03 +1954-10-01,1.17 +1954-11-01,1.14 +1954-12-01,1.21 +1955-01-01,1.39 +1955-02-01,1.57 +1955-03-01,1.59 +1955-04-01,1.75 +1955-05-01,1.90 +1955-06-01,1.91 +1955-07-01,2.02 +1955-08-01,2.37 +1955-09-01,2.36 +1955-10-01,2.39 +1955-11-01,2.48 +1955-12-01,2.73 +1956-01-01,2.58 +1956-02-01,2.49 +1956-03-01,2.61 +1956-04-01,2.92 +1956-05-01,2.94 +1956-06-01,2.74 +1956-07-01,2.76 +1956-08-01,3.10 +1956-09-01,3.35 +1956-10-01,3.28 +1956-11-01,3.44 +1956-12-01,3.68 +1957-01-01,3.37 +1957-02-01,3.38 +1957-03-01,3.42 +1957-04-01,3.49 +1957-05-01,3.48 +1957-06-01,3.65 +1957-07-01,3.81 +1957-08-01,4.01 +1957-09-01,4.07 +1957-10-01,4.01 +1957-11-01,3.57 +1957-12-01,3.18 +1958-01-01,2.65 +1958-02-01,1.99 +1958-03-01,1.84 +1958-04-01,1.45 +1958-05-01,1.37 +1958-06-01,1.23 +1958-07-01,1.61 +1958-08-01,2.50 +1958-09-01,3.05 +1958-10-01,3.19 +1958-11-01,3.10 +1958-12-01,3.29 +1959-01-01,3.36 +1959-02-01,3.54 +1959-03-01,3.61 +1959-04-01,3.72 +1959-05-01,3.96 +1959-06-01,4.07 +1959-07-01,4.39 +1959-08-01,4.42 +1959-09-01,5.00 +1959-10-01,4.80 +1959-11-01,4.81 +1959-12-01,5.14 +1960-01-01,5.03 +1960-02-01,4.66 +1960-03-01,4.02 +1960-04-01,4.04 +1960-05-01,4.21 +1960-06-01,3.36 +1960-07-01,3.20 +1960-08-01,2.95 +1960-09-01,3.07 +1960-10-01,3.04 +1960-11-01,3.08 +1960-12-01,2.86 +1961-01-01,2.81 +1961-02-01,2.93 +1961-03-01,2.88 +1961-04-01,2.88 +1961-05-01,2.87 +1961-06-01,3.06 +1961-07-01,2.92 +1961-08-01,3.06 +1961-09-01,3.06 +1961-10-01,3.05 +1961-11-01,3.07 +1961-12-01,3.18 +1962-01-01,3.28 +1962-02-01,3.28 +1962-03-01,3.06 +1962-04-01,2.99 +1962-05-01,3.03 +1962-06-01,3.03 +1962-07-01,3.29 +1962-08-01,3.20 +1962-09-01,3.06 +1962-10-01,2.98 +1962-11-01,3.00 +1962-12-01,3.01 +1963-01-01,3.04 +1963-02-01,3.01 +1963-03-01,3.03 +1963-04-01,3.11 +1963-05-01,3.12 +1963-06-01,3.20 +1963-07-01,3.48 +1963-08-01,3.53 +1963-09-01,3.57 +1963-10-01,3.64 +1963-11-01,3.74 +1963-12-01,3.81 +1964-01-01,3.79 +1964-02-01,3.78 +1964-03-01,3.91 +1964-04-01,3.91 +1964-05-01,3.84 +1964-06-01,3.83 +1964-07-01,3.72 +1964-08-01,3.74 +1964-09-01,3.84 +1964-10-01,3.86 +1964-11-01,3.91 +1964-12-01,4.02 +1965-01-01,3.94 +1965-02-01,4.03 +1965-03-01,4.06 +1965-04-01,4.04 +1965-05-01,4.03 +1965-06-01,3.99 +1965-07-01,3.98 +1965-08-01,4.07 +1965-09-01,4.20 +1965-10-01,4.30 +1965-11-01,4.37 +1965-12-01,4.72 +1966-01-01,4.88 +1966-02-01,4.94 +1966-03-01,4.97 +1966-04-01,4.90 +1966-05-01,4.93 +1966-06-01,4.97 +1966-07-01,5.17 +1966-08-01,5.54 +1966-09-01,5.82 +1966-10-01,5.58 +1966-11-01,5.54 +1966-12-01,5.20 +1967-01-01,4.75 +1967-02-01,4.71 +1967-03-01,4.35 +1967-04-01,4.11 +1967-05-01,4.15 +1967-06-01,4.48 +1967-07-01,5.01 +1967-08-01,5.13 +1967-09-01,5.24 +1967-10-01,5.37 +1967-11-01,5.61 +1967-12-01,5.71 +1968-01-01,5.43 +1968-02-01,5.41 +1968-03-01,5.58 +1968-04-01,5.71 +1968-05-01,6.14 +1968-06-01,5.98 +1968-07-01,5.65 +1968-08-01,5.43 +1968-09-01,5.45 +1968-10-01,5.57 +1968-11-01,5.75 +1968-12-01,6.19 +1969-01-01,6.34 +1969-02-01,6.41 +1969-03-01,6.34 +1969-04-01,6.26 +1969-05-01,6.42 +1969-06-01,7.04 +1969-07-01,7.60 +1969-08-01,7.54 +1969-09-01,7.82 +1969-10-01,7.64 +1969-11-01,7.89 +1969-12-01,8.17 +1970-01-01,8.10 +1970-02-01,7.59 +1970-03-01,6.97 +1970-04-01,7.06 +1970-05-01,7.75 +1970-06-01,7.55 +1970-07-01,7.10 +1970-08-01,6.98 +1970-09-01,6.73 +1970-10-01,6.43 +1970-11-01,5.51 +1970-12-01,5.00 +1971-01-01,4.57 +1971-02-01,3.89 +1971-03-01,3.69 +1971-04-01,4.30 +1971-05-01,5.04 +1971-06-01,5.64 +1971-07-01,6.04 +1971-08-01,5.80 +1971-09-01,5.41 +1971-10-01,4.91 +1971-11-01,4.67 +1971-12-01,4.60 +1972-01-01,4.28 +1972-02-01,4.27 +1972-03-01,4.67 +1972-04-01,4.96 +1972-05-01,4.64 +1972-06-01,4.93 +1972-07-01,4.96 +1972-08-01,4.98 +1972-09-01,5.52 +1972-10-01,5.52 +1972-11-01,5.27 +1972-12-01,5.52 +1973-01-01,5.89 +1973-02-01,6.19 +1973-03-01,6.85 +1973-04-01,6.85 +1973-05-01,6.89 +1973-06-01,7.31 +1973-07-01,8.39 +1973-08-01,8.82 +1973-09-01,8.31 +1973-10-01,7.40 +1973-11-01,7.57 +1973-12-01,7.27 +1974-01-01,7.42 +1974-02-01,6.88 +1974-03-01,7.76 +1974-04-01,8.62 +1974-05-01,8.78 +1974-06-01,8.67 +1974-07-01,8.80 +1974-08-01,9.36 +1974-09-01,8.87 +1974-10-01,8.05 +1974-11-01,7.66 +1974-12-01,7.31 +1975-01-01,6.83 +1975-02-01,5.98 +1975-03-01,6.11 +1975-04-01,6.90 +1975-05-01,6.39 +1975-06-01,6.29 +1975-07-01,7.11 +1975-08-01,7.70 +1975-09-01,7.75 +1975-10-01,6.95 +1975-11-01,6.49 +1975-12-01,6.60 +1976-01-01,5.81 +1976-02-01,5.91 +1976-03-01,6.21 +1976-04-01,5.92 +1976-05-01,6.40 +1976-06-01,6.52 +1976-07-01,6.20 +1976-08-01,6.00 +1976-09-01,5.84 +1976-10-01,5.50 +1976-11-01,5.29 +1976-12-01,4.89 +1977-01-01,5.29 +1977-02-01,5.47 +1977-03-01,5.50 +1977-04-01,5.44 +1977-05-01,5.84 +1977-06-01,5.80 +1977-07-01,5.94 +1977-08-01,6.37 +1977-09-01,6.53 +1977-10-01,6.97 +1977-11-01,6.95 +1977-12-01,6.96 +1978-01-01,7.28 +1978-02-01,7.34 +1978-03-01,7.31 +1978-04-01,7.45 +1978-05-01,7.82 +1978-06-01,8.09 +1978-07-01,8.39 +1978-08-01,8.31 +1978-09-01,8.64 +1978-10-01,9.14 +1978-11-01,10.01 +1978-12-01,10.30 +1979-01-01,10.41 +1979-02-01,10.24 +1979-03-01,10.25 +1979-04-01,10.12 +1979-05-01,10.12 +1979-06-01,9.57 +1979-07-01,9.64 +1979-08-01,9.98 +1979-09-01,10.84 +1979-10-01,12.44 +1979-11-01,12.39 +1979-12-01,11.98 +1980-01-01,12.06 +1980-02-01,13.92 +1980-03-01,15.82 +1980-04-01,13.30 +1980-05-01,9.39 +1980-06-01,8.16 +1980-07-01,8.65 +1980-08-01,10.24 +1980-09-01,11.52 +1980-10-01,12.49 +1980-11-01,14.15 +1980-12-01,14.88 +1981-01-01,14.08 +1981-02-01,14.57 +1981-03-01,13.71 +1981-04-01,14.32 +1981-05-01,16.20 +1981-06-01,14.86 +1981-07-01,15.72 +1981-08-01,16.72 +1981-09-01,16.52 +1981-10-01,15.38 +1981-11-01,12.41 +1981-12-01,12.85 +1982-01-01,14.32 +1982-02-01,14.73 +1982-03-01,13.95 +1982-04-01,13.98 +1982-05-01,13.34 +1982-06-01,14.07 +1982-07-01,13.24 +1982-08-01,11.43 +1982-09-01,10.85 +1982-10-01,9.32 +1982-11-01,9.16 +1982-12-01,8.91 +1983-01-01,8.62 +1983-02-01,8.92 +1983-03-01,9.04 +1983-04-01,8.98 +1983-05-01,8.90 +1983-06-01,9.66 +1983-07-01,10.20 +1983-08-01,10.53 +1983-09-01,10.16 +1983-10-01,9.81 +1983-11-01,9.94 +1983-12-01,10.11 +1984-01-01,9.90 +1984-02-01,10.04 +1984-03-01,10.59 +1984-04-01,10.90 +1984-05-01,11.66 +1984-06-01,12.08 +1984-07-01,12.03 +1984-08-01,11.82 +1984-09-01,11.58 +1984-10-01,10.90 +1984-11-01,9.82 +1984-12-01,9.33 +1985-01-01,9.02 +1985-02-01,9.29 +1985-03-01,9.86 +1985-04-01,9.14 +1985-05-01,8.46 +1985-06-01,7.80 +1985-07-01,7.86 +1985-08-01,8.05 +1985-09-01,8.07 +1985-10-01,8.01 +1985-11-01,7.88 +1985-12-01,7.67 +1986-01-01,7.73 +1986-02-01,7.61 +1986-03-01,7.03 +1986-04-01,6.44 +1986-05-01,6.65 +1986-06-01,6.73 +1986-07-01,6.27 +1986-08-01,5.93 +1986-09-01,5.77 +1986-10-01,5.72 +1986-11-01,5.80 +1986-12-01,5.87 +1987-01-01,5.78 +1987-02-01,5.96 +1987-03-01,6.03 +1987-04-01,6.50 +1987-05-01,7.00 +1987-06-01,6.80 +1987-07-01,6.68 +1987-08-01,7.03 +1987-09-01,7.67 +1987-10-01,7.59 +1987-11-01,6.96 +1987-12-01,7.17 +1988-01-01,6.99 +1988-02-01,6.64 +1988-03-01,6.71 +1988-04-01,7.01 +1988-05-01,7.40 +1988-06-01,7.49 +1988-07-01,7.75 +1988-08-01,8.17 +1988-09-01,8.09 +1988-10-01,8.11 +1988-11-01,8.48 +1988-12-01,8.99 +1989-01-01,9.05 +1989-02-01,9.25 +1989-03-01,9.57 +1989-04-01,9.36 +1989-05-01,8.98 +1989-06-01,8.44 +1989-07-01,7.89 +1989-08-01,8.18 +1989-09-01,8.22 +1989-10-01,7.99 +1989-11-01,7.77 +1989-12-01,7.72 +1990-01-01,7.92 +1990-02-01,8.11 +1990-03-01,8.35 +1990-04-01,8.40 +1990-05-01,8.32 +1990-06-01,8.10 +1990-07-01,7.94 +1990-08-01,7.78 +1990-09-01,7.76 +1990-10-01,7.55 +1990-11-01,7.31 +1990-12-01,7.05 +1991-01-01,6.64 +1991-02-01,6.27 +1991-03-01,6.40 +1991-04-01,6.24 +1991-05-01,6.13 +1991-06-01,6.36 +1991-07-01,6.31 +1991-08-01,5.78 +1991-09-01,5.57 +1991-10-01,5.33 +1991-11-01,4.89 +1991-12-01,4.38 +1992-01-01,4.15 +1992-02-01,4.29 +1992-03-01,4.63 +1992-04-01,4.30 +1992-05-01,4.19 +1992-06-01,4.17 +1992-07-01,3.60 +1992-08-01,3.47 +1992-09-01,3.18 +1992-10-01,3.30 +1992-11-01,3.68 +1992-12-01,3.71 +1993-01-01,3.50 +1993-02-01,3.39 +1993-03-01,3.33 +1993-04-01,3.24 +1993-05-01,3.36 +1993-06-01,3.54 +1993-07-01,3.47 +1993-08-01,3.44 +1993-09-01,3.36 +1993-10-01,3.39 +1993-11-01,3.58 +1993-12-01,3.61 +1994-01-01,3.54 +1994-02-01,3.87 +1994-03-01,4.32 +1994-04-01,4.82 +1994-05-01,5.31 +1994-06-01,5.27 +1994-07-01,5.48 +1994-08-01,5.56 +1994-09-01,5.76 +1994-10-01,6.11 +1994-11-01,6.54 +1994-12-01,7.14 +1995-01-01,7.05 +1995-02-01,6.70 +1995-03-01,6.43 +1995-04-01,6.27 +1995-05-01,6.00 +1995-06-01,5.64 +1995-07-01,5.59 +1995-08-01,5.75 +1995-09-01,5.62 +1995-10-01,5.59 +1995-11-01,5.43 +1995-12-01,5.31 +1996-01-01,5.09 +1996-02-01,4.94 +1996-03-01,5.34 +1996-04-01,5.54 +1996-05-01,5.64 +1996-06-01,5.81 +1996-07-01,5.85 +1996-08-01,5.67 +1996-09-01,5.83 +1996-10-01,5.55 +1996-11-01,5.42 +1996-12-01,5.47 +1997-01-01,5.61 +1997-02-01,5.53 +1997-03-01,5.80 +1997-04-01,5.99 +1997-05-01,5.87 +1997-06-01,5.69 +1997-07-01,5.54 +1997-08-01,5.56 +1997-09-01,5.52 +1997-10-01,5.46 +1997-11-01,5.46 +1997-12-01,5.53 +1998-01-01,5.24 +1998-02-01,5.31 +1998-03-01,5.39 +1998-04-01,5.38 +1998-05-01,5.44 +1998-06-01,5.41 +1998-07-01,5.36 +1998-08-01,5.21 +1998-09-01,4.71 +1998-10-01,4.12 +1998-11-01,4.53 +1998-12-01,4.52 +1999-01-01,4.51 +1999-02-01,4.70 +1999-03-01,4.78 +1999-04-01,4.69 +1999-05-01,4.85 +1999-06-01,5.10 +1999-07-01,5.03 +1999-08-01,5.20 +1999-09-01,5.25 +1999-10-01,5.43 +1999-11-01,5.55 +1999-12-01,5.84 +2000-01-01,6.12 +2000-02-01,6.22 +2000-03-01,6.22 +2000-04-01,6.15 +2000-05-01,6.33 +2000-06-01,6.17 +2000-07-01,6.08 +2000-08-01,6.18 +2000-09-01,6.13 +2000-10-01,6.01 +2000-11-01,6.09 +2000-12-01,5.60 +2001-01-01,4.81 +2001-02-01,4.68 +2001-03-01,4.30 +2001-04-01,3.98 +2001-05-01,3.78 +2001-06-01,3.58 +2001-07-01,3.62 +2001-08-01,3.47 +2001-09-01,2.82 +2001-10-01,2.33 +2001-11-01,2.18 +2001-12-01,2.22 +2002-01-01,2.16 +2002-02-01,2.23 +2002-03-01,2.57 +2002-04-01,2.48 +2002-05-01,2.35 +2002-06-01,2.20 +2002-07-01,1.96 +2002-08-01,1.76 +2002-09-01,1.72 +2002-10-01,1.65 +2002-11-01,1.49 +2002-12-01,1.45 +2003-01-01,1.36 +2003-02-01,1.30 +2003-03-01,1.24 +2003-04-01,1.27 +2003-05-01,1.18 +2003-06-01,1.01 +2003-07-01,1.12 +2003-08-01,1.31 +2003-09-01,1.24 +2003-10-01,1.25 +2003-11-01,1.34 +2003-12-01,1.31 +2004-01-01,1.24 +2004-02-01,1.24 +2004-03-01,1.19 +2004-04-01,1.43 +2004-05-01,1.78 +2004-06-01,2.12 +2004-07-01,2.10 +2004-08-01,2.02 +2004-09-01,2.12 +2004-10-01,2.23 +2004-11-01,2.50 +2004-12-01,2.67 +2005-01-01,2.86 +2005-02-01,3.03 +2005-03-01,3.30 +2005-04-01,3.32 +2005-05-01,3.33 +2005-06-01,3.36 +2005-07-01,3.64 +2005-08-01,3.87 +2005-09-01,3.85 +2005-10-01,4.18 +2005-11-01,4.33 +2005-12-01,4.35 +2006-01-01,4.45 +2006-02-01,4.68 +2006-03-01,4.77 +2006-04-01,4.90 +2006-05-01,5.00 +2006-06-01,5.16 +2006-07-01,5.22 +2006-08-01,5.08 +2006-09-01,4.97 +2006-10-01,5.01 +2006-11-01,5.01 +2006-12-01,4.94 +2007-01-01,5.06 +2007-02-01,5.05 +2007-03-01,4.92 +2007-04-01,4.93 +2007-05-01,4.91 +2007-06-01,4.96 +2007-07-01,4.96 +2007-08-01,4.47 +2007-09-01,4.14 +2007-10-01,4.10 +2007-11-01,3.50 +2007-12-01,3.26 +2008-01-01,2.71 +2008-02-01,2.05 +2008-03-01,1.54 +2008-04-01,1.74 +2008-05-01,2.06 +2008-06-01,2.42 +2008-07-01,2.28 +2008-08-01,2.18 +2008-09-01,1.91 +2008-10-01,1.42 +2008-11-01,1.07 +2008-12-01,0.49 +2009-01-01,0.44 +2009-02-01,0.62 +2009-03-01,0.64 +2009-04-01,0.55 +2009-05-01,0.50 +2009-06-01,0.51 +2009-07-01,0.48 +2009-08-01,0.46 +2009-09-01,0.40 +2009-10-01,0.37 +2009-11-01,0.31 +2009-12-01,0.37 +2010-01-01,0.35 +2010-02-01,0.35 +2010-03-01,0.40 +2010-04-01,0.45 +2010-05-01,0.37 +2010-06-01,0.32 +2010-07-01,0.29 +2010-08-01,0.26 +2010-09-01,0.26 +2010-10-01,0.23 +2010-11-01,0.25 +2010-12-01,0.29 +2011-01-01,0.27 +2011-02-01,0.29 +2011-03-01,0.26 +2011-04-01,0.25 +2011-05-01,0.19 +2011-06-01,0.18 +2011-07-01,0.19 +2011-08-01,0.11 +2011-09-01,0.10 +2011-10-01,0.11 +2011-11-01,0.11 +2011-12-01,0.12 +2012-01-01,0.12 +2012-02-01,0.16 +2012-03-01,0.19 +2012-04-01,0.18 +2012-05-01,0.19 +2012-06-01,0.19 +2012-07-01,0.19 +2012-08-01,0.18 +2012-09-01,0.18 +2012-10-01,0.18 +2012-11-01,0.18 +2012-12-01,0.16 +2013-01-01,0.15 +2013-02-01,0.16 +2013-03-01,0.15 +2013-04-01,0.12 +2013-05-01,0.12 +2013-06-01,0.14 +2013-07-01,0.12 +2013-08-01,0.13 +2013-09-01,0.12 +2013-10-01,0.12 +2013-11-01,0.12 +2013-12-01,0.13 +2014-01-01,0.12 +2014-02-01,0.12 +2014-03-01,0.13 +2014-04-01,0.11 +2014-05-01,0.10 +2014-06-01,0.10 +2014-07-01,0.11 +2014-08-01,0.11 +2014-09-01,0.11 +2014-10-01,0.10 +2014-11-01,0.13 +2014-12-01,0.21 +2015-01-01,0.20 +2015-02-01,0.22 +2015-03-01,0.25 +2015-04-01,0.23 +2015-05-01,0.24 +2015-06-01,0.28 +2015-07-01,0.30 +2015-08-01,0.38 +2015-09-01,0.37 +2015-10-01,0.26 +2015-11-01,0.48 +2015-12-01,0.65 +2016-01-01,0.54 +2016-02-01,0.53 +2016-03-01,0.66 +2016-04-01,0.56 +2016-05-01,0.59 +2016-06-01,0.55 +2016-07-01,0.51 +2016-08-01,0.57 +2016-09-01,0.59 +2016-10-01,0.66 +2016-11-01,0.74 +2016-12-01,0.87 +2017-01-01,0.83 +2017-02-01,0.82 +2017-03-01,1.01 +2017-04-01,1.04 +2017-05-01,1.12 +2017-06-01,1.20 +2017-07-01,1.22 +2017-08-01,1.23 +2017-09-01,1.28 +2017-10-01,1.40 +2017-11-01,1.56 +2017-12-01,1.70 +2018-01-01,1.80 +2018-02-01,1.96 +2018-03-01,2.06 +2018-04-01,2.15 +2018-05-01,2.27 +2018-06-01,2.33 +2018-07-01,2.39 +2018-08-01,2.45 +2018-09-01,2.56 +2018-10-01,2.65 +2018-11-01,2.70 +2018-12-01,2.66 +2019-01-01,2.58 +2019-02-01,2.55 +2019-03-01,2.49 +2019-04-01,2.42 +2019-05-01,2.34 +2019-06-01,2.00 +2019-07-01,1.96 +2019-08-01,1.77 +2019-09-01,1.80 +2019-10-01,1.61 +2019-11-01,1.57 +2019-12-01,1.55 +2020-01-01,1.53 +2020-02-01,1.41 +2020-03-01,0.33 +2020-04-01,0.18 +2020-05-01,0.16 +2020-06-01,0.18 +2020-07-01,0.15 +2020-08-01,0.13 +2020-09-01,0.13 +2020-10-01,0.13 +2020-11-01,0.12 +2020-12-01,0.10 +2021-01-01,0.10 +2021-02-01,0.07 +2021-03-01,0.08 +2021-04-01,0.06 +2021-05-01,0.05 +2021-06-01,0.07 +2021-07-01,0.08 +2021-08-01,0.07 +2021-09-01,0.08 +2021-10-01,0.11 +2021-11-01,0.18 +2021-12-01,0.30 +2022-01-01,0.55 +2022-02-01,1.00 +2022-03-01,1.34 diff --git a/code/py/data/WFII10.csv b/code/py/data/WFII10.csv new file mode 100644 index 0000000..d405522 --- /dev/null +++ b/code/py/data/WFII10.csv @@ -0,0 +1,523 @@ +DATE,WFII10 +2012-04-13,-0.23 +2012-04-20,-0.25 +2012-04-27,-0.27 +2012-05-04,-0.29 +2012-05-11,-0.27 +2012-05-18,-0.35 +2012-05-25,-0.39 +2012-06-01,-0.48 +2012-06-08,-0.52 +2012-06-15,-0.51 +2012-06-22,-0.49 +2012-06-29,-0.46 +2012-07-06,-0.51 +2012-07-13,-0.58 +2012-07-20,-0.62 +2012-07-27,-0.66 +2012-08-03,-0.67 +2012-08-10,-0.62 +2012-08-17,-0.48 +2012-08-24,-0.53 +2012-08-31,-0.65 +2012-09-07,-0.67 +2012-09-14,-0.69 +2012-09-21,-0.73 +2012-09-28,-0.76 +2012-10-05,-0.82 +2012-10-12,-0.79 +2012-10-19,-0.70 +2012-10-26,-0.68 +2012-11-02,-0.75 +2012-11-09,-0.79 +2012-11-16,-0.82 +2012-11-23,-0.74 +2012-11-30,-0.76 +2012-12-07,-0.84 +2012-12-14,-0.79 +2012-12-21,-0.69 +2012-12-28,-0.72 +2013-01-04,-0.60 +2013-01-11,-0.62 +2013-01-18,-0.65 +2013-01-25,-0.63 +2013-02-01,-0.55 +2013-02-08,-0.57 +2013-02-15,-0.56 +2013-02-22,-0.54 +2013-03-01,-0.63 +2013-03-08,-0.59 +2013-03-15,-0.52 +2013-03-22,-0.59 +2013-03-29,-0.62 +2013-04-05,-0.67 +2013-04-12,-0.66 +2013-04-19,-0.62 +2013-04-26,-0.65 +2013-05-03,-0.62 +2013-05-10,-0.49 +2013-05-17,-0.37 +2013-05-24,-0.28 +2013-05-31,-0.09 +2013-06-07,-0.04 +2013-06-14,0.14 +2013-06-21,0.33 +2013-06-28,0.58 +2013-07-05,0.52 +2013-07-12,0.58 +2013-07-19,0.39 +2013-07-26,0.38 +2013-08-02,0.44 +2013-08-09,0.37 +2013-08-16,0.53 +2013-08-23,0.73 +2013-08-30,0.63 +2013-09-06,0.85 +2013-09-13,0.82 +2013-09-20,0.60 +2013-09-27,0.46 +2013-10-04,0.45 +2013-10-11,0.48 +2013-10-18,0.48 +2013-10-25,0.37 +2013-11-01,0.39 +2013-11-08,0.52 +2013-11-15,0.56 +2013-11-22,0.56 +2013-11-29,0.57 +2013-12-06,0.71 +2013-12-13,0.72 +2013-12-20,0.72 +2013-12-27,0.79 +2014-01-03,0.76 +2014-01-10,0.68 +2014-01-17,0.60 +2014-01-24,0.62 +2014-01-31,0.58 +2014-02-07,0.53 +2014-02-14,0.56 +2014-02-21,0.60 +2014-02-28,0.53 +2014-03-07,0.52 +2014-03-14,0.53 +2014-03-21,0.58 +2014-03-28,0.59 +2014-04-04,0.64 +2014-04-11,0.55 +2014-04-18,0.51 +2014-04-25,0.50 +2014-05-02,0.48 +2014-05-09,0.44 +2014-05-16,0.39 +2014-05-23,0.35 +2014-05-30,0.25 +2014-06-06,0.40 +2014-06-13,0.41 +2014-06-20,0.39 +2014-06-27,0.30 +2014-07-04,0.34 +2014-07-11,0.29 +2014-07-18,0.28 +2014-07-25,0.24 +2014-08-01,0.25 +2014-08-08,0.23 +2014-08-15,0.19 +2014-08-22,0.24 +2014-08-29,0.23 +2014-09-05,0.28 +2014-09-12,0.42 +2014-09-19,0.54 +2014-09-26,0.54 +2014-10-03,0.51 +2014-10-10,0.41 +2014-10-17,0.29 +2014-10-24,0.35 +2014-10-31,0.41 +2014-11-07,0.42 +2014-11-14,0.45 +2014-11-21,0.49 +2014-11-28,0.42 +2014-12-05,0.50 +2014-12-12,0.49 +2014-12-19,0.49 +2014-12-26,0.55 +2015-01-02,0.51 +2015-01-09,0.39 +2015-01-16,0.29 +2015-01-23,0.25 +2015-01-30,0.14 +2015-02-06,0.12 +2015-02-13,0.31 +2015-02-20,0.39 +2015-02-27,0.25 +2015-03-06,0.29 +2015-03-13,0.41 +2015-03-20,0.29 +2015-03-27,0.16 +2015-04-03,0.12 +2015-04-10,0.09 +2015-04-17,0.07 +2015-04-24,0.06 +2015-05-01,0.11 +2015-05-08,0.28 +2015-05-15,0.38 +2015-05-22,0.37 +2015-05-29,0.33 +2015-06-05,0.49 +2015-06-12,0.57 +2015-06-19,0.45 +2015-06-26,0.50 +2015-07-03,0.49 +2015-07-10,0.45 +2015-07-17,0.54 +2015-07-24,0.52 +2015-07-31,0.50 +2015-08-07,0.53 +2015-08-14,0.54 +2015-08-21,0.56 +2015-08-28,0.59 +2015-09-04,0.62 +2015-09-11,0.64 +2015-09-18,0.66 +2015-09-25,0.66 +2015-10-02,0.62 +2015-10-09,0.55 +2015-10-16,0.54 +2015-10-23,0.59 +2015-10-30,0.62 +2015-11-06,0.69 +2015-11-13,0.77 +2015-11-20,0.71 +2015-11-27,0.63 +2015-12-04,0.64 +2015-12-11,0.69 +2015-12-18,0.78 +2015-12-25,0.76 +2016-01-01,0.76 +2016-01-08,0.67 +2016-01-15,0.68 +2016-01-22,0.71 +2016-01-29,0.61 +2016-02-05,0.52 +2016-02-12,0.50 +2016-02-19,0.52 +2016-02-26,0.39 +2016-03-04,0.33 +2016-03-11,0.42 +2016-03-18,0.38 +2016-03-25,0.31 +2016-04-01,0.21 +2016-04-08,0.14 +2016-04-15,0.21 +2016-04-22,0.22 +2016-04-29,0.19 +2016-05-06,0.17 +2016-05-13,0.15 +2016-05-20,0.23 +2016-05-27,0.28 +2016-06-03,0.27 +2016-06-10,0.14 +2016-06-17,0.16 +2016-06-24,0.23 +2016-07-01,0.09 +2016-07-08,-0.04 +2016-07-15,0.06 +2016-07-22,0.09 +2016-07-29,0.03 +2016-08-05,0.08 +2016-08-12,0.07 +2016-08-19,0.10 +2016-08-26,0.09 +2016-09-02,0.12 +2016-09-09,0.09 +2016-09-16,0.21 +2016-09-23,0.14 +2016-09-30,0.02 +2016-10-07,0.08 +2016-10-14,0.13 +2016-10-21,0.08 +2016-10-28,0.10 +2016-11-04,0.12 +2016-11-11,0.20 +2016-11-18,0.41 +2016-11-25,0.44 +2016-12-02,0.46 +2016-12-09,0.45 +2016-12-16,0.62 +2016-12-23,0.63 +2016-12-30,0.55 +2017-01-06,0.46 +2017-01-13,0.41 +2017-01-20,0.41 +2017-01-27,0.42 +2017-02-03,0.42 +2017-02-10,0.40 +2017-02-17,0.44 +2017-02-24,0.36 +2017-03-03,0.41 +2017-03-10,0.54 +2017-03-17,0.54 +2017-03-24,0.45 +2017-03-31,0.43 +2017-04-07,0.40 +2017-04-14,0.39 +2017-04-21,0.37 +2017-04-28,0.41 +2017-05-05,0.46 +2017-05-12,0.53 +2017-05-19,0.46 +2017-05-26,0.45 +2017-06-02,0.39 +2017-06-09,0.40 +2017-06-16,0.47 +2017-06-23,0.49 +2017-06-30,0.51 +2017-07-07,0.61 +2017-07-14,0.60 +2017-07-21,0.51 +2017-07-28,0.49 +2017-08-04,0.47 +2017-08-11,0.43 +2017-08-18,0.45 +2017-08-25,0.43 +2017-09-01,0.38 +2017-09-08,0.28 +2017-09-15,0.34 +2017-09-22,0.40 +2017-09-29,0.43 +2017-10-06,0.49 +2017-10-13,0.45 +2017-10-20,0.49 +2017-10-27,0.55 +2017-11-03,0.49 +2017-11-10,0.47 +2017-11-17,0.51 +2017-11-24,0.51 +2017-12-01,0.52 +2017-12-08,0.49 +2017-12-15,0.49 +2017-12-22,0.54 +2017-12-29,0.48 +2018-01-05,0.46 +2018-01-12,0.52 +2018-01-19,0.55 +2018-01-26,0.58 +2018-02-02,0.64 +2018-02-09,0.73 +2018-02-16,0.79 +2018-02-23,0.79 +2018-03-02,0.74 +2018-03-09,0.76 +2018-03-16,0.75 +2018-03-23,0.78 +2018-03-30,0.72 +2018-04-06,0.71 +2018-04-13,0.70 +2018-04-20,0.73 +2018-04-27,0.82 +2018-05-04,0.79 +2018-05-11,0.81 +2018-05-18,0.91 +2018-05-25,0.88 +2018-06-01,0.77 +2018-06-08,0.81 +2018-06-15,0.83 +2018-06-22,0.79 +2018-06-29,0.75 +2018-07-06,0.71 +2018-07-13,0.74 +2018-07-20,0.77 +2018-07-27,0.84 +2018-08-03,0.85 +2018-08-10,0.83 +2018-08-17,0.79 +2018-08-24,0.74 +2018-08-31,0.76 +2018-09-07,0.81 +2018-09-14,0.86 +2018-09-21,0.92 +2018-09-28,0.92 +2018-10-05,0.99 +2018-10-12,1.04 +2018-10-19,1.06 +2018-10-26,1.06 +2018-11-02,1.09 +2018-11-09,1.15 +2018-11-16,1.10 +2018-11-23,1.09 +2018-11-30,1.09 +2018-12-07,0.98 +2018-12-14,1.06 +2018-12-21,1.02 +2018-12-28,1.01 +2019-01-04,0.93 +2019-01-11,0.91 +2019-01-18,0.93 +2019-01-25,0.96 +2019-02-01,0.87 +2019-02-08,0.83 +2019-02-15,0.83 +2019-02-22,0.77 +2019-03-01,0.76 +2019-03-08,0.76 +2019-03-15,0.69 +2019-03-22,0.60 +2019-03-29,0.55 +2019-04-05,0.60 +2019-04-12,0.59 +2019-04-19,0.63 +2019-04-26,0.59 +2019-05-03,0.60 +2019-05-10,0.60 +2019-05-17,0.56 +2019-05-24,0.59 +2019-05-31,0.48 +2019-06-07,0.37 +2019-06-14,0.43 +2019-06-21,0.37 +2019-06-28,0.33 +2019-07-05,0.33 +2019-07-12,0.35 +2019-07-19,0.29 +2019-07-26,0.27 +2019-08-02,0.25 +2019-08-09,0.10 +2019-08-16,0.02 +2019-08-23,0.03 +2019-08-30,-0.06 +2019-09-06,-0.01 +2019-09-13,0.14 +2019-09-20,0.17 +2019-09-27,0.12 +2019-10-04,0.09 +2019-10-11,0.11 +2019-10-18,0.18 +2019-10-25,0.16 +2019-11-01,0.19 +2019-11-08,0.19 +2019-11-15,0.21 +2019-11-22,0.15 +2019-11-29,0.14 +2019-12-06,0.13 +2019-12-13,0.14 +2019-12-20,0.15 +2019-12-27,0.16 +2020-01-03,0.10 +2020-01-10,0.09 +2020-01-17,0.07 +2020-01-24,0.04 +2020-01-31,-0.05 +2020-02-07,-0.04 +2020-02-14,-0.07 +2020-02-21,-0.11 +2020-02-28,-0.23 +2020-03-06,-0.45 +2020-03-13,-0.17 +2020-03-20,0.35 +2020-03-27,-0.16 +2020-04-03,-0.31 +2020-04-10,-0.45 +2020-04-17,-0.47 +2020-04-24,-0.41 +2020-05-01,-0.48 +2020-05-08,-0.43 +2020-05-15,-0.42 +2020-05-22,-0.46 +2020-05-29,-0.47 +2020-06-05,-0.44 +2020-06-12,-0.48 +2020-06-19,-0.55 +2020-06-26,-0.65 +2020-07-03,-0.70 +2020-07-10,-0.76 +2020-07-17,-0.79 +2020-07-24,-0.88 +2020-07-31,-0.95 +2020-08-07,-1.05 +2020-08-14,-0.98 +2020-08-21,-0.99 +2020-08-28,-1.02 +2020-09-04,-1.05 +2020-09-11,-1.00 +2020-09-18,-0.98 +2020-09-25,-0.93 +2020-10-02,-0.95 +2020-10-09,-0.92 +2020-10-16,-0.96 +2020-10-23,-0.91 +2020-10-30,-0.88 +2020-11-06,-0.84 +2020-11-13,-0.80 +2020-11-20,-0.83 +2020-11-27,-0.87 +2020-12-04,-0.92 +2020-12-11,-0.96 +2020-12-18,-0.99 +2020-12-25,-1.01 +2021-01-01,-1.04 +2021-01-08,-1.02 +2021-01-15,-0.95 +2021-01-22,-1.00 +2021-01-29,-1.04 +2021-02-05,-1.03 +2021-02-12,-1.04 +2021-02-19,-0.88 +2021-02-26,-0.74 +2021-03-05,-0.71 +2021-03-12,-0.67 +2021-03-19,-0.63 +2021-03-26,-0.67 +2021-04-02,-0.64 +2021-04-09,-0.65 +2021-04-16,-0.71 +2021-04-23,-0.75 +2021-04-30,-0.77 +2021-05-07,-0.85 +2021-05-14,-0.88 +2021-05-21,-0.83 +2021-05-28,-0.83 +2021-06-04,-0.83 +2021-06-11,-0.84 +2021-06-18,-0.80 +2021-06-25,-0.81 +2021-07-02,-0.86 +2021-07-09,-0.93 +2021-07-16,-0.98 +2021-07-23,-1.02 +2021-07-30,-1.14 +2021-08-06,-1.14 +2021-08-13,-1.06 +2021-08-20,-1.05 +2021-08-27,-1.02 +2021-09-03,-1.04 +2021-09-10,-1.04 +2021-09-17,-1.02 +2021-09-24,-0.93 +2021-10-01,-0.86 +2021-10-08,-0.89 +2021-10-15,-0.96 +2021-10-22,-0.94 +2021-10-29,-1.03 +2021-11-05,-0.98 +2021-11-12,-1.14 +2021-11-19,-1.12 +2021-11-26,-0.99 +2021-12-03,-1.04 +2021-12-10,-0.99 +2021-12-17,-0.96 +2021-12-24,-0.98 +2021-12-31,-1.02 +2022-01-07,-0.83 +2022-01-14,-0.72 +2022-01-21,-0.56 +2022-01-28,-0.61 +2022-02-04,-0.59 +2022-02-11,-0.47 +2022-02-18,-0.46 +2022-02-25,-0.55 +2022-03-04,-0.86 +2022-03-11,-0.95 +2022-03-18,-0.70 +2022-03-25,-0.56 +2022-04-01,-0.48 +2022-04-08,-0.24 diff --git a/code/py/ez_utility.py b/code/py/ez_utility.py new file mode 100644 index 0000000..9a6a28b --- /dev/null +++ b/code/py/ez_utility.py @@ -0,0 +1,137 @@ +""" +Epstein--Zin utility: solving the recursion for a given consumption +path. + +""" + +from quantecon import compute_fixed_point +from quantecon.markov import tauchen + +import numpy as np +from numba import njit +from collections import namedtuple + + +# NamedTuple Model +Model = namedtuple("Model", ("β", "ρ", "σ", "α", "γ", "c", "x_vals", "P")) + + +def create_ez_utility_model( + n=200, # size of state space + ρ=0.96, # correlation coef in AR(1) + σ=0.1, # volatility + β=0.99, # time discount factor + α=0.75, # EIS parameter + γ=-2.0): # risk aversion parameter + mc = tauchen(n, ρ, σ, 0, 5) + x_vals, P = mc.state_values, mc.P + c = np.exp(x_vals) + return Model(β=β, ρ=ρ, σ=σ, α=α, γ=γ, c=c, x_vals=x_vals, P=P) + + +@njit +def K(v, model): + β, ρ, σ, α, γ, c, x_vals, P = model + R = np.dot(P, v**γ)**(1/γ) + return ((1 - β) * c**α + β * R**α)**(1/α) + + +def compute_ez_utility(model): + v_init = np.ones(len(model.x_vals)) + v_star = compute_fixed_point(lambda v: K(v, model), v_init, + error_tol=1e-6, max_iter=1000, print_skip=100) + return v_star + + + +# Plots + + +import matplotlib.pyplot as plt + + +def plot_convergence(savefig=False, + num_iter=100, + figname="./figures/ez_utility_c.pdf"): + + fig, ax = plt.subplots(figsize=(10, 5.2)) + model = create_ez_utility_model() + β, ρ, σ, α, γ, c, x_vals, P = model + + + v_star = compute_ez_utility(model) + v = 0.1 * v_star + ax.plot(x_vals, v, "k-", linewidth=3, alpha=0.7, label=r"$v_0$") + + greys = [str(g) for g in np.linspace(0.0, 0.4, num_iter)] + greys.reverse() + + for g in greys: + ax.plot(x_vals, v, "k-", linewidth=1, alpha=0.7) + for t in range(20): + v = K(v, model) + + v_star = compute_ez_utility(model) + ax.plot(x_vals, v_star, linewidth=3, alpha=0.7, label=r"$v^*$") + ax.set_xlabel(r"$x$") + + ax.legend(frameon=False, loc="upper left") + plt.show() + if savefig: + fig.savefig(figname) + + +def plot_v(savefig=False, + figname="./figures/ez_utility_1.pdf"): + + fig, ax = plt.subplots(figsize=(10, 5.2)) + model = create_ez_utility_model() + β, ρ, σ, α, γ, c, x_vals, P = model + v_star = compute_ez_utility(model) + ax.plot(x_vals, v_star, linewidth=2, alpha=0.7, label=r"$v^*$") + ax.set_xlabel(r"$x$") + + ax.legend(frameon=False, loc="upper left") + plt.show() + if savefig: + fig.savefig(figname) + + +def vary_gamma(gamma_vals=[1.0, -8.0], + savefig=False, + figname="./figures/ez_utility_2.pdf"): + + fig, ax = plt.subplots(figsize=(10, 5.2)) + + for γ in gamma_vals: + model = create_ez_utility_model(γ=γ) + β, ρ, σ, α, γ, c, x_vals, P = model + v_star = compute_ez_utility(model) + ax.plot(x_vals, v_star, linewidth=2, alpha=0.7, label=r"$\gamma=$" + f"{γ}") + ax.set_xlabel(r"$x$") + ax.set_ylabel(r"$v(x)$") + + ax.legend(frameon=False, loc="upper left") + plt.show() + if savefig: + fig.savefig(figname) + + +def vary_alpha(alpha_vals=[0.5, 0.6], + savefig=False, + figname="./figures/ez_utility_3.pdf"): + + fig, ax = plt.subplots(figsize=(10, 5.2)) + + for α in alpha_vals: + model = create_ez_utility_model(α=α) + β, ρ, σ, α, γ, c, x_vals, P = model + v_star = compute_ez_utility(model) + ax.plot(x_vals, v_star, linewidth=2, alpha=0.7, label=r"$\alpha=$"+f"{α}") + ax.set_xlabel(r"$x$") + ax.set_ylabel(r"$v(x)$") + + ax.legend(frameon=False, loc="upper left") + plt.show() + if savefig: + fig.savefig(figname) diff --git a/code/py/finite_lq.py b/code/py/finite_lq.py new file mode 100644 index 0000000..4039d9c --- /dev/null +++ b/code/py/finite_lq.py @@ -0,0 +1,248 @@ +from quantecon import compute_fixed_point +from quantecon.markov import tauchen, MarkovChain + +import numpy as np +from collections import namedtuple +from numba import njit, prange +import time + + +# NamedTuple Model +Model = namedtuple("Model", ("β", "a_0", "a_1", "γ", "c", + "y_grid", "z_grid", "Q")) + + +def create_investment_model( + r=0.04, # Interest rate + a_0=10.0, a_1=1.0, # Demand parameters + γ=25.0, c=1.0, # Adjustment and unit cost + y_min=0.0, y_max=20.0, y_size=100, # Grid for output + ρ=0.9, ν=1.0, # AR(1) parameters + z_size=25): # Grid size for shock + β = 1/(1+r) + y_grid = np.linspace(y_min, y_max, y_size) + mc = tauchen(y_size, ρ, ν) + z_grid, Q = mc.state_values, mc.P + return Model(β=β, a_0=a_0, a_1=a_1, γ=γ, c=c, + y_grid=y_grid, z_grid=z_grid, Q=Q) + + +@njit +def B(i, j, k, v, model): + """ + The aggregator B is given by + + B(y, z, y′) = r(y, z, y′) + β Σ_z′ v(y′, z′) Q(z, z′)." + + where + + r(y, z, y′) := (a_0 - a_1 * y + z - c) y - γ * (y′ - y)^2 + + """ + β, a_0, a_1, γ, c, y_grid, z_grid, Q = model + y, z, y_1 = y_grid[i], z_grid[j], y_grid[k] + r = (a_0 - a_1 * y + z - c) * y - γ * (y_1 - y)**2 + return r + β * np.dot(v[k, :], Q[j, :]) + + +@njit(parallel=True) +def T_σ(v, σ, model): + """The policy operator.""" + v_new = np.empty_like(v) + for i in prange(len(model.y_grid)): + for j in prange(len(model.z_grid)): + v_new[i, j] = B(i, j, σ[i, j], v, model) + return v_new + + +@njit(parallel=True) +def T(v, model): + """The Bellman operator.""" + v_new = np.empty_like(v) + for i in prange(len(model.y_grid)): + for j in prange(len(model.z_grid)): + tmp = np.array([B(i, j, k, v, model) for k + in np.arange(len(model.y_grid))]) + v_new[i, j] = np.max(tmp) + return v_new + + +@njit(parallel=True) +def get_greedy(v, model): + """Compute a v-greedy policy.""" + n, m = len(model.y_grid), len(model.z_grid) + σ = np.empty((n, m), dtype=np.int32) + for i in prange(n): + for j in prange(m): + tmp = np.array([B(i, j, k, v, model) for k + in np.arange(n)]) + σ[i, j] = np.argmax(tmp) + return σ + + + +def value_iteration(model, tol=1e-5): + """Value function iteration routine.""" + vz = np.zeros((len(model.y_grid), len(model.z_grid))) + v_star = compute_fixed_point(lambda v: T(v, model), vz, + error_tol=tol, max_iter=1000, print_skip=25) + return get_greedy(v_star, model) + + +@njit +def single_to_multi(m, zn): + # Function to extract (i, j) from m = i + (j-1)*zn + return (m//zn, m%zn) + + +@njit(parallel=True) +def get_value(σ, model): + """Get the value v_σ of policy σ.""" + # Unpack and set up + β, a_0, a_1, γ, c, y_grid, z_grid, Q = model + yn, zn = len(y_grid), len(z_grid) + n = yn * zn + # Allocate and create single index versions of P_σ and r_σ + P_σ = np.zeros((n, n)) + r_σ = np.zeros(n) + for m in prange(n): + i, j = single_to_multi(m, zn) + y, z, y_1 = y_grid[i], z_grid[j], y_grid[σ[i, j]] + r_σ[m] = (a_0 - a_1 * y + z - c) * y - γ * (y_1 - y)**2 + for m_1 in prange(n): + i_1, j_1 = single_to_multi(m_1, zn) + if i_1 == σ[i, j]: + P_σ[m, m_1] = Q[j, j_1] + + I = np.identity(n) + # Solve for the value of σ + v_σ = np.linalg.solve((I - β * P_σ), r_σ) + # Return as multi-index array + v_σ = v_σ.reshape(yn, zn) + return v_σ + + +@njit +def policy_iteration(model): + """Howard policy iteration routine.""" + yn, zn = len(model.y_grid), len(model.z_grid) + σ = np.ones((yn, zn), dtype=np.int32) + i, error = 0, 1.0 + while error > 0: + v_σ = get_value(σ, model) + σ_new = get_greedy(v_σ, model) + error = np.max(np.abs(σ_new - σ)) + σ = σ_new + i = i + 1 + print(f"Concluded loop {i} with error: {error}.") + return σ + + +@njit +def optimistic_policy_iteration(model, tol=1e-5, m=100): + """Optimistic policy iteration routine.""" + v = np.zeros((len(model.y_grid), len(model.z_grid))) + error = tol + 1 + while error > tol: + last_v = v + σ = get_greedy(v, model) + for i in range(m): + v = T_σ(v, σ, model) + error = np.max(np.abs(v - last_v)) + return get_greedy(v, model) + + +# Plots + +import matplotlib.pyplot as plt + + +def plot_policy(savefig=False, figname="./figures/finite_lq_0.pdf"): + model = create_investment_model() + β, a_0, a_1, γ, c, y_grid, z_grid, Q = model + σ_star = optimistic_policy_iteration(model) + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(y_grid, y_grid, "k--", label=r"$45$") + ax.plot(y_grid, y_grid[σ_star[:, 0]], label=r"$\sigma^*(\cdot, z_1)$") + ax.plot(y_grid, y_grid[σ_star[:, -1]], label="$\sigma^*(\cdot, z_N)$") + ax.legend() + plt.show() + if savefig: + fig.savefig(figname) + + +def plot_sim(savefig=False, figname="./figures/finite_lq_1.pdf"): + ts_length = 200 + + fig, axes = plt.subplots(4, 1, figsize=(9, 11.2)) + + for (ax, γ) in zip(axes, (1, 10, 20, 30)): + model = create_investment_model(γ=γ) + β, a_0, a_1, γ, c, y_grid, z_grid, Q = model + σ_star = optimistic_policy_iteration(model) + mc = MarkovChain(Q, z_grid) + + z_sim_idx = mc.simulate_indices(ts_length) + z_sim = z_grid[z_sim_idx] + + y_sim_idx = np.empty(ts_length, dtype=np.int32) + y_1 = (a_0 - c + z_sim[1]) / (2 * a_1) + + y_sim_idx[0] = np.searchsorted(y_grid, y_1) + for t in range(ts_length-1): + y_sim_idx[t+1] = σ_star[y_sim_idx[t], z_sim_idx[t]] + y_sim = y_grid[y_sim_idx] + y_bar_sim = (a_0 - c + z_sim) / (2 * a_1) + + ax.plot(np.arange(1, ts_length+1), y_sim, label=r"$Y_t$") + ax.plot(np.arange(1, ts_length+1), y_bar_sim, label=r"$\bar Y_t$") + ax.legend(frameon=False, loc="upper right") + ax.set_ylabel("output") + ax.set_ylim(1, 9) + ax.set_title(r"$\gamma = $" + f"{γ}") + + fig.tight_layout() + plt.show() + if savefig: + fig.savefig(figname) + + +def plot_timing(m_vals=np.arange(1, 601, 10), + savefig=False, + figname="./figures/finite_lq_time.pdf" + ): + # NOTE: Uncomment the following lines in this function to + # include Policy iteration plot + model = create_investment_model() + # print("Running Howard policy iteration.") + # t1 = time.time() + # σ_pi = policy_iteration(model) + # pi_time = time.time() - t1 + # print(f"PI completed in {pi_time} seconds.") + print("Running value function iteration.") + t1 = time.time() + σ_vfi = value_iteration(model) + vfi_time = time.time() - t1 + print(f"VFI completed in {vfi_time} seconds.") + opi_times = [] + for m in m_vals: + print(f"Running optimistic policy iteration with m={m}.") + t1 = time.time() + σ_opi = optimistic_policy_iteration(model, m=m, tol=1e-5) + t2 = time.time() + print(f"OPI with m={m} completed in {t2-t1} seconds.") + opi_times.append(t2-t1) + + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(m_vals, [vfi_time]*len(m_vals), + linewidth=2, label="value function iteration") + # ax.plot(m_vals, [pi_time]*len(m_vals), + # linewidth=2, label="Howard policy iteration") + ax.plot(m_vals, opi_times, linewidth=2, label="optimistic policy iteration") + ax.legend(frameon=False) + ax.set_xlabel(r"$m$") + ax.set_ylabel("time") + plt.show() + if savefig: + fig.savefig(figname) + return (vfi_time, opi_times) diff --git a/code/py/finite_opt_saving_0.py b/code/py/finite_opt_saving_0.py new file mode 100644 index 0000000..41bbca8 --- /dev/null +++ b/code/py/finite_opt_saving_0.py @@ -0,0 +1,60 @@ +from quantecon.markov import tauchen +import numpy as np +from collections import namedtuple +from numba import njit, prange + + +# NamedTuple Model +Model = namedtuple("Model", ("β", "R", "γ", "w_grid", "y_grid", "Q")) + + +def create_savings_model(R=1.01, β=0.98, γ=2.5, + w_min=0.01, w_max=20.0, w_size=200, + ρ=0.9, ν=0.1, y_size=5): + w_grid = np.linspace(w_min, w_max, w_size) + mc = tauchen(y_size, ρ, ν) + y_grid, Q = np.exp(mc.state_values), mc.P + return Model(β=β, R=R, γ=γ, w_grid=w_grid, y_grid=y_grid, Q=Q) + + +@njit +def U(c, γ): + return c**(1-γ)/(1-γ) + + +@njit +def B(i, j, k, v, model): + """ + B(w, y, w′, v) = u(R*w + y - w′) + β Σ_y′ v(w′, y′) Q(y, y′). + """ + β, R, γ, w_grid, y_grid, Q = model + w, y, w_1 = w_grid[i], y_grid[j], w_grid[k] + c = w + y - (w_1 / R) + value = -np.inf + if c > 0: + value = U(c, γ) + β * np.dot(v[k, :], Q[j, :]) + return value + + +@njit(parallel=True) +def T(v, model): + """The Bellman operator.""" + β, R, γ, w_grid, y_grid, Q = model + v_new = np.empty_like(v) + for i in prange(w_grid.shape[0]): + for j in prange(y_grid.shape[0]): + x_tmp = np.array([B(i, j, k, v, model) for k + in np.arange(w_grid.shape[0])]) + v_new[i, j] = np.max(x_tmp) + return v_new + + +@njit(parallel=True) +def T_σ(v, σ, model): + """The policy operator.""" + β, R, γ, w_grid, y_grid, Q = model + v_new = np.empty_like(v) + for i in prange(w_grid.shape[0]): + for j in prange(y_grid.shape[0]): + v_new[i, j] = B(i, j, σ[i, j], v, model) + return v_new diff --git a/code/py/finite_opt_saving_1.py b/code/py/finite_opt_saving_1.py new file mode 100644 index 0000000..da15753 --- /dev/null +++ b/code/py/finite_opt_saving_1.py @@ -0,0 +1,52 @@ +import numpy as np +from finite_opt_saving_0 import U, B +from numba import njit, prange + +@njit(parallel=True) +def get_greedy(v, model): + """Compute a v-greedy policy.""" + β, R, γ, w_grid, y_grid, Q = model + σ = np.empty((w_grid.shape[0], y_grid.shape[0]), dtype=np.int32) + for i in prange(w_grid.shape[0]): + for j in range(y_grid.shape[0]): + x_tmp = np.array([B(i, j, k, v, model) for k in + np.arange(w_grid.shape[0])]) + σ[i, j] = np.argmax(x_tmp) + return σ + + +@njit +def single_to_multi(m, yn): + # Function to extract (i, j) from m = i + (j-1)*yn + return (m//yn, m%yn) + +@njit(parallel=True) +def get_value(σ, model): + """Get the value v_σ of policy σ.""" + # Unpack and set up + β, R, γ, w_grid, y_grid, Q = model + wn, yn = len(w_grid), len(y_grid) + n = wn * yn + # Build P_σ and r_σ as multi-index arrays + P_σ = np.zeros((wn, yn, wn, yn)) + r_σ = np.zeros((wn, yn)) + for i in range(wn): + for j in range(yn): + w, y, w_1 = w_grid[i], y_grid[j], w_grid[σ[i, j]] + r_σ[i, j] = U(w + y - w_1/R, γ) + for i_1 in range(wn): + for j_1 in range(yn): + if i_1 == σ[i, j]: + P_σ[i, j, i_1, j_1] = Q[j, j_1] + + # Solve for the value of σ + P_σ = P_σ.reshape(n, n) + r_σ = r_σ.reshape(n) + + I = np.identity(n) + v_σ = np.linalg.solve((I - β * P_σ), r_σ) + # Return as multi-index array + v_σ = v_σ.reshape(wn, yn) + return v_σ + + diff --git a/code/py/finite_opt_saving_2.py b/code/py/finite_opt_saving_2.py new file mode 100644 index 0000000..72ae431 --- /dev/null +++ b/code/py/finite_opt_saving_2.py @@ -0,0 +1,183 @@ +from quantecon import compute_fixed_point + +import numpy as np +from numba import njit +import time +from finite_opt_saving_1 import get_greedy, get_value +from finite_opt_saving_0 import create_savings_model, T, T_σ +from quantecon import MarkovChain + + +def value_iteration(model, tol=1e-5): + """Value function iteration routine.""" + vz = np.zeros((len(model.w_grid), len(model.y_grid))) + v_star = compute_fixed_point(lambda v: T(v, model), vz, + error_tol=tol, max_iter=1000, print_skip=25) + return get_greedy(v_star, model) + + +@njit(cache=True, fastmath=True) +def policy_iteration(model): + """Howard policy iteration routine.""" + wn, yn = len(model.w_grid), len(model.y_grid) + σ = np.ones((wn, yn), dtype=np.int32) + i, error = 0, 1.0 + while error > 0: + v_σ = get_value(σ, model) + σ_new = get_greedy(v_σ, model) + error = np.max(np.abs(σ_new - σ)) + σ = σ_new + i = i + 1 + print(f"Concluded loop {i} with error: {error}.") + return σ + + +@njit +def optimistic_policy_iteration(model, tolerance=1e-5, m=100): + """Optimistic policy iteration routine.""" + v = np.zeros((len(model.w_grid), len(model.y_grid))) + error = tolerance + 1 + while error > tolerance: + last_v = v + σ = get_greedy(v, model) + for i in range(0, m): + v = T_σ(v, σ, model) + error = np.max(np.abs(v - last_v)) + return get_greedy(v, model) + +# Simulations and inequality measures + +def simulate_wealth(m): + + model = create_savings_model() + σ_star = optimistic_policy_iteration(model) + β, R, γ, w_grid, y_grid, Q = model + + # Simulate labor income (indices rather than grid values) + mc = MarkovChain(Q) + y_idx_series = mc.simulate(ts_length=m) + + # Compute corresponding wealth time series + w_idx_series = np.empty_like(y_idx_series) + w_idx_series[0] = 1 # initial condition + for t in range(m-1): + i, j = w_idx_series[t], y_idx_series[t] + w_idx_series[t+1] = σ_star[i, j] + w_series = w_grid[w_idx_series] + + return w_series + +def lorenz(v): # assumed sorted vector + S = np.cumsum(v) # cumulative sums: [v[1], v[1] + v[2], ... ] + F = np.arange(1, len(v) + 1) / len(v) + L = S / S[-1] + return (F, L) # returns named tuple + +gini = lambda v: (2 * sum(i * y for (i, y) in enumerate(v))/sum(v) - (len(v) + 1))/len(v) + +# Plots + +import matplotlib.pyplot as plt + + +def plot_timing(m_vals=np.arange(1, 601, 10), + savefig=False): + model = create_savings_model(y_size=5) + print("Running Howard policy iteration.") + t1 = time.time() + σ_pi = policy_iteration(model) + pi_time = time.time() - t1 + print(f"PI completed in {pi_time} seconds.") + print("Running value function iteration.") + t1 = time.time() + σ_vfi = value_iteration(model) + vfi_time = time.time() - t1 + print(f"VFI completed in {vfi_time} seconds.") + + assert np.allclose(σ_vfi, σ_pi), "Warning: policies deviated." + + opi_times = [] + for m in m_vals: + print(f"Running optimistic policy iteration with m={m}.") + t1 = time.time() + σ_opi = optimistic_policy_iteration(model, m=m) + t2 = time.time() + assert np.allclose(σ_opi, σ_pi), "Warning: policies deviated." + print(f"OPI with m={m} completed in {t2-t1} seconds.") + opi_times.append(t2-t1) + + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(m_vals, [vfi_time]*len(m_vals), + linewidth=2, label="value function iteration") + ax.plot(m_vals, [pi_time]*len(m_vals), + linewidth=2, label="Howard policy iteration") + ax.plot(m_vals, opi_times, linewidth=2, + label="optimistic policy iteration") + ax.legend(frameon=False) + ax.set_xlabel(r"$m$") + ax.set_ylabel("time") + plt.show() + if savefig: + fig.savefig("./figures/finite_opt_saving_2_1.png") + return (pi_time, vfi_time, opi_times) + + +def plot_policy(method="pi", savefig=False): + model = create_savings_model() + β, R, γ, w_grid, y_grid, Q = model + if method == "vfi": + σ_star = value_iteration(model) + elif method == "pi": + σ_star = policy_iteration(model) + else: + method = "OPT" + σ_star = optimistic_policy_iteration(model) + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(w_grid, w_grid, "k--", label=r"$45$") + ax.plot(w_grid, w_grid[σ_star[:, 0]], label=r"$\sigma^*(\cdot, y_1)$") + ax.plot(w_grid, w_grid[σ_star[:, -1]], label=r"$\sigma^*(\cdot, y_N)$") + ax.legend() + plt.title(f"Method: {method}") + plt.show() + if savefig: + fig.savefig(f"./figures/finite_opt_saving_2_2_{method}.png") + +def plot_time_series(m=2_000, savefig=False): + + w_series = simulate_wealth(m) + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(w_series, label="w_t") + ax.set_xlabel("time") + ax.legend() + plt.show() + if savefig: + fig.savefig("./figures/finite_opt_saving_ts.pdf") + +def plot_histogram(m=1_000_000, savefig=False): + + w_series = simulate_wealth(m) + w_series.sort() + g = round(gini(w_series), ndigits=2) + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.hist(w_series, bins=40, density=True) + ax.set_xlabel("wealth") + ax.text(15, 0.4, f"Gini = {g}") + plt.show() + + if savefig: + fig.savefig("./figures/finite_opt_saving_hist.pdf") + +def plot_lorenz(m=1_000_000, savefig=False): + + w_series = simulate_wealth(m) + w_series.sort() + (F, L) = lorenz(w_series) + + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(F, F, label="Lorenz curve, equality") + ax.plot(F, L, label="Lorenz curve, wealth distribution") + ax.legend() + plt.show() + + if savefig: + fig.savefig("./figures/finite_opt_saving_lorenz.pdf") diff --git a/code/py/firm_exit.py b/code/py/firm_exit.py new file mode 100644 index 0000000..38730c1 --- /dev/null +++ b/code/py/firm_exit.py @@ -0,0 +1,112 @@ +""" +Firm valuation with exit option. + +""" + +from quantecon.markov import tauchen +from quantecon import compute_fixed_point + +import numpy as np +from collections import namedtuple +from numba import njit + + +# NamedTuple Model +Model = namedtuple("Model", ("n", "z_vals", "Q", "β", "s")) + + +def create_exit_model( + n=200, # productivity grid size + ρ=0.95, μ=0.1, ν=0.1, # persistence, mean and volatility + β=0.98, s=100.0 # discount factor and scrap value + ): + """ + Creates an instance of the firm exit model. + """ + mc = tauchen(n, ρ, ν, mu=μ) + z_vals, Q = mc.state_values, mc.P + return Model(n=n, z_vals=z_vals, Q=Q, β=β, s=s) + + +@njit +def no_exit_value(model): + """Compute value of firm without exit option.""" + n, z_vals, Q, β, s = model + I = np.identity(n) + return np.linalg.solve((I - β * Q), z_vals) + + +@njit +def T(v, model): + """The Bellman operator Tv = max{s, π + β Q v}.""" + n, z_vals, Q, β, s = model + h = z_vals + β * np.dot(Q, v) + return np.maximum(s, h) + + +@njit +def get_greedy(v, model): + """Get a v-greedy policy.""" + n, z_vals, Q, β, s = model + σ = s >= z_vals + β * np.dot(Q, v) + return σ + + +def vfi(model): + """Solve by VFI.""" + v_init = no_exit_value(model) + v_star = compute_fixed_point(lambda v: T(v, model), v_init, error_tol=1e-6, + max_iter=1000, print_skip=25) + σ_star = get_greedy(v_star, model) + return v_star, σ_star + + +# Plots + + +import matplotlib.pyplot as plt + + +def plot_val(savefig=False, + figname="./figures/firm_exit_1.pdf"): + + fig, ax = plt.subplots(figsize=(9, 5.2)) + + model = create_exit_model() + n, z_vals, Q, β, s = model + + v_star, σ_star = vfi(model) + h = z_vals + β * np.dot(Q, v_star) + + ax.plot(z_vals, h, "-", linewidth=3, alpha=0.6, label=r"$h^*$") + ax.plot(z_vals, s * np.ones(n), "-", linewidth=3, alpha=0.6, label=r"$s$") + ax.plot(z_vals, v_star, "k--", linewidth=1.5, alpha=0.8, label=r"$v^*$") + + ax.legend(frameon=False) + ax.set_xlabel(r"$z$") + + plt.show() + if savefig: + fig.savefig(figname) + + +def plot_comparison(savefig=False, + figname="./figures/firm_exit_2.pdf"): + + fig, ax = plt.subplots(figsize=(9, 5.2)) + + model = create_exit_model() + n, z_vals, Q, β, s = model + + v_star, σ_star = vfi(model) + w = no_exit_value(model) + + ax.plot(z_vals, v_star, "k-", linewidth=2, alpha=0.6, label="$v^*$") + ax.plot(z_vals, w, linewidth=2, alpha=0.6, label="no-exit value") + + ax.legend(frameon=False) + ax.set_xlabel(r"$z$") + + plt.show() + if savefig: + fig.savefig(figname) diff --git a/code/py/firm_hiring.py b/code/py/firm_hiring.py new file mode 100644 index 0000000..43b246c --- /dev/null +++ b/code/py/firm_hiring.py @@ -0,0 +1,172 @@ +import numpy as np +from quantecon.markov import tauchen, MarkovChain + +from collections import namedtuple +from numba import njit, prange + + +# NamedTuple Model +Model = namedtuple("Model", ("β", "κ", "α", "p", "w", "l_grid", + "z_grid", "Q")) + + +def create_hiring_model( + r=0.04, # Interest rate + κ=1.0, # Adjustment cost + α=0.4, # Production parameter + p=1.0, w=1.0, # Price and wage + l_min=0.0, l_max=30.0, l_size=100, # Grid for labor + ρ=0.9, ν=0.4, b=1.0, # AR(1) parameters + z_size=100): # Grid size for shock + β = 1/(1+r) + l_grid = np.linspace(l_min, l_max, l_size) + mc = tauchen(z_size, ρ, ν, b, 6) + z_grid, Q = mc.state_values, mc.P + return Model(β=β, κ=κ, α=α, p=p, w=w, + l_grid=l_grid, z_grid=z_grid, Q=Q) + + +@njit +def B(i, j, k, v, model): + """ + The aggregator B is given by + + B(l, z, l′) = r(l, z, l′) + β Σ_z′ v(l′, z′) Q(z, z′)." + + where + + r(l, z, l′) := p * z * f(l) - w * l - κ 1{l != l′} + + """ + β, κ, α, p, w, l_grid, z_grid, Q = model + l, z, l_1 = l_grid[i], z_grid[j], l_grid[k] + r = p * z * l**α - w * l - κ * (l != l_1) + return r + β * np.dot(v[k, :], Q[j, :]) + + +@njit(parallel=True) +def T_σ(v, σ, model): + """The policy operator.""" + v_new = np.empty_like(v) + for i in prange(len(model.l_grid)): + for j in prange(len(model.z_grid)): + v_new[i, j] = B(i, j, σ[i, j], v, model) + return v_new + + +@njit(parallel=True) +def get_greedy(v, model): + """Compute a v-greedy policy.""" + β, κ, α, p, w, l_grid, z_grid, Q = model + n, m = len(l_grid), len(z_grid) + σ = np.empty((n, m), dtype=np.int32) + for i in prange(n): + for j in prange(m): + tmp = np.array([B(i, j, k, v, model) for k + in np.arange(n)]) + σ[i, j] = np.argmax(tmp) + return σ + + +@njit +def optimistic_policy_iteration(model, tolerance=1e-5, m=100): + """Optimistic policy iteration routine.""" + v = np.zeros((len(model.l_grid), len(model.z_grid))) + error = tolerance + 1 + while error > tolerance: + last_v = v + σ = get_greedy(v, model) + for i in range(m): + v = T_σ(v, σ, model) + error = np.max(np.abs(v - last_v)) + return get_greedy(v, model) + + +# Plots + +import matplotlib.pyplot as plt + + +def plot_policy(savefig=False, + figname="./figures/firm_hiring_pol.pdf"): + model = create_hiring_model() + β, κ, α, p, w, l_grid, z_grid, Q = model + σ_star = optimistic_policy_iteration(model) + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(l_grid, l_grid, "k--", label=r"$45$") + ax.plot(l_grid, l_grid[σ_star[:, 0]], label=r"$\sigma^*(\cdot, z_1)$") + ax.plot(l_grid, l_grid[σ_star[:, -1]], label=r"$\sigma^*(\cdot, z_N)$") + ax.legend() + plt.show() + if savefig: + fig.savefig(figname) + + +def sim_dynamics(model, ts_length): + β, κ, α, p, w, l_grid, z_grid, Q = model + σ_star = optimistic_policy_iteration(model) + mc = MarkovChain(Q, z_grid) + z_sim_idx = mc.simulate_indices(ts_length) + z_sim = z_grid[z_sim_idx] + l_sim_idx = np.empty(ts_length, dtype=np.int32) + l_sim_idx[0] = 32 + for t in range(ts_length-1): + l_sim_idx[t+1] = σ_star[l_sim_idx[t], z_sim_idx[t]] + l_sim = l_grid[l_sim_idx] + + y_sim = np.empty_like(l_sim) + for (i, l) in enumerate(l_sim): + y_sim[i] = p * z_sim[i] * l_sim[i]**α + + t = ts_length - 1 + l_g, y_g, z_g = np.zeros(t), np.zeros(t), np.zeros(t) + + for i in range(t): + l_g[i] = (l_sim[i+1] - l_sim[i]) / l_sim[i] + y_g[i] = (y_sim[i+1] - y_sim[i]) / y_sim[i] + z_g[i] = (z_sim[i+1] - z_sim[i]) / z_sim[i] + + return l_sim, y_sim, z_sim, l_g, y_g, z_g + + +def plot_sim(savefig=False, + figname="./figures/firm_hiring_ts.pdf", + ts_length = 250): + model = create_hiring_model() + β, κ, α, p, w, l_grid, z_grid, Q = model + l_sim, y_sim, z_sim, l_g, y_g, z_g = sim_dynamics(model, ts_length) + fig, ax = plt.subplots(figsize=(9, 5.2)) + x_grid = np.arange(ts_length) + ax.plot(x_grid, l_sim, label=r"$\ell_t$") + ax.plot(x_grid, z_sim, alpha=0.6, label=r"$Z_t$") + ax.legend(frameon=False) + ax.set_ylabel("employment") + ax.set_xlabel("time") + + plt.show() + if savefig: + fig.savefig(figname) + + +def plot_growth(savefig=False, + figname="./figures/firm_hiring_g.pdf", + ts_length = 10_000_000): + + model = create_hiring_model() + β, κ, α, p, w, l_grid, z_grid, Q = model + l_sim, y_sim, z_sim, l_g, y_g, z_g = sim_dynamics(model, ts_length) + + fig, ax = plt.subplots() + ax.hist(l_g, alpha=0.6, bins=100) + ax.set_xlabel("growth") + + #fig, axes = plt.subplots(2, 1) + #series = y_g, z_g + #for (ax, g) in zip(axes, series): + # ax.hist(g, alpha=0.6, bins=100) + # ax.set_xlabel("growth") + + plt.tight_layout() + plt.show() + if savefig: + fig.savefig(figname) diff --git a/code/py/iid_job_search.py b/code/py/iid_job_search.py new file mode 100644 index 0000000..b0ff361 --- /dev/null +++ b/code/py/iid_job_search.py @@ -0,0 +1,106 @@ +""" +VFI approach to job search in the infinite-horizon IID case. + +""" + +from quantecon import compute_fixed_point + +from two_period_job_search import create_job_search_model + +from numba import njit +import numpy as np + + +# A model with default parameters +default_model = create_job_search_model() + + +@njit +def T(v, model): + """ The Bellman operator. """ + n, w_vals, φ, β, c = model + return np.array([np.maximum(w / (1 - β), + c + β * np.sum(v * φ)) for w in w_vals]) + + +@njit +def get_greedy(v, model): + """ Get a v-greedy policy. """ + n, w_vals, φ, β, c = model + σ = w_vals / (1 - β) >= c + β * np.sum(v * φ) # Boolean policy vector + return σ + + +def vfi(model=default_model): + """ Solve the infinite-horizon IID job search model by VFI. """ + v_init = np.zeros_like(model.w_vals) + v_star = compute_fixed_point(lambda v: T(v, model), v_init, + error_tol=1e-5, max_iter=1000, print_skip=25) + σ_star = get_greedy(v_star, model) + return v_star, σ_star + + + +# == Plots == # + +import matplotlib.pyplot as plt + + +def fig_vseq(model=default_model, + k=3, + savefig=False, + figname="./figures/iid_job_search_1.pdf", + fs=10): + + v = np.zeros_like(model.w_vals) + fig, ax = plt.subplots(figsize=(9, 5.5)) + for i in range(k): + ax.plot(model.w_vals, v, linewidth=3, alpha=0.6, + label=f"iterate {i}") + v = T(v, model) + + for i in range(1000): + v = T(v, model) + + ax.plot(model.w_vals, v, "k-", linewidth=3.0, + label="iterate 1000", alpha=0.7) + + fontdict = {'fontsize': fs} + ax.set_xlabel("wage offer", fontdict=fontdict) + ax.set_ylabel("lifetime value", fontdict=fontdict) + + ax.legend(fontsize=fs, frameon=False) + + if savefig: + fig.savefig(figname) + plt.show() + + +def fig_vstar(model=default_model, + savefig=False, fs=10, + figname="./figures/iid_job_search_3.pdf"): + """ Plot the fixed point. """ + n, w_vals, φ, β, c = model + v_star, σ_star = vfi(model) + + fig, ax = plt.subplots(figsize=(9, 5.5)) + ax.plot(w_vals, v_star, "k-", linewidth=1.5, label="value function") + cont_val = c + β * np.sum(v_star * φ) + ax.plot(w_vals, [cont_val]*(n+1), + "--", + linewidth=5, + alpha=0.5, + label="continuation value") + + ax.plot(w_vals, + w_vals / (1 - β), + "--", + linewidth=5, + alpha=0.5, + label=r"$w/(1 - \beta)$") + + ax.legend(frameon=False, fontsize=fs, loc="lower right") + + if savefig: + fig.savefig(figname) + plt.show() diff --git a/code/py/inventory_dp.py b/code/py/inventory_dp.py new file mode 100644 index 0000000..f14069a --- /dev/null +++ b/code/py/inventory_dp.py @@ -0,0 +1,127 @@ +from quantecon import compute_fixed_point + +import numpy as np +from collections import namedtuple +from numba import njit + + +# NamedTuple Model +Model = namedtuple("Model", ("β", "K", "c", "κ", "p")) + + +def create_inventory_model(β=0.98, # discount factor + K=40, # maximum inventory + c=0.2, κ=2, # cost parameters + p=0.6): # demand parameter + return Model(β=β, K=K, c=c, κ=κ, p=p) + + +@njit +def demand_pdf(d, p): + return (1 - p)**d * p + + +@njit +def B(x, a, v, model, d_max=101): + """ + The function B(x, a, v) = r(x, a) + β Σ_x′ v(x′) P(x, a, x′). + """ + β, K, c, κ, p = model + x1 = np.array([np.minimum(x, d)*demand_pdf(d, p) for d in np.arange(d_max)]) + reward = np.sum(x1) - c * a - κ * (a > 0) + x2 = np.array([v[np.maximum(0, x - d) + a] * demand_pdf(d, p) + for d in np.arange(d_max)]) + continuation_value = β * np.sum(x2) + return reward + continuation_value + + +@njit +def T(v, model): + """The Bellman operator.""" + β, K, c, κ, p = model + new_v = np.empty_like(v) + for x in range(0, K+1): + x1 = np.array([B(x, a, v, model) for a in np.arange(K-x+1)]) + new_v[x] = np.max(x1) + return new_v + + +@njit +def get_greedy(v, model): + """ + Get a v-greedy policy. Returns a zero-based array. + """ + β, K, c, κ, p = model + σ_star = np.zeros(K+1, dtype=np.int32) + for x in range(0, K+1): + x1 = np.array([B(x, a, v, model) for a in np.arange(K-x+1)]) + σ_star[x] = np.argmax(x1) + return σ_star + + +def solve_inventory_model(v_init, model): + """Use successive_approx to get v_star and then compute greedy.""" + β, K, c, κ, p = model + v_star = compute_fixed_point(lambda v: T(v, model), v_init, + error_tol=1e-5, max_iter=1000, print_skip=25) + σ_star = get_greedy(v_star, model) + return v_star, σ_star + + +# == Plots == # + +import matplotlib.pyplot as plt + + +# Create an instance of the model and solve it +model = create_inventory_model() +β, K, c, κ, p = model +v_init = np.zeros(K+1) +v_star, σ_star = solve_inventory_model(v_init, model) + + +def sim_inventories(ts_length=400, X_init=0): + """Simulate given the optimal policy.""" + global p, σ_star + X = np.zeros(ts_length, dtype=np.int32) + X[0] = X_init + # Subtracts 1 because numpy generates only positive integers + rand = np.random.default_rng().geometric(p=p, size=ts_length-1) - 1 + for t in range(0, ts_length-1): + X[t+1] = np.maximum(X[t] - rand[t], 0) + σ_star[X[t] + 1] + return X + + +def plot_vstar_and_opt_policy(fontsize=10, + figname="./figures/inventory_dp_vs.pdf", + savefig=False): + fig, axes = plt.subplots(2, 1, figsize=(8, 6.5)) + + ax = axes[0] + ax.plot(np.arange(K+1), v_star, label=r"$v^*$") + ax.set_ylabel("value", fontsize=fontsize) + ax.legend(fontsize=fontsize, frameon=False) + + ax = axes[1] + ax.plot(np.arange(K+1), σ_star, label=r"$\sigma^*$") + ax.set_xlabel("inventory", fontsize=fontsize) + ax.set_ylabel("optimal choice", fontsize=fontsize) + ax.legend(fontsize=fontsize, frameon=False) + plt.show() + if savefig: + fig.savefig(figname) + + +def plot_ts(fontsize=10, + figname="./figures/inventory_dp_ts.pdf", + savefig=False): + X = sim_inventories() + fig, ax = plt.subplots(figsize=(9, 5.5)) + ax.plot(X, label="$X_t$", alpha=0.7) + ax.set_xlabel("$t$", fontsize=fontsize) + ax.set_ylabel("inventory", fontsize=fontsize) + ax.legend(fontsize=fontsize, frameon=False) + ax.set_ylim(0, np.max(X)+4) + plt.show() + if savefig: + fig.savefig(figname) diff --git a/code/py/inventory_sdd.py b/code/py/inventory_sdd.py new file mode 100644 index 0000000..20f719a --- /dev/null +++ b/code/py/inventory_sdd.py @@ -0,0 +1,225 @@ +""" + +Inventory management model with state-dependent discounting. The discount +factor takes the form β_t = Z_t, where (Z_t) is a discretization of a +Gaussian AR(1) process + + X_t = ρ X_{t-1} + b + ν W_t. + +""" + +from quantecon import compute_fixed_point +from quantecon.markov import tauchen, MarkovChain + +import numpy as np +from time import time +from numba import njit, prange +from collections import namedtuple + +# NamedTuple Model +Model = namedtuple("Model", ("K", "c", "κ", "p", "r", + "R", "y_vals", "z_vals", "Q")) + + +@njit +def ϕ(p, d): + return (1 - p)**d * p + +@njit +def f(y, a, d): + return np.maximum(y - d, 0) + a # Inventory update + +def create_sdd_inventory_model( + ρ=0.98, ν=0.002, n_z=20, b=0.97, # Z state parameters + K=40, c=0.2, κ=0.8, p=0.6, # firm and demand parameters + d_max=100): # truncation of demand shock + d_vals = np.arange(d_max+1) + ϕ_vals = ϕ(p, d_vals) + y_vals = np.arange(K+1) + n_y = len(y_vals) + mc = tauchen(n_z, ρ, ν) + z_vals, Q = mc.state_values + b, mc.P + ρL = np.max(np.abs(np.linalg.eigvals(z_vals * Q))) + assert ρL < 1, "Error: ρ(L) >= 1." # check r(L) < 1 + + R = np.zeros((n_y, n_y, n_y)) + for i_y, y in enumerate(y_vals): + for i_y_1, y_1 in enumerate(y_vals): + for i_a, a in enumerate(range(K - y + 1)): + hits = [f(y, a, d) == y_1 for d in d_vals] + R[i_y, i_a, i_y_1] = np.dot(hits, ϕ_vals) + + + r = np.empty((n_y, n_y)) + for i_y, y in enumerate(y_vals): + for i_a, a in enumerate(range(K - y + 1)): + cost = c * a + κ * (a > 0) + r[i_y, i_a] = np.dot(np.minimum(y, d_vals), ϕ_vals) - cost + + + return Model(K=K, c=c, κ=κ, p=p, r=r, R=R, + y_vals=y_vals, z_vals=z_vals, Q=Q) + +@njit +def B(i_y, i_z, i_a, v, model): + """ + The function B(x, z, a, v) = r(x, a) + β(z) Σ_x′ v(x′) P(x, a, x′). + """ + K, c, κ, p, r, R, y_vals, z_vals, Q = model + β = z_vals[i_z] + cv = 0.0 + + for i_z_1 in prange(len(z_vals)): + for i_y_1 in prange(len(y_vals)): + cv += v[i_y_1, i_z_1] * R[i_y, i_a, i_y_1] * Q[i_z, i_z_1] + return r[i_y, i_a] + β * cv + +@njit(parallel=True) +def T(v, model): + """The Bellman operator.""" + K, c, κ, p, r, R, y_vals, z_vals, Q = model + new_v = np.empty_like(v) + for i_z in prange(len(z_vals)): + for (i_y, y) in enumerate(y_vals): + Γy = np.arange(K - y + 1) + new_v[i_y, i_z] = np.max(np.array([B(i_y, i_z, i_a, v, model) + for i_a in Γy])) + return new_v + +@njit +def T_σ(v, σ, model): + """The policy operator.""" + K, c, κ, p, r, R, y_vals, z_vals, Q = model + new_v = np.empty_like(v) + for (i_z, z) in enumerate(z_vals): + for (i_y, y) in enumerate(y_vals): + new_v[i_y, i_z] = B(i_y, i_z, σ[i_y, i_z], v, model) + return new_v + +@njit(parallel=True) +def get_greedy(v, model): + """Get a v-greedy policy. Returns a zero-based array.""" + K, c, κ, p, r, R, y_vals, z_vals, Q = model + n_z = len(z_vals) + σ_star = np.zeros((K+1, n_z), dtype=np.int32) + for (i_z, z) in enumerate(z_vals): + for (i_y, y) in enumerate(y_vals): + Γy = np.arange(K - y + 1) + i_a = np.argmax(np.array([B(i_y, i_z, i_a, v, model) + for i_a in Γy])) + σ_star[i_y, i_z] = Γy[i_a] + return σ_star + +@njit +def get_value(v_init, σ, m, model): + """Approximate lifetime value of policy σ.""" + v = v_init + for _ in range(m): + v = T_σ(v, σ, model) + return v + +def solve_inventory_model(v_init, model): + """Use successive_approx to get v_star and then compute greedy.""" + v_star = compute_fixed_point(lambda v: T(v, model), v_init, + error_tol=1e-5, max_iter=1000, print_skip=25) + σ_star = get_greedy(v_star, model) + return v_star, σ_star + +def optimistic_policy_iteration(v_init, + model, + tolerance=1e-6, + max_iter=1_000, + print_step=10, + m=60): + v = v_init + error = tolerance + 1 + k = 1 + while (error > tolerance) and (k < max_iter): + last_v = v + σ = get_greedy(v, model) + v = get_value(v, σ, m, model) + error = np.max(np.abs(v - last_v)) + if k % print_step == 0: + print(f"Completed iteration {k} with error {error}.") + k += 1 + return v, get_greedy(v, model) + + +# == Plots == # + +import matplotlib.pyplot as plt + +# Create an instance of the model and solve it +model = create_sdd_inventory_model() +K, c, κ, p, r, R, y_vals, z_vals, Q = model +n_z = len(z_vals) +v_init = np.zeros((K+1, n_z), dtype=float) +print("Solving model.") +v_star, σ_star = optimistic_policy_iteration(v_init, model) +z_mc = MarkovChain(Q, z_vals) + +def sim_inventories(ts_length, X_init=0): + """Simulate given the optimal policy.""" + global p, z_mc + i_z = z_mc.simulate_indices(ts_length, init=1) + X = np.zeros(ts_length, dtype=np.int32) + X[0] = X_init + rand = np.random.default_rng().geometric(p=p, size=ts_length-1) - 1 + for t in range(ts_length-1): + X[t+1] = f(X[t], σ_star[X[t], i_z[t]], rand[t]) + return X, z_vals[i_z] + +def plot_ts(ts_length=400, + fontsize=10, + figname="./figures/inventory_sdd_ts.pdf", + savefig=False): + + X, Z = sim_inventories(ts_length) + fig, axes = plt.subplots(2, 1, figsize=(9, 5.5)) + + ax = axes[0] + ax.plot(X, label="inventory", alpha=0.7) + ax.set_xlabel(r"$t$", fontsize=fontsize) + ax.legend(fontsize=fontsize, frameon=False) + ax.set_ylim(0, np.max(X)+3) + + # calculate interest rate from discount factors + r = (1 / Z) - 1 + + ax = axes[1] + ax.plot(r, label=r"$r_t$", alpha=0.7) + ax.set_xlabel(r"$t$", fontsize=fontsize) + ax.legend(fontsize=fontsize, frameon=False) + + plt.tight_layout() + plt.show() + if savefig: + fig.savefig(figname) + +def plot_timing(m_vals=np.arange(1, 400, 10), + fontsize=16, + savefig=False): + print("Running value function iteration.") + t_start = time() + solve_inventory_model(v_init, model) + vfi_time = time() - t_start + print(f"VFI completed in {vfi_time} seconds.") + opi_times = [] + for m in m_vals: + print(f"Running optimistic policy iteration with m = {m}.") + t_start = time() + optimistic_policy_iteration(v_init, model, m=m) + opi_time = time() - t_start + print(f"OPI with m = {m} completed in {opi_time} seconds.") + opi_times.append(opi_time) + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(m_vals, np.full(len(m_vals), vfi_time), + lw=2, label="value function iteration") + ax.plot(m_vals, opi_times, lw=2, label="optimistic policy iteration") + ax.legend(fontsize=fontsize, frameon=False) + ax.set_xlabel(r"$m$", fontsize=fontsize) + ax.set_ylabel("time", fontsize=fontsize) + plt.show() + if savefig: + fig.savefig("./figures/inventory_sdd_timing.pdf") + return (opi_time, vfi_time, opi_times) \ No newline at end of file diff --git a/code/py/inventory_sim.py b/code/py/inventory_sim.py new file mode 100644 index 0000000..ec8f3a4 --- /dev/null +++ b/code/py/inventory_sim.py @@ -0,0 +1,92 @@ +import numpy as np +from scipy.stats import geom +from itertools import product +from quantecon import MarkovChain +from collections import namedtuple + +# NamedTuple Model +Model = namedtuple("Model", ("S", "s", "p", "φ", "h")) + +def create_inventory_model(S=100, # Order size + s=10, # Order threshold + p=0.4): # Demand parameter + φ = geom(p, loc=-1) # loc sets support to {0,1,...} + h = lambda x, d: max(x - d, 0) + S*(x <= s) + return Model(S=S, s=s, p=p, φ=φ, h=h) + + +def sim_inventories(model, ts_length=200): + """Simulate the inventory process.""" + S, s, p, φ, h = model + X = np.empty(ts_length) + X[0] = S # Initial condition + for t in range(0, ts_length - 1): + X[t+1] = h(X[t], φ.rvs()) + return X + + +def compute_mc(model, d_max=100): + """Compute the transition probabilities and state.""" + S, s, p, φ, h = model + n = S + s + 1 # Size of state space + state_vals = np.arange(n) + P = np.empty((n, n)) + for (i, j) in product(range(0, n), range(0, n)): + P[i, j] = sum((h(i, d) == j)*φ.pmf(d) for d in range(d_max+1)) + return MarkovChain(P, state_vals) + + +def compute_stationary_dist(model): + """Compute the stationary distribution of the model.""" + mc = compute_mc(model) + return mc.state_values, mc.stationary_distributions[0] + + + +# Plots + +import matplotlib.pyplot as plt + + +default_model = create_inventory_model() + + +def plot_ts(model, fontsize=16, + figname="./figures/inventory_sim_1.pdf", + savefig=False): + S, s, p, φ, h = model + X = sim_inventories(model) + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(X, label=r"$X_t$", linewidth=3, alpha=0.6) + fontdict = {'fontsize': fontsize} + ax.set_xlabel(r"$t$", fontdict=fontdict) + ax.set_ylabel("inventory", fontdict=fontdict) + ax.legend(fontsize=fontsize, frameon=False) + ax.set_ylim(0, S + s + 20) + + plt.show() + if savefig: + fig.savefig(figname) + + +def plot_hist(model, fontsize=16, + figname="./figures/inventory_sim_2.pdf", + savefig=False): + S, s, p, φ, h = model + state_values, ψ_star = compute_stationary_dist(model) + X = sim_inventories(model, 1_000_000) + histogram = [np.mean(X == i) for i in state_values] + + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(state_values, ψ_star, "k-", linewidth=3, alpha=0.7, + label=r"$\psi^*$") + ax.bar(state_values, histogram, alpha=0.7, label="frequency") + fontdict = {'fontsize': fontsize} + ax.set_xlabel("state", fontdict=fontdict) + + ax.legend(fontsize=fontsize, frameon=False) + ax.set_ylim(0, 0.015) + + plt.show() + if savefig: + fig.savefig(figname) diff --git a/code/py/is_irreducible.py b/code/py/is_irreducible.py new file mode 100644 index 0000000..47b0328 --- /dev/null +++ b/code/py/is_irreducible.py @@ -0,0 +1,10 @@ +from quantecon import MarkovChain +import numpy as np + +P = np.array([ + [0.1, 0.9], + [0.0, 1.0] +]) + +mc = MarkovChain(P) +print(mc.is_irreducible) diff --git a/code/py/laborer_sim.py b/code/py/laborer_sim.py new file mode 100644 index 0000000..c0eb3bb --- /dev/null +++ b/code/py/laborer_sim.py @@ -0,0 +1,38 @@ +import numpy as np +from collections import namedtuple + + +# NamedTuple Model +Model = namedtuple("Model", ("α", "β")) + + +def create_laborer_model(α=0.3, β=0.2): + return Model(α=α, β=β) + + +def laborer_update(x, model): # update X from t to t+1 + if x == 1: + x_ = 2 if np.random.rand() < model.α else 1 + else: + x_ = 1 if np.random.rand() < model.β else 2 + return x_ + + +def sim_chain(k, p, model): + X = np.empty(k) + X[0] = 1 if np.random.rand() < p else 2 + for t in range(0, k-1): + X[t+1] = laborer_update(X[t], model) + return X + + +def test_convergence(k=10_000_000, p=0.5): + model = create_laborer_model() + α, β = model + ψ_star = (1/(α + β)) * np.array([β, α]) + X = sim_chain(k, p, model) + ψ_e = (1/k) * np.array([sum(X == 1), sum(X == 2)]) + error = np.max(np.abs(ψ_star - ψ_e)) + approx_equal = np.allclose(ψ_star, ψ_e, rtol=0.01) + print(f"Sup norm deviation is {error}") + print(f"Approximate equality is {approx_equal}") diff --git a/code/py/lake.py b/code/py/lake.py new file mode 100644 index 0000000..0855de7 --- /dev/null +++ b/code/py/lake.py @@ -0,0 +1,116 @@ +import numpy as np + +α, λ, d, b = 0.01, 0.1, 0.02, 0.025 +g = b - d +A = np.mat(np.array([[(1 - d) * (1 - λ) + b, (1 - d) * α + b], + [(1 - d) * λ, (1 - d) * (1 - α)]])) + +ū = (1 + g - (1 - d) * (1 - α)) / (1 + g - (1 - d) * (1 - α) + (1 - d) * λ) + +ē = 1 - ū +x̄ = np.array([[ū], [ē]]) + +print(np.allclose(A * x̄, (1 + g) * x̄)) # prints true + +# == Plots == # + +import matplotlib.pyplot as plt + + +def plot_paths(figname="./figures/lake_1.pdf", savefig=False): + + path_length = 100 + x_path_1 = np.zeros((2, path_length)) + x_path_2 = np.zeros((2, path_length)) + x_0_1 = 5.0, 0.1 + x_0_2 = 0.1, 4.0 + x_path_1[0, 0] = x_0_1[0] + x_path_1[1, 0] = x_0_1[1] + x_path_2[0, 0] = x_0_2[0] + x_path_2[1, 0] = x_0_2[1] + + + for t in range(path_length-1): + x_path_1[:, t+1] = (A * x_path_1[:, t][np.newaxis].T).flatten() + x_path_2[:, t+1] = (A * x_path_2[:, t][np.newaxis].T).flatten() + + + fig, ax = plt.subplots() + + # Set the axes through the origin + for spine in ["left", "bottom"]: + ax.spines[spine].set_position("zero") + for spine in ["right", "top"]: + ax.spines[spine].set_color("none") + + ax.set_xlim(0, 6) + ax.set_ylim(0, 6) + ax.set_xlabel("unemployed workforce") + ax.set_ylabel("employed workforce") + ax.set_xticks((0, 6)) + ax.set_yticks((0, 6)) + s = 10 + ax.plot([0, s * ū], [0, s * ē], "k--", lw=1) + ax.scatter(x_path_1[0, :], x_path_1[1, :], s=4, c="blue") + ax.scatter(x_path_2[0, :], x_path_2[1, :], s=4, c="green") + + ax.plot([ū], [ē], "ko", ms=4, alpha=0.6) + ax.annotate(r"$\bar{x}$", + xy=(ū, ē), + xycoords="data", + xytext=(20, -20), + textcoords="offset points", + arrowprops={"arrowstyle" : "->"}) + + x, y = x_0_1[0], x_0_1[1] + #lb = r"\$x_0 = ($(x), $(y))\$" + ax.plot([x], [y], "ko", ms=2, alpha=0.6) + ax.annotate(rf"$x_0 = ({x}, {y})$", + xy=(x, y), + xycoords="data", + xytext=(0, 20), + textcoords="offset points", + arrowprops={"arrowstyle" : "->"}) + + x, y = x_0_2[0], x_0_2[1] + #lb = r"\$x_0 = ($(x), $(y))\$" + ax.plot([x], [y], "ko", ms=2, alpha=0.6) + ax.annotate(rf"$x_0 = ({x}, {y})$", + xy=(x, y), + xycoords="data", + xytext=(0, 20), + textcoords="offset points", + arrowprops={"arrowstyle" : "->"}) + + plt.show() + if savefig: + fig.savefig(figname) + + + +def plot_growth(savefig=False, figname="./figures/lake_2.pdf"): + + path_length = 100 + x_0 = 2.1, 1.2 + x = np.zeros((2, path_length)) + x[0, 0] = 0.6 + x[1, 0] = 1.2 + + for t in range(path_length-1): + x[:, t+1] = (A * x[:, t][np.newaxis].T).flatten() + + fig, axes = plt.subplots(3, 1) + u = x[0, :] + e = x[1, :] + n = x[0, :] + x[1, :] + paths = u, e, n + labels = r"$u_t$", r"$e_t$", r"$n_t$" + for (ax, path, label) in zip(axes, paths, labels): + ax.plot(path, label=label) + ax.legend(frameon=False, fontsize=14) + ax.set_xlabel(r"t") + + plt.tight_layout() + plt.show() + if savefig: + fig.savefig(figname) diff --git a/code/py/linear_iter.py b/code/py/linear_iter.py new file mode 100644 index 0000000..c113316 --- /dev/null +++ b/code/py/linear_iter.py @@ -0,0 +1,28 @@ +from s_approx import successive_approx +import numpy as np + +# Compute the fixed point of Tx = Ax + b via linear algebra +A = np.array([ + [0.4, 0.1], + [0.7, 0.2] +]) + +b = np.array([ + [1.0], + [2.0] +]) + +I = np.identity(2) +x_star = np.linalg.solve(I - A, b) # compute (I - A)^{-1} * b + + +# Compute the fixed point via successive approximation +T = lambda x: np.dot(A, x) + b +x_0 = np.array([ + [1.0], + [1.0] +]) +x_star_approx = successive_approx(T, x_0) + +# Test for approximate equality (prints "True") +print(np.allclose(x_star, x_star_approx, rtol=1e-5)) diff --git a/code/py/linear_iter_fig.py b/code/py/linear_iter_fig.py new file mode 100644 index 0000000..675f21f --- /dev/null +++ b/code/py/linear_iter_fig.py @@ -0,0 +1,44 @@ +import matplotlib.pyplot as plt +import numpy as np + +from linear_iter import x_star, T + +def plot_main(savefig=False, figname="./figures/linear_iter_fig_1.pdf"): + + fig, ax = plt.subplots() + + e = 0.02 + + marker_size = 60 + fs = 10 + + colors = ("red", "blue", "orange", "green") + u_0_vecs = ([[2.0], [3.0]], [[3.0], [5.2]], [[2.4], [3.6]], [[2.6], [5.6]]) + u_0_vecs = list(map(np.array, u_0_vecs)) + iter_range = 8 + + for (x_0, color) in zip(u_0_vecs, colors): + x = x_0 + s, t = x + ax.text(s+e, t-4*e, r"$u_0$", fontsize=fs) + + for i in range(iter_range): + s, t = x + ax.scatter((s,), (t,), c=color, alpha=0.2, s=marker_size) + x_new = T(x) + s_new, t_new = x_new + ax.plot((s, s_new), (t, t_new), marker='.',linewidth=0.5, alpha=0.5, color=color) + x = x_new + + s_star, t_star = x_star + ax.scatter((s_star,), (t_star,), c="k", s=marker_size * 1.2) + ax.text(s_star-4*e, t_star+4*e, r"$u^*$", fontsize=fs) + + ax.set_xticks((2.0, 2.5, 3.0)) + ax.set_yticks((3.0, 4.0, 5.0, 6.0)) + ax.set_xlim(1.8, 3.2) + ax.set_ylim(2.8, 6.1) + + plt.show() + if savefig: + fig.savefig(figname) diff --git a/code/py/markov_js.py b/code/py/markov_js.py new file mode 100644 index 0000000..c41eeeb --- /dev/null +++ b/code/py/markov_js.py @@ -0,0 +1,117 @@ +""" +Infinite-horizon job search with Markov wage draws. + +""" + +from quantecon.markov import tauchen +import numpy as np +from collections import namedtuple +from s_approx import successive_approx + + +# NamedTuple Model +Model = namedtuple("Model", ("n", "w_vals", "P", "β", "c")) + +def create_markov_js_model( + n=200, # wage grid size + ρ=0.9, # wage persistence + ν=0.2, # wage volatility + β=0.98, # discount factor + c=1.0 # unemployment compensation + ): + """ + Creates an instance of the job search model with Markov wages. + """ + mc = tauchen(n, ρ, ν) + w_vals, P = np.exp(mc.state_values), mc.P + return Model(n=n, w_vals=w_vals, P=P, β=β, c=c) + + +def T(v, model): + """ + The Bellman operator Tv = max{e, c + β P v} with e(w) = w / (1-β). + """ + n, w_vals, P, β, c = model + h = c + β * np.dot(P, v) + e = w_vals / (1 - β) + return np.maximum(e, h) + + +def get_greedy(v, model): + """Get a v-greedy policy.""" + n, w_vals, P, β, c = model + σ = w_vals / (1 - β) >= c + β * np.dot(P, v) + return σ + + + +def vfi(model): + """Solve the infinite-horizon Markov job search model by VFI.""" + v_init = np.zeros(model.w_vals.shape) + v_star = successive_approx(lambda v: T(v, model), v_init) + σ_star = get_greedy(v_star, model) + return v_star, σ_star + + + +# == Policy iteration == # + + +def get_value(σ, model): + """Get the value of policy σ.""" + n, w_vals, P, β, c = model + e = w_vals / (1 - β) + K_σ = β * ((1 - σ) * P.T).T + r_σ = σ * e + (1 - σ) * c + I = np.identity(K_σ.shape[0]) + return np.linalg.solve((I - K_σ), r_σ) + + +def policy_iteration(model): + """ + Howard policy iteration routine. + """ + σ = np.zeros(model.n, dtype=bool) + i, error = 0, True + while error: + v_σ = get_value(σ, model) + σ_new = get_greedy(v_σ, model) + error = np.any(σ_new ^ σ) + σ = σ_new + i = i + 1 + print(f"Concluded loop {i} with error: {error}.") + return σ + + +# == Plots == # + +import matplotlib.pyplot as plt + + +default_model = create_markov_js_model() + + +def plot_main(model=default_model, + method="vfi", + savefig=False, + figname="./figures/markov_js_vfix.png"): + n, w_vals, P, β, c = model + + if method == "vfi": + v_star, σ_star = vfi(model) + else: + σ_star = policy_iteration(model) + v_star = get_value(σ_star, model) + + h_star = c + β * np.dot(P, v_star) + e = w_vals / (1 - β) + + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(w_vals, h_star, linewidth=4, ls="--", alpha=0.4, label=r"$h^*(w)$") + ax.plot(w_vals, e, linewidth=4, ls="--", alpha=0.4, label=r"$w/(1-\beta)$") + ax.plot(w_vals, np.maximum(e, h_star), "k-", alpha=0.7, label=r"$v^*(w)$") + ax.legend(frameon=False) + ax.set_xlabel(r"$w$") + plt.show() + if savefig: + fig.savefig(figname) diff --git a/code/py/markov_js_with_sep.py b/code/py/markov_js_with_sep.py new file mode 100644 index 0000000..02b5caa --- /dev/null +++ b/code/py/markov_js_with_sep.py @@ -0,0 +1,126 @@ +""" +Infinite-horizon job search with Markov wage draws and separation. + +""" + +from quantecon.markov import tauchen +import numpy as np +from collections import namedtuple +from s_approx import successive_approx + + +# NamedTuple Model +Model = namedtuple("Model", ("n", "w_vals", "P", "β", "c", "α")) + + +def create_js_with_sep_model( + n=200, # wage grid size + ρ=0.9, ν=0.2, # wage persistence and volatility + β=0.98, α=0.1, # discount factor and separation rate + c=1.0): # unemployment compensation + """Creates an instance of the job search model with separation.""" + mc = tauchen(n, ρ, ν) + w_vals, P = np.exp(mc.state_values), mc.P + return Model(n=n, w_vals=w_vals, P=P, β=β, c=c, α=α) + + +def T(v, model): + """The Bellman operator for the value of being unemployed.""" + n, w_vals, P, β, c, α = model + d = 1 / (1 - β * (1 - α)) + accept = d * (w_vals + α * β * np.dot(P, v)) + reject = c + β * np.dot(P, v) + return np.maximum(accept, reject) + + +def get_greedy(v, model): + """ Get a v-greedy policy.""" + n, w_vals, P, β, c, α = model + d = 1 / (1 - β * (1 - α)) + accept = d * (w_vals + α * β * np.dot(P, v)) + reject = c + β * np.dot(P, v) + σ = accept >= reject + return σ + + +def vfi(model): + """Solve by VFI.""" + v_init = np.zeros(model.w_vals.shape) + v_star = successive_approx(lambda v: T(v, model), v_init) + σ_star = get_greedy(v_star, model) + return v_star, σ_star + + + +# == Plots == # + +import matplotlib.pyplot as plt + + +default_model = create_js_with_sep_model() + + +def plot_main(model=default_model, + savefig=False, + figname="./figures/markov_js_with_sep_1.pdf"): + n, w_vals, P, β, c, α = model + v_star, σ_star = vfi(model) + + d = 1 / (1 - β * (1 - α)) + accept = d * (w_vals + α * β * np.dot(P, v_star)) + h_star = c + β * np.dot(P, v_star) + + w_star = np.inf + for (i, w) in enumerate(w_vals): + if accept[i] >= h_star[i]: + w_star = w + break + + assert w_star != np.inf, "Agent never accepts" + + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(w_vals, h_star, linewidth=4, ls="--", alpha=0.4, + label="continuation value") + ax.plot(w_vals, accept, linewidth=4, ls="--", alpha=0.4, + label="stopping value") + ax.plot(w_vals, v_star, "k-", alpha=0.7, label=r"$v_u^*(w)$") + ax.legend(frameon=False) + ax.set_xlabel(r"$w$") + plt.show() + if savefig: + fig.savefig(figname) + + +def plot_w_stars(α_vals=np.linspace(0.0, 1.0, 10), + savefig=False, + figname="./figures/markov_js_with_sep_2.pdf"): + + w_star_vec = np.empty_like(α_vals) + for (i_α, α) in enumerate(α_vals): + print(i_α, α) + model = create_js_with_sep_model(α=α) + n, w_vals, P, β, c, α = model + v_star, σ_star = vfi(model) + + d = 1 / (1 - β * (1 - α)) + accept = d * (w_vals + α * β * np.dot(P, v_star)) + h_star = c + β * np.dot(P, v_star) + + w_star = np.inf + for (i_w, w) in enumerate(w_vals): + if accept[i_w] >= h_star[i_w]: + w_star = w + break + + assert w_star != np.inf, "Agent never accepts" + w_star_vec[i_α] = w_star + + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(α_vals, w_star_vec, linewidth=2, alpha=0.6, + label="reservation wage") + ax.legend(frameon=False) + ax.set_xlabel(r"$\alpha$") + ax.set_xlabel(r"$w$") + plt.show() + if savefig: + fig.savefig(figname) diff --git a/code/py/modified_opt_savings.py b/code/py/modified_opt_savings.py new file mode 100644 index 0000000..c025fe5 --- /dev/null +++ b/code/py/modified_opt_savings.py @@ -0,0 +1,280 @@ +from quantecon import tauchen, MarkovChain + +import numpy as np +from collections import namedtuple +from numba import njit, prange +from math import floor + + +# NamedTuple Model +Model = namedtuple("Model", ("β", "γ", "η_grid", "φ", + "w_grid", "y_grid", "Q")) + + +def create_savings_model(β=0.98, γ=2.5, + w_min=0.01, w_max=20.0, w_size=100, + ρ=0.9, ν=0.1, y_size=20, + η_min=0.75, η_max=1.25, η_size=2): + η_grid = np.linspace(η_min, η_max, η_size) + φ = np.ones(η_size) * (1 / η_size) # Uniform distributoin + w_grid = np.linspace(w_min, w_max, w_size) + mc = tauchen(y_size, ρ, ν) + y_grid, Q = np.exp(mc.state_values), mc.P + return Model(β=β, γ=γ, η_grid=η_grid, φ=φ, w_grid=w_grid, + y_grid=y_grid, Q=Q) + +## == Functions for regular OPI == ## + +@njit +def U(c, γ): + return c**(1-γ)/(1-γ) + +@njit +def B(i, j, k, l, v, model): + """ + The function + + B(w, y, η, w′) = u(w + y - w′/η)) + β Σ v(w′, y′, η′) Q(y, y′) ϕ(η′) + + """ + β, γ, η_grid, φ, w_grid, y_grid, Q = model + w, y, η, w_1 = w_grid[i], y_grid[j], η_grid[k], w_grid[l] + c = w + y - (w_1 / η) + exp_value = 0.0 + for m in prange(len(y_grid)): + for n in prange(len(η_grid)): + exp_value += v[l, m, n] * Q[j, m] * φ[n] + return U(c, γ) + β * exp_value if c > 0 else -np.inf + + +@njit(parallel=True) +def T_σ(v, σ, model): + """The policy operator.""" + β, γ, η_grid, φ, w_grid, y_grid, Q = model + v_new = np.empty_like(v) + for i in prange(len(w_grid)): + for j in prange(len(y_grid)): + for k in prange(len(η_grid)): + v_new[i, j, k] = B(i, j, k, σ[i, j, k], v, model) + return v_new + + +@njit(parallel=True) +def get_greedy(v, model): + """Compute a v-greedy policy.""" + β, γ, η_grid, φ, w_grid, y_grid, Q = model + w_n, y_n, η_n = len(w_grid), len(y_grid), len(η_grid) + σ = np.empty((w_n, y_n, η_n), dtype=np.int32) + for i in prange(w_n): + for j in prange(y_n): + for k in prange(η_n): + _tmp = np.array([B(i, j, k, l, v, model) for l + in range(w_n)]) + σ[i, j, k] = np.argmax(_tmp) + return σ + + +def optimistic_policy_iteration(model, tolerance=1e-5, m=100): + """Optimistic policy iteration routine.""" + β, γ, η_grid, φ, w_grid, y_grid, Q = model + w_n, y_n, η_n = len(w_grid), len(y_grid), len(η_grid) + v = np.zeros((w_n, y_n, η_n)) + error = tolerance + 1 + while error > tolerance: + last_v = v + σ = get_greedy(v, model) + for i in range(m): + v = T_σ(v, σ, model) + error = np.max(np.abs(v - last_v)) + print(f"OPI current error = {error}") + return get_greedy(v, model) + + +## == Functions for modified OPI == ## + + +@njit +def D(i, j, k, l, g, model): + """D(w, y, η, w′, g) = u(w + y - w′/η) + β g(y, w′).""" + β, γ, η_grid, φ, w_grid, y_grid, Q = model + w, y, η, w_1 = w_grid[i], y_grid[j], η_grid[k], w_grid[l] + c = w + y - (w_1 / η) + return U(c, γ) + β * g[j, l] if c > 0 else -np.inf + + +@njit(parallel=True) +def get_g_greedy(g, model): + """Compute a g-greedy policy.""" + β, γ, η_grid, φ, w_grid, y_grid, Q = model + w_n, y_n, η_n = len(w_grid), len(y_grid), len(η_grid) + σ = np.empty((w_n, y_n, η_n), dtype=np.int32) + for i in prange(w_n): + for j in prange(y_n): + for k in prange(η_n): + _tmp = np.array([D(i, j, k, l, g, model) for l + in range(w_n)]) + σ[i, j, k] = np.argmax(_tmp) + return σ + + +@njit(parallel=True) +def R_σ(g, σ, model): + """The modified policy operator.""" + β, γ, η_grid, φ, w_grid, y_grid, Q = model + w_n, y_n, η_n = len(w_grid), len(y_grid), len(η_grid) + g_new = np.empty_like(g) + for j in prange(y_n): + for i_1 in prange(w_n): + out = 0.0 + for j_1 in prange(y_n): + for k_1 in prange(η_n): + out += D(i_1, j_1, k_1, σ[i_1, j_1, k_1], g, + model) * Q[j, j_1] * φ[k_1] + g_new[j, i_1] = out + return g_new + + +def mod_opi(model, tolerance=1e-5, m=100): + """Modified optimistic policy iteration routine.""" + β, γ, η_grid, φ, w_grid, y_grid, Q = model + g = np.zeros((len(y_grid), len(w_grid))) + error = tolerance + 1 + while error > tolerance: + last_g = g + σ = get_g_greedy(g, model) + for i in range(m): + g = R_σ(g, σ, model) + error = np.max(np.abs(g - last_g)) + print(f"OPI current error = {error}") + return get_g_greedy(g, model) + + +def simulate_wealth(m): + + model = create_savings_model() + σ_star = mod_opi(model) + β, γ, η_grid, φ, w_grid, y_grid, Q = model + + # Simulate labor income + mc = MarkovChain(Q) + y_idx_series = mc.simulate(ts_length=m) + + # IID Markov chain with uniform draws + l = len(η_grid) + mc = MarkovChain(np.ones((l, l)) / l) + η_idx_series = mc.simulate(ts_length=m) + + w_idx_series = np.empty_like(y_idx_series) + w_idx_series[0] = 1 # initial condition + for t in range(m-1): + i, j, k = w_idx_series[t], y_idx_series[t], η_idx_series[t] + w_idx_series[t+1] = σ_star[i, j, k] + w_series = w_grid[w_idx_series] + + return w_series + +def lorenz(v): # assumed sorted vector + S = np.cumsum(v) # cumulative sums: [v[1], v[1] + v[2], ... ] + F = np.arange(1, len(v) + 1) / len(v) + L = S / S[-1] + return (F, L) # returns named tuple + +gini = lambda v: (2 * sum(i * y for (i, y) in enumerate(v))/sum(v) - (len(v) + 1))/len(v) + +# Plots + + +import matplotlib.pyplot as plt + + +def plot_contours(savefig=False, + figname="./figures/modified_opt_savings_1.pdf"): + + model = create_savings_model() + β, γ, η_grid, φ, w_grid, y_grid, Q = model + σ_star = optimistic_policy_iteration(model) + + fig, axes = plt.subplots(2, 1, figsize=(10, 8)) + y_n, η_n = len(y_grid), len(η_grid) + y_idx, η_idx = np.arange(y_n), np.arange(η_n) + H = np.zeros((y_n, η_n)) + + w_indices = (0, len(w_grid)-1) + titles = "low wealth", "high wealth" + for (ax, w_idx, title) in zip(axes, w_indices, titles): + + for i_y in y_idx: + for i_η in η_idx: + w, y, η = w_grid[w_idx], y_grid[i_y], η_grid[i_η] + H[i_y, i_η] = w_grid[σ_star[w_idx, i_y, i_η]] / (w + y) + + cs1 = ax.contourf(y_grid, η_grid, np.transpose(H), alpha=0.5) + + plt.colorbar(cs1, ax=ax) #, format="%.6f") + + ax.set_title(title) + ax.set_xlabel(r"$y$") + ax.set_ylabel(r"$\varepsilon$") + + plt.tight_layout() + if savefig: + fig.savefig(figname) + plt.show() + +def plot_policies(savefig=False): + model = create_savings_model() + β, γ, η_grid, φ, w_grid, y_grid, Q = model + σ_star = mod_opi(model) + y_bar = floor(len(y_grid) / 2) # index of mid-point of y_grid + + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(w_grid, w_grid, "k--", label=r"$45$") + + for (i, η) in enumerate(η_grid): + label = r"$\sigma^*$" + " at " + r"$\eta = $" + f"{η.round(2)}" + ax.plot(w_grid, w_grid[σ_star[:, y_bar, i]], label=label) + + ax.legend() + plt.show() + if savefig: + fig.savefig(f"./figures/modified_opt_saving_2.pdf") + +def plot_time_series(m=2_000, savefig=False): + + w_series = simulate_wealth(m) + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(w_series, label=r"$w_t$") + ax.set_xlabel("time") + ax.legend() + plt.show() + if savefig: + fig.savefig("./figures/modified_opt_saving_ts.pdf") + +def plot_histogram(m=1_000_000, savefig=False): + + w_series = simulate_wealth(m) + w_series.sort() + g = round(gini(w_series), ndigits=2) + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.hist(w_series, bins=40, density=True) + ax.set_xlabel("wealth") + ax.text(15, 0.4, f"Gini = {g}") + plt.show() + + if savefig: + fig.savefig("./figures/modified_opt_saving_hist.pdf") + +def plot_lorenz(m=1_000_000, savefig=False): + + w_series = simulate_wealth(m) + w_series.sort() + (F, L) = lorenz(w_series) + + fig, ax = plt.subplots(figsize=(9, 5.2)) + ax.plot(F, F, label="Lorenz curve, equality") + ax.plot(F, L, label="Lorenz curve, wealth distribution") + ax.legend() + plt.show() + + if savefig: + fig.savefig("./figures/modified_opt_saving_lorenz.pdf") \ No newline at end of file diff --git a/code/py/pd_ratio.py b/code/py/pd_ratio.py new file mode 100644 index 0000000..16047e6 --- /dev/null +++ b/code/py/pd_ratio.py @@ -0,0 +1,76 @@ +""" +Price-dividend ratio in a model with dividend and consumption growth. + +""" + +from quantecon.markov import tauchen +import numpy as np +from collections import namedtuple + + +# NamedTuple Model +Model = namedtuple("Model", ("x_vals", "P", "β", "γ", + "μ_c", "σ_c", "μ_d", "σ_d")) + + +def create_asset_pricing_model( + n=200, # state grid size + ρ=0.9, ν=0.2, # state persistence and volatility + β=0.99, γ=2.5, # discount and preference parameter + μ_c=0.01, σ_c=0.02, # consumption growth mean and volatility + μ_d=0.02, σ_d=0.1): # dividend growth mean and volatility + """ + Creates an instance of the asset pricing model with Markov state. + """ + mc = tauchen(n, ρ, ν) + x_vals, P = np.exp(mc.state_values), mc.P + return Model(x_vals=x_vals, P=P, β=β, γ=γ, + μ_c=μ_c, σ_c=σ_c, μ_d=μ_d, σ_d=σ_d) + + +def build_discount_matrix(model): + """Build the discount matrix A.""" + x_vals, P, β, γ, μ_c, σ_c, μ_d, σ_d = model + e = np.exp(μ_d - γ*μ_c + (γ**2 * σ_c**2 + σ_d**2)/2 + (1-γ)*x_vals) + return β * (e * P.T).T + + + +def pd_ratio(model): + """ + Compute the price-dividend ratio associated with the model. + """ + x_vals, P, β, γ, μ_c, σ_c, μ_d, σ_d = model + A = build_discount_matrix(model) + assert np.max(np.abs(np.linalg.eigvals(A))) < 1, "Requires r(A) < 1." + n = len(x_vals) + I = np.identity(n) + return np.linalg.solve((I - A), np.dot(A, np.ones(n))) + + +# == Plots == # + + +import matplotlib.pyplot as plt + + +default_model = create_asset_pricing_model() + + +def plot_main(μ_d_vals=(0.02, 0.08), + savefig=False, + figname="./figures/pd_ratio_1.pdf"): + fig, ax = plt.subplots(figsize=(9, 5.2)) + + for μ_d in μ_d_vals: + model = create_asset_pricing_model(μ_d=μ_d) + x_vals, P, β, γ, μ_c, σ_c, μ_d, σ_d = model + v_star = pd_ratio(model) + ax.plot(x_vals, v_star, linewidth=2, alpha=0.6, + label=r"$\mu_d$=" + f"{μ_d}") + + ax.legend(frameon=False) + ax.set_xlabel(r"$x$") + plt.show() + if savefig: + fig.savefig(figname) diff --git a/code/py/plot_interest_rates.py b/code/py/plot_interest_rates.py new file mode 100644 index 0000000..9e02d17 --- /dev/null +++ b/code/py/plot_interest_rates.py @@ -0,0 +1,23 @@ +# Nominal interest rate from https://fred.stlouisfed.org/series/GS1 +# Real interest rate from https://fred.stlouisfed.org/series/WFII10 +# +# Download as CSV files +# + +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt + +df_nominal = pd.read_csv("./data/GS1.csv") +df_real = pd.read_csv("./data/WFII10.csv") + +def plot_rates(df, fontsize=16, savefig=False): + r_type = 'nominal' if df.equals(df_nominal) else 'real' + fig, ax = plt.subplots(figsize=(9, 5)) + ax.plot(df.iloc[:, 0], df.iloc[:, 1], label=f'{r_type} interest rate') + ax.plot(df.iloc[:, 0], np.zeros(df.iloc[:, 1].size), c='k', ls='--') + ax.set_xlim(df.iloc[0, 0], df.iloc[-1, 0]) + ax.legend(fontsize=fontsize, frameon=False) + plt.show() + if savefig: + fig.savefig(f'./figures/plot_interest_rates_{r_type}.pdf') \ No newline at end of file diff --git a/code/py/power_series.py b/code/py/power_series.py new file mode 100644 index 0000000..b6a2056 --- /dev/null +++ b/code/py/power_series.py @@ -0,0 +1,26 @@ +import numpy as np + +# Primitives +A = np.array([ + [0.4, 0.1], + [0.7, 0.2] +]) + + +# Method one: direct inverse +I = np.identity(2) +B_inverse = np.linalg.inv(I - A) + + +# Method two: power series +def power_series(A): + B_sum = np.zeros((2, 2)) + A_power = np.identity(2) + for k in range(50): + B_sum += A_power + A_power = np.dot(A_power, A) + return B_sum + + +# Print maximal error +print(np.max(np.abs(B_inverse - power_series(A)))) diff --git a/code/py/quantile_function.py b/code/py/quantile_function.py new file mode 100644 index 0000000..45d59ff --- /dev/null +++ b/code/py/quantile_function.py @@ -0,0 +1,27 @@ +from scipy.stats import rv_discrete +import numpy as np + +from numba import njit + +"Compute the τ-th quantile of v(X) when X ∼ ϕ and v = sort(v)." +@njit +def quantile(τ, v, ϕ): + for (i, v_value) in enumerate(v): + p = sum(ϕ[:i+1]) # sum all ϕ[j] s.t. v[j] ≤ v_value + if p >= τ: # exit and return v_value if prob ≥ τ + return v_value + +"For each i, compute the τ-th quantile of v(Y) when Y ∼ P(i, ⋅)" +def R(τ, v, P): + return np.array([quantile(τ, v, P[i, :]) for i in range(len(v))]) + +def quantile_test(τ): + ϕ = [0.1, 0.2, 0.7] + v = [10, 20, 30] + + #d = DiscreteNonParametric(v, ϕ) + d = rv_discrete(values=(v, ϕ)) + return quantile(τ, v, ϕ), d.ppf(τ) + + + diff --git a/code/py/quantile_js.py b/code/py/quantile_js.py new file mode 100644 index 0000000..545ac07 --- /dev/null +++ b/code/py/quantile_js.py @@ -0,0 +1,93 @@ +""" +Job search with Markov wage draws and quantile preferences. + +""" +from quantecon import tauchen, MarkovChain +import numpy as np +from quantile_function import * + +"Creates an instance of the job search model." +def create_markov_js_model( + n=100, # wage grid size + ρ=0.9, # wage persistence + ν=0.2, # wage volatility + β=0.98, # discount factor + c=1.0, # unemployment compensation + τ=0.5 # quantile parameter + ): + mc = tauchen(n, ρ, ν) + w_vals, P = np.exp(mc.state_values), mc.P + return (n, w_vals, P, β, c, τ) + +""" +The policy operator + + (T_σ v)(w) = σ(w) (w / (1-β)) + (1 - σ(w))(c + β (R_τ v)(w)) + +""" +def T_σ(v, σ, model): + n, w_vals, P, β, c, τ = model + h = [x + c for x in β * R(τ, v, P)] + e = w_vals / (1 - β) + return σ * e + (1 - σ) * h + +" Get a v-greedy policy." +def get_greedy(v, model): + n, w_vals, P, β, c, τ = model + σ = w_vals / (1 - β) >= c + β * R(τ, v, P) + return σ + + +"Optimistic policy iteration routine." +def optimistic_policy_iteration(model, tolerance=1e-5, m=100): + n, w_vals, P, β, c, τ = model + v = np.ones(n) + error = tolerance + 1 + while error > tolerance: + last_v = v + σ = get_greedy(v, model) + for i in range(m): + v = T_σ(v, σ, model) + + error = max(np.abs(v - last_v)) + print(f"OPI current error = {error}") + + return v, get_greedy(v, model) + + +# == Plots == # + +import matplotlib.pyplot as plt + + +def plot_main(tau_vals=(0.1, 0.25, 0.5, 0.6, 0.7, 0.8), + savefig=False, + figname="./figures/quantile_js.pdf"): + + w_star_vals = np.zeros(len(tau_vals)) + + for (τ_idx, τ) in enumerate(tau_vals): + model = create_markov_js_model(τ=τ) + n, w_vals, P, β, c, τ = model + v_star, σ_star = optimistic_policy_iteration(model) + for i in range(n): + if σ_star[i] > 0: + w_star_vals[τ_idx] = w_vals[i] + break + + model = create_markov_js_model() + n, w_vals, P, β, c, τ = model + mc = MarkovChain(P) + s = mc.stationary_distributions[0] + + fig, ax = plt.subplots() + ax.plot(tau_vals, w_star_vals, "k--", lw=2, alpha=0.7, label="reservation wage") + ax.barh(w_vals, 32 * s, alpha=0.05, align="center") + ax.legend(frameon=False, loc="upper center") + ax.set_xlabel("quantile") + ax.set_ylabel("wages") + + plt.show() + if savefig: + fig.savefig(figname) + diff --git a/code/py/risk_sensitive_js.py b/code/py/risk_sensitive_js.py new file mode 100644 index 0000000..b85c50a --- /dev/null +++ b/code/py/risk_sensitive_js.py @@ -0,0 +1,95 @@ +""" +Infinite-horizon job search with Markov wage draws and risk-sensitive preferences. + +""" + +from quantecon import compute_fixed_point +from quantecon.markov import tauchen + +import numpy as np +from numba import njit +from collections import namedtuple + +# NamedTuple Model +Model = namedtuple("Model", ("n", "w_vals", "P", "β", "c", "θ")) + + +def create_markov_js_model( + n=200, # wage grid size + ρ=0.9, # wage persistence + ν=0.2, # wage volatility + β=0.98, # discount factor + c=1.0, # unemployment compensation + θ=-0.01 # risk parameter + ): + """Creates an instance of the job search model with Markov wages.""" + mc = tauchen(n, ρ, ν) + w_vals, P = np.exp(mc.state_values), mc.P + return Model(n=n, w_vals=w_vals, P=P, β=β, c=c, θ=θ) + + +@njit +def T(v, model): + """ + The Bellman operator Tv = max{e, c + β R v} with + + e(w) = w / (1-β) and + + (Rv)(w) = (1/θ) ln{E_w[ exp(θ v(W'))]} + + """ + n, w_vals, P, β, c, θ = model + h = c + (β / θ) * np.log(np.dot(P, (np.exp(θ * v)))) + e = w_vals / (1 - β) + return np.maximum(e, h) + + +@njit +def get_greedy(v, model): + """Get a v-greedy policy.""" + n, w_vals, P, β, c, θ = model + σ = w_vals / (1 - β) >= c + (β / θ) * np.log(np.dot(P, (np.exp(θ * v)))) + return σ + + +def vfi(model): + """Solve the infinite-horizon Markov job search model by VFI.""" + v_init = np.zeros(model.w_vals.shape) + v_star = compute_fixed_point(lambda v: T(v, model), v_init, + error_tol=1e-5, max_iter=1000, print_skip=25) + σ_star = get_greedy(v_star, model) + return v_star, σ_star + + +# == Plots == # + + +import matplotlib.pyplot as plt + + +def plot_main(theta_vals=(-10, 0.0001, 0.1), + savefig=False, + figname="./figures/risk_sensitive_js.pdf"): + + fig, axes = plt.subplots(len(theta_vals), 1, figsize=(9, 22)) + + for (θ, ax) in zip(theta_vals, axes): + model = create_markov_js_model(θ=θ) + n, w_vals, P, β, c, θ = model + v_star, σ_star = vfi(model) + + h_star = c + (β / θ) * np.log(np.dot(P, (np.exp(θ * v_star)))) + e = w_vals / (1 - β) + + ax.plot(w_vals, h_star, linewidth=4, ls="--", alpha=0.4, label=r"$h^*(w)$") + ax.plot(w_vals, e, linewidth=4, ls="--", alpha=0.4, label=r"$w/(1-\beta)$") + ax.plot(w_vals, np.maximum(e, h_star), "k-", alpha=0.7, label=r"$v^*(w)$") + ax.set_title(r"$\theta = $" + f"{θ}") + ax.legend(frameon=False) + ax.set_xlabel(r"$w$") + + fig.tight_layout() + plt.show() + + if savefig: + fig.savefig(figname) diff --git a/code/py/rs_utility.py b/code/py/rs_utility.py new file mode 100644 index 0000000..f091d28 --- /dev/null +++ b/code/py/rs_utility.py @@ -0,0 +1,87 @@ +from quantecon import compute_fixed_point +from quantecon.markov import tauchen + +import numpy as np +from numba import njit +from collections import namedtuple + + +# NamedTuple Model +Model = namedtuple("Model", ("β", "θ", "ρ", "σ", "r", "x_vals", "P")) + + +def create_rs_utility_model( + n=180, # size of state space + β=0.95, # time discount factor + ρ=0.96, # correlation coef in AR(1) + σ=0.1, # volatility + θ=-1.0): # risk aversion + mc = tauchen(n, ρ, σ, 0, 10) # n_std = 10 + x_vals, P = mc.state_values, mc.P + r = x_vals # special case u(c(x)) = x + return Model(β=β, θ=θ, ρ=ρ, σ=σ, r=r, x_vals=x_vals, P=P) + + +@njit +def K(v, model): + β, θ, ρ, σ, r, x_vals, P = model + return r + (β/θ) * np.log(np.dot(P, (np.exp(θ*v)))) + + +def compute_rs_utility(model): + β, θ, ρ, σ, r, x_vals, P = model + v_init = np.zeros(len(x_vals)) + v_star = compute_fixed_point(lambda v: K(v, model), v_init, + error_tol=1e-10, max_iter=1000, print_skip=25) + return v_star + + +# Plots + + +import matplotlib.pyplot as plt + + +def plot_v(savefig=False, + figname="./figures/rs_utility_1.pdf"): + + fig, ax = plt.subplots(figsize=(10, 5.2)) + model = create_rs_utility_model() + β, θ, ρ, σ, r, x_vals, P = model + + a = 1/(1 - (ρ*β)) + b = (β /(1 - β)) * (θ/2) * (a*σ)**2 + + v_star = compute_rs_utility(model) + v_star_a = a * x_vals + b + ax.plot(x_vals, v_star, linewidth=2, alpha=0.7, + label="approximate fixed point") + ax.plot(x_vals, v_star_a, "k--", linewidth=2, alpha=0.7, + label=r"$v(x)=ax + b$") + ax.set_xlabel(r"$x$") + + ax.legend(frameon=False, loc="upper left") + plt.show() + if savefig: + fig.savefig(figname) + + +def plot_multiple_v(savefig=False, + figname="./figures/rs_utility_2.pdf"): + + fig, ax = plt.subplots(figsize=(10, 5.2)) + σ_vals = 0.05, 0.1 + + for σ in σ_vals: + model = create_rs_utility_model(σ=σ) + β, θ, ρ, σ, r, x_vals, P = model + v_star = compute_rs_utility(model) + ax.plot(x_vals, v_star, linewidth=2, alpha=0.7, + label=r"$\sigma=$" + f"{σ}") + ax.set_xlabel(r"$x$") + ax.set_ylabel(r"$v(x)$") + + ax.legend(frameon=False, loc="upper left") + plt.show() + if savefig: + fig.savefig(figname) diff --git a/code/py/s_approx.py b/code/py/s_approx.py new file mode 100644 index 0000000..9a54931 --- /dev/null +++ b/code/py/s_approx.py @@ -0,0 +1,26 @@ +""" +Computes the approximate fixed point of T via successive approximation. +""" + +import numpy as np + +def successive_approx(T, # Operator (callable) + x_0, # Initial condition + tolerance=1e-6, # Error tolerance + max_iter=10_000, # Max iteration bound + print_step=25): # Print at multiples + x = x_0 + error = tolerance + 1 + k = 1 + while (error > tolerance) and (k <= max_iter): + x_new = T(x) + error = np.max(np.abs(x_new - x)) + if k % print_step == 0: + print(f"Completed iteration {k} with error {error}.") + x = x_new + k += 1 + if error <= tolerance: + print(f"Terminated successfully in {k} iterations.") + else: + print("Warning: hit iteration bound.") + return x diff --git a/code/py/solow_fp.py b/code/py/solow_fp.py new file mode 100644 index 0000000..c780aa3 --- /dev/null +++ b/code/py/solow_fp.py @@ -0,0 +1,64 @@ +import matplotlib.pyplot as plt +import numpy as np + +x0 = 0.25 +xmin, xmax = 0, 3 + + +k_grid = np.linspace(xmin, xmax, 1200) + + +def plot_45(ax, k0=0.5, + A=2.0, s=0.3, alpha=0.3, delta=0.4, + fs=10, # font size + num_arrows=8): + + # Define the function and the fixed point + g = lambda k: A * s * k**alpha + (1 - delta) * k + kstar = ((s * A) / delta)**(1/(1 - alpha)) + + # Plot the functions + lb = r"$g(k) = sAk^{\alpha} + (1 - \delta)k$" + ax.plot(k_grid, g(k_grid), linewidth=2, alpha=0.6, label=lb) + ax.plot(k_grid, k_grid, "k--", linewidth=1, alpha=0.7, label=r"$45$") + + # Show and annotate the fixed point + fps = (kstar,) + ax.plot(fps, fps, "go", ms=10, alpha=0.6) + ax.annotate(r"$k^* = (sA / \delta)^{\frac{1}{1-\alpha}}$", + xy=(kstar, kstar), + xycoords="data", + xytext=(20, -20), + textcoords="offset points", + fontsize=fs) + + # Draw the arrow sequence + + arrow_args = {'fc': "k", 'ec': "k", 'head_width': 0.03, + 'length_includes_head': True, 'linewidth': 1, + 'alpha': 0.6, 'head_length': 0.03} + + k = k0 + for i in range(num_arrows): + ax.arrow(k, k, 0.0, g(k)-k, **arrow_args) # x, y, dx, dy + ax.arrow(k, g(k), g(k) - k, 0, **arrow_args) + k = g(k) + + + ax.legend(loc="upper left", frameon=False, fontsize=fs) + + ax.set_xticks((0, k0, 3)) + ax.set_xticklabels((0, r"$k_0$", 3), fontsize=fs) + ax.set_yticks((0, 1, 2, 3)) + ax.set_yticklabels((0, 1, 2, 3), fontsize=fs) + ax.set_ylim(0, 3) + ax.set_xlabel(r"$k_t$", fontsize=fs) + ax.set_ylabel(r"$k_{t+1}$", fontsize=fs) + + +fig, ax = plt.subplots() + +plot_45(ax, A=2.0, s=0.3, alpha=0.4, delta=0.4) +fig.tight_layout() +plt.show() +fig.savefig("./figures/solow_fp.pdf") diff --git a/code/py/two_period_job_search.py b/code/py/two_period_job_search.py new file mode 100644 index 0000000..f8ebc17 --- /dev/null +++ b/code/py/two_period_job_search.py @@ -0,0 +1,102 @@ +from quantecon.distributions import BetaBinomial + +import numpy as np +from numba import njit +from collections import namedtuple + + +# NamedTuple Model +Model = namedtuple("Model", ("n", "w_vals", "φ", "β", "c")) + + +def create_job_search_model( + n=50, # wage grid size + w_min=10.0, # lowest wage + w_max=60.0, # highest wage + a=200, # wage distribution parameter + b=100, # wage distribution parameter + β=0.96, # discount factor + c=10.0 # unemployment compensation + ): + """ + Creates the parameters for job search model and returns the + instance of namedtuple Model + """ + w_vals = np.linspace(w_min, w_max, n+1) + φ = BetaBinomial(n, a, b).pdf() + return Model(n=n, w_vals=w_vals, φ=φ, β=β, c=c) + + +@njit +def v_1(w, model): + """ + Computes lifetime value at t=1 given current wage w_1 = w + """ + β, c = model.β, model.c + s = np.maximum(c, model.w_vals) + h_1 = c + β * np.sum(s * model.φ) + return np.maximum(w + β * w, h_1) + + +@njit +def res_wage(model): + """ + Computes reservation wage at t=1 + """ + β, c = model.β, model.c + s = np.maximum(c, model.w_vals) + h_1 = c + β * np.sum(s * model.φ) + return h_1 / (1 + β) + + + +##### Plots ##### + +import matplotlib.pyplot as plt + + +default_model = create_job_search_model() + + +def fig_dist(model=default_model, fs=10): + """ + Plot the distribution of wages + """ + fig, ax = plt.subplots() + ax.plot(model.w_vals, model.φ, "-o", alpha=0.5, label="wage distribution") + ax.legend(loc="upper left", fontsize=fs) + plt.show() + + +def fig_v1(model=default_model, savefig=False, + figname="./figures/iid_job_search_0_py.pdf", fs=18): + """ + Plot two-period value function and res wage + """ + n, w_vals, φ, β, c = model + + v = [v_1(w, model) for w in w_vals] + w_star = res_wage(model) + s = np.maximum(c, w_vals) + continuation_val = c + β * np.sum(s * φ) + min_w, max_w = np.min(w_vals), np.max(w_vals) + + fontdict = {'fontsize': 10} + fig, ax = plt.subplots(figsize=(9, 5.5)) + ax.set_ylim(0, 120) + ax.set_xlim(min_w, max_w) + ax.vlines((w_star,), (0,), (continuation_val,), lw=0.5) + ax.set_yticks((0, 50, 100)) + ax.set_yticklabels((0, 50, 100), fontdict=fontdict) + ax.set_xticks((min_w, w_star, max_w)) + ax.set_xticklabels((min_w, r"$w^*_1$", max_w), fontdict=fontdict) + ax.plot(w_vals, w_vals + β * w_vals, alpha=0.8, linewidth=3, + label=r"$w_1 + \beta w_1$") + ax.plot(w_vals, [continuation_val]*(n+1), linewidth=3, alpha=0.8, + label=r"$c + \beta \sum_{w'} \max\{c, w'\} \varphi(w')$" ) + ax.plot(w_vals, v, "k--", markersize=2, alpha=1.0, linewidth=2, + label=r"$v_1(w_1)$") + ax.legend(frameon=False, fontsize=fs, loc="upper left") + if savefig: + fig.savefig(figname) + plt.show() diff --git a/pdf/dp.pdf b/pdf/dp.pdf new file mode 100644 index 0000000..e3129c1 Binary files /dev/null and b/pdf/dp.pdf differ diff --git a/CNAME b/website/CNAME similarity index 100% rename from CNAME rename to website/CNAME diff --git a/LICENSE.txt b/website/LICENSE.txt similarity index 100% rename from LICENSE.txt rename to website/LICENSE.txt diff --git a/assets/css/fontawesome-all.min.css b/website/assets/css/fontawesome-all.min.css similarity index 100% rename from assets/css/fontawesome-all.min.css rename to website/assets/css/fontawesome-all.min.css diff --git a/assets/css/images/overlay.png b/website/assets/css/images/overlay.png similarity index 100% rename from assets/css/images/overlay.png rename to website/assets/css/images/overlay.png diff --git a/assets/css/main.css b/website/assets/css/main.css similarity index 100% rename from assets/css/main.css rename to website/assets/css/main.css diff --git a/assets/js/breakpoints.min.js b/website/assets/js/breakpoints.min.js similarity index 100% rename from assets/js/breakpoints.min.js rename to website/assets/js/breakpoints.min.js diff --git a/assets/js/browser.min.js b/website/assets/js/browser.min.js similarity index 100% rename from assets/js/browser.min.js rename to website/assets/js/browser.min.js diff --git a/assets/js/jquery.min.js b/website/assets/js/jquery.min.js similarity index 100% rename from assets/js/jquery.min.js rename to website/assets/js/jquery.min.js diff --git a/assets/js/jquery.poptrox.min.js b/website/assets/js/jquery.poptrox.min.js similarity index 100% rename from assets/js/jquery.poptrox.min.js rename to website/assets/js/jquery.poptrox.min.js diff --git a/assets/js/main.js b/website/assets/js/main.js similarity index 100% rename from assets/js/main.js rename to website/assets/js/main.js diff --git a/assets/js/util.js b/website/assets/js/util.js similarity index 100% rename from assets/js/util.js rename to website/assets/js/util.js diff --git a/assets/sass/libs/_breakpoints.scss b/website/assets/sass/libs/_breakpoints.scss similarity index 100% rename from assets/sass/libs/_breakpoints.scss rename to website/assets/sass/libs/_breakpoints.scss diff --git a/assets/sass/libs/_functions.scss b/website/assets/sass/libs/_functions.scss similarity index 100% rename from assets/sass/libs/_functions.scss rename to website/assets/sass/libs/_functions.scss diff --git a/assets/sass/libs/_html-grid.scss b/website/assets/sass/libs/_html-grid.scss similarity index 100% rename from assets/sass/libs/_html-grid.scss rename to website/assets/sass/libs/_html-grid.scss diff --git a/assets/sass/libs/_mixins.scss b/website/assets/sass/libs/_mixins.scss similarity index 100% rename from assets/sass/libs/_mixins.scss rename to website/assets/sass/libs/_mixins.scss diff --git a/assets/sass/libs/_vars.scss b/website/assets/sass/libs/_vars.scss similarity index 100% rename from assets/sass/libs/_vars.scss rename to website/assets/sass/libs/_vars.scss diff --git a/assets/sass/libs/_vendor.scss b/website/assets/sass/libs/_vendor.scss similarity index 100% rename from assets/sass/libs/_vendor.scss rename to website/assets/sass/libs/_vendor.scss diff --git a/assets/sass/main.scss b/website/assets/sass/main.scss similarity index 100% rename from assets/sass/main.scss rename to website/assets/sass/main.scss diff --git a/assets/webfonts/fa-brands-400.eot b/website/assets/webfonts/fa-brands-400.eot similarity index 100% rename from assets/webfonts/fa-brands-400.eot rename to website/assets/webfonts/fa-brands-400.eot diff --git a/assets/webfonts/fa-brands-400.svg b/website/assets/webfonts/fa-brands-400.svg similarity index 100% rename from assets/webfonts/fa-brands-400.svg rename to website/assets/webfonts/fa-brands-400.svg diff --git a/assets/webfonts/fa-brands-400.ttf b/website/assets/webfonts/fa-brands-400.ttf similarity index 100% rename from assets/webfonts/fa-brands-400.ttf rename to website/assets/webfonts/fa-brands-400.ttf diff --git a/assets/webfonts/fa-brands-400.woff b/website/assets/webfonts/fa-brands-400.woff similarity index 100% rename from assets/webfonts/fa-brands-400.woff rename to website/assets/webfonts/fa-brands-400.woff diff --git a/assets/webfonts/fa-brands-400.woff2 b/website/assets/webfonts/fa-brands-400.woff2 similarity index 100% rename from assets/webfonts/fa-brands-400.woff2 rename to website/assets/webfonts/fa-brands-400.woff2 diff --git a/assets/webfonts/fa-regular-400.eot b/website/assets/webfonts/fa-regular-400.eot similarity index 100% rename from assets/webfonts/fa-regular-400.eot rename to website/assets/webfonts/fa-regular-400.eot diff --git a/assets/webfonts/fa-regular-400.svg b/website/assets/webfonts/fa-regular-400.svg similarity index 100% rename from assets/webfonts/fa-regular-400.svg rename to website/assets/webfonts/fa-regular-400.svg diff --git a/assets/webfonts/fa-regular-400.ttf b/website/assets/webfonts/fa-regular-400.ttf similarity index 100% rename from assets/webfonts/fa-regular-400.ttf rename to website/assets/webfonts/fa-regular-400.ttf diff --git a/assets/webfonts/fa-regular-400.woff b/website/assets/webfonts/fa-regular-400.woff similarity index 100% rename from assets/webfonts/fa-regular-400.woff rename to website/assets/webfonts/fa-regular-400.woff diff --git a/assets/webfonts/fa-regular-400.woff2 b/website/assets/webfonts/fa-regular-400.woff2 similarity index 100% rename from assets/webfonts/fa-regular-400.woff2 rename to website/assets/webfonts/fa-regular-400.woff2 diff --git a/assets/webfonts/fa-solid-900.eot b/website/assets/webfonts/fa-solid-900.eot similarity index 100% rename from assets/webfonts/fa-solid-900.eot rename to website/assets/webfonts/fa-solid-900.eot diff --git a/assets/webfonts/fa-solid-900.svg b/website/assets/webfonts/fa-solid-900.svg similarity index 100% rename from assets/webfonts/fa-solid-900.svg rename to website/assets/webfonts/fa-solid-900.svg diff --git a/assets/webfonts/fa-solid-900.ttf b/website/assets/webfonts/fa-solid-900.ttf similarity index 100% rename from assets/webfonts/fa-solid-900.ttf rename to website/assets/webfonts/fa-solid-900.ttf diff --git a/assets/webfonts/fa-solid-900.woff b/website/assets/webfonts/fa-solid-900.woff similarity index 100% rename from assets/webfonts/fa-solid-900.woff rename to website/assets/webfonts/fa-solid-900.woff diff --git a/assets/webfonts/fa-solid-900.woff2 b/website/assets/webfonts/fa-solid-900.woff2 similarity index 100% rename from assets/webfonts/fa-solid-900.woff2 rename to website/assets/webfonts/fa-solid-900.woff2 diff --git a/website/code.html b/website/code.html new file mode 100644 index 0000000..d0b905d --- /dev/null +++ b/website/code.html @@ -0,0 +1,92 @@ + + + + + + Slides + + + + + + + + + + + +
+ + +
+ + +
+

Back

+ +
+ + + + + + + + + + + +
Julia CodeFiles on GitHub
Python CodeFiles on GitHub
+
+
+ +
+ + + +
+
Design: HTML5 UP +
+
+ +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/elements.html b/website/elements.html similarity index 100% rename from elements.html rename to website/elements.html diff --git a/images/avatar.jpg b/website/images/avatar.jpg similarity index 100% rename from images/avatar.jpg rename to website/images/avatar.jpg diff --git a/images/bg.jpg b/website/images/bg.jpg similarity index 100% rename from images/bg.jpg rename to website/images/bg.jpg diff --git a/images/bg.png b/website/images/bg.png similarity index 100% rename from images/bg.png rename to website/images/bg.png diff --git a/images/book.png b/website/images/book.png similarity index 100% rename from images/book.png rename to website/images/book.png diff --git a/images/fulls/01.jpg b/website/images/fulls/01.jpg similarity index 100% rename from images/fulls/01.jpg rename to website/images/fulls/01.jpg diff --git a/images/fulls/02.jpg b/website/images/fulls/02.jpg similarity index 100% rename from images/fulls/02.jpg rename to website/images/fulls/02.jpg diff --git a/images/fulls/03.jpg b/website/images/fulls/03.jpg similarity index 100% rename from images/fulls/03.jpg rename to website/images/fulls/03.jpg diff --git a/images/fulls/04.jpg b/website/images/fulls/04.jpg similarity index 100% rename from images/fulls/04.jpg rename to website/images/fulls/04.jpg diff --git a/images/fulls/05.jpg b/website/images/fulls/05.jpg similarity index 100% rename from images/fulls/05.jpg rename to website/images/fulls/05.jpg diff --git a/images/fulls/06.jpg b/website/images/fulls/06.jpg similarity index 100% rename from images/fulls/06.jpg rename to website/images/fulls/06.jpg diff --git a/images/github-mark.png b/website/images/github-mark.png similarity index 100% rename from images/github-mark.png rename to website/images/github-mark.png diff --git a/images/julia.svg b/website/images/julia.svg similarity index 100% rename from images/julia.svg rename to website/images/julia.svg diff --git a/images/logo-square.png b/website/images/logo-square.png similarity index 100% rename from images/logo-square.png rename to website/images/logo-square.png diff --git a/images/logo-square.svg b/website/images/logo-square.svg similarity index 100% rename from images/logo-square.svg rename to website/images/logo-square.svg diff --git a/images/mail.png b/website/images/mail.png similarity index 100% rename from images/mail.png rename to website/images/mail.png diff --git a/images/python.png b/website/images/python.png similarity index 100% rename from images/python.png rename to website/images/python.png diff --git a/images/qe-logo-small.png b/website/images/qe-logo-small.png similarity index 100% rename from images/qe-logo-small.png rename to website/images/qe-logo-small.png diff --git a/images/slides.png b/website/images/slides.png similarity index 100% rename from images/slides.png rename to website/images/slides.png diff --git a/images/thumbs/01.jpg b/website/images/thumbs/01.jpg similarity index 100% rename from images/thumbs/01.jpg rename to website/images/thumbs/01.jpg diff --git a/images/thumbs/02.jpg b/website/images/thumbs/02.jpg similarity index 100% rename from images/thumbs/02.jpg rename to website/images/thumbs/02.jpg diff --git a/images/thumbs/03.jpg b/website/images/thumbs/03.jpg similarity index 100% rename from images/thumbs/03.jpg rename to website/images/thumbs/03.jpg diff --git a/images/thumbs/04.jpg b/website/images/thumbs/04.jpg similarity index 100% rename from images/thumbs/04.jpg rename to website/images/thumbs/04.jpg diff --git a/images/thumbs/05.jpg b/website/images/thumbs/05.jpg similarity index 100% rename from images/thumbs/05.jpg rename to website/images/thumbs/05.jpg diff --git a/images/thumbs/06.jpg b/website/images/thumbs/06.jpg similarity index 100% rename from images/thumbs/06.jpg rename to website/images/thumbs/06.jpg diff --git a/index.html b/website/index.html similarity index 84% rename from index.html rename to website/index.html index 18ee60b..33e2e4d 100644 --- a/index.html +++ b/website/index.html @@ -65,13 +65,13 @@

Download

diff --git a/slides.html b/website/slides.html similarity index 86% rename from slides.html rename to website/slides.html index fb32391..ad9c507 100644 --- a/slides.html +++ b/website/slides.html @@ -31,52 +31,54 @@

Slides

+

Back

+
- + - + - + - + - + - + - + - + - + - + - +
Chapter 0Preface and OverviewPreface and Overview
Chapter 1IntroductionIntroduction
Chapter 2Operators and Fixed PointsOperators and Fixed Points
Chapter 3Markov DynamicsMarkov Dynamics
Chapter 4Optimal StoppingOptimal Stopping
Chapter 5Markov Decision ProcessesMarkov Decision Processes
Chapter 6Stochastic DiscountingStochastic Discounting
Chapter 7ValuationValuation
Chapter 8Recursive Decision ProcessesRecursive Decision Processes
Chapter 9Abstract Dynamic ProgrammingAbstract Dynamic Programming
Chapter 10Continuous TimeContinuous Time