The Method of Moderation: Illustrative Notebook
Source
# Import Econ-ARK styling and display header
from style import (
HEADER_HTML_NOTEBOOK,
apply_ark_style,
apply_notebook_css,
)
# Apply Econ-ARK branding and styling
apply_ark_style()
apply_notebook_css()
# Official ECON-ARK Brand Colors Available (ONLY APPROVED):
# ARK_BLUE (#1f476b) - Primary brand blue
# ARK_LIGHTBLUE (#00aeef) - Accent light blue
# ARK_PINK (#ed217c) - Brand pink
# ARK_GREEN (#39b54a) - Brand green
# ARK_YELLOW (#fcb040) - Brand yellow
# ARK_GREY (#676470) - Brand grey
# Display Econ-ARK header (for Jupyter notebooks)
from IPython.display import HTML, display
display(HTML(HEADER_HTML_NOTEBOOK))
The Method of Moderation: Illustrative Notebook¶
Author: Alan Lujan, Johns Hopkins University
The Method of Moderation (MoM) solves the extrapolation problem that affects sparse grid implementations of the Endogenous Grid Method (EGM). When EGM extrapolates beyond its computed grid, it can predict negative precautionary saving, violating economic theory. MoM prevents this by using theoretically-grounded bounds and asymptotically linear transformations.
Key insight: Optimal consumption is always bounded between analytical “optimist” and “pessimist” solutions. MoM interpolates a transformed ratio that respects these bounds, ensuring economically sensible extrapolation.
from __future__ import annotations
import numpy as np
from moderation import (
IndShockEGMConsumerType,
IndShockMoMConsumerType,
)
from plotting import (
plot_chi_function,
plot_consumption_bounds,
plot_moderation_ratio,
plot_mom_mpc,
plot_precautionary_gaps,
plot_value_functions,
)
# Model setup: Consumer with income uncertainty
params = {
"CRRA": 2.0,
"DiscFac": 0.96,
"Rfree": [1.02],
"TranShkStd": [1.0],
"cycles": 1,
"LivPrb": [1.0],
"vFuncBool": True,
"CubicBool": True,
"PermGroFac": [1.0],
"PermShkStd": [0.0],
"TranShkCount": 7,
"UnempPrb": 0.0,
"BoroCnstArt": None,
}
# Dense grid for "truth" solution (high precision)
dense_grid = {"aXtraMin": 0.001, "aXtraMax": 40, "aXtraCount": 500, "aXtraNestFac": 3}
# Sparse grid for practical comparison (5 points only)
sparse_grid = {"aXtraMin": 0.001, "aXtraMax": 4, "aXtraCount": 5, "aXtraNestFac": -1}
# Solve three versions: Truth (dense EGM), Sparse EGM, Sparse MoM
IndShockTruth = IndShockEGMConsumerType(**(params | dense_grid))
IndShockTruth.solve()
IndShockTruthSol = IndShockTruth.solution[0]
# Unpack theoretical bounds (same for all methods)
TruthOpt = IndShockTruthSol.Optimist
TruthPes = IndShockTruthSol.Pessimist
TruthTight = IndShockTruthSol.TighterUpperBound
# Sparse EGM solution (standard approach)
IndShockEGMApprox = IndShockEGMConsumerType(**(params | sparse_grid))
IndShockEGMApprox.solve()
IndShockEGMApproxSol = IndShockEGMApprox.solution[0]
# Sparse MoM solution (same grid, different method)
IndShockMoMApprox = IndShockMoMConsumerType(**(params | sparse_grid))
IndShockMoMApprox.solve()
IndShockMoMApproxSol = IndShockMoMApprox.solution[0]
# Grid parameters for plotting
mNrmMax = IndShockMoMApproxSol.mNrmMin + IndShockMoMApprox.aXtraGrid.max()
Setup: Three solutions using identical economic parameters. The “truth” uses a dense 500-point grid, while EGM and MoM use a realistic 5-point sparse grid. The test: which method extrapolates better beyond the computed grid?
Consumption Function Analysis¶
Figure 1: The EGM Extrapolation Problem¶
The key test of any solution method is its extrapolation properties: how well does it predict behavior outside the computed grid? Economic theory tells us that the gap between optimist and realist consumption (the “precautionary saving gap”) should always be positive - consumers should always save more than the optimist due to income uncertainty.
EGM struggles with this extrapolation challenge, as demonstrated in the paper.
# Define the precautionary saving gap functions
def truth_gap(m):
"""True precautionary saving gap."""
return TruthOpt.cFunc(m) - IndShockTruthSol.cFunc(m)
def egm_approx_gap(m):
"""EGM approximation gap."""
return TruthOpt.cFunc(m) - IndShockEGMApproxSol.cFunc(m)
# Test extrapolation over a wide range including high wealth levels
# Extend from near borrowing constraint to high wealth to capture all grid points
m_grid_wide = np.linspace(IndShockEGMApproxSol.mNrmMin + 0.001, 30, 100)
# Figure 1: EGM Extrapolation Failure
# Grid points will be handled internally by plotting functions
plot_precautionary_gaps(
m_grid=m_grid_wide,
truth_gap=truth_gap(m_grid_wide),
approx_gap=egm_approx_gap(m_grid_wide),
legend="EGM Approximation",
title="Figure 1: EGM Extrapolation Failure",
subtitle="EGM Extrapolation Failure: Negative Precautionary Saving",
solution=IndShockEGMApproxSol,
grid_points=True,
)

