Exemplo n.º 1
0
    def get_pol(self, reform_file):
        """
        Reads the specified reform and implements it
        """

        REFORMS_URL = ("https://raw.githubusercontent.com/"
                       "PSLmodels/Tax-Calculator/master/taxcalc/reforms/")
        CURRENT_PATH = os.path.abspath(os.path.dirname(__file__))
        if reform_file == None:
            pol = tc.Policy()
        else:
            # check to see if file path to reform_file exists
            if isinstance(reform_file, str) and os.path.isfile(
                    os.path.join(CURRENT_PATH, reform_file)):
                reform_path = os.path.join(CURRENT_PATH, reform_file)
                pol = tc.Policy()
                pol.implement_reform(tc.Policy.read_json_reform(reform_path))
            # try reform_file as dictionary
            elif isinstance(reform_file, dict):
                reform = reform_file
                pol = tc.Policy()
                pol.implement_reform(reform)
            # if file path does not exist, check Tax-Calculator reforms file
            else:
                try:
                    reform_url = REFORMS_URL + reform_file
                    pol = tc.Policy()
                    pol.implement_reform(
                        tc.Policy.read_json_reform(reform_url))
                except:
                    raise 'Reform file does not exist'
        return pol
Exemplo n.º 2
0
    def get_pol(self, reform_file):
        """
        Reads the specified reform and implements it
        """

        REFORMS_URL = ("https://raw.githubusercontent.com/"
                       "PSLmodels/Tax-Calculator/master/taxcalc/reforms/")

        # check to see if file path to reform_file exists
        if isinstance(reform_file, str) and os.path.isfile(reform_file):
            pol = tc.Policy()
            pol.implement_reform(tc.Policy.read_json_reform(reform_file))
        # try reform_file as dictionary
        elif isinstance(reform_file, dict):
            pol = tc.Policy()
            try:
                pol.implement_reform(reform_file)
            except:
                # use adjust method for web app
                pol.adjust(reform_file)
        # if file path does not exist, check Tax-Calculator reforms file
        else:
            try:
                reform_url = REFORMS_URL + reform_file
                pol = tc.Policy()
                pol.implement_reform(tc.Policy.read_json_reform(reform_url))
            except:
                raise "Reform file does not exist"
        return pol
Exemplo n.º 3
0
    def choose_reform(self):
        """
        Creates Tax-Calculator Policy object for reform analysis

        Returns:
            self.pol2: Tax-Calculator Policy object for reform analysis 
        """
        REFORMS_URL = (
            "https://raw.githubusercontent.com/"
            "PSLmodels/Tax-Calculator/master/taxcalc/reforms/"
        )
        CURRENT_PATH = os.path.abspath(os.path.dirname(__file__))

        # if user specified a preset reform in their adjustment file, pull
        # reform from Tax-Calculator reforms folder
        if self.reform_options != "None" and self.custom_reform is None:
            reform_name = self.reform_options
            reform_url = REFORMS_URL + reform_name
            reform = tc.Calculator.read_json_param_objects(reform_url, None)
            self.pol2 = tc.Policy()
            self.pol2.implement_reform(reform["policy"])
        # otherwise, look for user-provided json reform file
        # first as file path
        elif self.reform_options == "None" and isinstance(self.custom_reform, str):
            try:
                reform_filename = os.path.join(
                    CURRENT_PATH, self.custom_reform)
                reform = tc.Calculator.read_json_param_objects(
                    reform_filename, None)
                self.pol2 = tc.Policy()
                self.pol2.implement_reform(reform["policy"])
            except:
                print("Reform file path does not exist")
        # then as dictionary
        elif self.reform_options == "None" and isinstance(self.custom_reform, dict):
            try:
                reform = self.custom_reform
                self.pol2 = tc.Policy()
                self.pol2.implement_reform(reform)
            except Exception as e:
                import traceback
                traceback.print_exc()
                print("Reform dictionary does not exist")
        # raise error if preset reform is chosen and custom reform is specified
        elif self.reform_options != "None" and self.custom_reform is not None:
            raise AttributeError(
                "You have specified a preset reform and a custom reform. Please choose one reform."
            )
        # if no reform file was given, set reform to current law
        else:
            self.pol2 = tc.Policy()

        return self.pol2
