# 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 | | `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 | | `== != < <= > >=`, `&& || !` | supported | | | 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 | 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 | ## 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 | Q320 | | 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 `&&`, `||`, `!` | lowered: steps that branch forward | Q301, Q302, Q303, Q304, Q305, Q307 | | `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](pulse.md) 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.