open-standards/wai/wai-quantum-py
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Transaction Science ccf91e9abc
All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 10s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (pull_request) Successful in 10s
wai-quantum contract / does this change touch it (pull_request) Successful in 13s
wai-quantum docs, course and locales / does this change touch it (pull_request) Successful in 7s
wai-quantum for Python / does this change touch it (pull_request) Successful in 6s
wai-quantum contract / the policy is linked, and the deprecations page reads (pull_request) Successful in 7s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (pull_request) Successful in 2m9s
wai-quantum for Python / python · macos (pull_request) Successful in 1m48s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (pull_request) Successful in 2m41s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 3m24s
wai-quantum contract / schemas and the committed tool lists · linux (pull_request) Successful in 3m2s
wai-quantum docs, course and locales / the Studio's words in every locale · linux (pull_request) Successful in 46s
wai-quantum docs, course and locales / the course and the command line's languages · linux (pull_request) Successful in 2m11s
wai-quantum contract / API compatibility · linux (pull_request) Successful in 4m32s
wai-quantum for Python / python · linux (pull_request) Successful in 2m33s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 6m37s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 8m48s
wai-quantum docs, course and locales / every command and snippet in the docs · linux (pull_request) Successful in 20m36s
wai-quantum for Python: installed from the Python Package Index
wai-quantum 0.1.0 is on the Python Package Index: the five wheels of the
wai-quantum-cli-v0.2.2 release (macOS arm64 and x86_64, manylinux arm64 and
x86_64, Windows x86_64), each matching its published .sha256 and checked by
twine. A fresh Python 3.9 environment installed it from the index and passed
tests/smoke.py.

The README (and its translation) said the package was not on a public index:
it now installs with `pip install wai-quantum` (and `[widgets]`), with the
release's wheels as the other way. The docs check passes both pages, the
Python blocks run against the installed package.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-29 08:15:29 -04:00
..
ci wai-quantum contract over the durable service: sets, provenance, dry runs, the receipt key set and the lease, each with its schema 2026-10-28 08:42:53 -04:00
notebooks wai-quantum 0.7.0 integrated: the generated material on the final tree, the pins it moves, and what replaying every job found 2026-10-28 08:43:06 -04:00
python/wai_quantum wai-quantum-py: error correction and mitigation from Python 2026-10-28 08:42:54 -04:00
src wai-quantum-py: error correction and mitigation from Python 2026-10-28 08:42:54 -04:00
tests wai-quantum 0.7.0 integrated: the generated material on the final tree, the pins it moves, and what replaying every job found 2026-10-28 08:43:06 -04:00
Cargo.lock wai-quantum 0.7.0, wai-quantum-cli 0.2.0, wai-quantum-qpu 0.3.0 and the job service 0.2.0: the release's versions, its changelog heading, and every lock 2026-10-27 23:44:28 -04:00
Cargo.toml wai-quantum-py: error correction and mitigation from Python 2026-10-28 08:42:54 -04:00
pyproject.toml wai-quantum-py: WAI Quantum in Python and notebooks, in-process 2026-10-27 19:51:08 -04:00
README.md wai-quantum for Python: installed from the Python Package Index 2026-10-29 08:15:29 -04:00
README.zh-CN.md wai-quantum for Python: installed from the Python Package Index 2026-10-29 08:15:29 -04:00

wai-quantum for Python

WAI Quantum from Python and notebooks, in-process. The package carries the WAI Quantum job engine (the Rust crate wai-quantum's engine, through a pyo3 extension module), so a job planned, run and sealed here gives the same plan, the same result and the same receipt as the wai-quantum command line, the job service, the MCP servers, the Studio and the desktop app.

  • The engine: run, plan, lint, draw (text and SVG), layout, critique, improve, shortcuts, equivalent, verify, seal, canonical, digest, methods, accuracy_classes, bloch, history, estimate, verify_estimate, qec_code, qec_decode and qec_threshold.
  • Receipts: every result is sealed with a key you pass (wai_quantum.Key), and any receipt (one made here, by the command line, by the service or in a browser tab) is checked here, offline.
  • The job service: wai_quantum.Service, a client in the standard library alone, for https://wai.transaction.science/quantum-jobs/v1.
  • Notebook widgets: Circuit, Result, Receipt and Bloch, with static HTML and SVG fallbacks that render in any notebook.

Install

From the Python Package Index. One wheel per platform serves every Python from 3.9 on (the stable ABI, abi3): macOS arm64 and x86_64, Linux x86_64 and arm64 (manylinux 2.17), Windows x86_64.

pip install wai-quantum              # the engine and the service client: no other dependency
pip install "wai-quantum[widgets]"   # and the notebook widgets (anywidget)

The same wheels are attached, each with its SHA-256, to the wai-quantum-cli-v<version> release on the forge that carries them:

pip install wai_quantum-0.1.0-cp39-abi3-<platform>.whl

To build the wheel yourself, with Rust 1.98 and maturin 1.15.0:

cd wai/wai-quantum-py
maturin build --release --locked -o dist
pip install dist/wai_quantum-*.whl

Use

import wai_quantum as wq

bell = "OPENQASM 3.0; qubit[2] q; bit[2] c; h q[0]; cx q[0], q[1]; c = measure q;"
key = wq.Key.generate()            # or Key.from_file(...), Key.demo(); by default, this user's key
job = wq.run(bell, shots=1000, key=key)

job.method, job.accuracy           # ('stabilizer', 'exact')
job.counts                         # {'00': 522, '11': 478}
job.probabilities()                # each count over the shots: sampled frequencies
wq.verify(job).valid               # True: the receipt seals this request and this result

A job is the service's form as a plain dict (status, method, why, estimate, request, result with its receipt), so json.dumps(job) writes what the service would send. Its helpers read the parts most often wanted: counts, shots, probabilities(), expectations, amplitudes, method, accuracy, receipt, verify().

run takes OpenQASM 3 (or 2) text, a path to a file of it, or a job dict, and the job's fields as keywords: shots, seed, method (auto by default), observables ("Z0 Z1" or {"pauli": "X3 Y4", "coeff": 0.5}), amplitudes (bit strings, qubit n − 1 first), noise (depolarizing_1q, depolarizing_2q, amplitude_damping, dephasing, readout), mitigate (with noise: zne, pea, pec, cdr, vd or readout, or {"method", ...} with its options) and options (per method; wq.methods() lists them). memory_gb (16, the command line's default) bounds what a job may take here, so a job plans onto the same method on both.

src = "OPENQASM 3.0; qubit[2] q; h q[0]; t q[0]; t q[0]; cx q[0], q[1];"
a, b = "qubit[1] q; h q[0]; x q[0]; h q[0];", "qubit[1] q; z q[0];"
job = wq.run(bell, shots=100, intent={"distribution": {"00": 0.5, "11": 0.5}}, key=key)
job.verdict["level"], job.verdict["holds"]      # ('proof', True): how far the engine checked, sealed
wq.verdict(job, {"distribution": {"00": 1.0}})  # another intent, worked out here: says it is not sealed
wq.repair("OPENQASM 3.0;\nqubit[1] q\nh q[0];\n")   # the fix on the flagged line, and what needs a person
wq.plan(bell)                                   # the method and why, without running
wq.lint("qubit[1] q;\nfoo q[0];")               # {'ok': False, 'error': 'line 2: unknown gate foo', 'line': 2}
print(wq.draw(bell))                            # the text drawing, as `wai-quantum draw` prints it
svg = wq.draw(bell, format="svg")               # the Studio's picture as an SVG document
wq.critique(src); wq.improve(src)               # measures and smells; variants proved equivalent
wq.equivalent(a, b)                             # {'verdict': 'equivalent', 'how': 'tableau', 'why': ...}
wq.bloch("OPENQASM 3.0; qubit[1] q; ry(0.3) q[0];", key=key)   # each qubit's reduced state, from the engine
e = wq.estimate(src, qubit="ns-e4", budget=1e-3, key=key)     # what a fault-tolerant machine needs, each estimate sealed
wq.verify_estimate(e)                           # the seals, every receipt, and the request re-run here
wq.estimate(counts={"numQubits": 100, "cczCount": 5000})      # or a logical-counts (or routine=) document

estimate gives the program's logical counts (logical qubits with the ancillas a gate under several controls takes, measurements, T gates, Toffolis, rotations and the T gates in their synthesised words, T depth and depth), the error budget's synthesis share and each rotation's part of it, the code model's calibration from the engine's own decoders, the estimate on the physical qubit and the frontier between physical qubits and runtime, every estimate sealed in the resource-estimate receipt family. export="counts" or "routine" adds the counts as a document. What each number means and assumes is on the estimate page.

c = wq.qec_code("color:7", key=key)            # its checks, k, the decoders it takes, its distance certified
wq.qec_code("color:7", certificate=c)          # the certificate's witnesses and search run again
wq.qec_decode("surface:5", error=[3, 12])      # its syndrome decoded, the residual judged
wq.qec_decode(memory=(5, 5, 0.003), shots=20000, decoder="correlated")   # a circuit-level failure rate
wq.qec_threshold(distances=[3, 5, 7], p=[0.002, 0.004], shots=20000)      # rates with their errors
job = wq.run(src, observables=["Z0 Z1"], noise={"depolarizing_2q": 0.02}, mitigate="zne", key=key)
job["result"]["mitigation"]                     # each value raw and mitigated with its error bar, and what the method assumes