Exemplo n.º 4
0
    def _make_calculators(self):
        """
        This function creates the baseline and reform calculators used when
        the `run()` method is called
        """
        # Create two microsimulation calculators
        gd_base = tc.GrowDiff()
        gf_base = tc.GrowFactors()
        # apply user specified growdiff
        if self.params["growdiff_baseline"]:
            gd_base.update_growdiff(self.params["growdiff_baseline"])
            gd_base.apply_to(gf_base)
        # Baseline calculator
        if self.use_cps:
            records = tc.Records.cps_constructor(data=self.microdata,
                                                 gfactors=gf_base)
        else:
            records = tc.Records(self.microdata, gfactors=gf_base)
        policy = tc.Policy(gf_base)
        if self.params["base_policy"]:
            update_policy(policy, self.params["base_policy"])
        base_calc = tc.Calculator(policy=policy,
                                  records=records,
                                  verbose=self.verbose)

        # Reform calculator
        # Initialize a policy object
        gd_reform = tc.GrowDiff()
        gf_reform = tc.GrowFactors()
        if self.params["growdiff_response"]:
            gd_reform.update_growdiff(self.params["growdiff_response"])
            gd_reform.apply_to(gf_reform)
        if self.use_cps:
            records = tc.Records.cps_constructor(data=self.microdata,
                                                 gfactors=gf_reform)
        else:
            records = tc.Records(self.microdata, gfactors=gf_reform)
        policy = tc.Policy(gf_reform)
        if self.params["base_policy"]:
            update_policy(policy, self.params["base_policy"])
        update_policy(policy, self.params["policy"])

        # Initialize Calculator
        reform_calc = tc.Calculator(policy=policy,
                                    records=records,
                                    verbose=self.verbose)
        # delete all unneeded variables
        del gd_base, gd_reform, records, gf_base, gf_reform, policy
        return base_calc, reform_calc
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])
Exemplo n.º 6
0
def get_inputs(meta_params_dict):
    """
    Return default parameters for Tax-Brain
    """
    meta_params = MetaParameters()
    with meta_params.transaction(defer_validation=True):
        meta_params.adjust(meta_params_dict)
        # Year must be at least 2014 when using the CPS. This rule is validated
        # in the validate_inputs function below.
        # See: https://github.com/PSLmodels/Tax-Brain/issues/176
        if meta_params.data_source == "CPS" and meta_params.year < 2014:
            meta_params.adjust({"year": 2014})

    policy_params = taxcalc.Policy()
    policy_params.set_state(year=meta_params.year.tolist())

    policy_defaults = cs2tc.convert_policy_defaults(meta_params, policy_params)

    behavior_params = BehaviorParams()

    default_params = {
        "policy": policy_defaults,
        "behavior": behavior_params.dump()
    }
    meta = meta_params.dump()

    return {"meta_parameters": meta, "model_parameters": default_params}
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)
Exemplo n.º 8
0
 def __init__(
         self,
         btax_policy_ref,
         itax_policy_ref,
         # baseline defaults are current-law policy
         btax_policy_base=Policy(),
         itax_policy_base=itax.Policy(),
         investor_data='puf.csv'):
     # Check policy argument types
     assert isinstance(btax_policy_ref, Policy)
     assert isinstance(itax_policy_ref, itax.Policy)
     assert isinstance(btax_policy_base, Policy)
     assert isinstance(itax_policy_base, itax.Policy)
     # Create Investor objects incorporating itax policy and investor data
     self.investor_base = Investor(itax_policy_base, investor_data)
     self.investor_ref = Investor(itax_policy_ref, investor_data)
     # Create btax policy parameters DataFrame objects
     self.btax_params_base = btax_policy_base.parameters_dataframe()
     self.btax_params_ref = btax_policy_ref.parameters_dataframe()
     # Create Corporations
     self.corp_base = Corporation(self.btax_params_base)
     self.corp_ref = Corporation(self.btax_params_ref)
     # Create PassThroughs
     self.passthru_base = PassThrough(self.btax_params_base)
     self.passthru_ref = PassThrough(self.btax_params_ref)
     # Declare calculated results objects
     self.multipliers = None
     self.model_results = None
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
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])
Exemplo n.º 11
0
def test_incorrect_instantiation():
    """
    Test incorrect Investor instantiation
    """
    with pytest.raises(ValueError):
        Investor(Policy())
    with pytest.raises(ValueError):
        Investor(itax.Policy(), list())
