policyengine-core 3.32.26__tar.gz → 3.32.28__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 (463) hide show
  1. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/CHANGELOG.md +15 -0
  2. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/PKG-INFO +1 -1
  3. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/data_storage/on_disk_storage.py +31 -4
  4. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/holders/holder.py +217 -8
  5. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/simulations/simulation.py +94 -17
  6. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/pyproject.toml +1 -1
  7. policyengine_core-3.32.28/tests/core/test_disk_period_filenames.py +167 -0
  8. policyengine_core-3.32.28/tests/core/test_input_tier_invalidation.py +129 -0
  9. policyengine_core-3.32.28/tests/core/test_option_result_cache.py +268 -0
  10. policyengine_core-3.32.28/tests/core/test_option_result_cache_property.py +136 -0
  11. policyengine_core-3.32.28/tests/core/test_option_result_input_provenance.py +120 -0
  12. policyengine_core-3.32.28/tests/core/test_registered_input_deletion.py +93 -0
  13. policyengine_core-3.32.28/tests/core/test_restore_input_provenance.py +50 -0
  14. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_restore_input_registry_property.py +109 -53
  15. policyengine_core-3.32.28/tests/core/test_user_input_keys.py +615 -0
  16. policyengine_core-3.32.28/tests/core/test_user_input_keys_property.py +611 -0
  17. policyengine_core-3.32.28/tests/core/test_user_input_keys_soundness.py +414 -0
  18. policyengine_core-3.32.28/tests/fixtures/option_caches.py +98 -0
  19. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/.github/ISSUE_TEMPLATE.md +0 -0
  20. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  21. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/.github/actionlint.yaml +0 -0
  22. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/.github/bump_version.py +0 -0
  23. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/.github/changelog_template.md +0 -0
  24. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/.github/get-changelog-diff.sh +0 -0
  25. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/.github/has-functional-changes.sh +0 -0
  26. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/.github/is-version-number-acceptable.sh +0 -0
  27. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/.github/publish-git-tag.sh +0 -0
  28. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/.github/requirements/legacy-country-smoke.txt +0 -0
  29. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/.github/workflows/pr.yaml +0 -0
  30. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/.github/workflows/push.yaml +0 -0
  31. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/.gitignore +0 -0
  32. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/.python-version +0 -0
  33. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/AGENTS.md +0 -0
  34. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/CLAUDE.md +0 -0
  35. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/CONTRIBUTING.md +0 -0
  36. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/LICENSE +0 -0
  37. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/MANIFEST.in +0 -0
  38. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/Makefile +0 -0
  39. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/README.md +0 -0
  40. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/_build/logs/myst.build.json +0 -0
  41. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/changelog.d/.gitkeep +0 -0
  42. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/_config.yml +0 -0
  43. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/_static/style.css +0 -0
  44. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/_toc.yml +0 -0
  45. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/add_plotly_to_book.py +0 -0
  46. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/contributing/intro.md +0 -0
  47. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/engineering/skills/README.md +0 -0
  48. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/engineering/skills/documentation_review.md +0 -0
  49. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/engineering/skills/github-prs.md +0 -0
  50. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/engineering/skills/testing.md +0 -0
  51. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/intro.md +0 -0
  52. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/logo.png +0 -0
  53. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/commons.md +0 -0
  54. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/country_template.md +0 -0
  55. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/data.md +0 -0
  56. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/data_storage.md +0 -0
  57. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/entities.md +0 -0
  58. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/enums.md +0 -0
  59. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/errors.md +0 -0
  60. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/experimental.md +0 -0
  61. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/extension_template.md +0 -0
  62. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/holders.md +0 -0
  63. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/parameters.md +0 -0
  64. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/periods.md +0 -0
  65. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/populations.md +0 -0
  66. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/projectors.md +0 -0
  67. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/reforms.md +0 -0
  68. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/scripts.md +0 -0
  69. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/simulations.md +0 -0
  70. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/taxbenefitsystems.md +0 -0
  71. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/taxscales.md +0 -0
  72. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/tools.md +0 -0
  73. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/tracers.md +0 -0
  74. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/types.md +0 -0
  75. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/variables.md +0 -0
  76. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/python_api/warnings.md +0 -0
  77. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/usage/country.ipynb +0 -0
  78. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/usage/datasets.ipynb +0 -0
  79. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/usage/parameters.md +0 -0
  80. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/usage/reforms.ipynb +0 -0
  81. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/docs/usage/simulation.ipynb +0 -0
  82. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/mypy.ini +0 -0
  83. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/__init__.py +0 -0
  84. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/build_metadata.py +0 -0
  85. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/charts/__init__.py +0 -0
  86. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/charts/api.py +0 -0
  87. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/charts/bar.py +0 -0
  88. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/charts/formatting.py +0 -0
  89. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/commons/__init__.py +0 -0
  90. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/commons/formulas.py +0 -0
  91. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/commons/misc.py +0 -0
  92. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/commons/rates.py +0 -0
  93. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/__init__.py +0 -0
  94. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/constants.py +0 -0
  95. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/data/__init__.py +0 -0
  96. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/data/datasets/__init__.py +0 -0
  97. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/data/datasets/country_template_dataset.py +0 -0
  98. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/entities.py +0 -0
  99. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/modelled_policies.yaml +0 -0
  100. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/parameters/benefits/basic_income.yaml +0 -0
  101. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/parameters/benefits/housing_allowance.yaml +0 -0
  102. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/parameters/benefits/index.yaml +0 -0
  103. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/parameters/benefits/parenting_allowance/amount.yaml +0 -0
  104. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/parameters/benefits/parenting_allowance/income_threshold.yaml +0 -0
  105. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/parameters/general/age_of_majority.yaml +0 -0
  106. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/parameters/general/age_of_retirement.yaml +0 -0
  107. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/parameters/taxes/housing_tax.yaml +0 -0
  108. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/parameters/taxes/income_tax_rate.yaml +0 -0
  109. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/parameters/taxes/social_security_contribution.yaml +0 -0
  110. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/reforms/__init__.py +0 -0
  111. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/reforms/add_dynamic_variable.py +0 -0
  112. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/reforms/add_new_tax.py +0 -0
  113. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/reforms/flat_social_security_contribution.py +0 -0
  114. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/reforms/modify_social_security_taxation.py +0 -0
  115. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/reforms/removal_basic_income.py +0 -0
  116. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/situation_examples/__init__.py +0 -0
  117. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/situation_examples/couple.json +0 -0
  118. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/situation_examples/housing.json +0 -0
  119. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/situation_examples/single.json +0 -0
  120. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/tests/age.yaml +0 -0
  121. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/tests/basic_income.yaml +0 -0
  122. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/tests/disposable_income.yaml +0 -0
  123. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/tests/housing_allowance.yaml +0 -0
  124. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/tests/housing_tax.yaml +0 -0
  125. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/tests/income_tax.yaml +0 -0
  126. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/tests/reforms/add_dynamic_variable.yaml +0 -0
  127. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/tests/reforms/add_new_tax.yaml +0 -0
  128. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/tests/reforms/modify_social_security_taxation.yaml +0 -0
  129. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/tests/situations/income_tax.yaml +0 -0
  130. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/tests/situations/parenting_allowance.yaml +0 -0
  131. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/tests/social_security_contribution.yaml +0 -0
  132. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/tests/test_microsimulation.py +0 -0
  133. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/variables/__init__.py +0 -0
  134. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/variables/benefits.py +0 -0
  135. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/variables/demographics.py +0 -0
  136. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/variables/housing.py +0 -0
  137. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/variables/ids.py +0 -0
  138. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/variables/income.py +0 -0
  139. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/variables/stats.py +0 -0
  140. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/country_template/variables/taxes.py +0 -0
  141. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/data/__init__.py +0 -0
  142. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/data/dataset.py +0 -0
  143. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/data_storage/__init__.py +0 -0
  144. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/data_storage/in_memory_storage.py +0 -0
  145. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/data_storage/storage_directory.py +0 -0
  146. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/data_structures/__init__.py +0 -0
  147. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/data_structures/parameter_metadata.py +0 -0
  148. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/data_structures/parameter_node_metadata.py +0 -0
  149. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/data_structures/reference.py +0 -0
  150. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/data_structures/unit.py +0 -0
  151. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/entities/__init__.py +0 -0
  152. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/entities/entity.py +0 -0
  153. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/entities/group_entity.py +0 -0
  154. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/entities/helpers.py +0 -0
  155. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/entities/role.py +0 -0
  156. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/enums/__init__.py +0 -0
  157. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/enums/config.py +0 -0
  158. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/enums/enum.py +0 -0
  159. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/enums/enum_array.py +0 -0
  160. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/errors/__init__.py +0 -0
  161. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/errors/cycle_error.py +0 -0
  162. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/errors/empty_argument_error.py +0 -0
  163. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/errors/nan_creation_error.py +0 -0
  164. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/errors/non_deterministic_formula_error.py +0 -0
  165. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/errors/parameter_not_found_error.py +0 -0
  166. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/errors/parameter_parsing_error.py +0 -0
  167. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/errors/period_mismatch_error.py +0 -0
  168. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/errors/situation_parsing_error.py +0 -0
  169. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/errors/spiral_error.py +0 -0
  170. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/errors/variable_name_conflict_error.py +0 -0
  171. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/errors/variable_not_found_error.py +0 -0
  172. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/experimental/__init__.py +0 -0
  173. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/experimental/memory_config.py +0 -0
  174. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/extension_template/__init__.py +0 -0
  175. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/extension_template/local_benefit.py +0 -0
  176. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/extension_template/parameters/local_town/child_allowance/amount.yaml +0 -0
  177. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/extension_template/parameters/local_town/child_allowance/index.yaml +0 -0
  178. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/extension_template/parameters/local_town/index.yaml +0 -0
  179. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/extension_template/tests/local_benefit.yaml +0 -0
  180. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/holders/__init__.py +0 -0
  181. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/holders/helpers.py +0 -0
  182. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/model_api.py +0 -0
  183. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/parameters/__init__.py +0 -0
  184. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/parameters/at_instant_like.py +0 -0
  185. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/parameters/config.py +0 -0
  186. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/parameters/helpers.py +0 -0
  187. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/parameters/operations/__init__.py +0 -0
  188. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/parameters/operations/get_parameter.py +0 -0
  189. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/parameters/operations/homogenize_parameters.py +0 -0
  190. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/parameters/operations/interpolate_parameters.py +0 -0
  191. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/parameters/operations/propagate_parameter_metadata.py +0 -0
  192. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/parameters/operations/uprate_parameters.py +0 -0
  193. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/parameters/parameter.py +0 -0
  194. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/parameters/parameter_at_instant.py +0 -0
  195. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/parameters/parameter_node.py +0 -0
  196. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/parameters/parameter_node_at_instant.py +0 -0
  197. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/parameters/parameter_scale.py +0 -0
  198. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/parameters/parameter_scale_bracket.py +0 -0
  199. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/parameters/vectorial_parameter_node_at_instant.py +0 -0
  200. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/periods/__init__.py +0 -0
  201. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/periods/config.py +0 -0
  202. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/periods/helpers.py +0 -0
  203. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/periods/instant_.py +0 -0
  204. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/periods/period_.py +0 -0
  205. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/populations/__init__.py +0 -0
  206. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/populations/config.py +0 -0
  207. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/populations/group_population.py +0 -0
  208. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/populations/population.py +0 -0
  209. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/projectors/__init__.py +0 -0
  210. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/projectors/entity_to_person_projector.py +0 -0
  211. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/projectors/first_person_to_entity_projector.py +0 -0
  212. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/projectors/helpers.py +0 -0
  213. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/projectors/projector.py +0 -0
  214. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/projectors/unique_role_to_entity_projector.py +0 -0
  215. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/reforms/__init__.py +0 -0
  216. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/reforms/reform.py +0 -0
  217. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/scripts/__init__.py +0 -0
  218. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/scripts/assets/__init__.py +0 -0
  219. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/scripts/assets/index.html +0 -0
  220. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/scripts/policyengine_command.py +0 -0
  221. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/scripts/run_data.py +0 -0
  222. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/scripts/run_test.py +0 -0
  223. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/scripts/simulation_generator.py +0 -0
  224. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/simulations/__init__.py +0 -0
  225. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/simulations/helpers.py +0 -0
  226. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/simulations/individual_sim.py +0 -0
  227. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/simulations/microsimulation.py +0 -0
  228. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/simulations/simulation_builder.py +0 -0
  229. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/simulations/simulation_macro_cache.py +0 -0
  230. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/taxbenefitsystems/__init__.py +0 -0
  231. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/taxbenefitsystems/tax_benefit_system.py +0 -0
  232. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/taxscales/__init__.py +0 -0
  233. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/taxscales/abstract_rate_tax_scale.py +0 -0
  234. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/taxscales/abstract_tax_scale.py +0 -0
  235. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/taxscales/amount_tax_scale_like.py +0 -0
  236. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/taxscales/helpers.py +0 -0
  237. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/taxscales/linear_average_rate_tax_scale.py +0 -0
  238. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/taxscales/marginal_amount_tax_scale.py +0 -0
  239. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/taxscales/marginal_rate_tax_scale.py +0 -0
  240. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/taxscales/rate_tax_scale_like.py +0 -0
  241. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/taxscales/single_amount_tax_scale.py +0 -0
  242. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/taxscales/tax_scale_like.py +0 -0
  243. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/tools/__init__.py +0 -0
  244. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/tools/google_cloud.py +0 -0
  245. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/tools/hugging_face.py +0 -0
  246. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/tools/simulation_dumper.py +0 -0
  247. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/tools/test_from_situation.py +0 -0
  248. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/tools/test_runner.py +0 -0
  249. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/tools/win_file_manager.py +0 -0
  250. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/tracers/__init__.py +0 -0
  251. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/tracers/computation_log.py +0 -0
  252. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/tracers/flat_trace.py +0 -0
  253. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/tracers/full_tracer.py +0 -0
  254. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/tracers/performance_log.py +0 -0
  255. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/tracers/simple_tracer.py +0 -0
  256. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/tracers/trace_node.py +0 -0
  257. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/tracers/tracing_parameter_node_at_instant.py +0 -0
  258. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/tracers/variable_graph.py +0 -0
  259. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/types/__init__.py +0 -0
  260. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/types/data_types/__init__.py +0 -0
  261. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/types/data_types/arrays.py +0 -0
  262. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/variables/__init__.py +0 -0
  263. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/variables/config.py +0 -0
  264. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/variables/defined_for.py +0 -0
  265. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/variables/formula_randomness.py +0 -0
  266. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/variables/helpers.py +0 -0
  267. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/variables/typing.py +0 -0
  268. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/variables/variable.py +0 -0
  269. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/warnings/__init__.py +0 -0
  270. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/warnings/libyaml_warning.py +0 -0
  271. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/warnings/memory_config_warning.py +0 -0
  272. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/warnings/parameter_key_warning.py +0 -0
  273. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/policyengine_core/warnings/tempfile_warning.py +0 -0
  274. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/pytest.ini +0 -0
  275. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/__init__.py +0 -0
  276. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/conftest.py +0 -0
  277. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/__init__.py +0 -0
  278. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/commons/__init__.py +0 -0
  279. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/commons/test_between_function.py +0 -0
  280. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/commons/test_formulas.py +0 -0
  281. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/commons/test_is_in_function.py +0 -0
  282. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/commons/test_random_seed.py +0 -0
  283. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/commons/test_rates.py +0 -0
  284. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/data/test_dataset.py +0 -0
  285. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/enums/test_enum.py +0 -0
  286. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/model_api/test_index_.py +0 -0
  287. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/__init__.py +0 -0
  288. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/array_type.yaml +0 -0
  289. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/duplicate_key.yaml +0 -0
  290. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/filesystem_hierarchy/empty_index_file/index.yaml +0 -0
  291. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/filesystem_hierarchy/node1/param.yaml +0 -0
  292. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/indentation.yaml +0 -0
  293. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/missing_value.yaml +0 -0
  294. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/parameter_for_unit_propagation.yaml +0 -0
  295. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/test_parameter_clone.py +0 -0
  296. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/test_parameter_validation.py +0 -0
  297. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/test_propagate_units.py +0 -0
  298. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/test_unknown_keys.py +0 -0
  299. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/unexpected_key_in_parameter.yaml +0 -0
  300. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/unexpected_key_in_scale.yaml +0 -0
  301. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/unexpected_key_in_value_at_instant.yaml +0 -0
  302. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/unexpected_key_in_value_history.yaml +0 -0
  303. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/unknown_key_beside_values.yaml +0 -0
  304. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/uprating_under_metadata.yaml +0 -0
  305. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/wrong_date.yaml +0 -0
  306. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/wrong_scale.yaml +0 -0
  307. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/wrong_type_in_bracket.yaml +0 -0
  308. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/wrong_type_in_brackets.yaml +0 -0
  309. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/wrong_type_in_parameter.yaml +0 -0
  310. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/wrong_type_in_scale.yaml +0 -0
  311. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/wrong_type_in_value_history.yaml +0 -0
  312. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/wrong_value.yaml +0 -0
  313. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameter_validation/yaml_hierarchy/node1.yaml +0 -0
  314. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameters/operations/test_interpolation.py +0 -0
  315. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameters/operations/test_nesting.py +0 -0
  316. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameters/operations/test_propagation.py +0 -0
  317. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameters/operations/test_scale_threshold_uprating.py +0 -0
  318. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameters/operations/test_uprating.py +0 -0
  319. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameters/test_numpy2_structured_arrays.py +0 -0
  320. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameters/test_vectorial_parameter_node_copy.py +0 -0
  321. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameters/test_vectorial_parameter_node_copy_property.py +0 -0
  322. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameters_fancy_indexing/__init__.py +0 -0
  323. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameters_fancy_indexing/bareme.yaml +0 -0
  324. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameters_fancy_indexing/local_tax.yaml +0 -0
  325. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameters_fancy_indexing/rate.yaml +0 -0
  326. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/parameters_fancy_indexing/test_fancy_indexing.py +0 -0
  327. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/simulations/test_formula_randomness_concurrency.py +0 -0
  328. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/tax_scales/__init__.py +0 -0
  329. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/tax_scales/test_abstract_rate_tax_scale.py +0 -0
  330. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/tax_scales/test_abstract_tax_scale.py +0 -0
  331. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/tax_scales/test_linear_average_rate_tax_scale.py +0 -0
  332. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/tax_scales/test_marginal_amount_tax_scale.py +0 -0
  333. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/tax_scales/test_marginal_rate_tax_scale.py +0 -0
  334. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/tax_scales/test_rate_tax_scale_like.py +0 -0
  335. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/tax_scales/test_single_amount_tax_scale.py +0 -0
  336. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/tax_scales/test_tax_scales_commons.py +0 -0
  337. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_apply_reform_invalidates_cache.py +0 -0
  338. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_apply_reform_preserves_user_inputs.py +0 -0
  339. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_assert_near_precision.py +0 -0
  340. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_axes.py +0 -0
  341. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_bincount_minlength.py +0 -0
  342. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_branch_scoped_delete_arrays.py +0 -0
  343. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_branch_shared_arrays.py +0 -0
  344. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_branch_shared_arrays_differential.py +0 -0
  345. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_build_metadata.py +0 -0
  346. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_calculate_output.py +0 -0
  347. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_carry_over_order.py +0 -0
  348. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_carry_over_order_property.py +0 -0
  349. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_clone_invalidated_caches.py +0 -0
  350. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_clone_rebinding.py +0 -0
  351. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_clone_rebinding_property.py +0 -0
  352. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_clone_reform_baseline.py +0 -0
  353. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_clone_reform_baseline_property.py +0 -0
  354. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_countries.py +0 -0
  355. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_cycles.py +0 -0
  356. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_data_storage_dir.py +0 -0
  357. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_data_storage_dir_property.py +0 -0
  358. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_dataset_getattr.py +0 -0
  359. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_dataset_unknown_column_warning.py +0 -0
  360. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_disk_storage_differential.py +0 -0
  361. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_disk_storage_ownership.py +0 -0
  362. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_docs_deployment.py +0 -0
  363. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_dump_restore.py +0 -0
  364. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_entities.py +0 -0
  365. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_eternity_subperiods.py +0 -0
  366. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_extensions.py +0 -0
  367. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_fast_cache.py +0 -0
  368. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_fast_cache_contained_periods.py +0 -0
  369. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_fast_cache_contained_periods_property.py +0 -0
  370. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_fast_cache_guards.py +0 -0
  371. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_formulas.py +0 -0
  372. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_holder_branch_fallback.py +0 -0
  373. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_holder_write_fast_cache.py +0 -0
  374. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_holder_write_fast_cache_property.py +0 -0
  375. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_holders.py +0 -0
  376. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_macro_cache_not_singleton.py +0 -0
  377. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_medium_fixes.py +0 -0
  378. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_microsimulation_person_accessor.py +0 -0
  379. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_microsimulation_weights.py +0 -0
  380. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_on_disk_storage_clones.py +0 -0
  381. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_opt_out_cache.py +0 -0
  382. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_pandas3_compatibility.py +0 -0
  383. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_parameter_security.py +0 -0
  384. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_parameters.py +0 -0
  385. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_periods.py +0 -0
  386. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_projectors.py +0 -0
  387. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_reform_baseline_system.py +0 -0
  388. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_reform_parameter_isolation.py +0 -0
  389. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_reform_period_keys.py +0 -0
  390. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_reforms.py +0 -0
  391. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_release_tagging.py +0 -0
  392. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_rerun_fixture_isolation.py +0 -0
  393. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_restore_input_registry.py +0 -0
  394. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_simulation_builder.py +0 -0
  395. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_simulation_copy_pickle.py +0 -0
  396. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_simulation_copy_pickle_property.py +0 -0
  397. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_simulations.py +0 -0
  398. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_stable_hash_seed.py +0 -0
  399. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_storage_anchored_periods.py +0 -0
  400. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_storage_delete_branch.py +0 -0
  401. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_storage_input_index.py +0 -0
  402. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_storage_shared_index.py +0 -0
  403. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_storage_shared_index_differential.py +0 -0
  404. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_subsample_baseline_branch.py +0 -0
  405. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_subsample_invalidates_cache.py +0 -0
  406. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_tax_benefit_system_cache_identity.py +0 -0
  407. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_tracers.py +0 -0
  408. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_tracing_parameter_isolation.py +0 -0
  409. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_tracing_parameter_isolation_properties.py +0 -0
  410. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_uprating_order.py +0 -0
  411. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_uprating_order_property.py +0 -0
  412. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/test_yaml.py +0 -0
  413. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/tools/__init__.py +0 -0
  414. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/tools/test_assert_near.py +0 -0
  415. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/tools/test_google_cloud.py +0 -0
  416. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/tools/test_hugging_face.py +0 -0
  417. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/tools/test_runner/test_yaml_runner.py +0 -0
  418. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/variables/test_adds.py +0 -0
  419. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/variables/test_annualize.py +0 -0
  420. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/variables/test_bounds.py +0 -0
  421. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/variables/test_defined_for.py +0 -0
  422. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/variables/test_formula_randomness.py +0 -0
  423. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/variables/test_non_numeric_uprating_and_defined_for.py +0 -0
  424. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/variables/test_subtracts.py +0 -0
  425. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/variables/test_subtracts_sign.py +0 -0
  426. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/variables/test_variable_clone.py +0 -0
  427. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/variables/test_variable_formula_determinism.py +0 -0
  428. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/variables/test_variable_uprating.py +0 -0
  429. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/core/variables/test_variables.py +0 -0
  430. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/__init__.py +0 -0
  431. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/appclient.py +0 -0
  432. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/branch_shared_arrays.py +0 -0
  433. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/carry_over.py +0 -0
  434. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/data_storage_dir.py +0 -0
  435. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/disk_storage.py +0 -0
  436. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/entities.py +0 -0
  437. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/fast_cache_contained_periods.py +0 -0
  438. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/simulation_copy.py +0 -0
  439. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/simulations.py +0 -0
  440. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/taxbenefitsystems.py +0 -0
  441. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/tracing.py +0 -0
  442. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/uprated_inputs.py +0 -0
  443. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/uprating_order.py +0 -0
  444. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/variables.py +0 -0
  445. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/vectorial_parameter_nodes.py +0 -0
  446. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/yaml_tests/__init__.py +0 -0
  447. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/yaml_tests/directory/subdirectory/test_4.yaml +0 -0
  448. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/yaml_tests/directory/test_1.yaml +0 -0
  449. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/yaml_tests/directory/test_2.yaml +0 -0
  450. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/yaml_tests/failing_test_absolute_error_margin.yaml +0 -0
  451. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/yaml_tests/failing_test_relative_error_margin.yaml +0 -0
  452. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/yaml_tests/test_absolute_error_margin.yaml +0 -0
  453. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/yaml_tests/test_failure.yaml +0 -0
  454. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/yaml_tests/test_name_filter.yaml +0 -0
  455. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/yaml_tests/test_relative_error_margin.yaml +0 -0
  456. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/yaml_tests/test_success.yml +0 -0
  457. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/yaml_tests/test_with_anchors.yaml +0 -0
  458. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/yaml_tests/test_with_extension.yaml +0 -0
  459. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/yaml_tests/test_with_reform.yaml +0 -0
  460. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/fixtures/yaml_tests/test_with_reform_2.yaml +0 -0
  461. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/smoke/test_country_init.py +0 -0
  462. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/tests/smoke/test_us.py +0 -0
  463. {policyengine_core-3.32.26 → policyengine_core-3.32.28}/uv.lock +0 -0