Figure 2: Truth Bounded by Theory¶
Before showing how MoM solves the problem, let’s understand the theoretical foundation. The true optimal consumption function is always bounded between two extreme cases with known analytical solutions.
# Create wealth grid for evaluation and plotting (matches paper ranges)
m_grid = np.linspace(IndShockTruthSol.mNrmMin + 0.01, 10, 100)
# Evaluate consumption functions on the grid
c_truth = IndShockTruthSol.cFunc(m_grid) # True optimal consumption
c_opt = TruthOpt.cFunc(m_grid) # Optimist consumption
c_pes = TruthPes.cFunc(m_grid) # Pessimist consumption
# Figure 2: Truth Bounded by Theory
plot_consumption_bounds(
m_grid=m_grid,
c_main=c_truth,
c_opt=c_opt,
c_pes=c_pes,
title="Figure 2: Truth Bounded by Economic Theory",
subtitle="True Consumption Always Lies Between Theoretical Bounds",
legend="Truth",
grid_points=False, # No grid points for theoretical bounds
)

Figure 3: Method of Moderation Solution¶
The Method of Moderation leverages the theoretical bounds to achieve superior extrapolation. Instead of extrapolating consumption directly, MoM
interpolates a transformed ratio that respects the bounds.
# Define MoM gap function
def mom_approx_gap(m):
"""MoM approximation gap."""
return TruthOpt.cFunc(m) - IndShockMoMApproxSol.cFunc(m)
# Figure 3: Method of Moderation Success
# Grid points will be handled internally by plotting functions
plot_precautionary_gaps(
m_grid=m_grid_wide,
truth_gap=truth_gap(m_grid_wide),
approx_gap=mom_approx_gap(m_grid_wide),
legend="MoM Approximation",
title="Figure 3: Method of Moderation Solves Extrapolation",
subtitle="MoM Maintains Positive Precautionary Saving",
solution=IndShockMoMApproxSol,
grid_points=True,
)
# Optional: Example of plotting multiple methods on same figure
# plot_precautionary_gaps(
# m_grid=m_grid_wide,
# truth_gap=truth_gap(m_grid_wide),
# approx_gap=[egm_approx_gap(m_grid_wide), mom_approx_gap(m_grid_wide)],
# legend=["EGM Approximation", "MoM Approximation"],
# title="Figure 3: Method Comparison",
# subtitle="EGM vs MoM Extrapolation Performance",
# solution=IndShockMoMApproxSol, # Can use either solution for grid points
# grid_points=True,
# )