Exemplo n.º 12
0
    def _make_stacked_objects(self):
        """
        This method makes the base calculator and policy and records objects
        for stacked reforms. The difference between this and the standard
        _make_calcuators method is that this method
        only fully creates the baseline calculator. For the reform, it creates
        policy and records objects and implements any growth assumptions
        provided by the user.
        """
        # Create two microsimulation calculators
        gd_base = tc.GrowDiff()
        gf_base = tc.GrowFactors()
        # apply user specified growdiff
        if self.params["growdiff_baseline"]:
            gd_base.update_growdiff(self.params["growdiff_baseline"])
            gd_base.apply_to(gf_base)
        # Baseline calculator
        if self.use_cps:
            records = tc.Records.cps_constructor(data=self.microdata,
                                                 gfactors=gf_base)
        else:
            records = tc.Records(self.microdata, gfactors=gf_base)
        policy = tc.Policy(gf_base)
        if self.params["base_policy"]:
            update_policy(policy, self.params["base_policy"])
        base_calc = tc.Calculator(policy=policy,
                                  records=records,
                                  verbose=self.verbose)

        # Reform calculator
        # Initialize a policy object
        gd_reform = tc.GrowDiff()
        gf_reform = tc.GrowFactors()
        if self.params["growdiff_response"]:
            gd_reform.update_growdiff(self.params["growdiff_response"])
            gd_reform.apply_to(gf_reform)
        if self.use_cps:
            records = tc.Records.cps_constructor(data=self.microdata,
                                                 gfactors=gf_reform)
        else:
            records = tc.Records(self.microdata, gfactors=gf_reform)
        policy = tc.Policy(gf_reform)
        return base_calc, policy, records
Exemplo n.º 13
0
    def choose_baseline(self):
        """
        Creates Tax-Calculator Policy object for baseline policy

        The default baseline policy is current law.

        Returns:
            self.pol: Tax-Calculator Policy object for baseline policy
        """
        REFORMS_URL = (
            "https://raw.githubusercontent.com/"
            "PSLmodels/Tax-Calculator/master/taxcalc/reforms/"
        )
        CURRENT_PATH = os.path.abspath(os.path.dirname(__file__))

        # if no baseline policy is specified, baseline is current law
        if self.baseline is None:
            self.pol = tc.Policy()
        # if a baseline policy is specified, first see if user created json
        # policy file
        else:
            exists = os.path.isfile(os.path.join(CURRENT_PATH, self.baseline))
            if exists:
                baseline_file = os.path.join(CURRENT_PATH, self.baseline)
                # baseline = tc.Calculator.read_json_param_objects(baseline_file, None)
                self.pol = tc.Policy()
                self.pol.implement_reform(
                    tc.Policy.read_json_reform(baseline_file))
            # if the user did not create a json file, try the Tax-Calculator
            # reforms file
            else:
                try:
                    baseline_file = self.baseline
                    baseline_url = REFORMS_URL + baseline_file
                    baseline = tc.Calculator.read_json_param_objects(
                        baseline_url, None)
                    self.pol = tc.Policy()
                    self.pol.implement_reform(baseline["policy"])
                except:
                    print("Baseline file does not exist")

        return self.pol
Exemplo n.º 14
0
def test_simple_adj(taxparams):
    pol = taxcalc.Policy()
    pol.implement_reform({
        "EITC_c": {
            2020: [10000, 10001, 10002, 10003],
            2023: [20000, 20001, 20002, 20003],
        }
    })

    taxparams.adjust({
        "EITC_c": [
            {
                "year": 2020,
                "EIC": "0kids",
                "value": 10000
            },
            {
                "year": 2020,
                "EIC": "1kid",
                "value": 10001
            },
            {
                "year": 2020,
                "EIC": "2kids",
                "value": 10002
            },
            {
                "year": 2020,
                "EIC": "3+kids",
                "value": 10003
            },
            {
                "year": 2023,
                "EIC": "0kids",
                "value": 20000
            },
            {
                "year": 2023,
                "EIC": "1kid",
                "value": 20001
            },
            {
                "year": 2023,
                "EIC": "2kids",
                "value": 20002
            },
            {
                "year": 2023,
                "EIC": "3+kids",
                "value": 20003
            },
        ]
    })
    cmp_with_taxcalc_values(taxparams, pol)
Exemplo n.º 15
0
def test_adj_CPI_offset(year):
    def convert(arr):
        return {2013 + i: arr[i] for i in range(len(arr))}

    taxparams_cpi = TaxParams()
    taxparams_cpi.adjust({"CPI_offset": [{"year": year, "value": -0.001}]})

    pol = taxcalc.Policy()
    pol.set_year(year)
    pol.implement_reform({"CPI_offset": {year: -0.001}})

    cmp_with_taxcalc_values(taxparams_cpi, pol)
Exemplo n.º 16
0
def test_incorrect_calc_all():
    """
    Test incorrect call of calc_all method.
    """
    # Do a quick "simulation" of executing response.calc_all(...) by setting
    # one of the calculated responses to anything other than None
    pre_calc_response = Response()
    pre_calc_response.investment_response = 9.99
    # Try to use pre_calc_response as argument to BusinessModel.calc_all method
    bizmod = BusinessModel(Policy(), itax.Policy(),
                           investor_data='nodata.csv')
    with pytest.raises(ValueError):
        bizmod.calc_all(response=pre_calc_response)
