OpenFisca-Core 44.2.2__tar.gz → 44.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/CHANGELOG.md +44 -0
  2. {openfisca_core-44.2.2 → openfisca_core-44.5.0/OpenFisca_Core.egg-info}/PKG-INFO +1 -1
  3. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/OpenFisca_Core.egg-info/SOURCES.txt +15 -0
  4. {openfisca_core-44.2.2/OpenFisca_Core.egg-info → openfisca_core-44.5.0}/PKG-INFO +1 -1
  5. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/entities/_core_entity.py +40 -0
  6. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/holders/holder.py +65 -0
  7. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/indexed_enums/enum_array.py +2 -4
  8. openfisca_core-44.5.0/openfisca_core/links/__init__.py +36 -0
  9. openfisca_core-44.5.0/openfisca_core/links/implicit.py +111 -0
  10. openfisca_core-44.5.0/openfisca_core/links/link.py +109 -0
  11. openfisca_core-44.5.0/openfisca_core/links/many2one.py +310 -0
  12. openfisca_core-44.5.0/openfisca_core/links/one2many.py +306 -0
  13. openfisca_core-44.5.0/openfisca_core/links/tests/__init__.py +58 -0
  14. openfisca_core-44.5.0/openfisca_core/links/tests/test_benchmark.py +93 -0
  15. openfisca_core-44.5.0/openfisca_core/links/tests/test_edge_cases.py +467 -0
  16. openfisca_core-44.5.0/openfisca_core/links/tests/test_implicit.py +76 -0
  17. openfisca_core-44.5.0/openfisca_core/links/tests/test_integration.py +192 -0
  18. openfisca_core-44.5.0/openfisca_core/links/tests/test_many2one.py +149 -0
  19. openfisca_core-44.5.0/openfisca_core/links/tests/test_one2many.py +112 -0
  20. openfisca_core-44.5.0/openfisca_core/links/tests/test_one2many_logic.py +398 -0
  21. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/populations/_core_population.py +32 -0
  22. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/populations/group_population.py +81 -21
  23. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/populations/population.py +7 -5
  24. openfisca_core-44.5.0/openfisca_core/populations/tests/test_members_position.py +116 -0
  25. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/simulations/_build_default_simulation.py +11 -0
  26. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/simulations/simulation.py +48 -0
  27. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/simulations/simulation_builder.py +2 -0
  28. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/taxscales/abstract_rate_tax_scale.py +1 -1
  29. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/taxscales/abstract_tax_scale.py +2 -2
  30. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/taxscales/tax_scale_like.py +2 -2
  31. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/variables/variable.py +28 -0
  32. openfisca_core-44.5.0/openfisca_web_api/scripts/__init__.py +0 -0
  33. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/pyproject.toml +1 -1
  34. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/setup.py +1 -1
  35. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/LICENSE +0 -0
  36. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/MANIFEST.in +0 -0
  37. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/OpenFisca_Core.egg-info/dependency_links.txt +0 -0
  38. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/OpenFisca_Core.egg-info/entry_points.txt +0 -0
  39. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/OpenFisca_Core.egg-info/requires.txt +0 -0
  40. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/OpenFisca_Core.egg-info/top_level.txt +0 -0
  41. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/README.md +0 -0
  42. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/__init__.py +0 -0
  43. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/commons/__init__.py +0 -0
  44. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/commons/dummy.py +0 -0
  45. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/commons/formulas.py +0 -0
  46. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/commons/misc.py +0 -0
  47. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/commons/py.typed +0 -0
  48. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/commons/rates.py +0 -0
  49. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/commons/tests/__init__.py +0 -0
  50. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/commons/tests/test_dummy.py +0 -0
  51. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/commons/tests/test_formulas.py +0 -0
  52. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/commons/tests/test_rates.py +0 -0
  53. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/data_storage/__init__.py +0 -0
  54. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/data_storage/in_memory_storage.py +0 -0
  55. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/data_storage/on_disk_storage.py +0 -0
  56. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/data_storage/types.py +0 -0
  57. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/entities/__init__.py +0 -0
  58. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/entities/_description.py +0 -0
  59. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/entities/entity.py +0 -0
  60. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/entities/group_entity.py +0 -0
  61. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/entities/helpers.py +0 -0
  62. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/entities/py.typed +0 -0
  63. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/entities/role.py +0 -0
  64. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/entities/tests/__init__.py +0 -0
  65. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/entities/tests/test_entity.py +0 -0
  66. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/entities/tests/test_group_entity.py +0 -0
  67. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/entities/tests/test_role.py +0 -0
  68. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/entities/types.py +0 -0
  69. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/errors/__init__.py +0 -0
  70. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/errors/cycle_error.py +0 -0
  71. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/errors/empty_argument_error.py +0 -0
  72. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/errors/nan_creation_error.py +0 -0
  73. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/errors/parameter_not_found_error.py +0 -0
  74. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/errors/parameter_parsing_error.py +0 -0
  75. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/errors/period_mismatch_error.py +0 -0
  76. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/errors/situation_parsing_error.py +0 -0
  77. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/errors/spiral_error.py +0 -0
  78. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/errors/variable_name_config_error.py +0 -0
  79. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/errors/variable_not_found_error.py +0 -0
  80. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/experimental/__init__.py +0 -0
  81. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/experimental/_errors.py +0 -0
  82. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/experimental/_memory_config.py +0 -0
  83. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/formula_helpers.py +0 -0
  84. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/holders/__init__.py +0 -0
  85. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/holders/helpers.py +0 -0
  86. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/holders/tests/__init__.py +0 -0
  87. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/holders/tests/test_helpers.py +0 -0
  88. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/holders/types.py +0 -0
  89. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/indexed_enums/__init__.py +0 -0
  90. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/indexed_enums/_enum_type.py +0 -0
  91. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/indexed_enums/_errors.py +0 -0
  92. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/indexed_enums/_guards.py +0 -0
  93. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/indexed_enums/_utils.py +0 -0
  94. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/indexed_enums/config.py +0 -0
  95. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/indexed_enums/enum.py +0 -0
  96. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/indexed_enums/py.typed +0 -0
  97. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/indexed_enums/tests/__init__.py +0 -0
  98. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/indexed_enums/tests/test_enum.py +0 -0
  99. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/indexed_enums/tests/test_enum_array.py +0 -0
  100. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/indexed_enums/types.py +0 -0
  101. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/memory_config.py +0 -0
  102. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/model_api.py +0 -0
  103. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/parameters/__init__.py +0 -0
  104. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/parameters/at_instant_like.py +0 -0
  105. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/parameters/config.py +0 -0
  106. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/parameters/helpers.py +0 -0
  107. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/parameters/parameter.py +0 -0
  108. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/parameters/parameter_at_instant.py +0 -0
  109. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/parameters/parameter_node.py +0 -0
  110. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/parameters/parameter_node_at_instant.py +0 -0
  111. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/parameters/parameter_scale.py +0 -0
  112. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/parameters/parameter_scale_bracket.py +0 -0
  113. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/parameters/values_history.py +0 -0
  114. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/parameters/vectorial_parameter_node_at_instant.py +0 -0
  115. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/periods/__init__.py +0 -0
  116. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/periods/_errors.py +0 -0
  117. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/periods/_parsers.py +0 -0
  118. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/periods/config.py +0 -0
  119. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/periods/date_unit.py +0 -0
  120. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/periods/helpers.py +0 -0
  121. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/periods/instant_.py +0 -0
  122. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/periods/period_.py +0 -0
  123. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/periods/py.typed +0 -0
  124. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/periods/tests/__init__.py +0 -0
  125. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/periods/tests/helpers/__init__.py +0 -0
  126. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/periods/tests/helpers/test_helpers.py +0 -0
  127. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/periods/tests/helpers/test_instant.py +0 -0
  128. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/periods/tests/helpers/test_period.py +0 -0
  129. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/periods/tests/test_instant.py +0 -0
  130. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/periods/tests/test_parsers.py +0 -0
  131. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/periods/tests/test_period.py +0 -0
  132. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/populations/__init__.py +0 -0
  133. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/populations/_errors.py +0 -0
  134. {openfisca_core-44.2.2/openfisca_core/scripts/assets → openfisca_core-44.5.0/openfisca_core/populations/tests}/__init__.py +0 -0
  135. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/populations/types.py +0 -0
  136. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/projectors/__init__.py +0 -0
  137. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/projectors/entity_to_person_projector.py +0 -0
  138. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/projectors/first_person_to_entity_projector.py +0 -0
  139. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/projectors/helpers.py +0 -0
  140. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/projectors/projector.py +0 -0
  141. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/projectors/typing.py +0 -0
  142. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/projectors/unique_role_to_entity_projector.py +0 -0
  143. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/rates.py +0 -0
  144. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/reforms/__init__.py +0 -0
  145. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/reforms/in_yaml_test_reform.py +0 -0
  146. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/reforms/reform.py +0 -0
  147. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/reforms/reform_excel.py +0 -0
  148. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/scripts/__init__.py +0 -0
  149. {openfisca_core-44.2.2/openfisca_core/scripts/migrations → openfisca_core-44.5.0/openfisca_core/scripts/assets}/__init__.py +0 -0
  150. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/scripts/assets/index.html +0 -0
  151. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/scripts/find_placeholders.py +0 -0
  152. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/scripts/measure_numpy_condition_notations.py +0 -0
  153. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/scripts/measure_performances.py +0 -0
  154. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/scripts/measure_performances_fancy_indexing.py +0 -0
  155. {openfisca_core-44.2.2/openfisca_core/scripts/migrations/v16_2_to_v17 → openfisca_core-44.5.0/openfisca_core/scripts/migrations}/__init__.py +0 -0
  156. {openfisca_core-44.2.2/openfisca_core/variables/tests → openfisca_core-44.5.0/openfisca_core/scripts/migrations/v16_2_to_v17}/__init__.py +0 -0
  157. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/scripts/migrations/v16_2_to_v17/legislation.xsd +0 -0
  158. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/scripts/migrations/v16_2_to_v17/xml_to_yaml_country_template.py +0 -0
  159. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/scripts/migrations/v16_2_to_v17/xml_to_yaml_extension_template.py +0 -0
  160. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/scripts/migrations/v24_to_25.py +0 -0
  161. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/scripts/openfisca_command.py +0 -0
  162. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/scripts/remove_fuzzy.py +0 -0
  163. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/scripts/run_test.py +0 -0
  164. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/scripts/simulation_generator.py +0 -0
  165. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/simulation_builder.py +0 -0
  166. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/simulations/__init__.py +0 -0
  167. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/simulations/_build_from_variables.py +0 -0
  168. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/simulations/_type_guards.py +0 -0
  169. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/simulations/helpers.py +0 -0
  170. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/simulations/typing.py +0 -0
  171. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/taxbenefitsystems/__init__.py +0 -0
  172. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/taxbenefitsystems/tax_benefit_system.py +0 -0
  173. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/taxscales/__init__.py +0 -0
  174. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/taxscales/amount_tax_scale_like.py +0 -0
  175. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/taxscales/helpers.py +0 -0
  176. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/taxscales/linear_average_rate_tax_scale.py +0 -0
  177. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/taxscales/marginal_amount_tax_scale.py +0 -0
  178. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/taxscales/marginal_rate_tax_scale.py +0 -0
  179. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/taxscales/rate_tax_scale_like.py +0 -0
  180. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/taxscales/single_amount_tax_scale.py +0 -0
  181. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/tools/__init__.py +0 -0
  182. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/tools/parallel_plugin.py +0 -0
  183. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/tools/simulation_dumper.py +0 -0
  184. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/tools/test_runner.py +0 -0
  185. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/tracers/__init__.py +0 -0
  186. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/tracers/computation_log.py +0 -0
  187. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/tracers/flat_trace.py +0 -0
  188. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/tracers/full_tracer.py +0 -0
  189. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/tracers/performance_log.py +0 -0
  190. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/tracers/simple_tracer.py +0 -0
  191. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/tracers/trace_node.py +0 -0
  192. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/tracers/tracing_parameter_node_at_instant.py +0 -0
  193. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/types.py +0 -0
  194. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/variables/__init__.py +0 -0
  195. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/variables/config.py +0 -0
  196. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/variables/helpers.py +0 -0
  197. {openfisca_core-44.2.2/openfisca_web_api → openfisca_core-44.5.0/openfisca_core/variables/tests}/__init__.py +0 -0
  198. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/variables/tests/test_definition_period.py +0 -0
  199. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/warnings/__init__.py +0 -0
  200. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/warnings/libyaml_warning.py +0 -0
  201. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_core/warnings/tempfile_warning.py +0 -0
  202. {openfisca_core-44.2.2/openfisca_web_api/scripts → openfisca_core-44.5.0/openfisca_web_api}/__init__.py +0 -0
  203. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_web_api/app.py +0 -0
  204. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_web_api/errors.py +0 -0
  205. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_web_api/handlers.py +0 -0
  206. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_web_api/loader/__init__.py +0 -0
  207. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_web_api/loader/entities.py +0 -0
  208. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_web_api/loader/parameters.py +0 -0
  209. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_web_api/loader/spec.py +0 -0
  210. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_web_api/loader/tax_benefit_system.py +0 -0
  211. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_web_api/loader/variables.py +0 -0
  212. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_web_api/openAPI.yml +0 -0
  213. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/openfisca_web_api/scripts/serve.py +0 -0
  214. {openfisca_core-44.2.2 → openfisca_core-44.5.0}/setup.cfg +0 -0
