Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revise Parameters class code, documentation, and tests #321

Closed
wants to merge 33 commits into from
Closed
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
26aabdc
Edit test_parameters.py to make all lines be less than 80 characters
martinholmer Jul 19, 2015
129dfd9
Incorporate changes in test.py from testpy-edit branch.
martinholmer Jul 19, 2015
9d54a39
Revise taxcalc/parameters.py so that almost all pylint warnings are
martinholmer Jul 19, 2015
caa3faf
Use Parameters.default_inflation_rate classmethod to eliminate several
martinholmer Jul 19, 2015
51bc21a
Add Sphinx-aware docstrings to taxcalc/parameters.py file.
martinholmer Jul 20, 2015
06b8453
Merge in recent changes on master branch.
martinholmer Jul 20, 2015
cfde59e
Revise Sphinx-aware docstring of Parameters class update method to
martinholmer Jul 22, 2015
15f38aa
Initial version of reform_test.py script used to test Parameters class
martinholmer Jul 24, 2015
08ea20c
Add brief period of no indexing for a one-dimensional parameter.
martinholmer Jul 24, 2015
f09fe45
Merge in recent master changes.
martinholmer Jul 24, 2015
768454c
Merge in parameters-edit2 branch.
martinholmer Jul 24, 2015
41112a7
Add Parameters.update method year_mods parameter checking logic.
martinholmer Jul 24, 2015
9041280
Enforce in Parameters.update method the requirement that the year in
martinholmer Jul 24, 2015
2c72455
Enforce in Parameters.update method the requirement that the year in
martinholmer Jul 24, 2015
666f271
Further simplification of Parameters.update method logic.
martinholmer Jul 25, 2015
9ca289c
Eliminate checking if year greater than last_year in Parameters.updat…
martinholmer Jul 25, 2015
0e1da4f
Additional code revisions to Parameters.update method.
martinholmer Jul 25, 2015
5a984b2
Use getattr() in reform_test.py to improve access to Parameters object
martinholmer Jul 25, 2015
1c21659
Add to Parameters class an end_year property and check in set_year()
martinholmer Jul 25, 2015
01bb789
Revise reform_test.py to have only one-dimensional plus
martinholmer Jul 25, 2015
e05bcb0
Fix Parameters.update(year_mods) logic when year is greater than
martinholmer Jul 25, 2015
aaa6ead
Use numpy.allclose() in reform_test.py check_eitc_c() function.
martinholmer Jul 25, 2015
ccfd133
Simplify corrected Parameters.update() logic.
martinholmer Jul 25, 2015
13bb5c0
Tentative fix to expand_2D() function in taxcalc/utils.py that
martinholmer Jul 25, 2015
0e8174f
Fix bugs in three tests in the test_utils.py file.
martinholmer Jul 26, 2015
eb1b757
Fix error in test_calculate.py test_Calculator_create_difference_table
martinholmer Jul 26, 2015
f2f29e5
Fix error in test_calculate.py test_Calculator_user_mods_with_cpi_flags
martinholmer Jul 26, 2015
32d5084
Fix error in test_calculate.py test_make_Calculator_user_mods_as_dict
martinholmer Jul 26, 2015
6686ab3
Fix docstring for the Parameters class update method so that it
martinholmer Jul 26, 2015
0025c64
Fix Parameters.update statement so that it works in Python 3.4 as well
martinholmer Jul 26, 2015
914488b
Improve Sphinx-aware docstrings in Parameters class.
martinholmer Jul 27, 2015
8c2ad87
Move ./reform_test.py code into test_parameters.py file.
martinholmer Jul 29, 2015
bc2be83
Remove ./reform_test.py file.
martinholmer Jul 29, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
241 changes: 241 additions & 0 deletions reform_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like some reasonable tests here, although it seems to me that this is testing the same functionality for various parameters. The maintenance burden goes up for each hard coded value that is tested (since it could change in the future), but if you think there is benefit in keeping all of the functions here, no problem. The right place for this file is taxcalc/tests/test_reform.py (or really anything starting with test_, with each of the functions prefaced with test_ so that py.test will run them each time.

Test implementation of complex reform in OSPC Tax-Calculator Parameters class.

SCRIPT USAGE: tax-calculator$ python reform_test.py

PYLINT USAGE: pylint --disable=locally-disabled reform_test.py
"""
import numpy as np
from numpy.testing import assert_array_equal
from taxcalc.parameters import Parameters
from taxcalc.utils import expand_array


def main():
"""
Test implementation of multi-year reform.
"""
# specify dimensions of policy Parameters object
syr = 2013
nyrs = 10

# specify assumed inflation rates
irates = {2013: 0.02, 2014: 0.02, 2015: 0.02, 2016: 0.03, 2017: 0.03,
2018: 0.04, 2019: 0.04, 2020: 0.04, 2021: 0.04, 2022: 0.04}
ifactors = {}
for i in range(0, nyrs):
ifactors[syr + i] = 1.0 + irates[syr + i]
iratelist = [irates[syr + i] for i in range(0, nyrs)]

# instantiate policy Parameters object
ppo = Parameters(start_year=syr, budget_years=nyrs, inflation_rates=irates)

# confirm that parameters have current-law values
assert_array_equal(getattr(ppo, '_AMT_thd_MarriedS'),
expand_array(np.array( #pylint: disable=no-member
[40400, 41050]),
inflate=True, inflation_rates=iratelist,
num_years=nyrs))
assert_array_equal(getattr(ppo, '_EITC_c'),
expand_array(np.array( #pylint: disable=no-member
[[487, 3250, 5372, 6044],
[496, 3305, 5460, 6143],
[503, 3359, 5548, 6242]]),
inflate=True, inflation_rates=iratelist,
num_years=nyrs))
assert_array_equal(getattr(ppo, '_II_em'),
expand_array(np.array( #pylint: disable=no-member
[3900, 3950, 4000]),
inflate=True, inflation_rates=iratelist,
num_years=nyrs))
assert_array_equal(getattr(ppo, '_SS_Earnings_c'),
expand_array(np.array( #pylint: disable=no-member
[113700, 117000, 118500]),
inflate=True, inflation_rates=iratelist,
num_years=nyrs))

# specify multi-year reform using a dictionary of year_provisions dicts
reform = {
2015: {
'_AMT_thd_MarriedS': [60000]
},
2016: {
'_EITC_c': [[900, 5000, 8000, 9000]],
'_II_em': [7000],
'_SS_Earnings_c': [300000]
},
2017: {
'_AMT_thd_MarriedS': [80000],
'_SS_Earnings_c': [500000], '_SS_Earnings_c_cpi': False
},
2019: {
'_EITC_c': [[1200, 7000, 10000, 12000]],
'_II_em': [9000],
'_SS_Earnings_c': [700000], '_SS_Earnings_c_cpi': True
}
}

# implement reform using Parameters class update method
# Note: apparently must pass update method a YEAR:PROVISIONS dictionary,
# where YEAR must equal Parameters current_year (although update
# method does not enforce this requirement). The PROVISIONS are
# a dictionary of parameter:value pairs (as in the reform
# dictionary above).
assert ppo.current_year == syr
reform_years_list = reform.keys()
last_reform_year = max(reform_years_list)
assert last_reform_year == 2019
while ppo.current_year < last_reform_year:
ppo.increment_year()
if ppo.current_year in reform_years_list:
year_provisions = {ppo.current_year: reform[ppo.current_year]}
ppo.update(year_provisions)

# move policy Parameters object back in time so current_year is syr+2
# Note: this would be typical usage because the first budget year
# is greater than Parameters start_year.
ppo._current_year = ppo.start_year #pylint: disable=protected-access
ppo.set_year(ppo.start_year)
assert ppo.current_year == ppo.start_year
ppo.increment_year()
ppo.increment_year()
assert ppo.current_year == syr + 2

# confirm that actual parameters have expected post-reform values
check_amt_thd_marrieds(ppo, reform, ifactors)
check_eitc_c(ppo, reform, ifactors)
check_ii_em(ppo, reform, ifactors)
check_ss_earnings_c(ppo, reform, ifactors)

# normal return
return 0


def check_amt_thd_marrieds(ppo, reform, ifactor):
"""
Compare actual and expected _AMT_thd_MarriedS parameter values.
"""
actual = {}
arr = getattr(ppo, '_AMT_thd_MarriedS')
for i in range(0, ppo.budget_years):
actual[ppo.start_year + i] = arr[i]
assert actual[2013] == 40400
assert actual[2014] == 41050
e2015 = reform[2015]['_AMT_thd_MarriedS'][0]
assert actual[2015] == e2015
e2016 = int(round(ifactor[2016] * actual[2015]))
assert actual[2016] == e2016
e2017 = reform[2017]['_AMT_thd_MarriedS'][0]
assert actual[2017] == e2017
e2018 = int(round(ifactor[2018] * actual[2017]))
assert actual[2018] == e2018
e2019 = int(round(ifactor[2019] * actual[2018]))
assert actual[2019] == e2019
e2020 = int(round(ifactor[2020] * actual[2019]))
assert actual[2020] == e2020
e2021 = int(round(ifactor[2021] * actual[2020]))
absdiff = abs(actual[2021] - e2021)
if absdiff <= 1:
pass # close enough for government work
else:
assert actual[2021] == e2021
e2022 = int(round(ifactor[2022] * actual[2021]))
assert actual[2022] == e2022


def check_eitc_c(ppo, reform, ifactor):
"""
Compare actual and expected _EITC_c parameter values.
"""
actual = {}
arr = getattr(ppo, '_EITC_c')
alen = len(arr[0])
for i in range(0, ppo.budget_years):
actual[ppo.start_year + i] = arr[i]
assert_array_equal(actual[2013], [487, 3250, 5372, 6044])
assert_array_equal(actual[2014], [496, 3305, 5460, 6143])
assert_array_equal(actual[2015], [503, 3359, 5548, 6242])
e2016 = reform[2016]['_EITC_c'][0]
assert_array_equal(actual[2016], e2016)
e2017 = [int(round(ifactor[2017] * actual[2016][j]))
for j in range(0, alen)]
assert_array_equal(actual[2017], e2017)
e2018 = [int(round(ifactor[2018] * actual[2017][j]))
for j in range(0, alen)]
assert np.allclose(actual[2018], e2018, #pylint: disable=no-member
rtol=0.0, atol=1.0)
e2019 = reform[2019]['_EITC_c'][0]
assert_array_equal(actual[2019], e2019)
e2020 = [int(round(ifactor[2020] * actual[2019][j]))
for j in range(0, alen)]
assert_array_equal(actual[2020], e2020)
e2021 = [int(round(ifactor[2021] * actual[2020][j]))
for j in range(0, alen)]
assert np.allclose(actual[2021], e2021, #pylint: disable=no-member
rtol=0.0, atol=1.0)
e2022 = [int(round(ifactor[2022] * actual[2021][j]))
for j in range(0, alen)]
assert np.allclose(actual[2022], e2022, #pylint: disable=no-member
rtol=0.0, atol=1.0)


def check_ii_em(ppo, reform, ifactor):
"""
Compare actual and expected _II_em parameter values.
"""
actual = {}
arr = getattr(ppo, '_II_em')
for i in range(0, ppo.budget_years):
actual[ppo.start_year + i] = arr[i]
assert actual[2013] == 3900
assert actual[2014] == 3950
assert actual[2015] == 4000
e2016 = reform[2016]['_II_em'][0]
assert actual[2016] == e2016
e2017 = int(round(ifactor[2017] * actual[2016]))
assert actual[2017] == e2017
e2018 = int(round(ifactor[2018] * actual[2017]))
assert actual[2018] == e2018
e2019 = reform[2019]['_II_em'][0]
assert actual[2019] == e2019
e2020 = int(round(ifactor[2020] * actual[2019]))
assert actual[2020] == e2020
e2021 = int(round(ifactor[2021] * actual[2020]))
assert actual[2021] == e2021
e2022 = int(round(ifactor[2022] * actual[2021]))
assert actual[2022] == e2022


def check_ss_earnings_c(ppo, reform, ifactor):
"""
Compare actual and expected _SS_Earnings_c parameter values.
"""
actual = {}
arr = getattr(ppo, '_SS_Earnings_c')
for i in range(0, ppo.budget_years):
actual[ppo.start_year + i] = arr[i]
assert actual[2013] == 113700
assert actual[2014] == 117000
assert actual[2015] == 118500
e2016 = reform[2016]['_SS_Earnings_c'][0]
assert actual[2016] == e2016
e2017 = reform[2017]['_SS_Earnings_c'][0]
assert actual[2017] == e2017
e2018 = actual[2017] # no indexing after 2017
assert actual[2018] == e2018
e2019 = reform[2019]['_SS_Earnings_c'][0]
assert actual[2019] == e2019
e2020 = int(round(ifactor[2020] * actual[2019])) # indexing after 2019
assert actual[2020] == e2020
e2021 = int(round(ifactor[2021] * actual[2020]))
assert actual[2021] == e2021
e2022 = int(round(ifactor[2022] * actual[2021]))
absdiff = abs(actual[2022] - e2022)
if absdiff <= 1:
pass # close enough for government work
else:
assert actual[2022] == e2022


if __name__ == '__main__':
exit(main())
Loading