Skip to content

Running circuits on QPUs

The aqora Python library ships a universal QPU that submits quantum programs to a provider platform on aqora.io — whatever framework they were written in.

With qiskit installed, run a Bell circuit:

from aqora import QPU
from qiskit import QuantumCircuit
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
qpu = QPU(platform="nexus:Selene")
job = qpu.run(qc, shots=1000)
print(job.counts()) # [{'00': 512, '11': 488}]

run() returns immediately with a QPUJob; counts() waits for the job to complete and normalizes its results.

The framework backends in aqora.qiskit, aqora.pytket and aqora.guppy each speak one fixed serialization format. The universal QPU instead reads the formats the platform accepts (most preferred first) and encodes into the first of them the submitted programs can produce — so a qiskit circuit can reach a HUGR-preferring platform, and a guppy program a QASM-preferring one.

When platform is omitted the server chooses a default platform, and programs are submitted in their native format.

platform selects the provider platform by name or id. The canonical form is provider:name:

qpu = QPU(platform="nexus:Selene")

A bare name like "Selene" or a global id also works.

run() accepts a single program or a list of them:

  • qiskit QuantumCircuits
  • pytket Circuits
  • @guppy-decorated functions
  • hugr Packages
  • raw HUGR envelope or QIR bitcode bytes
  • QASM source

Every program in a job shares one serialization format: the first the platform accepts that all of them can produce.

job = qpu.run(qc, shots=1000)
job.status() # "WAITING" | "RUNNING" | "COMPLETED" | ...
job.wait(timeout=60) # block until completed (raises on failure or timeout)
job.counts() # one counts dict per program, in submission order
job.result() # the ProviderResult of a single-program job
job.result_items() # every ProviderResult of a multi-program job

A job can be picked up again later — from another process or machine — by id:

from aqora import QPUJob
job = QPUJob.from_id("...")
print(job.counts())

Each result is a ProviderResult. counts() gives measurement counts normalized across every result format: keys are bitstrings, and a result carrying several named registers joins their values with a space in label order, matching qiskit’s convention. Counts are always joint over the registers of a shot, never a product of per-register marginals.

To work with a framework’s native result type instead, convert the raw result:

Converter Returns Requires
to_qiskit_result() qiskit Result aqora[qiskit]
to_backend_result() pytket BackendResult aqora[pytket]
to_cirq_result() cirq Result aqora[cirq]
to_qsys_result() hugr QsysResult aqora[guppy]
qsys_shots() raw QSYS shot array
qir_labeled() labeled QIR result
cudaq_register_counts() per-register counts dict

Workspaces on aqora.io come with aqora preinstalled and already authenticated, so the quick start above runs as-is in a marimo notebook. Add framework extras to your workspace as needed:

Terminal window
uv add "aqora[qiskit]"