@@ -1,3 +1,18 @@
1
+ ## [3.32.28] - 2026-10-09
2
+
3
+ ### Fixed
4
+
5
+ - Cache `calculate_add` and `calculate_divide` results as derived only where a plain read returns them without changing dtype, preserving visible inputs and allowing explicit aggregate refresh after an input changes or is deleted.
6
+
7
+
8
+ ## [3.32.27] - 2026-10-09
9
+
10
+ ### Fixed
11
+
12
+ - Fix compound period disk storage on Windows while preserving legacy filename restores and clone isolation.
13
+ - Keep input records aligned with memory/disk storage across deletion, copies, handlers, subsampling, period aliases, and early years; custom handlers must use `holder._set` or `holder.set_input` for inputs, since `put_in_cache` values are excluded from input export and reform replay and no longer redirected to the input's branch.
14
+
15
+
1
16
  ## [3.32.26] - 2026-10-09
2
17
 
3
18
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: policyengine-core
3
- Version: 3.32.26
3
+ Version: 3.32.28
4
4
  Summary: Core microsimulation engine enabling country-specific policy models.
5
5
  Project-URL: Homepage, https://github.com/policyengine/policyengine-core
6
6
  Author-email: PolicyEngine <hello@policyengine.org>
@@ -51,6 +51,11 @@ def _epoch() -> tuple:
51
51
  class OnDiskStorage:
