Source code for qbraid.runtime.qperfect.provider

# Copyright 2026 qBraid
#
# 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.

"""
Module defining QPerfect (MIMIQ) provider class.

The provider holds an authenticated ``MimiqConnection`` and hands it to each device and job.
:mod:`~qbraid.runtime.qperfect.client` resolves the credentials. Circuit conversion runs on the
transpiler's ``qiskit -> mimiqcircuits`` edge.

"""

from __future__ import annotations

from mimiqcircuits import Circuit as MimiqCircuit
from mimiqcircuits import MimiqConnection

from qbraid._caching import cached_method
from qbraid.programs import ExperimentType, ProgramSpec
from qbraid.runtime.exceptions import ResourceNotFoundError
from qbraid.runtime.profile import TargetProfile
from qbraid.runtime.provider import QuantumProvider

from .client import build_connection
from .device import QPerfectDevice

# The algorithm (auto / statevector / mps) is a per-job option, not a separate device.
_DEVICE_ID = "mimiq-emulator"

# Approximate reach per backend: state vector is memory-bound (2^N amplitudes), MPS scales with
# entanglement. The emulator errors if a run exceeds what its chosen backend can handle.
_ALGORITHM_QUBITS: dict[str, int] = {
    "statevector": 32,
    "mps": 256,
}


[docs] class QPerfectProvider(QuantumProvider): """QPerfect (MIMIQ) provider class."""
[docs] def __init__( self, token: str | None = None, *, url: str | None = None, username: str | None = None, password: str | None = None, ): """Build a QPerfect provider. Credentials are resolved on first connection use, so constructing a provider never raises for missing configuration. See :func:`~qbraid.runtime.qperfect.client.build_connection` for the resolution order and the environment variables behind each argument. Args: token: A MIMIQ refresh token. url: The MIMIQ cloud URL. username: MIMIQ account email. password: MIMIQ account password. """ self._token = token self._url = url self._username = username self._password = password self._connection: MimiqConnection | None = None
@property def connection(self) -> MimiqConnection: """Return an authenticated MIMIQ connection, establishing it on first use.""" if self._connection is None: self._connection = build_connection( self._token, url=self._url, username=self._username, password=self._password, ) return self._connection @staticmethod def _build_profile() -> TargetProfile: """Build the :class:`TargetProfile` for the MIMIQ emulator.""" return TargetProfile( device_id=_DEVICE_ID, simulator=True, experiment_type=ExperimentType.GATE_MODEL, # The emulator picks a backend per job, so advertise the largest reach. num_qubits=max(_ALGORITHM_QUBITS.values()), program_spec=ProgramSpec(MimiqCircuit), provider_name="QPerfect", ) @cached_method def get_devices(self) -> list[QPerfectDevice]: """Get the available QPerfect (MIMIQ) devices (a single cloud emulator).""" return [QPerfectDevice(self._build_profile(), self)] @cached_method def get_device(self, device_id: str) -> QPerfectDevice: """Get the QPerfect MIMIQ emulator by its device id (``"mimiq-emulator"``).""" if device_id != _DEVICE_ID: raise ResourceNotFoundError( f"Unknown QPerfect device '{device_id}'. The only device is '{_DEVICE_ID}'." ) return QPerfectDevice(self._build_profile(), self) def __hash__(self): if not hasattr(self, "_hash"): object.__setattr__(self, "_hash", hash((self._token, self._url, self._username))) return self._hash # pylint: disable=no-member