Exemplo n.º 1
0
def reform_results(reform_dict, puf_data, reform_2017_law):
    """
    Return actual results of the reform specified in reform_dict.
    """
    # pylint: disable=too-many-locals
    rec = Records(data=puf_data)
    # create baseline Calculator object, calc1
    pol = Policy()
    if reform_dict['baseline'] == '2017_law.json':
        pol.implement_reform(reform_2017_law)
    elif reform_dict['baseline'] == 'current_law_policy.json':
        pass
    else:
        msg = 'illegal baseline value {}'
        raise ValueError(msg.format(reform_dict['baseline']))
    calc1 = Calculator(policy=pol, records=rec, verbose=False, behavior=None)
    # create reform Calculator object, calc2, with possible behavioral response
    start_year = reform_dict['start_year']
    beh = Behavior()
    if '_BE_cg' in reform_dict['value']:
        elasticity = reform_dict['value']['_BE_cg']
        del reform_dict['value']['_BE_cg']  # in order to have a valid reform
        beh_assump = {start_year: {'_BE_cg': elasticity}}
        beh.update_behavior(beh_assump)
    reform = {start_year: reform_dict['value']}
    pol.implement_reform(reform)
    calc2 = Calculator(policy=pol, records=rec, verbose=False, behavior=beh)
    # increment both Calculator objects to reform's start_year
    calc1.advance_to_year(start_year)
    calc2.advance_to_year(start_year)
    # calculate prereform and postreform output for several years
    output_type = reform_dict['output_type']
    num_years = 4
    results = list()
    for _ in range(0, num_years):
        calc1.calc_all()
        prereform = calc1.array(output_type)
        if calc2.behavior_has_response():
            calc2_br = Behavior.response(calc1, calc2)
            postreform = calc2_br.array(output_type)
        else:
            calc2.calc_all()
            postreform = calc2.array(output_type)
        diff = postreform - prereform
        weighted_sum_diff = (diff * calc1.array('s006')).sum() * 1.0e-9
        results.append(weighted_sum_diff)
        calc1.increment_year()
        calc2.increment_year()
    # write actual results to actual_str
    actual_str = 'Tax-Calculator'
    for iyr in range(0, num_years):
        actual_str += ',{:.1f}'.format(results[iyr])
    return actual_str
Exemplo n.º 2
0
def test_behavioral_response(puf_subsample):
    """
    Test that behavioral-response results are the same
    when generated from standard Tax-Calculator calls and
    when generated from tbi.run_nth_year_taxcalc_model() calls
    """
    # specify reform and assumptions
    reform_json = """
    {"policy": {
        "_II_rt5": {"2020": [0.25]},
        "_II_rt6": {"2020": [0.25]},
        "_II_rt7": {"2020": [0.25]},
        "_PT_rt5": {"2020": [0.25]},
        "_PT_rt6": {"2020": [0.25]},
        "_PT_rt7": {"2020": [0.25]},
        "_II_em": {"2020": [1000]}
    }}
    """
    assump_json = """
    {"behavior": {"_BE_sub": {"2013": [0.25]}},
     "growdiff_baseline": {},
     "growdiff_response": {},
     "consumption": {},
     "growmodel": {}
    }
    """
    params = Calculator.read_json_param_objects(reform_json, assump_json)
    # specify keyword arguments used in tbi function call
    kwargs = {
        'start_year': 2019,
        'year_n': 0,
        'use_puf_not_cps': True,
        'use_full_sample': False,
        'user_mods': {
            'policy': params['policy'],
            'behavior': params['behavior'],
            'growdiff_baseline': params['growdiff_baseline'],
            'growdiff_response': params['growdiff_response'],
            'consumption': params['consumption'],
            'growmodel': params['growmodel']
        },
        'return_dict': False
    }
    # generate aggregate results two ways: using tbi and standard calls
    num_years = 9
    std_res = dict()
    tbi_res = dict()
    for using_tbi in [True, False]:
        for year in range(0, num_years):
            cyr = year + kwargs['start_year']
            if using_tbi:
                kwargs['year_n'] = year
                tables = run_nth_year_taxcalc_model(**kwargs)
                tbi_res[cyr] = dict()
                for tbl in ['aggr_1', 'aggr_2', 'aggr_d']:
                    tbi_res[cyr][tbl] = tables[tbl]
            else:
                rec = Records(data=puf_subsample)
                pol = Policy()
                calc1 = Calculator(policy=pol, records=rec)
                pol.implement_reform(params['policy'])
                assert not pol.parameter_errors
                beh = Behavior()
                beh.update_behavior(params['behavior'])
                calc2 = Calculator(policy=pol, records=rec, behavior=beh)
                assert calc2.behavior_has_response()
                calc1.advance_to_year(cyr)
                calc2.advance_to_year(cyr)
                calc2 = Behavior.response(calc1, calc2)
                std_res[cyr] = dict()
                for tbl in ['aggr_1', 'aggr_2', 'aggr_d']:
                    if tbl.endswith('_1'):
                        itax = calc1.weighted_total('iitax')
                        ptax = calc1.weighted_total('payrolltax')
                        ctax = calc1.weighted_total('combined')
                    elif tbl.endswith('_2'):
                        itax = calc2.weighted_total('iitax')
                        ptax = calc2.weighted_total('payrolltax')
                        ctax = calc2.weighted_total('combined')
                    elif tbl.endswith('_d'):
                        itax = (calc2.weighted_total('iitax') -
                                calc1.weighted_total('iitax'))
                        ptax = (calc2.weighted_total('payrolltax') -
                                calc1.weighted_total('payrolltax'))
                        ctax = (calc2.weighted_total('combined') -
                                calc1.weighted_total('combined'))
                    cols = ['0_{}'.format(year)]
                    rows = ['ind_tax', 'payroll_tax', 'combined_tax']
                    datalist = [itax, ptax, ctax]
                    std_res[cyr][tbl] = pd.DataFrame(data=datalist,
                                                     index=rows,
                                                     columns=cols)
    # compare the two sets of results
    # NOTE that the tbi results have been "fuzzed" for PUF privacy reasons,
    #      so there is no expectation that the results should be identical.
    no_diffs = True
    reltol = 0.004  # std and tbi differ if more than 0.4 percent different
    for year in range(0, num_years):
        cyr = year + kwargs['start_year']
        col = '0_{}'.format(year)
        for tbl in ['aggr_1', 'aggr_2', 'aggr_d']:
            tbi = tbi_res[cyr][tbl][col]
            std = std_res[cyr][tbl][col]
            if not np.allclose(tbi, std, atol=0.0, rtol=reltol):
                no_diffs = False
                print('**** DIFF for year {} (year_n={}):'.format(cyr, year))
                print('TBI RESULTS:')
                print(tbi)
                print('STD RESULTS:')
                print(std)
    assert no_diffs