52
52
  """
53
53
  Low-level class responsible for storing and retrieving calculated vectors on disk
54
+
55
+ Compound periods contain colons, which Windows forbids in filenames.
56
+ Their physical filenames use semicolons in the period suffix instead;
57
+ logical storage keys keep the original period strings. ``restore`` reads
58
+ both these portable filenames and legacy filenames containing colons.
54
59
  """
55
60
 
56
61
  # Defaults for ``__del__``, which also runs on an instance whose
@@ -315,13 +320,19 @@ class OnDiskStorage:
315
320
  own = self._own_paths.get(filename)
316
321
  if own is not None:
317
322
  return own
318
- path = os.path.join(self.storage_dir, filename) + ".npy"
323
+ # A period string can contain colons, which Windows forbids in
324
+ # filenames. Period strings never contain semicolons or underscores,
325
+ # so translating only the final suffix is reversible and leaves
326
+ # branch names untouched.
327
+ branch, separator, period_string = filename.rpartition("_")
328
+ portable_filename = f"{branch}{separator}{period_string.replace(':', ';')}"
329
+ path = os.path.join(self.storage_dir, portable_filename) + ".npy"
319
330
  if path not in self._family_files and not self._writes_only_new_files():
320
331
  return path
321
332
  directory = os.path.join(self.storage_dir, REPLACEMENTS_DIR)
