def test_nondefault_response_function(be_inc, cps_subsample):
    """
    Test that non-default behavior parameters produce expected results.
    """
    # ... specify Records object and policy reform
    rec = tc.Records.cps_constructor(data=cps_subsample)
    refyear = 2020
    reform = {'II_em': {refyear: 1500}}
    # ... specify non-default1 response elasticities
    elasticities_dict = {'sub': 0.25, 'inc': be_inc, 'cg': -0.79}
    # ... calculate behavioral response to reform
    pol = tc.Policy()
    calc1 = tc.Calculator(records=rec, policy=pol)
    pol.implement_reform(reform)
    calc2 = tc.Calculator(records=rec, policy=pol)
    del pol
    calc1.advance_to_year(refyear)
    calc2.advance_to_year(refyear)
    df1, df2 = response(calc1, calc2, elasticities_dict)
    del calc1
    del calc2
    itax1 = round((df1['iitax'] * df1['s006']).sum() * 1e-9, 3)
    itax2 = round((df2['iitax'] * df2['s006']).sum() * 1e-9, 3)
    del df1
    del df2
    if be_inc == 0.0:
        assert np.allclose([itax1, itax2], [1355.556, 1304.984])
    elif be_inc == -0.1:
        assert np.allclose([itax1, itax2], [1355.556, 1303.898])
def test_alternative_behavior_parameters(cps_subsample):
    """
    Test alternative behavior parameters to improve code coverage.
    Also, test response function's dump argument.
    """
    # ... specify Records object and policy reform
    rec = tc.Records.cps_constructor(data=cps_subsample)
    refyear = 2020
    reform = {'II_em': {refyear: 1500}}
    # ... specify non-default response elasticities
    elasticities_dict = {'inc': -0.1}
    # ... calculate behavioral response to reform
    pol = tc.Policy()
    calc1 = tc.Calculator(records=rec, policy=pol)
    pol.implement_reform(reform)
    calc2 = tc.Calculator(records=rec, policy=pol)
    del pol
    calc1.advance_to_year(refyear)
    calc2.advance_to_year(refyear)
    df1, df2 = response(calc1, calc2, elasticities_dict)
    del calc1
    del calc2
    itax1 = round((df1['iitax'] * df1['s006']).sum() * 1e-9, 3)
    itax2 = round((df2['iitax'] * df2['s006']).sum() * 1e-9, 3)
    del df1
    del df2
    assert np.allclose([itax1, itax2], [1355.556, 1302.09])
def test_sub_effect_independence(stcg):
    """
    Ensure that LTCG amount does not affect magnitude of substitution effect.
    """
    # pylint: disable=too-many-locals
    # specify reform that raises top-bracket marginal tax rate
    refyear = 2020
    reform = {'II_rt7': {refyear: 0.70}}
    # specify a substitution effect behavioral response elasticity
    elasticities_dict = {'sub': 0.25}
    # specify several high-earning filing units
    num_recs = 9
    input_csv = (u'RECID,MARS,e00200,e00200p,p22250,p23250\n'
                 u'1,2,1000000,1000000,stcg,    0\n'
                 u'2,2,1000000,1000000,stcg, 4800\n'
                 u'3,2,1000000,1000000,stcg, 3600\n'
                 u'4,2,1000000,1000000,stcg, 2400\n'
                 u'5,2,1000000,1000000,stcg, 1200\n'
                 u'6,2,1000000,1000000,stcg,    0\n'
                 u'7,2,1000000,1000000,stcg,-1200\n'
                 u'8,2,1000000,1000000,stcg,-2400\n'
                 u'9,2,1000000,1000000,stcg,-3600\n')
    inputcsv = input_csv.replace('stcg', str(stcg))
    input_dataframe = pd.read_csv(StringIO(inputcsv))
    assert len(input_dataframe.index) == num_recs
    recs = tc.Records(data=input_dataframe,
                      start_year=refyear,
                      gfactors=None,
                      weights=None)
    pol = tc.Policy()
    calc1 = tc.Calculator(records=recs, policy=pol)
    assert calc1.current_year == refyear
    pol.implement_reform(reform)
    calc2 = tc.Calculator(records=recs, policy=pol)
    assert calc2.current_year == refyear
    del pol
    df1, df2 = response(calc1, calc2, elasticities_dict)
    del calc1
    del calc2
    # compute change in taxable income for each of the filing units
    chg_funit = dict()
    for rid in range(1, num_recs + 1):
        idx = rid - 1
        chg_funit[rid] = df2['c04800'][idx] - df1['c04800'][idx]
    del df1
    del df2
    # confirm reform reduces taxable income when assuming substitution effect
    emsg = ''
    for rid in range(1, num_recs + 1):
        if not chg_funit[rid] < 0:
            txt = '\nFAIL: stcg={} : chg[{}]={:.2f} is not negative'
            emsg += txt.format(stcg, rid, chg_funit[rid])
    # confirm change in taxable income is same for all filing units
    for rid in range(2, num_recs + 1):
        if not np.allclose(chg_funit[rid], chg_funit[1]):
            txt = '\nFAIL: stcg={} : chg[{}]={:.2f} != chg[1]={:.2f}'
            emsg += txt.format(stcg, rid, chg_funit[rid], chg_funit[1])
    del chg_funit
    if emsg:
        raise ValueError(emsg)
