Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Part A of the Online Appendix tests interpolation methods on a consumption-saving problem with health investment, the model of Ludwig and Schoen (2018) extended with the wage and depreciation risk that White (2015) adds. This notebook first solves it as HARK does. EGMn then splits each period into stages, each one a closed-form inversion, whose last stage produces the curvilinear mesh that ENGINE interpolates. A final part compares the two solutions and reruns two of the archived comparisons.

1The Household Problem

Each period the household observes market resources mt and health capital ht, and chooses consumption ct and health investment nt:

vt(mt,ht)=maxct≥0,nt≥0⁡u(ct)+β𝒮t𝔼t[vt+1(mt+1,ht+1)]Ht=ht+g(nt),at=mt−ct−nt,𝒮t=1−𝖣t/(1+Ht),ht+1=(1−δt+1)Ht,mt+1=𝖱at+ωt+1Ht,

with u(c)=c1−ρ/(1−ρ) and g(n)=(γ/ε)nε. Investment builds post-investment health Ht, which lowers the probability of death and raises next period’s labor income ωt+1Ht before it depreciates at the random rate δt+1. The online appendix’s timing comparison sweeps the number of backward-induction cycles and the grid size around one base point, a 44×50 grid solved over ten cycles. The calibration below is that base point.

Parameter                     Code name      Value
----------------------------  -------------  ------------
Discount factor               DiscFac        0.95
Relative risk aversion        CRRA           0.5
Interest factor               Rfree          1.03
Health production elasticity  HealthProdExp  0.35
Health production scale       HealthProdFac  1.0
Maximum death probability     DieProbMax     0.5
Mean depreciation rate        DeprRteMean    0.05
Smallest wage draw                           0.0850
Asset grid nodes              aXtraCount     44
Extra nodes near zero assets  aXtraExtra     7
Health grid nodes             HLvlGrid       50
Health grid range             HLvlGrid       [0.01, 50.0]
Backward-induction cycles     cycles         10

HARK’s asset grid adds the extra nodes near zero to the regular ones. In the timing table, a grid labelled 44×50 counts only the regular asset nodes and is the grid of this calibration.

2HARK’s Baseline Solution

White (2015) folds the transitions and the risk into the end-of-period value wt(at,Ht)=β𝒮t𝔼t[vt+1] and inverts both first-order conditions at once on a rectangular grid of (at,Ht):

ct=(wta)−1/ρ,g′(nt)=wtawtH,mt=at+ct+nt,ht=Ht−g(nt).

The inverted states (mt,ht) form a curvilinear mesh, which White interpolates with the sector-walking method HARK implements as Curvilinear2DInterp. BasicHealthConsumerType runs that solver with that interpolant.

periods solved: 10
endogenous mesh at t = 0: 60 x 50 nodes

Every period’s solution is one interpolant with three outputs: the inverse value u−1(vt), consumption and health investment. Its nodes at t=0:

<Figure size 1050x750 with 1 Axes>

Each near-vertical string of dots is one asset node traced across the health grid. At low post-investment health the inversion assigns more investment, which maps the same asset node to more market resources and less beginning health. Every string therefore bends to the right as health falls. Nodes crowd near mt=0, where the asset grid is densest.

Below are cross-sections of the two policies at health capital ht=20.

<Figure size 1050x675 with 1 Axes>
at h = 20, the zero-asset mesh line lies at m = 2.58

Both policies rise with market resources. The kink at the printed value of mt marks where the household starts to save. Below it the liquidity constraint binds. Every additional unit of resources then goes to consumption or investment. Investment stays positive at every mt>0, since the marginal product g′(n) grows without bound as n→0.

3The Stage Decomposition

EGMn splits the period into three stages, each solved by one inversion. Following the online appendix, vt0≡vt denotes the value at the first decision stage and vt1 the value once investment is chosen.

  1. The post-decision stage values the end-of-period states, wt(at,Ht), together with its marginals wta and wtH.

  2. The consumption stage solves vt1(ℓt,Ht)=maxct⁡u(ct)+wt(ℓt−ct,Ht). For each fixed Ht this is a standard consumption-saving problem: ct=(wta)−1/ρ and liquid wealth ℓt=at+ct.

  3. The health investment stage solves vt0(mt,ht)=maxnt⁡vt1(mt−nt,ht+g(nt)). Its first-order condition g′(nt)=(vt1)ℓ/(vt1)H inverts in closed form. The transitions then return mt=ℓt+nt and ht=Ht−g(nt).

SequentialHealthConsumerType keeps each stage as its own interpolant. Run here with ENGINE:

post_decision      Engine2DInterp
consumption        Engine2DInterp
health_investment  Engine2DInterp

The first two stages are defined on the rectangular (a,H) grid. Only the last one, which maps to the decision-time states (m,h), needs curvilinear interpolation.

The cell below solves the model again at the default margin. It compares every period’s mesh and the policies at a grid of off-mesh points with the matched-margin solution.

search margin used for the health meshes: 16
default margin 2: identical meshes in all periods: True
default margin 2: identical policies at t = 0:      True

