Skip to content

Allow for Gauss-Hermite points; add log-normal shocks.#258

Merged
hmgaudecker merged 12 commits intomainfrom
gauss_hermite-and-log_normal
Mar 2, 2026
Merged

Allow for Gauss-Hermite points; add log-normal shocks.#258
hmgaudecker merged 12 commits intomainfrom
gauss_hermite-and-log_normal

Conversation

@hmgaudecker
Copy link
Member

Fixes #248.

API change: When requesting Normal / Tauchen / log-Normal, users now need to explicitly set a Boolean flag gauss_hermite.

Cleanup of prior PRs: Make example models run again; these had not included age among the initial states yet.

hmgaudecker and others added 3 commits February 26, 2026 08:09
The initial conditions validation (added in #257) now requires 'age'
in initial_states. Update both example models accordingly, and fix the
ty command in CLAUDE.md to include the correct environment flag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@hmgaudecker hmgaudecker requested a review from timmens February 26, 2026 08:01
@read-the-docs-community
Copy link

read-the-docs-community bot commented Feb 26, 2026

Documentation build overview

📚 pylcm | 🛠️ Build #31617283 | 📁 Comparing 5b13351 against latest (d00662f)


🔍 Preview build

Show files changed (17 files in total): 📝 17 modified | ➕ 0 added | ➖ 0 deleted
File Status
index.html 📝 modified
consumption-saving/index.html 📝 modified
defining-models/index.html 📝 modified
function-representation/index.html 📝 modified
grids/index.html 📝 modified
index-1/index.html 📝 modified
index-2/index.html 📝 modified
index-3/index.html 📝 modified
index-4/index.html 📝 modified
index-5/index.html 📝 modified
installation/index.html 📝 modified
interpolation/index.html 📝 modified
parameters/index.html 📝 modified
params-workflow/index.html 📝 modified
shocks/index.html 📝 modified
solving-and-simulating/index.html 📝 modified
tiny-example/index.html 📝 modified

Copy link
Member Author

@hmgaudecker hmgaudecker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Autoreview

@hmgaudecker hmgaudecker requested a review from mj023 February 26, 2026 08:42
rho, sigma, mu = kwargs["rho"], kwargs["sigma"], kwargs["mu"]
if self.gauss_hermite:
std_y = jnp.sqrt(sigma**2 / (1 - rho**2))
nodes, _weights = _gauss_hermite_normal(n_points, 0.0, std_y)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about the math on this. If understand this right, we want to follow what is in this note called Tauchen and Hussey (91). There they use sigma instead of std_y.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jeez, thanks. Making the same error twice really hurts...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, since Claude kept changing it back, I cared to ask and have been convinced:

● The new version fixes two problems with the original.

Problem 1: Wrong grid placement. The original places nodes on N(0, sigma) — the innovation distribution. But the stationary distribution of an AR(1) is N(0,
sigma / sqrt(1 - rho^2)). When rho is near 1, the unconditional std is much larger than sigma, so the old grid misses the tails entirely. The new code fixes
this:

std_y = jnp.sqrt(sigma2 / (1 - rho2))
nodes, _weights = _gauss_hermite_normal(n_points, 0.0, std_y)

Problem 2: Importance-sampling reweighting is fragile. The original computes weights * f_cond / g_prop — an importance-weight ratio. When rho is large and a
source node y is far from 0, the conditional density f_cond (centered at rho * y) and the proposal g_prop (centered at 0) barely overlap, so the ratio blows up
or underflows. The renormalization raw / raw.sum() papers over this but the result is numerically poor.

The new version avoids this entirely by using CDF binning (Tauchen-style): for each source state y_i, it computes P(y' falls in bin j | y_i) by evaluating the
conditional CDF at the midpoints between nodes. This is exact for the bin probabilities and numerically stable regardless of rho.

So: more code, but correct grid coverage and stable numerics. The original was essentially broken for high persistence.

❯ fair enough, but is this still the tauchen method? what would be the correct reference?

● It's a hybrid that doesn't cleanly fit either standard reference:

  • Tauchen (1986): evenly-spaced nodes + CDF binning for transitions
  • Tauchen & Hussey (1991): Gauss-Hermite nodes + importance-weight reweighting

The original code was textbook Tauchen-Hussey. The new code takes GH nodes from Tauchen-Hussey but replaces the importance weighting with CDF binning from
Tauchen. So it's neither method exactly — it combines the better node placement of Tauchen-Hussey with the more robust transition computation of Tauchen.

If you want a single reference for why this combination makes sense, Floden (2008) "A Note on the Accuracy of Markov-Chain Approximations to Highly Persistent
AR(1) Processes" documents the problems with both methods at high rho and proposes optimizing over the weighting function in Tauchen-Hussey. Kopecky & Suen
(2010) "Finite State Markov-chain Approximations to Highly Persistent Processes" explicitly advocate non-uniform node placement with Tauchen-style binning, which
is essentially what this code does.

I'd call it "Gauss-Hermite nodes with Tauchen binning" and cite Kopecky & Suen (2010) as the closest methodological reference, since they make the case for
exactly this kind of hybrid.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AI definitely seems to have read more papers than me. I checked the references and the $\sigma_y$ is from Floden 2008, and it sounds convincing, even though Floden 2008 finds an even better value for $\sigma$. For the Tauchen-Binning stuff I'm not so sure, I think it's not in the papers being cited and also it just seems suspect to me to go the distance to get the hermitian points and then just not use the associated weights at all. But in the end the Rouwenhorst method seems to be the most accurate anyways.

Copy link
Member

@timmens timmens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice! I have two general comments (do not need to be addressed here):

  1. Are we sure that we want to stick to the "math" notation for the shocks, i.e., using mu and sigma instead of mean and std? I think I am slightly in favor of the later.

  2. Thinking about the QuantEcon comment: Should add a test-suite to compare PyLCM against existing libraries?

Copy link
Member

@timmens timmens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, and one more comment: I find it a bit hard to completely judge the mathematical correctness here, as I haven't worked a lot with quadrature and Markov-chains. To make the review a bit easier (also for the future) we could add a page to the docs where the shock math is explained. On this level we can also then ask our friends with a background in numerical mathematics to check it.

hmgaudecker and others added 3 commits March 2, 2026 08:54
(will be gone, anyhow, though)

Co-authored-by: Tim Mensinger <mensingertim@gmail.com>
@hmgaudecker
Copy link
Member Author

hmgaudecker commented Mar 2, 2026

  1. Are we sure that we want to stick to the "math" notation for the shocks, i.e., using mu and sigma instead of mean and std? I think I am slightly in favor of the later.

I think I'd stick to this since it is so common notation (rho for sure is and once we open the door there...). But yeah, we could revisit in the future, no super-strong opinion.

  1. Thinking about the QuantEcon comment: Should add a test-suite to compare PyLCM against existing libraries?

Which ones did you have in mind?

Ah, and one more comment: I find it a bit hard to completely judge the mathematical correctness here, as I haven't worked a lot with quadrature and Markov-chains. To make the review a bit easier (also for the future) we could add a page to the docs where the shock math is explained. On this level we can also then ask our friends with a background in numerical mathematics to check it.

Good point, will do!

@hmgaudecker hmgaudecker merged commit b4ede68 into main Mar 2, 2026
9 checks passed
@hmgaudecker hmgaudecker deleted the gauss_hermite-and-log_normal branch March 2, 2026 08:17
@timmens
Copy link
Member

timmens commented Mar 2, 2026

Which ones did you have in mind?

Thinking mostly about Econ-Ark (HARK). If I am not mistaken they have a couple of papers replicated and also a couple of specific model families implemented. I would think that this would be nice argument for PyLCM if we can easily replicate all of this as well.

@hmgaudecker
Copy link
Member Author

Added to #173

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ENH: Add log-normal shocks and improve quadrature points

3 participants