def test_round_trip_tcja_reform(tests_path):
    """
    Check that current-law policy has the same policy parameter values in
    a future year as does a compound reform that first implements the
    reform specified in the 2017_law.json file and then implements the
    reform specified in the TCJA.json file.  This test checks that the
    future-year parameter values for current-law policy (which incorporates
    TCJA) are the same as future-year parameter values for the compound
    round-trip reform.  Doing this check ensures that the 2017_law.json
    and TCJA.json reform files are specified in a consistent manner.
    """
    # pylint: disable=too-many-locals
    fyear = 2020
    # create clp metadata dictionary for current-law policy in fyear
    pol = Policy()
    pol.set_year(fyear)
    clp_mdata = pol.metadata()
    # create rtr metadata dictionary for round-trip reform in fyear
    pol = Policy()
    reform_file = os.path.join(tests_path, '..', 'reforms', '2017_law.json')
    with open(reform_file, 'r') as rfile:
        rtext = rfile.read()
    pol.implement_reform(Policy.read_json_reform(rtext))
    # eventually activate: assert not clp.parameter_warnings
    ctc_c_warning = 'CTC_c was redefined in release 1.0.0\n'
    assert pol.parameter_warnings == ctc_c_warning
    assert not pol.parameter_errors
    reform_file = os.path.join(tests_path, '..', 'reforms', 'TCJA.json')
    with open(reform_file, 'r') as rfile:
        rtext = rfile.read()
    pol.implement_reform(Policy.read_json_reform(rtext))
    # eventually activate: assert not clp.parameter_warnings
    assert pol.parameter_warnings == ctc_c_warning
    assert not pol.parameter_errors
    pol.set_year(fyear)
    rtr_mdata = pol.metadata()
    # compare fyear policy parameter values
    assert clp_mdata.keys() == rtr_mdata.keys()
    fail_dump = False
    if fail_dump:
        rtr_fails = open('fails_rtr', 'w')
        clp_fails = open('fails_clp', 'w')
    fail_params = list()
    msg = '\nRound-trip-reform and current-law-policy param values differ for:'
    for pname in clp_mdata.keys():
        rtr_val = rtr_mdata[pname]['value']
        clp_val = clp_mdata[pname]['value']
        if not np.allclose(rtr_val, clp_val):
            fail_params.append(pname)
            msg += '\n  {} in {} : rtr={} clp={}'.format(
                pname, fyear, rtr_val, clp_val
            )
            if fail_dump:
                rtr_fails.write('{} {} {}\n'.format(pname, fyear, rtr_val))
                clp_fails.write('{} {} {}\n'.format(pname, fyear, clp_val))
    if fail_dump:
        rtr_fails.close()
        clp_fails.close()
    if fail_params:
        raise ValueError(msg)
Exemplo n.º 2
0
def test_round_trip_tcja_reform(tests_path):
    """
    Check that current-law policy has the same policy parameter values in
    a future year as does a compound reform that first implements the
    reform specified in the 2017_law.json file and then implements the
    reform specified in the TCJA.json file.  This test checks that the
    future-year parameter values for current-law policy (which incorporates
    TCJA) are the same as future-year parameter values for the compound
    round-trip reform.  Doing this check ensures that the 2017_law.json
    and TCJA.json reform files are specified in a consistent manner.
    """
    # pylint: disable=too-many-locals
    fyear = 2020
    # create clp metadata dictionary for current-law policy in fyear
    pol = Policy()
    pol.set_year(fyear)
    clp_mdata = pol.metadata()
    # create rtr metadata dictionary for round-trip reform in fyear
    pol = Policy()
    reform_file = os.path.join(tests_path, '..', 'reforms', '2017_law.json')
    with open(reform_file, 'r') as rfile:
        rtext = rfile.read()
    pol.implement_reform(Policy.read_json_reform(rtext))
    # eventually activate: assert not clp.parameter_warnings
    ctc_c_warning = 'CTC_c was redefined in release 1.0.0\n'
    assert pol.parameter_warnings == ctc_c_warning
    assert not pol.parameter_errors
    reform_file = os.path.join(tests_path, '..', 'reforms', 'TCJA.json')
    with open(reform_file, 'r') as rfile:
        rtext = rfile.read()
    pol.implement_reform(Policy.read_json_reform(rtext))
    # eventually activate: assert not clp.parameter_warnings
    assert pol.parameter_warnings == ctc_c_warning
    assert not pol.parameter_errors
    pol.set_year(fyear)
    rtr_mdata = pol.metadata()
    # compare fyear policy parameter values
    assert clp_mdata.keys() == rtr_mdata.keys()
    fail_dump = False
    if fail_dump:
        rtr_fails = open('fails_rtr', 'w')
        clp_fails = open('fails_clp', 'w')
    fail_params = list()
    msg = '\nRound-trip-reform and current-law-policy param values differ for:'
    for pname in clp_mdata.keys():
        rtr_val = rtr_mdata[pname]['value']
        clp_val = clp_mdata[pname]['value']
        if not np.allclose(rtr_val, clp_val):
            fail_params.append(pname)
            msg += '\n  {} in {} : rtr={} clp={}'.format(
                pname, fyear, rtr_val, clp_val)
            if fail_dump:
                rtr_fails.write('{} {} {}\n'.format(pname, fyear, rtr_val))
                clp_fails.write('{} {} {}\n'.format(pname, fyear, clp_val))
    if fail_dump:
        rtr_fails.close()
        clp_fails.close()
    if fail_params:
        raise ValueError(msg)
