policyengine 5.2.0__tar.gz → 5.3.0__tar.gz

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 (285) hide show
  1. {policyengine-5.2.0 → policyengine-5.3.0}/CHANGELOG.md +7 -0
  2. {policyengine-5.2.0 → policyengine-5.3.0}/PKG-INFO +1 -1
  3. policyengine-5.3.0/examples/belgium_axiom_pilot.py +75 -0
  4. {policyengine-5.2.0 → policyengine-5.3.0}/pyproject.toml +1 -1
  5. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/data/bundle/manifest.json +10 -10
  6. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/data/bundle/uk.trace.tro.jsonld +5 -5
  7. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/data/bundle/us.trace.tro.jsonld +5 -5
  8. policyengine-5.3.0/src/policyengine/tax_benefit_models/be/__init__.py +29 -0
  9. policyengine-5.3.0/src/policyengine/tax_benefit_models/be/datasets.py +190 -0
  10. policyengine-5.3.0/src/policyengine/tax_benefit_models/be/model.py +363 -0
  11. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine.egg-info/PKG-INFO +1 -1
  12. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine.egg-info/SOURCES.txt +5 -0
  13. policyengine-5.3.0/tests/test_be_axiom_pilot.py +613 -0
  14. {policyengine-5.2.0 → policyengine-5.3.0}/.claude/policyengine-guide.md +0 -0
  15. {policyengine-5.2.0 → policyengine-5.3.0}/.claude/quick-reference.md +0 -0
  16. {policyengine-5.2.0 → policyengine-5.3.0}/.github/CONTRIBUTING.md +0 -0
  17. {policyengine-5.2.0 → policyengine-5.3.0}/.github/bump_version.py +0 -0
  18. {policyengine-5.2.0 → policyengine-5.3.0}/.github/capture-version.sh +0 -0
  19. {policyengine-5.2.0 → policyengine-5.3.0}/.github/changelog_template.md +0 -0
  20. {policyengine-5.2.0 → policyengine-5.3.0}/.github/check-changelog.sh +0 -0
  21. {policyengine-5.2.0 → policyengine-5.3.0}/.github/copilot-instructions.md +0 -0
  22. {policyengine-5.2.0 → policyengine-5.3.0}/.github/dispatch-policyengine-release.sh +0 -0
  23. {policyengine-5.2.0 → policyengine-5.3.0}/.github/fetch_version.py +0 -0
  24. {policyengine-5.2.0 → policyengine-5.3.0}/.github/get-changelog-diff.sh +0 -0
  25. {policyengine-5.2.0 → policyengine-5.3.0}/.github/has-functional-changes.sh +0 -0
  26. {policyengine-5.2.0 → policyengine-5.3.0}/.github/is-version-number-acceptable.sh +0 -0
  27. {policyengine-5.2.0 → policyengine-5.3.0}/.github/publish-git-tag.sh +0 -0
  28. {policyengine-5.2.0 → policyengine-5.3.0}/.github/wait-for-pypi.sh +0 -0
  29. {policyengine-5.2.0 → policyengine-5.3.0}/.github/workflows/draft-pdf.yml +0 -0
  30. {policyengine-5.2.0 → policyengine-5.3.0}/.github/workflows/pr_code_changes.yaml +0 -0
  31. {policyengine-5.2.0 → policyengine-5.3.0}/.github/workflows/pr_docs_changes.yaml +0 -0
  32. {policyengine-5.2.0 → policyengine-5.3.0}/.github/workflows/push.yaml +0 -0
  33. {policyengine-5.2.0 → policyengine-5.3.0}/.gitignore +0 -0
  34. {policyengine-5.2.0 → policyengine-5.3.0}/.python-version +0 -0
  35. {policyengine-5.2.0 → policyengine-5.3.0}/AGENTS.md +0 -0
  36. {policyengine-5.2.0 → policyengine-5.3.0}/CITATION.cff +0 -0
  37. {policyengine-5.2.0 → policyengine-5.3.0}/CLAUDE.md +0 -0
  38. {policyengine-5.2.0 → policyengine-5.3.0}/CODE_OF_CONDUCT.md +0 -0
  39. {policyengine-5.2.0 → policyengine-5.3.0}/CONTRIBUTING.md +0 -0
  40. {policyengine-5.2.0 → policyengine-5.3.0}/LICENSE +0 -0
  41. {policyengine-5.2.0 → policyengine-5.3.0}/Makefile +0 -0
  42. {policyengine-5.2.0 → policyengine-5.3.0}/README.md +0 -0
  43. {policyengine-5.2.0 → policyengine-5.3.0}/architecture.png +0 -0
  44. {policyengine-5.2.0 → policyengine-5.3.0}/architecture.svg +0 -0
  45. {policyengine-5.2.0 → policyengine-5.3.0}/changelog.d/.gitkeep +0 -0
  46. {policyengine-5.2.0 → policyengine-5.3.0}/docs/.gitignore +0 -0
  47. {policyengine-5.2.0 → policyengine-5.3.0}/docs/_generator/README.md +0 -0
  48. {policyengine-5.2.0 → policyengine-5.3.0}/docs/_generator/build_reference.py +0 -0
  49. {policyengine-5.2.0 → policyengine-5.3.0}/docs/_quarto.yml +0 -0
  50. {policyengine-5.2.0 → policyengine-5.3.0}/docs/bundles.md +0 -0
  51. {policyengine-5.2.0 → policyengine-5.3.0}/docs/countries.md +0 -0
  52. {policyengine-5.2.0 → policyengine-5.3.0}/docs/data-publishing-design.md +0 -0
  53. {policyengine-5.2.0 → policyengine-5.3.0}/docs/decile-impact-spec.md +0 -0
  54. {policyengine-5.2.0 → policyengine-5.3.0}/docs/dev.md +0 -0
  55. {policyengine-5.2.0 → policyengine-5.3.0}/docs/engineering/runbooks/build-m-us-populace-certification.md +0 -0
  56. {policyengine-5.2.0 → policyengine-5.3.0}/docs/engineering/skills/README.md +0 -0
  57. {policyengine-5.2.0 → policyengine-5.3.0}/docs/engineering/skills/data-certification.md +0 -0
  58. {policyengine-5.2.0 → policyengine-5.3.0}/docs/engineering/skills/github-prs.md +0 -0
  59. {policyengine-5.2.0 → policyengine-5.3.0}/docs/engineering/skills/repository-guidance.md +0 -0
  60. {policyengine-5.2.0 → policyengine-5.3.0}/docs/examples.md +0 -0
  61. {policyengine-5.2.0 → policyengine-5.3.0}/docs/getting-started.md +0 -0
  62. {policyengine-5.2.0 → policyengine-5.3.0}/docs/households.md +0 -0
  63. {policyengine-5.2.0 → policyengine-5.3.0}/docs/impact-analysis.md +0 -0
  64. {policyengine-5.2.0 → policyengine-5.3.0}/docs/index.md +0 -0
  65. {policyengine-5.2.0 → policyengine-5.3.0}/docs/methodology/index.md +0 -0
  66. {policyengine-5.2.0 → policyengine-5.3.0}/docs/methodology/model-architecture.md +0 -0
  67. {policyengine-5.2.0 → policyengine-5.3.0}/docs/methodology/us-health-costs.md +0 -0
  68. {policyengine-5.2.0 → policyengine-5.3.0}/docs/microsim.md +0 -0
  69. {policyengine-5.2.0 → policyengine-5.3.0}/docs/outputs.md +0 -0
  70. {policyengine-5.2.0 → policyengine-5.3.0}/docs/programs/index.md +0 -0
  71. {policyengine-5.2.0 → policyengine-5.3.0}/docs/programs/us-chip.md +0 -0
  72. {policyengine-5.2.0 → policyengine-5.3.0}/docs/reference/index.md +0 -0
  73. {policyengine-5.2.0 → policyengine-5.3.0}/docs/reforms.md +0 -0
  74. {policyengine-5.2.0 → policyengine-5.3.0}/docs/regions.md +0 -0
  75. {policyengine-5.2.0 → policyengine-5.3.0}/docs/release-bundles.md +0 -0
  76. {policyengine-5.2.0 → policyengine-5.3.0}/docs/run-records.md +0 -0
  77. {policyengine-5.2.0 → policyengine-5.3.0}/docs/trace-case-study.md +0 -0
  78. {policyengine-5.2.0 → policyengine-5.3.0}/docs/visualisation.md +0 -0
  79. {policyengine-5.2.0 → policyengine-5.3.0}/examples/employment_income_variation_uk.py +0 -0
  80. {policyengine-5.2.0 → policyengine-5.3.0}/examples/employment_income_variation_us.py +0 -0
  81. {policyengine-5.2.0 → policyengine-5.3.0}/examples/household_impact_example.py +0 -0
  82. {policyengine-5.2.0 → policyengine-5.3.0}/examples/income_bands_uk.py +0 -0
  83. {policyengine-5.2.0 → policyengine-5.3.0}/examples/income_distribution_us.py +0 -0
  84. {policyengine-5.2.0 → policyengine-5.3.0}/examples/paper_repro_uk.py +0 -0
  85. {policyengine-5.2.0 → policyengine-5.3.0}/examples/policy_change_uk.py +0 -0
  86. {policyengine-5.2.0 → policyengine-5.3.0}/examples/speedtest_us_simulation.py +0 -0
  87. {policyengine-5.2.0 → policyengine-5.3.0}/examples/us_budgetary_impact.py +0 -0
  88. {policyengine-5.2.0 → policyengine-5.3.0}/paper-preview.html +0 -0
  89. {policyengine-5.2.0 → policyengine-5.3.0}/paper.bib +0 -0
  90. {policyengine-5.2.0 → policyengine-5.3.0}/paper.md +0 -0
  91. {policyengine-5.2.0 → policyengine-5.3.0}/scripts/bundle.py +0 -0
  92. {policyengine-5.2.0 → policyengine-5.3.0}/scripts/certify_data_release.py +0 -0
  93. {policyengine-5.2.0 → policyengine-5.3.0}/scripts/check_data_staleness.py +0 -0
  94. {policyengine-5.2.0 → policyengine-5.3.0}/scripts/export_bundle_release_assets.py +0 -0
  95. {policyengine-5.2.0 → policyengine-5.3.0}/scripts/generate_bundle_artifacts.py +0 -0
  96. {policyengine-5.2.0 → policyengine-5.3.0}/scripts/generate_trace_tros.py +0 -0
  97. {policyengine-5.2.0 → policyengine-5.3.0}/scripts/prepare_package_bundle_update.py +0 -0
  98. {policyengine-5.2.0 → policyengine-5.3.0}/setup.cfg +0 -0
  99. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/__init__.py +0 -0
  100. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/bundle.py +0 -0
  101. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/cli.py +0 -0
  102. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/core/__init__.py +0 -0
  103. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/core/cache.py +0 -0
  104. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/core/dataset.py +0 -0
  105. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/core/dynamic.py +0 -0
  106. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/core/output.py +0 -0
  107. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/core/parameter.py +0 -0
  108. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/core/parameter_node.py +0 -0
  109. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/core/parameter_value.py +0 -0
  110. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/core/policy.py +0 -0
  111. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/core/region.py +0 -0
  112. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/core/run_record.py +0 -0
  113. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/core/scoping_strategy.py +0 -0
  114. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/core/simulation.py +0 -0
  115. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/core/tax_benefit_model.py +0 -0
  116. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/core/tax_benefit_model_version.py +0 -0
  117. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/core/variable.py +0 -0
  118. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/countries/__init__.py +0 -0
  119. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/countries/uk/__init__.py +0 -0
  120. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/countries/uk/regions.py +0 -0
  121. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/countries/us/__init__.py +0 -0
  122. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/countries/us/data/__init__.py +0 -0
  123. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/countries/us/data/districts.py +0 -0
  124. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/countries/us/data/places.py +0 -0
  125. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/countries/us/data/states.py +0 -0
  126. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/countries/us/regions.py +0 -0
  127. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/data/__init__.py +0 -0
  128. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/data/schemas/trace_tro.schema.json +0 -0
  129. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/data/uk_geography_assets.py +0 -0
  130. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/derivations/__init__.py +0 -0
  131. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/derivations/narrate.py +0 -0
  132. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/derivations/trace.py +0 -0
  133. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/graph/__init__.py +0 -0
  134. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/graph/extractor.py +0 -0
  135. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/graph/graph.py +0 -0
  136. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/outputs/__init__.py +0 -0
  137. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/outputs/aggregate.py +0 -0
  138. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/outputs/change_aggregate.py +0 -0
  139. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/outputs/cliff_impact.py +0 -0
  140. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/outputs/congressional_district_impact.py +0 -0
  141. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/outputs/constituency_impact.py +0 -0
  142. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/outputs/decile_analysis.py +0 -0
  143. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/outputs/decile_grouping.py +0 -0
  144. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/outputs/decile_impact.py +0 -0
  145. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/outputs/extra_variables.py +0 -0
  146. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/outputs/inequality.py +0 -0
  147. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/outputs/intra_decile_impact.py +0 -0
  148. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/outputs/labor_supply_response.py +0 -0
  149. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/outputs/local_authority_impact.py +0 -0
  150. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/outputs/poverty.py +0 -0
  151. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/outputs/program_statistics.py +0 -0
  152. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/outputs/uk_geography_assets.py +0 -0
  153. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/outputs/uk_geography_impact.py +0 -0
  154. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/provenance/__init__.py +0 -0
  155. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/provenance/certification.py +0 -0
  156. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/provenance/dataset_materialization.py +0 -0
  157. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/provenance/manifest.py +0 -0
  158. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/provenance/pyproject_pins.py +0 -0
  159. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/provenance/trace.py +0 -0
  160. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/provenance/verify.py +0 -0
  161. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/provenance/zenodo.py +0 -0
  162. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/results/__init__.py +0 -0
  163. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/results/schema.py +0 -0
  164. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/results/trace_tro.py +0 -0
  165. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/results/tracking.py +0 -0
  166. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/tax_benefit_models/common/__init__.py +0 -0
  167. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/tax_benefit_models/common/axes.py +0 -0
  168. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/tax_benefit_models/common/extra_variables.py +0 -0
  169. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/tax_benefit_models/common/household.py +0 -0
  170. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/tax_benefit_models/common/model_version.py +0 -0
  171. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/tax_benefit_models/common/reform.py +0 -0
  172. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/tax_benefit_models/common/result.py +0 -0
  173. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/tax_benefit_models/uk/__init__.py +0 -0
  174. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/tax_benefit_models/uk/analysis.py +0 -0
  175. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/tax_benefit_models/uk/datasets.py +0 -0
  176. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/tax_benefit_models/uk/household.py +0 -0
  177. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/tax_benefit_models/uk/model.py +0 -0
  178. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/tax_benefit_models/us/__init__.py +0 -0
  179. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/tax_benefit_models/us/analysis.py +0 -0
  180. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/tax_benefit_models/us/datasets.py +0 -0
  181. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/tax_benefit_models/us/household.py +0 -0
  182. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/tax_benefit_models/us/model.py +0 -0
  183. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/utils/__init__.py +0 -0
  184. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/utils/dates.py +0 -0
  185. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/utils/design.py +0 -0
  186. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/utils/entity_utils.py +0 -0
  187. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/utils/errors.py +0 -0
  188. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/utils/hashing.py +0 -0
  189. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/utils/household_validation.py +0 -0
  190. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/utils/parameter_labels.py +0 -0
  191. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/utils/parametric_reforms.py +0 -0
  192. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine/utils/plotting.py +0 -0
  193. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine.egg-info/dependency_links.txt +0 -0
  194. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine.egg-info/entry_points.txt +0 -0
  195. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine.egg-info/requires.txt +0 -0
  196. {policyengine-5.2.0 → policyengine-5.3.0}/src/policyengine.egg-info/top_level.txt +0 -0
  197. {policyengine-5.2.0 → policyengine-5.3.0}/tests/__init__.py +0 -0
  198. {policyengine-5.2.0 → policyengine-5.3.0}/tests/conftest.py +0 -0
  199. {policyengine-5.2.0 → policyengine-5.3.0}/tests/fixtures/__init__.py +0 -0
  200. {policyengine-5.2.0 → policyengine-5.3.0}/tests/fixtures/filtering_fixtures.py +0 -0
  201. {policyengine-5.2.0 → policyengine-5.3.0}/tests/fixtures/household_calculator_snapshots/uk_couple_two_kids.json +0 -0
  202. {policyengine-5.2.0 → policyengine-5.3.0}/tests/fixtures/household_calculator_snapshots/uk_model_surface.json +0 -0
  203. {policyengine-5.2.0 → policyengine-5.3.0}/tests/fixtures/household_calculator_snapshots/uk_single_adult_employment_income.json +0 -0
  204. {policyengine-5.2.0 → policyengine-5.3.0}/tests/fixtures/household_calculator_snapshots/uk_single_adult_no_income.json +0 -0
  205. {policyengine-5.2.0 → policyengine-5.3.0}/tests/fixtures/household_calculator_snapshots/uk_single_parent_one_child.json +0 -0
  206. {policyengine-5.2.0 → policyengine-5.3.0}/tests/fixtures/household_calculator_snapshots/us_married_two_kids_high_income.json +0 -0
  207. {policyengine-5.2.0 → policyengine-5.3.0}/tests/fixtures/household_calculator_snapshots/us_model_surface.json +0 -0
  208. {policyengine-5.2.0 → policyengine-5.3.0}/tests/fixtures/household_calculator_snapshots/us_single_adult_employment_income.json +0 -0
  209. {policyengine-5.2.0 → policyengine-5.3.0}/tests/fixtures/household_calculator_snapshots/us_single_adult_no_income.json +0 -0
  210. {policyengine-5.2.0 → policyengine-5.3.0}/tests/fixtures/household_calculator_snapshots/us_single_parent_one_child.json +0 -0
  211. {policyengine-5.2.0 → policyengine-5.3.0}/tests/fixtures/parameter_labels_fixtures.py +0 -0
  212. {policyengine-5.2.0 → policyengine-5.3.0}/tests/fixtures/parametric_reforms_fixtures.py +0 -0
  213. {policyengine-5.2.0 → policyengine-5.3.0}/tests/fixtures/poverty_by_demographics_fixtures.py +0 -0
  214. {policyengine-5.2.0 → policyengine-5.3.0}/tests/fixtures/region_fixtures.py +0 -0
  215. {policyengine-5.2.0 → policyengine-5.3.0}/tests/fixtures/us_reform_fixtures.py +0 -0
  216. {policyengine-5.2.0 → policyengine-5.3.0}/tests/fixtures/variable_label_fixtures.py +0 -0
  217. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_aggregate.py +0 -0
  218. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_budgetary_impact.py +0 -0
  219. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_bump_version.py +0 -0
  220. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_bundle.py +0 -0
  221. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_bundle_metadata.py +0 -0
  222. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_cache.py +0 -0
  223. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_certify_data_release.py +0 -0
  224. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_change_aggregate.py +0 -0
  225. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_cliff_impact.py +0 -0
  226. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_cliff_impact_analysis.py +0 -0
  227. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_congressional_district_impact.py +0 -0
  228. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_constituency_impact.py +0 -0
  229. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_dataset_materialization.py +0 -0
  230. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_dataset_persistence.py +0 -0
  231. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_dataset_runtime.py +0 -0
  232. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_dataset_source.py +0 -0
  233. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_decile_grouping.py +0 -0
  234. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_derivations.py +0 -0
  235. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_dict_reforms_on_simulation.py +0 -0
  236. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_entity_mapping.py +0 -0
  237. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_entity_utils.py +0 -0
  238. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_entity_utils_characterization.py +0 -0
  239. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_errors.py +0 -0
  240. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_extra_variables.py +0 -0
  241. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_filtering.py +0 -0
  242. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_graph/__init__.py +0 -0
  243. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_graph/conftest.py +0 -0
  244. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_graph/test_extractor.py +0 -0
  245. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_household_calculator_snapshot.py +0 -0
  246. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_household_impact.py +0 -0
  247. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_inequality.py +0 -0
  248. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_intra_decile_impact.py +0 -0
  249. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_labor_supply_response.py +0 -0
  250. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_local_authority_impact.py +0 -0
  251. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_manifest_version_mismatch.py +0 -0
  252. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_models.py +0 -0
  253. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_pandas3_compatibility.py +0 -0
  254. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_parameter_labels.py +0 -0
  255. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_parameter_nodes.py +0 -0
  256. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_parameter_values.py +0 -0
  257. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_parametric_reforms.py +0 -0
  258. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_poverty.py +0 -0
  259. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_poverty_by_demographics.py +0 -0
  260. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_poverty_run.py +0 -0
  261. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_preservation_mirror.py +0 -0
  262. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_program_statistics.py +0 -0
  263. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_pyproject_pins.py +0 -0
  264. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_region.py +0 -0
  265. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_region_group_strategy.py +0 -0
  266. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_release_manifests.py +0 -0
  267. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_results.py +0 -0
  268. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_run_record.py +0 -0
  269. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_scoping_strategy.py +0 -0
  270. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_small_follow_ups.py +0 -0
  271. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_trace_tro.py +0 -0
  272. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_trace_tro_verify.py +0 -0
  273. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_uk_analysis.py +0 -0
  274. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_uk_geography_assets.py +0 -0
  275. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_uk_program_statistics.py +0 -0
  276. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_uk_regions.py +0 -0
  277. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_us_long_term_datasets.py +0 -0
  278. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_us_microsim_structural_reforms.py +0 -0
  279. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_us_program_statistics.py +0 -0
  280. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_us_reform_application.py +0 -0
  281. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_us_regions.py +0 -0
  282. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_variable_labels.py +0 -0
  283. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_variable_metadata.py +0 -0
  284. {policyengine-5.2.0 → policyengine-5.3.0}/tests/test_zenodo_mirror.py +0 -0
  285. {policyengine-5.2.0 → policyengine-5.3.0}/uv.lock +0 -0
