Skip to article content

EGMⁿ: The Sequential Endogenous Grid Method

EGM to the power of n

Back to Article
Basic Health Investment Model (HARK Baseline)
Download Notebook

Basic Health Investment Model (HARK Baseline)

This notebook documents the BasicHealthConsumerType from HARK, a model in which agents make decisions about consumption, saving, and health investment. The model serves as the baseline against which we compare the EGMn^n + ENGINE approach.

# Import the basic health investment consumer class, as well as some basic tools
from __future__ import annotations

from time import time

import matplotlib.pyplot as plt
import numpy as np
from HARK.ConsumptionSaving.ConsHealthModel import BasicHealthConsumerType
from HARK.utilities import plot_funcs

Background on the Model

BasicHealthConsumerType is a teaching model rather than a structural estimation tool. It demonstrates principles for solving dynamic stochastic optimization problems with multiple endogenous states and controls.

The model was originally adapted from the one in Ludwig and Schoen (Comp Econ 2018), for use by White (JEDC 2015). The version in White adds income and depreciation risk to the original Ludwig and Schoen model, and the model here very slightly adjusts how mortality works.

Ludwig & Schoen demonstrated that Delaunay triangulation can interpolate irregular endogenous gridpoints. White (2015) presented a specialized interpolation method exploiting known relationships among grid points. HARK’s default solver uses White’s method via Curvilinear2DInterp.

Model Statement

Each period tt, the agent experiences wage rate ωt\omega_t and health depreciation rate δt\delta_t. The agent observes market resources mtm_t and health capital hth_t, then chooses consumption ctc_t and health investment ntn_t. Consumption yields utility with CRRA preferences, while health investment produces additional health capital. Health hth_t is valued for two reasons: it reduces the probability of death after each period, and it increases the agent’s productivity (hence income).

The model can be expressed in Bellman form as follows:

vt(mt,ht)=maxct,ntU(ct)+βStE[vt+1(mt+1,ht+1)]  s.t.Ht=ht+g(nt),at=mtctnt,Dt=ϕt/(1+Ht),St=1Dt,ht+1=(1δt+1)Ht,yt+1=ωt+1ht+1,mt+1=Rt+1at+yt+1,U(c)=c1ρ1ρ,g(n)=(γ/α)nα,(ωt+1,δt+1)Ft+1.\begin{align} v_t(m_t, h_t) &= \max_{c_t, n_t} U(c_t) + \beta S_t E [ v_{t+1}(m_{t+1}, h_{t+1}) ] ~~\text{s.t.} \\ H_t &= h_t + g(n_t), \\ a_t &= m_t - c_t - n_t, \\ D_t &= \phi_t / (1 + H_t), \\ S_t &= 1 - D_t, \\ h_{t+1} &= (1-\delta_{t+1}) H_t, \\ y_{t+1} &= \omega_{t+1} h_{t+1}, \\ m_{t+1} &= R_{t+1} a_t + y_{t+1}, \\ U(c) &= \frac{c^{1-\rho}}{1-\rho}, \\ g(n) &= (\gamma / \alpha) n^{\alpha}, \\ (\omega_{t+1}, \delta_{t+1}) &\sim F_{t+1}. \end{align}

The current solver requires ρ<1\rho < 1 (so that utility is strictly positive for all c>0c > 0) and Pr(ω=0)>0\Pr(\omega=0) > 0, so that zero income is always possible. Combined with 0<α<10 < \alpha < 1 (so that health production is increasing and concave), these conditions guarantee that the first order conditions are necessary and sufficient for the optimal policy, and that they hold with equality.

Characterizing the Solution

As is typical in HARK, each period of this model is solved using the endogenous grid method (EGM). The end-of-period states are post-investment health capital HtH_t and retained assets ata_t. Substituting the transition dynamics into the Bellman equation, the continuation value function is:

vt(at,Ht)β(1ϕt/(1+Ht))E[vt+1(Rt+1at+ωt+1(1δt+1)Ht,(1δt+1)Ht)]\mathfrak{v}_t(a_t,H_t) \equiv \beta \left(1 - \phi_t / (1 + H_t) \right) E \left[ v_{t+1}(R_{t+1} a_t + \omega_{t+1} (1-\delta_{t+1}) H_t, (1-\delta_{t+1}) H_t) \right].

