From fa5511231e3c8692e889343e1de49ac0381d3a42 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Thu, 6 Nov 2025 16:37:02 -0500 Subject: [PATCH] Debug Tlaib notebooks Fixes #91 --- .../Congressional-Hackathon-2025 | 1 + us/debug_income_security_reforms.ipynb | 1147 +++++++++++++++++ us/test_ctc_adult_dependent_removal.ipynb | 376 ++++++ 3 files changed, 1524 insertions(+) create mode 160000 obbba_district_impacts/Congressional-Hackathon-2025 create mode 100644 us/debug_income_security_reforms.ipynb create mode 100644 us/test_ctc_adult_dependent_removal.ipynb diff --git a/obbba_district_impacts/Congressional-Hackathon-2025 b/obbba_district_impacts/Congressional-Hackathon-2025 new file mode 160000 index 0000000..3f6d05e --- /dev/null +++ b/obbba_district_impacts/Congressional-Hackathon-2025 @@ -0,0 +1 @@ +Subproject commit 3f6d05e76400c6e396a3a4eddd34a7b3f6919fc3 diff --git a/us/debug_income_security_reforms.ipynb b/us/debug_income_security_reforms.ipynb new file mode 100644 index 0000000..00d9386 --- /dev/null +++ b/us/debug_income_security_reforms.ipynb @@ -0,0 +1,1147 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Debug Tlaib Income Security Package Reforms\n", + "\n", + "Testing specific household scenarios to identify issues with reform impacts." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "c:\\Users\\dtsax\\envs\\pe\\Lib\\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" + ] + } + ], + "source": [ + "import pandas as pd\n", + "from policyengine_us import Simulation\n", + "\n", + "pd.set_option('display.float_format', '{:.2f}'.format)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Helper Functions" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def create_situation_baby_bonus():\n", + " \"\"\"Single parent with newborn (under 1), $50,000 income\"\"\"\n", + " return {\n", + " \"people\": {\n", + " \"parent\": {\n", + " \"age\": {\"2026\": 30},\n", + " \"employment_income\": {\"2026\": 50_000},\n", + " },\n", + " \"baby\": {\n", + " \"age\": {\"2026\": 0},\n", + " },\n", + " },\n", + " \"families\": {\"family\": {\"members\": [\"parent\", \"baby\"]}},\n", + " \"marital_units\": {\"marital_unit\": {\"members\": [\"parent\"]}},\n", + " \"tax_units\": {\"tax_unit\": {\"members\": [\"parent\", \"baby\"]}},\n", + " \"spm_units\": {\"spm_unit\": {\"members\": [\"parent\", \"baby\"]}},\n", + " \"households\": {\"household\": {\"members\": [\"parent\", \"baby\"]}},\n", + " }\n", + "\n", + "def create_situation_ecpa():\n", + " \"\"\"Married, three kids (6, 18, 19), $50,000 income\"\"\"\n", + " return {\n", + " \"people\": {\n", + " \"adult1\": {\n", + " \"age\": {\"2026\": 40},\n", + " \"employment_income\": {\"2026\": 50_000},\n", + " },\n", + " \"adult2\": {\n", + " \"age\": {\"2026\": 38},\n", + " },\n", + " \"child1\": {\n", + " \"age\": {\"2026\": 6},\n", + " },\n", + " \"child2\": {\n", + " \"age\": {\"2026\": 18},\n", + " },\n", + " \"child3\": {\n", + " \"age\": {\"2026\": 19},\n", + " },\n", + " },\n", + " \"families\": {\"family\": {\"members\": [\"adult1\", \"adult2\", \"child1\", \"child2\", \"child3\"]}},\n", + " \"marital_units\": {\"marital_unit\": {\"members\": [\"adult1\", \"adult2\"]}},\n", + " \"tax_units\": {\"tax_unit\": {\"members\": [\"adult1\", \"adult2\", \"child1\", \"child2\", \"child3\"]}},\n", + " \"spm_units\": {\"spm_unit\": {\"members\": [\"adult1\", \"adult2\", \"child1\", \"child2\", \"child3\"]}},\n", + " \"households\": {\"household\": {\"members\": [\"adult1\", \"adult2\", \"child1\", \"child2\", \"child3\"]}},\n", + " }\n", + "\n", + "def create_situation_boost():\n", + " \"\"\"Single person, $100,000 income\"\"\"\n", + " return {\n", + " \"people\": {\n", + " \"adult\": {\n", + " \"age\": {\"2026\": 35},\n", + " \"employment_income\": {\"2026\": 100_000},\n", + " },\n", + " },\n", + " \"families\": {\"family\": {\"members\": [\"adult\"]}},\n", + " \"marital_units\": {\"marital_unit\": {\"members\": [\"adult\"]}},\n", + " \"tax_units\": {\"tax_unit\": {\"members\": [\"adult\"]}},\n", + " \"spm_units\": {\"spm_unit\": {\"members\": [\"adult\"]}},\n", + " \"households\": {\"household\": {\"members\": [\"adult\"]}},\n", + " }\n", + "\n", + "def create_situation_combined():\n", + " \"\"\"Married, three kids (newborn, 18, 19), $100,000 income\"\"\"\n", + " return {\n", + " \"people\": {\n", + " \"adult1\": {\n", + " \"age\": {\"2026\": 40},\n", + " \"employment_income\": {\"2026\": 100_000},\n", + " },\n", + " \"adult2\": {\n", + " \"age\": {\"2026\": 38},\n", + " },\n", + " \"baby\": {\n", + " \"age\": {\"2026\": 0},\n", + " },\n", + " \"child2\": {\n", + " \"age\": {\"2026\": 18},\n", + " },\n", + " \"child3\": {\n", + " \"age\": {\"2026\": 19},\n", + " },\n", + " },\n", + " \"families\": {\"family\": {\"members\": [\"adult1\", \"adult2\", \"baby\", \"child2\", \"child3\"]}},\n", + " \"marital_units\": {\"marital_unit\": {\"members\": [\"adult1\", \"adult2\"]}},\n", + " \"tax_units\": {\"tax_unit\": {\"members\": [\"adult1\", \"adult2\", \"baby\", \"child2\", \"child3\"]}},\n", + " \"spm_units\": {\"spm_unit\": {\"members\": [\"adult1\", \"adult2\", \"baby\", \"child2\", \"child3\"]}},\n", + " \"households\": {\"household\": {\"members\": [\"adult1\", \"adult2\", \"baby\", \"child2\", \"child3\"]}},\n", + " }\n", + "\n", + "def test_scenario(situation, name, reform_params=None):\n", + " \"\"\"Test a scenario with baseline and reform\"\"\"\n", + " print(f\"\\n{'='*60}\")\n", + " print(f\"Testing: {name}\")\n", + " print(f\"{'='*60}\")\n", + " \n", + " # Baseline\n", + " baseline = Simulation(situation=situation)\n", + " \n", + " # Reform\n", + " if reform_params is None:\n", + " reform_params = {}\n", + " \n", + " reformed = Simulation(\n", + " situation=situation,\n", + " reform=reform_params\n", + " )\n", + " \n", + " # Standard variables that exist in both baseline and reform\n", + " standard_variables = [\n", + " # Income components\n", + " \"employment_income\",\n", + " \"adjusted_gross_income\",\n", + " \n", + " # Standard tax credits (check if affected)\n", + " \"income_tax_before_refundable_credits\",\n", + " \"income_tax_non_refundable_credits\",\n", + " \"non_refundable_ctc\",\n", + " \"income_tax_refundable_credits\",\n", + " \"refundable_ctc\",\n", + " \"eitc\",\n", + " \"cdcc\",\n", + " \"income_tax\",\n", + " \n", + " # Other benefit programs (check for unintended changes)\n", + " \"snap\",\n", + " \"wic\",\n", + " \"tanf\",\n", + " \"social_security\",\n", + " \"ssi\",\n", + " \"unemployment_compensation\",\n", + " \"free_school_meals\",\n", + " \"reduced_price_school_meals\",\n", + " \n", + " # Aggregated outcomes\n", + " \"household_benefits\",\n", + " \"household_tax_before_refundable_credits\",\n", + " \"household_refundable_tax_credits\",\n", + " \"household_net_income\",\n", + " ]\n", + " \n", + " # Reform-specific variables (only exist in reform)\n", + " reform_variables = [\n", + " \"baby_bonus_act_payment\",\n", + " \"boost_act_payment\",\n", + " \"boost_act_tax\",\n", + " \"ecpa_child_benefit\",\n", + " \"ecpa_adult_dependent_credit\",\n", + " \"ecpa_filer_credit\",\n", + " ]\n", + " \n", + " results = []\n", + " \n", + " # Calculate standard variables\n", + " for var in standard_variables:\n", + " try:\n", + " base_val = baseline.calculate(var, \"2026\")[0]\n", + " reform_val = reformed.calculate(var, \"2026\")[0]\n", + " diff = reform_val - base_val\n", + " \n", + " results.append({\n", + " \"Variable\": var,\n", + " \"Baseline\": base_val,\n", + " \"Reform\": reform_val,\n", + " \"Change\": diff\n", + " })\n", + " except Exception as e:\n", + " results.append({\n", + " \"Variable\": var,\n", + " \"Baseline\": \"ERROR\",\n", + " \"Reform\": \"ERROR\",\n", + " \"Change\": str(e)[:50]\n", + " })\n", + " \n", + " # Calculate reform-specific variables (baseline = 0)\n", + " for var in reform_variables:\n", + " try:\n", + " reform_val = reformed.calculate(var, \"2026\")[0]\n", + " results.append({\n", + " \"Variable\": var,\n", + " \"Baseline\": 0,\n", + " \"Reform\": reform_val,\n", + " \"Change\": reform_val\n", + " })\n", + " except Exception as e:\n", + " # Variable doesn't exist in this reform configuration\n", + " pass\n", + " \n", + " df = pd.DataFrame(results)\n", + " \n", + " # Only show rows with non-zero baseline or reform values\n", + " df_nonzero = df[\n", + " ((df[\"Baseline\"] != 0) & (df[\"Baseline\"] != \"ERROR\")) | \n", + " ((df[\"Reform\"] != 0) & (df[\"Reform\"] != \"ERROR\"))\n", + " ]\n", + " \n", + " print(\"\\n=== NON-ZERO VALUES ===\")\n", + " print(df_nonzero.to_string(index=False))\n", + " \n", + " # Show rows with changes\n", + " df_changes = df[\n", + " (df[\"Change\"] != 0) & (df[\"Change\"] != \"ERROR\")\n", + " ]\n", + " print(\"\\n=== VALUES THAT CHANGED ===\")\n", + " print(df_changes.to_string(index=False))\n", + " \n", + " return df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test 1: Baby Bonus (Single Parent with Newborn, $50k)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "============================================================\n", + "Testing: Baby Bonus: Single parent with newborn, $50k\n", + "============================================================\n", + "\n", + "=== NON-ZERO VALUES ===\n", + " Variable Baseline Reform Change\n", + " employment_income 50000.00 50000.00 0.00\n", + " adjusted_gross_income 50000.00 50000.00 0.00\n", + " income_tax_before_refundable_credits 548.00 548.00 0.00\n", + " income_tax_non_refundable_credits 2200.00 2200.00 0.00\n", + " non_refundable_ctc 2200.00 2200.00 0.00\n", + " income_tax_refundable_credits 246.63 246.63 0.00\n", + " eitc 246.63 246.63 0.00\n", + " income_tax 301.37 301.37 0.00\n", + " household_benefits 2186.02 4186.02 2000.00\n", + "household_tax_before_refundable_credits 4373.00 4373.00 0.00\n", + " household_refundable_tax_credits 246.63 246.63 0.00\n", + " household_net_income 48059.65 50059.65 2000.00\n", + "\n", + "=== VALUES THAT CHANGED ===\n", + " Variable Baseline Reform Change\n", + " household_benefits 2186.02 4186.02 2000.00\n", + "household_net_income 48059.65 50059.65 2000.00\n" + ] + } + ], + "source": [ + "baby_bonus_reform = {\n", + " \"gov.contrib.congress.tlaib.income_security_package.baby_bonus_act.in_effect\": {\n", + " \"2026-01-01.2100-12-31\": True\n", + " }\n", + "}\n", + "\n", + "df_baby = test_scenario(\n", + " create_situation_baby_bonus(),\n", + " \"Baby Bonus: Single parent with newborn, $50k\",\n", + " baby_bonus_reform\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test 2: ECPA (Married, 3 Kids Ages 6, 18, 19, $50k)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "============================================================\n", + "Testing: ECPA: Married, 3 kids (6, 18, 19), $50k\n", + "============================================================\n", + "\n", + "=== NON-ZERO VALUES ===\n", + " Variable Baseline Reform Change\n", + " employment_income 50000.00 50000.00 0.00\n", + " adjusted_gross_income 50000.00 50000.00 0.00\n", + " income_tax_before_refundable_credits 0.00 1780.00 1780.00\n", + " income_tax_non_refundable_credits 1780.00 0.00 -1780.00\n", + " non_refundable_ctc 1780.00 1780.00 0.00\n", + " income_tax_refundable_credits 4755.66 1600.00 -3155.66\n", + " refundable_ctc 1420.00 1420.00 0.00\n", + " eitc 3335.66 3335.66 0.00\n", + " income_tax -4755.66 180.00 4935.66\n", + " snap 3221.75 3221.75 0.00\n", + " free_school_meals 1130.96 1130.96 0.00\n", + " household_benefits 4352.72 15612.72 11260.00\n", + "household_tax_before_refundable_credits 3825.00 5605.00 1780.00\n", + " household_refundable_tax_credits 4755.66 1600.00 -3155.66\n", + " household_net_income 55283.38 61607.72 6324.34\n", + " ecpa_filer_credit 0.00 900.00 900.00\n", + "\n", + "=== VALUES THAT CHANGED ===\n", + " Variable Baseline Reform Change\n", + " income_tax_before_refundable_credits 0.00 1780.00 1780.00\n", + " income_tax_non_refundable_credits 1780.00 0.00 -1780.00\n", + " income_tax_refundable_credits 4755.66 1600.00 -3155.66\n", + " income_tax -4755.66 180.00 4935.66\n", + " household_benefits 4352.72 15612.72 11260.00\n", + "household_tax_before_refundable_credits 3825.00 5605.00 1780.00\n", + " household_refundable_tax_credits 4755.66 1600.00 -3155.66\n", + " household_net_income 55283.38 61607.72 6324.34\n", + " ecpa_filer_credit 0.00 900.00 900.00\n" + ] + } + ], + "source": [ + "ecpa_reform = {\n", + " \"gov.contrib.congress.tlaib.income_security_package.end_child_poverty_act.in_effect\": {\n", + " \"2026-01-01.2100-12-31\": True\n", + " }\n", + "}\n", + "\n", + "df_ecpa = test_scenario(\n", + " create_situation_ecpa(),\n", + " \"ECPA: Married, 3 kids (6, 18, 19), $50k\",\n", + " ecpa_reform\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test 3: BOOST (Single Person, $100k)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "============================================================\n", + "Testing: BOOST: Single person, $100k\n", + "============================================================\n", + "\n", + "=== NON-ZERO VALUES ===\n", + " Variable Baseline Reform Change\n", + " employment_income 100000.00 100000.00 0.00\n", + " adjusted_gross_income 100000.00 100000.00 0.00\n", + " income_tax_before_refundable_credits 13170.00 13170.00 0.00\n", + " income_tax 13170.00 14920.00 1750.00\n", + " household_benefits 0.00 3000.00 3000.00\n", + "household_tax_before_refundable_credits 25810.07 27560.07 1750.00\n", + " household_net_income 74189.94 75439.94 1250.00\n", + " boost_act_payment 0.00 3000.00 3000.00\n", + " boost_act_tax 0.00 1750.00 1750.00\n", + "\n", + "=== VALUES THAT CHANGED ===\n", + " Variable Baseline Reform Change\n", + " income_tax 13170.00 14920.00 1750.00\n", + " household_benefits 0.00 3000.00 3000.00\n", + "household_tax_before_refundable_credits 25810.07 27560.07 1750.00\n", + " household_net_income 74189.94 75439.94 1250.00\n", + " boost_act_payment 0.00 3000.00 3000.00\n", + " boost_act_tax 0.00 1750.00 1750.00\n" + ] + } + ], + "source": [ + "boost_reform = {\n", + " \"gov.contrib.congress.tlaib.income_security_package.boost_act.in_effect\": {\n", + " \"2026-01-01.2100-12-31\": True\n", + " }\n", + "}\n", + "\n", + "df_boost = test_scenario(\n", + " create_situation_boost(),\n", + " \"BOOST: Single person, $100k\",\n", + " boost_reform\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test 4: Combined (Married, 3 Kids including Newborn, $100k)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "============================================================\n", + "Testing: Combined: Married, 3 kids (newborn, 18, 19), $100k\n", + "============================================================\n", + "\n", + "=== NON-ZERO VALUES ===\n", + " Variable Baseline Reform Change\n", + " employment_income 100000.00 100000.00 0.00\n", + " adjusted_gross_income 100000.00 100000.00 0.00\n", + " income_tax_before_refundable_credits 4440.00 7640.00 3200.00\n", + " income_tax_non_refundable_credits 3200.00 0.00 -3200.00\n", + " non_refundable_ctc 3200.00 3200.00 0.00\n", + " income_tax_refundable_credits 0.00 700.00 700.00\n", + " income_tax 4440.00 7940.00 3500.00\n", + " household_benefits 2186.02 24446.02 22260.00\n", + "household_tax_before_refundable_credits 12663.31 16863.31 4200.00\n", + " household_refundable_tax_credits 0.00 700.00 700.00\n", + " household_net_income 89522.70 108282.70 18760.00\n", + " boost_act_payment 0.00 3000.00 3000.00\n", + " boost_act_tax 0.00 1000.00 1000.00\n", + "\n", + "=== VALUES THAT CHANGED ===\n", + " Variable Baseline Reform Change\n", + " income_tax_before_refundable_credits 4440.00 7640.00 3200.00\n", + " income_tax_non_refundable_credits 3200.00 0.00 -3200.00\n", + " income_tax_refundable_credits 0.00 700.00 700.00\n", + " income_tax 4440.00 7940.00 3500.00\n", + " household_benefits 2186.02 24446.02 22260.00\n", + "household_tax_before_refundable_credits 12663.31 16863.31 4200.00\n", + " household_refundable_tax_credits 0.00 700.00 700.00\n", + " household_net_income 89522.70 108282.70 18760.00\n", + " boost_act_payment 0.00 3000.00 3000.00\n", + " boost_act_tax 0.00 1000.00 1000.00\n" + ] + } + ], + "source": [ + "combined_reform = {\n", + " \"gov.contrib.congress.tlaib.income_security_package.baby_bonus_act.in_effect\": {\n", + " \"2026-01-01.2100-12-31\": True\n", + " },\n", + " \"gov.contrib.congress.tlaib.income_security_package.boost_act.in_effect\": {\n", + " \"2026-01-01.2100-12-31\": True\n", + " },\n", + " \"gov.contrib.congress.tlaib.income_security_package.end_child_poverty_act.in_effect\": {\n", + " \"2026-01-01.2100-12-31\": True\n", + " }\n", + "}\n", + "\n", + "df_combined = test_scenario(\n", + " create_situation_combined(),\n", + " \"Combined: Married, 3 kids (newborn, 18, 19), $100k\",\n", + " combined_reform\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Detailed Person-Level Analysis\n", + "\n", + "Let's check person-level values to see if payments are being assigned correctly:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def analyze_person_level(situation, name, reform_params):\n", + " print(f\"\\n{'='*60}\")\n", + " print(f\"Person-level analysis: {name}\")\n", + " print(f\"{'='*60}\")\n", + " \n", + " sim = Simulation(situation=situation, reform=reform_params)\n", + " \n", + " # Get all person IDs\n", + " people = list(situation[\"people\"].keys())\n", + " \n", + " # Standard variables\n", + " standard_vars = [\n", + " \"age\",\n", + " \"employment_income\",\n", + " \"is_tax_unit_dependent\",\n", + " ]\n", + " \n", + " # Reform-specific variables\n", + " reform_vars = [\n", + " \"baby_bonus_act_payment\",\n", + " \"boost_act_payment\",\n", + " \"ecpa_child_benefit\",\n", + " \"ecpa_adult_dependent_credit\",\n", + " ]\n", + " \n", + " results = []\n", + " for person_idx, person in enumerate(people):\n", + " row = {\"Person\": person}\n", + " \n", + " # Calculate standard variables\n", + " for var in standard_vars:\n", + " try:\n", + " vals = sim.calculate(var, \"2026\")\n", + " row[var] = vals[person_idx]\n", + " except Exception as e:\n", + " row[var] = f\"ERROR: {str(e)[:30]}\"\n", + " \n", + " # Calculate reform-specific variables\n", + " for var in reform_vars:\n", + " try:\n", + " vals = sim.calculate(var, \"2026\")\n", + " row[var] = vals[person_idx]\n", + " except Exception as e:\n", + " # Variable doesn't exist in this reform\n", + " row[var] = 0\n", + " \n", + " results.append(row)\n", + " \n", + " df = pd.DataFrame(results)\n", + " print(df.to_string(index=False))\n", + " return df" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "============================================================\n", + "Person-level analysis: Baby Bonus\n", + "============================================================\n", + "Person age employment_income is_tax_unit_dependent baby_bonus_act_payment boost_act_payment ecpa_child_benefit ecpa_adult_dependent_credit\n", + "parent 30.00 50000.00 False 0.00 0.00 0.00 0.00\n", + " baby 0.00 0.00 True 2000.00 0.00 0.00 0.00\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Personageemployment_incomeis_tax_unit_dependentbaby_bonus_act_paymentboost_act_paymentecpa_child_benefitecpa_adult_dependent_credit
0parent30.0050000.00False0.000.000.000.00
1baby0.000.00True2000.000.000.000.00
\n", + "
" + ], + "text/plain": [ + " Person age employment_income is_tax_unit_dependent \\\n", + "0 parent 30.00 50000.00 False \n", + "1 baby 0.00 0.00 True \n", + "\n", + " baby_bonus_act_payment boost_act_payment ecpa_child_benefit \\\n", + "0 0.00 0.00 0.00 \n", + "1 2000.00 0.00 0.00 \n", + "\n", + " ecpa_adult_dependent_credit \n", + "0 0.00 \n", + "1 0.00 " + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Baby Bonus person-level\n", + "analyze_person_level(\n", + " create_situation_baby_bonus(),\n", + " \"Baby Bonus\",\n", + " baby_bonus_reform\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "============================================================\n", + "Person-level analysis: ECPA\n", + "============================================================\n", + "Person age employment_income is_tax_unit_dependent baby_bonus_act_payment boost_act_payment ecpa_child_benefit ecpa_adult_dependent_credit\n", + "adult1 40.00 50000.00 False 0.00 0.00 0.00 0.00\n", + "adult2 38.00 0.00 False 0.00 0.00 0.00 0.00\n", + "child1 6.00 0.00 True 0.00 0.00 5630.00 0.00\n", + "child2 18.00 0.00 True 0.00 0.00 5630.00 0.00\n", + "child3 19.00 0.00 True 0.00 0.00 0.00 700.00\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Personageemployment_incomeis_tax_unit_dependentbaby_bonus_act_paymentboost_act_paymentecpa_child_benefitecpa_adult_dependent_credit
0adult140.0050000.00False0.000.000.000.00
1adult238.000.00False0.000.000.000.00
2child16.000.00True0.000.005630.000.00
3child218.000.00True0.000.005630.000.00
4child319.000.00True0.000.000.00700.00
\n", + "
" + ], + "text/plain": [ + " Person age employment_income is_tax_unit_dependent \\\n", + "0 adult1 40.00 50000.00 False \n", + "1 adult2 38.00 0.00 False \n", + "2 child1 6.00 0.00 True \n", + "3 child2 18.00 0.00 True \n", + "4 child3 19.00 0.00 True \n", + "\n", + " baby_bonus_act_payment boost_act_payment ecpa_child_benefit \\\n", + "0 0.00 0.00 0.00 \n", + "1 0.00 0.00 0.00 \n", + "2 0.00 0.00 5630.00 \n", + "3 0.00 0.00 5630.00 \n", + "4 0.00 0.00 0.00 \n", + "\n", + " ecpa_adult_dependent_credit \n", + "0 0.00 \n", + "1 0.00 \n", + "2 0.00 \n", + "3 0.00 \n", + "4 700.00 " + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# ECPA person-level\n", + "analyze_person_level(\n", + " create_situation_ecpa(),\n", + " \"ECPA\",\n", + " ecpa_reform\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "============================================================\n", + "Person-level analysis: BOOST\n", + "============================================================\n", + "Person age employment_income is_tax_unit_dependent baby_bonus_act_payment boost_act_payment ecpa_child_benefit ecpa_adult_dependent_credit\n", + " adult 35.00 100000.00 False 0.00 3000.00 0.00 0.00\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Personageemployment_incomeis_tax_unit_dependentbaby_bonus_act_paymentboost_act_paymentecpa_child_benefitecpa_adult_dependent_credit
0adult35.00100000.00False0.003000.000.000.00
\n", + "
" + ], + "text/plain": [ + " Person age employment_income is_tax_unit_dependent \\\n", + "0 adult 35.00 100000.00 False \n", + "\n", + " baby_bonus_act_payment boost_act_payment ecpa_child_benefit \\\n", + "0 0.00 3000.00 0.00 \n", + "\n", + " ecpa_adult_dependent_credit \n", + "0 0.00 " + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# BOOST person-level\n", + "analyze_person_level(\n", + " create_situation_boost(),\n", + " \"BOOST\",\n", + " boost_reform\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "============================================================\n", + "Person-level analysis: Combined\n", + "============================================================\n", + "Person age employment_income is_tax_unit_dependent baby_bonus_act_payment boost_act_payment ecpa_child_benefit ecpa_adult_dependent_credit\n", + "adult1 40.00 100000.00 False 0.00 3000.00 0.00 0.00\n", + "adult2 38.00 0.00 False 0.00 3000.00 0.00 0.00\n", + " baby 0.00 0.00 True 2000.00 0.00 5630.00 0.00\n", + "child2 18.00 0.00 True 0.00 0.00 5630.00 0.00\n", + "child3 19.00 0.00 True 0.00 3000.00 0.00 700.00\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Personageemployment_incomeis_tax_unit_dependentbaby_bonus_act_paymentboost_act_paymentecpa_child_benefitecpa_adult_dependent_credit
0adult140.00100000.00False0.003000.000.000.00
1adult238.000.00False0.003000.000.000.00
2baby0.000.00True2000.000.005630.000.00
3child218.000.00True0.000.005630.000.00
4child319.000.00True0.003000.000.00700.00
\n", + "
" + ], + "text/plain": [ + " Person age employment_income is_tax_unit_dependent \\\n", + "0 adult1 40.00 100000.00 False \n", + "1 adult2 38.00 0.00 False \n", + "2 baby 0.00 0.00 True \n", + "3 child2 18.00 0.00 True \n", + "4 child3 19.00 0.00 True \n", + "\n", + " baby_bonus_act_payment boost_act_payment ecpa_child_benefit \\\n", + "0 0.00 3000.00 0.00 \n", + "1 0.00 3000.00 0.00 \n", + "2 2000.00 0.00 5630.00 \n", + "3 0.00 0.00 5630.00 \n", + "4 0.00 3000.00 0.00 \n", + "\n", + " ecpa_adult_dependent_credit \n", + "0 0.00 \n", + "1 0.00 \n", + "2 0.00 \n", + "3 0.00 \n", + "4 700.00 " + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Combined person-level\n", + "analyze_person_level(\n", + " create_situation_combined(),\n", + " \"Combined\",\n", + " combined_reform\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Summary Comparison\n", + "\n", + "Net income impact for each scenario:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "============================================================\n", + "SUMMARY: Net Income Impact\n", + "============================================================\n", + " Scenario Baseline Net Income Reform Net Income Change\n", + "Baby Bonus 48059.65 50059.65 2000.00\n", + " ECPA 55283.38 61607.72 6324.34\n", + " BOOST 74189.94 75439.94 1250.00\n", + " Combined 89522.70 108282.70 18760.00\n" + ] + } + ], + "source": [ + "summary_data = []\n", + "\n", + "for df, name in [\n", + " (df_baby, \"Baby Bonus\"),\n", + " (df_ecpa, \"ECPA\"),\n", + " (df_boost, \"BOOST\"),\n", + " (df_combined, \"Combined\")\n", + "]:\n", + " net_income_row = df[df[\"Variable\"] == \"household_net_income\"]\n", + " if not net_income_row.empty:\n", + " summary_data.append({\n", + " \"Scenario\": name,\n", + " \"Baseline Net Income\": net_income_row[\"Baseline\"].values[0],\n", + " \"Reform Net Income\": net_income_row[\"Reform\"].values[0],\n", + " \"Change\": net_income_row[\"Change\"].values[0]\n", + " })\n", + "\n", + "summary_df = pd.DataFrame(summary_data)\n", + "print(\"\\n\" + \"=\"*60)\n", + "print(\"SUMMARY: Net Income Impact\")\n", + "print(\"=\"*60)\n", + "print(summary_df.to_string(index=False))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pe", + "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.12.10" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/us/test_ctc_adult_dependent_removal.ipynb b/us/test_ctc_adult_dependent_removal.ipynb new file mode 100644 index 0000000..6052445 --- /dev/null +++ b/us/test_ctc_adult_dependent_removal.ipynb @@ -0,0 +1,376 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Test: CTC Adult Dependent Credit Removal\n", + "\n", + "This notebook verifies that the current CTC adult dependent credit is properly removed when ECPA is active." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "c:\\Users\\dtsax\\envs\\pe\\Lib\\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" + ] + } + ], + "source": [ + "import pandas as pd\n", + "from policyengine_us import Simulation\n", + "\n", + "pd.set_option('display.float_format', '{:.2f}'.format)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test Scenario: Household with Adult Dependent\n", + "\n", + "Single filer, 1 adult dependent (age 21), $150,000 income\n", + "\n", + "Under current law, should get CTC adult dependent credit ($500 in 2026)." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "situation = {\n", + " \"people\": {\n", + " \"adult1\": {\n", + " \"age\": {\"2026\": 45},\n", + " \"employment_income\": {\"2026\": 150_000},\n", + " },\n", + " \"adult_dependent\": {\n", + " \"age\": {\"2026\": 21},\n", + " \"is_tax_unit_dependent\": {\"2026\": True},\n", + " },\n", + " },\n", + " \"families\": {\"family\": {\"members\": [\"adult1\", \"adult_dependent\"]}},\n", + " \"marital_units\": {\"marital_unit\": {\"members\": [\"adult1\"]}},\n", + " \"tax_units\": {\"tax_unit\": {\"members\": [\"adult1\", \"adult_dependent\"]}},\n", + " \"spm_units\": {\"spm_unit\": {\"members\": [\"adult1\", \"adult_dependent\"]}},\n", + " \"households\": {\"household\": {\"members\": [\"adult1\", \"adult_dependent\"]}},\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Debug: Check Dependency Status\n", + "\n", + "Let's verify the dependency relationship is being calculated correctly:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "============================================================\n", + "PERSON-LEVEL DEPENDENCY DEBUG\n", + "============================================================\n", + "\n", + "adult1:\n", + " age : 45.0\n", + " is_child : False\n", + " is_tax_unit_head : True\n", + " is_tax_unit_spouse : False\n", + " is_tax_unit_dependent : False\n", + " ctc_child_individual_maximum : 0.0\n", + " ctc_adult_individual_maximum : 0.0\n", + "\n", + "adult_dependent:\n", + " age : 21.0\n", + " is_child : False\n", + " is_tax_unit_head : False\n", + " is_tax_unit_spouse : True\n", + " is_tax_unit_dependent : True\n", + " ctc_child_individual_maximum : 0.0\n", + " ctc_adult_individual_maximum : 500.0\n" + ] + } + ], + "source": [ + "# Create simulation for debugging\n", + "debug_sim = Simulation(situation=situation)\n", + "\n", + "# Check dependency variables for each person\n", + "people = [\"adult1\", \"adult_dependent\"]\n", + "debug_vars = [\n", + " \"age\",\n", + " \"is_child\",\n", + " \"is_tax_unit_head\",\n", + " \"is_tax_unit_spouse\",\n", + " \"is_tax_unit_dependent\",\n", + " \"ctc_child_individual_maximum\",\n", + " \"ctc_adult_individual_maximum\",\n", + "]\n", + "\n", + "print(\"=\" * 60)\n", + "print(\"PERSON-LEVEL DEPENDENCY DEBUG\")\n", + "print(\"=\" * 60)\n", + "\n", + "for person_idx, person in enumerate(people):\n", + " print(f\"\\n{person}:\")\n", + " for var in debug_vars:\n", + " try:\n", + " vals = debug_sim.calculate(var, \"2026\")\n", + " val = vals[person_idx]\n", + " print(f\" {var:35s}: {val}\")\n", + " except Exception as e:\n", + " print(f\" {var:35s}: ERROR - {str(e)[:40]}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Baseline: No ECPA (Current Law)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "============================================================\n", + "BASELINE (Current Law)\n", + "============================================================\n", + "ctc_adult_individual_maximum : $0.00\n", + "ctc_child_individual_maximum : $0.00\n", + "ctc : $500.00\n", + "non_refundable_ctc : $500.00\n", + "refundable_ctc : $0.00\n", + "income_tax_non_refundable_credits : $500.00\n", + "income_tax_refundable_credits : $0.00\n", + "income_tax : $14,840.00\n" + ] + } + ], + "source": [ + "baseline = Simulation(situation=situation)\n", + "\n", + "print(\"=\" * 60)\n", + "print(\"BASELINE (Current Law)\")\n", + "print(\"=\" * 60)\n", + "\n", + "variables = [\n", + " \"ctc_adult_individual_maximum\",\n", + " \"ctc_child_individual_maximum\",\n", + " \"ctc\",\n", + " \"non_refundable_ctc\",\n", + " \"refundable_ctc\",\n", + " \"income_tax_non_refundable_credits\",\n", + " \"income_tax_refundable_credits\",\n", + " \"income_tax\",\n", + "]\n", + "\n", + "for var in variables:\n", + " try:\n", + " val = baseline.calculate(var, \"2026\")[0]\n", + " print(f\"{var:40s}: ${val:,.2f}\")\n", + " except Exception as e:\n", + " print(f\"{var:40s}: ERROR - {e}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## With ECPA Active" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "============================================================\n", + "WITH ECPA ACTIVE\n", + "============================================================\n", + "ctc_adult_individual_maximum : $0.00\n", + "ctc_child_individual_maximum : $0.00\n", + "ctc : $500.00\n", + "non_refundable_ctc : $500.00\n", + "refundable_ctc : $0.00\n", + "income_tax_non_refundable_credits : $0.00\n", + "income_tax_refundable_credits : $700.00\n", + "income_tax : $14,640.00\n" + ] + } + ], + "source": [ + "ecpa_reform = {\n", + " \"gov.contrib.congress.tlaib.income_security_package.end_child_poverty_act.in_effect\": {\n", + " \"2026-01-01.2100-12-31\": True\n", + " }\n", + "}\n", + "\n", + "reformed = Simulation(situation=situation, reform=ecpa_reform)\n", + "\n", + "print(\"\\n\" + \"=\" * 60)\n", + "print(\"WITH ECPA ACTIVE\")\n", + "print(\"=\" * 60)\n", + "\n", + "for var in variables:\n", + " try:\n", + " val = reformed.calculate(var, \"2026\")[0]\n", + " print(f\"{var:40s}: ${val:,.2f}\")\n", + " except Exception as e:\n", + " print(f\"{var:40s}: ERROR - {e}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Comparison" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "============================================================\n", + "COMPARISON\n", + "============================================================\n", + " Variable Baseline ECPA Change\n", + " ctc_adult_individual_maximum 0.00 0.00 0.00\n", + " ctc_child_individual_maximum 0.00 0.00 0.00\n", + " ctc 500.00 500.00 0.00\n", + " non_refundable_ctc 500.00 500.00 0.00\n", + " refundable_ctc 0.00 0.00 0.00\n", + "income_tax_non_refundable_credits 500.00 0.00 -500.00\n", + " income_tax_refundable_credits 0.00 700.00 700.00\n", + " income_tax 14840.00 14640.00 -200.00\n", + "\n", + "============================================================\n", + "KEY FINDINGS\n", + "============================================================\n", + "\n", + "Baseline CTC Adult Dependent Credit: $0.00\n", + "ECPA CTC Adult Dependent Credit: $0.00\n", + "\n", + "⚠ WARNING: No adult dependent in baseline (check test setup)\n", + "\n", + "ECPA Adult Dependent Credit: $0.00\n", + "⚠ ECPA adult dependent credit is $0 (check age boundaries)\n" + ] + } + ], + "source": [ + "print(\"\\n\" + \"=\" * 60)\n", + "print(\"COMPARISON\")\n", + "print(\"=\" * 60)\n", + "\n", + "results = []\n", + "for var in variables:\n", + " try:\n", + " base_val = baseline.calculate(var, \"2026\")[0]\n", + " reform_val = reformed.calculate(var, \"2026\")[0]\n", + " diff = reform_val - base_val\n", + " \n", + " results.append({\n", + " \"Variable\": var,\n", + " \"Baseline\": base_val,\n", + " \"ECPA\": reform_val,\n", + " \"Change\": diff\n", + " })\n", + " except Exception as e:\n", + " results.append({\n", + " \"Variable\": var,\n", + " \"Baseline\": \"ERROR\",\n", + " \"ECPA\": \"ERROR\",\n", + " \"Change\": str(e)[:40]\n", + " })\n", + "\n", + "df = pd.DataFrame(results)\n", + "print(df.to_string(index=False))\n", + "\n", + "print(\"\\n\" + \"=\" * 60)\n", + "print(\"KEY FINDINGS\")\n", + "print(\"=\" * 60)\n", + "\n", + "# Check if adult dependent credit is removed\n", + "base_adult_ctc = baseline.calculate(\"ctc_adult_individual_maximum\", \"2026\")[0]\n", + "reform_adult_ctc = reformed.calculate(\"ctc_adult_individual_maximum\", \"2026\")[0]\n", + "\n", + "print(f\"\\nBaseline CTC Adult Dependent Credit: ${base_adult_ctc:,.2f}\")\n", + "print(f\"ECPA CTC Adult Dependent Credit: ${reform_adult_ctc:,.2f}\")\n", + "\n", + "if base_adult_ctc > 0 and reform_adult_ctc == 0:\n", + " print(\"\\n✓ SUCCESS: CTC adult dependent credit properly removed under ECPA\")\n", + "elif base_adult_ctc > 0 and reform_adult_ctc > 0:\n", + " print(\"\\n✗ FAILURE: CTC adult dependent credit still present under ECPA\")\n", + " print(\" This means the old $500 credit is being added on top of new ECPA credits!\")\n", + "elif base_adult_ctc == 0:\n", + " print(\"\\n⚠ WARNING: No adult dependent in baseline (check test setup)\")\n", + "\n", + "# Check if ECPA adult dependent credit is present\n", + "try:\n", + " ecpa_adult_dep = reformed.calculate(\"ecpa_adult_dependent_credit\", \"2026\")[0]\n", + " print(f\"\\nECPA Adult Dependent Credit: ${ecpa_adult_dep:,.2f}\")\n", + " if ecpa_adult_dep == 700:\n", + " print(\"✓ ECPA adult dependent credit correctly calculated ($700)\")\n", + " elif ecpa_adult_dep == 0:\n", + " print(\"⚠ ECPA adult dependent credit is $0 (check age boundaries)\")\n", + "except Exception as e:\n", + " print(f\"\\n✗ ERROR: Could not calculate ecpa_adult_dependent_credit: {e}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pe", + "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.12.10" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}