policyengine-uk 2.45.3__py3-none-any.whl → 2.47.2__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.

Files changed (27) hide show
  1. policyengine_uk/__init__.py +1 -0
  2. policyengine_uk/data/dataset_schema.py +6 -3
  3. policyengine_uk/data/economic_assumptions.py +1 -1
  4. policyengine_uk/dynamics/labour_supply.py +306 -0
  5. policyengine_uk/dynamics/participation.py +629 -0
  6. policyengine_uk/dynamics/progression.py +376 -0
  7. policyengine_uk/microsimulation.py +23 -1
  8. policyengine_uk/parameters/gov/dynamic/obr_labour_supply_assumptions.yaml +9 -0
  9. policyengine_uk/parameters/gov/economic_assumptions/yoy_growth.yaml +270 -32
  10. policyengine_uk/simulation.py +184 -9
  11. policyengine_uk/tax_benefit_system.py +4 -1
  12. policyengine_uk/tests/microsimulation/reforms_config.yaml +7 -7
  13. policyengine_uk/tests/microsimulation/test_validity.py +2 -3
  14. policyengine_uk/tests/microsimulation/update_reform_impacts.py +104 -40
  15. policyengine_uk/tests/policy/baseline/gov/dfe/extended_childcare_entitlement/extended_childcare_entitlement.yaml +8 -8
  16. policyengine_uk/utils/__init__.py +1 -0
  17. policyengine_uk/utils/compare.py +28 -0
  18. policyengine_uk/utils/solve_private_school_attendance_factor.py +4 -6
  19. policyengine_uk/variables/gov/dwp/additional_state_pension.py +1 -1
  20. policyengine_uk/variables/gov/dwp/basic_state_pension.py +1 -1
  21. policyengine_uk/variables/household/demographic/benunit/benunit_count_adults.py +11 -0
  22. policyengine_uk/variables/input/consumption/property/council_tax.py +0 -35
  23. policyengine_uk/variables/input/rent.py +0 -40
  24. {policyengine_uk-2.45.3.dist-info → policyengine_uk-2.47.2.dist-info}/METADATA +5 -5
  25. {policyengine_uk-2.45.3.dist-info → policyengine_uk-2.47.2.dist-info}/RECORD +27 -21
  26. {policyengine_uk-2.45.3.dist-info → policyengine_uk-2.47.2.dist-info}/WHEEL +0 -0
  27. {policyengine_uk-2.45.3.dist-info → policyengine_uk-2.47.2.dist-info}/licenses/LICENSE +0 -0
@@ -15,14 +15,58 @@ obr:
15
15
  2019-01-01: 0.022
16
16
  2020-01-01: 0.003
17
17
  2021-01-01: 0.059