def test_default_response_function(cps_subsample):
    """
    Test that default behavior parameters produce static results.
    """
    # ... specify Records object and policy reform
    rec = tc.Records.cps_constructor(data=cps_subsample)
    refyear = 2020
    assert refyear >= 2018
    reform = {'II_em': {refyear: 1500}}
    # ... construct pre-reform calculator
    pol = tc.Policy()
    calc1 = tc.Calculator(records=rec, policy=pol)
    calc1.advance_to_year(refyear)
    # ... construct two post-reform calculators
    pol.implement_reform(reform)
    calc2s = tc.Calculator(records=rec, policy=pol)  # for static assumptions
    calc2s.advance_to_year(refyear)
    calc2d = tc.Calculator(records=rec, policy=pol)  # for default behavior
    calc2d.advance_to_year(refyear)
    del pol
    # ... calculate aggregate inctax using static assumptions
    calc2s.calc_all()
    df2s = calc2s.dataframe(['iitax', 's006'])
    itax2s = round((df2s['iitax'] * df2s['s006']).sum() * 1e-9, 3)
    # ... calculate aggregate inctax using zero response elasticities
    _, df2d = response(calc1, calc2d, elasticities={}, dump=True)
    itax2d = round((df2d['iitax'] * df2d['s006']).sum() * 1e-9, 3)
    assert np.allclose(itax2d, itax2s)
    # ... clean up
    del calc1
    del calc2s
    del calc2d
    del df2s
    del df2d
Exemple #5
0
 def _dynamic_run(self, varlist, base_calc, reform_calc):
     """
     Run a dynamic response
     """
     if "s006" not in varlist:  # ensure weight is always included
         varlist.append("s006")
     for year in range(self.start_year, self.end_year + 1):
         base_calc.advance_to_year(year)
         reform_calc.advance_to_year(year)
         base, reform = behresp.response(base_calc,
                                         reform_calc,
                                         self.params["behavior"],
                                         dump=True)
         self.base_data[year] = base[varlist]
         self.reform_data[year] = reform[varlist]
Exemple #6
0
    def _run_dynamic_calc(self, calc1, calc2, behavior, year, varlist):
        """
        Function used to parallelize the dynamic run function

        Parameters
        ----------
        calc1: Calculator object representing the baseline policy
        calc2: Calculator object representing the reform policy
        year: year for the calculations
        """
        calc1_copy = copy.deepcopy(calc1)
        calc2_copy = copy.deepcopy(calc2)
        calc1_copy.advance_to_year(year)
        calc2_copy.advance_to_year(year)
        # use response function to capture dynamic effects
        base, reform = response(calc1_copy, calc2_copy, behavior, dump=True)
        self.base_data[year] = base[varlist]
        self.reform_data[year] = reform[varlist]
        del calc1_copy, calc2_copy, base, reform
