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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove redundant uprating metadata and class-level aggregation metadata from variables that already define their computation explicitly.
45 changes: 39 additions & 6 deletions policyengine_us/data/economic_assumptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,38 @@
# is updated with new projection years, datasets will automatically
# extend to match — no hardcoded year constant to maintain.
CPI_U_PARAM_PATH = "gov.bls.cpi.cpi_u"
DEFAULT_MICRODATA_UPRATING = (
"calibration.gov.cbo.income_by_source.adjusted_gross_income"
)

MICRODATA_UPRATING_OVERRIDES = {
"american_opportunity_credit": DEFAULT_MICRODATA_UPRATING,
"cdcc_relevant_expenses": DEFAULT_MICRODATA_UPRATING,
"employment_income": "calibration.gov.irs.soi.employment_income",
"employment_income_last_year": "calibration.gov.irs.soi.employment_income",
"energy_efficient_home_improvement_credit": DEFAULT_MICRODATA_UPRATING,
"foreign_tax_credit": DEFAULT_MICRODATA_UPRATING,
"interest_deduction": DEFAULT_MICRODATA_UPRATING,
"long_term_capital_gains": "calibration.gov.irs.soi.long_term_capital_gains",
"misc_deduction": DEFAULT_MICRODATA_UPRATING,
"person_weight": "calibration.gov.census.populations.total",
"pre_tax_contributions": DEFAULT_MICRODATA_UPRATING,
"rent": "gov.bls.cpi.cpi_u",
"savers_credit": DEFAULT_MICRODATA_UPRATING,
"self_employment_income": "calibration.gov.irs.soi.self_employment_income",
"self_employed_health_insurance_ald": DEFAULT_MICRODATA_UPRATING,
"self_employed_pension_contribution_ald": DEFAULT_MICRODATA_UPRATING,
"social_security": "calibration.gov.irs.soi.social_security",
"spm_unit_weight": "calibration.gov.census.populations.total",
"spm_unit_spm_threshold": DEFAULT_MICRODATA_UPRATING,
"state_and_local_sales_or_income_tax": DEFAULT_MICRODATA_UPRATING,
"sstb_self_employment_income": "calibration.gov.irs.soi.self_employment_income",
"taxable_pension_income": "calibration.gov.irs.soi.taxable_pension_income",
"taxable_unemployment_compensation": DEFAULT_MICRODATA_UPRATING,
"tax_unit_weight": "calibration.gov.census.populations.total",
"tax_exempt_pension_income": DEFAULT_MICRODATA_UPRATING,
"total_self_employment_income": "calibration.gov.irs.soi.self_employment_income",
}


