You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+5Lines changed: 5 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
5
5
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
8
+
## [0.16.1] - 2026-06-04 12:00:00
9
+
10
+
### Added
11
+
12
+
- Adds a `use_sparse_FOC_jac``Specifications` parameter (default `True`) that accelerates the time path iteration (TPI) household solve. With it on, `scipy.optimize.root` is given a sparse (banded) finite-difference Jacobian for the stacked household Euler and labor first order conditions: the sparsity pattern is auto-detected once per problem size and the solver then needs far fewer function evaluations per Jacobian build (about 20x fewer on the default S=80 cohort solve), with an automatic fallback to dense finite differences if the Jacobian is not sparse enough to benefit or if a solve fails. The result matches the legacy dense-finite-difference solution to within the model's resource-constraint accuracy floor on every calibration tested (OG-Core standard example, OG-ETH, OG-ZAF, OG-PHL, OG-IDN), giving roughly a 1.9-2.4x TPI speedup at no accuracy cost. Set `use_sparse_FOC_jac=False` to recover bit-identical agreement with v0.16.0 and earlier.
Copy file name to clipboardExpand all lines: docs/book/content/theory/derivations.md
+13Lines changed: 13 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -114,3 +114,16 @@ In the Cobb-Douglas unit elasticity case ($\varepsilon=1$) of the CES production
114
114
```
115
115
116
116
Again, even if this simple case, we cannot solve for $r$ as a function of $w$ for the reasons above.
117
+
118
+
119
+
(SecAppDerivHHjac)=
120
+
## Sparsity of the household equation Jacobian
121
+
122
+
Holding fixed the prices and policies a type-$j$ cohort faces, its $2S$ stationarized necessary conditions {eq}`EqStnrz_eul_n`, {eq}`EqStnrz_eul_b`, and {eq}`EqStnrz_eul_bS` in the $2S$ unknowns $\{n_{j,s},\hat b_{j,s+1}\}_{s=E+1}^{E+S}$ have a banded Jacobian. From the budget constraint {eq}`EqStnrzHHBC`, stationarized consumption at age $s$ depends on only three unknowns,
where $\widehat{tax}_{j,s}$ depends only on $(\hat b_{j,s}, n_{j,s})$ through labor and capital income (already in the active set, so it adds no further coupling), and $X_{j,s}$ collects terms fixed in the inner solve (bequests $\hat{bq}_{j,s}$, remittances $\hat{rm}_{j,s}$, government transfers $\hat{tr}_{j,s}$, UBI $\hat{ubi}_{j,s}$, the pension benefit $\theta_j$, and the $\hat c_{min,i}$ terms). The labor Euler equation {eq}`EqStnrz_eul_n` at age $s$ therefore depends on $\{\hat b_{j,s},\hat b_{j,s+1},n_{j,s}\}$ alone, and the savings Euler equation {eq}`EqStnrz_eul_b`---which links $\hat c_{j,s}$ to $\hat b_{j,s+1}$ and $\hat c_{j,s+1}$---depends on $\{\hat b_{j,s},\hat b_{j,s+1},\hat b_{j,s+2},n_{j,s},n_{j,s+1}\}$. The marginal tax rates $\tau^{mtrx}_s$ and $\tau^{mtry}_{s+1}$ are functions of own-age income (already in these sets), so they add no further coupling, and the terminal condition {eq}`EqStnrz_eul_bS` is sparser still. Each of the $2S$ equations therefore depends on at most five of the $2S$ unknowns, regardless of $S$, so the Jacobian has at most $10S$ nonzero entries rather than the $(2S)^2 = 4S^2$ of a fully coupled system. This is the per-cohort counterpart to the dense $2JS$ system noted at the start of Chapter {ref}`Chap_Eqm`: cohorts couple only through prices, which are held fixed in the inner solve. A finite-difference Jacobian can then be built from a number of evaluations set by the bandwidth---about seven at $S = 80$---rather than $2S$, by probing together unknowns that affect no common equation (Figure {numref}`FigHHjacSparsity`).
Copy file name to clipboardExpand all lines: docs/book/content/theory/equilibrium.md
+9Lines changed: 9 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -25,6 +25,15 @@ In all of the specifications of `OG-Core`, we use a two-stage fixed point algori
25
25
26
26
Our approach is to choose the minimum number of macroeconomic variables in an outer loop in order to be able to solve the household's $2JS$ Euler equations in terms of only the $\bar{n}_{j,s}$ and $\bar{b}_{j,s+1}$ variables directly, holding all other variables constant. The household system of Euler equations has a provable root solution and is orders of magnitude more tractable (less nonlinear) to solve holding these outer loop variables constant.
27
27
28
+
Moreover, with the outer-loop variables held fixed, each cohort's system of $2S$ Euler equations is not only less nonlinear but structurally sparse: every equation involves at most five of the $2S$ unknowns---a household's own age and its immediate neighbors. The root finder normally probes each unknown separately when building each step ($2S = 160$ evaluations of the system when $S = 80$), but with most equations depending on only a handful of unknowns, those affecting no common equation can be probed together, cutting the count to about seven at $S = 80$---a number set by how many neighbors couple, not by $S$. The parameter `use_sparse_FOC_jac` (default `True`) controls this; set it to `False` to use the legacy dense-finite-difference Jacobian on every call. The structure is derived in Appendix {ref}`SecAppDerivHHjac`.
29
+
30
+
```{figure} ./images/HH_jac_sparsity.png
31
+
---
32
+
name: FigHHjacSparsity
33
+
---
34
+
Sparsity pattern of the household equation Jacobian, at $S = 12$. Left: the standard finite-difference solve treats every entry of the $2S\times 2S$ matrix as live ($(2S)^2 = 576$ entries). Right: the actual structure---each Euler equation depends only on a household's own age and its immediate neighbors, leaving most entries zero (92 of 576 here; 636 of 25{,}600 at the default $S = 80$).
35
+
```
36
+
28
37
The steady-state solution method for each of the cases above is associated with a solution method that has a subset of the following outer-loop variables $\Bigl\{\bar{r}_p, \bar{r}, \bar{w}, \{\bar{p}_m\}_{m=1}^{M-1}, \bar{Y}, \overline{TR}, \overline{BQ}, factor\Bigr\}$.
Copy file name to clipboardExpand all lines: ogcore/default_parameters.json
+17Lines changed: 17 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -4392,6 +4392,23 @@
4392
4392
"choice": {"choices": [true, false]}
4393
4393
}
4394
4394
},
4395
+
"use_sparse_FOC_jac": {
4396
+
"title": "Flag to use a sparse (banded) finite-difference Jacobian in the household first order condition root finder",
4397
+
"description": "Flag to use a sparse (banded) finite-difference Jacobian in the household first order condition root finder. When True (the default), the sparsity pattern of the stacked Euler/labor first order conditions is auto-detected once per problem size and supplied to scipy.optimize.root, which then needs far fewer function evaluations per Jacobian build than the default dense finite differences. The solver falls back to the dense finite-difference Jacobian automatically if the Jacobian is not sparse enough to benefit or if a solve fails. Set to False to use the legacy dense Jacobian on every call.",
4398
+
"short_description": "Use a banded finite-difference Jacobian in the household solver",
0 commit comments