policyengine 6.3.4__tar.gz → 6.4.1__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 (358) hide show
  1. {policyengine-6.3.4 → policyengine-6.4.1}/CHANGELOG.md +19 -0
  2. {policyengine-6.3.4/src/policyengine.egg-info → policyengine-6.4.1}/PKG-INFO +4 -4
  3. {policyengine-6.3.4 → policyengine-6.4.1}/docs/_quarto.yml +1 -0
  4. policyengine-6.4.1/docs/disclosure.md +239 -0
  5. {policyengine-6.3.4 → policyengine-6.4.1}/docs/engineering/runbooks/build-m-us-populace-certification.md +23 -6
  6. {policyengine-6.3.4 → policyengine-6.4.1}/docs/engineering/skills/data-certification.md +27 -2
  7. {policyengine-6.3.4 → policyengine-6.4.1}/docs/examples.md +7 -0
  8. {policyengine-6.3.4 → policyengine-6.4.1}/docs/outputs.md +22 -1
  9. {policyengine-6.3.4 → policyengine-6.4.1}/docs/regions.md +5 -0
  10. policyengine-6.4.1/examples/uk_geography_disclosure.py +84 -0
  11. {policyengine-6.3.4 → policyengine-6.4.1}/pyproject.toml +4 -4
  12. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/data/bundle/manifest.json +44 -44
  13. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/data/bundle/uk.trace.tro.jsonld +19 -19
  14. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/data/bundle/us.trace.tro.jsonld +5 -5
  15. policyengine-6.4.1/src/policyengine/disclosure/__init__.py +65 -0
  16. policyengine-6.4.1/src/policyengine/disclosure/__main__.py +37 -0
  17. policyengine-6.4.1/src/policyengine/disclosure/areas.py +216 -0
  18. policyengine-6.4.1/src/policyengine/disclosure/estimate.py +472 -0
  19. policyengine-6.4.1/src/policyengine/disclosure/noise.py +102 -0
  20. policyengine-6.4.1/src/policyengine/disclosure/release.py +858 -0
  21. policyengine-6.4.1/src/policyengine/disclosure/schema.py +278 -0
  22. policyengine-6.4.1/src/policyengine/disclosure/support.py +166 -0
  23. policyengine-6.4.1/src/policyengine/disclosure/validate.py +211 -0
  24. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/outputs/__init__.py +6 -0
  25. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/outputs/constituency_impact.py +12 -1
  26. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/outputs/local_authority_impact.py +12 -1
  27. policyengine-6.4.1/src/policyengine/outputs/uk_geography_disclosure.py +275 -0
  28. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/outputs/uk_geography_impact.py +19 -0
  29. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/uk/budget_reform_contract.py +8 -1
  30. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/uk/budget_reform_population.py +73 -13
  31. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/uk/model.py +6 -0
  32. {policyengine-6.3.4 → policyengine-6.4.1/src/policyengine.egg-info}/PKG-INFO +4 -4
  33. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine.egg-info/SOURCES.txt +22 -0
  34. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine.egg-info/requires.txt +3 -3
  35. policyengine-6.4.1/tests/fixtures/uk_contract/evaluate_case_rows.json +1 -0
  36. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_constituency_impact.py +29 -0
  37. policyengine-6.4.1/tests/test_disclosure_areas.py +132 -0
  38. policyengine-6.4.1/tests/test_disclosure_estimate.py +558 -0
  39. policyengine-6.4.1/tests/test_disclosure_noise.py +108 -0
  40. policyengine-6.4.1/tests/test_disclosure_release.py +678 -0
  41. policyengine-6.4.1/tests/test_disclosure_schema.py +186 -0
  42. policyengine-6.4.1/tests/test_disclosure_support.py +242 -0
  43. policyengine-6.4.1/tests/test_disclosure_validate.py +217 -0
  44. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_extra_variables.py +3 -0
  45. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_local_authority_impact.py +31 -0
  46. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_models.py +3 -3
  47. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_release_manifests.py +6 -6
  48. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_uk_budget_reform_contract.py +222 -0
  49. policyengine-6.4.1/tests/test_uk_geography_disclosure.py +277 -0
  50. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_uk_regions.py +1 -1
  51. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_uk_year_file_data_year.py +3 -1
  52. policyengine-6.4.1/tests/uk_contract_row_scenarios.py +208 -0
  53. {policyengine-6.3.4 → policyengine-6.4.1}/uv.lock +7 -7
  54. {policyengine-6.3.4 → policyengine-6.4.1}/.claude/policyengine-guide.md +0 -0
  55. {policyengine-6.3.4 → policyengine-6.4.1}/.claude/quick-reference.md +0 -0
  56. {policyengine-6.3.4 → policyengine-6.4.1}/.github/CONTRIBUTING.md +0 -0
  57. {policyengine-6.3.4 → policyengine-6.4.1}/.github/bump_version.py +0 -0
  58. {policyengine-6.3.4 → policyengine-6.4.1}/.github/capture-version.sh +0 -0
  59. {policyengine-6.3.4 → policyengine-6.4.1}/.github/changelog_template.md +0 -0
  60. {policyengine-6.3.4 → policyengine-6.4.1}/.github/check-changelog.sh +0 -0
  61. {policyengine-6.3.4 → policyengine-6.4.1}/.github/copilot-instructions.md +0 -0
  62. {policyengine-6.3.4 → policyengine-6.4.1}/.github/dispatch-policyengine-release.sh +0 -0
  63. {policyengine-6.3.4 → policyengine-6.4.1}/.github/fetch_version.py +0 -0
  64. {policyengine-6.3.4 → policyengine-6.4.1}/.github/get-changelog-diff.sh +0 -0
  65. {policyengine-6.3.4 → policyengine-6.4.1}/.github/has-functional-changes.sh +0 -0
  66. {policyengine-6.3.4 → policyengine-6.4.1}/.github/is-version-number-acceptable.sh +0 -0
  67. {policyengine-6.3.4 → policyengine-6.4.1}/.github/publish-git-tag.sh +0 -0
  68. {policyengine-6.3.4 → policyengine-6.4.1}/.github/wait-for-pypi.sh +0 -0
  69. {policyengine-6.3.4 → policyengine-6.4.1}/.github/workflows/draft-pdf.yml +0 -0
  70. {policyengine-6.3.4 → policyengine-6.4.1}/.github/workflows/pr_code_changes.yaml +0 -0
  71. {policyengine-6.3.4 → policyengine-6.4.1}/.github/workflows/pr_docs_changes.yaml +0 -0
  72. {policyengine-6.3.4 → policyengine-6.4.1}/.github/workflows/push.yaml +0 -0
  73. {policyengine-6.3.4 → policyengine-6.4.1}/.gitignore +0 -0
  74. {policyengine-6.3.4 → policyengine-6.4.1}/.python-version +0 -0
  75. {policyengine-6.3.4 → policyengine-6.4.1}/AGENTS.md +0 -0
  76. {policyengine-6.3.4 → policyengine-6.4.1}/CITATION.cff +0 -0
  77. {policyengine-6.3.4 → policyengine-6.4.1}/CLAUDE.md +0 -0
  78. {policyengine-6.3.4 → policyengine-6.4.1}/CODE_OF_CONDUCT.md +0 -0
  79. {policyengine-6.3.4 → policyengine-6.4.1}/CONTRIBUTING.md +0 -0
  80. {policyengine-6.3.4 → policyengine-6.4.1}/LICENSE +0 -0
  81. {policyengine-6.3.4 → policyengine-6.4.1}/Makefile +0 -0
  82. {policyengine-6.3.4 → policyengine-6.4.1}/README.md +0 -0
  83. {policyengine-6.3.4 → policyengine-6.4.1}/architecture.png +0 -0
  84. {policyengine-6.3.4 → policyengine-6.4.1}/architecture.svg +0 -0
  85. {policyengine-6.3.4 → policyengine-6.4.1}/changelog.d/.gitkeep +0 -0
  86. {policyengine-6.3.4 → policyengine-6.4.1}/docs/.gitignore +0 -0
  87. {policyengine-6.3.4 → policyengine-6.4.1}/docs/_generator/README.md +0 -0
  88. {policyengine-6.3.4 → policyengine-6.4.1}/docs/_generator/build_reference.py +0 -0
  89. {policyengine-6.3.4 → policyengine-6.4.1}/docs/bundles.md +0 -0
  90. {policyengine-6.3.4 → policyengine-6.4.1}/docs/countries.md +0 -0
  91. {policyengine-6.3.4 → policyengine-6.4.1}/docs/data-publishing-design.md +0 -0
  92. {policyengine-6.3.4 → policyengine-6.4.1}/docs/decile-impact-spec.md +0 -0
  93. {policyengine-6.3.4 → policyengine-6.4.1}/docs/dev.md +0 -0
  94. {policyengine-6.3.4 → policyengine-6.4.1}/docs/engineering/runbooks/acs-local-wic-replacement.md +0 -0
  95. {policyengine-6.3.4 → policyengine-6.4.1}/docs/engineering/runbooks/canonical-spm-development.md +0 -0
  96. {policyengine-6.3.4 → policyengine-6.4.1}/docs/engineering/runbooks/us-legacy-spm-sources.md +0 -0
  97. {policyengine-6.3.4 → policyengine-6.4.1}/docs/engineering/skills/README.md +0 -0
  98. {policyengine-6.3.4 → policyengine-6.4.1}/docs/engineering/skills/github-prs.md +0 -0
  99. {policyengine-6.3.4 → policyengine-6.4.1}/docs/engineering/skills/repository-guidance.md +0 -0
  100. {policyengine-6.3.4 → policyengine-6.4.1}/docs/getting-started.md +0 -0
  101. {policyengine-6.3.4 → policyengine-6.4.1}/docs/households.md +0 -0
  102. {policyengine-6.3.4 → policyengine-6.4.1}/docs/impact-analysis.md +0 -0
  103. {policyengine-6.3.4 → policyengine-6.4.1}/docs/index.md +0 -0
  104. {policyengine-6.3.4 → policyengine-6.4.1}/docs/methodology/index.md +0 -0
  105. {policyengine-6.3.4 → policyengine-6.4.1}/docs/methodology/model-architecture.md +0 -0
  106. {policyengine-6.3.4 → policyengine-6.4.1}/docs/methodology/us-health-costs.md +0 -0
  107. {policyengine-6.3.4 → policyengine-6.4.1}/docs/microsim.md +0 -0
  108. {policyengine-6.3.4 → policyengine-6.4.1}/docs/programs/index.md +0 -0
  109. {policyengine-6.3.4 → policyengine-6.4.1}/docs/programs/us-chip.md +0 -0
  110. {policyengine-6.3.4 → policyengine-6.4.1}/docs/reference/index.md +0 -0
  111. {policyengine-6.3.4 → policyengine-6.4.1}/docs/reforms.md +0 -0
  112. {policyengine-6.3.4 → policyengine-6.4.1}/docs/release-bundles.md +0 -0
  113. {policyengine-6.3.4 → policyengine-6.4.1}/docs/run-records.md +0 -0
  114. {policyengine-6.3.4 → policyengine-6.4.1}/docs/trace-case-study.md +0 -0
  115. {policyengine-6.3.4 → policyengine-6.4.1}/docs/visualisation.md +0 -0
  116. {policyengine-6.3.4 → policyengine-6.4.1}/examples/belgium_axiom_pilot.py +0 -0
  117. {policyengine-6.3.4 → policyengine-6.4.1}/examples/employment_income_variation_uk.py +0 -0
  118. {policyengine-6.3.4 → policyengine-6.4.1}/examples/employment_income_variation_us.py +0 -0
  119. {policyengine-6.3.4 → policyengine-6.4.1}/examples/household_impact_example.py +0 -0
  120. {policyengine-6.3.4 → policyengine-6.4.1}/examples/income_bands_uk.py +0 -0
  121. {policyengine-6.3.4 → policyengine-6.4.1}/examples/income_distribution_us.py +0 -0
  122. {policyengine-6.3.4 → policyengine-6.4.1}/examples/paper_repro_uk.py +0 -0
  123. {policyengine-6.3.4 → policyengine-6.4.1}/examples/policy_change_uk.py +0 -0
  124. {policyengine-6.3.4 → policyengine-6.4.1}/examples/speedtest_us_simulation.py +0 -0
  125. {policyengine-6.3.4 → policyengine-6.4.1}/examples/us_budgetary_impact.py +0 -0
  126. {policyengine-6.3.4 → policyengine-6.4.1}/paper-preview.html +0 -0
  127. {policyengine-6.3.4 → policyengine-6.4.1}/paper.bib +0 -0
  128. {policyengine-6.3.4 → policyengine-6.4.1}/paper.md +0 -0
  129. {policyengine-6.3.4 → policyengine-6.4.1}/scripts/bundle.py +0 -0
  130. {policyengine-6.3.4 → policyengine-6.4.1}/scripts/certify_data_release.py +0 -0
  131. {policyengine-6.3.4 → policyengine-6.4.1}/scripts/check_data_staleness.py +0 -0
  132. {policyengine-6.3.4 → policyengine-6.4.1}/scripts/check_native_spm_loading.py +0 -0
  133. {policyengine-6.3.4 → policyengine-6.4.1}/scripts/check_release_credentials.py +0 -0
  134. {policyengine-6.3.4 → policyengine-6.4.1}/scripts/export_bundle_release_assets.py +0 -0
  135. {policyengine-6.3.4 → policyengine-6.4.1}/scripts/generate_bundle_artifacts.py +0 -0
  136. {policyengine-6.3.4 → policyengine-6.4.1}/scripts/generate_trace_tros.py +0 -0
  137. {policyengine-6.3.4 → policyengine-6.4.1}/scripts/prepare_package_bundle_update.py +0 -0
  138. {policyengine-6.3.4 → policyengine-6.4.1}/scripts/release_lock.py +0 -0
  139. {policyengine-6.3.4 → policyengine-6.4.1}/scripts/spm_bundle.py +0 -0
  140. {policyengine-6.3.4 → policyengine-6.4.1}/setup.cfg +0 -0
  141. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/__init__.py +0 -0
  142. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/bundle.py +0 -0
  143. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/cli.py +0 -0
  144. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/core/__init__.py +0 -0
  145. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/core/cache.py +0 -0
  146. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/core/dataset.py +0 -0
  147. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/core/dynamic.py +0 -0
  148. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/core/output.py +0 -0
  149. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/core/parameter.py +0 -0
  150. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/core/parameter_node.py +0 -0
  151. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/core/parameter_value.py +0 -0
  152. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/core/policy.py +0 -0
  153. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/core/region.py +0 -0
  154. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/core/run_record.py +0 -0
  155. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/core/scoping_strategy.py +0 -0
  156. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/core/simulation.py +0 -0
  157. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/core/spm.py +0 -0
  158. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/core/tax_benefit_model.py +0 -0
  159. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/core/tax_benefit_model_version.py +0 -0
  160. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/core/variable.py +0 -0
  161. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/countries/__init__.py +0 -0
  162. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/countries/uk/__init__.py +0 -0
  163. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/countries/uk/regions.py +0 -0
  164. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/countries/us/__init__.py +0 -0
  165. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/countries/us/data/__init__.py +0 -0
  166. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/countries/us/data/districts.py +0 -0
  167. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/countries/us/data/places.py +0 -0
  168. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/countries/us/data/states.py +0 -0
  169. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/countries/us/regions.py +0 -0
  170. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/data/__init__.py +0 -0
  171. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/data/schemas/trace_tro.schema.json +0 -0
  172. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/data/uk_geography_assets.py +0 -0
  173. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/derivations/__init__.py +0 -0
  174. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/derivations/narrate.py +0 -0
  175. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/derivations/trace.py +0 -0
  176. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/graph/__init__.py +0 -0
  177. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/graph/extractor.py +0 -0
  178. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/graph/graph.py +0 -0
  179. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/outputs/aggregate.py +0 -0
  180. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/outputs/change_aggregate.py +0 -0
  181. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/outputs/cliff_impact.py +0 -0
  182. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/outputs/congressional_district_impact.py +0 -0
  183. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/outputs/decile_analysis.py +0 -0
  184. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/outputs/decile_grouping.py +0 -0
  185. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/outputs/decile_impact.py +0 -0
  186. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/outputs/extra_variables.py +0 -0
  187. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/outputs/inequality.py +0 -0
  188. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/outputs/intra_decile_impact.py +0 -0
  189. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/outputs/labor_supply_response.py +0 -0
  190. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/outputs/poverty.py +0 -0
  191. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/outputs/program_statistics.py +0 -0
  192. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/outputs/uk_geography_assets.py +0 -0
  193. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/provenance/__init__.py +0 -0
  194. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/provenance/bundle_validation.py +0 -0
  195. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/provenance/certification.py +0 -0
  196. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/provenance/dataset_materialization.py +0 -0
  197. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/provenance/manifest.py +0 -0
  198. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/provenance/pyproject_pins.py +0 -0
  199. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/provenance/spm_configuration.py +0 -0
  200. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/provenance/trace.py +0 -0
  201. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/provenance/verify.py +0 -0
  202. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/provenance/zenodo.py +0 -0
  203. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/results/__init__.py +0 -0
  204. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/results/schema.py +0 -0
  205. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/results/trace_tro.py +0 -0
  206. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/results/tracking.py +0 -0
  207. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/be/__init__.py +0 -0
  208. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/be/datasets.py +0 -0
  209. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/be/model.py +0 -0
  210. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/common/__init__.py +0 -0
  211. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/common/axes.py +0 -0
  212. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/common/extra_variables.py +0 -0
  213. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/common/household.py +0 -0
  214. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/common/model_version.py +0 -0
  215. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/common/reform.py +0 -0
  216. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/common/result.py +0 -0
  217. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/uk/__init__.py +0 -0
  218. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/uk/analysis.py +0 -0
  219. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/uk/datasets.py +0 -0
  220. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/uk/household.py +0 -0
  221. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/us/__init__.py +0 -0
  222. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/us/analysis.py +0 -0
  223. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/us/datasets.py +0 -0
  224. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/us/household.py +0 -0
  225. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/us/legacy_inputs.py +0 -0
  226. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/us/legacy_spm/__init__.py +0 -0
  227. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/us/legacy_spm/declarations.py +0 -0
  228. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/us/legacy_spm/independence.py +0 -0
  229. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/us/legacy_spm/model_adapter.py +0 -0
  230. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/us/legacy_spm/record.py +0 -0
  231. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/us/legacy_spm/registry.py +0 -0
  232. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/us/legacy_spm/runs.py +0 -0
  233. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/us/legacy_spm/universe.py +0 -0
  234. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/us/model.py +0 -0
  235. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/tax_benefit_models/us/spm.py +0 -0
  236. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/utils/__init__.py +0 -0
  237. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/utils/dates.py +0 -0
  238. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/utils/design.py +0 -0
  239. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/utils/entity_utils.py +0 -0
  240. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/utils/errors.py +0 -0
  241. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/utils/hashing.py +0 -0
  242. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/utils/household_validation.py +0 -0
  243. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/utils/parameter_labels.py +0 -0
  244. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/utils/parametric_reforms.py +0 -0
  245. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine/utils/plotting.py +0 -0
  246. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine.egg-info/dependency_links.txt +0 -0
  247. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine.egg-info/entry_points.txt +0 -0
  248. {policyengine-6.3.4 → policyengine-6.4.1}/src/policyengine.egg-info/top_level.txt +0 -0
  249. {policyengine-6.3.4 → policyengine-6.4.1}/tests/__init__.py +0 -0
  250. {policyengine-6.3.4 → policyengine-6.4.1}/tests/conftest.py +0 -0
  251. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/__init__.py +0 -0
  252. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/filtering_fixtures.py +0 -0
  253. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/household_calculator_snapshots/uk_couple_two_kids.json +0 -0
  254. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/household_calculator_snapshots/uk_model_surface.json +0 -0
  255. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/household_calculator_snapshots/uk_single_adult_employment_income.json +0 -0
  256. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/household_calculator_snapshots/uk_single_adult_no_income.json +0 -0
  257. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/household_calculator_snapshots/uk_single_parent_one_child.json +0 -0
  258. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/household_calculator_snapshots/us_married_two_kids_high_income.json +0 -0
  259. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/household_calculator_snapshots/us_model_surface.json +0 -0
  260. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/household_calculator_snapshots/us_single_adult_employment_income.json +0 -0
  261. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/household_calculator_snapshots/us_single_adult_no_income.json +0 -0
  262. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/household_calculator_snapshots/us_single_parent_one_child.json +0 -0
  263. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/parameter_labels_fixtures.py +0 -0
  264. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/parametric_reforms_fixtures.py +0 -0
  265. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/poverty_by_demographics_fixtures.py +0 -0
  266. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/region_fixtures.py +0 -0
  267. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/release_tros/README.md +0 -0
  268. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/release_tros/country-models.lock.toml +0 -0
  269. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/release_tros/manifest.json +0 -0
  270. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/release_tros/us-release_manifest.json +0 -0
  271. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/release_tros/us.trace.tro.jsonld +0 -0
  272. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/spm_development.py +0 -0
  273. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/us_reform_fixtures.py +0 -0
  274. {policyengine-6.3.4 → policyengine-6.4.1}/tests/fixtures/variable_label_fixtures.py +0 -0
  275. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_aggregate.py +0 -0
  276. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_be_axiom_pilot.py +0 -0
  277. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_budgetary_impact.py +0 -0
  278. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_bump_version.py +0 -0
  279. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_bundle.py +0 -0
  280. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_bundle_metadata.py +0 -0
  281. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_cache.py +0 -0
  282. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_certify_data_release.py +0 -0
  283. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_change_aggregate.py +0 -0
  284. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_cliff_impact.py +0 -0
  285. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_cliff_impact_analysis.py +0 -0
  286. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_congressional_district_impact.py +0 -0
  287. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_dataset_materialization.py +0 -0
  288. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_dataset_persistence.py +0 -0
  289. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_dataset_runtime.py +0 -0
  290. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_dataset_source.py +0 -0
  291. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_decile_grouping.py +0 -0
  292. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_derivations.py +0 -0
  293. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_dict_reforms_on_simulation.py +0 -0
  294. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_entity_mapping.py +0 -0
  295. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_entity_utils.py +0 -0
  296. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_entity_utils_characterization.py +0 -0
  297. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_errors.py +0 -0
  298. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_filtering.py +0 -0
  299. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_generate_trace_tros.py +0 -0
  300. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_graph/__init__.py +0 -0
  301. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_graph/conftest.py +0 -0
  302. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_graph/test_extractor.py +0 -0
  303. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_household_calculator_snapshot.py +0 -0
  304. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_household_impact.py +0 -0
  305. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_inequality.py +0 -0
  306. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_intra_decile_impact.py +0 -0
  307. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_labor_supply_response.py +0 -0
  308. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_manifest_version_mismatch.py +0 -0
  309. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_pandas3_compatibility.py +0 -0
  310. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_parameter_labels.py +0 -0
  311. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_parameter_nodes.py +0 -0
  312. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_parameter_values.py +0 -0
  313. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_parametric_reforms.py +0 -0
  314. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_poverty.py +0 -0
  315. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_poverty_by_demographics.py +0 -0
  316. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_poverty_run.py +0 -0
  317. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_preservation_mirror.py +0 -0
  318. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_program_statistics.py +0 -0
  319. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_pyproject_pins.py +0 -0
  320. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_region.py +0 -0
  321. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_region_group_strategy.py +0 -0
  322. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_release_credentials.py +0 -0
  323. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_release_lock.py +0 -0
  324. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_release_tro_generation.py +0 -0
  325. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_results.py +0 -0
  326. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_run_record.py +0 -0
  327. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_scoping_strategy.py +0 -0
  328. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_small_follow_ups.py +0 -0
  329. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_spm_bundle_bootstrap.py +0 -0
  330. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_spm_bundle_tooling.py +0 -0
  331. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_spm_decile.py +0 -0
  332. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_spm_development_authentication.py +0 -0
  333. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_spm_household.py +0 -0
  334. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_spm_model.py +0 -0
  335. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_spm_selection.py +0 -0
  336. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_spm_selection_schema.py +0 -0
  337. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_trace_tro.py +0 -0
  338. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_trace_tro_verify.py +0 -0
  339. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_uk_analysis.py +0 -0
  340. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_uk_dated_reforms.py +0 -0
  341. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_uk_geography_assets.py +0 -0
  342. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_uk_program_statistics.py +0 -0
  343. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_us_legacy_inputs.py +0 -0
  344. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_us_legacy_inputs_integration.py +0 -0
  345. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_us_legacy_spm.py +0 -0
  346. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_us_legacy_spm_integration.py +0 -0
  347. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_us_legacy_spm_model_contract.py +0 -0
  348. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_us_long_term_datasets.py +0 -0
  349. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_us_microsim_structural_reforms.py +0 -0
  350. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_us_native_alignment.py +0 -0
  351. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_us_native_hdf_weights.py +0 -0
  352. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_us_program_statistics.py +0 -0
  353. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_us_reform_application.py +0 -0
  354. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_us_regions.py +0 -0
  355. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_us_year_file_stamp.py +0 -0
  356. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_variable_labels.py +0 -0
  357. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_variable_metadata.py +0 -0
  358. {policyengine-6.3.4 → policyengine-6.4.1}/tests/test_zenodo_mirror.py +0 -0