18
- 2022-01-01: 0.064
19
- 2023-01-01: 0.069
20
- 2024-01-01: 0.047
21
- 2025-01-01: 0.037
22
- 2026-01-01: 0.022
23
- 2027-01-01: 0.021
24
- 2028-01-01: 0.023
25
- 2029-01-01: 0.025
18
+ 2022-01-01: 0.1287
19
+ 2023-01-01: 0.0748
20
+ 2024-01-01: 0.0331
21
+ 2025-01-01: 0.0416
22
+ 2026-01-01: 0.0308
23
+ 2027-01-01: 0.0300
24
+ 2028-01-01: 0.0283
25
+ 2029-01-01: 0.0283
26
+ 2030-01-01: 0.0238
27
+ 2031-01-01: 0.0230
28
+ 2032-01-01: 0.0237
29
+ 2033-01-01: 0.0238
30
+ 2034-01-01: 0.0238
31
+ 2035-01-01: 0.0239
32
+ 2036-01-01: 0.0239
33
+ 2037-01-01: 0.0239
34
+ 2038-01-01: 0.0239
35
+ 2039-01-01: 0.0239
36
+ 2040-01-01: 0.0239
37
+ 2041-01-01: 0.0239
38
+ 2042-01-01: 0.0239
39
+ 2043-01-01: 0.0239
40
+ 2044-01-01: 0.0239
41
+ 2045-01-01: 0.0239
42
+ 2046-01-01: 0.0239
43
+ 2047-01-01: 0.0239
44
+ 2048-01-01: 0.0239
45
+ 2049-01-01: 0.0239
46
+ 2050-01-01: 0.0239
47
+ 2051-01-01: 0.0239
48
+ 2052-01-01: 0.0239
49
+ 2053-01-01: 0.0239
50
+ 2054-01-01: 0.0239
51
+ 2055-01-01: 0.0239
52
+ 2056-01-01: 0.0239
53
+ 2057-01-01: 0.0239
54
+ 2058-01-01: 0.0239
55
+ 2059-01-01: 0.0239
56
+ 2060-01-01: 0.0239
57
+ 2061-01-01: 0.0239
58
+ 2062-01-01: 0.0239
59
+ 2063-01-01: 0.0239
60
+ 2064-01-01: 0.0239
61
+ 2065-01-01: 0.0239
62
+ 2066-01-01: 0.0239
63
+ 2067-01-01: 0.0239
64
+ 2068-01-01: 0.0239
65
+ 2069-01-01: 0.0239
66
+ 2070-01-01: 0.0239
67
+ 2071-01-01: 0.0239
68
+ 2072-01-01: 0.0239
69
+ 2073-01-01: 0.0239
26
70
  metadata:
27
71
  unit: /1
28
72
  label: retail price index growth
@@ -45,14 +89,58 @@ obr:
45
89
  2019-01-01: 0.022
46
90
  2020-01-01: 0.003
47
91
  2021-01-01: 0.059
48
- 2022-01-01: 0.064
49
- 2023-01-01: 0.069
50
- 2024-01-01: 0.047
51
- 2025-01-01: 0.037
52
- 2026-01-01: 0.022
53
- 2027-01-01: 0.021
54
- 2028-01-01: 0.023
55
- 2029-01-01: 0.025
92
+ 2022-01-01: 0.0644
93
+ 2023-01-01: 0.0688
94
+ 2024-01-01: 0.0467
95
+ 2025-01-01: 0.0371
96
+ 2026-01-01: 0.0216
97
+ 2027-01-01: 0.0207
98
+ 2028-01-01: 0.0225
99
+ 2029-01-01: 0.0253
100
+ 2030-01-01: 0.0292
101
+ 2031-01-01: 0.0332
102
+ 2032-01-01: 0.0371
103
+ 2033-01-01: 0.0374
104
+ 2034-01-01: 0.0377
105
+ 2035-01-01: 0.0380
106
+ 2036-01-01: 0.0383
107
+ 2037-01-01: 0.0383
108
+ 2038-01-01: 0.0383
109
+ 2039-01-01: 0.0383
110
+ 2040-01-01: 0.0383
111
+ 2041-01-01: 0.0383
112
+ 2042-01-01: 0.0383
113
+ 2043-01-01: 0.0383
114
+ 2044-01-01: 0.0383
115
+ 2045-01-01: 0.0383
116
+ 2046-01-01: 0.0383
117
+ 2047-01-01: 0.0383
118
+ 2048-01-01: 0.0383
119
+ 2049-01-01: 0.0383
120
+ 2050-01-01: 0.0383
121
+ 2051-01-01: 0.0383
122
+ 2052-01-01: 0.0383
123
+ 2053-01-01: 0.0383
124
+ 2054-01-01: 0.0383
125
+ 2055-01-01: 0.0383
126
+ 2056-01-01: 0.0383
127
+ 2057-01-01: 0.0383
128
+ 2058-01-01: 0.0383
129
+ 2059-01-01: 0.0383
130
+ 2060-01-01: 0.0383
131
+ 2061-01-01: 0.0383
132
+ 2062-01-01: 0.0383
133
+ 2063-01-01: 0.0383
134
+ 2064-01-01: 0.0383
135
+ 2065-01-01: 0.0383
136
+ 2066-01-01: 0.0383
137
+ 2067-01-01: 0.0383
138
+ 2068-01-01: 0.0383
139
+ 2069-01-01: 0.0383
140
+ 2070-01-01: 0.0383
141
+ 2071-01-01: 0.0383
142
+ 2072-01-01: 0.0383
143
+ 2073-01-01: 0.0383
56
144
  metadata:
57
145
  unit: /1
58
146
  label: average earnings growth
@@ -76,14 +164,58 @@ obr:
76
164
  2019-01-01: 0.017
77
165
  2020-01-01: 0.006
78
166
  2021-01-01: 0.04
79
- 2022-01-01: 0.1
80
- 2023-01-01: 0.057
81
- 2024-01-01: 0.023
82
- 2025-01-01: 0.032
83
- 2026-01-01: 0.019
84
- 2027-01-01: 0.02
85
- 2028-01-01: 0.02
86
- 2029-01-01: 0.02
167
+ 2022-01-01: 0.1004
168
+ 2023-01-01: 0.0567
169
+ 2024-01-01: 0.0233
170
+ 2025-01-01: 0.0318
171
+ 2026-01-01: 0.0193
172
+ 2027-01-01: 0.0200
173
+ 2028-01-01: 0.0200
174
+ 2029-01-01: 0.0200
175
+ 2030-01-01: 0.0200
176
+ 2031-01-01: 0.0200
177
+ 2032-01-01: 0.0200
178
+ 2033-01-01: 0.0200
179
+ 2034-01-01: 0.0200
180
+ 2035-01-01: 0.0200
181
+ 2036-01-01: 0.0200
182
+ 2037-01-01: 0.0200
183
+ 2038-01-01: 0.0200
184
+ 2039-01-01: 0.0200
185
+ 2040-01-01: 0.0200
186
+ 2041-01-01: 0.0200
187
+ 2042-01-01: 0.0200
188
+ 2043-01-01: 0.0200
189
+ 2044-01-01: 0.0200
190
+ 2045-01-01: 0.0200
191
+ 2046-01-01: 0.0200
192
+ 2047-01-01: 0.0200
193
+ 2048-01-01: 0.0200
194
+ 2049-01-01: 0.0200
195
+ 2050-01-01: 0.0200
196
+ 2051-01-01: 0.0200
197
+ 2052-01-01: 0.0200
198
+ 2053-01-01: 0.0200
199
+ 2054-01-01: 0.0200
200
+ 2055-01-01: 0.0200
201
+ 2056-01-01: 0.0200
202
+ 2057-01-01: 0.0200
203
+ 2058-01-01: 0.0200
204
+ 2059-01-01: 0.0200
205
+ 2060-01-01: 0.0200
206
+ 2061-01-01: 0.0200
207
+ 2062-01-01: 0.0200
208
+ 2063-01-01: 0.0200
209
+ 2064-01-01: 0.0200
210
+ 2065-01-01: 0.0200
211
+ 2066-01-01: 0.0200
212
+ 2067-01-01: 0.0200
213
+ 2068-01-01: 0.0200
214
+ 2069-01-01: 0.0200
215
+ 2070-01-01: 0.0200
216
+ 2071-01-01: 0.0200
217
+ 2072-01-01: 0.0200
218
+ 2073-01-01: 0.0200
87
219
  metadata:
88
220
  unit: /1
89
221
  label: consumer price index growth
@@ -113,6 +245,68 @@ obr:
113
245
  - title: ONS CPI series excluding housing costs
114
246
  href: https://www.ons.gov.uk/economy/inflationandpriceindices/adhocs/2863consumerpriceindicesseriesexcludingrentsmaintenancerepairsandwaterchargesfortheperiodjanuary1996toapril2025
115
247
 
248
+ cpih:
249
+ description: Consumer price index including owner occupiers' housing costs year-on-year growth.
250
+ values:
251
+ 2022-01-01: 0.0877
252
+ 2023-01-01: 0.0555
253
+ 2024-01-01: 0.0320
254
+ 2025-01-01: 0.0374
255
+ 2026-01-01: 0.0227
256
+ 2027-01-01: 0.0210
257
+ 2028-01-01: 0.0205
258
+ 2029-01-01: 0.0209
259
+ 2030-01-01: 0.0224
260
+ 2031-01-01: 0.0230
261
+ 2032-01-01: 0.0237
262
+ 2033-01-01: 0.0238
263
+ 2034-01-01: 0.0238
264
+ 2035-01-01: 0.0239
265
+ 2036-01-01: 0.0239
266
+ 2037-01-01: 0.0239
267
+ 2038-01-01: 0.0239
268
+ 2039-01-01: 0.0239
269
+ 2040-01-01: 0.0239
270
+ 2041-01-01: 0.0239
271
+ 2042-01-01: 0.0239
272
+ 2043-01-01: 0.0239
273
+ 2044-01-01: 0.0239
274
+ 2045-01-01: 0.0239
275
+ 2046-01-01: 0.0239
276
+ 2047-01-01: 0.0239
277
+ 2048-01-01: 0.0239
278
+ 2049-01-01: 0.0239
279
+ 2050-01-01: 0.0239
280
+ 2051-01-01: 0.0239
281
+ 2052-01-01: 0.0239
282
+ 2053-01-01: 0.0239
283
+ 2054-01-01: 0.0239
284
+ 2055-01-01: 0.0239
285
+ 2056-01-01: 0.0239
286
+ 2057-01-01: 0.0239
287
+ 2058-01-01: 0.0239
288
+ 2059-01-01: 0.0239
289
+ 2060-01-01: 0.0239
290
+ 2061-01-01: 0.0239
291
+ 2062-01-01: 0.0239
292
+ 2063-01-01: 0.0239
293
+ 2064-01-01: 0.0239
294
+ 2065-01-01: 0.0239
295
+ 2066-01-01: 0.0239
296
+ 2067-01-01: 0.0239
297
+ 2068-01-01: 0.0239
298
+ 2069-01-01: 0.0239
299
+ 2070-01-01: 0.0239
300
+ 2071-01-01: 0.0239
301
+ 2072-01-01: 0.0239
302
+ 2073-01-01: 0.0239
303
+ metadata:
304
+ unit: /1
305
+ label: CPIH growth
306
+ reference:
307
+ - title: OBR EFO March 2025
308
+ href: https://obr.uk/efo/economic-and-fiscal-outlook-march-2025/
309
+
116
310
  non_labour_income:
117
311
  description: Non-labour income year-on-year growth.
118
312
  values:
@@ -317,14 +511,58 @@ ons:
317
511
  description: Population year-on-year growth.
318
512
  values:
319
513
  2021-01-01: 0.004
320
- 2022-01-01: 0.003
321
- 2023-01-01: 0.014
322
- 2024-01-01: 0.01
323
- 2025-01-01: 0.011
324
- 2026-01-01: 0.007
325
- 2027-01-01: 0.008
326
- 2028-01-01: 0.004
327
- 2029-01-01: 0.005
514
+ 2022-01-01: 0.0093
515
+ 2023-01-01: 0.0131
516
+ 2024-01-01: 0.0107
517
+ 2025-01-01: 0.0072
518
+ 2026-01-01: 0.0038
519
+ 2027-01-01: 0.0037
520
+ 2028-01-01: 0.0040
521
+ 2029-01-01: 0.0044
522
+ 2030-01-01: 0.0045
523
+ 2031-01-01: 0.0043
524
+ 2032-01-01: 0.0042
525
+ 2033-01-01: 0.0041
526
+ 2034-01-01: 0.0040
527
+ 2035-01-01: 0.0039
528
+ 2036-01-01: 0.0038
529
+ 2037-01-01: 0.0037
530
+ 2038-01-01: 0.0036
531
+ 2039-01-01: 0.0035
532
+ 2040-01-01: 0.0035
533
+ 2041-01-01: 0.0034
534
+ 2042-01-01: 0.0034
535
+ 2043-01-01: 0.0033
536
+ 2044-01-01: 0.0032
537
+ 2045-01-01: 0.0032
538
+ 2046-01-01: 0.0031
539
+ 2047-01-01: 0.0030
540
+ 2048-01-01: 0.0029
541
+ 2049-01-01: 0.0028
542
+ 2050-01-01: 0.0027
543
+ 2051-01-01: 0.0025
544
+ 2052-01-01: 0.0024
545
+ 2053-01-01: 0.0022
546
+ 2054-01-01: 0.0021
547
+ 2055-01-01: 0.0020
548
+ 2056-01-01: 0.0019
549
+ 2057-01-01: 0.0018
550
+ 2058-01-01: 0.0017
551
+ 2059-01-01: 0.0016
552
+ 2060-01-01: 0.0016
553
+ 2061-01-01: 0.0016
554
+ 2062-01-01: 0.0015
555
+ 2063-01-01: 0.0015
556
+ 2064-01-01: 0.0015
557
+ 2065-01-01: 0.0015
558
+ 2066-01-01: 0.0015
559
+ 2067-01-01: 0.0015
560
+ 2068-01-01: 0.0015
561
+ 2069-01-01: 0.0015
562
+ 2070-01-01: 0.0015
563
+ 2071-01-01: 0.0015
564
+ 2072-01-01: 0.0015
565
+ 2073-01-01: 0.0014
328
566
  metadata:
329
567
  unit: /1
330
568
  label: population growth
@@ -1,5 +1,5 @@
1
1
  # Standard library imports
2
- from typing import Dict, Optional, Union, Type
2
+ from typing import Dict, Optional, Union, Type, List
3
3
 
4
4
  # Third-party imports
5
5
  import numpy as np
@@ -23,9 +23,12 @@ from policyengine_uk.utils.scenario import Scenario
23
23
  from policyengine_uk.data.economic_assumptions import (
24
24
  extend_single_year_dataset,
25
25
  )
26
+ from policyengine_uk.utils.dependencies import get_variable_dependencies
26
27
 
27
28
  from .tax_benefit_system import CountryTaxBenefitSystem
28
29
 
30
+ from microdf import MicroDataFrame
31
+
29
32
 
30
33
  class Simulation(CoreSimulation):
31
34
  """UK-specific simulation class for calculating tax and benefit outcomes.
@@ -36,6 +39,10 @@ class Simulation(CoreSimulation):
36
39
 
37
40
  default_input_period: int = 2025
38
41
  default_calculation_period: int = 2025
42
+ baseline: "Simulation"
43
+
44
+ calculated_periods: List[str] = []
45
+ _variable_dependencies: Dict[str, List[str]] = None
39
46
 
40
47
  def __init__(
41
48
  self,
@@ -103,10 +110,6 @@ class Simulation(CoreSimulation):
103
110
  else:
104
111
  raise ValueError(f"Unsupported dataset type: {dataset.__class__}")
105
112
 
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
113
  self.input_variables = self.get_known_variables()
111
114
 
112
115
  # Universal Credit reform (July 2025). Needs closer integration in the baseline,
@@ -123,6 +126,23 @@ class Simulation(CoreSimulation):
123
126
  if scenario.simulation_modifier is not None:
124
127
  scenario.simulation_modifier(self)
125
128
 
129
+ if scenario is not None:
130
+ self.baseline = Simulation(
131
+ scenario=None,
132
+ situation=situation,
133
+ dataset=dataset,
134
+ trace=trace,
135
+ )
136
+ else:
137
+ self.baseline = self.clone()
138
+
139
+ self.calculated_periods = []
140
+
141
+ def reset_calculations(self):
142
+ for variable in self.tax_benefit_system.variables:
143
+ if variable not in self.input_variables:
144
+ self.delete_arrays(variable)
145
+
126
146
  def get_known_variables(self):
127
147
  variables = []
128
148
  for variable in self.tax_benefit_system.variables:
@@ -167,6 +187,10 @@ class Simulation(CoreSimulation):
167
187
  builder.build_from_dict(self.tax_benefit_system, situation, self)
168
188
  self.has_axes = builder.has_axes
169
189
 
190
+ self.dataset = UKSingleYearDataset.from_simulation(
191
+ self, fiscal_year=self.default_input_period
192
+ )
193
+
170
194
  def build_from_url(self, url: str) -> None:
171
195
  """Build simulation from a HuggingFace dataset URL.
172
196
 
@@ -199,14 +223,11 @@ class Simulation(CoreSimulation):
199
223
  # Determine dataset type and build accordingly
200
224
  if UKMultiYearDataset.validate_file_path(dataset, False):
201
225
  self.build_from_multi_year_dataset(UKMultiYearDataset(dataset))
202
- self.dataset = dataset
203
226
  elif UKSingleYearDataset.validate_file_path(dataset, False):
204
227
  self.build_from_single_year_dataset(UKSingleYearDataset(dataset))
205
- self.dataset = dataset
206
228
  else:
207
229
  dataset = Dataset.from_file(dataset, self.default_input_period)
208
230
  self.build_from_dataset(dataset)
209
- self.dataset = dataset
210
231
 
211
232
  def build_from_dataframe(self, df: pd.DataFrame) -> None:
212
233
  """Build simulation from a pandas DataFrame.
@@ -407,13 +428,43 @@ class Simulation(CoreSimulation):
407
428
  simulation.set_input(variable_target, known_period, array)
408
429
  holder.delete_arrays(known_period)
409
430
 
431
+ def calculate_all(self, year: int) -> None:
432
+ person, benunit, household = (
433
+ pd.DataFrame(),
434
+ pd.DataFrame(),
435
+ pd.DataFrame(),
436
+ )
437
+ period = str(year)
438
+ for variable in self.tax_benefit_system.variables.values():
439
+ entity = variable.entity.key
440
+ result = self.calculate(variable.name, period).values
441
+ if entity == "person":
442
+ person[variable.name] = result
443
+ elif entity == "benunit":
444
+ benunit[variable.name] = result
445
+ elif entity == "household":
446
+ household[variable.name] = result
447
+
448
+ person = MicroDataFrame(person, weights="person_weight")
449
+ benunit = MicroDataFrame(benunit, weights="benunit_weight")
450
+ household = MicroDataFrame(household, weights="household_weight")
451
+
452
+ return UKSingleYearDataset(
453
+ person=person,
454
+ benunit=benunit,
455
+ household=household,
456
+ fiscal_year=year,
457
+ )
458
+
410
459
  def calculate(
411
460
  self,
412
- variable_name: str,
461
+ variable_name: str = None,
413
462
  period: str = None,
414
463
  map_to: str = None,
415
464
  decode_enums: bool = False,
416
465
  ):
466
+ if variable_name is None:
467
+ return self.calculate_all()
417
468
  tracer: SimpleTracer = self.tracer
418
469
  if len(tracer.stack) == 0:
419
470
  # Only decode enums to string values when we're not within
@@ -428,3 +479,127 @@ class Simulation(CoreSimulation):
428
479
  return super().calculate(
429
480
  variable_name, period, map_to=map_to, decode_enums=decode_enums
430
481
  )
482
+
483
+ def apply_dynamics(self, year: int):
484
+ """Apply dynamics to the simulation for a specific year.
485
+
486
+ Args:
487
+ year: Year to apply dynamics for
488
+ """
489
+ # Apply OBR labour supply dynamics if enabled
490
+ from policyengine_uk.dynamics.labour_supply import (
491
+ apply_labour_supply_responses,
492
+ )
493
+
494
+ return apply_labour_supply_responses(self, year=str(year))
495
+
496
+ def get_variable_dependencies(self, variable_name: str, depth: int = 1):
497
+ if self._variable_dependencies is None:
498
+ self._variable_dependencies = {}
499
+ self._example_simulation = Simulation(
500
+ situation={
501
+ "age": 30,
502
+ },
503
+ trace=True,
504
+ )
505
+ self._example_simulation.calculate("hbai_household_net_income")
506
+ for (
507
+ variable
508
+ ) in self._example_simulation.tax_benefit_system.variables:
509
+ try:
510
+ dependencies = get_variable_dependencies(
511
+ variable, self._example_simulation
512
+ )
513
+ self._variable_dependencies[variable] = dependencies
514
+ except Exception as e:
515
+ self._variable_dependencies[variable] = []
516
+
517
+ if variable_name not in self._variable_dependencies:
518
+ raise ValueError(
519
+ f"Variable '{variable_name}' not found in simulation dependencies."
520
+ )
521
+ dependencies = self._variable_dependencies[variable_name]
522
+
523
+ if depth <= 1:
524
+ return dependencies
525
+
526
+ all_dependencies = set(dependencies)
527
+ for dep in dependencies:
528
+ try:
529
+ sub_dependencies = self.get_variable_dependencies(
530
+ dep, depth - 1
531
+ )
532
+ all_dependencies.update(sub_dependencies)
533
+ except ValueError:
534
+ continue
535
+ return list(all_dependencies)
536
+
537
+ def compare(
538
+ self,
539
+ other: "Simulation",
540
+ variables: list[str] = None,
541
+ period: str = None,
542
+ change_only: bool = False,
543
+ ):
544
+ """Compare two simulations for a specific variable list.
545
+
546
+ Args:
547
+ other: Another Simulation instance to compare against
548
+ variables: List of variable names to compare. If None, compares all variables.
549
+
550
+ Returns:
551
+ DataFrame with comparison results
552
+ """
553
+
554
+ if variables is None:
555
+ variables = self.input_variables + [
556
+ "hbai_household_net_income",
557
+ ]
558
+
559
+ df_self = self.calculate_dataframe(variables, period=period)
560
+ df_other = other.calculate_dataframe(variables, period=period)
561
+
562
+ df_combined = pd.concat(
563
+ [df_self, df_other], axis=1, keys=["self", "other"]
564
+ )
565
+ # Reset columns, then order by variable name first then self/other
566
+ df_combined.columns = [
567
+ f"{var}_{entity}" for entity, var in df_combined.columns
568
+ ]
569
+ # Add change where applicable (numeric only)
570
+ for variable in variables:
571
+ self_col = f"{variable}_self"
572
+ other_col = f"{variable}_other"
573
+ try:
574
+ df_combined[f"{variable}_change"] = (
575
+ df_combined[other_col] - df_combined[self_col]
576
+ )
577
+ except:
578
+ # For other data types, use XOR
579
+ df_combined[f"{variable}_change"] = (
580
+ df_combined[other_col] != df_combined[self_col]
581
+ )
582
+ columns = []
583
+
584
+ for variable in variables:
585
+ columns.extend(
586
+ [
587
+ f"{variable}_self",
588
+ f"{variable}_other",
589
+ f"{variable}_change",
590
+ ]
591
+ )
592
+
593
+ if change_only:
594
+ variables_to_include = []
595
+ for variable in variables:
596
+ if df_combined[f"{variable}_change"].mean() > 0:
597
+ variables_to_include.extend(
598
+ [
599
+ f"{variable}_self",
600
+ f"{variable}_other",
601
+ f"{variable}_change",
602
+ ]
603
+ )
604
+ columns = variables_to_include
605
+ return df_combined[columns]
@@ -11,6 +11,7 @@ from policyengine_core.parameters.operations.uprate_parameters import (
11
11
  uprate_parameters,
12
12
  )
13
13
  from policyengine_core.taxbenefitsystems import TaxBenefitSystem
14
+ from policyengine_core.variables import Variable
14
15
 
15
16
  # PolicyEngine UK imports
16
17
  from policyengine_uk.entities import BenUnit, Household, Person
@@ -56,6 +57,8 @@ class CountryTaxBenefitSystem(TaxBenefitSystem):
56
57
  modelled_policies = COUNTRY_DIR / "modelled_policies.yaml"
57
58
  auto_carry_over_input_variables: bool = True
58
59
 
60
+ variables: Dict[str, Variable]
61
+
59
62
  def reset_parameter_caches(self):
60
63
  """Reset all caches in the tax-benefit system."""
61
64
  self._parameters_at_instant_cache = {}
@@ -102,7 +105,7 @@ class CountryTaxBenefitSystem(TaxBenefitSystem):
102
105
  def __init__(self):
103
106
  """Initialize the UK tax-benefit system with entities and parameters."""
104
107
  self._parameters_at_instant_cache: Dict[str, Any] = {}
105
- self.variables: Dict[Any, Any] = {}
108
+ self.variables = {}
106
109
 
107
110
  # Create copies of entity classes to avoid modifying originals
108
111
  person, benunit, household = (
@@ -1,10 +1,10 @@
1
1
  reforms:
2
2
  - name: Raise basic rate by 1pp
3
- expected_impact: 7.6
3
+ expected_impact: 8.0
4
4
  parameters:
5
5
  gov.hmrc.income_tax.rates.uk[0].rate: 0.21
6
6
  - name: Raise higher rate by 1pp
7
- expected_impact: 4.7
7
+ expected_impact: 4.8
8
8
  parameters:
9
9
  gov.hmrc.income_tax.rates.uk[1].rate: 0.42
10
10
  - name: Raise personal allowance by ~800GBP/year
@@ -12,22 +12,22 @@ reforms:
12
12
  parameters:
13
13
  gov.hmrc.income_tax.allowances.personal_allowance.amount: 13000
14
14
  - name: Raise child benefit by 25GBP/week per additional child
15
- expected_impact: -1.4
15
+ expected_impact: -1.3
16
16
  parameters:
17
17
  gov.hmrc.child_benefit.amount.additional: 25
18
18
  - name: Reduce Universal Credit taper rate to 20%
19
- expected_impact: -39.7
19
+ expected_impact: -33.8
20
20
  parameters:
21
21
  gov.dwp.universal_credit.means_test.reduction_rate: 0.2
22
22
  - name: Raise Class 1 main employee NICs rate to 10%
23
- expected_impact: 12.3
23
+ expected_impact: 12.7
24
24
  parameters:
25
25
  gov.hmrc.national_insurance.class_1.rates.employee.main: 0.1
26
26
  - name: Raise VAT standard rate by 2pp
27
- expected_impact: 18.9
27
+ expected_impact: 20.9
28
28
  parameters:
29
29
  gov.hmrc.vat.standard_rate: 0.22
30
30
  - name: Raise additional rate by 3pp
31
- expected_impact: 5.4
31
+ expected_impact: 5.1
32
32
  parameters:
33
33
  gov.hmrc.income_tax.rates.uk[2].rate: 0.48
@@ -1,4 +1,4 @@
1
- from policyengine import Simulation
1
+ from policyengine_uk import Microsimulation
2
2
  import pytest
3
3
 
4
4
  YEARS = range(2024, 2026)
@@ -6,8 +6,7 @@ YEARS = range(2024, 2026)
6
6
 
7
7
  @pytest.mark.parametrize("year", YEARS)
8
8
  def test_not_nan(year):
9
- baseline = Simulation(scope="macro", country="uk")
10
- baseline = baseline.baseline_simulation
9
+ baseline = Microsimulation()
11
10
  for variable in baseline.tax_benefit_system.variables:
12
11
  requires_computation_after = baseline.tax_benefit_system.variables[
13
12
  variable