policyengine 6.2.6__tar.gz → 6.2.7__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 (320) hide show
  1. {policyengine-6.2.6 → policyengine-6.2.7}/CHANGELOG.md +7 -0
  2. {policyengine-6.2.6/src/policyengine.egg-info → policyengine-6.2.7}/PKG-INFO +1 -1
  3. {policyengine-6.2.6 → policyengine-6.2.7}/docs/households.md +5 -1
  4. {policyengine-6.2.6 → policyengine-6.2.7}/docs/reforms.md +13 -1
  5. {policyengine-6.2.6 → policyengine-6.2.7}/pyproject.toml +1 -1
  6. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/data/bundle/manifest.json +10 -10
  7. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/data/bundle/uk.trace.tro.jsonld +5 -5
  8. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/data/bundle/us.trace.tro.jsonld +5 -5
  9. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/common/reform.py +50 -9
  10. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/uk/household.py +14 -5
  11. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/uk/model.py +36 -15
  12. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/utils/parametric_reforms.py +18 -4
  13. {policyengine-6.2.6 → policyengine-6.2.7/src/policyengine.egg-info}/PKG-INFO +1 -1
  14. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine.egg-info/SOURCES.txt +1 -0
  15. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_dict_reforms_on_simulation.py +98 -2
  16. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_household_impact.py +1 -2
  17. policyengine-6.2.7/tests/test_uk_dated_reforms.py +305 -0
  18. {policyengine-6.2.6 → policyengine-6.2.7}/uv.lock +1 -1
  19. {policyengine-6.2.6 → policyengine-6.2.7}/.claude/policyengine-guide.md +0 -0
  20. {policyengine-6.2.6 → policyengine-6.2.7}/.claude/quick-reference.md +0 -0
  21. {policyengine-6.2.6 → policyengine-6.2.7}/.github/CONTRIBUTING.md +0 -0
  22. {policyengine-6.2.6 → policyengine-6.2.7}/.github/bump_version.py +0 -0
  23. {policyengine-6.2.6 → policyengine-6.2.7}/.github/capture-version.sh +0 -0
  24. {policyengine-6.2.6 → policyengine-6.2.7}/.github/changelog_template.md +0 -0
  25. {policyengine-6.2.6 → policyengine-6.2.7}/.github/check-changelog.sh +0 -0
  26. {policyengine-6.2.6 → policyengine-6.2.7}/.github/copilot-instructions.md +0 -0
  27. {policyengine-6.2.6 → policyengine-6.2.7}/.github/dispatch-policyengine-release.sh +0 -0
  28. {policyengine-6.2.6 → policyengine-6.2.7}/.github/fetch_version.py +0 -0
  29. {policyengine-6.2.6 → policyengine-6.2.7}/.github/get-changelog-diff.sh +0 -0
  30. {policyengine-6.2.6 → policyengine-6.2.7}/.github/has-functional-changes.sh +0 -0
  31. {policyengine-6.2.6 → policyengine-6.2.7}/.github/is-version-number-acceptable.sh +0 -0
  32. {policyengine-6.2.6 → policyengine-6.2.7}/.github/publish-git-tag.sh +0 -0
  33. {policyengine-6.2.6 → policyengine-6.2.7}/.github/wait-for-pypi.sh +0 -0
  34. {policyengine-6.2.6 → policyengine-6.2.7}/.github/workflows/draft-pdf.yml +0 -0
  35. {policyengine-6.2.6 → policyengine-6.2.7}/.github/workflows/pr_code_changes.yaml +0 -0
  36. {policyengine-6.2.6 → policyengine-6.2.7}/.github/workflows/pr_docs_changes.yaml +0 -0
  37. {policyengine-6.2.6 → policyengine-6.2.7}/.github/workflows/push.yaml +0 -0
  38. {policyengine-6.2.6 → policyengine-6.2.7}/.gitignore +0 -0
  39. {policyengine-6.2.6 → policyengine-6.2.7}/.python-version +0 -0
  40. {policyengine-6.2.6 → policyengine-6.2.7}/AGENTS.md +0 -0
  41. {policyengine-6.2.6 → policyengine-6.2.7}/CITATION.cff +0 -0
  42. {policyengine-6.2.6 → policyengine-6.2.7}/CLAUDE.md +0 -0
  43. {policyengine-6.2.6 → policyengine-6.2.7}/CODE_OF_CONDUCT.md +0 -0
  44. {policyengine-6.2.6 → policyengine-6.2.7}/CONTRIBUTING.md +0 -0
  45. {policyengine-6.2.6 → policyengine-6.2.7}/LICENSE +0 -0
  46. {policyengine-6.2.6 → policyengine-6.2.7}/Makefile +0 -0
  47. {policyengine-6.2.6 → policyengine-6.2.7}/README.md +0 -0
  48. {policyengine-6.2.6 → policyengine-6.2.7}/architecture.png +0 -0
  49. {policyengine-6.2.6 → policyengine-6.2.7}/architecture.svg +0 -0
  50. {policyengine-6.2.6 → policyengine-6.2.7}/changelog.d/.gitkeep +0 -0
  51. {policyengine-6.2.6 → policyengine-6.2.7}/docs/.gitignore +0 -0
  52. {policyengine-6.2.6 → policyengine-6.2.7}/docs/_generator/README.md +0 -0
  53. {policyengine-6.2.6 → policyengine-6.2.7}/docs/_generator/build_reference.py +0 -0
  54. {policyengine-6.2.6 → policyengine-6.2.7}/docs/_quarto.yml +0 -0
  55. {policyengine-6.2.6 → policyengine-6.2.7}/docs/bundles.md +0 -0
  56. {policyengine-6.2.6 → policyengine-6.2.7}/docs/countries.md +0 -0
  57. {policyengine-6.2.6 → policyengine-6.2.7}/docs/data-publishing-design.md +0 -0
  58. {policyengine-6.2.6 → policyengine-6.2.7}/docs/decile-impact-spec.md +0 -0
  59. {policyengine-6.2.6 → policyengine-6.2.7}/docs/dev.md +0 -0
  60. {policyengine-6.2.6 → policyengine-6.2.7}/docs/engineering/runbooks/acs-local-wic-replacement.md +0 -0
  61. {policyengine-6.2.6 → policyengine-6.2.7}/docs/engineering/runbooks/build-m-us-populace-certification.md +0 -0
  62. {policyengine-6.2.6 → policyengine-6.2.7}/docs/engineering/runbooks/canonical-spm-development.md +0 -0
  63. {policyengine-6.2.6 → policyengine-6.2.7}/docs/engineering/skills/README.md +0 -0
  64. {policyengine-6.2.6 → policyengine-6.2.7}/docs/engineering/skills/data-certification.md +0 -0
  65. {policyengine-6.2.6 → policyengine-6.2.7}/docs/engineering/skills/github-prs.md +0 -0
  66. {policyengine-6.2.6 → policyengine-6.2.7}/docs/engineering/skills/repository-guidance.md +0 -0
  67. {policyengine-6.2.6 → policyengine-6.2.7}/docs/examples.md +0 -0
  68. {policyengine-6.2.6 → policyengine-6.2.7}/docs/getting-started.md +0 -0
  69. {policyengine-6.2.6 → policyengine-6.2.7}/docs/impact-analysis.md +0 -0
  70. {policyengine-6.2.6 → policyengine-6.2.7}/docs/index.md +0 -0
  71. {policyengine-6.2.6 → policyengine-6.2.7}/docs/methodology/index.md +0 -0
  72. {policyengine-6.2.6 → policyengine-6.2.7}/docs/methodology/model-architecture.md +0 -0
  73. {policyengine-6.2.6 → policyengine-6.2.7}/docs/methodology/us-health-costs.md +0 -0
  74. {policyengine-6.2.6 → policyengine-6.2.7}/docs/microsim.md +0 -0
  75. {policyengine-6.2.6 → policyengine-6.2.7}/docs/outputs.md +0 -0
  76. {policyengine-6.2.6 → policyengine-6.2.7}/docs/programs/index.md +0 -0
  77. {policyengine-6.2.6 → policyengine-6.2.7}/docs/programs/us-chip.md +0 -0
  78. {policyengine-6.2.6 → policyengine-6.2.7}/docs/reference/index.md +0 -0
  79. {policyengine-6.2.6 → policyengine-6.2.7}/docs/regions.md +0 -0
  80. {policyengine-6.2.6 → policyengine-6.2.7}/docs/release-bundles.md +0 -0
  81. {policyengine-6.2.6 → policyengine-6.2.7}/docs/run-records.md +0 -0
  82. {policyengine-6.2.6 → policyengine-6.2.7}/docs/trace-case-study.md +0 -0
  83. {policyengine-6.2.6 → policyengine-6.2.7}/docs/visualisation.md +0 -0
  84. {policyengine-6.2.6 → policyengine-6.2.7}/examples/belgium_axiom_pilot.py +0 -0
  85. {policyengine-6.2.6 → policyengine-6.2.7}/examples/employment_income_variation_uk.py +0 -0
  86. {policyengine-6.2.6 → policyengine-6.2.7}/examples/employment_income_variation_us.py +0 -0
  87. {policyengine-6.2.6 → policyengine-6.2.7}/examples/household_impact_example.py +0 -0
  88. {policyengine-6.2.6 → policyengine-6.2.7}/examples/income_bands_uk.py +0 -0
  89. {policyengine-6.2.6 → policyengine-6.2.7}/examples/income_distribution_us.py +0 -0
  90. {policyengine-6.2.6 → policyengine-6.2.7}/examples/paper_repro_uk.py +0 -0
  91. {policyengine-6.2.6 → policyengine-6.2.7}/examples/policy_change_uk.py +0 -0
  92. {policyengine-6.2.6 → policyengine-6.2.7}/examples/speedtest_us_simulation.py +0 -0
  93. {policyengine-6.2.6 → policyengine-6.2.7}/examples/us_budgetary_impact.py +0 -0
  94. {policyengine-6.2.6 → policyengine-6.2.7}/paper-preview.html +0 -0
  95. {policyengine-6.2.6 → policyengine-6.2.7}/paper.bib +0 -0
  96. {policyengine-6.2.6 → policyengine-6.2.7}/paper.md +0 -0
  97. {policyengine-6.2.6 → policyengine-6.2.7}/scripts/bundle.py +0 -0
  98. {policyengine-6.2.6 → policyengine-6.2.7}/scripts/certify_data_release.py +0 -0
  99. {policyengine-6.2.6 → policyengine-6.2.7}/scripts/check_data_staleness.py +0 -0
  100. {policyengine-6.2.6 → policyengine-6.2.7}/scripts/check_native_spm_loading.py +0 -0
  101. {policyengine-6.2.6 → policyengine-6.2.7}/scripts/check_release_credentials.py +0 -0
  102. {policyengine-6.2.6 → policyengine-6.2.7}/scripts/export_bundle_release_assets.py +0 -0
  103. {policyengine-6.2.6 → policyengine-6.2.7}/scripts/generate_bundle_artifacts.py +0 -0
  104. {policyengine-6.2.6 → policyengine-6.2.7}/scripts/generate_trace_tros.py +0 -0
  105. {policyengine-6.2.6 → policyengine-6.2.7}/scripts/prepare_package_bundle_update.py +0 -0
  106. {policyengine-6.2.6 → policyengine-6.2.7}/scripts/release_lock.py +0 -0
  107. {policyengine-6.2.6 → policyengine-6.2.7}/scripts/spm_bundle.py +0 -0
  108. {policyengine-6.2.6 → policyengine-6.2.7}/setup.cfg +0 -0
  109. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/__init__.py +0 -0
  110. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/bundle.py +0 -0
  111. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/cli.py +0 -0
  112. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/core/__init__.py +0 -0
  113. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/core/cache.py +0 -0
  114. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/core/dataset.py +0 -0
  115. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/core/dynamic.py +0 -0
  116. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/core/output.py +0 -0
  117. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/core/parameter.py +0 -0
  118. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/core/parameter_node.py +0 -0
  119. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/core/parameter_value.py +0 -0
  120. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/core/policy.py +0 -0
  121. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/core/region.py +0 -0
  122. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/core/run_record.py +0 -0
  123. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/core/scoping_strategy.py +0 -0
  124. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/core/simulation.py +0 -0
  125. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/core/spm.py +0 -0
  126. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/core/tax_benefit_model.py +0 -0
  127. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/core/tax_benefit_model_version.py +0 -0
  128. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/core/variable.py +0 -0
  129. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/countries/__init__.py +0 -0
  130. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/countries/uk/__init__.py +0 -0
  131. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/countries/uk/regions.py +0 -0
  132. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/countries/us/__init__.py +0 -0
  133. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/countries/us/data/__init__.py +0 -0
  134. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/countries/us/data/districts.py +0 -0
  135. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/countries/us/data/places.py +0 -0
  136. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/countries/us/data/states.py +0 -0
  137. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/countries/us/regions.py +0 -0
  138. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/data/__init__.py +0 -0
  139. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/data/schemas/trace_tro.schema.json +0 -0
  140. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/data/uk_geography_assets.py +0 -0
  141. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/derivations/__init__.py +0 -0
  142. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/derivations/narrate.py +0 -0
  143. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/derivations/trace.py +0 -0
  144. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/graph/__init__.py +0 -0
  145. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/graph/extractor.py +0 -0
  146. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/graph/graph.py +0 -0
  147. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/outputs/__init__.py +0 -0
  148. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/outputs/aggregate.py +0 -0
  149. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/outputs/change_aggregate.py +0 -0
  150. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/outputs/cliff_impact.py +0 -0
  151. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/outputs/congressional_district_impact.py +0 -0
  152. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/outputs/constituency_impact.py +0 -0
  153. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/outputs/decile_analysis.py +0 -0
  154. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/outputs/decile_grouping.py +0 -0
  155. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/outputs/decile_impact.py +0 -0
  156. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/outputs/extra_variables.py +0 -0
  157. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/outputs/inequality.py +0 -0
  158. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/outputs/intra_decile_impact.py +0 -0
  159. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/outputs/labor_supply_response.py +0 -0
  160. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/outputs/local_authority_impact.py +0 -0
  161. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/outputs/poverty.py +0 -0
  162. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/outputs/program_statistics.py +0 -0
  163. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/outputs/uk_geography_assets.py +0 -0
  164. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/outputs/uk_geography_impact.py +0 -0
  165. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/provenance/__init__.py +0 -0
  166. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/provenance/bundle_validation.py +0 -0
  167. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/provenance/certification.py +0 -0
  168. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/provenance/dataset_materialization.py +0 -0
  169. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/provenance/manifest.py +0 -0
  170. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/provenance/pyproject_pins.py +0 -0
  171. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/provenance/spm_configuration.py +0 -0
  172. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/provenance/trace.py +0 -0
  173. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/provenance/verify.py +0 -0
  174. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/provenance/zenodo.py +0 -0
  175. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/results/__init__.py +0 -0
  176. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/results/schema.py +0 -0
  177. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/results/trace_tro.py +0 -0
  178. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/results/tracking.py +0 -0
  179. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/be/__init__.py +0 -0
  180. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/be/datasets.py +0 -0
  181. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/be/model.py +0 -0
  182. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/common/__init__.py +0 -0
  183. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/common/axes.py +0 -0
  184. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/common/extra_variables.py +0 -0
  185. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/common/household.py +0 -0
  186. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/common/model_version.py +0 -0
  187. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/common/result.py +0 -0
  188. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/uk/__init__.py +0 -0
  189. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/uk/analysis.py +0 -0
  190. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/uk/datasets.py +0 -0
  191. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/us/__init__.py +0 -0
  192. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/us/analysis.py +0 -0
  193. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/us/datasets.py +0 -0
  194. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/us/household.py +0 -0
  195. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/us/legacy_inputs.py +0 -0
  196. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/us/model.py +0 -0
  197. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/tax_benefit_models/us/spm.py +0 -0
  198. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/utils/__init__.py +0 -0
  199. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/utils/dates.py +0 -0
  200. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/utils/design.py +0 -0
  201. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/utils/entity_utils.py +0 -0
  202. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/utils/errors.py +0 -0
  203. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/utils/hashing.py +0 -0
  204. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/utils/household_validation.py +0 -0
  205. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/utils/parameter_labels.py +0 -0
  206. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine/utils/plotting.py +0 -0
  207. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine.egg-info/dependency_links.txt +0 -0
  208. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine.egg-info/entry_points.txt +0 -0
  209. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine.egg-info/requires.txt +0 -0
  210. {policyengine-6.2.6 → policyengine-6.2.7}/src/policyengine.egg-info/top_level.txt +0 -0
  211. {policyengine-6.2.6 → policyengine-6.2.7}/tests/__init__.py +0 -0
  212. {policyengine-6.2.6 → policyengine-6.2.7}/tests/conftest.py +0 -0
  213. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/__init__.py +0 -0
  214. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/filtering_fixtures.py +0 -0
  215. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/household_calculator_snapshots/uk_couple_two_kids.json +0 -0
  216. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/household_calculator_snapshots/uk_model_surface.json +0 -0
  217. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/household_calculator_snapshots/uk_single_adult_employment_income.json +0 -0
  218. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/household_calculator_snapshots/uk_single_adult_no_income.json +0 -0
  219. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/household_calculator_snapshots/uk_single_parent_one_child.json +0 -0
  220. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/household_calculator_snapshots/us_married_two_kids_high_income.json +0 -0
  221. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/household_calculator_snapshots/us_model_surface.json +0 -0
  222. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/household_calculator_snapshots/us_single_adult_employment_income.json +0 -0
  223. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/household_calculator_snapshots/us_single_adult_no_income.json +0 -0
  224. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/household_calculator_snapshots/us_single_parent_one_child.json +0 -0
  225. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/parameter_labels_fixtures.py +0 -0
  226. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/parametric_reforms_fixtures.py +0 -0
  227. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/poverty_by_demographics_fixtures.py +0 -0
  228. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/region_fixtures.py +0 -0
  229. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/release_tros/README.md +0 -0
  230. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/release_tros/country-models.lock.toml +0 -0
  231. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/release_tros/manifest.json +0 -0
  232. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/release_tros/us-release_manifest.json +0 -0
  233. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/release_tros/us.trace.tro.jsonld +0 -0
  234. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/spm_development.py +0 -0
  235. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/us_reform_fixtures.py +0 -0
  236. {policyengine-6.2.6 → policyengine-6.2.7}/tests/fixtures/variable_label_fixtures.py +0 -0
  237. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_aggregate.py +0 -0
  238. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_be_axiom_pilot.py +0 -0
  239. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_budgetary_impact.py +0 -0
  240. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_bump_version.py +0 -0
  241. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_bundle.py +0 -0
  242. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_bundle_metadata.py +0 -0
  243. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_cache.py +0 -0
  244. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_certify_data_release.py +0 -0
  245. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_change_aggregate.py +0 -0
  246. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_cliff_impact.py +0 -0
  247. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_cliff_impact_analysis.py +0 -0
  248. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_congressional_district_impact.py +0 -0
  249. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_constituency_impact.py +0 -0
  250. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_dataset_materialization.py +0 -0
  251. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_dataset_persistence.py +0 -0
  252. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_dataset_runtime.py +0 -0
  253. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_dataset_source.py +0 -0
  254. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_decile_grouping.py +0 -0
  255. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_derivations.py +0 -0
  256. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_entity_mapping.py +0 -0
  257. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_entity_utils.py +0 -0
  258. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_entity_utils_characterization.py +0 -0
  259. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_errors.py +0 -0
  260. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_extra_variables.py +0 -0
  261. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_filtering.py +0 -0
  262. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_generate_trace_tros.py +0 -0
  263. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_graph/__init__.py +0 -0
  264. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_graph/conftest.py +0 -0
  265. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_graph/test_extractor.py +0 -0
  266. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_household_calculator_snapshot.py +0 -0
  267. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_inequality.py +0 -0
  268. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_intra_decile_impact.py +0 -0
  269. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_labor_supply_response.py +0 -0
  270. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_local_authority_impact.py +0 -0
  271. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_manifest_version_mismatch.py +0 -0
  272. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_models.py +0 -0
  273. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_pandas3_compatibility.py +0 -0
  274. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_parameter_labels.py +0 -0
  275. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_parameter_nodes.py +0 -0
  276. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_parameter_values.py +0 -0
  277. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_parametric_reforms.py +0 -0
  278. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_poverty.py +0 -0
  279. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_poverty_by_demographics.py +0 -0
  280. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_poverty_run.py +0 -0
  281. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_preservation_mirror.py +0 -0
  282. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_program_statistics.py +0 -0
  283. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_pyproject_pins.py +0 -0
  284. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_region.py +0 -0
  285. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_region_group_strategy.py +0 -0
  286. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_release_credentials.py +0 -0
  287. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_release_lock.py +0 -0
  288. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_release_manifests.py +0 -0
  289. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_release_tro_generation.py +0 -0
  290. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_results.py +0 -0
  291. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_run_record.py +0 -0
  292. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_scoping_strategy.py +0 -0
  293. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_small_follow_ups.py +0 -0
  294. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_spm_bundle_bootstrap.py +0 -0
  295. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_spm_bundle_tooling.py +0 -0
  296. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_spm_decile.py +0 -0
  297. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_spm_development_authentication.py +0 -0
  298. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_spm_household.py +0 -0
  299. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_spm_model.py +0 -0
  300. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_spm_selection.py +0 -0
  301. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_spm_selection_schema.py +0 -0
  302. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_trace_tro.py +0 -0
  303. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_trace_tro_verify.py +0 -0
  304. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_uk_analysis.py +0 -0
  305. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_uk_geography_assets.py +0 -0
  306. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_uk_program_statistics.py +0 -0
  307. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_uk_regions.py +0 -0
  308. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_uk_year_file_data_year.py +0 -0
  309. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_us_legacy_inputs.py +0 -0
  310. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_us_legacy_inputs_integration.py +0 -0
  311. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_us_long_term_datasets.py +0 -0
  312. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_us_microsim_structural_reforms.py +0 -0
  313. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_us_native_alignment.py +0 -0
  314. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_us_native_hdf_weights.py +0 -0
  315. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_us_program_statistics.py +0 -0
  316. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_us_reform_application.py +0 -0
  317. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_us_regions.py +0 -0
  318. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_variable_labels.py +0 -0
  319. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_variable_metadata.py +0 -0
  320. {policyengine-6.2.6 → policyengine-6.2.7}/tests/test_zenodo_mirror.py +0 -0
