# 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 `. 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:`, or `provider:`, 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:`, 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": ""}` | the program | the same circuit as the reference, up to a global phase | | `{"prepares": ""}` | 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": , "intent": }}`. 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": , "program": }}`, 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.