Re-landed from PR #676 (branch wai/quantum-parse-depth, merged with mainf6e302665at 0cf954bfc) as one commit on main, under the land-it model. Six review rounds; round 6's 16 findings are fixed, each with a test that fails ond5564748e. The public, anonymous job service is hardened on every route: - The reader: QEC indices refused past 2^24 and decodes capped; operator runs, else-if ladders and switches read flat at any length; real nesting bounded (Q006: 64 levels in the text, 256 through calls); arrays at most 7 dimensions. - Every read of a client's program is counted first and runs in a child capped on memory, time and answer size, covering admission, dry runs, checks, engine calls, set receipts, MCP verdicts and /v1/verify. - A result-size bound that is a true upper bound for every job kind (sampled intents' fits, trajectories, diagnostics, RFC 8785 numbers), held by a property test over random jobs, and tight where the method knows better (the stabilizer method's outcomes, a loop's verdict). - Dry runs apply exactly the run's checks, MCP's verdict included. - Turns: reading and check shares per client, a lent turn given back when others wait, at most eight waiting, J032 past them; long polls past a client's holds answer 429/503 with Retry-After and the job's status. - JSON-RPC ids echoed as sent; header and body time limits; large bodies read on one of four large-body turns, one per client. - wasmQ's /v1/verify and MCP verify go through the same admission, turns and reading child; wasmQ's profile states this reader's nesting bound. Passed on the merged tree (Framework, linux; M3, macOS): the service suite under ulimit -Sn 256 (118/119), the wai-quantum library (1,458), the CLI, wai-wasmq (67) and its 83 vectors on three engines, the engine with and without receipts, clippy -D warnings, wai breaking against main (5 acknowledged changes), and every repository check. Co-Authored-By: Claude Opus 5.5 <[email protected]>
14 KiB
OpenQASM 3.1: what the reader does with each construct
The reader (wai_quantum::qasm) is the one grammar every surface takes: the job
service, the command line, both MCP servers, the Studio, the desktop app, the
edge component and the in-browser IDE. This page lists every construct of
OpenQASM 3.1, and the OpenQASM 2 forms in common use, with what the reader does
with it and the diagnostic code it gives (qasm-diagnostics.md has the table
of codes).
- supported: read as the language defines it.
- lowered: read and lowered exactly to the engine's gates and steps.
- carried: kept in the program as written (timing, outputs); it changes nothing a method computes, and the result says so where it matters.
- preserved: kept verbatim for export, never executed.
- refused: the program is not read; the refusal names the line, says what to write instead, and ends with its code.
The conformance corpus (wai/quantum-conformance/qasm3/) holds the
specification's example programs and a program for each construct below, each
with what the reader must do with it; CI runs it natively, under wasm32-wasip2
and in the browser build.
Program structure
| Construct | Status | Codes |
|---|---|---|
OPENQASM 3.1;, OPENQASM 3;, OPENQASM 2.0;, or none |
supported; another version is read as OpenQASM 3 | Q010 |
include "stdgates.inc", include "qelib1.inc" |
supported: the libraries are built in | Q012 |
include of any other file |
warned: its definitions are not read | Q011 |
Comments // and /* */ |
supported; an unclosed block comment is refused | Q003 |
Identifiers, Unicode letters included (θ, π) |
supported | |
Statements and blocks { }, scopes |
supported; qubits, gates, subroutines and externs are declared at the top level | Q105 |
| Nesting: statements in blocks and bodies, parentheses, operators, brackets, calls, casts and types | supported to 64 levels together in the text (a statement, each enclosing statement, and each level of its expressions' trees count one). A run of operators of one precedence (a + b - c, c[0] && c[1] && …) is one level, and an else if chain one level, at any length: both are read and run in loops, left to right. Deeper is refused, never read by a recursion that could run out of stack; an array has at most 7 dimensions |
Q006 |
| Nesting in all, through the gates and subroutines a program calls | supported to 256 levels (each statement, expression and call the reader runs counts one), and calls 32 deep; deeper is refused. Both hold: a recursive subroutine whose body nests more than about seven levels reaches 256 before 32 calls | Q006, Q209 |
| What a program holds on reading: its classical values, gates, steps, measurements and timing | supported to 64 MiB for one value (an array or a bit register, each element counted at its width) and 512 MiB in all; more is refused before it is made. Making or copying a value counts toward the reader's 20 000 000 steps at one step for every 128 bytes, and an element read from an array copies the element alone | Q130, Q312 |
pragma …, #pragma …, annotations @keyword … |
warned: kept, not applied; @wai.max_iterations N bounds the while after it |
Q013 |
Literals
| Construct | Status | Codes |
|---|---|---|
Integers: decimal, 0x, 0o, 0b, with _ |
supported | Q002 |
Floats, exponents, .5 |
supported | Q002 |
Imaginary 2.5im |
supported | |
true, false |
supported | |
Bit strings "0101" |
supported | Q110 |
Durations ns, us, µs, ms, s, dt |
supported | Q406 |
pi π, tau τ, euler ℯ |
supported; not redeclarable | Q106 |
Declarations and types
| Construct | Status | Codes |
|---|---|---|
qubit q;, qubit[n] q;, qreg q[n]; |
supported (at most 65 536 qubits) | Q101, Q105, Q111 |
Physical qubits $0, $1 |
supported, in a program that declares none | Q124 |
bit c;, bit[n] c;, creg c[n]; |
supported: written by measurement; a value known on reading (bit[2] f = "11";) is held until a measurement writes the bit |
Q319 |
| A bit declared inside a block or a subroutine | lowered: a bit of its own, which the steps read and the results do not show | Q322 |
int, int[n], uint, uint[n] (n ≤ 64) |
supported; range-checked | Q112, Q122 |
float, float[16], float[32], float[64] |
supported, rounded to the width as defined; float[128] refused |
Q121, Q122 |
angle, angle[n] (n ≤ 64) |
supported: angle[n] holds n bits of a turn, so it is dyadic and the bit-exact method takes it |
Q121 |
complex, complex[float[n]] |
supported; a complex gate parameter is refused | Q110 |
bool |
supported | |
duration |
supported: SI units and dt, not mixed |
Q406 |
stretch |
carried by name, or as the expression that holds it, for a scheduler | Q403 |
array[T, d1, d2, …], nested values, sizeof |
supported | Q107, Q110 |
const |
supported: known on reading; not assignable | Q127 |
input int n;, input uint n; |
supported: given when the program is read (a generator) | Q114 |
input float theta;, input angle phi; |
a parameter: a job's params binds it as a const at exactly the double given before the program is read (left without a value, the job is refused with Q114 and J050); the reader alone gives it no value |
Q115 |
| a parameter followed into a gradient | supported through arithmetic, the built-in functions, loops and gate definitions into gates' arguments; refused under a modifier, in a classical variable, a condition, an index, a size, a loop's range, a subroutine, or a type that rounds it | Q129 |
output |
carried into the result: a bit register's places in each outcome, or a value | Q600, Q601 |
let a = q[0:2] ++ r; |
supported; a let that reuses an enclosing scope's name is refused |
Q101 |
extern f(…) -> T; |
supported as a declaration; a call is refused | Q125 |
Indexing
| Construct | Status | Codes |
|---|---|---|
q[i], negative q[-1] |
supported | Q107 |
Slices q[a:b], q[a:s:b], q[a:], q[:] |
supported | Q107, Q313 |
Index sets q[{0, 2, 5}] |
supported | Q107 |
Multi-dimensional m[i, j], m[i][j], array slices and slice assignment |
supported | Q107, Q110 |
An integer's bits k[i] |
supported |
Expressions
| Construct | Status | Codes |
|---|---|---|
+ - * / % **, unary - |
supported; integer overflow refused | Q112, Q113 |
Integer division with a remainder used as a real (1/2 in a gate) |
refused: OpenQASM 3 truncates it, OpenQASM 2 does not | Q118 |
| `& | ~ << >>` | supported on integers and bits |
^ |
exclusive or in an OpenQASM 3 program; a power in OpenQASM 2; between integers with no version stated it is refused, between reals read as a power with a warning | Q119, Q120 |
== != < <= > >=, `&& |
!` | |
Casts int[8](x), float(x), angle[n](b), bit[n](i), bool(x) |
supported (a cast to int truncates; bits to a signed integer of their width read as two's complement) |
Q123 |
sin cos tan arcsin arccos arctan exp log ln sqrt ceiling floor mod pow popcount rotl rotr real imag sizeof |
supported, computed with the crate's own functions, the same bits on every platform | Q104 |
durationof({ … }) |
refused: it needs gate durations the program does not give | Q405 |
Arithmetic on measured bits at run time (int k = c;) |
refused: the steps branch on bits and hold no other run-time value | Q306 |
| A value that depends on a measurement where one is needed on reading | refused | Q116 |
Gates
| Construct | Status | Codes |
|---|---|---|
U(θ, φ, λ), gphase(θ) |
supported; a global phase is dropped, and under controls is a phase on the controls | Q206 |
The standard library: p x y z h s sdg t tdg sx rx ry rz cx cy cz cp crx cry crz ch swap ccx cswap cu CX phase cphase id u1 u2 u3, and sxdg iswap rxx ryy rzz ccz cnot toffoli fredkin u cu1 cu3 |
supported, each checked against its definition in U and CX (cu(θ, φ, λ, γ) is controlled e^{iγ}U(θ, φ, λ)) |
Q012 |
gate name(params) qubits { … } |
lowered: inlined at each call; a redefinition of a standard gate keeps the standard one | Q200, Q212 |
opaque gates |
refused at a call: no matrix | Q126 |
| A call on whole registers (broadcast) | supported | Q108 |
A gate's duration x[20ns] q; |
carried in the timing | Q407 |
| Wrong parameters or qubits, a qubit named twice, an unknown gate | refused | Q103, Q104, Q109 |
Modifiers
| Construct | Status | Codes |
|---|---|---|
ctrl @, ctrl(n) @, negctrl @, negctrl(n) @ on any gate, user gates included |
lowered exactly (a negative control is a control between two x); at most 16 added controls in standard gates. A job on statevector holds a gate whose standard gates would double as one operation, under as many as 255, and lowers it only where a method needs standard gates (qasm::read_whole); exact does so only when asked ("options": {"controls": "whole"}), since its bits are those of the gates it applies. Those gates are x, y, z and h under three or more controls and s, sdg, t, tdg, p, rx, ry, rz, u, sx and sxdg under two or more, each lowered to the method's gates under its controls (sx as h s h, ry as sdg h rz h s), and not p(pi), which is z. A job with an intent is held to 16: an intent is checked in standard gates, while a verdict with none reads the program as it ran |
Q201, Q207, Q208 |
inv @ on any gate |
lowered exactly: each gate's inverse, in reverse order | Q202 |
pow(k) @, k a whole number |
lowered exactly: angles scaled or the gate repeated; negative k repeats the inverse |
Q203 |
pow(r) @, r fractional |
lowered where an exact principal root exists (pow(1/2) @ x is sx; rotations by less than 2π; phases; controlled forms); refused otherwise (u, cu, user gates) |
Q204, Q205 |
| A modifier on a subroutine, or nested as deep as 32 | refused | Q211, Q209 |
A chain of modifiers (inv @ inv @ …) |
read at any length: a list, not a nesting |
Measurement and reset
| Construct | Status | Codes |
|---|---|---|
c = measure q;, c[0] = measure q[0];, bit c = measure q; |
supported | Q321 |
measure q -> c; |
supported | Q321 |
measure q; |
supported where no bit register is declared, and refused in a program that calls externs | Q320, Q324 |
| Mid-circuit measurement, measurement again, a gate after it | lowered: the program runs as steps (counts by shot splitting, exact expectation values) | |
reset q; |
supported: before a qubit's first gate it changes nothing; after it, the program runs as steps | |
barrier, barrier q; |
carried in the timing | Q404 |
Classical control
| Construct | Status | Codes |
|---|---|---|
if/else known on reading |
decided on reading | Q300 |
if/else on measured bits: c[0], c == 3, c >= 2, int[2](c) != 0, with &&, ` |
, !` |
|
for over a range, a set or an array |
lowered: unrolled | Q308, Q313, Q312 |
while known on reading |
lowered: unrolled until it fails (at most 1 000 000 times) | Q309, Q312 |
while on measured bits |
lowered under @wai.max_iterations N, with a warning that runs still meeting the condition stop there; refused with no bound |
Q310, Q311 |
switch / case / default |
decided on reading, or a chain of conditions on measured bits | Q317 |
break, continue |
supported; under a condition on measured bits, jumps forward | Q316, Q314 |
end |
supported | Q316 |
Subroutines
| Construct | Status | Codes |
|---|---|---|
def f(T x, qubit q, qubit[n] r) -> T { … } |
lowered: inlined, classical arguments by value, qubits by reference | Q318, Q104 |
return v;, return measure q; |
supported; a returned measurement writes the caller's bit | Q315 |
readonly / mutable array arguments, #dim |
supported | |
| Bits declared in a subroutine | lowered: bits of its own, fed forward | Q322 |
A subroutine called as a gate (f(1) q, r;) |
supported | |
| Recursion | refused past 32 calls deep | Q209 |
Timing
| Construct | Status | Codes |
|---|---|---|
delay[d] q; |
carried | Q401 |
box, box [d] |
carried, around the gates it holds | Q402 |
stretch |
carried | Q403 |
| Durations in a result | no method applies them; a result of a program that states durations says so (timing.applied: false) |
Q400 |
Calibration (OpenPulse)
| Construct | Status | Codes |
|---|---|---|
defcalgrammar "openpulse"; |
preserved; in a pulse run, read (another grammar is refused) | Q452, Q454 |
cal { … } |
preserved, never run by the circuit methods; in a pulse run, read as OpenPulse and run in order | Q451, Q453 |
defcal … |
preserved, never run by the circuit methods; the gate's matrix comes from its definition or the library. In a pulse run, read as OpenPulse and run at each call on its physical qubits | Q450, Q126, Q453, Q459 |
Ports, frames, waveforms (constant, gaussian, sech, gaussian_square, drag, sine, explicit samples) |
in a pulse run, lowered to the schedule | Q453, Q455 to Q458 |
play, delay, shift_phase, set_phase, shift_frequency, set_frequency, capture_v0 to capture_v4, barrier |
in a pulse run, lowered to the schedule on frames | Q453, Q457, Q458, Q461 |
| Plays on a measurement port | in a pulse run, scheduled and timed; the readout is not simulated | Q462 |
A pulse run is the job method pulse; the pulse page gives the
model, the bound and the options.
The bit-exact method
The bit-exact state vector takes the reader's program under its own restriction, refusing with a code: dyadic angles only (Q500), at most 255 qubits (Q501), and measurements all at the end (Q502). A controlled gate the modifiers write is held whole, under all its controls. The methods in double precision take any angle.