Source code for taxcalc.calcfunctions

"""
Tax-Calculator functions that calculate payroll and individual income taxes.

These functions are imported into the Calculator class.

Note: the parameter_indexing_CPI_offset parameter is no longer used.
"""
# CODING-STYLE CHECKS:
# pycodestyle calcfunctions.py
# pylint --disable=locally-disabled calcfunctions.py
#
# pylint: disable=too-many-lines
# pylint: disable=invalid-name
# pylint: disable=too-many-arguments
# pylint: disable=too-many-positional-arguments
# pylint: disable=too-many-locals

import math
import numpy as np
from taxcalc.decorators import iterate_jit, JIT


[docs] def BenefitPrograms(calc): """ Aggregate per-record government cost and consumption value of the non-tax benefit programs tracked by Tax-Calculator and write them to the Records arrays `benefit_cost_total` and `benefit_value_total`. This function does not implement any IRS form; it is a model-internal aggregator. For each program a `BEN_*_repeal` policy parameter, when set, zeroes the program's per-record array before the sums are formed (UBI has no repeal flag because UBI is itself a reform construct that is zero under current law). In-kind programs are weighted by a `BEN_*_value` consumption parameter; cash programs (SSI, OASDI = e02400, UI = e02300, UBI) are valued at full dollar cost. `benefit_value_total` is consumed downstream by ExpandIncome via `expanded_income`. Parameters ---------- calc: Calculator object calc represents the reform while self represents the baseline Returns ------- None: The function modifies calc """ # programs aggregated below, in a fixed order: # (record_array_name, repeal_param_or_None, value_param_or_None) # repeal_param=None ==> program has no repeal flag (UBI only). # value_param=None ==> cash benefit, valued at full dollar cost. programs = ( ('housing_ben', 'BEN_housing_repeal', 'BEN_housing_value'), ('ssi_ben', 'BEN_ssi_repeal', None), ('snap_ben', 'BEN_snap_repeal', 'BEN_snap_value'), ('tanf_ben', 'BEN_tanf_repeal', 'BEN_tanf_value'), ('vet_ben', 'BEN_vet_repeal', 'BEN_vet_value'), ('wic_ben', 'BEN_wic_repeal', 'BEN_wic_value'), ('mcare_ben', 'BEN_mcare_repeal', 'BEN_mcare_value'), ('mcaid_ben', 'BEN_mcaid_repeal', 'BEN_mcaid_value'), ('e02400', 'BEN_oasdi_repeal', None), # OASDI Social Security ('e02300', 'BEN_ui_repeal', None), # Unemployment Insurance ('ubi', None, None), # UBI reform construct ('other_ben', 'BEN_other_repeal', 'BEN_other_value'), ) # zero out benefits delivered by repealed programs zero = np.zeros(calc.array_len) for name, repeal_param, _ in programs: if repeal_param is not None and calc.policy_param(repeal_param): calc.array(name, zero) # calculate government cost of all benefits cost = sum(calc.array(name) for name, _, _ in programs) calc.array('benefit_cost_total', cost) # calculate consumption value of all benefits # (cash benefits are valued at full dollar cost) value = sum( calc.array(name) if vparam is None else calc.array(name) * calc.consump_param(vparam) for name, _, vparam in programs ) calc.array('benefit_value_total', value)
@iterate_jit(nopython=True) def EI_PayrollTax(SS_Earnings_c, e00200p, e00200s, pencon_p, pencon_s, FICA_ss_trt_employer, FICA_ss_trt_employee, FICA_mc_trt_employer, FICA_mc_trt_employee, ALD_SelfEmploymentTax_hc, SS_Earnings_thd, SECA_Earnings_thd, e00900p, e00900s, e02100p, e02100s, k1bx14p, k1bx14s, payrolltax, ptax_was, setax, c03260, ptax_oasdi, sey, earned, earned_p, earned_s, was_plus_sey_p, was_plus_sey_s): """ Compute part of total OASDI+HI payroll taxes and earned income variables. Parameters ---------- SS_Earnings_c: float Maximum taxable earnings for Social Security. Individual earnings below this amount are subjected to OASDI payroll tax. This parameter is indexed by rate of growth in average wages not by the price inflation rate. e00200p: float Wages, salaries,tips/otime for taxpayer net of pension contributions e00200s: float Wages, salaries, tips/otime for spouse net of pension contributions pencon_p: float Contributions to defined-contribution pension plans for taxpayer pencon_s: float Contributions to defined-contribution pension plans for spouse FICA_ss_trt_employer: float Employer side social security payroll tax rate FICA_ss_trt_employee: float Employee side social security payroll tax rate FICA_mc_trt_employer: float Employer side medicare payroll tax rate FICA_mc_trt_employee: float Employee side medicare payroll tax rate ALD_SelfEmploymentTax_hc: float Adjustment for self-employment tax haircut If greater than zero, reduces the employer equivalent portion of self-employment adjustment Final adjustment amount = (1-Haircut)*SelfEmploymentTaxAdjustment SS_Earnings_thd: float Additional taxable earnings threshold for Social Security Individual earnings above this threshold are subjected to OASDI payroll tax, in addtion to earnings below the maximum taxable earnings threshold. SECA_Earnings_thd: float Threshold value for self-employment income below which there is no SECA tax liability e00900p: float Schedule C business net profit/loss for taxpayer e00900s: float Schedule C business net profit/loss for spouse e02100p: float Farm net income/loss for taxpayer e02100s: float Farm net income/loss for spouse k1bx14p: float Partner self-employment earnings/loss for taxpayer (included in e26270 total) k1bx14s: float Partner self-employment earnings/loss for spouse (included in e26270 total) payrolltax: float Total (employee and employer) payroll tax liability payrolltax = ptax_was ptax_was: float Employee and employer OASDI plus HI FICA tax setax: float Self-employment tax (included in othertaxes and iitax) c03260: float Deductible part of self-employment tax c03260 = (1 - ALD_SelfEmploymentTax_hc) * 0.5 * setax ptax_oasdi: float Employee and employer OASDI FICA tax plus self employment tax Excludes HI FICA so positive ptax_oasdi is less than ptax_was + setax sey: float Total self-employment income for filing unit earned: float Earned income for filing unit earned_p: float Earned income for taxpayer earned_s: float Earned income for spouse was_plus_sey_p: float Wage and salary income plus taxable self employment income for taxpayer was_plus_sey_s: float Wage and salary income plus taxable self employment income for spouse Returns ------- sey: float Total self-employment income for filing unit payrolltax: float Total (employee and employer) payroll tax liability payrolltax = ptax_was ptax_was: float Employee and employer OASDI plus HI FICA tax setax: float Self-employment tax (included in othertaxes and iitax) c03260: float Deductible part of self-employment tax c03260 = (1 - ALD_SelfEmploymentTax_hc) * 0.5 * setax ptax_oasdi: float Employee and employer OASDI FICA tax plus self employment tax Excludes HI FICA so positive ptax_oasdi is less than ptax_was + setax earned: float Earned income for filing unit earned_p: float Earned income for taxpayer earned_s: float Earned income for spouse was_plus_sey_p: float Wage and salary income plus taxable self employment income for taxpayer was_plus_sey_s: float Wage and salary income plus taxable self employment income for spouse """ # combined OASDI and HI FICA rates (employer + employee shares) ss_rate = FICA_ss_trt_employer + FICA_ss_trt_employee mc_rate = FICA_mc_trt_employer + FICA_mc_trt_employee # compute sey and its individual components sey_p = e00900p + e02100p + k1bx14p sey_s = e00900s + e02100s + k1bx14s sey = sey_p + sey_s # total self-employment income for filing unit # ---------- FICA on wages and salaries ---------- # compute gross wage and salary income ('was' denotes 'wage and salary') gross_ws_p = e00200p + pencon_p gross_ws_s = e00200s + pencon_s # compute taxable gross earnings for OASDI FICA txearn_was_p = min(SS_Earnings_c, gross_ws_p) txearn_was_s = min(SS_Earnings_c, gross_ws_s) # compute OASDI and HI payroll taxes on wage-and-salary income ptax_ss_ws_p = ss_rate * txearn_was_p ptax_ss_ws_s = ss_rate * txearn_was_s ptax_mc_ws_p = mc_rate * gross_ws_p ptax_mc_ws_s = mc_rate * gross_ws_s ptax_was = ptax_ss_ws_p + ptax_ss_ws_s + ptax_mc_ws_p + ptax_mc_ws_s # ---------- SECA on self-employment income (Sch SE Part I) ---------- # Sch SE line 4a multiplier 0.9235, generalized to current FICA rates seca_frac = 1.0 - 0.5 * (ss_rate + mc_rate) # Sch SE line 4c (taxable net SE earnings, per spouse) net_sey_p = max(0., sey_p * seca_frac) net_sey_s = max(0., sey_s * seca_frac) # Sch SE line 9: remaining OASDI base = SS_Earnings_c - W-2 SS wages txearn_sey_p = min(net_sey_p, SS_Earnings_c - txearn_was_p) txearn_sey_s = min(net_sey_s, SS_Earnings_c - txearn_was_s) # Sch SE line 10 (OASDI portion) and line 11 (HI portion), per spouse setax_ss_p = ss_rate * txearn_sey_p setax_ss_s = ss_rate * txearn_sey_s setax_mc_p = mc_rate * net_sey_p setax_mc_s = mc_rate * net_sey_s setax_p = setax_ss_p + setax_mc_p setax_s = setax_ss_s + setax_mc_s # Sch SE line 12: zero out if filing-unit SE earnings are below the # $400 floor (Sch SE line 4: "stop; you do not owe SE tax") if sey * seca_frac > SECA_Earnings_thd: setax = setax_p + setax_s else: setax = 0.0 # ---------- Reform-only extra OASDI bracket (not on Sch SE) ---------- # extra OASDI on the portion of (wages + taxable SE) above SS_Earnings_thd extra_frac = 1.0 - 0.5 * ss_rate was_plus_sey_p = gross_ws_p + max(0., sey_p * extra_frac) was_plus_sey_s = gross_ws_s + max(0., sey_s * extra_frac) extra_ss_income_p = max(0., was_plus_sey_p - SS_Earnings_thd) extra_ss_income_s = max(0., was_plus_sey_s - SS_Earnings_thd) extra_payrolltax = ss_rate * (extra_ss_income_p + extra_ss_income_s) # filing-unit payroll tax and OASDI-only part (HI excluded from ptax_oasdi) payrolltax = ptax_was + extra_payrolltax ptax_oasdi = (ptax_ss_ws_p + ptax_ss_ws_s + setax_ss_p + setax_ss_s + extra_payrolltax) # ---------- earned-income outputs and Sch SE line 13 deduction ---------- # c03260: deductible half of SE tax (Sch SE line 13 / Sch 1 line 15), # optionally reduced by the ALD_SelfEmploymentTax_hc reform haircut c03260 = (1. - ALD_SelfEmploymentTax_hc) * 0.5 * setax earned = max(0., e00200p + e00200s + sey - c03260) earned_p = max(0., (e00200p + sey_p - (1. - ALD_SelfEmploymentTax_hc) * 0.5 * setax_p)) earned_s = max(0., (e00200s + sey_s - (1. - ALD_SelfEmploymentTax_hc) * 0.5 * setax_s)) return (sey, payrolltax, ptax_was, setax, c03260, ptax_oasdi, earned, earned_p, earned_s, was_plus_sey_p, was_plus_sey_s) @iterate_jit(nopython=True) def DependentCare(nu13, elderly_dependents, earned, MARS, ALD_Dependents_thd, ALD_Dependents_hc, ALD_Dependents_Child_c, ALD_Dependents_Elder_c, care_deduction): """ Computes the dependent-care above-the-line deduction. Reform-only construct (originally specified for the 2016 Trump campaign tax plan); no IRS form correspondence. Inert under current law because all four ALD_Dependents_* parameters default to 0.0, which forces care_deduction = 0. for every record. The income test is a cliff, not a phaseout: filing units with earned income above ALD_Dependents_thd[MARS-1] receive zero deduction. care_deduction is summed into c02900 (Sch 1 line 26) by Adj under the legacy/reform-only banner. Parameters ---------- nu13: int Number of dependents under 13 years old elderly_dependents: int Number of elderly dependents age 65+ in filing unit other than taxpayer and spouse earned: float Earned income for filing unit MARS: int Filing marital status (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) ALD_Dependents_thd: list Maximum income to qualify for dependent care deduction ALD_Dependents_hc: float Deduction for childcare costs haircut ALD_Dependents_Child_c: float National weighted average cost of childcare, ceiling for available childcare deduction ALD_Dependents_Elder_c: float Eldercare deduction ceiling Returns ------- care_deduction: float Total above the line deductions for dependent care. """ earned_thd = ALD_Dependents_thd[MARS - 1] if earned <= earned_thd: hc_frac = 1. - ALD_Dependents_hc child_ded = hc_frac * nu13 * ALD_Dependents_Child_c elder_ded = hc_frac * elderly_dependents * ALD_Dependents_Elder_c care_deduction = child_ded + elder_ded else: care_deduction = 0. return care_deduction @iterate_jit(nopython=True) def Adj(e03220, e03290, c03260, e03300, e03270, e03400, e03500, e03150, e03210, e03230, e03240, care_deduction, ALD_EducatorExpenses_hc, ALD_HSADeduction_hc, ALD_KEOGH_SEP_hc, ALD_SelfEmp_HealthIns_hc, ALD_EarlyWithdraw_hc, ALD_AlimonyPaid_hc, ALD_IRAContributions_hc, ALD_StudentLoan_hc, ALD_Tuition_hc, ALD_DomesticProduction_hc, c02900): """ Adj calculates Form 1040 AGI adjustments (i.e., Above-the-Line Deductions). Summands are ordered to match 2025 Schedule 1, Part II. Parameters ----- e03220: float Educator expenses (Sch 1 line 11) e03290: float HSA deduction, Form 8889 (Sch 1 line 13) c03260: float Deductible part of self-employment tax, after haircut (Sch 1 line 15) e03300: float Total deductible KEOGH/SEP/SIMPLE/etc. plan contributions (Sch 1 line 16) e03270: float Self-employed health insurance premiums (Sch 1 line 17) e03400: float Penalty on early withdrawal of savings (Sch 1 line 18) e03500: float Alimony paid (Sch 1 line 19a) e03150: float Total deductible IRA plan contributions (Sch 1 line 20) e03210: float Student loan interest paid (Sch 1 line 21) e03230: float Tuition and fees, Form 8917 (legacy; permanently repealed for tax years after 2020) e03240: float Domestic production activity deduction, Form 8903 (legacy; expired after 2017) care_deduction: float Dependent care expense deduction (reform construct) ALD_EducatorExpenses_hc: float Educator expenses haircut ALD_HSADeduction_hc: float HSA deduction haircut ALD_KEOGH_SEP_hc: float KEOGH/etc. plan contribution deduction haircut ALD_SelfEmp_HealthIns_hc: float Self-employed h.i. deduction haircut ALD_EarlyWithdraw_hc: float Penalty on early withdrawal deduction haircut ALD_AlimonyPaid_hc: float Alimony paid deduction haircut ALD_IRAContributions_hc: float IRA contribution haircut ALD_StudentLoan_hc: float Student loan interest deduction haircut ALD_Tuition_hc: float Tuition and fees haircut ALD_DomesticProduction_hc: float Domestic production haircut Returns ------- c02900: float Total above-the-line income adjustments (Sch 1 line 26; flows to Form 1040 line 10) """ # Sch 1 Part II lines not modeled: line 12 (reservist/artist/fee-basis # gov-official biz expenses, Form 2106), line 14 (moving expenses for # Armed Forces, Form 3903), line 23 (Archer MSA), and lines 24a-24z / # 25 (other adjustments). c02900 = ( (1. - ALD_EducatorExpenses_hc) * e03220 + # Sch 1 line 11 (1. - ALD_HSADeduction_hc) * e03290 + # Sch 1 line 13 c03260 + # Sch 1 line 15 (1. - ALD_KEOGH_SEP_hc) * e03300 + # Sch 1 line 16 (1. - ALD_SelfEmp_HealthIns_hc) * e03270 + # Sch 1 line 17 (1. - ALD_EarlyWithdraw_hc) * e03400 + # Sch 1 line 18 (1. - ALD_AlimonyPaid_hc) * e03500 + # Sch 1 line 19a (1. - ALD_IRAContributions_hc) * e03150 + # Sch 1 line 20 (1. - ALD_StudentLoan_hc) * e03210 + # Sch 1 line 21 # legacy / reform-only items (zero under current law): (1. - ALD_Tuition_hc) * e03230 + # ALD repealed post-2020 (1. - ALD_DomesticProduction_hc) * e03240 + # ALD expired post-2017 care_deduction # ALD reform construct ) return c02900 @iterate_jit(nopython=True) def ALD_InvInc_ec_base(p22250, p23250, e00300, e00600, e01100, e01200, MARS, invinc_ec_base, Capital_loss_limitation): """ Computes invinc_ec_base, the base amount of investment income that the reform parameter ALD_InvInc_ec_rt multiplies in AGIIncome to produce invinc_agi_ec, the dollar amount of investment income excluded from AGI. No IRS form correspondence (reform plumbing); inert under current law because ALD_InvInc_ec_rt defaults to 0. The base is the sum of five investment-income components: taxable interest, ordinary dividends, net capital gain/(loss) after the Sch D §1211(b) per-MARS loss cap, capital-gain distributions not reported on Schedule D, and other gain/(loss) from Form 4797. Note: this function runs before CapGainsLoss in calc_all (so c01000 is not yet available) and therefore re-derives the loss-capped capital gain locally as `cgain`. By construction `cgain` equals the `c01000` that CapGainsLoss will produce one call later. Parameters ---------- p22250: float Net short-term capital gain/(loss) (Schedule D line 7) p23250: float Net long-term capital gain/(loss) (Schedule D line 15) e00300: float Taxable interest (Form 1040 line 2b) e00600: float Ordinary dividends (Form 1040 line 3b) e01100: float Capital gain distributions not reported on Schedule D e01200: float Other gain/(loss) from Form 4797 (Schedule 1 line 4) MARS: int Filing marital status (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) invinc_ec_base: float Base investment income subject to the reform exclusion (consumed by AGIIncome via ALD_InvInc_ec_rt) Capital_loss_limitation: list MARS-indexed dollar limit on net capital loss deductible against ordinary income (Schedule D line 21 cap) Returns ------- invinc_ec_base: float Base investment income subject to the reform exclusion """ # Schedule D line 21 / IRC §1211(b): cap any net capital loss at the # MARS-indexed limit. Re-derived here (rather than reusing c01000) # because CapGainsLoss has not yet run; result equals c01000. cgain = max( (-1 * Capital_loss_limitation[MARS - 1]), p22250 + p23250 ) invinc_ec_base = e00300 + e00600 + cgain + e01100 + e01200 return invinc_ec_base @iterate_jit(nopython=True) def CapGainsLoss(p22250, p23250, Capital_loss_limitation, MARS, c23650, c01000): """ Schedule D Part III netting of short-term and long-term capital gains and losses, capped by the per-MARS net-capital-loss deduction limit. Parameters ---------- p22250: float Net short-term capital gain/(loss) (Schedule D line 7) p23250: float Net long-term capital gain/(loss) (Schedule D line 15) Capital_loss_limitation: list MARS-indexed dollar limit on net capital loss deductible against ordinary income (Schedule D line 21 cap) MARS: int Filing marital status (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) c23650: float Net capital gain/(loss) before loss limitation (Schedule D line 16) c01000: float Net capital gain/(loss) after loss limitation (Schedule D line 21 / Form 1040 line 7) Returns ------- c23650: float Net capital gain/(loss) before loss limitation c01000: float Net capital gain/(loss) after loss limitation """ # Schedule D line 16: combine net short-term and net long-term c23650 = p23250 + p22250 # Schedule D line 21: cap any net loss at MARS-indexed limit c01000 = max((-1 * Capital_loss_limitation[MARS - 1]), c23650) return (c23650, c01000) @iterate_jit(nopython=True) def AGIIncome(e00200, e00300, e00400, e00600, e00650, e00700, e00800, e00900, e01100, e01200, e01400, e01700, e02000, e02100, e02300, e02400, c01000, c02900, e03210, e03230, e03240, ALD_StudentLoan_hc, ALD_Tuition_hc, ALD_DomesticProduction_hc, ALD_InvInc_ec_rt, invinc_ec_base, CG_nodiff, CG_ec, CG_reinvest_ec_rt, ALD_BusinessLosses_c, AlimonyReceived_frac_in_AGI, MARS, ymod, ymod1, invinc_agi_ec): """ Builds ymod1 (Form 1040 income lines + Schedule 1 Part I, the AGI building-block consumed by AGI()) and ymod (the modified-AGI used by SSBenefits to determine the taxable portion of OASDI benefits). Reform-only investment-income and QDCG exclusions are applied here. Parameters ---------- e00200: float Wages, salaries, tips (Form 1040 line 1) e00300: float Taxable interest (Form 1040 line 2b) e00400: float Tax-exempt interest (Form 1040 line 2a; not in AGI but used in the SS-benefits modAGI) e00600: float Ordinary dividends (Form 1040 line 3b) e00650: float Qualified dividends (Form 1040 line 3a; subset of e00600) e00700: float Taxable refunds of state and local income taxes (Schedule 1 line 1) e00800: float Alimony received, before TCJA inclusion gating (Schedule 1 line 2a; included in ymod1 only to the extent of AlimonyReceived_frac_in_AGI) e00900: float Schedule C business net profit/(loss) (Schedule 1 line 3) e01100: float Capital gain distributions not reported on Schedule D e01200: float Other gain/(loss) from Form 4797 (Schedule 1 line 4) e01400: float Taxable IRA distributions (Form 1040 line 4b) e01700: float Taxable pensions and annuities (Form 1040 line 5b) e02000: float Schedule E rental, royalty, partnership, S-corp, etc. income/(loss); includes e26270 and e27200 (Schedule 1 line 5) e02100: float Schedule F farm net income/(loss) (Schedule 1 line 6) e02300: float Unemployment compensation (Schedule 1 line 7) e02400: float Total social security (OASDI) benefits (Form 1040 line 6a) c01000: float Net capital gain/(loss) after loss limitation (Form 1040 line 7); set by CapGainsLoss c02900: float Total above-the-line adjustments (Schedule 1 line 26) e03210: float Student loan interest deduction (pre-haircut) e03230: float Tuition and fees deduction (legacy) e03240: float Domestic production activities deduction (legacy) ALD_StudentLoan_hc: float Reform haircut on the student loan interest deduction ALD_Tuition_hc: float Haircut on the tuition-and-fees deduction (1.0 from 2021, after IRC §222 was repealed by the Taxpayer Certainty and Disaster Tax Relief Act of 2020 §104) ALD_DomesticProduction_hc: float Haircut on the domestic-production-activities deduction (1.0 from 2018, after TCJA §13305(a) repealed IRC §199) ALD_InvInc_ec_rt: float Reform exclusion rate for investment income invinc_ec_base: float Base investment income subject to the reform exclusion (set by ALD_InvInc_ec_base) CG_nodiff: bool Reform: long-term capital gains and qualified dividends taxed at ordinary rates (no preferential treatment) CG_ec: float Reform: dollar amount of QDCG excluded from AGI when CG_nodiff CG_reinvest_ec_rt: float Reform: fraction of QDCG above CG_ec excluded from AGI when CG_nodiff ALD_BusinessLosses_c: list Reform: MARS-indexed cap on combined Sch C + Sch E losses AlimonyReceived_frac_in_AGI: float Fraction of e00800 (alimony received) included in AGI. 1.0 pre-TCJA (alimony was income to the recipient); 0.0 under TCJA (alimony received excluded from income for divorce or separation agreements executed after 2018-12-31) MARS: int Filing marital status ymod: float Modified-AGI used by SSBenefits to determine taxable portion of OASDI benefits ymod1: float AGI build-up: Form 1040 income lines + Schedule 1 Part I, net of reform investment-income and QDCG exclusions invinc_agi_ec: float Reform exclusion of investment income from AGI Returns ------- ymod: float ymod1: float invinc_agi_ec: float """ # investment income (1040 lines 2b, 3b, 7 + Sch 1 line 4 + capgain distrib) invinc = e00300 + e00600 + c01000 + e01100 + e01200 # reform: exclude a fraction of investment income from AGI invinc_agi_ec = ALD_InvInc_ec_rt * max(0., invinc_ec_base) # ymod1 = Form 1040 income lines + Schedule 1 Part I # (e00800 = alimony received, Sch 1 line 2a, included only to the # extent of AlimonyReceived_frac_in_AGI -- TCJA excludes alimony # received from income for post-2018 divorces.) ymod1 = (e00200 + e00700 + AlimonyReceived_frac_in_AGI * e00800 + e01400 + e01700 + invinc - invinc_agi_ec + e02100 + e02300 + max(e00900 + e02000, -ALD_BusinessLosses_c[MARS - 1])) if CG_nodiff: # reform: when QDCG receive no preferential rates, partially # exclude (qualified dividends + net capital gain) from AGI qdcg_pos = max(0., e00650 + c01000) qdcg_exclusion = (min(CG_ec, qdcg_pos) + CG_reinvest_ec_rt * max(0., qdcg_pos - CG_ec)) ymod1 = max(0., ymod1 - qdcg_exclusion) invinc_agi_ec += qdcg_exclusion # ymod = modAGI used by the SS-benefits worksheet (Pub. 915). # Worksheet line 6 = "Schedule 1, lines 11 through 20, and 23 and # 25" — it excludes Sch 1 line 21 (student loan interest) and the # legacy tuition / domestic-production lines. ymod3 adds back the # exact amount Adj subtracted into c02900 for those three items so # the worksheet line 6 omission is undone symmetrically. ymod2 = e00400 + (0.50 * e02400) - c02900 ymod3 = ((1. - ALD_StudentLoan_hc) * e03210 + (1. - ALD_Tuition_hc) * e03230 + (1. - ALD_DomesticProduction_hc) * e03240) ymod = ymod1 + ymod2 + ymod3 return (ymod, ymod1, invinc_agi_ec) @iterate_jit(nopython=True) def SSBenefits(MARS, ymod, e02400, SS_all_in_agi, SS_thd1, SS_thd2, SS_percentage1, SS_percentage2, c02500): """ Calculates the taxable portion of OASDI benefits, c02500 (Form 1040 line 6b), per the Social Security Benefits Worksheet in the Form 1040 instructions (also published as Pub. 915). The three-branch form below is algebraically equivalent to the worksheet's all-min/max formulation: * ymod < thd1 : worksheet line 9 <= 0 -> c02500 = 0 * thd1 <= ymod < thd2 : worksheet line 11 = 0 -> c02500 = p1 * min(ymod - thd1, e02400) * ymod >= thd2 : full worksheet -> c02500 = min(p2 * (ymod - thd2) + p1 * min(e02400, thd2 - thd1), p2 * e02400) where ymod is the worksheet line 7 amount built in AGIIncome. Note: MARS=3 thresholds correspond to "MFS lived apart all year"; Tax-Calculator does not model the MFS-lived-together case (base = 0). The reform flag SS_all_in_agi (default False under current law) overrides the worksheet and includes 100% of OASDI in AGI. Downstream: c02500 is added to c00100 (AGI) by AGI(). Parameters ---------- MARS: int Filing marital status (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) ymod: float Worksheet line 7 (provisional income) built in AGIIncome e02400: float Total OASDI benefits (worksheet line 1; SSA-1099 box 5 sum) SS_all_in_agi: bool Reform: include 100% of OASDI in AGI (override worksheet) SS_thd1: list MARS-indexed worksheet line 8 base amount SS_thd2: list MARS-indexed (line 8 + line 10) second-tier threshold SS_percentage1: float First-tier inclusion rate (worksheet line 13; 0.50 current law) SS_percentage2: float Second-tier inclusion rate (worksheet lines 15/17; 0.85 current law) c02500: float Taxable OASDI benefits (Form 1040 line 6b) Returns ------- c02500: float Taxable OASDI benefits (Form 1040 line 6b) """ # reform: include all OASDI in AGI (override worksheet) if SS_all_in_agi: c02500 = e02400 return c02500 thd1 = SS_thd1[MARS - 1] # worksheet line 8 thd2 = SS_thd2[MARS - 1] # worksheet line 8 + line 10 if ymod < thd1: # worksheet line 9 <= 0 c02500 = 0. elif ymod < thd2: # worksheet line 11 = 0; c02500 = line 14 = min(line 2, line 13) c02500 = SS_percentage1 * min(ymod - thd1, e02400) else: # c02500 = line 18 = min(line 16, line 17) # line 16 = line 14 + line 15 # = p1 * min(e02400, thd2 - thd1) + p2 * (ymod - thd2) # line 17 = p2 * e02400 c02500 = min(SS_percentage2 * (ymod - thd2) + SS_percentage1 * min(e02400, thd2 - thd1), SS_percentage2 * e02400) return c02500 @iterate_jit(nopython=True) def UBI(nu18, n1820, n21, UBI_u18, UBI_1820, UBI_21, UBI_ecrt, ubi, taxable_ubi, nontaxable_ubi): """ Calculates total and taxable Universal Basic Income (UBI) amount. Reform construct with no IRS-form correspondence. The per-person UBI parameters (UBI_u18, UBI_1820, UBI_21) default to 0 in current law, so this function is inert unless a reform activates UBI. Outputs flow as follows: ubi -> BenefitPrograms (benefit_cost_total and, via BEN_*_value, benefit_value_total which feeds expanded_income) taxable_ubi -> AGI (added to c00100) nontaxable_ubi -> Records-bound output only; not consumed downstream Parameters ---------- nu18: int Number of people in the tax unit under 18 n1820: int Number of people in the tax unit age 18-20 n21: int Number of people in the tax unit age 21+ UBI_u18: float UBI benefit for those under 18 UBI_1820: float UBI benefit for those between 18 to 20 UBI_21: float UBI benefit for those 21 or more UBI_ecrt: float Fraction of UBI benefits excluded from AGI; the complementary fraction (1 - UBI_ecrt) is the taxable share added to AGI ubi: float Total UBI received by the tax unit taxable_ubi: float Amount of UBI that is taxable (is added to AGI) nontaxable_ubi: float Amount of UBI that is excluded from AGI Returns ------- ubi: float Total UBI received by the tax unit taxable_ubi: float Amount of UBI that is taxable (is added to AGI) nontaxable_ubi: float Amount of UBI that is excluded from AGI """ ubi = nu18 * UBI_u18 + n1820 * UBI_1820 + n21 * UBI_21 taxable_ubi = ubi * (1. - UBI_ecrt) nontaxable_ubi = ubi - taxable_ubi return ubi, taxable_ubi, nontaxable_ubi @iterate_jit(nopython=True) def AGI(ymod1, c02500, c02900, XTOT, MARS, DSI, exact, nu18, taxable_ubi, II_em, II_em_ps, II_em_po_step_size, II_em_prt, II_no_em_nu18, e02300, UI_thd, UI_em, c00100, pre_c04600, c04600): """ Computes Adjusted Gross Income (c00100, Form 1040 line 11) and the reform-only personal exemption amount (pre_c04600 and c04600; no current-law form correspondence — TCJA repealed exemptions). Parameters ---------- -- AGI inputs (Form 1040 lines 9-11) -- ymod1: float Form 1040 lines 1z+2b+3b+4b+5b+7a+8 (total income excluding taxable Social Security benefits) c02500: float Form 1040 line 6b: taxable Social Security (OASDI) benefits c02900: float Form 1040 line 10: total above-the-line adjustments (Schedule 1, Part II, line 26) taxable_ubi: float Reform-only: amount of UBI that is added to AGI -- UI exclusion inputs (reform parameter; 2020 ARPA-style) -- MARS: int Filing marital status (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) e02300: float Unemployment compensation UI_thd: list AGI threshold for unemployment compensation exclusion UI_em: float Amount of unemployment compensation excluded from AGI -- Personal exemption inputs (reform / pre-TCJA only) -- XTOT: int Total number of exemptions for filing unit DSI: int 1 if claimed as dependent on another return; otherwise 0 exact: int Whether or not to do rounding of phaseout fraction nu18: int Number of people in the tax unit under 18 II_em: float Personal and dependent exemption amount II_em_ps: list Personal exemption phaseout starting income II_em_po_step_size: list Personal exemption phaseout step size II_em_prt: float Personal exemption phaseout rate II_no_em_nu18: float Repeal personal exemptions for dependents under age 18 -- Outputs (also accepted as inputs by iterate_jit) -- c00100: float Adjusted Gross Income (AGI), Form 1040 line 11 pre_c04600: float Personal exemption before phase-out (reform-only) c04600: float Personal exemptions after phase-out (reform-only) Returns ------- c00100: float Adjusted Gross Income (AGI), Form 1040 line 11 pre_c04600: float Personal exemption before phase-out (reform-only) c04600: float Personal exemptions after phase-out (reform-only) """ # ---------------------------------------------------------------- # Form 1040 line 11: Adjusted Gross Income # ---------------------------------------------------------------- # line 9 (total income) - line 10 (Sch 1 line 26 adjustments) # = line 11 (AGI); reform-only taxable_ubi is added in. c00100 = ymod1 + c02500 - c02900 + taxable_ubi # UI exclusion (2020 ARPA-style; reform parameters UI_em / UI_thd) if (c00100 - e02300) <= UI_thd[MARS - 1]: ui_excluded = min(e02300, UI_em) else: ui_excluded = 0. c00100 -= ui_excluded # ---------------------------------------------------------------- # Personal exemption pre-phaseout (reform / pre-TCJA only; # no line on the 2025 Form 1040) # ---------------------------------------------------------------- # pre_c04600 = XTOT * II_em, with optional under-18-dep repeal # (II_no_em_nu18) and dependent-filer override (DSI). if II_no_em_nu18: # repeal of personal exemptions for deps. under 18 pre_c04600 = max(0, XTOT - nu18) * II_em else: pre_c04600 = XTOT * II_em if DSI: pre_c04600 = 0. # ---------------------------------------------------------------- # Personal exemption phase-out (PEP) # Pre-TCJA "Deduction for Exemptions Worksheet" (lines 5-7); # reform-only. # ---------------------------------------------------------------- if exact == 1: # exact calculation as on tax forms pep_line5 = max(0., c00100 - II_em_ps[MARS - 1]) pep_line6 = math.ceil(pep_line5 / II_em_po_step_size[MARS - 1]) pep_line7 = II_em_prt * pep_line6 c04600 = max(0., pre_c04600 * (1. - pep_line7)) else: # smoothed calculation needed for sensible mtr calculation dispc_numer = II_em_prt * (c00100 - II_em_ps[MARS - 1]) dispc_denom = II_em_po_step_size[MARS - 1] dispc = min(1., max(0., dispc_numer / dispc_denom)) c04600 = pre_c04600 * (1. - dispc) return (c00100, pre_c04600, c04600) @iterate_jit(nopython=True) def MiscDed(age_head, age_spouse, MARS, c00100, exact, SeniorDed_c, SeniorDed_ps, SeniorDed_prt, overtime_income, OvertimeIncomeDed_c, OvertimeIncomeDed_ps, OvertimeIncomeDed_po_step_size, OvertimeIncomeDed_po_rate_per_step, tip_income, TipIncomeDed_c, TipIncomeDed_ps, TipIncomeDed_po_step_size, TipIncomeDed_po_rate_per_step, auto_loan_interest, AutoLoanInterestDed_c, AutoLoanInterestDed_ps, AutoLoanInterestDed_po_step_size, AutoLoanInterestDed_po_rate_per_step, senior_deduction, overtime_income_deduction, tip_income_deduction, auto_loan_interest_deduction): """ Computes the four below-the-line additional deductions on Schedule 1-A (new for 2025): qualified tips (Part II), qualified overtime compensation (Part III), qualified passenger-vehicle loan interest (Part IV), and the enhanced deduction for seniors (Part V). The four amounts are summed into Schedule 1-A line 38 (Form 1040 line 13b) and consumed downstream by `StdDed`/`TaxInc`. Each part caps the qualified input, then phases the cap out as MAGI exceeds a MARS-indexed start. Tips, overtime, and auto-loan use an `exact==1` stepped phaseout (floor or ceil of `excess / step_size`, scaled by `rate_per_step`) and a smooth linear fallback that gives sensible marginal-tax-rate behavior. The senior phaseout is always smooth (form Part V line 34 multiplies by 6%, no step rounding). MAGI: Schedule 1-A line 3 adds foreign-income exclusions (Puerto Rico, Form 2555 lines 45/50, Form 4563 line 15) to AGI; those inputs are not modeled in Tax-Calculator records, so `c00100` (Form 1040 line 11 AGI) is used directly as the MAGI proxy. MFS (`MARS=3`): Parts II/III/V state "If married, you must file jointly to claim this deduction"; the function therefore zeroes the tip, overtime, and senior deductions when `MARS=3`. Part IV (auto loan) carries no MFS restriction and is allowed for MFS. Senior deduction is per eligible head/spouse (born before 1961-01-02 ≈ age 65+); each gets the line-35 amount. Eligibility conditions documented on the form but not modeled here include: valid SSN for taxpayer (and spouse if MFJ); qualified-occupation listing for tips; qualified-overtime classification; and the per-vehicle VIN/QPVLI definition for auto-loan interest. The `tip_income`, `overtime_income`, and `auto_loan_interest` inputs are assumed to already represent the form-qualified amounts. Parameters ---------- age_head: int Age of tax unit head age_spouse: int Age of tax unit spouse MARS: int Filing marital status (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) c00100: float Adjusted gross income (Form 1040 line 11); used as the MAGI proxy for Schedule 1-A line 3 exact: int Whether or not to smooth deduction phase out -- Part II (Tips, Sch 1-A lines 4-13) -- tip_income: float Qualified tips received (Sch 1-A line 6) TipIncomeDed_c: float Cap on qualified tips deduction (Sch 1-A line 7) TipIncomeDed_ps: list[float] Phase-out start MAGI (Sch 1-A line 9) TipIncomeDed_po_step_size: float Phase-out MAGI step size (Sch 1-A line 11 denominator) TipIncomeDed_po_rate_per_step: float Phase-out rate per MAGI step (Sch 1-A line 12 multiplier divided by line 11 step size) -- Part III (Overtime, Sch 1-A lines 14-21) -- overtime_income: float Qualified overtime compensation (Sch 1-A line 14c) OvertimeIncomeDed_c: list[float] Cap on overtime deduction (Sch 1-A line 15) OvertimeIncomeDed_ps: list[float] Phase-out start MAGI (Sch 1-A line 17) OvertimeIncomeDed_po_step_size: float Phase-out MAGI step size (Sch 1-A line 19 denominator) OvertimeIncomeDed_po_rate_per_step: float Phase-out rate per MAGI step (Sch 1-A line 20) -- Part IV (Auto loan interest, Sch 1-A lines 22-30) -- auto_loan_interest: float Qualified passenger-vehicle loan interest paid (Sch 1-A line 23) AutoLoanInterestDed_c: float Cap on auto loan interest deduction (Sch 1-A line 24) AutoLoanInterestDed_ps: list[float] Phase-out start MAGI (Sch 1-A line 26) AutoLoanInterestDed_po_step_size: float Phase-out MAGI step size (Sch 1-A line 28 denominator) AutoLoanInterestDed_po_rate_per_step: float Phase-out rate per MAGI step (Sch 1-A line 29) -- Part V (Seniors, Sch 1-A lines 31-37) -- SeniorDed_c: float Maximum amount of senior deduction per elderly head/spouse (Sch 1-A line 35 base, $6,000 for 2025) SeniorDed_ps: list[float] Phase-out start MAGI (Sch 1-A line 32) SeniorDed_prt: float Phase-out rate (Sch 1-A line 34, 6% for 2025) Returns ------- senior_deduction: float Sch 1-A line 37 (enhanced deduction for seniors) overtime_income_deduction: float Sch 1-A line 21 (qualified overtime compensation deduction) tip_income_deduction: float Sch 1-A line 13 (qualified tips deduction) auto_loan_interest_deduction: float Sch 1-A line 30 (qualified passenger vehicle loan interest deduction) """ # pylint: disable=too-many-statements,too-many-branches # ---------------------------------------------------------------- # Sch 1-A Part I (lines 1-3): Modified AGI # Foreign-income add-backs (lines 2a-2d) are not modeled in # Tax-Calculator records, so AGI (Form 1040 line 11 = c00100) # is the MAGI proxy used by all four parts below. # ---------------------------------------------------------------- magi = c00100 # ---------------------------------------------------------------- # Sch 1-A Part II (lines 4-13): No Tax on Tips # MFJ-only if married (MARS=3 disallowed). # ---------------------------------------------------------------- tip_income_deduction = 0. if tip_income > 0. and MARS != 3: ded = min(tip_income, TipIncomeDed_c) # line 7 (cap) po_start = TipIncomeDed_ps[MARS - 1] # line 9 if magi > po_start: # line 10 (excess) excess_agi = magi - po_start po_rate = TipIncomeDed_po_rate_per_step if exact == 1: # exact calculation as on tax forms step_size = TipIncomeDed_po_step_size steps = math.floor(excess_agi / step_size) # line 11 po_amount = steps * step_size * po_rate # line 12 else: # smoothed calculation needed for sensible mtr calculation po_amount = excess_agi * po_rate ded = max(0., ded - po_amount) # line 13 tip_income_deduction = ded # ---------------------------------------------------------------- # Sch 1-A Part III (lines 14-21): No Tax on Overtime # MFJ-only if married (MARS=3 disallowed). # ---------------------------------------------------------------- overtime_income_deduction = 0. if overtime_income > 0. and MARS != 3: ded = min(overtime_income, OvertimeIncomeDed_c[MARS - 1]) # line 15 (cap) po_start = OvertimeIncomeDed_ps[MARS - 1] # line 17 if magi > po_start: # line 18 (excess) excess_agi = magi - po_start po_rate = OvertimeIncomeDed_po_rate_per_step if exact == 1: # exact calculation as on tax forms step_size = OvertimeIncomeDed_po_step_size steps = math.floor(excess_agi / step_size) # line 19 po_amount = steps * step_size * po_rate # line 20 else: # smoothed calculation needed for sensible mtr calculation po_amount = excess_agi * po_rate ded = max(0., ded - po_amount) # line 21 overtime_income_deduction = ded # ---------------------------------------------------------------- # Sch 1-A Part IV (lines 22-30): No Tax on Car Loan Interest # No MFS restriction on the form; allowed for all MARS values. # Step rounding is CEIL (line 28 "increase to next higher whole # number"), unlike Parts II/III which FLOOR. # ---------------------------------------------------------------- auto_loan_interest_deduction = 0. if AutoLoanInterestDed_c > 0. and auto_loan_interest > 0.: ded = min(auto_loan_interest, AutoLoanInterestDed_c) # line 24 po_start = AutoLoanInterestDed_ps[MARS - 1] # line 26 if magi > po_start: # line 27 (excess) excess_agi = magi - po_start po_rate = AutoLoanInterestDed_po_rate_per_step if exact == 1: # exact calculation as on tax forms step_size = AutoLoanInterestDed_po_step_size steps = math.ceil(excess_agi / step_size) # line 28 po_amount = steps * step_size * po_rate # line 29 else: # smoothed calculation needed for sensible mtr calculation po_amount = excess_agi * po_rate ded = max(0., ded - po_amount) # line 30 auto_loan_interest_deduction = ded # ---------------------------------------------------------------- # Sch 1-A Part V (lines 31-37): Enhanced Deduction for Seniors # MFJ-only if married (MARS=3 disallowed); per eligible # head/spouse aged 65+. Phaseout is smooth 6% (no exact branch). # ---------------------------------------------------------------- senior_deduction = 0. if SeniorDed_c > 0. and MARS != 3: seniors = 0 if age_head >= 65: seniors += 1 if MARS == 2 and age_spouse >= 65: seniors += 1 if seniors > 0: po_start = SeniorDed_ps[MARS - 1] # line 32 if magi > po_start: # line 33 (excess) excess_agi = magi - po_start po_amount = excess_agi * SeniorDed_prt # line 34 per_person_ded = max(0., SeniorDed_c - po_amount) # line 35 else: per_person_ded = SeniorDed_c # line 33: use $6,000 base senior_deduction = seniors * per_person_ded # lines 36a/36b/37 return (senior_deduction, overtime_income_deduction, tip_income_deduction, auto_loan_interest_deduction) @iterate_jit(nopython=True) def ItemDed(e17500, e18400, e18500, e19200, e19800, e20100, e20400, g20500, MARS, age_head, age_spouse, c00100, c04600, c04470, c21040, c21060, c17000, c18300, c19200, c19700, c20500, c20800, II_brk6, ID_ps, ID_Medical_frt, ID_Medical_frt_add4aged, ID_Medical_hc, ID_Casualty_frt, ID_Casualty_hc, ID_Miscellaneous_frt, ID_Miscellaneous_hc, ID_Charity_crt_all, ID_Charity_crt_noncash, ID_prt, ID_crt, ID_c, ID_StateLocalTax_hc, ID_Charity_frt, ID_Charity_hc, ID_InterestPaid_hc, ID_RealEstate_hc, ID_Medical_c, ID_StateLocalTax_c, ID_RealEstate_c, ID_InterestPaid_c, ID_Charity_c, ID_Casualty_c, ID_Miscellaneous_c, ID_AllTaxes_c, ID_AllTaxes_hc, ID_AllTaxes_c_ps, ID_AllTaxes_c_po_rate, ID_AllTaxes_c_po_floor, ID_StateLocalTax_crt, ID_RealEstate_crt, ID_Charity_f, ID_reduction_rate): """ Calculates itemized deductions, Schedule A (Form 1040). Body sections mirror Schedule A's six-section structure: Medical (lines 1-4) → Taxes (5-7) → Interest (8-10) → Charity (11-14) → Casualty (15) → Other (16) → Total (17). Schedule A inputs and form-arithmetic come first within each section; reform/legacy plumbing (per-section haircuts ``_hc``, per-section MARS-indexed dollar caps ``_c``, and AGI-fraction caps/floors absent from the form) is interleaved. Three post-Sch-A reform/legacy operations apply to the line-17 total ``c21060``: (1) the §68 Pease limitation (repealed by TCJA for 2018-2025; parameters retained for reform use); (2) the OBBBA top-bracket reduction (``ID_reduction_rate``, mutually exclusive with Pease via assert); (3) a reform hard cap ``ID_c`` on total itemized deductions. No attempt is made to adjust the per-section components for these post-total limitations; only ``c04470`` reflects them. Unmodeled Schedule A items: line 5c (state and local personal property taxes), line 6 (other taxes), line 13 (charity carryover from prior year). Mortgage interest (line 8) and investment interest (line 9) are pre-aggregated in input variable ``e19200``. The OBBBA SALT-cap phaseout (line 5e text: "If Form 1040 line 11b is more than $500,000 ($250,000 MFS), see instructions") is implemented via ``ID_AllTaxes_c`` + ``ID_AllTaxes_c_ps`` + ``ID_AllTaxes_c_po_rate`` + ``ID_AllTaxes_c_po_floor``. Parameters ---------- -- Filer attributes -- MARS: int Filing marital status (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) age_head: int Age in years of taxpayer age_spouse: int Age in years of spouse c00100: float Adjusted gross income (Form 1040 line 11) c04600: float Personal exemptions after phase out (used by OBBBA reduction block to derive a taxable-income proxy) II_brk6: list Bottom of top income tax rate bracket (used by OBBBA reduction) -- Sch A Medical and Dental Expenses (lines 1-4) -- e17500: float Medical and dental expenses paid (Sch A line 1) ID_Medical_frt: float AGI floor rate for medical-expense deduction (Sch A line 3, 7.5% under current law) ID_Medical_frt_add4aged: float Add-on AGI floor rate for filers age 65+ (zero post-TCJA) ID_Medical_hc: float Reform haircut on medical-expense deduction ID_Medical_c: list Reform per-MARS dollar cap on medical-expense deduction -- Sch A Taxes You Paid (lines 5-7) -- e18400: float State and local income or sales taxes (Sch A line 5a) e18500: float State and local real estate taxes (Sch A line 5b) ID_StateLocalTax_hc: float Reform haircut on Sch A line 5a ID_RealEstate_hc: float Reform haircut on Sch A line 5b ID_StateLocalTax_c: list Reform per-MARS dollar cap on Sch A line 5a ID_RealEstate_c: list Reform per-MARS dollar cap on Sch A line 5b ID_StateLocalTax_crt: float Reform AGI-fraction cap on Sch A line 5a ID_RealEstate_crt: float Reform AGI-fraction cap on Sch A line 5b ID_AllTaxes_hc: float Reform haircut on combined Sch A line 5d (state+local+RE) ID_AllTaxes_c: list SALT cap base amount on Sch A line 5e ($40k / $20k MFS for 2025) ID_AllTaxes_c_ps: list AGI level above which the line 5e SALT cap phases out ($500k / $250k MFS for 2025 per OBBBA) ID_AllTaxes_c_po_rate: float Phaseout rate per dollar of AGI above ID_AllTaxes_c_ps ID_AllTaxes_c_po_floor: list Floor below which the SALT cap cannot be reduced by the phaseout -- Sch A Interest You Paid (lines 8-10) -- e19200: float Total deductible interest (mortgage line 8e + investment line 9, pre-aggregated in input data) ID_InterestPaid_hc: float Reform haircut on interest deduction ID_InterestPaid_c: list Reform per-MARS dollar cap on interest deduction -- Sch A Gifts to Charity (lines 11-14) -- e19800: float Cash charitable contributions (Sch A line 11) e20100: float Non-cash charitable contributions (Sch A line 12) ID_Charity_frt: float AGI-fraction floor on total charitable contributions (OBBBA introduces 0.5% for tax years 2026+; not on form face) ID_Charity_f: list Per-MARS dollar floor on charitable contributions ID_Charity_crt_all: float AGI-fraction ceiling on total charitable contributions (~50% / 60% per IRC §170(b)) ID_Charity_crt_noncash: float AGI-fraction ceiling on noncash charitable contributions ID_Charity_hc: float Reform haircut on charitable deduction ID_Charity_c: list Reform per-MARS dollar cap on charitable deduction -- Sch A Casualty and Theft Losses (line 15) -- g20500: float Casualty / theft loss after Form 4684 10% AGI floor ID_Casualty_frt: float Reform additional AGI-fraction floor (zero under current law because g20500 is already post-Form-4684) ID_Casualty_hc: float Reform haircut on casualty deduction ID_Casualty_c: list Reform per-MARS dollar cap on casualty deduction -- Sch A Other Itemized Deductions (line 16, "miscellaneous") -- e20400: float Gross miscellaneous deductions ID_Miscellaneous_frt: float AGI-fraction floor (pre-TCJA 2% rule retained for reform use) ID_Miscellaneous_hc: float Reform haircut (1.0 under current law — TCJA repealed line-16 miscellaneous deductions for 2018-2025) ID_Miscellaneous_c: list Reform per-MARS dollar cap on miscellaneous deduction -- Post-form reform/legacy on Sch A line 17 total -- ID_ps: list Pease phaseout AGI start (per MARS) ID_prt: float Pease phaseout rate ID_crt: float Pease maximum-phaseout fraction of total itemizable amount ID_reduction_rate: float OBBBA top-bracket reduction rate on itemized deductions ID_c: list Reform hard cap on total itemized deductions (per MARS) -- iterate_jit Records-bound outputs (also appear as inputs) -- c17000: float Sch A line 4 (medical expenses deducted) c18300: float Sch A line 7 (state and local taxes deducted) c19200: float Sch A line 10 (interest deducted) c19700: float Sch A line 14 (charity deducted) c20500: float Sch A line 15 (casualty / theft loss deducted) c20800: float Sch A line 16 (other / miscellaneous deducted) c21040: float Pease phaseout amount applied to itemized deductions c21060: float Sch A line 17 (gross total before Pease / OBBBA / reform cap) c04470: float Final itemized deductions after Pease / OBBBA / reform cap (zero for non-itemizers) Returns ------- c17000, c18300, c19200, c19700, c20500, c20800, c21040, c21060, c04470 (see Records-bound section above for line correspondences) """ # pylint: disable=too-many-statements posagi = max(c00100, 0.) # ---------------------------------------------------------------- # Sch A Medical and Dental Expenses (lines 1-4) # Reform plumbing: ID_Medical_frt_add4aged (age 65+ floor add-on, # zero post-TCJA), ID_Medical_hc haircut, ID_Medical_c dollar cap. # ---------------------------------------------------------------- medical_frt = ID_Medical_frt # line 3 floor rate (7.5% of AGI) if age_head >= 65 or (MARS == 2 and age_spouse >= 65): medical_frt += ID_Medical_frt_add4aged c17750 = medical_frt * posagi # line 3 c17000 = max(0., e17500 - c17750) * (1. - ID_Medical_hc) # line 4 c17000 = min(c17000, ID_Medical_c[MARS - 1]) # reform cap # ---------------------------------------------------------------- # Sch A Taxes You Paid (lines 5-7) # Line 5c (personal property tax) and line 6 (other taxes) are # unmodeled. Per-component reform plumbing: ID_StateLocalTax_c / # ID_RealEstate_c (per-MARS dollar caps); ID_StateLocalTax_crt / # ID_RealEstate_crt (AGI-fraction caps); ID_AllTaxes_hc (haircut on # combined 5d). The OBBBA 5e SALT cap with $500k/$250k phaseout is # implemented via ID_AllTaxes_c + ID_AllTaxes_c_ps + # ID_AllTaxes_c_po_rate + ID_AllTaxes_c_po_floor. # ---------------------------------------------------------------- c18400 = min((1. - ID_StateLocalTax_hc) * e18400, ID_StateLocalTax_c[MARS - 1]) # line 5a (state inc/sales) c18500 = min((1. - ID_RealEstate_hc) * e18500, ID_RealEstate_c[MARS - 1]) # line 5b (real estate) # Per-component AGI-fraction caps. The 0.0001 (rather than zero) # leaves filers with negative AGI uncapped under current law. c18400 = min(c18400, ID_StateLocalTax_crt * max(c00100, 0.0001)) c18500 = min(c18500, ID_RealEstate_crt * max(c00100, 0.0001)) c18300 = (c18400 + c18500) * (1. - ID_AllTaxes_hc) # line 5d sum + hc salt_cap = ID_AllTaxes_c[MARS - 1] # line 5e base cap salt_ps = ID_AllTaxes_c_ps[MARS - 1] # line 5e phaseout start if posagi > salt_ps: salt_excess_agi = posagi - salt_ps salt_cap = max(0., salt_cap - salt_excess_agi * ID_AllTaxes_c_po_rate) salt_cap = max(salt_cap, ID_AllTaxes_c_po_floor[MARS - 1]) # c18300 is line 5e final (= line 7, since line 6 is unmodeled) c18300 = min(c18300, salt_cap) # ---------------------------------------------------------------- # Sch A Interest You Paid (lines 8-10) # Mortgage (line 8e) and investment (line 9) interest are # pre-aggregated in input variable e19200. Reform plumbing: # ID_InterestPaid_hc, ID_InterestPaid_c (no on-form analogue). # ---------------------------------------------------------------- c19200 = e19200 * (1. - ID_InterestPaid_hc) # line 10 (= 8e + 9) c19200 = min(c19200, ID_InterestPaid_c[MARS - 1]) # reform cap # ---------------------------------------------------------------- # Sch A Gifts to Charity (lines 11-14) # On-form: e19800 = line 11 (cash), e20100 = line 12 (noncash); # line 13 (carryover) not modeled. Reform/OBBBA plumbing: # ID_Charity_frt (AGI-fraction floor; OBBBA's 0.5% kicks in 2026+), # ID_Charity_f (dollar floor), ID_Charity_crt_noncash (AGI cap on # noncash), ID_Charity_crt_all (AGI cap on total per IRC §170(b)), # ID_Charity_hc, ID_Charity_c. The floor is applied once against # total contributions; the residual is allocated to cash/noncash in # proportion to gross contributions before the noncash AGI ceiling # is enforced on the noncash share. # ---------------------------------------------------------------- charity_floor = max(ID_Charity_frt * posagi, ID_Charity_f[MARS - 1]) total_contrib = e19800 + e20100 after_floor = max(0., total_contrib - charity_floor) if total_contrib > 0.: noncash_share = e20100 / total_contrib charity_ded_noncash = min(ID_Charity_crt_noncash * posagi, after_floor * noncash_share) charity_ded_cash = after_floor * (1. - noncash_share) else: charity_ded_noncash = 0. charity_ded_cash = 0. c19700 = charity_ded_noncash + charity_ded_cash # line 14 c19700 = min(c19700, ID_Charity_crt_all * posagi) * (1. - ID_Charity_hc) c19700 = min(c19700, ID_Charity_c[MARS - 1]) # ---------------------------------------------------------------- # Sch A Casualty and Theft Losses (line 15) # g20500 is post-Form-4684 (10% AGI floor already applied), so # ID_Casualty_frt defaults to 0 under current law. # ---------------------------------------------------------------- c20500 = (max(0., g20500 - ID_Casualty_frt * posagi) * (1. - ID_Casualty_hc)) # line 15 c20500 = min(c20500, ID_Casualty_c[MARS - 1]) # reform cap # ---------------------------------------------------------------- # Sch A Other Itemized Deductions (line 16, "miscellaneous" in code) # The pre-TCJA 2%-of-AGI miscellaneous deductions are repealed for # current law (ID_Miscellaneous_hc = 1.0); parameters retained for # reform use. # ---------------------------------------------------------------- c20750 = ID_Miscellaneous_frt * posagi # reform AGI floor c20800 = max(0., e20400 - c20750) * (1. - ID_Miscellaneous_hc) # line 16 c20800 = min(c20800, ID_Miscellaneous_c[MARS - 1]) # reform cap # ---------------------------------------------------------------- # Sch A Total Itemized Deductions (line 17 = sum of 4+7+10+14+15+16) # ---------------------------------------------------------------- c21060 = c17000 + c18300 + c19200 + c19700 + c20500 + c20800 # ---------------------------------------------------------------- # Reform/legacy post-form stack (no Sch A correspondence): # (1) §68 Pease overall-limitation — repealed by TCJA for 2018-2025; # parameters retained for reform use. Excludes medical (c17000) # and casualty (c20500) per §68(c). # (2) OBBBA top-bracket reduction (ID_reduction_rate). Mutually # exclusive with Pease via the assert. # (3) Reform hard cap ID_c on total itemized deductions. # No attempt is made to adjust c04470's components for any of these # post-total limitations. # ---------------------------------------------------------------- nonlimited = c17000 + c20500 limitstart = ID_ps[MARS - 1] if c21060 > nonlimited and c00100 > limitstart: dedmin = ID_crt * (c21060 - nonlimited) dedpho = ID_prt * max(0., posagi - limitstart) c21040 = min(dedmin, dedpho) c04470 = c21060 - c21040 else: c21040 = 0. c04470 = c21060 reduction = 0. if ID_reduction_rate > 0.: assert c21040 <= 0.0, 'Pease and OBBBA cannot both be in effect' tincome = max(0., c00100 - c04600) texcess = max(0., tincome - II_brk6[MARS - 1]) reduction = ID_reduction_rate * texcess c04470 = max(0., c04470 - reduction) c04470 = min(c04470, ID_c[MARS - 1]) return (c17000, c18300, c19200, c19700, c20500, c20800, c21040, c21060, c04470) @iterate_jit(nopython=True) def AdditionalMedicareTax(MARS, e00200, e00900p, e00900s, e02100p, e02100s, k1bx14p, k1bx14s, FICA_ss_trt_employer, FICA_ss_trt_employee, FICA_mc_trt_employer, FICA_mc_trt_employee, AMEDT_ec, AMEDT_rt, ptax_amc): """ Form 8959 Additional Medicare Tax. Liability flows into Schedule 2 line 11 via `othertaxes` in `C1040` and ultimately into `iitax`. Form 8959 has five Parts: Part I (lines 1-7) Additional Medicare Tax on Medicare wages Part II (lines 8-13) Additional Medicare Tax on SE income Part III(lines 14-17)Additional Medicare Tax on RRTA compensation — *not modeled* (no RRTA variables in records) Part IV (line 18) Total = line 7 + line 13 (+ line 17) Part V (lines 19-24)Withholding reconciliation — refundable side, not part of liability. Tax-Calculator does not separately track Medicare wages (W-2 box 5) from Form 4137 unreported tips and Form 8919 wages, so `e00200` substitutes for Form 8959 line 4 (the sum of lines 1+2+3). Form 8959 line 8 (Sch SE Part I line 6) is reconstructed per-spouse: Sch SE is filed separately by each spouse, so each spouse's net SE earnings are floored at zero independently before the joint total is formed. The records-bound `sey` is the unfloored sum, which would over-net a positive-sey spouse against a negative-sey spouse; this function therefore re-derives `sey_p`/`sey_s` from the underlying per-spouse input components (matching `EI_PayrollTax`) and applies the per-spouse 0-floor before summing. The line-5 / line-9 / line-15 thresholds are identical on the form ($250k MFJ / $125k MFS / $200k Single/HoH/QSS for 2025) and are parameterized by `AMEDT_ec[MARS-1]`. The 0.9% rate is parameterized by `AMEDT_rt`. Parameters ---------- MARS: int Filing status (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) -- Part I: Medicare wages (lines 1-7) -- e00200: float Wages and salaries; substitutes for Form 8959 line 4 (Medicare wages from W-2 box 5 + Form 4137 line 6 + Form 8919 line 6) -- Part II: Self-employment income (lines 8-13) -- e00900p: float Sch C (taxpayer) net profit/loss; component of `sey_p` e00900s: float Sch C (spouse) net profit/loss; component of `sey_s` e02100p: float Sch F (taxpayer) net profit/loss; component of `sey_p` e02100s: float Sch F (spouse) net profit/loss; component of `sey_s` k1bx14p: float Sch K-1 box 14 SE earnings (taxpayer); component of `sey_p` k1bx14s: float Sch K-1 box 14 SE earnings (spouse); component of `sey_s` FICA_ss_trt_employer: float Employer-side FICA OASDI tax rate (Sch SE line 4c reduction) FICA_ss_trt_employee: float Employee-side FICA OASDI tax rate (Sch SE line 4c reduction) FICA_mc_trt_employer: float Employer-side FICA HI tax rate (Sch SE line 4c reduction) FICA_mc_trt_employee: float Employee-side FICA HI tax rate (Sch SE line 4c reduction) -- Common to Parts I and II -- AMEDT_ec: list Form 8959 line 5 / line 9 threshold by MARS AMEDT_rt: float Form 8959 line 7 / line 13 rate (0.9% under current law) -- iterate_jit Records-bound output -- ptax_amc: float Additional Medicare Tax (Form 8959 line 18) Returns ------- ptax_amc: float Additional Medicare Tax (Form 8959 line 18) """ threshold = AMEDT_ec[MARS - 1] # line 5 (also line 9; same value) seca_frac = 1. - 0.5 * (FICA_ss_trt_employer + FICA_ss_trt_employee + FICA_mc_trt_employer + FICA_mc_trt_employee) # Per-spouse Sch SE Part I line 6 (each floored at 0; matches the # `sey_p`/`sey_s` and `net_sey_p`/`net_sey_s` construction in # `EI_PayrollTax`). sey_p = e00900p + e02100p + k1bx14p sey_s = e00900s + e02100s + k1bx14s net_sey_p = max(0., sey_p * seca_frac) net_sey_s = max(0., sey_s * seca_frac) # -- Part I: Medicare wages (lines 1-7) -- line4 = e00200 line6 = max(0., line4 - threshold) line7 = AMEDT_rt * line6 # -- Part II: Self-employment income (lines 8-13) -- line8 = net_sey_p + net_sey_s line11 = max(0., threshold - line4) # = max(0, line 9 - line 10) line12 = max(0., line8 - line11) line13 = AMEDT_rt * line12 # -- Part IV: Total (line 18); Part III RRTA not modeled -- ptax_amc = line7 + line13 return ptax_amc @iterate_jit(nopython=True) def StdDed(DSI, earned, STD, age_head, age_spouse, STD_Aged, STD_Dep, STD_Dep_earned_add, MARS, MIDR, blind_head, blind_spouse, standard, STD_allow_charity_ded_nonitemizers, e19800, ID_Charity_crt_all, c00100, STD_charity_ded_nonitemizers_max): """ Computes standard deduction (Form 1040 line 12). Mirrors the "Standard Deduction for—" chart on Form 1040 line 12 and the "Standard Deduction Worksheet for Dependents" in the 2025 Form 1040 instructions. The body is split into four sections: 1. MFS-and-spouse-itemizes override (line-12 chart bullet 3): if the filer files married-separately and the spouse itemizes, the standard deduction is zero (filer must itemize). 2. Basic standard deduction: either the dependent-worksheet amount (if claimed as a dependent) or the line-12 chart's STD[MARS-1]. The dependent worksheet caps the deduction at STD[MARS-1] but floors it at max(earned + STD_Dep_earned_add, STD_Dep). 3. Aged/blind add-on: STD_Aged[MARS-1] per checked box (filer 65+, filer blind, spouse 65+, spouse blind). Spouse boxes only count for MFJ filers. 4. Reform/legacy CARES cash-charity add-on for nonitemizers (off under current law; STD_allow_charity_ded_nonitemizers defaults to False). The Records-bound output is `standard` (Form 1040 line 12); downstream `TaxInc` consumes it into Form 1040 line 14. Parameters ----- DSI: int 1 if claimed as dependent on another return; otherwise 0 (selects the dependent worksheet branch) earned: float Earned income for filing unit (dependent worksheet line 1) STD: list Per-MARS basic standard deduction (Form 1040 line-12 chart; dependent worksheet line 6 / line-12 chart cap) age_head: int Age in years of taxpayer (≥65 → check filer aged box) age_spouse: int Age in years of spouse (≥65 → check spouse aged box; MFJ only) STD_Aged: list Per-MARS additional standard deduction per checked box (per-box amount on the line-12 chart for aged/blind) STD_Dep: float Minimum dependent standard deduction (worksheet line 4) STD_Dep_earned_add: float Earned-income additional amount in the dependent worksheet (worksheet line 2) MARS: int Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) MIDR: int 1 if separately filing spouse itemizes, 0 otherwise (only meaningful when MARS=3) blind_head: int 1 if taxpayer is blind, 0 otherwise (filer blind box) blind_spouse: int 1 if spouse is blind, 0 otherwise (spouse blind box; MFJ only) standard: float Records-bound output: standard deduction (zero for itemizers) -- Reform/legacy CARES nonitemizer charity add-on -- STD_allow_charity_ded_nonitemizers: bool Allow standard deduction filers to take the charitable contributions deduction (off under current law) e19800: float Schedule A line 11 cash charitable contributions ID_Charity_crt_all: float Fraction-of-AGI cap on all charitable deductions c00100: float Federal AGI (Form 1040 line 11) STD_charity_ded_nonitemizers_max: list Per-MARS ceiling amount (in dollars) on the nonitemizer charitable contributions deduction Returns ------- standard: float Standard deduction (zero for itemizers) """ # ---------------------------------------------------------------- # MFS-and-spouse-itemizes override (Form 1040 line-12 instructions: # "Married filing separately and your spouse itemizes deductions"). # Implementation invariant: MIDR=1 only occurs when MARS=3. # ---------------------------------------------------------------- if MARS == 3 and MIDR == 1: standard = 0. return standard # ---------------------------------------------------------------- # Basic standard deduction: dependent worksheet if claimed as a # dependent, otherwise the Form 1040 line-12 chart value. # ---------------------------------------------------------------- std_basic = STD[MARS - 1] # line-12 chart cap if DSI == 1: # Dependent Std Ded Worksheet: # line 3 = earned + STD_Dep_earned_add (worksheet lines 1+2) # line 5 = max(line 3, STD_Dep) (worksheet lines 4-5) # line 7 = min(line 5, STD[MARS-1]) (worksheet line 7) basic_stded = min(std_basic, max(earned + STD_Dep_earned_add, STD_Dep)) else: basic_stded = std_basic # ---------------------------------------------------------------- # Aged/blind add-on (line-12 chart): one STD_Aged box per # 65+/blind condition on filer; spouse boxes only count for MFJ. # ---------------------------------------------------------------- num_extra_stded = blind_head if age_head >= 65: num_extra_stded += 1 if MARS == 2: num_extra_stded += blind_spouse if age_spouse >= 65: num_extra_stded += 1 extra_stded = num_extra_stded * STD_Aged[MARS - 1] standard = basic_stded + extra_stded # ---------------------------------------------------------------- # Reform/legacy CARES cash-charity add-on for nonitemizers # (STD_allow_charity_ded_nonitemizers defaults to False under # current law; active in 2020-2021 and under reforms). # ---------------------------------------------------------------- if STD_allow_charity_ded_nonitemizers: capped_ded = min(e19800, ID_Charity_crt_all * c00100) standard += min(capped_ded, STD_charity_ded_nonitemizers_max[MARS - 1]) return standard @iterate_jit(nopython=True) def TaxInc(c00100, standard, c04470, c04600, MARS, e00900, c03260, e03270, e03300, e26270, e02100, e27200, e00650, p22250, p23250, senior_deduction, overtime_income_deduction, tip_income_deduction, auto_loan_interest_deduction, PT_SSTB_income, PT_binc_w2_wages, PT_ubia_property, PT_qbid_rt, PT_qbid_limited, PT_qbid_taxinc_thd, PT_qbid_taxinc_gap, PT_qbid_w2_wages_rt, PT_qbid_alt_w2_wages_rt, PT_qbid_alt_property_rt, PT_qbid_ps, PT_qbid_prt, PT_qbid_min_ded, PT_qbid_min_qbi, c04800, qbided): """ Form 1040 lines 13-15 (2025): the §199A pass-through Qualified Business Income deduction (line 13, from Form 8995 or Form 8995-A) and regular taxable income (line 15 = AGI - line 14). QBI deduction structure (TCJA §199A): - Filers with pre-QBID taxable income at or below PT_qbid_taxinc_thd[MARS-1] ($197,300 / $394,600 MFJ for 2025) file the simplified Form 8995: deduction = PT_qbid_rt * QBI, capped only by the income limitation. - Filers above the threshold file Form 8995-A. In the phase-in window of width PT_qbid_taxinc_gap[MARS-1] ($50,000 / $100,000 MFJ) above the threshold: * Specified Service Trade or Business (SSTB) filers have QBI and W-2 wages / UBIA scaled by Schedule A's "applicable percentage" = (upper_thd - taxinc) / gap, with full disallowance once taxinc >= upper_thd. * Non-SSTB filers face the W-2/UBIA cap with a Part III phase-in reduction; above the window the cap binds in full. - All filers face the Part IV income limitation: deduction may not exceed PT_qbid_rt * (pre-QBID taxinc - net_cg), where net_cg = qualified dividends + net long-term capital gain. QBI components (Form 8995 line 1c instructions): Sch C net profit `e00900`, Sch E partnership/S-corp `e26270`, Sch F `e02100`, Sch E farm rent `e27200`. Wages (`e00200`) and investment income are NOT QBI. QBI is reduced by the trade-or-business above-the-line items: deductible part of SECA tax `c03260` (Sch 1 line 15), self-employed retirement contributions `e03300` (Sch 1 line 16), and self-employed health insurance `e03270` (Sch 1 line 17). REIT dividends and PTP income (Form 8995 lines 6-9 / Form 8995-A Part IV lines 28-31) are not modeled. Pre-QBID taxable income (Form 8995 line 11 / Form 8995-A line 33) subtracts from AGI: max(itemized, standard) + personal exemption (reform-only `c04600`) + Sch 1-A senior/overtime/tip/auto-loan deductions. The QBID is "stacked last" so that c04800 = max(0, pre_qbid_taxinc - qbided). Reform-only constructs (no IRS form analogue): - PT_qbid_limited=False disables the W-2/UBIA cap, SSTB exclusion, and phase-in entirely (deduction = PT_qbid_rt * QBI subject only to the income cap). - PT_qbid_ps / PT_qbid_prt: linear phase-out of the deduction above PT_qbid_ps[MARS-1]. - PT_qbid_min_qbi / PT_qbid_min_ded: minimum-deduction floor for filers with QBI at or above PT_qbid_min_qbi. Downstream consumer: `c04800` is the input to `SchXYZ` / `SchXYZTax` / `GainsTax` (regular tax) and to `AMT` (Form 6251). Parameters ---------- c00100: float Adjusted Gross Income (Form 1040 line 11) standard: float Standard deduction (Form 1040 line 12; zero for itemizers) c04470: float Itemized deductions after phase-out (Form 1040 line 12; zero for non-itemizers) c04600: float Personal exemptions after phase-out (reform-only; zero under current law for 2018-2025 per TCJA suspension) MARS: int Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) e00900: float Schedule C business net profit/loss (QBI component) c03260: float Self-employment (SECA) tax above-the-line deduction (Sch 1 line 15); subtracted from QBI per §199A e03270: float Self-employed health insurance deduction (Sch 1 line 17); subtracted from QBI per Form 8995 line 1 instructions e03300: float Self-employed retirement contributions to SEP/SIMPLE/qualified plans (Sch 1 line 16); subtracted from QBI per Form 8995 line 1 instructions e26270: float Schedule E partnership / S-corporation net income/loss (QBI component) e02100: float Schedule F farm net income/loss (QBI component) e27200: float Schedule E farm rent net income/loss (QBI component) e00650: float Qualified dividends (Form 8995 line 12 / 8995-A line 34 input) p22250: float Sch D Part I line 7 (net short-term capital gain/loss); used to compute §1(h) net capital gain for Form 8995 line 12 / 8995-A line 34 p23250: float Sch D Part II line 15 (net long-term capital gain/loss); used to compute §1(h) net capital gain for Form 8995 line 12 / 8995-A line 34 senior_deduction: float Sch 1-A Part V senior deduction (reduces pre-QBID taxinc) overtime_income_deduction: float Sch 1-A Part III overtime deduction (reduces pre-QBID taxinc) tip_income_deduction: float Sch 1-A Part II tip deduction (reduces pre-QBID taxinc) auto_loan_interest_deduction: float Sch 1-A Part IV auto-loan-interest deduction (reduces pre-QBID taxinc) PT_SSTB_income: int 1 = QBI is from a Specified Service Trade or Business; 0 = QBI is from a qualified (non-SSTB) trade or business PT_binc_w2_wages: float Filing unit's allocable share of W-2 wages paid by the pass-through business (Form 8995-A line 4) PT_ubia_property: float Filing unit's allocable share of unadjusted basis immediately after acquisition of qualified property (Form 8995-A line 7) PT_qbid_rt: float QBID rate (Form 8995 line 5 / 8995-A line 3 multiplier; 20% in 2025) PT_qbid_limited: bool Reform switch; False disables the W-2/UBIA cap, SSTB exclusion, and Part III phase-in PT_qbid_taxinc_thd: list MARS-indexed lower threshold of pre-QBID taxable income (Form 8995-A line 21; $197,300 / $394,600 MFJ for 2025) PT_qbid_taxinc_gap: list MARS-indexed phase-in window width (Form 8995-A line 23; $50,000 / $100,000 MFJ for 2025) PT_qbid_w2_wages_rt: float Primary W-2-wages cap rate (Form 8995-A line 5; 50%) PT_qbid_alt_w2_wages_rt: float Alternative W-2-wages cap rate (Form 8995-A line 6; 25%) PT_qbid_alt_property_rt: float Alternative UBIA cap rate (Form 8995-A line 8; 2.5%) PT_qbid_ps: list Reform-only QBID phase-out start (no form analogue) PT_qbid_prt: float Reform-only QBID phase-out rate (no form analogue) PT_qbid_min_ded: float Reform-only minimum QBID amount (no form analogue) PT_qbid_min_qbi: float Reform-only minimum QBI to qualify for PT_qbid_min_ded floor c04800: float Regular taxable income (Form 1040 line 15; iterate_jit Records-bound output) qbided: float Qualified Business Income deduction (Form 1040 line 13; iterate_jit Records-bound output) Returns ------- c04800: float Regular taxable income (Form 1040 line 15) qbided: float Qualified Business Income deduction (Form 1040 line 13) """ # ---------------------------------------------------------------- # Pre-QBID taxable income (Form 8995 line 11 / Form 8995-A line 33) # = Form 1040 line 15 with the QBID line removed. QBID is "stacked # last" so all other below-AGI deductions are subtracted here: # max(itemized, standard) + reform-only personal exemption + # Sch 1-A senior/overtime/tip/auto-loan deductions. # ---------------------------------------------------------------- odeds = ( senior_deduction # Sch 1-A Part V + overtime_income_deduction # Sch 1-A Part III + tip_income_deduction # Sch 1-A Part II + auto_loan_interest_deduction # Sch 1-A Part IV ) pre_qbid_taxinc = max(0., c00100 - max(c04470, standard) - c04600 - odeds) # ---------------------------------------------------------------- # Qualified Business Income (QBI) build (Form 8995 line 1c / # Form 8995-A line 2): Sch C net, Sch E partnership/S-corp, Sch F # farm, Sch E farm rent, less the trade-or-business above-the-line # items per Form 8995 line 1 instructions: deductible SE tax (Sch 1 # line 15), SE retirement (Sch 1 line 16), SE health insurance # (Sch 1 line 17). Wages and investment income are NOT QBI. # ---------------------------------------------------------------- qbinc = max(0., e00900 - c03260 - e03300 - e03270 + e26270 + e02100 + e27200) qbid_before_limits = qbinc * PT_qbid_rt # Form 8995 line 5 / 8995-A line 3 if PT_qbid_limited: # ------------------------------------------------------------ # Form 8995-A Parts II/III: W-2 wage / UBIA cap and SSTB phase-in. # Filers with pre_qbid_taxinc <= lower_thd file the simplified # Form 8995 (no cap, no SSTB exclusion). Above lower_thd, four # sub-cases per Form 8995-A: (a) SSTB above upper_thd: 0; (b) # non-SSTB above upper_thd: line 11 = min(line 3, line 10); (c) # non-SSTB in phase-in: Part III lines 24-26; (d) SSTB in # phase-in: Schedule A scales QBI/W-2/UBIA by applicable %, then # Part III applied to scaled values. # ------------------------------------------------------------ lower_thd = PT_qbid_taxinc_thd[MARS - 1] # 8995-A line 21 if pre_qbid_taxinc <= lower_thd: # Form 8995 path: no W-2/UBIA cap, no SSTB exclusion qbided = qbid_before_limits else: gap = PT_qbid_taxinc_gap[MARS - 1] # 8995-A line 23 upper_thd = lower_thd + gap if PT_SSTB_income == 1 and pre_qbid_taxinc >= upper_thd: # (a) SSTB above phase-in: deduction fully disallowed qbided = 0. else: # W-2 wage / UBIA cap (8995-A lines 4-10): # line 5 = 50% * W-2 wages # line 9 = 25% * W-2 wages + 2.5% * UBIA # line 10 = max(line 5, line 9) wage_cap = PT_binc_w2_wages * PT_qbid_w2_wages_rt alt_cap = (PT_binc_w2_wages * PT_qbid_alt_w2_wages_rt + PT_ubia_property * PT_qbid_alt_property_rt) full_cap = max(wage_cap, alt_cap) if PT_SSTB_income == 0 and pre_qbid_taxinc >= upper_thd: # (b) non-SSTB above phase-in: line 11 = # min(line 3, line 10) qbided = min(full_cap, qbid_before_limits) elif PT_SSTB_income == 0 and pre_qbid_taxinc < upper_thd: # (c) non-SSTB in phase-in: 8995-A Part III lines # 24-26. line 26 = line 17 - line 25 # = qbid_before_limits # - prt * max(0, qbid_before_limits - full_cap) # where prt = line 24 = (taxinc - thd) / gap. Part III # is skipped when line 10 (full_cap) >= line 3 # (qbid_before_limits), so the cap-vs-QBI excess is # floored at 0 to prevent the formula from inflating # qbided above qbid_before_limits. prt = (pre_qbid_taxinc - lower_thd) / gap adj = prt * max(0., qbid_before_limits - full_cap) qbided = qbid_before_limits - adj else: # PT_SSTB_income == 1 and pre_qbid_taxinc < upper_thd # (d) SSTB in phase-in: Schedule A scales QBI and # W-2/UBIA cap by applicable_pct = (upper_thd - # taxinc) / gap; Part III phase-in is then applied # to the Schedule-A-adjusted line 3 and line 10, # with the same Part-III-skipped floor as case (c). prti = (upper_thd - pre_qbid_taxinc) / gap # applicable % qbid_adjusted = prti * qbid_before_limits # Sch A line 3 cap_adjusted = prti * full_cap # Sch A line 10 prt = (pre_qbid_taxinc - lower_thd) / gap adj = prt * max(0., qbid_adjusted - cap_adjusted) qbided = qbid_adjusted - adj # ------------------------------------------------------------ # Form 8995-A Part IV: income limitation (lines 33-37; same # role as Form 8995 lines 11-15). Form 8995 line 12 / 8995-A # line 34 net_cg = §1(h) net capital gain (excess of net LTCG # over net STCL) plus qualified dividends per §199A(e)(3) and # the form 8995 instructions. Net STCG must NOT enter net_cg, # so we use p22250/p23250 directly rather than the post-cap # Sch D total c01000 = p22250 + p23250 (capped at -3000). # Income cap = PT_qbid_rt * (pre_qbid_taxinc - net_cg) is # line 36 / 14; final qbided = min(line 32, line 36) is # line 37 / 15. # ------------------------------------------------------------ net_ltcg = max(0., p23250) net_stcl = max(0., -p22250) net_cg = max(0., net_ltcg - net_stcl) + e00650 taxinc_cap = PT_qbid_rt * max(0., pre_qbid_taxinc - net_cg) qbided = min(qbided, taxinc_cap) # ------------------------------------------------------------ # Reform-only: linear QBID phase-out above PT_qbid_ps (no IRS # form analogue). # ------------------------------------------------------------ if qbided > 0. and pre_qbid_taxinc > PT_qbid_ps[MARS - 1]: excess = pre_qbid_taxinc - PT_qbid_ps[MARS - 1] qbided = max(0., qbided - PT_qbid_prt * excess) else: # Reform: TCJA W-2/UBIA cap, SSTB exclusion, Part III phase-in, # and Part IV income limitation all disabled. qbided = qbid_before_limits # ---------------------------------------------------------------- # Reform-only: minimum QBID floor for filers with QBI at or above # PT_qbid_min_qbi (no IRS form analogue). # ---------------------------------------------------------------- if qbinc >= PT_qbid_min_qbi and qbided < PT_qbid_min_ded: qbided = PT_qbid_min_ded # ---------------------------------------------------------------- # Form 1040 line 15: taxable income = pre_qbid_taxinc - qbided # (line 14 = std/itemized + QBID; QBID stacked last as documented # in pre_qbid_taxinc construction above). # ---------------------------------------------------------------- c04800 = max(0., pre_qbid_taxinc - qbided) return (c04800, qbided)
[docs] @JIT(nopython=True) def SchXYZ(taxable_income, MARS, II_rt1, II_rt2, II_rt3, II_rt4, II_rt5, II_rt6, II_rt7, II_rt8, II_brk1, II_brk2, II_brk3, II_brk4, II_brk5, II_brk6, II_brk7): """ Function that returns tax amount given the progressive tax rate schedule specified by the II_rt? and (upper) II_brk? parameters and given taxable income and filing status (MARS). Parameters ---------- taxable_income: float Regular taxable income MARS: int Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) II_rt1: float Personal income (regular/non-AMT) tax rate 1 II_rt2: float Personal income (regular/non-AMT) tax rate 2 II_rt3: float Personal income (regular/non-AMT) tax rate 3 II_rt4: float Personal income (regular/non-AMT) tax rate 4 II_rt5: float Personal income (regular/non-AMT) tax rate 5 II_rt6: float Personal income (regular/non-AMT) tax rate 6 II_rt7: float Personal income (regular/non-AMT) tax rate 7 II_rt8: float Personal income (regular/non-AMT) tax rate 8 II_brk1: list Personal income (regular/non-AMT) tax bracket (upper threshold) 1 II_brk2: list Personal income (regular/non-AMT) tax bracket (upper threshold) 2 II_brk3: list Personal income (regular/non-AMT) tax bracket (upper threshold) 3 II_brk4: list Personal income (regular/non-AMT) tax bracket (upper threshold) 4 II_brk5: list Personal income (regular/non-AMT) tax bracket (upper threshold) 5 II_brk6: list Personal income (regular/non-AMT) tax bracket (upper threshold) 6 II_brk7: list Personal income (regular/non-AMT) tax bracket (upper threshold) 7 Returns ------- Regular individual income tax liability on all taxable income """ # pylint: disable=too-many-return-statements # IRS 2025 Tax Rate Schedules X (single), Y-1 (MFJ/QW), Y-2 (MFS), # Z (HoH): MARS - 1 selects the schedule via II_brk?[MARS - 1]. if taxable_income <= 0.: return 0. brk1 = II_brk1[MARS - 1] if taxable_income <= brk1: return II_rt1 * taxable_income tax = II_rt1 * brk1 brk2 = II_brk2[MARS - 1] if taxable_income <= brk2: return tax + II_rt2 * (taxable_income - brk1) tax = tax + II_rt2 * (brk2 - brk1) brk3 = II_brk3[MARS - 1] if taxable_income <= brk3: return tax + II_rt3 * (taxable_income - brk2) tax = tax + II_rt3 * (brk3 - brk2) brk4 = II_brk4[MARS - 1] if taxable_income <= brk4: return tax + II_rt4 * (taxable_income - brk3) tax = tax + II_rt4 * (brk4 - brk3) brk5 = II_brk5[MARS - 1] if taxable_income <= brk5: return tax + II_rt5 * (taxable_income - brk4) tax = tax + II_rt5 * (brk5 - brk4) brk6 = II_brk6[MARS - 1] if taxable_income <= brk6: return tax + II_rt6 * (taxable_income - brk5) tax = tax + II_rt6 * (brk6 - brk5) brk7 = II_brk7[MARS - 1] if taxable_income <= brk7: return tax + II_rt7 * (taxable_income - brk6) return tax + II_rt8 * (taxable_income - brk7)
@iterate_jit(nopython=True) def SchXYZTax(c04800, MARS, II_rt1, II_rt2, II_rt3, II_rt4, II_rt5, II_rt6, II_rt7, II_rt8, II_brk1, II_brk2, II_brk3, II_brk4, II_brk5, II_brk6, II_brk7, c05200): """ Function that routes c04800 (regular taxable income) through the SchXYZ rate-schedule function and stores the result in c05200 (tax amount from Tax Rate Schedules X, Y-1, Y-2, Z). See the SchXYZ function for the semantics of MARS, II_rt?, and II_brk?. """ c05200 = SchXYZ( c04800, MARS, II_rt1, II_rt2, II_rt3, II_rt4, II_rt5, II_rt6, II_rt7, II_rt8, II_brk1, II_brk2, II_brk3, II_brk4, II_brk5, II_brk6, II_brk7 ) return c05200 @iterate_jit(nopython=True) def GainsTax(e00650, c01000, c23650, p23250, e01100, e58990, e24515, e24518, MARS, c04800, c05200, II_rt1, II_rt2, II_rt3, II_rt4, II_rt5, II_rt6, II_rt7, II_rt8, II_brk1, II_brk2, II_brk3, II_brk4, II_brk5, II_brk6, II_brk7, CG_nodiff, CG_rt1, CG_rt2, CG_rt3, CG_rt4, CG_brk1, CG_brk2, CG_brk3, dwks10, dwks13, dwks14, dwks18, dwks43, c05700, taxbc): """ Computes the regular-tax preference for long-term capital gains and qualified dividends. Implements both IRS worksheets in a single body: * Qualified Dividends and Capital Gain Tax Worksheet (QDCGTW) from the 2025 Form 1040 instructions; and * Schedule D Tax Worksheet (Sch D TW) from the 2025 Schedule D instructions, which is QDCGTW plus extra rate buckets for un-recaptured section 1250 gain (25%) and collectibles 28%-rate gain (lines 11-12 and 33-41). Sch D TW reduces algebraically to QDCGTW when both e24515 and e24518 are zero, so the code runs the Sch D TW computation unconditionally and lets the Sch D-only blocks vanish when not applicable. Section banners below mark which IRS-worksheet lines are common to both worksheets and which belong to Sch D TW only. If CG_nodiff is true, qualified dividends and long-term capital gains are taxed at ordinary rates and the worksheet is skipped. Parameters ---------- e00650: float Qualified dividends included in ordinary dividends c01000: float Limitation on capital losses c23650: float Net capital gain (long term + short term) before exclusion p23250: float Schedule D: net long-term capital gains/losses e01100: float Capital gains distributions not reported on Schedule D e58990: float Investment income elected amount from Form 4952 e24515: float Schedule D: un-recaptured section 1250 Gain e24518: float Schedule D: 28% rate gain or loss MARS: int Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) c04800: float Regular taxable income c05200: float Tax amount from Schedule X,Y,Z tables II_rt1: float Personal income (regular/non-AMT) tax rate 1 II_rt2: float Personal income (regular/non-AMT) tax rate 2 II_rt3: float Personal income (regular/non-AMT) tax rate 3 II_rt4: float Personal income (regular/non-AMT) tax rate 4 II_rt5: float Personal income (regular/non-AMT) tax rate 5 II_rt6: float Personal income (regular/non-AMT) tax rate 6 II_rt7: float Personal income (regular/non-AMT) tax rate 7 II_rt8: float Personal income (regular/non-AMT) tax rate 8 II_brk1: list Personal income (regular/non-AMT) tax bracket (upper threshold) 1 II_brk2: list Personal income (regular/non-AMT) tax bracket (upper threshold) 2 II_brk3: list Personal income (regular/non-AMT) tax bracket (upper threshold) 3 II_brk4: list Personal income (regular/non-AMT) tax bracket (upper threshold) 4 II_brk5: list Personal income (regular/non-AMT) tax bracket (upper threshold) 5 II_brk6: list Personal income (regular/non-AMT) tax bracket (upper threshold) 6 II_brk7: list Personal income (regular/non-AMT) tax bracket (upper threshold) 7 CG_nodiff: bool Long term capital gains and qualified dividends taxed no differently than regular taxable income CG_rt1: float Long term capital gain and qualified dividends (regular/non-AMT) rate 1 CG_rt2: float Long term capital gain and qualified dividends (regular/non-AMT) rate 2 CG_rt3: float Long term capital gain and qualified dividends (regular/non-AMT) rate 3 CG_rt4: float Long term capital gain and qualified dividends (regular/non-AMT) rate 4 CG_brk1: list Top of long-term capital gains and qualified dividends (regular/non-AMT) tax bracket 1 CG_brk2: list Top of long-term capital gains and qualified dividends (regular/non-AMT) tax bracket 2 CG_brk3: list Top of long-term capital gains and qualified dividends (regular/non-AMT) tax bracket 3 dwks10: float Sum of dwks6 + dwks9 dwks13: float Difference of dwks10 - dwks12 dwks14: float Maximum of 0 and dwks1 - dwks13 dwks18: float Maximum of dwks16 and dwks17 dwks43: float separate tax on long-term capital gains and qualified dividends c05700: float Lump sum distributions taxbc: float Regular tax on regular taxable income before credits Returns ------- dwks10: float Sum of dwks6 + dwks9 dwks13: float Difference of dwks10 - dwks12 dwks14: float Maximum of 0 and dwks1 - dwks13 dwks18: float Maximum of dwks16 and dwks17 dwks43: float separate tax on long-term capital gains and qualified dividends c05700: float Lump sum distributions taxbc: float Regular tax on regular taxable income before credits """ # pylint: disable=too-many-statements has_qdivltcg = ( not CG_nodiff and (c01000 > 0. or c23650 > 0. or p23250 > 0. or e01100 > 0. or e00650 > 0.) ) if has_qdivltcg: # ---- Sch D TW lines 1-10 (common to QDCGTW) ---------------------- dwks1 = c04800 # line 1: taxable income dwks2 = e00650 # line 2: qualified dividends dwks3 = e58990 # line 3: Form 4952 line 4g dwks4 = 0. # line 4: Form 4952 line 4e (=0) dwks5 = max(0., dwks3 - dwks4) # line 5 dwks6 = max(0., dwks2 - dwks5) # line 6 dwks7 = min(p23250, c23650) # line 7: min(Sch D ln 15, ln 16) dwks8 = min(dwks3, dwks4) # line 8: min(4952 ln 4g, ln 4e) = 0 # line 9: max(0, dwks7 - dwks8) is the on-form value when Sch D # was filed; when Sch D was not filed, p23250 = c23650 = 0 so # dwks7 = 0 and the QDCGTW counterpart of line 9 is e01100 # (capital gain distributions on Form 1040 line 7). The two # cases are mutually exclusive, so the sum captures both. dwks9 = max(0., dwks7 - dwks8) + e01100 # line 9 dwks10 = dwks6 + dwks9 # line 10 # ---- Sch D TW lines 11-13 (Sch D TW only; vanish in QDCGTW) ----- dwks11 = e24515 + e24518 # line 11: Sch D ln 18 + ln 19 dwks12 = min(dwks9, dwks11) # line 12 dwks13 = dwks10 - dwks12 # line 13 # ---- Sch D TW lines 14-19 (rate-bracket setup, common) ---------- dwks14 = max(0., dwks1 - dwks13) # line 14 dwks15 = min(CG_brk1[MARS - 1], dwks1) # line 15 dwks16 = min(dwks14, dwks15) # line 16 dwks17 = max(0., dwks1 - dwks10) # line 17 dwks18 = max(dwks16, dwks17) # line 18 dwks19 = dwks15 - dwks16 # line 19: amount @ 0% lowest_rate_tax = CG_rt1 * dwks19 # line 20: 0% tax (=0) # ---- Sch D TW lines 21-32 (15% and 20% rate buckets, common) ---- dwks21 = min(dwks1, dwks13) # line 21 dwks22 = dwks19 # line 22 dwks23 = max(0., dwks21 - dwks22) # line 23 dwks25 = min(CG_brk2[MARS - 1], dwks1) # line 25 dwks26 = dwks18 + dwks19 # line 26 dwks27 = max(0., dwks25 - dwks26) # line 27 dwks28 = min(dwks23, dwks27) # line 28: amount @ 15% dwks29 = CG_rt2 * dwks28 # line 29: 15% tax dwks30 = dwks22 + dwks28 # line 30 dwks31 = dwks21 - dwks30 # line 31: amount @ 20% dwks32 = CG_rt3 * dwks31 # line 32: 20% tax # ---- Reform-only: 4th capital-gains bracket (not in IRS form) --- # Tax-Calculator extension that levies (CG_rt4 - CG_rt3) on the # portion of total taxed cap gains above CG_brk3. Inactive under # current law (CG_rt4 = CG_rt3). cg_all = dwks19 + dwks28 + dwks31 hi_base = max(0., cg_all - CG_brk3[MARS - 1]) hi_incremental_rate = CG_rt4 - CG_rt3 highest_rate_incremental_tax = hi_incremental_rate * hi_base # ---- Sch D TW lines 33-41 (Sch D TW only: 25% and 28% rates) ---- # These blocks zero out when e24515 = e24518 = 0, recovering QDCGTW. dwks33 = min(dwks9, e24515) # line 33 dwks34 = dwks10 + dwks18 # line 34 dwks36 = max(0., dwks34 - dwks1) # line 36 (line 35 omitted) dwks37 = max(0., dwks33 - dwks36) # line 37: amount @ 25% dwks38 = 0.25 * dwks37 # line 38: 25% tax dwks39 = dwks18 + dwks19 + dwks28 + dwks31 + dwks37 # line 39 dwks40 = dwks1 - dwks39 # line 40: amount @ 28% dwks41 = 0.28 * dwks40 # line 41: 28% tax # ---- Sch D TW lines 42-45 (final assembly, common) -------------- dwks42 = SchXYZ(dwks18, MARS, # line 42: ordinary tax II_rt1, II_rt2, II_rt3, II_rt4, II_rt5, II_rt6, II_rt7, II_rt8, II_brk1, II_brk2, II_brk3, II_brk4, II_brk5, II_brk6, II_brk7) dwks43 = (dwks29 + dwks32 + dwks38 + dwks41 + dwks42 + lowest_rate_tax + highest_rate_incremental_tax) # line 43 dwks44 = c05200 # line 44: ordinary tax on line 1 dwks45 = min(dwks43, dwks44) # line 45: smaller of 43, 44 c24580 = dwks45 else: # no qualified-rate preference c24580 = c05200 dwks10 = max(0., min(p23250, c23650)) + e01100 dwks13 = 0. dwks14 = 0. dwks18 = 0. dwks43 = 0. # final assembly (foreign earned income exclusion assumed zero, so # c05100 = c24580; Form 4972 lump-sum distributions not modeled) c05700 = 0. taxbc = c24580 return (dwks10, dwks13, dwks14, dwks18, dwks43, c05700, taxbc) @iterate_jit(nopython=True) def AGIsurtax(c00100, MARS, AGI_surtax_trt, AGI_surtax_thd, taxbc, surtax): """ Computes a flat surtax on the portion of Adjusted Gross Income (AGI) above a MARS-indexed threshold. Reform construct: there is no IRS form correspondence (the Internal Revenue Code has no general AGI surtax line). Inert under current law because `AGI_surtax_trt` defaults to 0.0 for all years and `AGI_surtax_thd` defaults to 9e+99 for every MARS value. When the rate is positive the same amount (`rate * max(0, AGI - thd)`) is added to two accumulators: - `taxbc` (regular tax on regular taxable income before credits) so the surtax flows through `c05800` into `iitax` downstream; - `surtax` (records-bound diagnostic accumulator, also incremented by `FairShareTax` for the "Buffett Rule" reform construct). Called in `Calculator.calc_all` after `GainsTax` (so `taxbc` already reflects the rate-schedule + qualified-div/LTCG tax) and before `NetInvIncTax` and `AMT`. Parameters ---------- c00100: float Adjusted Gross Income (Form 1040 line 11) MARS: int Filing (marital) status (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) AGI_surtax_trt: float Reform-only flat surtax rate applied to AGI above `AGI_surtax_thd[MARS-1]`. Default 0.0 (inert). AGI_surtax_thd: list Reform-only MARS-indexed AGI threshold above which the surtax applies. Default 9e+99 (inert). taxbc: float Regular tax on regular taxable income before credits (input; already includes rate-schedule + qualified-div/LTCG tax) surtax: float Records-bound surtax accumulator (input) Returns ------- taxbc: float Input `taxbc` augmented by the AGI surtax surtax: float Input `surtax` augmented by the AGI surtax """ # Reform construct: inert under current law (AGI_surtax_trt = 0). if AGI_surtax_trt > 0.: agi_surtax = ( AGI_surtax_trt * max(c00100 - AGI_surtax_thd[MARS - 1], 0.) ) taxbc += agi_surtax surtax += agi_surtax return (taxbc, surtax) @iterate_jit(nopython=True) def AMT(e07300, dwks13, standard, f6251, c00100, c18300, taxbc, c04470, c20800, c21040, e24515, MARS, dwks18, dwks14, c05700, e62900, e00700, dwks10, age_head, age_spouse, earned, cmbtp, qbided, AMT_child_em_c_age, AMT_brk1, AMT_em, AMT_prt, AMT_rt1, AMT_rt2_addon, AMT_child_em, AMT_em_ps, AMT_em_pe, AMT_CG_brk1, AMT_CG_brk2, AMT_CG_brk3, AMT_CG_rt1, AMT_CG_rt2, AMT_CG_rt3, AMT_CG_rt4, c05800, c09600, c62100): """ Computes Form 6251 (2025) Alternative Minimum Tax (AMT). Builds AMT taxable income c62100 (Form 6251 line 4), tentative minimum tax via either the flat-rate computation (line 7) or Part III's maximum-capital-gains-rates worksheet (lines 12-40), subtracts AMT foreign tax credit (line 8) to yield tentative minimum tax (line 9), and subtracts the regular-tax base (line 10) to yield AMT liability c09600 (line 11). Total tax before credits c05800 = taxbc + c09600. Form 6251 structure: - Part I (lines 1a-4): AMTI = taxable income (Form 1040 line 15) plus AMT-disallowed deductions (SALT line 2a, Sch A misc) and the unmodeled prefs/adjustments (lines 2c-2t + 3) captured in cmbtp; line 2b refunds (e00700) are subtracted. Note: 2025 Form 6251 has no medical add-back (TCJA/OBBBA harmonized regular and AMT Sch A medical floors at 7.5% of AGI). - Part II top (lines 5-6): exemption schedule with phaseout (line 5 = AMT_em - AMT_prt * max(0, AMTI - AMT_em_ps)); line 6 = AMTI - exemption. - Part II tax (line 7): flat-rate AMT tax (26% below AMT_brk1, 28% above) OR Part III's cap-gains-aware tax (line 40). - Part III (lines 12-40): tax computation using maximum capital gains rates (0% / 15% / 20% / 25% unrecap §1250) paralleling QDCGTW / Sch D Tax Worksheet. - Part II bottom (lines 8-11): AMT FTC, tentative minimum tax, regular-tax base, AMT. Special rules: - MARS == 3 (MFS): exemption fully phased out when c62100 > AMT_em_pe (IRC §55(d)(3) "$900,350 see instructions" cliff). - IRC §59(j) kiddie AMT: for filers under AMT_child_em_c_age (no qualifying older spouse), exemption capped at earned + AMT_child_em. - Reform-only 4th cap-gains bracket above AMT_CG_brk3 taxed at AMT_CG_rt4 (no form analogue; AMT_CG_brk3 default 9e+99 makes this inert under current law). Downstream: c05800 → C1040 (Form 1040 line 16 + Sch 2 line 1) → NonrefundableCredits → IITAX. Parameters ----------- standard: float Standard deduction (zero for itemizers); branches AMTI construction (form line 1b itemizer vs non-itemizer path) c00100: float Adjusted Gross Income (Form 1040 line 11) e00700: float Taxable refunds of state and local income taxes (Form 6251 line 2b subtraction) qbided: float Qualified business income deduction (Form 1040 line 13) c04470: float Itemized deductions after Pease phase-out (zero for non- itemizers); Form 1040 line 12 itemized portion c18300: float Schedule A SALT post-cap deduction (Form 6251 line 2a add-back for itemizers) c20800: float Schedule A miscellaneous deductions post-2% floor (TCJA- suspended 2018-2025; add-back) c21040: float Itemized deductions that are phased out by Pease (subtracted to undo Pease for AMT) cmbtp: float Estimate of income on AMT Form 6251 but not in AGI; captures Form 6251 lines 2c through 2t and line 3 (depreciation, depletion, ISO, PAB interest, etc.) not separately modeled MARS: int Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) AMT_em: list AMT exemption amount by MARS (Form 6251 line 5) AMT_prt: float AMT exemption phaseout rate (line 5 phaseout) AMT_em_ps: list AMT exemption phaseout start by MARS AMT_em_pe: float AMT exemption phaseout ending AMT taxable income for married filing separately (MARS == 3 cliff) AMT_child_em_c_age: float Age ceiling for IRC §59(j) kiddie-AMT exemption cap AMT_child_em: float Kiddie-AMT exemption increment: earned + this amount age_head: int Age in years of taxpayer (i.e. primary adult); 0 = unset age_spouse: int Age in years of spouse (i.e. secondary adult if present) earned: float Earned income for filing unit AMT_brk1: list AMT bracket 1 upper threshold by MARS (Form 6251 line 7: $239,100 / $119,550 MFS) AMT_rt1: float AMT rate 1 (26%) AMT_rt2_addon: float Additional AMT rate above AMT_brk1 (combined top = 28%) e24515: float Schedule D unrecaptured §1250 gain (Form 6251 line 14) dwks13: float QDCGTW line 4 / Sch D TW line 13 (Form 6251 line 13) dwks14: float QDCGTW line 5 / Sch D TW line 14 (Form 6251 line 20) dwks18: float QDCGTW line 5 / Sch D TW line 21 (Form 6251 line 27) dwks10: float Schedule D Tax Worksheet line 10 (cap on Form 6251 line 15) AMT_CG_brk1: list Top of long-term capital gains and qualified dividends (AMT) tax bracket 1 (Form 6251 line 19: top of 0% bracket) AMT_CG_brk2: list Top of long-term capital gains and qualified dividends (AMT) tax bracket 2 (Form 6251 line 25: top of 15% bracket) AMT_CG_brk3: list Reform-only top of cap-gains bracket 3 (default 9e+99 → inert under current law) AMT_CG_rt1: float Long term capital gain and qualified dividends (AMT) rate 1 (0%) AMT_CG_rt2: float Long term capital gain and qualified dividends (AMT) rate 2 (15%) AMT_CG_rt3: float Long term capital gain and qualified dividends (AMT) rate 3 (20%) AMT_CG_rt4: float Reform-only long term capital gain and qualified dividends (AMT) rate 4 f6251: int 1 if Form 6251 (AMT) attached to return, otherwise 0 e62900: float Alternative Minimum Tax foreign tax credit from Form 6251 (used when f6251 == 1) e07300: float Foreign tax credit from Form 1116 (used when f6251 == 0) taxbc: float Regular tax on regular taxable income before credits (used in the Form 6251 line 10 regular-tax base) c05700: float Lump sum distributions (Form 4972) subtracted from taxbc in the Form 6251 line 10 regular-tax base c05800: float Total (regular + AMT) income tax liability before credits c09600: float Alternative Minimum Tax (AMT) liability c62100: float Alternative Minimum Tax (AMT) taxable income Returns ------- c62100: float Alternative Minimum Tax (AMT) taxable income (Form 6251 line 4) c09600: float Alternative Minimum Tax (AMT) tax liability (Form 6251 line 11) c05800: float Total (regular + AMT) income tax liability before credits """ # pylint: disable=too-many-statements,too-many-branches # ---------------------------------------------------------------- # Form 6251 Part I (lines 1a-4): Alternative Minimum Taxable Income # ---------------------------------------------------------------- # Form 6251 line 1 = Form 1040 line 15 = AGI - (STD or itemized) - QBID. if standard == 0.0: c62100 = (c00100 - e00700 - qbided - c04470 + c18300 + # SALT add-back (Form 6251 line 2a) c20800 - # Sch A misc add-back (TCJA-suspended 2018-2025) c21040) # Pease undone for AMT if standard > 0.0: c62100 = c00100 - e00700 - qbided - standard c62100 += cmbtp # Form 6251 lines 2c-2t + 3: AMT prefs/adjustments # c62100 is AMT taxable income = Form 6251 line 4 # ---------------------------------------------------------------- # Form 6251 Part II top (lines 5-6): exemption and AMTI less exemption # ---------------------------------------------------------------- # line 5: AMT exemption amount (with phase-out) line5 = max(0., AMT_em[MARS - 1] - AMT_prt * max(0., c62100 - AMT_em_ps[MARS - 1])) if MARS == 3 and c62100 > AMT_em_pe: line5 = 0. # IRC §59(j) kiddie-AMT cap: exemption limited to earned + # AMT_child_em when filer is under AMT_child_em_c_age (and no # qualifying older spouse). young_head = age_head != 0 and age_head < AMT_child_em_c_age no_or_young_spouse = age_spouse < AMT_child_em_c_age if young_head and no_or_young_spouse: line5 = min(line5, earned + AMT_child_em) # line 6: AMT taxable income less AMT exemption amount line6 = max(0., c62100 - line5) # ---------------------------------------------------------------- # Form 6251 Part II tax (line 7): flat-rate AMT tax OR Part III # ---------------------------------------------------------------- # Flat-rate tax: 26% below AMT_brk1, 28% above. Also serves as # Part III line 39 (AMT-rate on full line 12 = line 6). amt_brk1 = AMT_brk1[MARS - 1] flat_rate_tax = (AMT_rt1 * line6 + AMT_rt2_addon * max(0., line6 - amt_brk1)) if dwks10 > 0. or dwks13 > 0. or dwks14 > 0. or dwks18 > 0. or e24515 > 0.: # ------------------------------------------------------------ # Form 6251 Part III (lines 12-40): tax computation using # maximum capital gains rates. line 12 == line 6. # ------------------------------------------------------------ line13 = dwks13 # QDCGTW ln 4 / SchDTW ln 13 line14 = e24515 # Sch D line 19 (unrecap §1250) line15 = min(line13 + line14, dwks10) # min(13+14, SchDTW line 10) line16 = min(line6, line15) line17 = max(0., line6 - line16) # ordinary-income portion line18 = (AMT_rt1 * line17 + # AMT-rate on line 17 AMT_rt2_addon * max(0., line17 - amt_brk1)) # line 19 = AMT_CG_brk1[MARS-1] (top of 0% bracket) cg_brk1 = AMT_CG_brk1[MARS - 1] line20 = dwks14 # QDCGTW ln 5 / SchDTW ln 14 line21 = max(0., cg_brk1 - line20) # unused 0% bracket line22 = min(line6, line13) # cap-gains-eligible portion line23 = min(line21, line22) # amount taxed at AMT_CG_rt1:0% cgtax1 = line23 * AMT_CG_rt1 line24 = line22 - line23 # line 25 = AMT_CG_brk2[MARS-1] (top of 15% bracket) # line 26 == line 21 line27 = dwks18 # QDCGTW ln 5 / SchDTW ln 21 line28 = line21 + line27 line29 = max(0., AMT_CG_brk2[MARS - 1] - line28) line30 = min(line24, line29) # amount taxed at AMT_CG_rt2:15% cgtax2 = line30 * AMT_CG_rt2 # line 31 = 15% * line 30 line32 = line23 + line30 # sum of 0% + 15% amounts # Form 6251 line 33 = line 22 - line 32 (residual cap-gains for # 20% bracket / reform-only 4th bracket). Skip when line 22 == # line 32 (no residual). if line22 == line32: line33 = 0. # amount taxed at AMT_CG_rt3:20% linex2 = 0. # amount taxed at AMT_CG_rt4:ref else: line33 = line22 - line32 # Reform-only 4th bracket above AMT_CG_brk3 (default # 9e+99 → linex2 collapses to 0 under current law). linex1 = min(line24, max(0., AMT_CG_brk3[MARS - 1] - line20 - line21)) linex2 = max(0., line30 - linex1) cgtax3 = line33 * AMT_CG_rt3 # line 34 = 20% * line 33 cgtax4 = linex2 * AMT_CG_rt4 if line14 == 0.: # line 35-37: §1250 25% line37 = 0. else: line37 = 0.25 * max(0., line6 - line17 - line32 - line33 - linex2) line38 = line18 + cgtax1 + cgtax2 + cgtax3 + cgtax4 + line37 line40 = min(flat_rate_tax, line38) # min(line 38, line 39) line7 = line40 else: # if not completing Form 6251 Part III line7 = flat_rate_tax # ---------------------------------------------------------------- # Form 6251 Part II bottom (lines 8-11): AMT FTC, TMT, regular-tax # base, AMT. # ---------------------------------------------------------------- if f6251 == 1: line8 = e62900 # AMT FTC from filed Form 6251 else: line8 = e07300 # regular FTC proxy line9 = line7 - line8 # tentative minimum tax # line 10 regular-tax base = max(0, taxbc - e07300 - c05700); # c05700 corresponds to "minus any tax from Form 4972". c09600 = max(0., line9 - max(0., taxbc - e07300 - c05700)) c05800 = taxbc + c09600 return (c62100, c09600, c05800) @iterate_jit(nopython=True) def NetInvIncTax(e00300, e00600, e02000, e26270, c01000, c00100, NIIT_thd, MARS, NIIT_PT_taxed, NIIT_rt, niit): """ Computes the Net Investment Income Tax (NIIT) per Form 8960 (2025), Parts I and III for individuals. Output `niit` flows downstream through `C1040` to Schedule 2 line 12 ("Other Taxes") and thence to Form 1040 line 23 / `iitax`. Form structure: * Part I (lines 1-8) Investment income. * Part II (lines 9-11) Investment expenses — NOT MODELED; effectively treated as zero (Tax-Calculator records do not separate the investment- interest, allocable state-tax, and misc- investment expense items from total Sch A). * Part III (lines 12-17) Tax computation for individuals (estate/trust lines 18-21 not applicable). Mapping of Form 8960 lines to inputs: * line 1 Taxable interest <- e00300 * line 2 Ordinary dividends <- e00600 * line 3 Annuities NOT MODELED (excluded from NII) * line 4a Sch E rents/royalties/ PT/S-corp/trust/business <- e02000 * line 4b Adjustment for non- section-1411 t-or-b <- -e26270 when NIIT_PT_taxed is False (default); 0 otherwise * line 4c Combine 4a + 4b <- e02000 [- e26270] * line 5a Property-disposition gain<- c01000 (Sch D §1211(b)-capped net cap gain/loss, Form 1040 line 7) * line 5b/5c Exclusions/adjustment NOT MODELED * line 5d Combine 5a+5b+5c <- c01000 * line 6 CFC/PFIC adjustments NOT MODELED * line 7 Other modifications NOT MODELED * line 8 Total investment income <- sum of above * line 12 NII = line 8 - line 11 <- max(0., line 8); line 11 = 0 * line 13 MAGI <- c00100 (no foreign earned income exclusion add-back because Form 2555 not modeled) * line 14 Threshold by filing <- NIIT_thd[MARS-1]; current-law status defaults match the form ($200k single/HoH; $250k MFJ/QW; $125k MFS) * line 15 max(0, line 13 - line 14) * line 16 min(line 12, line 15) * line 17 NIIT_rt * line 16 <- niit; current-law NIIT_rt default 0.038 matches the form `NIIT_PT_taxed` is a reform construct: setting it True zeroes out the Form 8960 line 4b adjustment so that active partnership / S-corp income (`e26270`) remains in the NIIT base. Parameters ---------- e00300: float Taxable interest income (Form 8960 line 1) e00600: float Ordinary dividends included in AGI (Form 8960 line 2) e02000: float Schedule E total rental, royalty, partnership, S-corporation, trust, and trade-or-business income/loss (Form 8960 line 4a) e26270: float Schedule E: combined partnership and S-corporation net income/loss; the portion of e02000 treated as non-section-1411 trade-or-business income for the Form 8960 line 4b adjustment c01000: float Net capital gain/loss after the Sch D §1211(b) cap (Form 8960 line 5a; lines 5b/5c not modeled) c00100: float Adjusted Gross Income (Form 8960 line 13 MAGI proxy; foreign earned income exclusion not modeled) NIIT_thd: list Net Investment Income Tax MAGI threshold by filing status (Form 8960 line 14) MARS: int Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) NIIT_PT_taxed: bool Reform switch: false (current law) excludes active partnership and S-corp income from the NIIT base via the Form 8960 line 4b adjustment; true keeps it in the base. NIIT_rt: float Net Investment Income Tax rate (Form 8960 line 17; 3.8% under current law) niit: float Net investment income tax from Form 8960 (workspace, overwritten) Returns ------- niit: float Net investment income tax from Form 8960 line 17 """ # ---- Part I: investment income (Form 8960 lines 1-8) ---- line1 = e00300 # line 1 line2 = e00600 # line 2 # line 3 (annuities): not modeled if NIIT_PT_taxed: line4c = e02000 # line 4b adjustment = 0 else: line4c = e02000 - e26270 # line 4b = -e26270 line5d = c01000 # lines 5b/5c not modeled # lines 6, 7: not modeled line8 = line1 + line2 + line4c + line5d # line 8 # ---- Part II: investment expenses (lines 9-11) not modeled => 0 ---- # ---- Part III: tax computation (Form 8960 lines 12-17) ---- line12 = max(0., line8) # line 12 (line 11 = 0) modAGI = c00100 # no foreign earned income exclusion to add line15 = max(0., modAGI - NIIT_thd[MARS - 1]) # lines 13-15 line16 = min(line12, line15) # line 16 niit = NIIT_rt * line16 # line 17 return niit @iterate_jit(nopython=True) def F2441(MARS, earned_p, earned_s, f2441, CDCC_c, e32800, exact, c00100, CDCC_ps1, CDCC_ps2, CDCC_po1_rate_max, CDCC_po1_rate_min, CDCC_po2_rate_min, CDCC_po1_step_size, CDCC_po2_step_size, CDCC_po_rate_per_step, CDCC_refundable, c05800, e07300, c32800, c07180, CDCC_refund): """ Calculates Form 2441 (2025) Child and Dependent Care Expenses credit. Maps to Form 2441 Part II lines 2-11: line 2 (qualifying persons) ......... f2441 (capped at 2) line 3 (expenses, capped $3k/$6k) ... c32800 = min(e32800, line2*CDCC_c) line 4 (taxpayer earned income) ..... earned_p line 5 (spouse earned income, MFJ) .. earned_s (else line 4) line 6 (smallest of 3, 4, 5) ........ line6 line 7 (AGI) ........................ c00100 line 8 (decimal from AGI table) ..... crate line 9a (line 6 * line 8) ............ line9a line 9b (2024 expenses paid in 2025) . not modeled (records data gap) line 9c (line 9a + line 9b) .......... = line9a here line 10 (tax-liability limit) ........ max(0, c05800 - e07300) line 11 (nonrefundable credit) ....... c07180 = min(line 9c, line 10) The line-8 rate is computed via two stepped phase-downs: - From CDCC_po1_rate_max (35%) down to CDCC_po1_rate_min (20%) starting at AGI > CDCC_ps1, in CDCC_po1_step_size AGI steps of size CDCC_po_rate_per_step. - From CDCC_po1_rate_min down to CDCC_po2_rate_min starting at AGI > CDCC_ps2[MARS-1], in CDCC_po2_step_size[MARS-1] AGI steps of size CDCC_po_rate_per_step (OBBBA upper phase-down). Form 2441 line 9b (Worksheet A line 13 = qualified 2024 dependent-care expenses paid in 2025 in excess of the 2024 cap) is not modeled because Tax-Calculator records do not separate prior-year-unpaid expenses from current-year expenses; for filers with a 2024 catch-up amount the modeled credit is biased downward by `line9b * crate`. The 2025 credit remains entirely non-refundable on-form; the reform-only CDCC_refundable switch instead routes the full line-9a amount to CDCC_refund (all-or-nothing, not the on-form line-9b catch-up provision). The line-10 Credit Limit Worksheet (2025) subtracts only Schedule 3 line 1 (foreign tax credit, modeled as e07300) and Schedule 3 line 6l (Form 8978 line 14, BBA partner imputed-underpayment push-out — not modeled). CDCC is itself Schedule 3 line 2, so no other nonrefundable credits precede it in the Sch 3 ordering, and `c05800 - e07300` faithfully implements the worksheet (modulo the unmodeled Form 8978 line). F2441 is correspondingly called first in calculator.py among the Schedule 3 credit functions, before PersonalTaxCredit, AmOppCreditParts, SchR, EducationTaxCredit, CharityCredit, and ChildDepTaxCredit. Parameters ---------- MARS: int Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) earned_p: float Earned income for taxpayer earned_s: float Earned income for spouse f2441: int Number of child/dependent care qualifying persons CDCC_c: float Maximum child/dependent care credit per dependent e32800: float Child/dependent care expenses for qualifying persons from Form 2441 exact: int Whether or not to do rounding of phaseout fraction c00100: float Adjusted Gross Income (AGI) CDCC_ps1: float Child/dependent care credit first phaseout start CDCC_ps2: list[float] Child/dependent care credit second phaseout start CDCC_po1_rate_max: float Child/dependent care credit first phaseout rate maximum CDCC_po1_rate_min: float Child/dependent care credit first phaseout rate minimum CDCC_po2_rate_min: float Child/dependent care credit second phaseout rate minimum CDCC_po1_step_size: float Child/dependent care credit first phaseout AGI step size CDCC_po2_step_size: float Child/dependent care credit second phaseout AGI step size CDCC_po_rate_per_step: float Child/dependent care credit phaseout rate per step size CDCC_refundable: bool Indicator for whether CDCC is refundable c05800: float Total (regular + AMT) income tax liability before credits e07300: float Foreign tax credit from Form 1116 c32800: float Child and dependent care expenses capped by policy (not by earnings) c07180: float Credit for child and dependent care expenses from Form 2441 Returns ------- c32800: float Child and dependent care expenses capped by policy (not by earnings) c07180: float Credit for child and dependent care expenses from Form 2441 CDCC_refund: float Refundable amount of c07180 amount """ # ---- Form 2441 Part II ------------------------------------------------ # line 3: qualifying expenses, capped at min(f2441,2) * CDCC_c max_credit = min(f2441, 2) * CDCC_c c32800 = max(0., min(e32800, max_credit)) # lines 4, 5, 6: limit to smallest earned income across taxpayer/spouse earned_s_eff = earned_s if MARS == 2 else earned_p line6 = max(0., min(c32800, earned_p, earned_s_eff)) # line 8: credit rate (phased down at high AGI via two stepped ramps) crate = CDCC_po1_rate_max ps1 = CDCC_ps1 if c00100 > ps1: # ... first phase-down from CDCC_po1_rate_max to CDCC_po1_rate_min steps_fractional = (c00100 - ps1) / CDCC_po1_step_size if exact == 1: # exact calculation as on tax forms steps = math.ceil(steps_fractional) else: steps = steps_fractional crate = max( CDCC_po1_rate_min, CDCC_po1_rate_max - steps * CDCC_po_rate_per_step ) # ... second phase-down from CDCC_po1_rate_min to CDCC_po2_rate_min ps2 = CDCC_ps2[MARS - 1] assert ps2 >= ps1, 'CDCC_ps2 must be no less than CDCC_ps1' if c00100 > ps2: step2 = CDCC_po2_step_size[MARS - 1] steps_fractional = (c00100 - ps2) / step2 if exact == 1: # exact calculation as on tax forms steps = math.ceil(steps_fractional) else: steps = steps_fractional crate = max( CDCC_po2_rate_min, CDCC_po1_rate_min - steps * CDCC_po_rate_per_step ) # line 9a: preliminary credit (line 6 * line 8) # line 9b (2024 expenses paid in 2025 catch-up via Worksheet A) is # not modeled, so on-form line 9c = line 9a + line 9b reduces to # line 9a here line9a = line6 * crate # lines 10, 11: nonrefundable credit limited by tax-before-credits # less FTC per the 2025 Credit Limit Worksheet (which subtracts only # Sch 3 line 1 and the unmodeled Sch 3 line 6l Form 8978 line 14; # CDCC itself is Sch 3 line 2 so no other nonrefundable credits # precede it). Under reform CDCC_refundable, the full line-9a # amount is routed to CDCC_refund instead. if CDCC_refundable: c07180 = 0. CDCC_refund = line9a else: c07180 = min(max(0., c05800 - e07300), line9a) CDCC_refund = 0. return (c32800, c07180, CDCC_refund)
[docs] @JIT(nopython=True) def EITCamount(basic_frac, phasein_rate, earnings, max_amount, phaseout_start, agi, phaseout_rate): """ Returns the EIC Worksheet A (or B) line 6 amount: the smaller of the earned-income-based credit (line 2, an EIC-Table lookup keyed on earnings) and the AGI-based credit (line 5, the same lookup keyed on AGI when AGI exceeds the phaseout start). The trapezoidal credit schedule is: - phase-in: phasein_rate * earnings - plateau: max_amount - phase-out: max_amount - phaseout_rate * (max(earnings, AGI) - ps) English parameter names are used because the EIC Table is published rather than the algebraic formula; `basic_frac` is a reform-only knob (0 under current law) that shifts the schedule so a fraction of `max_amount` is paid at zero earnings. Parameters ---------- basic_frac: float Fraction of maximum earned income credit paid at zero earnings (reform-only; 0 under current law) phasein_rate: float Earned income credit phasein rate (EIC Table phase-in slope) earnings: float Earned income for filing unit (EIC Worksheet A line 1) max_amount: float Maximum earned income credit (EIC Table plateau) phaseout_start: float Earned income credit phaseout start (EIC Worksheet A line 3 threshold; AGI/earnings at which line 5 begins to bite) agi: float Adjusted Gross Income (Form 1040 line 11; EIC Worksheet A line 3) — the worksheet uses AGI rather than earnings to compute line 5 when AGI exceeds line 4 phaseout_rate: float Earned income credit phaseout rate (EIC Table phase-out slope) Returns ------- eitc: float Earned Income Credit (EIC Worksheet A line 6) """ eitc = min((basic_frac * max_amount + (1.0 - basic_frac) * phasein_rate * earnings), max_amount) if earnings > phaseout_start or agi > phaseout_start: eitcx = max(0., (max_amount - phaseout_rate * max(0., max(earnings, agi) - phaseout_start))) eitc = min(eitc, eitcx) return eitc
@iterate_jit(nopython=True) def EITC(eitc_claim_thd, MARS, DSI, c00100, e00300, e00400, e00600, c01000, e02000, e26270, age_head, age_spouse, earned, earned_p, earned_s, EIC, EITC_ps, EITC_MinEligAge, EITC_MaxEligAge, EITC_ps_addon_MarriedJ, EITC_rt, EITC_c, EITC_prt, EITC_basic_frac, EITC_InvestIncome_c, EITC_excess_InvestIncome_rt, EITC_indiv, EITC_sep_filers_elig, c59660): """ Computes Earned Income Tax Credit (Form 1040 line 27a). Implements EIC Worksheet A (Form 1040 instructions) and the Pub 596 "Rules If You Have a Qualifying Child" / "Rules If You Do Not Have a Qualifying Child" eligibility tests. Schedule EIC supplies the qualifying-child count (the `EIC` Records variable); per-child EITC parameters (`EITC_rt`/`EITC_c`/`EITC_prt`/`EITC_ps`) are length-5 lists indexed by `EIC` ∈ {0, 1, 2, 3, 4} with index 4 cloned from 3 to encode the "3 or more" plateau. EIC Worksheet B (for filers with self-employment income) is not represented separately because Tax-Calculator's `earned` input (built by `EI_PayrollTax`) already includes net SE earnings, so Worksheets A and B collapse to a single `EITCamount` call. Body sections (mirroring the worksheet + Pub 596): (A) EIC Worksheet A credit amount (filing-unit, or reform-only per-spouse under `EITC_indiv`). MFJ adds `EITC_ps_addon_MarriedJ[EIC]` to the phaseout start. (B) Pub 596 Rule 11: childless filer must be age `EITC_MinEligAge`-`EITC_MaxEligAge` (current law 25-64; age == 0 in the data is treated as eligible). Applies only when EIC == 0; for MFJ either spouse meeting the age test qualifies. (C) Pub 596 Rule 3 (MFS) and Rule 10 (claimed as dependent). MFS eligibility tracks IRC §32(d) as amended by ARPA 2021 §9621 via `EITC_sep_filers_elig` (false 2013-2020, true 2021+); DSI==1 always disqualifies. (D) Pub 596 Rule 6: investment-income cliff (IRC §32(i)). Investment income = taxable interest (`e00300`) + tax-exempt interest (`e00400`) + ordinary dividends (`e00600`) + net capital gain (`max(0, c01000)`) + net non-passive rents and royalties (`max(0, e02000 - e26270)`, removing Sch E partnership/S-corp). The form's hard cliff at `EITC_InvestIncome_c` ($11,950 for 2025) is modeled as a smooth phaseout at `EITC_excess_InvestIncome_rt` (default 9e+99 → behaviorally identical to the cliff). (E) Model-specific claiming approximation: filers with expected credit below `eitc_claim_thd` (default 0) are assumed not to claim. No form analogue. Downstream: `c59660` is the records-bound EITC amount consumed by `IITAX` (Form 1040 line 27a, refundable credit) and reported in `taxcalc/cli/input_data_tests/tests.sh` baseline `*.tables`. Parameters ---------- eitc_claim_thd: float Model-specific behavioral parameter: EITC amount below which the credit is assumed unclaimed (no form analogue) MARS: int Filing (marital) status (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) DSI: int 1 if claimed as dependent on another return, otherwise 0 (Pub 596 Rule 10) EIC: int Number of EIC qualifying children (from Schedule EIC, capped at 3 for parameter-lookup purposes; values up to 4 are accepted but mapped to the index-4 entry which clones index 3) c00100: float Adjusted Gross Income (Form 1040 line 11; EIC Worksheet A line 3) e00300: float Taxable interest income (investment-income component) e00400: float Tax-exempt interest income (investment-income component; IRC §32(i)(2)(B)) e00600: float Ordinary dividends included in AGI (investment-income component) c01000: float Capital-loss-limited Schedule D total (Sch D line 21; investment income uses `max(0, c01000)` — only net gain per IRC §32(i)(2)(D)) e02000: float Schedule E total rental, royalty, partnership, S-corp, etc., income/loss e26270: float Schedule E combined partnership and S-corp net income/loss (subtracted from `e02000` to leave net rents and royalties) age_head: int Age in years of taxpayer (primary adult); 0 in the data means unknown and is treated as eligible age_spouse: int Age in years of spouse (secondary adult, if present); 0 means unknown / no spouse earned: float Earned income for filing unit (EIC Worksheet A/B line 1; already includes net SE earnings via `EI_PayrollTax`) earned_p: float Earned income for taxpayer (used only for reform-only `EITC_indiv` per-spouse EITC) earned_s: float Earned income for spouse (used only for reform-only `EITC_indiv` per-spouse EITC) EITC_ps: list EIC-indexed phaseout start (EIC Worksheet A line 3 threshold) EITC_MinEligAge: int Minimum age for childless EITC eligibility (Pub 596 Rule 11; 25 under current law) EITC_MaxEligAge: int Maximum age for childless EITC eligibility (Pub 596 Rule 11; 64 under current law) EITC_ps_addon_MarriedJ: list EIC-indexed addition to the phaseout start for MFJ filers (= EIC-Table MFJ threshold − single threshold) EITC_rt: list EIC-indexed phasein rate EITC_c: list EIC-indexed maximum credit (EIC Table plateau) EITC_prt: list EIC-indexed phaseout rate EITC_basic_frac: float Reform-only fraction of `EITC_c` paid at zero earnings; default 0 (inert under current law) EITC_InvestIncome_c: float Investment-income ceiling (IRC §32(i); $11,950 for 2025) EITC_excess_InvestIncome_rt: float Rate at which the EITC is reduced per dollar of investment income above `EITC_InvestIncome_c`; default 9e+99 makes the smooth phaseout behaviorally equivalent to the form's hard cliff EITC_indiv: bool Reform-only: if true, MFJ EITC is computed per spouse on individual earnings and summed; default false (filing-unit EITC per the form) EITC_sep_filers_elig: bool MFS eligibility (Pub 596 Rule 3 / IRC §32(d) as amended by ARPA 2021 §9621); false 2013-2020, true 2021+ c59660: float Records-bound EITC amount (input, overwritten) Returns ------- c59660: float Earned Income Tax Credit (Form 1040 line 27a) """ # pylint: disable=too-many-branches # ---------------- (A) EIC Worksheet A: credit amount ------------- phasein_rate = EITC_rt[EIC] max_amount = EITC_c[EIC] phaseout_rate = EITC_prt[EIC] po_start = EITC_ps[EIC] if MARS == 2: po_start += EITC_ps_addon_MarriedJ[EIC] if MARS == 2 and EITC_indiv: # reform-only: per-spouse EITC instead of filing-unit EITC eitc_p = EITCamount(EITC_basic_frac, phasein_rate, earned_p, max_amount, po_start, earned_p, phaseout_rate) eitc_s = EITCamount(EITC_basic_frac, phasein_rate, earned_s, max_amount, po_start, earned_s, phaseout_rate) eitc = eitc_p + eitc_s else: eitc = EITCamount(EITC_basic_frac, phasein_rate, earned, max_amount, po_start, c00100, phaseout_rate) # ---------------- (B) Pub 596 Rule 11: childless age test -------- # Filer (or, for MFJ, either spouse) with no qualifying children # must be in [EITC_MinEligAge, EITC_MaxEligAge] (25-64 under # current law). age == 0 in the data is treated as eligible. if EIC == 0: h_age_elig = EITC_MinEligAge <= age_head <= EITC_MaxEligAge h_ok = age_head == 0 or h_age_elig if MARS == 2: s_age_elig = EITC_MinEligAge <= age_spouse <= EITC_MaxEligAge s_ok = age_spouse == 0 or s_age_elig if not (h_ok or s_ok): eitc = 0. else: if not h_ok: eitc = 0. c59660 = eitc # ---------------- (C) Pub 596 Rules 3 (MFS) and 10 (dependent) --- if (MARS == 3 and not EITC_sep_filers_elig) or DSI == 1: c59660 = 0. # ---------------- (D) Pub 596 Rule 6: investment-income cliff ---- # IRC §32(i): no EITC if investment income exceeds the ceiling. # Modeled as a smooth phaseout; with EITC_excess_InvestIncome_rt # default 9e+99 the reduction immediately drives c59660 to 0. if c59660 > 0.: invinc = (e00400 + e00300 + e00600 + max(0., c01000) + max(0., (e02000 - e26270))) if invinc > EITC_InvestIncome_c: red = EITC_excess_InvestIncome_rt * (invinc - EITC_InvestIncome_c) c59660 = max(0., c59660 - red) # ---------------- (E) Behavioral claiming approximation ---------- # Not on the form: filers with expected credit < eitc_claim_thd # are assumed not to claim (default 0 = no suppression). if c59660 < eitc_claim_thd: c59660 = 0. return c59660 @iterate_jit(nopython=True) def RefundablePayrollTaxCredit(was_plus_sey_p, was_plus_sey_s, RPTC_c, RPTC_rt, rptc_p, rptc_s, rptc): """ Computes the Refundable Payroll Tax Credit (RPTC). Reform construct with no IRS form correspondence: RPTC is a Tax-Calculator-only credit designed to emulate a payroll-tax exemption via the refundable-credit side of Form 1040. Per the `RPTC_c` policy-parameter description, positive values of `RPTC_c` and `RPTC_rt` together emulate a payroll-tax exemption whose implied earnings ceiling is `RPTC_c / RPTC_rt` per spouse. Inert under current law: `RPTC_c` and `RPTC_rt` both default to 0.0 for all years (2013+), so `rptc_p = rptc_s = rptc = 0`. Body (per spouse): pre-phaseout credit = min(rate * earnings, cap), where "earnings" is `was_plus_sey_*` (gross wages-and-salaries plus the reform-only extra-OASDI taxable SE component) as produced by `EI_PayrollTax`. The filing-unit total `rptc` is the sum of the two per-spouse credits. Calling order (calculator.py): invoked after `EITC` and before `PersonalTaxCredit` in the refundable-credit sequence. Downstream consumer: `IITAX` subtracts `rptc` from total tax liability as a fully-refundable credit (Form 1040 line 31 / Schedule 3 line 13 territory, modeled here as a standalone refundable item). Parameters ---------- was_plus_sey_p: float Taxpayer's gross wages-and-salaries plus reform-only extra-OASDI taxable self-employment earnings (set by `EI_PayrollTax`). was_plus_sey_s: float Spouse's gross wages-and-salaries plus reform-only extra-OASDI taxable self-employment earnings (set by `EI_PayrollTax`). RPTC_c: float Per-spouse maximum refundable payroll tax credit (reform-only; default 0.0). RPTC_rt: float Phasein rate applied to per-spouse earnings before the cap (reform-only; default 0.0). rptc_p: float Records-bound output: RPTC for taxpayer. rptc_s: float Records-bound output: RPTC for spouse. rptc: float Records-bound output: RPTC for filing unit (`rptc_p + rptc_s`). Returns ------- rptc_p: float RPTC for taxpayer. rptc_s: float RPTC for spouse. rptc: float RPTC for filing unit. """ rptc_p = min(was_plus_sey_p * RPTC_rt, RPTC_c) rptc_s = min(was_plus_sey_s * RPTC_rt, RPTC_c) rptc = rptc_p + rptc_s return (rptc_p, rptc_s, rptc) @iterate_jit(nopython=True) def ChildDepTaxCredit(age_head, age_spouse, nu18, n24, MARS, c00100, XTOT, num, c05800, e07260, CR_ResidentialEnergy_hc, e07300, CR_ForeignTax_hc, c07180, c07230, e07240, CR_RetirementSavings_hc, c07200, CTC_c, CTC_ps, CTC_prt, exact, ODC_c, CTC_c_under6_bonus, nu06, CTC_is_refundable, CTC_include17, c07220, odc, codtc_limited): """ Computes nonrefundable Child Tax Credit and Credit for Other Dependents on Schedule 8812 Part I (and Credit Limit Worksheet A). https://www.irs.gov/pub/irs-pdf/f1040s8.pdf Parameters ---------- n24: int Number of children who are Child-Tax-Credit eligible, one condition for which is being under age 17 MARS: int Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) c00100: float Adjusted Gross Income (AGI) XTOT: int Total number of exemptions for filing unit num: int 2 when MARS is 2 (married filing jointly), otherwise 1 c05800: float Total (regular + AMT) income tax liability before credits e07260: float Residential energy credit from Form 5695 CR_ResidentialEnergy_hc: float Credit for residential energy haircut e07300: float Foreign tax credit from Form 1116 CR_ForeignTax_hc: float Credit for foreign tax credit c07180: float Credit for child and dependent care expenses from Form 2441 c07230: float Education tax credits non-refundable amount from Form 8863 e07240: float Retirement savings contributions credit from Form 8880 CR_RetirementSavings_hc: float Credit for retirement savings haircut c07200: float Schedule R credit for the elderly and the disabled CTC_c: float Maximum nonrefundable child tax credit per child CTC_ps: list Child tax credit phaseout MAGI start CTC_prt: float Child and dependent tax credit phaseout rate exact: int Whether or not to do rounding of phaseout fraction ODC_c: float Maximum nonrefundable other-dependent credit CTC_c_under6_bonus: float Bonus child tax credit maximum for qualifying children under six nu06: int Number of dependents under 6 years old c07220: float Child tax credit (adjusted) from Form 8812 odc: float Other Dependent Credit codtc_limited: float Tentative credit not yet absorbed by tax liability (Sch 8812 line 12 minus the nonrefundable amount); used by AdditionalCTC. Returns ------- c07220: float Child tax credit (adjusted) from Form 8812 odc: float Other Dependent Credit codtc_limited: float Tentative credit not yet absorbed by tax liability (Sch 8812 line 12 minus the nonrefundable amount); used by AdditionalCTC. """ # Sch 8812 lines 1-3: modified AGI (no foreign earned income exclusion) modAGI = c00100 # reform-only: redefine "qualifying child" as under 18 instead of under 17 if CTC_include17: tu18 = int(age_head < 18) # taxpayer is under age 18 su18 = int(MARS == 2 and age_spouse < 18) # spouse is under age 18 childnum = n24 + max(0, nu18 - tu18 - su18 - n24) else: childnum = n24 # Sch 8812 lines 4-5: tentative CTC (plus reform under-6 bonus) line5 = CTC_c * childnum + CTC_c_under6_bonus * nu06 # Sch 8812 lines 6-7: tentative ODC line7 = ODC_c * max(0, XTOT - childnum - num) # Sch 8812 line 8: sum of tentative CTC and ODC line8 = line5 + line7 # Sch 8812 lines 9-12: phase out using CTC_ps threshold and CTC_prt rate if line8 > 0. and modAGI > CTC_ps[MARS - 1]: excess = modAGI - CTC_ps[MARS - 1] if exact == 1: # exact calculation as on tax forms excess = 1000. * math.ceil(excess / 1000.) line12 = max(0., line8 - CTC_prt * excess) else: line12 = line8 if line12 > 0.: # Credit Limit Worksheet A: cap by c05800 minus other nonrefundable # credits already used (Sch 3 lines 1, 2, 3, 4, 5a, 6d) clwA_other = (e07300 * (1. - CR_ForeignTax_hc) + # foreign tax c07180 + # CDCC c07230 + # education e07240 * (1. - CR_RetirementSavings_hc) + # ret savings e07260 * (1. - CR_ResidentialEnergy_hc) + # res energy c07200) # Schedule R clwA_limit = max(0., c05800 - clwA_other) if CTC_is_refundable: # reform-only: skip tax-liability cap c07220 = line12 * line5 / line8 odc = max(0., line12 - c07220) codtc_limited = max(0., line12 - c07220 - odc) else: # Sch 8812 line 14: smaller of line 12 or Credit Limit Worksheet A line14 = min(line12, clwA_limit) # split line 14 into CTC portion and ODC portion c07220 = line14 * line5 / line8 odc = max(0., line14 - c07220) # tentative credit not absorbed by tax — passed to AdditionalCTC codtc_limited = max(0., line12 - line14) else: c07220 = 0. odc = 0. codtc_limited = 0. return (c07220, odc, codtc_limited) @iterate_jit(nopython=True) def PersonalTaxCredit(MARS, c00100, XTOT, nu18, II_credit, II_credit_ps, II_credit_prt, II_credit_nr, II_credit_nr_ps, II_credit_nr_prt, RRC_c, RRC_ps, RRC_pe, RRC_prt, RRC_c_kids, RRC_c_unit, personal_refundable_credit, personal_nonrefundable_credit, recovery_rebate_credit): """ Computes three reform-construct credits. None corresponds to a 2025 current-law IRS form line: - `personal_refundable_credit` and `personal_nonrefundable_credit` are generic AGI-phased reform knobs (`II_credit*` parameters). Both default to zero under current law. - `recovery_rebate_credit` emulates the 2020 CARES Act Economic Impact Payments and the 2021 ARPA Recovery Rebate Credit, both reconciled historically on Form 1040 line 30 via a published worksheet. For 2022+ all RRC parameters default to zero, so the credit is 0 under current law. The two `II_credit*` blocks are identical-shaped: start with the MARS-indexed maximum; if the rate is positive and AGI exceeds the MARS-indexed phaseout start, subtract `rate * (AGI - start)` and floor at 0. The RRC block encodes an either/or parameter regime: - 2020 CARES (`RRC_c=0`, `RRC_c_unit`/`RRC_c_kids` > 0, `RRC_prt=0.05`, `RRC_pe=0`): branches 1 + 3 implement the CARES linear phaseout from `RRC_ps`; branch 2 cannot fire because `c00100 < RRC_pe = 0` is never true. - 2021 ARPA (`RRC_c=1400`, `RRC_c_unit=RRC_c_kids=0`, `RRC_prt=0`): branches 1 + 2 implement the ARPA per-person linear ramp from `RRC_ps` to `RRC_pe`; branch 3 returns 0. Downstream: `personal_refundable_credit` and `recovery_rebate_credit` are added by `IITAX` on the refundable side (Form 1040 line 32-style additions); `personal_nonrefundable_credit` is sequentially limited against remaining tax in `NonrefundableCredits`. Parameters ---------- MARS: int Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) c00100: float Adjusted Gross Income (AGI) (Form 1040 line 11) XTOT: int Total number of exemptions for filing unit (RRC per-person count) nu18: int Number of people under 18 years old in the filing unit (RRC kids count) II_credit: list Personal refundable credit maximum amount by MARS (reform-only; default 0) II_credit_ps: list Personal refundable credit phaseout start by MARS (reform-only; default 0) II_credit_prt: float Personal refundable credit phaseout rate (reform-only; default 0) II_credit_nr: list Personal nonrefundable credit maximum amount by MARS (reform-only; default 0) II_credit_nr_ps: list Personal nonrefundable credit phaseout start by MARS (reform-only; default 0) II_credit_nr_prt: float Personal nonrefundable credit phaseout rate (reform-only; default 0) RRC_c: float Per-person Recovery Rebate Credit maximum (ARPA 2021 = 1400; 0 otherwise) RRC_ps: list Recovery Rebate Credit phaseout start by MARS RRC_pe: list Recovery Rebate Credit phaseout end by MARS (used only by the ARPA per-person ramp; 0 outside 2021) RRC_prt: float Recovery Rebate Credit phaseout rate (CARES 2020 = 0.05; 0 otherwise) RRC_c_kids: float Per-child amount for the Recovery Rebate Credit (CARES 2020; 0 otherwise) RRC_c_unit: list Per-filing-unit base amount for the Recovery Rebate Credit by MARS (CARES 2020; 0 otherwise) personal_refundable_credit: float Records-bound output: personal refundable credit personal_nonrefundable_credit: float Records-bound output: personal nonrefundable credit recovery_rebate_credit: float Records-bound output: recovery rebate credit Returns ------- personal_refundable_credit: float Personal refundable credit personal_nonrefundable_credit: float Personal nonrefundable credit recovery_rebate_credit: float Recovery rebate credit """ # ---- Personal refundable credit (reform-only) ---- ii_ps = II_credit_ps[MARS - 1] personal_refundable_credit = II_credit[MARS - 1] if II_credit_prt > 0. and c00100 > ii_ps: pout = II_credit_prt * (c00100 - ii_ps) personal_refundable_credit = max(0., personal_refundable_credit - pout) # ---- Personal nonrefundable credit (reform-only) ---- ii_nr_ps = II_credit_nr_ps[MARS - 1] personal_nonrefundable_credit = II_credit_nr[MARS - 1] if II_credit_nr_prt > 0. and c00100 > ii_nr_ps: pout = II_credit_nr_prt * (c00100 - ii_nr_ps) personal_nonrefundable_credit = max( 0., personal_nonrefundable_credit - pout ) # ---- Recovery Rebate Credit (CARES Act 2020 EIP + ARPA 2021 RRC) ---- # Historically Form 1040 line 30 (2020 + 2021); parameters are zeroed # for 2022+, so the three branches all return 0 under current law. rrc_ps = RRC_ps[MARS - 1] rrc_pe = RRC_pe[MARS - 1] rrc_unit_kids = RRC_c_unit[MARS - 1] + RRC_c_kids * nu18 if c00100 < rrc_ps: # below phaseout start: full per-person (ARPA) + unit+kids (CARES) recovery_rebate_credit = RRC_c * XTOT + rrc_unit_kids elif 0 < c00100 < rrc_pe: # ARPA-only per-person linear ramp between rrc_ps and rrc_pe prt = (c00100 - rrc_ps) / (rrc_pe - rrc_ps) recovery_rebate_credit = RRC_c * XTOT * (1 - prt) else: # CARES-only linear phaseout of unit+kids above rrc_ps recovery_rebate_credit = max( 0., rrc_unit_kids - RRC_prt * (c00100 - rrc_ps) ) return (personal_refundable_credit, personal_nonrefundable_credit, recovery_rebate_credit) @iterate_jit(nopython=True) def AmOppCreditParts(exact, e87521, num, c00100, CR_AmOppRefundable_hc, CR_AmOppNonRefundable_hc, c10960, c87668): """ Computes the American Opportunity Credit (AOTC) refundable and nonrefundable amounts from Form 8863 Part I (2025). Form 8863 Part I line map: * line 1 Tentative AOTC across students <- e87521 (Form 8863 Part III line 30 sum; capped at $2,500/student) * line 2 Phaseout base by filing status <- 90000*num ($180k MFJ / $90k other) * line 3 MAGI <- c00100 (no Form 2555 / Form 4563 / Puerto Rico exclusion add-back because these are not modeled) * line 4 max(0, line 2 - line 3) * line 5 Phaseout range length <- 10000*num ($20k MFJ / $10k other) * line 6 min(1.000, line 4 / line 5) <- rounded to 3 decimals per IRS instructions when exact == 1 * line 7 line 1 * line 6 <- post-phaseout AOTC * line 8 0.4 * line 7 <- refundable AOTC; goes to Form 1040 line 29 via c10960 * Part II line 9 = line 7 - line 8 <- nonrefundable portion feeding the Part II Credit Limit Worksheet via c87668 The reform haircuts `CR_AmOppRefundable_hc` and `CR_AmOppNonRefundable_hc` both default to 0.0 from 2013 onward, so under current law c10960 = 0.4*line7 and c87668 = 0.6*line7 exactly as on the form. `num` doubles the single thresholds for MARS=2 (MFJ thresholds on this form are exactly twice the single thresholds). The Form 8863 line-7 under-24 dependency-eligibility checkbox (IRC §25A(i)(6)(B)) is not modeled; Tax-Calculator records do not encode the parental/dependent status required to determine whether a filer is barred from the refundable portion. Calling order: `calculator.py` invokes `AmOppCreditParts` between `SchR` and `EducationTaxCredit`. `c10960` flows to `IITAX` refundable side (Form 1040 line 29); `c87668` flows to `EducationTaxCredit` Part II Credit Limit Worksheet line 10. Parameters ---------- exact: int If 1, round the line-6 phaseout fraction to 3 decimals per IRS instructions; if 0, use the unrounded fraction. e87521: float Tentative AOTC across all students (Form 8863 line 1 = sum of Part III line 30). num: int 2 when MARS is 2 (MFJ), otherwise 1; doubles the single thresholds on Form 8863 lines 2 and 5. c00100: float Adjusted Gross Income (Form 8863 line 3 MAGI proxy; Form 2555 / Form 4563 / Puerto Rico add-backs not modeled). CR_AmOppRefundable_hc: float Reform haircut on the refundable portion (Form 8863 line 8); current-law default 0.0 means no haircut. CR_AmOppNonRefundable_hc: float Reform haircut on the nonrefundable portion (Form 8863 Part II line 9); current-law default 0.0 means no haircut. c10960: float Records-bound output, computed below (refundable AOTC). c87668: float Records-bound output, computed below (nonrefundable AOTC fed to EducationTaxCredit Part II). Returns ------- c10960: float Refundable AOTC after the reform haircut (Form 8863 line 8). c87668: float Nonrefundable AOTC after the reform haircut (Form 8863 Part II line 9 input). """ if e87521 > 0.: # ---- Phaseout fraction (Form 8863 lines 2-6) ---- line4 = max(0., 90000. * num - c00100) # line 2 - line 3 line5 = 10000. * num # line 5 if exact == 1: # exact calculation as on tax forms line6_x1000 = 1000. * min(1., round(line4 / line5, 3)) else: line6_x1000 = 1000. * min(1., line4 / line5) # ---- Total AOTC after phaseout (line 7) ---- line7 = line6_x1000 * e87521 / 1000. # ---- Refundable (line 8) / nonrefundable (Part II line 9) split ---- ref_portion = 0.4 * line7 # Form 8863 line 8 base nonref_portion = line7 - ref_portion # Part II line 9 base = 0.6*line7 c10960 = ref_portion * (1. - CR_AmOppRefundable_hc) c87668 = nonref_portion * (1. - CR_AmOppNonRefundable_hc) else: c10960 = 0. c87668 = 0. return (c10960, c87668) @iterate_jit(nopython=True) def SchR(age_head, age_spouse, MARS, c00100, c05800, e07300, c07180, e02400, c02500, e01500, e01700, CR_SchR_hc, c07200): """ Calculates Schedule R (2025) Credit for the Elderly or the Disabled, c07200 (Schedule 3 line 6d, nonrefundable). Maps to Schedule R Part III lines 10-22 (Part I checkbox selects the line-10 base and line-15 AGI threshold; Part II physician statement not modeled). Schedule R policy amounts are hardcoded on the form (not inflation-indexed and not exposed as Policy parameters); only the reform haircut CR_SchR_hc is in policy_current_law.json. Part I box mapping (Tax-Calculator-reachable boxes only): Box 1 Single/HoH/QSS 65+ ......... MARS in (1, 4, 5) and age_head>=65 Box 3 MFJ both 65+ ............... MARS==2, both ages>=65 Box 7 MFJ one 65+ + other not disabled MARS==2, one age>=65 Box 8 MFS 65+ lived apart all year MARS==3 and age_head>=65 Boxes 2/4/5/6/9 (disability) are unreachable — Tax-Calculator records do not encode disability or taxable disability income, so the Part I "disabled under 65" paths are not modeled and line 12 reduces to line 10 (no smaller-of-line-10-or-line-11 step). Box 8 over-credit (model-input-data gap): the 2025 Schedule R instructions disallow the credit for MFS filers who "lived with [their] spouse at any time during 2025"; Box 8 applies only to MFS filers who lived apart from their spouse for all of 2025. Tax-Calculator records do not encode whether MFS filers lived apart all year, so this code treats every MARS==3 filer with age_head>=65 as Box 8 and over-credits the lived-with-spouse sub-population. Same record-data limitation pattern as in SSBenefits (lived-apart-all-year not encoded). Part III line-by-line mapping: line 10 (base) ............ 5000/7500/3750 per box (above) line 11 (disability inc) .. not modeled line 12 = line 10 (no disability claimed) line 13a (nontaxable OASDI). max(0, e02400 - c02500) line 13b (nontaxable pens). max(0, e01500 - e01700) — approximation line 13c (sum) ............ line13a + line13b line 14 (AGI) ............. c00100 line 15 (AGI threshold) ... 7500/10000/5000 per box line 16 ................... max(0, line14 - line15) line 17 ................... 0.5 * line16 line 18 ................... line13c + line17 line 19 ................... max(0, line12 - line18) line 20 ................... 0.15 * line19 line 21 (tax limit) ....... Credit Limit Worksheet output line 22 ................... min(line20, line21), with haircut applied The line-13b approximation (max(0, e01500 - e01700)) treats every dollar of gross pension above taxable pension as "nontaxable" under line-13b. It is exact when all of a filer's pensions are uniformly fully taxable or uniformly partially taxable; for filers receiving both a fully-taxable pension and a partially-taxable pension it over-states the line-13b add-back and biases the credit downward. The line-21 Credit Limit Worksheet (2025) for Schedule 3 line 6d per the IRS Schedule R 2025 instructions subtracts Sch 3 line 1 (foreign tax credit, e07300) + Sch 3 line 2 (CDCC, c07180) + Sch 3 line 6l (Form 8978). Form 8978 is not modeled in Tax-Calculator records (model-input-data gap). The on-form worksheet does NOT subtract Sch 3 line 3 Education credits, but EducationTaxCredit is called AFTER SchR in calc_all so c07230 would not be available here in any case; the final sequential nonrefundable-credit limit is enforced downstream by NonrefundableCredits. Calling-order context (calculator.py): F2441 -> PersonalTaxCredit -> AmOppCreditParts -> SchR -> EducationTaxCredit -> CharityCredit -> ChildDepTaxCredit -> NonrefundableCredits. Downstream: c07200 is consumed by NonrefundableCredits as a nonrefundable Sch 3 credit summed into the Form 1040 line 22 credit total. Parameters ---------- age_head: int Age in years of taxpayer (primary adult) age_spouse: int Age in years of spouse (secondary adult, if present) MARS: int Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) c00100: float Adjusted Gross Income (AGI); used for Schedule R line 14 c05800: float Total (regular + AMT) income tax liability before credits; input to the line-21 Credit Limit Worksheet e07300: float Foreign tax credit from Form 1116 (Sch 3 line 1); subtracted on line 21 c07180: float Credit for child and dependent care expenses from Form 2441 (Sch 3 line 2); subtracted on line 21 e02400: float Total social security (OASDI) benefits; line 13a gross c02500: float Social security (OASDI) benefits included in AGI; line 13a nontaxable = e02400 - c02500 e01500: float Total pensions and annuities; line 13b gross e01700: float Taxable pensions and annuities; line 13b nontaxable approximation = e01500 - e01700 CR_SchR_hc: float Schedule R credit haircut (reform parameter); default 0.0 under current law Returns ------- c07200: float Schedule R credit for the elderly and the disabled (Sch 3 line 6d) """ # ---- eligibility gate: 65+ paths only (disability not modeled) ---- if age_head >= 65 or (MARS == 2 and age_spouse >= 65): # ---- Part I -> Part III line 10 (base) and line 15 (AGI threshold) -- if MARS == 2: if age_head >= 65 and age_spouse >= 65: line10 = 7500. # Box 3 (MFJ, both 65+) else: line10 = 5000. # Box 7 (MFJ, one 65+, other not disabled) line15 = 10000. elif MARS == 3: line10 = 3750. # Box 8 (MFS 65+ lived apart all year) line15 = 5000. elif MARS in (1, 4, 5): line10 = 5000. # Box 1 (Single/HoH/QSS 65+) line15 = 7500. else: line10 = 0. line15 = 0. line12 = line10 # line 12 = line 10 (no disability claimed) # ---- Part III lines 13-17: nontaxable income + AGI excess ----------- line13a = max(0., e02400 - c02500) # nontaxable OASDI line13b = max(0., e01500 - e01700) # nontaxable pensions (approx) line13c = line13a + line13b # line 13c # line 14 = c00100 (AGI) line16 = max(0., c00100 - line15) # line 16 line17 = 0.5 * line16 # line 17 # ---- Part III lines 18-22: credit, tax-liability limit, haircut ----- line18 = line13c + line17 # line 18 line19 = max(0., line12 - line18) # line 19 line20 = 0.15 * line19 # line 20 # line 21 (Credit Limit Wksht): per 2025 IRS Schedule R # instructions the worksheet subtracts Sch 3 line 1 (FTC) + # line 2 (CDCC) + line 6l (Form 8978). Form 8978 is unmodeled # in Tax-Calculator records. Sch 3 line 3 (Education credits, # c07230) is NOT subtracted on the on-form worksheet; # EducationTaxCredit is computed AFTER SchR in calc_all and # Form 8863's own CLW subtracts c07200 instead. Final # sequential nonrefundable limiting is enforced downstream # by NonrefundableCredits. line21 = max(0., c05800 - e07300 - c07180) # line 22, with haircut c07200 = min(line20, line21) * (1. - CR_SchR_hc) else: c07200 = 0. return c07200 @iterate_jit(nopython=True) def EducationTaxCredit(exact, e87530, MARS, c00100, c05800, e07300, c07180, c07200, c87668, LLC_Expense_c, ETC_pe_Single, ETC_pe_Married, CR_Education_hc, c07230): """ Computes Education Tax Credits (`c07230`, Schedule 3 line 3) from Form 8863 (2025) Part II "Nonrefundable Education Credits" and the Credit Limit Worksheet (CLW) in the Form 8863 instructions. Part II line-by-line mapping: line 9 = `c87668` (nonref AOTC, computed by `AmOppCreditParts`) line 10 = `e87530` (LLC adjusted qualified expenses, summed over students) line 11 = min(`e87530`, `LLC_Expense_c`) (cap; $10,000 on form) line 12 = line 11 * 0.20 (LLC tentative) line 13 = $180,000 MFJ / $90,000 other (`ETC_pe_*` stored in thousands of dollars) line 14 = AGI (`c00100`) line 15 = max(0., line 13 - line 14) line 16 = $20,000 MFJ / $10,000 other (phaseout spread) line 17 = min(1., line 15 / line 16) rounded to 3 decimals when `exact==1` per the form instruction line 18 = line 12 * line 17 (LLC after phaseout) line 19 = CLW line 7 -> Sch 3 line 3 Credit Limit Worksheet (from Form 8863 instructions): CLW line 1 = Form 8863 line 18 (LLC after phaseout) CLW line 2 = Form 8863 line 9 (nonref AOTC) CLW line 3 = CLW line 1 + CLW line 2 CLW line 4 = Form 1040 line 18 = `c05800` CLW line 5 = Sch 3 line 1 (`e07300` FTC) + line 2 (`c07180` CDCC) + line 6d (`c07200` SchR) + 6f + 6l + 6m (6f / 6l / 6m unmodeled in Tax-Calculator records) CLW line 6 = CLW line 4 - CLW line 5 CLW line 7 = min(CLW line 3, CLW line 6) (nonref ed credits) The code applies CLW lines 5-7 sequentially (LLC first, then nonref AOTC, subtracting LLC from remaining tax-availability before AOTC); this is algebraically equivalent to the form's single combined `min(line 3, line 6)` because both portions are bounded above by the same tax-availability quantity (line 6). `ETC_pe_Single` / `ETC_pe_Married` are stored in thousands of dollars per `policy_current_law.json` (descriptions say "in thousands"), so the `* 1000.` factors convert them to the dollar thresholds the form prints on line 13. `CR_Education_hc` is a reform construct (haircut) with no IRS-form counterpart; under current law it defaults to 0.0 for all years, so `c07230` equals the on-form line 19 unmodified. Downstream consumer: `c07230` flows to `NonrefundableCredits` as Sch 3 line 3 in the credit-ordering and is sequentially re-limited against remaining tax-before-credits there before being summed into the nonrefundable-credit total subtracted in `IITAX`. Parameters ---------- exact: int Whether or not to do rounding of phaseout fraction (Form 8863 line 17 form-instruction rounding when `exact==1`). e87530: float Adjusted qualified lifetime learning expenses for all students (Form 8863 Part III line 31 summed across students; Part II line 10). MARS: int Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) c00100: float Adjusted Gross Income (Form 1040 line 11; Form 8863 line 14). c05800: float Total (regular + AMT) income tax liability before credits (CLW line 4). e07300: float Foreign tax credit from Form 1116 (Sch 3 line 1; CLW line 5). c07180: float Credit for child and dependent care expenses from Form 2441 (Sch 3 line 2; CLW line 5). c07200: float Schedule R credit for the elderly and the disabled (Sch 3 line 6d; CLW line 5). Sch 3 lines 6f / 6l / 6m also belong to CLW line 5 but are not modeled in Tax-Calculator records. c87668: float American Opportunity Credit nonrefundable amount from Form 8863 line 9 (= 0.6 * line 7), computed by `AmOppCreditParts`. LLC_Expense_c: float Lifetime learning credit expense limit (Form 8863 line 11 cap; $10,000 on the form). ETC_pe_Single: float Education tax credit phaseout end for non-joint filers, in thousands of dollars (Form 8863 line 13 = $90,000 for non-MFJ). ETC_pe_Married: float Education tax credit phaseout end for joint filers, in thousands of dollars (Form 8863 line 13 = $180,000 for MFJ). CR_Education_hc: float Education credits haircut. Reform construct; default 0.0 under current law (function emits on-form line 19 unmodified). c07230: float Records-bound output, computed below. Returns ------- c07230: float Education tax credits nonrefundable amount (Form 8863 line 19; Sch 3 line 3) with reform haircut applied. """ # ---- Form 8863 Part II lines 10-12 (LLC tentative) ---------------- # line 11 caps qualified expenses at LLC_Expense_c ($10,000 on form); # line 12 multiplies by 20% to get the pre-phaseout LLC. line12 = 0.2 * min(e87530, LLC_Expense_c) # ---- Form 8863 Part II lines 13-18 (AGI phaseout) ----------------- # ETC_pe_* are stored in thousands of dollars; * 1000. converts to # the on-form line-13 dollar values. Phaseout spread (line 16) is # $20,000 MFJ / $10,000 other. See IRC §25A(d)(1): # https://www.law.cornell.edu/uscode/text/26/25A#d_1 if MARS == 2: line13 = ETC_pe_Married * 1000. line16 = 20000. else: line13 = ETC_pe_Single * 1000. line16 = 10000. line15 = max(0., line13 - c00100) if exact == 1: # exact calculation as on tax forms line17 = min(1., round(line15 / line16, 3)) else: line17 = min(1., line15 / line16) line18 = line12 * line17 # ---- Credit Limit Worksheet (Form 8863 instructions) -------------- # CLW line 6 = c05800 - (FTC + CDCC + SchR); Sch 3 lines 6f/6l/6m # also subtracted on-form but unmodeled here. Sequential # application of LLC then nonref-AOTC below is algebraically # equivalent to the form's single min(CLW line 3, CLW line 6). clw_line6 = max(0., c05800 - (e07300 + c07180 + c07200)) llc_used = min(line18, clw_line6) aotc_used = min(c87668, max(0., clw_line6 - llc_used)) line19_pre_hc = llc_used + aotc_used # ---- reform haircut (current-law inert: CR_Education_hc == 0) ----- c07230 = line19_pre_hc * (1. - CR_Education_hc) return c07230 @iterate_jit(nopython=True) def CharityCredit(e19800, e20100, c00100, CR_Charity_rt, CR_Charity_f, CR_Charity_frt, MARS, charity_credit): """ Computes nonrefundable credit for charitable giving (a reform construct with no IRS form correspondence; not part of current-law policy). Under current law `CR_Charity_rt`, `CR_Charity_f`, and `CR_Charity_frt` all default to 0.0, so `charity_credit` is identically 0 and the function is inert. Under reform the credit equals CR_Charity_rt * max(0, (e19800 + e20100) - floor) where the floor is the larger of a MARS-indexed dollar amount (`CR_Charity_f[MARS-1]`) and an AGI-share (`CR_Charity_frt * c00100`); only giving in excess of the floor earns the credit. Downstream consumers: `charity_credit` is fed to `NonrefundableCredits`, where it is sequentially limited against the remaining tax-before-credits (`avail`), and the limited amount is then summed into the nonrefundable-credit total subtracted from tax in `IITAX`. Parameters ---------- e19800: float Itemizable charitable giving for cash and check contributions (Sch A line 11) e20100: float Itemizable charitable giving other than cash and check contributions (Sch A line 12) c00100: float Adjusted Gross Income (AGI; Form 1040 line 11) CR_Charity_rt: float Charity credit rate (reform-only; 0 under current law) CR_Charity_f: list MARS-indexed dollar floor: only giving above this amount is eligible (reform-only; 0 under current law) CR_Charity_frt: float AGI-share floor: only giving above this fraction of AGI is eligible (reform-only; 0 under current law) MARS: int Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) charity_credit: float Records-bound output, computed below. Returns ------- charity_credit: float Credit for charitable giving """ total_charity = e19800 + e20100 dollar_floor = CR_Charity_f[MARS - 1] floor = max(CR_Charity_frt * c00100, dollar_floor) eligible_giving = max(total_charity - floor, 0.) charity_credit = CR_Charity_rt * eligible_giving return charity_credit @iterate_jit(nopython=True) def NonrefundableCredits(c05800, e07240, e07260, e07300, e07400, e07600, p08000, odc, personal_nonrefundable_credit, CTC_is_refundable, ODC_is_refundable, CR_RetirementSavings_hc, CR_ForeignTax_hc, CR_ResidentialEnergy_hc, CR_GeneralBusiness_hc, CR_MinimumTax_hc, CR_OtherCredits_hc, charity_credit, c07180, c07200, c07220, c07230, c07240, c07260, c07300, c07400, c07600, c08000): """ Sequentially limits nonrefundable credits against remaining income tax liability and returns the limited per-credit amounts. The limited amounts are summed by `C1040` into Form 1040 line 22 via Form 1040 line 19 (CTC + Credit for Other Dependents) and line 20 (Schedule 3 Part I total). Application order mirrors Schedule 3 (2025) Part I line numbering plus Form 1040 line 19, with reform-construct credits appended: (A) Sch 3 Part I lines 1-4 (FTC -> CDCC -> Education -> RetSav) (B) Form 1040 line 19 (CTC -> ODC); CTC capping is skipped under reform-only `CTC_is_refundable` and ODC capping is skipped under reform-only `ODC_is_refundable`, with each credit independently routed to the refundable side in `IITAX` / `CTC_new` (C) Sch 3 Part I lines 5a, 6a, 6b, 6d, 6z (ResEnergy -> GenBus -> PrYrMin -> SchR -> Other); unmodeled on-form items are 5b, 6c, 6e-6m (D) Reform-only nonrefundable credits (charity_credit, personal_nonrefundable_credit); inert under current law CTC/ODC placement between Sch 3 line 4 and line 5a is consistent with Sch 8812 Credit Limit Worksheet A, which subtracts only Sch 3 lines 1, 2, 3, 4 (plus a set of unmodeled credits) from tax before applying CTC. Credits with statutory carryforwards (ResEnergy, GenBus, PrYrMin) come after CTC/ODC, which have no carryforward. Two flavors of inputs: * **Raw record values** `e07240`, `e07260`, `e07300`, `e07400`, `e07600`, `p08000`: the per-credit haircut `(1 - CR_*_hc)` is applied here, then `min` against remaining tax. All `CR_*_hc` parameters default to 0.0 (current-law inert). * **Pre-computed credits** `c07180`, `c07200`, `c07220`, `c07230`, `odc`, `charity_credit`, `personal_nonrefundable_credit`: the source function already applied any haircut and its own Credit Limit Worksheet; this function re-caps against remaining tax (belt-and-braces, and correcting any source-side worksheet that omitted a Sch 3 line ahead of itself -- e.g., `SchR` line-21 worksheet omits Sch 3 line 3 Education). Calling-order context: invoked by `Calculator.calc_all` after `ChildDepTaxCredit` (which produces `c07220` and `odc`) and the other per-credit source functions (`F2441` -> `c07180`, `EducationTaxCredit` -> `c07230`, `SchR` -> `c07200`, `CharityCredit` -> `charity_credit`, `PersonalTaxCredit` -> `personal_nonrefundable_credit`), and before `AdditionalCTC` / `C1040`. Parameters ---------- c05800: float Total (regular + AMT) income tax liability before credits (Form 1040 line 18) e07240: float Retirement savings contributions credit input from Form 8880 (Sch 3 line 4); haircut `(1 - CR_RetirementSavings_hc)` applied here e07260: float Residential clean energy credit input from Form 5695 line 15 (Sch 3 line 5a); haircut `(1 - CR_ResidentialEnergy_hc)` applied here e07300: float Foreign tax credit input from Form 1116 (Sch 3 line 1); haircut `(1 - CR_ForeignTax_hc)` applied here e07400: float General business credit input from Form 3800 (Sch 3 line 6a); haircut `(1 - CR_GeneralBusiness_hc)` applied here e07600: float Prior-year minimum tax credit input from Form 8801 (Sch 3 line 6b); haircut `(1 - CR_MinimumTax_hc)` applied here p08000: float Other nonrefundable credits input (Sch 3 line 6z); haircut `(1 - CR_OtherCredits_hc)` applied here odc: float Credit for Other Dependents from Sch 8812 (Form 1040 line 19 ODC component); pre-computed with `CTC_hc` already applied personal_nonrefundable_credit: float Reform-only personal nonrefundable credit from `PersonalTaxCredit`; current-law inert CTC_is_refundable: bool Reform-only switch (default false): when true, CTC is not limited here and is routed to the refundable side in `IITAX` / `CTC_new` ODC_is_refundable: bool Reform-only switch (default false): when true, ODC is not limited here and is routed to the refundable side in `IITAX` CR_RetirementSavings_hc: float Retirement savings credit haircut (default 0.0; current-law inert) CR_ForeignTax_hc: float Foreign tax credit haircut (default 0.0) CR_ResidentialEnergy_hc: float Residential clean energy credit haircut (default 0.0) CR_GeneralBusiness_hc: float General business credit haircut (default 0.0) CR_MinimumTax_hc: float Prior-year minimum tax credit haircut (default 0.0) CR_OtherCredits_hc: float Other nonrefundable credits haircut (default 0.0) charity_credit: float Reform-only nonrefundable charity credit from `CharityCredit`; current-law inert c07180: float Credit for child and dependent care expenses from Form 2441 (Sch 3 line 2); pre-computed by `F2441` c07200: float Schedule R credit for the elderly and the disabled (Sch 3 line 6d); pre-computed by `SchR` c07220: float Child tax credit from Sch 8812 (Form 1040 line 19 CTC component); pre-computed by `ChildDepTaxCredit` c07230: float Nonrefundable education credit from Form 8863 (Sch 3 line 3); pre-computed by `EducationTaxCredit` c07240: float Retirement savings credit limited output (overwritten here) c07260: float Residential clean energy credit limited output (overwritten here) c07300: float Foreign tax credit limited output (overwritten here) c07400: float General business credit limited output (overwritten here) c07600: float Prior-year minimum tax credit limited output (overwritten here) c08000: float Other nonrefundable credits limited output (overwritten here) Returns ------- c07180: float Limited credit for child and dependent care expenses (Sch 3 line 2) c07200: float Limited Schedule R credit (Sch 3 line 6d) c07220: float Limited child tax credit (Form 1040 line 19 CTC component); unchanged when `CTC_is_refundable` c07230: float Limited nonrefundable education credit (Sch 3 line 3) c07240: float Limited retirement savings credit (Sch 3 line 4) odc: float Limited Credit for Other Dependents (Form 1040 line 19 ODC component); unchanged when `ODC_is_refundable` c07260: float Limited residential clean energy credit (Sch 3 line 5a) c07300: float Limited foreign tax credit (Sch 3 line 1) c07400: float Limited general business credit (Sch 3 line 6a) c07600: float Limited prior-year minimum tax credit (Sch 3 line 6b) c08000: float Limited other nonrefundable credits (Sch 3 line 6z) charity_credit: float Limited reform-only nonrefundable charity credit personal_nonrefundable_credit: float Limited reform-only personal nonrefundable credit """ # Schedule 3 (2025) Part I + Form 1040 line 19: sequential limit # against remaining tax liability, mirroring the on-form line # order with reform-construct credits appended. avail = c05800 # (A) Sch 3 Part I lines 1-4 # Sch 3 line 1 -- Foreign tax credit (Form 1116) c07300 = min(e07300 * (1. - CR_ForeignTax_hc), avail) avail = avail - c07300 # Sch 3 line 2 -- Credit for child and dependent care expenses (Form 2441) c07180 = min(c07180, avail) avail = avail - c07180 # Sch 3 line 3 -- Education credits (Form 8863 line 19) c07230 = min(c07230, avail) avail = avail - c07230 # Sch 3 line 4 -- Retirement savings contributions credit (Form 8880) c07240 = min(e07240 * (1. - CR_RetirementSavings_hc), avail) avail = avail - c07240 # (B) Form 1040 line 19 -- CTC + Credit for Other Dependents (Sch 8812); # each component independently skipped under its reform-only refundability # switch (routed to refundable side in IITAX / CTC_new). # Form 1040 line 19 CTC component if not CTC_is_refundable: c07220 = min(c07220, avail) avail = avail - c07220 # Form 1040 line 19 ODC component if not ODC_is_refundable: odc = min(odc, avail) avail = avail - odc # (C) Sch 3 Part I lines 5a, 6a, 6b, 6d, 6z (carryforward-eligible # credits come after CTC/ODC, which have no carryforward) # Sch 3 line 5a -- Residential clean energy credit (Form 5695 line 15) c07260 = min(e07260 * (1. - CR_ResidentialEnergy_hc), avail) avail = avail - c07260 # Sch 3 line 6a -- General business credit (Form 3800) c07400 = min(e07400 * (1. - CR_GeneralBusiness_hc), avail) avail = avail - c07400 # Sch 3 line 6b -- Prior-year minimum tax credit (Form 8801) c07600 = min(e07600 * (1. - CR_MinimumTax_hc), avail) avail = avail - c07600 # Sch 3 line 6d -- Credit for the elderly or the disabled (Sch R) c07200 = min(c07200, avail) avail = avail - c07200 # Sch 3 line 6z -- Other nonrefundable credits c08000 = min(p08000 * (1. - CR_OtherCredits_hc), avail) avail = avail - c08000 # (D) Reform-only nonrefundable credits (current-law inert) charity_credit = min(charity_credit, avail) avail = avail - charity_credit personal_nonrefundable_credit = min(personal_nonrefundable_credit, avail) avail = avail - personal_nonrefundable_credit return (c07180, c07200, c07220, c07230, c07240, odc, c07260, c07300, c07400, c07600, c08000, charity_credit, personal_nonrefundable_credit) @iterate_jit(nopython=True) def AdditionalCTC(actc_claim_thd, codtc_limited, ACTC_c, n24, earned, ACTC_Income_thd, ACTC_rt, nu06, ACTC_rt_bonus_under6family, ACTC_ChildNum, CTC_is_refundable, CTC_include17, CTC_c, age_head, age_spouse, MARS, nu18, ptax_was, c03260, e09800, c59660, e11200, c11070): """ Calculates refundable Additional Child Tax Credit (ACTC), c11070, following Schedule 8812 Part II (Parts II-A, II-B, II-C). Parameters ---------- actc_claim_thd: float ACTC amount below which ACTC is unclaimed codtc_limited: float Sch 8812 line 16a: Part I tentative credit minus the nonrefundable portion absorbed (line 12 minus line 14); set in ChildDepTaxCredit ACTC_c: float Maximum refundable additional child tax credit n24: int Number of children who are Child-Tax-Credit eligible, one condition for which is being under age 17 earned: float Earned income for filing unit ACTC_Income_thd: float Additional Child Tax Credit income threshold ACTC_rt: float Additional Child Tax Credit rate nu06: int Number of dependents under 6 years old ACTC_rt_bonus_under6family: float Bonus additional child tax credit rate for families with qualifying children under 6 (reform-only; no form correspondence) ACTC_ChildNum: float Additional Child Tax Credit minimum number of qualified children for different formula ptax_was: float Employee and employer OASDI plus HI FICA tax c03260: float Self-employment tax deduction (after haircut) e09800: float Unreported payroll taxes from Form 4137 or 8919 c59660: float EITC amount e11200: float Excess payroll (FICA/RRTA) tax withheld c11070: float Child tax credit (refunded) from Form 8812 Returns ------- c11070: float Child tax credit (refunded) from Form 8812 """ # Sch 8812 line 16a: leftover Part I tentative credit line16a = codtc_limited # Sch 8812 line 16b: max refundable amount = ACTC_c per qualifying child if CTC_is_refundable: # reform-only: refundable portion handled in Part I line16b = 0. else: # reform-only: redefine "qualifying child" as under 18 instead # of under 17 if CTC_include17: tu18 = int(age_head < 18) # taxpayer is under age 18 su18 = int(MARS == 2 and age_spouse < 18) # spouse is under age 18 childnum = n24 + max(0, nu18 - tu18 - su18 - n24) else: childnum = n24 line16b = min(ACTC_c, CTC_c) * childnum c11070 = 0. # default if no refundable amount if line16a > 0. and line16b > 0.: # Sch 8812 line 17: smaller of line 16a or line 16b line17 = min(line16a, line16b) # Part II-A # Sch 8812 line 18a / line 20: earned income excess over threshold earned_excess = max(0., earned - ACTC_Income_thd) # reform-only: ACTC rate bonus for families with children under 6 if nu06 == 0: ACTC_rate = ACTC_rt else: ACTC_rate = ACTC_rt + ACTC_rt_bonus_under6family # Sch 8812 line 20: ACTC_rt times earned-income excess line20 = ACTC_rate * earned_excess if childnum < ACTC_ChildNum: # Sch 8812 line 27 (Part II-C): smaller of line 17 or line 20 if line20 > 0.: c11070 = min(line17, line20) else: # childnum >= ACTC_ChildNum: consider Part II-B alternative if line20 >= line17: c11070 = line17 else: # Part II-B # Sch 8812 line 22: SS+Medicare+Add'l Medicare withheld line22 = 0.5 * ptax_was # Sch 8812 line 23: deductible SE tax + unreported FICA line23 = c03260 + e09800 # Sch 8812 line 24: sum of lines 22 and 23 line24 = line22 + line23 # Sch 8812 line 25: EITC + excess SS/RRTA withheld line25 = c59660 + e11200 # Sch 8812 line 26: line 24 minus line 25 (not less than 0) line26 = max(0., line24 - line25) # Sch 8812 line 27 (Part II-b3157 # min(line 17, max(line 20, line 26)) line27 = max(line20, line26) c11070 = min(line17, line27) # approximate ACTC claiming behavior if c11070 < actc_claim_thd: c11070 = 0. return c11070 @iterate_jit(nopython=True) def C1040(c05800, c07180, c07200, c07220, c07230, c07240, c07260, c07300, c07400, c07600, c08000, e09700, e09800, e09900, niit, setax, ptax_amc, othertaxes, c07100, c09200, odc, charity_credit, personal_nonrefundable_credit, CTC_is_refundable, ODC_is_refundable): """ Assembles Form 1040 (2025) lines 22-24 + Schedule 2 (2025) Part II line 21: * `c07100` = Form 1040 line 21 = total nonrefundable credits used (Sch 3 line 8 + Form 1040 line 19 CTC + ODC components), re-summed here from the per-credit limited amounts produced by `NonrefundableCredits` and `ChildDepTaxCredit`. * `othertaxes` = Sch 2 line 21 = Form 1040 line 23, the sum of the six modeled "Other Taxes" items. * `c09200` = Form 1040 line 24 = line 22 + line 23, total tax before refundable credits and withholding (which are payments, not taxes, and are applied later by `IITAX`). https://www.irs.gov/pub/irs-pdf/f1040s2.pdf Schedule 2 Part II line 21 = line 4 + (lines 7-16) + line 18 + line 19. Modeled items: * line 4 -- Self-employment tax (Schedule SE) <- setax * line 7 -- Total addl SS/Medicare tax (Forms 4137 + 8919) <- e09800 * line 8 -- Addl tax on IRAs/other tax-favored accts <- e09900 * line 11 -- Additional Medicare Tax (Form 8959) <- ptax_amc * line 12 -- Net Investment Income Tax (Form 8960) <- niit * line 18 -- Total addl taxes (only line 17a recapture of investment credit modeled) <- e09700 Unmodeled Sch 2 Part II items: line 9 (household employment taxes, Sch H); lines 13-16 (uncollected SS/Medicare on tips, interest on installment-sale deferred tax, low-income housing recapture); the rest of line 17 (HSA/MSA, NQDC, golden parachute, Form 4255 EPE, etc.); line 19 (recapture of net EPE); line 20 (sec 965 installment). Schedule 2 Part I (line 1z additions to tax + line 2 AMT) is folded into the input `c05800` only via line 2 (AMT, produced by `AMT`); Part I additions (premium-tax-credit repayment, clean vehicle credit repayments, Form 4255 EPE additions) are unmodeled, so c05800 ~= Form 1040 line 16 + Sch 2 line 2 ~= Form 1040 line 18. Reform-only knobs: * `CTC_is_refundable` -- when true, CTC (`c07220`) is excluded from `c07100` here and `NonrefundableCredits` skips CTC capping; CTC is routed to the refundable side via `CTC_new` / `IITAX`. * `ODC_is_refundable` -- when true, ODC (`odc`) is excluded from `c07100` here and `NonrefundableCredits` skips ODC capping; ODC is routed to the refundable side via `IITAX`. Calling order (calculator.py): invoked after `NonrefundableCredits` and `AdditionalCTC` (so each per-credit input has had its final Sch 3 / Sch 8812 cap applied) and before `CTC_new` / `IITAX` (which subtract refundable credits from `c09200`). Parameters ---------- c05800: float Total (regular + AMT) income tax liability before credits (Form 1040 line 16 + Sch 2 line 2; approximates Form 1040 line 18 since Sch 2 line 1z additions are unmodeled) c07180: float Limited credit for child and dependent care expenses from Form 2441 (Sch 3 line 2) c07200: float Limited Schedule R credit for the elderly and the disabled (Sch 3 line 6d) c07220: float Limited child tax credit from Sch 8812 (Form 1040 line 19 CTC component); excluded from `c07100` when `CTC_is_refundable` c07230: float Limited nonrefundable education credit from Form 8863 (Sch 3 line 3) c07240: float Limited retirement savings credit from Form 8880 (Sch 3 line 4) c07260: float Limited residential clean energy credit from Form 5695 (Sch 3 line 5a) c07300: float Limited foreign tax credit from Form 1116 (Sch 3 line 1) c07400: float Limited general business credit from Form 3800 (Sch 3 line 6a) c07600: float Limited prior-year minimum tax credit from Form 8801 (Sch 3 line 6b) c08000: float Limited other nonrefundable credits (Sch 3 line 6z) e09700: float Recapture of Investment Credit (Sch 2 line 17a, the only modeled component of Sch 2 line 18) e09800: float Unreported payroll taxes from Form 4137 + Form 8919 (Sch 2 lines 5 + 6 -> Sch 2 line 7) e09900: float Additional tax on IRAs / other tax-favored accounts from Form 5329 (Sch 2 line 8) niit: float Net Investment Income Tax from Form 8960 (Sch 2 line 12) setax: float Self-employment tax from Schedule SE (Sch 2 line 4) ptax_amc: float Additional Medicare Tax from Form 8959 (Sch 2 line 11) othertaxes: float Records-bound output: Schedule 2 line 21 / Form 1040 line 23 (overwritten here) c07100: float Records-bound output: total nonrefundable credits used, Form 1040 line 21 (overwritten here) c09200: float Records-bound output: total tax before refundable credits, Form 1040 line 24 (overwritten here) odc: float Limited Credit for Other Dependents from Sch 8812 (Form 1040 line 19 ODC component); excluded from `c07100` when `ODC_is_refundable` charity_credit: float Reform-only nonrefundable charity credit (current-law inert) personal_nonrefundable_credit: float Reform-only personal nonrefundable credit (current-law inert) CTC_is_refundable: bool Reform-only switch (default false): when true, CTC is routed to the refundable side rather than included in `c07100` ODC_is_refundable: bool Reform-only switch (default false): when true, ODC is routed to the refundable side rather than included in `c07100` Returns ------- c07100: float Total nonrefundable credits used (Form 1040 line 21) othertaxes: float Sum of the six modeled Sch 2 Part II line 21 components (setax, e09800, e09900, ptax_amc, niit, e09700) = Form 1040 line 23 c09200: float Total tax before refundable credits = Form 1040 line 24 = line 22 + line 23 """ # (A) Form 1040 line 21 = Sch 3 line 8 + Form 1040 line 19: # sum of limited nonrefundable credits produced by # NonrefundableCredits and ChildDepTaxCredit (= total credits # used against c05800). Addition order preserved bit-exactly. c07100 = (c07180 # Sch 3 line 2 CDCC + c07200 # Sch 3 line 6d SchR + c07600 # Sch 3 line 6b PrYrMin + c07300 # Sch 3 line 1 FTC + c07400 # Sch 3 line 6a GenBus + c07220 * (1. - CTC_is_refundable) # 1040 line 19 CTC + c08000 # Sch 3 line 6z Other + c07230 # Sch 3 line 3 Education + c07240 # Sch 3 line 4 RetSav + c07260 # Sch 3 line 5a ResEnergy + odc * (1. - ODC_is_refundable) # 1040 line 19 ODC + charity_credit # reform-only + personal_nonrefundable_credit) # reform-only # (B) Form 1040 (2025) line 22 = max(0, line 18 - line 21): # tax remaining after nonrefundable credits applied. line22 = max(0., c05800 - c07100) # (C) Schedule 2 Part II line 21 / Form 1040 line 23: Other Taxes. # Modeled items only (lines 9, 13-16, 17b-17z besides 17a, 19, 20 # are unmodeled). Addition order preserved. othertaxes = (e09700 # Sch 2 line 18 (17a only) + e09800 # Sch 2 line 7 (lines 5+6) + e09900 # Sch 2 line 8 + niit # Sch 2 line 12 + setax # Sch 2 line 4 + ptax_amc) # Sch 2 line 11 # (D) Form 1040 (2025) line 24 = line 22 + line 23: total tax # before refundable credits (which are payments, applied later). c09200 = othertaxes + line22 return (c07100, othertaxes, c09200) @iterate_jit(nopython=True) def CTC_new(CTC_new_c, CTC_new_rt, CTC_new_c_under6_bonus, CTC_new_ps, CTC_new_prt, CTC_new_for_all, CTC_include17, CTC_new_refund_limited, CTC_new_refund_limit_payroll_rt, CTC_new_refund_limited_all_payroll, payrolltax, exact, n24, nu06, age_head, age_spouse, nu18, c00100, MARS, ptax_oasdi, c09200, ctc_new): """ Computes a reform-construct refundable Child Tax Credit (`ctc_new`) that is added on top of, not in place of, the current-law CTC/ODC machinery in `ChildDepTaxCredit` and `AdditionalCTC`. No IRS form: this credit does not correspond to any current-law Form 1040 / Schedule 8812 line. All `CTC_new_*` policy parameters default to 0 / false for all years except 2021, where defaults emulate the ARPA-2021 expanded CTC (`CTC_new_c=1000`, `CTC_new_c_under6_bonus=600`, `CTC_new_for_all=true`, `CTC_new_ps={75k single / 150k MFJ / 75k MFS / 112.5k HoH / 150k widow}`, `CTC_new_prt=0.05`). Under 2025 current law every parameter is 0/false, so `ctc_new` is identically zero. Body sections: (A) qualifying-child count: `n24`, optionally widened via `CTC_include17` to count under-18 non-`n24` dependents (same block as in `ChildDepTaxCredit` row 29). (B) tentative credit: `CTC_new_c·childnum + CTC_new_c_under6_bonus·nu06`, optionally capped at `CTC_new_rt·posagi` when `CTC_new_for_all=false` (an AGI-based phase-in). (C) AGI phase-out: reduction of `CTC_new_prt` per dollar of AGI above `CTC_new_ps[MARS-1]`, with `exact==1` rounding excess up to the next $1000 (mirrors the Sch 8812 line-9 step). (D) reform-only payroll-tax refundability cap: when `CTC_new_refund_limited=true`, the portion of `ctc_new` exceeding pre-refundable-credits liability `c09200` is capped at `CTC_new_refund_limit_payroll_rt` times either OASDI-only (`ptax_oasdi`) or all-FICA (`payrolltax`) payroll tax depending on `CTC_new_refund_limited_all_payroll`. Calling order: runs after `C1040` (so `c09200`, `ptax_oasdi`, and `payrolltax` are final) and immediately before `IITAX`, which subtracts `ctc_new` on the refundable side and rolls it into the `ctc_total` / `ctc_refundable` records-bound aggregates. Parameters ---------- CTC_new_c: float Reform: maximum new refundable CTC per qualifying child. CTC_new_rt: float Reform: AGI phase-in rate when `CTC_new_for_all=false`. CTC_new_c_under6_bonus: float Reform: bonus added to `CTC_new_c` for each under-6 dependent. CTC_new_ps: list Reform: MARS-indexed AGI threshold at which the phase-out begins. CTC_new_prt: float Reform: phase-out reduction rate per dollar of AGI above `CTC_new_ps[MARS-1]`. CTC_new_for_all: bool Reform: when true, full `CTC_new_c·childnum + ...` is available regardless of AGI (no `CTC_new_rt` phase-in); when false the tentative credit is capped at `CTC_new_rt·posagi`. CTC_include17: bool Reform: when true, dependents with `age < 18` not already in `n24` are added to `childnum` (same widening used in `ChildDepTaxCredit`). CTC_new_refund_limited: bool Reform: when true, the refund portion (excess of `ctc_new` over `c09200`) is capped via the payroll-tax limit below. CTC_new_refund_limit_payroll_rt: float Reform: payroll-tax-share cap on the refund portion of `ctc_new`. CTC_new_refund_limited_all_payroll: bool Reform: when true, the payroll cap applies to total FICA (`payrolltax`); when false it applies to OASDI only (`ptax_oasdi`). payrolltax: float Total (employee + employer) FICA payroll tax liability. exact: int When 1, round the phase-out excess up to the next $1000 (Sch 8812 line-9 step); when 0, use the smooth excess. n24: int Number of CTC-eligible children (a condition for which is being under age 17). nu06: int Number of dependents under 6 years old. age_head: int Age of taxpayer (filer); used by the `CTC_include17` widening. age_spouse: int Age of spouse (or 0 if not MFJ); used by the `CTC_include17` widening. nu18: int Number of dependents under 18 years old; used by the `CTC_include17` widening. c00100: float Adjusted Gross Income (AGI); floored at 0 as `posagi`. MARS: int Filing status (1=single, 2=MFJ, 3=MFS, 4=HoH, 5=widow(er)). ptax_oasdi: float Employee + employer OASDI FICA plus SE tax (excludes HI FICA). c09200: float Total income tax (including other taxes) after nonrefundable credits, before refundable credits. ctc_new: float Records-bound output, computed below. Returns ------- ctc_new: float Reform-only new refundable child tax credit; fed into `IITAX` on the refundable side. """ # (A) qualifying-child count (under-18 widening is reform-only) if CTC_include17: tu18 = int(age_head < 18) # taxpayer is under age 18 su18 = int(MARS == 2 and age_spouse < 18) # spouse is under age 18 childnum = n24 + max(0, nu18 - tu18 - su18 - n24) else: childnum = n24 if childnum > 0: posagi = max(c00100, 0.) # (B) tentative credit + optional AGI phase-in ctc_new = CTC_new_c * childnum + CTC_new_c_under6_bonus * nu06 if not CTC_new_for_all: ctc_new = min(CTC_new_rt * posagi, ctc_new) # (C) AGI phase-out above CTC_new_ps[MARS-1] ymax = CTC_new_ps[MARS - 1] if posagi > ymax: excess = posagi - ymax if exact == 1: # exact calculation as on tax form excess = math.ceil(excess / 1000.) * 1000. ctc_new = max(0., ctc_new - CTC_new_prt * excess) # (D) reform-only payroll-tax cap on the refund portion if ctc_new > 0. and CTC_new_refund_limited: refund_new = max(0., ctc_new - c09200) if CTC_new_refund_limited_all_payroll: limit_new = CTC_new_refund_limit_payroll_rt * payrolltax else: limit_new = CTC_new_refund_limit_payroll_rt * ptax_oasdi limited_new = max(0., refund_new - limit_new) ctc_new = max(0., ctc_new - limited_new) else: ctc_new = 0. return ctc_new @iterate_jit(nopython=True) def IITAX(c59660, c11070, c10960, personal_refundable_credit, ctc_new, rptc, c09200, payrolltax, CDCC_refund, recovery_rebate_credit, eitc, c07220, odc, CTC_is_refundable, ODC_is_refundable, refund, ctc_total, ctc_refundable, ctc_nonrefundable, iitax, combined): """ Final assembly: sums refundable credits and computes total income-tax liability net of those credits. Maps to Form 1040 (2025) lines 22-37 with two model-specific simplifications: (1) `refund` here is the analogue of Form 1040 line 32 (refundable- credit sum) ONLY. Lines 25d (federal income tax withheld) and 26 (estimated tax payments) are not modeled by Tax-Calculator, so line 33 = 25d + 26 + 32 reduces to line 32 in the model. (2) `iitax` = c09200 - refund = Form 1040 line 24 - line 32. The model does not split this into line 34 (refund when 33 > 24) vs. line 37 (owed when 24 > 33); `iitax` is sign-permissive (positive = amount owed, negative = refund position). Body sections: (A) Reform-only refundable conversions. When `CTC_is_refundable` or `ODC_is_refundable` are true (both default false under current law), the CTC (c07220) and ODC (odc) amounts that were applied as nonrefundable credits in `NonrefundableCredits` / `C1040` are added on the refundable side here. (B) Form 1040 line 32 build. Each addend is annotated with its 1040 line (27a EIC / 28 ACTC / 29 AOC / 30 historical RRC / 31 Sch 3 line 13 refundable CDCC) or reform-only label (no form line). (C) Records-bound CTC diagnostic. `ctc_total` / `ctc_refundable` / `ctc_nonrefundable` summarize the union of `ChildDepTaxCredit` (c07220 nonref + odc), `AdditionalCTC` (c11070 refundable), and `CTC_new` (reform-only) — partitioned by whether each piece is refundable under the current parameter regime. Not on the form. (D) Form 1040 line 24 − line 32 net + combined-tax accumulator. `combined` = iitax + payrolltax; `lumpsum_tax` is added later by `LumpSumTax` (also a reform construct, inert under current law). Reform constructs (no Form 1040 (2025) line): `personal_refundable_credit` (II_credit* knobs from `PersonalTaxCredit`), `ctc_new` (from `CTC_new`), `rptc` (from `RefundablePayrollTaxCredit`), and the `recovery_rebate_credit` (historically Form 1040 line 30 for 2020 CARES / 2021 ARPA RRC; inert for 2022+). Calling order: invoked by `Calculator._calc_one_year` after `CTC_new` (so c09200, c07220, odc, c10960, c11070, c59660, CDCC_refund are all final) and before `FairShareTax` / `LumpSumTax` / `BenefitPrograms`. Parameters ---------- c59660: float EITC amount from EITC function (Form 1040 line 27a) c11070: float Refundable Additional CTC from AdditionalCTC / Schedule 8812 Part II (Form 1040 line 28) c10960: float American Opportunity Credit refundable portion from EducationTaxCredit / Form 8863 line 8 (Form 1040 line 29) personal_refundable_credit: float Reform-only refundable personal credit from `PersonalTaxCredit` (no Form 1040 line; default 0 under current law) ctc_new: float Reform-only new refundable child tax credit from `CTC_new` (no Form 1040 line; default 0 under current law) rptc: float Reform-only refundable payroll tax credit for filing unit from `RefundablePayrollTaxCredit` (no Form 1040 line; default 0) c09200: float Total income tax (including other taxes, after nonrefundable credits) from `C1040` (Form 1040 line 24) payrolltax: float Total (employee + employer) payroll tax liability from `EI_PayrollTax` CDCC_refund: float Refundable portion of Child and Dependent Care Credit from `CDCC_refundable` (Schedule 3 line 13 → Form 1040 line 31) recovery_rebate_credit: float Recovery Rebate Credit from `PersonalTaxCredit` (historical Form 1040 line 30 for 2020/2021; inert for 2022+ under current law) eitc: float Records-bound output; rebound to `c59660` here c07220: float Nonrefundable CTC (post-NonrefundableCredits cap) from `ChildDepTaxCredit` / Schedule 8812 Credit Limit Worksheet A odc: float Nonrefundable Credit for Other Dependents (post-cap) from `ChildDepTaxCredit` / Schedule 8812 CTC_is_refundable: bool Reform-only switch (default false): when true, the entire CTC is treated as refundable (Schedule 8812 nonref portion `c07220` is added to `refund` here) ODC_is_refundable: bool Reform-only switch (default false): when true, ODC is treated as refundable (`odc` is added to `refund` here) refund: float Records-bound output (re-computed) ctc_total: float Records-bound output (re-computed) ctc_refundable: float Records-bound output (re-computed) ctc_nonrefundable: float Records-bound output (re-computed) iitax: float Records-bound output (re-computed) combined: float Records-bound output (re-computed) Returns ------- eitc: float Earned Income Credit (= c59660; Form 1040 line 27a) refund: float Sum of refundable credits (model-specific Form 1040 line 32 analogue; lines 25d and 26 not modeled) ctc_total: float Diagnostic: c07220 + c11070 + odc + ctc_new ctc_refundable: float Diagnostic: portion of ctc_total that is refundable ctc_nonrefundable: float Diagnostic: max(0, ctc_total - ctc_refundable) iitax: float Total federal individual income tax liability = c09200 − refund (sign-permissive: positive = amount owed, negative = refund position; model does not split into Form 1040 lines 34 vs 37) combined: float iitax + payrolltax (lumpsum_tax added later by `LumpSumTax`) """ eitc = c59660 # (A) reform-only refundable conversions of CTC / ODC ctc_refund = c07220 if CTC_is_refundable else 0. odc_refund = odc if ODC_is_refundable else 0. # (B) Form 1040 line 32: refundable-credit sum refund = (eitc + # Form 1040 line 27a (EIC) c11070 + # Form 1040 line 28 (ACTC) c10960 + # Form 1040 line 29 (AOC) recovery_rebate_credit + # Form 1040 line 30 (historical RRC) CDCC_refund + # Sch 3 line 13 → Form 1040 line 31 personal_refundable_credit + # reform-only (no form line) ctc_new + # reform-only (no form line) rptc + # reform-only (no form line) ctc_refund + # reform-only (CTC_is_refundable) odc_refund) # reform-only (ODC_is_refundable) # (C) records-bound CTC diagnostic (not on the form) ctc_total = c07220 + c11070 + odc + ctc_new ctc_refundable = ctc_refund + c11070 + odc_refund + ctc_new ctc_nonrefundable = max(0., ctc_total - ctc_refundable) # (D) Form 1040 line 24 − line 32 + combined-tax accumulator iitax = c09200 - refund combined = iitax + payrolltax return (eitc, refund, ctc_total, ctc_refundable, ctc_nonrefundable, iitax, combined) @iterate_jit(nopython=True) def FairShareTax(c00100, MARS, ptax_was, setax, ptax_amc, FST_AGI_trt, FST_AGI_thd_lo, FST_AGI_thd_hi, fstax, iitax, combined, surtax): """ Computes Fair Share Tax (aka "Buffett Rule") — a reform-only minimum tax on high-AGI filers. No IRS form correspondence: the construct models the 2012 Paying a Fair Share Act / Buffett Rule proposal, which was never enacted. Inert under current law because the rate parameter `FST_AGI_trt` defaults to 0.0 for all years 2013+. Mechanics (when active under reform): tentative = c00100 * FST_AGI_trt - iitax - employee_share where employee_share = 0.5*ptax_was + 0.5*setax + ptax_amc (the worker-borne half of OASDI+HI FICA and SECA, plus the employee-paid Additional Medicare Tax — credited against the minimum to avoid double-counting payroll already paid). The tentative amount is floored at 0, then linearly phased in between `FST_AGI_thd_lo` and `FST_AGI_thd_hi` (MARS-indexed), fully imposed at or above the high threshold. The resulting `fstax` is added to three running accumulators: `iitax` (income-tax total flowing to Form 1040 line 24), `combined` (iitax + payrolltax + lumpsum_tax), and `surtax` (records-bound diagnostic also incremented by `AGIsurtax`; see row 18). Called by `Calculator.calc_all` after `_calc_one_year` finishes, so `iitax`, `ptax_was`, `setax`, and `ptax_amc` are already final. Parameters ---------- c00100: float Adjusted Gross Income (AGI) MARS: int Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) ptax_was: float Employee and employer OASDI plus HI FICA tax on wages and salaries (`EI_PayrollTax` output) setax: float Self-employment tax (`EI_PayrollTax` output) ptax_amc: float Additional Medicare Tax (`AdditionalMedicareTax` output) FST_AGI_trt: float Reform-only minimum-tax rate applied to AGI (default 0.0 → function inert) FST_AGI_thd_lo: list MARS-indexed AGI floor below which no minimum tax is imposed FST_AGI_thd_hi: list MARS-indexed AGI level at which the minimum tax is fully phased in; equal to `FST_AGI_thd_lo` disables the phase-in fstax: float Records-bound output: Fair Share Tax amount iitax: float Records-bound accumulator: total federal income tax liability combined: float Records-bound accumulator: iitax + payrolltax + lumpsum_tax surtax: float Records-bound accumulator: diagnostic surtax total (also incremented by `AGIsurtax`) Returns ------- fstax: float Fair Share Tax amount iitax: float Total federal individual income tax liability combined: float Sum of iitax and payrolltax and lumpsum_tax surtax: float Individual income tax subtotal augmented by fstax """ # Reform construct: inert under current law (FST_AGI_trt = 0). thd_lo = FST_AGI_thd_lo[MARS - 1] if FST_AGI_trt <= 0. or c00100 < thd_lo: fstax = 0. return (fstax, iitax, combined, surtax) thd_hi = FST_AGI_thd_hi[MARS - 1] # Tentative minimum tax: rate * AGI, credited for income tax and the # worker-borne half of payroll (½ FICA + ½ SECA + employee AMC). employee_share = 0.5 * ptax_was + 0.5 * setax + ptax_amc fstax = max(c00100 * FST_AGI_trt - iitax - employee_share, 0.) # Linear phase-in between thd_lo and thd_hi (no phase-in if equal). thd_gap = max(thd_hi - thd_lo, 0.) if thd_gap > 0. and c00100 < thd_hi: fstax *= (c00100 - thd_lo) / thd_gap iitax += fstax combined += fstax surtax += fstax return (fstax, iitax, combined, surtax) @iterate_jit(nopython=True) def LumpSumTax(DSI, num, XTOT, LST, lumpsum_tax, combined): """ Computes a per-capita lump-sum ("head") tax and adds it to combined taxes. No IRS form correspondence: ``LST`` is a reform construct (section "Surtaxes / Lump-Sum Tax" in ``policy_current_law.json``) and defaults to 0.0 from 2013 onward, so this function is inert under current law. When active, every member of a non-dependent filing unit pays ``LST`` dollars; filing units claimed as dependents elsewhere (``DSI == 1``) are exempt. The head count is ``max(num, XTOT)``: ``num`` is 2 for MFJ and 1 otherwise, which floors the count at the number of filers when ``XTOT`` is unusually low. The resulting tax is added only to ``combined`` (not to ``iitax`` or ``payrolltax``). Parameters ---------- DSI: int 1 if claimed as dependent on another return, otherwise 0 num: int 2 when MARS is 2 (married filing jointly); otherwise 1 XTOT: int Total number of exemptions for filing unit LST: float Dollar amount of lump-sum tax (reform parameter; 0.0 under current law) lumpsum_tax: float Lumpsum (or head) tax combined: float Sum of iitax and payrolltax and lumpsum_tax Returns ------- lumpsum_tax: float Lumpsum (or head) tax combined: float Sum of iitax and payrolltax and lumpsum_tax """ if LST == 0.0 or DSI == 1: lumpsum_tax = 0. else: lumpsum_tax = LST * max(num, XTOT) combined += lumpsum_tax return (lumpsum_tax, combined) @iterate_jit(nopython=True) def ExpandIncome(e00200, pencon_p, pencon_s, e00300, e00400, e00600, e00700, e00800, e00900, e01100, e01200, e01400, e01500, e02000, e02100, p22250, p23250, cmbtp, ptax_was, benefit_value_total, expanded_income): """ Computes the records-bound `expanded_income` aggregate — a broad pre-tax income measure used for distributional analysis. This is a model-internal concept with no IRS form/schedule counterpart: it is not part of the tax-liability path and feeds only `AfterTaxIncome` (and downstream distributional tables). Relative to AGI (`c00100`), `expanded_income` differs by: + adding tax-exempt interest (`e00400`), + adding back DC pension contributions (`pencon_p`, `pencon_s`), so wage compensation enters gross rather than net of pension deferrals already removed from `e00200`, + adding the employer share of FICA (`0.5 * ptax_was`), + adding non-AGI AMT preference items (`cmbtp`, from Form 6251), + adding the consumption value of transfer benefits (`benefit_value_total`, from `BenefitPrograms`), − but NOT subtracting Sch 1 Part II above-the-line adjustments (`c02900`); Sch C / Sch E / Sch F enter gross of those. Sch C / Sch E / Sch F / Sch D / Form 4797 items enter at their statutory net (income net of losses), so capital losses and business losses reduce `expanded_income`. Body is organized into seven groups (A)-(G), summed in this order to preserve floating-point bit-exactness with the prior implementation. The categories are commentary only; no parenthesized subtotals are introduced. Calling order: `calculator.calc_all` invokes `ExpandIncome` after `BenefitPrograms` (which produces `benefit_value_total`) and after `_calc_one_year` / `FairShareTax` / `LumpSumTax`; it precedes `AfterTaxIncome`, which subtracts the `combined` total tax to produce `aftertax_income`. Parameters ---------- e00200: float Wages, salaries, tips/otime for filing unit net of pension contributions pencon_p: float Contributions to defined-contribution pension plans for taxpayer pencon_s: float Contributions to defined-contribution pension plans for spouse e00300: float Taxable interest income e00400: float Tax-exempt interest income (not in AGI; expanded-income add-on) e00600: float Ordinary dividends included in AGI e00700: float Taxable refunds of state and local income taxes e00800: float Alimony received e00900: float Schedule C business net profit/loss for filing unit e01100: float Capital gain distributions not reported on Schedule D e01200: float Other net gain/loss from Form 4797 e01400: float Taxable IRA distributions e01500: float Total pensions and annuities e02000: float Schedule E total rental, royalty, partnership, S-corporation, etc, income/loss e02100: float Farm net income/loss for filing unit from Schedule F p22250: float Schedule D net short term capital gains/losses p23250: float Schedule D net long term capital gains/losses cmbtp: float Estimate of income on (AMT) Form 6251 but not in AGI ptax_was: float Employee + employer OASDI and HI FICA tax on wages (from `EI_PayrollTax`); half is the employer share, added here as employer-side compensation not present in `e00200`. benefit_value_total: float Consumption value of all benefits received by tax unit (from `BenefitPrograms`); included in expanded income. expanded_income: float Records-bound output, computed below. Returns ------- expanded_income: float Broad pre-tax income measure (see body docstring above). """ expanded_income = ( # ---- (A) Wage compensation gross of DC pension contributions ---- e00200 + # wage and salary income net of DC pension contributions pencon_p + # tax-advantaged DC pension contributions for taxpayer pencon_s + # tax-advantaged DC pension contributions for spouse # ---- (B) Investment income incl. non-AGI tax-exempt interest ---- e00300 + # taxable interest income e00400 + # non-taxable interest income e00600 + # dividends # ---- (C) Other Sch 1 / 1040 ordinary income items ---- e00700 + # state and local income tax refunds e00800 + # alimony received e00900 + # Sch C business net income/loss e01100 + # capital gain distributions not reported on Sch D e01200 + # Form 4797 other net gain/loss e01400 + # taxable IRA distributions e01500 + # total pension & annuity income (including DB-plan benefits) e02000 + # Sch E total rental, ..., partnership, S-corp income/loss e02100 + # Sch F farm net income/loss # ---- (D) Sch D realized capital gains/losses ---- p22250 + # Sch D: net short-term capital gain/loss p23250 + # Sch D: net long-term capital gain/loss # ---- (E) AMT non-AGI add-backs (Form 6251 items not in AGI) ---- cmbtp + # other AMT taxable income items from Form 6251 # ---- (F) Employer-side FICA share (compensation not in e00200) ---- 0.5 * ptax_was + # employer share of FICA taxes on wages/salaries # ---- (G) Consumption value of transfer benefits ---- benefit_value_total # see BenefitPrograms for the per-program # cash-vs-in-kind valuation rule producing benefit_value_total ) return expanded_income @iterate_jit(nopython=True) def AfterTaxIncome(combined, expanded_income, aftertax_income): """ Computes the records-bound `aftertax_income` = `expanded_income` − `combined`. This is a model-internal distributional-analysis aggregate with no IRS form/schedule counterpart: it is not part of the tax-liability path. The output can be negative (no floor at zero) — e.g., when `combined` exceeds `expanded_income` for a unit with large business/capital losses. Note that any refundable credits have already been netted into `iitax` (and therefore into `combined`) upstream. Calling order: `Calculator.calc_all` invokes `AfterTaxIncome` last, immediately after `ExpandIncome` (which produces `expanded_income`) and after `LumpSumTax` (which finalizes `combined` = `iitax` + `payrolltax` + `lumpsum_tax`). Downstream consumers are distributional tables (`Calculator.decile_graph`, `mtr_graph`) and the consumer-equivalent welfare calculation `Calculator.ce_aftertax_income`. Parameters ---------- combined: float Sum of `iitax` + `payrolltax` + `lumpsum_tax` for the filing unit (finalized in `LumpSumTax`). expanded_income: float Broad pre-tax income measure including `benefit_value_total` (from `ExpandIncome`); see that function's docstring for the AGI-vs-expanded-income deltas. aftertax_income: float Records-bound output, computed below. Returns ------- aftertax_income: float `expanded_income` − `combined`; can be negative. """ aftertax_income = expanded_income - combined return aftertax_income