Exemple #7
0
    def _behresp_advance(self, base_calc, reform_calc, varlist, year):
        '''
        This function advances the year used in the Behavioral Responses
        model and saves the results to a dictionary.
        Args:
            calc1 (Tax-Calculator Calculator object): TC calculator
            year (int): year to begin advancing from
        Returns:
            tax_dict (dict): a dictionary of microdata with marginal tax
                rates and other information computed in TC
        '''
        base_calc.advance_to_year(year)
        reform_calc.advance_to_year(year)
        base, reform = behresp.response(base_calc,
                                        reform_calc,
                                        self.params["behavior"],
                                        dump=True)
        base_df = base[varlist]
        reform_df = reform[varlist]

        return [base_df, reform_df]
Exemple #8
0
pol.implement_reform(params['policy'])
calc2 = Calculator(policy=pol, records=recs)

# calculate reform income tax liabilities for cyr under static assumptions
calc2.advance_to_year(cyr)
calc2.calc_all()
itax_rev2sa = calc2.weighted_total('iitax')

# specify behavioral-response assumptions
behresp_json = '{"BE_sub": {"2018": 0.25}}'
behresp_dict = Calculator.read_json_assumptions(behresp_json)

# specify Calculator object for analysis of reform with behavioral response
calc2 = Calculator(policy=pol, records=recs)
calc2.advance_to_year(cyr)
_, df2br = behresp.response(calc1, calc2, behresp_dict)

# calculate reform income tax liabilities for cyr with behavioral response
itax_rev2br = (df2br['iitax'] * df2br['s006']).sum()

# print total income tax revenue estimates for cyr
# (estimates in billons of dollars rounded to nearest hundredth of a billion)
print('{}_CURRENT_LAW_P__itax_rev($B)= {:.3f}'.format(cyr, itax_rev1 * 1e-9))
print('{}_REFORM_STATIC__itax_rev($B)= {:.3f}'.format(cyr, itax_rev2sa * 1e-9))
print('{}_REFORM_DYNAMIC_itax_rev($B)= {:.3f}'.format(cyr, itax_rev2br * 1e-9))

# create multi-year diagnostic tables for
# (1) baseline,
# (2) reform excluding behavioral responses, and
# (3) reform including behavioral responses
num_years = 3  # number of diagnostic table years beginning with cyr
# specify Calculator object for static analysis of reform policy
pol.implement_reform(params['policy'])
calc2 = Calculator(policy=pol, records=recs)

# calculate reform income tax liabilities for cyr under static assumptions
calc2.advance_to_year(cyr)
calc2.calc_all()
itax_rev2sa = calc2.weighted_total('iitax')

# specify assumed non-zero response-function substitution elasticity
response_elasticities = {'sub': 0.25}

# specify Calculator object for analysis of reform with behavioral responses
calc2 = Calculator(policy=pol, records=recs)
calc2.advance_to_year(cyr)
_, df2br = behresp.response(calc1, calc2, response_elasticities)

# calculate reform income tax liabilities for cyr with behavioral response
itax_rev2br = (df2br['iitax'] * df2br['s006']).sum()

# print total income tax revenue estimates for cyr
# (estimates in billons of dollars)
print('{}_CURRENT_LAW_P__itax_rev($B)= {:.3f}'.format(cyr, itax_rev1 * 1e-9))
print('{}_REFORM_STATIC__itax_rev($B)= {:.3f}'.format(cyr, itax_rev2sa * 1e-9))
print('{}_REFORM_DYNAMIC_itax_rev($B)= {:.3f}'.format(cyr, itax_rev2br * 1e-9))

# create multi-year diagnostic tables for
# (1) baseline,
# (2) reform excluding behavioral responses, and
# (3) reform including behavioral responses
num_years = 3  # number of diagnostic table years beginning with cyr
# specify Calculator object for static analysis of reform policy
pol.implement_reform(Policy.read_json_reform('reformA.json'))
calc2 = Calculator(policy=pol, records=recs)

# calculate reform income tax liabilities for cyr under static assumptions
calc2.advance_to_year(cyr)
calc2.calc_all()
itax_rev2sa = calc2.weighted_total('iitax')

# specify assumed non-zero response-function substitution elasticity
response_elasticities = {'sub': 0.25}

# specify Calculator object for analysis of reform with behavioral responses
calc2 = Calculator(policy=pol, records=recs)
calc2.advance_to_year(cyr)
_, df2br = behresp.response(calc1, calc2, response_elasticities)

