bayesmithDocumentation0.10.0 source

bayesmith.dispatch.streaming

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

Module contract
Whether a graph's exact block can be executed one epoch at a time.

``evidence/`` answers "fold this campaign"; it does not answer "is this a
campaign, and on which axis". That second question is a dispatch question --
the same shape as "which latents does an exact method apply to" that
:mod:`~bayesmith.dispatch.classify` already answers -- and until this module
existed nothing asked it. The evidence layer was complete, validated against
dense oracles at every level and cross-checked bitwise against rheplicant's
``inference.sqrtinfo``, with **no caller anywhere in this package**.

**Not a new method, and not a new kind.** Measured on the four-epoch scalar
campaign: ``compile()`` already accepts such a graph and plans one joint
``GCR exact`` block over ``{g, n}`` -- the survivor and all E copies of the
per-epoch nuisance at once. The streamed route solves the SAME problem, so
the plan gains a note rather than a branch::

    compile()->estimate()       g = -0.10616253398516946   (8.8e-15 from oracle)
    compress_campaign + prior   g = -0.1061625339851607    (5.6e-17 from oracle)
    dense marginal oracle       g = -0.10616253398516065

What differs is scaling, not the answer: the joint block's solve grows with
``1 + E`` latents, and the fold holds one epoch at a time.

**The route is derived by TRYING, never by guessing.** A plate is an epoch
plate only if the evidence layer's own structural checks accept it, so this
module runs those checks and forwards their refusals verbatim. It deliberately
declines to pick when more than one plate qualifies: which axis indexes
epochs is a modelling fact the graph does not carry, and a dispatcher that
guessed would be right until it silently was not.

StreamingRoute

Defined in bayesmith.dispatch.streaming, line 48.

class StreamingRoute(eqx.Module)
plate: str | None = eqx.field(static=True)
survivors: tuple[str, ...] = eqx.field(static=True)
per_epoch: tuple[str, ...] = eqx.field(static=True)
refused: tuple[tuple[str, str], ...] = eqx.field(static=True)
Whether a campaign fold applies to this graph, and on which plate.

Attributes:
    plate: the epoch plate, or ``None`` when no plate qualifies or more
        than one does. ``None`` is never "we could not be bothered" --
        :attr:`refused` says which it is.
    survivors: latents outside that plate, which a streamed term is over.
        ``()`` when there is no route.
    per_epoch: latents inside it, integrated away within their own epoch.
    refused: ``(plate name, why not)`` per rejected candidate, in plate
        order, **in the evidence layer's own words**. A graph with no
        plates refuses nothing and this is empty -- an absence of
        candidates, not a rejection of any.

StreamingRoute.available

available(self) -> bool
Whether ``compress_campaign(graph, route.plate)`` will run.

Both directions are pinned by
``tests/dispatch/test_streaming.py::TestTheRouteDoesNotLie`` against
the real call, because a route that reports a plate the fold then
refuses is worse than no route at all.

StreamingRoute.line

line(self) -> str
The one line :class:`~bayesmith.dispatch.plan.InferencePlan` prints.

Empty when there is nothing to say, so a graph that is not a campaign
prints byte-identically to what it printed before this module
existed -- which is what keeps the twenty existing assertions on
``str(plan)`` measuring what they were written to measure.

streaming_route

Defined in bayesmith.dispatch.streaming, line 121.

streaming_route(graph: Graph, *, at: dict[str, Any] | None=None) -> StreamingRoute
Try every plate, and report which one the evidence layer accepts.

Runs the two STRUCTURAL checks ``epoch_terms`` makes before it folds --
:func:`~bayesmith.marginal.factorize.factorize` (plate membership, then
``epoch_leakage`` to test it) and "exactly one observed node is plated on
this axis". Running only the first would produce a route that reports a
plate ``compress_campaign`` then refuses, which is the failure this
module's own tests are built around.

It does NOT run the fold, so the one refusal it cannot anticipate is
``_refuse_unconstrained_epochs`` -- a rank verdict on the assembled
pivots, which exists only once the arithmetic has been done. That is
stated rather than hidden: :attr:`StreamingRoute.available` promises the
structural checks pass, which is what a plan can honestly record.

Cost is flat in the campaign length -- measured 0.278 s at E=4 and 0.342 s
at E=512, against ``compile()``'s own 1.0-1.3 s -- because the leakage
probe is one ``jacfwd`` of the whole plate, not one per epoch. A graph
with no plates costs nothing: the loop below does not run.

Args:
    graph: the model.
    at: values the leakage check holds fixed, forwarded to ``factorize``.
        Defaults to zeros, which is enough because an affine model's
        leakage does not depend on the point, and a model that is not
        affine in its per-epoch latents cannot be folded at all.

Returns:
    A :class:`StreamingRoute`. Does not raise for a graph the evidence
    layer declines OR one it cannot analyse: a refusal is the ANSWER
    here, not an error, because ``compile()`` calls this on every graph
    and must not start rejecting graphs it accepts today. The two are
    recorded distinguishably -- see the ``ValueError`` clause below.

Type a concept or API name.