- Migrated TRACE TRO emission from the prerelease TROv namespace
https://w3id.org/trace/2023/05/trov#to the canonicalhttps://w3id.org/trace/trov/0.1#. This aligns policyengine.py with policyengine-us-data (which already uses the canonical namespace per us-data PR #746), so both sides can share SHACL validators and TROs emitted by either side validate against the same vocabulary. Fixes #313.
- Added
policyengine.provenance.refresh_release_bundle. Given a country and optional new model/data versions, the helper fetches the updated PyPI wheel metadata + HF dataset sha256, rewritesdata/release_manifests/{country}.jsonand the matching extra pin inpyproject.toml, and (optionally) regenerates the bundle's TRACE TRO sidecar. A thinscripts/refresh_release_bundle.pywrapper exposes the library function as a CLI for release engineers. Unit-tested offline via mocked PyPI/HF responses.
- Rewrite docs content for the v4 API: separate pages per task (households, reforms, microsim, outputs, impact analysis, regions), updated code samples against real output classes and
Simulationdict reforms.
- Added
policyengine.graph— a static-analysis-based variable dependency graph for PolicyEngine source trees.extract_from_path(path)walks a directory of Variable subclasses, parses formula-method bodies forentity("<var>", period)andadd(entity, period, [list])references, and returns aVariableGraph. Queries includedeps(var)(direct dependencies),impact(var)(transitive downstream), andpath(src, dst)(shortest dependency chain). No runtime dependency on country models — indexespolicyengine-us(4,577 variables) in under a second.
- Fixed
Simulation.extra_variablesbeing silently ignored on the US and UK microsim paths. The field is declared on the baseSimulationand honored by the household calculator, but the countryrun()methods previously only iteratedself.entity_variables— extras passed viaSimulation(extra_variables={...})never reached the output dataset. Both country paths now route through a sharedMicrosimulationModelVersion.resolve_entity_variableshelper that merges defaults with extras, dedupes overlapping entries, and validates entity keys + variable names with close-match suggestions. Closes #303. - Fixed structural-reform propagation in the US microsim path.
Simulation(policy={"gov.contrib.ctc.*": ...})previously crashed at.ensure()withAttributeError: 'NoneType' object has no attribute 'entity'because_build_simulation_from_datasetinstantiated entities against the module-levelpolicyengine_us.system(no reform applied) instead ofmicrosim.tax_benefit_system(reform applied). Published external reforms that activate all threegov.contrib.ctc.*.in_effectgates (e.g., app.policyengine.org policy 94589) now run end-to-end throughpe.us.economic_impact_analysis.
-
Simulation(policy={...})andSimulation(dynamic={...})now accept the same flat{"param.path": value}/{"param.path": {date: value}}dict thatpe.{uk,us}.calculate_household(reform=...)accepts. Dicts are compiled to fullPolicy/Dynamicobjects on construction using thetax_benefit_model_versionfor parameter-path validation anddataset.yearfor scalar effective-date defaulting. Removes the last place where population microsim required buildingParameter/ParameterValueby hand. -
BREAKING (v4): Collapse the household-calculator surface into a single agent-friendly entry point,
pe.us.calculate_household/pe.uk.calculate_household.New public API:
policyengine/__init__.pypopulated with canonical accessors:pe.us,pe.uk,pe.Simulation(replacing the empty top-level module).import policyengine as penow gives you everything a new coding session needs to reach in one line.pe.us.calculate_household(**kwargs)andpe.uk.calculate_householdtake flat keyword arguments (people, per-entity overrides,year,reform,extra_variables) instead of a pydantic input wrapper.reform=accepts a plain dict:{parameter_path: value}or{parameter_path: {effective_date: value}}. Compiles internally.- Returns :class:
HouseholdResult(new) with dot-access:result.tax_unit.income_tax,result.household.household_net_income,result.person[0].age. Singleton entities are :class:EntityResult;personis a list of them.to_dict()andwrite(path)serialize to JSON. extra_variables=[...]is now a flat list; the library dispatches each name to its entity by looking it up on the model.- Unknown variable names (in
people, entity overrides, orextra_variables) raiseValueErrorwith adifflibclose-match suggestion and a paste-able fix hint. - Unknown dot-access on a result raises
AttributeErrorwith the list of available variables plus theextra_variables=[...]call that would surface the requested one.
Removed (v4 breaking):
USHouseholdInput/UKHouseholdInput/USHouseholdOutput/UKHouseholdOutputpydantic wrappers.calculate_household_impact— the name was misleading (it returned levels, not an impact vs. baseline). Reserved for a future delta function.- The bare
us_model/uk_modellabel-only singletons; each country module now exposes.modelpointing at the realTaxBenefitModelVersion(keptus_latest/uk_latestaliases for compatibility with any in-flight downstream code).
New internal module:
policyengine.tax_benefit_models.common—compile_reform,dispatch_extra_variables,EntityResult,HouseholdResultshared by both country implementations.
-
Extracted shared
MicrosimulationModelVersionbase class inpolicyengine.tax_benefit_models.common. Country subclasses now declare class-level metadata (country_code,package_name,group_entities) and implement a handful of thin hooks;run()stays per-country. Byte-level snapshot tests verify zero output drift. -
Documentation refreshed for the v4 agent-first surface. README,
core-concepts,economic-impact-analysis,country-models-{uk,us},regions-and-scoping,examples, anddevnow lead withpe.uk.*/pe.us.*entry points and flat-kwargcalculate_householdusage. Removed leftover docs for the droppedfilter_field/filter_valuesimulation fields.examples/household_impact_example.pyrewritten against the v4 API. -
BREAKING (v4): Separate the provenance layer from the core value-object layer.
policyengine/core/release_manifest.py→policyengine/provenance/manifest.pypolicyengine/core/trace_tro.py→policyengine/provenance/trace.py- New
policyengine.provenancepackage re-exports the public surface (get_release_manifest,get_data_release_manifest,build_trace_tro_from_release_bundle,build_simulation_trace_tro,serialize_trace_tro,canonical_json_bytes,compute_trace_composition_fingerprint, etc.). policyengine.coreno longer re-exports provenance types.policyengine.coreshrinks to value objects only (Dataset, Variable, Parameter, Policy, Dynamic, Simulation, Region, TaxBenefitModel, TaxBenefitModelVersion, scoping strategies).import policyengine.core.scoping_strategyno longer importsh5pyat module load; the weight-replacement code path lazy-imports it.import policyengine.outputs.constituency_impactandimport policyengine.outputs.local_authority_impactdo the same.- Migration for downstream: replace
from policyengine.core import DataReleaseManifest(et al.) withfrom policyengine.provenance import DataReleaseManifest. The country-module imports in internal code (tax_benefit_models/{us,uk}/model.pyanddatasets.py) are already updated.
-
Pre-launch cleanup — remove dead code and drop
plotlyfrom the core dependency set:- Delete
policyengine.tax_benefit_models.usandpolicyengine.tax_benefit_models.ukmodule shims. Python resolves the package directory first, so the.pyshims were always shadowed; worse, both attempted to re-exportgeneral_policy_reform_analysiswhich is not defined anywhere, makingfrom policyengine.tax_benefit_models.us import general_policy_reform_analysisraiseImportErrorat runtime. - Delete
_create_entity_output_modelplus thePersonOutput/BenunitOutput/HouseholdEntityOutputfactory products inpolicyengine.tax_benefit_models.uk.analysis— built viapydantic.create_modelbut never referenced anywhere in the codebase. - Delete
policyengine.core.DatasetVersion(only consumer was anOptionalfield onDatasetthat was never set, and thepolicyengine.corere-export). - Move
plotly>=5.0.0from the base install to a newpolicyengine[plotting]extra. Onlypolicyengine.utils.plottinguses it, and that module is itself only used by theexamples/scripts. The package now imports cleanly withoutplotly.
- Delete
-
BREAKING (v4): Remove the legacy
filter_field/filter_valuefields fromSimulationandRegion, the_auto_construct_strategymodel validator that rewrote them into aRowFilterStrategy, and the_filter_dataset_by_household_variablemethods they fed on both country models. All scoping now flows throughscoping_strategy: Optional[ScopingStrategy].Region.requires_filterbecomes a derived property (Trueiffscoping_strategy is not None). The sub-national region factories (countries/us/regions.py,countries/uk/regions.py) constructscoping_strategy=RowFilterStrategy(...)/WeightReplacementStrategy(...)directly. Callers that previously passedfilter_field="place_fips", filter_value="44000"now passscoping_strategy=RowFilterStrategy(variable_name="place_fips", variable_value="44000").
certify_data_release_compatibilitynow accepts full PEP 440 version specifiers (>=1.637.0,<2.0.0,~=1.637, etc.) in a data release manifest'scompatible_model_packages, not only==X.Y.Z. This lets the US data package declare a range of compatiblepolicyengine-usversions when thedata_build_fingerprintis known to be stable across them, avoiding the need to regenerate the dataset for every model patch release. Addspackaging>=23.0as a direct dependency.- TRACE TRO hardening: bundle TROs now hash the country model wheel (read from
PackageVersion.sha256when present, otherwise fetched from PyPI), use HTTPS artifact locations, carry structuredpe:*certification fields and GitHub Actions attestation metadata, and are validated in CI against a shipped JSON Schema. Adds apolicyengine trace-troCLI, per-simulation TROs throughpolicyengine.results.build_results_trace_tro/write_results_with_trace_tro, and restores theTaxBenefitModelVersion.trace_troproperty andpolicyengine.corere-exports that were dropped in #276.
- TRACE TRO emission now conforms to the public TROv 2023/05 vocabulary:
switched namespace to
https://w3id.org/trace/2023/05/trov#, flattenedtrov:hashnodes to the nativetrov:sha256property, renamedtrov:path→trov:hasLocationand the inverse pointer on ArtifactLocation totrov:hasArtifact, correctedTrustedResearchSystem→TransparentResearchSystemandTrustedResearchPerformance→TransparentResearchPerformance, and replaced the locally-inventedArrangementBindingchain withtrov:accessedArrangementas used by the published trov-demos. Every TRO now carriespe:emittedIn("local"or"github-actions") so a verifier can distinguish a CI-emitted TRO from a laptop rebuild. Per-simulation TROs accept abundle_tro_urlthat is recorded aspe:bundleTroUrl, letting a verifier independently fetch and re-hash the bundle to detect a forged reference. The composition fingerprint now joins hashes with\nto prevent hex-length concatenation collisions. Addspolicyengine trace-tro-validateCLI, removes the broken--offlineflag, wiresscripts/generate_trace_tros.pyinto theVersioningCI job so bundled TROs ship with every release, inlines the real model wheel sha256 onus.json/uk.json, and cleans up the deadDataReleaseArtifact.https_uri/_data_release_manifest_urlhelpers. - Bump the bundled US release manifest to
policyengine-us==1.653.3(from 1.647.0) to unblock downstream projects that want to pin the latest working model version throughpolicyengine.py. The dataset stays atpolicyengine-us-data==1.73.0(the latest US data release tagged on Hugging Face); certification is nowmatching_data_build_fingerprintwithbuilt_with_model_versionrecording the 1.647.0 that actually produced the data. Both bundled manifests (us.json,uk.json) updatepolicyengine_versionandbundle_idto 3.5.0 to match the current package version.
- Support Python 3.9–3.12 (in addition to 3.13–3.14). PEP 604
X | Yannotations (evaluated at runtime by pydantic) are rewritten asOptional[X]/Union[X, Y];StrEnum(3.11+) is replaced withclass Foo(str, Enum); PEP 695 generic class syntax incore/cache.pyandcore/output.pyis rewritten usingtyping.TypeVar+typing.Generic. Ruff and mypy target versions dropped to py39. Requirespolicyengine-us==1.602.0+andpolicyengine-uk==2.74.0+from the[us]/[uk]/[dev]extras to also support 3.9/3.10.
- Point CONTRIBUTING.md at the shared PolicyEngine contribution guide (https://github.com/PolicyEngine/.github) and trim the per-repo file to commands, repo-specific conventions, and anti-patterns. Removes the stale
changelog_entry.yaml/make changeloginstructions. - Change the installed-vs-manifest country-model version check from a hard
ValueErrorto aUserWarning. Calculations now run against whatever country-model version is installed; downstream code that cares about exact pinning can still inspectmodel.release_manifest. This stops routine country-model patch bumps from breakingUKTaxBenefitModel/USTaxBenefitModelinstantiation in callers that pinpolicyenginebut resolvepolicyengine-uk/policyengine-usvia>=.
- Bump pinned country-model versions in
[us],[uk], and[dev]extras, and the corresponding bundled release manifests, to versions that support Python 3.9, include the breakdown-range fixes required by the stricter validator in policyengine-core 3.24.0+, and ship with policyengine-core>=3.24.1. Previouslypolicyengine-us==1.602.0andpolicyengine-uk==2.74.0were stale pins that no longer installed cleanly under modern core. Data-package pins (policyengine-us-data==1.73.0,policyengine-uk-data==1.40.4) are unchanged — the bumped model versions read the same dataset artefacts as before. - Bump
policyengine_coreminimum to>=3.25.0. Includes theset_inputpreservation fix from PolicyEngine/policyengine-core#475 that restores UK household-impact calculations afterapply_reform(#1628). All 11tests/test_household_impact.pycases pass on the new pin.
- Added managed release-bundle runtime enforcement for bundled US and UK microsimulations, including manifest-backed dataset pinning and runtime bundle metadata.
- Add TRACE TRO export helpers for certified runtime bundles and expose them through
policyengine.core.
- Fix the release versioning workflow so it bumps from the highest known released version instead of regressing to a stale version from
pyproject.toml.
- Add certified bundle metadata that records runtime model pins alongside build-time data artifact provenance and compatibility fingerprints.
- Align the bundled UK release manifest with the pinned
policyengine-ukpackage version and updated data package revisions.
- Fixed the UK paper reproduction workflow so the checked-in example runs on Python 3.14 and the associated analysis helpers handle that path cleanly.
- Add winner, loser, and no-change percentages to the congressional district impact output.
- 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.
- Skip redundant Lint and Test in Phase 2 of push workflow since code is identical to Phase 1
- Use GitHub App token in push workflow Versioning job to enable auto-triggering of Phase 2 (Publish)
- Consolidate CI/CD workflows into a unified push workflow with two-phase sentinel pattern, enforce changelog fragments on PRs
- Use GITHUB_TOKEN instead of missing POLICYENGINE_GITHUB PAT in push workflow
- Migrated from changelog_entry.yaml to towncrier fragments to eliminate merge conflicts.
- Switched code formatter from black to ruff format.
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
3.2.0 - 2026-02-24 17:31:22
- Python 3.14 support
3.1.16 - 2026-01-25 14:20:29
- Bumped policyengine-core minimum version to 3.23.5 for pandas 3.0 compatibility
3.1.15 - 2025-12-14 23:51:27
- Household impacts
3.1.14 - 2025-12-10 21:59:24
- Improvements to loading taxbenefitsystems.
3.1.13 - 2025-12-10 19:29:28
- Naming improvements.
3.1.12 - 2025-12-10 14:43:52
- Bug where all datasets would be the last year.
3.1.11 - 2025-12-02 14:03:39
- Caching didn't save time!
3.1.10 - 2025-12-02 13:02:37
- Caching failure.
3.1.9 - 2025-12-02 12:48:37
- Added caching of saved simulations
3.1.8 - 2025-12-02 00:20:11
- Dataset speedup with better handling of string cols.
3.1.7 - 2025-11-24 16:34:53
- Build error
3.1.6 - 2025-11-24 16:23:57
- Parameter values now accessible from models.
3.1.5 - 2025-11-21 12:59:36
- Minor fixes
3.1.4 - 2025-11-20 14:06:49
- Minor fixes
3.1.3 - 2025-11-18 13:46:23
- Entity variables moved out to an editable constant.
3.1.2 - 2025-11-18 11:09:18
- Standardised saving and loading of simulations.
3.1.1 - 2025-11-17 11:45:43
- Policy and Dynamic classes now support addition operator (add) to combine policies and dynamics.
- Parameter values are appended when combining policies/dynamics.
- Simulation modifiers are chained in sequence when combining policies/dynamics.
3.1.0 - 2025-11-17 10:25:50
3.0.0 - 2025-09-23 08:43:22
- Major version bump to fix pypi issues.
2.0.0 - 2025-09-21 22:29:03
- Consolidated models and database integration.
1.0.0 - 2025-09-21 22:26:17
- Complete rewrite.
0.6.1 - 2025-08-14 08:11:19
- Fixed UK dataset loading issue.
0.6.0 - 2025-07-17 10:27:55
- Updated the UK data to the latest survey year.
0.5.0 - 2025-06-26 20:04:46
- Unit test for GeneralEconomyTask.calculate_cliffs() and fixture for the test
- Test for calculate_single_economy with cliff impacts
0.4.5 - 2025-06-05 05:08:16
- Added more log messages.
0.4.4 - 2025-06-04 07:35:40
- ECPS handling issue.
0.4.3 - 2025-06-02 07:53:55
- Removed bad type check.
0.4.2 - 2025-05-30 21:12:45
- Tests for Simulation._set_data()
- Disambiguated filepath management in Simulation._set_data()
- Refactored Simulation._set_data() to divide functionality into smaller methods
- Prevented passage of non-Path URIs to Dataset.from_file() at end of Simulation._set_data() execution
0.4.1 - 2025-05-27 11:10:28
- Model and data versions are always available.
0.4.0 - 2025-05-26 21:26:48
- Error handling for data and package version mismatches.
0.3.10 - 2025-05-23 00:09:23
- Always look for new data file versions even if we have a local copy of one.
0.3.9 - 2025-05-20 08:28:29
- Added cliff impacts.
0.3.8 - 2025-05-19 22:42:39
- google storage caching is now fully sync, not async and reenabled.
0.3.7 - 2025-05-19 16:53:05
- revert cached downloads from google storage
0.3.6 - 2025-05-16 16:18:15
- Removed max and min year bounds for Simulations.
0.3.5 - 2025-05-16 14:00:30
- downloads from google storage should now be properly cached.
0.3.4 - 2025-05-16 12:55:50
- Fixed
format_figto work with Python 3.11.
0.3.3 - 2025-05-16 12:23:45
- Changelog entry check.
0.3.2 - 2025-05-15 21:56:02
- new class CachingGoogleStorageClient for locally caching gs files to disk.
0.3.1 - 2025-05-15 15:52:24
- Refactored ParametricReform schema into clearer subschemas.
- Added conversion of Infinity and -Infinity to np.inf and -np.inf.
0.3.0 - 2025-05-13 23:23:01
- Economy simulation comparisons now include country package version
0.2.1 - 2025-04-30 11:33:36
- Default dataset handling (extra backups added).
- Bug in state tax revenue calculation.
0.2.0 - 2025-04-29 13:08:11
- Google Cloud Storage data downloads.
0.1.3 - 2025-04-26 16:25:19
- Gracefully handle HuggingFace 429s.
0.1.2 - 2025-04-26 16:21:55
- Gracefully handle data download 429s.
0.1.1 - 2025-02-21 14:02:22
- Dependency for
pkg_resources.
- Initial version of package.