# calculate reform income tax liabilities for cyr with behavioral response
itax_rev2br = (df2br['iitax'] * df2br['s006']).sum()

# print total income tax revenue estimates for cyr
# (estimates in billons of dollars)
print('{}_CURRENT_LAW_P__itax_rev($B)= {:.3f}'.format(cyr, itax_rev1 * 1e-9))
print('{}_REFORM_STATIC__itax_rev($B)= {:.3f}'.format(cyr, itax_rev2sa * 1e-9))
print('{}_REFORM_DYNAMIC_itax_rev($B)= {:.3f}'.format(cyr, itax_rev2br * 1e-9))

# create multi-year diagnostic tables for
# (1) baseline,
# (2) reform excluding behavioral responses, and
# (3) reform including behavioral responses
num_years = 3  # number of diagnostic table years beginning with cyr
Exemple #11
0
    def create_table(
        self,
        reform_file=None,
        tc_vars=None,
        tc_labels=None,
        include_mtr=True,
        be_sub=0,
        be_inc=0,
        be_cg=0,
    ):
        """
        Creates table of liabilities. Default is current law with no behavioral response
            (i.e. static analysis). User may specify a policy reform which is read and
            implemented below in get_pol() and/or or may specify elasticities for partial-
            equilibrium behavioral responses.

        reform_file: name of a reform file in the Tax-Calculator reforms folder,
            a file path to a custom JSON reform file, or a dictionary with a policy reform.

        tc_vars: list of Tax-Calculator output variables.

        tc_labels: list of labels for output table

        include_mtr: include MTR calculations in output table

        be_sub: Substitution elasticity of taxable income. Defined as proportional change
            in taxable income divided by proportional change in marginal net-of-tax rate
            (1-MTR) on taxpayer earnings caused by the reform.  Must be zero or positive.

        be_inc: Income elasticity of taxable income. Defined as dollar change in taxable
            income divided by dollar change in after-tax income caused by the reform.
            Must be zero or negative.

        be_cg: Semi-elasticity of long-term capital gains. Defined as change in logarithm
            of long-term capital gains divided by change in marginal tax rate (MTR) on
            long-term capital gains caused by the reform.  Must be zero or negative.

        Returns:
            df_res: a Pandas dataframe. Each observation is a separate tax filer
        """
        year = self.invar["FLPDYR"][0]
        year = int(year.item())
        recs = tc.Records(
            data=self.invar,
            start_year=year,
            gfactors=None,
            weights=None,
            adjust_ratios=None,
        )

        # if tc_vars and tc_labels are not specified, defaults are used
        if tc_vars is None:
            tc_vars = self.TC_VARS
        if tc_labels is None:
            tc_labels = self.TC_LABELS

        assert len(tc_vars) > 0
        assert len(tc_vars) == len(tc_labels)

        # if no reform file is passed, table will show current law values
        if reform_file is None:
            pol = tc.Policy()
            assert be_sub == be_inc == be_cg == 0
            calc = tc.Calculator(policy=pol, records=recs)
            calc.advance_to_year(year)
            calc.calc_all()
            calcs = calc.dataframe(tc_vars)
        # if a reform file is passed, table will show reform values
        else:
            pol = self.get_pol(reform_file)
            calc = tc.Calculator(policy=pol, records=recs)
            pol_base = tc.Policy()
            calc_base = tc.Calculator(policy=pol_base, records=recs)
            response_elasticities = {"sub": be_sub, "inc": be_inc, "cg": be_cg}
            _, df2br = br.response(calc_base,
                                   calc,
                                   response_elasticities,
                                   dump=True)
            calcs = df2br[tc_vars]

        # if include_mtr is True, the tables includes three columns with MTRs
        if include_mtr:
            mtr = self.calc_mtr(reform_file)
            mtr_df = pd.DataFrame(data=mtr).transpose()
            df_res = pd.concat([calcs, mtr_df], axis=1)
            col_labels = tc_labels + self.MTR_LABELS
            df_res.columns = col_labels
            df_res.index = range(self.rows)
        else:
            df_res = calcs
            df_res.columns = tc_labels
            df_res.index = range(self.rows)

        return df_res