Recipe 4: Estimating Differential Reform ResponseΒΆ

This is an advanced recipe that should be followed only after mastering the basic recipe. This recipe shows how to estimate the reform response in charitable giving when the response elasticities vary by earnings group. It employs the groupby technique used in the Creating a Custom Table recipe, so you might want to read that recipe first.

import taxcalc as tc
import behresp

# use publicly-available CPS input file
recs = tc.Records.cps_constructor()

# specify Calculator object for static analysis of current-law policy
pol = tc.Policy()
calc1 = tc.Calculator(policy=pol, records=recs)

CYR = 2020

# calculate current-law tax liabilities for CYR
calc1.advance_to_year(CYR)
calc1.calc_all()

# calculate marginal tax rate wrt cash charitable giving
(_, _, mtr1) = calc1.mtr('e19800', calc_all_already_called=True,
                         wrt_full_compensation=False)

# specify Calculator object for static analysis of reform policy
# TODO: Move this reform online so it can be read non-locally.
pol.implement_reform(tc.Policy.read_json_reform('github://PSLmodels:[email protected]/docs/recipes/_static/reformB.json'))
calc2 = tc.Calculator(policy=pol, records=recs)

# calculate reform tax liabilities for CYR
calc2.advance_to_year(CYR)
calc2.calc_all()

# calculate marginal tax rate wrt cash charitable giving
(_, _, mtr2) = calc2.mtr('e19800', calc_all_already_called=True,
                         wrt_full_compensation=False)

# extract variables needed for quantity_response function
# (note the aftertax price is 1+mtr because mtr wrt charity is non-positive)
vdf = calc1.dataframe(['s006', 'e19800', 'e00200'])
vdf['price1'] = 1.0 + mtr1
vdf['price2'] = 1.0 + mtr2
vdf['atinc1'] = calc1.array('aftertax_income')
vdf['atinc2'] = calc2.array('aftertax_income')

# group filing units into earnings groups with different response elasticities
# (note earnings groups are just an example based on no empirical results)
earnings_bins = [-9e99, 50e3, 9e99]  # two groups: below and above $50,000
vdf = tc.add_income_table_row_variable(vdf, 'e00200', earnings_bins)
gbydf = vdf.groupby('table_row', as_index=False)

# compute percentage response in charitable giving
# (note elasticity values are just an example based on no empirical results)
PRICE_ELASTICITY = [-0.1, -0.4]
INCOME_ELASTICITY = [0.1, 0.1]
print('\nResponse in Charitable Giving by Earnings Group')
results = '{:18s}\t{:8.3f}\t{:8.3f}\t{:8.2f}'
colhead = '{:18s}\t{:>8s}\t{:>8s}\t{:>8s}'
print(colhead.format('Earnings Group', 'Num(#M)', 'Resp($B)', 'Resp(%)'))
tot_funits = 0.
tot_response = 0.
tot_baseline = 0.
idx = 0
for grp_interval, grp in gbydf:
    funits = grp['s006'].sum() * 1e-6
    tot_funits += funits
    response = behresp.quantity_response(grp['e19800'],
                                         PRICE_ELASTICITY[idx],
                                         grp['price1'],
                                         grp['price2'],
                                         INCOME_ELASTICITY[idx],
                                         grp['atinc1'],
                                         grp['atinc2'])
    grp_response = (response * grp['s006']).sum() * 1e-9
    tot_response += grp_response
    grp_baseline = (grp['e19800'] * grp['s006']).sum() * 1e-9
    tot_baseline += grp_baseline
    pct_response = 100. * grp_response / grp_baseline
    glabel = '[{:.8g}, {:.8g})'.format(grp_interval.left, grp_interval.right)
    print(results.format(glabel, funits, grp_response, pct_response))
    idx += 1
pct_response = 100. * tot_response / tot_baseline
print(results.format('ALL', tot_funits, tot_response, pct_response))
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-2-e94011c2cb6b> in <module>
     13 # calculate current-law tax liabilities for CYR
     14 calc1.advance_to_year(CYR)
---> 15 calc1.calc_all()
     16 
     17 # calculate marginal tax rate wrt cash charitable giving