@@ -1,5 +1,49 @@
1
1
  # Changelog
2
2
 
3
+ ## 44.5.0
4
+
5
+ #### New features
6
+
7
+ - Add `as_of` attribute to `Variable` for persistent vector variables.
8
+ - A variable declared with `as_of = True` (or `"start"` / `"end"`) stores its value at a given instant and automatically returns that value for any later period, until a new value is explicitly set — the vectorial analogue of OpenFisca parameters.
9
+ - `as_of = "start"` (default when `True`): lookup uses the start of the requested period as reference instant.
10
+ - `as_of = "end"`: lookup uses the end of the requested period (useful for annual variables like income tax).
11
+ - Lookup is O(log P) via `bisect` on a sorted instants index.
12
+ - Reference sharing: when consecutive stored values are identical, the same array object is reused, reducing memory usage for stable variables (e.g. `marital_status`, `housing_occupancy_status`).
13
+ - Stored arrays are read-only (`writeable=False`) to prevent accidental in-place mutation.
14
+ - Combining `as_of` with `set_input` dispatch helpers raises a `ValueError` at variable definition time.
15
+
16
+ ## 44.4.0 [#1364](https://github.com/openfisca/openfisca-core/pull/1364)
17
+
18
+ #### New features
19
+
20
+ - **Entity links**: role-based and positional accessors, and dynamic population period-index helpers.
21
+ - `Many2OneLink.get_by_role(variable_name, period, role_value=...)`, `One2ManyLink.get_by_role(...)` and `ImplicitOne2ManyLink.get_by_role(...)`.
22
+ - `Many2OneLink.rank(variable_name, period)` (and on chained getter, e.g. `person.links["mother"].household.rank("age", period)`).
23
+ - `One2ManyLink.nth(n, variable_name, period, role=..., condition=...)` for the n-th target member per source.
24
+ - `has_role(role_value)` now supports `Role` objects (comparison by `.key`) in addition to raw values.
25
+ - `CorePopulation.snapshot_period(period)` and `get_period_id_to_rownum(period)` for optional dynamic-population period indexing.
26
+
27
+ #### Technical changes
28
+
29
+ - Removed unused `openfisca_core.model_api` import in `tests/core/parameters_date_indexing/test_date_indexing.py`.
30
+ - SimulationBuilder sets `_id_to_rownum` identity mapping for static simulations (`build_default_simulation`, `build_from_dict` / `build_from_entities`), for dynamic-population support.
31
+ - Add `PYTHON` variable to `tasks/lint.mk` so `make lint PYTHON=.venv/bin/python` works; fix style in `test_link_accessors.py` and remove unused variable in `test_many2one.py`.
32
+
33
+ ## 44.3.0 [#1365](https://github.com/openfisca/openfisca-core/pull/1365)
34
+
35
+ #### New features
36
+
37
+ - **Generic Entity Links (Phase 1-6)**: Introduced a new Liam2-inspired generic entity linking system avoiding rigid hierarchies like `Person -> Household`.
38
+ - Added new `Many2OneLink` and `One2ManyLink` models to create powerful inter-entity networks (e.g., `Person -> Employer`).
39
+ - Added implicit links directly binding members arrays. This powers the new `population.links` property natively inside `TaxBenefitSystem.instantiate_entities()`.
40
+ - Full capability to chain relationships via python: `person.mother.household.get("rent", period)`.
41
+ - Powerful vectorized declarative aggregations out-of-the-box (e.g., `households.persons.sum("salary", period, condition=is_female)`).
42
+
43
+ #### Technical changes
44
+
45
+ - Backward compatibility is 100% maintained. Existing syntax via Projectors natively redirects to implicit links via modified `__getattr__`.
46
+
3
47
  ## 44.2.2