The Bellman form of the problem can then be expressed as:

vt(mt,ht)=maxct,ntct1ρ1ρ+vt(at,Ht)  s.t.Ht=ht+(γ/α)ntα,at=mtctnt.\begin{align} v_t(m_t, h_t) &= \max_{c_t, n_t} \frac{c_t^{1-\rho}}{1-\rho} + \mathfrak{v}_t(a_t,H_t) ~~\text{s.t.} \\ H_t &= h_t + (\gamma / \alpha) n_t^{\alpha}, \\ a_t &= m_t - c_t - n_t. \end{align}

In this representation of the model, all of the intertemporal transitions (including risk) have been wrapped up into the “gothic” value function, with only intratemporal dynamics remaining.

The first order conditions with respect to ctc_t and ntn_t are thus:

ctρvta(at,Ht)=0,(γntα1)vtH(at,Ht)vta(at,Ht)=0.\begin{align} c_t^{-\rho} - \mathfrak{v}_t^a(a_t,H_t) &= 0, \\ (\gamma n_t^{\alpha-1}) \mathfrak{v}^H_t(a_t,H_t) - \mathfrak{v}_t^a(a_t,H_t) &= 0. \end{align}

Rearranging terms and solving for ctc_t and ntn_t yields:

ct=vta(at,Ht)1/ρ,nt=(vta(at,Ht)γvtH(at,Ht))1/(α1).\begin{align} c_t &= \mathfrak{v}_t^a(a_t,H_t)^{-1/\rho}, \\ n_t &= \left( \frac{\mathfrak{v}_t^a(a_t,H_t)}{\gamma \mathfrak{v}^H_t(a_t,H_t)} \right)^{1/(\alpha-1)}. \end{align}

Inverting the intraperiod dynamics, the endogenous gridpoints associated with these optimal controls are:

mt=at+ct+nt,ht=Htg(nt).\begin{align} m_t &= a_t + c_t + n_t, \\ h_t &= H_t - g(n_t). \end{align}

To solve the model using the endogenous grid method, we must choose a grid of end-of-period states (at,Ht)(a_t, H_t), compute end-of-period marginal value for each (with respect to both dimensions), and then apply the solution above to generate combinations of decision-time states (mt,ht)(m_t, h_t) and controls (ct,nt)(c_t, n_t) that satisfy the first order conditions. The solution to the period tt problem is an interpolant over those points.

The marginal value of end-of-period assets vta()\mathfrak{v}_t^a(\cdot) is straightforward. The marginal value of post-investment health vtH()\mathfrak{v}_t^H(\cdot) has three terms: a marginal increase in HtH_t raises survival probability (because DtD_t decreases), raises income in t+1t+1 due to higher human capital, and raises health in t+1t+1 (with further continuation benefits). See the paper for the full derivation.

Inputs to the Solver

The solution to the terminal period TT is simple: the agent should consume all resources, ct=mtc_t = m_t and invest nothing in their health; the value function is simply vT(mT,hT)=U(mT)v_T(m_T,h_T) = U(m_T). Each non-terminal period of the basic health investment model is solved using the function solve_one_period_ConsBasicHealth. The following objects are passed to the solver for each non-terminal period (in addition to a representation of the successor period’s solution); default values are provided:

ParamDescriptionCodeValueConstructed
β\betaIntertemporal discount factorDiscFac\texttt{DiscFac}0.95
ρ\rhoCoefficient of relative risk aversionCRRA\texttt{CRRA}0.5
Rt+1R_{t+1}Risk free interest factorRfree\texttt{Rfree}1.03
α\alphaExponent on health production functionHealthProdExp\texttt{HealthProdExp}0.35
γ\gammaFactor on health production functionHealthProdFac\texttt{HealthProdFac}1.0
ϕt\phi_tMaximum death probability (if Ht=0H_t=0)DieProbMax\texttt{DieProbMax}0.5\surd
Ft+1F_{t+1}Joint dstn of wage and depreciation ratesShockDstn\texttt{ShockDstn}*\surd
A\mathcal{A}Grid of end-of-period assetsaLvlGrid\texttt{aLvlGrid}*\surd
H\mathcal{H}Grid of post-investment healthHLvlGrid\texttt{HLvlGrid}*\surd

