policyengine-uk 2.40.1__py3-none-any.whl → 2.65.6__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.
- policyengine_uk/__init__.py +5 -3
- policyengine_uk/data/__init__.py +1 -0
- policyengine_uk/data/dataset_schema.py +70 -18
- policyengine_uk/data/economic_assumptions.py +36 -10
- policyengine_uk/data/filter_dataset.py +52 -0
- policyengine_uk/dynamics/labour_supply.py +343 -0
- policyengine_uk/dynamics/participation.py +629 -0
- policyengine_uk/dynamics/progression.py +384 -0
- policyengine_uk/microsimulation.py +105 -0
- policyengine_uk/model_api.py +1 -0
- policyengine_uk/parameters/gov/boe/base_rate.yaml +34 -0
- policyengine_uk/parameters/gov/boe/index.yaml +2 -0
- policyengine_uk/parameters/gov/contrib/behavioral_responses/employee_salary_sacrifice_reduction_rate.yaml +14 -0
- policyengine_uk/parameters/gov/contrib/behavioral_responses/salary_sacrifice_broad_base_haircut_rate.yaml +22 -0
- policyengine_uk/parameters/gov/contrib/cec/state_pension_increase.yaml +1 -1
- policyengine_uk/parameters/gov/contrib/ubi_center/carbon_tax.yaml +2 -2
- policyengine_uk/parameters/gov/contrib/ubi_center/land_value_tax.yaml +3 -3
- policyengine_uk/parameters/gov/dcms/bbc/tv_licence/colour.yaml +5 -5
- policyengine_uk/parameters/gov/dfe/education_spending.yaml +1 -1
- policyengine_uk/parameters/gov/dft/rail/fare_index.yaml +32 -0
- policyengine_uk/parameters/gov/dft/rail/prior_law_fare_index.yaml +32 -0
- policyengine_uk/parameters/gov/dft/rail/ridership_index.yaml +30 -0
- policyengine_uk/parameters/gov/dft/spending.yaml +2 -2
- policyengine_uk/parameters/gov/dwp/ESA/income/earn_disregard.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/ESA/income/income_disregard_couple.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/ESA/income/income_disregard_lone_parent.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/ESA/income/income_disregard_single.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/ESA/income/pension_disregard.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/IIDB/maximum.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/JSA/contrib/amount_over_25.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/JSA/contrib/earn_disregard.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/JSA/contrib/pension_disregard.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/JSA/income/amount_18_24.yaml +7 -7
- policyengine_uk/parameters/gov/dwp/JSA/income/amount_over_25.yaml +7 -7
- policyengine_uk/parameters/gov/dwp/JSA/income/income_disregard_couple.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/JSA/income/income_disregard_lone_parent.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/JSA/income/income_disregard_single.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/LHA/shared_accommodation_age_threshold.yaml +12 -0
- policyengine_uk/parameters/gov/dwp/attendance_allowance/higher.yaml +7 -7
- policyengine_uk/parameters/gov/dwp/attendance_allowance/lower.yaml +7 -7
- policyengine_uk/parameters/gov/dwp/benefit_cap.yaml +3 -3
- policyengine_uk/parameters/gov/dwp/carer_premium/couple.yaml +2 -2
- policyengine_uk/parameters/gov/dwp/carer_premium/single.yaml +6 -6
- policyengine_uk/parameters/gov/dwp/carers_allowance/rate.yaml +7 -7
- policyengine_uk/parameters/gov/dwp/disability_premia/disability_couple.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/disability_premia/enhanced_couple.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/disability_premia/enhanced_single.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/disability_premia/severe_couple.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/dla/mobility/higher.yaml +4 -4
- policyengine_uk/parameters/gov/dwp/dla/mobility/lower.yaml +8 -8
- policyengine_uk/parameters/gov/dwp/dla/self_care/higher.yaml +7 -7
- policyengine_uk/parameters/gov/dwp/dla/self_care/lower.yaml +8 -8
- policyengine_uk/parameters/gov/dwp/dla/self_care/middle.yaml +7 -7
- policyengine_uk/parameters/gov/dwp/housing_benefit/allowances/lone_parent/aged.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/housing_benefit/allowances/lone_parent/older.yaml +3 -3
- policyengine_uk/parameters/gov/dwp/housing_benefit/allowances/single/aged.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/housing_benefit/allowances/single/older.yaml +3 -3
- policyengine_uk/parameters/gov/dwp/housing_benefit/means_test/income_disregard/worker.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/housing_benefit/non_dep_deduction/amount.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/housing_benefit/takeup.yaml +7 -7
- policyengine_uk/parameters/gov/dwp/income_support/amounts/amount_16_24.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/income_support/amounts/amount_couples_over_18.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/income_support/means_test/earn_disregard.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/income_support/means_test/income_disregard_couple.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/income_support/means_test/income_disregard_lone_parent.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/income_support/means_test/income_disregard_single.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/income_support/means_test/pension_disregard.yaml +1 -1
- policyengine_uk/parameters/gov/dwp/income_support/takeup.yaml +7 -7
- policyengine_uk/parameters/gov/dwp/pension_credit/guarantee_credit/carer/addition.yaml +4 -4
- policyengine_uk/parameters/gov/dwp/pension_credit/guarantee_credit/minimum_guarantee.yaml +9 -9
- policyengine_uk/parameters/gov/dwp/pension_credit/guarantee_credit/severe_disability/addition.yaml +3 -3
- policyengine_uk/parameters/gov/dwp/pension_credit/savings_credit/threshold.yaml +5 -5
- policyengine_uk/parameters/gov/dwp/pip/daily_living/enhanced.yaml +7 -7
- policyengine_uk/parameters/gov/dwp/pip/daily_living/standard.yaml +8 -8
- policyengine_uk/parameters/gov/dwp/pip/mobility/enhanced.yaml +4 -4
- policyengine_uk/parameters/gov/dwp/pip/mobility/standard.yaml +9 -9
- policyengine_uk/parameters/gov/dwp/sda/maximum.yaml +7 -7
- policyengine_uk/parameters/gov/dwp/state_pension/basic_state_pension/amount.yaml +11 -11
- policyengine_uk/parameters/gov/dwp/state_pension/new_state_pension/amount.yaml +4 -4
- policyengine_uk/parameters/gov/dwp/tax_credits/child_tax_credit/limit/child_count.yaml +10 -1
- policyengine_uk/parameters/gov/dwp/tax_credits/child_tax_credit/takeup.yaml +7 -7
- policyengine_uk/parameters/gov/dwp/tax_credits/working_tax_credit/takeup.yaml +7 -7
- policyengine_uk/parameters/gov/dwp/universal_credit/elements/carer/amount.yaml +3 -3
- policyengine_uk/parameters/gov/dwp/universal_credit/elements/child/amount.yaml +2 -4
- policyengine_uk/parameters/gov/dwp/universal_credit/elements/child/disabled/amount.yaml +2 -4
- policyengine_uk/parameters/gov/dwp/universal_credit/elements/child/first/higher_amount.yaml +6 -8
- policyengine_uk/parameters/gov/dwp/universal_credit/elements/child/limit/child_count.yaml +6 -1
- policyengine_uk/parameters/gov/dwp/universal_credit/elements/child/severely_disabled/amount.yaml +3 -5
- policyengine_uk/parameters/gov/dwp/universal_credit/elements/childcare/cap.yaml +2 -6
- policyengine_uk/parameters/gov/dwp/universal_credit/elements/disabled/amount.yaml +4 -6
- policyengine_uk/parameters/gov/dwp/universal_credit/elements/housing/non_dep_deduction/amount.yaml +4 -1
- policyengine_uk/parameters/gov/dwp/universal_credit/rebalancing/active.yaml +9 -0
- policyengine_uk/parameters/gov/dwp/universal_credit/rebalancing/new_claimant_health_element.yaml +9 -0
- policyengine_uk/parameters/gov/dwp/universal_credit/rebalancing/standard_allowance_uplift.yaml +13 -0
- policyengine_uk/parameters/gov/dwp/universal_credit/standard_allowance/amount.yaml +5 -5
- policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/taxable_income_test/maximum_taxable_income.yaml +2 -1
- policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/taxable_income_test/use_maximum_taxable_income.yaml +1 -0
- policyengine_uk/parameters/gov/dynamic/obr_labour_supply_assumptions.yaml +9 -0
- policyengine_uk/parameters/gov/economic_assumptions/create_economic_assumption_indices.py +1 -1
- policyengine_uk/parameters/gov/economic_assumptions/yoy_growth.yaml +522 -153
- policyengine_uk/parameters/gov/hmrc/cgt/additional_rate.yaml +5 -0
- policyengine_uk/parameters/gov/hmrc/cgt/basic_rate.yaml +5 -0
- policyengine_uk/parameters/gov/hmrc/cgt/higher_rate.yaml +4 -0
- policyengine_uk/parameters/gov/hmrc/child_benefit/amount/additional.yaml +6 -6
- policyengine_uk/parameters/gov/hmrc/child_benefit/amount/eldest.yaml +8 -8
- policyengine_uk/parameters/gov/hmrc/child_benefit/takeup/by_age.yaml +1 -1
- policyengine_uk/parameters/gov/hmrc/fuel_duty/calculate_fuel_duty_rates.py +464 -0
- policyengine_uk/parameters/gov/hmrc/fuel_duty/petrol_and_diesel.yaml +86 -10
- policyengine_uk/parameters/gov/hmrc/income_tax/allowances/personal_allowance/amount.yaml +6 -0
- policyengine_uk/parameters/gov/hmrc/income_tax/earned_taxable_income_exclusions.yaml +2 -1
- policyengine_uk/parameters/gov/hmrc/income_tax/income_tax_additions.yaml +1 -0
- policyengine_uk/parameters/gov/hmrc/income_tax/rates/dividends.yaml +12 -0
- policyengine_uk/parameters/gov/hmrc/income_tax/rates/property.yaml +46 -0
- policyengine_uk/parameters/gov/hmrc/income_tax/rates/savings.yaml +46 -0
- policyengine_uk/parameters/gov/hmrc/income_tax/rates/scotland/rates.yaml +2 -2
- policyengine_uk/parameters/gov/hmrc/income_tax/rates/uk.yaml +14 -2
- policyengine_uk/parameters/gov/hmrc/national_insurance/class_1/rates/employee/additional.yaml +4 -6
- policyengine_uk/parameters/gov/hmrc/national_insurance/class_1/rates/employer.yaml +3 -3
- policyengine_uk/parameters/gov/hmrc/national_insurance/class_1/thresholds/secondary_threshold.yaml +14 -4
- policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/flat_rate.yaml +2 -2
- policyengine_uk/parameters/gov/hmrc/national_insurance/salary_sacrifice_pension_cap.yaml +16 -0
- policyengine_uk/parameters/gov/hmrc/student_loans/interest_rates/index.yaml +12 -0
- policyengine_uk/parameters/gov/hmrc/student_loans/interest_rates/plan_1/boe_margin.yaml +11 -0
- policyengine_uk/parameters/gov/hmrc/student_loans/interest_rates/plan_2/additional_rate.yaml +27 -0
- policyengine_uk/parameters/gov/hmrc/student_loans/interest_rates/plan_2/index.yaml +16 -0
- policyengine_uk/parameters/gov/hmrc/student_loans/interest_rates/plan_2/upper_threshold.yaml +48 -0
- policyengine_uk/parameters/gov/hmrc/student_loans/interest_rates/postgraduate_additional_rate.yaml +11 -0
- policyengine_uk/parameters/gov/hmrc/student_loans/postgraduate_repayment_rate.yaml +9 -0
- policyengine_uk/parameters/gov/hmrc/student_loans/repayment_rate.yaml +9 -0
- policyengine_uk/parameters/gov/hmrc/student_loans/thresholds/plan_1.yaml +25 -0
- policyengine_uk/parameters/gov/hmrc/student_loans/thresholds/plan_2.yaml +58 -0
- policyengine_uk/parameters/gov/hmrc/student_loans/thresholds/plan_4.yaml +19 -0
- policyengine_uk/parameters/gov/hmrc/student_loans/thresholds/plan_5.yaml +16 -0
- policyengine_uk/parameters/gov/hmrc/student_loans/thresholds/postgraduate.yaml +21 -0
- policyengine_uk/parameters/gov/hmrc/vat/reduced_rate_share.yaml +3 -3
- policyengine_uk/parameters/gov/indices/private_rent_index.yaml +9 -9
- policyengine_uk/parameters/gov/revenue_scotland/lbtt/non_residential.yaml +2 -2
- policyengine_uk/parameters/gov/revenue_scotland/lbtt/rent.yaml +2 -2
- policyengine_uk/parameters/gov/revenue_scotland/lbtt/residential/first_time_buyer_rate.yaml +2 -2
- policyengine_uk/parameters/gov/revenue_scotland/lbtt/residential/rate.yaml +2 -2
- policyengine_uk/parameters/gov/wra/land_transaction_tax/non_residential.yaml +2 -2
- policyengine_uk/parameters/gov/wra/land_transaction_tax/rent.yaml +2 -2
- policyengine_uk/parameters/gov/wra/land_transaction_tax/residential/higher_rate.yaml +1 -1
- policyengine_uk/parameters/gov/wra/land_transaction_tax/residential/primary.yaml +2 -2
- policyengine_uk/parameters/household/consumption/carbon/consumption.yaml +8 -6
- policyengine_uk/parameters/household/consumption/carbon/intensity.yaml +4 -1
- policyengine_uk/parameters/household/consumption/carbon/production.yaml +12 -7
- policyengine_uk/parameters/household/consumption/carbon/production_by_source.yaml +76 -41
- policyengine_uk/parameters/household/consumption/fuel/prices/petrol.yaml +1 -1
- policyengine_uk/parameters/household/poverty/absolute_poverty_threshold_bhc.yaml +1 -1
- policyengine_uk/reforms/policyengine/adjust_budgets.py +0 -1
- policyengine_uk/scenarios/__init__.py +4 -0
- policyengine_uk/scenarios/pip_reform.py +23 -0
- policyengine_uk/scenarios/reindex_benefit_cap.py +32 -0
- policyengine_uk/scenarios/repeal_two_child_limit.py +10 -0
- policyengine_uk/scenarios/uc_reform.py +50 -0
- policyengine_uk/simulation.py +619 -0
- policyengine_uk/system.py +3 -257
- policyengine_uk/tax_benefit_system.py +141 -0
- policyengine_uk/tests/behavioral_responses/test_labor_supply_responses.yaml +183 -0
- policyengine_uk/tests/microsimulation/reforms_config.yaml +8 -8
- policyengine_uk/tests/microsimulation/test_reform_impacts.py +2 -2
- policyengine_uk/tests/microsimulation/test_salary_sacrifice_cap_reform.py +401 -0
- policyengine_uk/tests/microsimulation/test_validity.py +2 -3
- policyengine_uk/tests/microsimulation/update_reform_impacts.py +104 -40
- policyengine_uk/tests/policy/baseline/contrib/policyengine/employer_ni/employer_ni_fixed_employer_cost_change.yaml +105 -0
- policyengine_uk/tests/policy/baseline/finance/benefit/family/child_benefit.yaml +2 -0
- policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support.yaml +0 -23
- policyengine_uk/tests/policy/baseline/gov/dcms/bbc/tv-licence/tv_licence.yaml +3 -0
- policyengine_uk/tests/policy/baseline/gov/dfe/extended_childcare_entitlement/extended_childcare_entitlement.yaml +17 -17
- policyengine_uk/tests/policy/baseline/gov/dwp/basic_state_pension.yaml +44 -0
- policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/allowances/gift_aid.yaml +71 -0
- policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/allowances/personal_allowance.yaml +161 -0
- policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/salary_sacrifice_pension_ni_employee.yaml +107 -0
- policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/salary_sacrifice_pension_ni_employer.yaml +95 -0
- policyengine_uk/tests/policy/baseline/gov/hmrc/student_loans/student_loan_interest_rate.yaml +153 -0
- policyengine_uk/tests/policy/baseline/gov/hmrc/student_loans/student_loan_repayment.yaml +130 -0
- policyengine_uk/tests/policy/baseline/household/wealth/vehicle.yaml +27 -0
- policyengine_uk/tests/policy/reforms/nov_2025_budget/income_source_tax_rates.yaml +235 -0
- policyengine_uk/tests/policy/reforms/nov_2025_budget/income_tax_freeze.yaml +83 -0
- policyengine_uk/tests/policy/reforms/parametric/basic_income/basic_income.yaml +1 -0
- policyengine_uk/tests/test_behavioral_responses.py +215 -0
- policyengine_uk/tests/test_fiscal_year_parameters.py +131 -0
- policyengine_uk/utils/__init__.py +1 -0
- policyengine_uk/utils/compare.py +28 -0
- policyengine_uk/utils/create_ahc_deflator.py +169 -0
- policyengine_uk/utils/create_triple_lock.py +1 -1
- policyengine_uk/utils/dependencies.py +259 -0
- policyengine_uk/utils/parameters.py +12 -1
- policyengine_uk/utils/scenario.py +225 -0
- policyengine_uk/utils/solve_private_school_attendance_factor.py +4 -6
- policyengine_uk/variables/contrib/policyengine/education_budget_change.py +0 -1
- policyengine_uk/variables/contrib/policyengine/employer_ni/baseline_employer_cost.py +5 -1
- policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_fixed_employer_cost_change.py +23 -23
- policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_response_capital_incidence.py +1 -1
- policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_response_consumer_incidence.py +1 -1
- policyengine_uk/variables/contrib/policyengine/other_public_spending_budget_change.py +0 -1
- policyengine_uk/variables/gov/dfe/targeted_childcare_entitlement/targeted_childcare_entitlement_eligible.py +0 -1
- policyengine_uk/variables/gov/dft/rail_subsidy_spending.py +16 -1
- policyengine_uk/variables/gov/dft/rail_usage.py +16 -0
- policyengine_uk/variables/gov/dwp/BRMA_LHA_rate.py +7 -2
- policyengine_uk/variables/gov/dwp/LHA_category.py +4 -2
- policyengine_uk/variables/gov/dwp/additional_state_pension.py +4 -2
- policyengine_uk/variables/gov/dwp/basic_state_pension.py +26 -8
- policyengine_uk/variables/gov/dwp/is_CTC_eligible.py +1 -1
- policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt.py +9 -13
- policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_earnings.py +66 -0
- policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_health_disability.py +75 -0
- policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_other.py +66 -0
- policyengine_uk/variables/gov/dwp/winter_fuel_allowance.py +5 -4
- policyengine_uk/variables/gov/gov_tax.py +0 -2
- policyengine_uk/variables/gov/hmrc/household_tax.py +0 -1
- policyengine_uk/variables/gov/hmrc/income_tax/allowances/gift_aid.py +23 -0
- policyengine_uk/variables/gov/hmrc/income_tax/allowances/personal_allowance.py +9 -2
- policyengine_uk/variables/gov/hmrc/income_tax/income_tax_pre_charges.py +1 -0
- policyengine_uk/variables/gov/hmrc/income_tax/liability/property_income_tax.py +75 -0
- policyengine_uk/variables/gov/hmrc/income_tax/liability/savings_income_tax.py +4 -4
- policyengine_uk/variables/gov/hmrc/national_insurance/salary_sacrifice_broad_base_haircut.py +43 -0
- policyengine_uk/variables/gov/hmrc/national_insurance/salary_sacrifice_pension_ni_employee.py +38 -0
- policyengine_uk/variables/gov/hmrc/national_insurance/salary_sacrifice_pension_ni_employer.py +27 -0
- policyengine_uk/variables/gov/hmrc/pensions/pension_contributions_via_salary_sacrifice_adjusted.py +31 -0
- policyengine_uk/variables/gov/hmrc/pensions/salary_sacrifice_returned_to_income.py +41 -0
- policyengine_uk/variables/gov/hmrc/student_loans/__init__.py +2 -0
- policyengine_uk/variables/gov/hmrc/student_loans/plan_1_interest_rate.py +23 -0
- policyengine_uk/variables/gov/hmrc/student_loans/plan_2_interest_rate.py +31 -0
- policyengine_uk/variables/gov/hmrc/student_loans/plan_4_interest_rate.py +22 -0
- policyengine_uk/variables/gov/hmrc/student_loans/plan_5_interest_rate.py +18 -0
- policyengine_uk/variables/gov/hmrc/student_loans/postgraduate_interest_rate.py +23 -0
- policyengine_uk/variables/gov/hmrc/student_loans/student_loan_plan.py +27 -0
- policyengine_uk/variables/gov/hmrc/student_loans/student_loan_repayment.py +91 -0
- policyengine_uk/variables/gov/hmrc/student_loans/student_loan_repayment_rate.py +31 -0
- policyengine_uk/variables/gov/hmrc/would_claim_child_benefit.py +5 -1
- policyengine_uk/variables/household/demographic/benunit/benunit_count_adults.py +11 -0
- policyengine_uk/variables/household/demographic/is_disabled_for_benefits.py +13 -1
- policyengine_uk/variables/household/income/hbai_household_net_income.py +29 -1
- policyengine_uk/variables/household/income/hbai_household_net_income_ahc.py +13 -0
- policyengine_uk/variables/household/income/household_net_income.py +5 -1
- policyengine_uk/variables/household/income/inflation_adjustment.py +24 -0
- policyengine_uk/variables/household/post_tax_income.py +12 -0
- policyengine_uk/variables/household/wealth/num_vehicles.py +9 -0
- policyengine_uk/variables/household/wealth/owns_vehicle.py +17 -0
- policyengine_uk/variables/input/consumption/property/council_tax.py +0 -35
- policyengine_uk/variables/input/consumption/property/employee_pension_contributions.py +8 -1
- policyengine_uk/variables/input/consumption/property/employee_pension_contributions_reported.py +16 -0
- policyengine_uk/variables/input/consumption/property/pension_contributions_via_salary_sacrifice.py +16 -0
- policyengine_uk/variables/input/employment_income.py +2 -0
- policyengine_uk/variables/input/rent.py +0 -40
- policyengine_uk/variables/input/savings_interest_income.py +3 -1
- {policyengine_uk-2.40.1.dist-info → policyengine_uk-2.65.6.dist-info}/METADATA +17 -8
- {policyengine_uk-2.40.1.dist-info → policyengine_uk-2.65.6.dist-info}/RECORD +252 -173
- {policyengine_uk-2.40.1.dist-info → policyengine_uk-2.65.6.dist-info}/WHEEL +1 -1
- policyengine_uk/repo.py +0 -3
- policyengine_uk/tests/policy/baseline/gov/abolitions/abolition_parameters.yaml +0 -250
- policyengine_uk/variables/contrib/policyengine/high_income_incident_tax_change.py +0 -22
- policyengine_uk-2.40.1.data/data/share/openfisca/openfisca-country-template/CHANGELOG.md +0 -2285
- policyengine_uk-2.40.1.data/data/share/openfisca/openfisca-country-template/README.md +0 -37
- policyengine_uk-2.40.1.dist-info/licenses/LICENSE +0 -661
- {policyengine_uk-2.40.1.data/data/share/openfisca/openfisca-country-template → policyengine_uk-2.65.6.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
"""Labour supply progression (intensive margin) dynamics module.
|
|
2
|
+
|
|
3
|
+
This module handles the intensive margin of labour supply - how people adjust their
|
|
4
|
+
working hours in response to policy changes. It implements the elasticity-based
|
|
5
|
+
methodology from the OBR's labour supply framework.
|
|
6
|
+
|
|
7
|
+
Reference: https://obr.uk/docs/dlm_uploads/NICS-Cut-Impact-on-Labour-Supply-Note.pdf
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
import pandas as pd
|
|
12
|
+
from policyengine_uk import Simulation
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def calculate_derivative(
|
|
16
|
+
sim: Simulation,
|
|
17
|
+
target_variable: str = "hbai_household_net_income",
|
|
18
|
+
input_variable: str = "employment_income",
|
|
19
|
+
year: int = 2025,
|
|
20
|
+
count_adults: int = 2,
|
|
21
|
+
delta: float = 1_000,
|
|
22
|
+
) -> np.ndarray:
|
|
23
|
+
"""Calculate the marginal rate of change of target variable with respect to input variable.
|
|
24
|
+
|
|
25
|
+
This function computes numerical derivatives by applying small changes to the input
|
|
26
|
+
variable and measuring the resulting change in the target variable. This is used
|
|
27
|
+
to estimate marginal tax rates and benefit withdrawal rates.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
sim: PolicyEngine simulation object
|
|
31
|
+
target_variable: Variable to measure changes in (typically household_net_income)
|
|
32
|
+
input_variable: Variable to change (typically employment_income)
|
|
33
|
+
year: Year for calculation
|
|
34
|
+
count_adults: Number of adults to apply changes to
|
|
35
|
+
delta: Size of change to apply for derivative calculation (£)
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Array of marginal rates clipped between 0 and 1
|
|
39
|
+
"""
|
|
40
|
+
# Get baseline values for input variable and identify adults
|
|
41
|
+
input_variable_values = sim.calculate(input_variable, year).copy()
|
|
42
|
+
adult_index = sim.calculate("adult_index")
|
|
43
|
+
entity_key = sim.tax_benefit_system.variables[input_variable].entity.key
|
|
44
|
+
|
|
45
|
+
# Calculate baseline target values
|
|
46
|
+
original_target_values = sim.calculate(
|
|
47
|
+
target_variable, year, map_to=entity_key
|
|
48
|
+
)
|
|
49
|
+
new_target_values = original_target_values.copy()
|
|
50
|
+
|
|
51
|
+
# Apply delta change to each adult sequentially to calculate marginal effects
|
|
52
|
+
for i in range(count_adults):
|
|
53
|
+
gets_pay_rise = adult_index == i + 1
|
|
54
|
+
new_input_variable_values = input_variable_values.copy()
|
|
55
|
+
new_input_variable_values[gets_pay_rise] += delta
|
|
56
|
+
sim.reset_calculations()
|
|
57
|
+
sim.set_input(input_variable, year, new_input_variable_values)
|
|
58
|
+
new_target_values[gets_pay_rise] = sim.calculate(
|
|
59
|
+
target_variable, year, map_to=entity_key
|
|
60
|
+
)[gets_pay_rise]
|
|
61
|
+
|
|
62
|
+
# Calculate marginal rate as change in target per unit change in input
|
|
63
|
+
rel_marginal_wages = (new_target_values - original_target_values) / delta
|
|
64
|
+
|
|
65
|
+
# Set non-adult observations to NaN
|
|
66
|
+
rel_marginal_wages[
|
|
67
|
+
~pd.Series(adult_index).isin(range(1, count_adults + 1))
|
|
68
|
+
] = np.nan
|
|
69
|
+
|
|
70
|
+
# Reset simulation to original state
|
|
71
|
+
sim.reset_calculations()
|
|
72
|
+
sim.set_input(input_variable, year, input_variable_values)
|
|
73
|
+
|
|
74
|
+
# Clip to ensure rates are between 0 and 1 (0% to 100% retention)
|
|
75
|
+
return rel_marginal_wages.round(4)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def calculate_relative_income_change(
|
|
79
|
+
sim: Simulation,
|
|
80
|
+
target_variable: str = "hbai_household_net_income",
|
|
81
|
+
year: int = 2025,
|
|
82
|
+
) -> pd.DataFrame:
|
|
83
|
+
"""Calculate relative change in income between baseline and scenario.
|
|
84
|
+
|
|
85
|
+
This function compares the target variable values between the baseline
|
|
86
|
+
simulation and the reform scenario to measure the income effect of the policy.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
sim: PolicyEngine simulation object (should have baseline attribute)
|
|
90
|
+
target_variable: Variable to measure changes in
|
|
91
|
+
year: Year for calculation
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
DataFrame with baseline, scenario, relative change, and absolute change columns
|
|
95
|
+
"""
|
|
96
|
+
# Get income values from baseline and reform scenarios
|
|
97
|
+
original_target_values = sim.baseline.calculate(
|
|
98
|
+
target_variable, year, map_to="person"
|
|
99
|
+
)
|
|
100
|
+
reformed_target_values = sim.calculate(
|
|
101
|
+
target_variable, year, map_to="person"
|
|
102
|
+
)
|
|
103
|
+
original_target_values = pd.Series(
|
|
104
|
+
original_target_values, index=reformed_target_values.index
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Calculate relative change, handling division by zero
|
|
108
|
+
rel_change = (
|
|
109
|
+
reformed_target_values - original_target_values
|
|
110
|
+
) / original_target_values
|
|
111
|
+
rel_change[original_target_values == 0] = np.nan
|
|
112
|
+
|
|
113
|
+
# Clip extreme values and fill NaN with 0
|
|
114
|
+
rel_changes = rel_change.clip(-1, 1).fillna(0)
|
|
115
|
+
|
|
116
|
+
return pd.DataFrame(
|
|
117
|
+
{
|
|
118
|
+
"baseline": original_target_values,
|
|
119
|
+
"scenario": reformed_target_values,
|
|
120
|
+
"rel_change": rel_changes,
|
|
121
|
+
"abs_change": reformed_target_values - original_target_values,
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def calculate_derivative_change(
|
|
127
|
+
sim: Simulation,
|
|
128
|
+
target_variable: str = "hbai_household_net_income",
|
|
129
|
+
input_variable: str = "employment_income",
|
|
130
|
+
year: int = 2025,
|
|
131
|
+
count_adults: int = 2,
|
|
132
|
+
delta: float = 1_000,
|
|
133
|
+
) -> pd.DataFrame:
|
|
134
|
+
"""Calculate change in marginal rates between baseline and scenario.
|
|
135
|
+
|
|
136
|
+
This function computes how marginal tax rates or benefit withdrawal rates
|
|
137
|
+
change as a result of the policy reform, which drives substitution effects
|
|
138
|
+
in labour supply responses.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
sim: PolicyEngine simulation object (should have baseline attribute)
|
|
142
|
+
target_variable: Variable to measure marginal rates for
|
|
143
|
+
input_variable: Variable to change for derivative calculation
|
|
144
|
+
year: Year for calculation
|
|
145
|
+
count_adults: Number of adults to calculate derivatives for
|
|
146
|
+
delta: Size of change for derivative calculation (£)
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
DataFrame with baseline, scenario, relative change, and absolute change in derivatives
|
|
150
|
+
"""
|
|
151
|
+
# Calculate marginal rates under baseline and reform scenarios
|
|
152
|
+
original_deriv = calculate_derivative(
|
|
153
|
+
sim=sim.baseline,
|
|
154
|
+
target_variable=target_variable,
|
|
155
|
+
input_variable=input_variable,
|
|
156
|
+
year=year,
|
|
157
|
+
count_adults=count_adults,
|
|
158
|
+
delta=delta,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
reformed_deriv = calculate_derivative(
|
|
162
|
+
sim=sim,
|
|
163
|
+
target_variable=target_variable,
|
|
164
|
+
input_variable=input_variable,
|
|
165
|
+
year=year,
|
|
166
|
+
count_adults=count_adults,
|
|
167
|
+
delta=delta,
|
|
168
|
+
)
|
|
169
|
+
original_deriv = pd.Series(original_deriv, index=reformed_deriv.index)
|
|
170
|
+
|
|
171
|
+
# Calculate relative and absolute changes in marginal rates
|
|
172
|
+
rel_change = reformed_deriv / original_deriv - 1
|
|
173
|
+
abs_change = reformed_deriv - original_deriv
|
|
174
|
+
|
|
175
|
+
# Clip extreme relative changes to avoid misleading results from small baseline derivatives
|
|
176
|
+
rel_change = rel_change.clip(-1, 1)
|
|
177
|
+
|
|
178
|
+
rel_change[rel_change == np.inf] = 0
|
|
179
|
+
|
|
180
|
+
return pd.DataFrame(
|
|
181
|
+
{
|
|
182
|
+
"baseline": original_deriv,
|
|
183
|
+
"scenario": reformed_deriv,
|
|
184
|
+
"rel_change": rel_change,
|
|
185
|
+
"abs_change": abs_change,
|
|
186
|
+
}
|
|
187
|
+
).fillna(0)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def calculate_labour_substitution_elasticities(
|
|
191
|
+
sim: Simulation,
|
|
192
|
+
) -> np.ndarray:
|
|
193
|
+
"""Calculate labour supply substitution elasticities by demographic group.
|
|
194
|
+
|
|
195
|
+
Uses OBR elasticity estimates to assign substitution elasticities based on
|
|
196
|
+
gender, marital status, and presence/age of children. These elasticities
|
|
197
|
+
measure how labour supply responds to changes in marginal tax rates.
|
|
198
|
+
|
|
199
|
+
Reference: https://obr.uk/docs/dlm_uploads/NICS-Cut-Impact-on-Labour-Supply-Note.pdf
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
sim: PolicyEngine simulation object
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Array of substitution elasticities for each person
|
|
206
|
+
"""
|
|
207
|
+
# Get demographic characteristics for elasticity assignment
|
|
208
|
+
gender = sim.calculate("gender")
|
|
209
|
+
is_married = sim.calculate("is_married", map_to="person")
|
|
210
|
+
has_children = sim.calculate("benunit_count_children", map_to="person") > 0
|
|
211
|
+
youngest_child_age = sim.calculate("youngest_child_age", map_to="person")
|
|
212
|
+
|
|
213
|
+
# Initialize elasticity array
|
|
214
|
+
elasticities = np.zeros(gender.shape, dtype=float)
|
|
215
|
+
|
|
216
|
+
# Married or cohabiting women - higher elasticities, especially with young children
|
|
217
|
+
married_women = (gender == "FEMALE") & is_married
|
|
218
|
+
elasticities[married_women & ~has_children] = 0.14 # No children
|
|
219
|
+
|
|
220
|
+
# Elasticities vary significantly by youngest child's age
|
|
221
|
+
elasticities[married_women & has_children & (youngest_child_age <= 2)] = (
|
|
222
|
+
0.301 # 0-2 years
|
|
223
|
+
)
|
|
224
|
+
elasticities[
|
|
225
|
+
married_women
|
|
226
|
+
& has_children
|
|
227
|
+
& (youngest_child_age >= 3)
|
|
228
|
+
& (youngest_child_age <= 4)
|
|
229
|
+
] = 0.439 # 3-4 years (highest)
|
|
230
|
+
elasticities[
|
|
231
|
+
married_women
|
|
232
|
+
& has_children
|
|
233
|
+
& (youngest_child_age >= 5)
|
|
234
|
+
& (youngest_child_age <= 10)
|
|
235
|
+
] = 0.173 # 5-10 years
|
|
236
|
+
elasticities[married_women & has_children & (youngest_child_age >= 11)] = (
|
|
237
|
+
0.160 # 11+ years
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# Lone parents - lower elasticities than married women, reflecting different constraints
|
|
241
|
+
lone_parents = (gender == "FEMALE") & ~is_married & has_children
|
|
242
|
+
elasticities[lone_parents & (youngest_child_age <= 4)] = 0.094 # 0-4 years
|
|
243
|
+
elasticities[
|
|
244
|
+
lone_parents & (youngest_child_age >= 5) & (youngest_child_age <= 10)
|
|
245
|
+
] = 0.128 # 5-10 years
|
|
246
|
+
elasticities[
|
|
247
|
+
lone_parents & (youngest_child_age >= 11) & (youngest_child_age <= 18)
|
|
248
|
+
] = 0.136 # 11-18 years
|
|
249
|
+
|
|
250
|
+
# Men (excluding lone fathers) - moderate, consistent elasticity
|
|
251
|
+
elasticities[(gender == "MALE") & ~(~is_married & has_children)] = 0.15
|
|
252
|
+
|
|
253
|
+
# Single women without children - same as men
|
|
254
|
+
elasticities[(gender == "FEMALE") & ~is_married & ~has_children] = 0.15
|
|
255
|
+
|
|
256
|
+
return elasticities
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def calculate_labour_net_income_elasticities(
|
|
260
|
+
sim: Simulation,
|
|
261
|
+
) -> np.ndarray:
|
|
262
|
+
"""Calculate labour supply income elasticities by demographic group.
|
|
263
|
+
|
|
264
|
+
Uses OBR elasticity estimates to assign income elasticities based on
|
|
265
|
+
gender, marital status, and presence/age of children. These elasticities
|
|
266
|
+
measure how labour supply responds to changes in unearned income.
|
|
267
|
+
|
|
268
|
+
Reference: https://obr.uk/docs/dlm_uploads/NICS-Cut-Impact-on-Labour-Supply-Note.pdf
|
|
269
|
+
Table A2 - Income elasticities
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
sim: PolicyEngine simulation object
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
Array of income elasticities for each person (typically negative)
|
|
276
|
+
"""
|
|
277
|
+
# Get demographic characteristics for elasticity assignment
|
|
278
|
+
gender = sim.calculate("gender")
|
|
279
|
+
is_married = sim.calculate("is_married", map_to="person")
|
|
280
|
+
has_children = sim.calculate("benunit_count_children", map_to="person") > 0
|
|
281
|
+
youngest_child_age = sim.calculate("youngest_child_age", map_to="person")
|
|
282
|
+
|
|
283
|
+
# Initialize elasticity array
|
|
284
|
+
elasticities = np.zeros(gender.shape, dtype=float)
|
|
285
|
+
|
|
286
|
+
# Married or cohabiting women - negative income elasticities (normal good)
|
|
287
|
+
married_women = (gender == "FEMALE") & is_married
|
|
288
|
+
elasticities[married_women & ~has_children] = (
|
|
289
|
+
0.0 # No income effect without children
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
# Stronger negative income effects with younger children
|
|
293
|
+
elasticities[married_women & has_children & (youngest_child_age <= 2)] = (
|
|
294
|
+
-0.185
|
|
295
|
+
) # 0-2 years
|
|
296
|
+
elasticities[
|
|
297
|
+
married_women
|
|
298
|
+
& has_children
|
|
299
|
+
& (youngest_child_age >= 3)
|
|
300
|
+
& (youngest_child_age <= 4)
|
|
301
|
+
] = -0.173 # 3-4 years
|
|
302
|
+
elasticities[
|
|
303
|
+
married_women
|
|
304
|
+
& has_children
|
|
305
|
+
& (youngest_child_age >= 5)
|
|
306
|
+
& (youngest_child_age <= 10)
|
|
307
|
+
] = -0.102 # 5-10 years
|
|
308
|
+
elasticities[married_women & has_children & (youngest_child_age >= 11)] = (
|
|
309
|
+
-0.063
|
|
310
|
+
) # 11+ years
|
|
311
|
+
|
|
312
|
+
# Lone parents - smaller negative income effects than married women
|
|
313
|
+
lone_parents = (gender == "FEMALE") & ~is_married & has_children
|
|
314
|
+
elasticities[lone_parents & (youngest_child_age <= 4)] = (
|
|
315
|
+
-0.037
|
|
316
|
+
) # 0-4 years
|
|
317
|
+
elasticities[
|
|
318
|
+
lone_parents & (youngest_child_age >= 5) & (youngest_child_age <= 10)
|
|
319
|
+
] = -0.075 # 5-10 years
|
|
320
|
+
elasticities[
|
|
321
|
+
lone_parents & (youngest_child_age >= 11) & (youngest_child_age <= 18)
|
|
322
|
+
] = -0.054 # 11-18 years
|
|
323
|
+
|
|
324
|
+
# Men (excluding lone fathers) - small negative income effect
|
|
325
|
+
elasticities[(gender == "MALE") & ~(~is_married & has_children)] = -0.05
|
|
326
|
+
|
|
327
|
+
# Single women without children - same as men
|
|
328
|
+
elasticities[(gender == "FEMALE") & ~is_married & ~has_children] = -0.05
|
|
329
|
+
|
|
330
|
+
return elasticities
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def calculate_employment_income_change(
|
|
334
|
+
employment_income: np.ndarray,
|
|
335
|
+
derivative_changes: pd.DataFrame,
|
|
336
|
+
income_changes: pd.DataFrame,
|
|
337
|
+
substitution_elasticities: np.ndarray,
|
|
338
|
+
income_elasticities: np.ndarray,
|
|
339
|
+
) -> np.ndarray:
|
|
340
|
+
"""Calculate total labour supply response combining substitution and income effects.
|
|
341
|
+
|
|
342
|
+
This function implements the Slutsky equation decomposition of labour supply
|
|
343
|
+
responses into substitution and income effects. The total response is the
|
|
344
|
+
sum of these two components.
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
employment_income: Baseline employment income levels
|
|
348
|
+
derivative_changes: Changes in marginal rates (substitution effect driver)
|
|
349
|
+
income_changes: Changes in income levels (income effect driver)
|
|
350
|
+
substitution_elasticities: Elasticities for substitution effects
|
|
351
|
+
income_elasticities: Elasticities for income effects
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
Array of employment income changes due to labour supply responses
|
|
355
|
+
"""
|
|
356
|
+
# Calculate substitution effect: response to changes in marginal rates
|
|
357
|
+
substitution_response = (
|
|
358
|
+
employment_income
|
|
359
|
+
* derivative_changes["wage_rel_change"]
|
|
360
|
+
* substitution_elasticities
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
# Calculate income effect: response to changes in unearned income
|
|
364
|
+
income_response = (
|
|
365
|
+
employment_income
|
|
366
|
+
* income_changes["income_rel_change"]
|
|
367
|
+
* income_elasticities
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
# Total labour supply response is sum of substitution and income effects
|
|
371
|
+
total_response = substitution_response + income_response
|
|
372
|
+
|
|
373
|
+
# No response for people with zero employment income
|
|
374
|
+
total_response[employment_income == 0] = 0
|
|
375
|
+
|
|
376
|
+
df = pd.DataFrame(
|
|
377
|
+
{
|
|
378
|
+
"substitution_response": substitution_response,
|
|
379
|
+
"income_response": income_response,
|
|
380
|
+
"total_response": total_response,
|
|
381
|
+
}
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
return df.fillna(0)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Standard library imports
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
|
|
4
|
+
# Third-party imports
|
|
5
|
+
import numpy as np
|
|
6
|
+
from microdf import MicroDataFrame, MicroSeries
|
|
7
|
+
|
|
8
|
+
# PolicyEngine core imports
|
|
9
|
+
from policyengine_core.tracers import SimpleTracer
|
|
10
|
+
|
|
11
|
+
from .simulation import Simulation
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Microsimulation(Simulation):
|
|
15
|
+
"""Extended simulation class with weighting support for microsimulation.
|
|
16
|
+
|
|
17
|
+
Provides weighted calculations using survey weights for population-level
|
|
18
|
+
estimates and statistics.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def get_weights(
|
|
22
|
+
self, variable_name: str, period: str, map_to: Optional[str] = None
|
|
23
|
+
) -> np.ndarray:
|
|
24
|
+
"""Get weights for the specified variable's entity.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
variable_name: Name of the variable to get weights for
|
|
28
|
+
period: Time period for the weights
|
|
29
|
+
map_to: Optional entity key to map weights to
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Array of weights for the entity
|
|
33
|
+
"""
|
|
34
|
+
variable = self.tax_benefit_system.get_variable(variable_name)
|
|
35
|
+
entity_key = map_to or variable.entity.key
|
|
36
|
+
weight_variable_name = f"{entity_key}_weight"
|
|
37
|
+
return self.calculate(
|
|
38
|
+
weight_variable_name, period, map_to=map_to, unweighted=True
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def calculate(
|
|
42
|
+
self,
|
|
43
|
+
variable_name: str,
|
|
44
|
+
period: str = None,
|
|
45
|
+
map_to: str = None,
|
|
46
|
+
decode_enums: bool = False,
|
|
47
|
+
unweighted: bool = False,
|
|
48
|
+
):
|
|
49
|
+
tracer: SimpleTracer = self.tracer
|
|
50
|
+
result = super().calculate(
|
|
51
|
+
variable_name, period, map_to=map_to, decode_enums=decode_enums
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
if not unweighted and len(tracer.stack) == 0:
|
|
55
|
+
weights = self.get_weights(variable_name, period, map_to=map_to)
|
|
56
|
+
return MicroSeries(result, weights=weights)
|
|
57
|
+
|
|
58
|
+
return result
|
|
59
|
+
|
|
60
|
+
def calculate_dataframe(
|
|
61
|
+
self,
|
|
62
|
+
variable_names: List[str],
|
|
63
|
+
period: Optional[str] = None,
|
|
64
|
+
map_to: Optional[str] = None,
|
|
65
|
+
use_weights: bool = True,
|
|
66
|
+
) -> MicroDataFrame:
|
|
67
|
+
"""Calculate multiple variables as a weighted DataFrame.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
variable_names: List of variable names to calculate
|
|
71
|
+
period: Time period for calculation
|
|
72
|
+
map_to: Optional entity key to map results to
|
|
73
|
+
use_weights: Whether to apply survey weights
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
MicroDataFrame with calculated values and weights
|
|
77
|
+
"""
|
|
78
|
+
values = super().calculate_dataframe(variable_names, period, map_to)
|
|
79
|
+
if not use_weights:
|
|
80
|
+
return values
|
|
81
|
+
weights = self.get_weights(variable_names[0], period, map_to=map_to)
|
|
82
|
+
return MicroDataFrame(values, weights=weights)
|
|
83
|
+
|
|
84
|
+
def compare(
|
|
85
|
+
self,
|
|
86
|
+
other: "Simulation",
|
|
87
|
+
variables: list[str] = None,
|
|
88
|
+
period: str = None,
|
|
89
|
+
change_only: bool = False,
|
|
90
|
+
):
|
|
91
|
+
"""Compare two simulations for a specific variable list.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
other: Another Simulation instance to compare against
|
|
95
|
+
variables: List of variable names to compare. If None, compares all variables.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
DataFrame with comparison results
|
|
99
|
+
"""
|
|
100
|
+
df = super().compare(
|
|
101
|
+
other, variables=variables, period=period, change_only=change_only
|
|
102
|
+
)
|
|
103
|
+
return MicroDataFrame(
|
|
104
|
+
df, weights=self.get_weights(variables[0], period)
|
|
105
|
+
)
|
policyengine_uk/model_api.py
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
description: Bank of England base rate
|
|
2
|
+
metadata:
|
|
3
|
+
label: BoE base rate
|
|
4
|
+
unit: /1
|
|
5
|
+
period: year
|
|
6
|
+
reference:
|
|
7
|
+
- title: Bank of England - Bank Rate history
|
|
8
|
+
href: https://www.bankofengland.co.uk/boeapps/database/Bank-Rate.asp
|
|
9
|
+
- title: OBR EFO November 2025 - Market expectations for Bank Rate
|
|
10
|
+
href: https://obr.uk/efo/economic-and-fiscal-outlook-november-2025/
|
|
11
|
+
documentation: |
|
|
12
|
+
The Bank of England base rate (Bank Rate).
|
|
13
|
+
Used in student loan interest calculations for Plans 1 and 4,
|
|
14
|
+
which charge the lower of RPI or (BoE base rate + 1%).
|
|
15
|
+
|
|
16
|
+
Historical values are from Bank of England data.
|
|
17
|
+
Forecast values (2025+) are based on OBR November 2025 EFO market
|
|
18
|
+
expectations: Bank Rate expected to fall from 4% to 3.6% by end-2026,
|
|
19
|
+
then gradually return to 4% by end-2030.
|
|
20
|
+
|
|
21
|
+
# Historical: Bank of England outturn
|
|
22
|
+
# Forecast: OBR November 2025 EFO market expectations
|
|
23
|
+
values:
|
|
24
|
+
2020-01-01: 0.001 # 0.1% from March 2020
|
|
25
|
+
2021-01-01: 0.001 # 0.1% throughout
|
|
26
|
+
2022-01-01: 0.01 # Rising from 0.25% to 3.5%
|
|
27
|
+
2023-01-01: 0.0425 # Rising from 3.5% to 5.25%
|
|
28
|
+
2024-01-01: 0.0525 # 5.25% until August, then 5%
|
|
29
|
+
2025-01-01: 0.04 # 4% as of Nov 2025
|
|
30
|
+
2026-01-01: 0.036 # OBR: falls to 3.6% by end-2026
|
|
31
|
+
2027-01-01: 0.037 # OBR: gradual rise back
|
|
32
|
+
2028-01-01: 0.038 # OBR: average ~3.8% 2025-2029
|
|
33
|
+
2029-01-01: 0.039 # OBR: rising toward 4%
|
|
34
|
+
2030-01-01: 0.04 # OBR: returns to 4% by end-2030
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# The percentage by which employees reduce their salary sacrifice pension contributions in response to the cap.
|
|
2
|
+
# - 0 = no adjustment (employees keep high salary sacrifice and pay NI on excess)
|
|
3
|
+
# - 0.5 = employees reduce salary sacrifice by 50% of the excess above cap
|
|
4
|
+
# - 1.0 = employees fully optimize (reduce salary sacrifice to exactly the cap)
|
|
5
|
+
# Most employees would optimize their salary sacrifice to avoid NI charges by reducing it to the cap level.
|
|
6
|
+
description: The percentage by which employees reduce their salary sacrifice pension contributions in response to the salary sacrifice pension cap.
|
|
7
|
+
values:
|
|
8
|
+
2010-01-01: 1.0 # Default: full optimization (employees reduce to cap level)
|
|
9
|
+
metadata:
|
|
10
|
+
unit: /1
|
|
11
|
+
label: Employee salary sacrifice reduction rate in response to cap
|
|
12
|
+
reference:
|
|
13
|
+
- title: "Cap on UK salary sacrifice benefits is 'short-term' choice, warn experts"
|
|
14
|
+
href: https://docs.google.com/document/d/1Rhrfrg7A_oZHudmA775otAn1EE4-YthgeyS9nL-PrE8/edit?tab=t.0
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# The rate at which ALL workers' employment income is reduced due to the salary sacrifice cap.
|
|
2
|
+
# When the salary sacrifice cap is active, employers face increased NI costs on excess contributions.
|
|
3
|
+
# They spread these costs across ALL employees (not just affected workers), reducing everyone's pay.
|
|
4
|
+
#
|
|
5
|
+
# Calculation from blog (https://policyengine.org/uk/research/uk-salary-sacrifice-cap):
|
|
6
|
+
# - Total excess above £2,000 cap: £13.8 billion
|
|
7
|
+
# - Employer NI on excess (at 15%): £2.1 billion
|
|
8
|
+
# - Total UK employment income: ~£1.3 trillion
|
|
9
|
+
# - Broad-base haircut rate: £2.1bn / £1.3tn ≈ 0.0016 (0.16%)
|
|
10
|
+
#
|
|
11
|
+
# This is applied to ALL workers' employment income, not just salary sacrificers.
|
|
12
|
+
# The economic logic: employers cannot target only affected workers (who would negotiate
|
|
13
|
+
# to recoup the loss), so they spread the cost across all employees.
|
|
14
|
+
description: Rate at which all workers' employment income is reduced when salary sacrifice cap is active.
|
|
15
|
+
values:
|
|
16
|
+
2010-01-01: 0.0016
|
|
17
|
+
metadata:
|
|
18
|
+
unit: /1
|
|
19
|
+
label: Salary sacrifice broad-base haircut rate
|
|
20
|
+
reference:
|
|
21
|
+
- title: PolicyEngine UK Salary Sacrifice Cap Analysis
|
|
22
|
+
href: https://policyengine.org/uk/research/uk-salary-sacrifice-cap
|
|
@@ -7,11 +7,11 @@ consumer_incidence:
|
|
|
7
7
|
name: carbon_tax_consumer_incidence
|
|
8
8
|
unit: /1
|
|
9
9
|
values:
|
|
10
|
-
2010-01-01: 1
|
|
10
|
+
2010-01-01: 1
|
|
11
11
|
rate:
|
|
12
12
|
description: Price per tonne of carbon emissions
|
|
13
13
|
metadata:
|
|
14
14
|
label: Carbon tax
|
|
15
15
|
unit: currency-GBP
|
|
16
16
|
values:
|
|
17
|
-
2010-01-01: 0
|
|
17
|
+
2010-01-01: 0
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
rate:
|
|
2
2
|
description: Tax rate on the unimproved value of land
|
|
3
3
|
values:
|
|
4
|
-
2010-01-01: 0
|
|
4
|
+
2010-01-01: 0
|
|
5
5
|
metadata:
|
|
6
6
|
label: Land value tax
|
|
7
7
|
unit: /1
|
|
8
8
|
household_rate:
|
|
9
9
|
description: Tax rate on the unimproved value of land owned by households
|
|
10
10
|
values:
|
|
11
|
-
2010-01-01: 0
|
|
11
|
+
2010-01-01: 0
|
|
12
12
|
metadata:
|
|
13
13
|
label: Land value tax (households)
|
|
14
14
|
unit: /1
|
|
15
15
|
corporate_rate:
|
|
16
16
|
description: Tax rate on the unimproved value of land owned by corporations
|
|
17
17
|
values:
|
|
18
|
-
2010-01-01: 0
|
|
18
|
+
2010-01-01: 0
|
|
19
19
|
metadata:
|
|
20
20
|
label: Land value tax (corporations)
|
|
21
21
|
unit: /1
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
description: Full TV licence for a colour TV, before any discounts are applied.
|
|
2
2
|
values:
|
|
3
|
-
2017-04-01: 147
|
|
4
|
-
2018-04-01: 150.
|
|
5
|
-
2019-04-01: 154.
|
|
6
|
-
2020-04-01: 157.
|
|
7
|
-
2021-04-01: 159
|
|
3
|
+
2017-04-01: 147
|
|
4
|
+
2018-04-01: 150.5
|
|
5
|
+
2019-04-01: 154.5
|
|
6
|
+
2020-04-01: 157.5
|
|
7
|
+
2021-04-01: 159
|
|
8
8
|
metadata:
|
|
9
9
|
unit: currency-GBP
|
|
10
10
|
period: year
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
description: Total spending by the Department for Education (DfE) on state education services (£ billions). Includes both Resource Departmental Expenditure Limit and Capital Departmental Expenditure Limit, but excludes depreciation.
|
|
2
2
|
values:
|
|
3
|
-
2023-01-01: 88
|
|
3
|
+
2023-01-01: 88
|
|
4
4
|
2024-01-01: 94.9
|
|
5
5
|
2025-01-01: 100.9
|
|
6
6
|
metadata:
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
description: Cumulative index of regulated rail fares under current law. Base year 2020 = 1.0. Reflects the Autumn Budget 2025 fare freeze in 2026.
|
|
2
|
+
values:
|
|
3
|
+
# Base year
|
|
4
|
+
2020-01-01: 1.000
|
|
5
|
+
# 2021: +1.0% (COVID-suppressed)
|
|
6
|
+
2021-01-01: 1.010
|
|
7
|
+
# 2022: +3.8%
|
|
8
|
+
2022-01-01: 1.048
|
|
9
|
+
# 2023: +5.9%
|
|
10
|
+
2023-01-01: 1.110
|
|
11
|
+
# 2024: +4.9%
|
|
12
|
+
2024-01-01: 1.165
|
|
13
|
+
# 2025: +4.5% (March 2025 actual)
|
|
14
|
+
2025-01-01: 1.217
|
|
15
|
+
# 2026: FROZEN (Autumn Budget 2025 policy)
|
|
16
|
+
2026-01-01: 1.217
|
|
17
|
+
# 2027: +4.2% (projected - OBR RPI ~3.2% + 1%, from frozen base)
|
|
18
|
+
2027-01-01: 1.268
|
|
19
|
+
# 2028: +3.9% (projected - OBR RPI ~2.9% + 1%)
|
|
20
|
+
2028-01-01: 1.318
|
|
21
|
+
# 2029: +3.9% (projected - OBR RPI ~2.9% + 1%)
|
|
22
|
+
2029-01-01: 1.369
|
|
23
|
+
metadata:
|
|
24
|
+
unit: /1
|
|
25
|
+
label: Regulated rail fare index (current law)
|
|
26
|
+
reference:
|
|
27
|
+
- title: GOV.UK Rail Fares Freeze Announcement - Autumn Budget 2025
|
|
28
|
+
href: https://www.gov.uk/government/news/rail-fares-freeze-autumn-budget-2025
|
|
29
|
+
- title: OBR Economic and Fiscal Outlook March 2025
|
|
30
|
+
href: https://obr.uk/efo/economic-and-fiscal-outlook-march-2025/
|
|
31
|
+
- title: Network Rail - How train fares are set
|
|
32
|
+
href: https://www.networkrail.co.uk/stories/how-train-fares-set/
|