policyengine-uk 2.43.2__py3-none-any.whl → 2.43.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of policyengine-uk might be problematic. Click here for more details.

@@ -0,0 +1,83 @@
1
+ # Standard library imports
2
+ from typing import List, Optional
3
+
4
+ # Third-party imports
5
+ import numpy as np
6
+ from microdf import MicroDataFrame, MicroSeries
7
+
8
+ # PolicyEngine core imports
9
+ from policyengine_core.tracers import SimpleTracer
10
+
11
+ from .simulation import Simulation
12
+
13
+
14
+ class Microsimulation(Simulation):
15
+ """Extended simulation class with weighting support for microsimulation.
16
+
17
+ Provides weighted calculations using survey weights for population-level
18
+ estimates and statistics.
19
+ """
20
+
21
+ def get_weights(
22
+ self, variable_name: str, period: str, map_to: Optional[str] = None
23
+ ) -> np.ndarray:
24
+ """Get weights for the specified variable's entity.
25
+
26
+ Args:
27
+ variable_name: Name of the variable to get weights for
28
+ period: Time period for the weights
29
+ map_to: Optional entity key to map weights to
30
+
31
+ Returns:
32
+ Array of weights for the entity
33
+ """
34
+ variable = self.tax_benefit_system.get_variable(variable_name)
35
+ entity_key = map_to or variable.entity.key
36
+ weight_variable_name = f"{entity_key}_weight"
37
+ return self.calculate(
38
+ weight_variable_name, period, map_to=map_to, unweighted=True
39
+ )
40
+
41
+ def calculate(
42
+ self,
43
+ variable_name: str,
44
+ period: str = None,
45
+ map_to: str = None,
46
+ decode_enums: bool = False,
47
+ unweighted: bool = False,
48
+ ):
49
+ tracer: SimpleTracer = self.tracer
50
+
51
+ result = super().calculate(
52
+ variable_name, period, map_to=map_to, decode_enums=decode_enums
53
+ )
54
+
55
+ if not unweighted and len(tracer.stack) == 0:
56
+ weights = self.get_weights(variable_name, period, map_to=map_to)
57
+ return MicroSeries(result, weights=weights)
58
+
59
+ return result
60
+
61
+ def calculate_dataframe(
62
+ self,
63
+ variable_names: List[str],
64
+ period: Optional[str] = None,
65
+ map_to: Optional[str] = None,
66
+ use_weights: bool = True,
67
+ ) -> MicroDataFrame:
68
+ """Calculate multiple variables as a weighted DataFrame.
69
+
70
+ Args:
71
+ variable_names: List of variable names to calculate
72
+ period: Time period for calculation
73
+ map_to: Optional entity key to map results to
74
+ use_weights: Whether to apply survey weights
75
+
76
+ Returns:
77
+ MicroDataFrame with calculated values and weights
78
+ """
79
+ values = super().calculate_dataframe(variable_names, period, map_to)
80
+ if not use_weights:
81
+ return values
82
+ weights = self.get_weights(variable_names[0], period, map_to=map_to)
83
+ return MicroDataFrame(values, weights=weights)
@@ -1,6 +1,7 @@
1
- description: Households in England or Wales with pensioners earning under this amount of yearly taxable income are eligible for the Winter Fuel Payment.
1
+ description: Households in England or Wales with pensioners earning under this amount of yearly taxable income are eligible for the Winter Fuel Payment.
2
2
  values:
3
3
  2000-01-01: 0
4
+ 2025-01-01: 35_000
4
5
  metadata:
5
6
  unit: currency-GBP
6
7
  label: Winter Fuel Payment maximum taxable income
@@ -1,6 +1,7 @@
1
1
  description: When this is true, households in England or Wales with pensioners earning under the maximum taxable income are eligible for the Winter Fuel Payment.
2
2
  values:
3
3
  2000-01-01: false
4
+ 2025-01-01: true
4
5
  metadata:
5
6
  unit: bool
6
7
  label: WFA maximum taxable income eligibility