@@ -1,3 +1,22 @@
1
+ ## [6.4.1] - 2026-10-15
2
+
3
+ ### Changed
4
+
5
+ - The UK Budget reform contract report gives each assertion a unique id, and the population checks declare the rows a full run emits.
6
+ - Certify policyengine-uk 2.130.1 with policyengine-uk-data 1.58.1 (enhanced_frs_2024_25) as the UK default pair, on the publisher's compatibility claim.
7
+
8
+
9
+ ## [6.4.0] - 2026-10-14
10
+
11
+ ### Added
12
+
13
+ - Added `policyengine.disclosure`, a self-contained layer for statistical disclosure control and uncertainty on area-level outputs, and `compute_uk_geography_disclosure` for UK regions, constituencies and local authorities. Each published number rests on at least 10 distinct survey households (clones counted once through `source_household_id`), carries a 90% bootstrap interval and is rounded to the order of its standard error. Rows are `reported`, `approximate` or `withheld`. A release check reports numbers a reader could recover by subtraction across geography levels, a package and its components, and earlier publications. Keyed noise per survey household is available behind `DisclosureSettings(noise=True)`. `validate_public` and `python -m policyengine.disclosure validate` check the format of published files. UK runs now keep `source_household_id` and `region` on the household output when the dataset provides them.
14
+
15
+ ### Changed
16
+
17
+ - Deprecated `compute_uk_constituency_impacts`, `compute_uk_local_authority_impacts` and their `ConstituencyImpact` and `LocalAuthorityImpact` classes. Their output is not disclosure-controlled; use `compute_uk_geography_disclosure` instead. They now log a warning through the `policyengine` logger and emit a `DeprecationWarning`, and otherwise behave as before.
18
+
19
+
1
20
  ## [6.3.4] - 2026-10-13
