Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/section-68-2026-md-me-nm.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Apply the 2026 federal section 68 itemized deduction reduction in Maryland, Maine and New Mexico itemized deductions.
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,91 @@
# Phase-out reduction: $150,000 * 7.5% = $11,250
# Final deductions: $38,000 - $11,250 = $26,750
md_itemized_deductions: 26_750

- name: Maryland applies the 2026 federal section 68 reduction before its own phaseout.
period: 2026
absolute_error_margin: 0.01
input:
state_code: MD
filing_status: SINGLE
adjusted_gross_income: 1_000_000
total_itemized_taxable_income_deductions: 100_000
itemized_deductions_less_salt: 100_000
itemized_taxable_income_deductions_reduction: 5_405.405405
salt_deduction: 0
capped_property_taxes: 0
output:
# Tax-General 10-218(b)(1): federal itemized deductions after section 68.
# No income taxes to remove, so the whole reduction stays.
# $100,000 - $5,405.41 = $94,594.59.
# Maryland phaseout: ($1,000,000 - $200,000) * 7.5% = $60,000.
# $94,594.59 - $60,000 = $34,594.59.
md_itemized_deductions: 34_594.594595

- name: Maryland restores the income-tax share of the 2026 section 68 reduction.
period: 2026
absolute_error_margin: 0.01
input:
state_code: MD
filing_status: SINGLE
adjusted_gross_income: 1_000_000
total_itemized_taxable_income_deductions: 110_000
itemized_deductions_less_salt: 100_000
itemized_taxable_income_deductions_reduction: 5_945.945946
salt_deduction: 10_000
capped_property_taxes: 0
output:
# Tax-General 10-218(b)(3): the $10,000 of SALT is all income tax.
# Pro rata restoration: $5,945.95 * $10,000 / $110,000 = $540.54.
# $110,000 - $5,945.95 - $10,000 + $540.54 = $94,594.59.
# Less the $60,000 Maryland phaseout = $34,594.59.
md_itemized_deductions: 34_594.594595

- name: Maryland deduction is unchanged in 2026 when the section 68 reduction is zero.
period: 2026
absolute_error_margin: 0.01
input:
state_code: MD
filing_status: SINGLE
adjusted_gross_income: 150_000
total_itemized_taxable_income_deductions: 40_000
itemized_deductions_less_salt: 30_000
itemized_taxable_income_deductions_reduction: 0
salt_deduction: 10_000
capped_property_taxes: 4_000
output:
# $30,000 + $4,000 property taxes; AGI is below the $200,000 phaseout.
md_itemized_deductions: 34_000

- name: Maryland keeps property taxes but scales them by the 2026 section 68 reduction.
period: 2026
absolute_error_margin: 0.01
input:
state_code: MD
filing_status: JOINT
adjusted_gross_income: 400_000
total_itemized_taxable_income_deductions: 74_000
itemized_deductions_less_salt: 34_000
itemized_taxable_income_deductions_reduction: 3_700
salt_deduction: 40_000
capped_property_taxes: 15_000
output:
# Income-tax component: $74,000 - $34,000 - $15,000 = $25,000.
# $74,000 - $3,700 - $25,000 + $3,700 * $25,000 / $74,000 = $46,550.
# Equivalently $49,000 * (1 - $3,700 / $74,000) = $46,550.
# Phaseout: ($400,000 - $200,000) * 7.5% = $15,000; $46,550 - $15,000.
md_itemized_deductions: 31_550

- name: Maryland 2026 deduction is zero when there are no itemized deductions.
period: 2026
absolute_error_margin: 0.01
input:
state_code: MD
filing_status: SINGLE
adjusted_gross_income: 1_000_000
total_itemized_taxable_income_deductions: 0
itemized_deductions_less_salt: 0
itemized_taxable_income_deductions_reduction: 0
capped_property_taxes: 0
output:
md_itemized_deductions: 0
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,90 @@
state_code: ME
output:
me_itemized_deductions_pre_phaseout: 36_150 # cap of $31,150 + $5,000 medical expenses

- name: Maine starts with the federally limited itemized deduction in 2026.
period: 2026
absolute_error_margin: 0.01
input:
state_code: ME
filing_status: SINGLE
adjusted_gross_income: 1_000_000
me_agi: 100_000
tax_unit_itemizes: true
total_itemized_taxable_income_deductions: 20_000
itemized_deductions_less_salt: 20_000
itemized_taxable_income_deductions_reduction: 1_081.081081
salt_deduction: 0
medical_expense_deduction: 0
real_estate_taxes: 0
output:
# 36 M.R.S. 5125(1): deductions claimed federally, after section 68.
# $20,000 - $20,000 * 2/37 = $18,918.92, below the cap and above the
# $15,700 standard deduction; Maine AGI is below the $102,250 phaseout.
me_itemized_deductions_pre_phaseout: 18_918.918919
me_deductions: 18_918.918919