def get_parameter_last_year(parameter) -> int:
Expand Down Expand Up @@ -100,11 +132,10 @@ def _apply_uprating(dataset: USMultiYearDataset, system=None) -> USMultiYearData
def _apply_single_year_uprating(current, previous, system):
"""Apply multiplicative uprating from previous year to current year.

For each variable column in each entity DataFrame, looks up the
variable's uprating parameter path in ``system.variables``. If the
variable has an uprating parameter, computes the growth factor as
``param(current_year) / param(previous_year)`` and multiplies the
column by that factor.
For each variable column in each entity DataFrame, looks up its
dataset-extension uprating parameter path. Formula and adds/subtracts
variables cannot use Core variable-level uprating, so their dataset-only
upraters live in ``MICRODATA_UPRATING_OVERRIDES`` instead.

Variables without an uprating parameter (or whose uprating parameter
evaluates to 0 for the previous year) are left unchanged — they were
Expand All @@ -122,7 +153,9 @@ def _apply_single_year_uprating(current, previous, system):
if col not in system.variables:
continue
var = system.variables[col]
uprating_path = getattr(var, "uprating", None)
uprating_path = MICRODATA_UPRATING_OVERRIDES.get(col) or getattr(
var, "uprating", None
)
if uprating_path is None:
continue

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,30 @@ def test_given_variable_without_uprating_then_values_unchanged(
# Then — age has no uprating, should be unchanged
np.testing.assert_array_equal(current.person["age"].values, AGE_BASE)

def test_given_computed_variable_with_microdata_override_then_values_scaled(
self, base_dataset
):
# Given
current = base_dataset.copy()
current.time_period = str(BASE_YEAR + 1)
previous = base_dataset
variables = {
"employment_income": MockVariable("employment_income", uprating=None)
}
system = MockSystem(
variables=variables,
parameters=build_mock_parameters(
{EMPLOYMENT_INCOME_UPRATING: EMPLOYMENT_INCOME_PARAM_VALUES}
),
)

# When
_apply_single_year_uprating(current, previous, system)

# Then
expected = EMPLOYMENT_INCOME_BASE * EMPLOYMENT_INCOME_GROWTH_FACTOR_2024_TO_2025
np.testing.assert_allclose(current.person["employment_income"].values, expected)

def test_given_household_variable_with_uprating_then_values_scaled(
self, base_dataset, mock_system
):
Expand Down
46 changes: 46 additions & 0 deletions policyengine_us/tests/test_system_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,49 @@ def test_package_import_does_not_raise():
import policyengine_us

importlib.reload(policyengine_us)


def test_variables_use_at_most_one_computation_mode():
"""Variables should choose exactly one computation mode after all
system-level mutations, including default uprating assignment.
"""
from policyengine_us.system import CountryTaxBenefitSystem

system = CountryTaxBenefitSystem()
conflicts = []
for name, variable in system.variables.items():
modes = []
if variable.formulas:
modes.append("formula")
if variable.adds is not None or variable.subtracts is not None:
modes.append("adds/subtracts")
if variable.uprating is not None:
modes.append("uprating")
if len(modes) > 1:
conflicts.append(f"{name}: {', '.join(modes)}")

assert conflicts == []


def test_computed_default_uprated_variables_have_microdata_overrides():
"""Default uprating can only be assigned to input variables at runtime.
Computed columns that previously received the default uprater keep that
behavior through microdata-only overrides.
"""
from policyengine_us.data.economic_assumptions import MICRODATA_UPRATING_OVERRIDES
from policyengine_us.system import CountryTaxBenefitSystem
from policyengine_us.tools.default_uprating import INPUT_VARIABLES

system = CountryTaxBenefitSystem()
missing_overrides = []
for name in INPUT_VARIABLES:
variable = system.variables.get(name)
if variable is None:
continue
if (
not variable.is_input_variable()
and name not in MICRODATA_UPRATING_OVERRIDES
):
missing_overrides.append(name)

assert missing_overrides == []
6 changes: 5 additions & 1 deletion policyengine_us/tools/default_uprating.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@

def add_default_uprating(system):
for variable in system.variables.values():
if (variable.name in INPUT_VARIABLES) and (variable.uprating is None):
if (
(variable.name in INPUT_VARIABLES)
and (variable.uprating is None)
and variable.is_input_variable()
):
variable.uprating = (
"calibration.gov.cbo.income_by_source.adjusted_gross_income"
)
Expand Down
1 change: 0 additions & 1 deletion policyengine_us/variables/gov/ssa/ss/social_security.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,3 @@ class social_security(Variable):
"social_security_" + i
for i in ["dependents", "disability", "retirement", "survivors"]
]
uprating = "calibration.gov.irs.soi.social_security"
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ class state_itemized_deductions(Variable):
label = "State itemized deductions"
unit = USD
definition_period = YEAR
adds = "gov.states.household.state_itemized_deductions"

def formula(tax_unit, period, parameters):
# States that adopt the federal itemized deductions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ class taxsim_state_agi(Variable):
label = "State adjusted gross income"
unit = USD
definition_period = YEAR
adds = "gov.states.household.state_agis"

def formula(tax_unit, period, parameters):
# States that adopt the federal AGI
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ class person_weight(Variable):
entity = Person
label = "Person weight"
definition_period = YEAR
uprating = "calibration.gov.census.populations.total"

def formula(person, period, parameters):
return person.household("household_weight", period)
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ class spm_unit_weight(Variable):
entity = SPMUnit
label = "SPM unit weight"
definition_period = YEAR
uprating = "calibration.gov.census.populations.total"

def formula(spm_unit, period, parameters):
# Use household weights if not provided
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ class tax_unit_weight(Variable):
entity = TaxUnit
label = "Tax unit weight"
definition_period = YEAR
uprating = "calibration.gov.census.populations.total"

def formula(tax_unit, period, parameters):
return tax_unit.household("household_weight", period)
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ class rent(Variable):
label = "Rent"
unit = USD
definition_period = YEAR
uprating = "gov.bls.cpi.cpi_u"

def formula(person, period, parameters):
pre_subsidy_rent = person("pre_subsidy_rent", period)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ class long_term_capital_gains(Variable):
title="26 U.S. Code § 1222(3)",
href="https://www.law.cornell.edu/uscode/text/26/1222#3",
)
uprating = "calibration.gov.irs.soi.long_term_capital_gains"
adds = [
"long_term_capital_gains_before_response",
"capital_gains_behavioral_response",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ class employment_income_last_year(Variable):
documentation = "Wages and salaries in prior year, including tips and commissions."
unit = USD
definition_period = YEAR
uprating = "calibration.gov.irs.soi.employment_income"

def formula_2024(person, period, parameters):
employment_income_target = parameters.calibration.gov.irs.soi.employment_income
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@ class taxable_pension_income(Variable):
label = "taxable pension income"
unit = USD
definition_period = YEAR
uprating = "calibration.gov.irs.soi.taxable_pension_income"

adds = ["taxable_public_pension_income", "taxable_private_pension_income"]
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,3 @@ class total_self_employment_income(Variable):
definition_period = YEAR
adds = ["self_employment_income", "sstb_self_employment_income"]
reference = "https://www.law.cornell.edu/uscode/text/26/1402#a"
uprating = "calibration.gov.irs.soi.self_employment_income"
1 change: 0 additions & 1 deletion policyengine_us/variables/input/employment_income.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,3 @@ class employment_income(Variable):
"employment_income_behavioral_response",
]
reference = "https://www.law.cornell.edu/uscode/text/26/3401#a"
uprating = "calibration.gov.irs.soi.employment_income"
1 change: 0 additions & 1 deletion policyengine_us/variables/input/self_employment_income.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,3 @@ class self_employment_income(Variable):
"self_employment_income_behavioral_response",
]
reference = "https://www.law.cornell.edu/uscode/text/26/1402#a"
uprating = "calibration.gov.irs.soi.self_employment_income"
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,3 @@ class sstb_self_employment_income(Variable):
"sstb_self_employment_income_before_lsr",
"sstb_self_employment_income_behavioral_response",
]
uprating = "calibration.gov.irs.soi.self_employment_income"
Loading