New York State income tax#

New York households pay progressive tax rates on income, and have access to several refundable and non-refundable tax credits. Consider a set of New York family types, each with $1,000 monthly rent and $50 monthly broadband costs, and varying in terms of marital status and number of children. Their net income—after state and federal taxes and benefits modeled by OpenFisca US—is shown in the graph below.

The cliff is due to New York’s emergency SNAP allotment, which entitles SNAP-eligible households to the maximum benefit for their household size; this also affects other benefits through categorical eligibility.

from policyengine_us import IndividualSim
import pandas as pd
import plotly.express as px

LIGHT_GRAY = "#F5F5F5"
GRAY = "#BDBDBD"
BLUE = "#5091cc"
LIGHT_BLUE = "lightblue"
DARK_BLUE = "darkblue"


def make_tax(adults, children):
    sim = IndividualSim(year=2022)
    sim.add_person(name="head", age=25, rent=12_000)
    members = ["head"]
    if adults == 2:
        sim.add_person(name="spouse", age=25)
        members += ["spouse"]
    for i in range(children):
        child = "child{}".format(i)
        sim.add_person(name=child, age=6)
        members += [child]
    sim.add_tax_unit(name="tax_unit", members=members, premium_tax_credit=0)
    # $1,000 monthly rent, $50 monthly broadband.
    sim.add_spm_unit(name="spm_unit", members=members, broadband_cost=600)
    sim.add_household(name="household", members=members, state_code="NY")
    sim.vary("employment_income", max=100_000, step=100)
    employment_income = sim.calc("employment_income")[0]
    spm_unit_net_income = sim.calc("spm_unit_net_income")[0].round()
    mtr = 1 - sim.deriv(
        "spm_unit_net_income", "employment_income", wrt_target="head"
    )
    return pd.DataFrame(
        dict(
            employment_income=employment_income,
            spm_unit_net_income=spm_unit_net_income,
            mtr=mtr,
            adults=adults,
            children=str(children),
        )
    )


# Make a table of state taxes for different numbers of adults and children.
l = []
for adults in range(1, 3):
    for children in range(0, 4):
        l.append(make_tax(adults, children))

df = pd.concat(l)

LABELS = dict(
    employment_income="Employment income",
    spm_unit_net_income="Net income",
    mtr="Marginal tax rate",
    adults="Adults",
    children="Children",
)

COLOR_MAP = {"0": GRAY, "1": LIGHT_BLUE, "2": BLUE, "3": DARK_BLUE}

fig = px.line(
    df,
    "employment_income",
    "spm_unit_net_income",
    color="children",
    animation_frame="adults",
    labels=LABELS,
    title="Net income for a New York household",
    color_discrete_map=COLOR_MAP,
)
fig.update_layout(
    xaxis_tickformat="$,",
    yaxis_tickformat="$,",
    plot_bgcolor="white",
    xaxis_gridcolor=LIGHT_GRAY,
    yaxis_gridcolor=LIGHT_GRAY,
)
fig.show()
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/individual_sim.py:261, in IndividualSim.calc(self, var, period, target, index, map_to, reform)
    260 try:
--> 261     result = self.simulation.calculate(var, period)
    262 except:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:684, in Simulation._run_formula(self, variable, population, period)
    683     for added_variable in adds_list:
--> 684         values = values + self.calculate(
    685             added_variable, period, map_to=variable.entity.key
    686         )
    687 if variable.subtracts is not None:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py:34, in spm_unit_benefits.formula(spm_unit, period, parameters)
     33     BENEFITS.append("spm_unit_capped_housing_subsidy")
---> 34 return add(spm_unit, period, BENEFITS)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/commons/formulas.py:225, in add(entity, period, variables, options)
    211 """Sums a list of variables.
    212 
    213 Args:
   (...)
    223     ArrayLike: The result of the operation.
    224 """
--> 225 return for_each_variable(
    226     entity, period, variables, agg_func="add", options=options
    227 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/commons/formulas.py:184, in for_each_variable(entity, period, variables, agg_func, group_agg_func, options)
    182 elif variable_entity.is_person:
    183     values = group_agg_func(
--> 184         entity.members(variable, period, options=options)
    185     )
    186 elif entity.entity.is_person:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/ssi.py:16, in ssi.formula(person, period, parameters)
     15     return 0
---> 16 return max_(0, person("uncapped_ssi", period))

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/uncapped_ssi.py:13, in uncapped_ssi.formula(person, period, parameters)
     12 def formula(person, period, parameters):
---> 13     amount = person("ssi_amount_if_eligible", period)
     14     meets_resource_test = person("meets_ssi_resource_test", period)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/ssi_amount_if_eligible.py:16, in ssi_amount_if_eligible.formula(person, period, parameters)
     13 ssi = parameters(period).gov.ssa.ssi.amount
     14 return (
     15     where(
---> 16         person("ssi_claim_is_joint", period),
     17         ssi.couple,
     18         ssi.individual,
     19     )
     20     * MONTHS_IN_YEAR
     21 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/ssi_claim_is_joint.py:12, in ssi_claim_is_joint.formula(person, period, parameters)
     11 eligible = person("is_ssi_aged_blind_disabled", period)
---> 12 both_eligible = person.marital_unit.sum(eligible) == 2
     13 income_is_deemed = (
     14     person("ssi_income_deemed_from_ineligible_spouse", period) > 0
     15 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/projectors/projector.py:28, in Projector.__getattr__.<locals>.projector_function(*args, **kwargs)
     27 result = reference_attr(*args, **kwargs)
---> 28 return self.transform_and_bubble_up(result)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/projectors/projector.py:37, in Projector.transform_and_bubble_up(self, result)
     36 def transform_and_bubble_up(self, result):
---> 37     transformed_result = self.transform(result)
     38     if self.parent is None:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/projectors/entity_to_person_projector.py:19, in EntityToPersonProjector.transform(self, result)
     18 def transform(self, result: ArrayLike) -> ArrayLike:
---> 19     return self.reference_entity.project(result)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/group_population.py:335, in GroupPopulation.project(self, array, role)
    334 def project(self, array: ArrayLike, role: Role = None) -> ArrayLike:
--> 335     self.check_array_compatible_with_entity(array)
    336     self.entity.check_role_validity(role)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:60, in Population.check_array_compatible_with_entity(self, array)
     59 if not self.count == array.size:
---> 60     raise ValueError(
     61         "Input {} is not a valid value for the entity {} (size = {} != {} = count)".format(
     62             array, self.entity.key, array.size, self.count
     63         )
     64     )

ValueError: Input [0. 0. 0. ... 0. 0. 0.] is not a valid value for the entity marital_unit (size = 3002 != 3003 = count)

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/individual_sim.py:264, in IndividualSim.calc(self, var, period, target, index, map_to, reform)
    263 try:
--> 264     result = self.sim.calculate_add(var, period)
    265 except:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:597, in Simulation.calculate_add(self, variable_name, period, decode_enums)
    591     raise ValueError(
    592         "Unable to sum constant variable '{}' over period {}: only variables defined daily, monthly, or yearly can be summed over time.".format(
    593             variable.name, period
    594         )
    595     )
--> 597 return sum(
    598     self.calculate(variable_name, sub_period)
    599     for sub_period in period.get_subperiods(variable.definition_period)
    600 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:598, in <genexpr>(.0)
    591     raise ValueError(
    592         "Unable to sum constant variable '{}' over period {}: only variables defined daily, monthly, or yearly can be summed over time.".format(
    593             variable.name, period
    594         )
    595     )
    597 return sum(
--> 598     self.calculate(variable_name, sub_period)
    599     for sub_period in period.get_subperiods(variable.definition_period)
    600 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:684, in Simulation._run_formula(self, variable, population, period)
    683     for added_variable in adds_list:
--> 684         values = values + self.calculate(
    685             added_variable, period, map_to=variable.entity.key
    686         )
    687 if variable.subtracts is not None:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py:34, in spm_unit_benefits.formula(spm_unit, period, parameters)
     33     BENEFITS.append("spm_unit_capped_housing_subsidy")
---> 34 return add(spm_unit, period, BENEFITS)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/commons/formulas.py:225, in add(entity, period, variables, options)
    211 """Sums a list of variables.
    212 
    213 Args:
   (...)
    223     ArrayLike: The result of the operation.
    224 """
--> 225 return for_each_variable(
    226     entity, period, variables, agg_func="add", options=options
    227 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/commons/formulas.py:184, in for_each_variable(entity, period, variables, agg_func, group_agg_func, options)
    182 elif variable_entity.is_person:
    183     values = group_agg_func(
--> 184         entity.members(variable, period, options=options)
    185     )
    186 elif entity.entity.is_person:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/ssi.py:16, in ssi.formula(person, period, parameters)
     15     return 0
---> 16 return max_(0, person("uncapped_ssi", period))

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/uncapped_ssi.py:13, in uncapped_ssi.formula(person, period, parameters)
     12 def formula(person, period, parameters):
---> 13     amount = person("ssi_amount_if_eligible", period)
     14     meets_resource_test = person("meets_ssi_resource_test", period)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/ssi_amount_if_eligible.py:16, in ssi_amount_if_eligible.formula(person, period, parameters)
     13 ssi = parameters(period).gov.ssa.ssi.amount
     14 return (
     15     where(
---> 16         person("ssi_claim_is_joint", period),
     17         ssi.couple,
     18         ssi.individual,
     19     )
     20     * MONTHS_IN_YEAR
     21 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/ssi_claim_is_joint.py:12, in ssi_claim_is_joint.formula(person, period, parameters)
     11 eligible = person("is_ssi_aged_blind_disabled", period)
---> 12 both_eligible = person.marital_unit.sum(eligible) == 2
     13 income_is_deemed = (
     14     person("ssi_income_deemed_from_ineligible_spouse", period) > 0
     15 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/projectors/projector.py:28, in Projector.__getattr__.<locals>.projector_function(*args, **kwargs)
     27 result = reference_attr(*args, **kwargs)
---> 28 return self.transform_and_bubble_up(result)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/projectors/projector.py:37, in Projector.transform_and_bubble_up(self, result)
     36 def transform_and_bubble_up(self, result):
---> 37     transformed_result = self.transform(result)
     38     if self.parent is None:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/projectors/entity_to_person_projector.py:19, in EntityToPersonProjector.transform(self, result)
     18 def transform(self, result: ArrayLike) -> ArrayLike:
---> 19     return self.reference_entity.project(result)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/group_population.py:335, in GroupPopulation.project(self, array, role)
    334 def project(self, array: ArrayLike, role: Role = None) -> ArrayLike:
--> 335     self.check_array_compatible_with_entity(array)
    336     self.entity.check_role_validity(role)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:60, in Population.check_array_compatible_with_entity(self, array)
     59 if not self.count == array.size:
---> 60     raise ValueError(
     61         "Input {} is not a valid value for the entity {} (size = {} != {} = count)".format(
     62             array, self.entity.key, array.size, self.count
     63         )
     64     )

ValueError: Input [0. 0. 0. ... 0. 0. 0.] is not a valid value for the entity marital_unit (size = 3002 != 3003 = count)

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
Cell In[1], line 48
     46 for adults in range(1, 3):
     47     for children in range(0, 4):
---> 48         l.append(make_tax(adults, children))
     50 df = pd.concat(l)
     52 LABELS = dict(
     53     employment_income="Employment income",
     54     spm_unit_net_income="Net income",
   (...)
     57     children="Children",
     58 )

Cell In[1], line 29, in make_tax(adults, children)
     27 sim.vary("employment_income", max=100_000, step=100)
     28 employment_income = sim.calc("employment_income")[0]
---> 29 spm_unit_net_income = sim.calc("spm_unit_net_income")[0].round()
     30 mtr = 1 - sim.deriv(
     31     "spm_unit_net_income", "employment_income", wrt_target="head"
     32 )
     33 return pd.DataFrame(
     34     dict(
     35         employment_income=employment_income,
   (...)
     40     )
     41 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/individual_sim.py:266, in IndividualSim.calc(self, var, period, target, index, map_to, reform)
    264         result = self.sim.calculate_add(var, period)
    265     except:
--> 266         result = self.simulation.calculate_divide(var, period)
    267 if (
    268     target is not None
    269     and target not in self.situation_data[entity.plural]
    270 ):
    271     map_to = self.get_entity(target).key

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:634, in Simulation.calculate_divide(self, variable_name, period, decode_enums)
    630     return (
    631         self.calculate(variable_name, period=computation_period) / 12.0
    632     )
    633 elif period.unit == periods.YEAR:
--> 634     return self.calculate(variable_name, period)
    636 raise ValueError(
    637     "Unable to divide the value of '{}' to match period {}.".format(
    638         variable_name, period
    639     )
    640 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    315 self.tracer.record_calculation_start(
    316     variable_name, period, self.branch_name
    317 )
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:
    322         result = result.decode_to_str()

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    479 try:
    480     self._check_for_cycle(variable.name, period)
--> 481     array = self._run_formula(variable, population, period)
    483     # If no result, use the default value and cache it
    484     if array is None:
    485         # Check if the variable has a previously defined value

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:684, in Simulation._run_formula(self, variable, population, period)
    682     values = 0
    683     for added_variable in adds_list:
--> 684         values = values + self.calculate(
    685             added_variable, period, map_to=variable.entity.key
    686         )
    687 if variable.subtracts is not None:
    688     if isinstance(variable.subtracts, str):

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    315 self.tracer.record_calculation_start(
    316     variable_name, period, self.branch_name
    317 )
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:
    322         result = result.decode_to_str()

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    479 try:
    480     self._check_for_cycle(variable.name, period)
--> 481     array = self._run_formula(variable, population, period)
    483     # If no result, use the default value and cache it
    484     if array is None:
    485         # Check if the variable has a previously defined value

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    719     array = formula(population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py:34, in spm_unit_benefits.formula(spm_unit, period, parameters)
     32 if not parameters(period).gov.hud.abolition:
     33     BENEFITS.append("spm_unit_capped_housing_subsidy")
---> 34 return add(spm_unit, period, BENEFITS)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/commons/formulas.py:225, in add(entity, period, variables, options)
    205 def add(
    206     entity: Population,
    207     period: Period,
    208     variables: List[str],
    209     options: List[str] = None,
    210 ):
    211     """Sums a list of variables.
    212 
    213     Args:
   (...)
    223         ArrayLike: The result of the operation.
    224     """
--> 225     return for_each_variable(
    226         entity, period, variables, agg_func="add", options=options
    227     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/commons/formulas.py:184, in for_each_variable(entity, period, variables, agg_func, group_agg_func, options)
    181     values = entity(variable, period, options=options)
    182 elif variable_entity.is_person:
    183     values = group_agg_func(
--> 184         entity.members(variable, period, options=options)
    185     )
    186 elif entity.entity.is_person:
    187     raise ValueError(
    188         f"You requested to aggregate {variable} (defined for {variable_entity.plural}) to {entity.entity.plural}, but this is not yet implemented."
    189     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    130     return self.simulation.calculate_divide(
    131         variable_name, period, **calculate_kwargs
    132     )
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    315 self.tracer.record_calculation_start(
    316     variable_name, period, self.branch_name
    317 )
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:
    322         result = result.decode_to_str()

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    479 try:
    480     self._check_for_cycle(variable.name, period)
--> 481     array = self._run_formula(variable, population, period)
    483     # If no result, use the default value and cache it
    484     if array is None:
    485         # Check if the variable has a previously defined value

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    719     array = formula(population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/ssi.py:16, in ssi.formula(person, period, parameters)
     14 if parameters(period).gov.ssa.ssi.abolish_ssi:
     15     return 0
---> 16 return max_(0, person("uncapped_ssi", period))

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    130     return self.simulation.calculate_divide(
    131         variable_name, period, **calculate_kwargs
    132     )
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    315 self.tracer.record_calculation_start(
    316     variable_name, period, self.branch_name
    317 )
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:
    322         result = result.decode_to_str()

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    479 try:
    480     self._check_for_cycle(variable.name, period)
--> 481     array = self._run_formula(variable, population, period)
    483     # If no result, use the default value and cache it
    484     if array is None:
    485         # Check if the variable has a previously defined value

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    719     array = formula(population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/uncapped_ssi.py:13, in uncapped_ssi.formula(person, period, parameters)
     12 def formula(person, period, parameters):
---> 13     amount = person("ssi_amount_if_eligible", period)
     14     meets_resource_test = person("meets_ssi_resource_test", period)
     15     eligible = person("is_ssi_eligible_individual", period)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    130     return self.simulation.calculate_divide(
    131         variable_name, period, **calculate_kwargs
    132     )
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    315 self.tracer.record_calculation_start(
    316     variable_name, period, self.branch_name
    317 )
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:
    322         result = result.decode_to_str()

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    479 try:
    480     self._check_for_cycle(variable.name, period)
--> 481     array = self._run_formula(variable, population, period)
    483     # If no result, use the default value and cache it
    484     if array is None:
    485         # Check if the variable has a previously defined value

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    719     array = formula(population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/ssi_amount_if_eligible.py:16, in ssi_amount_if_eligible.formula(person, period, parameters)
     12 def formula(person, period, parameters):
     13     ssi = parameters(period).gov.ssa.ssi.amount
     14     return (
     15         where(
---> 16             person("ssi_claim_is_joint", period),
     17             ssi.couple,
     18             ssi.individual,
     19         )
     20         * MONTHS_IN_YEAR
     21     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    130     return self.simulation.calculate_divide(
    131         variable_name, period, **calculate_kwargs
    132     )
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    315 self.tracer.record_calculation_start(
    316     variable_name, period, self.branch_name
    317 )
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:
    322         result = result.decode_to_str()

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    479 try:
    480     self._check_for_cycle(variable.name, period)
--> 481     array = self._run_formula(variable, population, period)
    483     # If no result, use the default value and cache it
    484     if array is None:
    485         # Check if the variable has a previously defined value

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    719     array = formula(population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/ssi_claim_is_joint.py:12, in ssi_claim_is_joint.formula(person, period, parameters)
     10 def formula(person, period, parameters):
     11     eligible = person("is_ssi_aged_blind_disabled", period)
---> 12     both_eligible = person.marital_unit.sum(eligible) == 2
     13     income_is_deemed = (
     14         person("ssi_income_deemed_from_ineligible_spouse", period) > 0
     15     )
     16     return both_eligible | income_is_deemed

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/projectors/projector.py:28, in Projector.__getattr__.<locals>.projector_function(*args, **kwargs)
     26 def projector_function(*args, **kwargs):
     27     result = reference_attr(*args, **kwargs)
---> 28     return self.transform_and_bubble_up(result)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/projectors/projector.py:37, in Projector.transform_and_bubble_up(self, result)
     36 def transform_and_bubble_up(self, result):
---> 37     transformed_result = self.transform(result)
     38     if self.parent is None:
     39         return transformed_result

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/projectors/entity_to_person_projector.py:19, in EntityToPersonProjector.transform(self, result)
     18 def transform(self, result: ArrayLike) -> ArrayLike:
---> 19     return self.reference_entity.project(result)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/group_population.py:335, in GroupPopulation.project(self, array, role)
    334 def project(self, array: ArrayLike, role: Role = None) -> ArrayLike:
--> 335     self.check_array_compatible_with_entity(array)
    336     self.entity.check_role_validity(role)
    337     if role is None:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:60, in Population.check_array_compatible_with_entity(self, array)
     58 def check_array_compatible_with_entity(self, array: numpy.ndarray) -> None:
     59     if not self.count == array.size:
---> 60         raise ValueError(
     61             "Input {} is not a valid value for the entity {} (size = {} != {} = count)".format(
     62                 array, self.entity.key, array.size, self.count
     63             )
     64         )

ValueError: Input [0. 0. 0. ... 0. 0. 0.] is not a valid value for the entity marital_unit (size = 3002 != 3003 = count)

Marginal tax rates can reach as low as -84% and as high (excluding cliffs) as 65%.

fig = px.line(
    df,
    "employment_income",
    "mtr",
    color="children",
    animation_frame="adults",
    labels=LABELS,
    title="Marginal tax rate for a New York household",
    color_discrete_map=COLOR_MAP,
)
fig.update_layout(
    xaxis_tickformat="$,",
    yaxis_tickformat=".1%",
    yaxis_range=[-1, 1],
    plot_bgcolor="white",
    xaxis_gridcolor=LIGHT_GRAY,
    yaxis_gridcolor=LIGHT_GRAY,
)
fig.show()