Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
19290fb
Add default parameters to select statements for better performance
MaxGhenis Jul 23, 2025
d3b07f4
Add default parameters to more select statements
MaxGhenis Jul 23, 2025
18b5591
Add more default parameters to select statements
MaxGhenis Jul 23, 2025
dc76432
Add filing status utility function and refactor state tax calculations
MaxGhenis Jul 23, 2025
70cb096
Apply formatting to filing status utility changes
MaxGhenis Jul 23, 2025
335e082
Add default parameters to remaining select statements
MaxGhenis Jul 23, 2025
2805445
Add default parameters to NJ, NY, OR, and WI state tax select statements
MaxGhenis Jul 23, 2025
55deaae
Fix select statements in AZ family tax credit and CCDF duration of care
MaxGhenis Jul 23, 2025
f11d0be
Fix select statements in CCDF, taxsim, and FSLA overtime files
MaxGhenis Jul 23, 2025
09f26f8
Refactor 20 state tax files to use filing status utility and add defa…
MaxGhenis Jul 23, 2025
9971453
Refactor 10 more state tax files to use filing status utility
MaxGhenis Jul 23, 2025
e8f8a22
Add tests for select_filing_status_value utility function
MaxGhenis Jul 23, 2025
7c07f7e
Refactor NJ, MT, NM, ME, and MD tax files to use filing status utility
MaxGhenis Jul 23, 2025
89e91fa
Add default to CCDF age group select statement and refactor OR tax file
MaxGhenis Jul 23, 2025
81e1676
Fix select_filing_status_value to handle state-specific filing status…
MaxGhenis Jul 24, 2025
4f4735e
Fix household_income_decile test to include required household structure
MaxGhenis Jul 24, 2025
3426734
Merge upstream master and resolve conflict in household_income_decile…
MaxGhenis Jul 24, 2025
7df38fe
Remove redundant pell_grant_uses_sai variable
MaxGhenis Jul 24, 2025
4536e38
Consolidate Pell Grant calculation method variables
MaxGhenis Jul 25, 2025
3acd6ff
Replace select statements with where in Pell Grant variables
MaxGhenis Jul 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions changelog_entry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
- bump: patch
changes:
fixed:
- Added default parameters to select statements for better performance and clarity (issue #1176)
- Updated select statements with dummy True conditions to use default parameter instead
- Set SINGLE as default for filing status select statements per issue #3334
changed:
- Consolidated Pell Grant calculation method variables by replacing pell_grant_calculation_method and pell_grant_uses_efc with pell_grant_uses_sai
55 changes: 55 additions & 0 deletions policyengine_us/tests/tools/test_filing_status_utility.yaml
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we confirm that the unit test passes? Don't see the execution in the testing logs

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not blocking but it would be nice to see unit tests with the same md_taxable_income amount but different md_income_tax_before_credits based on household composition

Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Test that select_filing_status_value works correctly with MD income tax
- name: Test filing status utility with MD tax - single filer
period: 2024
input:
people:
person:
age: 30
tax_units:
tax_unit:
members: [person]
md_taxable_income: 50_000
households:
household:
members: [person]
state_code: MD
output:
md_income_tax_before_credits: 2_322.50

- name: Test filing status utility with MD tax - joint filers
period: 2024
input:
people:
head:
age: 30
spouse:
age: 28
tax_units:
tax_unit:
members: [head, spouse]
md_taxable_income: 100_000
households:
household:
members: [head, spouse]
state_code: MD
output:
md_income_tax_before_credits: 4_697.50

- name: Test filing status utility with MD tax - head of household
period: 2024
input:
people:
head:
age: 35
child:
age: 10
tax_units:
tax_unit:
members: [head, child]
md_taxable_income: 75_000
households:
household:
members: [head, child]
state_code: MD
output:
md_income_tax_before_credits: 3_510.00
78 changes: 78 additions & 0 deletions policyengine_us/tools/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,81 @@ def get_previous_threshold(
return t[
max_((t <= values.reshape((1, len(values))).T).sum(axis=1) - 1, 0)
]


def select_filing_status_value(
filing_status: ArrayLike,
filing_status_values: dict,
input_value: ArrayLike = None,
**kwargs,
) -> ArrayLike:
"""
Select a value based on filing status, with SINGLE as the default.

This is a common pattern for selecting parameter values based on filing status.
According to IRS SOI data, SINGLE is the most common filing status.

Args:
filing_status: Array of filing status enum values
filing_status_values: Dict mapping filing status to values or functions
input_value: Optional input value to pass to functions (e.g., taxable income)

Returns:
Array of selected values based on filing status

Example:
# For parameter values
result = select_filing_status_value(
filing_status,
parameters.amount
)

# For calculated values (e.g., tax brackets)
result = select_filing_status_value(
filing_status,
parameters.rates,
taxable_income
)
"""
statuses = filing_status.possible_values

# Helper function to get value
def get_value(fs_value):
if input_value is not None and hasattr(fs_value, "calc"):
# It's a rate schedule or similar
return fs_value.calc(input_value, **kwargs)
elif hasattr(fs_value, "__call__"):
# It's a callable
return (
fs_value(input_value, **kwargs)
if input_value is not None
else fs_value(**kwargs)
)
else:
# It's a simple value
return fs_value

# Build conditions and values, excluding SINGLE
conditions = []
values = []

# Check each filing status except SINGLE
for status_name in [
"JOINT",
"SEPARATE",
"HEAD_OF_HOUSEHOLD",
"SURVIVING_SPOUSE",
]:
# Check if this enum value exists in this filing status enum
if hasattr(statuses, status_name):
status_enum = getattr(statuses, status_name)
if status_enum.name.lower() in filing_status_values:
conditions.append(filing_status == status_enum)
values.append(
get_value(filing_status_values[status_enum.name.lower()])
)

# SINGLE is the default
default_value = get_value(filing_status_values["single"])

return select(conditions, values, default=default_value)
1 change: 1 addition & 0 deletions policyengine_us/variables/contrib/taxsim/taxsim_mstat.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ def formula(tax_unit, period, parameters):
6,
8,
],
default=1, # SINGLE
)
3 changes: 1 addition & 2 deletions policyengine_us/variables/contrib/taxsim/taxsim_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,12 @@ def formula(tax_unit, period, parameters):
state_code_str == "MA",
state_code_str == "NY",
state_code_str == "WA",
True,
],
[
21,
22,
33,
48,
0,
],
default=0,
)
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ def formula(person, period, parameters):
max_(0, head_contribution),
where(automatic_zero, 0, max_(0, head_contribution)),
],
default=0,
)

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ def formula(person, period, parameters):
assets = person("pell_grant_contribution_from_assets", period)
adjusted_available_income = available_income + assets
formula = person("pell_grant_formula", period)
uses_efc = person("pell_grant_uses_efc", period)
uses_sai = person("pell_grant_uses_sai", period)
p = parameters(period).gov.ed.pell_grant.head
positive_head_contribution = p.marginal_rate.calc(
Expand All @@ -40,4 +39,4 @@ def formula(person, period, parameters):
mask = dependents > 0
amount_per_dependent[mask] = total[mask] / dependents[mask]

return select([uses_efc, uses_sai], [amount_per_dependent, total])
return where(uses_sai, total, amount_per_dependent)
8 changes: 3 additions & 5 deletions policyengine_us/variables/gov/ed/pell_grant/pell_grant.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ def formula(person, period, parameters):
efc = person("pell_grant_efc", period)
sai = person("pell_grant_sai", period)
eligibility = person("pell_grant_eligibility_type", period)
uses_efc = person("pell_grant_uses_efc", period)
uses_sai = person("pell_grant_uses_sai", period)
p = parameters(period).gov.ed.pell_grant
contribution = select([uses_efc, uses_sai], [efc, sai])
contribution = where(uses_sai, sai, efc)
unbounded = coa - contribution
amount = where(unbounded < p.amount.min, 0, unbounded)
uncapped_efc_pell = amount * (
Expand All @@ -30,9 +29,8 @@ def formula(person, period, parameters):
eligibility == eligibility.possible_values.MINIMUM,
],
[0, p.amount.max, amount],
default=0,
)
uncapped = select(
[uses_efc, uses_sai], [uncapped_efc_pell, uncapped_sai_pell]
)
uncapped = where(uses_sai, uncapped_sai_pell, uncapped_efc_pell)
max = min_(coa, p.amount.max)
return min_(max, uncapped)

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ def formula(person, period, parameters):
PellGrantHouseholdType.INDEPENDENT_SINGLE,
PellGrantHouseholdType.INDEPENDENT_NOT_SINGLE,
],
default=PellGrantHouseholdType.INDEPENDENT_SINGLE,
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a unit test, just to sanity test years such as 2022 and 2025

Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ class pell_grant_uses_sai(Variable):
value_type = bool
entity = Person
definition_period = YEAR
label = "Pell Grant uses the student aid index"
label = "Pell Grant uses the Student Aid Index"

