Skip to content

Commit

Permalink
Merge pull request #1520 from martinholmer/generalize-suffix-logic
Browse files Browse the repository at this point in the history
Generalize policy parameter suffix logic to handle non-default baseline Growdiff
  • Loading branch information
martinholmer authored Aug 22, 2017
2 parents 587d692 + 01e49ce commit 2ce1335
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 38 deletions.
2 changes: 2 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Release 0.9.3 on 2017-??-??
by Martin Holmer]
- Add option to use policy parameter suffixes in JSON reform files
[[#1505](https://github.com/open-source-economics/Tax-Calculator/pull/1505)
by Martin Holmer] and
[[#1520](https://github.com/open-source-economics/Tax-Calculator/pull/1520)
by Martin Holmer]
- Add rounding of wage-inflated or price-inflated parameter values to nearest cent
[[#1506](https://github.com/open-source-economics/Tax-Calculator/pull/1506)
Expand Down
37 changes: 20 additions & 17 deletions taxcalc/calculate.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,20 +374,7 @@ def read_json_param_files(reform_filename, assump_filename,
in which case the file reading is skipped and the read_json_*_text
method is called.
"""
# process first reform parameter
if reform_filename is None:
rpol_dict = dict()
elif isinstance(reform_filename, str):
if os.path.isfile(reform_filename):
txt = open(reform_filename, 'r').read()
else:
txt = reform_filename
rpol_dict = (
Calculator._read_json_policy_reform_text(txt,
arrays_not_lists))
else:
raise ValueError('reform_filename is neither None nor str')
# process second assump parameter
# first process second assump parameter
if assump_filename is None:
cons_dict = dict()
behv_dict = dict()
Expand All @@ -406,7 +393,21 @@ def read_json_param_files(reform_filename, assump_filename,
arrays_not_lists))
else:
raise ValueError('assump_filename is neither None nor str')
# construct and return single composite dictionary
# next process first reform parameter
if reform_filename is None:
rpol_dict = dict()
elif isinstance(reform_filename, str):
if os.path.isfile(reform_filename):
txt = open(reform_filename, 'r').read()
else:
txt = reform_filename
rpol_dict = (
Calculator._read_json_policy_reform_text(txt,
arrays_not_lists,
gdiff_base_dict))
else:
raise ValueError('reform_filename is neither None nor str')
# finally construct and return single composite dictionary
param_dict = dict()
param_dict['policy'] = rpol_dict
param_dict['consumption'] = cons_dict
Expand Down Expand Up @@ -495,7 +496,8 @@ def _calc_one_year(self, zero_out_calc_vars=False):
IITAX(self.policy, self.records)

@staticmethod
def _read_json_policy_reform_text(text_string, arrays_not_lists):
def _read_json_policy_reform_text(text_string, arrays_not_lists,
growdiff_baseline_dict):
"""
Strip //-comments from text_string and return 1 dict based on the JSON.
Expand Down Expand Up @@ -547,7 +549,8 @@ def _read_json_policy_reform_text(text_string, arrays_not_lists):
msg = 'key "{}" should be in economic assumption file'
raise ValueError(msg.format(rkey))
# convert raw_dict['policy'] dictionary into prdict
tdict = Policy.translate_json_reform_suffixes(raw_dict['policy'])
tdict = Policy.translate_json_reform_suffixes(raw_dict['policy'],
growdiff_baseline_dict)
prdict = Calculator._convert_parameter_dict(tdict, arrays_not_lists)
return prdict

Expand Down
5 changes: 2 additions & 3 deletions taxcalc/growdiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
# pylint --disable=locally-disabled growdiff.py

import numpy as np
from taxcalc.policy import Policy
from taxcalc.parameters import ParametersBase


Expand Down Expand Up @@ -41,9 +40,9 @@ class Growdiff(ParametersBase):
class instance: Growdiff
"""

JSON_START_YEAR = Policy.JSON_START_YEAR
JSON_START_YEAR = 2013 # must be same as Policy.JSON_START_YEAR
DEFAULTS_FILENAME = 'growdiff.json'
DEFAULT_NUM_YEARS = Policy.DEFAULT_NUM_YEARS
DEFAULT_NUM_YEARS = 14 # must be same as Policy.DEFAULT_NUM_YEARS

def __init__(self, growdiff_dict=None,
start_year=JSON_START_YEAR,
Expand Down
31 changes: 21 additions & 10 deletions taxcalc/policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import numpy as np
from taxcalc.parameters import ParametersBase
from taxcalc.growfactors import Growfactors
from taxcalc.growdiff import Growdiff


class Policy(ParametersBase):
Expand Down Expand Up @@ -54,15 +55,18 @@ class instance: Policy
DEFAULT_NUM_YEARS = LAST_BUDGET_YEAR - JSON_START_YEAR + 1

def __init__(self,
gfactors=Growfactors(),
gfactors=None,
parameter_dict=None,
start_year=JSON_START_YEAR,
num_years=DEFAULT_NUM_YEARS):
super(Policy, self).__init__()

if not isinstance(gfactors, Growfactors):
raise ValueError('gfactors is not a Growfactors instance')
self._gfactors = gfactors
if gfactors is None:
self._gfactors = Growfactors()
elif isinstance(gfactors, Growfactors):
self._gfactors = gfactors
else:
raise ValueError('gfactors is not None or a Growfactors instance')

if parameter_dict is None: # read default parameters
self._vals = self._params_dict_from_json_file()
Expand All @@ -76,8 +80,8 @@ def __init__(self,

syr = start_year
lyr = start_year + num_years - 1
self._inflation_rates = gfactors.price_inflation_rates(syr, lyr)
self._wage_growth_rates = gfactors.wage_growth_rates(syr, lyr)
self._inflation_rates = self._gfactors.price_inflation_rates(syr, lyr)
self._wage_growth_rates = self._gfactors.wage_growth_rates(syr, lyr)

self.initialize(start_year, num_years)

Expand Down Expand Up @@ -227,7 +231,7 @@ def current_law_version(self):
}

@staticmethod
def translate_json_reform_suffixes(indict):
def translate_json_reform_suffixes(indict, growdiff_baseline_param_dict):
"""
Replace any array parameters with suffixes in the specified
JSON-derived "policy" dictionary, indict, and
Expand Down Expand Up @@ -271,11 +275,18 @@ def suffix_group_dict(idict):
return gdict

# define with_suffix function used only in this method
def with_suffix(gdict):
def with_suffix(gdict, growdiff_baseline_param_dict):
"""
Return param_base:year dictionary having only suffix parameters.
"""
pol = Policy()
if bool(growdiff_baseline_param_dict):
gdiff_baseline = Growdiff()
gdiff_baseline.update_growdiff(growdiff_baseline_param_dict)
growfactors = Growfactors()
gdiff_baseline.apply_to(growfactors)
else:
growfactors = None
pol = Policy(gfactors=growfactors)
odict = dict()
for param in gdict.keys():
odict[param] = dict()
Expand All @@ -298,7 +309,7 @@ def with_suffix(gdict):
gdict = suffix_group_dict(indict)
# - add to odict the consolidated values for parameters with a suffix
if len(gdict) > 0:
odict.update(with_suffix(gdict))
odict.update(with_suffix(gdict, growdiff_baseline_param_dict))
# - return policy dictionary containing constructed parameter arrays
return odict

Expand Down
20 changes: 14 additions & 6 deletions taxcalc/tests/test_calculate.py
Original file line number Diff line number Diff line change
Expand Up @@ -697,19 +697,27 @@ def test_translate_json_reform_suffixes_mars_indexed():
"_STD_single": {"2018": [18000], "2016": [16000]},
"_STD_widow": {"2017": [17000], "2019": [19000]}
}}"""
assump_json = """{
"consumption": {},
"behavior": {},
"growdiff_baseline": {
"_ACPIU": {"2013": [0.01]},
"_AWAGE": {"2013": [0.01]}},
"growdiff_response": {}
}"""
pdict1 = Calculator.read_json_param_files(reform_filename=json1,
assump_filename=None,
assump_filename=assump_json,
arrays_not_lists=True)
rdict1 = pdict1['policy']
json2 = """{"policy": {
"_STD": {"2016": [[16000.00, 12600.00, 6300.00, 9300.00, 12600.00]],
"2017": [[16364.80, 12887.28, 6443.64, 9512.04, 17000.00]],
"2018": [[18000.00, 13173.38, 6586.69, 9723.21, 17377.40]],
"2019": [[18412.20, 13475.05, 6737.52, 9945.87, 19000.00]]},
"_STD": {"2016": [[16000.00, 12600.00, 6300.00, 9300.00, 12600.00]],
"2017": [[16524.80, 13013.28, 6506.64, 9605.04, 17000.00]],
"2018": [[18000.00, 13432.31, 6716.15, 9914.32, 17547.40]],
"2019": [[18592.20, 13874.23, 6937.11, 10240.50, 19000.00]]},
"_II_em": {"2020": [20000], "2015": [15000]}
}}"""
pdict2 = Calculator.read_json_param_files(reform_filename=json2,
assump_filename=None,
assump_filename=assump_json,
arrays_not_lists=True)
rdict2 = pdict2['policy']
assert len(rdict2) == len(rdict1)
Expand Down
7 changes: 6 additions & 1 deletion taxcalc/tests/test_growdiff.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
from numpy.testing import assert_allclose
import pytest
from taxcalc import Growdiff, Growfactors
from taxcalc import Growdiff, Growfactors, Policy


def test_year_consistency():
assert Growdiff.JSON_START_YEAR == Policy.JSON_START_YEAR
assert Growdiff.DEFAULT_NUM_YEARS == Policy.DEFAULT_NUM_YEARS


def test_incorrect_growdiff_ctor():
Expand Down
5 changes: 4 additions & 1 deletion taxcalc/tests/test_reforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@ def test_reform_json(tests_path):
jpf_text = jfile.read()
# check that jpf_text has "policy" that can be implemented as a reform
if '"policy"' in jpf_text:
arrays_not_lists = True
growdiff_dict = {}
# pylint: disable=protected-access
policy_dict = (
Calculator._read_json_policy_reform_text(jpf_text,
arrays_not_lists=True)
arrays_not_lists,
growdiff_dict)
)
policy = Policy()
policy.implement_reform(policy_dict)
Expand Down

0 comments on commit 2ce1335

Please sign in to comment.