Skip to content

Entity links: role-based accessors, rank, nth, and dynamic population period index#1364

Merged
benjello merged 4 commits intomasterfrom
feat/entity-links-next
Mar 4, 2026
Merged

Entity links: role-based accessors, rank, nth, and dynamic population period index#1364
benjello merged 4 commits intomasterfrom
feat/entity-links-next

Conversation

@benjello
Copy link
Copy Markdown
Member

Summary

This PR extends the entity links API with role-based and positional accessors, improves role handling when roles are stored as Role objects, and adds optional dynamic-population period-index helpers on CorePopulation.

Type of change

  • Minor bump: New helpers and backward-compatible behaviour changes.

Changes

Entity links

  • has_role(role_value)
    Now supports both raw values (int/string) and Role objects. When the role array has dtype=object, comparison uses each element’s .key so that link.has_role("parent") and link.get_by_role(..., role_value="foo") work regardless of whether roles are stored as keys or Role instances. Applied in Many2OneLink and ImplicitOne2ManyLink._apply_filters.

  • Many2OneLink.get_by_role(variable_name, period, role_value=...)
    Returns the target variable only for source members that have the given role; other rows get the variable’s default (e.g. 0). Uses the existing role_field and the updated has_role().

  • Many2OneLink.rank(variable_name, period)
    Ranks each source member within its group (same target entity) by the value of a variable on the source population. Delegates to Population.get_rank. Also exposed on the chained link getter so persons.links["mother"].household.rank("age", period) works.

  • One2ManyLink.nth(n, variable_name, period, role=..., condition=...)
    Returns the value of the n-th target member for each source entity (same parameters as sum plus n). Order is that of the underlying population arrays.

  • One2ManyLink.get_by_role(variable_name, period, role_value, condition=...)
    Returns the value of the target that has the given role per source (using role_field on the target). If multiple targets share the same role, the last encountered value is used (aligned with GroupPopulation.value_from_person). Requires a non-None role_field.

  • ImplicitOne2ManyLink

    • Role filtering in _apply_filters now supports object arrays of Role instances (compare by .key).
    • Overrides get_by_role to use _source_population.members_role instead of a target role_field, with the same “last value wins” semantics.

Populations

  • CorePopulation
    • New optional attributes for dynamic population support: _dynamic, _permanent_ids, _id_to_rownum, _next_id, _period_index.
    • snapshot_period(period): stores the current _id_to_rownum for the given period (creates a default identity mapping if none exists).
    • get_period_id_to_rownum(period): returns the stored id→rownum mapping for the period, or None if not found.

Tests and cleanup

  • openfisca_core/links/tests/test_many2one.py

    • test_many2one_role_helpers: now also checks get_by_role("rent", "2024", role_value=10).
    • New test_many2one_rank: checks link.rank("age", "2024") and the chained getter persons.links["mother"].household.rank(...).
  • tests/core/parameters_date_indexing/test_date_indexing.py

    • Removed unused from openfisca_core.model_api import *.
  • tests/core/test_link_accessors.py (new)

    • Tests for nth and get_by_role on implicit one-to-many and many-to-one links (e.g. household ↔ persons with parent/child roles).

Impacted areas

  • openfisca_core/links/implicit.py
  • openfisca_core/links/many2one.py
  • openfisca_core/links/one2many.py
  • openfisca_core/populations/_core_population.py
  • Link and population tests; one unrelated test cleanup.

Breaking changes

None. New methods and optional attributes only; existing call sites remain valid. Role comparison now accepts both raw values and Role objects when the role array is an object array.

Checklist

  • Clear text explanation of changes (this description).
  • Changelog entry in CHANGELOG.md (to be added if maintainers require it for this release).

benjello and others added 2 commits March 4, 2026 11:35
Populate `_id_to_rownum` with the identity mapping `[0, 1, ..., n-1]`
for all static simulations built via `build_default_simulation` and
`build_from_dict` / `build_from_entities`, preparing the ground for
dynamic populations (LIAM2-inspired).

- Add `_BuildDefaultSimulation.add_id_to_rownum()` and chain it in
  `build_default_simulation()`
- Set `population._id_to_rownum` in `finalize_variables_init()` after
  `count` and `ids` are known
- Add tests: basic builder paths + edge cases (empty, single, group TBS,
  dtype check, index round-trip)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@benjello benjello force-pushed the feat/entity-links-next branch from 062a764 to c1f28ee Compare March 4, 2026 10:36
@benjello benjello enabled auto-merge March 4, 2026 10:42
@benjello benjello merged commit 06d4097 into master Mar 4, 2026
24 checks passed
@benjello benjello deleted the feat/entity-links-next branch March 4, 2026 10:47
@MattiSG
Copy link
Copy Markdown
Member

MattiSG commented Mar 6, 2026

This changeset added documentation that is partially in French and is not located in the doc website but instead in a new docs/implementation folder. @benjello @eraviart can you please explain what is your intention with documentation management? Would you like to oversee transitioning the documentation from the website to this new location?

See #1366 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants