diff --git a/changelog.d/repeal-state-dep-exemptions-claimable-filer.fixed.md b/changelog.d/repeal-state-dep-exemptions-claimable-filer.fixed.md new file mode 100644 index 00000000000..a4d0029f3aa --- /dev/null +++ b/changelog.d/repeal-state-dep-exemptions-claimable-filer.fixed.md @@ -0,0 +1 @@ +Keep each state's rule for a filer who can be claimed as a dependent on another return in the repeal of state dependent exemptions: Delaware, Hawaii, Nebraska, Rhode Island, Virginia, West Virginia and Wisconsin no longer give such a filer a personal exemption, Vermont's count no longer depends on which spouse is labelled head, and Michigan no longer keeps that return's dependent exemptions when the would-be claimant meets the filing exception. diff --git a/policyengine_us/reforms/state_dependent_exemptions/README.md b/policyengine_us/reforms/state_dependent_exemptions/README.md index aade9a629c4..f5f185057a8 100644 --- a/policyengine_us/reforms/state_dependent_exemptions/README.md +++ b/policyengine_us/reforms/state_dependent_exemptions/README.md @@ -2,7 +2,7 @@ The following adjustments have been made in this reform: Alabama: Neutralize dependent exemptions California: Remove the dependent exemption structure from `ca_exemptions` Georgia: Remove the dependent exemption structure from `ga_exemptions` -Hawaii: Change the `exemptions_count` to `head_spouse_count` +Hawaii: Change the `exemptions_count` to `head_spouse_count_not_dependent_elsewhere` Illinois: Neutralize dependent exemptions Indiana: Change the `tax_unit_size` to `head_spouse_count` Iowa: Remove the dependent exemption structure from `ia_exemption_credit` @@ -10,30 +10,32 @@ Kansas: Remove the dependents from the exemption count Louisiana: Neutralize dependent exemptions Maryland: Change the `tax_unit_size` to `head_spouse_count` Massachusetts: Remove the dependent exemption structure from `ma_income_tax_exemption_threshold` -Michigan: Change the `tax_unit_size` to `head_spouse_count` +Michigan: Change the `tax_unit_size` to `head_spouse_count`, and remove the dependents from `mi_exemptions` on a return with a filer who can be claimed as a dependent Minnesota: Neutralize dependent exemptions Mississippi: Neutralize dependent exemptions Montana: Neutralize the dependent exemptions person -Nebraska: Change the `tax_unit_size` to `head_spouse_count` +Nebraska: Change the `exemptions_count` to `head_spouse_count_not_dependent_elsewhere` New Jersey: Neutralize dependent exemptions New Mexico: Neutralize the deduction for certain dependents New York: Neutralize dependent exemptions North Carolina: Neutralize the child deduction Ohio: Remove `is_tax_unit_dependent` from the exemption eligibility criteria Oklahoma: Remove the dependents from the total exemptions -Rhode Island: Change the `exemptions_count` to `head_spouse_count` +Rhode Island: Change the `exemptions_count` to `head_spouse_count_not_dependent_elsewhere` South Carolina: Neutralize dependent exemptions Utah: Neutralize the personal exemption amount -Vermont: Remove the dependents from the personal exemptions -Virginia: Only apply the personal exemptions to the head and spouse -West Virginia: Change the `tax_unit_size` to `head_spouse_count` -Wisconsin: Change the `tax_unit_size` to `head_spouse_count` +Vermont: Remove the dependents from the personal exemptions, counting `head_spouse_count_not_dependent_elsewhere` +Virginia: Only apply the personal exemptions to a head or spouse who cannot be claimed as a dependent +West Virginia: Change the `exemptions_count` to `head_spouse_count_not_dependent_elsewhere` +Wisconsin: Change the `exemptions_count` to `head_spouse_count_not_dependent_elsewhere` Arizona: Neutralize dependent exemptions Arkansas: Neutralize dependent exemptions -Delaware: Change the `tax_unit_size` to `head_spouse_count` +Delaware: Change the `exemptions_count` to `head_spouse_count_not_dependent_elsewhere` Idaho: Neutralize dependent exemptions CTC Iowa: Change the `tax_unit_size` to `head_spouse_count` Kentucky: Change the `tax_unit_size` in family_size to `head_spouse_count` Maine: Neutralize dependent exemptions -Nebraska: Change the `tax_unit_size` to `head_spouse_count` +Nebraska: Change the `exemptions_count` to `head_spouse_count_not_dependent_elsewhere` Oklahoma: Remove CTC portion and update return + +Where a state's baseline gives no personal exemption (in Oklahoma, no regular exemption) to a filer who can be claimed as a dependent on another return, the reform keeps that rule: California, Delaware, Hawaii, Kansas before 2024, Maryland, Michigan, Nebraska, Ohio, Oklahoma, Rhode Island, Vermont, Virginia, West Virginia and Wisconsin. Where the baseline lets such a filer keep it, so does the reform: Georgia, Indiana, Iowa, Kansas from 2024, Kentucky's family size and Massachusetts. diff --git a/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py b/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py index d05820bfb6b..fe78a5f8441 100644 --- a/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py +++ b/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py @@ -13,13 +13,19 @@ class hi_regular_exemptions(Variable): defined_for = StateCode.HI def formula(tax_unit, period, parameters): - exemptions_count = tax_unit("head_spouse_count", period) + # The filers' own exemptions: none for a filer who can be claimed + # as a dependent, as in the baseline (HRS 235-54(a)). + exemptions_count = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) p = parameters(period).gov.states.hi.tax.income.exemptions - # Aged heads and spouses get an extra base exemption. + # Aged heads and spouses who cannot be claimed get an extra base + # exemption. person = tax_unit.members head_or_spouse = person("is_tax_unit_head_or_spouse", period) + claimable = person("claimable_as_dependent_on_another_return", period) aged = person("age", period) >= p.aged_threshold - aged_head_spouse_count = tax_unit.sum(aged & head_or_spouse) + aged_head_spouse_count = tax_unit.sum(aged & head_or_spouse & ~claimable) total_exemption_count_including_aged = ( exemptions_count + aged_head_spouse_count ) @@ -62,6 +68,62 @@ def formula(tax_unit, period, parameters): ) return exemptions * p.personal + class mi_exemptions(Variable): + value_type = float + entity = TaxUnit + label = "Michigan exemptions" + defined_for = StateCode.MI + unit = USD + definition_period = YEAR + reference = ( + "https://www.legislature.mi.gov/Laws/MCL?objectName=mcl-206-30", + "https://www.legislature.mi.gov/Publications/TaxpayerGuide.pdf", + ) + + def formula(tax_unit, period, parameters): + p = parameters(period).gov.states.mi.tax.income.exemptions + + # Personal Exemptions & Stillborn Exemptions + personal_exemption = tax_unit("mi_personal_exemptions", period) + + # Disabled exemptions + disabled_people = add( + tax_unit, period, ["mi_disabled_exemption_eligible_person"] + ) + disabled_exemption = disabled_people * p.disabled.amount.base + + # Disabled veteran exemptions + disabled_veterans = add( + tax_unit, period, ["is_fully_disabled_service_connected_veteran"] + ) + disabled_veteran_exemption = disabled_veterans * p.disabled.amount.veteran + + # MCL 206.30(4), as in the baseline: a filer who can be claimed as + # a dependent gets no personal exemption but subtracts $1,500, and + # the other spouse keeps their own. The baseline also allows that + # return's dependents when the would-be claimant meets the filing + # exception; the reform repeals those too. + filers = add(tax_unit, period, ["is_tax_unit_head_or_spouse"]) + independent_filers = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) + dependent_filers = filers - independent_filers + stillborn = tax_unit("tax_unit_stillborn_children", period) + independent_filer_exemptions = ( + independent_filers + where(independent_filers > 0, stillborn, 0) + ) * p.personal + dependent_filer_exemptions = dependent_filers * p.dependent_on_other_return + + return ( + where( + dependent_filers > 0, + independent_filer_exemptions + dependent_filer_exemptions, + personal_exemption, + ) + + disabled_exemption + + disabled_veteran_exemption + ) + class ne_exemptions(Variable): value_type = float entity = TaxUnit @@ -76,7 +138,13 @@ class ne_exemptions(Variable): def formula(tax_unit, period, parameters): p = parameters(period).gov.states.ne.tax.income.exemptions - return tax_unit("head_spouse_count", period) * p.amount + # The filers' own credits: none for a filer who can be claimed as + # a dependent (Neb. Rev. Stat. 77-2716.01), as in the baseline's + # federal count. + own_exemptions = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) + return own_exemptions * p.amount class oh_personal_exemptions_eligible_person(Variable): value_type = bool @@ -152,7 +220,11 @@ class ri_exemptions(Variable): def formula(tax_unit, period, parameters): p = parameters(period).gov.states.ri.tax.income.exemption - exemptions_count = tax_unit("head_spouse_count", period) + # The filers' own exemptions: none for a filer who can be claimed + # as a dependent, as in the baseline's federal count. + exemptions_count = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) exemption_amount = exemptions_count * p.amount @@ -178,15 +250,12 @@ class vt_personal_exemptions(Variable): def formula(tax_unit, period, parameters): p = parameters(period).gov.states.vt.tax.income.exemption - is_joint = tax_unit("tax_unit_is_joint", period) - elsewhere_head = tax_unit("head_is_dependent_elsewhere", period) - elsewhere_spouse = tax_unit("spouse_is_dependent_elsewhere", period) - eligible_head = (~elsewhere_head).astype(int) - eligible_spouse = (~elsewhere_spouse).astype(int) - eligible_count = eligible_head + (eligible_spouse * is_joint) - # add number of other dependents claimed on federal Form 1040 (line 5c) - total_exemption_count = eligible_count - return total_exemption_count * p.personal + # The filers' own exemptions: one for each head or spouse who + # cannot be claimed as a dependent, whichever spouse is head. + eligible_count = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) + return eligible_count * p.personal class va_personal_exemption_person(Variable): value_type = float @@ -200,9 +269,12 @@ class va_personal_exemption_person(Variable): ) def formula(person, period, parameters): + # The filers' own exemptions: none for a filer who can be claimed + # as a dependent, as in the baseline (IRC 151(d)(2)). head_or_spouse = person("is_tax_unit_head_or_spouse", period) + claimable = person("claimable_as_dependent_on_another_return", period) amount = parameters(period).gov.states.va.tax.income.exemptions.personal - return amount * head_or_spouse + return amount * (head_or_spouse & ~claimable) class wv_personal_exemption(Variable): value_type = float @@ -211,13 +283,20 @@ class wv_personal_exemption(Variable): defined_for = StateCode.WV unit = USD definition_period = YEAR - reference = "https://code.wvlegislature.gov/11-21/" + reference = "https://code.wvlegislature.gov/11-21-16/" def formula(tax_unit, period, parameters): p = parameters(period).gov.states.wv.tax.income.exemptions - tax_unit_size = tax_unit("head_spouse_count", period) + # The filers' own exemptions: none for a filer who can be claimed + # as a dependent, and $500 once per return when no filer has one + # (11-21-16(d)), as in the baseline. + own_exemptions = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) return where( - tax_unit_size == 0, p.base_personal, p.personal * tax_unit_size + own_exemptions == 0, + p.base_personal, + p.personal * own_exemptions, ) class ca_exemptions(Variable): @@ -373,7 +452,7 @@ def formula(tax_unit, period, parameters): ) return exempt_status.base[filing_status] + personal_exemptions_added - # Using head and spouse count instead of exemptions count + # Counting the filers' own exemptions instead of all exemptions class wi_base_exemption(Variable): value_type = float entity = TaxUnit @@ -390,9 +469,13 @@ class wi_base_exemption(Variable): defined_for = StateCode.WI def formula(tax_unit, period, parameters): - # compute base exemption amount + # compute base exemption amount: none for a filer who can be + # claimed as a dependent, as in the baseline's federal count p = parameters(period).gov.states.wi.tax.income - return tax_unit("head_spouse_count", period) * p.exemption.base + own_exemptions = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) + return own_exemptions * p.exemption.base class de_personal_credit(Variable): value_type = float @@ -405,8 +488,12 @@ class de_personal_credit(Variable): def formula(tax_unit, period, parameters): p = parameters(period).gov.states.de.tax.income.credits - head_spouse_count = tax_unit("head_spouse_count", period) - return p.personal_credits.personal * head_spouse_count + # The filers' own credits: none for a filer who can be claimed as + # a dependent, as in the baseline's federal count. + own_exemptions = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) + return p.personal_credits.personal * own_exemptions class ky_family_size_tax_credit_threshold(Variable): value_type = float @@ -480,6 +567,7 @@ def apply(self): self.update_variable(hi_regular_exemptions) self.update_variable(md_total_personal_exemptions) self.update_variable(mi_personal_exemptions) + self.update_variable(mi_exemptions) self.update_variable(ne_exemptions) self.update_variable(oh_personal_exemptions_eligible_person) self.update_variable(ok_count_exemptions) diff --git a/policyengine_us/tests/core/test_repeal_state_dependent_exemptions_claimable_invariants.py b/policyengine_us/tests/core/test_repeal_state_dependent_exemptions_claimable_invariants.py new file mode 100644 index 00000000000..6b09cfb4453 --- /dev/null +++ b/policyengine_us/tests/core/test_repeal_state_dependent_exemptions_claimable_invariants.py @@ -0,0 +1,400 @@ +"""Repealing state dependent exemptions keeps each state's rule for a filer +who can be claimed as a dependent on another return. + +The contributed reform `repeal_state_dependent_exemptions` overrides 21 state +formulas so that they count only the filers' own exemptions. Where a state's +baseline gives no personal exemption to a filer whom another taxpayer can +claim, the override keeps that rule; where the baseline lets such a filer keep +it (Georgia, Indiana, Iowa, Kansas from 2024, Kentucky and Massachusetts), so +does the override. + +The YAML tests in tests/policy/contrib/state_dependent_exemptions check fixed +households. This module checks, across couples and single filers drawn by +Hypothesis and a seeded population in the 19 states, with either, both or +neither filer claimable (and, if so, claimed or not, with or without the +would-be claimant's filing exception), and in two years, the properties a +YAML case cannot express because it runs only one system: + +1. Swap invariance: each output is the same under either head/spouse + labelling of a couple. +2. Monotonicity: under the reform, marking another filer as claimable never + raises an exemption. +3. Bound: the reform never raises an output above the baseline. +4. No dependents, no change: on a return without dependents the reform + equals the baseline. +5. A claimable filer, no change: a return with a filer who can be claimed + has no dependents (IRC 152(b)(1)) unless the would-be claimant meets the + filing exception, so on such a return the reform equals the baseline. + +Each property runs one baseline and one reform simulation over a vectorized +batch. The reform is built once and cached by policyengine-core. +""" + +import numpy as np +import pytest +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from policyengine_us import Simulation +from policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions import ( + repeal_state_dependent_exemptions, +) +from policyengine_us.tests.core.test_dependent_elsewhere_head_spouse_swap_invariance import ( + TOLERANCE, + adults, + dependents, +) + +STATES = [ + "CA", + "DE", + "GA", + "HI", + "IA", + "IN", + "KS", + "KY", + "MA", + "MD", + "MI", + "NE", + "OH", + "OK", + "RI", + "VA", + "VT", + "WI", + "WV", +] +# Kansas switches from per-person to filing-status exemptions in 2024. +YEARS = [2023, 2026] +# The reform's outputs, each compared with the baseline variable named here: +# the reform overrides Delaware's applied personal credit with its amount +# before the tax-liability limit, which ordered_capped_state_non_refundable_ +# credits applies afterwards, so it is compared with the baseline's potential. +TAX_UNIT_OUTPUTS = { + "hi_regular_exemptions": "hi_regular_exemptions", + "md_total_personal_exemptions": "md_total_personal_exemptions", + "mi_personal_exemptions": "mi_personal_exemptions", + "mi_exemptions": "mi_exemptions", + "ne_exemptions": "ne_exemptions", + "oh_personal_exemptions": "oh_personal_exemptions", + "ok_count_exemptions": "ok_count_exemptions", + "ri_exemptions": "ri_exemptions", + "vt_personal_exemptions": "vt_personal_exemptions", + "va_personal_exemption": "va_personal_exemption", + "wv_personal_exemption": "wv_personal_exemption", + "ca_exemptions": "ca_exemptions", + "ga_exemptions": "ga_exemptions", + "in_base_exemptions": "in_base_exemptions", + "ia_exemption_credit": "ia_exemption_credit", + "ks_exemptions": "ks_exemptions", + "ma_income_tax_exemption_threshold": "ma_income_tax_exemption_threshold", + "wi_base_exemption": "wi_base_exemption", + "de_personal_credit": "de_personal_credit_potential", + "ky_family_size_tax_credit_threshold": "ky_family_size_tax_credit_threshold", + "ok_child_care_child_tax_credit": "ok_child_care_child_tax_credit", +} +PERSON_OUTPUTS = { + "oh_personal_exemptions_eligible_person": "oh_personal_exemptions_eligible_person", + "va_personal_exemption_person": "va_personal_exemption_person", +} +# Oklahoma's credit is a share of the federal child care credit allowed after +# the federal tax-liability limit, which can rise when a claimable filer's +# smaller standard deduction raises federal tax; it is not an exemption. +NOT_MONOTONE = {"ok_child_care_child_tax_credit"} +# Outputs whose baseline still counts the dependents on a return with a +# claimable filer, so property 5 does not apply: Kentucky's family size is +# the tax unit size, and Michigan's baseline does not read +# mi_personal_exemptions on such a return (mi_exemptions is checked instead). +COUNTS_DEPENDENTS_OF_CLAIMABLE_FILER = { + "ky_family_size_tax_credit_threshold", + "mi_personal_exemptions", +} + + +@st.composite +def claim_facts(draw): + # Claimed implies claimable; the exception matters only when claimable. + claimable = draw(st.booleans()) + return { + "claimable": claimable, + "claimed": claimable and draw(st.booleans()), + "exception": draw(st.booleans()), + } + + +@st.composite +def units(draw): + n_adults = draw(st.sampled_from([1, 2])) + return { + "state": draw(st.sampled_from(STATES)), + "adults": [draw(adults()) for _ in range(n_adults)], + "dependents": draw(st.lists(dependents(), max_size=2)), + "claims": [draw(claim_facts()) for _ in range(n_adults)], + } + + +SEED = 20261010 + + +def _seeded_units(): + rng = np.random.default_rng(SEED) + out = [] + for state in STATES: + for n_adults in (1, 2, 2): + claimable = rng.permutation([True] + [False] * (n_adults - 1)) + out.append( + { + "state": state, + "adults": [ + { + "age": int(rng.choice([19, 20, 40, 70])), + "employment_income": float( + rng.choice([0, 8_000, 40_000, 120_000]) + ), + "is_blind": bool(rng.random() < 0.1), + } + for _ in range(n_adults) + ], + "dependents": [ + {"age": int(rng.integers(0, 18))} + for _ in range(int(rng.choice([0, 1, 2]))) + ], + "claims": [ + { + "claimable": bool(c), + "claimed": bool(c) and bool(rng.random() < 0.7), + "exception": bool(rng.random() < 0.3), + } + for c in claimable + ], + } + ) + return out + + +def _situation(units, year, variants): + """One copy of each unit per variant (first adult is head, claimable).""" + people = {} + groups = { + "tax_units": {}, + "spm_units": {}, + "families": {}, + "marital_units": {}, + "households": {}, + } + for i, unit in enumerate(units): + couple = len(unit["adults"]) == 2 + for copy, (first_is_head, claimable) in enumerate(variants(unit)): + names = [] + for slot, amounts in enumerate(unit["adults"]): + name = f"adult_{i}_{copy}_{slot}" + is_head = first_is_head if slot == 0 else not first_is_head + facts = unit["claims"][slot] + people[name] = { + **amounts, + "is_tax_unit_head": is_head, + "is_tax_unit_spouse": couple and not is_head, + "is_tax_unit_dependent": False, + "claimable_as_dependent_on_another_return": claimable[slot], + "claimed_as_dependent_on_another_return": claimable[slot] + and facts["claimed"], + "dependent_claimant_filing_exception": facts["exception"], + } + names.append(name) + groups["marital_units"][f"adults_{i}_{copy}"] = {"members": list(names)} + members = list(names) + for j, amounts in enumerate(unit["dependents"]): + child = f"dependent_{i}_{copy}_{j}" + people[child] = { + **amounts, + "is_tax_unit_head": False, + "is_tax_unit_spouse": False, + "is_tax_unit_dependent": True, + } + members.append(child) + groups["marital_units"][f"single_{i}_{copy}_{j}"] = {"members": [child]} + for group, prefix in ( + ("tax_units", "tax_unit"), + ("spm_units", "spm_unit"), + ("families", "family"), + ): + groups[group][f"{prefix}_{i}_{copy}"] = {"members": members} + groups["households"][f"household_{i}_{copy}"] = { + "members": members, + "state_code": {year: unit["state"]}, + } + people = { + name: {k: {year: v} for k, v in values.items()} + for name, values in people.items() + } + return {"people": people, **groups} + + +def _claimable(unit): + return tuple(facts["claimable"] for facts in unit["claims"]) + + +def _as_drawn(unit): + return [(True, _claimable(unit))] + + +def _swap_variants(unit): + # Singles have no labels to exchange, so both copies are the same. + return [(True, _claimable(unit)), (len(unit["adults"]) == 1, _claimable(unit))] + + +def _monotone_variants(unit): + # Nobody, either adult, then every adult claimable; labels fixed. + if len(unit["adults"]) == 1: + return [(True, (False,)), (True, (True,)), (True, (True,)), (True, (True,))] + return [ + (True, (False, False)), + (True, (True, False)), + (True, (False, True)), + (True, (True, True)), + ] + + +def _simulations(units, year, variants): + situation = _situation(units, year, variants) + baseline = Simulation(situation=situation) + # The module-level reform applies whatever the in_effect parameter says. + reformed = Simulation(situation=situation, reform=repeal_state_dependent_exemptions) + return baseline, reformed + + +def _values(sim, name, year): + return np.asarray(sim.calculate(name, year), dtype=float) + + +def _person_index(units, variants): + """Copy number and unit number of each person, in simulation order.""" + copies, owners = [], [] + for i, unit in enumerate(units): + for copy, _ in enumerate(variants(unit)): + n = len(unit["adults"]) + len(unit["dependents"]) + copies += [copy] * n + owners += [i] * n + return np.array(copies), np.array(owners) + + +def _describe(units, rows, a, b): + return ", ".join( + f"{units[i]['state']} claims={units[i]['claims']} " + f"dependents={len(units[i]['dependents'])}: {a[k]:.2f} vs {b[k]:.2f}" + for k, i in enumerate(rows[:10]) + ) + + +def check_swap(units, year): + """1. Each output is the same under either head/spouse labelling.""" + _, reformed = _simulations(units, year, _swap_variants) + copies, _ = _person_index(units, _swap_variants) + for name in TAX_UNIT_OUTPUTS: + values = _values(reformed, name, year) + drawn, swapped = values[0::2], values[1::2] + differs = np.flatnonzero(~(np.abs(swapped - drawn) <= TOLERANCE)) + assert not differs.size, ( + f"{name} in {year} changes when the head and spouse labels are " + "exchanged under the reform: " + + _describe(units, differs, drawn[differs], swapped[differs]) + ) + for name in PERSON_OUTPUTS: + values = _values(reformed, name, year) + drawn, swapped = values[copies == 0], values[copies == 1] + assert np.array_equal(drawn, swapped), ( + f"{name} in {year} changes for some person when the head and " + "spouse labels are exchanged under the reform" + ) + + +def check_monotone(units, year): + """2. Marking another filer as claimable never raises an exemption.""" + _, reformed = _simulations(units, year, _monotone_variants) + for name in TAX_UNIT_OUTPUTS: + if name in NOT_MONOTONE: + continue + values = _values(reformed, name, year).reshape(-1, 4) + nobody, first, second, both = values.T + for more, fewer, label in ( + (first, nobody, "first adult"), + (second, nobody, "second adult"), + (both, first, "every adult after the first"), + (both, second, "every adult after the second"), + ): + rises = np.flatnonzero(more > fewer + TOLERANCE) + assert not rises.size, ( + f"{name} in {year} rises under the reform when the {label} " + "can be claimed elsewhere: " + + _describe(units, rises, fewer[rises], more[rises]) + ) + + +def check_against_baseline(units, year): + """3-5. The reform against the baseline, return by return.""" + baseline, reformed = _simulations(units, year, _as_drawn) + no_dependents = np.array([not u["dependents"] for u in units]) + # The baseline's own flag: a filer can be claimed and the would-be + # claimant does not meet the filing exception. + claimable_filer = ( + _values( + baseline, + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", + year, + ) + > 0 + ) + _, owners = _person_index(units, _as_drawn) + checked = {3: 0, 4: 0, 5: 0} + for outputs, mask_of in ( + (TAX_UNIT_OUTPUTS, lambda m: m), + (PERSON_OUTPUTS, lambda m: m[owners]), + ): + for name, baseline_name in outputs.items(): + reform = _values(reformed, name, year) + base = _values(baseline, baseline_name, year) + rises = np.flatnonzero(reform > base + TOLERANCE) + assert not rises.size, ( + f"{name} in {year} is higher under the reform than " + f"{baseline_name} in the baseline: {reform[rises][:5]} vs " + f"{base[rises][:5]}" + ) + checked[3] += reform.size + same = mask_of(no_dependents) + checked[4] += int(same.sum()) + if name not in COUNTS_DEPENDENTS_OF_CLAIMABLE_FILER: + with_rule = mask_of(claimable_filer) + checked[5] += int((with_rule & ~same).sum()) + same = same | with_rule + differs = np.flatnonzero(same & ~(np.abs(reform - base) <= TOLERANCE)) + assert not differs.size, ( + f"{name} in {year} differs from {baseline_name} in the " + "baseline on a return the repeal should leave alone: " + f"{reform[differs][:5]} vs {base[differs][:5]}" + ) + return checked + + +@settings( + max_examples=2, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) +@given(st.sampled_from(YEARS), st.lists(units(), min_size=10, max_size=20)) +def test_repeal_keeps_claimable_filer_rules(year, drawn): + check_swap(drawn, year) + check_against_baseline(drawn, year) + + +@pytest.mark.parametrize("year", YEARS) +def test_repeal_keeps_claimable_filer_rules_seeded_population(year): + seeded = _seeded_units() + check_swap(seeded, year) + check_monotone(seeded, year) + checked = check_against_baseline(seeded, year) + # The seeded population exercises every property: returns without + # dependents, and returns with dependents and a claimable filer. + assert all(checked.values()), checked diff --git a/policyengine_us/tests/policy/contrib/state_dependent_exemptions/repeal_state_dependent_exemptions_claimable_filer.yaml b/policyengine_us/tests/policy/contrib/state_dependent_exemptions/repeal_state_dependent_exemptions_claimable_filer.yaml new file mode 100644 index 00000000000..b1f5372d4c1 --- /dev/null +++ b/policyengine_us/tests/policy/contrib/state_dependent_exemptions/repeal_state_dependent_exemptions_claimable_filer.yaml @@ -0,0 +1,1361 @@ +- name: Hawaii - a single filer who can be claimed elsewhere has no personal exemption under the reform + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + filer: + age: 19 + employment_income: 8_000 + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: HI + output: + # As in the baseline, a filer who can be claimed has no exemption. + hi_regular_exemptions: 0 + +- name: Hawaii - joint return whose head can be claimed elsewhere keeps only the spouse's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: HI + output: + # One exemption for the spouse who cannot be claimed; HRS 235-54(a); $1,144 each. + # The reform drops the child's exemption. + hi_regular_exemptions: 1_144 + +- name: Hawaii - joint return whose spouse can be claimed elsewhere keeps only the head's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: HI + output: + # The same couple with the head and spouse labels exchanged. + hi_regular_exemptions: 1_144 + +- name: Hawaii - joint return where neither spouse can be claimed keeps both personal exemptions + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: HI + output: + # 2 x 1,144; the reform drops the child's exemption. + hi_regular_exemptions: 2_288 + +- name: Rhode Island - a single filer who can be claimed elsewhere has no personal exemption under the reform + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + filer: + age: 19 + employment_income: 8_000 + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: RI + output: + # As in the baseline, a filer who can be claimed has no exemption. + ri_exemptions: 0 + +- name: Rhode Island - joint return whose head can be claimed elsewhere keeps only the spouse's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: RI + output: + # One exemption for the spouse who cannot be claimed; $5,100 each in 2025. + # The reform drops the child's exemption. + ri_exemptions: 5_100 + +- name: Rhode Island - joint return whose spouse can be claimed elsewhere keeps only the head's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: RI + output: + # The same couple with the head and spouse labels exchanged. + ri_exemptions: 5_100 + +- name: Rhode Island - joint return where neither spouse can be claimed keeps both personal exemptions + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: RI + output: + # 2 x 5,100; the reform drops the child's exemption. + ri_exemptions: 10_200 + +- name: Vermont - a single filer who can be claimed elsewhere has no personal exemption under the reform + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + filer: + age: 19 + employment_income: 8_000 + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: VT + output: + # As in the baseline, a filer who can be claimed has no exemption. + vt_personal_exemptions: 0 + +- name: Vermont - joint return whose head can be claimed elsewhere keeps only the spouse's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: VT + output: + # One exemption for the spouse who cannot be claimed; $5,300 each in 2025. + # The reform drops the child's exemption. + vt_personal_exemptions: 5_300 + +- name: Vermont - joint return whose spouse can be claimed elsewhere keeps only the head's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: VT + output: + # The same couple with the head and spouse labels exchanged. + vt_personal_exemptions: 5_300 + +- name: Vermont - joint return where neither spouse can be claimed keeps both personal exemptions + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: VT + output: + # 2 x 5,300; the reform drops the child's exemption. + vt_personal_exemptions: 10_600 + +- name: Virginia - a single filer who can be claimed elsewhere has no personal exemption under the reform + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + filer: + age: 19 + employment_income: 8_000 + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: VA + output: + # As in the baseline, a filer who can be claimed has no exemption. + va_personal_exemption: 0 + +- name: Virginia - joint return whose head can be claimed elsewhere keeps only the spouse's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: VA + output: + # One exemption for the spouse who cannot be claimed; Va. Code 58.1-322.03; $930 each. + # The reform drops the child's exemption. + va_personal_exemption: 930 + +- name: Virginia - joint return whose spouse can be claimed elsewhere keeps only the head's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: VA + output: + # The same couple with the head and spouse labels exchanged. + va_personal_exemption: 930 + +- name: Virginia - joint return where neither spouse can be claimed keeps both personal exemptions + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: VA + output: + # 2 x 930; the reform drops the child's exemption. + va_personal_exemption: 1_860 + +- name: Wisconsin - a single filer who can be claimed elsewhere has no personal exemption under the reform + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + filer: + age: 19 + employment_income: 8_000 + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: WI + output: + # As in the baseline, a filer who can be claimed has no exemption. + wi_base_exemption: 0 + +- name: Wisconsin - joint return whose head can be claimed elsewhere keeps only the spouse's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: WI + output: + # One exemption for the spouse who cannot be claimed; $700 each. + # The reform drops the child's exemption. + wi_base_exemption: 700 + +- name: Wisconsin - joint return whose spouse can be claimed elsewhere keeps only the head's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: WI + output: + # The same couple with the head and spouse labels exchanged. + wi_base_exemption: 700 + +- name: Wisconsin - joint return where neither spouse can be claimed keeps both personal exemptions + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: WI + output: + # 2 x 700; the reform drops the child's exemption. + wi_base_exemption: 1_400 + +- name: Delaware - a single filer who can be claimed elsewhere has no personal exemption under the reform + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + filer: + age: 19 + employment_income: 8_000 + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: DE + output: + # As in the baseline, a filer who can be claimed has no exemption. + de_personal_credit: 0 + +- name: Delaware - joint return whose head can be claimed elsewhere keeps only the spouse's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: DE + output: + # One exemption for the spouse who cannot be claimed; $110 each. + # The reform drops the child's exemption. + de_personal_credit: 110 + +- name: Delaware - joint return whose spouse can be claimed elsewhere keeps only the head's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: DE + output: + # The same couple with the head and spouse labels exchanged. + de_personal_credit: 110 + +- name: Delaware - joint return where neither spouse can be claimed keeps both personal exemptions + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: DE + output: + # 2 x 110; the reform drops the child's exemption. + de_personal_credit: 220 + +- name: West Virginia - a single filer who can be claimed elsewhere gets only the $500 exemption under the reform + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + filer: + age: 19 + employment_income: 8_000 + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: WV + output: + # No $2,000 exemption, but $500 once per return when the federal + # exemption count is zero (11-21-16(d)), as in the baseline. + wv_personal_exemption: 500 + +- name: West Virginia - joint return whose head can be claimed elsewhere keeps only the spouse's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: WV + output: + # One exemption for the spouse who cannot be claimed; W. Va. Code 11-21-16; $2,000 each. + # The reform drops the child's exemption. + wv_personal_exemption: 2_000 + +- name: West Virginia - joint return whose spouse can be claimed elsewhere keeps only the head's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: WV + output: + # The same couple with the head and spouse labels exchanged. + wv_personal_exemption: 2_000 + +- name: West Virginia - joint return where neither spouse can be claimed keeps both personal exemptions + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: WV + output: + # 2 x 2,000; the reform drops the child's exemption. + wv_personal_exemption: 4_000 + +- name: Nebraska - a single filer who can be claimed elsewhere has no personal exemption under the reform + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + filer: + age: 19 + employment_income: 8_000 + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: NE + output: + # As in the baseline, a filer who can be claimed has no exemption. + ne_exemptions: 0 + +- name: Nebraska - joint return whose head can be claimed elsewhere keeps only the spouse's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: NE + output: + # One exemption for the spouse who cannot be claimed; Neb. Rev. Stat. 77-2716.01; $171 each in 2025. + # The reform drops the child's exemption. + ne_exemptions: 171 + +- name: Nebraska - joint return whose spouse can be claimed elsewhere keeps only the head's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: NE + output: + # The same couple with the head and spouse labels exchanged. + ne_exemptions: 171 + +- name: Nebraska - joint return where neither spouse can be claimed keeps both personal exemptions + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: NE + output: + # 2 x 171; the reform drops the child's exemption. + ne_exemptions: 342 + +- name: Hawaii - aged joint return whose head can be claimed elsewhere gets one personal and one aged exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 70 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + person_b: + age: 70 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [person_a, person_b] + households: + household: + members: [person_a, person_b] + state_code: HI + output: + # (1 personal + 1 aged) x $1,144 for the spouse who cannot be claimed. + hi_regular_exemptions: 2_288 + +- name: Hawaii - aged joint return whose spouse can be claimed elsewhere gets one personal and one aged exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 70 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + person_b: + age: 70 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [person_a, person_b] + households: + household: + members: [person_a, person_b] + state_code: HI + output: + # (1 personal + 1 aged) x $1,144 for the spouse who cannot be claimed. + hi_regular_exemptions: 2_288 + +- name: Michigan - a single filer who can be claimed elsewhere gets the $1,500 dependent subtraction under the reform + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + filer: + age: 19 + employment_income: 8_000 + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: MI + output: + # MCL 206.30(4): no personal exemption, but $1,500, as in the baseline. + mi_exemptions: 1_500 + +- name: Michigan - joint return whose head can be claimed elsewhere keeps the other spouse's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: MI + output: + # $5,800 for the spouse who cannot be claimed + $1,500 for the one who can; + # no exemption for the child, as in the baseline. + mi_exemptions: 7_300 + +- name: Michigan - joint return whose spouse can be claimed elsewhere keeps the other spouse's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: MI + output: + # $5,800 for the spouse who cannot be claimed + $1,500 for the one who can; + # no exemption for the child, as in the baseline. + mi_exemptions: 7_300 + +- name: Michigan - joint return where neither spouse can be claimed keeps both personal exemptions + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: MI + output: + # 2 x $5,800; the reform drops the child's exemption. + mi_exemptions: 11_600 + +- name: Ohio - a single filer who can be claimed elsewhere is not eligible for the personal exemption under the reform + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + filer: + age: 19 + employment_income: 8_000 + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: OH + output: + # R.C. 5747.025: as in the baseline. + oh_personal_exemptions_eligible_person: [false] + +- name: Ohio - joint return whose head can be claimed elsewhere keeps only the spouse's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: OH + output: + # person_a (head) can be claimed; the reform drops the child. + oh_personal_exemptions_eligible_person: [false, true, false] + +- name: Ohio - joint return whose spouse can be claimed elsewhere keeps only the head's exemption + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: OH + output: + # person_a (now the spouse) can be claimed; person_b is head. + oh_personal_exemptions_eligible_person: [false, true, false] + +- name: Rhode Island - the reform drops the child's exemption that the filing exception restores + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: RI + output: + # The baseline allows the other spouse and the child (2 x $5,100); + # the reform keeps only the spouse who cannot be claimed. + ri_exemptions: 5_100 + +- name: Michigan - the reform drops the child's exemption that the filing exception restores + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: MI + output: + # $5,800 for the spouse who cannot be claimed + $1,500 for the one who can. + # The baseline adds $5,800 for the child outside mi_personal_exemptions. + mi_exemptions: 7_300 + +- name: Indiana - the reform keeps a claimable spouse's exemption, as the baseline does + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: IN + output: + # Each filer keeps the $1,000 exemption even when claimable (2 x $1,000); + # the reform drops only the child's exemption. + in_base_exemptions: 2_000 + +- name: Iowa - the reform keeps a claimable spouse's personal credit, as the baseline does + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: IA + output: + # Iowa allows the $40 personal credit even to a filer who is claimed (2 x $40); + # the reform drops only the child's credit. + ia_exemption_credit: 80 + +- name: West Virginia - joint return where both spouses can be claimed elsewhere gets the $500 exemption once + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + person_a: + age: 20 + employment_income: 8_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 8_000 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + households: + household: + members: [person_a, person_b, child] + state_code: WV + output: + # No filer has a federal exemption and the return has no dependents (IRC 152(b)(1)), + # so $500 once per return (11-21-16(d)), as in the baseline. + wv_personal_exemption: 500 + +- name: Oklahoma - a single filer who can be claimed elsewhere has no regular exemption under the reform + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + filer: + age: 19 + employment_income: 8_000 + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: OK + output: + # Form 511 item F: no regular exemption for a filer who can be claimed; + # the filer is under 65 and not blind, so no special or blind exemption. + ok_count_exemptions: 0