Skip to content

Add Belgium pilot: Axiom rules engine over populace-be entity tables#448

Open
MaxGhenis wants to merge 2 commits into
mainfrom
be-axiom-pilot
Open

Add Belgium pilot: Axiom rules engine over populace-be entity tables#448
MaxGhenis wants to merge 2 commits into
mainfrom
be-axiom-pilot

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Fixes #447

What this is

A pilot Belgium channel for policyengine.py — the first country that runs on the Axiom rules engine instead of a policyengine-core country package:

  • src/policyengine/tax_benefit_models/be/AxiomBelgiumPilot, a TaxBenefitModelVersion subclass (deliberately outside the certified-release machinery: no manifest, no region registry) whose run() builds a populace Frame, executes the rulespec-be composed worker pipeline through the populace AxiomEngine adapter, and returns weighted person/household MicroDataFrames as the output dataset.
  • PopulaceBelgiumDataset / BEYearData — two-entity (person, household) HDF5 dataset in the populace-be layout, calibrated household weights.
  • examples/belgium_axiom_pilot.py — the end-to-end population run.
  • tests/test_be_axiom_pilot.py — statutory assertions on a four-person fixture around the work-bonus phase-out (contribution wiped at 20k, partial at 30k, statutory 13.07% by 60k); skips cleanly unless populace-frame, axiom-rules-engine, and a rulespec-be checkout are present (source-only, so CI skips).

Validation status: machine-checked conformance vs EUROMOD

The encoding this channel executes is conformant against EUROMOD BE_2025 per the axiom-oracles scoreboard: 23/23 in-scope EUROMOD policies covered, 0 unexplained residuals, 0 open Axiom-attributed mismatches. Per-case, the two engines agree to the cent on the worker suites — including be-worker-disposable-income-list (gross − tax − SSC end to end, 0 mismatches) — and where they deliberately differ (e.g. the couple pipeline scopes out the worker-stage deductions), each residual carries a committed disposition with a statutory citation. Compositions behind every covered suite are now machine-recorded and reproducible (TheAxiomFoundation/axiom-oracles#235). Nothing in the tables below is unexplained.

Population results on the calibrated pilot dataset (2026-07-08)

populace-us support records reweighted to Belgian Ledger targets (Statbel demography, SPF fiscal income, ONSS contributions, ONEM recipients — 21/21 within 1.8%), 166,302 persons:

aggregate this channel (Axiom) EUROMOD, identical rows
employee SSC (gross-of-bonus concept) EUR 20.874B EUR 20.874B — agreement to EUR 643
employee SSC after work bonus EUR 16.16B EUR 15.02B
worker PIT before withholding (individual assessment) EUR 23.28B EUR 19.53B

Reading the PIT difference — every euro is attributed, and the buckets point in different directions:

  1. EUROMOD-attributed (~EUR 2B, documented and raised upstream). EUROMOD refunds the fiscal work-bonus credit without capping it at contributions due; CIR 92 article 289ter/1 credits the bonus "reellement accorde", which ONSS caps at contributions — the encoding follows the statute and the divergence is logged oracle-attributed in the conformance ledger. The work-bonus phase-out/January-timing residual is filed on the JRC's own engine repo (Belgium BE_2025 employee work-bonus output appears to apply February 2025 table to January ec-jrc/JRC-EUROMOD-software-source-code#5). EUROMOD also does not model the German-speaking community regime at all (Axiom does, grounded to the DG decrees).
  2. Axiom-side composition, tracked (~EUR 2B when couples/kids are linked). Dependant allowances and the marital quotient are encoded and their residuals dispositioned, but the couple pipeline does not yet chain the worker stages (SSC/forfait/work-bonus) per spouse — Chain worker SSC/forfait/work-bonus stages into the couple PIT pipeline (retire the marital-quotient dispositions) TheAxiomFoundation/rulespec-be#106 tracks that composition (compositions themselves are now mechanically recorded and resolvable per conformance: record per-suite runnable program compositions (#185) TheAxiomFoundation/axiom-oracles#235). With units linked, EUROMOD computes EUR 17.15B; closing that distance is on this side, not theirs.
  3. Demo-population labeling artifact. The pilot labels every worker full-time/full-year, which inflates low-wage bonus divergence on both engines' FTE machinery; this washes out with real data (SILC carries actual months and hours).

On the SPF Finances comparison in earlier revisions: worker PIT sits at 0.37 of SPF's EUR 62.84B all-PIT total — and moved further from it as the encoding improved (the newly encoded refundable credit reduces worker-slice PIT). That distance is scope by construction, not error: the pipeline models wage earners only (EUR 159.7B of remuneration against EUR 274.7B of fiscal income; no pensions, self-employed, or investment income in the base).

What this is not

Not a certified model and not representative Belgian microdata — the support records are American, worker-slice coverage, individual assessment. Labeled as such throughout. It demonstrates the channel (Simulation(dataset=..., tax_benefit_model_version=AxiomBelgiumPilot(...)).run()) so the certified pieces (SILC support strata, engine-computed targets, full composed coverage) can land behind a stable surface.

Dependency note

populace-frame and axiom-rules-engine are not on PyPI; nothing is added to pyproject.toml. The module docstring and test skip conditions document source installation. Publishing both is the follow-up that makes the example runnable for users (axiom-rules-engine wheel publishing is blocked on a maturin build regression on its main, filed as TheAxiomFoundation/axiom-rules-engine#92).

🤖 Generated with Claude Code

The first non-policyengine-core country channel: an Axiom-backed
TaxBenefitModelVersion outside the certified-release machinery, running
the rulespec-be composed worker pipeline (employee SSC and PIT before
withholding) over populace-be person/household tables with calibrated
weights. Includes a populace-style two-entity dataset class, a population
example scored against ONSS/SPF facts, and tests that skip cleanly when
the source-only dependencies are absent.

Fixes #447

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The composed pipeline now computes the ONSS work-bonus amounts from law
(the a/b supplied inputs are gone; the reference-wage input bridges 0 to
worker remuneration), and the ordinary-contribution concept nets the
bonus. Update the supplied defaults, exercise the phase-out in the test
fixture (wiped at 20k, partial at 30k, statutory 13.07 percent by 60k),
and label the example aggregates accordingly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Belgium pilot: run the Axiom rules engine over populace-be entity tables

1 participant