def test_2017_law_reform(tests_path):
    """
    Check that policy parameter values in a future year under current-law
    policy and under the reform specified in the 2017_law.json file are
    sensible.
    """
    # create pre metadata dictionary for 2017_law.json reform in fyear
    pol = Policy()
    reform_file = os.path.join(tests_path, '..', 'reforms', '2017_law.json')
    with open(reform_file, 'r') as rfile:
        rtext = rfile.read()
    pol.implement_reform(Policy.read_json_reform(rtext))
    # eventually activate: assert not clp.parameter_warnings
    ctc_c_warning = 'CTC_c was redefined in release 1.0.0\n'
    assert pol.parameter_warnings == ctc_c_warning
    assert not pol.parameter_errors
    pol.set_year(2018)
    pre_mdata = pol.metadata()
    # check some policy parameter values against expected values under 2017 law
    pre_expect = {
        # relation '<' implies asserting that actual < expect
        # relation '>' implies asserting that actual > expect
        # ... parameters not affected by TCJA and that are not indexed
        'AMEDT_ec': {'relation': '=', 'value': 200000},
        'SS_thd85': {'relation': '=', 'value': 34000},
        # ... parameters not affected by TCJA and that are indexed
        'STD_Dep': {'relation': '>', 'value': 1050},
        'CG_brk2': {'relation': '>', 'value': 425800},
        'AMT_CG_brk1': {'relation': '>', 'value': 38600},
        'AMT_brk1': {'relation': '>', 'value': 191100},
        'EITC_c': {'relation': '>', 'value': 519},
        'EITC_ps': {'relation': '>', 'value': 8490},
        'EITC_ps_MarriedJ': {'relation': '>', 'value': 5680},
        'EITC_InvestIncome_c': {'relation': '>', 'value': 3500},
        # ... parameters affected by TCJA and that are not indexed
        'ID_Charity_crt_all': {'relation': '=', 'value': 0.5},
        'II_rt3': {'relation': '=', 'value': 0.25},
        # ... parameters affected by TCJA and that are indexed
        'II_brk3': {'relation': '>', 'value': 91900},
        'STD': {'relation': '<', 'value': 7000},
        'II_em': {'relation': '>', 'value': 4050},
        'AMT_em_pe': {'relation': '<', 'value': 260000}
    }
    assert isinstance(pre_expect, dict)
    assert set(pre_expect.keys()).issubset(set(pre_mdata.keys()))
    for name in pre_expect:
        aval = pre_mdata[name]['value']
        if isinstance(aval, list):
            act = aval[0]  # comparing only first item in a vector parameter
        else:
            act = aval
        exp = pre_expect[name]['value']
        if pre_expect[name]['relation'] == '<':
            assert act < exp, '{} a={} !< e={}'.format(name, act, exp)
        elif pre_expect[name]['relation'] == '>':
            assert act > exp, '{} a={} !> e={}'.format(name, act, exp)
        elif pre_expect[name]['relation'] == '=':
            assert act == exp, '{} a={} != e={}'.format(name, act, exp)
Exemplo n.º 4
0
def test_json_reform_url():
    """
    Test reading a JSON reform from a URL. Results from the URL are expected
    to match the results from the string.
    """
    reform_str = """
    {
        // raise FICA payroll tax rate in 2018 and 2020
        "FICA_ss_trt": {
            "2018": 0.130,
            "2020": 0.140
        },
        // raise Medicare payroll tax rate in 2019 and 2021
        "FICA_mc_trt": {
            "2019": 0.030,
            "2021": 0.032
        }
    }
    """
    reform_url = ('https://raw.githubusercontent.com/PSLmodels/'
                  'Tax-Calculator/master/taxcalc/reforms/ptaxes0.json')
    params_str = Policy.read_json_reform(reform_str)
    params_url = Policy.read_json_reform(reform_url)
    assert params_str == params_url
