Devices

In this module, you will learn how to use the qBraid SDK to interface with quantum backends. We will demonstrate how to construct queries and search for available devices using the qbraid.get_devices function, and overview how to execute circuits using the qbraid.runtime module.

Device Wrapper

Given a qbraid_id retrieved from get_devices, a qbraid.runtime.QuantumDevice object can be created as follows:

from qbraid.runtime import QbraidProvider

provider = QbraidProvider()
qbraid_id = 'aws_oqc_lucy'  # as an example

qdevice = provider.get_device(qbraid_id)

From here, class methods are available to get information about the device, execute quantum programs (to be covered in the next section), access the wrapped device object directly, and more.

>>> qdevice.metadata()
{'id': 'arn:aws:braket:eu-west-2::device/qpu/oqc/Lucy',
'name': 'Lucy',
'provider': 'Oxford',
'vendor': 'AWS',
'numQubits': 8,
'deviceType': 'QPU',
'status': 'ONLINE',
'queueDepth': 9,
...,
...}
>>> type(qdevice._device)
braket.aws.aws_device.AwsDevice

Executing Circuits

Each QuantumDevice is equipped with a run method, which extends the wrapped object’s native execute, sample, run, or equivalent circuit execution method. This abstraction allows the user to pass a quantum circuit built using any qbraid-supported frontend to the run method of the wrapped device.

from qiskit import QuantumCircuit

def circuit0():
    circuit = QuantumCircuit(2)
    circuit.h(0)
    circuit.cx(0,1)
    return circuit
from cirq import Circuit, LineQubit, ops

def circuit1():
    q0, q1 = LineQubit.range(2)
    circuit = Circuit(ops.H(q0), ops.CNOT(q0, q1))
    return circuit
>>> qiskit_circuit = circuit0()
>>> cirq_circuit = circuit1()
>>> qjob0 = qdevice.run(qiskit_circuit)
>>> qjob1 = qdevice.run(cirq_circuit)

Above, I defined two quantum programs, one using qiskit and the other using cirq, and executed each on Oxford Quantum Circuit’s Lucy QPU, made available through Amazon Braket.

Example Flow: Least Busy QPU

In this section, we’ll piece together a workflow example, starting by using the ibm_least_busy_qpu function to get the qbraid_id of the IBMQ QPU with the least number of queued quantum jobs.

>>> from qbraid.runtime.qiskit import ibm_least_busy_qpu
>>> qbraid_id = ibm_least_busy_qpu()
>>> qdevice = provider.get_device(qbraid_id)
>>> qdevice.name
'Nairobi'
>>> qdevice.status()
<DeviceStatus.ONLINE: 0>

After applying the device wrapper and verifying the device is online, we’re ready to submit a job. This time, we’ll use a Cirq circuit as the run method input.

>>> from qbraid.interface import random_circuit
>>> cirq_circuit = random_circuit("cirq", num_qubits=qdevice.num_qubits)
>>> qdevice.queue_depth()
4
>>> qjob = qdevice.run(cirq_circuit)
>>> qjob.status()
<JobStatus.QUEUED: 1>
>>> qdevice.queue_depth()
5

For fun, we the set number of qubits used in the random circuit equal to the number of qubits supported by the backend. We then checked the backend’s number of pending jobs, and saw the number increase by one after submitting our job.

Summary

The device layer of the qBraid SDK enables users to execute quantum circuits of any qbraid.programs.QPROGRAM_TYPES on any simulator or QPU returned by qbraid.get_devices. Filter your search to the specifications of your task, identify a device, and execute your program through a consistent three-step protocol:

  1. Get qbraid device ID

  2. Apply device wrapper

  3. Execute program via run method