2
21
 
3
22
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: policyengine
3
- Version: 6.3.4
3
+ Version: 6.4.1
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
@@ -687,11 +687,11 @@ Requires-Dist: networkx>=3.0; extra == "graph"
687
687
  Provides-Extra: models
688
688
  Requires-Dist: policyengine-core==3.33.0; extra == "models"
689
689
  Requires-Dist: policyengine-us==2.2.1; extra == "models"
690
- Requires-Dist: policyengine-uk==2.126.12; extra == "models"
690
+ Requires-Dist: policyengine-uk==2.130.1; extra == "models"
691
691
  Requires-Dist: spm-calculator==1.0.0; extra == "models"
692
692
  Provides-Extra: uk
693
693
  Requires-Dist: policyengine-core==3.33.0; extra == "uk"
694
- Requires-Dist: policyengine-uk==2.126.12; extra == "uk"
694
+ Requires-Dist: policyengine-uk==2.130.1; extra == "uk"
695
695
  Provides-Extra: us
696
696
  Requires-Dist: policyengine-core==3.33.0; extra == "us"
697
697
  Requires-Dist: policyengine-us==2.2.1; extra == "us"
@@ -713,7 +713,7 @@ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
713
713
  Requires-Dist: hypothesis>=6.100.0; extra == "dev"
714
714
  Requires-Dist: policyengine-core==3.33.0; extra == "dev"
715
715
  Requires-Dist: policyengine-us==2.2.1; extra == "dev"
716
- Requires-Dist: policyengine-uk==2.126.12; extra == "dev"
716
+ Requires-Dist: policyengine-uk==2.130.1; extra == "dev"
717
717
  Requires-Dist: spm-calculator==1.0.0; extra == "dev"
718
718
  Dynamic: license-file
719
719
 
@@ -39,6 +39,7 @@ website:
39
39
  - impact-analysis.md
40
40
  - outputs.md
41
41
  - regions.md
42
+ - disclosure.md
42
43
  - visualisation.md
43
44
  - section: "Methodology"
44
45
  contents:
@@ -0,0 +1,239 @@
1
+ ---
2
+ title: "Disclosure control"
3
+ ---
4
+
5
+ Local results from survey microdata rest on few households, and a licence
6
+ binds every published number. `policyengine.disclosure` turns per-household
7
+ results into area tables that follow the licence's rules and carry their
8
+ uncertainty. Use it for every UK result below the national level that leaves
9
+ your machine.
10
+
11
+ ```python
12
+ from policyengine.outputs import compute_uk_geography_disclosure
13
+
14
+ impact = compute_uk_geography_disclosure(
15
+ baseline_simulation=baseline,
16
+ reform_simulation=reform,
17
+ year=2026,
18
+ levels=("region", "constituency"),
19
+ reform="budget",
20
+ )
21
+ impact.findings # what blocks publication, if anything
22
+ impact.public # the rows that may be published, or None while blocked
23
+ ```
24
+
25
+ The UK datasets derive from surveys under the UK Data Service End User
26
+ Licence. The defaults below follow its disclosure rules. You can raise them
27
+ for a study with stricter conditions; you cannot lower them.
28
+
29
+ ## What a published row holds
30
+
31
+ Each row is one outcome for one area, reform and year.
32
+
33
+ | Column | Content |
34
+ |---|---|
35
+ | `level`, `area_code`, `reform`, `year`, `outcome` | The key of the row |
36
+ | `value`, `lo`, `hi` | The rounded estimate and its 90% interval |
37
+ | `direction` | `gain`, `loss`, `too_close_to_call` or `no_change` |
38
+ | `tier` | `reported`, `approximate` or `withheld` |
39
+ | `reason` | `thin_support` for an approximate row, `disclosure_control` for a withheld row |
40
+ | `support_band` | `<10`, `10–29` or `30+` survey households, never an exact count |
41
+ | `noise` | Whether the row carries keyed noise |
42
+ | `estimator` | `direct`: the estimate comes from the area's own records |
43
+
44
+ The outcomes are `average_change`, `relative_change`, `total_change`,
45
+ `share_gaining`, `share_losing` and `share_no_change`. A withheld row carries
46
+ no value, interval, direction or support band.
47
+
48
+ ## The rules
49
+
50
+ **Count floor.** A number must rest on at least 10 distinct survey households
51
+ that contribute to it. For a change, a household contributes when its income
52
+ changes by more than `change_tolerance` (£1 a year, which is also the lowest
53
+ value the setting takes). Copies and clones of one survey household count
54
+ once, through the dataset's `source_household_id`. For a share, both sides
55
+ must pass: the households that gain and those that do not.
56
+
57
+ **Dominance.** While noise is off, a number is withheld in two cases:
58
+
59
+ - one survey household carries more than half of it, by size;
60
+ - the p% rule: a reader of the number, or one household that knows its own
61
+ part, could estimate some household's part to within 10%. Every household
62
+ is a possible target and every other household a possible reader, so the
63
+ rule also catches a total whose other parts cancel.
64
+
65
+ These rules protect each household against any reader and against any one
66
+ other household. They do not stop two or more households that pool what they
67
+ know: a number that three households carry almost alone passes, as long as 10
68
+ contribute.
69
+
70
+ **Effective sample.** Weights that differ a lot make an estimate less precise
71
+ than its household count suggests. Where the data gives one area and one
72
+ weight per record, a low effective sample makes a row `approximate`: it keeps
73
+ its value and interval, but the interval understates the uncertainty. Where
74
+ the data is an area × household weight matrix, the household count passes by
75
+ construction, so the effective sample must also be at least 10. Weights given
76
+ as a matrix keep this rule whatever they hold: one row of a dense matrix has
77
+ one weight per record, yet every household of the nation sits in that area.
78
+
79
+ **Zero cells.** A cell with no contributing household is published as zero,
80
+ with the interval [0, 0] and the direction `no_change`. It means that no
81
+ surveyed household in the area changed, not that no household there does. Its
82
+ support band counts the households it was measured on.
83
+
84
+ **Settings.** The layer validates its settings again before it uses them, so
85
+ a copy made with `model_copy(update=...)` cannot lower a floor.
86
+
87
+ **Linked outcomes.** Average, relative and total change share a numerator, so
88
+ they are published or withheld together. Gaining, losing and no change add to
89
+ one, so the third share is published only when it is safe to derive.
90
+
91
+ ## Uncertainty and rounding
92
+
93
+ Intervals come from a Poisson bootstrap over survey households with 1,000
94
+ replicates. A ratio resamples its numerator and denominator together. The
95
+ weights stay fixed, so the intervals leave out calibration uncertainty and are
96
+ too narrow, most of all where support is thin.
97
+
98
+ Values are rounded to the order of magnitude of their standard error. The
99
+ interval is rounded outward on the same base.
100
+
101
+ ## The release check
102
+
103
+ Published numbers constrain each other. If a region is public and all but one
104
+ of its constituencies are public, subtraction gives the last one. The check
105
+ looks for such remainders:
106
+
107
+ - child areas against their parent area (constituencies in regions, regions in
108
+ the nation);
109
+ - the components of a package against the package;
110
+ - when two levels that do not nest are released together, an area against
111
+ the areas of the other level that lie wholly inside it, and against the
112
+ areas that meet it (their sum minus the area is what they hold outside it);
113
+ - earlier publications, passed as `prior_public`.
114
+
115
+ A remainder gets the rules of a cell. Estimates added with `publish=False`
116
+ are a reference: the release does not emit their rows, but it treats the rows
117
+ that pass as public elsewhere and the withheld ones as secret, and checks
118
+ them like its own.
119
+
120
+ `compute_uk_geography_disclosure` always checks constituencies and local
121
+ authorities against their region, and regions against the nation, also when
122
+ you publish one level only. A level given as a weight matrix needs
123
+ `parent_of=` with the region of each area; without it the function raises,
124
+ because a check against the nation alone nearly always passes.
125
+
126
+ The check reports; it does not withhold cells by itself. A blocking finding
127
+ names the group and suggests a cell. Withhold it, then check again. When a
128
+ finding suggests no cell, withhold the parent (`finding.group` at
129
+ `finding.group_level`) instead:
130
+
131
+ ```python
132
+ for finding in impact.findings:
133
+ print(finding.code, finding.group, finding.suggested)
134
+
135
+ impact.release.withhold(
136
+ level="constituency",
137
+ area_code="E14001234",
138
+ reform="budget",
139
+ year=2026,
140
+ outcome="average_change",
141
+ )
142
+ impact.refresh() # checks again and fills impact.public once nothing blocks
143
+ ```
144
+
145
+ Two findings can be accepted after review, with `acknowledge=[...]`:
146
+
147
+ - `unchecked_overlap`: levels that overlap without nesting, such as
148
+ constituencies and local authorities, are released together. The release
149
+ checks each area against the areas wholly inside it and against the areas
150
+ that meet it. It does not check other differences between overlapping
151
+ areas, and on weight-matrix data it checks none.
152
+ - `noise_reference_public`: an earlier table shows the same cells under
153
+ another noise setting.
154
+
155
+ ## Noise
156
+
157
+ Noise is off by default. With `DisclosureSettings(noise=True)`, each survey
158
+ household's change is scaled by a fixed factor between 8% and 12% away from 1,
159
+ derived from its id and a secret key. The same household has the same factor
160
+ in every table and every rebuild. Noise replaces the dominance rule; the count
161
+ floor still applies. The range cannot start closer to 1 than 8%.
162
+
163
+ A number that one household dominates, published with noise, gives that
164
+ household's part to within 8 to 12%. The p% rule treats 10% as the line, so
165
+ noise puts such a number right at that line, not safely past it. Whether the
166
+ UK Data Service accepts noise in place of the dominance rule is a licence
167
+ question, which is why noise stays off by default.
168
+
169
+ - **Size.** The noise in a total is about a tenth of that total's sampling
170
+ error.
171
+ - **Key.** Pass `noise_key=` or set `POLICYENGINE_DISCLOSURE_NOISE_KEY`. There
172
+ is no default key. Keep the key out of repositories and CI logs, and do not
173
+ change it between releases of one dataset: two noised versions of a number
174
+ average towards the truth.
175
+ - **Limits.** Denominators carry no noise. The factor cancels in a ratio of
176
+ two numbers that the same household dominates. A remainder that subtraction
177
+ recovers is the noised sum of the withheld cells, so it has the protection
178
+ of a noised cell and no more: the count floor applies to it, and noise
179
+ stands in for the dominance rule as it does for a cell. An un-noised copy
180
+ of the same cell gives the factor away.
181
+
182
+ ## Validating published files
183
+
184
+ `validate_public` checks the format of a table, a CSV file or a directory. It
185
+ needs no key and no microdata, so it can run on every pull request:
186
+
187
+ ```bash
188
+ python -m policyengine.disclosure validate public/data --reviewed notes.csv
189
+ ```
190
+
191
+ It rejects private audit columns, unknown tiers, a value in a withheld row,
192
+ unrounded values, a direction that does not match its interval, and files it
193
+ does not recognise. It cannot tell whether a number rested on enough
194
+ households; only the release that made the file can.
195
+
196
+ ## Private objects
197
+
198
+ `audit_dir=` writes the exact values, household counts and shares behind every
199
+ row. The directory must be outside any git repository.
200
+
201
+ `AreaEstimates`, the object that `estimate_change` returns, holds the same
202
+ exact values. With noise on it also holds the noise factor of every household
203
+ that gains or loses, and within one dataset those factors do the same job as
204
+ the key. Keep it in memory and treat it as you treat the key. It cannot be
205
+ pickled or copied.
206
+
207
+ ## What a reader can still derive
208
+
209
+ `average_change` and `relative_change` together give the area's total
210
+ baseline income to within a few per cent (household count × average ÷
211
+ relative). That total is a denominator: it carries no noise and is not
212
+ checked for dominance.
213
+
214
+ ## Arrays without a simulation
215
+
216
+ The package works on arrays and imports nothing from the rest of the library:
217
+
218
+ ```python
219
+ from policyengine.disclosure import AreaWeights, Release, estimate_change
220
+
221
+ seats = estimate_change(
222
+ level="constituency",
223
+ reform="budget",
224
+ year=2026,
225
+ areas=AreaWeights.from_labels(seat_codes, household_weights),
226
+ source_ids=source_household_ids,
227
+ baseline=baseline_net_income,
228
+ reformed=reformed_net_income,
229
+ )
230
+ release = Release()
231
+ release.add(seats)
232
+ release.check()
233
+ public = release.to_public()
234
+ ```
235
+
236
+ `AreaWeights.from_matrix(matrix, area_codes)` takes an area × household weight
237
+ matrix instead. A dataset without `source_household_id` needs `source_ids=`
238
+ from the caller; the layer does not guess which records share a survey
239
+ household.
@@ -2,8 +2,10 @@
2
2
 
