Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 40 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,47 @@ A Python package for tax-benefit microsimulation analysis. Run policy simulation

## Quick start

### Household calculator

```python
from policyengine.core import Simulation
from policyengine.tax_benefit_models.uk import PolicyEngineUKDataset, uk_latest
from policyengine.outputs.aggregate import Aggregate, AggregateType
import policyengine as pe

# Load representative microdata
dataset = PolicyEngineUKDataset(
name="FRS 2023-24",
filepath="./data/frs_2023_24_year_2026.h5",
# 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_str": "CA"},
year=2026,
reform={"gov.irs.credits.ctc.amount.adult_dependent": 1000},
)
print(us.tax_unit.income_tax, us.household.household_net_income)
```

# Run simulation
simulation = Simulation(
dataset=dataset,
tax_benefit_model_version=uk_latest,
### Population analysis

```python
import policyengine as pe
from policyengine.core import Simulation
from policyengine.outputs.aggregate import Aggregate, AggregateType

datasets = pe.uk.ensure_datasets(
datasets=["hf://policyengine/policyengine-uk-data/enhanced_frs_2023_24.h5"],
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()

# Calculate total universal credit spending
agg = Aggregate(
simulation=simulation,
variable="universal_credit",
Expand All @@ -34,6 +55,9 @@ 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.

## Documentation

**Core concepts:**
Expand Down Expand Up @@ -179,12 +203,12 @@ dataset.load()
Simulations apply tax-benefit models to datasets:

```python
import policyengine as pe
from policyengine.core import Simulation
from policyengine.tax_benefit_models.uk import uk_latest

simulation = Simulation(
dataset=dataset,
tax_benefit_model_version=uk_latest,
tax_benefit_model_version=pe.uk.model,
)
simulation.run()

Expand Down Expand Up @@ -223,7 +247,7 @@ import datetime

parameter = Parameter(
name="gov.hmrc.income_tax.allowances.personal_allowance.amount",
tax_benefit_model_version=uk_latest,
tax_benefit_model_version=pe.uk.model,
data_type=float,
)

Expand All @@ -242,7 +266,7 @@ policy = Policy(
# Run reform simulation
reform_sim = Simulation(
dataset=dataset,
tax_benefit_model_version=uk_latest,
tax_benefit_model_version=pe.uk.model,
policy=policy,
)
reform_sim.run()
Expand Down
1 change: 1 addition & 0 deletions changelog.d/v4-docs-refresh.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Documentation refreshed for the v4 agent-first surface. README, `core-concepts`, `economic-impact-analysis`, `country-models-{uk,us}`, `regions-and-scoping`, `examples`, and `dev` now lead with `pe.uk.*` / `pe.us.*` entry points and flat-kwarg `calculate_household` usage. Removed leftover docs for the dropped `filter_field`/`filter_value` simulation fields. `examples/household_impact_example.py` rewritten against the v4 API.
81 changes: 69 additions & 12 deletions docs/core-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,55 @@

PolicyEngine.py is a Python package for tax-benefit microsimulation analysis. It provides a unified interface for running policy simulations, analysing distributional impacts, and visualising results across different countries.

## Quick start

Most analyses start from the country entry points on the top-level
package — ``policyengine.uk`` and ``policyengine.us``. They expose flat
keyword-argument functions that return structured results with
dot-access for scalar lookups.

```python
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.household.hbai_household_net_income) # net income
print(uk.person[0].income_tax) # per-person dot access

# US: married couple with two kids in Texas
us = pe.us.calculate_household(
people=[
{"age": 35, "employment_income": 40_000},
{"age": 33},
{"age": 8},
{"age": 5},
],
tax_unit={"filing_status": "JOINT"},
household={"state_code_str": "TX"},
year=2026,
)
print(us.tax_unit.income_tax, us.tax_unit.eitc, us.tax_unit.ctc)

# Apply a reform: just pass a parameter-path dict
reformed = pe.us.calculate_household(
people=[{"age": 35, "employment_income": 60_000}],
tax_unit={"filing_status": "SINGLE"},
year=2026,
reform={"gov.irs.credits.ctc.amount.adult_dependent": 1000},
)
```

Reforms can be scalar values (treated as ``{year}-01-01`` onwards) or a
mapping of effective-date strings to values for time-varying reforms.
Unknown variable names raise with suggestions instead of silently
returning zero.

For population-level analysis (budget impact, distributional effects),
see [Economic impact analysis](economic-impact-analysis.md).

## Architecture overview

The package is organised around several core concepts:
Expand All @@ -22,9 +71,14 @@ Tax-benefit models define the rules and calculations for a country's tax and ben

### Using a tax-benefit model

The country entry points expose pinned model versions as ``pe.uk.model``
and ``pe.us.model``:

```python
from policyengine.tax_benefit_models.uk import uk_latest
from policyengine.tax_benefit_models.us import us_latest
import policyengine as pe

uk_latest = pe.uk.model
us_latest = pe.us.model

# UK model includes variables like:
# - income_tax, national_insurance, universal_credit
Expand All @@ -46,7 +100,7 @@ Datasets contain microdata representing a population. Each dataset has:
### Dataset structure

```python
from policyengine.tax_benefit_models.uk import PolicyEngineUKDataset
from policyengine.tax_benefit_models.uk import PolicyEngineUKDataset # or: pe.uk.PolicyEngineUKDataset

dataset = PolicyEngineUKDataset(
name="FRS 2023-24",
Expand Down Expand Up @@ -126,7 +180,7 @@ Before running simulations, you need representative microdata. The package provi
- **`load_datasets()`**: Load previously saved HDF5 files from disk

```python
from policyengine.tax_benefit_models.us import ensure_datasets
from policyengine.tax_benefit_models.us import ensure_datasets # or: pe.us.ensure_datasets

# First run: downloads from HuggingFace, computes variables, saves to ./data/
# Subsequent runs: loads from disk instantly
Expand All @@ -139,7 +193,7 @@ dataset = datasets["enhanced_cps_2024_2026"]
```

```python
from policyengine.tax_benefit_models.uk import ensure_datasets
from policyengine.tax_benefit_models.uk import ensure_datasets # or: pe.uk.ensure_datasets

datasets = ensure_datasets(
datasets=["hf://policyengine/policyengine-uk-data/enhanced_frs_2023_24.h5"],
Expand All @@ -158,12 +212,12 @@ Simulations apply tax-benefit models to datasets, calculating all variables for
### Running a simulation

```python
import policyengine as pe
from policyengine.core import Simulation
from policyengine.tax_benefit_models.uk import uk_latest

simulation = Simulation(
dataset=dataset,
tax_benefit_model_version=uk_latest,
tax_benefit_model_version=pe.uk.model,
)
simulation.run()

Expand Down Expand Up @@ -201,7 +255,7 @@ After running a simulation, you can access the calculated variables from the out
```python
simulation = Simulation(
dataset=dataset,
tax_benefit_model_version=uk_latest,
tax_benefit_model_version=pe.uk.model,
)
simulation.run()

Expand All @@ -225,10 +279,13 @@ import datetime
# Define parameter to modify
parameter = Parameter(
name="gov.hmrc.income_tax.allowances.personal_allowance.amount",
tax_benefit_model_version=uk_latest,
tax_benefit_model_version=pe.uk.model,
description="Personal allowance for income tax",
data_type=float,
)
# Shortcut: when using `calculate_household`, reforms may be passed as a dict
# and compiled internally. This low-level construction is only needed when
# you are building a Simulation manually.

# Set new value
parameter_value = ParameterValue(
Expand All @@ -251,14 +308,14 @@ policy = Policy(
# Baseline simulation
baseline = Simulation(
dataset=dataset,
tax_benefit_model_version=uk_latest,
tax_benefit_model_version=pe.uk.model,
)
baseline.run()

# Reform simulation
reform = Simulation(
dataset=dataset,
tax_benefit_model_version=uk_latest,
tax_benefit_model_version=pe.uk.model,
policy=policy,
)
reform.run()
Expand Down Expand Up @@ -306,7 +363,7 @@ dynamic = Dynamic(

simulation = Simulation(
dataset=dataset,
tax_benefit_model_version=uk_latest,
tax_benefit_model_version=pe.uk.model,
policy=policy,
dynamic=dynamic,
)
Expand Down
48 changes: 43 additions & 5 deletions docs/country-models-uk.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,44 @@

The UK tax-benefit model implements the United Kingdom's tax and benefit system using PolicyEngine UK as the underlying calculation engine.

## Quick start

```python
import policyengine as pe

# Single adult earning £50k
result = pe.uk.calculate_household(
people=[{"age": 35, "employment_income": 50_000}],
year=2026,
)
print(result.person[0].income_tax, result.household.hbai_household_net_income)

# Family renting, with benefit claims explicitly on
result = pe.uk.calculate_household(
people=[
{"age": 35, "employment_income": 30_000},
{"age": 33},
{"age": 8},
{"age": 5},
],
benunit={"would_claim_uc": True, "would_claim_child_benefit": True},
household={"rent": 12_000, "region": "NORTH_WEST"},
year=2026,
)

# Apply a reform
result = pe.uk.calculate_household(
people=[{"age": 35, "employment_income": 50_000}],
year=2026,
reform={
"gov.hmrc.income_tax.allowances.personal_allowance.amount": 15_000,
},
)
```

For population-level analysis and reform analysis, see
[Economic impact analysis](economic-impact-analysis.md).

## Entity structure

The UK model uses three entity levels:
Expand Down Expand Up @@ -149,11 +187,11 @@ dataset = PolicyEngineUKDataset(

```python
from policyengine.core import Simulation
from policyengine.tax_benefit_models.uk import uk_latest
import policyengine as pe

simulation = Simulation(
dataset=dataset,
tax_benefit_model_version=uk_latest,
tax_benefit_model_version=pe.uk.model,
)
simulation.run()

Expand Down Expand Up @@ -203,7 +241,7 @@ import datetime

parameter = Parameter(
name="gov.hmrc.income_tax.allowances.personal_allowance.amount",
tax_benefit_model_version=uk_latest,
tax_benefit_model_version=pe.uk.model,
description="Personal allowance",
data_type=float,
)
Expand All @@ -227,7 +265,7 @@ policy = Policy(
```python
parameter = Parameter(
name="gov.dwp.universal_credit.means_test.reduction_rate",
tax_benefit_model_version=uk_latest,
tax_benefit_model_version=pe.uk.model,
description="UC taper rate",
data_type=float,
)
Expand All @@ -252,7 +290,7 @@ policy = Policy(
# Set subsequent child element equal to first child
parameter = Parameter(
name="gov.dwp.universal_credit.elements.child.subsequent_child",
tax_benefit_model_version=uk_latest,
tax_benefit_model_version=pe.uk.model,
description="UC subsequent child element",
data_type=float,
)
Expand Down
Loading
Loading