Objects listed as “constructed” are not specified as raw parameters, but instead built by a constructor function.

By default, the distribution of wage rate ww is lognormal with a point mass for unemployment, specified with parameters WageRteMean, WageRteStd, WageRteCount, UnempPrb, and IncUnemp. The distribution of depreciation rate δ\delta is uniform by default, specified with parameters DeprRteMean, DeprRteSpread, and DeprRteCount. The joint distribution of shocks is assumed to be independent.

The grid of end-of-period assets A\mathcal{A} has a default specification using the same constructor function as other HARK models, with the same inputs as usualt (aXtraMin, etc). The grid of post-investment health H\mathcal{H} has a default specification of being uniformly distributed between hLvlMin and hLvlMax with hLvlCount nodes. Note the (lack of) capitalization, which is intentional.

Maximum death probability ϕt\phi_t could be manually set as an age-varying parameter, but by default it is constructed using “logistic polynomial” coefficients. The parameter DieProbMaxCoeffs is a list representing polynomial coefficients (p0,p1,p2,)(p_0, p_1, p_2, \cdots), and the maximum death probability at each model age tt is constructed as:

φt=kpktk,     ϕt=(1+exp(φt))1\varphi_t = \sum_{k} p_k t^k, ~~~~~ \phi_t = (1 + \exp(-\varphi_t))^{-1}.

This form ensures that DieProbMax is strictly between 0 and 1 for all t<Tt < T, and that an increase in φt\varphi_t also increases ϕt\phi_t. By default, DieProbMaxCoeffs = [0.0], corresponding to ϕt=0.5\phi_t = 0.5, as in the referenced papers.

1Example Model Solution

Let’s solve one non-terminal period of the model and then plot the resulting set of interpolating nodes.

# Make and solve an example agent type with one non-terminal period
OnePeriodExample = BasicHealthConsumerType(cycles=1)
OnePeriodExample.solve()

# Plot the interpolation nodes of its solution
X = OnePeriodExample.solution[0].x_values
Y = OnePeriodExample.solution[0].y_values
plt.plot(X.flatten(), Y.flatten(), ".k", ms=2)
plt.xlim(0.0, 100.0)
plt.ylim(0.0, 50.0)
plt.xlabel(r"Market resources $m_t$")
plt.ylabel(r"Health capital $h_t$")
plt.title(r"Endogenous gridpoints in period $T-1$")
plt.show()
<Figure size 640x480 with 1 Axes>

The solver represents the solution as a single interpolant with three function outputs. The endogenous gridpoints are stored in the x_values and y_values attributes.

To match what’s done in the referenced papers, let’s solve a total of 100 periods (99 non-terminal periods) and plot the gridpoints.

# Make an agent that lives for 100 periods and solve their problem; this will take a moment
BasicHealthExample = BasicHealthConsumerType(cycles=99)
t0 = time()
BasicHealthExample.solve()
t1 = time()
# Plot the interpolation nodes of its solution at t=0
X = BasicHealthExample.solution[0].x_values
Y = BasicHealthExample.solution[0].y_values
plt.plot(X.flatten(), Y.flatten(), ".k", ms=2)
plt.xlim(0.0, 100.0)
plt.ylim(0.0, 50.0)
plt.xlabel(r"Market resources $m_t$")
plt.ylabel(r"Health capital $h_t$")
plt.title(r"Endogenous gridpoints in period $t=0$")
plt.show()
<Figure size 640x480 with 1 Axes>

Now let’s plot some cross sections of its policy functions. In the cell below, the function output indexed by [1] is consumption and the output indexed by [2] is health investment.

# Define cross sections of the consumption and health investment functions
t = 0
hLvl = 20.0


def C(mLvl):
    return BasicHealthExample.solution[t](mLvl, hLvl * np.ones_like(mLvl))[1]


def N(mLvl):
    return BasicHealthExample.solution[t](mLvl, hLvl * np.ones_like(mLvl))[2]


# Now plot them in a simple figure
plt.xlabel(r"Market resources $m_t$")
plt.ylabel(r"Consumption $c_t$ and investment $n_t$")
plot_funcs([C, N], 0.0, 20.0)
<Figure size 640x480 with 1 Axes>