Yuxuan / Economics Notebook library

HANK

Solving a RANK Model

A representative-agent New Keynesian model solved with a global fixed-point algorithm.

Julia 1.12.5 notebook · Static rendering · Code was not re-executed

This notebook solves a representative-agent New Keynesian (RANK) model with a global fixed-point algorithm. It is organized into four parts:

  1. Model: household behavior, firm pricing, monetary and fiscal policy, market clearing, and equilibrium;
  2. Julia implementation: calibration, state grids, and reusable numerical routines;
  3. Solution blocks: the NKPC, representative-household Euler equation, market clearing, and aggregate forecasting tables;
  4. RANK fixed point: the outer iteration, convergence diagnostics, and simulation.

The main equations and algorithmic steps remain visible in the notebook; reusable functions are collected in src/.

1. Julia setup

The notebook activates the repository's pinned Julia environment and loads the model functions from src/. It is self-contained when run from either the repository root or the notebooks/ directory.

using Pkg

function find_project_root(start_dir=pwd())
    dir = abspath(start_dir)
    while true
        isfile(joinpath(dir, "Project.toml")) && return dir
        parent = dirname(dir)
        parent == dir && error("Could not find Project.toml from $(start_dir)")
        dir = parent
    end
end

project_root = find_project_root()
Pkg.activate(project_root)

using LinearAlgebra
using Statistics
using Random
using Printf
using Plots
using HANK

default(
    size=(760, 430),
    linewidth=2,
    framestyle=:box,
    legend=:best,
    gridalpha=0.25,
)

println("Julia version: ", VERSION)
println("Julia threads: ", Threads.nthreads())
println("Project: ", project_root)
  Activating
Julia version: 
 project at `c:\Users\86132\Desktop\Yuxuan ZHAO\minnesota\Research\HANK`
1.12.5
Julia threads: 16
Project: c:\Users\86132\Desktop\Yuxuan ZHAO\minnesota\Research\HANK

2. Model

2.1 Fundamentals and shocks

A representative-agent New Keynesian model combines two blocks:

  • Representative household: chooses consumption, hours, and one-period government bonds.
  • New Keynesian firms: monopolistically competitive firms produce with labor and set prices subject to Rotemberg adjustment costs.

There is no physical capital. A competitive final-good producer aggregates differentiated intermediate goods, the central bank follows a Taylor rule, and the fiscal authority keeps real government debt fixed.

The main aggregate variables are:

Symbol Meaning
$z_t$ aggregate labor productivity
$\eta_t$ monetary-policy shock
$c_t,n_t,b_{t+1}$ consumption, hours, and bond holdings
$w_t,Y_t$ real wage and output
$\Pi_t,i_t,R_t$ gross inflation, nominal rate, and realized real return
$d_t,T_t$ dividends and lump-sum transfers

The aggregate exogenous state is

$$ s_t=(z_t,\eta_t). $$

TFP and the monetary-policy shock follow independent AR(1) processes:

$$ \begin{aligned} \log z_{t+1}&=\rho_z\log z_t+\varepsilon^z_{t+1},\\ \eta_{t+1}&=\rho_\eta\eta_t+\varepsilon^\eta_{t+1}. \end{aligned} $$

Tauchen discretization gives finite-state transition matrices $P_z$ and $P_\eta$. Independence implies

$$ P_s=P_z\otimes P_\eta. $$

2.2 Household problem

The representative household has preferences

$$ E_0\sum_{t=0}^{\infty}\beta^t \left(\log c_t-\frac{n_t^2}{2}\right). $$

Budget constraint

Let $b_t$ denote real government-bond wealth carried into period $t$. The original budget constraint is

$$ c_t+b_{t+1} =R_tb_t+w_tn_t+d_t+T_t, \qquad b_{t+1}\geq0, $$

where the realized gross real return is

$$ R_t=\frac{1+i_{t-1}}{\Pi_t}. $$

The fiscal authority keeps real debt fixed at $B$ and rebates its net financing need through

$$ T_t=B-R_tB. $$

Define shifted assets $\tilde a_t=b_t-B$. The household budget becomes

$$ c_t+\tilde a_{t+1} =R_t\tilde a_t+w_tn_t+d_t, \qquad \tilde a_{t+1}\geq-B. $$

Bellman equation

Define nonlabor cash on hand as

$$ m_t\equiv R_t\tilde a_t+d_t. $$

Let the aggregate shock be $s=(z,\eta)$. In a recursive equilibrium, aggregate variables are Markov functions

$$ X(s)=\bigl(w(s),Y(s)\bigr), \qquad \Pi(s)\equiv\Pi\bigl(s,X(s)\bigr). $$

Current inflation and the Taylor rule determine the nominal return

$$ 1+i(s) =\bar R\Pi(s)^{\varphi_\pi}\exp(\eta). $$

Conditional on next-period shock $s'$, the future real return is

$$ R(s';s) =\frac{1+i(s)}{\Pi(s')}. $$

The household's recursive problem is therefore