def formula(person, period, parameters):
method = person.tax_unit("pell_grant_calculation_method", period)
return method == method.possible_values.SAI
return True

def formula_2024(person, period, parameters):
return True

def formula_2023(person, period, parameters):
return False
1 change: 1 addition & 0 deletions policyengine_us/variables/gov/hhs/ccdf/ccdf_age_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ def formula(person, period, parameters):
CCDFAgeGroup.PRESCHOOLER,
CCDFAgeGroup.SCHOOL_AGE,
],
default=CCDFAgeGroup.SCHOOL_AGE,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it not default to infant to match the default value on line 14?

)
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,11 @@ def formula(person, period):
hours_per_week >= 30,
hours_per_day >= 6,
hours_per_day >= 3,
True,
],
[
CCDFDurationOfCare.WEEKLY,
CCDFDurationOfCare.DAILY,
CCDFDurationOfCare.PART_DAY,
CCDFDurationOfCare.HOURLY,
],
default=CCDFDurationOfCare.HOURLY,
)
1 change: 1 addition & 0 deletions policyengine_us/variables/gov/hhs/ccdf/ccdf_market_rate.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ def formula(person, period, parameters):
duration_of_care == durations_of_care.HOURLY,
],
[1, days_per_week, days_per_week, hours_per_week],
default=0,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, should this not default to 1 if the default value of ccdf_duration_of_care is weekly?

)
return rate_per_period * periods_per_week * WEEKS_IN_YEAR
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also haven't unit tested this -- can we add while in here?

Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def formula(person, period, parameters):
[
person(variable, period)
for variable in variable_to_category.keys()
]
+ [True],
list(variable_to_category.values()) + [MedicaidCategory.NONE],
],
list(variable_to_category.values()),
default=MedicaidCategory.NONE,
)
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,13 @@ def formula(tax_unit, period, parameters):
num_qualifying_individuals == 1,
num_qualifying_individuals == 2,
filing_status == filing_status.possible_values.SEPARATE,
True,
],
[
elderly_disabled.amount.one_qualified,
elderly_disabled.amount.two_qualified,
elderly_disabled.amount.separate,
0,
],
default=0,
)

