policyengine 3.2.4__tar.gz → 3.3.0__tar.gz
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-3.3.0/.github/workflows/pr_docs_changes.yaml +27 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/CHANGELOG.md +7 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/PKG-INFO +1 -1
- policyengine-3.3.0/docs/advanced-outputs.md +276 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/docs/core-concepts.md +112 -6
- {policyengine-3.2.4 → policyengine-3.3.0}/docs/country-models-uk.md +3 -5
- {policyengine-3.2.4 → policyengine-3.3.0}/docs/country-models-us.md +4 -5
- policyengine-3.3.0/docs/dev.md +101 -0
- policyengine-3.3.0/docs/economic-impact-analysis.md +287 -0
- policyengine-3.3.0/docs/examples.md +67 -0
- policyengine-3.3.0/docs/index.md +19 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/docs/myst.yml +6 -3
- policyengine-3.3.0/docs/regions-and-scoping.md +251 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/docs/visualisation.md +1 -1
- policyengine-3.3.0/examples/us_budgetary_impact.py +155 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/pyproject.toml +1 -1
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine.egg-info/PKG-INFO +1 -1
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine.egg-info/SOURCES.txt +5 -0
- policyengine-3.2.4/.github/workflows/pr_docs_changes.yaml +0 -36
- policyengine-3.2.4/docs/dev.md +0 -8
- policyengine-3.2.4/docs/index.md +0 -9
- {policyengine-3.2.4 → policyengine-3.3.0}/.claude/policyengine-guide.md +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/.claude/quick-reference.md +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/.github/CONTRIBUTING.md +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/.github/bump_version.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/.github/changelog_template.md +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/.github/check-changelog.sh +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/.github/fetch_version.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/.github/get-changelog-diff.sh +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/.github/has-functional-changes.sh +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/.github/is-version-number-acceptable.sh +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/.github/publish-git-tag.sh +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/.github/workflows/pr_code_changes.yaml +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/.github/workflows/push.yaml +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/.gitignore +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/.python-version +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/LICENSE +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/Makefile +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/README.md +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/changelog.d/.gitkeep +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/docs/.gitignore +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/examples/employment_income_variation_uk.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/examples/employment_income_variation_us.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/examples/household_impact_example.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/examples/income_bands_uk.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/examples/income_distribution_us.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/examples/policy_change_uk.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/examples/speedtest_us_simulation.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/setup.cfg +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/__init__.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/core/__init__.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/core/cache.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/core/dataset.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/core/dataset_version.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/core/dynamic.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/core/output.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/core/parameter.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/core/parameter_node.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/core/parameter_value.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/core/policy.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/core/region.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/core/scoping_strategy.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/core/simulation.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/core/tax_benefit_model.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/core/tax_benefit_model_version.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/core/variable.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/countries/__init__.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/countries/uk/__init__.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/countries/uk/regions.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/countries/us/__init__.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/countries/us/data/__init__.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/countries/us/data/districts.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/countries/us/data/places.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/countries/us/data/states.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/countries/us/regions.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/outputs/__init__.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/outputs/aggregate.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/outputs/change_aggregate.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/outputs/congressional_district_impact.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/outputs/constituency_impact.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/outputs/decile_impact.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/outputs/inequality.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/outputs/intra_decile_impact.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/outputs/local_authority_impact.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/outputs/poverty.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/tax_benefit_models/uk/__init__.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/tax_benefit_models/uk/analysis.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/tax_benefit_models/uk/datasets.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/tax_benefit_models/uk/model.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/tax_benefit_models/uk/outputs.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/tax_benefit_models/uk.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/tax_benefit_models/us/__init__.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/tax_benefit_models/us/analysis.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/tax_benefit_models/us/datasets.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/tax_benefit_models/us/model.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/tax_benefit_models/us/outputs.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/tax_benefit_models/us.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/utils/__init__.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/utils/dates.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/utils/entity_utils.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/utils/parameter_labels.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/utils/parametric_reforms.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine/utils/plotting.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine.egg-info/dependency_links.txt +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine.egg-info/requires.txt +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/src/policyengine.egg-info/top_level.txt +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/__init__.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/conftest.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/fixtures/__init__.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/fixtures/filtering_fixtures.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/fixtures/parameter_labels_fixtures.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/fixtures/parametric_reforms_fixtures.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/fixtures/poverty_by_demographics_fixtures.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/fixtures/region_fixtures.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/fixtures/us_reform_fixtures.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/fixtures/variable_label_fixtures.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_aggregate.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_cache.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_change_aggregate.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_congressional_district_impact.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_constituency_impact.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_entity_mapping.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_entity_utils.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_filtering.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_household_impact.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_inequality.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_intra_decile_impact.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_local_authority_impact.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_models.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_pandas3_compatibility.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_parameter_labels.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_parametric_reforms.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_poverty.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_poverty_by_demographics.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_poverty_run.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_region.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_scoping_strategy.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_uk_regions.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_us_reform_application.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_us_regions.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/tests/test_variable_labels.py +0 -0
- {policyengine-3.2.4 → policyengine-3.3.0}/uv.lock +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Workflow that runs on code changes to a pull request.
|
|
2
|
+
|
|
3
|
+
name: Docs changes
|
|
4
|
+
on:
|
|
5
|
+
pull_request:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
paths:
|
|
10
|
+
- docs/**
|
|
11
|
+
- .github/**
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
Test:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
name: Test documentation builds
|
|
18
|
+
steps:
|
|
19
|
+
- name: Checkout repo
|
|
20
|
+
uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
node-version: 18.x
|
|
24
|
+
- name: Install MyST
|
|
25
|
+
run: npm install -g mystmd
|
|
26
|
+
- name: Test documentation builds
|
|
27
|
+
run: cd docs && myst build --html
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [3.3.0] - 2026-03-20
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
|
|
5
|
+
- Added documentation for economic impact analysis, advanced outputs (DecileImpact, Poverty, Inequality, IntraDecileImpact), regions and scoping strategies, simulation lifecycle (ensure vs run), Dynamic class, data loading, and simulation modifiers. Added US budgetary impact example script. Fixed PR docs CI to use MyST matching production.
|
|
6
|
+
|
|
7
|
+
|
|
1
8
|
## [3.2.4] - 2026-03-17
|
|
2
9
|
|
|
3
10
|
### Changed
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# Advanced outputs
|
|
2
|
+
|
|
3
|
+
Beyond `Aggregate` and `ChangeAggregate` (covered in [Core concepts](core-concepts.md)), the package provides specialised output types for distributional analysis, poverty measurement, and inequality metrics.
|
|
4
|
+
|
|
5
|
+
All output types follow the same pattern: create an instance, call `.run()`, read the result fields. Convenience functions are provided for common use cases.
|
|
6
|
+
|
|
7
|
+
## OutputCollection
|
|
8
|
+
|
|
9
|
+
Many convenience functions return an `OutputCollection[T]`, a container holding both the individual output objects and a pandas DataFrame:
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
from policyengine.core import OutputCollection
|
|
13
|
+
|
|
14
|
+
# Returned by calculate_decile_impacts(), calculate_us_poverty_rates(), etc.
|
|
15
|
+
collection = calculate_us_poverty_rates(simulation)
|
|
16
|
+
|
|
17
|
+
# Access individual objects
|
|
18
|
+
for poverty in collection.outputs:
|
|
19
|
+
print(f"{poverty.poverty_type}: {poverty.rate:.4f}")
|
|
20
|
+
|
|
21
|
+
# Access as DataFrame
|
|
22
|
+
print(collection.dataframe)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## DecileImpact
|
|
26
|
+
|
|
27
|
+
Calculates the impact of a policy reform on a single income decile: baseline and reform mean income, absolute and relative change, and counts of people better off, worse off, and unchanged.
|
|
28
|
+
|
|
29
|
+
### Using the convenience function
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from policyengine.outputs.decile_impact import calculate_decile_impacts
|
|
33
|
+
|
|
34
|
+
decile_impacts = calculate_decile_impacts(
|
|
35
|
+
dataset=dataset,
|
|
36
|
+
tax_benefit_model_version=us_latest,
|
|
37
|
+
baseline_policy=None, # Current law
|
|
38
|
+
reform_policy=reform,
|
|
39
|
+
income_variable="household_net_income", # Default for US
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
for d in decile_impacts.outputs:
|
|
43
|
+
print(f"Decile {d.decile}: "
|
|
44
|
+
f"baseline={d.baseline_mean:,.0f}, "
|
|
45
|
+
f"reform={d.reform_mean:,.0f}, "
|
|
46
|
+
f"change={d.absolute_change:+,.0f} "
|
|
47
|
+
f"({d.relative_change:+.2f}%)")
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Using directly
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from policyengine.outputs.decile_impact import DecileImpact
|
|
54
|
+
|
|
55
|
+
impact = DecileImpact(
|
|
56
|
+
baseline_simulation=baseline_sim,
|
|
57
|
+
reform_simulation=reform_sim,
|
|
58
|
+
income_variable="household_net_income",
|
|
59
|
+
decile=5, # 5th decile
|
|
60
|
+
)
|
|
61
|
+
impact.run()
|
|
62
|
+
|
|
63
|
+
print(f"Count better off: {impact.count_better_off:,.0f}")
|
|
64
|
+
print(f"Count worse off: {impact.count_worse_off:,.0f}")
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Parameters
|
|
68
|
+
|
|
69
|
+
| Parameter | Default | Description |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `income_variable` | `equiv_hbai_household_net_income` | Income variable to group by and measure changes |
|
|
72
|
+
| `decile_variable` | `None` | Use a pre-computed grouping variable instead of `qcut` |
|
|
73
|
+
| `entity` | Auto-detected | Entity level for the income variable |
|
|
74
|
+
| `quantiles` | `10` | Number of quantile groups (10 = deciles, 5 = quintiles) |
|
|
75
|
+
|
|
76
|
+
For US simulations, use `income_variable="household_net_income"`. The UK default (`equiv_hbai_household_net_income`) is the equivalised HBAI measure.
|
|
77
|
+
|
|
78
|
+
## IntraDecileImpact
|
|
79
|
+
|
|
80
|
+
Classifies people within each decile into five income change categories:
|
|
81
|
+
|
|
82
|
+
| Category | Threshold |
|
|
83
|
+
|---|---|
|
|
84
|
+
| Lose more than 5% | change <= -5% |
|
|
85
|
+
| Lose less than 5% | -5% < change <= -0.1% |
|
|
86
|
+
| No change | -0.1% < change <= 0.1% |
|
|
87
|
+
| Gain less than 5% | 0.1% < change <= 5% |
|
|
88
|
+
| Gain more than 5% | change > 5% |
|
|
89
|
+
|
|
90
|
+
Proportions are people-weighted (using `household_count_people * household_weight`).
|
|
91
|
+
|
|
92
|
+
### Using the convenience function
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from policyengine.outputs.intra_decile_impact import compute_intra_decile_impacts
|
|
96
|
+
|
|
97
|
+
intra = compute_intra_decile_impacts(
|
|
98
|
+
baseline_simulation=baseline_sim,
|
|
99
|
+
reform_simulation=reform_sim,
|
|
100
|
+
income_variable="household_net_income",
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
for row in intra.outputs:
|
|
104
|
+
if row.decile == 0:
|
|
105
|
+
label = "Overall"
|
|
106
|
+
else:
|
|
107
|
+
label = f"Decile {row.decile}"
|
|
108
|
+
print(f"{label}: "
|
|
109
|
+
f"lose>5%={row.lose_more_than_5pct:.2%}, "
|
|
110
|
+
f"lose<5%={row.lose_less_than_5pct:.2%}, "
|
|
111
|
+
f"no change={row.no_change:.2%}, "
|
|
112
|
+
f"gain<5%={row.gain_less_than_5pct:.2%}, "
|
|
113
|
+
f"gain>5%={row.gain_more_than_5pct:.2%}")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The function returns deciles 1-10 plus an overall average at `decile=0`.
|
|
117
|
+
|
|
118
|
+
## Poverty
|
|
119
|
+
|
|
120
|
+
Calculates poverty headcount and rates for a single simulation, with optional demographic filtering.
|
|
121
|
+
|
|
122
|
+
### Poverty types
|
|
123
|
+
|
|
124
|
+
**UK** (4 measures):
|
|
125
|
+
- Absolute before housing costs (BHC)
|
|
126
|
+
- Absolute after housing costs (AHC)
|
|
127
|
+
- Relative before housing costs (BHC)
|
|
128
|
+
- Relative after housing costs (AHC)
|
|
129
|
+
|
|
130
|
+
**US** (2 measures):
|
|
131
|
+
- SPM poverty
|
|
132
|
+
- Deep SPM poverty (below 50% of SPM threshold)
|
|
133
|
+
|
|
134
|
+
### Calculating all poverty rates
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from policyengine.outputs.poverty import (
|
|
138
|
+
calculate_uk_poverty_rates,
|
|
139
|
+
calculate_us_poverty_rates,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# US
|
|
143
|
+
us_poverty = calculate_us_poverty_rates(simulation)
|
|
144
|
+
for p in us_poverty.outputs:
|
|
145
|
+
print(f"{p.poverty_type}: headcount={p.headcount:,.0f}, rate={p.rate:.4f}")
|
|
146
|
+
|
|
147
|
+
# UK
|
|
148
|
+
uk_poverty = calculate_uk_poverty_rates(simulation)
|
|
149
|
+
for p in uk_poverty.outputs:
|
|
150
|
+
print(f"{p.poverty_type}: headcount={p.headcount:,.0f}, rate={p.rate:.4f}")
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Poverty by demographic group
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
from policyengine.outputs.poverty import (
|
|
157
|
+
calculate_us_poverty_by_age,
|
|
158
|
+
calculate_us_poverty_by_gender,
|
|
159
|
+
calculate_us_poverty_by_race,
|
|
160
|
+
calculate_uk_poverty_by_age,
|
|
161
|
+
calculate_uk_poverty_by_gender,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# By age group (child <18, adult 18-64, senior 65+)
|
|
165
|
+
by_age = calculate_us_poverty_by_age(simulation)
|
|
166
|
+
for p in by_age.outputs:
|
|
167
|
+
print(f"{p.filter_group} {p.poverty_type}: {p.rate:.4f}")
|
|
168
|
+
|
|
169
|
+
# By gender
|
|
170
|
+
by_gender = calculate_us_poverty_by_gender(simulation)
|
|
171
|
+
|
|
172
|
+
# By race (US only: WHITE, BLACK, HISPANIC, OTHER)
|
|
173
|
+
by_race = calculate_us_poverty_by_race(simulation)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Custom filters
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
from policyengine.outputs.poverty import Poverty
|
|
180
|
+
|
|
181
|
+
# Child poverty only
|
|
182
|
+
child_poverty = Poverty(
|
|
183
|
+
simulation=simulation,
|
|
184
|
+
poverty_variable="spm_unit_is_in_spm_poverty",
|
|
185
|
+
entity="person",
|
|
186
|
+
filter_variable="age",
|
|
187
|
+
filter_variable_leq=17,
|
|
188
|
+
)
|
|
189
|
+
child_poverty.run()
|
|
190
|
+
print(f"Child SPM poverty rate: {child_poverty.rate:.4f}")
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Result fields
|
|
194
|
+
|
|
195
|
+
| Field | Description |
|
|
196
|
+
|---|---|
|
|
197
|
+
| `headcount` | Weighted count of people in poverty |
|
|
198
|
+
| `total_population` | Weighted total population (after filters) |
|
|
199
|
+
| `rate` | `headcount / total_population` |
|
|
200
|
+
| `filter_group` | Group label set by demographic convenience functions |
|
|
201
|
+
|
|
202
|
+
## Inequality
|
|
203
|
+
|
|
204
|
+
Calculates weighted inequality metrics for a single simulation: Gini coefficient and income share measures.
|
|
205
|
+
|
|
206
|
+
### Using convenience functions
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
from policyengine.outputs.inequality import (
|
|
210
|
+
calculate_uk_inequality,
|
|
211
|
+
calculate_us_inequality,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# US (uses household_net_income by default)
|
|
215
|
+
ineq = calculate_us_inequality(simulation)
|
|
216
|
+
print(f"Gini: {ineq.gini:.4f}")
|
|
217
|
+
print(f"Top 10% share: {ineq.top_10_share:.4f}")
|
|
218
|
+
print(f"Top 1% share: {ineq.top_1_share:.4f}")
|
|
219
|
+
print(f"Bottom 50% share: {ineq.bottom_50_share:.4f}")
|
|
220
|
+
|
|
221
|
+
# UK (uses equiv_hbai_household_net_income by default)
|
|
222
|
+
ineq = calculate_uk_inequality(simulation)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### With demographic filters
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
# Inequality among working-age adults only
|
|
229
|
+
ineq = calculate_us_inequality(
|
|
230
|
+
simulation,
|
|
231
|
+
filter_variable="age",
|
|
232
|
+
filter_variable_geq=18,
|
|
233
|
+
filter_variable_leq=64,
|
|
234
|
+
)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Using directly
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
from policyengine.outputs.inequality import Inequality
|
|
241
|
+
|
|
242
|
+
ineq = Inequality(
|
|
243
|
+
simulation=simulation,
|
|
244
|
+
income_variable="household_net_income",
|
|
245
|
+
entity="household",
|
|
246
|
+
)
|
|
247
|
+
ineq.run()
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Result fields
|
|
251
|
+
|
|
252
|
+
| Field | Description |
|
|
253
|
+
|---|---|
|
|
254
|
+
| `gini` | Weighted Gini coefficient (0 = perfect equality, 1 = perfect inequality) |
|
|
255
|
+
| `top_10_share` | Share of total income held by top 10% |
|
|
256
|
+
| `top_1_share` | Share of total income held by top 1% |
|
|
257
|
+
| `bottom_50_share` | Share of total income held by bottom 50% |
|
|
258
|
+
|
|
259
|
+
## Comparing baseline and reform
|
|
260
|
+
|
|
261
|
+
Poverty and inequality are single-simulation outputs. To compare baseline and reform, compute both and take the difference:
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
baseline_poverty = calculate_us_poverty_rates(baseline_sim)
|
|
265
|
+
reform_poverty = calculate_us_poverty_rates(reform_sim)
|
|
266
|
+
|
|
267
|
+
for bp, rp in zip(baseline_poverty.outputs, reform_poverty.outputs):
|
|
268
|
+
change = rp.rate - bp.rate
|
|
269
|
+
print(f"{bp.poverty_type}: {bp.rate:.4f} -> {rp.rate:.4f} ({change:+.4f})")
|
|
270
|
+
|
|
271
|
+
baseline_ineq = calculate_us_inequality(baseline_sim)
|
|
272
|
+
reform_ineq = calculate_us_inequality(reform_sim)
|
|
273
|
+
print(f"Gini change: {reform_ineq.gini - baseline_ineq.gini:+.4f}")
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
The `economic_impact_analysis()` function does this automatically and returns both baseline and reform poverty/inequality in the `PolicyReformAnalysis` result. See [Economic impact analysis](economic-impact-analysis.md).
|
|
@@ -117,6 +117,40 @@ dataset = PolicyEngineUKDataset(
|
|
|
117
117
|
)
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
+
## Data loading
|
|
121
|
+
|
|
122
|
+
Before running simulations, you need representative microdata. The package provides three functions for managing datasets:
|
|
123
|
+
|
|
124
|
+
- **`ensure_datasets()`**: Load from disk if available, otherwise download and compute (recommended)
|
|
125
|
+
- **`create_datasets()`**: Always download from HuggingFace and compute from scratch
|
|
126
|
+
- **`load_datasets()`**: Load previously saved HDF5 files from disk
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from policyengine.tax_benefit_models.us import ensure_datasets
|
|
130
|
+
|
|
131
|
+
# First run: downloads from HuggingFace, computes variables, saves to ./data/
|
|
132
|
+
# Subsequent runs: loads from disk instantly
|
|
133
|
+
datasets = ensure_datasets(
|
|
134
|
+
datasets=["hf://policyengine/policyengine-us-data/enhanced_cps_2024.h5"],
|
|
135
|
+
years=[2026],
|
|
136
|
+
data_folder="./data",
|
|
137
|
+
)
|
|
138
|
+
dataset = datasets["enhanced_cps_2024_2026"]
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from policyengine.tax_benefit_models.uk import ensure_datasets
|
|
143
|
+
|
|
144
|
+
datasets = ensure_datasets(
|
|
145
|
+
datasets=["hf://policyengine/policyengine-uk-data/enhanced_frs_2023_24.h5"],
|
|
146
|
+
years=[2026],
|
|
147
|
+
data_folder="./data",
|
|
148
|
+
)
|
|
149
|
+
dataset = datasets["enhanced_frs_2023_24_2026"]
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
All datasets are stored as HDF5 files on disk. No database server is required.
|
|
153
|
+
|
|
120
154
|
## Simulations
|
|
121
155
|
|
|
122
156
|
Simulations apply tax-benefit models to datasets, calculating all variables for the specified year.
|
|
@@ -141,6 +175,25 @@ output_household = simulation.output_dataset.data.household
|
|
|
141
175
|
print(output_household[["household_id", "household_net_income", "household_tax"]])
|
|
142
176
|
```
|
|
143
177
|
|
|
178
|
+
### Simulation lifecycle: `run()` vs `ensure()`
|
|
179
|
+
|
|
180
|
+
The `Simulation` class provides two methods for computing results:
|
|
181
|
+
|
|
182
|
+
| Method | Behaviour |
|
|
183
|
+
|---|---|
|
|
184
|
+
| `simulation.run()` | Always recomputes from scratch. No caching. |
|
|
185
|
+
| `simulation.ensure()` | Checks in-memory LRU cache, then tries loading from disk, then falls back to `run()` + `save()`. |
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
# One-off computation (no caching)
|
|
189
|
+
simulation.run()
|
|
190
|
+
|
|
191
|
+
# Cache-or-compute (preferred for production use)
|
|
192
|
+
simulation.ensure()
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
`ensure()` uses a module-level LRU cache (max 100 simulations) and saves output datasets as HDF5 files alongside the input dataset. On repeated calls, it returns cached results instantly. For baseline-vs-reform comparisons, `economic_impact_analysis()` calls `ensure()` internally, so you rarely need to call it yourself.
|
|
196
|
+
|
|
144
197
|
### Accessing calculated variables
|
|
145
198
|
|
|
146
199
|
After running a simulation, you can access the calculated variables from the output dataset:
|
|
@@ -211,6 +264,56 @@ reform = Simulation(
|
|
|
211
264
|
reform.run()
|
|
212
265
|
```
|
|
213
266
|
|
|
267
|
+
### Combining policies
|
|
268
|
+
|
|
269
|
+
Policies can be combined using the `+` operator:
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
combined = policy_a + policy_b
|
|
273
|
+
# Concatenates parameter_values and chains simulation_modifiers
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Simulation modifiers
|
|
277
|
+
|
|
278
|
+
For reforms that cannot be expressed as parameter value changes, `Policy` accepts a `simulation_modifier` callable that directly manipulates the underlying `policyengine_core` simulation:
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
def my_modifier(sim):
|
|
282
|
+
"""Custom reform logic applied to the core simulation object."""
|
|
283
|
+
p = sim.tax_benefit_system.parameters
|
|
284
|
+
# Modify parameters programmatically
|
|
285
|
+
return sim
|
|
286
|
+
|
|
287
|
+
policy = Policy(
|
|
288
|
+
name="Custom reform",
|
|
289
|
+
simulation_modifier=my_modifier,
|
|
290
|
+
)
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Note: the UK model supports `simulation_modifier`. The US model currently only uses the `parameter_values` path.
|
|
294
|
+
|
|
295
|
+
## Dynamic behavioural responses
|
|
296
|
+
|
|
297
|
+
The `Dynamic` class is structurally identical to `Policy` and represents behavioural responses to policy changes (e.g., labour supply elasticities). It is applied after the policy in the simulation pipeline.
|
|
298
|
+
|
|
299
|
+
```python
|
|
300
|
+
from policyengine.core.dynamic import Dynamic
|
|
301
|
+
|
|
302
|
+
dynamic = Dynamic(
|
|
303
|
+
name="Labour supply response",
|
|
304
|
+
parameter_values=[...], # Same format as Policy
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
simulation = Simulation(
|
|
308
|
+
dataset=dataset,
|
|
309
|
+
tax_benefit_model_version=uk_latest,
|
|
310
|
+
policy=policy,
|
|
311
|
+
dynamic=dynamic,
|
|
312
|
+
)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Dynamic responses can also be combined using the `+` operator and support `simulation_modifier` callables.
|
|
316
|
+
|
|
214
317
|
## Outputs
|
|
215
318
|
|
|
216
319
|
Output classes provide structured analysis of simulation results.
|
|
@@ -480,7 +583,7 @@ COLORS = {
|
|
|
480
583
|
|
|
481
584
|
### 1. Analyse employment income variation
|
|
482
585
|
|
|
483
|
-
See
|
|
586
|
+
See [UK employment income variation](examples.md#uk-employment-income-variation) for a complete example of:
|
|
484
587
|
- Creating custom datasets with varied parameters
|
|
485
588
|
- Running single simulations
|
|
486
589
|
- Extracting results with filters
|
|
@@ -488,7 +591,7 @@ See `examples/employment_income_variation_uk.py` for a complete example of:
|
|
|
488
591
|
|
|
489
592
|
### 2. Policy reform analysis
|
|
490
593
|
|
|
491
|
-
See
|
|
594
|
+
See [UK policy reform analysis](examples.md#uk-policy-reform-analysis) for:
|
|
492
595
|
- Applying parametric reforms
|
|
493
596
|
- Comparing baseline and reform
|
|
494
597
|
- Analysing winners/losers by decile
|
|
@@ -496,7 +599,7 @@ See `examples/policy_change_uk.py` for:
|
|
|
496
599
|
|
|
497
600
|
### 3. Distributional analysis
|
|
498
601
|
|
|
499
|
-
See
|
|
602
|
+
See [US income distribution](examples.md#us-income-distribution) for:
|
|
500
603
|
- Loading representative microdata
|
|
501
604
|
- Calculating statistics by income decile
|
|
502
605
|
- Mapping variables across entity levels
|
|
@@ -549,8 +652,11 @@ See `examples/income_distribution_us.py` for:
|
|
|
549
652
|
|
|
550
653
|
## Next steps
|
|
551
654
|
|
|
552
|
-
-
|
|
553
|
-
-
|
|
655
|
+
- [Economic impact analysis](economic-impact-analysis.md): Full baseline-vs-reform comparison workflow
|
|
656
|
+
- [Advanced outputs](advanced-outputs.md): DecileImpact, Poverty, Inequality, IntraDecileImpact
|
|
657
|
+
- [Regions and scoping](regions-and-scoping.md): Sub-national analysis (states, constituencies, districts)
|
|
658
|
+
- Country-specific documentation:
|
|
554
659
|
- [UK tax-benefit model](country-models-uk.md)
|
|
555
660
|
- [US tax-benefit model](country-models-us.md)
|
|
556
|
-
-
|
|
661
|
+
- [Visualisation](visualisation.md): Publication-ready charts
|
|
662
|
+
- [Examples](examples.md): Complete working scripts
|
|
@@ -363,11 +363,9 @@ When creating custom datasets, validate:
|
|
|
363
363
|
|
|
364
364
|
## Examples
|
|
365
365
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
-
|
|
369
|
-
- `policy_change_uk.py`: Apply reforms, analyse winners/losers
|
|
370
|
-
- `income_bands_uk.py`: Create income band scenarios
|
|
366
|
+
- [UK employment income variation](examples.md#uk-employment-income-variation): Vary employment income, analyse benefit phase-outs
|
|
367
|
+
- [UK policy reform analysis](examples.md#uk-policy-reform-analysis): Apply reforms, analyse winners/losers
|
|
368
|
+
- [UK income bands](examples.md#uk-income-bands): Calculate net income and tax by income decile
|
|
371
369
|
|
|
372
370
|
## References
|
|
373
371
|
|
|
@@ -431,11 +431,10 @@ When creating custom datasets, validate:
|
|
|
431
431
|
|
|
432
432
|
## Examples
|
|
433
433
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
-
|
|
437
|
-
-
|
|
438
|
-
- `speedtest_us_simulation.py`: Performance benchmarking
|
|
434
|
+
- [US income distribution](examples.md#us-income-distribution): Analyse benefit distribution by income decile
|
|
435
|
+
- [US employment income variation](examples.md#us-employment-income-variation): Vary employment income, analyse phase-outs
|
|
436
|
+
- [US budgetary impact](examples.md#us-budgetary-impact): Full baseline-vs-reform comparison
|
|
437
|
+
- [Simulation performance](examples.md#simulation-performance): Performance benchmarking
|
|
439
438
|
|
|
440
439
|
## References
|
|
441
440
|
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Development
|
|
2
|
+
|
|
3
|
+
## Principles
|
|
4
|
+
|
|
5
|
+
1. **STRONG** preference for simplicity. Let's make this package as simple as it possibly can be.
|
|
6
|
+
2. Remember the goal of this package: to make it easy to create, run, save and analyse PolicyEngine simulations. When considering further features, always ask: can we instead *make it super easy* for people to do this outside the package?
|
|
7
|
+
3. Be consistent about property names. `name` = human readable few words you could put as the noun in a sentence without fail. `id` = unique identifier, ideally a UUID. `description` = longer human readable text that describes the object. `created_at` and `updated_at` = timestamps for when the object was created and last updated.
|
|
8
|
+
4. Constraints can be good. We should set constraints where they help us simplify the codebase and usage, but not where they unnecessarily block useful functionality.
|
|
9
|
+
|
|
10
|
+
## Setup
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
git clone https://github.com/PolicyEngine/policyengine.py.git
|
|
14
|
+
cd policyengine.py
|
|
15
|
+
uv pip install -e .[dev]
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This installs both UK and US country models plus dev dependencies (pytest, ruff, mypy, towncrier).
|
|
19
|
+
|
|
20
|
+
## Common commands
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
make format # ruff format
|
|
24
|
+
make test # pytest with coverage
|
|
25
|
+
make docs # build documentation site
|
|
26
|
+
make clean # remove caches, build artifacts, .h5 files
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Testing
|
|
30
|
+
|
|
31
|
+
Tests require a `HUGGING_FACE_TOKEN` environment variable for downloading datasets:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
export HUGGING_FACE_TOKEN=hf_...
|
|
35
|
+
make test
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
To run a specific test:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pytest tests/test_models.py -v
|
|
42
|
+
pytest tests/test_parametric_reforms.py -k "test_uk" -v
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Linting and formatting
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
ruff format . # format code
|
|
49
|
+
ruff check . # lint
|
|
50
|
+
mypy src/policyengine # type check (informational)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## CI pipeline
|
|
54
|
+
|
|
55
|
+
PRs trigger the following checks:
|
|
56
|
+
|
|
57
|
+
| Check | Status | Command |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| Lint + format | Required | `ruff check .` + `ruff format --check .` |
|
|
60
|
+
| Tests (Python 3.13) | Required | `make test` |
|
|
61
|
+
| Tests (Python 3.14) | Required | `make test` |
|
|
62
|
+
| Mypy | Informational | `mypy src/policyengine` |
|
|
63
|
+
| Docs build | Required | MyST build |
|
|
64
|
+
|
|
65
|
+
## Versioning and releases
|
|
66
|
+
|
|
67
|
+
This project uses [towncrier](https://towncrier.readthedocs.io/) for changelog management. When making a PR, add a changelog fragment:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Fragment types: breaking, added, changed, fixed, removed
|
|
71
|
+
echo "Description of change" > changelog.d/my-change.added
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
On merge, the versioning workflow bumps the version, builds the changelog, and creates a GitHub Release.
|
|
75
|
+
|
|
76
|
+
## Architecture
|
|
77
|
+
|
|
78
|
+
### Package layout
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
src/policyengine/
|
|
82
|
+
├── core/ # Domain models (Simulation, Dataset, Policy, etc.)
|
|
83
|
+
├── tax_benefit_models/
|
|
84
|
+
│ ├── uk/ # UK model, datasets, analysis, outputs
|
|
85
|
+
│ └── us/ # US model, datasets, analysis, outputs
|
|
86
|
+
├── outputs/ # Output templates (Aggregate, Poverty, etc.)
|
|
87
|
+
├── countries/ # Geographic region registries
|
|
88
|
+
└── utils/ # Helpers (reforms, entity mapping, plotting)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Key design decisions
|
|
92
|
+
|
|
93
|
+
**Pydantic everywhere**: All domain objects are Pydantic `BaseModel` subclasses. This gives us validation, serialisation, and clear field documentation.
|
|
94
|
+
|
|
95
|
+
**HDF5 for storage**: Datasets and simulation outputs are stored as HDF5 files. No database server is required. The `MicroDataFrame` from the `microdf` package wraps pandas DataFrames with weight-aware `.sum()`, `.mean()`, `.count()`.
|
|
96
|
+
|
|
97
|
+
**Country-specific model classes**: `PolicyEngineUSLatest` and `PolicyEngineUKLatest` each implement `run()`, `save()`, and `load()`. The US model passes reforms as a dict at `Microsimulation(reform=...)` construction time. The UK model supports both parametric reforms and `simulation_modifier` callables applied post-construction.
|
|
98
|
+
|
|
99
|
+
**LRU cache + file caching**: `Simulation.ensure()` checks an in-process LRU cache (max 100 entries), then tries loading from disk, then falls back to `run()` + `save()`.
|
|
100
|
+
|
|
101
|
+
**Output pattern**: All output types inherit from `Output`, implement `.run()`, and populate result fields. Convenience functions (e.g., `calculate_us_poverty_rates()`) create, run, and return collections of output objects.
|