bayesmithDocumentation0.10.0 source

bayesmith.marginal.campaign

Source-derived signatures and contracts. See the reference index for coverage and conventions.

Module contract
A campaign, compressed from the graph, one epoch at a time.

Everything below this module works on matrices a caller assembled:
:func:`~bayesmith.marginal.compress.compress_epoch` takes designs, data and a
covariance and knows nothing about where they came from. This is where they
come from a :class:`~bayesmith.graph.graph.Graph`.

**Derived, not declared twice**, which is the migration spec's whole argument
for rewriting B11 rather than transplanting it. Every input
``compress_epoch`` needs is read off the graph:

===================  ======================================================
what                 where it comes from
===================  ======================================================
the partition        :func:`~bayesmith.marginal.factorize.factorize` --
                     plate membership, then ``epoch_leakage`` to test it
the designs          one ``jacfwd`` of the graph's own prediction map
the constant part    that map evaluated at zero
the data             the observed node's own ``obs``, sliced by epoch
the covariance       :func:`~bayesmith.exact.gaussian.precision_at`, sliced
the nuisance priors  the per-epoch latents' own ``dist_fn``
===================  ======================================================

The last row is the one rheplicant cannot have: its ``Factorization`` carries
per-epoch priors separately from the ``ParameterSpace`` that declares them,
and "the same space declared twice" is the error class this kills.

epoch_terms

Defined in bayesmith.marginal.campaign, line 47.

epoch_terms(graph: Graph, epoch_plate: str, *, at: dict[str, Any] | None=None, factorization: Factorization | None=None, stacked_only: bool=False) -> list[SqrtInfo]
One ``[R | z]`` term per epoch, over the survivors.

Each epoch's own per-epoch latents are integrated out inside it, at the
prior the graph declares for them -- so what comes back is a list of terms
all over the same latents, ready to fold.

Args:
    graph: the model. Exactly one observed node, plated on ``epoch_plate``.
    epoch_plate: the plate whose axis indexes epochs.
    at: values for latents this holds fixed. Defaults to zeros, which is
        what an affine model's design does not depend on.
    factorization: a partition already derived and checked. ``None``
        derives one, **including the leakage check** -- pass one only if
        you have already run that.
    stacked_only: return the terms as STACKED arrays rather than a list,
        so :func:`compress_campaign` can fold them under ``lax.scan``
        instead of unpacking E of them only to re-stack. Internal; the
        list is the shape a caller wants.

Raises:
    StructureError: from :func:`factorize`; or if the graph does not have
        exactly one observed node plated on ``epoch_plate``, which is what
        makes "one epoch's data" a thing that can be sliced.

fold_epochs

Defined in bayesmith.marginal.campaign, line 218.

fold_epochs(designs: jax.Array, targets: jax.Array, offsets: jax.Array, width: int) -> SqrtInfo | tuple[jax.Array, jax.Array, jax.Array]
Fold ``E`` stacked ``[R | z]`` terms into one, under ``lax.scan``.

Traceable, and that is the point. The Python loop it replaces costs one
eager QR per epoch and an unrolled ``jit`` of it compiles in time
proportional to ``E`` -- measured at ``E = 1000``, 5.4 s to compile a fold
whose warm run is 2 ms. A scan compiles once whatever ``E`` is.

The carry is the accumulated ``(factor, target, offset)``, and it keeps a
FIXED shape across the whole campaign -- which is why
:meth:`~bayesmith.marginal.sqrtinfo.SqrtInfo.null` is square rather than
zero-row. A carry whose treedef moved would retrace once per epoch and
defeat the exercise.

Args:
    designs: ``(E, rows, width)`` stacked factors.
    targets: ``(E, rows)`` stacked targets.
    offsets: ``(E,)`` stacked constants.
    width: the column count, needed statically for the carry's shape.

Returns:
    ``(factor, target, offset)`` of the folded term.

compress_campaign

Defined in bayesmith.marginal.campaign, line 273.

compress_campaign(graph: Graph, epoch_plate: str, *, at: dict[str, Any] | None=None, factorization: Factorization | None=None) -> SqrtInfo
Fold every epoch into one term over the survivors.

Holds one epoch's term at a time, which is the point of the exercise: the
raw data and the forward evaluation are gone by the end, and what remains
is a sufficient statistic whose size does not grow with the campaign.

epoch_observation

Defined in bayesmith.marginal.campaign, line 296.

epoch_observation(graph: Graph, epoch_plate: str) -> str
The one observed node whose data IS this plate's epochs, or a refusal.

PUBLIC because a caller deciding *whether* to fold needs the same
structural verdict the fold itself makes, and cannot get it from
``factorize`` alone -- see
:func:`~bayesmith.dispatch.streaming.streaming_route`, which was the
package's only cross-module private import until this name lost its
underscore.

Type a concept or API name.