cudaq-ibm-anton
Run CUDA-Q kernels on IBM Quantum, with error mitigation that's been measured.
CUDA-Q ships no IBM Quantum target — its backend list covers IonQ, Quantinuum, IQM, OQC, Braket and QuEra, but not IBM. This package provides that path, plus a mitigation layer where every claim is backed by hardware measurements, including the ones that didn't work.
import cudaq
from anton import AntonBackend
@cudaq.kernel
def ghz():
q = cudaq.qvector(8)
h(q[0])
for i in range(7):
x.ctrl(q[i], q[i + 1])
backend = AntonBackend("ibm_fez")
result = backend.run(ghz, shots=10000)
print(result.explain())
▶ Try it now — Colab quickstart, no IBM account needed
pip install cudaq-ibm-anton[cudaq,sim]
What it does
CUDA-Q kernel
→ cudaq.translate(format="openqasm2")
→ qiskit.qasm2.loads
→ transpile(optimization_level=3) Qiskit remains the compilation baseline
→ circuit + hardware feature extraction
→ mitigation policy
→ IBM Quantum
→ post-processing
→ AntonResult
cudaq is optional. The package also accepts Qiskit circuits and OpenQASM 2
text, so the mitigation layer is usable without CUDA-Q.
Measured on hardware
ibm_fez (156-qubit Heron r2), 215 rows across 30 IBM jobs. Full detail in
docs/FINDINGS.md.
Readout correction is the reliable win — 0.0992 → 0.0323 at n=8, ~15 sigma.
On ibm_fez readout error is 3.4× the two-qubit gate error.
The right strategy depends on the circuit. Interaction contrast 0.1373 ± 0.0150, z = 9.2. Few two-qubit gates → readout alone; gate-heavy → readout + ZNE + DD.
On 30 circuits never used to build the policy:
| policy | mean error | vs fixed | oracle hits |
|---|---|---|---|
| raw | 0.1269 | — | — |
| fixed (readout) | 0.1052 | 0.0% | 8/30 |
| two-qubit rule | 0.0849 | 19.3% | 22/30 |
| per-circuit oracle | 0.0777 | 26.1% | 30/30 |
Rule vs fixed: p = 0.0023, capturing 74% of the oracle's headroom.
Dynamical decoupling alone is harmful at every size we measured — it helps only in combination, on gate-heavy circuits.
The policy is a rule, and self-calibrating
result.explain() prints RULE, not AI on every run. We are explicit about
this because we tried the alternative: gradient-boosting models trained on
42,000 simulated circuits did not beat the one-feature rule (p = 0.43, and
directionally worse). That negative result and its diagnosis are in
FINDINGS §5. The LearnedPolicy interface ships anyway — if
you beat the rule, that's a real result and we'd like to see it.
The threshold is not a magic constant. What governs the decision is accumulated
infidelity (n_2q × e_2q), so the gate-count threshold rescales with the
device's measured error:
rule = TwoQubitRule.for_backend(backend.snapshot) # automatic in AntonBackend
A device with half the gate error needs twice as many gates before ZNE has
enough coherent error to extrapolate against. On ibm_fez this reproduces the
validated threshold of 25 exactly, because ibm_fez is the reference.
Untested claim, stated plainly: the rescaling is physically motivated but validated on one device. On
ibm_fez, gate errors span only 1.7×, so scaled and unscaled forms are nearly identical and we cannot distinguish them. Cross-device behaviour is unverified. If you run this elsewhere, the data would be valuable.
Three traps this package prevents
Feature units. Qiskit reports instruction.duration in the authored unit or
in dt, and dt is backend-specific. Mixing them is a silent 4× error. Every
duration here is converted to seconds, and FEATURE_SCHEMA declares a unit per
feature.
Logical vs ISA features. Computing features on the untranspiled circuit
shifts isa_1q by 2.6× (45 vs 117). extract() raises NotTranspiledError
rather than accepting one silently — testing operations against the backend's
actual basis, since a transpiled AerSimulator circuit is byte-identical to the
logical one.
Degenerate synthetic features. Modelling a device as homogeneous makes
max_readout_err ≡ mean_readout_err, which is a perfectly correlated pair. That
makes the whitened covariance singular and puts every real circuit at enormous
Mahalanobis distance — our OOD detector flagged 100% of real circuits for three
debugging cycles because of it. Real hardware is heterogeneous: on ibm_fez the
worst qubit carries 31× the median readout error. See
FINDINGS §7; this is probably the most transferable lesson
in the repository.
API
backend = AntonBackend("ibm_fez") # or "aer" for local
decision = backend.predict(kernel) # recommend, execute nothing
decision.action, decision.reason, decision.is_ai
result = backend.run(kernel, shots=10000) # policy chooses
result = backend.run(kernel, shots=10000, mitigation="raw") # or force one
features = backend.features(kernel) # 22 physical features, no family labels
snapshot = backend.snapshot # live calibration
snapshot.heterogeneity() # max/median spread per quantity
Modes: auto (default), raw, readout, dd, zne, pec. No technique is
silently bundled with another.
run() is a counts-based Sampler path and applies readout correction. ZNE and
PEC are expectation-value techniques — use MitigationAction.apply() with an
EstimatorV2.
Provenance
Every run writes an ExperimentRecord to ~/.anton/experiments/: git commit,
version, backend, calibration timestamp, OpenQASM 2, ISA depth, gate counts,
physical qubits, shots, mitigation parameters, raw and mitigated counts,
diagnostics, IBM job ids.
Examples
| file | needs QPU |
|---|---|
quickstart.ipynb |
no |
01_hello_ibm.py |
no (--backend aer) |
02_compare_mitigation.py |
no |
03_inspect_hardware.py |
account only, no QPU time |
Tests
pip install cudaq-ibm-anton[dev] && pytest tests/
18 tests, no IBM account required.
Scope
Physical (NISQ) error mitigation on unencoded circuits. Not quantum error correction — QEC decoders consume detector syndromes from an encoded logical qubit, which GHZ, QFT and VQE circuits do not produce. The two should not be mixed and this package does not mix them.
Contributing
The most useful contributions right now:
- Run the benchmark on a non-IBM-fez device. The single biggest gap is cross-device validation of the threshold rescaling.
- Beat the rule.
LearnedPolicyis the interface; FINDINGS §5 explains where our attempt failed and why. - Characterise PEC. Exposed but never measured here.
License
Apache-2.0. No third-party source vendored, no model weights bundled. See
NOTICE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cudaq_ibm_anton-0.1.0.tar.gz.
File metadata
- Download URL: cudaq_ibm_anton-0.1.0.tar.gz
- Upload date:
- Size: 29.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4950117de34d7658230944e2b02786334124630d229a359074e142674eebfc03
|
|
| MD5 |
7f573a59a59d1947ff9cc8214dc02c86
|
|
| BLAKE2b-256 |
f1ea01de92045eca9a1186515885205d06e85788241d574e7d8c2cbf6cfd115b
|
File details
Details for the file cudaq_ibm_anton-0.1.0-py3-none-any.whl.
File metadata
- Download URL: cudaq_ibm_anton-0.1.0-py3-none-any.whl
- Upload date:
- Size: 30.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f9b0f5efa7ef76f46718e2cafb81a1a8fe8915f6aad0755ea25cf8095868f0d7
|
|
| MD5 |
7d5828412d902434798f9d82555b4f0e
|
|
| BLAKE2b-256 |
bc6262268ca428ff6102c0fdb0da2da98ae3e5c43a28de9a82619fa12ff1d150
|