Exemplo n.º 17
0
def validate_inputs(meta_params_dict, adjustment, errors_warnings):
    """
    Function to validate COMP inputs
    """
    pol_params = cs2tc.convert_policy_adjustment(adjustment["policy"])
    policy_params = taxcalc.Policy()
    policy_params.adjust(pol_params, raise_errors=False, ignore_warnings=True)
    errors_warnings["policy"]["errors"].update(policy_params.errors)

    behavior_params = BehaviorParams()
    behavior_params.adjust(adjustment["behavior"], raise_errors=False)
    errors_warnings["behavior"]["errors"].update(behavior_params.errors)

    return {"errors_warnings": errors_warnings}
Exemplo n.º 18
0
def test_reforms(reform_number, reforms, puf_subsample, actual_vs_expect):
    """
    Test BusinessModel corporate tax return results under reforms
    with no response.
    """
    bizmod = BusinessModel(reforms[reform_number]['policy_obj'],
                           itax.Policy(),
                           investor_data=puf_subsample)
    bizmod.calc_all(response=None)
    # compare actual and expected results
    dec = 3
    results = bizmod.corp_ref.taxreturn.combined_return.round(dec)
    fname = 'bizmod_corp_ref{}_expect.csv'.format(reform_number)
    actual_vs_expect(results, fname, precision=dec)
Exemplo n.º 19
0
    def calc_mtr(self, reform_file):
        """
        Calculates income tax, payroll tax, and combined marginal rates
        """
        year = self.invar["FLPDYR"][0]
        year = int(year.item())
        recs_base = tc.Records(
            data=self.invar,
            start_year=year,
            gfactors=None,
            weights=None,
            adjust_ratios=None,
        )
        if reform_file is None:
            pol = tc.Policy()
        else:
            pol = self.get_pol(reform_file)

        calc_base = tc.Calculator(policy=pol, records=recs_base)
        calc_base.advance_to_year(year)
        calc_base.calc_all()
        payrolltax_base = calc_base.array("payrolltax")
        incometax_base = calc_base.array("iitax")
        combined_taxes_base = incometax_base + payrolltax_base

        recs_marg = tc.Records(
            data=self.invar_marg,
            start_year=year,
            gfactors=None,
            weights=None,
            adjust_ratios=None,
        )
        calc_marg = tc.Calculator(policy=pol, records=recs_marg)
        calc_marg.advance_to_year(year)
        calc_marg.calc_all()
        payrolltax_marg = calc_marg.array("payrolltax")
        incometax_marg = calc_marg.array("iitax")
        combined_taxes_marg = incometax_marg + payrolltax_marg

        payrolltax_diff = payrolltax_marg - payrolltax_base
        incometax_diff = incometax_marg - incometax_base
        combined_diff = combined_taxes_marg - combined_taxes_base

        mtr_payrolltax = payrolltax_diff / FINITE_DIFF
        mtr_incometax = incometax_diff / FINITE_DIFF
        mtr_combined = combined_diff / FINITE_DIFF

        return (mtr_payrolltax, mtr_incometax, mtr_combined)
Exemplo n.º 20
0
def cmp_with_taxcalc_values(taxparams, pol=None):
    if pol is None:
        pol = taxcalc.Policy()
    # test all keys are the same.
    assert set(map(lambda x: x[1:],
                   pol._vals.keys())) == set(taxparams._data.keys())
    # test all values are the same.
    pol.set_year(2029)
    # breakpoint()
    for param in taxparams._data:
        np.testing.assert_allclose(getattr(pol, f"_{param}"),
                                   getattr(taxparams, param))
    taxparams.set_state()
    for param in taxparams._data:
        np.testing.assert_allclose(getattr(pol, f"_{param}"),
                                   getattr(taxparams, param))
Exemplo n.º 21
0
def test_multiple_cpi_swaps():

    pol = taxcalc.Policy()
    pol.implement_reform({
        "II_em": {
            2016: 6000,
            2018: 7500,
            2020: 9000
        },
        "II_em-indexed": {
            2016: False,
            2018: True
        },
    })

    taxparams = TaxParams()
    taxparams.adjust({
        "II_em": [
            {
                "year": 2016,
                "value": 6000
            },
            {
                "year": 2018,
                "value": 7500
            },
            {
                "year": 2020,
                "value": 9000
            },
        ],
        "II_em-indexed": [
            {
                "year": 2016,
                "value": False
            },
            {
                "year": 2018,
                "value": True
            },
        ],
    })

    cmp_with_taxcalc_values(taxparams, pol)