Exemplo n.º 5
0
def test_puf_var_stats(tests_path, puf_fullsample):
    """
    Main logic of test.
    """
    # create a baseline Policy object containing 2017_law.json parameters
    pre_tcja_jrf = os.path.join(tests_path, '..', 'reforms', '2017_law.json')
    pre_tcja = Policy.read_json_reform(pre_tcja_jrf)
    baseline_policy = Policy()
    baseline_policy.implement_reform(pre_tcja)
    # create a Calculator object using baseline_policy and full puf.csv sample
    rec = Records(data=puf_fullsample)
    calc = Calculator(policy=baseline_policy, records=rec, verbose=False)
    # create base tables
    table_mean = create_base_table(tests_path)
    table_corr = copy.deepcopy(table_mean)
    del table_corr['description']
    # add statistics to tables
    year_headers = ['description']
    for year in range(Policy.JSON_START_YEAR, Policy.LAST_BUDGET_YEAR + 1):
        assert year == calc.current_year
        year_headers.append(str(year))
        calc.calc_all()
        calculate_mean_stats(calc, table_mean, year)
        if year == 2016:
            calculate_corr_stats(calc, table_corr)
        if year < Policy.LAST_BUDGET_YEAR:
            calc.increment_year()
    # write tables to new CSV files
    mean_path = os.path.join(tests_path, MEAN_FILENAME + '-new')
    table_mean.sort_index(inplace=True)
    table_mean.to_csv(mean_path, header=year_headers, float_format='%8.0f')
    corr_path = os.path.join(tests_path, CORR_FILENAME + '-new')
    table_corr.sort_index(inplace=True)
    table_corr.to_csv(corr_path,
                      float_format='%8.2f',
                      columns=table_corr.index)
    # compare new and old CSV files for differences
    mean_msg = differences(mean_path, mean_path[:-4], 'MEAN')
    corr_msg = differences(corr_path, corr_path[:-4], 'CORR')
    if mean_msg or corr_msg:
        raise ValueError(mean_msg + corr_msg)
def test_puf_var_stats(tests_path, puf_fullsample):
    """
    Main logic of test.
    """
    # create a baseline Policy object containing 2017_law.json parameters
    pre_tcja_jrf = os.path.join(tests_path, '..', 'reforms', '2017_law.json')
    pre_tcja = Policy.read_json_reform(pre_tcja_jrf)
    baseline_policy = Policy()
    baseline_policy.implement_reform(pre_tcja)
    # create a Calculator object using baseline_policy and full puf.csv sample
    rec = Records(data=puf_fullsample)
    calc = Calculator(policy=baseline_policy, records=rec, verbose=False)
    # create base tables
    table_mean = create_base_table(tests_path)
    table_corr = copy.deepcopy(table_mean)
    del table_corr['description']
    # add statistics to tables
    year_headers = ['description']
    for year in range(Policy.JSON_START_YEAR, Policy.LAST_BUDGET_YEAR + 1):
        assert year == calc.current_year
        year_headers.append(str(year))
        calc.calc_all()
        calculate_mean_stats(calc, table_mean, year)
        if year == 2016:
            calculate_corr_stats(calc, table_corr)
        if year < Policy.LAST_BUDGET_YEAR:
            calc.increment_year()
    # write tables to new CSV files
    mean_path = os.path.join(tests_path, MEAN_FILENAME + '-new')
    table_mean.sort_index(inplace=True)
    table_mean.to_csv(mean_path, header=year_headers, float_format='%8.0f')
    corr_path = os.path.join(tests_path, CORR_FILENAME + '-new')
    table_corr.sort_index(inplace=True)
    table_corr.to_csv(corr_path, float_format='%8.2f',
                      columns=table_corr.index)
    # compare new and old CSV files for differences
    mean_msg = differences(mean_path, mean_path[:-4], 'MEAN')
    corr_msg = differences(corr_path, corr_path[:-4], 'CORR')
    if mean_msg or corr_msg:
        raise ValueError(mean_msg + corr_msg)
Exemplo n.º 7
0
def test_read_json_reform_file_and_implement_reform(set_year):
    """
    Test reading and translation of reform JSON into a reform dictionary
    and then using that reform dictionary to implement reform.
    """
    pol = Policy()
    if set_year:
        pol.set_year(2015)
    pol.implement_reform(Policy.read_json_reform(REFORM_JSON))
    syr = pol.start_year
    # pylint: disable=protected-access
    amt_brk1 = pol._AMT_brk1
    assert amt_brk1[2015 - syr] == 200000
    assert amt_brk1[2016 - syr] > 200000
    assert amt_brk1[2017 - syr] == 300000
    assert amt_brk1[2018 - syr] > 300000
    ii_em = pol._II_em
    assert ii_em[2016 - syr] == 6000
    assert ii_em[2017 - syr] == 6000
    assert ii_em[2018 - syr] == 7500
    assert ii_em[2019 - syr] > 7500
    assert ii_em[2020 - syr] == 9000
    assert ii_em[2021 - syr] > 9000
    amt_em = pol._AMT_em
    assert amt_em[2016 - syr, 0] > amt_em[2015 - syr, 0]
    assert amt_em[2017 - syr, 0] > amt_em[2016 - syr, 0]
    assert amt_em[2018 - syr, 0] == amt_em[2017 - syr, 0]
    assert amt_em[2019 - syr, 0] == amt_em[2017 - syr, 0]
    assert amt_em[2020 - syr, 0] == amt_em[2017 - syr, 0]
    assert amt_em[2021 - syr, 0] > amt_em[2020 - syr, 0]
    assert amt_em[2022 - syr, 0] > amt_em[2021 - syr, 0]
    add4aged = pol._ID_Medical_frt_add4aged
    assert add4aged[2015 - syr] == -0.025
    assert add4aged[2016 - syr] == -0.025
    assert add4aged[2017 - syr] == 0.0
    assert add4aged[2022 - syr] == 0.0