The Method of Moderation builds on EGM’s computational strengths while addressing the extrapolation challenge through economic theory. Following the paper’s algorithm, the key insight is that optimal consumption is always bounded between the optimist and pessimist consumption functions, both of which have known analytical solutions.
MoM Algorithm Details
Instead of extrapolating consumption directly (which can violate bounds), MoM follows these steps (notation matches the paper):
- Solve standard EGM to get realist consumption at gridpoints
- Transform to log excess resources for convenient interpolation domain
- Compute moderation ratio (paper Eq. (12))
- Apply transformation for asymptotic linearity
- Interpolate function with derivatives for smooth extrapolation
- Reconstruct consumption using
This approach ensures consumption always stays within economic bounds while achieving excellent extrapolation properties through the asymptotically linear transformation function, as derived in the paper.
Figure 4: MoM Consumption Function¶
Let’s examine the consumption functions themselves to understand how MoM
achieves superior extrapolation by staying within theoretical bounds.
# Create specific grid for Figure 4 with max point at 3.0
m_grid_fig4 = np.linspace(IndShockTruthSol.mNrmMin + 0.01, 3.0, 100)
# Evaluate consumption functions for comparison at Figure 4 grid points
c_mom_fig4 = IndShockMoMApproxSol.cFunc(m_grid_fig4) # MoM consumption
c_opt_fig4 = TruthOpt.cFunc(m_grid_fig4) # Optimist consumption
c_pes_fig4 = TruthPes.cFunc(m_grid_fig4) # Pessimist consumption
c_tight_fig4 = TruthTight.cFunc(m_grid_fig4) # Tight upper bound
# Figure 4: MoM Consumption Function
plot_consumption_bounds(
m_grid=m_grid_fig4,
c_main=c_mom_fig4,
c_opt=c_opt_fig4,
c_pes=c_pes_fig4,
title="Figure 4: MoM Consumption Function",
subtitle="MoM Consumption Respects Theoretical Bounds",
legend="MoM Approximation",
c_tight=c_tight_fig4, # Automatically shown since c_tight is provided
solution=IndShockMoMApproxSol,
)

Bound Preservation: MoM consumption stays within theoretical bounds and below tight bound.
Figure 5: Direct Method Comparison¶
This decisive comparison shows why the Method of Moderation is superior to standard EGM for sparse grid problems. All three methods use the same sparse input data, but only MoM maintains economically sensible extrapolation behavior.
# Use the same wide grid for consistent comparison
m_grid_comparison = m_grid_wide
# Combined comparison: Truth, EGM failure, and MoM success
plot_precautionary_gaps(
m_grid=m_grid_comparison,
truth_gap=truth_gap(m_grid_comparison),
approx_gap=[egm_approx_gap(m_grid_comparison), mom_approx_gap(m_grid_comparison)],
legend=["EGM Approximation", "MoM Approximation"],
title="Figure 5: Direct Method Comparison",
subtitle="EGM vs MoM Extrapolation Performance",
solution=IndShockMoMApproxSol, # Use MoM solution for grid points
grid_points=True,
)

Method of Moderation Framework¶
Figure 6: Moderation Ratio Function ¶
The moderation ratio quantifies how the realist consumer balances between optimist and pessimist behaviors at different wealth levels (see (12)). It ranges from 0 to 1, where:
- : Realist behaves like optimist (perfect foresight, high wealth)
- : Realist behaves like pessimist (worst-case expectations, low wealth)
- : Balanced moderation based on uncertainty
The moderation ratio follows the formula: , providing the foundation for the Method of Moderation’s superior extrapolation properties.
# Create grid mirroring paper's wide evaluation range
m_grid_fig5 = np.linspace(IndShockMoMApproxSol.mNrmMin + 0.01, 50, 200)
# Access the moderation functions from the MoM solution
transformed_func = IndShockMoMApproxSol.cFunc # TransformedFunctionMoM
modRteFunc = transformed_func.modRteFunc # $\modRte(\logmNrmEx)$ function
logitModRteFunc = transformed_func.logitModRteFunc # transformation function
# Convert market resources to $\logmNrmEx$ ("mu" in the paper: $\logmNrmEx = \log(\mNrm-\mNrmMin)$)
from moderation import expit_moderate, log_mnrm_ex
m_min = transformed_func.mNrmMin
mu_grid_fig5 = log_mnrm_ex(m_grid_fig5, m_min)
# Evaluate moderation ratio $\modRte(\logmNrmEx)$ via transformation and inverse
chi_values_fig5 = logitModRteFunc(mu_grid_fig5)
omega_values_fig5 = expit_moderate(chi_values_fig5)
# Figure 6: Moderation Ratio Function (paper Fig. 6 counterpart)
plot_moderation_ratio(
m_grid=m_grid_fig5,
omega_values=omega_values_fig5,
title=r"Figure 6: Consumption Moderation Ratio $\omega(m)$",
subtitle="Wealth-Dependent Moderation Between Bounds",
solution=IndShockMoMApproxSol,
grid_type="consumption",
)