4
48
 
5
49
  #### Bug fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OpenFisca-Core
3
- Version: 44.2.2
3
+ Version: 44.5.0
4
4
  Summary: A versatile microsimulation free software
5
5
  Home-page: https://github.com/openfisca/openfisca-core
6
6
  Author: OpenFisca Team
@@ -78,6 +78,19 @@ openfisca_core/indexed_enums/types.py
78
78
  openfisca_core/indexed_enums/tests/__init__.py
79
79
  openfisca_core/indexed_enums/tests/test_enum.py
80
80
  openfisca_core/indexed_enums/tests/test_enum_array.py
81
+ openfisca_core/links/__init__.py
82
+ openfisca_core/links/implicit.py
83
+ openfisca_core/links/link.py
84
+ openfisca_core/links/many2one.py
85
+ openfisca_core/links/one2many.py
86
+ openfisca_core/links/tests/__init__.py
87
+ openfisca_core/links/tests/test_benchmark.py
88
+ openfisca_core/links/tests/test_edge_cases.py
89
+ openfisca_core/links/tests/test_implicit.py
90
+ openfisca_core/links/tests/test_integration.py
91
+ openfisca_core/links/tests/test_many2one.py
92
+ openfisca_core/links/tests/test_one2many.py
93
+ openfisca_core/links/tests/test_one2many_logic.py
81
94
  openfisca_core/parameters/__init__.py
