22from .soi import pe_to_soi , get_soi
33import numpy as np
44from policyengine_us_data .storage import STORAGE_FOLDER
5+ from policyengine_core .reforms import Reform
56
67
78def fmt (x ):
@@ -132,6 +133,7 @@ def build_loss_matrix(dataset: type, time_period):
132133 from policyengine_us import Microsimulation
133134
134135 sim = Microsimulation (dataset = dataset )
136+ sim .default_calculation_period = time_period
135137 hh_id = sim .calculate ("household_id" , map_to = "person" )
136138 tax_unit_hh_id = sim .map_result (
137139 hh_id , "person" , "tax_unit" , how = "value_from_first_person"
@@ -252,7 +254,7 @@ def build_loss_matrix(dataset: type, time_period):
252254 "alimony_income" : 13e9 ,
253255 "alimony_expense" : 13e9 ,
254256 # Rough estimate, not CPS derived
255- "real_estate_taxes" : 400e9 , # Rough estimate between 350bn and 600bn total property tax collections
257+ "real_estate_taxes" : 500e9 , # Rough estimate between 350bn and 600bn total property tax collections
256258 "rent" : 735e9 , # ACS total uprated by CPI
257259 }
258260
@@ -340,18 +342,22 @@ def build_loss_matrix(dataset: type, time_period):
340342 )
341343 targets_array .append (row ["population_under_5" ])
342344
343- # Population by number of newborns and pregancies
344-
345345 age = sim .calculate ("age" ).values
346346 infants = (age >= 0 ) & (age < 1 )
347347 label = "census/infants"
348348 loss_matrix [label ] = sim .map_result (infants , "person" , "household" )
349- targets_array .append (3_491_679 )
349+ # Total number of infants in the 1 Year ACS
350+ INFANTS_2023 = 3_491_679
351+ INFANTS_2022 = 3_437_933
352+ # Assume infant population grows at the same rate from 2023.
353+ infants_2024 = INFANTS_2023 * (INFANTS_2023 / INFANTS_2022 )
354+ targets_array .append (infants_2024 )
355+
356+ # SALT tax expenditure targeting
350357
351- pregnancies = (age >= - 0.75 ) & (age < 0 )
352- label = "census/pregnancies"
353- loss_matrix [label ] = sim .map_result (pregnancies , "person" , "household" )
354- targets_array .append (2_618_759 )
358+ _add_tax_expenditure_targets (
359+ dataset , time_period , sim , loss_matrix , targets_array
360+ )
355361
356362 if any (loss_matrix .isna ().sum () > 0 ):
357363 raise ValueError ("Some targets are missing from the loss matrix" )
@@ -360,3 +366,55 @@ def build_loss_matrix(dataset: type, time_period):
360366 raise ValueError ("Some targets are missing from the targets array" )
361367
362368 return loss_matrix , np .array (targets_array )
369+
370+
371+ def _add_tax_expenditure_targets (
372+ dataset ,
373+ time_period ,
374+ baseline_simulation ,
375+ loss_matrix : pd .DataFrame ,
376+ targets_array : list ,
377+ ):
378+ from policyengine_us import Microsimulation
379+
380+ income_tax_b = baseline_simulation .calculate (
381+ "income_tax" , map_to = "household"
382+ ).values
383+
384+ # Dictionary of itemized deductions and their target values
385+ # (in billions for 2024, per the 2024 JCT Tax Expenditures report)
386+ # https://www.jct.gov/publications/2024/jcx-48-24/
387+ ITEMIZED_DEDUCTIONS = {
388+ "salt_deduction" : 21.247e9 ,
389+ "medical_expense_deduction" : 11.4e9 ,
390+ "charitable_deduction" : 65.301e9 ,
391+ "interest_deduction" : 24.8e9 ,
392+ }
393+
394+ def make_repeal_class (deduction_var ):
395+ # Create a custom Reform subclass that neutralizes the given deduction.
396+ class RepealDeduction (Reform ):
397+ def apply (self ):
398+ self .neutralize_variable (deduction_var )
399+
400+ return RepealDeduction
401+
402+ for deduction , target in ITEMIZED_DEDUCTIONS .items ():
403+ # Generate the custom repeal class for the current deduction.
404+ RepealDeduction = make_repeal_class (deduction )
405+
406+ # Run the microsimulation using the repeal reform.
407+ simulation = Microsimulation (dataset = dataset , reform = RepealDeduction )
408+ simulation .default_calculation_period = time_period
409+
410+ # Calculate the baseline and reform income tax values.
411+ income_tax_r = simulation .calculate (
412+ "income_tax" , map_to = "household"
413+ ).values
414+
415+ # Compute the tax expenditure (TE) values.
416+ te_values = income_tax_r - income_tax_b
417+
418+ # Record the TE difference and the corresponding target value.
419+ loss_matrix [f"jct/{ deduction } _expenditure" ] = te_values
420+ targets_array .append (target )
0 commit comments