axiom

Reference

Every public symbol

843 of them, across 18 subpackages. The list is generated from each subpackage’s __all__ at build time, so it cannot drift from the code. Gate 12 fails the build if any symbol here lacks an executed notebook that uses it.

Click a symbol for its signature, its one-line docstring, and a real call — the shortest statement in the notebook series that actually uses it, lifted out of the executed .ipynb at build time. Nothing on this page is a hand-written example, which is why none of it can quietly stop compiling. 838 of the 843 have such a line; the 5 that do not say so where they sit.

axiom.core

foundation115 symbols · 7 notebooks

Specs, dimensions, intervals, expressions — the vocabulary everything else is written in.

AcceptanceRegionspec

AcceptanceRegion(n: int, p: float, alpha: float, lower: int, upper: int)

An exact binomial acceptance region for a rate criterion.

region: AcceptanceRegion = clopper_pearson(n=500, p=0.05, alpha=0.001)
Addspec

Add(node: Literal['add'] = 'add', terms: tuple[Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque, ...])

Sum of terms sharing one dimension.

response = Equation(lhs=y, rhs=Add(terms=(Param(name="a", dimension=D.outcome), hill)), name="response")
Applyspec

Apply(node: Literal['apply'] = 'apply', fn: Literal['exp', 'log', 'log1p', 'expm1', 'tanh', 'sigmoid', 'logit', 'softplus', 'neg', 'relu', 'step', 'sin', 'cos'], arg: Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque)

A scalar function of one argument. Argument and result are dimensionless.

print(fn, value(Apply(fn=fn, arg=Const(value=0.0, dimension=dimensionless()))))
ApplyFncallable

ApplyFn(*args, **kwargs)

fns: list[ApplyFn] = ["exp", "sigmoid", "softplus"]
Assumptionspec

Assumption(name: str, facet: str, statement: str, challenged_by: str = '', state: Literal['unverified', 'satisfied', 'violated', 'asserted'] = 'unverified', detail: dict[str, str] = {})

A named, falsifiable condition that licenses a step.

overlap = Assumption(
    name="overlap",
    facet="population",
    statement="every target stratum has positive support in the source",
    challenged_by="propensity / dose overlap diagnostic",
)
BASESvalue

The declarable set of base dimensions.

print(list(BASES))
BaseRegistryclass

BaseRegistry(defaults: tuple[tuple[str, str], ...] = ...) -> None

The declarable set of base dimensions.

private_bases = BaseRegistry()
Blockedspec

Blocked(reason: str, detail: dict[str, str] = {}, status: Literal['blocked'] = 'blocked')

The request is not licensed: no route, no transfer, no identification.

Capabilityclass

Capability(*values)

What a producer can do. A request needing an absent one degrades, typed.

return frozenset({Capability.COUNTERFACTUAL})
Constspec

Const(node: Literal['const'] = 'const', value: float | tuple[float, ...], dimension: Dimension)

A literal with a declared dimension: a scalar, or a short vector (a lag index, knots).

one = Const(value=1.0, dimension=dimensionless())
Constraintspec

Constraint(name: str, expr: Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque, family: Literal['normal', 'lognormal'], observed: float, scale: float, detail: dict[str, str] = {})

A soft constraint on a scalar function of the parameters: ``observed ~ p(expr(θ), scale)``.

lift = Constraint(
    name="mean_lift@experiment_1",
    expr=Reduce(op="mean", arg=hill),
    family="normal",
    observed=6.0,
    scale=0.5,
    detail={"measurement": "experiment_1", "doses": "observed"},
)
Convolvespec

Convolve(node: Literal['convolve'] = 'convolve', signal: Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque, kernel: Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque)

Causal convolution of ``signal`` with ``kernel`` along the time axis.

carry = Convolve(signal=dose, kernel=weights)
Covariatespec

Covariate(name: str, dimension: Dimension | None = None, unit: str | None = None, description: str = '')

Measured, not intervened on.

rainfall = Covariate(name="rainfall", dimension=dimensionless(), description="standardized")
Dvalue

The declarable set of base dimensions.

print(D.currency, D.time, D.outcome, D.entity)
DIGITSvalue

int([x]) -> integer int(x, base=10) -> integer

print("digits kept of the uncertainty:", DIGITS)
Dataspec

Data(node: Literal['data'] = 'data', name: str, dimension: Dimension)

A column of the panel. Its dimension is the one its role declares.

dose = Data(name="dose", dimension=D.currency)
DesignMatrixclass

DesignMatrix(X: NDArray[float64], columns: tuple[str, ...], offset: NDArray[float64], at: Mapping[str, NDArray[float64]]) -> None

``mu = offset + X @ theta[columns]`` at a fixed point of the nonlinear parameters.

dm = DesignMatrix(X=X, columns=("a", "b"), offset=np.zeros(4), at={"k": np.array(50.0)})
Dimensionspec

Dimension(exponents: dict[str, Fraction])

A product of declared base dimensions raised to rational exponents.

Dimension(exponents={"temperature": 1})
DimensionErrorerror

Two quantities of different dimension were combined.

try:
    D.currency.require_equal(D.time, context="Add(term_1, term_2)")
except DimensionError as e:
    print(e)
Divspec

Div(node: Literal['div'] = 'div', numerator: Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque, denominator: Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque)

Quotient; exponents subtract.

x = Div(numerator=dose, denominator=k)
Dosespec

Dose(name: str, dimension: Dimension | None = None, unit: str | None = None, description: str = '', numeraire: str | None = None)

The magnitude of an intervention on a treatment, with units and a numeraire.

dose = Dose(name="fertilizer_dose", dimension=D.currency, unit="USD", numeraire="USD")
Entityclass

Entity(*args, **kwargs)

What every entity spec provides: a name, an optional dimension and unit.

print(isinstance(dose, Entity), Treatment.__mro__[1].__name__)
EntityNamecallable

EntityName(*args, **kwargs)

Runtime representation of an annotated type.

name: EntityName = "tv-ads.v2"
Equationspec

Equation(node: Literal['equation'] = 'equation', lhs: Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque, rhs: Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque, name: str = '')

``lhs = rhs``; both sides must share a dimension.

response = Equation(lhs=y, rhs=Add(terms=(Param(name="a", dimension=D.outcome), hill)), name="response")
Exprcallable

Expr(*args, **kwargs)

Runtime representation of an annotated type.

hill: Expr = Mul(factors=(beta, Div(numerator=Pow(base=x, exponent=s),
                                    denominator=Add(terms=(one, Pow(base=x, exponent=s))))))
Failurevalue

Represent a PEP 604 union type

def realize(value_ok: bool) -> float | Failure:
    if not value_ok:
        return Blocked(reason="adjustment set contains an unmeasured variable", detail={"node": "U"})
    return 0.42
Gatherspec

Gather(node: Literal['gather'] = 'gather', source: Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque, index: Data)

``source[..., index]``: pick entries of a vector-valued expression by an integer column.

mean = Add(terms=(Gather(source=alpha, index=unit), hill))
Intervalspec

Interval(lower: float, upper: float, definition: Literal['eti', 'hdi', 'wald'], mass: float)

``[lower, upper]`` with the definition and the mass it was computed at.

Interval(lower=0.0, upper=1.0)
Interventionspec

Intervention(doses: dict[str, float], mode: Literal['set', 'scale', 'shift'] = 'set', version: str = 'unspecified', window: TimeWindow | None = None)

What is set, to what value, how, over what support.

iv = Intervention(doses={"fertilizer": 100.0, "irrigation": 20.0}, version="granular-v2")
LedgerLinespec

LedgerLine(kind: str, statement: str, assumption: Assumption | None = None, detail: dict[str, str] = {}, source: str = '', target: str = '')

One entry in the assumption ledger.

line = LedgerLine(
    kind="facet:population",
    statement="transferred from region=north to region=all under overlap",
    assumption=overlap.asserted(),
    detail={"from": "north", "to": "all"},
)
Likelihoodspec

Likelihood(family: Literal['normal', 'lognormal', 'student_t', 'poisson'], scale: str | None = None, scale_expr: Optional[Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque] = None, df: float | None = None)

How the outcome column is distributed around the mean expression.

Likelihood(family="normal", scale="sigma", scale_expr=se_col)
LikelihoodFamilycallable

LikelihoodFamily(*args, **kwargs)

family: LikelihoodFamily = "normal"
Linkspec

Link(node: Literal['link'] = 'link', fn: Literal['identity', 'log', 'logit'], arg: Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque)

A GLM link applied to a dimensionless argument (after a reference divide).

print(dimension(Link(fn=link, arg=Div(numerator=y, denominator=y_ref))))
LinkFncallable

LinkFn(*args, **kwargs)

link: LinkFn = "log"
Modelcallable

Model(*args, **kwargs)

bad_trees: list[Model] = [
    Apply(fn="log", arg=dose),
    Add(terms=(beta, Mul(factors=(beta, Apply(fn="log", arg=dose))))),
    Pow(base=dose, exponent=s),
    Convolve(signal=dose, kernel=k),
]
ModelSpecspec

ModelSpec(name: str, mean: Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque, outcome: Data, likelihood: Likelihood, parameters: tuple[Param, ...], constraints: tuple[Constraint, ...] = ())

Mean expression + outcome column + likelihood + every parameter's prior.

model = ModelSpec(
    name="panel_hill",
    mean=mean,
    outcome=y,
    likelihood=Likelihood(family=family, scale="sigma"),
    parameters=(k, s, beta, lam, a_mean, a_sd, alpha, sigma),
)
Mulspec

Mul(node: Literal['mul'] = 'mul', factors: tuple[Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque, ...])

Product; exponents add.

print(dimension(Pow(base=Mul(factors=(dose, dose)), exponent=Fraction(1, 2))))
NonEmptyStrcallable

NonEmptyStr(*args, **kwargs)

Runtime representation of an annotated type.

reason: NonEmptyStr = "a reason that is not blank"
ODESystemspec

ODESystem(node: Literal['ode'] = 'ode', states: tuple[Data, ...], rhs: tuple[Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque, ...], time: Data)

``d(state_i)/dt = rhs_i`` for each state. Dimension-check-only in 1.0 (C1).

ode = ODESystem(states=(S,), rhs=(Mul(factors=(r, S)),), time=t)
Opaquespec

Opaque(node: Literal['opaque'] = 'opaque', name: str, inputs: tuple[Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque, ...] = (), dimension: Dimension)

A user-supplied function the tree cannot express.

weights = Opaque(name="geometric", inputs=(lam,), dimension=dimensionless())
OpaqueFncallable

OpaqueFn(*args, **kwargs)

geometric: OpaqueFn = lambda lam, L=6: lam ** np.arange(L)
OpaqueRegistrycallable

OpaqueRegistry(*args, **kwargs)

A Mapping is a generic container for associating key/value pairs.

registry: OpaqueRegistry = {"geometric": geometric}
Outcomespec

Outcome(name: str, dimension: Dimension | None = None, unit: str | None = None, description: str = '', aggregation: Literal['sum', 'mean', 'rate'] = 'sum')

What you are trying to move.

yield_total = Outcome(name="yield_total", dimension=D.outcome, unit="kg", aggregation="sum")
PRIOR_HYPERvalue

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

print(PRIOR_HYPER)
Paramspec

Param(node: Literal['param'] = 'param', name: str, dimension: Dimension, prior: Prior | None = None, shape: tuple[int, ...] = ())

A parameter to be inferred, with a declared dimension and an optional prior.

k = Param(name="k", dimension=D.currency, prior=Prior(family="lognormal", hyper={"mu": 3.0, "sigma": 1.0}))
Populationspec

Population(name: str, description: str = '', strata: dict[str, dict[str, float]] = {})

A target population: a name and, where known, the strata weights that define it.

north = Population(name="north", strata={"soil": {"clay": 0.3, "loam": 0.7}})
Posteriorclass

Posterior(draws: Mapping[str, ArrayLike], *, coords: Mapping[str, Sequence[Any]] | None = None, provenance: Mapping[str, Any] | None = None) -> None

Draws keyed by name, each ``(chain, draw, *shape)``, plus coords and provenance.

post = Posterior(
    {
        "beta": rng.normal(0.3, 0.1, size=(4, 500, 2)),
        "sigma": rng.lognormal(-1, 0.2, size=(4, 500)),
    },
    coords={"treatment": ["fertilizer", "irrigation"]},
    provenance={"seed": 42, "backend": "hand-built"},
)
Powspec

Pow(node: Literal['pow'] = 'pow', base: Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque, exponent: Union[Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque, Fraction])

``base ** exponent``.

print(dimension(Pow(base=Mul(factors=(dose, dose)), exponent=Fraction(1, 2))))
PredictiveDrawsclass

PredictiveDraws(values: NDArray[float64], intervention: Intervention, window: TimeWindow | None = None, coords: Mapping[str, Sequence[Any]] = <factory>, seed: int | None = None) -> None

Outcome draws under an intervention: ``values`` is ``(chain, draw, *shape)``.

return PredictiveDraws(values=beta @ dose, intervention=iv, window=window, seed=seed)
Priorspec

Prior(family: Literal['normal', 'halfnormal', 'lognormal', 'beta', 'gamma', 'uniform', 'fixed'], hyper: dict[str, float | str])

A named prior family with hyperparameters.

k = Param(name="k", dimension=D.currency, prior=Prior(family="lognormal", hyper={"mu": 3.0, "sigma": 1.0}))
PriorFamilycallable

PriorFamily(*args, **kwargs)

fam: PriorFamily = "halfnormal"
Reducespec

Reduce(node: Literal['reduce'] = 'reduce', op: Literal['sum', 'mean', 'max'], arg: Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque, keepdims: bool = False)

Reduce ``arg`` along its last axis: ``sum``, ``mean``, ``max``. Dimension is preserved.

weights = Div(numerator=raw, denominator=Reduce(op="sum", arg=raw))
SchemaVersionErrorerror

SchemaVersionError(spec: str, found: str, expected: str) -> None

A serialized spec's version has no migration to the current class version.

try:
    ProbeDesignV2.from_json(json.dumps(v1_payload))
except SchemaVersionError as e:
    print("no migration:", e)
Specspec

Spec()

Frozen, versioned, hashable configuration object. Subclass freely.

Spec.from_json('{"not": "an envelope"}')
SpecDiffspec

SpecDiff(spec: str, changed: dict[str, tuple[Any, Any]])

Field-level differences between two specs of the same type.

d: SpecDiff = design.diff(
    design.model_copy(update={"levels": (0.0, 50.0, 150.0), "window": TimeWindow(start=0, stop=12)})
)
SpecErrorerror

Base class for spec construction and (de)serialization failures.

try:
    Spec.from_json('{"not": "an envelope"}')
except SpecError as e:
    print("bad envelope refused:", e)
Summaryspec

Summary(mean: float, median: float, sd: float, interval: Interval, n: int)

Point summaries of a set of draws plus one provenance-carrying interval.

s: Summary = summarize(draws, definition="hdi", mass=0.95)
SupportsEstimandsclass

SupportsEstimands(*args, **kwargs)

A fitted producer: posterior draws + counterfactual predictions + declared estimands.

print([m for m in dir(SupportsEstimands) if not m.startswith("_")])
SupportsForwardclass

SupportsForward(*args, **kwargs)

A deterministic response surface: ``forward`` == ``interpret.value(expr)``.

print(isinstance(surface, SupportsForward), surface.forward({"dose": d}, {"k": 50.0, "s": 2.0, "beta": 10.0}))
SupportsInterventionclass

SupportsIntervention(*args, **kwargs)

Anything you can ask a counterfactual of.

print(isinstance(surface, SupportsIntervention))
SupportsPosteriorclass

SupportsPosterior(*args, **kwargs)

Anything with draws you can ask questions of.

print(isinstance(post, SupportsPosterior), post.names(), post.n_chains, post.n_draws())
Systemspec

System(node: Literal['system'] = 'system', equations: tuple[Equation, ...])

A set of equations, each checked independently.

model = System(equations=(response, cost))
TimeWindowspec

TimeWindow(start: int, stop: int, basis: Literal['per_period', 'cumulative'] = 'cumulative')

A half-open index window ``[start, stop)`` on the panel's time axis, with a basis.

d: SpecDiff = design.diff(
    design.model_copy(update={"levels": (0.0, 50.0, 150.0), "window": TimeWindow(start=0, stop=12)})
)
Treatmentspec

Treatment(name: str, dimension: Dimension | None = None, unit: str | None = None, description: str = '')

The thing you can intervene on. Its dimension is the dose's dimension.

design = ProbeDesign(
    treatment=Treatment(name="fertilizer", dimension=D.currency, unit="USD"),
    levels=(0.0, 50.0, 100.0),
    window=TimeWindow(start=0, stop=8),
)
UNITSvalue

Units of measure, each attached to a base dimension, with conversions.

UNITS.declare("USD", "currency")
UndeclaredBaseErrorerror

A dimension named a base that the registry has not declared.

try:
    Dimension(exponents={"temperature": 1})
except UndeclaredBaseError as e:
    print(type(e).__name__, "->", e)
UndimensionedWarningerror

An entity was declared without a dimension and is treated as dimensionless (D6).

print(loose.dim, "|", [w.category.__name__ for w in caught], "|", issubclass(caught[0].category, UndimensionedWarning))
Unitspec

Unit(name: str, dimension: Dimension | None = None, unit: str | None = None, description: str = '', kind: Literal['individual', 'cluster', 'aggregate'] = 'individual')

The randomization / observation unit: a plot, a patient, a region.

plot = Unit(name="plot", dimension=D.entity, kind="cluster")
UnitConversionErrorerror

No conversion is registered between two units of the same dimension.

try:
    UNITS.convert(1.0, src, dst)
except (DimensionError, UnitConversionError) as e:
    print(f"{src} -> {dst}: {type(e).__name__}: {e}")
UnitSystemclass

UnitSystem() -> None

Units of measure, each attached to a base dimension, with conversions.

private_units = UnitSystem()
UnknownSpecErrorerror

The envelope names a class that cannot be imported or is not a Spec.

try:
    TimeWindow.from_json(design.to_json())
except UnknownSpecError as e:
    print("wrong class refused:", e)
Unsupportedspec

Unsupported(reason: str, detail: dict[str, str] = {}, missing: tuple[str, ...] = (), status: Literal['unsupported'] = 'unsupported')

The producer lacks a capability the request needs.

result = Unsupported(reason=f"surface lacks {', '.join(missing)}", missing=missing)
Unverifiedspec

Unverified(reason: str, detail: dict[str, str] = {}, status: Literal['unverified'] = 'unverified')

An assumption the machinery cannot check; the caller decides.

print(bool(Unsupported(reason="no marginal capability", missing=("marginal",))), bool(Unverified(reason="overlap not checked")))
Verdictspec

Verdict(status: Literal['identified', 'downgraded', 'blocked', 'unsupported', 'unverified'], reason: str = '', assumptions: tuple[Assumption, ...] = (), route: str = '')

An honest answer about whether a quantity is licensed, and under what.

v = Verdict(status="downgraded", route="backdoor", assumptions=(overlap,))
causal_convolvefn

causal_convolve(signal: Array, weights: Array) -> Array

``y[..., t] = Σ_l w[..., l] · x[..., t-l]`` with zero history before ``t=0``.

print(causal_convolve(np.ones(4), np.array([0.5, 0.25])))
checkfn

check(model: Model, expected: Dimension | None = None) -> Dimension

``dimension`` plus an optional assertion against a declared dimension.

print(check(hill, D.outcome))
childrenfn

children(node: Model) -> tuple[Model, ...]

Direct sub-nodes, in a stable order.

print(children(hill)[0], "|", node_path(hill, s))
clopper_pearsonfn

clopper_pearson(n: int, p: float, alpha: float = 0.001) -> AcceptanceRegion

Exact two-sided binomial acceptance region for ``k ~ Binomial(n, p)``.

region: AcceptanceRegion = clopper_pearson(n=500, p=0.05, alpha=0.001)
compile_jaxfn

compile_jax(model: Model, *, opaque: JaxOpaqueRegistry | None = None) -> JaxFn

Build a traceable ``f(data, params)`` for ``model``.

f_mean = compile_jax(mean)
compile_log_densityfn

compile_log_density(model: ModelSpec, *, opaque: JaxOpaqueRegistry | None = None) -> Callable[[Mapping[str, Any], Mapping[str, Any]], Any]

``f(data, z) -> log posterior`` in unconstrained coordinates, traceable by jax.

f_ld = compile_log_density(model)
constrainfn

constrain(model: ModelSpec, z: Mapping[str, ArrayLike]) -> tuple[dict[str, Array], float]

Inverse of ``unconstrain``; also returns the summed log-Jacobian ``log|dθ/dz|``.

back, log_jac = constrain(model, z)
data_namesfn

data_names(node: Model) -> tuple[str, ...]

Every distinct ``Data`` name in the tree, by first appearance.

print([p.name for p in params(model)], data_names(model))
decimals_forfn

decimals_for(uncertainty: float, *, digits: int = 2) -> int

Decimal places warranted by ``uncertainty``, for ``round`` and ``format``.

print(f"12.3456789 ± {u:<6} -> {format_measured(12.3456789, u):>8}   "
      f"decimals={decimals_for(u)}")
dimensionfn

dimension(model: Model, path: str = '') -> Dimension

The dimension of ``model``; raises ``DimensionError`` naming the failing node.

print(dimension(hill))
dimension_offn

dimension_of(entity: Entity) -> Dimension

The entity's dimension; dimensionless when none was declared (D6).

print(UNITS.factor("USD", "GBP"), "|", UNITS.dimension_of("week"))
dimensionlessfn

dimensionless() -> Dimension

print("x / x is dimensionless:", (rate / rate) == dimensionless(), (rate / rate).is_dimensionless)
effective_sample_sizefn

effective_sample_size(draws: ArrayLike) -> float

Bulk ESS via initial positive sequence of autocorrelations (Geyer 1992).

print("ESS iid   :", round(effective_sample_size(chains)))
etifn

eti(draws: ArrayLike, mass: float) -> Interval

Equal-tailed interval: the ``(1-mass)/2`` and ``1-(1-mass)/2`` quantiles.

format_intervalfn

format_interval(lower: float, upper: float, *, uncertainty: float | None = None, digits: int = 2, fallback: int = 6, group: bool = False) -> str

``[lower, upper]`` with both bounds at the precision the width supports.

print(format_interval(h.lower, h.upper), "from a half-width of", round(h.half_width, 4))
format_measuredfn

format_measured(value: float, uncertainty: float | None = None, *, digits: int = 2, fallback: int = 6, group: bool = False) -> str

``value`` as text, carrying only the digits ``uncertainty`` supports.

print("no stated uncertainty ->", format_measured(12.3456789))
free_parametersfn

free_parameters(model: ModelSpec) -> tuple[Param, ...]

Parameters that are inferred (everything not ``fixed``), in declaration order.

print([p.name for p in free_parameters(model)])
hdifn

hdi(draws: ArrayLike, mass: float) -> Interval

Highest-density interval: the narrowest window containing ``mass`` of the draws.

intervalfn

interval(draws: ArrayLike, *, definition: IntervalDefinition, mass: float) -> Interval

i = interval(draws, definition="eti", mass=0.5)
is_failurefn

is_failure(x: object) -> TypeGuard[Failure]

if is_failure(r):
    print(r.status, "->", r.reason, r.detail)
else:
    print("value", r)
jax_availablefn

jax_available() -> bool

print("jax available:", jax_available(), "|", require_jax())
latexfn

latex(model: Model) -> str

print(latex(hill))
latex_or_unsupportedfn

latex_or_unsupported(model: Model) -> str | Unsupported

``latex``, unless the tree contains an ``Opaque`` node — then a typed refusal.

print(latex_or_unsupported(carry))
likelihood_scalefn

likelihood_scale(model: ModelSpec, data: Mapping[str, ArrayLike], theta: Mapping[str, ArrayLike]) -> Array

The likelihood's scale at constrained values: the named parameter, or ``scale_expr`` evaluated on ``data`` and ``theta``. ``ValueError`` for a family without one.

print("scale per row:", likelihood_scale(known_scale, {"se": np.array([0.1, 0.2, 0.4])}, {"mu": 1.0}))
load_specfn

load_spec(s: str) -> Spec

Deserialize any spec from its envelope, resolving the class by name.

generic = load_spec(design.to_json())
log_densityfn

log_density(model: ModelSpec, data: Mapping[str, ArrayLike], z: Mapping[str, ArrayLike]) -> float

Unnormalized log posterior in unconstrained coordinates: prior + Jacobian + likelihood.

print("log prior:", round(log_prior(model, theta), 3), "| log density:", round(log_density(model, data, z), 3))
log_likelihoodfn

log_likelihood(model: ModelSpec, data: Mapping[str, ArrayLike], theta: Mapping[str, ArrayLike]) -> float

Outcome log-likelihood at constrained values, plus every soft constraint's term.

print('decomposition available:', callable(log_likelihood), callable(log_prior))
log_priorfn

log_prior(model: ModelSpec, theta: Mapping[str, ArrayLike]) -> float

Sum of log prior densities of the free parameters at constrained values.

print("log prior:", round(log_prior(model, theta), 3), "| log density:", round(log_density(model, data, z), 3))
mc_standard_errorfn

mc_standard_error(draws: ArrayLike) -> float

Monte-Carlo SE of the mean, ``sd / sqrt(ESS)``.

print("MCSE      :", mc_standard_error(ar))
missing_capabilitiesfn

missing_capabilities(producer: SupportsIntervention, required: frozenset[Capability] | set[Capability]) -> tuple[str, ...]

Names of required capabilities the producer lacks, sorted; empty means go.

missing = missing_capabilities(surface, needed)
node_pathfn

node_path(root: Model, target: Model) -> str

print(children(hill)[0], "|", node_path(hill, s))
paramsfn

params(node: Model) -> tuple[Param, ...]

Every distinct ``Param`` in the tree, by first appearance.

print([p.name for p in params(model)], data_names(model))
require_jaxfn

require_jax() -> Unsupported | None

print("jax available:", jax_available(), "|", require_jax())
round_tofn

round_to(value: float, uncertainty: float, *, digits: int = 2) -> float

``value`` rounded to the place ``uncertainty`` reaches.

print(round_to(s.mean, s.sd), "vs the stored", s.mean)
spec_type_namefn

spec_type_name(cls: type[Spec]) -> str

``"<module>:<qualname>"`` — the name written into the envelope.

print(spec_type_name(ProbeDesign))
summarizefn

summarize(draws: ArrayLike, *, definition: IntervalDefinition = 'hdi', mass: float = 0.9) -> Summary

s: Summary = summarize(draws, definition="hdi", mass=0.95)
unconstrainfn

unconstrain(model: ModelSpec, theta: Mapping[str, ArrayLike]) -> dict[str, Array]

Map constrained parameter values to R^k (log / logit per support).

z = unconstrain(model, theta)
valuefn

value(model: Model, *, data: Mapping[str, Any] | None = None, params: Mapping[str, Any] | None = None, opaque: OpaqueRegistry | None = None) -> Array

Evaluate ``model`` numerically. Missing inputs raise ``KeyError`` naming the name.

value(hill, data={"dose": d}, params={"k": 50.0, "s": 2.0, "beta": 10.0})
waldfn

wald(estimate: float, se: float, mass: float) -> Interval

Frequentist ``estimate ± z_{(1+mass)/2} · se``, labelled as such.

print(wald(estimate=1.2, se=0.3, mass=0.95))
walkfn

walk(node: Model, path: str = '') -> Iterator[tuple[str, Model]]

Pre-order traversal yielding ``(path, node)``; paths look like ``add[1].mul[0]``.

print([path for path, _ in walk(hill)][:5])
z_scorefn

z_score(value: float, reference: float, reference_se: float) -> float

``(value - reference) / se``: how the statistical golden fixtures compare.

print("z vs ref  :", z_score(value=ar.mean(), reference=0.0, reference_se=mc_standard_error(ar)))

axiom.data

foundation9 symbols · 2 notebooks

Panels and role maps: which column is the treatment, which is the outcome.

ColumnScalingspec

ColumnScaling(method: Literal['none', 'max', 'mean', 'standardize'], loc: float = 0.0, scale: float = 1.0)

``scaled = (x - loc) / scale``. ``loc`` is 0 unless standardizing.

manual = ScalingParameters(columns={"x": ColumnScaling(method=ref, scale=1000.0)})
Completenessspec

Completeness(n_units: int, n_periods: int, n_rows: int, balanced: bool, missing_cells: int, null_cells: int, gaps: dict[str, int] = {}, duplicate_rows: int = 0)

What the panel covers, so an estimator can decide whether it accepts it (review D5).

c: Completeness = panel.completeness()
Panelclass

Panel(frame: DataFrame, roles: RoleMap) -> None

A ``DataFrame`` plus a ``RoleMap``. Sorted by (unit, time) on construction.

panel = Panel(df, roles)
PanelErrorerror

The frame does not satisfy the role map.

try:
    holey.require_balanced(context="a synthetic-control estimator")
except PanelError as e:
    print("refused:", e)
RoleKindcallable

RoleKind(*args, **kwargs)

kind: RoleKind = roles.kind_of("fert_usd")
RoleMapspec

RoleMap(unit: str, time: str, outcome: tuple[str, Outcome], treatments: dict[str, Treatment] = {}, covariates: dict[str, Covariate] = {})

Column → role for a panel.

roles = RoleMap(
    unit="plot",
    time="week",
    outcome=("yield_kg", Outcome(name="yield_total", dimension=D.outcome, unit="kg")),
    treatments={
        "fert_usd": Treatment(name="fertilizer", dimension=D.currency, unit="USD"),
        "irrig_usd": Treatment(name="irrigation", dimension=D.currency, unit="USD"),
    },
    covariates={"rain_z": Covariate(name="rainfall", dimension=dimensionless())},
)
ScalingMethodcallable

ScalingMethod(*args, **kwargs)

ref: ScalingMethod = "max"
ScalingParametersspec

ScalingParameters(columns: dict[str, ColumnScaling])

Per-column scaling for a panel's measured columns.

manual = ScalingParameters(columns={"x": ColumnScaling(method=ref, scale=1000.0)})
fit_scalingfn

fit_scaling(panel: Panel, *, treatments: ScalingMethod = 'max', outcome: ScalingMethod = 'max', covariates: ScalingMethod = 'standardize') -> ScalingParameters

Fit scaling per role. Indices (unit, time) are never scaled.

params: ScalingParameters = fit_scaling(panel)

axiom.io

foundation8 symbols · 1 notebooks

Save and load a whole analysis as JSON plus a posterior file. Never a pickle.

Analysisclass

Analysis(specs: Mapping[str, Spec] = <factory>, panel: Panel | None = None, posterior: Posterior | None = None, evidence: tuple[Spec, ...] = (), ledger: tuple[LedgerLine, ...] = (), provenance: Provenance | None = None) -> None

Everything an analysis produced, keyed and hashed.

save_analysis(Analysis(specs={"m": Treatment(name="seed_mass", dimension=D.mass, unit="kg")}), root2)
ArtifactRegistryclass

ArtifactRegistry(root: str | Path) -> None

``root/<hash>.json`` per spec, plus ``root/index.tsv`` of ``hash\ttype``.

reg = ArtifactRegistry(root.parent / "registry")
FORMAT_VERSIONvalue

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

print(FORMAT_VERSION, "|", saved.provenance.seed, saved.provenance.created)
FormatErrorerror

The directory is not a readable ``analysis.axiom`` of a known format version.

try:
    load_analysis(root)
except FormatError as e:
    print("FormatError:", e)
Provenancespec

Provenance(axiom_version: str, created: str, hashes: dict[str, str] = {}, seed: int | None = None, environment: dict[str, str] = {}, notes: tuple[str, ...] = ())

Who made this, from what, with which seed.

prov = Provenance(axiom_version="0.0.0", created="2026-08-21T00:00:00+00:00", hashes=analysis.hashes(), seed=3)
environment_fingerprintfn

environment_fingerprint() -> dict[str, str]

print(environment_fingerprint())
load_analysisfn

load_analysis(path: str | Path) -> Analysis

loaded = load_analysis(root)
save_analysisfn

save_analysis(analysis: Analysis, path: str | Path, *, seed: int | None = None, overwrite: bool = False) -> Analysis

Write an analysis and return it with its provenance stamped.

saved = save_analysis(analysis, root, seed=3)

axiom.infer

sampler seam26 symbols · 3 notebooks

The sampler seam. Laplace in core; NumPyro and PyMC behind extras.

BACKEND_NAMESvalue

Built-in immutable sequence.

print(BACKEND_NAMES)
Backendclass

Backend(*args, **kwargs)

Something that turns a ``ModelSpec`` plus data into a ``Posterior``.

print(isinstance(lap, Backend), getattr(lap, "name", lap))
ConvergenceReportspec

ConvergenceReport(rows: tuple[ParameterDiagnostics, ...], divergences: int, thresholds: ConvergenceThresholds, converged: bool, n_chains: int, n_draws: int, nonfinite_draw_frac: float = 0.0)

Per-parameter diagnostics, the divergence count, and a verdict with its thresholds.

report: ConvergenceReport = diagnose(post)
ConvergenceThresholdsspec

ConvergenceThresholds(rhat_max: float = 1.01, ess_min: float = 400.0, divergences_max: int = 0)

What ``converged`` means. Defaults follow Vehtari et al. (2021).

print(row.passes(ConvergenceThresholds(rhat_max=1.05, ess_min=100.0)))
LaplaceBackendclass

LaplaceBackend()

The always-available backend: no sampler, a Gaussian at the mode.

backend = LaplaceBackend()
NUTS_SAMPLERSvalue

Built-in immutable sequence.

print("PyMC can dispatch NUTS to:", NUTS_SAMPLERS, "\n")
NumpyroBackendclass

NumpyroBackend(*, target_accept: float = 0.9, max_tree_depth: int = 10) -> None

``Backend`` over NumPyro's NUTS. ``optimize`` and ``laplace`` reuse ``infer.laplace``.

nuts = NumpyroBackend()
NutsSamplercallable

NutsSampler(*args, **kwargs)

choice: NutsSampler = "pymc"
ParameterDiagnosticsspec

ParameterDiagnostics(name: str, rhat: float | None, ess_bulk: float | None, ess_tail: float | None, mcse_mean: float | None, mean: float | None, sd: float | None, n_nonfinite: int = 0)

One row of the report: a scalar variable or one element of a vector one.

row: ParameterDiagnostics = report.row("tau")
PointEstimatespec

PointEstimate(theta: dict[str, float | tuple[float, ...]], log_density: float, converged: bool, method: str, n_iter: int, hessian_pd: bool | None = None, min_eigenvalue: float | None = None, newton_decrement: float | None = None)

A mode (MAP) in *constrained* coordinates with the optimizer's verdict.

pe: PointEstimate = backend.optimize(model, data, seed=0)
Posteriorclass

Posterior(draws: Mapping[str, ArrayLike], *, coords: Mapping[str, Sequence[Any]] | None = None, provenance: Mapping[str, Any] | None = None) -> None

Draws keyed by name, each ``(chain, draw, *shape)``, plus coords and provenance.

post = Posterior({"mu": good, "tau": np.exp(ar)}, provenance={"seed": 0, "divergences": 0})
PymcBackendclass

PymcBackend(*, target_accept: float = 0.9, nuts_sampler: NutsSampler = 'pymc') -> None

``Backend`` over PyMC's NUTS. ``optimize`` and ``laplace`` reuse ``infer.laplace``.

pymc_backend = PymcBackend(nuts_sampler=choice, target_accept=0.9)
SampleSettingsspec

SampleSettings(draws: int = 1000, tune: int = 1000, chains: int = 4, target_accept: float = 0.9)

How much to sample. The defaults are the usual NUTS defaults.

settings = SampleSettings(draws=300, tune=300, chains=2)
SamplerStatusclass

SamplerStatus(name: str, missing: tuple[str, ...] = (), error: str = '') -> None

Whether one NUTS sampler can actually be used, and why not when it cannot.

probed: SamplerStatus = samplers(probe=True)[name] if status.usable else status
diagnosefn

diagnose(posterior: SupportsPosterior, *, divergences: int | None = None, rhat_max: float = 1.01, ess_min: float = 400.0, divergences_max: int = 0) -> ConvergenceReport

Diagnose every variable of ``posterior`` and judge it against the thresholds.

report: ConvergenceReport = diagnose(post)
ess_bulkfn

ess_bulk(draws: ArrayLike) -> float

Bulk ESS: ESS of the rank-normalized split chains.

print(f"{name:8s} rhat={split_rhat(d):.3f} ess_bulk={ess_bulk(d):7.1f} ess_tail={ess_tail(d):7.1f} mcse={mcse_mean(d):.4f}")
ess_tailfn

ess_tail(draws: ArrayLike) -> float

Tail ESS: the smaller of the ESS of the 5% and 95% quantile indicators.

print(f"{name:8s} rhat={split_rhat(d):.3f} ess_bulk={ess_bulk(d):7.1f} ess_tail={ess_tail(d):7.1f} mcse={mcse_mean(d):.4f}")
find_modefn

find_mode(model: ModelSpec, data: Mapping[str, ArrayLike], *, init: Mapping[str, ArrayLike] | None = None, derivatives: Derivatives = 'auto', seed: int | None = None, allow_unverified: bool = False) -> PointEstimate | Unverified

Posterior mode in unconstrained space, reported in constrained coordinates.

mode = find_mode(model, data)
from_inference_datafn

from_inference_data(idata: Any) -> Posterior

Read the ``posterior`` group of an arviz object back into a ``Posterior``.

back = from_inference_data(idata)
get_backendfn

get_backend(name: str) -> Backend | Unsupported

Resolve a backend by name; ``Unsupported`` names the missing extra.

lap = get_backend("laplace")
hessian_atfn

hessian_at(model: ModelSpec, data: Mapping[str, ArrayLike], z: Mapping[str, ArrayLike], *, derivatives: Derivatives = 'auto') -> Array | Unverified

Hessian of the negative log density at unconstrained ``z``, in ``flat_layout`` order.

H_fd = hessian_at(model, data, z, derivatives="finite_difference")
laplacefn

laplace(model: ModelSpec, data: Mapping[str, ArrayLike], *, draws: int, seed: int | None, init: Mapping[str, ArrayLike] | None = None, chains: int = 1, derivatives: Derivatives = 'auto', allow_unverified: bool = False) -> Posterior | Unverified

Gaussian approximation at the mode, drawn and mapped back to constrained space.

post = backend.laplace(model, data, draws=4000, seed=0)
mcse_meanfn

mcse_mean(draws: ArrayLike) -> float

Monte-Carlo standard error of the posterior mean, ``sd / sqrt(ess)`` on the raw draws.

print(f"{name:8s} rhat={split_rhat(d):.3f} ess_bulk={ess_bulk(d):7.1f} ess_tail={ess_tail(d):7.1f} mcse={mcse_mean(d):.4f}")
samplersfn

samplers(*, probe: bool = False) -> dict[str, SamplerStatus]

Every NUTS sampler PyMC can dispatch to, and whether it is usable here.

probed: SamplerStatus = samplers(probe=True)[name] if status.usable else status
split_rhatfn

split_rhat(draws: ArrayLike) -> float

Rank-normalized split-R̂: the max of the bulk and the folded (tail) versions.

print(f"{name:8s} rhat={split_rhat(d):.3f} ess_bulk={ess_bulk(d):7.1f} ess_tail={ess_tail(d):7.1f} mcse={mcse_mean(d):.4f}")
to_inference_datafn

to_inference_data(posterior: Posterior) -> Any | Unsupported

Build an arviz object with a ``posterior`` group; provenance goes into its attrs.

idata = to_inference_data(post)

axiom.dynamics

domain31 symbols · 2 notebooks

A language for systems that are simultaneous or have time structure, compiled into ordinary expression trees.

AffineFormclass

AffineForm(coefficients: Mapping[str, Expr], intercept: Expr) -> None

``expr = sum_j coefficients[j] * unknown_j + intercept``, with expression coefficients.

form: AffineForm | None = affine_split(market.equation("quantity").rhs, ["quantity", "price"])
Blockspec

Block(variables: tuple[str, ...], simultaneous: bool)

One set of variables solved together, and whether it is a cycle.

first: Block = order.blocks[0]
BlockOrderspec

BlockOrder(blocks: tuple[Block, ...] = (), system: str = '')

The system's blocks in solution order, sources first.

order: BlockOrder = block_order(system)
BlockSolutionspec

BlockSolution(variables: tuple[str, ...], method: Literal['substitution', 'linear', 'fixed_point'], expressions: dict[str, Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque], residuals: dict[str, Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque] = {}, sweeps: int = 0, node_count: int, detail: dict[str, str] = {})

How one block was compiled, and into what.

solution: BlockSolution = solve_block(
    {v: market.equation(v).rhs for v in ("quantity", "price")},
    dims,
    simultaneous=True,
)
DynamicEquationspec

DynamicEquation(target: str, rhs: Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque, name: str = '')

``target[t] = rhs``, where ``rhs`` reads variables at ``t`` and earlier.

No executed statement uses this symbol — it appears in the notebooks only inside an import, which is all gate 12 requires. Worth a worked line.

DynamicSystemspec

DynamicSystem(name: str = '', variables: tuple[Variable, ...], equations: tuple[DynamicEquation, ...] = (), description: str = '')

A set of variables and one equation per endogenous variable.

DynamicSystem(variables=(Variable(name="y", dimension=NONE),), equations=())
DynamicsErrorerror

The system is malformed: an unknown reference, a missing equation, a bad name.

No executed statement uses this symbol — it appears in the notebooks only inside an import, which is all gate 12 requires. Worth a worked line.

Formcallable

Form(*args, **kwargs)

form: Form = marginal.form
LeadNotSupportedErrorerror

An equation refers to a future value; solving that needs rational expectations.

try:
    parse_ref("y.f1")
except LeadNotSupportedError as e:
    print("lead ->", e)
ParseErrorerror

The equation text does not parse, or names something undeclared.

try:
    parse_equations("y = wobble", variables=(Variable(name="y", dimension=NONE),))
except ParseError as e:
    print("unknown name ->", e)
Rolecallable

Role(*args, **kwargs)

role: Role = system.variable("inflow").role
SolveMethodcallable

SolveMethod(*args, **kwargs)

method: SolveMethod = solution.method
Unrolledspec

Unrolled(system: str = '', system_hash: str = '', form: Literal['conditional', 'marginal'], periods: int, expressions: dict[str, Const | Data | Param | Add | Mul | Div | Pow | Apply | Convolve | Reduce | Gather | Link | Opaque], solutions: tuple[BlockSolution, ...] = (), columns: tuple[str, ...] = (), edges: tuple[tuple[str, str], ...] = (), node_count: int, detail: dict[str, str] = {})

A compiled dynamic system: one expression per solved node, and how it got there.

No executed statement uses this symbol — it appears in the notebooks only inside an import, which is all gate 12 requires. Worth a worked line.

Variablespec

Variable(name: str, dimension: Dimension, role: Literal['endogenous', 'exogenous'] = 'endogenous', observed: bool = True, initial: float = 0.0, description: str = '')

One named quantity of the system, with its dimension and its role.

parse_equations("y = wobble", variables=(Variable(name="y", dimension=NONE),))
affine_splitfn

affine_split(expr: Expr, unknowns: Sequence[str]) -> AffineForm | None

Split ``expr`` into coefficients on ``unknowns`` and a remainder, or ``None``.

form: AffineForm | None = affine_split(market.equation("quantity").rhs, ["quantity", "price"])
block_orderfn

block_order(system: DynamicSystem) -> BlockOrder

Block-triangular decomposition of the contemporaneous dependency graph.

order: BlockOrder = block_order(system)
conditional_formfn

conditional_form(system: DynamicSystem, *, sweeps: int = 0, max_nodes: int = 20000) -> Unrolled | Unsupported

Compile one generic period: ``y = f(exogenous at t, every lag as a column)``.

print("without sweeps:", conditional_form(saturating).reason[:120])
lag_reffn

lag_ref(var: str, lag: int) -> str

``("y", 0) -> "y"``; ``("y", 2) -> "y.l2"``.

print(lag_ref("stock", 2), parse_ref("stock.l2"), time_ref("stock", 3))
lagged_columnsfn

lagged_columns(system: DynamicSystem) -> tuple[str, ...]

Every lagged reference the conditional form needs as a materialized column.

print("columns:", conditional.columns, "| lagged:", lagged_columns(system))
parse_equationsfn

parse_equations(text: str, *, variables: Sequence[Variable], parameters: Sequence[Param] = ()) -> tuple[DynamicEquation, ...]

Parse ``target = expression`` lines against declared variables and parameters.

parse_equations("y = wobble", variables=(Variable(name="y", dimension=NONE),))
parse_reffn

parse_ref(name: str) -> tuple[str, int]

``"y.l2" -> ("y", 2)``; ``"y" -> ("y", 0)``. A lead raises.

print(lag_ref("stock", 2), parse_ref("stock.l2"), time_ref("stock", 3))
parse_systemfn

parse_system(text: str, *, variables: Sequence[Variable], parameters: Sequence[Param] = (), name: str = '', description: str = '') -> DynamicSystem

Parse equations and assemble the ``DynamicSystem``, dimension checks included.

parse_system(
    "y = x",
    variables=(
        Variable(name="y", dimension=D.outcome),
        Variable(name="x", dimension=D.currency, role="exogenous"),
    ),
)
prepare_panelfn

prepare_panel(system: DynamicSystem, frame: DataFrame, *, unit: str | None = None, time: str) -> DataFrame

Add the lagged columns the conditional form reads, per unit, in time order.

prepared = prepare_panel(system, frame, unit="unit", time="t")
reduced_formfn

reduced_form(equations: Mapping[str, Expr], dimensions: Mapping[str, Dimension], *, max_nodes: int = 20000) -> dict[str, Expr] | Unsupported

Solve ``v = rhs_v(v, ...)`` exactly when every right-hand side is affine in the unknowns.

solved = reduced_form(
    {v: market.equation(v).rhs for v in ("quantity", "price")},
    dims,
)
refs_infn

refs_in(expr: Model) -> tuple[tuple[str, int], ...]

Every ``(variable, lag)`` the expression reads, by first appearance.

print(refs_in(system.equation("stock").rhs))
solve_blockfn

solve_block(equations: Mapping[str, Expr], dimensions: Mapping[str, Dimension], *, simultaneous: bool, start: Mapping[str, Expr] | None = None, sweeps: int = 0, max_nodes: int = 20000) -> BlockSolution | Unsupported

Compile one block: substitution, exact reduced form, or declared fixed-point sweeps.

solution: BlockSolution = solve_block(
    {v: market.equation(v).rhs for v in ("quantity", "price")},
    dims,
    simultaneous=True,
)
strongly_connected_componentsfn

strongly_connected_components(nodes: tuple[str, ...], edges: tuple[tuple[str, str], ...]) -> tuple[tuple[str, ...], ...]

Tarjan's SCCs, each sorted, the tuple in reverse topological order (sources last).

print(strongly_connected_components(("a", "b", "c"), (("a", "b"), ("b", "a"), ("b", "c"))))
time_reffn

time_ref(var: str, period: int) -> str

``("y", 3) -> "y.t3"`` — the name of ``y`` at absolute period 3.

print(lag_ref("stock", 2), parse_ref("stock.l2"), time_ref("stock", 3))
to_model_specfn

to_model_spec(unrolled: Unrolled, outcome: str, *, likelihood: Likelihood, priors: Mapping[str, Prior], extra_parameters: Sequence[Param] = (), period: int | None = None, name: str = '') -> ModelSpec

Wrap one compiled node as a ``ModelSpec`` that any backend fits.

model = to_model_spec(
    conditional,
    "stock",
    likelihood=Likelihood(family="normal", scale="sigma"),
    priors={
        "decay": Prior(family="beta", hyper={"alpha": 1.5, "beta": 1.5}),
        "beta": Prior(family="lognormal", hyper={"mu": 0.0, "sigma": 1.0}),
    },
    extra_parameters=(
        Param(name="sigma", dimension=D.outcome, prior=Prior(family="halfnormal", hyper={"sigma": 1.0})),
    ),
)
unrollfn

unroll(system: DynamicSystem, periods: int, *, sweeps: int = 0, max_nodes: int = 20000) -> Unrolled | Unsupported

Compile every period into expressions over exogenous inputs alone.

marginal = unroll(system, periods=4)
unrolled_edgesfn

unrolled_edges(system: DynamicSystem, periods: int, *, reduced: bool = True) -> tuple[tuple[str, str], ...]

Edges of the time-indexed graph, ``("x.t1", "y.t2")``, sorted and deduplicated.

print("as written (may be cyclic):", unrolled_edges(system, periods=2, reduced=False))

axiom.identify

domain65 symbols · 5 notebooks · walkthrough

Causal graphs, back-door / front-door / IV routes, transport verdicts.

CausalGraphspec

CausalGraph(nodes: tuple[str, ...] = (), edges: tuple[tuple[str, str], ...] = (), bidirected: tuple[tuple[str, str], ...] = (), unmeasured: tuple[str, ...] = (), selection: tuple[str, ...] = (), feedback: bool = False, name: str = '')

A directed acyclic graph over named nodes, with optional bidirected edges.

g = CausalGraph.from_edges("Z -> X, Z -> Y, X -> M, M -> Y, X <-> W, W -> Y", unmeasured=["W"], name="toy")
ClusterDAGspec

ClusterDAG(clusters: dict[str, tuple[str, ...]], edges: tuple[tuple[str, str], ...] = (), bidirected: tuple[tuple[str, str], ...] = (), unmeasured: tuple[str, ...] = (), name: str = '')

A DAG over clusters of variables, with the micro-level structure left open.

market = ClusterDAG(
    clusters={"cost": ("wage", "fuel"), "price": ("price",), "demand": ("units",)},
    edges=(("cost", "price"), ("price", "demand")),
    name="a two-block market",
)
Densityspec

Density(node: Literal['density'] = 'density', outcomes: tuple[str, ...], given: tuple[str, ...] = ())

``P(outcomes | given)`` — a conditional of the observational distribution.

ratio_example: Ratio = Ratio(
    numerator=Density(outcomes=("X", "Y")), denominator=Density(outcomes=("X",))
)
EndogeneityTestspec

EndogeneityTest(statistic: float, p_value: float, df: int, method: str, conclusion: Literal['endogenous', 'exogenous'], alpha: float = 0.05, treatment: str = '', outcome: str = '', detail: dict[str, float] = {})

Outcome of an endogeneity test, with the level the conclusion used.

print(dwh if not isinstance(dwh, EndogeneityTest) else (dwh.conclusion, round(dwh.statistic, 2), dwh.p_value))
Formulacallable

Formula(*args, **kwargs)

Runtime representation of an annotated type.

formula: Formula = front_door.formula
FrontDoorRoutespec

FrontDoorRoute(mediators: tuple[str, ...], treatment: str, outcome: str)

A front-door identification route: ``P(y | do(x))`` through ``mediators``.

route = FrontDoorRoute(mediators=("M",), treatment="X", outcome="Y")
GraphErrorerror

The graph is malformed (cycle, unknown node, self-edge).

try:
    CausalGraph.from_edges("A -> B, B -> C, C -> A")
except GraphError as e:
    print("refused:", e)
Hedgespec

Hedge(root: tuple[str, ...], subset: tuple[str, ...], variables: tuple[str, ...])

The witness that an effect is *not* identifiable — a proof, not a failed search.

hedge: Hedge = blocked.hedge
IdentificationVerdictspec

IdentificationVerdict(treatment: str, outcome: str, graph_hash: str, verdict: Verdict, route: Literal['no_causal_path', 'backdoor', 'frontdoor', 'instrument', 'backdoor_unmeasured', 'none'], adjustment_set: tuple[str, ...] = (), mediators: tuple[str, ...] = (), instrument: str = '', conditioning: tuple[str, ...] = (), unmeasured_required: tuple[str, ...] = (), alternatives: tuple[Literal['no_causal_path', 'backdoor', 'frontdoor', 'instrument', 'backdoor_unmeasured', 'none'], ...] = (), search_limits_hit: tuple[str, ...] = (), ... and 1 more field)

What the graph says about ``treatment -> outcome``, and by which route.

v: IdentificationVerdict = identify(simple, "X", "Y")
IdentifiedEffectspec

IdentifiedEffect(treatment: tuple[str, ...], outcome: tuple[str, ...], conditioned: tuple[str, ...] = (), graph_hash: str = '', formula: Optional[Density | Product | Marginal | Ratio] = None, hedge: Hedge | None = None, verdict: Verdict, detail: dict[str, str] = {})

What ID returned: a formula, or a hedge, and a verdict either way.

result: IdentifiedEffect = identify_effect(graph, "X", "Y")
InstrumentRoutespec

InstrumentRoute(instrument: str, conditioning: tuple[str, ...] = (), treatment: str, outcome: str)

An instrumental-variable route: ``instrument`` for ``treatment -> outcome``.

print(InstrumentRoute(instrument="Z", conditioning=("W",), treatment="X", outcome="Y"))
JointTableclass

JointTable(names: tuple[str, ...], table: Array) -> None

A joint distribution over discrete variables, as an array with one axis per variable.

joint = JointTable(names=("X", "Y", "Z"), table=observational)
LinearEstimatespec

LinearEstimate(estimate: float, se: float, n: int, method: Literal['ols', '2sls', 'frontdoor'], treatment: str, outcome: str, covariates: tuple[str, ...] = (), detail: dict[str, float] = {})

A point estimate with its classical standard error and provenance.

naive: LinearEstimate = ols(frame, "Y", "X")
Marginalspec

Marginal(node: Literal['marginal'] = 'marginal', over: tuple[str, ...], term: Density | Product | Marginal | Ratio)

``sum over `over` of `term``` — marginalization.

print("is it a Marginal of a Product of Density terms?", isinstance(formula, Marginal))
MixedGraphspec

MixedGraph(nodes: tuple[str, ...] = (), edges: tuple[tuple[str, str], ...] = (), bidirected: tuple[tuple[str, str], ...] = (), unmeasured: tuple[str, ...] = (), name: str = '')

A directed mixed graph. Directed edges may form cycles; separation is sigma.

loop = MixedGraph.from_edges("x -> a, a -> b, b -> a, b -> y")
Productspec

Product(node: Literal['product'] = 'product', factors: tuple[Density | Product | Marginal | Ratio, ...])

A product of formulas.

product_example: Product = Product(
    factors=(Density(outcomes=("Y",), given=("X",)), Density(outcomes=("X",)))
)
Ratiospec

Ratio(node: Literal['ratio'] = 'ratio', numerator: Density | Product | Marginal | Ratio, denominator: Density | Product | Marginal | Ratio)

``numerator / denominator``; how a conditional of a derived expression is written.

ratio_example: Ratio = Ratio(
    numerator=Density(outcomes=("X", "Y")), denominator=Density(outcomes=("X",))
)
Rolecallable

Role(*args, **kwargs)

r: dict[str, Role] = roles(m_bias, "X", "Y")
RoleAssignmentspec

RoleAssignment(graph_hash: str, treatment: str, outcome: str, roles: dict[str, Literal['treatment', 'outcome', 'confounder', 'mediator', 'collider', 'instrument', 'proxy', 'descendant_of_outcome', 'neutral']])

The roles of every node of one graph relative to one treatment–outcome pair.

ra: RoleAssignment = assign_roles(g, "X", "Y")
Routecallable

Route(*args, **kwargs)

pref: tuple[Route, ...] = ("frontdoor", "backdoor")
SequentialPlanspec

SequentialPlan(stages: tuple[str, ...], outcome: str, graph_hash: str, adjustments: tuple[tuple[str, ...], ...], verdict: Verdict, static_adjustment_fails: bool = False, detail: dict[str, str] = {})

Per-stage adjustment sets for a sequence of treatments, and the verdict on them.

plan: SequentialPlan = sequential_plan(graph, ["dose.t0", "dose.t1", "dose.t2"], "outcome.t2")
TransportVerdictspec

TransportVerdict(treatment: str, outcome: str, verdict: Verdict, s_admissible_set: tuple[str, ...] = (), mediators: tuple[str, ...] = (), given: tuple[str, ...] | None = None, selection: tuple[str, ...] = (), graph_hash: str = '', formula: str = '', missing: tuple[str, ...] = ())

Whether, and by what formula, an effect transports from source to target.

unadjusted: TransportVerdict = transport_verdict(g, "X", "Y", given=())
acyclifyfn

acyclify(graph: MixedGraph | CausalGraph) -> CausalGraph

The acyclification of a directed mixed graph (Mooij & Claassen 2020, Def. 3).

print("acyclified:", acyclify(loop).to_text())
adjustment_setsfn

adjustment_sets(graph: CausalGraph, x: str, y: str, *, measured_only: bool = True, max_candidates: int = 16) -> AdjustmentSets | Unsupported

Every back-door admissible set among the candidates, deterministically ordered.

print(adjustment_sets(simple, "X", "Y"), minimal_adjustment_sets(simple, "X", "Y"))
admissible_set_existsfn

admissible_set_exists(graph: CausalGraph, x: str, y: str, *, measured_only: bool = True) -> bool

Whether any back-door admissible set exists, in polynomial time.

print(canonical_adjustment_set(simple, "X", "Y"), admissible_set_exists(simple, "X", "Y"))
assign_rolesfn

assign_roles(graph: CausalGraph, x: str, y: str) -> RoleAssignment

``roles`` packaged as a ``Spec`` with the graph's content hash.

ra: RoleAssignment = assign_roles(g, "X", "Y")
backdoor_admissiblefn

backdoor_admissible(graph: CausalGraph, x: str, y: str, z: Iterable[str]) -> bool

Pearl's back-door criterion for ``z`` relative to ``(x, y)``.

print(backdoor_admissible(simple, "X", "Y", ["Z"]), backdoor_admissible(simple, "X", "Y", []))
canonical_adjustment_setfn

canonical_adjustment_set(graph: CausalGraph, x: str, y: str, *, measured_only: bool = True) -> frozenset[str] | None

The canonical back-door set, or ``None`` when no admissible set exists.

print(canonical_adjustment_set(simple, "X", "Y"), admissible_set_exists(simple, "X", "Y"))
compatiblefn

compatible(cdag: ClusterDAG, graph: CausalGraph) -> bool

Whether a micro-level DAG is compatible with the cluster-level claim.

print("compatible:", compatible(market, micro))
conditional_instrumentsfn

conditional_instruments(graph: CausalGraph, x: str, y: str, *, measured_only: bool = True, max_candidates: int = 12, max_conditioning: int = 2) -> tuple[tuple[str, frozenset[str]], ...] | Unsupported

All ``(z, W)`` pairs with ``|W| <= max_conditioning`` that satisfy the criterion.

print(instruments(civ, "X", "Y"), conditional_instruments(civ, "X", "Y"))
directly_transportablefn

directly_transportable(graph: CausalGraph, x: str, y: str) -> bool

Whether ``P*(y | do(x)) = P(y | do(x))`` (B&P 2014 Def. 7).

print("direct:", directly_transportable(g, "X", "Y"))
districtsfn

districts(graph: CausalGraph) -> tuple[tuple[str, ...], ...]

The C-components: connected components of the bidirected part (Tian & Pearl 2002).

print("districts:", districts(admg))
durbin_wu_hausmanfn

durbin_wu_hausman(frame: DataFrame, y: str, x: str, instruments: str | Sequence[str], covariates: str | Sequence[str] = (), alpha: float = 0.05) -> EndogeneityTest | Unverified

Control-function Durbin–Wu–Hausman test of the exogeneity of ``x``.

dwh = durbin_wu_hausman(frame, "Y", "X", instruments=["Z"])
frontdoor_admissiblefn

frontdoor_admissible(graph: CausalGraph, x: str, y: str, m: Iterable[str]) -> bool

Whether ``m`` satisfies the front-door criterion relative to ``(x, y)``.

print(frontdoor_admissible(fd_graph, "X", "Y", ["M"]), frontdoor_sets(fd_graph, "X", "Y"))
frontdoor_linearfn

frontdoor_linear(frame: DataFrame, y: str, x: str, mediators: str | Sequence[str], covariates: str | Sequence[str] = ()) -> LinearEstimate

Linear front-door estimate of the effect of ``x`` on ``y`` through ``mediators``.

fd = frontdoor_linear(frame, "Y", "X", mediators=v.mediators)
frontdoor_setsfn

frontdoor_sets(graph: CausalGraph, x: str, y: str, *, measured_only: bool = True, max_candidates: int = 12) -> tuple[frozenset[str], ...] | Unsupported

Every front-door admissible mediator set, smallest first, then lexicographic.

print(frontdoor_admissible(fd_graph, "X", "Y", ["M"]), frontdoor_sets(fd_graph, "X", "Y"))
hausman_iv_vs_olsfn

hausman_iv_vs_ols(est_ols: LinearEstimate, est_2sls: LinearEstimate, alpha: float = 0.05) -> EndogeneityTest | Unverified

Classic Hausman contrast of an OLS and a 2SLS estimate of the same effect.

h = hausman_iv_vs_ols(ols(frame, "Y", "X"), two_stage_least_squares(frame, "Y", "X", instruments=["Z"]))
identifyfn

identify(graph: CausalGraph, treatment: str, outcome: str, *, prefer: Sequence[Route] = ..., has_carryover: bool = False, max_candidates: int = 16) -> IdentificationVerdict

Decide how (and whether) ``treatment -> outcome`` is identified in ``graph``.

v: IdentificationVerdict = identify(simple, "X", "Y")
identify_cluster_effectfn

identify_cluster_effect(cdag: ClusterDAG, treatment: str | Iterable[str], outcome: str | Iterable[str]) -> IdentifiedEffect

``P(outcome cluster | do(treatment cluster))`` by running ID on the cluster graph.

print("effect of the cost block on demand:", identify_cluster_effect(market, "cost", "demand").to_text())
identify_conditional_effectfn

identify_conditional_effect(graph: CausalGraph, treatment: str | Iterable[str], outcome: str | Iterable[str], conditioned: Iterable[str]) -> IdentifiedEffect

``P(outcome | do(treatment), conditioned)`` by IDC (Shpitser & Pearl 2008).

conditional = identify_conditional_effect(
    CausalGraph.from_edges("Z -> X, Z -> Y, X -> Y, X -> W, W -> Y"), "X", "Y", ["W"]
)
identify_effectfn

identify_effect(graph: CausalGraph, treatment: str | Iterable[str], outcome: str | Iterable[str]) -> IdentifiedEffect

``P(outcome | do(treatment))`` by the ID algorithm: the estimand, or the hedge.

result: IdentifiedEffect = identify_effect(graph, "X", "Y")
instrument_admissiblefn

instrument_admissible(graph: CausalGraph, x: str, y: str, z: str, conditioning: Iterable[str] = ()) -> bool

Whether ``z`` is an instrument for the total effect of ``x`` on ``y`` given ``W``.

print(instruments(iv_graph, "X", "Y"), instrument_admissible(iv_graph, "X", "Y", "Z"))
instrumentsfn

instruments(graph: CausalGraph, x: str, y: str, *, measured_only: bool = True) -> tuple[str, ...]

All unconditional instruments (``W = ∅``) for ``x -> y``, sorted by name.

print(instruments(iv_graph, "X", "Y"), instrument_admissible(iv_graph, "X", "Y", "Z"))
latent_projectionfn

latent_projection(graph: CausalGraph, keep: Iterable[str] | None = None) -> CausalGraph

Project the unmeasured nodes out, leaving an ADMG over the ones kept.

admg = latent_projection(explicit)
minimal_adjustment_setsfn

minimal_adjustment_sets(graph: CausalGraph, x: str, y: str, *, measured_only: bool = True, max_candidates: int = 16) -> AdjustmentSets | Unsupported

Inclusion-minimal back-door admissible sets, ordered as ``adjustment_sets``.

print(adjustment_sets(simple, "X", "Y"), minimal_adjustment_sets(simple, "X", "Y"))
minimal_s_admissible_setsfn

minimal_s_admissible_sets(graph: CausalGraph, x: str, y: str, *, measured_only: bool = True, max_candidates: int = 14) -> tuple[frozenset[str], ...] | Unsupported

The S-admissible sets with no S-admissible proper subset, smallest first.

print(s_admissible_sets(g, "X", "Y"), minimal_s_admissible_sets(g, "X", "Y"))
olsfn

ols(frame: DataFrame, y: str, x: str, covariates: str | Sequence[str] = ()) -> LinearEstimate

Coefficient on ``x`` in OLS of ``y`` on ``[1, x, covariates]``.

naive: LinearEstimate = ols(frame, "Y", "X")
parse_edgesfn

parse_edges(text: str) -> tuple[tuple[Edge, ...], tuple[Edge, ...]]

Parse ``"Z -> X, Z -> Y, X -> Y, A <-> B"`` into (directed, bidirected) edge tuples.

print(parse_edges("A -> B; C <- B"))
requires_unmeasuredfn

requires_unmeasured(graph: CausalGraph, x: str, y: str) -> bool

True iff adjustment is possible only through a node the panel does not observe.

print("needs an unmeasured node:", requires_unmeasured(g, "X", "Y"))
rolesfn

roles(graph: CausalGraph, x: str, y: str) -> dict[str, Role]

Assign each node a descriptive role relative to ``(x, y)``.

r: dict[str, Role] = roles(m_bias, "X", "Y")
s_admissiblefn

s_admissible(graph: CausalGraph, x: str, y: str, z: Iterable[str]) -> bool

Whether ``Z`` is S-admissible for the effect of ``x`` on ``y`` (B&P 2014 Def. 8).

print("S-admissible {Z}:", s_admissible(g, "X", "Y", ["Z"]), "| {}:", s_admissible(g, "X", "Y", []))
s_admissible_setsfn

s_admissible_sets(graph: CausalGraph, x: str, y: str, *, measured_only: bool = True, max_candidates: int = 14) -> tuple[frozenset[str], ...] | Unsupported

Every S-admissible set, smallest first, then lexicographic.

print(s_admissible_sets(g, "X", "Y"), minimal_s_admissible_sets(g, "X", "Y"))
selection_diagramfn

selection_diagram(graph: CausalGraph) -> CausalGraph

The selection diagram of ``graph`` as a plain DAG (B&P 2014 Def. 4).

sd = selection_diagram(g)
sequential_backdoor_admissiblefn

sequential_backdoor_admissible(graph: CausalGraph, treatments: Sequence[str], outcome: str, adjustments: Sequence[Iterable[str]], *, measured_only: bool = True) -> tuple[bool, str]

The sequential back-door criterion; ``(True, "")`` or ``(False, why)``.

ok, why = sequential_backdoor_admissible(
    graph, ["dose.t0", "dose.t1"], "outcome.t2", [("outcome.t1",), ()]
)
sequential_ignorabilityfn

sequential_ignorability(graph: CausalGraph, stages: Sequence[str], outcome: str, adjustments: Sequence[Iterable[str]]) -> tuple[bool, str]

Sequential ignorability, read off one SWIG with every stage split.

print("SWIG agrees   :", sequential_ignorability(graph, stages, "outcome.t2", plan.adjustments))
sequential_planfn

sequential_plan(graph: CausalGraph, treatments: Sequence[str], outcome: str, *, candidates: Iterable[str] | None = None, prune: bool = True) -> SequentialPlan

Search for per-stage adjustment sets satisfying the sequential back-door criterion.

plan: SequentialPlan = sequential_plan(graph, ["dose.t0", "dose.t1", "dose.t2"], "outcome.t2")
sigma_separatedfn

sigma_separated(graph: MixedGraph | CausalGraph, x: str | Iterable[str], y: str | Iterable[str], z: Iterable[str] = ()) -> bool

Sigma-separation of ``X`` and ``Y`` given ``Z`` (Forré & Mooij 2017).

print("   quantity indep price given the block's parents?",
      structural.sigma_separated("quantity.t0", "price.t0", ["cost.t0"]))
single_world_ignorabilityfn

single_world_ignorability(graph: CausalGraph, treatment: str, outcome: str, adjustment: Iterable[str] = (), *, label: str = '') -> bool

Whether ``Y(x)`` is independent of ``X`` given ``Z`` on the SWIG.

print(f"Y(x) indep X | {adjustment or '{}'}:", single_world_ignorability(g, "X", "Y", adjustment))
swigfn

swig(graph: CausalGraph, interventions: Mapping[str, str] | Iterable[str]) -> CausalGraph

Split every intervened node; relabel its descendants as potential outcomes.

print("swig    :", swig(g, ["X"]).to_text())
transport_verdictfn

transport_verdict(graph: CausalGraph, x: str, y: str, *, given: Iterable[str] | None = None, max_candidates: int = 14) -> TransportVerdict

Read the transportability of ``P*(y | do(x))`` off ``graph``'s selection diagram.

unadjusted: TransportVerdict = transport_verdict(g, "X", "Y", given=())
trivially_transportablefn

trivially_transportable(graph: CausalGraph, x: str, y: str, *, max_candidates: int = 14) -> bool

Whether ``P*(y | do(x))`` is identifiable from the target alone (B&P 2014 Def. 6).

print("trivially (from target data alone):", trivially_transportable(g, "X", "Y"))
two_stage_least_squaresfn

two_stage_least_squares(frame: DataFrame, y: str, x: str, instruments: str | Sequence[str], covariates: str | Sequence[str] = ()) -> LinearEstimate

2SLS of ``y`` on ``x`` with ``instruments``, adjusting for ``covariates``.

iv = two_stage_least_squares(frame, "Y", "X", instruments=[v.instrument])
unrolled_graphfn

unrolled_graph(system: DynamicSystem, periods: int, *, name: str = '') -> CausalGraph

The acyclification of the time-indexed graph: an acyclic graph, honestly obtained.

graph = unrolled_graph(system, periods=3)
unrolled_mixed_graphfn

unrolled_mixed_graph(system: DynamicSystem, periods: int, *, name: str = '') -> MixedGraph

The time-indexed graph exactly as the equations were written, cycles included.

structural = unrolled_mixed_graph(market, periods=2)
weak_instrument_checkfn

weak_instrument_check(est: LinearEstimate, threshold: float = 10.0) -> Assumption

The instrument-strength assumption, checked against the first-stage F.

strength = weak_instrument_check(iv)

axiom.discover

domain25 symbols · 2 notebooks

Learn the graph instead of assuming it: essential graphs, GES/GIES, bootstrap stability, and FCI when a common cause may be unmeasured.

Datasetclass

Dataset(values: Array, names: tuple[str, ...], regimes: tuple[frozenset[str], ...]) -> None

Rows of measurements, each labelled with what was intervened on when it was taken.

return Dataset(np.column_stack([values[n] for n in order]), tuple(order), (frozenset(do),) * n)
DiscoveryResultspec

DiscoveryResult(essential: EssentialGraph, score: float, n_observations: int, targets: tuple[tuple[str, ...], ...] = (), phases: tuple[str, ...] = (), steps: tuple[str, ...] = (), penalty: float, limits_hit: tuple[str, ...] = (), detail: dict[str, str] = {})

What a structure search found, and how far it is entitled to be believed.

found: DiscoveryResult = ges(GaussianBIC(observational))
EdgeSupportspec

EdgeSupport(a: str, b: str, adjacent: float, forward: float, backward: float, undirected: float)

How often one pair of variables was joined, and which way it pointed.

assert isinstance(support, EdgeSupport)
EssentialGraphspec

EssentialGraph(nodes: tuple[str, ...] = (), directed: tuple[tuple[str, str], ...] = (), undirected: tuple[tuple[str, str], ...] = (), targets: tuple[tuple[str, ...], ...] = (), name: str = '')

A partially directed graph: compelled edges directed, the rest undirected.

partial = EssentialGraph(nodes=truth.nodes, directed=(("a", "b"),), undirected=(("b", "c"),))
GaussianBICclass

GaussianBIC(data: Dataset, penalty: float = 1.0) -> None

Decomposable BIC for linear-Gaussian structure, aware of which rows were randomized.

found: DiscoveryResult = ges(GaussianBIC(observational))
IndependenceResultspec

IndependenceResult(x: str, y: str, given: tuple[str, ...] = (), correlation: float, p_value: float, n: int, statistic: float = 0.0)

One conditional-independence test: what was asked, and both answers.

assert isinstance(check, IndependenceResult)
Markcallable

Mark(*args, **kwargs)

PAGspec

PAG(nodes: tuple[str, ...] = (), edges: tuple[PagEdge, ...] = (), limits_hit: tuple[str, ...] = (), detail: dict[str, str] = {})

A partial ancestral graph: what the data determines when latents are possible.

pag: PAG = fci(observed, oracle_independence(world))
PagEdgespec

PagEdge(a: str, b: str, mark_a: Literal['circle', 'arrow', 'tail'] = 'circle', mark_b: Literal['circle', 'arrow', 'tail'] = 'circle')

One edge of a PAG: two variables and the mark at each end.

assert isinstance(edge, PagEdge)
PartialCorrelationclass

PartialCorrelation(data: Dataset) -> None

A Gaussian conditional-independence test over a dataset.

check = PartialCorrelation(measured).test("seed", "harvest")
StabilityReportspec

StabilityReport(variables: tuple[str, ...], edges: tuple[EdgeSupport, ...] = (), n_bootstrap: int, n_rows: int, targets: tuple[tuple[str, ...], ...] = (), penalty: float, seed: int | None = None, detail: dict[str, str] = {})

Per-edge support across bootstrap resamples of the same data.

assert isinstance(stability, StabilityReport)
consistent_extensionfn

consistent_extension(graph: EssentialGraph) -> CausalGraph

One DAG from the class, by Dor & Tarsi's (1992) algorithm.

print("a representative:", consistent_extension(essential).to_text())
consistent_extensionsfn

consistent_extensions(graph: EssentialGraph, *, limit: int = 4096) -> tuple[CausalGraph, ...]

Every DAG in the class, by orienting the undirected edges every way.

for member in consistent_extensions(essential):
    print(" ", member.to_text())
cpdagfn

cpdag(graph: CausalGraph) -> EssentialGraph

The essential graph of a DAG: what observational data could ever orient.

print("chain    :", cpdag(CausalGraph.from_edges("a -> b, b -> c")).to_text())
edge_stabilityfn

edge_stability(data: Dataset, *, n_bootstrap: int = 100, penalty: float = 1.0, targets: Sequence[Iterable[str]] | None = None, seed: int | None = None) -> StabilityReport

Resample the rows, re-run the search, and count how often each edge returns.

stability = edge_stability(trial, n_bootstrap=60, seed=7)
fcifn

fci(variables: Sequence[str], independent: Independent, *, max_conditioning: int = 3, name: str = '') -> PAG

Discover a PAG without assuming causal sufficiency.

pag: PAG = fci(observed, oracle_independence(world))
fci_from_datafn

fci_from_data(data: Dataset, *, alpha: float = 0.05, max_conditioning: int = 3, variables: Iterable[str] | None = None, name: str = '') -> PAG

Run FCI with a partial-correlation test at level ``alpha``.

from_data = fci_from_data(measured, alpha=0.01)
gesfn

ges(score: GaussianBIC, *, name: str = '') -> DiscoveryResult

Greedy equivalence search on observational data: forward, then backward.

found: DiscoveryResult = ges(GaussianBIC(observational))
giesfn

gies(score: GaussianBIC, targets: Sequence[Iterable[str]] | None = None, *, name: str = '') -> DiscoveryResult

Greedy interventional equivalence search: the same, over I-essential graphs.

one = gies(GaussianBIC(pooled))
interventional_essential_graphfn

interventional_essential_graph(graph: CausalGraph, targets: Sequence[Iterable[str]]) -> EssentialGraph

What the data could orient after intervening on each target in the family.

after = interventional_essential_graph(truth, [["a"]])
markov_equivalentfn

markov_equivalent(first: CausalGraph, second: CausalGraph) -> bool

Same skeleton and same v-structures — the classical characterization.

print("all Markov equivalent to the truth:",
      all(markov_equivalent(m, truth) for m in consistent_extensions(essential)))
meek_closurefn

meek_closure(graph: EssentialGraph) -> EssentialGraph

Orient every edge forced by what is already oriented (Meek 1995, rules 1-4).

print("R1 propagation:", meek_closure(partial).to_text())
oracle_independencefn

oracle_independence(graph: CausalGraph) -> Independent

A perfect conditional-independence test, answered by d-separation in a known graph.

pag: PAG = fci(observed, oracle_independence(world))
orientation_gainfn

orientation_gain(graph: CausalGraph, targets: Sequence[Iterable[str]], *, baseline: Sequence[Iterable[str]] = ()) -> tuple[Edge, ...]

The edges an experiment would orient that are undirected without it.

gain = orientation_gain(truth, [target])
v_structuresfn

v_structures(graph: CausalGraph) -> frozenset[tuple[str, str, str]]

``(a, c, b)`` with ``a -> c <- b`` and ``a``, ``b`` non-adjacent; ``a < b``.

print("v-structures of the collider:", v_structures(CausalGraph.from_edges("a -> c, b -> c")))

axiom.estimands

domain19 symbols · 3 notebooks

Declare the number you want as a nine-facet object, then realize it.

Estimandspec

Estimand(name: str, quantity: Quantity, treatment: Treatment, intervention: Intervention, reference: Intervention | None = None, outcome: Outcome, population: Population, window: TimeWindow, level: Level, conditioning: tuple[str, ...] = (), dimension: Dimension, description: str = '')

A named, versioned, content-hashed counterfactual quantity. All facets required.

lift = Estimand(
    name="lift_at_100",
    quantity=Quantity(kind="contrast"),
    treatment=fertilizer,
    intervention=Intervention(doses={"fertilizer": 100.0}, version="granular"),
    reference=Intervention(doses={"fertilizer": 0.0}, version="granular"),
    outcome=yield_total,
    population=north,
    window=season,
    level=Level(unit="cluster", interference="none"),
    conditioning=(),
    dimension=D.outcome,
    description="season-total yield lift from 100 USD of granular fertilizer vs none, north region",
)
EstimandRegistryclass

EstimandRegistry(estimands: Iterable[Estimand] = ()) -> None

Estimands by name, with their content hashes.

registry: EstimandRegistry = standard_estimands(
    treatment=spec.treatments[0], outcome=spec.outcome, population=Population(name="all"),
    window=TimeWindow(start=0, stop=1), level=Level(unit="individual"), dose=60.0, reference_dose=0.0,
)
EstimandResultspec

EstimandResult(estimand_hash: str, estimand_name: str, kind: Literal['contrast', 'marginal', 'ratio', 'elasticity', 'area'], summary: Summary, dimension: Dimension, unit: str | None = None, status: Literal['identified', 'downgraded', 'blocked', 'unsupported'], identification: Verdict | None = None, assumptions: tuple[Assumption, ...] = (), ledger: tuple[LedgerLine, ...] = (), n_draws: int, producer_hash: str = '', ... and 1 more field)

A realized estimand: the summary, its dimension and unit, and how it was licensed.

if isinstance(asserted, EstimandResult):
    status: ResultStatus = asserted.status
    print(status, [line.kind for line in asserted.ledger], [a.name for a in asserted.assumptions])
FACETSvalue

Built-in immutable sequence.

Facetcallable

Facet(*args, **kwargs)

f: Facet = "intervention"
FacetDiffspec

FacetDiff(facet: Literal['quantity', 'intervention', 'outcome', 'population', 'window', 'level', 'conditioning', 'dimension'], statement: str, assumptions: tuple[Assumption, ...] = (), blocked: Blocked | None = None, corrections: tuple[str, ...] = ())

What one differing facet requires: assumptions, or a block, plus any correction.

entry: FacetDiff = plan.entry("intervention")
Levelspec

Level(unit: Literal['individual', 'cluster', 'aggregate'], interference: Literal['none', 'within_cluster', 'declared'] = 'none', interference_model: str = '')

Unit of analysis and interference model (review B2: SUTVA-2).

lift = Estimand(
    name="lift_at_100",
    quantity=Quantity(kind="contrast"),
    treatment=fertilizer,
    intervention=Intervention(doses={"fertilizer": 100.0}, version="granular"),
    reference=Intervention(doses={"fertilizer": 0.0}, version="granular"),
    outcome=yield_total,
    population=north,
    window=season,
    level=Level(unit="cluster", interference="none"),
    conditioning=(),
    dimension=D.outcome,
    description="season-total yield lift from 100 USD of granular fertilizer vs none, north region",
)
Quantityspec

Quantity(kind: Literal['contrast', 'marginal', 'ratio', 'elasticity', 'area'], scale: Literal['natural', 'log'] = 'natural')

The functional applied to the response.

mroi = lift.model_copy(update={
    "name": "marginal_at_100",
    "quantity": Quantity(kind="marginal"),
    "reference": None,
    "dimension": D.outcome / D.currency,
})
QuantityKindcallable

QuantityKind(*args, **kwargs)

kinds: list[QuantityKind] = ["contrast", "marginal", "ratio", "elasticity", "area"]
RealizedDrawsclass

RealizedDraws(result: EstimandResult, draws: Array) -> None

``realize(..., keep_draws=True)``: the result plus the draws it summarizes, flat.

if isinstance(rd, RealizedDraws):
    print(rd.draws.shape, rd.result.summary.n)
ResultStatuscallable

ResultStatus(*args, **kwargs)

status: ResultStatus = asserted.status
TransferPlanspec

TransferPlan(status: Literal['identified', 'downgraded', 'blocked', 'unsupported', 'unverified'], source: str, target: str, differing: tuple[Literal['quantity', 'intervention', 'outcome', 'population', 'window', 'level', 'conditioning', 'dimension'], ...], entries: tuple[FacetDiff, ...], assumptions: tuple[Assumption, ...], corrections: tuple[str, ...] = (), ledger_lines: tuple[LedgerLine, ...], reason: str = '')

The typed diff between two estimands and what licenses reading one as the other.

plan: TransferPlan = experiment.transfer_to(national)
check_estimand_dimensionfn

check_estimand_dimension(estimand: Estimand, expr: Expr) -> Dimension

``dimension(expr)``, asserted equal to the estimand's declared dimension (gate 10).

print(dimension(tree), "==", check_estimand_dimension(lift, tree))
derived_dimensionfn

derived_dimension(kind: QuantityKind, outcome: Dimension, dose: Dimension) -> Dimension

print(f"{kind:11s} -> {derived_dimension(kind, D.outcome, D.currency)}")
estimand_exprfn

estimand_expr(estimand: Estimand, *, mean: Expr, treatment_data: Data, reference: Mapping[str, float] | None = None, derivative: Expr | None = None) -> Expr | Unsupported

The estimand as a per-row expression over ``mean``, or a typed ``Unsupported``.

tree = estimand_expr(lift, mean=world.model.mean, treatment_data=Data(name="dose", dimension=D.currency))
evaluatefn

evaluate(estimands: Sequence[Estimand], producer: SupportsEstimands, *, verdicts: Mapping[str, Verdict] | None = None, assume_identified: bool = False, definition: IntervalDefinition = 'hdi', mass: float = 0.9, seed: int | None = None, unit_weights: Mapping[str, float] | None = None, unit_strata: Mapping[str, str] | None = None) -> dict[str, EstimandResult | Unsupported | Blocked]

``realize`` each estimand, keyed by name; ``verdicts`` maps estimand name to its verdict.

results = evaluate(list(registry), res, verdicts={name: verdict for name in registry.names()}, definition="hdi", mass=0.9)
realizefn

realize(estimand: Estimand, producer: SupportsEstimands, *, verdict: Verdict | None = None, assume_identified: bool = False, definition: IntervalDefinition = 'hdi', mass: float = 0.9, seed: int | None = None, unit_weights: Mapping[str, float] | None = None, unit_strata: Mapping[str, str] | None = None, keep_draws: bool = False) -> EstimandResult | RealizedDraws | Unsupported | Blocked

Realize one estimand against a producer; see the module docstring for the arithmetic.

print(realize(lift, crippled))
standard_estimandsfn

standard_estimands(treatment: Treatment, outcome: Outcome, population: Population, window: TimeWindow, level: Level, *, dose: float, reference_dose: float = 0.0, version: str = 'unspecified') -> EstimandRegistry

The five domain-general estimands for one treatment at one dose.

registry: EstimandRegistry = standard_estimands(
    treatment=spec.treatments[0], outcome=spec.outcome, population=Population(name="all"),
    window=TimeWindow(start=0, stop=1), level=Level(unit="individual"), dose=60.0, reference_dose=0.0,
)
substitutefn

substitute(expr: Expr, replacements: Mapping[str, Expr]) -> Expr

Rebuild ``expr`` with every ``Data`` node named in ``replacements`` swapped out.

pinned = substitute(world.model.mean, {"dose": __import__("axiom.core", fromlist=["Const"]).Const(value=60.0, dimension=D.currency)})

axiom.surface

domain88 symbols · 6 notebooks · walkthrough

Dose-response kernels, carryover, the one forward(), optimal allocation.

Allocationspec

Allocation(doses: dict[str, float], expected_outcome: float, budget: float, objective: Literal['mean', 'quantile'], q: float = 0.5, method: str, status: Literal['converged'] = 'converged', seed: int | None = None, detail: dict[str, float] = {})

One allocation: a dose per treatment and the objective it achieves.

if isinstance(alloc, Allocation):
    print(alloc.doses, round(alloc.expected_outcome, 3), alloc.status, round(alloc.total_dose, 3))
else:
    show(alloc)
AllocationMethodcallable

AllocationMethod(*args, **kwargs)

meth: AllocationMethod = "slsqp"
AnyCarryovercallable

AnyCarryover(*args, **kwargs)

Runtime representation of an annotated type.

AnyKernelcallable

AnyKernel(*args, **kwargs)

Runtime representation of an annotated type.

AnyNuisanceTermcallable

AnyNuisanceTerm(*args, **kwargs)

Runtime representation of an annotated type.

any_term: AnyNuisanceTerm = FourierSeasonality(period=12.0, order=1)
AscentPathspec

AscentPath(treatments: tuple[str, ...], points: tuple[tuple[float, ...], ...], values: tuple[float, ...], stop: Literal['decrease', 'converged', 'boundary', 'n_steps'])

The visited points of a steepest-ascent walk and the surface value at each.

if isinstance(path, AscentPath):
    print(path.n, path.stop, path.best(), round(path.values[-1], 3))
else:
    show(path)
BASIS_PRIOR_FAMILIESvalue

Build an immutable unordered collection of unique elements.

print("\ncoefficient priors may be signed:", sorted(BASIS_PRIOR_FAMILIES))
BandKindcallable

BandKind(*args, **kwargs)

kind: BandKind = band.kind
Boundsspec

Bounds(treatments: tuple[str, ...], low: tuple[float, ...], high: tuple[float, ...])

The experimental region: a box ``[low_i, high_i]`` per treatment, in dose units.

bounds = Bounds(treatments=("a", "b"), low=(0.0, 0.0), high=(100.0, 40.0))
CARRYOVERSvalue

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

print(sorted(KERNELS), "|", sorted(CARRYOVERS))
CarryoverKernelclass

CarryoverKernel(*args, **kwargs)

A lag-weight family that builds its own expression tree.

geo: CarryoverKernel = GeometricCarryover(max_lag=6)
CarryoverRolecallable

CarryoverRole(*args, **kwargs)

role: CarryoverRole = "shape"
CovarianceFamilycallable

CovarianceFamily(*args, **kwargs)

covariance: CovarianceFamily = gp.covariance
Criterioncallable

Criterion(*args, **kwargs)

crit: Criterion = "d"
DelayedCarryoverspec

DelayedCarryover(name: Literal['delayed'] = 'delayed', max_lag: int)

``w_l ∝ λ^((l − θ)^2)``: a peak at lag ``θ`` with symmetric geometric decay around it.

delayed = DelayedCarryover(max_lag=8)
Designspec

Design(treatments: tuple[str, ...], points: tuple[tuple[float, ...], ...], kind: str, detail: dict[str, float] = {})

A set of dose points (``points[i][j]`` is treatment ``j`` at run ``i``) and its recipe.

ccd: Design = central_composite(bounds, alpha="rotatable", center_points=3)
EventIndicatorsspec

EventIndicators(kind: Literal['events'] = 'events', events: tuple[str, ...], coefficient_scale: float = 1.0)

User-supplied 0/1 columns, one coefficient each.

No executed statement uses this symbol — it appears in the notebooks only inside an import, which is all gate 12 requires. Worth a worked line.

ExponentialKernelspec

ExponentialKernel(name: Literal['exponential'] = 'exponential', reference_dose: float = 1.0, amplitude_scale: float = 1.0, amplitude_prior: Prior | None = None)

``f(u) = 1 − exp(−u)``, ``u = dose / k``. Saturating; ``f(0) = 0``; concave everywhere.

for fam in (LogisticKernel(reference_dose=50.0), ExponentialKernel(reference_dose=50.0), PowerKernel(reference_dose=50.0), LinearKernel(reference_dose=50.0)):
    k: AnyKernel = fam
    names = [p.name for p in k.parameters("a", D.currency, D.outcome)]
    print(f"{k.name:12s} saturating={k.saturating!s:5s} params={names}")
FitResultclass

FitResult(surface: Surface, posterior: Posterior | Unverified | Unsupported, data: Mapping[str, NDArray[Any]], report: ConvergenceReport | None, provenance: dict[str, Any], dropped: frozenset[Capability] = frozenset(), declared: tuple[str, ...] = ()) -> None

What ``fit`` returns: the surface, its posterior (or a typed failure), and the record.

result: FitResult = fit(world.spec, world.panel, backend="laplace", draws=2000, seed=0)
FourierSeasonalityspec

FourierSeasonality(kind: Literal['seasonality'] = 'seasonality', period: float, order: int, coefficient_scale: float = 1.0)

``sin(2πk t / period)``, ``cos(2πk t / period)`` for ``k = 1 … order``.

any_term: AnyNuisanceTerm = FourierSeasonality(period=12.0, order=1)
Frontierspec

Frontier(budgets: tuple[float, ...], outcomes: tuple[float, ...], allocations: tuple[Allocation, ...])

Optimal outcome per budget: ``budgets`` ascend; ``allocations[i]`` reaches ``outcomes[i]``.

if isinstance(fr, Frontier):
    print(fr.as_frame())
    print("shadow prices:", np.round(fr.shadow_prices(), 4))
else:
    show(fr)
GRID_VERSIONvalue

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

print("predict marks a dose grid with version", repr(GRID_VERSION))
GaussianProcessKernelspec

GaussianProcessKernel(name: Literal['gaussian_process'] = 'gaussian_process', reference_dose: float = 1.0, amplitude_scale: float = 1.0, amplitude_prior: Prior | None = None, n_basis: int = 24, boundary_factor: float = 3.0, covariance: Literal['squared_exponential', 'matern32', 'matern52'] = 'squared_exponential', lengthscale_median: float = 0.3, lengthscale_spread: float = 0.5)

A Gaussian process over the dose axis, as its Hilbert-space basis approximation.

gp = GaussianProcessKernel(reference_dose=40.0, amplitude_scale=8.0)
GeometricCarryoverspec

GeometricCarryover(name: Literal['geometric'] = 'geometric', max_lag: int)

``w_l ∝ λ^l``: exponential decay from lag zero. ``λ ~ beta(2, 2)`` on (0, 1).

geo: CarryoverKernel = GeometricCarryover(max_lag=6)
HillKernelspec

HillKernel(name: Literal['hill'] = 'hill', reference_dose: float = 1.0, amplitude_scale: float = 1.0, amplitude_prior: Prior | None = None)

``f(u) = u^s / (1 + u^s)``, ``u = dose / k``. Saturating; ``f(0) = 0``, ``f(1) = 1/2``.

hill: ResponseKernel = HillKernel(reference_dose=50.0, amplitude_scale=10.0)
InterceptKindcallable

InterceptKind(*args, **kwargs)

kind: InterceptKind = "hierarchical"
KERNELSvalue

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

print(sorted(KERNELS), "|", sorted(CARRYOVERS))
KernelRolecallable

KernelRole(*args, **kwargs)

role: KernelRole = hill.roles[p.name.rsplit("_", 1)[0]] if p.name.rsplit("_", 1)[0] in hill.roles else "shape"
LinearKernelspec

LinearKernel(name: Literal['linear'] = 'linear', reference_dose: float = 1.0, amplitude_scale: float = 1.0, amplitude_prior: Prior | None = None)

``response = beta_rate · dose``. No scale, no shape; never saturates.

for fam in (LogisticKernel(reference_dose=50.0), ExponentialKernel(reference_dose=50.0), PowerKernel(reference_dose=50.0), LinearKernel(reference_dose=50.0)):
    k: AnyKernel = fam
    names = [p.name for p in k.parameters("a", D.currency, D.outcome)]
    print(f"{k.name:12s} saturating={k.saturating!s:5s} params={names}")
LinearTrendspec

LinearTrend(kind: Literal['trend'] = 'trend', origin: float | None = None, scale: float | None = None, coefficient_scale: float = 1.0)

``trend = (t − origin) / scale``: one dimensionless basis column.

term: NuisanceTerm = LinearTrend()
LogisticKernelspec

LogisticKernel(name: Literal['logistic'] = 'logistic', reference_dose: float = 1.0, amplitude_scale: float = 1.0, amplitude_prior: Prior | None = None)

``f(u) = 2 / (1 + exp(-u)) − 1 = tanh(u / 2)``, ``u = dose / k``. Saturating; ``f(0) = 0``.

for fam in (LogisticKernel(reference_dose=50.0), ExponentialKernel(reference_dose=50.0), PowerKernel(reference_dose=50.0), LinearKernel(reference_dose=50.0)):
    k: AnyKernel = fam
    names = [p.name for p in k.parameters("a", D.currency, D.outcome)]
    print(f"{k.name:12s} saturating={k.saturating!s:5s} params={names}")
MarginalHorizoncallable

MarginalHorizon(*args, **kwargs)

horizon: MarginalHorizon = "shift"
Methodcallable

Method(*args, **kwargs)

method: Method = "auto"
NoCarryoverspec

NoCarryover(name: Literal['none'] = 'none')

The identity: all weight at lag zero. ``apply`` returns the dose expression itself.

for c in (DelayedCarryover(max_lag=6), WeibullCarryover(max_lag=6), NoCarryover()):
    cc: AnyCarryover = c
    print(cc.name, [p.name for p in cc.parameters("a")])
NuisanceSetspec

NuisanceSet(kind: Literal['set'] = 'set', terms: tuple[FourierSeasonality | LinearTrend | EventIndicators, ...])

Several nuisance terms with one ``augment`` / ``expr`` / ``parameters``.

trend_spec = spec.model_copy(update={"nuisance": NuisanceSet(terms=(LinearTrend(),)), "carryover": {}})
NuisanceTermclass

NuisanceTerm(*args, **kwargs)

A baseline term: basis columns in the frame, coefficients in the tree.

term: NuisanceTerm = LinearTrend()
Objectivecallable

Objective(*args, **kwargs)

obj: Objective = "mean"
ParameterRolecallable

ParameterRole(*args, **kwargs)

roles: dict[str, ParameterRole] = parameter_roles(spec)
PiecewiseLinearKernelspec

PiecewiseLinearKernel(name: Literal['piecewise_linear'] = 'piecewise_linear', reference_dose: float = 1.0, amplitude_scale: float = 1.0, amplitude_prior: Prior | None = None, knots: tuple[float, ...] = (0.5,))

A **linear spline** in ``u = dose / reference_dose``: a broken stick on fixed knots.

broken = PiecewiseLinearKernel(reference_dose=40.0, knots=(15.0, 25.0))
PolynomialKernelspec

PolynomialKernel(name: Literal['polynomial'] = 'polynomial', reference_dose: float = 1.0, amplitude_scale: float = 1.0, amplitude_prior: Prior | None = None, degree: int = 3)

``response = Σ_{j=1..degree} beta_j · u^j``, ``u = dose / reference_dose``.

poly = PolynomialKernel(reference_dose=40.0, degree=3)
PowerKernelspec

PowerKernel(name: Literal['power'] = 'power', reference_dose: float = 1.0, amplitude_scale: float = 1.0, amplitude_prior: Prior | None = None)

``f(u) = u^s``, ``u = dose / k``. **Not saturating**: unbounded as the dose grows.

for fam in (LogisticKernel(reference_dose=50.0), ExponentialKernel(reference_dose=50.0), PowerKernel(reference_dose=50.0), LinearKernel(reference_dose=50.0)):
    k: AnyKernel = fam
    names = [p.name for p in k.parameters("a", D.currency, D.outcome)]
    print(f"{k.name:12s} saturating={k.saturating!s:5s} params={names}")
ResponseBandspec

ResponseBand(treatment: str, outcome: str, kind: Literal['response', 'marginal'], doses: tuple[float, ...], mean: tuple[float, ...], median: tuple[float, ...], lower: tuple[float, ...], upper: tuple[float, ...], definition: Literal['eti', 'hdi', 'wald'], mass: float, n_draws: int, dimension: Dimension, ... and 2 more fields)

A dose grid, the curve on it, and the interval around the curve at every point.

assert isinstance(band, ResponseBand)
ResponseKernelclass

ResponseKernel(*args, **kwargs)

A dose-response family that builds its own expression tree.

hill: ResponseKernel = HillKernel(reference_dose=50.0, amplitude_scale=10.0)
SplineKernelspec

SplineKernel(name: Literal['spline'] = 'spline', reference_dose: float = 1.0, amplitude_scale: float = 1.0, amplitude_prior: Prior | None = None, knots: tuple[float, ...] = (0.25, 0.5, 0.75))

A **natural cubic spline** in ``u = dose / reference_dose``, on fixed knots.

spline = SplineKernel(reference_dose=40.0, knots=(8.0, 16.0, 24.0, 32.0))
StationaryPointspec

StationaryPoint(treatments: tuple[str, ...], origin: tuple[float, ...], point: tuple[float, ...], value: float, kind: Literal['maximum', 'minimum', 'saddle'], gradient: tuple[float, ...], eigenvalues: tuple[float, ...], eigenvectors: tuple[tuple[float, ...], ...])

Canonical analysis at ``origin``: the stationary point of the local quadratic and its shape.

No executed statement uses this symbol — it appears in the notebooks only inside an import, which is all gate 12 requires. Worth a worked line.

SupportsBandsclass

SupportsBands(*args, **kwargs)

What a band needs from a fit: draws under an intervention, and the fitted grid.

print("a fit satisfies the band protocol:", isinstance(result, SupportsBands))
Surfaceclass

Surface(spec: SurfaceSpec, *, provenance: Mapping[str, Any] | None = None) -> None

A ``SurfaceSpec`` with its built model: the ``SupportsForward`` the upper layers use.

surface = Surface(spec)
SurfaceSpecspec

SurfaceSpec(name: str, treatments: tuple[Treatment, ...], outcome: Outcome, kernels: dict[str, HillKernel | LogisticKernel | ExponentialKernel | PowerKernel | LinearKernel | PolynomialKernel | SplineKernel | PiecewiseLinearKernel | GaussianProcessKernel] = {}, carryover: dict[str, GeometricCarryover | DelayedCarryover | WeibullCarryover | NoCarryover] = {}, nuisance: NuisanceSet = NuisanceSet(kind='set', terms=()), intercept: Literal['none', 'shared', 'per_unit', 'hierarchical'] = 'shared', interactions: tuple[tuple[str, str], ...] = (), likelihood: Likelihood = ..., time_column: str = 't', unit_column: str = 'unit', unit_labels: tuple[str, ...] = (), ... and 3 more fields)

A response surface over named treatments: what is fit, declared once.

spec = SurfaceSpec(
    name="two_treatment_panel",
    treatments=(Treatment(name="a", dimension=D.currency, unit="USD"), Treatment(name="b", dimension=D.currency, unit="USD")),
    outcome=Outcome(name="y", dimension=D.outcome, unit="units"),
    kernels={"a": HillKernel(reference_dose=50.0), "b": HillKernel(reference_dose=20.0)},
    carryover={"a": GeometricCarryover(max_lag=4)},
    nuisance=NuisanceSet(terms=(FourierSeasonality(period=12.0, order=1),)),
    intercept=kind,
    interactions=(("a", "b"),),
    likelihood=Likelihood(family="normal", scale="sigma"),
    unit_labels=("u0", "u1", "u2"),
)
WeibullCarryoverspec

WeibullCarryover(name: Literal['weibull'] = 'weibull', max_lag: int)

``w_l ∝ exp(−(l / λ)^κ)``: the Weibull survival function over lags.

for c in (DelayedCarryover(max_lag=6), WeibullCarryover(max_lag=6), NoCarryover()):
    cc: AnyCarryover = c
    print(cc.name, [p.name for p in cc.parameters("a")])
a_criterionfn

a_criterion(X: DesignMatrix | ArrayLike) -> float

