From fca981772b6b762c6d5b6ae106ae25ef6e053c31 Mon Sep 17 00:00:00 2001 From: "martin.holmer@gmail.com" Date: Sat, 14 Dec 2024 18:59:43 -0500 Subject: [PATCH 01/11] Remove setting self._gfactors in Parameters.adjust_with_indexing --- taxcalc/parameters.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/taxcalc/parameters.py b/taxcalc/parameters.py index 8ebbf04cb..2ec9ba004 100644 --- a/taxcalc/parameters.py +++ b/taxcalc/parameters.py @@ -1,15 +1,12 @@ -import copy import os -import re +import copy from collections import defaultdict from typing import Union, Mapping, Any, List import marshmallow as ma import paramtools as pt import numpy as np -import requests -import taxcalc from taxcalc.growfactors import GrowFactors @@ -249,7 +246,6 @@ def adjust_with_indexing(self, params_or_path, **kwargs): label_to_extend = self.label_to_extend array_first = self.array_first self.array_first = False - self._gfactors = GrowFactors() params = self.read_params(params_or_path) @@ -508,12 +504,14 @@ def set_rates(self): def wage_growth_rates(self, year=None): if year is not None: - return self._wage_growth_rates[year - self.start_year] + syr = max(self.start_year, self._gfactors.first_year) + return self._wage_growth_rates[year - syr] return self._wage_growth_rates or [] def inflation_rates(self, year=None): if year is not None: - return self._inflation_rates[year - self.start_year] + syr = max(self.start_year, self._gfactors.first_year) + return self._inflation_rates[year - syr] return self._inflation_rates or [] # alias methods below From dd98ef0ac3418c7ce596769de0a0053728a295bd Mon Sep 17 00:00:00 2001 From: "martin.holmer@gmail.com" Date: Sat, 14 Dec 2024 19:01:13 -0500 Subject: [PATCH 02/11] Remove setting self._gfactors in Policy.set_rates --- taxcalc/policy.py | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/taxcalc/policy.py b/taxcalc/policy.py index 1c5bf52f7..827342b87 100644 --- a/taxcalc/policy.py +++ b/taxcalc/policy.py @@ -148,7 +148,10 @@ def parameter_list(): return [k for k in defaults if k != "schema"] def set_rates(self): - """Initialize taxcalc indexing data.""" + """ + Initialize policy parameter indexing rates. + """ + syr = max(self.start_year, self._gfactors.first_year) cpi_vals = [ vo["value"] for vo in self._data["parameter_indexing_CPI_offset"]["value"] @@ -156,24 +159,25 @@ def set_rates(self): # extend parameter_indexing_CPI_offset values through budget window # if they have not been extended already. cpi_vals = cpi_vals + cpi_vals[-1:] * ( - self.end_year - self.start_year + 1 - len(cpi_vals) + self.end_year - syr + 1 - len(cpi_vals) ) - cpi_offset = { - (self.start_year + ix): val - for ix, val in enumerate(cpi_vals) - } - - self._gfactors = GrowFactors() - - self._inflation_rates = [ - np.round(rate + cpi_offset[self.start_year + ix], 4) - for ix, rate in enumerate( - self._gfactors.price_inflation_rates( - self.start_year, self.end_year + if any(cpi_vals): + cpi_offset = { + (self.start_year + ix): val + for ix, val in enumerate(cpi_vals) + } + self._inflation_rates = [ + np.round(rate + cpi_offset[self.start_year + ix], 4) + for ix, rate in enumerate( + self._gfactors.price_inflation_rates( + syr, self.end_year + ) ) + ] + else: # all cpi_vals are zero + self._inflation_rates = self._gfactors.price_inflation_rates( + syr, self.end_year ) - ] - self._wage_growth_rates = self._gfactors.wage_growth_rates( - self.start_year, self.end_year + syr, self.end_year ) From 051727dd6a9b00ab1de2908e080b8662d9834b59 Mon Sep 17 00:00:00 2001 From: "martin.holmer@gmail.com" Date: Sat, 14 Dec 2024 19:54:08 -0500 Subject: [PATCH 03/11] Fix reform_documentation logic --- taxcalc/calculator.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/taxcalc/calculator.py b/taxcalc/calculator.py index d0b92a598..3c1cac849 100644 --- a/taxcalc/calculator.py +++ b/taxcalc/calculator.py @@ -1110,7 +1110,7 @@ def read_json_param_objects(reform, assump): return param_dict @staticmethod - def reform_documentation(params, policy_dicts=None): + def reform_documentation(params, growfactors, policy_dicts=None): """ Generate reform documentation versus current-law policy. @@ -1120,6 +1120,9 @@ def reform_documentation(params, policy_dicts=None): dictionary is structured like dict returned from the static Calculator.read_json_param_objects() method + growfactors: GrowFactors + GrowFactors object used to construct Calculator Policy object + policy_dicts : list of dict or None each dictionary in list is a params['policy'] dictionary representing second and subsequent elements of a compound @@ -1255,13 +1258,14 @@ def lines(text, num_indent_spaces, max_line_length=77): # create Policy object with current-law-policy values gdiff_base = GrowDiff() gdiff_base.update_growdiff(params['growdiff_baseline']) - gfactors_clp = GrowFactors() + assert isinstance(growfactors, GrowFactors) + gfactors_clp = copy.deepcopy(growfactors) gdiff_base.apply_to(gfactors_clp) clp = Policy(gfactors=gfactors_clp) # create Policy object with post-reform values gdiff_resp = GrowDiff() gdiff_resp.update_growdiff(params['growdiff_response']) - gfactors_ref = GrowFactors() + gfactors_ref = copy.deepcopy(growfactors) gdiff_base.apply_to(gfactors_ref) gdiff_resp.apply_to(gfactors_ref) ref = Policy(gfactors=gfactors_ref) From 46f95a40580eb3501971efe10ff1a524ef951340 Mon Sep 17 00:00:00 2001 From: "martin.holmer@gmail.com" Date: Sat, 14 Dec 2024 19:55:46 -0500 Subject: [PATCH 04/11] Use new reform_documentation arguments in test --- taxcalc/tests/test_calculator.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/taxcalc/tests/test_calculator.py b/taxcalc/tests/test_calculator.py index c9f2cb743..ac2e18004 100644 --- a/taxcalc/tests/test_calculator.py +++ b/taxcalc/tests/test_calculator.py @@ -13,7 +13,7 @@ import pytest import numpy as np import pandas as pd -from taxcalc import Policy, Records, Calculator, Consumption +from taxcalc import GrowFactors, Policy, Records, Calculator, Consumption def test_make_calculator(cps_subsample): @@ -568,7 +568,8 @@ def test_noreform_documentation(): """ params = Calculator.read_json_param_objects(reform_json, assump_json) assert isinstance(params, dict) - actual_doc = Calculator.reform_documentation(params) + gfs = GrowFactors() + actual_doc = Calculator.reform_documentation(params, gfs) expected_doc = ( 'REFORM DOCUMENTATION\n' 'Baseline Growth-Difference Assumption Values by Year:\n' @@ -623,7 +624,8 @@ def test_reform_documentation(): params = Calculator.read_json_param_objects(reform_json, assump_json) assert isinstance(params, dict) second_reform = {'II_em': {2019: 6500}} - doc = Calculator.reform_documentation(params, [second_reform]) + gfs = GrowFactors() + doc = Calculator.reform_documentation(params, gfs, [second_reform]) assert isinstance(doc, str) dump = False # set to True to print documentation and force test failure if dump: From 67f63236322cdf266dc6f722039668cb09a06051 Mon Sep 17 00:00:00 2001 From: "martin.holmer@gmail.com" Date: Sat, 14 Dec 2024 19:57:18 -0500 Subject: [PATCH 05/11] Use new reform_documentation arguments in TaxCalcIO logic --- taxcalc/taxcalcio.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/taxcalc/taxcalcio.py b/taxcalc/taxcalcio.py index 2ead655e2..963731c13 100644 --- a/taxcalc/taxcalcio.py +++ b/taxcalc/taxcalcio.py @@ -68,6 +68,7 @@ def __init__(self, input_data, tax_year, baseline, reform, assump, outdir=None): # pylint: disable=too-many-arguments,too-many-locals # pylint: disable=too-many-branches,too-many-statements + self.gf_reform = None self.errmsg = '' # check name and existence of INPUT file inp = 'x' @@ -288,6 +289,7 @@ def init(self, input_data, tax_year, baseline, reform, assump, gfactors_ref = GrowFactors() gdiff_baseline.apply_to(gfactors_ref) gdiff_response.apply_to(gfactors_ref) + self.gf_reform = copy.deepcopy(gfactors_ref) # create Policy objects: # ... the baseline Policy object base = Policy(gfactors=gfactors_base) @@ -571,10 +573,13 @@ def write_doc_file(self): Write reform documentation to text file. """ if len(self.policy_dicts) <= 1: - doc = Calculator.reform_documentation(self.param_dict) + doc = Calculator.reform_documentation( + self.param_dict, self.gf_reform + ) else: - doc = Calculator.reform_documentation(self.param_dict, - self.policy_dicts[1:]) + doc = Calculator.reform_documentation( + self.param_dict, self.gf_reform, self.policy_dicts[1:] + ) doc_fname = self._output_filename.replace('.csv', '-doc.text') with open(doc_fname, 'w', encoding='utf-8') as dfile: dfile.write(doc) From 138a4e18c9a806fa391b56ba40964623241149dc Mon Sep 17 00:00:00 2001 From: "martin.holmer@gmail.com" Date: Sat, 14 Dec 2024 19:58:31 -0500 Subject: [PATCH 06/11] Fix test_parameters to use new Parameters logic --- taxcalc/tests/test_parameters.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/taxcalc/tests/test_parameters.py b/taxcalc/tests/test_parameters.py index ef445e1cd..438e24cf1 100644 --- a/taxcalc/tests/test_parameters.py +++ b/taxcalc/tests/test_parameters.py @@ -18,10 +18,10 @@ Parameters, Policy, Consumption, + GrowDiff, GrowFactors, is_paramtools_format, ) -from taxcalc.growdiff import GrowDiff # Test specification and use of simple Parameters-derived class that has @@ -244,7 +244,7 @@ def test_json_file_contents(tests_path, fname): elif fname == "growdiff.json": o = GrowDiff() param_list = [] - for k in o: + for k in o: if k[0].isupper(): # find parameters by case of first letter param_list.append(k) for param in param_list: @@ -386,7 +386,7 @@ class ArrayParams(Parameters): array_first = False START_YEAR = 2013 - LAST_YEAR = 2034 + LAST_YEAR = 2028 NUM_YEARS = LAST_YEAR - START_YEAR + 1 def __init__(self, **kwargs): @@ -395,6 +395,7 @@ def __init__(self, **kwargs): ArrayParams.NUM_YEARS, **kwargs ) + self._gfactors = GrowFactors() self._inflation_rates = [0.02] * self.num_years self._wage_growth_rates = [0.03] * self.num_years From 7f6958ea5e6342c489b4e338cc257d8f3d8458f7 Mon Sep 17 00:00:00 2001 From: "martin.holmer@gmail.com" Date: Sun, 15 Dec 2024 09:59:50 -0500 Subject: [PATCH 07/11] Update version to 4.3.4a --- taxcalc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taxcalc/__init__.py b/taxcalc/__init__.py index 9aba956b3..fa0c5d7bb 100644 --- a/taxcalc/__init__.py +++ b/taxcalc/__init__.py @@ -14,6 +14,6 @@ from taxcalc.utils import * from taxcalc.cli import * -__version__ = '4.3.4' +__version__ = '4.3.4a' __min_python3_version__ = 10 __max_python3_version__ = 12 From 83d0d39788463f7ecbd90acf214ec049c1efceb7 Mon Sep 17 00:00:00 2001 From: "martin.holmer@gmail.com" Date: Sun, 15 Dec 2024 12:52:17 -0500 Subject: [PATCH 08/11] Fix typo --- taxcalc/tests/test_parameters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taxcalc/tests/test_parameters.py b/taxcalc/tests/test_parameters.py index 438e24cf1..b72fe8256 100644 --- a/taxcalc/tests/test_parameters.py +++ b/taxcalc/tests/test_parameters.py @@ -386,7 +386,7 @@ class ArrayParams(Parameters): array_first = False START_YEAR = 2013 - LAST_YEAR = 2028 + LAST_YEAR = 2034 NUM_YEARS = LAST_YEAR - START_YEAR + 1 def __init__(self, **kwargs): From 5b009124fa92c26ad3c21e5d877ccadaf4ccfc4d Mon Sep 17 00:00:00 2001 From: "martin.holmer@gmail.com" Date: Sun, 15 Dec 2024 13:38:10 -0500 Subject: [PATCH 09/11] No coverage of obsolete code now that _offset is always zero in p_c_l.json --- taxcalc/policy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/taxcalc/policy.py b/taxcalc/policy.py index 827342b87..c5aab9e6f 100644 --- a/taxcalc/policy.py +++ b/taxcalc/policy.py @@ -1,4 +1,4 @@ -""" +>""" Tax-Calculator federal tax policy Policy class. """ # CODING-STYLE CHECKS: @@ -161,7 +161,7 @@ def set_rates(self): cpi_vals = cpi_vals + cpi_vals[-1:] * ( self.end_year - syr + 1 - len(cpi_vals) ) - if any(cpi_vals): + if any(cpi_vals): # pragma: no cover cpi_offset = { (self.start_year + ix): val for ix, val in enumerate(cpi_vals) From b60499eff629a282436fe3be5eb0d82a88432265 Mon Sep 17 00:00:00 2001 From: "martin.holmer@gmail.com" Date: Sun, 15 Dec 2024 13:42:33 -0500 Subject: [PATCH 10/11] Fix typo in policy.py module --- taxcalc/policy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taxcalc/policy.py b/taxcalc/policy.py index c5aab9e6f..11eb9981e 100644 --- a/taxcalc/policy.py +++ b/taxcalc/policy.py @@ -1,4 +1,4 @@ ->""" +""" Tax-Calculator federal tax policy Policy class. """ # CODING-STYLE CHECKS: From 8cf5ba56e9735077944bcef9dfa1476e168b77d4 Mon Sep 17 00:00:00 2001 From: "martin.holmer@gmail.com" Date: Sun, 15 Dec 2024 14:09:11 -0500 Subject: [PATCH 11/11] Simplify Policy.set_rates logic --- taxcalc/policy.py | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/taxcalc/policy.py b/taxcalc/policy.py index 11eb9981e..8f101dd6d 100644 --- a/taxcalc/policy.py +++ b/taxcalc/policy.py @@ -8,7 +8,6 @@ import os import json from pathlib import Path -import numpy as np from taxcalc.parameters import Parameters from taxcalc.growfactors import GrowFactors @@ -151,33 +150,17 @@ def set_rates(self): """ Initialize policy parameter indexing rates. """ - syr = max(self.start_year, self._gfactors.first_year) cpi_vals = [ vo["value"] for vo in self._data["parameter_indexing_CPI_offset"]["value"] ] - # extend parameter_indexing_CPI_offset values through budget window - # if they have not been extended already. - cpi_vals = cpi_vals + cpi_vals[-1:] * ( - self.end_year - syr + 1 - len(cpi_vals) + # policy_current_law.json should not specify any non-zero values + # for the parameter_indexing_CPI_offset parameter, so check this + assert any(cpi_vals) is False + syr = max(self.start_year, self._gfactors.first_year) + self._inflation_rates = self._gfactors.price_inflation_rates( + syr, self.end_year ) - if any(cpi_vals): # pragma: no cover - cpi_offset = { - (self.start_year + ix): val - for ix, val in enumerate(cpi_vals) - } - self._inflation_rates = [ - np.round(rate + cpi_offset[self.start_year + ix], 4) - for ix, rate in enumerate( - self._gfactors.price_inflation_rates( - syr, self.end_year - ) - ) - ] - else: # all cpi_vals are zero - self._inflation_rates = self._gfactors.price_inflation_rates( - syr, self.end_year - ) self._wage_growth_rates = self._gfactors.wage_growth_rates( syr, self.end_year )