taxcalc 4.6.1__py3-none-any.whl → 4.6.3__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 +2 -2
- taxcalc/cli/tc.py +85 -29
- taxcalc/parameters.py +188 -118
- taxcalc/policy.py +26 -4
- taxcalc/policy_current_law.json +2 -2
- taxcalc/reforms/REFORMS.md +0 -2
- taxcalc/reforms/ext.json +0 -2
- taxcalc/taxcalcio.py +95 -24
- taxcalc/tests/conftest.py +1 -1
- taxcalc/tests/reforms.json +8 -19
- taxcalc/tests/reforms_expect.csv +9 -10
- taxcalc/tests/test_4package.py +0 -1
- taxcalc/tests/test_parameters.py +18 -22
- taxcalc/tests/test_policy.py +153 -41
- taxcalc/tests/test_reforms.py +2 -2
- taxcalc/tests/test_taxcalcio.py +35 -13
- taxcalc/utils.py +32 -10
- {taxcalc-4.6.1.dist-info → taxcalc-4.6.3.dist-info}/METADATA +5 -2
- {taxcalc-4.6.1.dist-info → taxcalc-4.6.3.dist-info}/RECORD +24 -24
- {taxcalc-4.6.1.dist-info → taxcalc-4.6.3.dist-info}/WHEEL +1 -1
- {taxcalc-4.6.1.dist-info → taxcalc-4.6.3.dist-info}/entry_points.txt +0 -0
- {taxcalc-4.6.1.dist-info → taxcalc-4.6.3.dist-info}/licenses/LICENSE +0 -0
- {taxcalc-4.6.1.dist-info → taxcalc-4.6.3.dist-info}/top_level.txt +0 -0
taxcalc/parameters.py
CHANGED
@@ -7,13 +7,13 @@ import copy
|
|
7
7
|
from collections import defaultdict
|
8
8
|
from typing import Union, Mapping, Any, List
|
9
9
|
import numpy as np
|
10
|
-
import marshmallow
|
11
|
-
import paramtools
|
10
|
+
import marshmallow
|
11
|
+
import paramtools
|
12
12
|
|
13
13
|
|
14
|
-
class CompatibleDataSchema(
|
14
|
+
class CompatibleDataSchema(marshmallow.Schema):
|
15
15
|
"""
|
16
|
-
Schema for
|
16
|
+
Schema for compatible_data object
|
17
17
|
|
18
18
|
.. code-block :: json
|
19
19
|
|
@@ -22,18 +22,17 @@ class CompatibleDataSchema(ma.Schema):
|
|
22
22
|
}
|
23
23
|
|
24
24
|
"""
|
25
|
+
puf = marshmallow.fields.Boolean()
|
26
|
+
cps = marshmallow.fields.Boolean()
|
25
27
|
|
26
|
-
puf = ma.fields.Boolean()
|
27
|
-
cps = ma.fields.Boolean()
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
ma.fields.Nested(CompatibleDataSchema())
|
29
|
+
paramtools.register_custom_type(
|
30
|
+
'compatible_data',
|
31
|
+
marshmallow.fields.Nested(CompatibleDataSchema())
|
33
32
|
)
|
34
33
|
|
35
34
|
|
36
|
-
class Parameters(
|
35
|
+
class Parameters(paramtools.Parameters):
|
37
36
|
"""
|
38
37
|
Base class that wraps ParamTools, providing parameter indexing
|
39
38
|
for tax policy in the ``adjust`` method and convenience methods
|
@@ -69,7 +68,7 @@ class Parameters(pt.Parameters):
|
|
69
68
|
# pylint: disable=too-many-instance-attributes
|
70
69
|
defaults = None
|
71
70
|
array_first = True
|
72
|
-
label_to_extend =
|
71
|
+
label_to_extend = 'year'
|
73
72
|
uses_extend_func = True
|
74
73
|
|
75
74
|
REMOVED_PARAMS = None
|
@@ -113,15 +112,15 @@ class Parameters(pt.Parameters):
|
|
113
112
|
self._wage_indexed = wage_indexed or self.WAGE_INDEXED_PARAMS
|
114
113
|
if (
|
115
114
|
(start_year or self.JSON_START_YEAR) and
|
116
|
-
|
115
|
+
'initial_state' not in kwargs
|
117
116
|
):
|
118
|
-
kwargs[
|
119
|
-
|
117
|
+
kwargs['initial_state'] = {
|
118
|
+
'year': start_year or self.JSON_START_YEAR
|
120
119
|
}
|
121
120
|
# update defaults to correspond to user-defined parameter years
|
122
121
|
self.defaults = super().get_defaults()
|
123
|
-
label = self.defaults[
|
124
|
-
label[
|
122
|
+
label = self.defaults['schema']['labels']['year']
|
123
|
+
label['validators']['range']['max'] = last_budget_year
|
125
124
|
super().__init__(**kwargs)
|
126
125
|
|
127
126
|
def adjust( # pylint: disable=arguments-differ
|
@@ -166,9 +165,9 @@ class Parameters(pt.Parameters):
|
|
166
165
|
"""
|
167
166
|
if print_warnings:
|
168
167
|
_data = copy.deepcopy(self._data)
|
169
|
-
kwargs[
|
168
|
+
kwargs['ignore_warnings'] = False
|
170
169
|
else:
|
171
|
-
kwargs[
|
170
|
+
kwargs['ignore_warnings'] = True
|
172
171
|
self._warnings = {}
|
173
172
|
self._errors = {}
|
174
173
|
try:
|
@@ -178,20 +177,20 @@ class Parameters(pt.Parameters):
|
|
178
177
|
with self.transaction(
|
179
178
|
defer_validation=True,
|
180
179
|
raise_errors=True,
|
181
|
-
ignore_warnings=kwargs[
|
180
|
+
ignore_warnings=kwargs['ignore_warnings'],
|
182
181
|
):
|
183
182
|
return self.adjust_with_indexing(
|
184
183
|
params_or_path, raise_errors=True, **kwargs
|
185
184
|
)
|
186
|
-
except
|
185
|
+
except paramtools.ValidationError as ve:
|
187
186
|
if self.errors and raise_errors:
|
188
187
|
raise ve
|
189
188
|
if self.errors and not raise_errors:
|
190
189
|
return {}
|
191
190
|
if print_warnings:
|
192
|
-
print(
|
191
|
+
print('WARNING:')
|
193
192
|
print(self.warnings)
|
194
|
-
kwargs[
|
193
|
+
kwargs['ignore_warnings'] = True
|
195
194
|
# pylint: disable=possibly-used-before-assignment
|
196
195
|
self._data = _data
|
197
196
|
# pylint: enable=possibly-used-before-assignment
|
@@ -244,7 +243,7 @@ class Parameters(pt.Parameters):
|
|
244
243
|
3. Update all parameters that are not indexing related, i.e. they are
|
245
244
|
not "parameter_indexing_CPI_offset" or do not end with "-indexed".
|
246
245
|
|
247
|
-
4.
|
246
|
+
4. Returns parsed adjustment with all adjustments, including "-indexed"
|
248
247
|
parameters.
|
249
248
|
|
250
249
|
Notable side-effects:
|
@@ -270,11 +269,11 @@ class Parameters(pt.Parameters):
|
|
270
269
|
# reset values after the first year in which the
|
271
270
|
# parameter_indexing_CPI_offset is changed.
|
272
271
|
needs_reset = []
|
273
|
-
if params.get(
|
272
|
+
if params.get('parameter_indexing_CPI_offset') is not None:
|
274
273
|
# Update parameter_indexing_CPI_offset with new value.
|
275
274
|
cpi_adj = super().adjust(
|
276
|
-
{
|
277
|
-
params[
|
275
|
+
{'parameter_indexing_CPI_offset':
|
276
|
+
params['parameter_indexing_CPI_offset']}, **kwargs
|
278
277
|
)
|
279
278
|
# turn off extend now that parameter_indexing_CPI_offset
|
280
279
|
# has been updated.
|
@@ -282,42 +281,42 @@ class Parameters(pt.Parameters):
|
|
282
281
|
# Get first year in which parameter_indexing_CPI_offset
|
283
282
|
# is changed.
|
284
283
|
cpi_min_year = min(
|
285
|
-
cpi_adj[
|
286
|
-
key=lambda vo: vo[
|
284
|
+
cpi_adj['parameter_indexing_CPI_offset'],
|
285
|
+
key=lambda vo: vo['year']
|
287
286
|
)
|
288
287
|
|
289
288
|
rate_adjustment_vals = (
|
290
|
-
self.sel[
|
291
|
-
>= cpi_min_year[
|
289
|
+
self.sel['parameter_indexing_CPI_offset']['year']
|
290
|
+
>= cpi_min_year['year']
|
292
291
|
)
|
293
292
|
# "Undo" any existing parameter_indexing_CPI_offset for
|
294
293
|
# years after parameter_indexing_CPI_offset has
|
295
294
|
# been updated.
|
296
295
|
self._inflation_rates = self._inflation_rates[
|
297
|
-
:cpi_min_year[
|
296
|
+
:cpi_min_year['year'] - self.start_year
|
298
297
|
] + self._gfactors.price_inflation_rates(
|
299
|
-
cpi_min_year[
|
298
|
+
cpi_min_year['year'], self.LAST_BUDGET_YEAR)
|
300
299
|
|
301
300
|
# Then apply new parameter_indexing_CPI_offset values to
|
302
301
|
# inflation rates
|
303
302
|
for cpi_vo in rate_adjustment_vals:
|
304
303
|
self._inflation_rates[
|
305
|
-
cpi_vo[
|
306
|
-
] += cpi_vo[
|
304
|
+
cpi_vo['year'] - self.start_year
|
305
|
+
] += cpi_vo['value']
|
307
306
|
# 1. Delete all unknown values.
|
308
307
|
# 1.a For revision, these are years specified after cpi_min_year.
|
309
308
|
to_delete = {}
|
310
309
|
for param in params:
|
311
310
|
if (
|
312
|
-
param ==
|
311
|
+
param == 'parameter_indexing_CPI_offset' or
|
313
312
|
param in self._wage_indexed
|
314
313
|
):
|
315
314
|
continue
|
316
|
-
if param.endswith(
|
317
|
-
param = param.split(
|
318
|
-
if self._data[param].get(
|
315
|
+
if param.endswith('-indexed'):
|
316
|
+
param = param.split('-indexed')[0]
|
317
|
+
if self._data[param].get('indexed', False):
|
319
318
|
to_delete[param] = (
|
320
|
-
self.sel[param][
|
319
|
+
self.sel[param]['year'] > cpi_min_year['year']
|
321
320
|
)
|
322
321
|
needs_reset.append(param)
|
323
322
|
self.delete(to_delete, **kwargs)
|
@@ -351,7 +350,7 @@ class Parameters(pt.Parameters):
|
|
351
350
|
# only revert param in 2026 if it's not in revision
|
352
351
|
if params.get(param) is None:
|
353
352
|
# grab param values from 2017
|
354
|
-
vos = self.sel[param][
|
353
|
+
vos = self.sel[param]['year'] == pyear
|
355
354
|
# use final_ifactor to inflate from 2017 to 2026
|
356
355
|
for vo in vos:
|
357
356
|
long_param_vals[param].append(
|
@@ -359,7 +358,7 @@ class Parameters(pt.Parameters):
|
|
359
358
|
dict(
|
360
359
|
vo,
|
361
360
|
value=min(9e99, round(
|
362
|
-
vo[
|
361
|
+
vo['value'] * final_ifactor, 0)),
|
363
362
|
year=fyear,
|
364
363
|
)
|
365
364
|
)
|
@@ -370,36 +369,37 @@ class Parameters(pt.Parameters):
|
|
370
369
|
for param in self._data:
|
371
370
|
if (
|
372
371
|
param in params or
|
373
|
-
param ==
|
372
|
+
param == 'parameter_indexing_CPI_offset' or
|
374
373
|
param in self._wage_indexed
|
375
374
|
):
|
376
375
|
continue
|
377
|
-
if self._data[param].get(
|
376
|
+
if self._data[param].get('indexed', False):
|
378
377
|
# pylint: disable=singleton-comparison
|
379
|
-
to_delete[param] = self.sel[param][
|
378
|
+
to_delete[param] = self.sel[param]['_auto'] == True
|
380
379
|
# pylint warning message:
|
381
380
|
# Comparison 'self.sel[param]['_auto'] == True' should
|
382
381
|
# be 'self.sel[param]['_auto'] is True' if checking for
|
383
382
|
# the singleton value True, or
|
384
383
|
# 'bool(self.sel[param]['_auto'])' if testing for
|
385
384
|
# truthiness
|
385
|
+
# NOTE: following either pylint suggestion causes errors
|
386
386
|
# pylint: enable=singleton-comparison
|
387
387
|
needs_reset.append(param)
|
388
388
|
|
389
389
|
self.delete(to_delete, **kwargs)
|
390
390
|
|
391
|
-
self.extend(label=
|
391
|
+
self.extend(label='year')
|
392
392
|
|
393
393
|
# 2. Handle -indexed parameters.
|
394
394
|
self.label_to_extend = None
|
395
395
|
index_affected = set([])
|
396
396
|
for param, values in params.items():
|
397
|
-
if param.endswith(
|
398
|
-
base_param = param.split(
|
399
|
-
if not self._data[base_param].get(
|
400
|
-
msg = f
|
401
|
-
raise
|
402
|
-
{
|
397
|
+
if param.endswith('-indexed'):
|
398
|
+
base_param = param.split('-indexed')[0]
|
399
|
+
if not self._data[base_param].get('indexable', None):
|
400
|
+
msg = f'Parameter {base_param} is not indexable'
|
401
|
+
raise paramtools.ValidationError(
|
402
|
+
{'errors': {base_param: msg}}, labels=None
|
403
403
|
)
|
404
404
|
index_affected |= {param, base_param}
|
405
405
|
indexed_changes = {}
|
@@ -407,39 +407,36 @@ class Parameters(pt.Parameters):
|
|
407
407
|
indexed_changes[self.start_year] = values
|
408
408
|
elif isinstance(values, list):
|
409
409
|
for vo in values:
|
410
|
-
indexed_changes[
|
411
|
-
|
412
|
-
]
|
410
|
+
indexed_changes[
|
411
|
+
vo.get('year', self.start_year)
|
412
|
+
] = vo['value']
|
413
413
|
else:
|
414
|
-
msg =
|
415
|
-
|
416
|
-
|
417
|
-
)
|
418
|
-
raise pt.ValidationError(
|
419
|
-
{"errors": {base_param: msg}}, labels=None
|
414
|
+
msg = 'Index adjustment parameter must be boolean or list'
|
415
|
+
raise paramtools.ValidationError(
|
416
|
+
{'errors': {base_param: msg}}, labels=None
|
420
417
|
)
|
421
418
|
# 2.a Adjust values less than first year in which index status
|
422
419
|
# was changed.
|
423
420
|
if base_param in params:
|
424
421
|
min_index_change_year = min(indexed_changes.keys())
|
425
|
-
vos = self.sel[params[base_param]][
|
422
|
+
vos = self.sel[params[base_param]]['year'].lt(
|
426
423
|
min_index_change_year, strict=False
|
427
424
|
)
|
428
425
|
|
429
426
|
if list(vos):
|
430
|
-
min_adj_year = min(
|
431
|
-
|
432
|
-
]
|
427
|
+
min_adj_year = min(
|
428
|
+
vos, key=lambda vo: vo['year']
|
429
|
+
)['year']
|
433
430
|
self.delete(
|
434
431
|
{
|
435
432
|
base_param:
|
436
|
-
self.sel[base_param][
|
433
|
+
self.sel[base_param]['year'] > min_adj_year
|
437
434
|
}
|
438
435
|
)
|
439
436
|
super().adjust({base_param: vos}, **kwargs)
|
440
437
|
self.extend(
|
441
438
|
params=[base_param],
|
442
|
-
label=
|
439
|
+
label='year',
|
443
440
|
label_values=list(
|
444
441
|
range(self.start_year, min_index_change_year)
|
445
442
|
),
|
@@ -450,7 +447,7 @@ class Parameters(pt.Parameters):
|
|
450
447
|
# Get and delete all default values after year where
|
451
448
|
# indexed status changed.
|
452
449
|
self.delete(
|
453
|
-
{base_param: self.sel[base_param][
|
450
|
+
{base_param: self.sel[base_param]['year'] > year}
|
454
451
|
)
|
455
452
|
|
456
453
|
# 2.b Extend values for this parameter to the year where
|
@@ -458,25 +455,25 @@ class Parameters(pt.Parameters):
|
|
458
455
|
if year > self.start_year:
|
459
456
|
self.extend(
|
460
457
|
params=[base_param],
|
461
|
-
label=
|
458
|
+
label='year',
|
462
459
|
label_values=list(
|
463
460
|
range(self.start_year, year + 1)
|
464
461
|
),
|
465
462
|
)
|
466
463
|
|
467
464
|
# 2.c Set indexed status.
|
468
|
-
self._data[base_param][
|
465
|
+
self._data[base_param]['indexed'] = indexed_val
|
469
466
|
|
470
467
|
# 2.d Adjust with values greater than or equal to current
|
471
468
|
# year in params
|
472
469
|
if base_param in params:
|
473
|
-
vos = self.sel[params[base_param]][
|
470
|
+
vos = self.sel[params[base_param]]['year'].gte(
|
474
471
|
year, strict=False
|
475
472
|
)
|
476
473
|
super().adjust({base_param: vos}, **kwargs)
|
477
474
|
|
478
475
|
# 2.e Extend values through remaining years.
|
479
|
-
self.extend(params=[base_param], label=
|
476
|
+
self.extend(params=[base_param], label='year')
|
480
477
|
|
481
478
|
needs_reset.append(base_param)
|
482
479
|
# Re-instate ops.
|
@@ -525,7 +522,7 @@ class Parameters(pt.Parameters):
|
|
525
522
|
|
526
523
|
def wage_growth_rates(self, year=None):
|
527
524
|
"""
|
528
|
-
|
525
|
+
Returns wage growth rates used in parameter indexing.
|
529
526
|
"""
|
530
527
|
if year is not None:
|
531
528
|
syr = max(self.start_year, self._gfactors.first_year)
|
@@ -534,7 +531,7 @@ class Parameters(pt.Parameters):
|
|
534
531
|
|
535
532
|
def inflation_rates(self, year=None):
|
536
533
|
"""
|
537
|
-
|
534
|
+
Returns price inflation rates used in parameter indexing.
|
538
535
|
"""
|
539
536
|
if year is not None:
|
540
537
|
syr = max(self.start_year, self._gfactors.first_year)
|
@@ -552,7 +549,7 @@ class Parameters(pt.Parameters):
|
|
552
549
|
"""
|
553
550
|
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
554
551
|
# Handle case where project hasn't been initialized yet
|
555
|
-
if getattr(self,
|
552
|
+
if getattr(self, '_data', None) is None:
|
556
553
|
return Parameters.__init__(
|
557
554
|
self, start_year, num_years, last_known_year=last_known_year,
|
558
555
|
removed=removed, redefined=redefined,
|
@@ -593,71 +590,67 @@ class Parameters(pt.Parameters):
|
|
593
590
|
"""
|
594
591
|
# pylint: disable=too-many-branches
|
595
592
|
if not isinstance(revision, dict):
|
596
|
-
raise
|
597
|
-
{
|
593
|
+
raise paramtools.ValidationError(
|
594
|
+
{'errors': {'schema': 'Revision must be a dictionary'}},
|
598
595
|
None
|
599
596
|
)
|
600
597
|
new_params = defaultdict(list)
|
601
598
|
for param, val in revision.items():
|
602
599
|
if not isinstance(param, str):
|
603
|
-
msg = f
|
604
|
-
raise
|
605
|
-
{
|
600
|
+
msg = f'Parameter {param} is not a string'
|
601
|
+
raise paramtools.ValidationError(
|
602
|
+
{'errors': {'schema': msg}},
|
606
603
|
None
|
607
604
|
)
|
608
605
|
if (
|
609
606
|
param not in self._data and
|
610
|
-
param.split(
|
607
|
+
param.split('-indexed')[0] not in self._data
|
611
608
|
):
|
612
609
|
if self._removed_params and param in self._removed_params:
|
613
|
-
msg = f
|
610
|
+
msg = f'{param} {self._removed_params[param]}'
|
614
611
|
elif (
|
615
612
|
self._redefined_params and param in self._redefined_params
|
616
613
|
):
|
617
614
|
msg = self._redefined_params[param]
|
618
615
|
else:
|
619
|
-
msg = f
|
620
|
-
raise
|
621
|
-
{
|
616
|
+
msg = f'Parameter {param} does not exist'
|
617
|
+
raise paramtools.ValidationError(
|
618
|
+
{'errors': {'schema': msg}},
|
622
619
|
None
|
623
620
|
)
|
624
|
-
if param.endswith(
|
621
|
+
if param.endswith('-indexed'):
|
625
622
|
for year, yearval in val.items():
|
626
|
-
new_params[param] += [{
|
623
|
+
new_params[param] += [{'year': year, 'value': yearval}]
|
627
624
|
elif isinstance(val, dict):
|
628
625
|
for year, yearval in val.items():
|
629
626
|
val = getattr(self, param)
|
630
627
|
if (
|
631
|
-
self._data[param].get(
|
628
|
+
self._data[param].get('type', None) == 'str' and
|
632
629
|
isinstance(yearval, str)
|
633
630
|
):
|
634
|
-
new_params[param] += [{
|
631
|
+
new_params[param] += [{'value': yearval}]
|
635
632
|
continue
|
636
633
|
|
637
634
|
yearval = np.array(yearval)
|
638
635
|
if (
|
639
|
-
getattr(val,
|
636
|
+
getattr(val, 'shape', None) and
|
640
637
|
yearval.shape != val[0].shape
|
641
638
|
):
|
642
639
|
exp_dims = val[0].shape
|
643
640
|
if exp_dims == tuple():
|
644
|
-
msg =
|
645
|
-
f"{param} is not an array "
|
646
|
-
f"parameter."
|
647
|
-
)
|
641
|
+
msg = f'{param} is not an array parameter'
|
648
642
|
elif yearval.shape:
|
649
643
|
msg = (
|
650
|
-
f
|
651
|
-
f
|
652
|
-
f"elements."
|
644
|
+
f'{param} has {yearval.shape[0]} elements '
|
645
|
+
f'but should only have {exp_dims[0]} elements'
|
653
646
|
)
|
654
647
|
else:
|
655
648
|
msg = (
|
656
|
-
f
|
657
|
-
f
|
649
|
+
f'{param} is an array parameter with '
|
650
|
+
f'{exp_dims[0]} elements'
|
658
651
|
)
|
659
|
-
raise
|
660
|
-
{
|
652
|
+
raise paramtools.ValidationError(
|
653
|
+
{'errors': {'schema': msg}},
|
661
654
|
None
|
662
655
|
)
|
663
656
|
|
@@ -669,11 +662,11 @@ class Parameters(pt.Parameters):
|
|
669
662
|
new_params[param] += value_objects
|
670
663
|
else:
|
671
664
|
msg = (
|
672
|
-
f
|
673
|
-
|
665
|
+
f'{param} must be a year:value dictionary '
|
666
|
+
'if you are not using the new adjust method'
|
674
667
|
)
|
675
|
-
raise
|
676
|
-
{
|
668
|
+
raise paramtools.ValidationError(
|
669
|
+
{'errors': {'schema': msg}},
|
677
670
|
None
|
678
671
|
)
|
679
672
|
return self.adjust(
|
@@ -689,17 +682,17 @@ class Parameters(pt.Parameters):
|
|
689
682
|
@property
|
690
683
|
def current_year(self):
|
691
684
|
"""Propery docstring"""
|
692
|
-
return self.label_grid[
|
685
|
+
return self.label_grid['year'][0]
|
693
686
|
|
694
687
|
@property
|
695
688
|
def start_year(self):
|
696
689
|
"""Propery docstring"""
|
697
|
-
return self._stateless_label_grid[
|
690
|
+
return self._stateless_label_grid['year'][0]
|
698
691
|
|
699
692
|
@property
|
700
693
|
def end_year(self):
|
701
694
|
"""Propery docstring"""
|
702
|
-
return self._stateless_label_grid[
|
695
|
+
return self._stateless_label_grid['year'][-1]
|
703
696
|
|
704
697
|
@property
|
705
698
|
def num_years(self):
|
@@ -765,7 +758,7 @@ class Parameters(pt.Parameters):
|
|
765
758
|
if obj is None:
|
766
759
|
return {}
|
767
760
|
|
768
|
-
full_dict =
|
761
|
+
full_dict = paramtools.read_json(obj)
|
769
762
|
|
770
763
|
# check top-level key contents of dictionary
|
771
764
|
if topkey in full_dict.keys():
|
@@ -781,14 +774,14 @@ class Parameters(pt.Parameters):
|
|
781
774
|
|
782
775
|
def metadata(self):
|
783
776
|
"""
|
784
|
-
|
777
|
+
Returns parameter specification.
|
785
778
|
"""
|
786
779
|
return self.specification(meta_data=True, use_state=False)
|
787
780
|
|
788
781
|
@staticmethod
|
789
782
|
def years_in_revision(revision):
|
790
783
|
"""
|
791
|
-
|
784
|
+
Returns list of years in specified revision dictionary, which is
|
792
785
|
assumed to have a param:year:value format.
|
793
786
|
"""
|
794
787
|
assert isinstance(revision, dict)
|
@@ -808,17 +801,94 @@ class Parameters(pt.Parameters):
|
|
808
801
|
``pol.EITC_c``.
|
809
802
|
"""
|
810
803
|
if (
|
811
|
-
attr.startswith(
|
812
|
-
attr[1:] in super().__getattribute__(
|
804
|
+
attr.startswith('_') and
|
805
|
+
attr[1:] in super().__getattribute__('_data')
|
813
806
|
):
|
814
807
|
return self.to_array(
|
815
808
|
attr[1:], year=list(range(self.start_year, self.end_year + 1))
|
816
809
|
)
|
817
|
-
raise AttributeError(f
|
810
|
+
raise AttributeError(f'{attr} is not defined')
|
811
|
+
|
812
|
+
def extend_func(
|
813
|
+
self,
|
814
|
+
param: str,
|
815
|
+
extend_vo: paramtools.ValueObject,
|
816
|
+
known_vo: paramtools.ValueObject,
|
817
|
+
extend_grid: List,
|
818
|
+
label: str,
|
819
|
+
):
|
820
|
+
"""
|
821
|
+
Method for applying indexing rates to extended parameter values.
|
822
|
+
Returns:
|
823
|
+
- `extend_vo`: New `paramtools.ValueObject`.
|
824
|
+
"""
|
825
|
+
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
826
|
+
# pylint: disable=too-many-locals,too-many-branches
|
827
|
+
if not self.uses_extend_func or not self._data[param].get(
|
828
|
+
'indexed', False
|
829
|
+
):
|
830
|
+
return extend_vo
|
831
|
+
known_val = known_vo[label]
|
832
|
+
toext_val = extend_vo[label]
|
833
|
+
if toext_val == known_val:
|
834
|
+
return extend_vo
|
835
|
+
|
836
|
+
trace = False
|
837
|
+
# params_to_trace = ['II_em']
|
838
|
+
# params_to_trace = ['II_brk3']
|
839
|
+
params_to_trace = ['EITC_c']
|
840
|
+
# params_to_trace = ['ID_AmountCap_Switch']
|
841
|
+
if trace and param in params_to_trace: # pragma: no cover
|
842
|
+
vlabel = None
|
843
|
+
vvalue = None
|
844
|
+
if len(extend_vo) > 2:
|
845
|
+
extend_vo_keys = extend_vo.keys()
|
846
|
+
vo_labels = ['MARS', 'EIC', 'idedtype']
|
847
|
+
for vo_label in vo_labels:
|
848
|
+
if vo_label in extend_vo_keys:
|
849
|
+
vlabel = vo_label
|
850
|
+
vvalue = extend_vo[vlabel]
|
851
|
+
break
|
852
|
+
assert vlabel, f'{param} has no valid vector label'
|
853
|
+
print(
|
854
|
+
'***param,yr0,yr1,vlabel,vvalue=',
|
855
|
+
param, known_val, toext_val, vlabel, vvalue,
|
856
|
+
)
|
857
|
+
# print('extend_vo=', extend_vo)
|
858
|
+
# print('known_vo=', known_vo)
|
859
|
+
print('before:extend_vo[value]=', extend_vo['value'])
|
860
|
+
|
861
|
+
known_ix = extend_grid.index(known_val)
|
862
|
+
toext_ix = extend_grid.index(toext_val)
|
863
|
+
if toext_ix > known_ix:
|
864
|
+
# grow value according to the indexing rate supplied by
|
865
|
+
# the user defined self.get_index_rate method
|
866
|
+
for ix in range(known_ix, toext_ix):
|
867
|
+
v = extend_vo['value'] * (
|
868
|
+
1 + self.get_index_rate(param, extend_grid[ix])
|
869
|
+
)
|
870
|
+
extend_vo['value'] = np.round(v, 2) if v < 9e99 else 9e99
|
871
|
+
else: # pragma: no cover
|
872
|
+
# shrink value according to the indexing rate supplied by
|
873
|
+
# the user defined self.get_index_rate method
|
874
|
+
for ix in reversed(range(toext_ix, known_ix)):
|
875
|
+
v = (
|
876
|
+
extend_vo['value']
|
877
|
+
* (1 + self.get_index_rate(param, extend_grid[ix])) ** -1
|
878
|
+
)
|
879
|
+
extend_vo['value'] = np.round(v, 2) if v < 9e99 else 9e99
|
880
|
+
|
881
|
+
if trace and param in params_to_trace: # pragma: no cover
|
882
|
+
print('after:extend_vo[value]=', extend_vo['value'])
|
883
|
+
if toext_val == 2035:
|
884
|
+
for pname in params_to_trace:
|
885
|
+
print(f'data[{pname}]=', self._data[pname])
|
886
|
+
|
887
|
+
return extend_vo
|
818
888
|
|
819
889
|
|
820
890
|
TaxcalcReform = Union[str, Mapping[int, Any]]
|
821
|
-
ParamToolsAdjustment = Union[str, List[
|
891
|
+
ParamToolsAdjustment = Union[str, List[paramtools.ValueObject]]
|
822
892
|
|
823
893
|
|
824
894
|
def is_paramtools_format(params: Union[TaxcalcReform, ParamToolsAdjustment]):
|
@@ -845,11 +915,11 @@ def is_paramtools_format(params: Union[TaxcalcReform, ParamToolsAdjustment]):
|
|
845
915
|
"ss_rate": [{"year": 2024, "value": 0.2}]}
|
846
916
|
}
|
847
917
|
|
848
|
-
Returns
|
918
|
+
Returns:
|
849
919
|
-------
|
850
920
|
bool:
|
851
|
-
|
852
|
-
|
921
|
+
Whether ``params`` is likely to be a ParamTools formatted
|
922
|
+
adjustment or not.
|
853
923
|
"""
|
854
924
|
for data in params.values():
|
855
925
|
if isinstance(data, dict):
|