322
333
  os.makedirs(directory, exist_ok=True)
323
334
  descriptor, path = tempfile.mkstemp(
324
- prefix=f"{filename}.", suffix=".npy", dir=directory
335
+ prefix=f"{portable_filename}.", suffix=".npy", dir=directory
325
336
  )
326
337
  os.close(descriptor)
327
338
  return path
@@ -365,13 +376,21 @@ class OnDiskStorage:
365
376
  """Read back the values stored in this storage's directory: for each
366
377
  key, the file named for it (not those in ``REPLACEMENTS_DIR``).
367
378
 
368
- This storage writes over those files, as over those it wrote, until
379
+ This storage writes over portable files, as over those it wrote, until
369
380
  it is cloned or copied, or this process forks: from then on it writes
370
381
  a new file instead, since the clone, copy or forked process reads
371
382
  them. A storage made separately for the directory, the one that wrote
372
383
  them say, still writes over them, as before. So may another storage
373
384
  in this one's family, when ``restore`` runs after it was cloned and
374
385
  this process has not forked since.
386
+
387
+ The period suffix of new filenames uses semicolons for the colons
388
+ in compound period strings. Legacy filenames containing colons are
389
+ still accepted. If both versions of a key exist after updating a
390
+ legacy value, the portable filename holds the current value and wins
391
+ regardless of directory listing order. Readers still indexed to the
392
+ legacy file keep its old value until they restore again. Branch names
393
+ are unchanged.
375
394
  """