Figure 7: The Logit Transformation¶
The logit transformation enables asymptotically linear interpolation in unbounded space .
# Create market resources grid from near constraint to high wealth to show full behavior
m_grid_wide = np.linspace(m_min + 0.001, 50, 200)
# Convert to mu grid for x-axis (this is the natural domain for logit function)
mu_grid_chi = log_mnrm_ex(m_grid_wide, m_min)
# Evaluate logit function over this extended range
chi_values_fig6 = logitModRteFunc(mu_grid_chi)
# Figure 7: Logit Transformation Function plotted over mu (log excess resources)
# X-axis will be mu = log(m - mNrmMin), Y-axis will be logit(omega)
plot_chi_function(
mu_grid=mu_grid_chi, # mu values for x-axis (log excess market resources)
chi_values=chi_values_fig6,
title="Figure 7: Logit Transformation for Stable Extrapolation",
subtitle="Unbounded Transformation for Stable Extrapolation",
solution=IndShockMoMApproxSol,
grid_points=True,
)

Function Properties and Bounds¶
Figure 8: MoM MPC Bounded by Theory¶
The marginal propensity to consume () tells us how much additional consumption results from an additional dollar of market resources. This is crucial for understanding:
- Monetary policy transmission: How interest rate changes affect spending
- Fiscal policy effectiveness: How tax rebates stimulate consumption
- Wealth effects: How asset price changes impact the economy
Like consumption, the MPC must also respect theoretical bounds between and , as shown in the paper's extensions.
# Calculate MPCs - now using the implemented MoM MPC derivative
mpc_mom = IndShockMoMApproxSol.cFunc.derivative(m_grid) # MoM MPC (varies with wealth)
mpc_opt_const = IndShockTruthSol.MPCmin # Optimist MPC (constant)
mpc_tight_const = IndShockTruthSol.MPCmax # Tight bound MPC (constant)
# Create constant arrays for plotting
mpc_opt_vals = np.full_like(m_grid, mpc_opt_const)
mpc_tight_vals = np.full_like(m_grid, mpc_tight_const)
# Figure 8: MoM MPC Bounds
plot_mom_mpc(
m_grid=m_grid,
mpc_values=mpc_mom,
mpc_opt_vals=mpc_opt_vals,
mpc_tight_vals=mpc_tight_vals,
title="Figure 8: MoM MPC Bounded by Theory",
subtitle="MoM MPC Stays Within Theoretical Bounds",
mpc_label="MoM MPC",
solution=IndShockMoMApproxSol,
grid_points=True, # Explicitly enable grid points
)

Figure 9: Value Functions Bounded by Theory¶
The value function represents the consumer’s expected lifetime utility as a function of current market resources. This figure shows the theoretical bounds (optimist and pessimist) along with the EGM sparse grid approximation, demonstrating how well the 5-point grid captures value function behavior between the analytical limits.
# Create wealth grid specifically for value functions (starting from mNrmMin + 0.001)
m_grid_vfunc = np.linspace(IndShockEGMApproxSol.mNrmMin + 0.001, 3.0, 100)
# Evaluate value functions on the grid
v_truth = IndShockTruthSol.vFunc(m_grid_vfunc) # Truth
v_opt = TruthOpt.vFunc(m_grid_vfunc) # Optimist
v_pes = TruthPes.vFunc(m_grid_vfunc) # Pessimist
v_tight = TruthTight.vFunc(m_grid_vfunc) # Tight upper bound
v_egm_sparse = IndShockEGMApproxSol.vFunc(
m_grid_vfunc,
) # EGM Approximation
v_mom_sparse = IndShockMoMApproxSol.vFunc(m_grid_vfunc) # MoM Approximation
# Figure 9: Value Functions (truth, optimist, pessimist, tight, EGM, MoM)
plot_value_functions(
m_grid=m_grid_vfunc,
title="Figure 9: Value Functions Bounded by Economic Theory",
subtitle="Value Function: True Solution vs Sparse Approximations",
v_truth=v_truth,
v_opt=v_opt,
v_pes=v_pes,
v_tight=v_tight,
v_egm_sparse=v_egm_sparse,
v_mom_sparse=v_mom_sparse,
figure_num=7,
mom_solution=IndShockMoMApproxSol,
egm_solution=None,
)

