Repository navigation
Conversation
|
Hold (Max, d1054, 2026-10-07). This PR merges only after the dataset stops routing non-investment interest into The §163(d) / Form 4952 logic is correct, and review confirmed the arithmetic. The problem is the default-dataset input:
The fix, following Max's principle that inputs should be as close to leaf facts as possible:
The open review item (dependents' amounts reaching the 28%-rate and §1250 helpers) is being fixed in the meantime. |
|
The three failing checks on this head ( What breaks. This PR makes Evidence from this PR's CI (run 37627884656):
Tested patch. Applied to a detached checkout of
Results on Patch (git diff against a0d4dce)diff --git a/policyengine_us/parameters/gov/irs/deductions/itemized/limitation/excluded_deductions.yaml b/policyengine_us/parameters/gov/irs/deductions/itemized/limitation/excluded_deductions.yaml
index 96e642fd5d..21198c4543 100644
--- a/policyengine_us/parameters/gov/irs/deductions/itemized/limitation/excluded_deductions.yaml
+++ b/policyengine_us/parameters/gov/irs/deductions/itemized/limitation/excluded_deductions.yaml
@@ -3,8 +3,8 @@ values:
# Section 68(c) excludes the medical expense deduction (section 213), any
# deduction for investment interest (section 163(d)), and casualty, theft
# and wagering losses (section 165(c)(2), (c)(3) and (d)).
- # investment_interest_expense is the investment interest that
- # interest_deduction includes through non_mortgage_interest.
+ # investment_interest_expense_deduction is the Form 4952 line 8 amount
+ # (limited under section 163(d)) that interest_deduction includes.
# wagering_losses_deduction is deducted outside
# total_itemized_taxable_income_deductions, so it never enters the base.
# The 2017 worksheet also removes qualified disaster-relief contributions
@@ -12,7 +12,7 @@ values:
# not modeled.
2009-01-01:
- medical_expense_deduction
- - investment_interest_expense
+ - investment_interest_expense_deduction
- casualty_loss_deduction
metadata:
unit: list
diff --git a/policyengine_us/tests/core/test_itemized_deduction_limitation_properties.py b/policyengine_us/tests/core/test_itemized_deduction_limitation_properties.py
index 36c428c709..4bcf5dbe83 100644
--- a/policyengine_us/tests/core/test_itemized_deduction_limitation_properties.py
+++ b/policyengine_us/tests/core/test_itemized_deduction_limitation_properties.py
@@ -57,15 +57,20 @@ APPLICABLE_AMOUNTS = {
SEPARATE_AMOUNTS = {2015: 154_950, 2016: 155_650, 2017: 156_900}
# Components of total_itemized_taxable_income_deductions, split by whether
-# section 68(c) excludes them. Mortgage and investment interest are person
-# inputs that interest_deduction adds up.
-EXCLUDED = ("medical_expense_deduction", "casualty_loss_deduction")
-EXCLUDED_PERSON = ("investment_interest_expense",)
+# section 68(c) excludes them. interest_deduction adds mortgage interest (a
+# person input) and the Form 4952 investment interest deduction.
+EXCLUDED = (
+ "medical_expense_deduction",
+ "casualty_loss_deduction",
+ "investment_interest_expense_deduction",
+)
+EXCLUDED_PERSON = ()
OTHER = ("charitable_deduction", "salt_deduction", "misc_deduction")
OTHER_PERSON = ("deductible_mortgage_interest",)
COMPONENTS = EXCLUDED + OTHER
PERSON_COMPONENTS = EXCLUDED_PERSON + OTHER_PERSON
ALL_COMPONENTS = COMPONENTS + PERSON_COMPONENTS
+PAID = "investment_interest_expense"
# PolicyEngine computes in float32, whose spacing near $1 million is 6 cents,
# so tolerances scale with the size of the operands (not of the result: the
# 80 percent base is a difference of two such sums).
@@ -102,6 +107,12 @@ def _draw(seed):
for name in ALL_COMPONENTS:
amount = np.round(rng.uniform(0, 150_000, N), 2)
units[name] = np.where(rng.random(N) < 0.5, 0, amount)
+ # Investment interest paid can exceed the Form 4952 deduction; only the
+ # deduction is in the total and in the section 68(c) exclusion.
+ disallowed = np.round(rng.uniform(0, 50_000, N), 2)
+ units[PAID] = units["investment_interest_expense_deduction"] + np.where(
+ rng.random(N) < 0.5, 0, disallowed
+ )
# Every fifth unit has only excluded deductions (worksheet line 3 "No").
only_excluded = np.arange(N) % 5 == 0
for name in OTHER + OTHER_PERSON:
@@ -115,7 +126,7 @@ def _simulate(year, units, reform=None):
for i in range(N):
person = f"p{i}"
people[person] = {"age": {period: 50}}
- for name in PERSON_COMPONENTS:
+ for name in PERSON_COMPONENTS + (PAID,):
people[person][name] = {period: float(units[name][i])}
tax_units[f"t{i}"] = {
"members": [person],
diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/itemizing/itemized_taxable_income_deductions_reduction.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/itemizing/itemized_taxable_income_deductions_reduction.yaml
index 965f6ed655..33c73c8c16 100644
--- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/itemizing/itemized_taxable_income_deductions_reduction.yaml
+++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/itemizing/itemized_taxable_income_deductions_reduction.yaml
@@ -73,6 +73,8 @@
members: [head, spouse]
adjusted_gross_income: 600_000
filing_status: JOINT
+ # Schedule A line 14 is the Form 4952 deduction, supplied directly.
+ investment_interest_expense_deduction: 30_000
casualty_loss_deduction: 20_000
charitable_deduction: 10_000
medical_expense_deduction: 0The OBBB branch is unaffected: it uses the total with no exclusion list. |
What and why
Apply the §163(d) investment-interest deduction limit through Form 4952. Schedule A replaces current raw investment interest with allowed line 8, preserving the existing aggregate interest and mortgage conventions. Explicit prior-year disallowed interest is available on line 2 and the unused amount is exposed on line 7; carryover is not automatically propagated between simulations.
All Form 4952 income, gains, elections, current debt and expenses belong to the filer and spouse. The deduction and Schedule D/§911 calculations share the effective capped election and investment gains, retaining independent Schedule D gain arithmetic and default gain-first allocation. Montana spouses compute separate limits before 2024. California, New York and Virginia substitutions use allowed federal interest; California's high-AGI exclusion retains the federal-form approach. Federal miscellaneous investment fees remain disallowed after 2017, including 2026.
Sources: 26 USC 163(d), IRS Form 4952, 26 USC 67(h), and 2023 Montana instructions, printed page 31.
Test plan
make formatand changelog fragment.Seven source-overlay regressions fail with prior formulas and pass with final formulas (not a full old-checkout replay). The federal example changes tax from $20,447 to $21,647; Montana separate deductions change from [150,450] to [300,0]. The new AR fixture initially failed entity parsing; correcting its input placement preserved the expected deduction. Local property setup-inclusive time was 158.62s and peak memory ~1.02GiB; Linux CI resource evidence is pending. The federal assertions passed even though its external timing wrapper hit a sandbox restriction afterward.
The saved build's 21 federal YAML cases, six property cases and 60 serial targets passed on an older baseline; these are historical evidence, not validation of this head.
Impact and data limitation
Hub microsimulation (2026-10-07): merge base
dd9cb3f683against this head0522816764, onpopulace_us_2024.h5@populace-us-2024-spm-20260909. It ran under the shared heavy lock, one run per side, compared record by record.itemized_taxable_income_deductions)investment_interest_expense(input, unchanged)Read these totals together with the data limitation below. The law change is the Form 4952 limit: the deduction cannot exceed net investment income, with the excess carried forward. Main deducted the dataset's entire $52bn of
investment_interest_expensewith no limit. The default dataset's value is an imputed Schedule A residual, not observed interest paid, so most of the ~$30bn now disallowed is imputed amount that has no investment income behind it. The direction is the statute's. The size mostly measures how far that imputed field exceeds modeled net investment income, and the true revenue effect is likely much smaller. A data follow-up should re-derive investment interest from observed sources before anyone cites the aggregate.The default
populace-us-2024-spm-20260909input is an imputed Schedule A deduction residual, not observed raw investment interest paid. Its parent build assigns deductible points, mortgage-insurance premiums and investment-interest deductions to this residual. The old $53.68B input screen and illustrative +$5.4–9.4B estimate are therefore not a validated §163(d) revenue score. A pinned comparison can show mechanical model changes; economic interpretation needs corrected data concepts or a sensitivity design from the data owners.Coverage limits: no alternative “Elec.” allocation; no investment/noninvestment capital-asset split; missing distinct annuity/royalty/K-1 income; no Montana-specific income adjustments or per-spouse carryover inputs. Federal AMT's second Form 4952 and NIIT Form 8960 line 9a remain explicit follow-ups. California’s own FTB 3526 income/election/carryover calculation remains assigned to the separate California work. This PR precedes that work because both touch its pre-limitation formula and exclusion parameter.
axiom: 26 USC 163(d) TheAxiomFoundation/rulespec-us#1558 queued | 26 USC 67(a)-(b); 2017 Form4952 line5 TheAxiomFoundation/rulespec-us#1559 queued | MT Form2 line10 (TY2023), WorksheetA (TY2024) TheAxiomFoundation/rulespec-us#1560 queued | CA ScheduleCA line9 TheAxiomFoundation/rulespec-us#1561 queued | CA ScheduleCA itemized worksheet line2 TheAxiomFoundation/rulespec-us#1562 queued | NY IT196 lines14,24–28,40 TheAxiomFoundation/rulespec-us#1563 queued | VA2021 ScheduleA PartA TheAxiomFoundation/rulespec-us#1564 queued | 26 USC 1(h)(2),(11)(D)(i) TheAxiomFoundation/rulespec-us#1488 queued | 26 USC 1(h)(4),(6) TheAxiomFoundation/rulespec-us#1499 queued | 26 USC 911(f) TheAxiomFoundation/rulespec-us#1478 queued | 26 USC 68(c)(2) historical TheAxiomFoundation/rulespec-us#1449 queued | Ark.Code 26-51-437 TheAxiomFoundation/rulespec-us#1411 queued | 26 USC 67(h) encoded-correct (us/statutes/26/67/h.yaml#individual_miscellaneous_itemized_deduction_disallowed_in_2026)
Recovery and detailed evidence: fix report, dataset lineage and exact hub impact request. Code head:
05228167642374813d0afbdf4c0aa2cf559de7aa; main merged atdd9cb3f6839.