qec_code, qec_decode and qec_threshold are the engine's error correction, as the command line's wai-quantum qec and both MCP servers' qec_* tools answer it, each answer sealed in a job's form (wq.verify checks it). A distance certificate gives, in each sector, the least weight of a logical found, a witness of that weight, and every lighter support ruled out by an exhaustive search; a search stopped by bound settles nothing past it. What each decoder guarantees, and each mitigation method's bias and variance, are on the error-correction page.

Keys and receipts

Key.demo() is the published demo key (the command line's --demo-key): receipts signed with it repeat byte for byte everywhere and prove only that the result is unaltered. Key.generate() makes a key; key.save(path) writes it readable by its owner only, in the command line's form (64 hex digits), so wai-quantum run --key path signs with the same key. With no key given, run signs with this user's key, the command line's own ($WAI_QUANTUM_KEY, else ~/.config/wai/quantum-key, made on first use).

A receipt is an Ed25519 signature over BLAKE3 of the request and the result in their RFC 8785 canonical JSON. wq.canonical(value) gives those bytes and wq.digest(request, result) the digest; wq.seal(request, result, key=...) seals any result. A refusal raises wq.EngineError with the engine's own message, its line where it names one, and its code: the reader's (Q103) or the job layer's (J011), as the message ends with it. wq.repair(program, proposed=...) raises one with J021 for a proposal that changes a line no diagnostic flags. The verdict page has the levels and the rules.

The job service

svc = wq.Service()                  # $WAI_QUANTUM_SERVICE, or https://wai.transaction.science/quantum-jobs/v1
job = svc.run(bell, shots=1000)     # submit and wait
rec = svc.submit(bell, shots=1000)  # or: submit, then status / result / cancel
job = svc.result(rec["id"])
svc.verify(job)                     # checked here, offline; compare its key with svc.describe()["receipt_key"]

The client uses urllib and names itself wai-quantum-python/<version> in each request's User-Agent: the service's edge refuses requests that carry a generic library's default agent. A refusal, a failed or cancelled job, or a service out of reach raises wq.ServiceError with the HTTP status and the service's words.

Notebooks

h = wq.history("start", {"source": src, "by": {"kind": "person"}, "at": 1}, key=key)["history"]
program = "OPENQASM 3.0; qubit[2] q; ry(0.3) q[0]; h q[1];"
wq.Circuit(bell)                          # the engine's drawing; click a gate: .selected is its operation
wq.Circuit(src, history=h)                # each gate marked with whose hand it is in (wq.history)
wq.Result(job)                            # the counts as bars (or frequencies), the method, the accuracy class, the receipt's check
wq.Receipt(job)                           # signer, digest, and a verified / INVALID badge the engine computed here
wq.Bloch(program, key=key)                # each qubit's Bloch vector; drag to turn the view

Each widget's front end is a small plain ES module (python/wai_quantum/static/). Without the widgets extra, or in a front end without the widget extension, each renders as static HTML (and a Circuit as SVG); a Job displays as its result's card. Every widget shows what the engine said and computes nothing of its own: its data is the engine's answer.

The doc notebooks in notebooks/ run in CI and are held to their stored outputs: getting-started, bell-receipt (a Bell pair with its receipt), equivalence, noisy-run, and service-job (a job on a service started for the run; without one its cells skip).

Versions

The package's public surface is what wai_quantum.__all__ lists and the widgets, with their parameters and what they return. A job is the engine's job JSON, and follows its schemas (wai-quantum/schemas/); its result names the engine that made it (job["result"]["engine"]) and its schema. What each version promises, for how long, and how a change is announced (a deprecated name raises a DeprecationWarning naming its replacement): the policy; what is deprecated: the deprecations.

Tests

ci/check.sh is the whole check, as CI runs it (.github/workflows/wai-quantum-py.yml, on linux and macos): it builds the command line and the job service from this checkout, the wheel with maturin, installs the wheel in a fresh venv with the pinned and hashed test requirements (tests/requirements.lock), runs pytest with every test required, runs the notebooks against a local service, and runs tests/smoke.py on Python 3.9 with the wheel alone. The tests hold the package to the other surfaces: a job run here equals the command line's and the service's (but for each host's time and its word on energy), a receipt sealed here verifies with wai-quantum verify and the command line's verify here, both sign with the same user key, and the widgets' front ends run under node on the data the package builds.

Energy: a run here is not metered; its result says so (energy.unmetered), as the command line's and a browser tab's do. The service meters each job's worker.

Licence: Apache-2.0.