@@ -38,7 +38,7 @@ def create_economic_assumption_indices(
38
38
  start_year = int(descendant.values_list[-1].instant_str[:4])
39
39
  values = {start_year: 1.0}
40
40
 
41
- for year in range(start_year + 1, 2030):
41
+ for year in range(start_year + 1, 2040):
42
42
  yoy_growth = descendant(year)
43
43
  indices_value = round(
44
44
  values[year - 1] * (1 + yoy_growth),
@@ -0,0 +1,418 @@
1
+ # Standard library imports
2
+ from typing import Dict, Optional, Union, Type
3
+
4
+ # Third-party imports
5
+ import numpy as np
6
+ import pandas as pd
7
+
8
+ # PolicyEngine core imports
9
+ from policyengine_core.data import Dataset
10
+ from policyengine_core.periods import period as period_
11
+ from policyengine_core.parameters import Parameter
12
+ from policyengine_core.reforms import Reform
13
+ from policyengine_core.simulations import Simulation as CoreSimulation
14
+ from policyengine_core.tools.hugging_face import download_huggingface_dataset
15
+ from policyengine_core.tracers import FullTracer, SimpleTracer
16
+
17
+ # PolicyEngine UK imports
18
+ from policyengine_uk.data.dataset_schema import (
19
+ UKMultiYearDataset,
20
+ UKSingleYearDataset,
21
+ )
22
+ from policyengine_uk.utils.scenario import Scenario
23
+ from policyengine_uk.data.economic_assumptions import (
24
+ extend_single_year_dataset,
25
+ )
26
+
27
+ from .tax_benefit_system import CountryTaxBenefitSystem
28
+
29
+
30
+ class Simulation(CoreSimulation):
31
+ """UK-specific simulation class for calculating tax and benefit outcomes.
32
+
33
+ Extends the core simulation functionality with UK-specific features
34
+ and data handling capabilities.
35
+ """
36
+
37
+ default_input_period: int = 2025
38
+ default_calculation_period: int = 2025
39
+
40
+ def __init__(
41
+ self,
42
+ scenario: Optional[Scenario] = None,
43
+ situation: Optional[Dict] = None,
44
+ dataset: Optional[
45
+ Union[pd.DataFrame, str, UKSingleYearDataset, UKMultiYearDataset]
46
+ ] = None,
47
+ trace: bool = False,
48
+ reform: Dict | Type[Reform] = None,
49
+ ):
50
+ """Initialize a UK simulation.
51
+
52
+ Args:
53
+ scenario: A Scenario object defining a modification to the simulation
54
+ situation: A dictionary describing the situation to simulate
55
+ dataset: Data source - can be DataFrame, URL string, or Dataset object
56
+ trace: Whether to enable detailed tracing of calculations
57
+ """
58
+ # Initialize tax-benefit rules
59
+ self.tax_benefit_system = CountryTaxBenefitSystem()
60
+
61
+ # Migrate Reform to Scenario
62
+
63
+ if reform is not None:
64
+ scenario = Scenario.from_reform(reform)
65
+
66
+ # Apply parametric reforms here
67
+
68
+ if scenario is not None:
69
+ if scenario.parameter_changes is not None:
70
+ self.apply_parameter_changes(scenario.parameter_changes)
71
+
72
+ self.branch_name = "default"
73
+ self.invalidated_caches = set()
74
+ self.debug: bool = False
75
+ self.trace: bool = trace
76
+ self.tracer: SimpleTracer = (
77
+ SimpleTracer() if not trace else FullTracer()
78
+ )
79
+ self.opt_out_cache: bool = False
80
+ self.max_spiral_loops: int = 10
81
+ self.memory_config = None
82
+ self._data_storage_dir: Optional[str] = None
83
+
84
+ self.branches: Dict[str, Simulation] = {}
85
+
86
+ # Build simulation from appropriate source
87
+ if situation is not None:
88
+ self.build_from_situation(situation)
89
+ elif isinstance(dataset, str):
90
+ self.build_from_url(dataset)
91
+ elif isinstance(dataset, pd.DataFrame):
92
+ self.build_from_dataframe(dataset)
93
+ elif isinstance(dataset, Dataset):
94
+ self.build_from_dataset(dataset)
95
+ elif isinstance(dataset, UKSingleYearDataset):
96
+ self.build_from_single_year_dataset(dataset)
97
+ elif isinstance(dataset, UKMultiYearDataset):
98
+ self.build_from_multi_year_dataset(dataset)
99
+ elif dataset is None:
100
+ self.build_from_url(
101
+ "hf://policyengine/policyengine-uk-data/enhanced_frs_2023_24.h5"
102
+ )
103
+ else:
104
+ raise ValueError(f"Unsupported dataset type: {dataset.__class__}")
105
+
106
+ # Handle behavioral responses for earnings and capital gains
107
+ self.move_values("employment_income", "employment_income_before_lsr")
108
+ self.move_values("capital_gains", "capital_gains_before_response")
109
+
110
+ self.input_variables = self.get_known_variables()
111
+
112
+ # Apply structural modifiers
113
+
114
+ if scenario is not None:
115
+ if scenario.simulation_modifier is not None:
116
+ scenario.simulation_modifier(self)
117
+
118
+ def get_known_variables(self):
119
+ variables = []
120
+ for variable in self.tax_benefit_system.variables:
121
+ if len(self.get_holder(variable).get_known_periods()) > 0:
122
+ variables.append(variable)
123
+ return variables
124
+
125
+ def apply_parameter_changes(self, changes: dict):
126
+ self.tax_benefit_system.reset_parameters()
127
+
128
+ for parameter in changes:
129
+ p: Parameter = self.tax_benefit_system.parameters.get_child(
130
+ parameter
131
+ )
132
+ if isinstance(changes[parameter], dict):
133
+ # Time-period specific changes
134
+ for time_period in changes[parameter]:
135
+ p.update(
136
+ period=time_period,
137
+ value=changes[parameter][time_period],
138
+ )
139
+ else:
140
+ p.update(period="year:2000:100", value=changes[parameter])
141
+
142
+ self.tax_benefit_system.process_parameters()
143
+
144
+ def build_from_situation(self, situation: Dict) -> None:
145
+ """Build simulation from a situation dictionary.
146
+
147
+ Args:
148
+ situation: Dictionary describing household composition and characteristics
149
+ """
150
+ self.build_from_populations(
151
+ self.tax_benefit_system.instantiate_entities()
152
+ )
153
+ from policyengine_core.simulations.simulation_builder import (
154
+ SimulationBuilder,
155
+ ) # Import here to avoid circular dependency
156
+
157
+ builder = SimulationBuilder()
158
+ builder.default_period = self.default_input_period
159
+ builder.build_from_dict(self.tax_benefit_system, situation, self)
160
+ self.has_axes = builder.has_axes
161
+
162
+ def build_from_url(self, url: str) -> None:
163
+ """Build simulation from a HuggingFace dataset URL.
164
+
165
+ Args:
166
+ url: HuggingFace URL in format "hf://owner/repo/filename"
167
+
168
+ Raises:
169
+ ValueError: If URL is not a HuggingFace URL
170
+ """
171
+ if "hf://" not in url:
172
+ raise ValueError(
173
+ f"Non-HuggingFace URLs are currently not supported."
174
+ )
175
+
176
+ # Parse HuggingFace URL components
177
+ owner, repo, filename = url.split("/")[-3:]
178
+ if "@" in filename:
179
+ version = filename.split("@")[-1]
180
+ filename = filename.split("@")[0]
181
+ else:
182
+ version = None
183
+
184
+ # Download dataset from HuggingFace
185
+ dataset = download_huggingface_dataset(
186
+ repo=f"{owner}/{repo}",
187
+ repo_filename=filename,
188
+ version=version,
189
+ )
190
+
191
+ # Determine dataset type and build accordingly
192
+ if UKMultiYearDataset.validate_file_path(dataset, False):
193
+ self.build_from_multi_year_dataset(UKMultiYearDataset(dataset))
194
+ self.dataset = dataset
195
+ elif UKSingleYearDataset.validate_file_path(dataset, False):
196
+ self.build_from_single_year_dataset(UKSingleYearDataset(dataset))
197
+ self.dataset = dataset
198
+ else:
199
+ dataset = Dataset.from_file(dataset, self.default_input_period)
200
+ self.build_from_dataset(dataset)
201
+
202
+ def build_from_dataframe(self, df: pd.DataFrame) -> None:
203
+ """Build simulation from a pandas DataFrame.
204
+
205
+ Args:
206
+ df: DataFrame with columns in format "variable_name__time_period"
207
+ """
208
+
209
+ def get_first_array(variable_name: str) -> pd.Series:
210
+ """Extract the first array for a given variable name pattern."""
211
+ columns = df.columns[df.columns.str.contains(variable_name + "__")]
212
+ return df[columns[0]]
213
+
214
+ # Extract ID columns
215
+ (
216
+ person_id,
217
+ person_benunit_id,
218
+ person_household_id,
219
+ benunit_id,
220
+ household_id,
221
+ ) = map(
222
+ get_first_array,
223
+ [
224
+ "person_id",
225
+ "person_benunit_id",
226
+ "person_household_id",
227
+ "benunit_id",
228
+ "household_id",
229
+ ],
230
+ )
231
+
232
+ # Build entity structure
233
+ self.build_from_ids(
234
+ person_id,
235
+ person_benunit_id,
236
+ person_household_id,
237
+ benunit_id,
238
+ household_id,
239
+ )
240
+
241
+ # Set input values for each variable and time period
242
+ for column in df:
243
+ variable, time_period = column.split("__")
244
+ if variable not in self.tax_benefit_system.variables:
245
+ continue
246
+ self.set_input(variable, time_period, df[column])
247
+
248
+ def build_from_dataset(self, dataset: Dataset) -> None:
249
+ """Build simulation from a Dataset object.
250
+
251
+ Args:
252
+ dataset: PolicyEngine Dataset object containing simulation data
253
+ """
254
+ data: Dict[str, Dict[str, Union[float, int, str]]] = (
255
+ dataset.load_dataset()
256
+ )
257
+
258
+ first_variable = data[list(data.keys())[0]]
259
+ first_time_period = list(first_variable.keys())[0]
260
+
261
+ def get_first_array(variable_name: str) -> np.ndarray:
262
+ """Get the first time period's values for a variable."""
263
+ time_period_values = data[variable_name]
264
+ return time_period_values[first_time_period]
265
+
266
+ # Build entity structure from IDs
267
+ self.build_from_ids(
268
+ *map(
269
+ get_first_array,
270
+ [
271
+ "person_id",
272
+ "person_benunit_id",
273
+ "person_household_id",
274
+ "benunit_id",
275
+ "household_id",
276
+ ],
277
+ )
278
+ )
279
+
280
+ # Load all variable values
281
+ for variable in data:
282
+ for time_period in data[variable]:
283
+ if variable not in self.tax_benefit_system.variables:
284
+ continue
285
+ self.set_input(
286
+ variable, time_period, data[variable][time_period]
287
+ )
288
+
289
+ # Now convert to the new UKSingleYearDataset
290
+ self.input_variables = self.get_known_variables()
291
+ self.dataset = dataset
292
+ dataset = UKSingleYearDataset.from_simulation(
293
+ self, fiscal_year=first_time_period
294
+ )
295
+ multi_year_dataset = extend_single_year_dataset(dataset)
296
+
297
+ self.build_from_multi_year_dataset(multi_year_dataset)
298
+ self.dataset = multi_year_dataset
299
+
300
+ def build_from_single_year_dataset(
301
+ self, dataset: UKSingleYearDataset
302
+ ) -> None:
303
+ """Build simulation from a single-year UK dataset.
304
+
305
+ Args:
306
+ dataset: UKSingleYearDataset containing one year of data
307
+ """
308
+
309
+ dataset = extend_single_year_dataset(dataset)
310
+ self.build_from_multi_year_dataset(dataset)
311
+
312
+ def build_from_multi_year_dataset(
313
+ self, dataset: UKMultiYearDataset
314
+ ) -> None:
315
+ """Build simulation from a multi-year UK dataset.
316
+
317
+ Args:
318
+ dataset: UKMultiYearDataset containing multiple years of data
319
+ """
320
+ # Use first year to establish entity structure
321
+ first_year = dataset[dataset.years[0]]
322
+ self.build_from_ids(
323
+ first_year.person.person_id,
324
+ first_year.person.person_benunit_id,
325
+ first_year.person.person_household_id,
326
+ first_year.benunit.benunit_id,
327
+ first_year.household.household_id,
328
+ )
329
+
330
+ # Load variable values for all years
331
+ for year in dataset.years:
332
+ for table in dataset[year].tables:
333
+ for variable in table.columns:
334
+ if variable not in self.tax_benefit_system.variables:
335
+ continue
336
+ self.set_input(variable, year, table[variable])
337
+
338
+ def build_from_ids(
339
+ self,
340
+ person_id: np.ndarray,
341
+ person_benunit_id: np.ndarray,
342
+ person_household_id: np.ndarray,
343
+ benunit_id: np.ndarray,
344
+ household_id: np.ndarray,
345
+ ) -> None:
346
+ """Build simulation entities from ID arrays.
347
+
348
+ Args:
349
+ person_id: Array of person IDs
350
+ person_benunit_id: Array mapping persons to benefit units
351
+ person_household_id: Array mapping persons to households
352
+ benunit_id: Array of benefit unit IDs
353
+ household_id: Array of household IDs
354
+ """
355
+ from policyengine_core.simulations.simulation_builder import (
356
+ SimulationBuilder,
357
+ ) # Import here to avoid circular dependency
358
+
359
+ builder = SimulationBuilder()
360
+ builder.populations = self.tax_benefit_system.instantiate_entities()
361
+
362
+ # Declare entities
363
+ builder.declare_person_entity("person", person_id)
364
+ builder.declare_entity("benunit", np.unique(benunit_id))
365
+ builder.declare_entity("household", np.unique(household_id))
366
+
367
+ # Link persons to benefit units and households
368
+ builder.join_with_persons(
369
+ builder.populations["benunit"],
370
+ person_benunit_id,
371
+ np.array(["member"] * len(person_benunit_id)),
372
+ )
373
+ builder.join_with_persons(
374
+ builder.populations["household"],
375
+ person_household_id,
376
+ np.array(["member"] * len(person_household_id)),
377
+ )
378
+
379
+ self.build_from_populations(builder.populations)
380
+
381
+ def move_values(self, variable_donor: str, variable_target: str) -> None:
382
+ """Move values from one variable to another across all branches.
383
+
384
+ Used for behavioral response modeling where original values need
385
+ to be preserved.
386
+
387
+ Args:
388
+ variable_donor: Variable to move values from
389
+ variable_target: Variable to move values to
390
+ """
391
+ for simulation in list(self.branches.values()) + [self]:
392
+ holder = simulation.get_holder(variable_donor)
393
+ for known_period in holder.get_known_periods():
394
+ array = holder.get_array(known_period)
395
+ simulation.set_input(variable_target, known_period, array)
396
+ holder.delete_arrays(known_period)
397
+
398
+ def calculate(
399
+ self,
400
+ variable_name: str,
401
+ period: str = None,
402
+ map_to: str = None,
403
+ decode_enums: bool = False,
404
+ ):
405
+ tracer: SimpleTracer = self.tracer
406
+ if len(tracer.stack) == 0:
407
+ # Only decode enums to string values when we're not within
408
+ # the simulation tree.
409
+ decode_enums = True
410
+
411
+ if period is None:
412
+ period = self.default_calculation_period
413
+
414
+ period = period_(period)
415
+
416
+ return super().calculate(
417
+ variable_name, period, map_to=map_to, decode_enums=decode_enums
418
+ )
policyengine_uk/system.py CHANGED
@@ -1,614 +1,3 @@
1
- # Standard library imports
2
- import copy
3
- from pathlib import Path
4
- from typing import Any, Dict, List, Optional, Union, Type
5
-
6
- # Third-party imports
7
- import numpy as np
8
- import pandas as pd
9
- from microdf import MicroDataFrame, MicroSeries
10
-
11
- # PolicyEngine core imports
12
- from policyengine_core.data import Dataset
13
- from policyengine_core.parameters.operations.propagate_parameter_metadata import (
14
- propagate_parameter_metadata,
15
- )
16
- from policyengine_core.periods import period as period_
17
- from policyengine_core.parameters.operations.uprate_parameters import (
18
- uprate_parameters,
19
- )
20
- from policyengine_core.parameters import Parameter
21
- from policyengine_core.reforms import Reform
22
- from policyengine_core.simulations import Simulation as CoreSimulation
23
- from policyengine_core.taxbenefitsystems import TaxBenefitSystem
24
- from policyengine_core.tools.hugging_face import download_huggingface_dataset
25
- from policyengine_core.tracers import FullTracer, SimpleTracer
26
-
27
- # PolicyEngine UK imports
28
- from policyengine_uk.data.dataset_schema import (
29
- UKMultiYearDataset,
30
- UKSingleYearDataset,
31
- )
32
- from policyengine_uk.entities import BenUnit, Household, Person
33
- from policyengine_uk.parameters.gov.contrib.create_private_pension_uprating import (
34
- add_private_pension_uprating_factor,
35
- )
36
- from policyengine_uk.parameters.gov.dwp.state_pension.triple_lock.create_triple_lock import (
37
- add_triple_lock,
38
- )
39
- from policyengine_uk.parameters.gov.economic_assumptions.create_economic_assumption_indices import (
40
- create_economic_assumption_indices,
41
- )
42
- from policyengine_uk.parameters.gov.economic_assumptions.lag_average_earnings import (
43
- add_lagged_earnings,
44
- )
45
- from policyengine_uk.parameters.gov.economic_assumptions.lag_cpi import (
46
- add_lagged_cpi,
47
- )
48
- from policyengine_uk.utils.parameters import (
49
- backdate_parameters,
50
- convert_to_fiscal_year_parameters,
51
- )
52
- from policyengine_uk.utils.scenario import Scenario
53
- from policyengine_uk.data.economic_assumptions import (
54
- apply_uprating,
55
- extend_single_year_dataset,
56
- )
57
-
58
- # Module constants
59
- COUNTRY_DIR = Path(__file__).parent
60
- ENHANCED_FRS = "hf://policyengine/policyengine-uk-data/enhanced_frs_2023_24.h5"
61
-
62
-
63
- class CountryTaxBenefitSystem(TaxBenefitSystem):
64
- """UK-specific tax and benefit system implementation.
65
-
66
- This class defines the UK tax-benefit system with all relevant
67
- variables, parameters, and entities (Person, BenUnit, Household).
68
- """
69
-
70
- basic_inputs: List[str] = [
71
- "brma",
72
- "local_authority",
73
- "region",
74
- "employment_income",
75
- "age",
76
- ]
77
- modelled_policies = COUNTRY_DIR / "modelled_policies.yaml"
78
- auto_carry_over_input_variables: bool = True
79
-
80
- def reset_parameters(self) -> None:
81
- """Reset parameters by reloading from the parameters directory."""
82
- self._parameters_at_instant_cache = {}
83
- self.load_parameters(self.parameters_dir)
84
-
85
- def process_parameters(self) -> None:
86
- """Process and transform parameters with UK-specific adjustments.
87
-
88
- Applies various parameter transformations including:
89
- - Private pension uprating factors
90
- - Lagged earnings and CPI indices
91
- - Triple lock calculations for state pensions
92
- - Economic assumption indices
93
- - Parameter uprating and backdating
94
- - Conversion to fiscal year parameters
95
- """
96
- self._parameters_at_instant_cache = {}
97
- # Add various UK-specific parameter adjustments
98
- self.parameters = add_private_pension_uprating_factor(self.parameters)
99
- self.parameters = add_lagged_earnings(self.parameters)
100
- self.parameters = add_lagged_cpi(self.parameters)
101
- self.parameters = add_triple_lock(self.parameters)
102
- self.parameters = create_economic_assumption_indices(self.parameters)
103
-
104
- # Create baseline parameters for reform comparisons
105
- self.parameters.add_child("baseline", self.parameters.clone())
106
-
107
- # Apply general parameter operations
108
- self.parameters = propagate_parameter_metadata(self.parameters)
109
- self.parameters = uprate_parameters(self.parameters)
110
- self.parameters = backdate_parameters(self.parameters, "2015-01-01")
111
- self.parameters.gov = convert_to_fiscal_year_parameters(
112
- self.parameters.gov
113
- )
114
-
115
- def __init__(self):
116
- """Initialize the UK tax-benefit system with entities and parameters."""
117
- self._parameters_at_instant_cache: Dict[str, Any] = {}
118
- self.variables: Dict[Any, Any] = {}
119
-
120
- # Create copies of entity classes to avoid modifying originals
121
- person, benunit, household = (
122
- copy.copy(Person),
123
- copy.copy(BenUnit),
124
- copy.copy(Household),
125
- )
126
-
127
- # Set up entities
128
- self.entities = [person, benunit, household]
129
- self.person_entity = person
130
- self.group_entities = [benunit, household]
131
- self.group_entity_keys = [entity.key for entity in self.group_entities]
132
-
133
- # Link entities to this tax-benefit system
134
- for entity in self.entities:
135
- entity.set_tax_benefit_system(self)
136
-
137
- self.variable_module_metadata = {}
138
-
139
- # Load all variables from the variables directory
140
- self.add_variables_from_directory(COUNTRY_DIR / "variables")
141
-
142
- # Set up and process parameters
143
- self.parameters_dir = COUNTRY_DIR / "parameters"
144
- self.reset_parameters()
145
- self.process_parameters()
146
-
147
-
148
- # Create system instance for module-level access
149
- system = CountryTaxBenefitSystem()
150
- parameters = system.parameters
151
- variables = system.variables
152
-
153
-
154
- class Simulation(CoreSimulation):
155
- """UK-specific simulation class for calculating tax and benefit outcomes.
156
-
157
- Extends the core simulation functionality with UK-specific features
158
- and data handling capabilities.
159
- """
160
-
161
- default_input_period: int = 2025
162
- default_calculation_period: int = 2025
163
-
164
- def __init__(
165
- self,
166
- scenario: Optional[Scenario] = None,
167
- situation: Optional[Dict] = None,
168
- dataset: Optional[
169
- Union[pd.DataFrame, str, UKSingleYearDataset, UKMultiYearDataset]
170
- ] = None,
171
- trace: bool = False,
172
- reform: Dict | Type[Reform] = None,
173
- ):
174
- """Initialize a UK simulation.
175
-
176
- Args:
177
- scenario: A Scenario object defining a modification to the simulation
178
- situation: A dictionary describing the situation to simulate
179
- dataset: Data source - can be DataFrame, URL string, or Dataset object
180
- trace: Whether to enable detailed tracing of calculations
181
- """
182
- # Initialize tax-benefit rules
183
- self.tax_benefit_system = CountryTaxBenefitSystem()
184
-
185
- # Migrate Reform to Scenario
186
-
187
- if reform is not None:
188
- scenario = Scenario.from_reform(reform)
189
-
190
- # Apply parametric reforms here
191
-
192
- if scenario is not None:
193
- if scenario.parameter_changes is not None:
194
- self.apply_parameter_changes(scenario.parameter_changes)
195
-
196
- self.branch_name = "default"
197
- self.invalidated_caches = set()
198
- self.debug: bool = False
199
- self.trace: bool = trace
200
- self.tracer: SimpleTracer = (
201
- SimpleTracer() if not trace else FullTracer()
202
- )
203
- self.opt_out_cache: bool = False
204
- self.max_spiral_loops: int = 10
205
- self.memory_config = None
206
- self._data_storage_dir: Optional[str] = None
207
-
208
- self.branches: Dict[str, Simulation] = {}
209
-
210
- # Build simulation from appropriate source
211
- if situation is not None:
212
- self.build_from_situation(situation)
213
- elif isinstance(dataset, str):
214
- self.build_from_url(dataset)
215
- elif isinstance(dataset, pd.DataFrame):
216
- self.build_from_dataframe(dataset)
217
- elif isinstance(dataset, Dataset):
218
- self.build_from_dataset(dataset)
219
- elif isinstance(dataset, UKSingleYearDataset):
220
- self.build_from_single_year_dataset(dataset)
221
- elif isinstance(dataset, UKMultiYearDataset):
222
- self.build_from_multi_year_dataset(dataset)
223
- elif dataset is None:
224
- self.build_from_url(
225
- "hf://policyengine/policyengine-uk-data/enhanced_frs_2023_24.h5"
226
- )
227
- else:
228
- raise ValueError(f"Unsupported dataset type: {dataset.__class__}")
229
-
230
- # Handle behavioral responses for earnings and capital gains
231
- self.move_values("employment_income", "employment_income_before_lsr")
232
- self.move_values("capital_gains", "capital_gains_before_response")
233
-
234
- self.input_variables = self.get_known_variables()
235
-
236
- # Apply structural modifiers
237
-
238
- if scenario is not None:
239
- if scenario.simulation_modifier is not None:
240
- scenario.simulation_modifier(self)
241
-
242
- def get_known_variables(self):
243
- variables = []
244
- for variable in self.tax_benefit_system.variables:
245
- if len(self.get_holder(variable).get_known_periods()) > 0:
246
- variables.append(variable)
247
- return variables
248
-
249
- def apply_parameter_changes(self, changes: dict):
250
- self.tax_benefit_system.reset_parameters()
251
-
252
- for parameter in changes:
253
- p: Parameter = self.tax_benefit_system.parameters.get_child(
254
- parameter
255
- )
256
- if isinstance(changes[parameter], dict):
257
- # Time-period specific changes
258
- for time_period in changes[parameter]:
259
- p.update(
260
- period=time_period,
261
- value=changes[parameter][time_period],
262
- )
263
- else:
264
- p.update(period="year:2000:100", value=changes[parameter])
265
-
266
- self.tax_benefit_system.process_parameters()
267
-
268
- def build_from_situation(self, situation: Dict) -> None:
269
- """Build simulation from a situation dictionary.
270
-
271
- Args:
272
- situation: Dictionary describing household composition and characteristics
273
- """
274
- self.build_from_populations(
275
- self.tax_benefit_system.instantiate_entities()
276
- )
277
- from policyengine_core.simulations.simulation_builder import (
278
- SimulationBuilder,
279
- ) # Import here to avoid circular dependency
280
-
281
- builder = SimulationBuilder()
282
- builder.default_period = self.default_input_period
283
- builder.build_from_dict(self.tax_benefit_system, situation, self)
284
- self.has_axes = builder.has_axes
285
-
286
- def build_from_url(self, url: str) -> None:
287
- """Build simulation from a HuggingFace dataset URL.
288
-
289
- Args:
290
- url: HuggingFace URL in format "hf://owner/repo/filename"
291
-
292
- Raises:
293
- ValueError: If URL is not a HuggingFace URL
294
- """
295
- if "hf://" not in url:
296
- raise ValueError(
297
- f"Non-HuggingFace URLs are currently not supported."
298
- )
299
-
300
- # Parse HuggingFace URL components
301
- owner, repo, filename = url.split("/")[-3:]
302
- if "@" in filename:
303
- version = filename.split("@")[-1]
304
- filename = filename.split("@")[0]
305
- else:
306
- version = None
307
-
308
- # Download dataset from HuggingFace
309
- dataset = download_huggingface_dataset(
310
- repo=f"{owner}/{repo}",
311
- repo_filename=filename,
312
- version=version,
313
- )
314
-
315
- # Determine dataset type and build accordingly
316
- if UKMultiYearDataset.validate_file_path(dataset, False):
317
- self.build_from_multi_year_dataset(UKMultiYearDataset(dataset))
318
- self.dataset = dataset
319
- elif UKSingleYearDataset.validate_file_path(dataset, False):
320
- self.build_from_single_year_dataset(UKSingleYearDataset(dataset))
321
- self.dataset = dataset
322
- else:
323
- dataset = Dataset.from_file(dataset, self.default_input_period)
324
- self.build_from_dataset(dataset)
325
-
326
- def build_from_dataframe(self, df: pd.DataFrame) -> None:
327
- """Build simulation from a pandas DataFrame.
328
-
329
- Args:
330
- df: DataFrame with columns in format "variable_name__time_period"
331
- """
332
-
333
- def get_first_array(variable_name: str) -> pd.Series:
334
- """Extract the first array for a given variable name pattern."""
335
- columns = df.columns[df.columns.str.contains(variable_name + "__")]
336
- return df[columns[0]]
337
-
338
- # Extract ID columns
339
- (
340
- person_id,
341
- person_benunit_id,
342
- person_household_id,
343
- benunit_id,
344
- household_id,
345
- ) = map(
346
- get_first_array,
347
- [
348
- "person_id",
349
- "person_benunit_id",
350
- "person_household_id",
351
- "benunit_id",
352
- "household_id",
353
- ],
354
- )
355
-
356
- # Build entity structure
357
- self.build_from_ids(
358
- person_id,
359
- person_benunit_id,
360
- person_household_id,
361
- benunit_id,
362
- household_id,
363
- )
364
-
365
- # Set input values for each variable and time period
366
- for column in df:
367
- variable, time_period = column.split("__")
368
- if variable not in self.tax_benefit_system.variables:
369
- continue
370
- self.set_input(variable, time_period, df[column])
371
-
372
- def build_from_dataset(self, dataset: Dataset) -> None:
373
- """Build simulation from a Dataset object.
374
-
375
- Args:
376
- dataset: PolicyEngine Dataset object containing simulation data
377
- """
378
- data: Dict[str, Dict[str, Union[float, int, str]]] = (
379
- dataset.load_dataset()
380
- )
381
-
382
- first_variable = data[list(data.keys())[0]]
383
- first_time_period = list(first_variable.keys())[0]
384
-
385
- def get_first_array(variable_name: str) -> np.ndarray:
386
- """Get the first time period's values for a variable."""
387
- time_period_values = data[variable_name]
388
- return time_period_values[first_time_period]
389
-
390
- # Build entity structure from IDs
391
- self.build_from_ids(
392
- *map(
393
- get_first_array,
394
- [
395
- "person_id",
396
- "person_benunit_id",
397
- "person_household_id",
398
- "benunit_id",
399
- "household_id",
400
- ],
401
- )
402
- )
403
-
404
- # Load all variable values
405
- for variable in data:
406
- for time_period in data[variable]:
407
- if variable not in self.tax_benefit_system.variables:
408
- continue
409
- self.set_input(
410
- variable, time_period, data[variable][time_period]
411
- )
412
-
413
- # Now convert to the new UKSingleYearDataset
414
- self.input_variables = self.get_known_variables()
415
- self.dataset = dataset
416
- dataset = UKSingleYearDataset.from_simulation(
417
- self, fiscal_year=first_time_period
418
- )
419
- multi_year_dataset = extend_single_year_dataset(dataset)
420
-
421
- self.build_from_multi_year_dataset(multi_year_dataset)
422
- self.dataset = multi_year_dataset
423
-
424
- def build_from_single_year_dataset(
425
- self, dataset: UKSingleYearDataset
426
- ) -> None:
427
- """Build simulation from a single-year UK dataset.
428
-
429
- Args:
430
- dataset: UKSingleYearDataset containing one year of data
431
- """
432
-
433
- dataset = extend_single_year_dataset(dataset)
434
- self.build_from_multi_year_dataset(dataset)
435
-
436
- def build_from_multi_year_dataset(
437
- self, dataset: UKMultiYearDataset
438
- ) -> None:
439
- """Build simulation from a multi-year UK dataset.
440
-
441
- Args:
442
- dataset: UKMultiYearDataset containing multiple years of data
443
- """
444
- # Use first year to establish entity structure
445
- first_year = dataset[dataset.years[0]]
446
- self.build_from_ids(
447
- first_year.person.person_id,
448
- first_year.person.person_benunit_id,
449
- first_year.person.person_household_id,
450
- first_year.benunit.benunit_id,
451
- first_year.household.household_id,
452
- )
453
-
454
- # Load variable values for all years
455
- for year in dataset.years:
456
- for table in dataset[year].tables:
457
- for variable in table.columns:
458
- if variable not in self.tax_benefit_system.variables:
459
- continue
460
- self.set_input(variable, year, table[variable])
461
-
462
- def build_from_ids(
463
- self,
464
- person_id: np.ndarray,
465
- person_benunit_id: np.ndarray,
466
- person_household_id: np.ndarray,
467
- benunit_id: np.ndarray,
468
- household_id: np.ndarray,
469
- ) -> None:
470
- """Build simulation entities from ID arrays.
471
-
472
- Args:
473
- person_id: Array of person IDs
474
- person_benunit_id: Array mapping persons to benefit units
475
- person_household_id: Array mapping persons to households
476
- benunit_id: Array of benefit unit IDs
477
- household_id: Array of household IDs
478
- """
479
- from policyengine_core.simulations.simulation_builder import (
480
- SimulationBuilder,
481
- ) # Import here to avoid circular dependency
482
-
483
- builder = SimulationBuilder()
484
- builder.populations = self.tax_benefit_system.instantiate_entities()
485
-
486
- # Declare entities
487
- builder.declare_person_entity("person", person_id)
488
- builder.declare_entity("benunit", np.unique(benunit_id))
489
- builder.declare_entity("household", np.unique(household_id))
490
-
491
- # Link persons to benefit units and households
492
- builder.join_with_persons(
493
- builder.populations["benunit"],
494
- person_benunit_id,
495
- np.array(["member"] * len(person_benunit_id)),
496
- )
497
- builder.join_with_persons(
498
- builder.populations["household"],
499
- person_household_id,
500
- np.array(["member"] * len(person_household_id)),
501
- )
502
-
503
- self.build_from_populations(builder.populations)
504
-
505
- def move_values(self, variable_donor: str, variable_target: str) -> None:
506
- """Move values from one variable to another across all branches.
507
-
508
- Used for behavioral response modeling where original values need
509
- to be preserved.
510
-
511
- Args:
512
- variable_donor: Variable to move values from
513
- variable_target: Variable to move values to
514
- """
515
- for simulation in list(self.branches.values()) + [self]:
516
- holder = simulation.get_holder(variable_donor)
517
- for known_period in holder.get_known_periods():
518
- array = holder.get_array(known_period)
519
- simulation.set_input(variable_target, known_period, array)
520
- holder.delete_arrays(known_period)
521
-
522
- def calculate(
523
- self,
524
- variable_name: str,
525
- period: str = None,
526
- map_to: str = None,
527
- decode_enums: bool = False,
528
- ):
529
- tracer: SimpleTracer = self.tracer
530
- if len(tracer.stack) == 0:
531
- # Only decode enums to string values when we're not within
532
- # the simulation tree.
533
- decode_enums = True
534
-
535
- if period is None:
536
- period = self.default_calculation_period
537
-
538
- period = period_(period)
539
-
540
- return super().calculate(
541
- variable_name, period, map_to=map_to, decode_enums=decode_enums
542
- )
543
-
544
-
545
- class Microsimulation(Simulation):
546
- """Extended simulation class with weighting support for microsimulation.
547
-
548
- Provides weighted calculations using survey weights for population-level
549
- estimates and statistics.
550
- """
551
-
552
- def get_weights(
553
- self, variable_name: str, period: str, map_to: Optional[str] = None
554
- ) -> np.ndarray:
555
- """Get weights for the specified variable's entity.
556
-
557
- Args:
558
- variable_name: Name of the variable to get weights for
559
- period: Time period for the weights
560
- map_to: Optional entity key to map weights to
561
-
562
- Returns:
563
- Array of weights for the entity
564
- """
565
- variable = self.tax_benefit_system.get_variable(variable_name)
566
- entity_key = map_to or variable.entity.key
567
- weight_variable_name = f"{entity_key}_weight"
568
- return self.calculate(
569
- weight_variable_name, period, map_to=map_to, unweighted=True
570
- )
571
-
572
- def calculate(
573
- self,
574
- variable_name: str,
575
- period: str = None,
576
- map_to: str = None,
577
- decode_enums: bool = False,
578
- unweighted: bool = False,
579
- ):
580
- tracer: SimpleTracer = self.tracer
581
-
582
- result = super().calculate(
583
- variable_name, period, map_to=map_to, decode_enums=decode_enums
584
- )
585
-
586
- if not unweighted and len(tracer.stack) == 0:
587
- weights = self.get_weights(variable_name, period, map_to=map_to)
588
- return MicroSeries(result, weights=weights)
589
-
590
- return result
591
-
592
- def calculate_dataframe(
593
- self,
594
- variable_names: List[str],
595
- period: Optional[str] = None,
596
- map_to: Optional[str] = None,
597
- use_weights: bool = True,
598
- ) -> MicroDataFrame:
599
- """Calculate multiple variables as a weighted DataFrame.
600
-
601
- Args:
602
- variable_names: List of variable names to calculate
603
- period: Time period for calculation
604
- map_to: Optional entity key to map results to
605
- use_weights: Whether to apply survey weights
606
-
607
- Returns:
608
- MicroDataFrame with calculated values and weights
609
- """
610
- values = super().calculate_dataframe(variable_names, period, map_to)
611
- if not use_weights:
612
- return values
613
- weights = self.get_weights(variable_names[0], period, map_to=map_to)
614
- return MicroDataFrame(values, weights=weights)
1
+ from .tax_benefit_system import *
2
+ from .simulation import *
3
+ from .microsimulation import *
@@ -0,0 +1,130 @@
1
+ # Standard library imports
2
+ import copy
3
+ from pathlib import Path
4
+ from typing import Any, Dict, List
5
+
6
+ # PolicyEngine core imports
7
+ from policyengine_core.parameters.operations.propagate_parameter_metadata import (
8
+ propagate_parameter_metadata,
9
+ )
10
+ from policyengine_core.parameters.operations.uprate_parameters import (
11
+ uprate_parameters,
12
+ )
13
+ from policyengine_core.taxbenefitsystems import TaxBenefitSystem
14
+
15
+ # PolicyEngine UK imports
16
+ from policyengine_uk.entities import BenUnit, Household, Person
17
+ from policyengine_uk.parameters.gov.contrib.create_private_pension_uprating import (
18
+ add_private_pension_uprating_factor,
19
+ )
20
+ from policyengine_uk.parameters.gov.dwp.state_pension.triple_lock.create_triple_lock import (
21
+ add_triple_lock,
22
+ )
23
+ from policyengine_uk.parameters.gov.economic_assumptions.create_economic_assumption_indices import (
24
+ create_economic_assumption_indices,
25
+ )
26
+ from policyengine_uk.parameters.gov.economic_assumptions.lag_average_earnings import (
27
+ add_lagged_earnings,
28
+ )
29
+ from policyengine_uk.parameters.gov.economic_assumptions.lag_cpi import (
30
+ add_lagged_cpi,
31
+ )
32
+ from policyengine_uk.utils.parameters import (
33
+ backdate_parameters,
34
+ convert_to_fiscal_year_parameters,
35
+ )
36
+
37
+ # Module constants
38
+ COUNTRY_DIR = Path(__file__).parent
39
+ ENHANCED_FRS = "hf://policyengine/policyengine-uk-data/enhanced_frs_2023_24.h5"
40
+
41
+
42
+ class CountryTaxBenefitSystem(TaxBenefitSystem):
43
+ """UK-specific tax and benefit system implementation.
44
+
45
+ This class defines the UK tax-benefit system with all relevant
46
+ variables, parameters, and entities (Person, BenUnit, Household).
47
+ """
48
+
49
+ basic_inputs: List[str] = [
50
+ "brma",
51
+ "local_authority",
52
+ "region",
53
+ "employment_income",
54
+ "age",
55
+ ]
56
+ modelled_policies = COUNTRY_DIR / "modelled_policies.yaml"
57
+ auto_carry_over_input_variables: bool = True
58
+
59
+ def reset_parameters(self) -> None:
60
+ """Reset parameters by reloading from the parameters directory."""
61
+ self._parameters_at_instant_cache = {}
62
+ self.load_parameters(self.parameters_dir)
63
+
64
+ def process_parameters(self) -> None:
65
+ """Process and transform parameters with UK-specific adjustments.
66
+
67
+ Applies various parameter transformations including:
68
+ - Private pension uprating factors
69
+ - Lagged earnings and CPI indices
70
+ - Triple lock calculations for state pensions
71
+ - Economic assumption indices
72
+ - Parameter uprating and backdating
73
+ - Conversion to fiscal year parameters
74
+ """
75
+ self._parameters_at_instant_cache = {}
76
+ # Add various UK-specific parameter adjustments
77
+ self.parameters = add_private_pension_uprating_factor(self.parameters)
78
+ self.parameters = add_lagged_earnings(self.parameters)
79
+ self.parameters = add_lagged_cpi(self.parameters)
80
+ self.parameters = add_triple_lock(self.parameters)
81
+ self.parameters = create_economic_assumption_indices(self.parameters)
82
+
83
+ # Create baseline parameters for reform comparisons
84
+ self.parameters.add_child("baseline", self.parameters.clone())
85
+
86
+ # Apply general parameter operations
87
+ self.parameters = propagate_parameter_metadata(self.parameters)
88
+ self.parameters = uprate_parameters(self.parameters)
89
+ self.parameters = backdate_parameters(self.parameters, "2015-01-01")
90
+ self.parameters.gov = convert_to_fiscal_year_parameters(
91
+ self.parameters.gov
92
+ )
93
+
94
+ def __init__(self):
95
+ """Initialize the UK tax-benefit system with entities and parameters."""
96
+ self._parameters_at_instant_cache: Dict[str, Any] = {}
97
+ self.variables: Dict[Any, Any] = {}
98
+
99
+ # Create copies of entity classes to avoid modifying originals
100
+ person, benunit, household = (
101
+ copy.copy(Person),
102
+ copy.copy(BenUnit),
103
+ copy.copy(Household),
104
+ )
105
+
106
+ # Set up entities
107
+ self.entities = [person, benunit, household]
108
+ self.person_entity = person
109
+ self.group_entities = [benunit, household]
110
+ self.group_entity_keys = [entity.key for entity in self.group_entities]
111
+
112
+ # Link entities to this tax-benefit system
113
+ for entity in self.entities:
114
+ entity.set_tax_benefit_system(self)
115
+
116
+ self.variable_module_metadata = {}
117
+
118
+ # Load all variables from the variables directory
119
+ self.add_variables_from_directory(COUNTRY_DIR / "variables")
120
+
121
+ # Set up and process parameters
122
+ self.parameters_dir = COUNTRY_DIR / "parameters"
123
+ self.reset_parameters()
124
+ self.process_parameters()
125
+
126
+
127
+ # Create system instance for module-level access
128
+ system = CountryTaxBenefitSystem()
129
+ parameters = system.parameters
130
+ variables = system.variables
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: policyengine-uk
3
- Version: 2.43.2
3
+ Version: 2.43.4
4
4
  Summary: PolicyEngine tax and benefit system for the UK
5
5
  Project-URL: Homepage, https://github.com/PolicyEngine/policyengine-uk
6
6
  Project-URL: Repository, https://github.com/PolicyEngine/policyengine-uk
@@ -1,8 +1,11 @@
1
1
  policyengine_uk/__init__.py,sha256=ud1uvjBep-lcZKBWUd0eo-PAT_FM92_PqUlDp5OYl4g,355
2
2
  policyengine_uk/entities.py,sha256=9yUbkUWQr3WydNE-gHhUudG97HGyXIZY3dkgQ6Z3t9A,1212
3
+ policyengine_uk/microsimulation.py,sha256=k2u8v6TlHiu8hO5gCjd8-qOsfzorCnjrKfnvs8cbGOc,2676
3
4
  policyengine_uk/model_api.py,sha256=KdwJCL2HkXVxDpL6fCsCnQ9Sub6Kqp7Hyxlis3MNIx4,241
4
5
  policyengine_uk/modelled_policies.yaml,sha256=TLhvmkuI9ip-Fjq63n66RzDErCkN8K4BzY6XLxLMtFg,463
5
- policyengine_uk/system.py,sha256=OooTAxHpzh4H5k8rWiH_qE4SooBomn-pjZnk4UvVR8A,21813
6
+ policyengine_uk/simulation.py,sha256=Cibz4giX9Ue7j7IqqBFwUkUKhhBvH9RpjAQ4eIDWUK8,14738
7
+ policyengine_uk/system.py,sha256=Z-ax_ImUq5k4tycuThczTxQNW5iTE6ikBJIBQdKfIzU,91
8
+ policyengine_uk/tax_benefit_system.py,sha256=7cahlxO7v-SzmyYSsteklCpkMuFOyOMomcYeh0LoRK8,4762
6
9
  policyengine_uk/data/__init__.py,sha256=J0bZ3WnvPzZ4qfVLYcwOc4lziMUMdbcMqJ3xwjoekbM,101
7
10
  policyengine_uk/data/dataset_schema.py,sha256=4SPUqDmzTsmmWFaJIlpDzT7J6WvBDgxRPfUnbjjtFuQ,9854
8
11
  policyengine_uk/data/economic_assumptions.py,sha256=4ZmE6z8Bbf21n0Hm-IpsS04Astg2NJAypv9TdA_udEk,6189
@@ -308,9 +311,9 @@ policyengine_uk/parameters/gov/dwp/winter_fuel_payment/amount/lower.yaml,sha256=
308
311
  policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/higher_age_requirement.yaml,sha256=ZDE_w3GvtK9THT55qTpDFeUvpdTAj8KsZp5lrp6zjRw,332
309
312
  policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/require_benefits.yaml,sha256=7vvHW5NXpaSixFayBKFXFCu6WToGlmH3ejJELVbdfDw,583
310
313
  policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/state_pension_age_requirement.yaml,sha256=8wXlC5wX-ceoZ-s2kOk4IhAiomsXSCM-vnRRbL2kChA,356
311
- policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/taxable_income_test/maximum_taxable_income.yaml,sha256=gLj2M2zoCMPuZ-0UgNSf-FTA5I4CCXhxBsd3qVro7Ck,260
312
- policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/taxable_income_test/use_maximum_taxable_income.yaml,sha256=pHldHvNHeGhG0mcn7LvjYA3b-q_AxrAxm5bu-mkTn1A,261
313
- policyengine_uk/parameters/gov/economic_assumptions/create_economic_assumption_indices.py,sha256=Z4dYghSit5gXo4V3wBpnLIc9zgTX4cfEyb_TUlJkTGY,1937
314
+ policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/taxable_income_test/maximum_taxable_income.yaml,sha256=vtXLTgcLg1tmvnsbmJUv45maVEzzKQTWK-lFYTGzuVk,280
315
+ policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/taxable_income_test/use_maximum_taxable_income.yaml,sha256=MKnXmHJaDJbpO-mLemudo5n-yQMCByuVwUtlxItOvjg,280
316
+ policyengine_uk/parameters/gov/economic_assumptions/create_economic_assumption_indices.py,sha256=uxJy4X30P8NRSL7dH757bgpPANMdf5efar8Bu7uR78w,1937
314
317
  policyengine_uk/parameters/gov/economic_assumptions/lag_average_earnings.py,sha256=ksHcyUQkLAJmKizCeSg8j0hzPc7ajgIvbExWLgCra4U,701
315
318
  policyengine_uk/parameters/gov/economic_assumptions/lag_cpi.py,sha256=IdtaMLN1_OSu-RFZsQV8vBlbOvXsPlnNlsOuinRHrxg,642
316
319
  policyengine_uk/parameters/gov/economic_assumptions/yoy_growth.yaml,sha256=k2xxY4YvioJ0agEOHwebcEWVwQbgePrnXO1dvogGWCs,17673
@@ -1373,7 +1376,7 @@ policyengine_uk/variables/misc/spi_imputed.py,sha256=iPVlBF_TisM0rtKvO-3-PQ2UYCe
1373
1376
  policyengine_uk/variables/misc/uc_migrated.py,sha256=zFNcUJaO8gwmbL1iY9GKgUt3G6J9yrCraqBV_5dCvlM,306
1374
1377
  policyengine_uk/variables/misc/categories/lower_middle_or_higher.py,sha256=C54tHYz2DmOyvQYCC1bF8RJwRZinhAq_e3aYC-9F5fM,157
1375
1378
  policyengine_uk/variables/misc/categories/lower_or_higher.py,sha256=81NIbLLabRr9NwjpUZDuV8IV8_mqmp5NM-CZvt55TwE,129
1376
- policyengine_uk-2.43.2.dist-info/METADATA,sha256=gM6cSwX8bQk0XtTzVCzRKyBHlJJ5BLVd3-RRFbzTz_k,3919
1377
- policyengine_uk-2.43.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1378
- policyengine_uk-2.43.2.dist-info/licenses/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
1379
- policyengine_uk-2.43.2.dist-info/RECORD,,
1379
+ policyengine_uk-2.43.4.dist-info/METADATA,sha256=zSNA8LoZZDX4ZBX_hWyz2W7EcCAfTV6_oaEnfdyBkKk,3919
1380
+ policyengine_uk-2.43.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1381
+ policyengine_uk-2.43.4.dist-info/licenses/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
1382
+ policyengine_uk-2.43.4.dist-info/RECORD,,