open-standards/wai/extensions/quantum-providers.md
Transaction Science 887c959ad0
Some checks failed
wai-quantum command line / does this change touch it (push) Successful in 21s
wai-quantum studio and desktop / does this change touch it (push) Successful in 15s
wai-quantum editors / does this change touch it (push) Successful in 1m25s
wai-quantum contract / does this change touch it (push) Successful in 1m29s
wai-quantum docs, course and locales / does this change touch it (push) Successful in 1m27s
wai-quantum views in agent hosts / does this change touch it (push) Successful in 1m19s
wai-quantum for Python / does this change touch it (push) Successful in 1m25s
wai-quantum for Python / python · macos (push) Waiting to run
wai-quantum editors / editors · macos (push) Waiting to run
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Successful in 6m21s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 11s
wai-quantum contract / the policy is linked, and the deprecations page reads (push) Successful in 17s
wai-quantum docs, course and locales / the Studio's words in every locale · linux (push) Successful in 29s
wai-quantum docs, course and locales / the course and the command line's languages · linux (push) Successful in 3m12s
wai-quantum studio and desktop / studio · browser (push) Successful in 7m37s
wai-quantum contract / API compatibility · linux (push) Successful in 4m44s
wai-quantum studio and desktop / desktop app · linux (push) Successful in 6m13s
wai-quantum editors / editors · linux (push) Successful in 3m15s
wai-quantum views in agent hosts / views · the page and its engine (push) Successful in 7m36s
wai-quantum for Python / python · linux (push) Successful in 9m53s
wai-quantum views in agent hosts / views · a scripted host (push) Successful in 10m47s
wai-quantum contract / schemas and the committed tool lists · linux (push) Successful in 13m51s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 44s
wai-quantum docs, course and locales / every command and snippet in the docs · linux (push) Failing after 18m48s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 5m11s
wai-quantum command line / command line · linux (push) Successful in 25m52s
wai-quantum studio and desktop / desktop app · windows (push) Has started running
wai-quantum command line / command line · macos (push) Successful in 2m39s
wai-quantum studio and desktop / desktop app · macos (push) Has started running
Provider estimates: an unpriced form, counts stated and no amount, confirmed before it runs
A provider that could count a job but had no price for it had no ok estimate: a client
refused an estimate not free with no amount, so it answered `unsupported` and a landscape
job's operations and bytes reached the person only in a sentence.

- quantum-providers §5.2: an estimate is free, priced, or unpriced, `{"free": false,
  "priced": false}` with no amount, currency, unit or at_least. It is answered after the
  job is checked as submit would, states what the job's form counts (a landscape job's
  operations and bytes, §11.2; nothing more for a circuit), holds the person to no amount,
  and its cost is unknown: shown so, confirmed for each run (§7), recorded in the receipt
  with priced false and unknown (§8.1), meeting no ceiling (§8.7 cost_unknown). §10 says
  why it is additive: an earlier client refuses it as an estimate not of its form, takes the
  cost as unknown and runs nothing it would not have; an earlier checker fails it.
- wai-quantum-qpu: estimate_of reads the three forms and refuses priced beside an amount, a
  unit, at_least or free true, priced true, and priced of another type; Estimate::unpriced
  and NO_PRICE_FOR_JOB. The plan's and the receipt's estimate (job::estimate_fields, shared
  with landscape) carry priced false and unknown; the planner's money is unknown with that
  sentence, and the plan's confirm says the cost is unknown.
- The reference provider: Config::unpriced and --unpriced (not with a price): billing's
  third form, and the unpriced form for every estimate, a landscape job's counts with it.
- The checker reads every estimate, circuit and landscape, through estimate_of: an answer of
  no form fails, and an unpriced one passes as a cost it does not know, so the jobs run only
  with the spend allowed.
- provider-v1.json `estimates`: 7 read as free, priced or unpriced and 12 refused.
  schemas/provider.v1.json's estimate_answer is the three forms (oneOf), naming at_least and
  priced.

