open-standards/wai/extensions/quantum-providers.md
Transaction Science a1bbce7fad
Some checks failed
wai-audit collector and player integration / does this change touch it (push) Successful in 21s
WAI operation catalogue / does this change touch it (push) Successful in 21s
wai-quantum clippy / does this change touch it (push) Successful in 23s
wai-quantum command line / does this change touch it (push) Successful in 27s
wai-quantum docs, course and locales / does this change touch it (push) Successful in 21s
wai-quantum error correction and mitigation / does this change touch it (push) Successful in 15s
wai-quantum qudit jobs / does this change touch it (push) Successful in 18s
wai-quantum for Python / does this change touch it (push) Successful in 22s
wai-quantum extension points / does this change touch it (push) Successful in 23s
wai-quantum service / does this change touch it (push) Successful in 24s
wai-quantum studio and desktop / does this change touch it (push) Successful in 21s
wai-quantum studio and desktop / desktop app · windows (push) Waiting to run
wai-quantum service / service energy · linux packages (push) Failing after 8s
wai-quantum variational and algorithm library / does this change touch it (push) Successful in 26s
wai-quantum verdicts and repairs / does this change touch it (push) Successful in 19s
wai-quantum views in agent hosts / does this change touch it (push) Successful in 19s
wai-quantum on Windows / does this change touch it (push) Successful in 32s
wai-sink-android native sink library / does this change touch it (push) Successful in 21s
wai-quantum on Windows / engine · windows (push) Waiting to run
wai-quantum on Windows / command line · windows (push) Waiting to run
wai-quantum on Windows / job service · windows (push) Waiting to run
wai-quantum on Windows / python · windows (push) Waiting to run
wai-sink-swift native sink package / does this change touch it (push) Successful in 25s
wai-quantum for JavaScript / does this change touch it (push) Successful in 2m37s
WAI Studio / does this change touch it (push) Successful in 12s
wai-quantum OpenQASM reader / does this change touch it (push) Successful in 2m43s
wai-quantum editors / does this change touch it (push) Successful in 2m55s
wai-quantum pulse and dynamics / does this change touch it (push) Successful in 2m42s
wai-quantum contract / does this change touch it (push) Successful in 2m59s
wai-quantum other formats / does this change touch it (push) Successful in 2m48s
wai-quantum-landscape-fpga reference design / does this change touch it (push) Successful in 2m45s
wai-quantum resource estimation / does this change touch it (push) Successful in 2m51s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 11s
WAI examples / WAI examples · does this change touch them (push) Successful in 22s
wai-quantum landscape stage 0, the sampler against the exact law / does this change touch it (push) Successful in 10s
wai-quantum landscape energy to solution / does this change touch it (push) Successful in 17s
wai-quantum energy-landscape jobs / does this change touch it (push) Successful in 12s
wai-audit collector and player integration / collector · linux (push) Successful in 3m47s
wai-quantum clippy / clippy, warnings denied, every feature set · linux (push) Successful in 3m46s
wai-quantum docs, course and locales / the Studio's words in every locale · linux (push) Successful in 16s
wai-quantum wasmQ / does this change touch it (push) Successful in 19s
wai-quantum qudit jobs / the module and the conformance set under wasm32-wasip2 (wasmtime) (push) Successful in 2m28s
wai-quantum docs, course and locales / the course and the command line's languages · linux (push) Successful in 3m53s
wai-quantum studio and desktop / studio · browser (push) Successful in 4m10s
WAI operation catalogue / the catalogue and every door · linux (push) Successful in 12m50s
wai-quantum extension points / the registry under wasm32-wasip2 (wasmtime) (push) Successful in 5m9s
wai-quantum verdicts and repairs / the intent set and the verdict under wasm32-wasip2 (wasmtime) (push) Successful in 1m37s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux, release (push) Successful in 2m35s
wai-quantum verdicts and repairs / verdicts, repairs and the intent set · linux (push) Successful in 2m58s
wai-quantum error correction and mitigation / codes, decoders, texts and mitigation · linux, release (push) Successful in 10m5s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Successful in 2m21s
wai-quantum studio and desktop / desktop app · linux (push) Successful in 7m30s
wai-quantum OpenQASM reader / corpus under wasm32-wasip2 (wasmtime) (push) Successful in 51s
wai-quantum contract / the policy is linked, and the deprecations page reads (push) Successful in 13s
wai-quantum qudit jobs / the module, the job, the conformance set, the referee and the verb · linux (push) Successful in 12m45s
wai-quantum views in agent hosts / views · the page and its engine (push) Successful in 4m54s
wai-quantum extension points / the registry and the example · linux (push) Successful in 11m13s
wai-quantum for JavaScript / javascript · linux (push) Successful in 4m55s
wai-quantum for Python / python · linux (push) Successful in 12m31s
wai-quantum OpenQASM reader / reader and corpus · linux (push) Successful in 4m0s
wai-quantum pulse and dynamics / reader, simulator and pulse jobs under wasm32-wasip2 (wasmtime) (push) Successful in 1m22s
wai-quantum command line / command line · linux (push) Successful in 19m37s
wai-quantum pulse and dynamics / reader, simulator and pulse jobs · linux, release (push) Successful in 3m20s
wai-quantum editors / editors · linux (push) Successful in 3m41s
wai-quantum other formats / the set under wasm32-wasip2 (wasmtime) (push) Successful in 3m7s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 2m44s
wai-quantum landscape stage 0, the sampler against the exact law / the small tier, to the byte · linux (push) Successful in 1m4s
wai-quantum service / service · linux (push) Successful in 17m3s
wai-quantum landscape energy to solution / the small tier, to the byte · linux (push) Successful in 2m23s
wai-quantum views in agent hosts / views · a scripted host (push) Successful in 14m9s
wai-quantum contract / schemas and the committed tool lists · linux (push) Successful in 10m9s
wai-quantum other formats / readers, writers and the set · linux (push) Successful in 9m28s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 7m43s
wai-quantum contract / API compatibility · linux (push) Successful in 11m25s
wai-quantum wasmQ / the reference modules rebuild to their modules of record · linux (push) Successful in 34s
WAI Studio / Studio in WebKit · linux (push) Successful in 14m43s
wai-quantum resource estimation / estimation and calibration · linux, release (push) Successful in 10m56s
wai-quantum energy-landscape jobs / native against WebAssembly, over many seeds (push) Successful in 8m32s
wai-quantum wasmQ / interpreter and vectors under wasm32-wasip2 (wasmtime) (push) Successful in 6m27s
WAI examples / WAI examples · portable · linux (push) Successful in 11m57s
wai-quantum wasmQ / profile, engines, reference modules, adversarial, vectors · linux (push) Successful in 8m56s
wai-quantum energy-landscape jobs / the module under wasm32-wasip2 (wasmtime) (push) Successful in 13m28s
wai-quantum-landscape-fpga reference design / the member against the model of the logic, and the logic against the model · linux, release (push) Successful in 22m33s
wai-quantum energy-landscape jobs / reader, kernels, answers and the stated result · linux, release (push) Successful in 17m1s
wai-quantum docs, course and locales / every command and snippet in the docs · linux (push) Successful in 40m14s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Successful in 2m13s
wai-quantum command line / command line · macos (push) Successful in 2m55s
wai-quantum qudit jobs / the same bits · macos (push) Successful in 1m47s
wai-quantum for Python / python · macos (push) Successful in 3m41s
wai-quantum studio and desktop / desktop app · macos (push) Successful in 5m34s
wai-quantum service / service · macos (push) Successful in 7m44s
wai-quantum editors / editors · macos (push) Successful in 1m53s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Successful in 9m39s
wai-quantum pulse and dynamics / the same bits · macos (push) Successful in 1m33s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Successful in 6m5s
wai-quantum landscape stage 0, the sampler against the exact law / the small tier, to the byte · macos (push) Successful in 33s
wai-quantum extension points / the registry and the example · linux-arm64 (push) Successful in 7m49s
wai-quantum landscape energy to solution / the small tier, to the byte · macos (push) Successful in 2m5s
wai-quantum-landscape-fpga reference design / the board's code on the board's processor's architecture · linux-arm64, release (push) Successful in 1m50s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (push) Successful in 4m21s
wai-quantum pulse and dynamics / the same bits · linux-arm64 (push) Successful in 3m11s
WAI Studio / Studio in Chromium and Firefox · macos (push) Successful in 11m4s
wai-quantum energy-landscape jobs / the same bits · macos (push) Successful in 7m36s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Successful in 10m54s
wai-quantum wasmQ / profile, engines, reference modules, adversarial, vectors · macos (push) Successful in 2m40s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (push) Successful in 21m33s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Failing after 28m52s
WAI examples / WAI examples · android-app · linux-arm64 (push) Successful in 15m51s
wai-quantum energy-landscape jobs / the same bits · linux-arm64 (push) Successful in 19m35s
WAI Quantum: the sign-off loop on the job service, in Python and npm; a grade held to the service's bounds case by case; a whole task set graded
The sign-off loop (check, grade, attest; 4b17804c2) ran on the command line and
its MCP server. It now runs wherever the engine does, with the same answers.

- engine::front holds the check, the attestation and the loop's MCP tools
  (check_quantum_program, grade_quantum_program, attest_quantum_job, in one
  wording with the sealer named), which the command line, both MCP servers, the
  Python package and the npm package call; the command line keeps its flags and
  its printing. The output schemas of check and attest move from the command
  line's local-tools.v1.json into the engine's tools.v1.json ($defs/check,
  $defs/attest), which both servers name.
- The job service serves the three tools on its MCP server and as POST
  /v1/check, /v1/grade and /v1/attest (GET /v1, the OpenAPI document and the
  README list them), a grade and a sign-off sealed with its own key, their
  energy unmetered with the reason. Each is counted as a check, held to its
  bounds and made in a reading child, each case within half the child's memory.
  A check of a task, a grade, and a sealed grade POST /v1/verify works out again
  are now held case by case (analysis::grade_bounds: each case's intent that
  compares the program with a reference held to the equivalence check's bound
  with the program written out at that case's inputs, and the task's job read as
  a job's is), where before a grade was remade within the reading child's budget
  whole; a case past them is refused at that case (J031, "case N: ...", naming
  wai-quantum grade). An attestation holds what verifying the job works out
  again and the intent its sign-off checks to the same bounds (naming
  wai-quantum attest).
- Python: wq.check, wq.grade, wq.attest (and _engine.check/grade/attest); npm:
  QuantumEngine.check, .grade, .attest; each with a test (test_engine.py,
  smoke.mjs, a wasm32 test in wai-web) and a README example.
- The instructions both MCP servers give at initialize describe the loop
  (guide::LOOP), and the committed lists (docs/local-mcp.json,
  docs/service-mcp.json), llms.txt, llms-full.txt and the skill's tools.md are
  written again from the live lists.
- A whole task set graded: wai-quantum grade --set FILE --programs ANSWERS (the
  model harness's set; the programs a JSON object of programs or of a model's
  answers as eval run --scripted reads them, or a directory of NAME.qasm files),
  or --course --programs DIR; engine::grade::batch, wai.quantum.grades/1
  (schemas/grades.v1.json), quantum-providers §8.10 "A task set graded": one
  sealed grade a task, missing and refused tasks said so, the set's digest and
  a summary. The batch is not sealed; wai-quantum verify works it out again
  (grade::check_batch): each grade checked and made again, each entry's name and
  grade held to what its grade seals, a refused entry refused again, the digest
  and the summary made again from the sealed results. The same set, programs and
  key give the same bytes. quantum-conformance/grades/ holds a batch and six
  changed ones with what a reader must say (written by hand from §8.10),
  natively and under wasm32-wasip2 (tests/grades_conformance.rs), including one
  that shows what a batch does not: a grade dropped as missing with the summary
  made to agree is valid; that a task had no program is the grader's statement.
- wai-web's pinned digest of a level-2 compile (COMPILE_DIGEST) follows the
  compile of 7c00ef138, which writes each region of CNOT and phase gates again
  at level 2 and so changed those sealed bytes; wai-web wasm32 runs on pull
  requests and nightly only, so nothing had run it since. Native and wasm32 give
  the new digest alike. The npm smoke runs the loop on an engine of 1 GiB: one of
  256 MiB says an intent's exact check is unchecked, and why (asserted too).
- Which trust list an attester used stays outside the seal: sealing it would add
  a member to the sign-off's closed request, which every released verifier
  refuses (J001), and it would show only which keys the attester trusted, which
  is each reader's to decide; the changes entry and the skill say so.

Tests: engine::front, engine::grade (the batch), the guide's instructions,
service analysis (bounds case by case), mcp (an agent runs the loop over the
service's server; every tool's answer held to its output schema), verify.rs
(the loop over HTTP in a running service, its grade and sign-off verified),
the command line's front_door.rs (a whole set graded through the binary,
verified, a changed summary found) and check_schemas.py (the batch and an
attestation held to their schemas). wai-quantum-verdict.yml, -service.yml,
-cli.yml and wai-web-wasm.yml require the new tests by name. schema::FILES gains
grades.v1.json (acknowledged in the changes entry).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-11 17:14:39 -05:00

