From 0750f6d69433392d76e8b8c9e258bac8bd2521aa Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 14:56:16 +0000 Subject: [PATCH 1/3] Add failing 2026 section 68 tests for MD, ME and NM itemized deductions Each state starts from federal itemized deductions, which from 2026 the P.L. 119-21 section 68 reduction limits; main uses the pre-reduction total. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LwQq6JzbnbD1RtbxQx1ds6 --- .../deductions/md_itemized_deductions.yaml | 88 +++++++++++++++++++ .../me_itemized_deductions_pre_phaseout.yaml | 87 ++++++++++++++++++ .../deductions/nm_itemized_deductions.yaml | 37 ++++++++ .../income/deductions/nm_salt_add_back.yaml | 21 +++++ 4 files changed, 233 insertions(+) diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/tax/income/deductions/md_itemized_deductions.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/tax/income/deductions/md_itemized_deductions.yaml index b35851ee477..236461760b2 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/tax/income/deductions/md_itemized_deductions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/tax/income/deductions/md_itemized_deductions.yaml @@ -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 diff --git a/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/taxable_income/deductions/me_itemized_deductions_pre_phaseout.yaml b/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/taxable_income/deductions/me_itemized_deductions_pre_phaseout.yaml index 046870d43b8..b5f92694661 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/taxable_income/deductions/me_itemized_deductions_pre_phaseout.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/taxable_income/deductions/me_itemized_deductions_pre_phaseout.yaml @@ -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 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/deductions/nm_itemized_deductions.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/deductions/nm_itemized_deductions.yaml index 5c2e7716132..5178990e99b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/deductions/nm_itemized_deductions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/deductions/nm_itemized_deductions.yaml @@ -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 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/deductions/nm_salt_add_back.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/deductions/nm_salt_add_back.yaml index fb2d03d2c83..96d3dd0de4c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/deductions/nm_salt_add_back.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/deductions/nm_salt_add_back.yaml @@ -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 From 896192cfa24fd748df959da703ccc46debf3a418 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 15:16:02 +0000 Subject: [PATCH 2/3] Apply the 2026 section 68 reduction in MD, ME and NM itemized deductions Each state starts from federal itemized deductions allowed after section 68 (Md. Tax-General 10-218(b), 36 M.R.S. 5125(1), NMSA 7-2-2(N)(2)), but main used the pre-reduction total, overstating deductions for high-income itemizers from 2026. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LwQq6JzbnbD1RtbxQx1ds6 --- changelog.d/section-68-2026-md-me-nm.fixed.md | 1 + .../deductions/md_itemized_deductions.py | 19 +++++++++++ .../me_itemized_deductions_pre_phaseout.py | 33 +++++++++++++++++++ .../deductions/nm_itemized_deductions.py | 14 ++++++-- .../tax/income/deductions/nm_salt_add_back.py | 7 ++++ 5 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 changelog.d/section-68-2026-md-me-nm.fixed.md diff --git a/changelog.d/section-68-2026-md-me-nm.fixed.md b/changelog.d/section-68-2026-md-me-nm.fixed.md new file mode 100644 index 00000000000..1f9ed3aed58 --- /dev/null +++ b/changelog.d/section-68-2026-md-me-nm.fixed.md @@ -0,0 +1 @@ +Apply the 2026 federal section 68 itemized deduction reduction in Maryland, Maine and New Mexico itemized deductions. diff --git a/policyengine_us/variables/gov/states/md/tax/income/deductions/md_itemized_deductions.py b/policyengine_us/variables/gov/states/md/tax/income/deductions/md_itemized_deductions.py index 1284b66fabb..92fd78ea5d4 100644 --- a/policyengine_us/variables/gov/states/md/tax/income/deductions/md_itemized_deductions.py +++ b/policyengine_us/variables/gov/states/md/tax/income/deductions/md_itemized_deductions.py @@ -12,6 +12,13 @@ class md_itemized_deductions(Variable): "https://interactive.marylandtaxes.gov/Individuals/iFile_ChooseForm/PriorYearForms/Resident_Booklet_2021.pdf#page=5", "https://interactive.marylandtaxes.gov/Individuals/iFile_ChooseForm/PriorYearForms/Resident_Booklet_2022.pdf#page=5", "https://mgaleg.maryland.gov/2025RS/Chapters_noln/CH_604_hb0352e.pdf#page=167", # Maryland House Bill 352 - Budget Reconciliation and Financing Act of 2025 + # Tax-General 10-218(b)(1) and (b)(3): federal itemized deductions, + # less income taxes net of their pro rata share of the section 68 + # reduction. + "https://mgaleg.maryland.gov/mgawebsite/Laws/StatuteText?article=gtg§ion=10-218&enactments=false", + # Section 68 as amended by P.L. 119-21, sec. 70111, from 2026. + # PDF pages 94-95 + "https://www.govinfo.gov/content/pkg/PLAW-119publ21/pdf/PLAW-119publ21.pdf#page=94", ] defined_for = StateCode.MD @@ -22,6 +29,18 @@ def formula(tax_unit, period, parameters): period, ["itemized_deductions_less_salt", "capped_property_taxes"], ) + if parameters(period).gov.irs.deductions.itemized.limitation.obbb.applies: + # Tax-General 10-218(b)(1) starts from federal itemized deductions + # after the section 68 reduction R, and 10-218(b)(3) subtracts + # the income taxes I net of their pro rata share of R: + # T - R - I + R * I / T, where T is the pre-reduction total. + # With I = T - base this equals base * (1 - R / T). + total = tax_unit("total_itemized_taxable_income_deductions", period) + reduction = tax_unit("itemized_taxable_income_deductions_reduction", period) + mask = total > 0 + reduction_share = np.zeros_like(total) + reduction_share[mask] = reduction[mask] / total[mask] + base_itemized = base_itemized * (1 - reduction_share) # Apply income-based phase-out if applicable p = parameters(period).gov.states.md.tax.income.deductions.itemized diff --git a/policyengine_us/variables/gov/states/me/tax/income/taxable_income/deductions/me_itemized_deductions_pre_phaseout.py b/policyengine_us/variables/gov/states/me/tax/income/taxable_income/deductions/me_itemized_deductions_pre_phaseout.py index 850de06be97..674572eedc8 100644 --- a/policyengine_us/variables/gov/states/me/tax/income/taxable_income/deductions/me_itemized_deductions_pre_phaseout.py +++ b/policyengine_us/variables/gov/states/me/tax/income/taxable_income/deductions/me_itemized_deductions_pre_phaseout.py @@ -10,6 +10,9 @@ class me_itemized_deductions_pre_phaseout(Variable): reference = ( "https://www.maine.gov/revenue/sites/maine.gov.revenue/files/inline-files/22_1040me_sched_2_ff.pdf", "https://www.mainelegislature.org/legis/statutes/36/title36sec5125.html", + # Section 68 as amended by P.L. 119-21, sec. 70111, from 2026. + # PDF pages 94-95 + "https://www.govinfo.gov/content/pkg/PLAW-119publ21/pdf/PLAW-119publ21.pdf#page=94", ) defined_for = StateCode.ME @@ -26,6 +29,36 @@ def formula(tax_unit, period, parameters): # Get real estate (and property) taxes. real_estate_taxes = add(tax_unit, period, ["real_estate_taxes"]) + p_limitation = parameters(period).gov.irs.deductions.itemized.limitation + if p_limitation.obbb.applies: + # 36 M.R.S. 5125(1) starts from the itemized deductions claimed + # federally, after the section 68 reduction. Allocate the uniform + # 2026 reduction pro rata to each federal component Maine + # subtracts or carves out (a deduced allocation; no 2026 agency + # rule was verified). + total = tax_unit("total_itemized_taxable_income_deductions", period) + allowed = tax_unit("itemized_taxable_income_deductions", period) + mask = total > 0 + allowed_share = np.zeros_like(total) + allowed_share[mask] = allowed[mask] / total[mask] + deduction = deduction * allowed_share + medical_expenses = medical_expenses * allowed_share + # Property taxes inside the federal SALT deduction (pro rata + # within SALT) keep their section 68 reduction; those the SALT + # cap excluded are added back in full under 5125(3)(A-1). + salt = tax_unit("salt", period) + salt_deduction = tax_unit("salt_deduction", period) + deducted_property_taxes = np.zeros_like(salt) + salt_mask = salt > 0 + deducted_property_taxes[salt_mask] = ( + salt_deduction[salt_mask] + * real_estate_taxes[salt_mask] + / salt[salt_mask] + ) + real_estate_taxes = ( + real_estate_taxes - (1 - allowed_share) * deducted_property_taxes + ) + # Calculate uncapped (potential) itemized deductions. deduction_no_med = deduction - medical_expenses + real_estate_taxes diff --git a/policyengine_us/variables/gov/states/nm/tax/income/deductions/nm_itemized_deductions.py b/policyengine_us/variables/gov/states/nm/tax/income/deductions/nm_itemized_deductions.py index 0dea59a7cb8..99ab95765ad 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/deductions/nm_itemized_deductions.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/deductions/nm_itemized_deductions.py @@ -7,7 +7,17 @@ class nm_itemized_deductions(Variable): label = "New Mexico itemized deductions" unit = USD definition_period = YEAR - reference = "https://klvg4oyd4j.execute-api.us-west-2.amazonaws.com/prod/PublicFiles/34821a9573ca43e7b06dfad20f5183fd/1afc56af-ea90-4d48-82e5-1f9aeb43255a/PITbook2022.pdf" + reference = ( + "https://klvg4oyd4j.execute-api.us-west-2.amazonaws.com/prod/PublicFiles/34821a9573ca43e7b06dfad20f5183fd/1afc56af-ea90-4d48-82e5-1f9aeb43255a/PITbook2022.pdf", + # NMSA 1978 7-2-2(N)(2): itemized deductions allowed under the IRC. + "https://www.nmlegis.gov/Sessions/23%20Regular/final/HB0368.pdf#page=7", + "https://realfile.tax.newmexico.gov/2025pit-1-ins.pdf#page=25", + ) defined_for = StateCode.NM - adds = ["total_itemized_taxable_income_deductions"] + def formula(tax_unit, period, parameters): + p = parameters(period).gov.irs.deductions.itemized.limitation + if p.obbb.applies: + # From 2026 the allowed federal total is net of section 68. + return tax_unit("itemized_taxable_income_deductions", period) + return tax_unit("total_itemized_taxable_income_deductions", period) diff --git a/policyengine_us/variables/gov/states/nm/tax/income/deductions/nm_salt_add_back.py b/policyengine_us/variables/gov/states/nm/tax/income/deductions/nm_salt_add_back.py index e8b387a9976..076dac058b7 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/deductions/nm_salt_add_back.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/deductions/nm_salt_add_back.py @@ -11,6 +11,7 @@ class nm_salt_add_back(Variable): "https://klvg4oyd4j.execute-api.us-west-2.amazonaws.com/prod/PublicFiles/34821a9573ca43e7b06dfad20f5183fd/1afc56af-ea90-4d48-82e5-1f9aeb43255a/PITbook2022.pdf#page=28", "https://law.justia.com/codes/new-mexico/chapter-7/article-2/section-7-2-2/", "https://klvg4oyd4j.execute-api.us-west-2.amazonaws.com/prod/PublicFiles/34821a9573ca43e7b06dfad20f5183fd/1afc56af-ea90-4d48-82e5-1f9aeb43255a/PITbook2022.pdf#page=28", + "https://realfile.tax.newmexico.gov/2025pit-1-ins.pdf#page=24", ) defined_for = StateCode.NM @@ -40,6 +41,12 @@ def formula(tax_unit, period, parameters): standard_deduction = tax_unit("standard_deduction", period) itm_deds_less_salt = tax_unit("itemized_deductions_less_salt", period) us_itemized_deductions = itm_deds_less_salt + salt_claimed + if p.itemized.limitation.obbb.applies: + # From 2026, compare the allowed federal total, net of the + # section 68 reduction, with the standard deduction. + us_itemized_deductions = tax_unit( + "itemized_taxable_income_deductions", period + ) item_deds = max_(us_itemized_deductions - standard_deduction, 0) nm_item = min_(salt, item_deds) From 6409fc53feab366a21f5f04ba8c981291091d76a Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 15:45:30 +0000 Subject: [PATCH 3/3] Add a Hypothesis property test for the 2026 section 68 state identities YAML cases fix single points; this checks the MD, ME and NM identities over random totals and positive reductions in one simulation per example. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LwQq6JzbnbD1RtbxQx1ds6 --- .../test_state_itemized_section_68_2026.py | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 policyengine_us/tests/test_state_itemized_section_68_2026.py diff --git a/policyengine_us/tests/test_state_itemized_section_68_2026.py b/policyengine_us/tests/test_state_itemized_section_68_2026.py new file mode 100644 index 00000000000..efbbf940ab9 --- /dev/null +++ b/policyengine_us/tests/test_state_itemized_section_68_2026.py @@ -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)