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]>
106 KiB
WAI Extension: Quantum Providers (wai.quantum.provider/1)
Status: Draft. Client, reference provider and conformance checker:
wai-quantum-qpu (src/open_provider.rs, src/reference.rs,
src/conformance.rs); vectors: wai-quantum-qpu/conformance/provider-v1.json.
The engine's side of a provider job: wai-quantum/src/engine/provider.rs.
Keywords MUST, MUST NOT, SHOULD and MAY are RFC 2119/8174.
0. What this extension adds
Bring your own quantum. WAI provides the engine, the free job service and the verifier at no cost, and never pays for or resells time on anyone's hardware. A person connects their own quantum hardware, provider accounts, on-premises devices or simulators, and every surface treats a provider's result as it treats a WAI result: planned, its cost shown before it is submitted, checked, and sealed in a receipt.
This text is the protocol a device speaks so that it can be brought without a client written for it: JSON messages, one request and one answer at a time, over HTTP or to a local process (§2). Six operations (§5) describe the device, estimate a job, submit it, follow it, read its counts and cancel it. A program travels in the device's own native gates, with every angle an exact fraction of a turn (§4), so the program the device runs is the one the client compiled and checked. A 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
- The credential stays with the person. A host keeps a provider's credential in the operating system's keychain (or, where there is none, a file only its owner can read, labelled as such) and sends it only to that provider (§2). A credential MUST NOT appear in a browser page, a log, a receipt, a command line, an answer the host prints, or the hosted job service. The job service never routes a job to a provider and never takes a credential: a request that names a provider is refused there.
- Cost before submit. A client MUST show a job's estimate (§5.2) before it submits the job, and MUST NOT submit a job that is not free without the person's explicit confirmation (§7). An unknown cost is not free.
- Exact programs. A client compiles a circuit exactly to the device's native gates and connectivity and checks the compiled program against the circuit before sending it. Angles cross the wire exactly (§4).
- Returned, not recomputed. A provider's counts are sealed as the device
it names returned them (§8):
measuredfor a processor,simulatedfor 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; …"
}
-
basisis one of four gate sets, and every gate is of it:basis gates rzsxxczrz(t),sx,x,czprxczprx(t, p),czgpizzgpi(p),gpi2(p),zz(t)prxrzxxprx(t, p),rz(t),xx(t)A program in
rzsxxczorprxczmay also holdmoveon a device with computational resonators (below). -
An angle is a fraction of a full turn,
[m, k]meaningm/2^kof 2π, with0 ≤ m < 2^kandk ≤ 32, so every angle crosses the wire exactly. The gates, as operators on the named qubits, up to a global phase:rz(t)= exp(−iπt·Z);sx= √X;x= X;cz= diag(1, 1, 1, −1);prx(t, p)= Rz(2πp)·Rx(2πt)·Rz(−2πp);gpi(p)= Rz(2πp)·X·Rz(−2πp);gpi2(p)= Rz(2πp)·Rx(π/2)·Rz(−2πp);zz(t)= exp(−iπt·Z⊗Z);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'srxx(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. Inprxrzxx,xx(t)takes 0 ≤ t ≤ ¼ andprx(t, p)takes 0 ≤ t ≤ ½, the ranges devices of that gate set take; a client writesczasxx(¼)betweenprx(¼, ¼)(Ry(π/2)) on each qubit before andprx(¼, ¾)(Ry(−π/2)) after, theZrotations it leaves carried in the phases, so no program it compiles needs more;moveon[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 themoveback cancels.
-
A program uses a resonator in turn: a
moveinto a resonator only when it is empty, by a qubit whose state is in no resonator; themoveout by the qubit that moved in; no gate on that qubit between the two; onlymoveandczon a resonator, which is never measured; and every resonator empty at the end. Aczbetween a qubit that moves into a resonator and one coupled to it is thenmove · cz(partner, resonator) · move, whateverais. -
measuregives, for each classical bit0 … bits − 1, the physical qubit it reads at the end of the program. Every bit reads exactly one qubit, and no qubit is read twice. -
qasm3is the same program as OpenQASM 3 on physical qubits ($p), theprxcz,gpizzandprxrzxxgates defined in it from the standard gates (xxin radians, asrxx), andmoveas 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, thegatesare 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
submitdid 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_providerwithconfirm_cost: true), or a dialog (the desktop app); - MUST NOT hold a standing confirmation across runs.
A tool that can submit a job that is not free MUST say in its description that it can spend the person's money.
8. The receipt
The host seals each provider job as the job service seals its own, 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
\uescape 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,falseandnullas 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,\fand\r; every other character below U+0020 as\u00and 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, withsthe fewest decimal digitsd1 … dkthat read back as the double (of those, the nearest to it, a tie going to the even last digit) andnsuch 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; otherwised1, then.andd2 … dkwhen k > 1, thene, 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-0is a double:-0.0); - every other number is written from its double:
-0.0for negative zero, and0.0for zero; otherwise, withs=d1 … dkandnas 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; otherwised1, then.andd2 … dkwhen k > 1, thene, 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
AandRas 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
Aof small order (a point P with [8]P the identity) andAnot in the prime-order subgroup ([ℓ]A not the identity, ℓ = 2^252 + 27742317777372353535851937790883648493); - refuses
Rof small order, andS(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.
not_named:namedis notnull, and this is not it.fabric:namedisnull, and the request'sfabricis neither absent,nullnorauto, and it iscpuand this ismethod:statevector_singleormethod:cutrank_single, or it is anything else and this is notmethod:statevector.cannot:classisunknownandpriceisnull.class:wantedis notnullandclassdoes not meet it; orwantedissimulated, the contract names it (wanted_fromcontract), andapproximateistrue; orwantedisnulland 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.cannot:priceisnull.memory:memory_bytesis abovehost.job_bytes.providers: this is a provider's route, andcontract.providersis notallowed.deadline_unknown,deadline: the contract givesdeadline_seconds, andtime.secondsisnull, or above it.- For a provider's route whose
money.freeis nottrue:cost_no_ceiling, the contract gives nomax_costand this is not thenamedroute;cost_unknown, it gives one andmoneyhas noamountthat is an amount in decimals, or nocurrency;cost_currency, the two currencies differ;cost, the amount is abovemax_cost.amount;cost_at_least,money.at_leastistrue, 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 nomax_cost, meets this rule: its cost is the person's to confirm (§7), as it is with no contract. energy_unwatched,energy: the contract givesmax_joules, andenergyhas nowatch, so nothing can stop the run at it; orenergy.joulesis 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:
- when the contract gives no
exactnessandauto's route is let through, it stands: a contract that names no class changes nothing a job already meets; - otherwise the least of them in this order:
money.freetruefirst, then anamountin decimals (at most 64 characters), the lesser first, then any other; atime.secondsbefore anull, and then the lesser of eachseconds× 1000 rounded to the nearest whole number, halves up (the floor ofseconds× 1000 + 0.5);localbeforeprovider; the lessermemory_bytes, anullread 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.
digestis the BLAKE3, as 64 lower-case hex digits, of the RFC 8785 text (§8.2) of the object withjob_bytes,host.job_bytes, androutes, an array with one object for each of those routes in that order:route,class, andseconds,memory_bytesandworkfrom its price, eachnullwhen it has none. It is the same for this request andjob_byteson 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.choiceistrueexactly whenchoicenames one of those routes, every route withhost_dependenttrueis rejected withnot_named,fabricorclass,contract.providersis notallowed, and the contract gives nomax_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:
ranfalse, withcode, a string, andwhy, a sentence: whenplanner.shadowhas noroute,codeis itscode; otherwisefailed, the method's run did not complete. The provider's answer stands, alone, and says so;--recheckruns the planned shadow, and a host whose failed shadow runs there is found.rantrue, whereplanner.shadowhas a route: withrouteandrequest, equal to those ofplanner.shadow(numbers by value);shots, the result's;accuracy, an object whoseclassis a string that meetsplanner.shadow.classand is one of its method's;counts, keyed by outcomes all as long as the provider's counts' (where they list one) and each of0and1only, each a whole number written as one from 1 to 2^53 − 1, andomitted_shots, a whole number of at least 0 by value, which sum exactly to the result'sshots(a whole number by value);distinct_outcomes, a whole number of at least 0, and, with no shot omitted, the number of outcomes listed; optionallyelapsed_ms; anddistance.
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|, anddenominator, 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:
describeanswers'billing, each with how a client reads it:free,priced,unpriced(not free, and no price set), orrefused, of none of the three forms of §5.1, and so a description that is not complete; - estimates:
estimateanswers, 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), orrefused, of none of the three forms; - results:
resultanswers 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 (itsaccepted_shotswhen it gives one, else every shot asked), orrefused: counts that sum to neither, or anaccepted_shotsthat is not a whole number of at least 1. Nothing of a refused answer is sealed; - landscape, for a device whose
describehaslandscape(§11): its description's form; messages that each break one rule of §11.2, each refused with its code; and jobs, each estimated (itsoperationsandbytesas the vector gives them), run when every estimate says free or the person allows the spend, followed todone, its answer read twice and found the same, held to quantum-landscape §5.4, and, from a member whose parity isreferenceorbit-exact, its replay digest the vector's; and each job'scancelanswered 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,compareorreplay(examplesruns 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:
invalidfor a message with bothlandscapeandprogram, alandscapenot of that form, or anopnot one of those four;unsupportedfor a landscape job on a device whosedescribehas nolandscape, or for anopit does not list;refusedfor a request quantum-landscape's reader refuses, or one past the device's own limit (max_bytes):messageis that refusal's plain sentence, as quantum-landscape §2 writes it, and the error object addsline, 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.