Skip to content

Azure Quantum

Azure Quantum is an aggregator: one connection unlocks Quantinuum, IonQ-via-Azure, Rigetti, Pasqal, and IQM targets. It is also the only provider that runs Q# programs on real hardware (compiled to QIR), and the recommended path to Quantinuum hardware — the direct quantinuum provider requires pytket-quantinuum<0.26 (why), while Azure’s Quantinuum targets work with current SDKs.

  1. In the Azure portal, create a Quantum workspace (subscription → resource group → workspace; note the location).
  2. Install the SDK in the kernel environment — the setup wizard’s azure entry, i.e. pip install azure-quantum (catalog).
  3. Authenticate the machine: the kernel uses azure.quantum’s default credential chain (az login, a browser prompt, environment service principal, managed identity — in the standard Azure order). There is deliberately no token field in Nuclei’s credential form.

Four fields, sent via hardware_connect:

KeyRequiredDefault
subscription_idyes
resource_groupyes
workspace_nameyes
locationnoeastus
illustrative/hardware_connect_azure.request.json
{
"type": "hardware_connect",
"provider": "azure",
"credentials": {
"subscription_id": "00000000-0000-0000-0000-000000000000",
"resource_group": "my-quantum-rg",
"workspace_name": "my-workspace",
"location": "eastus"
}
}

connect validates by listing the workspace’s targets over the network — success: false usually means wrong workspace coordinates or a missing az login. On success the four fields persist to the OS keyring like any provider (overview).

hardware_list_backends with provider: "azure" maps workspace targets to BackendInfo — with caveats worth knowing:

  • status is online when Azure reports availability containing available, otherwise maintenance.
  • queue_length is filled from Azure’s average queue time — it is a time figure, not a number of jobs.
  • qubit_count is hardcoded by name prefix (quantinuum 56, ionq 29, rigetti 80, pasqal 100, iqm 20) — estimates, not live calibration data; unrecognized targets report 0.
  • connectivity, gate_set, and average_error_rate are not populated.

Python code goes through the standard extraction path: the kernel exec()s it, pulls the Qiskit QuantumCircuit (search order qiskit → cirq → cudaq), and calls target.submit(circuit, shots=shots). Translation to the target’s native format relies on the azure-quantum SDK’s framework plugins — install the matching extra (e.g. pip install "azure-quantum[qiskit]") in the kernel environment.

# Submitted as hardware_submit code with provider "azure",
# backend e.g. "ionq.simulator" — illustrative, not replay-tested.
from qiskit import QuantumCircuit
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0, 1], [0, 1])

Q# source never runs as Python — it is compiled to QIR via the Q# adapter and the resulting QIR program object is what target.submit receives. Profile selection is automatic:

Backend name containsQIR target profile
quantinuum (case-insensitive)Adaptive_RI — mid-circuit measurement + classical feedback
anything elseBase
illustrative/hardware_submit_azure_qsharp.request.json
{
"type": "hardware_submit",
"provider": "azure",
"backend": "quantinuum.sim.h1-1e",
"shots": 500,
"code": "operation Main() : Result[] {\n use qs = Qubit[2];\n H(qs[0]);\n CNOT(qs[0], qs[1]);\n return [M(qs[0]), M(qs[1])];\n}\n",
"language": "qsharp"
}
illustrative/hardware_submit_azure_qsharp.responses.json
[
{
"type": "hardware_job_submitted",
"job": {
"id": "8f4b8f6e-1c2d-4e5f-9a0b-3c4d5e6f7a8b",
"provider": "azure",
"backend": "quantinuum.sim.h1-1e",
"status": "queued",
"queue_position": null,
"shots": 500,
"submitted_at": "2026-06-10T12:00:00.000000+00:00",
"error": null
}
}
]

The usual Q# entry-point rules apply (Main(), else the last zero-parameter operation). A program that compiles locally under the Unrestricted profile can still fail QIR compilation under Base — surfaced in the error envelope.

Providers behind Azure disagree about result shape, so the kernel coerces everything into integer counts before hardware_result.data.measurements:

Provider returnsCoercion
Integer counts (Quantinuum style)Passed through.
Float distribution — all values in [0, 1] summing to ~1.0 (±0.01)Treated as probabilities, scaled by the shot count (IonQ style; includes the deterministic {"0": 1.0} case).
Other floatsRounded to the nearest integer (counts serialized as floats).
Array-style keys — [0, 1], (1, 0), nestedNormalized element-wise to "01"-style strings. Plain string/int keys pass through verbatim (IonQ integer state keys are legitimate).
Two keys normalizing to the same stateCounts summed, not last-wins.
Anything unrecognizable (empty dict, negative values, malformed keys)data.error with a truncated raw preview — never a silent empty success.

While the job is pending, hardware_results returns {"status": "running", "message": ...} — keep polling.

  • ionq.simulator — IonQ’s cloud simulator, free, the cheapest way to prove the whole pipeline end-to-end.
  • quantinuum.sim.h1-1sc — Quantinuum’s syntax checker: validates compilation/submission for free but returns no meaningful counts.
  • Quantinuum emulator targets (e.g. quantinuum.sim.h1-1e) consume workspace credit but model real-device noise.
  • Start with low shots and confirm the coerced measurements shape before burning hardware credit.
  • The job id is Nuclei’s, not Azure’s. Handles carry a locally generated UUID; the Azure portal shows Azure’s own id — cross-referencing is manual.
  • Restart semantics: kernel restart turns in-flight Azure jobs stale; results are then unrecoverable through Nuclei (the job itself survives in the Azure portal). Details.
  • No queue positions: Azure doesn’t expose one, so hardware_status reports queue_position: -1 (“unknown”) for pending jobs.
  • Cancellation maps to status: "failed" like every provider — there is no cancelled wire status.