Skip to content

Quivers Indexed Effect Core

The stable kernel every QVR module checks against and every transpiler reads. The package re-exports the modules below.

qiec

Quivers Indexed Effect Core (QIEC) reference kernel.

QIEC is a small typed value/computation IR for indexed families and algebraic effects. It is deliberately independent of the QVR surface grammar and the target probabilistic IR: the compiler may lower into QIEC incrementally, while Didactic and Panproto check its first-order indexed-family projection.

ABORT module-attribute

ABORT = _effect_ref('Abort')

Abort[P]: the prelude interface for stopping with a payload of type P.

ABORT_ABORT module-attribute

ABORT_ABORT = _operation_id(ABORT, 'abort')

Abort.abort: stop the computation with an error payload.

ABORT_EFFECT module-attribute

ABORT_EFFECT = EffectDef(ABORT, (_P_BINDER,), (OperationDef(ABORT_ABORT, 'abort', (_A_BINDER,), (ArgumentDef('error', PAYLOAD),), A),))

The declaration of :data:ABORT; :func:abort_effect applies it.

BUILTIN_EFFECTS module-attribute

Every prelude interface declaration, which every module may name without declaring.

CHOOSE module-attribute

CHOOSE = _effect_ref('Choose')

Choose: the prelude interface for nondeterministic choice.

CHOOSE_CHOOSE module-attribute

CHOOSE_CHOOSE = _operation_id(CHOOSE, 'choose')

Choose.choose: pick one of a collection of alternatives.

CHOOSE_EFFECT module-attribute

CHOOSE_EFFECT = EffectDef(CHOOSE, (), (OperationDef(CHOOSE_CHOOSE, 'choose', (_A_BINDER,), (ArgumentDef('alternatives', CHOICES_A),), A),))

The declaration of :data:CHOOSE.

COMPUTE module-attribute

COMPUTE = _effect_ref('Compute')

Compute[X, A]: the prelude interface for a host computation; see :data:COMPUTE_EFFECT.

COMPUTE_APPLY module-attribute

COMPUTE_APPLY = _operation_id(COMPUTE, 'apply')

Compute.apply: run the host computation on an argument of type X.

COMPUTE_EFFECT module-attribute

COMPUTE_EFFECT = EffectDef(COMPUTE, (_X_BINDER, _ANSWER_BINDER), (OperationDef(COMPUTE_APPLY, 'apply', (), (ArgumentDef('argument', INPUT),), ANSWER),))

Compute[X, A]: a host computation as an effect interface.

A morphism, a closure, or any other process-local function a program depends on is not a kernel value; performing apply on an instance of this interface asks the handler installed for it, which a runtime provider supplies, so the dependence is explicit in the row.

PARAM module-attribute

PARAM = _effect_ref('Param')

Param: the prelude interface for learned parameter lookup; see :data:PARAM_EFFECT.

PARAM_EFFECT module-attribute

PARAM_EFFECT = EffectDef(PARAM, (), (OperationDef(PARAM_GET, 'get', (_A_BINDER,), (ArgumentDef('name', STRING),), A),))

Param: learned parameter lookup as an effect.

A host computation reads each learned tensor it depends on by name through get, so a handler between it and the parameter store can answer with a perturbed value, as a prior lift does, or record the read.

PARAM_GET module-attribute

PARAM_GET = _operation_id(PARAM, 'get')

Param.get: read a learned parameter by name.

RANDOM module-attribute

RANDOM = _effect_ref('Random')

Random: the prelude interface for drawing a value at a sample site.

RANDOM_EFFECT module-attribute

RANDOM_EFFECT = EffectDef(RANDOM, (), (OperationDef(RANDOM_SAMPLE, 'sample', (_A_BINDER,), (ArgumentDef('site', SITE_A), ArgumentDef('sampleable', SAMPLEABLE_A)), A),))

The declaration of :data:RANDOM.

RANDOM_SAMPLE module-attribute

RANDOM_SAMPLE = _operation_id(RANDOM, 'sample')

Random.sample: draw a value of type A at a site from a Sampleable[A].

SCORE module-attribute

SCORE = _effect_ref('Score')

Score: the prelude interface for adding a log-density contribution.

SCORE_ADD module-attribute

SCORE_ADD = _operation_id(SCORE, 'add')

Score.add: add one LogWeight to the running score.

SCORE_EFFECT module-attribute

SCORE_EFFECT = EffectDef(SCORE, (), (OperationDef(SCORE_ADD, 'add', (), (ArgumentDef('weight', LOG_WEIGHT),), UNIT),))

The declaration of :data:SCORE.

SEARCH_REDUCTIONS module-attribute

SEARCH_REDUCTIONS: tuple[str, ...] = ('logsumexp', 'max', 'or', 'sum')

The semiring additions :func:search_handler may combine its shots by.

STATE_EFFECT module-attribute

STATE_EFFECT = EffectDef(STATE_EFFECT_REF, (_S_BINDER,), (OperationDef(STATE_GET, 'get', (), (), STATE), OperationDef(STATE_PUT, 'put', (), (ArgumentDef('value', STATE),), UNIT)))

The declaration of :data:STATE_EFFECT_REF; :func:state_effect applies it.

STATE_EFFECT_REF module-attribute

STATE_EFFECT_REF = _effect_ref('State')

State[S]: the prelude interface for one mutable cell of type S.

STATE_GET module-attribute

STATE_GET = _operation_id(STATE_EFFECT_REF, 'get')

State.get: read the cell.

STATE_PUT module-attribute

STATE_PUT = _operation_id(STATE_EFFECT_REF, 'put')

State.put: overwrite the cell.

WEIGHT module-attribute

WEIGHT = _effect_ref('Weight')

Weight[K]: the prelude interface for accumulating a semiring weight of type K.

WEIGHT_ADD module-attribute

WEIGHT_ADD = _operation_id(WEIGHT, 'add')

Weight.add: fold one weight into the running total in the handler's semiring.

WEIGHT_EFFECT module-attribute

WEIGHT_EFFECT = EffectDef(WEIGHT, (_K_BINDER,), (OperationDef(WEIGHT_ADD, 'add', (), (ArgumentDef('weight', K),), UNIT),))

The declaration of :data:WEIGHT; :func:weight_effect applies it.

BUILTIN_TYPE_CONSTRUCTORS module-attribute

BUILTIN_TYPE_CONSTRUCTORS: dict[str, TypeConstructorRef] = {'Site': SITE_CONSTRUCTOR, 'Sampleable': SAMPLEABLE_CONSTRUCTOR, 'Tensor': TENSOR_CONSTRUCTOR, 'LogWeight': LOG_WEIGHT.constructor}

The canonical names a source type may refer to without declaring them.

ELEMENT module-attribute

ELEMENT = TypeVariable('a')

The type variable :data:ELEMENT_BINDER binds.

ELEMENT_BINDER module-attribute

ELEMENT_BINDER = TypeBinder('a')

The element-type binder a of the canonical constructors' telescopes.

LOG_WEIGHT module-attribute

LOG_WEIGHT = TypeApplication(builtin_constructor('LogWeight'))

LogWeight: a log-density contribution.

SAMPLEABLE_CONSTRUCTOR module-attribute

SAMPLEABLE_CONSTRUCTOR = builtin_constructor('Sampleable', ELEMENT_BINDER)

Sampleable[A]: a distribution over values of type A.

SHAPE_BINDER module-attribute

SHAPE_BINDER = IndexBinder('shape', ShapeSort())

The shape-index binder of :data:TENSOR_CONSTRUCTOR's telescope.

SITE_CONSTRUCTOR module-attribute

SITE_CONSTRUCTOR = builtin_constructor('Site', ELEMENT_BINDER)

Site[A]: a named sample site whose value has type A.

TENSOR_CONSTRUCTOR module-attribute

TENSOR_CONSTRUCTOR = builtin_constructor('Tensor', ELEMENT_BINDER, SHAPE_BINDER)

Tensor[A](shape): an array of A with a static shape index.

EMPTY_ROW module-attribute

EMPTY_ROW = EffectRow()

The row of a computation that performs no effects.

FAMILIES module-attribute

FAMILIES: Mapping[str, DistributionFamily] = MappingProxyType({item.name: item for item in _FAMILIES})

Every family by source name.

The mapping is closed: a name absent here is not a distribution family, and no backend may invent one.

QIEC_ABI module-attribute

QIEC_ABI = 'qiec-core/v1alpha1'

The kernel ABI tag a module records, so a reader can reject a module built against another kernel.

EFFECT module-attribute

EFFECT = EffectKind()

The kind of effect interface applications.

NAT module-attribute

NAT = NatSort()

The sort of natural-number indices.

ROW module-attribute

ROW = RowKind()

The kind of effect rows.

SHAPE module-attribute

SHAPE = ShapeSort()

The sort of static tensor shapes.

TYPE module-attribute

TYPE = TypeKind()

The kind of types.

IMPLEMENTATIONS module-attribute

IMPLEMENTATIONS: Mapping[str, Callable[..., object]] = MappingProxyType({signature.name: implementation for signature, implementation in _ENTRIES})

The reference evaluator's implementation of every primitive, by name.

PRIMITIVES module-attribute

PRIMITIVES: Mapping[str, PrimitiveSignature] = MappingProxyType({signature.name: signature for signature, _ in _ENTRIES})

Every primitive by name.

The mapping is closed: a name absent here is not a primitive, and no backend may resolve one dynamically.

QIEC_WIRE_FORMAT module-attribute

QIEC_WIRE_FORMAT = 'qiec-json/v1'

The format tag every encoded document carries, checked on decoding.

SCALE_FLOOR module-attribute

SCALE_FLOOR = 1e-07

The floor an exponentiated scale head of an :class:AffineMap or :class:TableMap is clamped at, which every backend applies alike.

BOOL module-attribute

The Boolean type.

INT module-attribute

The integer type.

REAL module-attribute

The real-number type.

STRING module-attribute

The string type.

UNIT module-attribute

The unit type, inhabited by None alone.

EntryKind

EntryKind = Literal['computation', 'program']

How an entry point is declared: with define or with program.

HostValue

HostValue = bool | int | float | str | Tensor | tuple['HostValue', ...] | Mapping[str, 'HostValue']

A value the reference machine takes or returns: a scalar, a tensor, a tuple, or a record of such values.

EvaluationTraceHook

EvaluationTraceHook = Callable[[str, Mapping[str, object]], None]

A callback receiving each evaluator event's name and detail mapping.

OperationClause

OperationClause = Callable[['RuntimeRequest', 'Resumption', 'ClauseContext'], object]

A host implementation of one handler operation clause.

It receives the request, the delimited continuation, and the clause's runtime services, and returns the clause's answer, a :class:Forward, or a :class:TailResume.

ResponseHook

ResponseHook = Callable[[object], object]

A callback applied to the reply a forwarded request receives.

It returns the value the resumed computation receives in place of the reply.

ReturnClause

ReturnClause = Callable[[object, 'ClauseContext'], object]

A host implementation of a handler's return clause.

It receives the handled computation's final value and the clause's runtime services, and returns the handler's answer.

RuntimeValidator

RuntimeValidator = Callable[[object], bool | None]

A predicate a host value must pass to inhabit a QIEC type.

It returns False to reject the value; True or None accepts it.

EqualityEvidence

EqualityEvidence = Reflexivity | BranchGiven

Kernel-generated evidence of a static equality: reflexivity or a branch's given.

RuntimeProviderFactory

RuntimeProviderFactory = Callable[[Mapping[str, object]], RuntimeProvider]

Build a runtime provider from the options its configuration entry records.

TraceObserver

TraceObserver = Callable[['ExecutionTraceEvent'], None]

A callback receiving each event an execution emits, in order.

ParameterConstraint

ParameterConstraint = str

A symbolic constraint on a parameter value, such as "positive", "unit_interval", "simplex", "positive_definite", or "dependent" when the admissible values depend on other parameters. A compositional family's parameters use "sampleable" for a distribution it is built from and "transform" for a named bijector chain; an atom's "numeric" value is an integer or a real, and fixes which the atom samples.

SupportKind

SupportKind = str

A symbolic description of where samples live, in the same vocabulary as the parameter constraints.

AddressRelation

AddressRelation = Literal['preserve', 'split', 'duplicate', 'eliminate']

How an effect request relates to its source site.

"preserve" marks the request as written; "split" and "duplicate" mark a request a handler derived from it; "eliminate" marks one a handler discharged.

IndexSort

The sort of an index binder: naturals, shapes, contexts, or a declared sort.

Kind

The kind of a static binder: a type, an effect, a row, or an arrow between kinds.

Telescope

Telescope = tuple[TelescopeBinder, ...]

The static binders a declaration takes, in order; later binders may mention earlier ones.

TelescopeBinder

TelescopeBinder = TypeBinder | IndexBinder | EffectBinder

One binder of a declaration telescope: a type, an index, or an effect binder.

PrimitiveCapability

PrimitiveCapability = Literal['arithmetic', 'comparison', 'boolean', 'string', 'conversion', 'math', 'special', 'activation', 'weight']

The feature a target must support to render a primitive.

ParameterRole

ParameterRole = Literal['domain', 'scalar', 'data', 'observation', 'fibration', 'weight', 'bias', 'table', 'kernel-input']

How a program parameter is supplied.

domain parameters carry the value the program is applied to, one per real factor of its domain; scalar ones are its declared parameters; data ones are free names its steps read; observation ones are the values observe steps score; fibration ones map observation rows to groups; weight and bias are the numbers of a declared morphism's affine parameter map and table those of a morphism over a finite domain, one row per element; kernel-input holds a kernel's input locations.

SiteKind

SiteKind = Literal['sample', 'observe', 'marginal']

What a program step does at its site.

JsonValue

JsonValue = None | bool | int | float | str | list[JsonValue] | dict[str, JsonValue]

A JSON document as json.loads returns it and json.dumps accepts it.

Computation

Computation = Return | Bind | Perform | Handle | Case | If | Call | Resume | NewInstance

A computation term: every term that may perform effects or control flow.

LiteralData

LiteralData = None | bool | int | float | str | bytes | tuple[LiteralData, ...]

The host data a literal carries: a scalar, bytes, or a tuple of literal data.

ReductionOperator

ReductionOperator = Literal['sum', 'mean', 'max', 'min', 'logsumexp', 'prod']

The reductions of a whole tensor to one number.

RowwiseOperator

RowwiseOperator = Literal['softmax', 'log_softmax', 'cumsum', 'sort', 'normalize']

The operations along a tensor's last axis that keep its shape.

IndexTerm

A static index: a variable, a literal, a constructor application, or a shape.

StaticArgument

StaticArgument = TypeExpr | IndexTerm | EffectRef | EffectVariable

Anything a telescope binder may be instantiated at: a type, an index, or an effect.

TypeExpr

A type: a variable, a constructor application, a function type, or an equality.

Aborted dataclass

Aborted(error: object)

The default answer of an abort handler, wrapping the abort payload.

PARAMETER DESCRIPTION
error

The payload the computation aborted with.

TYPE: object

ExtraValuePolicy

Bases: str, Enum

What a handler does with a supplied value no sample site consumed.

IGNORE discards it; ERROR rejects the run when the handled computation returns.

MissingValuePolicy

Bases: str, Enum

What a condition handler does with a sample site it has no observation for.

FORWARD passes the request to an outer handler; ERROR rejects it.

ReplayPolicy

Bases: str, Enum

Whether a replayed value is scored as well as clamped.

CLAMP_AND_SCORE sends the replayed value's log density to a Score instance; CLAMP_NO_SCORE and INTERVENE replay it without scoring.

ScoreAccumulator dataclass

ScoreAccumulator(total: object, contributions: list[object] = list())

Mutable result owned by one installed Score handler.

PARAMETER DESCRIPTION
total

The running combination of every contribution so far, starting at the handler's identity.

TYPE: object

contributions

Every contribution in the order it was added.

TYPE: list[object] DEFAULT: list()

StateCell dataclass

StateCell(value: object)

The mutable cell owned by one installed State handler.

PARAMETER DESCRIPTION
value

The current state.

TYPE: object

TraceEvent dataclass

TraceEvent(address: tuple[str, tuple[tuple[str, str | int], ...], tuple[int, ...]], instance: EffectInstanceId, operation: OperationId, arguments: tuple[object, ...], result: object, mode: str)

One recorded request and the value produced for it.

PARAMETER DESCRIPTION
address

The request's dynamic key: its static site, address frames, and resumption path.

TYPE: tuple[str, tuple[tuple[str, str | int], ...], tuple[int, ...]]

instance

The effect instance the request was performed on.

TYPE: EffectInstanceId

operation

The operation requested.

TYPE: OperationId

arguments

The evaluated value arguments.

TYPE: tuple[object, ...]

result

The value produced for the request.

TYPE: object

mode

How the value was produced, such as "forwarded".

TYPE: str

TraceRecorder dataclass

TraceRecorder(events: list[TraceEvent] = list())

Ordered trace; repeated dynamic addresses remain observable as events.

PARAMETER DESCRIPTION
events

The recorded events, in order; empty to start.

TYPE: list[TraceEvent] DEFAULT: list()

record

record(request: RuntimeRequest, result: object, *, mode: str) -> object

Record one request and the value produced for it.

PARAMETER DESCRIPTION
request

The request being answered and its arguments.

TYPE: RuntimeRequest

result

The value produced for the request.

TYPE: object

mode

How the value was produced.

TYPE: str

RETURNS DESCRIPTION
object

The value, unchanged, after recording. Returned so the caller can use this in place of the value it passed.

Source code in src/quivers/qiec/builtins.py
def record(self, request: RuntimeRequest, result: object, *, mode: str) -> object:
    """Record one request and the value produced for it.

    Parameters
    ----------
    request
        The request being answered and its arguments.
    result
        The value produced for the request.
    mode
        How the value was produced.

    Returns
    -------
    object
        The value, unchanged, after recording. Returned so the caller can
        use this in place of the value it passed.
    """
    self.events.append(
        TraceEvent(
            request.address,
            request.instance,
            request.operation,
            request.arguments,
            result,
            mode,
        )
    )
    return result

WeightAccumulator dataclass

WeightAccumulator(total: object, contributions: list[object] = list())

Mutable result owned by one installed Weight handler.

PARAMETER DESCRIPTION
total

The running combination of every contribution so far, starting at the handler's identity.

TYPE: object

contributions

Every contribution in the order it was added.

TYPE: list[object] DEFAULT: list()

CheckContext dataclass

CheckContext(locals: tuple[Local, ...] = (), givens: tuple[BranchGiven, ...] = (), static_scopes: tuple[StaticScopeId, ...] = (), static_variables: tuple[StaticVariableId, ...] = (), resumption: ResumptionType | None = None)

What is in scope at one point in a term being checked.

PARAMETER DESCRIPTION
locals

Value bindings, innermost last.

TYPE: tuple[Local, ...] DEFAULT: ()

givens

Equality evidence introduced by enclosing case branches, which is what makes an index refinement usable in the branch body.

TYPE: tuple[BranchGiven, ...] DEFAULT: ()

static_scopes

Static scopes already entered. Tracked so a branch cannot reuse an enclosing scope's identity and silently capture its variables.

TYPE: tuple[StaticScopeId, ...] DEFAULT: ()

static_variables

Rigid variables currently active, tracked for the same reason.

TYPE: tuple[StaticVariableId, ...] DEFAULT: ()

resumption class-attribute instance-attribute

resumption: ResumptionType | None = None

The continuation, when checking a handler clause body.

None everywhere else, which is what makes resume outside a clause a checker error rather than a term with no meaning.

extend

extend(local: Local) -> CheckContext

Return this context with one more value binding.

PARAMETER DESCRIPTION
local

The binding to add.

TYPE: Local

RETURNS DESCRIPTION
CheckContext

A context carrying the new binding innermost.

RAISES DESCRIPTION
KernelError

If the name is already bound. Rebinding is rejected rather than shadowed, so a name denotes one value throughout a body.

Source code in src/quivers/qiec/checking.py
def extend(self, local: Local) -> CheckContext:
    """Return this context with one more value binding.

    Parameters
    ----------
    local : Local
        The binding to add.

    Returns
    -------
    CheckContext
        A context carrying the new binding innermost.

    Raises
    ------
    KernelError
        If the name is already bound. Rebinding is rejected rather
        than shadowed, so a name denotes one value throughout a body.
    """
    if any(existing.name == local.name for existing in self.locals):
        raise KernelError(f"local already bound: {local.name!r}")
    return CheckContext(
        (*self.locals, local),
        self.givens,
        self.static_scopes,
        self.static_variables,
        self.resumption,
    )

with_givens

with_givens(givens: tuple[BranchGiven, ...]) -> CheckContext

Return this context with further equality evidence in scope.

PARAMETER DESCRIPTION
givens

Evidence a case branch introduces by matching a constructor.

TYPE: tuple[BranchGiven, ...]

RETURNS DESCRIPTION
CheckContext

A context in which that evidence may be appealed to.

Source code in src/quivers/qiec/checking.py
def with_givens(self, givens: tuple[BranchGiven, ...]) -> CheckContext:
    """Return this context with further equality evidence in scope.

    Parameters
    ----------
    givens : tuple[BranchGiven, ...]
        Evidence a case branch introduces by matching a constructor.

    Returns
    -------
    CheckContext
        A context in which that evidence may be appealed to.
    """
    return CheckContext(
        self.locals,
        (*self.givens, *givens),
        self.static_scopes,
        self.static_variables,
        self.resumption,
    )

with_static_scope

with_static_scope(scope: StaticScopeId, variables: tuple[StaticVariableId, ...] = ()) -> CheckContext

Return this context inside a fresh static scope.

PARAMETER DESCRIPTION
scope

Identity of the scope being entered.

TYPE: StaticScopeId

variables

Rigid variables the scope introduces.

TYPE: tuple[StaticVariableId, ...] DEFAULT: ()

RETURNS DESCRIPTION
CheckContext

A context recording the scope and its variables as active.

RAISES DESCRIPTION
KernelError

If the scope or one of its variables is already active. Reuse would let a branch's rigid variable be confused with an enclosing one, which is what keeps branch refinements from leaking out of the branch.

Source code in src/quivers/qiec/checking.py
def with_static_scope(
    self,
    scope: StaticScopeId,
    variables: tuple[StaticVariableId, ...] = (),
) -> CheckContext:
    """Return this context inside a fresh static scope.

    Parameters
    ----------
    scope : StaticScopeId
        Identity of the scope being entered.
    variables : tuple[StaticVariableId, ...]
        Rigid variables the scope introduces.

    Returns
    -------
    CheckContext
        A context recording the scope and its variables as active.

    Raises
    ------
    KernelError
        If the scope or one of its variables is already active. Reuse
        would let a branch's rigid variable be confused with an
        enclosing one, which is what keeps branch refinements from
        leaking out of the branch.
    """
    if scope in self.static_scopes:
        raise KernelError("a case branch reused an enclosing static scope")
    if set(variables) & set(self.static_variables):
        raise KernelError("a case branch reused an active static variable")
    return CheckContext(
        self.locals,
        self.givens,
        (*self.static_scopes, scope),
        (*self.static_variables, *variables),
        self.resumption,
    )

with_resumption

with_resumption(resumption: ResumptionType) -> CheckContext

Return this context inside a handler clause body.

PARAMETER DESCRIPTION
resumption

The continuation the clause body may invoke.

TYPE: ResumptionType

RETURNS DESCRIPTION
CheckContext

A context in which resume is available. A clause body nested inside another handler's clause replaces the outer resumption rather than stacking, since resume names the innermost clause's continuation and there is no syntax for reaching past it.

Source code in src/quivers/qiec/checking.py
def with_resumption(self, resumption: ResumptionType) -> CheckContext:
    """Return this context inside a handler clause body.

    Parameters
    ----------
    resumption : ResumptionType
        The continuation the clause body may invoke.

    Returns
    -------
    CheckContext
        A context in which `resume` is available. A clause body
        nested inside another handler's clause replaces the outer
        resumption rather than stacking, since `resume` names the
        innermost clause's continuation and there is no syntax for
        reaching past it.
    """
    return CheckContext(
        self.locals,
        self.givens,
        self.static_scopes,
        self.static_variables,
        resumption,
    )

local_type

local_type(name: str) -> TypeExpr | None

The type of a value binding in scope.

PARAMETER DESCRIPTION
name

The bound name to resolve.

TYPE: str

RETURNS DESCRIPTION
TypeExpr or None

The binding's type, or None when the name is not bound here. The innermost binding wins, though extend rejects rebinding, so at most one can match.

Source code in src/quivers/qiec/checking.py
def local_type(self, name: str) -> TypeExpr | None:
    """The type of a value binding in scope.

    Parameters
    ----------
    name : str
        The bound name to resolve.

    Returns
    -------
    TypeExpr or None
        The binding's type, or None when the name is not bound here.
        The innermost binding wins, though `extend` rejects rebinding,
        so at most one can match.
    """
    return next(
        (local.type for local in reversed(self.locals) if local.name == name),
        None,
    )

given

given(equality: EqualityId) -> BranchGiven | None

The equality evidence with a given identity, if in scope.

PARAMETER DESCRIPTION
equality

The identity to resolve.

TYPE: EqualityId

RETURNS DESCRIPTION
BranchGiven or None

The evidence, or None when it is not in scope, which is how an appeal to a refinement outside its branch is caught.

Source code in src/quivers/qiec/checking.py
def given(self, equality: EqualityId) -> BranchGiven | None:
    """The equality evidence with a given identity, if in scope.

    Parameters
    ----------
    equality : EqualityId
        The identity to resolve.

    Returns
    -------
    BranchGiven or None
        The evidence, or None when it is not in scope, which is how
        an appeal to a refinement outside its branch is caught.
    """
    return next((given for given in self.givens if given.id == equality), None)

ComputationSignature dataclass

ComputationSignature(id: ComputationId, name: str, telescope: Telescope, parameters: tuple[TypeExpr, ...], result: TypeExpr, effects: EffectRow)

What a call needs to know about a computation it invokes.

A signature rather than a declaration, and deliberately so: a call resolves against this, not against the callee's body. That is what lets every signature be collected before any body is checked, which in turn makes a forward call and mutual recursion ordinary rather than requiring a forward declaration.

PARAMETER DESCRIPTION
id

The declaration's stable identity, which the call carries.

TYPE: ComputationId

name

The authored name, used in diagnostics.

TYPE: str

telescope

Static binders the computation takes.

TYPE: Telescope

parameters

Value parameter types, in order, before instantiation.

TYPE: tuple[TypeExpr, ...]

result

The result type before instantiation.

TYPE: TypeExpr

effects

The row the computation performs, before instantiation.

TYPE: EffectRow

KernelError

KernelError(message: str, code: str = 'qiec-kind')

Bases: TypeError

A static QIEC kernel rejection.

Carries a stable diagnostic code alongside its message. The code is attached where the rejection is raised, because only that site knows which condition fired; classifying by message text afterwards is a guess that silently changes whenever the prose is reworded.

PARAMETER DESCRIPTION
message

What was rejected, in prose, for a human reader.

TYPE: str

code

A member of the published code set. Defaults to "qiec-kind", which is where a rejection with no more specific classification belongs.

TYPE: str DEFAULT: 'qiec-kind'

Source code in src/quivers/qiec/checking.py
def __init__(self, message: str, code: str = "qiec-kind") -> None:
    super().__init__(message)
    self.message = message
    self.code = code

KernelRegistry dataclass

KernelRegistry(families: dict[FamilyId, FamilyDecl] = dict(), constructors: dict[ConstructorId, ConstructorDecl] = dict(), effects: dict[EffectId, EffectDef] = dict(), operations: dict[OperationId, tuple[EffectDef, OperationDef]] = dict(), handlers: dict[HandlerId, HandlerDef] = dict(), type_constructors: dict[TypeId, TypeConstructorRef] = dict(), computations: dict[ComputationId, ComputationSignature] = dict(), instance_names: dict[EffectInstanceId, str] = dict())

Resolved declarations used by the reference checker.

Every table is keyed by stable identity, so a lookup never depends on a display name.

PARAMETER DESCRIPTION
families

Indexed data families by identity.

TYPE: dict[FamilyId, FamilyDecl] DEFAULT: dict()

constructors

Family constructors by identity.

TYPE: dict[ConstructorId, ConstructorDecl] DEFAULT: dict()

effects

Effect interfaces by identity.

TYPE: dict[EffectId, EffectDef] DEFAULT: dict()

operations

Each operation by identity, paired with the interface declaring it.

TYPE: dict[OperationId, tuple[EffectDef, OperationDef]] DEFAULT: dict()

handlers

Handler declarations by identity.

TYPE: dict[HandlerId, HandlerDef] DEFAULT: dict()

type_constructors

Type constructors by identity, recording the telescope each is known with.

TYPE: dict[TypeId, TypeConstructorRef] DEFAULT: dict()

computations

Named computation signatures by identity.

TYPE: dict[ComputationId, ComputationSignature] DEFAULT: dict()

instance_names

The source name of each lexical effect instance, for rendering rows in diagnostics.

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

validate_static

validate_static(term: StaticArgument, scope: StaticScope | None = None) -> None

Validate a static term against every declaration known here.

PARAMETER DESCRIPTION
term

The static term to check.

TYPE: StaticArgument

scope

Binders in scope at the term's position. None means the empty scope, so any variable reference is out of scope.

TYPE: StaticScope or None DEFAULT: None

RAISES DESCRIPTION
KernelError

If the term is ill-kinded, references an unbound static variable, or applies a known declaration wrongly.

Source code in src/quivers/qiec/checking.py
def validate_static(
    self,
    term: StaticArgument,
    scope: StaticScope | None = None,
) -> None:
    """Validate a static term against every declaration known here.

    Parameters
    ----------
    term : StaticArgument
        The static term to check.
    scope : StaticScope or None
        Binders in scope at the term's position. None means the empty
        scope, so any variable reference is out of scope.

    Raises
    ------
    KernelError
        If the term is ill-kinded, references an unbound static
        variable, or applies a known declaration wrongly.
    """
    check_static(term, scope, registry=self)
    self._record_type_constructors(term)

validate_type

validate_type(type_: TypeExpr, scope: StaticScope | None = None) -> None

Validate a value type against every declaration known here.

PARAMETER DESCRIPTION
type_

The value type to check.

TYPE: TypeExpr

scope

Binders in scope at the type's position. None means the empty scope.

TYPE: StaticScope or None DEFAULT: None

RAISES DESCRIPTION
KernelError

If the type is ill-kinded or references an unbound variable.

Source code in src/quivers/qiec/checking.py
def validate_type(
    self,
    type_: TypeExpr,
    scope: StaticScope | None = None,
) -> None:
    """Validate a value type against every declaration known here.

    Parameters
    ----------
    type_ : TypeExpr
        The value type to check.
    scope : StaticScope or None
        Binders in scope at the type's position. None means the empty
        scope.

    Raises
    ------
    KernelError
        If the type is ill-kinded or references an unbound variable.
    """
    check_type(type_, scope, registry=self)
    self._record_type_constructors(type_)

validate_effect_row

validate_effect_row(row: EffectRow) -> None

Validate all concrete interface applications in an effect row.

PARAMETER DESCRIPTION
row

The row whose entries to check. An open tail carries no application and so is not checked here.

TYPE: EffectRow

RAISES DESCRIPTION
KernelError

If an entry applies its interface wrongly.

Source code in src/quivers/qiec/checking.py
def validate_effect_row(self, row: EffectRow) -> None:
    """Validate all concrete interface applications in an effect row.

    Parameters
    ----------
    row : EffectRow
        The row whose entries to check. An open tail carries no
        application and so is not checked here.

    Raises
    ------
    KernelError
        If an entry applies its interface wrongly.
    """
    for entry in row.entries:
        self.validate_static(entry.effect)

validate_computation_type

validate_computation_type(type_: ComputationType) -> None

Validate both strata of a computation type.

PARAMETER DESCRIPTION
type_

The computation type whose effect row and result type to check.

TYPE: ComputationType

RAISES DESCRIPTION
KernelError

If either stratum is ill-formed.

Source code in src/quivers/qiec/checking.py
def validate_computation_type(self, type_: ComputationType) -> None:
    """Validate both strata of a computation type.

    Parameters
    ----------
    type_ : ComputationType
        The computation type whose effect row and result type to
        check.

    Raises
    ------
    KernelError
        If either stratum is ill-formed.
    """
    self.validate_effect_row(type_.effects)
    self.validate_type(type_.result)

register_family

register_family(family: FamilyDecl) -> None

Record an indexed family declaration.

PARAMETER DESCRIPTION
family

The family to register, with its uniform parameters and its refinable indices.

TYPE: FamilyDecl

RAISES DESCRIPTION
KernelError

If a family with this identity is already registered, if two of its binders share a name, or if its type constructor disagrees with one already recorded.

Source code in src/quivers/qiec/checking.py
def register_family(self, family: FamilyDecl) -> None:
    """Record an indexed family declaration.

    Parameters
    ----------
    family : FamilyDecl
        The family to register, with its uniform parameters and its
        refinable indices.

    Raises
    ------
    KernelError
        If a family with this identity is already registered, if two
        of its binders share a name, or if its type constructor
        disagrees with one already recorded.
    """
    if family.id in self.families:
        raise KernelError(f"family already registered: {family.name!r}")
    StaticScope().extend(
        (*family.parameters, *family.indices),
        subject=f"family {family.name!r}",
    )
    self._record_type_constructor(family.type_constructor)
    self.families[family.id] = family

register_constructor

register_constructor(constructor: ConstructorDecl) -> None

Record a constructor against the family that declares it.

PARAMETER DESCRIPTION
constructor

The constructor to register. Its family must already be registered and must list this constructor.

TYPE: ConstructorDecl

RAISES DESCRIPTION
KernelError

If the constructor is already registered, if its family is unknown or does not declare it, if it returns the wrong number of result indices, if its telescope shadows a family binder, or if a field type or result index is ill-formed.

Source code in src/quivers/qiec/checking.py
def register_constructor(self, constructor: ConstructorDecl) -> None:
    """Record a constructor against the family that declares it.

    Parameters
    ----------
    constructor : ConstructorDecl
        The constructor to register. Its family must already be
        registered and must list this constructor.

    Raises
    ------
    KernelError
        If the constructor is already registered, if its family is
        unknown or does not declare it, if it returns the wrong
        number of result indices, if its telescope shadows a family
        binder, or if a field type or result index is ill-formed.
    """
    if constructor.id in self.constructors:
        raise KernelError(f"constructor already registered: {constructor.name!r}")
    family = self.families.get(constructor.family)
    if family is None:
        raise KernelError(f"unknown family for constructor {constructor.name!r}")
    if constructor.id not in family.constructors:
        raise KernelError(
            f"constructor {constructor.name!r} is not declared by family {family.name!r}"
        )
    if len(constructor.result_indices) != len(family.indices):
        raise KernelError(
            f"constructor {constructor.name!r} returns "
            f"{len(constructor.result_indices)} indices; expected {len(family.indices)}"
        )
    family_names = {binder.name for binder in (*family.parameters, *family.indices)}
    constructor_names = {binder.name for binder in constructor.telescope}
    shadowed = family_names & constructor_names
    if shadowed:
        raise KernelError(
            f"constructor {constructor.name!r} shadows family binders: {shadowed!r}"
        )
    scope = (
        StaticScope()
        .extend(
            family.parameters,
            subject=f"family parameters of {family.name!r}",
        )
        .extend(
            constructor.telescope,
            subject=f"constructor {constructor.name!r}",
        )
    )
    for field_definition in constructor.fields:
        self.validate_type(field_definition.type, scope)
    for result, binder in zip(
        constructor.result_indices,
        family.indices,
        strict=True,
    ):
        self.validate_static(result, scope)
        _check_binder_argument(
            binder,
            result,
            subject=f"result index of constructor {constructor.name!r}",
        )
    self.constructors[constructor.id] = constructor

register_effect

register_effect(effect: EffectDef) -> None

Record an effect interface and index its operations.

Each operation's signature is checked in a scope carrying the interface binders and then its own, and may mention the interface being declared.

PARAMETER DESCRIPTION
effect

The interface to register.

TYPE: EffectDef

RAISES DESCRIPTION
KernelError

If the interface or one of its operations is already registered, if a binder is shadowed, or if an argument or result type is ill-formed.

Source code in src/quivers/qiec/checking.py
def register_effect(self, effect: EffectDef) -> None:
    """Record an effect interface and index its operations.

    Each operation's signature is checked in a scope carrying the
    interface binders and then its own, and may mention the interface
    being declared.

    Parameters
    ----------
    effect : EffectDef
        The interface to register.

    Raises
    ------
    KernelError
        If the interface or one of its operations is already
        registered, if a binder is shadowed, or if an argument or
        result type is ill-formed.
    """
    if effect.ref.id in self.effects:
        raise KernelError(f"effect already registered: {effect.ref.name!r}")
    interface_scope = StaticScope().extend(
        effect.telescope,
        subject=f"effect {effect.ref.name!r}",
    )
    for operation in effect.operations:
        if operation.id in self.operations:
            raise KernelError(f"operation already registered: {operation.name!r}")
        operation_scope = interface_scope.extend(
            operation.telescope,
            subject=f"operation {operation.name!r}",
        )
        for argument in operation.arguments:
            self.validate_type(argument.type, operation_scope)
            self._validate_effect_references(argument.type, (effect,))
        self.validate_type(operation.result_type, operation_scope)
        self._validate_effect_references(operation.result_type, (effect,))
    self.effects[effect.ref.id] = effect
    for operation in effect.operations:
        self.operations[operation.id] = (effect, operation)

register_handler

register_handler(handler: HandlerDef) -> None

Record a handler against the interface it handles.

Coverage is checked here rather than at construction, because it is a claim about the interface's operations and only the registry knows those.

PARAMETER DESCRIPTION
handler

The handler signature to register.

TYPE: HandlerDef

RAISES DESCRIPTION
KernelError

If the handler is already registered, if its telescope shadows a binder, if its input, output, or introduced row is ill-formed, if the interface it names is unknown or wrongly applied, if a clause covers an operation the interface does not declare, or if the handler claims totality while leaving an operation uncovered, or if an authored clause body fails to check, answers the wrong type, or resumes more often than its grade allows.

Source code in src/quivers/qiec/checking.py
def register_handler(self, handler: HandlerDef) -> None:
    """Record a handler against the interface it handles.

    Coverage is checked here rather than at construction, because it
    is a claim about the interface's operations and only the registry
    knows those.

    Parameters
    ----------
    handler : HandlerDef
        The handler signature to register.

    Raises
    ------
    KernelError
        If the handler is already registered, if its telescope
        shadows a binder, if its input, output, or introduced row is
        ill-formed, if the interface it names is unknown or wrongly
        applied, if a clause covers an operation the interface does
        not declare, or if the handler claims totality while leaving
        an operation uncovered, or if an authored clause body fails
        to check, answers the wrong type, or resumes more often than
        its grade allows.
    """
    if handler.id in self.handlers:
        raise KernelError(f"handler already registered: {handler.name!r}")
    scope = StaticScope().extend(
        handler.telescope,
        subject=f"handler {handler.name!r}",
    )
    self.validate_static(handler.effect, scope)
    self.validate_type(handler.input_type, scope)
    self.validate_type(handler.output_type, scope)
    for entry in handler.introduced.entries:
        self.validate_static(entry.effect, scope)
        introduced = self.effects.get(entry.effect.id)
        if introduced is None or not introduced.matches(entry.effect):
            raise KernelError(
                f"handler {handler.name!r} introduces an unknown effect interface"
            )
    effect = self.effects.get(handler.effect.id)
    if effect is None or not effect.matches(handler.effect):
        raise KernelError(f"unknown effect interface for handler {handler.name!r}")
    declared = {operation.id for operation in effect.operations}
    clauses = {clause.operation for clause in handler.clauses}
    unknown = clauses - declared
    if unknown:
        raise KernelError(
            f"handler has clauses for unknown operations: {unknown!r}"
        )
    if handler.total and clauses != declared:
        missing = declared - clauses
        raise KernelError(f"total handler is missing operations: {missing!r}")
    self._check_handler_bodies(
        handler,
        effect,
        substitution=instantiate_telescope(
            effect.telescope, handler.effect.arguments
        ),
    )
    self.handlers[handler.id] = handler

register_computation

register_computation(signature: ComputationSignature) -> None

Record a computation's signature so calls can resolve to it.

Signatures are registered before any body is checked. A call therefore resolves whether the callee is declared above or below it, and mutual recursion needs no forward declaration.

PARAMETER DESCRIPTION
signature

The signature to record.

TYPE: ComputationSignature

RAISES DESCRIPTION
KernelError

If a computation with this identity is already registered, if its telescope shadows a binder, or if a parameter type, the result type, or the declared row is ill-formed. A duplicate identity is rejected rather than overwritten, since a call carrying that identity would otherwise resolve to whichever declaration happened to register last.

Source code in src/quivers/qiec/checking.py
def register_computation(self, signature: ComputationSignature) -> None:
    """Record a computation's signature so calls can resolve to it.

    Signatures are registered before any body is checked. A call
    therefore resolves whether the callee is declared above or below
    it, and mutual recursion needs no forward declaration.

    Parameters
    ----------
    signature : ComputationSignature
        The signature to record.

    Raises
    ------
    KernelError
        If a computation with this identity is already registered, if
        its telescope shadows a binder, or if a parameter type, the
        result type, or the declared row is ill-formed. A duplicate
        identity is rejected rather than overwritten, since a call
        carrying that identity would otherwise resolve to whichever
        declaration happened to register last.
    """
    if signature.id in self.computations:
        raise KernelError(f"computation already registered: {signature.name!r}")
    scope = StaticScope().extend(
        signature.telescope,
        subject=f"computation {signature.name!r}",
    )
    for parameter in signature.parameters:
        self.validate_type(parameter, scope)
    self.validate_type(signature.result, scope)
    self.validate_effect_row(signature.effects)
    self.computations[signature.id] = signature

computation

computation(computation: ComputationId) -> ComputationSignature

The registered signature with a given identity.

PARAMETER DESCRIPTION
computation

The identity to resolve.

TYPE: ComputationId

RETURNS DESCRIPTION
ComputationSignature

The registered signature.

RAISES DESCRIPTION
KernelError

If no computation is registered under that identity, which is how a call to an undeclared name is caught.

Source code in src/quivers/qiec/checking.py
def computation(self, computation: ComputationId) -> ComputationSignature:
    """The registered signature with a given identity.

    Parameters
    ----------
    computation : ComputationId
        The identity to resolve.

    Returns
    -------
    ComputationSignature
        The registered signature.

    Raises
    ------
    KernelError
        If no computation is registered under that identity, which is
        how a call to an undeclared name is caught.
    """
    try:
        return self.computations[computation]
    except KeyError as exc:
        raise KernelError(
            f"unknown computation {computation}", "qiec-call"
        ) from exc

constructor

constructor(constructor: ConstructorId) -> ConstructorDecl

The registered constructor with a given identity.

PARAMETER DESCRIPTION
constructor

The identity to resolve.

TYPE: ConstructorId

RETURNS DESCRIPTION
ConstructorDecl

The registered declaration.

RAISES DESCRIPTION
KernelError

If no constructor is registered under that identity. The underlying KeyError is wrapped so callers handle one kernel error class rather than two.

Source code in src/quivers/qiec/checking.py
def constructor(self, constructor: ConstructorId) -> ConstructorDecl:
    """The registered constructor with a given identity.

    Parameters
    ----------
    constructor : ConstructorId
        The identity to resolve.

    Returns
    -------
    ConstructorDecl
        The registered declaration.

    Raises
    ------
    KernelError
        If no constructor is registered under that identity. The
        underlying `KeyError` is wrapped so callers handle one
        kernel error class rather than two.
    """
    try:
        return self.constructors[constructor]
    except KeyError as exc:
        raise KernelError(f"unknown constructor {constructor}") from exc

handler

handler(handler: HandlerId) -> HandlerDef

The registered handler with a given identity.

PARAMETER DESCRIPTION
handler

The identity to resolve.

TYPE: HandlerId

RETURNS DESCRIPTION
HandlerDef

The registered signature.

RAISES DESCRIPTION
KernelError

If no handler is registered under that identity.

Source code in src/quivers/qiec/checking.py
def handler(self, handler: HandlerId) -> HandlerDef:
    """The registered handler with a given identity.

    Parameters
    ----------
    handler : HandlerId
        The identity to resolve.

    Returns
    -------
    HandlerDef
        The registered signature.

    Raises
    ------
    KernelError
        If no handler is registered under that identity.
    """
    try:
        return self.handlers[handler]
    except KeyError as exc:
        raise KernelError(f"unknown handler {handler}") from exc

operation

operation(operation: OperationId) -> tuple[EffectDef, OperationDef]

The operation with a given identity, and its owning interface.

PARAMETER DESCRIPTION
operation

The identity to resolve.

TYPE: OperationId

RETURNS DESCRIPTION
tuple[EffectDef, OperationDef]

The interface that declares the operation, and the operation itself. The interface comes back too because checking a request needs both.

RAISES DESCRIPTION
KernelError

If no operation is registered under that identity.

Source code in src/quivers/qiec/checking.py
def operation(self, operation: OperationId) -> tuple[EffectDef, OperationDef]:
    """The operation with a given identity, and its owning interface.

    Parameters
    ----------
    operation : OperationId
        The identity to resolve.

    Returns
    -------
    tuple[EffectDef, OperationDef]
        The interface that declares the operation, and the operation
        itself. The interface comes back too because checking a
        request needs both.

    Raises
    ------
    KernelError
        If no operation is registered under that identity.
    """
    try:
        return self.operations[operation]
    except KeyError as exc:
        raise KernelError(f"unknown operation {operation}") from exc

family_for_type

family_for_type(type_: TypeApplication) -> FamilyDecl

The indexed family a type application is an instance of.

PARAMETER DESCRIPTION
type_

The applied type whose family to find.

TYPE: TypeApplication

RETURNS DESCRIPTION
FamilyDecl

The family whose type constructor the application names.

RAISES DESCRIPTION
KernelError

If the application is ill-formed, or names a constructor no registered family declares, which is how a case analysis on a non-family type is rejected.

Source code in src/quivers/qiec/checking.py
def family_for_type(self, type_: TypeApplication) -> FamilyDecl:
    """The indexed family a type application is an instance of.

    Parameters
    ----------
    type_ : TypeApplication
        The applied type whose family to find.

    Returns
    -------
    FamilyDecl
        The family whose type constructor the application names.

    Raises
    ------
    KernelError
        If the application is ill-formed, or names a constructor no
        registered family declares, which is how a case analysis on
        a non-family type is rejected.
    """
    self.validate_static(type_)
    for family in self.families.values():
        if family.type_constructor == type_.constructor:
            return family
    raise KernelError(f"unknown indexed family type {type_.constructor.name!r}")

ResumptionType dataclass

ResumptionType(result: TypeExpr, answer: TypeExpr, effects: EffectRow)

The continuation available inside one handler clause body.

A resumption is not a value and cannot be stored, so it is not a local binding. It is a capability of the position instead: available inside a clause body and nowhere else.

PARAMETER DESCRIPTION
result

What the resumed operation supplies, so a resume must carry a value of this type.

TYPE: TypeExpr

answer

What resuming produces, which is the clause's own answer type.

TYPE: TypeExpr

effects

The row resuming performs.

TYPE: EffectRow

StaticScope dataclass

StaticScope(types: tuple[tuple[str, Kind], ...] = (), indices: tuple[tuple[str, IndexSort], ...] = (), effects: tuple[str, ...] = ())

The three disjoint namespaces visible in a declaration body.

PARAMETER DESCRIPTION
types

Type binders in scope, each with its kind.

TYPE: tuple[tuple[str, Kind], ...] DEFAULT: ()

indices

Index binders in scope, each with its sort.

TYPE: tuple[tuple[str, IndexSort], ...] DEFAULT: ()

effects

Effect binder names in scope.

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

names property

names: frozenset[str]

Every static name bound here, across all three namespaces.

RETURNS DESCRIPTION
frozenset[str]

The union of the type, index, and effect binder names. The namespaces are disjoint by construction, so this is what shadowing is tested against.

extend

extend(telescope: Telescope, *, subject: str) -> StaticScope

Return this scope widened by a telescope's binders.

PARAMETER DESCRIPTION
telescope

Binders to add. Each must be a type, index, or effect binder.

TYPE: Telescope

subject

What is being extended, named in any diagnostic so the error points at the declaration rather than at the scope.

TYPE: str

RETURNS DESCRIPTION
StaticScope

A scope carrying the original binders and the new ones.

RAISES DESCRIPTION
KernelError

If a binder is of an unknown class, or if its name is already bound. Shadowing is rejected rather than resolved so a static name always denotes one thing within a declaration.

Source code in src/quivers/qiec/checking.py
def extend(self, telescope: Telescope, *, subject: str) -> StaticScope:
    """Return this scope widened by a telescope's binders.

    Parameters
    ----------
    telescope : Telescope
        Binders to add. Each must be a type, index, or effect binder.
    subject : str
        What is being extended, named in any diagnostic so the error
        points at the declaration rather than at the scope.

    Returns
    -------
    StaticScope
        A scope carrying the original binders and the new ones.

    Raises
    ------
    KernelError
        If a binder is of an unknown class, or if its name is already
        bound. Shadowing is rejected rather than resolved so a static
        name always denotes one thing within a declaration.
    """
    types = list(self.types)
    indices = list(self.indices)
    effects = list(self.effects)
    seen = set(self.names)
    for binder in telescope:
        name = getattr(binder, "name", None)
        if not isinstance(name, str):  # pragma: no cover - closed binder union
            raise KernelError(f"unknown binder in {subject}: {binder!r}")
        if name in seen:
            raise KernelError(f"static binder {name!r} is shadowed in {subject}")
        seen.add(name)
        if isinstance(binder, TypeBinder):
            types.append((binder.name, binder.kind))
        elif isinstance(binder, IndexBinder):
            indices.append((binder.name, binder.sort))
        elif isinstance(binder, EffectBinder):
            effects.append(binder.name)
        else:  # pragma: no cover - closed binder union
            raise KernelError(f"unknown binder in {subject}: {binder!r}")
    return StaticScope(tuple(types), tuple(indices), tuple(effects))

type_kind

type_kind(name: str) -> Kind | None

The kind of a type binder in scope.

PARAMETER DESCRIPTION
name

The binder name to resolve.

TYPE: str

RETURNS DESCRIPTION
Kind or None

The binder's kind, or None when name is not a type binder here. None does not mean unbound: the name may be an index or effect binder instead.

Source code in src/quivers/qiec/checking.py
def type_kind(self, name: str) -> Kind | None:
    """The kind of a type binder in scope.

    Parameters
    ----------
    name : str
        The binder name to resolve.

    Returns
    -------
    Kind or None
        The binder's kind, or None when `name` is not a type binder
        here. None does not mean unbound: the name may be an index or
        effect binder instead.
    """
    return next((kind for candidate, kind in self.types if candidate == name), None)

index_sort

index_sort(name: str) -> IndexSort | None

The sort of an index binder in scope.

PARAMETER DESCRIPTION
name

The binder name to resolve.

TYPE: str

RETURNS DESCRIPTION
IndexSort or None

The binder's sort, or None when name is not an index binder here.

Source code in src/quivers/qiec/checking.py
def index_sort(self, name: str) -> IndexSort | None:
    """The sort of an index binder in scope.

    Parameters
    ----------
    name : str
        The binder name to resolve.

    Returns
    -------
    IndexSort or None
        The binder's sort, or None when `name` is not an index binder
        here.
    """
    return next(
        (sort for candidate, sort in self.indices if candidate == name), None
    )

BranchPattern dataclass

BranchPattern(constructor: ConstructorId | None, guarded: bool = False)

The shape of one case branch, as coverage sees it.

PARAMETER DESCRIPTION
constructor

The constructor the branch matches, or None for a wildcard.

TYPE: ConstructorId | None

guarded

Whether the branch carries a guard that may reject a matching value, which keeps it from counting toward completeness.

TYPE: bool DEFAULT: False

BranchRefinement dataclass

BranchRefinement(skolems: tuple[str, ...], givens: tuple[BranchGiven, ...], reachability: Reachability = UNKNOWN)

Rigid skolems and local equalities available inside one branch.

PARAMETER DESCRIPTION
skolems

The names of the branch's rigid static variables.

TYPE: tuple[str, ...]

givens

The equalities the branch's constructor makes available.

TYPE: tuple[BranchGiven, ...]

reachability

Whether the givens are jointly satisfiable, as far as the unifier decides.

TYPE: Reachability DEFAULT: UNKNOWN

CoverageResult dataclass

CoverageResult(status: CoverageStatus, missing: tuple[ConstructorId, ...] = (), duplicates: tuple[ConstructorId, ...] = ())

The outcome of checking branch patterns against a family.

PARAMETER DESCRIPTION
status

Whether the patterns cover the family.

TYPE: CoverageStatus

missing

Constructors no pattern matches.

TYPE: tuple[ConstructorId, ...] DEFAULT: ()

duplicates

Constructors more than one pattern matches.

TYPE: tuple[ConstructorId, ...] DEFAULT: ()

CoverageStatus

Bases: str, Enum

Whether a set of branch patterns covers a family.

Reachability

Bases: str, Enum

Whether a branch can be entered, as far as its equalities decide.

ConstructorDecl dataclass

ConstructorDecl(id: ConstructorId, family: FamilyId, name: str, telescope: Telescope, fields: tuple[FieldDef, ...], result_indices: tuple[StaticArgument, ...])

One GADT constructor.

Constructor-local telescope binders are rigid skolems while a branch is checked. result_indices may mention those binders and thereby refine the scrutinee indices.

PARAMETER DESCRIPTION
id

Stable identity.

TYPE: ConstructorId

family

The family this constructor belongs to, which must list it.

TYPE: FamilyId

name

Source name.

TYPE: str

telescope

Binders local to the constructor. These become rigid skolems while a branch matching it is checked.

TYPE: Telescope

fields

The values the constructor carries, uniquely named.

TYPE: tuple[FieldDef, ...]

result_indices

What the constructor fixes the family's indices to. One per family index, and may mention the local telescope, which is how matching refines the scrutinee's type.

TYPE: tuple[StaticArgument, ...]

__post_init__

__post_init__() -> None

Reject a constructor whose telescope or fields are malformed.

RAISES DESCRIPTION
ValueError

If the telescope is malformed or two fields share a name.

Source code in src/quivers/qiec/declarations.py
def __post_init__(self) -> None:
    """Reject a constructor whose telescope or fields are malformed.

    Raises
    ------
    ValueError
        If the telescope is malformed or two fields share a name.
    """
    validate_telescope(self.telescope)
    names = [field.name for field in self.fields]
    if len(set(names)) != len(names):
        raise ValueError(f"duplicate field in constructor {self.name!r}")

FamilyDecl dataclass

FamilyDecl(id: FamilyId, name: str, parameters: Telescope, indices: Telescope, constructors: tuple[ConstructorId, ...], closed: bool = True)

A closed or opaque indexed data family.

parameters are uniform across every constructor. indices may be refined by constructor results and therefore are kept separate even when both are represented by index binders.

PARAMETER DESCRIPTION
id

Stable identity.

TYPE: FamilyId

name

Source name, used for display and for the derived type constructor.

TYPE: str

parameters

Binders uniform across every constructor.

TYPE: Telescope

indices

Binders a constructor result may refine. Each must be marked refinable, which is what separates them from parameters.

TYPE: Telescope

constructors

The constructors this family declares, without repetition.

TYPE: tuple[ConstructorId, ...]

closed

Whether the constructor list is exhaustive. A closed family admits coverage checking; an opaque one does not.

TYPE: bool DEFAULT: True

type_constructor property

type_constructor: TypeConstructorRef

The type constructor this family introduces.

RETURNS DESCRIPTION
TypeConstructorRef

A constructor whose identity is derived from the family's, taking the parameters and indices together as its telescope. Deriving rather than storing it keeps the two from drifting apart.

__post_init__

__post_init__() -> None

Reject a family whose binders or constructors are malformed.

RAISES DESCRIPTION
ValueError

If either telescope is malformed, a name is shared between a parameter and an index, an index is not marked refinable, or a constructor is listed twice.

Source code in src/quivers/qiec/declarations.py
def __post_init__(self) -> None:
    """Reject a family whose binders or constructors are malformed.

    Raises
    ------
    ValueError
        If either telescope is malformed, a name is shared between a
        parameter and an index, an index is not marked refinable, or
        a constructor is listed twice.
    """
    validate_telescope(self.parameters)
    validate_telescope(self.indices)
    names = [binder.name for binder in (*self.parameters, *self.indices)]
    if len(set(names)) != len(names):
        raise ValueError(f"duplicate binder in family {self.name!r}")
    if any(not binder.refinable for binder in self.indices):
        raise ValueError("family indices must be marked refinable")
    if len(set(self.constructors)) != len(self.constructors):
        raise ValueError(f"duplicate constructor in family {self.name!r}")

FieldDef dataclass

FieldDef(name: str, type: TypeExpr)

One named, typed field of a constructor.

PARAMETER DESCRIPTION
name

The field's display name.

TYPE: str

type

The field's type, which may mention the constructor's telescope.

TYPE: TypeExpr

DistributionBackend

Bases: Protocol

What a host must supply to sample and score a family.

sample

sample(family: str, arguments: Mapping[str, object], rng: Random) -> object

Draw one value.

PARAMETER DESCRIPTION
family

The family's source name.

TYPE: str

arguments

The named parameters as host values.

TYPE: Mapping[str, object]

rng

The generator to draw from.

TYPE: Random

RETURNS DESCRIPTION
object

The drawn host value.

Source code in src/quivers/qiec/distributions.py
def sample(
    self, family: str, arguments: Mapping[str, object], rng: random.Random
) -> object:
    """Draw one value.

    Parameters
    ----------
    family : str
        The family's source name.
    arguments : Mapping[str, object]
        The named parameters as host values.
    rng : random.Random
        The generator to draw from.

    Returns
    -------
    object
        The drawn host value.
    """
    ...

log_prob

log_prob(family: str, arguments: Mapping[str, object], value: object) -> float

Evaluate the log density at a value.

PARAMETER DESCRIPTION
family

The family's source name.

TYPE: str

arguments

The named parameters as host values.

TYPE: Mapping[str, object]

value

The point evaluated.

TYPE: object

RETURNS DESCRIPTION
float

The log density.

Source code in src/quivers/qiec/distributions.py
def log_prob(
    self, family: str, arguments: Mapping[str, object], value: object
) -> float:
    """Evaluate the log density at a value.

    Parameters
    ----------
    family : str
        The family's source name.
    arguments : Mapping[str, object]
        The named parameters as host values.
    value : object
        The point evaluated.

    Returns
    -------
    float
        The log density.
    """
    ...

log_mass

log_mass(family: str, arguments: Mapping[str, object]) -> float

The log of the total mass of a construction.

PARAMETER DESCRIPTION
family

The family's source name.

TYPE: str

arguments

The named parameters as host values.

TYPE: Mapping[str, object]

RETURNS DESCRIPTION
float

Zero for a probability measure; the log mass of a restriction or of a mixture of restrictions, which Normalize divides by.

Source code in src/quivers/qiec/distributions.py
def log_mass(self, family: str, arguments: Mapping[str, object]) -> float:
    """The log of the total mass of a construction.

    Parameters
    ----------
    family : str
        The family's source name.
    arguments : Mapping[str, object]
        The named parameters as host values.

    Returns
    -------
    float
        Zero for a probability measure; the log mass of a restriction
        or of a mixture of restrictions, which ``Normalize`` divides
        by.
    """
    ...

ReferenceBackend

Plain-Python sampling and scoring for every family of the registry.

The scalar core lives beside the backend and the structured and compositional families in :mod:quivers.qiec.reference_families; a family outside the tables is reported by name rather than approximated, so a host provider can be installed for it.

families property

families: frozenset[str]

The families this backend implements.

RETURNS DESCRIPTION
frozenset[str]

The family names.

sample

sample(family: str, arguments: Mapping[str, object], rng: Random) -> object

Draw one value from an implemented family.

PARAMETER DESCRIPTION
family

The family's source name.

TYPE: str

arguments

The named parameters as host values.

TYPE: Mapping[str, object]

rng

The generator to draw from.

TYPE: Random

RETURNS DESCRIPTION
object

The drawn value: a float, an int, a bool, or a tuple of floats.

RAISES DESCRIPTION
DistributionError

If the family is not implemented here or a parameter is missing or malformed.

Source code in src/quivers/qiec/distributions.py
def sample(
    self, family: str, arguments: Mapping[str, object], rng: random.Random
) -> object:
    """Draw one value from an implemented family.

    Parameters
    ----------
    family : str
        The family's source name.
    arguments : Mapping[str, object]
        The named parameters as host values.
    rng : random.Random
        The generator to draw from.

    Returns
    -------
    object
        The drawn value: a float, an int, a bool, or a tuple of floats.

    Raises
    ------
    DistributionError
        If the family is not implemented here or a parameter is
        missing or malformed.
    """
    sampler = _SAMPLERS.get(family)
    if sampler is None:
        raise DistributionError(
            f"the reference backend cannot sample {family}; install a "
            "distribution backend for it"
        )
    return sampler(arguments, rng)

log_prob

log_prob(family: str, arguments: Mapping[str, object], value: object) -> float

Evaluate the log density of an implemented family.

PARAMETER DESCRIPTION
family

The family's source name.

TYPE: str

arguments

The named parameters as host values.

TYPE: Mapping[str, object]

value

The point evaluated.

TYPE: object

RETURNS DESCRIPTION
float

The log density, -inf outside the support.

RAISES DESCRIPTION
DistributionError

If the family is not implemented here or a parameter or the point is malformed.

Source code in src/quivers/qiec/distributions.py
def log_prob(
    self, family: str, arguments: Mapping[str, object], value: object
) -> float:
    """Evaluate the log density of an implemented family.

    Parameters
    ----------
    family : str
        The family's source name.
    arguments : Mapping[str, object]
        The named parameters as host values.
    value : object
        The point evaluated.

    Returns
    -------
    float
        The log density, ``-inf`` outside the support.

    Raises
    ------
    DistributionError
        If the family is not implemented here or a parameter or the
        point is malformed.
    """
    density = _DENSITIES.get(family)
    if density is None:
        raise DistributionError(
            f"the reference backend cannot score {family}; install a "
            "distribution backend for it"
        )
    return density(arguments, value)

log_mass

log_mass(family: str, arguments: Mapping[str, object]) -> float

The log of the total mass of a construction.

PARAMETER DESCRIPTION
family

The family's source name.

TYPE: str

arguments

The named parameters as host values.

TYPE: Mapping[str, object]

RETURNS DESCRIPTION
float

The family's log mass, zero for every probability family.

RAISES DESCRIPTION
DistributionError

If the family is not implemented here.

Source code in src/quivers/qiec/distributions.py
def log_mass(self, family: str, arguments: Mapping[str, object]) -> float:
    """The log of the total mass of a construction.

    Parameters
    ----------
    family : str
        The family's source name.
    arguments : Mapping[str, object]
        The named parameters as host values.

    Returns
    -------
    float
        The family's log mass, zero for every probability family.

    Raises
    ------
    DistributionError
        If the family is not implemented here.
    """
    if family not in _DENSITIES:
        raise DistributionError(
            f"the reference backend cannot weigh {family}; install a "
            "distribution backend for it"
        )
    mass = _LOG_MASSES.get(family)
    return 0.0 if mass is None else mass(arguments)

RuntimeDistribution dataclass

RuntimeDistribution(family: str, arguments: Mapping[str, object], batch: tuple[int, ...] = (), event: tuple[int, ...] = (), ranks: Mapping[str, int] = MappingProxyType({}), natural: tuple[int, ...] = ())

A constructed distribution, as host data.

PARAMETER DESCRIPTION
family

The family's source name.

TYPE: str

arguments

The named parameters as host values; frozen on construction.

TYPE: Mapping[str, object]

batch

The plate's batch extents, outermost first; each position is an independent draw scored on its own.

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

event

The plate's extended event extents, outermost first; draws along them join one event and are scored together.

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

ranks

Each parameter's own tensor rank, from the family registry, so an argument carrying plate dimensions can be told from one that does not.

TYPE: Mapping[str, int] DEFAULT: MappingProxyType({})

natural

The family's own event extents, so a scalar given for a tensor parameter can be broadcast to the shape the family expects.

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

plated property

plated: bool

Whether the distribution is constructed over a plate.

RETURNS DESCRIPTION
bool

True when it has batch or extended event axes.

__post_init__

__post_init__() -> None

Freeze the arguments so a distribution value cannot drift.

Source code in src/quivers/qiec/distributions.py
def __post_init__(self) -> None:
    """Freeze the arguments so a distribution value cannot drift."""
    object.__setattr__(self, "arguments", MappingProxyType(dict(self.arguments)))
    object.__setattr__(self, "ranks", MappingProxyType(dict(self.ranks)))

at

at(position: tuple[int, ...]) -> RuntimeDistribution

The distribution at one position of the plate, unplated.

PARAMETER DESCRIPTION
position

A position of the leading plate axes, outermost first; fewer positions than axes leave the remaining axes in place.

TYPE: tuple[int, ...]

RETURNS DESCRIPTION
RuntimeDistribution

The family with its arguments selected at the position and the selected axes dropped from the plate.

RAISES DESCRIPTION
DistributionError

If the position has more entries than the plate has axes.

Source code in src/quivers/qiec/distributions.py
def at(self, position: tuple[int, ...]) -> RuntimeDistribution:
    """The distribution at one position of the plate, unplated.

    Parameters
    ----------
    position : tuple[int, ...]
        A position of the leading plate axes, outermost first; fewer
        positions than axes leave the remaining axes in place.

    Returns
    -------
    RuntimeDistribution
        The family with its arguments selected at the position and
        the selected axes dropped from the plate.

    Raises
    ------
    DistributionError
        If the position has more entries than the plate has axes.
    """
    shape = (*self.batch, *self.event)
    if len(position) > len(shape):
        raise DistributionError(
            f"position {position!r} is deeper than a plate of rank {len(shape)}"
        )
    arguments = {
        name: self._broadcast(
            name, _slice_at(value, position, self.ranks.get(name, 0))
        )
        for name, value in self.arguments.items()
    }
    remaining = len(position)
    batch = self.batch[remaining:] if remaining < len(self.batch) else ()
    event = self.event[max(remaining - len(self.batch), 0) :]
    return RuntimeDistribution(
        self.family, arguments, batch, event, self.ranks, self.natural
    )

sample

sample(rng: Random | None = None) -> object

Draw one value through the installed backend.

PARAMETER DESCRIPTION
rng

The generator to draw from; the module generator by default, which :func:seed_reference_rng controls.

TYPE: Random | None DEFAULT: None

RETURNS DESCRIPTION
object

The drawn host value: one draw of the family, or nested tuples of draws over the plate's batch and event axes.

RAISES DESCRIPTION
DistributionError

If the backend cannot sample the family.

Source code in src/quivers/qiec/distributions.py
def sample(self, rng: random.Random | None = None) -> object:
    """Draw one value through the installed backend.

    Parameters
    ----------
    rng : random.Random | None
        The generator to draw from; the module generator by default,
        which :func:`seed_reference_rng` controls.

    Returns
    -------
    object
        The drawn host value: one draw of the family, or nested
        tuples of draws over the plate's batch and event axes.

    Raises
    ------
    DistributionError
        If the backend cannot sample the family.
    """
    generator = rng or _RNG
    if not self.plated:
        return _backend.sample(self.family, self._at(()), generator)
    shape = (*self.batch, *self.event)
    return _nest(
        {
            position: _backend.sample(self.family, self._at(position), generator)
            for position in _positions(shape)
        },
        shape,
    )

log_mass

log_mass() -> float

The log of the distribution's total mass through the installed backend.

RETURNS DESCRIPTION
float

Zero for a probability measure; over a plate, the sum of the masses at every position, since the plate's draw is their product.

Source code in src/quivers/qiec/distributions.py
def log_mass(self) -> float:
    """The log of the distribution's total mass through the installed backend.

    Returns
    -------
    float
        Zero for a probability measure; over a plate, the sum of the
        masses at every position, since the plate's draw is their
        product.
    """
    if not self.plated:
        return _backend.log_mass(self.family, self._at(()))
    shape = (*self.batch, *self.event)
    return math.fsum(
        _backend.log_mass(self.family, self._at(position))
        for position in _positions(shape)
    )

log_prob

log_prob(value: object, keep_batch: bool = False) -> object

Evaluate the log density at a value through the installed backend.

PARAMETER DESCRIPTION
value

The point evaluated; over a plate, nested tuples of points shaped like the plate.

TYPE: object

keep_batch

Whether to keep one weight per batch position rather than total them.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
object

The log density as a float, or, with keep_batch over a batch, nested tuples of floats shaped like the batch axes.

RAISES DESCRIPTION
DistributionError

If the backend cannot score the family, or the point is not shaped like the plate.

Source code in src/quivers/qiec/distributions.py
def log_prob(self, value: object, keep_batch: bool = False) -> object:
    """Evaluate the log density at a value through the installed backend.

    Parameters
    ----------
    value : object
        The point evaluated; over a plate, nested tuples of points
        shaped like the plate.
    keep_batch : bool
        Whether to keep one weight per batch position rather than
        total them.

    Returns
    -------
    object
        The log density as a float, or, with ``keep_batch`` over a
        batch, nested tuples of floats shaped like the batch axes.

    Raises
    ------
    DistributionError
        If the backend cannot score the family, or the point is not
        shaped like the plate.
    """
    if not self.plated:
        return _backend.log_prob(self.family, self._at(()), value)
    shape = (*self.batch, *self.event)
    if tensor_rank(value) < len(shape):
        raise DistributionError(
            f"a point scored under a plate of rank {len(shape)} has rank "
            f"{tensor_rank(value)}"
        )
    totals: dict[tuple[int, ...], float] = {
        position: 0.0 for position in _positions(self.batch)
    }
    for position in _positions(shape):
        point = value
        for index in position:
            point = point[index]  # type: ignore[index]
        totals[position[: len(self.batch)]] += _backend.log_prob(
            self.family, self._at(position), point
        )
    if keep_batch:
        return _nest(totals, self.batch)
    return math.fsum(totals.values())

ArgumentDef dataclass

ArgumentDef(name: str, type: TypeExpr)

One named, typed value argument of an operation.

PARAMETER DESCRIPTION
name

The argument's display name.

TYPE: str

type

The argument's type, which may mention the operation's telescope.

TYPE: TypeExpr

ComputationType dataclass

ComputationType(effects: EffectRow, result: TypeExpr)

The type of a computation: its effect row and result type.

PARAMETER DESCRIPTION
effects

The row of effect instances the computation may perform.

TYPE: EffectRow

result

The type of the value it returns.

TYPE: TypeExpr

EffectDef dataclass

EffectDef(ref: EffectRef, telescope: Telescope, operations: tuple[OperationDef, ...])

An operation interface.

Identity is nominal: the declaration is fixed by its module and its own name, carried in ref. Static arguments distinguish applications of the interface without changing which declaration they apply.

PARAMETER DESCRIPTION
ref

The declaration's own reference, which must be unsaturated: a declaration names the interface, and applying it is what supplies arguments.

TYPE: EffectRef

telescope

Static binders the interface takes.

TYPE: Telescope

operations

The declared operations, unique by both identity and name.

TYPE: tuple[OperationDef, ...]

__post_init__

__post_init__() -> None

Validate the declaration against its operations.

RAISES DESCRIPTION
ValueError

If the telescope is malformed, if ref is saturated, if two operations share an identity or a name, or if an operation's telescope shadows an interface binder. Shadowing is rejected rather than resolved so that a binder in an operation signature always denotes the one thing.

Source code in src/quivers/qiec/effects.py
def __post_init__(self) -> None:
    """Validate the declaration against its operations.

    Raises
    ------
    ValueError
        If the telescope is malformed, if `ref` is saturated, if two
        operations share an identity or a name, or if an operation's
        telescope shadows an interface binder. Shadowing is rejected
        rather than resolved so that a binder in an operation
        signature always denotes the one thing.
    """
    validate_telescope(self.telescope)
    if self.ref.arguments:
        raise ValueError("an effect declaration ref must be unsaturated")
    ids = [operation.id for operation in self.operations]
    names = [operation.name for operation in self.operations]
    if len(set(ids)) != len(ids):
        raise ValueError(f"duplicate operation ID in effect {self.ref.name!r}")
    if len(set(names)) != len(names):
        raise ValueError(f"duplicate operation name in effect {self.ref.name!r}")
    interface_names = {binder.name for binder in self.telescope}
    for operation in self.operations:
        overlap = interface_names & {binder.name for binder in operation.telescope}
        if overlap:
            raise ValueError(
                f"operation {operation.name!r} shadows interface binders: {overlap!r}"
            )

apply

apply(arguments: tuple[StaticArgument, ...]) -> EffectRef

Instantiate the interface telescope as a concrete effect.

PARAMETER DESCRIPTION
arguments

Static arguments to saturate the telescope with.

TYPE: tuple[StaticArgument, ...]

RETURNS DESCRIPTION
EffectRef

The saturated application, carrying this declaration's identity together with the arguments given.

RAISES DESCRIPTION
TypeError

If an argument is of the wrong static class for its binder.

ValueError

If the argument count does not match the telescope, or an argument is ill-kinded.

Source code in src/quivers/qiec/effects.py
def apply(self, arguments: tuple[StaticArgument, ...]) -> EffectRef:
    """Instantiate the interface telescope as a concrete effect.

    Parameters
    ----------
    arguments : tuple[StaticArgument, ...]
        Static arguments to saturate the telescope with.

    Returns
    -------
    EffectRef
        The saturated application, carrying this declaration's
        identity together with the arguments given.

    Raises
    ------
    TypeError
        If an argument is of the wrong static class for its binder.
    ValueError
        If the argument count does not match the telescope, or an
        argument is ill-kinded.
    """
    check_static_arguments(self.telescope, arguments)
    return EffectRef(
        self.ref.id,
        self.ref.name,
        arguments,
    )

matches

matches(effect: EffectRef) -> bool

Whether a concrete effect is a well-kinded application here.

PARAMETER DESCRIPTION
effect

The saturated application to test.

TYPE: EffectRef

RETURNS DESCRIPTION
bool

True when effect names this declaration and its arguments saturate the telescope. False rather than raising, so callers can test candidates; use apply when the reason matters.

Source code in src/quivers/qiec/effects.py
def matches(self, effect: EffectRef) -> bool:
    """Whether a concrete effect is a well-kinded application here.

    Parameters
    ----------
    effect : EffectRef
        The saturated application to test.

    Returns
    -------
    bool
        True when `effect` names this declaration and its arguments
        saturate the telescope. False rather than raising, so callers
        can test candidates; use `apply` when the reason matters.
    """
    if effect.id != self.ref.id:
        return False
    try:
        self.apply(effect.arguments)
    except TypeError, ValueError:
        return False
    return True

operation

operation(operation: OperationId) -> OperationDef

The declared operation with a given identity.

PARAMETER DESCRIPTION
operation

The identity to resolve.

TYPE: OperationId

RETURNS DESCRIPTION
OperationDef

The matching declaration.

RAISES DESCRIPTION
KeyError

If this interface declares no such operation, which is how a request naming an operation another effect owns is caught.

Source code in src/quivers/qiec/effects.py
def operation(self, operation: OperationId) -> OperationDef:
    """The declared operation with a given identity.

    Parameters
    ----------
    operation : OperationId
        The identity to resolve.

    Returns
    -------
    OperationDef
        The matching declaration.

    Raises
    ------
    KeyError
        If this interface declares no such operation, which is how a
        request naming an operation another effect owns is caught.
    """
    for candidate in self.operations:
        if candidate.id == operation:
            return candidate
    raise KeyError(str(operation))

EffectRequest dataclass

EffectRequest(instance: EffectInstanceId, effect: EffectRef, operation: OperationId, static_arguments: tuple[StaticArgument, ...], arguments: tuple[Value, ...], result_type: TypeExpr, origin: SiteProvenance, tag: Literal['effect_request'] = 'effect_request')

A typed request allocated before filtering or handler dispatch.

PARAMETER DESCRIPTION
instance

The effect instance the request is addressed to.

TYPE: EffectInstanceId

effect

The instance's interface application.

TYPE: EffectRef

operation

The operation requested.

TYPE: OperationId

static_arguments

The operation's telescope instantiation, in binder order.

TYPE: tuple[StaticArgument, ...]

arguments

The value arguments, one per operation argument.

TYPE: tuple[Value, ...]

result_type

The type of the value the operation returns under the instantiation.

TYPE: TypeExpr

origin

Where the request was written and how it was reached.

TYPE: SiteProvenance

tag

The serialization discriminator; always "effect_request".

TYPE: Literal['effect_request'] DEFAULT: 'effect_request'

EffectRow dataclass

EffectRow(entries: tuple[RowEntry, ...] = (), tail: RowVariable | None = None)

A finite map from lexical instance IDs to interfaces plus a tail.

Entries are sorted by stable identifier at construction time, so row equality is insensitive to source order. Two instances of the same interface remain distinct entries.

PARAMETER DESCRIPTION
entries

The instances the row names, each with its interface; any order is accepted and normalized.

TYPE: tuple[RowEntry, ...] DEFAULT: ()

tail

A row variable standing for further entries, or None for a closed row. An open row's tail must prove it lacks every explicit entry.

TYPE: RowVariable | None DEFAULT: None

__post_init__

__post_init__() -> None

Sort the entries and reject a row that cannot be well formed.

RAISES DESCRIPTION
ValueError

If one lexical instance appears twice, or if the row is open and its tail does not prove it lacks every explicit entry. Without that proof the tail could later be instantiated to a row repeating an instance already named here.

Source code in src/quivers/qiec/effects.py
def __post_init__(self) -> None:
    """Sort the entries and reject a row that cannot be well formed.

    Raises
    ------
    ValueError
        If one lexical instance appears twice, or if the row is open
        and its tail does not prove it lacks every explicit entry.
        Without that proof the tail could later be instantiated to a
        row repeating an instance already named here.
    """
    ordered = tuple(sorted(self.entries, key=lambda entry: str(entry.instance)))
    instances = [entry.instance for entry in ordered]
    if len(set(instances)) != len(instances):
        raise ValueError("an effect row cannot contain a lexical instance twice")
    if self.tail is not None:
        unconstrained = [
            instance
            for instance in instances
            if not self.tail.proves_lacks(instance)
        ]
        if unconstrained:
            raise ValueError(
                "an open row must prove its tail lacks every explicit instance"
            )
    object.__setattr__(self, "entries", ordered)

lookup

lookup(instance: EffectInstanceId) -> EffectRef | None

The interface an instance implements in this row.

PARAMETER DESCRIPTION
instance

The lexical instance to resolve.

TYPE: EffectInstanceId

RETURNS DESCRIPTION
EffectRef or None

The interface when the row carries the instance explicitly, and None otherwise. An open tail is not consulted, so None means absent from the explicit entries rather than absent from every instantiation of the row.

Source code in src/quivers/qiec/effects.py
def lookup(self, instance: EffectInstanceId) -> EffectRef | None:
    """The interface an instance implements in this row.

    Parameters
    ----------
    instance : EffectInstanceId
        The lexical instance to resolve.

    Returns
    -------
    EffectRef or None
        The interface when the row carries the instance explicitly,
        and None otherwise. An open tail is not consulted, so None
        means absent from the explicit entries rather than absent
        from every instantiation of the row.
    """
    return next(
        (entry.effect for entry in self.entries if entry.instance == instance),
        None,
    )

contains

contains(instance: EffectInstanceId) -> bool

Whether the row carries an explicit entry for an instance.

PARAMETER DESCRIPTION
instance

The lexical instance to test.

TYPE: EffectInstanceId

RETURNS DESCRIPTION
bool

True when an explicit entry names the instance.

Source code in src/quivers/qiec/effects.py
def contains(self, instance: EffectInstanceId) -> bool:
    """Whether the row carries an explicit entry for an instance.

    Parameters
    ----------
    instance : EffectInstanceId
        The lexical instance to test.

    Returns
    -------
    bool
        True when an explicit entry names the instance.
    """
    return self.lookup(instance) is not None

add

add(entry: RowEntry) -> EffectRow

Return this row extended with one entry.

PARAMETER DESCRIPTION
entry

The instance and interface to add. Re-adding an entry the row already carries returns the row unchanged, so the operation is idempotent.

TYPE: RowEntry

RETURNS DESCRIPTION
EffectRow

The extended row. When the row is open, its tail gains a lacks constraint for the new instance, which keeps the invariant __post_init__ enforces.

RAISES DESCRIPTION
ValueError

If the row already binds this instance to a different interface, since one lexical instance implements one interface.

Source code in src/quivers/qiec/effects.py
def add(self, entry: RowEntry) -> EffectRow:
    """Return this row extended with one entry.

    Parameters
    ----------
    entry : RowEntry
        The instance and interface to add. Re-adding an entry the row
        already carries returns the row unchanged, so the operation is
        idempotent.

    Returns
    -------
    EffectRow
        The extended row. When the row is open, its tail gains a lacks
        constraint for the new instance, which keeps the invariant
        `__post_init__` enforces.

    Raises
    ------
    ValueError
        If the row already binds this instance to a different
        interface, since one lexical instance implements one
        interface.
    """
    existing = self.lookup(entry.instance)
    if existing is not None and existing != entry.effect:
        raise ValueError("one lexical instance cannot implement two interfaces")
    if existing is not None:
        return self
    tail = self.tail
    if tail is not None and not tail.proves_lacks(entry.instance):
        tail = tail.with_lacks((entry.instance,))
    return EffectRow((*self.entries, entry), tail)

without

without(instance: EffectInstanceId) -> EffectRow

Return this row with one instance discharged.

PARAMETER DESCRIPTION
instance

The lexical instance to remove, as a handler does when it handles every operation of that instance.

TYPE: EffectInstanceId

RETURNS DESCRIPTION
EffectRow

The row without that entry, keeping the tail as it was.

RAISES DESCRIPTION
KeyError

If the row carries no explicit entry for the instance. This is an error rather than a no-op because discharging an effect the row never had indicates the caller has the wrong row.

Source code in src/quivers/qiec/effects.py
def without(self, instance: EffectInstanceId) -> EffectRow:
    """Return this row with one instance discharged.

    Parameters
    ----------
    instance : EffectInstanceId
        The lexical instance to remove, as a handler does when it
        handles every operation of that instance.

    Returns
    -------
    EffectRow
        The row without that entry, keeping the tail as it was.

    Raises
    ------
    KeyError
        If the row carries no explicit entry for the instance. This
        is an error rather than a no-op because discharging an effect
        the row never had indicates the caller has the wrong row.
    """
    if not self.contains(instance):
        raise KeyError(str(instance))
    return EffectRow(
        tuple(entry for entry in self.entries if entry.instance != instance),
        self.tail,
    )

union

union(other: EffectRow) -> EffectRow

Union two rows, preserving a compatible open tail.

PARAMETER DESCRIPTION
other

The row to merge in.

TYPE: EffectRow

RETURNS DESCRIPTION
EffectRow

A row carrying every entry of both. When both are open their tails are merged, and the surviving tail is constrained to lack every explicit instance on either side.

RAISES DESCRIPTION
ValueError

If the two rows bind one instance to different interfaces, or if their tails are distinct variables and so cannot merge.

Source code in src/quivers/qiec/effects.py
def union(self, other: EffectRow) -> EffectRow:
    """Union two rows, preserving a compatible open tail.

    Parameters
    ----------
    other : EffectRow
        The row to merge in.

    Returns
    -------
    EffectRow
        A row carrying every entry of both. When both are open their
        tails are merged, and the surviving tail is constrained to
        lack every explicit instance on either side.

    Raises
    ------
    ValueError
        If the two rows bind one instance to different interfaces, or
        if their tails are distinct variables and so cannot merge.
    """
    if self.tail is not None and other.tail is not None:
        tail = self.tail.merge(other.tail)
    else:
        tail = self.tail or other.tail
    if tail is not None:
        tail = tail.with_lacks(
            tuple(entry.instance for entry in (*self.entries, *other.entries))
        )
    result = EffectRow(self.entries, tail)
    for entry in other.entries:
        result = result.add(entry)
    return result

HandlerClauseDef dataclass

HandlerClauseDef(operation: OperationId, grade: ResumptionGrade, parameters: tuple[Local, ...] = (), body: Computation | None = None)

One operation a handler covers, and how it may resume.

PARAMETER DESCRIPTION
operation

The operation this clause handles.

TYPE: OperationId

grade

How often the clause may invoke its continuation. The grade is part of the clause contract and is checked against the body.

TYPE: ResumptionGrade

parameters

Binders for the operation's value arguments, in declaration order. The body refers to the request's arguments through these.

TYPE: tuple[Local, ...] DEFAULT: ()

body

What the clause does. None is a signature rather than an implementation, and is legal only for a handler whose implementation is supplied at runtime. An absent body never means "look up a callback": the declaration has to say so.

TYPE: Computation or None DEFAULT: None

HandlerDef dataclass

HandlerDef(id: HandlerId, name: str, effect: EffectRef, clauses: tuple[HandlerClauseDef, ...], input_type: TypeExpr, output_type: TypeExpr, introduced: EffectRow = EMPTY_ROW, total: bool = True, forwards_unknown: bool = False, telescope: Telescope = (), return_clause: HandlerReturnClauseDef | None = None, implementation: Literal['authored', 'foreign'] = 'foreign')

Serializable type-and-coverage signature for a handler.

An authored handler carries its clause bodies in the stable core; a foreign handler's bodies are runtime attachments keyed by id.

PARAMETER DESCRIPTION
id

The handler's stable identity.

TYPE: HandlerId

name

The handler's display name.

TYPE: str

effect

The interface application the handler handles.

TYPE: EffectRef

clauses

One clause per operation the handler covers.

TYPE: tuple[HandlerClauseDef, ...]

input_type

The result type of the computation the handler accepts.

TYPE: TypeExpr

output_type

The type of the handler's answer.

TYPE: TypeExpr

introduced

The effects the handler's own clauses perform, which the residual row of a handled computation gains.

TYPE: EffectRow DEFAULT: EMPTY_ROW

total

Whether the clauses cover every operation of effect.

TYPE: bool DEFAULT: True

forwards_unknown

Whether an operation no clause covers is forwarded outward rather than rejected; incompatible with total.

TYPE: bool DEFAULT: False

telescope

The static parameters the handler abstracts over, in order.

TYPE: Telescope DEFAULT: ()

return_clause

How the handler answers the computation's return, or None to return the value unchanged.

TYPE: HandlerReturnClauseDef | None DEFAULT: None

implementation

"authored" when every clause carries a body, "foreign" when none does and a runtime provider supplies the behavior.

TYPE: Literal['authored', 'foreign'] DEFAULT: 'foreign'

__post_init__

__post_init__() -> None

Validate the clause set against the coverage claim.

RAISES DESCRIPTION
ValueError

If the telescope is malformed, if two clauses cover the same operation, if the handler claims to be both total and forwarding, or if its bodies disagree with its declared implementation. A total handler covering every operation has nothing left to forward. An authored handler missing a body has nowhere for that operation's behavior to live, and a body on a foreign handler would be ignored in favour of its runtime provider; both are worse than being rejected.

Source code in src/quivers/qiec/effects.py
def __post_init__(self) -> None:
    """Validate the clause set against the coverage claim.

    Raises
    ------
    ValueError
        If the telescope is malformed, if two clauses cover the same
        operation, if the handler claims to be both total and
        forwarding, or if its bodies disagree with its declared
        implementation. A total handler covering every operation has
        nothing left to forward. An authored handler missing a body
        has nowhere for that operation's behavior to live, and a body
        on a foreign handler would be ignored in favour of its
        runtime provider; both are worse than being rejected.
    """
    validate_telescope(self.telescope)
    operations = [clause.operation for clause in self.clauses]
    if len(set(operations)) != len(operations):
        raise ValueError(f"duplicate clause in handler {self.name!r}")
    if self.total and self.forwards_unknown:
        raise ValueError("a handler cannot be both total and forwarding")
    authored = self.implementation == "authored"
    missing = [clause.operation for clause in self.clauses if clause.body is None]
    if authored and missing:
        raise ValueError(
            f"authored handler {self.name!r} has clauses without a body: "
            f"{missing!r}; write the bodies, or declare the handler "
            f"foreign if its behavior comes from a runtime provider"
        )
    supplied = [
        clause.operation for clause in self.clauses if clause.body is not None
    ]
    if not authored and supplied:
        raise ValueError(
            f"foreign handler {self.name!r} carries authored clause bodies: "
            f"{supplied!r}; a foreign handler's behavior comes from its "
            f"runtime provider, so a body here would be ignored"
        )
    if not authored and self.return_clause is not None:
        raise ValueError(
            f"foreign handler {self.name!r} carries a return clause; its "
            f"answer comes from its runtime provider"
        )

clause

clause(operation: OperationId) -> HandlerClauseDef | None

The clause covering an operation, if this handler has one.

PARAMETER DESCRIPTION
operation

The operation to look for.

TYPE: OperationId

RETURNS DESCRIPTION
HandlerClauseDef or None

The clause, or None when the operation is uncovered. For a forwarding handler None means the request passes through rather than that it is an error.

Source code in src/quivers/qiec/effects.py
def clause(self, operation: OperationId) -> HandlerClauseDef | None:
    """The clause covering an operation, if this handler has one.

    Parameters
    ----------
    operation : OperationId
        The operation to look for.

    Returns
    -------
    HandlerClauseDef or None
        The clause, or None when the operation is uncovered. For a
        forwarding handler None means the request passes through
        rather than that it is an error.
    """
    return next(
        (clause for clause in self.clauses if clause.operation == operation),
        None,
    )

HandlerReturnClauseDef dataclass

HandlerReturnClauseDef(binder: Local, body: Computation)

What a handler does with a value its computation returns.

Separate from an operation clause because it answers a different thing: an operation clause responds to a request and may resume, while this responds to the computation finishing and has no continuation to invoke.

PARAMETER DESCRIPTION
binder

Names the returned value inside the body.

TYPE: Local

body

What the handler produces from it. There is no signature-only form: a handler that answers returns at all has to say how.

TYPE: Computation

OperationDef dataclass

OperationDef(id: OperationId, name: str, telescope: Telescope, arguments: tuple[ArgumentDef, ...], result_type: TypeExpr)

One result-indexed request constructor in an effect interface.

PARAMETER DESCRIPTION
id

Stable identity, derived from the owning effect and this operation's name.

TYPE: OperationId

name

Source name, as written in the effect declaration.

TYPE: str

telescope

Static binders this operation introduces on top of the interface's own. They may not shadow the interface binders.

TYPE: Telescope

arguments

Value arguments the request carries, named uniquely.

TYPE: tuple[ArgumentDef, ...]

result_type

What resuming the request supplies, possibly mentioning the binders above.

TYPE: TypeExpr

__post_init__

__post_init__() -> None

Validate the telescope and the argument names.

RAISES DESCRIPTION
ValueError

If the telescope is malformed, or two arguments share a name.

Source code in src/quivers/qiec/effects.py
def __post_init__(self) -> None:
    """Validate the telescope and the argument names.

    Raises
    ------
    ValueError
        If the telescope is malformed, or two arguments share a name.
    """
    validate_telescope(self.telescope)
    names = [argument.name for argument in self.arguments]
    if len(set(names)) != len(names):
        raise ValueError(f"duplicate argument in operation {self.name!r}")

ResumptionGrade

Bases: str, Enum

Quantitative use allowed for a handler-clause resumption.

RowEntry dataclass

RowEntry(instance: EffectInstanceId, effect: EffectRef)

One lexical effect instance and the interface it implements.

PARAMETER DESCRIPTION
instance

The lexical instance. Two instances of one interface stay distinct, so this rather than effect is the row's key.

TYPE: EffectInstanceId

effect

The saturated interface application the instance implements.

TYPE: EffectRef

RowSubstitution dataclass

RowSubstitution(bindings: tuple[tuple[RowVariableId, EffectRow], ...] = ())

A finite, occurs-checked substitution for stable row variables.

PARAMETER DESCRIPTION
bindings

What each row variable stands for. At most one binding per variable, and no binding whose row is tailed by the variable it replaces.

TYPE: tuple[tuple[RowVariableId, EffectRow], ...] DEFAULT: ()

__post_init__

__post_init__() -> None

Reject a substitution that is ambiguous or immediately cyclic.

RAISES DESCRIPTION
ValueError

If one variable is bound twice, or if a variable is bound to a row whose own tail is that same variable. The second is the occurs check at depth one; deeper cycles are caught while applying the substitution.

Source code in src/quivers/qiec/effects.py
def __post_init__(self) -> None:
    """Reject a substitution that is ambiguous or immediately cyclic.

    Raises
    ------
    ValueError
        If one variable is bound twice, or if a variable is bound to
        a row whose own tail is that same variable. The second is the
        occurs check at depth one; deeper cycles are caught while
        applying the substitution.
    """
    identities = [identity for identity, _ in self.bindings]
    if len(set(identities)) != len(identities):
        raise ValueError("duplicate row substitution binding")
    for identity, row in self.bindings:
        if row.tail is not None and row.tail.identity == identity:
            raise ValueError("recursive row substitution")

lookup

lookup(variable: RowVariable) -> EffectRow | None

The row a variable stands for under this substitution.

PARAMETER DESCRIPTION
variable

The variable to resolve, matched on its identity rather than its display name.

TYPE: RowVariable

RETURNS DESCRIPTION
EffectRow or None

The bound row, or None when the variable is unbound and so remains an open tail.

Source code in src/quivers/qiec/effects.py
def lookup(self, variable: RowVariable) -> EffectRow | None:
    """The row a variable stands for under this substitution.

    Parameters
    ----------
    variable : RowVariable
        The variable to resolve, matched on its identity rather than
        its display name.

    Returns
    -------
    EffectRow or None
        The bound row, or None when the variable is unbound and so
        remains an open tail.
    """
    return next(
        (row for identity, row in self.bindings if identity == variable.identity),
        None,
    )

apply

apply(row: EffectRow) -> EffectRow

Replace this row's tail, and any tail it resolves to.

PARAMETER DESCRIPTION
row

The row to substitute into. A closed row is returned as is.

TYPE: EffectRow

RETURNS DESCRIPTION
EffectRow

The row with its tail resolved as far as the bindings reach.

RAISES DESCRIPTION
ValueError

If the bindings are cyclic, or if resolving a tail would introduce an instance that tail was proven to lack.

Source code in src/quivers/qiec/effects.py
def apply(self, row: EffectRow) -> EffectRow:
    """Replace this row's tail, and any tail it resolves to.

    Parameters
    ----------
    row : EffectRow
        The row to substitute into. A closed row is returned as is.

    Returns
    -------
    EffectRow
        The row with its tail resolved as far as the bindings reach.

    Raises
    ------
    ValueError
        If the bindings are cyclic, or if resolving a tail would
        introduce an instance that tail was proven to lack.
    """
    return self._apply(row, frozenset())

RowUnification dataclass

RowUnification(row: EffectRow, substitution: RowSubstitution = RowSubstitution())

The common row two rows unify to, and how their tails were bound.

PARAMETER DESCRIPTION
row

The unified row.

TYPE: EffectRow

substitution

The bindings that carry each input row to row. Empty when the rows already agreed.

TYPE: RowSubstitution DEFAULT: RowSubstitution()

RowVariable dataclass

RowVariable(name: str, identity: RowVariableId, lacks: tuple[EffectInstanceId, ...] = ())

The open tail of an effect row, with the instances it excludes.

PARAMETER DESCRIPTION
name

Display name, used in diagnostics only. Two variables with the same identity and different names are the same variable.

TYPE: str

identity

Stable identity. Equality and hashing are taken from this rather than from name, so renaming for readability cannot silently split one variable into two.

TYPE: RowVariableId

lacks

Instances the tail is proven not to contain. Sorted at construction, so equality is insensitive to the order given.

TYPE: tuple[EffectInstanceId, ...] DEFAULT: ()

__post_init__

__post_init__() -> None

Sort the lacks constraints and reject a malformed variable.

RAISES DESCRIPTION
ValueError

If name is empty, or lacks names one instance twice.

Source code in src/quivers/qiec/effects.py
def __post_init__(self) -> None:
    """Sort the lacks constraints and reject a malformed variable.

    Raises
    ------
    ValueError
        If `name` is empty, or `lacks` names one instance twice.
    """
    if not self.name:
        raise ValueError("row variable name cannot be empty")
    ordered = tuple(sorted(self.lacks, key=str))
    if len(set(ordered)) != len(ordered):
        raise ValueError("a row lacks constraint cannot repeat an instance")
    object.__setattr__(self, "lacks", ordered)

proves_lacks

proves_lacks(instance: EffectInstanceId) -> bool

Whether this tail is known not to contain instance.

PARAMETER DESCRIPTION
instance

The lexical instance to test.

TYPE: EffectInstanceId

RETURNS DESCRIPTION
bool

True when the constraint is carried, which is what permits an explicit entry for instance to sit beside this tail.

Source code in src/quivers/qiec/effects.py
def proves_lacks(self, instance: EffectInstanceId) -> bool:
    """Whether this tail is known not to contain `instance`.

    Parameters
    ----------
    instance : EffectInstanceId
        The lexical instance to test.

    Returns
    -------
    bool
        True when the constraint is carried, which is what permits an
        explicit entry for `instance` to sit beside this tail.
    """
    return instance in self.lacks

__eq__

__eq__(other: object) -> bool

Compare by identity and constraints, ignoring the display name.

PARAMETER DESCRIPTION
other

The value to compare against.

TYPE: object

RETURNS DESCRIPTION
bool

True when other is a row variable with the same identity and the same lacks constraints.

Source code in src/quivers/qiec/effects.py
def __eq__(self, other: object) -> bool:
    """Compare by identity and constraints, ignoring the display name.

    Parameters
    ----------
    other : object
        The value to compare against.

    Returns
    -------
    bool
        True when `other` is a row variable with the same identity and
        the same lacks constraints.
    """
    return (
        isinstance(other, RowVariable)
        and self.identity == other.identity
        and self.lacks == other.lacks
    )

__hash__

__hash__() -> int

Hash the fields that __eq__ compares.

RETURNS DESCRIPTION
int

A hash of the identity and the lacks constraints.

Source code in src/quivers/qiec/effects.py
def __hash__(self) -> int:
    """Hash the fields that `__eq__` compares.

    Returns
    -------
    int
        A hash of the identity and the lacks constraints.
    """
    return hash((self.identity, self.lacks))

merge

merge(other: RowVariable) -> RowVariable

Unify two occurrences, accumulating their lacks constraints.

PARAMETER DESCRIPTION
other

Another occurrence of the same variable.

TYPE: RowVariable

RETURNS DESCRIPTION
RowVariable

One variable carrying the union of both constraint sets, which is sound because each occurrence's constraints hold of the single variable they both name.

RAISES DESCRIPTION
ValueError

If the two variables have different identities, and so are not occurrences of one variable at all.

Source code in src/quivers/qiec/effects.py
def merge(self, other: RowVariable) -> RowVariable:
    """Unify two occurrences, accumulating their lacks constraints.

    Parameters
    ----------
    other : RowVariable
        Another occurrence of the same variable.

    Returns
    -------
    RowVariable
        One variable carrying the union of both constraint sets, which
        is sound because each occurrence's constraints hold of the
        single variable they both name.

    Raises
    ------
    ValueError
        If the two variables have different identities, and so are not
        occurrences of one variable at all.
    """
    if self.identity != other.identity:
        raise ValueError("cannot unify distinct row variables")
    merged = tuple(sorted(set((*self.lacks, *other.lacks)), key=str))
    return RowVariable(self.name, self.identity, merged)

with_lacks

with_lacks(instances: tuple[EffectInstanceId, ...]) -> RowVariable

Return this variable with further instances excluded.

PARAMETER DESCRIPTION
instances

Instances to add to the constraint set. Instances already excluded are absorbed rather than repeated.

TYPE: tuple[EffectInstanceId, ...]

RETURNS DESCRIPTION
RowVariable

A variable of the same identity carrying the wider constraint.

Source code in src/quivers/qiec/effects.py
def with_lacks(self, instances: tuple[EffectInstanceId, ...]) -> RowVariable:
    """Return this variable with further instances excluded.

    Parameters
    ----------
    instances : tuple[EffectInstanceId, ...]
        Instances to add to the constraint set. Instances already
        excluded are absorbed rather than repeated.

    Returns
    -------
    RowVariable
        A variable of the same identity carrying the wider constraint.
    """
    merged = tuple(sorted(set((*self.lacks, *instances)), key=str))
    return RowVariable(self.name, self.identity, merged)

EntryParameter didactic-model

EntryParameter(*, name: str, type: str, role: str)

Bases: Model

One value parameter of an entry point.

PARAMETER DESCRIPTION
name

The parameter's name.

TYPE: str

type

Its type, rendered in the surface spelling.

TYPE: str

role

How a program parameter is supplied (its data, an observation, a fibration, a scalar, or the program's domain); "value" for a computation's parameter.

TYPE: str

EntryPoint didactic-model

EntryPoint(*, name: str, kind: EntryKind, parameters: tuple[EntryParameter, ...], statics: tuple[str, ...], effects: tuple[str, ...], result: str, sites: tuple[str, ...])

Bases: Model

An executable entry point of a checked module.

PARAMETER DESCRIPTION
name

The entry's source name.

TYPE: str

kind

Whether it is a define computation or a program.

TYPE: EntryKind

parameters

Its value parameters, in order.

TYPE: tuple[EntryParameter, ...]

statics

The names of the static binders its telescope takes, in order.

TYPE: tuple[str, ...]

effects

The effect instances its body performs, rendered as "<instance> : <Effect>" in name order; empty for a pure computation and for a program, whose canonical instances the run handles.

TYPE: tuple[str, ...]

result

Its result type, rendered.

TYPE: str

sites

The labels of a program's sample sites, which a run may condition on; empty for a computation.

TYPE: tuple[str, ...]

EntryRun dataclass

EntryRun(entry: EntryPoint, value: HostValue, log_joint: float | None, result: ExecutionResult)

What invoking an entry point produced.

PARAMETER DESCRIPTION
entry

The entry invoked.

TYPE: EntryPoint

value

The validated host value the entry returned.

TYPE: HostValue

log_joint

The log joint density a program run accumulated, or None for a computation.

TYPE: float | None

result

The underlying execution result, with its trace.

TYPE: ExecutionResult

to_data

to_data() -> dict[str, object]

Render the run as JSON-compatible data.

RETURNS DESCRIPTION
dict[str, object]

The execution result's data, with the entry's kind and, for a program, the log joint.

Source code in src/quivers/qiec/entries.py
def to_data(self) -> dict[str, object]:
    """Render the run as JSON-compatible data.

    Returns
    -------
    dict[str, object]
        The execution result's data, with the entry's kind and, for a
        program, the log joint.
    """
    data = self.result.to_data()
    data["kind"] = self.entry.kind
    if self.log_joint is not None:
        data["log_joint"] = self.log_joint
    return data

AttachmentBinding dataclass

AttachmentBinding(value: object, type: TypeExpr, validator: RuntimeValidator, duplicable: bool = False)

One host value attached to an otherwise serializable core term.

duplicable is a semantic assertion about the host value, not merely a promise that copy.copy happens to work. Multi-shot resumptions reject captured attachment values unless this bit was supplied explicitly.

PARAMETER DESCRIPTION
value

The host value.

TYPE: object

type

The type the value inhabits.

TYPE: TypeExpr

validator

The predicate that checks a host value against type.

TYPE: RuntimeValidator

duplicable

Whether an unrestricted resumption may capture the value.

TYPE: bool DEFAULT: False

ClauseComputation dataclass

ClauseComputation(computation: Computation, bindings: tuple[tuple[Local, object], ...] = ())

Ask the evaluator to run a structural clause body outside the handler.

Host values introduced by a handler must first be installed through :meth:ClauseContext.attach; arbitrary callback closures do not enter the computation tree.

PARAMETER DESCRIPTION
computation

The stable computation to run.

TYPE: Computation

bindings

Locals the computation refers to, each with its evaluated value.

TYPE: tuple[tuple[Local, object], ...] DEFAULT: ()

ClauseContext

ClauseContext(evaluator: Evaluator, environment: Mapping[Local, object], outer_stack: tuple[ContinuationFrame, ...], resumption_path: tuple[int, ...], request: RuntimeRequest | None)

Disciplined runtime services available to an attached handler clause.

PARAMETER DESCRIPTION
evaluator

The evaluator running the clause.

TYPE: Evaluator

environment

The bindings in scope at the handler.

TYPE: Mapping[Local, object]

outer_stack

The continuation frames outside the handler.

TYPE: tuple[ContinuationFrame, ...]

resumption_path

The ordinal of each resumption taken to reach the clause.

TYPE: tuple[int, ...]

request

The request the clause answers, or None in a return clause.

TYPE: RuntimeRequest | None

Source code in src/quivers/qiec/evaluator.py
def __init__(
    self,
    evaluator: Evaluator,
    environment: Mapping[Local, object],
    outer_stack: tuple[ContinuationFrame, ...],
    resumption_path: tuple[int, ...],
    request: RuntimeRequest | None,
) -> None:
    self._evaluator = evaluator
    self._environment = environment
    self._outer_stack = _frames(outer_stack)
    self._resumption_path = resumption_path
    self._request = request

request property

request: RuntimeRequest | None

The request this clause is answering.

RETURNS DESCRIPTION
RuntimeRequest or None

The request, or None in a return clause, which answers a completed computation rather than an operation.

attachments property

attachments: RuntimeAttachments

The host values available to this clause.

RETURNS DESCRIPTION
RuntimeAttachments

The evaluator's attachment table.

evaluate

evaluate(computation: Computation) -> object

Evaluate a clause body in the outer handler context.

A delimiter prevents the clause body from consuming the continuation outside the handled expression. Effects performed here can reach only handlers outside the matched handler, which is the chosen deep-handler semantics.

PARAMETER DESCRIPTION
computation

The clause body to run.

TYPE: Computation

RETURNS DESCRIPTION
object

What the body produced.

Source code in src/quivers/qiec/evaluator.py
def evaluate(self, computation: Computation) -> object:
    """Evaluate a clause body in the outer handler context.

    A delimiter prevents the clause body from consuming the continuation
    outside the handled expression.  Effects performed here can reach only
    handlers outside the matched handler, which is the chosen deep-handler
    semantics.

    Parameters
    ----------
    computation : Computation
        The clause body to run.

    Returns
    -------
    object
        What the body produced.
    """
    delimiter = _DelimiterFrame()
    stack = [*self._outer_stack, delimiter]
    return self._evaluator._evaluate_delimited(
        computation,
        dict(self._environment),
        stack,
        self._outer_stack,
        self._resumption_path,
        delimiter,
    )

attach

attach(value: object, type: TypeExpr, *, role: str, validator: RuntimeValidator = _accept, duplicable: bool = False) -> AttachmentRef

Install a generated host value under a deterministic runtime ID.

PARAMETER DESCRIPTION
value

The host value to attach.

TYPE: object

type

The type the core ascribes to it.

TYPE: TypeExpr

role

What the value is for, which enters its derived identity.

TYPE: str

validator

Checks the value inhabits the type.

TYPE: RuntimeValidator DEFAULT: _accept

duplicable

Whether the value may be copied by a multi-shot resumption.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
AttachmentRef

A reference usable in a core term. The identity derives from the run, the request, and the role rather than being generated, so a trace can name the same attachment twice.

Source code in src/quivers/qiec/evaluator.py
def attach(
    self,
    value: object,
    type: TypeExpr,
    *,
    role: str,
    validator: RuntimeValidator = _accept,
    duplicable: bool = False,
) -> AttachmentRef:
    """Install a generated host value under a deterministic runtime ID.

    Parameters
    ----------
    value : object
        The host value to attach.
    type : TypeExpr
        The type the core ascribes to it.
    role : str
        What the value is for, which enters its derived identity.
    validator : RuntimeValidator
        Checks the value inhabits the type.
    duplicable : bool
        Whether the value may be copied by a multi-shot resumption.

    Returns
    -------
    AttachmentRef
        A reference usable in a core term. The identity derives from
        the run, the request, and the role rather than being
        generated, so a trace can name the same attachment twice.
    """
    if self._request is None:
        seed: tuple[object, ...] = (
            self._evaluator._run_serial,
            "return-clause",
            self._resumption_path,
            role,
        )
    else:
        seed = (
            self._evaluator._run_serial,
            self._request.address,
            str(self._request.operation),
            role,
        )
    id = AttachmentId.derive("runtime-generated", *seed)
    return self.attachments.bind_value(
        id,
        value,
        type,
        validator=validator,
        duplicable=duplicable,
    )

ContinuationFrame

One frame of the reference evaluator's explicit continuation stack.

The evaluator creates and consumes every frame. A handler clause sees frames only through the :class:ClauseContext and :class:Resumption the evaluator hands it, and never inspects or builds one itself; the class exists so those two constructors can name the stack they capture.

EvaluationError

Bases: RuntimeError

Base class for failures at the typed-core/runtime boundary.

Evaluator

Evaluator(attachments: RuntimeAttachments | None = None, *, handler_manifest: HandlerManifest | None = None, trace_hook: EvaluationTraceHook | None = None, module: QiecModule | None = None, fuel: int | None = None, type_validator: Callable[[TypeExpr], RuntimeValidator | None] | None = None)

Execute QIEC computations with explicit runtime attachments.

PARAMETER DESCRIPTION
attachments

The host values and handlers available; empty by default.

TYPE: RuntimeAttachments | None DEFAULT: None

handler_manifest

Checked handler definitions every attached handler must match exactly on each :meth:evaluate, or None to skip that check there; :meth:evaluate_checked always derives a manifest from its registry.

TYPE: HandlerManifest | None DEFAULT: None

trace_hook

Called with each execution event's name and payload, or None for no tracing.

TYPE: EvaluationTraceHook | None DEFAULT: None

module

The module whose named computations calls resolve to and whose authored handlers handle may install without an attachment, or None when the computation calls nothing and installs only attached handlers.

TYPE: QiecModule | None DEFAULT: None

fuel

The number of machine steps a run may take before failing with :class:FuelExhaustedError, or None for no bound.

TYPE: int | None DEFAULT: None

type_validator

Supplies the host validator for a type at an authored handler's boundaries: the values it is resumed with, the handled computation's result, and its answer. None accepts every host value there, which is sound for values that only ever flowed through checked terms.

TYPE: Callable[[TypeExpr], RuntimeValidator | None] | None DEFAULT: None

Source code in src/quivers/qiec/evaluator.py
def __init__(
    self,
    attachments: RuntimeAttachments | None = None,
    *,
    handler_manifest: HandlerManifest | None = None,
    trace_hook: EvaluationTraceHook | None = None,
    module: QiecModule | None = None,
    fuel: int | None = None,
    type_validator: Callable[[TypeExpr], RuntimeValidator | None] | None = None,
) -> None:
    self.attachments = attachments or RuntimeAttachments()
    self.handler_manifest = handler_manifest
    self.trace_hook = trace_hook
    self.fuel = fuel
    self._type_validator = type_validator
    self._computations: dict[ComputationId, NamedComputation] = {}
    self._handler_definitions: dict[HandlerId, HandlerDef] = {}
    if module is not None:
        self._computations = {item.id: item for item in module.computations}
        self._handler_definitions = {item.id: item for item in module.handlers}
    self._run_serial = 0
    self._steps = 0
    self._call_serial = 0
    self._instance_serial = 0

evaluate

evaluate(computation: Computation, environment: Mapping[Local, object] | None = None) -> object

Evaluate a closed or explicitly supplied computation.

PARAMETER DESCRIPTION
computation

The computation to run.

TYPE: Computation

environment

Values for the computation's free locals. None means it is closed.

TYPE: Mapping[Local, object] or None DEFAULT: None

RETURNS DESCRIPTION
object

What the computation produced.

RAISES DESCRIPTION
EvaluationError

If a request reaches no handler, a handler is missing from the manifest, a resumption exceeds its grade, or a host value fails validation.

Source code in src/quivers/qiec/evaluator.py
def evaluate(
    self,
    computation: Computation,
    environment: Mapping[Local, object] | None = None,
) -> object:
    """Evaluate a closed or explicitly supplied computation.

    Parameters
    ----------
    computation : Computation
        The computation to run.
    environment : Mapping[Local, object] or None
        Values for the computation's free locals. None means it is
        closed.

    Returns
    -------
    object
        What the computation produced.

    Raises
    ------
    EvaluationError
        If a request reaches no handler, a handler is missing from
        the manifest, a resumption exceeds its grade, or a host value
        fails validation.
    """
    if self.handler_manifest is not None:
        self._validate_handler_manifest(self.handler_manifest)
    self._begin_run()
    stack: list[_Frame] = []
    try:
        return self._drive_computation(
            computation,
            dict(environment or {}),
            stack,
            (),
        )
    except _HostUnwind as unwind:
        self._drop_stack(tuple(stack))
        raise EvaluationError(
            "a value reached a host delimiter no resumption was waiting on"
        ) from unwind
    except BaseException:
        self._drop_stack(tuple(stack))
        raise

evaluate_checked

evaluate_checked(computation: Computation, registry: KernelRegistry, environment: Mapping[Local, object] | None = None) -> object

Kernel-check a computation and fail closed on handler attachments.

PARAMETER DESCRIPTION
computation

The computation to run.

TYPE: Computation

registry

Declarations to check it against.

TYPE: KernelRegistry

environment

Values for its free locals.

TYPE: Mapping[Local, object] or None DEFAULT: None

RETURNS DESCRIPTION
object

What the computation produced.

RAISES DESCRIPTION
KernelError

If the computation does not check.

EvaluationError

If execution fails, as in evaluate.

Source code in src/quivers/qiec/evaluator.py
def evaluate_checked(
    self,
    computation: Computation,
    registry: KernelRegistry,
    environment: Mapping[Local, object] | None = None,
) -> object:
    """Kernel-check a computation and fail closed on handler attachments.
    Parameters
    ----------
    computation : Computation
        The computation to run.
    registry : KernelRegistry
        Declarations to check it against.
    environment : Mapping[Local, object] or None
        Values for its free locals.

    Returns
    -------
    object
        What the computation produced.

    Raises
    ------
    KernelError
        If the computation does not check.
    EvaluationError
        If execution fails, as in `evaluate`.
    """
    runtime_environment = dict(environment or {})
    context = CheckContext()
    for local in runtime_environment:
        context = context.extend(local)
    infer_computation(
        computation,
        registry,
        context,
    )
    return self.evaluate_validated(computation, registry, runtime_environment)

evaluate_validated

evaluate_validated(computation: Computation, registry: KernelRegistry, environment: Mapping[Local, object] | None = None) -> object

Run a computation a module validation has already checked.

validate_module checks every body of a module against its registry; a caller holding that registry runs such a body without checking it again. The handler manifest is still held against the attachments.

PARAMETER DESCRIPTION
computation

The computation to run, a body of the validated module.

TYPE: Computation

registry

The registry the module validated against.

TYPE: KernelRegistry

environment

Values for its free locals.

TYPE: Mapping[Local, object] or None DEFAULT: None

RETURNS DESCRIPTION
object

What the computation produced.

RAISES DESCRIPTION
EvaluationError

If execution fails, as in evaluate.

Source code in src/quivers/qiec/evaluator.py
def evaluate_validated(
    self,
    computation: Computation,
    registry: KernelRegistry,
    environment: Mapping[Local, object] | None = None,
) -> object:
    """Run a computation a module validation has already checked.

    `validate_module` checks every body of a module against its
    registry; a caller holding that registry runs such a body
    without checking it again. The handler manifest is still held
    against the attachments.

    Parameters
    ----------
    computation : Computation
        The computation to run, a body of the validated module.
    registry : KernelRegistry
        The registry the module validated against.
    environment : Mapping[Local, object] or None
        Values for its free locals.

    Returns
    -------
    object
        What the computation produced.

    Raises
    ------
    EvaluationError
        If execution fails, as in `evaluate`.
    """
    runtime_environment = dict(environment or {})
    manifest = HandlerManifest.from_registry(registry)
    self._validate_handler_manifest(manifest)
    self._begin_run()
    stack: list[_Frame] = []
    try:
        return self._drive_computation(
            computation,
            runtime_environment,
            stack,
            (),
        )
    except BaseException:
        self._drop_stack(tuple(stack))
        raise

evaluate_value

evaluate_value(value: Value, environment: Mapping[Local, object] | None = None) -> object

Erase and evaluate a checked QIEC value.

PARAMETER DESCRIPTION
value

The value to evaluate.

TYPE: Value

environment

Values for its free locals.

TYPE: Mapping[Local, object] or None DEFAULT: None

RETURNS DESCRIPTION
object

The host representation of the value.

Source code in src/quivers/qiec/evaluator.py
def evaluate_value(
    self,
    value: Value,
    environment: Mapping[Local, object] | None = None,
) -> object:
    """Erase and evaluate a checked QIEC value.
    Parameters
    ----------
    value : Value
        The value to evaluate.
    environment : Mapping[Local, object] or None
        Values for its free locals.

    Returns
    -------
    object
        The host representation of the value.
    """
    return self._value(value, environment or {})

Forward dataclass

Forward(on_response: ResponseHook | None = None)

Forward a request to an outer handler, optionally observing its reply.

PARAMETER DESCRIPTION
on_response

Called with the outer handler's answer before it reaches the resumed computation, or None to forward blindly.

TYPE: ResponseHook | None DEFAULT: None

FuelExhaustedError

FuelExhaustedError(steps: int)

Bases: EvaluationError

The step budget ran out before the computation finished.

General recursion is allowed to diverge, so a budget is the only way a caller can bound a run. Exhaustion is reported as an ordinary evaluation failure rather than a host recursion error, which the machine's explicit stack never raises.

PARAMETER DESCRIPTION
steps

The budget that was exhausted; kept on the error as steps.

TYPE: int

Source code in src/quivers/qiec/evaluator.py
def __init__(self, steps: int) -> None:
    super().__init__(f"QIEC evaluation exceeded its budget of {steps} steps")
    self.steps = steps

HandlerManifest dataclass

HandlerManifest(definitions: Mapping[HandlerId, HandlerDef])

Immutable checked handler definitions accepted by an evaluator.

Construct this from a :class:KernelRegistry after its declarations have passed kernel registration. Runtime clause objects are then accepted only when their complete structural definitions equal the manifest entry, not merely when their stable IDs collide.

PARAMETER DESCRIPTION
definitions

The checked handler definitions by identity.

TYPE: Mapping[HandlerId, HandlerDef]

__post_init__

__post_init__() -> None

Freeze the definitions behind a read-only view.

The manifest is what a runtime resolves a handler through, so it is copied and wrapped rather than aliased: a caller mutating the mapping it passed in would otherwise change which handler a running computation reaches.

Source code in src/quivers/qiec/evaluator.py
def __post_init__(self) -> None:
    """Freeze the definitions behind a read-only view.

    The manifest is what a runtime resolves a handler through, so it
    is copied and wrapped rather than aliased: a caller mutating the
    mapping it passed in would otherwise change which handler a
    running computation reaches.
    """
    object.__setattr__(
        self,
        "definitions",
        MappingProxyType(dict(self.definitions)),
    )

from_registry classmethod

from_registry(registry: KernelRegistry) -> HandlerManifest

Build a manifest from a checked registry.

PARAMETER DESCRIPTION
registry

A registry whose handlers have already been checked.

TYPE: KernelRegistry

RETURNS DESCRIPTION
HandlerManifest

A manifest over those handlers.

Source code in src/quivers/qiec/evaluator.py
@classmethod
def from_registry(cls, registry: KernelRegistry) -> HandlerManifest:
    """Build a manifest from a checked registry.

    Parameters
    ----------
    registry : KernelRegistry
        A registry whose handlers have already been checked.

    Returns
    -------
    HandlerManifest
        A manifest over those handlers.
    """
    return cls(registry.handlers)

InvalidHandlerError

Bases: EvaluationError

A runtime handler contradicts its checked structural signature.

MissingAttachmentError

Bases: EvaluationError

A stable runtime reference has no value or handler attachment.

NonDuplicableContinuationError

Bases: ResumptionUsageError

An unrestricted clause captured state that cannot safely be copied.

Resumption

Resumption(evaluator: Evaluator, request: RuntimeRequest, grade: ResumptionGrade, captured_environment: Mapping[Local, object], captured_stack: tuple[ContinuationFrame, ...], outer_stack: tuple[ContinuationFrame, ...], validator: RuntimeValidator, path: tuple[int, ...])

Dynamically checked, delimited continuation supplied to one clause.

PARAMETER DESCRIPTION
evaluator

The evaluator that captured the continuation.

TYPE: Evaluator

request

The request whose result the continuation awaits.

TYPE: RuntimeRequest

grade

How often the clause may invoke the continuation.

TYPE: ResumptionGrade

captured_environment

The bindings in scope at the request.

TYPE: Mapping[Local, object]

captured_stack

The continuation frames between the request and the handler.

TYPE: tuple[ContinuationFrame, ...]

outer_stack

The continuation frames outside the handler.

TYPE: tuple[ContinuationFrame, ...]

validator

The predicate every resumed value must pass.

TYPE: RuntimeValidator

path

The ordinal of each resumption taken to reach the clause.

TYPE: tuple[int, ...]

Source code in src/quivers/qiec/evaluator.py
def __init__(
    self,
    evaluator: Evaluator,
    request: RuntimeRequest,
    grade: ResumptionGrade,
    captured_environment: Mapping[Local, object],
    captured_stack: tuple[ContinuationFrame, ...],
    outer_stack: tuple[ContinuationFrame, ...],
    validator: RuntimeValidator,
    path: tuple[int, ...],
) -> None:
    self._evaluator = evaluator
    self._request = request
    self._grade = grade
    self._captured_environment = captured_environment
    self._captured_stack = _frames(captured_stack)
    self._outer_stack = _frames(outer_stack)
    self._validator = validator
    self._path = path
    self._calls = 0
    self._closed = False
    delimiters = tuple(
        index
        for index, frame in enumerate(self._captured_stack)
        if isinstance(frame, _DelimiterFrame)
    )
    self._owned_start = delimiters[-1] + 1 if delimiters else 0
    if grade is ResumptionGrade.UNRESTRICTED:
        evaluator._validate_duplicable_capture(
            captured_environment,
            self._captured_stack[self._owned_start :],
        )
        # Snapshot before the clause body runs. Every shot, including the
        # first, forks from this untouched seed rather than a previous
        # branch or the live handler context.
        self._seed_stack = self._fork_owned(self._captured_stack)
    else:
        self._seed_stack = self._captured_stack

grade property

The grade this resumption is held to.

RETURNS DESCRIPTION
ResumptionGrade

The declared grade, checked statically and again here.

calls property

calls: int

How many times this resumption has been invoked so far.

RETURNS DESCRIPTION
int

The count, which the grade bounds.

__call__

__call__(value: object) -> object

Resume the suspended computation with a value.

The static analysis already rejects a clause that can exceed its grade. This guard stays because it protects against a foreign handler, whose body the analysis never saw, and against a compiler bug: a grade is what lets a runtime discard a continuation, so exceeding it is not recoverable.

PARAMETER DESCRIPTION
value

What the resumed operation supplies.

TYPE: object

RETURNS DESCRIPTION
object

What the resumed computation produced.

RAISES DESCRIPTION
ResumptionUsageError

If the grade is zero, or an affine or linear resumption is invoked more than once.

NonDuplicableContinuationError

If an unrestricted resumption would have to copy a captured value that is not duplicable.

RuntimeValidationError

If the value does not inhabit the operation's result type.

Source code in src/quivers/qiec/evaluator.py
def __call__(self, value: object) -> object:
    """Resume the suspended computation with a value.

    The static analysis already rejects a clause that can exceed its
    grade. This guard stays because it protects against a foreign
    handler, whose body the analysis never saw, and against a
    compiler bug: a grade is what lets a runtime discard a
    continuation, so exceeding it is not recoverable.

    Parameters
    ----------
    value : object
        What the resumed operation supplies.

    Returns
    -------
    object
        What the resumed computation produced.

    Raises
    ------
    ResumptionUsageError
        If the grade is zero, or an affine or linear resumption is
        invoked more than once.
    NonDuplicableContinuationError
        If an unrestricted resumption would have to copy a captured
        value that is not duplicable.
    RuntimeValidationError
        If the value does not inhabit the operation's result type.
    """
    captured, path = self._begin_shot(value)
    # Outer handlers remain visible to effects performed by the resumed
    # continuation, but the delimiter prevents normal completion from
    # consuming the continuation outside the handled expression.
    delimiter = _DelimiterFrame()
    stack = [*self._outer_stack, delimiter, *captured]
    protected = (
        *self._outer_stack,
        *captured[: self._owned_start],
    )
    try:
        return self._evaluator._drive_value(
            value,
            dict(self._captured_environment),
            stack,
            path,
            delimiter,
        )
    except _HostUnwind as unwind:
        if unwind.delimiter is not delimiter:
            raise
        return unwind.value
    finally:
        # A normally returned shot has already exited every copied frame.
        # Exceptional or delimited exits leave some frames live.  Lifecycle
        # guards make this final sweep exact-once in either case.
        self._evaluator._drop_local_handlers(
            (*captured[self._owned_start :], *stack),
            protected,
        )

ResumptionUsageError

Bases: EvaluationError

A handler clause used its resumption outside the declared grade.

RuntimeAttachments dataclass

RuntimeAttachments(values: dict[AttachmentId, AttachmentBinding] = dict(), handlers: dict[HandlerId, RuntimeHandler] = dict())

Process-local values and handlers keyed by stable core identifiers.

PARAMETER DESCRIPTION
values

Attached host values by attachment identity.

TYPE: dict[AttachmentId, AttachmentBinding] DEFAULT: dict()

handlers

Runtime handlers by handler identity.

TYPE: dict[HandlerId, RuntimeHandler] DEFAULT: dict()

bind_value

bind_value(attachment: AttachmentId, value: object, type: TypeExpr, *, validator: RuntimeValidator = _accept, duplicable: bool = False) -> AttachmentRef

Attach a host value and return its stable core reference.

PARAMETER DESCRIPTION
attachment

The identity the core term refers to the value by.

TYPE: AttachmentId

value

The host value.

TYPE: object

type

The type the core ascribes to it.

TYPE: TypeExpr

validator

Checks the value really inhabits that type. The default accepts anything, for a value the static check already settles.

TYPE: RuntimeValidator DEFAULT: _accept

duplicable

Whether the value may be copied. This is an assertion about the value, not a guess: a multi-shot resumption capturing a non-duplicable attachment is refused, because copying it would give two continuations a shared mutable thing.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
AttachmentRef

The core reference to use in a term.

RAISES DESCRIPTION
InvalidHandlerError

If the identity is already bound to a different value, type, validator, or duplicability. Rebinding is refused rather than overwriting, since a term already built would silently start meaning something else.

RuntimeValidationError

If the validator rejects the value.

Source code in src/quivers/qiec/evaluator.py
def bind_value(
    self,
    attachment: AttachmentId,
    value: object,
    type: TypeExpr,
    *,
    validator: RuntimeValidator = _accept,
    duplicable: bool = False,
) -> AttachmentRef:
    """Attach a host value and return its stable core reference.

    Parameters
    ----------
    attachment : AttachmentId
        The identity the core term refers to the value by.
    value : object
        The host value.
    type : TypeExpr
        The type the core ascribes to it.
    validator : RuntimeValidator
        Checks the value really inhabits that type. The default
        accepts anything, for a value the static check already
        settles.
    duplicable : bool
        Whether the value may be copied. This is an assertion about
        the value, not a guess: a multi-shot resumption capturing a
        non-duplicable attachment is refused, because copying it
        would give two continuations a shared mutable thing.

    Returns
    -------
    AttachmentRef
        The core reference to use in a term.

    Raises
    ------
    InvalidHandlerError
        If the identity is already bound to a different value, type,
        validator, or duplicability. Rebinding is refused rather than
        overwriting, since a term already built would silently start
        meaning something else.
    RuntimeValidationError
        If the validator rejects the value.
    """
    _validate(value, validator, type, f"attachment {attachment}")
    binding = AttachmentBinding(value, type, validator, duplicable)
    previous = self.values.get(attachment)
    if previous is not None:
        same_binding = (
            previous.value is value
            and previous.type == type
            and previous.validator is validator
            and previous.duplicable == duplicable
        )
        if not same_binding:
            raise InvalidHandlerError(
                f"runtime attachment {attachment} is already bound differently"
            )
    self.values[attachment] = binding
    return AttachmentRef(attachment, type)

bind_handler

bind_handler(handler: RuntimeHandler) -> HandlerId

Attach executable clauses to their structural handler identity.

PARAMETER DESCRIPTION
handler

The executable handler, already checked against its declaration.

TYPE: RuntimeHandler

RETURNS DESCRIPTION
HandlerId

The identity it was bound under.

RAISES DESCRIPTION
InvalidHandlerError

If a different handler is already bound to that identity.

Source code in src/quivers/qiec/evaluator.py
def bind_handler(self, handler: RuntimeHandler) -> HandlerId:
    """Attach executable clauses to their structural handler identity.

    Parameters
    ----------
    handler : RuntimeHandler
        The executable handler, already checked against its
        declaration.

    Returns
    -------
    HandlerId
        The identity it was bound under.

    Raises
    ------
    InvalidHandlerError
        If a different handler is already bound to that identity.
    """
    id = handler.definition.id
    previous = self.handlers.get(id)
    if previous is not None and previous is not handler:
        raise InvalidHandlerError(f"runtime handler {id} is already bound")
    self.handlers[id] = handler
    return id

RuntimeClause dataclass

RuntimeClause(invoke: OperationClause, result_validator: RuntimeValidator)

Executable operation clause and validator for the resumed value.

PARAMETER DESCRIPTION
invoke

The clause body, called with the request, its resumption, and a clause context.

TYPE: OperationClause

result_validator

The predicate every value resumed through this clause must pass.

TYPE: RuntimeValidator

RuntimeConstructor dataclass

RuntimeConstructor(constructor: object, static_arguments: tuple[object, ...], fields: tuple[object, ...], result_type: TypeExpr)

Erased runtime representation of a checked constructor value.

PARAMETER DESCRIPTION
constructor

The constructor's stable identity.

TYPE: object

static_arguments

The constructor's telescope instantiation, as evaluated static data.

TYPE: tuple[object, ...]

fields

The evaluated field values, in declaration order.

TYPE: tuple[object, ...]

result_type

The family type the value inhabits.

TYPE: TypeExpr

RuntimeHandler dataclass

RuntimeHandler(definition: HandlerDef, clauses: Mapping[OperationId, RuntimeClause], return_clause: ReturnClause = _identity_return, input_validator: RuntimeValidator = _accept, output_validator: RuntimeValidator = _accept, duplicable_context: bool = False, mutable_context: bool = False, fork_context: Callable[['RuntimeHandler'], 'RuntimeHandler'] | None = None, context_factory: Callable[[], 'RuntimeHandler'] | None = None, on_enter: Callable[[], None] | None = None, on_exit: Callable[[], None] | None = None, on_drop: Callable[[], None] | None = None)

Executable attachment for a structural :class:HandlerDef.

The checked definition contains coverage, grades, input/output types, and introduced effects. This attachment contains only process-local code and state. It is never serialized as QIEC.

PARAMETER DESCRIPTION
definition

The checked declaration this attachment implements.

TYPE: HandlerDef

clauses

One runtime clause per covered operation, by identity.

TYPE: Mapping[OperationId, RuntimeClause]

return_clause

How the handler answers the computation's return; the identity return by default.

TYPE: ReturnClause DEFAULT: _identity_return

input_validator

The predicate the handled computation's result must pass.

TYPE: RuntimeValidator DEFAULT: _accept

output_validator

The predicate the handler's answer must pass.

TYPE: RuntimeValidator DEFAULT: _accept

duplicable_context

Whether an unrestricted resumption may capture an installation of this handler.

TYPE: bool DEFAULT: False

mutable_context

Whether an installation carries state; such a handler needs a context_factory so installations do not share it.

TYPE: bool DEFAULT: False

fork_context

Builds an independent copy of an installation for another shot; required when the context is both mutable and duplicable.

TYPE: Callable[['RuntimeHandler'], 'RuntimeHandler'] | None DEFAULT: None

context_factory

Builds a fresh installation each time the handler is installed.

TYPE: Callable[[], 'RuntimeHandler'] | None DEFAULT: None

on_enter

Called when an installation is entered.

TYPE: Callable[[], None] | None DEFAULT: None

on_exit

Called once when the handled computation completes.

TYPE: Callable[[], None] | None DEFAULT: None

on_drop

Called once when the handled computation is abandoned instead; exclusive with on_exit.

TYPE: Callable[[], None] | None DEFAULT: None

__post_init__

__post_init__() -> None

Check the executable clauses against the checked declaration.

RAISES DESCRIPTION
InvalidHandlerError

If the runtime implements an operation the declaration does not cover, or leaves a declared one unimplemented. Both directions matter: an extra clause would never be dispatched to, and a missing one would fail only when that operation was first performed, perhaps long after the handler was installed.

Source code in src/quivers/qiec/evaluator.py
def __post_init__(self) -> None:
    """Check the executable clauses against the checked declaration.

    Raises
    ------
    InvalidHandlerError
        If the runtime implements an operation the declaration does
        not cover, or leaves a declared one unimplemented. Both
        directions matter: an extra clause would never be dispatched
        to, and a missing one would fail only when that operation was
        first performed, perhaps long after the handler was
        installed.
    """
    structural = {clause.operation for clause in self.definition.clauses}
    executable = set(self.clauses)
    unknown = executable - structural
    if unknown:
        rendered = ", ".join(str(operation) for operation in sorted(unknown))
        raise InvalidHandlerError(
            f"runtime handler {self.definition.name!r} implements undeclared "
            f"clauses: {rendered}"
        )
    missing = structural - executable
    if missing:
        rendered = ", ".join(str(operation) for operation in sorted(missing))
        raise InvalidHandlerError(
            f"runtime handler {self.definition.name!r} has no attachment for "
            f"declared clauses: {rendered}"
        )
    if self.fork_context is not None and not self.duplicable_context:
        raise InvalidHandlerError(
            "a handler with fork_context must declare a duplicable context"
        )
    if (
        self.mutable_context
        and self.duplicable_context
        and self.fork_context is None
    ):
        raise InvalidHandlerError("a mutable duplicable handler needs fork_context")

RuntimeRequest dataclass

RuntimeRequest(core: EffectRequest, arguments: tuple[object, ...], resumption_path: tuple[int, ...], dynamic_path: tuple[DynamicAddressFrame, ...] = ())

An evaluated request supplied to a process-local handler clause.

PARAMETER DESCRIPTION
core

The stable request as written, before evaluation.

TYPE: EffectRequest

arguments

The evaluated value arguments, in order.

TYPE: tuple[object, ...]

resumption_path

The ordinal of each resumption taken to reach this request.

TYPE: tuple[int, ...]

dynamic_path

The call and local-instance frames live when the request was performed, outermost first, so one site reached through two recursive iterations yields two addresses.

TYPE: tuple[DynamicAddressFrame, ...] DEFAULT: ()

instance property

instance: EffectInstanceId

The lexical instance this request was performed on.

RETURNS DESCRIPTION
EffectInstanceId

The instance, which decides which handler answers.

operation property

operation: OperationId

The operation requested.

RETURNS DESCRIPTION
OperationId

The operation, which decides which clause answers.

address property

address: tuple[str, tuple[tuple[str, str | int], ...], tuple[int, ...]]

Return the static, dynamic, and multi-shot address components.

RETURNS DESCRIPTION
tuple[str, tuple[tuple[str, str | int], ...], tuple[int, ...]]

The source site, the dynamic address frames, and the resumption path. One request site reached twice, under a loop or under a resumed continuation, yields two addresses, which is what lets a trace name each occurrence separately.

RuntimeTypeMismatch

Bases: EvaluationError

A host result failed the validator for its QIEC result type.

TailResume dataclass

TailResume(value: object)

Resume a clause's continuation in place, on the machine's own stack.

A foreign clause that answers with this resumes once, exactly as an authored resume(value) in tail position does: the captured frames return to the machine's stack and the handler's return clause applies when the computation completes. Unlike calling the resumption from the clause, which drives the continuation in a nested host call that stays pending until it returns, this leaves no host frame behind, so a multi-shot resumption captured later inside the same handler may run the continuation any number of times.

PARAMETER DESCRIPTION
value

What the resumed operation supplies.

TYPE: object

UnhandledEffectError

UnhandledEffectError(request: EffectRequest)

Bases: EvaluationError

No enclosing lexical handler accepts an effect request.

PARAMETER DESCRIPTION
request

The request nothing handled; kept on the error as request.

TYPE: EffectRequest

Source code in src/quivers/qiec/evaluator.py
def __init__(self, request: EffectRequest) -> None:
    super().__init__(
        "unhandled QIEC operation "
        f"{request.operation} on instance {request.instance}"
    )
    self.request = request

UnknownComputationError

Bases: EvaluationError

A call names a computation the evaluator was not given a body for.

BranchGiven dataclass

BranchGiven(id: EqualityId, equality: EqualityType, tag: Literal['branch_given'] = 'branch_given')

Evidence granted by a case branch's constructor refinement.

PARAMETER DESCRIPTION
id

The stable identity of the given, derived from the branch scope.

TYPE: EqualityId

equality

The proposition the branch's constructor makes available.

TYPE: EqualityType

tag

The serialization discriminator; always "branch_given".

TYPE: Literal['branch_given'] DEFAULT: 'branch_given'

Reflexivity dataclass

Reflexivity(equality: EqualityType, tag: Literal['reflexivity'] = 'reflexivity')

Evidence that an equality holds because both sides are the same term.

PARAMETER DESCRIPTION
equality

The proposition witnessed, whose sides must be equal.

TYPE: EqualityType

tag

The serialization discriminator; always "reflexivity".

TYPE: Literal['reflexivity'] DEFAULT: 'reflexivity'

CoreRuntimeProvider dataclass

CoreRuntimeProvider(options: Mapping[str, object] = dict(), name: str = 'core')

Primitive validators plus explicitly configured structural handlers.

options.handlers maps authored handler names to either {"kind": "passthrough"}, {"kind": "state", "initial": ...}, or {"kind": "scripted", "responses": {"op": [...]}}. The source handler still controls types, coverage, and resumption grades; options supply only process-local clause behavior and state.

PARAMETER DESCRIPTION
options

JSON-compatible configuration; frozen on construction.

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

name

The provider's name, reported in traces and result labels.

TYPE: str DEFAULT: 'core'

__post_init__

__post_init__() -> None

Freeze options so a provider's configuration cannot drift.

Source code in src/quivers/qiec/execution.py
def __post_init__(self) -> None:
    """Freeze ``options`` so a provider's configuration cannot drift."""
    object.__setattr__(self, "options", MappingProxyType(dict(self.options)))

attach

attach(module: QiecModule, attachments: RuntimeAttachments) -> None

Bind one structural handler per configured authored handler.

PARAMETER DESCRIPTION
module

The validated module whose handlers the configuration names.

TYPE: QiecModule

attachments

The per-invocation table receiving the constructed handlers.

TYPE: RuntimeAttachments

RAISES DESCRIPTION
ValueError

If options.handlers is not an object, names a handler the module does not declare, holds a non-object configuration, or requests a kind other than passthrough, scripted, state, or one of the prelude kinds draw, draw-scoring (with the option score_instance), enumerate (with the options grouped and reduction), replay, collect (with the options combine and forkable), score, search (with the option reduction), and param (with the option values).

Source code in src/quivers/qiec/execution.py
def attach(self, module: QiecModule, attachments: RuntimeAttachments) -> None:
    """Bind one structural handler per configured authored handler.

    Parameters
    ----------
    module
        The validated module whose handlers the configuration names.
    attachments
        The per-invocation table receiving the constructed handlers.

    Raises
    ------
    ValueError
        If ``options.handlers`` is not an object, names a handler the module
        does not declare, holds a non-object configuration, or requests a
        kind other than ``passthrough``, ``scripted``, ``state``, or one
        of the prelude kinds ``draw``, ``draw-scoring`` (with the option
        ``score_instance``), ``enumerate`` (with the options
        ``grouped`` and ``reduction``), ``replay``, ``collect`` (with
        the options ``combine`` and ``forkable``), ``score``, ``search`` (with the
        option ``reduction``), and ``param`` (with the option
        ``values``).
    """
    configured = self.options.get("handlers", {})
    if not isinstance(configured, Mapping):
        raise ValueError("core runtime option 'handlers' must be an object")
    definitions = {handler.name: handler for handler in module.handlers}
    unknown = set(configured) - set(definitions)
    if unknown:
        raise ValueError(
            f"core runtime names unknown handlers: {', '.join(sorted(unknown))}"
        )
    operation_names = {
        operation.id: operation.name
        for effect in module.effects
        for operation in effect.operations
    }
    for handler_name, raw_options in configured.items():
        if not isinstance(handler_name, str) or not isinstance(
            raw_options, Mapping
        ):
            raise ValueError("each core handler configuration must be an object")
        definition = definitions[handler_name]
        kind = raw_options.get("kind")
        if kind == "passthrough":
            runtime = self._passthrough(definition, raw_options, operation_names)
        elif kind == "scripted":
            runtime = self._scripted(definition, raw_options, operation_names)
        elif kind == "state":
            runtime = self._state(definition, raw_options, operation_names)
        elif kind == "draw":
            runtime = self._prelude(definition, draw_handler, "Random")
        elif kind == "draw-scoring":
            score_instance = _replay_score_instance(raw_options)
            if score_instance is None:
                raise ValueError(
                    f"core handler {definition.name!r} of kind 'draw-scoring' "
                    "needs a `score_instance`"
                )
            runtime = self._prelude(
                definition,
                lambda result_validator, answer_type: draw_scoring_handler(
                    score_instance=score_instance,
                    result_validator=result_validator,
                    answer_type=answer_type,
                ),
                "Random",
            )
        elif kind == "enumerate":
            runtime = self._prelude(
                definition,
                lambda result_validator, answer_type: enumerate_handler(
                    result_validator,
                    answer_type=answer_type,
                    weight_type=(
                        definition.output_type
                        if _enumerate_grouped(raw_options, definition.name)
                        else None
                    ),
                    reduction=_enumerate_reduction(raw_options, definition.name),
                ),
                "Random",
            )
        elif kind == "replay":
            runtime = self._prelude(
                definition,
                lambda result_validator, answer_type: replay_handler(
                    _replay_values(raw_options, definition.name),
                    result_validator=result_validator,
                    policy=_replay_policy(raw_options, definition.name),
                    score_instance=_replay_score_instance(raw_options),
                    answer_type=answer_type,
                ),
                "Random",
            )
        elif kind == "collect":
            identity, combine = _collect_combine(raw_options, definition.name)
            forkable = raw_options.get("forkable", False)
            if not isinstance(forkable, bool):
                raise ValueError(
                    f"core handler {definition.name!r} option 'forkable' must "
                    "be a boolean"
                )
            runtime = self._prelude(
                definition,
                lambda **_: weight_handler(
                    identity,
                    combine,
                    _weights_validator(_weight_type(definition)),
                    weight_type=_weight_type(definition),
                    answer_type=definition.input_type,
                    answer_validator=_weights_validator(definition.input_type),
                    forkable=forkable,
                )[0],
                "Weight",
            )
        elif kind == "search":
            runtime = self._search(definition, raw_options)
        elif kind == "param":
            runtime = self._param(definition, raw_options)
        elif kind == "score":
            runtime = self._prelude(
                definition,
                lambda **_: score_handler(
                    weight_validator=_require_core_handler_validator(
                        LOG_WEIGHT, definition.name, "weight"
                    ),
                    answer_validator=_require_core_handler_validator(
                        definition.input_type, definition.name, "input"
                    ),
                    expose_total=definition.output_type != definition.input_type,
                )[0],
                "Score",
            )
        else:
            raise ValueError(
                f"core handler {handler_name!r} has unknown kind {kind!r}"
            )
        attachments.bind_handler(runtime)

validator_for

validator_for(type_: TypeExpr) -> RuntimeValidator | None

Supply the primitive validator for a closed runtime type.

PARAMETER DESCRIPTION
type_

The closed QIEC type an argument or result must inhabit.

TYPE: TypeExpr

RETURNS DESCRIPTION
RuntimeValidator | None

The core validator, or None for types the core runtime cannot check on its own.

Source code in src/quivers/qiec/execution.py
def validator_for(self, type_: TypeExpr) -> RuntimeValidator | None:
    """Supply the primitive validator for a closed runtime type.

    Parameters
    ----------
    type_
        The closed QIEC type an argument or result must inhabit.

    Returns
    -------
    RuntimeValidator | None
        The core validator, or ``None`` for types the core runtime cannot
        check on its own.
    """
    return _core_validator(type_)

ExecutionDiagnostic dataclass

ExecutionDiagnostic(code: str, message: str, computation: str | None = None, origin: SourceOrigin | None = None, severity: Literal['error', 'warning', 'note'] = 'error')

A stable failure at the source-to-host execution boundary.

PARAMETER DESCRIPTION
code

The stable diagnostic code, such as "qiec-run-arity".

TYPE: str

message

The human-readable explanation.

TYPE: str

computation

The named computation involved, if any.

TYPE: str | None DEFAULT: None

origin

The source location involved, if any.

TYPE: SourceOrigin | None DEFAULT: None

severity

"error", "warning", or "note".

TYPE: Literal['error', 'warning', 'note'] DEFAULT: 'error'

to_data

to_data() -> dict[str, object]

Render the diagnostic as JSON-compatible data.

RETURNS DESCRIPTION
dict[str, object]

The code, severity, message, computation name, and origin (or None when the failure has no source location).

Source code in src/quivers/qiec/execution.py
def to_data(self) -> dict[str, object]:
    """Render the diagnostic as JSON-compatible data.

    Returns
    -------
    dict[str, object]
        The code, severity, message, computation name, and origin (or
        ``None`` when the failure has no source location).
    """
    return {
        "code": self.code,
        "severity": self.severity,
        "message": self.message,
        "computation": self.computation,
        "origin": self.origin.to_data() if self.origin is not None else None,
    }

ExecutionFailure

ExecutionFailure(diagnostic: ExecutionDiagnostic)

Bases: EvaluationError

Raised with a serializable diagnostic when named execution cannot run.

PARAMETER DESCRIPTION
diagnostic

The failure, kept on the error as diagnostic; its message becomes the error's message.

TYPE: ExecutionDiagnostic

Source code in src/quivers/qiec/execution.py
def __init__(self, diagnostic: ExecutionDiagnostic) -> None:
    super().__init__(diagnostic.message)
    self.diagnostic = diagnostic

ExecutionResult dataclass

ExecutionResult(computation: str, value: object, result_type: TypeExpr, runtime: str, trace: tuple[ExecutionTraceEvent, ...])

The checked value and trace produced by one named invocation.

PARAMETER DESCRIPTION
computation

The name of the computation run.

TYPE: str

value

The validated host result.

TYPE: object

result_type

The computation's result type under its specialization.

TYPE: TypeExpr

runtime

The label of the runtime configuration used.

TYPE: str

trace

Every event the invocation emitted, in order.

TYPE: tuple[ExecutionTraceEvent, ...]

to_data

to_data() -> dict[str, object]

Render the result as JSON-compatible data.

RETURNS DESCRIPTION
dict[str, object]

ok set to True, the computation name, the JSON-compatible value, the rendered result type, the runtime label, and the trace.

Source code in src/quivers/qiec/execution.py
def to_data(self) -> dict[str, object]:
    """Render the result as JSON-compatible data.

    Returns
    -------
    dict[str, object]
        ``ok`` set to ``True``, the computation name, the JSON-compatible
        value, the rendered result type, the runtime label, and the trace.
    """
    return {
        "ok": True,
        "computation": self.computation,
        "value": _json_value(self.value),
        "result_type": _render_type(self.result_type),
        "runtime": self.runtime,
        "trace": [event.to_data() for event in self.trace],
    }

ExecutionTraceEvent dataclass

ExecutionTraceEvent(sequence: int, event: str, computation: str, detail: Mapping[str, object] = dict())

One stable event emitted by a named QIEC invocation.

PARAMETER DESCRIPTION
sequence

The event's position in the invocation's trace, from zero.

TYPE: int

event

The stable event name, such as "run.started".

TYPE: str

computation

The name of the computation being run.

TYPE: str

detail

JSON-compatible event data; frozen on construction.

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

__post_init__

__post_init__() -> None

Freeze detail so a recorded event cannot change after emission.

Source code in src/quivers/qiec/execution.py
def __post_init__(self) -> None:
    """Freeze ``detail`` so a recorded event cannot change after emission."""
    object.__setattr__(self, "detail", MappingProxyType(dict(self.detail)))

to_data

to_data() -> dict[str, object]

Render the event as JSON-compatible data.

RETURNS DESCRIPTION
dict[str, object]

The sequence number, event name, computation name, and detail mapping, with every detail value made JSON-compatible.

Source code in src/quivers/qiec/execution.py
def to_data(self) -> dict[str, object]:
    """Render the event as JSON-compatible data.

    Returns
    -------
    dict[str, object]
        The sequence number, event name, computation name, and detail
        mapping, with every detail value made JSON-compatible.
    """
    return {
        "sequence": self.sequence,
        "event": self.event,
        "computation": self.computation,
        "detail": _json_value(dict(self.detail)),
    }

ExecutionTraceRecorder dataclass

ExecutionTraceRecorder(events: list[ExecutionTraceEvent] = list())

Collect events while also satisfying the :class:TraceObserver API.

PARAMETER DESCRIPTION
events

The recorded events, in order; empty to start.

TYPE: list[ExecutionTraceEvent] DEFAULT: list()

__call__

__call__(event: ExecutionTraceEvent) -> None

Record one event.

PARAMETER DESCRIPTION
event

The event the invocation just emitted.

TYPE: ExecutionTraceEvent

Source code in src/quivers/qiec/execution.py
def __call__(self, event: ExecutionTraceEvent) -> None:
    """Record one event.

    Parameters
    ----------
    event
        The event the invocation just emitted.
    """
    self.events.append(event)

RuntimeConfiguration dataclass

RuntimeConfiguration(selections: tuple[RuntimeSelection, ...] = (RuntimeSelection('core'),), providers: tuple[RuntimeProvider, ...] = ())

Complete, explicit runtime selection for one or more invocations.

PARAMETER DESCRIPTION
selections

Providers to build by name for every invocation; the core provider alone by default.

TYPE: tuple[RuntimeSelection, ...] DEFAULT: (RuntimeSelection('core'),)

providers

Already-built provider objects, appended after the selections when used from Python.

TYPE: tuple[RuntimeProvider, ...] DEFAULT: ()

label property

label: str

A +-joined label naming every selected and supplied provider.

RETURNS DESCRIPTION
str

The provider names in order, or "detached" when there are none.

instantiate

instantiate() -> tuple[RuntimeProvider, ...]

Build every named provider and append the programmatic ones.

RETURNS DESCRIPTION
tuple[RuntimeProvider, ...]

The providers in selection order, followed by providers.

RAISES DESCRIPTION
ExecutionFailure

With code qiec-run-provider if a selection names an unknown provider, its factory cannot be loaded, or the factory rejects the selection's options.

Source code in src/quivers/qiec/execution.py
def instantiate(self) -> tuple[RuntimeProvider, ...]:
    """Build every named provider and append the programmatic ones.

    Returns
    -------
    tuple[RuntimeProvider, ...]
        The providers in selection order, followed by ``providers``.

    Raises
    ------
    ExecutionFailure
        With code ``qiec-run-provider`` if a selection names an unknown
        provider, its factory cannot be loaded, or the factory rejects the
        selection's options.
    """
    providers: list[RuntimeProvider] = []
    for selection in self.selections:
        try:
            factory = _provider_factory(selection.provider)
        except KeyError as error:
            available = ", ".join(available_runtime_providers()) or "(none)"
            raise ExecutionFailure(
                ExecutionDiagnostic(
                    "qiec-run-provider",
                    f"unknown runtime provider {selection.provider!r}; "
                    f"available providers: {available}",
                )
            ) from error
        except Exception as error:
            raise ExecutionFailure(
                ExecutionDiagnostic(
                    "qiec-run-provider",
                    f"could not load runtime provider {selection.provider!r}: {error}",
                )
            ) from error
        try:
            providers.append(factory(selection.options))
        except Exception as error:
            raise ExecutionFailure(
                ExecutionDiagnostic(
                    "qiec-run-provider",
                    f"runtime provider {selection.provider!r} rejected its "
                    f"configuration: {error}",
                )
            ) from error
    providers.extend(self.providers)
    return tuple(providers)

to_data

to_data() -> dict[str, object]

Render the configuration as JSON-compatible data.

RETURNS DESCRIPTION
dict[str, object]

The named selections with their options, and the names of any programmatic providers.

Source code in src/quivers/qiec/execution.py
def to_data(self) -> dict[str, object]:
    """Render the configuration as JSON-compatible data.

    Returns
    -------
    dict[str, object]
        The named selections with their options, and the names of any
        programmatic providers.
    """
    return {
        "providers": [
            {"name": selection.provider, "options": dict(selection.options)}
            for selection in self.selections
        ],
        "programmatic_providers": [provider.name for provider in self.providers],
    }

RuntimeProvider

Bases: Protocol

An explicit source of process-local handlers and type validators.

name property

name: str

The provider's stable name, reported in traces and result labels.

attach

attach(module: QiecModule, attachments: RuntimeAttachments) -> None

Bind this provider's handlers into a fresh attachment table.

PARAMETER DESCRIPTION
module

The validated module whose handlers and effects are being served.

TYPE: QiecModule

attachments

The per-invocation table receiving runtime handlers.

TYPE: RuntimeAttachments

Source code in src/quivers/qiec/execution.py
def attach(self, module: QiecModule, attachments: RuntimeAttachments) -> None:
    """Bind this provider's handlers into a fresh attachment table.

    Parameters
    ----------
    module
        The validated module whose handlers and effects are being served.
    attachments
        The per-invocation table receiving runtime handlers.
    """
    ...

validator_for

validator_for(type_: TypeExpr) -> RuntimeValidator | None

Supply a host validator for a runtime type, if this provider has one.

PARAMETER DESCRIPTION
type_

The closed QIEC type an argument or result must inhabit.

TYPE: TypeExpr

RETURNS DESCRIPTION
RuntimeValidator | None

A predicate over host values, or None when this provider does not cover the type.

Source code in src/quivers/qiec/execution.py
def validator_for(self, type_: TypeExpr) -> RuntimeValidator | None:
    """Supply a host validator for a runtime type, if this provider has one.

    Parameters
    ----------
    type_
        The closed QIEC type an argument or result must inhabit.

    Returns
    -------
    RuntimeValidator | None
        A predicate over host values, or ``None`` when this provider does
        not cover the type.
    """
    ...

RuntimeSelection dataclass

RuntimeSelection(provider: str, options: Mapping[str, object] = dict())

One named provider and its JSON-compatible construction options.

PARAMETER DESCRIPTION
provider

The registered or entry-point name of the provider; must be non-empty.

TYPE: str

options

The options passed to the provider's factory; frozen on construction.

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

__post_init__

__post_init__() -> None

Reject empty provider names and freeze the options.

RAISES DESCRIPTION
ValueError

If provider is empty.

Source code in src/quivers/qiec/execution.py
def __post_init__(self) -> None:
    """Reject empty provider names and freeze the options.

    Raises
    ------
    ValueError
        If ``provider`` is empty.
    """
    if not self.provider:
        raise ValueError("runtime provider name cannot be empty")
    object.__setattr__(self, "options", MappingProxyType(dict(self.options)))

DistributionFamily dataclass

DistributionFamily(name: str, parameters: tuple[FamilyParameter, ...], support: SupportKind, element: TypeExpr, event_rank: int, event_source: str | None, discrete: bool, reparameterizable: bool, compositional: bool, targets: Mapping[str, str] = dict(), finite_support: tuple[object, ...] | None = None, relaxes: tuple[str, tuple[object, ...]] | None = None)

The semantic record of one distribution family.

PARAMETER DESCRIPTION
name

The family's source name, such as "Normal".

TYPE: str

parameters

The parameters, in the order a positional application supplies them. Alternative parameterizations, such as probs and logits, are both listed; an application supplies one.

TYPE: tuple[FamilyParameter, ...]

support

Where samples live.

TYPE: SupportKind

element

The scalar type of one sample element.

TYPE: TypeExpr

event_rank

The rank of one sample: 0 for a scalar family, 1 for a vector family, 2 for a matrix family.

TYPE: int

event_source

The parameter whose trailing event_rank dimensions fix the sample's event shape, or None when the family is scalar or the shape comes from elsewhere.

TYPE: str | None

discrete

Whether samples are discrete.

TYPE: bool

reparameterizable

Whether a sample can be drawn as a differentiable function of the parameters.

TYPE: bool

compositional

Whether the family is built from other distributions or morphisms rather than from tensor parameters alone.

TYPE: bool

targets

The spelling each transpile target uses for the family.

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

finite_support

The finite set of values a sample ranges over, when the family has one independent of its parameters.

TYPE: tuple[object, ...] | None DEFAULT: None

relaxes

The discrete family a continuous relaxation stands in for, and the atoms of that family's support as the relaxation's samples spell them: a marginalization over the relaxation enumerates those atoms under the discrete family's masses, as the torch runtime does.

TYPE: tuple[str, tuple[object, ...]] | None DEFAULT: None

id property

The family's stable identity.

RETURNS DESCRIPTION
DistributionId

An identity derived from the builtin namespace and the name, the same in every module.

parameter_names property

parameter_names: tuple[str, ...]

The parameter names in positional order.

RETURNS DESCRIPTION
tuple[str, ...]

The names.

__post_init__

__post_init__() -> None

Freeze the target spellings and check the parameter names.

RAISES DESCRIPTION
ValueError

If two parameters share a name, or event_source names no parameter.

Source code in src/quivers/qiec/families.py
def __post_init__(self) -> None:
    """Freeze the target spellings and check the parameter names.

    Raises
    ------
    ValueError
        If two parameters share a name, or ``event_source`` names no
        parameter.
    """
    object.__setattr__(self, "targets", MappingProxyType(dict(self.targets)))
    names = [parameter.name for parameter in self.parameters]
    if len(set(names)) != len(names):
        raise ValueError(f"family {self.name!r} repeats a parameter name")
    if self.event_source is not None and self.event_source not in names:
        raise ValueError(
            f"family {self.name!r} names event source "
            f"{self.event_source!r}, which is not a parameter"
        )

parameter

parameter(name: str) -> FamilyParameter

Look up a parameter by name.

PARAMETER DESCRIPTION
name

The parameter's name.

TYPE: str

RETURNS DESCRIPTION
FamilyParameter

The parameter.

RAISES DESCRIPTION
KeyError

If the family has no such parameter.

Source code in src/quivers/qiec/families.py
def parameter(self, name: str) -> FamilyParameter:
    """Look up a parameter by name.

    Parameters
    ----------
    name : str
        The parameter's name.

    Returns
    -------
    FamilyParameter
        The parameter.

    Raises
    ------
    KeyError
        If the family has no such parameter.
    """
    for parameter in self.parameters:
        if parameter.name == name:
            return parameter
    raise KeyError(f"family {self.name!r} has no parameter {name!r}")

FamilyParameter dataclass

FamilyParameter(name: str, constraint: ParameterConstraint, rank: int)

One parameter of a distribution family.

PARAMETER DESCRIPTION
name

The parameter's name, as the source spells it by keyword.

TYPE: str

constraint

The symbolic constraint on admissible values.

TYPE: ParameterConstraint

rank

The tensor rank of one parameter value: 0 for a scalar, 1 for a vector, 2 for a matrix.

TYPE: int

AttachmentId dataclass

AttachmentId(digest: str)

Bases: StableId

Stable identity of a runtime attachment.

ComputationId dataclass

ComputationId(digest: str)

Bases: StableId

Stable identity of a named computation.

ConstructorId dataclass

ConstructorId(digest: str)

Bases: StableId

Stable identity of a family constructor.

DistributionId dataclass

DistributionId(digest: str)

Bases: StableId

Stable identity of a distribution family.

DynamicAddressFrame dataclass

DynamicAddressFrame(scope: str, key: str | int)

One serializable component of a dynamic effect address.

PARAMETER DESCRIPTION
scope

What kind of frame this is, such as a call, a handler clause, a resumption, or a local instance.

TYPE: str

key

The frame's position within its scope: a name or an ordinal.

TYPE: str | int

EffectId dataclass

EffectId(digest: str)

Bases: StableId

Stable identity of an effect interface.

EffectInstanceId dataclass

EffectInstanceId(digest: str)

Bases: StableId

Stable identity of an effect instance.

EqualityId dataclass

EqualityId(digest: str)

Bases: StableId

Stable identity of an equality proposition.

FamilyId dataclass

FamilyId(digest: str)

Bases: StableId

Stable identity of an indexed type family.

HandlerId dataclass

HandlerId(digest: str)

Bases: StableId

Stable identity of a handler.

OperationId dataclass

OperationId(digest: str)

Bases: StableId

Stable identity of an effect operation.

PrimitiveId dataclass

PrimitiveId(digest: str)

Bases: StableId

Stable identity of a pure primitive.

RowVariableId dataclass

RowVariableId(digest: str)

Bases: StableId

Stable identity of a row variable.

SiteId dataclass

SiteId(digest: str)

Bases: StableId

Stable identity of a source site.

SiteProvenance dataclass

SiteProvenance(origin: SourceOrigin, dynamic_path: tuple[DynamicAddressFrame, ...] = (), resumption_path: tuple[int, ...] = (), relation: AddressRelation = 'preserve', parents: tuple[SiteId, ...] = ())

Static and dynamic provenance of an effect request.

PARAMETER DESCRIPTION
origin

The source site the request was written at.

TYPE: SourceOrigin

dynamic_path

The address frames entered to reach this occurrence of the site, outermost first.

TYPE: tuple[DynamicAddressFrame, ...] DEFAULT: ()

resumption_path

The ordinal of each resumption taken to reach this occurrence, so a site reached twice under a multi-shot handler yields two keys.

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

relation

How this request relates to the source site: "preserve" for the request as written, "split" or "duplicate" for a request a handler derived from it, and "eliminate" for one it discharged.

TYPE: AddressRelation DEFAULT: 'preserve'

parents

The static sites a derived request was generated from.

TYPE: tuple[SiteId, ...] DEFAULT: ()

static_site property

static_site: SiteId

The source site this request was written at.

RETURNS DESCRIPTION
SiteId

The static identity, shared by every dynamic occurrence of the request.

dynamic_key

dynamic_key() -> tuple[str, tuple[tuple[str, str | int], ...], tuple[int, ...]]

A hashable key distinguishing occurrences of one static site.

RETURNS DESCRIPTION
tuple[str, tuple[tuple[str, str | int], ...], tuple[int, ...]]

The static site, the dynamic address frames, and the resumption path. One site reached twice, under a loop or under a resumed continuation, yields two keys, which is what lets a trace name each occurrence separately.

Source code in src/quivers/qiec/identifiers.py
def dynamic_key(
    self,
) -> tuple[str, tuple[tuple[str, str | int], ...], tuple[int, ...]]:
    """A hashable key distinguishing occurrences of one static site.

    Returns
    -------
    tuple[str, tuple[tuple[str, str | int], ...], tuple[int, ...]]
        The static site, the dynamic address frames, and the
        resumption path. One site reached twice, under a loop or
        under a resumed continuation, yields two keys, which is what
        lets a trace name each occurrence separately.
    """
    return (
        str(self.static_site),
        tuple((frame.scope, frame.key) for frame in self.dynamic_path),
        self.resumption_path,
    )

to_data

to_data() -> dict[str, object]

Return the serializable form of this provenance.

RETURNS DESCRIPTION
dict[str, object]

The origin, the static site, and the dynamic address. The static site is written out alongside the origin it derives from so a reader need not re-derive it.

Source code in src/quivers/qiec/identifiers.py
def to_data(self) -> dict[str, object]:
    """Return the serializable form of this provenance.

    Returns
    -------
    dict[str, object]
        The origin, the static site, and the dynamic address. The
        static site is written out alongside the origin it derives
        from so a reader need not re-derive it.
    """
    return {
        "origin": self.origin.to_data(),
        "static_site": self.static_site.to_data(),
        "dynamic_path": [
            {"scope": frame.scope, "key": frame.key} for frame in self.dynamic_path
        ],
        "resumption_path": list(self.resumption_path),
        "relation": self.relation,
        "parents": [parent.to_data() for parent in self.parents],
    }

SourceOrigin dataclass

SourceOrigin(module: str, structural_path: tuple[str | int, ...], role: str, source_protocol: str, file: str | None = None, line: int | None = None, column: int | None = None)

Stable structural source origin with optional diagnostic coordinates.

structural_path is the protocol path after parsing and name resolution. Line and column are diagnostic metadata and therefore do not participate in :meth:site_id.

PARAMETER DESCRIPTION
module

The module the site belongs to.

TYPE: str

structural_path

The path of names and positions from the module root to the site.

TYPE: tuple[str | int, ...]

role

What the site is, such as "sample" or "call".

TYPE: str

source_protocol

The source language or protocol the path is expressed in.

TYPE: str

file

The file the site was read from, if any.

TYPE: str | None DEFAULT: None

line

The one-based line of the site, if known.

TYPE: int | None DEFAULT: None

column

The one-based column of the site, if known.

TYPE: int | None DEFAULT: None

site_id

site_id() -> SiteId

The stable identity of the source site this origin describes.

RETURNS DESCRIPTION
SiteId

An identity derived from the protocol, module, structural path, and role. The file, line, and column are deliberately excluded, so reformatting a source file does not change the identity of the sites within it.

Source code in src/quivers/qiec/identifiers.py
def site_id(self) -> SiteId:
    """The stable identity of the source site this origin describes.

    Returns
    -------
    SiteId
        An identity derived from the protocol, module, structural
        path, and role. The file, line, and column are deliberately
        excluded, so reformatting a source file does not change the
        identity of the sites within it.
    """
    return SiteId.derive(
        self.source_protocol,
        self.module,
        self.structural_path,
        self.role,
    )

to_data

to_data() -> dict[str, object]

Return the serializable form of this origin.

RETURNS DESCRIPTION
dict[str, object]

Every field, including the diagnostic coordinates. Those do not enter site_id but are carried here so a deserialized origin can still point at a line.

Source code in src/quivers/qiec/identifiers.py
def to_data(self) -> dict[str, object]:
    """Return the serializable form of this origin.

    Returns
    -------
    dict[str, object]
        Every field, including the diagnostic coordinates. Those do
        not enter `site_id` but are carried here so a deserialized
        origin can still point at a line.
    """
    return {
        "module": self.module,
        "structural_path": list(self.structural_path),
        "role": self.role,
        "source_protocol": self.source_protocol,
        "file": self.file,
        "line": self.line,
        "column": self.column,
    }

StableId dataclass

StableId(digest: str)

A namespaced, SHA-256-derived kernel identifier.

The human-readable declaration name is intentionally not stored in the identifier. Renaming display labels must not silently alter equality; callers choose the declaration path supplied to :meth:derive and record renames explicitly in a migration map.

PARAMETER DESCRIPTION
digest

The 64-character lowercase hex SHA-256 digest; usually produced by :meth:derive rather than written by hand.

TYPE: str

__post_init__

__post_init__() -> None

Reject a digest that is not 64 lowercase hex characters.

RAISES DESCRIPTION
ValueError

If the digest is the wrong length or contains a character outside 0-9a-f.

Source code in src/quivers/qiec/identifiers.py
def __post_init__(self) -> None:
    """Reject a digest that is not 64 lowercase hex characters.

    Raises
    ------
    ValueError
        If the digest is the wrong length or contains a character
        outside ``0-9a-f``.
    """
    if len(self.digest) != 64 or any(
        c not in "0123456789abcdef" for c in self.digest
    ):
        raise ValueError(f"invalid {self.namespace} digest: {self.digest!r}")

derive classmethod

derive(*parts: object) -> Self

Derive an identifier from the values that determine it.

PARAMETER DESCRIPTION
parts

The determining values, in order. Order matters, so two identifiers built from the same values differently arranged are distinct.

TYPE: object DEFAULT: ()

RETURNS DESCRIPTION
Self

The derived identifier. The ABI version and the namespace are mixed in, so an identifier from one namespace can never equal one from another, and a future ABI change re-derives rather than silently reusing.

RAISES DESCRIPTION
TypeError

If a part has no canonical encoding.

Source code in src/quivers/qiec/identifiers.py
@classmethod
def derive(cls, *parts: object) -> Self:
    """Derive an identifier from the values that determine it.

    Parameters
    ----------
    parts : object
        The determining values, in order. Order matters, so two
        identifiers built from the same values differently arranged
        are distinct.

    Returns
    -------
    Self
        The derived identifier. The ABI version and the namespace are
        mixed in, so an identifier from one namespace can never equal
        one from another, and a future ABI change re-derives rather
        than silently reusing.

    Raises
    ------
    TypeError
        If a part has no canonical encoding.
    """
    payload = (QIEC_ABI, cls.namespace, *parts)
    return cls(sha256(_canonical_bytes(payload)).hexdigest())

parse classmethod

parse(text: str) -> Self

Read an identifier back from its wire form.

PARAMETER DESCRIPTION
text

The wire form, qiec:<namespace>:<digest>.

TYPE: str

RETURNS DESCRIPTION
Self

The parsed identifier.

RAISES DESCRIPTION
ValueError

If the text carries another namespace's prefix, or the remainder is not a valid digest. Parsing is namespace-checked so a handler identifier cannot be read as an effect one.

Source code in src/quivers/qiec/identifiers.py
@classmethod
def parse(cls, text: str) -> Self:
    """Read an identifier back from its wire form.

    Parameters
    ----------
    text : str
        The wire form, ``qiec:<namespace>:<digest>``.

    Returns
    -------
    Self
        The parsed identifier.

    Raises
    ------
    ValueError
        If the text carries another namespace's prefix, or the
        remainder is not a valid digest. Parsing is namespace-checked
        so a handler identifier cannot be read as an effect one.
    """
    prefix = f"qiec:{cls.namespace}:"
    if not text.startswith(prefix):
        raise ValueError(f"expected {prefix!r} identifier, got {text!r}")
    return cls(text.removeprefix(prefix))

__str__

__str__() -> str

The wire form of this identifier.

RETURNS DESCRIPTION
str

qiec:<namespace>:<digest>, which parse accepts.

Source code in src/quivers/qiec/identifiers.py
def __str__(self) -> str:
    """The wire form of this identifier.

    Returns
    -------
    str
        ``qiec:<namespace>:<digest>``, which `parse` accepts.
    """
    return f"qiec:{self.namespace}:{self.digest}"

to_data

to_data() -> str

Return the stable wire representation.

RETURNS DESCRIPTION
str

The same string as __str__, named for the serialization protocol the rest of the kernel uses.

Source code in src/quivers/qiec/identifiers.py
def to_data(self) -> str:
    """Return the stable wire representation.

    Returns
    -------
    str
        The same string as `__str__`, named for the serialization
        protocol the rest of the kernel uses.
    """
    return str(self)

StaticScopeId dataclass

StaticScopeId(digest: str)

Bases: StableId

Stable identity of a static scope.

StaticVariableId dataclass

StaticVariableId(digest: str)

Bases: StableId

Stable identity of a static variable.

TypeId dataclass

TypeId(digest: str)

Bases: StableId

Stable identity of a type.

ArrowKind dataclass

ArrowKind(domain: Kind, codomain: Kind, tag: Literal['arrow'] = 'arrow')

The kind of a constructor from one kind to another.

PARAMETER DESCRIPTION
domain

The kind of the argument.

TYPE: Kind

codomain

The kind of the result.

TYPE: Kind

tag

The serialization discriminator; always "arrow".

TYPE: Literal['arrow'] DEFAULT: 'arrow'

ContextSort dataclass

ContextSort(signature: str, tag: Literal['context'] = 'context')

A logic-program context indexed by a stable signature name.

PARAMETER DESCRIPTION
signature

The name of the signature whose contexts inhabit this sort.

TYPE: str

tag

The serialization discriminator; always "context".

TYPE: Literal['context'] DEFAULT: 'context'

EffectBinder dataclass

EffectBinder(name: str, refinable: bool = False, tag: Literal['effect'] = 'effect')

A telescope binder for an effect argument.

PARAMETER DESCRIPTION
name

The binder's display name.

TYPE: str

refinable

Whether case analysis may refine the bound argument.

TYPE: bool DEFAULT: False

tag

The serialization discriminator; always "effect".

TYPE: Literal['effect'] DEFAULT: 'effect'

EffectKind dataclass

EffectKind(tag: Literal['effect'] = 'effect')

The kind of effect interfaces.

PARAMETER DESCRIPTION
tag

The serialization discriminator; always "effect".

TYPE: Literal['effect'] DEFAULT: 'effect'

IndexBinder dataclass

IndexBinder(name: str, sort: IndexSort, refinable: bool = False, tag: Literal['index'] = 'index')

A telescope binder for an index argument.

PARAMETER DESCRIPTION
name

The binder's display name.

TYPE: str

sort

The closed index sort of the bound argument.

TYPE: IndexSort

refinable

Whether case analysis may refine the bound argument; true for a family's indices and false for its uniform parameters.

TYPE: bool DEFAULT: False

tag

The serialization discriminator; always "index".

TYPE: Literal['index'] DEFAULT: 'index'

NatSort dataclass

NatSort(tag: Literal['nat'] = 'nat')

The sort of natural-number indices.

PARAMETER DESCRIPTION
tag

The serialization discriminator; always "nat".

TYPE: Literal['nat'] DEFAULT: 'nat'

RowKind dataclass

RowKind(tag: Literal['row'] = 'row')

The kind of effect rows.

PARAMETER DESCRIPTION
tag

The serialization discriminator; always "row".

TYPE: Literal['row'] DEFAULT: 'row'

ShapeSort dataclass

ShapeSort(rank: int | None = None, tag: Literal['shape'] = 'shape')

The sort of shapes, optionally restricted to a fixed rank.

PARAMETER DESCRIPTION
rank

The number of dimensions, or None for any rank. A binder of unspecified rank accepts a shape of any rank, which is what makes a rank-polymorphic signature expressible.

TYPE: int or None DEFAULT: None

tag

Discriminator for the index-sort union.

TYPE: Literal['shape'] DEFAULT: 'shape'

__post_init__

__post_init__() -> None

Reject a negative rank.

RAISES DESCRIPTION
ValueError

If rank is negative.

Source code in src/quivers/qiec/kinds.py
def __post_init__(self) -> None:
    """Reject a negative rank.

    Raises
    ------
    ValueError
        If `rank` is negative.
    """
    if self.rank is not None and self.rank < 0:
        raise ValueError("shape rank cannot be negative")

TypeBinder dataclass

TypeBinder(name: str, kind: Kind = TYPE, refinable: bool = False, tag: Literal['type'] = 'type')

A telescope binder for a type or effect-kinded static argument.

PARAMETER DESCRIPTION
name

The binder's display name.

TYPE: str

kind

The kind of the bound argument.

TYPE: Kind DEFAULT: TYPE

refinable

Whether case analysis may refine the bound argument; true for a family's indices and false for its uniform parameters.

TYPE: bool DEFAULT: False

tag

The serialization discriminator; always "type".

TYPE: Literal['type'] DEFAULT: 'type'

TypeKind dataclass

TypeKind(tag: Literal['type'] = 'type')

The kind of types.

PARAMETER DESCRIPTION
tag

The serialization discriminator; always "type".

TYPE: Literal['type'] DEFAULT: 'type'

UserIndexSort dataclass

UserIndexSort(name: str, constructors: tuple[str, ...], arities: tuple[int, ...] = (), tag: Literal['user'] = 'user')

A closed user-defined index datatype.

Constructor names are part of the sort identity. Open index sorts are deliberately absent from the first kernel because they make coverage unstable under separate compilation.

PARAMETER DESCRIPTION
name

The sort's name, part of its identity.

TYPE: str

constructors

Constructor names, unique and part of the identity. Two sorts with the same name and different constructors are different sorts, which is what keeps coverage decidable.

TYPE: tuple[str, ...]

arities

Argument count per constructor, positionally aligned with constructors. Empty defaults to all-nullary.

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

tag

Discriminator for the index-sort union.

TYPE: Literal['user'] DEFAULT: 'user'

__post_init__

__post_init__() -> None

Default the arities and reject a malformed sort.

RAISES DESCRIPTION
ValueError

If the name is empty, there are no constructors, a constructor name repeats, the arities do not align with the constructors, or an arity is negative.

Source code in src/quivers/qiec/kinds.py
def __post_init__(self) -> None:
    """Default the arities and reject a malformed sort.

    Raises
    ------
    ValueError
        If the name is empty, there are no constructors, a
        constructor name repeats, the arities do not align with the
        constructors, or an arity is negative.
    """
    if not self.name:
        raise ValueError("user index sort name cannot be empty")
    if not self.constructors:
        raise ValueError("a closed user index sort needs a constructor")
    if len(set(self.constructors)) != len(self.constructors):
        raise ValueError(f"duplicate constructors in index sort {self.name!r}")
    if not self.arities:
        object.__setattr__(self, "arities", (0,) * len(self.constructors))
    if len(self.arities) != len(self.constructors):
        raise ValueError("index constructor arities must align with constructors")
    if any(arity < 0 for arity in self.arities):
        raise ValueError("index constructor arities cannot be negative")

constructor_arity

constructor_arity(name: str) -> int

The number of arguments one constructor takes.

PARAMETER DESCRIPTION
name

The constructor name.

TYPE: str

RETURNS DESCRIPTION
int

Its arity, zero for a nullary constructor.

RAISES DESCRIPTION
ValueError

If the sort has no such constructor. The sort is closed, so an unknown name is an error rather than an open extension.

Source code in src/quivers/qiec/kinds.py
def constructor_arity(self, name: str) -> int:
    """The number of arguments one constructor takes.

    Parameters
    ----------
    name : str
        The constructor name.

    Returns
    -------
    int
        Its arity, zero for a nullary constructor.

    Raises
    ------
    ValueError
        If the sort has no such constructor. The sort is closed, so
        an unknown name is an error rather than an open extension.
    """
    try:
        position = self.constructors.index(name)
    except ValueError as error:
        raise ValueError(
            f"{name!r} is not a constructor of index sort {self.name!r}"
        ) from error
    return self.arities[position]

NamedComputation dataclass

NamedComputation(id: ComputationId, name: str, telescope: Telescope, parameters: tuple[Local, ...], body: Computation, type: ComputationType, origin: SourceOrigin)

One named QVR computation and its checked QIEC type.

PARAMETER DESCRIPTION
id

The computation's stable identity, which calls refer to.

TYPE: ComputationId

name

The display name; must be non-empty and unique within the module.

TYPE: str

telescope

The static parameters the computation abstracts over, in order.

TYPE: Telescope

parameters

The value parameters, in order; their names must be distinct and must not reuse a telescope binder's name.

TYPE: tuple[Local, ...]

body

The computation's body, with the parameters in scope.

TYPE: Computation

type

The checked effect row and result type of body.

TYPE: ComputationType

origin

Where the computation was declared.

TYPE: SourceOrigin

__post_init__

__post_init__() -> None

Reject a computation whose names collide or are absent.

RAISES DESCRIPTION
ValueError

If the name is empty, the telescope is malformed, two parameters share a name, or a parameter shadows a static binder. Shadowing is rejected so a name inside the body denotes one thing.

Source code in src/quivers/qiec/module.py
def __post_init__(self) -> None:
    """Reject a computation whose names collide or are absent.

    Raises
    ------
    ValueError
        If the name is empty, the telescope is malformed, two
        parameters share a name, or a parameter shadows a static
        binder. Shadowing is rejected so a name inside the body
        denotes one thing.
    """
    if not self.name:
        raise ValueError("a computation name cannot be empty")
    validate_telescope(self.telescope)
    static_names = {binder.name for binder in self.telescope}
    local_names = [parameter.name for parameter in self.parameters]
    if len(set(local_names)) != len(local_names):
        raise ValueError(f"duplicate parameter in computation {self.name!r}")
    overlap = static_names & set(local_names)
    if overlap:
        raise ValueError(
            f"computation {self.name!r} reuses static names as parameters: "
            f"{sorted(overlap)!r}"
        )

NamedEffectInstance dataclass

NamedEffectInstance(name: str, entry: RowEntry, origin: SourceOrigin)

A source name for one deterministically allocated lexical instance.

PARAMETER DESCRIPTION
name

The name requests resolve through; must be non-empty.

TYPE: str

entry

The allocated instance and its interface application.

TYPE: RowEntry

origin

Where the instance was declared.

TYPE: SourceOrigin

__post_init__

__post_init__() -> None

Reject an unnamed instance.

RAISES DESCRIPTION
ValueError

If the source name is empty. The name is how a request resolves to this instance, so an empty one names nothing.

Source code in src/quivers/qiec/module.py
def __post_init__(self) -> None:
    """Reject an unnamed instance.

    Raises
    ------
    ValueError
        If the source name is empty. The name is how a request
        resolves to this instance, so an empty one names nothing.
    """
    if not self.name:
        raise ValueError("an effect instance name cannot be empty")

QiecModule dataclass

QiecModule(module: str, source_protocol: str, index_sorts: tuple[UserIndexSort, ...] = (), families: tuple[FamilyDecl, ...] = (), constructors: tuple[ConstructorDecl, ...] = (), effects: tuple[EffectDef, ...] = (), instances: tuple[NamedEffectInstance, ...] = (), handlers: tuple[HandlerDef, ...] = (), computations: tuple[NamedComputation, ...] = (), entries: tuple[ProgramEntry, ...] = (), gap: str = '', abi: str = QIEC_ABI)

A complete, serializable QIEC compilation unit.

source_protocol records the exact source route that produced the module. The wire envelope separately records :data:QIEC_ABI, so neither endpoint can be guessed during deserialization or compiler integration.

PARAMETER DESCRIPTION
module

The module's name, from which every stable identity in it derives.

TYPE: str

source_protocol

The source route that produced the module.

TYPE: str

index_sorts

The user-defined closed index sorts.

TYPE: tuple[UserIndexSort, ...] DEFAULT: ()

families

The indexed data families.

TYPE: tuple[FamilyDecl, ...] DEFAULT: ()

constructors

The family constructors.

TYPE: tuple[ConstructorDecl, ...] DEFAULT: ()

effects

The effect interfaces.

TYPE: tuple[EffectDef, ...] DEFAULT: ()

instances

The module-scoped named effect instances.

TYPE: tuple[NamedEffectInstance, ...] DEFAULT: ()

handlers

The handler declarations.

TYPE: tuple[HandlerDef, ...] DEFAULT: ()

computations

The named computations.

TYPE: tuple[NamedComputation, ...] DEFAULT: ()

entries

The program entry points, each naming one of the computations.

TYPE: tuple[ProgramEntry, ...] DEFAULT: ()

gap

The diagnostic of a program the module lowered without, because it uses a construct whose elaboration is not yet defined; empty when every program elaborated.

TYPE: str DEFAULT: ''

abi

The kernel ABI the module is expressed against; must equal :data:QIEC_ABI.

TYPE: str DEFAULT: QIEC_ABI

__post_init__

__post_init__() -> None

Reject a module whose identity or ABI is missing or wrong.

RAISES DESCRIPTION
ValueError

If the module name or source protocol is empty, or the ABI is not the one this kernel speaks. The ABI is checked here as well as at the wire envelope so a module built in process cannot claim a version it was not built for.

Source code in src/quivers/qiec/module.py
def __post_init__(self) -> None:
    """Reject a module whose identity or ABI is missing or wrong.

    Raises
    ------
    ValueError
        If the module name or source protocol is empty, or the ABI is
        not the one this kernel speaks. The ABI is checked here as
        well as at the wire envelope so a module built in process
        cannot claim a version it was not built for.
    """
    if not isinstance(self.module, str) or not self.module:
        raise ValueError("a QIEC module name cannot be empty")
    if not isinstance(self.source_protocol, str) or not self.source_protocol:
        raise ValueError("a QIEC source protocol cannot be empty")
    if self.abi != QIEC_ABI:
        raise ValueError(f"QIEC module ABI must be {QIEC_ABI!r}")
    _require_unique(
        (sort.name for sort in self.index_sorts),
        subject="index sort name",
    )
    _require_unique((family.id for family in self.families), subject="family ID")
    _require_unique(
        (family.name for family in self.families), subject="family name"
    )
    _require_unique(
        (constructor.id for constructor in self.constructors),
        subject="constructor ID",
    )
    _require_unique((effect.ref.id for effect in self.effects), subject="effect ID")
    _require_unique(
        (effect.ref.name for effect in self.effects), subject="effect name"
    )
    _require_unique(
        (instance.entry.instance for instance in self.instances),
        subject="effect-instance ID",
    )
    _require_unique(
        (instance.name for instance in self.instances),
        subject="effect-instance name",
    )
    _require_unique((handler.id for handler in self.handlers), subject="handler ID")
    _require_unique(
        (handler.name for handler in self.handlers), subject="handler name"
    )
    _require_unique(
        (computation.name for computation in self.computations),
        subject="computation name",
    )

    declared_constructors = {
        constructor.id: constructor for constructor in self.constructors
    }
    family_ids = {family.id for family in self.families}
    expected_constructor_ids = {
        constructor
        for family in self.families
        for constructor in family.constructors
    }
    if set(declared_constructors) != expected_constructor_ids:
        raise ValueError(
            "module constructors must exactly match the constructors named by families"
        )
    if any(
        constructor.family not in family_ids
        for constructor in declared_constructors.values()
    ):
        raise ValueError("a module constructor names an unknown family")

    effect_ids = {effect.ref.id for effect in self.effects}
    if any(
        instance.entry.effect.id not in effect_ids for instance in self.instances
    ):
        raise ValueError("an effect instance names an unknown interface")
    if any(handler.effect.id not in effect_ids for handler in self.handlers):
        raise ValueError("a handler names an unknown effect interface")
    for item in (*self.instances, *self.computations):
        if item.origin.module != self.module:
            raise ValueError("source origin belongs to another QIEC module")
        if item.origin.source_protocol != self.source_protocol:
            raise ValueError("source origin uses another source protocol")

PrimitiveSignature dataclass

PrimitiveSignature(name: str, parameters: tuple[TypeExpr, ...], result: TypeExpr, capability: PrimitiveCapability)

One primitive's name, types, and the capability a backend needs for it.

PARAMETER DESCRIPTION
name

The stable nominal name, such as "add_int".

TYPE: str

parameters

The argument types, in order.

TYPE: tuple[TypeExpr, ...]

result

The result type.

TYPE: TypeExpr

capability

The feature a target must support to render the primitive.

TYPE: PrimitiveCapability

id property

The primitive's stable identity.

RETURNS DESCRIPTION
PrimitiveId

An identity derived from the builtin namespace and the name, so the same primitive has the same identity in every module.

DeductionRun dataclass

DeductionRun(weight: object, result: ExecutionResult)

The outcome of running a deduction on the reference machine.

PARAMETER DESCRIPTION
weight

The inside weight of the goal in the deduction's semiring: a log weight, a Boolean, or a count.

TYPE: object

result

The machine's full result, with its trace.

TYPE: ExecutionResult

ProgramRun dataclass

ProgramRun(value: object, log_joint: float, result: ExecutionResult)

What running a program on the reference machine produced.

PARAMETER DESCRIPTION
value

The value the program returned.

TYPE: object

log_joint

The log joint density: every replayed site's log density under its distribution plus every observation's and score's weight.

TYPE: float

result

The underlying execution result, with its trace.

TYPE: ExecutionResult

ProgramEntry dataclass

ProgramEntry(name: str, computation: ComputationId, parameters: tuple[ProgramParameter, ...], return_names: tuple[str, ...], return_labels: tuple[str, ...] | None, sites: tuple[ProgramSite, ...], random_instance: EffectInstanceId, score_instance: EffectInstanceId, telescope: Telescope = (), tag: Literal['program_entry'] = 'program_entry')

The typed entry point a program declaration elaborates to.

PARAMETER DESCRIPTION
name

The program's source name.

TYPE: str

computation

The identity of the named computation holding its body.

TYPE: ComputationId

parameters

The computation's parameters with their roles, in order.

TYPE: tuple[ProgramParameter, ...]

return_names

The names the program returns, in order.

TYPE: tuple[str, ...]

return_labels

The labels of a labelled return tuple, or None.

TYPE: tuple[str, ...] | None

sites

The program's probabilistic steps, in source order, those of marginalization scopes included.

TYPE: tuple[ProgramSite, ...]

random_instance

The lexical Random instance the program's samples address.

TYPE: EffectInstanceId

score_instance

The lexical Score instance the program's scores address.

TYPE: EffectInstanceId

telescope

The static binders the computation takes: one index binder per extent of an input the program's steps leave open, which a run reads off the data it is given.

TYPE: Telescope DEFAULT: ()

tag

The serialization discriminator; always "program_entry".

TYPE: Literal['program_entry'] DEFAULT: 'program_entry'

ProgramParameter dataclass

ProgramParameter(name: str, role: ParameterRole, type: TypeExpr, axes: tuple[str, ...] = (), tag: Literal['program_parameter'] = 'program_parameter')

One parameter of an elaborated program.

PARAMETER DESCRIPTION
name

The parameter's name, which is the computation parameter's.

TYPE: str

role

How the parameter is supplied.

TYPE: ParameterRole

type

Its type.

TYPE: TypeExpr

axes

The name of each dimension of a tensor-typed parameter, the object or extent it ranges over; empty for a scalar.

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

tag

The serialization discriminator; always "program_parameter".

TYPE: Literal['program_parameter'] DEFAULT: 'program_parameter'

ProgramSite dataclass

ProgramSite(name: str, kind: SiteKind, family: str, batch: tuple[PlateAxis, ...] = (), event: tuple[PlateAxis, ...] = (), tag: Literal['program_site'] = 'program_site')

One probabilistic step of an elaborated program.

PARAMETER DESCRIPTION
name

The site's label, the name the step binds or scores.

TYPE: str

kind

Whether the step samples, observes, or marginalizes.

TYPE: SiteKind

family

The distribution family the step draws from.

TYPE: str

batch

The plate's batch axes.

TYPE: tuple[PlateAxis, ...] DEFAULT: ()

event

The plate's event axes.

TYPE: tuple[PlateAxis, ...] DEFAULT: ()

tag

The serialization discriminator; always "program_site".

TYPE: Literal['program_site'] DEFAULT: 'program_site'

DistributionError

Bases: ValueError

A distribution could not be constructed, sampled, or scored.

SerializationError

Bases: ValueError

A value is not in the stable QIEC wire language.

StaticSubstitution dataclass

StaticSubstitution(types: tuple[tuple[str, TypeExpr], ...] = (), indices: tuple[tuple[str, IndexTerm], ...] = (), effects: tuple[tuple[str, EffectRef | EffectVariable], ...] = ())

A deterministic substitution split by static namespace.

PARAMETER DESCRIPTION
types

Bindings from type-variable names to types.

TYPE: tuple[tuple[str, TypeExpr], ...] DEFAULT: ()

indices

Bindings from index-variable names to index terms.

TYPE: tuple[tuple[str, IndexTerm], ...] DEFAULT: ()

effects

Bindings from effect-variable names to interface applications or other effect variables.

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

__post_init__

__post_init__() -> None

Reject a substitution that binds one name twice.

RAISES DESCRIPTION
ValueError

If a name repeats within a namespace. Two bindings for one name would make the result depend on lookup order.

Source code in src/quivers/qiec/substitution.py
def __post_init__(self) -> None:
    """Reject a substitution that binds one name twice.

    Raises
    ------
    ValueError
        If a name repeats within a namespace. Two bindings for one
        name would make the result depend on lookup order.
    """
    for namespace in (self.types, self.indices, self.effects):
        names = [name for name, _ in namespace]
        if len(set(names)) != len(names):
            raise ValueError("duplicate substitution binding")

type

type(name: str) -> TypeExpr | None

What a type binder of this name is bound to.

PARAMETER DESCRIPTION
name

The binder name.

TYPE: str

RETURNS DESCRIPTION
TypeExpr or None

The bound type, or None when this substitution says nothing about the name, in which case the variable stands.

Source code in src/quivers/qiec/substitution.py
def type(self, name: str) -> TypeExpr | None:
    """What a type binder of this name is bound to.

    Parameters
    ----------
    name : str
        The binder name.

    Returns
    -------
    TypeExpr or None
        The bound type, or None when this substitution says nothing
        about the name, in which case the variable stands.
    """
    return next((value for key, value in self.types if key == name), None)

index

index(name: str) -> IndexTerm | None

What an index binder of this name is bound to.

PARAMETER DESCRIPTION
name

The binder name.

TYPE: str

RETURNS DESCRIPTION
IndexTerm or None

The bound index, or None when the name is unbound here.

Source code in src/quivers/qiec/substitution.py
def index(self, name: str) -> IndexTerm | None:
    """What an index binder of this name is bound to.

    Parameters
    ----------
    name : str
        The binder name.

    Returns
    -------
    IndexTerm or None
        The bound index, or None when the name is unbound here.
    """
    return next((value for key, value in self.indices if key == name), None)

effect

effect(name: str) -> EffectRef | EffectVariable | None

What an effect binder of this name is bound to.

PARAMETER DESCRIPTION
name

The binder name.

TYPE: str

RETURNS DESCRIPTION
EffectRef or EffectVariable or None

The bound interface application or effect variable, or None when the name is unbound here.

Source code in src/quivers/qiec/substitution.py
def effect(self, name: str) -> EffectRef | EffectVariable | None:
    """What an effect binder of this name is bound to.

    Parameters
    ----------
    name : str
        The binder name.

    Returns
    -------
    EffectRef or EffectVariable or None
        The bound interface application or effect variable, or None
        when the name is unbound here.
    """
    return next((value for key, value in self.effects if key == name), None)

AffineMap dataclass

AffineMap(weight: Value, bias: Value, sources: tuple[Value, ...], row_offset: int, rows: int, transform: Literal['identity', 'exp', 'exp_floor'], result_type: TypeExpr, tag: Literal['affine_map'] = 'affine_map')

One head of an affine parameter map, W x + b on a row block.

For i below rows the head's coordinate i is the sum over j of weight[row_offset + i, j] * x[j] plus bias[row_offset + i], where x concatenates the sources in order; exp exponentiates every coordinate afterwards, and exp_floor exponentiates and clamps each at the scale floor.

PARAMETER DESCRIPTION
weight

A Tensor[Real]([total_rows, columns]).

TYPE: Value

bias

A Tensor[Real]([total_rows]).

TYPE: Value

sources

The conditioning row's factors in order, each a Real or a Tensor[Real]([width]).

TYPE: tuple[Value, ...]

row_offset

The first row of the head's block.

TYPE: int

rows

The block's height.

TYPE: int

transform

"identity", "exp", or "exp_floor".

TYPE: Literal['identity', 'exp', 'exp_floor']

result_type

Tensor[Real]([rows]), or Real for a one-row head.

TYPE: TypeExpr

tag

The serialization discriminator; always "affine_map".

TYPE: Literal['affine_map'] DEFAULT: 'affine_map'

AttachmentRef dataclass

AttachmentRef(attachment: AttachmentId, type: TypeExpr, tag: Literal['attachment'] = 'attachment')

A stable reference to a runtime-owned host value.

PARAMETER DESCRIPTION
attachment

The identity under which a runtime provider binds the host value.

TYPE: AttachmentId

type

The type the bound value must inhabit.

TYPE: TypeExpr

tag

The serialization discriminator; always "attachment".

TYPE: Literal['attachment'] DEFAULT: 'attachment'

Bind dataclass

Bind(binder: Local, first: Computation, then: Computation, tag: Literal['bind'] = 'bind')

Sequence two computations, binding the first's result in the second.

PARAMETER DESCRIPTION
binder

The local the first computation's result is bound to.

TYPE: Local

first

The computation run first.

TYPE: Computation

then

The computation run second, with binder in scope.

TYPE: Computation

tag

The serialization discriminator; always "bind".

TYPE: Literal['bind'] DEFAULT: 'bind'

Call dataclass

Call(callee: ComputationId, name: str, static_arguments: tuple[StaticArgument, ...], arguments: tuple[Value, ...], result_type: TypeExpr, effects: EffectRow, origin: SourceOrigin, tag: Literal['call'] = 'call')

Application of a named computation.

The callee is a stable identifier rather than an inlined body, which is what lets a recursive or mutually recursive call graph serialize as a finite tree.

PARAMETER DESCRIPTION
callee

The stable identity of the computation called.

TYPE: ComputationId

name

The callee's display name, for diagnostics and traces.

TYPE: str

static_arguments

The callee's telescope instantiation, in binder order.

TYPE: tuple[StaticArgument, ...]

arguments

The value arguments, one per callee parameter.

TYPE: tuple[Value, ...]

result_type

The callee's result type under the instantiation.

TYPE: TypeExpr

effects

The callee's effect row under the instantiation.

TYPE: EffectRow

origin

The call site's source location.

TYPE: SourceOrigin

tag

The serialization discriminator; always "call".

TYPE: Literal['call'] DEFAULT: 'call'

Case dataclass

Case(scrutinee: Value, motive: CaseMotive, branches: tuple[CaseBranch, ...], tag: Literal['case'] = 'case')

Case analysis over an indexed family value.

PARAMETER DESCRIPTION
scrutinee

The value analyzed.

TYPE: Value

motive

The result family, over the scrutinee's indices.

TYPE: CaseMotive

branches

One branch per constructor considered; coverage is checked against the exact applied family.

TYPE: tuple[CaseBranch, ...]

tag

The serialization discriminator; always "case".

TYPE: Literal['case'] DEFAULT: 'case'

CaseBranch dataclass

CaseBranch(constructor: ConstructorId, static_arguments: tuple[StaticArgument, ...], fields: tuple[Local, ...], body: Computation, scope: StaticScopeId)

One stable GADT branch with no host-language closure.

PARAMETER DESCRIPTION
constructor

The constructor this branch matches.

TYPE: ConstructorId

static_arguments

The constructor's static arguments as bound by the pattern, in binder order.

TYPE: tuple[StaticArgument, ...]

fields

The locals binding the constructor's fields, in declaration order.

TYPE: tuple[Local, ...]

body

The computation run when the branch is selected.

TYPE: Computation

scope

The static scope identity under which the branch's skolems and equality evidence are minted.

TYPE: StaticScopeId

CaseMotive dataclass

CaseMotive(indices: Telescope, result_type: TypeExpr)

The result family of an indexed case expression.

PARAMETER DESCRIPTION
indices

The binders abstracting the scrutinee's indices in result_type.

TYPE: Telescope

result_type

The type every branch must produce, over indices.

TYPE: TypeExpr

Comprehension dataclass

Comprehension(binder: Local, extent: IndexTerm, body: Value, result_type: TypeExpr, tag: Literal['comprehension'] = 'comprehension')

A tensor built by evaluating a body at every index of an axis.

PARAMETER DESCRIPTION
binder

The Int local the body reads the index through.

TYPE: Local

extent

The axis's extent, an index term of the nat sort.

TYPE: IndexTerm

body

The entry at each index.

TYPE: Value

result_type

Tensor[T]([extent]) for the body's type T, or, when the body is itself a tensor, the tensor with extent prepended.

TYPE: TypeExpr

tag

The serialization discriminator; always "comprehension".

TYPE: Literal['comprehension'] DEFAULT: 'comprehension'

ConstructorValue dataclass

ConstructorValue(constructor: ConstructorId, static_arguments: tuple[StaticArgument, ...], fields: tuple[Value, ...], result_type: TypeExpr, tag: Literal['constructor'] = 'constructor')

Application of a declared constructor to static and value arguments.

PARAMETER DESCRIPTION
constructor

The stable identity of the constructor applied.

TYPE: ConstructorId

static_arguments

The constructor's telescope instantiation, in binder order.

TYPE: tuple[StaticArgument, ...]

fields

The field values, in declaration order.

TYPE: tuple[Value, ...]

result_type

The indexed family type the application inhabits.

TYPE: TypeExpr

tag

The serialization discriminator; always "constructor".

TYPE: Literal['constructor'] DEFAULT: 'constructor'

DistributionValue dataclass

DistributionValue(family: DistributionId, name: str, arguments: tuple[tuple[str, Value], ...], result_type: TypeExpr, origin: SourceOrigin, plate: PlateShape = PlateShape(), tag: Literal['distribution'] = 'distribution')

Construction of a distribution from a family of the semantic registry.

The family is named nominally and its parameters are supplied by name, so the term records which parameterization the source used and a backend renders the spelling it knows without inspecting values. A plate replicates the family over batch axes and joins draws along event axes; a parameter may then be a tensor whose leading dimensions follow a suffix of the plate's axes and broadcast over the rest.

PARAMETER DESCRIPTION
family

The stable identity of the family applied.

TYPE: DistributionId

name

The family's source name, as the registry spells it.

TYPE: str

arguments

The parameters supplied, each named, in source order.

TYPE: tuple[tuple[str, Value], ...]

result_type

The Sampleable type of the constructed distribution.

TYPE: TypeExpr

origin

The construction's source location.

TYPE: SourceOrigin

plate

The plate the distribution is constructed over; empty for one draw of the family.

TYPE: PlateShape DEFAULT: PlateShape()

tag

The serialization discriminator; always "distribution".

TYPE: Literal['distribution'] DEFAULT: 'distribution'

EvidenceValue dataclass

EvidenceValue(evidence: EqualityEvidence, tag: Literal['evidence'] = 'evidence')

Kernel-checked equality evidence used as a value.

PARAMETER DESCRIPTION
evidence

The evidence term.

TYPE: EqualityEvidence

tag

The serialization discriminator; always "evidence".

TYPE: Literal['evidence'] DEFAULT: 'evidence'

Gather dataclass

Gather(value: Value, index: Value, result_type: TypeExpr, tag: Literal['gather'] = 'gather')

Selection along the outermost axis of a tensor.

PARAMETER DESCRIPTION
value

The tensor selected from.

TYPE: Value

index

An Int selecting one slice, or a Tensor[Int] selecting a slice per entry.

TYPE: Value

result_type

The selection's type: the slice type for an integer index, or a tensor of slices shaped like the index tensor.

TYPE: TypeExpr

tag

The serialization discriminator; always "gather".

TYPE: Literal['gather'] DEFAULT: 'gather'

Handle dataclass

Handle(instance: EffectInstanceId, handler: HandlerId, computation: Computation, static_arguments: tuple[StaticArgument, ...] = (), tag: Literal['handle'] = 'handle')

Run a computation under a handler installed for one effect instance.

PARAMETER DESCRIPTION
instance

The effect instance whose operations the handler intercepts.

TYPE: EffectInstanceId

handler

The stable identity of the handler installed.

TYPE: HandlerId

computation

The handled computation.

TYPE: Computation

static_arguments

The handler's telescope instantiation, in binder order.

TYPE: tuple[StaticArgument, ...] DEFAULT: ()

tag

The serialization discriminator; always "handle".

TYPE: Literal['handle'] DEFAULT: 'handle'

If dataclass

If(condition: Value, then: Computation, otherwise: Computation, tag: Literal['if'] = 'if')

Branch on a Boolean value.

Both branches must produce the same type; the row of the whole is the union of the branches' rows. This is what lets a recursive computation stop, since a value-level select would evaluate both branches and never terminate.

PARAMETER DESCRIPTION
condition

The Boolean value branched on.

TYPE: Value

then

The computation run when the condition holds.

TYPE: Computation

otherwise

The computation run when it does not.

TYPE: Computation

tag

The serialization discriminator; always "if".

TYPE: Literal['if'] DEFAULT: 'if'

KernelMatrix dataclass

KernelMatrix(inputs: Value, kernel: str, length_scale: float, jitter: float, result_type: TypeExpr, tag: Literal['kernel_matrix'] = 'kernel_matrix')

A positive-definite covariance over input locations.

PARAMETER DESCRIPTION
inputs

A Tensor[Real]([n]) of locations.

TYPE: Value

kernel

The kernel's name; "rbf" is the squared-exponential kernel.

TYPE: str

length_scale

The kernel's length scale.

TYPE: float

jitter

The diagonal added for numerical positive definiteness.

TYPE: float

result_type

Tensor[Real]([n, n]).

TYPE: TypeExpr

tag

The serialization discriminator; always "kernel_matrix".

TYPE: Literal['kernel_matrix'] DEFAULT: 'kernel_matrix'

LiteralValue dataclass

LiteralValue(value: LiteralData, type: TypeExpr, tag: Literal['literal'] = 'literal')

A literal host value at a primitive or tuple type.

PARAMETER DESCRIPTION
value

The literal's data: None, a bool, int, float, str, bytes, or a tuple of literal data.

TYPE: LiteralData

type

The literal's type.

TYPE: TypeExpr

tag

The serialization discriminator; always "literal".

TYPE: Literal['literal'] DEFAULT: 'literal'

Local dataclass

Local(name: str, type: TypeExpr)

A typed binder for a runtime value.

Locals are compared by identity of name and type together, so two binders of the same name at different types are distinct.

PARAMETER DESCRIPTION
name

The binder's display name.

TYPE: str

type

The type of every value bound to it.

TYPE: TypeExpr

LogDensity dataclass

LogDensity(sampleable: Value, value: Value, origin: SourceOrigin, batch: tuple[PlateAxis, ...] = (), tag: Literal['log_density'] = 'log_density')

Evaluation of a distribution's log density at a value.

PARAMETER DESCRIPTION
sampleable

The distribution, of type Sampleable[A].

TYPE: Value

value

The point evaluated, of type A.

TYPE: Value

origin

The evaluation's source location.

TYPE: SourceOrigin

batch

The batch axes the density is kept apart over: empty for one LogWeight totalling every draw, else the batch axes of the plated construction sampleable must be, for one weight per position.

TYPE: tuple[PlateAxis, ...] DEFAULT: ()

tag

The serialization discriminator; always "log_density".

TYPE: Literal['log_density'] DEFAULT: 'log_density'

NewInstance dataclass

NewInstance(instance: EffectInstanceId, effect: EffectRef, body: Computation, origin: SourceOrigin, tag: Literal['new_instance'] = 'new_instance')

Lexically scoped allocation of an effect instance.

The identity is derived from the module, the enclosing computation, the lexical path, and the applied interface, so the same allocation site yields the same instance on every run while two sites of the same interface stay distinct.

PARAMETER DESCRIPTION
instance

The scope-derived stable identity of the allocated instance.

TYPE: EffectInstanceId

effect

The applied interface the instance provides.

TYPE: EffectRef

body

The computation within which the instance is in scope.

TYPE: Computation

origin

The allocation's source location.

TYPE: SourceOrigin

tag

The serialization discriminator; always "new_instance".

TYPE: Literal['new_instance'] DEFAULT: 'new_instance'

Perform dataclass

Perform(request: EffectRequest, tag: Literal['perform'] = 'perform')

Perform one effect operation.

PARAMETER DESCRIPTION
request

The operation, its instance, static arguments, value arguments, and result type.

TYPE: EffectRequest

tag

The serialization discriminator; always "perform".

TYPE: Literal['perform'] DEFAULT: 'perform'

PlateAxis dataclass

PlateAxis(name: str, size: IndexTerm, tag: Literal['plate_axis'] = 'plate_axis')

One axis of a plated distribution.

PARAMETER DESCRIPTION
name

The axis's source name, the object it ranges over.

TYPE: str

size

The axis's extent, an index term of the nat sort.

TYPE: IndexTerm

tag

The serialization discriminator; always "plate_axis".

TYPE: Literal['plate_axis'] DEFAULT: 'plate_axis'

PlateShape dataclass

PlateShape(batch: tuple[PlateAxis, ...] = (), event: tuple[PlateAxis, ...] = (), tag: Literal['plate_shape'] = 'plate_shape')

The plate a distribution is constructed over.

Batch axes replicate the distribution independently, one draw per position, each scored on its own; event axes join the draws along them into one event, scored together. The trailing event axes are the family's own event dimensions when it has any; any leading ones extend a scalar family's event.

PARAMETER DESCRIPTION
batch

The independent replication axes, outermost first.

TYPE: tuple[PlateAxis, ...] DEFAULT: ()

event

The joint axes, outermost first.

TYPE: tuple[PlateAxis, ...] DEFAULT: ()

tag

The serialization discriminator; always "plate_shape".

TYPE: Literal['plate_shape'] DEFAULT: 'plate_shape'

empty property

empty: bool

Whether the plate has no axes at all.

RETURNS DESCRIPTION
bool

True when neither batch nor event axes are declared.

PrimitiveApplication dataclass

PrimitiveApplication(primitive: PrimitiveId, name: str, arguments: tuple[Value, ...], result_type: TypeExpr, origin: SourceOrigin, tag: Literal['primitive'] = 'primitive')

Application of a pure primitive from the closed registry.

The primitive is named nominally and its signature is fixed, so the checker validates the arguments against the registry and a backend renders the name it knows or refuses the capability; nothing is resolved by inspecting a runtime value.

PARAMETER DESCRIPTION
primitive

The stable identity of the primitive applied.

TYPE: PrimitiveId

name

The primitive's nominal name, as the registry spells it.

TYPE: str

arguments

The value arguments, one per parameter.

TYPE: tuple[Value, ...]

result_type

The primitive's result type.

TYPE: TypeExpr

origin

The application's source location.

TYPE: SourceOrigin

tag

The serialization discriminator; always "primitive".

TYPE: Literal['primitive'] DEFAULT: 'primitive'

Projection dataclass

Projection(value: Value, position: int, result_type: TypeExpr, tag: Literal['projection'] = 'projection')

Selection of one component of a finite product.

PARAMETER DESCRIPTION
value

The product value projected from.

TYPE: Value

position

The zero-based component selected.

TYPE: int

result_type

The selected component's type.

TYPE: TypeExpr

tag

The serialization discriminator; always "projection".

TYPE: Literal['projection'] DEFAULT: 'projection'

Reduction dataclass

Reduction(operator: ReductionOperator, value: Value, result_type: TypeExpr, tag: Literal['reduction'] = 'reduction')

A number summarizing every entry of a tensor.

PARAMETER DESCRIPTION
operator

The reduction.

TYPE: ReductionOperator

value

A tensor of reals, or of integers for sum, max, min, and prod.

TYPE: Value

result_type

The element type.

TYPE: TypeExpr

tag

The serialization discriminator; always "reduction".

TYPE: Literal['reduction'] DEFAULT: 'reduction'

Resume dataclass

Resume(value: Value, origin: SourceOrigin, tag: Literal['resume'] = 'resume')

Invocation of the enclosing handler clause's continuation.

The resumption is not a value and cannot be stored, so it is a term of its own rather than a call to a bound name. That is what makes a clause's grade checkable by counting invocations along the paths through its body.

PARAMETER DESCRIPTION
value

The value returned to the suspended computation as the operation's result.

TYPE: Value

origin

The resumption's source location.

TYPE: SourceOrigin

tag

The serialization discriminator; always "resume".

TYPE: Literal['resume'] DEFAULT: 'resume'

Return dataclass

Return(value: Value, tag: Literal['return'] = 'return')

Return a value with no effects.

PARAMETER DESCRIPTION
value

The value produced.

TYPE: Value

tag

The serialization discriminator; always "return".

TYPE: Literal['return'] DEFAULT: 'return'

Rowwise dataclass

Rowwise(operator: RowwiseOperator, value: Value, result_type: TypeExpr, tag: Literal['rowwise'] = 'rowwise')

An operation along a tensor's last axis keeping its shape.

PARAMETER DESCRIPTION
operator

The operation.

TYPE: RowwiseOperator

value

A tensor of reals.

TYPE: Value

result_type

The tensor's own type.

TYPE: TypeExpr

tag

The serialization discriminator; always "rowwise".

TYPE: Literal['rowwise'] DEFAULT: 'rowwise'

SegmentSum dataclass

SegmentSum(value: Value, index: Value, groups: IndexTerm, result_type: TypeExpr, tag: Literal['segment_sum'] = 'segment_sum')

Per-group totals of a vector of log weights.

PARAMETER DESCRIPTION
value

A Tensor[LogWeight]([n]).

TYPE: Value

index

A Tensor[Int]([n]) naming each entry's group.

TYPE: Value

groups

The number of groups, an index term of the nat sort.

TYPE: IndexTerm

result_type

Tensor[LogWeight]([groups]).

TYPE: TypeExpr

tag

The serialization discriminator; always "segment_sum".

TYPE: Literal['segment_sum'] DEFAULT: 'segment_sum'

SiteValue dataclass

SiteValue(label: str, result_type: TypeExpr, tag: Literal['site'] = 'site')

A named sample site.

A site is the address a probabilistic step is observed and traced under. Its label is the source name of the step, which is stable data; the dynamic address a run adds comes from the request that carries it.

PARAMETER DESCRIPTION
label

The site's source name.

TYPE: str

result_type

The Site type of the value the site produces.

TYPE: TypeExpr

tag

The serialization discriminator; always "site".

TYPE: Literal['site'] DEFAULT: 'site'

TableMap dataclass

TableMap(table: Value, index: Value, row_offset: int, rows: int, transform: Literal['identity', 'exp', 'exp_floor'], result_type: TypeExpr, tag: Literal['table_map'] = 'table_map')

One head of a table-indexed parameter map, a row block of T[i].

A kernel over a finite domain reads its parameters from a table with one row per element of the domain: for i below rows the head's coordinate i is table[index, row_offset + i], with exp exponentiating every coordinate afterwards and exp_floor exponentiating and clamping each at the scale floor.

PARAMETER DESCRIPTION
table

A Tensor[Real]([entries, total_rows]).

TYPE: Value

index

An Int naming the row, the element of the domain.

TYPE: Value

row_offset

The first column of the head's block.

TYPE: int

rows

The block's width.

TYPE: int

transform

"identity", "exp", or "exp_floor".

TYPE: Literal['identity', 'exp', 'exp_floor']

result_type

Tensor[Real]([rows]), or Real for a one-column head.

TYPE: TypeExpr

tag

The serialization discriminator; always "table_map".

TYPE: Literal['table_map'] DEFAULT: 'table_map'

TensorValue dataclass

TensorValue(items: tuple[Value, ...], result_type: TypeExpr, tag: Literal['tensor'] = 'tensor')

Construction of a tensor from its entries along the outermost axis.

A rank-one tensor lists its elements; a tensor of higher rank lists the tensors one rank lower that are its slices, all of one shape, so a matrix literal is a tensor of row tensors.

PARAMETER DESCRIPTION
items

The entries along the outermost axis, in order.

TYPE: tuple[Value, ...]

result_type

The Tensor type constructed, whose leading dimension is the number of entries.

TYPE: TypeExpr

tag

The serialization discriminator; always "tensor".

TYPE: Literal['tensor'] DEFAULT: 'tensor'

TransportValue dataclass

TransportValue(evidence: EqualityEvidence, value: Value, target_type: TypeExpr, tag: Literal['transport'] = 'transport')

Transport a value along kernel-checked equality evidence.

PARAMETER DESCRIPTION
evidence

The equality the value is carried across.

TYPE: EqualityEvidence

value

The value at the equality's left type.

TYPE: Value

target_type

The type the transported value inhabits, which the equality's right side determines.

TYPE: TypeExpr

tag

The serialization discriminator; always "transport".

TYPE: Literal['transport'] DEFAULT: 'transport'

TupleValue dataclass

TupleValue(items: tuple[Value, ...], result_type: TypeExpr, tag: Literal['tuple'] = 'tuple')

Construction of a finite product from its components.

PARAMETER DESCRIPTION
items

The component values, in order.

TYPE: tuple[Value, ...]

result_type

The product type of the components.

TYPE: TypeExpr

tag

The serialization discriminator; always "tuple".

TYPE: Literal['tuple'] DEFAULT: 'tuple'

Var dataclass

Var(local: Local, tag: Literal['var'] = 'var')

Reference to a bound local.

PARAMETER DESCRIPTION
local

The binder being referenced.

TYPE: Local

tag

The serialization discriminator; always "var".

TYPE: Literal['var'] DEFAULT: 'var'

WeightSum dataclass

WeightSum(value: Value, tag: Literal['weight_sum'] = 'weight_sum')

The total of a tensor of log weights.

PARAMETER DESCRIPTION
value

A Tensor[LogWeight] of any shape, or a LogWeight.

TYPE: Value

tag

The serialization discriminator; always "weight_sum".

TYPE: Literal['weight_sum'] DEFAULT: 'weight_sum'

EffectRef dataclass

EffectRef(id: EffectId, name: str, arguments: tuple[StaticArgument, ...] = (), tag: Literal['effect_ref'] = 'effect_ref')

One closed effect interface application.

PARAMETER DESCRIPTION
id

The stable identity of the interface declaration.

TYPE: EffectId

name

The interface's display name.

TYPE: str

arguments

The arguments instantiating the interface's telescope, in order.

TYPE: tuple[StaticArgument, ...] DEFAULT: ()

tag

The serialization discriminator; always "effect_ref".

TYPE: Literal['effect_ref'] DEFAULT: 'effect_ref'

__eq__

__eq__(other: object) -> bool

Compare a concrete interface application by stable identity.

PARAMETER DESCRIPTION
other

The value to compare against.

TYPE: object

RETURNS DESCRIPTION
bool

True when both name the same declaration and supply equal static arguments. The display name is ignored, and identity is nominal, so two applications of one interface differ only by their arguments.

Source code in src/quivers/qiec/types.py
def __eq__(self, other: object) -> bool:
    """Compare a concrete interface application by stable identity.

    Parameters
    ----------
    other : object
        The value to compare against.

    Returns
    -------
    bool
        True when both name the same declaration and supply equal
        static arguments. The display name is ignored, and identity
        is nominal, so two applications of one interface differ only
        by their arguments.
    """
    return (
        isinstance(other, EffectRef)
        and self.id == other.id
        and self.arguments == other.arguments
    )

__hash__

__hash__() -> int

Hash the fields __eq__ compares.

RETURNS DESCRIPTION
int

A hash of the declaration identity and the arguments.

Source code in src/quivers/qiec/types.py
def __hash__(self) -> int:
    """Hash the fields `__eq__` compares.

    Returns
    -------
    int
        A hash of the declaration identity and the arguments.
    """
    return hash((self.id, self.arguments))

EffectVariable dataclass

EffectVariable(name: str, identity: StaticVariableId | None = None, tag: Literal['effect_variable'] = 'effect_variable')

A variable standing for an effect interface application.

PARAMETER DESCRIPTION
name

The variable's display name.

TYPE: str

identity

The rigid identity of a case branch's skolem, or None for a declaration binder matched by name.

TYPE: StaticVariableId | None DEFAULT: None

tag

The serialization discriminator; always "effect_variable".

TYPE: Literal['effect_variable'] DEFAULT: 'effect_variable'

__eq__

__eq__(other: object) -> bool

Compare by identity when either side is rigid, else by name.

PARAMETER DESCRIPTION
other

The value to compare against.

TYPE: object

RETURNS DESCRIPTION
bool

True when both denote the same effect variable. A rigid variable never equals a free one, which is what keeps a branch's skolem from unifying with an enclosing binder.

Source code in src/quivers/qiec/types.py
def __eq__(self, other: object) -> bool:
    """Compare by identity when either side is rigid, else by name.

    Parameters
    ----------
    other : object
        The value to compare against.

    Returns
    -------
    bool
        True when both denote the same effect variable. A rigid
        variable never equals a free one, which is what keeps a
        branch's skolem from unifying with an enclosing binder.
    """
    if not isinstance(other, EffectVariable):
        return False
    if self.identity is not None or other.identity is not None:
        return self.identity is not None and self.identity == other.identity
    return self.name == other.name

__hash__

__hash__() -> int

Hash the fields __eq__ compares.

RETURNS DESCRIPTION
int

A hash of the identity when the variable is rigid, and of the name otherwise.

Source code in src/quivers/qiec/types.py
def __hash__(self) -> int:
    """Hash the fields `__eq__` compares.

    Returns
    -------
    int
        A hash of the identity when the variable is rigid, and of the
        name otherwise.
    """
    return hash(self.identity if self.identity is not None else self.name)

EqualityType dataclass

EqualityType(kind: Kind | IndexSort, left: StaticArgument, right: StaticArgument, tag: Literal['equality_type'] = 'equality_type')

The proposition that two static arguments are equal.

PARAMETER DESCRIPTION
kind

The kind or index sort both sides inhabit.

TYPE: Kind | IndexSort

left

The left side of the equation.

TYPE: StaticArgument

right

The right side of the equation.

TYPE: StaticArgument

tag

The serialization discriminator; always "equality_type".

TYPE: Literal['equality_type'] DEFAULT: 'equality_type'

FunctionType dataclass

FunctionType(parameter: TypeExpr, result: TypeExpr, tag: Literal['function_type'] = 'function_type')

A pure function type.

Effectful codomains are represented explicitly by :class:quivers.qiec.effects.ComputationType, keeping the value and computation strata separate.

PARAMETER DESCRIPTION
parameter

The argument type.

TYPE: TypeExpr

result

The result type.

TYPE: TypeExpr

tag

The serialization discriminator; always "function_type".

TYPE: Literal['function_type'] DEFAULT: 'function_type'

IndexConstructor dataclass

IndexConstructor(name: str, arguments: tuple[IndexTerm, ...], sort: IndexSort, tag: Literal['index_constructor'] = 'index_constructor')

A fully applied constructor of a user-defined index sort.

PARAMETER DESCRIPTION
name

The constructor's name, which must belong to sort.

TYPE: str

arguments

The constructor's index arguments, in order.

TYPE: tuple[IndexTerm, ...]

sort

The sort the constructor builds.

TYPE: IndexSort

tag

The serialization discriminator; always "index_constructor".

TYPE: Literal['index_constructor'] DEFAULT: 'index_constructor'

__post_init__

__post_init__() -> None

Check the constructor belongs to its sort and is fully applied.

RAISES DESCRIPTION
ValueError

If the sort is not a user-defined one, if it declares no such constructor, or if the argument count differs from the declared arity. A partially applied index constructor is rejected rather than curried, since an index is a value of its sort and a partial application is not one.

Source code in src/quivers/qiec/types.py
def __post_init__(self) -> None:
    """Check the constructor belongs to its sort and is fully applied.

    Raises
    ------
    ValueError
        If the sort is not a user-defined one, if it declares no such
        constructor, or if the argument count differs from the
        declared arity. A partially applied index constructor is
        rejected rather than curried, since an index is a value of
        its sort and a partial application is not one.
    """

    if not isinstance(self.sort, UserIndexSort):
        raise ValueError("named index constructors require a user index sort")
    expected = self.sort.constructor_arity(self.name)
    if len(self.arguments) != expected:
        raise ValueError(
            f"index constructor {self.name!r} expects {expected} arguments, "
            f"got {len(self.arguments)}"
        )
    for argument in self.arguments:
        if index_sort(argument) != self.sort:
            raise ValueError(
                f"argument of index constructor {self.name!r} has the wrong sort"
            )

IndexLiteral dataclass

IndexLiteral(value: int | str, sort: IndexSort, tag: Literal['index_literal'] = 'index_literal')

A closed index literal.

PARAMETER DESCRIPTION
value

A natural number for the nat sort, or a nullary constructor name for a user-defined index sort.

TYPE: int | str

sort

The sort the literal inhabits.

TYPE: IndexSort

tag

The serialization discriminator; always "index_literal".

TYPE: Literal['index_literal'] DEFAULT: 'index_literal'

__post_init__

__post_init__() -> None

Check the literal inhabits the sort it claims.

RAISES DESCRIPTION
ValueError

If a Nat literal is not a nonnegative integer, if a user sort's literal names a constructor that sort does not declare, or if it names one that takes arguments. The last is the subtle case: a nullary constructor is a literal, while one with arguments has to be applied.

Source code in src/quivers/qiec/types.py
def __post_init__(self) -> None:
    """Check the literal inhabits the sort it claims.

    Raises
    ------
    ValueError
        If a `Nat` literal is not a nonnegative integer, if a user
        sort's literal names a constructor that sort does not
        declare, or if it names one that takes arguments. The last is
        the subtle case: a nullary constructor is a literal, while
        one with arguments has to be applied.
    """

    if isinstance(self.sort, NatSort):
        if not isinstance(self.value, int) or self.value < 0:
            raise ValueError("Nat literals must be nonnegative integers")
    if (
        isinstance(self.sort, UserIndexSort)
        and self.value not in self.sort.constructors
    ):
        raise ValueError(
            f"{self.value!r} is not a constructor of index sort {self.sort.name!r}"
        )
    if (
        isinstance(self.sort, UserIndexSort)
        and isinstance(self.value, str)
        and self.sort.constructor_arity(self.value) != 0
    ):
        raise ValueError(f"index constructor {self.value!r} is not nullary")

IndexVariable dataclass

IndexVariable(name: str, sort: IndexSort, identity: StaticVariableId | None = None, tag: Literal['index_variable'] = 'index_variable')

A variable standing for an index term.

PARAMETER DESCRIPTION
name

The variable's display name.

TYPE: str

sort

The closed index sort of the terms it stands for.

TYPE: IndexSort

identity

The rigid identity of a case branch's skolem, or None for a declaration binder matched by name.

TYPE: StaticVariableId | None DEFAULT: None

tag

The serialization discriminator; always "index_variable".

TYPE: Literal['index_variable'] DEFAULT: 'index_variable'

__eq__

__eq__(other: object) -> bool

Compare by identity when either side is rigid, else by name.

A variable carrying a StaticVariableId is rigid: a case branch's skolem, distinct from every other even where the names agree. One without is a declaration binder, which two occurrences share by name. Comparing a rigid variable to a free one is therefore false, which is what stops a branch's skolem from unifying with an enclosing binder.

PARAMETER DESCRIPTION
other

The value to compare against.

TYPE: object

RETURNS DESCRIPTION
bool

True when both denote the same variable at the same sort.

Source code in src/quivers/qiec/types.py
def __eq__(self, other: object) -> bool:
    """Compare by identity when either side is rigid, else by name.

    A variable carrying a `StaticVariableId` is rigid: a case branch's
    skolem, distinct from every other even where the names agree. One
    without is a declaration binder, which two occurrences share by
    name. Comparing a rigid variable to a free one is therefore
    false, which is what stops a branch's skolem from unifying with
    an enclosing binder.

    Parameters
    ----------
    other : object
        The value to compare against.

    Returns
    -------
    bool
        True when both denote the same variable at the same sort.
    """
    if not isinstance(other, IndexVariable) or self.sort != other.sort:
        return False
    if self.identity is not None or other.identity is not None:
        return self.identity is not None and self.identity == other.identity
    return self.name == other.name

__hash__

__hash__() -> int

Hash the fields __eq__ compares.

RETURNS DESCRIPTION
int

A hash of the sort with the identity when rigid, and with the name otherwise, so equal variables hash alike.

Source code in src/quivers/qiec/types.py
def __hash__(self) -> int:
    """Hash the fields `__eq__` compares.

    Returns
    -------
    int
        A hash of the sort with the identity when rigid, and with
        the name otherwise, so equal variables hash alike.
    """
    return hash(
        (self.sort, self.identity if self.identity is not None else self.name)
    )

ShapeIndex dataclass

ShapeIndex(dimensions: tuple[IndexTerm, ...], tag: Literal['shape_index'] = 'shape_index')

A shape given by its dimensions.

PARAMETER DESCRIPTION
dimensions

One index term per dimension, outermost first.

TYPE: tuple[IndexTerm, ...]

tag

The serialization discriminator; always "shape_index".

TYPE: Literal['shape_index'] DEFAULT: 'shape_index'

TypeApplication dataclass

TypeApplication(constructor: TypeConstructorRef, arguments: tuple[StaticArgument, ...] = (), tag: Literal['type_application'] = 'type_application')

A type constructor applied to static arguments.

PARAMETER DESCRIPTION
constructor

The constructor applied.

TYPE: TypeConstructorRef

arguments

The arguments instantiating the constructor's telescope, in order; empty for a nullary constructor.

TYPE: tuple[StaticArgument, ...] DEFAULT: ()

tag

The serialization discriminator; always "type_application".

TYPE: Literal['type_application'] DEFAULT: 'type_application'

TypeConstructorRef dataclass

TypeConstructorRef(id: TypeId, name: str, telescope: Telescope = ())

A fully qualified type constructor and its kinding telescope.

PARAMETER DESCRIPTION
id

The constructor's stable identity, which alone determines equality.

TYPE: TypeId

name

The constructor's display name.

TYPE: str

telescope

The binders the constructor's arguments instantiate, in order.

TYPE: Telescope DEFAULT: ()

builtin classmethod

builtin(name: str) -> TypeConstructorRef

A constructor for one of the language's own types.

PARAMETER DESCRIPTION
name

The builtin's name.

TYPE: str

RETURNS DESCRIPTION
TypeConstructorRef

A nullary constructor whose identity derives from the builtin namespace, so Int means the same type in every module without being declared in any of them.

Source code in src/quivers/qiec/types.py
@classmethod
def builtin(cls, name: str) -> TypeConstructorRef:
    """A constructor for one of the language's own types.

    Parameters
    ----------
    name : str
        The builtin's name.

    Returns
    -------
    TypeConstructorRef
        A nullary constructor whose identity derives from the
        ``builtin`` namespace, so `Int` means the same type in every
        module without being declared in any of them.
    """
    return cls(TypeId.derive("builtin", name), name)

__eq__

__eq__(other: object) -> bool

Compare semantic identity, excluding diagnostic presentation data.

PARAMETER DESCRIPTION
other

The value to compare against.

TYPE: object

RETURNS DESCRIPTION
bool

True when both name the same declaration. The display name and the telescope are presentation, so a constructor read back under a different spelling is still the same type.

Source code in src/quivers/qiec/types.py
def __eq__(self, other: object) -> bool:
    """Compare semantic identity, excluding diagnostic presentation data.

    Parameters
    ----------
    other : object
        The value to compare against.

    Returns
    -------
    bool
        True when both name the same declaration. The display name
        and the telescope are presentation, so a constructor read
        back under a different spelling is still the same type.
    """
    return isinstance(other, TypeConstructorRef) and self.id == other.id

__hash__

__hash__() -> int

Hash the identity __eq__ compares.

RETURNS DESCRIPTION
int

A hash of the declaration identity alone.

Source code in src/quivers/qiec/types.py
def __hash__(self) -> int:
    """Hash the identity `__eq__` compares.

    Returns
    -------
    int
        A hash of the declaration identity alone.
    """
    return hash(self.id)

TypeVariable dataclass

TypeVariable(name: str, kind: Kind = TYPE, identity: StaticVariableId | None = None, tag: Literal['type_variable'] = 'type_variable')

A variable standing for a type or effect-kinded static argument.

PARAMETER DESCRIPTION
name

The variable's display name.

TYPE: str

kind

The kind of the arguments it stands for.

TYPE: Kind DEFAULT: TYPE

identity

The rigid identity of a case branch's skolem, or None for a declaration binder matched by name.

TYPE: StaticVariableId | None DEFAULT: None

tag

The serialization discriminator; always "type_variable".

TYPE: Literal['type_variable'] DEFAULT: 'type_variable'

__eq__

__eq__(other: object) -> bool

Compare by identity when either side is rigid, else by name.

A variable carrying a StaticVariableId is rigid: a case branch's skolem, distinct from every other even where the names agree. One without is a declaration binder, which two occurrences share by name. Comparing a rigid variable to a free one is therefore false, which is what stops a branch's skolem from unifying with an enclosing binder.

PARAMETER DESCRIPTION
other

The value to compare against.

TYPE: object

RETURNS DESCRIPTION
bool

True when both denote the same variable at the same kind.

Source code in src/quivers/qiec/types.py
def __eq__(self, other: object) -> bool:
    """Compare by identity when either side is rigid, else by name.

    A variable carrying a `StaticVariableId` is rigid: a case branch's
    skolem, distinct from every other even where the names agree. One
    without is a declaration binder, which two occurrences share by
    name. Comparing a rigid variable to a free one is therefore
    false, which is what stops a branch's skolem from unifying with
    an enclosing binder.

    Parameters
    ----------
    other : object
        The value to compare against.

    Returns
    -------
    bool
        True when both denote the same variable at the same kind.
    """
    if not isinstance(other, TypeVariable) or self.kind != other.kind:
        return False
    if self.identity is not None or other.identity is not None:
        return self.identity is not None and self.identity == other.identity
    return self.name == other.name

__hash__

__hash__() -> int

Hash the fields __eq__ compares.

RETURNS DESCRIPTION
int

A hash of the kind with the identity when rigid, and with the name otherwise, so equal variables hash alike.

Source code in src/quivers/qiec/types.py
def __hash__(self) -> int:
    """Hash the fields `__eq__` compares.

    Returns
    -------
    int
        A hash of the kind with the identity when rigid, and with
        the name otherwise, so equal variables hash alike.
    """
    return hash(
        (self.kind, self.identity if self.identity is not None else self.name)
    )

StructuralRuntimeProvider dataclass

StructuralRuntimeProvider(runtimes: tuple[RuntimeHandler, ...], term_types: frozenset[TypeId], name: str = 'structural')

Bases: RuntimeProvider

PyTorch components and validators for one structural invocation.

PARAMETER DESCRIPTION
runtimes

The host handlers implementing the module's structural computations.

TYPE: tuple[RuntimeHandler, ...]

term_types

Type identities whose host values are structural :class:Term instances.

TYPE: frozenset[TypeId]

name

The provider name recorded in execution results and traces.

TYPE: str DEFAULT: 'structural'

attach

attach(module: QiecModule, attachments: RuntimeAttachments) -> None

Attach each host computation handler.

PARAMETER DESCRIPTION
module

The checked module, unused because the handlers were selected while constructing this provider.

TYPE: QiecModule

attachments

The invocation's attachment table.

TYPE: RuntimeAttachments

Source code in src/quivers/qiec/structural_runtime.py
def attach(self, module: QiecModule, attachments: RuntimeAttachments) -> None:
    """Attach each host computation handler.

    Parameters
    ----------
    module
        The checked module, unused because the handlers were selected
        while constructing this provider.
    attachments
        The invocation's attachment table.
    """

    del module
    for runtime in self.runtimes:
        attachments.bind_handler(runtime)

validator_for

validator_for(type_: TypeExpr) -> RuntimeValidator | None

Return the validator for structural terms and torch values.

PARAMETER DESCRIPTION
type_

The closed QIEC type a host argument or result must inhabit.

TYPE: TypeExpr

RETURNS DESCRIPTION
RuntimeValidator | None

A predicate for scalar weights, structural terms, tensors, and structural products, or None when the provider does not cover type_.

Source code in src/quivers/qiec/structural_runtime.py
def validator_for(self, type_: TypeExpr) -> RuntimeValidator | None:
    """Return the validator for structural terms and torch values.

    Parameters
    ----------
    type_
        The closed QIEC type a host argument or result must inhabit.

    Returns
    -------
    RuntimeValidator | None
        A predicate for scalar weights, structural terms, tensors, and
        structural products, or ``None`` when the provider does not cover
        ``type_``.
    """

    if type_ == REAL or type_ == LOG_WEIGHT:
        return lambda value: (
            isinstance(value, int | float)
            and not isinstance(value, bool)
            or isinstance(value, torch.Tensor)
            and value.shape == ()
        )
    if _is_term_type(type_, self.term_types):
        return lambda value: isinstance(value, Term)
    if tensor_shape(type_) is not None:
        return lambda value: _tensor_value(type_, value)
    if isinstance(type_, TypeApplication) and type_.constructor.name.startswith(
        "Product"
    ):
        components = tuple(
            item for item in type_.arguments if isinstance(item, TypeApplication)
        )
        validators = tuple(self.validator_for(item) for item in components)
        if len(validators) != len(type_.arguments) or any(
            validator is None for validator in validators
        ):
            return None

        def product(value: object) -> bool:
            """Check a structural product componentwise.

            Parameters
            ----------
            value
                The host value to validate against the product type.

            Returns
            -------
            bool
                Whether ``value`` is a tuple whose components pass the
                corresponding validators.
            """

            return (
                isinstance(value, tuple)
                and len(value) == len(validators)
                and all(
                    validator is not None and validator(item)
                    for validator, item in zip(validators, value, strict=True)
                )
            )

        return product
    return None

abort_effect

abort_effect(error_type: TypeExpr) -> EffectRef

Construct the concrete Abort[error_type] interface.

PARAMETER DESCRIPTION
error_type

The type to instantiate the interface's binder at.

TYPE: TypeExpr

RETURNS DESCRIPTION
EffectRef

The saturated application.

RAISES DESCRIPTION
TypeError

If the argument is not a type.

ValueError

If it is ill-kinded.

Source code in src/quivers/qiec/builtins.py
def abort_effect(error_type: TypeExpr) -> EffectRef:
    """Construct the concrete ``Abort[error_type]`` interface.

    Parameters
    ----------
    error_type : TypeExpr
        The type to instantiate the interface's binder at.

    Returns
    -------
    EffectRef
        The saturated application.

    Raises
    ------
    TypeError
        If the argument is not a type.
    ValueError
        If it is ill-kinded.
    """
    return ABORT_EFFECT.apply((error_type,))

abort_handler

abort_handler(*, on_abort: Callable[[object], object] = Aborted, result_validator: RuntimeValidator = lambda _value: True, error_type: TypeExpr = PAYLOAD, input_type: TypeExpr = ANSWER, output_type: TypeExpr = ANSWER, output_validator: RuntimeValidator = lambda _value: True, duplicable_context: bool = False, key: str = 'abort') -> RuntimeHandler

Handle Abort.abort without resuming the discarded continuation.

PARAMETER DESCRIPTION
on_abort

Receives the payload when the computation aborts and supplies the handler's answer; wraps it in Aborted by default.

TYPE: Callable[[object], object] DEFAULT: Aborted

result_validator

The abort operation's result validator; never exercised, since the clause does not resume.

TYPE: RuntimeValidator DEFAULT: lambda _value: True

error_type

The payload type an abort carries.

TYPE: TypeExpr DEFAULT: PAYLOAD

input_type

What the handled computation returns.

TYPE: TypeExpr DEFAULT: ANSWER

output_type

What the handler answers with.

TYPE: TypeExpr DEFAULT: ANSWER

output_validator

Checks the handler's answer.

TYPE: RuntimeValidator DEFAULT: lambda _value: True

duplicable_context

Whether the handler's own state may be copied for a multi-shot resumption. Asserted rather than inferred: copying a handler owning mutable state would let two shots share it.

TYPE: bool DEFAULT: False

key

Distinguishes this handler from others of the same name, entering its derived identity.

TYPE: str DEFAULT: 'abort'

RETURNS DESCRIPTION
RuntimeHandler

The attachment, ready to bind into a runtime environment.

Source code in src/quivers/qiec/builtins.py
def abort_handler(
    *,
    on_abort: Callable[[object], object] = Aborted,
    result_validator: RuntimeValidator = lambda _value: True,
    error_type: TypeExpr = PAYLOAD,
    input_type: TypeExpr = ANSWER,
    output_type: TypeExpr = ANSWER,
    output_validator: RuntimeValidator = lambda _value: True,
    duplicable_context: bool = False,
    key: str = "abort",
) -> RuntimeHandler:
    """Handle Abort.abort without resuming the discarded continuation.

    Parameters
    ----------
    on_abort
        Receives the payload when the computation aborts and supplies
        the handler's answer; wraps it in `Aborted` by default.
    result_validator
        The abort operation's result validator; never exercised, since
        the clause does not resume.
    error_type
        The payload type an abort carries.
    input_type
        What the handled computation returns.
    output_type
        What the handler answers with.
    output_validator
        Checks the handler's answer.
    duplicable_context
        Whether the handler's own state may be copied for a
        multi-shot resumption. Asserted rather than inferred: copying a
        handler owning mutable state would let two shots share it.
    key
        Distinguishes this handler from others of the same name,
        entering its derived identity.

    Returns
    -------
    RuntimeHandler
        The attachment, ready to bind into a runtime environment.
    """
    effect = abort_effect(error_type)
    definition = _handler_def(
        "Abort.run",
        effect,
        ((ABORT_ABORT, ResumptionGrade.ZERO),),
        key=key,
        input_type=input_type,
        output_type=output_type,
        telescope=tuple(
            dict.fromkeys(
                (
                    *_generic_binders(answer_type=input_type),
                    *_generic_binders(answer_type=output_type),
                    *_generic_binders(error_type=error_type),
                )
            )
        ),
    )

    def abort(
        request: RuntimeRequest,
        _resume: Resumption,
        _context: ClauseContext,
    ) -> object:
        """Answer an abort request without resuming.

        Parameters
        ----------
        request
            The request being answered and its arguments.
        _resume
            The continuation, which this clause does not invoke.
        _context
            Runtime services, unused by this clause.

        Returns
        -------
        object
            What ``on_abort`` makes of the payload; the continuation is
            discarded.

        Raises
        ------
        InvalidHandlerError
            If the request does not carry exactly one argument.
        """
        (error,) = _expect_arguments(request, 1, definition.name)
        return on_abort(error)

    return RuntimeHandler(
        definition,
        {ABORT_ABORT: RuntimeClause(abort, result_validator)},
        output_validator=output_validator,
        duplicable_context=duplicable_context,
    )

add_weights

add_weights(left: object, right: object) -> object

Add two log weights, entrywise over tensors of them.

PARAMETER DESCRIPTION
left

A float, or nested tuples of floats.

TYPE: object

right

A float, or nested tuples of floats shaped like left when both are tensors.

TYPE: object

RETURNS DESCRIPTION
object

The entrywise sum; a scalar broadcasts over a tensor.

RAISES DESCRIPTION
RuntimeTypeMismatch

If two tensors differ in shape.

Source code in src/quivers/qiec/builtins.py
def add_weights(left: object, right: object) -> object:
    """Add two log weights, entrywise over tensors of them.

    Parameters
    ----------
    left : object
        A float, or nested tuples of floats.
    right : object
        A float, or nested tuples of floats shaped like ``left`` when
        both are tensors.

    Returns
    -------
    object
        The entrywise sum; a scalar broadcasts over a tensor.

    Raises
    ------
    RuntimeTypeMismatch
        If two tensors differ in shape.
    """
    if isinstance(left, tuple) and isinstance(right, tuple):
        if len(left) != len(right):
            raise RuntimeTypeMismatch(
                f"weights of lengths {len(left)} and {len(right)} cannot be added"
            )
        return tuple(add_weights(a, b) for a, b in zip(left, right, strict=True))
    if isinstance(left, tuple):
        return tuple(add_weights(item, right) for item in left)
    if isinstance(right, tuple):
        return tuple(add_weights(left, item) for item in right)
    return cast(float, left) + cast(float, right)

block_handler

block_handler(hidden: Callable[[object], bool], *, score_instance: EffectInstanceId, result_validator: RuntimeValidator, weight_validator: RuntimeValidator = lambda _value: True, draw: Callable[[object, object, RuntimeRequest], object] | None = None, answer_type: TypeExpr = ANSWER, key: str = 'block') -> RuntimeHandler

Answer hidden sites locally so no outer handler sees them.

A hidden site is drawn and scored here, exactly as the default scoring draw would; every other site forwards.

PARAMETER DESCRIPTION
hidden

Whether a site is hidden from the handlers outside this one.

TYPE: Callable[[object], bool]

score_instance

The lexical Score instance a hidden draw's log density is sent to.

TYPE: EffectInstanceId

result_validator

Checks the value a resumption carries inhabits the operation's result type.

TYPE: RuntimeValidator

weight_validator

Checks each log-density contribution.

TYPE: RuntimeValidator DEFAULT: lambda _value: True

draw

Supplies the drawn value for a hidden site, given the site, the sampleable, and the request. None uses the sampleable's own sampling interface.

TYPE: Callable[[object, object, RuntimeRequest], object] | None DEFAULT: None

answer_type

What the handler answers with.

TYPE: TypeExpr DEFAULT: ANSWER

key

Distinguishes this handler from others of the same name, entering its derived identity.

TYPE: str DEFAULT: 'block'

RETURNS DESCRIPTION
RuntimeHandler

The attachment, ready to bind into a runtime environment.

Source code in src/quivers/qiec/builtins.py
def block_handler(
    hidden: Callable[[object], bool],
    *,
    score_instance: EffectInstanceId,
    result_validator: RuntimeValidator,
    weight_validator: RuntimeValidator = lambda _value: True,
    draw: Callable[[object, object, RuntimeRequest], object] | None = None,
    answer_type: TypeExpr = ANSWER,
    key: str = "block",
) -> RuntimeHandler:
    """Answer hidden sites locally so no outer handler sees them.

    A hidden site is drawn and scored here, exactly as the default
    scoring draw would; every other site forwards.

    Parameters
    ----------
    hidden
        Whether a site is hidden from the handlers outside this one.
    score_instance
        The lexical `Score` instance a hidden draw's log density is sent
        to.
    result_validator
        Checks the value a resumption carries inhabits the operation's
        result type.
    weight_validator
        Checks each log-density contribution.
    draw
        Supplies the drawn value for a hidden site, given the site, the
        sampleable, and the request. None uses the sampleable's own
        sampling interface.
    answer_type
        What the handler answers with.
    key
        Distinguishes this handler from others of the same name,
        entering its derived identity.

    Returns
    -------
    RuntimeHandler
        The attachment, ready to bind into a runtime environment.
    """
    definition = _handler_def(
        "Random.block",
        RANDOM,
        ((RANDOM_SAMPLE, ResumptionGrade.LINEAR),),
        key=key,
        input_type=answer_type,
        output_type=answer_type,
        introduced=EffectRow((RowEntry(score_instance, SCORE),)),
        total=False,
        telescope=_generic_binders(answer_type=answer_type),
    )

    def sample(
        request: RuntimeRequest,
        resume: Resumption,
        context: ClauseContext,
    ) -> object:
        """Answer a hidden site's `Random.sample` request locally.

        Parameters
        ----------
        request
            The request being answered and its arguments.
        resume
            The continuation, invoked within the clause's declared grade.
        context
            Runtime services available to the clause.

        Returns
        -------
        object
            What the resumed computation produced, or a `Forward` for a
            site that is not hidden.

        Raises
        ------
        InvalidHandlerError
            If the request does not carry a site and a sampleable, or the
            sampleable offers no sampling or scoring interface.
        """
        site, sampleable = _site_and_sampleable(request, definition.name)
        if not hidden(site):
            return Forward()
        value = (
            draw(site, sampleable, request)
            if draw is not None
            else _default_draw(sampleable)
        )
        _emit_score(
            context,
            request,
            score_instance,
            _log_density(sampleable, value),
            weight_validator,
            role="block-score",
        )
        return TailResume(value)

    return RuntimeHandler(
        definition, {RANDOM_SAMPLE: RuntimeClause(sample, result_validator)}
    )

choose_handler

choose_handler(choice_validator: RuntimeValidator, *, combine: Callable[[list[object]], object] | None = None, combine_validator: RuntimeValidator | None = None, answer_type: TypeExpr = ANSWER, answer_validator: RuntimeValidator = lambda _value: True, key: str = 'choose') -> RuntimeHandler

Enumerate finite choices using an unrestricted deep resumption.

PARAMETER DESCRIPTION
choice_validator

Checks each alternative resumed with.

TYPE: RuntimeValidator

combine

Folds the per-alternative results into the handler's answer; None concatenates them, the return clause having wrapped each answer in a one-element tuple.

TYPE: Callable[[list[object]], object] | None DEFAULT: None

combine_validator

Checks the combined answer; required with a custom combine, and otherwise a tuple of answer_validator values.

TYPE: RuntimeValidator | None DEFAULT: None

answer_type

What the handled computation returns; the handler answers with ChoiceResults of it.

TYPE: TypeExpr DEFAULT: ANSWER

answer_validator

Checks the handled computation's answer.

TYPE: RuntimeValidator DEFAULT: lambda _value: True

key

Distinguishes this handler from others of the same name, entering its derived identity.

TYPE: str DEFAULT: 'choose'

RETURNS DESCRIPTION
RuntimeHandler

The attachment, ready to bind into a runtime environment.

RAISES DESCRIPTION
ValueError

If combine is given without combine_validator.

Source code in src/quivers/qiec/builtins.py
def choose_handler(
    choice_validator: RuntimeValidator,
    *,
    combine: Callable[[list[object]], object] | None = None,
    combine_validator: RuntimeValidator | None = None,
    answer_type: TypeExpr = ANSWER,
    answer_validator: RuntimeValidator = lambda _value: True,
    key: str = "choose",
) -> RuntimeHandler:
    """Enumerate finite choices using an unrestricted deep resumption.

    Parameters
    ----------
    choice_validator
        Checks each alternative resumed with.
    combine
        Folds the per-alternative results into the handler's answer;
        ``None`` concatenates them, the return clause having wrapped each
        answer in a one-element tuple.
    combine_validator
        Checks the combined answer; required with a custom ``combine``,
        and otherwise a tuple of ``answer_validator`` values.
    answer_type
        What the handled computation returns; the handler answers with
        ``ChoiceResults`` of it.
    answer_validator
        Checks the handled computation's answer.
    key
        Distinguishes this handler from others of the same name,
        entering its derived identity.

    Returns
    -------
    RuntimeHandler
        The attachment, ready to bind into a runtime environment.

    Raises
    ------
    ValueError
        If ``combine`` is given without ``combine_validator``.
    """
    if combine is not None and combine_validator is None:
        raise ValueError("a custom Choose combine needs an output validator")
    definition = _handler_def(
        "Choose.enumerate",
        CHOOSE,
        ((CHOOSE_CHOOSE, ResumptionGrade.UNRESTRICTED),),
        key=key,
        input_type=answer_type,
        output_type=TypeApplication(CHOICE_RESULTS_CONSTRUCTOR, (answer_type,)),
        telescope=_generic_binders(answer_type=answer_type),
    )

    def choose(
        request: RuntimeRequest,
        resume: Resumption,
        _context: ClauseContext,
    ) -> object:
        """Answer a choice request, resuming once per alternative.

        Parameters
        ----------
        request
            The request being answered and its arguments.
        resume
            The continuation, invoked within the clause's declared grade.
        _context
            Runtime services, unused by this clause.

        Returns
        -------
        object
            The combined result over every alternative.

        Raises
        ------
        InvalidHandlerError
            If the request does not carry exactly one argument, the
            alternatives are a string, bytes, or not iterable, or, under
            the default combination, the return clause's results are not
            tuples.
        """
        (alternatives,) = _expect_arguments(request, 1, definition.name)
        if isinstance(alternatives, (str, bytes)):
            raise InvalidHandlerError("Choose alternatives must be a finite collection")
        try:
            options = tuple(alternatives)  # type: ignore[arg-type]
        except TypeError as error:
            raise InvalidHandlerError("Choose alternatives must be iterable") from error
        results = [resume(option) for option in options]
        if combine is not None:
            return combine(results)
        flattened: list[object] = []
        for result in results:
            if not isinstance(result, tuple):
                raise InvalidHandlerError(
                    "the default Choose handler expects its deep return clause "
                    "to produce tuples"
                )
            flattened.extend(result)
        return tuple(flattened)

    return RuntimeHandler(
        definition,
        {CHOOSE_CHOOSE: RuntimeClause(choose, choice_validator)},
        return_clause=lambda value, _context: (value,),
        input_validator=answer_validator,
        output_validator=(
            combine_validator
            if combine_validator is not None
            else _tuple_of(answer_validator)
        ),
        duplicable_context=True,
    )

compute_handler

compute_handler(function: Callable[[object, ClauseContext], object], *, effect: EffectRef, result_validator: RuntimeValidator, introduced: EffectRow = EffectRow(), answer_type: TypeExpr = ANSWER, key: str = 'compute') -> RuntimeHandler

Serve a Compute instance with a host function.

PARAMETER DESCRIPTION
function

The host function, called with the request's argument and the clause context, through which it may perform effects the introduced row names.

TYPE: Callable[[object, ClauseContext], object]

effect

The applied Compute[X, A] interface the instance carries.

TYPE: EffectRef

result_validator

Checks the function's result inhabits the operation's result type.

TYPE: RuntimeValidator

introduced

Effects the host function performs.

TYPE: EffectRow DEFAULT: EffectRow()

answer_type

What the handler answers with.

TYPE: TypeExpr DEFAULT: ANSWER

key

Distinguishes this handler from others of the same name, entering its derived identity.

TYPE: str DEFAULT: 'compute'

RETURNS DESCRIPTION
RuntimeHandler

The attachment, ready to bind into a runtime environment.

Source code in src/quivers/qiec/builtins.py
def compute_handler(
    function: Callable[[object, ClauseContext], object],
    *,
    effect: EffectRef,
    result_validator: RuntimeValidator,
    introduced: EffectRow = EffectRow(),
    answer_type: TypeExpr = ANSWER,
    key: str = "compute",
) -> RuntimeHandler:
    """Serve a ``Compute`` instance with a host function.

    Parameters
    ----------
    function
        The host function, called with the request's argument and the
        clause context, through which it may perform effects the
        ``introduced`` row names.
    effect
        The applied ``Compute[X, A]`` interface the instance carries.
    result_validator
        Checks the function's result inhabits the operation's result
        type.
    introduced
        Effects the host function performs.
    answer_type
        What the handler answers with.
    key
        Distinguishes this handler from others of the same name,
        entering its derived identity.

    Returns
    -------
    RuntimeHandler
        The attachment, ready to bind into a runtime environment.
    """
    definition = _handler_def(
        "Compute.host",
        effect,
        ((COMPUTE_APPLY, ResumptionGrade.LINEAR),),
        key=key,
        input_type=answer_type,
        output_type=answer_type,
        introduced=introduced,
        telescope=_generic_binders(answer_type=answer_type),
    )

    def apply(
        request: RuntimeRequest,
        resume: Resumption,
        context: ClauseContext,
    ) -> object:
        """Answer a `Compute.apply` request with the host function's result.

        Parameters
        ----------
        request
            The request being answered and its arguments.
        resume
            The continuation, invoked within the clause's declared grade.
        context
            Runtime services available to the clause.

        Returns
        -------
        object
            What the resumed computation produced.

        Raises
        ------
        InvalidHandlerError
            If the request does not carry exactly one argument.
        """
        (argument,) = _expect_arguments(request, 1, definition.name)
        return TailResume(function(argument, context))

    return RuntimeHandler(
        definition, {COMPUTE_APPLY: RuntimeClause(apply, result_validator)}
    )

condition_handler

condition_handler(observations: Mapping[object, object], *, score_instance: EffectInstanceId, result_validator: RuntimeValidator, weight_validator: RuntimeValidator = lambda _value: True, missing: MissingValuePolicy = FORWARD, extra: ExtraValuePolicy = ERROR, answer_type: TypeExpr = ANSWER, key: str = 'condition') -> RuntimeHandler

Supply likelihood observations and emit their densities as Score.

Unobserved sites forward to the next Random handler. The explicit introduced Score row distinguishes likelihood observation from intervention and unscored replay.

PARAMETER DESCRIPTION
observations

The values to condition on, keyed by site.

TYPE: Mapping[object, object]

score_instance

The lexical Score instance each observation's log density is sent to.

TYPE: EffectInstanceId

result_validator

Checks an observed value inhabits the request's result type.

TYPE: RuntimeValidator

weight_validator

Checks each log-density contribution.

TYPE: RuntimeValidator DEFAULT: lambda _value: True

missing

Whether a sample request at a site with no observation is forwarded outward or rejected.

TYPE: MissingValuePolicy DEFAULT: FORWARD

extra

What to do, when the handled computation returns, with an observation at a site the computation never sampled.

TYPE: ExtraValuePolicy DEFAULT: ERROR

answer_type

What the handler answers with.

TYPE: TypeExpr DEFAULT: ANSWER

key

Distinguishes this handler from others of the same name, entering its derived identity.

TYPE: str DEFAULT: 'condition'

RETURNS DESCRIPTION
RuntimeHandler

The attachment, ready to bind into a runtime environment.

Source code in src/quivers/qiec/builtins.py
def condition_handler(
    observations: Mapping[object, object],
    *,
    score_instance: EffectInstanceId,
    result_validator: RuntimeValidator,
    weight_validator: RuntimeValidator = lambda _value: True,
    missing: MissingValuePolicy = MissingValuePolicy.FORWARD,
    extra: ExtraValuePolicy = ExtraValuePolicy.ERROR,
    answer_type: TypeExpr = ANSWER,
    key: str = "condition",
) -> RuntimeHandler:
    """Supply likelihood observations and emit their densities as Score.

    Unobserved sites forward to the next ``Random`` handler.  The explicit
    introduced Score row distinguishes likelihood observation from intervention
    and unscored replay.

    Parameters
    ----------
    observations
        The values to condition on, keyed by site.
    score_instance
        The lexical `Score` instance each observation's log density is
        sent to.
    result_validator
        Checks an observed value inhabits the request's result type.
    weight_validator
        Checks each log-density contribution.
    missing
        Whether a sample request at a site with no observation is
        forwarded outward or rejected.
    extra
        What to do, when the handled computation returns, with an
        observation at a site the computation never sampled.
    answer_type
        What the handler answers with.
    key
        Distinguishes this handler from others of the same name,
        entering its derived identity.

    Returns
    -------
    RuntimeHandler
        The attachment, ready to bind into a runtime environment.
    """
    definition = _handler_def(
        "Random.condition",
        RANDOM,
        ((RANDOM_SAMPLE, ResumptionGrade.LINEAR),),
        key=key,
        input_type=answer_type,
        output_type=answer_type,
        introduced=EffectRow((RowEntry(score_instance, SCORE),)),
        total=False,
        telescope=_generic_binders(answer_type=answer_type),
    )

    def make_context() -> RuntimeHandler:
        """Build a fresh per-installation handler.

        Returns
        -------
        RuntimeHandler
            A handler with its own state, so two installations of this
            declaration do not share it.
        """
        seen: set[object] = set()

        def sample(
            request: RuntimeRequest,
            resume: Resumption,
            context: ClauseContext,
        ) -> object:
            """Answer a `Random.sample` request.

            Parameters
            ----------
            request
                The request being answered and its arguments.
            resume
                The continuation, invoked within the clause's declared grade.
            context
                Runtime services available to the clause.

            Returns
            -------
            object
                What the resumed computation produced, or a `Forward` when
                the site has no observation and ``missing`` forwards.

            Raises
            ------
            InvalidHandlerError
                If the request's site has no observation and ``missing``
                rejects, or the request does not carry a site and a
                sampleable.
            RuntimeTypeMismatch
                If the observed value does not inhabit the site's type.
            """
            site, sampleable = _site_and_sampleable(request, definition.name)
            if site not in observations:
                if missing is MissingValuePolicy.ERROR:
                    raise InvalidHandlerError(f"missing observation for site {site!r}")
                return Forward()
            value = observations[site]
            _require(
                value,
                result_validator,
                request.core.result_type,
                f"observation {site!r}",
            )
            seen.add(site)
            weight = _log_density(sampleable, value)
            _emit_score(
                context,
                request,
                score_instance,
                weight,
                weight_validator,
                role="condition-score",
            )
            return TailResume(value)

        def finish(value: object, _context: ClauseContext) -> object:
            """Answer the handled computation's return.

            Parameters
            ----------
            value
                The value the handled computation returned.
            _context
                Runtime services, unused by this clause.

            Returns
            -------
            object
                The value, unchanged.

            Raises
            ------
            InvalidHandlerError
                If observations were supplied for sites the computation never
                sampled. Reported rather than ignored, since an unused
                observation usually means a site was renamed.
            """
            if extra is ExtraValuePolicy.ERROR:
                extras = set(observations) - seen
                if extras:
                    rendered = ", ".join(
                        repr(item) for item in sorted(extras, key=repr)
                    )
                    raise InvalidHandlerError(f"unused observations: {rendered}")
            return value

        return RuntimeHandler(
            definition,
            {RANDOM_SAMPLE: RuntimeClause(sample, result_validator)},
            return_clause=finish,
            mutable_context=True,
        )

    prototype = make_context()
    prototype.context_factory = make_context
    return prototype

draw_handler

draw_handler(result_validator: RuntimeValidator, *, draw: Callable[[object, object, RuntimeRequest], object] | None = None, answer_type: TypeExpr = ANSWER, duplicable_context: bool = False, key: str = 'draw') -> RuntimeHandler

Interpret Random.sample by drawing exactly once.

PARAMETER DESCRIPTION
result_validator

Checks the value a resumption carries inhabits the operation's result type.

TYPE: RuntimeValidator

draw

Supplies the drawn value, given the site, the sampleable, and the request. None uses the sampleable's own reparameterised or plain sampling interface.

TYPE: Callable[[object, object, RuntimeRequest], object] | None DEFAULT: None

answer_type

What the handler answers with.

TYPE: TypeExpr DEFAULT: ANSWER

duplicable_context

Whether the handler's own state may be copied for a multi-shot resumption. Asserted rather than inferred: copying a handler owning mutable state would let two shots share it.

TYPE: bool DEFAULT: False

key

Distinguishes this handler from others of the same name, entering its derived identity.

TYPE: str DEFAULT: 'draw'

RETURNS DESCRIPTION
RuntimeHandler

The attachment, ready to bind into a runtime environment.

Source code in src/quivers/qiec/builtins.py
def draw_handler(
    result_validator: RuntimeValidator,
    *,
    draw: Callable[[object, object, RuntimeRequest], object] | None = None,
    answer_type: TypeExpr = ANSWER,
    duplicable_context: bool = False,
    key: str = "draw",
) -> RuntimeHandler:
    """Interpret ``Random.sample`` by drawing exactly once.

    Parameters
    ----------
    result_validator
        Checks the value a resumption carries inhabits the
        operation's result type.
    draw
        Supplies the drawn value, given the site, the sampleable, and
        the request. None uses the sampleable's own reparameterised or
        plain sampling interface.
    answer_type
        What the handler answers with.
    duplicable_context
        Whether the handler's own state may be copied for a
        multi-shot resumption. Asserted rather than inferred: copying a
        handler owning mutable state would let two shots share it.
    key
        Distinguishes this handler from others of the same name,
        entering its derived identity.

    Returns
    -------
    RuntimeHandler
        The attachment, ready to bind into a runtime environment.
    """
    definition = _handler_def(
        "Random.draw",
        RANDOM,
        ((RANDOM_SAMPLE, ResumptionGrade.LINEAR),),
        key=key,
        input_type=answer_type,
        output_type=answer_type,
        telescope=_generic_binders(answer_type=answer_type),
    )

    def sample(
        request: RuntimeRequest,
        resume: Resumption,
        _context: ClauseContext,
    ) -> object:
        """Answer a `Random.sample` request.

        Parameters
        ----------
        request
            The request being answered and its arguments.
        resume
            The continuation, invoked within the clause's declared grade.
        _context
            Runtime services, unused by this clause.

        Returns
        -------
        object
            What the resumed computation produced.

        Raises
        ------
        InvalidHandlerError
            If the request does not carry a site and a sampleable, or the
            default draw finds no sampling interface on the sampleable.
        """
        site, sampleable = _expect_arguments(request, 2, definition.name)
        value = (
            draw(site, sampleable, request)
            if draw is not None
            else _default_draw(sampleable)
        )
        return TailResume(value)

    return RuntimeHandler(
        definition,
        {RANDOM_SAMPLE: RuntimeClause(sample, result_validator)},
        duplicable_context=duplicable_context,
    )

draw_scoring_handler

draw_scoring_handler(*, score_instance: EffectInstanceId, result_validator: RuntimeValidator, weight_validator: RuntimeValidator = lambda _value: True, draw: Callable[[object, object, RuntimeRequest], object] | None = None, answer_type: TypeExpr = ANSWER, key: str = 'draw-scoring') -> RuntimeHandler

Interpret Random.sample by drawing once and scoring the draw.

The draw's log density under its own sampleable is sent to a Score instance, so a run whose latent sites are drawn accumulates the joint density of the draw, which is what a trace of the run reports.

PARAMETER DESCRIPTION
score_instance

The lexical Score instance each draw's log density is sent to.

TYPE: EffectInstanceId

result_validator

Checks the value a resumption carries inhabits the operation's result type.

TYPE: RuntimeValidator

weight_validator

Checks each log-density contribution.

TYPE: RuntimeValidator DEFAULT: lambda _value: True

draw

Supplies the drawn value, given the site, the sampleable, and the request. None uses the sampleable's own sampling interface.

TYPE: Callable[[object, object, RuntimeRequest], object] | None DEFAULT: None

answer_type

What the handler answers with.

TYPE: TypeExpr DEFAULT: ANSWER

key

Distinguishes this handler from others of the same name, entering its derived identity.

TYPE: str DEFAULT: 'draw-scoring'

RETURNS DESCRIPTION
RuntimeHandler

The attachment, ready to bind into a runtime environment.

Source code in src/quivers/qiec/builtins.py
def draw_scoring_handler(
    *,
    score_instance: EffectInstanceId,
    result_validator: RuntimeValidator,
    weight_validator: RuntimeValidator = lambda _value: True,
    draw: Callable[[object, object, RuntimeRequest], object] | None = None,
    answer_type: TypeExpr = ANSWER,
    key: str = "draw-scoring",
) -> RuntimeHandler:
    """Interpret ``Random.sample`` by drawing once and scoring the draw.

    The draw's log density under its own sampleable is sent to a `Score`
    instance, so a run whose latent sites are drawn accumulates the joint
    density of the draw, which is what a trace of the run reports.

    Parameters
    ----------
    score_instance
        The lexical `Score` instance each draw's log density is sent to.
    result_validator
        Checks the value a resumption carries inhabits the operation's
        result type.
    weight_validator
        Checks each log-density contribution.
    draw
        Supplies the drawn value, given the site, the sampleable, and
        the request. None uses the sampleable's own sampling interface.
    answer_type
        What the handler answers with.
    key
        Distinguishes this handler from others of the same name,
        entering its derived identity.

    Returns
    -------
    RuntimeHandler
        The attachment, ready to bind into a runtime environment.
    """
    definition = _handler_def(
        "Random.draw-scoring",
        RANDOM,
        ((RANDOM_SAMPLE, ResumptionGrade.LINEAR),),
        key=key,
        input_type=answer_type,
        output_type=answer_type,
        introduced=EffectRow((RowEntry(score_instance, SCORE),)),
        telescope=_generic_binders(answer_type=answer_type),
    )

    def sample(
        request: RuntimeRequest,
        resume: Resumption,
        context: ClauseContext,
    ) -> object:
        """Answer a `Random.sample` request by drawing and scoring.

        Parameters
        ----------
        request
            The request being answered and its arguments.
        resume
            The continuation, invoked within the clause's declared grade.
        context
            Runtime services available to the clause.

        Returns
        -------
        object
            What the resumed computation produced.

        Raises
        ------
        InvalidHandlerError
            If the request does not carry a site and a sampleable, or the
            sampleable offers no sampling or scoring interface.
        """
        site, sampleable = _site_and_sampleable(request, definition.name)
        value = (
            draw(site, sampleable, request)
            if draw is not None
            else _default_draw(sampleable)
        )
        _emit_score(
            context,
            request,
            score_instance,
            _log_density(sampleable, value),
            weight_validator,
            role="draw-score",
        )
        return TailResume(value)

    return RuntimeHandler(
        definition,
        {RANDOM_SAMPLE: RuntimeClause(sample, result_validator)},
    )

enumerate_handler

enumerate_handler(result_validator: RuntimeValidator, *, answer_type: TypeExpr = ANSWER, weight_type: TypeExpr | None = None, reduction: str = 'logsumexp', key: str = 'enumerate') -> RuntimeHandler

Interpret Random.sample by aggregating over a finite support.

The handled computation must answer with its value paired with the log weight it accumulated, as the collecting Weight handler answers. The clause resumes once per support value, adds each shot's weight to the prior log probability of its value, and aggregates the shots by the reduction: logsumexp answers the log of the summed probabilities, the marginal; sum the sum of the weighted shots; mean their average. The aggregate is totalled over the plate's positions when the latent is plated, or kept one per position when a weight type is given, which is what a marginalization nested in a grouped one adds to its group's weights.

PARAMETER DESCRIPTION
result_validator

Checks the value a resumption carries inhabits the operation's result type.

TYPE: RuntimeValidator

answer_type

What the handled computation answers with.

TYPE: TypeExpr DEFAULT: ANSWER

weight_type

The type of the per-position weights the clause answers with, the collected weights' own tensor type, whose shape the answer takes from the shots' weights; None totals the positions into one LogWeight.

TYPE: TypeExpr | None DEFAULT: None

reduction

How the shots aggregate: "logsumexp", "sum", or "mean".

TYPE: str DEFAULT: 'logsumexp'

key

Distinguishes this handler from others of the same name.

TYPE: str DEFAULT: 'enumerate'

RETURNS DESCRIPTION
RuntimeHandler

The attachment.

RAISES DESCRIPTION
ValueError

If the reduction is not one of the three.

Source code in src/quivers/qiec/builtins.py
def enumerate_handler(
    result_validator: RuntimeValidator,
    *,
    answer_type: TypeExpr = ANSWER,
    weight_type: TypeExpr | None = None,
    reduction: str = "logsumexp",
    key: str = "enumerate",
) -> RuntimeHandler:
    """Interpret ``Random.sample`` by aggregating over a finite support.

    The handled computation must answer with its value paired with the
    log weight it accumulated, as the collecting ``Weight`` handler
    answers. The clause resumes once per support value, adds each shot's
    weight to the prior log probability of its value, and aggregates the
    shots by the reduction: ``logsumexp`` answers the log of the summed
    probabilities, the marginal; ``sum`` the sum of the weighted shots;
    ``mean`` their average. The aggregate is totalled over the plate's
    positions when the latent is plated, or kept one per position when
    a weight type is given, which is what a marginalization nested in a
    grouped one adds to its group's weights.

    Parameters
    ----------
    result_validator
        Checks the value a resumption carries inhabits the operation's
        result type.
    answer_type
        What the handled computation answers with.
    weight_type
        The type of the per-position weights the clause answers with,
        the collected weights' own tensor type, whose shape the answer
        takes from the shots' weights; ``None`` totals the positions
        into one ``LogWeight``.
    reduction
        How the shots aggregate: ``"logsumexp"``, ``"sum"``, or
        ``"mean"``.
    key
        Distinguishes this handler from others of the same name.

    Returns
    -------
    RuntimeHandler
        The attachment.

    Raises
    ------
    ValueError
        If the reduction is not one of the three.
    """
    if reduction not in ("logsumexp", "sum", "mean"):
        raise ValueError(
            f"enumeration reduces by logsumexp, sum, or mean, not {reduction!r}"
        )
    definition = _handler_def(
        "Random.enumerate",
        RANDOM,
        ((RANDOM_SAMPLE, ResumptionGrade.UNRESTRICTED),),
        key=key,
        input_type=answer_type,
        output_type=LOG_WEIGHT if weight_type is None else weight_type,
        telescope=_generic_binders(answer_type=answer_type),
    )
    per_position = weight_type is not None

    def sample(
        request: RuntimeRequest,
        resume: Resumption,
        _context: ClauseContext,
    ) -> object:
        """Answer a `Random.sample` request by enumeration.

        Parameters
        ----------
        request
            The request being answered and its arguments.
        resume
            The continuation, resumed once per support value.
        _context
            Runtime services, unused by this clause.

        Returns
        -------
        object
            The log of the marginal likelihood the scope accumulates, or
            the per-position marginals shaped by the weight type.

        Raises
        ------
        InvalidHandlerError
            If the request does not carry a site and a sampleable, the
            family has no finite support, a shot answers with something
            other than a value paired with its weight, or the shots'
            weights do not fill the weight type's positions.
        """
        _site, sampleable = _expect_arguments(request, 2, definition.name)
        support = _support(sampleable)
        distribution = cast(RuntimeDistribution, sampleable)
        prior_of = _hard_prior(distribution)
        shape = (*distribution.batch, *distribution.event)
        totals: list[list[float]] = []
        shapes: list[tuple[int, ...]] = []
        for choice in support:
            value = _reshape([choice] * math.prod(shape), shape) if shape else choice  # type: ignore[list-item]
            prior = prior_of.log_prob(_hard_atom(prior_of, value), keep_batch=True)
            answer = resume(value)
            if not isinstance(answer, tuple) or len(answer) != 2:
                raise InvalidHandlerError(
                    "enumeration expects each shot to answer with a value and "
                    "its accumulated weight"
                )
            weight = add_weights(prior, answer[1])
            totals.append(_entries(weight))
            shapes.append(_weight_shape(weight))
        width = max(len(entries) for entries in totals)
        marginals = [
            _aggregate([entries[position] for entries in totals], reduction)
            for position in range(width)
        ]
        if not per_position:
            return math.fsum(marginals)
        widest = max(shapes, key=len)
        if any(shape not in ((), widest) for shape in shapes):
            raise InvalidHandlerError(
                "enumeration answers per-position weights only when every "
                "shot's weights share one shape"
            )
        return _reshape(marginals, widest)

    return RuntimeHandler(
        definition,
        {RANDOM_SAMPLE: RuntimeClause(sample, result_validator)},
        duplicable_context=True,
    )

intervene_handler

intervene_handler(values: Mapping[object, object], *, result_validator: RuntimeValidator, on_intervene: Callable[[RuntimeRequest, object], None] | None = None, answer_type: TypeExpr = ANSWER, key: str = 'intervene') -> RuntimeHandler

Fix selected sites to given values without scoring them.

An intervened site's distribution is replaced by a point mass, so it contributes nothing to the joint; sites without a value forward.

PARAMETER DESCRIPTION
values

The value for each intervened site, keyed by site.

TYPE: Mapping[object, object]

result_validator

Checks an intervened value inhabits the request's result type.

TYPE: RuntimeValidator

on_intervene

Called with the request and the value fixed for it, so a caller can mark the site as deterministic.

TYPE: Callable[[RuntimeRequest, object], None] | None DEFAULT: None

answer_type

What the handler answers with.

TYPE: TypeExpr DEFAULT: ANSWER

key

Distinguishes this handler from others of the same name, entering its derived identity.

TYPE: str DEFAULT: 'intervene'

RETURNS DESCRIPTION
RuntimeHandler

The attachment, ready to bind into a runtime environment.

Source code in src/quivers/qiec/builtins.py
def intervene_handler(
    values: Mapping[object, object],
    *,
    result_validator: RuntimeValidator,
    on_intervene: Callable[[RuntimeRequest, object], None] | None = None,
    answer_type: TypeExpr = ANSWER,
    key: str = "intervene",
) -> RuntimeHandler:
    """Fix selected sites to given values without scoring them.

    An intervened site's distribution is replaced by a point mass, so it
    contributes nothing to the joint; sites without a value forward.

    Parameters
    ----------
    values
        The value for each intervened site, keyed by site.
    result_validator
        Checks an intervened value inhabits the request's result type.
    on_intervene
        Called with the request and the value fixed for it, so a caller
        can mark the site as deterministic.
    answer_type
        What the handler answers with.
    key
        Distinguishes this handler from others of the same name,
        entering its derived identity.

    Returns
    -------
    RuntimeHandler
        The attachment, ready to bind into a runtime environment.
    """
    definition = _handler_def(
        "Random.intervene",
        RANDOM,
        ((RANDOM_SAMPLE, ResumptionGrade.LINEAR),),
        key=key,
        input_type=answer_type,
        output_type=answer_type,
        total=False,
        telescope=_generic_binders(answer_type=answer_type),
    )

    def sample(
        request: RuntimeRequest,
        resume: Resumption,
        _context: ClauseContext,
    ) -> object:
        """Answer a `Random.sample` request with the intervened value.

        Parameters
        ----------
        request
            The request being answered and its arguments.
        resume
            The continuation, invoked within the clause's declared grade.
        _context
            Runtime services, unused by this clause.

        Returns
        -------
        object
            What the resumed computation produced, or a `Forward` when
            the site is not intervened.

        Raises
        ------
        InvalidHandlerError
            If the request does not carry a site and a sampleable.
        RuntimeTypeMismatch
            If the value does not inhabit the site's type.
        """
        site, _ = _site_and_sampleable(request, definition.name)
        if site not in values:
            return Forward()
        value = values[site]
        _require(
            value, result_validator, request.core.result_type, f"intervention {site!r}"
        )
        if on_intervene is not None:
            on_intervene(request, value)
        return TailResume(value)

    return RuntimeHandler(
        definition, {RANDOM_SAMPLE: RuntimeClause(sample, result_validator)}
    )

param_handler

param_handler(lookup: Callable[[str, RuntimeRequest], object], *, result_validator: RuntimeValidator, total: bool = True, answer_type: TypeExpr = ANSWER, key: str = 'param') -> RuntimeHandler

Serve Param.get from a host parameter store.

PARAMETER DESCRIPTION
lookup

Returns the value of the named parameter, given the name and the request.

TYPE: Callable[[str, RuntimeRequest], object]

result_validator

Checks a value inhabits the request's result type.

TYPE: RuntimeValidator

total

Whether the handler discharges the instance; a handler layered over a store leaves the instance in the row so the store stays required outside it.

TYPE: bool DEFAULT: True

answer_type

What the handler answers with.

TYPE: TypeExpr DEFAULT: ANSWER

key

Distinguishes this handler from others of the same name, entering its derived identity.

TYPE: str DEFAULT: 'param'

RETURNS DESCRIPTION
RuntimeHandler

The attachment, ready to bind into a runtime environment.

Source code in src/quivers/qiec/builtins.py
def param_handler(
    lookup: Callable[[str, RuntimeRequest], object],
    *,
    result_validator: RuntimeValidator,
    total: bool = True,
    answer_type: TypeExpr = ANSWER,
    key: str = "param",
) -> RuntimeHandler:
    """Serve ``Param.get`` from a host parameter store.

    Parameters
    ----------
    lookup
        Returns the value of the named parameter, given the name and the
        request.
    result_validator
        Checks a value inhabits the request's result type.
    total
        Whether the handler discharges the instance; a handler layered
        over a store leaves the instance in the row so the store stays
        required outside it.
    answer_type
        What the handler answers with.
    key
        Distinguishes this handler from others of the same name,
        entering its derived identity.

    Returns
    -------
    RuntimeHandler
        The attachment, ready to bind into a runtime environment.
    """
    definition = _handler_def(
        "Param.store",
        PARAM,
        ((PARAM_GET, ResumptionGrade.LINEAR),),
        key=key,
        input_type=answer_type,
        output_type=answer_type,
        total=total,
        telescope=_generic_binders(answer_type=answer_type),
    )

    def get(
        request: RuntimeRequest,
        resume: Resumption,
        _context: ClauseContext,
    ) -> object:
        """Answer a `Param.get` request with the stored value.

        Parameters
        ----------
        request
            The request being answered and its arguments.
        resume
            The continuation, invoked within the clause's declared grade.
        _context
            Runtime services, unused by this clause.

        Returns
        -------
        object
            What the resumed computation produced.

        Raises
        ------
        InvalidHandlerError
            If the request does not carry exactly one string argument.
        RuntimeTypeMismatch
            If the value does not inhabit the request's result type.
        """
        (name,) = _expect_arguments(request, 1, definition.name)
        if not isinstance(name, str):
            raise InvalidHandlerError("Param.get takes a parameter name")
        value = lookup(name, request)
        _require(
            value, result_validator, request.core.result_type, f"parameter {name!r}"
        )
        return TailResume(value)

    return RuntimeHandler(definition, {PARAM_GET: RuntimeClause(get, result_validator)})

replay_handler

replay_handler(values: Mapping[object, object], *, result_validator: RuntimeValidator, policy: ReplayPolicy = CLAMP_NO_SCORE, score_instance: EffectInstanceId | None = None, weight_validator: RuntimeValidator = lambda _value: True, extra: ExtraValuePolicy = ERROR, answer_type: TypeExpr = ANSWER, key: str = 'replay') -> RuntimeHandler

Replay selected sites under an explicit scoring/intervention policy.

A sample request at a site in values resumes with the recorded value; a request at any other site is forwarded to an outer handler. A site reached more than once, as a recurrence reaches the sites of its step at every position, replays the value keyed "<site>@<n>" at its n-th occurrence, counted from zero, when one is recorded, and the value keyed by the site alone otherwise.

PARAMETER DESCRIPTION
values

The recorded value for each replayed site, keyed by site, or by site and occurrence as "<site>@<n>".

TYPE: Mapping[object, object]

result_validator

Checks a replayed value inhabits the request's result type.

TYPE: RuntimeValidator

policy

Whether a replayed value is also scored: CLAMP_AND_SCORE sends its log density to score_instance, while CLAMP_NO_SCORE and INTERVENE replay it without scoring.

TYPE: ReplayPolicy DEFAULT: CLAMP_NO_SCORE

score_instance

The lexical Score instance replayed densities are sent to; required under CLAMP_AND_SCORE.

TYPE: EffectInstanceId | None DEFAULT: None

weight_validator

Checks each log-density contribution.

TYPE: RuntimeValidator DEFAULT: lambda _value: True

extra

What to do, when the handled computation returns, with a recorded site the computation never sampled.

TYPE: ExtraValuePolicy DEFAULT: ERROR

answer_type

What the handler answers with.

TYPE: TypeExpr DEFAULT: ANSWER

key

Distinguishes this handler from others of the same name, entering its derived identity.

TYPE: str DEFAULT: 'replay'

RETURNS DESCRIPTION
RuntimeHandler

The attachment, ready to bind into a runtime environment.

RAISES DESCRIPTION
ValueError

If policy is CLAMP_AND_SCORE and no score_instance is given.

Source code in src/quivers/qiec/builtins.py
def replay_handler(
    values: Mapping[object, object],
    *,
    result_validator: RuntimeValidator,
    policy: ReplayPolicy = ReplayPolicy.CLAMP_NO_SCORE,
    score_instance: EffectInstanceId | None = None,
    weight_validator: RuntimeValidator = lambda _value: True,
    extra: ExtraValuePolicy = ExtraValuePolicy.ERROR,
    answer_type: TypeExpr = ANSWER,
    key: str = "replay",
) -> RuntimeHandler:
    """Replay selected sites under an explicit scoring/intervention policy.

    A sample request at a site in ``values`` resumes with the recorded
    value; a request at any other site is forwarded to an outer handler.
    A site reached more than once, as a recurrence reaches the sites of
    its step at every position, replays the value keyed ``"<site>@<n>"``
    at its ``n``-th occurrence, counted from zero, when one is recorded,
    and the value keyed by the site alone otherwise.

    Parameters
    ----------
    values
        The recorded value for each replayed site, keyed by site, or by
        site and occurrence as ``"<site>@<n>"``.
    result_validator
        Checks a replayed value inhabits the request's result type.
    policy
        Whether a replayed value is also scored: ``CLAMP_AND_SCORE`` sends
        its log density to ``score_instance``, while ``CLAMP_NO_SCORE``
        and ``INTERVENE`` replay it without scoring.
    score_instance
        The lexical `Score` instance replayed densities are sent to;
        required under ``CLAMP_AND_SCORE``.
    weight_validator
        Checks each log-density contribution.
    extra
        What to do, when the handled computation returns, with a recorded
        site the computation never sampled.
    answer_type
        What the handler answers with.
    key
        Distinguishes this handler from others of the same name,
        entering its derived identity.

    Returns
    -------
    RuntimeHandler
        The attachment, ready to bind into a runtime environment.

    Raises
    ------
    ValueError
        If ``policy`` is ``CLAMP_AND_SCORE`` and no ``score_instance`` is
        given.
    """
    if policy is ReplayPolicy.CLAMP_AND_SCORE and score_instance is None:
        raise ValueError("scored replay requires a Score effect instance")
    introduced = (
        EffectRow((RowEntry(score_instance, SCORE),))
        if score_instance is not None and policy is ReplayPolicy.CLAMP_AND_SCORE
        else EffectRow()
    )
    definition = _handler_def(
        f"Random.replay.{policy.value}",
        RANDOM,
        ((RANDOM_SAMPLE, ResumptionGrade.LINEAR),),
        key=key,
        input_type=answer_type,
        output_type=answer_type,
        introduced=introduced,
        total=False,
        telescope=_generic_binders(answer_type=answer_type),
    )

    def make_context() -> RuntimeHandler:
        """Build a fresh per-installation handler.

        Returns
        -------
        RuntimeHandler
            A handler with its own state, so two installations of this
            declaration do not share it.
        """
        seen: set[object] = set()
        occurrences: dict[object, int] = {}

        def sample(
            request: RuntimeRequest,
            resume: Resumption,
            context: ClauseContext,
        ) -> object:
            """Answer a `Random.sample` request.

            Parameters
            ----------
            request
                The request being answered and its arguments.
            resume
                The continuation, invoked within the clause's declared grade.
            context
                Runtime services available to the clause.

            Returns
            -------
            object
                What the resumed computation produced, or a `Forward` when
                the site has no recorded value.

            Raises
            ------
            InvalidHandlerError
                If the request does not carry a site and a sampleable, or
                the value is scored and the sampleable has no ``log_prob``.
            RuntimeTypeMismatch
                If the recorded value does not inhabit the site's type.
            """
            site, sampleable = _site_and_sampleable(request, definition.name)
            occurrence = occurrences.get(site, 0)
            occurrences[site] = occurrence + 1
            key_at = f"{site}@{occurrence}"
            if key_at in values:
                site_key: object = key_at
            elif site in values:
                site_key = site
            else:
                return Forward()
            value = values[site_key]
            _require(
                value,
                result_validator,
                request.core.result_type,
                f"replay {site!r}",
            )
            seen.add(site_key)
            if policy is ReplayPolicy.CLAMP_AND_SCORE:
                assert score_instance is not None
                _emit_score(
                    context,
                    request,
                    score_instance,
                    _log_density(sampleable, value),
                    weight_validator,
                    role="replay-score",
                )
            return TailResume(value)

        def finish(value: object, _context: ClauseContext) -> object:
            """Answer the handled computation's return.

            Parameters
            ----------
            value
                The value the handled computation returned.
            _context
                Runtime services, unused by this clause.

            Returns
            -------
            object
                The value, unchanged.

            Raises
            ------
            InvalidHandlerError
                If recorded values remain unconsumed and the policy forbids it.
            """
            if extra is ExtraValuePolicy.ERROR:
                extras = set(values) - seen
                if extras:
                    rendered = ", ".join(
                        repr(item) for item in sorted(extras, key=repr)
                    )
                    raise InvalidHandlerError(f"unused replay values: {rendered}")
            return value

        return RuntimeHandler(
            definition,
            {RANDOM_SAMPLE: RuntimeClause(sample, result_validator)},
            return_clause=finish,
            mutable_context=True,
        )

    prototype = make_context()
    prototype.context_factory = make_context
    return prototype

reweight_handler

reweight_handler(transform: Callable[[object], object], *, score_instance: EffectInstanceId, weight_validator: RuntimeValidator = lambda _value: True, answer_type: TypeExpr = ANSWER, key: str = 'reweight') -> RuntimeHandler

Transform every Score.add contribution before passing it outward.

Scaling and masking are transformers: each contribution arriving at the handler is replaced by its image and sent to the same instance's outer handler, with the provenance of the site it came from kept.

PARAMETER DESCRIPTION
transform

The map applied to each contribution.

TYPE: Callable[[object], object]

score_instance

The lexical Score instance handled, which the transformed contribution is sent on to.

TYPE: EffectInstanceId

weight_validator

Checks each transformed contribution.

TYPE: RuntimeValidator DEFAULT: lambda _value: True

answer_type

What the handler answers with.

TYPE: TypeExpr DEFAULT: ANSWER

key

Distinguishes this handler from others of the same name, entering its derived identity.

TYPE: str DEFAULT: 'reweight'

RETURNS DESCRIPTION
RuntimeHandler

The attachment, ready to bind into a runtime environment.

Source code in src/quivers/qiec/builtins.py
def reweight_handler(
    transform: Callable[[object], object],
    *,
    score_instance: EffectInstanceId,
    weight_validator: RuntimeValidator = lambda _value: True,
    answer_type: TypeExpr = ANSWER,
    key: str = "reweight",
) -> RuntimeHandler:
    """Transform every ``Score.add`` contribution before passing it outward.

    Scaling and masking are transformers: each contribution arriving at
    the handler is replaced by its image and sent to the same instance's
    outer handler, with the provenance of the site it came from kept.

    Parameters
    ----------
    transform
        The map applied to each contribution.
    score_instance
        The lexical `Score` instance handled, which the transformed
        contribution is sent on to.
    weight_validator
        Checks each transformed contribution.
    answer_type
        What the handler answers with.
    key
        Distinguishes this handler from others of the same name,
        entering its derived identity.

    Returns
    -------
    RuntimeHandler
        The attachment, ready to bind into a runtime environment.
    """
    definition = _handler_def(
        "Score.reweight",
        SCORE,
        ((SCORE_ADD, ResumptionGrade.LINEAR),),
        key=key,
        input_type=answer_type,
        output_type=answer_type,
        introduced=EffectRow((RowEntry(score_instance, SCORE),)),
        telescope=_generic_binders(answer_type=answer_type),
    )

    def add(
        request: RuntimeRequest,
        resume: Resumption,
        context: ClauseContext,
    ) -> object:
        """Answer a `Score.add` request by re-emitting the transformed weight.

        Parameters
        ----------
        request
            The request being answered and its arguments.
        resume
            The continuation, invoked within the clause's declared grade.
        context
            Runtime services available to the clause.

        Returns
        -------
        object
            What the resumed computation produced.

        Raises
        ------
        InvalidHandlerError
            If the request does not carry exactly one argument.
        RuntimeTypeMismatch
            If the transformed contribution does not inhabit the weight
            type.
        """
        (weight,) = _expect_arguments(request, 1, definition.name)
        # The transformed contribution keeps the role the original was
        # emitted under, so a reader of the accumulator still knows
        # whether the site was drawn, conditioned, or replayed.
        _emit_score(
            context,
            request,
            score_instance,
            transform(weight),
            weight_validator,
            role=request.core.origin.origin.role,
        )
        return TailResume(None)

    return RuntimeHandler(definition, {SCORE_ADD: RuntimeClause(add, _unit)})

score_handler

score_handler(*, identity: object = 0.0, combine: Callable[[object, object], object] = _RUNTIME_ADD, weight_validator: RuntimeValidator = lambda _value: True, answer_validator: RuntimeValidator = lambda _value: True, total_validator: RuntimeValidator | None = None, expose_total: bool = False, answer_type: TypeExpr = ANSWER, key: str = 'score', observer: Callable[[RuntimeRequest, object], None] | None = None) -> tuple[RuntimeHandler, ScoreAccumulator]

Accumulate Score.add contributions in their evaluation order.

PARAMETER DESCRIPTION
identity

The accumulator's value before any contribution.

TYPE: object DEFAULT: 0.0

combine

Folds a contribution into the running total.

TYPE: Callable[[object, object], object] DEFAULT: _RUNTIME_ADD

weight_validator

Checks an individual weight contribution.

TYPE: RuntimeValidator DEFAULT: lambda _value: True

answer_validator

Checks the handled computation's answer.

TYPE: RuntimeValidator DEFAULT: lambda _value: True

total_validator

Checks the accumulated total; weight_validator when omitted.

TYPE: RuntimeValidator | None DEFAULT: None

expose_total

Whether the accumulated total is returned alongside the answer, rather than kept internal.

TYPE: bool DEFAULT: False

answer_type

What the handler answers with.

TYPE: TypeExpr DEFAULT: ANSWER

key

Distinguishes this handler from others of the same name, entering its derived identity.

TYPE: str DEFAULT: 'score'

observer

Called with each request and its contribution as it is accumulated, so a caller can attribute contributions to the sites their provenance names.

TYPE: Callable[[RuntimeRequest, object], None] | None DEFAULT: None

RETURNS DESCRIPTION
tuple[RuntimeHandler, ScoreAccumulator]

The attachment, ready to bind into a runtime environment, and the accumulator each installation publishes into when its computation completes.

Source code in src/quivers/qiec/builtins.py
def score_handler(
    *,
    identity: object = 0.0,
    combine: Callable[[object, object], object] = _RUNTIME_ADD,
    weight_validator: RuntimeValidator = lambda _value: True,
    answer_validator: RuntimeValidator = lambda _value: True,
    total_validator: RuntimeValidator | None = None,
    expose_total: bool = False,
    answer_type: TypeExpr = ANSWER,
    key: str = "score",
    observer: Callable[[RuntimeRequest, object], None] | None = None,
) -> tuple[RuntimeHandler, ScoreAccumulator]:
    """Accumulate ``Score.add`` contributions in their evaluation order.

    Parameters
    ----------
    identity
        The accumulator's value before any contribution.
    combine
        Folds a contribution into the running total.
    weight_validator
        Checks an individual weight contribution.
    answer_validator
        Checks the handled computation's answer.
    total_validator
        Checks the accumulated total; ``weight_validator`` when omitted.
    expose_total
        Whether the accumulated total is returned alongside the
        answer, rather than kept internal.
    answer_type
        What the handler answers with.
    key
        Distinguishes this handler from others of the same name,
        entering its derived identity.
    observer
        Called with each request and its contribution as it is
        accumulated, so a caller can attribute contributions to the
        sites their provenance names.

    Returns
    -------
    tuple[RuntimeHandler, ScoreAccumulator]
        The attachment, ready to bind into a runtime environment, and
        the accumulator each installation publishes into when its
        computation completes.
    """
    accumulator = ScoreAccumulator(identity)
    checked_total = total_validator or weight_validator
    output_type = (
        TypeApplication(WEIGHTED_RESULT_CONSTRUCTOR, (answer_type, LOG_WEIGHT))
        if expose_total
        else answer_type
    )
    definition = _handler_def(
        "Score.accumulate",
        SCORE,
        ((SCORE_ADD, ResumptionGrade.LINEAR),),
        key=key,
        input_type=answer_type,
        output_type=output_type,
        telescope=_generic_binders(answer_type=answer_type),
    )

    def make_context() -> RuntimeHandler:
        """Build a fresh per-installation handler.

        Returns
        -------
        RuntimeHandler
            A handler with its own state, so two installations of this
            declaration do not share it.
        """
        local = ScoreAccumulator(identity)

        def add(
            request: RuntimeRequest,
            resume: Resumption,
            _context: ClauseContext,
        ) -> object:
            """Answer a `Score.add` request by accumulating the weight.

            Parameters
            ----------
            request
                The request being answered and its arguments.
            resume
                The continuation, invoked within the clause's declared grade.
            _context
                Runtime services, unused by this clause.

            Returns
            -------
            object
                What the resumed computation produced.

            Raises
            ------
            InvalidHandlerError
                If the request does not carry exactly one argument.
            RuntimeTypeMismatch
                If the contribution does not inhabit the weight type.
            """
            (weight,) = _expect_arguments(request, 1, definition.name)
            _require(weight, weight_validator, LOG_WEIGHT, "score contribution")
            local.contributions.append(weight)
            local.total = combine(local.total, weight)
            if observer is not None:
                observer(request, weight)
            return TailResume(None)

        def finish(value: object, _context: ClauseContext) -> object:
            """Answer the handled computation's return.

            Parameters
            ----------
            value
                The value the handled computation returned.
            _context
                Runtime services, unused by this clause.

            Returns
            -------
            object
                The value paired with the accumulated total when
                ``expose_total`` is set, else the value alone.
            """
            return (value, local.total) if expose_total else value

        def publish() -> None:
            """Copy this installation's total and contributions to the accumulator."""
            accumulator.total = local.total
            accumulator.contributions[:] = local.contributions

        return RuntimeHandler(
            definition,
            {SCORE_ADD: RuntimeClause(add, _unit)},
            return_clause=finish,
            input_validator=answer_validator,
            output_validator=(
                _pair_of(answer_validator, checked_total)
                if expose_total
                else answer_validator
            ),
            mutable_context=True,
            on_exit=publish,
        )

    prototype = make_context()
    prototype.context_factory = make_context
    return prototype, accumulator

search_handler

search_handler(definition: HandlerDef, operation: OperationId, *, reduction: str, alternative_validator: RuntimeValidator) -> RuntimeHandler

Enumerate a tensor of alternatives, summing the shots' weights.

The handled computation answers with a unit paired with the weight a collecting handler inside it accumulated, which the return clause reduces to the weight. The clause resumes once per entry of the tensor it is asked to choose from and answers the semiring sum of what the resumptions answer, so an empty choice contributes the semiring's zero; a resumption's answer is itself a semiring sum when the continuation chooses again, and the sum is associative, so the nesting does not matter.

PARAMETER DESCRIPTION
definition

The module's declaration of the handler, whose one clause handles the choosing operation with an unrestricted resumption.

TYPE: HandlerDef

operation

The choosing operation.

TYPE: OperationId

reduction

The semiring addition, one of SEARCH_REDUCTIONS.

TYPE: str

alternative_validator

Checks the value each resumption carries.

TYPE: RuntimeValidator

RETURNS DESCRIPTION
RuntimeHandler

The attachment.

RAISES DESCRIPTION
ValueError

If the reduction is unknown.

Source code in src/quivers/qiec/builtins.py
def search_handler(
    definition: HandlerDef,
    operation: OperationId,
    *,
    reduction: str,
    alternative_validator: RuntimeValidator,
) -> RuntimeHandler:
    """Enumerate a tensor of alternatives, summing the shots' weights.

    The handled computation answers with a unit paired with the weight
    a collecting handler inside it accumulated, which the return clause
    reduces to the weight. The clause resumes once per entry of the
    tensor it is asked to choose from and answers the semiring sum of
    what the resumptions answer, so an empty choice contributes the
    semiring's zero; a resumption's answer is itself a semiring sum when
    the continuation chooses again, and the sum is associative, so the
    nesting does not matter.

    Parameters
    ----------
    definition
        The module's declaration of the handler, whose one clause
        handles the choosing operation with an unrestricted resumption.
    operation
        The choosing operation.
    reduction
        The semiring addition, one of ``SEARCH_REDUCTIONS``.
    alternative_validator
        Checks the value each resumption carries.

    Returns
    -------
    RuntimeHandler
        The attachment.

    Raises
    ------
    ValueError
        If the reduction is unknown.
    """
    if reduction not in SEARCH_REDUCTIONS:
        raise ValueError(
            f"a search combines its shots by one of {', '.join(SEARCH_REDUCTIONS)}, "
            f"not {reduction!r}"
        )

    def choose(
        request: RuntimeRequest,
        resume: Resumption,
        _context: ClauseContext,
    ) -> object:
        """Answer a choice by resuming once per alternative.

        Parameters
        ----------
        request
            The request being answered and its arguments.
        resume
            The continuation, resumed once per alternative.
        _context
            Runtime services, unused by this clause.

        Returns
        -------
        object
            The semiring sum of the shots' weights.

        Raises
        ------
        InvalidHandlerError
            If the request does not carry one tensor of alternatives.
        """
        (alternatives,) = _expect_arguments(request, 1, definition.name)
        if not isinstance(alternatives, tuple):
            raise InvalidHandlerError("a search chooses among the entries of a tensor")
        return _search_combine(
            reduction, [resume(alternative) for alternative in alternatives]
        )

    def finish(value: object, _context: ClauseContext) -> object:
        """Reduce the handled computation's answer to its weight.

        Parameters
        ----------
        value
            The value paired with its accumulated weight.
        _context
            Runtime services, unused by this clause.

        Returns
        -------
        object
            The weight.

        Raises
        ------
        InvalidHandlerError
            If the answer is not a value paired with its weight.
        """
        if not isinstance(value, tuple) or len(value) != 2:
            raise InvalidHandlerError(
                "a search expects the handled computation to answer with a value "
                "and its accumulated weight"
            )
        return value[1]

    return RuntimeHandler(
        definition,
        {operation: RuntimeClause(choose, alternative_validator)},
        return_clause=finish,
        duplicable_context=True,
    )

state_effect

state_effect(state_type: TypeExpr) -> EffectRef

Construct the concrete State[state_type] interface.

PARAMETER DESCRIPTION
state_type

The type to instantiate the interface's binder at.

TYPE: TypeExpr

RETURNS DESCRIPTION
EffectRef

The saturated application.

RAISES DESCRIPTION
TypeError

If the argument is not a type.

ValueError

If it is ill-kinded.

Source code in src/quivers/qiec/builtins.py
def state_effect(state_type: TypeExpr) -> EffectRef:
    """Construct the concrete ``State[state_type]`` interface.

    Parameters
    ----------
    state_type : TypeExpr
        The type to instantiate the interface's binder at.

    Returns
    -------
    EffectRef
        The saturated application.

    Raises
    ------
    TypeError
        If the argument is not a type.
    ValueError
        If it is ill-kinded.
    """
    return STATE_EFFECT.apply((state_type,))

state_handler

state_handler(initial: object, state_validator: RuntimeValidator, *, state_type: TypeExpr = STATE, expose_final: bool = False, answer_type: TypeExpr = ANSWER, answer_validator: RuntimeValidator = lambda _value: True, key: str = 'state') -> tuple[RuntimeHandler, StateCell]

Interpret lexical State.get/put with a nonduplicable mutable cell.

PARAMETER DESCRIPTION
initial

The state before any operation.

TYPE: object

state_validator

Checks the initial state, each value put, and each value got.

TYPE: RuntimeValidator

state_type

The type of the handler's state.

TYPE: TypeExpr DEFAULT: STATE

expose_final

Whether the final state is returned alongside the answer.

TYPE: bool DEFAULT: False

answer_type

What the handler answers with.

TYPE: TypeExpr DEFAULT: ANSWER

answer_validator

Checks the handled computation's answer.

TYPE: RuntimeValidator DEFAULT: lambda _value: True

key

Distinguishes this handler from others of the same name, entering its derived identity.

TYPE: str DEFAULT: 'state'

RETURNS DESCRIPTION
tuple[RuntimeHandler, StateCell]

The attachment, ready to bind into a runtime environment, and the cell each installation publishes its final state into when its computation completes.

RAISES DESCRIPTION
RuntimeTypeMismatch

If initial does not inhabit state_type.

Source code in src/quivers/qiec/builtins.py
def state_handler(
    initial: object,
    state_validator: RuntimeValidator,
    *,
    state_type: TypeExpr = STATE,
    expose_final: bool = False,
    answer_type: TypeExpr = ANSWER,
    answer_validator: RuntimeValidator = lambda _value: True,
    key: str = "state",
) -> tuple[RuntimeHandler, StateCell]:
    """Interpret lexical State.get/put with a nonduplicable mutable cell.

    Parameters
    ----------
    initial
        The state before any operation.
    state_validator
        Checks the initial state, each value put, and each value got.
    state_type
        The type of the handler's state.
    expose_final
        Whether the final state is returned alongside the answer.
    answer_type
        What the handler answers with.
    answer_validator
        Checks the handled computation's answer.
    key
        Distinguishes this handler from others of the same name,
        entering its derived identity.

    Returns
    -------
    tuple[RuntimeHandler, StateCell]
        The attachment, ready to bind into a runtime environment, and
        the cell each installation publishes its final state into when
        its computation completes.

    Raises
    ------
    RuntimeTypeMismatch
        If ``initial`` does not inhabit ``state_type``.
    """
    _require(initial, state_validator, state_type, "initial state")
    cell = StateCell(initial)
    effect = state_effect(state_type)
    definition = _handler_def(
        "State.run",
        effect,
        (
            (STATE_GET, ResumptionGrade.LINEAR),
            (STATE_PUT, ResumptionGrade.LINEAR),
        ),
        key=key,
        input_type=answer_type,
        output_type=(
            TypeApplication(STATE_RESULT_CONSTRUCTOR, (answer_type, state_type))
            if expose_final
            else answer_type
        ),
        telescope=_generic_binders(
            answer_type=answer_type,
            state_type=state_type,
        ),
    )

    def make_context() -> RuntimeHandler:
        """Build a fresh per-installation handler.

        Returns
        -------
        RuntimeHandler
            A handler with its own state, so two installations of this
            declaration do not share it.
        """
        local = StateCell(initial)

        def get(
            request: RuntimeRequest,
            resume: Resumption,
            _context: ClauseContext,
        ) -> object:
            """Answer a `State.get` request with the current state.

            Parameters
            ----------
            request
                The request being answered and its arguments.
            resume
                The continuation, invoked within the clause's declared grade.
            _context
                Runtime services, unused by this clause.

            Returns
            -------
            object
                What the resumed computation produced.

            Raises
            ------
            InvalidHandlerError
                If the request carries any argument.
            """
            _expect_arguments(request, 0, definition.name)
            return TailResume(local.value)

        def put(
            request: RuntimeRequest,
            resume: Resumption,
            _context: ClauseContext,
        ) -> object:
            """Answer a `State.put` request by replacing the state.

            Parameters
            ----------
            request
                The request being answered and its arguments.
            resume
                The continuation, invoked within the clause's declared grade.
            _context
                Runtime services, unused by this clause.

            Returns
            -------
            object
                What the resumed computation produced.

            Raises
            ------
            InvalidHandlerError
                If the request does not carry exactly one argument.
            RuntimeTypeMismatch
                If the new state does not inhabit ``state_type``.
            """
            (new_value,) = _expect_arguments(request, 1, definition.name)
            _require(new_value, state_validator, state_type, "state update")
            local.value = new_value
            return TailResume(None)

        def finish(value: object, _context: ClauseContext) -> object:
            """Answer the handled computation's return.

            Parameters
            ----------
            value
                The value the handled computation returned.
            _context
                Runtime services, unused by this clause.

            Returns
            -------
            object
                The value paired with the final state when
                ``expose_final`` is set, else the value alone.
            """
            return (value, local.value) if expose_final else value

        def publish() -> None:
            """Copy this installation's final state to the shared cell."""
            cell.value = local.value

        return RuntimeHandler(
            definition,
            {
                STATE_GET: RuntimeClause(get, state_validator),
                STATE_PUT: RuntimeClause(put, _unit),
            },
            return_clause=finish,
            input_validator=answer_validator,
            output_validator=(
                _pair_of(answer_validator, state_validator)
                if expose_final
                else answer_validator
            ),
            mutable_context=True,
            on_exit=publish,
        )

    prototype = make_context()
    prototype.context_factory = make_context
    return prototype, cell

trace_handler

trace_handler(effect: EffectRef, operations: Iterable[OperationId], recorder: TraceRecorder, *, validators: Mapping[OperationId, RuntimeValidator], answer_type: TypeExpr = ANSWER, key: str = 'trace') -> RuntimeHandler

Observe forwarded operation replies without interpreting the effect.

PARAMETER DESCRIPTION
effect

The interface whose operations are recorded.

TYPE: EffectRef

operations

Which operations to record.

TYPE: Iterable[OperationId]

recorder

Receives each recorded request.

TYPE: TraceRecorder

validators

Per-operation result validators.

TYPE: Mapping[OperationId, RuntimeValidator]

answer_type

What the handler answers with.

TYPE: TypeExpr DEFAULT: ANSWER

key

Distinguishes this handler from others of the same name, entering its derived identity.

TYPE: str DEFAULT: 'trace'

RETURNS DESCRIPTION
RuntimeHandler

The attachment, ready to bind into a runtime environment.

Source code in src/quivers/qiec/builtins.py
def trace_handler(
    effect: EffectRef,
    operations: Iterable[OperationId],
    recorder: TraceRecorder,
    *,
    validators: Mapping[OperationId, RuntimeValidator],
    answer_type: TypeExpr = ANSWER,
    key: str = "trace",
) -> RuntimeHandler:
    """Observe forwarded operation replies without interpreting the effect.

    Parameters
    ----------
    effect
        The interface whose operations are recorded.
    operations
        Which operations to record.
    recorder
        Receives each recorded request.
    validators
        Per-operation result validators.
    answer_type
        What the handler answers with.
    key
        Distinguishes this handler from others of the same name,
        entering its derived identity.

    Returns
    -------
    RuntimeHandler
        The attachment, ready to bind into a runtime environment.
    """
    operation_tuple = tuple(operations)
    definition = _handler_def(
        f"{effect.name}.trace",
        effect,
        tuple((operation, ResumptionGrade.AFFINE) for operation in operation_tuple),
        key=key,
        input_type=answer_type,
        output_type=answer_type,
        total=False,
        telescope=_generic_binders(answer_type=answer_type),
    )

    def make_clause(operation: OperationId):
        """Build the clause implementing one operation.

        Parameters
        ----------
        operation
            The operation this clause is being built for.

        Returns
        -------
        RuntimeClause
            The clause and its result validator.

        Raises
        ------
        ValueError
            If the operation has no validator among those supplied.
        """

        def clause(
            request: RuntimeRequest,
            _resume: Resumption,
            _context: ClauseContext,
        ) -> object:
            """Answer the request this clause covers.

            Parameters
            ----------
            request
                The request being answered and its arguments.
            _resume
                The continuation, which this clause does not invoke.
            _context
                Runtime services, unused by this clause.

            Returns
            -------
            object
                A `Forward` whose response hook records the outer
                handler's reply.
            """
            return Forward(
                lambda result: recorder.record(request, result, mode="forwarded")
            )

        try:
            validator = validators[operation]
        except KeyError as error:
            raise ValueError(
                f"trace handler needs a validator for {operation}"
            ) from error

        def checked_clause(
            request: RuntimeRequest,
            resume: Resumption,
            context: ClauseContext,
        ) -> object:
            """Answer the request, validating the resumed value.

            Parameters
            ----------
            request
                The request being answered and its arguments.
            resume
                The continuation, invoked within the clause's declared grade.
            context
                Runtime services available to the clause.

            Returns
            -------
            object
                A `Forward` whose response hook validates the outer
                handler's reply before recording it.
            """
            answer = clause(request, resume, context)
            assert isinstance(answer, Forward)
            hook = answer.on_response

            def validate_and_record(result: object) -> object:
                """Check a produced value and record it.

                Parameters
                ----------
                result
                    The value produced for the request.

                Returns
                -------
                object
                    The value, unchanged, so the caller can use this in
                    place of what it passed.

                Raises
                ------
                RuntimeTypeMismatch
                    If the value does not inhabit the operation's result
                    type.
                """
                _require(
                    result,
                    validator,
                    request.core.result_type,
                    f"traced result of {request.operation}",
                )
                assert hook is not None
                return hook(result)

            return Forward(validate_and_record)

        return RuntimeClause(checked_clause, validator)

    return RuntimeHandler(
        definition,
        {operation: make_clause(operation) for operation in operation_tuple},
    )

weight_effect

weight_effect(weight_type: TypeExpr) -> EffectRef

Construct the concrete Weight[weight_type] interface.

PARAMETER DESCRIPTION
weight_type

The type to instantiate the interface's binder at.

TYPE: TypeExpr

RETURNS DESCRIPTION
EffectRef

The saturated application.

RAISES DESCRIPTION
TypeError

If the argument is not a type.

ValueError

If it is ill-kinded.

Source code in src/quivers/qiec/builtins.py
def weight_effect(weight_type: TypeExpr) -> EffectRef:
    """Construct the concrete ``Weight[weight_type]`` interface.

    Parameters
    ----------
    weight_type : TypeExpr
        The type to instantiate the interface's binder at.

    Returns
    -------
    EffectRef
        The saturated application.

    Raises
    ------
    TypeError
        If the argument is not a type.
    ValueError
        If it is ill-kinded.
    """
    return WEIGHT_EFFECT.apply((weight_type,))

weight_handler

weight_handler(identity: object, combine: Callable[[object, object], object], weight_validator: RuntimeValidator, *, weight_type: TypeExpr = K, expose_total: bool = True, answer_type: TypeExpr = ANSWER, answer_validator: RuntimeValidator = lambda _value: True, total_validator: RuntimeValidator | None = None, forkable: bool = False, key: str = 'weight') -> tuple[RuntimeHandler, WeightAccumulator]

Accumulate values in a caller-supplied semiring multiplication.

PARAMETER DESCRIPTION
identity

The accumulator's value before any contribution.

TYPE: object

combine

Folds a contribution into the running total.

TYPE: Callable[[object, object], object]

weight_validator

Checks an individual weight contribution.

TYPE: RuntimeValidator

weight_type

The type of an individual weight.

TYPE: TypeExpr DEFAULT: K

expose_total

Whether the accumulated total is returned alongside the answer, rather than kept internal.

TYPE: bool DEFAULT: True

answer_type

What the handler answers with.

TYPE: TypeExpr DEFAULT: ANSWER

answer_validator

Checks the handled computation's answer.

TYPE: RuntimeValidator DEFAULT: lambda _value: True

total_validator

Checks the accumulated total; weight_validator when omitted.

TYPE: RuntimeValidator | None DEFAULT: None

forkable

Whether an unrestricted resumption may capture an installation: each shot then continues from its own copy of the total so far, which is what a search inside the scope needs.

TYPE: bool DEFAULT: False

key

Distinguishes this handler from others of the same name, entering its derived identity.

TYPE: str DEFAULT: 'weight'

RETURNS DESCRIPTION
tuple[RuntimeHandler, WeightAccumulator]

The attachment, ready to bind into a runtime environment, and the accumulator each installation publishes into when its computation completes.

Source code in src/quivers/qiec/builtins.py
def weight_handler(
    identity: object,
    combine: Callable[[object, object], object],
    weight_validator: RuntimeValidator,
    *,
    weight_type: TypeExpr = K,
    expose_total: bool = True,
    answer_type: TypeExpr = ANSWER,
    answer_validator: RuntimeValidator = lambda _value: True,
    total_validator: RuntimeValidator | None = None,
    forkable: bool = False,
    key: str = "weight",
) -> tuple[RuntimeHandler, WeightAccumulator]:
    """Accumulate values in a caller-supplied semiring multiplication.

    Parameters
    ----------
    identity
        The accumulator's value before any contribution.
    combine
        Folds a contribution into the running total.
    weight_validator
        Checks an individual weight contribution.
    weight_type
        The type of an individual weight.
    expose_total
        Whether the accumulated total is returned alongside the
        answer, rather than kept internal.
    answer_type
        What the handler answers with.
    answer_validator
        Checks the handled computation's answer.
    total_validator
        Checks the accumulated total; ``weight_validator`` when omitted.
    forkable
        Whether an unrestricted resumption may capture an installation:
        each shot then continues from its own copy of the total so far,
        which is what a search inside the scope needs.
    key
        Distinguishes this handler from others of the same name,
        entering its derived identity.

    Returns
    -------
    tuple[RuntimeHandler, WeightAccumulator]
        The attachment, ready to bind into a runtime environment, and
        the accumulator each installation publishes into when its
        computation completes.
    """
    accumulator = WeightAccumulator(identity)
    checked_total = total_validator or weight_validator
    effect = weight_effect(weight_type)
    definition = _handler_def(
        "Weight.accumulate",
        effect,
        ((WEIGHT_ADD, ResumptionGrade.LINEAR),),
        key=key,
        input_type=answer_type,
        output_type=(
            TypeApplication(WEIGHTED_RESULT_CONSTRUCTOR, (answer_type, weight_type))
            if expose_total
            else answer_type
        ),
        telescope=_generic_binders(
            answer_type=answer_type,
            weight_type=weight_type,
        ),
    )

    def make_context(seed: WeightAccumulator | None = None) -> RuntimeHandler:
        """Build a fresh per-installation handler.

        Parameters
        ----------
        seed : WeightAccumulator | None
            The state a fork continues from; ``None`` starts at the
            identity.

        Returns
        -------
        RuntimeHandler
            A handler with its own state, so two installations of this
            declaration do not share it.
        """
        local = (
            WeightAccumulator(identity)
            if seed is None
            else WeightAccumulator(seed.total, list(seed.contributions))
        )

        def add(
            request: RuntimeRequest,
            resume: Resumption,
            _context: ClauseContext,
        ) -> object:
            """Answer a `Score.add` request by accumulating the weight.

            Parameters
            ----------
            request
                The request being answered and its arguments.
            resume
                The continuation, invoked within the clause's declared grade.
            _context
                Runtime services, unused by this clause.

            Returns
            -------
            object
                A tail resumption with the unit answer, so the scope
                continues on the machine's stack and a search inside it
                may resume any number of times.

            Raises
            ------
            InvalidHandlerError
                If the request does not carry exactly one argument.
            RuntimeTypeMismatch
                If the contribution does not inhabit the weight type.
            """
            del resume
            (weight,) = _expect_arguments(request, 1, definition.name)
            _require(weight, weight_validator, weight_type, "semiring weight")
            local.contributions.append(weight)
            local.total = combine(local.total, weight)
            return TailResume(None)

        def finish(value: object, _context: ClauseContext) -> object:
            """Answer the handled computation's return.

            Parameters
            ----------
            value
                The value the handled computation returned.
            _context
                Runtime services, unused by this clause.

            Returns
            -------
            object
                The value paired with the accumulated total when
                ``expose_total`` is set, else the value alone.
            """
            return (value, local.total) if expose_total else value

        def publish() -> None:
            """Copy this installation's total and contributions to the accumulator."""
            accumulator.total = local.total
            accumulator.contributions[:] = local.contributions

        def fork(_handler: RuntimeHandler) -> RuntimeHandler:
            """Copy this installation for another shot.

            Parameters
            ----------
            _handler : RuntimeHandler
                The installation forked, which is this one.

            Returns
            -------
            RuntimeHandler
                An installation continuing from the same total.
            """
            return make_context(local)

        return RuntimeHandler(
            definition,
            {WEIGHT_ADD: RuntimeClause(add, _unit)},
            return_clause=finish,
            input_validator=answer_validator,
            output_validator=(
                _pair_of(answer_validator, checked_total)
                if expose_total
                else answer_validator
            ),
            mutable_context=True,
            duplicable_context=forkable,
            fork_context=fork if forkable else None,
            on_exit=publish,
        )

    prototype = make_context()
    prototype.context_factory = make_context
    return prototype, accumulator

builtin_constructor

builtin_constructor(name: str, *binders: TypeBinder | IndexBinder) -> TypeConstructorRef

A type constructor in the builtin namespace.

PARAMETER DESCRIPTION
name

The constructor's name.

TYPE: str

*binders

Its kinding telescope.

TYPE: TypeBinder | IndexBinder DEFAULT: ()

RETURNS DESCRIPTION
TypeConstructorRef

The constructor. Its identity derives from the namespace and the name alone, so it is the same constructor in every module.

Source code in src/quivers/qiec/canonical.py
def builtin_constructor(
    name: str, *binders: TypeBinder | IndexBinder
) -> TypeConstructorRef:
    """A type constructor in the ``builtin`` namespace.

    Parameters
    ----------
    name : str
        The constructor's name.
    *binders : TypeBinder | IndexBinder
        Its kinding telescope.

    Returns
    -------
    TypeConstructorRef
        The constructor. Its identity derives from the namespace and the
        name alone, so it is the same constructor in every module.
    """
    return TypeConstructorRef(
        TypeId.derive("builtin", name),
        name,
        tuple(binders),
    )

sampleable_type

sampleable_type(element: TypeExpr) -> TypeApplication

The type of a distribution over element.

PARAMETER DESCRIPTION
element

The sampled value's type.

TYPE: TypeExpr

RETURNS DESCRIPTION
TypeApplication

Sampleable[element].

Source code in src/quivers/qiec/canonical.py
def sampleable_type(element: TypeExpr) -> TypeApplication:
    """The type of a distribution over ``element``.

    Parameters
    ----------
    element : TypeExpr
        The sampled value's type.

    Returns
    -------
    TypeApplication
        ``Sampleable[element]``.
    """
    return TypeApplication(SAMPLEABLE_CONSTRUCTOR, (element,))

sampled_element

sampled_element(type_: TypeExpr) -> TypeExpr | None

The value type a Sampleable produces.

PARAMETER DESCRIPTION
type_

A type that may be a sampleable application.

TYPE: TypeExpr

RETURNS DESCRIPTION
TypeExpr | None

The element type, or None when type_ is not Sampleable[A].

Source code in src/quivers/qiec/canonical.py
def sampled_element(type_: TypeExpr) -> TypeExpr | None:
    """The value type a ``Sampleable`` produces.

    Parameters
    ----------
    type_ : TypeExpr
        A type that may be a sampleable application.

    Returns
    -------
    TypeExpr | None
        The element type, or ``None`` when ``type_`` is not
        ``Sampleable[A]``.
    """
    if (
        isinstance(type_, TypeApplication)
        and type_.constructor == SAMPLEABLE_CONSTRUCTOR
        and len(type_.arguments) == 1
    ):
        argument = type_.arguments[0]
        if isinstance(argument, TypeApplication | TypeVariable):
            return argument
    return None

site_type

site_type(element: TypeExpr) -> TypeApplication

The type of a sample site producing element.

PARAMETER DESCRIPTION
element

The sampled value's type.

TYPE: TypeExpr

RETURNS DESCRIPTION
TypeApplication

Site[element].

Source code in src/quivers/qiec/canonical.py
def site_type(element: TypeExpr) -> TypeApplication:
    """The type of a sample site producing ``element``.

    Parameters
    ----------
    element : TypeExpr
        The sampled value's type.

    Returns
    -------
    TypeApplication
        ``Site[element]``.
    """
    return TypeApplication(SITE_CONSTRUCTOR, (element,))

tensor_shape

tensor_shape(type_: TypeExpr) -> tuple[TypeExpr, tuple[IndexTerm, ...]] | None

Split a tensor type into its element type and dimensions.

PARAMETER DESCRIPTION
type_

A type that may be a tensor application.

TYPE: TypeExpr

RETURNS DESCRIPTION
tuple[TypeExpr, tuple[IndexTerm, ...]] | None

The element type and dimensions, or None when type_ is not Tensor[A](shape) with a literal shape.

Source code in src/quivers/qiec/canonical.py
def tensor_shape(type_: TypeExpr) -> tuple[TypeExpr, tuple[IndexTerm, ...]] | None:
    """Split a tensor type into its element type and dimensions.

    Parameters
    ----------
    type_ : TypeExpr
        A type that may be a tensor application.

    Returns
    -------
    tuple[TypeExpr, tuple[IndexTerm, ...]] | None
        The element type and dimensions, or ``None`` when ``type_`` is
        not ``Tensor[A](shape)`` with a literal shape.
    """
    if (
        isinstance(type_, TypeApplication)
        and type_.constructor == TENSOR_CONSTRUCTOR
        and len(type_.arguments) == 2
    ):
        element, shape = type_.arguments
        if isinstance(element, TypeApplication | TypeVariable) and isinstance(
            shape, ShapeIndex
        ):
            return element, shape.dimensions
    return None

tensor_type

tensor_type(element: TypeExpr, dimensions: tuple[IndexTerm, ...]) -> TypeApplication

The type of an array of element with the given static shape.

PARAMETER DESCRIPTION
element

The element type.

TYPE: TypeExpr

dimensions

One index term per dimension, outermost first.

TYPE: tuple[IndexTerm, ...]

RETURNS DESCRIPTION
TypeApplication

Tensor[element](shape).

Source code in src/quivers/qiec/canonical.py
def tensor_type(
    element: TypeExpr, dimensions: tuple[IndexTerm, ...]
) -> TypeApplication:
    """The type of an array of ``element`` with the given static shape.

    Parameters
    ----------
    element : TypeExpr
        The element type.
    dimensions : tuple[IndexTerm, ...]
        One index term per dimension, outermost first.

    Returns
    -------
    TypeApplication
        ``Tensor[element](shape)``.
    """
    return TypeApplication(TENSOR_CONSTRUCTOR, (element, ShapeIndex(dimensions)))

check_evidence

check_evidence(evidence: EqualityEvidence, context: CheckContext) -> EqualityType

Check an appeal to equality evidence and return what it proves.

PARAMETER DESCRIPTION
evidence

The appeal being made, either reflexivity or an appeal to a branch given.

TYPE: EqualityEvidence

context

Scope the appeal is made in, which decides whether a branch given is still available.

TYPE: CheckContext

RETURNS DESCRIPTION
EqualityType

The equality the evidence establishes.

RAISES DESCRIPTION
KernelError

If the stated equality is ill-kinded, if reflexivity is claimed between unequal endpoints, if a branch given is appealed to outside its branch or with an equality other than the one it carries, or if the evidence is of an unknown class.

Source code in src/quivers/qiec/checking.py
def check_evidence(evidence: EqualityEvidence, context: CheckContext) -> EqualityType:
    """Check an appeal to equality evidence and return what it proves.

    Parameters
    ----------
    evidence : EqualityEvidence
        The appeal being made, either reflexivity or an appeal to a
        branch given.
    context : CheckContext
        Scope the appeal is made in, which decides whether a branch given
        is still available.

    Returns
    -------
    EqualityType
        The equality the evidence establishes.

    Raises
    ------
    KernelError
        If the stated equality is ill-kinded, if reflexivity is claimed
        between unequal endpoints, if a branch given is appealed to
        outside its branch or with an equality other than the one it
        carries, or if the evidence is of an unknown class.
    """
    check_type(evidence.equality)
    if isinstance(evidence, Reflexivity):
        if evidence.equality.left != evidence.equality.right:
            raise KernelError("reflexivity evidence has unequal endpoints")
        return evidence.equality
    if isinstance(evidence, BranchGiven):
        available = context.given(evidence.id)
        if available is None or available.equality != evidence.equality:
            raise KernelError("branch equality evidence escaped or was forged")
        return evidence.equality
    raise KernelError(f"unknown equality evidence {evidence!r}")

check_request

check_request(request: EffectRequest, registry: KernelRegistry, context: CheckContext) -> tuple[EffectRef, TypeExpr]

Check one effect request and report what performing it yields.

PARAMETER DESCRIPTION
request

The request to check, naming an instance, an interface application, an operation, and the arguments it carries.

TYPE: EffectRequest

registry

Declarations the request is resolved against.

TYPE: KernelRegistry

context

Scope the request is made in.

TYPE: CheckContext

RETURNS DESCRIPTION
tuple[EffectRef, TypeExpr]

The interface application the request performs, and the type resuming it supplies. The interface comes back because the caller needs it to build the row this request adds to.

RAISES DESCRIPTION
KernelError

If the operation is unknown, if the named interface does not own it, if the static or value arguments do not match the operation's signature, if the declared result type disagrees with the instantiated one, or if any part mentions a rigid variable out of scope.

Source code in src/quivers/qiec/checking.py
def check_request(
    request: EffectRequest,
    registry: KernelRegistry,
    context: CheckContext,
) -> tuple[EffectRef, TypeExpr]:
    """Check one effect request and report what performing it yields.

    Parameters
    ----------
    request : EffectRequest
        The request to check, naming an instance, an interface
        application, an operation, and the arguments it carries.
    registry : KernelRegistry
        Declarations the request is resolved against.
    context : CheckContext
        Scope the request is made in.

    Returns
    -------
    tuple[EffectRef, TypeExpr]
        The interface application the request performs, and the type
        resuming it supplies. The interface comes back because the caller
        needs it to build the row this request adds to.

    Raises
    ------
    KernelError
        If the operation is unknown, if the named interface does not own
        it, if the static or value arguments do not match the operation's
        signature, if the declared result type disagrees with the
        instantiated one, or if any part mentions a rigid variable out of
        scope.
    """
    registry.validate_static(request.effect)
    _check_static_variable_scope(request.effect, context, subject="request effect")
    for argument in request.static_arguments:
        registry.validate_static(argument)
        _check_static_variable_scope(
            argument,
            context,
            subject="request static argument",
        )
    registry.validate_type(request.result_type)
    _check_static_variable_scope(
        request.result_type,
        context,
        subject="request result type",
    )
    effect, operation_object = registry.operation(request.operation)
    if not effect.matches(request.effect):
        raise KernelError("request effect interface does not own the operation")
    interface_substitution = instantiate_telescope(
        effect.telescope,
        request.effect.arguments,
    )
    argument_types, result_type = instantiate_operation(
        operation_object,
        request.static_arguments,
        interface_substitution,
    )
    if len(request.arguments) != len(argument_types):
        raise KernelError(
            f"operation {operation_object.name!r} expects {len(argument_types)} arguments"
        )
    for argument, expected in zip(request.arguments, argument_types, strict=True):
        actual = infer_value(argument, registry, context)
        if actual != expected:
            raise KernelError(
                f"operation argument has type {render_static(actual)}, expected {render_static(expected)}"
            )
    if request.result_type != result_type:
        raise KernelError(
            f"request result is {render_static(request.result_type)}, expected {render_static(result_type)}"
        )
    return request.effect, result_type

check_static

check_static(term: StaticArgument, scope: StaticScope | None = None, *, registry: KernelRegistry | None = None) -> None

Check the intrinsic kind/sort structure of one static term.

PARAMETER DESCRIPTION
term

The static term to check.

TYPE: StaticArgument

scope

Binders in scope. None checks the term's intrinsic structure alone and permits no variable reference, which is the right mode for a closed declaration.

TYPE: StaticScope or None DEFAULT: None

registry

Registry to resolve interface applications against. None skips that check, leaving only the structural one.

TYPE: KernelRegistry or None DEFAULT: None

RAISES DESCRIPTION
KernelError

If the term is ill-kinded, references an unbound or wrongly kinded static variable, carries a scoped variable where a declaration is expected, or applies a known interface wrongly.

Source code in src/quivers/qiec/checking.py
def check_static(
    term: StaticArgument,
    scope: StaticScope | None = None,
    *,
    registry: KernelRegistry | None = None,
) -> None:
    """Check the intrinsic kind/sort structure of one static term.

    Parameters
    ----------
    term : StaticArgument
        The static term to check.
    scope : StaticScope or None
        Binders in scope. None checks the term's intrinsic structure
        alone and permits no variable reference, which is the right mode
        for a closed declaration.
    registry : KernelRegistry or None
        Registry to resolve interface applications against. None skips
        that check, leaving only the structural one.

    Raises
    ------
    KernelError
        If the term is ill-kinded, references an unbound or wrongly
        kinded static variable, carries a scoped variable where a
        declaration is expected, or applies a known interface wrongly.
    """
    if registry is not None:
        registry._validate_effect_references(term)
    if isinstance(term, TypeVariable):
        if scope is not None:
            if term.identity is not None:
                raise KernelError(
                    "a scoped type variable cannot occur in a declaration"
                )
            bound_kind = scope.type_kind(term.name)
            if bound_kind is None:
                raise KernelError(f"unbound type variable {term.name!r}")
            if bound_kind != term.kind:
                raise KernelError(f"type variable {term.name!r} has the wrong kind")
        return
    if isinstance(term, TypeApplication):
        if len(term.constructor.telescope) != len(term.arguments):
            raise KernelError(
                f"type constructor {term.constructor.name!r} expects "
                f"{len(term.constructor.telescope)} static arguments"
            )
        for binder, argument in zip(
            term.constructor.telescope,
            term.arguments,
            strict=True,
        ):
            _check_binder_argument(
                binder,
                argument,
                subject=f"argument of type constructor {term.constructor.name!r}",
            )
            check_static(argument, scope)
        return
    if isinstance(term, FunctionType):
        check_type(term.parameter, scope)
        check_type(term.result, scope)
        return
    if isinstance(term, EqualityType):
        check_static(term.left, scope)
        check_static(term.right, scope)
        if isinstance(term.kind, (NatSort, ShapeSort, ContextSort, UserIndexSort)):
            index_nodes = (IndexVariable, IndexLiteral, IndexConstructor, ShapeIndex)
            if not isinstance(term.left, index_nodes) or not isinstance(
                term.right, index_nodes
            ):
                raise KernelError("index equality endpoints must be index terms")
            if not _sort_matches(term.kind, index_sort(term.left)):  # type: ignore[arg-type]
                raise KernelError("left equality endpoint has the wrong index sort")
            if not _sort_matches(term.kind, index_sort(term.right)):  # type: ignore[arg-type]
                raise KernelError("right equality endpoint has the wrong index sort")
        else:
            if (
                static_kind(term.left) != term.kind
                or static_kind(term.right) != term.kind
            ):
                raise KernelError("equality endpoints have the wrong kind")
        return
    if isinstance(term, EffectRef):
        for argument in term.arguments:
            check_static(argument, scope)
        return
    if isinstance(term, EffectVariable):
        if scope is not None:
            if term.identity is not None:
                raise KernelError(
                    "a scoped effect variable cannot occur in a declaration"
                )
            if term.name not in scope.effects:
                raise KernelError(f"unbound effect variable {term.name!r}")
        return
    if isinstance(term, IndexConstructor):
        for argument in term.arguments:
            check_static(argument, scope)
        return
    if isinstance(term, ShapeIndex):
        index_sort(term)
        for dimension in term.dimensions:
            check_static(dimension, scope)
        return
    if isinstance(term, IndexVariable):
        if scope is not None:
            if term.identity is not None:
                raise KernelError(
                    "a scoped index variable cannot occur in a declaration"
                )
            bound_sort = scope.index_sort(term.name)
            if bound_sort is None:
                raise KernelError(f"unbound index variable {term.name!r}")
            if not _sort_matches(bound_sort, term.sort):
                raise KernelError(f"index variable {term.name!r} has the wrong sort")
        return
    if isinstance(term, IndexLiteral):
        return
    raise KernelError(f"unknown static term {term!r}")

check_type

check_type(type_: TypeExpr, scope: StaticScope | None = None, *, registry: KernelRegistry | None = None) -> None

Check that a type expression is intrinsically well-kinded.

PARAMETER DESCRIPTION
type_

The type expression to check.

TYPE: TypeExpr

scope

Binders in scope, or None for a closed type.

TYPE: StaticScope or None DEFAULT: None

registry

Registry to resolve interface applications against, or None.

TYPE: KernelRegistry or None DEFAULT: None

RAISES DESCRIPTION
KernelError

If the expression is ill-kinded, or is a well-formed static term that is not a type, such as an index where a type is required.

Source code in src/quivers/qiec/checking.py
def check_type(
    type_: TypeExpr,
    scope: StaticScope | None = None,
    *,
    registry: KernelRegistry | None = None,
) -> None:
    """Check that a type expression is intrinsically well-kinded.

    Parameters
    ----------
    type_ : TypeExpr
        The type expression to check.
    scope : StaticScope or None
        Binders in scope, or None for a closed type.
    registry : KernelRegistry or None
        Registry to resolve interface applications against, or None.

    Raises
    ------
    KernelError
        If the expression is ill-kinded, or is a well-formed static term
        that is not a type, such as an index where a type is required.
    """
    check_static(type_, scope, registry=registry)
    if static_kind(type_) != TYPE:
        raise KernelError(f"expected a type, got {render_static(type_)}")

infer_computation

infer_computation(computation: Computation, registry: KernelRegistry, context: CheckContext = CheckContext()) -> ComputationType

Infer the result type and effect row of a computation.

This is the kernel's entry point for checking effectful code. The row it returns is what remains unhandled: a Handle discharges its instance, so a fully handled computation comes back with the empty row.

PARAMETER DESCRIPTION
computation

The term to check.

TYPE: Computation

registry

Declarations the term is resolved against.

TYPE: KernelRegistry

context

Bindings and evidence in scope. The default empty context suits a closed computation.

TYPE: CheckContext DEFAULT: CheckContext()

RETURNS DESCRIPTION
ComputationType

The result type paired with the effects still outstanding.

RAISES DESCRIPTION
KernelError

If two case branches share a static scope, if a request, value, or branch fails to check, if a handler's interface does not match the instance it discharges, if branch types or rows fail to unify, or if a rigid variable escapes the branch that introduced it.

Source code in src/quivers/qiec/checking.py
def infer_computation(
    computation: Computation,
    registry: KernelRegistry,
    context: CheckContext = CheckContext(),
) -> ComputationType:
    """Infer the result type and effect row of a computation.

    This is the kernel's entry point for checking effectful code. The
    row it returns is what remains unhandled: a `Handle` discharges its
    instance, so a fully handled computation comes back with the empty
    row.

    Parameters
    ----------
    computation : Computation
        The term to check.
    registry : KernelRegistry
        Declarations the term is resolved against.
    context : CheckContext
        Bindings and evidence in scope. The default empty context suits a
        closed computation.

    Returns
    -------
    ComputationType
        The result type paired with the effects still outstanding.

    Raises
    ------
    KernelError
        If two case branches share a static scope, if a request, value,
        or branch fails to check, if a handler's interface does not match
        the instance it discharges, if branch types or rows fail to
        unify, or if a rigid variable escapes the branch that introduced
        it.
    """
    static_scopes = _computation_static_scopes(computation)
    if len(set(static_scopes)) != len(static_scopes):
        raise KernelError("case branch static scopes must be globally fresh")
    return _infer_computation(computation, registry, context)

infer_value

infer_value(value: Value, registry: KernelRegistry, context: CheckContext = CheckContext()) -> TypeExpr

Infer the type of a pure value term.

PARAMETER DESCRIPTION
value

The value to check.

TYPE: Value

registry

Declarations the value's types are resolved against.

TYPE: KernelRegistry

context

Bindings and evidence in scope. The default empty context suits a closed value.

TYPE: CheckContext DEFAULT: CheckContext()

RETURNS DESCRIPTION
TypeExpr

The inferred type.

RAISES DESCRIPTION
KernelError

If a local is unbound or carries a type other than the one the term claims, if a literal's value does not inhabit its type, if a constructor is applied wrongly, if equality evidence is forged or out of scope, or if an inferred type mentions a rigid variable that escapes its branch.

Source code in src/quivers/qiec/checking.py
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
def infer_value(
    value: Value,
    registry: KernelRegistry,
    context: CheckContext = CheckContext(),
) -> TypeExpr:
    """Infer the type of a pure value term.

    Parameters
    ----------
    value : Value
        The value to check.
    registry : KernelRegistry
        Declarations the value's types are resolved against.
    context : CheckContext
        Bindings and evidence in scope. The default empty context suits a
        closed value.

    Returns
    -------
    TypeExpr
        The inferred type.

    Raises
    ------
    KernelError
        If a local is unbound or carries a type other than the one the
        term claims, if a literal's value does not inhabit its type, if
        a constructor is applied wrongly, if equality evidence is forged
        or out of scope, or if an inferred type mentions a rigid variable
        that escapes its branch.
    """
    if isinstance(value, Var):
        actual = context.local_type(value.local.name)
        if actual is None or actual != value.local.type:
            raise KernelError(f"unbound or mistyped local {value.local.name!r}")
        registry.validate_type(actual)
        _check_static_variable_scope(actual, context, subject="local type")
        return actual
    if isinstance(value, LiteralValue):
        registry.validate_type(value.type)
        _check_static_variable_scope(value.type, context, subject="literal type")
        _check_literal(value.value, value.type)
        return value.type
    if isinstance(value, AttachmentRef):
        registry.validate_type(value.type)
        _check_static_variable_scope(value.type, context, subject="attachment type")
        return value.type
    if isinstance(value, EvidenceValue):
        equality = check_evidence(value.evidence, context)
        registry.validate_type(equality)
        _check_static_variable_scope(equality, context, subject="equality evidence")
        return equality
    if isinstance(value, TransportValue):
        equality = check_evidence(value.evidence, context)
        registry.validate_type(equality)
        _check_static_variable_scope(equality, context, subject="transport equality")
        source = infer_value(value.value, registry, context)
        registry.validate_type(value.target_type)
        _check_static_variable_scope(
            value.target_type,
            context,
            subject="transport target",
        )
        if equality.left != source or equality.right != value.target_type:
            raise KernelError("transport endpoints do not match value and target types")
        return value.target_type
    if isinstance(value, PrimitiveApplication):
        signature = PRIMITIVES.get(value.name)
        if signature is None:
            raise KernelError(f"unknown primitive {value.name!r}", "qiec-primitive")
        if signature.id != value.primitive:
            raise KernelError(
                f"primitive {value.name!r} is applied under the identity of "
                "another primitive",
                "qiec-primitive",
            )
        if len(value.arguments) != len(signature.parameters):
            raise KernelError(
                f"primitive {value.name!r} takes {len(signature.parameters)} "
                f"argument(s), got {len(value.arguments)}",
                "qiec-primitive",
            )
        broadcast: tuple[IndexTerm, ...] | None = None
        for position, (argument, expected) in enumerate(
            zip(value.arguments, signature.parameters, strict=True)
        ):
            actual = infer_value(argument, registry, context)
            if actual == expected:
                continue
            shape = tensor_shape(actual)
            if shape is None or shape[0] != expected:
                raise KernelError(
                    f"argument {position} of primitive {value.name!r} has type "
                    f"{actual!r}; the primitive takes {expected!r} or a Tensor "
                    "of it",
                    "qiec-primitive",
                )
            if broadcast is not None and tuple(shape[1]) != broadcast:
                raise KernelError(
                    f"primitive {value.name!r} is applied to tensors of shapes "
                    f"{broadcast!r} and {tuple(shape[1])!r}, which do not agree",
                    "qiec-primitive",
                )
            broadcast = tuple(shape[1])
        expected_result: TypeExpr = (
            signature.result
            if broadcast is None
            else tensor_type(signature.result, broadcast)
        )
        if value.result_type != expected_result:
            raise KernelError(
                f"primitive {value.name!r} produces {render_static(expected_result)}, not "
                f"the claimed {render_static(value.result_type)}",
                "qiec-primitive",
            )
        return expected_result
    if isinstance(value, DistributionValue):
        return _infer_distribution(value, registry, context)
    if isinstance(value, SiteValue):
        if not value.label:
            raise KernelError("a site label cannot be empty", "qiec-distribution")
        if not (
            isinstance(value.result_type, TypeApplication)
            and value.result_type.constructor == SITE_CONSTRUCTOR
            and len(value.result_type.arguments) == 1
        ):
            raise KernelError(
                f"site {value.label!r} claims {render_static(value.result_type)}, not a Site",
                "qiec-distribution",
            )
        registry.validate_type(value.result_type)
        _check_static_variable_scope(value.result_type, context, subject="site type")
        return value.result_type
    if isinstance(value, LogDensity):
        sampleable = infer_value(value.sampleable, registry, context)
        element = sampled_element(sampleable)
        if element is None:
            raise KernelError(
                f"log_prob takes a Sampleable, not {sampleable!r}",
                "qiec-distribution",
            )
        actual = infer_value(value.value, registry, context)
        if actual != element:
            raise KernelError(
                f"log_prob evaluates a distribution over {render_static(element)} at a value "
                f"of type {render_static(actual)}",
                "qiec-distribution",
            )
        if not value.batch:
            return LOG_WEIGHT
        if not (
            isinstance(value.sampleable, DistributionValue)
            and value.sampleable.plate.batch == value.batch
        ):
            raise KernelError(
                "log_prob keeps its weights apart over batch axes only for a "
                "construction plated over those axes",
                "qiec-distribution",
            )
        return tensor_type(LOG_WEIGHT, tuple(axis.size for axis in value.batch))
    if isinstance(value, Gather):
        return _infer_gather(value, registry, context)
    if isinstance(value, WeightSum):
        actual = infer_value(value.value, registry, context)
        shape = tensor_shape(actual)
        if actual != LOG_WEIGHT and (shape is None or shape[0] != LOG_WEIGHT):
            raise KernelError(
                f"a weight sum totals log weights, not {actual!r}",
                "qiec-distribution",
            )
        return LOG_WEIGHT
    if isinstance(value, SegmentSum):
        return _infer_segment_sum(value, registry, context)
    if isinstance(value, KernelMatrix):
        actual = infer_value(value.inputs, registry, context)
        shape = tensor_shape(actual)
        if shape is None or shape[0] != REAL or len(shape[1]) != 1:
            raise KernelError(
                f"a kernel matrix takes a Tensor[Real] of rank 1, not {actual!r}",
                "qiec-distribution",
            )
        if value.kernel != "rbf":
            raise KernelError(
                f"unknown kernel {value.kernel!r}; the kernels are rbf",
                "qiec-distribution",
            )
        if not value.length_scale > 0 or value.jitter < 0:
            raise KernelError(
                "a kernel matrix needs a positive length scale and a "
                "nonnegative jitter",
                "qiec-distribution",
            )
        expected = tensor_type(REAL, (shape[1][0], shape[1][0]))
        if value.result_type != expected:
            raise KernelError(
                f"kernel matrix has type {render_static(expected)}, not the claimed "
                f"{render_static(value.result_type)}",
                "qiec-distribution",
            )
        return expected
    if isinstance(value, AffineMap):
        return _infer_affine_map(value, registry, context)
    if isinstance(value, TableMap):
        return _infer_table_map(value, registry, context)
    if isinstance(value, Reduction):
        shape = tensor_shape(infer_value(value.value, registry, context))
        if shape is None:
            raise KernelError(
                f"a {value.operator} reduction takes a Tensor", "qiec-primitive"
            )
        element = shape[0]
        integral = value.operator in ("sum", "max", "min", "prod")
        if element != REAL and not (integral and element == INT):
            raise KernelError(
                f"a {value.operator} reduction takes a Tensor[Real]"
                + (" or a Tensor[Int]" if integral else "")
                + f", not one of {element!r}",
                "qiec-primitive",
            )
        if value.result_type != element:
            raise KernelError(
                f"reduction has type {render_static(element)}, not the claimed "
                f"{render_static(value.result_type)}",
                "qiec-primitive",
            )
        return element
    if isinstance(value, Rowwise):
        actual = infer_value(value.value, registry, context)
        shape = tensor_shape(actual)
        if shape is None or shape[0] != REAL:
            raise KernelError(
                f"a {value.operator} takes a Tensor[Real], not {actual!r}",
                "qiec-primitive",
            )
        if value.result_type != actual:
            raise KernelError(
                f"{value.operator} keeps its argument's type {render_static(actual)}, not the "
                f"claimed {render_static(value.result_type)}",
                "qiec-primitive",
            )
        return actual
    if isinstance(value, Comprehension):
        if value.binder.type != INT:
            raise KernelError(
                "a comprehension's binder is an Int index", "qiec-primitive"
            )
        registry.validate_type(value.result_type)
        body = infer_value(value.body, registry, context.extend(value.binder))
        inner = tensor_shape(body)
        expected = (
            tensor_type(body, (value.extent,))
            if inner is None
            else tensor_type(inner[0], (value.extent, *inner[1]))
        )
        if value.result_type != expected:
            raise KernelError(
                f"comprehension has type {render_static(expected)}, not the claimed "
                f"{render_static(value.result_type)}",
                "qiec-primitive",
            )
        _check_static_variable_scope(expected, context, subject="comprehension type")
        return expected
    if isinstance(value, TupleValue):
        components = tuple(infer_value(item, registry, context) for item in value.items)
        expected = product_type(*components)
        if value.result_type != expected:
            raise KernelError(
                f"tuple has type {render_static(expected)}, not the claimed {render_static(value.result_type)}",
                "qiec-primitive",
            )
        registry.validate_type(expected)
        _check_static_variable_scope(expected, context, subject="tuple type")
        return expected
    if isinstance(value, TensorValue):
        return _infer_tensor(value, registry, context)
    if isinstance(value, Projection):
        source = infer_value(value.value, registry, context)
        if not (
            isinstance(source, TypeApplication)
            and source.constructor.name.startswith("Product")
            and source.constructor.id == product_type(*source.arguments).constructor.id  # type: ignore[arg-type]
        ):
            raise KernelError(
                f"projection from a value of non-product type {render_static(source)}",
                "qiec-primitive",
            )
        if not 0 <= value.position < len(source.arguments):
            raise KernelError(
                f"projection position {value.position} is outside a product of "
                f"{len(source.arguments)} components",
                "qiec-primitive",
            )
        component = source.arguments[value.position]
        if value.result_type != component:
            raise KernelError(
                f"projection {value.position} has type {render_static(component)}, not the "
                f"claimed {render_static(value.result_type)}",
                "qiec-primitive",
            )
        return component  # type: ignore[return-value]
    if isinstance(value, ConstructorValue):
        constructor = registry.constructor(value.constructor)
        family = registry.families[constructor.family]
        for argument in value.static_arguments:
            registry.validate_static(argument)
            _check_static_variable_scope(
                argument,
                context,
                subject="constructor static argument",
            )
        registry.validate_type(value.result_type)
        _check_static_variable_scope(
            value.result_type,
            context,
            subject="constructor result type",
        )
        parameter_count = len(family.parameters)
        local_count = len(constructor.telescope)
        if len(value.static_arguments) != parameter_count + local_count:
            raise KernelError(
                f"constructor {constructor.name!r} expects "
                f"{parameter_count + local_count} static arguments"
            )
        parameters = instantiate_telescope(
            family.parameters,
            value.static_arguments[:parameter_count],
        )
        locals_ = instantiate_telescope(
            constructor.telescope,
            value.static_arguments[parameter_count:],
        )
        substitution = _combine_substitutions(parameters, locals_)
        if len(value.fields) != len(constructor.fields):
            raise KernelError(
                f"constructor {constructor.name!r} expects {len(constructor.fields)} fields"
            )
        for field_value, field in zip(value.fields, constructor.fields, strict=True):
            actual = infer_value(field_value, registry, context)
            expected = substitute_type(field.type, substitution)
            if actual != expected:
                raise KernelError(
                    f"field {field.name!r} has type {render_static(actual)}, expected {render_static(expected)}"
                )
        parameter_arguments = value.static_arguments[:parameter_count]
        result_indices = tuple(
            substitute_static(index, substitution)
            for index in constructor.result_indices
        )
        expected_result = TypeApplication(
            family.type_constructor,
            (*parameter_arguments, *result_indices),
        )
        if value.result_type != expected_result:
            raise KernelError(
                f"constructor result is {render_static(value.result_type)}, expected {render_static(expected_result)}"
            )
        return expected_result
    raise KernelError(f"unknown value term {value!r}")

check_coverage

check_coverage(family: FamilyDecl, patterns: tuple[BranchPattern, ...], *, certified_impossible: frozenset[ConstructorId] = frozenset()) -> CoverageResult

Check constructor coverage without pretending opaque cases are total.

PARAMETER DESCRIPTION
family

The family being matched on.

TYPE: FamilyDecl

patterns

The branch patterns written, in source order.

TYPE: tuple[BranchPattern, ...]

certified_impossible

Constructors an equality solver has proven cannot occur, which may therefore be omitted without making the match incomplete.

TYPE: frozenset[ConstructorId] DEFAULT: frozenset()

RETURNS DESCRIPTION
CoverageResult

The status, the constructors still missing, and any matched twice. The status is UNKNOWN rather than COMPLETE whenever the family is open, a pattern is a wildcard or guarded, or a pattern names a constructor the family does not declare, since none of those can be decided here.

Source code in src/quivers/qiec/coverage.py
def check_coverage(
    family: FamilyDecl,
    patterns: tuple[BranchPattern, ...],
    *,
    certified_impossible: frozenset[ConstructorId] = frozenset(),
) -> CoverageResult:
    """Check constructor coverage without pretending opaque cases are total.

    Parameters
    ----------
    family : FamilyDecl
        The family being matched on.
    patterns : tuple[BranchPattern, ...]
        The branch patterns written, in source order.
    certified_impossible : frozenset[ConstructorId]
        Constructors an equality solver has proven cannot occur, which
        may therefore be omitted without making the match incomplete.

    Returns
    -------
    CoverageResult
        The status, the constructors still missing, and any matched
        twice. The status is `UNKNOWN` rather than `COMPLETE` whenever
        the family is open, a pattern is a wildcard or guarded, or a
        pattern names a constructor the family does not declare, since
        none of those can be decided here.
    """
    known = tuple(
        pattern.constructor for pattern in patterns if pattern.constructor is not None
    )
    seen: set[ConstructorId] = set()
    duplicates: list[ConstructorId] = []
    for constructor in known:
        if constructor in seen and constructor not in duplicates:
            duplicates.append(constructor)
        seen.add(constructor)

    missing = tuple(
        constructor
        for constructor in family.constructors
        if constructor not in seen and constructor not in certified_impossible
    )
    unresolved = (
        not family.closed
        or any(pattern.constructor is None or pattern.guarded for pattern in patterns)
        or any(constructor not in family.constructors for constructor in known)
    )
    if unresolved:
        status = CoverageStatus.UNKNOWN
    elif missing or duplicates:
        status = CoverageStatus.INCOMPLETE
    else:
        status = CoverageStatus.COMPLETE
    return CoverageResult(status, missing, tuple(duplicates))

check_indexed_coverage

check_indexed_coverage(family: FamilyDecl, constructors: tuple[ConstructorDecl, ...], scrutinee: TypeApplication, patterns: tuple[BranchPattern, ...]) -> CoverageResult

Coverage with omissions justified by closed-index contradiction.

Only constructors that the small equality solver certifies as impossible may be omitted. An unknown result remains a required case.

PARAMETER DESCRIPTION
family

The family being matched on.

TYPE: FamilyDecl

constructors

Declarations of the family's constructors, needed to refine each one against the scrutinee.

TYPE: tuple[ConstructorDecl, ...]

scrutinee

The applied family type being matched.

TYPE: TypeApplication

patterns

The branch patterns written.

TYPE: tuple[BranchPattern, ...]

RETURNS DESCRIPTION
CoverageResult

Coverage judged against the constructors that remain possible.

RAISES DESCRIPTION
TypeError

If a constructor belongs to another family, or the scrutinee does not inhabit the matched family.

Source code in src/quivers/qiec/coverage.py
def check_indexed_coverage(
    family: FamilyDecl,
    constructors: tuple[ConstructorDecl, ...],
    scrutinee: TypeApplication,
    patterns: tuple[BranchPattern, ...],
) -> CoverageResult:
    """Coverage with omissions justified by closed-index contradiction.

    Only constructors that the small equality solver certifies as impossible
    may be omitted.  An unknown result remains a required case.

    Parameters
    ----------
    family : FamilyDecl
        The family being matched on.
    constructors : tuple[ConstructorDecl, ...]
        Declarations of the family's constructors, needed to refine each
        one against the scrutinee.
    scrutinee : TypeApplication
        The applied family type being matched.
    patterns : tuple[BranchPattern, ...]
        The branch patterns written.

    Returns
    -------
    CoverageResult
        Coverage judged against the constructors that remain possible.

    Raises
    ------
    TypeError
        If a constructor belongs to another family, or the scrutinee does
        not inhabit the matched family.
    """
    impossible: set[ConstructorId] = set()
    for constructor in constructors:
        scope = StaticScopeId.derive(
            "coverage",
            family.id,
            constructor.id,
            scrutinee,
        )
        refinement = refine_branch(
            family,
            constructor,
            scrutinee,
            constructor_skolems(constructor, scope),
        )
        if refinement.reachability is Reachability.IMPOSSIBLE:
            impossible.add(constructor.id)
    return check_coverage(
        family,
        patterns,
        certified_impossible=frozenset(impossible),
    )

constructor_skolems

constructor_skolems(constructor: ConstructorDecl, scope: StaticScopeId) -> tuple[StaticArgument, ...]

Create rigid static variables for a constructor's local telescope.

PARAMETER DESCRIPTION
constructor

The constructor whose telescope to instantiate.

TYPE: ConstructorDecl

scope

Identity of the branch scope. It enters each variable's identity, so skolems from two branches are never confused and cannot be mistaken for one another when a type escapes.

TYPE: StaticScopeId

RETURNS DESCRIPTION
tuple[StaticArgument, ...]

One rigid variable per binder, in telescope order.

Source code in src/quivers/qiec/coverage.py
def constructor_skolems(
    constructor: ConstructorDecl,
    scope: StaticScopeId,
) -> tuple[StaticArgument, ...]:
    """Create rigid static variables for a constructor's local telescope.

    Parameters
    ----------
    constructor : ConstructorDecl
        The constructor whose telescope to instantiate.
    scope : StaticScopeId
        Identity of the branch scope. It enters each variable's identity,
        so skolems from two branches are never confused and cannot be
        mistaken for one another when a type escapes.

    Returns
    -------
    tuple[StaticArgument, ...]
        One rigid variable per binder, in telescope order.
    """
    arguments: list[StaticArgument] = []
    for position, binder in enumerate(constructor.telescope):
        identity = StaticVariableId.derive(
            scope,
            constructor.id,
            position,
            binder.tag,
        )
        if isinstance(binder, TypeBinder):
            arguments.append(TypeVariable(binder.name, binder.kind, identity))
        elif isinstance(binder, IndexBinder):
            arguments.append(IndexVariable(binder.name, binder.sort, identity))
        elif isinstance(binder, EffectBinder):
            arguments.append(EffectVariable(binder.name, identity))
    return tuple(arguments)

join_branch_rows

join_branch_rows(branches: tuple[tuple[Reachability, EffectRow], ...]) -> EffectRow

Union effects of every possibly reachable branch.

UNKNOWN is intentionally treated as reachable. Dropping it would make an incomplete equality solver unsound for effect inference.

PARAMETER DESCRIPTION
branches

Each branch's reachability and the row its body performs.

TYPE: tuple[tuple[Reachability, EffectRow], ...]

RETURNS DESCRIPTION
EffectRow

The union over every branch not certified impossible. Only a proven contradiction removes a branch's effects from the result.

RAISES DESCRIPTION
ValueError

If two reachable branches bind one instance to different interfaces, or their open tails cannot merge.

Source code in src/quivers/qiec/coverage.py
def join_branch_rows(
    branches: tuple[tuple[Reachability, EffectRow], ...],
) -> EffectRow:
    """Union effects of every possibly reachable branch.

    ``UNKNOWN`` is intentionally treated as reachable.  Dropping it would make
    an incomplete equality solver unsound for effect inference.

    Parameters
    ----------
    branches : tuple[tuple[Reachability, EffectRow], ...]
        Each branch's reachability and the row its body performs.

    Returns
    -------
    EffectRow
        The union over every branch not certified impossible. Only a
        proven contradiction removes a branch's effects from the result.

    Raises
    ------
    ValueError
        If two reachable branches bind one instance to different
        interfaces, or their open tails cannot merge.
    """
    result = EMPTY_ROW
    for reachability, row in branches:
        if reachability is not Reachability.IMPOSSIBLE:
            result = result.union(row)
    return result

refine_branch

refine_branch(family: FamilyDecl, constructor: ConstructorDecl, scrutinee: TypeApplication, constructor_arguments: tuple[StaticArgument, ...]) -> BranchRefinement

Introduce constructor skolems and index equalities for a branch.

The equality solver is intentionally conservative. Syntactically equal indices are reachable; contradictory closed literals are impossible; all other equations remain UNKNOWN and must still contribute branch effects and coverage.

PARAMETER DESCRIPTION
family

The family being matched on.

TYPE: FamilyDecl

constructor

The constructor this branch matches.

TYPE: ConstructorDecl

scrutinee

The applied family type being scrutinised, supplying the parameters and the indices to be refined.

TYPE: TypeApplication

constructor_arguments

Arguments instantiating the constructor's local telescope, normally the rigid skolems from constructor_skolems.

TYPE: tuple[StaticArgument, ...]

RETURNS DESCRIPTION
BranchRefinement

The skolems in scope, the equalities the match makes available, and whether the branch is reachable.

RAISES DESCRIPTION
TypeError

If the constructor belongs to another family, if the scrutinee does not inhabit the matched family, or if the scrutinee supplies the wrong number of arguments.

Source code in src/quivers/qiec/coverage.py
def refine_branch(
    family: FamilyDecl,
    constructor: ConstructorDecl,
    scrutinee: TypeApplication,
    constructor_arguments: tuple[StaticArgument, ...],
) -> BranchRefinement:
    """Introduce constructor skolems and index equalities for a branch.

    The equality solver is intentionally conservative.  Syntactically equal
    indices are reachable; contradictory closed literals are impossible; all
    other equations remain ``UNKNOWN`` and must still contribute branch
    effects and coverage.

    Parameters
    ----------
    family : FamilyDecl
        The family being matched on.
    constructor : ConstructorDecl
        The constructor this branch matches.
    scrutinee : TypeApplication
        The applied family type being scrutinised, supplying the
        parameters and the indices to be refined.
    constructor_arguments : tuple[StaticArgument, ...]
        Arguments instantiating the constructor's local telescope,
        normally the rigid skolems from `constructor_skolems`.

    Returns
    -------
    BranchRefinement
        The skolems in scope, the equalities the match makes available,
        and whether the branch is reachable.

    Raises
    ------
    TypeError
        If the constructor belongs to another family, if the scrutinee
        does not inhabit the matched family, or if the scrutinee supplies
        the wrong number of arguments.
    """
    if constructor.family != family.id:
        raise TypeError(f"constructor {constructor.name!r} belongs to another family")
    if scrutinee.constructor != family.type_constructor:
        raise TypeError("scrutinee does not inhabit the matched family")

    parameter_count = len(family.parameters)
    expected_count = parameter_count + len(family.indices)
    if len(scrutinee.arguments) != expected_count:
        raise TypeError(
            f"family {family.name!r} expects {expected_count} arguments, "
            f"got {len(scrutinee.arguments)}"
        )
    parameter_substitution = instantiate_telescope(
        family.parameters,
        scrutinee.arguments[:parameter_count],
    )
    local_substitution = instantiate_telescope(
        constructor.telescope,
        constructor_arguments,
    )
    substitution = StaticSubstitution(
        (*parameter_substitution.types, *local_substitution.types),
        (*parameter_substitution.indices, *local_substitution.indices),
        (*parameter_substitution.effects, *local_substitution.effects),
    )

    actual_indices = scrutinee.arguments[parameter_count:]
    if len(constructor.result_indices) != len(actual_indices):
        raise TypeError(
            f"constructor {constructor.name!r} returns the wrong number of indices"
        )

    givens: list[BranchGiven] = []
    states: list[Reachability] = []
    for position, (binder, actual, result) in enumerate(
        zip(family.indices, actual_indices, constructor.result_indices, strict=True)
    ):
        result = substitute_static(result, substitution)
        equality = EqualityType(_binder_classifier(binder), actual, result)
        equality_id = EqualityId.derive(constructor.id, position, equality)
        givens.append(BranchGiven(equality_id, equality))
        if actual == result:
            states.append(Reachability.REACHABLE)
        elif _closed_distinct(actual, result):
            states.append(Reachability.IMPOSSIBLE)
        else:
            states.append(Reachability.UNKNOWN)

    if any(state is Reachability.IMPOSSIBLE for state in states):
        reachability = Reachability.IMPOSSIBLE
    elif all(state is Reachability.REACHABLE for state in states):
        reachability = Reachability.REACHABLE
    else:
        reachability = Reachability.UNKNOWN
    return BranchRefinement(
        tuple(binder.name for binder in constructor.telescope),
        tuple(givens),
        reachability,
    )

distribution_backend

distribution_backend() -> DistributionBackend

The backend distribution values currently sample and score through.

RETURNS DESCRIPTION
DistributionBackend

The installed backend; the reference backend until replaced.

Source code in src/quivers/qiec/distributions.py
def distribution_backend() -> DistributionBackend:
    """The backend distribution values currently sample and score through.

    Returns
    -------
    DistributionBackend
        The installed backend; the reference backend until replaced.
    """
    return _backend

install_distribution_backend

install_distribution_backend(backend: DistributionBackend) -> DistributionBackend

Replace the backend distribution values sample and score through.

PARAMETER DESCRIPTION
backend

The backend to install, such as one over a host library that covers every family.

TYPE: DistributionBackend

RETURNS DESCRIPTION
DistributionBackend

The backend that was installed before, so a caller can restore it.

Source code in src/quivers/qiec/distributions.py
def install_distribution_backend(backend: DistributionBackend) -> DistributionBackend:
    """Replace the backend distribution values sample and score through.

    Parameters
    ----------
    backend : DistributionBackend
        The backend to install, such as one over a host library that
        covers every family.

    Returns
    -------
    DistributionBackend
        The backend that was installed before, so a caller can restore it.
    """
    global _backend
    previous = _backend
    _backend = backend
    return previous

seed_reference_rng

seed_reference_rng(seed: int) -> None

Reseed the generator reference draws use by default.

PARAMETER DESCRIPTION
seed

The seed; two runs seeded alike draw alike.

TYPE: int

Source code in src/quivers/qiec/distributions.py
def seed_reference_rng(seed: int) -> None:
    """Reseed the generator reference draws use by default.

    Parameters
    ----------
    seed : int
        The seed; two runs seeded alike draw alike.
    """
    _RNG.seed(seed)

instantiate_effect

instantiate_effect(effect: EffectRef, *, module: str, lexical_path: tuple[str | int, ...]) -> RowEntry

Allocate a deterministic lexical instance of an effect interface.

The instance identity is derived rather than generated, so the same interface allocated at the same place in the same module is the same instance on every run. That is what lets a serialized row refer to an instance across processes.

PARAMETER DESCRIPTION
effect

The saturated interface application to allocate.

TYPE: EffectRef

module

Module the allocation occurs in. Part of the identity, so two modules allocating at the same lexical path stay distinct.

TYPE: str

lexical_path

Position of the allocation within the module. Two allocations of one interface at different paths are different instances, which is what makes two instances of the same effect distinguishable.

TYPE: tuple[str | int, ...]

RETURNS DESCRIPTION
RowEntry

The fresh instance paired with the interface it implements, ready to add to a row.

Source code in src/quivers/qiec/effects.py
def instantiate_effect(
    effect: EffectRef,
    *,
    module: str,
    lexical_path: tuple[str | int, ...],
) -> RowEntry:
    """Allocate a deterministic lexical instance of an effect interface.

    The instance identity is derived rather than generated, so the same
    interface allocated at the same place in the same module is the same
    instance on every run. That is what lets a serialized row refer to an
    instance across processes.

    Parameters
    ----------
    effect : EffectRef
        The saturated interface application to allocate.
    module : str
        Module the allocation occurs in. Part of the identity, so two
        modules allocating at the same lexical path stay distinct.
    lexical_path : tuple[str | int, ...]
        Position of the allocation within the module. Two allocations of
        one interface at different paths are different instances, which
        is what makes two instances of the same effect distinguishable.

    Returns
    -------
    RowEntry
        The fresh instance paired with the interface it implements, ready
        to add to a row.
    """
    instance = EffectInstanceId.derive(
        str(effect.id),
        effect.arguments,
        module,
        lexical_path,
    )
    return RowEntry(instance, effect)

render_instance

render_instance(identity: EffectInstanceId, names: Mapping[EffectInstanceId, str] | None = None) -> str

Render one effect instance in the surface spelling.

PARAMETER DESCRIPTION
identity

The instance.

TYPE: EffectInstanceId

names

The source name of each lexical instance.

TYPE: Mapping[EffectInstanceId, str] | None DEFAULT: None

RETURNS DESCRIPTION
str

Its source name, or instance: and the first eight characters of its digest when it has none.

Source code in src/quivers/qiec/effects.py
def render_instance(
    identity: EffectInstanceId, names: Mapping[EffectInstanceId, str] | None = None
) -> str:
    """Render one effect instance in the surface spelling.

    Parameters
    ----------
    identity : EffectInstanceId
        The instance.
    names : Mapping[EffectInstanceId, str] | None
        The source name of each lexical instance.

    Returns
    -------
    str
        Its source name, or ``instance:`` and the first eight characters
        of its digest when it has none.
    """
    table = names or {}
    return table.get(identity, f"instance:{identity.digest[:8]}")

render_row

render_row(row: EffectRow, names: Mapping[EffectInstanceId, str] | None = None) -> str

Render an effect row in the surface spelling.

PARAMETER DESCRIPTION
row

The row.

TYPE: EffectRow

names

The source name of each lexical instance; an instance with no name renders as the first eight characters of its digest.

TYPE: Mapping[EffectInstanceId, str] | None DEFAULT: None

RETURNS DESCRIPTION
str

!{a : Effect, b : Other[Int] | rho}: each entry as its instance's name and interface, in name order, and an open row's tail after a bar, with the instances it lacks after a backslash.

Source code in src/quivers/qiec/effects.py
def render_row(
    row: EffectRow, names: Mapping[EffectInstanceId, str] | None = None
) -> str:
    """Render an effect row in the surface spelling.

    Parameters
    ----------
    row : EffectRow
        The row.
    names : Mapping[EffectInstanceId, str] | None
        The source name of each lexical instance; an instance with no
        name renders as the first eight characters of its digest.

    Returns
    -------
    str
        ``!{a : Effect, b : Other[Int] | rho}``: each entry as its
        instance's name and interface, in name order, and an open row's
        tail after a bar, with the instances it lacks after a backslash.
    """
    entries = ", ".join(
        sorted(
            f"{render_instance(entry.instance, names)} : {render_static(entry.effect)}"
            for entry in row.entries
        )
    )
    if row.tail is None:
        return "!{" + entries + "}"
    tail = row.tail.name
    if row.tail.lacks:
        tail += " \\ " + ", ".join(
            render_instance(item, names) for item in row.tail.lacks
        )
    return "!{" + (entries + " | " if entries else "") + tail + "}"

unify_effect_rows

unify_effect_rows(left: EffectRow, right: EffectRow) -> RowUnification

Unify two rows, returning their common row and tail substitution.

Unification is symmetric and driven by the tails. Two closed rows unify only when already equal. One open row unifies with a closed one by binding its tail to the entries it lacks. Two open rows with distinct tails unify by binding both to a fresh tail carrying every instance either side names.

PARAMETER DESCRIPTION
left

One row to unify.

TYPE: EffectRow

right

The other row. The result does not depend on the order of the two arguments.

TYPE: EffectRow

RETURNS DESCRIPTION
RowUnification

The common row and the substitution taking both inputs to it.

RAISES DESCRIPTION
ValueError

If the rows bind one instance to different interfaces, if two closed rows differ, if an open row carries an entry the closed row lacks, if one side needs an instance the other's tail is proven to lack, or if the computed substitution fails to carry both inputs to the unified row.

Source code in src/quivers/qiec/effects.py
def unify_effect_rows(left: EffectRow, right: EffectRow) -> RowUnification:
    """Unify two rows, returning their common row and tail substitution.

    Unification is symmetric and driven by the tails. Two closed rows
    unify only when already equal. One open row unifies with a closed one
    by binding its tail to the entries it lacks. Two open rows with
    distinct tails unify by binding both to a fresh tail carrying every
    instance either side names.

    Parameters
    ----------
    left : EffectRow
        One row to unify.
    right : EffectRow
        The other row. The result does not depend on the order of the two
        arguments.

    Returns
    -------
    RowUnification
        The common row and the substitution taking both inputs to it.

    Raises
    ------
    ValueError
        If the rows bind one instance to different interfaces, if two
        closed rows differ, if an open row carries an entry the closed
        row lacks, if one side needs an instance the other's tail is
        proven to lack, or if the computed substitution fails to carry
        both inputs to the unified row.
    """
    left_entries = {entry.instance: entry.effect for entry in left.entries}
    right_entries = {entry.instance: entry.effect for entry in right.entries}
    for instance in left_entries.keys() & right_entries.keys():
        if left_entries[instance] != right_entries[instance]:
            raise ValueError("row entries disagree on an effect interface")
    left_only = tuple(
        RowEntry(instance, effect)
        for instance, effect in left_entries.items()
        if instance not in right_entries
    )
    right_only = tuple(
        RowEntry(instance, effect)
        for instance, effect in right_entries.items()
        if instance not in left_entries
    )

    if left.tail is None and right.tail is None:
        if left_only or right_only:
            raise ValueError("closed effect rows do not unify")
        return RowUnification(left)

    if left.tail is not None and right.tail is None:
        if left_only:
            raise ValueError("an open row has entries absent from the closed row")
        replacement = EffectRow(right_only)
        substitution = RowSubstitution(((left.tail.identity, replacement),))
        if substitution.apply(left) != right:
            raise ValueError("row substitution violates a lacks constraint")
        return RowUnification(right, substitution)

    if left.tail is None and right.tail is not None:
        result = unify_effect_rows(right, left)
        return RowUnification(result.row, result.substitution)

    assert left.tail is not None and right.tail is not None
    if left.tail.identity == right.tail.identity:
        if left_only or right_only:
            raise ValueError(
                "two occurrences of one row variable have incompatible entries"
            )
        tail = left.tail.merge(right.tail)
        return RowUnification(EffectRow(left.entries, tail))

    if any(entry.instance in left.tail.lacks for entry in right_only):
        raise ValueError("left row lacks an entry required by the right row")
    if any(entry.instance in right.tail.lacks for entry in left_only):
        raise ValueError("right row lacks an entry required by the left row")
    all_instances = tuple((*left_entries.keys(), *right_entries.keys()))
    fresh_identity = RowVariableId.derive(
        "unify",
        *sorted((left.tail.identity, right.tail.identity), key=str),
        *sorted(all_instances, key=str),
    )
    fresh = RowVariable(
        "unified_tail",
        fresh_identity,
        tuple(
            sorted(
                set((*left.tail.lacks, *right.tail.lacks, *all_instances)),
                key=str,
            )
        ),
    )
    left_replacement = EffectRow(right_only, fresh)
    right_replacement = EffectRow(left_only, fresh)
    substitution = RowSubstitution(
        (
            (left.tail.identity, left_replacement),
            (right.tail.identity, right_replacement),
        )
    )
    unified_entries = tuple(
        RowEntry(instance, effect)
        for instance, effect in {**left_entries, **right_entries}.items()
    )
    unified = EffectRow(unified_entries, fresh)
    if substitution.apply(left) != unified or substitution.apply(right) != unified:
        raise ValueError("row unification produced an inconsistent substitution")
    return RowUnification(unified, substitution)

entry_point

entry_point(module: QiecModule, name: str) -> EntryPoint

One entry point of a module, by name.

PARAMETER DESCRIPTION
module

The checked module.

TYPE: QiecModule

name

The entry's source name.

TYPE: str

RETURNS DESCRIPTION
EntryPoint

The entry.

RAISES DESCRIPTION
ExecutionFailure

With code qiec-run-computation if the module has no such entry.

Source code in src/quivers/qiec/entries.py
def entry_point(module: QiecModule, name: str) -> EntryPoint:
    """One entry point of a module, by name.

    Parameters
    ----------
    module : QiecModule
        The checked module.
    name : str
        The entry's source name.

    Returns
    -------
    EntryPoint
        The entry.

    Raises
    ------
    ExecutionFailure
        With code ``qiec-run-computation`` if the module has no such
        entry.
    """
    for point in entry_points(module):
        if point.name == name:
            return point
    available = ", ".join(point.name for point in entry_points(module)) or "(none)"
    raise ExecutionFailure(
        ExecutionDiagnostic(
            "qiec-run-computation",
            f"unknown entry point {name!r}; available: {available}",
            name,
        )
    )

entry_points

entry_points(module: QiecModule) -> tuple[EntryPoint, ...]

Every executable entry point of a module.

PARAMETER DESCRIPTION
module

The checked module.

TYPE: QiecModule

RETURNS DESCRIPTION
tuple[EntryPoint, ...]

The programs and the computations, in declaration order; a computation a program elaborated to appears once, as the program, and a marginalization helper not at all.

Source code in src/quivers/qiec/entries.py
def entry_points(module: QiecModule) -> tuple[EntryPoint, ...]:
    """Every executable entry point of a module.

    Parameters
    ----------
    module : QiecModule
        The checked module.

    Returns
    -------
    tuple[EntryPoint, ...]
        The programs and the computations, in declaration order; a
        computation a program elaborated to appears once, as the program,
        and a marginalization helper not at all.
    """
    programs = {entry.computation: entry for entry in module.entries}
    helpers = {
        computation.id
        for computation in module.computations
        if computation.name.startswith("__")
    }
    points: list[EntryPoint] = []
    for computation in module.computations:
        program = programs.get(computation.id)
        if program is not None:
            points.append(_program_entry_point(program, computation))
        elif computation.id not in helpers:
            points.append(_computation_entry(module, computation))
    return tuple(points)

invoke_entry

invoke_entry(module: QiecModule, name: str, arguments: Sequence[HostValue] = (), *, data: Mapping[str, HostValue] | None = None, sites: Mapping[str, HostValue] | None = None, static_arguments: Sequence[str] = (), runtime: RuntimeConfiguration | None = None, fuel: int | None = None, seed: int | None = None, observer: TraceObserver | None = None) -> EntryRun

Invoke an entry point.

PARAMETER DESCRIPTION
module

The checked module.

TYPE: QiecModule

name

The entry's source name.

TYPE: str

arguments

Host values for the entry's value parameters, in order. A program's parameters may instead, or additionally, be given by name through data.

TYPE: Sequence[HostValue] DEFAULT: ()

data

A program's parameters by name: its data, observations, fibrations, and scalars. A name given both positionally and here is an error.

TYPE: Mapping[str, HostValue] | None DEFAULT: None

sites

The values a program run conditions its sample sites on, by label; every other site is drawn. Not accepted for a computation.

TYPE: Mapping[str, HostValue] | None DEFAULT: None

static_arguments

Closed terms instantiating a computation's telescope, as NAME=TERM text in any order. A program's extents are read off its data where the data fix them, so a program takes a static argument only for an extent no data fixes, a template's object parameter.

TYPE: Sequence[str] DEFAULT: ()

runtime

The providers a computation runs under; the core provider alone by default. A program's handlers are fixed by the run, so only the default configuration is accepted for a program.

TYPE: RuntimeConfiguration | None DEFAULT: None

fuel

A step budget for the run.

TYPE: int | None DEFAULT: None

seed

A seed for the reference generator every draw of the run uses; two runs seeded alike draw alike. None continues the generator's current state.

TYPE: int | None DEFAULT: None

observer

A callback receiving every trace event as it is emitted.

TYPE: TraceObserver | None DEFAULT: None

RETURNS DESCRIPTION
EntryRun

The value, the log joint for a program, and the execution result.

RAISES DESCRIPTION
ExecutionFailure

With code qiec-run-computation for an unknown entry, qiec-run-config for an option the entry's kind does not accept or a parameter given twice, qiec-run-arity for a program parameter not given, and the codes of run_named for the run.

Source code in src/quivers/qiec/entries.py
def invoke_entry(
    module: QiecModule,
    name: str,
    arguments: Sequence[HostValue] = (),
    *,
    data: Mapping[str, HostValue] | None = None,
    sites: Mapping[str, HostValue] | None = None,
    static_arguments: Sequence[str] = (),
    runtime: RuntimeConfiguration | None = None,
    fuel: int | None = None,
    seed: int | None = None,
    observer: TraceObserver | None = None,
) -> EntryRun:
    """Invoke an entry point.

    Parameters
    ----------
    module : QiecModule
        The checked module.
    name : str
        The entry's source name.
    arguments : Sequence[HostValue]
        Host values for the entry's value parameters, in order. A
        program's parameters may instead, or additionally, be given by
        name through ``data``.
    data : Mapping[str, HostValue] | None
        A program's parameters by name: its data, observations,
        fibrations, and scalars. A name given both positionally and here
        is an error.
    sites : Mapping[str, HostValue] | None
        The values a program run conditions its sample sites on, by
        label; every other site is drawn. Not accepted for a computation.
    static_arguments : Sequence[str]
        Closed terms instantiating a computation's telescope, as
        ``NAME=TERM`` text in any order. A program's extents are read
        off its data where the data fix them, so a program takes a
        static argument only for an extent no data fixes, a template's
        object parameter.
    runtime : RuntimeConfiguration | None
        The providers a computation runs under; the core provider alone
        by default. A program's handlers are fixed by the run, so only
        the default configuration is accepted for a program.
    fuel : int | None
        A step budget for the run.
    seed : int | None
        A seed for the reference generator every draw of the run uses;
        two runs seeded alike draw alike. ``None`` continues the
        generator's current state.
    observer : TraceObserver | None
        A callback receiving every trace event as it is emitted.

    Returns
    -------
    EntryRun
        The value, the log joint for a program, and the execution result.

    Raises
    ------
    ExecutionFailure
        With code ``qiec-run-computation`` for an unknown entry,
        ``qiec-run-config`` for an option the entry's kind does not
        accept or a parameter given twice, ``qiec-run-arity`` for a
        program parameter not given, and the codes of
        [`run_named`][quivers.qiec.execution.run_named] for the run.
    """
    entry = entry_point(module, name)
    if seed is not None:
        seed_reference_rng(seed)
    if entry.kind == "computation":
        if data:
            _refuse(name, "a computation takes its arguments positionally")
        if sites:
            _refuse(name, "a computation has no sites to condition on")
        statics = parse_static_arguments(module, name, tuple(static_arguments))
        result = run_named(
            module,
            name,
            tuple(arguments),
            static_arguments=statics,
            runtime=runtime,
            observer=observer,
            fuel=fuel,
        )
        return EntryRun(
            entry=entry,
            value=cast(HostValue, result.value),
            log_joint=None,
            result=result,
        )
    statics = (
        parse_static_arguments(module, name, tuple(static_arguments))
        if static_arguments
        else ()
    )
    if runtime is not None and runtime.selections not in (
        RuntimeConfiguration().selections,
        (),
    ):
        _refuse(
            name,
            "a program's handlers are fixed by the run; runtime selections "
            "must be the default core provider or empty",
        )
    program = program_entry(module, name)
    if len(arguments) > len(program.parameters):
        raise ExecutionFailure(
            ExecutionDiagnostic(
                "qiec-run-arity",
                f"program {name!r} takes {len(program.parameters)} parameters, "
                f"got {len(arguments)} positional arguments",
                name,
            )
        )
    supplied: dict[str, HostValue] = {}
    for parameter, value in zip(program.parameters, arguments):
        supplied[parameter.name] = value
    for parameter_name, value in (data or {}).items():
        if parameter_name in supplied:
            _refuse(name, f"parameter {parameter_name!r} is given twice")
        supplied[parameter_name] = value
    missing = [
        parameter.name
        for parameter in program.parameters
        if parameter.name not in supplied
    ]
    if missing:
        raise ExecutionFailure(
            ExecutionDiagnostic(
                "qiec-run-arity",
                f"program {name!r} is missing parameter(s) {', '.join(missing)}",
                name,
            )
        )
    unknown = [
        parameter_name
        for parameter_name in supplied
        if all(parameter.name != parameter_name for parameter in program.parameters)
    ]
    if unknown:
        _refuse(name, f"program {name!r} has no parameter(s) {', '.join(unknown)}")
    try:
        run = sample_program(
            module,
            name,
            data=supplied,
            sites=sites,
            static_arguments=statics,
            fuel=fuel,
            observer=observer,
            providers=runtime.providers if runtime is not None else (),
        )
    except KeyError as error:
        _refuse(name, str(error.args[0]))
    computation = next(
        item for item in module.computations if item.id == program.computation
    )
    # The wrapper returns the value paired with the log weight; the
    # entry's result is the program's value at the program's type,
    # instantiated at the extents the run fixed.
    extents = static_arguments_for(program, supplied, statics)
    result = replace(
        run.result,
        computation=name,
        value=run.value,
        result_type=substitute_type(
            computation.type.result,
            StaticSubstitution(
                indices=tuple(
                    (binder.name, cast(IndexTerm, extent))
                    for binder, extent in zip(program.telescope, extents, strict=True)
                    if isinstance(binder, IndexBinder)
                )
            ),
        ),
    )
    return EntryRun(
        entry=entry,
        value=cast(HostValue, run.value),
        log_joint=run.log_joint,
        result=result,
    )

json_value

json_value(value: JsonValue) -> HostValue

Read a parsed JSON value as a host value.

PARAMETER DESCRIPTION
value

The parsed JSON.

TYPE: JsonValue

RETURNS DESCRIPTION
HostValue

The value with every list read as a tuple, which is how the runtime carries a tensor.

RAISES DESCRIPTION
ValueError

If the value is null, which no host type inhabits.

Source code in src/quivers/qiec/entries.py
def json_value(value: JsonValue) -> HostValue:
    """Read a parsed JSON value as a host value.

    Parameters
    ----------
    value : JsonValue
        The parsed JSON.

    Returns
    -------
    HostValue
        The value with every list read as a tuple, which is how the
        runtime carries a tensor.

    Raises
    ------
    ValueError
        If the value is ``null``, which no host type inhabits.
    """
    if value is None:
        raise ValueError("null is not a host value")
    if isinstance(value, list):
        return tuple(json_value(item) for item in value)
    if isinstance(value, dict):
        return {key: json_value(item) for key, item in value.items()}
    return value

parse_bindings

parse_bindings(items: Sequence[str]) -> dict[str, HostValue]

Read NAME=JSON bindings, as the command line and REPL take them.

PARAMETER DESCRIPTION
items

The bindings as typed.

TYPE: Sequence[str]

RETURNS DESCRIPTION
dict[str, HostValue]

Name to the parsed value, lists read as tuples.

RAISES DESCRIPTION
ValueError

If an item has no =, names a binding twice, or carries a value that is not JSON.

Source code in src/quivers/qiec/entries.py
def parse_bindings(items: Sequence[str]) -> dict[str, HostValue]:
    """Read ``NAME=JSON`` bindings, as the command line and REPL take them.

    Parameters
    ----------
    items : Sequence[str]
        The bindings as typed.

    Returns
    -------
    dict[str, HostValue]
        Name to the parsed value, lists read as tuples.

    Raises
    ------
    ValueError
        If an item has no ``=``, names a binding twice, or carries a
        value that is not JSON.
    """
    bindings: dict[str, HostValue] = {}
    for item in items:
        binding_name, separator, text = item.partition("=")
        binding_name = binding_name.strip()
        if not separator or not binding_name:
            raise ValueError(f"binding {item!r} must have the form NAME=JSON")
        if binding_name in bindings:
            raise ValueError(f"binding {binding_name!r} is given twice")
        try:
            bindings[binding_name] = json_value(json.loads(text))
        except json.JSONDecodeError as error:
            raise ValueError(
                f"binding {binding_name!r} has a value that is not JSON: {error.msg}"
            ) from error
    return bindings

render_entry

render_entry(entry: EntryPoint) -> str

Render an entry point as one signature line.

PARAMETER DESCRIPTION
entry

The entry.

TYPE: EntryPoint

RETURNS DESCRIPTION
str

name(params) : result with the kind, the statics, the effects, and a program's sites where present.

Source code in src/quivers/qiec/entries.py
def render_entry(entry: EntryPoint) -> str:
    """Render an entry point as one signature line.

    Parameters
    ----------
    entry : EntryPoint
        The entry.

    Returns
    -------
    str
        ``name(params) : result`` with the kind, the statics, the effects,
        and a program's sites where present.
    """
    parameters = ", ".join(
        f"{parameter.name} : {parameter.type}" for parameter in entry.parameters
    )
    statics = f"[{', '.join(entry.statics)}]" if entry.statics else ""
    line = f"{entry.kind} {entry.name}{statics}({parameters}) : {entry.result}"
    if entry.effects:
        line += " !{" + ", ".join(entry.effects) + "}"
    if entry.sites:
        line += " sites " + ", ".join(entry.sites)
    return line

available_runtime_providers

available_runtime_providers() -> tuple[str, ...]

Return built-in and installed quivers.qiec_runtime entry points.

RETURNS DESCRIPTION
tuple[str, ...]

Every selectable provider name, sorted.

Source code in src/quivers/qiec/execution.py
def available_runtime_providers() -> tuple[str, ...]:
    """Return built-in and installed ``quivers.qiec_runtime`` entry points.

    Returns
    -------
    tuple[str, ...]
        Every selectable provider name, sorted.
    """

    installed = {
        point.name for point in entry_points().select(group="quivers.qiec_runtime")
    }
    return tuple(sorted({*_PROVIDER_FACTORIES, *installed}))

load_runtime_configuration

load_runtime_configuration(path: str | Path) -> RuntimeConfiguration

Load an explicit runtime selection without importing executable code.

PARAMETER DESCRIPTION
path

A JSON file in the format :func:runtime_configuration_from_data accepts.

TYPE: str | Path

RETURNS DESCRIPTION
RuntimeConfiguration

The decoded selection.

RAISES DESCRIPTION
ValueError

If the file cannot be read, is not valid JSON, or does not decode as a runtime configuration.

Source code in src/quivers/qiec/execution.py
def load_runtime_configuration(path: str | Path) -> RuntimeConfiguration:
    """Load an explicit runtime selection without importing executable code.

    Parameters
    ----------
    path
        A JSON file in the format :func:`runtime_configuration_from_data`
        accepts.

    Returns
    -------
    RuntimeConfiguration
        The decoded selection.

    Raises
    ------
    ValueError
        If the file cannot be read, is not valid JSON, or does not decode
        as a runtime configuration.
    """

    source = Path(path)
    try:
        data = json.loads(source.read_text())
    except (OSError, json.JSONDecodeError) as error:
        raise ValueError(
            f"could not load runtime configuration {source}: {error}"
        ) from error
    return runtime_configuration_from_data(data)

parse_static_arguments

parse_static_arguments(module: QiecModule, computation: str, assignments: tuple[str, ...]) -> tuple[StaticArgument, ...]

Resolve CLI NAME=TERM specializations in telescope order.

This intentionally accepts only closed, nominal terms available in the module: primitive or declared types, closed index constructors/literals, and declared effect applications. Static variables and inferred holes are not runtime configuration values.

PARAMETER DESCRIPTION
module

The module declaring computation and the names its terms may use.

TYPE: QiecModule

computation

The named computation whose telescope is being instantiated.

TYPE: str

assignments

NAME=TERM strings, one per telescope binder, in any order.

TYPE: tuple[str, ...]

RETURNS DESCRIPTION
tuple[StaticArgument, ...]

The parsed arguments in the telescope's order.

RAISES DESCRIPTION
ExecutionFailure

With code qiec-run-computation if the computation is unknown, or qiec-run-static if an assignment is malformed, repeated, names a binder the telescope lacks, omits one it has, or does not parse at the binder's sort.

Source code in src/quivers/qiec/execution.py
def parse_static_arguments(
    module: QiecModule,
    computation: str,
    assignments: tuple[str, ...],
) -> tuple[StaticArgument, ...]:
    """Resolve CLI ``NAME=TERM`` specializations in telescope order.

    This intentionally accepts only closed, nominal terms available in the
    module: primitive or declared types, closed index constructors/literals,
    and declared effect applications.  Static variables and inferred holes are
    not runtime configuration values.

    Parameters
    ----------
    module
        The module declaring ``computation`` and the names its terms may use.
    computation
        The named computation whose telescope is being instantiated.
    assignments
        ``NAME=TERM`` strings, one per telescope binder, in any order.

    Returns
    -------
    tuple[StaticArgument, ...]
        The parsed arguments in the telescope's order.

    Raises
    ------
    ExecutionFailure
        With code ``qiec-run-computation`` if the computation is unknown, or
        ``qiec-run-static`` if an assignment is malformed, repeated, names a
        binder the telescope lacks, omits one it has, or does not parse at
        the binder's sort.
    """

    selected = next(
        (
            candidate
            for candidate in module.computations
            if candidate.name == computation
        ),
        None,
    )
    if selected is None:
        _fail("qiec-run-computation", f"unknown QIEC computation {computation!r}")
    assert selected is not None
    values: dict[str, str] = {}
    for assignment in assignments:
        name, separator, value = assignment.partition("=")
        if not separator or not name or not value:
            _fail(
                "qiec-run-static",
                f"static argument {assignment!r} must have the form NAME=TERM",
                computation,
                selected.origin,
            )
        if name in values:
            _fail(
                "qiec-run-static",
                f"static argument {name!r} was supplied more than once",
                computation,
                selected.origin,
            )
        values[name] = value
    expected = {binder.name for binder in selected.telescope}
    unknown = set(values) - expected
    missing = expected - set(values)
    if unknown or missing:
        detail: list[str] = []
        if missing:
            detail.append(f"missing {', '.join(sorted(missing))}")
        if unknown:
            detail.append(f"unknown {', '.join(sorted(unknown))}")
        _fail(
            "qiec-run-static",
            "invalid static specialization: " + "; ".join(detail),
            computation,
            selected.origin,
        )
    try:
        return tuple(
            _parse_static(module, binder, values[binder.name])
            for binder in selected.telescope
        )
    except (TypeError, ValueError) as error:
        _fail(
            "qiec-run-static",
            f"invalid static specialization for {computation!r}: {error}",
            computation,
            selected.origin,
        )

register_runtime_provider

register_runtime_provider(name: str, factory: RuntimeProviderFactory) -> None

Register a provider factory for explicit configuration by name.

Registration makes a provider selectable; it never activates that provider for an invocation. Replacing an existing name is rejected so plugin load order cannot silently change execution semantics.

PARAMETER DESCRIPTION
name

The name a configuration uses to select the provider.

TYPE: str

factory

The callable building a provider from JSON-compatible options.

TYPE: RuntimeProviderFactory

RAISES DESCRIPTION
ValueError

If name is empty or already registered.

Source code in src/quivers/qiec/execution.py
def register_runtime_provider(name: str, factory: RuntimeProviderFactory) -> None:
    """Register a provider factory for explicit configuration by name.

    Registration makes a provider selectable; it never activates that provider
    for an invocation.  Replacing an existing name is rejected so plugin load
    order cannot silently change execution semantics.

    Parameters
    ----------
    name
        The name a configuration uses to select the provider.
    factory
        The callable building a provider from JSON-compatible options.

    Raises
    ------
    ValueError
        If ``name`` is empty or already registered.
    """

    if not name or name in _PROVIDER_FACTORIES:
        raise ValueError(f"runtime provider {name!r} is already registered or invalid")
    _PROVIDER_FACTORIES[name] = factory

run_named

run_named(module: QiecModule, computation: str, arguments: tuple[object, ...] = (), *, static_arguments: tuple[StaticArgument, ...] = (), runtime: RuntimeConfiguration | None = None, observer: TraceObserver | None = None, fuel: int | None = None) -> ExecutionResult

Validate and execute one named computation from module.

The attachment table and every configured provider instance are allocated inside this call. Mutable handler state can thus never leak from one named invocation to the next.

PARAMETER DESCRIPTION
module

The module declaring the computation; it is validated before the body is evaluated.

TYPE: QiecModule

computation

The name of the computation to run.

TYPE: str

arguments

Host values for the computation's value parameters, in order.

TYPE: tuple[object, ...] DEFAULT: ()

static_arguments

Closed terms instantiating the computation's telescope, in order; required whenever the telescope is non-empty.

TYPE: tuple[StaticArgument, ...] DEFAULT: ()

runtime

The providers to attach; defaults to the core provider alone.

TYPE: RuntimeConfiguration | None DEFAULT: None

observer

A callback receiving every trace event as it is emitted.

TYPE: TraceObserver | None DEFAULT: None

fuel

The number of machine steps the run may take, or None for no bound. General recursion may diverge, and a bound is the only way a caller can be sure a run ends.

TYPE: int | None DEFAULT: None

RETURNS DESCRIPTION
ExecutionResult

The validated result value, its specialized type, the runtime label, and the full trace.

RAISES DESCRIPTION
ExecutionFailure

With code qiec-run-computation for an unknown name, qiec-run-static for a bad specialization, qiec-run-arity for a wrong argument count, qiec-run-provider when a provider cannot be built or attached, qiec-run-validator when no provider covers an argument or result type, qiec-run-argument or qiec-run-result when a value fails validation, qiec-run-module when the module fails validation, qiec-run-fuel when the step budget runs out, and qiec-run-evaluation when evaluation or a runtime clause fails.

Source code in src/quivers/qiec/execution.py
def run_named(
    module: QiecModule,
    computation: str,
    arguments: tuple[object, ...] = (),
    *,
    static_arguments: tuple[StaticArgument, ...] = (),
    runtime: RuntimeConfiguration | None = None,
    observer: TraceObserver | None = None,
    fuel: int | None = None,
) -> ExecutionResult:
    """Validate and execute one named computation from ``module``.

    The attachment table and every configured provider instance are allocated
    inside this call.  Mutable handler state can thus never leak from one named
    invocation to the next.

    Parameters
    ----------
    module
        The module declaring the computation; it is validated before the
        body is evaluated.
    computation
        The name of the computation to run.
    arguments
        Host values for the computation's value parameters, in order.
    static_arguments
        Closed terms instantiating the computation's telescope, in order;
        required whenever the telescope is non-empty.
    runtime
        The providers to attach; defaults to the core provider alone.
    observer
        A callback receiving every trace event as it is emitted.
    fuel
        The number of machine steps the run may take, or ``None`` for no
        bound. General recursion may diverge, and a bound is the only way
        a caller can be sure a run ends.

    Returns
    -------
    ExecutionResult
        The validated result value, its specialized type, the runtime label,
        and the full trace.

    Raises
    ------
    ExecutionFailure
        With code ``qiec-run-computation`` for an unknown name,
        ``qiec-run-static`` for a bad specialization, ``qiec-run-arity`` for
        a wrong argument count, ``qiec-run-provider`` when a provider cannot
        be built or attached, ``qiec-run-validator`` when no provider covers
        an argument or result type, ``qiec-run-argument`` or
        ``qiec-run-result`` when a value fails validation,
        ``qiec-run-module`` when the module fails validation,
        ``qiec-run-fuel`` when the step budget runs out, and
        ``qiec-run-evaluation`` when evaluation or a runtime clause fails.
    """

    selected = next(
        (
            candidate
            for candidate in module.computations
            if candidate.name == computation
        ),
        None,
    )
    if selected is None:
        available = ", ".join(item.name for item in module.computations) or "(none)"
        _fail(
            "qiec-run-computation",
            f"unknown QIEC computation {computation!r}; available: {available}",
            computation,
        )
    assert selected is not None
    config = runtime or RuntimeConfiguration()
    recorder = ExecutionTraceRecorder()
    sequence = 0

    def emit(event: str, detail: Mapping[str, object] | None = None) -> None:
        """Record one trace event and forward it to the observer.

        Parameters
        ----------
        event
            The stable event name.
        detail
            JSON-compatible event data; ``None`` records an empty mapping.
        """
        nonlocal sequence
        trace_event = ExecutionTraceEvent(sequence, event, selected.name, detail or {})
        sequence += 1
        recorder(trace_event)
        if observer is not None:
            observer(trace_event)

    emit("run.started", {"runtime": config.label})
    substitution = _specialize(selected, static_arguments)
    if static_arguments:
        emit(
            "run.specialized",
            {"arguments": [_render_static(argument) for argument in static_arguments]},
        )
    specialized_parameters, specialized_body = _specialize_computation(
        selected, substitution
    )
    specialized_result = substitute_type(selected.type.result, substitution)
    if len(arguments) != len(selected.parameters):
        _fail(
            "qiec-run-arity",
            f"computation {selected.name!r} expects {len(selected.parameters)} "
            f"value arguments, got {len(arguments)}",
            selected.name,
            selected.origin,
        )

    providers = config.instantiate()
    attachments = RuntimeAttachments()
    try:
        for provider in providers:
            provider.attach(module, attachments)
            emit("runtime.attached", {"provider": provider.name})
    except ExecutionFailure:
        raise
    except Exception as error:
        _fail(
            "qiec-run-provider",
            f"runtime provider failed while attaching: {error}",
            selected.name,
            selected.origin,
        )

    for position, (parameter, specialized_parameter, value) in enumerate(
        zip(selected.parameters, specialized_parameters, arguments, strict=True)
    ):
        expected = specialized_parameter.type
        validator = _validator_for(expected, providers)
        if validator is None:
            _fail(
                "qiec-run-validator",
                f"no configured runtime provider can validate argument "
                f"{parameter.name!r} at type {_render_type(expected)}",
                selected.name,
                selected.origin,
            )
        try:
            accepted = validator(value)
        except Exception as error:
            _fail(
                "qiec-run-argument",
                f"validator raised for argument {parameter.name!r}: {error}",
                selected.name,
                selected.origin,
            )
        if accepted is False:
            _fail(
                "qiec-run-argument",
                f"argument {position} ({parameter.name}) does not inhabit "
                f"{_render_type(expected)}: {value!r}",
                selected.name,
                selected.origin,
            )
        emit("argument.validated", {"name": parameter.name, "position": position})

    try:
        registry = validate_module(module)
    except Exception as error:
        _fail(
            "qiec-run-module",
            f"QIEC module failed validation before execution: {error}",
            selected.name,
            selected.origin,
        )
    environment = dict(zip(specialized_parameters, arguments, strict=True))

    def evaluator_trace(event: str, detail: Mapping[str, object]) -> None:
        """Forward an evaluator trace event into the invocation's trace.

        Parameters
        ----------
        event
            The stable event name the evaluator emitted.
        detail
            The event's data.
        """
        emit(event, detail)

    evaluator = Evaluator(
        attachments,
        trace_hook=evaluator_trace,
        module=module,
        fuel=fuel,
        type_validator=lambda type_: _validator_for(type_, providers),
    )
    try:
        value = evaluator.evaluate_checked(specialized_body, registry, environment)
    except ExecutionFailure:
        raise
    except FuelExhaustedError as error:
        emit("run.failed", {"error": type(error).__name__, "steps": error.steps})
        _fail(
            "qiec-run-fuel",
            f"computation {selected.name!r} did not finish within {error.steps} steps",
            selected.name,
            selected.origin,
        )
    except EvaluationError as error:
        emit("run.failed", {"error": type(error).__name__})
        _fail(
            "qiec-run-evaluation",
            f"computation {selected.name!r} failed: {error}",
            selected.name,
            selected.origin,
        )
    except Exception as error:
        emit("run.failed", {"error": type(error).__name__})
        _fail(
            "qiec-run-evaluation",
            f"runtime clause for computation {selected.name!r} failed: {error}",
            selected.name,
            selected.origin,
        )
    validator = _validator_for(specialized_result, providers)
    if validator is None:
        _fail(
            "qiec-run-validator",
            f"no configured runtime provider can validate result type "
            f"{_render_type(specialized_result)}",
            selected.name,
            selected.origin,
        )
    try:
        accepted = validator(value)
    except Exception as error:
        _fail(
            "qiec-run-result",
            f"result validator raised: {error}",
            selected.name,
            selected.origin,
        )
    if accepted is False:
        _fail(
            "qiec-run-result",
            f"result does not inhabit {_render_type(specialized_result)}: {value!r}",
            selected.name,
            selected.origin,
        )
    emit("run.completed", {"result_type": _render_type(specialized_result)})
    return ExecutionResult(
        selected.name,
        value,
        specialized_result,
        config.label,
        tuple(recorder.events),
    )

runtime_configuration_from_data

runtime_configuration_from_data(data: object) -> RuntimeConfiguration

Decode the non-executable JSON runtime configuration format.

PARAMETER DESCRIPTION
data

The decoded JSON document; providers lists names or {"name": ..., "options": {...}} objects and defaults to core.

TYPE: object

RETURNS DESCRIPTION
RuntimeConfiguration

The selections in document order, with no programmatic providers.

RAISES DESCRIPTION
ValueError

If data is not an object, providers is not a list, an entry is neither a name nor an object with a name, or an entry's options is not an object.

Source code in src/quivers/qiec/execution.py
def runtime_configuration_from_data(data: object) -> RuntimeConfiguration:
    """Decode the non-executable JSON runtime configuration format.

    Parameters
    ----------
    data
        The decoded JSON document; ``providers`` lists names or
        ``{"name": ..., "options": {...}}`` objects and defaults to ``core``.

    Returns
    -------
    RuntimeConfiguration
        The selections in document order, with no programmatic providers.

    Raises
    ------
    ValueError
        If ``data`` is not an object, ``providers`` is not a list, an entry
        is neither a name nor an object with a ``name``, or an entry's
        ``options`` is not an object.
    """

    if not isinstance(data, dict):
        raise ValueError("runtime configuration must be a JSON object")
    raw = data.get("providers", [{"name": "core"}])
    if not isinstance(raw, list):
        raise ValueError("runtime configuration 'providers' must be a list")
    selections: list[RuntimeSelection] = []
    for item in raw:
        if isinstance(item, str):
            selections.append(RuntimeSelection(item))
            continue
        if not isinstance(item, dict) or not isinstance(item.get("name"), str):
            raise ValueError(
                "each runtime provider must be a name or object with 'name'"
            )
        options = item.get("options", {})
        if not isinstance(options, dict):
            raise ValueError("runtime provider 'options' must be an object")
        selections.append(RuntimeSelection(item["name"], options))
    return RuntimeConfiguration(tuple(selections))

family

family(name: str) -> DistributionFamily

Look up a distribution family by source name.

PARAMETER DESCRIPTION
name

The family's source name.

TYPE: str

RETURNS DESCRIPTION
DistributionFamily

The family's semantic record.

RAISES DESCRIPTION
KeyError

If no family has the name.

Source code in src/quivers/qiec/families.py
def family(name: str) -> DistributionFamily:
    """Look up a distribution family by source name.

    Parameters
    ----------
    name : str
        The family's source name.

    Returns
    -------
    DistributionFamily
        The family's semantic record.

    Raises
    ------
    KeyError
        If no family has the name.
    """
    try:
        return FAMILIES[name]
    except KeyError as error:
        raise KeyError(f"unknown distribution family {name!r}") from error

validate_telescope

validate_telescope(telescope: Telescope) -> None

Reject duplicate names in one dependent telescope.

PARAMETER DESCRIPTION
telescope

The binders to check, in order.

TYPE: Telescope

RAISES DESCRIPTION
ValueError

If a binder name is empty or repeats. Later binders may depend on earlier ones, so a repeated name would make a reference ambiguous.

Source code in src/quivers/qiec/kinds.py
def validate_telescope(telescope: Telescope) -> None:
    """Reject duplicate names in one dependent telescope.

    Parameters
    ----------
    telescope : Telescope
        The binders to check, in order.

    Raises
    ------
    ValueError
        If a binder name is empty or repeats. Later binders may depend on
        earlier ones, so a repeated name would make a reference ambiguous.
    """
    seen: set[str] = set()
    for binder in telescope:
        if not binder.name:
            raise ValueError("telescope binder name cannot be empty")
        if binder.name in seen:
            raise ValueError(f"duplicate telescope binder {binder.name!r}")
        seen.add(binder.name)

called_computations

called_computations(body: Computation) -> frozenset[ComputationId]

The computations a body calls, directly.

PARAMETER DESCRIPTION
body

The body to walk.

TYPE: Computation

RETURNS DESCRIPTION
frozenset[ComputationId]

The callee of every Call in the body, at any depth.

Source code in src/quivers/qiec/module.py
def called_computations(body: Computation) -> frozenset[ComputationId]:
    """The computations a body calls, directly.

    Parameters
    ----------
    body : Computation
        The body to walk.

    Returns
    -------
    frozenset[ComputationId]
        The callee of every ``Call`` in the body, at any depth.
    """
    found: set[ComputationId] = set()
    pending: list[Computation] = [body]
    while pending:
        term = pending.pop()
        if isinstance(term, Call):
            found.add(term.callee)
        elif isinstance(term, Bind):
            pending.append(term.then)
            pending.append(term.first)
        elif isinstance(term, Handle):
            pending.append(term.computation)
        elif isinstance(term, Case):
            pending.extend(branch.body for branch in term.branches)
        elif isinstance(term, If):
            pending.append(term.otherwise)
            pending.append(term.then)
        elif isinstance(term, NewInstance):
            pending.append(term.body)
    return frozenset(found)

computation_type_conforms

computation_type_conforms(actual: ComputationType, declared: ComputationType) -> bool

Check exact rows or sound weakening into an explicitly open row.

An inferred open tail can inhabit a declared open row only when it proves every absence guaranteed by the declaration. Row-variable names and IDs are lexical identities, so conformance compares their constraints rather than treating unrelated tails as identical.

PARAMETER DESCRIPTION
actual

The type inferred from the body.

TYPE: ComputationType

declared

The type the declaration states.

TYPE: ComputationType

RETURNS DESCRIPTION
bool

True when the body's type inhabits the declaration. The result types must be equal; the rows may differ only by the inferred row being weakened into a declared open one that it proves every absence of.

Source code in src/quivers/qiec/module.py
def computation_type_conforms(
    actual: ComputationType,
    declared: ComputationType,
) -> bool:
    """Check exact rows or sound weakening into an explicitly open row.

    An inferred open tail can inhabit a declared open row only when it proves
    every absence guaranteed by the declaration. Row-variable names and IDs
    are lexical identities, so conformance compares their constraints rather
    than treating unrelated tails as identical.

    Parameters
    ----------
    actual : ComputationType
        The type inferred from the body.
    declared : ComputationType
        The type the declaration states.

    Returns
    -------
    bool
        True when the body's type inhabits the declaration. The result
        types must be equal; the rows may differ only by the inferred row
        being weakened into a declared open one that it proves every
        absence of.
    """

    if actual.result != declared.result:
        return False
    if declared.effects.tail is None:
        return actual.effects == declared.effects
    actual_entries = {entry.instance: entry.effect for entry in actual.effects.entries}
    declared_entries = {
        entry.instance: entry.effect for entry in declared.effects.entries
    }
    for instance, effect in actual_entries.items():
        expected = declared_entries.get(instance)
        if expected is not None:
            if expected != effect:
                return False
            continue
        if declared.effects.tail.proves_lacks(instance):
            return False
    if actual.effects.tail is None:
        return True
    forbidden = set(declared.effects.tail.lacks) - set(declared_entries)
    if any(instance in actual_entries for instance in forbidden):
        return False
    if any(not actual.effects.tail.proves_lacks(instance) for instance in forbidden):
        return False
    return True

inferred_computation_type

inferred_computation_type(module: QiecModule, registry: KernelRegistry, name: str) -> ComputationType

The type a named computation's body has, as the checker infers it.

PARAMETER DESCRIPTION
module

The module, already validated.

TYPE: QiecModule

registry

The registry validate_module resolved for it.

TYPE: KernelRegistry

name

The computation's name.

TYPE: str

RETURNS DESCRIPTION
ComputationType

The inferred result type and the row the body performs, which the declared row may widen.

RAISES DESCRIPTION
KeyError

If the module has no computation of that name.

Source code in src/quivers/qiec/module.py
def inferred_computation_type(
    module: QiecModule, registry: KernelRegistry, name: str
) -> ComputationType:
    """The type a named computation's body has, as the checker infers it.

    Parameters
    ----------
    module : QiecModule
        The module, already validated.
    registry : KernelRegistry
        The registry `validate_module` resolved for it.
    name : str
        The computation's name.

    Returns
    -------
    ComputationType
        The inferred result type and the row the body performs, which the
        declared row may widen.

    Raises
    ------
    KeyError
        If the module has no computation of that name.
    """
    computation = next(
        (item for item in module.computations if item.name == name), None
    )
    if computation is None:
        raise KeyError(f"module {module.module!r} has no computation {name!r}")
    context = CheckContext()
    for parameter in computation.parameters:
        context = context.extend(parameter)
    return infer_computation(computation.body, registry, context)

program_computations

program_computations(module: QiecModule) -> frozenset[ComputationId]

The computations that hold programs and their own helpers.

PARAMETER DESCRIPTION
module

The module.

TYPE: QiecModule

RETURNS DESCRIPTION
frozenset[ComputationId]

The identity of every entry point's computation and of every computation the elaboration made for a program, such as a marginalization helper or a scan's recurrence: what a target renders from the program's plan rather than as a computation of its own.

Source code in src/quivers/qiec/module.py
def program_computations(module: QiecModule) -> frozenset[ComputationId]:
    """The computations that hold programs and their own helpers.

    Parameters
    ----------
    module : QiecModule
        The module.

    Returns
    -------
    frozenset[ComputationId]
        The identity of every entry point's computation and of every
        computation the elaboration made for a program, such as a
        marginalization helper or a scan's recurrence: what a target
        renders from the program's plan rather than as a computation
        of its own.
    """
    entries = {entry.computation for entry in module.entries}
    return frozenset(
        computation.id
        for computation in module.computations
        if computation.id in entries
        or computation.origin.structural_path[:1] == ("programs",)
    )

reachable_computations

reachable_computations(module: QiecModule) -> frozenset[ComputationId]

The computations a program reaches.

PARAMETER DESCRIPTION
module

The module.

TYPE: QiecModule

RETURNS DESCRIPTION
frozenset[ComputationId]

The identity of every entry point's computation and of every computation it calls, transitively.

Source code in src/quivers/qiec/module.py
def reachable_computations(module: QiecModule) -> frozenset[ComputationId]:
    """The computations a program reaches.

    Parameters
    ----------
    module : QiecModule
        The module.

    Returns
    -------
    frozenset[ComputationId]
        The identity of every entry point's computation and of every
        computation it calls, transitively.
    """
    by_id = {computation.id: computation for computation in module.computations}
    found: set[ComputationId] = set()
    pending = [entry.computation for entry in module.entries]
    while pending:
        identity = pending.pop()
        if identity in found or identity not in by_id:
            continue
        found.add(identity)
        pending.extend(called_computations(by_id[identity].body))
    return frozenset(found)

render_computation_type

render_computation_type(type_: ComputationType, names: Mapping[EffectInstanceId, str] | None = None) -> str

Render a computation type as its result and row.

PARAMETER DESCRIPTION
type_

The type.

TYPE: ComputationType

names

The source name of each lexical instance, for the row.

TYPE: Mapping[EffectInstanceId, str] | None DEFAULT: None

RETURNS DESCRIPTION
str

Result !{row}.

Source code in src/quivers/qiec/module.py
def render_computation_type(
    type_: ComputationType, names: Mapping[EffectInstanceId, str] | None = None
) -> str:
    """Render a computation type as its result and row.

    Parameters
    ----------
    type_ : ComputationType
        The type.
    names : Mapping[EffectInstanceId, str] | None
        The source name of each lexical instance, for the row.

    Returns
    -------
    str
        ``Result !{row}``.
    """
    return f"{render_static(type_.result)} {render_row(type_.effects, names)}"

validate_module

validate_module(module: QiecModule) -> KernelRegistry

Recheck a whole lowered module and return its resolved registry.

This is deliberately independent of the lowerer. It rebuilds the registry from the module's own declarations and rechecks every body against it, so a module that arrived from elsewhere, or from a lowerer with a bug, is held to the same standard as one just built.

PARAMETER DESCRIPTION
module

The lowered module to recheck.

TYPE: QiecModule

RETURNS DESCRIPTION
KernelRegistry

The registry resolved from the module's declarations, for a caller that wants to check further terms against it.

RAISES DESCRIPTION
KernelError

If the ABI does not match, a declaration is ill-formed, an instance or index sort is undeclared, a body fails to check, or an inferred type does not conform to the declared one.

Source code in src/quivers/qiec/module.py
def validate_module(module: QiecModule) -> KernelRegistry:
    """Recheck a whole lowered module and return its resolved registry.

    This is deliberately independent of the lowerer. It rebuilds the
    registry from the module's own declarations and rechecks every body
    against it, so a module that arrived from elsewhere, or from a
    lowerer with a bug, is held to the same standard as one just built.

    Parameters
    ----------
    module : QiecModule
        The lowered module to recheck.

    Returns
    -------
    KernelRegistry
        The registry resolved from the module's declarations, for a
        caller that wants to check further terms against it.

    Raises
    ------
    KernelError
        If the ABI does not match, a declaration is ill-formed, an
        instance or index sort is undeclared, a body fails to check, or
        an inferred type does not conform to the declared one.
    """

    if module.abi != QIEC_ABI:
        raise KernelError(f"unsupported QIEC ABI {module.abi!r}")
    _validate_index_sorts(module)
    registry = KernelRegistry()
    for family in module.families:
        registry.register_family(family)
    for constructor in module.constructors:
        registry.register_constructor(constructor)
    for effect in module.effects:
        registry.register_effect(effect)
    for instance in module.instances:
        _validate_closed_effect_instance(instance)
        registry.validate_static(instance.entry.effect)
        declared = registry.effects.get(instance.entry.effect.id)
        if declared is None or not declared.matches(instance.entry.effect):
            raise KernelError(
                f"unknown effect interface for instance {instance.name!r}"
            )
        registry.instance_names[instance.entry.instance] = instance.name
    # Every computation signature is registered before any body is
    # rechecked, for the same reason the lowerer collects them first: a
    # body may call a computation declared after it, and two may call
    # each other.
    for computation in module.computations:
        registry.register_computation(
            ComputationSignature(
                computation.id,
                computation.name,
                computation.telescope,
                tuple(parameter.type for parameter in computation.parameters),
                computation.type.result,
                computation.type.effects,
            )
        )
    for handler in module.handlers:
        registry.register_handler(handler)
    instance_interfaces = {
        instance.entry.instance: instance.entry.effect for instance in module.instances
    }
    for handler in module.handlers:
        _validate_effect_instances(
            handler.introduced,
            instance_interfaces,
            subject=f"handler {handler.name!r}",
            module=module.module,
            source_protocol=module.source_protocol,
        )
    for computation in module.computations:
        _validate_named_static_scope(computation, computation.telescope)
        registry.validate_computation_type(computation.type)
        _validate_effect_instances(
            computation.type.effects,
            instance_interfaces,
            subject=f"declared row of computation {computation.name!r}",
            module=module.module,
            source_protocol=module.source_protocol,
        )
        _validate_effect_instances(
            computation.body,
            instance_interfaces,
            subject=f"body of computation {computation.name!r}",
            module=module.module,
            source_protocol=module.source_protocol,
        )
        context = CheckContext()
        for parameter in computation.parameters:
            registry.validate_type(parameter.type)
            context = context.extend(parameter)
        actual = infer_computation(computation.body, registry, context)
        if not computation_type_conforms(actual, computation.type):
            raise KernelError(
                f"computation {computation.name!r} has type "
                f"{render_computation_type(actual, registry.instance_names)}, "
                f"not declared type "
                f"{render_computation_type(computation.type, registry.instance_names)}"
            )
    return registry

primitive

primitive(name: str) -> PrimitiveSignature

Look up a primitive by name.

PARAMETER DESCRIPTION
name

The nominal name.

TYPE: str

RETURNS DESCRIPTION
PrimitiveSignature

The primitive's signature.

RAISES DESCRIPTION
KeyError

If no primitive has the name. Callers turn this into a source-located diagnostic; the registry itself has no location to blame.

Source code in src/quivers/qiec/primitives.py
def primitive(name: str) -> PrimitiveSignature:
    """Look up a primitive by name.

    Parameters
    ----------
    name : str
        The nominal name.

    Returns
    -------
    PrimitiveSignature
        The primitive's signature.

    Raises
    ------
    KeyError
        If no primitive has the name. Callers turn this into a
        source-located diagnostic; the registry itself has no location to
        blame.
    """
    try:
        return PRIMITIVES[name]
    except KeyError as error:
        raise KeyError(f"unknown QIEC primitive {name!r}") from error

deduction_item

deduction_item(module: QiecModule, name: str, symbol: str, *fields: object) -> RuntimeConstructor

Build an item of a deduction's family as a host value.

PARAMETER DESCRIPTION
module

The module holding the deduction.

TYPE: QiecModule

name

The deduction's name.

TYPE: str

symbol

The atom or constructor symbol, as the deduction spells it.

TYPE: str

*fields

The constructor's fields: integers for position slots, items for the rest.

TYPE: object DEFAULT: ()

RETURNS DESCRIPTION
RuntimeConstructor

The item, which run_deduction takes among the axioms of a deduction without a lexicon.

RAISES DESCRIPTION
KeyError

If the deduction declares no such symbol.

ValueError

If the field count differs from the constructor's arity.

Source code in src/quivers/qiec/program_runtime.py
def deduction_item(
    module: QiecModule, name: str, symbol: str, *fields: object
) -> RuntimeConstructor:
    """Build an item of a deduction's family as a host value.

    Parameters
    ----------
    module : QiecModule
        The module holding the deduction.
    name : str
        The deduction's name.
    symbol : str
        The atom or constructor symbol, as the deduction spells it.
    *fields : object
        The constructor's fields: integers for position slots, items for
        the rest.

    Returns
    -------
    RuntimeConstructor
        The item, which ``run_deduction`` takes among the axioms of a
        deduction without a lexicon.

    Raises
    ------
    KeyError
        If the deduction declares no such symbol.
    ValueError
        If the field count differs from the constructor's arity.
    """
    constructor_name = f"{name}__{symbol}"
    constructor = next(
        (item for item in module.constructors if item.name == constructor_name), None
    )
    if constructor is None:
        raise KeyError(f"deduction {name!r} declares no symbol {symbol!r}")
    if len(fields) != len(constructor.fields):
        raise ValueError(
            f"{symbol!r} of deduction {name!r} takes {len(constructor.fields)} "
            f"field(s), not {len(fields)}"
        )
    family = next(item for item in module.families if item.id == constructor.family)
    return RuntimeConstructor(
        constructor.id, (), tuple(fields), TypeApplication(family.type_constructor, ())
    )

foreign_handler_configuration

foreign_handler_configuration(module: QiecModule) -> dict[str, object]

The runtime configuration of a module's elaborated foreign handlers.

PARAMETER DESCRIPTION
module

The module.

TYPE: QiecModule

RETURNS DESCRIPTION
dict[str, object]

By handler name: the enumeration handlers of marginalization blocks, the collecting handlers of those blocks and of deductions, and the search handlers of deductions, each with the options its name records.

Source code in src/quivers/qiec/program_runtime.py
def foreign_handler_configuration(module: QiecModule) -> dict[str, object]:
    """The runtime configuration of a module's elaborated foreign handlers.

    Parameters
    ----------
    module : QiecModule
        The module.

    Returns
    -------
    dict[str, object]
        By handler name: the enumeration handlers of marginalization
        blocks, the collecting handlers of those blocks and of
        deductions, and the search handlers of deductions, each with
        the options its name records.
    """
    configured: dict[str, object] = {}
    for handler in module.handlers:
        if handler.implementation != "foreign":
            continue
        marginal = _marginal_handler(handler.name)
        deduction = _deduction_handler(handler.name)
        if handler.effect.name == "Random" and marginal is not None:
            grouped, reduction = marginal
            configured[handler.name] = {
                "kind": "enumerate",
                "grouped": grouped,
                "reduction": reduction,
            }
        elif handler.effect.name == "Weight" and handler.name.endswith(
            "collect_marginal"
        ):
            configured[handler.name] = {"kind": "collect"}
        elif deduction is not None:
            kind, semiring = deduction
            if kind == "search":
                configured[handler.name] = {
                    "kind": "search",
                    "reduction": SEARCH_REDUCTION_OF[semiring],
                }
            else:
                configured[handler.name] = {
                    "kind": "collect",
                    "combine": COLLECT_COMBINE_OF[semiring],
                    "forkable": True,
                }
    return configured

joint_module

joint_module(module: QiecModule, name: str) -> tuple[QiecModule, str]

Extend a module with a computation scoring one program's log joint.

PARAMETER DESCRIPTION
module

The module holding the program.

TYPE: QiecModule

name

The program's name.

TYPE: str

RETURNS DESCRIPTION
tuple[QiecModule, str]

The extended module and the wrapper computation's name. The wrapper takes the program's parameters, handles its score instance with an accumulating handler and its random instance with a replaying one, and returns the program's value paired with the accumulated log weight.

RAISES DESCRIPTION
KeyError

If the module has no such program.

Source code in src/quivers/qiec/program_runtime.py
def joint_module(module: QiecModule, name: str) -> tuple[QiecModule, str]:
    """Extend a module with a computation scoring one program's log joint.

    Parameters
    ----------
    module : QiecModule
        The module holding the program.
    name : str
        The program's name.

    Returns
    -------
    tuple[QiecModule, str]
        The extended module and the wrapper computation's name. The
        wrapper takes the program's parameters, handles its ``score``
        instance with an accumulating handler and its ``random``
        instance with a replaying one, and returns the program's value
        paired with the accumulated log weight.

    Raises
    ------
    KeyError
        If the module has no such program.
    """
    return _wrapped_module(module, name, sampling=False)

program_entry

program_entry(module: QiecModule, name: str) -> ProgramEntry

Find a program's entry point in a module.

PARAMETER DESCRIPTION
module

The module.

TYPE: QiecModule

name

The program's name.

TYPE: str

RETURNS DESCRIPTION
ProgramEntry

The entry.

RAISES DESCRIPTION
KeyError

If the module has no program of that name.

Source code in src/quivers/qiec/program_runtime.py
def program_entry(module: QiecModule, name: str) -> ProgramEntry:
    """Find a program's entry point in a module.

    Parameters
    ----------
    module : QiecModule
        The module.
    name : str
        The program's name.

    Returns
    -------
    ProgramEntry
        The entry.

    Raises
    ------
    KeyError
        If the module has no program of that name.
    """
    for entry in module.entries:
        if entry.name == name:
            return entry
    raise KeyError(f"module {module.module!r} has no program {name!r}")

run_deduction

run_deduction(module: QiecModule, name: str, *, tokens: Sequence[str] | None = None, axioms: Sequence[object] | None = None, axiom_weights: Sequence[object] | None = None, parameters: Mapping[str, object] | None = None, fuel: int | None = None) -> DeductionRun

Run a deduction's entry on the reference machine.

PARAMETER DESCRIPTION
module

The module holding the deduction.

TYPE: QiecModule

name

The deduction's name.

TYPE: str

tokens

The sentence, for a deduction with a lexicon.

TYPE: Sequence[str] | None DEFAULT: None

axioms

The axiom items, for a deduction without one; runtime constructor values of its item family.

TYPE: Sequence[object] | None DEFAULT: None

axiom_weights

One weight per axiom in the semiring's carrier.

TYPE: Sequence[object] | None DEFAULT: None

parameters

The learned weights by name; an absent weight reads as zero.

TYPE: Mapping[str, object] | None DEFAULT: None

fuel

A step budget for the run.

TYPE: int | None DEFAULT: None

RETURNS DESCRIPTION
DeductionRun

The goal's inside weight.

RAISES DESCRIPTION
KeyError

If the module has no such deduction.

ValueError

If the input does not fit the deduction: tokens for one that takes axioms, or the reverse.

ExecutionFailure

If the run fails.

Source code in src/quivers/qiec/program_runtime.py
def run_deduction(
    module: QiecModule,
    name: str,
    *,
    tokens: Sequence[str] | None = None,
    axioms: Sequence[object] | None = None,
    axiom_weights: Sequence[object] | None = None,
    parameters: Mapping[str, object] | None = None,
    fuel: int | None = None,
) -> DeductionRun:
    """Run a deduction's entry on the reference machine.

    Parameters
    ----------
    module : QiecModule
        The module holding the deduction.
    name : str
        The deduction's name.
    tokens : Sequence[str] | None
        The sentence, for a deduction with a lexicon.
    axioms : Sequence[object] | None
        The axiom items, for a deduction without one; runtime
        constructor values of its item family.
    axiom_weights : Sequence[object] | None
        One weight per axiom in the semiring's carrier.
    parameters : Mapping[str, object] | None
        The learned weights by name; an absent weight reads as zero.
    fuel : int | None
        A step budget for the run.

    Returns
    -------
    DeductionRun
        The goal's inside weight.

    Raises
    ------
    KeyError
        If the module has no such deduction.
    ValueError
        If the input does not fit the deduction: tokens for one that
        takes axioms, or the reverse.
    ExecutionFailure
        If the run fails.
    """
    entry_name = f"{name}__derive"
    if not any(item.name == entry_name for item in module.computations):
        raise KeyError(f"module {module.module!r} has no deduction {name!r}")
    computation = next(
        item for item in module.computations if item.name == f"{name}__run"
    )
    takes_tokens = len(computation.parameters) == 1
    if takes_tokens and (tokens is None or axioms is not None):
        raise ValueError(f"deduction {name!r} takes a sentence of tokens")
    if not takes_tokens and (
        axioms is None or axiom_weights is None or tokens is not None
    ):
        raise ValueError(f"deduction {name!r} takes its axioms and their weights")
    extended, wrapper_name = _deduction_wrapper(module, computation)
    if takes_tokens:
        assert tokens is not None
        arguments: tuple[object, ...] = (tuple(tokens),)
        extent = len(tokens)
    else:
        assert axioms is not None and axiom_weights is not None
        arguments = (tuple(axioms), tuple(axiom_weights))
        extent = len(axioms)
    binder = computation.telescope[0]
    assert isinstance(binder, IndexBinder)
    configured: dict[str, object] = {}
    if wrapper_name != computation.name:
        configured[PARAMS_HANDLER] = {"kind": "param", "values": dict(parameters or {})}
    configured.update(foreign_handler_configuration(module))
    result = run_named(
        extended,
        wrapper_name,
        arguments,
        static_arguments=(IndexLiteral(extent, binder.sort),),  # type: ignore[arg-type]
        runtime=RuntimeConfiguration(
            (RuntimeSelection("core", {"handlers": configured}),)
        ),
        fuel=fuel,
    )
    return DeductionRun(result.value, result)

run_program

run_program(module: QiecModule, name: str, *, data: Mapping[str, object], sites: Mapping[str, object], parameters: Mapping[str, object] | None = None, static_arguments: Sequence[StaticArgument] = (), fuel: int | None = None, observer: TraceObserver | None = None, providers: tuple[RuntimeProvider, ...] = ()) -> ProgramRun

Run a program with every site replayed and score its log joint.

PARAMETER DESCRIPTION
module

The module holding the program.

TYPE: QiecModule

name

The program's name.

TYPE: str

data

A host value for each of the program's parameters, by name: its data, observations, fibrations, and map numbers.

TYPE: Mapping[str, object]

sites

A host value for each sampled site, by label, or by label and occurrence as "<label>@<n>" for a site a recurrence reaches at every position. Every site the program reaches must be given, and every given site reached.

TYPE: Mapping[str, object]

parameters

The learned weights a deduction the program calls reads, by name; an absent weight reads as zero.

TYPE: Mapping[str, object] | None DEFAULT: None

static_arguments

The program's static extents, one per telescope binder, for an extent no data fixes (a template's object parameter); empty when the data fix every extent.

TYPE: Sequence[StaticArgument] DEFAULT: ()

fuel

A step budget for the run.

TYPE: int | None DEFAULT: None

observer

A callback receiving every trace event as it is emitted.

TYPE: TraceObserver | None DEFAULT: None

providers

Additional typed providers used by computations the program calls.

TYPE: tuple[RuntimeProvider, ...] DEFAULT: ()

RETURNS DESCRIPTION
ProgramRun

The returned value and the log joint.

RAISES DESCRIPTION
KeyError

If the module has no such program, a parameter is not given, or an open extent cannot be read off the data.

ExecutionFailure

If the run fails, a site is missing, or a value does not inhabit its type.

Source code in src/quivers/qiec/program_runtime.py
def run_program(
    module: QiecModule,
    name: str,
    *,
    data: Mapping[str, object],
    sites: Mapping[str, object],
    parameters: Mapping[str, object] | None = None,
    static_arguments: Sequence[StaticArgument] = (),
    fuel: int | None = None,
    observer: TraceObserver | None = None,
    providers: tuple[RuntimeProvider, ...] = (),
) -> ProgramRun:
    """Run a program with every site replayed and score its log joint.

    Parameters
    ----------
    module : QiecModule
        The module holding the program.
    name : str
        The program's name.
    data : Mapping[str, object]
        A host value for each of the program's parameters, by name:
        its data, observations, fibrations, and map numbers.
    sites : Mapping[str, object]
        A host value for each sampled site, by label, or by label and
        occurrence as ``"<label>@<n>"`` for a site a recurrence reaches
        at every position. Every site the program reaches must be
        given, and every given site reached.
    parameters : Mapping[str, object] | None
        The learned weights a deduction the program calls reads, by
        name; an absent weight reads as zero.
    static_arguments : Sequence[StaticArgument]
        The program's static extents, one per telescope binder, for an
        extent no data fixes (a template's object parameter); empty
        when the data fix every extent.
    fuel : int | None
        A step budget for the run.
    observer : TraceObserver | None
        A callback receiving every trace event as it is emitted.
    providers : tuple[RuntimeProvider, ...]
        Additional typed providers used by computations the program calls.

    Returns
    -------
    ProgramRun
        The returned value and the log joint.

    Raises
    ------
    KeyError
        If the module has no such program, a parameter is not given, or
        an open extent cannot be read off the data.
    ExecutionFailure
        If the run fails, a site is missing, or a value does not inhabit
        its type.
    """
    entry = program_entry(module, name)
    extended, wrapper_name = joint_module(module, name)
    configured: dict[str, object] = {
        REPLAY_HANDLER: {
            "kind": "replay",
            "values": dict(sites),
            "score_instance": entry.score_instance,
        },
        ACCUMULATE_HANDLER: {"kind": "score"},
    }
    return _run_wrapped(
        module,
        extended,
        wrapper_name,
        entry,
        configured,
        data=data,
        parameters=parameters,
        static_arguments=static_arguments,
        fuel=fuel,
        observer=observer,
        providers=providers,
    )

sample_module

sample_module(module: QiecModule, name: str) -> tuple[QiecModule, str]

Extend a module with a computation running one program forward.

PARAMETER DESCRIPTION
module

The module holding the program.

TYPE: QiecModule

name

The program's name.

TYPE: str

RETURNS DESCRIPTION
tuple[QiecModule, str]

The extended module and the wrapper computation's name. The wrapper takes the program's parameters, handles its random instance with a conditioning replay of the given sites inside a scoring draw of every other site, its score instance with an accumulating handler, and returns the program's value paired with the accumulated log weight: the log joint of the draws it made and the sites it was given.

RAISES DESCRIPTION
KeyError

If the module has no such program.

Source code in src/quivers/qiec/program_runtime.py
def sample_module(module: QiecModule, name: str) -> tuple[QiecModule, str]:
    """Extend a module with a computation running one program forward.

    Parameters
    ----------
    module : QiecModule
        The module holding the program.
    name : str
        The program's name.

    Returns
    -------
    tuple[QiecModule, str]
        The extended module and the wrapper computation's name. The
        wrapper takes the program's parameters, handles its ``random``
        instance with a conditioning replay of the given sites inside a
        scoring draw of every other site, its ``score`` instance with an
        accumulating handler, and returns the program's value paired
        with the accumulated log weight: the log joint of the draws it
        made and the sites it was given.

    Raises
    ------
    KeyError
        If the module has no such program.
    """
    return _wrapped_module(module, name, sampling=True)

sample_program

sample_program(module: QiecModule, name: str, *, data: Mapping[str, object], sites: Mapping[str, object] | None = None, parameters: Mapping[str, object] | None = None, static_arguments: Sequence[StaticArgument] = (), fuel: int | None = None, observer: TraceObserver | None = None, providers: tuple[RuntimeProvider, ...] = ()) -> ProgramRun

Run a program forward, drawing every site it is not given.

PARAMETER DESCRIPTION
module

The module holding the program.

TYPE: QiecModule

name

The program's name.

TYPE: str

data

A host value for each of the program's parameters, by name: its data, observations, fibrations, and map numbers.

TYPE: Mapping[str, object]

sites

A host value for each conditioned site, by label, or by label and occurrence as "<label>@<n>"; every other site is drawn from its distribution. A given site the program never reaches is an error.

TYPE: Mapping[str, object] | None DEFAULT: None

parameters

The learned weights a deduction the program calls reads, by name; an absent weight reads as zero.

TYPE: Mapping[str, object] | None DEFAULT: None

static_arguments

The program's static extents, one per telescope binder, for an extent no data fixes; empty when the data fix every extent.

TYPE: Sequence[StaticArgument] DEFAULT: ()

fuel

A step budget for the run.

TYPE: int | None DEFAULT: None

observer

A callback receiving every trace event as it is emitted.

TYPE: TraceObserver | None DEFAULT: None

providers

Additional typed providers used by computations the program calls.

TYPE: tuple[RuntimeProvider, ...] DEFAULT: ()

RETURNS DESCRIPTION
ProgramRun

The returned value and the log joint of the drawn and given sites together with the program's observations and scores.

RAISES DESCRIPTION
KeyError

If the module has no such program, a parameter is not given, or an open extent cannot be read off the data.

ExecutionFailure

If the run fails or a value does not inhabit its type.

Source code in src/quivers/qiec/program_runtime.py
def sample_program(
    module: QiecModule,
    name: str,
    *,
    data: Mapping[str, object],
    sites: Mapping[str, object] | None = None,
    parameters: Mapping[str, object] | None = None,
    static_arguments: Sequence[StaticArgument] = (),
    fuel: int | None = None,
    observer: TraceObserver | None = None,
    providers: tuple[RuntimeProvider, ...] = (),
) -> ProgramRun:
    """Run a program forward, drawing every site it is not given.

    Parameters
    ----------
    module : QiecModule
        The module holding the program.
    name : str
        The program's name.
    data : Mapping[str, object]
        A host value for each of the program's parameters, by name:
        its data, observations, fibrations, and map numbers.
    sites : Mapping[str, object] | None
        A host value for each conditioned site, by label, or by label
        and occurrence as ``"<label>@<n>"``; every other site is drawn
        from its distribution. A given site the program never reaches
        is an error.
    parameters : Mapping[str, object] | None
        The learned weights a deduction the program calls reads, by
        name; an absent weight reads as zero.
    static_arguments : Sequence[StaticArgument]
        The program's static extents, one per telescope binder, for an
        extent no data fixes; empty when the data fix every extent.
    fuel : int | None
        A step budget for the run.
    observer : TraceObserver | None
        A callback receiving every trace event as it is emitted.
    providers : tuple[RuntimeProvider, ...]
        Additional typed providers used by computations the program calls.

    Returns
    -------
    ProgramRun
        The returned value and the log joint of the drawn and given
        sites together with the program's observations and scores.

    Raises
    ------
    KeyError
        If the module has no such program, a parameter is not given, or
        an open extent cannot be read off the data.
    ExecutionFailure
        If the run fails or a value does not inhabit its type.
    """
    entry = program_entry(module, name)
    extended, wrapper_name = sample_module(module, name)
    configured: dict[str, object] = {
        CONDITION_HANDLER: {
            "kind": "replay",
            "values": dict(sites or {}),
            "score_instance": entry.score_instance,
        },
        DRAW_HANDLER: {"kind": "draw-scoring", "score_instance": entry.score_instance},
        ACCUMULATE_HANDLER: {"kind": "score"},
    }
    return _run_wrapped(
        module,
        extended,
        wrapper_name,
        entry,
        configured,
        data=data,
        parameters=parameters,
        static_arguments=static_arguments,
        fuel=fuel,
        observer=observer,
        providers=providers,
    )

static_arguments_for

static_arguments_for(entry: ProgramEntry, data: Mapping[str, object], given: Sequence[StaticArgument] = ()) -> tuple[StaticArgument, ...]

Read a program's static extents off the data it is applied to.

PARAMETER DESCRIPTION
entry

The program's entry point, whose telescope names the extents.

TYPE: ProgramEntry

data

The host value of each parameter, by name.

TYPE: Mapping[str, object]

given

Static arguments supplied explicitly, one per telescope binder in telescope order, or none. A template's object parameter is an extent no data fixes, so a run of the template supplies it; a binder the data does fix must agree with what is supplied.

TYPE: Sequence[StaticArgument] DEFAULT: ()

RETURNS DESCRIPTION
tuple[StaticArgument, ...]

One index literal per telescope binder, in telescope order: the length of the axis of the first parameter whose tensor type is shaped by the binder, else the supplied argument.

RAISES DESCRIPTION
KeyError

If a binder shapes no parameter and none is supplied, or the parameter is not given or has too few axes, or a supplied argument disagrees with the data, or the supplied arguments do not number the telescope.

Source code in src/quivers/qiec/program_runtime.py
def static_arguments_for(
    entry: ProgramEntry,
    data: Mapping[str, object],
    given: Sequence[StaticArgument] = (),
) -> tuple[StaticArgument, ...]:
    """Read a program's static extents off the data it is applied to.

    Parameters
    ----------
    entry : ProgramEntry
        The program's entry point, whose telescope names the extents.
    data : Mapping[str, object]
        The host value of each parameter, by name.
    given : Sequence[StaticArgument]
        Static arguments supplied explicitly, one per telescope binder
        in telescope order, or none. A template's object parameter is
        an extent no data fixes, so a run of the template supplies it;
        a binder the data does fix must agree with what is supplied.

    Returns
    -------
    tuple[StaticArgument, ...]
        One index literal per telescope binder, in telescope order: the
        length of the axis of the first parameter whose tensor type is
        shaped by the binder, else the supplied argument.

    Raises
    ------
    KeyError
        If a binder shapes no parameter and none is supplied, or the
        parameter is not given or has too few axes, or a supplied
        argument disagrees with the data, or the supplied arguments do
        not number the telescope.
    """
    if given and len(given) != len(entry.telescope):
        raise KeyError(
            f"program {entry.name!r} binds {len(entry.telescope)} extent(s); "
            f"{len(given)} static argument(s) were given"
        )
    arguments: list[StaticArgument] = []
    for position, binder in enumerate(entry.telescope):
        supplied = given[position] if given else None
        found: int | None = None
        for parameter in entry.parameters:
            type_ = parameter.type
            if not isinstance(type_, TypeApplication) or len(type_.arguments) != 2:
                continue
            shape = type_.arguments[1]
            if not isinstance(shape, ShapeIndex):
                continue
            for depth, dimension in enumerate(shape.dimensions):
                if (
                    isinstance(dimension, IndexVariable)
                    and dimension.name == binder.name
                ):
                    try:
                        found = _extent_of(data[parameter.name], depth)
                    except KeyError as error:
                        raise KeyError(
                            f"parameter {parameter.name!r} of program "
                            f"{entry.name!r} does not fix its extent "
                            f"{binder.name!r}: {error.args[0]}"
                        ) from error
                    break
            if found is not None:
                break
        if found is None:
            if supplied is None:
                raise KeyError(
                    f"program {entry.name!r} has an extent {binder.name!r} that "
                    "no parameter fixes; supply it as a static argument"
                )
            arguments.append(supplied)
            continue
        literal = IndexLiteral(found, binder.sort)  # type: ignore[arg-type]
        if supplied is not None and supplied != literal:
            raise KeyError(
                f"static argument {binder.name!r} of program {entry.name!r} is "
                f"given as {render_static(supplied)}, but its data fix it at "
                f"{found}"
            )
        arguments.append(literal)
    return tuple(arguments)

dumps

dumps(value: object) -> str

Serialize a QIEC graph as deterministic UTF-8-compatible JSON text.

PARAMETER DESCRIPTION
value

The graph to serialize.

TYPE: object

RETURNS DESCRIPTION
str

Compact, key-sorted JSON. Determinism matters because identities downstream are content addressed: the same graph must produce the same bytes on every run and every platform.

RAISES DESCRIPTION
SerializationError

If the graph contains a value that may not cross the boundary.

Source code in src/quivers/qiec/serialization.py
def dumps(value: object) -> str:
    """Serialize a QIEC graph as deterministic UTF-8-compatible JSON text.

    Parameters
    ----------
    value : object
        The graph to serialize.

    Returns
    -------
    str
        Compact, key-sorted JSON. Determinism matters because identities
        downstream are content addressed: the same graph must produce the
        same bytes on every run and every platform.

    Raises
    ------
    SerializationError
        If the graph contains a value that may not cross the boundary.
    """
    return json.dumps(
        to_data(value),
        ensure_ascii=False,
        allow_nan=False,
        separators=(",", ":"),
        sort_keys=True,
    )

from_data

from_data(data: object) -> object

Decode a QIEC graph, rejecting other ABIs and unknown node tags.

PARAMETER DESCRIPTION
data

The decoded envelope.

TYPE: object

RETURNS DESCRIPTION
object

The reconstructed graph.

RAISES DESCRIPTION
SerializationError

If the envelope is malformed, declares another wire format or ABI, or holds a graph that fails to decode. An ABI mismatch is refused rather than attempted, since a payload from another version may use the same tags for different shapes.

Source code in src/quivers/qiec/serialization.py
def from_data(data: object) -> object:
    """Decode a QIEC graph, rejecting other ABIs and unknown node tags.

    Parameters
    ----------
    data : object
        The decoded envelope.

    Returns
    -------
    object
        The reconstructed graph.

    Raises
    ------
    SerializationError
        If the envelope is malformed, declares another wire format or
        ABI, or holds a graph that fails to decode. An ABI mismatch is
        refused rather than attempted, since a payload from another
        version may use the same tags for different shapes.
    """
    if not isinstance(data, dict) or any(not isinstance(key, str) for key in data):
        raise SerializationError("QIEC wire envelope must be an object")
    typed = cast(dict[str, JsonValue], data)
    _exact_keys(typed, {"$schema", "abi", "root"}, "QIEC wire envelope")
    if typed["$schema"] != QIEC_WIRE_FORMAT:
        raise SerializationError(f"unsupported QIEC wire format {typed['$schema']!r}")
    if typed["abi"] != QIEC_ABI:
        raise SerializationError(
            f"QIEC ABI mismatch: expected {QIEC_ABI!r}, got {typed['abi']!r}"
        )
    return _decode(typed["root"])

loads

loads(text: str | bytes | bytearray) -> object

Deserialize one canonical QIEC JSON document.

PARAMETER DESCRIPTION
text

The document to read.

TYPE: str or bytes or bytearray

RETURNS DESCRIPTION
object

The reconstructed graph.

RAISES DESCRIPTION
SerializationError

If the text is not valid JSON, repeats an object key at any depth, or fails any of the decoding checks.

Source code in src/quivers/qiec/serialization.py
def loads(text: str | bytes | bytearray) -> object:
    """Deserialize one canonical QIEC JSON document.

    Parameters
    ----------
    text : str or bytes or bytearray
        The document to read.

    Returns
    -------
    object
        The reconstructed graph.

    Raises
    ------
    SerializationError
        If the text is not valid JSON, repeats an object key at any
        depth, or fails any of the decoding checks.
    """
    try:
        data = json.loads(text, object_pairs_hook=_unique_object)
    except (json.JSONDecodeError, UnicodeDecodeError) as error:
        raise SerializationError(f"invalid QIEC JSON: {error}") from error
    return from_data(data)

to_data

to_data(value: object) -> dict[str, JsonValue]

Encode a stable QIEC graph in its versioned wire envelope.

PARAMETER DESCRIPTION
value

The graph to encode.

TYPE: object

RETURNS DESCRIPTION
dict[str, JsonValue]

The envelope, carrying the wire format and the ABI alongside the graph. Both are recorded because neither can be guessed from the payload, and a reader that guessed wrong would misread it.

RAISES DESCRIPTION
SerializationError

If the graph contains a value that may not cross the boundary.

Source code in src/quivers/qiec/serialization.py
def to_data(value: object) -> dict[str, JsonValue]:
    """Encode a stable QIEC graph in its versioned wire envelope.

    Parameters
    ----------
    value : object
        The graph to encode.

    Returns
    -------
    dict[str, JsonValue]
        The envelope, carrying the wire format and the ABI alongside the
        graph. Both are recorded because neither can be guessed from the
        payload, and a reader that guessed wrong would misread it.

    Raises
    ------
    SerializationError
        If the graph contains a value that may not cross the boundary.
    """
    return {
        "$schema": QIEC_WIRE_FORMAT,
        "abi": QIEC_ABI,
        "root": _encode(value),
    }

instantiate_operation

instantiate_operation(operation: OperationDef, arguments: tuple[StaticArgument, ...], outer: StaticSubstitution = StaticSubstitution()) -> tuple[tuple[TypeExpr, ...], TypeExpr]

Instantiate an operation's signature at a request site.

PARAMETER DESCRIPTION
operation

The declared operation.

TYPE: OperationDef

arguments

Static arguments for the operation's own telescope.

TYPE: tuple[StaticArgument, ...]

outer

Bindings already in force from the interface's telescope. These come first, so an operation binder of the same name shadows the interface's, which is the scoping a reader expects.

TYPE: StaticSubstitution DEFAULT: StaticSubstitution()

RETURNS DESCRIPTION
tuple[tuple[TypeExpr, ...], TypeExpr]

The instantiated parameter types and result type.

RAISES DESCRIPTION
TypeError

If an argument is of the wrong static class for its binder.

ValueError

If the argument count is wrong, or an argument is ill-kinded.

Source code in src/quivers/qiec/substitution.py
def instantiate_operation(
    operation: OperationDef,
    arguments: tuple[StaticArgument, ...],
    outer: StaticSubstitution = StaticSubstitution(),
) -> tuple[tuple[TypeExpr, ...], TypeExpr]:
    """Instantiate an operation's signature at a request site.

    Parameters
    ----------
    operation : OperationDef
        The declared operation.
    arguments : tuple[StaticArgument, ...]
        Static arguments for the operation's own telescope.
    outer : StaticSubstitution
        Bindings already in force from the interface's telescope. These
        come first, so an operation binder of the same name shadows the
        interface's, which is the scoping a reader expects.

    Returns
    -------
    tuple[tuple[TypeExpr, ...], TypeExpr]
        The instantiated parameter types and result type.

    Raises
    ------
    TypeError
        If an argument is of the wrong static class for its binder.
    ValueError
        If the argument count is wrong, or an argument is ill-kinded.
    """
    local = instantiate_telescope(operation.telescope, arguments)
    substitution = StaticSubstitution(
        (*outer.types, *local.types),
        (*outer.indices, *local.indices),
        (*outer.effects, *local.effects),
    )
    parameter_types = tuple(
        substitute_type(argument.type, substitution) for argument in operation.arguments
    )
    return parameter_types, substitute_type(operation.result_type, substitution)

instantiate_telescope

instantiate_telescope(telescope: Telescope, arguments: tuple[StaticArgument, ...]) -> StaticSubstitution

Kind-check static arguments and construct their substitution.

PARAMETER DESCRIPTION
telescope

The binders to instantiate, in order. Later binders may depend on earlier ones, so the arguments are checked left to right with the substitution built so far already applied.

TYPE: Telescope

arguments

One argument per binder.

TYPE: tuple[StaticArgument, ...]

RETURNS DESCRIPTION
StaticSubstitution

The substitution carrying each binder to its argument.

RAISES DESCRIPTION
TypeError

If an argument is of the wrong static class for its binder.

ValueError

If the counts differ, or an argument is of the wrong kind or index sort.

Source code in src/quivers/qiec/substitution.py
def instantiate_telescope(
    telescope: Telescope,
    arguments: tuple[StaticArgument, ...],
) -> StaticSubstitution:
    """Kind-check static arguments and construct their substitution.

    Parameters
    ----------
    telescope : Telescope
        The binders to instantiate, in order. Later binders may depend on
        earlier ones, so the arguments are checked left to right with the
        substitution built so far already applied.
    arguments : tuple[StaticArgument, ...]
        One argument per binder.

    Returns
    -------
    StaticSubstitution
        The substitution carrying each binder to its argument.

    Raises
    ------
    TypeError
        If an argument is of the wrong static class for its binder.
    ValueError
        If the counts differ, or an argument is of the wrong kind or
        index sort.
    """
    types, indices, effects = check_static_arguments(telescope, arguments)
    return StaticSubstitution(types, indices, effects)

substitute_computation

substitute_computation(computation: Computation, substitution: StaticSubstitution) -> Computation

Substitute static arguments throughout a computation.

Capture cannot occur, and the reason is structural rather than a freshening pass. A substitution binds declaration binders, which carry no identity and are matched by name. Every binder a computation introduces is rigid: a case branch's skolems are built with derived StaticVariableIds, and substitute_type and substitute_static return a variable carrying an identity unchanged. So a branch's own binders are invisible to substitution while references to an enclosing declaration binder inside that branch are rewritten, which is exactly the capture-avoiding behavior.

PARAMETER DESCRIPTION
computation

The computation to rewrite.

TYPE: Computation

substitution

The static bindings to apply.

TYPE: StaticSubstitution

RETURNS DESCRIPTION
Computation

The computation with every static position substituted. Identities are preserved throughout: a call still names the same callee, a handle the same handler, and an allocation the same instance.

RAISES DESCRIPTION
TypeError

If the computation, or a term nested in it, is of an unknown class.

Source code in src/quivers/qiec/substitution.py
def substitute_computation(
    computation: Computation,
    substitution: StaticSubstitution,
) -> Computation:
    """Substitute static arguments throughout a computation.

    Capture cannot occur, and the reason is structural rather than a
    freshening pass. A substitution binds declaration binders, which
    carry no identity and are matched by name. Every binder a computation
    introduces is rigid: a case branch's skolems are built with derived
    `StaticVariableId`s, and `substitute_type` and `substitute_static`
    return a variable carrying an identity unchanged. So a branch's own
    binders are invisible to substitution while references to an
    enclosing declaration binder inside that branch are rewritten, which
    is exactly the capture-avoiding behavior.

    Parameters
    ----------
    computation : Computation
        The computation to rewrite.
    substitution : StaticSubstitution
        The static bindings to apply.

    Returns
    -------
    Computation
        The computation with every static position substituted.
        Identities are preserved throughout: a call still names the same
        callee, a handle the same handler, and an allocation the same
        instance.

    Raises
    ------
    TypeError
        If the computation, or a term nested in it, is of an unknown
        class.
    """
    if isinstance(computation, Return):
        return Return(substitute_value(computation.value, substitution))
    if isinstance(computation, Bind):
        return Bind(
            Local(
                computation.binder.name,
                substitute_type(computation.binder.type, substitution),
            ),
            substitute_computation(computation.first, substitution),
            substitute_computation(computation.then, substitution),
        )
    if isinstance(computation, Perform):
        return Perform(substitute_request(computation.request, substitution))
    if isinstance(computation, Handle):
        return Handle(
            computation.instance,
            computation.handler,
            substitute_computation(computation.computation, substitution),
            tuple(
                substitute_static(argument, substitution)
                for argument in computation.static_arguments
            ),
        )
    if isinstance(computation, Case):
        return Case(
            substitute_value(computation.scrutinee, substitution),
            CaseMotive(
                computation.motive.indices,
                substitute_type(computation.motive.result_type, substitution),
            ),
            tuple(
                CaseBranch(
                    branch.constructor,
                    tuple(
                        substitute_static(argument, substitution)
                        for argument in branch.static_arguments
                    ),
                    tuple(
                        Local(field.name, substitute_type(field.type, substitution))
                        for field in branch.fields
                    ),
                    substitute_computation(branch.body, substitution),
                    branch.scope,
                )
                for branch in computation.branches
            ),
        )
    if isinstance(computation, If):
        return If(
            substitute_value(computation.condition, substitution),
            substitute_computation(computation.then, substitution),
            substitute_computation(computation.otherwise, substitution),
        )
    if isinstance(computation, Call):
        return Call(
            computation.callee,
            computation.name,
            tuple(
                substitute_static(argument, substitution)
                for argument in computation.static_arguments
            ),
            tuple(
                substitute_value(argument, substitution)
                for argument in computation.arguments
            ),
            substitute_type(computation.result_type, substitution),
            substitute_row(computation.effects, substitution),
            computation.origin,
        )
    if isinstance(computation, Resume):
        return Resume(
            substitute_value(computation.value, substitution),
            computation.origin,
        )
    if isinstance(computation, NewInstance):
        return NewInstance(
            computation.instance,
            substitute_effect(computation.effect, substitution),
            substitute_computation(computation.body, substitution),
            computation.origin,
        )
    raise TypeError(f"unknown computation term {computation!r}")

substitute_effect

substitute_effect(effect: EffectRef, substitution: StaticSubstitution) -> EffectRef

Substitute into an interface application's static arguments.

PARAMETER DESCRIPTION
effect

The application to rewrite.

TYPE: EffectRef

substitution

The bindings to apply.

TYPE: StaticSubstitution

RETURNS DESCRIPTION
EffectRef

The application with its arguments rewritten. The declaration's identity and name are unchanged, since substitution instantiates an interface rather than selecting a different one.

Source code in src/quivers/qiec/substitution.py
def substitute_effect(effect: EffectRef, substitution: StaticSubstitution) -> EffectRef:
    """Substitute into an interface application's static arguments.

    Parameters
    ----------
    effect : EffectRef
        The application to rewrite.
    substitution : StaticSubstitution
        The bindings to apply.

    Returns
    -------
    EffectRef
        The application with its arguments rewritten. The declaration's
        identity and name are unchanged, since substitution instantiates
        an interface rather than selecting a different one.
    """
    return EffectRef(
        effect.id,
        effect.name,
        tuple(substitute_static(arg, substitution) for arg in effect.arguments),
    )

substitute_evidence

substitute_evidence(evidence: EqualityEvidence, substitution: StaticSubstitution) -> EqualityEvidence

Substitute into the equality an evidence term states.

PARAMETER DESCRIPTION
evidence

The evidence to rewrite.

TYPE: EqualityEvidence

substitution

The static bindings to apply.

TYPE: StaticSubstitution

RETURNS DESCRIPTION
EqualityEvidence

Evidence of the same class stating the substituted equality. A branch given keeps its identity, since substituting into what it proves does not make it a different piece of evidence.

RAISES DESCRIPTION
TypeError

If the evidence is of an unknown class.

Source code in src/quivers/qiec/substitution.py
def substitute_evidence(
    evidence: EqualityEvidence,
    substitution: StaticSubstitution,
) -> EqualityEvidence:
    """Substitute into the equality an evidence term states.

    Parameters
    ----------
    evidence : EqualityEvidence
        The evidence to rewrite.
    substitution : StaticSubstitution
        The static bindings to apply.

    Returns
    -------
    EqualityEvidence
        Evidence of the same class stating the substituted equality. A
        branch given keeps its identity, since substituting into what it
        proves does not make it a different piece of evidence.

    Raises
    ------
    TypeError
        If the evidence is of an unknown class.
    """
    equality = EqualityType(
        evidence.equality.kind,
        substitute_static(evidence.equality.left, substitution),
        substitute_static(evidence.equality.right, substitution),
    )
    if isinstance(evidence, Reflexivity):
        return Reflexivity(equality)
    if isinstance(evidence, BranchGiven):
        return BranchGiven(evidence.id, equality)
    raise TypeError(f"unknown equality evidence {evidence!r}")

substitute_index

substitute_index(term: IndexTerm, substitution: StaticSubstitution) -> IndexTerm

Substitute into an index term.

PARAMETER DESCRIPTION
term

The index to rewrite.

TYPE: IndexTerm

substitution

The bindings to apply.

TYPE: StaticSubstitution

RETURNS DESCRIPTION
IndexTerm

The rewritten index. A variable carrying an identity is rigid and is returned unchanged, which is what keeps a branch's skolem from being captured by a like-named declaration binder.

Source code in src/quivers/qiec/substitution.py
def substitute_index(term: IndexTerm, substitution: StaticSubstitution) -> IndexTerm:
    """Substitute into an index term.

    Parameters
    ----------
    term : IndexTerm
        The index to rewrite.
    substitution : StaticSubstitution
        The bindings to apply.

    Returns
    -------
    IndexTerm
        The rewritten index. A variable carrying an identity is rigid and
        is returned unchanged, which is what keeps a branch's skolem from
        being captured by a like-named declaration binder.
    """
    if isinstance(term, IndexVariable) and term.identity is None:
        return substitution.index(term.name) or term
    if isinstance(term, IndexConstructor):
        return IndexConstructor(
            term.name,
            tuple(substitute_index(arg, substitution) for arg in term.arguments),
            term.sort,
        )
    if isinstance(term, ShapeIndex):
        return ShapeIndex(
            tuple(
                substitute_index(dimension, substitution)
                for dimension in term.dimensions
            )
        )
    return term

substitute_plate

substitute_plate(plate: PlateShape, substitution: StaticSubstitution) -> PlateShape

Substitute static variables in a plate's axis sizes.

PARAMETER DESCRIPTION
plate

The plate.

TYPE: PlateShape

substitution

The static substitution.

TYPE: StaticSubstitution

RETURNS DESCRIPTION
PlateShape

The plate with every axis size substituted.

Source code in src/quivers/qiec/substitution.py
def substitute_plate(plate: PlateShape, substitution: StaticSubstitution) -> PlateShape:
    """Substitute static variables in a plate's axis sizes.

    Parameters
    ----------
    plate : PlateShape
        The plate.
    substitution : StaticSubstitution
        The static substitution.

    Returns
    -------
    PlateShape
        The plate with every axis size substituted.
    """
    return PlateShape(
        tuple(
            PlateAxis(axis.name, substitute_index(axis.size, substitution))
            for axis in plate.batch
        ),
        tuple(
            PlateAxis(axis.name, substitute_index(axis.size, substitution))
            for axis in plate.event
        ),
    )

substitute_request

substitute_request(request: EffectRequest, substitution: StaticSubstitution) -> EffectRequest

Substitute static arguments throughout an effect request.

PARAMETER DESCRIPTION
request

The request to rewrite.

TYPE: EffectRequest

substitution

The static bindings to apply.

TYPE: StaticSubstitution

RETURNS DESCRIPTION
EffectRequest

The request against the substituted interface application, with its own static and value arguments and result type rewritten. The instance, the operation, and the site are identities and do not move.

Source code in src/quivers/qiec/substitution.py
def substitute_request(
    request: EffectRequest,
    substitution: StaticSubstitution,
) -> EffectRequest:
    """Substitute static arguments throughout an effect request.

    Parameters
    ----------
    request : EffectRequest
        The request to rewrite.
    substitution : StaticSubstitution
        The static bindings to apply.

    Returns
    -------
    EffectRequest
        The request against the substituted interface application, with
        its own static and value arguments and result type rewritten. The
        instance, the operation, and the site are identities and do not
        move.
    """
    return EffectRequest(
        request.instance,
        substitute_effect(request.effect, substitution),
        request.operation,
        tuple(
            substitute_static(argument, substitution)
            for argument in request.static_arguments
        ),
        tuple(
            substitute_value(argument, substitution) for argument in request.arguments
        ),
        substitute_type(request.result_type, substitution),
        request.origin,
    )

substitute_row

substitute_row(row: EffectRow, substitution: StaticSubstitution) -> EffectRow

Substitute into every interface application in an effect row.

PARAMETER DESCRIPTION
row

The row to rewrite.

TYPE: EffectRow

substitution

The bindings to apply.

TYPE: StaticSubstitution

RETURNS DESCRIPTION
EffectRow

The row with each entry's interface rewritten. Instance identities and the open tail are untouched: this substitutes static arguments, and a row variable is solved by unification rather than by this substitution.

Source code in src/quivers/qiec/substitution.py
def substitute_row(row: EffectRow, substitution: StaticSubstitution) -> EffectRow:
    """Substitute into every interface application in an effect row.

    Parameters
    ----------
    row : EffectRow
        The row to rewrite.
    substitution : StaticSubstitution
        The bindings to apply.

    Returns
    -------
    EffectRow
        The row with each entry's interface rewritten. Instance
        identities and the open tail are untouched: this substitutes
        static arguments, and a row variable is solved by unification
        rather than by this substitution.
    """
    return EffectRow(
        tuple(
            RowEntry(entry.instance, substitute_effect(entry.effect, substitution))
            for entry in row.entries
        ),
        row.tail,
    )

substitute_static

substitute_static(term: StaticArgument, substitution: StaticSubstitution) -> StaticArgument

Substitute into a static argument of any namespace.

PARAMETER DESCRIPTION
term

A type, index, or effect argument.

TYPE: StaticArgument

substitution

The bindings to apply.

TYPE: StaticSubstitution

RETURNS DESCRIPTION
StaticArgument

The rewritten argument, dispatched to the namespace it belongs to.

RAISES DESCRIPTION
TypeError

If the term is of an unknown class.

Source code in src/quivers/qiec/substitution.py
def substitute_static(
    term: StaticArgument,
    substitution: StaticSubstitution,
) -> StaticArgument:
    """Substitute into a static argument of any namespace.

    Parameters
    ----------
    term : StaticArgument
        A type, index, or effect argument.
    substitution : StaticSubstitution
        The bindings to apply.

    Returns
    -------
    StaticArgument
        The rewritten argument, dispatched to the namespace it belongs
        to.

    Raises
    ------
    TypeError
        If the term is of an unknown class.
    """
    if isinstance(term, EffectVariable) and term.identity is None:
        return substitution.effect(term.name) or term
    if isinstance(term, EffectVariable):
        return term
    if isinstance(term, EffectRef):
        return substitute_effect(term, substitution)
    if isinstance(term, (IndexVariable, IndexConstructor, ShapeIndex)) or hasattr(
        term, "sort"
    ):
        return substitute_index(term, substitution)  # type: ignore[arg-type]
    return substitute_type(term, substitution)  # type: ignore[arg-type]

substitute_type

substitute_type(type_: TypeExpr, substitution: StaticSubstitution) -> TypeExpr

Substitute into a type expression.

PARAMETER DESCRIPTION
type_

The type to rewrite.

TYPE: TypeExpr

substitution

The bindings to apply.

TYPE: StaticSubstitution

RETURNS DESCRIPTION
TypeExpr

The rewritten type. As with indices, a variable carrying an identity is rigid and survives unchanged.

RAISES DESCRIPTION
TypeError

If the expression is of an unknown class.

Source code in src/quivers/qiec/substitution.py
def substitute_type(type_: TypeExpr, substitution: StaticSubstitution) -> TypeExpr:
    """Substitute into a type expression.

    Parameters
    ----------
    type_ : TypeExpr
        The type to rewrite.
    substitution : StaticSubstitution
        The bindings to apply.

    Returns
    -------
    TypeExpr
        The rewritten type. As with indices, a variable carrying an
        identity is rigid and survives unchanged.

    Raises
    ------
    TypeError
        If the expression is of an unknown class.
    """
    if isinstance(type_, TypeVariable) and type_.identity is None:
        return substitution.type(type_.name) or type_
    if isinstance(type_, TypeVariable):
        return type_
    if isinstance(type_, TypeApplication):
        return TypeApplication(
            type_.constructor,
            tuple(substitute_static(arg, substitution) for arg in type_.arguments),
        )
    if isinstance(type_, FunctionType):
        return FunctionType(
            substitute_type(type_.parameter, substitution),
            substitute_type(type_.result, substitution),
        )
    if isinstance(type_, EqualityType):
        return EqualityType(
            type_.kind,
            substitute_static(type_.left, substitution),
            substitute_static(type_.right, substitution),
        )
    raise TypeError(f"unknown type term {type_!r}")

substitute_value

substitute_value(value: Value, substitution: StaticSubstitution) -> Value

Substitute static arguments throughout a value term.

PARAMETER DESCRIPTION
value

The value to rewrite.

TYPE: Value

substitution

The static bindings to apply.

TYPE: StaticSubstitution

RETURNS DESCRIPTION
Value

The value with every type, index, and effect position substituted. Value-level structure is untouched: this rewrites the static layer, not the data.

RAISES DESCRIPTION
TypeError

If the value is of an unknown class.

Source code in src/quivers/qiec/substitution.py
def substitute_value(value: Value, substitution: StaticSubstitution) -> Value:
    """Substitute static arguments throughout a value term.

    Parameters
    ----------
    value : Value
        The value to rewrite.
    substitution : StaticSubstitution
        The static bindings to apply.

    Returns
    -------
    Value
        The value with every type, index, and effect position
        substituted. Value-level structure is untouched: this rewrites
        the static layer, not the data.

    Raises
    ------
    TypeError
        If the value is of an unknown class.
    """
    if isinstance(value, Var):
        return Var(
            Local(value.local.name, substitute_type(value.local.type, substitution))
        )
    if isinstance(value, LiteralValue):
        return LiteralValue(value.value, substitute_type(value.type, substitution))
    if isinstance(value, ConstructorValue):
        return ConstructorValue(
            value.constructor,
            tuple(
                substitute_static(argument, substitution)
                for argument in value.static_arguments
            ),
            tuple(substitute_value(field, substitution) for field in value.fields),
            substitute_type(value.result_type, substitution),
        )
    if isinstance(value, EvidenceValue):
        return EvidenceValue(substitute_evidence(value.evidence, substitution))
    if isinstance(value, AttachmentRef):
        return AttachmentRef(
            value.attachment, substitute_type(value.type, substitution)
        )
    if isinstance(value, TransportValue):
        return TransportValue(
            substitute_evidence(value.evidence, substitution),
            substitute_value(value.value, substitution),
            substitute_type(value.target_type, substitution),
        )
    if isinstance(value, PrimitiveApplication):
        return PrimitiveApplication(
            value.primitive,
            value.name,
            tuple(substitute_value(item, substitution) for item in value.arguments),
            substitute_type(value.result_type, substitution),
            value.origin,
        )
    if isinstance(value, TupleValue):
        return TupleValue(
            tuple(substitute_value(item, substitution) for item in value.items),
            substitute_type(value.result_type, substitution),
        )
    if isinstance(value, TensorValue):
        return TensorValue(
            tuple(substitute_value(item, substitution) for item in value.items),
            substitute_type(value.result_type, substitution),
        )
    if isinstance(value, Projection):
        return Projection(
            substitute_value(value.value, substitution),
            value.position,
            substitute_type(value.result_type, substitution),
        )
    if isinstance(value, DistributionValue):
        return DistributionValue(
            value.family,
            value.name,
            tuple(
                (name, substitute_value(argument, substitution))
                for name, argument in value.arguments
            ),
            substitute_type(value.result_type, substitution),
            value.origin,
            substitute_plate(value.plate, substitution),
        )
    if isinstance(value, Gather):
        return Gather(
            substitute_value(value.value, substitution),
            substitute_value(value.index, substitution),
            substitute_type(value.result_type, substitution),
        )
    if isinstance(value, WeightSum):
        return WeightSum(substitute_value(value.value, substitution))
    if isinstance(value, SegmentSum):
        return SegmentSum(
            substitute_value(value.value, substitution),
            substitute_value(value.index, substitution),
            substitute_index(value.groups, substitution),
            substitute_type(value.result_type, substitution),
        )
    if isinstance(value, KernelMatrix):
        return KernelMatrix(
            substitute_value(value.inputs, substitution),
            value.kernel,
            value.length_scale,
            value.jitter,
            substitute_type(value.result_type, substitution),
        )
    if isinstance(value, AffineMap):
        return AffineMap(
            substitute_value(value.weight, substitution),
            substitute_value(value.bias, substitution),
            tuple(substitute_value(item, substitution) for item in value.sources),
            value.row_offset,
            value.rows,
            value.transform,
            substitute_type(value.result_type, substitution),
        )
    if isinstance(value, TableMap):
        return TableMap(
            substitute_value(value.table, substitution),
            substitute_value(value.index, substitution),
            value.row_offset,
            value.rows,
            value.transform,
            substitute_type(value.result_type, substitution),
        )
    if isinstance(value, Reduction):
        return Reduction(
            value.operator,
            substitute_value(value.value, substitution),
            substitute_type(value.result_type, substitution),
        )
    if isinstance(value, Rowwise):
        return Rowwise(
            value.operator,
            substitute_value(value.value, substitution),
            substitute_type(value.result_type, substitution),
        )
    if isinstance(value, Comprehension):
        return Comprehension(
            Local(value.binder.name, substitute_type(value.binder.type, substitution)),
            substitute_index(value.extent, substitution),
            substitute_value(value.body, substitution),
            substitute_type(value.result_type, substitution),
        )
    if isinstance(value, LogDensity):
        return LogDensity(
            substitute_value(value.sampleable, substitution),
            substitute_value(value.value, substitution),
            value.origin,
            tuple(
                PlateAxis(axis.name, substitute_index(axis.size, substitution))
                for axis in value.batch
            ),
        )
    if isinstance(value, SiteValue):
        return SiteValue(value.label, substitute_type(value.result_type, substitution))
    raise TypeError(f"unknown value term {value!r}")

check_static_arguments

check_static_arguments(telescope: Telescope, arguments: tuple[StaticArgument, ...]) -> tuple[tuple[tuple[str, TypeExpr], ...], tuple[tuple[str, IndexTerm], ...], tuple[tuple[str, EffectRef | EffectVariable], ...]]

Kind-check static arguments against a telescope and classify them.

PARAMETER DESCRIPTION
telescope

The binders the arguments instantiate, in order.

TYPE: Telescope

arguments

One argument per binder.

TYPE: tuple[StaticArgument, ...]

RETURNS DESCRIPTION
tuple[tuple[tuple[str, TypeExpr], ...], tuple[tuple[str, IndexTerm], ...], tuple[tuple[str, EffectRef | EffectVariable], ...]]

The type, index, and effect bindings the arguments make, each paired with its binder's name, in telescope order.

RAISES DESCRIPTION
TypeError

If the counts differ, an argument is of the wrong static class for its binder, or an argument is of the wrong kind or index sort.

ValueError

If a nested application is ill-kinded, or an index constructor is applied at the wrong arity.

Source code in src/quivers/qiec/types.py
def check_static_arguments(
    telescope: Telescope,
    arguments: tuple[StaticArgument, ...],
) -> tuple[
    tuple[tuple[str, TypeExpr], ...],
    tuple[tuple[str, IndexTerm], ...],
    tuple[tuple[str, EffectRef | EffectVariable], ...],
]:
    """Kind-check static arguments against a telescope and classify them.

    Parameters
    ----------
    telescope : Telescope
        The binders the arguments instantiate, in order.
    arguments : tuple[StaticArgument, ...]
        One argument per binder.

    Returns
    -------
    tuple[tuple[tuple[str, TypeExpr], ...], tuple[tuple[str, IndexTerm], ...], tuple[tuple[str, EffectRef | EffectVariable], ...]]
        The type, index, and effect bindings the arguments make, each
        paired with its binder's name, in telescope order.

    Raises
    ------
    TypeError
        If the counts differ, an argument is of the wrong static class
        for its binder, or an argument is of the wrong kind or index
        sort.
    ValueError
        If a nested application is ill-kinded, or an index constructor is
        applied at the wrong arity.
    """
    if len(telescope) != len(arguments):
        raise TypeError(
            f"expected {len(telescope)} static arguments, got {len(arguments)}"
        )

    types: list[tuple[str, TypeExpr]] = []
    indices: list[tuple[str, IndexTerm]] = []
    effects: list[tuple[str, EffectRef | EffectVariable]] = []
    for binder, argument in zip(telescope, arguments, strict=True):
        validate_static_argument(argument)
        if isinstance(binder, TypeBinder):
            if not isinstance(
                argument,
                (TypeVariable, TypeApplication, FunctionType, EqualityType),
            ):
                raise TypeError(f"{binder.name!r} expects a type argument")
            if static_kind(argument) != binder.kind:
                raise TypeError(f"type argument for {binder.name!r} has wrong kind")
            types.append((binder.name, argument))
        elif isinstance(binder, IndexBinder):
            if not isinstance(
                argument,
                (IndexVariable, IndexConstructor, ShapeIndex),
            ) and not hasattr(argument, "sort"):
                raise TypeError(f"{binder.name!r} expects an index argument")
            actual_sort = index_sort(argument)  # type: ignore[arg-type]
            if not _sort_matches(binder.sort, actual_sort):
                raise TypeError(
                    f"index argument for {binder.name!r} has sort {actual_sort!r}, "
                    f"expected {binder.sort!r}"
                )
            indices.append((binder.name, argument))  # type: ignore[arg-type]
        elif isinstance(binder, EffectBinder):
            if not isinstance(argument, (EffectRef, EffectVariable)):
                raise TypeError(f"{binder.name!r} expects an effect argument")
            effects.append((binder.name, argument))
        else:  # pragma: no cover - closed binder union
            raise TypeError(f"unknown telescope binder {binder!r}")
    return tuple(types), tuple(indices), tuple(effects)

index_sort

index_sort(term: IndexTerm) -> IndexSort

Return an index term's sort, checking shape dimensions.

PARAMETER DESCRIPTION
term

The index whose sort is wanted.

TYPE: IndexTerm

RETURNS DESCRIPTION
IndexSort

The term's sort. A shape reports its concrete rank, which is what lets a rank-polymorphic binder accept it while a binder of a fixed rank does not.

RAISES DESCRIPTION
TypeError

If a shape dimension is not of Nat sort. A dimension is a count, so admitting anything else would let a shape be indexed by a value that cannot be one.

Source code in src/quivers/qiec/types.py
def index_sort(term: IndexTerm) -> IndexSort:
    """Return an index term's sort, checking shape dimensions.

    Parameters
    ----------
    term : IndexTerm
        The index whose sort is wanted.

    Returns
    -------
    IndexSort
        The term's sort. A shape reports its concrete rank, which is what
        lets a rank-polymorphic binder accept it while a binder of a
        fixed rank does not.

    Raises
    ------
    TypeError
        If a shape dimension is not of `Nat` sort. A dimension is a
        count, so admitting anything else would let a shape be indexed by
        a value that cannot be one.
    """

    if isinstance(term, ShapeIndex):
        for dimension in term.dimensions:
            if index_sort(dimension) != NAT:
                raise TypeError("shape dimensions must have Nat sort")
        return ShapeSort(rank=len(term.dimensions))
    return term.sort

product_type

product_type(*components: TypeExpr) -> TypeApplication

Construct the canonical finite-product type of the given arity.

PARAMETER DESCRIPTION
components

The component types, in order.

TYPE: TypeExpr DEFAULT: ()

RETURNS DESCRIPTION
TypeApplication

The product. Its constructor identity derives from the arity, so every two-component product in every module is the same type constructor and two products of different arity are different ones.

Source code in src/quivers/qiec/types.py
def product_type(*components: TypeExpr) -> TypeApplication:
    """Construct the canonical finite-product type of the given arity.

    Parameters
    ----------
    components : TypeExpr
        The component types, in order.

    Returns
    -------
    TypeApplication
        The product. Its constructor identity derives from the arity, so
        every two-component product in every module is the same type
        constructor and two products of different arity are different
        ones.
    """

    arity = len(components)
    constructor = TypeConstructorRef(
        TypeId.derive("builtin", "Product", arity),
        f"Product{arity}",
        tuple(TypeBinder(f"item{position}") for position in range(arity)),
    )
    return TypeApplication(constructor, components)

render_static

render_static(term: StaticArgument) -> str

Render a static term in the surface spelling.

PARAMETER DESCRIPTION
term

The type, index, or effect term to render.

TYPE: StaticArgument

RETURNS DESCRIPTION
str

A readable rendering: a constructor applied to its arguments in brackets, with shape arguments in parentheses after them; an arrow for a function type; an equality with ~; a shape as a bracketed dimension list; an index constructor as a call.

RAISES DESCRIPTION
TypeError

If the term is not a static term.

Source code in src/quivers/qiec/types.py
def render_static(term: StaticArgument) -> str:
    """Render a static term in the surface spelling.

    Parameters
    ----------
    term : StaticArgument
        The type, index, or effect term to render.

    Returns
    -------
    str
        A readable rendering: a constructor applied to its arguments in
        brackets, with shape arguments in parentheses after them; an
        arrow for a function type; an equality with ``~``; a shape as a
        bracketed dimension list; an index constructor as a call.

    Raises
    ------
    TypeError
        If the term is not a static term.
    """
    if isinstance(term, TypeVariable | EffectVariable | IndexVariable):
        return term.name
    if isinstance(term, IndexLiteral):
        return str(term.value)
    if isinstance(term, IndexConstructor):
        if not term.arguments:
            return term.name
        inner = ", ".join(render_static(argument) for argument in term.arguments)
        return f"{term.name}({inner})"
    if isinstance(term, ShapeIndex):
        inner = ", ".join(render_static(dimension) for dimension in term.dimensions)
        return f"[{inner}]"
    if isinstance(term, TypeApplication | EffectRef):
        name = term.constructor.name if isinstance(term, TypeApplication) else term.name
        statics = [
            render_static(argument)
            for argument in term.arguments
            if not isinstance(argument, ShapeIndex)
        ]
        shapes = [
            render_static(argument)
            for argument in term.arguments
            if isinstance(argument, ShapeIndex)
        ]
        rendered = name
        if statics:
            rendered += f"[{', '.join(statics)}]"
        if shapes:
            rendered += f"({', '.join(shapes)})"
        return rendered
    if isinstance(term, FunctionType):
        return f"{render_static(term.parameter)} -> {render_static(term.result)}"
    if isinstance(term, EqualityType):
        return f"{render_static(term.left)} ~ {render_static(term.right)}"
    raise TypeError(f"not a static term: {term!r}")

static_kind

static_kind(term: StaticArgument) -> Kind

Return the outer kind of a static term.

Index sorts are deliberately not collapsed into Type: callers that validate an index binder compare the term's concrete sort separately.

PARAMETER DESCRIPTION
term

The static term to classify.

TYPE: StaticArgument

RETURNS DESCRIPTION
Kind

The term's kind.

RAISES DESCRIPTION
TypeError

If the term is an index, which carries a sort rather than a kind. Raising rather than returning a placeholder is what keeps a caller from comparing an index against a kind and finding them equal.

Source code in src/quivers/qiec/types.py
def static_kind(term: StaticArgument) -> Kind:
    """Return the outer kind of a static term.

    Index sorts are deliberately not collapsed into ``Type``: callers that
    validate an index binder compare the term's concrete ``sort`` separately.

    Parameters
    ----------
    term : StaticArgument
        The static term to classify.

    Returns
    -------
    Kind
        The term's kind.

    Raises
    ------
    TypeError
        If the term is an index, which carries a sort rather than a kind.
        Raising rather than returning a placeholder is what keeps a
        caller from comparing an index against a kind and finding them
        equal.
    """

    if isinstance(term, (EffectRef, EffectVariable)):
        return EFFECT
    if isinstance(term, TypeVariable):
        return term.kind
    if isinstance(term, (TypeApplication, FunctionType, EqualityType)):
        return TYPE
    raise TypeError("index terms have a sort, not a QIEC kind")

validate_static_argument

validate_static_argument(term: StaticArgument) -> None

Recursively validate one intrinsically kinded static argument.

PARAMETER DESCRIPTION
term

The static term to validate, together with everything nested in it.

TYPE: StaticArgument

RAISES DESCRIPTION
TypeError

If a term is of a class that cannot appear in a static position.

ValueError

If an application is ill-kinded, or an index constructor is applied at the wrong arity.

Source code in src/quivers/qiec/types.py
def validate_static_argument(term: StaticArgument) -> None:
    """Recursively validate one intrinsically kinded static argument.

    Parameters
    ----------
    term : StaticArgument
        The static term to validate, together with everything nested in
        it.

    Raises
    ------
    TypeError
        If a term is of a class that cannot appear in a static position.
    ValueError
        If an application is ill-kinded, or an index constructor is
        applied at the wrong arity.
    """
    if isinstance(term, TypeVariable | EffectVariable | IndexVariable | IndexLiteral):
        return
    if isinstance(term, TypeApplication):
        check_static_arguments(term.constructor.telescope, term.arguments)
        return
    if isinstance(term, FunctionType):
        validate_static_argument(term.parameter)
        validate_static_argument(term.result)
        if static_kind(term.parameter) != TYPE:
            raise TypeError("function parameter must have Type kind")
        if static_kind(term.result) != TYPE:
            raise TypeError("function result must have Type kind")
        return
    if isinstance(term, EqualityType):
        validate_static_argument(term.left)
        validate_static_argument(term.right)
        index_kinds = (NatSort, ShapeSort, ContextSort, UserIndexSort)
        index_nodes = (IndexVariable, IndexLiteral, IndexConstructor, ShapeIndex)
        if isinstance(term.kind, index_kinds):
            if not isinstance(term.left, index_nodes) or not isinstance(
                term.right, index_nodes
            ):
                raise TypeError("index equality endpoints must be index terms")
            if not _sort_matches(term.kind, index_sort(term.left)) or not _sort_matches(
                term.kind, index_sort(term.right)
            ):
                raise TypeError("index equality endpoint has the wrong sort")
        elif (
            static_kind(term.left) != term.kind or static_kind(term.right) != term.kind
        ):
            raise TypeError("equality endpoint has the wrong kind")
        return
    if isinstance(term, EffectRef):
        for argument in term.arguments:
            validate_static_argument(argument)
        return
    if isinstance(term, IndexConstructor):
        for argument in term.arguments:
            validate_static_argument(argument)
        index_sort(term)
        return
    if isinstance(term, ShapeIndex):
        index_sort(term)
        for dimension in term.dimensions:
            validate_static_argument(dimension)
        return
    raise TypeError(f"unknown static argument {term!r}")

run_structural

run_structural(module: QiecModule, program: Program, name: str, arguments: Sequence[object], *, fuel: int | None = None) -> ExecutionResult

Run a structural computation through the compiled PyTorch attachments.

PARAMETER DESCRIPTION
module

The checked QIEC module.

TYPE: QiecModule

program

The classic compiled container supplying encoders, decoders, losses, and their learned tensors.

TYPE: Program

name

The structural computation name.

TYPE: str

arguments

Its host arguments.

TYPE: Sequence[object]

fuel

Optional reference-machine step budget.

TYPE: int | None DEFAULT: None

RETURNS DESCRIPTION
ExecutionResult

The typed result and trace. Torch tensor results retain their autograd graph back to the attached component parameters.

RAISES DESCRIPTION
KeyError

If name is not a structural computation.

Source code in src/quivers/qiec/structural_runtime.py
def run_structural(
    module: QiecModule,
    program: quivers.program.Program,
    name: str,
    arguments: Sequence[object],
    *,
    fuel: int | None = None,
) -> ExecutionResult:
    """Run a structural computation through the compiled PyTorch attachments.

    Parameters
    ----------
    module
        The checked QIEC module.
    program
        The classic compiled container supplying encoders, decoders, losses,
        and their learned tensors.
    name
        The structural computation name.
    arguments
        Its host arguments.
    fuel
        Optional reference-machine step budget.

    Returns
    -------
    ExecutionResult
        The typed result and trace. Torch tensor results retain their autograd
        graph back to the attached component parameters.

    Raises
    ------
    KeyError
        If ``name`` is not a structural computation.
    """

    computation = next(
        (
            item
            for item in module.computations
            if item.name == name and item.origin.structural_path[:1] == ("structural",)
        ),
        None,
    )
    if computation is None:
        raise KeyError(
            f"module {module.module!r} has no structural computation {name!r}"
        )
    return run_named(
        module,
        computation.name,
        tuple(arguments),
        runtime=structural_runtime_configuration(module, program),
        fuel=fuel,
    )

structural_handler_definition

structural_handler_definition(effect: EffectRef, result: TypeExpr, *, instance_name: str, computation_name: str) -> HandlerDef

Build the stable checked handler signature for one neural attachment.

PARAMETER DESCRIPTION
effect

The applied Compute interface served by the attachment.

TYPE: EffectRef

result

The closed result type returned by the host component.

TYPE: TypeExpr

instance_name

The source-derived effect-instance name.

TYPE: str

computation_name

The source-derived structural computation name.

TYPE: str

RETURNS DESCRIPTION
HandlerDef

A stable foreign handler declaration for the component.

Source code in src/quivers/qiec/structural_runtime.py
def structural_handler_definition(
    effect: EffectRef,
    result: TypeExpr,
    *,
    instance_name: str,
    computation_name: str,
) -> HandlerDef:
    """Build the stable checked handler signature for one neural attachment.

    Parameters
    ----------
    effect
        The applied ``Compute`` interface served by the attachment.
    result
        The closed result type returned by the host component.
    instance_name
        The source-derived effect-instance name.
    computation_name
        The source-derived structural computation name.

    Returns
    -------
    HandlerDef
        A stable foreign handler declaration for the component.
    """

    runtime = compute_handler(
        lambda argument, _context: argument,
        effect=effect,
        result_validator=lambda _value: True,
        answer_type=result,
        key=f"structural-{instance_name}-{computation_name}",
    )
    return replace(
        runtime.definition,
        name=f"__structural_{instance_name}_{computation_name}",
    )

structural_runtime_configuration

structural_runtime_configuration(module: QiecModule, program: Program) -> RuntimeConfiguration

Return the standard runtime configuration for structural entries.

PARAMETER DESCRIPTION
module

The checked QIEC module whose structural entries may be invoked.

TYPE: QiecModule

program

The compiled container supplying their PyTorch attachments.

TYPE: Program

RETURNS DESCRIPTION
RuntimeConfiguration

A configuration selecting the structural provider when the module has structural runtimes, or no providers otherwise.

Source code in src/quivers/qiec/structural_runtime.py
def structural_runtime_configuration(
    module: QiecModule,
    program: quivers.program.Program,
) -> RuntimeConfiguration:
    """Return the standard runtime configuration for structural entries.

    Parameters
    ----------
    module
        The checked QIEC module whose structural entries may be invoked.
    program
        The compiled container supplying their PyTorch attachments.

    Returns
    -------
    RuntimeConfiguration
        A configuration selecting the structural provider when the module has
        structural runtimes, or no providers otherwise.
    """

    provider = structural_runtime_provider(module, program)
    return RuntimeConfiguration(
        providers=(provider,) if provider.runtimes else (),
    )

structural_runtime_provider

structural_runtime_provider(module: QiecModule, program: Program) -> StructuralRuntimeProvider

Build the typed neural-attachment provider for a compiled module.

PARAMETER DESCRIPTION
module

The checked QIEC module whose structural instances need handlers.

TYPE: QiecModule

program

The compiled container supplying the corresponding PyTorch objects.

TYPE: Program

RETURNS DESCRIPTION
StructuralRuntimeProvider

A provider containing one validated runtime handler per structural instance.

RAISES DESCRIPTION
TypeError

If a structural component returns a type with no host validator.

Source code in src/quivers/qiec/structural_runtime.py
def structural_runtime_provider(
    module: QiecModule,
    program: quivers.program.Program,
) -> StructuralRuntimeProvider:
    """Build the typed neural-attachment provider for a compiled module.

    Parameters
    ----------
    module
        The checked QIEC module whose structural instances need handlers.
    program
        The compiled container supplying the corresponding PyTorch objects.

    Returns
    -------
    StructuralRuntimeProvider
        A provider containing one validated runtime handler per structural
        instance.

    Raises
    ------
    TypeError
        If a structural component returns a type with no host validator.
    """

    term_types = frozenset(
        family.type_constructor.id
        for family in module.families
        if family.name.endswith("__Term")
    )
    runtimes: list[RuntimeHandler] = []
    for instance in module.instances:
        if instance.origin.structural_path[:1] != ("structural",):
            continue
        result_type = instance.entry.effect.arguments[1]
        assert isinstance(result_type, TypeApplication)
        component_name = str(instance.origin.structural_path[2])
        validator = _validator_for(result_type, term_types)
        if validator is None:
            raise TypeError(
                f"structural component {component_name!r} has no validator for "
                f"{result_type!r}"
            )
        runtime = compute_handler(
            _component_function(program, instance),
            effect=instance.entry.effect,
            result_validator=validator,
            answer_type=result_type,
            key=f"structural-{instance.name}-{component_name}",
        )
        runtime.definition = structural_handler_definition(
            instance.entry.effect,
            result_type,
            instance_name=instance.name,
            computation_name=component_name,
        )
        runtimes.append(runtime)
    return StructuralRuntimeProvider(tuple(runtimes), term_types)