~/work/Tax-Calculator/Tax-Calculator/taxcalc/calculator.py in calc_all(self, zero_out_calc_vars)
    175         FairShareTax(self.__policy, self.__records)
    176         LumpSumTax(self.__policy, self.__records)
--> 177         ExpandIncome(self.__policy, self.__records)
    178         AfterTaxIncome(self.__policy, self.__records)
    179 

~/work/Tax-Calculator/Tax-Calculator/taxcalc/decorators.py in wrapper(*args, **kwargs)
    323                  {"applied_f": applied_jitted_f}, fakeglobals)
    324             high_level_fn = fakeglobals['hl_func']
--> 325             ans = high_level_fn(*args, **kwargs)
    326             return ans
    327 

<string> in hl_func(pm, pf)

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/dispatcher.py in _compile_for_args(self, *args, **kws)
    370         return_val = None
    371         try:
--> 372             return_val = self.compile(tuple(argtypes))
    373         except errors.ForceLiteralArg as e:
    374             # Received request for compiler re-entry with the list of arguments

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/dispatcher.py in compile(self, sig)
    907                 with ev.trigger_event("numba:compile", data=ev_details):
    908                     try:
--> 909                         cres = self._compiler.compile(args, return_type)
    910                     except errors.ForceLiteralArg as e:
    911                         def folded(args, kws):

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/dispatcher.py in compile(self, args, return_type)
     77 
     78     def compile(self, args, return_type):
---> 79         status, retval = self._compile_cached(args, return_type)
     80         if status:
     81             return retval

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/dispatcher.py in _compile_cached(self, args, return_type)
     91 
     92         try:
---> 93             retval = self._compile_core(args, return_type)
     94         except errors.TypingError as e:
     95             self._failed_cache[key] = e

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/dispatcher.py in _compile_core(self, args, return_type)
    104 
    105         impl = self._get_implementation(args, {})