376
395
  self._files = files = {}
377
396
  # Files read back from a directory carry no derived marks.
@@ -382,7 +401,15 @@ class OnDiskStorage:
382
401
  continue
383
402
  path = os.path.join(self.storage_dir, filename)
384
403
  filename_core = filename.rsplit(".", 1)[0]
385
- files[filename_core] = path
404
+ branch, separator, period_string = filename_core.rpartition("_")
405
+ key = filename_core
406
+ if separator:
407
+ key = f"{branch}{separator}{period_string.replace(';', ':')}"
408
+ # Updating a legacy value may leave its old colon filename
409
+ # beside the portable file. Prefer the latter, independent of
410
+ # directory listing order, without parsing arbitrary basenames.
411
+ if key not in files or key != filename_core:
412
+ files[key] = path
386
413
  self._restored_paths.update(files.values())
387
414
 
388
415
  def __del__(self, _rmtree=shutil.rmtree, _getpid=os.getpid) -> None:
@@ -1,7 +1,7 @@
1
1
  import os
2
2
  import tempfile
3
3
  import warnings
4
- from typing import TYPE_CHECKING, Any, List, Tuple
4
+ from typing import TYPE_CHECKING, Any, List, Optional, Tuple
5
5
 
6
6
  import numpy
7
7
  import psutil
@@ -23,6 +23,8 @@ class Holder:
23
23
  A holder keeps tracks of a variable values after they have been calculated, or set as an input.
24
24
  """
25
25
 
26
+ _user_input_storage: dict[tuple[str, str], tuple[Period, frozenset[str]]]
27
+
26
28
  def __init__(self, variable: "Variable", population: "Population"):
27
29
  self.population = population
28
30
  self.variable = variable
@@ -63,6 +65,7 @@ class Holder:
63
65
  "simulation",
64
66
  "_memory_storage",
65
67
  "_disk_storage",
68
+ "_user_input_storage",
66
69
  ):
67
70
  new_dict[key] = value
68
71
 
@@ -74,6 +77,9 @@ class Holder:
74
77
  new._disk_storage = (
75
78
  self._disk_storage.clone() if self._disk_storage is not None else None
76
79
  )
80
+ if hasattr(self, "_user_input_storage"):
81
+ # Locations are immutable; only the holder's index needs copying.
82
+ new._user_input_storage = self._user_input_storage.copy()
77
83
 
78
84
  new_dict["population"] = population
79
85
  new_dict["simulation"] = population.simulation
@@ -116,13 +122,186 @@ class Holder:
116
122
  If ``period`` is ``None``, remove all known values of the variable.
117
123
 
118
124
  If ``period`` is not ``None``, only remove all values for any period included in period (e.g. if period is "2017", values for "2017-01", "2017-07", etc. would be removed)
125
+
126
+ A deleted value set with ``set_input`` stops counting as an input, so
127
+ a value calculated later for the same period is not taken for one.
119
128
  """
129
+ simulation = getattr(self, "simulation", None)
130
+ user_input_keys = getattr(simulation, "_user_input_keys", None)
131
+ stored_before = self._stored_keys() if user_input_keys else None
120
132
 
121
133
  self._memory_storage.delete(period, branch_name)
122
134
  if self._disk_storage:
123
135
  self._disk_storage.delete(period, branch_name)
124
136
  self._evict_fast_cache(period, branch_name, contained=True)
125
137
 
138
+ if user_input_keys:
139
+ self._forget_deleted_inputs(user_input_keys, stored_before)
140
+
141
+ def _stored_keys(self) -> Tuple[set, set]:
142
+ """The keys memory and disk storage hold values under."""
143
+ return (
144
+ set(self._memory_storage._arrays),
145
+ set(self._disk_storage._files) if self._disk_storage is not None else set(),
146
+ )
147
+
148
+ def _forget_deleted_inputs(
149
+ self, user_input_keys: set, stored_before: Tuple[set, set]
150
+ ) -> None:
151
+ """Drop the simulation's record of the inputs ``delete_arrays`` deleted.
152
+
153
+ ``_user_input_keys`` records each (variable, branch, period) stored
154
+ through ``set_input``, with the period as storage keys it (see
155
+ ``_set``). ``_invalidate_all_caches`` keeps the values it names and
156
+ ``to_input_dataframe`` exports them, so an entry left behind for a
157
+ deleted value would make a formula result stored later for that
158
+ period count as an input.
159
+
160
+ The keys the deletion removed are found by comparing the keys each
161
+ storage holds before and after, so the cost depends on what this
162
+ holder stores, not on the size of the record, and no stored file is
163
+ read. An entry for a removed key stays only while a storage still
164
+ holds an input for it. A computed disk value does not become an input
165
+ when the memory input for the same key is deleted.
166
+ """
167
+ name = self.variable.name
168
+ memory_before, disk_before = stored_before
169
+ arrays = self._memory_storage._arrays
170
+ files = self._disk_storage._files if self._disk_storage is not None else {}
171
+ # Memory keys are "{branch}:{period}"; branch names cannot contain ":".
172
+ removed = [key.split(":", 1) for key in memory_before if key not in arrays]
173
+ # Disk keys are "{branch}_{period}"; branch names can contain "_" but
174
+ # period strings cannot, so the period follows the last "_".
175
+ removed += [key.rsplit("_", 1) for key in disk_before if key not in files]
176
+ forgotten = []
177
+ recorded = getattr(self, "_user_input_storage", {})
178
+ for branch, period_string in removed:
179
+ slot = (branch, period_string)
180
+ input_storage = recorded.get(slot)
181
+ if input_storage is not None:
182
+ input_period, locations = input_storage
183
+ locations = frozenset(
184
+ location
185
+ for location in locations
186
+ if (
187
+ f"{branch}:{period_string}" in arrays
188
+ if location == "memory"
189
+ else f"{branch}_{period_string}" in files
190
+ )
191
+ )
192
+ recorded[slot] = (input_period, locations)
193
+ else:
194
+ try:
195
+ # Storage strings omit early years' leading zeros;
196
+ # the period parser requires a four-digit year.
197
+ components = period_string.split(":")
198
+ date_index = 1 if len(components) > 1 else 0
199
+ date_components = components[date_index].split("-")
200
+ date_components[0] = date_components[0].zfill(4)
201
+ components[date_index] = "-".join(date_components)
202
+ input_period = periods.period(":".join(components))
203
+ except ValueError:
204
+ # Not a key ``put`` wrote (say, a file ``restore`` found).
205
+ continue
206
+ key = (name, branch, input_period)
207
+ if key in user_input_keys and not self._stores_user_input(
208
+ input_period, branch
209
+ ):
210
+ forgotten.append(key)
211
+ recorded.pop(slot, None)
212
+ user_input_keys.difference_update(forgotten)
213
+
214
+ def _seed_input_storage(self, period: Period, branch_name: str) -> None:
215
+ """Capture registered input locations before their first new write.
216
+
217
+ Restoration can register inputs without this holder's tier metadata.
218
+ Only an already-recorded slot can inherit storage's input marks: an
219
+ unrecorded cache value must never become a replayable input.
220
+ """
221
+ recorded = getattr(self, "_user_input_storage", None)
222
+ slot = (branch_name, str(period))
223
+ if recorded is not None and slot in recorded:
224
+ return
225
+ keys = getattr(self.simulation, "_user_input_keys", ())
226
+ if (self.variable.name, branch_name, period) not in keys:
227
+ return
228
+ locations = frozenset(
229
+ location
230
+ for location, storage in (
231
+ ("memory", self._memory_storage),
232
+ ("disk", self._disk_storage),
233
+ )
234
+ if storage is not None
235
+ and storage.has(period, branch_name)
236
+ and not storage.is_derived(period, branch_name)
237
+ )
238
+ if recorded is None:
239
+ recorded = self._user_input_storage = {}
240
+ recorded[slot] = (period, locations)
241
+
242
+ def _record_input_storage(
243
+ self, period: Period, branch_name: str, storage: str, is_input: bool = True
244
+ ) -> None:
245
+ """Remember which tier received the actual ``set_input`` value.
246
+
247
+ Storage's carry-over marks are broader: ``put_in_cache`` defaults to
248
+ ``derived=False`` without making its value a replay/export input.
249
+ Keep this metadata only for slots that have received an input, with
250
+ immutable locations so clones can copy the index independently.
251
+ """
252
+ recorded = getattr(self, "_user_input_storage", None)
253
+ slot = (branch_name, str(period))
254
+ if not is_input and (recorded is None or slot not in recorded):
255
+ return
256
+ if recorded is None:
257
+ recorded = self._user_input_storage = {}
258
+ _, locations = recorded.get(slot, (period, frozenset()))
259
+ locations = locations | {storage} if is_input else locations - {storage}
260
+ recorded[slot] = (period, locations)
261
+
262
+ def _stores_user_input(self, period: Period, branch_name: str) -> bool:
263
+ """Whether an actual input survives in either tier, without reading it.
264
+
265
+ Called only for a slot already in the simulation's replay record.
266
+ Register-only inputs (including restored inputs) may have no location
267
+ metadata yet; their existing storage marks identify the surviving
268
+ tier. Marks never add an unrecorded slot to the replay record.
269
+ """
270
+ recorded = getattr(self, "_user_input_storage", {}).get(
271
+ (branch_name, str(period))
272
+ )
273
+ locations = recorded[1] if recorded is not None else {"memory", "disk"}
274
+ for location, storage in (
275
+ ("memory", self._memory_storage),
276
+ ("disk", self._disk_storage),
277
+ ):
278
+ if (
279
+ location in locations
280
+ and storage is not None
281
+ and storage.has(period, branch_name)
282
+ and not storage.is_derived(period, branch_name)
283
+ ):
284
+ return True
285
+ return False
286
+
287
+ def _storage_period(self, period: Period) -> Period:
288
+ """The period storage keys a value for ``period`` under.
289
+
290
+ Match the string key's aliases using the period fields: eternity for
291
+ an eternal variable, and a year for twelve months. Do not reparse the
292
+ string: valid early-year periods stringify without leading zeros.
293
+ """
294
+ if self._memory_storage.is_eternal:
295
+ return periods.period(periods.ETERNITY)
296
+ period = periods.period(period)
297
+ if period.unit in (periods.MONTH, periods.YEAR):
298
+ unit, size = period.unit, period.size
299
+ if unit == periods.MONTH and size == 12:
300
+ unit, size = periods.YEAR, 1
301
+ start = periods.instant((period.start.year, period.start.month, 1))
302
+ return Period((unit, start, size))
303
+ return period
304
+
126
305
  def _get_array_from_storage(
127
306
  self, period: Period, branch_name: str = "default"
128
307
  ) -> ArrayLike:
@@ -257,6 +436,10 @@ class Holder:
257
436
 
258
437
 
259
438
  If a ``set_input`` property has been set for the variable, this method may accept inputs for periods not matching the ``definition_period`` of the variable. To read more about this, check the `documentation <https://openfisca.org/doc/coding-the-legislation/35_periods.html#set-input-automatically-process-variable-inputs-defined-for-periods-not-matching-the-definition-period>`_.
439
+
440
+ Custom handlers store replayable inputs with ``holder._set`` or
441
+ ``holder.set_input``. Values written through ``put_in_cache`` are
442
+ excluded from input export and reform replay.
260
443
  """
261
444
 
262
445
  period = periods.period(period)
@@ -365,15 +548,18 @@ class Holder:
365
548
  branch_name: str = "default",
366
549
  validate_nan: bool = False,
367
550
  derived: bool = False,
551
+ is_input: Optional[bool] = None,
368
552
  ) -> None:
369
553
  simulation = getattr(self, "simulation", None)
370
- # A value calculated while an input is being set (say, by a
371
- # ``set_input`` helper that calculates) is not part of that input: it
372
- # belongs to the branch it was calculated on.
373
554
  user_input_contexts = (
374
555
  None if derived else getattr(simulation, "_user_input_contexts", None)
375
556
  )
376
- if user_input_contexts and branch_name == "default":
557
+ # A value is an input when stored while ``set_input`` runs, unless the
558
+ # caller says otherwise: ``put_in_cache`` stores calculated values,
559
+ # including those a custom ``set_input`` handler calculates.
560
+ if is_input is None:
561
+ is_input = bool(user_input_contexts)
562
+ if is_input and user_input_contexts and branch_name == "default":
377
563
  branch_name = user_input_contexts[-1]
378
564
  value = self._to_array(value, validate_nan=validate_nan)
379
565
  if self.variable.definition_period != periods.ETERNITY:
@@ -389,15 +575,38 @@ class Holder:
389
575
  >= self.simulation.memory_config.max_memory_occupation_pc
390
576
  )
391
577
 
578
+ stored_period = (
579
+ self._storage_period(period)
580
+ if is_input
581
+ or hasattr(self, "_user_input_storage")
582
+ or (not derived and getattr(simulation, "_user_input_keys", None))
583
+ else None
584
+ )
585
+ if stored_period is not None:
586
+ self._seed_input_storage(stored_period, branch_name)
587
+
392
588
  if should_store_on_disk:
393
589
  self._disk_storage.put(value, period, branch_name, derived=derived)
394
590
  else:
395
591
  self._memory_storage.put(value, period, branch_name, derived=derived)
396
592
  self._evict_fast_cache(period, branch_name)
397
- if user_input_contexts:
593
+ if stored_period is not None:
594
+ self._record_input_storage(
595
+ stored_period,
596
+ branch_name,
597
+ "disk" if should_store_on_disk else "memory",
598
+ is_input=is_input,
599
+ )
600
+ if is_input and simulation is not None:
398
601
  if not hasattr(simulation, "_user_input_keys"):
399
602
  simulation._user_input_keys = set()
400
- simulation._user_input_keys.add((self.variable.name, branch_name, period))
603
+ # Record the period as storage keys the value (eternity for an
604
+ # eternal variable, whatever period it was set for; the year for
605
+ # twelve months starting on the first of a month), so each entry
606
+ # names one stored value.
607
+ simulation._user_input_keys.add(
608
+ (self.variable.name, branch_name, stored_period)
609
+ )
401
610
 
402
611
  def put_in_cache(
403
612
  self,
@@ -436,7 +645,7 @@ class Holder:
436
645
  ):
437
646
  return
438
647
 
439
- self._set(period, value, branch_name, derived=derived)
648
+ self._set(period, value, branch_name, is_input=False, derived=derived)
440
649
 
441
650
  def default_array(self) -> ArrayLike:
442
651
  """
@@ -301,7 +301,11 @@ class Simulation:
301
301
  # populates so ``_invalidate_all_caches`` can tell user-provided
302
302
  # source data apart from formula-computed caches. Without this the
303
303
  # post-``apply_reform`` cache wipe would also wipe the dataset the
304
- # simulation was loaded from.
304
+ # simulation was loaded from. The record follows this simulation's
305
+ # storage: each entry names one stored value (the period is the one
306
+ # storage keys it under), ``delete_arrays`` drops the entries for the
307
+ # values it deletes, and ``clone`` (so also ``get_branch``) gives the
308
+ # copy its own record.
305
309
  self._user_input_keys: set[tuple[str, str, Period]] = set()
306
310
  self.debug: bool = False
307
311
  self.trace: bool = trace
@@ -433,11 +437,11 @@ class Simulation:
433
437
  Called after ``apply_reform`` and any other operation that changes
434
438
  the tax-benefit system underneath an already-calculated simulation.
435
439
 
436
- Every (variable, branch, period) that was populated via
437
- ``set_input`` is preserved — those are source data, not stale
438
- formula output — so a structural reform applied after dataset
439
- load doesn't silently discard the dataset. Everything else
440
- (formula outputs, cached short-path results, on-disk caches) is
440
+ Still-present supplied inputs are preserved from the tiers that
441
+ hold them, so a structural reform applied after dataset load
442
+ doesn't silently discard the dataset. A cache write replacing an
443
+ input does not make that cached value a supplied input. Everything
444
+ else (formula outputs, cached short-path results, on-disk caches) is
441
445
  wiped so the next ``calculate`` recomputes under the new
442
446
  tax-benefit system.
