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

Patch structural reforms #5401

Merged
merged 5 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions changelog_entry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- bump: patch
changes:
added:
- Capability to select custom start time for simulations; this is a patch for structural reforms that occur at non-default time periods.
146 changes: 146 additions & 0 deletions docs/usage/structural_reform_dating.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Inputting Dates for Structural Reforms\n",
"\n",
"\"Structural\" reforms are those reforms that modify not only set values in the tax-benefit system, but also the formulas used to calculate taxes and benefits. These are typcially larger, more involved reforms that require custom coding.\n",
"\n",
"Due to the current limitations of the `Microsimulation` class, a code patch is required when running structural reforms with parameters that begin at any date other than January 1st of the current year. \n",
"\n",
"For example, the code cell below illustrates a standard way to instantiating a structural reform, without the patch, when simulating in 2024:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/opt/miniconda3/envs/us-3.11/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
" from .autonotebook import tqdm as notebook_tqdm\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Diff: -2605373719.2974854\n"
]
}
],
"source": [
"from policyengine_us import Microsimulation\n",
"from policyengine_core.reforms import Reform\n",
"\n",
"reform_1 = Reform.from_dict({\n",
" \"gov.contrib.salt_phase_out.in_effect\": {\n",
" \"2024-01-01.2100-12-31\": True\n",
" },\n",
" \"gov.contrib.salt_phase_out.rate.joint[1].rate\": {\n",
" \"2024-01-01.2100-12-31\": 0.001\n",
" },\n",
" \"gov.contrib.salt_phase_out.rate.joint[1].threshold\": {\n",
" \"2024-01-01.2100-12-31\": 200000\n",
" },\n",
" \"gov.contrib.salt_phase_out.rate.other[1].rate\": {\n",
" \"2024-01-01.2100-12-31\": 0.001\n",
" },\n",
" \"gov.contrib.salt_phase_out.rate.other[1].threshold\": {\n",
" \"2024-01-01.2100-12-31\": 400000\n",
" }\n",
"}, country_id=\"us\")\n",
"\n",
"\n",
"baseline_sim_1 = Microsimulation()\n",
"reformed_sim_1 = Microsimulation(reform=reform_1)\n",
"baseline_salt_1 = baseline_sim_1.calculate(\"salt_deduction\", period=2026)\n",
"reformed_salt_1 = reformed_sim_1.calculate(\"salt_deduction\", period=2026)\n",
"print(f\"Diff: {reformed_salt_1.sum() - baseline_salt_1.sum()}\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The cell below shows a series of reforms that begin in 2026, later than the current year. To effectively handle this case, we need to add an argument to the `Microsimulation` classes that we call. \n",
"\n",
"This argument, called `start_instant`, should be set to the same date as the start of the reforms, in ISO date format. In the case of the example below, this is `2026-01-01`, so our altered call to `Microsimulation` looks like:\n",
"\n",
"```\n",
"baseline_sim_2 = Microsimulation(start_instant=\"2026-01-01\")\n",
"reformed_sim_2 = Microsimulation(reform=reform_2, start_instant=\"2026-01-01\")\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Diff: -2605373719.2974854\n"
]
}
],
"source": [
"from policyengine_us import Microsimulation\n",
"from policyengine_core.reforms import Reform\n",
"\n",
"reform_2 = Reform.from_dict({\n",
" \"gov.contrib.salt_phase_out.in_effect\": {\n",
" \"2026-01-01.2100-12-31\": True\n",
" },\n",
" \"gov.contrib.salt_phase_out.rate.joint[1].rate\": {\n",
" \"2026-01-01.2100-12-31\": 0.001\n",
" },\n",
" \"gov.contrib.salt_phase_out.rate.joint[1].threshold\": {\n",
" \"2026-01-01.2100-12-31\": 200000\n",
" },\n",
" \"gov.contrib.salt_phase_out.rate.other[1].rate\": {\n",
" \"2026-01-01.2100-12-31\": 0.001\n",
" },\n",
" \"gov.contrib.salt_phase_out.rate.other[1].threshold\": {\n",
" \"2026-01-01.2100-12-31\": 400000\n",
" }\n",
"}, country_id=\"us\")\n",
"\n",
"\n",
"baseline_sim_2 = Microsimulation(start_instant=\"2026-01-01\")\n",
"reformed_sim_2 = Microsimulation(reform=reform_2, start_instant=\"2026-01-01\")\n",
"baseline_salt_2 = baseline_sim_2.calculate(\"salt_deduction\", period=2026)\n",
"reformed_salt_2 = reformed_sim_2.calculate(\"salt_deduction\", period=2026)\n",
"print(f\"Diff: {reformed_salt_2.sum() - baseline_salt_2.sum()}\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "us-3.11",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.10"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
72 changes: 67 additions & 5 deletions policyengine_us/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,33 @@
from .tools.default_uprating import add_default_uprating
from policyengine_us_data import DATASETS, CPS_2024

from typing import Annotated


COUNTRY_DIR = Path(__file__).parent

CURRENT_YEAR = 2024
year_start = str(CURRENT_YEAR) + "-01-01"
DEFAULT_START_DATE = str(CURRENT_YEAR) + "-01-01"


class CountryTaxBenefitSystem(TaxBenefitSystem):
"""
The tax-benefit system for the United States.
This structure is a modification of the -core
package's base TaxBenefitSystem class.

Args:
reform (tuple | None): A tuple of reforms to apply to the system.
If no reform is applied, the system will be initialized with the
default tax/benefit parameters.

start_instant(str: ISO date format YYYY-MM-DD): Optional; The date
at which the simulation begins; defaults to 2024-01-01; this is a
temporary patch for structural reforms, and must be set to the start
date of a structural reform parameter if it begins on a date other
than the first day of the current year.
"""

variables_dir = COUNTRY_DIR / "variables"
auto_carry_over_input_variables = True
basic_inputs = [
Expand All @@ -45,7 +65,13 @@ class CountryTaxBenefitSystem(TaxBenefitSystem):
]
modelled_policies = COUNTRY_DIR / "modelled_policies.yaml"

def __init__(self, reform=None):
def __init__(
self,
reform: tuple | None = None,
start_instant: Annotated[
str, "ISO date format YYYY-MM-DD"
] = DEFAULT_START_DATE,
):
super().__init__(entities, reform=reform)
self.load_parameters(COUNTRY_DIR / "parameters")
self.add_abolition_parameters()
Expand All @@ -62,7 +88,7 @@ def __init__(self, reform=None):
add_default_uprating(self)

structural_reform = create_structural_reforms_from_parameters(
self.parameters, year_start
self.parameters, start_instant
)
if reform is None:
reform = ()
Expand All @@ -85,6 +111,21 @@ def __init__(self, reform=None):


class Simulation(CoreSimulation):
"""
A simulation of the tax-benefit system for the United States,
defined against the base simulation class in the -core package.

This simulation is commonly used for household-level impacts, as it
does not include society-wide microdata.

Args:
start_instant(str: ISO date format YYYY-MM-DD): Optional; The date
at which the simulation begins; defaults to 2024-01-01; this is a
temporary patch for structural reforms, and must be set to the start
date of a structural reform parameter if it begins on a date other
than the first day of the current year.
"""

default_tax_benefit_system = CountryTaxBenefitSystem
default_tax_benefit_system_instance = system
default_role = "member"
Expand All @@ -93,10 +134,13 @@ class Simulation(CoreSimulation):
datasets = DATASETS

def __init__(self, *args, **kwargs):
start_instant: Annotated[str, "ISO date format YYYY-MM-DD"] = (
kwargs.pop("start_instant", DEFAULT_START_DATE)
)
super().__init__(*args, **kwargs)

reform = create_structural_reforms_from_parameters(
self.tax_benefit_system.parameters, year_start
self.tax_benefit_system.parameters, start_instant
)
if reform is not None:
self.apply_reform(reform)
Expand Down Expand Up @@ -137,6 +181,21 @@ def __init__(self, *args, **kwargs):


class Microsimulation(CoreMicrosimulation):
"""
A microsimulation of the tax-benefit system for the United States,
defined against the base microsimulation class in the -core package.

This simulation contains society-wide representative microdata, and is
thus suitable for society-level impacts.

Args:
start_instant(str: ISO date format YYYY-MM-DD): Optional; The date
at which the simulation begins; defaults to 2024-01-01; this is a
temporary patch for structural reforms, and must be set to the start
date of a structural reform parameter if it begins on a date other
than the first day of the current year.
"""

default_tax_benefit_system = CountryTaxBenefitSystem
default_tax_benefit_system_instance = system
default_dataset = CPS_2024
Expand All @@ -147,10 +206,13 @@ class Microsimulation(CoreMicrosimulation):
datasets = DATASETS

def __init__(self, *args, **kwargs):
start_instant: Annotated[str, "ISO date format YYYY-MM-DD"] = (
kwargs.pop("start_instant", DEFAULT_START_DATE)
)
super().__init__(*args, **kwargs)

reform = create_structural_reforms_from_parameters(
self.tax_benefit_system.parameters, year_start
self.tax_benefit_system.parameters, start_instant
)
if reform is not None:
self.apply_reform(reform)
Expand Down
Loading