# Limitations on under-65s
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,10 @@ def formula(tax_unit, period, parameters):
[
under_first_threshold,
under_second_threshold,
True,
],
[
0,
amount_if_under_second_threshold,
amount_if_over_second_threshold,
],
default=amount_if_over_second_threshold,
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from policyengine_us.model_api import *
from policyengine_us.tools.general import select_filing_status_value


class nyc_household_credit(Variable):
Expand All @@ -23,23 +24,25 @@ def formula(tax_unit, period, parameters):
# Then get their number of people.
tax_unit_size = tax_unit("tax_unit_size", period)

filing_statuses = filing_status.possible_values

return select(
[
filing_status == filing_statuses.SINGLE,
filing_status == filing_statuses.SEPARATE,
],
[
# Single filers get a flat amount.
p.flat_amount.calc(federal_agi, right=True),
# Separate filers get an amount for each person in the tax
# unit, varying with AGI.
p.separate_per_dependent.calc(federal_agi, right=True)
* tax_unit_size,
],
# Joint, head of household, and surviving spouse filers have a different
# amount per person, varying with AGI.
default=p.other_per_dependent.calc(federal_agi, right=True)
# Create a dictionary of values for each filing status
filing_status_values = {
"single": p.flat_amount.calc(federal_agi, right=True),
"separate": p.separate_per_dependent.calc(federal_agi, right=True)
* tax_unit_size,
# Joint, head of household, and surviving spouse use the same formula
"joint": p.other_per_dependent.calc(federal_agi, right=True)
* tax_unit_size,
"head_of_household": p.other_per_dependent.calc(
federal_agi, right=True
)
* tax_unit_size,
"surviving_spouse": p.other_per_dependent.calc(
federal_agi, right=True
)
* tax_unit_size,
}

return select_filing_status_value(
filing_status,
filing_status_values,
)
Loading