About · Python 3.12+ · 843 public symbols
Most of the work happens before the estimator
Statistical libraries are generous with estimators and silent about everything around them. But the expensive mistakes in applied work are almost never a mis-specified regression. They are a quantity nobody defined precisely enough to compare, a graph that never licensed the comparison in the first place, an experiment sized for an effect everyone already believed, and a number that arrived in a slide with no record of what it assumed.
axiom is built for that part. It starts a step earlier — is the number you want recoverable from the data you have? — and keeps going a step later — what is it worth, what did it assume, and does it still mean the same thing where you are about to use it?
The organizing idea
One loop, and every arrow is a package
axiom supports one workflow end to end, and nothing else is in scope. Each box is a question somebody actually has to answer; each arrow is a subpackage that turns the answer into the next question's input. The loop closes because a quantity measured by an experiment and a quantity read off a model can be compared — which is the whole product, and the reason an estimand here has eight facets rather than a name.
The loop the charter commits to
Declare, check, design, measure, fold in, decide, pool — and the pooled evidence becomes the next question's prior.
docs/plan/00-charter.md — “Each arrow is a package. Nothing else is in scope.”
estimands — the quantity, in eight facets, so
two numbers can be compared or refused.
8 facets
Why the loop matters more than any box in it
Each of these exists elsewhere, in better-known packages. What does not exist elsewhere is the handoff: an identification verdict that travels with the estimate, an experiment priced against the decision waiting on it, and a transfer between two quantities that is refused when it cannot be licensed. The seams are the library.
Where it came from
A rewrite, not a fork
axiom began inside a marketing-mix platform: 177,000 lines across 32 subpackages, with a web API, a frontend, an agent framework and a reporting engine attached. The five subpackages that mattered — estimands, calibration, planning, diagnostics, benchmarks — came to about 24,000 lines, and every one of them reached into a 5,375-line model class for the posterior it operated on.
Stripping that out in place would have meant rewriting the seam anyway, in a repo that also had to keep a server and a frontend compiling. So the seam was stated once, and everything was written against it:
the causal, design and calibration layer should depend on a posterior, not on a model class.
That is about 120 lines of protocol in axiom.core. A NumPyro fit, a
Laplace approximation, an analysis reloaded from disk and a dict built by hand are
the same kind of thing to everything above it.
The second decision was the vocabulary. The mathematics came out of marketing; the nouns deliberately did not. A saturating response curve is not a marketing idea, and the moment it is called a “channel response” the next team that needs it for a fertilizer trial writes it again. So the core speaks treatment, dose, unit, outcome, covariate — and marketing is one adapter, with a build gate that keeps its words out of everything below it.
Clean-room, against recorded values
Nothing was copied. Every ported computation is
checked against the value the parent produced, recorded in
tests/golden/ — and where a port deliberately changes a number, the
fixture points at the note that says why. A golden failure stops the build.
The vocabulary rule is a test
A gate walks every identifier in the library and
fails on channel, spend, geo,
kpi, roas. It exempts exactly one package:
axiom.adapters, which exports 17 symbols
and is the only place those words are allowed.
Domain-general on purpose
The same library runs the 12 worked examples: agronomy, labour economics, education, epidemiology, ecology, public health, clinical research, energy, process engineering, marketing, psychology and product analytics. None of them is a special case in the code.
Scope
What it refuses to be
A short list of things that are out permanently, rather than “later”. Each one is a real request that was turned down, and writing them here is cheaper than discovering the boundary by pull request.
Not a platform
No web application, HTTP API or frontend. No authentication, tenancy, sessions, job queues or object stores. axiom is a library that returns typed results to whatever is calling it.
Not an agent
No language model and no agent framework inside the library. A model that writes report prose exists, as a separate distribution that depends on axiom and that axiom never imports — the gates walk the package and cannot see it.
Not a marketing-mix model
An MMM is a configuration of the response surface plus the marketing adapter, not a class in this repository. Nor are ad-platform connectors, Excel configuration or data-studio pipelines.
Two amendments, recorded rather than quietly taken
The charter said “no report generator”, and a report
engine shipped anyway — templates plus HTML, PowerPoint and PDF renderers — because a
result that cannot leave the notebook does not close the loop. And the charter said
“no language model”, which still holds inside the library while an add-on that writes
prose lives outside it. Both are written down as deviations with their reasons, in
docs/notes/, which is where an argument this project lost is kept.
How you can tell
Four kinds of evidence, none of them a claim
A library about provenance has to be able to answer the question it asks of everyone else. Each of these is a command you can run.
01 · AGAINST OTHERS
Published values
Real datasets collected by other people, reproduced end to end and checked against
R metafor, NIST certified values and two published papers.
3e-04 worst relative disagreement across 12 checked values
See the benchmarks
02 · ACROSS FIELDS
Runnable examples
The same library in twelve fields, each a narrated walkthrough that says why every step is the necessary step — and what the run actually showed.
49s to run all 12 on the core install
See the examples
03 · AGAINST ITSELF
Every symbol, demonstrated
A gate fails the build if a public symbol is not used in an executed notebook. The API page shows a real call for each one, lifted out of those notebooks.
838 / 843 symbols with a real call on the page
Browse the API
04 · END TO END
Known-truth recovery
Every estimator is checked against a simulated world whose answer is written down, and the trial in the case study is one of those worlds run in full.
12 gates contract tests that fail the build, on every commit
Read the case study
The twelve gates are not unit tests. They encode the design decisions: that importing the library pulls no sampler, that no core module mentions a marketing noun, that every interval carries its definition and mass, that every evidence transfer leaves a ledger line, that the numpy and jax evaluations of a model agree to eight decimals, and that no public symbol goes undemonstrated.
Who it is for
Three readers, three entrances
You have data and a decision
Start with the concept guides — no installation needed — and then the tutorial, which carries one decision from “what are we asking” to a written recommendation. The most common finding is that the data on hand cannot answer the question, and it is better to learn that in an afternoon than in a review.
You are designing a study
Go to Design, then GEIGER-1911 — a case study in which nothing is fitted until the last section, because the deliverable is a set of angles, an aperture, a time allocation and a stopping rule, all produced before the apparatus exists.
You maintain a modelling stack
Read the API map and the layering it is
grouped by, then the notebooks under nbs/. Imports point down only and
a contract test enforces it, so a subpackage can be read on its own.
In practice
What you would need to know to use it
Status, and what is deferred
Every subpackage is implemented, every public symbol is demonstrated in an executed notebook, and the contract gates and recovery suites are green. Deferred deliberately, and written down rather than implied: non-parametric surfaces, discrete latent structure, block-bootstrap switchback standard errors, and stage-wise-ordered estimates for a stopped sequential trial.
The plan that produced the code, and every decision taken
along the way — including the ones that were reversed — are in the repository under
docs/plan/ and docs/notes/.
Practical facts
import axiom.identify, axiom.design, axiom.surface, axiom.meta, axiom.estimands pulls no sampler
at all.
0.78s
Next