Skip to content

Commit 6d63621

Browse files
committed
Refactor and update documentation, code, and configs
This commit refines documentation and code comments for clarity, updates author affiliations in README, improves formatting in markdown and YAML files, and adjusts code for better style and maintainability. It also updates the HARK dependency in requirements.txt, adds new flake8 ignore rules in pyproject.toml, and removes unnecessary blank lines from several files. Minor code refactoring in moderation.py ensures fallback logic is always executed, and notebook files are cleaned up for consistency.
1 parent a3dd249 commit 6d63621

File tree

20 files changed

+72
-73
lines changed

20 files changed

+72
-73
lines changed

.github/workflows/binder.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ jobs:
55
Create-MyBinderOrg-Cache:
66
runs-on: ubuntu-latest
77
steps:
8-
- name: cache binder build on mybinder.org
9-
uses: jupyterhub/repo2docker-action@master
10-
with:
11-
NO_PUSH: true
12-
MYBINDERORG_TAG: ${{ github.event.ref }} # This builds the container on mybinder.org with the branch that was pushed on.
8+
- name: cache binder build on mybinder.org
9+
uses: jupyterhub/repo2docker-action@master
10+
with:
11+
NO_PUSH: true
12+
MYBINDERORG_TAG: ${{ github.event.ref }} # This builds the container on mybinder.org with the branch that was pushed on.

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Private directory
1+
# Private directory
22
.private/
33

44
# GitHub Copilot instructions (local configuration)

