Source code for qiskit_qm_provider.providers.qm_provider
# Copyright 2026 Arthur Strauss
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""QMProvider: load QuAM state and create QMBackend for local/simulator use.
Author: Arthur Strauss
Date: 2026-02-08
"""
from __future__ import annotations
from typing import Optional, TYPE_CHECKING, Type
from ..backend.qm_backend import QMBackend
if TYPE_CHECKING:
from quam.core import QuamRoot as Quam
[docs]
class QMProvider:
"""Provider for Quantum Machines hardware using a local QuAM state.
``QMProvider`` is hardware-agnostic: users supply their own
:class:`~quam.core.QuamRoot` subclass (via ``quam_cls``) and their own
:class:`QMBackend` subclass (via ``backend_cls``) to match their specific
hardware. This avoids a hard dependency on any particular architecture
(e.g. flux-tunable transmons) and lets any QuAM-compatible machine be
used with the Qiskit stack.
If ``quam_cls`` is omitted the provider falls back to
``FluxTunableQuam`` from *quam-builder* for backward compatibility,
but users are encouraged to provide their own class.
Example::
from qiskit_qm_provider import QMProvider
from my_lab.quam import MyCustomQuam
from my_lab.backend import MyBackend
provider = QMProvider(
state_folder_path="/path/to/quam/state",
quam_cls=MyCustomQuam,
)
backend = provider.get_backend(backend_cls=MyBackend)
"""
def __init__(
self,
state_folder_path: Optional[str] = None,
quam_cls: Type[Quam] | None = None,
):
self.state_folder_path = state_folder_path
self._quam_cls: Type[Quam] = quam_cls
if self._quam_cls is None:
from quam_builder.architecture.superconducting.qpu.flux_tunable_quam import (
FluxTunableQuam as Quam,
)
self._quam_cls = Quam
[docs]
def get_machine(self) -> Quam:
"""Load and return the latest QuAM state."""
return self._quam_cls.load(self.state_folder_path)
[docs]
def get_backend(
self,
machine: Optional[Quam] = None,
backend_cls: Type[QMBackend] | None = None,
**backend_options,
) -> QMBackend:
"""Create a :class:`QMBackend` (or subclass) from a QuAM machine.
Users should pass ``backend_cls`` to select the backend implementation
that matches their hardware. When omitted the base :class:`QMBackend`
is used, which provides the full circuit-to-QUA pipeline but no
hardware-specific channel mapping or initialization macro.
Args:
machine: A pre-loaded :class:`~quam.core.QuamRoot` instance.
If ``None``, the provider loads one via :meth:`get_machine`.
backend_cls: The :class:`QMBackend` subclass to instantiate.
Common choices include ``FluxTunableTransmonBackend`` (for
flux-tunable transmon setups) or a user-defined subclass.
Defaults to :class:`QMBackend`.
**backend_options: Forwarded to the backend constructor. Typical
keys: ``qmm``, ``name``, ``shots``, ``compiler_options``,
``simulate``, ``memory``, ``skip_reset``, ``meas_level``,
``meas_return``.
Returns:
A :class:`QMBackend` instance (or the requested subclass).
"""
from quam.core import QuamRoot as Quam
if machine is not None and not isinstance(machine, Quam):
raise ValueError("Machine should be a Quam instance")
if backend_cls is None:
backend_cls = QMBackend
return backend_cls(
machine if machine is not None else self.get_machine(),
**backend_options,
provider=self,
)