- name: An additional federal deduction keeps the section 68 reduction in Maine.
period: 2026
absolute_error_margin: 0.01
input:
state_code: ME
filing_status: SINGLE
adjusted_gross_income: 1_000_000
me_agi: 100_000
tax_unit_itemizes: true
total_itemized_taxable_income_deductions: 30_000
itemized_deductions_less_salt: 30_000
itemized_taxable_income_deductions_reduction: 1_621.621622
salt_deduction: 0
medical_expense_deduction: 0
real_estate_taxes: 0
output:
# $30,000 - $30,000 * 2/37 = $28,378.38.
me_itemized_deductions_pre_phaseout: 28_378.378378
me_deductions: 28_378.378378

- name: Maine scales SALT, property and medical components by the 2026 section 68 ratio.
period: 2026
absolute_error_margin: 0.01
input:
state_code: ME
filing_status: SINGLE
tax_unit_itemizes: true
total_itemized_taxable_income_deductions: 50_000
itemized_deductions_less_salt: 40_000
itemized_taxable_income_deductions_reduction: 2_702.702703
salt_deduction: 10_000
state_and_local_sales_or_income_tax: 6_000
medical_expense_deduction: 10_000
real_estate_taxes: 4_000
output:
# Deduced proportional allocation: q = $47,297.30 / $50,000 = 35/37.
# Non-SALT deductions after section 68: $40,000 * q = $37,837.84.
# Less medical $10,000 * q = $9,459.46, giving $28,378.38.
# Property taxes: all $4,000 was within the SALT deduction, so the
# add-back keeps the section 68 share: $4,000 * q = $3,783.78.
# Capped part: $32,162.16 (below the cap); plus medical $9,459.46.
me_itemized_deductions_pre_phaseout: 41_621.621622

- name: Maine adds back property taxes the federal SALT cap excluded in full in 2026.
period: 2026
absolute_error_margin: 0.01
input:
state_code: ME
filing_status: SINGLE
tax_unit_itemizes: true
total_itemized_taxable_income_deductions: 27_000
itemized_deductions_less_salt: 17_000
itemized_taxable_income_deductions_reduction: 1_000
salt_deduction: 10_000
state_and_local_sales_or_income_tax: 0
medical_expense_deduction: 0
real_estate_taxes: 12_000
output:
# q = $26,000 / $27,000. Non-SALT: $17,000 * q = $16,370.37.
# Property in the SALT deduction, pro rata: $10,000 * $12,000 / $12,000
# = $10,000, kept at $10,000 * q = $9,629.63. The $2,000 the SALT cap
# excluded is added back in full under 5125(3)(A-1).
# $16,370.37 + $9,629.63 + $2,000 = $28,000, below the cap.
me_itemized_deductions_pre_phaseout: 28_000
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,40 @@
total_itemized_taxable_income_deductions: 19_500
nm_itemized_deductions: 19_500
state_itemized_deductions: 19_500

- name: NM uses deductions after the full 2026 federal section 68 limitation.
period: 2026
absolute_error_margin: 0.01
input:
state_code: NM
filing_status: SINGLE
adjusted_gross_income: 2_000_000
taxable_income_before_itemized_deductions: 2_000_000
total_itemized_taxable_income_deductions: 37_000
tax_unit_itemizes: true
state_and_local_sales_or_income_tax: 0
real_estate_taxes: 0
output:
# NMSA 7-2-2(N)(2): itemized deductions allowed under the IRC.
# $37,000 * 2/37 = $2,000 section 68 reduction, leaving $35,000.
itemized_taxable_income_deductions_reduction: 2_000
itemized_taxable_income_deductions: 35_000
nm_itemized_deductions: 35_000
nm_salt_add_back: 0

- name: NM keeps the full deduction when the 2026 federal limitation is zero.
period: 2026
absolute_error_margin: 0.01
input:
state_code: NM
filing_status: SINGLE
adjusted_gross_income: 100_000
taxable_income_before_itemized_deductions: 100_000
total_itemized_taxable_income_deductions: 37_000
tax_unit_itemizes: true
state_and_local_sales_or_income_tax: 0
real_estate_taxes: 0
output:
itemized_taxable_income_deductions_reduction: 0
nm_itemized_deductions: 37_000
nm_salt_add_back: 0
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,24 @@
state_code: NM
output:
nm_salt_add_back: 0