82
95
  openfisca_core/parameters/at_instant_like.py
83
96
  openfisca_core/parameters/config.py
@@ -113,6 +126,8 @@ openfisca_core/populations/_errors.py
113
126
  openfisca_core/populations/group_population.py
114
127
  openfisca_core/populations/population.py
115
128
  openfisca_core/populations/types.py
129
+ openfisca_core/populations/tests/__init__.py
130
+ openfisca_core/populations/tests/test_members_position.py
116
131
  openfisca_core/projectors/__init__.py
117
132
  openfisca_core/projectors/entity_to_person_projector.py
118
133
  openfisca_core/projectors/first_person_to_entity_projector.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OpenFisca-Core
3
- Version: 44.2.2
3
+ Version: 44.5.0
4
4
  Summary: A versatile microsimulation free software
5
5
  Home-page: https://github.com/openfisca/openfisca-core
6
6
  Author: OpenFisca Team
@@ -47,6 +47,9 @@ class CoreEntity:
47
47
  #: A ``TaxBenefitSystem`` instance.
48
48
  _tax_benefit_system: None | t.TaxBenefitSystem = None
49
49
 
50
+ #: Named links to other entities (Many2One, One2Many, etc.).
51
+ _links: dict
52
+
50
53
  @abc.abstractmethod
