"""An :class:`AvailableDevice` is a device on which one can run or submit a
circuit. While it is an abstract class, all it's concrete implementations are
enums with a few methods, required by :class:`AvailableDevice`.
Each supported provider has its available devices listed as these enums, which
you can find bellow:
- :class:`IBMDevice`,
- :class:`ATOSDevice`,
- :class:`AWSDevice`,
- :class:`GOOGLEDevice`.
Not all combinations of :class:`AvailableDevice` and
:class:`JobType<mpqp.execution.job.JobType>` are possible. Here is the list of
compatible jobs types and devices.
.. csv-table:: Job/Device Compatibility Matrix
:file: ../../docs/resources/job-device_compat.csv
:widths: 7, 25, 7, 10, 10, 15
:header-rows: 1
"""
from abc import abstractmethod
from enum import Enum, auto
from mpqp.execution.connection.env_manager import get_env_variable
[docs]class AvailableDevice(Enum):
"""Class used to define a generic device (quantum computer, or simulator)."""
[docs] @abstractmethod
def is_remote(self) -> bool:
"""Indicates whether a device is remote or not.
Returns:
``True`` if this device is remote.
"""
pass
[docs] @abstractmethod
def is_gate_based(self) -> bool:
"""Indicates whether a device is gate based or not.
Returns:
``True`` if this device is a gate-based simulator/QPU."""
pass
[docs] @abstractmethod
def is_simulator(self) -> bool:
"""Indicates whether a device is a simulator or not.
Returns:
``True`` if this device is a simulator."""
pass
[docs] @abstractmethod
def is_noisy_simulator(self) -> bool:
"""Indicates whether a device can simulate noise or not.
Returns:
``True`` if this device can simulate noise.
"""
pass
[docs]class IBMDevice(AvailableDevice):
"""Enum regrouping all available devices provided by IBM Quantum.
Warning:
Since previous version, many devices were disabled by IBM. This may
affect your code. We are currently investigating the issue to check if a
workaround is possible for some of them (like replacing a simulator by
an equivalent one for instance).
"""
# PULSE_SIMULATOR = "pulse_simulator"
AER_SIMULATOR = "aer_simulator"
AER_SIMULATOR_STATEVECTOR = "aer_simulator_statevector"
# TODO: many devices are no longer working, explore why
# AER_SIMULATOR_DENSITY_MATRIX = "aer_simulator_density_matrix"
# AER_SIMULATOR_STABILIZER = "aer_simulator_stabilizer"
# AER_SIMULATOR_MATRIX_PRODUCT_STATE = "aer_simulator_matrix_product_state"
# AER_SIMULATOR_EXTENDED_STABILIZER = "aer_simulator_extended_stabilizer"
# AER_SIMULATOR_UNITARY = "aer_simulator_unitary"
# AER_SIMULATOR_SUPEROP = "aer_simulator_superop"
# IBMQ_SIMULATOR_STATEVECTOR = "simulator_statevector"
# IBMQ_SIMULATOR_STABILIZER = "simulator_stabilizer"
# IBMQ_SIMULATOR_EXTENDED_STABILIZER = "simulator_extended_stabilizer"
# IBMQ_SIMULATOR_MPS = "simulator_mps"
# IBMQ_QASM_SIMULATOR = "ibmq_qasm_simulator"
IBM_BRISBANE = "ibm_brisbane"
IBM_OSAKA = "ibm_osaka"
IBM_KYOTO = "ibm_kyoto"
IBM_SHERBROOKE = "ibm_sherbrooke"
IBM_KYIV = "ibm_kyiv"
IBM_NAZCA = "ibm_nazca"
IBM_CUSCO = "ibm_cusco"
IBM_ITHACA = "ibm_ithaca"
IBM_TORINO = "ibm_torino"
IBM_QUEBEC = "ibm_quebec"
IBM_KAWASAKI = "ibm_kawasaki"
IBM_CLEVELAND = "ibm_cleveland"
IBM_CAIRO = "ibm_cairo"
IBM_HANOI = "ibm_hanoi"
IBM_ALGIERS = "ibm_algiers"
IBM_KOLKATA = "ibm_kolkata"
IBM_MUMBAI = "ibm_mumbai"
IBM_PEEKSKILL = "ibm_peekskill"
IBM_RANDOM_SMALL_DEVICE = "ibm_small_device"
IBM_SMALL_DEVICES_LEAST_BUSY = "ibm_least_busy"
def is_remote(self) -> bool:
return self.name.startswith("IBM")
def is_gate_based(self) -> bool:
return True
# return self != IBMDevice.PULSE_SIMULATOR
def is_simulator(self) -> bool:
return "simulator" in self.value
def is_noisy_simulator(self) -> bool:
raise NotImplementedError()
# TODO: determine which devices can simulate noise or not for Qiskit remote, or local
noise_support_devices = {
IBMDevice.IBMQ_SIMULATOR_STATEVECTOR: True,
IBMDevice.AER_SIMULATOR_STABILIZER: True,
IBMDevice.IBMQ_SIMULATOR_EXTENDED_STABILIZER: False,
IBMDevice.IBMQ_SIMULATOR_MPS: False,
IBMDevice.IBMQ_QASM_SIMULATOR: True,
}
return self in noise_support_devices
[docs]class ATOSDevice(AvailableDevice):
"""Enum regrouping all available devices provided by ATOS."""
MYQLM_PYLINALG = auto()
MYQLM_CLINALG = auto()
QLM_LINALG = auto()
QLM_MPS = auto()
QLM_MPO = auto()
QLM_NOISYQPROC = auto()
def is_remote(self):
return self.name.startswith("QLM")
def is_gate_based(self) -> bool:
return True
def is_simulator(self) -> bool:
return True
def is_noisy_simulator(self) -> bool:
return self in {ATOSDevice.QLM_NOISYQPROC, ATOSDevice.QLM_MPO}
[docs] @staticmethod
def from_str_remote(name: str):
"""Returns the first remote ATOSDevice containing the name given in parameter.
Args:
name: A string containing the name of the device.
Raises:
ValueError: If no device corresponding to the given name could be
found.
Examples:
>>> ATOSDevice.from_str_remote('NoisyQProc')
<ATOSDevice.QLM_NOISYQPROC: 6>
>>> ATOSDevice.from_str_remote('linalg')
<ATOSDevice.QLM_LINALG: 3>
>>> ATOSDevice.from_str_remote('Mps')
<ATOSDevice.QLM_MPS: 4>
"""
u_name = name.upper()
for elem in ATOSDevice:
if u_name in elem.name and elem.is_remote():
return elem
raise ValueError(f"No device found for name `{name}`.")
[docs]class AWSDevice(AvailableDevice):
"""Enum regrouping all available devices provided by AWS Braket."""
BRAKET_LOCAL_SIMULATOR = "LocalSimulator"
BRAKET_SV1_SIMULATOR = "quantum-simulator/amazon/sv1"
BRAKET_DM1_SIMULATOR = "quantum-simulator/amazon/dm1"
BRAKET_TN1_SIMULATOR = "quantum-simulator/amazon/tn1"
BRAKET_IONQ_HARMONY = "qpu/ionq/Harmony"
BRAKET_IONQ_ARIA_1 = "qpu/ionq/Aria-1"
BRAKET_IONQ_ARIA_2 = "qpu/ionq/Aria-2"
BRAKET_IONQ_FORTE_1 = "qpu/ionq/Forte-1"
BRAKET_OQC_LUCY = "qpu/oqc/Lucy"
BRAKET_QUERA_AQUILA = "qpu/quera/Aquila"
BRAKET_RIGETTI_ASPEN_M_3 = "qpu/rigetti/Aspen-M-3"
def is_remote(self):
return self != AWSDevice.BRAKET_LOCAL_SIMULATOR
def is_gate_based(self) -> bool:
return True
def is_simulator(self) -> bool:
return "SIMULATOR" in self.name
def is_noisy_simulator(self) -> bool:
return self in [
AWSDevice.BRAKET_LOCAL_SIMULATOR,
AWSDevice.BRAKET_DM1_SIMULATOR,
]
[docs] def get_arn(self) -> str:
"""Retrieve the AwsDevice arn from this AWSDevice element.
Returns:
The arn of the device.
Examples:
>>> AWSDevice.BRAKET_IONQ_HARMONY.get_arn()
'arn:aws:braket:us-east-1::device/qpu/ionq/Harmony'
>>> AWSDevice.BRAKET_SV1_SIMULATOR.get_arn()
'arn:aws:braket:::device/quantum-simulator/amazon/sv1'
>>> AWSDevice.BRAKET_RIGETTI_ASPEN_M_3.get_arn()
'arn:aws:braket:us-west-1::device/qpu/rigetti/Aspen-M-3'
"""
region = self.get_region()
if self.is_simulator():
region = ""
return "arn:aws:braket:" + region + "::device/" + self.value
[docs] def get_region(self) -> str:
"""Retrieve the Aws region from this AWSDevice element.
Returns:
The region of the device.
Examples:
>>> AWSDevice.BRAKET_IONQ_HARMONY.get_region()
'us-east-1'
>>> AWSDevice.BRAKET_SV1_SIMULATOR.get_region() == get_env_variable("AWS_DEFAULT_REGION")
True
>>> AWSDevice.BRAKET_RIGETTI_ASPEN_M_3.get_region()
'us-west-1'
"""
if not self.is_remote():
raise ValueError("No arn for a local simulator")
elif self == AWSDevice.BRAKET_RIGETTI_ASPEN_M_3:
return "us-west-1"
elif self == AWSDevice.BRAKET_OQC_LUCY:
return "eu-west-2"
elif self in [
AWSDevice.BRAKET_IONQ_HARMONY,
AWSDevice.BRAKET_IONQ_ARIA_1,
AWSDevice.BRAKET_IONQ_ARIA_2,
AWSDevice.BRAKET_IONQ_FORTE_1,
AWSDevice.BRAKET_QUERA_AQUILA,
]:
return "us-east-1"
else:
return get_env_variable("AWS_DEFAULT_REGION")
[docs] @staticmethod
def from_arn(arn: str):
"""Returns the right AWSDevice from the arn given in parameter.
Args:
arn: The AWS arn identifying the AwsDevice.
Examples:
>>> AWSDevice.from_arn('arn:aws:braket:us-east-1::device/qpu/ionq/Harmony')
<AWSDevice.BRAKET_IONQ_HARMONY: 'qpu/ionq/Harmony'>
>>> AWSDevice.from_arn('arn:aws:braket:::device/quantum-simulator/amazon/sv1')
<AWSDevice.BRAKET_SV1_SIMULATOR: 'quantum-simulator/amazon/sv1'>
"""
for elem in AWSDevice:
if elem.value in arn:
return elem
raise ValueError(f"No device found for ARN `{arn}`.")
[docs]class GOOGLEDevice(AvailableDevice):
"""Enum regrouping all available devices provided by CIRQ."""
CIRQ_LOCAL_SIMULATOR = "LocalSimulator"
PROCESSOR_RAINBOW = "rainbow"
PROCESSOR_WEBER = "weber"
IONQ_SIMULATOR = "simulator"
IONQ_QPU = "qpu"
def is_remote(self):
if self.name.startswith("IONQ"):
return True
return False
[docs] def is_ionq(self):
return self.name.startswith("IONQ")
def is_gate_based(self) -> bool:
return True
def is_simulator(self) -> bool:
return "SIMULATOR" in self.name
[docs] def is_processor(self) -> bool:
"""
Check if the device is a processor.
Returns:
True if the device is a processor, False otherwise.
"""
return self.name.startswith("PROCESSOR")