Source code for quantum_executor.local_aer.provider
"""Local Qiskit Aer Provider Class compatible with qBraid."""
from __future__ import annotations
from collections.abc import Sequence
from typing import TYPE_CHECKING
from qbraid._caching import cached_method # type: ignore
from qbraid.programs import ProgramSpec # type: ignore
from qbraid.runtime.profile import TargetProfile # type: ignore
from qbraid.runtime.provider import QuantumProvider # type: ignore
from qiskit import QuantumCircuit # type: ignore
from qiskit_aer import AerSimulator # type: ignore
from qiskit_ibm_runtime.fake_provider import FakeProviderForBackendV2 # type: ignore
from quantum_executor.local_aer.device import LocalAERBackend
if TYPE_CHECKING: # pragma: no cover
from qiskit.providers import BackendV2 # type: ignore
[docs]
class LocalAERProvider(QuantumProvider): # type: ignore
"""Provider class for local AerSimulator backends.
This class provides methods for retrieving simulated quantum devices,
including both noiseless and noisy simulators.
"""
def _build_runtime_profile(self, backend: BackendV2, program_spec: ProgramSpec | None = None) -> TargetProfile:
"""Build a runtime profile from a backend.
Parameters
----------
backend : AerSimulator
The Qiskit AerSimulator backend instance.
program_spec : ProgramSpec, optional
A specification for the quantum program, defaulting to a circuit.
Returns
-------
TargetProfile
A target profile with device details for runtime execution.
"""
program_spec = program_spec or ProgramSpec(QuantumCircuit)
return TargetProfile(
device_id=backend.name,
simulator=True,
num_qubits=backend.num_qubits,
program_spec=program_spec,
provider_name="Local_AER",
)
[docs]
@cached_method # type: ignore
def get_devices(self) -> list[LocalAERBackend]:
"""Retrieve a list of available quantum backends.
This method combines a noiseless AerSimulator with additional backends
provided by a fake IBM provider, returning a list of LocalAERBackend objects.
Returns
-------
list[qbraid.runtime.ibm.QiskitBackend]
A list of quantum backend instances.
"""
# Create a list of backends: the primary noiseless AerSimulator, plus others.
backends: Sequence[BackendV2] = [AerSimulator(), *FakeProviderForBackendV2().backends()]
program_spec = ProgramSpec(QuantumCircuit)
return [
LocalAERBackend(
profile=self._build_runtime_profile(backend, program_spec=program_spec),
backend=backend,
)
for backend in backends
]
[docs]
@cached_method # type: ignore
def get_device(self, device_id: str) -> LocalAERBackend:
"""Retrieve a specific quantum backend by its device identifier.
This method returns a LocalAERBackend instance corresponding to the specified device_id.
It supports both the default noiseless simulator and a noisy simulator generated based on
a noise strength encoded in the device_id.
Parameters
----------
device_id : str
The identifier of the desired quantum backend.
Returns
-------
qbraid.runtime.ibm.QiskitBackend
The corresponding quantum backend.
Raises
------
ValueError
If the device is not found among local AerSimulator backends.
"""
if device_id == "aer_simulator":
backend = AerSimulator()
else:
try:
backend = FakeProviderForBackendV2().backend(device_id)
except Exception as e:
raise ValueError(f"Device '{device_id}' not found in local AerSimulator backends.") from e
program_spec = ProgramSpec(QuantumCircuit)
return LocalAERBackend(
profile=self._build_runtime_profile(backend, program_spec=program_spec),
backend=backend,
)
def __hash__(self) -> int:
"""Return a hash value for the LocalAERProvider instance.
This hash is used for caching and comparison purposes.
Returns
-------
int
The hash value of the LocalAERProvider instance.
"""
if not hasattr(self, "_hash"):
object.__setattr__(self, "_hash", hash("Local_AER"))
return int(self._hash) # pylint: disable=no-member #type: ignore[unused-ignore]