Every Stage in Closed Form

The solver’s stage functions are ordinary module functions, which this notebook calls directly. Rebuilding the post-decision marginals at t=0 from next period’s solution and applying the two inversions reproduces the mesh ENGINE interpolates. Both first-order conditions hold at every node.

max |u'(c) / w_a - 1|        = 2.2e-16
max |g'(n) w_H / w_a - 1|    = 4.4e-16
max |m - ENGINE mesh m|      = 0.0e+00
max |h - ENGINE mesh h|      = 0.0e+00

The investment stage works on the consumption stage’s own nodes (ℓ,H), where the envelope conditions give (vt1)ℓ=u′(ct)=wta and (vt1)H=wtH. No interpolation therefore separates the two inversions. Because every wage draw is positive (the calibration table prints the smallest), a household can end the period with no assets. Beneath the inverted nodes, the solver adds constrained_N liquidity-constrained rows at a=0 and a row at m=0.

The Endogenous Mesh

The paper’s grid-transformation figure, redrawn below on its 25×20 grid for a two-period model, shows the map from the regular post-decision grid to the curvilinear decision-time mesh:

<Figure size 1950x825 with 3 Axes>

Lines of constant post-investment health and constant assets keep their order through the warp, the index-monotone property (IPM) ENGINE requires. The check below runs on every period’s mesh of the ten-cycle solution.

t  IPM breaks
-  ----------
0  0
1  0
2  0
3  0
4  0
5  0
6  0
7  0
8  0
9  0

Every mesh satisfies IPM.

4Comparing the Two Solutions

The Same Inversion

Given the same interpolant, the two solvers perform the same inversions on the same grid. The archive tests this on its small accuracy grid, where solver_equivalence_rows solves the model both ways under HARK’s curvilinear interpolant and measures the gap between the two sets of policies at off-mesh points; health_solver_equivalence.csv stores the result. Rerunning the check reproduces the file.

Reading     This run   Published
----------  ---------  ---------
c abs_mean  1.228e-14  1.289e-14
c abs_max   2.611e-13  2.931e-13
n abs_mean  5.235e-17  5.757e-17
n abs_max   1.221e-15  8.882e-16

The largest absolute gap in consumption is below 10-12, rounding error.

Because the solvers agree, any difference between two solutions computed at the same grid comes from the interpolant alone.

Three Interpolants

The archive’s second check holds the simultaneous solver fixed and runs each interpolant inside its own backward solve: White’s curvilinear method, ENGINE and Delaunay triangulation, which Ludwig and Schoen (2018) introduced on the version of this model whose only risk is survival. On a uniform 50×40 grid of off-mesh points spanning m,h∈[0.5,25], it measures each method’s relative policy difference from the curvilinear solution, stored in health_interp_policy_diff.csv. Rerun here:

Reading              This run   Published
-------------------  ---------  ---------
engine c rel_mean    5.754e-05  5.754e-05
engine c rel_max     7.161e-03  7.161e-03
engine c n_valid     2000       2000
engine n rel_mean    1.783e-03  1.783e-03
engine n rel_max     4.782e-02  4.782e-02
engine n n_valid     2000       2000
delaunay c rel_mean  3.530e-04  3.530e-04
delaunay c rel_max   1.461e-01  1.461e-01
delaunay c n_valid   2000       2000
delaunay n rel_mean  4.920e-03  4.920e-03
delaunay n rel_max   3.513e-01  3.513e-01
delaunay n n_valid   2000       2000

The paper’s error map draws the consumption difference over the query plane:

<Figure size 1050x465 with 3 Axes>

ENGINE stays close to the curvilinear solution over most of the plane. Its largest differences lie in a few spots at low market resources, all on the dashed mesh line of zero assets, where the liquidity constraint stops binding and the policies kink. The triangulation’s differences are larger and cover the whole plane, with its maximum at the lowest market resources.

Solve Times

Timings depend on the machine, so this page reads the published measurements from health_model_summary.csv instead of timing the solves during the build. Each cell is the mean of ten timed solves.

Configuration           Simultaneous + curvilinear  EGM^n + ENGINE
----------------------  --------------------------  --------------
2 cycles, 44x50 grid    0.037                       0.013 (2.8x)
5 cycles, 44x50 grid    0.131                       0.033 (4.0x)
10 cycles, 44x50 grid   0.296                       0.071 (4.2x)
15 cycles, 44x50 grid   0.460                       0.103 (4.4x)
20x15 grid, 10 cycles   0.044                       0.014 (3.1x)
44x50 grid, 10 cycles   0.296                       0.071 (4.2x)
88x50 grid, 10 cycles   0.559                       0.126 (4.4x)
132x50 grid, 10 cycles  0.860                       0.191 (4.5x)
176x50 grid, 10 cycles  1.162                       0.247 (4.7x)

Times are in seconds. The ratio in parentheses is the curvilinear baseline’s time over that of EGMn with ENGINE, which solves the model 2.8 to 4.7 times faster across both sweeps.