def test_init_errors(reformfile0, assumpfile0, year, asm, gdr): """ Ensure error messages generated by TaxCalcIO.init method. """ recdict = {'RECID': 1, 'MARS': 1, 'e00300': 100000, 's006': 1e8} recdf = pd.DataFrame(data=recdict, index=[0]) if asm == 'assumpfile0': assump = assumpfile0.name else: assump = asm if gdr == 'has_gdiff_response': gdiff_response = Growdiff() gdiff_response.update_growdiff({2015: {"_ABOOK": [-0.01]}}) else: gdiff_response = gdr tcio = TaxCalcIO(input_data=recdf, tax_year=year, reform=reformfile0.name, assump=assump) assert len(tcio.errmsg) == 0 tcio.init(input_data=recdf, tax_year=year, reform=reformfile0.name, assump=assump, growdiff_response=gdiff_response, aging_input_data=False, exact_calculations=False) assert len(tcio.errmsg) > 0
def test_response_json(tests_path): """ Check that each JSON file can be converted into dictionaries that can be used to construct objects needed for a Calculator object. """ responses_path = os.path.join(tests_path, '..', 'responses', '*.json') for jpf in glob.glob(responses_path): # read contents of jpf (JSON parameter filename) jfile = open(jpf, 'r') jpf_text = jfile.read() # check that jpf_text can be used to construct objects response_file = ('"consumption"' in jpf_text and '"behavior"' in jpf_text and '"growdiff_baseline"' in jpf_text and '"growdiff_response"' in jpf_text) if response_file: # pylint: disable=protected-access (con, beh, gdiff_base, gdiff_resp) = Calculator._read_json_econ_assump_text(jpf_text) cons = Consumption() cons.update_consumption(con) behv = Behavior() behv.update_behavior(beh) growdiff_baseline = Growdiff() growdiff_baseline.update_growdiff(gdiff_base) growdiff_response = Growdiff() growdiff_response.update_growdiff(gdiff_resp) else: # jpf_text is not a valid JSON response assumption file print('test-failing-filename: ' + jpf) assert False
def test_taxbrain_json(taxbrain_path): # pylint: disable=redefined-outer-name """ Check that each JSON parameter file can be converted into dictionaries that can be used to construct objects needed for a Calculator object. """ for jpf in glob.glob(taxbrain_path): # read contents of jpf (JSON parameter filename) jfile = open(jpf, 'r') jpf_text = jfile.read() # check that jpf_text can be used to construct objects if '"policy"' in jpf_text: pol = Calculator.read_json_policy_reform_text(jpf_text) policy = Policy() policy.implement_reform(pol) elif ('"consumption"' in jpf_text and '"behavior"' in jpf_text and '"growdiff_baseline"' in jpf_text and '"growdiff_response"' in jpf_text): (con, beh, gdiff_base, gdiff_resp) = Calculator.read_json_econ_assump_text(jpf_text) cons = Consumption() cons.update_consumption(con) behv = Behavior() behv.update_behavior(beh) growdiff_baseline = Growdiff() growdiff_baseline.update_growdiff(gdiff_base) growdiff_response = Growdiff() growdiff_response.update_growdiff(gdiff_resp) else: # jpf_text is not a valid JSON parameter file print('test-failing-filename: ' + jpf) assert False
def test_creation_with_aging(rawinputfile, reformfile0): """ Test TaxCalcIO instantiation with/without no policy reform and with aging. """ taxyear = 2021 tcio = TaxCalcIO(input_data=rawinputfile.name, tax_year=taxyear, reform=reformfile0.name, assump=None) assert len(tcio.errmsg) == 0 tcio.init(input_data=rawinputfile.name, tax_year=taxyear, reform=reformfile0.name, assump=None, growdiff_response=Growdiff(), aging_input_data=True, exact_calculations=False) assert len(tcio.errmsg) == 0 assert tcio.tax_year() == taxyear taxyear = 2016 tcio = TaxCalcIO(input_data=rawinputfile.name, tax_year=taxyear, reform=None, assump=None) assert len(tcio.errmsg) == 0 tcio.init(input_data=rawinputfile.name, tax_year=taxyear, reform=None, assump=None, growdiff_response=None, aging_input_data=True, exact_calculations=False) assert len(tcio.errmsg) == 0 assert tcio.tax_year() == taxyear
def test_init_errors(reformfile0, reformfilex1, reformfilex2, baselinebad, assumpfile0, year, base, ref, asm, gdr): """ Ensure error messages generated by TaxCalcIO.init method. """ # pylint: disable=too-many-arguments,too-many-locals recdict = {'RECID': 1, 'MARS': 1, 'e00300': 100000, 's006': 1e8} recdf = pd.DataFrame(data=recdict, index=[0]) if base == 'reformfile0': baseline = reformfile0.name elif base == 'baselinebad': baseline = baselinebad.name else: baseline = base if ref == 'reformfile0': reform = reformfile0.name elif ref == 'reformfilex1': reform = reformfilex1.name elif ref == 'reformfilex2': # specify compound reform reform = '{}+{}'.format(reformfilex1.name, reformfilex2.name) else: reform = ref if asm == 'assumpfile0': assump = assumpfile0.name else: assump = asm if gdr == 'has_gdiff_response': gdiff_response = Growdiff() gdiff_response.update_growdiff({2015: {"_ABOOK": [-0.01]}}) else: gdiff_response = gdr tcio = TaxCalcIO(input_data=recdf, tax_year=year, baseline=baseline, reform=reform, assump=assump) assert not tcio.errmsg tcio.init(input_data=recdf, tax_year=year, baseline=baseline, reform=reform, assump=assump, growdiff_response=gdiff_response, aging_input_data=False, exact_calculations=False) assert tcio.errmsg
def test_has_any_response(): syr = 2014 gdiff = Growdiff(start_year=syr) assert gdiff.has_any_response() is False gdiff.update_growdiff({2020: {'_AWAGE': [0.01]}}) assert gdiff.current_year == syr assert gdiff.has_any_response() is True
def test_chained_cpi_reform(offset): """ Test that _cpi_offset policy parameter works as expected. """ # specify reform without using cpi_offset parameter pem = 10000 bare_reform = {2022: {'_II_em': [pem]}} with_reform = {2022: {'_II_em': [pem]}, 2020: {'_cpi_offset': [offset]}} syr = Policy.JSON_START_YEAR pol_bare = Policy(start_year=syr) pol_bare.implement_reform(bare_reform) pol_with = Policy(start_year=syr) pol_with.implement_reform(with_reform) # check relative _II_em values in the two reforms for several years pem_bare = pol_bare._II_em pem_with = pol_with._II_em if offset == 0: assert pem_with[2019 - syr] == pem_bare[2019 - syr] assert pem_with[2020 - syr] == pem_bare[2020 - syr] assert pem_with[2021 - syr] == pem_bare[2021 - syr] assert pem_with[2022 - syr] == pem assert pem_bare[2022 - syr] == pem assert pem_with[2023 - syr] == pem_bare[2023 - syr] elif offset < 0: assert pem_with[2019 - syr] == pem_bare[2019 - syr] assert pem_with[2020 - syr] == pem_bare[2020 - syr] assert pem_with[2021 - syr] < pem_bare[2021 - syr] assert pem_with[2022 - syr] == pem assert pem_bare[2022 - syr] == pem assert pem_with[2023 - syr] < pem_bare[2023 - syr] # check exact _II_em values for 2023, which are # equal to 2022 values indexed by 2022 inflation rates unchained_cpi = pol_bare.inflation_rates()[2022 - syr] assert pem_bare[2023 - syr] == round(pem * (1 + unchained_cpi), 2) chained_cpi = unchained_cpi + offset assert pem_with[2023 - syr] == round(pem * (1 + chained_cpi), 2) # check that _STD value for 2023 with chained CPI indexing is # equal to _STD value for 2023 when specifying chained CPI indexing # as a difference in assumed inflation rates if offset != 0: # ... compute _STD value using difference-in-growth-factors approach growfactors = Growfactors() growdiff = Growdiff() growdiff.update_growdiff({2020: {'_ACPIU': [offset]}}) growdiff.apply_to(growfactors) pol = Policy(gfactors=growfactors, start_year=syr) pol.implement_reform(bare_reform) # ... compare the _STD values derived from the two approaches assert_allclose(pol_with._STD[2023 - syr], pol._STD[2023 - syr], atol=0.01, rtol=0.0)
def test_update_and_apply_growdiff(): syr = 2013 nyrs = 5 lyr = syr + nyrs - 1 gdiff = Growdiff(start_year=syr, num_years=nyrs) # update Growdiff instance diffs = {2014: {'_AWAGE': [0.01]}, 2016: {'_AWAGE': [0.02]}} gdiff.update_growdiff(diffs) expected_wage_diffs = [0.00, 0.01, 0.01, 0.02, 0.02] assert_allclose(gdiff._AWAGE, expected_wage_diffs, atol=0.0, rtol=0.0) # apply growdiff to Growfactors instance gf = Growfactors() pir_pre = gf.price_inflation_rates(syr, lyr) wgr_pre = gf.wage_growth_rates(syr, lyr) gfactors = Growfactors() gdiff.apply_to(gfactors) pir_pst = gfactors.price_inflation_rates(syr, lyr) wgr_pst = gfactors.wage_growth_rates(syr, lyr) expected_wgr_pst = [ wgr_pre[i] + expected_wage_diffs[i] for i in range(0, nyrs) ] assert_allclose(pir_pre, pir_pst, atol=0.0, rtol=0.0) assert_allclose(wgr_pst, expected_wgr_pst, atol=1.0e-9, rtol=0.0)
def reform_warnings_errors(user_mods): """ The reform_warnings_errors function assumes user_mods is a dictionary returned by the Calculator.read_json_param_objects() function. This function returns a dictionary containing two STR:STR pairs: {'warnings': '<empty-or-message(s)>', 'errors': '<empty-or-message(s)>'} In each pair the second string is empty if there are no messages. Any returned messages are generated using current_law_policy.json information on known policy parameter names and parameter value ranges. Note that this function will return one or more error messages if the user_mods['policy'] dictionary contains any unknown policy parameter names or if any *_cpi parameters have values other than True or False. These situations prevent implementing the policy reform specified in user_mods, and therefore, no range-related warnings or errors will be returned in this case. """ rtn_dict = {'warnings': '', 'errors': ''} # create Growfactors object gdiff_baseline = Growdiff() gdiff_baseline.update_growdiff(user_mods['growdiff_baseline']) gdiff_response = Growdiff() gdiff_response.update_growdiff(user_mods['growdiff_response']) growfactors = Growfactors() gdiff_baseline.apply_to(growfactors) gdiff_response.apply_to(growfactors) # create Policy object and implement reform pol = Policy(gfactors=growfactors) try: pol.implement_reform(user_mods['policy']) rtn_dict['warnings'] = pol.reform_warnings rtn_dict['errors'] = pol.reform_errors except ValueError as valerr_msg: rtn_dict['errors'] = valerr_msg.__str__() return rtn_dict
def test_correct_but_not_useful_growdiff_ctor(): gdiff = Growdiff(growdiff_dict={}) assert gdiff
def test_incorrect_growdiff_ctor(): with pytest.raises(ValueError): gdiff = Growdiff(growdiff_dict=list()) with pytest.raises(ValueError): gdiff = Growdiff(num_years=0)
def calculate_baseline_and_reform(year_n, start_year, taxrec_df, user_mods): """ calculate_baseline_and_reform function assumes specified user_mods is a dictionary returned by the Calculator.read_json_parameter_files() function with an extra key:value pair that is specified as 'gdp_elasticity': {'value': <float_value>}. """ # pylint: disable=too-many-locals,too-many-branches,too-many-statements check_user_mods(user_mods) # Specify Consumption instance consump = Consumption() consump_assumptions = user_mods['consumption'] consump.update_consumption(consump_assumptions) # Specify growdiff_baseline and growdiff_response growdiff_baseline = Growdiff() growdiff_response = Growdiff() growdiff_base_assumps = user_mods['growdiff_baseline'] growdiff_resp_assumps = user_mods['growdiff_response'] growdiff_baseline.update_growdiff(growdiff_base_assumps) growdiff_response.update_growdiff(growdiff_resp_assumps) # Create pre-reform and post-reform Growfactors instances growfactors_pre = Growfactors() growdiff_baseline.apply_to(growfactors_pre) growfactors_post = Growfactors() growdiff_baseline.apply_to(growfactors_post) growdiff_response.apply_to(growfactors_post) # Create pre-reform Calculator instance recs1 = Records(data=taxrec_df.copy(deep=True), gfactors=growfactors_pre) policy1 = Policy(gfactors=growfactors_pre) calc1 = Calculator(policy=policy1, records=recs1, consumption=consump) while calc1.current_year < start_year: calc1.increment_year() calc1.calc_all() assert calc1.current_year == start_year # Create pre-reform Calculator instance with extra income recs1p = Records(data=taxrec_df.copy(deep=True), gfactors=growfactors_pre) # add one dollar to total wages and salaries of each filing unit recs1p.e00200 += 1.0 # pylint: disable=no-member policy1p = Policy(gfactors=growfactors_pre) calc1p = Calculator(policy=policy1p, records=recs1p, consumption=consump) while calc1p.current_year < start_year: calc1p.increment_year() calc1p.calc_all() assert calc1p.current_year == start_year # Construct mask to show which of the calc1 and calc1p results differ soit1 = results(calc1) soit1p = results(calc1p) mask = (soit1._iitax != soit1p._iitax) # pylint: disable=protected-access # Specify Behavior instance behv = Behavior() behavior_assumps = user_mods['behavior'] behv.update_behavior(behavior_assumps) # Prevent both behavioral response and growdiff response if behv.has_any_response() and growdiff_response.has_any_response(): msg = 'BOTH behavior AND growdiff_response HAVE RESPONSE' raise ValueError(msg) # Create post-reform Calculator instance with behavior recs2 = Records(data=taxrec_df.copy(deep=True), gfactors=growfactors_post) policy2 = Policy(gfactors=growfactors_post) policy_reform = user_mods['policy'] policy2.implement_reform(policy_reform) calc2 = Calculator(policy=policy2, records=recs2, consumption=consump, behavior=behv) while calc2.current_year < start_year: calc2.increment_year() calc2.calc_all() assert calc2.current_year == start_year # Seed random number generator with a seed value based on user_mods seed = random_seed(user_mods) print('seed={}'.format(seed)) np.random.seed(seed) # pylint: disable=no-member # Increment Calculator objects for year_n years and calculate for _ in range(0, year_n): calc1.increment_year() calc2.increment_year() calc1.calc_all() if calc2.behavior.has_response(): calc2 = Behavior.response(calc1, calc2) else: calc2.calc_all() # Return calculated results and mask soit1 = results(calc1) soit2 = results(calc2) return soit1, soit2, mask
def calculate(year_n, start_year, use_puf_not_cps, use_full_sample, user_mods, behavior_allowed): """ The calculate function assumes the specified user_mods is a dictionary returned by the Calculator.read_json_param_objects() function. The function returns (calc1, calc2, mask) where calc1 is pre-reform Calculator object calculated for year_n, calc2 is post-reform Calculator object calculated for year_n, and mask is boolean array marking records with reform-induced iitax diffs Set behavior_allowed to False when generating static results or set behavior_allowed to True when generating dynamic results. """ # pylint: disable=too-many-arguments,too-many-locals # pylint: disable=too-many-branches,too-many-statements check_user_mods(user_mods) # specify Consumption instance consump = Consumption() consump_assumptions = user_mods['consumption'] consump.update_consumption(consump_assumptions) # specify growdiff_baseline and growdiff_response growdiff_baseline = Growdiff() growdiff_response = Growdiff() growdiff_base_assumps = user_mods['growdiff_baseline'] growdiff_resp_assumps = user_mods['growdiff_response'] growdiff_baseline.update_growdiff(growdiff_base_assumps) growdiff_response.update_growdiff(growdiff_resp_assumps) # create pre-reform and post-reform Growfactors instances growfactors_pre = Growfactors() growdiff_baseline.apply_to(growfactors_pre) growfactors_post = Growfactors() growdiff_baseline.apply_to(growfactors_post) growdiff_response.apply_to(growfactors_post) # create sample pd.DataFrame from specified input file and sampling scheme stime = time.time() tbi_path = os.path.abspath(os.path.dirname(__file__)) if use_puf_not_cps: # first try TaxBrain deployment path input_path = 'puf.csv.gz' if not os.path.isfile(input_path): # otherwise try local Tax-Calculator deployment path input_path = os.path.join(tbi_path, '..', '..', 'puf.csv') sampling_frac = 0.05 sampling_seed = 180 else: # if using cps input not puf input # first try Tax-Calculator code path input_path = os.path.join(tbi_path, '..', 'cps.csv.gz') if not os.path.isfile(input_path): # otherwise read from taxcalc package "egg" input_path = None # pragma: no cover full_sample = read_egg_csv('cps.csv.gz') # pragma: no cover sampling_frac = 0.03 sampling_seed = 180 if input_path: full_sample = pd.read_csv(input_path) if use_full_sample: sample = full_sample else: sample = full_sample.sample( # pylint: disable=no-member frac=sampling_frac, random_state=sampling_seed) if use_puf_not_cps: print('puf-read-time= {:.1f}'.format(time.time() - stime)) else: print('cps-read-time= {:.1f}'.format(time.time() - stime)) # create pre-reform Calculator instance if use_puf_not_cps: recs1 = Records(data=copy.deepcopy(sample), gfactors=growfactors_pre) else: recs1 = Records.cps_constructor(data=copy.deepcopy(sample), gfactors=growfactors_pre) policy1 = Policy(gfactors=growfactors_pre) calc1 = Calculator(policy=policy1, records=recs1, consumption=consump) while calc1.current_year < start_year: calc1.increment_year() calc1.calc_all() assert calc1.current_year == start_year # compute mask array res1 = calc1.dataframe(DIST_VARIABLES) if use_puf_not_cps: # create pre-reform Calculator instance with extra income recs1p = Records(data=copy.deepcopy(sample), gfactors=growfactors_pre) # add one dollar to the income of each filing unit to determine # which filing units undergo a resulting change in tax liability recs1p.e00200 += 1.0 # pylint: disable=no-member recs1p.e00200p += 1.0 # pylint: disable=no-member policy1p = Policy(gfactors=growfactors_pre) # create Calculator with recs1p and calculate for start_year calc1p = Calculator(policy=policy1p, records=recs1p, consumption=consump) while calc1p.current_year < start_year: calc1p.increment_year() calc1p.calc_all() assert calc1p.current_year == start_year # compute mask showing which of the calc1 and calc1p results differ; # mask is true if a filing unit's income tax liability changed after # a dollar was added to the filing unit's wage and salary income res1p = calc1p.dataframe(DIST_VARIABLES) mask = np.logical_not( # pylint: disable=no-member np.isclose(res1.iitax, res1p.iitax, atol=0.001, rtol=0.0)) assert np.any(mask) else: # if use_cps_not_cps is False # indicate that no fuzzing of reform results is required mask = np.zeros(res1.shape[0], dtype=np.int8) # specify Behavior instance behv = Behavior() behavior_assumps = user_mods['behavior'] behv.update_behavior(behavior_assumps) # always prevent both behavioral response and growdiff response if behv.has_any_response() and growdiff_response.has_any_response(): msg = 'BOTH behavior AND growdiff_response HAVE RESPONSE' raise ValueError(msg) # optionally prevent behavioral response if behv.has_any_response() and not behavior_allowed: msg = 'A behavior RESPONSE IS NOT ALLOWED' raise ValueError(msg) # create post-reform Calculator instance if use_puf_not_cps: recs2 = Records(data=copy.deepcopy(sample), gfactors=growfactors_post) else: recs2 = Records.cps_constructor(data=copy.deepcopy(sample), gfactors=growfactors_post) policy2 = Policy(gfactors=growfactors_post) policy_reform = user_mods['policy'] policy2.implement_reform(policy_reform) calc2 = Calculator(policy=policy2, records=recs2, consumption=consump, behavior=behv) while calc2.current_year < start_year: calc2.increment_year() calc2.calc_all() assert calc2.current_year == start_year # increment Calculator objects for year_n years and calculate for _ in range(0, year_n): calc1.increment_year() calc2.increment_year() calc1.calc_all() if calc2.behavior_has_response(): calc2 = Behavior.response(calc1, calc2) else: calc2.calc_all() # return calculated Calculator objects and mask return (calc1, calc2, mask)
def calculate(year_n, start_year, use_puf_not_cps, use_full_sample, user_mods, behavior_allowed): """ The calculate function assumes the specified user_mods is a dictionary returned by the Calculator.read_json_param_objects() function. The function returns (calc1, calc2) where calc1 is pre-reform Calculator object calculated for year_n, and calc2 is post-reform Calculator object calculated for year_n. Set behavior_allowed to False when generating static results or set behavior_allowed to True when generating dynamic results. """ # pylint: disable=too-many-arguments,too-many-locals # pylint: disable=too-many-branches,too-many-statements check_user_mods(user_mods) # specify Consumption instance consump = Consumption() consump_assumptions = user_mods['consumption'] consump.update_consumption(consump_assumptions) # specify growdiff_baseline and growdiff_response growdiff_baseline = Growdiff() growdiff_response = Growdiff() growdiff_base_assumps = user_mods['growdiff_baseline'] growdiff_resp_assumps = user_mods['growdiff_response'] growdiff_baseline.update_growdiff(growdiff_base_assumps) growdiff_response.update_growdiff(growdiff_resp_assumps) # create pre-reform and post-reform Growfactors instances growfactors_pre = Growfactors() growdiff_baseline.apply_to(growfactors_pre) growfactors_post = Growfactors() growdiff_baseline.apply_to(growfactors_post) growdiff_response.apply_to(growfactors_post) # create sample pd.DataFrame from specified input file and sampling scheme stime = time.time() tbi_path = os.path.abspath(os.path.dirname(__file__)) if use_puf_not_cps: # first try TaxBrain deployment path input_path = 'puf.csv.gz' if not os.path.isfile(input_path): # otherwise try local Tax-Calculator deployment path input_path = os.path.join(tbi_path, '..', '..', 'puf.csv') sampling_frac = 0.05 sampling_seed = 180 else: # if using cps input not puf input # first try Tax-Calculator code path input_path = os.path.join(tbi_path, '..', 'cps.csv.gz') if not os.path.isfile(input_path): # otherwise read from taxcalc package "egg" input_path = None # pragma: no cover full_sample = read_egg_csv('cps.csv.gz') # pragma: no cover sampling_frac = 0.03 sampling_seed = 180 if input_path: full_sample = pd.read_csv(input_path) if use_full_sample: sample = full_sample else: sample = full_sample.sample( # pylint: disable=no-member frac=sampling_frac, random_state=sampling_seed) if use_puf_not_cps: print('puf-read-time= {:.1f}'.format(time.time() - stime)) else: print('cps-read-time= {:.1f}'.format(time.time() - stime)) # create pre-reform Calculator instance if use_puf_not_cps: recs1 = Records(data=sample, gfactors=growfactors_pre) else: recs1 = Records.cps_constructor(data=sample, gfactors=growfactors_pre) policy1 = Policy(gfactors=growfactors_pre) calc1 = Calculator(policy=policy1, records=recs1, consumption=consump) while calc1.current_year < start_year: calc1.increment_year() calc1.calc_all() assert calc1.current_year == start_year # specify Behavior instance behv = Behavior() behavior_assumps = user_mods['behavior'] behv.update_behavior(behavior_assumps) # always prevent both behavioral response and growdiff response if behv.has_any_response() and growdiff_response.has_any_response(): msg = 'BOTH behavior AND growdiff_response HAVE RESPONSE' raise ValueError(msg) # optionally prevent behavioral response if behv.has_any_response() and not behavior_allowed: msg = 'A behavior RESPONSE IS NOT ALLOWED' raise ValueError(msg) # create post-reform Calculator instance if use_puf_not_cps: recs2 = Records(data=sample, gfactors=growfactors_post) else: recs2 = Records.cps_constructor(data=sample, gfactors=growfactors_post) policy2 = Policy(gfactors=growfactors_post) policy_reform = user_mods['policy'] policy2.implement_reform(policy_reform) calc2 = Calculator(policy=policy2, records=recs2, consumption=consump, behavior=behv) while calc2.current_year < start_year: calc2.increment_year() assert calc2.current_year == start_year # delete objects now embedded in calc1 and calc2 del sample del full_sample del consump del growdiff_baseline del growdiff_response del growfactors_pre del growfactors_post del behv del recs1 del recs2 del policy1 del policy2 # increment Calculator objects for year_n years and calculate for _ in range(0, year_n): calc1.increment_year() calc2.increment_year() calc1.calc_all() if calc2.behavior_has_response(): calc2 = Behavior.response(calc1, calc2) else: calc2.calc_all() # return calculated Calculator objects return (calc1, calc2)
afile.write(contents) afile.close() # must close and then yield for Windows platform yield afile if os.path.isfile(afile.name): try: os.remove(afile.name) except OSError: pass # sometimes we can't remove a generated temporary file # for fixture args, pylint: disable=redefined-outer-name @pytest.mark.parametrize("year, asm, gdr", [ (2000, 'assumpfile0', Growdiff()), (2099, None, None), (2020, None, dict()), (2020, 'assumpfile0', 'has_gdiff_response'), ]) def test_init_errors(reformfile0, assumpfile0, year, asm, gdr): """ Ensure error messages generated by TaxCalcIO.init method. """ recdict = {'RECID': 1, 'MARS': 1, 'e00300': 100000, 's006': 1e8} recdf = pd.DataFrame(data=recdict, index=[0]) if asm == 'assumpfile0': assump = assumpfile0.name else: assump = asm if gdr == 'has_gdiff_response':
]) def test_ctor_errors(input_data, baseline, reform, assump, outdir): """ Ensure error messages are generated by TaxCalcIO.__init__. """ tcio = TaxCalcIO(input_data=input_data, tax_year=2013, baseline=baseline, reform=reform, assump=assump, outdir=outdir) assert tcio.errmsg @pytest.mark.parametrize('year, base, ref, asm, gdr', [ (2000, 'reformfile0', 'reformfile0', 'assumpfile0', Growdiff()), (2099, 'reformfile0', 'reformfile0', None, None), (2020, 'reformfile0', 'reformfile0', None, dict()), (2020, 'reformfile0', 'reformfile0', 'assumpfile0', 'has_gdiff_response'), (2020, 'reformfile0', 'reformfilex1', 'assumpfile0', 'has_gdiff_response'), (2020, 'reformfile0', 'reformfilex2', 'assumpfile0', 'has_gdiff_response'), (2020, 'baselinebad', 'reformfile0', 'assumpfile0', 'has_gdiff_response') ]) def test_init_errors(reformfile0, reformfilex1, reformfilex2, baselinebad, assumpfile0, year, base, ref, asm, gdr): """ Ensure error messages generated by TaxCalcIO.init method. """ # pylint: disable=too-many-arguments,too-many-locals recdict = {'RECID': 1, 'MARS': 1, 'e00300': 100000, 's006': 1e8} recdf = pd.DataFrame(data=recdict, index=[0])
def dropq_calculate(year_n, start_year, taxrec_df, user_mods, behavior_allowed, mask_computed): """ The dropq_calculate function assumes specified user_mods is a dictionary returned by the Calculator.read_json_parameter_files() function with an extra key:value pair that is specified as 'gdp_elasticity': {'value': <float_value>}. The function returns (calc1, calc2, mask) where calc1 is pre-reform Calculator object calculated for year_n, calc2 is post-reform Calculator object calculated for year_n, and mask is boolean array if compute_mask=True or None otherwise """ # pylint: disable=too-many-arguments,too-many-locals,too-many-statements check_user_mods(user_mods) # specify Consumption instance consump = Consumption() consump_assumptions = user_mods['consumption'] consump.update_consumption(consump_assumptions) # specify growdiff_baseline and growdiff_response growdiff_baseline = Growdiff() growdiff_response = Growdiff() growdiff_base_assumps = user_mods['growdiff_baseline'] growdiff_resp_assumps = user_mods['growdiff_response'] growdiff_baseline.update_growdiff(growdiff_base_assumps) growdiff_response.update_growdiff(growdiff_resp_assumps) # create pre-reform and post-reform Growfactors instances growfactors_pre = Growfactors() growdiff_baseline.apply_to(growfactors_pre) growfactors_post = Growfactors() growdiff_baseline.apply_to(growfactors_post) growdiff_response.apply_to(growfactors_post) # create pre-reform Calculator instance recs1 = Records(data=taxrec_df.copy(deep=True), gfactors=growfactors_pre) policy1 = Policy(gfactors=growfactors_pre) calc1 = Calculator(policy=policy1, records=recs1, consumption=consump) while calc1.current_year < start_year: calc1.increment_year() calc1.calc_all() assert calc1.current_year == start_year # optionally compute mask if mask_computed: # create pre-reform Calculator instance with extra income recs1p = Records(data=taxrec_df.copy(deep=True), gfactors=growfactors_pre) # add one dollar to total wages and salaries of each filing unit recs1p.e00200 += 1.0 # pylint: disable=no-member recs1p.e00200p += 1.0 # pylint: disable=no-member policy1p = Policy(gfactors=growfactors_pre) # create Calculator with recs1p and calculate for start_year calc1p = Calculator(policy=policy1p, records=recs1p, consumption=consump) while calc1p.current_year < start_year: calc1p.increment_year() calc1p.calc_all() assert calc1p.current_year == start_year # compute mask that shows which of the calc1 and calc1p results differ res1 = results(calc1.records) res1p = results(calc1p.records) mask = (res1.iitax != res1p.iitax) else: mask = None # specify Behavior instance behv = Behavior() behavior_assumps = user_mods['behavior'] behv.update_behavior(behavior_assumps) # always prevent both behavioral response and growdiff response if behv.has_any_response() and growdiff_response.has_any_response(): msg = 'BOTH behavior AND growdiff_response HAVE RESPONSE' raise ValueError(msg) # optionally prevent behavioral response if behv.has_any_response() and not behavior_allowed: msg = 'A behavior RESPONSE IS NOT ALLOWED' raise ValueError(msg) # create post-reform Calculator instance recs2 = Records(data=taxrec_df.copy(deep=True), gfactors=growfactors_post) policy2 = Policy(gfactors=growfactors_post) policy_reform = user_mods['policy'] policy2.implement_reform(policy_reform) calc2 = Calculator(policy=policy2, records=recs2, consumption=consump, behavior=behv) while calc2.current_year < start_year: calc2.increment_year() calc2.calc_all() assert calc2.current_year == start_year # increment Calculator objects for year_n years and calculate for _ in range(0, year_n): calc1.increment_year() calc2.increment_year() calc1.calc_all() if calc2.behavior.has_response(): calc2 = Behavior.response(calc1, calc2) else: calc2.calc_all() # return calculated Calculator objects and mask return (calc1, calc2, mask)
def run_nth_year_gdp_elast_model(year_n, start_year, taxrec_df, user_mods, return_json=True): """ The run_nth_year_gdp_elast_model function assumes user_mods is a dictionary returned by the Calculator.read_json_parameter_files() function with an extra key:value pair that is specified as 'gdp_elasticity': {'value': <float_value>}. """ # pylint: disable=too-many-arguments,too-many-locals,too-many-statements check_user_mods(user_mods) # Only makes sense to run for budget years 1 through n-1 (not for year 0) assert year_n > 0 # Specify value of gdp_elasticity gdp_elasticity = user_mods['gdp_elasticity']['value'] # Specify Consumption instance consump = Consumption() consump_assumps = user_mods['consumption'] consump.update_consumption(consump_assumps) # Specify growdiff_baseline and growdiff_response growdiff_baseline = Growdiff() growdiff_response = Growdiff() growdiff_base_assumps = user_mods['growdiff_baseline'] growdiff_resp_assumps = user_mods['growdiff_response'] growdiff_baseline.update_growdiff(growdiff_base_assumps) growdiff_response.update_growdiff(growdiff_resp_assumps) # Create pre-reform and post-reform Growfactors instances growfactors_pre = Growfactors() growdiff_baseline.apply_to(growfactors_pre) growfactors_post = Growfactors() growdiff_baseline.apply_to(growfactors_post) growdiff_response.apply_to(growfactors_post) # Create pre-reform Calculator instance records1 = Records(data=taxrec_df.copy(deep=True), gfactors=growfactors_pre) policy1 = Policy(gfactors=growfactors_pre) calc1 = Calculator(policy=policy1, records=records1, consumption=consump) while calc1.current_year < start_year: calc1.increment_year() assert calc1.current_year == start_year # Create post-reform Calculator instance records2 = Records(data=taxrec_df.copy(deep=True), gfactors=growfactors_post) policy2 = Policy(gfactors=growfactors_post) policy_reform = user_mods['policy'] policy2.implement_reform(policy_reform) calc2 = Calculator(policy=policy2, records=records2, consumption=consump) while calc2.current_year < start_year: calc2.increment_year() assert calc2.current_year == start_year # Seed random number generator with a seed value based on user_mods seed = random_seed(user_mods) np.random.seed(seed) # pylint: disable=no-member for _ in range(0, year_n - 1): calc1.increment_year() calc2.increment_year() calc1.calc_all() calc2.calc_all() # Assert that the current year is one behind the year we are calculating assert (calc1.current_year + 1) == (start_year + year_n) assert (calc2.current_year + 1) == (start_year + year_n) # Compute gdp effect gdp_effect = proportional_change_gdp(calc1, calc2, gdp_elasticity) # Return gdp_effect results if return_json: gdp_df = pd.DataFrame(data=[gdp_effect], columns=['col0']) gdp_elast_names_i = [ x + '_' + str(year_n) for x in GDP_ELAST_ROW_NAMES ] gdp_elast_total = create_json_table(gdp_df, row_names=gdp_elast_names_i, num_decimals=5) gdp_elast_total = dict((k, v[0]) for k, v in gdp_elast_total.items()) return gdp_elast_total else: return gdp_effect