Exemplo n.º 8
0
def fixture_baseline_2017_law():
    """
    Read ../taxcalc/2017_law.json and return its policy dictionary.
    """
    pre_tcja_jrf = os.path.join(CUR_PATH, '..', 'taxcalc', '2017_law.json')
    return Policy.read_json_reform(pre_tcja_jrf)
Exemplo n.º 9
0
def test_2017_law_reform():
    """
    Check that policy parameter values in a future year under current-law
    policy and under the reform specified in the 2017_law.json file are
    sensible.
    """
    # create pre metadata dictionary for 2017_law.json reform in fyear
    pol = Policy()
    reform_file = os.path.join(CUR_PATH, '..', 'taxcalc', '2017_law.json')
    with open(reform_file, 'r') as rfile:
        rtext = rfile.read()
    pol.implement_reform(Policy.read_json_reform(rtext))
    assert not pol.parameter_warnings
    pol.set_year(2018)
    pre_mdata = dict(pol.items())
    # check some policy parameter values against expected values under 2017 law
    pre_expect = {
        # relation '<' implies asserting that actual < expect
        # relation '>' implies asserting that actual > expect
        # ... parameters not affected by TCJA and that are not indexed
        'AMEDT_ec': {
            'relation': '=',
            'value': 200000
        },
        'SS_thd85': {
            'relation': '=',
            'value': 34000
        },
        # ... parameters not affected by TCJA and that are indexed
        'STD_Dep': {
            'relation': '>',
            'value': 1050
        },
        'CG_brk2': {
            'relation': '>',
            'value': 425800
        },
        'AMT_CG_brk1': {
            'relation': '>',
            'value': 38600
        },
        'AMT_brk1': {
            'relation': '>',
            'value': 191100
        },
        'EITC_c': {
            'relation': '>',
            'value': 519
        },
        'EITC_ps': {
            'relation': '>',
            'value': 8490
        },
        'EITC_ps_MarriedJ': {
            'relation': '>',
            'value': 5680
        },
        'EITC_InvestIncome_c': {
            'relation': '>',
            'value': 3500
        },
        # ... parameters affected by TCJA and that are not indexed
        'ID_Charity_crt_all': {
            'relation': '=',
            'value': 0.5
        },
        'II_rt3': {
            'relation': '=',
            'value': 0.25
        },
        # ... parameters affected by TCJA and that are indexed
        'II_brk3': {
            'relation': '>',
            'value': 91900
        },
        'STD': {
            'relation': '<',
            'value': 7000
        },
        'II_em': {
            'relation': '>',
            'value': 4050
        },
        'AMT_em_pe': {
            'relation': '<',
            'value': 260000
        }
    }
    assert isinstance(pre_expect, dict)
    assert set(pre_expect.keys()).issubset(set(pre_mdata.keys()))
    for name in pre_expect:
        aval = pre_mdata[name]
        if aval.ndim == 2:
            act = aval[0][0]  # comparing only first item in a vector parameter
        else:
            act = aval[0]
        exp = pre_expect[name]['value']
        if pre_expect[name]['relation'] == '<':
            assert act < exp, '{} a={} !< e={}'.format(name, act, exp)
        elif pre_expect[name]['relation'] == '>':
            assert act > exp, '{} a={} !> e={}'.format(name, act, exp)
        elif pre_expect[name]['relation'] == '=':
            assert act == exp, '{} a={} != e={}'.format(name, act, exp)
