N-NB-EGM: nested endogenous-grid solver for two-margin durable-choice models - #403
Merged
Merged
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BNQkLG5QzXcgGdpgH6sN4u
The NEGM keeper/adjuster architecture generalized to an NB-EGM inner: - the adjuster strips the outer post-decision transition and binds the outer value as a flat param per outer-grid node; the keeper injects the no-adjustment map, making the durable a passive ride-along state; - inner adapters can expose several traced cores (the ride-along NB-EGM kernel splits into continuation and envelope cores), so the nested adapter republishes them under keeper:/adjuster: prefixes; - the outer axis collapses by an fmax fold (an infeasible candidate's NaN-dead cells never poison a cell another candidate solves) and the published continuation is the pointwise (bridged) upper envelope of the candidates' carry rows on the shared liquid state grid — inner configs publishing jump-topology rows are rejected until the topology-preserving publication lands. The smooth two-asset toy pins the wrapper: two-period agreement with NEGM(inner=DCEGM) (tight away from the borrowing-constrained corner), dense-brute consistency with matched choice sets, three-period tracking through published carries, and exact outer-batch invariance. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BNQkLG5QzXcgGdpgH6sN4u
…stein-Zin Admit NestedNBEGM to the nonlinear-certainty-equivalent gate: its inner NB-EGM solve already threads the Epstein-Zin context, the composite-flow Euler inversion reads the fixed-durable flow's power structure, the regime-level survival split takes the joint certainty equivalent, and the outer durable choice is a plain max. On a two-asset EZ problem (liquid Euler margin, illiquid durable with a credited outer move, Cobb-Douglas composite flow, stochastic survival with a bequest) the nested value weakly dominates the dense grid-search value everywhere and tracks it to under a percent on average — the endogenous grid dissolving the durable/consumption grid quantization is the point of the method. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BNQkLG5QzXcgGdpgH6sN4u
…dget The nested toy exercises an inner NB-EGM whose budget declares no breakpoints, matching the zero-breakpoint support the inner solver carries, so the nested solve is validated against the empty-partition path as well as the kinked one. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BNQkLG5QzXcgGdpgH6sN4u
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BNQkLG5QzXcgGdpgH6sN4u
Documentation build overview
42 files changed ·
|
…ed out) The solver, its inner-spec, period kernel, guards, and tests use the NNBEGM / n-nb-egm name, parallel to NBEGM. Descriptive nesting language and the separate NEGM (nested-EGM) solver are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BNQkLG5QzXcgGdpgH6sN4u
The keeper's no-adjustment durable law may depend on more than the stock: a permanent-income deflator keep(car, growth) = (1-vs*delta)*car/growth reads the durable and a growth node. _durable_keeper_transition now introspects the map's signature and threads all its declared arguments (copying each annotation from the map), so the engine resolves them from the regime's DAG. A map reading only the durable is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BNQkLG5QzXcgGdpgH6sN4u
…length Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BNQkLG5QzXcgGdpgH6sN4u
…n seam too The keeper wires its no-adjustment map into the model at two seams: the durable state transition (`_durable_keeper_transition`) and the econ-function DAG's outer post-decision (`_with_no_adjustment_outer_function`). The transition already threads every argument the map declares; the DAG seam still hard-coded a durable-only call, so a keeper that reads a further node — e.g. a permanent-income growth factor `keep(car, growth)` — dropped that argument and failed to resolve. Generalize the DAG seam to introspect the map's signature and thread every declared argument (forcing the durable to `ContinuousState`), mirroring the transition seam. The `no_adjustment_func is None` identity branch is unchanged: `next_<durable>` still declares only the durable stock and holds it. Also greens the branch's type-check: the keeper tests cast their toy functions to the `EconFunction`/`EconFunctionsMapping` protocols, and the two-asset EZ model test annotates its constraints mapping so `|=` type-checks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BNQkLG5QzXcgGdpgH6sN4u
# Conflicts: # src/_lcm/user_regime_validation.py
- The certainty-equivalent build gates (PowerMean-only, H_epstein_zin required) apply to NNBEGM regimes as well as NBEGM ones: the nested solver runs the same EZ inner kernel per outer candidate, so a linear-in-probabilities CE or a custom H is exactly as wrong there. Rejection messages name the concrete solver class. - The nested-vs-dense-brute tests assert unsigned relative gaps (tight mean, loose per-cell cap) instead of one-sided dominance: the two solvers optimize over different outer candidate sets by construction (post-decision grid with off-grid inner policy vs state-dependent action grid with grid-quantized consumption), so neither value weakly dominates the other. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BNQkLG5QzXcgGdpgH6sN4u
Member
Author
|
Round-4 external audit repairs (ledger:
🤖 Generated with Claude Code |
Benchmark comparison (main → HEAD)Comparing
|
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BNQkLG5QzXcgGdpgH6sN4u
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BNQkLG5QzXcgGdpgH6sN4u
The NEGM outer-cost contract requires an inner resources function that does not read the outer post-decision directly: the toy's NEGM variant now declares `outer_cost="credited"` with the cost-free base `resources_before_outer_cost`, and pylcm composes the identical resources quantity at model build. The keeper argument-threading checks compare recomputed arithmetic, so their tolerance scales with the active float dtype's roundoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BNQkLG5QzXcgGdpgH6sN4u
The NNBEGM machinery moves out of the deleted _lcm/solution/solvers.py into its own _lcm/solution/nnbegm.py in the upstream per-solver layout; the multi-argument keeper threading lands in negm.py. The NNBEGM kernel adopts the KernelResult vocabulary (continuation / simulation_policy) and the renamed continuation mapping. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BNQkLG5QzXcgGdpgH6sN4u
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BNQkLG5QzXcgGdpgH6sN4u
…ithful crossing) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BNQkLG5QzXcgGdpgH6sN4u
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #400 (NB-EGM). Adds N-NB-EGM — an outer durable-margin grid search wrapping an inner 1-D NB-EGM (or DCEGM) solve — the pylcm solver for models with one endogenous liquid Euler margin and a second, non-convexly-adjusted durable/illiquid margin.
What this adds
NNBEGMInnerSpec— one vocabulary for a nested 1-D EGM inner configuration (inner Euler margin, post-decision function, savings grid), normalized from aDCEGM/NBEGMinner.NNBEGM— the solver. Each period the outer margind'is enumerated on a grid (a keeper branch holdingd', an adjuster branch choosing it and paying an adjustment cost); conditional ond'the inner solver returns the liquid-margin policy by Euler inversion, and the outer envelope takes the max overd'. The inner cores are the NB-EGM: breakpoint-aware EGM solver for non-convex, discrete-continuous budgets #400 NB-EGM kernels, so any inner capability is inherited.Applicability
N-NB-EGM is a general two-margin solver — durable/illiquid consumption-and-adjustment models, portfolio-with-transaction-cost problems, and the (S)Cars-style car-choice DP all fit its shape. Two capabilities exercised here as concrete tests:
test_n_nbegm_epstein_zin.py) — liquid margin + illiquid durable with a credited adjust move, Cobb-Douglas composite flow, stochastic survival (mortality), Epstein-Zin. The N-NB-EGM value weakly dominates and tracks (< 1% mean) a dense two-actionGridSearchoracle — the established off-grid-EGM criterion, since grid-quantized brute is a lower bound for the durable choice.Notes
c; a flow whose consumption elasticity varies (a nested CES) needs a numeric inverse.Audit repair set merged from #400
The external adversarial audit of #400's Epstein-Zin implementation
(findings F1–F10, see the audit section in #400's description) is merged
down: anchored log-domain certainty-equivalent partials, per-node lottery
transforms (stochastic pre-fold gated off under EZ), the paired
value-and-gradient marginal read, build-time recursion gates, the
Cobb-Douglas unit-EIS limit, and the below-support-only carry-read
extrapolation. The N-NB-EGM keeper branch reads its continuation through
the same repaired seams. Full suite on this branch after the merge:
green (zero failures).
🤖 Generated with Claude Code
https://claude.ai/code/session_01BNQkLG5QzXcgGdpgH6sN4u