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]>
16 KiB
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
- 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.
- 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.
- 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).
- 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:
{ "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:
{
"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; …"
}
-
basisis one of three gate sets, and every gate is of it:basis gates rzsxxczrz(t),sx,x,czprxczprx(t, p),czgpizzgpi(p),gpi2(p),zz(t) -
An angle is a fraction of a full turn,
[m, k]meaningm/2^kof 2π, with0 ≤ m < 2^kandk ≤ 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).
-
measuregives, for each classical bit0 … 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. -
qasm3is the same program as OpenQASM 3 on physical qubits ($p), theprxczandgpizzgates defined in it from the standard gates, for a provider that reads OpenQASM. It is informative: where the two differ, thegatesare 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_providerwithconfirm_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 itsdescribeanswer;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.