Back
+ +Julia Code | +Files on GitHub | +
Python Code | +Files on GitHub | +
+ +
Links
++
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 @@ + + + + +
+Julia Code | +Files on GitHub | +
Python Code | +Files on GitHub | +
Chapter 0 | -Preface and Overview | +Preface and Overview |
Chapter 1 | -Introduction | +Introduction |
Chapter 2 | -Operators and Fixed Points | +Operators and Fixed Points |
Chapter 3 | -Markov Dynamics | +Markov Dynamics |
Chapter 4 | -Optimal Stopping | +Optimal Stopping |
Chapter 5 | -Markov Decision Processes | +Markov Decision Processes |
Chapter 6 | -Stochastic Discounting | +Stochastic Discounting |
Chapter 7 | -Valuation | +Valuation |
Chapter 8 | -Recursive Decision Processes | +Recursive Decision Processes |
Chapter 9 | -Abstract Dynamic Programming | +Abstract Dynamic Programming |
Chapter 10 | -Continuous Time | +Continuous Time |