Exemplo n.º 22
0
def convert_params(params):
    """
    Convert ParamTools style parameter inputs to traditional taxcalc style
    parameters for report policy table creation

    Parameters
    ----------
    params: dict
        ParamTools style reform dictionary

    Returns
    -------
    reform: dict
        a dictionary in traditional taxcalc style
    """
    pol = tc.Policy()
    pol.adjust(params)
    indexed_params = []
    reform = defaultdict(dict)
    first_yr = pol.LAST_BUDGET_YEAR
    for param in params.keys():
        yrs = []
        if param.endswith("-indexed"):
            indexed_params.append(param)
            continue
        for adj in params[param]:
            yrs.append(adj["year"])
        yrs = set(yrs)
        values = getattr(pol, f"_{param}")
        for yr in yrs:
            idx = yr - pol.JSON_START_YEAR
            vals = values[idx]
            if isinstance(vals, np.ndarray):
                vals = list(vals)
            first_yr = min(yr, first_yr)
            reform[param][yr] = vals
    # add indexed parameters as being implemented in first year of reform
    for param in indexed_params:
        val = params[param][0]['value']
        reform[param][first_yr] = val
    return reform
Exemplo n.º 23
0
def get_inputs(meta_params_dict):
    """
    Return default parameters for Tax-Brain
    """
    meta_params = MetaParameters()
    meta_params.adjust(meta_params_dict)

    policy_params = taxcalc.Policy()
    policy_params.set_state(year=meta_params.year.tolist(), )

    policy_defaults = cs2tc.convert_policy_defaults(meta_params, policy_params)

    behavior_params = BehaviorParams()

    default_params = {
        "policy": policy_defaults,
        "behavior": behavior_params.dump()
    }
    meta = meta_params.dump()

    return {"meta_parameters": meta, "model_parameters": default_params}
Exemplo n.º 24
0
def test_adj_indexed_status_and_param_value(taxparams, year):
    pol = taxcalc.Policy()
    pol.implement_reform({
        "EITC_c": {
            year: [10000, 10001, 10002, 10003]
        },
        "EITC_c-indexed": {
            year: False
        },
    })

    taxparams.adjust({
        "EITC_c": [
            {
                "year": year,
                "EIC": "0kids",
                "value": 10000
            },
            {
                "year": year,
                "EIC": "1kid",
                "value": 10001
            },
            {
                "year": year,
                "EIC": "2kids",
                "value": 10002
            },
            {
                "year": year,
                "EIC": "3+kids",
                "value": 10003
            },
        ],
        "EITC_c-indexed": [{
            "year": year,
            "value": False
        }],
    })
    cmp_with_taxcalc_values(taxparams, pol)
Exemplo n.º 25
0
def test_adj_activates_index(taxparams, year):
    pol = taxcalc.Policy()
    pol.implement_reform({
        "CTC_c": {
            year: 1005
        },
        "CTC_c-indexed": {
            year: True
        }
    })

    taxparams.adjust({
        "CTC_c": [{
            "year": year,
            "value": 1005
        }],
        "CTC_c-indexed": [{
            "year": year,
            "value": True
        }],
    })
    cmp_with_taxcalc_values(taxparams, pol)
Exemplo n.º 26
0
def reformat_params():
    """
    Translates ParamTools-style policy_current_law.json
    to a dictionary that resembles the old Tax-Calculator
    parameter schema
    """
    # Parameters that were changed by TCJA will be extended through
    # 2026 in the uguide
    tcja = tc.Policy.read_json_reform(TCJA_PATH)

    pol = tc.Policy()
    pol.clear_state()
    years_short = list(range(START_YEAR, END_YEAR_SHORT))
    years_long = list(range(START_YEAR, END_YEAR_LONG))
    pol.set_year(years_long)
    params = pol.specification(serializable=True, sort_values=True)

    # Create parameter dictionary that resembles old Tax-Calculator
    # parameter schema
    params_dict = {}
    for param in params.keys():
        if param in tcja.keys():
            years = years_long
        else:
            years = years_short
        params_dict[param] = {}
        params_dict[param]['years'] = years
        list_vals2 = []
        for year in years:
            list_vals1 = []
            for idx in range(0, len(params[param])):
                if params[param][idx]['year'] == year:
                    list_vals1.append(params[param][idx]['value'])
                    if (params[param][idx]['year'] !=
                            params[param][idx - 1]['year']):
                        list_vals2.append(list_vals1)
                        params_dict[param]['values'] = list_vals2
    return params_dict
Exemplo n.º 27
0
def test_adj_CPI_offset_and_index_status():
    taxparams = TaxParams()
    taxparams.adjust({
        "CPI_offset": [{
            "year": 2020,
            "value": -0.005
        }],
        "CTC_c-indexed": [{
            "year": 2020,
            "value": True
        }],
    })

    pol = taxcalc.Policy()
    pol.implement_reform({
        "CTC_c-indexed": {
            2020: True
        },
        "CPI_offset": {
            2020: -0.005
        }
    })

    cmp_with_taxcalc_values(taxparams, pol)