1925 lines
136 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 device may also take energy-landscape jobs
([quantum-landscape](quantum-landscape.md), and its second version,
[quantum-landscape-2](quantum-landscape-2.md)), which carry no circuit: they travel in
that extension's own form, and come back as its answer (§11).
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. **Returned, not recomputed.** A provider's counts are sealed as the device
it names returned them (§8): `measured` for a processor, `simulated` for a
simulator or an emulator, which no device measured. The result says where
the counts come from, since counts rebuilt from probabilities are derived
and not shot records, and what was set off at the provider. 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, a resonator used out of turn (§4), 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 four 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)` |
| `prxrzxx` | `prx(t, p)`, `rz(t)`, `xx(t)` |
A program in `rzsxxcz` or `prxcz` may also hold `move` on a device with
computational resonators (below).
- 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);
- `xx(t)` = exp(−iπt·X⊗X), the two-qubit gate of the Mølmer–Sørensen
type: `xx(¼)` = exp(−iπ/4·X⊗X) entangles fully. It is OpenQASM's
`rxx(2πt)`, and a device that takes its angle θ in units of π, as
exp(−iπθ/2·X⊗X), takes θ = 2t: `xx(¼)` is θ = ½, and every angle stays
exact. In `prxrzxx`, `xx(t)` takes 0 ≤ t ≤ ¼ and `prx(t, p)` takes
0 ≤ t ≤ ½, the ranges devices of that gate set take; a client writes
`cz` as `xx(¼)` between `prx(¼, ¼)` (Ry(π/2)) on each qubit before and
`prx(¼, ¾)` (Ry(−π/2)) after, the `Z` rotations it leaves carried in the
phases, so no program it compiles needs more;
- `move` on `[q, r]`, a qubit and a computational resonator (§5.1): the
qubit's state into the empty resonator, leaving the qubit in |0⟩, or back
out to the qubit that moved it in. It is defined only while one of the
two holds |0⟩, where it exchanges their states up to a phase the device
leaves undefined (on |qubit, resonator⟩: |10⟩ → a*·|01⟩ in, |01⟩ →
a·|10⟩ out), which the `move` back cancels.
- A program uses a resonator in turn: a `move` into a resonator only when it
is empty, by a qubit whose state is in no resonator; the `move` out by the
qubit that moved in; no gate on that qubit between the two; only `move`
and `cz` on a resonator, which is never measured; and every resonator empty
at the end. A `cz` between a qubit that moves into a resonator and one
coupled to it is then `move · cz(partner, resonator) · move`, whatever `a`
is.
- `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`, `gpizz` and `prxrzxx` gates defined in it from the standard gates
(`xx` in radians, as `rxx`), and `move` as the exchange it is on every state
a program in turn reaches, 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` (a `move` outside `rzsxxcz` and `prxcz`), an angle out of
range (in `prxrzxx`, an `xx` past ¼ or a `prx` past ½ of a turn), or a bit
that reads no qubit; and (`refused`) a program in a basis its device does not
run, that uses a resonator out of turn, or on qubits or pairs it does not
have.
## 5. Operations
An energy-landscape job carries `landscape` in place of `program` and `shots` in
`estimate` and `submit`, and its `result` carries the landscape answer in place of
counts: §11 says how. `describe`, `status` and `cancel` are the same for every job.
### 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, in the member of that name (`"simulator": "…"`) |
| `qubits` | the device's qubit count; with `resonators`, the count of its places, the resonators among them |
| `native_gates` | the gates it runs; a client compiles to the first of the bases of §4 whose every gate is listed (`rzsxxcz`, `prxcz`, `gpizz`, `prxrzxx`), and a device with resonators lists `move` too |
| `connectivity` | `"all"`, or the pairs `[a, b]` a two-qubit gate may act on, in either order; a pair with a resonator is where `cz` acts between that qubit and the resonator |
| `resonators` | optional: the places, among `0 … qubits − 1`, that are computational resonators rather than qubits (a star device's qubits sit round one). A resonator takes no one-qubit gate, is never measured and pairs with no other resonator; a client places no logical qubit on it and compiles to it only in `rzsxxcz` or `prxcz` (§4) |
| `moves` | optional, with `resonators`: each `[qubit, resonator]` on which `move` runs. A client reaches two qubits through a resonator when one moves into it and the other is coupled to it |
| `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` | one of three forms: `{"free": true}`, the device is free to use; `{"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; or `{"free": false, "priced": false}`: the device is not free, and the provider sets no price (below). `priced` is in the third form only, which holds no `unit`, `price` or `currency`. In any form, 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) |
| `approximate` | optional, with `simulator` or `emulator`: `true` when its results are approximate (a truncated tensor network, a sampled Pauli path); a result is then labelled so (§8.1) |
| `cancels` | optional: `false` when it cannot cancel a job it took (§5.6), so that a client can say so before it sends one |
| `landscape` | optional: the energy-landscape jobs it takes (§11.1) |
A client MUST NOT take a device to be free unless `billing` says
`"free": true`, or an estimate says so (§5.2).
A device whose `billing` sets no price is not free, and a job on it that no
estimate prices has an unknown cost, wherever that cost is shown or sealed: a
client shows the cost as unknown and submits the job only on the person's
confirmation for that run (§7), and the receipt records its estimate as
unknown (§8.1). It is the form for a provider that has no prices yet. A
`billing` of none of the three forms (`{}`, `{"free": false}`) states nothing:
a client takes the cost to be unknown all the same, and the checker refuses
the description (§9).
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 priced: the cost, the amount as a decimal string |
| `priced` | in the unpriced form only, and `false` there (below) |
| `unit` | optional, when it is priced: the billing unit the cost counts |
| `seconds` | optional: how long it is expected to take, queue included |
| `at_least` | optional, when it is priced: `true` when the amount is only the least the job can come to, as with a device billed by the time a job turns out to take |
| `note` | optional |
An answer is in one of three forms: **free**, `"free": true`; **priced**,
`"free": false` with a decimal `amount` and a `currency`; or **unpriced**,
`{"free": false, "priced": false}` with no `amount`, `currency`, `unit` or
`at_least`. The unpriced form is the estimate of a job the provider can take and
count but sets no price for, as `billing`'s third form is of a device (§5.1). It
is answered as any estimate is, after the job has been read and checked as
`submit` would check it, so a job the device cannot run is refused here; and it
states what the job's form counts: a landscape job's `operations` and `bytes`
(§11.2), and for a circuit nothing beyond `seconds` and `note`. It holds the
person to no amount and states none: the job's cost is unknown. A client shows
the estimate as unknown beside its counts, submits the job only on the person's
confirmation for that run (§7), records the estimate in the receipt as an
unknown cost (§8.1), and takes it to meet no cost ceiling (§8.7,
`cost_unknown`). An answer of none of the three forms (`{"free": false}` alone, an
amount that is a number, an amount with no currency, `priced` beside an amount, a
unit, `at_least` or `"free": true`, `priced` `true`, or `priced` of another type)
is refused: the client takes the cost to be unknown and shows no figure of it.
A provider that cannot estimate answers `unsupported`; the client then treats
the cost as unknown (§7). A provider that can count a job but has no price for it
answers the unpriced form rather than `unsupported`, so that its counts reach the
person: a counted job's operations and memory are not only in a sentence.
### 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, or to `accepted_shots` when it is given |
| `shots` | the shots |
| `accepted_shots` | optional: the shots the device kept, when it kept another number than asked (a post-selected or heralded readout discards the shots that fail its test; a device may run more than asked), a whole number of at least 1. The counts are of these shots. A client refuses counts that do not sum to it, or, when it is not given, to 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 |
| `counts_source` | optional: where the counts come from (§8.3): `shots` (tallied from one record a shot), `vendor_counts` (the device's or service's own counts), or `probabilities` (rebuilt from probabilities); an answer without it is read as `vendor_counts` |
| `execution` | optional: how the device ran the program (§8.3): each of `mitigation`, `twirling`, `decoupling`, `remapping` and `optimisation` as `off`, `on`, `none` (the device has no such step) or `unknown` (a step the provider cannot vouch for), `verbatim` (`true` when the gates ran as written, on the qubits named), and an optional `note` |
A `result` asked twice of the same job MUST give the same counts.
A provider that relays a job to another service (a wrapper around a service's
own interface) MUST give `execution`, from what it sets in its own request, and
MUST give `counts_source`, `probabilities` when it rebuilds counts from them.
Each is the provider's word, and sealed as such (§8.3). A client refuses a
`counts_source` or an `execution` not of the form above, and seals nothing.
### 5.6 `cancel`
Field: `job`. The answer's `status` is `cancelled`; or, for a job that had
already ended, how it ended: `done` for one that had finished, and `failed`,
with its `reason`, for one that had failed. A provider that cannot cancel
answers `unsupported`.
### 5.7 A job outlasts its client
A device's queue can outlast any process waiting on it, so a job is not tied to
the connection or the process that submitted it. A provider SHOULD answer
`status` and `result` for a job's id from any later connection, and SHOULD keep
a finished job's result for as long as it can, and answers `not_found` for one
it no longer holds. A provider that runs as a local process (§2.2) ends with its
client unless it keeps its jobs itself.
A client:
- SHOULD record a job before it submits it, and the job's id as soon as it has
one, with the program it sent, since a device's calibration can change before
the counts are read;
- MUST NOT cancel a job only because it stopped waiting for it, unless it keeps
no record to come back to;
- MUST NOT submit a job again when its `submit` did not complete, without the
person's confirmation: the first may have been taken, and may be billed.
## 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 provider whose `billing` sets no price
(§5.1) and that gives no estimate leaves every job's cost unknown, and so does an
estimate in the unpriced form (§5.2). 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, and the
same verifier checks both: an Ed25519 signature, by the host's key, over the
request as submitted and the result as returned (§8.2). A job's body is an
object holding `request`, the job as it was asked, and `result`, which carries
its `receipt`. This section says what a provider's result carries (§8.1), how
it is sealed and read (§8.2), what its counts' source and its `execution` say
(§8.3), what a verifier checks and the reason it gives for each failure
(§8.4), what the receipt proves (§8.5), what a job under a contract carries
beside it (§8.7), and how a reader holds a receipt to the keys it trusts and to a
time (§8.8). A verifier of §8.2, §8.4 and §8.7 checks any job's receipt, a
provider's or not.
### 8.1 What a provider's result carries
| member | |
|---|---|
| `method` | `provider` |
| `accuracy` | `class`: `measured` when the provider's `provenance` is `silicon`, `simulated` when it is a simulator's or an emulator's (no device measured those counts); `approximate`: `true`, only on a `simulated` result whose provider's `describe` says `approximate` (§5.1), and absent otherwise; `note`, a sentence |
| `provider` | `name` (the person's name for it, the request's `provider`), `kind` (`sampler-v2`, `prx-circuits`, `native-turns`, `cloud-tasks`, `http` or `process`), `protocol` (`wai.quantum.provider/1`, or a built-in client's own), `device`, `provenance` (`silicon`; or `simulator` or `emulator`, alone or followed by `:` and the sentence naming it), `qubits` (the device's), `basis` (§4), and `describe`, the BLAKE3 of the RFC 8785 text (§8.2) of the provider's `describe` answer (for a built-in kind, of the description its client makes of the device). `provenance` is `silicon` exactly, or its part before any `:` is `simulator` or `emulator` |
| `calibration` | the calibration snapshot the job ran under, as the provider names it, or `null` |
| `compiled` | the program sent: `basis`; `qasm3`, its OpenQASM 3 text, `$i` being index `i` of the coupling map the provider described; `qubit_names`, the device's own name for each index the program uses, where a client sends it under those names (a `cloud-tasks` processor's labels, each index itself on its simulator; a `prx-circuits` device's qubits), each as it was when the job was sent; `program`, the BLAKE3 of `qasm3`'s UTF-8 bytes; `hash`, a sentence saying so; `native_gates`, `two_qubit_gates` and `swaps`, its counts; `layout` and `final_layout`, for each logical qubit the index it starts and ends on; `checked`, the check it passed before it was sent; `placement`, how it was placed (with a calibration read as a target, the target's hash, the layout's expected success and the fewest-swaps layout's) |
| `job` | `id`, the provider's id for the job, and `polls`, the status checks before it finished |
| `counts` | keyed by the program's classical bits (§6), each outcome that occurred with its count, at least 1 (a host drops an outcome a provider counts 0); with `distinct_outcomes`, the outcomes that occurred, and `omitted_shots`, the shots of outcomes not listed (at most 65,536 outcomes are listed). Every provider result an engine has sealed carries these, the earlier form's included |
| `shots` | the shots: the request's, 1024 when it gives none |
| `accepted_shots` | the shots the counts are of, where the device kept another number than `shots` (§5.5: a post-selected or heralded readout, or a device that runs more than asked), a whole number of at least 1; absent when the counts are of every shot asked. Every host before it refused counts of another sum, so a result without it, of either form, is of every shot asked. Who said the device kept that many is read from `provider.kind`, as for `counts_source` (§8.3) |
| `counts_source` | where the counts come from (§8.3) |
| `execution` | how the device was asked to run the program (§8.3) |
| `ideal` | the circuit's exact distribution p set beside the counts' frequencies q (up to 22 qubits): `computed`, and when it is `true` `total_variation_distance` (½ Σ \|q − p\|), `hellinger_fidelity` ((Σ √(p·q))²), `sampling_tvd_expected` (½ Σ √(2p(1 − p)/(π·shots)), the distance the shots alone leave on average, by the normal approximation) and `ideal_outcomes`; when `false`, `why`. The host computes them, and no verifier can without the program's simulation |
| `cost` | `free`; `estimate`, the estimate shown before submitting (an object, with `unknown`, a sentence, when none was known; an estimate in the unpriced form of §5.2 has `priced` `false` and `unknown` beside what it stated); `charged`, what the provider says it charged (`{"amount", "currency"}`), or `null`; `confirmed`, `true` exactly when the run was not free: such a run ran only with the person's confirmation (§7), and a free run needs none and records none; `confirmed_by`, on a provider's own record only (below), `submit` |
| `energy` | `unmetered`, saying why it is not metered here; and, when the provider gives one, its own figure `service_kwh`, labelled as its own by `service_kwh_is` |
| `engine`, `schema` | the engine that made the result (`{"name": "wai-quantum", "version"}`) and `wai.quantum.job-result/1` |
**A provider's own record.** A provider may seal its own record of a job it
ran, as a host seals a job (§8.2): its own key in the host's place, and a
result of this form, which the same verifier checks (§8.4). It cannot record
the person's confirmation, which the person gives their host and which never
reaches the provider. Its record of a run that is not free has `confirmed`
`true` and `confirmed_by` `submit`: the provider took the job's authenticated
`submit` as the confirmation, since a client submits a job that is not free
only on the person's confirmation for that run (§7). A free run records
neither. A host's receipt has no `confirmed_by`: its `confirmed` is the
confirmation the host itself took.
A result sealed before results carried `counts_source` and `execution` is of
the *earlier form*: it carries neither, its class is `measured` whatever ran
it, and some of the members above are missing. It verifies by §8.4's rules
for that form. A verifier reads its class by its provenance, and says so: a
simulator's counts sealed as `measured` are a simulator's.
A job's `id` is the provider's, sealed: some name the person's account (a
cloud account's number inside a task's identifier). Sharing a receipt shares
it.
### 8.2 How it is sealed, and how a job is read
**The seal.** The result's `receipt` is an object:
| member | |
|---|---|
| `alg` | `ed25519` |
| `canon` | `rfc8785`; absent on a receipt of the first form (below) |
| `key` | the signer's Ed25519 public key `A`, its 32 bytes as 64 hex digits |
| `digest` | the digest signed, 32 bytes as 64 hex digits |
| `signature` | the signature `R ‖ S`, 64 bytes as 128 hex digits |
| `time` | when the receipt was issued; absent on a receipt that seals no time (*The time*, below) |
Hex digits are lower case, `0`–`9` and `a`–`f`, and nothing else. The receipt
is not sealed, so each member has one spelling. Any other member of the
receipt is ignored.
The digest is BLAKE3 (32 bytes, the hash mode) of
d ‖ u64(|Q|) ‖ Q ‖ u64(|T|) ‖ T
where `d` is the 27 ASCII bytes `wai:quantum-service-receipt` and then the one
byte `0x02` (`0x01` for the first form); `Q` is the canonical text of `request`;
`T` is the canonical text of `result` with its `receipt` member removed; and
`u64(n)` is `n` as 8 bytes, big-endian. The signature is Ed25519 as RFC 8032
§5.1 defines it, with no context and not prehashed, over the 32 bytes of the
digest. Since the domain byte is inside the digest, a receipt of one form does
not verify as the other.
**The time.** A receipt may state when it was issued. Its `time` is an object:
| member | |
|---|---|
| `issued_ms` | when the signer sealed the receipt, by its own clock: milliseconds since 1970-01-01T00:00:00Z, a whole number from 0 to 2^53 − 1 |
| `not_after_ms` | optional: the first moment the receipt is no longer offered, a whole number of the same kind, greater than `issued_ms` |
and no other member. A whole number here is a number with that value however it
is written (`1.5e2` is 150, and `-0` is 0). A receipt with a `time` has `canon`
`rfc8785`, and its digest is BLAKE3 of
d ‖ u64(|Q|) ‖ Q ‖ u64(|T|) ‖ T ‖ u64(|V|) ‖ V
with `d` ending in the byte `0x03` and `V` the canonical text of `time`. The time
is so inside what is signed: one changed, taken off, or put on a receipt sealed
without it gives another digest, and the receipt does not verify. `Q` and `T` are
what they are in a receipt with no time, so a job's request and result, and every
digest taken of them (a set's leaves, §8.6; a replay digest), are the same whether
its receipt states a time or not: the time is the receipt's alone.
A host states its clock's reading and nothing else. One with no clock to read (a
browser's WebAssembly has none), or whose owner does not want its receipts to
differ from one run to the next, seals no time: its receipt has no `time`, and is
the receipt this section has always described. No host writes a time it did not
read, and none writes a placeholder. The job service seals a time on every receipt
(a deployment can turn that off); the command line seals none unless it is asked
(`WAI_QUANTUM_RECEIPT_TIME`), so that the same key and job give the same receipt
byte for byte. A verifier states what it found: the time, or that none is sealed.
A receipt that seals none is as valid as it was, and can be placed in no window
that ends (§8.8).
Only `request` and `result` are sealed. Whatever else a job's body holds (its
`status`, `id`, `method`, `why`, `estimate` and times) is the host's, and
unsealed: a verifier takes nothing from it, and its verdict does not change
with it.
**Reading.** A job is read from its text, strictly, before anything is
checked. The text is UTF-8 with no byte order mark, and one JSON value
(RFC 8259) with whitespace allowed around it. It is unreadable when:
- an object names a member twice (names compared after their escapes are read);
- a string holds a lone surrogate: a `\u` escape of a high surrogate not
followed at once by one of a low surrogate, or of a low surrogate alone;
- a string holds a control character (U+0000 to U+001F) not escaped;
- a number's magnitude is 2^1024 − 2^970 or more, which rounds to an infinite
double;
- arrays and objects nest deeper than 127, the outermost counted as the first.
A noncharacter (U+FDD0 to U+FDEF, and the last two code points of each plane)
is a character like any other. Each number is read as the nearest IEEE 754
double, ties to even; a number too small for any double other than zero reads
as zero. A whole number written as one (a literal with no fraction and no
exponent) in the request or the result that no double holds exactly says more
than the receipt seals, since the seal holds its double (§8.4,
`inexact_number`), unless it is the canonical text (below) of the double it
reads as. That one spelling is RFC 8785's for a whole double whose magnitude is
2^53 or more and under 10^21 (a negative one with its `-`), its shortest digits
padded with zeros (2^60 as
1152921504606847000), and what any JSON writer that follows RFC 8785 or
ECMAScript writes: it reads as the double that was sealed, and no other
spelling of an inexact whole number is taken. A canonical receipt seals every number as its double; a
first-form receipt seals a whole number written as one in its writer's integer
range by its digits, and every other number by its double (below), and only
what a receipt seals as a double is held to this.
**The canonical text** of a value is RFC 8785's:
- `true`, `false` and `null` as they are;
- a string between `"`s, with `"` and `\` escaped as `\"` and `\\`; U+0008,
U+0009, U+000A, U+000C and U+000D as `\b`, `\t`, `\n`, `\f` and `\r`; every
other character below U+0020 as `\u00` and two lower-case hex digits; and
every other character as its own UTF-8 bytes, unescaped (`/` and U+007F
included) and not normalised;
- a number as ECMAScript's Number::toString writes the double it reads as
(ECMA-262, 6.1.6.1.20): zero (also negative zero) as `0`; otherwise, with
`s` the fewest decimal digits `d1 … dk` that read back as the double (of
those, the nearest to it, a tie going to the even last digit) and `n` such
that the value is 0.`s` × 10^n: if k ≤ n ≤ 21, the digits and n − k zeros;
if 0 < n ≤ 21, the digits with a `.` after the first n; if −6 < n ≤ 0, `0.`,
−n zeros, and the digits; otherwise `d1`, then `.` and `d2 … dk` when k > 1,
then `e`, a sign (`+` or `-`), and |n − 1| in decimal; a negative number is
the same with `-` before it;
- an array as `[`, its values' texts joined by `,`, and `]`;
- an object as `{`, its members joined by `,`, each the name's text, `:` and
the value's text, in the order of their names compared as sequences of
UTF-16 code units (unsigned), and `}`;
- with no whitespace anywhere outside strings.
So a result verifies after any JSON reader and writer has carried it, even
one that writes `1.0` as `1` or orders members otherwise.
**The first form.** The first receipts, which name no `canon`, sealed the text
their signer's JSON writer wrote, not the canonical text, under the domain byte
`0x01`. A verifier reads `Q` and `T` as that writer did: the value's compact
text (no whitespace outside strings), its object members in the order the job
gives them (the result's `receipt` removed, the others kept in order), its
strings as in the canonical text, and its numbers so:
- a whole number written as one (no fraction, no exponent), without a `-` and
from 0 to 2^64 − 1, or with a `-` and from −2^63 to −1, is written as its
decimal digits, with its `-` (so `-0` is a double: `-0.0`);
- every other number is written from its double: `-0.0` for negative zero, and
`0.0` for zero; otherwise, with `s` = `d1 … dk` and `n` as above, if n ≥ k
and n ≤ 16, the digits, n − k zeros and `.0`; if 0 < n ≤ 16, the digits with
a `.` after the first n; if −5 < n ≤ 0, `0.`, −n zeros and the digits;
otherwise `d1`, then `.` and `d2 … dk` when k > 1, then `e`, a sign (`+`
or `-`), and |n − 1| in decimal (`1e+16`, `1e-7`); a negative number is the
same with `-` before it.
The first form was sealed from 2026-10-17 to 2026-10-20, every receipt by one
writer, whose text these rules restate.
A first-form receipt verifies only as its signer wrote it: reordered members
break it, and so can a number written another way. No host writes the first
form now. A `canon` other than `rfc8785` (`null` among them) names no form.
**The signature, strictly.** RFC 8032 leaves some choices open, and two
verifiers that chose differently would disagree on the same receipt. A
verifier here:
- decodes `A` and `R` as RFC 8032 §5.1.3 does, and refuses an encoding whose y
(its low 255 bits, little-endian) is p = 2^255 − 19 or more, one that is not
on the curve, and one with x = 0 and the sign bit set;
- refuses `A` of small order (a point P with [8]P the identity) and `A` not in
the prime-order subgroup ([ℓ]A not the identity, ℓ = 2^252 +
27742317777372353535851937790883648493);
- refuses `R` of small order, and `S` (little-endian) of ℓ or more;
- accepts exactly when [S]B = R + [k]A, with k = SHA-512(R ‖ A ‖ the 32
digest bytes) read little-endian, mod ℓ, compared as points with no cofactor
(and so as their canonical encodings).
Without the rules on `A`, the identity as `A`, any `S` and `R` = [S]B hold
for every digest: a receipt for any content at all. A library's default check
also takes the identity as `R` with `S` = 0.
**Every quantum receipt, by this rule.** The rule above is SPEC §7
*Signatures* with the prime-order subgroup added, and it is the one rule every
quantum receipt is verified by, whatever its family: the job receipt here and
a set's (§8.6), the circuit receipt ([quantum-sim](quantum-sim.md)), the
calibration, job, mitigation, decode, compile and rearrange receipts
([quantum-ops](quantum-ops.md)), the bill of materials, the fabric,
sparse-Pauli, resource-estimate and phasor receipts, each in its labelled
form too ([energy-measurement](energy-measurement.md) §2.8), and the job
service's key transitions. Each reads its keys, signatures and hashes, and
the bits of each double it seals, as lower-case hexadecimal of exactly their
length, and refuses any other spelling: a reader that took a second spelling
would give a sealed receipt a second text that also verifies. The reference
implementation holds one copy of the rule and of the reader
(`wai_quantum::sig`).
### 8.3 Where the counts come from, and how the device was asked to run them
**`counts_source`** is one of:
| value | the counts are |
|---|---|
| `shots` | tallied from the device's records, one a shot |
| `vendor_counts` | the provider's own counts, as it returned them |
| `probabilities` | rebuilt from the probabilities the device gave: each `p·shots` rounded down, then the shots left given one each to the largest remainders, a tie to the lower outcome. They are derived, and not shot records |
The counts are of the shots the device kept (`accepted_shots`, §8.1), which a post-selected
or heralded readout makes fewer than asked; the source says how those were counted. A device
that reads an ensemble (an expectation value of each qubit over many systems at once, rather
than one outcome a shot) gives none of these: its readings are not counts, rebuilding counts
from them would state outcomes no shot had, and no provider result carries them.
Who did so is read from `provider.kind`. On a built-in kind the host's client
did, and it is the host's word. On `http` and `process` the host received counts
and did neither, and it is the provider's word for how it made them (§5.5): the
rule for rebuilding them is the one the provider says it kept, which the host
cannot vouch for.
**`execution`** says what the host knows of the five steps a provider may
take between receiving a program and running it: `mitigation` (correcting
counts after the run, readout correction or debiasing), `twirling` (of gates
or of measurements), `decoupling` (dynamical decoupling of idle qubits),
`remapping` (moving the program to other qubits) and `optimisation`
(rewriting its gates). It is an object of:
| member | |
|---|---|
| `stated_by` | `client`, the host's client from the request it sends; `provider`, the provider in its `result` answer (§5.5); or `nobody` |
| the five steps | each `off` or `on` (by a setting the client sent, or as the provider states), `default` (said by a client: its request sets nothing for it, so the provider's default stands, whatever it is), `none` (said by a provider: its device has no such step), or `unknown` (when nobody states it, or a provider cannot vouch for the step) |
| `verbatim` | `true` when the request asks for the gates to run as written, on the qubits named (a verbatim box); `false` when it does not; `null` when nobody states it |
| `sent` | with `client` only: `fields`, the members of the request's body, each once, in increasing order of their UTF-8 bytes; and `settings`, an object of what the request sets: JSON Pointers (RFC 6901) into the body, each with the value it holds there, and names for settings the body holds otherwise (`verbatim_box`); and nothing else |
| `note` | optional: a sentence |
and nothing else. Its rules: with `stated_by` `client`, each step is `off`,
`on` or `default`, `verbatim` is `true` or `false`, and `sent` is given; with
`provider`, each step is `off`, `on`, `none` or `unknown`, `verbatim` is
`true` or `false`, and there is no `sent`; with `nobody`, each step is
`unknown`, `verbatim` is `null` and there is no `sent`. Each key of
`sent.settings` either begins with `/`, and its first reference token (up to
the next `/`, its `~1` and `~0` read as `/` and `~`) is one of `fields`, the
rest of it not read; or is a name, not empty, that does not begin with `/`. A program run as written is
neither remapped nor optimised, so with `verbatim` `true`, `remapping` and
`optimisation` are each `off` or `none`. A client that says a step is `off` or
`on` says by which setting: `settings` is then not empty.
**What each built-in kind records.** A client builds its `execution` from the
very body it sends, so that what is stated is what was sent, and its counts'
source from what the service answered. These are their rows. A later client
that sends something else adds a row; a row is never removed, so every
receipt keeps verifying. A kind of the open protocol (`http`, `process`)
records the provider's word (`provider`), or `nobody` when it says nothing. On
any kind, a job recorded before its client stated what it sent, and collected
since, is stated by `nobody`: nothing else is known of what was sent.
| kind | `counts_source` | `sent.fields` | `sent.settings` | steps | `verbatim` |
|---|---|---|---|---|---|
| `sampler-v2` | `shots` | `backend`, `params`, `program_id` | `/params/options/dynamical_decoupling/enable`: `false`, `/params/options/twirling/enable_gates`: `false`, `/params/options/twirling/enable_measure`: `false` | `decoupling` and `twirling` `off`; the other three `default` | `false` |
| `prx-circuits` | `vendor_counts` | `circuits`, `shots` | none: the client sends nothing else | all five `default` | `false` |
| `native-turns` | `probabilities`: the service answers probabilities, so its counts are always rebuilt | `backend`, `input`, `settings`, `shots`, `type`; and `noise`, on its simulator under a device's noise model | `/input/gateset`: `native`, `/settings/error_mitigation/debiasing`: `false`; and `/noise/model`: the model's name, with `noise` | `mitigation` `off`; the other four `default` | `false` |
| `cloud-tasks`, on a processor (`silicon`) | `shots`, from the task's rows; `probabilities`, when its result holds none | `action`, `clientToken`, `deviceArn`, `outputS3Bucket`, `outputS3KeyPrefix`, `shots` | `verbatim_box`: `true`, the program's gates in the service's verbatim box on physical qubits | `remapping` and `optimisation` `off`; the other three `default` | `true` |
| `cloud-tasks`, on a simulator | as on a processor | as on a processor | none | all five `default` | `false` |
A row is the `execution` its kind states, less its `note`: `stated_by` is
`client`, and every member is as the row gives it. A row holds for its
provenance: `cloud-tasks`' first row for `silicon`, its second for any other
(a simulator or an emulator); `native-turns`' row with `noise` for any
provenance but `silicon`, its model's name a string, not empty, and its row
without `noise` for any. `prx-circuits` sends only
`circuits` and `shots`, so it turns nothing off, and says so. No row says a
provider runs a program as sent where its client sets nothing that asks it to.
### 8.4 What a verifier checks
A verifier reads the job (§8.2) and reports every reason below that applies,
as a set: none for a valid receipt. It is valid exactly when there is none.
| reason | when |
|---|---|
| `unreadable` | the text breaks a rule of reading (§8.2). Reported alone |
| `not_a_job` | the value is not an object whose `request` and `result` are both objects. Reported alone |
| `no_receipt` | the result has no `receipt`. Reported alone |
| `too_large` | the job is longer than the surface reads, in its bytes or in its values (one, and one more for each `,`, `:`, `[` and `{` outside its strings: what a check holds follows them), which it states with the verdict (`read_at_most`: `{"bytes", "values"}`); it judged none of it. Reported alone. `wai-quantum verify`, on every host, reads 384 MiB and 2^22 values, more than any job holds; the job service's `POST /v1/verify` and `/mcp` read 4 MiB and 2^19 values. A job given as a string is its text's bytes and values; one given parsed in a message, as it is written there |
| `receipt_form` | the receipt is not an object; its `alg` is not `ed25519`; it has a `canon` other than `rfc8785`; its `key`, `digest` or `signature` is not lower-case hex of its length; or it has a `time` that is not of its form (§8.2), or has one and no `canon`. When it is reported, the next three are not checked |
| `digest_mismatch` | the digest the receipt states is not the digest (§8.2) of this request and result |
| `bad_key` | `key` is refused by the strict rules (§8.2). When it is reported, the signature is not checked |
| `bad_signature` | the signature does not hold, strictly (§8.2), over the digest the receipt states (so a body changed after it was sealed gives `digest_mismatch` alone) |
| `inexact_number` | a whole number written as one, in what the receipt seals (the request, and the result less its receipt; for a set, the request and its head, §8.6) and sealed as a double (§8.2), is not a double, and is not the canonical text of the double it reads as. A receipt is of the first form when `receipt_form` is not reported and it has no `canon`; one that `receipt_form` refuses names no form, and its numbers are read as a canonical receipt's |
| `provider_class` | a provider's result whose `accuracy.class` is not the one its form gives: `measured` in the earlier form; in the stated form, `measured` for `silicon` and `simulated` for a provenance whose part before any `:` is `simulator` or `emulator` (none for any other); or whose `accuracy.approximate` is present and not `true`, or `true` in the earlier form or on a class other than `simulated`. Or a result of another method whose class is `measured` or `simulated`, other than a sign-off's (§8.9), which repeats the class of the job it signs off |
| `request_provider` | a provider's result whose `provider.name` is not a string equal to the request's `provider`; or, when the request gives none (or `null`) and its `contract.providers` is `allowed`, equal to what follows `provider:` in `planner.choice` (§8.7) |
| `request_shots` | a provider's result whose `shots` is not the request's (1024 when the request gives none, or `null`) |
| `counts` | a provider's result whose `counts` is not an object of outcomes, all of one length and each of `0` and `1` only, to whole numbers from 1 to 2^53 − 1; or whose `shots` (a whole number of at least 1), `omitted_shots` and `distinct_outcomes` (whole numbers of at least 0) are missing; or that has an `accepted_shots` (whatever its value) that is not a whole number of at least 1; or whose counts and `omitted_shots` do not sum to `accepted_shots` when it has one, and to `shots` when it has none; or whose `distinct_outcomes` is not the number of outcomes listed when `omitted_shots` is 0; or, when it is not 0, lists other than 65,536 outcomes, or no more distinct outcomes than it lists |
| `provider_members` | a stated provider's result (below) without, or with a wrong type of: `provider`'s `name`, `kind`, `protocol` and `device` (each a string not empty), `provenance` (as `provider_class` reads it), `describe` (64 lower-case hex digits), `qubits` (a whole number of at least 1) and `basis` (equal to `compiled.basis`); `calibration` (a string or `null`); `compiled`'s `basis` (`rzsxxcz`, `prxcz`, `gpizz` or `prxrzxx`), `program` (64 lower-case hex digits), `qasm3` (a string), `native_gates`, `two_qubit_gates` and `swaps` (whole numbers of at least 0), `layout` and `final_layout` (arrays of whole numbers of at least 0) and `checked` (a string); `compiled.qubit_names`, where it is given or required, is below; `job`'s `id` (a string not empty) and `polls` (a whole number of at least 0); `ideal.computed` (`true` or `false`); `cost`'s `free` and `confirmed` (each `true` or `false`), `estimate` (an object) and `charged` (`null`, or an object whose `amount` and `currency` are strings); `energy.unmetered` (a string), and `energy.service_kwh_is` (a string) when there is `energy.service_kwh`; `schema` (`wai.quantum.job-result/1`); `engine.name` (`wai-quantum`) and `engine.version` (a string) |
| `counts_source` | a stated provider's result whose `counts_source` is not one of §8.3's values, or, for a kind with rows, not one the rows that hold for its provenance give |
| `execution` | a stated provider's result whose `execution` breaks a rule of §8.3; or, for a kind with rows, is neither one of the rows that hold for its provenance nor stated by `nobody`; or, for `http` and `process`, is not stated by `provider` or `nobody` |
| `compiled_program` | a stated provider's result whose `compiled.program` is not the BLAKE3 of `compiled.qasm3`'s UTF-8 bytes, when `qasm3` is a string and `program` is 64 lower-case hex digits |
| `cost_confirmation` | a stated provider's result whose `cost.confirmed` is `true` on a free run or `false` on one that is not free (§7), when both are `true` or `false`; or whose `cost` has a member `confirmed_by` (whatever its value, `null` included) that is not `submit`, or that is `submit` beside a `confirmed` that is not `true`: a submit is taken as the confirmation of a run that is not free, and of no other (§8.1) |
| `contract_form`, `planner_form`, `planner_route`, `planner_choice`, `planner_stable`, `planner_kept`, `contract_class`, `shadow`, `stopped` | §8.7, for any job's result, a provider's or not |
| `wasmq_form` | a wasmQ job's result (its `format` is `wai.quantum.wasmq.result/1`, or `/2` for the second version, [quantum-wasmq](quantum-wasmq.md)) whose request or result breaks a rule of [quantum-wasmq §6.5](quantum-wasmq.md#65-verifying), as [§11.6](quantum-wasmq.md#116-verifying) reads them for the second version: the job is not one that profile writes |
| `landscape_form` | an energy-landscape job's result (its `protocol` or its `schema` is `wai.quantum.landscape/1`, [quantum-landscape](quantum-landscape.md), or `wai.quantum.landscape/2`, [quantum-landscape-2](quantum-landscape-2.md)) whose request or result breaks a rule of [quantum-landscape §5.4](quantum-landscape.md#54-verifying-an-answer), as [quantum-landscape-2 §5.4](quantum-landscape-2.md#54-verifying-an-answer) reads them for a job of the second version, whether it ran here or on a provider (§11.3) |
A provider's result is one whose `method` is `provider`. It is *stated* when it
carries a member `counts_source` or a member `execution` (whatever their
values), and of the earlier form when it carries neither; the earlier form is
held to `provider_class`, `request_provider`, `request_shots` and `counts`
only. A whole number is any number whose value is whole, however it is
written (`1`, `1.0`, `1e0`), however large; only a count's value has a ceiling,
and a sum is exact, not a double's. A number's value, in every rule here and on
every form of receipt, is its nearest double (§8.2): the digits of a first-form
receipt are for what it sealed, and for nothing checked here. `-0` is 0.
"The request's" is by value: two equal strings are the same, and that a string
is no number is another rule's to say.
A class a verifier does not know is not a reason: a later version may add one
(VERSIONING.md).
`compiled.qubit_names` is required of a stated result of `cloud-tasks` or
`prx-circuits` whose `execution` is stated by `client`, and optional otherwise.
When given (`null` is given, and is no object) it is an object: each key an index
in decimal, with no leading zero; each value a string, not empty; the value of
every entry of `layout` and `final_layout`, written in decimal in full, is among
its keys; and so is every index `qasm3` acts on, when `qasm3` is a string: each
`$` anywhere in it followed by one or more of the ASCII digits 0 to 9 names the
index those digits read as, written in decimal with no leading zero (a `$`
followed by no such digit names none). Otherwise it is a `provider_members` reason.
**What a host seals, its own verifier accepts.** A host refuses a request holding
a whole number past ±(2^53 − 1), or any number that it or RFC 8785 would write in
a spelling this section refuses, where it reads it, on every path to a seal (a
set's own request, its sweep's values among them, included); and it writes a
whole number no double holds in a result as the double it seals. A receipt an
earlier release sealed with such a number is refused with `inexact_number`,
though its signature holds: what it sealed is the double, and its text says
more. Written as that double's exact digits, or as its canonical text, it
verifies. The local hosts' `qec` took a seed of any size; their compile, pulse,
estimate and `@Seal` took any number in a request; and a set took any number in
its own request and its sweep's values. The job service refused such a number
in every request but a set's own.
A verifier also states, for a provider's result (one with no receipt
included), its class as its provenance reads it, and, in the stated form, its
counts' source and its `execution`, each as the result gives it.
A wasmQ job's result is held to `wasmq_form` whatever its `method`, and to the provider
rules too when its `method` is `provider`. A verifier also states, for a wasmQ job, what
quantum-wasmq §6.5 says it states beside the verdict: how the run ended, each rule it
breaks and where the body's unsealed members differ from what is sealed. None of that
changes the verdict.
These are all the rules: what §8.1 names and this table does not check
(`compiled.hash` and `placement`, the notes, the `ideal` figures, the
estimate's contents, a route's price where §8.7 does not recompute from it) is
the host's word, unchecked. The schemas
(`wai.quantum.job-result/1`, `wai.quantum.receipt/2`) describe what a host
writes; a verifier judges by this section.
### 8.5 What the receipt proves, and what it does not
A valid receipt proves that the holder of the key it names signed this
request and this result, nothing else and nothing less: every member of
both, the counts, `counts_source` and `execution` among them. It does not
say whose key it is. The job service publishes its keys and their windows
(`GET /.well-known/wai-quantum-receipt-keys`); a host's key is the person's
own, and anyone relying on a receipt decides whether they trust it, and says so
to the verifier with a trust list (§8.8). A receipt that seals a time (§8.2) says
when its signer issued it, in the signer's word, and can be held to a key's window,
to a revocation and to its own end; one that seals none can be shown again at any
time, and no window that ends can be applied to it. A verifier says which it is. A host can still seal a
result of the earlier form, stating neither its counts' source nor its
`execution`; a verifier then holds it to the earlier form's rules, reads its
class by its provenance, and says that it states neither. A host can likewise
seal a stated result with `execution` stated by `nobody` on any kind, meant for
a job recorded before its client said what it sent: that says nothing of what
was sent, and a verifier says that nobody stated it.
A provider's own record (§8.1) is the provider's word, signed with its key,
and its `confirmed_by` `submit` says only that the provider took the job's
submit as the confirmation: whether the person was asked is in their host's
receipt, not in the provider's.
Everything the result says the host received, sent and checked is the host's
own word, signed: that this was the provider's answer, under the calibration
snapshot named, and that the host compiled the program whose text and hash it
carries exactly and checked it against the circuit before sending it. The
compiled program's text is in the receipt, with the device's names for its
qubits where its client uses them, so anyone can read which gates were sent to
which qubits and check its hash. It is not the bytes sent: a client writes the
same gates in what its service takes. `execution` says what the request
asked of the provider. 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.
A provider's simulator's counts say what its software gives, which is why
they are `simulated`, not `measured`. The `ideal` distance says how far the
counts are from what the circuit gives; a distance well above the sampling
distance is the device's or the simulator's error, or a program not run as
sent.
### 8.6 A set's receipt
A set is one request that runs many jobs (`programs`, a list of programs or
jobs, or `sweep`, values for the inputs of the request's own `qasm`); its result
has `"format": "wai.quantum.set/1"`. Its `head` is its result less `items` and
`receipt`. The set's receipt seals the set's request and its head, by §8.2, as a
job's seals its request and result; each item that ran carries a result with a
receipt of its own, over its own request. A verifier reads a set as it reads a
job (§8.2), and reports, beside §8.4's reasons for the set's request and head
(`unreadable`, `not_a_job`, `no_receipt`, `receipt_form`, `digest_mismatch`,
`bad_key`, `bad_signature`, `inexact_number`; the provider rules do not apply to
a head), each of these that applies:
| reason | when |
|---|---|
| `set_items` | `items` is not a non-empty array whose `i`-th entry is an object with `index` equal to `i` (a whole number), `request` an object, exactly one of the members `result` (an object) and `error` (a string) present (a member that is present is so whatever its value, `null` included), and `path` an array of strings each of 64 lower-case hex digits. When it is reported so, the next two are not checked. Or: `count` is not the number of items; or the items are not the ones the request gives. With `programs` (an array, and no `sweep`), item `i`'s request is the request less `programs`, `sweep` and `dry_run`, with program `i` over it: a string as `qasm`, or an object's members, each replacing one of the same name. With `sweep` (an object, and no `programs`), each of its values is a non-empty array, the items are as many as the product of their lengths, and each item's request less `qasm` is the request less `programs`, `sweep`, `dry_run` and `qasm`, its `qasm` a string (the request's program written out at its point, which §8 does not check: below). A request with neither, or with both, gives no items; a member that is `null` is none. Requests are compared as JSON values: members in any order, numbers by value |
| `set_item` | an item with a `result` is not a valid job: `{"request": its request, "result": its result}`, read as §8.2 reads a job, gives any reason of §8.4; or its result is a set's (`format` `wai.quantum.set/1`: an item is one job); or its receipt is valid and names a `key` other than the set's receipt's `key` member as it stands (one the set's receipt does not have included). Or an item with an `error` has a request that writes a whole number its leaf could not seal: as `inexact_number` reads one in a canonical receipt, whatever the set's form, since a leaf is the canonical form's digest |
| `set_root` | `root` is not 64 lower-case hex digits, or not the Merkle root of the items' leaves; or an item's `path` does not lead from its leaf to `root` |
An item's leaf is the digest (§8.2, the canonical form's) of its request and its
result less its `receipt`, or, for an item with an `error`, of its request and
`{"error": the error}`. The Merkle tree: a leaf's hash is BLAKE3 of
`moq-jwp:merkle-leaf` and the byte `0x01`, then the leaf; a node's is BLAKE3 of
`moq-jwp:merkle-node` and the byte `0x01`, then the left hash and the right; a
level of odd length pairs its last hash with itself; the root of one leaf is its
hash. A path lists the sibling at each level from the leaf up: with `n` items it
has as many entries as halvings (rounding up) take `n` to 1, and a path of any
other length does not lead to the root; at each level the item's position,
halved at each step, says whether the sibling is on the right (an even position)
or the left. This is the tree the WAI worlds' and provenance receipts use, its
domains theirs, kept so that every set already sealed verifies. Two things
follow from it. The root alone does not fix the number of items (a last leaf
paired with itself gives the root a level of one more would), and `count`,
sealed in the head, does; one item shown with its path and the head alone is
placed by `count`. And the root does not say whether an item failed: an item
with an `error` and one whose result is `{"error": …}` have one leaf, and only
the item's own receipt tells them apart.
An item is read for its `index`, `request`, `result`, `error` and `path`; what
else it holds (its `status`, `method`, `why`, a sweep's `point`) is the host's,
unsealed, as a job body's members beyond its request and result are, and a
member its own receipt holds beyond its five is ignored, as a job's is.
The engine also writes each item of a sweep out again at its point, and says
whether each `qasm` is that program (`derived`, beside the reasons); that is the
engine's check of the set, not a rule of its receipt, and a verifier that does
not write programs out does not make it.
A program that calls WebAssembly between its gates ([quantum-wasmq](quantum-wasmq.md))
is not a program of §4: it has steps between gates that this protocol does not carry. A
client MUST NOT send one to a provider; it runs where its runner is (quantum-wasmq §7).
### 8.7 A job under a contract
A job says what it wants of an answer in its request's `contract`, and the
planner that took it says why the route it chose was the one, in its result's
`planner`. A *contract job* is one whose request holds a `contract` member other
than `null`. A job with none carries neither `planner` nor `shadow`, and plans as
it did before this section was written, byte for byte.
**The contract.** An object of these members, each optional; `null` is absent.
| member | |
|---|---|
| `exactness` | the class wanted: a string of the table below |
| `max_cost` | the most a provider's estimate of the run may come to, before it runs: an object of exactly `amount`, an amount in decimals (one or more digits, optionally `.` and one or more digits, at most 64 characters in all), and `currency`, a string not empty. It bounds the estimate, not the charge, which no one knows before the run: what was charged is stated after it, beside the ceiling (`kept.cost`) |
| `deadline_seconds` | a number above 0: when it must be done by, in seconds, as below |
| `max_joules` | a number above 0: the most energy the run may take, in joules, enforced while it runs: a live reading stops it when it reaches this, and the receipt says so (below). A route with no live reading is refused under a ceiling. Nothing before a run can promise a figure: a job's work leaves out what drives its time, and the same job read twice differs |
| `providers` | `never` or `allowed`: whether a route may be one of the person's providers. `never` is the default, given or not, and a request that names a `provider` under a contract that does not allow one is refused (`J070`) |
A contract of any other form is refused (`J070`). So is any other member, which
read as absent would be a requirement dropped in silence.
`exactness` is met by these classes of a result (§8.1, and the engine's own):
| wanted | met by |
|---|---|
| `bit-exact` | `bit-exact` |
| `exact` | `bit-exact`, `exact` |
| `single` | `bit-exact`, `exact`, `single` |
| `bounded` | `bit-exact`, `exact`, `bounded` |
| `estimate` | `bit-exact`, `exact`, `bounded`, `estimate` |
| `heuristic` | `bit-exact`, `exact`, `single`, `bounded`, `estimate`, `heuristic` |
| `measured` | `measured` |
| `simulated` | `simulated`, on a result that is not `approximate` |
| `any` | every class |
This is a table and not a ladder: `single` states no bound, so it does not meet
`bounded`, and a device's counts are in no classical class.
**What a deadline measures.** For a run of one of the engine's methods, the time
the engine took over the run itself (the result's `elapsed_ms`): not the queue, the
planning, a restart before it, the check of its intents, nor the sealing. For a
provider's run, the time from the job's submission to the moment the host first
finds it finished: the provider's queue and its run count, and a host that comes
back to a job late (one sent without waiting, collected later) counts the wait
too, so its `elapsed_ms` can only overstate. A shadow is not counted: it runs in
what the deadline leaves, or not at all. A host that can stop a run stops it at
its deadline: the job service stops its worker, and nothing is sealed (`J074`). A
provider's job is looked at only while a host follows it: past the deadline, the
next look cancels it where the client can, and it is `cancelled` once the provider
says so (`J074`). A job no one follows, or one a client cannot cancel (its route
says so: `cancel` in `leaves_out`), runs on and can be billed; when it finishes,
`kept.deadline` says it was missed.
**What a ceiling measures, and how it is held.** `max_joules` is held by a live
reading, while the run goes: a host runs the job in a process of its own, reads the
route's `watch` every few milliseconds as the host's scheduler allows, and kills that
process when the reading reaches the ceiling, so the work stops there; only a host
that can stop a run can give a route a `watch`. The reading is of what `watch` says, and of nothing
else: a process's own CPU energy leaves out memory, the host's idle draw and any
device it drives, so a method on a GPU or an accelerator has no `watch`; a processor
package's counts every process on it, so a busy host stops a run sooner. A run can
pass the ceiling by what it takes between two reads, and by what a counter has not
yet shown. A host that runs a job inside a process doing other work, or that reads
nothing, gives no `watch`, nor does a provider's route: the route is refused under a
ceiling (`energy_unwatched`). A counter that stops reading during a run leaves nothing
holding the ceiling: the host stops the run after a second of time without a reading,
or with one that stood still while the run spent CPU time (a frozen counter is not a
live one), and seals nothing. The run's process ends with the process that watches
it, however that one ends. A host reads the run's process whole when it ends, killed or not (a package's
counter added up read by read, each step corrected for a wrap, so that a run passing
the counter's range is read in full; a process's read that is far below what its CPU
time implies is no reading), and seals that figure in `energy`, so a run that passed its ceiling between two reads
and finished is sealed as having missed it.
A run so stopped gave no answer. Its result has no member but `method`, `qubits`,
`gates`, `accuracy`, `stopped`, `elapsed_ms`, `planner`, `energy`, `engine`, `schema`
and `receipt`, so no counts, expectations, amplitudes, verdict, outputs, mitigation or
distribution of any kind; its `accuracy.class` is `none`;
and it has `stopped`, an object of exactly `at`, the string `max_joules`; `joules`,
what the reading had come to, a number at or above the ceiling; `max_joules`, the contract's;
`elapsed_ms`, a whole number and the result's `elapsed_ms`; and `read`, a sentence
saying what read it and the longest time between two reads, the first counted from
the start. Its `energy.joules_micro`, where it has one, is no less than `joules` ×
10^6, less one for its rounding. It is sealed like any result, `energy` the whole
figure of the process killed where the host could read it, its `kept.energy` says the
ceiling was not kept, and a command line exits 4. A result whose energy was not read
whole states `kept.energy.joules` `null`.
**The reasoning.** `planner` is an object of:
| member | |
|---|---|
| `format` | `wai.quantum.planner/1` |
| `routes` | every route the host could see, an array with at least one member, each route's name once |
| `named` | the route the job names, or `null`: as below |
| `question` | the question the job asks, as below: `noise`, `counts`, `expectations` or `amplitudes` |
| `auto` | the route `auto` picks for this job, or `null`, as below |
| `wanted`, `wanted_from` | the class the routes are held to, and where it comes from, as below |
| `host` | `job_bytes`, the memory one job may take on the host, a whole number; `providers`, the names of the providers it brought, an array of strings: exactly the provider routes' names, in their order |
| `choice` | the name of the route chosen: a route of `routes` |
| `why` | a sentence |
| `stable` | below |
| `kept` | below |
| `shadow` | below: exactly when `choice` is a provider's |
A route, an object of:
| member | |
|---|---|
| `route` | its name: `method:<name>`, or `provider:<the person's name for it>`, neither name empty |
| `kind` | `local` for a method, `provider` for a provider |
| `class` | a string: the class its result has at worst on this job. A built-in method's is held to the table below; a method a host registered that declares one class has that one, and one that declares several has the first of `bit-exact`, `exact`, `single`, `bounded`, `estimate` and `heuristic` that each of them meets (so `single` and `bounded` together are `heuristic`). A provider's route has the class its `provenance` gives (§8.4), and one that cannot run the job is `unknown` |
| `host_dependent` | `true` or `false`: whether the route depends on the host. A provider's, a method on a GPU (`statevector_single`, `cutrank_single`) and a method the host registered are, and no other built-in is |
| `auto_rank` | on a local route: its rank in `auto`'s order for the job's `question`, a whole number, or `null` where `auto` does not try it. A built-in method's is the table's below; a registered method's is the host's word |
| `price` | `null`, or an object: below |
| `outcome` | `chosen`, `met` or `rejected`. Exactly one route is `chosen`, and it is `choice` |
| `rejected` | exactly when `outcome` is `rejected`: `code`, one of the codes below, and `why`, a sentence |
| `approximate` | `true`, on a provider's route whose provider states its simulator is approximate |
and a provider's route may name its `provider`, `device` and `provenance`, a
method's its `method`, as words a person reads. Where a provider's route names its
`provenance`, its class is the one that gives (§8.4), and where the route chosen names
its `device` or `provenance`, they are the result's provider's (`planner_choice`).
The classes of the built-in methods, and which of them a route has on a job:
| method | classes | a route's |
|---|---|---|
| `exact` | `bit-exact` | |
| `statevector`, `tensor_network`, `density_matrix` | `exact` | |
| `statevector_single`, `cutrank_single` | `single` | |
| `pauli`, `pulse` | `bounded` | |
| `trajectories` | `estimate` | |
| `mps` | `exact`, `heuristic` | either; a route says `heuristic`, since a run can be truncated, and a result not truncated says `exact` |
| `stabilizer` | `exact`, `estimate` | `estimate` where the request gives a `noise` or a device `target`, else `exact` |
| `stabilizer_rank` | `exact`, `estimate` | `estimate` where its `options` give `terms` or `epsilon`, else `exact` |
| `cutrank` | `exact`, `single` | either: the memory a job has chooses the precision of its table |
Each is the class of the route listed in `routes`, priced or not, on a job that names
a provider as on any other: a shadow's class is its route's. The `accuracy.class` of a
result of a built-in method, and of a shadow's, is one of its method's; `contract_class`
and `shadow` hold them.
A price, an object of:
| member | |
|---|---|
| `money` | `free`: `true`, or `false` with `amount` and `currency` (strings, the amount as the provider gives it, at most 64 characters, with an optional `unit`), or `false` with `unknown`, a sentence. An unknown cost is not free (§7). A local route's is `free` `true`: only a provider's costs. `at_least`, absent or `true`: the amount is the least the job can come to, and what it takes decides the rest |
| `time` | `seconds`, a number from 0 to 10^12 or `null`; with a number, `basis`: `nominal`, at the engine's rate for the method's work, or `provider`, the provider's own estimate, queue included; with `null`, `why`. A host takes a provider's estimate past 10^12 seconds, or an amount past 64 characters, as no estimate: `null` here, `unknown` there, with why |
| `memory_bytes`, `work` | what a method takes, as whole numbers, a double past 2^53 − 1; `null`, and only `null`, on a provider's route |
| `energy` | `joules`, a number or `null`: an estimate of what the run takes, from past runs, with `basis`, and never a promise: a job's work leaves out what drives its time, and the same job read twice differs. With `null`, `why`. `watch`, a sentence, says what reads the run while it runs, and what that covers; with none, `unwatched`, a sentence, says why nothing can. A route has at most one of the two. A contract's `max_joules` is enforced by that reading, while the run goes (below): an estimate can only refuse a job clearly past a ceiling. No provider states a job's energy before it runs and no host reads one's run, so a provider's `joules` is `null` and it has no `watch` |
| `leaves_out` | what the price leaves out, an array of: `time`, it states no time; `amount`, it states no amount; `energy`, it states no energy; `mitigation`, what the provider's own mitigation costs or does, unless a client set it off or the device has none; `decoder`, any decoder's latency; `charged`, what the provider charges may differ from what it estimates; `cancel`, its client cannot stop a job it sent |
| `program` | on a provider's route, optional: the `shots`, `native_gates`, `two_qubit_gates` and `swaps` of what would be sent, and `expected_success` when a calibration states one |
**What is named.** `named` is `provider:` the request's `provider`, where it gives
a string; else `method:` its `method`, where that is a string other than `auto`;
else, for a request that names a device `target`, the one method that target's
noise takes, a local route of `routes` (the host's word: the engine's own choice
for a target); else `null`.
**The question** a job asks is read from its request alone: `noise` when it has a
`noise` other than `null`; else `counts` when its shots are above 0 (its `shots`,
a whole number written as one from 0 to 2^64 − 1, or, where it gives none or
another, 1024 when it gives no non-empty `observables` and no non-empty
`amplitudes`, and 0 otherwise); else `expectations` when it gives non-empty
`observables` and no non-empty `amplitudes`; else `amplitudes`.
**What `auto` picks** is read from the routes: `null` when `named` is not `null`;
for a request whose `fabric` is neither absent, `null`, `auto` nor `cpu`,
`method:statevector` where that route's `price` is not `null`, else `null`;
otherwise the local route whose `price` is not `null` and whose `auto_rank` is
least, the first in `routes` of equal ranks, or `null` when there is none. Each
built-in method's rank, by question:
| method | noise | counts | expectations | amplitudes |
|---|---|---|---|---|
| `exact` | | 20 | 20 | 10 |
| `statevector` | | 30 | 30 | 20 |
| `statevector_single` | | 40 | 40 | 30 |
| `stabilizer` | 20 | 10 | 10 | |
| `pauli` | | | 60 | |
| `mps` | | 60 | 70 | 70 |
| `stabilizer_rank` | | | 50 | 50 |
| `tensor_network` | | | | 60 |
| `cutrank` | | 50 | | 40 |
| `cutrank_single`, `pulse` | | | | |
| `density_matrix` | 10 | | | |
| `trajectories` | 30 | | | |
(an empty cell is `null`). So what `auto` picks is what a verifier finds: a host
cannot lower the class a job keeps by naming a weaker route `auto`'s. Whether a
route has a price is the host's word, held to what the engine gives by
`--recheck` (below).
**What is wanted.** `wanted` is the contract's `exactness` when it gives one,
with `wanted_from` `contract`. When it gives none, the job keeps what it would
have had: the class of the `named` route, with `wanted_from` `named`; when `named`
is `null`, the class of `auto`'s route, with `wanted_from` `auto`; and when `auto`
is `null` too, both are `null`. `named`, `question`, each built-in method's
`auto_rank`, `auto`, `wanted` and `wanted_from` must each be what these give.
**Each route's outcome.** A verifier recomputes it from the request, the route and
`host.job_bytes`, by these rules in this order: the first that applies is the
route's `rejected.code`, and a route to which none applies is not rejected.
1. `not_named`: `named` is not `null`, and this is not it.
2. `fabric`: `named` is `null`, and the request's `fabric` is neither absent,
`null` nor `auto`, and it is `cpu` and this is `method:statevector_single` or
`method:cutrank_single`, or it is anything else and this is not
`method:statevector`.
3. `cannot`: `class` is `unknown` and `price` is `null`.
4. `class`: `wanted` is not `null` and `class` does not meet it; or `wanted` is
`simulated`, the contract names it (`wanted_from` `contract`), and `approximate`
is `true`; or `wanted` is `null` and this is a provider's route. A contract
that names no class changes nothing a job already meets: an approximate
simulator the job names keeps its place.
5. `cannot`: `price` is `null`.
6. `memory`: `memory_bytes` is above `host.job_bytes`.
7. `providers`: this is a provider's route, and `contract.providers` is not
`allowed`.
8. `deadline_unknown`, `deadline`: the contract gives `deadline_seconds`, and
`time.seconds` is `null`, or above it.
9. For a provider's route whose `money.free` is not `true`: `cost_no_ceiling`, the
contract gives no `max_cost` and this is not the `named` route;
`cost_unknown`, it gives one and `money` has no `amount` that is an amount in
decimals, or no `currency`; `cost_currency`, the two currencies differ;
`cost`, the amount is above `max_cost.amount`; `cost_at_least`, `money.at_least`
is `true`, so what it will come to is not known to be within the ceiling.
Amounts are compared exactly, as decimals, with no double between them, and no
currency is converted. A
provider the request names, with no `max_cost`, meets this rule: its cost is
the person's to confirm (§7), as it is with no contract.
10. `energy_unwatched`, `energy`: the contract gives `max_joules`, and `energy`
has no `watch`, so nothing can stop the run at it; or `energy.joules` is above
it: an estimate past a ceiling refuses a job clearly over it, and one under it
promises nothing.
So an unknown never passes where something is asked of it, and nothing falls back:
not from exact to approximate, since a less exact route does not meet `wanted`; not
from a simulator to a device or the reverse, since neither's class meets the
other's. A cost ceiling confirms nothing: a run that is not free still needs the
person's word for that run (§7). Where a host chose the provider (the request names
none), that word names what the person saw: a host gives the route, who it is (its
kind, device, provenance and description, and what the person's name for it stands
for: its endpoint, settings and credential's place), its estimate's `money`, the
program and shots it would send, the job's `contract`, and how many runs of this job
the host has sent on such a word before, as a short digest, and sends the job only when
the word it is given is that digest made again at send time; a route, a device, an
estimate, a program or a contract that changed since is not sent, and a word sends
one run: the next run of the same job has another word. The count is read and the run
recorded under one lock, so two sends of one word do not both go; a run the provider
took is a run whatever became of it, and only one it refused before taking it leaves
the word unspent. A job that names a provider under a contract that allows one but
wants a class no provider's counts have is refused (`J071`) before the provider is
asked anything. A host asks no provider
anything (an estimate's request carries the compiled program, and a description
says a job is coming) where the contract does not allow one, or names no class a
provider's counts are in: with none named the job keeps a classical class, which
no provider's meets. And a host holds its reasoning to these rules before it sends
anything, since a receipt it would refuse afterwards would be a run paid for and
lost. If no route is left, the job is refused (`J071`) with each route's reason, and
there is no result to seal.
**The choice.** Of the routes these rules let through, the job runs the one this
picks, and `choice` names it:
1. when the contract gives no `exactness` and `auto`'s route is let through, it
stands: a contract that names no class changes nothing a job already meets;
2. otherwise the least of them in this order: `money.free` `true` first, then an
`amount` in decimals (at most 64 characters), the lesser first, then any other;
a `time.seconds` before a `null`, and then the lesser of each `seconds` × 1000
rounded to the nearest whole number, halves up (the floor of `seconds` × 1000 +
0.5); `local` before `provider`; the lesser `memory_bytes`, a `null` read as 0;
and the name, by its UTF-8 bytes.
`choice` must also be what ran: `method:` the result's `method`, or, when that is
`provider`, `provider:` its `provider.name`.
**`stable`** says what is the same on every host. It is an object of `scope`, a
sentence; `routes`, the names of the routes whose `kind` is `local` and whose
`host_dependent` is `false`, in the order of `routes`; `digest`; and `choice`.
- `digest` is the BLAKE3, as 64 lower-case hex digits, of the RFC 8785 text
(§8.2) of the object with `job_bytes`, `host.job_bytes`, and `routes`, an array
with one object for each of those routes in that order: `route`, `class`, and
`seconds`, `memory_bytes` and `work` from its price, each `null` when it has
none. It is the same for this request and `job_bytes` on every host of one
engine; a host that gives another has priced them otherwise, and a verifier that
holds the engine can price them again.
- `choice` is `true` exactly when `choice` names one of those routes, every route
with `host_dependent` `true` is rejected with `not_named`, `fabric` or `class`,
`contract.providers` is not `allowed`, and the contract gives no `max_joules`:
then the route chosen depends on nothing of the host.
**`kept`** says what the contract asked, against what happened. It holds exactly a
member for each of these the contract gives, each of exactly the members its row
names (`max_cost` of exactly `amount` and `currency`). A result that missed one is still an
answer, and it says so: a `kept` of `false` breaks no rule. A host that can stop a
run stops it at its deadline (`J074`), and seals nothing.
| member | |
|---|---|
| `deadline` | `seconds`, the contract's; `elapsed_ms`, a whole number or `null`: the result's `elapsed_ms` by value when its `method` is not `provider` and it states one; for a method's result that states none, so that its bits are the same on every host (a device target's, a pulse program's), the host's own time of the run; and for a provider's, the host's time from the job's submission to its counts; `kept`, whether `elapsed_ms` ≤ `seconds` × 1000, or `null` when `elapsed_ms` is `null` |
| `energy` | `max_joules`, the contract's; `joules`, the result's `stopped.joules`, else its `energy.joules_micro` ÷ 10^6, or `null` when it has neither; `kept`, `false` for a stopped run, else whether `joules` ≤ `max_joules`, or `null` when `joules` is `null` |
| `cost` | `max_cost`, the contract's; `charged`, equal to the result's `cost.charged` (numbers by value), or `null` when it has none; `kept`: with a `charged` in the ceiling's currency and an amount in decimals, whether it is at most the ceiling; with any other `charged`, `null`; with no `charged`, `true` when the result's `cost.free` is `true` or it has none, and otherwise `null` |
**The class given.** The result's `accuracy.class` meets `wanted` when `wanted` is
not `null`, and a result whose `accuracy.approximate` is `true` does not meet a
`simulated` the contract names. A route whose result falls short of `wanted`, whether the contract
named it or the job keeps what it would have had, gives no answer (`J072`): a
host seals no result this rule refuses.
**The shadow.** When a provider answers a contract job, a classical method that can
reach the same counts within the contract runs too. `planner.shadow` says which,
before anything runs, and the result's `shadow` carries what it gave. A shadow can
be run again by anyone; it is the one part of a provider's result that can.
`planner.shadow` is an object with `route`, a method's route `method:<name>`, or
`null`. With a route, which `routes` lists: `class`, a class of `bit-exact`, `exact`,
`single`, `bounded`, `estimate` and `heuristic`, and the one `routes` gives that route;
`budget_seconds`, what the contract leaves it: the
deadline less the chosen route's `time.seconds`, and 0 when that is more, the
deadline where it is `null`, and 300 with no deadline; `price`, a local route's
price, whose `time.seconds` is a number at most `budget_seconds`; and `request`,
an object of exactly `qasm`, the request's (a string); `shots`, the result's
(by value); `seed`, the request's `seed` (a whole number written as one, from 0 to
2^64 − 1, and 1 when it gives none or another) plus 2^32, modulo 2^53, a stream of
its own so that a seeded simulator's counts are not its counts by construction;
and `method`, what follows `method:` in `route`. No other member: a noise model or
any option would make the shadow another program. With `null`: `code`, one of
`budget`, `memory` and `none`; `why`, a sentence; and optionally `tried`, an array,
each method's own.
A result's `shadow` is one of:
- `ran` `false`, with `code`, a string, and `why`, a sentence: when `planner.shadow`
has no `route`, `code` is its `code`; otherwise `failed`, the method's run did not
complete. The provider's answer stands, alone, and says so; `--recheck` runs the
planned shadow, and a host whose failed shadow runs there is found.
- `ran` `true`, where `planner.shadow` has a route: with `route` and `request`,
equal to those of `planner.shadow` (numbers by value); `shots`, the result's;
`accuracy`, an object whose `class` is a string that meets `planner.shadow.class`
and is one of its method's; `counts`, keyed by outcomes all as long as the
provider's counts' (where they list one) and each of `0` and `1` only, each a whole number written as one from 1 to 2^53 − 1, and
`omitted_shots`, a whole number of at least 0 by value, which sum exactly to the
result's `shots` (a whole number by value); `distinct_outcomes`, a whole number
of at least 0, and, with no shot omitted, the number of outcomes listed; optionally
`elapsed_ms`; and `distance`.
`distance` is an object. With `computed` `false`, it has `why`, and it is `false`
only when the provider's or the shadow's `omitted_shots` is not 0 (by value: `0`
however it is written is 0). Otherwise it is `computed` `true` and, with a_k and b_k
the provider's and the shadow's count of each outcome k in either, S the result's
`shots` read as a whole number from 0 to 2^64 − 1 (its double's whole part, and
2^64 − 1 past it), and the outcomes taken in the order of their UTF-8 bytes:
- `numerator`, the sum of |a_k − b_k|, and `denominator`, 2·S, whole numbers,
each exact and written as a double past 2^53 − 1;
- `total_variation`, `numerator` ÷ `denominator`;
- `hellinger_fidelity`, R·R ÷ (S·S), with R the sum, in that order, of √(a_k × b_k),
each count taken as a double and the product a double's;
- `sampling_tvd_bound`, the lesser of 1 and 0.5·F ÷ S, with F the sum, in that
order, of m(a_k + b_k): a bound on the total variation these shots alone would
leave between two samples of one distribution, on average, for every number of
outcomes and shots. m(n) is what a fair coin's split of n leaves between its two
sides on average, E|2B − n| with B binomial (n, ½): 0 for n = 0; for n = 2j + 1
or 2j + 2 with j < 4096, the product, from 1 and in that order, of (2i + 3) ÷
(2i + 2) for i from 0 to j − 1; and from j = 4096, (2j + 1) × c with c = (1 −
1 ÷ (8j) + 1 ÷ (128·j·j) + 5 ÷ (1024·j·j·j)) ÷ √(π·j), each term as written. Given
how many shots of each outcome the two lists hold together, two lists of S shots
each split them at least as tightly as the coin does, so F ÷ 2S is at least the
expected total variation.
A count is a whole number written as one (no fraction, no exponent) from 0 to
2^64 − 1, and 0 when it is anything else or absent. S of 0, or counts that sum to
more than 2·S, gives no distance, and a `distance` that states one then does not
hold. Each double is IEEE 754, rounded to nearest, computed in the order
written, so that every verifier gets the same bits.
**Reasons.** A verifier reports each of these that applies, beside those of §8.4:
| reason | when |
|---|---|
| `contract_form` | the request's `contract` is not of its form (of §8.7, then reported alone); or, under a contract, a provider's result of the earlier form (§8.4), which states neither where its counts come from nor what was done to them |
| `contract_provider` | the request names a `provider` and its contract's `providers` is not `allowed` |
| `planner_form` | a contract job's result has no `planner`, or one not of the form above: `format`, `routes` and each route's members (a local route's `auto_rank`, its money free, every `seconds` and amount within its bounds), `named`, `auto` and `choice` each naming a route of `routes` or `null` (`choice` a route), `question`, `host`, `why`, `stable` and `kept` an object, each present; or `named`, `question`, a built-in method's `auto_rank`, `auto`, `wanted` or `wanted_from` is not what the request and the routes give. Or a result with no contract carries a `planner`. Reported alone of §8.7, beside `contract_provider` |
| `planner_route` | a route's `rejected.code` is not what the rules above give it (or it is rejected when they let it through, or not when they do not), or the route `chosen` is not one and `choice` |
| `planner_choice` | `choice` is not the route the choice's rules pick from those the rules let through, whatever their `outcome` says; or it is not what ran: a method's result is of another method, and a provider's names another provider, or its `cost` is not its route's `money` (`free` alike, and `amount`, `currency`, `unit` and `at_least` in its `estimate` alike), or the route names a `device` or `provenance` that is not its provider's. Members are alike as numbers are by value (`7` and `7.0`, `-0` and `0`), a boolean only to a boolean, and `null` as an absent member |
| `planner_stable` | `stable.routes`, `digest` or `choice` is not what the routes give |
| `stopped` | the result's `stopped` is not of its form above, or is on a job whose contract gives no `max_joules` |
| `planner_kept` | `kept` does not hold exactly a member for each the contract gives, or any of those members is not what the table gives |
| `contract_class` | the result's class does not meet `wanted` |
| `shadow` | `planner.shadow` or the result's `shadow` is missing when `choice` is a provider's, or present when it is not; or `planner.shadow` is not of its form above (its route, class, budget, price and request), or the result's `shadow` is not of its form, its `request` is not what `planner.shadow` gives, its counts do not sum to the shots, its class does not meet the plan's, or its `distance` is not what its counts give. Or a result with no contract carries a `shadow` |
A verifier also states, for a contract job, its `choice`, `wanted`, the class given,
`stable.choice`, `kept`, whether its shadow ran, and its `total_variation`. A price
is the host's word, as everything it says it received, sent and checked is (§8.5):
what a verifier recomputes is what follows from it. A verifier that holds the engine
can price the routes again and run the shadow again
(`wai-quantum verify --recheck`), which holds `digest`, `named` and `auto` to what
this engine gives (`auto` over the routes that depend on the host as the host stated
them: a verifier here has neither its GPU nor its registered methods); holds the
shadow's counts and class to a run here; runs a shadow the host says failed; and,
on the same engine, plans the shadow again, so that which method ran, or that none
could, is what this engine gives: a verifier without one takes those on the host's
word. A receipt sealed by another engine whose shadow did not run is held neither
way: what this engine plans is said, and the recheck does not say it holds. So is any
receipt sealed by another engine version: a recheck there gives `holds` `null` unless
something differs.
The free job service reaches no provider. It lists none among its routes, a
contract that wants a device's class is refused there (`J071`), and a request that
names a provider is refused there as it always is (`J002`).
### 8.8 Whose key, and when
§8.4 says whether a key signed a job. It does not say whose key it is, whether
that key was still its holder's when it signed, or whether the receipt is still
offered. Those are the reader's to decide, and a verifier takes them as two
things the reader gives it, each optional: a **trust list**, and a **time**. With
neither, a verifier judges the seal and the rules of §8.4 and §8.7 alone, and its
verdict says nothing was held to a list.
**The trust list** is a key set in the form the job service publishes
(`wai.quantum.receipt-keys/1`, at `/.well-known/wai-quantum-receipt-keys`), so a
reader who trusts a service fetches its set and gives it as it is; a reader who
trusts a person's key writes one entry. It is an object:
| member | |
|---|---|
| `format` | `wai.quantum.receipt-keys/1` |
| `algorithm` | optional; `ed25519` when present |
| `keys` | an array of entries, each an object |
and an entry:
| member | |
|---|---|
| `key` | the public key, 64 lower-case hex digits; a key is listed once |
| `not_before_ms` | optional: the first moment of the window the key signed in. Absent or `null`: open from the start |
| `not_after_ms` | optional: the first moment after that window. Absent or `null`: no end |
| `revoked_from_ms` | optional: the time from which whoever keeps the list no longer vouches for what the key signed. Absent or `null`: not revoked |
| `transition` | optional: for a key retired by rotation, the statement both keys signed (below). Absent or `null`: none |
Each time is a whole number of milliseconds since 1970, from 0 to 2^53 − 1,
however it is written. Any other member (the service's `did`, `current`,
`successor` and `note`) is ignored. A window is half-open: `not_after_ms` is its
successor's first moment and not its own last.
A **transition** is `{"statement", "retiring_signature", "successor_signature"}`,
its statement `{"format": "wai.quantum.key-transition/1", "retiring", "successor",
"at_ms"}` with each key as 64 lower-case hex digits. Each signature (128
lower-case hex digits) is Ed25519, strictly (§8.2), by the key it is named for,
over BLAKE3 of the 26 ASCII bytes `wai:quantum-key-transition`, the byte `0x01`,
and the statement's canonical text. A transition on an entry must name that
entry's key as the one `retiring` and be signed by both keys.
The list is read as a job's text is (§8.2). One that does not read, or is not of
this form in any entry, is refused whole: the verifier judges nothing by it, says
why, and gives no verdict (the two verifiers exit 2). A list is never read in
part.
**The reasons.** Held to a list, to a time, or to both, a verifier reports each of
these that applies beside those of §8.4, for a receipt of its form (one for which
`receipt_form` is not reported, and none of the reasons reported alone). `issued`
is the receipt's `time.issued_ms`, and "no time" a receipt with no `time`:
| reason | when |
|---|---|
| `key_untrusted` | a list is given, and no entry's `key` is the receipt's. The next two are then not reported |
| `key_window` | the entry has a `not_before_ms` and `issued` is before it; or it has a `not_after_ms`, and `issued` is at or after it or the receipt seals no time |
| `key_revoked` | the entry has a `revoked_from_ms`, and `issued` is at or after it or the receipt seals no time |
| `expired` | a time is given, the receipt has a `not_after_ms`, and the time given is at or after it |
| `issued_later` | a time is given, and `issued` is after it by more than the allowance given with it (0 when none is) |
A receipt that seals no time can be shown to precede no moment, so it passes a
window with no end and a key that is not revoked, and fails a window that ends and
any revocation. Held to a time alone it gets no reason: it states nothing to hold
to one. A set (§8.6) is placed by its own receipt; its items are bound by its
root.
A verdict states what it found and what it was held to: `time`, `{"sealed": true,
"issued_ms", "not_after_ms"}` (the end `null` when the receipt states none) or
`{"sealed": false}`; with a list, `trust`, `{"listed": true, "not_before_ms",
"not_after_ms", "revoked_from_ms"}` as the entry gives them (`null` for each it
does not) or `{"listed": false}`; and with a time, `checked_at_ms` and `skew_ms`.
`wai-quantum verify --trust KEYS.json --at MS --skew-ms MS` and
`receipt_verify.py --trust KEYS.json --at MS --skew-ms MS` take them alike;
`--at now` on the command line reads its machine's clock.
**What this proves, and what it does not.** The time is the signer's word, signed.
An honest signer's receipts are placed where they were made, so a rotation's
window and a receipt's own end hold against whoever shows an old receipt again.
It does not hold against the key's holder: whoever has a key can seal any time
with it. So a key that is retired, or revoked because its holder stopped using it,
is revoked from that moment, and what it signed before stands. A key that was
taken by someone else is revoked from 0, or from the moment it was last known
safe, by whoever keeps the list: every receipt it signed from then is refused,
whatever time it states, and so is every one that states none. A revocation is in
the list a reader holds, and reaches a reader when they fetch the list again;
nothing in a receipt carries one.
### 8.9 The verified answer
A receipt shows that a result is the one its signer sealed. It does not show that the
answer is right. A job MAY say what its answer must do, and its result then seals how far
that was checked, by what, and what that check does not show.
**The intent.** A job's `intent` is one intent or a list of them. Each names one target,
with its tolerance where it has one:
| intent | it is of | what must hold |
|---|---|---|
| `{"distribution": {"00": 0.5, "11": 0.5}, "tolerance"}` | the program | the exact distribution of the measured bits is within `tolerance` of it in total variation distance |
| `{"expectation": {"observable": "Z0 Z1", "value": 1.0}, "tolerance"}` | the program | the observable's exact value on the program's state is within `tolerance` of `value` |
| `{"equivalent": "<program>"}` | the program | the same circuit as the reference, up to a global phase |
| `{"prepares": "<program>"}` | the program | the same state from \|0…0⟩, the same qubits measured into the same bits |
| `{"sampled": {…}, "test", "alpha", "shots", "seed"}` | a run made for it | samples of the program, run again as the job runs it, are consistent with the distribution by the test at the level |
| `{"outcomes": ["00", "11"], "tolerance"}` | the answer | no more than `tolerance` of the answer lies outside the outcomes named |
| `{"conserves": {"weight": 2}}`, `{"conserves": {"parity": 0}}`, with `tolerance` | the answer | no more than `tolerance` of the answer lies outside the outcomes whose digits sum to `weight`, or whose digits' sum modulo 2 is `parity` (a symmetry of the problem) |
| `{"counts": {…}, "test", "alpha"}` or `{"counts": {…}, "tolerance"}` | the answer | the counts the job returned are consistent with the distribution by the test at the level, or within `tolerance` of it in total variation distance |
| `{"agrees": "simulation", "test", "alpha"}` or with `tolerance` | the answer | the counts the job returned are consistent with, or within `tolerance` of, the exact distribution the engine computes of the program |
Any intent MAY carry a `name`, a `note`, and `"require": "proof"`, under which a check
that is only evidence counts as not decided. An intent that does not read is refused
before anything runs (`J020`). A provider's job is read before it is sent, so a job is
never paid for and then refused its verdict; a `sampled` intent is refused there, since
no run of the engine stands for a device's: its samples are held to a distribution by
`counts`, and to the program's by `agrees`.
**The verdict.** The result carries `verdict`, of the form `wai.quantum.verdict/1`
(`wai-quantum/schemas/verdict.v1.json`), inside what the receipt seals. It states a
`level` (`refused`, `parsed`, `executed`, `evidence`, `proof`), `holds` (`true`, `false`,
or `null` when an intent was not decided), each intent's check under `intent`
(`outcome`: `holds`, `fails` or `unchecked`; `strength`: `proof`, `evidence` or `none`;
`how`; `why`), and since this section the **layer** that decided it and that layer's
**limits**, for each check and for the verdict:
| `layer` | what the engine did (`how`) | strength | its limits |
|---|---|---|---|
| `exact_simulation` | computed the program's exact state, distribution or expectation value and compared (`statevector`, `density_matrix`, `exact`, `state`); for an energy-landscape job, every arrangement (`enumeration`) | proof | only where the engine can simulate: up to 26 qubits without noise, 16 under the job's noise, every basis state up to 8 qubits, every arrangement up to 16 variables; in double precision; of the program as written, not of a device |
| `stabilizer` | equal stabilizer tableaus (`tableau`) | proof | Clifford circuits only, at any width |
| `pass_equivalence` | each rewrite or compile pass checked where it acts, the distances added (`rewrites`; a compile's `proof`, `approximate`) | proof | what the engine's rewrite rules reach; an approximate compile holds within its stated bound |
| `reading` | what the reader found of the two programs decides it (`shape`) | proof | decides a difference only |
| `certificate` | the answer itself held to the checks its problem states (`checks`) | evidence | the answer passes those checks; nothing shows no better answer exists |
| `random_states` | the two circuits run on random product states (`random`) | evidence | up to 22 qubits, on the inputs drawn |
| `agreement` | the counts the job returned held to the exact distribution the engine computed of the program (`chi_squared`, `g_test`, `total_variation`) | evidence | only where the engine can simulate the program; a test that does not reject is no proof |
| `samples` | samples held to a stated distribution (`chi_squared`, `g_test`, `total_variation`) | evidence | no more than its samples can show; the distribution is the one the intent states |
| `physical_consistency` | the counts the job returned obey a conserved quantity or lie in a set of outcomes (`counts`) | evidence | a necessary condition on the samples returned: nothing of the outcomes' weights, nor of an outcome too rare to be sampled |
| `unchecked` | nothing decided it | none | the answer stands on the run alone, by a method of the stated accuracy class |
A host that seals a verdict:
1. MUST decide each intent by the strongest layer it has for it. An `outcomes` or a
`conserves` intent is decided by `exact_simulation` where the run is the program's own
(not a device's, nor a device model's) and the engine can compute its distribution, and
on the counts otherwise.
2. MUST name, for each check, the layer its `how` is of, and the strength that layer has in
the table. It MUST NOT name a stronger one.
3. MUST say `unchecked`, with why, where no layer decided an intent: the program is wider
than the engine simulates, measures in the middle, the job returned no counts, or its
counts leave out some of its shots. A verdict with no intent, or with one intent
undecided, has the layer `unchecked` and `holds` `null`.
4. MUST give the verdict the weakest of its checks' layers, in the table's order from the
bottom, and that layer's limits.
5. MUST name in `executed` the method the result names and the accuracy class the result
states.
**The accuracy class.** Every result a host seals states what its numbers are, under
`accuracy.class`: `exact`, `single`, `bounded`, `estimate` or `heuristic` for the engine's
methods; `bit-exact` for a qudit job and for the bit-exact method; `measured` for a
processor's counts and `simulated` for a simulator's (§8.1); `exact` for a wasmQ job by
the tableau and `bit-exact` for one by the state vector (quantum-wasmq §11.4); and
for an energy-landscape job the parity its member ran at, `bit-exact` or `statistical`
(`member.parity`). A verdict repeats it under `executed.accuracy`, so the class is beside
the layer wherever the verdict is read.
**Which jobs carry one.** A circuit job run by the engine, a provider's job and a qudit
job (`wai.quantum.qudit/1`, whose intents are of its counts: `outcomes`, `conserves`,
`counts`, in its digits, `parity` modulo its dimension) take `intent` in the request and
seal `verdict` in the result. An energy-landscape job states what its answer must do in
its program, and seals its own word (quantum-landscape §3.1): a verifier reads `solved`
with a truth as `holds` by `exact_simulation`, `solved` on the program's checks alone as
`holds` by `certificate`, `missed` and `failed` as `fails`, and `unchecked` as
`unchecked`. A wasmQ job's form is closed (quantum-wasmq §6.5) and holds no intent.
**A sign-off.** A job whose form holds no intent, or one another host sealed, is given
one by a job about it. Its request is `{"signoff": {"job": <the job, as its host returned
it>, "intent": <an intent, a list, or absent>}}`. Its result has `format`
`wai.quantum.signoff/1`; `job`, with `digest` (the digest that job's own receipt signs,
§8.2, in hex), `key` and `form`; `accuracy.class`, the job's own (`unstated` where it
states none); and `verdict`: the job's own sealed verdict where the sign-off gives no
intent, otherwise the intent checked against the job, a job with counts and no program
the engine reads being held to its counts. A host MUST refuse to sign off a job whose
receipt does not verify (`J001`), a job whose sealed verdict or compile certificate
differs from what the job bears out (`J001`: the job is not valid though its seal holds,
and a sign-off would seal its verdict again under another key), and a sign-off of a
sign-off. The sign-off's receipt seals the job, the intent and the verdict together under
the signing host's key.
**What a verifier does.** Given a job whose receipt and every rule of §8.4 hold and whose
result seals a verdict, a verifier works the verdict out again and states it under
`answer`, beside the receipt's check:
- `level`, `holds`, `layer`, `limits` and `accuracy`, as the verdict's own checks conclude
them;
- `again`: `reproduced` when every check was decided again here and is the same;
`partly` when some were and the rest could not be; `host_statement` when none could be:
the verdict is then the host's word under its seal, and a reader takes it as that; and
`differs` when the verdict is not what the job bears out;
- `checks`, each with the layer, what was sealed, what was found here and its `status`;
`differences`; and `statement`, in one line.
A verdict `differs`, and the job is then not `valid` though its seal holds, when: its
`level`, `holds` or `layer` is not what its own checks conclude; a check names a
strength or a layer its `how` does not have; it names a method or an accuracy class the
result does not; a sign-off names another job (its digest, its key, its receipt's form) or
another accuracy class than the job it holds gives; it checks other intents than the
request gives; or a check decided here has another outcome, or only a weaker strength,
than the one sealed. Two cases are the host's statement and not a difference, since only
the host could show them. A `sampled` check's samples are of a run the host made: where
the samples drawn again here give another outcome, the check's own sealed samples
(`counts`) are held to its test at its level, and where they bear the sealed outcome out
the check is the host's statement (where they do not, it differs). And a check sealed by
another version of the engine (`engine.version`) that this one decides with the same
outcome and only a weaker strength is the host's statement, with both versions named. A verifier decides a
check again within the state vector of 22 qubits (11 under noise), and reads one past
that as the host's statement. A distance within 1e-9 of its tolerance on both sides is
the same. A verdict sealed before layers were named has none to compare, and is held to
its level and its checks. A record that copies a run's verdict and holds none of the run
(a project's check, which binds the run's receipt by its digest) is held to the verdict's
own form, and is otherwise the host's statement. `wai-quantum verify --lines` judges each line as a receipt
alone, and works no verdict out again.
A verifier simulates what a classical machine can. Where it cannot, the receipt says so,
and never claims a layer that did not hold.
### 8.10 A grade
A grade holds a program to a task's cases and seals what it found as a job, so that
anyone can run the grade again and reach the same answer.
**The request.** `{"grade": {"task": <the task>, "program": <the program's text>}}`, and
no other member. The task is in the exercise form (`wai-quantum/schemas/exercise.v1.json`):
- `intent`, one intent or a list, as §8.9 gives them; or `cases`, at most 16, each with a
`name`, `inputs` (a generator's integer inputs) and its own `intent` or the task's, at
most 8 intents each;
- `job`: `shots`, `seed` and `noise`, and nothing else;
- `constraints` on how the program is written: the gates allowed, gate counts, the T
count, the two-qubit count, the critique's smells, the resource estimate's counts, the
qubits, and the input whose value is the qubit an injected error flips;
- a `title` and a `task` in words.
A task MUST NOT carry `reference`, `wrong` or `hints`: the receipt seals the task whole,
and those would publish its answer with it. A host MUST refuse a task that is not of this
form (`J001`) or whose intents do not read (`J020`), before anything runs.
**Each case.** The program is read at the case's inputs and run as the task's job asks (16
shots at seed 1 where it names none), with the case's intent. The run's verdict decides
the case as §8.9 decides a job's, each intent by the strongest layer the host has for it.
A case is:
- `passed` when its intents hold, checked as proof or as evidence;
- `failed` when one does not hold, or when the reader refuses the program at the case's
inputs;
- `unchecked` otherwise: no layer decided an intent, or the run was refused (a job past
the host's memory).
**The result.** Its `format` is `wai.quantum.grade/1` (`wai-quantum/schemas/grade.v1.json`):
- `cases`, in the task's order, each with its `name`, `inputs`, `grade` and `passed`, the
verdict of its run (`verdict`), and that verdict's `level`, `holds` and `layer` beside it;
- `constraints`, each measured on the program as it reads in every case: `rule`,
`subject`, `holds` and `found`;
- `what_went_wrong` and `pointer`: what went wrong and where to look, in English, from the
reader's diagnostics, the verdicts and the critique, never from an answer;
- `summary`: how many cases have each grade, how many constraints there are, and how many
do not hold;
- `passed`: true when every case passed and every constraint holds;
- `verdict`: one verdict of every case's checks, in the cases' order. A case that ran no
check stands as its intents, each `unchecked`, with why. Its level, `holds` and layer are
what those checks conclude, as for one job, so its layer is the weakest of any case's.
The receipt seals the request and the result together: the task, the program and the
grade under the host's key. A host that seals no time (§8.2) seals the same bytes each time
it makes the same grade with the same key.
**What a verifier does.** A grade's result seals a verdict, so a verifier works it out
again as §8.9 says, the grade made again standing for the job run again. A grade also
differs, and is not valid though its seal holds, when:
- a case's verdict is not of its own form, or a case's `level`, `holds` or `layer` is not
its verdict's;
- a case's `grade` or `passed` is not what its verdict's checks give;
- the grade's `verdict` does not check its cases' checks;
- `passed` or `summary` is not what the cases and the constraints give;
- against the grade made again, a case's name, inputs or the method it ran on, or a
constraint as measured, is different.
A constraint that another version of the engine (`engine.version`) measured differently
is that host's statement, not a difference.
**A task set graded.** A grader MAY grade every task of a set at once. The batch is a
document, not a job: it is not sealed itself, and each of its grades is. Its `format` is
`wai.quantum.grades/1` (`wai-quantum/schemas/grades.v1.json`), and it holds:
- `grades`, one entry a task in the set's order, each with its `name`: the task's `id`
where it has one, else its `title`, which the grade's request seals with the task. A
set whose tasks do not each name themselves, once, is refused whole. An entry is one of:
- a grade: `job`, the grade as a host returns it (`{"request", "result"}` as above,
sealed), and `grade`, the task's: `passed` when the grade passed, `failed` when a case
failed or a constraint does not hold, `unchecked` otherwise;
- `missing`: no program was given for the task, with the `task`;
- `refused`: the grade was refused (the task is not of the exercise form, its intents do
not read, or it carries its answer), with the `task`, the `program` and why.
- `set`: BLAKE3 of the RFC 8785 canonical text of the list of the tasks, each as its
grade's request seals it or as its entry gives it, in order, in lower-case hex;
- `summary`: how many tasks there are, how many were graded, passed, failed and unchecked,
how many are missing and how many refused; and the graded tasks' cases (passed, failed,
unchecked) and constraints (broken) added up.
A grader that seals no time seals the same grades each time it grades the same set with
the same programs and the same key, so the batch is the same bytes.
**What a reader does with a batch.** It verifies each grade as a job (§8.4) and works it
out again (§8.9, as above), holding each entry's `name` and `grade` to what that grade's
request and result seal; grades a refused entry again and finds it refused; makes `set`
and `summary` again from the entries; and holds the batch valid only when every grade is
valid and nothing differs. What a batch does not show: that a task listed as missing had
no program, or that a refused one was given no other. Those are the grader's statements; a
reader who holds the programs grades them again.
## 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;
- **billing**: `describe` answers' `billing`, each with how a client reads it:
`free`, `priced`, `unpriced` (not free, and no price set), or `refused`, of
none of the three forms of §5.1, and so a description that is not complete;
- **estimates**: `estimate` answers, each with how a client reads it (§5.2):
`free`, `priced`, `unpriced` (not free, and no price set for the job: its cost
unknown, a landscape job's counts stated), or `refused`, of none of the three
forms;
- **results**: `result` answers to a job of two bits that asked a number of
shots, each with how a client reads it (§5.5): `kept`, its counts of the
shots the device kept (its `accepted_shots` when it gives one, else every
shot asked), or `refused`: counts that sum to neither, or an
`accepted_shots` that is not a whole number of at least 1. Nothing of a
refused answer is sealed;
- **landscape**, for a device whose `describe` has `landscape` (§11): its
description's form; messages that each break one rule of §11.2, each refused
with its code; and jobs, each estimated (its `operations` and `bytes` as the
vector gives them), run when every estimate says free or the person allows the
spend, followed to `done`, its answer read twice and found the same, of the
version its request names and held to quantum-landscape §5.4 as that version
reads it, and, from a member whose parity is `reference` or
`bit-exact`, its replay digest the vector's; and each job's `cancel` answered by
how it ended. The set holds messages and jobs of both versions of the landscape
protocol. A job's version is its request's `protocol` (the first where it names
none), and a device is sent the jobs of the versions its description lists
(§11.1). A message that names its `version` is sent to a device that lists that
version; one that names none holds in every version, and a device that does not
list the first is sent it with its own version named in the request. A device is
also sent one job of each version it does not list, which it must not take:
`unsupported`, or `refused` from a provider of this text as it stood before the
second version (§11.2).
The checker (`wai-quantum providers test NAME`) also checks the `describe`
answer in full (its `billing` one of the three forms), each `estimate` answer it
is given (one of §5.2's three forms; an unpriced one is a cost it does not know,
so the jobs run only with the spend allowed), that zero shots are
`invalid`, and that a program in another basis is refused. A device that runs no
circuits (§11.1) is held to the messages and to its landscape vectors, and its
circuit programs are not run. It runs the
programs only when every estimate says free, or the person allows the spend
(`--yes`). A program's counts are of the shots the device kept (§5.5), and its
vector's shares are read of those. It then asks to cancel each job it ran, and holds the answer to
§5.6: `done` for a job that finished, and `failed` with its reason for one that
failed (or `unsupported`, from a provider that cannot cancel).
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. It states its counts' source and its `execution` (§5.5). It takes
energy-landscape jobs of both versions on the reference's own kernels (§11), unless
it is told not to (`--no-landscape`) or to take one version alone
(`--landscape-protocol 1`, or `2`); with `--qubits 0` it takes those alone. With `--unpriced` it
sets no price: its `billing` is §5.1's third form, and every estimate §5.2's
unpriced form.
**Receipts.** `quantum-conformance/receipts/` holds job receipts and what a
verifier must say of each (§8.4): receipts made by the command line on this
machine and by the job service, provider jobs of every built-in kind through
services that follow their contracts, jobs under a contract (§8.7), the earlier
form, the first (receipts the
first form's own writer sealed, and others `generate.py` made from §8.2's text),
receipts that seal a time (§8.2), and receipts sealed to break one rule each. Its
`held/` holds receipts judged held to a trust list or a time (§8.8), each rule at
its bound on both sides, with what each is held to and the reasons a verifier must
then give; and its `trust/` the trust lists, those a verifier reads and those it
must refuse whole. A
receipt from a person's account names that account, and is not published: it is
checked with the differential's `--extra`, outside CI. A receipt once made is
kept, pinned by its digest. Two verifiers judge every one of them, and every
receipt made from a valid one by changing one member, numbers among them to
values past a double's range of whole numbers: `wai-quantum verify`, and
`quantum-conformance/tools/receipt_verify.py`, written from §8 alone in another
language and sharing no code with the engine. They must report the same reasons
for every receipt, and the manifest's for each in the corpus. Every other
surface that verifies is held to the manifest by its own tests, given each
receipt's text: the job service over HTTP and as a tool, the command line's tool
(each takes the job's text), the Python package (text, bytes or a path), the
desktop app and a browser tab. A job is judged from text wherever there is text:
a tool or a view given a job as members of its message (the job as an object, a
call's arguments as the job's own members, a view's object) judges those members
as the message's text writes them, read as §8.2 reads a job, so that a member
named twice (`unreadable`) and a whole number past a double (`inexact_number`) are
seen in a job given either way. Each such verdict says how the job was given
(`given`): `text`, the job's own text; `members`, as the message wrote them; or
`parsed`, a value some reader had already parsed with no text behind it (a call
made inside a program), whose verdict also names what it could not see
(`not_seen`: a member named twice, and a whole number past what that reader keeps).
`members` says that the message's own text was judged, and no more: a sender that
parsed the job before it wrote the message had already lost what parsing loses, so
a job is sent as its text where that matters. A job parsed deeper than a text is read (127) is
`unreadable` there too, however deep it is: a message that carries a job (a tool's
call, a view's request) is read so that the job reaches its verdict (a job nested past
127, or past what the surface reads, is judged before the message is read whole, and the
message around it may nest at most 8 more), and a message that does not read is the
protocol's error, as a line that is not UTF-8 is to the command line's MCP server, which
answers it and reads on. The Python package judges a dict as the text it writes
for it, so a dict's nesting and its whole numbers are judged as text's are; only a
member named twice is past what a dict can hold. It reports every other reason, and is held to the
manifest on every receipt but those. A tool's text is a JSON string, which holds
UTF-8 only, so text that is not UTF-8 reaches a verifier only as a file's bytes.
## 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.
A result's receipt seals every member it holds, so a member a later text adds
to the result is in no receipt sealed before it, and every such receipt
verifies as it did (§8.4's earlier form). `counts_source`, `execution`,
`compiled.qasm3`, the class `simulated` and `accuracy.approximate` were added
so, as were the answers' `counts_source` and `execution`, `describe`'s
`approximate`, and a request's `contract` with a result's `planner` and `shadow`
(§8.7): a receipt of a request with no contract verifies as it did. So was
`cost.confirmed_by` (§8.1), which no receipt sealed before it holds.
A receipt's `time` (§8.2) was added within this version, and differently: it is
inside the digest, under a domain byte of its own, so that it cannot be taken off.
Every receipt sealed without one verifies as it did, and a verifier of this text
says of it that no time is sealed. A verifier of the earlier text ignores a
receipt's `time` as a member it does not know, computes the digest of a receipt
with none, and reports `digest_mismatch`: it fails closed on a receipt that seals
a time, and never takes one for a receipt that seals none. So a host seals a time
only for readers whose verifier knows it; the job service does by default and can
be told not to, and the command line does only when asked. A trust list and a time
to hold a receipt to (§8.8) are a verifier's inputs, not a receipt's members: a
verifier given neither answers as it did, with `time` added to what it states.
The basis `prxrzxx`, the gate `move` and `describe`'s `resonators` and
`moves` were added within this version too. A client that does not know
`prxrzxx` finds no basis it compiles to on such a device and refuses it; one
that does not know `resonators` takes a resonator's pairs for a qubit's, and
what it sends there a provider refuses (§4: a one-qubit gate on a resonator,
a measured resonator), so neither runs a program the device was not meant to
run. A verifier that does not know `prxrzxx` reports `provider_members` on a
result in it: it fails closed.
A form an earlier client already reads safely may also be added within this
version. `billing`'s third form, `{"free": false, "priced": false}` (§5.1),
was added so: a client that does not know it reads a `billing` of no form it
knows, and so a cost it does not know, which is what the form says; only a
checker that does not know it refuses the description. So was a `cancel`
answering `failed`, with its reason, for a job that had failed (§5.6): a client
reads a job's end from its `status` (§5.4), which has always said `failed`.
`estimate`'s third form, `{"free": false, "priced": false}` (§5.2), was added
the same way. A client that does not know it reads an answer that is not free and
names no amount, which it refuses as an estimate not of its form, and so takes the
job's cost to be unknown: it shows no figure, the job is not free, and it is
submitted only on the person's confirmation for that run, which is what the form
says. Such a client never runs the job as free, and loses only the counts the
answer states, which it does not show. A checker that does not know the form fails
the estimate; one that does reads it as a cost it does not know. A provider of the
earlier text never sends it, and a client of this text reads every answer such a
provider sends as it did.
Energy-landscape jobs (§11) were added so. `describe`'s `landscape` is optional, and
a client that does not know it sends no such job. A provider that does not know it
is sent none, since no client sends one to a device whose description does not
offer it; sent one all the same, it finds no `program` and answers `invalid`. A
device that takes landscape jobs and no circuits states `qubits` 0, which a client
of this version that does not know §11 reads as a device it cannot compile to. A
landscape job's result is not a provider's result of §8.1 (it has no `method`), so
§8.4's provider rules, and every receipt they hold, are as they were; its own rules
are `landscape_form`'s.
The second version of the landscape protocol, `wai.quantum.landscape/2`, was added
to §11 the same way, with `describe`'s `landscape.protocols`. A provider of the
earlier text states `protocol` alone, `wai.quantum.landscape/1`, and a client of
this text reads that as a device that takes the first version only, and sends it
no job of the second. A client of the earlier text sends no job of the second
version to any provider. It reads `protocol`, which is the earliest version a
device takes, and ignores `protocols`: a device that takes both is to it the device
it was, and one that takes the second alone states a `protocol` it does not know,
whose description it refuses, so it sends that device nothing. A provider of the
earlier text that is sent a second-version job all the same answers `refused`,
since its reader reads the first version's requests only; this text asks
`unsupported` of a device for a version it does not list, and a client reads
either as a job that was not taken. A receipt of a first-version job verifies as
it did; a second-version job's result names its version, and `landscape_form`
holds it to that version's rules (§8.4).
## 11. Energy-landscape jobs
A device may take jobs of [quantum-landscape](quantum-landscape.md),
`wai.quantum.landscape/1`, and of its second version,
[quantum-landscape-2](quantum-landscape-2.md), `wai.quantum.landscape/2`: no
circuit, but an energy over variables of three states, and a request for a state
of low energy. Such a job travels in that extension's own form, its operation and
its request, and its answer comes back as that extension's answer (§3 there): this
protocol carries them, and defines no second form of either. A device that says
nothing of them takes none, and nothing here changes a circuit's job.
A job's version is the one its request names under `protocol`, and the first
where it names none, as the extension reads a request (quantum-landscape-2 §2).
Wherever this section names a section of quantum-landscape, a job of the second
version is read by that section as quantum-landscape-2 changes it: its request and
its program (§2 there), its answer and its joules (§3, §4.3), its members (§5) and
the rules a verifier holds it to (§5.4). The bounds and the counts of
quantum-landscape §2.0 are the same in both versions.
The provider is the host of quantum-landscape §5: its reader, its truth, its
checks, and its placing of the job on the members it has (a CPU, a GPU, a board).
The answer says which member ran it, at what parity the provider found that
member's kernel, what its audit checked and which members it passed over, as that
extension's §3 says every answer does.
### 11.1 Describing it
A `describe` answer whose device takes landscape jobs carries `landscape`, an
object of:
| member | |
|---|---|
| `protocol` | the earliest version of the landscape protocol it takes: `"wai.quantum.landscape/1"` for a device that takes the first version, and `"wai.quantum.landscape/2"` for one that takes the second alone |
| `protocols` | optional: every version it takes, a non-empty array of `"wai.quantum.landscape/1"` and `"wai.quantum.landscape/2"`, none twice, `protocol` the earliest of them. Where it is absent the device takes `protocol` alone |
| `ops` | the operations it takes: a non-empty array of `run`, `sweep`, `compare` and `replay`, none twice |
| `members` | optional: what it places jobs on, in the order it offers them (quantum-landscape §5), each `{"id", "class"}` |
| `max_bytes` | optional: the most memory a job may hold on it, as quantum-landscape §2.0 counts it, where that is less than 268,435,456 |
A device that takes landscape jobs and no circuits gives `qubits` 0,
`native_gates` `[]` and `connectivity` `[]`; it still reads a program, and answers
`invalid` for one that does not read and `refused` for one that does (it has no
qubit to run it on). A client MUST NOT send a landscape job to a device whose
`describe` has no `landscape`, an operation it does not list, or a job of a
version it does not list: a device whose `landscape` has no `protocols` takes the
version its `protocol` names and no other. A device that lists a version takes
every operation in `ops` in it, on members that run that version
(quantum-landscape-2 §5): a member that does not is passed over, and the answer
says so under `refusals`.
### 11.2 The messages
`estimate` and `submit` carry, in place of `program`, `shots` and `seed`, the
member `landscape`: an object of exactly
- `op`: the operation, `run`, `sweep`, `compare` or `replay` (`examples` runs
nothing, and is not sent);
- `request`: the request of quantum-landscape §2, an object, its seed,
temperatures and parity among its members, and its `protocol`, which a job of
the second version MUST name (quantum-landscape-2 §2).
The provider answers:
- `invalid` for a message with both `landscape` and `program`, a `landscape` not of
that form, or an `op` not one of those four;
- `unsupported` for a landscape job on a device whose `describe` has no
`landscape`, for an `op` it does not list, or for a job of a version of the
landscape protocol it does not list (a provider of this text as it stood before
the second version answers `refused` to that last, as its reader does, and a
client reads either as a job not taken);
- `refused` for a request quantum-landscape's reader refuses (a `protocol` that
names no version of the extension among them), or one past the
device's own limit (`max_bytes`): `message` is that refusal's plain sentence, as
quantum-landscape §2 writes it, and the error object adds `line`, the line of the
program the sentence names, where it names one.
A provider reads a request as quantum-landscape §2 reads it, and checks every bound
of §2.0 there before it takes it, so a job it cannot run is refused at `estimate`
and at `submit` alike, and never after it was taken.
`estimate`'s answer is §5.2's, with `operations` and `bytes` added: the job's
operations and memory as quantum-landscape §2.0 counts them before anything runs.
They are the same for the same request on every host, and a client that counts
them otherwise refuses the estimate. Any billing unit of §5.2 holds; one operation
of §2.0 there is the natural one. A provider with no price for a landscape job
answers §5.2's unpriced form with them, `{"free": false, "priced": false,
"operations", "bytes"}`, and not `unsupported`, which would leave them unstated.
`submit`'s answer is §5.3's `job`. `status` and `cancel` are §5.4's and §5.6's,
whatever the job.
`result`'s answer, once the job is `done`, has `landscape`: the answer of
quantum-landscape §3 to the job's operation, of the version the request names
(its `protocol` says which), as the provider gave it; and
optionally `charged` (§5.5). It has no `counts`, `shots`, `counts_source`,
`execution` or `energy`, which say nothing of such a job: a member with a meter
states its reading where quantum-landscape §4.4 puts it, in the answer's
`joules.metered`. A `result` asked twice of the same job gives the same answer.
### 11.3 What the client checks, and what it seals
Cost and confirmation are §7's: a client shows a landscape job's estimate, or that
its cost is unknown, before it submits it, and submits one that is not free only
on the person's confirmation for that run.
A client checks the request before it asks anything, as quantum-landscape §2 reads
it, and refuses one the reader refuses. It checks the answer before it seals it:
it is the answer of the operation it sent, of the version it sent, and holds every
rule of quantum-landscape §5.4 for the request it sent, as that version reads them.
An answer that does not is refused, and nothing is sealed: an answer of one version
to a job of the other breaks rule 2 or rule 3 there.
The client seals the job as every host seals a job (§8.2): `request` is the
request it sent, with `provider` added, the person's name for the provider (a
request that names a provider already is refused before anything is sent); and
`result` is the answer with these added, each of exactly these members:
| member | |
|---|---|
| `provider` | `name`, the request's `provider`; `kind`, `http` or `process`; `protocol`, `wai.quantum.provider/1`; `device`; `provenance`; and `describe`, each as §8.1 has it |
| `job` | `id`, the provider's id for the job, and `polls`, the status checks before it finished |
| `cost` | `free`, `estimate`, `charged` and `confirmed`, as §8.1 has them: `charged` is `null` or `{"amount", "currency"}`, and `confirmed` is `true` exactly where the run was not free |
and `engine` and `schema` (the answer's version, `wai.quantum.landscape/1` or
`wai.quantum.landscape/2`), as every engine result. The
result has no `method`: it is no provider's result of §8.1, and §8.4's provider
rules do not apply to it. quantum-landscape §5.4's do (`landscape_form`), the
members above among them (rule 13 there).
The receipt proves what §8.5 says any receipt proves: that the key it names signed
this request and this answer, and nothing about whether the device ran it. The
state is the provider's word, as counts are, and so are the member it names and
the parity it states. What anyone can work out from the request and the answer is
held to it by the rules: the program's digest, the state's hash and the replay
digest. The energy can be worked out from the program, and `wai-quantum verify`
does so (quantum-landscape §5.4). An answer whose member's parity is `reference` or
`bit-exact` gives the reference's bits, so anyone can run the job on the reference
and compare its replay digest.
A job service that serves the public reaches no provider, for a landscape job as
for any (§1): a request there that names a `provider` is refused.