51
54
  def __init__(self, *__args: object, **__kwargs: object) -> None: ...
52
55
 
@@ -57,6 +60,43 @@ class CoreEntity:
57
60
  """A ``CoreEntity`` belongs to a ``TaxBenefitSystem``."""
58
61
  self._tax_benefit_system = tax_benefit_system
59
62
 
63
+ # -- Link management --------------------------------------------------
64
+
65
+ def add_link(self, link) -> None:
66
+ """Register a named link on this entity.
67
+
68
+ Args:
69
+ link: A ``Link`` instance (Many2OneLink, One2ManyLink, etc.).
70
+
71
+ Example::
72
+
73
+ from openfisca_core.links import Many2OneLink
74
+
75
+ mother = Many2OneLink(
76
+ name="mother",
77
+ link_field="mother_id",
78
+ target_entity_key="person",
79
+ )
80
+ person_entity.add_link(mother)
81
+
82
+ """
83
+ if not hasattr(self, "_links") or self._links is None:
84
+ self._links = {}
85
+ self._links[link.name] = link
86
+
87
+ def get_link(self, name: str):
88
+ """Retrieve a link by name, or ``None`` if not found."""
89
+ if not hasattr(self, "_links") or self._links is None:
90
+ return None
91
+ return self._links.get(name)
92
+
93
+ @property
94
+ def links(self) -> dict:
95
+ """All links registered on this entity."""
96
+ if not hasattr(self, "_links") or self._links is None:
97
+ self._links = {}
98
+ return self._links
99
+
60
100
  def get_variable(
61
101
  self,
62
102
  variable_name: t.VariableName,
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  from collections.abc import Sequence
4
4
  from typing import Any
5
5
 
6
+ import bisect
6
7
  import os
7
8
  import warnings
8
9
 
@@ -29,7 +30,10 @@ class Holder:
29
30
  self.variable = variable
30
31
  self.simulation = population.simulation
31
32
  self._eternal = self.variable.definition_period == periods.DateUnit.ETERNITY
33
+ self._as_of = getattr(self.variable, "as_of", False)
32
34
  self._memory_storage = storage.InMemoryStorage(is_eternal=self._eternal)
35
+ if self._as_of:
36
+ self._sorted_instants: list = []
33
37
 
34
38
  # By default, do not activate on-disk storage, or variable dropping
35
39
  self._disk_storage = None
@@ -54,6 +58,11 @@ class Holder:
54
58
  if key not in ("population", "formula", "simulation"):
55
59
  new_dict[key] = value
56
60
 
61
+ # _sorted_instants must be an independent copy so that writes to the
62
+ # clone don't corrupt the original's index.
63
+ if self._as_of:
64
+ new_dict["_sorted_instants"] = list(self._sorted_instants)
65
+
57
66
  new_dict["population"] = population
58
67
  new_dict["simulation"] = population.simulation
59
68
 
@@ -90,10 +99,52 @@ class Holder:
90
99
  value = self._memory_storage.get(period)
91
100
  if value is not None:
92
101
  return value
102
+ if self._as_of:
103
+ value = self._get_as_of(period)
104
+ if value is not None:
105
+ return value
93
106
  if self._disk_storage:
94
107
  return self._disk_storage.get(period)
95
108
  return None
96
109
 
110
+ def _get_as_of(self, period):
111
+ """Return the most recent stored value at or before the reference instant.
112
+
113
+ Uses a sorted list of instants + bisect for O(log P) lookup.
114
+ Falls back to a linear scan if _sorted_instants is not yet initialised
115
+ (e.g. after clone()).
116
+ """
117
+ target = period.start if self._as_of == "start" else period.stop
118
+ sorted_instants = getattr(self, "_sorted_instants", None)
119
+
120
+ if sorted_instants is not None:
121
+ pos = bisect.bisect_right(sorted_instants, target)
122
+ if pos == 0:
123
+ return None
124
+ best_instant = sorted_instants[pos - 1]
125
+ else:
126
+ # Fallback linear scan (e.g. after clone without _sorted_instants)
127
+ best_instant = None
128
+ for known_period in self._memory_storage.get_known_periods():
129
+ start = known_period.start
130
+ if start <= target:
131
+ if best_instant is None or start > best_instant:
132
+ best_instant = start
133
+ if best_instant is None:
134
+ return None
135
+
136
+ for known_period in self._memory_storage.get_known_periods():
137
+ if known_period.start == best_instant:
138
+ return self._memory_storage.get(known_period)
139
+ return None
140
+
141
+ def _register_instant(self, period) -> None:
142
+ """Insert period.start into the sorted instants list (no duplicates)."""
143
+ instant = period.start
144
+ pos = bisect.bisect_left(self._sorted_instants, instant)
145
+ if pos == len(self._sorted_instants) or self._sorted_instants[pos] != instant:
146
+ self._sorted_instants.insert(pos, instant)
147
+
97
148
  def get_memory_usage(self) -> t.MemoryUsage:
98
149
  """Get data about the virtual memory usage of the Holder.
99
150
 
@@ -302,11 +353,25 @@ class Holder:
302
353
  >= self.simulation.memory_config.max_memory_occupation_pc
303
354
  )
304
355
 
356
+ if self._as_of:
357
+ # Reference sharing: reuse existing array object when value unchanged,
358
+ # otherwise store a read-only defensive copy so that callers cannot
359
+ # corrupt stored data by mutating their original array in-place.
360
+ prev = self._get_as_of(period)
361
+ if prev is not None and numpy.array_equal(value, prev):
362
+ value = prev # prev is already read-only
363
+ else:
364
+ value = value.copy()
365
+ value.flags.writeable = False
366
+
305
367
  if should_store_on_disk:
306
368
  self._disk_storage.put(value, period)
307
369
  else:
308
370
  self._memory_storage.put(value, period)
309
371
 
372
+ if self._as_of:
373
+ self._register_instant(period)
374
+
310
375
  def put_in_cache(self, value, period) -> None:
311
376
  if self._do_not_store:
312
377
  return
@@ -266,8 +266,7 @@ class EnumArray(t.EnumArray):
266
266
  result: t.ObjArray
267
267
  if self.possible_values is None:
268
268
  msg = (
269
- f"The possible values of the {self.__class__.__name__} are "
270
- f"not defined."
269
+ f"The possible values of the {self.__class__.__name__} are not defined."
271
270
  )
272
271
  raise TypeError(msg)
273
272
  array = self.reshape(1).astype(t.EnumDType) if self.ndim == 0 else self
@@ -301,8 +300,7 @@ class EnumArray(t.EnumArray):
301
300
  result: t.StrArray
302
301
  if self.possible_values is None:
303
302
  msg = (
304
- f"The possible values of the {self.__class__.__name__} are "
305
- f"not defined."
303
+ f"The possible values of the {self.__class__.__name__} are not defined."
306
304
  )
307
305
  raise TypeError(msg)
308
306
  array = self.reshape(1).astype(t.EnumDType) if self.ndim == 0 else self
@@ -0,0 +1,36 @@
1
+ """Links between entities (Many2One, One2Many, chaining).
2
+
3
+ This module implements a generic link system inspired by LIAM2, enriched
4
+ with OpenFisca's role semantics. Links are orthogonal to the existing
5
+ GroupEntity/Projector machinery: current code keeps working unchanged,
6
+ and links provide additional capabilities (intra-entity links, chaining,
7
+ arbitrary named links).
8
+
9
+ Usage in a country package::
10
+
11
+ from openfisca_core.links import Many2OneLink, One2ManyLink
12
+
13
+ # Declare a person→person link (intra-entity)
14
+ mother = Many2OneLink(
15
+ name="mother",
16
+ link_field="mother_id",
17
+ target_entity_key="person",
18
+ )
19
+
20
+ # Declare a person→employer link (inter-entity)
21
+ employer = Many2OneLink(
22
+ name="employer",
23
+ link_field="employer_id",
24
+ target_entity_key="employer",
25
+ )
26
+ """
27
+
28
+ from .link import Link
29
+ from .many2one import Many2OneLink
30
+ from .one2many import One2ManyLink
31
+
32
+ __all__ = [
33
+ "Link",
34
+ "Many2OneLink",
35
+ "One2ManyLink",
36
+ ]
@@ -0,0 +1,111 @@
1
+ """Implicit links dynamically inferred from OpenFisca's GroupPopulation system."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import numpy
6
+
7
+ from .many2one import Many2OneLink
8
+ from .one2many import One2ManyLink
9
+
10
+
11
+ class ImplicitMany2OneLink(Many2OneLink):
12
+ """A person → group link using GroupPopulation's internal arrays.
13
+
14
+ This bypasses the usual Variable lookup (``simulation.calculate``)
15
+ and directly reads ``members_entity_id`` and ``members_role`` from
16
+ the target GroupPopulation.
17
+ """
18
+
19
+ def __init__(self, group_entity_key: str):
20
+ super().__init__(
21
+ name=group_entity_key,
22
+ link_field="", # Not used
23
+ target_entity_key=group_entity_key,
24
+ )
25
+
26
+ def _get_target_ids(self, period) -> numpy.ndarray:
27
+ return self._target_population.members_entity_id
28
+
29
+ @property
30
+ def role(self) -> numpy.ndarray | None:
31
+ return self._target_population.members_role
32
+
33
+ def _project_implicit(self, result: numpy.ndarray) -> numpy.ndarray:
34
+ # Fully compatible with old Projector logic
35
+ return self._target_population.project(result)
36
+
37
+
38
+ class ImplicitOne2ManyLink(One2ManyLink):
39
+ """A group → person link using GroupPopulation's internal arrays."""
40
+
41
+ def __init__(self, name: str, group_entity_key: str):
42
+ super().__init__(
43
+ name=name,
44
+ link_field="", # Not used
45
+ target_entity_key="person", # The target of the O2M is persons
46
+ )
47
+ self._group_entity_key = group_entity_key
48
+
49
+ def _source_rows(self, period) -> numpy.ndarray:
50
+ # For a group->person O2M, the source is the group, the target is the person.
51
+ # members_entity_id is an array of length person.count, containing the group index.
52
+ # So members_entity_id IS the array of source rows for each target member.
53
+ return self._source_population.members_entity_id
54
+
55
+ def _apply_filters(self, period, values, role, condition):
56
+ source_rows = self._source_rows(period)
57
+ mask = numpy.ones(len(source_rows), dtype=bool)
58
+
59
+ if role is not None:
60
+ roles = self._source_population.members_role
61
+ # roles may be an object array of Role instances, so compare by key
62
+ if roles.dtype == object:
63
+ try:
64
+ keys = numpy.fromiter(
65
+ (getattr(x, "key", x) for x in roles),
66
+ dtype=object,
67
+ )
68
+ except Exception:
69
+ mask &= roles == role
70
+ else:
71
+ mask &= keys == role
72
+ else:
73
+ mask &= roles == role
74
+
75
+ if condition is not None:
76
+ mask &= condition
77
+
78
+ source_rows = source_rows[mask]
79
+ values = values[mask]
80
+
81
+ valid = source_rows >= 0
82
+ return source_rows[valid], values[valid]
83
+
84
+ # override to avoid relying on ``role_field`` which is meaningless for
85
+ # implicit links (the role information is stored on the source population)
86
+ def get_by_role(
87
+ self,
88
+ variable_name: str,
89
+ period,
90
+ role_value,
91
+ *,
92
+ condition: numpy.ndarray | None = None,
93
+ ) -> numpy.ndarray:
94
+ """Fetch value for a specific role value on a one-to-many implicit link.
95
+
96
+ This mirrors :meth:`One2ManyLink.get_by_role` but uses
97
+ ``self._source_population.members_role`` instead of a named role field
98
+ on the target population.
99
+ """
100
+ values = self._target_population.simulation.calculate(variable_name, period)
101
+ source_rows, values = self._apply_filters(period, values, role_value, condition)
102
+
103
+ result = numpy.zeros(self._source_population.count, dtype=values.dtype)
104
+ # last value wins (same semantics as GroupPopulation.value_from_person)
105
+ for tgt_idx, src in enumerate(source_rows):
106
+ if src >= 0:
107
+ result[src] = values[tgt_idx]
108
+ return result
109
+
110
+
111
+ __all__ = ["ImplicitMany2OneLink", "ImplicitOne2ManyLink"]
@@ -0,0 +1,109 @@
1
+ """Base Link class for entity relationships."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ import numpy
9
+
10
+
11
+ class Link:
12
+ """A named, directed relationship between two entity populations.
13
+
14
+ A Link is defined by:
15
+
16
+ - A *name* used to access the link from formulas (e.g. "mother", "household").
17
+ - A *link_field*: the name of the Variable (on the source entity) that holds
18
+ the target entity IDs.
19
+ - A *target_entity_key*: the entity key of the target population.
20
+ - Optionally, a *role_field* and *position_field* to attach OpenFisca-style
21
+ role and position metadata to the relationship.
22
+
23
+
24
+ Sub-classes ``Many2OneLink`` and ``One2ManyLink`` add resolution logic.
25
+
26
+ Parameters
27
+ ----------
28
+ name : str
29
+ Human-readable name of the link (e.g. ``"mother"``).
30
+ link_field : str
31
+ Variable name on the **source** entity that holds IDs pointing to
32
+ the target entity.
33
+ target_entity_key : str
34
+ The ``Entity.key`` of the target entity.
35
+ role_field : str or None
36
+ Optional variable name holding the role of each source member
37
+ within the target group.
38
+ position_field : str or None
39
+ Optional variable name holding the positional index of each
40
+ source member within the target group.
41
+ """
42
+
43
+ def __init__(
44
+ self,
45
+ name: str,
46
+ link_field: str,
47
+ target_entity_key: str,
48
+ *,
49
+ role_field: str | None = None,
50
+ position_field: str | None = None,
51
+ ) -> None:
52
+ self.name = name
53
+ self.link_field = link_field
54
+ self.target_entity_key = target_entity_key
55
+ self.role_field = role_field
56
+ self.position_field = position_field
57
+
58
+ # Resolved after simulation setup
59
+ self._source_population = None
60
+ self._target_population = None
61
+
62
+ # -- lifecycle ----------------------------------------------------------
63
+
64
+ def attach(self, source_population) -> None:
65
+ """Bind this link to its source population."""
66
+ self._source_population = source_population
67
+
68
+ def resolve(self, populations: dict) -> None:
69
+ """Resolve ``target_entity_key`` to an actual population object.
70
+
71
+ Parameters
72
+ ----------
73
+ populations : dict[str, Population]
74
+ All populations in the simulation, keyed by entity key.
75
+ """
76
+ if self.target_entity_key not in populations:
77
+ msg = (
78
+ f"Link '{self.name}': target entity "
79
+ f"'{self.target_entity_key}' not found in populations "
80
+ f"{list(populations.keys())}"
81
+ )
82
+ raise KeyError(msg)
83
+ self._target_population = populations[self.target_entity_key]
84
+
85
+ # -- helpers ------------------------------------------------------------
86
+
87
+ @property
88
+ def is_resolved(self) -> bool:
89
+ return (
90
+ self._source_population is not None and self._target_population is not None
91
+ )
92
+
93
+ def _get_link_ids(self, period) -> numpy.ndarray:
94
+ """Return the array of target IDs for every source member.
95
+
96
+ This reads the ``link_field`` variable from the source population.
97
+ """
98
+ return self._source_population(self.link_field, period)
99
+
100
+ def __repr__(self) -> str:
101
+ return (
102
+ f"{self.__class__.__name__}("
103
+ f"name={self.name!r}, "
104
+ f"link_field={self.link_field!r}, "
105
+ f"target={self.target_entity_key!r})"
106
+ )
107
+
108
+
109
+ __all__ = ["Link"]