Exemplo n.º 28
0
    def __init__(self,
                 start_year,
                 end_year=LAST_BUDGET_YEAR,
                 microdata=None,
                 use_cps=False,
                 reform=None,
                 behavior=None,
                 assump=None,
                 verbose=False):
        """
        Constructor for the TaxBrain class
        Parameters
        ----------
        start_year: First year in the analysis. Must be no earlier than the
                    first year allowed in Tax-Calculator.
        end_year: Last year in the analysis. Must be no later than the last
                  year allowed in Tax-Calculator.
        microdata: Either a path to a micro-data file or a Pandas DataFrame
                   containing micro-data.
        use_cps: A boolean value to indicate whether or not the analysis should
                 be run using the CPS file included in Tax-Calculator.
                 Note: use_cps cannot be True if a file was also specified with
                 the microdata parameter.
        reform: Individual income tax policy reform. Can be either a string
                pointing to a JSON reform file, or the contents of a JSON file.
        behavior: Individual behavior assumptions use by the Behavior-Response
                  package.
        assump: A string pointing to a JSON file containing user specified
                economic assumptions.
        verbose: A boolean value indicated whether or not to write model
                 progress reports.
        """
        if not use_cps and microdata is None:
            raise ValueError("Must specify microdata or set 'use_cps' to True")
        assert isinstance(start_year, int) & isinstance(end_year, int), (
            "Start and end years must be integers")
        assert start_year <= end_year, (
            f"Specified end year, {end_year}, is before specified start year, "
            f"{start_year}.")
        assert TaxBrain.FIRST_BUDGET_YEAR <= start_year, (
            f"Specified start_year, {start_year}, comes before first known "
            f"budget year, {TaxBrain.FIRST_BUDGET_YEAR}.")
        assert end_year <= TaxBrain.LAST_BUDGET_YEAR, (
            f"Specified end_year, {end_year}, comes after last known "
            f"budget year, {TaxBrain.LAST_BUDGET_YEAR}.")
        self.use_cps = use_cps
        self.start_year = start_year
        self.end_year = end_year
        self.base_data = {yr: {} for yr in range(start_year, end_year + 1)}
        self.reform_data = {yr: {} for yr in range(start_year, end_year + 1)}
        self.verbose = verbose

        # Process user inputs early to throw any errors quickly
        self.params = self._process_user_mods(reform, assump)
        self.params["behavior"] = behavior

        # Create two microsimulation calculators
        gd_base = tc.GrowDiff()
        gf_base = tc.GrowFactors()
        # apply user specified growdiff
        if self.params["growdiff_baseline"]:
            gd_base.update_growdiff(self.params["growdiff_baseline"])
            gd_base.apply_to(gf_base)
        # Baseline calculator
        if use_cps:
            records = tc.Records.cps_constructor(data=microdata,
                                                 gfactors=gf_base)
        else:
            records = tc.Records(microdata, gfactors=gf_base)
        self.base_calc = tc.Calculator(policy=tc.Policy(gf_base),
                                       records=records,
                                       verbose=self.verbose)

        # Reform calculator
        # Initialize a policy object
        gd_reform = tc.GrowDiff()
        gf_reform = tc.GrowFactors()
        if self.params["growdiff_response"]:
            gd_reform.update_growdiff(self.params["growdiff_response"])
            gd_reform.apply_to(gf_reform)
        if use_cps:
            records = tc.Records.cps_constructor(data=microdata,
                                                 gfactors=gf_reform)
        else:
            records = tc.Records(microdata, gfactors=gf_reform)
        policy = tc.Policy(gf_reform)
        policy.implement_reform(self.params['policy'])
        # Initialize Calculator
        self.reform_calc = tc.Calculator(policy=policy,
                                         records=records,
                                         verbose=self.verbose)