Exemplo n.º 3
0
def calculator_objects(year_n, start_year,
                       use_puf_not_cps,
                       use_full_sample,
                       user_mods,
                       behavior_allowed):
    """
    This function assumes that the specified user_mods is a dictionary
      returned by the Calculator.read_json_param_objects() function.
    This function returns (calc1, calc2) where
      calc1 is pre-reform Calculator object calculated for year_n, and
      calc2 is post-reform Calculator object calculated for year_n.
    Set behavior_allowed to False when generating static results or
      set behavior_allowed to True when generating dynamic results.
    """
    # pylint: disable=too-many-arguments,too-many-locals
    # pylint: disable=too-many-branches,too-many-statements

    check_user_mods(user_mods)

    # specify Consumption instance
    consump = Consumption()
    consump_assumptions = user_mods['consumption']
    consump.update_consumption(consump_assumptions)

    # specify growdiff_baseline and growdiff_response
    growdiff_baseline = GrowDiff()
    growdiff_response = GrowDiff()
    growdiff_base_assumps = user_mods['growdiff_baseline']
    growdiff_resp_assumps = user_mods['growdiff_response']
    growdiff_baseline.update_growdiff(growdiff_base_assumps)
    growdiff_response.update_growdiff(growdiff_resp_assumps)

    # create pre-reform and post-reform GrowFactors instances
    growfactors_pre = GrowFactors()
    growdiff_baseline.apply_to(growfactors_pre)
    growfactors_post = GrowFactors()
    growdiff_baseline.apply_to(growfactors_post)
    growdiff_response.apply_to(growfactors_post)

    # create sample pd.DataFrame from specified input file and sampling scheme
    tbi_path = os.path.abspath(os.path.dirname(__file__))
    if use_puf_not_cps:
        # first try TaxBrain deployment path
        input_path = 'puf.csv.gz'
        if not os.path.isfile(input_path):
            # otherwise try local Tax-Calculator deployment path
            input_path = os.path.join(tbi_path, '..', '..', 'puf.csv')
        sampling_frac = 0.05
        sampling_seed = 2222
    else:  # if using cps input not puf input
        # first try Tax-Calculator code path
        input_path = os.path.join(tbi_path, '..', 'cps.csv.gz')
        if not os.path.isfile(input_path):
            # otherwise read from taxcalc package "egg"
            input_path = None  # pragma: no cover
            full_sample = read_egg_csv('cps.csv.gz')  # pragma: no cover
        sampling_frac = 0.03
        sampling_seed = 180
    if input_path:
        full_sample = pd.read_csv(input_path)
    if use_full_sample:
        sample = full_sample
    else:
        sample = full_sample.sample(frac=sampling_frac,
                                    random_state=sampling_seed)

    # create pre-reform Calculator instance
    if use_puf_not_cps:
        recs1 = Records(data=sample,
                        gfactors=growfactors_pre)
    else:
        recs1 = Records.cps_constructor(data=sample,
                                        gfactors=growfactors_pre)
    policy1 = Policy(gfactors=growfactors_pre)
    calc1 = Calculator(policy=policy1, records=recs1, consumption=consump)
    while calc1.current_year < start_year:
        calc1.increment_year()
    calc1.calc_all()
    assert calc1.current_year == start_year

    # specify Behavior instance
    behv = Behavior()
    behavior_assumps = user_mods['behavior']
    behv.update_behavior(behavior_assumps)

    # always prevent both behavioral response and growdiff response
    if behv.has_any_response() and growdiff_response.has_any_response():
        msg = 'BOTH behavior AND growdiff_response HAVE RESPONSE'
        raise ValueError(msg)

    # optionally prevent behavioral response
    if behv.has_any_response() and not behavior_allowed:
        msg = 'A behavior RESPONSE IS NOT ALLOWED'
        raise ValueError(msg)

    # create post-reform Calculator instance
    if use_puf_not_cps:
        recs2 = Records(data=sample,
                        gfactors=growfactors_post)
    else:
        recs2 = Records.cps_constructor(data=sample,
                                        gfactors=growfactors_post)
    policy2 = Policy(gfactors=growfactors_post)
    policy_reform = user_mods['policy']
    policy2.implement_reform(policy_reform)
    calc2 = Calculator(policy=policy2, records=recs2,
                       consumption=consump, behavior=behv)
    while calc2.current_year < start_year:
        calc2.increment_year()
    assert calc2.current_year == start_year

    # delete objects now embedded in calc1 and calc2
    del sample
    del full_sample
    del consump
    del growdiff_baseline
    del growdiff_response
    del growfactors_pre
    del growfactors_post
    del behv
    del recs1
    del recs2
    del policy1
    del policy2

    # increment Calculator objects for year_n years and calculate
    for _ in range(0, year_n):
        calc1.increment_year()
        calc2.increment_year()
    calc1.calc_all()
    if calc2.behavior_has_response():
        calc2 = Behavior.response(calc1, calc2)
    else:
        calc2.calc_all()

    # return calculated Calculator objects
    return (calc1, calc2)