@@ -1,3 +1,10 @@
1
+ ## [5.3.0] - 2026-08-30
2
+
3
+ ### Added
4
+
5
+ - Belgium pilot: add an Axiom-backed model over Microcosm-BE entity tables with content-bound RuleSpec/engine provenance, separately labeled data and policy periods, metadata-preserving HDF5 round trips, nonuniform calibrated-weight coverage, and in-memory outputs that cannot overwrite their inputs.
6
+
7
+
1
8
  ## [5.2.0] - 2026-08-29
2
9
 
3
10
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: policyengine
3
- Version: 5.2.0
3
+ Version: 5.3.0
4
4
  Summary: A package to conduct policy analysis using PolicyEngine tax-benefit models.
5
5
  Author-email: PolicyEngine <hello@policyengine.org>
6
6
  License: GNU AFFERO GENERAL PUBLIC LICENSE
@@ -0,0 +1,75 @@
1
+ """Belgium population microsimulation through the Axiom rules engine.
2
+
3
+ Runs the calibrated populace-be pilot dataset (populace-us support records
4
+ reweighted to Statbel/SPF/ONSS/ONEM targets from PolicyEngine/ledger)
5
+ through the rulespec-be composed worker pipeline, and scores the aggregates
6
+ against Belgian administrative facts.
7
+
8
+ This is a demonstration of the engine channel, not a certified Belgian
9
+ model: the support records are American, and coverage is the worker slice
10
+ (employee SSC with the statutory low-wage work bonus, and individual PIT
11
+ before withholding with the fiscal work-bonus credit).
12
+
13
+ The pilot's unpublished source stack currently requires Python 3.14: install
14
+ ``microcosm-frame`` from PolicyEngine/microcosm, plus the
15
+ ``axiom-rules-engine`` Python wrapper and dense extension from source. Their
16
+ current checkouts must agree on the canonical RuleSpec-root loader API. The
17
+ output is intentionally in memory (``filepath=None``) so it cannot overwrite
18
+ the input artifact; set a distinct output filepath explicitly before saving.
19
+
20
+ Usage::
21
+
22
+ POPULACE_BE_DATASET=.../populace_be_pilot_2026.h5 \\
23
+ RULESPEC_BE_ROOT=.../rulespec-be \\
24
+ uv run python examples/belgium_axiom_pilot.py
25
+ """
26
+
27
+ import os
28
+
29
+ from policyengine.core.simulation import Simulation
30
+ from policyengine.tax_benefit_models.be import (
31
+ EMPLOYEE_SSC,
32
+ PIT_BEFORE_WITHHOLDING,
33
+ AxiomBelgiumPilot,
34
+ PopulaceBelgiumDataset,
35
+ )
36
+
37
+ DATASET = os.environ["POPULACE_BE_DATASET"]
38
+ RULESPEC = os.environ.get("RULESPEC_BE_ROOT", "~/TheAxiomFoundation/rulespec-be")
39
+
40
+ # Ledger facts (PolicyEngine/ledger, Belgian publisher packages)
41
+ ONSS_WORKER_CONTRIBUTIONS_2024 = 20_836_582_673
42
+ SPF_PIT_BEFORE_WITHHOLDING_2023 = 62_840_116_134
43
+
44
+ dataset = PopulaceBelgiumDataset(
45
+ name="populace-be-pilot",
46
+ description="populace-us support reweighted to Belgian ledger targets",
47
+ filepath=DATASET,
48
+ year=2026,
49
+ )
50
+ model_version = AxiomBelgiumPilot(rulespec_root=RULESPEC, period=2025)
51
+
52
+ simulation = Simulation(dataset=dataset, tax_benefit_model_version=model_version)
53
+ simulation.run()
54
+
55
+ output = simulation.output_dataset
56
+ person = output.data.person
57
+ ssc_net = person[EMPLOYEE_SSC].sum()
58
+ pit = person[PIT_BEFORE_WITHHOLDING].sum()
59
+
60
+ print("Belgium pilot (Axiom engine over populace-be, worker slice)")
61
+ print(
62
+ f" data vintage {output.year}; policy period {output.policy_period}; "
63
+ f"model {model_version.version}"
64
+ )
65
+ print(f" employee SSC after work bonus EUR {ssc_net / 1e9:6.2f}B")
66
+ print(" (ONSS 2024 contributions: EUR 20.84B; the bonus is outsized here")
67
+ print(" because the US-support wage distribution is low-wage-heavy)")
68
+ print(
69
+ f" PIT before withholding EUR {pit / 1e9:6.2f}B "
70
+ "(SPF 2023, all PIT: EUR 62.84B)"
71
+ )
72
+ print(
73
+ f" PIT vs SPF ratio {pit / SPF_PIT_BEFORE_WITHHOLDING_2023:.3f}"
74
+ " (worker slice only)"
75
+ )
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "policyengine"
7
- version = "5.2.0"
7
+ version = "5.3.0"
8
8
  description = "A package to conduct policy analysis using PolicyEngine tax-benefit models."
9
9
  readme = "README.md"
10
10
  authors = [
@@ -1,10 +1,10 @@
1
1
  {
2
- "bundle_version": "5.2.0",
2
+ "bundle_version": "5.3.0",
3
3
  "citation": {
4
4
  "publisher": "PolicyEngine",
5
- "title": "PolicyEngine bundle 5.2.0",
5
+ "title": "PolicyEngine bundle 5.3.0",
6
6
  "type": "software-bundle",
7
- "version": "5.2.0"
7
+ "version": "5.3.0"
8
8
  },
9
9
  "countries": {
10
10
  "uk": {
@@ -17,7 +17,7 @@
17
17
  "data_releases": {
18
18
  "uk": {
19
19
  "build_id": "policyengine-uk-data-1.56.16",
20
- "bundle_id": "uk-5.2.0",
20
+ "bundle_id": "uk-5.3.0",
21
21
  "certification": {
22
22
  "built_with_model_git_sha": "12a1e028afeef08d8b2d74ee03fd9de3a78b2dd3",
23
23
  "built_with_model_version": "2.89.2",
@@ -99,7 +99,7 @@
99
99
  "version": "2.90.2",
100
100
  "wheel_url": "https://files.pythonhosted.org/packages/ec/da/658bff2df6094bd7bf2b0f561ff60fd41508adbd0a3af29b4553c83bc678/policyengine_uk-2.90.2-py3-none-any.whl"
101
101
  },
102
- "policyengine_version": "5.2.0",
102
+ "policyengine_version": "5.3.0",
103
103
  "region_datasets": {
104
104
  "national": {
105
105
  "path_template": "enhanced_frs_2024_25.h5"
@@ -112,7 +112,7 @@
112
112
  },
113
113
  "us": {
114
114
  "build_id": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z",
115
- "bundle_id": "us-5.2.0",
115
+ "bundle_id": "us-5.3.0",
116
116
  "certification": {
117
117
  "built_with_model_version": "1.764.6",
118
118
  "certified_by": "policyengine.py bundle certification",
@@ -186,7 +186,7 @@
186
186
  "version": "1.764.6",
187
187
  "wheel_url": "https://files.pythonhosted.org/packages/e2/5a/0566666f30ad06e416f0cf25b24bdee73acd71897fb2de3c0ac43b59e3e3/policyengine_us-1.764.6-py3-none-any.whl"
188
188
  },
189
- "policyengine_version": "5.2.0",
189
+ "policyengine_version": "5.3.0",
190
190
  "region_datasets": {
191
191
  "national": {
192
192
  "path_template": "populace_us_2024.h5"
@@ -254,10 +254,10 @@
254
254
  "packages": {
255
255
  "policyengine": {
256
256
  "import_name": "policyengine",
257
- "install_requirement": "policyengine==5.2.0",
257
+ "install_requirement": "policyengine==5.3.0",
258
258
  "name": "policyengine",
259
259
  "role": "bundle_carrier",
260
- "version": "5.2.0"
260
+ "version": "5.3.0"
261
261
  },
262
262
  "policyengine-core": {
263
263
  "import_name": "policyengine_core",
@@ -283,6 +283,6 @@
283
283
  "version": "1.764.6"
284
284
  }
285
285
  },
286
- "policyengine_version": "5.2.0",
286
+ "policyengine_version": "5.3.0",
287
287
  "schema_version": 2
288
288
  }
@@ -18,18 +18,18 @@
18
18
  "schema:url": "https://policyengine.org"
19
19
  },
20
20
  "schema:dateCreated": "2026-08-18T14:20:32.100961Z",
21
- "schema:description": "TRACE TRO for certified runtime bundle uk-5.2.0 covering the bundle manifest, the certified dataset artifact, the country model wheel, and the country data release manifest when it is available.",
21
+ "schema:description": "TRACE TRO for certified runtime bundle uk-5.3.0 covering the bundle manifest, the certified dataset artifact, the country model wheel, and the country data release manifest when it is available.",
22
22
  "schema:name": "policyengine uk certified bundle TRO",
23
23
  "trov:createdWith": {
24
24
  "@type": "schema:SoftwareApplication",
25
25
  "schema:name": "policyengine",
26
- "schema:softwareVersion": "5.2.0"
26
+ "schema:softwareVersion": "5.3.0"
27
27
  },
28
28
  "trov:hasArrangement": [
29
29
  {
30
30
  "@id": "arrangement/1",
31
31
  "@type": "trov:ArtifactArrangement",
32
- "rdfs:comment": "Certified arrangement for bundle uk-5.2.0.",
32
+ "rdfs:comment": "Certified arrangement for bundle uk-5.3.0.",
33
33
  "trov:hasArtifactLocation": [
34
34
  {
35
35
  "@id": "arrangement/1/location/bundle_manifest",
@@ -75,7 +75,7 @@
75
75
  "@type": "trov:ResearchArtifact",
76
76
  "schema:name": "policyengine.py bundle manifest for uk",
77
77
  "trov:mimeType": "application/json",
78
- "trov:sha256": "623ea4f52023a9030cbae4ba689ac7d57195a313e735473bbbe63f54bbf89dbe"
78
+ "trov:sha256": "57c6fe9c5c370ea2b9460120656004b1a583db06cabb6c774551effc3c125695"
79
79
  },
80
80
  {
81
81
  "@id": "composition/1/artifact/data_release_manifest",
@@ -102,7 +102,7 @@
102
102
  "trov:hasFingerprint": {
103
103
  "@id": "composition/1/fingerprint",
104
104
  "@type": "trov:CompositionFingerprint",
105
- "trov:sha256": "1ceaa12197f71695c9f09d4ffa1479319addd03570764f89d22136f08f791ead"
105
+ "trov:sha256": "90a113dd7e642af4c137af233b512fa7ff68b5ba0aa9b4102b7dd02bd8fe5cbf"
106
106
  }
107
107
  },
108
108
  "trov:hasPerformance": {
@@ -18,18 +18,18 @@
18
18
  "schema:url": "https://policyengine.org"
19
19
  },
20
20
  "schema:dateCreated": "2026-07-28T03:56:03.415544+00:00",
21
- "schema:description": "TRACE TRO for certified runtime bundle us-5.2.0 covering the bundle manifest, the certified dataset artifact, the country model wheel, and the country data release manifest when it is available.",
21
+ "schema:description": "TRACE TRO for certified runtime bundle us-5.3.0 covering the bundle manifest, the certified dataset artifact, the country model wheel, and the country data release manifest when it is available.",
22
22
  "schema:name": "policyengine us certified bundle TRO",
23
23
  "trov:createdWith": {
24
24
  "@type": "schema:SoftwareApplication",
25
25
  "schema:name": "policyengine",
26
- "schema:softwareVersion": "5.2.0"
26
+ "schema:softwareVersion": "5.3.0"
27
27
  },
28
28
  "trov:hasArrangement": [
29
29
  {
30
30
  "@id": "arrangement/1",
31
31
  "@type": "trov:ArtifactArrangement",
32
- "rdfs:comment": "Certified arrangement for bundle us-5.2.0.",
32
+ "rdfs:comment": "Certified arrangement for bundle us-5.3.0.",
33
33
  "trov:hasArtifactLocation": [
34
34
  {
35
35
  "@id": "arrangement/1/location/bundle_manifest",
@@ -75,7 +75,7 @@
75
75
  "@type": "trov:ResearchArtifact",
76
76
  "schema:name": "policyengine.py bundle manifest for us",
77
77
  "trov:mimeType": "application/json",
78
- "trov:sha256": "623ea4f52023a9030cbae4ba689ac7d57195a313e735473bbbe63f54bbf89dbe"
78
+ "trov:sha256": "57c6fe9c5c370ea2b9460120656004b1a583db06cabb6c774551effc3c125695"
79
79
  },
80
80
  {
81
81
  "@id": "composition/1/artifact/data_release_manifest",
@@ -102,7 +102,7 @@
102
102
  "trov:hasFingerprint": {
103
103
  "@id": "composition/1/fingerprint",
104
104
  "@type": "trov:CompositionFingerprint",
105
- "trov:sha256": "9f1943b24f205871778b0c31bf4da7191c6ce7453065645a366ae87f10b98efa"
105
+ "trov:sha256": "c9806c4dbc8cb664ca2ed431af72f8540c08171fea5ce02d6e579f2d6866cf21"
106
106
  }
107
107
  },
108
108
  "trov:hasPerformance": {
@@ -0,0 +1,29 @@
1
+ """Belgium pilot: Axiom rules engine over Microcosm entity tables.
2
+
3
+ The first non-policyengine-core country in policyengine.py. Statutes are
4
+ encoded as RuleSpec YAML (TheAxiomFoundation/rulespec-be), compiled and
5
+ executed by axiom-rules-engine, and driven over Microcosm-BE entity tables.
6
+ See ``examples/belgium_axiom_pilot.py`` for the end-to-end population run
7
+ and ``model.py`` for scope and source-install requirements.
8
+ """
9
+
10
+ from .datasets import BEYearData, PopulaceBelgiumDataset
11
+ from .model import (
12
+ EMPLOYEE_SSC,
13
+ PIT_BEFORE_WITHHOLDING,
14
+ REMUNERATION,
15
+ AxiomBelgium,
16
+ AxiomBelgiumPilot,
17
+ be_model,
18
+ )
19
+
20
+ __all__ = [
21
+ "AxiomBelgium",
22
+ "AxiomBelgiumPilot",
23
+ "BEYearData",
24
+ "EMPLOYEE_SSC",
25
+ "PIT_BEFORE_WITHHOLDING",
26
+ "PopulaceBelgiumDataset",
27
+ "REMUNERATION",
28
+ "be_model",
29
+ ]
@@ -0,0 +1,190 @@
1
+ """Belgium pilot dataset: Microcosm entity tables with calibrated weights.
2
+
3
+ The pilot layout has two entities (person, household), mirroring the
4
+ Microcosm ``BE_SCHEMA``. Files are pandas HDF5 stores with ``person``,
5
+ ``household``, and ``_time_period`` keys. The canonical Microcosm-BE layout
6
+ stores calibrated weights once, on the household table; PolicyEngine derives
7
+ effective person weights through ``person_household_id`` when loading. Legacy
8
+ files may also carry ``person_weight``, but that redundant copy must match the
9
+ household weights exactly. Dataset metadata and the optional policy period are
10
+ stored as attributes on the ``_time_period`` record so the current
11
+ ``microcosm.frame`` reader can ignore them while PolicyEngine can round-trip
12
+ them.
13
+ """
14
+
15
+ import json
16
+ from pathlib import Path
17
+ from typing import Any, ClassVar, Optional
18
+
19
+ import numpy as np
20
+ import pandas as pd
21
+ from microdf import MicroDataFrame
22
+ from pydantic import ConfigDict, Field
23
+
24
+ from policyengine.core import Dataset, YearData
25
+
26
+
27
+ def _person_with_household_weights(
28
+ person: pd.DataFrame,
29
+ household: pd.DataFrame,
30
+ ) -> pd.DataFrame:
31
+ """Return persons with effective weights derived from household weights."""
32
+ person_membership = "person_household_id"
33
+ household_id = "household_id"
34
+ household_weight = "household_weight"
35
+ for table_name, table, required in (
36
+ ("person", person, person_membership),
37
+ ("household", household, household_id),
38
+ ("household", household, household_weight),
39
+ ):
40
+ if required not in table.columns:
41
+ raise ValueError(
42
+ f"Belgium {table_name} table is missing required column {required!r}."
43
+ )
44
+ duplicate_ids = household[household_id].duplicated(keep=False)
45
+ if duplicate_ids.any():
46
+ values = household.loc[duplicate_ids, household_id].drop_duplicates().tolist()
47
+ raise ValueError(f"Belgium household_id values must be unique; found {values}.")
48
+ if household[household_weight].isna().any():
49
+ raise ValueError("Belgium household_weight values must not be null.")
50
+
51
+ weight_by_household = household.set_index(household_id)[household_weight]
52
+ derived = person[person_membership].map(weight_by_household)
53
+ if derived.isna().any():
54
+ missing = (
55
+ person.loc[derived.isna(), person_membership].drop_duplicates().tolist()
56
+ )
57
+ raise ValueError(
58
+ "Belgium person_household_id values must resolve to household rows; "
59
+ f"missing {missing}."
60
+ )
61
+
62
+ if "person_weight" in person.columns:
63
+ legacy = pd.to_numeric(person["person_weight"], errors="coerce")
64
+ if legacy.isna().any() or not np.array_equal(
65
+ legacy.to_numpy(dtype=float),
66
+ derived.to_numpy(dtype=float),
67
+ ):
68
+ raise ValueError(
69
+ "Legacy Belgium person_weight values do not exactly match the "
70
+ "effective household_weight values."
71
+ )
72
+
73
+ result = person.copy()
74
+ result["person_weight"] = derived.to_numpy(copy=True)
75
+ return result
76
+
77
+
78
+ class BEYearData(YearData):
79
+ """Entity-level data for a single Belgian year."""
80
+
81
+ model_config = ConfigDict(arbitrary_types_allowed=True)
82
+
83
+ person: MicroDataFrame
84
+ household: MicroDataFrame
85
+
86
+ @property
87
+ def entity_data(self) -> dict[str, MicroDataFrame]:
88
+ return {"person": self.person, "household": self.household}
89
+
90
+
91
+ class PopulaceBelgiumDataset(Dataset):
92
+ """Belgium pilot dataset loaded from a Microcosm-BE HDF5 artifact.
93
+
94
+ ``year`` identifies the input dataset vintage. ``policy_period`` is set on
95
+ derived outputs and identifies the year of law executed by Axiom. Keeping
96
+ both fields prevents a 2025-policy run over a 2026-vintage population from
97
+ being mislabeled as though those were the same period.
98
+ """
99
+
100
+ data: Optional[BEYearData] = None
101
+ metadata: dict[str, Any] = Field(default_factory=dict)
102
+ policy_period: Optional[int] = None
103
+
104
+ _TIME_PERIOD_KEY: ClassVar[str] = "_time_period"
105
+ _METADATA_ATTRIBUTE: ClassVar[str] = "policyengine_metadata_json"
106
+ _POLICY_PERIOD_ATTRIBUTE: ClassVar[str] = "policyengine_policy_period"
107
+
108
+ def load(self) -> None:
109
+ if self.filepath is None:
110
+ raise ValueError("Cannot load a Belgium pilot dataset without a filepath.")
111
+ with pd.HDFStore(self.filepath, mode="r") as store:
112
+ person = store["person"]
113
+ household = store["household"]
114
+ if f"/{self._TIME_PERIOD_KEY}" in store.keys():
115
+ stored_year = int(store[self._TIME_PERIOD_KEY].iloc[0])
116
+ if stored_year != self.year:
117
+ raise ValueError(
118
+ "Belgium dataset period mismatch: "
119
+ f"constructor year={self.year}, HDF5 "
120
+ f"{self._TIME_PERIOD_KEY}={stored_year}."
121
+ )
122
+ attributes = store.get_storer(self._TIME_PERIOD_KEY).attrs
123
+ metadata_json = getattr(attributes, self._METADATA_ATTRIBUTE, None)
124
+ if metadata_json is not None:
125
+ stored_metadata = json.loads(str(metadata_json))
126
+ if self.metadata and self.metadata != stored_metadata:
127
+ raise ValueError(
128
+ "Belgium dataset metadata differs from the metadata "
129
+ "stored in its HDF5 artifact."
130
+ )
131
+ self.metadata = stored_metadata
132
+ stored_policy_period = getattr(
133
+ attributes, self._POLICY_PERIOD_ATTRIBUTE, None
134
+ )
135
+ if stored_policy_period is not None:
136
+ stored_policy_period = int(stored_policy_period)
137
+ if (
138
+ self.policy_period is not None
139
+ and self.policy_period != stored_policy_period
140
+ ):
141
+ raise ValueError(
142
+ "Belgium dataset policy-period mismatch: "
143
+ f"constructor policy_period={self.policy_period}, "
144
+ f"HDF5 policy period={stored_policy_period}."
145
+ )
146
+ self.policy_period = stored_policy_period
147
+ person = _person_with_household_weights(person, household)
148
+ self.data = BEYearData(
149
+ person=MicroDataFrame(person, weights="person_weight"),
150
+ household=MicroDataFrame(household, weights="household_weight"),
151
+ )
152
+
153
+ def save(self) -> None:
154
+ if self.data is None:
155
+ raise ValueError("No data to save.")
156
+ if self.filepath is None:
157
+ raise ValueError("Cannot save a Belgium pilot dataset without a filepath.")
158
+ # Serialize before opening in mode="w": invalid metadata must not
159
+ # truncate an existing destination.
160
+ metadata_json = json.dumps(
161
+ self.metadata,
162
+ allow_nan=False,
163
+ separators=(",", ":"),
164
+ sort_keys=True,
165
+ )
166
+ household = pd.DataFrame(self.data.household)
167
+ person = _person_with_household_weights(
168
+ pd.DataFrame(self.data.person),
169
+ household,
170
+ )
171
+ filepath = Path(self.filepath)
172
+ filepath.parent.mkdir(parents=True, exist_ok=True)
173
+ with pd.HDFStore(filepath, mode="w") as store:
174
+ # Microcosm carries only explicit entity weights. Person weights
175
+ # are inherited from households and reconstructed on load.
176
+ store["person"] = person.drop(columns=["person_weight"])
177
+ store["household"] = household
178
+ store.put(
179
+ self._TIME_PERIOD_KEY,
180
+ pd.Series([self.year]),
181
+ format="table",
182
+ )
183
+ attributes = store.get_storer(self._TIME_PERIOD_KEY).attrs
184
+ setattr(attributes, self._METADATA_ATTRIBUTE, metadata_json)
185
+ if self.policy_period is not None:
186
+ setattr(
187
+ attributes,
188
+ self._POLICY_PERIOD_ATTRIBUTE,
189
+ int(self.policy_period),
190
+ )