Gap
src/reforms/mod.rs currently implements reforms as YAML parameter overlays. The recently-added interfaces/python/policyengine_uk_compiled/structural.py pre/post hook (#23) is a workaround that mutates inputs/outputs around the Rust binary — not a true structural reform.
Python's Reform system supports:
- Callable transforms that can rewrite formulas
- Variable neutralisation (zero out a variable everywhere)
- Replacing a variable's class with a new implementation
- Composing multiple reforms
- Both parametric (parameter overrides) and structural (formula changes)
Examples to study:
policyengine_uk/reforms/conservatives/household_based_hitc.py — replaces a variable's calculation
policyengine_uk/reforms/policyengine/disable_simulated_benefits.py — neutralises variables
policyengine_uk/reforms/policyengine/adjust_budgets.py — composite reform
policyengine_uk/reforms/conservatives/, policyengine_uk/reforms/cps/, policyengine_uk/reforms/scotland/
Why it matters
Almost every PolicyEngine reform notebook (rail subsidy, salary-sacrifice cap, NICs reform, energy VAT, etc.) requires structural reforms — at minimum, swapping in a new formula for an existing variable. The Rust port cannot reproduce these analyses with parameter overrides alone.
Proposal
Design a Rust trait + registry where a reform can:
- Override parameters (current behaviour — keep)
- Replace a
compute_* function with a custom implementation
- Neutralise (set output to zero) a named variable
- Compose other reforms
Expose this through the Python wrapper so reform authors can subclass in Python and have it dispatch into the Rust engine.
Effort
Large. This is foundational and unlocks issues for CGT reforms, salary-sacrifice cap, and most other notebooks.
References
- Python:
policyengine_uk/reforms/, policyengine_uk/system.py, policyengine_uk/tax_benefit_system.py
- Rust:
src/reforms/mod.rs, interfaces/python/policyengine_uk_compiled/structural.py
Gap
src/reforms/mod.rscurrently implements reforms as YAML parameter overlays. The recently-addedinterfaces/python/policyengine_uk_compiled/structural.pypre/post hook (#23) is a workaround that mutates inputs/outputs around the Rust binary — not a true structural reform.Python's
Reformsystem supports:Examples to study:
policyengine_uk/reforms/conservatives/household_based_hitc.py— replaces a variable's calculationpolicyengine_uk/reforms/policyengine/disable_simulated_benefits.py— neutralises variablespolicyengine_uk/reforms/policyengine/adjust_budgets.py— composite reformpolicyengine_uk/reforms/conservatives/,policyengine_uk/reforms/cps/,policyengine_uk/reforms/scotland/Why it matters
Almost every PolicyEngine reform notebook (rail subsidy, salary-sacrifice cap, NICs reform, energy VAT, etc.) requires structural reforms — at minimum, swapping in a new formula for an existing variable. The Rust port cannot reproduce these analyses with parameter overrides alone.
Proposal
Design a Rust trait + registry where a reform can:
compute_*function with a custom implementationExpose this through the Python wrapper so reform authors can subclass in Python and have it dispatch into the Rust engine.
Effort
Large. This is foundational and unlocks issues for CGT reforms, salary-sacrifice cap, and most other notebooks.
References
policyengine_uk/reforms/,policyengine_uk/system.py,policyengine_uk/tax_benefit_system.pysrc/reforms/mod.rs,interfaces/python/policyengine_uk_compiled/structural.py