axiom

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.

declare an estimand is it identified? what would identify it? fit a response surface design an experiment make a decision calibrate on the result pool into meta-evidence no yes

docs/plan/00-charter.md — “Each arrow is a package. Nothing else is in scope.”

declare estimands — the quantity, in eight facets, so two numbers can be compared or refused. 8 facets
check identify — the route the graph licenses, or the named variable that is missing. 4 routes
design design — how big, which estimator, what it is worth, what withholding costs. 165 symbols
fold in calibrate — the experiment enters the model through the prior or the likelihood, with a ledger line for every assumption. 2 routes
map surface — the dose–response, its optimum under a budget, and where to probe next. one forward()
pool meta — everything you have run, and what a new study should expect. 70 symbols
critique diagnose — would the procedure have found an effect that was not there, and what would overturn this one? 85 symbols

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.

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

python 3.12 or newer. 3.12+
core install numpy, scipy, pandas, pydantic — enough for identification, design, planning and closed-form meta-analysis. A sampler is an extra you add when you need posterior draws. 4 packages
import cost import axiom.identify, axiom.design, axiom.surface, axiom.meta, axiom.estimands pulls no sampler at all. 0.78s
docs This site is the narrative one. The generated API reference, plus the plan and the notes, build separately from the docstrings. 2 sites
author Matthew Reda. 2026
licence Proprietary. All rights reserved — no licence to use, copy, modify or distribute is granted by the source being visible. proprietary

Next

Where to go from here

read The concept guides — what makes a question causal, and why a number that was right where it was measured can be wrong where it is used. no install
follow The tutorial — one decision, eight steps, ending in a document a board could act on. 8 steps
watch The two case studies: HYPER-3, a trial that stops an arm early, and GEIGER-1911, a design worked out before the apparatus exists. 2 studies
verify The benchmarks and the examples, both runnable from the repository. 12 values
use The API map, searchable, grouped by subpackage and layer, with a real call for every symbol. 843 symbols