- name: NM SALT addback compares the federally allowed 2026 total with the standard deduction.
period: 2026
absolute_error_margin: 0.01
input:
state_code: NM
filing_status: SINGLE
tax_unit_itemizes: true
adjusted_gross_income: 2_000_000
taxable_income_before_itemized_deductions: 2_000_000
total_itemized_taxable_income_deductions: 17_000
itemized_deductions_less_salt: 7_000
state_and_local_sales_or_income_tax: 10_000
real_estate_taxes: 0
standard_deduction: 16_000
output:
# Section 68: $17,000 * 2/37 = $918.92; allowed total $16,081.08.
# Excess over the standard deduction: $16,081.08 - $16,000 = $81.08.
# SALT is all income tax ($10,000), so the addback is the lesser: $81.08.
itemized_taxable_income_deductions: 16_081.081081
nm_salt_add_back: 81.081081
131 changes: 131 additions & 0 deletions policyengine_us/tests/test_state_itemized_section_68_2026.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
"""Property tests: Maryland, Maine and New Mexico itemized deductions in 2026
carry the federal section 68 reduction (P.L. 119-21, sec. 70111).

Each state starts from the federal itemized deductions allowed after section
68. For a pre-reduction federal total T > 0 and reduction 0 <= R <= T, write
q = (T - R) / T. With every input drawn independently and R > 0, these hold:

1. Maryland (Tax-General 10-218(b)(1), (b)(3)), below its phaseout threshold:
md_itemized_deductions = q * (non-SALT deductions + capped property
taxes). This equals T - R - I + R * I / T for income taxes
I = T - base, the pro rata restoration of 10-218(b)(3).
2. Maine (36 M.R.S. 5125(1)), with no SALT or medical deduction:
me_itemized_deductions_pre_phaseout = min(T - R, cap).
3. New Mexico (NMSA 7-2-2(N)(2)): nm_itemized_deductions = T - R, and the
SALT addback lies between zero and the excess of T - R over the standard
deduction.

Each identity fails on main whenever R > 0, so none passes trivially: main
uses T (or the unreduced base) and ignores R. All households share one
simulation per example, which keeps the test to a few model builds.
"""

import numpy as np
from hypothesis import HealthCheck, given, settings
from hypothesis import strategies as st

from policyengine_us import Simulation

YEAR = 2026
TOLERANCE = 0.05


def amount(low=0, high=60_000):
return st.integers(min_value=low, max_value=high)


@st.composite
def unit(draw):
base = draw(amount(1_000))
income_tax = draw(amount(0, 30_000))
total = base + income_tax
# Section 68 reduction: up to 2/37 of the total, drawn positive.
reduction = draw(st.integers(min_value=1, max_value=total * 2 // 37 + 1))
return dict(
base=base,
income_tax=income_tax,
total=total,
reduction=min(reduction, total),
standard=draw(amount(5_000, 40_000)),
)


def build(units):
people, tax_units, households = {}, {}, {}
for state in ["MD", "ME", "NM"]:
for i, u in enumerate(units):
key = f"{state}{i}"
people[key] = {"age": {YEAR: 40}}
common = {
"members": [key],
"filing_status": {YEAR: "SINGLE"},
"tax_unit_itemizes": {YEAR: True},
"adjusted_gross_income": {YEAR: 100_000},
"itemized_taxable_income_deductions_reduction": {YEAR: u["reduction"]},
"medical_expense_deduction": {YEAR: 0},
}
if state == "MD":
common.update(
total_itemized_taxable_income_deductions={YEAR: u["total"]},
itemized_deductions_less_salt={YEAR: u["base"]},
capped_property_taxes={YEAR: 0},
salt_deduction={YEAR: u["income_tax"]},
)
elif state == "ME":
common.update(
total_itemized_taxable_income_deductions={YEAR: u["total"]},
itemized_deductions_less_salt={YEAR: u["total"]},
salt_deduction={YEAR: 0},
me_agi={YEAR: 50_000},
)
else:
common.update(
total_itemized_taxable_income_deductions={YEAR: u["total"]},
itemized_deductions_less_salt={YEAR: u["base"]},
standard_deduction={YEAR: u["standard"]},
state_and_local_sales_or_income_tax={YEAR: u["income_tax"]},
)
tax_units[f"t{key}"] = common
households[f"h{key}"] = {"members": [key], "state_code": {YEAR: state}}
return Simulation(
situation={
"people": people,
"tax_units": tax_units,
"households": households,
}
)


@settings(
max_examples=15,
deadline=None,
suppress_health_check=[HealthCheck.too_slow],
)
@given(st.lists(unit(), min_size=1, max_size=6))
def test_state_itemized_deductions_carry_section_68(units):
n = len(units)
sim = build(units)
total = np.array([u["total"] for u in units], dtype=float)
reduction = np.array([u["reduction"] for u in units], dtype=float)
base = np.array([u["base"] for u in units], dtype=float)
standard = np.array([u["standard"] for u in units], dtype=float)
allowed = total - reduction
q = allowed / total

md = sim.calculate("md_itemized_deductions", YEAR)[:n]
me = sim.calculate("me_itemized_deductions_pre_phaseout", YEAR)[n : 2 * n]
nm = sim.calculate("nm_itemized_deductions", YEAR)[2 * n :]
nm_add_back = sim.calculate("nm_salt_add_back", YEAR)[2 * n :]
me_cap = sim.tax_benefit_system.parameters(
YEAR
).gov.states.me.tax.income.deductions.itemized.cap

# 1. Maryland: base scaled by q, the pro rata income-tax restoration.
np.testing.assert_allclose(md, base * q, atol=TOLERANCE)
assert np.all(md < base)
# 2. Maine: the federally allowed total, capped.
np.testing.assert_allclose(me, np.minimum(allowed, me_cap), atol=TOLERANCE)
# 3. New Mexico: the allowed total, and an addback within its bounds.
np.testing.assert_allclose(nm, allowed, atol=TOLERANCE)
assert np.all(nm_add_back >= -TOLERANCE)
assert np.all(nm_add_back <= np.maximum(allowed - standard, 0) + TOLERANCE)
Loading
Loading