policyengine 3.0.0__py3-none-any.whl → 3.1.1__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.
Files changed (76) hide show
  1. policyengine/__pycache__/__init__.cpython-313.pyc +0 -0
  2. policyengine/core/__init__.py +22 -0
  3. policyengine/core/dataset.py +260 -0
  4. policyengine/core/dataset_version.py +16 -0
  5. policyengine/core/dynamic.py +43 -0
  6. policyengine/core/output.py +26 -0
  7. policyengine/{models → core}/parameter.py +4 -2
  8. policyengine/{models → core}/parameter_value.py +1 -1
  9. policyengine/core/policy.py +43 -0
  10. policyengine/{models → core}/simulation.py +10 -14
  11. policyengine/core/tax_benefit_model.py +11 -0
  12. policyengine/core/tax_benefit_model_version.py +34 -0
  13. policyengine/core/variable.py +15 -0
  14. policyengine/outputs/__init__.py +21 -0
  15. policyengine/outputs/aggregate.py +124 -0
  16. policyengine/outputs/change_aggregate.py +184 -0
  17. policyengine/outputs/decile_impact.py +140 -0
  18. policyengine/tax_benefit_models/uk/__init__.py +26 -0
  19. policyengine/tax_benefit_models/uk/analysis.py +97 -0
  20. policyengine/tax_benefit_models/uk/datasets.py +176 -0
  21. policyengine/tax_benefit_models/uk/model.py +268 -0
  22. policyengine/tax_benefit_models/uk/outputs.py +108 -0
  23. policyengine/tax_benefit_models/uk.py +33 -0
  24. policyengine/tax_benefit_models/us/__init__.py +36 -0
  25. policyengine/tax_benefit_models/us/analysis.py +99 -0
  26. policyengine/tax_benefit_models/us/datasets.py +307 -0
  27. policyengine/tax_benefit_models/us/model.py +447 -0
  28. policyengine/tax_benefit_models/us/outputs.py +108 -0
  29. policyengine/tax_benefit_models/us.py +32 -0
  30. policyengine/utils/__init__.py +3 -0
  31. policyengine/utils/dates.py +40 -0
  32. policyengine/utils/parametric_reforms.py +39 -0
  33. policyengine/utils/plotting.py +179 -0
  34. {policyengine-3.0.0.dist-info → policyengine-3.1.1.dist-info}/METADATA +185 -20
  35. policyengine-3.1.1.dist-info/RECORD +39 -0
  36. policyengine/database/__init__.py +0 -56
  37. policyengine/database/aggregate.py +0 -33
  38. policyengine/database/baseline_parameter_value_table.py +0 -66
  39. policyengine/database/baseline_variable_table.py +0 -40
  40. policyengine/database/database.py +0 -251
  41. policyengine/database/dataset_table.py +0 -41
  42. policyengine/database/dynamic_table.py +0 -34
  43. policyengine/database/link.py +0 -82
  44. policyengine/database/model_table.py +0 -27
  45. policyengine/database/model_version_table.py +0 -28
  46. policyengine/database/parameter_table.py +0 -31
  47. policyengine/database/parameter_value_table.py +0 -62
  48. policyengine/database/policy_table.py +0 -34
  49. policyengine/database/report_element_table.py +0 -48
  50. policyengine/database/report_table.py +0 -24
  51. policyengine/database/simulation_table.py +0 -50
  52. policyengine/database/user_table.py +0 -28
  53. policyengine/database/versioned_dataset_table.py +0 -28
  54. policyengine/models/__init__.py +0 -30
  55. policyengine/models/aggregate.py +0 -92
  56. policyengine/models/baseline_parameter_value.py +0 -14
  57. policyengine/models/baseline_variable.py +0 -12
  58. policyengine/models/dataset.py +0 -18
  59. policyengine/models/dynamic.py +0 -15
  60. policyengine/models/model.py +0 -124
  61. policyengine/models/model_version.py +0 -14
  62. policyengine/models/policy.py +0 -17
  63. policyengine/models/policyengine_uk.py +0 -114
  64. policyengine/models/policyengine_us.py +0 -115
  65. policyengine/models/report.py +0 -10
  66. policyengine/models/report_element.py +0 -36
  67. policyengine/models/user.py +0 -14
  68. policyengine/models/versioned_dataset.py +0 -12
  69. policyengine/utils/charts.py +0 -286
  70. policyengine/utils/compress.py +0 -20
  71. policyengine/utils/datasets.py +0 -71
  72. policyengine-3.0.0.dist-info/RECORD +0 -47
  73. policyengine-3.0.0.dist-info/entry_points.txt +0 -2
  74. {policyengine-3.0.0.dist-info → policyengine-3.1.1.dist-info}/WHEEL +0 -0
  75. {policyengine-3.0.0.dist-info → policyengine-3.1.1.dist-info}/licenses/LICENSE +0 -0
  76. {policyengine-3.0.0.dist-info → policyengine-3.1.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,176 @@
1
+ from pathlib import Path
2
+
3
+ import pandas as pd
4
+ from microdf import MicroDataFrame
5
+ from pydantic import BaseModel, ConfigDict
6
+
7
+ from policyengine.core import Dataset, map_to_entity
8
+
9
+
10
+ class UKYearData(BaseModel):
11
+ """Entity-level data for a single year."""
12
+
13
+ model_config = ConfigDict(arbitrary_types_allowed=True)
14
+
15
+ person: MicroDataFrame
16
+ benunit: MicroDataFrame
17
+ household: MicroDataFrame
18
+
19
+ def map_to_entity(
20
+ self, source_entity: str, target_entity: str, columns: list[str] = None
21
+ ) -> MicroDataFrame:
22
+ """Map data from source entity to target entity using join keys.
23
+
24
+ Args:
25
+ source_entity (str): The source entity name ('person', 'benunit', 'household').
26
+ target_entity (str): The target entity name ('person', 'benunit', 'household').
27
+ columns (list[str], optional): List of column names to map. If None, maps all columns.
28
+
29
+ Returns:
30
+ MicroDataFrame: The mapped data at the target entity level.
31
+
32
+ Raises:
33
+ ValueError: If source or target entity is invalid.
34
+ """
35
+ entity_data = {
36
+ "person": self.person,
37
+ "benunit": self.benunit,
38
+ "household": self.household,
39
+ }
40
+ return map_to_entity(
41
+ entity_data=entity_data,
42
+ source_entity=source_entity,
43
+ target_entity=target_entity,
44
+ person_entity="person",
45
+ columns=columns,
46
+ )
47
+
48
+
49
+ class PolicyEngineUKDataset(Dataset):
50
+ """UK dataset with multi-year entity-level data."""
51
+
52
+ data: UKYearData | None = None
53
+
54
+ def model_post_init(self, __context):
55
+ """Called after Pydantic initialization."""
56
+ # Make sure we are synchronised between in-memory and storage, at least on initialisation
57
+ if self.data is not None:
58
+ self.save()
59
+ elif self.filepath and not self.data:
60
+ try:
61
+ self.load()
62
+ except FileNotFoundError:
63
+ # File doesn't exist yet, that's OK
64
+ pass
65
+
66
+ def save(self) -> None:
67
+ """Save dataset to HDF5 file."""
68
+ filepath = Path(self.filepath)
69
+ if not filepath.parent.exists():
70
+ filepath.parent.mkdir(parents=True, exist_ok=True)
71
+ with pd.HDFStore(filepath, mode="w") as store:
72
+ store["person"] = pd.DataFrame(self.data.person)
73
+ store["benunit"] = pd.DataFrame(self.data.benunit)
74
+ store["household"] = pd.DataFrame(self.data.household)
75
+
76
+ def load(self) -> None:
77
+ """Load dataset from HDF5 file into this instance."""
78
+ filepath = self.filepath
79
+ with pd.HDFStore(filepath, mode="r") as store:
80
+ self.data = UKYearData(
81
+ person=MicroDataFrame(
82
+ store["person"], weights="person_weight"
83
+ ),
84
+ benunit=MicroDataFrame(
85
+ store["benunit"], weights="benunit_weight"
86
+ ),
87
+ household=MicroDataFrame(
88
+ store["household"], weights="household_weight"
89
+ ),
90
+ )
91
+
92
+ def __repr__(self) -> str:
93
+ if self.data is None:
94
+ return f"<PolicyEngineUKDataset id={self.id} year={self.year} filepath={self.filepath} (not loaded)>"
95
+ else:
96
+ n_people = len(self.data.person)
97
+ n_benunits = len(self.data.benunit)
98
+ n_households = len(self.data.household)
99
+ return f"<PolicyEngineUKDataset id={self.id} year={self.year} filepath={self.filepath} people={n_people} benunits={n_benunits} households={n_households}>"
100
+
101
+
102
+ def create_datasets(
103
+ datasets: list[str] = [
104
+ "hf://policyengine/policyengine-uk-data/frs_2023_24.h5",
105
+ "hf://policyengine/policyengine-uk-data/enhanced_frs_2023_24.h5",
106
+ ],
107
+ years: list[int] = [2026, 2027, 2028, 2029, 2030],
108
+ ) -> None:
109
+ for dataset in datasets:
110
+ from policyengine_uk import Microsimulation
111
+
112
+ sim = Microsimulation(dataset=dataset)
113
+ for year in years:
114
+ year_dataset = sim.dataset[year]
115
+
116
+ # Convert to pandas DataFrames and add weight columns
117
+ person_df = pd.DataFrame(year_dataset.person)
118
+ benunit_df = pd.DataFrame(year_dataset.benunit)
119
+ household_df = pd.DataFrame(year_dataset.household)
120
+
121
+ # Map household weights to person and benunit levels
122
+ person_df = person_df.merge(
123
+ household_df[["household_id", "household_weight"]],
124
+ left_on="person_household_id",
125
+ right_on="household_id",
126
+ how="left",
127
+ )
128
+ person_df = person_df.rename(
129
+ columns={"household_weight": "person_weight"}
130
+ )
131
+ person_df = person_df.drop(columns=["household_id"])
132
+
133
+ # Get household_id for each benunit from person table
134
+ benunit_household_map = person_df[
135
+ ["person_benunit_id", "person_household_id"]
136
+ ].drop_duplicates()
137
+ benunit_df = benunit_df.merge(
138
+ benunit_household_map,
139
+ left_on="benunit_id",
140
+ right_on="person_benunit_id",
141
+ how="left",
142
+ )
143
+ benunit_df = benunit_df.merge(
144
+ household_df[["household_id", "household_weight"]],
145
+ left_on="person_household_id",
146
+ right_on="household_id",
147
+ how="left",
148
+ )
149
+ benunit_df = benunit_df.rename(
150
+ columns={"household_weight": "benunit_weight"}
151
+ )
152
+ benunit_df = benunit_df.drop(
153
+ columns=[
154
+ "person_benunit_id",
155
+ "person_household_id",
156
+ "household_id",
157
+ ],
158
+ errors="ignore",
159
+ )
160
+
161
+ uk_dataset = PolicyEngineUKDataset(
162
+ name=f"{dataset}-year-{year}",
163
+ description=f"UK Dataset for year {year} based on {dataset}",
164
+ filepath=f"./data/{Path(dataset).stem}_year_{year}.h5",
165
+ year=year,
166
+ data=UKYearData(
167
+ person=MicroDataFrame(person_df, weights="person_weight"),
168
+ benunit=MicroDataFrame(
169
+ benunit_df, weights="benunit_weight"
170
+ ),
171
+ household=MicroDataFrame(
172
+ household_df, weights="household_weight"
173
+ ),
174
+ ),
175
+ )
176
+ uk_dataset.save()
@@ -0,0 +1,268 @@
1
+ import datetime
2
+ from importlib.metadata import version
3
+ from pathlib import Path
4
+ from typing import TYPE_CHECKING
5
+
6
+ import pandas as pd
7
+ import requests
8
+ from microdf import MicroDataFrame
9
+
10
+ from policyengine.core import (
11
+ Parameter,
12
+ ParameterValue,
13
+ TaxBenefitModel,
14
+ TaxBenefitModelVersion,
15
+ Variable,
16
+ )
17
+ from policyengine.utils import parse_safe_date
18
+
19
+ from .datasets import PolicyEngineUKDataset, UKYearData
20
+
21
+ if TYPE_CHECKING:
22
+ from policyengine.core.simulation import Simulation
23
+
24
+
25
+ class PolicyEngineUK(TaxBenefitModel):
26
+ id: str = "policyengine-uk"
27
+ description: str = "The UK's open-source dynamic tax and benefit microsimulation model maintained by PolicyEngine."
28
+
29
+
30
+ uk_model = PolicyEngineUK()
31
+
32
+ pkg_version = version("policyengine-uk")
33
+
34
+ # Get published time from PyPI
35
+ response = requests.get("https://pypi.org/pypi/policyengine-uk/json")
36
+ data = response.json()
37
+ upload_time = data["releases"][pkg_version][0]["upload_time_iso_8601"]
38
+
39
+
40
+ class PolicyEngineUKLatest(TaxBenefitModelVersion):
41
+ model: TaxBenefitModel = uk_model
42
+ version: str = pkg_version
43
+ created_at: datetime.datetime = datetime.datetime.fromisoformat(
44
+ upload_time
45
+ )
46
+
47
+ def __init__(self, **kwargs: dict):
48
+ super().__init__(**kwargs)
49
+ from policyengine_core.enums import Enum
50
+ from policyengine_uk.system import system
51
+
52
+ self.id = f"{self.model.id}@{self.version}"
53
+
54
+ self.variables = []
55
+ for var_obj in system.variables.values():
56
+ variable = Variable(
57
+ id=self.id + "-" + var_obj.name,
58
+ name=var_obj.name,
59
+ tax_benefit_model_version=self,
60
+ entity=var_obj.entity.key,
61
+ description=var_obj.documentation,
62
+ data_type=var_obj.value_type
63
+ if var_obj.value_type is not Enum
64
+ else str,
65
+ )
66
+ if (
67
+ hasattr(var_obj, "possible_values")
68
+ and var_obj.possible_values is not None
69
+ ):
70
+ variable.possible_values = list(
71
+ map(
72
+ lambda x: x.name,
73
+ var_obj.possible_values._value2member_map_.values(),
74
+ )
75
+ )
76
+ self.variables.append(variable)
77
+
78
+ self.parameters = []
79
+ from policyengine_core.parameters import Parameter as CoreParameter
80
+
81
+ for param_node in system.parameters.get_descendants():
82
+ if isinstance(param_node, CoreParameter):
83
+ parameter = Parameter(
84
+ id=self.id + "-" + param_node.name,
85
+ name=param_node.name,
86
+ tax_benefit_model_version=self,
87
+ description=param_node.description,
88
+ data_type=type(
89
+ param_node(2025)
90
+ ), # Example year to infer type
91
+ unit=param_node.metadata.get("unit"),
92
+ )
93
+ self.parameters.append(parameter)
94
+
95
+ for i in range(len(param_node.values_list)):
96
+ param_at_instant = param_node.values_list[i]
97
+ if i + 1 < len(param_node.values_list):
98
+ next_instant = param_node.values_list[i + 1]
99
+ else:
100
+ next_instant = None
101
+ parameter_value = ParameterValue(
102
+ parameter=parameter,
103
+ start_date=parse_safe_date(
104
+ param_at_instant.instant_str
105
+ ),
106
+ end_date=parse_safe_date(next_instant.instant_str)
107
+ if next_instant
108
+ else None,
109
+ value=param_at_instant.value,
110
+ )
111
+ self.parameter_values.append(parameter_value)
112
+
113
+ def run(self, simulation: "Simulation") -> "Simulation":
114
+ from policyengine_uk import Microsimulation
115
+ from policyengine_uk.data import UKSingleYearDataset
116
+
117
+ from policyengine.utils.parametric_reforms import (
118
+ simulation_modifier_from_parameter_values,
119
+ )
120
+
121
+ assert isinstance(simulation.dataset, PolicyEngineUKDataset)
122
+
123
+ dataset = simulation.dataset
124
+ dataset.load()
125
+ input_data = UKSingleYearDataset(
126
+ person=dataset.data.person,
127
+ benunit=dataset.data.benunit,
128
+ household=dataset.data.household,
129
+ fiscal_year=dataset.year,
130
+ )
131
+ microsim = Microsimulation(dataset=input_data)
132
+
133
+ if (
134
+ simulation.policy
135
+ and simulation.policy.simulation_modifier is not None
136
+ ):
137
+ simulation.policy.simulation_modifier(microsim)
138
+ elif simulation.policy:
139
+ modifier = simulation_modifier_from_parameter_values(
140
+ simulation.policy.parameter_values
141
+ )
142
+ modifier(microsim)
143
+
144
+ if (
145
+ simulation.dynamic
146
+ and simulation.dynamic.simulation_modifier is not None
147
+ ):
148
+ simulation.dynamic.simulation_modifier(microsim)
149
+ elif simulation.dynamic:
150
+ modifier = simulation_modifier_from_parameter_values(
151
+ simulation.dynamic.parameter_values
152
+ )
153
+ modifier(microsim)
154
+
155
+ # Allow custom variable selection, or use defaults
156
+ if simulation.variables is not None:
157
+ entity_variables = simulation.variables
158
+ else:
159
+ # Default comprehensive variable set
160
+ entity_variables = {
161
+ "person": [
162
+ # IDs and weights
163
+ "person_id",
164
+ "benunit_id",
165
+ "household_id",
166
+ "person_weight",
167
+ # Demographics
168
+ "age",
169
+ "gender",
170
+ "is_adult",
171
+ "is_SP_age",
172
+ "is_child",
173
+ # Income
174
+ "employment_income",
175
+ "self_employment_income",
176
+ "pension_income",
177
+ "private_pension_income",
178
+ "savings_interest_income",
179
+ "dividend_income",
180
+ "property_income",
181
+ "total_income",
182
+ "earned_income",
183
+ # Benefits
184
+ "universal_credit",
185
+ "child_benefit",
186
+ "pension_credit",
187
+ "income_support",
188
+ "working_tax_credit",
189
+ "child_tax_credit",
190
+ # Tax
191
+ "income_tax",
192
+ "national_insurance",
193
+ ],
194
+ "benunit": [
195
+ # IDs and weights
196
+ "benunit_id",
197
+ "benunit_weight",
198
+ # Structure
199
+ "family_type",
200
+ # Income and benefits
201
+ "universal_credit",
202
+ "child_benefit",
203
+ "working_tax_credit",
204
+ "child_tax_credit",
205
+ ],
206
+ "household": [
207
+ # IDs and weights
208
+ "household_id",
209
+ "household_weight",
210
+ # Income measures
211
+ "household_net_income",
212
+ "hbai_household_net_income",
213
+ "equiv_hbai_household_net_income",
214
+ "household_market_income",
215
+ "household_gross_income",
216
+ # Benefits and tax
217
+ "household_benefits",
218
+ "household_tax",
219
+ "vat",
220
+ # Housing
221
+ "rent",
222
+ "council_tax",
223
+ "tenure_type",
224
+ ],
225
+ }
226
+
227
+ data = {
228
+ "person": pd.DataFrame(),
229
+ "benunit": pd.DataFrame(),
230
+ "household": pd.DataFrame(),
231
+ }
232
+
233
+ for entity, variables in entity_variables.items():
234
+ for var in variables:
235
+ data[entity][var] = microsim.calculate(
236
+ var, period=simulation.dataset.year, map_to=entity
237
+ ).values
238
+
239
+ data["person"] = MicroDataFrame(
240
+ data["person"], weights="person_weight"
241
+ )
242
+ data["benunit"] = MicroDataFrame(
243
+ data["benunit"], weights="benunit_weight"
244
+ )
245
+ data["household"] = MicroDataFrame(
246
+ data["household"], weights="household_weight"
247
+ )
248
+
249
+ simulation.output_dataset = PolicyEngineUKDataset(
250
+ name=dataset.name,
251
+ description=dataset.description,
252
+ filepath=str(
253
+ Path(simulation.dataset.filepath).parent
254
+ / (simulation.id + ".h5")
255
+ ),
256
+ year=simulation.dataset.year,
257
+ is_output_dataset=True,
258
+ data=UKYearData(
259
+ person=data["person"],
260
+ benunit=data["benunit"],
261
+ household=data["household"],
262
+ ),
263
+ )
264
+
265
+ simulation.output_dataset.save()
266
+
267
+
268
+ uk_latest = PolicyEngineUKLatest()
@@ -0,0 +1,108 @@
1
+ """UK-specific output templates."""
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from pydantic import ConfigDict
6
+
7
+ from policyengine.core import Output
8
+ from policyengine.outputs.aggregate import Aggregate, AggregateType
9
+ from policyengine.outputs.change_aggregate import (
10
+ ChangeAggregate,
11
+ ChangeAggregateType,
12
+ )
13
+
14
+ if TYPE_CHECKING:
15
+ from policyengine.core.simulation import Simulation
16
+
17
+
18
+ class ProgrammeStatistics(Output):
19
+ """Single programme's statistics from a policy reform - represents one database row."""
20
+
21
+ model_config = ConfigDict(arbitrary_types_allowed=True)
22
+
23
+ baseline_simulation: "Simulation"
24
+ reform_simulation: "Simulation"
25
+ programme_name: str
26
+ entity: str
27
+ is_tax: bool = False
28
+
29
+ # Results populated by run()
30
+ baseline_total: float | None = None
31
+ reform_total: float | None = None
32
+ change: float | None = None
33
+ baseline_count: float | None = None
34
+ reform_count: float | None = None
35
+ winners: float | None = None
36
+ losers: float | None = None
37
+
38
+ def run(self):
39
+ """Calculate statistics for this programme."""
40
+ # Baseline totals
41
+ baseline_total = Aggregate(
42
+ simulation=self.baseline_simulation,
43
+ variable=self.programme_name,
44
+ aggregate_type=AggregateType.SUM,
45
+ entity=self.entity,
46
+ )
47
+ baseline_total.run()
48
+
49
+ # Reform totals
50
+ reform_total = Aggregate(
51
+ simulation=self.reform_simulation,
52
+ variable=self.programme_name,
53
+ aggregate_type=AggregateType.SUM,
54
+ entity=self.entity,
55
+ )
56
+ reform_total.run()
57
+
58
+ # Count of recipients/payers (baseline)
59
+ baseline_count = Aggregate(
60
+ simulation=self.baseline_simulation,
61
+ variable=self.programme_name,
62
+ aggregate_type=AggregateType.COUNT,
63
+ entity=self.entity,
64
+ filter_variable=self.programme_name,
65
+ filter_variable_geq=0.01,
66
+ )
67
+ baseline_count.run()
68
+
69
+ # Count of recipients/payers (reform)
70
+ reform_count = Aggregate(
71
+ simulation=self.reform_simulation,
72
+ variable=self.programme_name,
73
+ aggregate_type=AggregateType.COUNT,
74
+ entity=self.entity,
75
+ filter_variable=self.programme_name,
76
+ filter_variable_geq=0.01,
77
+ )
78
+ reform_count.run()
79
+
80
+ # Winners and losers
81
+ winners = ChangeAggregate(
82
+ baseline_simulation=self.baseline_simulation,
83
+ reform_simulation=self.reform_simulation,
84
+ variable=self.programme_name,
85
+ aggregate_type=ChangeAggregateType.COUNT,
86
+ entity=self.entity,
87
+ change_geq=0.01 if not self.is_tax else -0.01,
88
+ )
89
+ winners.run()
90
+
91
+ losers = ChangeAggregate(
92
+ baseline_simulation=self.baseline_simulation,
93
+ reform_simulation=self.reform_simulation,
94
+ variable=self.programme_name,
95
+ aggregate_type=ChangeAggregateType.COUNT,
96
+ entity=self.entity,
97
+ change_leq=-0.01 if not self.is_tax else 0.01,
98
+ )
99
+ losers.run()
100
+
101
+ # Populate results
102
+ self.baseline_total = float(baseline_total.result)
103
+ self.reform_total = float(reform_total.result)
104
+ self.change = float(reform_total.result - baseline_total.result)
105
+ self.baseline_count = float(baseline_count.result)
106
+ self.reform_count = float(reform_count.result)
107
+ self.winners = float(winners.result)
108
+ self.losers = float(losers.result)
@@ -0,0 +1,33 @@
1
+ """PolicyEngine UK tax-benefit model - imports from uk/ module."""
2
+
3
+ from .uk import (
4
+ PolicyEngineUK,
5
+ PolicyEngineUKDataset,
6
+ PolicyEngineUKLatest,
7
+ ProgrammeStatistics,
8
+ UKYearData,
9
+ create_datasets,
10
+ general_policy_reform_analysis,
11
+ uk_latest,
12
+ uk_model,
13
+ )
14
+
15
+ __all__ = [
16
+ "UKYearData",
17
+ "PolicyEngineUKDataset",
18
+ "create_datasets",
19
+ "PolicyEngineUK",
20
+ "PolicyEngineUKLatest",
21
+ "uk_model",
22
+ "uk_latest",
23
+ "general_policy_reform_analysis",
24
+ "ProgrammeStatistics",
25
+ ]
26
+
27
+ # Rebuild models to resolve forward references
28
+ from policyengine.core import Dataset
29
+
30
+ Dataset.model_rebuild()
31
+ UKYearData.model_rebuild()
32
+ PolicyEngineUKDataset.model_rebuild()
33
+ PolicyEngineUKLatest.model_rebuild()
@@ -0,0 +1,36 @@
1
+ """PolicyEngine US tax-benefit model."""
2
+
3
+ from importlib.util import find_spec
4
+
5
+ if find_spec("policyengine_us") is not None:
6
+ from policyengine.core import Dataset
7
+
8
+ from .analysis import general_policy_reform_analysis
9
+ from .datasets import PolicyEngineUSDataset, USYearData, create_datasets
10
+ from .model import (
11
+ PolicyEngineUS,
12
+ PolicyEngineUSLatest,
13
+ us_latest,
14
+ us_model,
15
+ )
16
+ from .outputs import ProgramStatistics
17
+
18
+ # Rebuild Pydantic models to resolve forward references
19
+ Dataset.model_rebuild()
20
+ USYearData.model_rebuild()
21
+ PolicyEngineUSDataset.model_rebuild()
22
+ PolicyEngineUSLatest.model_rebuild()
23
+
24
+ __all__ = [
25
+ "USYearData",
26
+ "PolicyEngineUSDataset",
27
+ "create_datasets",
28
+ "PolicyEngineUS",
29
+ "PolicyEngineUSLatest",
30
+ "us_model",
31
+ "us_latest",
32
+ "general_policy_reform_analysis",
33
+ "ProgramStatistics",
34
+ ]
35
+ else:
36
+ __all__ = []