Exemplo n.º 4
0
def test_behavioral_response(use_puf_not_cps, puf_subsample, cps_fullsample):
    """
    Test that behavioral-response results are the same
    when generated from standard Tax-Calculator calls and
    when generated from tbi.run_nth_year_taxcalc_model() calls
    """
    # specify reform and assumptions
    reform_json = """
    {"policy": {
        "_II_rt5": {"2020": [0.25]},
        "_II_rt6": {"2020": [0.25]},
        "_II_rt7": {"2020": [0.25]},
        "_PT_rt5": {"2020": [0.25]},
        "_PT_rt6": {"2020": [0.25]},
        "_PT_rt7": {"2020": [0.25]},
        "_II_em": {"2020": [1000]}
    }}
    """
    assump_json = """
    {"behavior": {"_BE_sub": {"2013": [0.25]}},
     "growdiff_baseline": {},
     "growdiff_response": {},
     "consumption": {},
     "growmodel": {}
    }
    """
    params = Calculator.read_json_param_objects(reform_json, assump_json)
    # specify keyword arguments used in tbi function call
    kwargs = {
        'start_year': 2019,
        'year_n': 0,
        'use_puf_not_cps': use_puf_not_cps,
        'use_full_sample': False,
        'user_mods': {
            'policy': params['policy'],
            'behavior': params['behavior'],
            'growdiff_baseline': params['growdiff_baseline'],
            'growdiff_response': params['growdiff_response'],
            'consumption': params['consumption'],
            'growmodel': params['growmodel']
        },
        'return_dict': False
    }
    # generate aggregate results two ways: using tbi and standard calls
    num_years = 9
    std_res = dict()
    tbi_res = dict()
    if use_puf_not_cps:
        rec = Records(data=puf_subsample)
    else:
        # IMPORTANT: must use same subsample as used in test_cpscsv.py because
        #            that is the subsample used by run_nth_year_taxcalc_model
        std_cps_subsample = cps_fullsample.sample(frac=0.03, random_state=180)
        rec = Records.cps_constructor(data=std_cps_subsample)
    for using_tbi in [True, False]:
        for year in range(0, num_years):
            cyr = year + kwargs['start_year']
            if using_tbi:
                kwargs['year_n'] = year
                tables = run_nth_year_taxcalc_model(**kwargs)
                tbi_res[cyr] = dict()
                for tbl in ['aggr_1', 'aggr_2', 'aggr_d']:
                    tbi_res[cyr][tbl] = tables[tbl]
            else:
                pol = Policy()
                calc1 = Calculator(policy=pol, records=rec)
                pol.implement_reform(params['policy'])
                assert not pol.parameter_errors
                beh = Behavior()
                beh.update_behavior(params['behavior'])
                calc2 = Calculator(policy=pol, records=rec, behavior=beh)
                assert calc2.behavior_has_response()
                calc1.advance_to_year(cyr)
                calc2.advance_to_year(cyr)
                calc2 = Behavior.response(calc1, calc2)
                std_res[cyr] = dict()
                for tbl in ['aggr_1', 'aggr_2', 'aggr_d']:
                    if tbl.endswith('_1'):
                        itax = calc1.weighted_total('iitax')
                        ptax = calc1.weighted_total('payrolltax')
                        ctax = calc1.weighted_total('combined')
                    elif tbl.endswith('_2'):
                        itax = calc2.weighted_total('iitax')
                        ptax = calc2.weighted_total('payrolltax')
                        ctax = calc2.weighted_total('combined')
                    elif tbl.endswith('_d'):
                        itax = (calc2.weighted_total('iitax') -
                                calc1.weighted_total('iitax'))
                        ptax = (calc2.weighted_total('payrolltax') -
                                calc1.weighted_total('payrolltax'))
                        ctax = (calc2.weighted_total('combined') -
                                calc1.weighted_total('combined'))
                    cols = ['0_{}'.format(year)]
                    rows = ['ind_tax', 'payroll_tax', 'combined_tax']
                    datalist = [itax, ptax, ctax]
                    std_res[cyr][tbl] = pd.DataFrame(data=datalist,
                                                     index=rows,
                                                     columns=cols)
                    for col in std_res[cyr][tbl].columns:
                        val = std_res[cyr][tbl][col] * 1e-9
                        std_res[cyr][tbl][col] = round(val, 3)

    # compare the two sets of results
    # NOTE that the PUF tbi results have been "fuzzed" for privacy reasons,
    #      so there is no expectation that those results should be identical.
    no_diffs = True
    cps_dump = False  # setting to True produces dump output and test failure
    if use_puf_not_cps:
        reltol = 0.004  # std and tbi differ if more than 0.4 percent different
        dataset = 'PUF'
        dumping = False
    else:  # CPS results are not "fuzzed", so
        reltol = 1e-9  # std and tbi should be virtually identical
        dataset = 'CPS'
        dumping = cps_dump
    for year in range(0, num_years):
        cyr = year + kwargs['start_year']
        do_dump = bool(dumping and cyr >= 2019 and cyr <= 2020)
        col = '0_{}'.format(year)
        for tbl in ['aggr_1', 'aggr_2', 'aggr_d']:
            tbi = tbi_res[cyr][tbl][col]
            if do_dump:
                txt = 'DUMP of {} {} table for year {}:'
                print(txt.format(dataset, tbl, cyr))
                print(tbi)
            std = std_res[cyr][tbl][col]
            if not np.allclose(tbi, std, atol=0.0, rtol=reltol):
                no_diffs = False
                txt = '***** {} diff in {} table for year {} (year_n={}):'
                print(txt.format(dataset, tbl, cyr, year))
                print('TBI RESULTS:')
                print(tbi)
                print('STD RESULTS:')
                print(std)
    assert no_diffs
    assert not dumping
