Skip to content

SPDX-License-Identifier: AGPL-3.0-or-later

Commercial license available

© Concepts 1996–2026 Miroslav Šotek. All rights reserved.

© Code 2020–2026 Miroslav Šotek. All rights reserved.

ORCID: 0009-0009-3560-0851

Contact: www.anulum.li | protoscience@anulum.li

scpn-quantum-control — Backends & Plugin Registry Documentation

Backends & Plugin Registry

Two modules for runtime backend management:

  1. Backend dispatch (backend_dispatch.py) — switch between numpy, JAX, and PyTorch for array operations
  2. Plugin registry (hardware/plugin_registry.py) — legacy runner registration for direct adapter construction
  3. Provider-neutral backend registry (hardware/backends.py) — production routing descriptors for IBM Runtime, local Aer, Cirq, Amazon Braket, PennyLane, analogue, and hybrid compiler paths

Part 1: Backend Dispatch

scpn_quantum_control.backend_dispatch

Runtime array backend selection, inspired by TensorCircuit's tc.set_backend(). All array operations in downstream code use the selected backend.

API Reference

from scpn_quantum_control.backend_dispatch import (
    set_backend,
    get_backend,
    get_array_module,
    to_numpy,
    from_numpy,
    available_backends,
)
Function Signature Description
set_backend(name) str → None Set active backend: "numpy", "jax", "torch"
get_backend() () → str Current backend name
get_array_module() () → module Active array module (np, jnp, or torch)
to_numpy(arr) Any → ndarray Convert any backend array to numpy
from_numpy(arr) ndarray → Any Convert numpy to current backend
available_backends() () → list[str] List installed backends

Example

from scpn_quantum_control.backend_dispatch import (
    set_backend, get_backend, available_backends,
    get_array_module, to_numpy, from_numpy
)
import numpy as np

# Check what's available
print(available_backends())  # ['numpy', 'jax', 'torch'] (if installed)

# Default is numpy
assert get_backend() == "numpy"

# Switch to JAX
set_backend("jax")
xp = get_array_module()  # jax.numpy
arr = from_numpy(np.array([1.0, 2.0, 3.0]))
print(type(arr))  # jaxlib.xla_extension.ArrayImpl

# Convert back
arr_np = to_numpy(arr)
print(type(arr_np))  # numpy.ndarray

# Switch to PyTorch
set_backend("torch")
xp = get_array_module()  # torch
arr_t = from_numpy(np.array([1.0, 2.0]))
print(type(arr_t))  # torch.Tensor

# Reset to numpy
set_backend("numpy")

Part 2: Plugin Registry

scpn_quantum_control.hardware.plugin_registry

Extensible plugin architecture for quantum hardware backends. Register and discover backends at runtime without hard-coding imports.

Inspired by OpenFermion's plugin system (Google Quantum AI).

Built-In Backends

The registry includes lazy loaders for three backends:

Backend Package Provides
qiskit qiskit Trotter circuits, IBM execution
pennylane pennylane Differentiable circuits
cirq cirq-core Google Quantum circuits

These are loaded on first access — no import cost if unused.

API Reference

from scpn_quantum_control.hardware.plugin_registry import registry

PluginRegistry Methods

Method Signature Description
list_backends() () → list[str] All registered + lazy-loadable names
available_backends() () → list[str] Only importable backends
is_available(name) str → bool Check if backend is installed
get_runner(name, K, omega, **kw) (str, ndarray, ndarray, ...) → Runner Get instantiated runner
register(name) str → decorator Decorator for custom backends
register_class(name, cls) (str, type) → None Programmatic registration

Runner Interface

Runners returned by get_runner implement:

class Runner:
    def __init__(self, K, omega, **kwargs): ...
    def run_trotter(self, t: float, reps: int) -> dict: ...
    def run_vqe(self, **kwargs) -> dict: ...  # optional

Example: Using Built-In Backends

from scpn_quantum_control.hardware.plugin_registry import registry
import numpy as np