``-trace((X'X)^-1)`` (larger is better); ``-inf`` when singular.

print("D:", round(d_criterion(X), 3), "A:", round(a_criterion(X), 3), "E:", round(e_criterion(X), 4))
allocatefn

allocate(surface: SupportsForward, posterior_or_theta: SupportsPosterior | Theta | Failure, *, budget: float, bounds: Bounds, objective: Objective = 'mean', q: float = 0.5, seed: int | None = None, method: AllocationMethod = 'slsqp', start: Mapping[str, float] | None = None, n_starts: int = 1, n_draws: int | None = None, maxiter: int = 200, tol: float = 1e-12, n_grid: int = 101, context: Context | None = None) -> Allocation | Failure

Maximize the posterior-expected outcome subject to ``Σ dose ≤ budget`` and ``bounds``.

alloc = allocate(surface, theta, budget=60.0, bounds=bounds, objective=obj, method=meth, seed=0)
bayesian_criterionfn

bayesian_criterion(surface: SupportsForward, design: Design, theta_draws: Sequence[Mapping[str, ArrayLike]], criterion: Criterion = 'd') -> float

Mean over ``theta_draws`` of the criterion on ``surface.linearize(design, theta).X``.

print("naive   :", round(bayesian_criterion(surface, naive, prior_draws), 3))
box_behnkenfn

box_behnken(bounds: Bounds, *, center_points: int = 3) -> Design

The published Box-Behnken design for ``k`` in 3..7: blocked two-level factorials.

print("box-behnken k=3:", box_behnken(b3, center_points=2).n)
buildfn

build(spec: SurfaceSpec) -> ModelSpec

The ``ModelSpec`` a backend fits: ``mean = intercept + Σ_i beta_i f_i(carry_i(dose_i)) + Σ gamma_ij f_i f_j + nuisance``, every parameter with its prior, dimension-checked.

canonical_analysisfn

canonical_analysis(surface: SupportsForward, theta: Theta, x0: Mapping[str, float], *, method: Method = 'auto', tol: float = 1e-08, bounds: Bounds | None = None) -> StationaryPoint | Unsupported

Second-order expansion at ``x0``: stationary point ``x0 - H^-1 g`` and its nature.

sp = canonical_analysis(surface, theta, {"a": 30.0, "b": 10.0}, method=method)
carryover_from_namefn

carryover_from_name(name: str, **fields: Any) -> CarryoverKernel

Build a carryover by family name, e.g. ``carryover_from_name("geometric", max_lag=8)``.

print(carryover_from_name("geometric", max_lag=4))
central_compositefn

central_composite(bounds: Bounds, *, alpha: float | Literal['rotatable', 'face'] = 'rotatable', center_points: int = 4, fraction: int | None = None, inscribed: bool = False) -> Design

Cube (2^k, or 2^(k-p) with ``fraction=p``), 2k axial points at ``±alpha``, centre runs.

ccd: Design = central_composite(bounds, alpha="rotatable", center_points=3)
check_linearizationfn

check_linearization(surface: SupportsForward, dose: Mapping[str, ArrayLike], theta: Mapping[str, ArrayLike]) -> float

``‖X @ theta_lin + offset − forward(dose, theta)‖∞`` — the gate's number, for one point.

print("invariant:", check_linearization(ws, world.data, world.theta))
column_namesfn

column_names(model: ModelSpec, linear: Sequence[str]) -> tuple[str, ...]

One column per scalar entry of every linear parameter, in ``linear`` order.

print(term.column_names("n0_"), any_term.column_names("n1_"))
counterfactual_dosesfn

counterfactual_doses(surface: Surface, data: Mapping[str, ArrayLike], iv: Intervention) -> dict[str, NDArray[Any]]

The fitted data dict with ``iv`` applied to the treatments it names.

cf = counterfactual_doses(surface, world.data, Intervention(doses={"a": 2.0}, mode="scale", window=TimeWindow(start=5, stop=10)))
d_criterionfn

d_criterion(X: DesignMatrix | ArrayLike) -> float

``log det(X'X)``; ``-inf`` when the information matrix is singular.

print("D:", round(d_criterion(X), 3), "A:", round(a_criterion(X), 3), "E:", round(e_criterion(X), 4))
defining_relationfn

defining_relation(generators: Sequence[str]) -> tuple[str, ...]

The words of ``I = ...`` implied by two-level generators, e.g. ``("ABCD",)`` for ``D=ABC``.

print("2^(4-1):", ff.n, ff.detail, defining_relation(("D=ABC",)))
design_matrixfn

design_matrix(model: ModelSpec, linear: Sequence[str], dose: Mapping[str, ArrayLike], theta_at: Mapping[str, ArrayLike]) -> DesignMatrix

``mu = offset + X @ theta[columns]`` for ``model.mean`` at ``theta_at``.

print(design_matrix(ws.model, ws.linear, world.data, world.theta).at.keys())
e_criterionfn

e_criterion(X: DesignMatrix | ArrayLike) -> float

Smallest eigenvalue of ``X'X``; ``0`` when singular (a zero eigenvalue, not a guess).

print("D:", round(d_criterion(X), 3), "A:", round(a_criterion(X), 3), "E:", round(e_criterion(X), 4))
equal_spacingfn

equal_spacing(bounds: Bounds, n_per_treatment: int) -> Design

The naive comparator: a regular grid with ``n_per_treatment`` levels on every axis.

print("LHS:", lhs.n, "| equal spacing:", equal_spacing(bounds, 4).n)
fitfn

fit(spec: SurfaceSpec, panel: Panel, *, backend: Backend | str = 'laplace', draws: int = 1000, tune: int = 1000, chains: int = 4, seed: int | None = None) -> FitResult

Prepare the panel, sample the built model through ``backend``, and record everything.

result: FitResult = fit(world.spec, world.panel, backend="laplace", draws=2000, seed=0)
forwardfn

forward(surface: SupportsForward, dose: Mapping[str, ArrayLike], theta: Mapping[str, ArrayLike]) -> Array

``value(surface.expr, data=dose, params=theta)`` — the one forward().

mu = forward(ws, world.data, world.theta)
fractional_factorialfn

fractional_factorial(bounds: Bounds, generators: tuple[str, ...]) -> Design

A two-level 2^(k-p) fraction from generators like ``("D=ABC",)``; A is the first treatment.

ff = fractional_factorial(Bounds(treatments=("A", "B", "C", "D"), low=(0.0,) * 4, high=(1.0,) * 4), generators=("D=ABC",))
frontierfn

frontier(surface: SupportsForward, posterior_or_theta: SupportsPosterior | Theta | Failure, budgets: Sequence[float], bounds: Bounds, *, objective: Objective = 'mean', q: float = 0.5, seed: int | None = None, method: AllocationMethod = 'slsqp', n_starts: int = 1, n_draws: int | None = None, maxiter: int = 200, tol: float = 1e-12, n_grid: int = 101, context: Context | None = None) -> Frontier | Failure

``allocate`` at every budget (sorted ascending), each warm-started from the last.

fr = frontier(surface, theta, budgets=[10.0, 30.0, 60.0, 120.0], bounds=bounds, seed=0)
full_factorialfn

full_factorial(bounds: Bounds, levels: int | tuple[int, ...]) -> Design

Every combination of equally spaced levels (``levels`` per treatment, or one count).

print("3^2 factorial:", full_factorial(bounds, 3).n)
gradientfn

gradient(surface: SupportsForward, theta: Theta, x: Mapping[str, float], *, method: Method = 'auto', bounds: Bounds | None = None) -> dict[str, float] | Unsupported

``d forward / d dose`` at ``x``, keyed like ``x``; the key order fixes treatment order.

print(gradient(surface, theta, {"a": 30.0, "b": 10.0}, method=method))
hessianfn

hessian(surface: SupportsForward, theta: Theta, x: Mapping[str, float], *, method: Method = 'auto', bounds: Bounds | None = None) -> Array | Unsupported

Second derivatives of ``forward`` at ``x``; rows and columns follow ``x``'s key order.

print(np.round(hessian(surface, theta, {"a": 30.0, "b": 10.0}, method=method), 5))
interaction_namefn

interaction_name(first: str, second: str) -> str

The parameter name of the pairwise interaction ``gamma`` between two treatments.

print(interaction_name("a", "b"), "|", dimension(model.mean))
kernel_from_namefn

kernel_from_name(name: str, **fields: Any) -> ResponseKernel

Build a kernel by family name, e.g. ``kernel_from_name("hill", reference_dose=50.0)``.

print(kernel_from_name("hill", reference_dose=10.0))
latin_hypercubefn

latin_hypercube(bounds: Bounds, n: int, *, seed: int | None = None, centered: bool = False, n_restarts: int = 10) -> Design

``n`` points with one per stratum per treatment; maximin over ``n_restarts`` draws.

lhs = latin_hypercube(bounds, 12, seed=0)
linear_coefficientsfn

linear_coefficients(columns: Sequence[str], theta: Mapping[str, ArrayLike]) -> Array

The vector ``theta[columns]``: scalars by name, vector entries by ``name[i]``.

print(column_names(ws.model, ws.linear)[:4], linear_coefficients(dm.columns, world.theta)[:4].round(3))
marginalfn

marginal(surface: Surface, theta: Mapping[str, ArrayLike], dose: Mapping[str, ArrayLike], treatment: str) -> Array

``w_0 · ∂mu_t/∂c_t``: the effect of one more unit of dose in period ``t`` on outcome ``t``.

print(marginal(surface, world.theta, world.data, "a")[0, :4].round(4), marginal_total(surface, world.theta, world.data, "a")[0, :4].round(4))
marginal_bandfn

marginal_band(result: SupportsBands, treatment: str, *, doses: Sequence[float] | None = None, n_grid: int = 25, mass: float = 0.9, definition: IntervalDefinition = 'eti', window: TimeWindow | None = None, seed: int | None = None) -> ResponseBand | Unsupported

``d outcome / d dose`` against dose, with the posterior interval at every point.

slope = marginal_band(result, "a", n_grid=9, mass=0.9)
marginal_exprfn

marginal_expr(surface: Surface, treatment: str) -> Expr

``∂mu / ∂c`` with respect to a treatment's *carried* dose, as a tree.

print(dimension(marginal_expr(surface, "a")))
marginal_totalfn

marginal_total(surface: Surface, theta: Mapping[str, ArrayLike], dose: Mapping[str, ArrayLike], treatment: str) -> Array

``Σ_l w_l · ∂mu_{t+l}/∂c_{t+l}``: the total effect of one more unit in period ``t``.

print(marginal(surface, world.theta, world.data, "a")[0, :4].round(4), marginal_total(surface, world.theta, world.data, "a")[0, :4].round(4))
optimal_exchangefn

optimal_exchange(surface: SupportsForward, candidates: Design, n: int, theta_draws: Sequence[Mapping[str, ArrayLike]], *, criterion: Criterion = 'd', seed: int | None = None, max_passes: int = 20, n_restarts: int = 3) -> Design | Unsupported

Fedorov point exchange: the best ``n`` candidate rows (replicates allowed) by the criterion.

opt = optimal_exchange(surface, candidates, n=naive.n, theta_draws=prior_draws, criterion=crit, seed=0)
parameter_rolesfn

parameter_roles(spec: SurfaceSpec) -> dict[str, ParameterRole]

``{parameter name: "linear" | "nonlinear" | "auxiliary"}`` in declaration order.

roles: dict[str, ParameterRole] = parameter_roles(spec)
predictfn

predict(surface: Surface, posterior: SupportsPosterior, dose: Mapping[str, ArrayLike], *, seed: int | None = None, noise: bool = False) -> PredictiveDraws

Every draw pushed through ``forward``: ``values`` is ``(chain, draw, *forward shape)``.

print(np.allclose(dm.predict(world.theta).reshape(mu.shape), mu))
predict_marginalfn

predict_marginal(surface: Surface, posterior: SupportsPosterior, dose: Mapping[str, ArrayLike], treatment: str, *, horizon: MarginalHorizon = 'total', support: TimeWindow | None = None, seed: int | None = None) -> PredictiveDraws

Every draw pushed through ``marginal`` / ``marginal_total`` / ``marginal_shift``.

pm = predict_marginal(surface, post, world.data, "a", horizon=horizon, support=TimeWindow(start=5, stop=10))
preparefn

prepare(spec: SurfaceSpec, panel: Panel, *, conventions: Conventions | None = None) -> DataDict

The data dict the built model reads, laid out ``(n_units, n_periods)``.

data = prepare(world.spec, world.panel)
resolve_conventionsfn

resolve_conventions(spec: SurfaceSpec, panel: Panel, conventions: Conventions | None = None) -> dict[str, dict[str, float]]

The trend conventions ``prepare`` lays the panel out with, one entry per trend column.

conv = resolve_conventions(world_t.spec, world_t.panel)
response_bandfn

response_band(result: SupportsBands, treatment: str, *, doses: Sequence[float] | None = None, n_grid: int = 25, mass: float = 0.9, definition: IntervalDefinition = 'eti', window: TimeWindow | None = None, seed: int | None = None) -> ResponseBand | Unsupported

Expected outcome against dose, with the posterior interval at every grid point.

band = response_band(result, "a", n_grid=9, mass=0.9, definition="eti")
steepest_ascentfn

steepest_ascent(surface: SupportsForward, theta: Theta, start: Mapping[str, float], *, step: float, n_steps: int, bounds: Bounds | None = None, method: Method = 'auto', recompute: bool = False) -> AscentPath | Unsupported

Walk ``step`` dose units per move up the gradient until the value stops rising.

path = steepest_ascent(surface, theta, {"a": 10.0, "b": 5.0}, step=5.0, n_steps=20, bounds=bounds)

axiom.sim

domain23 symbols · 2 notebooks

Synthetic worlds with known truth, so every claim has a recovery test.

DoseDistributioncallable

DoseDistribution(*args, **kwargs)

dist: DoseDistribution = "lognormal"
DosePlanspec

DosePlan(distribution: Literal['lognormal', 'uniform'] = 'lognormal', scale: float = 1.0, spread: float = 0.5, zero_fraction: float = 0.0)

How doses are drawn for one treatment: a family, a scale, a spread, and a zero share.

plan = DosePlan(distribution=dist, scale=50.0, spread=0.6, zero_fraction=0.1)
Horizoncallable

Horizon(*args, **kwargs)

horizon: Horizon = "total"
LinearSCMspec

LinearSCM(graph: CausalGraph, coefficients: dict[str, float] = {}, noise_sd: dict[str, float] = {}, latent_sd: dict[str, float] = {}, intercepts: dict[str, float] = {})

A linear-Gaussian SCM over a ``CausalGraph`` with optional latent confounding.

scm = LinearSCM.from_text("Z -> X: 0.8, Z -> Y: 1.5, X -> Y: 2.0, X <-> Y: 0.7", name="demo")
SCMErrorerror

The structural model is malformed (missing coefficient, unknown node, bad key).

try:
    LinearSCM.from_text("X -> Y: 1.0, X -> Y: 2.0")
except SCMError as e:
    print("refused:", e)
SurfaceWorldclass

SurfaceWorld(spec: SurfaceSpec, model: ModelSpec, theta: dict[str, Array], panel: Panel, data: dict[str, Array], mean: Array, seed: int | None, noise_sd: float | None, provenance: dict[str, Any] = <factory>) -> None

A ``SurfaceSpec``, its true parameters, and a balanced panel generated from them.

world: SurfaceWorld = surface_world(
    n_units=4, n_periods=26, treatments=("a", "b"),
    kernels={"a": HillKernel(reference_dose=50.0), "b": HillKernel(reference_dose=20.0)},
    carryover={"a": GeometricCarryover(max_lag=4)},
    doses={"a": plan, "b": DosePlan(scale=20.0, spread=0.4)},
    nuisance=NuisanceSet(terms=(FourierSeasonality(period=13.0, order=1),)),
    intercept="hierarchical", truth_mode=mode, noise_sd=0.5, seed=3,
)
TruthModecallable

TruthMode(*args, **kwargs)

mode: TruthMode = "centre"
arms_worldfn

arms_world(*, n_units: int, treatments: Sequence[Treatment | str] = ('dose',), kernels: Mapping[str, Kernel] | Kernel | None = None, doses: Mapping[str, DosePlan] | DosePlan | Mapping[str, ArrayLike] | None = None, outcome: Outcome | None = None, truth: Mapping[str, ArrayLike] | None = None, truth_mode: TruthMode = 'centre', noise_sd: float | None = None, intercept_scale: float = 1.0, seed: int | None = None, name: str = 'arms_world') -> SurfaceWorld

The single-period world: one row per unit, a shared intercept, no carryover.

arms = arms_world(n_units=12, treatments=("dose",), doses={"dose": np.linspace(0, 100, 12)}, seed=0)
coefficient_keyfn

coefficient_key(a: str, b: str) -> str

The ``coefficients`` key for the directed edge ``a -> b``: ``"a->b"``.

print(coefficient_key("Z", "X"), latent_key("Y", "X"))
confounded_worldfn

confounded_world() -> LinearSCM

``Z -> X`` (0.8), ``Z -> Y`` (1.5), ``X -> Y`` (2.0); everything measured.

for w in (confounded_world, hidden_confounder_world, iv_world, frontdoor_world, mediator_world, feedback_world):
    s = w()
    print(f"{w.__name__:24s} {s.graph.to_text():40s} truth={s.total_effect('X', 'Y'):.2f} unmeasured={s.graph.unmeasured} feedback={s.graph.feedback}")
draw_dosesfn

draw_doses(plan: DosePlan, n_units: int, n_periods: int, *, seed: int | None = None) -> Array

``(n_units, n_periods)`` doses from ``plan``; deterministic given ``seed``.

print(draw_doses(plan, 2, 6, seed=0).round(1))
feedback_worldfn

feedback_world() -> LinearSCM

The confounded world's summary graph, flagged ``feedback=True``.

for w in (confounded_world, hidden_confounder_world, iv_world, frontdoor_world, mediator_world, feedback_world):
    s = w()
    print(f"{w.__name__:24s} {s.graph.to_text():40s} truth={s.total_effect('X', 'Y'):.2f} unmeasured={s.graph.unmeasured} feedback={s.graph.feedback}")
frontdoor_worldfn

frontdoor_world() -> LinearSCM

``X -> M`` (1.2), ``M -> Y`` (1.5), ``X <-> Y`` (latent scale 1.0).

for w in (confounded_world, hidden_confounder_world, iv_world, frontdoor_world, mediator_world, feedback_world):
    s = w()
    print(f"{w.__name__:24s} {s.graph.to_text():40s} truth={s.total_effect('X', 'Y'):.2f} unmeasured={s.graph.unmeasured} feedback={s.graph.feedback}")
hidden_confounder_worldfn

hidden_confounder_world() -> LinearSCM

The confounded world with ``Z`` unmeasured.

print(scm.observed(frame).columns.tolist(), "| unmeasured dropped:", hidden_confounder_world().observed(hidden_confounder_world().simulate(3, seed=0)).columns.tolist())
iv_worldfn

iv_world() -> LinearSCM

``Z -> X`` (1.0), ``X -> Y`` (2.0), ``X <-> Y`` (latent scale 1.0).

for w in (confounded_world, hidden_confounder_world, iv_world, frontdoor_world, mediator_world, feedback_world):
    s = w()
    print(f"{w.__name__:24s} {s.graph.to_text():40s} truth={s.total_effect('X', 'Y'):.2f} unmeasured={s.graph.unmeasured} feedback={s.graph.feedback}")
latent_keyfn

latent_key(a: str, b: str) -> str

The ``latent_sd`` key for the bidirected edge ``a <-> b``: ``"a<->b"`` with ``a < b``.

print(coefficient_key("Z", "X"), latent_key("Y", "X"))
long_framefn

long_frame(columns: Mapping[str, ArrayLike], *, units: Sequence[str], periods: Sequence[int] | None = None, unit_column: str = 'unit', time_column: str = 't') -> DataFrame

Stack ``(n_units, n_periods)`` arrays into a unit-major, period-minor long table.

frame = long_frame({"x": [[1.0, 2.0], [3.0, 4.0]], "y": [[0.5, 0.6], [0.7, 0.8]]}, units=("u0", "u1"))
mediator_worldfn

mediator_world() -> LinearSCM

``X -> M`` (1.0), ``M -> Y`` (0.5), ``X -> Y`` (1.0); everything measured, no confounding.

for w in (confounded_world, hidden_confounder_world, iv_world, frontdoor_world, mediator_world, feedback_world):
    s = w()
    print(f"{w.__name__:24s} {s.graph.to_text():40s} truth={s.total_effect('X', 'Y'):.2f} unmeasured={s.graph.unmeasured} feedback={s.graph.feedback}")
panel_from_arraysfn

panel_from_arrays(*, doses: Mapping[str, ArrayLike], outcome: ArrayLike, treatments: Sequence[Treatment], outcome_entity: Outcome, covariates: Mapping[str, tuple[Covariate, ArrayLike]] | None = None, units: Sequence[str], periods: Sequence[int] | None = None, unit_column: str = 'unit', time_column: str = 't') -> Panel

A balanced ``Panel`` from wide arrays plus the entities that give the columns roles.

panel = panel_from_arrays(
    doses={"x": [[1.0, 2.0], [3.0, 4.0]]}, outcome=[[0.5, 0.6], [0.7, 0.8]],
    treatments=[Treatment(name="x", dimension=D.currency, unit="USD")],
    outcome_entity=Outcome(name="y", dimension=D.outcome), units=("u0", "u1"),
)
surface_worldfn

surface_world(*, n_units: int, n_periods: int, treatments: Sequence[Treatment | str] = ('a', 'b'), kernels: Mapping[str, Kernel] | Kernel | None = None, carryover: Mapping[str, Carryover] | Carryover | None = None, doses: Mapping[str, DosePlan] | DosePlan | None = None, nuisance: NuisanceSet | None = None, intercept: InterceptKind = 'hierarchical', interactions: Sequence[tuple[str, str]] = (), outcome: Outcome | None = None, likelihood: Likelihood | None = None, truth: Mapping[str, ArrayLike] | None = None, truth_mode: TruthMode = 'centre', noise_sd: float | None = None, intercept_scale: float = 1.0, seed: int | None = None, name: str = 'surface_world') -> SurfaceWorld

A units × periods panel world with saturation, carryover, intercepts, and nuisance.

world: SurfaceWorld = surface_world(
    n_units=4, n_periods=26, treatments=("a", "b"),
    kernels={"a": HillKernel(reference_dose=50.0), "b": HillKernel(reference_dose=20.0)},
    carryover={"a": GeometricCarryover(max_lag=4)},
    doses={"a": plan, "b": DosePlan(scale=20.0, spread=0.4)},
    nuisance=NuisanceSet(terms=(FourierSeasonality(period=13.0, order=1),)),
    intercept="hierarchical", truth_mode=mode, noise_sd=0.5, seed=3,
)
transport_pairfn

transport_pair() -> tuple[LinearSCM, LinearSCM]

A source and a target population that differ only in ``Z``'s distribution.

src, tgt = transport_pair()
true_parametersfn

true_parameters(model: ModelSpec, *, structural: Sequence[str] = (), truth: Mapping[str, ArrayLike] | None = None, mode: TruthMode = 'centre', seed: int | None = None) -> dict[str, Array]

A full true-parameter dictionary for ``model`` (see the module docstring for the rules).

theta_prior = true_parameters(world.model, structural=world.structural, mode="prior", seed=11)
unit_labelsfn

unit_labels(n_units: int, prefix: str = 'u') -> tuple[str, ...]

``("u00", "u01", …)`` — zero-padded so that string order is numeric order.

print(unit_labels(3))

axiom.design

pillar165 symbols · 8 notebooks · walkthrough

Power, MDE, value of information, method leaderboards, sequential boundaries.

ASSUMPTIONSvalue

Read-only proxy of a mapping.

print("assumption 'parallel_trends':", ASSUMPTIONS["parallel_trends"].statement)
AnchoredEffectspec

AnchoredEffect(parameter: str, posterior_mean: float, posterior_sd: float, posterior_interval: Interval, n_draws: int, mde: float, probability_exceeds_mde: float, probability_exceeds_mde_gaussian: float, credence: float, anchored_effect: float, already_believed: bool)

A posterior's view of an MDE.

assert isinstance(ae, AnchoredEffect)
Assignmentspec

Assignment(unit: Unit, labels: tuple[str, ...], treated: tuple[int, ...], control: tuple[int, ...], pairs: tuple[tuple[int, int], ...], unpaired: tuple[int, ...], metric: Literal['level', 'trajectory'], seed: int | None, pre_mean_treated: float, pre_mean_control: float, pre_smd: float, mean_pair_distance: float)

A matched-pair treatment assignment of clusters.

asg: Assignment = match_clusters(pre, labels=[f"r{i}" for i in range(9)], unit=region, metric=metric, seed=1)
Boundaryspec

Boundary(kind: Literal['efficacy', 'harm', 'futility'], side: Literal['upper', 'lower', 'two_sided'], z: tuple[float, ...], binding: bool = True, spent: tuple[float, ...] = (), detail: dict[str, str] = {})

Signed thresholds on the monitoring statistic, one per look.

futility = Boundary(kind="futility", side="lower", z=(-1.5, -0.8, -0.2, 0.3, 0.8), binding=False)
BoundaryKindcallable

BoundaryKind(*args, **kwargs)

kind: BoundaryKind = with_margin.kind
CANONICALvalue

A named, falsifiable condition that licenses a step.

print("named assumptions:", CANONICAL.name, "|", STOPPED_ESTIMATE_BIAS.name)
CalibrationResultspec

CalibrationResult(method: str, design: Literal['holdout', 'switchback', 'ghost'], n_simulations: int, n_unsupported: int, false_positive_count: int, false_positive_rate: float, alpha: float, region: AcceptanceRegion, passed: bool, reason: str = '', seed: int)

A method's A/A false-positive count against its exact binomial acceptance region.

cal: CalibrationResult = calibrate_method("difference_in_differences", spec, alpha=0.05)
CandidateScorespec

CandidateScore(candidate: DesignCandidate, eig: float, evpi: float, evsi: float, opportunity_cost: float, cost: float, net_value: float, power: float, effect: float, alpha: float, numeraire: str = '', detail: dict[str, str] = {})

A candidate's information, value, cost, and power on one decision.

scores: list[CandidateScore] = [evaluate_candidate(c, decision, prior_mean=mean, prior_sd=sd, economics=economics) for c in designs]
ClusterDesignspec

ClusterDesign(unit: Unit, n_clusters: int, cluster_size: int, icc: float, allocation: float = 0.5)

A cluster-randomized design and its variance inflation.

cd = ClusterDesign(unit=region, n_clusters=24, cluster_size=40, icc=0.05, allocation=0.5)
Combinationspec

Combination(exponents: dict[str, int], kind: Literal['symmetry', 'estimable'], scaling: Literal['log', 'absolute'] = 'log', score: float, label: str = '')

A monomial (or linear combination) of parameters, and how flat it is.

ratio = Combination(exponents={"alpha": 1, "k": -1}, kind="estimable", score=0.0)
CostPerOutcomeIntervalspec

CostPerOutcomeInterval(cost: float, effect: float, effect_se: float, alpha: float, estimate: float, lower: float, upper: float | None, method: Literal['fieller'] = 'fieller', status: Literal['bounded', 'unbounded'], effect_interval: Interval, naive_se: float, naive_interval: Interval)

``cost / effect`` with a Fieller interval and the naive delta-method interval beside it.

ci: CostPerOutcomeInterval = cost_per_outcome_interval(cost=1000.0, effect=25.0, effect_se=6.0)
CostPerOutcomePowerspec

CostPerOutcomePower(cost: float, true_effect: float, effect_se: float, alpha: float, threshold: float | None, effect_required: float, power: float)

Power to bound the cost per outcome unit (below ``threshold`` if one is set).

cp: CostPerOutcomePower = cost_per_outcome_power(1000.0, true_effect=25.0, effect_se=6.0, threshold=60.0)
CrossingProbabilitiesspec

CrossingProbabilities(drift: float, information: tuple[float, ...], per_look: dict[str, tuple[float, ...]], continue_probability: float, binding_only: bool)

Per-look probability that each boundary is the one that stops the study.

assert isinstance(spent, CrossingProbabilities)
DESIGN_RELATIVE_SEvalue

Read-only proxy of a mapping.

print("relative SE by design kind:", dict(DESIGN_RELATIVE_SE))
Decisioncallable

Decision(*args, **kwargs)

decision: Decision = path.decision
DecisionSpecspec

DecisionSpec(name: str = 'act', threshold: float = 0.0, value_per_outcome_unit: float = 1.0, numeraire: str = '')

A two-action decision with value linear in the parameter.

decision = DecisionSpec(name="scale_up", threshold=0.0, value_per_outcome_unit=1000.0, numeraire="USD")
DerivativeMethodcallable

DerivativeMethod(*args, **kwargs)

method: DerivativeMethod = "finite"
DesignCandidatespec

DesignCandidate(name: str, method: str, n_units: int, n_periods: int, holdout_fraction: float, experiment_se: float, cost: float, n_clusters: int | None = None, cooldown_periods: int = 0, detail: dict[str, str] = {})

One concrete experiment design: method, size, horizon, holdout, precision, cost.

designs = [
    DesignCandidate(name="small_holdout", method="difference_in_differences", n_units=20, n_periods=6, holdout_fraction=0.2, experiment_se=0.35, cost=200.0, cooldown_periods=2),
    DesignCandidate(name="large_holdout", method="cluster_based_regression", n_units=60, n_periods=8, holdout_fraction=0.4, experiment_se=0.12, cost=600.0, n_clusters=12, cooldown_periods=4),
    DesignCandidate(name="switchback", method="switchback", n_units=20, n_periods=10, holdout_fraction=0.5, experiment_se=0.2, cost=300.0, cooldown_periods=1),
    DesignCandidate(name="ghost", method="ghost", n_units=40, n_periods=6, holdout_fraction=0.3, experiment_se=0.3, cost=450.0),
]
EIGEstimatespec

EIGEstimate(eig: float, se: float, n_sims: int, seed: int | None = None, method: Literal['gaussian', 'nested_monte_carlo'], n_prior_draws: int = 0, experiment_se: float, unit: Literal['nats'] = 'nats')

Expected information gain in nats, with the error of its estimator.

est: EIGEstimate = eig_monte_carlo(prior_draws, 0.3, n_sims=n_sims, seed=1)
EVOIResultspec

EVOIResult(evpi: float, evsi: float, preposterior_sd: float, decision_threshold: float, n_sims: int, seed: int | None = None, method: Literal['gaussian', 'monte_carlo'], evsi_se: float = 0.0, experiment_se: float, prior_mean: float, prior_sd: float, value_per_outcome_unit: float, ... and 1 more field)

EVPI and EVSI of one experiment against one decision, in the numeraire.

res: EVOIResult = evoi_gaussian(decision, mean, sd, se)
EconomicInputsspec

EconomicInputs(value_per_outcome: ValuePerOutcome, dose_per_period: float, discount_rate: float, dose_unit: str = 'dose', dose_cost_per_unit: float = 0.0, marginal_value_ratio: float | None = None)

The economic inputs a candidate's opportunity cost needs, stated once.

economics = EconomicInputs(value_per_outcome=vpo, dose_per_period=20.0, discount_rate=0.02, dose_unit="L", dose_cost_per_unit=1.0, marginal_value_ratio=1.2)
EstimabilityReportspec

EstimabilityReport(parameters: tuple[str, ...], observations: tuple[str, ...] = (), scaling: Literal['log', 'absolute'] = 'log', n_points: int = 1, rank: int, deficiency: int, persistent_deficiency: int, singular_values: tuple[float, ...] = (), condition_number: float, symmetries: tuple[Combination, ...] = (), estimable: tuple[Combination, ...] = (), null_basis: tuple[tuple[float, ...], ...] = (), ... and 4 more fields)

What this design can and cannot separate, at one parameter point and across several.

report: EstimabilityReport = estimable_combinations([low], theta, tolerance=3e-2)
ExperimentValuespec

ExperimentValue(information_value: float, opportunity_cost: float, fixed_cost: float, net: float, numeraire: str, detail: dict[str, str] = {})

``net = information_value − opportunity_cost − fixed_cost``, all in ``numeraire``.

ev: ExperimentValue = experiment_value(info, oc, fixed_cost=250.0)
FisherInformationspec

FisherInformation(parameters: tuple[str, ...], matrix: tuple[tuple[float, ...], ...], noise_sd: float, n_observations: int, det: float, min_eigenvalue: float, singular: bool, method: Literal['jax', 'finite'], detail: dict[str, str] = {})

``J'J / noise_sd²`` for the named parameters at one design and one ``theta``.

assert isinstance(fi, FisherInformation)
HoldoutTradeoffspec

HoldoutTradeoff(design: ClusterDesign, fractions: tuple[float, ...], mdes: tuple[float, ...], relative_mde: tuple[float, ...], n_holdout: tuple[int, ...], best_fraction: float, alpha: float, power: float, two_sided: bool)

MDE as a function of the share of clusters held out of treatment.

ht: HoldoutTradeoff = holdout_tradeoff(cd, sd=2.0, fractions=(0.1, 0.2, 0.3, 0.4, 0.5))
IdentifiabilityRidgespec

IdentifiabilityRidge(parameters: tuple[str, ...], direction: dict[str, float], min_eigenvalue: float, max_eigenvalue: float, condition_number: float, pairs: tuple[tuple[str, str], ...] = (), correlations: tuple[float, ...] = (), detail: dict[str, str] = {})

The flattest direction of the information matrix and the couplings it implies.

assert isinstance(ridge, IdentifiabilityRidge)
IdentifyingDesignspec

IdentifyingDesign(target: str, design: Design, indices: tuple[int, ...], expected_sd: float, expected_sds: dict[str, float], prior_sds: dict[str, float] = {}, noise_sd: float, seed: int | None = None, n_restarts: int, passes: int, detail: dict[str, str] = {})

The ``n`` candidate rows that minimize a target parameter's expected posterior sd.

assert isinstance(out, IdentifyingDesign)
Leaderboardspec

Leaderboard(spec: SimulationSpec, alpha: float, effects: tuple[float, ...], rows: tuple[LeaderboardRow, ...], calibrations: tuple[CalibrationResult, ...], powers: tuple[tuple[SimulatedPower, ...], ...])

Methods ranked by mean power at a calibrated size; uncalibrated methods rank last.

board: Leaderboard = leaderboard(small, effects=(0.8, 1.6), alpha=0.05)
LeaderboardRowspec

LeaderboardRow(method: str, design: Literal['holdout', 'switchback', 'ghost'], status: Literal['stable', 'experimental'], calibrated: bool, false_positive_rate: float, powers: tuple[float, ...], mean_power: float, biases: tuple[float, ...], coverages: tuple[float, ...], rmses: tuple[float, ...], n_unsupported: int, rank: int)

One method's line: its calibrated size and its power, bias and coverage per effect.

assert isinstance(row, LeaderboardRow)
LearningPriorityspec

LearningPriority(treatment: str, eig: float, evoi: float, net_value: float, cost: float, rank: int, numeraire: str = '', detail: dict[str, str] = {})

A candidate's EIG, EVSI, and net value, with its rank (``1`` is first).

assert isinstance(p, LearningPriority)
LookOutcomeclass

LookOutcome(look: int, label: str, information: float, z: float, decision: Decision, crossed: str = '', effect: float | None = None, se: float | None = None) -> None

One interim look: what it saw and what the rule said about it.

assert isinstance(outcome, LookOutcome)
LookSchedulespec

LookSchedule(labels: tuple[str, ...], information: tuple[float, ...])

When the study looks at itself, and how much information it has when it does.

schedule = LookSchedule(labels=tuple(f"look_{k + 1}" for k in range(LOOKS)), information=information)
MDEspec

MDE(design: Literal['difference_in_means', 'coefficient'], effect: float, se: float, alpha: float, power: float, two_sided: bool, n: int | None = None, sd: float | None = None, allocation: float | None = None)

The smallest effect detected with probability ``power`` at level ``alpha``.

m: MDE = mde(200, sd=2.0, power=0.8)
METHODSvalue

Read-only proxy of a mapping.

for name, spec in METHODS.items():
    print(f"{name:28s} status={spec.status:12s} pre={spec.requires_pre_period!s:5} controls={spec.requires_controls!s:5} {spec.assumption_names()}")
MatchMetriccallable

MatchMetric(*args, **kwargs)

metric: MatchMetric = "trajectory"
MethodEstimatespec

MethodEstimate(method: str, effect: float, se: float, interval: Interval, n_treated: int, n_control: int, n_pre: int, n_post: int, se_method: str, critical: Literal['normal', 'student_t'] = 'normal', detail: dict[str, float] = {})

One method's estimate: the average effect per treated unit per post period.

assert isinstance(out, MethodEstimate)
MethodNamecallable

MethodName(*args, **kwargs)

name: MethodName = "switchback"
MethodSpecspec

MethodSpec(name: str, assumptions: tuple[Assumption, ...], requires_pre_period: bool, requires_controls: bool, status: Literal['stable', 'experimental'] = 'stable', description: str = '')

A registered experiment method: its assumptions, requirements, and status.

ms: MethodSpec = method_spec("switchback")
MethodStatuscallable

MethodStatus(*args, **kwargs)

status: MethodStatus = ms.status
MonitoringPathclass

MonitoringPath(rule: StoppingRule, looks: tuple[LookOutcome, ...], decision: Decision, stopped_at: int | None = None) -> None

A realized sequence of looks and the decision it produced.

assert isinstance(path, MonitoringPath)
Observationclass

Observation(label: str, expr: Expr, data: Mapping[str, Any] = <factory>, noise_sd: float = 1.0) -> None

One measurable quantity, the data it is measured at, and its noise.

low = Observation("low dose", hill, {"dose": np.linspace(0.01, 0.3, 12)}, noise_sd=0.05)
OperatingCharacteristicsspec

OperatingCharacteristics(rule: str, drift: float, crossings: CrossingProbabilities, expected_information: float, expected_looks: float)

What a rule does at one drift: where it stops, and what it costs to get there.

assert isinstance(oc, OperatingCharacteristics)
OpportunityCostspec

OpportunityCost(value: float, numeraire: str, holdout_fraction: float, n_periods: int, discount_rate: float, dose_per_period: float, dose_unit: str, dose_withheld: float, dose_withheld_discounted: float, dose_cost_per_unit: float, ratio_mean: float, ratio_sd: float, ... and 5 more fields)

What a holdout forgoes, in the numeraire, with every factor that produced it.

oc: OpportunityCost = opportunity_cost(0.25, 8, dose_per_period=20.0, marginal_value_ratio=ratio_draws,
                                      value_per_outcome=vpo, discount_rate=0.02, dose_unit="L", dose_cost_per_unit=1.0)
PanelArraysclass

PanelArrays(outcome: Array, treated: tuple[int, ...], pre: slice, post: slice, assignment: Array | None = None, exposed: Array | None = None) -> None

The arrays every method reads.

arrays: PanelArrays = simulate_panel(spec, rng, design="holdout")
PanelDesigncallable

PanelDesign(*args, **kwargs)

d: PanelDesign = design_for_method(m)
Parametercallable

Parameter(*args, **kwargs)

parameter: Parameter = "value_per_outcome"
Patterncallable

Pattern(*args, **kwargs)

p: Pattern = s.pattern
PowerCurvespec

PowerCurve(design: Literal['difference_in_means', 'coefficient'], effects: tuple[float, ...], powers: tuple[float, ...], se: float, alpha: float, two_sided: bool, n: int | None = None, sd: float | None = None, allocation: float | None = None)

Power as a function of effect size at a fixed design (``n``, ``sd``, ``allocation``).

curve: PowerCurve = power_curve(200, 2.0, effects=np.linspace(0.0, 1.2, 7))
PowerResultspec

PowerResult(design: Literal['difference_in_means', 'coefficient'], power: float, effect: float, se: float, alpha: float, two_sided: bool, n: int | None = None, sd: float | None = None, allocation: float | None = None)

Power to detect ``effect`` at ``alpha`` with standard error ``se``.

pr: PowerResult = power(200, effect=0.6, sd=2.0)
Prescriptionspec

Prescription(added: tuple[str, ...] = (), rank_before: int, rank_after: int, n_parameters: int, broken: tuple[tuple[str, ...], ...] = (), still_flat: tuple[str, ...] = (), considered: tuple[str, ...] = (), complete: bool = False, detail: dict[str, str] = {})

The smallest set of extra measurements that restores rank, and what each one buys.

plan: Prescription = prescribe_measurements([low], [wide], theta, tolerance=3e-2)
ProfileReportspec

ProfileReport(targets: tuple[str, ...], truth: dict[str, float], recovered: dict[str, float], interval: dict[str, tuple[float, float]], level: float, threshold: float, flat: tuple[str, ...] = (), n_observations: int, seed: int | None = None, converged: bool = True, detail: dict[str, str] = {})

Profile-likelihood intervals for named targets, from one simulated experiment.

profile: ProfileReport = simulated_identifiability(
    model, {"dose": doses}, truth,
    targets=["alpha", "k"], combinations=[ratio],
    seed=3, range_factor=30.0, n_grid=11,
)
ProgramSchedulespec

ProgramSchedule(horizon_periods: int, slots: tuple[ScheduledExperiment, ...], skipped: tuple[str, ...], total_net_value: float, numeraire: str = '', detail: dict[str, str] = {})

A non-overlapping sequence of experiments within ``horizon_periods``.

sched: ProgramSchedule = schedule_with_cooldown(scores, horizon_periods=20)
ReExperimentTimingspec

ReExperimentTiming(posterior_sd: float, half_life_periods: float, experiment_se: float, min_eig: float, sd_at_threshold: float, periods: float, eig_now: float, design_kind: str = 'unspecified')

When an experiment with ``experiment_se`` is worth ``min_eig`` nats again.

timing: ReExperimentTiming = time_to_re_experiment(post_sd, 6.0, experiment_se=0.2, min_eig=0.3, design_kind="cluster_holdout")
Recommendationspec

Recommendation(selected: tuple[str, ...], priorities: tuple[LearningPriority, ...], budget: float | None = None, total_cost: float, total_net_value: float, numeraire: str = '', detail: dict[str, str] = {})

The selected treatments and why: the ranked priorities and the budget arithmetic.

assert isinstance(rec, Recommendation)
STOPPED_ESTIMATE_BIASvalue

A named, falsifiable condition that licenses a step.

print("named assumptions:", CANONICAL.name, "|", STOPPED_ESTIMATE_BIAS.name)
SampleSizespec

SampleSize(design: Literal['difference_in_means', 'coefficient'], n: int, effect: float, se: float, alpha: float, power: float, target_power: float, two_sided: bool, sd: float | None = None, allocation: float | None = None, n_treated: int | None = None, n_control: int | None = None)

The smallest ``n`` whose achieved power reaches the target for ``effect``.

assert isinstance(ss, SampleSize)
Scalingcallable

Scaling(*args, **kwargs)

scaling: Scaling = "log"
Schedulespec

Schedule(pattern: Literal['constant', 'pulse', 'ramp', 'random_switchback', 'alternating'], n_periods: int, doses: tuple[float, ...], treatment: str = 'dose', detail: dict[str, float] = {})

One treatment's dose per period: ``doses[t]`` for ``t = 0 … n_periods − 1``.

schedules: list[Schedule] = [
    constant(T, 50.0),
    pulse(T, 80.0, 20.0, on=2, off=2),
    alternating(T, 80.0, 20.0),
    ramp(T, 0.0, 100.0),
    random_switchback(T, 80.0, 20.0, seed=0),
]
ScheduledExperimentspec

ScheduledExperiment(name: str, start: int, end: int, free_at: int, net_value: float)

One slot of a program: ``[start, end)`` is the run, ``[end, free_at)`` the cooldown.

assert isinstance(slot, ScheduledExperiment)
SensitivityMatrixclass

SensitivityMatrix(matrix: Array, parameters: tuple[str, ...], observations: tuple[str, ...], scaling: Scaling, theta: dict[str, float], zero_columns: tuple[str, ...] = ()) -> None

``d f / d log theta`` (or ``d f / d theta``) stacked over observations.

sensitivity: SensitivityMatrix = sensitivity_matrix([low], theta, scaling=scaling)
SensitivityTablespec

SensitivityTable(parameter: Literal['prior_sd', 'experiment_se', 'value_per_outcome', 'discount_rate', 'holdout_fraction'], mode: Literal['absolute', 'multiplier'], grid: tuple[float, ...], candidates: tuple[str, ...], net_values: tuple[tuple[float, ...], ...], winners: tuple[str, ...], tipping_points: tuple[tuple[float, float], ...], base_value: float, base_winner: str, base_net_values: tuple[float, ...], numeraire: str = '', detail: dict[str, str] = {})

Net value of every candidate at every grid value of one input, and where the winner flips.

table: SensitivityTable = perturb(designs, decision, mean, sd, economics, parameter, grid=(0.5, 1.0, 2.0, 3.0, 5.0, 8.0))
Sidecallable

Side(*args, **kwargs)

side: Side = with_margin.side
SimulatedPowerspec

SimulatedPower(method: str, design: Literal['holdout', 'switchback', 'ghost'], effect: float, truth: float, n_simulations: int, n_unsupported: int, rejections: int, power: float, alpha: float, mean_effect: float, bias: float, coverage: float, ... and 5 more fields)

Realized A/B power and accuracy of a method at one effect size.

sp: SimulatedPower = simulated_power("difference_in_differences", ab, predicted_power=predicted)
SimulationSpecspec

SimulationSpec(n_units: int = 20, n_periods: int = 16, n_pre: int = 8, n_treated: int = 10, unit_sd: float = 1.0, noise_sd: float = 1.0, period_sd: float = 0.5, rho: float = 0.5, effect: float = 0.0, n_simulations: int = 200, seed: int = 0, mass: float = 0.95, ... and 2 more fields)

One simulated panel: its shape, its variance components, its effect, and its budget.

spec = SimulationSpec(n_units=16, n_periods=20, n_pre=10, n_treated=8, effect=1.0, noise_sd=1.0, seed=7)
SpendingFunctioncallable

SpendingFunction(*args, **kwargs)

kind_: SpendingFunction = kind
StoppingRulespec

StoppingRule(name: str, looks: LookSchedule, boundaries: tuple[Boundary, ...])

A look schedule and the boundaries evaluated at every look.

rule = StoppingRule(name=name, looks=schedule, boundaries=(boundary,))
StudySummaryspec

StudySummary(treatment: str, estimate: float, se: float, periods_ago: float, definition: str, name: str = '')

One past study of a treatment: its estimate, standard error, and age.

history = [
    StudySummary(treatment="fertilizer", estimate=1.4, se=0.3, periods_ago=12.0, definition="wald"),
    StudySummary(treatment="fertilizer", estimate=1.0, se=0.4, periods_ago=2.0, definition="wald"),
]
TreatmentCandidatespec

TreatmentCandidate(name: str, prior_mean: float, prior_sd: float, experiment_se: float, decision: DecisionSpec, opportunity_cost: float = 0.0, fixed_cost: float = 0.0)

A treatment one could run an experiment on, with the inputs its value needs.

candidates = [
    TreatmentCandidate(name="fertilizer", prior_mean=mean, prior_sd=sd, experiment_se=0.2, decision=decision, opportunity_cost=oc.value, fixed_cost=250.0),
    TreatmentCandidate(name="irrigation", prior_mean=0.8, prior_sd=0.6, experiment_se=0.3, decision=decision, opportunity_cost=100.0, fixed_cost=400.0),
    TreatmentCandidate(name="pruning", prior_mean=0.1, prior_sd=0.1, experiment_se=0.3, decision=decision, opportunity_cost=0.0, fixed_cost=60.0),
]
ValueFncallable

ValueFn(*args, **kwargs)

capped: ValueFn = lambda theta: np.minimum(decision.payoff(theta), 200.0)
ValuePerOutcomespec

ValuePerOutcome(value: float, outcome_unit: str, numeraire: str, source: str, assumption: Assumption | None = None)

``value`` numeraire units per ``outcome_unit``, and where that number came from.

vpo = ValuePerOutcome(value=3.0, outcome_unit="kg", numeraire="USD", source="contract price, 2026 season")
alpha_spendingfn

alpha_spending(alpha: float, information: Sequence[float] | LookSchedule, *, family: SpendingFunction = 'obrien_fleming', side: Side = 'two_sided', kind: BoundaryKind = 'efficacy', rho: float = 1.0, n_grid: int = 601, width: float = 8.0) -> Boundary

A Lan–DeMets boundary: solve each threshold to spend exactly what is budgeted.

efficacy = alpha_spending(ALPHA / 2, information, family="obrien_fleming", side="upper")
alternatingfn

alternating(n_periods: int, high: float, low: float, *, treatment: str = 'dose') -> Schedule

``high, low, high, low, …`` — the maximal-contrast pulse with ``on = off = 1``.

schedules: list[Schedule] = [
    constant(T, 50.0),
    pulse(T, 80.0, 20.0, on=2, off=2),
    alternating(T, 80.0, 20.0),
    ramp(T, 0.0, 100.0),
    random_switchback(T, 80.0, 20.0, seed=0),
]
anchor_drawsfn

anchor_draws(draws: ArrayLike, parameter: str, mde: float, *, credence: float = 0.9, definition: IntervalDefinition = 'hdi', mass: float = 0.9) -> AnchoredEffect | Unsupported

``anchor_effect`` on a flat array of draws of one scalar parameter.

print("from flat draws:", round(anchor_draws(draws.ravel(), "beta", 0.5).anchored_effect, 3))
anchor_effectfn

anchor_effect(posterior: SupportsPosterior, parameter: str, mde: float, *, index: int | None = None, credence: float = 0.9, definition: IntervalDefinition = 'hdi', mass: float = 0.9) -> AnchoredEffect | Unsupported

Anchor an MDE against the posterior of ``parameter``.

ae = anchor_effect(posterior, "beta", mde=0.5, credence=0.9)
bartlett_bandwidthfn

bartlett_bandwidth(n_periods: int) -> int

Newey–West rule of thumb ``max(1, floor(4 · (T/100)^(2/9)))``.

print(f"switchback: {sb.effect:.3f} ± {sb.se:.3f}  bandwidth={sb.detail.get('bandwidth')}  (rule: {bartlett_bandwidth(spec.n_periods)})")
calibrate_methodfn

calibrate_method(method: str | MethodSpec, spec: SimulationSpec, *, alpha: float = 0.05, registry: Mapping[str, MethodSpec] = ..., design: PanelDesign | None = None) -> CalibrationResult

A/A calibration: ``spec`` with ``effect=0`` and intervals at mass ``1 − alpha``.

cal: CalibrationResult = calibrate_method("difference_in_differences", spec, alpha=0.05)
calibrate_registryfn

calibrate_registry(spec: SimulationSpec, registry: Mapping[str, MethodSpec] = ..., *, alpha: float = 0.05) -> tuple[Mapping[str, MethodSpec], tuple[CalibrationResult, ...]]

Calibrate every method in ``registry`` and return a new registry with statuses set.

registry, results = calibrate_registry(small, alpha=0.05)
cluster_mdefn

cluster_mde(design: ClusterDesign, sd: float, *, alpha: float = 0.05, power: float = 0.8, two_sided: bool = True) -> MDE

Minimum detectable effect under ``design``; see ``cluster_power`` for the scale.

print("cluster MDE:", round(cluster_mde(cd, sd=2.0).effect, 4))
cluster_powerfn

cluster_power(design: ClusterDesign, effect: float, sd: float, *, alpha: float = 0.05, two_sided: bool = True) -> PowerResult

Power to detect ``effect`` on the individual-level scale under ``design``.

print("cluster power for effect 0.5:", round(cluster_power(cd, effect=0.5, sd=2.0).power, 3))
clusters_neededfn

clusters_needed(effect: float, sd: float, cluster_size: int, icc: float, *, alpha: float = 0.05, power: float = 0.8, two_sided: bool = True, allocation: float = 0.5) -> SampleSize | Unsupported

Smallest number of clusters of size ``cluster_size`` reaching ``power`` for ``effect``.

cn = clusters_needed(effect=0.5, sd=2.0, cluster_size=40, icc=0.05)
coefficient_mdefn

coefficient_mde(se: float, *, alpha: float = 0.05, power: float = 0.8, two_sided: bool = True) -> MDE

Minimum detectable coefficient given its design standard error.

print("coefficient_mde:", round(coefficient_mde(se=0.1, power=0.9).effect, 4))
coefficient_powerfn

coefficient_power(effect: float, se: float, *, alpha: float = 0.05, two_sided: bool = True) -> PowerResult

Power to detect a coefficient of size ``effect`` whose design SE is ``se``.

print("coefficient_power:", round(coefficient_power(0.3, se=0.1).power, 3))
coefficient_sample_sizefn

coefficient_sample_size(effect: float, se: float, n: int, *, alpha: float = 0.05, power: float = 0.8, two_sided: bool = True) -> SampleSize | Unsupported

Observations needed for a coefficient whose SE is ``se`` at ``n`` observations.

css = coefficient_sample_size(effect=0.2, se=0.1, n=500)
constantfn

constant(n_periods: int, dose: float, *, treatment: str = 'dose') -> Schedule

The same dose in every period — no temporal contrast.

flat = fisher_information(world.surface, data_with(constant(T, 50.0)), world.theta, NOISE_SD, method=method)
contrast_scorefn

contrast_score(schedule: Schedule) -> float

Mean squared first difference over mean squared dose (module docstring); ``0`` if flat.

print(f"{p:18s} mean={s.mean:5.1f} total={s.total:6.1f} contrast={contrast_score(s):.3f}  {s.doses[:6]}...")
cost_per_outcome_intervalfn

cost_per_outcome_interval(cost: float, effect: float, effect_se: float, *, alpha: float = 0.05) -> CostPerOutcomeInterval

Fieller and naive intervals for ``cost / effect`` at confidence ``1 − alpha``.

ci: CostPerOutcomeInterval = cost_per_outcome_interval(cost=1000.0, effect=25.0, effect_se=6.0)
cost_per_outcome_powerfn

cost_per_outcome_power(cost: float, true_effect: float, effect_se: float, *, alpha: float = 0.05, threshold: float | None = None) -> CostPerOutcomePower

Probability the Fieller upper bound is finite (``threshold=None``) or below ``threshold``.

cp: CostPerOutcomePower = cost_per_outcome_power(1000.0, true_effect=25.0, effect_se=6.0, threshold=60.0)
crossing_probabilitiesfn

crossing_probabilities(rule: StoppingRule, drift: float, *, binding_only: bool = True, n_grid: int = 601, width: float = 8.0) -> CrossingProbabilities

Exact first-crossing probabilities for ``rule`` at ``drift``.

spent = crossing_probabilities(harm_only, 0.0)
decayed_sdfn

decayed_sd(posterior_sd: float, periods_elapsed: float, half_life_periods: float) -> float

``posterior_sd · 2^(t / (2 · half_life))``: the posterior variance doubles every half-life.

print("decayed sd after 0, 6, 12 periods (hl=6):", [round(decayed_sd(post_sd, t, 6.0), 4) for t in (0, 6, 12)])
design_effectfn

design_effect(cluster_size: int, icc: float) -> float

``1 + (m − 1) · icc`` — variance inflation of a cluster-randomized mean.

print("design effect:", design_effect(40, 0.05), "| effective n:", round(effective_sample_size(24, 40, 0.05), 1))
design_for_methodfn

design_for_method(method: str | MethodSpec, registry: Mapping[str, MethodSpec] = ...) -> PanelDesign

The panel design a method's data requirements imply.

d: PanelDesign = design_for_method(m)
design_to_identifyfn

design_to_identify(surface: SupportsForward, candidates: Design, theta: Theta, noise_sd: float, target: str, n: int, *, prior_sds: Mapping[str, float] | None = None, seed: int | None = None, n_restarts: int = 3, max_passes: int = 20, method: DerivativeMethod = 'auto', parameter_scales: Mapping[str, float] | None = None) -> IdentifyingDesign | Unsupported

The ``n`` candidate rows (replicates allowed) minimizing ``target``'s expected posterior sd.

print("one row under flat priors ->", type(design_to_identify(steady, candidates, theta, NOISE_SD, target="k_a", n=1, seed=0, method=method)).__name__)
difference_in_differences_sefn

difference_in_differences_se(spec: SimulationSpec) -> float

Exact SE of the DiD estimator under the spec's DGP (derivation in the module docstring).

se = difference_in_differences_se(spec)
difference_sefn

difference_se(n: int, sd: float, allocation: float = 0.5) -> float

``sd / sqrt(n · p · (1 − p))`` — the SE of a two-arm difference in means.

se = difference_se(200, sd=2.0, allocation=0.5)
discount_weightsfn

discount_weights(n_periods: int, rate: float) -> Array

``(1 + rate)^(-t)`` for ``t = 0 … n_periods − 1``: period 0 is undiscounted.

print("discount weights (8 periods, 2%):", np.round(discount_weights(8, 0.02), 4))
effective_sample_sizefn

effective_sample_size(n_clusters: int, cluster_size: int, icc: float) -> float

``k · m / DE`` — the number of independent observations the design is worth.

print("design effect:", design_effect(40, 0.05), "| effective n:", round(effective_sample_size(24, 40, 0.05), 1))
eig_gaussianfn

eig_gaussian(prior_sd: float, experiment_se: float) -> float

``0.5 · ln(1 + prior_sd² / experiment_se²)`` nats.

print(f"experiment_se={se:.2f}  EIG = {eig_gaussian(prior_sd, se):.4f} nats")
eig_monte_carlofn

eig_monte_carlo(prior_draws: ArrayLike, experiment_se: float, n_sims: int = 2000, seed: int | None = None, *, chunk: int = 256) -> EIGEstimate

Nested Monte-Carlo EIG of ``y = theta + N(0, experiment_se²)`` under an empirical prior.

est: EIGEstimate = eig_monte_carlo(prior_draws, 0.3, n_sims=n_sims, seed=1)
elasticityfn

elasticity(table: SensitivityTable) -> dict[str, float]

``(Δnet / net_base) / (Δx / x_base)`` per candidate, from the grid values around the base.

print("elasticities:", {k: round(v, 3) for k, v in elasticity(table).items()})
estimable_combinationsfn

estimable_combinations(observations: Sequence[Observation], theta: Theta, *, at: Sequence[Theta] = (), parameters: Sequence[str] | None = None, scaling: Scaling = 'log', tolerance: float = 1e-08, direction_tolerance: float | None = None, max_support: int = 3, max_coefficient: int = 3, max_candidates: int = 200000, step: float = 0.0001, parameter_scales: Mapping[str, float] | None = None) -> EstimabilityReport | Unsupported

Rank, flat directions, and the named combinations this design can estimate.

report = estimable_combinations([low], theta, tolerance=tolerance)
estimatefn

estimate(method: str | MethodSpec, arrays: PanelArrays, *, mass: float = 0.95, registry: Mapping[str, MethodSpec] = ...) -> MethodEstimate | Unsupported

Run a registered method on ``arrays`` with a ``wald`` interval at ``mass``.

out = estimate(method, arrays, mass=0.95)
estimate_cluster_based_regressionfn

estimate_cluster_based_regression(outcome: ArrayLike, treated: Sequence[int], pre: slice, post: slice, *, mass: float = 0.95) -> MethodEstimate | Unsupported

Keyword convenience: build the ``PanelArrays`` and run ``estimate_arrays``.

cbr = estimate_cluster_based_regression(y, tr, pre, post)
estimate_difference_in_differencesfn

estimate_difference_in_differences(outcome: ArrayLike, treated: Sequence[int], pre: slice, post: slice, *, mass: float = 0.95) -> MethodEstimate | Unsupported

Keyword convenience: build the ``PanelArrays`` and run ``estimate_arrays``.

did = estimate_difference_in_differences(y, tr, pre, post)
estimate_ghostfn

estimate_ghost(outcome: ArrayLike, treated: Sequence[int], post: slice, exposed: ArrayLike, *, pre: slice = slice(0, 0, None), mass: float = 0.95) -> MethodEstimate | Unsupported

Keyword convenience: build ``PanelArrays`` with ``exposed`` and run ``estimate_arrays``.

gh = estimate_ghost(gh_arrays.outcome, gh_arrays.treated, gh_arrays.post, gh_arrays.exposed)
estimate_switchbackfn

estimate_switchback(outcome: ArrayLike, assignment: ArrayLike, *, treated: Sequence[int] = (), pre: slice = slice(0, 0, None), post: slice = slice(None, None, None), mass: float = 0.95) -> MethodEstimate | Unsupported

Keyword convenience: build ``PanelArrays`` with a schedule and run ``estimate_arrays``.

sb = estimate_switchback(sb_arrays.outcome, sb_arrays.assignment)
estimate_synthetic_controlfn

estimate_synthetic_control(outcome: ArrayLike, treated: Sequence[int], pre: slice, post: slice, *, mass: float = 0.95, rmspe_ratio_max: float | None = None) -> MethodEstimate | Unsupported

Keyword convenience with the optional RMSPE-ratio placebo filter.

sc = estimate_synthetic_control(y, tr, pre, post, rmspe_ratio_max=5.0)
estimate_time_based_regressionfn

estimate_time_based_regression(outcome: ArrayLike, treated: Sequence[int], pre: slice, post: slice, *, mass: float = 0.95) -> MethodEstimate | Unsupported

Keyword convenience: build the ``PanelArrays`` and run ``estimate_arrays``.

tbr = estimate_time_based_regression(y, tr, pre, post)
evaluate_candidatefn

evaluate_candidate(candidate: DesignCandidate, decision: DecisionSpec, prior_mean: float, prior_sd: float, economics: EconomicInputs, *, alpha: float = 0.05, effect: float | None = None) -> CandidateScore

Score ``candidate`` on ``decision`` under ``theta ~ N(prior_mean, prior_sd²)``.

scores: list[CandidateScore] = [evaluate_candidate(c, decision, prior_mean=mean, prior_sd=sd, economics=economics) for c in designs]
evoi_gaussianfn

evoi_gaussian(decision: DecisionSpec, prior_mean: float, prior_sd: float, experiment_se: float) -> EVOIResult

``evpi_gaussian`` and ``evsi_gaussian`` together, as an ``EVOIResult``.

res: EVOIResult = evoi_gaussian(decision, mean, sd, se)
evpifn

evpi(decision: DecisionSpec, prior_draws: ArrayLike, *, value_fn: ValueFn | None = None) -> float

Monte-Carlo EVPI: ``mean(max(payoff, 0)) − max(mean(payoff), 0)`` over the prior draws.

print("MC EVPI:", round(evpi(decision, draws), 2), "vs closed", round(evpi_gaussian(decision, mean, sd), 2))
evpi_gaussianfn

evpi_gaussian(decision: DecisionSpec, prior_mean: float, prior_sd: float) -> float

Expected value of perfect information under ``theta ~ N(prior_mean, prior_sd²)``.

print("EVPI:", round(evpi_gaussian(decision, mean, sd), 2), decision.numeraire)
evsifn

evsi(decision: DecisionSpec, prior_draws: ArrayLike, experiment_se: float, *, n_sims: int = 1000, seed: int | None = None, value_fn: ValueFn | None = None, chunk: int = 256) -> EVOIResult

Monte-Carlo EVSI of observing ``y = theta + N(0, experiment_se²)``.

mc = evsi(decision, draws, se, n_sims=n_sims, seed=2)
evsi_gaussianfn

evsi_gaussian(decision: DecisionSpec, prior_mean: float, prior_sd: float, experiment_se: float) -> float

Expected value of the sample information an experiment with ``experiment_se`` yields.

print("EVSI:", round(evsi_gaussian(decision, mean, sd, se), 2), decision.numeraire)
expected_posterior_sdfn

expected_posterior_sd(prior_sds: Mapping[str, float] | None, fisher: FisherInformation) -> dict[str, float] | Unsupported

Laplace posterior sds ``sqrt(diag((diag(1/sd²) + FI)⁻¹))`` per parameter coordinate.

print("expected posterior sd (flat prior) ->", type(expected_posterior_sd(None, flat)).__name__)
experiment_se_for_designfn

experiment_se_for_design(design_kind: str, reference_value: float, *, relative_se: float | None = None) -> float | Unsupported

``relative_se · |reference_value|``, with ``relative_se`` looked up by design kind.

se_holdout = experiment_se_for_design("cluster_holdout", reference_value=2.0)
experiment_valuefn

experiment_value(information_value: float, opportunity_cost: OpportunityCost | float, fixed_cost: float, *, numeraire: str = '') -> ExperimentValue

``information_value − opportunity_cost − fixed_cost`` in one numeraire.

ev: ExperimentValue = experiment_value(info, oc, fixed_cost=250.0)
fisher_informationfn

fisher_information(surface: SupportsForward, design_doses: Theta, theta: Theta, noise_sd: float, *, parameters: Sequence[str] | None = None, method: DerivativeMethod = 'auto', parameter_scales: Mapping[str, float] | None = None) -> FisherInformation | Unsupported

``J'J / noise_sd²`` at ``design_doses`` and ``theta`` for the mean's parameters.

fi = fisher_information(world.surface, data_with(s), world.theta, NOISE_SD, method=method)
harm_boundaryfn

harm_boundary(probability: float, information: Sequence[float] | LookSchedule, *, margin: float = 0.0, se_at_full_information: float = 1.0, kind: BoundaryKind = 'harm') -> Boundary

The Z threshold of a posterior-probability harm rule, look by look.

flat = harm_boundary(0.95, information)
holdout_tradeofffn

holdout_tradeoff(design: ClusterDesign, sd: float, fractions: Sequence[float] = (0.1, 0.2, 0.3, 0.4, 0.5), *, alpha: float = 0.05, power: float = 0.8, two_sided: bool = True) -> HoldoutTradeoff

MDE at each holdout share for ``design`` (its own ``allocation`` is ignored).

ht: HoldoutTradeoff = holdout_tradeoff(cd, sd=2.0, fractions=(0.1, 0.2, 0.3, 0.4, 0.5))
identifiability_ridgefn

identifiability_ridge(surface: SupportsForward, design_doses: Theta, theta: Theta, noise_sd: float, *, pairs: Sequence[tuple[str, str]] = (), parameters: Sequence[str] | None = None, method: DerivativeMethod = 'auto', parameter_scales: Mapping[str, float] | None = None) -> IdentifiabilityRidge | Unsupported

The flattest direction of the information matrix at this design, and pairwise couplings.

ridge = identifiability_ridge(world.surface, data_with(pulse(T, 80.0, 20.0, on=2, off=2)), world.theta, NOISE_SD, pairs=(("beta_a", "k_a"),), method=method)
information_fractionsfn

information_fractions(counts: Sequence[float], total: float | None = None) -> tuple[float, ...]

Information fractions from cumulative counts (enrolled, completed, events).

print("from cumulative enrollment:", information_fractions([48, 96, 144, 192, 240]))
information_half_lifefn

information_half_life(prior_sd: float, posterior_sd: float, half_life_periods: float) -> float

Periods until half the information the experiment bought has decayed.

print("information half-life:", round(information_half_life(prior_sd, post_sd, 6.0), 2), "periods")
information_value_offn

information_value_of(decision: DecisionSpec, prior_mean: float, prior_sd: float, experiment_se: float) -> float

EVSI of an experiment measuring the decision parameter to ``experiment_se``.

info = information_value_of(decision, prior_mean=1.2, prior_sd=0.5, experiment_se=0.2)
leaderboardfn

leaderboard(spec: SimulationSpec, effects: Sequence[float], registry: Mapping[str, MethodSpec] = ..., *, alpha: float = 0.05) -> Leaderboard

Rank ``registry``'s methods by mean power over ``effects`` at the calibrated size.

board: Leaderboard = leaderboard(small, effects=(0.8, 1.6), alpha=0.05)
match_clustersfn

match_clusters(pre_outcomes: ArrayLike, *, labels: Sequence[str] | None = None, unit: Unit | None = None, metric: MatchMetric = 'level', seed: int | None = None) -> Assignment

Pair clusters by pre-period similarity and randomize one member of each pair.

asg: Assignment = match_clusters(pre, labels=[f"r{i}" for i in range(9)], unit=region, metric=metric, seed=1)
max_detectable_cost_per_outcomefn

max_detectable_cost_per_outcome(cost: float, effect_mde: float) -> float

``cost / effect_mde``: the largest cost per outcome unit the experiment can bound.

print("largest bounded cost per outcome at the MDE:", round(max_detectable_cost_per_outcome(1000.0, m.effect), 2))
mdefn

mde(n: int, sd: float, *, alpha: float = 0.05, power: float = 0.8, two_sided: bool = True, allocation: float = 0.5) -> MDE

Minimum detectable effect of a two-arm difference in means with ``n`` units in total.

m: MDE = mde(200, sd=2.0, power=0.8)
method_assumptionfn

method_assumption(name: str, statement: str, challenged_by: str) -> Assumption

A method-facet ``Assumption`` in its default ``unverified`` state.

extra = method_assumption("balanced_exposure", "exposure is balanced across arms", "an imbalance test")
method_specfn

method_spec(method: str | MethodSpec, registry: Mapping[str, MethodSpec] = ...) -> MethodSpec

Resolve a name through ``registry`` (a ``MethodSpec`` passes through).

ms: MethodSpec = method_spec("switchback")
mid_horizon_factorfn

mid_horizon_factor(n_periods: int, rate: float) -> float

Mean of ``discount_weights``: undiscounted total × this = discounted total.

print("mid-horizon factor:", round(mid_horizon_factor(8, 0.02), 4))
monitorfn

monitor(rule: StoppingRule, z: Sequence[float], *, effects: Sequence[float] | None = None, ses: Sequence[float] | None = None) -> MonitoringPath

Walk realized statistics against ``rule`` and stop at the first crossing.

path = monitor(rule, observed_z[:3], effects=effects[:3], ses=ses[:3])
obrien_flemingfn

obrien_fleming(alpha: float, information: Sequence[float] | LookSchedule, *, side: Side = 'two_sided', kind: BoundaryKind = 'efficacy', n_grid: int = 601, width: float = 8.0) -> Boundary

Thresholds ``c / sqrt(t)``, solved so the total error is ``alpha``.

shapes = {
    "pocock": pocock(ALPHA, information),
    "obrien_fleming": obrien_fleming(ALPHA, information),
    "lan_demets_obf": alpha_spending(ALPHA, information, family="obrien_fleming"),
    "lan_demets_pocock": alpha_spending(ALPHA, information, family="pocock"),
}
observation_from_modelfn

observation_from_model(model: ModelSpec, data: Mapping[str, Any], *, noise_sd: float | None = None, theta: Theta | None = None, label: str = '') -> Observation

The observation a fitted ``ModelSpec`` represents: its mean over the panel's rows.

print(observation_from_model(model, {"dose": np.array([1.0])}, theta=truth).noise_sd)
operating_characteristicsfn

operating_characteristics(rule: StoppingRule, drift: float, *, binding_only: bool = True, n_grid: int = 601, width: float = 8.0) -> OperatingCharacteristics

:func:`crossing_probabilities` plus the expected information and look count.

null = operating_characteristics(rule, 0.0)
opportunity_costfn

opportunity_cost(holdout_fraction: float, n_periods: int, dose_per_period: float, marginal_value_ratio: float | ArrayLike, value_per_outcome: ValuePerOutcome, discount_rate: float, *, dose_unit: str = 'dose', dose_cost_per_unit: float = 0.0) -> OpportunityCost

Signed value forgone by withholding ``holdout_fraction`` of the dose for ``n_periods``.

negative = opportunity_cost(0.25, 8, 20.0, 0.2, vpo, 0.02, dose_cost_per_unit=1.0)
panel_arraysfn

panel_arrays(outcome: ArrayLike, treated: Sequence[int], pre: slice, post: slice, *, assignment: ArrayLike | None = None, exposed: ArrayLike | None = None) -> PanelArrays

Validate and freeze the arrays a method reads; see ``PanelArrays``.

all_treated = panel_arrays(y, treated=range(y.shape[0]), pre=pre, post=post)
pareto_frontfn

pareto_front(scores: Sequence[CandidateScore], objectives: Sequence[str] = ('net_value', '-cost', 'eig')) -> tuple[CandidateScore, ...]

The non-dominated scores: nothing is at least as good on every objective and better on one.

front = pareto_front(scores, objectives=("net_value", "-cost", "eig"))
perturbfn

perturb(candidates: Sequence[DesignCandidate], decision: DecisionSpec, prior_mean: float, prior_sd: float, economics: EconomicInputs, parameter: Parameter, grid: Sequence[float], *, alpha: float = 0.05, effect: float | None = None) -> SensitivityTable

Re-score every candidate at each grid value of ``parameter``; report winners and flips.

table: SensitivityTable = perturb(designs, decision, mean, sd, economics, parameter, grid=(0.5, 1.0, 2.0, 3.0, 5.0, 8.0))
pocockfn

pocock(alpha: float, information: Sequence[float] | LookSchedule, *, side: Side = 'two_sided', kind: BoundaryKind = 'efficacy', n_grid: int = 601, width: float = 8.0) -> Boundary

A constant critical value on the Z scale, solved so the total error is ``alpha``.

shapes = {
    "pocock": pocock(ALPHA, information),
    "obrien_fleming": obrien_fleming(ALPHA, information),
    "lan_demets_obf": alpha_spending(ALPHA, information, family="obrien_fleming"),
    "lan_demets_pocock": alpha_spending(ALPHA, information, family="pocock"),
}
powerfn

power(n: int, effect: float, sd: float, *, alpha: float = 0.05, two_sided: bool = True, allocation: float = 0.5) -> PowerResult

Power of a two-arm difference in means with ``n`` units in total.

pr: PowerResult = power(200, effect=0.6, sd=2.0)
power_curvefn

power_curve(n: int, sd: float, effects: Sequence[float], *, alpha: float = 0.05, two_sided: bool = True, allocation: float = 0.5) -> PowerCurve

Power over a grid of effect sizes for a fixed two-arm design.

curve: PowerCurve = power_curve(200, 2.0, effects=np.linspace(0.0, 1.2, 7))
power_from_sefn

power_from_se(effect: float, se: float, *, alpha: float = 0.05, two_sided: bool = True) -> PowerResult

Power for an effect whose estimator has standard error ``se``.

print("power_from_se:", round(power_from_se(0.3, 0.1).power, 3))
preposterior_sdfn

preposterior_sd(prior_sd: float, experiment_se: float) -> float

``prior_sd · preposterior_sd_ratio``: the sd of the posterior mean to come.

print("preposterior ratio:", round(preposterior_sd_ratio(sd, se), 4), "| preposterior sd:", round(preposterior_sd(sd, se), 4))
preposterior_sd_ratiofn

preposterior_sd_ratio(prior_sd: float, experiment_se: float) -> float

``sqrt(prior_sd² / (prior_sd² + experiment_se²))``.

print("preposterior ratio:", round(preposterior_sd_ratio(sd, se), 4), "| preposterior sd:", round(preposterior_sd(sd, se), 4))
prescribe_measurementsfn

prescribe_measurements(observations: Sequence[Observation], candidates: Sequence[Observation], theta: Theta, *, parameters: Sequence[str] | None = None, scaling: Scaling = 'log', tolerance: float = 1e-08, max_additions: int = 4, step: float = 0.0001, parameter_scales: Mapping[str, float] | None = None) -> Prescription | Unsupported

Greedily choose candidates until the design has full rank, or say what is left.

plan: Prescription = prescribe_measurements([low], [wide], theta, tolerance=3e-2)
prior_from_historyfn

prior_from_history(summaries: Sequence[StudySummary], half_life_periods: float) -> tuple[float, float]

``(mean, sd)`` of the inverse-variance combination of the decayed studies.

mean, sd = prior_from_history(history, half_life_periods=8.0)
profile_combinationfn

profile_combination(model: ModelSpec, data: Mapping[str, Any], start: Mapping[str, Any], combination: Combination, *, grid: Sequence[float]) -> tuple[tuple[float, ...], tuple[float, ...]]

The profile of a *combination* of parameters — ``alpha / k`` rather than ``alpha``.

values, drops = profile_combination(model, simulated, truth, ratio, grid=ratio_grid)
profile_likelihoodfn

profile_likelihood(model: ModelSpec, data: Mapping[str, Any], start: Mapping[str, Any], target: str, *, grid: Sequence[float]) -> tuple[tuple[float, ...], tuple[float, ...]]

``(values, drops)``: the profile of one parameter, as the drop in log-likelihood.

values, drops = profile_likelihood(model, simulated, truth, "alpha", grid=grid)
pulsefn

pulse(n_periods: int, high: float, low: float, *, on: int, off: int, start_on: bool = True, treatment: str = 'dose') -> Schedule

``on`` periods at ``high`` then ``off`` at ``low``, repeating.

pulsed = fisher_information(world.surface, data_with(pulse(T, 80.0, 20.0, on=2, off=2)), world.theta, NOISE_SD, method=method)
rampfn

ramp(n_periods: int, start: float, stop: float, *, treatment: str = 'dose') -> Schedule

Linear from ``start`` at period 0 to ``stop`` at the last period (``start`` alone if one).

schedules: list[Schedule] = [
    constant(T, 50.0),
    pulse(T, 80.0, 20.0, on=2, off=2),
    alternating(T, 80.0, 20.0),
    ramp(T, 0.0, 100.0),
    random_switchback(T, 80.0, 20.0, seed=0),
]
random_switchbackfn

random_switchback(n_periods: int, high: float, low: float, *, seed: int | None, p_high: float = 0.5, treatment: str = 'dose') -> Schedule

Each period independently at ``high`` with probability ``p_high``, else ``low``; seeded.

schedules: list[Schedule] = [
    constant(T, 50.0),
    pulse(T, 80.0, 20.0, on=2, off=2),
    alternating(T, 80.0, 20.0),
    ramp(T, 0.0, 100.0),
    random_switchback(T, 80.0, 20.0, seed=0),
]
rank_treatmentsfn

rank_treatments(candidates: Sequence[TreatmentCandidate]) -> tuple[LearningPriority, ...]

Every candidate scored and ranked by net value, then EIG, then name.

for p in rank_treatments(candidates):
    assert isinstance(p, LearningPriority)
    print(f"#{p.rank} {p.treatment:12s} eig={p.eig:.3f} evsi={p.evoi:8.2f} net={p.net_value:8.2f} cost={p.cost:7.2f}")
recommendfn

recommend(candidates: Sequence[TreatmentCandidate], budget: float | None = None) -> Recommendation | Unsupported

Greedy knapsack over ``rank_treatments``: net value per cost, within ``budget``.

rec = recommend(candidates, budget=600.0)
ridge_offn

ridge_of(fisher: FisherInformation, *, pairs: Sequence[tuple[str, str]] = ()) -> IdentifiabilityRidge | Unsupported

``identifiability_ridge`` from an information matrix already computed.

print("from the flat design:", ridge_of(flat).ridge_parameters, "| with pairs ->", type(ridge_of(flat, pairs=(("beta_a", "k_a"),))).__name__)
sample_sizefn

sample_size(effect: float, sd: float, *, alpha: float = 0.05, power: float = 0.8, two_sided: bool = True, allocation: float = 0.5) -> SampleSize | Unsupported

Smallest total ``n`` (integer arms at ``allocation``) reaching ``power`` for ``effect``.

ss = sample_size(effect=0.6, sd=2.0, power=0.8)
schedule_with_cooldownfn

schedule_with_cooldown(scores: Sequence[CandidateScore], horizon_periods: int) -> ProgramSchedule

Greedy end-to-end sequence: best net value first, each run then its cooldown.

sched: ProgramSchedule = schedule_with_cooldown(scores, horizon_periods=20)
sensitivity_matrixfn

sensitivity_matrix(observations: Sequence[Observation], theta: Theta, *, parameters: Sequence[str] | None = None, scaling: Scaling = 'log', step: float = 0.0001, parameter_scales: Mapping[str, float] | None = None) -> SensitivityMatrix | Unsupported

Central differences of every observation with respect to every parameter.

sensitivity: SensitivityMatrix = sensitivity_matrix([low], theta, scaling=scaling)
simplex_weightsfn

simplex_weights(target: Array, donors: Array) -> Array

Non-negative weights summing to one that best reproduce ``target`` from ``donors`` rows.

w = simplex_weights(target, donors)
simulate_panelfn

simulate_panel(spec: SimulationSpec, rng: Generator, *, design: PanelDesign = 'holdout') -> PanelArrays

Draw one panel from ``spec`` and apply ``spec.effect`` under ``design``.

arrays: PanelArrays = simulate_panel(spec, rng, design="holdout")
simulated_identifiabilityfn

simulated_identifiability(model: ModelSpec, data: Mapping[str, Any], truth: Mapping[str, Any], *, targets: Sequence[str] | None = None, combinations: Sequence[Combination] = (), seed: int | None = None, level: float = 0.95, range_factor: float = 20.0, n_grid: int = 21, noise_sd: float | None = None) -> ProfileReport | Unsupported

Run the proposed experiment on simulated data and profile each target.

print(simulated_identifiability(model, {"dose": doses}, {"alpha": 4.0, "k": 10.0}).missing)
simulated_powerfn

simulated_power(method: str | MethodSpec, spec: SimulationSpec, *, registry: Mapping[str, MethodSpec] = ..., design: PanelDesign | None = None, predicted_power: float | None = None) -> SimulatedPower

A/B simulation at ``spec.effect``: power, bias, RMSE and coverage at ``spec.mass``.

sp: SimulatedPower = simulated_power("difference_in_differences", ab, predicted_power=predicted)
spendingfn

spending(kind: SpendingFunction, t: float, alpha: float, *, rho: float = 1.0) -> float

Cumulative error a spending function allows to have been spent by fraction ``t``.

print(f"{'power (rho=3)':16s} " + " ".join(f"{spending('power', t, ALPHA, rho=3.0):7.4f}" for t in grid))
t_criticalfn

t_critical(mass: float, df: float) -> float

Student-t two-sided critical value ``t_{df, (1 + mass) / 2}``.

print(df, round(t_critical(0.95, df), 4))
time_to_re_experimentfn

time_to_re_experiment(posterior_sd: float, half_life_periods: float, experiment_se: float, min_eig: float, *, design_kind: str = 'unspecified') -> ReExperimentTiming

First period at which re-running the experiment is worth ``min_eig`` nats.

timing: ReExperimentTiming = time_to_re_experiment(post_sd, 6.0, experiment_se=0.2, min_eig=0.3, design_kind="cluster_holdout")
wald_tfn

wald_t(effect: float, se: float, mass: float, df: float) -> Interval

``effect ± t_critical(mass, df) · se`` as a ``wald`` interval at ``mass``.

print(wald_t(1.0, 0.5, 0.95, df=10))

axiom.calibrate

pillar31 symbols · 4 notebooks · walkthrough

Fold a randomized result into an observational model, and log what that assumed.

Agreementspec

Agreement(estimand_name: str, estimand_hash: str, measurement_hash: str, source: str, estimate: float, se: float, posterior_mean: float, posterior_sd: float, interval: Interval, z: float, p: float, inside: bool, ... and 5 more fields)

How a measurement sits against the posterior of its own estimand.

assert isinstance(ok, Agreement)
AgreementVerdictcallable

AgreementVerdict(*args, **kwargs)

verdict: AgreementVerdict = ok.verdict
CalibratedSpecspec

CalibratedSpec(spec: SurfaceSpec, treatment: str, parameter: str, prior: Prior, family: Literal['lognormal', 'gamma'], target: str, sources: tuple[str, ...], plan_statuses: dict[str, str], target_mean: float, target_se: float, design_factor: float, amplitude_mean: float, ... and 2 more fields)

The prior route's output: a new surface spec and how it was reached.

assert isinstance(cal, CalibratedSpec)
Correctionspec

Correction(name: str, kind: Literal['multiplicative', 'additive', 'se_scale'], value: float, counterfactual: float, corrected: float, detail: dict[str, str] = {}, ledger_line: LedgerLine)

One correction operator's output: the number applied and the line that records it.

assert isinstance(c, Correction)
CorrectionKindcallable

CorrectionKind(*args, **kwargs)

kind: CorrectionKind = c.kind
FACET_PREFIXvalue

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

print(FACET_PREFIX, "->", [facet_of(line) for line in baseline.lines])
Ledgerspec

Ledger(lines: tuple[LedgerLine, ...] = ())

An immutable sequence of ``LedgerLine``; see the module docstring for the rule.

pruned = Ledger(lines=tuple(ln for ln in ledger.lines if facet_of(ln) != "level"))
Measurementspec

Measurement(estimand: Estimand, estimate: float, se: float, definition: Literal['wald', 'eti', 'hdi'] = 'wald', mass: float = 0.95, method: str = '', n_units: int | None = None, n_periods: int | None = None, design_factor: float | None = None, source: str, assumptions: tuple[Assumption, ...] = ())

One external estimate of an estimand, with its uncertainty and provenance (D6.1).

m2 = Measurement(estimand=lift, estimate=0.9, se=0.5, definition="eti", mass=0.9, source="study-2")
PriorFamilycallable

PriorFamily(*args, **kwargs)

family: PriorFamily = fam
ResolvedTransferspec

ResolvedTransfer(status: Literal['identified', 'downgraded', 'blocked', 'unsupported', 'unverified'], plan: TransferPlan, factor: float = 1.0, offset: float = 0.0, se_scale: float = 1.0, corrections: tuple[Correction, ...] = (), transport: Verdict | None = None, ledger: Ledger, completeness: Verdict, reason: str = '')

A plan with its corrections applied: the composite operator and the ledger that licenses it.

resolved: ResolvedTransfer = resolve(plan, corrections=[window, level])
UNCORRECTEDvalue

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

print("UNCORRECTED =", repr(UNCORRECTED))
aggregation_levelfn

aggregation_level(level_source: Level, level_target: Level, *, cluster_size: int, icc: float) -> Correction | Unsupported

Individual ↔ cluster ↔ aggregate: point factor 1, SE through ``variance_reweight``.

level = aggregation_level(source.level, target.level, cluster_size=25, icc=0.05)
agreementfn

agreement(producer: SupportsEstimands, measurement: Measurement, *, definition: IntervalDefinition = 'hdi', mass: float = 0.9, seed: int | None = None, tension_at: float = 1.0, disagrees_at: float = 2.0) -> Agreement | Unsupported | Blocked

Realize ``measurement.estimand`` on ``producer`` and compare (module docstring).

ok = agreement(calibrated, experiment, definition="hdi", mass=0.9, seed=0)
amplitude_priorfn

amplitude_prior(mean: float, sd: float, family: PriorFamily = 'lognormal') -> Prior

The moment-matched ``Prior`` for a positive amplitude with the given mean and sd.

print(amplitude_prior(amp_mean, amp_sd, family))
attachfn

attach(measurement: Measurement, surface: Surface, data: Mapping[str, ArrayLike], *, doses: DoseMap | None = None) -> ModelSpec | Unsupported

``surface.model`` with the measurement's constraint appended, or why it cannot be.

model = attach(experiment, surface, data)
carryover_window_factorfn

carryover_window_factor(carryover: CarryoverKernel, theta: Theta, window_periods: int, *, treatment: str) -> Correction | Unsupported

``factor = 1 / Σ_{l < window_periods} w_l``: a short-window total scaled to steady state.

window = carryover_window_factor(geo, {"lam_a": lam}, 2, treatment="a")
chord_to_marginalfn

chord_to_marginal(surface: Surface, theta: Theta, treatment: str, d0: float, d1: float, at: float, *, held: Mapping[str, ArrayLike] | None = None) -> Correction | Unsupported

``factor = f'(at) / ((f(d1) − f(d0)) / (d1 − d0))``: read a chord as a marginal.

c = chord_to_marginal(surface, theta, "a", d0, d1, at)
combine_inverse_variancefn

combine_inverse_variance(targets: Sequence[float], ses: Sequence[float]) -> tuple[float, float]

Fixed-effect pooling of independent estimates of one quantity: ``(mean, se)``.

mean, se = combine_inverse_variance([m1.estimate, m2.estimate], [m1.se, m2.se])
combine_measurementsfn

combine_measurements(measurements: Sequence[Measurement], *, target: Estimand | None = None, corrections: Mapping[str, float] | None = None) -> tuple[float, float, tuple[LedgerLine, ...], dict[str, str]] | Unsupported

Pool measurements on one target estimand: ``(mean, se, ledger_lines, plan_statuses)``.

combined = combine_measurements([experiment, second])
constraint_forfn

constraint_for(measurement: Measurement, surface: Surface, data: Mapping[str, ArrayLike], *, doses: DoseMap | None = None) -> Constraint | Unsupported

The soft constraint that says the surface predicts ``measurement`` at its experiment.

c = constraint_for(experiment, surface, data)
derive_priorfn

derive_prior(measurements: Sequence[Measurement], spec: SurfaceSpec, treatment: str, *, beta_draws: ArrayLike, contribution_draws: ArrayLike, family: PriorFamily = 'lognormal', target: Estimand | None = None, corrections: Mapping[str, float] | None = None) -> CalibratedSpec | Unsupported

Turn measurements of a target estimand into an amplitude prior on ``spec`` (D6.2).

cal = derive_prior([experiment, second], spec, "a", beta_draws=beta_draws,
                   contribution_draws=contribution_draws, family=family)
design_factorfn

design_factor(beta_samples: ArrayLike, contribution_samples: ArrayLike) -> float

``mean(contribution / beta)`` over paired posterior draws.

f = design_factor(beta_draws, contribution_draws)
dose_path_accumulationfn

dose_path_accumulation(carryover: CarryoverKernel, theta: Theta, dose_path: ArrayLike, *, treatment: str) -> Correction | Unsupported

``factor = Σ_t (w ∗ x)_t / Σ_t x_t``: the effective dose delivered inside the path's window.

acc = dose_path_accumulation(geo, {"lam_a": lam}, path, treatment="a")
facet_offn

facet_of(line: LedgerLine) -> str | None

The facet a line stands in for, or ``None`` for a non-facet line.

print(FACET_PREFIX, "->", [facet_of(line) for line in baseline.lines])
fit_calibratedfn

fit_calibrated(spec: SurfaceSpec, panel: Panel, measurements: Sequence[Measurement], *, backend: Backend | str = 'laplace', draws: int = 1000, tune: int = 1000, chains: int = 4, seed: int | None = None, doses: DoseMap | None = None) -> FitResult | Unsupported

``surface.fit`` with every measurement attached as a soft constraint.

calibrated = fit_calibrated(spec, world.panel, [experiment], backend="laplace", draws=500, seed=0)
lognormal_from_momentsfn

lognormal_from_moments(mean: float, sd: float) -> tuple[float, float]

``(mu, sigma)`` of the lognormal with the given mean and standard deviation.

mu, sigma = lognormal_from_moments(amp_mean, amp_sd)
lognormal_mu_from_momentsfn

lognormal_mu_from_moments(value: float, se: float) -> float

``log(value) - sigma^2 / 2``: the log-scale location whose lognormal has mean ``value``.

mu = lognormal_mu_from_moments(2.0, 0.3)
lognormal_sigma_from_momentsfn

lognormal_sigma_from_moments(value: float, se: float) -> float

``sqrt(log(1 + (se / value)^2))``: the log-scale sigma of a lognormal with that mean and sd.

sigma = lognormal_sigma_from_moments(2.0, 0.3)
mean_sd_to_gammafn

mean_sd_to_gamma(mean: float, sd: float) -> tuple[float, float]

``(shape, rate)`` of the gamma with the given mean and standard deviation.

shape, rate = mean_sd_to_gamma(amp_mean, amp_sd)
resolvefn

resolve(plan: TransferPlan, *, transport: Verdict | None = None, corrections: Sequence[Correction] = ()) -> ResolvedTransfer

Compose the corrections over the plan and write the ledger (module docstring).

resolved: ResolvedTransfer = resolve(plan, corrections=[window, level])
variance_reweightfn

variance_reweight(se: float, n_source: int, n_target: int, *, icc: float | None = None, cluster_size: int | None = None) -> Correction

``se · sqrt(n_source / n_target) · sqrt(1 + (m − 1)·icc)``: the SE at another size.

vr = variance_reweight(0.3, 40, 80, icc=0.05, cluster_size=10)

axiom.meta

pillar70 symbols · 5 notebooks · walkthrough

Pool a corpus of studies; heterogeneity, bias terms, privacy-budgeted release.

BaujatDataspec

BaujatData(k: int, q_contribution: tuple[float, ...], influence: tuple[float, ...], detail: dict[str, str] = {})

Per-study ``Q`` contribution (x) against influence on the pooled estimate (y).

bj: BaujatData = baujat(y_asym, se)
Cellspec

Cell(records: tuple[tuple[str, float], ...], name: str = '')

The ``(contributor, value)`` pairs behind one publishable statistic.

small = Cell(records=(("c0", 0.4), ("c1", 0.5)), name="k-1")
Corpusspec

Corpus(records: tuple[StudyRecord, ...], name: str = 'corpus')

An ordered collection of ``StudyRecord`` with unique study ids.

corpus = Corpus(records=records, name="demo")
CorpusStoreclass

CorpusStore(root: str | Path) -> None

Records and corpora by content hash; corpora also findable by name.

store = CorpusStore(tmp)
DEFAULT_COLUMNSvalue

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

print("default mapping:", DEFAULT_COLUMNS)
EffectShrinkagespec

EffectShrinkage(effect: str, studies: tuple[str, ...], estimate: float, se: float, theta: float, interval: Interval, analytic: float, empirical: float | None)

One study effect's raw and shrunk estimate.

s0: EffectShrinkage = rows[0]
EggerTestspec

EggerTest(k: int, df: int, intercept: float, se: float, t: float, p: float, slope: float, slope_se: float, mass: float, interval: Interval, detail: dict[str, str] = {})

Egger's regression test for funnel asymmetry.

eg: EggerTest = egger(yy, se)
EpsilonChargespec

EpsilonCharge(release_id: str, epsilon: float, mechanism: Literal['laplace', 'gaussian'], delta: float = 0.0)

One ε charge against the ledger, named by the release that made it (timestamp-free).

line: EpsilonCharge = l2.charges[-1]
EpsilonLedgerspec

EpsilonLedger(budget: float, charges: tuple[EpsilonCharge, ...] = ())

An immutable ε budget with its sequence of charges (basic sequential composition).

ledger = EpsilonLedger(budget=policy.epsilon_total)
EpsilonSplitspec

EpsilonSplit(epsilon: float, parts: int, epsilons: tuple[float, ...], composition: str = 'sequential: the parts sum to epsilon')

An ε divided equally among ``parts`` releases under sequential composition.

split: EpsilonSplit = orthogonal_split(1.0, 3)
ForestDataspec

ForestData(rows: tuple[ForestRow, ...], mass: float, pooled_estimate: float, pooled_se: float | None, pooled_interval: Interval | None, prediction_interval: Interval | None, tau2: float | None, detail: dict[str, str] = {})

Per-study rows, the pooled row, and the prediction interval when available.

forest: ForestData = forest_data(corpus, None, re)
ForestRowspec

ForestRow(label: str, estimate: float, se: float, interval: Interval, weight: float | None = None)

One study on a forest plot: its estimate, Wald interval and pooling weight (share).

row: ForestRow = forest.rows[0]
FunnelContourspec

FunnelContour(mass: float, se: tuple[float, ...], lower: tuple[float, ...], upper: tuple[float, ...])

``pooled ± z_mass · se`` evaluated on a grid of standard errors.

c95: FunnelContour = next(c for c in fd.contours if c.mass == 0.95)
FunnelDataspec

FunnelData(k: int, y: tuple[float, ...], se: tuple[float, ...], precision: tuple[float, ...], pooled: float, contours: tuple[FunnelContour, ...], detail: dict[str, str] = {})

Study points (``y`` against ``se``) with pseudo-confidence contours around ``pooled``.

def outside_95(fd: FunnelData) -> int:
    c95: FunnelContour = next(c for c in fd.contours if c.mass == 0.95)
    z = (c95.upper[-1] - fd.pooled) / c95.se[-1]
    return int(np.sum(np.abs(np.asarray(fd.y) - fd.pooled) > z * np.asarray(fd.se)))
Heterogeneityspec

Heterogeneity(q: float, df: int, p_value: float, i2: float, h2: float, k: int)

Cochran's ``Q`` on ``df = k − 1`` with its chi-square ``p``, ``I²`` and ``H²``.

het: Heterogeneity = heterogeneity(y, se)
LeaveOneOutspec

LeaveOneOut(method: Literal['fe', 'dl', 'pm', 'reml'], k: int, mass: float, full_estimate: float, full_se: float, full_interval: Interval, estimates: tuple[float, ...], ses: tuple[float, ...], intervals: tuple[Interval, ...], influence: tuple[float, ...], tau2s: tuple[float, ...], detail: dict[str, str] = {})

Pooled estimates with each study removed, and each study's influence.

loo: LeaveOneOut = leave_one_out(y_sym, se, method=method)
Mechanismcallable

Mechanism(*args, **kwargs)

mech: Mechanism = "laplace"
ModeratorDesignclass

ModeratorDesign(names: tuple[str, ...], columns: Array, means: Array) -> None

Centered moderator columns ``(n_records, n_moderators)``, their names, and the means subtracted — a plain carrier (Specs hold no arrays).

assert isinstance(design, ModeratorDesign)
NO_DUAL_READvalue

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

print("without dual reads:", v_no.status, "|", v_no.reason, "| matches NO_DUAL_READ:", NO_DUAL_READ in v_no.reason)
POOLABLE_QUANTITIESvalue

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

for q in POOLABLE_QUANTITIES.values():
    print(f"{q.name:22s} dimensionless={str(q.dimensionless):5s} scale={q.scale:8s} {q.description}")
ParameterSummaryspec

ParameterSummary(name: str, mean: float, sd: float, interval: Interval, identified: bool = True, note: str = '')

Posterior mean, sd, and one provenance-carrying interval for a scalar parameter.

mu: ParameterSummary = res.mu
PoolMethodcallable

PoolMethod(*args, **kwargs)

method: PoolMethod = "reml"
PoolPriorsspec

PoolPriors(mu_scale: float = 1.0, tau_scale: float = 1.0, gamma_scale: float = 1.0, delta_scale: float = 1.0, tau_fixed: float | None = None)

Prior scales of the pool, all explicit.

priors = PoolPriors(mu_scale=2.0, tau_scale=0.5, gamma_scale=0.5)
PoolResultspec

PoolResult(family: str, quantity: str, scale: Literal['natural', 'log'], k: int, n_effects: int, mu: ParameterSummary, tau: ParameterSummary, gamma: tuple[ParameterSummary, ...], delta: ParameterSummary | None, thetas: tuple[ParameterSummary, ...], shrinkage: tuple[EffectShrinkage, ...], delta_verdict: Verdict | None, ... and 7 more fields)

Posterior summaries of one family's pool, every interval at ``mass``/``definition``.

res: PoolResult = out.result
PoolSpecspec

PoolSpec(family: str, moderators: tuple[str, ...] = (), bias_term: bool = False, effect_key: Literal['study', 'contributor'] = 'contributor', priors: PoolPriors = ..., parametrization: Literal['auto', 'centered', 'marginal'] = 'auto', mass: float = 0.95, definition: Literal['eti', 'hdi'] = 'eti')

What to pool and how.

spec = PoolSpec(family="fertilizer", moderators=("follow_up",), priors=priors, mass=0.9, definition="eti")
PoolableQuantityclass

PoolableQuantity(name: str, description: str, dimensionless: bool, scale: Scale) -> None

One entry of the catalog: what the quantity is, whether it is dimensionless by construction, and the scale it is pooled on.

entry: PoolableQuantity = poolable_quantity("elasticity")
Pooledclass

Pooled(result: PoolResult, posterior: Posterior, model: ModelSpec, data: dict[str, Array], spec: PoolSpec) -> None

``pool``'s return: the ``PoolResult`` plus the ``Posterior`` it was summarized from (``theta_<family>`` draws included under either parametrization), the ``ModelSpec`` and the data the backend saw — a plain carrier, so the draws stay out of the Spec.

assert isinstance(out, Pooled) and isinstance(out.posterior, Posterior)
PooledEstimatespec

PooledEstimate(estimate: float, se: float, interval: Interval, model: Literal['fixed', 'random'], tau2: float, tau_method: Literal['none', 'dl', 'pm', 'reml'], k: int, weights: tuple[float, ...], heterogeneity: Heterogeneity, knapp_hartung: bool = False, mass: float, tau_estimate: TauEstimate | None = None, ... and 1 more field)

The pooled mean, its standard error and interval, and everything that produced them.

fe: PooledEstimate = fixed_effect(y, se)
PriorTargetcallable

PriorTarget(*args, **kwargs)

t: PriorTarget = target
PrivacyPolicyspec

PrivacyPolicy(k: int = 3, dominance_p: float = 0.5, dominance_top_n: int = 2, dominance_top_p: float = 0.8, epsilon_total: float = 1.0)

Disclosure thresholds for one release programme.

policy = PrivacyPolicy(k=3, dominance_p=0.5, dominance_top_n=2, dominance_top_p=0.8, epsilon_total=1.0)
ReadKindcallable

ReadKind(*args, **kwargs)

def study(i: int, read: ReadKind) -> StudyRecord:
    return StudyRecord(
        study=f"s{i}", contributor=f"c{i % 3}", quantity="elasticity", estimate=Y[i], se=SE[i],
        read=read, family="fertilizer", n=40 + 10 * i, moderators={"follow_up": float(4 + 2 * i)},
        source=f"report-{i}",
    )
Releasespec

Release(release_id: str, cell_name: str = '', statistic: Literal['clipped_mean'] = 'clipped_mean', value: float, interval: Interval, mass: float, n: int, clip_lower: float, clip_upper: float, n_clipped: int, sensitivity: float, epsilon: float, ... and 7 more fields)

A published clipped mean with every input that made it reproducible and auditable.

rel_g: Release = g[0]
StudyRecordspec

StudyRecord(study: str, contributor: str, quantity: str, estimate: float, se: float, read: Literal['model', 'experiment'], family: str, estimand_hash: str = '', n: int | None = None, moderators: dict[str, float] = {}, dimension: Dimension = Dimension(1), unit_scale: str = '', ... and 3 more fields)

One study's estimate of one poolable quantity, with its uncertainty and provenance.

StudyRecord(study="bad", contributor="c", quantity="elasticity", estimate=1.0, se=0.1,
            read="model", family="fertilizer", dimension=D.outcome)
TauEstimatespec

TauEstimate(tau2: float, method: Literal['dl', 'pm', 'reml'], iterations: int, converged: bool, truncated: bool = False, tolerance: float = 1e-12, detail: dict[str, str] = {})

A between-study variance estimate with the record of how it was found.

t_reml: TauEstimate = estimates["reml"]
TauMethodcallable

TauMethod(*args, **kwargs)

tm: TauMethod = method
baujatfn

baujat(y: ArrayLike, se: ArrayLike) -> BaujatData

Baujat et al. (2002) under fixed-effect pooling.

bj: BaujatData = baujat(y_asym, se)
carry_forwardfn

carry_forward(previous: Release, cell: Cell, policy: PrivacyPolicy, *, churn_threshold: float) -> Release | Blocked

Re-use ``previous`` at no ε cost when contributor churn is below ``churn_threshold``.

carried = carry_forward(rel, same, policy, churn_threshold=0.25)
cell_from_recordsfn

cell_from_records(records: Iterable[Any], *, name: str = '') -> Cell

Build a ``Cell`` from objects exposing ``contributor`` and ``estimate`` (``StudyRecord``).

cell: Cell = cell_from_records(records, name="fertilizer/elasticity")
chargefn

charge(ledger: EpsilonLedger, *, release_id: str, epsilon: float, mechanism: Mechanism, delta: float = 0.0) -> EpsilonLedger | Blocked

A new ledger with the charge appended, or ``Blocked`` (ledger untouched) if over budget.

l1 = charge(ledger, release_id="q1", epsilon=0.3, mechanism=mech)
check_cellfn

check_cell(cell: Cell, policy: PrivacyPolicy) -> Verdict

``identified`` when the cell clears k-anonymity and both dominance rules; else ``blocked``.

ok: Verdict = check_cell(cell, policy)
contributor_totalsfn

contributor_totals(cell: Cell) -> dict[str, float]

Absolute contribution per contributor: the sum of ``|value|`` over its records.

print("totals:", contributor_totals(cell))
delta_identificationfn

delta_identification(corpus: Corpus, family: str | None = None) -> Verdict

``identified`` when at least one contributor in the family reports both a model and an experimental read; otherwise ``blocked`` with the reason ``"no contributor reports both reads"``.

v_yes: Verdict = delta_identification(with_dual, "fertilizer")
eggerfn

egger(y: ArrayLike, se: ArrayLike, *, mass: float = 0.95) -> EggerTest

Egger et al. (1997): OLS of ``z = y/se`` on precision ``1/se`` with an intercept.

eg: EggerTest = egger(yy, se)
fixed_effectfn

fixed_effect(y: ArrayLike, se: ArrayLike, *, mass: float = 0.95) -> PooledEstimate

Inverse-variance fixed-effect pooling; the interval is ``μ̂ ± z·se`` (``wald``).

fe: PooledEstimate = fixed_effect(y, se)
forest_datafn

forest_data(y_or_corpus: Any, se: ArrayLike | None, pooled: Any, *, labels: Sequence[str] | None = None, mass: float = 0.95) -> ForestData

Forest-plot data from ``(y, se)`` arrays or a ``Corpus`` (``se=None``) plus the pooled fit.

forest: ForestData = forest_data(corpus, None, re)
from_framefn

from_frame(df: DataFrame, *, columns: Mapping[str, str] | None = None, moderators: Sequence[str] = ()) -> tuple[StudyRecord, ...]

Build records from a frame under an explicit ``field → column`` mapping.

recs = from_frame(frame, columns={"study": "id", "contributor": "who", "estimate": "est", "se": "stderr"},
                  moderators=["weeks"])
funnel_datafn

funnel_data(y: ArrayLike, se: ArrayLike, pooled: Any, *, masses: Sequence[float] = (0.9, 0.95, 0.99), n_grid: int = 50) -> FunnelData

Points and closed-form contours ``pooled ± z_m · se`` for ``se`` from 0 to ``max(se)``.

print("bare float pooled also accepted:", funnel_data(y_sym, se, 0.5).pooled)
gaussian_sigmafn

gaussian_sigma(sensitivity: float, epsilon: float, delta: float, *, tol: float = 1e-12) -> float

Analytic Gaussian mechanism (Balle & Wang 2018): the smallest ``σ`` with

print("analytic still defined at ε=2:", round(gaussian_sigma(sens, 2.0, 1e-6), 4))
gaussian_sigma_classicalfn

gaussian_sigma_classical(sensitivity: float, epsilon: float, delta: float) -> float

Classical ``σ = Δ · sqrt(2 ln(1.25/δ)) / ε`` (Dwork & Roth 2014, Thm A.1; needs ε < 1).

print(f"ε={eps}: analytic σ = {gaussian_sigma(sens, eps, 1e-6):.4f}   classical σ = {gaussian_sigma_classical(sens, eps, 1e-6):.4f}")
heterogeneityfn

heterogeneity(y: ArrayLike, se: ArrayLike) -> Heterogeneity

Cochran's ``Q`` about the fixed-effect mean, with ``I²`` and ``H²``.

het: Heterogeneity = heterogeneity(y, se)
jaccard_distancefn

jaccard_distance(a: frozenset[str] | set[str], b: frozenset[str] | set[str]) -> float

``1 − |a ∩ b| / |a ∪ b|``; zero for two empty sets.

churn = jaccard_distance(frozenset(rel.contributors), same.contributors)
laplace_scalefn

laplace_scale(sensitivity: float, epsilon: float) -> float

Laplace mechanism scale ``b = sensitivity / epsilon``.

print("laplace scale at ε=0.5:", laplace_scale(sens, 0.5))
leave_one_outfn

leave_one_out(y: ArrayLike, se: ArrayLike, *, method: PoolMethod = 'dl', mass: float = 0.95) -> LeaveOneOut

Re-pool without each study; influence is the shift in pooled-``se`` units.

loo: LeaveOneOut = leave_one_out(y_sym, se, method=method)
moderator_matrixfn

moderator_matrix(corpus: Corpus, names: Sequence[str]) -> ModeratorDesign | Unsupported

The centered moderator design of ``corpus`` for ``names``, in record order.

design = moderator_matrix(corpus, ["follow_up"])
normalizefn

normalize(records_or_frame: Sequence[StudyRecord] | Corpus | DataFrame, *, plans: Mapping[str, TransferPlan] | None = None, name: str = 'corpus', columns: Mapping[str, str] | None = None, moderators: Sequence[str] = ()) -> Corpus | Unsupported

Admit records into a ``Corpus`` or refuse with a reason naming the study.

refused = normalize([per_kg, per_lb])
orthogonal_splitfn

orthogonal_split(epsilon: float, parts: int) -> EpsilonSplit

Equal split ``epsilon / parts`` per release; sequential composition sums to ``epsilon``.

split: EpsilonSplit = orthogonal_split(1.0, 3)
poolfn

pool(spec: PoolSpec, corpus: Corpus, *, backend: str = 'laplace', draws: int = 2000, seed: int | None = 0) -> Pooled | Unverified | Unsupported

Fit the pool and summarize it.

out = pool(spec, corpus, backend="laplace", draws=1000, seed=0)
pool_modelfn

pool_model(spec: PoolSpec, corpus: Corpus) -> tuple[ModelSpec, dict[str, Array]] | Unsupported

The model and its data for ``spec`` on ``corpus``.

built = pool_model(spec, corpus)
poolable_quantityfn

poolable_quantity(name: str) -> PoolableQuantity

The catalog entry for ``name``; ``ValueError`` naming the catalog otherwise.

entry: PoolableQuantity = poolable_quantity("elasticity")
prediction_intervalfn

prediction_interval(pooled: PooledEstimate, mass: float | None = None) -> Interval

``μ̂ ± t_{k−2} · sqrt(τ² + se²)``: where a new study's true effect is expected to lie.

pi = prediction_interval(kh)
prior_from_poolfn

prior_from_pool(result: PoolResult, *, target: PriorTarget = 'predictive', family: PriorFamilyOut = 'normal') -> tuple[Prior, LedgerLine]

Turn a ``PoolResult`` into a ``core.Prior`` plus the ledger line that records it.

prior, line = prior_from_pool(res, target=t, family="normal")
random_effectsfn

random_effects(y: ArrayLike, se: ArrayLike, *, tau_method: TauMethod = 'dl', knapp_hartung: bool = False, mass: float = 0.95) -> PooledEstimate

Random-effects pooling with ``τ²`` by DL, PM or REML; optional Knapp–Hartung interval.

kh = random_effects(y, se, tau_method="reml", knapp_hartung=True)
record_from_resultfn

record_from_result(result: EstimandResult, *, study: str, contributor: str, read: ReadKind, family: str, quantity: str | None = None, moderators: Mapping[str, float] | None = None, period: str = '', source: str = '') -> StudyRecord

A ``StudyRecord`` from a realized estimand.

rec = record_from_result(result, study="fit-1", contributor="c0", read="model", family="fertilizer")
record_from_summaryfn

record_from_summary(*, study: str, contributor: str, quantity: str, estimate: float, se: float, read: ReadKind, family: str, n: int | None = None, moderators: Mapping[str, float] | None = None, dimension: Dimension | None = None, unit_scale: str = '', estimand_hash: str = '', period: str = '', source: str = '', detail: Mapping[str, str] | None = None) -> StudyRecord

A ``StudyRecord`` from hand-entered numbers (a published table, a report).

ratio = record_from_summary(study="neg", contributor="c9", quantity="response_ratio", estimate=-0.2,
                            se=0.1, read="experiment", family="fertilizer")
releasefn

release(cell: Cell, policy: PrivacyPolicy, ledger: EpsilonLedger, *, release_id: str, epsilon: float, clip: tuple[float, float], seed: int, mechanism: Mechanism = 'laplace', delta: float = 1e-06, mass: float = 0.95) -> tuple[Release, EpsilonLedger] | Blocked

Publish the cell's clipped mean under ``policy`` and ``ledger``, or refuse.

out = release(cell, policy, ledger, release_id="r-laplace", epsilon=0.5, clip=(0.0, 1.0), seed=0, mechanism="laplace")
reml_log_likelihoodfn

reml_log_likelihood(y: ArrayLike, se: ArrayLike, tau2: float) -> float

The restricted log-likelihood (up to a constant) at ``tau2``; for checking ``tau_reml``.

ll = reml_log_likelihood(y, se, t_reml.tau2)
se_from_summaryfn

se_from_summary(summary: Summary) -> tuple[float, str]

``(se, rule)``: interval half-width over ``z`` for ``wald``/``eti``, else the sd.

print(se_from_summary(wald_summary))
tau_dersimonian_lairdfn

tau_dersimonian_laird(y: ArrayLike, se: ArrayLike) -> TauEstimate

DerSimonian–Laird closed form: ``(Q − df) / (Σw − Σw²/Σw)``, truncated at zero.

estimates = {m: f(y, se) for m, f in (("dl", tau_dersimonian_laird), ("pm", tau_paule_mandel), ("reml", tau_reml))}
tau_paule_mandelfn

tau_paule_mandel(y: ArrayLike, se: ArrayLike) -> TauEstimate

Paule–Mandel: the ``τ²`` solving ``Q(τ²) = k − 1``; zero when ``Q(0) ≤ k − 1``.

estimates = {m: f(y, se) for m, f in (("dl", tau_dersimonian_laird), ("pm", tau_paule_mandel), ("reml", tau_reml))}
tau_remlfn

tau_reml(y: ArrayLike, se: ArrayLike) -> TauEstimate

REML: the root of the restricted score ``Σw²(y−μ̂)² − Σw + Σw²/Σw`` on ``τ² ≥ 0``.

estimates = {m: f(y, se) for m, f in (("dl", tau_dersimonian_laird), ("pm", tau_paule_mandel), ("reml", tau_reml))}

axiom.diagnose

composition85 symbols · 6 notebooks

SBC, coverage, posterior predictive checks, refutations, specification curves.

Backtestspec

Backtest(spec_hash: str, spec_name: str, panel_hash: str, origins: tuple[int, ...], horizon: int, n_units: int, mass: float, definition: Literal['eti', 'hdi', 'wald'], alpha: float, scores: tuple[HorizonScore, ...], forecasts: tuple[OriginForecast, ...], failures: tuple[OriginFailure, ...] = (), ... and 4 more fields)

A rolling-origin backtest: per-horizon scores, per-origin forecasts, and the failures.

assert isinstance(bt, Backtest)
Benchmarkspec

Benchmark(covariate: str, k_d: float, k_y: float, r2_dxj_x: float, r2_yxj_dx: float, r2_dz_x: float, r2_yz_dx: float, bounds: BiasBounds)

Partial-R² bounds for a confounder ``k_d`` / ``k_y`` times an observed covariate.

assert isinstance(bm, Benchmark)
BiasBoundsspec

BiasBounds(estimate: float, se: float, df: int, r2_yz_dx: float, r2_dz_x: float, reduce: bool, bias: float, adjusted_estimate: float, adjusted_se: float, adjusted_t: float, adjusted_interval: Interval)

The omitted-variable bias a confounder of stated strength would produce.

assert isinstance(b, BiasBounds)
Correctioncallable

Correction(*args, **kwargs)

correction: Correction = "holm"
CoverageResultspec

CoverageResult(n: int, mass: float, definition: Literal['eti', 'hdi', 'wald'], alpha: float, n_fitted: int, n_failed_fits: int, failure_reasons: tuple[str, ...] = (), nominal_region: AcceptanceRegion, parameters: tuple[ParameterCoverage, ...], failed_parameters: tuple[str, ...], passed: bool, provenance: dict[str, Any] = {})

Per-parameter coverage plus the bookkeeping: ``n_fitted + n_failed_fits == n``.

assert isinstance(cov, CoverageResult)
DEFAULT_STATISTICSvalue

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

print("default statistics:", list(DEFAULT_STATISTICS))
EstimandCoveragespec

EstimandCoverage(name: str, estimand_hash: str, n: int, covered: int, mass: float, definition: Literal['eti', 'hdi', 'wald'], region: AcceptanceRegion, passed: bool, true_values: tuple[float, ...] = ())

Coverage of one estimand: ``covered`` of ``n`` realized intervals held the true value (realized at the truth through the same path). ``true_values`` is kept so a plot can show the spread of truths across replications.

assert isinstance(e, EstimandCoverage)
EstimandCoverageResultspec

EstimandCoverageResult(n: int, mass: float, definition: Literal['eti', 'hdi', 'wald'], alpha: float, n_fitted: int, n_failed_fits: int, failure_reasons: tuple[str, ...] = (), nominal_region: AcceptanceRegion, estimands: tuple[EstimandCoverage, ...], failed_estimands: tuple[str, ...], passed: bool, provenance: dict[str, Any] = {})

Per-estimand coverage; ``n_fitted + n_failed_fits == n``. A replication on which an estimand could not be realized (``Unsupported`` / ``Blocked``) counts as a failed fit for that estimand and is named in ``failure_reasons``.

assert isinstance(ec, EstimandCoverageResult)
ExpectedSigncallable

ExpectedSign(*args, **kwargs)

sign: ExpectedSign = "positive"
FitSettingsspec

FitSettings(backend: str = 'laplace', draws: int = 200, tune: int = 200, chains: int = 1)

How each specification is fitted: backend name and sampler sizes.

spec_l, settings_l = apply_option(small.spec, FitSettings(draws=40), axes[0].options[1])
Fittercallable

Fitter(*args, **kwargs)

fit_one: Fitter = fitter
FrozenPredictorclass

FrozenPredictor(spec: SurfaceSpec, posterior: Posterior, conventions: dict[str, dict[str, float]] = <factory>, provenance: dict[str, Any] = <factory>) -> None

A fit's spec, posterior, and nuisance conventions, frozen for replay on new panels.

assert isinstance(fp, FrozenPredictor)
HorizonScorespec

HorizonScore(step: int, n: int, mae: float, rmse: float, crps: float, coverage: float, coverage_region: AcceptanceRegion, passed: bool, bias: float)

Scores for one step ahead, pooled over origins and units (``n`` cells).

assert isinstance(sc, HorizonScore)
ImpliedIndependencespec

ImpliedIndependence(x: str, y: str, given: tuple[str, ...] = ())

``x`` is independent of ``y`` given ``given`` — a claim the graph makes.

assert isinstance(implication, ImpliedIndependence)
IndependenceCheckspec

IndependenceCheck(implication: ImpliedIndependence, result: IndependenceResult, adjusted_p: float, refuted: bool)

One implication, tested: the evidence against it and the verdict on it.

assert isinstance(check, IndependenceCheck)
LearningReportspec

LearningReport(parameters: tuple[ParameterLearning, ...], n_prior: int, n_posterior: int, seed: int | None, threshold: float, prior_dominated: tuple[str, ...], passed: bool)

Per-parameter learning plus the prior-dominated set.

assert isinstance(lr, LearningReport)
OriginFailurespec

OriginFailure(origin: int, reason: str)

An origin whose refit produced no posterior, with the backend's reason.

print("failures:", [OriginFailure.model_validate(f.model_dump()) for f in bt.failures], "| passed:", bt.passed)
OriginForecastspec

OriginForecast(origin: int, periods: tuple[float, ...], units: tuple[str, ...], observed: tuple[tuple[float, ...], ...], mean: tuple[tuple[float, ...], ...], lower: tuple[tuple[float, ...], ...], upper: tuple[tuple[float, ...], ...], converged: bool)

One origin's forecast: the posterior-mean path, its interval, and what was observed.

assert isinstance(f0, OriginForecast)
PPCResultspec

PPCResult(n_draws: int, seed: int | None, mass: float, alpha: float, statistics: tuple[StatisticCheck, ...], skipped: tuple[str, ...] = (), extreme_statistics: tuple[str, ...], provenance: dict[str, Any] = {})

Every statistic's check plus the run's bookkeeping. ``skipped`` names statistics whose observed value was not finite (a lag-1 autocorrelation on a one-period panel, say) — reported, never silently dropped.

assert isinstance(ppc, PPCResult)
ParameterCoveragespec

ParameterCoverage(name: str, n: int, covered: int, mass: float, definition: Literal['eti', 'hdi', 'wald'], region: AcceptanceRegion, passed: bool)

Coverage of one (scalar slice of a) parameter over ``n`` fitted replications.

assert isinstance(p, ParameterCoverage)
ParameterLearningspec

ParameterLearning(name: str, prior_mean: float, prior_sd: float, posterior_mean: float, posterior_sd: float, contraction: float, overlap: float, shift: float, prior_dominated: bool)

Prior and posterior moments of one scalar parameter and what moved.

assert isinstance(p, ParameterLearning)
ParameterRanksspec

ParameterRanks(name: str, n: int, n_ranks: int, bins: int, ranks: tuple[int, ...], histogram: tuple[int, ...], chi2_statistic: float, chi2_p_value: float, ecdf_statistic: float, ecdf_band: float, alpha: float, passed: bool)

The rank histogram of one (scalar slice of a) parameter and its two uniformity checks.

assert isinstance(r, ParameterRanks)
PriorPredictivespec

PriorPredictive(spec_hash: str, panel_hash: str, n: int, seed: int | None, n_units: int, n_periods: int, treatments: tuple[str, ...], outcome_sd: float, expected_sign: Literal['positive', 'negative', 'any'], magnitude_factor: float, tolerance: float, mass: float, ... and 7 more fields)

Prior-implied responses at the observed doses and the share that is implausible.

assert isinstance(pp, PriorPredictive)
Refutationspec

Refutation(kind: Literal['placebo_treatment', 'permutation', 'random_subset', 'added_noise'], estimand_name: str, estimand_hash: str, treatment: str, original: float, original_interval: Interval, refuted_mean: float, refuted_sd: float, refuted_interval: Interval, refuted_estimates: tuple[float, ...] = (), p_value: float, n: int, ... and 7 more fields)

The outcome of one refutation check, with the rule it passed or failed under.

assert isinstance(placebo, Refutation)
RefutationKindcallable

RefutationKind(*args, **kwargs)

kind: RefutationKind = placebo.kind
ResidualReportspec

ResidualReport(n_units: int, n_periods: int, n: int, alpha: float, residual_sd: float, units: tuple[UnitResiduals, ...], tests: tuple[ResidualTest, ...], skipped: tuple[str, ...] = (), flagged: tuple[str, ...], provenance: dict[str, Any] = {})

Per-unit summaries and the pooled tests. ``skipped`` names tests that could not run on this panel (a Ljung–Box lag beyond the series, Shapiro above its size limit), with why.

assert isinstance(rr, ResidualReport)
ResidualTestspec

ResidualTest(name: str, statistic: float, p_value: float | None = None, n: int, lag: int | None = None, note: str = '')

One test: statistic, p-value (``None`` when the test has no reference distribution, as Durbin–Watson), the ``n`` it was computed on, and the lag where one applies.

assert isinstance(t, ResidualTest)
RobustnessValuespec

RobustnessValue(estimate: float, se: float, df: int, q: float, alpha: float, t: float, r2_yd_x: float, rv: float, rv_alpha: float)

Cinelli–Hazlett robustness values for one linear estimate.

assert isinstance(rv, RobustnessValue)
SBCResultspec

SBCResult(spec: SBCSpec, model_hash: str, refit_model_hash: str, alpha_per_parameter: float, n_simulations: int, n_fitted: int, n_failed_fits: int, failure_reasons: tuple[str, ...] = (), parameters: tuple[ParameterRanks, ...], failed_parameters: tuple[str, ...], passed: bool)

Every parameter's rank check plus the bookkeeping of the run.

assert isinstance(out, SBCResult)
SBCSpecspec

SBCSpec(n_simulations: int, draws: int = 200, rank_draws: int = 19, bins: int | None = None, seed: int = 0, backend: str = 'laplace', parameters: tuple[str, ...] = (), alpha: float = 0.05)

How many replications, how big each fit, and how ranks are resolved.

spec = SBCSpec(n_simulations=24, draws=60, rank_draws=19, seed=3, alpha=0.05)
ShiftedPosteriorclass

ShiftedPosterior(posterior: Posterior, estimate: Summary, bias: Summary, shifted: Summary) -> None

Estimate draws with a bias draw subtracted from each — the bias-adjusted posterior.

assert isinstance(s, ShiftedPosterior)
Simulatorcallable

Simulator(*args, **kwargs)

simulate: Simulator = my_simulator
SpecCurvespec

SpecCurve(estimand_name: str, estimand_hash: str, base_spec_hash: str, axes: tuple[SpecificationAxis, ...], rows: tuple[SpecRow, ...], n_total: int, n_dropped: int, max_specs: int, definition: Literal['eti', 'hdi', 'wald'], mass: float, settings: FitSettings, seed: int | None = None)

Every specification's realized estimand, the cap applied, and the estimand realized.

assert isinstance(curve, SpecCurve)
SpecCurveSummaryspec

SpecCurveSummary(n: int, n_failed: int, n_converged: int, median: float | None = None, iqr_lower: float | None = None, iqr_upper: float | None = None, minimum: float | None = None, maximum: float | None = None, share_excluding_zero: float | None = None, share_positive: float | None = None)

Median / IQR of the estimates and the share of specifications excluding zero.

assert isinstance(summary, SpecCurveSummary)
SpecOptionspec

SpecOption(label: str, spec_update: dict[str, Any] = {}, fit_update: dict[str, int | str] = {})

One alternative on an axis: a label plus the spec fields and fit settings it changes.

axes = (
    SpecificationAxis(name="kernel", options=(
        SpecOption(label="hill"),
        SpecOption(label="logistic", spec_update={"kernels": {"a": LogisticKernel(reference_dose=50.0)}}),
    )),
    SpecificationAxis(name="intercept_scale", options=(
        SpecOption(label="tight", spec_update={"intercept_scale": 0.5}),
        SpecOption(label="wide", spec_update={"intercept_scale": 5.0}, fit_update={"draws": 50}),
    )),
)
SpecRowspec

SpecRow(index: int, labels: dict[str, str], spec_hash: str, spec_name: str, estimate: float | None = None, interval: Interval | None = None, sd: float | None = None, n_draws: int = 0, converged: bool = False, failure: str | None = None)

One fitted specification: its option labels, the realized point, and its fate.

assert isinstance(row, SpecRow)
SpecificationAxisspec

SpecificationAxis(name: str, options: tuple[SpecOption, ...])

One analyst choice: a name and at least one labelled option.

axes = (
    SpecificationAxis(name="kernel", options=(
        SpecOption(label="hill"),
        SpecOption(label="logistic", spec_update={"kernels": {"a": LogisticKernel(reference_dose=50.0)}}),
    )),
    SpecificationAxis(name="intercept_scale", options=(
        SpecOption(label="tight", spec_update={"intercept_scale": 0.5}),
        SpecOption(label="wide", spec_update={"intercept_scale": 5.0}, fit_update={"draws": 50}),
    )),
)
Statisticcallable

Statistic(*args, **kwargs)

custom: dict[str, Statistic] = {"spread": spread, "nan": lambda y: float("nan")}
StatisticCheckspec

StatisticCheck(name: str, observed: float, interval: Interval, p_value: float, p_two_sided: float, n: int, alpha: float, extreme: bool)

One statistic: observed value, predictive interval, and the tail p-values over ``n`` replicates. ``p_value`` is ``P(T_rep >= T_obs)``; ``p_two_sided`` is ``min(1, 2 · min(P(T_rep >= T_obs), P(T_rep <= T_obs)))``.

assert isinstance(s, StatisticCheck)
StructureRefutationspec

StructureRefutation(graph_hash: str = '', graph_name: str = '', checks: tuple[IndependenceCheck, ...] = (), alpha: float, effect_threshold: float, correction: Literal['none', 'holm', 'benjamini_hochberg'] = 'holm', n_tested: int, n_refuted: int, implicated: tuple[tuple[str, str, float], ...] = (), untested: tuple[tuple[str, str], ...] = (), verdict: Verdict, detail: dict[str, str] = {})

What the data says about the graph's own claims.

assert isinstance(report, StructureRefutation)
SupportsWorldclass

SupportsWorld(*args, **kwargs)

What a replication needs: the spec to fit, the panel to fit it on, the truth to score against. ``axiom.sim.SurfaceWorld`` satisfies it; so does ``WorldView``.

assert isinstance(view, WorldView) and isinstance(view, SupportsWorld)
TippingPointspec

TippingPoint(threshold: float, certainty: float, n_draws: int, bias_grid: tuple[float, ...], probabilities: tuple[float, ...], decision_at_zero: bool, probability_at_zero: float, flipped: bool, bias: float | None, probability: float | None, interval: Interval | None)

The smallest hypothesised bias at which a threshold decision flips.

assert isinstance(tp, TippingPoint)
UnitResidualsspec

UnitResiduals(unit: str, n: int, mean: float, sd: float)

Residual mean and sd for one unit over its periods.

assert isinstance(u, UnitResiduals)
WeakIdReportspec

WeakIdReport(parameters: tuple[str, ...], n_draws: int, correlation: tuple[tuple[float, ...], ...], condition_number: float, rho_threshold: float, condition_threshold: float, ratio_threshold: float, high_pairs: tuple[tuple[str, str, float], ...], prior_sd: dict[str, float], posterior_sd: dict[str, float], sd_ratio: dict[str, float], unlearned: tuple[str, ...], ... and 5 more fields)

Posterior geometry of the structural parameters and what it flags.

assert isinstance(rep, WeakIdReport)
WorldFactorycallable

WorldFactory(*args, **kwargs)

make_world: WorldFactory = factory
WorldViewclass

WorldView(spec: SurfaceSpec, panel: Panel, theta: dict[str, Array]) -> None

A (spec, panel, theta) triple — a world seen through a possibly different spec.

assert isinstance(view, WorldView) and isinstance(view, SupportsWorld)
added_noisefn

added_noise(result: FitResult, sd_fraction: float = 0.5, n: int = 10, seed: int = 0, *, estimand: Estimand | None = None, treatment: str | None = None, alpha: float = 0.1, backend: str = 'laplace', draws: int = 200, chains: int = 1, definition: IntervalDefinition = 'eti', mass: float = 0.9, panel: Panel | None = None) -> Refutation | Unsupported

Refit ``n`` times with Gaussian noise of sd ``sd_fraction · σ̂`` added to the outcome.

noisy = added_noise(res, sd_fraction=0.5, n=3, seed=0, alpha=0.1, draws=DRAWS, panel=world.panel)
apply_optionfn

apply_option(base: SurfaceSpec, settings: FitSettings, option: SpecOption) -> tuple[SurfaceSpec, FitSettings]

``base`` and ``settings`` with one option applied (a fresh, validated ``SurfaceSpec``).

spec_l, settings_l = apply_option(small.spec, FitSettings(draws=40), axes[0].options[1])
benchmarkfn

benchmark(estimate: float, se: float, df: int, *, covariate: str, r2_dxj_x: float, r2_yxj_dx: float, k_d: float = 1.0, k_y: float = 1.0, reduce: bool = True, mass: float = 0.95) -> Benchmark | Unsupported

Bound a confounder by an observed covariate (Cinelli & Hazlett 2020, §4.4; sensemakr).

bm = benchmark(**DARFUR, covariate="female", r2_dxj_x=0.00916, r2_yxj_dx=0.11, k_d=1.0, k_y=1.0)
bhattacharyyafn

bhattacharyya(mean_a: float, sd_a: float, mean_b: float, sd_b: float) -> float

Bhattacharyya coefficient of two normals (1 = identical, 0 = disjoint).

print("same:", bhattacharyya(0, 1, 0, 1), "| far:", round(bhattacharyya(0, 1, 10, 1), 8), "| wider:", round(bhattacharyya(0, 1, 0, 2), 4))
bias_boundsfn

bias_bounds(estimate: float, se: float, df: int, *, r2_yz_dx: float, r2_dz_x: float, reduce: bool = True, mass: float = 0.95) -> BiasBounds

Price a confounder with partial R² ``(r2_yz_dx, r2_dz_x)`` (Cinelli & Hazlett 2020, §4.1).

b = bias_bounds(**DARFUR, r2_yz_dx=0.1246, r2_dz_x=0.0092, mass=0.95)
coveragefn

coverage(world_factory: WorldFactory, *, n: int, mass: float = 0.9, definition: IntervalDefinition = 'eti', parameters: Sequence[str] | None = None, fit: Fitter | None = None, backend: str = 'laplace', draws: int = 200, seed: int = 0, alpha: float = 0.01) -> CoverageResult

Fixed-truth coverage of the ``mass`` ``definition`` interval over ``n`` replications.

cov = coverage(make_world, n=10, mass=0.9, definition="eti", fit=fit_one, seed=5, alpha=0.01)
crpsfn

crps(draws: ArrayLike, observed: ArrayLike) -> Array

Empirical CRPS per cell: ``E|X − y| − ½ E|X − X'|`` from draws along the first axis.

print("crps of an ensemble:", np.round(crps(ensemble, np.zeros(4)), 3), "| point forecast = |error|:", crps(np.full((1, 2), 2.0), np.full(2, 0.5)))
default_binsfn

default_bins(n: int, n_ranks: int, *, min_expected: int = 10) -> int

The largest divisor of ``n_ranks + 1`` that expects at least ``min_expected`` ranks per bin from ``n`` ranks; ``2`` when none does (a two-bin histogram is always defined).

print("default bins for N=60, L=19:", default_bins(60, 19), "| N=200:", default_bins(200, 19))
default_estimandfn

default_estimand(spec: SurfaceSpec, panel: Panel, treatment: str | None = None, *, dose: float | None = None) -> Estimand

The contrast ``E[Y(dose)] − E[Y(0)]`` of one treatment: the per-unit average (``Level(unit="individual")``, uniform unit weights), cumulative over the whole horizon.

est = default_estimand(small.spec, small.panel)
draw_priorfn

draw_prior(model: ModelSpec, rng: Generator, *, n: int = 1) -> dict[str, Array]

``n`` joint draws from the model's prior, constrained, shaped ``(n, *param_shape)``.

theta = draw_prior(model, rng, n=3)
estimand_coveragefn

estimand_coverage(world_factory: WorldFactory, *, estimands: Sequence[Estimand], n: int, mass: float = 0.9, definition: IntervalDefinition = 'eti', fit: Fitter | None = None, backend: str = 'laplace', draws: int = 200, seed: int = 0, alpha: float = 0.01, truth_world: Callable[[SupportsWorld], SupportsWorld] | None = None) -> EstimandCoverageResult

Coverage of realized estimands over ``n`` replications.

ec = estimand_coverage(hill_world, estimands=[contrast], n=6, draws=60, seed=1)
forecastfn

forecast(spec: SurfaceSpec, posterior: Posterior, panel: Panel, *, origin: int, horizon: int, conventions: Conventions | None = None, seed: int | None = None, noise: bool = False) -> PredictiveDraws

Predictive draws for periods ``[origin, origin + horizon)`` with the carryover state carried from the periods before ``origin``.

fc = forecast(carry.spec, truth, carry.panel, origin=8, horizon=3)
freezefn

freeze(result: FitResult, *, panel: Panel | None = None) -> FrozenPredictor | Unsupported

Freeze a ``FitResult``'s spec, posterior, and conventions; ``Unsupported`` without a posterior.

implied_independenciesfn

implied_independencies(graph: CausalGraph, *, max_conditioning: int = 4) -> tuple[tuple[ImpliedIndependence | None, tuple[str, str]], ...]

One testable claim per non-adjacent pair, with a set the graph says separates them.

claims = implied_independencies(proposed)
learningfn

learning(model: ModelSpec, posterior: Posterior | FitResult, *, parameters: Sequence[str] | None = None, n_prior: int = 4000, threshold: float = 0.1, seed: int | None = 0) -> LearningReport | Unsupported

Contraction, overlap and shift of every (or the named) free parameter.

lr = learning(toy, learned, n_prior=4000, seed=1, threshold=0.1)
misspecifyfn

misspecify(world: SupportsWorld, spec: SurfaceSpec) -> WorldView

``world``'s panel and truth, to be fitted with ``spec`` instead of the generating spec.

view = misspecify(carry, naive_spec)
partial_r2fn

partial_r2(t_statistic: float, df: int) -> float

Partial R² of a regressor from its t statistic: ``t² / (t² + df)``.

print("partial R2 of t=4.19 on df=783:", round(partial_r2(0.0973 / 0.0232, 783), 4))
permutationfn

permutation(result: FitResult, n: int = 20, seed: int = 0, *, estimand: Estimand | None = None, treatment: str | None = None, alpha: float = 0.05, backend: str = 'laplace', draws: int = 200, chains: int = 1, definition: IntervalDefinition = 'eti', mass: float = 0.9, panel: Panel | None = None) -> Refutation | Unsupported

Null distribution of the estimand from ``n`` refits with the doses shuffled across cells.

perm = permutation(res, n=5, seed=0, alpha=0.2, draws=DRAWS, panel=world.panel)
placebo_treatmentfn

placebo_treatment(result: FitResult, *, estimand: Estimand | None = None, treatment: str | None = None, seed: int = 0, n: int = 1, backend: str = 'laplace', draws: int = 200, chains: int = 1, definition: IntervalDefinition = 'eti', mass: float = 0.9, panel: Panel | None = None) -> Refutation | Unsupported

Reassign the treatment's dose series across units, refit, and expect no effect.

placebo = placebo_treatment(res, seed=0, draws=DRAWS, panel=world.panel)
posterior_predictivefn

posterior_predictive(result: FitResult, *, statistics: Mapping[str, Statistic] | None = None, n_draws: int = 200, seed: int | None = 0, mass: float = 0.9, alpha: float = 0.05) -> PPCResult | Unsupported

Posterior predictive checks of ``result`` on its own fitted panel.

ppc = posterior_predictive(res, n_draws=80, seed=1)
prior_momentsfn

prior_moments(prior: Prior, resolved: Mapping[str, float] | None = None) -> tuple[float, float]

``(mean, sd)`` of a prior family; a hyperparameter naming a parameter is looked up in ``resolved``. ``KeyError`` when it is not there; ``(value, 0)`` for ``fixed``.

print("normal(1, 3):", prior_moments(Prior(family="normal", hyper={"mu": 1, "sigma": 3})))
prior_predictivefn

prior_predictive(spec: SurfaceSpec, panel: Panel, *, n: int = 200, seed: int | None = 0, expected_sign: ExpectedSign = 'positive', magnitude_factor: float = 10.0, tolerance: float = 0.2, mass: float = 0.9) -> PriorPredictive | Unsupported

Push ``n`` prior draws through ``Surface.forward`` at the panel's doses and summarize.

pp = prior_predictive(world.spec, world.panel, n=60, seed=4, expected_sign=sign, magnitude_factor=10.0)
random_subsetfn

random_subset(result: FitResult, fraction: float = 0.7, n: int = 10, seed: int = 0, *, estimand: Estimand | None = None, treatment: str | None = None, alpha: float = 0.1, backend: str = 'laplace', draws: int = 200, chains: int = 1, definition: IntervalDefinition = 'eti', mass: float = 0.9, panel: Panel | None = None) -> Refutation | Unsupported

Refit on ``n`` random subsets holding ``fraction`` of the units; expect stability.

subset = random_subset(res, fraction=0.6, n=3, seed=0, alpha=0.1, draws=DRAWS, panel=world.panel)
rank_uniformityfn

rank_uniformity(name: str, ranks: ArrayLike, *, n_ranks: int, alpha: float, bins: int | None = None) -> ParameterRanks

Chi-square and ECDF-difference checks of ``ranks`` on ``{0, ..., n_ranks}`` (module docstring). ``ValueError`` for an empty rank vector, a rank off the support, or ``bins`` that do not divide ``n_ranks + 1``.

uniform = rank_uniformity("u", rng.integers(0, 20, size=200), n_ranks=19, alpha=0.05)
realized_pointfn

realized_point(estimand: Estimand, result: FitResult, *, definition: IntervalDefinition, mass: float, seed: int | None = None) -> RealizedDraws | Unsupported

``realize(..., keep_draws=True)`` with every non-result outcome as ``Unsupported``.

pt = realized_point(est, base_fit, definition="eti", mass=0.9)
refute_structurefn

refute_structure(graph: CausalGraph, data: Dataset, *, alpha: float = 0.05, effect_threshold: float = 0.0, correction: Correction = 'holm', max_conditioning: int = 4) -> StructureRefutation | Unsupported

Test every independence the graph implies, and report the edges any failure implicates.

report = refute_structure(proposed, trial, alpha=0.05, correction=correction)
residualsfn

residuals(result: FitResult, *, lags: tuple[int, ...] = (1, 5, 10), alpha: float = 0.05) -> ResidualReport | Unsupported

The residual report of ``result`` on its fitted panel (module docstring).

rr = residuals(res, lags=(1, 4, 20), alpha=0.05)
robustness_valuefn

robustness_value(estimate: float, se: float, df: int, *, q: float = 1.0, alpha: float = 0.05) -> RobustnessValue

``RV_q`` and ``RV_{q,α}`` for a linear estimate (Cinelli & Hazlett 2020, §4.2).

rv = robustness_value(**DARFUR, q=1.0, alpha=0.05)
rolling_originfn

rolling_origin(spec: SurfaceSpec, panel: Panel, *, origins: Sequence[int], horizon: int, backend: str = 'laplace', draws: int = 200, tune: int = 200, chains: int = 1, seed: int | None = 0, mass: float = 0.9, definition: IntervalDefinition = 'eti', alpha: float = 0.01) -> Backtest | Unsupported

Refit at each origin, forecast ``horizon`` periods through the one forward, and score.

bt = rolling_origin(carry.spec, carry.panel, origins=(8, 10), horizon=2, draws=DRAWS, chains=1, seed=0)
sbcfn

sbc(model: ModelSpec, data_template: DataDict, *, spec: SBCSpec, simulate: Simulator | None = None, refit: ModelSpec | None = None) -> SBCResult | Unsupported

Run SBC on ``model`` over ``data_template`` (the covariate columns; the outcome column is overwritten every replication).

out = sbc(model, data, spec=spec, simulate=simulate)
sbc_poolfn

sbc_pool(pool_spec: PoolSpec, corpus: Corpus, *, sbc_spec: SBCSpec, refit: PoolSpec | None = None) -> SBCResult | Unsupported

SBC for the meta-analytic pool: ``meta.pool_model`` gives the model and its data (the records' standard errors, loadings, moderators); the outcome column is re-simulated from the pool's likelihood at each prior draw. ``refit`` pools with a different spec (the negative control). ``Unsupported`` when the corpus cannot be laid out.

pooled = sbc_pool(PoolSpec(family="f", priors=PoolPriors(tau_fixed=0.3)), corpus.build(), sbc_spec=SBCSpec(n_simulations=6, draws=40))
sbc_surfacefn

sbc_surface(spec: SurfaceSpec, panel_template: Panel, *, sbc_spec: SBCSpec, refit: SurfaceSpec | None = None) -> SBCResult | Unsupported

SBC for a response surface: the outcome is simulated through ``Surface.forward`` at the prior draw plus the spec's likelihood noise, then refitted with ``refit`` (default: the same spec). ``panel_template`` supplies the doses and layout; its outcome is ignored.

surf = sbc_surface(world.spec, world.panel, sbc_spec=SBCSpec(n_simulations=6, draws=40))
shift_posteriorfn

shift_posterior(draws: ArrayLike | Posterior, *, name: str | None = None, bias_draws: ArrayLike | None = None, bias_mean: float = 0.0, bias_sd: float = 0.0, definition: IntervalDefinition = 'eti', mass: float = 0.9, seed: int | None = None) -> ShiftedPosterior

Subtract a bias from every estimate draw without refitting.

s = shift_posterior(draws, bias_mean=0.5, bias_sd=0.5, mass=0.9, seed=3)
simulate_outcomefn

simulate_outcome(model: ModelSpec, data: DataDict, theta: Mapping[str, Array], rng: Generator) -> Array

One outcome array from ``model``'s likelihood at ``mean(theta)`` on ``data``.

y = simulate_outcome(model, data, one, rng)
specification_curvefn

specification_curve(base: SurfaceSpec, panel: Panel, axes: Sequence[SpecificationAxis], *, estimand: Estimand | None = None, backend: str = 'laplace', draws: int = 200, tune: int = 200, chains: int = 1, seed: int | None = 0, max_specs: int = 64, definition: IntervalDefinition = 'eti', mass: float = 0.9) -> SpecCurve

Fit every combination of the axes' options and realize ``estimand`` under each.

curve = specification_curve(small.spec, small.panel, axes, draws=40, chains=1, seed=3)
tipping_pointfn

tipping_point(estimate_draws: ArrayLike | Posterior, decision_threshold: float, bias_grid: ArrayLike, *, name: str | None = None, certainty: float = 0.5, definition: IntervalDefinition = 'eti', mass: float = 0.9) -> TippingPoint

Walk ``bias_grid`` (sorted by absolute size) and find where the decision flips.

tp = tipping_point(rng.normal(1.0, 0.1, size=4000), 0.5, np.linspace(-1.0, 1.0, 41), certainty=0.5)
training_panelfn

training_panel(panel: Panel, n_periods: int) -> Panel

The first ``n_periods`` periods of ``panel`` (every unit, roles unchanged).

train = training_panel(carry.panel, 8)
truth_producerfn

truth_producer(world: SupportsWorld) -> FitResult

A ``FitResult`` whose posterior is a two-draw point mass at the world's truth, so an estimand realized on it is the *true* estimand computed through ``estimands.realize``. The generating spec is used, not a mis-specified view's.

producer = truth_producer(w)
weak_identificationfn

weak_identification(result: FitResult | Posterior, model: ModelSpec | None = None, *, parameters: Sequence[str] | None = None, rho_threshold: float = 0.9, condition_threshold: float = 1000.0, ratio_threshold: float = 0.9, saturation_share: float = 0.1, saturation_eps: float = 0.001) -> WeakIdReport | Unsupported

Correlation ridge, condition number, prior-vs-posterior sd ratio, bound saturation.

rep = weak_identification(post, toy, rho_threshold=0.9)

axiom.build

composition13 symbols · 1 notebooks

Fluent builders for graphs, priors, and corpora.

BuildErrorerror

A builder was asked to build before it had what it needs, or was given a bad value.

try:
    f.require("z", builder="demo")
except BuildError as e:
    print("BuildError:", e)
Builderclass

Builder(*args, **kwargs)

Anything that can assemble a ``Spec`` from what it has collected.

print("builders satisfy the protocol:", isinstance(SurfaceBuilder(), Builder), isinstance(PriorBuilder(), Builder))
EntitySpecvalue

Represent a PEP 604 union type

t: EntitySpec = VariableBuilder().kind(kind).name("x").dimension(D.currency).measured_in("USD").build()
Fieldsclass

Fields(values: Mapping[str, Any] = <factory>) -> None

An immutable ``name → value`` mapping with a copy-on-write ``with_``.

f = Fields({"a": 1})
GraphBuilderclass

GraphBuilder(fields: Fields = Fields(values=mappingproxy({}))) -> None

``GraphBuilder().edge("Z", "X").edge("X", "Y").unmeasured("U").build()``.

estimand = GraphBuilder().edge("x", "y").estimand(Treatment(name="x", dimension=D.currency, unit="USD"), Outcome(name="y", dimension=D.outcome), dose=10.0, window=TimeWindow(start=0, stop=4))
MetaBuilderclass

MetaBuilder(fields: Fields = Fields(values=mappingproxy({}))) -> None

Collect study records and pooling choices.

print("from a frame:", [r.study for r in MetaBuilder().from_frame(corpus.to_frame()).build().records])
MomentFamilycallable

MomentFamily(*args, **kwargs)

family: MomentFamily = "lognormal"
PriorBuilderclass

PriorBuilder(fields: Fields = Fields(values=mappingproxy({}))) -> None

Collect a family and hyperparameters; ``build()`` is a ``core.Prior``.

print("builders satisfy the protocol:", isinstance(SurfaceBuilder(), Builder), isinstance(PriorBuilder(), Builder))
SchedulePatterncallable

SchedulePattern(*args, **kwargs)

pattern: SchedulePattern = "pulse"
StudyBuilderclass

StudyBuilder(fields: Fields = Fields(values=mappingproxy({}))) -> None

Collect a study's plan; build the design spec, candidate, schedule, or measurement.

StudyBuilder().size(n_periods=4).schedule("alternating").build_schedule()
SurfaceBuilderclass

SurfaceBuilder(fields: Fields = Fields(values=mappingproxy({}))) -> None

Fluent construction of a ``SurfaceSpec``; every method returns a new builder.

print("builders satisfy the protocol:", isinstance(SurfaceBuilder(), Builder), isinstance(PriorBuilder(), Builder))
VariableBuilderclass

VariableBuilder(fields: Fields = Fields(values=mappingproxy({}))) -> None

``VariableBuilder().treatment("x").dimension(D.currency).unit("USD").build()``.

t: EntitySpec = VariableBuilder().kind(kind).name("x").dimension(D.currency).measured_in("USD").build()
VariableKindcallable

VariableKind(*args, **kwargs)

kind: VariableKind = "treatment"

axiom.adapters

composition17 symbols · 2 notebooks

Domain vocabulary — marketing is one adapter, not the core.

Channelspec

Channel(name: str, dimension: Dimension | None = None, unit: str | None = None, description: str = '')

The thing you can intervene on. Its dimension is the dose's dimension.

print("Channel is Treatment:", Channel is Treatment, "| Geo is Unit:", Geo is Unit, "| KPI is Outcome:", KPI is Outcome)
EXPOSUREvalue

A product of declared base dimensions raised to rational exponents.

print(impressions("tv_impressions"), "| EXPOSURE == D.exposure_count:", EXPOSURE == D.exposure_count)
Geospec

Geo(name: str, dimension: Dimension | None = None, unit: str | None = None, description: str = '', kind: Literal['individual', 'cluster', 'aggregate'] = 'individual')

The randomization / observation unit: a plot, a patient, a region.

print("Channel is Treatment:", Channel is Treatment, "| Geo is Unit:", Geo is Unit, "| KPI is Outcome:", KPI is Outcome)
KPIspec

KPI(name: str, dimension: Dimension | None = None, unit: str | None = None, description: str = '', aggregation: Literal['sum', 'mean', 'rate'] = 'sum')

What you are trying to move.

print("Channel is Treatment:", Channel is Treatment, "| Geo is Unit:", Geo is Unit, "| KPI is Outcome:", KPI is Outcome)
MarketingRolesspec

MarketingRoles(kpi: str, channels: tuple[str, ...], geo: str = 'geo', date: str = 'date', impressions: tuple[str, ...] = (), controls: tuple[str, ...] = (), currency: str = 'USD', kpi_dimension: Literal['currency', 'outcome'] = 'outcome', kpi_unit: str | None = None)

Which columns of a marketing frame play which role.

roles = MarketingRoles(kpi="revenue", channels=("tv", "search"), geo="geo", date="week", impressions=("tv_impressions",), controls=("holiday",), kpi_dimension="currency")
agronomyvalue

The agronomy adapter: plot / rate / yield / optimum over the general core.

print("agronomy's public surface:", len(agronomy.__all__), "names")
contributionfn

contribution(result: FitResult, channel: str, window: TimeWindow | tuple[int, int] | None = None, *, mass: float = 0.9, definition: IntervalDefinition = 'hdi', seed: int | None = None) -> EstimandResult | Unsupported | Blocked

The channel's contribution to the KPI: ``KPI at observed spend − KPI at zero spend``, summed over geos and the window (the ``contrast`` estimand, KPI units).

for fn in (roas, roi, contribution, marginal_roas):
    out = fn(res, "tv", mass=0.9, seed=0)
    assert isinstance(out, EstimandResult)
    print(f"{out.estimand_name:16s} {out.kind:9s} {out.summary.mean:9.3f}  {out.summary.interval}  unit={out.unit}")
impressionsfn

impressions(name: str) -> Covariate

An impressions / exposures covariate on the ``exposure_count`` base.

print(impressions("tv_impressions"), "| EXPOSURE == D.exposure_count:", EXPOSURE == D.exposure_count)
marginal_roasfn

marginal_roas(result: FitResult, channel: str, window: TimeWindow | tuple[int, int] | None = None, *, mass: float = 0.9, definition: IntervalDefinition = 'hdi', seed: int | None = None) -> EstimandResult | Unsupported | Blocked

The derivative of the window's KPI with respect to a common shift of the channel's observed spend (the ``marginal`` estimand). Needs a currency-valued KPI.

windowed = marginal_roas(res, "tv", window=(2, 8))
marketingvalue

The marketing adapter: channel / spend / geo / KPI over the general core.

print(" ", marketing.roas.__doc__.strip().splitlines()[0])
marketing_specfn

marketing_spec(panel: Panel, channels: Sequence[str] | None = None, *, name: str = 'marketing', kernel: str = 'hill', carryover: str | None = 'geometric', max_lag: int = 4, intercept: InterceptKind = 'hierarchical', seasonality: tuple[float, int] | None = None, trend: bool = False, amplitude_scale: float = 1.0) -> SurfaceSpec

A ``SurfaceSpec`` for a marketing panel through ``build.SurfaceBuilder``.

spec = marketing_spec(panel, seasonality=(4.0, 1), trend=True)
panel_from_marketing_framefn

panel_from_marketing_frame(df: DataFrame, roles: MarketingRoles) -> Panel

A ``Panel`` from a wide geo × date frame; columns with no role are dropped.

panel = panel_from_marketing_frame(wide, roles)
panel_from_mfffn

panel_from_mff(df: DataFrame, roles: MarketingRoles, *, variable: str = 'variable', value: str = 'value') -> Panel

The MFF long format (one row per ``geo × date × variable``) → ``Panel``.

print("MFF long format gives the same panel:", panel_from_mff(long, roles).content_hash() == panel.content_hash())
roasfn

roas(result: FitResult, channel: str, window: TimeWindow | tuple[int, int] | None = None, *, mass: float = 0.9, definition: IntervalDefinition = 'hdi', seed: int | None = None) -> EstimandResult | Unsupported | Blocked

Return on ad spend: ``(KPI at observed spend − KPI at zero) / observed spend``.

bad = roas(count_fit, "tv")
roifn

roi(result: FitResult, channel: str, window: TimeWindow | tuple[int, int] | None = None, *, mass: float = 0.9, definition: IntervalDefinition = 'hdi', seed: int | None = None) -> EstimandResult | Unsupported | Blocked

Return on investment ``ROAS − 1``: the same draws shifted, re-summarized.

for fn in (roas, roi, contribution, marginal_roas):
    out = fn(res, "tv", mass=0.9, seed=0)
    assert isinstance(out, EstimandResult)
    print(f"{out.estimand_name:16s} {out.kind:9s} {out.summary.mean:9.3f}  {out.summary.interval}  unit={out.unit}")
role_mapfn

role_map(roles: MarketingRoles) -> RoleMap

The general ``RoleMap`` the marketing roles translate to.

rm = role_map(roles)
spendfn

spend(channel: str, *, currency: str = 'USD') -> Dose

The spend dose of ``channel``: currency-dimensioned, costed in its own currency.

print(spend("tv"))

axiom.viz

composition19 symbols · 1 notebooks

Plot helpers for the diagnostics that have a canonical picture.

availablefn

available() -> bool

True when plotly can be imported.

print("plotly available:", available())
backtest_plotfn

backtest_plot(backtest: Any) -> Any | Unsupported

Per-horizon scores of a rolling-origin backtest — MAE, RMSE, CRPS — and the interval coverage against the nominal mass.

fig = backtest_plot(bt)
boundaryfn

boundary(rule: Any, *, height: float = 360.0) -> Any | Unsupported

The stopping threshold at each look, which is the protocol sentence. (``design``)

boundary(rule)
causal_graphfn

causal_graph(graph: Any, *, height: float = 420.0) -> Any | Unsupported

The graph itself: nodes, directed edges, and the bidirected ones.

figure = causal_graph(graph)
convergencefn

convergence(report: Any, *, height: float = 380.0) -> Any | Unsupported

R-hat per parameter against the threshold that decides the run. (``infer``)

convergence(report)
correctionsfn

corrections(applied: Sequence[Any], *, height: float = 360.0) -> Any | Unsupported

What each correction did to the number, in order. (``calibrate``)

corrections(applied)
coverage_plotfn

coverage_plot(result: Any) -> Any | Unsupported

Per-parameter coverage against the nominal mass with each parameter's exact acceptance region (``region.lower``/``region.upper``).

print("coverage_plot traces:", len(coverage_plot(cov).data))
forestfn

forest(data: Any) -> Any | Unsupported

A forest plot: one row per study with its interval, the pooled row, and the prediction interval when the data carries one.

print("forest traces:", len(forest(forest_data(y, se, pooled, labels=list("abcde"))).data))
funnelfn

funnel(data: Any) -> Any | Unsupported

A funnel plot: study estimates against their standard errors (precision upward) with the pseudo-confidence contours around the pooled estimate.

print("funnel traces:", len(funnel(funnel_data(y, se, pooled)).data))
intervalsfn

intervals(named: Any, *, height: float = 360.0, unit: str = '') -> Any | Unsupported

Several named intervals on one axis. (``core``)

intervals(
    {
        "40 mg": Interval(lower=-16.7, upper=-8.1, definition="eti", mass=0.9),
        "20 mg": Interval(lower=-6.2, upper=-3.4, definition="eti", mass=0.9),
        "10 mg": Interval(lower=-2.1, upper=1.4, definition="eti", mass=0.9),
    },
    unit="mmHg",
)
marginal_curvefn

marginal_curve(result: Any, treatment: str = '', *, n_grid: int = 25, mass: float = 0.9, definition: IntervalDefinition = 'eti', window: TimeWindow | None = None, seed: int | None = None) -> Any | Unsupported

``d outcome / d dose`` against dose, with its posterior band.

slope = marginal_curve(res, "a", n_grid=8, mass=0.8)
panel_coveragefn

panel_coverage(panel: Any, *, height: float = 400.0, max_units: int = 60) -> Any | Unsupported

Which unit was observed in which period. (``data``)

panel_coverage(panel)
recoveryfn

recovery(truth: Any, estimated: Any, *, height: float = 400.0) -> Any | Unsupported

Estimated against true, for a world where the truth is known. (``sim``)

recovery(truth, estimated)
response_curvefn

response_curve(result: Any, treatment: str = '', *, n_grid: int = 25, mass: float = 0.9, definition: IntervalDefinition = 'eti', window: TimeWindow | None = None, seed: int | None = None) -> Any | Unsupported

Expected outcome against the dose of ``treatment``, **with its posterior band**.

fig = response_curve(res, "a", n_grid=8, mass=0.8)
sbc_ranksfn

sbc_ranks(result: Any, *, columns: int = 3) -> Any | Unsupported

Rank histograms per parameter from an SBC run, with the expected count per bin and the binomial band around it; failed parameters are titled as such.

print("sbc_ranks traces:", len(sbc_ranks(sbc_like, columns=2).data))
spec_curve_plotfn

spec_curve_plot(curve: Any) -> Any | Unsupported

The specification curve: estimates sorted with their intervals, coloured by whether the interval excludes zero; failed specifications are omitted and counted in the title.

print("spec_curve_plot traces:", len(spec_curve_plot(curve).data))
stabilityfn

stability(report: Any, *, height: float = 380.0) -> Any | Unsupported

How often each edge came back, split by which way it pointed.

stability(report)
transferfn

transfer(plan: Any, *, height: float = 320.0) -> Any | Unsupported

Which facets differ between source and target, and what that costs. (``estimands``)

transfer(plan)
unrolledfn

unrolled(system: Any, *, height: float = 420.0) -> Any | Unsupported

A dynamic system after unrolling, laid out so depth is time. (``dynamics``)

unrolled(unroll(system, periods=4))

axiom.report

composition34 symbols · 1 notebooks

Templated HTML / PPTX / PDF reports over the viz layer.

AnyBlockcallable

AnyBlock(*args, **kwargs)

Runtime representation of an annotated type.

blocks: tuple[AnyBlock, ...] = (
    Heading(text="Per-arm detail", level=2),
    Paragraph(text="One page per arm, generated in a loop.", emphasis=True),
    Metric(source="contrast", label="Effect", unit="units"),
    Table(source="arms"),
    Divider(),
    PageBreak(),
    LedgerBlock(source="ledger"),
)
Dividerspec

Divider(block: Literal['divider'] = 'divider')

A horizontal rule.

blocks: tuple[AnyBlock, ...] = (
    Heading(text="Per-arm detail", level=2),
    Paragraph(text="One page per arm, generated in a loop.", emphasis=True),
    Metric(source="contrast", label="Effect", unit="units"),
    Table(source="arms"),
    Divider(),
    PageBreak(),
    LedgerBlock(source="ledger"),
)
FORMATSvalue

Built-in immutable sequence.

print("formats:", FORMATS, "| page sizes:", sorted(GEOMETRY))
Figurespec

Figure(block: Literal['figure'] = 'figure', source: str, caption: str = '', height: float | None = None, full_page: bool = False)

A figure, named by the context key that holds it.

broken = Report(name="b", title="b",
                sections=(Section(title="s", blocks=(Figure(source="sigma"),)),))
Formatcallable

Format(*args, **kwargs)

chosen: Format = fmt
GEOMETRYvalue

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

print("formats:", FORMATS, "| page sizes:", sorted(GEOMETRY))
Headingspec

Heading(block: Literal['heading'] = 'heading', text: str, level: int = 2)

A section heading. ``level`` 1-3, matching the theme's three heading sizes.

blocks: tuple[AnyBlock, ...] = (
    Heading(text="Per-arm detail", level=2),
    Paragraph(text="One page per arm, generated in a loop.", emphasis=True),
    Metric(source="contrast", label="Effect", unit="units"),
    Table(source="arms"),
    Divider(),
    PageBreak(),
    LedgerBlock(source="ledger"),
)
LedgerBlockspec

LedgerBlock(block: Literal['ledger'] = 'ledger', source: str, caption: str = '', show_detail: bool = False)

The assumption trail: a sequence of ``core.LedgerLine``.

blocks: tuple[AnyBlock, ...] = (
    Heading(text="Per-arm detail", level=2),
    Paragraph(text="One page per arm, generated in a loop.", emphasis=True),
    Metric(source="contrast", label="Effect", unit="units"),
    Table(source="arms"),
    Divider(),
    PageBreak(),
    LedgerBlock(source="ledger"),
)
Metricspec

Metric(block: Literal['metric'] = 'metric', source: str, label: str, unit: str = '', precision: int | None = None)

One number, with its interval when it has one.

blocks: tuple[AnyBlock, ...] = (
    Heading(text="Per-arm detail", level=2),
    Paragraph(text="One page per arm, generated in a loop.", emphasis=True),
    Metric(source="contrast", label="Effect", unit="units"),
    Table(source="arms"),
    Divider(),
    PageBreak(),
    LedgerBlock(source="ledger"),
)
PageBreakspec

PageBreak(block: Literal['page_break'] = 'page_break')

Start a new page or slide here.

blocks: tuple[AnyBlock, ...] = (
    Heading(text="Per-arm detail", level=2),
    Paragraph(text="One page per arm, generated in a loop.", emphasis=True),
    Metric(source="contrast", label="Effect", unit="units"),
    Table(source="arms"),
    Divider(),
    PageBreak(),
    LedgerBlock(source="ledger"),
)
PageSizecallable

PageSize(*args, **kwargs)

size: PageSize = house.page
Paragraphspec

Paragraph(block: Literal['paragraph'] = 'paragraph', text: str, emphasis: bool = False)

Body text. Supports ``{placeholders}`` and a little inline markup.

blocks: tuple[AnyBlock, ...] = (
    Heading(text="Per-arm detail", level=2),
    Paragraph(text="One page per arm, generated in a loop.", emphasis=True),
    Metric(source="contrast", label="Effect", unit="units"),
    Table(source="arms"),
    Divider(),
    PageBreak(),
    LedgerBlock(source="ledger"),
)
Reportspec

Report(name: str, title: str, subtitle: str = '', theme: Theme = ..., sections: tuple[Section, ...] = (), footer: str = '')

A styled, repeatable report: sections of blocks that name their data.

broken = Report(name="b", title="b",
                sections=(Section(title="s", blocks=(Figure(source="sigma"),)),))
ReportBuilderclass

ReportBuilder(name: str, title: str) -> None

A frozen builder for a ``Report``. Every method returns a new one.

template = (
    ReportBuilder("dose_readout", "Dose-response readout")
    .subtitle("Fitted {as_of}. Every number carries its interval.")
    .footer("Generated by axiom.report — template {template_hash}")
    .section("Headline", summary="What the fit says, and how sure it is.")
    .paragraph("The response **saturates**: the marginal effect at {top_dose:.0f} is "
               "already within rounding of zero.")
    .metric("contrast", "Expected outcome at 50", unit="units")
    .metric("sigma", "Residual sd", unit="units")
    .section("Curves", summary="Both figures carry a 90 % band, because they cannot not.")
    .figure("response", caption="Expected outcome against dose")
    .figure("marginal", caption="Marginal effect — where the band straddles zero, the model "
                                "does not know whether the next unit helps")
    .section("Detail")
    .table("arms", caption="Observed means by dose bucket", precision=2)
    .divider()
    # ...
ResolvedBlockvalue

Represent a PEP 604 union type

block: ResolvedBlock = metrics[0]
ResolvedFigureclass

ResolvedFigure(figure: Any, caption: str = '', height: float | None = None, full_page: bool = False) -> None

A plotly figure, its caption, and how tall it should be drawn.

figures = [b for s in resolved.sections for b in s.blocks if isinstance(b, ResolvedFigure)]
ResolvedLedgerclass

ResolvedLedger(lines: tuple[LedgerLine, ...], caption: str = '', show_detail: bool = False) -> None

The assumption trail, as lines of kind / statement / assumption.

ledgers = [b for s in resolved.sections for b in s.blocks if isinstance(b, ResolvedLedger)]
ResolvedMetricclass

ResolvedMetric(label: str, value: float, text: str, interval: Interval | None = None, unit: str = '') -> None

A number, its label, and the interval it came with — if it had one.

metrics = [b for s in resolved.sections for b in s.blocks if isinstance(b, ResolvedMetric)]
ResolvedReportclass

ResolvedReport(name: str, title: str, subtitle: str, footer: str, theme: Any, sections: tuple[ResolvedSection, ...], source_hash: str) -> None

Everything the renderers need and nothing they do not.

assert isinstance(resolved, ResolvedReport)
ResolvedSectionclass

ResolvedSection(title: str, summary: str, blocks: tuple[ResolvedBlock, ...] = (), breaks: frozenset[int] = <factory>) -> None

One section's title, optional summary, and its blocks in reading order.

assert isinstance(section, ResolvedSection)
ResolvedTableclass

ResolvedTable(columns: tuple[str, ...], rows: tuple[tuple[str, ...], ...], caption: str = '', truncated: int = 0) -> None

Header row, body rows already formatted to strings, and what was truncated.

tables = [b for s in resolved.sections for b in s.blocks if isinstance(b, ResolvedTable)]
ResolvedTextclass

ResolvedText(text: str, level: int = 0, emphasis: bool = False) -> None

A heading or paragraph with its placeholders filled in.

texts = [b for s in resolved.sections for b in s.blocks if isinstance(b, ResolvedText)]
Sectionspec

Section(title: str, blocks: tuple[Heading | Paragraph | Figure | Table | Metric | LedgerBlock | Divider | PageBreak, ...] = (), summary: str = '')

A titled run of blocks. Each section starts a new page or slide.

broken = Report(name="b", title="b",
                sections=(Section(title="s", blocks=(Figure(source="sigma"),)),))
Tablespec

Table(block: Literal['table'] = 'table', source: str, caption: str = '', columns: tuple[str, ...] = (), max_rows: int = 20, precision: int = 3)

A table, named by the context key that holds it.

blocks: tuple[AnyBlock, ...] = (
    Heading(text="Per-arm detail", level=2),
    Paragraph(text="One page per arm, generated in a loop.", emphasis=True),
    Metric(source="contrast", label="Effect", unit="units"),
    Table(source="arms"),
    Divider(),
    PageBreak(),
    LedgerBlock(source="ledger"),
)
Themespec

Theme(name: str = 'axiom', font: str = 'Helvetica', font_fallback: str = 'system-ui, -apple-system, sans-serif', mono_font: str = 'Courier', base_size: float = 11.0, title_size: float = 24.0, heading_sizes: tuple[float, float, float] = (18.0, 14.0, 12.0), text_color: str = '#1c1c1c', muted_color: str = '#5b6472', accent_color: str = '#2f7fd1', background_color: str = '#ffffff', rule_color: str = '#d8dbe0', ... and 6 more fields)

Fonts, colours and geometry for a rendered report.

house = Theme(
    name="house", font="Helvetica", accent_color="#b5453b", muted_color="#6b7280",
    palette=("#b5453b", "#2f7fd1", "#3aa17e"), base_size=10.5, title_size=26.0,
    page="a4", slide="standard", figure_height=280.0,
)
figure_pngfn

figure_png(figure: Any, theme: Theme, *, height: float, scale: float = 2.0) -> bytes | Unsupported

A themed figure as PNG bytes, or a typed refusal naming the missing extra.

png = figure_png(figures[0].figure, house, height=220.0)
missingfn

missing(report: Report, context: Mapping[str, object]) -> tuple[str, ...]

Context keys the report needs and the context does not have, in reading order.

print("missing now:", missing(template, context))
placeholdersfn

placeholders(text: str) -> tuple[str, ...]

The ``{name}`` fields in a template string, in order, without duplicates.

print("placeholders in the subtitle:", placeholders(template.subtitle))
renderfn

render(report: Report, context: Mapping[str, object], format: Format = 'html', *, inline_plotly: bool = True) -> str | bytes | Unsupported

Fill ``report`` from ``context`` and render it. ``str`` for HTML, ``bytes`` otherwise.

partial = render(template, {"as_of": "x"}, "html")
render_htmlfn

render_html(resolved: ResolvedReport, *, inline_plotly: bool = True) -> str | Unsupported

A complete, self-contained HTML document. Figures stay interactive.

html = render_html(resolved)
render_pdffn

render_pdf(resolved: ResolvedReport) -> bytes | Unsupported

The report as a PDF, as bytes. ``Unsupported`` without the extra.

paged = render_pdf(resolved_generated)
render_pptxfn

render_pptx(resolved: ResolvedReport) -> bytes | Unsupported

The report as a ``.pptx`` file, as bytes. ``Unsupported`` without the extra.

deck = render_pptx(resolved_generated)
resolvefn

resolve(report: Report, context: Mapping[str, object]) -> ResolvedReport | Unsupported

Fill the template from the context, or say exactly which keys are absent.

resolved = resolve(template, context)
writefn

write(report: Report, context: Mapping[str, object], path: str, format: Format | None = None, *, inline_plotly: bool = True) -> str | Unsupported

Render and write to ``path``, returning the path. Format is inferred from the suffix.

written = write(template, context, str(out / f"readout.{fmt}"), chosen)

How to read this

Layers, and why they matter to you

foundation

core, data, io. Specs, dimensions, intervals, panels, and the analysis round-trip. Pure numpy / scipy / pandas / pydantic. If you only ever import these, axiom is a very small dependency.

sampler seam

infer. The one place an optional dependency is allowed to live. Laplace works with no extras; NumPyro and PyMC sit behind pip install "axiom[numpyro]" and pip install "axiom[pymc]", and four NUTS samplers agree to within 0.12 posterior standard deviations over one shared log density.

domain

dynamics, identify, discover, estimands, surface, sim. The causal and functional vocabulary — a language for systems that are simultaneous or unroll over time, graphs and the searches that learn one from data, nine-facet estimands, response kernels, and the synthetic worlds that make every claim in the test suite checkable against a known truth.

the pillars

design, calibrate, meta. Peers, with no cross-imports except design ← surface and calibrate ← estimands. You can use any one of them without dragging in the others.

composition

diagnose, build, adapters, viz, report. Everything that composes the layers below — model checking, fluent builders, the domain adapters, plots, and templated HTML / PPTX / PDF reports.

Imports point down only

A contract test walks the import graph and fails the build if an arrow ever points back up. That is what keeps the foundation usable on its own and lets the pillars be adopted one at a time.

Getting the full documentation

This page is a map with a worked line per symbol, not a reference manual: no parameter tables, no prose on edge cases. The Sphinx documentation — full signatures, complete docstrings, and the notebook series for each subpackage — builds from the repository with make docs, and every subpackage’s API page links its own notebook series.

shell
git clone https://github.com/redam94/axiom
cd axiom
uv sync --group dev

make docs        # Sphinx, builds with zero warnings
make notebooks   # execute every notebook under nbs/
make gates       # the twelve contract tests

Where to start reading code

nbs/end-to-end Five notebooks, one per pillar. The shortest path from nothing to a working analysis. 5
nbs/case-studies The HYPER-3 trial, worked over one synthetic world. 6
docs/plan The charter, architecture, porting ledger, roadmap, and the contracts-and-testing rules the build enforces. 6
docs/notes Numbered design decisions, written as the work happened — including why a number deliberately changed from its parent. 8