Exemplo n.º 29
0
def report(tb,
           name=None,
           change_threshold=0.05,
           description=None,
           outdir=None,
           author=None,
           css=None,
           verbose=False):
    """
    Create a PDF report based on TaxBrain results

    Parameters
    ----------
    tb: TaxBrain object
    name: Name you want used for the title of the report
    change_threshold: Percentage change (expressed as a decimal fraction) in
        an aggregate variable for it to be considered notable
    description: A description of the reform being run
    outdir: Output directory
    author: Person or persons to be listed as the author of the report
    css: Path to a CSS file used to format the final report
    verbose: boolean indicating whether or not to write progress as report is
        created
    """
    def format_table(df):
        """
        Apply formatting to a given table
        """
        for col in df.columns:
            df.update(df[col].astype(float).apply("{:,.2f}".format))
        return df

    def export_plot(plot, graph):
        """
        Export a bokeh plot based on Cairo version
        """
        # export graph as a PNG or SVG depending on Cairo version is installed
        # higher quality SVG only works with Cairo version >= 1.15.4
        cairo_version = cairo.cairo_version()
        if cairo_version < 11504:
            filename = f"{graph}_graph.png"
            full_filename = Path(output_path, filename)
            export_png(plot, full_filename)
            print("For a higher quality SVG image file, install Cairo 1.15.4")
        else:
            filename = f"{graph}_graph.svg"
            full_filename = Path(output_path, filename)
            export_svgs(plot, full_filename)

        return filename

    if not tb.has_run:
        tb.run()
    if not name:
        name = f"Policy Report-{date()}"
    if not outdir:
        outdir = "-".join(name)
    if author:
        author = f"Report Prepared by {author.title()}"
    # create directory to hold report contents
    output_path = Path(outdir)
    if not output_path.exists():
        output_path.mkdir()
    # dictionary to hold pieces of the final text
    text_args = {
        "start_year": tb.start_year,
        "end_year": tb.end_year,
        "title": name,
        "date": date(),
        "author": author
    }
    if verbose:
        print("Writing Introduction")
    # find policy areas used in the reform
    pol = tc.Policy()
    pol_meta = pol.metadata()
    pol_areas = set()
    for var in tb.params["policy"].keys():
        area = pol_meta[var]["section_1"]
        if area != "":
            pol_areas.add(area)
    pol_areas = list(pol_areas)
    # add policy areas to the intro text
    text_args["introduction"] = form_intro(pol_areas, description)
    # write final sentance of introduction
    current_law = tb.params["base_policy"]
    text_args["baseline_intro"] = form_baseline_intro(current_law)

    if verbose:
        print("Writing Summary")
    agg_table = tb.weighted_totals("combined")
    rev_change = agg_table.loc["Difference"].sum()
    rev_direction = "increase"
    if rev_change < 0:
        rev_direction = "decrease"
    text_args["rev_direction"] = rev_direction
    text_args["rev_change"] = f"{rev_change:,.0f}"

    # create differences table
    if verbose:
        print("Creating distribution table")
    diff_table = tb.differences_table(tb.start_year, "standard_income_bins",
                                      "combined")
    # find which income bin sees the largest change in tax liability
    largest_change = largest_tax_change(diff_table)
    text_args["largest_change_group"] = largest_change[0]
    text_args["largest_change_str"] = largest_change[1]
    diff_table.columns = tc.DIFF_TABLE_LABELS
    # drop certain columns to save space
    drop_cols = [
        "Share of Overall Change", "Count with Tax Cut",
        "Count with Tax Increase"
    ]
    sub_diff_table = diff_table.drop(columns=drop_cols)

    # convert DataFrame to Markdown table
    diff_table.index.name = "_Income Bin_"
    # apply formatting
    diff_table = format_table(diff_table)
    diff_md = convert_table(sub_diff_table)
    text_args["differences_table"] = diff_md

    # aggregate results
    if verbose:
        print("Compiling aggregate results")
    # format aggregate table
    agg_table *= 1e-9
    agg_table = format_table(agg_table)
    agg_md = convert_table(agg_table)
    text_args["agg_table"] = agg_md

    # aggregate table by tax type
    tax_vars = ["iitax", "payrolltax", "combined"]
    agg_base = tb.multi_var_table(tax_vars, "base")
    agg_reform = tb.multi_var_table(tax_vars, "reform")
    agg_diff = agg_reform - agg_base
    agg_diff.index = ["Income Tax", "Payroll Tax", "Combined"]
    agg_diff *= 1e-9
    agg_diff = format_table(agg_diff)
    text_args["agg_tax_type"] = convert_table(agg_diff)

    # summary of policy changes
    text_args["reform_summary"] = policy_table(tb.params["policy"])

    # policy baseline
    if tb.params["base_policy"]:
        text_args["policy_baseline"] = policy_table(tb.params["base_policy"])
    else:
        text_args["policy_baseline"] = (
            f"This report is based on current law as of {date()}.")

    # notable changes
    if verbose:
        print("Finding notable changes")
    text_args["notable_changes"] = notable_changes(tb, change_threshold)

    # behavioral assumptions
    if verbose:
        print("Compiling assumptions")
    text_args["behavior_assumps"] = behavioral_assumptions(tb)
    # consumption asssumptions
    text_args["consump_assumps"] = consumption_assumptions(tb)
    # growth assumptions
    text_args["growth_assumps"] = growth_assumptions(tb)

    # determine model versions
    text_args["model_versions"] = [{
        "name": "Tax-Brain",
        "release": taxbrain.__version__
    }, {
        "name": "Tax-Calculator",
        "release": tc.__version__
    }, {
        "name": "Behavioral-Responses",
        "release": behresp.__version__
    }]

    # create graphs
    if verbose:
        print("Creating graphs")
    dist_graph = taxbrain.distribution_plot(tb, tb.start_year, width=650)
    dist_graph.background_fill_color = None
    dist_graph.border_fill_color = None
    text_args["distribution_graph"] = export_plot(dist_graph, "dist")

    # differences graph
    diff_graph = taxbrain.differences_plot(tb, "combined", width=640)
    diff_graph.background_fill_color = None
    diff_graph.border_fill_color = None
    text_args["agg_graph"] = export_plot(diff_graph, "difference")

    # fill in the report template
    if verbose:
        print("Compiling report")
    template_path = Path(CUR_PATH, "report_files", "report_template.md")
    report_md = write_text(template_path, **text_args)

    # create PDF and HTML used to create the PDF
    wpdf, html = md_to_pdf(report_md, str(output_path), css)
    # write PDF, markdown files, HTML
    filename = name.replace(" ", "-")
    pdf_path = Path(output_path, f"{filename}.pdf")
    pdf_path.write_bytes(wpdf)
    md_path = Path(output_path, f"{filename}.md")
    md_path.write_text(report_md)
    html_path = Path(output_path, f"{filename}.html")
    html_path.write_text(html)
