API Reference: qbraid.runtime.aqt

Overview

The qbraid.runtime.AQTProvider provides direct access to Alpine Quantum Technologies (AQT) trapped-ion quantum systems through the arnica cloud. You can write quantum circuits in Qiskit (or any other qBraid-supported framework); the provider transpiles them to AQT’s native gate set and executes them on AQT hardware such as the IBEX QPU, all from within the qBraid Runtime framework.

AQT devices are also available through the qBraid platform — accessed with QbraidProvider and billed in qBraid credits — as well as through Amazon Braket and Open Quantum. Each is a separate access point with its own device ids.

Getting started

Before you begin, make sure you have:

  1. An AQT arnica account with machine-to-machine (OIDC client-credentials) access.
  2. Your arnica client ID and client secret.
  3. Python >= 3.10

Set up the qBraid-SDK

Install qBraid with the aqt extra from PyPI using pip:

pip install 'qbraid[aqt]'

This installs the required dependencies: aqt-connector and qiskit.

Note: The qBraid-SDK requires Python 3.10 or greater. You can check your Python version by running python --version from the command line.

Authentication

The AQTProvider authenticates to the arnica cloud non-interactively using OIDC client credentials (machine-to-machine) — the interactive device/login flow is never triggered. Provide your credentials via environment variables:

export AQT_CLIENT_ID="your-client-id"
export AQT_CLIENT_SECRET="your-client-secret"

Then initialize the provider:

from qbraid.runtime.aqt import AQTProvider

provider = AQTProvider()

You can also pass credentials — or a pre-obtained access token — directly instead of using environment variables:

# client-credentials
provider = AQTProvider(client_id="your-client-id", client_secret="your-client-secret")

# or a pre-obtained bearer token
provider = AQTProvider(access_token="your-access-token")

List available devices

Use the AQTProvider to list the devices to which you have access:

from qbraid.runtime.aqt import AQTProvider

provider = AQTProvider()

devices = provider.get_devices()
print(devices)

AQT devices are addressed by a "<workspace>/<resource>" identifier. Get a specific device by its id:

device = provider.get_device("<workspace>/<resource>")

print(device.status())
# <DeviceStatus.ONLINE>

Use get_devices() to discover the "<workspace>/<resource>" ids you have access to — for example, the IBEX QPU is exposed under its arnica workspace and resource.

Submitting jobs

The AQTProvider accepts circuits written in any qBraid-supported framework. device.run() transpiles the circuit to AQT’s native basis (RZ, R, RXX) via the qiskit -> aqt_connector conversion, then submits it.

AQT enforces per-job limits: up to 2000 shots per job and up to 2000 operations (gates) per circuit. Jobs that exceed either limit are rejected. The IBEX QPU provides 12 qubits; a circuit wider than the device is rejected before submission.

Create a circuit

from qiskit import QuantumCircuit

# Bell state circuit
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()

Run a job

Use device.run() to transpile and submit a circuit:

from qiskit import QuantumCircuit
from qbraid.runtime.aqt import AQTProvider

provider = AQTProvider()
device = provider.get_device("<workspace>/<resource>")

circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()

job = device.run(circuit, shots=1000)
print(f"Job ID: {job.id}")

Batch submission

Submit multiple circuits in a single call. All circuits are bundled into one arnica job:

from qiskit import QuantumCircuit
from qbraid.runtime.aqt import AQTProvider

provider = AQTProvider()
device = provider.get_device("<workspace>/<resource>")

qc1 = QuantumCircuit(1)
qc1.h(0)
qc1.measure_all()

qc2 = QuantumCircuit(2)
qc2.h(0)
qc2.cx(0, 1)
qc2.measure_all()

job = device.run([qc1, qc2], shots=500)
print(f"Job ID: {job.id}")

When submitting a list of circuits, all are executed as a single batch job on arnica. The returned measurement counts will be a list of dictionaries, one per circuit.

Retrieving results

result = job.result()

# Measurement counts (little-endian: qubit 0 is the rightmost bit)
print(result.data.get_counts())
# {'00': 512, '11': 488}

# Job metadata
print(f"Device: {result.device_id}")
print(f"Job ID: {result.job_id}")
print(f"Success: {result.success}")

Check job status

from qbraid.runtime.enums import JobStatus

status = job.status()
print(status)
# <JobStatus.COMPLETED>

Cancel a job

job.cancel()

Cancellation targets queued or ongoing jobs. Jobs already in a terminal state (COMPLETED, FAILED, CANCELLED) cannot be cancelled.

Execution time

Retrieve the wall-clock run time (in seconds) for a completed job:

print(f"Execution time: {job.execution_time_s()} seconds")

execution_time_s() is derived from the arnica timing_data as the ongoing → finished span (the time the job spent running, excluding queue wait). It returns None until the job has completed, and raises AQTJobError if a completed job’s timing data is incomplete.

Configuration options

The device.run() method accepts the following keyword arguments:

ParameterTypeDefaultDescription
shotsint100Number of measurement shots per circuit (max 2000).
namestr"qbraid"Optional human-readable job label.

Credentials are configured via environment variables:

VariableDescription
AQT_CLIENT_IDOIDC client ID for the arnica client-credentials flow.
AQT_CLIENT_SECRETOIDC client secret for the arnica client-credentials flow.

Full example

A complete end-to-end workflow submitting a GHZ state to an AQT device:

from qiskit import QuantumCircuit
from qbraid.runtime.aqt import AQTProvider

# 1. Initialize provider (reads AQT_CLIENT_ID / AQT_CLIENT_SECRET from the environment)
provider = AQTProvider()

# 2. Get a device by its "<workspace>/<resource>" id
device = provider.get_device("<workspace>/<resource>")
print(f"Device status: {device.status()}")

# 3. Define a GHZ state circuit
circuit = QuantumCircuit(3)
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.measure_all()

# 4. Submit the job
job = device.run(circuit, shots=1000)
print(f"Submitted job: {job.id}")

# 5. Retrieve results
result = job.result()
print(f"Counts: {result.data.get_counts()}")
# Expected output (approximate): {'000': ~500, '111': ~500}