Tests: conformance::tests::every_estimate_vector_reads_as_it_says;
a_provider_that_sets_no_price_states_its_counts_and_runs_only_with_the_spend_allowed (the
unpriced reference conforms, skips without the spend and runs with it; an unpriced answer
with an amount fails every estimate); open_provider's estimate test (the form and seven
refusals); reference's argument test (--unpriced, its billing, its circuit and landscape
estimates, refused beside a price); protocol_schema
the_schema_takes_exactly_the_estimate_forms_a_client_reads, and the unpriced reference's
messages and answers through the client; the CLI's not-free test adds an --unpriced
provider over a process (run refused without --yes, its estimate unknown, run with it, the
receipt's estimate priced false; a landscape dry run with its counts and priced false, and
its run refused without --yes). wai-quantum-qpu.yml: library 108 → 110, and the new tests
by name.

Tested on the build host: wai-quantum-qpu cargo test (lib 111 on macOS, clients 20,
protocol_schema 5, providers 12); wai-quantum-cli providers:: (7) and tests/providers.rs
(11); clippy -D warnings on both, all targets.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-10 18:56:04 -05:00

106 KiB
Raw Permalink Blame History

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), which carry no circuit: they travel in that extension's own form, and come back as its answer (§11).

A provider is the program or service that answers the messages. A device is what it runs programs on: a processor, an emulator or a simulator. A client is what sends the messages; a host is the client's process, which holds the person's credential and signing key (the command line, the desktop app).

1. Principles

  1. The credential stays with the person. A host keeps a provider's credential in the operating system's keychain (or, where there is none, a file only its owner can read, labelled as such) and sends it only to that provider (§2). A credential MUST NOT appear in a browser page, a log, a receipt, a command line, an answer the host prints, or the hosted job service. The job service never routes a job to a provider and never takes a credential: a request that names a provider is refused there.
  2. Cost before submit. A client MUST show a job's estimate (§5.2) before it submits the job, and MUST NOT submit a job that is not free without the person's explicit confirmation (§7). An unknown cost is not free.
  3. Exact programs. A client compiles a circuit exactly to the device's native gates and connectivity and checks the compiled program against the circuit before sending it. Angles cross the wire exactly (§4).
  4. Returned, not recomputed. A provider's counts are sealed as the device it names returned them (§8): measured for a processor, simulated for a simulator or an emulator, which no device measured. The result says where the counts come from, since counts rebuilt from probabilities are derived and not shot records, and what was set off at the provider. Nothing recomputes them.

2. Transports

Every message is a JSON object, and so is every answer. A client sends one message and reads its answer before it sends the next on the same transport.

2.1 HTTP

A message is the body of a POST to the provider's endpoint (one URL for every operation), with Content-Type: application/json. The answer is the body of a 200 response, also for an answer with "ok": false (§3). Any other status is a transport failure, not an answer.

A credential, when there is one, goes in Authorization: Bearer <credential>. A client MUST NOT send a credential over http:// to any host but this machine (localhost, a loopback address); over the network it uses https://.

2.2 A local process

The provider is a program the person names, with its arguments, started by the client without a shell. Each message is one line of JSON on the program's standard input; each answer is one line of JSON on its standard output, in order. The program MUST write nothing else to its standard output. Its standard error is its own log, and a client MAY discard it. The program runs until its standard input ends.

A credential, when there is one, is in the program's environment as WAI_PROVIDER_CREDENTIAL, never on its command line. A client SHOULD NOT pass the program another provider's credential.

3. Messages and errors

Every message carries:

field
protocol "wai.quantum.provider/1"
op describe, estimate, submit, status, result or cancel
settings optional: the person's non-secret settings for this provider (a device name, a region), passed as they are

and the operation's own fields (§5). Every answer carries "ok": true and the operation's fields, or:

{ "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:

{
  "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), and what a job under a contract carries beside it (§8.7). 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

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.

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), the calibration, job, mitigation, decode, compile and rearrange receipts (quantum-ops), the bill of materials, the fabric, sparse-Pauli, resource-estimate and phasor receipts, each in its labelled form too (energy-measurement §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; or its key, digest or signature is not lower-case hex of its length. 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
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) whose request or result breaks a rule of quantum-wasmq §6.5, as §11.6 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) whose request or result breaks a rule of quantum-landscape §5.4, 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. Nothing sealed carries a time: a receipt can be shown again, and no window can be applied to it from its bytes. For the same reason 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) is not a program of §4: it has steps between gates that this protocol does not carry. A client MUST NOT send one to a provider; it runs where its runner is (quantum-wasmq §7).