Exemplo n.º 5
0
def calculate(year_n, start_year, use_puf_not_cps, use_full_sample, user_mods,
              behavior_allowed):
    """
    The calculate function assumes the specified user_mods is a dictionary
      returned by the Calculator.read_json_param_objects() function.
    The function returns (calc1, calc2, mask) where
      calc1 is pre-reform Calculator object calculated for year_n,
      calc2 is post-reform Calculator object calculated for year_n, and
      mask is boolean array marking records with reform-induced iitax diffs
    Set behavior_allowed to False when generating static results or
      set behavior_allowed to True when generating dynamic results.
    """
    # pylint: disable=too-many-arguments,too-many-locals
    # pylint: disable=too-many-branches,too-many-statements

    check_user_mods(user_mods)

    # specify Consumption instance
    consump = Consumption()
    consump_assumptions = user_mods['consumption']
    consump.update_consumption(consump_assumptions)

    # specify growdiff_baseline and growdiff_response
    growdiff_baseline = Growdiff()
    growdiff_response = Growdiff()
    growdiff_base_assumps = user_mods['growdiff_baseline']
    growdiff_resp_assumps = user_mods['growdiff_response']
    growdiff_baseline.update_growdiff(growdiff_base_assumps)
    growdiff_response.update_growdiff(growdiff_resp_assumps)

    # create pre-reform and post-reform Growfactors instances
    growfactors_pre = Growfactors()
    growdiff_baseline.apply_to(growfactors_pre)
    growfactors_post = Growfactors()
    growdiff_baseline.apply_to(growfactors_post)
    growdiff_response.apply_to(growfactors_post)

    # create sample pd.DataFrame from specified input file and sampling scheme
    stime = time.time()
    tbi_path = os.path.abspath(os.path.dirname(__file__))
    if use_puf_not_cps:
        # first try TaxBrain deployment path
        input_path = 'puf.csv.gz'
        if not os.path.isfile(input_path):
            # otherwise try local Tax-Calculator deployment path
            input_path = os.path.join(tbi_path, '..', '..', 'puf.csv')
        sampling_frac = 0.05
        sampling_seed = 180
    else:  # if using cps input not puf input
        # first try Tax-Calculator code path
        input_path = os.path.join(tbi_path, '..', 'cps.csv.gz')
        if not os.path.isfile(input_path):
            # otherwise read from taxcalc package "egg"
            input_path = None  # pragma: no cover
            full_sample = read_egg_csv('cps.csv.gz')  # pragma: no cover
        sampling_frac = 0.03
        sampling_seed = 180
    if input_path:
        full_sample = pd.read_csv(input_path)
    if use_full_sample:
        sample = full_sample
    else:
        sample = full_sample.sample(  # pylint: disable=no-member
            frac=sampling_frac,
            random_state=sampling_seed)
    if use_puf_not_cps:
        print('puf-read-time= {:.1f}'.format(time.time() - stime))
    else:
        print('cps-read-time= {:.1f}'.format(time.time() - stime))

    # create pre-reform Calculator instance
    if use_puf_not_cps:
        recs1 = Records(data=copy.deepcopy(sample), gfactors=growfactors_pre)
    else:
        recs1 = Records.cps_constructor(data=copy.deepcopy(sample),
                                        gfactors=growfactors_pre)
    policy1 = Policy(gfactors=growfactors_pre)
    calc1 = Calculator(policy=policy1, records=recs1, consumption=consump)
    while calc1.current_year < start_year:
        calc1.increment_year()
    calc1.calc_all()
    assert calc1.current_year == start_year

    # compute mask array
    res1 = calc1.dataframe(DIST_VARIABLES)
    if use_puf_not_cps:
        # create pre-reform Calculator instance with extra income
        recs1p = Records(data=copy.deepcopy(sample), gfactors=growfactors_pre)
        # add one dollar to the income of each filing unit to determine
        # which filing units undergo a resulting change in tax liability
        recs1p.e00200 += 1.0  # pylint: disable=no-member
        recs1p.e00200p += 1.0  # pylint: disable=no-member
        policy1p = Policy(gfactors=growfactors_pre)
        # create Calculator with recs1p and calculate for start_year
        calc1p = Calculator(policy=policy1p,
                            records=recs1p,
                            consumption=consump)
        while calc1p.current_year < start_year:
            calc1p.increment_year()
        calc1p.calc_all()
        assert calc1p.current_year == start_year
        # compute mask showing which of the calc1 and calc1p results differ;
        # mask is true if a filing unit's income tax liability changed after
        # a dollar was added to the filing unit's wage and salary income
        res1p = calc1p.dataframe(DIST_VARIABLES)
        mask = np.logical_not(  # pylint: disable=no-member
            np.isclose(res1.iitax, res1p.iitax, atol=0.001, rtol=0.0))
        assert np.any(mask)
    else:  # if use_cps_not_cps is False
        # indicate that no fuzzing of reform results is required
        mask = np.zeros(res1.shape[0], dtype=np.int8)

    # specify Behavior instance
    behv = Behavior()
    behavior_assumps = user_mods['behavior']
    behv.update_behavior(behavior_assumps)

    # always prevent both behavioral response and growdiff response
    if behv.has_any_response() and growdiff_response.has_any_response():
        msg = 'BOTH behavior AND growdiff_response HAVE RESPONSE'
        raise ValueError(msg)

    # optionally prevent behavioral response
    if behv.has_any_response() and not behavior_allowed:
        msg = 'A behavior RESPONSE IS NOT ALLOWED'
        raise ValueError(msg)

    # create post-reform Calculator instance
    if use_puf_not_cps:
        recs2 = Records(data=copy.deepcopy(sample), gfactors=growfactors_post)
    else:
        recs2 = Records.cps_constructor(data=copy.deepcopy(sample),
                                        gfactors=growfactors_post)
    policy2 = Policy(gfactors=growfactors_post)
    policy_reform = user_mods['policy']
    policy2.implement_reform(policy_reform)
    calc2 = Calculator(policy=policy2,
                       records=recs2,
                       consumption=consump,
                       behavior=behv)
    while calc2.current_year < start_year:
        calc2.increment_year()
    calc2.calc_all()
    assert calc2.current_year == start_year

    # increment Calculator objects for year_n years and calculate
    for _ in range(0, year_n):
        calc1.increment_year()
        calc2.increment_year()
    calc1.calc_all()
    if calc2.behavior_has_response():
        calc2 = Behavior.response(calc1, calc2)
    else:
        calc2.calc_all()

    # return calculated Calculator objects and mask
    return (calc1, calc2, mask)
