open-standards/wai/extensions/quantum-providers.md
Transaction Science 15b3deac34 wai-quantum-qpu: a provider's described calibration places the program
When a provider's describe answer states errors per qubit or per pair, the
host reads it as a device target and places the program on the qubits and
routes most likely to succeed (engine::targets::layout::place), in the
device's own gates; otherwise the compiler's own layout is kept. The program
is checked against the circuit on the device's connectivity before it is
sent, as before. The plan and the sealed result's compiled block say how it
was placed, with the target's hash and the expected success beside the
fewest-swaps layout's.

The provider protocol now states the durations field's form, and that a
client may place by the described calibration.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 23:44:27 -04:00

330 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# WAI Extension: Quantum Providers (`wai.quantum.provider/1`)
Status: **Draft.** Client, reference provider and conformance checker:
`wai-quantum-qpu` (`src/open_provider.rs`, `src/reference.rs`,
`src/conformance.rs`); vectors: `wai-quantum-qpu/conformance/provider-v1.json`.
The engine's side of a provider job: `wai-quantum/src/engine/provider.rs`.
Keywords **MUST**, **MUST NOT**, **SHOULD** and **MAY** are RFC 2119/8174.
## 0. What this extension adds
Bring your own quantum. WAI provides the engine, the free job service and the
verifier at no cost, and never pays for or resells time on anyone's hardware.
A person connects their own quantum hardware, provider accounts, on-premises
devices or simulators, and every surface treats a provider's result as it
treats a WAI result: planned, its cost shown before it is submitted, checked,
and sealed in a receipt.
This text is the protocol a device speaks so that it can be brought without a
client written for it: JSON messages, one request and one answer at a time,
over HTTP or to a local process (§2). Six operations (§5) describe the
device, estimate a job, submit it, follow it, read its counts and cancel it.
A program travels in the device's own native gates, with every angle an exact
fraction of a turn (§4), so the program the device runs is the one the client
compiled and checked.
A provider is the program or service that answers the messages. A device is
what it runs programs on: a processor, an emulator or a simulator. A client
is what sends the messages; a host is the client's process, which holds the
person's credential and signing key (the command line, the desktop app).
## 1. Principles
1. **The credential stays with the person.** A host keeps a provider's
credential in the operating system's keychain (or, where there is none, a
file only its owner can read, labelled as such) and sends it only to that
provider (§2). A credential MUST NOT appear in a browser page, a log, a
receipt, a command line, an answer the host prints, or the hosted job
service. The job service never routes a job to a provider and never takes
a credential: a request that names a provider is refused there.
2. **Cost before submit.** A client MUST show a job's estimate (§5.2) before
it submits the job, and MUST NOT submit a job that is not free without the
person's explicit confirmation (§7). An unknown cost is not free.
3. **Exact programs.** A client compiles a circuit exactly to the device's
native gates and connectivity and checks the compiled program against the
circuit before sending it. Angles cross the wire exactly (§4).
4. **Measured, not recomputed.** A provider's counts are sealed as measured on
the device it names (§8). Nothing recomputes them.
## 2. Transports
Every message is a JSON object, and so is every answer. A client sends one
message and reads its answer before it sends the next on the same transport.
### 2.1 HTTP
A message is the body of a `POST` to the provider's endpoint (one URL for
every operation), with `Content-Type: application/json`. The answer is the
body of a `200` response, also for an answer with `"ok": false` (§3). Any
other status is a transport failure, not an answer.
A credential, when there is one, goes in `Authorization: Bearer <credential>`.
A client MUST NOT send a credential over `http://` to any host but this
machine (`localhost`, a loopback address); over the network it uses
`https://`.
### 2.2 A local process
The provider is a program the person names, with its arguments, started by
the client without a shell. Each message is one line of JSON on the program's
standard input; each answer is one line of JSON on its standard output, in
order. The program MUST write nothing else to its standard output. Its
standard error is its own log, and a client MAY discard it. The program runs
until its standard input ends.
A credential, when there is one, is in the program's environment as
`WAI_PROVIDER_CREDENTIAL`, never on its command line. A client SHOULD NOT
pass the program another provider's credential.
## 3. Messages and errors
Every message carries:
| field | |
|---|---|
| `protocol` | `"wai.quantum.provider/1"` |
| `op` | `describe`, `estimate`, `submit`, `status`, `result` or `cancel` |
| `settings` | optional: the person's non-secret settings for this provider (a device name, a region), passed as they are |
and the operation's own fields (§5). Every answer carries `"ok": true` and
the operation's fields, or:
```json
{ "ok": false, "error": { "code": "not_found", "message": "no job j-17" } }
```
The `message` is for a person; it MUST NOT quote a credential. The codes:
| code | when |
|---|---|
| `invalid` | the message is malformed: a field missing or of the wrong type, a program that does not read (§4), zero shots, a message without `protocol` |
| `unsupported` | an operation the provider does not offer (`cancel`, `estimate`), or an `op` it does not know |
| `unsupported_version` | a `protocol` other than this one |
| `unauthorized` | no credential, or one the provider does not accept |
| `not_found` | a job the provider does not hold |
| `refused` | a well-formed program the device cannot run: a gate outside its basis, a qubit it does not have, a pair it does not connect, too many shots; or a result asked of a job that has not finished or was cancelled |
| `quota` | the person's account cannot pay for or schedule the job |
| `unavailable` | the device is offline or in maintenance |
| `internal` | anything else |
## 4. The program
`estimate` and `submit` carry a `program` in the device's own gates, on its
physical qubits:
```json
{
"format": "wai.native/1",
"basis": "rzsxxcz",
"bits": 2,
"gates": [
{ "gate": "rz", "qubits": [0], "turns": [[1, 2]] },
{ "gate": "sx", "qubits": [0] },
{ "gate": "cz", "qubits": [0, 1] }
],
"measure": [{ "qubit": 0, "bit": 0 }, { "qubit": 1, "bit": 1 }],
"qasm3": "OPENQASM 3.0; …"
}
```
- `basis` is one of three gate sets, and every gate is of it:
| basis | gates |
|---|---|
| `rzsxxcz` | `rz(t)`, `sx`, `x`, `cz` |
| `prxcz` | `prx(t, p)`, `cz` |
| `gpizz` | `gpi(p)`, `gpi2(p)`, `zz(t)` |
- An angle is a fraction of a full turn, `[m, k]` meaning `m/2^k` of 2π, with
`0 ≤ m < 2^k` and `k ≤ 32`, so every angle crosses the wire exactly. The
gates, as operators on the named qubits, up to a global phase:
- `rz(t)` = exp(−iπt·Z); `sx` = √X; `x` = X; `cz` = diag(1, 1, 1, −1);
- `prx(t, p)` = Rz(2πp)·Rx(2πt)·Rz(−2πp);
- `gpi(p)` = Rz(2πp)·X·Rz(−2πp); `gpi2(p)` = Rz(2πp)·Rx(π/2)·Rz(−2πp);
- `zz(t)` = exp(−iπt·Z⊗Z).
- `measure` gives, for each classical bit `0 … bits − 1`, the physical qubit it
reads at the end of the program. Every bit reads exactly one qubit, and no
qubit is read twice.
- `qasm3` is the same program as OpenQASM 3 on physical qubits (`$p`), the
`prxcz` and `gpizz` gates defined in it from the standard gates, for a
provider that reads OpenQASM. It is informative: where the two differ, the
`gates` are the program.
A provider MUST refuse (`invalid`) a program of another `format`, a gate
outside its `basis`, an angle out of range, or a bit that reads no qubit; and
(`refused`) a program in a basis its device does not run, or on qubits or
pairs it does not have.
## 5. Operations
### 5.1 `describe`
No fields. The answer describes the device:
| field | |
|---|---|
| `protocol` | `"wai.quantum.provider/1"` |
| `provider`, `device` | the provider's name for itself, and the device's |
| `provenance` | `silicon` (a processor), `emulator`, or `simulator`; with `simulator` (or `emulator`) a sentence naming what it is |
| `qubits` | the device's qubit count |
| `native_gates` | the gates it runs; a client compiles to the first of the bases of §4 whose every gate is listed |
| `connectivity` | `"all"`, or the pairs `[a, b]` a two-qubit gate may act on, in either order |
| `durations` | optional: `{"unit", "gates", "measure"}`: the unit (`ns`, `us`, `ms` or `s`), each native gate's duration wherever it runs (`{"sx": 35, "cz": 68}`), and the measurement's |
| `calibration` | `{"id"}`, the calibration snapshot the device is under; optionally `qubits` (`{"qubit", "readout_error", "error_1q", "t1_us", "t2_us"}`) and `edges` (`{"qubits": [a, b], "error"}`), each error from 0 to 1 |
| `billing` | `{"free": true}`, or `{"unit", "price", "currency"}`: the billing unit (`shot`, `job`, `second`, …), the price of one as a decimal string (`"0.0003"`), and the currency or credit it is in; optionally `free_tier`, the free use offered and its limits, as the provider states them |
| `max_shots` | optional: the most shots a job may ask |
| `seeded` | optional: `true` when a job's `seed` makes its counts reproducible (a simulator) |
A client MUST NOT take a device to be free unless `billing` says
`"free": true`, or an estimate says so (§5.2).
A client MAY read a `describe` answer whose `calibration` states errors as a
device target (`wai.quantum.target/1`, `wai-quantum/src/engine/targets`)
and place the program on the qubits and pairs most likely to succeed. The
program it sends is checked against the circuit as every program is (§1), and
the receipt names how it was placed (§8).
### 5.2 `estimate`
Fields: `program` (§4), `shots`. The answer, before anything is submitted:
| field | |
|---|---|
| `free` | whether this job is free |
| `amount`, `currency` | when it is not: the cost, the amount as a decimal string |
| `unit` | optional: the billing unit the cost counts |
| `seconds` | optional: how long it is expected to take, queue included |
| `note` | optional |
A provider that cannot estimate answers `unsupported`; the client then treats
the cost as unknown (§7).
### 5.3 `submit`
Fields: `program`, `shots`, and optionally `seed` (a seeded simulator uses
it; a device ignores it). The answer's `job` is the job's id.
### 5.4 `status`
Field: `job`. The answer's `status` is `queued`, `running`, `done`, `failed`
or `cancelled`, with an optional `reason`.
### 5.5 `result`
Field: `job`. The answer, once the job is `done`:
| field | |
|---|---|
| `counts` | an object from outcomes to counts (§6), summing to the shots |
| `shots` | the shots |
| `calibration` | optional: the calibration snapshot the job ran under, when it is not the one `describe` names |
| `charged` | optional: `{"amount", "currency"}`, what the job cost |
| `energy` | optional: `{"kwh"}`, the provider's own figure for the job's energy |
A `result` asked twice of the same job MUST give the same counts.
### 5.6 `cancel`
Field: `job`. The answer's `status` is `cancelled`, or `done` for a job that
had already finished. A provider that cannot cancel answers `unsupported`.
## 6. Counts and bit order
An outcome is a string of `bits` characters, `0` or `1`, the highest bit
first: its last character is bit 0. `"01"` is bit 0 read as 1 and bit 1 read
as 0. This is OpenQASM's order, and the job service's.
The client compiles each logical qubit `i` of the person's circuit to end on
the physical qubit that bit `i` measures, and reads the counts back onto the
program's own classical bits. The first job on a new provider SHOULD be the
probe (an `X` on qubit 0, every qubit measured), whose counts must all land
on `…01`; the conformance vectors begin with it (§9).
## 7. Cost and confirmation
A job is free when the provider's estimate says `"free": true`, or, for a
provider that gives no estimate, when its `billing` says so. Every other job,
an unknown cost included, is not free. A client:
- MUST show the estimate (or that the cost is unknown) before submitting;
- MUST NOT submit a job that is not free without the person's explicit
confirmation for that run: a command-line flag (`wai-quantum run --provider
NAME --yes`), an agent tool's argument the agent sets only after the person
agreed (`run_on_provider` with `confirm_cost: true`), or a dialog (the
desktop app);
- MUST NOT hold a standing confirmation across runs.
A tool that can submit a job that is not free MUST say in its description
that it can spend the person's money.
## 8. The receipt
The host seals each provider job as the job service seals its own: an Ed25519
signature, by the host's key, over the request as submitted and the result
as returned, each in its RFC 8785 canonical text, verified by the same
verifier. The result's `method` is `provider` and its accuracy class is
`measured`. It carries:
- `provider`: the person's name for it, its kind, its protocol, the device,
its provenance, and the BLAKE3 hash of its `describe` answer;
- `calibration`: the calibration snapshot the job ran under;
- `compiled`: the basis, the BLAKE3 hash of the compiled program's OpenQASM 3
text, its gate and swap counts, its initial and final layout, the check it
passed before it was sent, and how it was placed (`placement`: with a
calibration read as a target, the target's hash, the layout's expected
success and the fewest-swaps layout's);
- `job`: the provider's job id and the status checks before it finished;
- `counts`, keyed by the program's classical bits (§6);
- `ideal`: the circuit's exact distribution set beside the counts (up to 22
qubits): the total variation distance, the Hellinger fidelity, and the
distance the shots alone would leave on average;
- `cost`: the estimate shown before submitting, what the provider says it
charged, and whether the person confirmed a run that was not free;
- `energy`: unmetered here, with the provider's own figure, labelled as its
own, when it gives one.
What the receipt proves: that this result, with these counts, is what the
signer's host received for this request from the provider and device it
names, under the calibration snapshot named, for the compiled program whose
hash it carries; and that the host compiled that program exactly and checked
it against the circuit before sending it. What it does not prove: that the
device ran that program, or ran anything. The counts are the provider's word,
and they are not reproducible by recomputation: the same job run again gives
other counts. The `ideal` distance says how far they are from what the
circuit gives; a distance well above the sampling distance is the device's
error, or a program the device did not run as sent.
## 9. Conformance
`wai-quantum-qpu/conformance/provider-v1.json` holds the vectors:
- **messages**, sent as they are: an unknown operation (`unsupported`),
another version (`unsupported_version`), a message without its protocol
(`invalid`), a job that does not exist (`not_found`), malformed programs
(`invalid`);
- **programs**, each compiled to the device, estimated, run with seed 1, and
checked by its counts: the probe, bits named out of order, a phase only
interference shows, a phase kicked back by two quarter-turn controlled
phases, a Bell pair, and a triangle of two-qubit gates that needs routing
on a line. The thresholds leave room for a device's noise;
- **reference**: the counts the reference provider gives for each program at
seed 1, so its determinism is held on every platform.
The checker (`wai-quantum providers test NAME`) also checks the `describe`
answer in full, that zero shots are `invalid`, and that a program in another
basis is refused. It runs the programs only when every estimate says free,
or the person allows the spend (`--yes`).
The reference provider (`wai-quantum providers reference --stdio`, or
`--http ADDR`) is this protocol around WAI's exact simulator. It is the
provider the tests run against over both transports, and the template to
copy: a provider for any device is the reference with its simulation replaced
by the device.
## 10. Versioning
`wai.quantum.provider/1` is this text. A later version is a new `protocol`
string; a provider that speaks only this one answers `unsupported_version` to
another. Fields marked optional may be added to answers without a new
version, and a client MUST ignore fields it does not know.