Skip to content

Handler base

The handler stack, the run context, and the installation record.

base

Effect handlers for probabilistic programs, as lexical kernel handlers.

A MonadicProgram runs on the reference machine as the kernel computation :mod:quivers.effects.program_module encodes it to, whose sites are Random.sample requests on the program's random instance and whose densities are Score.add requests on its score instance. An EffectHandler is a description of a handler of one of those instances: entering it as a context manager pushes it on a thread-local stack, and quivers.effects.interpreter.run_program installs every handler on the stack as a lexical handler around the program, in the order the stack gives, so the composition rules are the kernel's own. The design descends from the Pyro poutine and NumPyro handlers shape (Pyro poutine docs) and from the operational account of handlers in Plotkin and Pretnar (2009); its application to probabilistic programming follows Scibior et al. (2018).

Two rules govern how a stack composes. A handler of the random instance sees a site's request before any handler outside it, and sees the outer answer flow back through its resumption, so an inner trace records what an outer clamp or intervention decided. The density a site contributes is a Score.add the answering handler emits outward, so every handler of the score instance on the stack transforms every site's density on its way to the run's accumulator, and a trace reads a site's density from what reached the accumulator under that site's provenance.

SiteKey

SiteKey = tuple[str, tuple[tuple[str, str | int], ...], tuple[int, ...]]

The dynamic address of one occurrence of a site: its static identity, its dynamic address frames, and its resumption path.

Contribution dataclass

Contribution(key: SiteKey, role: str, weight: Tensor, path: tuple[str | int, ...] = ())

One score contribution that reached the run's accumulator.

PARAMETER DESCRIPTION
key

The address of the site the contribution was derived from.

TYPE: SiteKey

role

What the contribution was for, from the emitting handler's provenance: a scored draw, a conditioned observation, a replay.

TYPE: str

weight

The contribution as it arrived, after every transformer.

TYPE: Tensor

path

The structural path of the request's origin; a score step of the program has the path of its step, a derived contribution the path it was generated at.

TYPE: tuple[str | int, ...] DEFAULT: ()

RunContext dataclass

RunContext(kernel: ProgramKernel, validator: RuntimeValidator, contributions: list[Contribution] = list(), interventions: set[SiteKey] = set(), annotations: dict[str, dict[str, object]] = dict(), parameters: dict[str, Tensor] = dict(), observations: dict[str, Tensor] = dict(), supplied: dict[str, Tensor] = dict())

What one run shares with the handlers installed on it.

PARAMETER DESCRIPTION
kernel

The program's kernel encoding.

TYPE: ProgramKernel

validator

The host value validator every handler's clauses check with.

TYPE: RuntimeValidator

contributions

The score contributions that reached the accumulator, in order.

TYPE: list[Contribution] DEFAULT: list()

interventions

The sites an intervention fixed, by address.

TYPE: set[SiteKey] DEFAULT: set()

annotations

Per-site annotations handlers attach, keyed by site label.

TYPE: dict[str, dict[str, object]] DEFAULT: dict()

parameters

The learned parameters of the program's morphisms by qualified name, as the parameter store answers them.

TYPE: dict[str, Tensor] DEFAULT: dict()

observations

The run's observations at declared sites, by site label.

TYPE: dict[str, Tensor] DEFAULT: dict()

supplied

Values standing in for let bindings, by name, which their steps read rather than compute.

TYPE: dict[str, Tensor] DEFAULT: dict()

result_type property

result_type: TypeExpr

The type of the program's result.

RETURNS DESCRIPTION
TypeExpr

The entry computation's result type, which every transparent handler answers with.

key_of

key_of(request: RuntimeRequest) -> SiteKey

The address of the site a request belongs to.

PARAMETER DESCRIPTION
request

A sample request, or a score request derived from one.

TYPE: RuntimeRequest

RETURNS DESCRIPTION
SiteKey

The request's own address for a sample request; for a derived request, the address of the site it was derived from.

Source code in src/quivers/effects/base.py
def key_of(self, request: RuntimeRequest) -> SiteKey:
    """The address of the site a request belongs to.

    Parameters
    ----------
    request : RuntimeRequest
        A sample request, or a score request derived from one.

    Returns
    -------
    SiteKey
        The request's own address for a sample request; for a derived
        request, the address of the site it was derived from.
    """
    static, dynamic, path = request.address
    parents = request.core.origin.parents
    if parents:
        # A derived request's declared dynamic path is the address of
        # the site it was derived from.
        static = str(parents[0])
        dynamic = tuple(
            (frame.scope, frame.key) for frame in request.core.origin.dynamic_path
        )
    return (static, dynamic, path)

Installation dataclass

Installation(instance: NamedEffectInstance, runtime: RuntimeHandler)

One lexical handler a stack entry installs on a run.

PARAMETER DESCRIPTION
instance

The instance the handler handles: the program's random, score, or param instance.

TYPE: NamedEffectInstance

runtime

The executable handler, whose definition joins the run's module.