Exemplo n.º 6
0
def test_behavioral_response_calculator(cps_subsample):
    # create Records object
    rec = Records.cps_constructor(data=cps_subsample)
    year = rec.current_year
    # create Policy object
    pol = Policy()
    # create current-law Calculator object
    calc1 = Calculator(policy=pol, records=rec)
    # implement policy reform
    reform = {year: {'_II_rt7': [0.496], '_PT_rt7': [0.496]}}
    pol.implement_reform(reform)
    # create reform Calculator object with no behavioral response
    behv = Behavior()
    calc2 = Calculator(policy=pol, records=rec, behavior=behv)
    # test incorrect use of Behavior._mtr12 method
    with pytest.raises(ValueError):
        Behavior._mtr12(calc1, calc2, mtr_of='e00200p', tax_type='nonsense')
    # vary substitution and income effects in Behavior object
    behavior0 = {year: {'_BE_sub': [0.0], '_BE_cg': [0.0]}}
    behv0 = Behavior()
    behv0.update_behavior(behavior0)
    calc2 = Calculator(policy=pol, records=rec, behavior=behv0)
    assert calc2.behavior_has_response() is False
    calc2_behv0 = Behavior.response(calc1, calc2)
    behavior1 = {year: {'_BE_sub': [0.3], '_BE_inc': [-0.1], '_BE_cg': [0.0]}}
    behv1 = Behavior()
    behv1.update_behavior(behavior1)
    calc2 = Calculator(policy=pol, records=rec, behavior=behv1)
    assert calc2.behavior_has_response() is True
    epsilon = 1e-9
    assert abs(calc2.behavior('BE_sub') - 0.3) < epsilon
    calc2.behavior('BE_sub', 0.3)
    assert abs(calc2.behavior('BE_sub') - 0.3) < epsilon
    assert abs(calc2.behavior('BE_inc') - -0.1) < epsilon
    assert abs(calc2.behavior('BE_cg') - 0.0) < epsilon
    calc2_behv1 = Behavior.response(calc1, calc2)
    behavior2 = {year: {'_BE_sub': [0.5], '_BE_cg': [-0.8]}}
    behv2 = Behavior()
    behv2.update_behavior(behavior2)
    calc2 = Calculator(policy=pol, records=rec, behavior=behv2)
    assert calc2.behavior_has_response() is True
    calc2_behv2 = Behavior.response(calc1, calc2, trace=True)
    behavior3 = {year: {'_BE_inc': [-0.2], '_BE_cg': [-0.8]}}
    behv3 = Behavior()
    behv3.update_behavior(behavior3)
    calc2 = Calculator(policy=pol, records=rec, behavior=behv3)
    assert calc2.behavior_has_response() is True
    calc2_behv3 = Behavior.response(calc1, calc2)
    behavior4 = {year: {'_BE_cg': [-0.8]}}
    behv4 = Behavior()
    behv4.update_behavior(behavior4)
    calc2 = Calculator(policy=pol, records=rec, behavior=behv4)
    assert calc2.behavior_has_response() is True
    calc2_behv4 = Behavior.response(calc1, calc2)
    # check that total income tax liability differs across the
    # five sets of behavioral-response elasticities
    assert (calc2_behv0.weighted_total('iitax') !=
            calc2_behv1.weighted_total('iitax') !=
            calc2_behv2.weighted_total('iitax') !=
            calc2_behv3.weighted_total('iitax') !=
            calc2_behv4.weighted_total('iitax'))