@@ -1,3 +1,10 @@
1
+ ## [6.2.7] - 2026-10-12
2
+
3
+ ### Fixed
4
+
5
+ - Align UK household and population reform routes with policy effective dates and inclusive date intervals, reject ambiguous bare-year keys, and pass parameter reforms at country-model construction so dated changes are reprocessed when supported by policyengine-uk. A UK policy or dynamic carrying both parameter values and a simulation modifier (as a `Policy` or `Dynamic` sum does) now applies its values and then its modifier, where the modifier used to replace the values.
6
+
7
+
1
8
  ## [6.2.6] - 2026-10-10
2
9
 
3
10
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: policyengine
3
- Version: 6.2.6
3
+ Version: 6.2.7
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
@@ -150,7 +150,7 @@ Scale parameters use bracket indexing:
150
150
  reform = {"gov.irs.credits.ctc.amount.base[0].amount": 3_000}
151
151
  ```
152
152
 
153
- Time-varying reforms use a nested dict of `YYYY-MM-DD → value`:
153
+ Time-varying reforms use a nested dict of date keys to values:
154
154
 
155
155
  ```python
156
156
  reform = {
@@ -161,6 +161,10 @@ reform = {
161
161
  }
162
162
  ```
163
163
 
164
+ For UK households, a `YYYY-MM-DD` key is effective from that date until the next value for the same parameter, or December 31, 2100 if no later value follows. A `YYYY-MM-DD.YYYY-MM-DD` key applies over that inclusive interval. Scalar values default to January 1 of `year`.
165
+
166
+ UK households reject bare years such as `"2027"` or `2027`, because the underlying packages assign different meanings to them. Use `"2027-04-06.2028-04-05"` for one UK fiscal year or `"2027-01-01"` for an effective date. See [Reforms](reforms.md) for the shared `Simulation(policy=...)` and `Simulation(dynamic=...)` date contract. The US household route retains its country package's period-key handling.
167
+
164
168
  Structural reforms (new variables, formula swaps) require the `Simulation` path — see [Reforms](reforms.md).
165
169
 
166
170
  ## Year
@@ -29,7 +29,19 @@ reform = {
29
29
  }
30
30
  ```
31
31
 
32
- Dates that haven't passed become "from this date onward." Earlier dates replace the baseline schedule.
32
+ A `YYYY-MM-DD` key is an effective date: its value applies from that date until the next date in the same parameter's reform schedule. If no later value follows, the wrapper emits an interval ending on December 31, 2100.
33
+
34
+ Use `YYYY-MM-DD.YYYY-MM-DD` for an inclusive interval. For example, this UK reform applies for fiscal year 2027 only:
35
+
36
+ ```python
37
+ reform = {
38
+ "gov.hmrc.income_tax.rates.uk[0].rate": {
39
+ "2027-04-06.2028-04-05": 0.21,
40
+ },
41
+ }
42
+ ```
43
+
44
+ `Simulation(policy=...)`, `Simulation(dynamic=...)`, and UK `calculate_household(reform=...)` accept these two date formats and reject an interval whose end precedes its start. They reject bare years such as `"2027"` or `2027`, because policyengine-uk reads them as fiscal year 2027 only, while policyengine-core's `Reform.from_dict` reads them as from 2027 onward. Use `"2027-04-06.2028-04-05"` for one UK fiscal year or `"2027-01-01"` for an effective date. The US household route retains its country package's period-key handling.
33
45
 
34
46
  ### Multiple changes
35
47
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "policyengine"
7
- version = "6.2.6"
7
+ version = "6.2.7"
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": "6.2.6",
2
+ "bundle_version": "6.2.7",
3
3
  "citation": {
4
4
  "publisher": "PolicyEngine",
5
- "title": "PolicyEngine bundle 6.2.6",
5
+ "title": "PolicyEngine bundle 6.2.7",
6
6
  "type": "software-bundle",
7
- "version": "6.2.6"
7
+ "version": "6.2.7"
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.58.0",
20
- "bundle_id": "uk-6.2.6",
20
+ "bundle_id": "uk-6.2.7",
21
21
  "certification": {
22
22
  "built_with_model_git_sha": "4cbedbecca352f52dffe47e849553c2759a00711",
23
23
  "built_with_model_version": "2.122.2",
@@ -99,7 +99,7 @@
99
99
  "version": "2.125.1",
100
100
  "wheel_url": "https://files.pythonhosted.org/packages/ec/3a/1d90bad4346d1a8c2715be211ed3d3d7f48917da0d90405b8257b7304091/policyengine_uk-2.125.1-py3-none-any.whl"
101
101
  },
102
- "policyengine_version": "6.2.6",
102
+ "policyengine_version": "6.2.7",
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-spm-20260915",
115
- "bundle_id": "us-6.2.6",
115
+ "bundle_id": "us-6.2.7",
116
116
  "certification": {
117
117
  "built_with_model_version": "2.2.1",
118
118
  "certified_by": "policyengine.py bundle certification",
@@ -217,7 +217,7 @@
217
217
  "version": "2.2.1",
218
218
  "wheel_url": "https://files.pythonhosted.org/packages/5e/09/86ac3968d2a18e28fce3ba1186fc21728e9f299b96a806d6f204f1e0aaed/policyengine_us-2.2.1-py3-none-any.whl"
219
219
  },
220
- "policyengine_version": "6.2.6",
220
+ "policyengine_version": "6.2.7",
221
221
  "region_datasets": {
222
222
  "congressional_district": {
223
223
  "path_template": "populace_us_2024_acs_local.h5"
@@ -295,10 +295,10 @@
295
295
  "packages": {
296
296
  "policyengine": {
297
297
  "import_name": "policyengine",
298
- "install_requirement": "policyengine==6.2.6",
298
+ "install_requirement": "policyengine==6.2.7",
299
299
  "name": "policyengine",
300
300
  "role": "bundle_carrier",
301
- "version": "6.2.6"
301
+ "version": "6.2.7"
302
302
  },
303
303
  "policyengine-core": {
304
304
  "import_name": "policyengine_core",
@@ -334,6 +334,6 @@
334
334
  "wheel_url": "https://files.pythonhosted.org/packages/19/a0/c484f69a0ebf88a9b9fd0ac28176f8df66550f46714f1b0c5ec0b360824e/spm_calculator-1.0.0-py3-none-any.whl"
335
335
  }
336
336
  },
337
- "policyengine_version": "6.2.6",
337
+ "policyengine_version": "6.2.7",
338
338
  "schema_version": 2
339
339
  }
@@ -18,18 +18,18 @@
18
18
  "schema:url": "https://policyengine.org"
19
19
  },
20
20
  "schema:dateCreated": "2026-10-07T17:31:53.189867Z",
21
- "schema:description": "TRACE TRO for certified runtime bundle uk-6.2.6 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-6.2.7 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": "6.2.6"
26
+ "schema:softwareVersion": "6.2.7"
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-6.2.6.",
32
+ "rdfs:comment": "Certified arrangement for bundle uk-6.2.7.",
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": "8633ad814c9c85e53c7c513f69c4c7b1500a03cd2f6ef133d6e2ea8346b2db54"
78
+ "trov:sha256": "5dadfb8081cee1419de51bfae094fc856444c154505c13bf86ceb40e0838d20c"
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": "e6c6d39c2dfcf28a1047e783f4a927673d0dfbcd84b5b305675e1de10e6c4050"
105
+ "trov:sha256": "b0f0f60ca2071d9cc27fdd46fa07d19e518a209929ccc7df64c9017ab3a57114"
106
106
  }
107
107
  },
