From c1c0d3f4d1e1155d1d229c3e26287703602ff338 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 08:44:30 -0400 Subject: [PATCH 1/5] Limit Alabama, North Carolina and Hawaii mortgage interest to the amounts their laws allow Alabama (Code 40-18-15(a)(2)) and North Carolina (G.S. 105-153.5(a)(2)b) allow only the mortgage interest federal law allows, so they now read deductible_mortgage_interest, after the 26 U.S.C. 163(h)(3) acquisition-debt limits, instead of uncapped mortgage_interest. Hawaii makes IRC 163(h)(3)(F) inoperative (HRS 235-2.4(j)(3), (i)(3) from 2026 under Act 35), so it keeps the pre-TCJA limits for debt incurred at any time: $1,000,000 of acquisition debt plus up to $100,000 of home equity debt ($500,000 / $50,000 separate), per 2017 Publication 936 Table 1. The new hi_mortgage_interest_deduction applies them to gross interest, reusing the federal vintage split for grandfathered debt. With $40,000 of interest on $1.5M of 2020 debt, main allowed $40,000 in all three states; now AL and NC allow the federal $20,000 and HI $29,333.33. Co-Authored-By: Claude Opus 5.5 --- ...ate-mortgage-interest-federal-cap.fixed.md | 1 + .../interest/home_acquisition_debt_limit.yaml | 31 +++ .../interest/home_equity_debt_limit.yaml | 31 +++ .../test_hi_mortgage_debt_limit_invariants.py | 163 ++++++++++++++++ .../itemized/al_interest_deduction.yaml | 151 +++++++++++++++ .../hi_mortgage_interest_deduction.yaml | 176 ++++++++++++++++++ .../itemized/nc_claim_of_right_deduction.yaml | 2 +- .../deductions/nc_itemized_deductions.yaml | 10 +- ...ge_and_property_tax_before_limitation.yaml | 90 ++++++++- ...c_mortgage_and_property_tax_deduction.yaml | 51 +++++ .../itemized/al_investment_interest.py | 22 ++- .../itemized/hi_interest_deduction.py | 2 +- .../hi_mortgage_interest_deduction.py | 101 ++++++++++ ...gage_and_property_tax_before_limitation.py | 10 +- 14 files changed, 824 insertions(+), 17 deletions(-) create mode 100644 changelog.d/state-mortgage-interest-federal-cap.fixed.md create mode 100644 policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/interest/home_acquisition_debt_limit.yaml create mode 100644 policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/interest/home_equity_debt_limit.yaml create mode 100644 policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py create mode 100644 policyengine_us/tests/policy/baseline/gov/states/al/tax/income/deductions/itemized/al_interest_deduction.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml create mode 100644 policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.py diff --git a/changelog.d/state-mortgage-interest-federal-cap.fixed.md b/changelog.d/state-mortgage-interest-federal-cap.fixed.md new file mode 100644 index 00000000000..c99e111f41a --- /dev/null +++ b/changelog.d/state-mortgage-interest-federal-cap.fixed.md @@ -0,0 +1 @@ +Limit Alabama's and North Carolina's mortgage interest deductions to the federally allowed amount after the 26 U.S.C. 163(h)(3) acquisition-debt limits, and Hawaii's to its own pre-TCJA limits ($1,000,000 of acquisition debt plus $100,000 of home equity debt) under HRS 235-2.4, instead of all interest paid. diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/interest/home_acquisition_debt_limit.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/interest/home_acquisition_debt_limit.yaml new file mode 100644 index 00000000000..7d5104076b4 --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/interest/home_acquisition_debt_limit.yaml @@ -0,0 +1,31 @@ +description: Hawaii limits the home acquisition debt on which mortgage interest is deductible to this amount, reduced by any grandfathered debt, based on filing status. +SINGLE: + 2018-01-01: 1_000_000 +JOINT: + 2018-01-01: 1_000_000 +SEPARATE: + 2018-01-01: 500_000 +HEAD_OF_HOUSEHOLD: + 2018-01-01: 1_000_000 +SURVIVING_SPOUSE: + 2018-01-01: 1_000_000 +metadata: + propagate_metadata_to_children: true + breakdown: filing_status + unit: currency-USD + period: year + label: Hawaii home acquisition debt limit + reference: + # HRS 235-2.4 makes IRC 163(h)(3)(F), which lowers the limit to $750,000 + # for later debt, inoperative, so the IRC 163(h)(3)(B)(ii) limit applies + # whenever the debt was incurred. + - title: Hawaii Revised Statutes 235-2.4(j)(3) + href: https://data.capitol.hawaii.gov/hrscurrent/Vol04_Ch0201-0257/HRS0235/HRS_0235-0002_0004.htm + - title: Act 35, SLH 2026 (HB2329 CD1), HRS 235-2.4(i)(3), for taxable years beginning after December 31, 2025 + href: https://data.capitol.hawaii.gov/sessions/session2026/bills/HB2329_CD1_.pdf#page=20 + - title: 2018 Hawaii N-11 Instructions, Line 21c (Act 27, SLH 2018 does not lower the limit) + href: https://files.hawaii.gov/tax/forms/2018/n11ins.pdf#page=19 + - title: 2025 Hawaii N-11 Instructions, Line 21c + href: https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=17 + - title: 2017 IRS Publication 936, Table 1, line 3 + href: https://www.irs.gov/pub/irs-prior/p936--2017.pdf#page=11 diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/interest/home_equity_debt_limit.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/interest/home_equity_debt_limit.yaml new file mode 100644 index 00000000000..0cadb08f61d --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/interest/home_equity_debt_limit.yaml @@ -0,0 +1,31 @@ +description: Hawaii limits the home equity debt on which mortgage interest is deductible to this amount, based on filing status. +SINGLE: + 2018-01-01: 100_000 +JOINT: + 2018-01-01: 100_000 +SEPARATE: + 2018-01-01: 50_000 +HEAD_OF_HOUSEHOLD: + 2018-01-01: 100_000 +SURVIVING_SPOUSE: + 2018-01-01: 100_000 +metadata: + propagate_metadata_to_children: true + breakdown: filing_status + unit: currency-USD + period: year + label: Hawaii home equity debt limit + reference: + # HRS 235-2.4 makes IRC 163(h)(3)(F), which suspends the home equity + # interest deduction, inoperative, so the IRC 163(h)(3)(C)(ii) limit + # applies. + - title: Hawaii Revised Statutes 235-2.4(j)(3) + href: https://data.capitol.hawaii.gov/hrscurrent/Vol04_Ch0201-0257/HRS0235/HRS_0235-0002_0004.htm + - title: Act 35, SLH 2026 (HB2329 CD1), HRS 235-2.4(i)(3), for taxable years beginning after December 31, 2025 + href: https://data.capitol.hawaii.gov/sessions/session2026/bills/HB2329_CD1_.pdf#page=20 + - title: 2018 Hawaii N-11 Instructions, Line 21c (Act 27, SLH 2018 does not suspend home equity interest) + href: https://files.hawaii.gov/tax/forms/2018/n11ins.pdf#page=19 + - title: 2025 Hawaii N-11 Instructions, Line 21c + href: https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=17 + - title: "2017 IRS Publication 936, Home equity debt limit and Table 1, line 7 #page 10-11" + href: https://www.irs.gov/pub/irs-prior/p936--2017.pdf#page=10 diff --git a/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py b/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py new file mode 100644 index 00000000000..11ec42e899a --- /dev/null +++ b/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py @@ -0,0 +1,163 @@ +"""Invariants for Hawaii's mortgage debt limits. + +HRS 235-2.4(j)(3) (235-2.4(i)(3) from 2026) makes IRC 163(h)(3)(F) +inoperative, so Hawaii keeps the pre-TCJA limits for debt incurred at any +time. IRS Publication 936 (2017), Table 1 (PDF page 11), computes them: +grandfathered debt, incurred on or before October 13, 1987, has no dollar +limit and reduces the $1,000,000 acquisition-debt limit ($500,000 married +filing separately); up to $100,000 ($50,000) of debt above that limit counts +as home equity debt (Figure A, note 3, PDF page 3). + +Hypothesis draws pairs of loans around every cutoff and limit and checks +_hi_qualified_loan_limit against two independent restatements, one line by +line from Table 1 and one in the statute's order, and against the federal +helper: + +1. Differential: the helper equals both restatements. +2. Bounds: grandfathered debt <= Hawaii limit <= total debt, and the Hawaii + limit is at least the federal limit for the same loans, so Hawaii never + allows less interest than federal law and never more than was paid. +3. Order: swapping the two loans never changes the limit. +4. Vintage: Hawaii has one limit for debt incurred after October 13, 1987, so + moving a loan between 2017 and 2018 never changes the limit, while moving + it into the grandfathered years never lowers it. +5. Monotone: a larger balance never lowers the limit. + +These are for-all properties of the helper, which YAML cannot state. The +policy outcomes and interest proration are checked in +hi_mortgage_interest_deduction.yaml. +""" + +import numpy as np +from hypothesis import given, settings +from hypothesis import strategies as st + +from policyengine_us.variables.gov.states.hi.tax.income.deductions.itemized.hi_mortgage_interest_deduction import ( + _hi_qualified_loan_limit, +) +from policyengine_us.variables.household.expense.tax_unit.mortgage_interest_structure import ( + _debt_by_vintage, + _qualified_loan_limit, +) + +GRANDFATHERED_YEAR = 1987 +PRE_TCJA_YEAR = 2017 +# Filing status -> ((Hawaii acquisition limit, Hawaii home equity limit), +# (federal limit for debt from 2017 or earlier, federal limit for later debt)). +LIMITS = { + "other": ((1_000_000, 100_000), (1_000_000, 750_000)), + "separate": ((500_000, 50_000), (500_000, 375_000)), +} +TOLERANCE = 0.01 + +YEARS = (0, 1960, 1986, 1987, 1988, 2000, 2016, 2017, 2018, 2025) +BOUNDARY_BALANCES = ( + 0, + 1, + 50_000, + 100_000, + 375_000, + 500_000, + 550_000, + 750_000, + 1_000_000, + 1_100_000, +) +balances = st.one_of( + st.sampled_from(BOUNDARY_BALANCES), + st.integers(0, 3_000_000), +).map(float) +loans = st.tuples(balances, st.sampled_from(YEARS)) + + +def grandfathered(year): + return 0 < year <= GRANDFATHERED_YEAR + + +def split(loan_list): + old = sum(balance for balance, year in loan_list if grandfathered(year)) + other = sum(balance for balance, year in loan_list if not grandfathered(year)) + return old, other + + +def table_1(loan_list, limits): + """Publication 936 (2017) Table 1, lines 1-8.""" + acquisition_limit, home_equity_limit = limits + line_1, line_2 = split(loan_list) + line_3 = acquisition_limit + line_4 = max(line_1, line_3) + line_5 = line_1 + line_2 + line_6 = min(line_4, line_5) + # Debt above the acquisition limit, up to the home equity limit. + line_7 = min(line_5 - line_6, home_equity_limit) + return line_6 + line_7 + + +def statute(loan_list, limits): + """26 U.S.C. 163(h)(3)(B)(ii), (C)(ii) and (D) without (F), in order.""" + acquisition_limit, home_equity_limit = limits + old, other = split(loan_list) + acquisition = min(other, max(0.0, acquisition_limit - old)) + home_equity = min(home_equity_limit, other - acquisition) + return old + acquisition + home_equity + + +def model_limit(loan_list, limits): + debt = _debt_by_vintage( + [np.array([balance]) for balance, _ in loan_list], + [np.array([year]) for _, year in loan_list], + GRANDFATHERED_YEAR, + PRE_TCJA_YEAR, + ) + old, pre, post = debt + return float(_hi_qualified_loan_limit(old, pre + post, *limits)[0]) + + +def federal_limit(loan_list, limits): + debt = _debt_by_vintage( + [np.array([balance]) for balance, _ in loan_list], + [np.array([year]) for _, year in loan_list], + GRANDFATHERED_YEAR, + PRE_TCJA_YEAR, + ) + return float(_qualified_loan_limit(*debt, *limits)[0]) + + +SETTINGS = dict(max_examples=400, deadline=None, derandomize=True) + + +@settings(**SETTINGS) +@given(first=loans, second=loans, status=st.sampled_from(sorted(LIMITS))) +def test_hawaii_qualified_loan_limit_properties(first, second, status): + hawaii_limits, federal_limits = LIMITS[status] + pair = [first, second] + limit = model_limit(pair, hawaii_limits) + old, other = split(pair) + total = old + other + + # 1. Differential. + assert abs(limit - table_1(pair, hawaii_limits)) <= TOLERANCE + assert abs(limit - statute(pair, hawaii_limits)) <= TOLERANCE + + # 2. Bounds. + assert old - TOLERANCE <= limit <= total + TOLERANCE + assert limit >= federal_limit(pair, federal_limits) - TOLERANCE + + # 3. Order. + assert abs(model_limit([second, first], hawaii_limits) - limit) <= TOLERANCE + + for i, (balance, year) in enumerate(pair): + # 4. Vintage. + for other_year in YEARS: + moved = list(pair) + moved[i] = (balance, other_year) + moved_limit = model_limit(moved, hawaii_limits) + if grandfathered(other_year) == grandfathered(year): + assert abs(moved_limit - limit) <= TOLERANCE + elif grandfathered(other_year): + assert moved_limit >= limit - TOLERANCE + + # 5. Monotone. + bigger = list(pair) + bigger[i] = (balance + 100_000, year) + assert model_limit(bigger, hawaii_limits) >= limit - TOLERANCE diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/tax/income/deductions/itemized/al_interest_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/tax/income/deductions/itemized/al_interest_deduction.yaml new file mode 100644 index 00000000000..a1167069a25 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/al/tax/income/deductions/itemized/al_interest_deduction.yaml @@ -0,0 +1,151 @@ +# Code of Alabama 40-18-15(a)(2) limits the interest deduction to "the amount +# allowable as an interest deduction for federal income tax purposes in the +# corresponding tax year", and the Form 40 instructions for Schedule A lines +# 10a through 14 use "the same amount as currently allowable for federal +# purposes" (2025 booklet, PDF pages 19-20). So Alabama allows the home +# mortgage interest left after the 26 U.S.C. 163(h)(3) acquisition-debt +# limits (IRS Publication 936 (2025), Table 1): +# grandfathered debt (1987 or earlier): no dollar limit +# other debt from 2017 or earlier: $1,000,000 ($500,000 separate) +# debt from 2018 or later: $750,000 ($375,000 separate) +# Deductible interest = interest x qualified loan limit / total debt. + +- name: Interest on debt above the federal limit is not deductible + period: 2025 + input: + state_code: AL + filing_status: SINGLE + home_mortgage_interest: 40_000 + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 2020 + output: + # $40,000 x $750,000 / $1,500,000 = $20,000. Before the fix Alabama + # allowed all $40,000. + deductible_mortgage_interest: 20_000 + al_interest_deduction: 20_000 + +- name: Interest on debt within the federal limit is fully deductible + period: 2025 + input: + state_code: AL + filing_status: SINGLE + home_mortgage_interest: 24_000 + first_home_mortgage_balance: 600_000 + first_home_mortgage_origination_year: 2020 + output: + al_interest_deduction: 24_000 + +- name: Interest is fully deductible when no balance is reported + period: 2025 + input: + state_code: AL + filing_status: SINGLE + home_mortgage_interest: 40_000 + output: + al_interest_deduction: 40_000 + +- name: Grandfathered debt above $1,000,000 is fully deductible + period: 2025 + input: + state_code: AL + filing_status: JOINT + home_mortgage_interest: 40_000 + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 1986 + output: + al_interest_deduction: 40_000 + +- name: Debt from 2017 or earlier keeps the $1,000,000 limit + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: AL + filing_status: JOINT + home_mortgage_interest: 40_000 + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 2016 + output: + # $40,000 x $1,000,000 / $1,500,000. + al_interest_deduction: 26_666.67 + +- name: Earlier debt of $750,000 or more leaves no room under the later limit + period: 2025 + input: + state_code: AL + filing_status: JOINT + home_mortgage_interest: 42_500 + first_home_mortgage_balance: 900_000 + first_home_mortgage_origination_year: 2020 + second_home_mortgage_balance: 800_000 + second_home_mortgage_origination_year: 2016 + output: + # Qualified loan limit = $800,000 of 2016 debt; the 2020 debt adds + # nothing because $800,000 already exceeds $750,000. + # $42,500 x $800,000 / $1,700,000 = $20,000. + al_interest_deduction: 20_000 + +- name: Married filing separately uses the $375,000 limit + period: 2025 + input: + state_code: AL + filing_status: SEPARATE + home_mortgage_interest: 24_000 + first_home_mortgage_balance: 600_000 + first_home_mortgage_origination_year: 2020 + output: + # $24,000 x $375,000 / $600,000. + al_interest_deduction: 15_000 + +- name: Investment interest is added to the federally allowed mortgage interest + period: 2025 + input: + state_code: AL + filing_status: SINGLE + home_mortgage_interest: 40_000 + investment_interest_expense: 2_000 + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 2020 + output: + al_interest_deduction: 22_000 + +- name: The federal limit stays at $750,000 in 2026 + period: 2026 + input: + state_code: AL + filing_status: SINGLE + home_mortgage_interest: 40_000 + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 2020 + output: + # P.L. 119-21 section 70108 made the $750,000 limit permanent. + al_interest_deduction: 20_000 + +- name: Spouses' interest is limited together and reaches the itemized total + period: 2025 + input: + people: + head: + age: 45 + employment_income: 150_000 + home_mortgage_interest: 30_000 + spouse: + age: 45 + employment_income: 50_000 + home_mortgage_interest: 10_000 + tax_units: + tax_unit: + members: [head, spouse] + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 2020 + marital_units: + marital_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: AL + output: + # $40,000 x $750,000 / $1,500,000 = $20,000, split in proportion to + # each spouse's interest. + deductible_mortgage_interest: [15_000, 5_000] + al_interest_deduction: 20_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml new file mode 100644 index 00000000000..435501ee03c --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml @@ -0,0 +1,176 @@ +# HRS 235-2.4(j)(3) (235-2.4(i)(3) from 2026, Act 35, SLH 2026) makes IRC +# 163(h)(3)(F) inoperative, so Hawaii keeps the pre-TCJA limits for debt +# incurred at any time (2018 N-11 instructions, PDF page 19; 2025, PDF page +# 17). IRS Publication 936 (2017), Table 1 (PDF page 11): +# G = grandfathered debt, 1987 or earlier (line 1); A = other debt (line 2) +# line 6 = min(max(G, $1,000,000), G + A) +# line 7 = min(G + A - line 6, $100,000): debt above the acquisition limit +# may qualify as home equity debt (Figure A, note 3, PDF page 3) +# line 8 = line 6 + line 7 (qualified loan limit) +# deductible interest = interest x line 8 / (G + A) +# Limits are halved for married filing separately. The federal deduction uses +# the $750,000 limit for debt from 2018 or later and no home equity debt. + +- name: Hawaii allows interest on $1,000,000 of acquisition debt plus $100,000 of home equity debt + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: SINGLE + home_mortgage_interest: 40_000 + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 2020 + output: + # Federal: $40,000 x $750,000 / $1,500,000 = $20,000. + deductible_mortgage_interest: 20_000 + # Hawaii: $40,000 x ($1,000,000 + $100,000) / $1,500,000. Before the fix + # Hawaii allowed all $40,000. + hi_mortgage_interest_deduction: 29_333.33 + hi_interest_deduction: 29_333.33 + +- name: Debt between the federal and Hawaii limits is fully deductible in Hawaii + period: 2025 + input: + state_code: HI + filing_status: SINGLE + home_mortgage_interest: 36_000 + first_home_mortgage_balance: 900_000 + first_home_mortgage_origination_year: 2020 + output: + # Federal: $36,000 x $750,000 / $900,000 = $30,000. + deductible_mortgage_interest: 30_000 + hi_mortgage_interest_deduction: 36_000 + +- name: Up to $100,000 above the acquisition limit counts as home equity debt + period: 2025 + input: + state_code: HI + filing_status: JOINT + home_mortgage_interest: 42_000 + first_home_mortgage_balance: 1_050_000 + first_home_mortgage_origination_year: 2020 + output: + # Line 6 = $1,000,000; line 7 = min($50,000, $100,000); line 8 equals + # the whole balance. + hi_mortgage_interest_deduction: 42_000 + +- name: Interest on debt within both limits is fully deductible + period: 2025 + input: + state_code: HI + filing_status: SINGLE + home_mortgage_interest: 24_000 + first_home_mortgage_balance: 600_000 + first_home_mortgage_origination_year: 2020 + output: + deductible_mortgage_interest: 24_000 + hi_mortgage_interest_deduction: 24_000 + +- name: Interest is fully deductible when no balance is reported + period: 2025 + input: + state_code: HI + filing_status: SINGLE + home_mortgage_interest: 40_000 + output: + hi_mortgage_interest_deduction: 40_000 + +- name: Grandfathered debt above $1,000,000 is fully deductible + period: 2025 + input: + state_code: HI + filing_status: JOINT + home_mortgage_interest: 40_000 + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 1986 + output: + hi_mortgage_interest_deduction: 40_000 + +- name: Grandfathered debt uses up the acquisition limit but not the home equity limit + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: JOINT + home_mortgage_interest: 40_000 + first_home_mortgage_balance: 1_200_000 + first_home_mortgage_origination_year: 1985 + second_home_mortgage_balance: 300_000 + second_home_mortgage_origination_year: 2020 + output: + # Federal: qualified loan limit $1,200,000; $40,000 x 1.2 / 1.5. + deductible_mortgage_interest_tax_unit: 32_000 + # Hawaii: line 6 = $1,200,000; line 7 = min($300,000, $100,000); + # $40,000 x $1,300,000 / $1,500,000. + hi_mortgage_interest_deduction: 34_666.67 + +- name: Married filing separately uses the $500,000 and $50,000 limits + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: SEPARATE + home_mortgage_interest: 40_000 + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 2020 + output: + # Federal: $40,000 x $375,000 / $1,500,000 = $10,000. + deductible_mortgage_interest: 10_000 + # Hawaii: $40,000 x ($500,000 + $50,000) / $1,500,000. + hi_mortgage_interest_deduction: 14_666.67 + +- name: Hawaii keeps its limits in 2026 under Act 35 + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: SINGLE + home_mortgage_interest: 40_000 + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 2020 + output: + deductible_mortgage_interest: 20_000 + hi_mortgage_interest_deduction: 29_333.33 + +- name: The interest deduction adds investment interest to Hawaii's mortgage interest + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: SINGLE + home_mortgage_interest: 40_000 + investment_interest_expense: 2_000 + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 2020 + output: + hi_interest_deduction: 31_333.33 + +- name: Spouses' interest is limited together + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 45 + employment_income: 150_000 + home_mortgage_interest: 30_000 + spouse: + age: 45 + employment_income: 50_000 + home_mortgage_interest: 10_000 + tax_units: + tax_unit: + members: [head, spouse] + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 2020 + marital_units: + marital_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: HI + output: + deductible_mortgage_interest: [15_000, 5_000] + # $40,000 x $1,100,000 / $1,500,000. + hi_mortgage_interest_deduction: 29_333.33 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/itemized/nc_claim_of_right_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/itemized/nc_claim_of_right_deduction.yaml index b0eafe19c2f..927ded40a87 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/itemized/nc_claim_of_right_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/itemized/nc_claim_of_right_deduction.yaml @@ -323,7 +323,7 @@ head: age: 40 claim_of_right_repayment: 5_000 - mortgage_interest: 18_000 + home_mortgage_interest: 18_000 real_estate_taxes: 6_000 tax_units: tax_unit: diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/nc_itemized_deductions.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/nc_itemized_deductions.yaml index 2ec2b5c8876..b0cef847f3c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/nc_itemized_deductions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/nc_itemized_deductions.yaml @@ -2,7 +2,7 @@ period: 2022 input: filing_status: JOINT - mortgage_interest: 9_000 + home_mortgage_interest: 9_000 real_estate_taxes: 11_000 charitable_deduction: 0 medical_expense_deduction: 0 @@ -14,7 +14,7 @@ period: 2022 input: filing_status: SURVIVING_SPOUSE - mortgage_interest: 11_000 + home_mortgage_interest: 11_000 real_estate_taxes: 11_000 charitable_deduction: 0 medical_expense_deduction: 0 @@ -26,7 +26,7 @@ period: 2022 input: filing_status: SURVIVING_SPOUSE - mortgage_interest: 11_000 + home_mortgage_interest: 11_000 real_estate_taxes: 11_000 charitable_deduction: 1_000 medical_expense_deduction: 500 @@ -38,7 +38,7 @@ period: 2022 input: filing_status: SEPARATE - mortgage_interest: 9_000 + home_mortgage_interest: 9_000 real_estate_taxes: 11_000 charitable_deduction: 0 medical_expense_deduction: 0 @@ -91,7 +91,7 @@ health_insurance_premiums: 6_000 self_employed_health_insurance_ald: 1_000 other_medical_expenses: 500 - mortgage_interest: 0 + home_mortgage_interest: 0 real_estate_taxes: 0 charitable_deduction: 0 output: diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/nc_mortgage_and_property_tax_before_limitation.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/nc_mortgage_and_property_tax_before_limitation.yaml index 482f949d18b..66ecff840a5 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/nc_mortgage_and_property_tax_before_limitation.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/nc_mortgage_and_property_tax_before_limitation.yaml @@ -5,7 +5,7 @@ period: 2024 input: filing_status: JOINT - mortgage_interest: 9_000 + home_mortgage_interest: 9_000 real_estate_taxes: 4_000 state_code: NC output: @@ -15,7 +15,7 @@ period: 2024 input: filing_status: SEPARATE - mortgage_interest: 9_000 + home_mortgage_interest: 9_000 real_estate_taxes: 11_000 state_code: NC output: @@ -26,7 +26,91 @@ period: 2024 input: filing_status: SINGLE - mortgage_interest: 25_000 + home_mortgage_interest: 25_000 state_code: NC output: nc_mortgage_and_property_tax_before_limitation: 25_000 + +# Line 1 is "the amount allowed as a deduction for interest paid or accrued +# during the taxable year under section 163(h) of the Code with respect to any +# qualified residence" (G.S. 105-153.5(a)(2)b; 2025 Form D-401, PDF page 20), +# so the 26 U.S.C. 163(h)(3) acquisition-debt limits apply: $750,000 +# ($375,000 separate) for debt from 2018 or later, $1,000,000 ($500,000) for +# other debt from 2017 or earlier, and no dollar limit for grandfathered debt +# from 1987 or earlier (IRS Publication 936 (2025), Table 1). + +- name: Interest on debt above the federal limit is excluded + period: 2025 + input: + state_code: NC + filing_status: SINGLE + home_mortgage_interest: 40_000 + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 2020 + output: + # $40,000 x $750,000 / $1,500,000 = $20,000. Before the fix line 1 was + # all $40,000. + deductible_mortgage_interest: 20_000 + nc_mortgage_and_property_tax_before_limitation: 20_000 + +- name: Interest on debt within the federal limit is fully counted + period: 2025 + input: + state_code: NC + filing_status: SINGLE + home_mortgage_interest: 24_000 + real_estate_taxes: 3_000 + first_home_mortgage_balance: 600_000 + first_home_mortgage_origination_year: 2020 + output: + nc_mortgage_and_property_tax_before_limitation: 27_000 + +- name: Grandfathered debt above $1,000,000 is fully counted + period: 2025 + input: + state_code: NC + filing_status: JOINT + home_mortgage_interest: 15_000 + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 1986 + output: + nc_mortgage_and_property_tax_before_limitation: 15_000 + +- name: Earlier debt of $750,000 or more leaves no room under the later limit + period: 2025 + input: + state_code: NC + filing_status: JOINT + home_mortgage_interest: 17_000 + first_home_mortgage_balance: 900_000 + first_home_mortgage_origination_year: 2020 + second_home_mortgage_balance: 800_000 + second_home_mortgage_origination_year: 2016 + output: + # Qualified loan limit = $800,000; $17,000 x $800,000 / $1,700,000. + nc_mortgage_and_property_tax_before_limitation: 8_000 + +- name: Married filing separately uses the $375,000 limit + period: 2025 + input: + state_code: NC + filing_status: SEPARATE + home_mortgage_interest: 24_000 + first_home_mortgage_balance: 600_000 + first_home_mortgage_origination_year: 2020 + output: + # $24,000 x $375,000 / $600,000. + nc_mortgage_and_property_tax_before_limitation: 15_000 + +- name: The July 5, 2025 Code keeps the $750,000 limit in 2026 + period: 2026 + input: + state_code: NC + filing_status: SINGLE + home_mortgage_interest: 40_000 + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 2020 + output: + # S.L. 2026-31 section 12(a) adopts the Code as of July 5, 2025, which + # includes P.L. 119-21 section 70108's permanent $750,000 limit. + nc_mortgage_and_property_tax_before_limitation: 20_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/nc_mortgage_and_property_tax_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/nc_mortgage_and_property_tax_deduction.yaml index b22505ec460..a8dfcb63255 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/nc_mortgage_and_property_tax_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/nc_mortgage_and_property_tax_deduction.yaml @@ -332,3 +332,54 @@ nc_mortgage_and_property_tax_before_limitation: [25_000, 15_000] nc_mortgage_and_property_tax_limitation: [12_500, 0] nc_mortgage_and_property_tax_deduction: [12_500, 0] + +- name: Only federally allowed interest enters the $20,000 limitation + period: 2025 + input: + state_code: NC + filing_status: SINGLE + home_mortgage_interest: 15_000 + real_estate_taxes: 5_000 + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 2020 + output: + # Line 1 = $15,000 x $750,000 / $1,500,000 = $7,500 (section 163(h)); + # line 3 = $7,500 + $5,000 = $12,500, under the $20,000 limitation. + # Before the fix line 3 was $20,000. + nc_mortgage_and_property_tax_deduction: 12_500 + +- name: Spouses filing separately share the limitation using federally allowed interest + period: 2025 + input: + people: + spouse1: + age: 45 + employment_income: 100_000 + home_mortgage_interest: 30_000 + spouse2: + age: 45 + employment_income: 100_000 + home_mortgage_interest: 10_000 + marital_units: + couple: + members: [spouse1, spouse2] + tax_units: + tax_unit1: + members: [spouse1] + filing_status: SEPARATE + first_home_mortgage_balance: 1_200_000 + first_home_mortgage_origination_year: 2020 + tax_unit2: + members: [spouse2] + filing_status: SEPARATE + households: + household: + members: [spouse1, spouse2] + state_code: NC + output: + # Spouse 1: $30,000 x $375,000 / $1,200,000 = $9,375. Spouse 2 reports + # no balance, so all $10,000 counts. Together $19,375 is under $20,000, + # so neither is prorated. Before the fix the $40,000 total was prorated + # to $15,000 and $5,000. + nc_mortgage_and_property_tax_before_limitation: [9_375, 10_000] + nc_mortgage_and_property_tax_deduction: [9_375, 10_000] diff --git a/policyengine_us/variables/gov/states/al/tax/income/deductions/itemized/al_investment_interest.py b/policyengine_us/variables/gov/states/al/tax/income/deductions/itemized/al_investment_interest.py index 7d576955da5..a102d1f9576 100644 --- a/policyengine_us/variables/gov/states/al/tax/income/deductions/itemized/al_investment_interest.py +++ b/policyengine_us/variables/gov/states/al/tax/income/deductions/itemized/al_investment_interest.py @@ -4,17 +4,29 @@ class al_interest_deduction(Variable): value_type = float entity = TaxUnit - label = "Alabama investment interest" + label = "Alabama interest deduction" unit = USD definition_period = YEAR + documentation = ( + "Alabama limits the interest deduction to the amount allowable for " + "federal income tax purposes, so home mortgage interest on acquisition " + "debt above the 26 U.S.C. 163(h)(3) limits is not deductible." + ) reference = ( + # Code of Alabama Section 40-18-15(a)(2) "https://law.justia.com/codes/alabama/2022/title-40/chapter-18/article-1/section-40-18-15/", - # Code of Alabama Section 40-18-15 + # 2025 Form 40 instructions, Schedule A lines 10a through 14, + # PDF pages 19-20 + "https://www.revenue.alabama.gov/wp-content/uploads/2026/01/25f40bk.pdf#page=19", + # 2022 Schedule A (Form 40) "https://www.revenue.alabama.gov/ultraviewer/viewer/basic_viewer/index.html?form=2023/01/22f40schabdc_blk.pdf#page=1", - # 2022 Schedule A (Form 1040) + # 2021 Schedule A (Form 40) "https://www.revenue.alabama.gov/ultraviewer/viewer/basic_viewer/index.html?form=2022/06/21f40schabdc_blk.pdf#page=1", - # 2021 Schedule A (Form 1040) ) defined_for = StateCode.AL - adds = ["mortgage_interest", "investment_interest_expense"] + # Code of Alabama 40-18-15(a)(2): interest "limited to the amount + # allowable as an interest deduction for federal income tax purposes in + # the corresponding tax year", so mortgage interest follows the federal + # acquisition-debt limits. + adds = ["deductible_mortgage_interest", "investment_interest_expense"] diff --git a/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_interest_deduction.py b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_interest_deduction.py index 5d90b997a73..344dde4f3a2 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_interest_deduction.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_interest_deduction.py @@ -13,4 +13,4 @@ class hi_interest_deduction(Variable): definition_period = YEAR defined_for = StateCode.HI - adds = ["investment_interest_expense", "mortgage_interest"] + adds = ["investment_interest_expense", "hi_mortgage_interest_deduction"] diff --git a/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.py b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.py new file mode 100644 index 00000000000..6f0506a9076 --- /dev/null +++ b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.py @@ -0,0 +1,101 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.household.expense.tax_unit.mortgage_interest_structure import ( + _debt_by_vintage, +) + + +def _hi_qualified_loan_limit( + grandfathered_debt, + other_debt, + home_acquisition_debt_limit, + home_equity_debt_limit, +): + """2017 IRS Publication 936, Table 1, lines 1-8, as Hawaii applies it. + + Hawaii makes IRC 163(h)(3)(F) inoperative, so it never lowers the + acquisition-debt limit for later debt or suspends home equity interest. + Grandfathered debt, incurred on or before October 13, 1987, has no dollar + limit but reduces the acquisition-debt limit for other debt (lines 1-6). + Debt above the acquisition-debt limit may qualify as home equity debt, up + to the home equity debt limit (line 7; Publication 936 (2017), Figure A, + note 3). The home equity debt limit is also capped by each home's fair + market value less its acquisition debt; PolicyEngine has no home value + input, so every home is treated as worth at least its mortgages. + """ + total_debt = grandfathered_debt + other_debt + # Lines 3-6. + line_6 = min_(max_(grandfathered_debt, home_acquisition_debt_limit), total_debt) + # Line 7. + line_7 = min_(total_debt - line_6, home_equity_debt_limit) + # Line 8. + return line_6 + line_7 + + +class hi_mortgage_interest_deduction(Variable): + value_type = float + entity = TaxUnit + label = "Hawaii home mortgage interest deduction" + unit = USD + definition_period = YEAR + documentation = ( + "Home mortgage interest Hawaii allows. Hawaii makes IRC 163(h)(3)(F) " + "inoperative, so it keeps the pre-TCJA limits whenever the debt was " + "incurred: interest on up to $1,000,000 of home acquisition debt plus " + "$100,000 of home equity debt ($500,000 and $50,000 for married " + "filing separately), rather than the federal $750,000 limit for debt " + "incurred after December 15, 2017." + ) + reference = ( + # HRS 235-2.4(j)(3) + "https://data.capitol.hawaii.gov/hrscurrent/Vol04_Ch0201-0257/HRS0235/HRS_0235-0002_0004.htm", + # Act 35, SLH 2026, HRS 235-2.4(i)(3) + "https://data.capitol.hawaii.gov/sessions/session2026/bills/HB2329_CD1_.pdf#page=20", + # 2025 N-11 instructions, line 21c + "https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=17", + # 2017 Publication 936, Figure A note 3 (PDF page 3), home equity debt + # limit (PDF page 10) and Table 1 (PDF page 11) + "https://www.irs.gov/pub/irs-prior/p936--2017.pdf#page=3", + ) + defined_for = StateCode.HI + + def formula(tax_unit, period, parameters): + balances = [ + tax_unit("first_home_mortgage_balance", period), + tax_unit("second_home_mortgage_balance", period), + ] + origination_years = [ + tax_unit("first_home_mortgage_origination_year", period), + tax_unit("second_home_mortgage_origination_year", period), + ] + p_irs = parameters(period).gov.irs.deductions.itemized.interest.mortgage + grandfathered_debt, pre_tcja_debt, post_tcja_debt = _debt_by_vintage( + balances, + origination_years, + p_irs.grandfathered_origination_year, + p_irs.pre_tcja_origination_year, + ) + # Hawaii has one limit for debt incurred after October 13, 1987, + # whenever it was incurred. + other_debt = pre_tcja_debt + post_tcja_debt + filing_status = tax_unit("filing_status", period) + p = parameters(period).gov.states.hi.tax.income.deductions.itemized.interest + qualified_loan_limit = _hi_qualified_loan_limit( + grandfathered_debt, + other_debt, + p.home_acquisition_debt_limit[filing_status], + p.home_equity_debt_limit[filing_status], + ) + # Table 1, lines 9-12. As in the federal calculation, interest is + # treated as within the limits when no balance is reported, and the + # ratio is not rounded. + total_debt = grandfathered_debt + other_debt + deductible_share = np.divide( + qualified_loan_limit, + total_debt, + out=np.ones_like(total_debt), + where=total_debt > 0, + ) + # Line 10: interest before any limit, the federally deductible and + # non-deductible parts together. + interest = add(tax_unit, period, ["mortgage_interest"]) + return interest * deductible_share diff --git a/policyengine_us/variables/gov/states/nc/tax/income/deductions/nc_mortgage_and_property_tax_before_limitation.py b/policyengine_us/variables/gov/states/nc/tax/income/deductions/nc_mortgage_and_property_tax_before_limitation.py index 24da4bdf9a9..7cda9be057e 100644 --- a/policyengine_us/variables/gov/states/nc/tax/income/deductions/nc_mortgage_and_property_tax_before_limitation.py +++ b/policyengine_us/variables/gov/states/nc/tax/income/deductions/nc_mortgage_and_property_tax_before_limitation.py @@ -7,8 +7,11 @@ class nc_mortgage_and_property_tax_before_limitation(Variable): label = "North Carolina mortgage interest and real estate property taxes before limitation" unit = USD definition_period = YEAR - documentation = "Form D-400 Schedule A line 3: home mortgage interest (line 1) plus real estate property taxes (line 2), before the $20,000 limitation. Calculated for all states so a separately filing spouse outside North Carolina contributes to the shared cap." + documentation = "Form D-400 Schedule A line 3: home mortgage interest (line 1) plus real estate property taxes (line 2), before the $20,000 limitation. Line 1 is the interest allowed under section 163(h) of the Code, after the federal acquisition-debt limits. Calculated for all states so a separately filing spouse outside North Carolina contributes to the shared cap." reference = ( + # SL 2026-31 section 12(a) adopts the IRC as of July 5, 2025, + # including the permanent $750,000 acquisition-debt limit from 2026. + "https://www.ncleg.gov/EnactedLegislation/SessionLaws/PDF/2025-2026/SL2026-31.pdf#page=42", # N.C. Gen. Stat. 105-153.5(a)(2)b "https://www.ncleg.gov/EnactedLegislation/Statutes/HTML/BySection/Chapter_105/GS_105-153.5.html", # 2025 Form D-401 instructions, Form D-400 Schedule A lines 1 to 3 @@ -20,7 +23,10 @@ class nc_mortgage_and_property_tax_before_limitation(Variable): def formula(tax_unit, period, parameters): filing_status = tax_unit("filing_status", period) - mortgage_interest = add(tax_unit, period, ["mortgage_interest"]) + # Line 1 is "the amount allowed as a deduction for interest ... under + # section 163(h) of the Code", so interest on acquisition debt above + # the federal limits is excluded. + mortgage_interest = add(tax_unit, period, ["deductible_mortgage_interest"]) pirs = parameters(period).gov.irs.deductions.itemized.salt_and_real_estate property_taxes = min_( add(tax_unit, period, ["real_estate_taxes"]), From 9fde8b6eae3e4e98dc6565974be017ad340ef58c Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 08:47:40 -0400 Subject: [PATCH 2/5] Round Hawaii's worksheet ratio to three places, as 2017 Publication 936 Table 1 requires The 2025 N-11 instructions (line 21c) send Hawaii filers to the 2017 Publication 936 worksheet, whose line 11 rounds the qualified-loan-limit ratio to three places. TheAxiomFoundation/rulespec-us#1556 encodes the same rounding, so PE and Axiom agree. Its three companion cases are added to the Hawaii YAML, and the property test now covers the rounded share. Co-Authored-By: Claude Opus 5.5 --- .../test_hi_mortgage_debt_limit_invariants.py | 26 +++++- .../hi_mortgage_interest_deduction.yaml | 90 ++++++++++++++++--- .../hi_mortgage_interest_deduction.py | 31 ++++--- 3 files changed, 122 insertions(+), 25 deletions(-) diff --git a/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py b/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py index 11ec42e899a..c349a69fbf8 100644 --- a/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py +++ b/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py @@ -22,8 +22,12 @@ moving a loan between 2017 and 2018 never changes the limit, while moving it into the grandfathered years never lowers it. 5. Monotone: a larger balance never lowers the limit. +6. Share: line 11, the limit over total debt "rounded to three places", lies + in [0, 1], within half a thousandth of the exact ratio and, for the drawn + balances, at or above the federal (exact) share, so Hawaii's deduction + stays between the federal deduction and the interest paid. -These are for-all properties of the helper, which YAML cannot state. The +These are for-all properties of the helpers, which YAML cannot state. The policy outcomes and interest proration are checked in hi_mortgage_interest_deduction.yaml. """ @@ -33,6 +37,7 @@ from hypothesis import strategies as st from policyengine_us.variables.gov.states.hi.tax.income.deductions.itemized.hi_mortgage_interest_deduction import ( + _hi_deductible_share, _hi_qualified_loan_limit, ) from policyengine_us.variables.household.expense.tax_unit.mortgage_interest_structure import ( @@ -161,3 +166,22 @@ def test_hawaii_qualified_loan_limit_properties(first, second, status): bigger = list(pair) bigger[i] = (balance + 100_000, year) assert model_limit(bigger, hawaii_limits) >= limit - TOLERANCE + + # 6. Share. + share = float(_hi_deductible_share(np.array([limit]), np.array([total]))[0]) + assert 0 <= share <= 1 + if total > 0: + assert abs(share - limit / total) <= 0.0005 + 1e-9 + assert share >= federal_limit(pair, federal_limits) / total - 1e-9 + else: + assert share == 1 + + +def test_hawaii_share_rounds_half_up_to_three_places(): + limits = np.array([550_000.0, 1_100_000.0, 1_100_000.0, 0.0]) + debt = np.array([1_760_000.0, 1_500_000.0, 1_300_000.0, 0.0]) + # .3125 rounds up to .313; .7333 down to .733; .8462 down to .846; no + # balance gives one. + np.testing.assert_allclose( + _hi_deductible_share(limits, debt), [0.313, 0.733, 0.846, 1.0] + ) diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml index 435501ee03c..b7d8932ed60 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml @@ -7,9 +7,12 @@ # line 7 = min(G + A - line 6, $100,000): debt above the acquisition limit # may qualify as home equity debt (Figure A, note 3, PDF page 3) # line 8 = line 6 + line 7 (qualified loan limit) -# deductible interest = interest x line 8 / (G + A) +# line 11 = line 8 / (G + A), "rounded to three places" +# line 12 = interest x line 11 +# The 2025 N-11 instructions (line 21c) send filers to this worksheet. # Limits are halved for married filing separately. The federal deduction uses -# the $750,000 limit for debt from 2018 or later and no home equity debt. +# the $750,000 limit for debt from 2018 or later, no home equity debt and the +# exact ratio. - name: Hawaii allows interest on $1,000,000 of acquisition debt plus $100,000 of home equity debt period: 2025 @@ -23,10 +26,10 @@ output: # Federal: $40,000 x $750,000 / $1,500,000 = $20,000. deductible_mortgage_interest: 20_000 - # Hawaii: $40,000 x ($1,000,000 + $100,000) / $1,500,000. Before the fix - # Hawaii allowed all $40,000. - hi_mortgage_interest_deduction: 29_333.33 - hi_interest_deduction: 29_333.33 + # Hawaii: line 11 = ($1,000,000 + $100,000) / $1,500,000 = .733; + # $40,000 x .733. Before the fix Hawaii allowed all $40,000. + hi_mortgage_interest_deduction: 29_320 + hi_interest_deduction: 29_320 - name: Debt between the federal and Hawaii limits is fully deductible in Hawaii period: 2025 @@ -101,8 +104,8 @@ # Federal: qualified loan limit $1,200,000; $40,000 x 1.2 / 1.5. deductible_mortgage_interest_tax_unit: 32_000 # Hawaii: line 6 = $1,200,000; line 7 = min($300,000, $100,000); - # $40,000 x $1,300,000 / $1,500,000. - hi_mortgage_interest_deduction: 34_666.67 + # line 11 = $1,300,000 / $1,500,000 = .867; $40,000 x .867. + hi_mortgage_interest_deduction: 34_680 - name: Married filing separately uses the $500,000 and $50,000 limits period: 2025 @@ -116,8 +119,9 @@ output: # Federal: $40,000 x $375,000 / $1,500,000 = $10,000. deductible_mortgage_interest: 10_000 - # Hawaii: $40,000 x ($500,000 + $50,000) / $1,500,000. - hi_mortgage_interest_deduction: 14_666.67 + # Hawaii: line 11 = ($500,000 + $50,000) / $1,500,000 = .367; + # $40,000 x .367. + hi_mortgage_interest_deduction: 14_680 - name: Hawaii keeps its limits in 2026 under Act 35 period: 2026 @@ -130,7 +134,7 @@ first_home_mortgage_origination_year: 2020 output: deductible_mortgage_interest: 20_000 - hi_mortgage_interest_deduction: 29_333.33 + hi_mortgage_interest_deduction: 29_320 - name: The interest deduction adds investment interest to Hawaii's mortgage interest period: 2025 @@ -143,7 +147,7 @@ first_home_mortgage_balance: 1_500_000 first_home_mortgage_origination_year: 2020 output: - hi_interest_deduction: 31_333.33 + hi_interest_deduction: 31_320 - name: Spouses' interest is limited together period: 2025 @@ -172,5 +176,63 @@ state_code: HI output: deductible_mortgage_interest: [15_000, 5_000] - # $40,000 x $1,100,000 / $1,500,000. - hi_mortgage_interest_deduction: 29_333.33 + # $40,000 x .733. + hi_mortgage_interest_deduction: 29_320 + +# Companion cases of TheAxiomFoundation/rulespec-us#1556, computed by hand +# from the 2017 Publication 936 Table 1 and the N-11 instructions. + +- name: Line 11 rounds the ratio to three places before multiplying + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: SINGLE + home_mortgage_interest: 90_000 + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 2020 + output: + # round($1,100,000 / $1,500,000, 3) = .733; .733 x $90,000. + hi_mortgage_interest_deduction: 65_970 + +- name: A separate filer's balance above $500,000 counts as home equity debt up to $50,000 + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: SEPARATE + home_mortgage_interest: 30_000 + first_home_mortgage_balance: 600_000 + first_home_mortgage_origination_year: 2020 + output: + # round($550,000 / $600,000, 3) = .917; .917 x $30,000. + hi_mortgage_interest_deduction: 27_510 + +- name: Both homes' mortgages share one limit + period: 2024 + absolute_error_margin: 0.01 + input: + people: + head: + age: 45 + home_mortgage_interest: 40_000 + spouse: + age: 45 + home_mortgage_interest: 25_000 + tax_units: + tax_unit: + members: [head, spouse] + first_home_mortgage_balance: 800_000 + first_home_mortgage_origination_year: 2020 + second_home_mortgage_balance: 500_000 + second_home_mortgage_origination_year: 2021 + marital_units: + marital_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: HI + output: + # round($1,100,000 / $1,300,000, 3) = .846; .846 x $65,000. + hi_mortgage_interest_deduction: 54_990 diff --git a/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.py b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.py index 6f0506a9076..95f9e29038b 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.py @@ -31,6 +31,22 @@ def _hi_qualified_loan_limit( return line_6 + line_7 +def _hi_deductible_share(qualified_loan_limit, total_debt): + """2017 IRS Publication 936, Table 1, line 11. + + The qualified loan limit divided by the total debt, "rounded to three + places" (half up). Interest is treated as within the limits, a share of + one, when no balance is reported, as in the federal calculation. + """ + ratio = np.divide( + qualified_loan_limit, + total_debt, + out=np.ones_like(total_debt), + where=total_debt > 0, + ) + return np.floor(ratio * 1_000 + 0.5) / 1_000 + + class hi_mortgage_interest_deduction(Variable): value_type = float entity = TaxUnit @@ -85,17 +101,12 @@ def formula(tax_unit, period, parameters): p.home_acquisition_debt_limit[filing_status], p.home_equity_debt_limit[filing_status], ) - # Table 1, lines 9-12. As in the federal calculation, interest is - # treated as within the limits when no balance is reported, and the - # ratio is not rounded. + # Lines 9 and 11. The 2025 N-11 instructions send filers to this + # worksheet, which rounds the ratio; the federal calculation uses the + # exact ratio. total_debt = grandfathered_debt + other_debt - deductible_share = np.divide( - qualified_loan_limit, - total_debt, - out=np.ones_like(total_debt), - where=total_debt > 0, - ) + deductible_share = _hi_deductible_share(qualified_loan_limit, total_debt) # Line 10: interest before any limit, the federally deductible and - # non-deductible parts together. + # non-deductible parts together. Line 12 multiplies it by line 11. interest = add(tax_unit, period, ["mortgage_interest"]) return interest * deductible_share From 6dadd9973dcb5d8db40499394010d4510416c55d Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 09:37:29 -0400 Subject: [PATCH 3/5] Keep a supplied Hawaii deduction and round line 11 in double precision Review r1 (GPT-6.1 Sol) findings: - Hawaii multiplied a directly supplied deductible_mortgage_interest by the worksheet share again ($20,000 supplied on $1.5M of debt gave $14,660). Hawaii now starts from interest paid as the federally deductible plus non-deductible parts, keeps the federal amount when federal law disallows nothing, and never goes below it (_hi_allowed_interest). - In single precision $1,100,000 / $1,128,784 = .97449999 became .9745 and rounded up; the ratio is now computed in double precision. - Alabama and North Carolina YAML headers say the expectations follow PE's exact federal ratio, not the worksheet's three-place rounding. - Alabama's spouse case now asserts al_itemized_deductions. - hi_interest_deduction cites the 2022 N-11 once, with both pages. The property test now covers single-precision inputs and line 12. Co-Authored-By: Claude Opus 5.5 --- .../test_hi_mortgage_debt_limit_invariants.py | 44 ++++++++++++++++- .../itemized/al_interest_deduction.yaml | 10 ++-- .../hi_mortgage_interest_deduction.yaml | 35 ++++++++++++-- ...ge_and_property_tax_before_limitation.yaml | 5 +- .../itemized/hi_interest_deduction.py | 3 +- .../hi_mortgage_interest_deduction.py | 48 +++++++++++++------ 6 files changed, 120 insertions(+), 25 deletions(-) diff --git a/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py b/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py index c349a69fbf8..c4602ca8ab9 100644 --- a/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py +++ b/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py @@ -24,8 +24,13 @@ 5. Monotone: a larger balance never lowers the limit. 6. Share: line 11, the limit over total debt "rounded to three places", lies in [0, 1], within half a thousandth of the exact ratio and, for the drawn - balances, at or above the federal (exact) share, so Hawaii's deduction - stays between the federal deduction and the interest paid. + balances, at or above the federal (exact) share. It is the same whether + the balances arrive in single precision, as in a simulation, or double. +7. Line 12: the Hawaii deduction lies between the federal deduction and the + interest paid, equals the federal deduction when federal law disallows + nothing (so a deduction supplied directly is not limited again), and + otherwise equals interest paid times line 11 unless that would fall below + the federal amount. These are for-all properties of the helpers, which YAML cannot state. The policy outcomes and interest proration are checked in @@ -37,6 +42,7 @@ from hypothesis import strategies as st from policyengine_us.variables.gov.states.hi.tax.income.deductions.itemized.hi_mortgage_interest_deduction import ( + _hi_allowed_interest, _hi_deductible_share, _hi_qualified_loan_limit, ) @@ -175,6 +181,34 @@ def test_hawaii_qualified_loan_limit_properties(first, second, status): assert share >= federal_limit(pair, federal_limits) / total - 1e-9 else: assert share == 1 + single = _hi_deductible_share( + np.array([limit], dtype=np.float32), np.array([total], dtype=np.float32) + ) + assert float(single[0]) == share + + +amounts = st.one_of(st.just(0.0), st.floats(0, 500_000, allow_nan=False)) + + +@settings(**SETTINGS) +@given( + deductible=amounts, + non_deductible=amounts, + share=st.one_of(st.sampled_from((0.0, 0.5, 0.733, 1.0)), st.floats(0, 1)), +) +def test_hawaii_allowed_interest_properties(deductible, non_deductible, share): + allowed = float( + _hi_allowed_interest( + np.array([deductible]), np.array([non_deductible]), np.array([share]) + )[0] + ) + paid = deductible + non_deductible + # 7. Line 12. + assert deductible - TOLERANCE <= allowed <= paid + TOLERANCE + if non_deductible == 0: + assert allowed == deductible + else: + assert abs(allowed - max(paid * share, deductible)) <= TOLERANCE def test_hawaii_share_rounds_half_up_to_three_places(): @@ -185,3 +219,9 @@ def test_hawaii_share_rounds_half_up_to_three_places(): np.testing.assert_allclose( _hi_deductible_share(limits, debt), [0.313, 0.733, 0.846, 1.0] ) + # .97449999 rounds down, in single precision too. + near_tie = _hi_deductible_share( + np.array([1_100_000], dtype=np.float32), + np.array([1_128_784], dtype=np.float32), + ) + np.testing.assert_array_equal(near_tie, [0.974]) diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/tax/income/deductions/itemized/al_interest_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/tax/income/deductions/itemized/al_interest_deduction.yaml index a1167069a25..94b10aa9db2 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/al/tax/income/deductions/itemized/al_interest_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/al/tax/income/deductions/itemized/al_interest_deduction.yaml @@ -8,7 +8,11 @@ # grandfathered debt (1987 or earlier): no dollar limit # other debt from 2017 or earlier: $1,000,000 ($500,000 separate) # debt from 2018 or later: $750,000 ($375,000 separate) -# Deductible interest = interest x qualified loan limit / total debt. +# Deductible interest = interest x qualified loan limit / total debt. The +# expectations follow PolicyEngine's federal calculation, which uses the exact +# ratio; Table 1 line 14 rounds it to three places, which moves some amounts +# by up to 0.05% of the interest (for example $26,680 rather than $26,666.67 +# for the 2016 case below). - name: Interest on debt above the federal limit is not deductible period: 2025 @@ -126,11 +130,9 @@ people: head: age: 45 - employment_income: 150_000 home_mortgage_interest: 30_000 spouse: age: 45 - employment_income: 50_000 home_mortgage_interest: 10_000 tax_units: tax_unit: @@ -149,3 +151,5 @@ # each spouse's interest. deductible_mortgage_interest: [15_000, 5_000] al_interest_deduction: 20_000 + # No other itemized deduction applies. + al_itemized_deductions: 20_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml index b7d8932ed60..cee12a32341 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml @@ -10,9 +10,11 @@ # line 11 = line 8 / (G + A), "rounded to three places" # line 12 = interest x line 11 # The 2025 N-11 instructions (line 21c) send filers to this worksheet. -# Limits are halved for married filing separately. The federal deduction uses -# the $750,000 limit for debt from 2018 or later, no home equity debt and the -# exact ratio. +# Limits are halved for married filing separately. PolicyEngine's federal +# deduction uses the $750,000 limit for debt from 2018 or later, no home +# equity debt and the exact ratio (its worksheet rounds too). Interest paid +# (line 10) is the federally deductible and non-deductible parts together; a +# deduction supplied directly has already been limited and passes through. - name: Hawaii allows interest on $1,000,000 of acquisition debt plus $100,000 of home equity debt period: 2025 @@ -236,3 +238,30 @@ output: # round($1,100,000 / $1,300,000, 3) = .846; .846 x $65,000. hi_mortgage_interest_deduction: 54_990 + +- name: Line 11 is rounded from the exact ratio + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: SINGLE + home_mortgage_interest: 40_000 + first_home_mortgage_balance: 1_128_784 + first_home_mortgage_origination_year: 2020 + output: + # $1,100,000 / $1,128,784 = .97449999, which rounds to .974 (single + # precision would give .9745 and round up); .974 x $40,000. + hi_mortgage_interest_deduction: 38_960 + +- name: A deduction supplied directly is not limited a second time + period: 2025 + input: + state_code: HI + filing_status: SINGLE + deductible_mortgage_interest: 20_000 + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 2020 + output: + # The supplied $20,000 is already after the federal limit and no gross + # interest is reported, so Hawaii keeps it rather than taking .733 of it. + hi_mortgage_interest_deduction: 20_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/nc_mortgage_and_property_tax_before_limitation.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/nc_mortgage_and_property_tax_before_limitation.yaml index 66ecff840a5..4302946a542 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/nc_mortgage_and_property_tax_before_limitation.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/tax/income/deductions/nc_mortgage_and_property_tax_before_limitation.yaml @@ -37,7 +37,10 @@ # so the 26 U.S.C. 163(h)(3) acquisition-debt limits apply: $750,000 # ($375,000 separate) for debt from 2018 or later, $1,000,000 ($500,000) for # other debt from 2017 or earlier, and no dollar limit for grandfathered debt -# from 1987 or earlier (IRS Publication 936 (2025), Table 1). +# from 1987 or earlier (IRS Publication 936 (2025), Table 1). The expectations +# follow PolicyEngine's federal calculation, which uses the exact ratio; Table +# 1 line 14 rounds it to three places (for example $8,007 rather than $8,000 +# for the mixed-vintage case below). - name: Interest on debt above the federal limit is excluded period: 2025 diff --git a/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_interest_deduction.py b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_interest_deduction.py index 344dde4f3a2..3b21411e76c 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_interest_deduction.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_interest_deduction.py @@ -7,8 +7,9 @@ class hi_interest_deduction(Variable): label = "Hawaii interest deduction" unit = USD reference = ( + # 2022 N-11 instructions, line 21c and the total itemized deduction + # worksheet, PDF pages 17, 32 "https://files.hawaii.gov/tax/forms/2022/n11ins.pdf#page=17", - "https://files.hawaii.gov/tax/forms/2022/n11ins.pdf#page=32", # total itemized deduction worksheet ) definition_period = YEAR defined_for = StateCode.HI diff --git a/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.py b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.py index 95f9e29038b..bd9db2f97f6 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.py @@ -36,17 +36,33 @@ def _hi_deductible_share(qualified_loan_limit, total_debt): The qualified loan limit divided by the total debt, "rounded to three places" (half up). Interest is treated as within the limits, a share of - one, when no balance is reported, as in the federal calculation. + one, when no balance is reported, as in the federal calculation. The + ratio is computed in double precision: in single precision a ratio just + below a half-thousandth, such as $1,100,000 / $1,128,784 = .97449999, + rounds the wrong way. """ - ratio = np.divide( - qualified_loan_limit, - total_debt, - out=np.ones_like(total_debt), - where=total_debt > 0, - ) + limit = np.asarray(qualified_loan_limit, dtype=np.float64) + debt = np.asarray(total_debt, dtype=np.float64) + ratio = np.divide(limit, debt, out=np.ones_like(debt), where=debt > 0) return np.floor(ratio * 1_000 + 0.5) / 1_000 +def _hi_allowed_interest(deductible, non_deductible, deductible_share): + """Table 1, line 12: interest paid (line 10) times line 11. + + Interest paid is the federally deductible and non-deductible parts + together. When federal law disallows none of it, Hawaii's limits, which + are never lower than the federal ones, disallow none either; keeping the + federal amount then also passes through a deduction supplied directly, + which the federal limits have already reduced, rather than reducing it + again. For the same reason the result is never below the federal amount, + which the worksheet's rounding could otherwise undercut by a fraction of + a percent. + """ + hawaii = (deductible + non_deductible) * deductible_share + return where(non_deductible > 0, max_(hawaii, deductible), deductible) + + class hi_mortgage_interest_deduction(Variable): value_type = float entity = TaxUnit @@ -76,8 +92,8 @@ class hi_mortgage_interest_deduction(Variable): def formula(tax_unit, period, parameters): balances = [ - tax_unit("first_home_mortgage_balance", period), - tax_unit("second_home_mortgage_balance", period), + tax_unit("first_home_mortgage_balance", period).astype(np.float64), + tax_unit("second_home_mortgage_balance", period).astype(np.float64), ] origination_years = [ tax_unit("first_home_mortgage_origination_year", period), @@ -102,11 +118,13 @@ def formula(tax_unit, period, parameters): p.home_equity_debt_limit[filing_status], ) # Lines 9 and 11. The 2025 N-11 instructions send filers to this - # worksheet, which rounds the ratio; the federal calculation uses the - # exact ratio. + # worksheet, which rounds the ratio; PolicyEngine's federal + # calculation uses the exact ratio. total_debt = grandfathered_debt + other_debt deductible_share = _hi_deductible_share(qualified_loan_limit, total_debt) - # Line 10: interest before any limit, the federally deductible and - # non-deductible parts together. Line 12 multiplies it by line 11. - interest = add(tax_unit, period, ["mortgage_interest"]) - return interest * deductible_share + # Lines 10 and 12. + return _hi_allowed_interest( + add(tax_unit, period, ["deductible_mortgage_interest"]), + add(tax_unit, period, ["non_deductible_mortgage_interest"]), + deductible_share, + ) From 24b9d1e60fe74a28db5f233ad2285df30676d22b Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Tue, 20 Oct 2026 17:20:01 -0400 Subject: [PATCH 4/5] Clarify Hawaii worksheet gap-fill and federal-floor safeguard --- .../test_hi_mortgage_debt_limit_invariants.py | 17 ++++-- .../hi_mortgage_interest_deduction.yaml | 25 +++++++- .../hi_mortgage_interest_deduction.py | 59 ++++++++++++------- 3 files changed, 74 insertions(+), 27 deletions(-) diff --git a/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py b/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py index c4602ca8ab9..964cef54ad9 100644 --- a/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py +++ b/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py @@ -8,6 +8,11 @@ filing separately); up to $100,000 ($50,000) of debt above that limit counts as home equity debt (Figure A, note 3, PDF page 3). +The law does not prescribe allocation rounding. PE uses the N-11 / 2017 +Publication 936 worksheet as a gap-fill and separately applies a federal +deduction floor as its chosen approximation. The floor is a PE safeguard, +not part of worksheet line 12. + Hypothesis draws pairs of loans around every cutoff and limit and checks _hi_qualified_loan_limit against two independent restatements, one line by line from Table 1 and one in the statute's order, and against the federal @@ -15,8 +20,8 @@ 1. Differential: the helper equals both restatements. 2. Bounds: grandfathered debt <= Hawaii limit <= total debt, and the Hawaii - limit is at least the federal limit for the same loans, so Hawaii never - allows less interest than federal law and never more than was paid. + limit is at least the federal limit for the same loans. PE's chosen + approximation separately preserves the federal deduction floor below. 3. Order: swapping the two loans never changes the limit. 4. Vintage: Hawaii has one limit for debt incurred after October 13, 1987, so moving a loan between 2017 and 2018 never changes the limit, while moving @@ -26,10 +31,10 @@ in [0, 1], within half a thousandth of the exact ratio and, for the drawn balances, at or above the federal (exact) share. It is the same whether the balances arrive in single precision, as in a simulation, or double. -7. Line 12: the Hawaii deduction lies between the federal deduction and the - interest paid, equals the federal deduction when federal law disallows +7. PE safeguard: the Hawaii deduction lies between the federal deduction and + the interest paid, equals the federal deduction when federal law disallows nothing (so a deduction supplied directly is not limited again), and - otherwise equals interest paid times line 11 unless that would fall below + otherwise equals worksheet interest paid times line 11, floored by PE at the federal amount. These are for-all properties of the helpers, which YAML cannot state. The @@ -203,7 +208,7 @@ def test_hawaii_allowed_interest_properties(deductible, non_deductible, share): )[0] ) paid = deductible + non_deductible - # 7. Line 12. + # 7. PE federal-floor safeguard. assert deductible - TOLERANCE <= allowed <= paid + TOLERANCE if non_deductible == 0: assert allowed == deductible diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml index cee12a32341..339fd96e2c2 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml @@ -9,7 +9,9 @@ # line 8 = line 6 + line 7 (qualified loan limit) # line 11 = line 8 / (G + A), "rounded to three places" # line 12 = interest x line 11 -# The 2025 N-11 instructions (line 21c) send filers to this worksheet. +# The 2025 N-11 instructions (line 21c) send filers to this worksheet, which +# fills the law's allocation-rounding gap. PE separately floors the result +# at its federal deduction as a safeguard; that floor is not worksheet line 12. # Limits are halved for married filing separately. PolicyEngine's federal # deduction uses the $750,000 limit for debt from 2018 or later, no home # equity debt and the exact ratio (its worksheet rounds too). Interest paid @@ -253,6 +255,27 @@ # precision would give .9745 and round up); .974 x $40,000. hi_mortgage_interest_deduction: 38_960 +- name: PE's federal floor binds when worksheet rounding would allow less than federal + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: SINGLE + home_mortgage_interest: 40_000 + first_home_mortgage_balance: 497_550_016 + first_home_mortgage_origination_year: 1986 + second_home_mortgage_balance: 2_449_984 + second_home_mortgage_origination_year: 2020 + output: + # Total debt is $500,000,000; the federal limit is $497,550,016. + # $40,000 x $497,550,016 / $500,000,000 = $39,804.00128. + deductible_mortgage_interest: 39_804 + # Hawaii's limit is $497,650,016: line 11 rounds .995300032 to .995, + # so worksheet line 12 gives $39,800. PE's separate floor keeps the + # federal amount, about $39,804, instead. + hi_mortgage_interest_deduction: 39_804 + hi_interest_deduction: 39_804 + - name: A deduction supplied directly is not limited a second time period: 2025 input: diff --git a/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.py b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.py index bd9db2f97f6..67a8036904a 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.py @@ -34,9 +34,10 @@ def _hi_qualified_loan_limit( def _hi_deductible_share(qualified_loan_limit, total_debt): """2017 IRS Publication 936, Table 1, line 11. - The qualified loan limit divided by the total debt, "rounded to three - places" (half up). Interest is treated as within the limits, a share of - one, when no balance is reported, as in the federal calculation. The + The worksheet specifies the qualified loan limit divided by total debt, + "rounded to three places"; PE uses half-up rounding at ties. Interest is + treated as within the limits, a share of one, when no balance is + reported, as in the federal calculation. The ratio is computed in double precision: in single precision a ratio just below a half-thousandth, such as $1,100,000 / $1,128,784 = .97449999, rounds the wrong way. @@ -48,16 +49,20 @@ def _hi_deductible_share(qualified_loan_limit, total_debt): def _hi_allowed_interest(deductible, non_deductible, deductible_share): - """Table 1, line 12: interest paid (line 10) times line 11. + """N-11 / Publication 936 worksheet with a PE federal-floor safeguard. - Interest paid is the federally deductible and non-deductible parts - together. When federal law disallows none of it, Hawaii's limits, which - are never lower than the federal ones, disallow none either; keeping the - federal amount then also passes through a deduction supplied directly, - which the federal limits have already reduced, rather than reducing it - again. For the same reason the result is never below the federal amount, - which the worksheet's rounding could otherwise undercut by a fraction of - a percent. + The law does not prescribe allocation rounding, so the N-11 / 2017 + Publication 936 worksheet supplies a gap-fill: line 11 rounds the share + to three places, and line 12 multiplies interest paid by that share. + Interest paid (line 10) is the federally deductible and non-deductible + parts together. + + PE separately floors that worksheet result at its federal deduction. + This safeguard is PE's chosen approximation, not worksheet line 12; it + prevents rounded Hawaii interest from falling below PE's unrounded + federal calculation. When federal law disallows none of the interest, + keeping the federal amount also passes through a deduction supplied + directly, which the federal limits have already reduced. """ hawaii = (deductible + non_deductible) * deductible_share return where(non_deductible > 0, max_(hawaii, deductible), deductible) @@ -75,17 +80,31 @@ class hi_mortgage_interest_deduction(Variable): "incurred: interest on up to $1,000,000 of home acquisition debt plus " "$100,000 of home equity debt ($500,000 and $50,000 for married " "filing separately), rather than the federal $750,000 limit for debt " - "incurred after December 15, 2017." + "incurred after December 15, 2017. The N-11 / Publication 936 " + "worksheet fills the law's allocation-rounding gap; PE separately " + "preserves its federal deduction as a safeguard." ) reference = ( - # HRS 235-2.4(j)(3) - "https://data.capitol.hawaii.gov/hrscurrent/Vol04_Ch0201-0257/HRS0235/HRS_0235-0002_0004.htm", + # HRS 235-2.4(j)(3), 235-2.5(b) and 235-3(a): retained limits, + # authority to adopt Treasury regulations and conformity of forms. + # PDF pages 11, 16-17. + "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=11", # Act 35, SLH 2026, HRS 235-2.4(i)(3) - "https://data.capitol.hawaii.gov/sessions/session2026/bills/HB2329_CD1_.pdf#page=20", + "https://data.capitol.hawaii.gov/sessions/session2026/bills/GM1135_.PDF#page=21", + # HAR 18-235-2.3(a)(3) adopts federal regulations through January 1, + # 1979, before the 1987 temporary mortgage-interest regulation. + "https://files.hawaii.gov/tax/legal/har/har_235.pdf#page=15", + # Treasury Regulation 1.163-10T(c)-(g): alternative allocation + # methods without prescribed decimal rounding. PDF pages 3-4, 6. + "https://www.govinfo.gov/content/pkg/CFR-2025-title26-vol3/pdf/CFR-2025-title26-vol3-sec1-163-10T.pdf#page=3", + # CCA 201201017 (nonprecedential): reasonable allocation methods + # and approximations under the later debt limits. + "https://www.irs.gov/pub/irs-wd/1201017.pdf#page=5", # 2025 N-11 instructions, line 21c "https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=17", # 2017 Publication 936, Figure A note 3 (PDF page 3), home equity debt - # limit (PDF page 10) and Table 1 (PDF page 11) + # limit (PDF page 10) and Table 1 (PDF page 11, including the + # three-place rounding and multiplication, without PE's floor). "https://www.irs.gov/pub/irs-prior/p936--2017.pdf#page=3", ) defined_for = StateCode.HI @@ -118,11 +137,11 @@ def formula(tax_unit, period, parameters): p.home_equity_debt_limit[filing_status], ) # Lines 9 and 11. The 2025 N-11 instructions send filers to this - # worksheet, which rounds the ratio; PolicyEngine's federal - # calculation uses the exact ratio. + # worksheet, which supplies the rounding gap-fill; PolicyEngine's + # federal calculation uses the unrounded ratio. total_debt = grandfathered_debt + other_debt deductible_share = _hi_deductible_share(qualified_loan_limit, total_debt) - # Lines 10 and 12. + # Worksheet lines 10 and 12, with PE's separate federal-floor safeguard. return _hi_allowed_interest( add(tax_unit, period, ["deductible_mortgage_interest"]), add(tax_unit, period, ["non_deductible_mortgage_interest"]), From d830def93a4af1d8fba4ae7b658cdc4ff8c30d2a Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 22 Oct 2026 03:44:21 -0400 Subject: [PATCH 5/5] Test Hawaii's limit boundaries and every filing status - Five 2025 cases on 2020 debt, hand-checked from 2017 Publication 936 Table 1: exactly $1,100,000 (line 8 equals line 9, all deductible), $1,101,000 (.999), a separate filer at exactly $550,000, and head of household and surviving spouse at $1,500,000 (.733). A comment notes that three-place rounding keeps the share at 1.000 until total debt passes about $1,100,550. - The property test reads the Hawaii and federal limits from the parameter tree for all five filing statuses in 2018, 2025 and 2026, ties the Hawaii parameters to the statute's dollar amounts, and checks the 1.000 share threshold at $1,100,550 and $1,100,551 in single and double precision. - The ratio-rounding candidate now says what each part follows: the federal, Alabama and North Carolina amounts use the exact ratio; Hawaii rounds as a worksheet gap-fill and floors at the federal amount. It drops the stale "not yet on main" note and lists the code. Co-Authored-By: Claude Opus 5.5 --- .../test_hi_mortgage_debt_limit_invariants.py | 61 +++++++++++++- .../hi_mortgage_interest_deduction.yaml | 82 +++++++++++++++++++ source_conflicts/candidates.yaml | 29 +++++-- 3 files changed, 159 insertions(+), 13 deletions(-) diff --git a/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py b/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py index 964cef54ad9..000d445a40e 100644 --- a/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py +++ b/policyengine_us/tests/core/test_hi_mortgage_debt_limit_invariants.py @@ -37,6 +37,11 @@ otherwise equals worksheet interest paid times line 11, floored by PE at the federal amount. +The limits are read from the Hawaii and federal parameters for every filing +status in 2018, 2025 and 2026, so the properties hold for the values the model +uses. A separate check ties the Hawaii parameters to the statute's dollar +amounts. + These are for-all properties of the helpers, which YAML cannot state. The policy outcomes and interest proration are checked in hi_mortgage_interest_deduction.yaml. @@ -46,6 +51,7 @@ from hypothesis import given, settings from hypothesis import strategies as st +from policyengine_us.system import system from policyengine_us.variables.gov.states.hi.tax.income.deductions.itemized.hi_mortgage_interest_deduction import ( _hi_allowed_interest, _hi_deductible_share, @@ -58,11 +64,39 @@ GRANDFATHERED_YEAR = 1987 PRE_TCJA_YEAR = 2017 -# Filing status -> ((Hawaii acquisition limit, Hawaii home equity limit), -# (federal limit for debt from 2017 or earlier, federal limit for later debt)). +FILING_STATUSES = ( + "SINGLE", + "JOINT", + "SEPARATE", + "HEAD_OF_HOUSEHOLD", + "SURVIVING_SPOUSE", +) +TAX_YEARS = (2018, 2025, 2026) + + +def parameter_limits(year, filing_status): + """((Hawaii acquisition limit, Hawaii home equity limit), (federal limit + for debt from 2017 or earlier, federal limit for later debt)).""" + p = system.parameters(f"{year}-01-01") + hawaii = p.gov.states.hi.tax.income.deductions.itemized.interest + federal = p.gov.irs.deductions.itemized.interest.mortgage + return ( + ( + float(hawaii.home_acquisition_debt_limit[filing_status]), + float(hawaii.home_equity_debt_limit[filing_status]), + ), + ( + float(federal.pre_tcja_cap[filing_status]), + float(federal.cap[filing_status]), + ), + ) + + +# (tax year, filing status) -> limits, read once from the parameter tree. LIMITS = { - "other": ((1_000_000, 100_000), (1_000_000, 750_000)), - "separate": ((500_000, 50_000), (500_000, 375_000)), + (year, filing_status): parameter_limits(year, filing_status) + for year in TAX_YEARS + for filing_status in FILING_STATUSES } TOLERANCE = 0.01 @@ -142,6 +176,17 @@ def federal_limit(loan_list, limits): SETTINGS = dict(max_examples=400, deadline=None, derandomize=True) +def test_hawaii_limits_are_the_statutory_amounts(): + # IRC 163(h)(3)(B)(ii) and (C)(ii), kept by HRS 235-2.4(j)(3): $1,000,000 + # of acquisition debt and $100,000 of home equity debt, halved for a + # married individual filing a separate return. + for (year, filing_status), (hawaii_limits, _) in LIMITS.items(): + expected = ( + (500_000, 50_000) if filing_status == "SEPARATE" else (1_000_000, 100_000) + ) + assert hawaii_limits == expected, (year, filing_status) + + @settings(**SETTINGS) @given(first=loans, second=loans, status=st.sampled_from(sorted(LIMITS))) def test_hawaii_qualified_loan_limit_properties(first, second, status): @@ -230,3 +275,11 @@ def test_hawaii_share_rounds_half_up_to_three_places(): np.array([1_128_784], dtype=np.float32), ) np.testing.assert_array_equal(near_tie, [0.974]) + # The share stays 1.000 until total debt passes $1,100,000 / .9995 = + # $1,100,550.28, in single precision too. + for dtype in (np.float32, np.float64): + boundary = _hi_deductible_share( + np.full(4, 1_100_000, dtype=dtype), + np.array([1_100_000, 1_100_550, 1_100_551, 1_101_000], dtype=dtype), + ) + np.testing.assert_array_equal(boundary, [1.0, 1.0, 0.999, 0.999]) diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml index 339fd96e2c2..c024fda7946 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.yaml @@ -127,6 +127,88 @@ # $40,000 x .367. hi_mortgage_interest_deduction: 14_680 +- name: Debt exactly at Hawaii's $1,100,000 limit is fully deductible + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: SINGLE + home_mortgage_interest: 44_000 + first_home_mortgage_balance: 1_100_000 + first_home_mortgage_origination_year: 2020 + output: + # Federal: $44,000 x $750,000 / $1,100,000 = $30,000. + deductible_mortgage_interest: 30_000 + # Hawaii: line 6 = $1,000,000; line 7 = min($100,000, $100,000); line 8 + # = $1,100,000 equals line 9, so "stop here": all $44,000 is deductible. + hi_mortgage_interest_deduction: 44_000 + +- name: Debt just over Hawaii's $1,100,000 limit takes the rounded share + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: SINGLE + home_mortgage_interest: 44_000 + first_home_mortgage_balance: 1_101_000 + first_home_mortgage_origination_year: 2020 + output: + # Federal: $44,000 x $750,000 / $1,101,000 = $29,972.75. + deductible_mortgage_interest: 29_972.75 + # Hawaii: line 8 = $1,100,000 < line 9 = $1,101,000; line 11 = + # .99909 rounds to .999; $44,000 x .999. Three-place rounding keeps the + # share at 1.000 until total debt passes about $1,100,550 ($1,100,000 / + # .9995). + hi_mortgage_interest_deduction: 43_956 + +- name: A separate filer's debt exactly at Hawaii's $550,000 limit is fully deductible + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: SEPARATE + home_mortgage_interest: 22_000 + first_home_mortgage_balance: 550_000 + first_home_mortgage_origination_year: 2020 + output: + # Federal: $22,000 x $375,000 / $550,000 = $15,000. + deductible_mortgage_interest: 15_000 + # Hawaii: line 6 = $500,000; line 7 = min($50,000, $50,000); line 8 = + # $550,000 equals line 9, so all $22,000 is deductible. + hi_mortgage_interest_deduction: 22_000 + +- name: A head of household uses the $1,000,000 and $100,000 limits + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: HEAD_OF_HOUSEHOLD + home_mortgage_interest: 40_000 + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 2020 + output: + # Federal: $40,000 x $750,000 / $1,500,000 = $20,000. + deductible_mortgage_interest: 20_000 + # Hawaii: line 11 = ($1,000,000 + $100,000) / $1,500,000 = .733; + # $40,000 x .733. + hi_mortgage_interest_deduction: 29_320 + +- name: A surviving spouse uses the $1,000,000 and $100,000 limits + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: SURVIVING_SPOUSE + home_mortgage_interest: 40_000 + first_home_mortgage_balance: 1_500_000 + first_home_mortgage_origination_year: 2020 + output: + # Federal: $40,000 x $750,000 / $1,500,000 = $20,000. + deductible_mortgage_interest: 20_000 + # Hawaii: line 11 = ($1,000,000 + $100,000) / $1,500,000 = .733; + # $40,000 x .733. + hi_mortgage_interest_deduction: 29_320 + - name: Hawaii keeps its limits in 2026 under Act 35 period: 2026 absolute_error_margin: 0.01 diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index cea0e7bcead..5fa3f5971a4 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -5912,30 +5912,41 @@ candidates: - id: us:mortgage-interest-limit-ratio-rounding program: income-tax years: - from: 2025 - to: 2025 + from: 2018 type: tax question: Should the deductible share of mortgage interest above the debt limit use the exact ratio or the ratio rounded to three places in Table 1? sources: - kind: statute citation: 26 U.S.C. 163(h)(3) - url: null + url: https://www.law.cornell.edu/uscode/text/26/163#h_3 - kind: worksheet citation: 2025 Pub. 936 Table 1, line 14 - url: null + url: https://www.irs.gov/pub/irs-prior/p936--2025.pdf#page=14 + - kind: worksheet + citation: 2017 Pub. 936 Table 1, line 11, which the 2025 Hawaii N-11 instructions (line 21c) direct filers to + url: https://www.irs.gov/pub/irs-prior/p936--2017.pdf#page=11 readings: statute: Section 163(h)(3) does not specify rounding of the deductible-interest ratio. - other: Publication 936 Table 1 line 14 rounds the ratio to three decimal places. - example: A 2016 joint example gives $26,666.67 using the exact ratio versus $26,680 using the rounded ratio. + other: Publication 936 Table 1 (2025 line 14; 2017 line 11) rounds the ratio to three decimal places. + example: A 2025 joint return with $40,000 of interest on $1,500,000 of 2016 debt gives $26,666.67 using the exact + ratio versus $26,680 using the ratio rounded to .667. pe_us: follows: unknown - code: [] + code: + - policyengine_us/variables/household/expense/tax_unit/mortgage_interest_structure.py + - policyengine_us/variables/gov/states/al/tax/income/deductions/itemized/al_investment_interest.py + - policyengine_us/variables/gov/states/nc/tax/income/deductions/nc_mortgage_and_property_tax_before_limitation.py + - policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_mortgage_interest_deduction.py prs: - https://github.com/PolicyEngine/policyengine-us/pull/10274 state: unreviewed - note: 'This is a disputed worksheet gap-fill; PE-US uses the exact ratio, and the supplied worked example is for - 2016 rather than the cited 2025 worksheet. Code not yet on main: deductible_mortgage_interest.' + note: 'PE-US follows two conventions, so follows stays unknown until one is picked. The federal deduction + (deductible_mortgage_interest_tax_unit) uses the exact ratio, and Alabama and North Carolina read that federal + amount. Hawaii (hi_mortgage_interest_deduction) follows the 2017 worksheet as a gap-fill, rounding line 11 to + three places, and floors the result at the federal deduction, a PE safeguard that binds only when the rounded + share falls below the exact federal share. Settling the federal reading (rulespec-us#1413) and using it for + both would make that floor unnecessary.' - id: us:niit-excess-business-loss-addback program: income-tax years: