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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/credit-recompute-branches-keep-inputs.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Keep plain inputs such as EITC take-up when New York and Alabama recompute federal credits under pinned rules, so a 2021 New York filer who does not take up the federal EITC no longer gets the New York EITC.
Original file line number Diff line number Diff line change
Expand Up @@ -286,3 +286,56 @@
dependent_care_assistance_exclusion: 9_000
cdcc: 3_500
al_federal_income_tax_deduction: 4_390

- name: 2021 head of household, one child - Part II recomputes the EITC only for a taker
period: 2021
absolute_error_margin: 0.01
input:
people:
parent:
age: 35
employment_income: 41_000
child:
age: 3
tax_units:
tax_unit:
members: [parent, child]
takes_up_eitc: true
households:
household:
members: [parent, child]
state_code: AL
output:
# Federal tax: $41,000 - $18,800 standard deduction = $22,200 taxable;
# 10% x 14,200 + 12% x 8,000 = $2,380.
income_tax_before_refundable_credits: 2_380
# Part I: 2,380 - 3,600 refundable CTC - 185.50 EITC < 0, so 0.
# Part II (2020 IRC): 2,380 - 2,000 nonrefundable 2020 CTC = 380 (line 6);
# 2020 EITC 3,584 - 15.98% x (41,000 - 19,330) = 121.13; the 2020 CTC is
# all nonrefundable here, so 380 - 121.13 = 258.87.
al_federal_income_tax_deduction: 258.87

- name: 2021 head of household, one child, no EITC take-up - Part II recomputes no EITC
period: 2021
absolute_error_margin: 0.01
input:
people:
parent:
age: 35
employment_income: 41_000
child:
age: 3
tax_units:
tax_unit:
members: [parent, child]
takes_up_eitc: false
households:
household:
members: [parent, child]
state_code: AL
output:
eitc: 0
# Part I: 2,380 - 3,600 refundable CTC < 0, so 0. Part II: line 6 is
# 380 and the 2020-rule EITC is zero for a filer who does not take it
# up, so 380.
al_federal_income_tax_deduction: 380
Original file line number Diff line number Diff line change
Expand Up @@ -405,3 +405,58 @@
output:
ny_ctc_pre_2024_eligible: false
ny_ctc_pre_2024: 0

# The pre-TCJA branch copies the parent's cache, so this case lists the
# current-law federal credit first: the branch must recompute it, not read it.
- name: 2023 pre-TCJA credit recomputes the federal CTC after the current-law CTC
period: 2023
absolute_error_margin: 0.01
input:
people:
parent:
age: 35
employment_income: 30_000
child:
age: 3
tax_units:
tax_unit:
members: [parent, child]
households:
household:
members: [parent, child]
state_code: NY
output:
# Current law: $2,000 per child.
ctc: 2_000
# Pre-TCJA: $1,000 per child, under the $75,000 head-of-household
# phase-out threshold. Federal tax is (30,000 - 20,800) x 10% = 920, so
# 920 is nonrefundable and the other 80 refundable (15% x (30,000 -
# 3,000) = 4,050 exceeds it): 1,000. 33% x 1,000 = 330, above the $100
# minimum. The 2023 minimum age is zero, so the child aged 3 counts.
ny_ctc_pre_2024: 330

# A supplied ctc_qualifying_child is the current-law determination; the
# pre-TCJA branch recomputes it under the pinned rules.
- name: 2023 pre-TCJA eligibility recomputes a supplied federal qualifying-child flag
period: 2023
absolute_error_margin: 0.01
input:
people:
parent:
age: 35
employment_income: 30_000
child:
age: 6
ctc_qualifying_child: false
tax_units:
tax_unit:
members: [parent, child]
households:
household:
members: [parent, child]
state_code: NY
output:
# The child qualifies under the pre-TCJA rules, so the credit is the
# same 33% x 1,000 = 330 as above.
ny_ctc_pre_2024_eligible: true
ny_ctc_pre_2024: 330
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,107 @@
adjusted_gross_income: 41_500
ny_recomputed_federal_agi: 41_800
ny_eitc: 0

- name: 2021 NY EITC for a parent who takes up the federal EITC
period: 2021
absolute_error_margin: 0.01
input:
people:
parent:
age: 35
employment_income: 10_000
child:
age: 3
tax_units:
tax_unit:
members: [parent, child]
takes_up_eitc: true
households:
household:
members: [parent, child]
state_code: NY
output:
# Tax Law 606(d) and the 2021 IT-215 instructions: 30% of the federal
# credit computed without ARPA. One child: 34% phase-in x $10,000 =
# $3,400, under the $3,618 maximum. 30% x 3,400 = $1,020, less the
# household credit capped at NY tax before credits, which is zero.
eitc: 3_400
ny_income_tax_before_credits: 0
ny_eitc: 1_020

- name: 2021 NY EITC is zero for a parent who does not take up the federal EITC
period: 2021
input:
people:
parent:
age: 35
employment_income: 10_000
child:
age: 3
tax_units:
tax_unit:
members: [parent, child]
takes_up_eitc: false
households:
household:
members: [parent, child]
state_code: NY
output:
# The pre-ARPA recomputation reads the same take-up input, so the
# recomputed federal credit, and 30% of it, is zero.
eitc: 0
ny_eitc: 0

# The pre-ARPA branch copies the parent's cache, so this case lists the
# current-law federal credit first: the branch must recompute it, not read it.
- name: 2021 NY EITC recomputes the federal credit without ARPA after the ARPA credit
period: 2021
absolute_error_margin: 0.01
input:
people:
filer:
age: 30
employment_income: 9_000
households:
household:
members: [filer]
state_code: NY
output:
# ARPA childless credit (gov.irs.credits.eitc.phase_in_rate and .max,
# 2021, no children): 15.3% x 9,000 = 1,377, under the 1,502 maximum,
# and below the 11,610 ARPA phase-out start.
eitc: 1_377
# The model's NY tax before credits and household credit for this filer.
ny_income_tax_before_credits: 40
ny_household_credit: 40
# Pre-ARPA (Rev. Proc. 2020-45): gov.states.ny.tax.income.credits.eitc
# .pre_arpa childless_max 543 and childless_phase_out_start 8,880, and
# the 2020 childless phase-out rate of 7.65% (eitc.phase_out.rate):
# 543 - 7.65% x (9,000 - 8,880) = 533.82; 30% = 160.146, less the
# household credit of 40 (capped at NY tax of 40) = 120.15.
ny_eitc: 120.15

# A supplied eitc is the current-law (ARPA) amount; the pre-ARPA branch
# recomputes the federal credit rather than reading it.
- name: 2021 NY EITC recomputes the federal credit when the ARPA credit is supplied
period: 2021
absolute_error_margin: 0.01
input:
people:
parent:
age: 35
employment_income: 10_000
child:
age: 3
tax_units:
tax_unit:
members: [parent, child]
eitc: 500
households:
household:
members: [parent, child]
state_code: NY
output:
# Pre-ARPA one-child credit: 34% x 10,000 = 3,400, under the 3,618
# maximum; 30% = 1,020, less a household credit capped at NY tax of 0.
ny_eitc: 1_020
23 changes: 21 additions & 2 deletions policyengine_us/tools/period_branch.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@

``get_branch_for_period`` is the same with no inputs, for branches that
change the tax-benefit system rather than inputs: the caller swaps the system
and deletes the variables it recalculates.
and drops the values of the formula variables it recalculates with
``drop_recomputed_values``, which keeps plain inputs.
"""

from typing import Dict, Iterable, Optional, Set, Tuple, Union
Expand Down Expand Up @@ -176,6 +177,23 @@ def drop_inherited_values(
branch._fast_cache = {}


def drop_recomputed_values(branch: Simulation, name_parts: Iterable[str]) -> None:
"""Drop the values of formula variables a pinned-rules branch recalculates.

A branch that swaps in a pinned tax-benefit system recalculates every
formula variable whose name contains one of ``name_parts``, including one
the caller supplied: a supplied value is the current-law amount, which the
pinned rules replace. Plain inputs (no formula), such as ``takes_up_eitc``,
are facts the pinned rules don't change, so they are kept.
"""
name_parts = tuple(name_parts)
for name, variable in branch.tax_benefit_system.variables.items():
if any(part in name for part in name_parts) and (
variable.formulas or variable.adds or variable.subtracts
):
branch.delete_arrays(name)


def get_override_branch(
simulation: Simulation,
name: str,
Expand Down Expand Up @@ -239,7 +257,8 @@ def get_branch_for_period(
"""Return ``simulation``'s branch ``name`` for ``period``, created per period.

For branches that change the tax-benefit system rather than inputs: the
caller swaps the system and deletes the variables it recalculates. Within
caller swaps the system and drops the formula variables it recalculates
with ``drop_recomputed_values``. Within
a period the branch is shared; a branch left from another period is
dropped and created again from the parent as it stands now.
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from policyengine_us.model_api import *
from policyengine_us.tools.period_branch import drop_recomputed_values
from policyengine_us.tools.pinned_tbs import (
CREDIT_DEPENDENT_VARIABLES,
IRC_2020_RECOMPUTED_VARIABLES,
Expand Down Expand Up @@ -60,18 +61,19 @@ def formula(tax_unit, period, parameters):
simulation = tax_unit.simulation
branch = get_branch_for_period(simulation, "al_2020_irc", period)
branch.tax_benefit_system = get_2020_irc_tbs(simulation.tax_benefit_system)
for variable in branch.tax_benefit_system.variables:
if any(
key in variable
for key in (
"ctc",
"cdcc",
"eitc",
*CREDIT_DEPENDENT_VARIABLES,
*IRC_2020_RECOMPUTED_VARIABLES,
)
):
branch.delete_arrays(variable)
# Recalculate the credits' formula variables under the 2020 IRC, a
# supplied current-law value included; keep plain inputs such as
# takes_up_eitc.
drop_recomputed_values(
branch,
[
"ctc",
"cdcc",
"eitc",
*CREDIT_DEPENDENT_VARIABLES,
*IRC_2020_RECOMPUTED_VARIABLES,
],
)
recomputed_ctc = branch.tax_unit("ctc", period)
recomputed_refundable_ctc = branch.tax_unit("refundable_ctc", period)
# Under the 2020 IRC the CDCC is non-refundable (limited by tax), so the
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from policyengine_us.model_api import *
from policyengine_us.tools.period_branch import drop_recomputed_values
from policyengine_us.tools.pinned_tbs import (
CREDIT_DEPENDENT_VARIABLES,
get_pre_tcja_ctc_tbs,
Expand Down Expand Up @@ -53,12 +54,9 @@ def formula(tax_unit, period, parameters):
simulation.tax_benefit_system
)
branch_parameters = pre_tcja_ctc.tax_benefit_system.parameters
# Delete all arrays from pre-TCJA CTC branch.
for variable in pre_tcja_ctc.tax_benefit_system.variables:
if "ctc" in variable or any(
name in variable for name in CREDIT_DEPENDENT_VARIABLES
):
pre_tcja_ctc.delete_arrays(variable)
# Recalculate every CTC formula variable under the pinned rules, a
# supplied current-law value included.
drop_recomputed_values(pre_tcja_ctc, ["ctc", *CREDIT_DEPENDENT_VARIABLES])
# Calculate pre-TCJA CTC. IT-213 Worksheet A, line 1 counts every
# child on form line 4, including children under the NY minimum
# age; lines 9-12 below prorate the result.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from policyengine_us.model_api import *
from policyengine_us.tools.period_branch import drop_recomputed_values
from policyengine_us.tools.pinned_tbs import (
CREDIT_DEPENDENT_VARIABLES,
get_pre_tcja_ctc_tbs,
Expand Down Expand Up @@ -42,12 +43,9 @@ def formula(tax_unit, period, parameters):
pre_tcja_ctc.tax_benefit_system = get_pre_tcja_ctc_tbs(
simulation.tax_benefit_system
)
# Delete all arrays from pre-TCJA CTC branch.
for variable in pre_tcja_ctc.tax_benefit_system.variables:
if "ctc" in variable or any(
name in variable for name in CREDIT_DEPENDENT_VARIABLES
):
pre_tcja_ctc.delete_arrays(variable)
# Recalculate every CTC formula variable under the pinned rules, a
# supplied current-law value included.
drop_recomputed_values(pre_tcja_ctc, ["ctc", *CREDIT_DEPENDENT_VARIABLES])
qualifies_for_federal_ctc = pre_tcja_ctc.person(
"ctc_qualifying_child", period
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from policyengine_us.model_api import *
from policyengine_us.tools.period_branch import drop_recomputed_values
from policyengine_us.tools.pinned_tbs import get_pre_arpa_eitc_tbs
from policyengine_us.variables.gov.states.ny.tax.income.adjusted_gross_income.ny_recomputed_federal_agi import (
get_ny_recomputed_branch,
Expand Down Expand Up @@ -33,9 +34,10 @@ def formula(tax_unit, period, parameters):
branch.tax_benefit_system = get_pre_arpa_eitc_tbs(
simulation.tax_benefit_system
)
for variable in branch.tax_benefit_system.variables:
if "eitc" in variable:
branch.delete_arrays(variable)
# Recalculate every EITC formula variable under the pinned rules,
# a supplied current-law eitc included; keep plain inputs such as
# takes_up_eitc.
drop_recomputed_values(branch, ["eitc"])
federal_eitc = branch.tax_unit("eitc", period)
else:
# IT-215: the federal credit recomputed with recomputed federal
Expand Down
Loading