Exemplo n.º 30
0
def test_bm_corp0(with_response, actual_vs_expect,
                  puf_subsample, tests_path):
    """
    Test BusinessModel corporate results under a corporate-income-tax reform
    using calc_all(response=None) and calc_all(response=zero_elasticities),
    checking that the two sets of results are exactly the same, which is what
    is expected.
    """
    # ensure that expected results in the two with_response cases are the same
    assert filecmp.cmp(os.path.join(tests_path,
                                    'bm_corp0_base_nresp_expect.csv'),
                       os.path.join(tests_path,
                                    'bm_corp0_base_wresp_expect.csv'),
                       shallow=False)
    assert filecmp.cmp(os.path.join(tests_path,
                                    'bm_corp0_refm_nresp_expect.csv'),
                       os.path.join(tests_path,
                                    'bm_corp0_refm_wresp_expect.csv'),
                       shallow=False)
    # specify corporate-income-tax reform policy with these provisions:
    # - apply a 28% corporate tax rate
    # - eliminate bonus depreciation
    # - establish 50% haircut on the deductibility of interest on new debt
    btax_reform = {
        'tau_c': {2018: 0.28},
        'depr_3yr_bonus': {2018: 0.0},
        'depr_5yr_bonus': {2018: 0.0},
        'depr_7yr_bonus': {2018: 0.0},
        'depr_10yr_bonus': {2018: 0.0},
        'depr_15yr_bonus': {2018: 0.0},
        'depr_20yr_bonus': {2018: 0.0},
        'depr_25yr_bonus': {2018: 0.0},
        'depr_275yr_bonus': {2018: 0.0},
        'depr_39yr_bonus': {2018: 0.0},
        'pymtc_hc': {2018: 1.0},
        'newIntPaid_corp_hc': {2018: 1.0},
        'newIntPaid_corp_hcyear': {2018: 2018},
        'oldIntPaid_corp_hc': {2018: 1.0},
        'oldIntPaid_corp_hcyear': {2018: 2018},
        'newIntPaid_noncorp_hc': {2018: 1.0},
        'newIntPaid_noncorp_hcyear': {2018: 2018},
        'oldIntPaid_noncorp_hc': {2018: 1.0},
        'oldIntPaid_noncorp_hcyear': {2018: 2018}
    }
    btax_policy_ref = Policy()
    btax_policy_ref.implement_reform(btax_reform)
    # specify individual-tax reform dictionary with no reform provisions
    itax_reform = {}
    itax_policy_ref = itax.Policy()
    itax_policy_ref.implement_reform(itax_reform)
    # calculate results in different ways depending on value of with_response
    if with_response:
        zero_elast_response = Response()
        zero_elast_response.update_elasticities({})  # all zero elasticities
        bizmod = BusinessModel(btax_policy_ref, itax_policy_ref,
                               investor_data=puf_subsample)
        bizmod.calc_all(response=zero_elast_response)
    else:
        bizmod = BusinessModel(btax_policy_ref, itax_policy_ref,
                               investor_data=puf_subsample)
        bizmod.calc_all(response=None)
    # compare actual and expected results
    resp = 'wresp' if with_response else 'nresp'
    dec = 4
    results = bizmod.corp_base.taxreturn.combined_return.round(dec)
    fname = 'bm_corp0_base_{}_expect.csv'.format(resp)
    actual_vs_expect(results, fname, precision=dec)
    results = bizmod.corp_ref.taxreturn.combined_return.round(dec)
    fname = 'bm_corp0_refm_{}_expect.csv'.format(resp)
    actual_vs_expect(results, fname, precision=dec)