A processor that takes rx(θ) and rzz(θ) runs an angle between two qubits in one two-qubit gate where rz sx x cz takes two cz. - quantum_toolchain::native::Basis::RzSxXCzRxRzz (label rzsxxczrxrzz): a controlled phase, a run of them on one pair, and cx·p(θ)·cx with only Z rotations between are each one rzz; h·p(θ)·h is one rx, ry one rx between frames; rx(π/2) and rx(π) are sx and x. rzz is held to 0 < θ ≤ π/2, the range the service publishes, an angle outside folded in by Z⊗Z and an X on either side; the check before sending refuses an rzz of no turn or past a quarter turn and an rx with a phase. A three-qubit Fourier transform takes three rzz where rz sx x cz takes six cz. - Proved by the engine's equivalence check (controlled phases each side of zero and past a quarter turn, the folds, rotations between qubits, X and Y rotations, the transform, random programs, none with more two-qubit gates than in rz sx x cz); six native vectors pinned and proved, four refusals. - layout::native_in and place_in place a program in the basis its device's client compiles to; on a target of rz sx x cz rx rzz, rx and rzz are costed as themselves. native_of still reads such a target as rz sx x cz. - sampler-v2 compiles to it with the setting basis=rzsxxczrxrzz, only where basis_gates list rx and rzz, on pairs both maps list; tested against the fake built from the service's own answers, which holds every rzz to the published range. The protocol (§4, §8.4) carries the set as a built-in client's: no description is compiled to it, so no provider is sent one; both verifiers read it in compiled.basis. - The pin, by values: the backend's snapshot was dated anew three times in three and a half hours on 2026-11-12 with no value a placement reads changed (only thresholds and initialisation errors), so a date comparison would refuse nearly every job that waits. The placement now names the hash of the values it read (Target::values_hash), the client states the values in force when the job began running (Stated::ran_under), and a job whose values differ is refused, in the sending process and in a collecting one. Workflow pins: the two engine tests in the registry workflow, the sampler-v2 binary's count (10) and its new test in the qpu workflow. Co-Authored-By: Claude Opus 5.5 <[email protected]>
20 KiB
Bring your own quantum
WAI gives you the engine, the free job service and the verifier at no cost. It does not sell time on anyone's hardware, and it never pays for yours. You bring your own quantum hardware: a device on your bench, an on-premises system, an account with a provider, or a simulator you trust. Every surface then treats a run on it as it treats a WAI run:
- it is planned: the circuit is compiled exactly to the device's own gates and connectivity, and the compiled program is checked against the circuit by simulation before anything is sent;
- its cost is shown before it is submitted, and a run that is not free (or whose cost is unknown) runs only when you confirm it;
- its counts are sealed in a receipt with your key, which the existing verifier checks.
Your credentials stay with you, in the operating system's keychain. They never go to a browser page, a log, a receipt, or the hosted service.
What is free
wai-quantum providers free
lists what anyone can use at no cost:
- the engine's own methods on your machine, each with the most qubits it holds in a job's memory;
- the free job service,
https://wai.transaction.science/quantum-jobs, with no account (its limits are per client;GET …/v1states them); - each of your providers whose own description states free use, with the limits it states. No provider is named in WAI's code: a provider says what it offers, about itself.
Adding a provider
A provider is one of six kinds:
| kind | what it is | the credential |
|---|---|---|
http |
any provider of the open provider protocol at an HTTPS URL | Authorization: Bearer |
process |
any program on your machine that speaks the protocol on its standard input and output | its environment (WAI_PROVIDER_CREDENTIAL) |
sampler-v2 |
a Sampler V2 job service (OpenQASM 3 on physical qubits, rz sx x cz) | an API key, exchanged for a bearer token |
prx-circuits |
a circuit service of phased-X and CZ instructions, star devices (qubits round a computational resonator, reached by move) among them |
the token you generate in the service's dashboard |
native-turns |
a native-gate service with angles in turns (gpi gpi2 zz, or gpi gpi2 ms where the backend, or its noise model's device, runs only ms) | an API key |
cloud-tasks |
a cloud service of quantum tasks, on your own account, its results in your own storage bucket | your account's keys, id:secret or id:secret:token, or its usual environment variables |
# A program of your own, or the reference provider, on this machine:
wai-quantum providers add lab --kind process -- wai-quantum providers reference --stdio
# A provider at a URL, its credential read from standard input into the keychain:
wai-quantum providers add cloud --kind http --endpoint https://quantum.example/provider --credential-stdin < token.txt
# A built-in protocol, its settings beside it, its credential in the keychain:
wai-quantum providers add mine --kind sampler-v2 --set crn=… --set backend=… --credential-stdin < key.txt
# A cloud service of tasks: a device by its name there, your own results bucket and your own keys:
wai-quantum providers add tasks --kind cloud-tasks --set region=us-east-1 --set device=… --set bucket=your-results-bucket --credential-stdin < keys.txt
# Or a credential read from an environment variable whenever it is used, kept nowhere:
wai-quantum providers add turns --kind native-turns --set backend=simulator --credential-env MY_TURNS_KEY
A credential is never given on a command line (where other users of the
machine could read it). --credential-stdin keeps it:
- on macOS, in the keychain, through the system
securitytool; - on Linux, in the desktop secret service, through
secret-tool, when it is installed and answers; - elsewhere (Linux with no secret service, Windows), in a file only you can
read (
~/.config/wai/quantum-provider-credentials/NAME, mode 0600), and every listing says so.WAI_QUANTUM_CREDENTIALS=filechooses the file;WAI_QUANTUM_CREDENTIALS=keychainaccepts nothing but the keychain.
The list itself (~/.config/wai/quantum-providers.json, or the file
$WAI_QUANTUM_PROVIDERS names) holds no credential: only each provider's
kind, endpoint or command, non-secret settings, and where its credential is.
A setting whose name looks like a credential (token, key, secret, …) is
refused. The built-in kinds still read their environment variables
(WAI_QPU_SAMPLER_API_KEY and the rest, in the README) when no
credential is kept for them.
wai-quantum providers list # each provider, and where its credential is
wai-quantum providers describe lab # its qubits, gates, connectivity, calibration and prices
wai-quantum providers test lab # the protocol's conformance vectors; jobs only when free, or with --yes
wai-quantum providers remove turns # and the credential kept for it
Running on it
wai-quantum run bell.qasm --provider lab --dry-run # the plan, the check and the estimate; nothing sent
wai-quantum run bell.qasm --provider lab # free: it runs
On a provider that charges (here cloud, reached with your own account):
wai-quantum run bell.qasm --provider cloud # not free: refused, with the estimate (exit 3)
wai-quantum run bell.qasm --provider cloud --yes # you confirmed it: it runs
wai-quantum run bell.qasm --provider cloud --json > job.json && wai-quantum verify job.json
A run on a provider asks for counts (--shots, 1024 by default) of a program
whose measurements all come at the end and whose every angle is a dyadic
multiple of 2π (pi/4, 3*pi/8, …), so it compiles exactly. A job that asks
for expectation values, amplitudes, a noise model or one of the engine's
methods is refused; those are the engine's.
An agent has the same through wai-quantum mcp:
providers_listlists your providers (and, withfree: true, what is free);provider_estimateis the dry run;run_on_providerruns, and says in its description that it can spend your money. It refuses a run that is not free unlessconfirm_cost: true, which an agent sets only after you have seen the estimate and agreed.
The desktop app lists your providers under Your hardware, asks for the estimate first, and shows a dialog before a run that is not free. The browser Studio cannot: a browser tab must not hold a credential, so there the switch says that such runs go through the desktop app or the command line. The hosted job service never routes a job to a provider and never takes a credential; a request that names a provider is refused there.
When the queue is long
A device's queue can take days, and nothing has to stay running for it. A job
is written down before it is sent, in provider-jobs beside your registry
(readable only by you, with no credential in it), and the provider's id beside
that as soon as the provider gives one.
wai-quantum run bell.qasm --provider lab --no-wait # sends it, and answers with its handle
wai-quantum providers jobs # every job recorded here, and where it last stood
wai-quantum providers collect pj-0123456789abcdef # asks, and seals its result when it has one
wai-quantum providers cancel pj-0123456789abcdef
A plain run waits two hours and then stops waiting, with the job left at the
provider and its handle on the screen. Only your asking cancels a job. If the
command, the terminal or the machine stops, the job goes on, and collect
picks it up: exit 0 done, 1 ended otherwise, 2 still at the provider, 75 could
not ask, which says nothing of the job. An agent has the same in
get_provider_job, cancel_provider_job and list_provider_jobs, and the job
from run_on_provider is the same job.
The counts are read against the program that was sent, and the receipt states that program and what the provider said when it was sent, whatever its calibration has become since. A request that did not complete may have reached the provider, so it is never sent again, since that could be a second bill: the record says so, and you look in the provider's own list. A provider you run as a local process has to keep its jobs itself, or they end with it.
The protocol, and the reference as your template
The open provider protocol is six
operations as JSON messages, over HTTP or a local process: describe
(native gates, connectivity, qubits, durations, a calibration snapshot id with
optional per-qubit and per-pair errors, and the billing: free, a unit and
its price, or not free with no price set, so that a job's cost is unknown and
it runs only when you confirm it), estimate, submit, status, result
(counts keyed by classical bits) and cancel. A program travels in your
device's own gates, every angle an exact fraction of a turn, with the same
program as OpenQASM 3 beside it.
The reference provider is the protocol around WAI's exact simulator:
wai-quantum providers reference --stdio # on standard input and output
wai-quantum providers reference --help-args # its device: qubits, basis, connectivity, a price, a readout error
wai-quantum providers reference --http 127.0.0.1:8090 # over HTTP, until stopped
To bring a device, copy src/reference.rs: keep the
message handling, the checks of a program against the device, and the two
servers, and replace Reference::simulate with your device's run (and
describe with your device's description). Then hold it to the vectors:
wai-quantum providers add mine --kind process -- ./my-provider --stdio # your own provider's program
wai-quantum providers test mine
A device whose gates are a phased X rotation, a Z rotation and XX(θ),
reached through a service with an interface of its own (its angles in units of
π, say), is brought the same way. Its provider describes "native_gates": ["prx", "rz", "xx"] and is sent programs in prxrzxx, which it writes in the
service's terms: prx(t, p) as the rotation by 2t and the phase 2p in units
of π (0 to 1, and 0 to 2), rz(t) as 2t (less 2 past 1, where the service
takes −1 to 1), and xx(t), which is exp(−iπt·X⊗X), as 2t (0 to ½: every
cz arrives as xx(¼), so ½). Each angle is a dyadic fraction and stays
exact as a number of that form. The provider turns off whatever the service
would compile, remap or mitigate, and says what it sent in the answer's
execution; and where the service deletes a result a day after it is read, it
keeps each result as it reads it. A sign or a unit wrong in its xx leaves the
probe's counts as they are: run a circuit whose outcome turns on the entangling
gate's phase, as the certification's do, before trusting other results. A star
device describes its resonators and the moves into them, and is sent
move in prxcz or rzsxxcz.
The first job on a new provider is the probe, an X on qubit 0 whose counts
must all land on …01. It settles the bit order before any other result is
trusted.
Programs that call WebAssembly between gates
A wasmQ envelope (quantum-wasmq) calls its WebAssembly
modules between gates: a decoder between a syndrome and its correction, a planner between
loading atoms and moving them. A provider's program is a fixed sequence of native gates
measured at the end, so it cannot carry that, and run ENVELOPE.wai --provider NAME is
refused; the envelope runs on this machine, its quantum part simulated. Nothing about
bringing your own quantum changes for it. What a hardware run of such a program would
need, and what replays from its record, is §7 of that extension.
What the receipt proves, and what it does not
A processor's result is labelled measured. A result from a provider's
simulator or emulator is labelled simulated, since no device measured it,
and approximate too when the provider says its simulator is. Its receipt
binds the request, the provider's name, kind and protocol, the device and its
provenance, the hash of the provider's own description, the calibration
snapshot, the compiled program's text and its hash, the counts, the cost (the
estimate, the charge, and whether you confirmed it), and the counts set beside
the circuit's exact distribution with the distance between them.
It also says two things a later receipt could not add:
- Where the counts come from (
counts_source): tallied from the device's records, one a shot (shots); the provider's own counts (vendor_counts); or rebuilt from probabilities (probabilities). Rebuilt counts are derived, not shot records.native-turnsreads its job's shot records where the service lists them (as a noisy simulation does), else the service's own counts, and rebuilds them only from probabilities, as on an ideal simulator; it holds every form the job lists to the others, and the shots the service flags as leaked are counted as read, and their number sealed beside them (leakage). A result the service narrows to the qubits up to the last its gates touch is read with the qubits above it 0. A job the service finished whose result does not read (keys of no form it writes, forms that disagree, or a width it does not write) endsfailed, with the reason, and is not asked again.cloud-tasksreads a task's rows and rebuilds only when its result has none. - What was set at the provider (
execution), built from the very request the client sends: each of mitigation, twirling, dynamical decoupling, remapping and optimisation asoff,on, ordefaultwhen the request sets nothing for it, and whether the gates were to run as written.sampler-v2turns decoupling and twirling off;native-turnsturns debiasing off, and sends its simulator, under a device's noise model, the job's seed, so a noisy simulation is the same at each run of that seed, and its note ends with what the service says it compiled (precision, gate basis, version) and whether the circuit it lists as compiled is the one sent;cloud-tasksputs a processor's gates in the service's verbatim box;prx-circuitspins the job to the calibration set it was compiled against and sends dynamical decoupling disabled, with the service's other modes as it wants them. A provider of the open protocol states its own (§5.5 of the protocol), and a wrapper around another service must.
wai-quantum verify checks the seal and every rule a receipt keeps, and names
each one it breaks (the protocol's §8.4); --json gives the verdict as data.
It proves that your key signed this result for this request: what it says your host received from the provider and device it names, sent and checked is your host's word, and the counts are the provider's. It does not prove that the device ran that program, or ran anything. The counts are not reproducible by recomputation; the same job run again gives other counts. The distance from the exact distribution tells you how far they are from what the circuit gives: a distance well above what the shots alone leave is the device's or the simulator's error, or a program not run as sent.
A receipt seals the provider's id for the job. For cloud-tasks that is the
task's identifier, which names your account's number: sharing the receipt
shares it.
A first run on a phased-X circuit service's mock quantum computer
A prx-circuits service may list, beside each quantum computer, a mock of it:
the same name with :mock after it, a server with no quantum hardware that
answers random bits. It checks the whole wire (your token, the device's
description, the job, the cancel, the counts) before any processor runs
anything. Its results are sealed as simulated, never as measured.
-
In the service's dashboard, generate your token. Generating it again makes the old one stop working.
-
Keep it where
wai-quantumkeeps every credential, read from standard input (paste it, then press Ctrl-D), never from the command line:wai-quantum providers add mock --kind prx-circuits --set qc=NAME:mock --credential-stdinOn macOS that is the keychain: an item whose service is
wai-quantum-providerand whose account is the provider's name, heremock. On Linux it is the desktop secret service, and elsewhere a file only you can read;wai-quantum providers listsays which. A token you keep already, under another item of the keychain, goes over without being shown:security find-generic-password -s YOUR-ITEM -w | wai-quantum providers add mock --kind prx-circuits --set qc=NAME:mock --credential-stdinWAI_QPU_PRX_TOKENis read instead when a provider names no credential. -
Read the device, its calibration and its errors, with nothing sent:
wai-quantum providers describe mock # qubits, gates, pairs, and the calibration set it is under wai-quantum providers test mock # the device and its calibration's errors; the programs only with --yes wai-quantum targets import --provider mock --name mock # the calibration's quality metrics, kept as a target wai-quantum run probe.qasm --provider mock --dry-run # the compile, the placement, the calibration it is pinned to -
Run the probe, then the conformance programs. The service sets no price through its interface, so each is confirmed with
--yes; look at your account's usage before and after to see what the mock cost:wai-quantum run probe.qasm --provider mock --yes --json > probe.json && wai-quantum verify probe.json wai-quantum providers test mock --yes
Each job is sent pinned to the calibration set the device was read under, so
the receipt's calibration is the set the service says it compiled the job
against, and a run is refused rather than sealed when the service says it
compiled against another. If the service recalibrated and no longer runs the
pinned set, it fails the job (calibration set not found), its record stating no
billed time, and the next run reads the current one. A
service that publishes no quality metrics for its calibration leaves the
compiler's own layout, and the plan's placement says so.
A sampler-v2 backend's calibration is its published properties, read after
the device and named by their last_update_date: the job is placed by them,
and once it has run the client reads the properties in force when it began
running, so a job that ran under another calibration than the one it was
placed by is refused rather than sealed. On a free plan the estimate says how
many seconds of the plan's limit are left, and the receipt seals what the job
was charged, in qpu-seconds.
Connecting a real device: what is yours to do
WAI ships no account and no key. To run on a real device:
- Open an account with the provider, or set up your own device, and accept its terms. Any price, quota or free tier is between you and the provider.
- Mint a credential (an API key or a token) in the provider's own console.
- Add it:
wai-quantum providers add NAME --kind … --credential-stdin, with the settings its kind takes (crnandbackendforsampler-v2, andbasis=rzsxxczrxrzzto compile to itsrxandrzztoo where the backend lists them;qcforprx-circuits,backendfornative-turns,region,deviceandbucketforcloud-tasks), or--kind httpfor a provider of the open protocol. Forcloud-tasks, first turn the service on in your account. The bucket must be in the device's region, its name must beginamazon-braket-, and its policy must let the service write to it. A name with a dot works: it is read by path. A region or a bucket that is not a name is refused when the provider is added, and this kind takes no--endpoint. On one of its simulators,--set basis=gpizz(orprxcz, orprxrzxxfor a processor ofprx,xxandrz) compiles to that processor basis, so you can check the gates before paying for the processor. - Run
wai-quantum providers describe NAME, thenwai-quantum run probe.qasm --provider NAME --dry-runto see the compile and the estimate, then the probe itself, confirmed with--yesif it is not free.
Nothing in WAI's tests spends money: they run against the reference provider, whose price, when it has one, is a number in its configuration.