8.7 A job under a contract

A job says what it wants of an answer in its request's contract, and the planner that took it says why the route it chose was the one, in its result's planner. A contract job is one whose request holds a contract member other than null. A job with none carries neither planner nor shadow, and plans as it did before this section was written, byte for byte.

The contract. An object of these members, each optional; null is absent.

member
exactness the class wanted: a string of the table below
max_cost the most a provider's estimate of the run may come to, before it runs: an object of exactly amount, an amount in decimals (one or more digits, optionally . and one or more digits, at most 64 characters in all), and currency, a string not empty. It bounds the estimate, not the charge, which no one knows before the run: what was charged is stated after it, beside the ceiling (kept.cost)
deadline_seconds a number above 0: when it must be done by, in seconds, as below
max_joules a number above 0: the most energy the run may take, in joules, enforced while it runs: a live reading stops it when it reaches this, and the receipt says so (below). A route with no live reading is refused under a ceiling. Nothing before a run can promise a figure: a job's work leaves out what drives its time, and the same job read twice differs
providers never or allowed: whether a route may be one of the person's providers. never is the default, given or not, and a request that names a provider under a contract that does not allow one is refused (J070)

A contract of any other form is refused (J070). So is any other member, which read as absent would be a requirement dropped in silence.

exactness is met by these classes of a result (§8.1, and the engine's own):

wanted met by
bit-exact bit-exact
exact bit-exact, exact
single bit-exact, exact, single
bounded bit-exact, exact, bounded
estimate bit-exact, exact, bounded, estimate
heuristic bit-exact, exact, single, bounded, estimate, heuristic
measured measured
simulated simulated, on a result that is not approximate
any every class

This is a table and not a ladder: single states no bound, so it does not meet bounded, and a device's counts are in no classical class.

What a deadline measures. For a run of one of the engine's methods, the time the engine took over the run itself (the result's elapsed_ms): not the queue, the planning, a restart before it, the check of its intents, nor the sealing. For a provider's run, the time from the job's submission to the moment the host first finds it finished: the provider's queue and its run count, and a host that comes back to a job late (one sent without waiting, collected later) counts the wait too, so its elapsed_ms can only overstate. A shadow is not counted: it runs in what the deadline leaves, or not at all. A host that can stop a run stops it at its deadline: the job service stops its worker, and nothing is sealed (J074). A provider's job is looked at only while a host follows it: past the deadline, the next look cancels it where the client can, and it is cancelled once the provider says so (J074). A job no one follows, or one a client cannot cancel (its route says so: cancel in leaves_out), runs on and can be billed; when it finishes, kept.deadline says it was missed.

