Skip to content

Chain shapes

chain_shape

ChainShape: per-step metadata derived from a compiled QVR program.

A ChainShape walks a quivers.dsl.ast_nodes.Module AST once and records, for every let / latent / observe / marginalize step inside the module's program block:

  • the bound variable name,
  • the source line / column where the step was declared,
  • the program's governing algebra (looked up from the top-level algebra <name> declaration via the algebra registry),
  • the step's depth in the chain of stochastic / scoring binds, numbered from 1 at the first latent / observe and incremented at every subsequent stochastic step. let steps carry the depth of the most recent preceding stochastic step (0 if there isn't one yet),
  • the step's intermediate axis size where it can be derived from the program's object declarations (Resp cardinality for per-row lets / observes; the plate object cardinality for per-level latents; the morphism's codomain otherwise). When the size cannot be statically inferred we record None.

The result is purely metadata; ChainShape never rewrites or recompiles the program. The fields are what downstream tooling (algebra-specific initialization, saturation warnings, hook-based telemetry) reads.

StepKind module-attribute

StepKind = Literal['latent', 'observe', 'marginalize', 'let']

The kind tag of a StepShape: a stochastic bind (latent, observe, marginalize) or a deterministic let.

StepShape didactic-model

StepShape(*, name: str, kind: StepKind, source_line: int = 0, source_col: int = 0, depth: int = 0, algebra_name: str = '', intermediate_size: int | None = None)

Bases: Model

Per-step metadata derived by ChainShape.

ATTRIBUTE DESCRIPTION
name

Bound variable name (the LHS of the step).

TYPE: str

kind

One of "latent" / "observe" / "marginalize" (stochastic-bind steps) or "let" (deterministic).

TYPE: str

source_line, source_col

Source position where the step was declared.

TYPE: int

depth

The 1-indexed position of the step in the chain of stochastic binds (latent / observe / marginalize). Let steps inherit the depth of the most recent stochastic predecessor; a let before any stochastic step has depth 0.

TYPE: int

algebra_name

Name of the program's governing algebra.

TYPE: str

intermediate_size

Cardinality of the bound coordinate's intermediate axis, when statically inferable from the AST. None if not.

TYPE: int | None

ChainShape didactic-model

ChainShape(*, algebra_name: str = '', object_cardinalities: Mapping[str, int] = field(default_factory=dict, opaque=True), steps: tuple[StepShape, ...] = ())

Bases: Model

Sequence of StepShape records for a program.

ATTRIBUTE DESCRIPTION
algebra_name

Name of the program's governing algebra (the top-level algebra <name> declaration).

TYPE: str

object_cardinalities

Cardinality of every object X : N declaration preceding the program. Used to resolve plate sizes referenced by : T annotations on latents / observes.

TYPE: Mapping[str, int]

steps

Per-step metadata in source order.

TYPE: tuple[StepShape, ...]

stochastic_depth property

stochastic_depth: int

Number of stochastic-bind steps (latent, observe, or marginalize).

algebra property

algebra: Algebra | None

Resolve algebra_name against the algebra registry. Returns None if the name is unknown (e.g. a user-defined inline composition rule not registered).

latents

latents() -> tuple[StepShape, ...]

All latent (sample-mode) steps.

Source code in src/quivers/analysis/chain_shape.py
def latents(self) -> tuple[StepShape, ...]:
    """All ``latent`` (sample-mode) steps."""
    return tuple(s for s in self.steps if s.kind == "latent")

observes

observes() -> tuple[StepShape, ...]

All observe (score-mode) steps.

Source code in src/quivers/analysis/chain_shape.py
def observes(self) -> tuple[StepShape, ...]:
    """All ``observe`` (score-mode) steps."""
    return tuple(s for s in self.steps if s.kind == "observe")

from_module classmethod

from_module(module: Module) -> 'ChainShape'

Build a ChainShape from a compiled Module AST.

Walks the module's statement list, captures the algebra name from any top-level CompositionDecl, captures every ObjectDecl's numeric cardinality, then walks the unique ProgramDecl's steps in source order. MarginalizeStep bodies are walked recursively; their inner steps are recorded after the enclosing MarginalizeStep.

Source code in src/quivers/analysis/chain_shape.py
@classmethod
def from_module(cls, module: Module) -> "ChainShape":
    """Build a `ChainShape` from a compiled
    `Module` AST.

    Walks the module's statement list, captures the algebra
    name from any top-level `CompositionDecl`, captures
    every `ObjectDecl`'s numeric cardinality, then walks
    the unique `ProgramDecl`'s steps in source order.
    `MarginalizeStep` bodies are walked recursively;
    their inner steps are recorded after the enclosing
    `MarginalizeStep`.
    """
    algebra_name = "product_fuzzy"
    cardinalities: dict[str, int] = {}
    program: ProgramDecl | None = None
    for stmt in module.statements:
        if isinstance(stmt, CompositionDecl):
            algebra_name = stmt.name
        elif isinstance(stmt, ObjectDecl):
            cardinality = _type_decl_cardinality(stmt)
            if cardinality is not None:
                for decl_name in stmt.names:
                    cardinalities[decl_name] = cardinality
        elif isinstance(stmt, ProgramDecl) and program is None:
            program = stmt

    steps: list[StepShape] = []
    depth = 0

    def record(
        name: str,
        kind: StepKind,
        line: int,
        col: int,
        intermediate: int | None,
    ) -> None:
        nonlocal depth
        depth += 1
        steps.append(
            StepShape(
                name=name,
                kind=kind,
                source_line=line,
                source_col=col,
                depth=depth,
                algebra_name=algebra_name,
                intermediate_size=intermediate,
            )
        )

    def walk(program_steps: tuple[ProgramStep, ...]) -> None:
        for step in program_steps:
            if isinstance(step, SampleStep):
                record(
                    step.vars[0] if step.vars else "",
                    "latent",
                    step.line,
                    step.col,
                    _index_size(step.index, cardinalities),
                )
            elif isinstance(step, ObserveStep):
                record(
                    step.vars[0],
                    "observe",
                    step.line,
                    step.col,
                    _index_size(step.index, cardinalities),
                )
            elif isinstance(step, MarginalizeStep):
                record(
                    step.var,
                    "marginalize",
                    step.line,
                    step.col,
                    _index_size(step.index, cardinalities),
                )
                walk(step.scope)
            elif isinstance(step, LetStep):
                steps.append(
                    StepShape(
                        name=step.name,
                        kind="let",
                        source_line=step.line,
                        source_col=step.col,
                        depth=depth,
                        algebra_name=algebra_name,
                        intermediate_size=None,
                    )
                )

    if program is not None:
        walk(program.draws)

    return cls(
        algebra_name=algebra_name,
        object_cardinalities=dict(cardinalities),
        steps=tuple(steps),
    )