Exemplo n.º 10
0
def test_reform_json_and_output():
    """
    Check that each JSON reform file can be converted into a reform dictionary
    that can then be passed to the Policy class implement_reform method that
    generates no parameter_errors.
    Then use each reform to generate static tax results for small set of
    filing units in a single tax_year and compare those results with
    expected results from a CSV-formatted file.
    """

    # pylint: disable=too-many-statements,too-many-locals

    # embedded function used only in test_reform_json_and_output
    def write_res_file(calc, resfilename):
        """
        Write calc output to CSV-formatted file with resfilename.
        """
        varlist = [
            'RECID', 'c00100', 'standard', 'c04800', 'iitax', 'payrolltax'
        ]
        # varnames  AGI    STD         TaxInc    ITAX     PTAX
        stats = calc.dataframe(varlist)
        stats['RECID'] = stats['RECID'].astype(int)
        with open(resfilename, 'w') as resfile:
            stats.to_csv(resfile, index=False, float_format='%.2f')

    # embedded function used only in test_reform_json_and_output
    def res_and_out_are_same(base):
        """
        Return True if base.res.csv and base.out.csv file contents are same;
        return False if base.res.csv and base.out.csv file contents differ.
        """
        resdf = pd.read_csv(base + '.res.csv')
        outdf = pd.read_csv(base + '.out.csv')
        diffs = False
        for col in resdf:
            if col in outdf:
                if not np.allclose(resdf[col], outdf[col]):
                    diffs = True
            else:
                diffs = True
        return not diffs

    # specify Records object containing cases data
    tax_year = 2020
    cases_path = os.path.join(CUR_PATH, '..', 'taxcalc', 'cases.csv')
    cases = Records(
        data=cases_path,
        start_year=tax_year,  # set raw input data year
        gfactors=None,  # keeps raw data unchanged
        weights=None,
        adjust_ratios=None)
    # specify list of reform failures
    failures = list()
    # specify current-law-policy Calculator object
    calc = Calculator(policy=Policy(), records=cases, verbose=False)
    calc.advance_to_year(tax_year)
    calc.calc_all()
    res_path = cases_path.replace('cases.csv', 'clp.res.csv')
    write_res_file(calc, res_path)
    if res_and_out_are_same(res_path.replace('.res.csv', '')):
        os.remove(res_path)
    else:
        failures.append(res_path)
    del calc
    # read 2017_law.json reform file and specify its parameters dictionary
    pre_tcja_jrf = os.path.join(CUR_PATH, '..', 'taxcalc', '2017_law.json')
    pre_tcja = Policy.read_json_reform(pre_tcja_jrf)
    # check reform file contents and reform results for each reform
    reforms_path = os.path.join(CUR_PATH, '..', 'taxcalc', '*.json')
    json_reform_files = glob.glob(reforms_path)
    for jrf in json_reform_files:
        # determine reform's baseline by reading contents of jrf
        with open(jrf, 'r') as rfile:
            jrf_text = rfile.read()
        pre_tcja_baseline = 'Reform_Baseline: 2017_law.json' in jrf_text
        # implement the reform relative to its baseline
        reform = Policy.read_json_reform(jrf_text)
        pol = Policy()  # current-law policy
        if pre_tcja_baseline:
            pol.implement_reform(pre_tcja)
            assert not pol.parameter_errors
        pol.implement_reform(reform)
        assert not pol.parameter_errors
        calc = Calculator(policy=pol, records=cases, verbose=False)
        calc.advance_to_year(tax_year)
        calc.calc_all()
        res_path = jrf.replace('.json', '.res.csv')
        write_res_file(calc, res_path)
        if res_and_out_are_same(res_path.replace('.res.csv', '')):
            os.remove(res_path)
        else:
            failures.append(res_path)
        del calc
    if failures:
        msg = 'Following reforms have res-vs-out differences:\n'
        for ref in failures:
            msg += '{}\n'.format(os.path.basename(ref))
        raise ValueError(msg)
Exemplo n.º 11
0
def fixture_baseline_2017_law(tests_path):
    """
    Read ../reforms/2017_law.json and return its policy dictionary.
    """
    pre_tcja_jrf = os.path.join(tests_path, '..', 'reforms', '2017_law.json')
    return Policy.read_json_reform(pre_tcja_jrf)