CITATION.cff

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,16 @@ contact:
3333
orcid: 0000-0002-5289-7054
3434
email: alujan@jhu.edu
3535
abstract: >
36-
In a risky world, a pessimist assumes the worst will happen. Someone who
37-
ignores risk altogether is an optimist. Consumption decisions are
38-
mathematically simple for both the pessimist and the optimist because both
39-
behave as if they live in a riskless world. A realist (someone who wants
40-
to respond optimally to risk) faces a much more difficult problem, but
41-
(under standard conditions) will choose a level of spending somewhere
42-
between pessimist's and the optimist's. We use this fact to redefine the
43-
space in which the realist searches for optimal consumption rules. The
44-
resulting solution accurately represents the numerical consumption rule
45-
over the entire interval of feasible wealth values with remarkably few
36+
In a risky world, a pessimist assumes the worst will happen. Someone who
37+
ignores risk altogether is an optimist. Consumption decisions are
38+
mathematically simple for both the pessimist and the optimist because both
39+
behave as if they live in a riskless world. A realist (someone who wants
40+
to respond optimally to risk) faces a much more difficult problem, but
41+
(under standard conditions) will choose a level of spending somewhere
42+
between pessimist's and the optimist's. We use this fact to redefine the
43+
space in which the realist searches for optimal consumption rules. The
44+
resulting solution accurately represents the numerical consumption rule
45+
over the entire interval of feasible wealth values with remarkably few
4646
computations.
4747
keywords:
4848
- consumption

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ This project is structured as a [REMARK](https://github.com/econ-ark/REMARK) (Re
2626

2727
- **Christopher D. Carroll** - Johns Hopkins University ([ORCID: 0000-0003-3732-9312](https://orcid.org/0000-0003-3732-9312))
2828
- **Alan Lujan** - Johns Hopkins University ([ORCID: 0000-0002-5289-7054](https://orcid.org/0000-0002-5289-7054)) *Corresponding Author*
29-
- **Kiichi Tokuoka** - European Central Bank / International Monetary Fund
29+
- **Karsten Chipeniuk** - Reserve Bank of New Zealand
30+
- **Kiichi Tokuoka** - Japanese Ministry of Finance
3031
- **Weifeng Wu** - Fannie Mae
3132

3233
## Abstract
@@ -84,10 +85,10 @@ See [REMARK reproduction](#reproducibility) section above for full reproduction
8485
```bibtex
8586
@software{carroll2025moderation,
8687
title={The Method of Moderation},
87-
author={Carroll, Christopher D. and
88-
Lujan, Alan and
89-
Chipeniuk, Karsten and
90-
Tokuoka, Kiichi and
88+
author={Carroll, Christopher D. and
89+
Lujan, Alan and
90+
Chipeniuk, Karsten and
91+
Tokuoka, Kiichi and
9192
Wu, Weifeng},
9293
year={2025},
9394
url={https://github.com/econ-ark/method-of-moderation},

binder/environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ dependencies:
1010
- uv
1111
- pip
1212
- pip:
13-
- -e ../.
13+
- -e ../.

code/moderation.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,15 +1577,14 @@ def derivative(self, m):
15771577
# Paper eq:MPCModeration: MPC = (1-MPCmod) * MPCmin + MPCmod * MPCmax
15781578
# This is GUARANTEED to satisfy MPCmin <= MPC <= MPCmax
15791579
return (1 - MPCmod) * MPCmin + MPCmod * self.MPCmax
1580-
else:
1581-
# Fallback: use omega'_mu directly in the footnote formula
1582-
omega_prime_mu = (
1583-
self.modRteFunc.derivativeX(mu)
1584-
if hasattr(self.modRteFunc, "derivativeX")
1585-
else self.modRteFunc.derivative(mu)
1586-
)
1587-
# Paper footnote 659: MPC = MPCmin * (1 + (h_nrm_ex/m_ex) * omega'_mu)
1588-
return MPCmin * (1 + (h_nrm_ex / m_ex) * omega_prime_mu)
1580+
# Fallback: use omega'_mu directly in the footnote formula
1581+
omega_prime_mu = (
1582+
self.modRteFunc.derivativeX(mu)
1583+
if hasattr(self.modRteFunc, "derivativeX")
1584+
else self.modRteFunc.derivative(mu)
1585+
)
1586+
# Paper footnote 659: MPC = MPCmin * (1 + (h_nrm_ex/m_ex) * omega'_mu)
1587+
return MPCmin * (1 + (h_nrm_ex / m_ex) * omega_prime_mu)
15891588

15901589
# 4. General case: use full product rule for functions with different bound slopes
15911590
dmu_dm = 1.0 / m_ex

code/notebook.ipynb

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,6 @@
192192
"apply_notebook_css()\n",
193193
"\n",
194194
"\n",
195-
"\n",
196195
"# Display Econ-ARK header (for Jupyter notebooks)\n",
197196
"from IPython.display import HTML, display\n",
198197
"\n",
@@ -249,7 +248,6 @@
249248
"source": [
250249
"from __future__ import annotations\n",
251250
"\n",
252-
"import numpy as np\n",
253251
"from moderation import (\n",
254252
" IndShockEGMConsumerType,\n",
255253
" IndShockMoMConsumerType,\n",
@@ -352,7 +350,7 @@
352350
}
353351
],
354352
"source": [
355-
"#| label: fig:egm-extrapolation-problem\n",
353+
"# | label: fig:egm-extrapolation-problem\n",
356354
"\n",
357355
"# Figure 1: EGM Extrapolation Failure\n",
358356
"plot_precautionary_gaps(\n",
@@ -412,7 +410,7 @@
412410
}
413411
],
414412
"source": [
415-
"#| label: fig:truth-bounded-by-theory\n",
413+
"# | label: fig:truth-bounded-by-theory\n",
416414
"\n",
417415
"# Figure 2: Truth Bounded by Theory\n",
418416
"plot_consumption_bounds(\n",
@@ -464,7 +462,7 @@
464462
}
465463
],
466464
"source": [
467-
"#| label: fig:mom-solution\n",
465+
"# | label: fig:mom-solution\n",
468466
"\n",
469467
"# Figure 3: Method of Moderation Success\n",
470468
"plot_precautionary_gaps(\n",
@@ -541,7 +539,7 @@
541539
}
542540
],
543541
"source": [
544-
"#| label: fig:mom-consumption-function\n",
542+
"# | label: fig:mom-consumption-function\n",
545543
"\n",
546544
"# Figure 4: MoM Consumption Function\n",
547545
"plot_consumption_bounds(\n",
@@ -592,7 +590,7 @@
592590
}
593591
],
594592
"source": [
595-
"#| label: fig:direct-comparison\n",
593+
"# | label: fig:direct-comparison\n",
596594
"\n",
597595
"# Figure 5: Direct Method Comparison\n",
598596
"plot_precautionary_gaps(\n",
@@ -660,7 +658,7 @@
660658
}
661659
],
662660
"source": [
663-
"#| label: fig:moderation-ratio\n",
661+
"# | label: fig:moderation-ratio\n",
664662
"\n",
665663
"# Figure 6: Moderation Ratio Function\n",
666664
"plot_moderation_ratio(\n",
@@ -727,7 +725,7 @@
727725
}
728726
],
729727
"source": [
730-
"#| label: fig:logit-transformation\n",
728+
"# | label: fig:logit-transformation\n",
731729
"\n",
732730
"# Figure 7: Logit Transformation Function\n",
733731
"plot_logit_function(\n",
@@ -809,7 +807,7 @@
809807
}
810808
],
811809
"source": [
812-
"#| label: fig:mpc-bounds\n",
810+
"# | label: fig:mpc-bounds\n",
813811
"\n",
814812
"# Figure 8: MoM MPC Bounds\n",
815813
"plot_mom_mpc(\n",
@@ -868,7 +866,7 @@
868866
}
869867
],
870868
"source": [
871-
"#| label: fig:value-functions\n",
869+
"# | label: fig:value-functions\n",
872870
"\n",
873871
"# Figure 9: Value Functions\n",
874872
"plot_value_functions(\n",
@@ -936,7 +934,7 @@
936934
}
937935
],
938936
"source": [
939-
"#| label: fig:inverse-value-functions\n",
937+
"# | label: fig:inverse-value-functions\n",
940938
"\n",
941939
"# Figure 10: Inverse Value Functions\n",
942940
"plot_value_functions(\n",
@@ -1006,7 +1004,7 @@
10061004
}
10071005
],
10081006
"source": [
1009-
"#| label: fig:value-moderation-ratio\n",
1007+
"# | label: fig:value-moderation-ratio\n",
10101008
"\n",
10111009
"# Figure 11: Value Function Moderation Ratio\n",
10121010
"plot_moderation_ratio(\n",

code/notebook.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ from style import (
2929
apply_ark_style()
3030
apply_notebook_css()
3131

32+
3233
# Display Econ-ARK header (for Jupyter notebooks)
3334
from IPython.display import HTML, display
3435

@@ -37,7 +38,7 @@ display(HTML(HEADER_HTML_NOTEBOOK))
3738

3839
**Author:** <span style="color: var(--ark-lightblue); font-weight: bold;">Alan Lujan</span>, <span style="color: var(--ark-blue); font-weight: bold;">Johns Hopkins University</span>
3940

40-
This notebook provides a pedagogical introduction to the Method of Moderation (MoM), a novel technique for solving consumption-saving models with superior accuracy and stability. We begin by motivating the problem that MoM solves: the "extrapolation problem" inherent in sparse-grid implementations of the Endogenous Grid Method (EGM). We then build the theoretical foundations for MoM, demonstrating how it leverages analytical bounds to ensure economically sensible behavior across the entire state space.
41+
This notebook provides a pedagogical introduction to the Method of Moderation (MoM), a novel technique for solving consumption-saving models with superior accuracy and stability. We begin by motivating the problem that MoM solvesthe "extrapolation problem" inherent in sparse-grid implementations of the Endogenous Grid Method (EGM). We then build the theoretical foundations for MoM, demonstrating how it leverages analytical bounds to ensure economically sensible behavior across the entire state space.
4142

4243
## Model Foundations: The Friedman-Muth Income Process
4344

@@ -52,7 +53,7 @@ The model used in this notebook, drawn from {cite:t}`SolvingMicroDSOPs`, impleme
5253

5354
At its core, the Method of Moderation (MoM) is designed to solve a persistent challenge in computational economics: the **extrapolation problem**. This issue is particularly pronounced in sparse-grid implementations of the Endogenous Grid Method (EGM), a widely-used technique for solving dynamic stochastic optimization problems.
5455

55-
When EGM is used to solve a consumption-saving model, it computes the optimal consumption policy at a finite set of grid points. However, to simulate agent behavior or analyze policy implications, we often need to evaluate the consumption function at points that lie outside this pre-computed grid. Standard practice is to extrapolate from the grid, but this can lead to results that violate fundamental economic theory. Specifically, linear extrapolation can predict **negative precautionary saving**, which implies that consumers with greater income uncertainty would save *less* than those with no uncertainty. This is a direct contradiction of established economic principles {cite:p}`Leland1968,Sandmo1970,Kimball1990`.
56+
When EGM is used to solve a consumption-saving model, it computes the optimal consumption policy at a finite set of grid points. However, to simulate agent behavior or analyze policy implications, we often need to evaluate the consumption function at points that lie outside this pre-computed grid. Standard practice is to extrapolate from the grid, but this can lead to results that violate fundamental economic theory. Specifically, linear extrapolation can predict **negative precautionary saving**, which implies that consumers with greater income uncertainty would save *less* than those with no uncertaintya direct contradiction of established economic principles {cite:p}`Leland1968,Sandmo1970,Kimball1990`.
5657

5758
MoM addresses this problem by abandoning direct extrapolation of the consumption function. Instead, it operates in a transformed space defined by two analytical, theoretically-grounded bounds. This approach builds on a long literature on "buffer-stock" saving behavior {cite:p}`Carroll1997`, which has established theoretical properties of consumption functions under uncertainty {cite:p}`StachurskiToda2019JET,MST2020JET`. The two bounds are:
5859

@@ -66,7 +67,6 @@ The true, "realist" consumption function must lie between these two extremes. Mo
6667
```python
6768
from __future__ import annotations
6869

69-
import numpy as np
7070
from moderation import (
7171
IndShockEGMConsumerType,
7272
IndShockMoMConsumerType,
@@ -139,7 +139,7 @@ At the heart of any numerical solution to a consumption-saving problem lies the
139139
This gap, defined as the difference between the consumption of a perfect-foresight "optimist" and that of a "realist" facing uncertainty, should always be positive. The presence of uninsurable income risk should lead consumers to save more (and thus consume less) than they would in a certain world. However, as {ref}`demonstrated in the paper <benchmark-the-method-of-endogenous-gridpoints>` and illustrated in [](#fig:egm-extrapolation-problem) (which corresponds to {ref}`Figure 1 <fig:ExtrapProblem>` in the academic paper), standard EGM struggles with this constraint when extrapolating {cite:p}`carrollEGM`.
140140

141141
```python
142-
#| label: fig:egm-extrapolation-problem
142+
# | label: fig:egm-extrapolation-problem
143143

144144
# Figure 1: EGM Extrapolation Failure
145145
plot_precautionary_gaps(
@@ -169,7 +169,7 @@ Having established the extrapolation problem, we now turn to the theoretical fou
169169
This figure illustrates these theoretical bounds, demonstrating that the true, high-precision consumption function (our "Truth" solution) lies neatly between them. This provides the theoretical justification for the MoM's approach, as shown in [](#fig:truth-bounded-by-theory) (which corresponds to {ref}`Figure 2 <fig:IntExpFOCInvPesReaOptNeedHi>` in the academic paper).
170170

171171
```python
172-
#| label: fig:truth-bounded-by-theory
172+
# | label: fig:truth-bounded-by-theory
173173

174174
# Figure 2: Truth Bounded by Theory
175175
plot_consumption_bounds(
@@ -191,7 +191,7 @@ With the theoretical bounds established, we can now demonstrate how the Method o
191191
Because this ratio is interpolated in a transformed space that guarantees it will respect the bounds, the resulting consumption function also respects them. As this figure shows, the MoM approximation of the precautionary saving gap remains positive and closely tracks the true solution, even far beyond the computed grid. The success of this approach is clearly demonstrated in [](#fig:mom-solution) (which corresponds to {ref}`Figure 3 <fig:ExtrapProblemSolved>` in the academic paper).
192192

193193
```python
194-
#| label: fig:mom-solution
194+
# | label: fig:mom-solution
195195

196196
# Figure 3: Method of Moderation Success
197197
plot_precautionary_gaps(
@@ -238,7 +238,7 @@ Having shown that MoM produces a precautionary saving gap with the correct theor
238238
As expected, the MoM solution remains strictly between the optimist and pessimist bounds. The figure also introduces a **tighter upper bound**. This bound is derived from the maximum possible marginal propensity to consume ($\MPCmax$) as market resources approach the natural borrowing constraint. The existence of such a bound is a key result from the buffer-stock saving literature {cite:p}`Carroll2001MPCBound`, with explicit formulas for the limiting MPC derived in {cite:p}`MaToda2021SavingRateRich` and {cite:p}`CarrollToche2009`. The fact that the MoM solution respects this tighter bound as well, as shown in [](#fig:mom-consumption-function) (which corresponds to {ref}`Figure 4 <fig:IntExpFOCInvPesReaOptNeed45>` in the academic paper), demonstrates its robustness and theoretical consistency.
239239

240240
```python
241-
#| label: fig:mom-consumption-function
241+
# | label: fig:mom-consumption-function
242242

243243
# Figure 4: MoM Consumption Function
244244
plot_consumption_bounds(
@@ -259,7 +259,7 @@ We now arrive at the decisive comparison between the Endogenous Grid Method and
259259
All three solutions are generated from the same underlying economic parameters, and both the EGM and MoM approximations use the same 5-point sparse grid. The difference in their extrapolation behavior is therefore entirely attributable to the solution method itself. The results, presented in [](#fig:direct-comparison), clearly show the superiority of the Method of Moderation for problems where extrapolation is a concern.
260260

261261
```python
262-
#| label: fig:direct-comparison
262+
# | label: fig:direct-comparison
263263

264264
# Figure 5: Direct Method Comparison
265265
plot_precautionary_gaps(
@@ -297,7 +297,7 @@ This figure plots the moderation ratio as a function of market resources, reveal
297297
::::
298298

299299
```python
300-
#| label: fig:moderation-ratio
300+
# | label: fig:moderation-ratio
301301

302302
# Figure 6: Moderation Ratio Function
303303
plot_moderation_ratio(
@@ -334,7 +334,7 @@ where $\logmNrmEx = \log(\mNrm - \mNrmMin)$ is the log of "excess" market resour
334334
::::
335335

336336
```python
337-
#| label: fig:logit-transformation
337+
# | label: fig:logit-transformation
338338

339339
# Figure 7: Logit Transformation Function
340340
plot_logit_function(
@@ -373,7 +373,7 @@ This mathematical elegance translates directly into superior numerical propertie
373373

374374
### Figure 8: MoM MPC Bounded by Theory
375375

376-
Beyond the consumption function itself, its derivative (the **marginal propensity to consume**, or MPC) is of central economic importance. The MPC, $\partial c / \partial m$, measures the change in consumption for a one-unit change in market resources and is a key input for macroeconomic models and policy analysis. It is crucial for understanding:
376+
Beyond the consumption function itself, its derivativethe **marginal propensity to consume (MPC)**is of central economic importance. The MPC, $\partial c / \partial m$, measures the change in consumption for a one-unit change in market resources and is a key input for macroeconomic models and policy analysis. It is crucial for understanding:
377377

378378
* **Monetary policy transmission**: How interest rate changes affect spending
379379
* **Fiscal policy effectiveness**: How tax rebates stimulate consumption
@@ -386,7 +386,7 @@ In practice, bounded MPC estimates are crucial for policy analysis. If a DSGE mo
386386
```
387387

388388
```python
389-
#| label: fig:mpc-bounds
389+
# | label: fig:mpc-bounds
390390

391391
# Figure 8: MoM MPC Bounds
392392
plot_mom_mpc(
@@ -415,7 +415,7 @@ We now turn from the policy function, $c(m)$, to the **value function**, $v(m)$,
415415
This figure serves two purposes. First, it demonstrates that the value function, like the consumption function, is bounded by the optimist and pessimist solutions {cite:p}`Aiyagari1994,Huggett1993`. Second, it compares the value functions generated by the high-precision "truth" model, the sparse-grid EGM, and the sparse-grid MoM. This allows us to see how accurately each method approximates the true value function, as shown in [](#fig:value-functions).
416416

417417
```python
418-
#| label: fig:value-functions
418+
# | label: fig:value-functions
419419

420420
# Figure 9: Value Functions
421421
plot_value_functions(
@@ -453,7 +453,7 @@ For numerical stability and easier interpretation, it is often convenient to wor
453453
This transformation is particularly useful because, as this figure shows, it tends to be much more linear than the value function itself, especially near the borrowing constraint. This makes it better suited for numerical interpolation and is a key reason for its use in HARK. This figure compares the inverse value functions from our three solutions, again demonstrating the accuracy of the MoM approximation, as shown in [](#fig:inverse-value-functions).
454454

455455
```python
456-
#| label: fig:inverse-value-functions
456+
# | label: fig:inverse-value-functions
457457

458458
# Figure 10: Inverse Value Functions
459459
plot_value_functions(
@@ -493,7 +493,7 @@ $$
493493
This ratio measures how the realist's inverse value function relates to the theoretical bounds, with $\valModRte = 0$ at low wealth (pessimistic) and $\valModRte = 1$ at high wealth (optimistic). As the figure shows, the pattern is similar to that of the consumption moderation ratio, providing further evidence of the robustness of the Method of Moderation, as shown in [](#fig:value-moderation-ratio).
494494

495495
```python
496-
#| label: fig:value-moderation-ratio
496+
# | label: fig:value-moderation-ratio
497497

498498
# Figure 11: Value Function Moderation Ratio
499499
plot_moderation_ratio(

0 commit comments

Comments
 (0)