Overview

The qbraid.runtime.QuantinuumProvider provides support for Quantinuum’s trapped-ion quantum systems through the Quantinuum Nexus platform. This means you can write quantum circuits using pytket, compile and execute them on Quantinuum’s H-series processors and emulators, all from within the qBraid Runtime framework.

This page covers QuantinuumProvider, which runs against your own Nexus account. If you would rather have the Nexus account managed by qBraid, see Through qBraid at the end. You need only a qBraid API key.

Device names

Quantinuum names hardware <family>-<n> and derives its other targets from it by suffix. The suffix is the only thing distinguishing an emulator from the QPU it models, so it is worth reading carefully before submitting:

NameKind
H1-1, H2-1, Helios-1QPU
H1-1E, H2-1EDevice-hosted emulator (noise model of the QPU)
H1-1LE, H2-1LENexus-hosted emulator
H1-Emulator, H2-EmulatorNexus-hosted emulator
H1-1SC, H2-1SCSyntax checker (validates without executing)

device.profile.simulator reflects this classification. device.status() reports ONLINE, UNAVAILABLE (maintenance), or OFFLINE; Nexus-hosted emulators have no machine-status endpoint and always report ONLINE.

Getting started

Before you begin, make sure you have:

  1. A Quantinuum Nexus account with valid credentials.
  2. The qnexus CLI tool installed and authenticated (see Authentication below).
  3. Python >= 3.10

Set up the qBraid-SDK

Install qBraid with the quantinuum extra from PyPI using pip:

pip install 'qbraid[quantinuum]'

This installs the required dependencies: pytket, pytket-quantinuum, and qnexus.

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 QuantinuumProvider authenticates through the qnexus library. Before using the provider, you must log in using the qnexus CLI:

qnx login

This opens a browser-based authentication flow and stores your credentials locally. Once authenticated, the provider will automatically use your stored credentials. No API keys or environment variables are needed.

Then initialize the provider:

from qbraid.runtime.quantinuum import QuantinuumProvider

provider = QuantinuumProvider()

List available devices

Use the QuantinuumProvider to list all devices to which you have access:

from qbraid.runtime.quantinuum import QuantinuumProvider

provider = QuantinuumProvider()

devices = provider.get_devices()
print(devices)

Get a specific device by its ID:

device = provider.get_device("H1-1")

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

Devices with an “E” suffix (e.g., H1-1E) are emulators (simulators) that model the behavior of the corresponding hardware device.

Submitting jobs

The QuantinuumProvider accepts quantum programs written as pytket Circuit objects.

Create a circuit

from pytket.circuit import Circuit

# Bell state circuit
circuit = Circuit(2)
circuit.H(0)
circuit.CX(0, 1)
circuit.measure_all()

Run a job

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

from pytket.circuit import Circuit
from qbraid.runtime.quantinuum import QuantinuumProvider

provider = QuantinuumProvider()
device = provider.get_device("H1-1E")

circuit = Circuit(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 Nexus job:

from pytket.circuit import Circuit
from qbraid.runtime.quantinuum import QuantinuumProvider

provider = QuantinuumProvider()
device = provider.get_device("H1-1E")

circuits = [
    Circuit(1).H(0).measure_all(),
    Circuit(1).X(0).measure_all(),
    Circuit(2).H(0).CX(0, 1).measure_all(),
]

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

When submitting a list of circuits, all circuits are compiled and executed as a single batch job on Quantinuum Nexus. The returned measurement_counts will be a list of dictionaries, one per circuit.

Retrieving results

result = job.result()

# Measurement counts
print(result.data.measurement_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}")

Counts are keyed most-significant-bit first, matching qBraid’s other providers. Nexus and pytket use least-significant-bit-first ordering internally, and qBraid converts on the way out, so a bitstring read here is reversed relative to the same result fetched through pytket directly.

Your circuit must contain measurements. Quantinuum executes exactly the circuit you submit, so a circuit with no measurement instruction produces no counts. Some frameworks imply a final measure-all that does not survive conversion, so add measurements explicitly rather than relying on it.

Check job status

from qbraid.runtime.enums import JobStatus

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

Cancel a job

job.cancel()

Cancellation is best-effort. Jobs already in a terminal state (COMPLETED, FAILED, CANCELLED) cannot be cancelled.

Execution time

You can retrieve the wall-clock execution time (in seconds) for a completed job:

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

Configuration options

The device.run() method accepts additional keyword arguments to control compilation and project scoping:

ParameterTypeDefaultDescription
shotsint1000Number of measurement shots per circuit.
project_namestr"qbraid"Nexus project name to scope compile/execute jobs.
optimisation_levelint1pytket optimization level (0-2).

The project_name and optimisation_level parameters can also be set via environment variables:

VariableDefaultDescription
QUANTINUUM_NEXUS_PROJECT_NAME"qbraid"Default Nexus project name.
QUANTINUUM_NEXUS_OPT_LEVEL1Default pytket optimization level.
QUANTINUUM_NEXUS_COMPILE_TIMEOUT900Seconds run waits for compilation before raising.
QUANTINUUM_NEXUS_HTTP_TIMEOUT60Per-request timeout on every Nexus HTTP call.

Nexus runs a submission as two jobs: a compile job, which run blocks on, and an execute job, which the returned QuantinuumJob tracks. QUANTINUUM_NEXUS_COMPILE_TIMEOUT bounds that blocking wait, so a compile stuck in the Nexus queue raises QuantinuumDeviceError rather than holding the calling thread indefinitely. Raise it if you routinely submit large circuits.

A malformed value for any of these raises QuantinuumDeviceError naming the variable, rather than surfacing later as an unrelated failure.

Full example

A complete end-to-end workflow targeting a Quantinuum emulator:

from pytket.circuit import Circuit
from qbraid.runtime.quantinuum import QuantinuumProvider

# 1. Initialize provider (assumes qnx login was run)
provider = QuantinuumProvider()

# 2. Get an emulator device
device = provider.get_device("H1-1E")
print(f"Device status: {device.status()}")

# 3. Define a GHZ state circuit
circuit = Circuit(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, optimisation_level=2)
print(f"Submitted job: {job.id}")

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

Through qBraid

If you do not have a Nexus account, you can reach the same devices through QbraidProvider, where the Nexus credentials are managed by qBraid. You need a qBraid API key and nothing else: no qnexus install, no qnx login:

pip install qbraid
export QBRAID_API_KEY="your_api_key_here"

Devices are addressed by their qBraid resource name rather than the Nexus device name:

from qiskit import QuantumCircuit
from qbraid.runtime import QbraidProvider

provider = QbraidProvider()
device = provider.get_device("quantinuum:quantinuum:sim:h2-emulator")

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

job = device.run(circuit, shots=1000)
print(job.result().data.get_counts())
{'00': 496, '11': 504}

To see which are available to you:

devices = [d for d in provider.get_devices() if d.profile.provider_name == "quantinuum"]

The bit-ordering and measurement notes above apply equally here. Two differences from the direct path: circuits are submitted one per job rather than as a Nexus batch, and the QUANTINUUM_NEXUS_* variables are set on the qBraid runtime rather than by you.