Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

build_from_populations keeps _user_input_keys for the holders it replaces

Open Beginner friendly
#605 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 2 days

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
76/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
python
Domain
backend, testing

Research direction

Start in Simulation.build_from_populations, where the issue says populations and holders are replaced, and compare its handling of _user_input_keys with subsample. Add a regression test that sets an input, rebuilds without it, then calculates that period and checks the record no longer lists the input. The payload does not name a test file, so locate the existing simulation rebuild tests first.

Written by the indexing model from the issue text.

Description

Simulation.build_from_populations replaces every population, and so every holder, but keeps Simulation._user_input_keys. After a rebuild, the record of set_input values still lists (variable, branch, period) entries for values the new holders never received. This is the same drift as #559, through a path #561 did not cover. #561 keeps the record in step for Holder.delete_arrays and clone, and subsample resets it before rebuilding.

Reproduction (core only, 3.32.27; same code path in 3.33.0)

from policyengine_core import periods
from policyengine_core.country_template import CountryTaxBenefitSystem
from policyengine_core.simulations import SimulationBuilder

FEB = periods.period("2018-02")
tbs = CountryTaxBenefitSystem()
households = {"h": {"parents": ["a"]}}
sim = SimulationBuilder().build_from_entities(
    tbs,
    {
        "persons": {"a": {"salary": {"2018-02": 1000}, "disposable_income": {"2018-02": 5000}}},
        "households": households,
    },
)
# Rebuild the same simulation with new populations and no disposable_income input.
sim.build_from_populations(tbs.instantiate_entities())
SimulationBuilder().build_from_entities(
    tbs,
    {"persons": {"a": {"salary": {"2018-01": 1000, "2018-02": 1000}}}, "households": households},
    sim,
)
print(sim.get_holder("disposable_income")._get_array_from_storage(FEB))  # None
print(("disposable_income", "default", FEB) in sim._user_input_keys)       # True
sim.calculate("disposable_income", "2018-02")  # the formula value is cached under that record

Output on 3.32.27:

after rebuild: stored 2018-02: None
after rebuild: record has disposable_income 2018-02: True
calculated disposable_income 2018-02: [1430.0]
to_input_dataframe exports disposable_income: False ['salary__2018-01', 'salary__2018-02']

to_input_dataframe does not export the stale entry. Code that reads the record directly does see it, though. PolicyEngine-UK's supplied-input helpers (policyengine_uk/utils/supplied_inputs.py) treat a recorded (variable, branch, period) with a stored value as supplied. After a rebuild, a value carried over or calculated into that period passes as a supplied input. PE-UK reproduced this for a Class 4 trading loss: profits came out at £20,000 and NIC at £445.80, where they should be £30,000 and £1,045.80. PE-UK keeps a sweep in Simulation.calculate for this case (PolicyEngine/policyengine-uk#2260); it can go once core fixes the rebuild.

PE-UK's Simulation.build_from_situation calls build_from_populations(tax_benefit_system.instantiate_entities()) and then SimulationBuilder.build_from_dict(..., self). Its property tests rebuild cloned template simulations this way (test_state_pension_age.py, test_state_pension_credit_qualifying_age.py).

Suggested fix

In build_from_populations, reset _user_input_keys to an empty set, as subsample does. Every holder is new at that point, so none of the old entries can name a stored value. Also reset _user_input_contexts if no set_input can be running across a rebuild. Add a regression test: set an input, rebuild without it, calculate that period, and check that the record no longer names it.

Dominant language
Python
Stars
22
Forks
30
Avg merge
2d 19h
Merged PRs (30d)
25

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from PolicyEngine/policyengine-core

All issues in PolicyEngine/policyengine-core

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.