taxcalc 5.3.0__py3-none-any.whl → 6.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- taxcalc/__init__.py +1 -1
- taxcalc/calcfunctions.py +11 -11
- taxcalc/calculator.py +1 -1
- taxcalc/cli/tc.py +1 -8
- taxcalc/data.py +1 -2
- taxcalc/policy.py +7 -22
- taxcalc/policy_current_law.json +6 -6
- taxcalc/records.py +78 -82
- taxcalc/records_variables.json +106 -106
- taxcalc/reforms/2017_law.json +1 -1
- taxcalc/reforms/ARPA.json +1 -1
- taxcalc/reforms/Renacci.json +1 -1
- taxcalc/reforms/TCJA.json +1 -1
- taxcalc/reforms/Trump2016.json +1 -1
- taxcalc/reforms/Trump2017.json +1 -1
- taxcalc/taxcalcio.py +60 -51
- taxcalc/tests/conftest.py +19 -14
- taxcalc/tests/reforms.json +1 -1
- taxcalc/tests/reforms_expect.csv +54 -54
- taxcalc/tests/test_4package.py +3 -15
- taxcalc/tests/test_calcfunctions.py +2 -2
- taxcalc/tests/test_calculator.py +197 -160
- taxcalc/tests/test_cpscsv.py +0 -22
- taxcalc/tests/test_data.py +11 -3
- taxcalc/tests/test_parameters.py +42 -0
- taxcalc/tests/test_records.py +139 -8
- taxcalc/tests/test_reforms.py +5 -7
- taxcalc/tests/test_taxcalcio.py +3 -58
- {taxcalc-5.3.0.dist-info → taxcalc-6.1.0.dist-info}/METADATA +1 -1
- {taxcalc-5.3.0.dist-info → taxcalc-6.1.0.dist-info}/RECORD +34 -40
- taxcalc/puf_ratios.csv +0 -26
- taxcalc/puf_weights.csv.gz +0 -0
- taxcalc/tests/test_compare.py +0 -330
- taxcalc/tests/test_compatible_data.py +0 -334
- taxcalc/tests/test_puf_var_stats.py +0 -194
- taxcalc/tests/test_pufcsv.py +0 -328
- {taxcalc-5.3.0.dist-info → taxcalc-6.1.0.dist-info}/WHEEL +0 -0
- {taxcalc-5.3.0.dist-info → taxcalc-6.1.0.dist-info}/entry_points.txt +0 -0
- {taxcalc-5.3.0.dist-info → taxcalc-6.1.0.dist-info}/licenses/LICENSE +0 -0
- {taxcalc-5.3.0.dist-info → taxcalc-6.1.0.dist-info}/top_level.txt +0 -0
taxcalc/puf_ratios.csv
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
agi_bin,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18
|
2
|
-
INT2011,1.0259,0.5597,0.9448,0.9681,0.9728,0.9464,0.8390,0.8997,0.9713,0.9239,0.9342,0.9413,0.9497,0.9510,0.9693,0.9702,0.9569,1.0123,1.7014
|
3
|
-
INT2012,0.7778,0.9454,0.8433,0.8152,0.7153,0.8101,0.7961,0.7940,0.8481,0.9063,0.9129,0.9183,1.0343,1.0435,1.0129,1.1436,1.0975,1.2339,1.3445
|
4
|
-
INT2013,1.1325,0.7670,0.7821,0.7778,0.8935,0.8699,0.9558,0.9045,0.8342,0.8595,0.9746,1.0269,1.0499,1.0505,1.0891,0.9632,1.0449,0.9690,1.1443
|
5
|
-
INT2014,0.9106,0.8669,0.8492,0.7738,0.8431,0.8802,0.9729,0.8840,0.8368,1.0109,0.8448,1.0532,1.0274,0.9291,1.0609,1.1524,1.0624,1.0728,1.0860
|
6
|
-
INT2015,0.9813,0.9511,0.9323,0.9470,0.9543,0.9246,0.9368,0.9315,0.9463,0.9698,0.9887,1.0165,0.9966,0.9906,1.0241,0.9827,1.0221,1.1437,1.1671
|
7
|
-
INT2016,0.9843,1.0086,1.0453,1.0319,1.0447,1.0342,1.0110,1.0028,1.0025,1.0174,0.9482,0.9531,0.9752,1.0131,1.0723,1.0130,1.0804,1.0810,0.9938
|
8
|
-
INT2017,0.9932,0.9174,0.8978,0.8918,0.8944,0.9131,0.9215,0.9399,0.9611,0.9788,1.0156,1.0693,1.0312,0.9881,0.9354,0.9756,0.9565,1.0484,1.1942
|
9
|
-
INT2018,0.9991,0.9744,0.9683,0.9673,0.9757,0.9725,0.9748,0.9779,0.9742,0.9969,0.9929,1.0038,1.0148,1.0067,1.0226,0.9898,1.0182,1.0909,0.9981
|
10
|
-
INT2019,0.9979,0.9771,0.9763,0.9903,0.9793,0.9805,0.9769,0.9852,0.9826,0.9937,0.9934,1.0109,1.0020,1.0074,0.9985,1.0006,1.0253,1.0283,1.0196
|
11
|
-
INT2020,1.0017,0.9799,0.9739,0.9756,0.9843,0.9849,0.9831,0.9833,0.9932,0.9965,0.9958,1.0114,1.0058,1.0047,1.0076,0.9991,1.0129,1.0113,1.0113
|
12
|
-
INT2021,0.9959,0.9797,0.9776,0.9781,0.9836,0.9898,0.9883,0.9869,0.9920,0.9978,1.0043,0.9974,1.0066,1.0119,1.0049,1.0040,1.0303,1.0085,1.0022
|
13
|
-
INT2022,1.0062,0.9815,0.9810,0.9783,0.9724,0.9820,0.9850,0.9893,0.9934,1.0049,1.0022,1.0050,1.0029,1.0042,1.0047,1.0044,1.0133,0.9990,1.0111
|
14
|
-
INT2023,0.9983,0.9812,0.9761,0.9789,0.9891,0.9893,0.9852,1.0199,0.9942,0.9956,1.0022,1.0012,1.0023,1.0073,1.0006,1.0093,1.0214,1.0020,0.9943
|
15
|
-
INT2024,1.0016,0.9843,0.9813,0.9791,0.9712,0.9789,0.9893,1.0100,0.9905,0.9984,0.9987,0.9971,1.0048,1.0077,1.0089,1.0075,1.0238,1.0148,1.0081
|
16
|
-
INT2025,1.0086,0.9934,0.9772,0.9767,0.9834,0.9817,1.0012,0.9941,0.9935,1.0008,0.9990,0.9978,1.0051,1.0074,1.0142,1.0150,1.0238,0.9941,0.9912
|
17
|
-
INT2026,1.0089,0.9843,0.9792,0.9796,0.9872,0.9904,0.9989,0.9909,0.9953,0.9939,0.9931,0.9932,1.0070,1.0142,1.0373,1.0250,1.0208,0.9992,0.9937
|
18
|
-
INT2027,1.0115,0.9888,0.9839,0.9805,0.9888,0.9905,0.9824,0.9951,0.9905,0.9944,0.9993,0.9913,1.0073,1.0184,1.0175,1.0275,1.0259,0.9935,0.9946
|
19
|
-
INT2028,1.0062,0.9977,0.9772,0.9829,0.9863,0.9878,1.0107,0.9957,0.9924,0.9954,0.9931,0.9921,1.0068,1.0177,1.0271,1.0221,1.0222,0.9960,0.9918
|
20
|
-
INT2029,1.0101,0.9939,0.9780,0.9801,0.9824,0.9852,1.0025,0.9850,0.9945,0.9933,0.9934,0.9928,1.0033,1.0285,1.0278,1.0474,1.0236,1.0013,0.9924
|
21
|
-
INT2030,1.0295,1.0010,0.9821,0.9840,0.9821,0.9917,0.9822,0.9859,0.9830,0.9890,0.9815,0.9922,1.0058,1.0359,1.0382,1.0449,1.0299,0.9947,0.9921
|
22
|
-
INT2031,1.0280,1.0214,0.9858,0.9918,0.9829,0.9886,0.9858,0.9788,0.9910,0.9938,0.9791,0.9869,0.9950,1.0446,1.0442,1.0732,1.0287,1.0061,0.9932
|
23
|
-
INT2032,1.0079,1.1009,0.9920,0.9811,0.9867,0.9849,0.9881,0.9740,0.9732,0.9791,0.9662,0.9835,0.9898,1.0786,1.0900,1.1373,1.0482,1.0099,0.9983
|
24
|
-
INT2033,0.9352,0.8346,0.9707,0.9745,0.9896,0.9850,0.9878,1.0383,1.0313,1.0351,1.1156,1.0746,1.0377,0.8505,0.8247,0.7425,0.8974,1.0567,1.0930
|
25
|
-
INT2034,1.0278,0.9998,0.9801,0.9808,0.9905,0.9879,0.9984,0.9804,0.9885,0.9931,0.9775,0.9841,1.0025,1.0381,1.0368,1.0580,1.0409,1.0028,0.9939
|
26
|
-
INT2035,1.0241,1.0200,0.9891,0.9840,0.9837,0.9859,0.9886,0.9688,0.9850,0.9882,0.9774,0.9926,1.0011,1.0428,1.0542,1.0592,1.0396,0.9982,0.9940
|
taxcalc/puf_weights.csv.gz
DELETED
Binary file
|
taxcalc/tests/test_compare.py
DELETED
@@ -1,330 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Compares Tax-Calculator PUF and CPS results with historical information.
|
3
|
-
"""
|
4
|
-
# CODING-STYLE CHECKS:
|
5
|
-
# pycodestyle test_compare.py
|
6
|
-
# pylint --disable=locally-disabled test_compare.py
|
7
|
-
|
8
|
-
import os
|
9
|
-
import pytest
|
10
|
-
import numpy as np
|
11
|
-
from taxcalc.policy import Policy
|
12
|
-
from taxcalc.records import Records
|
13
|
-
from taxcalc.calculator import Calculator
|
14
|
-
from taxcalc.utils import add_income_table_row_variable, SOI_AGI_BINS
|
15
|
-
|
16
|
-
|
17
|
-
# 2015 IRS-SOI amounts by AGI category are from "Table 3.3 All Returns: Tax
|
18
|
-
# Liability, Tax Credits, and Tax Payments by Size of Adjusted Gross Income,
|
19
|
-
# Tax Year 2015" which is available as a spreadsheet at this URL:
|
20
|
-
# <https://www.irs.gov/statistics/soi-tax-stats-individual-
|
21
|
-
# statistical-tables-by-size-of-adjusted-gross-income>
|
22
|
-
# The IRS-SOI amounts are from 19 rows in the spreadsheet numbered from
|
23
|
-
# 11 (AGI under one dollar) through 29 (AGI $10M or more).
|
24
|
-
# Dollar IRS-SOI amounts are expressed in billions of dollars and rounded
|
25
|
-
# to the nearest one-tenth of a million dollars.
|
26
|
-
ITAX = {
|
27
|
-
'0:EITC': {
|
28
|
-
# Full earned income credit
|
29
|
-
# in 2015 using the IRS-SOI information described above.
|
30
|
-
# EITC is column (37) in the Table 3.3 spreadsheet,
|
31
|
-
# which is the sum of columns (47), (75) and (97).
|
32
|
-
'SOI': [0.2104,
|
33
|
-
1.1843,
|
34
|
-
7.1562,
|
35
|
-
16.5927,
|
36
|
-
15.8799,
|
37
|
-
11.1025,
|
38
|
-
7.5150,
|
39
|
-
7.4528,
|
40
|
-
1.3936,
|
41
|
-
0.0375,
|
42
|
-
0.0000,
|
43
|
-
0.0000,
|
44
|
-
0.0000,
|
45
|
-
0.0000,
|
46
|
-
0.0000,
|
47
|
-
0.0000,
|
48
|
-
0.0000,
|
49
|
-
0.0000,
|
50
|
-
0.0000],
|
51
|
-
'TC': ['eitc']
|
52
|
-
},
|
53
|
-
|
54
|
-
'1:FCTC': {
|
55
|
-
# Full (basic and additional, refundable and nonrefundable) child tax
|
56
|
-
# credit in 2015 using the IRS-SOI information described above.
|
57
|
-
# FCTC is sum of columns (13) and (39) in the Table 3.3 spreadsheet.
|
58
|
-
'SOI': [0.1301,
|
59
|
-
0.0793,
|
60
|
-
1.4740,
|
61
|
-
4.2580,
|
62
|
-
5.2104,
|
63
|
-
4.6582,
|
64
|
-
4.3166,
|
65
|
-
7.2320,
|
66
|
-
5.2848,
|
67
|
-
9.4151,
|
68
|
-
6.4075,
|
69
|
-
5.2222,
|
70
|
-
0.0018,
|
71
|
-
0.0000,
|
72
|
-
0.0000,
|
73
|
-
0.0000,
|
74
|
-
0.0000,
|
75
|
-
0.0000,
|
76
|
-
0.0000],
|
77
|
-
'TC': ['c07220', # FCTC that is nonrefundable
|
78
|
-
'c11070'] # FCTC that isrefundable
|
79
|
-
},
|
80
|
-
|
81
|
-
'2:NIIT': {
|
82
|
-
# Net investment income tax
|
83
|
-
# in 2015 using the IRS-SOI information described above.
|
84
|
-
# NIIT is column (53) in the Table 3.3 spreadsheet.
|
85
|
-
# NIIT is included in Tax-Calculator individual income tax liability.
|
86
|
-
'SOI': [0.0004,
|
87
|
-
0.0000,
|
88
|
-
0.0000,
|
89
|
-
0.0000,
|
90
|
-
0.0000,
|
91
|
-
0.0000,
|
92
|
-
0.0001,
|
93
|
-
0.0000,
|
94
|
-
0.0000,
|
95
|
-
0.0014,
|
96
|
-
0.0005,
|
97
|
-
0.0213,
|
98
|
-
2.6397,
|
99
|
-
3.1356,
|
100
|
-
1.6715,
|
101
|
-
1.0775,
|
102
|
-
3.1267,
|
103
|
-
2.0949,
|
104
|
-
8.2730],
|
105
|
-
'TC': ['niit']
|
106
|
-
},
|
107
|
-
|
108
|
-
'3:ITAX': {
|
109
|
-
# Total income tax liability
|
110
|
-
# in 2015 using the IRS-SOI information described above.
|
111
|
-
# ITAX is column (55) in the Table 3.3 spreadsheet,
|
112
|
-
# which includes NIIT and is after all credits.
|
113
|
-
'SOI': [0.2425,
|
114
|
-
0.0409,
|
115
|
-
0.3680,
|
116
|
-
1.3813,
|
117
|
-
3.5238,
|
118
|
-
6.1911,
|
119
|
-
8.7526,
|
120
|
-
25.1677,
|
121
|
-
32.5302,
|
122
|
-
99.7918,
|
123
|
-
105.9015,
|
124
|
-
316.3496,
|
125
|
-
299.8322,
|
126
|
-
154.3888,
|
127
|
-
66.3236,
|
128
|
-
39.6716,
|
129
|
-
101.4885,
|
130
|
-
56.3344,
|
131
|
-
139.6113],
|
132
|
-
'TC': ['iitax']
|
133
|
-
},
|
134
|
-
|
135
|
-
'4:SETAX': {
|
136
|
-
# Self-employment tax
|
137
|
-
# in 2015 using the IRS-SOI information described above.
|
138
|
-
# SETAX is column (59) in the Table 3.3 spreadsheet,
|
139
|
-
# which is not part of ITAX but is part of total payroll taxes.
|
140
|
-
'SOI': [0.6557,
|
141
|
-
0.5554,
|
142
|
-
1.8956,
|
143
|
-
3.5143,
|
144
|
-
2.8228,
|
145
|
-
1.9959,
|
146
|
-
1.8020,
|
147
|
-
3.3598,
|
148
|
-
2.8199,
|
149
|
-
5.9579,
|
150
|
-
5.2751,
|
151
|
-
12.1488,
|
152
|
-
9.6864,
|
153
|
-
3.4864,
|
154
|
-
1.1938,
|
155
|
-
0.6432,
|
156
|
-
1.2527,
|
157
|
-
0.4698,
|
158
|
-
0.6383],
|
159
|
-
'TC': ['setax']
|
160
|
-
},
|
161
|
-
|
162
|
-
'5:AMTAX': {
|
163
|
-
# Additional Medicare tax
|
164
|
-
# in 2015 using the IRS-SOI information described above.
|
165
|
-
# AMTAX is column (71) in the Table 3.3 spreadsheet,
|
166
|
-
# which is not part of ITAX but is part of total payroll taxes.
|
167
|
-
'SOI': [0.0225,
|
168
|
-
0.0003,
|
169
|
-
0.0000,
|
170
|
-
0.0002,
|
171
|
-
0.0002,
|
172
|
-
0.0004,
|
173
|
-
0.0002,
|
174
|
-
0.0041,
|
175
|
-
0.0071,
|
176
|
-
0.0057,
|
177
|
-
0.0026,
|
178
|
-
0.0372,
|
179
|
-
1.8356,
|
180
|
-
2.0214,
|
181
|
-
0.8602,
|
182
|
-
0.4898,
|
183
|
-
1.1730,
|
184
|
-
0.5805,
|
185
|
-
0.9787],
|
186
|
-
'TC': ['ptax_amc']
|
187
|
-
}
|
188
|
-
}
|
189
|
-
|
190
|
-
|
191
|
-
def comparison(cname, calc, cmpdata, ofile):
|
192
|
-
"""
|
193
|
-
Write comparison results for cname to ofile.
|
194
|
-
"""
|
195
|
-
# pylint: disable=too-many-locals
|
196
|
-
# generate compare table for cvarname
|
197
|
-
vardf = calc.dataframe(['s006', 'c00100']) # weight and AGI
|
198
|
-
# add compare variable to vardf
|
199
|
-
cvar = np.zeros(calc.array_len)
|
200
|
-
for var in cmpdata[cname]['TC']:
|
201
|
-
cvar += calc.array(var)
|
202
|
-
vardf['cvar'] = cvar
|
203
|
-
# construct AGI table
|
204
|
-
vardf = add_income_table_row_variable(vardf, 'c00100', SOI_AGI_BINS)
|
205
|
-
gbydf = vardf.groupby('table_row', as_index=False, observed=True)
|
206
|
-
# write AGI table with ALL row at bottom of ofile
|
207
|
-
ofile.write(f"TABLE for {cname.split(':')[1]}\n")
|
208
|
-
results = '{:23s}\t{:8.3f}\t{:8.3f}\t{:+6.1f}\n'
|
209
|
-
colhead = f"{'AGIcategory':23s}\t{'T-C':>8s}\t{'SOI':>8s}\t{'%diff':>6s}\n"
|
210
|
-
ofile.write(colhead)
|
211
|
-
# pylint: disable=consider-using-f-string
|
212
|
-
txc_tot = 0.
|
213
|
-
soi_tot = 0.
|
214
|
-
idx = 0
|
215
|
-
for grp_interval, grp in gbydf:
|
216
|
-
txc = (grp['cvar'] * grp['s006']).sum() * 1e-9
|
217
|
-
soi = cmpdata[cname]['SOI'][idx]
|
218
|
-
txc_tot += txc
|
219
|
-
soi_tot += soi
|
220
|
-
if soi > 0:
|
221
|
-
pct_diff = 100. * ((txc / soi) - 1.)
|
222
|
-
else:
|
223
|
-
pct_diff = np.nan
|
224
|
-
glabel = f'[{grp_interval.left:.8g}, {grp_interval.right:.8g})'
|
225
|
-
ofile.write(results.format(glabel, txc, soi, pct_diff))
|
226
|
-
idx += 1
|
227
|
-
pct_diff = 100. * ((txc_tot / soi_tot) - 1.)
|
228
|
-
ofile.write(results.format('ALL', txc_tot, soi_tot, pct_diff))
|
229
|
-
# pylint: enable=consider-using-f-string
|
230
|
-
|
231
|
-
|
232
|
-
def nonsmall_diffs(linelist1, linelist2, small=0.0):
|
233
|
-
"""
|
234
|
-
Return True if line lists differ significantly; otherwise return False.
|
235
|
-
Significant numerical difference means one or more numbers differ (between
|
236
|
-
linelist1 and linelist2) by more than the specified small amount.
|
237
|
-
"""
|
238
|
-
# embedded function used only in nonsmall_diffs function
|
239
|
-
def isfloat(value):
|
240
|
-
"""
|
241
|
-
Return True if value can be cast to float; otherwise return False.
|
242
|
-
"""
|
243
|
-
try:
|
244
|
-
float(value)
|
245
|
-
return True
|
246
|
-
except ValueError:
|
247
|
-
return False
|
248
|
-
# begin nonsmall_diffs logic
|
249
|
-
assert isinstance(linelist1, list)
|
250
|
-
assert isinstance(linelist2, list)
|
251
|
-
if len(linelist1) != len(linelist2):
|
252
|
-
return True
|
253
|
-
assert 0.0 <= small <= 1.0
|
254
|
-
epsilon = 1e-6
|
255
|
-
smallamt = small + epsilon
|
256
|
-
for line1, line2 in zip(linelist1, linelist2):
|
257
|
-
if line1 == line2:
|
258
|
-
continue
|
259
|
-
tokens1 = line1.replace(',', '').split()
|
260
|
-
tokens2 = line2.replace(',', '').split()
|
261
|
-
for tok1, tok2 in zip(tokens1, tokens2):
|
262
|
-
tok1_isfloat = isfloat(tok1)
|
263
|
-
tok2_isfloat = isfloat(tok2)
|
264
|
-
if tok1_isfloat and tok2_isfloat:
|
265
|
-
if abs(float(tok1) - float(tok2)) <= smallamt:
|
266
|
-
continue
|
267
|
-
return True
|
268
|
-
if not tok1_isfloat and not tok2_isfloat:
|
269
|
-
if tok1 == tok2:
|
270
|
-
continue
|
271
|
-
return True
|
272
|
-
return True
|
273
|
-
return False
|
274
|
-
|
275
|
-
|
276
|
-
def differences(afilename, efilename):
|
277
|
-
"""
|
278
|
-
Check for differences between results in afilename and efilename files.
|
279
|
-
"""
|
280
|
-
with open(afilename, 'r', encoding='utf-8') as afile:
|
281
|
-
actres = afile.read()
|
282
|
-
with open(efilename, 'r', encoding='utf-8') as efile:
|
283
|
-
expres = efile.read()
|
284
|
-
diffs = nonsmall_diffs(actres.splitlines(True),
|
285
|
-
expres.splitlines(True), 0.0)
|
286
|
-
if diffs:
|
287
|
-
afname = os.path.basename(afilename)
|
288
|
-
efname = os.path.basename(efilename)
|
289
|
-
msg = 'COMPARE RESULTS DIFFER\n'
|
290
|
-
msg += '-------------------------------------------------\n'
|
291
|
-
msg += f'--- NEW RESULTS IN {afname} FILE ---\n'
|
292
|
-
msg += f'--- if new OK, copy {afname} to ---\n'
|
293
|
-
msg += f'--- {efname} ---\n'
|
294
|
-
msg += '--- and rerun test. ---\n'
|
295
|
-
msg += '-------------------------------------------------\n'
|
296
|
-
raise ValueError(msg)
|
297
|
-
os.remove(afilename)
|
298
|
-
|
299
|
-
|
300
|
-
@pytest.mark.pre_release
|
301
|
-
@pytest.mark.requires_pufcsv
|
302
|
-
@pytest.mark.parametrize('using_puf', [True, False])
|
303
|
-
def test_itax_compare(tests_path, using_puf, puf_fullsample, cps_fullsample):
|
304
|
-
"""
|
305
|
-
Conduct income tax comparisons using ITAX data.
|
306
|
-
"""
|
307
|
-
using_puf_adjust_ratios = True
|
308
|
-
# generate 2015 estimates by AGI category using Tax-Calculator
|
309
|
-
if using_puf:
|
310
|
-
if using_puf_adjust_ratios:
|
311
|
-
recs = Records(data=puf_fullsample)
|
312
|
-
else:
|
313
|
-
recs = Records(data=puf_fullsample, adjust_ratios=None)
|
314
|
-
else:
|
315
|
-
recs = Records.cps_constructor(data=cps_fullsample)
|
316
|
-
calc = Calculator(policy=Policy(), records=recs, verbose=False)
|
317
|
-
calc.advance_to_year(2015)
|
318
|
-
calc.calc_all()
|
319
|
-
# open actual output file
|
320
|
-
if using_puf:
|
321
|
-
afilename = os.path.join(tests_path, 'cmpi_puf_actual.txt')
|
322
|
-
else:
|
323
|
-
afilename = os.path.join(tests_path, 'cmpi_cps_actual.txt')
|
324
|
-
with open(afilename, 'w', encoding='utf-8') as afile:
|
325
|
-
# write compare results to afile
|
326
|
-
for cname in sorted(ITAX.keys()):
|
327
|
-
comparison(cname, calc, ITAX, afile)
|
328
|
-
# check for differences between actual and expect output files
|
329
|
-
efilename = afilename.replace('actual', 'expect')
|
330
|
-
differences(afilename, efilename)
|
@@ -1,334 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Tests of the compatible_data fields in the policy_current_law.json file.
|
3
|
-
|
4
|
-
In order to tap into the parallelization capabilities of py.test, this module
|
5
|
-
leans heavily on py.tests's `parametrization` method. Once you do so, the
|
6
|
-
plug-in pytest-xdist is able to run all parametrized functions in parallel
|
7
|
-
"""
|
8
|
-
# CODING-STYLE CHECKS:
|
9
|
-
# pycodestyle test_compatible_data.py
|
10
|
-
# pylint --disable=locally-disabled test_compatible_data.py
|
11
|
-
|
12
|
-
import copy
|
13
|
-
import pytest
|
14
|
-
import numpy as np
|
15
|
-
from taxcalc.policy import Policy
|
16
|
-
from taxcalc.records import Records
|
17
|
-
from taxcalc.calculator import Calculator
|
18
|
-
|
19
|
-
|
20
|
-
@pytest.fixture(scope='module', name='allparams')
|
21
|
-
def fixture_allparams():
|
22
|
-
"""
|
23
|
-
Return metadata for current-law policy parameters.
|
24
|
-
"""
|
25
|
-
clp = Policy()
|
26
|
-
return clp.metadata()
|
27
|
-
|
28
|
-
|
29
|
-
def test_compatible_data_presence(allparams):
|
30
|
-
"""
|
31
|
-
Test that every parameter in the policy_current_law.json file
|
32
|
-
has a compatible_data field that is a dictionary.
|
33
|
-
"""
|
34
|
-
compatible_data_keys_set = set(['puf', 'cps'])
|
35
|
-
|
36
|
-
# Nested function used only in test_compatible_data_presence
|
37
|
-
def valid_compatible_data(compatible_data):
|
38
|
-
"""
|
39
|
-
Return True if compatible_data is a valid dictionary;
|
40
|
-
otherwise return False
|
41
|
-
"""
|
42
|
-
if not isinstance(compatible_data, dict):
|
43
|
-
return False
|
44
|
-
if set(compatible_data.keys()) != compatible_data_keys_set:
|
45
|
-
return False
|
46
|
-
for key in compatible_data:
|
47
|
-
boolean = (compatible_data[key] is True or
|
48
|
-
compatible_data[key] is False)
|
49
|
-
if not boolean:
|
50
|
-
return False
|
51
|
-
return True
|
52
|
-
|
53
|
-
# Main logic of test_compatible_data_presence function
|
54
|
-
problem_pnames = []
|
55
|
-
for pname in allparams:
|
56
|
-
if 'compatible_data' in allparams[pname]:
|
57
|
-
compatible_data = allparams[pname]['compatible_data']
|
58
|
-
else:
|
59
|
-
compatible_data = None
|
60
|
-
if not valid_compatible_data(compatible_data):
|
61
|
-
problem_pnames.append(pname)
|
62
|
-
if problem_pnames:
|
63
|
-
msg = '{} has no or invalid compatible_data field'
|
64
|
-
for pname in problem_pnames:
|
65
|
-
print(msg.format(pname))
|
66
|
-
assert False, 'ERROR: list of problem_pnames is above'
|
67
|
-
|
68
|
-
|
69
|
-
XX_YEAR = 2019
|
70
|
-
TEST_YEAR = 2020
|
71
|
-
|
72
|
-
|
73
|
-
@pytest.fixture(scope='module', name='reform_xx')
|
74
|
-
def fixture_reform_xx():
|
75
|
-
"""
|
76
|
-
Fixture for reform dictionary where reform starts before TEST_YEAR.
|
77
|
-
|
78
|
-
The provisions in the baseline reform, designated in _reform_xx,
|
79
|
-
are chosen to activate parameters that are inactive under current law.
|
80
|
-
For example a phaseout rate for a new credit is inactive if the credit's
|
81
|
-
amount is set to zero under current law. In order to activate the phaseout
|
82
|
-
rate, the credit amount should be set above zero. The provisions interact
|
83
|
-
with each other: you may acidentally deactivate one parameter
|
84
|
-
by introducing a provision to activate another. If you find that a pair of
|
85
|
-
parameters are impossible test jointly, add one to the local variable
|
86
|
-
`exempt_from_testing` in `test_compatible_data()` as a last resort.
|
87
|
-
"""
|
88
|
-
assert XX_YEAR < TEST_YEAR
|
89
|
-
|
90
|
-
# Set baseline to activate parameters that are inactive under current law.
|
91
|
-
_reform_xx = {
|
92
|
-
XX_YEAR: {
|
93
|
-
'FST_AGI_trt': 0.5,
|
94
|
-
'CTC_new_rt': 0.5,
|
95
|
-
'CTC_new_c': 5000,
|
96
|
-
'CTC_new_prt': 0.1,
|
97
|
-
'CTC_new_refund_limited': True,
|
98
|
-
'CTC_new_refund_limit_payroll_rt': 1,
|
99
|
-
'ACTC_ChildNum': 1,
|
100
|
-
'UBI_u18': 1000,
|
101
|
-
'UBI_1820': 1000,
|
102
|
-
'UBI_21': 1000,
|
103
|
-
'II_credit_prt': 0.1,
|
104
|
-
'II_credit': [100, 100, 100, 100, 100],
|
105
|
-
'CG_brk3': [1000000, 1000000, 1000000, 1000000, 1000000],
|
106
|
-
'ALD_Dependents_Child_c': 1000,
|
107
|
-
'II_credit_nr': [1000, 1000, 1000, 1000, 1000],
|
108
|
-
'II_credit_nr_prt': 0.1,
|
109
|
-
'AMT_CG_brk3': [500000, 500000, 500000, 500000, 500000],
|
110
|
-
'AGI_surtax_thd': [1000000, 1000000, 1000000, 1000000, 1000000],
|
111
|
-
'AGI_surtax_trt': 0.5,
|
112
|
-
'II_brk7': [1000000, 1000000, 1000000, 1000000, 1000000],
|
113
|
-
'II_em': 1000,
|
114
|
-
'ID_Casualty_hc': 0.1,
|
115
|
-
'ID_Miscellaneous_hc': 0.1,
|
116
|
-
'ID_prt': 0.03,
|
117
|
-
'ID_crt': 0.8,
|
118
|
-
'CR_Charity_rt': 0.4,
|
119
|
-
'CR_Charity_f': [5000, 5000, 5000, 5000, 5000],
|
120
|
-
'CR_Charity_frt': 0.5,
|
121
|
-
'CR_SchR_hc': 0.5
|
122
|
-
}
|
123
|
-
}
|
124
|
-
return _reform_xx
|
125
|
-
|
126
|
-
|
127
|
-
@pytest.fixture(scope='module', name='sorted_param_names')
|
128
|
-
def fixture_sorted_param_names(allparams):
|
129
|
-
"""
|
130
|
-
Fixture for storing a sorted parameter list
|
131
|
-
"""
|
132
|
-
return sorted(list(allparams.keys()))
|
133
|
-
|
134
|
-
|
135
|
-
NPARAMS = 219 # hard-code NPARAMS to len(allparams)
|
136
|
-
BATCHSIZE = 10
|
137
|
-
BATCHES = int(np.floor(NPARAMS / BATCHSIZE)) + 1
|
138
|
-
|
139
|
-
|
140
|
-
@pytest.fixture(scope='module', name='allparams_batch',
|
141
|
-
params=list(range(0, BATCHES)))
|
142
|
-
def fixture_allparams_batch(request, allparams, sorted_param_names):
|
143
|
-
"""
|
144
|
-
Fixture for grouping Tax-Calculator parameters
|
145
|
-
|
146
|
-
Experiments indicated that there is some overhead when you run
|
147
|
-
`test_compatible_data` on each parameter individually. Suppose it takes X
|
148
|
-
amount of time to set up the test data for `test_compatible_data` and Y
|
149
|
-
amount of time to run `test_compatible_data` on each parameter wihtout
|
150
|
-
parallelization. Then, if there is no overhead from parallelization, you
|
151
|
-
would expect it to take Y + (X / NUMBER_WORKERS) to run these tests in
|
152
|
-
parallel. Note that setup data is only created once if you set the
|
153
|
-
fixture scope to 'module'. However, experiments indicated that there was
|
154
|
-
so much overhead that the tests weren't that much faster in parallel than
|
155
|
-
if they were run sequentially.
|
156
|
-
|
157
|
-
I found that running the parameters in batches decreased the amount of
|
158
|
-
overhead. Further, there was an optimal batch size that I found through
|
159
|
-
trial and error. On my local machine, this was something like 10
|
160
|
-
parameters. Others may find a different optimal batch size on their
|
161
|
-
machines. Further, if the number of parameters changes, the optimal
|
162
|
-
batch size could change, too.
|
163
|
-
|
164
|
-
Math for partitioning the parameters:
|
165
|
-
|
166
|
-
Suppose we have N parameters and choose batch size n. Then, we have
|
167
|
-
B batches where B equals floor(N / n) + 1.
|
168
|
-
|
169
|
-
Case 1: N % n = 0
|
170
|
-
Then we have:
|
171
|
-
idx_min = {i * b, i = 0, 1, 2, 3, ..., B - 1} and
|
172
|
-
idx_max = {min((i + 1) * b, N), i = 0, 1, 2, 3, ..., B - 1}
|
173
|
-
|
174
|
-
So, if i equals 0, the batch contains the first b - 1 parameters.
|
175
|
-
Then, if i equals B, then idx_min is n * (B - 1) = N and idx_max is N and
|
176
|
-
thus, the last batch is empty.
|
177
|
-
|
178
|
-
Case 2: N % n = r > 0
|
179
|
-
Then, everything is the same as case 1, except for the final batch.
|
180
|
-
In the final batch, idx_min = b * (B - 1) = b * floor(N / n) < N, and
|
181
|
-
idx_max is N. So, we our final batch size is
|
182
|
-
idx_max - idx_min = N - b * B = r.
|
183
|
-
|
184
|
-
returns: dictionary of size, BATCHSIZE, or for the final batch,
|
185
|
-
either an empty dictionary or dictionary of size NPARAMS mod BATCHSIZE
|
186
|
-
"""
|
187
|
-
idx = request.param
|
188
|
-
idx_start = idx * BATCHSIZE
|
189
|
-
idx_end = min((idx + 1) * BATCHSIZE, NPARAMS)
|
190
|
-
pnames = sorted_param_names[idx_start: idx_end]
|
191
|
-
return {pname: allparams[pname] for pname in pnames}
|
192
|
-
|
193
|
-
|
194
|
-
@pytest.fixture(scope='module', name='tc_objs',
|
195
|
-
params=[True, False])
|
196
|
-
def fixture_tc_objs(request, reform_xx, puf_subsample, cps_subsample):
|
197
|
-
"""
|
198
|
-
Fixture for creating Tax-Calculator objects that use the PUF and
|
199
|
-
use the CPS (called only twice: once for PUF and once for CPS)
|
200
|
-
"""
|
201
|
-
puftest = request.param
|
202
|
-
p_xx = Policy()
|
203
|
-
p_xx.implement_reform(reform_xx, raise_errors=False)
|
204
|
-
if puftest:
|
205
|
-
rec_xx = Records(data=puf_subsample)
|
206
|
-
else:
|
207
|
-
rec_xx = Records.cps_constructor(data=cps_subsample)
|
208
|
-
c_xx = Calculator(policy=p_xx, records=rec_xx)
|
209
|
-
c_xx.advance_to_year(TEST_YEAR)
|
210
|
-
c_xx.calc_all()
|
211
|
-
return rec_xx, c_xx, puftest
|
212
|
-
|
213
|
-
|
214
|
-
@pytest.mark.skip
|
215
|
-
@pytest.mark.pre_release
|
216
|
-
@pytest.mark.compatible_data
|
217
|
-
@pytest.mark.requires_pufcsv
|
218
|
-
def test_compatible_data(cps_subsample, puf_subsample,
|
219
|
-
allparams, reform_xx,
|
220
|
-
tc_objs, allparams_batch):
|
221
|
-
"""
|
222
|
-
Test that the compatible_data attribute in policy_current_law.json
|
223
|
-
is accurate by implementing the min and max values of each parameter
|
224
|
-
as reforms and ensuring that revenue differs from baseline when for
|
225
|
-
at least one of these reforms when using datasets marked compatible
|
226
|
-
and does not differ when using datasets marked as incompatible.
|
227
|
-
"""
|
228
|
-
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
229
|
-
# pylint: disable=too-many-statements,too-many-branches,too-many-locals
|
230
|
-
|
231
|
-
# Check NPARAMS value
|
232
|
-
assert NPARAMS == len(allparams)
|
233
|
-
|
234
|
-
# Get taxcalc objects from tc_objs fixture
|
235
|
-
rec_xx, c_xx, puftest = tc_objs
|
236
|
-
|
237
|
-
# These parameters are exempt because they are not active under
|
238
|
-
# current law and activating them would deactivate other parameters,
|
239
|
-
# or if it is difficult to devise a test for them.
|
240
|
-
exempt_from_testing = [
|
241
|
-
'CG_ec', 'CG_reinvest_ec_rt',
|
242
|
-
'II_prt', 'ID_prt', 'ID_crt',
|
243
|
-
'CR_SchR_hc', 'ACTC_ChildNum'
|
244
|
-
]
|
245
|
-
|
246
|
-
# Loop through the parameters in allparams_batch
|
247
|
-
errmsg = 'ERROR: {} {}\n'
|
248
|
-
errors = ''
|
249
|
-
for pname in allparams_batch:
|
250
|
-
param = allparams_batch[pname]
|
251
|
-
max_listed = param['valid_values']['max']
|
252
|
-
# handle links to other params or self
|
253
|
-
if isinstance(max_listed, str):
|
254
|
-
if isinstance(allparams[max_listed]['value'][0], list):
|
255
|
-
max_val = allparams[max_listed]['value'][0]
|
256
|
-
else:
|
257
|
-
max_val = float(allparams[max_listed]['value'][0])
|
258
|
-
else:
|
259
|
-
if isinstance(param['value'][0], list):
|
260
|
-
max_val = [max_listed] * len(param['value'][0])
|
261
|
-
else:
|
262
|
-
max_val = max_listed
|
263
|
-
min_listed = param['valid_values']['min']
|
264
|
-
if isinstance(min_listed, str):
|
265
|
-
if isinstance(allparams[min_listed]['value'][0], list):
|
266
|
-
min_val = allparams[min_listed]['value'][0]
|
267
|
-
else:
|
268
|
-
min_val = float(allparams[min_listed]['value'][0])
|
269
|
-
else:
|
270
|
-
if isinstance(param['value'][0], list):
|
271
|
-
min_val = [min_listed] * len(param['value'][0])
|
272
|
-
else:
|
273
|
-
min_val = min_listed
|
274
|
-
# create reform dictionaries
|
275
|
-
max_reform = copy.deepcopy(reform_xx)
|
276
|
-
min_reform = copy.deepcopy(reform_xx)
|
277
|
-
max_reform[XX_YEAR][str(pname)] = [max_val]
|
278
|
-
min_reform[XX_YEAR][str(pname)] = [min_val]
|
279
|
-
# assess whether max reform changes results
|
280
|
-
if puftest:
|
281
|
-
rec_yy = Records(data=puf_subsample)
|
282
|
-
else:
|
283
|
-
rec_yy = Records.cps_constructor(data=cps_subsample)
|
284
|
-
p_yy = Policy()
|
285
|
-
p_yy.implement_reform(max_reform, raise_errors=False)
|
286
|
-
c_yy = Calculator(policy=p_yy, records=rec_yy, verbose=False)
|
287
|
-
c_yy.advance_to_year(TEST_YEAR)
|
288
|
-
c_yy.calc_all()
|
289
|
-
if pname.startswith('BEN') and pname.endswith('_repeal'):
|
290
|
-
max_reform_change = (
|
291
|
-
c_yy.weighted_total('benefit_cost_total') -
|
292
|
-
c_xx.weighted_total('benefit_cost_total')
|
293
|
-
)
|
294
|
-
else:
|
295
|
-
max_reform_change = (
|
296
|
-
c_yy.weighted_total('combined') -
|
297
|
-
c_xx.weighted_total('combined')
|
298
|
-
)
|
299
|
-
min_reform_change = 0
|
300
|
-
# assess whether min reform changes results, if max reform did not
|
301
|
-
if max_reform_change == 0:
|
302
|
-
p_yy = Policy()
|
303
|
-
p_yy.implement_reform(min_reform, raise_errors=False)
|
304
|
-
c_yy = Calculator(policy=p_yy, records=rec_xx)
|
305
|
-
c_yy.advance_to_year(TEST_YEAR)
|
306
|
-
c_yy.calc_all()
|
307
|
-
if pname.startswith('BEN') and pname.endswith('_repeal'):
|
308
|
-
min_reform_change = (
|
309
|
-
c_yy.weighted_total('benefit_cost_total') -
|
310
|
-
c_xx.weighted_total('benefit_cost_total')
|
311
|
-
)
|
312
|
-
else:
|
313
|
-
min_reform_change = (
|
314
|
-
c_yy.weighted_total('combined') -
|
315
|
-
c_xx.weighted_total('combined')
|
316
|
-
)
|
317
|
-
if min_reform_change == 0 and pname not in exempt_from_testing:
|
318
|
-
if puftest:
|
319
|
-
if param['compatible_data']['puf'] is True:
|
320
|
-
errors += errmsg.format(pname, 'is not True for puf')
|
321
|
-
else:
|
322
|
-
if param['compatible_data']['cps'] is True:
|
323
|
-
errors += errmsg.format(pname, 'is not True for cps')
|
324
|
-
if max_reform_change != 0 or min_reform_change != 0:
|
325
|
-
if puftest:
|
326
|
-
if param['compatible_data']['puf'] is False:
|
327
|
-
errors += errmsg.format(pname, 'is not False for puf')
|
328
|
-
else:
|
329
|
-
if param['compatible_data']['cps'] is False:
|
330
|
-
errors += errmsg.format(pname, 'is not False for cps')
|
331
|
-
# test failure if any errors
|
332
|
-
if errors:
|
333
|
-
print(errors)
|
334
|
-
assert False, 'ERROR: compatible_data is invalid; see errors above'
|