| title | Reforms |
|---|
A reform is a change to the rules used in a calculation. PolicyEngine supports two kinds: parametric (adjust a parameter value) and structural (swap or subclass a rule formula).
A dict of parameter path → new value. The same shape works for calculate_household (reform=) and Simulation (policy=).
reform = {
"gov.irs.credits.ctc.amount.adult_dependent": 1_000,
}
pe.us.calculate_household(..., reform=reform)Scalar values are treated as effective January 1 of the simulation year and onward.
reform = {
"gov.irs.credits.ctc.amount.adult_dependent": {
"2026-01-01": 1_000,
"2028-01-01": 2_000,
},
}Dates that haven't passed become "from this date onward." Earlier dates replace the baseline schedule.
Any number of paths in the same dict compose into one reform:
reform = {
"gov.irs.credits.ctc.amount.adult_dependent": 1_000,
"gov.irs.credits.eitc.phase_out.rate[0]": 0.08,
"gov.states.ca.tax.income.credits.eitc.max_amount": 500,
}Scale parameters (brackets with thresholds and amounts) are addressed by bracket index:
reform = {
"gov.irs.income.tax.rate[0]": 0.12, # first bracket rate
"gov.irs.income.tax.threshold[1]": 50_000, # second bracket threshold
"gov.irs.credits.ctc.amount.base[0].amount": 3_000,
}Every parameter has a canonical path matching the YAML directory structure in the country model. gov.irs.credits.ctc.amount.adult_dependent corresponds to policyengine_us/parameters/gov/irs/credits/ctc/amount/adult_dependent.yaml.
An auto-generated parameter reference is a planned addition; for now, browse the YAML tree in the country-model repository or rely on the error-message suggestions — an unknown path raises with the closest match.
Same dict, different keyword name:
from policyengine.core import Simulation
baseline = Simulation(
dataset=dataset,
tax_benefit_model_version=pe.us.model,
)
reformed = Simulation(
dataset=dataset,
tax_benefit_model_version=pe.us.model,
policy={"gov.irs.credits.ctc.amount.base[0].amount": 3_000},
)For rule changes that can't be expressed as a parameter change — swapping a formula, adding a variable, neutralising a program — drop down to the underlying country package. Both policyengine_us and policyengine_uk expose Reform.from_dict(...) and class-based reforms with overridable formulas; use them directly and run the simulation via managed_microsimulation (or by constructing a country-package Microsimulation yourself).
from policyengine_us import Microsimulation
from policyengine_us.model_api import Reform
class NeutralizeEITC(Reform):
def apply(self):
self.neutralize_variable("eitc")
sim = Microsimulation(reform=NeutralizeEITC)The current policyengine.py surface (Simulation, calculate_household) accepts parametric reforms only.
The parameter catalog is known at import time. Wrong paths raise before the simulation starts, with the closest-matching path.
For time-varying reforms, effective dates are checked against the parameter's defined start and end. A date before the parameter existed raises.
- Impact analysis — baseline-vs-reform with population estimates
- Examples — runnable scripts for reform scenarios in
examples/