3
3
  Use this runbook to certify a new national US Populace release together with
4
4
  the shared ACS-local release used for state and congressional-district
5
- simulations. Read the [data certification](../skills/data-certification.md)
6
- skill first for validation semantics.
5
+ simulations. For now, state and district simulations use the national release
6
+ instead; see the interim exception in Step 2. Read the
7
+ [data certification](../skills/data-certification.md) skill first for
8
+ validation semantics.
7
9
 
8
10
  This historical filename is retained so existing links remain valid; the
9
11
  procedure itself is release-independent.
@@ -13,7 +15,8 @@ procedure itself is release-independent.
13
15
  Both release manifests have been published to `policyengine/populace-us`, and
14
16
  the model version used by the national release is known. The national release
15
17
  will remain the default dataset; the local-area release will remain
16
- non-default and be selected only for supported regional runs.
18
+ non-default and be selected only for supported regional runs (none while the
19
+ interim exception in Step 2 holds).
17
20
 
18
21
  ## Prerequisites
19
22
 
@@ -60,6 +63,19 @@ This one command:
60
63
  - writes the changelog fragment
61
64
  `changelog.d/certify-us-$NATIONAL_RELEASE_ID.changed.md`.
62
65
 
66
+ **Interim exception (from policyengine 6.3.4; PolicyEngine/policyengine.py#604).**
67
+ State and congressional-district regions filter the national dataset until an
68
+ ACS-local release has no SPM unit without a classified adult
69
+ (PolicyEngine/microcosm#1263). Run the command above unchanged, with
70
+ `--regional-manifest-uri`: without it, certification drops the ACS-local
71
+ dataset and leaves no state or district template. Then, before Step 3, set
72
+ `region_datasets.state` and `region_datasets.congressional_district` in
73
+ `data_releases.us` to `{"path_template": "populace_us_2024.h5"}` by hand. In
74
+ Step 5, expect those two templates to select the national artifact, not the
75
+ local-area one. The data-certification skill
76
+ (`docs/engineering/skills/data-certification.md`) says what must hold before
77
+ the exception ends.
78
+
63
79
  ## Step 3 — regenerate TRACE sidecars
64
80
 
65
81
  The certify step does not touch TRACE sidecars. Regenerate them so they record
@@ -87,15 +103,16 @@ PE_UPDATE_SNAPSHOTS=1 pytest tests/test_household_calculator_snapshot.py
87
103
 
88
104
  ## Step 5 — certify and verify the local-area release
89
105
 
90
- The certification command passes the immutable ACS-local release manifest with
91
- `--regional-manifest-uri`. Confirm all of the following:
106
+ The certification command passes the immutable ACS-local
107
+ release manifest with `--regional-manifest-uri`. Confirm all of the following:
92
108
 
93
109
  - `default_dataset` remains the national Populace dataset;
94
110
  - the local-area artifact appears in `data_releases.us.datasets` with its
95
111
  repository type, immutable revision, and SHA-256;
96
112
  - `region_datasets.national` selects the national artifact;
97
113
  - `region_datasets.state` and `region_datasets.congressional_district` select
98
- the shared local-area artifact;
114
+ the shared local-area artifact (the national artifact while the interim
115
+ exception in Step 2 holds);
99
116
  - no derived `states/*.h5` or `districts/*.h5` artifacts are runtime inputs.
100
117
 
101
118
  ```bash
@@ -35,8 +35,33 @@ python scripts/bundle.py certify-data --country us --data-producer populace \
35
35
  --model-version "<policyengine-us-version>"
36
36
  ```
37
37
 
38
- US state and congressional-district regions are row filters over one shared,
39
- certified ACS-local Populace dataset. Its release manifest must declare
38
+ **Interim exception (from policyengine 6.3.4; PolicyEngine/policyengine.py#604).**
39
+ State and congressional-district regions currently filter the certified
40
+ national dataset, not the ACS-local one. The certified ACS-local release has
41
+ no source-backed SPM independence, so canonical SPM refuses its SPM units that
42
+ have no classified adult, and with them every state-region run
43
+ (PolicyEngine/microcosm#1263, PolicyEngine/policyengine-sim-api#747). The 6.3.4
44
+ manifest was certified with `--regional-manifest-uri`, as below, and then
45
+ edited by hand. Until the exception ends, do the same:
46
+
47
+ - still pass `--regional-manifest-uri`. Without it, certification rewrites
48
+ `data_releases.us` from the national manifest alone, which drops the
49
+ ACS-local dataset and leaves no state or district template at all;
50
+ - then set `region_datasets.state` and `region_datasets.congressional_district`
51
+ to `{"path_template": "populace_us_2024.h5"}` by hand, before regenerating
52
+ the TRACE sidecars. `tests/test_release_manifests.py` fails if they name
53
+ the ACS-local file, and also if the ACS-local dataset is no longer
54
+ certified.
55
+
56
+ Restore the ACS-local regions only with an ACS-local release in which every
57
+ SPM unit has at least one classified adult under the pinned spm-calculator's
58
+ rule (zero unclassified units), and after a state-region run through the
59
+ public wrapper succeeds on it. Then remove this exception, the temporary test
60
+ and the matching notes in `docs/regions.md` and `docs/bundles.md`.
61
+
62
+ Outside that exception, US state and congressional-district regions are row
63
+ filters over one shared, certified ACS-local Populace dataset. Its release
64
+ manifest must declare
40
65
  `dataset_role: non_default_local_area`, `is_default: false`, no default
41
66
  datasets, and exactly one pinned H5 microdata artifact. Certification writes:
42
67
 
@@ -16,6 +16,13 @@ Applying parametric reforms, comparing baseline and reform with `ChangeAggregate
16
16
  ```{.python include="../examples/policy_change_uk.py"}
17
17
  ```
18
18
 
19
+ ## UK geography results under disclosure control
20
+
21
+ Estimating results by area from per-household arrays, checking the release, withholding a cell and validating the public table. The data is synthetic.
22
+
23
+ ```{.python include="../examples/uk_geography_disclosure.py"}
24
+ ```
25
+
19
26
  ## UK income bands
20
27
 
21
28
  Calculating net income and tax by income decile using representative microdata and `Aggregate` with quantile filters.
@@ -266,7 +266,28 @@ for row in impacts.district_results:
266
266
  print(row["district_geoid"], row["avg_change"], row["winner_percentage"])
267
267
  ```
268
268
 
269
- ### UK constituencies / local authorities
269
+ ### UK regions, constituencies and local authorities
270
+
271
+ `compute_uk_geography_disclosure` returns results by area under disclosure
272
+ control: each number passes a floor on the survey households behind it and
273
+ carries a 90% interval. Use it for anything you publish. See
274
+ [Disclosure control](disclosure.md).
275
+
276
+ ```python
277
+ from policyengine.outputs import compute_uk_geography_disclosure
278
+
279
+ impact = compute_uk_geography_disclosure(
280
+ baseline_simulation=baseline,
281
+ reform_simulation=reform,
282
+ year=2026,
283
+ levels=("region", "constituency"),
284
+ )
285
+ impact.public.to_frame()
286
+ ```
287
+
288
+ The two older functions below are deprecated. Their output is not
289
+ disclosure-controlled, so do not publish it when the data is licensed survey
290
+ microdata.
270
291
 
271
292
  Constituency and local-authority breakdowns group household output rows by
272
293
  dataset-provided longwise geography columns. Constituencies use
@@ -93,6 +93,11 @@ national UK dataset.
93
93
 
94
94
  ## UK parliamentary constituencies
95
95
 
96
+ To publish results by constituency, local authority or region, use
97
+ `compute_uk_geography_disclosure`; see [Disclosure control](disclosure.md).
98
+ The functions in this section and the next are deprecated: their output is
99
+ not disclosure-controlled.
100
+
96
101
  Constituency-level impacts group household output rows by the longwise
97
102
  `constituency_code_oa` column carried by the dataset. If the constituency CSV is
98
103
  available locally or from the PolicyEngine UK GCS bucket, PolicyEngine uses it
@@ -0,0 +1,84 @@
1
+ """Example: Publish area results under disclosure control.
2
+
3
+ This script demonstrates:
4
+ 1. Building estimates for two geography levels from per-household arrays
5
+ 2. Checking a release for numbers a reader could recover by subtraction
6
+ 3. Resolving a finding by withholding the suggested cell
7
+ 4. Writing the public table and validating its format
8
+
9
+ The data is synthetic, so the script needs no dataset.
10
+
11
+ Run: python examples/uk_geography_disclosure.py
12
+ """
13
+
14
+ import tempfile
15
+ from pathlib import Path
16
+
17
+ import numpy as np
18
+
19
+ from policyengine.disclosure import (
20
+ AreaWeights,
21
+ Release,
22
+ estimate_change,
23
+ validate_public,
24
+ )
25
+
26
+
27
+ def main() -> Path:
28
+ rng = np.random.default_rng(0)
29
+ # Two regions of three seats, 400 households a seat. In seat S1 the
30
+ # reform reaches only a handful of households.
31
+ seats = np.repeat(["S1", "S2", "S3", "S4", "S5", "S6"], 400)
32
+ regions = np.repeat(["North", "South"], 1_200)
33
+ reach = np.where(seats == "S1", 0.01, np.where(seats == "S2", 0.08, 0.35))
34
+ affected = rng.random(len(seats)) < reach
35
+ weights = rng.uniform(150.0, 450.0, len(seats))
36
+ baseline = rng.lognormal(10.4, 0.5, len(seats))
37
+ reformed = baseline + np.where(affected, rng.uniform(100.0, 900.0, len(seats)), 0)
38
+ source_ids = np.arange(len(seats))
39
+
40
+ release = Release()
41
+ for level, codes in (("region", regions), ("constituency", seats)):
42
+ release.add(
43
+ estimate_change(
44
+ level=level,
45
+ reform="example",
46
+ year=2026,
47
+ areas=AreaWeights.from_labels(codes, weights),
48
+ source_ids=source_ids,
49
+ baseline=baseline,
50
+ reformed=reformed,
51
+ )
52
+ )
53
+ release.add_nesting("constituency", "region")
54
+
55
+ # S1 is withheld. Its region is public, so subtraction would recover it
56
+ # unless a second seat is withheld too.
57
+ for finding in release.check():
58
+ print(f"{finding.code}: {finding.message} Suggested: {finding.suggested}")
59
+ release.withhold(
60
+ level="constituency",
61
+ area_code=finding.suggested,
62
+ reform="example",
63
+ year=2026,
64
+ outcome="average_change" if finding.family == "change" else finding.family,
65
+ )
66
+ assert release.check() == []
67
+
68
+ public = release.to_public()
69
+ frame = public.to_frame()
70
+ print(
71
+ frame[frame["outcome"] == "average_change"][
72
+ ["level", "area_code", "value", "lo", "hi", "tier", "support_band"]
73
+ ].to_string(index=False)
74
+ )
75
+
76
+ output = Path(tempfile.mkdtemp(prefix="disclosure-example-"))
77
+ public.to_csv(output / "example.csv")
78
+ assert validate_public(output) == []
79
+ print(f"Wrote and validated {output / 'example.csv'}")
80
+ return output
81
+
82
+
83
+ if __name__ == "__main__":
84
+ main()
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "policyengine"
7
- version = "6.3.4"
7
+ version = "6.4.1"
8
8
  description = "A package to conduct policy analysis using PolicyEngine tax-benefit models."
9
9
  readme = "README.md"
10
10
  authors = [
@@ -42,12 +42,12 @@ graph = [
42
42
  models = [
43
43
  "policyengine-core==3.33.0",
44
44
  "policyengine-us==2.2.1",
45
- "policyengine-uk==2.126.12",
45
+ "policyengine-uk==2.130.1",
46
46
  "spm-calculator==1.0.0",
47
47
  ]
48
48
  uk = [
49
49
  "policyengine-core==3.33.0",
50
- "policyengine-uk==2.126.12",
50
+ "policyengine-uk==2.130.1",
51
51
  ]
52
52
  us = [
53
53
  "policyengine-core==3.33.0",
@@ -71,7 +71,7 @@ dev = [
71
71
  "hypothesis>=6.100.0",
72
72
  "policyengine-core==3.33.0",
73
73
  "policyengine-us==2.2.1",
74
- "policyengine-uk==2.126.12",
74
+ "policyengine-uk==2.130.1",
75
75
  "spm-calculator==1.0.0",
76
76
  ]
77
77