Figure 10: Inverse Value Functions ¶
The inverse value function represents the consumption equivalent of the value function - the constant consumption level that would provide the same lifetime utility as the value function at each wealth level. This transformation is crucial for HARK’s numerical stability and provides intuitive economic interpretation.
# Evaluate inverse value functions on the same grid as Figure 8
vNvrs_truth = IndShockTruthSol.vFunc.vFuncNvrs(m_grid_vfunc) # Truth inverse value
vNvrs_opt = TruthOpt.vFunc.vFuncNvrs(m_grid_vfunc) # Optimist inverse value
vNvrs_pes = TruthPes.vFunc.vFuncNvrs(m_grid_vfunc) # Pessimist inverse value
vNvrs_egm_sparse = IndShockEGMApproxSol.vFunc.vFuncNvrs(
m_grid_vfunc,
) # EGM inverse value
vNvrs_mom_sparse = IndShockMoMApproxSol.vFunc.vFuncNvrs(
m_grid_vfunc,
) # MoM inverse value
# Figure 10: Inverse Value Functions
plot_value_functions(
m_grid=m_grid_vfunc,
title="Figure 10: Inverse Value Functions",
subtitle="Inverse Value Function: Consumption-Equivalent Utility",
v_truth=vNvrs_truth,
v_opt=vNvrs_opt,
v_pes=vNvrs_pes,
v_tight=None,
v_egm_sparse=vNvrs_egm_sparse,
v_mom_sparse=vNvrs_mom_sparse,
figure_num=8,
mom_solution=IndShockMoMApproxSol,
egm_solution=IndShockEGMApproxSol,
)

Figure 11: Value Function Moderation Ratio¶
The Method of Moderation applies generally to any bounded function. For inverse value functions, the moderation principle works exactly the same as for consumption functions.
The value function moderation ratio is simply: , where higher indicates the realist inverse value is closer to the pessimist bound (reflecting higher uncertainty effects).
# Access the value function moderation functions from the MoM solution
# Extract value function moderation functions
vfunc_transformed = (
IndShockMoMApproxSol.vFunc.vFuncNvrs
) # TransformedFunctionMoM for value function
vfunc_modRteFunc = (
vfunc_transformed.modRteFunc
) # $\valModRte(\logmNrmEx)$ function for value function
vfunc_logitModRteFunc = (
vfunc_transformed.logitModRteFunc
) # $\logitValModRte(\logmNrmEx)$ function for value function
# Create specific grid for Figure 10 with xlim at 10
m_grid_vfunc_mod = np.linspace(IndShockMoMApproxSol.mNrmMin + 0.01, 10, 200)
# Convert to $\logmNrmEx$ for evaluation
mu_grid_vfunc = log_mnrm_ex(m_grid_vfunc_mod, m_min)
# Evaluate value function moderation ratio $\valModRte(\logmNrmEx)$ by using chi function and inverse transformation
vfunc_chi_values = vfunc_logitModRteFunc(mu_grid_vfunc)
vfunc_omega_values = expit_moderate(vfunc_chi_values)
# Figure 11: Value Function Moderation Ratio
plot_moderation_ratio(
m_grid=m_grid_vfunc_mod,
omega_values=vfunc_omega_values,
title=r"Figure 11: Value Function Moderation Ratio $\Omega(m)$",
subtitle="Value Function Moderation Between Bounds",
solution=IndShockMoMApproxSol,
grid_type="value",
)

Summary: Why the Method of Moderation Matters¶
The figures demonstrate MoM’s comprehensive solution to the sparse grid extrapolation problem:
Core Innovation: MoM transforms the bounded moderation ratio into an unbounded logit function that becomes asymptotically linear, enabling stable extrapolation while respecting economic bounds .
Key Advantages: MoM builds on EGM’s efficiency while preventing extrapolation failures through theoretically-grounded bounds, ensuring reliable behavior for policy analysis and large-scale simulations.
For complete theoretical development see The Method of Moderation.