PolicyEngine.py
A Python package for tax-benefit microsimulation analysis. Run policy simulations, analyse distributional impacts, and visualise results across the UK and US.
Requires Python 3.11–3.14.
Results are estimates. PolicyEngine simulates a large, evolving body of tax-benefit law (the US model alone encodes more than 95,000 parameters across 5,500+ variables) over survey microdata calibrated to administrative targets. Treat outputs as estimates, and validate them against the policies relevant to your analysis, the scope of the rules engine, and external or back-of-the-envelope calculations. You can inspect the certified US dataset's calibration at https://calibration-diagnostics.vercel.app/populace.
Quick start
Household calculator
import policyengine as pe
# UK: single adult earning £50,000
uk = pe.uk.calculate_household(
people=[{"age": 35, "employment_income": 50_000}],
year=2026,
)
print(uk.person[0].income_tax) # income tax
print(uk.household.hbai_household_net_income) # net income
# US: single filer in California, with a reform
us = pe.us.calculate_household(
people=[{"age": 35, "employment_income": 60_000}],
tax_unit={"filing_status": "SINGLE"},
household={"state_code": "CA", "county_fips": "06037"},
year=2026,
reform={"gov.irs.credits.ctc.amount.adult_dependent": 1000},
)
print(us.tax_unit.income_tax, us.household.household_net_income)
US default outputs require observed county FIPS or an explicit national/SPM-area choice. See the SPM household contract for settings, provenance and the coordinated bundle this release ships.
Population analysis
import policyengine as pe
from policyengine.core import Simulation
from policyengine.outputs.aggregate import Aggregate, AggregateType
datasets = pe.uk.ensure_datasets(
datasets=["enhanced_frs_2023_24"],
years=[2026],
data_folder="./data",
)
dataset = datasets["enhanced_frs_2023_24_2026"]
simulation = Simulation(dataset=dataset, tax_benefit_model_version=pe.uk.model)
simulation.run()
agg = Aggregate(
simulation=simulation,
variable="universal_credit",
aggregate_type=AggregateType.SUM,
entity="benunit",
)
agg.run()
print(f"Total UC spending: £{agg.result / 1e9:.1f}bn")
For baseline-vs-reform comparisons, see pe.uk.economic_impact_analysis
and its US counterpart.
UK population data is stored in a private Hugging Face model repository. Set
HUGGING_FACE_TOKEN to a token from an account with access before running UK
population examples. To download the raw .h5 file directly, see
Microsimulation.
Documentation
Core concepts:
- Microsimulation: Datasets, simulations, outputs, performance
- Country models: UK and US entities, defaults, and differences
- Model architecture: How rules, data, and simulations fit together
Examples:
examples/income_distribution_us.py: Analyse benefit distribution by decileexamples/employment_income_variation_uk.py: Model employment income phase-outsexamples/policy_change_uk.py: Analyse policy reform impactsexamples/paper_repro_uk.py: Reproduce the UK reform analysis used in the JOSS paper draft
Installation
As a library
pip install policyengine
This installs both UK and US country models. To install only one:
pip install policyengine[uk] # UK model only
pip install policyengine[us] # US model only
For a certified package-plus-dataset bundle, use the bundle installer as the single setup command:
uvx --from policyengine policyengine bundle install
This installs the bundled package scaffold with pip, downloads the certified US
and UK datasets into ./data, and writes a local receipt that can be checked
with policyengine bundle status. When run from uvx or pipx, the installer
creates or reuses ./.venv; inside an existing virtualenv or conda environment,
it installs into the active environment.
For development
git clone https://github.com/PolicyEngine/policyengine.py.git
cd policyengine.py
uv pip install -e .[dev] # install with dev dependencies (pytest, ruff, mypy, etc.)
Development
Running configurations
| Configuration | Install | Use case |
|---|---|---|
| Library user | pip install policyengine |
Using the package in your own code |
| UK only | pip install policyengine[uk] |
Only need UK simulations |
| US only | pip install policyengine[us] |
Only need US simulations |
| Certified bundle | uvx --from policyengine policyengine bundle install |
Reproducible model-plus-data setup |
| Developer | uv pip install -e .[dev] |
Contributing to the package |
Common commands
make format # ruff format
make test # pytest with coverage
make docs # build static Quarto HTML docs
make docs-serve # preview the docs locally
make clean # remove caches, build artifacts, .h5 files
Testing
Tests require a HUGGING_FACE_TOKEN environment variable for downloading datasets:
export HUGGING_FACE_TOKEN=hf_...
make test
To run a specific test:
pytest tests/test_models.py -v
pytest tests/test_parametric_reforms.py -k "test_uk" -v
Linting and type checking
ruff format . # format code
ruff check . # lint
mypy src/policyengine # type check (informational — not yet enforced in CI)
CI pipeline
PRs trigger the following checks:
| Check | Status | Command |
|---|---|---|
| Lint + format | Required | ruff check . + ruff format --check . |
| Tests (Python 3.13) | Required | make test |
| Tests (Python 3.14) | Required | make test |
| Mypy | Informational | mypy src/policyengine |
| Docs build | Required | Jupyter Book build |
Versioning and releases
This project uses towncrier for changelog management. When making a PR, add a changelog fragment:
# Fragment types: breaking, added, changed, fixed, removed
echo "Description of change" > changelog.d/my-change.added
On merge, the versioning workflow bumps the version, builds the changelog, and creates a GitHub Release.
Paper reproduction
Use the pinned interpreter and the UK extra to run the checked-in paper repro:
uv run --python 3.14 --extra uk python examples/paper_repro_uk.py
On first run this will create ./data/enhanced_frs_2023_24_year_2026.h5.
Features
- Multi-country support: UK and US tax-benefit systems
- Representative microdata: Load FRS, CPS, or create custom scenarios
- Policy reforms: Parametric reforms with date-bound parameter values
- Distributional analysis: Aggregate statistics by income decile, demographics
- Entity mapping: Automatic mapping between person, household, tax unit levels
- Visualisation: PolicyEngine-branded charts with Plotly
Key concepts
Datasets
Datasets contain microdata at entity level (person, household, tax unit). Load representative data or create custom scenarios:
from policyengine.tax_benefit_models.uk import PolicyEngineUKDataset
dataset = PolicyEngineUKDataset(
name="Representative data",
filepath="./data/frs_2023_24_year_2026.h5",
year=2026,
)
dataset.load()
Simulations
Simulations apply tax-benefit models to datasets:
import policyengine as pe
from policyengine.core import Simulation
simulation = Simulation(
dataset=dataset,
tax_benefit_model_version=pe.uk.model,
)
simulation.run()
# Access calculated variables
output = simulation.output_dataset.data
print(output.household[["household_net_income", "household_benefits"]])
Outputs
Extract insights with aggregate statistics:
from policyengine.outputs.aggregate import Aggregate, AggregateType
# Mean income in top decile
agg = Aggregate(
simulation=simulation,
variable="household_net_income",
aggregate_type=AggregateType.MEAN,
filter_variable="household_net_income",
quantile=10,
quantile_eq=10,
)
agg.run()
print(f"Top decile mean income: £{agg.result:,.0f}")
Policy reforms
Apply parametric reforms:
from policyengine.core import Policy, Parameter, ParameterValue
import datetime
parameter = Parameter(
name="gov.hmrc.income_tax.allowances.personal_allowance.amount",
tax_benefit_model_version=pe.uk.model,
data_type=float,
)
policy = Policy(
name="Increase personal allowance",
parameter_values=[
ParameterValue(
parameter=parameter,
start_date=datetime.date(2026, 1, 1),
end_date=datetime.date(2026, 12, 31),
value=15000,
)
],
)
# Run reform simulation
reform_sim = Simulation(
dataset=dataset,
tax_benefit_model_version=pe.uk.model,
policy=policy,
)
reform_sim.run()
Country models
UK
Three entity levels:
- Person: Individual with income and demographics
- Benunit: Benefit unit (single person or couple with children)
- Household: Residence unit
Key benefits: Universal Credit, Child Benefit, Pension Credit Key taxes: Income tax, National Insurance
US
Six entity levels:
- Person: Individual
- Tax unit: Federal tax filing unit
- SPM unit: Supplemental Poverty Measure unit
- Family: Census family definition
- Marital unit: Married couple or single person
- Household: Residence unit
Key benefits: SNAP, TANF, EITC, CTC, SSI, Social Security Key taxes: Federal income tax, payroll tax
Contributing
See CONTRIBUTING.md for development setup and guidelines.
License
AGPL-3.0
Citing
If you use this package in research, please cite it. Citation metadata is in CITATION.cff, and each release is archived on Zenodo:
Ahmadi, V., Ghenis, M., Woodruff, N., Makarchuk, P., & Volk, A. (2026). policyengine: A Microsimulation Tool for Tax-Benefit Policy Analysis (Version 5.0.4) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.22145449
Release files for policyengine 6.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| policyengine-6.1.1.tar.gz | 744.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| policyengine-6.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 988.6 kB
Release files / policyengine-6.1.1.tar.gz
| Download URL | policyengine-6.1.1.tar.gz |
|---|---|
| Size | 744.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
66fdf75f5c2e9873874fbca8b846f6499c90e33be99625914026d85d1b537a86
|
|
BLAKE2b-256 checksum How to use checksums |
7d06ac14a09281a11b101fa620596ed8b2550136d9721b0dcb6dc7084895f4bc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / policyengine-6.1.1-py3-none-any.whl
| Download URL | policyengine-6.1.1-py3-none-any.whl |
|---|---|
| Size | 244.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d5a72861d790db0eb2328e2216ed4bcbef9bac59a28fd8ddce1849278d6567d4
|
|
BLAKE2b-256 checksum How to use checksums |
9a6b0e475d4f0dec8d9b2d89cae669f91eaadc168fbf71c00d834ef9e2b172e9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|