108
108
  "trov:hasPerformance": {
@@ -17,18 +17,18 @@
17
17
  "schema:name": "PolicyEngine",
18
18
  "schema:url": "https://policyengine.org"
19
19
  },
20
- "schema:description": "TRACE TRO for certified runtime bundle us-6.2.6 covering the bundle manifest, the certified dataset artifact, the country model wheel, and the country data release manifest when it is available.",
20
+ "schema:description": "TRACE TRO for certified runtime bundle us-6.2.7 covering the bundle manifest, the certified dataset artifact, the country model wheel, and the country data release manifest when it is available.",
21
21
  "schema:name": "policyengine us certified bundle TRO",
22
22
  "trov:createdWith": {
23
23
  "@type": "schema:SoftwareApplication",
24
24
  "schema:name": "policyengine",
25
- "schema:softwareVersion": "6.2.6"
25
+ "schema:softwareVersion": "6.2.7"
26
26
  },
27
27
  "trov:hasArrangement": [
28
28
  {
29
29
  "@id": "arrangement/1",
30
30
  "@type": "trov:ArtifactArrangement",
31
- "rdfs:comment": "Certified arrangement for bundle us-6.2.6.",
31
+ "rdfs:comment": "Certified arrangement for bundle us-6.2.7.",
32
32
  "trov:hasArtifactLocation": [
33
33
  {
34
34
  "@id": "arrangement/1/location/bundle_manifest",
@@ -74,7 +74,7 @@
74
74
  "@type": "trov:ResearchArtifact",
75
75
  "schema:name": "policyengine.py bundle manifest for us",
76
76
  "trov:mimeType": "application/json",
77
- "trov:sha256": "8633ad814c9c85e53c7c513f69c4c7b1500a03cd2f6ef133d6e2ea8346b2db54"
77
+ "trov:sha256": "5dadfb8081cee1419de51bfae094fc856444c154505c13bf86ceb40e0838d20c"
78
78
  },
79
79
  {
80
80
  "@id": "composition/1/artifact/data_release_manifest",
@@ -101,7 +101,7 @@
101
101
  "trov:hasFingerprint": {
102
102
  "@id": "composition/1/fingerprint",
103
103
  "@type": "trov:CompositionFingerprint",
104
- "trov:sha256": "4c980b674225037116186b6a8af9a22ff68f534b52dc05dc4519b2dbe2e9055c"
104
+ "trov:sha256": "e9c575e1126a1c4b688ad1a0cbd9231f0ee0b0b08ad05347c6930c264461e8e0"
105
105
  }
106
106
  },
107
107
  "trov:hasPerformance": {
@@ -1,4 +1,4 @@
1
- """Compile a simple reform dict into the format policyengine_core expects.
1
+ """Compile a simple reform dict into country-model inputs or parameter values.
2
2
 
3
3
  Accepted shapes for the agent-facing API:
4
4
 
@@ -10,6 +10,13 @@ Accepted shapes for the agent-facing API:
10
10
  # With explicit effective date(s).
11
11
  reform = {"gov.irs.deductions.salt.cap": {"2026-01-01": 0}}
12
12
 
13
+ # With an inclusive start and end date.
14
+ reform = {
15
+ "gov.hmrc.income_tax.rates.uk[0].rate": {
16
+ "2027-04-06.2028-04-05": 0.21,
17
+ },
18
+ }
19
+
13
20
  # Multiple parameters.
14
21
  reform = {
15
22
  "gov.irs.deductions.salt.cap": 0,
@@ -25,9 +32,17 @@ the top-bracket SS wage base is ``gov.ssa.payroll.cap``. If a reform
25
32
  dict uses the bracket-head path instead of ``[0].amount`` the
26
33
  ``ValueError`` will list the close match.
27
34
 
28
- The compiled form is ``{param_path: {period: value}}`` — exactly what
29
- ``policyengine_us.Simulation(reform=...)`` /
30
- ``policyengine_uk.Simulation(reform=...)`` accept at construction.
35
+ The parameter-value compiler accepts ``YYYY-MM-DD`` as an effective date
36
+ with no end date, or ``YYYY-MM-DD.YYYY-MM-DD`` as an inclusive interval.
37
+ Months and days may be unpadded (``2027-4-6``), as ``strptime`` reads them.
38
+ Other keys, including bare years, are rejected: policyengine-uk interprets
39
+ a bare year as one fiscal year, while policyengine-core interprets it as
40
+ an effective year continuing onward. Use ``2027-04-06.2028-04-05`` for one
41
+ UK fiscal year, or ``2027-01-01`` for an effective date.
42
+
43
+ The low-level :func:`compile_reform` returns ``{param_path: {period: value}}``
44
+ without interpreting period keys. Parameter-value routes convert values
45
+ to explicit intervals before passing them to the country model.
31
46
 
32
47
  Scalar reforms default to ``{year}-01-01`` so a caller running
33
48
  mid-year does not accidentally get a blended partial-year result.
@@ -103,8 +118,8 @@ def _reform_dict_to_parameter_values(
103
118
  """Compile a flat reform dict into a list of ``ParameterValue`` objects.
104
119
 
105
120
  Uses :func:`compile_reform` for path validation and effective-date
106
- defaulting, then materialises each ``{path: {date: value}}`` pair
107
- as an open-ended ``ParameterValue`` bound to a
121
+ defaulting, then materialises each effective date or inclusive interval
122
+ as a ``ParameterValue`` bound to a
108
123
  ``Parameter(name=path, tax_benefit_model_version=model_version)``.
109
124
  """
110
125
  from policyengine.core.parameter import Parameter
@@ -117,6 +132,32 @@ def _reform_dict_to_parameter_values(
117
132
  parameter_values: list[ParameterValue] = []
118
133
  for path, date_to_value in compiled.items():
119
134
  for effective_date, value in date_to_value.items():
135
+ try:
136
+ parts = str(effective_date).split(".")
137
+ if len(parts) > 2:
138
+ raise ValueError("Too many dates")
139
+ start_date = datetime.datetime.strptime(parts[0], "%Y-%m-%d")
140
+ end_date = (
141
+ datetime.datetime.strptime(parts[1], "%Y-%m-%d")
142
+ if len(parts) == 2
143
+ else None
144
+ )
145
+ except ValueError as exc:
146
+ raise ValueError(
147
+ f"Invalid reform date key {effective_date!r}. Accepted formats are "
148
+ "'YYYY-MM-DD' (effective date) and "
149
+ "'YYYY-MM-DD.YYYY-MM-DD' (inclusive interval); months and "
150
+ "days may be unpadded. Bare years are "
151
+ "ambiguous: policyengine-uk's reform dict reads '2027' as fiscal "
152
+ "year 2027 only, while policyengine-core's Reform.from_dict "
153
+ "reads it as from 2027 on. Use '2027-04-06.2028-04-05' for one "
154
+ "UK fiscal year, or '2027-01-01' for an effective date."
155
+ ) from exc
156
+ if end_date is not None and end_date < start_date:
157
+ raise ValueError(
158
+ f"Reform interval {effective_date!r} has an end date before "
159
+ "its start date."
160
+ )
120
161
  data_type = type(value) if isinstance(value, (int, float, bool)) else float
121
162
  parameter_values.append(
122
163
  ParameterValue(
@@ -125,8 +166,8 @@ def _reform_dict_to_parameter_values(
125
166
  tax_benefit_model_version=model_version,
126
167
  data_type=data_type,
127
168
  ),
128
- start_date=datetime.datetime.strptime(effective_date, "%Y-%m-%d"),
129
- end_date=None,
169
+ start_date=start_date,
170
+ end_date=end_date,
130
171
  value=value,
131
172
  )
132
173
  )
@@ -160,7 +201,7 @@ def compile_reform_to_policy(
160
201
  """Compile a flat reform dict into a fully-assembled ``Policy``.
161
202
 
162
203
  Accepts the same ``{param.path: value}`` /
163
- ``{param.path: {date: value}}`` shape as :func:`compile_reform`,
204
+ ``{param.path: {date_or_interval: value}}`` shape as :func:`compile_reform`,
164
205
  but returns a ready-to-use ``Policy`` with ``ParameterValue``
165
206
  objects so ``Simulation(policy={...})`` works without hand-building
166
207
  ``Parameter`` / ``ParameterValue``.
@@ -26,13 +26,16 @@ from typing import Any, Optional
26
26
  from policyengine.tax_benefit_models.common import (
27
27
  EntityResult,
28
28
  HouseholdResult,
29
- compile_reform,
30
29
  dispatch_extra_variables,
31
30
  normalize_axes,
32
31
  validate_annual_household_inputs,
33
32
  values_for_entity,
34
33
  )
34
+ from policyengine.tax_benefit_models.common.reform import (
35
+ _reform_dict_to_parameter_values,
36
+ )
35
37
  from policyengine.utils.household_validation import validate_household_input
38
+ from policyengine.utils.parametric_reforms import reform_dict_from_parameter_values
36
39
 
37
40
  from .model import uk_latest
38
41
 
@@ -129,9 +132,11 @@ def calculate_household(
129
132
  Must be non-empty.
130
133
  benunit, household: Optional per-entity overrides.
131
134
  year: Calendar year. Defaults to 2026.
132
- reform: Optional reform dict. Scalar values default to
133
- ``{year}-01-01``; invalid parameter paths raise with a
134
- close-match suggestion.
135
+ reform: Optional reform dict. Scalars start at ``{year}-01-01``.
136
+ ``YYYY-MM-DD`` keys are effective dates; a value lasts until
137
+ the next effective date or the end of the model horizon.
138
+ ``YYYY-MM-DD.YYYY-MM-DD`` keys specify inclusive intervals.
139
+ Invalid parameter paths raise with a close-match suggestion.
135
140
  extra_variables: Flat list of extra UK variables to compute;
136
141
  the library dispatches each to its entity.
137
142
  axes: Optional household-calculator axes. Pass either the lower-level
@@ -181,7 +186,11 @@ def calculate_household(
181
186
  names=extra_variables or [],
182
187
  )
183
188
  output_columns = _default_output_columns(extra_by_entity)
184
- reform_dict = compile_reform(reform, year=year, model_version=uk_latest)
189
+ reform_dict = reform_dict_from_parameter_values(
190
+ _reform_dict_to_parameter_values(
191
+ reform or {}, year=year, model_version=uk_latest
192
+ )
193
+ )
185
194
  normalized_axes = normalize_axes(axes=axes, year=year, model_version=uk_latest)
186
195
  axes_active = normalized_axes is not None
187
196
 
@@ -167,6 +167,8 @@ class PolicyEngineUKLatest(MicrosimulationModelVersion):
167
167
  from policyengine_uk import Microsimulation
168
168
 
169
169
  from policyengine.utils.parametric_reforms import (
170
+ build_reform_dict,
171
+ merge_reform_dicts,
170
172
  simulation_modifier_from_parameter_values,
171
173
  )
172
174
 
@@ -208,23 +210,42 @@ class PolicyEngineUKLatest(MicrosimulationModelVersion):
208
210
  data_year_data=dataset.data_year_data,
209
211
  )
210
212
 
211
- microsim = Microsimulation(dataset=_policyengine_uk_input(dataset))
213
+ policy = simulation.policy
214
+ dynamic = simulation.dynamic
215
+ policy_modifier = policy.simulation_modifier if policy else None
216
+ dynamic_modifier = dynamic.simulation_modifier if dynamic else None
217
+ # Each object's parameter values come before its modifier, and the
218
+ # policy before the dynamic: policy values, policy modifier, dynamic
219
+ # values, dynamic modifier. A modifier never replaces its object's
220
+ # parameter values (a Policy or Dynamic sum can carry both).
221
+ # Parameter values reach policyengine-uk as a reform dict at
222
+ # construction, which writes them onto the statutory timeline and
223
+ # reprocesses the parameters. The dynamic's values join that reform
224
+ # (merge_reform_dicts puts them last, so they win) unless a policy
225
+ # modifier must run before them; then they are applied after that
226
+ # modifier, to the annualised parameters, as before.
227
+ reform_dict = build_reform_dict(policy)
228
+ late_dynamic_values = None
229
+ if policy_modifier is None:
230
+ reform_dict = merge_reform_dicts(reform_dict, build_reform_dict(dynamic))
231
+ elif dynamic is not None and dynamic.parameter_values:
232
+ late_dynamic_values = dynamic.parameter_values
233
+ # policyengine-uk also builds an internal baseline from the same
234
+ # input, which its formulas read through simulation.baseline.
235
+ # _policyengine_uk_input gives this run fresh tables, so enum
236
+ # encoding cannot mutate the caller's dataset.
237
+ microsim = Microsimulation(
238
+ dataset=_policyengine_uk_input(dataset), reform=reform_dict
239
+ )
212
240
 
213
- if simulation.policy and simulation.policy.simulation_modifier is not None:
214
- simulation.policy.simulation_modifier(microsim)
215
- elif simulation.policy:
216
- modifier = simulation_modifier_from_parameter_values(
217
- simulation.policy.parameter_values
218
- )
219
- modifier(microsim)
241
+ if policy_modifier is not None:
242
+ policy_modifier(microsim)
220
243
 
221
- if simulation.dynamic and simulation.dynamic.simulation_modifier is not None:
222
- simulation.dynamic.simulation_modifier(microsim)
223
- elif simulation.dynamic:
224
- modifier = simulation_modifier_from_parameter_values(
225
- simulation.dynamic.parameter_values
226
- )
227
- modifier(microsim)
244
+ if late_dynamic_values is not None:
245
+ simulation_modifier_from_parameter_values(late_dynamic_values)(microsim)
246
+
247
+ if dynamic_modifier is not None:
248
+ dynamic_modifier(microsim)
228
249
 
229
250
  data = {
230
251
  "person": pd.DataFrame(),
@@ -131,7 +131,15 @@ def merge_reform_dicts(
131
131
  """Merge two reform dicts, with override values taking precedence.
132
132
 
133
133
  Either or both dicts can be None. When both have entries for the same
134
- parameter, period-level values from override replace those in base.
134
+ parameter, an override value replaces the base value with the same
135
+ period key, and every override period comes after the base's periods.
136
+
137
+ Where an override period overlaps a base period with a different key,
138
+ the result depends on the country model. policyengine-uk applies a
139
+ parameter's periods in the order given, so the override wins.
140
+ policyengine-core's ``Reform.from_dict``, which the US route uses,
141
+ applies them in order of start date, so the later-starting period wins
142
+ whichever dict it came from.
135
143
 
136
144
  Args:
137
145
  base: The base reform dict (e.g., from policy).
@@ -147,7 +155,13 @@ def merge_reform_dicts(
147
155
 
148
156
  merged = {k: dict(v) for k, v in base.items()}
149
157
  for param_name, period_values in override.items():
150
- if param_name not in merged:
151
- merged[param_name] = {}
152
- merged[param_name].update(period_values)
158
+ # Override values go after the base's, so a country model that
159
+ # applies a parameter's periods in order lets them win where they
160
+ # overlap a base period with a different key.
161
+ kept = {
162
+ period: value
163
+ for period, value in merged.get(param_name, {}).items()
164
+ if period not in period_values
165
+ }
166
+ merged[param_name] = {**kept, **period_values}
153
167
  return merged
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: policyengine
3
- Version: 6.2.6
3
+ Version: 6.2.7
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
@@ -272,6 +272,7 @@ tests/test_spm_selection_schema.py
272
272
  tests/test_trace_tro.py
273
273
  tests/test_trace_tro_verify.py
274
274
  tests/test_uk_analysis.py
275
+ tests/test_uk_dated_reforms.py
275
276
  tests/test_uk_geography_assets.py
276
277
  tests/test_uk_program_statistics.py
277
278
  tests/test_uk_regions.py
@@ -10,12 +10,19 @@ the tests are fast and don't need HF credentials.
10
10
 
11
11
  from __future__ import annotations
12
12
 
13
+ import datetime
14
+
13
15
  import pytest
14
16
 
15
17
  pytest.importorskip("policyengine_us")
16
18
 
17
19
  import policyengine as pe
18
20
  from policyengine.core import Dynamic, Policy, Simulation
21
+ from policyengine.tax_benefit_models.common.reform import (
22
+ _reform_dict_to_parameter_values,
23
+ compile_reform_to_dynamic,
24
+ compile_reform_to_policy,
25
+ )
19
26
 
20
27
  # ``us_test_dataset`` is registered globally via ``tests/conftest.py``.
21
28
 
@@ -45,6 +52,7 @@ class TestDictPolicyCoercion:
45
52
  # Scalar reforms default the effective date to {year}-01-01.
46
53
  assert pv.start_date.year == 2026
47
54
  assert pv.start_date.month == 1
55
+ assert pv.end_date is None
48
56
 
49
57
  def test__dict_policy_with_effective_date__then_start_date_matches(
50
58
  self, tiny_dataset
@@ -66,6 +74,7 @@ class TestDictPolicyCoercion:
66
74
  "2026-07-01",
67
75
  "2027-01-01",
68
76
  ]
77
+ assert all(pv.end_date is None for pv in sim.policy.parameter_values)
69
78
 
70
79
  def test__unknown_parameter_path__raises_with_suggestion(self, tiny_dataset):
71
80
  with pytest.raises(ValueError) as exc:
@@ -81,8 +90,6 @@ class TestDictPolicyCoercion:
81
90
  assert "did you mean" in str(exc.value)
82
91
 
83
92
  def test__existing_policy_object_passes_through_unchanged(self, tiny_dataset):
84
- import datetime
85
-
86
93
  from policyengine.core import Parameter, ParameterValue
87
94
 
88
95
  existing = Policy(
@@ -126,3 +133,92 @@ class TestDictDynamicCoercion:
126
133
  assert isinstance(sim.dynamic, Dynamic)
127
134
  assert len(sim.dynamic.parameter_values) == 1
128
135
  assert sim.dynamic.parameter_values[0].value == 2_800
136
+
137
+
138
+ class TestReformDictDates:
139
+ path = "gov.irs.credits.ctc.amount.base[0].amount"
140
+
141
+ @pytest.mark.parametrize(
142
+ "compiler", [compile_reform_to_policy, compile_reform_to_dynamic]
143
+ )
144
+ @pytest.mark.parametrize("stop", ["2027-04-06", "2028-04-05"])
145
+ def test__interval__then_dates_are_preserved(self, compiler, stop):
146
+ reform = compiler(
147
+ {self.path: {f"2027-04-06.{stop}": 3_000}},
148
+ year=2027,
149
+ model_version=pe.us.model,
150
+ )
151
+ assert len(reform.parameter_values) == 1
152
+ pv = reform.parameter_values[0]
153
+ assert pv.start_date == datetime.datetime(2027, 4, 6)
154
+ assert pv.end_date == datetime.datetime.strptime(stop, "%Y-%m-%d")
155
+ assert pv.value == 3_000
156
+
157
+ def test__interval_end_before_start__raises(self):
158
+ with pytest.raises(ValueError, match="end date before its start date"):
159
+ _reform_dict_to_parameter_values(
160
+ {self.path: {"2028-04-06.2027-04-05": 3_000}},
161
+ year=2027,
162
+ model_version=pe.us.model,
163
+ )
164
+
165
+ @pytest.mark.parametrize("key", ["2027", 2027])
166
+ def test__bare_year__raises_with_country_semantics(self, key):
167
+ with pytest.raises(ValueError) as exc:
168
+ _reform_dict_to_parameter_values(
169
+ {self.path: {key: 3_000}}, year=2027, model_version=pe.us.model
170
+ )
171
+ message = str(exc.value)
172
+ assert "'YYYY-MM-DD'" in message
173
+ assert "'YYYY-MM-DD.YYYY-MM-DD'" in message
174
+ assert "Bare years are ambiguous" in message
175
+ assert "policyengine-uk's reform dict" in message
176
+ assert "fiscal year 2027 only" in message
177
+ assert "policyengine-core's Reform.from_dict" in message
178
+ assert "from 2027 on" in message
179
+ assert "'2027-04-06.2028-04-05'" in message
180
+ assert "'2027-01-01'" in message
181
+
182
+ @pytest.mark.parametrize(
183
+ "key",
184
+ [
185
+ "2027-02-29",
186
+ "2027-04-06.2028-02-30",
187
+ "2027-04-06.2028-04-05.2029-04-05",
188
+ "2027-04",
189
+ "not-a-date",
190
+ ],
191
+ )
192
+ def test__other_date_keys__raise_with_accepted_formats(self, key):
193
+ with pytest.raises(ValueError, match="Accepted formats"):
194
+ _reform_dict_to_parameter_values(
195
+ {self.path: {key: 3_000}}, year=2027, model_version=pe.us.model
196
+ )
197
+
198
+ @pytest.mark.parametrize("spec", [3_000, {"2027-01-01": 3_000}])
199
+ def test__scalar_or_effective_date__then_value_is_open_ended(self, spec):
200
+ values = _reform_dict_to_parameter_values(
201
+ {self.path: spec}, year=2027, model_version=pe.us.model
202
+ )
203
+ assert len(values) == 1
204
+ assert values[0].start_date == datetime.datetime(2027, 1, 1)
205
+ assert values[0].end_date is None
206
+ assert values[0].value == 3_000
207
+
208
+ @pytest.mark.parametrize(
209
+ "key, start, end",
210
+ [
211
+ ("2027-4-6", datetime.datetime(2027, 4, 6), None),
212
+ (
213
+ "2027-4-6.2028-4-5",
214
+ datetime.datetime(2027, 4, 6),
215
+ datetime.datetime(2028, 4, 5),
216
+ ),
217
+ ],
218
+ )
219
+ def test__unpadded_dates__then_accepted_as_before(self, key, start, end):
220
+ # The compiler has always accepted what strptime("%Y-%m-%d") does.
221
+ (value,) = _reform_dict_to_parameter_values(
222
+ {self.path: {key: 3_000}}, year=2027, model_version=pe.us.model
223
+ )
224
+ assert (value.start_date, value.end_date) == (start, end)
@@ -67,10 +67,9 @@ class TestUKCalculateHousehold:
67
67
  year=2026,
68
68
  reform={"gov.hmrc.child_benefit.amount.eldest": 50.0},
69
69
  )
70
- # If the param path is valid the calc runs; if results differ the reform took.
71
- # Accept either: the key thing is the reform dict was accepted without error.
72
70
  assert isinstance(reformed.benunit.child_benefit, float)
73
71
  assert isinstance(baseline.benunit.child_benefit, float)
72
+ assert reformed.benunit.child_benefit > baseline.benunit.child_benefit
74
73
 
75
74
  def test__monthly_year_period__then_raises_before_calculation(self):
76
75
  with pytest.raises(ValueError, match="Monthly periods are not supported"):