Conversation
calculate_add and calculate_divide stored their result at the requested period, where every later plain calculate of that variable and period found it. For a STOCK variable that is not its plain value (last month of the year, or the year's value for a month), so a monthly STOCK read 120 instead of 10 after an ADD and a yearly one 1 instead of 12 after a DIVIDE. The same happened over several periods of a variable's own unit (a plain read raises), for day variables, for integer or boolean FLOW results the cache truncates, and over an input stored at that period. Results are now cached only for the case _calculate itself routes to these options (a FLOW variable over a period of another unit), when the result has the variable's dtype and nothing is stored there yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 2, 2026
Draft
This branch has not been deployed
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.
Summary
calculate_addandcalculate_dividestored their result at the requested period, and every later plaincalculateof that variable and period returned it. That is the right value for a FLOW variable (its plain value over a period of another unit is exactly that sum or twelfth), but not elsewhere. So a plain read returned a different value depending on whether the option had run first:calculate(var, "2012")aftercalculate_add(var, "2012")calculate(var, "2012-06")aftercalculate_divide(var, "2012-06")calculate(var, "year:2012:2")aftercalculate_addover itcalculate(var, "2012-01")aftercalculate_addover the monthset_input = None):calculate(var, "2012")aftercalculate_addcalculate(var, "2012-01")Found while reviewing #562 (soundness review, finding 5).
The rule
Simulation._cache_option_resultstores an ADD or DIVIDE result only where_calculateitself routes a plain read to that option (a FLOW variable read over a period of another unit), only when the result already has the variable's dtype, and never over a value a plain read already finds there. Everywhere else the option result is returned but not cached._calculate's routing is unchanged, so FLOW reads keep their cache.Invariants
calculate,calculate_addandcalculate_dividerequests, a request returns the same values (or raises the same error) as in a new simulation with the same inputs. Property test:tests/core/test_option_result_cache_property.py(Hypothesis, 400 examples over MONTH/YEAR, STOCK/FLOW, float/int/bool, with and without formulas andset_input; auto-carry-over off, because carrying over is a separate order dependence fixed by Carry over only inputs, the latest at or before the requested period #562). It fails on master within seconds.Downstream impact
Real runs on master b78b0ba and on all three of #571/#572/#573 merged together (scratch merge 37aadada), each output compared array by array (
compare.py). The PE-US 2026 run was also repeated on each branch alone, and the 2035 run on #572 alone; all identical too.Neither policyengine-us nor policyengine-uk formulas use the ADD or DIVIDE options (
git grepon main), so nothing they calculate goes through the changed caching.Tests
tests/core/test_option_result_cache.py: 12 regressions; 10 fail on master, and the other 2 pin the FLOW caching that is kept.tests/core/test_option_result_cache_property.py: the property above, behindpytest.importorskip("hypothesis")for the smoke job.Composition
Touches
calculate_add/calculate_divide, which #562 also edits (it addsderived=Trueand skips single-sub-period sums). The conflict is textual: keep #562'sderived=Trueinside_cache_option_result. The rule here already never caches a single sub-period's sum.axiom: n/a: core engine caching, no policy encoding
🤖 Generated with Claude Code