def test_credit_reforms(puf_subsample): """ Test personal credit reforms using puf.csv subsample """ rec = Records(data=puf_subsample) reform_year = 2017 # create current-law Calculator object, calc1 pol = Policy() calc1 = Calculator(policy=pol, records=rec) calc1.advance_to_year(reform_year) calc1.calc_all() itax1 = calc1.weighted_total('iitax') # create personal-refundable-credit-reform Calculator object, calc2 reform = {reform_year: {'_II_credit': [[1000, 1000, 1000, 1000, 1000]]}} pol.implement_reform(reform) calc2 = Calculator(policy=pol, records=rec) calc2.advance_to_year(reform_year) calc2.calc_all() itax2 = calc2.weighted_total('iitax') # create personal-nonrefundable-credit-reform Calculator object, calc3 reform = {reform_year: {'_II_credit_nr': [[1000, 1000, 1000, 1000, 1000]]}} pol = Policy() pol.implement_reform(reform) calc3 = Calculator(policy=pol, records=rec) calc3.advance_to_year(reform_year) calc3.calc_all() itax3 = calc3.weighted_total('iitax') # check income tax revenues generated by the three Calculator objects assert itax2 < itax1 # because refundable credits lower revenues assert itax3 > itax2 # because nonrefundable credits lower revenues less assert itax3 < itax1 # because nonrefundable credits lower revenues some
def test_credit_reforms(puf_subsample): """ Test personal credit reforms using puf.csv subsample """ rec = Records(data=puf_subsample) reform_year = 2017 # create current-law Calculator object, calc1 pol = Policy() calc1 = Calculator(policy=pol, records=rec) calc1.advance_to_year(reform_year) calc1.calc_all() itax1 = calc1.weighted_total('iitax') # create personal-refundable-credit-reform Calculator object, calc2 reform = {'II_credit': {reform_year: [1000, 1000, 1000, 1000, 1000]}} pol.implement_reform(reform) calc2 = Calculator(policy=pol, records=rec) calc2.advance_to_year(reform_year) calc2.calc_all() itax2 = calc2.weighted_total('iitax') # create personal-nonrefundable-credit-reform Calculator object, calc3 reform = {'II_credit_nr': {reform_year: [1000, 1000, 1000, 1000, 1000]}} pol = Policy() pol.implement_reform(reform) calc3 = Calculator(policy=pol, records=rec) calc3.advance_to_year(reform_year) calc3.calc_all() itax3 = calc3.weighted_total('iitax') # check income tax revenues generated by the three Calculator objects assert itax2 < itax1 # because refundable credits lower revenues assert itax3 > itax2 # because nonrefundable credits lower revenues less assert itax3 < itax1 # because nonrefundable credits lower revenues some
def test_correct_Calculator_instantiation(pit_fullsample, pit_subsample, gst_sample, cit_crosssample): syr = Policy.JSON_START_YEAR pol = Policy() assert pol.current_year == syr # specify expected number of filers and aggregate PIT liability expect_weight = 35.241e6 expect_pitax = 1812.601e9 # expect_corpweight = ??? # expect_citax = ??? # create full-sample Calculator object rec_full = Records(data=pit_fullsample) grec = GSTRecords(data=gst_sample) crec = CorpRecords(data=cit_crosssample) calc_full = Calculator(policy=pol, records=rec_full, gstrecords=grec, corprecords=crec) assert isinstance(calc_full, Calculator) assert calc_full.current_year == syr assert calc_full.records_current_year() == syr calc_full.calc_all() actual_full_weight = calc_full.total_weight() actual_full_pitax = calc_full.weighted_total('pitax') assert np.allclose([actual_full_weight], [expect_weight]) assert np.allclose([actual_full_pitax], [expect_pitax]) # TODO: test for corporate results # create sub-sample Calculator object """
def test_calculator_using_nonstd_input(): """ Test Calculator using non-standard input records. """ # check Calculator handling of raw, non-standard input data with no aging pol = Policy() pol.set_year(RAWINPUT_YEAR) # set policy params to input data year nonstd = Records( data=pd.read_csv(StringIO(RAWINPUT_CONTENTS)), start_year=RAWINPUT_YEAR, # set raw input data year gfactors=None, # keeps raw data unchanged weights=None) assert nonstd.array_length == RAWINPUT_FUNITS calc = Calculator(policy=pol, records=nonstd, sync_years=False) # keeps raw data unchanged assert calc.current_year == RAWINPUT_YEAR calc.calc_all() assert calc.weighted_total('e00200') == 0 assert calc.total_weight() == 0 varlist = ['RECID', 'MARS'] dframe = calc.dataframe(varlist) assert isinstance(dframe, pd.DataFrame) assert dframe.shape == (RAWINPUT_FUNITS, len(varlist)) mars = calc.array('MARS') assert isinstance(mars, np.ndarray) assert mars.shape == (RAWINPUT_FUNITS, ) exp_iitax = np.zeros((nonstd.array_length, )) assert np.allclose(calc.array('iitax'), exp_iitax) mtr_ptax, _, _ = calc.mtr(wrt_full_compensation=False) exp_mtr_ptax = np.zeros((nonstd.array_length, )) exp_mtr_ptax.fill(0.153) assert np.allclose(mtr_ptax, exp_mtr_ptax)
def test_calculator_using_nonstd_input(rawinputfile): # check Calculator handling of raw, non-standard input data with no aging pol = Policy() pol.set_year(RAWINPUTFILE_YEAR) # set policy params to input data year nonstd = Records( data=rawinputfile.name, gfactors=None, # keeps raw data unchanged weights=None, start_year=RAWINPUTFILE_YEAR) # set raw input data year assert nonstd.dim == RAWINPUTFILE_FUNITS calc = Calculator(policy=pol, records=nonstd, sync_years=False) # keeps raw data unchanged assert calc.current_year == RAWINPUTFILE_YEAR calc.calc_all() assert calc.weighted_total('e00200') == 0 assert calc.total_weight() == 0 varlist = ['RECID', 'MARS'] pdf = calc.dataframe(varlist) assert isinstance(pdf, pd.DataFrame) assert pdf.shape == (RAWINPUTFILE_FUNITS, len(varlist)) mars = calc.array('MARS') assert isinstance(mars, np.ndarray) assert mars.shape == (RAWINPUTFILE_FUNITS, ) exp_iitax = np.zeros((nonstd.dim, )) assert np.allclose(calc.records.iitax, exp_iitax) mtr_ptax, _, _ = calc.mtr(wrt_full_compensation=False) exp_mtr_ptax = np.zeros((nonstd.dim, )) exp_mtr_ptax.fill(0.153) assert np.allclose(mtr_ptax, exp_mtr_ptax)
def test_calculator_using_nonstd_input(rawinputfile): """ Test Calculator using non-standard input records. """ # check Calculator handling of raw, non-standard input data with no aging pol = Policy() pol.set_year(RAWINPUTFILE_YEAR) # set policy params to input data year nonstd = Records(data=rawinputfile.name, gfactors=None, # keeps raw data unchanged weights=None, start_year=RAWINPUTFILE_YEAR) # set raw input data year assert nonstd.array_length == RAWINPUTFILE_FUNITS calc = Calculator(policy=pol, records=nonstd, sync_years=False) # keeps raw data unchanged assert calc.current_year == RAWINPUTFILE_YEAR calc.calc_all() assert calc.weighted_total('e00200') == 0 assert calc.total_weight() == 0 varlist = ['RECID', 'MARS'] dframe = calc.dataframe(varlist) assert isinstance(dframe, pd.DataFrame) assert dframe.shape == (RAWINPUTFILE_FUNITS, len(varlist)) mars = calc.array('MARS') assert isinstance(mars, np.ndarray) assert mars.shape == (RAWINPUTFILE_FUNITS,) exp_iitax = np.zeros((nonstd.array_length,)) assert np.allclose(calc.array('iitax'), exp_iitax) mtr_ptax, _, _ = calc.mtr(wrt_full_compensation=False) exp_mtr_ptax = np.zeros((nonstd.array_length,)) exp_mtr_ptax.fill(0.153) assert np.allclose(mtr_ptax, exp_mtr_ptax)
def test_calc_all_benefits_amounts(cps_subsample): ''' Testing how benefits are handled in the calc_all method ''' # set a reform with a positive UBI amount ubi_ref = {'UBI_21': {2020: 1000}} # create baseline calculator pol = Policy() recs = Records.cps_constructor(data=cps_subsample) calc_base = Calculator(pol, recs) calc_base.advance_to_year(2020) calc_base.calc_all() # create reform calculator pol_ubi = Policy() pol_ubi.implement_reform(ubi_ref) calc_ubi = Calculator(pol_ubi, recs) calc_ubi.advance_to_year(2020) calc_ubi.calc_all() # check that differences in benefits totals are equal to diffs in # UBI ubi_diff = (calc_ubi.weighted_total('ubi') - calc_base.weighted_total('ubi')) / 1e9 benefit_cost_diff = (calc_ubi.weighted_total('benefit_cost_total') - calc_base.weighted_total('benefit_cost_total')) / 1e9 benefit_value_diff = (calc_ubi.weighted_total('benefit_cost_total') - calc_base.weighted_total('benefit_cost_total')) / 1e9 assert np.allclose(ubi_diff, benefit_cost_diff) assert np.allclose(ubi_diff, benefit_value_diff)
def test_behavioral_response(puf_subsample): """ Test that behavioral-response results are the same when generated from standard Tax-Calculator calls and when generated from tbi.run_nth_year_taxcalc_model() calls """ # specify reform and assumptions reform_json = """ {"policy": { "_II_rt5": {"2020": [0.25]}, "_II_rt6": {"2020": [0.25]}, "_II_rt7": {"2020": [0.25]}, "_PT_rt5": {"2020": [0.25]}, "_PT_rt6": {"2020": [0.25]}, "_PT_rt7": {"2020": [0.25]}, "_II_em": {"2020": [1000]} }} """ assump_json = """ {"behavior": {"_BE_sub": {"2013": [0.25]}}, "growdiff_baseline": {}, "growdiff_response": {}, "consumption": {}, "growmodel": {} } """ params = Calculator.read_json_param_objects(reform_json, assump_json) # specify keyword arguments used in tbi function call kwargs = { 'start_year': 2019, 'year_n': 0, 'use_puf_not_cps': True, 'use_full_sample': False, 'user_mods': { 'policy': params['policy'], 'behavior': params['behavior'], 'growdiff_baseline': params['growdiff_baseline'], 'growdiff_response': params['growdiff_response'], 'consumption': params['consumption'], 'growmodel': params['growmodel'] }, 'return_dict': False } # generate aggregate results two ways: using tbi and standard calls num_years = 9 std_res = dict() tbi_res = dict() for using_tbi in [True, False]: for year in range(0, num_years): cyr = year + kwargs['start_year'] if using_tbi: kwargs['year_n'] = year tables = run_nth_year_taxcalc_model(**kwargs) tbi_res[cyr] = dict() for tbl in ['aggr_1', 'aggr_2', 'aggr_d']: tbi_res[cyr][tbl] = tables[tbl] else: rec = Records(data=puf_subsample) pol = Policy() calc1 = Calculator(policy=pol, records=rec) pol.implement_reform(params['policy']) assert not pol.parameter_errors beh = Behavior() beh.update_behavior(params['behavior']) calc2 = Calculator(policy=pol, records=rec, behavior=beh) assert calc2.behavior_has_response() calc1.advance_to_year(cyr) calc2.advance_to_year(cyr) calc2 = Behavior.response(calc1, calc2) std_res[cyr] = dict() for tbl in ['aggr_1', 'aggr_2', 'aggr_d']: if tbl.endswith('_1'): itax = calc1.weighted_total('iitax') ptax = calc1.weighted_total('payrolltax') ctax = calc1.weighted_total('combined') elif tbl.endswith('_2'): itax = calc2.weighted_total('iitax') ptax = calc2.weighted_total('payrolltax') ctax = calc2.weighted_total('combined') elif tbl.endswith('_d'): itax = (calc2.weighted_total('iitax') - calc1.weighted_total('iitax')) ptax = (calc2.weighted_total('payrolltax') - calc1.weighted_total('payrolltax')) ctax = (calc2.weighted_total('combined') - calc1.weighted_total('combined')) cols = ['0_{}'.format(year)] rows = ['ind_tax', 'payroll_tax', 'combined_tax'] datalist = [itax, ptax, ctax] std_res[cyr][tbl] = pd.DataFrame(data=datalist, index=rows, columns=cols) # compare the two sets of results # NOTE that the tbi results have been "fuzzed" for PUF privacy reasons, # so there is no expectation that the results should be identical. no_diffs = True reltol = 0.004 # std and tbi differ if more than 0.4 percent different for year in range(0, num_years): cyr = year + kwargs['start_year'] col = '0_{}'.format(year) for tbl in ['aggr_1', 'aggr_2', 'aggr_d']: tbi = tbi_res[cyr][tbl][col] std = std_res[cyr][tbl][col] if not np.allclose(tbi, std, atol=0.0, rtol=reltol): no_diffs = False print('**** DIFF for year {} (year_n={}):'.format(cyr, year)) print('TBI RESULTS:') print(tbi) print('STD RESULTS:') print(std) assert no_diffs
def test_behavioral_response(use_puf_not_cps, puf_subsample, cps_fullsample): """ Test that behavioral-response results are the same when generated from standard Tax-Calculator calls and when generated from tbi.run_nth_year_taxcalc_model() calls """ # specify reform and assumptions reform_json = """ {"policy": { "_II_rt5": {"2020": [0.25]}, "_II_rt6": {"2020": [0.25]}, "_II_rt7": {"2020": [0.25]}, "_PT_rt5": {"2020": [0.25]}, "_PT_rt6": {"2020": [0.25]}, "_PT_rt7": {"2020": [0.25]}, "_II_em": {"2020": [1000]} }} """ assump_json = """ {"behavior": {"_BE_sub": {"2013": [0.25]}}, "growdiff_baseline": {}, "growdiff_response": {}, "consumption": {}, "growmodel": {} } """ params = Calculator.read_json_param_objects(reform_json, assump_json) # specify keyword arguments used in tbi function call kwargs = { 'start_year': 2019, 'year_n': 0, 'use_puf_not_cps': use_puf_not_cps, 'use_full_sample': False, 'user_mods': { 'policy': params['policy'], 'behavior': params['behavior'], 'growdiff_baseline': params['growdiff_baseline'], 'growdiff_response': params['growdiff_response'], 'consumption': params['consumption'], 'growmodel': params['growmodel'] }, 'return_dict': False } # generate aggregate results two ways: using tbi and standard calls num_years = 9 std_res = dict() tbi_res = dict() if use_puf_not_cps: rec = Records(data=puf_subsample) else: # IMPORTANT: must use same subsample as used in test_cpscsv.py because # that is the subsample used by run_nth_year_taxcalc_model std_cps_subsample = cps_fullsample.sample(frac=0.03, random_state=180) rec = Records.cps_constructor(data=std_cps_subsample) for using_tbi in [True, False]: for year in range(0, num_years): cyr = year + kwargs['start_year'] if using_tbi: kwargs['year_n'] = year tables = run_nth_year_taxcalc_model(**kwargs) tbi_res[cyr] = dict() for tbl in ['aggr_1', 'aggr_2', 'aggr_d']: tbi_res[cyr][tbl] = tables[tbl] else: pol = Policy() calc1 = Calculator(policy=pol, records=rec) pol.implement_reform(params['policy']) assert not pol.parameter_errors beh = Behavior() beh.update_behavior(params['behavior']) calc2 = Calculator(policy=pol, records=rec, behavior=beh) assert calc2.behavior_has_response() calc1.advance_to_year(cyr) calc2.advance_to_year(cyr) calc2 = Behavior.response(calc1, calc2) std_res[cyr] = dict() for tbl in ['aggr_1', 'aggr_2', 'aggr_d']: if tbl.endswith('_1'): itax = calc1.weighted_total('iitax') ptax = calc1.weighted_total('payrolltax') ctax = calc1.weighted_total('combined') elif tbl.endswith('_2'): itax = calc2.weighted_total('iitax') ptax = calc2.weighted_total('payrolltax') ctax = calc2.weighted_total('combined') elif tbl.endswith('_d'): itax = (calc2.weighted_total('iitax') - calc1.weighted_total('iitax')) ptax = (calc2.weighted_total('payrolltax') - calc1.weighted_total('payrolltax')) ctax = (calc2.weighted_total('combined') - calc1.weighted_total('combined')) cols = ['0_{}'.format(year)] rows = ['ind_tax', 'payroll_tax', 'combined_tax'] datalist = [itax, ptax, ctax] std_res[cyr][tbl] = pd.DataFrame(data=datalist, index=rows, columns=cols) for col in std_res[cyr][tbl].columns: val = std_res[cyr][tbl][col] * 1e-9 std_res[cyr][tbl][col] = round(val, 3) # compare the two sets of results # NOTE that the PUF tbi results have been "fuzzed" for privacy reasons, # so there is no expectation that those results should be identical. no_diffs = True cps_dump = False # setting to True produces dump output and test failure if use_puf_not_cps: reltol = 0.004 # std and tbi differ if more than 0.4 percent different dataset = 'PUF' dumping = False else: # CPS results are not "fuzzed", so reltol = 1e-9 # std and tbi should be virtually identical dataset = 'CPS' dumping = cps_dump for year in range(0, num_years): cyr = year + kwargs['start_year'] do_dump = bool(dumping and cyr >= 2019 and cyr <= 2020) col = '0_{}'.format(year) for tbl in ['aggr_1', 'aggr_2', 'aggr_d']: tbi = tbi_res[cyr][tbl][col] if do_dump: txt = 'DUMP of {} {} table for year {}:' print(txt.format(dataset, tbl, cyr)) print(tbi) std = std_res[cyr][tbl][col] if not np.allclose(tbi, std, atol=0.0, rtol=reltol): no_diffs = False txt = '***** {} diff in {} table for year {} (year_n={}):' print(txt.format(dataset, tbl, cyr, year)) print('TBI RESULTS:') print(tbi) print('STD RESULTS:') print(std) assert no_diffs assert not dumping
def test_itemded_component_amounts(year, cvname, hcname, puf_fullsample): """ Check that all c04470 components are adjusted to reflect the filing unit's standard-vs-itemized-deduction decision. Check for 2018 (when current law has no Pease phaseout of itemized deductions and already has complete haircuts for Casualty and Miscellaneous deductions) and 2017 (when current law has a Pease phaseout of itemized deductions and has no haircuts). The calcfunctions.py code makes no attempt to adjust the components for the effects of Pease-like phaseout or any other type of limitation on total itemized deductions, so the pre-2018 tests here use c21060, instead of c04470, as the itemized deductions total. """ # pylint: disable=too-many-locals recs = Records(data=puf_fullsample) # policy1 such that everybody itemizes deductions and all are allowed policy1 = Policy() reform1 = { 'STD_Aged': { year: [0.0, 0.0, 0.0, 0.0, 0.0] }, 'STD': { year: [0.0, 0.0, 0.0, 0.0, 0.0] } } policy1.implement_reform(reform1) assert not policy1.parameter_errors # policy2 such that everybody itemizes deductions but one is disallowed policy2 = Policy() reform2 = { 'STD_Aged': { year: [0.0, 0.0, 0.0, 0.0, 0.0] }, 'STD': { year: [0.0, 0.0, 0.0, 0.0, 0.0] }, hcname: { year: 1.0 } } policy2.implement_reform(reform2) assert not policy2.parameter_errors # compute tax liability in specified year calc1 = Calculator(policy=policy1, records=recs, verbose=False) calc1.advance_to_year(year) calc1.calc_all() calc2 = Calculator(policy=policy2, records=recs, verbose=False) calc2.advance_to_year(year) calc2.calc_all() # confirm that nobody is taking the standard deduction assert np.allclose(calc1.array('standard'), 0.) assert np.allclose(calc2.array('standard'), 0.) # calculate different in total itemized deductions if year == 2017: # pre-Pease limitation total itemized deductions itmded1 = calc1.weighted_total('c21060') * 1e-9 itmded2 = calc2.weighted_total('c21060') * 1e-9 elif year == 2018: # total itemized deductions (no Pease-like limitation) itmded1 = calc1.weighted_total('c04470') * 1e-9 itmded2 = calc2.weighted_total('c04470') * 1e-9 else: raise ValueError('illegal year value = {}'.format(year)) difference_in_total_itmded = itmded1 - itmded2 # calculate itemized component amount component_amt = calc1.weighted_total(cvname) * 1e-9 # confirm that component amount is equal to difference in total deductions if year == 2017 and cvname == 'c19700': atol = 0.009 elif year == 2017 and cvname == 'c19200': atol = 0.010 else: atol = 0.00001 if not np.allclose(component_amt, difference_in_total_itmded, atol=atol): txt = '\n{}={:.3f} != {:.3f}=difference_in_total_itemized_deductions' msg = txt.format(cvname, component_amt, difference_in_total_itmded) raise ValueError(msg)
def test_compatible_data(cps_subsample, puf_subsample, allparams, reform_xx, tc_objs, allparams_batch): """ Test that the compatible_data attribute in policy_current_law.json is accurate by implementing the min and max values of each parameter as reforms and ensuring that revenue differs from baseline when for at least one of these reforms when using datasets marked compatible and does not differ when using datasets marked as incompatible. """ # pylint: disable=too-many-arguments,too-many-locals # pylint: disable=too-many-statements,too-many-branches # Check NPARAMS value assert NPARAMS == len(allparams) # Get taxcalc objects from tc_objs fixture rec_xx, c_xx, puftest = tc_objs # These parameters are exempt because they are not active under # current law and activating them would deactivate other parameters, # or if it is difficult to devise a test for them. exempt_from_testing = [ 'CG_ec', 'CG_reinvest_ec_rt', 'II_prt', 'ID_prt', 'ID_crt', 'CR_SchR_hc', 'ACTC_ChildNum' ] # Loop through the parameters in allparams_batch errmsg = 'ERROR: {} {}\n' errors = '' for pname in allparams_batch: param = allparams_batch[pname] max_listed = param['valid_values']['max'] # handle links to other params or self if isinstance(max_listed, str): if isinstance(allparams[max_listed]['value'][0], list): max_val = allparams[max_listed]['value'][0] else: max_val = float(allparams[max_listed]['value'][0]) else: if isinstance(param['value'][0], list): max_val = [max_listed] * len(param['value'][0]) else: max_val = max_listed min_listed = param['valid_values']['min'] if isinstance(min_listed, str): if isinstance(allparams[min_listed]['value'][0], list): min_val = allparams[min_listed]['value'][0] else: min_val = float(allparams[min_listed]['value'][0]) else: if isinstance(param['value'][0], list): min_val = [min_listed] * len(param['value'][0]) else: min_val = min_listed # create reform dictionaries max_reform = copy.deepcopy(reform_xx) min_reform = copy.deepcopy(reform_xx) max_reform[XX_YEAR][str(pname)] = [max_val] min_reform[XX_YEAR][str(pname)] = [min_val] # assess whether max reform changes results if puftest: rec_yy = Records(data=puf_subsample) else: rec_yy = Records.cps_constructor(data=cps_subsample) p_yy = Policy() p_yy.implement_reform(max_reform, raise_errors=False) c_yy = Calculator(policy=p_yy, records=rec_yy, verbose=False) c_yy.advance_to_year(TEST_YEAR) c_yy.calc_all() if pname.startswith('BEN') and pname.endswith('_repeal'): max_reform_change = ( c_yy.weighted_total('benefit_cost_total') - c_xx.weighted_total('benefit_cost_total') ) else: max_reform_change = ( c_yy.weighted_total('combined') - c_xx.weighted_total('combined') ) min_reform_change = 0 # assess whether min reform changes results, if max reform did not if max_reform_change == 0: p_yy = Policy() p_yy.implement_reform(min_reform, raise_errors=False) c_yy = Calculator(policy=p_yy, records=rec_xx) c_yy.advance_to_year(TEST_YEAR) c_yy.calc_all() if pname.startswith('BEN') and pname.endswith('_repeal'): min_reform_change = ( c_yy.weighted_total('benefit_cost_total') - c_xx.weighted_total('benefit_cost_total') ) else: min_reform_change = ( c_yy.weighted_total('combined') - c_xx.weighted_total('combined') ) if min_reform_change == 0 and pname not in exempt_from_testing: if puftest: if param['compatible_data']['puf'] is True: errors += errmsg.format(pname, 'is not True for puf') else: if param['compatible_data']['cps'] is True: errors += errmsg.format(pname, 'is not True for cps') if max_reform_change != 0 or min_reform_change != 0: if puftest: if param['compatible_data']['puf'] is False: errors += errmsg.format(pname, 'is not False for puf') else: if param['compatible_data']['cps'] is False: errors += errmsg.format(pname, 'is not False for cps') # test failure if any errors if errors: print(errors) assert 'compatible_data' == 'invalid'
def test_compatible_data(cps_subsample, puf_subsample, allparams, reform_xx, tc_objs, allparams_batch): """ Test that the compatible_data attribute in current_law_policy.json is accurate by implementing the min and max values of each parameter as reforms and ensuring that revenue differs from baseline when for at least one of these reforms when using datasets marked compatible and does not differ when using datasets marked as incompatible. """ # pylint: disable=too-many-arguments,too-many-locals # pylint: disable=too-many-statements,too-many-branches # Get taxcalc objects from tc_objs fixture rec_xx, c_xx, puftest = tc_objs # These parameters are exempt because they are not active under # current law and activating them would deactivate other parameters. exempt = ['_CG_ec', '_CG_reinvest_ec_rt'] # Loop through the parameters in allparams_batch errmsg = 'ERROR: {} not {} for {}\n' errors = '' for pname in allparams_batch: param = allparams_batch[pname] max_listed = param['range']['max'] # handle links to other params or self if isinstance(max_listed, six.string_types): if max_listed == 'default': max_val = param['value'][-1] else: max_val = allparams[max_listed]['value'][0] if not isinstance(max_listed, six.string_types): if isinstance(param['value'][0], list): max_val = [max_listed] * len(param['value'][0]) else: max_val = max_listed min_listed = param['range']['min'] if isinstance(min_listed, six.string_types): if min_listed == 'default': min_val = param['value'][-1] else: min_val = allparams[min_listed]['value'][0] if not isinstance(min_listed, six.string_types): if isinstance(param['value'][0], list): min_val = [min_listed] * len(param['value'][0]) else: min_val = min_listed # create reform dictionaries max_reform = copy.deepcopy(reform_xx) min_reform = copy.deepcopy(reform_xx) max_reform[XX_YEAR][str(pname)] = [max_val] min_reform[XX_YEAR][str(pname)] = [min_val] # assess whether max reform changes results if puftest: rec_yy = Records(data=puf_subsample) else: rec_yy = Records.cps_constructor(data=cps_subsample) p_yy = Policy() p_yy.implement_reform(max_reform) c_yy = Calculator(policy=p_yy, records=rec_yy, verbose=False) c_yy.advance_to_year(TEST_YEAR) c_yy.calc_all() max_reform_change = (c_yy.weighted_total('combined') - c_xx.weighted_total('combined')) min_reform_change = 0 # assess whether min reform changes results, if max reform did not if max_reform_change == 0: p_yy = Policy() p_yy.implement_reform(min_reform) c_yy = Calculator(policy=p_yy, records=rec_xx) c_yy.advance_to_year(TEST_YEAR) c_yy.calc_all() min_reform_change = (c_yy.weighted_total('combined') - c_xx.weighted_total('combined')) if min_reform_change == 0 and pname not in exempt: if puftest: if param['compatible_data']['puf'] is not False: errors += errmsg.format(pname, 'False', 'puf') else: if param['compatible_data']['cps'] is not False: errors += errmsg.format(pname, 'False', 'cps') if max_reform_change != 0 or min_reform_change != 0: if puftest: if param['compatible_data']['puf'] is not True: errors += errmsg.format(pname, 'True', 'puf') else: if param['compatible_data']['cps'] is not True: errors += errmsg.format(pname, 'True', 'cps') # test failure if any errors if errors: print(errors) assert 'compatible_data' == 'invalid'
def test_compatible_data(cps_subsample, puf_subsample, allparams, reform_xx, tc_objs, allparams_batch): """ Test that the compatible_data attribute in current_law_policy.json is accurate by implementing the min and max values of each parameter as reforms and ensuring that revenue differs from baseline when for at least one of these reforms when using datasets marked compatible and does not differ when using datasets marked as incompatible. """ # pylint: disable=too-many-arguments,too-many-locals # pylint: disable=too-many-statements,too-many-branches # Get taxcalc objects from tc_objs fixture rec_xx, c_xx, puftest = tc_objs # These parameters are exempt because they are not active under # current law and activating them would deactivate other parameters, # or if it is difficult to devise a test for them. exempt_from_testing = ['_CG_ec', '_CG_reinvest_ec_rt'] """ # TODO: see if can remove this comment exempt_from_testing.append('_ACTC_ChildNum') # above statement added in PR#203? to silence the following FALSE error: # ERROR: _ACTC_ChildNum is not True for cps """ """ # TODO: remove this comment after puf.csv is fixed to include DSI=1 funits exempt_from_testing.append('_STD_Dep') # above added in PR#2008 to silence this false error: # ERROR: _STD_Dep is not True for puf """ # Loop through the parameters in allparams_batch errmsg = 'ERROR: {} {}\n' errors = '' for pname in allparams_batch: param = allparams_batch[pname] max_listed = param['range']['max'] # handle links to other params or self if isinstance(max_listed, six.string_types): if max_listed == 'default': max_val = param['value'][-1] else: max_val = allparams[max_listed]['value'][0] if not isinstance(max_listed, six.string_types): if isinstance(param['value'][0], list): max_val = [max_listed] * len(param['value'][0]) else: max_val = max_listed min_listed = param['range']['min'] if isinstance(min_listed, six.string_types): if min_listed == 'default': min_val = param['value'][-1] else: min_val = allparams[min_listed]['value'][0] if not isinstance(min_listed, six.string_types): if isinstance(param['value'][0], list): min_val = [min_listed] * len(param['value'][0]) else: min_val = min_listed # create reform dictionaries max_reform = copy.deepcopy(reform_xx) min_reform = copy.deepcopy(reform_xx) max_reform[XX_YEAR][str(pname)] = [max_val] min_reform[XX_YEAR][str(pname)] = [min_val] # assess whether max reform changes results if puftest: rec_yy = Records(data=puf_subsample) else: rec_yy = Records.cps_constructor(data=cps_subsample) p_yy = Policy() p_yy.implement_reform(max_reform, raise_errors=False) c_yy = Calculator(policy=p_yy, records=rec_yy, verbose=False) c_yy.advance_to_year(TEST_YEAR) c_yy.calc_all() if pname.startswith('_BEN') and pname.endswith('_repeal'): max_reform_change = (c_yy.weighted_total('benefit_cost_total') - c_xx.weighted_total('benefit_cost_total')) else: max_reform_change = (c_yy.weighted_total('combined') - c_xx.weighted_total('combined')) min_reform_change = 0 # assess whether min reform changes results, if max reform did not if max_reform_change == 0: p_yy = Policy() p_yy.implement_reform(min_reform, raise_errors=False) c_yy = Calculator(policy=p_yy, records=rec_xx) c_yy.advance_to_year(TEST_YEAR) c_yy.calc_all() if pname.startswith('_BEN') and pname.endswith('_repeal'): min_reform_change = ( c_yy.weighted_total('benefit_cost_total') - c_xx.weighted_total('benefit_cost_total')) else: min_reform_change = (c_yy.weighted_total('combined') - c_xx.weighted_total('combined')) if min_reform_change == 0 and pname not in exempt_from_testing: if puftest: if param['compatible_data']['puf'] is True: errors += errmsg.format(pname, 'is not True for puf') else: if param['compatible_data']['cps'] is True: errors += errmsg.format(pname, 'is not True for cps') if max_reform_change != 0 or min_reform_change != 0: if puftest: if param['compatible_data']['puf'] is False: errors += errmsg.format(pname, 'is not False for puf') else: if param['compatible_data']['cps'] is False: errors += errmsg.format(pname, 'is not False for cps') # test failure if any errors if errors: print(errors) assert 'compatible_data' == 'invalid'
def test_itemded_component_amounts(year, cvname, hcname, puf_fullsample): """ Check that all c04470 components are adjusted to reflect the filing unit's standard-vs-itemized-deduction decision. Check for 2018 (when current law has no Pease phaseout of itemized deductions and already has complete haircuts for Casualty and Miscellaneous deductions) and 2017 (when current law has a Pease phaseout of itemized deductions and has no haircuts). The calcfunctions.py code makes no attempt to adjust the components for the effects of Pease-like phaseout or any other type of limitation on total itemized deductions, so the pre-2018 tests here use c21060, instead of c04470, as the itemized deductions total. """ # pylint: disable=too-many-locals recs = Records(data=puf_fullsample) # policy1 such that everybody itemizes deductions and all are allowed policy1 = Policy() reform1 = { 'STD_Aged': {year: [0.0, 0.0, 0.0, 0.0, 0.0]}, 'STD': {year: [0.0, 0.0, 0.0, 0.0, 0.0]} } policy1.implement_reform(reform1) assert not policy1.parameter_errors # policy2 such that everybody itemizes deductions but one is disallowed policy2 = Policy() reform2 = { 'STD_Aged': {year: [0.0, 0.0, 0.0, 0.0, 0.0]}, 'STD': {year: [0.0, 0.0, 0.0, 0.0, 0.0]}, hcname: {year: 1.0} } policy2.implement_reform(reform2) assert not policy2.parameter_errors # compute tax liability in specified year calc1 = Calculator(policy=policy1, records=recs, verbose=False) calc1.advance_to_year(year) calc1.calc_all() calc2 = Calculator(policy=policy2, records=recs, verbose=False) calc2.advance_to_year(year) calc2.calc_all() # confirm that nobody is taking the standard deduction assert np.allclose(calc1.array('standard'), 0.) assert np.allclose(calc2.array('standard'), 0.) # calculate different in total itemized deductions if year == 2017: # pre-Pease limitation total itemized deductions itmded1 = calc1.weighted_total('c21060') * 1e-9 itmded2 = calc2.weighted_total('c21060') * 1e-9 elif year == 2018: # total itemized deductions (no Pease-like limitation) itmded1 = calc1.weighted_total('c04470') * 1e-9 itmded2 = calc2.weighted_total('c04470') * 1e-9 else: raise ValueError('illegal year value = {}'.format(year)) difference_in_total_itmded = itmded1 - itmded2 # calculate itemized component amount component_amt = calc1.weighted_total(cvname) * 1e-9 # confirm that component amount is equal to difference in total deductions if year == 2017 and cvname == 'c19700': atol = 0.009 elif year == 2017 and cvname == 'c19200': atol = 0.010 else: atol = 0.00001 if not np.allclose(component_amt, difference_in_total_itmded, atol=atol): txt = '\n{}={:.3f} != {:.3f}=difference_in_total_itemized_deductions' msg = txt.format(cvname, component_amt, difference_in_total_itmded) raise ValueError(msg)