Skip to content

Implement BackendV3 abstraction #16111

@ihincks

Description

@ihincks

At a high level, one can see BackendV3 as a marriage of BackendV2 with the V2 primitives. Like BackendV2 (but unlike the V2 primitives), we want to tie constraints of a particular implementation/hardware provider to the API that executes. However, like the V2 primitives (but unlike BackendV2), we want a strong contract for the output types, and one that can be determined before execution. The abstractions described here are powerful enough to supersede both the estimator and the sampler, including their typical mitigation-heavy workflows.

The BackendV3 abstraction needs to be implemented in Rust with bindings to both C and Python. The Python bindings need to end up somehow equivalent to the snippet below. C of course does not have a formal notion of an abstract base class, and nor does Rust, so the C bindings will have to be something like a struct of raw function pointers.

class BackendV3(abc.ABC):
    @property
    @abc.abstractmethod
    def target(self) -> Target:
        """A target to specify constraints of any circuit contained in a submitted quantum program."""

    @property
    @abc.abstractmethod
    def supported_nodes(self) -> dict[str, set[str]]:
        """A map from runner names to glob patterns of quantum program nodes they support.

        Patterns are matched against the qualified name ``"{namespace}.{name}"`` of each node
        in the submitted program. Nodes whose qualified name matches a pattern for runner ``r``
        will be contracted into a subprogram and sent to that runner.

        Example::

            {"ibm_runtime": {"qiskit.circuit.*", "samplomatic.*"}}
        """

    @abc.abstractmethod
    def run(self, program: QuantumProgram) -> Job:
        """Validate, partition, and execute a quantum program.

        Raises if validation returns errors.
        """

    def validate_program(self, program: QuantumProgram) -> list[str]:
        """Return a list of validation error messages, or an empty list if valid.

        The default implementation runs :func:`trace` and returns its errors. Subclasses may
        extend this with hardware-specific checks.
        """

supported_nodes determines how the program is partitioned before execution. Nodes whose qualified name matches a glob pattern are grouped by graph connectivity and each connected component is contracted into a QuantumProgram subnode sent to the corresponding runner via _execute_remote. Nodes that don't match any pattern are executed locally via call(). This design allows a program to contain both quantum (remote) and classical post-processing (local) nodes in the same graph.

Metadata

Metadata

Assignees

Labels

mod: providersRelated to the backend and job abstractions

Type

No type

Projects

Status

Ready

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions