Skip to content

Entry points

The executable entry points of a checked module, and one invocation the command line, the REPL, and Python share.

entries

Entry points of a checked module and one way to invoke them.

A checked module has two kinds of executable entry point: a named computation declared with define, run by run_named under a runtime configuration, and a program, elaborated to a computation over the module's canonical random and score instances and run by sample_program with its data and any conditioned sites. The command line, the REPL, and a Python caller invoke either through invoke_entry, so an entry validates its arguments, selects its providers, traces, and fails with the same codes however it is reached.

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.

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

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)

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,
        )
    )

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,
    )

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

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

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