443
447
  """
@@ -451,7 +455,16 @@ class Simulation:
451
455
  user_input_keys = getattr(self, "_user_input_keys", None) or set()
452
456
  for variable_name, branch_name, period in user_input_keys:
453
457
  holder = self.get_holder(variable_name)
454
- stored_value = holder._memory_storage.get(period, branch_name)
458
+ holder._seed_input_storage(period, branch_name)
459
+ locations = holder._user_input_storage[(branch_name, str(period))][1]
460
+ # Carry-over marks also include external cache writes. Replay
461
+ # only tiers that still hold the recorded, supplied input.
462
+ stored_value = (
463
+ holder._memory_storage.get(period, branch_name)
464
+ if "memory" in locations
465
+ and not holder._memory_storage.is_derived(period, branch_name)
466
+ else None
467
+ )
455
468
  if stored_value is not None:
456
469
  preserved.append(
457
470
  PreservedUserInput(
@@ -463,7 +476,11 @@ class Simulation:
463
476
  )
464
477
  )
465
478
  continue
466
- if holder._disk_storage is not None:
479
+ if (
480
+ "disk" in locations
481
+ and holder._disk_storage is not None
482
+ and not holder._disk_storage.is_derived(period, branch_name)
483
+ ):
467
484
  disk_period = (
468
485
  periods.period(periods.ETERNITY)
469
486
  if holder._disk_storage.is_eternal
@@ -484,6 +501,10 @@ class Simulation:
484
501
  disk_enum=holder._disk_storage._enums.get(disk_file),
485
502
  )
486
503
  )
504
+ self._user_input_keys = {
505
+ (user_input.variable_name, user_input.branch_name, user_input.period)
506
+ for user_input in preserved
507
+ }
487
508
  # Iterate only over holders that already exist on each population —
488
509
  # lazy-creating a holder for every variable in the tax-benefit
489
510
  # system (thousands in policyengine-us) inflated the cost of
@@ -497,6 +518,8 @@ class Simulation:
497
518
  if holder._disk_storage is not None:
498
519
  holder._disk_storage._files = {}
499
520
  holder._disk_storage._derived = set()
521
+ if hasattr(holder, "_user_input_storage"):
522
+ holder._user_input_storage = {}
500
523
  # Replay preserved user inputs so ``calculate`` still sees them.
501
524
  for user_input in preserved:
502
525
  holder = self.get_holder(user_input.variable_name)
@@ -512,6 +535,9 @@ class Simulation:
512
535
  user_input.period,
513
536
  user_input.branch_name,
514
537
  )
538
+ holder._record_input_storage(
539
+ user_input.period, user_input.branch_name, user_input.storage
540
+ )
515
541
  for branch in self.branches.values():
516
542
  branch._invalidate_all_caches()
517
543
 
@@ -1316,12 +1342,7 @@ class Simulation:
1316
1342
  result = sum(
1317
1343
  self.calculate(variable_name, sub_period) for sub_period in sub_periods
1318
1344
  )
1319
- # Cache only a sum over several sub-periods, as derived from them. A
1320
- # single sub-period's value is already stored, as an input or
1321
- # derived, by ``calculate``.
1322
- if len(sub_periods) > 1:
1323
- holder = self.get_holder(variable.name)
1324
- holder.put_in_cache(result, period, self.branch_name, derived=True)
1345
+ self._cache_option_result(variable, period, result)
1325
1346
  return result
1326
1347
 
1327
1348
  def calculate_divide(
@@ -1353,8 +1374,7 @@ class Simulation:
1353
1374
  if period.unit == periods.MONTH:
1354
1375
  computation_period = period.this_year
1355
1376
  result = self.calculate(variable_name, period=computation_period) / 12.0
1356
- holder = self.get_holder(variable.name)
1357
- holder.put_in_cache(result, period, self.branch_name, derived=True)
1377
+ self._cache_option_result(variable, period, result)
1358
1378
  return result
1359
1379
  elif period.unit == periods.YEAR:
1360
1380
  return self.calculate(variable_name, period)
@@ -1365,6 +1385,55 @@ class Simulation:
1365
1385
  )
1366
1386
  )
1367
1387
 
1388
+ def _cache_option_result(
1389
+ self, variable: Variable, period: Period, result: ArrayLike
1390
+ ) -> None:
1391
+ """Cache an ADD or DIVIDE result at ``period`` if a plain read would return it.
1392
+
1393
+ A value cached at ``period`` is what every later ``calculate`` of the
1394
+ variable at ``period`` returns. ``_calculate`` computes a FLOW
1395
+ variable over a period of another unit with these same options (a
1396
+ monthly variable over a year with ``calculate_add``, a yearly one over
1397
+ a month with ``calculate_divide``), so their result is the plain value
1398
+ there and is cached. Anywhere else it is not:
1399
+
1400
+ - A STOCK variable's plain value over a year is its last month's, and
1401
+ over a month the year's, not the sum or the twelfth.
1402
+ - Over several periods of the variable's own unit, a plain read
1403
+ raises instead.
1404
+ - A day variable's plain read over a month or a year does not sum.
1405
+ - Over a single period of its own unit, the sum is the value
1406
+ ``calculate`` has already stored.
1407
+
1408
+ Caching there would make a later plain read depend on whether the
1409
+ option ran first. Nor is an option result cached when storing it would
1410
+ change it (the twelfth of an integer or a count of true months is
1411
+ stored as the variable's own type, so a later read would return the
1412
+ truncated value where the first returned the exact one).
1413
+
1414
+ The result is cached as derived, so auto-carry-over and uprating
1415
+ never take it for an input, and ``put_in_cache`` keeps an input a
1416
+ plain read finds at ``period`` (in this branch, an ancestor or
1417
+ ``default``) instead of storing it. It does replace a value
1418
+ calculated there before, which may predate a change to the inputs.
1419
+ Whether the value read is an input comes from the storage's mark for
1420
+ the first visible value, which every write sets. The simulation's
1421
+ supplied-input record follows inputs across storage tiers for replay
1422
+ and export; it can name a disk input hidden by a derived memory value,
1423
+ and raw holder inputs need not be registered there. Accepted cache
1424
+ writes never register the aggregate as a supplied input.
1425
+ """
1426
+ if variable.quantity_type == QuantityType.STOCK:
1427
+ return
1428
+ routed = (variable.definition_period == MONTH and period.unit == YEAR) or (
1429
+ variable.definition_period == YEAR and period.unit == MONTH
1430
+ )
1431
+ if not routed or np.asarray(result).dtype != variable.dtype:
1432
+ return
1433
+ self.get_holder(variable.name).put_in_cache(
1434
+ result, period, self.branch_name, derived=True
1435
+ )
1436
+
1368
1437
  def calculate_output(self, variable_name: str, period: Period = None) -> ArrayLike:
1369
1438
  """
1370
1439
  Calculate the value of a variable using the ``calculate_output`` attribute of the variable.
@@ -1628,6 +1697,9 @@ class Simulation:
1628
1697
  The calling branch, each ancestor branch, and the default branch are
1629
1698
  purged from this simulation's private holder storage. Other branch
1630
1699
  names and the parent simulation's holder storage remain unchanged.
1700
+ Deleted inputs stop counting as inputs: a value calculated later for
1701
+ the same period is a formula result, which ``apply_reform`` discards
1702
+ and ``to_input_dataframe`` does not export.
1631
1703
 
1632
1704
  :param variable: the variable whose cached values should be deleted
1633
1705
  :param period: the period to delete, or all periods when omitted
@@ -1884,6 +1956,8 @@ class Simulation:
1884
1956
  new.tax_benefit_system = self.tax_benefit_system
1885
1957
  new.debug = debug
1886
1958
  new.trace = trace
1959
+ # A ``set_input`` running on this simulation is not running on the copy.
1960
+ new._user_input_contexts = []
1887
1961
 
1888
1962
  # A branch shares its parent's baseline: formulas that run in a
1889
1963
  # branch read the parent's baseline values through it. That holds for
@@ -2371,8 +2445,11 @@ class Simulation:
2371
2445
 
2372
2446
  df = subset_df
2373
2447
 
2374
- # Update the dataset and rebuild the simulation
2448
+ # Update the dataset and rebuild the simulation. Rebuilding replaces
2449
+ # every stored value, so the record of inputs starts again with the
2450
+ # ones the rebuild sets.
2375
2451
  self.dataset = Dataset.from_dataframe(df, self.dataset.time_period)
2452
+ self._user_input_keys = set()
2376
2453
  self.build_from_dataset()
2377
2454
 
2378
2455
  # Purge ``_fast_cache`` entries populated by ``to_input_dataframe``
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "policyengine-core"
3
- version = "3.32.26"
3
+ version = "3.32.28"
4
4
  description = "Core microsimulation engine enabling country-specific policy models."
5
5
  readme = "README.md"
6
6
  authors = [