Repository navigation
Measure a branch's behavioural responses against baseline policy, with its own cache - #9913
Conversation
…h its own cache
A branch of a reform simulation measured its labour-supply and capital-gains
responses against branch.get_branch("baseline"), a new branch under the
reform policy, so the response came out zero. Build the baseline measurement
as a branch of the simulation itself under a private copy of the baseline
policy, keeping only its inputs.
Core's clone copies the instance dictionary by reference, so clones and
branches shared one measurements dictionary. A clone now starts empty, a new
branch starts from a copy of its parent's, and the dictionary is dropped with
the rest of the cached formula output.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…xact inputs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9913 +/- ##
===========================================
- Coverage 100.00% 98.61% -1.39%
===========================================
Files 4 2 -2
Lines 76 144 +68
Branches 2 7 +5
===========================================
+ Hits 76 142 +66
- Misses 0 2 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Context from the session that opened this PR, for the US + core hub, which owns it now. I won't push to the branch. My uncommitted round-1 edits match 23e7661 byte for byte, so nothing in my worktree is lost. Everything below is in Round-1 review.
Nit responses, never sent to the reviewer:
Round-2 brief. Tests on main. Local timings on a host at load 200–450, an upper bound: property test 496 s, cross-period test 201 s, cheap tests 25–50 s each. Peak memory was 6.1 GB. Impact harnesses (not yet run to completion).
Repro evidence. |
|
US + core hub merge audit, us#9913 at
|
Summary
Measure a reform simulation's branches against baseline policy applied to their own inputs. Give each simulation its own behavioral-response measurement dictionary, clear it on direct cloning and cache invalidation, and let new perturbation branches inherit an independent copy of their household's measurements.
Previously an early branch could measure reform policy against itself and report zero response. A calculated clone could reuse another household's measurements, and measurements for another period could leak into its parent.
Round-1 findings
baseline is None. Regressions cover both a no-reform household and a reform simulation's own baseline, including all five elasticity/change helpers returning zero.hub-evidence/main_r1.jsonandfix_r1.json. Those historical executions used the prior household input bookkeeping; Round 3 separately fixes records left behind when the original arrays are deleted.Main merge and Core integration
Plain-merged main at
a844fc1717e8632bc92a9b0d7c9d41dda5359fdc.spm.pymerged cleanly: retain #9994 (09d7d9d0b8)'s rebuilt-baseline reset, incompatible-holder pruning, policy rebinding, tracing isolation and county receipts alongside this PR's_invalidate_all_caches,cloneandget_branchoverrides. These serve complementary purposes: policy ownership and holder suitability versus measurement ownership and lifetime.The textual conflict in
capital_gains_responses.pyretains main'suser_set_variableshandling and this PR's income-before-branch ordering. Baseline measurement branches also prune holders whose entity or ETERNITY storage cannot serve baseline policy before rebinding; structural-reform regressions cover both differences.Current fetched main's
uv.lockpins Core 3.32.8, with a>=3.32.8project requirement. Round 3 leaves that lock unchanged. The complete behavioral-response copies regression file passes on both 3.32.8 and 3.33.1, including the fully-calculated-clone regression, perturbed measured-branch property, and input-provenance regression. The remaining measurements and SPM checks are tracked below. #10069's SPM property remains intact: after subsampling, recalculate reform-only variables because 3.33.1 rebuilds from recorded inputs rather than exporting calculated values as inputs.Round-3 findings and fixes
Core 3.32.8 deletes a holder's arrays without forgetting their records in
_user_input_keys. After this package moves the household's original behavioral inputs to their pre-response slots, those stale original-name records can make a fully calculated clone treat inherited post-response earnings as input. In the regression, calculate household net income at $50,000, clone, clear inherited formula values, and setemployment_income_before_lsrto $100,000: obsolete earnings enter both measurement arms, so the clone disagrees with a fresh $100,000 household.Fix the provenance at migration in both
SimulationandMicrosimulation: when dropping the original holder's arrays, remove records for the exact storage keys that were dropped. Detach the input-record set before pruning so the already-created baseline retains its original raw inputs. Keep the migrated pre-response records and later explicit overrides, including original-name overrides for another period. This handles Core 3.32.8 and 3.33.1 without relying on the newer Core deletion behavior or bumpinguv.lock.The saved fully-calculated-clone regression is included. A provenance regression covers all five migrated inputs—employment income, self-employment income, SSTB self-employment income, weekly hours, and long-term capital gains—and checks baseline preservation plus a clone's deliberate later-period employment-income override surviving formula-value cleanup without reaching its source.
The Hypothesis copy property now exercises an explicit branch created after measurement and then changed from $50,000 to $100,000, rather than replacing its perturbed wages with the source wages. Under d1021, each cached measurement array must equal its original household snapshot, and the response must scale with the branch's new wages using those fixed measurements and this reform's uniform elasticities. Direct clones and branches created before measurement still use the fresh-household oracle.
d1021 and landing order
d1021 was RULED on 2026-10-08: keep reuse. MTR and perturbation branches hold response measurements at the household's measured values. New branches copy the dictionary; later measurements stay private. Direct clones measure afresh. The ruling states “#9698 and #9913 match.” #9698 is hub-owned and unchanged here; whichever of #9698 and #9913 lands second must rebase the overlapping simulation/behavioral-response files and rerun the checks.
Invariants and Python coverage
Python coverage is required because YAML cannot exercise clone/branch APIs, cache ownership, calculation order or storage rebinding. The new copies file belongs to the existing
coreRest Python group, measurements toheavy-b, and the SPM tests to their existing separate process on the Microsimulation runner. No jobs or concurrent heavy processes are added. These local macOS resource measurements include process setup and are not Linux CI measurements.Tests
All runs below completed. Local macOS elapsed times include process setup; peak memory is reported per process and is not a Linux CI measurement.
7bb51930ad5a0510f303d2a88b0561aa7bab0958, Core 3.32.804be263a411c77a032b79eabfb8ee285bc13b93e, Core 3.32.8test_behavioral_response_measurement_copies.pyat04be263a411c77a032b79eabfb8ee285bc13b93e, Core 3.32.8, including the fully-calculated-clone regressiontest_behavioral_response_measurement_copies.pyat04be263a411c77a032b79eabfb8ee285bc13b93e, Core 3.33.1, including the fully-calculated-clone regressiontest_behavioral_response_measurements.py, Core 3.32.8test_spm_system.py, Core 3.32.8The unfixed clone returned a labor-supply response of 631.1710815429688, while a fresh $100,000 household returned -52.118892669677734. This reproduces the stale-earnings failure on the locked Core version. The selected saved regression now passes on Core 3.32.8 at the fixed head; its resource record is preserved in
hub-evidence/regression-after-core-3.32.8.json.The completed Core 3.32.8 full copies run validates all 16 cases, including the saved clone regression, input-provenance regression, and perturbed measured-branch property. Its resource record is preserved in
hub-evidence/copies-after-core-3.32.8-standard.json. The Core 3.33.1 full copies run also passes all 16 cases at the same fixed head; its resource record ishub-evidence/copies-after-core-3.33.1.json. The Core 3.32.8 measurements file passes all 15 cases; its resource record ishub-evidence/measurements-core-3.32.8.json. The Core 3.32.8 SPM file passes all 76 cases. All completed after-fix checks use the same fixed head.Two earlier full copies attempts had the optional
faulthandler_timeout=300diagnostic setting enabled and were interrupted with incomplete diagnostic dumps, after two passes and no completed tests respectively. Those partial attempts are distinct from the completed standardfaulthandler_timeout=0run above.Tests run directly in the foreground, one file at a time, with
OMP_NUM_THREADS,OPENBLAS_NUM_THREADS,MKL_NUM_THREADS,VECLIB_MAXIMUM_THREADS, andNUMEXPR_NUM_THREADScapped at 4 andOMP_WAIT_POLICY=PASSIVE. Hypothesis remains enabled; no tests or strategies are disabled. No YAML files are changed relative to merged main.make formatand its linter passed before each Round-3 commit. Failing-regression evidence stays outside Git inhub-evidence/; earlier interrupted attempts and mutation-run records are historical evidence, rather than completed validation of this head.axiom: n/a: simulation machinery for behavioral-response measurement, cloning and cache isolation; no policy rule or legal provision changes.
Hub microsimulation impact (current head)
A real microsimulation on the default dataset, main at the merge base
a844fc1717against this PR's head04be263a41, for 2026. The runs went one at a time under the shared heavy-job lock. Sums are weighted.employment_incomeself_employment_incomelong_term_capital_gainsweekly_hours_workedlabor_supply_behavioral_responsestate_income_taxincome_taxhousehold_net_incomehousehold_benefitsAll measured outputs are unchanged: behavioural responses are off by default, and this PR changes how measurements are kept and cloned, not any default calculation.
Comparison files are in
fixes/9913-impact/on the hub host.