n = 4
K = 0.45 * np.exp(-0.3 * np.abs(np.subtract.outer(range(n), range(n))))
np.fill_diagonal(K, 0.0)
omega = np.linspace(0.8, 1.2, n)

# List available backends
print(registry.available_backends())

# Use Qiskit backend
if registry.is_available("qiskit"):
    runner = registry.get_runner("qiskit", K, omega)
    result = runner.run_trotter(t=0.1, reps=5)
    print(f"Qiskit circuit depth: {result['depth']}")

Example: Custom Backend

from scpn_quantum_control.hardware.plugin_registry import registry

@registry.register("my_simulator")
class MySimulator:
    def __init__(self, K, omega, **kwargs):
        self.K = K
        self.omega = omega

    def run_trotter(self, t=0.1, reps=5):
        # Custom simulation logic
        return {"energy": -1.23, "method": "my_simulator"}

# Now usable via registry
runner = registry.get_runner("my_simulator", K, omega)
result = runner.run_trotter(t=0.1, reps=5)

Part 3: Provider-Neutral Quantum Backends

scpn_quantum_control.hardware.backends

The production registry exposes a single capability contract across the hardware and simulator surface. Registry lookup is deliberately non-authenticating and non-submitting: it imports at most the local SDK module needed for availability checks and never reads credentials, opens network sessions, or queues QPU jobs.

Built-In Production Descriptors

Backend Provider Execution mode Submission policy
qiskit_ibm IBM Quantum cloud QPU approval required
qiskit_aer local Qiskit Aer local simulator no submission
cirq Google Cirq local simulator/export no submission
braket Amazon Braket cloud QPU or managed simulator approval required
pennylane PennyLane adapter router provider plugin decides
analog_kuramoto internal compiler analogue programme compiler no registry-time submission
hybrid_digital_analog internal compiler hybrid compiler no registry-time submission

Cloud descriptors advertise that a submission interface exists, but they also set submit_requires_approval=True. Production execution code must pass through the explicit hardware approval scheduler before any live IBM or AWS work is attempted.

Descriptor API

from scpn_quantum_control.hardware import describe_backend, list_quantum_backends

ibm = describe_backend("qiskit_ibm")
assert ibm.provider == "ibm_quantum"
assert ibm.can_submit is True
assert ibm.submit_requires_approval is True

local = describe_backend("qiskit_aer")
assert local.can_simulate is True
assert local.can_submit is False

for descriptor in list_quantum_backends():
    print(descriptor.name, descriptor.execution_mode, descriptor.available)

Every descriptor records:

Field Meaning
name registry key used by routing code
provider provider namespace, e.g. ibm_quantum, local_qiskit_aer, aws_braket
execution_mode local simulator, cloud QPU, managed simulator, or adapter router
sdk_package Python package expected for the route
adapter_module repository module that owns execution or export
available import-time availability, without credentials or network calls
can_simulate / can_submit whether the descriptor exposes simulation or live submission semantics
submit_requires_approval mandatory cloud-job approval flag
supports_* shot, statevector, mid-circuit, and pulse capability flags
capabilities / workloads stable machine-readable routing tags

Legacy third-party plugins that only implement name and is_available() are still accepted. describe_backend() gives them a conservative descriptor with can_submit=False and submit_requires_approval=True until they implement a real descriptor() method returning QuantumBackendDescriptor.


Comparison

Feature Backend Dispatch Plugin Registry TensorCircuit
Array backend switching Yes No Yes
Hardware backend registry No Yes No
Custom backends No Yes (decorator) No
Lazy loading N/A Yes No
JAX support Yes Via backends Yes
PyTorch support Yes Via backends Yes

References

  1. Zhang, S.-X. et al. "TensorCircuit: An open-source cloud-oriented quantum computing platform." arXiv:2205.10091 (2022).
  2. McClean, J. R. et al. "OpenFermion: The electronic structure package for quantum computers." Quantum Sci. Technol. 5, 034014 (2020).

See Also