Exemplo n.º 12
0
def test_round_trip_reforms(fyear, tests_path):
    """
    Check that current-law policy has the same policy parameter values in
    a future year as does a compound reform that first implements the
    2017 tax law as specified in the 2017_law.json file and then implements
    reforms that represents new tax legislation since 2017.
    This test checks that the future-year parameter values for
    current-law policy (which incorporates recent legislation such as
    the TCJA, CARES Act, and ARPA) are the same as future-year
    parameter values for the compound round-trip reform.
    Doing this check ensures that the 2017_law.json
    and subsequent reform files that represent recent legislation are
    specified in a consistent manner.
    """
    # pylint: disable=too-many-locals
    # create clp metadata dictionary for current-law policy in fyear
    clp_pol = Policy()
    clp_pol.set_year(fyear)
    clp_mdata = dict(clp_pol.items())
    # create rtr metadata dictionary for round-trip reform in fyear
    rtr_pol = Policy()
    # Revert to 2017 law
    reform_file = os.path.join(tests_path, '..', 'reforms', '2017_law.json')
    with open(reform_file, 'r') as rfile:
        rtext = rfile.read()
    rtr_pol.implement_reform(Policy.read_json_reform(rtext))
    assert not rtr_pol.parameter_warnings
    assert not rtr_pol.errors
    # Layer on TCJA
    reform_file = os.path.join(tests_path, '..', 'reforms', 'TCJA.json')
    with open(reform_file, 'r') as rfile:
        rtext = rfile.read()
    rtr_pol.implement_reform(Policy.read_json_reform(rtext))
    assert not rtr_pol.parameter_warnings
    assert not rtr_pol.errors
    # Layer on the CARES Act
    rtr_pol.implement_reform({
        'ID_Charity_crt_all': {
            2020: 1.0,
            2021: 0.6
        },
        'STD_allow_charity_ded_nonitemizers': {
            2020: True,
            2021: False
        },
        'STD_charity_ded_nonitemizers_max': {
            2020: 300.0,
            2021: 0.0
        }
    })
    assert not rtr_pol.parameter_warnings
    assert not rtr_pol.errors
    # Layer on ARPA
    rtr_pol.implement_reform({
        'RRC_c': {
            2021: 1400,
            2022: 0
        },
        'RRC_ps': {
            2021: [75000, 150000, 75000, 112500, 150000],
            2022: [0, 0, 0, 0, 0]
        },
        'RRC_pe': {
            2021: [80000, 160000, 80000, 120000, 160000],
            2022: [0, 0, 0, 0, 0]
        },
        'UI_em': {
            2020: 10200,
            2021: 0
        },
        'UI_thd': {
            2020: [150000, 150000, 150000, 150000, 150000],
            2021: [0, 0, 0, 0, 0]
        },
        'CTC_refundable': {
            2021: True,
            2022: False
        },
        'CTC_include17': {
            2021: True,
            2022: False
        },
        'CTC_new_c': {
            2021: 1000,
            2022: 0
        },
        'CTC_new_c_under6_bonus': {
            2021: 600,
            2022: 0
        },
        'CTC_new_for_all': {
            2021: True,
            2022: False
        },
        'CTC_new_ps': {
            2021: [75000, 150000, 75000, 112500, 150000],
            2022: [0, 0, 0, 0, 0]
        },
        'CTC_new_prt': {
            2021: 0.05,
            2022: 0
        },
        'EITC_c': {
            2021: [1502.46, 3606.44, 5960.95, 6706.58],
            2022: [546.21, 3640.7, 6017.58, 6770.29]
        },
        'EITC_rt': {
            2021: [0.153, 0.34, 0.4, 0.45],
            2022: [0.0765, 0.34, 0.4, 0.45]
        },
        'EITC_ps': {
            2021: [11610, 19464.12, 19464.12, 19464.12],
            2022: [8931.38, 19649.03, 19649.03, 19649.03]
        },
        'EITC_MinEligAge': {
            2021: 19,
            2022: 25
        },
        'EITC_MaxEligAge': {
            2021: 125,
            2022: 64
        },
        'EITC_InvestIncome_c': {
            2021: 10000
        },
        'EITC_sep_filers_elig': {
            2021: True
        },
        'CDCC_c': {
            2021: 8000,
            2022: 3000
        },
        'CDCC_ps': {
            2021: 125000,
            2022: 15000
        },
        'CDCC_ps2': {
            2021: 400000,
            2022: 9e+99
        },
        'CDCC_crt': {
            2021: 50.0,
            2022: 35.0
        },
        'CDCC_refundable': {
            2021: True,
            2022: False
        },
        'ALD_BusinessLosses_c': {
            2026: [283535.22, 567070.42, 283535.22, 283535.22, 567070.42],
            2027: [9e+99, 9e+99, 9e+99, 9e+99, 9e+99]
        }
    })
    assert not rtr_pol.parameter_warnings
    assert not rtr_pol.errors
    rtr_pol.set_year(fyear)
    rtr_mdata = dict(rtr_pol.items())
    # compare fyear policy parameter values
    assert clp_mdata.keys() == rtr_mdata.keys()
    fail_dump = False
    if fail_dump:
        rtr_fails = open('fails_rtr', 'w')
        clp_fails = open('fails_clp', 'w')
    fail_params = list()
    msg = '\nRound-trip-reform and current-law-policy param values differ for:'
    for pname in clp_mdata.keys():
        rtr_val = rtr_mdata[pname]
        clp_val = clp_mdata[pname]
        if not np.allclose(rtr_val, clp_val):
            fail_params.append(pname)
            msg += '\n  {} in {} : rtr={} clp={}'.format(
                pname, fyear, rtr_val, clp_val)
            if fail_dump:
                rtr_fails.write('{} {} {}\n'.format(pname, fyear, rtr_val))
                clp_fails.write('{} {} {}\n'.format(pname, fyear, clp_val))
    if fail_dump:
        rtr_fails.close()
        clp_fails.close()
    if fail_params:
        raise ValueError(msg)
Exemplo n.º 13
0
def test_read_json_reform_and_implement_reform(set_year):
    """
    Test reading and translation of reform file into a reform dictionary
    that is then used to call implement_reform method.
    NOTE: implement_reform called when policy.current_year == policy.start_year
    """
    reform_json = """
    // Example of JSON reform text suitable for the
    // Policy.read_json_reform() method.
    // This JSON text can contain any number of trailing //-style comments,
    // which will be removed before the contents are converted from JSON to
    // a dictionary.
    // The primary keys are policy parameters and secondary keys are years.
    // Both the primary & secondary key values must be enclosed in quotes (").
    // Boolean variables are specified as true or false with no quotes and all
    // lowercase characters.
    {
        "AMT_brk1": // top of first AMT tax bracket
        {"2015": 200000,
         "2017": 300000
        },
        "EITC_c": // max EITC amount by number of qualifying kids (0,1,2,3+)
        {"2016": [ 900, 5000,  8000,  9000],
         "2019": [1200, 7000, 10000, 12000]
        },
        "II_em": // personal exemption amount (see indexing changes below)
        {"2016": 6000,
         "2018": 7500,
         "2020": 9000
        },
        "II_em-indexed": // personal exemption amount indexing status
        {"2016": false, // values in future years are same as this year value
         "2018": true   // vals in future years indexed with this year as base
        },
        "SS_Earnings_c": // Social Security (OASDI) maximum taxable earnings
        {"2016": 300000,
         "2018": 500000,
         "2020": 700000
        },
        "AMT_em-indexed": // AMT exemption amount indexing status
        {"2017": false, // values in future years are same as this year value
         "2020": true   // vals in future years indexed with this year as base
        }
    }
    """
    policy = Policy()
    if set_year:
        policy.set_year(2015)
    reform_dict = Policy.read_json_reform(reform_json)
    policy.implement_reform(reform_dict)
    syr = policy.start_year
    amt_brk1 = policy._AMT_brk1
    assert amt_brk1[2015 - syr] == 200000
    assert amt_brk1[2016 - syr] > 200000
    assert amt_brk1[2017 - syr] == 300000
    assert amt_brk1[2018 - syr] > 300000
    ii_em = policy._II_em
    assert ii_em[2016 - syr] == 6000
    assert ii_em[2017 - syr] == 6000
    assert ii_em[2018 - syr] == 7500
    assert ii_em[2019 - syr] > 7500
    assert ii_em[2020 - syr] == 9000
    assert ii_em[2021 - syr] > 9000
    amt_em = policy._AMT_em
    assert amt_em[2016 - syr, 0] > amt_em[2015 - syr, 0]
    assert amt_em[2017 - syr, 0] > amt_em[2016 - syr, 0]
    assert amt_em[2018 - syr, 0] == amt_em[2017 - syr, 0]
    assert amt_em[2019 - syr, 0] == amt_em[2017 - syr, 0]
    assert amt_em[2020 - syr, 0] == amt_em[2017 - syr, 0]
    assert amt_em[2021 - syr, 0] > amt_em[2020 - syr, 0]
    assert amt_em[2022 - syr, 0] > amt_em[2021 - syr, 0]
    add4aged = policy._ID_Medical_frt_add4aged
    assert add4aged[2015 - syr] == -0.025
    assert add4aged[2016 - syr] == -0.025
    assert add4aged[2017 - syr] == 0.0
    assert add4aged[2022 - syr] == 0.0
Exemplo n.º 14
0
def test_round_trip_tcja_reform(tests_path):
    """
    Check that current-law policy has the same policy parameter values in
    a future year as does a compound reform that first implements the
    reform specified in the 2017_law.json file and then implements the
    reform specified in the TCJA.json file.  This test checks that the
    future-year parameter values for current-law policy (which incorporates
    TCJA) are the same as future-year parameter values for the compound
    round-trip reform.  Doing this check ensures that the 2017_law.json
    and TCJA.json reform files are specified in a consistent manner.
    """
    # pylint: disable=too-many-locals
    fyear = 2020
    # create clp metadata dictionary for current-law policy in fyear
    pol = Policy()
    pol.set_year(fyear)
    clp_mdata = dict(pol.items())
    # create rtr metadata dictionary for round-trip reform in fyear
    pol = Policy()
    # Revert to 2017 law
    reform_file = os.path.join(tests_path, '..', 'reforms', '2017_law.json')
    with open(reform_file, 'r') as rfile:
        rtext = rfile.read()
    pol.implement_reform(Policy.read_json_reform(rtext))
    assert not pol.parameter_warnings
    assert not pol.errors
    # Layer on TCJA
    reform_file = os.path.join(tests_path, '..', 'reforms', 'TCJA.json')
    with open(reform_file, 'r') as rfile:
        rtext = rfile.read()
    pol.implement_reform(Policy.read_json_reform(rtext))
    assert not pol.parameter_warnings
    assert not pol.errors
    # Layer on the CARES Act
    pol.implement_reform({
        'ID_Charity_crt_all': {
            2020: 1.0,
            2021: 0.6
        },
        'STD_allow_charity_ded_nonitemizers': {
            2020: True,
            2021: False
        },
        'STD_charity_ded_nonitemizers_max': {
            2020: 300.0,
            2021: 0.0
        }
    })
    assert not pol.parameter_warnings
    assert not pol.errors
    pol.set_year(fyear)
    rtr_mdata = dict(pol.items())
    # compare fyear policy parameter values
    assert clp_mdata.keys() == rtr_mdata.keys()
    fail_dump = False
    if fail_dump:
        rtr_fails = open('fails_rtr', 'w')
        clp_fails = open('fails_clp', 'w')
    fail_params = list()
    msg = '\nRound-trip-reform and current-law-policy param values differ for:'
    for pname in clp_mdata.keys():
        rtr_val = rtr_mdata[pname]
        clp_val = clp_mdata[pname]
        if not np.allclose(rtr_val, clp_val):
            fail_params.append(pname)
            msg += '\n  {} in {} : rtr={} clp={}'.format(
                pname, fyear, rtr_val, clp_val)
            if fail_dump:
                rtr_fails.write('{} {} {}\n'.format(pname, fyear, rtr_val))
                clp_fails.write('{} {} {}\n'.format(pname, fyear, clp_val))
    if fail_dump:
        rtr_fails.close()
        clp_fails.close()
    if fail_params:
        raise ValueError(msg)