What a ceiling measures, and how it is held. max_joules is held by a live reading, while the run goes: a host runs the job in a process of its own, reads the route's watch every few milliseconds as the host's scheduler allows, and kills that process when the reading reaches the ceiling, so the work stops there; only a host that can stop a run can give a route a watch. The reading is of what watch says, and of nothing else: a process's own CPU energy leaves out memory, the host's idle draw and any device it drives, so a method on a GPU or an accelerator has no watch; a processor package's counts every process on it, so a busy host stops a run sooner. A run can pass the ceiling by what it takes between two reads, and by what a counter has not yet shown. A host that runs a job inside a process doing other work, or that reads nothing, gives no watch, nor does a provider's route: the route is refused under a ceiling (energy_unwatched). A counter that stops reading during a run leaves nothing holding the ceiling: the host stops the run after a second of time without a reading, or with one that stood still while the run spent CPU time (a frozen counter is not a live one), and seals nothing. The run's process ends with the process that watches it, however that one ends. A host reads the run's process whole when it ends, killed or not (a package's counter added up read by read, each step corrected for a wrap, so that a run passing the counter's range is read in full; a process's read that is far below what its CPU time implies is no reading), and seals that figure in energy, so a run that passed its ceiling between two reads and finished is sealed as having missed it.

A run so stopped gave no answer. Its result has no member but method, qubits, gates, accuracy, stopped, elapsed_ms, planner, energy, engine, schema and receipt, so no counts, expectations, amplitudes, verdict, outputs, mitigation or distribution of any kind; its accuracy.class is none; and it has stopped, an object of exactly at, the string max_joules; joules, what the reading had come to, a number at or above the ceiling; max_joules, the contract's; elapsed_ms, a whole number and the result's elapsed_ms; and read, a sentence saying what read it and the longest time between two reads, the first counted from the start. Its energy.joules_micro, where it has one, is no less than joules × 10^6, less one for its rounding. It is sealed like any result, energy the whole figure of the process killed where the host could read it, its kept.energy says the ceiling was not kept, and a command line exits 4. A result whose energy was not read whole states kept.energy.joules null.

The reasoning. planner is an object of:

member
format wai.quantum.planner/1
routes every route the host could see, an array with at least one member, each route's name once
named the route the job names, or null: as below
question the question the job asks, as below: noise, counts, expectations or amplitudes
auto the route auto picks for this job, or null, as below
wanted, wanted_from the class the routes are held to, and where it comes from, as below
host job_bytes, the memory one job may take on the host, a whole number; providers, the names of the providers it brought, an array of strings: exactly the provider routes' names, in their order
choice the name of the route chosen: a route of routes
why a sentence
stable below
kept below
shadow below: exactly when choice is a provider's

A route, an object of:

member
route its name: method:<name>, or provider:<the person's name for it>, neither name empty
kind local for a method, provider for a provider
class a string: the class its result has at worst on this job. A built-in method's is held to the table below; a method a host registered that declares one class has that one, and one that declares several has the first of bit-exact, exact, single, bounded, estimate and heuristic that each of them meets (so single and bounded together are heuristic). A provider's route has the class its provenance gives (§8.4), and one that cannot run the job is unknown
host_dependent true or false: whether the route depends on the host. A provider's, a method on a GPU (statevector_single, cutrank_single) and a method the host registered are, and no other built-in is
auto_rank on a local route: its rank in auto's order for the job's question, a whole number, or null where auto does not try it. A built-in method's is the table's below; a registered method's is the host's word
price null, or an object: below
outcome chosen, met or rejected. Exactly one route is chosen, and it is choice
rejected exactly when outcome is rejected: code, one of the codes below, and why, a sentence
approximate true, on a provider's route whose provider states its simulator is approximate

and a provider's route may name its provider, device and provenance, a method's its method, as words a person reads. Where a provider's route names its provenance, its class is the one that gives (§8.4), and where the route chosen names its device or provenance, they are the result's provider's (planner_choice).

The classes of the built-in methods, and which of them a route has on a job:

method classes a route's
exact bit-exact
statevector, tensor_network, density_matrix exact
statevector_single, cutrank_single single
pauli, pulse bounded
trajectories estimate
mps exact, heuristic either; a route says heuristic, since a run can be truncated, and a result not truncated says exact
stabilizer exact, estimate estimate where the request gives a noise or a device target, else exact
stabilizer_rank exact, estimate estimate where its options give terms or epsilon, else exact
cutrank exact, single either: the memory a job has chooses the precision of its table

Each is the class of the route listed in routes, priced or not, on a job that names a provider as on any other: a shadow's class is its route's. The accuracy.class of a result of a built-in method, and of a shadow's, is one of its method's; contract_class and shadow hold them.

A price, an object of:

member
money free: true, or false with amount and currency (strings, the amount as the provider gives it, at most 64 characters, with an optional unit), or false with unknown, a sentence. An unknown cost is not free (§7). A local route's is free true: only a provider's costs. at_least, absent or true: the amount is the least the job can come to, and what it takes decides the rest
time seconds, a number from 0 to 10^12 or null; with a number, basis: nominal, at the engine's rate for the method's work, or provider, the provider's own estimate, queue included; with null, why. A host takes a provider's estimate past 10^12 seconds, or an amount past 64 characters, as no estimate: null here, unknown there, with why
memory_bytes, work what a method takes, as whole numbers, a double past 2^53 − 1; null, and only null, on a provider's route
energy joules, a number or null: an estimate of what the run takes, from past runs, with basis, and never a promise: a job's work leaves out what drives its time, and the same job read twice differs. With null, why. watch, a sentence, says what reads the run while it runs, and what that covers; with none, unwatched, a sentence, says why nothing can. A route has at most one of the two. A contract's max_joules is enforced by that reading, while the run goes (below): an estimate can only refuse a job clearly past a ceiling. No provider states a job's energy before it runs and no host reads one's run, so a provider's joules is null and it has no watch
leaves_out what the price leaves out, an array of: time, it states no time; amount, it states no amount; energy, it states no energy; mitigation, what the provider's own mitigation costs or does, unless a client set it off or the device has none; decoder, any decoder's latency; charged, what the provider charges may differ from what it estimates; cancel, its client cannot stop a job it sent
program on a provider's route, optional: the shots, native_gates, two_qubit_gates and swaps of what would be sent, and expected_success when a calibration states one

What is named. named is provider: the request's provider, where it gives a string; else method: its method, where that is a string other than auto; else, for a request that names a device target, the one method that target's noise takes, a local route of routes (the host's word: the engine's own choice for a target); else null.

The question a job asks is read from its request alone: noise when it has a noise other than null; else counts when its shots are above 0 (its shots, a whole number written as one from 0 to 2^64 − 1, or, where it gives none or another, 1024 when it gives no non-empty observables and no non-empty amplitudes, and 0 otherwise); else expectations when it gives non-empty observables and no non-empty amplitudes; else amplitudes.

What auto picks is read from the routes: null when named is not null; for a request whose fabric is neither absent, null, auto nor cpu, method:statevector where that route's price is not null, else null; otherwise the local route whose price is not null and whose auto_rank is least, the first in routes of equal ranks, or null when there is none. Each built-in method's rank, by question:

method noise counts expectations amplitudes
exact 20 20 10
statevector 30 30 20
statevector_single 40 40 30
stabilizer 20 10 10
pauli 60
mps 60 70 70
stabilizer_rank 50 50
tensor_network 60
cutrank 50 40
cutrank_single, pulse
density_matrix 10
trajectories 30

(an empty cell is null). So what auto picks is what a verifier finds: a host cannot lower the class a job keeps by naming a weaker route auto's. Whether a route has a price is the host's word, held to what the engine gives by --recheck (below).

What is wanted. wanted is the contract's exactness when it gives one, with wanted_from contract. When it gives none, the job keeps what it would have had: the class of the named route, with wanted_from named; when named is null, the class of auto's route, with wanted_from auto; and when auto is null too, both are null. named, question, each built-in method's auto_rank, auto, wanted and wanted_from must each be what these give.

Each route's outcome. A verifier recomputes it from the request, the route and host.job_bytes, by these rules in this order: the first that applies is the route's rejected.code, and a route to which none applies is not rejected.

  1. not_named: named is not null, and this is not it.
  2. fabric: named is null, and the request's fabric is neither absent, null nor auto, and it is cpu and this is method:statevector_single or method:cutrank_single, or it is anything else and this is not method:statevector.
  3. cannot: class is unknown and price is null.
  4. class: wanted is not null and class does not meet it; or wanted is simulated, the contract names it (wanted_from contract), and approximate is true; or wanted is null and this is a provider's route. A contract that names no class changes nothing a job already meets: an approximate simulator the job names keeps its place.
  5. cannot: price is null.
  6. memory: memory_bytes is above host.job_bytes.
  7. providers: this is a provider's route, and contract.providers is not allowed.
  8. deadline_unknown, deadline: the contract gives deadline_seconds, and time.seconds is null, or above it.
  9. For a provider's route whose money.free is not true: cost_no_ceiling, the contract gives no max_cost and this is not the named route; cost_unknown, it gives one and money has no amount that is an amount in decimals, or no currency; cost_currency, the two currencies differ; cost, the amount is above max_cost.amount; cost_at_least, money.at_least is true, so what it will come to is not known to be within the ceiling. Amounts are compared exactly, as decimals, with no double between them, and no currency is converted. A provider the request names, with no max_cost, meets this rule: its cost is the person's to confirm (§7), as it is with no contract.
  10. energy_unwatched, energy: the contract gives max_joules, and energy has no watch, so nothing can stop the run at it; or energy.joules is above it: an estimate past a ceiling refuses a job clearly over it, and one under it promises nothing.

So an unknown never passes where something is asked of it, and nothing falls back: not from exact to approximate, since a less exact route does not meet wanted; not from a simulator to a device or the reverse, since neither's class meets the other's. A cost ceiling confirms nothing: a run that is not free still needs the person's word for that run (§7). Where a host chose the provider (the request names none), that word names what the person saw: a host gives the route, who it is (its kind, device, provenance and description, and what the person's name for it stands for: its endpoint, settings and credential's place), its estimate's money, the program and shots it would send, the job's contract, and how many runs of this job the host has sent on such a word before, as a short digest, and sends the job only when the word it is given is that digest made again at send time; a route, a device, an estimate, a program or a contract that changed since is not sent, and a word sends one run: the next run of the same job has another word. The count is read and the run recorded under one lock, so two sends of one word do not both go; a run the provider took is a run whatever became of it, and only one it refused before taking it leaves the word unspent. A job that names a provider under a contract that allows one but wants a class no provider's counts have is refused (J071) before the provider is asked anything. A host asks no provider anything (an estimate's request carries the compiled program, and a description says a job is coming) where the contract does not allow one, or names no class a provider's counts are in: with none named the job keeps a classical class, which no provider's meets. And a host holds its reasoning to these rules before it sends anything, since a receipt it would refuse afterwards would be a run paid for and lost. If no route is left, the job is refused (J071) with each route's reason, and there is no result to seal.

The choice. Of the routes these rules let through, the job runs the one this picks, and choice names it:

  1. when the contract gives no exactness and auto's route is let through, it stands: a contract that names no class changes nothing a job already meets;
  2. otherwise the least of them in this order: money.free true first, then an amount in decimals (at most 64 characters), the lesser first, then any other; a time.seconds before a null, and then the lesser of each seconds × 1000 rounded to the nearest whole number, halves up (the floor of seconds × 1000 + 0.5); local before provider; the lesser memory_bytes, a null read as 0; and the name, by its UTF-8 bytes.

choice must also be what ran: method: the result's method, or, when that is provider, provider: its provider.name.

stable says what is the same on every host. It is an object of scope, a sentence; routes, the names of the routes whose kind is local and whose host_dependent is false, in the order of routes; digest; and choice.

  • digest is the BLAKE3, as 64 lower-case hex digits, of the RFC 8785 text (§8.2) of the object with job_bytes, host.job_bytes, and routes, an array with one object for each of those routes in that order: route, class, and seconds, memory_bytes and work from its price, each null when it has none. It is the same for this request and job_bytes on every host of one engine; a host that gives another has priced them otherwise, and a verifier that holds the engine can price them again.
  • choice is true exactly when choice names one of those routes, every route with host_dependent true is rejected with not_named, fabric or class, contract.providers is not allowed, and the contract gives no max_joules: then the route chosen depends on nothing of the host.

kept says what the contract asked, against what happened. It holds exactly a member for each of these the contract gives, each of exactly the members its row names (max_cost of exactly amount and currency). A result that missed one is still an answer, and it says so: a kept of false breaks no rule. A host that can stop a run stops it at its deadline (J074), and seals nothing.

member
deadline seconds, the contract's; elapsed_ms, a whole number or null: the result's elapsed_ms by value when its method is not provider and it states one; for a method's result that states none, so that its bits are the same on every host (a device target's, a pulse program's), the host's own time of the run; and for a provider's, the host's time from the job's submission to its counts; kept, whether elapsed_ms ≤ seconds × 1000, or null when elapsed_ms is null
energy max_joules, the contract's; joules, the result's stopped.joules, else its energy.joules_micro ÷ 10^6, or null when it has neither; kept, false for a stopped run, else whether joules ≤ max_joules, or null when joules is null
cost max_cost, the contract's; charged, equal to the result's cost.charged (numbers by value), or null when it has none; kept: with a charged in the ceiling's currency and an amount in decimals, whether it is at most the ceiling; with any other charged, null; with no charged, true when the result's cost.free is true or it has none, and otherwise null

The class given. The result's accuracy.class meets wanted when wanted is not null, and a result whose accuracy.approximate is true does not meet a simulated the contract names. A route whose result falls short of wanted, whether the contract named it or the job keeps what it would have had, gives no answer (J072): a host seals no result this rule refuses.

The shadow. When a provider answers a contract job, a classical method that can reach the same counts within the contract runs too. planner.shadow says which, before anything runs, and the result's shadow carries what it gave. A shadow can be run again by anyone; it is the one part of a provider's result that can.

planner.shadow is an object with route, a method's route method:<name>, or null. With a route, which routes lists: class, a class of bit-exact, exact, single, bounded, estimate and heuristic, and the one routes gives that route; budget_seconds, what the contract leaves it: the deadline less the chosen route's time.seconds, and 0 when that is more, the deadline where it is null, and 300 with no deadline; price, a local route's price, whose time.seconds is a number at most budget_seconds; and request, an object of exactly qasm, the request's (a string); shots, the result's (by value); seed, the request's seed (a whole number written as one, from 0 to 2^64 − 1, and 1 when it gives none or another) plus 2^32, modulo 2^53, a stream of its own so that a seeded simulator's counts are not its counts by construction; and method, what follows method: in route. No other member: a noise model or any option would make the shadow another program. With null: code, one of budget, memory and none; why, a sentence; and optionally tried, an array, each method's own.

A result's shadow is one of:

  • ran false, with code, a string, and why, a sentence: when planner.shadow has no route, code is its code; otherwise failed, the method's run did not complete. The provider's answer stands, alone, and says so; --recheck runs the planned shadow, and a host whose failed shadow runs there is found.
  • ran true, where planner.shadow has a route: with route and request, equal to those of planner.shadow (numbers by value); shots, the result's; accuracy, an object whose class is a string that meets planner.shadow.class and is one of its method's; counts, keyed by outcomes all as long as the provider's counts' (where they list one) and each of 0 and 1 only, each a whole number written as one from 1 to 2^53 − 1, and omitted_shots, a whole number of at least 0 by value, which sum exactly to the result's shots (a whole number by value); distinct_outcomes, a whole number of at least 0, and, with no shot omitted, the number of outcomes listed; optionally elapsed_ms; and distance.

distance is an object. With computed false, it has why, and it is false only when the provider's or the shadow's omitted_shots is not 0 (by value: 0 however it is written is 0). Otherwise it is computed true and, with a_k and b_k the provider's and the shadow's count of each outcome k in either, S the result's shots read as a whole number from 0 to 2^64 − 1 (its double's whole part, and 2^64 − 1 past it), and the outcomes taken in the order of their UTF-8 bytes:

  • numerator, the sum of |a_k − b_k|, and denominator, 2·S, whole numbers, each exact and written as a double past 2^53 − 1;
  • total_variation, numerator ÷ denominator;
  • hellinger_fidelity, R·R ÷ (S·S), with R the sum, in that order, of √(a_k × b_k), each count taken as a double and the product a double's;
  • sampling_tvd_bound, the lesser of 1 and 0.5·F ÷ S, with F the sum, in that order, of m(a_k + b_k): a bound on the total variation these shots alone would leave between two samples of one distribution, on average, for every number of outcomes and shots. m(n) is what a fair coin's split of n leaves between its two sides on average, E|2B − n| with B binomial (n, ½): 0 for n = 0; for n = 2j + 1 or 2j + 2 with j < 4096, the product, from 1 and in that order, of (2i + 3) ÷ (2i + 2) for i from 0 to j − 1; and from j = 4096, (2j + 1) × c with c = (1 − 1 ÷ (8j) + 1 ÷ (128·j·j) + 5 ÷ (1024·j·j·j)) ÷ √(π·j), each term as written. Given how many shots of each outcome the two lists hold together, two lists of S shots each split them at least as tightly as the coin does, so F ÷ 2S is at least the expected total variation.

A count is a whole number written as one (no fraction, no exponent) from 0 to 2^64 − 1, and 0 when it is anything else or absent. S of 0, or counts that sum to more than 2·S, gives no distance, and a distance that states one then does not hold. Each double is IEEE 754, rounded to nearest, computed in the order written, so that every verifier gets the same bits.

Reasons. A verifier reports each of these that applies, beside those of §8.4:

reason when
contract_form the request's contract is not of its form (of §8.7, then reported alone); or, under a contract, a provider's result of the earlier form (§8.4), which states neither where its counts come from nor what was done to them
contract_provider the request names a provider and its contract's providers is not allowed
planner_form a contract job's result has no planner, or one not of the form above: format, routes and each route's members (a local route's auto_rank, its money free, every seconds and amount within its bounds), named, auto and choice each naming a route of routes or null (choice a route), question, host, why, stable and kept an object, each present; or named, question, a built-in method's auto_rank, auto, wanted or wanted_from is not what the request and the routes give. Or a result with no contract carries a planner. Reported alone of §8.7, beside contract_provider
planner_route a route's rejected.code is not what the rules above give it (or it is rejected when they let it through, or not when they do not), or the route chosen is not one and choice
planner_choice choice is not the route the choice's rules pick from those the rules let through, whatever their outcome says; or it is not what ran: a method's result is of another method, and a provider's names another provider, or its cost is not its route's money (free alike, and amount, currency, unit and at_least in its estimate alike), or the route names a device or provenance that is not its provider's. Members are alike as numbers are by value (7 and 7.0, -0 and 0), a boolean only to a boolean, and null as an absent member
planner_stable stable.routes, digest or choice is not what the routes give
stopped the result's stopped is not of its form above, or is on a job whose contract gives no max_joules
planner_kept kept does not hold exactly a member for each the contract gives, or any of those members is not what the table gives
contract_class the result's class does not meet wanted
shadow planner.shadow or the result's shadow is missing when choice is a provider's, or present when it is not; or planner.shadow is not of its form above (its route, class, budget, price and request), or the result's shadow is not of its form, its request is not what planner.shadow gives, its counts do not sum to the shots, its class does not meet the plan's, or its distance is not what its counts give. Or a result with no contract carries a shadow

A verifier also states, for a contract job, its choice, wanted, the class given, stable.choice, kept, whether its shadow ran, and its total_variation. A price is the host's word, as everything it says it received, sent and checked is (§8.5): what a verifier recomputes is what follows from it. A verifier that holds the engine can price the routes again and run the shadow again (wai-quantum verify --recheck), which holds digest, named and auto to what this engine gives (auto over the routes that depend on the host as the host stated them: a verifier here has neither its GPU nor its registered methods); holds the shadow's counts and class to a run here; runs a shadow the host says failed; and, on the same engine, plans the shadow again, so that which method ran, or that none could, is what this engine gives: a verifier without one takes those on the host's word. A receipt sealed by another engine whose shadow did not run is held neither way: what this engine plans is said, and the recheck does not say it holds. So is any receipt sealed by another engine version: a recheck there gives holds null unless something differs.

The free job service reaches no provider. It lists none among its routes, a contract that wants a device's class is refused there (J071), and a request that names a provider is refused there as it always is (J002).

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, held to quantum-landscape §5.4, 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 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 on the reference's own kernels (§11), unless it is told not to (--no-landscape); 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), and receipts sealed to break one rule each. 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 surface given a job already parsed into objects (a tool's request and result, a view's object) cannot see what parsing loses: a member named twice (unreadable), and a whole number past what its reader keeps (inexact_number). 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.

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.

11. Energy-landscape jobs

A device may take jobs of quantum-landscape, wai.quantum.landscape/1: 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.

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 "wai.quantum.landscape/1"
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, or an operation it does not list.

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.

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, or for an op it does not list;
  • refused for a request quantum-landscape's reader refuses, 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, 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, and holds every rule of quantum-landscape §5.4 for the request it sent. An answer that does not is refused, and nothing is sealed.

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 (wai.quantum.landscape/1), 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.