Reforms#

The policyengine_core.reforms module contains the definition for Reform, a class which can be used to edit a TaxBenefitSystem instance.

Reform#

class policyengine_core.reforms.Reform(baseline: TaxBenefitSystem)#

A modified TaxBenefitSystem

All reforms must subclass Reform and implement a method apply().

In this method, the reform can add or replace variables and call modify_parameters to modify the parameters of the legislation.

Example:

>>> from policyengine_core import reforms
>>> from policyengine_core.parameters import load_parameter_file
>>>
>>> def modify_my_parameters(parameters):
>>>     # Add new parameters
>>>     new_parameters = load_parameter_file(name='reform_name', file_path='path_to_yaml_file.yaml')
>>>     parameters.add_child('reform_name', new_parameters)
>>>
>>>     # Update a value
>>>     parameters.taxes.some_tax.some_param.update(period=some_period, value=1000.0)
>>>
>>>    return parameters
>>>
>>> class MyReform(reforms.Reform):
>>>    def apply(self):
>>>        self.add_variable(some_variable)
>>>        self.update_variable(some_other_variable)
>>>        self.modify_parameters(modifier_function = modify_my_parameters)
static from_dict(parameter_values: dict, country_id: str = None, name: str = None) → Reform#

Create a reform from a dictionary of parameters.

Parameters:

parameter_values – A mapping of path -> {period_key: value} (or the path -> scalar shorthand, applied across year:2000:100).

Period-key formats, interpreted per parameter (see the module-level _*_update_kwargs helpers):

  • Bare ISO instant ("2026" / "2026-01" / "2026-01-01"): the value applies from that instant onward, flattening any later scheduled breakpoints (e.g. uprating). The instant is literal: {"2026-06-01": v} applies from 1 June 2026, so an annual value read at 1 January 2026 is still the baseline — use "2026-01-01" for a full-year-onward change. To keep later breakpoints, bound the change with a range instead.

  • Range "start.stop" (e.g. "2026-01-01.2027-12-31"): the value applies over the bounded interval [start, stop]; the prior value is restored afterwards.

  • Compound bounded period ("year:2026:5" / "month:2026-01:3"): the value applies over that bounded period only.

  • "ETERNITY": the value applies for all time.

Entries are applied in ascending start-instant order, so a later-starting key never clobbers an earlier one. Entries sharing a start instant are applied in their given order (the last wins for any overlap). Non-string keys are coerced to str.

Invalid input raises during construction before any parameter is modified: a malformed period key or bad value yields no partial reform, and errors from Parameter.update propagate rather than being silently swallowed.

Returns:

A reform.

modify_parameters(modifier_function: Callable[[ParameterNode], ParameterNode]) → None#

Make modifications on the parameters of the legislation.

Call this function in apply() if the reform asks for legislation parameter modifications.

Parameters:

modifier_function – A function that takes a ParameterNode and should return an object of the same type.

preprocess_situation(situation: dict, default_period=None) → dict#

Normalize entity-form input before the builder creates populations.

Country models can override this to fill missing entity definitions. SimulationBuilder.build_from_entities passes a deep copy of the caller’s situation, with any axes still present, and its default period as set (None, a string, an integer or a Period). Through build_from_dict and Simulation(situation=...), singular entity aliases are already expanded; a direct build_from_entities call passes the caller’s keys as given. The hook runs before the builder validates entities, so it may receive a situation with no persons. Return a dict holding an entity-form situation for the builder’s usual validation. A Reform runs its country system’s hook with the reform as the system, so the hook sees reformed parameters and variables. Only the country system’s hook runs: a Reform subclass that overrides this method is skipped when another reform wraps it, and a hook set on the country system instance itself stays bound to that instance, so it reads the baseline’s values. Dataset and variable-only inputs do not use this hook.