A reform applied after holders exist leaves them on the old variable, so a variable's default depends on who else is in the simulation

Đang mở
#644 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 2 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
35/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
python
Lĩnh vực
backend

Hướng nghiên cứu

Start in simulations/simulation.py at apply_reform (around line 927) and _invalidate_all_caches, then populations/population.py at get_holder (around line 169) to see why existing holders keep their old variable. Compare the holder.default_array() path with the np.where(mask, array, variable.default_value) path in _calculate. Done means one reform gives the same result whether every row or only some rows fail defined_for, with a regression test added.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Problem

Simulation.apply_reform replaces variables on the tax-benefit system but leaves any holder that already exists pointing at the old variable. Holder.default_array() then returns the old default, while Simulation._calculate reads the new one, so one calculation can use both.

We traced this in policyengine-core 3.33.12 and confirmed the lines below at 3.34.1, in simulations/simulation.py unless noted:

  • apply_reform (line 927) runs the reform, then _invalidate_all_caches (line 940). That clears stored values and keeps each Holder.variable.
  • Population.get_holder (populations/population.py, line 169) returns an existing holder unchanged.
  • When every row fails defined_for, _calculate returns holder.default_array() (line 1779), which reads the holder's old variable.
  • When only some rows fail, _calculate reaches np.where(mask, array, variable.default_value) (line 1933), which reads the system's new variable.

policyengine-uk exposes this because Simulation.get_known_variables creates a holder for every variable before its after-data reform runs.

Example

We ran policyengine-uk 2da212abc on Core 3.33.12 with a reform that gives pension_credit_entitlement a default of £2,000, applied after data load. A man aged 40 fails that variable's defined_for.

Simulation The man's pension_credit_entitlement
Alone, reform applied after data load £0
With two men aged 63, reform applied after data load £2,000
Alone, reform applied before data load £2,000
Alone, reform applied after data load, then _bind_to_tax_benefit_system() £2,000

His result depends on who else the simulation holds, and the system's default reads £2,000 throughout.

What to change

  • Rebind holders after a reform: have apply_reform call _bind_to_tax_benefit_system() (line 1307), or have get_holder replace a holder whose variable has left the system.
  • Add a test: apply a reform that changes a defined_for variable's default after a holder exists, and assert one result for a population where every row fails and for a population where some do.

Related: #612, #632.

🤖 Generated with Claude Code

Ngôn ngữ chính
Python
Star
22
Fork
30
Merge trung bình
4 ngày 20 giờ
Pull request đã merge (30 ngày)
26

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của PolicyEngine/policyengine-core

Tất cả issue của PolicyEngine/policyengine-core

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.