TYPE: RuntimeHandler

named

named(name: str) -> Installation

The installation with its handler declared under a name.

A module's handler names and identities are unique, while the prelude factories name every handler of a kind alike and derive its identity from the stack entry that made it; a run renames each installation it makes and derives its identity from the name, so two runs installing the same kinds of handler in the same order build the same module.

PARAMETER DESCRIPTION
name

The declaration's name, unique within the run.

TYPE: str

RETURNS DESCRIPTION
Installation

The installation with the renamed declaration, the rename carried into any per-installation factory the handler has.

Source code in src/quivers/effects/base.py
def named(self, name: str) -> Installation:
    """The installation with its handler declared under a name.

    A module's handler names and identities are unique, while the
    prelude factories name every handler of a kind alike and derive
    its identity from the stack entry that made it; a run renames
    each installation it makes and derives its identity from the
    name, so two runs installing the same kinds of handler in the
    same order build the same module.

    Parameters
    ----------
    name : str
        The declaration's name, unique within the run.

    Returns
    -------
    Installation
        The installation with the renamed declaration, the rename
        carried into any per-installation factory the handler has.
    """
    definition = replace(
        self.runtime.definition,
        id=HandlerId.derive("run", name),
        name=name,
    )
    runtime = replace(self.runtime, definition=definition)
    factory = self.runtime.context_factory
    if factory is not None:

        def context_factory() -> RuntimeHandler:
            """Build a fresh installation under the renamed declaration.

            Returns
            -------
            RuntimeHandler
                The handler the original factory builds, renamed.
            """
            return replace(factory(), definition=definition)

        runtime.context_factory = context_factory
    return Installation(self.instance, runtime)

EffectHandler

Bases: ABC

A handler of a program's canonical instances, stacked by with.

Subclasses implement :meth:install, which builds the lexical handlers the entry contributes to one run, and may implement :meth:finish, which runs after the program returns with the run's context. The handler activates by being used as a context manager:

with clamp({"z": z_val}):
    samples = predictive.rsample(x)

Nested with blocks stack handlers outer-first; the innermost handler of an instance is installed closest to the program.

__enter__

__enter__() -> Self

Push the handler on the thread's stack.

RETURNS DESCRIPTION
Self

The handler, so with handler as h binds it.

Source code in src/quivers/effects/base.py
def __enter__(self) -> Self:
    """Push the handler on the thread's stack.

    Returns
    -------
    Self
        The handler, so ``with handler as h`` binds it.
    """
    _handler_stack().append(self)
    return self

__exit__

__exit__(exc_type: type[BaseException] | None, exc: BaseException | None, tb: object) -> None

Pop the handler off the thread's stack.

PARAMETER DESCRIPTION
exc_type

The exception type leaving the block, if any.

TYPE: type[BaseException] | None

exc

The exception, if any.

TYPE: BaseException | None

tb

Its traceback, if any.

TYPE: object

RAISES DESCRIPTION
RuntimeError

If the handler is not the top of the stack, which means the blocks were exited out of order.

Source code in src/quivers/effects/base.py
def __exit__(
    self,
    exc_type: type[BaseException] | None,
    exc: BaseException | None,
    tb: object,
) -> None:
    """Pop the handler off the thread's stack.

    Parameters
    ----------
    exc_type : type[BaseException] | None
        The exception type leaving the block, if any.
    exc : BaseException | None
        The exception, if any.
    tb : object
        Its traceback, if any.

    Raises
    ------
    RuntimeError
        If the handler is not the top of the stack, which means the
        blocks were exited out of order.
    """
    del exc_type, exc, tb
    stack = _handler_stack()
    if not stack or stack[-1] is not self:
        raise RuntimeError(
            "EffectHandler.__exit__: handler stack corruption; "
            "handlers must be entered / exited in strict LIFO order."
        )
    stack.pop()

install abstractmethod

install(run: RunContext) -> tuple[Installation, ...]

Build the lexical handlers this entry installs on a run.

PARAMETER DESCRIPTION
run

The run being prepared.

TYPE: RunContext

RETURNS DESCRIPTION
tuple[Installation, ...]

The handlers, each naming the instance it handles.

Source code in src/quivers/effects/base.py
@abstractmethod
def install(self, run: RunContext) -> tuple[Installation, ...]:
    """Build the lexical handlers this entry installs on a run.

    Parameters
    ----------
    run : RunContext
        The run being prepared.

    Returns
    -------
    tuple[Installation, ...]
        The handlers, each naming the instance it handles.
    """

finish

finish(run: RunContext, output: object) -> None

Observe the completed run.

PARAMETER DESCRIPTION
run

The run, with every contribution the accumulator received.

TYPE: RunContext

output

The program's output.

TYPE: object

Source code in src/quivers/effects/base.py
def finish(self, run: RunContext, output: object) -> None:
    """Observe the completed run.

    Parameters
    ----------
    run : RunContext
        The run, with every contribution the accumulator received.
    output : object
        The program's output.
    """
    del run, output