$$ \begin{aligned} V(m,s) =\max_{c,n,\tilde a'}\quad & \log c-\frac{n^2}{2} +\beta\sum_{s'}P(s'\mid s) V(m',s')\\ \text{s.t.}\quad & c+\tilde a'=m+w(s)n,\\ &\tilde a'\geq-B,\qquad c>0,\\ &m'=R(s';s)\tilde a'+d(s'). \end{aligned} $$

The solution gives the complete household policy functions

$$ c=h_c(m,s), \qquad n=h_n(m,s), \qquad \tilde a'=h_a(m,s). $$

The Bellman equation makes the timing explicit: the household knows the current nominal rate but integrates over next-period shocks, real returns, and dividends.

Optimality conditions

The intratemporal labor condition is

$$ n=\frac{w(s)}{c}. $$

The Kuhn--Tucker condition for bond saving is

$$ \frac{1}{c} \geq \beta\sum_{s'}P(s'\mid s) R(s';s)\frac{1}{c'}, $$

with equality whenever $\tilde a'>-B$.

Bond-market clearing

The representative household holds the entire fixed government debt supply:

$$ b_t=B \quad\Longleftrightarrow\quad \tilde a_t=0 $$

at every date. Hence $m(s)=d(s)$. Since $B>0$, the borrowing constraint is slack in equilibrium, so the Euler equation holds with equality. The equilibrium household budget simplifies to

$$ c_t=w_tn_t+d_t. $$

2.3 Firm problem

Final-good demand

A competitive final-good producer combines a continuum of differentiated intermediate goods with a CES technology:

$$ Y_t =\left[ \int_0^1 y_{j,t}^{\frac{\epsilon-1}{\epsilon}}dj \right]^{\frac{\epsilon}{\epsilon-1}}, \qquad \epsilon>1. $$

It chooses intermediate inputs to minimize expenditure:

$$ \min_{\{y_{j,t}\}_{j\in[0,1]}} \int_0^1 p_{j,t}y_{j,t}dj $$

subject to the CES production constraint. The resulting aggregate price index and demand for variety $j$ are

$$ P_t =\left[ \int_0^1 p_{j,t}^{1-\epsilon}dj \right]^{\frac{1}{1-\epsilon}}, \qquad y_{j,t} =\left(\frac{p_{j,t}}{P_t}\right)^{-\epsilon}Y_t. $$

Intermediate firms and current profits

Intermediate firm $j$ produces with labor only:

$$ y_{j,t}=z_t\ell_{j,t}, \qquad \ell_{j,t} =\frac{1}{z_t} \left(\frac{p_{j,t}}{P_t}\right)^{-\epsilon}Y_t. $$

The firm inherits last period's price $p_{j,t-1}$ and pays a Rotemberg cost when changing it. Measured in units of the final good, current profits are

$$ \mathcal D_{j,t} =\left[ \left(\frac{p_{j,t}}{P_t}\right)^{1-\epsilon} -\frac{w_t}{z_t} \left(\frac{p_{j,t}}{P_t}\right)^{-\epsilon} -\frac{\kappa}{2} \left(\frac{p_{j,t}}{p_{j,t-1}}-1\right)^2 \right]Y_t. $$

A larger $\kappa$ makes prices more rigid; $\kappa=0$ gives flexible prices.

Dynamic price setting

Let $p_{-1}$ be the firm's inherited price, $s$ the aggregate shock, $X=(w,Y)$ the current aggregate variables, and $P$ the aggregate price level. Taking aggregate variables and the equilibrium functions as given, the firm's value function is

$$ J(p_{-1},s,X,P) =\max_p\left\{ \mathcal D(p,p_{-1};s,X,P) +\beta_F\sum_{s'}\Pr(s'\mid s) J\!\left(p,s',X',P'\right) \right\}, $$

where

$$ X'=X(s'), \qquad P'=\Pi(s',X')P. $$

The firm chooses its current price $p$. This price becomes next period's inherited price, while future aggregate variables are read from the equilibrium functions. Future profits are discounted by the constant factor $\beta_F$.

Symmetric equilibrium and the nonlinear NKPC

The individual firm first chooses $p_{j,t}$ while treating the aggregate price index $P_t$ as given. Symmetry is imposed only after deriving the firm's first-order condition:

$$ p_{j,t}=P_t, \qquad y_{j,t}=Y_t, \qquad \Pi_t =\frac{P_t}{P_{t-1}} =\frac{p_{j,t}}{p_{j,t-1}}, \qquad Y_t=z_tN_t. $$

The resulting nonlinear New Keynesian Phillips curve is

$$ (1-\epsilon)+\epsilon\frac{w_t}{z_t} -\kappa(\Pi_t-1)\Pi_t +\beta_F E_t\!\left[ \frac{Y_{t+1}}{Y_t} \kappa(\Pi_{t+1}-1)\Pi_{t+1} \right]=0. $$

Under flexible prices, this reduces to

$$ \frac{w_t}{z_t}=\frac{\epsilon-1}{\epsilon}, $$

so real marginal cost equals the inverse desired markup. Given the equilibrium functions for future wages and output, the nonlinear NKPC determines the inflation policy $\Pi(s,X)=\Pi(z,\eta,w,Y)$.

Dividends and resource costs

In symmetric equilibrium, real revenue is $Y_t$, the wage bill is $(w_t/z_t)Y_t$, and Rotemberg adjustment costs absorb final goods. Dividends paid to the representative household are therefore

$$ d_t =\left[ 1-\frac{w_t}{z_t} -\frac{\kappa}{2}(\Pi_t-1)^2 \right]Y_t. $$

Thus price adjustment costs affect both firm pricing and the resources distributed to the household.

2.4 Monetary and fiscal policy

The central bank follows

$$ 1+i_t=\bar R\Pi_t^{\varphi_\pi}\exp(\eta_t). $$

The next realized real return is

$$ R_{t+1} =\frac{1+i_t}{\Pi_{t+1}} =\bar R \frac{\Pi_t^{\varphi_\pi}\exp(\eta_t)}{\Pi_{t+1}}. $$

The fiscal authority fixes $B_t=B$ and sets $T_t=B-R_tB$. In RANK, $B$ affects the off-equilibrium borrowing limit but not the equilibrium allocation: interest receipts on $B$ are exactly offset by the fiscal transfer rule.

2.5 Market clearing and feasibility

Given the aggregate state $s_t$, the two unknown aggregates $(w_t,Y_t)$ satisfy

$$ \tilde a_{t+1}=0 \qquad\text{(bond market)}, $$

$$ n_t=\frac{Y_t}{z_t} \qquad\text{(labor market)}. $$

The first condition is equivalent to household bond holdings of $b_{t+1}=B$; the second equates household labor supply with firms' labor demand. Real-resource-cost dividends imply the goods-market feasibility condition

$$ c_t+\frac{\kappa}{2}(\Pi_t-1)^2Y_t=Y_t. $$

This resource constraint is not a third independent market-clearing equation. It holds automatically by Walras' law.

2.6 Recursive equilibrium

Let

$$ s=(z,\eta), \qquad X(s)=\bigl(w(s),Y(s)\bigr). $$

For the firm pricing policy, $X=(w,Y)$ denotes a generic current aggregate point.

A recursive RANK equilibrium is a collection

$$ \left\{ V(m,s),h_c(m,s),h_n(m,s),h_a(m,s); \ \Pi(s,X),\Pi(s),d(s),i(s),R(s';s),T(s';s); \ w(s),Y(s) \right\}. $$

which satisfy:

  1. Household optimality: Given $X(s)$, $R(s';s)$, and $d(s)$, $V$ solves the household Bellman equation and $(h_c,h_n,h_a)$ are optimal.

$$ \begin{aligned} V(m,s) =\max_{c,n,\tilde a'}\quad & \log c-\frac{n^2}{2} +\beta\sum_{s'}P(s'\mid s) V(m',s')\\ \text{s.t.}\quad & c+\tilde a'=m+w(s)n,\\ &\tilde a'\geq-B,\qquad c>0,\\ &m'=R(s';s)\tilde a'+d(s'). \end{aligned} $$

  1. Firm optimality: Given the future equilibrium function $X(s')$, $\Pi(s,X)$ solves the nonlinear NKPC:

$$ \begin{aligned} 0={}&(1-\epsilon)+\epsilon\frac{w}{z} -\kappa\bigl(\Pi(s,X)-1\bigr)\Pi(s,X)\\ &+\beta_F\sum_{s'}P(s'\mid s) \frac{Y(s')}{Y} \kappa\left[\Pi\bigl(s',X(s')\bigr)-1\right] \Pi\bigl(s',X(s')\bigr). \end{aligned} $$

  1. Policy consistency: The equilibrium policies satisfy the pricing, dividend, Taylor-rule, Fisher, and fixed-debt fiscal equations:

$$ \begin{aligned} \Pi(s)&=\Pi\bigl(s,X(s)\bigr),\\ d(s) &=\left[1-\frac{w(s)}{z} -\frac{\kappa}{2}\bigl(\Pi(s)-1\bigr)^2\right]Y(s),\\ 1+i(s) &=\bar R\Pi(s)^{\varphi_\pi}\exp(\eta),\\ R(s';s) &=\frac{1+i(s)}{\Pi(s')},\\ T(s';s) &=B-R(s';s)B. \end{aligned} $$

  1. Aggregate consistency and market clearing: $w(s)$ and $Y(s)$ satisfy the bond- and labor-market equations:

$$ h_a\bigl(d(s),s\bigr)=0, \qquad h_n\bigl(d(s),s\bigr)=\frac{Y(s)}{z}. $$

2.7 Numerical algorithm: inner and outer loops

The global solution represents equilibrium wages and output by two tables over the aggregate Markov state:

$$ \widehat X(s)=\bigl(\widehat w(s),\widehat Y(s)\bigr), \qquad s=(z,\eta). $$

Inner equilibrium: given $\widehat X$

  1. Solve the forward-looking nonlinear NKPC and obtain $\Pi(s,X)$ and $d(s,X)$.

  2. Impose bond-market clearing, $\tilde a=\tilde a'=0$. Given $\widehat X$, use the household budget constraint and labor FOC to recover current and future consumption, and construct the Euler residual $F_E(w,Y;s)$.

  3. For every state $s$, find $(w,Y)$ such that the Euler equation and labor-market clearing condition hold:

    $$ F_E(w,Y;s)=0, \qquad h_n\bigl(d(s,X),s,X\bigr)=\frac{Y}{z}. $$

    Denote the solution by

    $$ X^{\mathrm{clear}}(s) =\bigl(w^{\mathrm{clear}}(s),Y^{\mathrm{clear}}(s)\bigr). $$

Outer loop: update the forecast tables

Update in logs using damping parameter $\lambda$:

$$ \log\widehat X^{\mathrm{new}}(s) =(1-\lambda)\log\widehat X^{\mathrm{old}}(s) +\lambda\log X^{\mathrm{clear}}(s). $$

The NKPC is warm-started from the preceding outer iteration. Early iterations use only a few pricing updates; the update budget rises as the forecast distance falls. A strict pricing--table polishing loop is applied before the household policies are recovered.

3. Calibration and state grids

The following calibration is used throughout the notebook.

Parameter Value Economic meaning
$\beta$ 0.975 household discount factor
$\beta_F$ 0.975 firm discount factor
$\epsilon$ 10 elasticity across intermediate varieties
$\kappa$ 100 Rotemberg price-adjustment cost
$\varphi_\pi$ 1.5 Taylor-rule response to inflation
$\bar R$ $0.997/0.975$ Taylor-rule intercept
$B$ 5 fixed real government debt
$(\rho_z,\sigma_z)$ $(0.9,0.07\sqrt{1-0.9^2})$ TFP process
$(\rho_\eta,\sigma_\eta)$ $(0.9,0.002\sqrt{1-0.9^2})$ monetary-policy shock process
$m_\tau$ 2.9 Tauchen grid width

The numerical configuration uses 11 TFP states, 11 monetary-policy states, 7 wage nodes, and 31 output nodes.

par = RANKParams()
model = RANKModel(par)

println("Deterministic steady state")
println(repeat("-", 48))
@printf("%-28s %12.8f\n", "gross inflation", model.steady_inflation)
@printf("%-28s %12.8f\n", "gross real return", model.steady_real_return)
@printf("%-28s %12.8f\n", "real wage", model.steady_wage)
@printf("%-28s %12.8f\n", "output", model.steady_output)
@printf("%-28s %12.8f\n", "consumption", model.steady_consumption)

println("\nGrid sizes")
println(repeat("-", 48))
println("aggregate shock nodes  = ", (par.n_z, par.n_eta))
println("wage/output nodes      = ", (par.n_wages, par.n_outputs))
println("wage grid              = ", extrema(model.wage_grid))
println("output grid            = ", extrema(model.output_grid))
Deterministic steady state
------------------------------------------------
gross inflation                1.00602711
gross real return              1.02564103
real wage                      0.90151586
output                         0.95034534
consumption                    0.94861922

Grid sizes
------------------------------------------------
aggregate shock nodes  = (11, 11)
wage/output nodes      = (7, 31)
wage grid              = (0.55, 1.25)
output grid            = (0.6, 1.4)

3.1 Deterministic steady state

In a deterministic steady state, the household Euler equation requires

$$ R^*=\frac{1}{\beta}. $$

The Taylor rule implies

$$ R^*=\bar R(\Pi^*)^{\varphi_\pi-1}. $$

Combining the two equations gives

$$ \Pi^* =\left(\frac{1}{\beta\bar R}\right)^{1/(\varphi_\pi-1)}. $$

Under the calibration above, $\Pi^*\approx1.006$. The steady wage follows from the nonlinear NKPC, while steady consumption, hours, and output follow from the labor FOC, production, and the resource constraint.

4. Step 0: initialize perceived equilibrium tables

The initial guess uses the flexible-price/log-utility benchmark:

$$ \widehat w^{(0)}(z,\eta)=w^*z, \qquad \widehat Y^{(0)}(z,\eta)=Y^*z. $$

The guess ignores the small monetary shock initially but already captures the first-order TFP scaling. This keeps the first market-clearing pass inside the aggregate grids.

initial_rules = initial_rank_forecast_rules(model)

println("Initial forecast ranges")
println("wage   = ", extrema(initial_rules.wage))
println("output = ", extrema(initial_rules.output))

eta_middle = div(par.n_eta, 2) + 1
initial_rule_plot = plot(
    model.z_grid,
    initial_rules.wage[:, eta_middle];
    label="wage forecast",
    xlabel="TFP z",
    ylabel="level",
    title="Initial perceived equilibrium at middle monetary state",
)
plot!(
    initial_rule_plot,
    model.z_grid,
    initial_rules.output[:, eta_middle];
    label="output forecast",
)
initial_rule_plot
Initial forecast ranges
wage   = (0.7338697834719826, 1.1013936442065662)
output = (0.7736189234325769, 1.1610492549174372)
No description has been provided for this image

5. Step 1: solve the nonlinear NKPC

Define

$$ x(s,w,Y)=\kappa\bigl(\Pi(s,w,Y)-1\bigr)\Pi(s,w,Y). $$

Given the forecast tables, the nonlinear NKPC can be written as

$$ \begin{aligned} x(s,w,Y) ={}&(1-\epsilon)+\epsilon\frac{w}{z}\\ &+\beta_F\sum_{s'}P(s'|s) \frac{\widehat Y(s')}{Y} x\bigl(s',\widehat w(s'),\widehat Y(s')\bigr). \end{aligned} $$

Starting from $x^{(0)}(s,w,Y)$, one fixed-point update is

$$ \begin{aligned} x^{\mathrm{new}}(s,w,Y) ={}&(1-\epsilon)+\epsilon\frac{w}{z}\\ &+\beta_F\sum_{s'}P(s'|s) \frac{\widehat Y(s')}{Y} x^{(j)}\bigl(s',\widehat w(s'),\widehat Y(s')\bigr). \end{aligned} $$

The implementation applies damping,

$$ x^{(j+1)}=(1-\lambda_\Pi)x^{(j)}+\lambda_\Pi x^{\mathrm{new}}, $$

and iterates until $\lVert x^{(j+1)}-x^{(j)}\rVert_\infty$ is below tolerance. Gross inflation is then recovered from the positive root of $x=\kappa(\Pi^2-\Pi)$:

$$ \Pi=\frac{1+\sqrt{1+4x/\kappa}}{2}. $$

For off-grid evaluation, multiply the NKPC by current output and define

$$ q(s,w,Y)=Yx(s,w,Y). $$

Then

$$ q=Y\left[(1-\epsilon)+\epsilon\frac{w}{z}\right] +\beta_F\sum_{s'}P(s'|s)q(s',\widehat X(s')), $$

which is bilinear in current $(w,Y)$. The code therefore interpolates $q=Yx$, divides by $Y$ to recover $x$, and then recovers $\Pi$.

initial_nkpc = solve_rank_nkpc(
    model, par, initial_rules; max_steps=20,
)

println("NKPC iterations           = ", length(initial_nkpc.error_history))
println("NKPC update error          = ", last(initial_nkpc.error_history))
println("inflation policy range     = ", extrema(initial_nkpc.inflation_policy))
println("inflation array size       = ", size(initial_nkpc.inflation_policy))
NKPC iterations           = 20
NKPC update error          = 0.007760218007954123
inflation policy range     = (0.9534287949848759, 1.060768453929688)
inflation array size       = (11, 11, 7, 31)

6. Step 2: solve the reduced household equilibrium

At outer iteration $k$, Step 1 has solved the pricing block under the forecast table $\widehat X^k$.

Input: $\quad s,\widehat X^k(s'),\Pi^k(s,X)$

Output: $\quad F^k(w,Y;s)$

Bond-market clearing places the representative household on the equilibrium locus

$$ \tilde a=\tilde a'=0. $$

Because $B>0$, the constraint $\tilde a'\geq-B$ is slack at this point, so the Euler equation holds with equality. For a trial $X=(w,Y)$, evaluate

$$ \Pi=\Pi^k(s,X), \qquad d(s,X) =\left[1-\frac{w}{z} -\frac{\kappa}{2}\bigl(\Pi^k(s,X)-1\bigr)^2\right]Y. $$

Substituting $n=w/c$ into $c=wn+d(s,X)$ gives

$$ c^2-d(s,X)c-w^2=0. $$

Taking the positive root gives

$$ \mathcal C^k(s,X) =\frac{d(s,X)+\sqrt{d(s,X)^2+4w^2}}{2}, \qquad \mathcal N^k(s,X) =\frac{w}{\mathcal C^k(s,X)}. $$

These are candidate equilibrium-locus choices conditional on the trial $X$; they are not yet the complete household policies.

For each future state $s'$, set $X'=\widehat X^k(s')$ and compute

$$ R'(s';s,X) =\frac{1+i(s,X)}{\Pi^k(s',\widehat X^k(s'))}. $$

The two residuals are

$$ F_E^k(w,Y;s) =\frac{1}{\mathcal C^k(s,X)} -\beta\sum_{s'}P(s'\mid s) R'(s';s,X) \frac{1}{\mathcal C^k(s',\widehat X^k(s'))}, $$

$$ F_N^k(w,Y;s) =\mathcal N^k(s,X)-\frac{Y}{z}. $$

Thus Step 2 constructs

$$ F^k(w,Y;s) =\begin{bmatrix}F_E^k(w,Y;s)\\F_N^k(w,Y;s)\end{bmatrix}. $$

z_middle = argmin(abs.(model.log_z_grid))
eta_middle = argmin(abs.(model.log_eta_grid))

household_probe = rank_household_residuals(
    initial_rules.wage[z_middle, eta_middle],
    initial_rules.output[z_middle, eta_middle],
    initial_nkpc.inflation_policy,
    initial_rules,
    model,
    z_middle,
    eta_middle,
    par,
)

println("Household block at the initial middle-state forecast")
println(repeat("-", 58))
@printf("%-34s %16.6e\n", "Euler residual", household_probe.euler)
@printf("%-34s %16.6e\n", "labor residual", household_probe.labor)
@printf("%-34s %16.8f\n", "consumption", household_probe.consumption)
@printf("%-34s %16.8f\n", "hours", household_probe.hours)
Household block at the initial middle-state forecast
----------------------------------------------------------
Euler residual                         2.159470e-03
labor residual                        -8.902108e-04
consumption                              0.94690485
hours                                    0.94945513

7. Step 3: solve for $X^{\mathrm{clear}}$

For every state $s$, use $\widehat X^k(s)$ as the initial guess and solve

$$ F_E^k(w,Y;s)=0, \qquad F_N^k(w,Y;s)=0. $$

The implementation uses a damped two-dimensional Newton method with a finite-difference Jacobian. Its output is

$$ X^{\mathrm{clear},k}(s) =\left(w^{\mathrm{clear},k}(s),Y^{\mathrm{clear},k}(s)\right). $$

Repeating the root solve at every $(z,\eta)$ node produces the complete table $X^{\mathrm{clear},k}$ passed to the outer update.

first_equilibrium_map = solve_rank_household_grid(
    initial_nkpc.inflation_policy,
    initial_rules,
    model,
    par;
    tolerance=1e-9,
)
first_fitted_rules = RANKForecastRules(
    first_equilibrium_map.wage,
    first_equilibrium_map.output,
)

println("First market-clearing pass")
println(repeat("-", 52))
@printf("%-31s %16.3e\n", "maximum Euler residual",
    maximum(abs.(first_equilibrium_map.euler_residual)))
@printf("%-31s %16.3e\n", "maximum labor residual",
    maximum(abs.(first_equilibrium_map.labor_residual)))
@printf("%-31s %16.3e\n", "forecast-table distance",
    rank_forecast_distance(initial_rules, first_fitted_rules))
println("all statewise Newton solves converged = ", all(first_equilibrium_map.converged))
First market-clearing pass
----------------------------------------------------
maximum Euler residual                 9.145e-10
maximum labor residual                 2.518e-10
forecast-table distance                5.181e-02
all statewise Newton solves converged = true

8. Step 4: update the aggregate tables

Update the perceived equilibrium table in logs:

$$ \log\widehat X^{k+1}(s) =(1-\lambda)\log\widehat X^k(s) +\lambda\log X^{\mathrm{clear},k}(s). $$

The aggregate loop is

forecast tables
    -> iterate the NKPC fixed point
    -> construct the Euler and labor residuals
    -> solve for Xclear at every shock node
    -> damp the wage/output tables
    -> repeat

The NKPC update is warm-started. Its step budget rises from 20 to 40, 80, 160, and 320 as the aggregate-table distance falls. Once the coarse fixed point is reached, a strict NKPC polishing loop makes the pricing and aggregate tables mutually consistent.

9. Step 5: recover the complete household policies

After the aggregate fixed point has converged, hold $X(s)$ and $\Pi(s)$ fixed and solve the household problem on $(m,s)$ with EGM. Only the consumption policy is iterated.

Given $h_c^j(m,s)$ and an exogenous choice $\tilde a'$, compute

$$ m'(s')=R(s';s)\tilde a'+d(s'), $$

$$ c =\left[ \beta\sum_{s'}P(s'\mid s) \frac{R(s';s)}{h_c^j(m'(s'),s')} \right]^{-1}, \qquad n=\frac{w(s)}{c}. $$

The corresponding endogenous cash node is

$$ m^{\mathrm{endo}}=c+\tilde a'-w(s)n. $$

Interpolating from $m^{\mathrm{endo}}$ to the fixed cash grid gives

$$ h_a^{j+1}(m,s), \qquad h_c^{j+1}(m,s), \qquad h_n^{j+1}(m,s). $$

Below the first endogenous node, the borrowing constraint binds and $h_a(m,s)=-B$. Iterate until the consumption-policy distance converges. Finally evaluate the policies at the equilibrium cash point $m=d(s)$ and verify

$$ h_a(d(s),s)=0, \qquad h_n(d(s),s)=\frac{Y(s)}{z}. $$

solve_start = time()
result = solve_rank(
    par;
    initial_rules=initial_rules,
    max_outer_iterations=300,
    outer_tolerance=1e-5,
    forecast_damping=0.20,
    nkpc_steps_per_outer=20,
    nkpc_max_steps_per_outer=320,
    polish_tolerance=1e-7,
    market_tolerance=1e-9,
    policy_tolerance=1e-8,
    verbose=true,
)
solve_seconds = time() - solve_start
summary_stats = rank_solution_summary(result)

println("RANK solution summary")
println(repeat("-", 58))
println(rpad("converged", 34), summary_stats.converged)
println(rpad("aggregate outer iterations", 34), summary_stats.outer_iterations)
println(rpad("strict polishing iterations", 34), summary_stats.polish_iterations)
println(rpad("household EGM iterations", 34), summary_stats.household_iterations)
@printf("%-34s %.3e\n", "forecast-table distance", summary_stats.rule_distance)
@printf("%-34s %.3e\n", "NKPC iteration error", summary_stats.nkpc_iteration_error)
@printf("%-34s %.3e\n", "household policy error", summary_stats.egm_iteration_error)
@printf("%-34s %.3e\n", "maximum bond residual", summary_stats.max_bond_residual)
@printf("%-34s %.3e\n", "policy labor residual", summary_stats.max_policy_labor_residual)
@printf("%-34s %.3f seconds\n", "wall-clock solve time", solve_seconds)
 iter | NKPC error | rule distance | max clearing | clear share
----------------------------------------------------------------------
    1 |   7.76e-03 |      5.18e-02 |     9.14e-10 |      1.0000
    2 |   2.04e-02 |      4.77e-02 |     8.09e-10 |      1.0000
    3 |   2.65e-02 |      4.32e-02 |     9.05e-10 |      1.0000
    4 |   2.78e-02 |      3.88e-02 |     9.69e-10 |      1.0000
    5 |   2.66e-02 |      3.49e-02 |     9.85e-10 |      1.0000
    6 |   2.40e-02 |      3.49e-02 |     9.05e-10 |      1.0000
    7 |   2.08e-02 |      3.72e-02 |     9.00e-10 |      1.0000
    8 |   1.75e-02 |      3.88e-02 |     9.43e-10 |      1.0000
    9 |   1.43e-02 |      3.95e-02 |     9.85e-10 |      1.0000
   10 |   1.13e-02 |      3.94e-02 |     9.96e-10 |      1.0000
   11 |   8.73e-03 |      3.86e-02 |     8.81e-10 |      1.0000
   12 |   7.75e-03 |      3.73e-02 |     7.52e-10 |      1.0000
   13 |   7.27e-03 |      3.56e-02 |     7.24e-10 |      1.0000
   14 |   6.69e-03 |      3.38e-02 |     4.61e-10 |      1.0000
   15 |   5.98e-03 |      3.20e-02 |     7.15e-10 |      1.0000
   16 |   5.17e-03 |      3.03e-02 |     8.03e-10 |      1.0000
   17 |   4.28e-03 |      2.87e-02 |     9.78e-10 |      1.0000
   18 |   3.36e-03 |      2.74e-02 |     5.34e-10 |      1.0000
   19 |   2.44e-03 |      2.62e-02 |     8.76e-10 |      1.0000
   20 |   2.45e-03 |      2.53e-02 |     6.42e-10 |      1.0000
   21 |   2.68e-03 |      2.46e-02 |     8.05e-10 |      1.0000
   22 |   2.88e-03 |      2.41e-02 |     8.60e-10 |      1.0000
   23 |   3.01e-03 |      2.36e-02 |     6.55e-10 |      1.0000
   24 |   3.07e-03 |      2.33e-02 |     7.80e-10 |      1.0000
   25 |   3.04e-03 |      2.29e-02 |     7.64e-10 |      1.0000
   26 |   2.92e-03 |      2.26e-02 |     9.78e-10 |      1.0000
   27 |   2.72e-03 |      2.22e-02 |     8.43e-10 |      1.0000
   28 |   2.46e-03 |      2.17e-02 |     7.94e-10 |      1.0000
   29 |   2.16e-03 |      2.12e-02 |     6.25e-10 |      1.0000
   30 |   1.84e-03 |      2.06e-02 |     9.76e-10 |      1.0000
   31 |   1.52e-03 |      1.99e-02 |     7.08e-10 |      1.0000
   32 |   1.22e-03 |      1.92e-02 |     5.03e-10 |      1.0000
   33 |   9.60e-04 |      1.84e-02 |     3.51e-10 |      1.0000
   34 |   7.41e-04 |      1.77e-02 |     8.95e-10 |      1.0000
   35 |   5.71e-04 |      1.69e-02 |     8.38e-10 |      1.0000
   36 |   4.96e-04 |      1.62e-02 |     9.98e-10 |      1.0000
   37 |   4.93e-04 |      1.55e-02 |     9.74e-10 |      1.0000
   38 |   4.53e-04 |      1.48e-02 |     9.16e-10 |      1.0000
   39 |   3.83e-04 |      1.43e-02 |     9.87e-10 |      1.0000
   40 |   3.96e-04 |      1.37e-02 |     9.77e-10 |      1.0000
   41 |   4.44e-04 |      1.33e-02 |     8.74e-10 |      1.0000
   42 |   4.98e-04 |      1.29e-02 |     9.44e-10 |      1.0000
   43 |   5.50e-04 |      1.26e-02 |     7.97e-10 |      1.0000
   44 |   5.93e-04 |      1.23e-02 |     9.83e-10 |      1.0000
   45 |   6.24e-04 |      1.20e-02 |     9.34e-10 |      1.0000
   46 |   6.39e-04 |      1.17e-02 |     9.95e-10 |      1.0000
   47 |   6.36e-04 |      1.15e-02 |     9.79e-10 |      1.0000
   48 |   6.18e-04 |      1.12e-02 |     9.30e-10 |      1.0000
   49 |   5.87e-04 |      1.10e-02 |     9.72e-10 |      1.0000
   50 |   5.44e-04 |      1.07e-02 |     9.30e-10 |      1.0000
   51 |   4.93e-04 |      1.05e-02 |     9.89e-10 |      1.0000
   52 |   4.40e-04 |      1.02e-02 |     1.00e-09 |      1.0000
   53 |   3.85e-04 |      9.87e-03 |     9.72e-10 |      1.0000
   54 |   4.41e-05 |      9.65e-03 |     9.95e-10 |      1.0000
   55 |   3.02e-05 |      9.32e-03 |     9.95e-10 |      1.0000
   56 |   4.39e-05 |      8.98e-03 |     9.80e-10 |      1.0000
   57 |   4.74e-05 |      8.65e-03 |     9.04e-10 |      1.0000
   58 |   4.29e-05 |      8.33e-03 |     7.73e-10 |      1.0000
   59 |   3.33e-05 |      8.04e-03 |     7.65e-10 |      1.0000
   60 |   2.14e-05 |      7.78e-03 |     9.28e-10 |      1.0000
   61 |   1.46e-05 |      7.53e-03 |     8.76e-10 |      1.0000
   62 |   2.30e-05 |      7.30e-03 |     7.52e-10 |      1.0000
   63 |   2.88e-05 |      7.09e-03 |     8.93e-10 |      1.0000
   64 |   3.16e-05 |      6.89e-03 |     7.83e-10 |      1.0000
   65 |   3.17e-05 |      6.69e-03 |     9.60e-10 |      1.0000
   66 |   2.96e-05 |      6.50e-03 |     9.76e-10 |      1.0000
   67 |   2.61e-05 |      6.30e-03 |     8.95e-10 |      1.0000
   68 |   2.20e-05 |      6.11e-03 |     8.56e-10 |      1.0000
   69 |   1.80e-05 |      5.92e-03 |     8.13e-10 |      1.0000
   70 |   1.45e-05 |      5.73e-03 |     7.71e-10 |      1.0000
   71 |   1.18e-05 |      5.55e-03 |     9.59e-10 |      1.0000
   72 |   9.96e-06 |      5.37e-03 |     9.93e-10 |      1.0000
   73 |   9.00e-06 |      5.19e-03 |     9.59e-10 |      1.0000
   74 |   8.70e-06 |      5.02e-03 |     9.52e-10 |      1.0000
   75 |   8.85e-06 |      4.86e-03 |     9.20e-10 |      1.0000
   76 |   9.21e-06 |      4.70e-03 |     8.72e-10 |      1.0000
   77 |   9.61e-06 |      4.55e-03 |     8.23e-10 |      1.0000
   78 |   9.89e-06 |      4.40e-03 |     7.55e-10 |      1.0000
   79 |   9.98e-06 |      4.26e-03 |     6.96e-10 |      1.0000
   80 |   9.85e-06 |      4.12e-03 |     9.86e-10 |      1.0000
   81 |   9.51e-06 |      3.99e-03 |     9.31e-10 |      1.0000
   82 |   9.02e-06 |      3.86e-03 |     8.91e-10 |      1.0000
   83 |   8.44e-06 |      3.73e-03 |     8.19e-10 |      1.0000
   84 |   7.83e-06 |      3.61e-03 |     7.61e-10 |      1.0000
   85 |   7.23e-06 |      3.49e-03 |     8.73e-10 |      1.0000
   86 |   6.70e-06 |      3.37e-03 |     9.38e-10 |      1.0000
   87 |   6.24e-06 |      3.26e-03 |     8.69e-10 |      1.0000
   88 |   5.87e-06 |      3.15e-03 |     8.05e-10 |      1.0000
   89 |   5.57e-06 |      3.04e-03 |     7.44e-10 |      1.0000
   90 |   5.34e-06 |      2.94e-03 |     9.42e-10 |      1.0000
   91 |   5.15e-06 |      2.84e-03 |     8.74e-10 |      1.0000
   92 |   4.99e-06 |      2.74e-03 |     8.12e-10 |      1.0000
   93 |   4.84e-06 |      2.65e-03 |     7.56e-10 |      1.0000
   94 |   4.69e-06 |      2.56e-03 |     7.04e-10 |      1.0000
   95 |   4.53e-06 |      2.47e-03 |     9.25e-10 |      1.0000
   96 |   4.36e-06 |      2.39e-03 |     8.98e-10 |      1.0000
   97 |   4.19e-06 |      2.31e-03 |     7.46e-10 |      1.0000
   98 |   4.00e-06 |      2.23e-03 |     6.21e-10 |      1.0000
   99 |   3.82e-06 |      2.15e-03 |     5.43e-10 |      1.0000
  100 |   3.64e-06 |      2.08e-03 |     4.75e-10 |      1.0000
  101 |   3.47e-06 |      2.01e-03 |     4.32e-10 |      1.0000
  102 |   3.31e-06 |      1.94e-03 |     4.02e-10 |      1.0000
  103 |   3.15e-06 |      1.87e-03 |     3.75e-10 |      1.0000
  104 |   3.02e-06 |      1.80e-03 |     3.49e-10 |      1.0000
  105 |   2.89e-06 |      1.74e-03 |     3.25e-10 |      1.0000
  106 |   2.77e-06 |      1.68e-03 |     3.03e-10 |      1.0000
  107 |   2.66e-06 |      1.62e-03 |     2.83e-10 |      1.0000
  108 |   2.56e-06 |      1.56e-03 |     2.64e-10 |      1.0000
  109 |   2.46e-06 |      1.51e-03 |     2.46e-10 |      1.0000
  110 |   2.36e-06 |      1.46e-03 |     2.30e-10 |      1.0000
  111 |   2.27e-06 |      1.40e-03 |     2.15e-10 |      1.0000
  112 |   2.18e-06 |      1.35e-03 |     2.01e-10 |      1.0000
  113 |   2.09e-06 |      1.31e-03 |     1.88e-10 |      1.0000
  114 |   2.01e-06 |      1.26e-03 |     1.76e-10 |      1.0000
  115 |   1.93e-06 |      1.22e-03 |     9.25e-10 |      1.0000
  116 |   1.85e-06 |      1.17e-03 |     8.18e-10 |      1.0000
  117 |   1.77e-06 |      1.13e-03 |     7.24e-10 |      1.0000
  118 |   1.70e-06 |      1.09e-03 |     6.41e-10 |      1.0000
  119 |   1.63e-06 |      1.05e-03 |     5.68e-10 |      1.0000
  120 |   1.56e-06 |      1.01e-03 |     5.04e-10 |      1.0000
  121 |   1.50e-06 |      9.78e-04 |     4.47e-10 |      1.0000
  122 |   4.08e-08 |      9.43e-04 |     3.99e-10 |      1.0000
  123 |   3.76e-08 |      9.09e-04 |     3.54e-10 |      1.0000
  124 |   3.77e-08 |      8.76e-04 |     3.14e-10 |      1.0000
  125 |   3.71e-08 |      8.45e-04 |     2.79e-10 |      1.0000
  126 |   3.58e-08 |      8.14e-04 |     2.48e-10 |      1.0000
  127 |   3.41e-08 |      7.85e-04 |     9.64e-10 |      1.0000
  128 |   3.23e-08 |      7.56e-04 |     8.89e-10 |      1.0000
  129 |   3.06e-08 |      7.29e-04 |     8.20e-10 |      1.0000
  130 |   2.92e-08 |      7.03e-04 |     7.57e-10 |      1.0000
  131 |   2.79e-08 |      6.77e-04 |     6.99e-10 |      1.0000
  132 |   2.68e-08 |      6.53e-04 |     6.45e-10 |      1.0000
  133 |   2.58e-08 |      6.29e-04 |     5.95e-10 |      1.0000
  134 |   2.49e-08 |      6.06e-04 |     5.50e-10 |      1.0000
  135 |   2.40e-08 |      5.84e-04 |     5.08e-10 |      1.0000
  136 |   2.31e-08 |      5.63e-04 |     4.69e-10 |      1.0000
  137 |   2.22e-08 |      5.42e-04 |     4.33e-10 |      1.0000
  138 |   2.13e-08 |      5.22e-04 |     4.00e-10 |      1.0000
  139 |   2.05e-08 |      5.03e-04 |     3.70e-10 |      1.0000
  140 |   1.97e-08 |      4.85e-04 |     3.42e-10 |      1.0000
  141 |   1.89e-08 |      4.67e-04 |     3.16e-10 |      1.0000
  142 |   1.82e-08 |      4.50e-04 |     2.92e-10 |      1.0000
  143 |   1.75e-08 |      4.34e-04 |     2.70e-10 |      1.0000
  144 |   1.68e-08 |      4.18e-04 |     2.49e-10 |      1.0000
  145 |   1.62e-08 |      4.02e-04 |     2.31e-10 |      1.0000
  146 |   1.56e-08 |      3.88e-04 |     2.13e-10 |      1.0000
  147 |   1.50e-08 |      3.73e-04 |     9.35e-10 |      1.0000
  148 |   1.44e-08 |      3.60e-04 |     8.61e-10 |      1.0000
  149 |   1.38e-08 |      3.46e-04 |     7.93e-10 |      1.0000
  150 |   1.33e-08 |      3.34e-04 |     7.31e-10 |      1.0000
  151 |   1.28e-08 |      3.21e-04 |     6.74e-10 |      1.0000
  152 |   1.23e-08 |      3.10e-04 |     6.21e-10 |      1.0000
  153 |   1.18e-08 |      2.98e-04 |     5.73e-10 |      1.0000
  154 |   1.14e-08 |      2.87e-04 |     9.98e-10 |      1.0000
  155 |   1.10e-08 |      2.77e-04 |     9.25e-10 |      1.0000
  156 |   1.05e-08 |      2.66e-04 |     8.59e-10 |      1.0000
  157 |   1.01e-08 |      2.57e-04 |     7.96e-10 |      1.0000
  158 |   9.75e-09 |      2.47e-04 |     9.83e-10 |      1.0000
  159 |   9.38e-09 |      2.38e-04 |     6.85e-10 |      1.0000
  160 |   9.73e-09 |      2.29e-04 |     6.36e-10 |      1.0000
  161 |   9.36e-09 |      2.21e-04 |     5.90e-10 |      1.0000
  162 |   9.71e-09 |      2.12e-04 |     5.47e-10 |      1.0000
  163 |   9.35e-09 |      2.05e-04 |     5.08e-10 |      1.0000
  164 |   9.70e-09 |      1.97e-04 |     4.71e-10 |      1.0000
  165 |   9.33e-09 |      1.90e-04 |     4.37e-10 |      1.0000
  166 |   9.69e-09 |      1.83e-04 |     4.05e-10 |      1.0000
  167 |   9.33e-09 |      1.76e-04 |     3.76e-10 |      1.0000
  168 |   9.69e-09 |      1.69e-04 |     3.49e-10 |      1.0000
  169 |   9.33e-09 |      1.63e-04 |     3.24e-10 |      1.0000
  170 |   9.70e-09 |      1.57e-04 |     3.00e-10 |      1.0000
  171 |   9.33e-09 |      1.51e-04 |     2.79e-10 |      1.0000
  172 |   9.71e-09 |      1.46e-04 |     2.58e-10 |      1.0000
  173 |   9.35e-09 |      1.40e-04 |     2.40e-10 |      1.0000
  174 |   9.73e-09 |      1.35e-04 |     2.22e-10 |      1.0000
  175 |   9.36e-09 |      1.30e-04 |     2.06e-10 |      1.0000
  176 |   9.75e-09 |      1.25e-04 |     9.94e-10 |      1.0000
  177 |   9.39e-09 |      1.21e-04 |     9.84e-10 |      1.0000
  178 |   9.79e-09 |      1.16e-04 |     9.93e-10 |      1.0000
  179 |   9.41e-09 |      1.12e-04 |     9.84e-10 |      1.0000
  180 |   9.82e-09 |      1.08e-04 |     9.76e-10 |      1.0000
  181 |   9.46e-09 |      1.04e-04 |     9.77e-10 |      1.0000
  182 |   9.86e-09 |      9.97e-05 |     9.72e-10 |      1.0000
  183 |   9.49e-09 |      9.60e-05 |     9.81e-10 |      1.0000
  184 |   9.92e-09 |      9.24e-05 |     9.95e-10 |      1.0000
  185 |   9.54e-09 |      8.90e-05 |     7.46e-10 |      1.0000
  186 |   9.98e-09 |      8.57e-05 |     9.99e-10 |      1.0000
  187 |   9.61e-09 |      8.25e-05 |     9.98e-10 |      1.0000
  188 |   9.25e-09 |      7.94e-05 |     9.94e-10 |      1.0000
  189 |   9.68e-09 |      7.65e-05 |     1.00e-09 |      1.0000
  190 |   9.30e-09 |      7.36e-05 |     1.00e-09 |      1.0000
  191 |   9.74e-09 |      7.09e-05 |     1.00e-09 |      1.0000
  192 |   9.38e-09 |      6.82e-05 |     9.94e-10 |      1.0000
  193 |   9.82e-09 |      6.57e-05 |     1.00e-09 |      1.0000
  194 |   9.46e-09 |      6.32e-05 |     9.99e-10 |      1.0000
  195 |   9.90e-09 |      6.09e-05 |     9.97e-10 |      1.0000
  196 |   9.55e-09 |      5.86e-05 |     9.98e-10 |      1.0000
  197 |   9.19e-09 |      5.64e-05 |     1.00e-09 |      1.0000
  198 |   9.66e-09 |      5.43e-05 |     9.97e-10 |      1.0000
  199 |   9.29e-09 |      5.23e-05 |     9.92e-10 |      1.0000
  200 |   9.74e-09 |      5.03e-05 |     9.98e-10 |      1.0000
  201 |   9.39e-09 |      4.85e-05 |     9.97e-10 |      1.0000
  202 |   9.87e-09 |      4.67e-05 |     9.99e-10 |      1.0000
  203 |   9.50e-09 |      4.49e-05 |     9.91e-10 |      1.0000
  204 |   9.99e-09 |      4.32e-05 |     9.87e-10 |      1.0000
  205 |   9.62e-09 |      4.16e-05 |     9.99e-10 |      1.0000
  206 |   9.25e-09 |      4.01e-05 |     9.98e-10 |      1.0000
  207 |   9.74e-09 |      3.86e-05 |     9.98e-10 |      1.0000
  208 |   9.39e-09 |      3.71e-05 |     9.99e-10 |      1.0000
  209 |   9.90e-09 |      3.57e-05 |     9.97e-10 |      1.0000
  210 |   9.54e-09 |      3.44e-05 |     9.96e-10 |      1.0000
  211 |   9.17e-09 |      3.31e-05 |     1.00e-09 |      1.0000
  212 |   9.66e-09 |      3.19e-05 |     9.98e-10 |      1.0000
  213 |   9.32e-09 |      3.07e-05 |     9.98e-10 |      1.0000
  214 |   9.83e-09 |      2.96e-05 |     1.00e-09 |      1.0000
  215 |   9.47e-09 |      2.84e-05 |     1.00e-09 |      1.0000
  216 |   9.99e-09 |      2.74e-05 |     9.98e-10 |      1.0000
  217 |   9.63e-09 |      2.64e-05 |     9.99e-10 |      1.0000
  218 |   9.31e-09 |      2.54e-05 |     9.98e-10 |      1.0000
  219 |   9.82e-09 |      2.44e-05 |     9.85e-10 |      1.0000
  220 |   9.45e-09 |      2.35e-05 |     9.98e-10 |      1.0000
  221 |   1.00e-08 |      2.26e-05 |     9.99e-10 |      1.0000
  222 |   9.62e-09 |      2.18e-05 |     9.96e-10 |      1.0000
  223 |   9.27e-09 |      2.10e-05 |     9.96e-10 |      1.0000
  224 |   9.85e-09 |      2.02e-05 |     9.94e-10 |      1.0000
  225 |   9.50e-09 |      1.94e-05 |     9.92e-10 |      1.0000
  226 |   9.14e-09 |      1.87e-05 |     9.98e-10 |      1.0000
  227 |   9.68e-09 |      1.80e-05 |     9.92e-10 |      1.0000
  228 |   9.35e-09 |      1.73e-05 |     9.99e-10 |      1.0000
  229 |   9.89e-09 |      1.67e-05 |     9.98e-10 |      1.0000
  230 |   9.52e-09 |      1.61e-05 |     1.00e-09 |      1.0000
  231 |   9.18e-09 |      1.55e-05 |     9.97e-10 |      1.0000
  232 |   9.77e-09 |      1.49e-05 |     9.96e-10 |      1.0000
  233 |   9.42e-09 |      1.43e-05 |     1.00e-09 |      1.0000
  234 |   9.06e-09 |      1.38e-05 |     9.99e-10 |      1.0000
  235 |   9.62e-09 |      1.33e-05 |     9.98e-10 |      1.0000
  236 |   9.31e-09 |      1.28e-05 |     9.99e-10 |      1.0000
  237 |   9.92e-09 |      1.23e-05 |     9.99e-10 |      1.0000
  238 |   9.55e-09 |      1.18e-05 |     9.98e-10 |      1.0000
  239 |   9.15e-09 |      1.14e-05 |     9.99e-10 |      1.0000
  240 |   9.77e-09 |      1.10e-05 |     9.97e-10 |      1.0000
  241 |   9.40e-09 |      1.06e-05 |     9.98e-10 |      1.0000
  242 |   9.12e-09 |      1.02e-05 |     9.97e-10 |      1.0000
  243 |   9.73e-09 |      9.79e-06 |     9.93e-10 |      1.0000
P   1 |   9.91e-09 |      7.96e-06 |     9.99e-10 |      1.0000
P   2 |   9.58e-09 |      7.44e-06 |     9.98e-10 |      1.0000
P   3 |   9.86e-09 |      6.95e-06 |     9.97e-10 |      1.0000
P   4 |   9.34e-09 |      6.50e-06 |     9.91e-10 |      1.0000
P   5 |   9.64e-09 |      6.07e-06 |     1.00e-09 |      1.0000
P   6 |   9.10e-09 |      5.68e-06 |     9.90e-10 |      1.0000
P   7 |   9.38e-09 |      5.30e-06 |     9.95e-10 |      1.0000
P   8 |   9.91e-09 |      4.96e-06 |     9.93e-10 |      1.0000
P   9 |   9.24e-09 |      4.63e-06 |     9.92e-10 |      1.0000
P  10 |   9.57e-09 |      4.33e-06 |     1.00e-09 |      1.0000
P  11 |   9.05e-09 |      4.05e-06 |     9.87e-10 |      1.0000
P  12 |   9.51e-09 |      3.78e-06 |     9.97e-10 |      1.0000
P  13 |   9.84e-09 |      3.53e-06 |     1.00e-09 |      1.0000
P  14 |   9.06e-09 |      3.30e-06 |     9.99e-10 |      1.0000
P  15 |   9.52e-09 |      3.09e-06 |     9.79e-10 |      1.0000
P  16 |   9.00e-09 |      2.89e-06 |     9.95e-10 |      1.0000
P  17 |   9.36e-09 |      2.70e-06 |     9.99e-10 |      1.0000
P  18 |   9.82e-09 |      2.52e-06 |     9.99e-10 |      1.0000
P  19 |   9.33e-09 |      2.36e-06 |     9.95e-10 |      1.0000
P  20 |   9.65e-09 |      2.20e-06 |     9.99e-10 |      1.0000
P  21 |   9.06e-09 |      2.06e-06 |     9.98e-10 |      1.0000
P  22 |   9.49e-09 |      1.92e-06 |     9.95e-10 |      1.0000
P  23 |   9.16e-09 |      1.80e-06 |     9.95e-10 |      1.0000
P  24 |   9.60e-09 |      1.68e-06 |     9.89e-10 |      1.0000
P  25 |   9.99e-09 |      1.57e-06 |     9.96e-10 |      1.0000
P  26 |   9.35e-09 |      1.47e-06 |     9.91e-10 |      1.0000
P  27 |   9.80e-09 |      1.37e-06 |     9.98e-10 |      1.0000
P  28 |   9.05e-09 |      1.28e-06 |     9.94e-10 |      1.0000
P  29 |   9.57e-09 |      1.20e-06 |     9.89e-10 |      1.0000
P  30 |   9.03e-09 |      1.12e-06 |     9.97e-10 |      1.0000
P  31 |   9.68e-09 |      1.05e-06 |     9.91e-10 |      1.0000
P  32 |   9.20e-09 |      9.78e-07 |     1.00e-09 |      1.0000
P  33 |   9.79e-09 |      9.14e-07 |     1.00e-09 |      1.0000
P  34 |   9.34e-09 |      8.54e-07 |     9.93e-10 |      1.0000
P  35 |   9.82e-09 |      7.98e-07 |     9.97e-10 |      1.0000
P  36 |   9.16e-09 |      7.47e-07 |     9.95e-10 |      1.0000
P  37 |   9.70e-09 |      6.98e-07 |     9.92e-10 |      1.0000
P  38 |   9.21e-09 |      6.52e-07 |     9.92e-10 |      1.0000
P  39 |   9.59e-09 |      6.09e-07 |     9.93e-10 |      1.0000
P  40 |   9.00e-09 |      5.70e-07 |     9.93e-10 |      1.0000
P  41 |   9.59e-09 |      5.32e-07 |     9.99e-10 |      1.0000
P  42 |   9.11e-09 |      4.98e-07 |     9.94e-10 |      1.0000
P  43 |   9.92e-09 |      4.65e-07 |     9.96e-10 |      1.0000
P  44 |   9.46e-09 |      4.35e-07 |     9.96e-10 |      1.0000
P  45 |   8.83e-09 |      4.07e-07 |     9.99e-10 |      1.0000
P  46 |   9.27e-09 |      3.80e-07 |     9.94e-10 |      1.0000
P  47 |   8.80e-09 |      3.56e-07 |     9.97e-10 |      1.0000
P  48 |   9.67e-09 |      3.32e-07 |     1.00e-09 |      1.0000
P  49 |   8.84e-09 |      3.11e-07 |     9.99e-10 |      1.0000
P  50 |   9.61e-09 |      2.90e-07 |     9.95e-10 |      1.0000
P  51 |   8.97e-09 |      2.72e-07 |     9.99e-10 |      1.0000
P  52 |   9.83e-09 |      2.54e-07 |     9.89e-10 |      1.0000
P  53 |   9.28e-09 |      2.38e-07 |     9.92e-10 |      1.0000
P  54 |   8.84e-09 |      2.23e-07 |     9.95e-10 |      1.0000
P  55 |   9.50e-09 |      2.08e-07 |     9.83e-10 |      1.0000
P  56 |   9.47e-09 |      1.95e-07 |     9.99e-10 |      1.0000
P  57 |   8.70e-09 |      1.83e-07 |     9.72e-10 |      1.0000
P  58 |   9.13e-09 |      1.71e-07 |     9.83e-10 |      1.0000
P  59 |   8.74e-09 |      1.60e-07 |     9.99e-10 |      1.0000
P  60 |   9.43e-09 |      1.50e-07 |     9.99e-10 |      1.0000
P  61 |   8.89e-09 |      1.40e-07 |     9.96e-10 |      1.0000
P  62 |   9.67e-09 |      1.31e-07 |     9.99e-10 |      1.0000
P  63 |   9.47e-09 |      1.23e-07 |     9.95e-10 |      1.0000
P  64 |   8.82e-09 |      1.15e-07 |     9.99e-10 |      1.0000
P  65 |   9.47e-09 |      1.08e-07 |     9.96e-10 |      1.0000
P  66 |   9.12e-09 |      1.01e-07 |     9.96e-10 |      1.0000
P  67 |   9.91e-09 |      9.42e-08 |     9.96e-10 |      1.0000
EGM     1 | policy error 6.864e-01
EGM   100 | policy error 1.164e-03
EGM   200 | policy error 5.920e-05
EGM   300 | policy error 3.635e-06
EGM   400 | policy error 2.357e-07
EGM   500 | policy error 1.567e-08
EGM   517 | policy error 9.899e-09
RANK solution summary
----------------------------------------------------------
converged                         true
aggregate outer iterations        243
strict polishing iterations       67
household EGM iterations          517
forecast-table distance            9.418e-08
NKPC iteration error               9.913e-09
household policy error             9.899e-09
maximum bond residual              6.742e-07
policy labor residual              3.181e-07
wall-clock solve time              133.163 seconds
aggregate_iterations = collect(eachindex(result.history))
history_has_phase = !isempty(result.history) && hasproperty(first(result.history), :phase)
outer_count = history_has_phase ?
    count(entry -> entry.phase == :outer, result.history) : length(result.history)
rule_distance = [entry.rule_distance for entry in result.history]
clearing_error = [max(entry.max_clearing_error, eps()) for entry in result.history]

aggregate_convergence = plot(
    aggregate_iterations,
    rule_distance;
    yscale=:log10,
    label="forecast-table distance",
    xlabel="outer iteration",
    ylabel="error (log scale)",
    title="Aggregate fixed point and polishing",
)
plot!(
    aggregate_convergence,
    aggregate_iterations,
    clearing_error;
    label="max Euler/labor residual",
)
vline!(aggregate_convergence, [outer_count]; label="strict polishing", linestyle=:dash)

egm_error = result.household.error_history
household_convergence = plot(
    eachindex(egm_error),
    egm_error;
    yscale=:log10,
    label=false,
    xlabel="EGM iteration",
    ylabel="policy error (log scale)",
    title="Household policy iteration",
)

plot(aggregate_convergence, household_convergence; layout=(1, 2), size=(950, 380))
No description has been provided for this image

9.1 Household policy functions

The policies below are functions of cash on hand $m$. The vertical line marks the equilibrium point $m=d(s)$ in the middle aggregate state. The plot omits the far-right numerical boundary, where the finite asset grid forces savings to its upper endpoint.

household = result.household
z_middle = argmin(abs.(model.log_z_grid))
eta_middle = argmin(abs.(model.log_eta_grid))
middle_state = z_middle + (eta_middle - 1) * par.n_z
equilibrium_cash = household.environment.dividend[middle_state]

# Do not plot the artificial upper-asset boundary.
saving_slice = household.saving[:, middle_state]
plot_asset_limit = 0.95 * last(model.asset_grid)
last_interior = findlast(value -> value <= plot_asset_limit, saving_slice)
plot_range = firstindex(household.cash_grid):last_interior
plot_cash = household.cash_grid[plot_range]

p_hc = plot(
    plot_cash,
    household.consumption[plot_range, middle_state];
    label=false,
    xlabel="cash on hand m",
    ylabel="consumption",
    title="h_c(m,s)",
)
vline!(p_hc, [equilibrium_cash]; label="m=d(s)", linestyle=:dash)

p_ha = plot(
    plot_cash,
    household.saving[plot_range, middle_state];
    label=false,
    xlabel="cash on hand m",
    ylabel="next assets",
    title="h_a(m,s)",
)
vline!(p_ha, [equilibrium_cash]; label="m=d(s)", linestyle=:dash)

p_hn = plot(
    plot_cash,
    household.hours[plot_range, middle_state];
    label=false,
    xlabel="cash on hand m",
    ylabel="hours",
    title="h_n(m,s)",
)
vline!(p_hn, [equilibrium_cash]; label="m=d(s)", linestyle=:dash)

household_policy_plot =
    plot(p_hc, p_ha, p_hn; layout=(1, 3), size=(1050, 330))
No description has been provided for this image

10. Solution checks

The final solution verifies the aggregate fixed point and the recovered household policies separately. In particular, the EGM policies must clear the bond and labor markets when evaluated at $m=d(s)$.

diagnostics = rank_equilibrium_diagnostics(result)

println("Equilibrium diagnostics")
println(repeat("-", 62))
@printf("%-36s %16.3e\n", "forecast-table distance", diagnostics.forecast_distance)
@printf("%-36s %16.3e\n", "maximum Euler residual", diagnostics.max_euler_residual)
@printf("%-36s %16.3e\n", "maximum NKPC residual", diagnostics.max_nkpc_residual)
@printf("%-36s %16.3e\n", "maximum EGM bond residual", diagnostics.max_bond_residual)
@printf("%-36s %16.3e\n", "EGM labor residual", diagnostics.max_policy_labor_residual)
@printf("%-36s %16.3e\n", "household policy error", diagnostics.household_policy_error)
@printf("%-36s %16.3e\n", "minimum endogenous-grid spacing", diagnostics.minimum_egm_spacing)
println(rpad("cash grid", 37), diagnostics.household_cash_bounds)
println(rpad("cash reached by transitions", 37), diagnostics.reached_cash_range)

@assert result.converged "RANK solution did not converge"
@assert diagnostics.all_markets_converged
@assert diagnostics.forecast_distance <= 1.1e-7
@assert diagnostics.max_euler_residual < 1e-7
@assert diagnostics.max_bond_residual < 2e-6
@assert diagnostics.max_policy_labor_residual < 2e-6
@assert diagnostics.household_policy_error <= 1.1e-8
@assert diagnostics.minimum_egm_spacing > 0.0
@assert diagnostics.max_nkpc_residual < 2e-6
@assert diagnostics.wage_boundary_share == 0.0
@assert diagnostics.output_boundary_share == 0.0
Equilibrium diagnostics
--------------------------------------------------------------
forecast-table distance                     9.418e-08
maximum Euler residual                      9.088e-09
maximum NKPC residual                       3.780e-07
maximum EGM bond residual                   6.742e-07
EGM labor residual                          3.181e-07
household policy error                      9.899e-09
minimum endogenous-grid spacing             1.583e-02
cash grid                            (-7.230772711904202, 70.54572192101112)
cash reached by transitions          (-5.735070892040446, 69.05002010114737)

10.1 Aggregate equilibrium functions

The following slices hold the monetary shock at its middle node and show how the solved allocation varies with TFP. Since the model is nonlinear, these are levels rather than local linear impulse responses.

equilibrium = result.equilibrium
eta_middle = div(par.n_eta, 2) + 1

p_wy = plot(
    model.z_grid,
    equilibrium.wage[:, eta_middle];
    label="wage",
    xlabel="TFP z",
    ylabel="level",
    title="Wage and output",
)
plot!(p_wy, model.z_grid, equilibrium.output[:, eta_middle]; label="output")

p_cp = plot(
    model.z_grid,
    equilibrium.consumption[:, eta_middle];
    label="consumption",
    xlabel="TFP z",
    ylabel="level",
    title="Consumption and hours",
)
plot!(p_cp, model.z_grid, equilibrium.hours[:, eta_middle]; label="hours")

p_pi = plot(
    model.z_grid,
    400 .* log.(equilibrium.inflation[:, eta_middle]);
    label=false,
    xlabel="TFP z",
    ylabel="annualized log points",
    title="Inflation",
)

p_i = plot(
    model.z_grid,
    400 .* log.(1 .+ equilibrium.nominal_rate[:, eta_middle]);
    label=false,
    xlabel="TFP z",
    ylabel="annualized log points",
    title="Nominal policy rate",
)

plot(p_wy, p_cp, p_pi, p_i; layout=(2, 2), size=(900, 650))
No description has been provided for this image

11. Generalized impulse responses

For an aggregate variable $q$, compare a shocked initial state $s_0^\delta$ with the middle baseline state $s_0^0$:

$$ \operatorname{GIRF}_q(h) =E\left[q(s_h)\mid s_0=s_0^\delta\right] -E\left[q(s_h)\mid s_0=s_0^0\right]. $$

Here the aggregate state is an exogenous finite Markov chain. Hence the conditional expectations are computed exactly:

$$ \operatorname{GIRF}_q(h) =\left(\mu_h^\delta-\mu_h^0\right)'q, \qquad \mu_{h+1}=P'\mu_h. $$

No Monte Carlo simulation is needed for these responses.

tfp_girf = rank_generalized_irf(
    result;
    shock=:tfp,
    direction=1,
    horizon=40,
)
monetary_girf = rank_generalized_irf(
    result;
    shock=:monetary,
    direction=1,
    horizon=40,
)

@printf("TFP impact shock       = %+.3f innovation s.d.\n",
    tfp_girf.impact_innovation_sd)
@printf("Monetary impact shock  = %+.3f innovation s.d.\n",
    monetary_girf.impact_innovation_sd)
TFP impact shock       = +1.331 innovation s.d.
Monetary impact shock  = +1.331 innovation s.d.

11.1 Positive TFP shock

function plot_rank_girf(girf; figure_title)
    periods = 0:(girf.horizon - 1)
    shock_response = if girf.shock == :tfp
        girf.response.log_tfp ./ par.sigma_z
    else
        girf.response.log_monetary ./ par.sigma_eta
    end
    shock_title = girf.shock == :tfp ? "TFP shock" : "Monetary-policy shock"

    p_shock = plot(periods, shock_response;
        label=false, xlabel="period", ylabel="innovation s.d.", title=shock_title)
    p_output = plot(periods, 100 .* girf.response.log_output;
        label=false, xlabel="period", ylabel="percent", title="Output")
    p_inflation = plot(periods, 400 .* girf.response.log_inflation;
        label=false, xlabel="period", ylabel="annualized log points", title="Inflation")
    p_rate = plot(periods, 400 .* girf.response.log_nominal_rate;
        label=false, xlabel="period", ylabel="annualized log points", title="Nominal rate")

    for panel in (p_shock, p_output, p_inflation, p_rate)
        hline!(panel, [0.0]; color=:black, alpha=0.4, linestyle=:dash, label=false)
    end
    return plot(
        p_shock,
        p_output,
        p_inflation,
        p_rate;
        layout=(2, 2),
        size=(850, 600),
        plot_title=figure_title,
    )
end

tfp_girf_plot = plot_rank_girf(
    tfp_girf;
    figure_title="RANK GIRF: positive TFP shock",
)
No description has been provided for this image

11.2 Contractionary monetary-policy shock

monetary_girf_plot = plot_rank_girf(
    monetary_girf;
    figure_title="RANK GIRF: contractionary monetary-policy shock",
)
No description has been provided for this image

12. Simulation moments

After convergence, simulation draws the finite aggregate Markov chain and looks up the corresponding equilibrium allocation. Because $s=(z,\eta)$ is discrete and persistent, the simulated series are step functions: flat segments mean that the Markov state has not changed, not that a numerical boundary has been reached. The figure shows the complete simulated sample.

simulation = simulate_rank(result; length_path=par.simulation_length, seed=par.seed)

println("Simulation moments")
println(repeat("-", 52))
@printf("%-28s %12.6f\n", "mean output", mean(simulation.output))
@printf("%-28s %12.6f\n", "std log output", std(log.(simulation.output)))
@printf("%-28s %12.6f\n", "mean consumption", mean(simulation.consumption))
@printf("%-28s %12.6f\n", "std log consumption", std(log.(simulation.consumption)))
@printf("%-28s %12.6f\n", "mean gross inflation", mean(simulation.inflation))
@printf("%-28s %12.6f\n", "std log inflation", std(log.(simulation.inflation)))
println(rpad("simulated output range", 29), extrema(simulation.output))
println(rpad("numerical output grid", 29), extrema(model.output_grid))

plot_window = 1:par.simulation_length
p_output = plot(
    plot_window,
    simulation.output[plot_window];
    seriestype=:steppost,
    label=false,
    ylabel="output",
    title="Discrete-state RANK simulation",
)
p_inflation = plot(
    plot_window,
    400 .* log.(simulation.inflation[plot_window]);
    seriestype=:steppost,
    label=false,
    xlabel="period",
    ylabel="annualized log points",
)
simulation_plot = plot(p_output, p_inflation; layout=(2, 1), size=(850, 560))
Simulation moments
----------------------------------------------------
mean output                      0.949308
std log output                   0.061350
mean consumption                 0.942388
std log consumption              0.066444
mean gross inflation             1.004872
std log inflation                0.011224
simulated output range       (0.8304655267614807, 1.1505418503074896)
numerical output grid        (0.6, 1.4)
No description has been provided for this image