OpenFisca-Core 44.5.0__tar.gz → 44.7.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.5.0 → openfisca_core-44.7.0}/CHANGELOG.md +6 -36
- {openfisca_core-44.5.0 → openfisca_core-44.7.0/OpenFisca_Core.egg-info}/PKG-INFO +1 -1
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/OpenFisca_Core.egg-info/SOURCES.txt +0 -13
- {openfisca_core-44.5.0/OpenFisca_Core.egg-info → openfisca_core-44.7.0}/PKG-INFO +1 -1
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/entities/_core_entity.py +0 -40
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/holders/holder.py +0 -65
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/indexed_enums/enum_array.py +4 -2
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/populations/_core_population.py +0 -32
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/populations/group_population.py +15 -68
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/populations/population.py +5 -7
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/reforms/reform_excel.py +14 -2
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/simulations/_build_default_simulation.py +0 -11
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/simulations/simulation.py +0 -48
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/simulations/simulation_builder.py +0 -2
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/taxscales/abstract_rate_tax_scale.py +1 -1
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/taxscales/abstract_tax_scale.py +2 -2
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/taxscales/tax_scale_like.py +2 -2
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/variables/variable.py +0 -28
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/pyproject.toml +1 -1
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/setup.py +1 -1
- openfisca_core-44.5.0/openfisca_core/links/__init__.py +0 -36
- openfisca_core-44.5.0/openfisca_core/links/implicit.py +0 -111
- openfisca_core-44.5.0/openfisca_core/links/link.py +0 -109
- openfisca_core-44.5.0/openfisca_core/links/many2one.py +0 -310
- openfisca_core-44.5.0/openfisca_core/links/one2many.py +0 -306
- openfisca_core-44.5.0/openfisca_core/links/tests/__init__.py +0 -58
- openfisca_core-44.5.0/openfisca_core/links/tests/test_benchmark.py +0 -93
- openfisca_core-44.5.0/openfisca_core/links/tests/test_edge_cases.py +0 -467
- openfisca_core-44.5.0/openfisca_core/links/tests/test_implicit.py +0 -76
- openfisca_core-44.5.0/openfisca_core/links/tests/test_integration.py +0 -192
- openfisca_core-44.5.0/openfisca_core/links/tests/test_many2one.py +0 -149
- openfisca_core-44.5.0/openfisca_core/links/tests/test_one2many.py +0 -112
- openfisca_core-44.5.0/openfisca_core/links/tests/test_one2many_logic.py +0 -398
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/LICENSE +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/MANIFEST.in +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/OpenFisca_Core.egg-info/dependency_links.txt +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/OpenFisca_Core.egg-info/entry_points.txt +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/OpenFisca_Core.egg-info/requires.txt +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/OpenFisca_Core.egg-info/top_level.txt +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/README.md +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/commons/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/commons/dummy.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/commons/formulas.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/commons/misc.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/commons/py.typed +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/commons/rates.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/commons/tests/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/commons/tests/test_dummy.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/commons/tests/test_formulas.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/commons/tests/test_rates.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/data_storage/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/data_storage/in_memory_storage.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/data_storage/on_disk_storage.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/data_storage/types.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/entities/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/entities/_description.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/entities/entity.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/entities/group_entity.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/entities/helpers.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/entities/py.typed +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/entities/role.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/entities/tests/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/entities/tests/test_entity.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/entities/tests/test_group_entity.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/entities/tests/test_role.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/entities/types.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/errors/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/errors/cycle_error.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/errors/empty_argument_error.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/errors/nan_creation_error.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/errors/parameter_not_found_error.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/errors/parameter_parsing_error.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/errors/period_mismatch_error.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/errors/situation_parsing_error.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/errors/spiral_error.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/errors/variable_name_config_error.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/errors/variable_not_found_error.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/experimental/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/experimental/_errors.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/experimental/_memory_config.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/formula_helpers.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/holders/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/holders/helpers.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/holders/tests/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/holders/tests/test_helpers.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/holders/types.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/indexed_enums/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/indexed_enums/_enum_type.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/indexed_enums/_errors.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/indexed_enums/_guards.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/indexed_enums/_utils.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/indexed_enums/config.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/indexed_enums/enum.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/indexed_enums/py.typed +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/indexed_enums/tests/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/indexed_enums/tests/test_enum.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/indexed_enums/tests/test_enum_array.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/indexed_enums/types.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/memory_config.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/model_api.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/parameters/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/parameters/at_instant_like.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/parameters/config.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/parameters/helpers.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/parameters/parameter.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/parameters/parameter_at_instant.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/parameters/parameter_node.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/parameters/parameter_node_at_instant.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/parameters/parameter_scale.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/parameters/parameter_scale_bracket.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/parameters/values_history.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/parameters/vectorial_parameter_node_at_instant.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/periods/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/periods/_errors.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/periods/_parsers.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/periods/config.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/periods/date_unit.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/periods/helpers.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/periods/instant_.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/periods/period_.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/periods/py.typed +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/periods/tests/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/periods/tests/helpers/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/periods/tests/helpers/test_helpers.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/periods/tests/helpers/test_instant.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/periods/tests/helpers/test_period.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/periods/tests/test_instant.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/periods/tests/test_parsers.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/periods/tests/test_period.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/populations/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/populations/_errors.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/populations/tests/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/populations/tests/test_members_position.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/populations/types.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/projectors/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/projectors/entity_to_person_projector.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/projectors/first_person_to_entity_projector.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/projectors/helpers.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/projectors/projector.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/projectors/typing.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/projectors/unique_role_to_entity_projector.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/rates.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/reforms/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/reforms/in_yaml_test_reform.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/reforms/reform.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/scripts/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/scripts/assets/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/scripts/assets/index.html +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/scripts/find_placeholders.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/scripts/measure_numpy_condition_notations.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/scripts/measure_performances.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/scripts/measure_performances_fancy_indexing.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/scripts/migrations/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/scripts/migrations/v16_2_to_v17/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/scripts/migrations/v16_2_to_v17/legislation.xsd +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/scripts/migrations/v16_2_to_v17/xml_to_yaml_country_template.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/scripts/migrations/v16_2_to_v17/xml_to_yaml_extension_template.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/scripts/migrations/v24_to_25.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/scripts/openfisca_command.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/scripts/remove_fuzzy.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/scripts/run_test.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/scripts/simulation_generator.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/simulation_builder.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/simulations/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/simulations/_build_from_variables.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/simulations/_type_guards.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/simulations/helpers.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/simulations/typing.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/taxbenefitsystems/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/taxbenefitsystems/tax_benefit_system.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/taxscales/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/taxscales/amount_tax_scale_like.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/taxscales/helpers.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/taxscales/linear_average_rate_tax_scale.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/taxscales/marginal_amount_tax_scale.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/taxscales/marginal_rate_tax_scale.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/taxscales/rate_tax_scale_like.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/taxscales/single_amount_tax_scale.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/tools/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/tools/parallel_plugin.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/tools/simulation_dumper.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/tools/test_runner.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/tracers/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/tracers/computation_log.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/tracers/flat_trace.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/tracers/full_tracer.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/tracers/performance_log.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/tracers/simple_tracer.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/tracers/trace_node.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/tracers/tracing_parameter_node_at_instant.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/types.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/variables/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/variables/config.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/variables/helpers.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/variables/tests/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/variables/tests/test_definition_period.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/warnings/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/warnings/libyaml_warning.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/warnings/tempfile_warning.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_web_api/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_web_api/app.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_web_api/errors.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_web_api/handlers.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_web_api/loader/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_web_api/loader/entities.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_web_api/loader/parameters.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_web_api/loader/spec.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_web_api/loader/tax_benefit_system.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_web_api/loader/variables.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_web_api/openAPI.yml +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_web_api/scripts/__init__.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_web_api/scripts/serve.py +0 -0
- {openfisca_core-44.5.0 → openfisca_core-44.7.0}/setup.cfg +0 -0
|
@@ -1,48 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 44.
|
|
3
|
+
## 44.7.0 [#1357](https://github.com/openfisca/openfisca-core/pull/1357)
|
|
4
4
|
|
|
5
5
|
#### New features
|
|
6
6
|
|
|
7
|
-
-
|
|
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.
|
|
7
|
+
- Manage list parameter in Excel reform
|
|
15
8
|
|
|
16
|
-
## 44.
|
|
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)`).
|
|
9
|
+
## 44.6.0
|
|
42
10
|
|
|
43
11
|
#### Technical changes
|
|
44
12
|
|
|
45
|
-
-
|
|
13
|
+
- Revert the codebase to commit `8d26af3` to align the repository with the currently published `44.2.2` baseline on PyPI.
|
|
14
|
+
- Mark versions `44.3.0`, `44.4.0`, `44.4.1`, and `44.5.0` as yanked.
|
|
15
|
+
- Bump version to `44.6.0` to restore continuous deployment from this reverted baseline.
|
|
46
16
|
|
|
47
17
|
## 44.2.2
|
|
48
18
|
|
|
@@ -78,19 +78,6 @@ 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
|
|
94
81
|
openfisca_core/parameters/__init__.py
|
|
95
82
|
openfisca_core/parameters/at_instant_like.py
|
|
96
83
|
openfisca_core/parameters/config.py
|
|
@@ -47,9 +47,6 @@ 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
|
-
|
|
53
50
|
@abc.abstractmethod
|
|
54
51
|
def __init__(self, *__args: object, **__kwargs: object) -> None: ...
|
|
55
52
|
|
|
@@ -60,43 +57,6 @@ class CoreEntity:
|
|
|
60
57
|
"""A ``CoreEntity`` belongs to a ``TaxBenefitSystem``."""
|
|
61
58
|
self._tax_benefit_system = tax_benefit_system
|
|
62
59
|
|
|
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
|
-
|
|
100
60
|
def get_variable(
|
|
101
61
|
self,
|
|
102
62
|
variable_name: t.VariableName,
|
|
@@ -3,7 +3,6 @@ from __future__ import annotations
|
|
|
3
3
|
from collections.abc import Sequence
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
|
-
import bisect
|
|
7
6
|
import os
|
|
8
7
|
import warnings
|
|
9
8
|
|
|
@@ -30,10 +29,7 @@ class Holder:
|
|
|
30
29
|
self.variable = variable
|
|
31
30
|
self.simulation = population.simulation
|
|
32
31
|
self._eternal = self.variable.definition_period == periods.DateUnit.ETERNITY
|
|
33
|
-
self._as_of = getattr(self.variable, "as_of", False)
|
|
34
32
|
self._memory_storage = storage.InMemoryStorage(is_eternal=self._eternal)
|
|
35
|
-
if self._as_of:
|
|
36
|
-
self._sorted_instants: list = []
|
|
37
33
|
|
|
38
34
|
# By default, do not activate on-disk storage, or variable dropping
|
|
39
35
|
self._disk_storage = None
|
|
@@ -58,11 +54,6 @@ class Holder:
|
|
|
58
54
|
if key not in ("population", "formula", "simulation"):
|
|
59
55
|
new_dict[key] = value
|
|
60
56
|
|
|
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
|
-
|
|
66
57
|
new_dict["population"] = population
|
|
67
58
|
new_dict["simulation"] = population.simulation
|
|
68
59
|
|
|
@@ -99,52 +90,10 @@ class Holder:
|
|
|
99
90
|
value = self._memory_storage.get(period)
|
|
100
91
|
if value is not None:
|
|
101
92
|
return value
|
|
102
|
-
if self._as_of:
|
|
103
|
-
value = self._get_as_of(period)
|
|
104
|
-
if value is not None:
|
|
105
|
-
return value
|
|
106
93
|
if self._disk_storage:
|
|
107
94
|
return self._disk_storage.get(period)
|
|
108
95
|
return None
|
|
109
96
|
|
|
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
|
-
|
|
148
97
|
def get_memory_usage(self) -> t.MemoryUsage:
|
|
149
98
|
"""Get data about the virtual memory usage of the Holder.
|
|
150
99
|
|
|
@@ -353,25 +302,11 @@ class Holder:
|
|
|
353
302
|
>= self.simulation.memory_config.max_memory_occupation_pc
|
|
354
303
|
)
|
|
355
304
|
|
|
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
|
-
|
|
367
305
|
if should_store_on_disk:
|
|
368
306
|
self._disk_storage.put(value, period)
|
|
369
307
|
else:
|
|
370
308
|
self._memory_storage.put(value, period)
|
|
371
309
|
|
|
372
|
-
if self._as_of:
|
|
373
|
-
self._register_instant(period)
|
|
374
|
-
|
|
375
310
|
def put_in_cache(self, value, period) -> None:
|
|
376
311
|
if self._do_not_store:
|
|
377
312
|
return
|
|
@@ -266,7 +266,8 @@ 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
|
|
269
|
+
f"The possible values of the {self.__class__.__name__} are "
|
|
270
|
+
f"not defined."
|
|
270
271
|
)
|
|
271
272
|
raise TypeError(msg)
|
|
272
273
|
array = self.reshape(1).astype(t.EnumDType) if self.ndim == 0 else self
|
|
@@ -300,7 +301,8 @@ class EnumArray(t.EnumArray):
|
|
|
300
301
|
result: t.StrArray
|
|
301
302
|
if self.possible_values is None:
|
|
302
303
|
msg = (
|
|
303
|
-
f"The possible values of the {self.__class__.__name__} are
|
|
304
|
+
f"The possible values of the {self.__class__.__name__} are "
|
|
305
|
+
f"not defined."
|
|
304
306
|
)
|
|
305
307
|
raise TypeError(msg)
|
|
306
308
|
array = self.reshape(1).astype(t.EnumDType) if self.ndim == 0 else self
|
{openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/populations/_core_population.py
RENAMED
|
@@ -42,12 +42,6 @@ class CorePopulation:
|
|
|
42
42
|
|
|
43
43
|
#: The :class:`~simulations.Simulation` for which the population is calculated.
|
|
44
44
|
simulation: None | t.Simulation = None
|
|
45
|
-
# === Dynamic population helpers (optional) ===
|
|
46
|
-
_dynamic: bool = False
|
|
47
|
-
_permanent_ids: None | numpy.ndarray = None
|
|
48
|
-
_id_to_rownum: None | numpy.ndarray = None
|
|
49
|
-
_next_id: None | int = None
|
|
50
|
-
_period_index: None | dict = None
|
|
51
45
|
|
|
52
46
|
def __init__(self, entity: t.CoreEntity, *__args: object, **__kwds: object) -> None:
|
|
53
47
|
self.entity = entity
|
|
@@ -457,31 +451,5 @@ class CorePopulation:
|
|
|
457
451
|
by_variable=holders_memory_usage,
|
|
458
452
|
)
|
|
459
453
|
|
|
460
|
-
# -- Dynamic population period index helpers -------------------------
|
|
461
|
-
|
|
462
|
-
def snapshot_period(self, period: t.PeriodLike) -> None:
|
|
463
|
-
"""Save the current ``_id_to_rownum`` mapping for ``period``.
|
|
464
|
-
|
|
465
|
-
If no ``_id_to_rownum`` exists yet, a default identity mapping is
|
|
466
|
-
created (positions == ids) sized to ``self.count``.
|
|
467
|
-
"""
|
|
468
|
-
if self._period_index is None:
|
|
469
|
-
self._period_index = {}
|
|
470
|
-
|
|
471
|
-
if self._id_to_rownum is None:
|
|
472
|
-
# Default identity mapping: id -> row (0..count-1)
|
|
473
|
-
self._id_to_rownum = numpy.arange(self.count, dtype=numpy.intp)
|
|
474
|
-
|
|
475
|
-
self._period_index[periods.period(period)] = self._id_to_rownum.copy()
|
|
476
|
-
|
|
477
|
-
def get_period_id_to_rownum(self, period: t.PeriodLike) -> None | numpy.ndarray:
|
|
478
|
-
"""Return the saved ``id -> rownum`` mapping for ``period`` or ``None``.
|
|
479
|
-
|
|
480
|
-
Returns ``None`` when no index was saved for that period.
|
|
481
|
-
"""
|
|
482
|
-
if self._period_index is None:
|
|
483
|
-
return None
|
|
484
|
-
return self._period_index.get(periods.period(period))
|
|
485
|
-
|
|
486
454
|
|
|
487
455
|
__all__ = ["CorePopulation"]
|
{openfisca_core-44.5.0 → openfisca_core-44.7.0}/openfisca_core/populations/group_population.py
RENAMED
|
@@ -96,42 +96,16 @@ class GroupPopulation(Population):
|
|
|
96
96
|
None,
|
|
97
97
|
)
|
|
98
98
|
|
|
99
|
-
# Filtering helpers
|
|
100
|
-
|
|
101
|
-
def _build_member_mask(self, role=None, condition=None):
|
|
102
|
-
"""Build a combined boolean mask from role and condition filters.
|
|
103
|
-
|
|
104
|
-
Returns None if no filtering is needed (both are None), otherwise
|
|
105
|
-
returns a boolean array of length ``members.count``.
|
|
106
|
-
"""
|
|
107
|
-
if role is None and condition is None:
|
|
108
|
-
return None
|
|
109
|
-
|
|
110
|
-
mask = numpy.ones(self.members.count, dtype=bool)
|
|
111
|
-
|
|
112
|
-
if role is not None:
|
|
113
|
-
role_filter = self.members.has_role(role)
|
|
114
|
-
mask &= role_filter
|
|
115
|
-
|
|
116
|
-
if condition is not None:
|
|
117
|
-
mask &= condition
|
|
118
|
-
|
|
119
|
-
return mask
|
|
120
|
-
|
|
121
99
|
# Aggregation persons -> entity
|
|
122
100
|
|
|
123
101
|
@projectors.projectable
|
|
124
|
-
def sum(self, array, role=None
|
|
102
|
+
def sum(self, array, role=None):
|
|
125
103
|
"""Return the sum of ``array`` for the members of the entity.
|
|
126
104
|
|
|
127
105
|
``array`` must have the dimension of the number of persons in the simulation
|
|
128
106
|
|
|
129
107
|
If ``role`` is provided, only the entity member with the given role are taken into account.
|
|
130
108
|
|
|
131
|
-
If ``condition`` is provided (a boolean array of the same size as the
|
|
132
|
-
person population), only members for whom the condition is ``True`` are
|
|
133
|
-
taken into account. ``role`` and ``condition`` can be combined.
|
|
134
|
-
|
|
135
109
|
Example:
|
|
136
110
|
>>> salaries = household.members(
|
|
137
111
|
... "salary", "2018-01"
|
|
@@ -142,27 +116,23 @@ class GroupPopulation(Population):
|
|
|
142
116
|
"""
|
|
143
117
|
self.entity.check_role_validity(role)
|
|
144
118
|
self.members.check_array_compatible_with_entity(array)
|
|
145
|
-
|
|
146
|
-
|
|
119
|
+
if role is not None:
|
|
120
|
+
role_filter = self.members.has_role(role)
|
|
147
121
|
return numpy.bincount(
|
|
148
|
-
self.members_entity_id[
|
|
149
|
-
weights=array[
|
|
122
|
+
self.members_entity_id[role_filter],
|
|
123
|
+
weights=array[role_filter],
|
|
150
124
|
minlength=self.count,
|
|
151
125
|
)
|
|
152
126
|
return numpy.bincount(self.members_entity_id, weights=array)
|
|
153
127
|
|
|
154
128
|
@projectors.projectable
|
|
155
|
-
def any(self, array, role=None
|
|
129
|
+
def any(self, array, role=None):
|
|
156
130
|
"""Return ``True`` if ``array`` is ``True`` for any members of the entity.
|
|
157
131
|
|
|
158
132
|
``array`` must have the dimension of the number of persons in the simulation
|
|
159
133
|
|
|
160
134
|
If ``role`` is provided, only the entity member with the given role are taken into account.
|
|
161
135
|
|
|
162
|
-
If ``condition`` is provided (a boolean array of the same size as the
|
|
163
|
-
person population), only members for whom the condition is ``True`` are
|
|
164
|
-
taken into account. ``role`` and ``condition`` can be combined.
|
|
165
|
-
|
|
166
136
|
Example:
|
|
167
137
|
>>> salaries = household.members(
|
|
168
138
|
... "salary", "2018-01"
|
|
@@ -171,18 +141,16 @@ class GroupPopulation(Population):
|
|
|
171
141
|
>>> array([True])
|
|
172
142
|
|
|
173
143
|
"""
|
|
174
|
-
sum_in_entity = self.sum(array, role=role
|
|
144
|
+
sum_in_entity = self.sum(array, role=role)
|
|
175
145
|
return sum_in_entity > 0
|
|
176
146
|
|
|
177
147
|
@projectors.projectable
|
|
178
|
-
def reduce(self, array, reducer, neutral_element, role=None
|
|
148
|
+
def reduce(self, array, reducer, neutral_element, role=None):
|
|
179
149
|
self.members.check_array_compatible_with_entity(array)
|
|
180
150
|
self.entity.check_role_validity(role)
|
|
181
151
|
position_in_entity = self.members_position
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
mask = True # scalar True broadcasts; preserves old upcast behavior
|
|
185
|
-
filtered_array = numpy.where(mask, array, neutral_element)
|
|
152
|
+
role_filter = self.members.has_role(role) if role is not None else True
|
|
153
|
+
filtered_array = numpy.where(role_filter, array, neutral_element)
|
|
186
154
|
|
|
187
155
|
result = self.filled_array(
|
|
188
156
|
neutral_element,
|
|
@@ -199,17 +167,13 @@ class GroupPopulation(Population):
|
|
|
199
167
|
return result
|
|
200
168
|
|
|
201
169
|
@projectors.projectable
|
|
202
|
-
def all(self, array, role=None
|
|
170
|
+
def all(self, array, role=None):
|
|
203
171
|
"""Return ``True`` if ``array`` is ``True`` for all members of the entity.
|
|
204
172
|
|
|
205
173
|
``array`` must have the dimension of the number of persons in the simulation
|
|
206
174
|
|
|
207
175
|
If ``role`` is provided, only the entity member with the given role are taken into account.
|
|
208
176
|
|
|
209
|
-
If ``condition`` is provided (a boolean array of the same size as the
|
|
210
|
-
person population), only members for whom the condition is ``True`` are
|
|
211
|
-
taken into account. ``role`` and ``condition`` can be combined.
|
|
212
|
-
|
|
213
177
|
Example:
|
|
214
178
|
>>> salaries = household.members(
|
|
215
179
|
... "salary", "2018-01"
|
|
@@ -223,21 +187,16 @@ class GroupPopulation(Population):
|
|
|
223
187
|
reducer=numpy.logical_and,
|
|
224
188
|
neutral_element=True,
|
|
225
189
|
role=role,
|
|
226
|
-
condition=condition,
|
|
227
190
|
)
|
|
228
191
|
|
|
229
192
|
@projectors.projectable
|
|
230
|
-
def max(self, array, role=None
|
|
193
|
+
def max(self, array, role=None):
|
|
231
194
|
"""Return the maximum value of ``array`` for the entity members.
|
|
232
195
|
|
|
233
196
|
``array`` must have the dimension of the number of persons in the simulation
|
|
234
197
|
|
|
235
198
|
If ``role`` is provided, only the entity member with the given role are taken into account.
|
|
236
199
|
|
|
237
|
-
If ``condition`` is provided (a boolean array of the same size as the
|
|
238
|
-
person population), only members for whom the condition is ``True`` are
|
|
239
|
-
taken into account. ``role`` and ``condition`` can be combined.
|
|
240
|
-
|
|
241
200
|
Example:
|
|
242
201
|
>>> salaries = household.members(
|
|
243
202
|
... "salary", "2018-01"
|
|
@@ -251,21 +210,16 @@ class GroupPopulation(Population):
|
|
|
251
210
|
reducer=numpy.maximum,
|
|
252
211
|
neutral_element=-numpy.inf,
|
|
253
212
|
role=role,
|
|
254
|
-
condition=condition,
|
|
255
213
|
)
|
|
256
214
|
|
|
257
215
|
@projectors.projectable
|
|
258
|
-
def min(self, array, role=None
|
|
216
|
+
def min(self, array, role=None):
|
|
259
217
|
"""Return the minimum value of ``array`` for the entity members.
|
|
260
218
|
|
|
261
219
|
``array`` must have the dimension of the number of persons in the simulation
|
|
262
220
|
|
|
263
221
|
If ``role`` is provided, only the entity member with the given role are taken into account.
|
|
264
222
|
|
|
265
|
-
If ``condition`` is provided (a boolean array of the same size as the
|
|
266
|
-
person population), only members for whom the condition is ``True`` are
|
|
267
|
-
taken into account. ``role`` and ``condition`` can be combined.
|
|
268
|
-
|
|
269
223
|
Example:
|
|
270
224
|
>>> salaries = household.members(
|
|
271
225
|
... "salary", "2018-01"
|
|
@@ -283,18 +237,13 @@ class GroupPopulation(Population):
|
|
|
283
237
|
reducer=numpy.minimum,
|
|
284
238
|
neutral_element=numpy.inf,
|
|
285
239
|
role=role,
|
|
286
|
-
condition=condition,
|
|
287
240
|
)
|
|
288
241
|
|
|
289
242
|
@projectors.projectable
|
|
290
|
-
def nb_persons(self, role=None
|
|
243
|
+
def nb_persons(self, role=None):
|
|
291
244
|
"""Returns the number of persons contained in the entity.
|
|
292
245
|
|
|
293
246
|
If ``role`` is provided, only the entity member with the given role are taken into account.
|
|
294
|
-
|
|
295
|
-
If ``condition`` is provided (a boolean array of the same size as the
|
|
296
|
-
person population), only members for whom the condition is ``True`` are
|
|
297
|
-
counted. ``role`` and ``condition`` can be combined.
|
|
298
247
|
"""
|
|
299
248
|
if role:
|
|
300
249
|
if role.subroles:
|
|
@@ -303,9 +252,7 @@ class GroupPopulation(Population):
|
|
|
303
252
|
)
|
|
304
253
|
else:
|
|
305
254
|
role_condition = self.members_role == role
|
|
306
|
-
return self.sum(role_condition
|
|
307
|
-
if condition is not None:
|
|
308
|
-
return self.sum(condition.astype(float)).astype(int)
|
|
255
|
+
return self.sum(role_condition)
|
|
309
256
|
return numpy.bincount(self.members_entity_id)
|
|
310
257
|
|
|
311
258
|
# Projection person -> entity
|
|
@@ -24,9 +24,6 @@ class Population(CorePopulation):
|
|
|
24
24
|
return result
|
|
25
25
|
|
|
26
26
|
def __getattr__(self, attribute: str) -> projectors.Projector:
|
|
27
|
-
if hasattr(self, "links") and attribute in self.links:
|
|
28
|
-
return self.links[attribute]
|
|
29
|
-
|
|
30
27
|
projector: projectors.Projector | None
|
|
31
28
|
projector = projectors.get_projector_from_shortcut(self, attribute)
|
|
32
29
|
|
|
@@ -114,10 +111,11 @@ class Population(CorePopulation):
|
|
|
114
111
|
|
|
115
112
|
"""
|
|
116
113
|
# If entity is for instance 'person.household', we get the reference entity 'household' behind the projector
|
|
117
|
-
|
|
118
|
-
entity
|
|
119
|
-
|
|
120
|
-
|
|
114
|
+
entity = (
|
|
115
|
+
entity
|
|
116
|
+
if not isinstance(entity, projectors.Projector)
|
|
117
|
+
else entity.reference_entity
|
|
118
|
+
)
|
|
121
119
|
|
|
122
120
|
positions = entity.members_position
|
|
123
121
|
biggest_entity_size = numpy.max(positions) + 1
|
|
@@ -117,7 +117,10 @@ class ReformExcelTemplateGenerator:
|
|
|
117
117
|
for threshold, val in zip(value.thresholds, threshold_values):
|
|
118
118
|
values.append((f"{name}.{threshold}", val))
|
|
119
119
|
else:
|
|
120
|
-
|
|
120
|
+
if isinstance(value, list):
|
|
121
|
+
values.append((name, ", ".join(value)))
|
|
122
|
+
else:
|
|
123
|
+
values.append((name, value))
|
|
121
124
|
return sorted(values, key=lambda v: v[0])
|
|
122
125
|
|
|
123
126
|
def parameter_data(self) -> list[tuple[str, float]]:
|
|
@@ -212,7 +215,16 @@ class ReformExcel(Reform):
|
|
|
212
215
|
)
|
|
213
216
|
)
|
|
214
217
|
else:
|
|
215
|
-
leaf.
|
|
218
|
+
if "unit" in leaf.metadata and leaf.metadata["unit"] == "list":
|
|
219
|
+
if value is None:
|
|
220
|
+
leaf.update(start=date_, value=[])
|
|
221
|
+
else:
|
|
222
|
+
values = [
|
|
223
|
+
p.replace(",", "").strip() for p in value.split(" ")
|
|
224
|
+
]
|
|
225
|
+
leaf.update(start=date_, value=[v for v in values if v])
|
|
226
|
+
else:
|
|
227
|
+
leaf.update(start=date_, value=value)
|
|
216
228
|
|
|
217
229
|
for leaf, threshold in params_with_thresholds.values():
|
|
218
230
|
sorted_brackets = [v[1] for v in sorted(threshold, key=lambda x: x[0])]
|
|
@@ -30,7 +30,6 @@ class _BuildDefaultSimulation:
|
|
|
30
30
|
... .add_count()
|
|
31
31
|
... .add_ids()
|
|
32
32
|
... .add_members_entity_id()
|
|
33
|
-
... .add_id_to_rownum()
|
|
34
33
|
... )
|
|
35
34
|
|
|
36
35
|
>>> builder.count
|
|
@@ -122,16 +121,6 @@ class _BuildDefaultSimulation:
|
|
|
122
121
|
|
|
123
122
|
return self
|
|
124
123
|
|
|
125
|
-
def add_id_to_rownum(self) -> Self:
|
|
126
|
-
"""Set identity id_to_rownum mapping on all populations.
|
|
127
|
-
|
|
128
|
-
For static simulations, each entity's permanent ID equals its row
|
|
129
|
-
position, so id_to_rownum is the identity: id_to_rownum[i] = i.
|
|
130
|
-
"""
|
|
131
|
-
for population in self.populations.values():
|
|
132
|
-
population._id_to_rownum = numpy.arange(self.count, dtype=numpy.intp)
|
|
133
|
-
return self
|
|
134
|
-
|
|
135
124
|
def add_members_entity_id(self) -> Self:
|
|
136
125
|
"""Add ???
|
|
137
126
|
|
|
@@ -47,7 +47,6 @@ class Simulation:
|
|
|
47
47
|
self.persons = self.populations[tax_benefit_system.person_entity.key]
|
|
48
48
|
self.link_to_entities_instances()
|
|
49
49
|
self.create_shortcuts()
|
|
50
|
-
self._resolve_links()
|
|
51
50
|
|
|
52
51
|
self.invalidated_caches = set()
|
|
53
52
|
|
|
@@ -83,53 +82,6 @@ class Simulation:
|
|
|
83
82
|
# create shortcut simulation.person and simulation.household (for instance)
|
|
84
83
|
setattr(self, population.entity.key, population)
|
|
85
84
|
|
|
86
|
-
def _resolve_links(self) -> None:
|
|
87
|
-
"""Attach and resolve all links declared on every entity.
|
|
88
|
-
|
|
89
|
-
This also auto-generates implicit links for each GroupEntity:
|
|
90
|
-
- A Many2One link on the person entity (e.g. ``person.household``)
|
|
91
|
-
- A One2Many link on the group entity (e.g. ``household.members``)
|
|
92
|
-
|
|
93
|
-
This is called once at ``__init__`` time, after
|
|
94
|
-
``link_to_entities_instances`` and ``create_shortcuts``.
|
|
95
|
-
"""
|
|
96
|
-
from openfisca_core.links.implicit import (
|
|
97
|
-
ImplicitMany2OneLink,
|
|
98
|
-
ImplicitOne2ManyLink,
|
|
99
|
-
)
|
|
100
|
-
from openfisca_core.populations.group_population import GroupPopulation
|
|
101
|
-
|
|
102
|
-
person_entity = self.persons.entity
|
|
103
|
-
|
|
104
|
-
# Auto-generate implicit links
|
|
105
|
-
for population in self.populations.values():
|
|
106
|
-
if not isinstance(population, GroupPopulation):
|
|
107
|
-
continue
|
|
108
|
-
group_key = population.entity.key
|
|
109
|
-
|
|
110
|
-
# person -> group (Many2One)
|
|
111
|
-
if not person_entity.get_link(group_key):
|
|
112
|
-
m2o = ImplicitMany2OneLink(group_key)
|
|
113
|
-
person_entity.add_link(m2o)
|
|
114
|
-
|
|
115
|
-
# group -> persons (One2Many)
|
|
116
|
-
o2m_name = person_entity.plural
|
|
117
|
-
if not population.entity.get_link(o2m_name):
|
|
118
|
-
o2m = ImplicitOne2ManyLink(o2m_name, group_key)
|
|
119
|
-
population.entity.add_link(o2m)
|
|
120
|
-
|
|
121
|
-
from copy import copy
|
|
122
|
-
|
|
123
|
-
# Attach and resolve all links, making a simulation-local copy
|
|
124
|
-
for _key, population in self.populations.items():
|
|
125
|
-
entity = population.entity
|
|
126
|
-
population.links = {}
|
|
127
|
-
for name, link in entity.links.items():
|
|
128
|
-
bound_link = copy(link)
|
|
129
|
-
bound_link.attach(population)
|
|
130
|
-
bound_link.resolve(self.populations)
|
|
131
|
-
population.links[name] = bound_link
|
|
132
|
-
|
|
133
85
|
@property
|
|
134
86
|
def data_storage_dir(self):
|
|
135
87
|
"""Temporary folder used to store intermediate calculation data in case the memory is saturated."""
|