--> 106         cres = compiler.compile_extra(self.targetdescr.typing_context,
    107                                       self.targetdescr.target_context,
    108                                       impl,

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler.py in compile_extra(typingctx, targetctx, func, args, return_type, flags, locals, library, pipeline_class)
    604     pipeline = pipeline_class(typingctx, targetctx, library,
    605                               args, return_type, flags, locals)
--> 606     return pipeline.compile_extra(func)
    607 
    608 

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler.py in compile_extra(self, func)
    351         self.state.lifted = ()
    352         self.state.lifted_from = None
--> 353         return self._compile_bytecode()
    354 
    355     def compile_ir(self, func_ir, lifted=(), lifted_from=None):

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler.py in _compile_bytecode(self)
    413         """
    414         assert self.state.func_ir is None
--> 415         return self._compile_core()
    416 
    417     def _compile_ir(self):

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler.py in _compile_core(self)
    384             res = None
    385             try:
--> 386                 pm.run(self.state)
    387                 if self.state.cr is not None:
    388                     break

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler_machinery.py in run(self, state)
    328                 pass_inst = _pass_registry.get(pss).pass_inst
    329                 if isinstance(pass_inst, CompilerPass):
--> 330                     self._runPass(idx, pass_inst, state)
    331                 else:
    332                     raise BaseException("Legacy pass in use")

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler_lock.py in _acquire_compile_lock(*args, **kwargs)
     33         def _acquire_compile_lock(*args, **kwargs):
     34             with self:
---> 35                 return func(*args, **kwargs)
     36         return _acquire_compile_lock
     37 

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler_machinery.py in _runPass(self, index, pss, internal_state)
    287             mutated |= check(pss.run_initialization, internal_state)
    288         with SimpleTimer() as pass_time:
--> 289             mutated |= check(pss.run_pass, internal_state)
    290         with SimpleTimer() as finalize_time:
    291             mutated |= check(pss.run_finalizer, internal_state)

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler_machinery.py in check(func, compiler_state)
    260 
    261         def check(func, compiler_state):
--> 262             mangled = func(compiler_state)
    263             if mangled not in (True, False):
    264                 msg = ("CompilerPass implementations should return True/False. "

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/typed_passes.py in run_pass(self, state)
    461 
    462         # TODO: Pull this out into the pipeline
--> 463         NativeLowering().run_pass(state)
    464         lowered = state['cr']
    465         signature = typing.signature(state.return_type, *state.args)

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/typed_passes.py in run_pass(self, state)
    384                 lower.lower()
    385                 if not flags.no_cpython_wrapper:
--> 386                     lower.create_cpython_wrapper(flags.release_gil)
    387 
    388                 if not flags.no_cfunc_wrapper:

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/lowering.py in create_cpython_wrapper(self, release_gil)
    240                                                 self.env, self.call_helper,
    241                                                 release_gil=release_gil)
--> 242         self.context.create_cpython_wrapper(self.library, self.fndesc,
    243                                             self.env, self.call_helper,
    244                                             release_gil=release_gil)

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/cpu.py in create_cpython_wrapper(self, library, fndesc, env, call_helper, release_gil)
    160                                 fndesc, env, call_helper=call_helper,
    161                                 release_gil=release_gil)
--> 162         builder.build()
    163         library.add_ir_module(wrapper_module)
    164 

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/callwrapper.py in build(self)
    120 
    121         api = self.context.get_python_api(builder)
--> 122         self.build_wrapper(api, builder, closure, args, kws)
    123 
    124         return wrapper, api

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/callwrapper.py in build_wrapper(self, api, builder, closure, args, kws)
    153                 innerargs.append(None)
    154             else:
--> 155                 val = cleanup_manager.add_arg(builder.load(obj), ty)
    156                 innerargs.append(val)
    157 

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/callwrapper.py in add_arg(self, obj, ty)
     56             "arg%d.err" % self.arg_count)
     57         with self.builder.goto_block(cleanupblk):
---> 58             cleanup_arg()
     59             # Go to next cleanup block
     60             self.builder.branch(self.nextblk)

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/callwrapper.py in cleanup_arg()
     48             #  may need the native value)
     49             if self.context.enable_nrt:
---> 50                 self.context.nrt.decref(self.builder, ty, native.value)
     51 
     52         self.cleanups.append(cleanup_arg)

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/runtime/context.py in decref(self, builder, typ, value)
    222         Recursively decref the given *value* and its members.
    223         """
--> 224         self._call_incref_decref(builder, typ, value, "NRT_decref")
    225 
    226     def get_nrt_api(self, builder):

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/runtime/context.py in _call_incref_decref(self, builder, typ, value, funcname)
    202         from numba.core.runtime.nrtdynmod import incref_decref_ty
    203 
--> 204         meminfos = self.get_meminfos(builder, typ, value)
    205         for _, mi in meminfos:
    206             mod = builder.module

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/runtime/context.py in get_meminfos(self, builder, ty, val)
    190 
    191         for mtyp, getter in members:
--> 192             field = getter(val)
    193             inner_meminfos = self.get_meminfos(builder, mtyp, field)
    194             meminfos.extend(inner_meminfos)

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/datamodel/models.py in getter(k, value)
    708                 args = self.get_value_type(), value.type
    709                 raise TypeError("expecting {0} but got {1}".format(*args))
--> 710             return self.get(builder, value, k)
    711 
    712         return [(self.get_type(k), partial(getter, k)) for k in self._fields]

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/datamodel/models.py in get(self, builder, val, pos)
    642         if isinstance(pos, str):
    643             pos = self.get_field_position(pos)
--> 644         return builder.extract_value(val, [pos],
    645                                      name="extracted." + self._fields[pos])
    646 

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/llvmlite/ir/builder.py in extract_value(self, agg, idx, name)
    968         if not isinstance(idx, (tuple, list)):
    969             idx = [idx]
--> 970         instr = instructions.ExtractValue(self.block, agg, idx, name=name)
    971         self._insert(instr)
    972         return instr

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/llvmlite/ir/instructions.py in __init__(self, parent, agg, indices, name)
    631                             % (list(indices), agg.type))
    632 
--> 633         super(ExtractValue, self).__init__(parent, typ, "extractvalue",
    634                                            [agg], name=name)
    635 

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/llvmlite/ir/instructions.py in __init__(self, parent, typ, opname, operands, name, flags)
     18         self.operands = operands
     19         self.flags = list(flags)
---> 20         self.metadata = {}
     21 
     22     @property

KeyboardInterrupt: