Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
203 commits
Select commit Hold shift + click to select a range
4f2de0f
Introduce the solver-selection seam (BruteForce dispatch + DCEGM stub)
hmgaudecker Jun 17, 2026
91cc590
Pin benchmark aca-model to the dcegm-solver merge (54fb8df)
hmgaudecker Jun 18, 2026
44e8522
Redesign the solver seam as an ABC (GridSearch + DCEGM stub).
hmgaudecker Jun 19, 2026
a8db6c6
DC-EGM solver implementation (squashed; re-rooted onto the solver seam)
hmgaudecker Jun 17, 2026
9501df7
Accumulate the DC-EGM stochastic-node expectation in lax.scan blocks.
hmgaudecker Jun 17, 2026
e91025c
Add a Credits & Acknowledgments page and fill out references.
hmgaudecker Jun 18, 2026
fd8fff6
Attribute the FUES backend's provenance (OSE/upper-envelope).
hmgaudecker Jun 18, 2026
7611a0a
Add NOTICE and the upstream license for the FUES derivation.
hmgaudecker Jun 18, 2026
2ea0fbd
Extract shared regime-introspection helpers into their own module.
hmgaudecker Jun 19, 2026
de9f553
Extract the DC-EGM kernel-scope checks into their own module.
hmgaudecker Jun 19, 2026
f386bab
Extract the DC-EGM continuation subsystem into its own module.
hmgaudecker Jun 19, 2026
1382734
Extract the textbook single-post-state DC-EGM solve into step_core.py.
hmgaudecker Jun 19, 2026
1a6728d
Extract the asset-row DC-EGM path into its own module.
hmgaudecker Jun 19, 2026
0e46784
Bind the DC-EGM continuation behind a single per-savings-node callable.
hmgaudecker Jun 19, 2026
a3910b8
Fix the beartype forward-ref cycle in the solver contract and finish …
hmgaudecker Jun 19, 2026
6755192
Build a valid DC-EGM model in the solver-seam build test.
hmgaudecker Jun 19, 2026
5ad5297
Unify the solve loop behind a single period-kernel adapter (seam laye…
hmgaudecker Jun 19, 2026
a73c274
Stream the asset-row refine-to-query into the FUES scan
hmgaudecker Jun 19, 2026
ea67474
Skip per-period gc.collect() when no continuation carry rolled off.
hmgaudecker Jun 21, 2026
e1f6974
Add DS housing-keeper DC-EGM feasibility probe
hmgaudecker Jun 21, 2026
a7b591b
Add NEGM Phase-0 risk-gate probes and findings.
hmgaudecker Jun 17, 2026
e6d8d97
Add NEGM (nested-EGM) solver prototype, contract validator, and G1 mo…
hmgaudecker Jun 21, 2026
f214285
Add brute-force oracle + value-parity test for DS housing keeper
hmgaudecker Jun 21, 2026
a4b3023
Make EGM batch-invariance tests precision-aware for 32-bit CI.
hmgaudecker Jun 21, 2026
4a7bc4f
Rename solve_brute → backward_induction (module + tests)
hmgaudecker Jun 21, 2026
d903f83
Docs: warn against materializing JAX arrays at module import
hmgaudecker Jun 21, 2026
31ff80b
Merge branch 'feat/solver-seam' into feat/dcegm
hmgaudecker Jun 21, 2026
0d3a855
Merge branch 'main' into feat/dcegm
hmgaudecker Jun 21, 2026
a51852b
Add type-local continuation-V tests (correctness guard + all-gather d…
hmgaudecker Jun 21, 2026
aa3d772
Add co-mapped in_axes to vmap_1d for device-local pytree slicing
hmgaudecker Jun 21, 2026
f9a4248
Type the HLO test helper's return as non-optional
hmgaudecker Jun 21, 2026
041dc2d
Make the continuation-V interpolation device-local for fixed shards
hmgaudecker Jun 21, 2026
b85d824
Land 1-D RFC as DCEGM(upper_envelope="rfc")
hmgaudecker Jun 19, 2026
2228aa1
Add inverse-bilinear point-in-quad cell locator (P4 foundation).
hmgaudecker Jun 21, 2026
54c53d9
Add two-asset (liquid + pension) model + brute oracle (P3/rung-1).
hmgaudecker Jun 21, 2026
eb78caf
Add two-asset unconstrained inverse-Euler step (rung-1 kernel core).
hmgaudecker Jun 21, 2026
1d180f3
Add post-decision value + gradients for the two-asset model (rung-1 k…
hmgaudecker Jun 21, 2026
db2f55a
Merge branch 'feat/p4-cell-locator' into feat/ds-pension-model
hmgaudecker Jun 21, 2026
b762cae
Assemble + validate the rung-1 2-D EGM step against brute (P4 foundat…
hmgaudecker Jun 21, 2026
3553880
Add dcon (deposit-constrained) inverse-Euler step for the two-asset m…
hmgaudecker Jun 21, 2026
25098ea
Add acon/con constrained-region inverses for the two-asset EGM step
hmgaudecker Jun 21, 2026
935c810
Add faithful DS pension benchmark model as a three-regime brute oracl…
hmgaudecker Jun 22, 2026
f69fcae
Add DS housing benchmark model as a brute oracle (P3b)
hmgaudecker Jun 22, 2026
26c9674
Fix next_illiquid annotation mismatch in the NEGM kinked-toy model
hmgaudecker Jun 22, 2026
6d5c892
Pop marginal_continuation from the params template for NEGM regimes
hmgaudecker Jun 22, 2026
a01a228
Wire the NEGM outer post-decision into the inner child-carry continua…
hmgaudecker Jun 22, 2026
8921b54
Build terminal carry producers for NEGM models (model_has_dcegm_regime)
hmgaudecker Jun 22, 2026
124b0d0
Filter the NEGM no-adjustment candidate's kwargs to its signature
hmgaudecker Jun 22, 2026
b8a58b5
Add triangular-mesh barycentric geometry for the 2-D G2EGM envelope (…
hmgaudecker Jun 22, 2026
8d4f120
Add the 2-D G2EGM first/second upper envelope over triangular meshes …
hmgaudecker Jun 22, 2026
bd71c58
Add the two-asset Bellman-objective evaluator for the G2EGM envelope …
hmgaudecker Jun 22, 2026
deeb70b
Add the SegmentMesh builder from a two-asset region cloud (P5 v3)
hmgaudecker Jun 22, 2026
678e191
Add the exhaustive all-simplex host oracle for the G2EGM first envelo…
hmgaudecker Jun 22, 2026
80ef2a7
Add region-aware envelope gradient and the cross-segment switch mask …
hmgaudecker Jun 22, 2026
f749ba9
Assemble the four-segment G2EGM upper-envelope step (P5 v3)
hmgaudecker Jun 22, 2026
ff8751c
Harden the G2EGM envelope admissibility and candidate masking (v3 audit)
hmgaudecker Jun 22, 2026
f329202
Mask out-of-domain candidates and add the direct-Bellman hole-fill (v…
hmgaudecker Jun 22, 2026
5d6fd96
Report the winning KKT region label and a no-candidate flag from the …
hmgaudecker Jun 22, 2026
ee9639f
Correct the marginal-value docstring and reject sub-cell triangulatio…
hmgaudecker Jun 22, 2026
5a55eaa
Make the exhaustive envelope oracle fully independent of production (…
hmgaudecker Jun 22, 2026
0ec88ea
Chain the four-segment G2EGM step across periods, matching brute on t…
hmgaudecker Jun 22, 2026
22d6048
Add the 1-D retirement EGM step for the DS pension driver
hmgaudecker Jun 22, 2026
91cbd14
Add the working->retired boundary post-decision reader
hmgaudecker Jun 22, 2026
b019248
Add the working->retired boundary G2EGM step via an injected post-dec…
hmgaudecker Jun 22, 2026
c692dc9
Add the standalone G2EGM driver solving the full DS pension lifecycle
hmgaudecker Jun 22, 2026
29bd67a
Add the unit-free consumption Euler error as a solution-accuracy metric
hmgaudecker Jun 22, 2026
440459a
Publish the G2EGM consumption/deposit policy from the working steps
hmgaudecker Jun 22, 2026
b6188fe
Add the working consumption Euler error, completing the G2EGM accurac…
hmgaudecker Jun 22, 2026
81aab48
Add the DS pension comparison harness, emitting the G2EGM table column
hmgaudecker Jun 22, 2026
411c034
Scale the post-decision grids with resolution in the DS pension bench…
hmgaudecker Jun 22, 2026
1fbb497
Note the DJ-2017 post-decision-grid guidance in the benchmark docstring
hmgaudecker Jun 22, 2026
6b5aec2
Carry multiple cores through the AOT contract; add the NEGM dual-core…
hmgaudecker Jun 22, 2026
029bbcc
Feed the NEGM keeper its identity outer post-decision in resources an…
hmgaudecker Jun 22, 2026
9815dd0
Match the keeper identity function's annotations to the regime functions
hmgaudecker Jun 22, 2026
382c438
Treat a NEGM carry target as a DC-EGM child in the continuation read
hmgaudecker Jun 22, 2026
4af912a
Make G2EGM and 1-D EGM prime-time Solvers (TwoDimEGM, OneAssetEGM)
hmgaudecker Jun 22, 2026
1dd85af
Synthesize the intrinsic budget constraint for NEGM simulate phases
hmgaudecker Jun 22, 2026
8400035
Add GPU-gated NEGM forward-simulation tests for the kinked toy
hmgaudecker Jun 22, 2026
4993189
Merge feat/dcegm-negm-impl into feat/dcegm (consolidate NEGM + G2EGM)
hmgaudecker Jun 23, 2026
baac891
Merge branch 'feat/ds-housing-keeper' into feat/dcegm
hmgaudecker Jun 23, 2026
b62002a
Add DS-2026 Application 1 FUES Euler-error accuracy harness.
hmgaudecker Jun 23, 2026
16cdd07
Add the RFC-1D column to the DS-2026 App.1 accuracy harness
hmgaudecker Jun 23, 2026
f425ced
Add LTM brute upper-envelope backend (DS-2026 #114).
hmgaudecker Jun 23, 2026
e49cd8a
Add compile/runtime-separated timing to the DS-2026 App.1 harness
hmgaudecker Jun 23, 2026
3ad066e
Add MSS upper-envelope backend (HARK's EGM upper envelope)
hmgaudecker Jun 23, 2026
a2c356b
Add DS-2026 App.2 housing model as a pylcm NEGM model
hmgaudecker Jun 23, 2026
42736db
Clear the JAX cache before app1_timing's compile measurement
hmgaudecker Jun 23, 2026
ea9060f
Add DS-2026 App.3 discrete-housing model (no-tax) + construction test
hmgaudecker Jun 23, 2026
13307df
Add the taste-shock retirement variant (DS-2026 Table 6) to the App.1…
hmgaudecker Jun 23, 2026
64f8061
Add DS-2026 App.3 discrete-housing VFI Euler-accuracy harness
hmgaudecker Jun 23, 2026
1d3d1c8
Thread runtime process grids into DC-EGM resources continuation
hmgaudecker Jun 23, 2026
c9f0024
Admit choice-driven discrete transitions into terminal DC-EGM carries
hmgaudecker Jun 23, 2026
c87b7d4
Generalize the App.3 accuracy harness to the DC-EGM solvers
hmgaudecker Jun 23, 2026
76c06a2
Admit outer post-decision as a bound param in the NEGM adjuster scope…
hmgaudecker Jun 23, 2026
c476a1a
Support a terminal bequest over the Euler state plus passive durable …
hmgaudecker Jun 23, 2026
9e663eb
Resolve next-state reads in the NEGM forward-simulation decision
hmgaudecker Jun 23, 2026
8f6aa41
Floor the DS App.2 housing grid above zero
hmgaudecker Jun 23, 2026
db619bd
Floor next_housing in DS App.2 simulate to de-corner the NEGM policy
hmgaudecker Jun 23, 2026
587a3ad
Add DS App.2 NEGM Euler-error accuracy harness
hmgaudecker Jun 23, 2026
29cc336
Add DS App.2 EGM-FUES discrete-housing model + VFI oracle
hmgaudecker Jun 23, 2026
1ddfc62
Add DS App.2 EGM-FUES Euler/timing harness + NEGM timing
hmgaudecker Jun 23, 2026
36fc30e
Build DS App.3 with-tax variant (Table 5) + FUES-vs-VFI oracle
hmgaudecker Jun 23, 2026
8b69c4b
Add one-command paper-scale DS-2026 table runner
hmgaudecker Jun 23, 2026
a80ffc1
Fix App.1 multi-method table assembly in the paper-scale runner
hmgaudecker Jun 23, 2026
7019b44
Cap the App.2 Euler policy-reconstruction seed grid for paper-scale NG
hmgaudecker Jun 23, 2026
5402cb4
Fix App.3 with-tax Euler scorer to net the marginal tax rate (S1).
hmgaudecker Jun 23, 2026
8c029bf
Lock FUES exact-node branch-crossing bug as strict-xfail (audit F5).
hmgaudecker Jun 23, 2026
eb38d06
Add branch-aware exact upper-envelope oracle for the EGM backends.
hmgaudecker Jun 23, 2026
9bed115
Certify all four envelope backends against the exact oracle.
hmgaudecker Jun 23, 2026
5b8dc29
Fix kinked-toy inverse marginal utility to carry the durable flow offset
hmgaudecker Jun 24, 2026
88d04dd
Repair the kinked-toy brute oracle's wealth domain and harden provenance
hmgaudecker Jun 24, 2026
dafecfb
Replace the kinked-toy NEGM parity tests with valid matched comparisons
hmgaudecker Jun 24, 2026
13403c7
Add the NEGM cash-on-hand outer-max carry envelope helper
hmgaudecker Jun 24, 2026
b117344
Publish the NEGM continuation as the cash-on-hand outer-max envelope
hmgaudecker Jun 24, 2026
693877e
Align the kinked-toy NEGM wealth domain with the repaired oracle
hmgaudecker Jun 24, 2026
b4fdc24
Make kinked-toy NEGM oracle/convergence tests platform-robust.
hmgaudecker Jun 24, 2026
f5a6e7c
Scope the kinked-toy oracle-reproducibility test to CPU.
hmgaudecker Jun 24, 2026
3ed9ec1
Reject n_housing < 2 in App.2 FUES discrete-housing builder
hmgaudecker Jun 24, 2026
b029dc1
Cap App.2 next-housing at housing_max in simulation, not just floor it
hmgaudecker Jun 24, 2026
bcb8ec4
Document the App.3 subsidy-bracket tax discontinuity at a=6.97
hmgaudecker Jun 24, 2026
e8a7057
Reword box-named harness comments to qualitative dependencies
hmgaudecker Jun 24, 2026
85920b3
Assert concrete NEGM simulated consumption, not just finiteness
hmgaudecker Jun 24, 2026
7911f5b
Reject decisions that read a target-dependent deterministic state law
hmgaudecker Jun 24, 2026
df203fc
Make the NEGM carry envelope handle separable states and discrete axes
hmgaudecker Jun 24, 2026
dbade84
Reimplement the DS App.2 housing cost as the eq. 12 round-trip cost
hmgaudecker Jun 24, 2026
81ddeaf
Publish the G2EGM hole-fill's maximizing policy, not just its value
hmgaudecker Jun 24, 2026
44ba7bb
Correct the App.2 EGM-vs-VFI (S,s) gap explanation: comparator orderi…
hmgaudecker Jun 24, 2026
f5a4ecf
Add host NumPy/SciPy reference for the multidim RFC (DS-2024 P6 step 1)
hmgaudecker Jun 24, 2026
11a5213
Add on-device 2-D RFC delete kernel, validated vs host mask (DS-2024 …
hmgaudecker Jun 24, 2026
0c71a3b
Add on-device 2-D RFC barycentric publisher (DS-2024 P6 step 3)
hmgaudecker Jun 24, 2026
f9772c6
Add jit-safe valid-mask to the 2-D RFC publisher (DS-2024 P6)
hmgaudecker Jun 24, 2026
1f2e297
Add combined-cloud 2-D RFC two-asset step (DS-2024 P6 step 5)
hmgaudecker Jun 24, 2026
74850e7
Scope RFC two-asset parity test to the validated bulk interior
hmgaudecker Jun 24, 2026
8ecfbc2
Route TwoDimEGM.upper_envelope to the RFC two-asset step (DS-2024 P6)
hmgaudecker Jun 24, 2026
b3e4852
Scope RFC pension test interior to the corner-propagation pattern
hmgaudecker Jun 24, 2026
7697aee
Fix FUES dropping a genuine cross-segment crossing (#121 F5)
hmgaudecker Jun 24, 2026
a2aad83
Add RFC row + comparison runner to the DS pension harness (DS-2024 P7)
hmgaudecker Jun 24, 2026
0cf6293
Add liquid-batch knob + incremental driver for App.2 paper-scale sweep
hmgaudecker Jun 24, 2026
c3f9b3d
Fix stale expected consumption in NEGM service-flow simulate test
hmgaudecker Jun 24, 2026
619b80f
Add DS-2024 pension G2EGM-vs-RFC comparison driver
hmgaudecker Jun 24, 2026
c43d7d5
Fold the NEGM outer search instead of materialising every node
hmgaudecker Jun 24, 2026
836b195
Chunk the NEGM outer search by a tunable batch size
hmgaudecker Jun 24, 2026
f2b3432
Materialise the NEGM outer fold per chunk
hmgaudecker Jun 25, 2026
c820af8
Subject-batch the App.2 NEGM Euler-accuracy simulate
hmgaudecker Jun 25, 2026
a738df8
Add branch-aware VFI oracle pinning the App.2 (S,s) comparator gap
hmgaudecker Jun 25, 2026
be82fe7
Add DS-2024 housing NEGM model + brute-twin oracle (delta=0 faithful)
hmgaudecker Jun 25, 2026
3d12374
Add DS-2024 housing RFC column (discrete-housing DC-EGM) + brute oracle
hmgaudecker Jun 25, 2026
4cb7b59
Add DS-2024 housing RFC-vs-NEGM comparison harness
hmgaudecker Jun 25, 2026
73020f2
Merge remote-tracking branch 'origin/feat/type-local-continuation-v' …
hmgaudecker Jun 25, 2026
aa6f3f0
Guard test-only imports in DS replication benchmark drivers
hmgaudecker Jun 25, 2026
9ea23df
Default the published benchmark graph x-axis to the date scale
hmgaudecker Jun 25, 2026
7dc5887
Split asv benchmarks from manual drivers into benchmarks/asv/.
hmgaudecker Jun 25, 2026
78cefed
Correct the DC-EGM-vs-brute speed claims in bench_iskhakov.
hmgaudecker Jun 25, 2026
726d508
Add red spec for FUES on-grid crossing defect (W2 goal, P0.1).
hmgaudecker Jun 25, 2026
01bcb37
Add red specs for NEGM-island and 2-D-RFC-KKT defects (W2 reverify).
hmgaudecker Jun 25, 2026
a0d77e9
Harden the host envelope oracle into the authoritative reference (W2a).
hmgaudecker Jun 25, 2026
8a313d9
Add red spec for the LTM/MSS spurious-bridge defect (W2, P0.2).
hmgaudecker Jun 25, 2026
4329d40
Compute per-region KKT validity masks on the two-asset region clouds …
hmgaudecker Jun 25, 2026
6048a43
LTM and MSS consume explicit branch topology (W2, P0.2).
hmgaudecker Jun 25, 2026
4354057
FUES preserves node-aligned crossings via a dual-policy post-pass (W2…
hmgaudecker Jun 25, 2026
140d538
Add the exact query-side envelope backend (W2b).
hmgaudecker Jun 25, 2026
a22f77b
NEGM outer envelope captures sub-grid (S,s) islands (P0.3)
hmgaudecker Jun 25, 2026
c978c09
Document the single-branch refine contract (P0.2 generation half)
hmgaudecker Jun 25, 2026
0420bc7
Apply 2-D KKT region masks; smallest-simplex RFC publish (P0.4 + 2-D …
hmgaudecker Jun 25, 2026
02bdca1
Apply the KKT region mask to the G2EGM segment mesh (P0.4)
hmgaudecker Jun 25, 2026
747b4ab
Add numerical inverse marginal utility for iEGM (W1 core)
hmgaudecker Jun 25, 2026
7a6379b
Auto-select numeric inverse when no analytic one is supplied (W1 wiring)
hmgaudecker Jun 25, 2026
d653ac0
Add a continuation operator (value-transform pair) to Q_and_F (W4a)
hmgaudecker Jun 25, 2026
e3abee0
Address Pro review: W1 bracket guard, RFC publish docstring, W4a soun…
hmgaudecker Jun 25, 2026
2deac07
W4a: joint-lottery continuation operator, sound for multi-regime EZ
hmgaudecker Jun 26, 2026
e021641
RFC publish: most-local well-conditioned simplex (Pro A.3)
hmgaudecker Jun 26, 2026
ca7667d
W1: log-space safeguarded Newton for the numeric inverse marginal uti…
hmgaudecker Jun 26, 2026
2df9426
Speed B: add DCEGM.fues_scan_unroll knob for the FUES candidate scan
hmgaudecker Jun 26, 2026
0f1a91e
Speed B: blocked segment scan for envelope_at_query (memory-wall fix)
hmgaudecker Jun 26, 2026
2316737
Pro round-2: A.3 mean-ratio normalization, W1 positivity gate, A.5 ti…
hmgaudecker Jun 26, 2026
c385d25
NEGM: wire outer_no_adjustment_candidate as the keeper durable map
hmgaudecker Jun 26, 2026
1e69422
DS-2024 housing: depreciate the keeper stock to h(1-delta)
hmgaudecker Jun 28, 2026
f4fc1a3
DS-2024 housing: validate the delta>0 keeper against a free-keep VFI …
hmgaudecker Jun 28, 2026
c352bef
NEGM: document why the coh-shift keeper reference stays the identity …
hmgaudecker Jun 28, 2026
11fa593
App.3: pin the tax schedule to its upstream commit + checksum
hmgaudecker Jun 28, 2026
18b9dc5
Fix asv GPU peak-mem subprocess: _PROJECT_ROOT off by one directory
hmgaudecker Jun 29, 2026
305c33e
Drop the stale missing-inverse-marginal-utility contract-violation case
hmgaudecker Jun 29, 2026
5102262
Run the test suite across 4 xdist workers (CPU + GPU)
hmgaudecker Jun 29, 2026
d5cd052
Admit MappingLeaf/SequenceLeaf params in the Q-bundle threading contract
hmgaudecker Jun 29, 2026
ba93bda
Run the GPU test jobs serially (the runner can't host 4 parallel comp…
hmgaudecker Jun 29, 2026
5f54e42
Deselect the DC-EGM solve battery on the macOS CI runner
hmgaudecker Jun 29, 2026
fa32a01
Update lock file using correct syntax.
hmgaudecker Jun 29, 2026
12571d7
Bump CI pixi-version to v0.71.2 to read the v7 lock format
hmgaudecker Jun 29, 2026
8ea1947
Feed a canonical int32 param to the raw-inputs simulate test
hmgaudecker Jun 29, 2026
dd94481
Merge remote-tracking branch 'origin/main' into feat/type-local-conti…
hmgaudecker Jun 29, 2026
e362009
Merge branch 'feat/type-local-continuation-v' into feat/dcegm
hmgaudecker Jun 29, 2026
10e96ea
Merge main into feat/dcegm (Epstein-Zin certainty-equivalent ↔ DC-EGM).
hmgaudecker Jul 6, 2026
06170a1
Scale the DC-EGM battery's tolerances to the active float precision
hmgaudecker Jul 6, 2026
8805937
Accept a one-grid-step band in the NEGM simulate reproduction test
hmgaudecker Jul 6, 2026
d0fccd1
CI: phase the CPU coverage run, isolate the Mahler-Yum GPU module
hmgaudecker Jul 6, 2026
3d88937
prek autoupdate.
hmgaudecker Jul 6, 2026
72ba8de
Restore pixi.lock
hmgaudecker Jul 6, 2026
47562f1
Satisfy ty 0.0.56 across the deterministic-transition wrapper and sci…
hmgaudecker Jul 6, 2026
fdc162f
Move type checking from a pixi task to the prek ty hook
hmgaudecker Jul 6, 2026
b9412f5
Bump .ai-instructions to current main
hmgaudecker Jul 6, 2026
af57b3c
CI: bump action and pixi pins to current releases
hmgaudecker Jul 6, 2026
239919c
[pre-commit.ci] pre-commit autoupdate (#399)
pre-commit-ci[bot] Jul 7, 2026
13fdf82
Fix DC-EGM review findings and make the FUES scan exhaustive by default
hmgaudecker Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/benchmark-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
sleep 60
done
echo "GPU is free."
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Install environment
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/benchmark-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
sleep 60
done
echo "GPU is free."
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
Expand Down
77 changes: 56 additions & 21 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,46 @@ jobs:
- windows-latest
python-version:
- '3.14'
# The small macOS runner can't host four parallel JAX AOT compiles of the
# DC-EGM solve/oracle battery (it swap-thrashes for hours). Drop the `slow`
# set there; the platform-independent kernel stays covered on Linux + GPU.
include:
- os: macos-latest
pytest_extra: -m "not slow"
steps:
- uses: actions/checkout@v6
- uses: prefix-dev/setup-pixi@v0.9.6
- uses: actions/checkout@v7
- uses: prefix-dev/setup-pixi@v0.10.0
with:
pixi-version: v0.70.1
pixi-version: v0.72.0
cache: true
cache-write: ${{ github.event_name == 'push' && github.ref_name == 'main' }}
environments: tests-cpu
activate-environment: true
frozen: true
- name: Run pytest
shell: bash {0}
run: pixi run -e tests-cpu tests
run: pixi run -e tests-cpu tests ${{ matrix.pytest_extra }}
if: runner.os != 'Linux' || matrix.python-version != '3.14'
- name: Run pytest and collect coverage
shell: bash {0}
run: pixi run -e tests-cpu tests-with-cov
# Phased: JAX compile caches make a pytest process's resident memory
# grow with every distinct compiled program it has run, and the DC-EGM
# solve/oracle battery (`slow`) compiles enough programs that one
# full-suite pass exhausts the 16 GB runner (the agent receives a
# shutdown signal mid-suite). Sequential invocations release each
# phase's memory before the next starts; coverage is appended across
# them.
run: |
pixi run -e tests-cpu pytest tests -m "not slow" \
--cov=./ --cov-report= -n 4 \
&& pixi run -e tests-cpu pytest tests/solution -m slow \
--cov=./ --cov-append --cov-report= -n 2 \
&& pixi run -e tests-cpu pytest tests --ignore=tests/solution -m slow \
--cov=./ --cov-append --cov-report=xml -n 2
if: runner.os == 'Linux' && matrix.python-version == '3.14'
- name: Upload coverage report
if: runner.os == 'Linux' && matrix.python-version == '3.14'
uses: codecov/codecov-action@v6.0.1
uses: codecov/codecov-action@v7.0.0
with:
token: ${{ secrets.CODECOV_TOKEN }}
run-ty:
Expand All @@ -61,17 +80,19 @@ jobs:
python-version:
- '3.14'
steps:
- uses: actions/checkout@v6
- uses: prefix-dev/setup-pixi@v0.9.6
- uses: actions/checkout@v7
- uses: prefix-dev/setup-pixi@v0.10.0
with:
pixi-version: v0.70.1
pixi-version: v0.72.0
cache: true
cache-write: ${{ github.event_name == 'push' && github.ref_name == 'main' }}
environments: type-checking
frozen: true
- name: Run ty
shell: bash {0}
run: pixi run ty
# The prek `ty` hook is skipped on pre-commit.ci (no pixi envs there);
# this job runs it against the `type-checking` env instead.
run: pixi run -e type-checking prek run ty --all-files
run-tests-gpu:
name: Run tests on GPU
# Config:
Expand All @@ -84,17 +105,29 @@ jobs:
run: |
nvidia-smi
nvcc --version || echo "nvcc not found"
- uses: actions/checkout@v6
- uses: prefix-dev/setup-pixi@v0.9.6
- uses: actions/checkout@v7
- uses: prefix-dev/setup-pixi@v0.10.0
with:
pixi-version: v0.70.1
pixi-version: v0.72.0
cache: true
cache-write: ${{ github.event_name == 'push' && github.ref_name == 'main' }}
environments: tests-cuda12
frozen: false
- name: Run pytest on GPU
shell: bash {0}
run: pixi run -e tests-cuda12 tests
# Run serially (-n0 overrides the task's -n 4): the single GPU runner is
# starved of host CPU/memory by parallel JAX AOT compiles. CPU jobs keep -n 4.
#
# The GPU-scale Mahler & Yum module runs first, in its own process: its
# solve kernels only fit the device when XLA fully fuses the
# max-over-actions reduction, and compiling them late in a long-lived
# process that has run the whole DC-EGM battery has produced an
# unfused executable whose scratch arena (half the state-action product)
# exceeds device memory. A fresh process pins the fused compile.
run: |
pixi run -e tests-cuda12 pytest tests/test_mahler_yum_2024.py -n0 \
&& pixi run -e tests-cuda12 pytest tests -n0 \
--ignore=tests/test_mahler_yum_2024.py
run-tests-gpu-32bit:
name: Run tests on GPU (32-bit precision)
runs-on: ubuntu-latest-gpu
Expand All @@ -103,25 +136,27 @@ jobs:
run: |
nvidia-smi
nvcc --version || echo "nvcc not found"
- uses: actions/checkout@v6
- uses: prefix-dev/setup-pixi@v0.9.6
- uses: actions/checkout@v7
- uses: prefix-dev/setup-pixi@v0.10.0
with:
pixi-version: v0.70.1
pixi-version: v0.72.0
cache: true
cache-write: ${{ github.event_name == 'push' && github.ref_name == 'main' }}
environments: tests-cuda12
frozen: false
- name: Run pytest on GPU with 32-bit precision
shell: bash {0}
run: pixi run -e tests-cuda12 tests-32bit
# Run serially (-n0 overrides the task's -n 4): the single GPU runner is
# starved of host CPU/memory by parallel JAX AOT compiles. CPU jobs keep -n 4.
run: pixi run -e tests-cuda12 tests-32bit -n0
run-explanation-notebooks:
name: Run explanation notebooks on Python 3.14
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: prefix-dev/setup-pixi@v0.9.6
- uses: actions/checkout@v7
- uses: prefix-dev/setup-pixi@v0.10.0
with:
pixi-version: v0.70.1
pixi-version: v0.72.0
cache: true
cache-write: ${{ github.event_name == 'push' && github.ref_name == 'main' }}
environments: docs
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-to-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
name: Build and publish Python 🐍 distributions 📦 to PyPI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Set up Python 3.13
uses: actions/setup-python@v6
with:
Expand Down
21 changes: 17 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ repos:
- id: check-hooks-apply
- id: check-useless-excludes
- repo: https://github.com/tox-dev/pyproject-fmt
rev: v2.23.0
rev: v2.25.1
hooks:
- id: pyproject-fmt
- repo: https://github.com/lyz-code/yamlfix
Expand Down Expand Up @@ -39,7 +39,7 @@ repos:
- id: name-tests-test
args:
- --pytest-test-first
exclude: tests/mock_regime.py|^tests/test_models/|^tests/data/
exclude: tests/mock_regime.py|^tests/test_models/|^tests/data/|tests/solution/_envelope_oracle.py|tests/solution/_rfc_2d_host.py|tests/solution/_branch_aware_vfi_oracle.py|tests/solution/_ds2024_housing_vfi_oracle.py
- id: no-commit-to-branch
args:
- --branch
Expand All @@ -50,11 +50,11 @@ repos:
hooks:
- id: yamllint
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.37.2
rev: 0.37.4
hooks:
- id: check-github-workflows
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.16
rev: v0.15.20
hooks:
- id: ruff-check
args:
Expand All @@ -68,6 +68,15 @@ repos:
- jupyter
- pyi
- python
- repo: https://github.com/astral-sh/ty-pre-commit
rev: v0.0.56
hooks:
- id: ty
# `--no-project` keeps uv from creating a `.venv`/`uv.lock` in this
# pixi-managed repo; ty resolves third-party imports from the env named
# in `[tool.ty] environment.python` (run `pixi install` once).
args:
- --no-project
- repo: https://github.com/kynan/nbstripout
rev: 0.9.1
hooks:
Expand Down Expand Up @@ -99,3 +108,7 @@ repos:
exclude: docs/index\.md
ci:
autoupdate_schedule: monthly
# pre-commit.ci has no pixi environments and blocks network at hook runtime;
# the GitHub Actions run-ty job covers type checking.
skip:
- ty
49 changes: 49 additions & 0 deletions .roborev.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# roborev local repo config.
# review_guidelines is injected into every review prompt for this repo as a
# "## Project Guidelines" section. Agent/model/reasoning come from the global
# ~/.roborev/config.toml (review_agent = codex, reasoning = thorough).

display_name = "dev-pylcm"

review_guidelines = '''
Run this review as a sequence of GATES. Treat the diff as work that must earn a
PASS at each gate before you sign off. Default to FAIL under uncertainty —
absence of evidence is not evidence of correctness.

At each gate, evaluate the change from TWO adversarial stances, then reconcile
them into a single PASS/FAIL verdict for that gate:

PROSECUTOR — "This change is broken. Find the failure."
Assume a defect exists. Hunt for: incorrect logic, off-by-one and boundary
errors, unhandled None/empty/NaN/error cases, broken invariants, mutable
default or aliasing bugs, race conditions, resource leaks, data loss,
regressions in callers, and silent behavior changes. Construct the concrete
input or call sequence that breaks it. If you cannot construct one after a
genuine attempt, say so explicitly.

DEFENDER — "This change is correct and complete. Justify it."
Point to the evidence: tests covering the new/changed paths, invariants
preserved, edge cases handled, types/docs updated. Where the defense relies
on an assumption not visible in the diff, name the assumption.

VERDICT: PASS only if the defense withstands the prosecution with evidence in
the diff. Otherwise FAIL, and report the prosecutor's strongest concrete
finding.

GATES (apply each; a gate that does not apply to this diff is PASS — state why):
1. Correctness & logic — does it do what the commit intends, for all inputs?
2. Edge cases & error handling — empty, None, zero, large, concurrent,
failure paths; numerical stability where relevant.
3. Tests — are the changed paths actually exercised? New behavior without a
test is a finding unless tests are clearly out of scope for this commit.
4. Interfaces & callers — are all call sites updated; any breaking
signature/behavior change?
5. Safety — data loss/corruption, unsafe deserialization, injection, secrets,
path traversal.
6. Clarity & maintainability — naming, dead code, needless complexity
(report as low severity only).

Report only findings that survive the adversarial pass. For each: file:line,
the gate it failed, severity, and the concrete failure scenario or the minimal
fix. Do not pad the review with style nits dressed up as defects.
'''
8 changes: 5 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ automation. Python 3.14+ is required.
- `pixi run tests-with-cov` - Run tests with coverage reporting
- `pytest tests/test_specific_module.py` - Run specific test file
- `pytest tests/test_specific_module.py::test_function_name` - Run specific test
- `pixi run ty` - Type checking with ty
- `prek run --all-files` - Run all pre-commit hooks
- `prek run ty --all-files` - Type checking with ty (a pre-commit hook; resolves
third-party imports from the pixi env named in `[tool.ty] environment.python`, so run
`pixi install` once)
- `prek run --all-files` - Run all pre-commit hooks (includes the `ty` hook)
- `pixi run -e docs build-docs` - Build documentation
- `pixi run -e docs view-docs` - Live preview documentation
- `pixi install` - Install dependencies
Expand Down Expand Up @@ -572,7 +574,7 @@ prose hides cases.

- Extensive use of typing with custom types: user-facing aliases in `src/lcm/typing.py`,
engine-side aliases and protocols in `src/_lcm/typing.py`
- Type checking with ty (pixi run ty)
- Type checking with ty (`prek run ty --all-files`)
- Use `# ty: ignore[error-code]` for type suppression, never `# type: ignore`
- JAX typing integration via jaxtyping

Expand Down
69 changes: 20 additions & 49 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,55 +7,26 @@ chronological order. We follow [semantic versioning](https://semver.org/).

## Unreleased

### Phase grammar, cross-regime transitions, and model-level regime slots

- `Phased(solve=..., simulate=...)` gives any regime-slot value a per-phase
variant; a bare value broadcasts to both phases. Carried states —
`Phased(solve=callable, simulate=Grid)` in `states` — are derived functions
during backward induction and genuine seeded-and-evolved states in
simulation. See the [phase grammar](docs/explanations/phase_grammar.ipynb)
explanation.

- `fixed_transition(state_name)` marks a fixed state (identity law) in
`state_transitions`. The `None` spelling for fixed states is removed; a
regime-level `None` now masks a model-level entry instead.

- Regime transitions take a third form: a per-target dict
`{target_regime: MarkovTransition(prob_func)}` whose key set declares the regime's
reachable targets — omitted regimes are structurally unreachable. Per-target
dicts in `state_transitions` hand state values across regime boundaries,
including into states the source regime does not carry and across grids that
differ between regimes.

- Model-level regime slots: `Model(functions=..., constraints=..., states=...,
state_transitions=..., actions=...)` declares shared structure once and
merges it into every regime under the exactly-one-level rule. Broadcast
states and actions are pruned per regime by DAG reachability;
`model.pruned_variables` records the result.

- `model.user_regimes` holds plain `lcm.regime.Regime` instances, finalized at
model build (model-level slots merged, default `H` injected, completeness
validated).

### Per-target parameters

- Per-target transition parameters nest under the target regime's name in the
params template — `template[regime][target][func][param]` — replacing the
`to_<target>_…` spelling. Param qnames parallel engine function qnames.

- Parameters resolve at four levels, most to least specific: target / function
(one value broadcasts over the law's targets) / regime / model. Exactly one
level per parameter; multi-level specifications are ambiguity errors.

- Canonical flat params always key transition-law params per target, every
target of a broadcast value sharing one leaf object. A coarse regime
transition is evaluated once and shared, so it takes no per-target
parameters.

- Model-level `derived_categoricals` follow the exactly-one-level rule of the
other model-level slots: a name declared at model level and regime level is
an ambiguity error, also when the grids match.

- Adds the DC-EGM solver (Iskhakov, Jørgensen, Rust & Schjerning 2017) as a
per-regime alternative to grid search: `Regime(solver=lcm.DCEGM(...))`.
Euler-equation inversion on an exogenous savings grid with a fast
upper-envelope scan (Dobrescu & Shanker 2022) — no consumption grid enters
the solve, and the credit-constrained segment is exact. Requires declared
`resources`, post-decision, and `inverse_marginal_utility` regime functions;
the model contract is validated at `Model` construction. Supports discrete
states and actions, EV1 taste shocks, stochastic processes, and passive
continuous states. Forward simulation works with grid-restricted consumption
(the intrinsic budget constraint is applied as a feasibility mask).

- Adds regime-level EV1 taste shocks as a model property:
`Regime(taste_shocks=lcm.ExtremeValueTasteShocks())` with the scale as the
runtime param `{"taste_shocks": {"scale": ...}}`. The solve aggregates
discrete actions by the smoothed expected maximum and simulation draws the
discrete action by Gumbel-max — identical solutions under either solver.

- Promotes the Iskhakov et al. (2017) retirement model to
`lcm_examples.iskhakov_et_al_2017` (brute-force and DC-EGM variants) with an
explanation notebook comparing the two solvers.

## 0.0.1

Expand Down
18 changes: 18 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
PyLCM
Copyright (c) 2023- The PyLCM Authors

This product includes software adapted from third-party open-source projects.
Their copyright notices and license terms are reproduced in the `licenses/`
directory.

--------------------------------------------------------------------------------
OpenSourceEconomics/upper-envelope
https://github.com/OpenSourceEconomics/upper-envelope

Copyright (c) 2023-2025 The Upper-Envelope Authors
Licensed under the Apache License, Version 2.0
(full text in `licenses/upper-envelope-LICENSE`).

The Fast Upper-Envelope Scan in `src/_lcm/egm/upper_envelope/fues.py` was adapted
from this package and has since been substantially modified.
--------------------------------------------------------------------------------
Loading
Loading