OpenFisca-Core 44.2.1__tar.gz → 44.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/CHANGELOG.md +20 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0/OpenFisca_Core.egg-info}/PKG-INFO +1 -1
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/OpenFisca_Core.egg-info/SOURCES.txt +15 -0
- {openfisca_core-44.2.1/OpenFisca_Core.egg-info → openfisca_core-44.3.0}/PKG-INFO +1 -1
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/entities/_core_entity.py +40 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/indexed_enums/_enum_type.py +1 -1
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/indexed_enums/_utils.py +18 -18
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/indexed_enums/enum_array.py +2 -4
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/indexed_enums/types.py +2 -1
- openfisca_core-44.3.0/openfisca_core/links/__init__.py +36 -0
- openfisca_core-44.3.0/openfisca_core/links/implicit.py +73 -0
- openfisca_core-44.3.0/openfisca_core/links/link.py +109 -0
- openfisca_core-44.3.0/openfisca_core/links/many2one.py +227 -0
- openfisca_core-44.3.0/openfisca_core/links/one2many.py +242 -0
- openfisca_core-44.3.0/openfisca_core/links/tests/__init__.py +58 -0
- openfisca_core-44.3.0/openfisca_core/links/tests/test_benchmark.py +93 -0
- openfisca_core-44.3.0/openfisca_core/links/tests/test_edge_cases.py +467 -0
- openfisca_core-44.3.0/openfisca_core/links/tests/test_implicit.py +76 -0
- openfisca_core-44.3.0/openfisca_core/links/tests/test_integration.py +192 -0
- openfisca_core-44.3.0/openfisca_core/links/tests/test_many2one.py +121 -0
- openfisca_core-44.3.0/openfisca_core/links/tests/test_one2many.py +112 -0
- openfisca_core-44.3.0/openfisca_core/links/tests/test_one2many_logic.py +398 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/populations/group_population.py +81 -21
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/populations/population.py +7 -5
- openfisca_core-44.3.0/openfisca_core/populations/tests/test_members_position.py +116 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/simulations/simulation.py +48 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/taxscales/abstract_rate_tax_scale.py +1 -1
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/taxscales/abstract_tax_scale.py +2 -2
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/taxscales/tax_scale_like.py +2 -2
- openfisca_core-44.3.0/openfisca_web_api/scripts/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/pyproject.toml +1 -1
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/setup.py +1 -1
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/LICENSE +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/MANIFEST.in +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/OpenFisca_Core.egg-info/dependency_links.txt +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/OpenFisca_Core.egg-info/entry_points.txt +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/OpenFisca_Core.egg-info/requires.txt +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/OpenFisca_Core.egg-info/top_level.txt +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/README.md +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/commons/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/commons/dummy.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/commons/formulas.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/commons/misc.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/commons/py.typed +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/commons/rates.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/commons/tests/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/commons/tests/test_dummy.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/commons/tests/test_formulas.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/commons/tests/test_rates.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/data_storage/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/data_storage/in_memory_storage.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/data_storage/on_disk_storage.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/data_storage/types.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/entities/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/entities/_description.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/entities/entity.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/entities/group_entity.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/entities/helpers.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/entities/py.typed +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/entities/role.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/entities/tests/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/entities/tests/test_entity.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/entities/tests/test_group_entity.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/entities/tests/test_role.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/entities/types.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/errors/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/errors/cycle_error.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/errors/empty_argument_error.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/errors/nan_creation_error.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/errors/parameter_not_found_error.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/errors/parameter_parsing_error.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/errors/period_mismatch_error.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/errors/situation_parsing_error.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/errors/spiral_error.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/errors/variable_name_config_error.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/errors/variable_not_found_error.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/experimental/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/experimental/_errors.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/experimental/_memory_config.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/formula_helpers.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/holders/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/holders/helpers.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/holders/holder.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/holders/tests/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/holders/tests/test_helpers.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/holders/types.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/indexed_enums/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/indexed_enums/_errors.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/indexed_enums/_guards.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/indexed_enums/config.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/indexed_enums/enum.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/indexed_enums/py.typed +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/indexed_enums/tests/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/indexed_enums/tests/test_enum.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/indexed_enums/tests/test_enum_array.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/memory_config.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/model_api.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/parameters/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/parameters/at_instant_like.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/parameters/config.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/parameters/helpers.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/parameters/parameter.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/parameters/parameter_at_instant.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/parameters/parameter_node.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/parameters/parameter_node_at_instant.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/parameters/parameter_scale.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/parameters/parameter_scale_bracket.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/parameters/values_history.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/parameters/vectorial_parameter_node_at_instant.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/periods/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/periods/_errors.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/periods/_parsers.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/periods/config.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/periods/date_unit.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/periods/helpers.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/periods/instant_.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/periods/period_.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/periods/py.typed +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/periods/tests/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/periods/tests/helpers/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/periods/tests/helpers/test_helpers.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/periods/tests/helpers/test_instant.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/periods/tests/helpers/test_period.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/periods/tests/test_instant.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/periods/tests/test_parsers.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/periods/tests/test_period.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/populations/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/populations/_core_population.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/populations/_errors.py +0 -0
- {openfisca_core-44.2.1/openfisca_core/scripts/assets → openfisca_core-44.3.0/openfisca_core/populations/tests}/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/populations/types.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/projectors/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/projectors/entity_to_person_projector.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/projectors/first_person_to_entity_projector.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/projectors/helpers.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/projectors/projector.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/projectors/typing.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/projectors/unique_role_to_entity_projector.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/rates.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/reforms/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/reforms/in_yaml_test_reform.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/reforms/reform.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/reforms/reform_excel.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/scripts/__init__.py +0 -0
- {openfisca_core-44.2.1/openfisca_core/scripts/migrations → openfisca_core-44.3.0/openfisca_core/scripts/assets}/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/scripts/assets/index.html +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/scripts/find_placeholders.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/scripts/measure_numpy_condition_notations.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/scripts/measure_performances.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/scripts/measure_performances_fancy_indexing.py +0 -0
- {openfisca_core-44.2.1/openfisca_core/scripts/migrations/v16_2_to_v17 → openfisca_core-44.3.0/openfisca_core/scripts/migrations}/__init__.py +0 -0
- {openfisca_core-44.2.1/openfisca_core/variables/tests → openfisca_core-44.3.0/openfisca_core/scripts/migrations/v16_2_to_v17}/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/scripts/migrations/v16_2_to_v17/legislation.xsd +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/scripts/migrations/v16_2_to_v17/xml_to_yaml_country_template.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/scripts/migrations/v16_2_to_v17/xml_to_yaml_extension_template.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/scripts/migrations/v24_to_25.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/scripts/openfisca_command.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/scripts/remove_fuzzy.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/scripts/run_test.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/scripts/simulation_generator.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/simulation_builder.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/simulations/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/simulations/_build_default_simulation.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/simulations/_build_from_variables.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/simulations/_type_guards.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/simulations/helpers.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/simulations/simulation_builder.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/simulations/typing.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/taxbenefitsystems/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/taxbenefitsystems/tax_benefit_system.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/taxscales/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/taxscales/amount_tax_scale_like.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/taxscales/helpers.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/taxscales/linear_average_rate_tax_scale.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/taxscales/marginal_amount_tax_scale.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/taxscales/marginal_rate_tax_scale.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/taxscales/rate_tax_scale_like.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/taxscales/single_amount_tax_scale.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/tools/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/tools/parallel_plugin.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/tools/simulation_dumper.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/tools/test_runner.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/tracers/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/tracers/computation_log.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/tracers/flat_trace.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/tracers/full_tracer.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/tracers/performance_log.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/tracers/simple_tracer.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/tracers/trace_node.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/tracers/tracing_parameter_node_at_instant.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/types.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/variables/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/variables/config.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/variables/helpers.py +0 -0
- {openfisca_core-44.2.1/openfisca_web_api → openfisca_core-44.3.0/openfisca_core/variables/tests}/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/variables/tests/test_definition_period.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/variables/variable.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/warnings/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/warnings/libyaml_warning.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_core/warnings/tempfile_warning.py +0 -0
- {openfisca_core-44.2.1/openfisca_web_api/scripts → openfisca_core-44.3.0/openfisca_web_api}/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_web_api/app.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_web_api/errors.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_web_api/handlers.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_web_api/loader/__init__.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_web_api/loader/entities.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_web_api/loader/parameters.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_web_api/loader/spec.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_web_api/loader/tax_benefit_system.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_web_api/loader/variables.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_web_api/openAPI.yml +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/openfisca_web_api/scripts/serve.py +0 -0
- {openfisca_core-44.2.1 → openfisca_core-44.3.0}/setup.cfg +0 -0
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 44.3.0
|
|
4
|
+
|
|
5
|
+
#### New Features
|
|
6
|
+
|
|
7
|
+
- **Generic Entity Links (Phase 1-6)**: Introduced a new Liam2-inspired generic entity linking system avoiding rigid hierarchies like `Person -> Household`.
|
|
8
|
+
- Added new `Many2OneLink` and `One2ManyLink` models to create powerful inter-entity networks (e.g., `Person -> Employer`).
|
|
9
|
+
- Added implicit links directly binding members arrays. This powers the new `population.links` property natively inside `TaxBenefitSystem.instantiate_entities()`.
|
|
10
|
+
- Full capability to chain relationships via python: `person.mother.household.get("rent", period)`.
|
|
11
|
+
- Powerful vectorized declarative aggregations out-of-the-box (e.g., `households.persons.sum("salary", period, condition=is_female)`).
|
|
12
|
+
|
|
13
|
+
#### Technical Changes
|
|
14
|
+
|
|
15
|
+
- Backward compatibility is 100% maintained. Existing syntax via Projectors natively redirects to implicit links via modified `__getattr__`.
|
|
16
|
+
|
|
17
|
+
## 44.2.2
|
|
18
|
+
|
|
19
|
+
#### Bug fixes
|
|
20
|
+
|
|
21
|
+
- Fix enum index overflow for enums with more than 256 members (e.g. NAF). Index arrays were encoded as `uint8` (0-255), causing indices > 255 to be truncated and wrong values in simulations. Encoding now uses `int16` (`ENUM_ARRAY_DTYPE`) consistently.
|
|
22
|
+
|
|
3
23
|
## 44.2.1 [#1341](https://github.com/openfisca/openfisca-core/pull/1341)
|
|
4
24
|
|
|
5
25
|
#### Technical changes
|
|
@@ -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
|
|
@@ -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,
|
|
@@ -39,22 +39,22 @@ def _enum_to_index(value: t.ObjArray | t.ArrayLike[t.Enum]) -> t.IndexArray:
|
|
|
39
39
|
TypeError: 'Road' object is not iterable
|
|
40
40
|
|
|
41
41
|
>>> _enum_to_index([Road.AVENUE])
|
|
42
|
-
array([1], dtype=
|
|
42
|
+
array([1], dtype=int16)
|
|
43
43
|
|
|
44
44
|
>>> _enum_to_index(numpy.array(Road.AVENUE))
|
|
45
45
|
Traceback (most recent call last):
|
|
46
46
|
TypeError: iteration over a 0-d array
|
|
47
47
|
|
|
48
48
|
>>> _enum_to_index(numpy.array([Road.AVENUE]))
|
|
49
|
-
array([1], dtype=
|
|
49
|
+
array([1], dtype=int16)
|
|
50
50
|
|
|
51
51
|
>>> value = numpy.array([Road.STREET, Road.AVENUE, Road.STREET])
|
|
52
52
|
>>> _enum_to_index(value)
|
|
53
|
-
array([0, 1, 0], dtype=
|
|
53
|
+
array([0, 1, 0], dtype=int16)
|
|
54
54
|
|
|
55
55
|
>>> value = numpy.array([Road.AVENUE, Road.AVENUE, Rogue.BOULEVARD])
|
|
56
56
|
>>> _enum_to_index(value)
|
|
57
|
-
array([1, 1, 0], dtype=
|
|
57
|
+
array([1, 1, 0], dtype=int16)
|
|
58
58
|
|
|
59
59
|
"""
|
|
60
60
|
return numpy.array([enum.index for enum in value], t.EnumDType)
|
|
@@ -92,28 +92,28 @@ def _int_to_index(
|
|
|
92
92
|
... )
|
|
93
93
|
|
|
94
94
|
>>> _int_to_index(Road, 1)
|
|
95
|
-
array([1], dtype=
|
|
95
|
+
array([1], dtype=int16)
|
|
96
96
|
|
|
97
97
|
>>> _int_to_index(Road, [1])
|
|
98
|
-
array([1], dtype=
|
|
98
|
+
array([1], dtype=int16)
|
|
99
99
|
|
|
100
100
|
>>> _int_to_index(Road, array("B", [1]))
|
|
101
|
-
array([1], dtype=
|
|
101
|
+
array([1], dtype=int16)
|
|
102
102
|
|
|
103
103
|
>>> _int_to_index(Road, memoryview(array("B", [1])))
|
|
104
|
-
array([1], dtype=
|
|
104
|
+
array([1], dtype=int16)
|
|
105
105
|
|
|
106
106
|
>>> _int_to_index(Road, numpy.array(1))
|
|
107
|
-
array([1], dtype=
|
|
107
|
+
array([1], dtype=int16)
|
|
108
108
|
|
|
109
109
|
>>> _int_to_index(Road, numpy.array([1]))
|
|
110
|
-
array([1], dtype=
|
|
110
|
+
array([1], dtype=int16)
|
|
111
111
|
|
|
112
112
|
>>> _int_to_index(Road, numpy.array([0, 1, 0]))
|
|
113
|
-
array([0, 1, 0], dtype=
|
|
113
|
+
array([0, 1, 0], dtype=int16)
|
|
114
114
|
|
|
115
115
|
>>> _int_to_index(Road, numpy.array([1, 1, 2]))
|
|
116
|
-
array([1, 1], dtype=
|
|
116
|
+
array([1, 1], dtype=int16)
|
|
117
117
|
|
|
118
118
|
"""
|
|
119
119
|
indices = enum_class.indices
|
|
@@ -151,22 +151,22 @@ def _str_to_index(
|
|
|
151
151
|
... )
|
|
152
152
|
|
|
153
153
|
>>> _str_to_index(Road, "AVENUE")
|
|
154
|
-
array([1], dtype=
|
|
154
|
+
array([1], dtype=int16)
|
|
155
155
|
|
|
156
156
|
>>> _str_to_index(Road, ["AVENUE"])
|
|
157
|
-
array([1], dtype=
|
|
157
|
+
array([1], dtype=int16)
|
|
158
158
|
|
|
159
159
|
>>> _str_to_index(Road, numpy.array("AVENUE"))
|
|
160
|
-
array([1], dtype=
|
|
160
|
+
array([1], dtype=int16)
|
|
161
161
|
|
|
162
162
|
>>> _str_to_index(Road, numpy.array(["AVENUE"]))
|
|
163
|
-
array([1], dtype=
|
|
163
|
+
array([1], dtype=int16)
|
|
164
164
|
|
|
165
165
|
>>> _str_to_index(Road, numpy.array(["STREET", "AVENUE", "STREET"]))
|
|
166
|
-
array([0, 1, 0], dtype=
|
|
166
|
+
array([0, 1, 0], dtype=int16)
|
|
167
167
|
|
|
168
168
|
>>> _str_to_index(Road, numpy.array(["AVENUE", "AVENUE", "BOULEVARD"]))
|
|
169
|
-
array([1, 1], dtype=
|
|
169
|
+
array([1, 1], dtype=int16)
|
|
170
170
|
|
|
171
171
|
"""
|
|
172
172
|
values = numpy.asarray(value)
|
|
@@ -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
|
|
@@ -10,9 +10,10 @@ from numpy import (
|
|
|
10
10
|
int32 as IntDType,
|
|
11
11
|
object_ as ObjDType,
|
|
12
12
|
str_ as StrDType,
|
|
13
|
-
uint8 as EnumDType,
|
|
14
13
|
)
|
|
15
14
|
|
|
15
|
+
from .config import ENUM_ARRAY_DTYPE as EnumDType
|
|
16
|
+
|
|
16
17
|
#: Type for enum indices arrays.
|
|
17
18
|
IndexArray: TypeAlias = Array[EnumDType]
|
|
18
19
|
|
|
@@ -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,73 @@
|
|
|
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
|
+
mask &= roles == role
|
|
62
|
+
|
|
63
|
+
if condition is not None:
|
|
64
|
+
mask &= condition
|
|
65
|
+
|
|
66
|
+
source_rows = source_rows[mask]
|
|
67
|
+
values = values[mask]
|
|
68
|
+
|
|
69
|
+
valid = source_rows >= 0
|
|
70
|
+
return source_rows[valid], values[valid]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
__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"]
|