Source code for qbraid.runtime.aws.device

# Copyright (C) 2024 qBraid
#
# This file is part of the qBraid-SDK
#
# The qBraid-SDK is free software released under the GNU General Public License v3
# or later. You can redistribute and/or modify it under the terms of the GPL v3.
# See the LICENSE file in the project root or <https://www.gnu.org/licenses/gpl-3.0.html>.
#
# THERE IS NO WARRANTY for the qBraid-SDK, as per Section 15 of the GPL v3.

"""
Module defining BraketDeviceWrapper Class

"""
from __future__ import annotations

from typing import TYPE_CHECKING, Optional, Union

from braket.ahs.analog_hamiltonian_simulation import AnalogHamiltonianSimulation
from braket.aws import AwsDevice
from braket.circuits import Circuit

from qbraid.programs import NATIVE_REGISTRY, QPROGRAM_REGISTRY, ExperimentType, load_program
from qbraid.runtime.device import QuantumDevice
from qbraid.runtime.enums import DeviceStatus
from qbraid.runtime.exceptions import DeviceProgramTypeMismatchError
from qbraid.transpiler import transpile

from .availability import next_available_time
from .job import BraketQuantumTask

if TYPE_CHECKING:
    import braket.aws

    import qbraid.runtime
    import qbraid.runtime.aws
    import qbraid.transpiler


[docs] class BraketDevice(QuantumDevice): """Wrapper class for Amazon Braket ``Device`` objects."""
[docs] def __init__( self, profile: qbraid.runtime.TargetProfile, session: Optional[braket.aws.AwsSession] = None, ): """Create a BraketDevice.""" super().__init__(profile=profile) self._device = AwsDevice(arn=self.id, aws_session=session) self._provider_name = self.profile.get("provider_name")
@property def name(self) -> str: """Return the name of this Device.""" return self._device.name def __str__(self): """Official string representation of QuantumDevice object.""" return f"{self.__class__.__name__}('{self._provider_name} {self.name}')" def status(self) -> qbraid.runtime.DeviceStatus: """Return the status of this Device.""" if self._device.status == "ONLINE": if self._device.is_available: return DeviceStatus.ONLINE return DeviceStatus.UNAVAILABLE if self._device.status == "RETIRED": return DeviceStatus.RETIRED return DeviceStatus.OFFLINE def availability_window(self) -> tuple[bool, str, str]: """Provides device availability status. Indicates current availability, time remaining (hours, minutes, seconds) until next availability or unavailability, and future UTC datetime of next change in availability status. Returns: tuple[bool, str, Optional[str]]: Current device availability, hr/min/sec until availability switch, future UTC datetime of availability switch """ return next_available_time(self._device) def queue_depth(self) -> int: """Return the number of jobs in the queue for the device.""" queue_depth_info = self._device.queue_depth() total_queued = 0 for queue_type in ["Normal", "Priority"]: num_queued = queue_depth_info.quantum_tasks[queue_type] if isinstance(num_queued, str) and num_queued.startswith(">"): num_queued = num_queued[1:] total_queued += int(num_queued) return total_queued def transform( self, run_input: Union[Circuit, AnalogHamiltonianSimulation] ) -> Union[Circuit, AnalogHamiltonianSimulation]: """Transpile a circuit for the device.""" program = run_input provider = (self.profile.provider_name or "").upper() experiment_type = self.profile.experiment_type if experiment_type == ExperimentType.GATE_MODEL and not isinstance(program, Circuit): raise DeviceProgramTypeMismatchError(program, str(Circuit), experiment_type) if experiment_type == ExperimentType.AHS and not isinstance( program, AnalogHamiltonianSimulation ): raise DeviceProgramTypeMismatchError( program, str(AnalogHamiltonianSimulation), experiment_type ) if provider == "IONQ": graph = self.scheme.conversion_graph if ( graph is not None and graph.has_edge("pytket", "braket") and QPROGRAM_REGISTRY["pytket"] == NATIVE_REGISTRY["pytket"] and QPROGRAM_REGISTRY["braket"] == NATIVE_REGISTRY["braket"] and self._target_spec.alias == "braket" ): tk_circuit = transpile(program, "pytket", max_path_depth=1, conversion_graph=graph) tk_program = load_program(tk_circuit) tk_program.transform(self) tk_transformed = tk_program.program braket_transformed = transpile( tk_transformed, "braket", max_path_depth=1, conversion_graph=graph ) program = braket_transformed else: qprogram = load_program(program) qprogram.transform(self) program = qprogram.program return program def submit( self, run_input: Union[ Circuit, AnalogHamiltonianSimulation, list[Circuit], list[AnalogHamiltonianSimulation] ], *args, **kwargs, ) -> Union[BraketQuantumTask, list[BraketQuantumTask]]: """Run a quantum task specification on this quantum device. Task must represent a quantum circuit, annealing problems not supported. Args: run_input: Specification of a task to run on device. Keyword Args: shots (int): The number of times to run the task on the device. Returns: The job like object for the run. """ is_single_input = not isinstance(run_input, list) run_input = [run_input] if is_single_input else run_input aws_quantum_task_batch = self._device.run_batch(run_input, *args, **kwargs) tasks = [ BraketQuantumTask(task.id, task=task, device=self._device) for task in aws_quantum_task_batch.tasks ] if is_single_input: return tasks[0] return tasks