def fixture_baseline_2017_law(tests_path):
    """
    Read ../reforms/2017_law.json and return its policy dictionary.
    """
    pre_tcja_jrf = os.path.join(tests_path, '..', 'reforms', '2017_law.json')
    return Policy.read_json_reform(pre_tcja_jrf)
def test_reform_json_and_output(tests_path):
    """
    Check that each JSON reform file can be converted into a reform dictionary
    that can then be passed to the Policy class implement_reform method that
    generates no parameter_errors.
    Then use each reform to generate static tax results for small set of
    filing units in a single tax_year and compare those results with
    expected results from a CSV-formatted file.
    """
    # pylint: disable=too-many-statements,too-many-locals

    # embedded function used only in test_reform_json_and_output
    def write_res_file(calc, resfilename):
        """
        Write calc output to CSV-formatted file with resfilename.
        """
        varlist = [
            'RECID', 'c00100', 'standard', 'c04800', 'iitax', 'payrolltax'
        ]
        # varnames  AGI    STD         TaxInc    ITAX     PTAX
        stats = calc.dataframe(varlist)
        stats['RECID'] = stats['RECID'].astype(int)
        with open(resfilename, 'w') as resfile:
            stats.to_csv(resfile, index=False, float_format='%.2f')

    # embedded function used only in test_reform_json_and_output
    def res_and_out_are_same(base):
        """
        Return True if base.res.csv and base.out.csv file contents are same;
        return False if base.res.csv and base.out.csv file contents differ.
        """
        resdf = pd.read_csv(base + '.res.csv')
        outdf = pd.read_csv(base + '.out.csv')
        diffs = False
        for col in resdf:
            if col in outdf:
                if not np.allclose(resdf[col], outdf[col]):
                    diffs = True
            else:
                diffs = True
        return not diffs

    # specify Records object containing cases data
    tax_year = 2020
    cases_path = os.path.join(tests_path, '..', 'reforms', 'cases.csv')
    cases = Records(data=cases_path,
                    start_year=tax_year,  # set raw input data year
                    gfactors=None,  # keeps raw data unchanged
                    weights=None,
                    adjust_ratios=None)
    # specify list of reform failures
    failures = list()
    # specify current-law-policy Calculator object
    calc = Calculator(policy=Policy(), records=cases, verbose=False)
    calc.advance_to_year(tax_year)
    calc.calc_all()
    res_path = cases_path.replace('cases.csv', 'clp.res.csv')
    write_res_file(calc, res_path)
    if res_and_out_are_same(res_path.replace('.res.csv', '')):
        os.remove(res_path)
    else:
        failures.append(res_path)
    del calc
    # read 2017_law.json reform file and specify its parameters dictionary
    pre_tcja_jrf = os.path.join(tests_path, '..', 'reforms', '2017_law.json')
    pre_tcja = Policy.read_json_reform(pre_tcja_jrf)
    # check reform file contents and reform results for each reform
    reforms_path = os.path.join(tests_path, '..', 'reforms', '*.json')
    json_reform_files = glob.glob(reforms_path)
    for jrf in json_reform_files:
        # determine reform's baseline by reading contents of jrf
        with open(jrf, 'r') as rfile:
            jrf_text = rfile.read()
        pre_tcja_baseline = 'Reform_Baseline: 2017_law.json' in jrf_text
        # implement the reform relative to its baseline
        reform = Policy.read_json_reform(jrf_text)
        pol = Policy()  # current-law policy
        if pre_tcja_baseline:
            pol.implement_reform(pre_tcja)
            assert not pol.parameter_errors
        pol.implement_reform(reform)
        assert not pol.parameter_errors
        calc = Calculator(policy=pol, records=cases, verbose=False)
        calc.advance_to_year(tax_year)
        calc.calc_all()
        res_path = jrf.replace('.json', '.res.csv')
        write_res_file(calc, res_path)
        if res_and_out_are_same(res_path.replace('.res.csv', '')):
            os.remove(res_path)
        else:
            failures.append(res_path)
        del calc
    if failures:
        msg = 'Following reforms have res-vs-out differences:\n'
        for ref in failures:
            msg += '{}\n'.format(os.path.basename(ref))
        raise ValueError(msg)