From 4866dd91598c80cd6f925f7044817fb7c4ed8977 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Fri, 30 Oct 2026 10:13:12 -0400 Subject: [PATCH 01/10] wai-quantum: QIR 2.x under its profiles, and jeff, read and written engine::formats reads QIR (LLVM text, 1.x and 2.x) under the profile its entry point names: the base profile's rules and the adaptive profile's module flags are enforced, and what a profile forbids is refused with its code and line. Rotations at arbitrary angles are read at their exact doubles, with a note that the double-precision methods take them; a Clifford+T module stays on the bit-exact path. A program is written as QIR 2.0: the base profile, or the adaptive profile with forward branches for a program that measures in the middle or branches. jeff (format 0.3) is read and written through a small Cap'n Proto reader and writer of the crate's own: structured control flow decided or unrolled on reading, a switch on a measured bit written as a condition, Pauli-product rotations lowered exactly, custom gates read only by a standard name. Every refusal has a code (Q700 to Q721) and its place, and convert() checks each conversion with the equivalence ladder. Co-Authored-By: Claude Opus 5.5 --- wai/wai-quantum/docs/README.md | 2 + wai/wai-quantum/docs/formats.md | 174 ++ wai/wai-quantum/docs/qasm-diagnostics.md | 21 +- wai/wai-quantum/src/engine/formats/capnp.rs | 693 ++++++++ wai/wai-quantum/src/engine/formats/emit.rs | 217 +++ wai/wai-quantum/src/engine/formats/jeff.rs | 1662 +++++++++++++++++++ wai/wai-quantum/src/engine/formats/mod.rs | 439 +++++ wai/wai-quantum/src/engine/formats/qir.rs | 1619 ++++++++++++++++++ wai/wai-quantum/src/engine/formats/tests.rs | 715 ++++++++ wai/wai-quantum/src/engine/mod.rs | 5 + wai/wai-quantum/src/qasm.rs | 2 +- wai/wai-quantum/src/qasm/diag.rs | 21 + 12 files changed, 5568 insertions(+), 2 deletions(-) create mode 100644 wai/wai-quantum/docs/formats.md create mode 100644 wai/wai-quantum/src/engine/formats/capnp.rs create mode 100644 wai/wai-quantum/src/engine/formats/emit.rs create mode 100644 wai/wai-quantum/src/engine/formats/jeff.rs create mode 100644 wai/wai-quantum/src/engine/formats/mod.rs create mode 100644 wai/wai-quantum/src/engine/formats/qir.rs create mode 100644 wai/wai-quantum/src/engine/formats/tests.rs diff --git a/wai/wai-quantum/docs/README.md b/wai/wai-quantum/docs/README.md index b4f2d5732..afe489a58 100644 --- a/wai/wai-quantum/docs/README.md +++ b/wai/wai-quantum/docs/README.md @@ -33,6 +33,8 @@ run one says why. - [The reader's diagnostics](qasm-diagnostics.md) and [what it does with each construct](qasm-feature-matrix.md). - [The job layer's refusal codes](job-refusals.md). +- [Other formats](formats.md): QIR under its profiles and jeff, read and + written, each conversion checked. - [Error correction and mitigation](qec.md): codes with their certified distances, the decoders, thresholds, and mitigation in a job. - [Pulse and dynamics](pulse.md): pulse programs read into schedules on diff --git a/wai/wai-quantum/docs/formats.md b/wai/wai-quantum/docs/formats.md new file mode 100644 index 000000000..28c8b2e65 --- /dev/null +++ b/wai/wai-quantum/docs/formats.md @@ -0,0 +1,174 @@ +# Other formats: QIR and jeff + +The engine reads programs in two formats beside OpenQASM and writes them +(`wai_quantum::engine::formats`). A reader gives the program as OpenQASM 3, +which the one reader every surface uses then reads, so a program from any +format plans, runs, checks and draws as one written in OpenQASM. A +construct a reader cannot hold exactly is refused with a code (`Q700` to +`Q721`, in [the table of codes](qasm-diagnostics.md)) and where it stands: a +line of a QIR module, or a function and an operation of a jeff module. + +| Format | Read | Written | +|---|---|---| +| QIR, LLVM text | 1.x (typed pointers) and 2.x (opaque pointers), under the base and adaptive profiles | 2.0, opaque pointers: the base profile, or the adaptive profile for a program that measures in the middle, resets or branches | +| jeff, its Cap'n Proto message | 0.2 and 0.3 | 0.3.1 | + +## Converting + +```text +wai-quantum convert prog.qasm --to qir > prog.ll # QIR 2.0, base profile +wai-quantum convert prog.qasm --to qir --profile adaptive +wai-quantum convert prog.ll --to jeff -o prog.jeff # the format read is found from the bytes +wai-quantum convert prog.jeff --to qasm # as OpenQASM 3 +wai-quantum convert prog.jeff --to qasm --json # with the notes, what was read and the check +wai-quantum run prog.ll --shots 1000 # every verb that reads a program reads these +wai-quantum run prog.bin --from jeff # a name the extension does not give +``` + +Every conversion is checked. What is written is read back and held to the +program read by the equivalence ladder (a tableau, every basis state at up +to 8 qubits, rewrites, random product states, or unchecked with the +reason). A program read from QIR or jeff is written back to its format and +read again, and the two readings are held to each other the same way. A +program that measures in the middle or branches is held step for step, and +where its steps differ (a condition on several bits written as one branch +per bit, say) by the probability of every outcome, branch by branch with +its weight, which is evidence and says so. + +The same conversion is the MCP tool `convert_program` on both servers, the +job service's `POST /v1/convert`, and `engine_convert` in the browser +engine. A job may carry its program in another format: +`{"source": {"format": "qir", "program": "…"}}` in place of `"qasm"` +(`"program_base64"` for jeff); it is read before anything else reads the +job, and the request keeps its source. + +## QIR + +**Profiles.** The entry point's `qir_profiles` names the profile, and the +module is held to it: + +- **base**: gates, then measurements, then output records. A branch, a + classical instruction, reading a result, or a gate or reset on a qubit + after its measurement is what the profile forbids (`Q704`). +- **adaptive**: measurements anywhere; results read as booleans + (`__quantum__rt__read_result`, or QIR 1's `__quantum__rt__result_equal` + with `result_get_one`) and branched on, forward; `i1` logic (`and`, `or`, + `xor`, `icmp eq` and `ne`, `select`). An optional capability its module + flags do not declare is forbidden (`Q704`). Integer and float computation + the flags declare is computed on reading where its operands are known + (an angle computed from constants); computation on a measurement anywhere + but a condition, loops, `switch`, `phi`, IR-defined functions, dynamic + qubits or results and arrays are not taken here, though declared + (`Q705`). + +A module that names no profile is read under the profile its instructions +need, and one that leaves out the version flags or the qubit and result +counts is read from what it holds; each says so (`Q702`). A profile other +than these two is refused (`Q703`), as is a major version other than 1 or 2 +(`Q701`). + +**The program.** Qubit `i` is `q[i]` and result `r` is `c[r]`; the control +flow, a tree of forward branches, is written as nested `if` and `else`, +each branch running to the block where its paths meet again. A result read, +measured again and only then branched on is refused (`Q708`): the text would +read the later measurement. Output records are listed in what was read +(`recorded`); classical values recorded as output are not carried (`Q721`). + +**Intrinsics read.** `__quantum__qis____body`, and `__adj` for the +inverse: + +| Intrinsic | OpenQASM | Intrinsic | OpenQASM | +|---|---|---|---| +| `x` `y` `z` `h` | `x` `y` `z` `h` | `cnot`, `cx` | `cx` | +| `s`, `s__adj` | `s`, `sdg` | `cy` `cz` `swap` | `cy` `cz` `swap` | +| `t`, `t__adj` | `t`, `tdg` | `ccx` | `ccx` | +| `sx` | `sx` | `ccz` | `ctrl(2) @ z` | +| `rx` `ry` `rz` (angle first) | `rx` `ry` `rz` | `rxx` `ryy` `rzz` | their basis changes, two `cx` and one `rz` | +| `r1` | `p` | `mz`, `m`, `mresetz`, `reset`, `barrier` | `measure`, `reset`; a barrier is left out | + +Any other intrinsic is refused (`Q706`), and so is a `__ctl` form, whose +controls come in an array (`Q705`). Angles are read at their exact doubles, +LLVM's hex form (`double 0x400921FB54442D18`) or decimal. A Clifford+T +module stays Clifford+T and runs on the bit-exact method; a rotation at an +angle that is not a dyadic multiple of 2π reads with a note (`Q709`): the +double-precision methods take it, the bit-exact method refuses it. + +**Writing.** QIR 2.0 with opaque pointers, the four mandatory module flags +(and, under the adaptive profile, `ir_functions`, `backwards_branching`, +`multiple_target_branching` and `multiple_return_points`, each off), one +`array_record_output` and a `result_record_output` for every bit. The base +profile's four blocks; under the adaptive profile, one block per step a +branch reaches, a condition on several bits read bit by bit with +`__quantum__rt__read_result` and `br` alone. Gates are written as the +intrinsics above where one exists; the others are lowered exactly, up to a +global phase (`U` as `rz ry rz`, `cp` and the controlled rotations by their +standard identities, `ch`, `cu`, `iswap` and `cswap` by theirs), and a phase +at a multiple of π/4 is written as `z`, `s` and `t`, so a Clifford+T program +stays Clifford+T. Every angle is written as its exact hex double. A program +whose steps jump backwards is refused (`Q720`), as is a program that branches +when the base profile is asked for. + +## jeff + +**The module.** A list of functions, each a dataflow graph of operations +over values a qubit uses exactly once. The reader runs the entry point's +graph in an order its values allow: + +- **Qubits**: each `alloc` is a new qubit, in the order the reader reaches + it; a register (`qureg`) of a size known on reading is its qubits, + extracted, inserted, sliced, split and joined by index. An entry point + that takes qubits takes them as the program's first qubits, from `|0⟩`, + and returns them unmeasured (`Q716`). +- **Gates**: the well-known gates (`x y z s t h`, `i` as `id`, `r1` as `p`, + `rx ry rz`, `u` as `U`, `swap`, `gphase`), with their control qubits + given after their targets, as the schema says, and `adjoint` and `power` + (0 and 1 both apply the gate once) as `ctrl @`, `inv @` and `pow @`. A + Pauli-product rotation is `exp(iθP)`, as the schema defines it, lowered + exactly to basis changes, a parity ladder and one `rz` (`Q712`). A custom + gate is read only when its name is a gate of the standard library, in any + case, with that gate's qubits and parameters, and says so (`Q711`); any + other is refused (`Q710`). +- **Measurement**: `measure` and `measureNd` each write a new bit, in the + order the reader reaches them; when the entry point returns its bits + (one-bit integers, or arrays of them), the program's bits are those, in + that order. `reset` resets. +- **Classical values** known on reading are computed then, the same bits on + every platform: integers at their width, floats through the crate's own + functions. A measured bit may be negated, combined with `and`, `or`, + `xor`, `eq` and `select`, widened, and branched on; other arithmetic on it + is refused (`Q714`). +- **Control flow** (`Q715`): a `for` whose bounds are known, and a `while` + whose condition is, unrolled; a `switch` on a known value decided; a + `switch` on a measured bit written as `if … else`, a bit set on one side + joined with the other side's as a condition; and a call of a function the + module defines inlined, up to 32 deep. + +A value used twice, before it is defined or as the wrong type is refused +(`Q713`); a value the reader needs on reading and cannot know is refused +(`Q714`), as is a qubit the two sides of a branch leave in different places +(`Q708`). Metadata is not read. The reader takes up to 20 000 000 operations +run (`Q312`), 65 536 qubits and 64 MiB. + +**The wire format.** A Cap'n Proto message, read and written by a reader +and writer of its own (no dependency): segments, far pointers and every +element size, each pointer bounds-checked, at most 8 Mi words visited and +64 pointers deep, so a message that points many times at one object or +loops does no more work than that. + +**Writing.** One function, `main`: every qubit allocated in order, each gate +a well-known gate, exactly, global phase included (controlled gates with +their control qubits, `sx` as `rx(π/2)` and a global phase, `rxx`, `ryy` +and `rzz` as Pauli-product rotations, `iswap` by its Clifford identity, `cu` +as a controlled `u` and the phase on its control), each measurement written +into a bit array at its bit's index, which `main` returns, and every qubit +not measured last freed. A program that branches on what it measured is +refused (`Q720`); one that measures in the middle or resets is written with +`measureNd` and `reset`. + +## Conformance + +`wai/quantum-conformance/formats/` holds a module in each format for each +construct, with the program it must read to, the checks that hold it (the +same circuit by the ladder, the same counts, a round trip, the notes it +gives) and the refusals with their codes and places; CI runs it natively +and under wasm32-wasip2 (`wai-quantum-formats.yml`). diff --git a/wai/wai-quantum/docs/qasm-diagnostics.md b/wai/wai-quantum/docs/qasm-diagnostics.md index 063205fea..fbb98b799 100644 --- a/wai/wai-quantum/docs/qasm-diagnostics.md +++ b/wai/wai-quantum/docs/qasm-diagnostics.md @@ -140,5 +140,24 @@ engine. A test holds this page to the code's table. | `Q502` | refused | A program that measures a qubit and then acts on it, resets one after its first gate, or acts on measured bits: the bit-exact circuit takes programs whose measurements all come at the end. | | `Q600` | carried | An `output` declaration, carried into the result: its bits (their places in each outcome) or its value. | | `Q601` | refused | An `output` that is not a bit and whose value depends on a measurement at the end. | +| `Q700` | refused | A file that does not read in the format named: QIR that is not LLVM text with one entry point, or jeff that is not a Cap'n Proto message of the jeff schema (a pointer outside the message, a struct or list of the wrong shape, more than the reader takes); the message says what and where. | +| `Q701` | refused | A format version the reader does not take: QIR whose `qir_major_version` is not 1 or 2, or jeff other than 0.2 and 0.3. | +| `Q702` | warned | A QIR module that leaves out what its profile requires (the `qir_profiles` attribute, the version flags, `required_num_qubits`, `required_num_results`, the `entry_point` attribute of its only function): read from what it holds, and said so. | +| `Q703` | refused | A QIR profile the reader does not take: `qir_profiles` names neither `base_profile` nor `adaptive_profile`. | +| `Q704` | refused | What the module's own QIR profile forbids: under the base profile a branch, a classical instruction, reading a result, or a gate or reset on a qubit after its measurement; under the adaptive profile an optional capability its module flags do not declare (computation on integers or floats of a type, IR-defined functions, loops, `switch`, several returns, dynamic qubits or results, arrays). | +| `Q705` | refused | A QIR capability the module may use that this reader does not take: loops, `switch`, `phi`, IR-defined functions, dynamic allocation of qubits or results, arrays, controlled forms that take their controls in an array, or a classical value computed from a measurement anywhere but a branch's condition. | +| `Q706` | refused | An instruction or function the reader does not know: a quantum intrinsic outside the gates it reads (`docs/formats.md` lists them), a runtime function it does not take, an LLVM instruction no profile lists, or a function a module declares and does not define. | +| `Q707` | refused | A QIR operand that does not read: a qubit or result that is not a constant index, an index past `required_num_qubits` or `required_num_results`, or an angle that is not a finite number known on reading. | +| `Q708` | refused | A value whose meaning the program's text cannot keep: in QIR a result read, measured again and only then branched on; in jeff a qubit the two sides of a branch on a measurement leave in different places. | +| `Q709` | warned | Rotations at angles that are not dyadic multiples of 2π, read at their exact doubles: the double-precision methods take them; the bit-exact method refuses them (Q500). | +| `Q710` | refused | A jeff custom gate the reader does not know: its name is not a gate of the standard library (in any case), or its qubits or parameters are not that gate's. | +| `Q711` | warned | A jeff custom gate named as a gate of the standard library (`H`, `CX`, `Rx`, in any case) with that gate's qubits and parameters: read as that gate, by its name alone. | +| `Q712` | lowered | A jeff Pauli-product rotation, `exp(iθP)` as its schema defines it, lowered exactly to basis changes, a parity ladder and one `rz`. | +| `Q713` | refused | A jeff value used where it cannot be: a qubit or register used twice, a value used before it is defined or never defined, a cycle, or a value of the wrong type or count for its operation. | +| `Q714` | refused | A jeff value the reader needs on reading and cannot know: a gate angle, a loop's bounds or a `while` condition from a measurement, a register's size or index, a `switch` on a value that is neither known nor a measured bit, or integer arithmetic on a measured bit beyond not, and, or, xor, eq and select. | +| `Q715` | lowered | jeff's structured control flow: a `for` or `while` decided on reading unrolled, a `switch` on a known value decided, a `switch` on a measured bit written as `if … else`, and a call of a function the module defines inlined. | +| `Q716` | warned | A jeff entry point that takes qubits: they are the program's first qubits, starting in \|0⟩, and the qubits it returns are not measured. | +| `Q720` | refused | A program the format written cannot hold as it is: under QIR's base profile a measurement in the middle, a reset after a gate or a condition (write the adaptive profile), a loop in QIR, or in jeff a branch on a measured bit. | +| `Q721` | warned | What the format read or written has no place for, left out and said so: delays, boxes, barriers and gate durations, calibrations, `output` declarations, and classical values recorded as output. | -See `qasm-feature-matrix.md` for each construct of OpenQASM 3.1 and its code. +See `qasm-feature-matrix.md` for each construct of OpenQASM 3.1 and its code. The codes from `Q700` are those of the readers and writers of other formats (QIR and jeff, `formats.md`). diff --git a/wai/wai-quantum/src/engine/formats/capnp.rs b/wai/wai-quantum/src/engine/formats/capnp.rs new file mode 100644 index 000000000..41451dff3 --- /dev/null +++ b/wai/wai-quantum/src/engine/formats/capnp.rs @@ -0,0 +1,693 @@ +//! The Cap'n Proto wire format, as far as a schema-driven exchange format +//! needs it: a message read from its segments (structs, lists of every +//! element size, text and far pointers) with every pointer bounds-checked and +//! a limit on the words a reader may visit, and a message written as one +//! segment in preorder, each object after the one that points to it. +//! +//! The layout of each struct (which word holds a field, which pointer slot a +//! list sits in) is the schema's, and is the caller's business: this module +//! knows the encoding, not any schema. +//! +//! - A message is a segment table (the segment count less one, then each +//! segment's size in words, as little-endian `u32`s, padded to a word) +//! followed by the segments. +//! - A pointer is one word. Its low two bits are its kind: `0` a struct (a +//! signed offset in words from the end of the pointer, the data section's +//! size in words, the pointer section's), `1` a list (offset, element size, +//! count), `2` a far pointer to a landing pad in another segment, `3` a +//! capability, which a message on disk never holds. +//! - A list's element size is `0` void, `1` a bit, `2` a byte, `3` two bytes, +//! `4` four, `5` eight, `6` a pointer, `7` a struct each (composite), with a +//! tag word before the elements that gives their count and size. + +/// The most words a reader visits in one message: a list or struct read +/// twice counts twice, so a message that points many times at one object +/// cannot make the reader do more work than this. +pub const TRAVERSAL_LIMIT_WORDS: u64 = 8 << 20; +/// The most segments a message may have. +pub const MAX_SEGMENTS: usize = 512; +/// The deepest the reader follows pointers. +pub const MAX_DEPTH: u32 = 64; + +/// A message read: its segments, and what is left of its traversal budget. +pub struct Message<'a> { + segments: Vec<&'a [u8]>, + budget: std::cell::Cell, +} + +/// A struct inside a message. +#[derive(Clone, Copy)] +pub struct Struct<'a> { + msg: &'a Message<'a>, + seg: usize, + /// Byte offset of the data section in its segment. + data: usize, + data_words: u16, + /// Byte offset of the pointer section in its segment. + ptrs: usize, + ptr_count: u16, + depth: u32, +} + +/// A list inside a message. +#[derive(Clone, Copy)] +pub struct List<'a> { + msg: &'a Message<'a>, + seg: usize, + /// Byte offset of the first element. + start: usize, + /// The element size code (0 to 7). + size: u8, + len: u32, + /// For a composite list: each element's data and pointer words. + data_words: u16, + ptr_count: u16, + depth: u32, +} + +fn word(seg: &[u8], byte: usize) -> u64 { + u64::from_le_bytes(seg[byte..byte + 8].try_into().expect("eight bytes")) +} + +impl<'a> Message<'a> { + /// Reads a message's segment table. + pub fn read(bytes: &'a [u8]) -> Result, String> { + if bytes.len() < 8 { + return Err(format!("{} bytes: shorter than a segment table", bytes.len())); + } + let count = u32::from_le_bytes(bytes[0..4].try_into().expect("four bytes")) as usize + 1; + if count > MAX_SEGMENTS { + return Err(format!("{count} segments: more than the {MAX_SEGMENTS} a message may have here")); + } + let table = (4 + 4 * count).div_ceil(8) * 8; + if bytes.len() < table { + return Err("the segment table runs past the end of the file".into()); + } + let mut segments = Vec::with_capacity(count); + let mut at = table; + for i in 0..count { + let words = u32::from_le_bytes(bytes[4 + 4 * i..8 + 4 * i].try_into().expect("four bytes")) as usize; + let end = words.checked_mul(8).and_then(|b| b.checked_add(at)).filter(|&e| e <= bytes.len()).ok_or_else(|| format!("segment {i} ({words} words) runs past the end of the file"))?; + segments.push(&bytes[at..end]); + at = end; + } + if at != bytes.len() { + return Err(format!("{} bytes after the last segment", bytes.len() - at)); + } + if segments[0].len() < 8 { + return Err("the first segment holds no root pointer".into()); + } + Ok(Message { segments, budget: std::cell::Cell::new(TRAVERSAL_LIMIT_WORDS) }) + } + + fn spend(&self, words: u64) -> Result<(), String> { + let left = self.budget.get(); + if words > left { + return Err(format!("the message points at more than the {TRAVERSAL_LIMIT_WORDS} words a reader visits here")); + } + self.budget.set(left - words); + Ok(()) + } + + /// The root struct. + pub fn root(&'a self) -> Result, String> { + self.struct_at(0, 0, 0)?.ok_or_else(|| "the root pointer is null".to_string()) + } + + /// The pointer at byte `at` of segment `seg`, followed through a far + /// pointer: the segment and byte offset of the object, and the pointer + /// word that describes it (a struct's or list's own). + fn resolve(&'a self, seg: usize, at: usize) -> Result, String> { + let s = self.segments[seg]; + if at + 8 > s.len() { + return Err(format!("a pointer at word {} of segment {seg} is past its end", at / 8)); + } + let p = word(s, at); + if p == 0 { + return Ok(None); + } + match p & 3 { + 0 | 1 => { + let off = ((p as u32 as i32) >> 2) as i64; + let target = (at as i64 + 8) + off * 8; + if target < 0 || target as usize > s.len() { + return Err(format!("a pointer at word {} of segment {seg} points outside it", at / 8)); + } + Ok(Some((seg, target as usize, p))) + } + 2 => { + let double = p >> 2 & 1 == 1; + let pad = ((p as u32) >> 3) as usize * 8; + let tseg = (p >> 32) as usize; + let ts = *self.segments.get(tseg).ok_or_else(|| format!("a far pointer names segment {tseg}, which the message does not have"))?; + if !double { + if pad + 8 > ts.len() { + return Err("a far pointer's landing pad is past its segment's end".into()); + } + let inner = word(ts, pad); + if inner & 3 == 2 { + return Err("a far pointer lands on another far pointer".into()); + } + return self.resolve(tseg, pad); + } + if pad + 16 > ts.len() { + return Err("a double-far pointer's landing pad is past its segment's end".into()); + } + let far = word(ts, pad); + let tag = word(ts, pad + 8); + if far & 7 != 2 { + return Err("a double-far landing pad does not start with a far pointer".into()); + } + let cseg = (far >> 32) as usize; + let cs = *self.segments.get(cseg).ok_or_else(|| format!("a far pointer names segment {cseg}, which the message does not have"))?; + let content = ((far as u32) >> 3) as usize * 8; + if content > cs.len() || tag & 3 > 1 || (tag as u32) >> 2 != 0 { + return Err("a double-far pointer's tag or content does not read".into()); + } + Ok(Some((cseg, content, tag))) + } + _ => Err("a capability pointer: a message on disk holds none".into()), + } + } + + fn struct_at(&'a self, seg: usize, at: usize, depth: u32) -> Result>, String> { + if depth > MAX_DEPTH { + return Err(format!("pointers nested deeper than {MAX_DEPTH}")); + } + let Some((seg, start, p)) = self.resolve(seg, at)? else { return Ok(None) }; + if p & 3 != 0 { + return Err("a list where a struct is expected".into()); + } + let data_words = (p >> 32) as u16; + let ptr_count = (p >> 48) as u16; + let words = data_words as usize + ptr_count as usize; + if start + words * 8 > self.segments[seg].len() { + return Err("a struct runs past the end of its segment".into()); + } + self.spend(words.max(1) as u64)?; + Ok(Some(Struct { msg: self, seg, data: start, data_words, ptrs: start + data_words as usize * 8, ptr_count, depth })) + } + + fn list_at(&'a self, seg: usize, at: usize, depth: u32) -> Result>, String> { + if depth > MAX_DEPTH { + return Err(format!("pointers nested deeper than {MAX_DEPTH}")); + } + let Some((seg, start, p)) = self.resolve(seg, at)? else { return Ok(None) }; + if p & 3 != 1 { + return Err("a struct where a list is expected".into()); + } + let size = (p >> 32 & 7) as u8; + let count = (p >> 35) as u32; + let s = self.segments[seg]; + if size == 7 { + // `count` is the words of every element; the tag before them says how many. + if start + 8 > s.len() { + return Err("a list's tag is past the end of its segment".into()); + } + let tag = word(s, start); + if tag & 3 != 0 { + return Err("a composite list's tag is not a struct's".into()); + } + let len = ((tag as u32) >> 2) as usize; + let (dw, pc) = ((tag >> 32) as u16, (tag >> 48) as u16); + let per = dw as usize + pc as usize; + if len.checked_mul(per).is_none_or(|w| w > count as usize) { + return Err("a composite list's elements do not fit the words it gives".into()); + } + if start + 8 + count as usize * 8 > s.len() { + return Err("a list runs past the end of its segment".into()); + } + // An element of no words still costs a word to visit. + self.spend((count as u64).max(len as u64))?; + return Ok(Some(List { msg: self, seg, start: start + 8, size, len: len as u32, data_words: dw, ptr_count: pc, depth })); + } + let bits: u64 = match size { + 0 => 0, + 1 => 1, + 2 => 8, + 3 => 16, + 4 => 32, + 5 | 6 => 64, + _ => unreachable!(), + }; + let bytes = (count as u64 * bits).div_ceil(8) as usize; + if start + bytes > s.len() { + return Err("a list runs past the end of its segment".into()); + } + self.spend((bytes as u64).div_ceil(8).max(count as u64 / 64).max(1))?; + Ok(Some(List { msg: self, seg, start, size, len: count, data_words: 0, ptr_count: 0, depth })) + } +} + +impl<'a> Struct<'a> { + fn seg(&self) -> &'a [u8] { + self.msg.segments[self.seg] + } + + fn bytes(&self, byte: usize) -> Option<[u8; N]> { + (byte + N <= self.data_words as usize * 8).then(|| self.seg()[self.data + byte..self.data + byte + N].try_into().expect("N bytes")) + } + + /// The `u8` at byte `byte` of the data section (0 past its end, as an + /// older writer's struct reads). + pub fn u8(&self, byte: usize) -> u8 { + self.bytes::<1>(byte).map_or(0, |b| b[0]) + } + /// The `u16` at element `i` (bytes `2i`, `2i + 1`). + pub fn u16(&self, i: usize) -> u16 { + self.bytes::<2>(2 * i).map_or(0, u16::from_le_bytes) + } + /// The `u32` at element `i` (bytes `4i` to `4i + 3`). + pub fn u32(&self, i: usize) -> u32 { + self.bytes::<4>(4 * i).map_or(0, u32::from_le_bytes) + } + /// The `u64` at element `i`. + pub fn u64(&self, i: usize) -> u64 { + self.bytes::<8>(8 * i).map_or(0, u64::from_le_bytes) + } + /// The `f32` at element `i`, from its bits. + pub fn f32(&self, i: usize) -> f32 { + f32::from_bits(self.u32(i)) + } + /// The `f64` at element `i`, from its bits. + pub fn f64(&self, i: usize) -> f64 { + f64::from_bits(self.u64(i)) + } + /// The bit at bit offset `bit` of the data section. + pub fn bool(&self, bit: usize) -> bool { + self.u8(bit / 8) >> (bit % 8) & 1 == 1 + } + + fn ptr_at(&self, i: usize) -> Option { + (i < self.ptr_count as usize).then(|| self.ptrs + i * 8) + } + + /// The struct pointer slot `i` holds, if it is not null. + pub fn get_struct(&self, i: usize) -> Result>, String> { + match self.ptr_at(i) { + Some(at) => self.msg.struct_at(self.seg, at, self.depth + 1), + None => Ok(None), + } + } + + /// The list pointer slot `i` holds, if it is not null. + pub fn get_list(&self, i: usize) -> Result>, String> { + match self.ptr_at(i) { + Some(at) => self.msg.list_at(self.seg, at, self.depth + 1), + None => Ok(None), + } + } + + /// The text pointer slot `i` holds: a list of bytes ending in a NUL, in + /// UTF-8. Null reads as empty. + pub fn get_text(&self, i: usize) -> Result { + match self.get_list(i)? { + None => Ok(String::new()), + Some(l) => l.text(), + } + } +} + +impl<'a> List<'a> { + pub fn len(&self) -> usize { + self.len as usize + } + + #[allow(dead_code)] + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + fn seg(&self) -> &'a [u8] { + self.msg.segments[self.seg] + } + + /// Element `i` of a list of structs. + pub fn get_struct(&self, i: usize) -> Result, String> { + if i >= self.len() { + return Err(format!("element {i} of a list of {}", self.len)); + } + if self.size != 7 { + return Err("a list of values where a list of structs is expected".into()); + } + let per = (self.data_words as usize + self.ptr_count as usize) * 8; + let at = self.start + i * per; + Ok(Struct { msg: self.msg, seg: self.seg, data: at, data_words: self.data_words, ptrs: at + self.data_words as usize * 8, ptr_count: self.ptr_count, depth: self.depth }) + } + + fn need(&self, size: u8, what: &str) -> Result<(), String> { + if self.size != size { + return Err(format!("a list of element size {} where a list of {what} is expected", self.size)); + } + Ok(()) + } + + /// A list of `u32`s. + pub fn u32s(&self) -> Result, String> { + self.need(4, "32-bit values")?; + let s = self.seg(); + Ok((0..self.len()).map(|i| u32::from_le_bytes(s[self.start + 4 * i..self.start + 4 * i + 4].try_into().expect("four bytes"))).collect()) + } + /// A list of `u16`s. + pub fn u16s(&self) -> Result, String> { + self.need(3, "16-bit values")?; + let s = self.seg(); + Ok((0..self.len()).map(|i| u16::from_le_bytes(s[self.start + 2 * i..self.start + 2 * i + 2].try_into().expect("two bytes"))).collect()) + } + /// A list of bytes. + pub fn u8s(&self) -> Result, String> { + self.need(2, "bytes")?; + Ok(self.seg()[self.start..self.start + self.len()].to_vec()) + } + /// A list of `u64`s. + pub fn u64s(&self) -> Result, String> { + self.need(5, "64-bit values")?; + let s = self.seg(); + Ok((0..self.len()).map(|i| word(s, self.start + 8 * i)).collect()) + } + /// A list of bits. + pub fn bools(&self) -> Result, String> { + self.need(1, "bits")?; + let s = self.seg(); + Ok((0..self.len()).map(|i| s[self.start + i / 8] >> (i % 8) & 1 == 1).collect()) + } + /// Text: a list of bytes that ends in a NUL, in UTF-8. + pub fn text(&self) -> Result { + let b = self.u8s()?; + match b.split_last() { + Some((0, body)) => String::from_utf8(body.to_vec()).map_err(|_| "text that is not UTF-8".to_string()), + None => Ok(String::new()), + _ => Err("text that does not end in a NUL".into()), + } + } + /// A list of text. + pub fn texts(&self) -> Result, String> { + self.need(6, "pointers")?; + (0..self.len()) + .map(|i| match self.msg.list_at(self.seg, self.start + 8 * i, self.depth + 1)? { + None => Ok(String::new()), + Some(l) => l.text(), + }) + .collect() + } +} + +// --------------------------------------------------------------------------- +// Writing +// --------------------------------------------------------------------------- + +/// A struct to write: its data words and its pointer slots, as the schema +/// sizes them. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Out { + pub data: Vec, + pub ptrs: Vec, +} + +/// What a pointer slot holds. +#[derive(Clone, Debug, Default, PartialEq)] +pub enum Ptr { + #[default] + Null, + Struct(Box), + /// Structs of the same data and pointer sizes, as a composite list. + Structs { data_words: u16, ptr_count: u16, items: Vec }, + #[allow(dead_code)] + U8s(Vec), + U16s(Vec), + U32s(Vec), + #[allow(dead_code)] + U64s(Vec), + Bools(Vec), + Text(String), + Texts(Vec), +} + +impl Out { + /// A struct of `data_words` data words and `ptr_count` null pointers. + pub fn new(data_words: usize, ptr_count: usize) -> Out { + Out { data: vec![0; data_words], ptrs: vec![Ptr::Null; ptr_count] } + } + pub fn set_u8(&mut self, byte: usize, v: u8) { + let w = &mut self.data[byte / 8]; + let sh = (byte % 8) * 8; + *w = (*w & !(0xff << sh)) | (u64::from(v) << sh); + } + pub fn set_u16(&mut self, i: usize, v: u16) { + let w = &mut self.data[i / 4]; + let sh = (i % 4) * 16; + *w = (*w & !(0xffff << sh)) | (u64::from(v) << sh); + } + pub fn set_u32(&mut self, i: usize, v: u32) { + let w = &mut self.data[i / 2]; + let sh = (i % 2) * 32; + *w = (*w & !(0xffff_ffff << sh)) | (u64::from(v) << sh); + } + pub fn set_f64(&mut self, i: usize, v: f64) { + self.data[i] = v.to_bits(); + } + pub fn set_bool(&mut self, bit: usize, v: bool) { + let w = &mut self.data[bit / 64]; + let m = 1u64 << (bit % 64); + *w = if v { *w | m } else { *w & !m }; + } +} + +/// `root` as a message of one segment, every object written after the +/// pointer to it (a preorder, depth first): the same bytes for the same +/// struct, on every platform. +pub fn write(root: &Out) -> Vec { + let mut w: Vec = vec![0]; + place_struct(&mut w, 0, root); + let mut out = Vec::with_capacity(8 + w.len() * 8); + out.extend_from_slice(&0u32.to_le_bytes()); + out.extend_from_slice(&(w.len() as u32).to_le_bytes()); + for x in w { + out.extend_from_slice(&x.to_le_bytes()); + } + out +} + +fn offset(ptr: usize, target: usize) -> u64 { + (((target as i64 - ptr as i64 - 1) as i32 as u32) << 2) as u64 +} + +fn struct_ptr(ptr: usize, target: usize, data: usize, ptrs: usize) -> u64 { + offset(ptr, target) | (data as u64) << 32 | (ptrs as u64) << 48 +} + +fn list_ptr(ptr: usize, target: usize, size: u64, count: usize) -> u64 { + offset(ptr, target) | 1 | size << 32 | (count as u64) << 35 +} + +/// Writes `s` at the end of `w` and points slot `ptr` at it. +fn place_struct(w: &mut Vec, ptr: usize, s: &Out) { + let at = w.len(); + w.extend_from_slice(&s.data); + let ptrs = w.len(); + w.extend(std::iter::repeat_n(0, s.ptrs.len())); + w[ptr] = struct_ptr(ptr, at, s.data.len(), s.ptrs.len()); + for (i, p) in s.ptrs.iter().enumerate() { + place(w, ptrs + i, p); + } +} + +fn pack(items: &[T], bytes: usize, to: impl Fn(T) -> u64) -> Vec { + let per = 8 / bytes; + items.chunks(per).map(|c| c.iter().enumerate().fold(0u64, |acc, (i, &x)| acc | to(x) << (i * bytes * 8))).collect() +} + +fn place(w: &mut Vec, ptr: usize, p: &Ptr) { + let at = w.len(); + match p { + Ptr::Null => {} + Ptr::Struct(s) => place_struct(w, ptr, s), + Ptr::Structs { data_words, ptr_count, items } => { + let per = *data_words as usize + *ptr_count as usize; + w.push((items.len() as u64) << 2 | u64::from(*data_words) << 32 | u64::from(*ptr_count) << 48); + let first = w.len(); + for s in items { + debug_assert_eq!((s.data.len(), s.ptrs.len()), (*data_words as usize, *ptr_count as usize)); + w.extend_from_slice(&s.data); + w.extend(std::iter::repeat_n(0, s.ptrs.len())); + } + w[ptr] = list_ptr(ptr, at, 7, items.len() * per); + for (k, s) in items.iter().enumerate() { + let ptrs = first + k * per + *data_words as usize; + for (i, q) in s.ptrs.iter().enumerate() { + place(w, ptrs + i, q); + } + } + } + Ptr::U8s(v) => { + w.extend(pack(v, 1, u64::from)); + w[ptr] = list_ptr(ptr, at, 2, v.len()); + } + Ptr::Text(t) => { + let mut b = t.as_bytes().to_vec(); + b.push(0); + w.extend(pack(&b, 1, u64::from)); + w[ptr] = list_ptr(ptr, at, 2, b.len()); + } + Ptr::U16s(v) => { + w.extend(pack(v, 2, u64::from)); + w[ptr] = list_ptr(ptr, at, 3, v.len()); + } + Ptr::U32s(v) => { + w.extend(pack(v, 4, u64::from)); + w[ptr] = list_ptr(ptr, at, 4, v.len()); + } + Ptr::U64s(v) => { + w.extend_from_slice(v); + w[ptr] = list_ptr(ptr, at, 5, v.len()); + } + Ptr::Bools(v) => { + w.extend(v.chunks(64).map(|c| c.iter().enumerate().fold(0u64, |acc, (i, &b)| acc | u64::from(b) << i))); + w[ptr] = list_ptr(ptr, at, 1, v.len()); + } + Ptr::Texts(v) => { + w.extend(std::iter::repeat_n(0, v.len())); + w[ptr] = list_ptr(ptr, at, 6, v.len()); + for (i, t) in v.iter().enumerate() { + place(w, at + i, &Ptr::Text(t.clone())); + } + } + } + // An empty list still has a pointer (so it reads as empty, not null). + if w[ptr] == 0 && !matches!(p, Ptr::Null) { + let size = match p { + Ptr::Structs { .. } => 7, + Ptr::U8s(_) | Ptr::Text(_) => 2, + Ptr::U16s(_) => 3, + Ptr::U32s(_) => 4, + Ptr::U64s(_) => 5, + Ptr::Bools(_) => 1, + _ => 6, + }; + w[ptr] = list_ptr(ptr, at, size, 0); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// A struct of every primitive, a nested struct, a composite list and + /// text, written and read back field for field. + #[test] + fn what_is_written_reads_back() { + let mut inner = Out::new(1, 1); + inner.set_u16(1, 0xbeef); + inner.ptrs[0] = Ptr::Text("héllo".into()); + let mut items = Vec::new(); + for k in 0..3u32 { + let mut e = Out::new(1, 1); + e.set_u32(0, 100 + k); + e.ptrs[0] = Ptr::U32s((0..k).collect()); + items.push(e); + } + let mut root = Out::new(2, 6); + root.set_u32(0, 7); + root.set_u16(2, 0x1234); + root.set_u8(6, 9); + root.set_bool(56, true); + root.set_f64(1, -0.1); + root.ptrs[0] = Ptr::Struct(Box::new(inner)); + root.ptrs[1] = Ptr::Structs { data_words: 1, ptr_count: 1, items }; + root.ptrs[2] = Ptr::Texts(vec!["a".into(), String::new(), "bcd".into()]); + root.ptrs[3] = Ptr::Bools(vec![true, false, true, true, false, false, false, false, true]); + root.ptrs[4] = Ptr::U16s(vec![]); + root.ptrs[5] = Ptr::U64s(vec![u64::MAX, 1]); + let bytes = write(&root); + assert_eq!(bytes.len() % 8, 0); + let m = Message::read(&bytes).unwrap(); + let r = m.root().unwrap(); + assert_eq!((r.u32(0), r.u16(2), r.u8(6), r.bool(56), r.bool(57)), (7, 0x1234, 9, true, false)); + assert_eq!(r.f64(1).to_bits(), (-0.1f64).to_bits()); + let i = r.get_struct(0).unwrap().unwrap(); + assert_eq!((i.u16(1), i.get_text(0).unwrap()), (0xbeef, "héllo".to_string())); + let l = r.get_list(1).unwrap().unwrap(); + assert_eq!(l.len(), 3); + for k in 0..3 { + let e = l.get_struct(k).unwrap(); + assert_eq!(e.u32(0), 100 + k as u32); + assert_eq!(e.get_list(0).unwrap().unwrap().u32s().unwrap(), (0..k as u32).collect::>()); + } + assert_eq!(r.get_list(2).unwrap().unwrap().texts().unwrap(), vec!["a", "", "bcd"]); + assert_eq!(r.get_list(3).unwrap().unwrap().bools().unwrap(), vec![true, false, true, true, false, false, false, false, true]); + let empty = r.get_list(4).unwrap().unwrap(); + assert!(empty.is_empty() && empty.u16s().unwrap().is_empty()); + assert_eq!(r.get_list(5).unwrap().unwrap().u64s().unwrap(), vec![u64::MAX, 1]); + // A field past the struct's sections reads as its default. + assert_eq!((r.u64(9), r.get_list(9).unwrap().is_none()), (0, true)); + assert_eq!(write(&root), bytes, "the same struct writes the same bytes"); + } + + /// A message whose pointers lead outside it, loop, or ask for more words + /// than it has is refused, never read past its end. + #[test] + fn malformed_messages_are_refused() { + assert!(Message::read(&[]).is_err()); + assert!(Message::read(&[0, 0, 0, 0, 5, 0, 0, 0]).is_err(), "a segment longer than the file"); + // A root pointing past its segment. + let mut b = vec![0u8, 0, 0, 0, 1, 0, 0, 0]; + b.extend_from_slice(&(struct_ptr(0, 10, 1, 0)).to_le_bytes()); + assert!(Message::read(&b).unwrap().root().is_err()); + // A root pointing at itself as a struct with a pointer: following it + // over and over runs into the depth limit, not forever. + let mut b = vec![0u8, 0, 0, 0, 2, 0, 0, 0]; + b.extend_from_slice(&struct_ptr(0, 1, 0, 1).to_le_bytes()); + b.extend_from_slice(&struct_ptr(1, 1, 0, 1).to_le_bytes()); + let m = Message::read(&b).unwrap(); + let mut s = m.root().unwrap(); + let mut steps = 0; + let err = loop { + match s.get_struct(0) { + Ok(Some(n)) => s = n, + Ok(None) => panic!("the loop ended"), + Err(e) => break e, + } + steps += 1; + }; + assert!(err.contains("deeper") && steps <= MAX_DEPTH as usize, "{err}"); + // A composite list whose tag claims more elements than its words hold. + let mut b = vec![0u8, 0, 0, 0, 4, 0, 0, 0]; + b.extend_from_slice(&struct_ptr(0, 1, 0, 1).to_le_bytes()); + b.extend_from_slice(&list_ptr(1, 2, 7, 1).to_le_bytes()); + b.extend_from_slice(&((5u64 << 2) | 1 << 32).to_le_bytes()); + b.extend_from_slice(&0u64.to_le_bytes()); + let m = Message::read(&b).unwrap(); + assert!(m.root().unwrap().get_list(0).is_err()); + // Text without its NUL. + let mut t = Out::new(0, 1); + t.ptrs[0] = Ptr::U8s(b"abc".to_vec()); + let bytes = write(&t); + assert!(Message::read(&bytes).unwrap().root().unwrap().get_text(0).is_err()); + } + + /// A far pointer and a double-far pointer to an object in another + /// segment read as a near one does. + #[test] + fn far_pointers_are_followed() { + // Segment 0: the root's far pointer. Segment 1: a landing pad and the struct. + let mut seg0 = vec![2u64 | (1u64 << 32)]; + let mut seg1 = vec![struct_ptr(0, 1, 1, 0), 0x2a]; + let mut b = vec![1u8, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0]; + for w in seg0.drain(..).chain(seg1.drain(..)) { + b.extend_from_slice(&w.to_le_bytes()); + } + let m = Message::read(&b).unwrap(); + assert_eq!(m.root().unwrap().u64(0), 0x2a); + // Double far: segment 0 points at a two-word pad in segment 1, whose + // far pointer names the struct's place in segment 2 and whose tag its size. + let words: Vec> = vec![vec![2 | 4 | (1u64 << 32)], vec![2 | (2u64 << 32), 1u64 << 32], vec![0x55]]; + let mut b = vec![2u8, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0]; + for s in &words { + for w in s { + b.extend_from_slice(&w.to_le_bytes()); + } + } + let m = Message::read(&b).unwrap(); + assert_eq!(m.root().unwrap().u64(0), 0x55); + } +} diff --git a/wai/wai-quantum/src/engine/formats/emit.rs b/wai/wai-quantum/src/engine/formats/emit.rs new file mode 100644 index 000000000..01ef1e233 --- /dev/null +++ b/wai/wai-quantum/src/engine/formats/emit.rs @@ -0,0 +1,217 @@ +//! What a reader of another format gives: a circuit of standard gates, +//! measurements, resets and conditions on measured bits, written out as +//! OpenQASM 3 for the one reader every surface uses. Angles are written as +//! the shortest decimal that reads back as the same double, so a rotation +//! keeps its exact bits through the text. + +/// A condition on measured bits. +#[derive(Clone, Debug, PartialEq)] +pub(crate) enum Cond { + Const(bool), + /// The bit, as last measured on the path that reaches the condition. + Bit(u32), + Not(Box), + And(Box, Box), + Or(Box, Box), +} + +impl Cond { + pub(crate) fn not(c: Cond) -> Cond { + match c { + Cond::Const(b) => Cond::Const(!b), + Cond::Not(x) => *x, + x => Cond::Not(Box::new(x)), + } + } + pub(crate) fn and(a: Cond, b: Cond) -> Cond { + match (a, b) { + (Cond::Const(false), _) | (_, Cond::Const(false)) => Cond::Const(false), + (Cond::Const(true), x) | (x, Cond::Const(true)) => x, + (a, b) => Cond::And(Box::new(a), Box::new(b)), + } + } + pub(crate) fn or(a: Cond, b: Cond) -> Cond { + match (a, b) { + (Cond::Const(true), _) | (_, Cond::Const(true)) => Cond::Const(true), + (Cond::Const(false), x) | (x, Cond::Const(false)) => x, + (a, b) => Cond::Or(Box::new(a), Box::new(b)), + } + } + pub(crate) fn xor(a: Cond, b: Cond) -> Cond { + Cond::or(Cond::and(a.clone(), Cond::not(b.clone())), Cond::and(Cond::not(a), b)) + } + fn size(&self) -> usize { + match self { + Cond::Const(_) | Cond::Bit(_) => 1, + Cond::Not(x) => 1 + x.size(), + Cond::And(a, b) | Cond::Or(a, b) => 1 + a.size() + b.size(), + } + } + fn text(&self, s: &mut String) { + match self { + Cond::Const(b) => s.push_str(if *b { "true" } else { "false" }), + Cond::Bit(b) => s.push_str(&format!("c[{b}]")), + Cond::Not(x) => { + s.push_str("!("); + x.text(s); + s.push(')'); + } + Cond::And(a, b) | Cond::Or(a, b) => { + s.push('('); + a.text(s); + s.push_str(if matches!(self, Cond::And(..)) { " && " } else { " || " }); + b.text(s); + s.push(')'); + } + } + } +} + +/// The most nodes a condition may have once written out. +pub(crate) const MAX_COND: usize = 4096; + +/// One statement. +#[derive(Clone, Debug, PartialEq)] +pub(crate) enum Stmt { + /// A gate of the standard library (or `U`, `gphase`), with its + /// parameters, under `controls` (written first), inverted, and repeated + /// `pow` times (1 or more). + Gate { name: &'static str, params: Vec, controls: Vec, qubits: Vec, inv: bool, pow: u32 }, + Measure { qubit: u32, bit: u32 }, + Reset(u32), + If { cond: Cond, then: Vec, other: Vec }, +} + +impl Stmt { + pub(crate) fn gate(name: &'static str, params: Vec, qubits: Vec) -> Stmt { + Stmt::Gate { name, params, controls: Vec::new(), qubits, inv: false, pow: 1 } + } +} + +/// A circuit read from another format. +#[derive(Clone, Debug, Default, PartialEq)] +pub(crate) struct Circuit { + pub n_qubits: u32, + pub n_bits: u32, + pub body: Vec, +} + +/// A double as the shortest decimal that reads back as itself. +pub(crate) fn number(x: f64) -> String { + let s = format!("{x:?}"); + // `{:?}` writes `1e-7` and `1e300`, which OpenQASM reads; it writes no `inf` or `NaN` here. + debug_assert!(x.is_finite()); + s +} + +impl Circuit { + /// The circuit as an OpenQASM 3 program. + pub(crate) fn to_qasm(&self, header: &str) -> String { + let mut s = String::new(); + for line in header.lines() { + s.push_str("// "); + s.push_str(line); + s.push('\n'); + } + s.push_str("OPENQASM 3.0;\ninclude \"stdgates.inc\";\n"); + s.push_str(&format!("qubit[{}] q;\n", self.n_qubits)); + if self.n_bits > 0 { + s.push_str(&format!("bit[{}] c;\n", self.n_bits)); + } + write_body(&mut s, &self.body, 0); + s + } +} + +fn write_body(s: &mut String, body: &[Stmt], depth: usize) { + let pad = " ".repeat(depth); + for st in body { + s.push_str(&pad); + match st { + Stmt::Gate { name, params, controls, qubits, inv, pow } => { + match controls.len() { + 0 => {} + 1 => s.push_str("ctrl @ "), + k => s.push_str(&format!("ctrl({k}) @ ")), + } + if *inv { + s.push_str("inv @ "); + } + if *pow > 1 { + s.push_str(&format!("pow({pow}) @ ")); + } + s.push_str(name); + if !params.is_empty() { + s.push('('); + s.push_str(¶ms.iter().map(|&p| number(p)).collect::>().join(", ")); + s.push(')'); + } + let ops: Vec = controls.iter().chain(qubits).map(|q| format!("q[{q}]")).collect(); + if !ops.is_empty() { + s.push(' '); + s.push_str(&ops.join(", ")); + } + s.push_str(";\n"); + } + Stmt::Measure { qubit, bit } => s.push_str(&format!("c[{bit}] = measure q[{qubit}];\n")), + Stmt::Reset(q) => s.push_str(&format!("reset q[{q}];\n")), + Stmt::If { cond, then, other } => { + s.push_str("if ("); + cond.text(s); + s.push_str(") {\n"); + write_body(s, then, depth + 1); + s.push_str(&pad); + s.push('}'); + if !other.is_empty() { + s.push_str(" else {\n"); + write_body(s, other, depth + 1); + s.push_str(&pad); + s.push('}'); + } + s.push('\n'); + } + } + } +} + +/// Whether a condition is small enough to write. +pub(crate) fn cond_fits(c: &Cond) -> bool { + c.size() <= MAX_COND +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn a_circuit_writes_as_openqasm_and_reads_back() { + let c = Circuit { + n_qubits: 3, + n_bits: 2, + body: vec![ + Stmt::gate("h", vec![], vec![0]), + Stmt::Gate { name: "rz", params: vec![0.1], controls: vec![0, 1], qubits: vec![2], inv: true, pow: 3 }, + Stmt::gate("gphase", vec![-1e-7], vec![]), + Stmt::Measure { qubit: 0, bit: 0 }, + Stmt::If { cond: Cond::xor(Cond::Bit(0), Cond::Const(true)), then: vec![Stmt::gate("x", vec![], vec![1])], other: vec![Stmt::Reset(2)] }, + Stmt::Measure { qubit: 1, bit: 1 }, + ], + }; + let text = c.to_qasm("read from a test"); + assert!(text.starts_with("// read from a test\nOPENQASM 3.0;"), "{text}"); + assert!(text.contains("ctrl(2) @ inv @ pow(3) @ rz(0.1) q[0], q[1], q[2];"), "{text}"); + assert!(text.contains("gphase(-1e-7);"), "{text}"); + assert!(text.contains("if (!(c[0])) {\n x q[1];\n} else {\n reset q[2];\n}"), "{text}"); + let p = crate::qasm::parse(&text).unwrap(); + assert_eq!((p.n_qubits, p.n_bits), (3, 2)); + assert!(p.dynamic.is_some()); + } + + #[test] + fn numbers_keep_their_bits() { + for x in [0.1, -0.5, std::f64::consts::PI, 1e-300, 123456789.0, -0.0, 5e-324] { + let t = number(x); + assert_eq!(t.parse::().unwrap().to_bits(), x.to_bits(), "{t}"); + } + } +} diff --git a/wai/wai-quantum/src/engine/formats/jeff.rs b/wai/wai-quantum/src/engine/formats/jeff.rs new file mode 100644 index 000000000..a2453396b --- /dev/null +++ b/wai/wai-quantum/src/engine/formats/jeff.rs @@ -0,0 +1,1662 @@ +//! jeff, the open exchange format for quantum programs (format 0.3, its +//! Cap'n Proto schema `jeff.capnp`), read to a program and written from one. +//! +//! A jeff module is a list of functions, each a dataflow graph: operations +//! that consume values and produce new ones, a qubit used exactly once after +//! it is produced. Reading runs the entry point's graph in an order its +//! values allow and writes what it does as OpenQASM 3 for the one reader: +//! +//! - **Qubits**: each `alloc` is a new qubit `q[i]`, in the order the reader +//! reaches them; registers (`qureg`) of a size known on reading are their +//! qubits, extracted, inserted, sliced, split and joined by index. An entry +//! point that takes qubits takes them as the program's first qubits, from +//! `|0⟩`, and returns them unmeasured +//! ([`Q716`](crate::qasm::diag::JEFF_ENTRY_QUBITS)). +//! - **Gates**: the well-known gates, with their control qubits (given after +//! the targets, as the schema says), `adjoint` and `power` (0 and 1 both +//! apply the gate once) as `ctrl @`, `inv @` and `pow @`. A Pauli-product +//! rotation `exp(iθP)` is lowered exactly +//! ([`Q712`](crate::qasm::diag::JEFF_PPR)). A custom gate is read only when +//! its name is a gate of the standard library with its qubits and +//! parameters ([`Q711`](crate::qasm::diag::JEFF_CUSTOM_NAMED)); any other +//! is refused ([`Q710`](crate::qasm::diag::JEFF_CUSTOM)). +//! - **Measurement**: each `measure` and `measureNd` writes a new bit, in the +//! order the reader reaches them; when the entry point returns its bits +//! (one-bit integers, or an array of them), the program's bits are those, +//! in that order. +//! - **Classical values** known on reading are computed then, the same bits +//! on every platform (integers at their width; floats through the crate's +//! own functions). A bit from a measurement may be negated, combined with +//! `and`, `or`, `xor`, `eq` and `select`, and branched on. +//! - **Control flow** ([`Q715`](crate::qasm::diag::JEFF_STRUCTURED)): `for` +//! and `while` with what decides them known on reading are unrolled, a +//! `switch` on a known value decided, a `switch` on a measured bit written +//! as `if … else`, and a call of a function defined in the module inlined. +//! +//! Anything else is refused with its code, the function and the operation. +//! +//! **Writing** gives a module of one function, `main`: every qubit +//! allocated, each gate as a well-known gate (controlled gates with their +//! control qubits, `rxx`, `ryy`, `rzz` as Pauli-product rotations), exact +//! including the global phase, and the measurements written into a bit array +//! that `main` returns, its index each measurement's bit. A program that +//! branches on what it measured is refused ([`Q720`](crate::qasm::diag::WRITE_CANNOT)). + +use super::capnp::{Message, Out, Ptr, Struct}; +use super::emit::{self, Circuit, Cond, Stmt}; +use crate::qasm::diag::{self, Diagnostic}; +use crate::qasm::{Instr, Kind, Program}; +use serde_json::{Value, json}; +use std::collections::BTreeMap; + +/// The format version written. +pub const VERSION: (u32, u32, u32) = (0, 3, 1); + +/// What a module was read as. +#[derive(Clone, Debug, PartialEq)] +pub struct Reading { + /// The program, as OpenQASM 3. + pub qasm: String, + /// The warnings and notes, each with its code; where they stand is in + /// the message. + pub notes: Vec, + /// The version, the tool that wrote it, the entry point, its qubits and + /// bits. + pub summary: Value, +} + +fn unread(msg: impl Into) -> Diagnostic { + Diagnostic::new(diag::FORMAT_UNREAD, None, msg) +} + +// --------------------------------------------------------------------------- +// The schema, decoded +// --------------------------------------------------------------------------- + +#[derive(Clone, Debug, PartialEq)] +enum Ty { + Qubit, + Qureg(Option), + Int(u8), + IntArray(u8, Option), + Float(u16), + FloatArray(u16, Option), +} + +#[derive(Clone, Debug)] +struct Region { + sources: Vec, + targets: Vec, + ops: Vec, +} + +#[derive(Clone, Debug)] +struct Op { + inputs: Vec, + outputs: Vec, + ins: Ins, +} + +#[derive(Clone, Debug)] +enum GateKind { + WellKnown(u16), + Custom { name: u16, qubits: u8, params: u8 }, + Ppr(Vec), +} + +#[derive(Clone, Debug)] +struct Gate { + kind: GateKind, + controls: u8, + adjoint: bool, + power: u8, +} + +#[derive(Clone, Debug)] +enum Ins { + Qubit(u16, Option), + Qureg(u16), + Int(u16, u64), + IntArray(u16, Vec, u8), + Float(u16, f64), + FloatArray(u16, Vec), + Switch(Vec, Option>), + For(Box), + While(Box, Box), + Call(u16), +} + +#[derive(Clone, Debug)] +enum Func { + Definition { body: Region, values: Vec }, + Declaration, +} + +struct Module { + version: (u32, u32, u32), + tool: String, + tool_version: String, + functions: Vec<(u16, Func)>, + strings: Vec, + entry: u16, +} + +fn list_u32(s: &Struct, i: usize) -> Result, String> { + Ok(match s.get_list(i)? { + Some(l) => l.u32s()?, + None => Vec::new(), + }) +} + +fn structs<'a>(s: &Struct<'a>, i: usize) -> Result>, String> { + match s.get_list(i)? { + Some(l) => (0..l.len()).map(|k| l.get_struct(k)).collect(), + None => Ok(Vec::new()), + } +} + +fn ty(s: &Struct) -> Result { + let len = |disc_at: usize, val_at: usize| match s.u16(disc_at) { + 0 => Ok(None), + 1 => Ok(Some(s.u32(val_at))), + d => Err(format!("a length of kind {d}, which the schema does not have")), + }; + Ok(match s.u16(0) { + 0 => Ty::Qubit, + 1 => Ty::Qureg(len(1, 1)?), + 2 => Ty::Int(s.u8(2)), + 3 => Ty::IntArray(s.u8(2), len(2, 2)?), + 4 => Ty::Float(s.u16(1)), + 5 => Ty::FloatArray(s.u16(1), len(2, 2)?), + d => return Err(format!("a type of kind {d}, which the schema does not have")), + }) +} + +fn values(s: &Struct, i: usize) -> Result, String> { + structs(s, i)?.iter().map(|v| v.get_struct(0)?.ok_or_else(|| "a value with no type".to_string()).and_then(|t| ty(&t))).collect() +} + +fn region(s: &Struct) -> Result { + Ok(Region { sources: list_u32(s, 0)?, targets: list_u32(s, 1)?, ops: structs(s, 2)?.iter().map(op).collect::>()? }) +} + +fn region_at(s: &Struct, i: usize) -> Result, String> { + s.get_struct(i)?.map(|r| region(&r)).transpose() +} + +fn op(s: &Struct) -> Result { + let inputs = list_u32(s, 0)?; + let outputs = list_u32(s, 1)?; + let body = s.get_struct(3)?; + fn need(b: Option>) -> Result, String> { + b.ok_or_else(|| "an operation with no instruction".to_string()) + } + let ins = match s.u16(0) { + 0 => { + let q = need(body)?; + let d = q.u16(0); + let gate = if d == 6 { + let g = q.get_struct(0)?.ok_or("a gate with no gate")?; + let kind = match g.u16(1) { + 0 => GateKind::WellKnown(g.u16(0)), + 1 => GateKind::Custom { name: g.u16(0), qubits: g.u8(4), params: g.u8(5) }, + 2 => GateKind::Ppr(match g.get_list(0)? { + Some(l) => l.u16s()?, + None => Vec::new(), + }), + k => return Err(format!("a gate of kind {k}, which the schema does not have")), + }; + Some(Gate { kind, controls: g.u8(6), adjoint: g.bool(56), power: g.u8(8) }) + } else { + None + }; + Ins::Qubit(d, gate) + } + 1 => Ins::Qureg(need(body)?.u16(0)), + 2 => { + let i = need(body)?; + let d = i.u16(1); + let v = match d { + 0 => u64::from(i.bool(0)), + 1 => u64::from(i.u8(0)), + 2 => u64::from(i.u16(0)), + 3 => u64::from(i.u32(1)), + 4 => i.u64(1), + _ => 0, + }; + Ins::Int(d, v) + } + 3 => { + let a = need(body)?; + let d = a.u16(0); + let consts = match (d, a.get_list(0)?) { + (0, Some(l)) => l.bools()?.into_iter().map(u64::from).collect(), + (1, Some(l)) => l.u8s()?.into_iter().map(u64::from).collect(), + (2, Some(l)) => l.u16s()?.into_iter().map(u64::from).collect(), + (3, Some(l)) => l.u32s()?.into_iter().map(u64::from).collect(), + (4, Some(l)) => l.u64s()?, + _ => Vec::new(), + }; + Ins::IntArray(d, consts, a.u8(2)) + } + 4 => { + let f = need(body)?; + let d = f.u16(2); + let v = match d { + 0 => f64::from(f.f32(0)), + 1 => f.f64(1), + _ => 0.0, + }; + Ins::Float(d, v) + } + 5 => { + let f = need(body)?; + let d = f.u16(0); + let consts = match (d, f.get_list(0)?) { + (0, Some(l)) => l.u32s()?.into_iter().map(|b| f64::from(f32::from_bits(b))).collect(), + (1, Some(l)) => l.u64s()?.into_iter().map(f64::from_bits).collect(), + _ => Vec::new(), + }; + Ins::FloatArray(d, consts) + } + 6 => { + let c = need(body)?; + match c.u16(0) { + 0 => Ins::Switch(structs(&c, 0)?.iter().map(region).collect::>()?, region_at(&c, 1)?.map(Box::new)), + 1 => Ins::For(Box::new(region_at(&c, 0)?.ok_or("a `for` with no body")?)), + 2 => Ins::While(Box::new(region_at(&c, 0)?.ok_or("a `while` with no `before` region")?), Box::new(region_at(&c, 1)?.ok_or("a `while` with no `after` region")?)), + k => return Err(format!("a control-flow operation of kind {k}, which the schema does not have")), + } + } + 7 => Ins::Call(need(body)?.u16(0)), + k => return Err(format!("an instruction of kind {k}, which the schema does not have")), + }; + Ok(Op { inputs, outputs, ins }) +} + +fn decode(bytes: &[u8]) -> Result { + let m = Message::read(bytes)?; + let root = m.root()?; + let mut functions = Vec::new(); + for f in structs(&root, 0)? { + let func = match f.u16(1) { + 0 => { + let body = region_at(&f, 0)?.unwrap_or(Region { sources: vec![], targets: vec![], ops: vec![] }); + Func::Definition { body, values: values(&f, 1)? } + } + 1 => Func::Declaration, + k => return Err(format!("a function of kind {k}, which the schema does not have")), + }; + functions.push((f.u16(0), func)); + } + let strings = match root.get_list(1)? { + Some(l) => l.texts()?, + None => Vec::new(), + }; + Ok(Module { version: (root.u32(0), root.u32(2), root.u32(3)), tool: root.get_text(3)?, tool_version: root.get_text(4)?, functions, strings, entry: root.u16(2) }) +} + +// --------------------------------------------------------------------------- +// Running the graph +// --------------------------------------------------------------------------- + +/// A value as the reader holds it. +#[derive(Clone, Debug, PartialEq)] +enum V { + Qubit(u32), + Qureg(Vec>), + /// An integer of a width: known on reading, or (a one-bit integer) a + /// condition on measured bits. + Int(u8, IntV), + IntArray(u8, Vec), + Float(f64), + FloatArray(Vec), + /// Used already (a qubit or a register). + Used, + /// What the two sides of a branch leave different, and is not a bit. + Unknown(String), +} + +#[derive(Clone, Debug, PartialEq)] +enum IntV { + Known(u64), + Cond(Cond), +} + +const WELL_KNOWN: [(&str, &str, usize, usize); 14] = [ + // (jeff name, OpenQASM gate, qubits, parameters), by enum value. + ("x", "x", 1, 0), + ("y", "y", 1, 0), + ("z", "z", 1, 0), + ("s", "s", 1, 0), + ("t", "t", 1, 0), + ("r1", "p", 1, 1), + ("rx", "rx", 1, 1), + ("ry", "ry", 1, 1), + ("rz", "rz", 1, 1), + ("h", "h", 1, 0), + ("u", "U", 1, 3), + ("swap", "swap", 2, 0), + ("i", "id", 1, 0), + ("gphase", "gphase", 0, 1), +]; + +/// The OpenQASM names a custom gate may carry, any case, to be read as that gate. +const STANDARD: &[&str] = &[ + "id", "x", "y", "z", "h", "s", "sdg", "t", "tdg", "sx", "sxdg", "rx", "ry", "rz", "p", "phase", "u1", "u2", "u3", "u", "cx", "cnot", "cy", "cz", "ch", "cp", "cphase", "cu1", "crx", "cry", "crz", "cu3", "cu", "swap", "iswap", "rxx", "ryy", "rzz", "ccx", "toffoli", "ccz", "cswap", "fredkin", +]; + +/// The most operations run, unrolled loops and inlined calls included. +const MAX_STEPS: usize = 20_000_000; +/// The deepest calls and regions are nested. +const MAX_NESTING: u32 = 32; + +struct Runner<'m> { + m: &'m Module, + n_qubits: u32, + n_bits: u32, + steps: usize, + notes: BTreeMap<&'static str, (usize, String)>, + custom: BTreeMap, + ppr: usize, + arbitrary: usize, +} + +/// A function's values as it runs. +struct Frame<'m> { + f: u16, + values: &'m [Ty], + vals: Vec>, +} + +impl<'m> Runner<'m> { + fn name(&self, f: u16) -> String { + match self.m.functions.get(f as usize) { + Some((n, _)) => self.m.strings.get(*n as usize).cloned().unwrap_or_else(|| format!("#{f}")), + None => format!("#{f}"), + } + } + + fn at(&self, fr: &Frame, k: usize) -> String { + format!("in function `{}`, operation {k}", self.name(fr.f)) + } + + fn note(&mut self, code: &'static str, what: String) { + self.notes.entry(code).or_insert((0, what)).0 += 1; + } + + fn take(&self, fr: &mut Frame, v: u32, at: &str) -> Result { + let slot = fr.vals.get_mut(v as usize).ok_or_else(|| Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: value {v} is outside the function's {} values", fr.values.len())))?; + let x = slot.clone().ok_or_else(|| Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: value {v} is used before it is defined")))?; + match x { + V::Used => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: value {v} (a qubit or register) is used a second time"))), + V::Qubit(_) | V::Qureg(_) => { + *slot = Some(V::Used); + Ok(x) + } + V::Unknown(why) => Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: value {v} is not known on reading: {why}"))), + other => Ok(other), + } + } + + fn put(&self, fr: &mut Frame, v: u32, x: V, at: &str) -> Result<(), Diagnostic> { + let ty = fr.values.get(v as usize).ok_or_else(|| Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: value {v} is outside the function's {} values", fr.values.len())))?; + let fits = match (&x, ty) { + (V::Qubit(_), Ty::Qubit) | (V::Qureg(_), Ty::Qureg(_)) | (V::Float(_), Ty::Float(_)) | (V::FloatArray(_), Ty::FloatArray(..)) | (V::Unknown(_), _) => true, + (V::Int(w, _), Ty::Int(t)) => w == t, + (V::IntArray(w, _), Ty::IntArray(t, _)) => w == t, + _ => false, + }; + if !fits { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: value {v} is declared {ty:?}, and the operation gives it something else"))); + } + fr.vals[v as usize] = Some(x); + Ok(()) + } + + fn qubit(&mut self, x: V, at: &str) -> Result { + match x { + V::Qubit(q) => Ok(q), + _ => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a qubit is expected"))), + } + } + + fn known_int(&self, x: &V, at: &str, what: &str) -> Result { + match x { + V::Int(_, IntV::Known(v)) => Ok(*v), + V::Int(_, IntV::Cond(_)) => Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: {what} depends on a measurement"))), + _ => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: {what} is not an integer"))), + } + } + + fn float(&self, x: &V, at: &str) -> Result { + match x { + V::Float(f) if f.is_finite() => Ok(*f), + V::Float(_) => Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: an angle that is not finite"))), + _ => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a float is expected"))), + } + } + + fn fresh_qubit(&mut self, at: &str) -> Result { + if self.n_qubits >= crate::qasm::MAX_QUBITS { + return Err(Diagnostic::new(diag::TOO_MANY_QUBITS, None, format!("{at}: more qubits than the reader takes ({})", crate::qasm::MAX_QUBITS))); + } + self.n_qubits += 1; + Ok(self.n_qubits - 1) + } + + fn step(&mut self, at: &str) -> Result<(), Diagnostic> { + self.steps += 1; + if self.steps > MAX_STEPS { + return Err(Diagnostic::new(diag::UNROLL_LIMIT, None, format!("{at}: the module runs to more than {MAX_STEPS} operations"))); + } + Ok(()) + } + + /// Runs `r` with its sources bound to `args`, and gives its targets. + fn region(&mut self, fr: &mut Frame, r: &Region, args: Vec, out: &mut Vec, depth: u32) -> Result, Diagnostic> { + if depth > MAX_NESTING { + return Err(Diagnostic::new(diag::TOO_DEEP, None, format!("in function `{}`: regions and calls nested deeper than {MAX_NESTING}", self.name(fr.f)))); + } + if args.len() != r.sources.len() { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("in function `{}`: a region takes {} values and is given {}", self.name(fr.f), r.sources.len(), args.len()))); + } + // The region's own values are defined afresh each time it runs. + for o in &r.ops { + for &v in &o.outputs { + if let Some(s) = fr.vals.get_mut(v as usize) { + *s = None; + } + } + } + for (&s, a) in r.sources.iter().zip(args) { + let at = format!("in function `{}`, a region's source", self.name(fr.f)); + self.put(fr, s, a, &at)?; + } + for k in order(r, fr.vals.len()).map_err(|v| Diagnostic::new(diag::JEFF_LINEAR, None, format!("in function `{}`: {v}", self.name(fr.f))))? { + self.op(fr, &r.ops[k], k, out, depth)?; + } + let at = format!("in function `{}`, a region's targets", self.name(fr.f)); + r.targets.iter().map(|&t| self.take(fr, t, &at)).collect() + } + + fn op(&mut self, fr: &mut Frame, o: &Op, k: usize, out: &mut Vec, depth: u32) -> Result<(), Diagnostic> { + let at = self.at(fr, k); + self.step(&at)?; + let ins: Vec = match &o.ins { + // A region's inputs are taken by the control flow below. + Ins::Switch(..) | Ins::For(_) | Ins::While(..) | Ins::Call(_) => Vec::new(), + _ => o.inputs.iter().map(|&v| self.take(fr, v, &at)).collect::>()?, + }; + let outs: Vec = match &o.ins { + Ins::Qubit(d, gate) => self.qubit_op(*d, gate.as_ref(), ins, out, &at)?, + Ins::Qureg(d) => self.qureg_op(*d, ins, &at)?, + Ins::Int(d, c) => vec![self.int_op(fr, o, *d, *c, &ins, &at)?], + Ins::IntArray(d, consts, w) => vec![self.int_array_op(fr, o, *d, consts, *w, ins, &at)?], + Ins::Float(d, c) => vec![self.float_op(*d, *c, &ins, &at)?], + Ins::FloatArray(d, consts) => vec![self.float_array_op(*d, consts, ins, &at)?], + Ins::Switch(branches, default) => { + let mut args: Vec = o.inputs.iter().map(|&v| self.take(fr, v, &at)).collect::>()?; + if args.is_empty() { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a `switch` with nothing to switch on"))); + } + let sel = args.remove(0); + self.note(diag::JEFF_STRUCTURED, "structured control flow decided on reading, unrolled, or written as conditions".into()); + match sel { + V::Int(_, IntV::Known(v)) => { + let r = branches.get(v as usize).or(default.as_deref()).ok_or_else(|| Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a `switch` on {v} with no such branch and no default")))?; + self.region(fr, r, args, out, depth + 1)? + } + V::Int(1, IntV::Cond(c)) => { + let zero = branches.first().ok_or_else(|| Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a `switch` with no branches")))?; + let one = branches.get(1).or(default.as_deref()).ok_or_else(|| Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a `switch` on a measured bit with no branch for 1")))?; + let (mut then, mut other) = (Vec::new(), Vec::new()); + let a = self.region(fr, one, args.clone(), &mut then, depth + 1)?; + let b = self.region(fr, zero, args, &mut other, depth + 1)?; + if a.len() != b.len() { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a `switch` whose branches give different numbers of values"))); + } + let merged = a.into_iter().zip(b).map(|(x, y)| join(&c, x, y)).collect::, String>>().map_err(|e| Diagnostic::new(diag::STALE_READ, None, format!("{at}: {e}")))?; + if !emit::cond_fits(&c) { + return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a condition of more than {} terms", emit::MAX_COND))); + } + out.push(Stmt::If { cond: c, then, other }); + merged + } + _ => return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a `switch` on a value that is neither known on reading nor a measured bit"))), + } + } + Ins::For(body) => { + let mut args: Vec = o.inputs.iter().map(|&v| self.take(fr, v, &at)).collect::>()?; + if args.len() < 3 { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a `for` takes a start, a stop and a step"))); + } + let state = args.split_off(3); + let w = match &args[0] { + V::Int(w, _) => *w, + _ => 64, + }; + let sign = |v: u64| -> i128 { if w >= 64 || w == 0 { v as i64 as i128 } else { ((v << (64 - w)) as i64 >> (64 - w)) as i128 } }; + let start = sign(self.known_int(&args[0], &at, "a loop's start")?); + let stop = sign(self.known_int(&args[1], &at, "a loop's stop")?); + let step = sign(self.known_int(&args[2], &at, "a loop's step")?); + if step == 0 { + return Err(Diagnostic::new(diag::BAD_RANGE, None, format!("{at}: a `for` with a step of 0"))); + } + self.note(diag::JEFF_STRUCTURED, "structured control flow decided on reading, unrolled, or written as conditions".into()); + let mut state = state; + let mut i = start; + while (step > 0 && i < stop) || (step < 0 && i > stop) { + self.step(&at)?; + let mut a = vec![V::Int(w, IntV::Known(i as u64 & mask(w)))]; + a.extend(state); + state = self.region(fr, body, a, out, depth + 1)?; + i += step; + } + state + } + Ins::While(before, after) => { + let mut state: Vec = o.inputs.iter().map(|&v| self.take(fr, v, &at)).collect::>()?; + self.note(diag::JEFF_STRUCTURED, "structured control flow decided on reading, unrolled, or written as conditions".into()); + loop { + self.step(&at)?; + let mut r = self.region(fr, before, state, out, depth + 1)?; + if r.is_empty() { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a `while` whose `before` region gives no condition"))); + } + let c = r.remove(0); + match c { + V::Int(_, IntV::Known(0)) => break r, + V::Int(_, IntV::Known(_)) => state = self.region(fr, after, r, out, depth + 1)?, + _ => return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a `while` whose condition depends on a measurement"))), + } + } + } + Ins::Call(f) => { + let args: Vec = o.inputs.iter().map(|&v| self.take(fr, v, &at)).collect::>()?; + self.note(diag::JEFF_STRUCTURED, "structured control flow decided on reading, unrolled, or written as conditions".into()); + self.call(*f, args, out, depth + 1, &at)? + } + }; + if outs.len() != o.outputs.len() { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: the operation gives {} values and names {}", outs.len(), o.outputs.len()))); + } + for (&v, x) in o.outputs.iter().zip(outs) { + self.put(fr, v, x, &at)?; + } + Ok(()) + } + + fn call(&mut self, f: u16, args: Vec, out: &mut Vec, depth: u32, at: &str) -> Result, Diagnostic> { + if depth > MAX_NESTING { + return Err(Diagnostic::new(diag::TOO_DEEP, None, format!("{at}: calls nested deeper than {MAX_NESTING}"))); + } + let m = self.m; + match m.functions.get(f as usize) { + Some((_, Func::Definition { body, values })) => { + let mut fr = Frame { f, values, vals: vec![None; values.len()] }; + self.region(&mut fr, body, args, out, depth) + } + Some((_, Func::Declaration)) => Err(Diagnostic::new(diag::INSTRUCTION_UNKNOWN, None, format!("{at}: a call of `{}`, which the module declares and does not define", self.name(f)))), + None => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a call of function {f}, which the module does not have"))), + } + } + + fn qubit_op(&mut self, d: u16, gate: Option<&Gate>, ins: Vec, out: &mut Vec, at: &str) -> Result, Diagnostic> { + let arity = |n: usize| -> Result<(), Diagnostic> { + if ins.len() != n { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: the operation takes {n} values and is given {}", ins.len()))); + } + Ok(()) + }; + Ok(match d { + 0 => { + arity(0)?; + vec![V::Qubit(self.fresh_qubit(at)?)] + } + 1 | 2 => { + arity(1)?; + self.qubit(ins.into_iter().next().expect("one"), at)?; + vec![] + } + 3 | 4 => { + arity(1)?; + let q = self.qubit(ins.into_iter().next().expect("one"), at)?; + let b = self.n_bits; + self.n_bits += 1; + out.push(Stmt::Measure { qubit: q, bit: b }); + let bit = V::Int(1, IntV::Cond(Cond::Bit(b))); + if d == 3 { vec![bit] } else { vec![V::Qubit(q), bit] } + } + 5 => { + arity(1)?; + let q = self.qubit(ins.into_iter().next().expect("one"), at)?; + out.push(Stmt::Reset(q)); + vec![V::Qubit(q)] + } + 6 => self.gate(gate.ok_or_else(|| unread(format!("{at}: a gate with no gate")))?, ins, out, at)?, + k => return Err(unread(format!("{at}: a qubit operation of kind {k}, which the schema does not have"))), + }) + } + + fn gate(&mut self, g: &Gate, ins: Vec, out: &mut Vec, at: &str) -> Result, Diagnostic> { + let (nq, np) = match &g.kind { + GateKind::WellKnown(w) => { + let e = WELL_KNOWN.get(*w as usize).ok_or_else(|| unread(format!("{at}: well-known gate {w}, which the schema does not have")))?; + (e.2, e.3) + } + GateKind::Custom { qubits, params, .. } => (*qubits as usize, *params as usize), + GateKind::Ppr(p) => (p.len(), 1), + }; + let nc = g.controls as usize; + if ins.len() != nq + nc + np { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: the gate takes {} qubits, {nc} controls and {np} parameters, and is given {} values", nq, ins.len()))); + } + let mut it = ins.into_iter(); + let mut targets = Vec::with_capacity(nq); + for _ in 0..nq { + targets.push(self.qubit(it.next().expect("counted"), at)?); + } + let mut controls = Vec::with_capacity(nc); + for _ in 0..nc { + controls.push(self.qubit(it.next().expect("counted"), at)?); + } + let params: Vec = it.map(|x| self.float(&x, at)).collect::>()?; + let mut all = targets.clone(); + all.extend(&controls); + let mut seen = all.clone(); + seen.sort_unstable(); + seen.dedup(); + if seen.len() != all.len() { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: one qubit given to a gate twice"))); + } + let pow = u32::from(g.power.max(1)); + for &p in ¶ms { + if crate::qasm::dyadic(p).is_none() { + self.arbitrary += 1; + } + } + match &g.kind { + GateKind::WellKnown(w) => { + let name = WELL_KNOWN[*w as usize].1; + out.push(Stmt::Gate { name, params, controls, qubits: targets.clone(), inv: g.adjoint, pow }); + } + GateKind::Custom { name, .. } => { + let text = self.m.strings.get(*name as usize).cloned().ok_or_else(|| unread(format!("{at}: a custom gate named by string {name}, which the module does not have")))?; + let lower = text.to_ascii_lowercase(); + let known = STANDARD.iter().find(|&&s| s == lower).and_then(|&s| crate::qasm::builtin(s).map(|(kind, given)| (s, kind, given))); + let Some((std, _, _)) = known.filter(|(_, kind, given)| kind.arity().1 == nq && *given == np) else { + return Err(Diagnostic::new(diag::JEFF_CUSTOM, None, format!("{at}: the custom gate `{text}` ({nq} qubits, {np} parameters) is not a gate of the standard library with those qubits and parameters"))); + }; + *self.custom.entry(text).or_insert(0) += 1; + if controls.is_empty() && !g.adjoint && pow == 1 { + out.extend(super::qir::portable(std, ¶ms, &targets, false)); + } else { + out.push(Stmt::Gate { name: std, params, controls, qubits: targets.clone(), inv: g.adjoint, pow }); + } + } + GateKind::Ppr(paulis) => { + self.ppr += 1; + let mut theta = params[0] * f64::from(pow); + if g.adjoint { + theta = -theta; + } + let mut on = Vec::new(); + for (k, &p) in paulis.iter().enumerate() { + match p { + 0 => {} + 1..=3 => on.push((targets[k], p)), + _ => return Err(unread(format!("{at}: Pauli {p}, which the schema does not have"))), + } + } + if on.is_empty() { + out.push(Stmt::Gate { name: "gphase", params: vec![theta], controls, qubits: vec![], inv: false, pow: 1 }); + } else { + let g1 = |n: &'static str, q: u32| Stmt::gate(n, vec![], vec![q]); + let mut pre = Vec::new(); + let mut post = Vec::new(); + for &(q, p) in &on { + match p { + 1 => { + pre.push(g1("h", q)); + post.push(g1("h", q)); + } + 2 => { + pre.push(g1("sdg", q)); + pre.push(g1("h", q)); + post.push(g1("s", q)); + post.push(g1("h", q)); + } + _ => {} + } + } + let last = on.last().expect("one").0; + let ladder: Vec = on[..on.len() - 1].iter().map(|&(q, _)| Stmt::gate("cx", vec![], vec![q, last])).collect(); + out.extend(pre); + out.extend(ladder.iter().cloned()); + out.push(Stmt::Gate { name: "rz", params: vec![-2.0 * theta], controls, qubits: vec![last], inv: false, pow: 1 }); + out.extend(ladder.into_iter().rev()); + out.extend(post.into_iter().rev()); + } + } + } + Ok(all.into_iter().map(V::Qubit).collect()) + } + + fn qureg_op(&mut self, d: u16, ins: Vec, at: &str) -> Result, Diagnostic> { + let reg = |x: &V| -> Result>, Diagnostic> { + match x { + V::Qureg(r) => Ok(r.clone()), + _ => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a qubit register is expected"))), + } + }; + let n = |r: &Self, x: &V, what: &str| -> Result { r.known_int(x, at, what).map(|v| v as u32 as usize) }; + let want = |k: usize| -> Result<(), Diagnostic> { + if ins.len() != k { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: the register operation takes {k} values and is given {}", ins.len()))); + } + Ok(()) + }; + let slot = |r: &[Option], i: usize| -> Result<(), Diagnostic> { + if i >= r.len() { + return Err(Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: index {i} of a register of {}", r.len()))); + } + Ok(()) + }; + Ok(match d { + 0 => { + want(1)?; + let k = n(self, &ins[0], "a register's size")?; + let mut r = Vec::with_capacity(k); + for _ in 0..k { + r.push(Some(self.fresh_qubit(at)?)); + } + vec![V::Qureg(r)] + } + 1 | 10 => { + want(1)?; + reg(&ins[0])?; + vec![] + } + 2 => { + want(2)?; + let mut r = reg(&ins[0])?; + let i = n(self, &ins[1], "an index")?; + slot(&r, i)?; + let q = r[i].take().ok_or_else(|| Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: slot {i} of the register is empty")))?; + vec![V::Qureg(r), V::Qubit(q)] + } + 3 => { + want(3)?; + let mut r = reg(&ins[0])?; + let i = n(self, &ins[1], "an index")?; + let q = self.qubit(ins[2].clone(), at)?; + slot(&r, i)?; + if r[i].is_some() { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: slot {i} of the register is filled already"))); + } + r[i] = Some(q); + vec![V::Qureg(r)] + } + 4 => { + want(3)?; + let mut r = reg(&ins[0])?; + let (s, l) = (n(self, &ins[1], "a slice's start")?, n(self, &ins[2], "a slice's length")?); + if s.checked_add(l).is_none_or(|e| e > r.len()) { + return Err(Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: a slice [{s}, {s}+{l}) of a register of {}", r.len()))); + } + let slice: Vec> = r[s..s + l].iter_mut().map(Option::take).collect(); + vec![V::Qureg(r), V::Qureg(slice)] + } + 5 => { + want(3)?; + let mut r = reg(&ins[0])?; + let s = n(self, &ins[1], "a slice's start")?; + let slice = reg(&ins[2])?; + if s.checked_add(slice.len()).is_none_or(|e| e > r.len()) { + return Err(Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: a slice of {} at {s} in a register of {}", slice.len(), r.len()))); + } + for (k, q) in slice.into_iter().enumerate() { + if q.is_some() { + if r[s + k].is_some() { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: slot {} of the register is filled already", s + k))); + } + r[s + k] = q; + } + } + vec![V::Qureg(r)] + } + 6 => { + want(1)?; + let r = reg(&ins[0])?; + let l = r.len() as u64; + vec![V::Qureg(r), V::Int(32, IntV::Known(l))] + } + 7 => { + want(2)?; + let mut r = reg(&ins[0])?; + let i = n(self, &ins[1], "an index")?; + if i > r.len() { + return Err(Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: a split at {i} of a register of {}", r.len()))); + } + let after = r.split_off(i); + vec![V::Qureg(r), V::Qureg(after)] + } + 8 => { + want(2)?; + let mut a = reg(&ins[0])?; + a.extend(reg(&ins[1])?); + vec![V::Qureg(a)] + } + 9 => { + let qs = ins.into_iter().map(|x| self.qubit(x, at).map(Some)).collect::, _>>()?; + vec![V::Qureg(qs)] + } + k => return Err(unread(format!("{at}: a register operation of kind {k}, which the schema does not have"))), + }) + } + + fn int_op(&mut self, fr: &Frame, o: &Op, d: u16, c: u64, ins: &[V], at: &str) -> Result { + let w = match o.outputs.first().and_then(|&v| fr.values.get(v as usize)) { + Some(Ty::Int(w)) => *w, + _ => return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: an integer operation whose output is not an integer"))), + }; + if (0..=4).contains(&d) { + let cw = [1u8, 8, 16, 32, 64][d as usize]; + if cw != w { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a {cw}-bit constant in a {w}-bit value"))); + } + return Ok(V::Int(w, IntV::Known(c & mask(w)))); + } + let arg = |k: usize| -> Result<&IntV, Diagnostic> { + match ins.get(k) { + Some(V::Int(_, v)) => Ok(v), + _ => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: an integer operand is expected"))), + } + }; + let in_w = match ins.first() { + Some(V::Int(w, _)) => *w, + _ => w, + }; + // A bit from a measurement, in the logic a condition can hold. + let as_cond = |v: &IntV| match v { + IntV::Cond(c) => Some(c.clone()), + IntV::Known(k) => Some(Cond::Const(k & 1 == 1)), + }; + let any_cond = ins.iter().any(|x| matches!(x, V::Int(_, IntV::Cond(_)))); + if any_cond { + let c = match d { + 14 if in_w == 1 => Cond::not(as_cond(arg(0)?).expect("a bit")), + 11 | 12 | 13 | 19 if in_w == 1 => { + let (a, b) = (as_cond(arg(0)?).expect("a bit"), as_cond(arg(1)?).expect("a bit")); + match d { + 11 => Cond::and(a, b), + 12 => Cond::or(a, b), + 13 => Cond::xor(a, b), + _ => Cond::not(Cond::xor(a, b)), + } + } + 29 if w == 1 => { + let (s, a, b) = (as_cond(arg(0)?).expect("a bit"), as_cond(arg(1)?).expect("a bit"), as_cond(arg(2)?).expect("a bit")); + Cond::or(Cond::and(s.clone(), a), Cond::and(Cond::not(s), b)) + } + // A bit widened is the same bit. + 30 | 31 if in_w == 1 && w >= 1 => return Ok(V::Int(w, arg(0)?.clone())), + _ => return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: integer arithmetic on a measured bit: the reader holds a measured bit only as a condition (not, and, or, xor, eq, select)"))), + }; + if !emit::cond_fits(&c) { + return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a condition of more than {} terms", emit::MAX_COND))); + } + return Ok(V::Int(w, IntV::Cond(c))); + } + let k = |i: usize| -> Result { + match arg(i)? { + IntV::Known(v) => Ok(*v), + IntV::Cond(_) => unreachable!(), + } + }; + let sx = |v: u64, w: u8| -> i64 { if w >= 64 || w == 0 { v as i64 } else { ((v << (64 - w)) as i64) >> (64 - w) } }; + let bin = |_: &Self| -> Result<(u64, u64), Diagnostic> { Ok((k(0)?, k(1)?)) }; + let v: u64 = match d { + 5 => { + let (a, b) = bin(self)?; + a.wrapping_add(b) + } + 6 => { + let (a, b) = bin(self)?; + a.wrapping_sub(b) + } + 7 => { + let (a, b) = bin(self)?; + a.wrapping_mul(b) + } + 8 | 9 | 25 | 26 => { + let (a, b) = bin(self)?; + if b & mask(in_w) == 0 { + return Err(Diagnostic::new(diag::DIVISION_BY_ZERO, None, format!("{at}: division by zero"))); + } + match d { + 8 => sx(a, in_w).wrapping_div(sx(b, in_w)) as u64, + 9 => (a & mask(in_w)) / (b & mask(in_w)), + 25 => sx(a, in_w).wrapping_rem(sx(b, in_w)) as u64, + _ => (a & mask(in_w)) % (b & mask(in_w)), + } + } + 10 => { + let (a, b) = bin(self)?; + a.wrapping_pow(b.min(u64::from(u32::MAX)) as u32) + } + 11 => k(0)? & k(1)?, + 12 => k(0)? | k(1)?, + 13 => k(0)? ^ k(1)?, + 14 => !k(0)?, + 15 => sx(k(0)?, in_w).min(sx(k(1)?, in_w)) as u64, + 16 => (k(0)? & mask(in_w)).min(k(1)? & mask(in_w)), + 17 => sx(k(0)?, in_w).max(sx(k(1)?, in_w)) as u64, + 18 => (k(0)? & mask(in_w)).max(k(1)? & mask(in_w)), + 19 => u64::from(k(0)? & mask(in_w) == k(1)? & mask(in_w)), + 20 => u64::from(sx(k(0)?, in_w) < sx(k(1)?, in_w)), + 21 => u64::from(sx(k(0)?, in_w) <= sx(k(1)?, in_w)), + 22 => u64::from(k(0)? & mask(in_w) < k(1)? & mask(in_w)), + 23 => u64::from(k(0)? & mask(in_w) <= k(1)? & mask(in_w)), + 24 => sx(k(0)?, in_w).unsigned_abs(), + 27 => k(0)?.checked_shl(k(1)? as u32).unwrap_or(0), + 28 => (k(0)? & mask(in_w)).checked_shr(k(1)? as u32).unwrap_or(0), + 29 => { + if k(0)? & 1 == 1 { + k(1)? + } else { + k(2)? + } + } + 30 => sx(k(0)?, in_w) as u64, + 31 | 32 => k(0)? & mask(in_w), + 33 | 34 => return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: an integer converted to a float has a float output"))), + k => return Err(unread(format!("{at}: an integer operation of kind {k}, which the schema does not have"))), + }; + Ok(V::Int(w, IntV::Known(v & mask(w)))) + } + + #[allow(clippy::too_many_arguments)] + fn int_array_op(&mut self, fr: &Frame, o: &Op, d: u16, consts: &[u64], zw: u8, ins: Vec, at: &str) -> Result { + let w = match o.outputs.first().and_then(|&v| fr.values.get(v as usize)) { + Some(Ty::IntArray(w, _)) => *w, + Some(Ty::Int(w)) => *w, + _ => 64, + }; + Ok(match d { + 0..=4 => V::IntArray(w, consts.iter().map(|&c| IntV::Known(c & mask(w))).collect()), + 5 => { + let n = self.known_int(ins.first().unwrap_or(&V::Used), at, "an array's length")? as usize; + if n > crate::qasm::MAX_OPS { + return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: an array of {n}"))); + } + V::IntArray(zw, vec![IntV::Known(0); n]) + } + 6 => { + let (V::IntArray(aw, a), Some(i)) = (&ins[0], ins.get(1)) else { return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: getIndex takes an array and an index"))) }; + let i = self.known_int(i, at, "an index")? as usize; + let x = a.get(i).cloned().ok_or_else(|| Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: index {i} of an array of {}", a.len())))?; + V::Int(*aw, x) + } + 7 => { + let (V::IntArray(aw, a), Some(i), Some(V::Int(_, x))) = (&ins[0], ins.get(1), ins.get(2)) else { return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: setIndex takes an array, an index and a value"))) }; + let i = self.known_int(i, at, "an index")? as usize; + let mut a = a.clone(); + let n = a.len(); + *a.get_mut(i).ok_or_else(|| Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: index {i} of an array of {n}")))? = x.clone(); + V::IntArray(*aw, a) + } + 8 => match &ins[0] { + V::IntArray(_, a) => V::Int(32, IntV::Known(a.len() as u64)), + _ => return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: length of something that is not an array"))), + }, + 9 => V::IntArray( + w, + ins.iter() + .map(|x| match x { + V::Int(_, v) => Ok(v.clone()), + _ => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: an array of integers is made of integers"))), + }) + .collect::>()?, + ), + k => return Err(unread(format!("{at}: an integer-array operation of kind {k}, which the schema does not have"))), + }) + } + + fn float_op(&mut self, d: u16, c: f64, ins: &[V], at: &str) -> Result { + use crate::qasm::value as m; + let f = |k: usize| -> Result { + match ins.get(k) { + Some(V::Float(x)) => Ok(*x), + _ => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a float operand is expected"))), + } + }; + let b = |x: bool| V::Int(1, IntV::Known(u64::from(x))); + Ok(match d { + 0 | 1 => V::Float(c), + 2 => V::Float(f(0)? + f(1)?), + 3 => V::Float(f(0)? - f(1)?), + 4 => V::Float(f(0)? * f(1)?), + 32 => V::Float(f(0)? / f(1)?), + 5 => V::Float(m::powf(f(0)?, f(1)?)), + 6 => b(f(0)? == f(1)?), + 7 => b(f(0)? < f(1)?), + 8 => b(f(0)? <= f(1)?), + 9 => V::Float(f(0)?.sqrt()), + 10 => V::Float(f(0)?.abs()), + 11 => V::Float(f(0)?.ceil()), + 12 => V::Float(f(0)?.floor()), + 13 => b(f(0)?.is_nan()), + 14 => b(f(0)?.is_infinite()), + 15 => V::Float(m::exp(f(0)?)), + 16 => V::Float(m::ln(f(0)?)), + 17 => V::Float(m::sin(f(0)?)), + 18 => V::Float(m::cos(f(0)?)), + 19 => V::Float(m::tan(f(0)?)), + 20 => V::Float(m::arcsin(f(0)?)), + 21 => V::Float(m::arccos(f(0)?)), + 22 => V::Float(m::arctan(f(0)?)), + 23 => V::Float(crate::repro::atan2(f(0)?, f(1)?)), + 24 => { + let x = f(0)?; + V::Float((m::exp(x) - m::exp(-x)) / 2.0) + } + 25 => { + let x = f(0)?; + V::Float((m::exp(x) + m::exp(-x)) / 2.0) + } + 26 => { + let x = f(0)?; + let (a, z) = (m::exp(x), m::exp(-x)); + V::Float((a - z) / (a + z)) + } + 27 => { + let x = f(0)?; + V::Float(m::ln(x + (x * x + 1.0).sqrt())) + } + 28 => { + let x = f(0)?; + V::Float(m::ln(x + (x * x - 1.0).sqrt())) + } + 29 => { + let x = f(0)?; + V::Float(0.5 * m::ln((1.0 + x) / (1.0 - x))) + } + 30 => V::Float(f(0)?.max(f(1)?)), + 31 => V::Float(f(0)?.min(f(1)?)), + 33 => match ins.first() { + Some(V::Int(_, IntV::Known(s))) => V::Float(if s & 1 == 1 { f(1)? } else { f(2)? }), + _ => return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a float chosen by a measured bit"))), + }, + 34 => V::Float(f(0)?), + 35 => V::Float(f64::from(f(0)? as f32)), + 36 | 37 => return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a float converted to an integer has an integer output"))), + k => return Err(unread(format!("{at}: a float operation of kind {k}, which the schema does not have"))), + }) + } + + fn float_array_op(&mut self, d: u16, consts: &[f64], ins: Vec, at: &str) -> Result { + Ok(match d { + 0 | 1 => V::FloatArray(consts.to_vec()), + 2 => { + let n = self.known_int(ins.first().unwrap_or(&V::Used), at, "an array's length")? as usize; + if n > crate::qasm::MAX_OPS { + return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: an array of {n}"))); + } + V::FloatArray(vec![0.0; n]) + } + 3 => { + let (V::FloatArray(a), Some(i)) = (&ins[0], ins.get(1)) else { return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: getIndex takes an array and an index"))) }; + let i = self.known_int(i, at, "an index")? as usize; + V::Float(*a.get(i).ok_or_else(|| Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: index {i} of an array of {}", a.len())))?) + } + 4 => { + let (V::FloatArray(a), Some(i), Some(V::Float(x))) = (&ins[0], ins.get(1), ins.get(2)) else { return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: setIndex takes an array, an index and a float"))) }; + let i = self.known_int(i, at, "an index")? as usize; + let mut a = a.clone(); + let n = a.len(); + *a.get_mut(i).ok_or_else(|| Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: index {i} of an array of {n}")))? = *x; + V::FloatArray(a) + } + 5 => match &ins[0] { + V::FloatArray(a) => V::Int(32, IntV::Known(a.len() as u64)), + _ => return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: length of something that is not an array"))), + }, + 6 => V::FloatArray(ins.iter().map(|x| self.float(x, at)).collect::>()?), + k => return Err(unread(format!("{at}: a float-array operation of kind {k}, which the schema does not have"))), + }) + } +} + +fn mask(w: u8) -> u64 { + if w >= 64 { u64::MAX } else { (1u64 << w) - 1 } +} + +/// What a value is after a branch on `c`: the same on both sides, or (a bit) +/// the one side's bit or the other's. +fn join(c: &Cond, a: V, b: V) -> Result { + if a == b { + return Ok(a); + } + match (a, b) { + (V::Int(1, x), V::Int(1, y)) => { + let as_c = |v: IntV| match v { + IntV::Cond(c) => c, + IntV::Known(k) => Cond::Const(k & 1 == 1), + }; + Ok(V::Int(1, IntV::Cond(Cond::or(Cond::and(c.clone(), as_c(x)), Cond::and(Cond::not(c.clone()), as_c(y)))))) + } + (V::Qubit(_), V::Qubit(_)) | (V::Qureg(_), V::Qureg(_)) => Err("a qubit leaves the two sides of a branch in different places".into()), + (V::IntArray(w, x), V::IntArray(_, y)) if w == 1 && x.len() == y.len() => { + let mut out = Vec::with_capacity(x.len()); + for (p, q) in x.into_iter().zip(y) { + match join(c, V::Int(1, p), V::Int(1, q))? { + V::Int(_, v) => out.push(v), + _ => unreachable!(), + } + } + Ok(V::IntArray(1, out)) + } + _ => Ok(V::Unknown("it differs between the two sides of a branch on a measurement".into())), + } +} + +/// An order to run a region's operations in: the order they are listed in +/// where that defines every value before its use, else an order their values +/// allow (the first ready operation each time). +fn order(r: &Region, n_values: usize) -> Result, String> { + let mut producer: BTreeMap = BTreeMap::new(); + for (k, o) in r.ops.iter().enumerate() { + for &v in &o.outputs { + if (v as usize) >= n_values { + return Err(format!("operation {k} gives value {v}, outside the function's {n_values} values")); + } + if producer.insert(v, k).is_some() { + return Err(format!("value {v} is given by two operations")); + } + } + } + let deps: Vec> = r.ops.iter().map(|o| inputs_of(o).iter().filter_map(|v| producer.get(v).copied()).collect()).collect(); + let mut indeg: Vec = deps.iter().map(Vec::len).collect(); + let mut users: Vec> = vec![Vec::new(); r.ops.len()]; + for (k, ds) in deps.iter().enumerate() { + for &d in ds { + users[d].push(k); + } + } + let mut ready: std::collections::BinaryHeap> = (0..r.ops.len()).filter(|&k| indeg[k] == 0).map(std::cmp::Reverse).collect(); + let mut out = Vec::with_capacity(r.ops.len()); + while let Some(std::cmp::Reverse(k)) = ready.pop() { + out.push(k); + for &u in &users[k] { + indeg[u] -= 1; + if indeg[u] == 0 { + ready.push(std::cmp::Reverse(u)); + } + } + } + if out.len() != r.ops.len() { + return Err("its operations' values form a cycle".into()); + } + Ok(out) +} + +/// The values an operation reads: its inputs (a region inside it reads its +/// own sources, which its operation's inputs give). +fn inputs_of(o: &Op) -> Vec { + o.inputs.clone() +} + +/// Reads a jeff module. +pub fn read(bytes: &[u8]) -> Result { + if bytes.len() > 64 << 20 { + return Err(unread("a module larger than the 64 MiB the reader takes")); + } + let m = decode(bytes).map_err(|e| unread(format!("not a jeff module: {e}")))?; + let (maj, min, patch) = m.version; + if maj != 0 || !(2..=3).contains(&min) { + return Err(Diagnostic::new(diag::FORMAT_VERSION, None, format!("jeff {maj}.{min}.{patch}: the reader takes 0.2 and 0.3"))); + } + let entry = m.entry; + let (body, values) = match m.functions.get(entry as usize) { + Some((_, Func::Definition { body, values })) => (body, values), + Some((_, Func::Declaration)) => return Err(Diagnostic::new(diag::INSTRUCTION_UNKNOWN, None, "the entry point is declared and not defined")), + None => return Err(unread(format!("the entry point is function {entry}, which the module does not have"))), + }; + let mut r = Runner { m: &m, n_qubits: 0, n_bits: 0, steps: 0, notes: BTreeMap::new(), custom: BTreeMap::new(), ppr: 0, arbitrary: 0 }; + let mut fr = Frame { f: entry, values, vals: vec![None; values.len()] }; + // The entry point's arguments: qubits, from |0⟩. + let mut args = Vec::new(); + for &s in &body.sources { + let at = format!("in function `{}`, the entry point's arguments", r.name(entry)); + match values.get(s as usize) { + Some(Ty::Qubit) => args.push(V::Qubit(r.fresh_qubit(&at)?)), + Some(Ty::Qureg(Some(n))) => { + let mut q = Vec::new(); + for _ in 0..*n { + q.push(Some(r.fresh_qubit(&at)?)); + } + args.push(V::Qureg(q)); + } + Some(t) => return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: an entry point that takes {t:?}, which is not known on reading"))), + None => return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: value {s} is outside the function's values"))), + } + } + if !args.is_empty() { + r.note(diag::JEFF_ENTRY_QUBITS, format!("the entry point takes {} qubit argument{}: they are the program's first qubits, from |0⟩, and what it returns is not measured", args.len(), if args.len() == 1 { "" } else { "s" })); + } + let mut out = Vec::new(); + let targets = r.region(&mut fr, body, args, &mut out, 0)?; + if r.n_qubits == 0 { + return Err(Diagnostic::new(diag::NO_QUBITS, None, format!("the entry point `{}` uses no qubits", r.name(entry)))); + } + // The bits it returns, in order, are the program's. + let mut returned: Vec> = Vec::new(); + let mut plain = true; + for t in &targets { + let entries: Vec = match t { + V::Int(1, v) => vec![v.clone()], + V::IntArray(1, a) => a.clone(), + _ => continue, + }; + for e in entries { + match e { + IntV::Known(0) => returned.push(None), + IntV::Cond(Cond::Bit(b)) if !returned.contains(&Some(b)) => returned.push(Some(b)), + _ => plain = false, + } + } + } + let mut n_bits = r.n_bits; + let mut remap: Vec = (0..r.n_bits).collect(); + let mut bits_note = "the bits are in the order the program measures".to_string(); + if plain && !returned.is_empty() { + let mut next = returned.len() as u32; + let mut placed = vec![false; r.n_bits as usize]; + for (i, e) in returned.iter().enumerate() { + if let Some(b) = e { + remap[*b as usize] = i as u32; + placed[*b as usize] = true; + } + } + for b in 0..r.n_bits as usize { + if !placed[b] { + remap[b] = next; + next += 1; + } + } + n_bits = next; + bits_note = "the bits are those the entry point returns, in its order".into(); + renumber(&mut out, &remap); + } + let mut notes: Vec = std::mem::take(&mut r.notes).into_iter().map(|(code, (_, what))| Diagnostic::new(code, None, what)).collect(); + notes.extend(collect_notes(&r)); + let circuit = Circuit { n_qubits: r.n_qubits, n_bits, body: out }; + let tool = if m.tool.is_empty() { String::new() } else { format!(", written by {} {}", m.tool, m.tool_version).trim_end().to_string() }; + let qasm = circuit.to_qasm(&format!("Read from jeff {maj}.{min}.{patch}{tool}, entry point `{}`: {bits_note}.", r.name(entry))); + let summary = json!({ + "format": "jeff", + "version": format!("{maj}.{min}.{patch}"), + "tool": m.tool, + "tool_version": m.tool_version, + "entry_point": r.name(entry), + "functions": m.functions.len(), + "qubits": r.n_qubits, + "bits": n_bits, + "bits_returned": plain && !returned.is_empty(), + "pauli_product_rotations": r.ppr, + "custom_gates": r.custom, + "arbitrary_angles": r.arbitrary, + }); + Ok(Reading { qasm, notes, summary }) +} + +fn collect_notes(r: &Runner) -> Vec { + let mut v = Vec::new(); + for (name, n) in &r.custom { + v.push(Diagnostic::new(diag::JEFF_CUSTOM_NAMED, None, format!("the custom gate `{name}` ({n} use{}) read as the standard library's `{}`, by its name", if *n == 1 { "" } else { "s" }, name.to_ascii_lowercase()))); + } + if r.ppr > 0 { + v.push(Diagnostic::new(diag::JEFF_PPR, None, format!("{} Pauli-product rotation{} exp(iθP) lowered exactly to basis changes, a parity ladder and one rz", r.ppr, if r.ppr == 1 { "" } else { "s" }))); + } + if r.arbitrary > 0 { + v.push(Diagnostic::new(diag::ARBITRARY_ANGLE, None, format!("{} rotation{} at angles that are not dyadic multiples of 2π: the double-precision methods take them; the bit-exact method refuses them (Q500)", r.arbitrary, if r.arbitrary == 1 { "" } else { "s" }))); + } + v +} + +fn renumber(body: &mut [Stmt], map: &[u32]) { + fn cond(c: &mut Cond, map: &[u32]) { + match c { + Cond::Const(_) => {} + Cond::Bit(b) => *b = map[*b as usize], + Cond::Not(x) => cond(x, map), + Cond::And(a, b) | Cond::Or(a, b) => { + cond(a, map); + cond(b, map); + } + } + } + for s in body { + match s { + Stmt::Measure { bit, .. } => *bit = map[*bit as usize], + Stmt::If { cond: c, then, other } => { + cond(c, map); + renumber(then, map); + renumber(other, map); + } + _ => {} + } + } +} + +// --------------------------------------------------------------------------- +// Writing +// --------------------------------------------------------------------------- + +struct W { + values: Vec, + ops: Vec, + cur: Vec, +} + +fn ty_out(t: &Ty) -> Out { + let mut o = Out::new(2, 0); + match t { + Ty::Qubit => o.set_u16(0, 0), + Ty::Qureg(n) => { + o.set_u16(0, 1); + if let Some(n) = n { + o.set_u16(1, 1); + o.set_u32(1, *n); + } + } + Ty::Int(w) => { + o.set_u16(0, 2); + o.set_u8(2, *w); + } + Ty::IntArray(w, n) => { + o.set_u16(0, 3); + o.set_u8(2, *w); + if let Some(n) = n { + o.set_u16(2, 1); + o.set_u32(2, *n); + } + } + Ty::Float(p) => { + o.set_u16(0, 4); + o.set_u16(1, *p); + } + Ty::FloatArray(p, n) => { + o.set_u16(0, 5); + o.set_u16(1, *p); + if let Some(n) = n { + o.set_u16(2, 1); + o.set_u32(2, *n); + } + } + } + o +} + +impl W { + fn value(&mut self, t: Ty) -> u32 { + self.values.push(t); + self.values.len() as u32 - 1 + } + /// An operation: `kind` is the instruction's union member, `body` its struct. + fn op(&mut self, inputs: Vec, outputs: Vec, kind: u16, body: Out) { + let mut o = Out::new(1, 4); + o.set_u16(0, kind); + o.ptrs[0] = Ptr::U32s(inputs); + o.ptrs[1] = Ptr::U32s(outputs); + o.ptrs[3] = Ptr::Struct(Box::new(body)); + self.ops.push(o); + } + fn float(&mut self, x: f64) -> u32 { + let v = self.value(Ty::Float(1)); + let mut b = Out::new(2, 0); + b.set_u16(2, 1); + b.set_f64(1, x); + self.op(vec![], vec![v], 4, b); + v + } + fn int32(&mut self, x: u32) -> u32 { + let v = self.value(Ty::Int(32)); + let mut b = Out::new(2, 0); + b.set_u16(1, 3); + b.set_u32(1, x); + self.op(vec![], vec![v], 2, b); + v + } + fn qubit_op(&mut self, kind: u16, gate: Option, inputs: Vec, outputs: Vec) { + let mut q = Out::new(1, 1); + q.set_u16(0, kind); + if let Some(g) = gate { + q.ptrs[0] = Ptr::Struct(Box::new(g)); + } + self.op(inputs, outputs, 0, q); + } + /// A gate on `targets` under `controls`: a well-known gate, or a + /// Pauli-product rotation when `ppr` is given. + fn gate(&mut self, well_known: u16, ppr: Option>, targets: &[u32], controls: &[u32], params: &[f64], adjoint: bool) { + let mut g = Out::new(2, 1); + match ppr { + Some(p) => { + g.set_u16(1, 2); + g.ptrs[0] = Ptr::U16s(p); + } + None => g.set_u16(0, well_known), + } + g.set_u8(6, controls.len() as u8); + g.set_bool(56, adjoint); + let floats: Vec = params.iter().map(|&p| self.float(p)).collect(); + let qs: Vec = targets.iter().chain(controls).copied().collect(); + let mut inputs: Vec = qs.iter().map(|&q| self.cur[q as usize]).collect(); + inputs.extend(floats); + let outputs: Vec = qs.iter().map(|_| self.value(Ty::Qubit)).collect(); + for (&q, &o) in qs.iter().zip(&outputs) { + self.cur[q as usize] = o; + } + self.qubit_op(6, Some(g), inputs, outputs); + } +} + +// The well-known gates' enum values. +const X: u16 = 0; +const Y: u16 = 1; +const Z: u16 = 2; +const S: u16 = 3; +const T: u16 = 4; +const R1: u16 = 5; +const RX: u16 = 6; +const RY: u16 = 7; +const RZ: u16 = 8; +const H: u16 = 9; +const U: u16 = 10; +const SWAP: u16 = 11; +const I: u16 = 12; +const GPHASE: u16 = 13; + +fn write_op(w: &mut W, kind: Kind, q: &[u32], p: &[f64]) { + use std::f64::consts::{FRAC_PI_2, FRAC_PI_4}; + let one = |w: &mut W, g: u16, a: u32, params: &[f64], adj: bool| w.gate(g, None, &[a], &[], params, adj); + match kind { + Kind::Id => one(w, I, q[0], &[], false), + Kind::X => one(w, X, q[0], &[], false), + Kind::Y => one(w, Y, q[0], &[], false), + Kind::Z => one(w, Z, q[0], &[], false), + Kind::H => one(w, H, q[0], &[], false), + Kind::S => one(w, S, q[0], &[], false), + Kind::Sdg => one(w, S, q[0], &[], true), + Kind::T => one(w, T, q[0], &[], false), + Kind::Tdg => one(w, T, q[0], &[], true), + // √X = e^{iπ/4} Rx(π/2). + Kind::Sx => { + one(w, RX, q[0], &[FRAC_PI_2], false); + w.gate(GPHASE, None, &[], &[], &[FRAC_PI_4], false); + } + Kind::Sxdg => { + one(w, RX, q[0], &[-FRAC_PI_2], false); + w.gate(GPHASE, None, &[], &[], &[-FRAC_PI_4], false); + } + Kind::Rx => one(w, RX, q[0], p, false), + Kind::Ry => one(w, RY, q[0], p, false), + Kind::Rz => one(w, RZ, q[0], p, false), + Kind::P => one(w, R1, q[0], p, false), + Kind::U => one(w, U, q[0], p, false), + Kind::Cx => w.gate(X, None, &[q[1]], &[q[0]], &[], false), + Kind::Cy => w.gate(Y, None, &[q[1]], &[q[0]], &[], false), + Kind::Cz => w.gate(Z, None, &[q[1]], &[q[0]], &[], false), + Kind::Ch => w.gate(H, None, &[q[1]], &[q[0]], &[], false), + Kind::Cp => w.gate(R1, None, &[q[1]], &[q[0]], p, false), + Kind::Crx => w.gate(RX, None, &[q[1]], &[q[0]], p, false), + Kind::Cry => w.gate(RY, None, &[q[1]], &[q[0]], p, false), + Kind::Crz => w.gate(RZ, None, &[q[1]], &[q[0]], p, false), + // Controlled e^{iγ} U(θ, φ, λ): the controlled U, then the phase γ on the control. + Kind::Cu => { + w.gate(U, None, &[q[1]], &[q[0]], &p[..3], false); + one(w, R1, q[0], &[p[3]], false); + } + Kind::Swap => w.gate(SWAP, None, &[q[0], q[1]], &[], &[], false), + Kind::Iswap => { + one(w, S, q[0], &[], false); + one(w, S, q[1], &[], false); + one(w, H, q[0], &[], false); + w.gate(X, None, &[q[1]], &[q[0]], &[], false); + w.gate(X, None, &[q[0]], &[q[1]], &[], false); + one(w, H, q[1], &[], false); + } + // Rpp(θ) = exp(−iθ/2 P⊗P), and the schema's rotation is exp(iφP). + Kind::Rxx => w.gate(0, Some(vec![1, 1]), &[q[0], q[1]], &[], &[-p[0] / 2.0], false), + Kind::Ryy => w.gate(0, Some(vec![2, 2]), &[q[0], q[1]], &[], &[-p[0] / 2.0], false), + Kind::Rzz => w.gate(0, Some(vec![3, 3]), &[q[0], q[1]], &[], &[-p[0] / 2.0], false), + Kind::Ccx => w.gate(X, None, &[q[2]], &[q[0], q[1]], &[], false), + Kind::Ccz => w.gate(Z, None, &[q[2]], &[q[0], q[1]], &[], false), + Kind::Cswap => w.gate(SWAP, None, &[q[1], q[2]], &[q[0]], &[], false), + } +} + +/// Writes `p` as a jeff module. +pub fn write(p: &Program) -> Result<(Vec, Vec), Diagnostic> { + let mut notes = Vec::new(); + super::qir::dropped(p, &mut notes); + let mut w = W { values: Vec::new(), ops: Vec::new(), cur: Vec::new() }; + for _ in 0..p.n_qubits { + let v = w.value(Ty::Qubit); + w.qubit_op(0, None, vec![], vec![v]); + w.cur.push(v); + } + let static_steps: Vec; + let steps: &[Instr] = match &p.dynamic { + Some(s) => s, + None => { + static_steps = (0..p.ops.len()).map(Instr::Gate).chain(p.measured.iter().map(|&(qubit, bit)| Instr::Measure { qubit, bit })).collect(); + &static_steps + } + }; + let mut n_bits = p.n_bits; + for s in steps { + match s { + Instr::Measure { bit, .. } => n_bits = n_bits.max(bit + 1), + Instr::Jump(_) | Instr::JumpUnless(..) => { + return Err(Diagnostic::new(diag::WRITE_CANNOT, None, "this program branches on what it measured: the jeff writer writes programs whose steps are gates, measurements and resets, without conditions")); + } + _ => {} + } + } + // The bits, as an array the program returns. + let mut bits = None; + if n_bits > 0 { + let v = w.value(Ty::IntArray(1, Some(n_bits))); + let mut a = Out::new(1, 1); + a.set_u16(0, 0); + a.ptrs[0] = Ptr::Bools(vec![false; n_bits as usize]); + w.op(vec![], vec![v], 3, a); + bits = Some(v); + } + // A qubit measured for the last time, with nothing after it, is measured destructively. + let mut last_use = vec![usize::MAX; p.n_qubits as usize]; + for (i, s) in steps.iter().enumerate() { + let qs: Vec = match s { + Instr::Gate(k) => p.ops[*k].qubits.clone(), + Instr::Measure { qubit, .. } | Instr::Reset(qubit) => vec![*qubit], + _ => vec![], + }; + for q in qs { + last_use[q as usize] = i; + } + } + let mut consumed = vec![false; p.n_qubits as usize]; + for (i, s) in steps.iter().enumerate() { + match s { + Instr::Gate(k) => { + let op = &p.ops[*k]; + write_op(&mut w, op.kind, &op.qubits, &op.params); + } + Instr::Measure { qubit, bit } => { + let q = *qubit as usize; + let r = w.value(Ty::Int(1)); + if last_use[q] == i { + w.qubit_op(3, None, vec![w.cur[q]], vec![r]); + consumed[q] = true; + } else { + let nq = w.value(Ty::Qubit); + w.qubit_op(4, None, vec![w.cur[q]], vec![nq, r]); + w.cur[q] = nq; + } + let idx = w.int32(*bit); + let arr = bits.expect("bits"); + let next = w.value(Ty::IntArray(1, Some(n_bits))); + let mut set = Out::new(1, 1); + set.set_u16(0, 7); + w.op(vec![arr, idx, r], vec![next], 3, set); + bits = Some(next); + } + Instr::Reset(q) => { + let nq = w.value(Ty::Qubit); + w.qubit_op(5, None, vec![w.cur[*q as usize]], vec![nq]); + w.cur[*q as usize] = nq; + } + _ => unreachable!("refused above"), + } + } + let cur = w.cur.clone(); + for (q, &done) in consumed.iter().enumerate() { + if !done { + w.qubit_op(1, None, vec![cur[q]], vec![]); + } + } + let mut region = Out::new(0, 4); + region.ptrs[0] = Ptr::U32s(vec![]); + region.ptrs[1] = Ptr::U32s(bits.into_iter().collect()); + region.ptrs[2] = Ptr::Structs { data_words: 1, ptr_count: 4, items: std::mem::take(&mut w.ops) }; + let mut func = Out::new(1, 3); + func.set_u16(0, 0); + func.set_u16(1, 0); + func.ptrs[0] = Ptr::Struct(Box::new(region)); + func.ptrs[1] = Ptr::Structs { + data_words: 0, + ptr_count: 2, + items: w + .values + .iter() + .map(|t| { + let mut v = Out::new(0, 2); + v.ptrs[0] = Ptr::Struct(Box::new(ty_out(t))); + v + }) + .collect(), + }; + let mut module = Out::new(2, 5); + module.set_u32(0, VERSION.0); + module.set_u32(2, VERSION.1); + module.set_u32(3, VERSION.2); + module.set_u16(2, 0); + module.ptrs[0] = Ptr::Structs { data_words: 1, ptr_count: 3, items: vec![func] }; + module.ptrs[1] = Ptr::Texts(vec!["main".into()]); + module.ptrs[3] = Ptr::Text("wai-quantum".into()); + module.ptrs[4] = Ptr::Text(env!("CARGO_PKG_VERSION").into()); + Ok((super::capnp::write(&module), notes)) +} diff --git a/wai/wai-quantum/src/engine/formats/mod.rs b/wai/wai-quantum/src/engine/formats/mod.rs new file mode 100644 index 000000000..179862e41 --- /dev/null +++ b/wai/wai-quantum/src/engine/formats/mod.rs @@ -0,0 +1,439 @@ +//! Programs in other formats, read to the engine's one program and written +//! from it: QIR (LLVM text, under its base and adaptive profiles, [`qir`]) +//! and jeff (the structured exchange format, a Cap'n Proto message, +//! [`jeff`]). +//! +//! A reader gives the program as OpenQASM 3, which the one reader every +//! surface uses ([`crate::qasm`]) then reads, so a program from any format +//! plans, runs, checks and draws as one written in OpenQASM. Every +//! construct a reader cannot hold exactly is refused with a code of the +//! reader's table (`Q700`–`Q721`, [`crate::qasm::diag`]) and where it stands +//! (a line of a QIR module; a function and an operation of a jeff module); +//! what it reads with something to know (a rotation at an arbitrary angle, a +//! custom gate read by its name) is a note with its code. +//! +//! [`convert`] carries a program from one format to another and checks the +//! result: what it writes is read back and held to the program by the +//! equivalence ladder ([`super::equiv`]), and a program read from another +//! format is written back to it and read again, the two readings held to +//! each other the same way. [`convert_json`] is the same as JSON, the form +//! every surface serves (the command line's `convert`, both MCP servers' +//! `convert_program`, the service's `POST /v1/convert`, the Studio's +//! `engine_convert`); a binary format travels in it as base64. +//! +//! [`expand`] lets a job carry its program in another format: +//! `{"source": {"format": "qir", "program": …}}` in place of `"qasm"` +//! (`"program_base64"` for jeff) is read to the job's `"qasm"` before +//! anything else reads the job, and the request keeps its source. + +pub mod jeff; +pub mod qir; +mod capnp; +mod emit; + +pub use qir::Profile; + +use crate::qasm::diag::{self, Diagnostic}; +use crate::qasm::Program; +use serde_json::{Value, json}; + +/// The format of a conversion's answer. +pub const FORMAT: &str = "wai.quantum.convert/1"; + +/// A format a program is read from or written to. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum Format { + /// OpenQASM 3 (and the OpenQASM 2 forms in common use). + Qasm, + /// QIR, as LLVM text. + Qir, + /// jeff, as its Cap'n Proto message. + Jeff, +} + +impl Format { + /// Every format, in order. + pub fn all() -> &'static [Format] { + &[Format::Qasm, Format::Qir, Format::Jeff] + } + + pub fn name(self) -> &'static str { + match self { + Format::Qasm => "qasm", + Format::Qir => "qir", + Format::Jeff => "jeff", + } + } + + /// A format by name: `qasm` (`openqasm`, `openqasm3`), `qir` (`ll`) or `jeff`. + pub fn parse(name: &str) -> Option { + match name.trim().to_ascii_lowercase().as_str() { + "qasm" | "qasm3" | "qasm2" | "openqasm" | "openqasm3" | "openqasm2" => Some(Format::Qasm), + "qir" | "ll" => Some(Format::Qir), + "jeff" => Some(Format::Jeff), + _ => None, + } + } + + /// Whether the format is binary (and travels in JSON as base64). + pub fn is_binary(self) -> bool { + matches!(self, Format::Jeff) + } + + /// The format a file's extension names: `.qasm`, `.ll` or `.qir`, `.jeff`. + pub fn from_extension(path: &str) -> Option { + let ext = path.rsplit_once('.')?.1.to_ascii_lowercase(); + match ext.as_str() { + "qasm" | "inc" => Some(Format::Qasm), + "ll" | "qir" => Some(Format::Qir), + "jeff" => Some(Format::Jeff), + _ => None, + } + } + + /// The format a file's bytes look like: jeff when they are not text, QIR + /// when they are LLVM text calling quantum intrinsics, OpenQASM otherwise. + pub fn sniff(bytes: &[u8]) -> Format { + match std::str::from_utf8(bytes) { + Err(_) => Format::Jeff, + Ok(t) if t.contains("__quantum__") && (t.contains("define ") || t.contains("declare ")) => Format::Qir, + Ok(t) if t.contains('\0') => Format::Jeff, + Ok(_) => Format::Qasm, + } + } + + /// The file extension it is written with. + pub fn extension(self) -> &'static str { + match self { + Format::Qasm => "qasm", + Format::Qir => "ll", + Format::Jeff => "jeff", + } + } +} + +/// A program read from a format. +#[derive(Clone, Debug, PartialEq)] +pub struct Imported { + /// The program, as OpenQASM 3. + pub qasm: String, + /// The reader's warnings and notes. + pub notes: Vec, + /// What was read: the format, its version and what it declared. + pub summary: Value, +} + +/// A program written to a format. +#[derive(Clone, Debug, PartialEq)] +pub struct Exported { + pub bytes: Vec, + /// What was left out, with its code. + pub notes: Vec, + /// The format, its version and profile. + pub summary: Value, +} + +/// A program read from `input` in `format`, as OpenQASM 3 that the engine's +/// reader reads. +pub fn read(format: Format, input: &[u8]) -> Result { + let text = || std::str::from_utf8(input).map_err(|_| Diagnostic::new(diag::FORMAT_UNREAD, None, format!("{} is text, and this is not UTF-8", format.name()))); + let imported = match format { + Format::Qasm => { + let t = text()?; + crate::qasm::read(t)?; + return Ok(Imported { qasm: t.to_string(), notes: Vec::new(), summary: json!({ "format": "qasm" }) }); + } + Format::Qir => { + let r = qir::read(text()?)?; + Imported { qasm: r.qasm, notes: r.notes, summary: r.summary } + } + Format::Jeff => { + let r = jeff::read(input)?; + Imported { qasm: r.qasm, notes: r.notes, summary: r.summary } + } + }; + // The text a reader writes reads; if it did not, that is this reader's + // fault, and it is refused rather than passed on. + crate::qasm::read(&imported.qasm).map_err(|d| Diagnostic::new(d.code, None, format!("the program read from {} does not read as OpenQASM: {}", format.name(), d.message)))?; + Ok(imported) +} + +/// `p` written in `format`. `profile` is QIR's (the base profile when the +/// program's measurements all come at the end, else the adaptive one, when +/// none is given); other formats take none. +pub fn write(format: Format, p: &Program, profile: Option) -> Result { + match format { + Format::Qasm => { + if p.dynamic.is_some() { + return Err(Diagnostic::new(diag::WRITE_CANNOT, None, "a program that branches is written as OpenQASM from its own text, not from its steps")); + } + let text = super::rewrite::render(&p.ops, p.n_qubits, &p.measured, p.n_bits); + Ok(Exported { bytes: text.into_bytes(), notes: Vec::new(), summary: json!({ "format": "qasm" }) }) + } + Format::Qir => { + let (text, notes) = qir::write(p, profile)?; + let profile = if text.contains("\"qir_profiles\"=\"adaptive_profile\"") { "adaptive" } else { "base" }; + Ok(Exported { bytes: text.into_bytes(), notes, summary: json!({ "format": "qir", "version": "2.0", "profile": profile }) }) + } + Format::Jeff => { + if profile.is_some() { + return Err(Diagnostic::new(diag::WRITE_CANNOT, None, "a profile is QIR's; jeff takes none")); + } + let (bytes, notes) = jeff::write(p)?; + let (a, b, c) = jeff::VERSION; + Ok(Exported { bytes, notes, summary: json!({ "format": "jeff", "version": format!("{a}.{b}.{c}") }) }) + } + } +} + +/// A conversion: the program in the format asked for, the program as read, +/// what each side noted, and the check. +#[derive(Clone, Debug, PartialEq)] +pub struct Converted { + pub from: Format, + pub to: Format, + pub output: Vec, + /// The program as OpenQASM 3, as read from the input. + pub qasm: String, + pub notes: Vec, + pub read: Value, + pub written: Value, + /// How the output was held to the input (the equivalence ladder's + /// verdict, and on what), or null when no check was asked. + pub check: Value, +} + +/// `input` in `from` carried to `to`. With `check`, what is written is read +/// back and held to the program read by the equivalence ladder; a program +/// read into OpenQASM is written back to its own format and read again, and +/// the two readings held to each other. +pub fn convert(from: Format, to: Format, input: &[u8], profile: Option, check: bool) -> Result { + if profile.is_some() && to != Format::Qir { + return Err(Diagnostic::new(diag::WRITE_CANNOT, None, format!("a profile is QIR's; {} takes none", to.name()))); + } + let imported = read(from, input)?; + let program = crate::qasm::read(&imported.qasm)?.program; + let mut notes = imported.notes.clone(); + let (output, written) = if to == Format::Qasm { + (imported.qasm.clone().into_bytes(), json!({ "format": "qasm" })) + } else { + // (The same format as read is written afresh from the program read.) + let e = write(to, &program, profile)?; + notes.extend(e.notes); + (e.bytes, e.summary) + }; + let check = if !check { + Value::Null + } else if to != Format::Qasm { + let back = read(to, &output)?; + let p2 = crate::qasm::read(&back.qasm)?.program; + json!({ "what": format!("the {} written, read back, against the program read", to.name()), "verdict": held(&program, &imported.qasm, &p2, &back.qasm) }) + } else if from != Format::Qasm { + let again = write(from, &program, None).and_then(|e| read(from, &e.bytes)); + match again { + Ok(a) => { + let p2 = crate::qasm::read(&a.qasm)?.program; + json!({ "what": format!("the program read, written back as {} and read again, against the first reading", from.name()), "verdict": held(&program, &imported.qasm, &p2, &a.qasm) }) + } + Err(d) => json!({ "what": format!("the program read, written back as {}", from.name()), "verdict": { "verdict": "unchecked", "how": "none", "why": d.to_string() } }), + } + } else { + json!({ "what": "OpenQASM to OpenQASM: the text is the program", "verdict": { "verdict": "equivalent", "how": "identity", "why": "the program is passed through as it is" } }) + }; + Ok(Converted { from, to, output, qasm: imported.qasm, notes, read: imported.summary, written, check }) +} + +/// The ladder's verdict on two programs. Programs that measure in the middle +/// or branch are held to each other step for step, and where their steps +/// differ (a condition on several bits written as one branch per bit, say), +/// by the probability every outcome has: each branch at its measurements in +/// the middle with its weight, and each leaf's final measurements, from the +/// debugger. +fn held(a: &Program, a_text: &str, b: &Program, b_text: &str) -> Value { + if a.dynamic.is_none() && b.dynamic.is_none() { + return super::equiv::equivalent_programs(a, b); + } + if a.n_qubits == b.n_qubits && a.ops == b.ops && a.dynamic == b.dynamic && a.measured == b.measured && a.n_bits == b.n_bits { + return json!({ "verdict": "equivalent", "how": "steps", "why": "the same gates, measurements, resets and branches, step for step" }); + } + if a.n_qubits != b.n_qubits || a.n_bits != b.n_bits { + return json!({ "verdict": "different", "how": "shape", "why": format!("one has {} qubits and {} bits, the other {} and {}", a.n_qubits, a.n_bits, b.n_qubits, b.n_bits) }); + } + match (distribution(a_text), distribution(b_text)) { + (Some(x), Some(y)) => { + let keys: std::collections::BTreeSet<&String> = x.keys().chain(y.keys()).collect(); + for k in keys { + let (p, q) = (x.get(k).copied().unwrap_or(0.0), y.get(k).copied().unwrap_or(0.0)); + if (p - q).abs() > 1e-9 { + return json!({ "verdict": "different", "how": "distribution", "why": format!("the outcome {k} has probability {p:.9} in one and {q:.9} in the other") }); + } + } + json!({ "verdict": "equivalent", "how": "distribution", "why": "every outcome has the same probability in both, from each branch at its measurements in the middle with its weight and each leaf's final measurements: the same results, which is evidence, not a proof that the circuits are the same" }) + } + _ => json!({ "verdict": "unchecked", "how": "none", "why": "the programs differ step by step, and their branches are more than the debugger enumerates here" }), + } +} + +/// The probability of every outcome of a program that branches, from the +/// debugger's branches: `None` when it cannot enumerate them all. +fn distribution(text: &str) -> Option> { + let v = super::debug::branches(&json!({ "qasm": text })).ok()?; + let mut d = std::collections::BTreeMap::new(); + let mut total = 0.0; + for leaf in v["leaves"].as_array()? { + let w = leaf["weight"].as_f64()?; + for f in leaf["final"].as_array()? { + let p = f["p"].as_f64()? * w; + total += p; + *d.entry(f["bits"].as_str()?.to_string()).or_insert(0.0) += p; + } + } + ((total - 1.0).abs() < 1e-9).then_some(d) +} + +// --------------------------------------------------------------------------- +// As JSON, for every surface +// --------------------------------------------------------------------------- + +const B64: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/// Bytes as standard base64, with padding. +pub fn encode_base64(bytes: &[u8]) -> String { + let mut s = String::with_capacity(bytes.len().div_ceil(3) * 4); + for c in bytes.chunks(3) { + let n = (u32::from(c[0]) << 16) | (u32::from(*c.get(1).unwrap_or(&0)) << 8) | u32::from(*c.get(2).unwrap_or(&0)); + s.push(B64[(n >> 18) as usize & 63] as char); + s.push(B64[(n >> 12) as usize & 63] as char); + s.push(if c.len() > 1 { B64[(n >> 6) as usize & 63] as char } else { '=' }); + s.push(if c.len() > 2 { B64[n as usize & 63] as char } else { '=' }); + } + s +} + +/// Standard base64 (padding optional, whitespace ignored) as bytes. +pub fn decode_base64(text: &str) -> Result, String> { + let mut out = Vec::with_capacity(text.len() / 4 * 3); + let (mut acc, mut bits) = (0u32, 0u32); + let mut ended = false; + for (i, ch) in text.bytes().enumerate() { + if ch.is_ascii_whitespace() { + continue; + } + if ch == b'=' { + ended = true; + continue; + } + if ended { + return Err(format!("base64 with data after its padding, at {i}")); + } + let v = B64.iter().position(|&b| b == ch).ok_or_else(|| format!("{:?} at {i} is not base64", ch as char))? as u32; + acc = (acc << 6) | v; + bits += 6; + if bits >= 8 { + bits -= 8; + out.push((acc >> bits) as u8); + acc &= (1 << bits) - 1; + } + } + if bits >= 6 { + return Err("base64 that ends partway through a byte".into()); + } + Ok(out) +} + +fn format_field(v: &Value, key: &str) -> Result, String> { + match v.get(key) { + None | Some(Value::Null) => Ok(None), + Some(Value::String(s)) => Format::parse(s).map(Some).ok_or_else(|| crate::engine::refusal::tag(crate::engine::refusal::BAD_FIELD, format!("{key}: {s} is not a format; the formats are {}", names()))), + Some(_) => Err(crate::engine::refusal::tag(crate::engine::refusal::BAD_FIELD, format!("{key} is a format's name: {}", names()))), + } +} + +fn names() -> String { + Format::all().iter().map(|f| f.name()).collect::>().join(", ") +} + +/// The program a request carries: `program` (text) or `program_base64` +/// (bytes, for a binary format). +fn program_bytes(v: &Value) -> Result, String> { + use crate::engine::refusal::{BAD_FIELD, tag}; + match (v.get("program").filter(|x| !x.is_null()), v.get("program_base64").filter(|x| !x.is_null())) { + (Some(Value::String(s)), None) => Ok(s.clone().into_bytes()), + (None, Some(Value::String(b))) => decode_base64(b).map_err(|e| tag(BAD_FIELD, format!("program_base64: {e}"))), + (Some(_), Some(_)) => Err(tag(BAD_FIELD, "give the program as \"program\" (text) or \"program_base64\" (bytes), not both")), + (None, None) => Err(tag(BAD_FIELD, "no program: give \"program\" (text) or \"program_base64\" (bytes, for jeff)")), + _ => Err(tag(BAD_FIELD, "\"program\" is text and \"program_base64\" is base64 text")), + } +} + +fn notes_json(notes: &[Diagnostic]) -> Value { + Value::Array(notes.iter().map(Diagnostic::to_json).collect()) +} + +/// A conversion as JSON: the request is `{"to", "program" | "program_base64", +/// "from"?, "profile"?, "check"?}` (`from` is found from the bytes when it is +/// not given; `check` is on unless false), and the answer `{"format", "from", +/// "to", "program" | "program_base64", "qasm", "notes", "read", "written", +/// "check"}`. A refusal is the reader's or writer's diagnostic, with its code +/// last (`… [Q704]`), or the job layer's for a request that does not read. +pub fn convert_json(req: &Value) -> Result { + use crate::engine::refusal::{BAD_FIELD, tag}; + if !req.is_object() { + return Err(tag(BAD_FIELD, "a conversion is a JSON object: {\"to\", \"program\" or \"program_base64\", \"from\", \"profile\", \"check\"}")); + } + let bytes = program_bytes(req)?; + let from = format_field(req, "from")?.unwrap_or_else(|| Format::sniff(&bytes)); + let to = format_field(req, "to")?.ok_or_else(|| tag(BAD_FIELD, format!("\"to\": the format to write, one of {}", names())))?; + let profile = match req.get("profile") { + None | Some(Value::Null) => None, + Some(Value::String(s)) => Some(Profile::parse(s).ok_or_else(|| tag(BAD_FIELD, format!("profile: {s} is not a QIR profile; the profiles are base and adaptive")))?), + Some(_) => return Err(tag(BAD_FIELD, "profile is base or adaptive")), + }; + let check = match req.get("check") { + None | Some(Value::Null) => true, + Some(Value::Bool(b)) => *b, + Some(_) => return Err(tag(BAD_FIELD, "check is true or false")), + }; + let c = convert(from, to, &bytes, profile, check).map_err(String::from)?; + let mut out = json!({ + "format": FORMAT, + "from": c.from.name(), + "to": c.to.name(), + "qasm": c.qasm, + "notes": notes_json(&c.notes), + "read": c.read, + "written": c.written, + "check": c.check, + }); + if c.to.is_binary() { + out["program_base64"] = json!(encode_base64(&c.output)); + } else { + out["program"] = json!(String::from_utf8(c.output).expect("a text format writes text")); + } + Ok(out) +} + +/// A job whose program is in another format, with `"qasm"` read from its +/// `"source"`: `{"source": {"format", "program" | "program_base64"}}`. A +/// request with no `source` is `None`, to be read as it is; one with both +/// `source` and `qasm` is refused, as is a source that does not read. +pub fn expand(req: &Value) -> Result, String> { + use crate::engine::refusal::{BAD_FIELD, NOT_A_JOB, tag}; + let Some(src) = req.get("source").filter(|s| !s.is_null()) else { return Ok(None) }; + if req.get("qasm").is_some_and(|q| !q.is_null()) { + return Err(tag(BAD_FIELD, "a job gives its program as \"qasm\" or as \"source\", not both")); + } + if !src.is_object() { + return Err(tag(BAD_FIELD, "\"source\" is {\"format\", \"program\" or \"program_base64\"}")); + } + let bytes = program_bytes(src)?; + let format = format_field(src, "format")?.unwrap_or_else(|| Format::sniff(&bytes)); + let imported = read(format, &bytes).map_err(|d| tag(NOT_A_JOB, format!("the source does not read as {}: {d}", format.name())))?; + let mut out = req.clone(); + out["qasm"] = json!(imported.qasm); + Ok(Some(out)) +} + +#[cfg(test)] +mod tests; diff --git a/wai/wai-quantum/src/engine/formats/qir.rs b/wai/wai-quantum/src/engine/formats/qir.rs new file mode 100644 index 000000000..7e550eef9 --- /dev/null +++ b/wai/wai-quantum/src/engine/formats/qir.rs @@ -0,0 +1,1619 @@ +//! QIR, read under its profiles and written from a program. +//! +//! **Reading** takes a module as LLVM text, QIR 1.x (typed pointers, +//! `%Qubit*`) or 2.x (opaque pointers, `ptr`), and holds it to the profile +//! its entry point names (`"qir_profiles"`): +//! +//! - **base**: gates, then measurements, then output records. A branch, a +//! classical instruction, reading a result, or a gate or reset on a qubit +//! after its measurement is refused with the code of what the profile +//! forbids ([`Q704`](crate::qasm::diag::PROFILE_FORBIDS)). +//! - **adaptive**: measurements anywhere, results read as booleans and +//! branched on (forward only), with the `i1` logic the profile allows +//! (`and`, `or`, `xor`, `icmp eq`/`ne`, `select`). An optional capability +//! the module's flags do not declare is refused as forbidden; one they +//! declare and this reader does not take (loops, `switch`, `phi`, +//! IR-defined functions, dynamic qubits or results, arrays, a value +//! computed from a measurement anywhere but a condition) is refused with +//! [`Q705`](crate::qasm::diag::CAPABILITY_NOT_TAKEN). Integer and float +//! computation the flags declare is computed on reading where its operands +//! are constants. +//! +//! The control flow (a tree of forward branches) is written as nested `if` +//! and `else`, each branch running to the block where its paths meet again. +//! Every refusal names its line. The module is read to an OpenQASM 3 +//! program for the one reader every surface uses: result `r` is bit `c[r]`, +//! qubit `i` is `q[i]`. +//! +//! **Angles** are read at their exact doubles (hex or decimal). A +//! Clifford+T module stays Clifford+T and reads to the bit-exact method; +//! an arbitrary-angle rotation reads with a warning +//! ([`Q709`](crate::qasm::diag::ARBITRARY_ANGLE)): the double-precision methods +//! take it, the bit-exact method refuses it. +//! +//! **Writing** gives QIR 2.0 with opaque pointers: the base profile for a +//! program whose measurements all come at the end, the adaptive profile for +//! one that measures in the middle, resets or branches (forward branches on +//! results, written with `__quantum__rt__read_result` and `br` alone). A +//! gate outside the intrinsics written is lowered exactly, up to a global +//! phase; Clifford+T gates are written as Clifford+T intrinsics, and every +//! angle as its exact hex double. + +use super::emit::{self, Circuit, Cond, Stmt}; +use crate::qasm::diag::{self, Diagnostic, Span}; +use crate::qasm::{Instr, Kind, Program}; +use serde_json::{Value, json}; +use std::collections::BTreeMap; + +/// The QIR profiles. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum Profile { + Base, + Adaptive, +} + +impl Profile { + pub fn name(self) -> &'static str { + match self { + Profile::Base => "base", + Profile::Adaptive => "adaptive", + } + } + pub fn parse(s: &str) -> Option { + match s.to_ascii_lowercase().trim_end_matches("_profile") { + "base" => Some(Profile::Base), + "adaptive" => Some(Profile::Adaptive), + _ => None, + } + } + fn attribute(self) -> &'static str { + match self { + Profile::Base => "base_profile", + Profile::Adaptive => "adaptive_profile", + } + } +} + +/// The intrinsics the reader takes, each `(name without its prefix, the +/// OpenQASM gate, parameters, qubits)`. `__body` and `__adj` forms are both +/// read where the gate has an inverse in the table. +const GATES: &[(&str, &str, usize, usize)] = &[ + ("x", "x", 0, 1), + ("y", "y", 0, 1), + ("z", "z", 0, 1), + ("h", "h", 0, 1), + ("s", "s", 0, 1), + ("t", "t", 0, 1), + ("sx", "sx", 0, 1), + ("rx", "rx", 1, 1), + ("ry", "ry", 1, 1), + ("rz", "rz", 1, 1), + ("r1", "p", 1, 1), + ("cnot", "cx", 0, 2), + ("cx", "cx", 0, 2), + ("cy", "cy", 0, 2), + ("cz", "cz", 0, 2), + ("swap", "swap", 0, 2), + ("ccx", "ccx", 0, 3), + ("ccz", "ccz", 0, 3), + ("rxx", "rxx", 1, 2), + ("ryy", "ryy", 1, 2), + ("rzz", "rzz", 1, 2), +]; + +/// What a module was read as. +#[derive(Clone, Debug, PartialEq)] +pub struct Reading { + /// The program, as OpenQASM 3. + pub qasm: String, + /// The warnings and notes, each with its code and its line in the module. + pub notes: Vec, + /// The profile, the version, the entry point, the qubits and results, the + /// capabilities the flags declare and the results recorded as output. + pub summary: Value, +} + +fn err(code: &'static str, line: usize, msg: impl Into) -> Diagnostic { + Diagnostic::new(code, (line > 0).then_some(Span { line: line as u32, column: 1, length: 0 }), msg) +} + +// --------------------------------------------------------------------------- +// The module's text +// --------------------------------------------------------------------------- + +/// A line without its comment, and its number. +#[derive(Clone, Debug)] +struct Line<'a> { + no: usize, + text: &'a str, +} + +/// `s` with a `;` comment removed (outside quotes). +fn strip_comment(s: &str) -> &str { + let mut quoted = false; + for (i, ch) in s.char_indices() { + match ch { + '"' => quoted = !quoted, + ';' if !quoted => return &s[..i], + _ => {} + } + } + s +} + +#[derive(Clone, Debug, Default)] +struct Func<'a> { + name: String, + line: usize, + attrs: Vec, + /// Its blocks, in order: (label, its lines). + blocks: Vec<(String, Vec>)>, +} + +#[derive(Default)] +struct Module<'a> { + defines: Vec>, + declares: BTreeMap>, + groups: BTreeMap)>>, + metadata: BTreeMap, + flags: Vec, + typed_pointers: bool, +} + +/// `#0`, `#1`, … in `s`. +fn group_refs(s: &str) -> Vec { + s.split_whitespace().filter(|t| t.starts_with('#') && t[1..].bytes().all(|b| b.is_ascii_digit()) && t.len() > 1).map(String::from).collect() +} + +/// The attributes in an attribute group's braces: `"name"` or `"name"="value"`, and bare words. +fn attributes(s: &str) -> Vec<(String, Option)> { + let mut out = Vec::new(); + let b = s.as_bytes(); + let mut i = 0; + while i < b.len() { + match b[i] { + b'"' => { + let end = s[i + 1..].find('"').map_or(b.len(), |e| i + 1 + e); + let name = s[i + 1..end].to_string(); + i = end + 1; + if i < b.len() && b[i] == b'=' && i + 1 < b.len() && b[i + 1] == b'"' { + let vend = s[i + 2..].find('"').map_or(b.len(), |e| i + 2 + e); + out.push((name, Some(s[i + 2..vend].to_string()))); + i = vend + 1; + } else { + out.push((name, None)); + } + } + c if c.is_ascii_alphabetic() => { + let end = s[i..].find(|c: char| c.is_whitespace() || c == '}').map_or(b.len(), |e| i + e); + out.push((s[i..end].to_string(), None)); + i = end; + } + _ => i += 1, + } + } + out +} + +fn parse_module(src: &str) -> Result, Diagnostic> { + let mut m = Module::default(); + let mut current: Option = None; + for (i, raw) in src.lines().enumerate() { + let no = i + 1; + let text = strip_comment(raw).trim(); + if text.is_empty() { + continue; + } + if let Some(f) = current.as_mut() { + if text == "}" { + m.defines.push(current.take().expect("a function")); + continue; + } + // A label: `name:` (possibly quoted), the rest a comment. + if let Some(label) = text.strip_suffix(':').filter(|l| !l.contains(' ') && !l.contains('=')) { + f.blocks.push((label.trim_matches('"').to_string(), Vec::new())); + continue; + } + if f.blocks.is_empty() { + // The entry block may have no label. + f.blocks.push(("".into(), Vec::new())); + } + f.blocks.last_mut().expect("a block").1.push(Line { no, text }); + continue; + } + if text.starts_with("define ") { + let at = text.find('@').ok_or_else(|| err(diag::FORMAT_UNREAD, no, "a `define` with no function name"))?; + let name: String = text[at + 1..].chars().take_while(|&c| c != '(').collect(); + let close = text.rfind(')').unwrap_or(text.len()); + let mut attrs = group_refs(&text[close..]); + attrs.extend(attributes(&text[close..]).into_iter().filter(|(n, _)| n == "entry_point").map(|(n, _)| n)); + if !text.ends_with('{') { + return Err(err(diag::FORMAT_UNREAD, no, "a `define` whose body does not open on its line")); + } + current = Some(Func { name: name.trim_matches('"').to_string(), line: no, attrs, blocks: Vec::new() }); + continue; + } + if text.starts_with("declare ") { + if let Some(at) = text.find('@') { + let name: String = text[at + 1..].chars().take_while(|&c| c != '(').collect(); + let close = text.rfind(')').unwrap_or(text.len()); + m.declares.insert(name, group_refs(&text[close..])); + } + continue; + } + if let Some(rest) = text.strip_prefix("attributes ") { + let (id, body) = rest.split_once('=').ok_or_else(|| err(diag::FORMAT_UNREAD, no, "an attribute group that does not read"))?; + let body = body.trim().trim_start_matches('{').trim_end_matches('}'); + m.groups.insert(id.trim().to_string(), attributes(body)); + continue; + } + if let Some(rest) = text.strip_prefix("!llvm.module.flags") { + m.flags = rest.split(['{', '}', ',', '=']).map(str::trim).filter(|t| t.starts_with('!') && t.len() > 1).map(String::from).collect(); + continue; + } + if text.starts_with('!') { + if let Some((id, body)) = text.split_once('=') { + m.metadata.insert(id.trim().to_string(), body.trim().to_string()); + } + continue; + } + if text.contains("%Qubit*") || text.contains("%Result*") || text.starts_with("%Qubit") || text.starts_with("%Result") { + m.typed_pointers = true; + } + // Globals (output labels), types, `source_filename`, `target …`: nothing to read. + } + if current.is_some() { + return Err(err(diag::FORMAT_UNREAD, src.lines().count(), "a function body that is not closed with `}`")); + } + Ok(m) +} + +/// A module flag: `!{i32 1, !"name", }`. +fn flag<'m>(m: &'m Module, name: &str) -> Option<&'m str> { + let want = format!("!\"{name}\""); + m.flags.iter().filter_map(|r| m.metadata.get(r)).find(|body| body.contains(&want)).and_then(|body| body.trim_start_matches('!').trim_matches(['{', '}']).split(',').nth(2).map(str::trim)) +} + +/// The strings of a metadata tuple a flag refers to (`!10 = !{!"i32", !"i64"}`). +fn flag_strings(m: &Module, name: &str) -> Option> { + let v = flag(m, name)?; + let body = if v.starts_with("!{") { v.to_string() } else { m.metadata.get(v)?.clone() }; + Some(body.trim_start_matches('!').trim_matches(['{', '}']).split(',').map(|s| s.trim().trim_start_matches('!').trim_matches('"').to_string()).filter(|s| !s.is_empty()).collect()) +} + +fn flag_bool(m: &Module, name: &str) -> Option { + flag(m, name).map(|v| matches!(v.split_whitespace().last(), Some("true" | "1"))) +} + +fn flag_int(m: &Module, name: &str) -> Option { + flag(m, name).and_then(|v| v.split_whitespace().last()).and_then(|v| match v { + "true" => Some(1), + "false" => Some(0), + n => n.parse().ok(), + }) +} + +// --------------------------------------------------------------------------- +// Instructions and their operands +// --------------------------------------------------------------------------- + +/// Splits `s` at commas outside brackets, parentheses and quotes. +fn split_top(s: &str) -> Vec<&str> { + let mut out = Vec::new(); + let (mut depth, mut quoted, mut start) = (0i32, false, 0); + for (i, ch) in s.char_indices() { + match ch { + '"' => quoted = !quoted, + '(' | '[' | '{' | '<' if !quoted => depth += 1, + ')' | ']' | '}' | '>' if !quoted => depth -= 1, + ',' if !quoted && depth == 0 => { + out.push(s[start..i].trim()); + start = i + 1; + } + _ => {} + } + } + let last = s[start..].trim(); + if !last.is_empty() { + out.push(last); + } + out +} + +/// The value of an argument written `TYPE [attributes] VALUE`. +fn arg_value(arg: &str) -> &str { + let toks: Vec<(usize, &str)> = arg.split_whitespace().scan(0usize, |pos, t| { + let at = arg[*pos..].find(t).map_or(*pos, |i| *pos + i); + *pos = at + t.len(); + Some((at, t)) + }).collect(); + let mut i = 1; // the type is the first token + while i < toks.len() { + let t = toks[i].1; + let value_start = t == "null" + || t == "true" + || t == "false" + || t == "undef" + || t == "poison" + || t.starts_with("inttoptr") + || t.starts_with("getelementptr") + || t.starts_with('%') + || t.starts_with('@') + || t.starts_with('-') + || t.starts_with(|c: char| c.is_ascii_digit()); + if value_start { + return arg[toks[i].0..].trim(); + } + if t == "align" { + i += 1; + } + i += 1; + } + "" +} + +/// A value an operand holds on reading. +#[derive(Clone, Debug, PartialEq)] +enum Val { + /// A boolean computed from measured results, with the measurement each + /// result held when it was read. + Bool(Cond, Vec<(u32, u32)>), + Int(i128), + Float(f64), + /// The result `One` (QIR 1.x's `__quantum__rt__result_get_one`). + One, +} + +/// A pointer operand's constant index: `null`, `inttoptr (i64 N to …)`. +fn pointer_index(v: &str) -> Option { + let v = v.trim(); + if v == "null" { + return Some(0); + } + let inner = v.strip_prefix("inttoptr")?.trim().trim_start_matches('(').trim_end_matches(')'); + let mut t = inner.split_whitespace(); + if t.next()? != "i64" { + return None; + } + t.next()?.parse().ok() +} + +/// A floating-point literal: decimal, or LLVM's exact hex form (`0x` and the +/// double's 64 bits). +fn float_literal(v: &str) -> Option { + let v = v.trim(); + if let Some(h) = v.strip_prefix("0x") { + if h.len() == 16 && h.bytes().all(|b| b.is_ascii_hexdigit()) { + return u64::from_str_radix(h, 16).ok().map(f64::from_bits); + } + return None; + } + v.parse::().ok().filter(|x| x.is_finite()) +} + +/// The intrinsic a call names, without its prefix, and which form. +enum Callee<'a> { + Qis(&'a str), + Rt(&'a str), + Other(&'a str), +} + +fn callee(name: &str) -> Callee<'_> { + if let Some(r) = name.strip_prefix("__quantum__qis__") { + Callee::Qis(r) + } else if let Some(r) = name.strip_prefix("__quantum__rt__") { + Callee::Rt(r) + } else { + Callee::Other(name) + } +} + +/// A call: `[%x =] [tail] call TYPE @name(ARGS) …`. +struct Call<'a> { + def: Option<&'a str>, + name: &'a str, + args: Vec<&'a str>, +} + +fn parse_call(text: &str) -> Option> { + let (def, rest) = match text.split_once('=') { + Some((d, r)) if d.trim().starts_with('%') && !d.contains('(') => (Some(d.trim()), r.trim()), + _ => (None, text), + }; + let rest = rest.trim_start_matches("tail ").trim_start_matches("notail ").trim_start_matches("musttail ").trim(); + let rest = rest.strip_prefix("call ")?; + let at = rest.find('@')?; + let after = &rest[at + 1..]; + let open = after.find('(')?; + let name = after[..open].trim_matches('"'); + // The arguments run to the matching parenthesis. + let mut depth = 0; + let mut end = None; + for (i, ch) in after[open..].char_indices() { + match ch { + '(' => depth += 1, + ')' => { + depth -= 1; + if depth == 0 { + end = Some(open + i); + break; + } + } + _ => {} + } + } + let args = split_top(&after[open + 1..end?]); + Some(Call { def, name, args }) +} + +// --------------------------------------------------------------------------- +// Reading +// --------------------------------------------------------------------------- + +/// What the profile and the module's flags allow. +#[derive(Clone, Debug)] +struct Allowed { + profile: Profile, + ints: Vec, + floats: Vec, + ir_functions: bool, + backwards: i64, + multi_target: bool, + multi_return: bool, + dyn_qubits: bool, + dyn_results: bool, + arrays: bool, +} + +/// The state along one path through the blocks. +#[derive(Clone, Debug, Default)] +struct Path { + env: BTreeMap, + /// How many times each result has been measured on this path. + gens: Vec, + /// Each qubit: measured (base profile), touched by a gate. + measured: Vec, + touched: Vec, +} + +struct Reader<'m, 'a> { + m: &'m Module<'a>, + f: &'m Func<'a>, + allowed: Allowed, + n_qubits: u32, + n_results: u32, + declared_qubits: bool, + declared_results: bool, + labels: BTreeMap<&'m str, usize>, + notes: Vec, + records: Vec, + arbitrary: usize, + emitted: usize, + visits: usize, + used_qubits: u32, + used_results: u32, +} + +/// The most statements the branches are written out to (a block reached by +/// many paths is written once per path). +const MAX_STATEMENTS: usize = crate::qasm::MAX_OPS; +/// The most blocks visited, over every path. +const MAX_VISITS: usize = 1 << 20; + +enum Term<'a> { + Ret, + Br(&'a str), + CondBr(&'a str, &'a str, &'a str), +} + +impl<'m, 'a> Reader<'m, 'a> { + fn forbidden(&self, line: usize, what: &str) -> Diagnostic { + err(diag::PROFILE_FORBIDS, line, format!("the {} profile forbids {what}", self.allowed.profile.name())) + } + + fn not_taken(&self, line: usize, what: &str) -> Diagnostic { + err(diag::CAPABILITY_NOT_TAKEN, line, format!("{what}: the module may use it, and this reader does not take it")) + } + + fn qubit(&mut self, v: &str, line: usize) -> Result { + let i = pointer_index(v).ok_or_else(|| { + if v.starts_with('%') { + if self.allowed.dyn_qubits { self.not_taken(line, "a qubit that is not a constant index (dynamic qubit management)") } else { err(diag::OPERAND, line, format!("the qubit {v} is not a constant index")) } + } else { + err(diag::OPERAND, line, format!("the qubit {v} is not a constant index")) + } + })?; + if self.declared_qubits && i >= u64::from(self.n_qubits) { + return Err(err(diag::OPERAND, line, format!("qubit {i} is past required_num_qubits={}", self.n_qubits))); + } + if i >= u64::from(crate::qasm::MAX_QUBITS) { + return Err(err(diag::OPERAND, line, format!("qubit {i}: more qubits than the reader takes"))); + } + self.used_qubits = self.used_qubits.max(i as u32 + 1); + Ok(i as u32) + } + + fn result(&mut self, v: &str, line: usize) -> Result { + let i = pointer_index(v).ok_or_else(|| { + if v.starts_with('%') && self.allowed.dyn_results { + self.not_taken(line, "a result that is not a constant index (dynamic result management)") + } else { + err(diag::OPERAND, line, format!("the result {v} is not a constant index")) + } + })?; + if self.declared_results && i >= u64::from(self.n_results) { + return Err(err(diag::OPERAND, line, format!("result {i} is past required_num_results={}", self.n_results))); + } + if i >= u64::from(crate::qasm::MAX_QUBITS) { + return Err(err(diag::OPERAND, line, format!("result {i}: more results than the reader takes"))); + } + self.used_results = self.used_results.max(i as u32 + 1); + Ok(i as u32) + } + + fn value(&self, v: &str, st: &Path, line: usize) -> Result { + let v = v.trim(); + if v.starts_with('%') { + return st.env.get(v).cloned().ok_or_else(|| err(diag::OPERAND, line, format!("{v} is not defined on every path that reaches this line, or holds what the reader does not keep (a pointer)"))); + } + Ok(match v { + "true" => Val::Bool(Cond::Const(true), vec![]), + "false" => Val::Bool(Cond::Const(false), vec![]), + _ => { + if let Ok(i) = v.parse::() { + Val::Int(i) + } else if let Some(f) = float_literal(v) { + Val::Float(f) + } else { + return Err(err(diag::OPERAND, line, format!("{v}: not a value the reader reads"))); + } + } + }) + } + + /// A gate's angle: a finite double known on reading. + fn angle(&mut self, arg: &str, st: &Path, line: usize) -> Result { + let v = arg_value(arg); + match self.value(v, st, line)? { + Val::Float(f) if f.is_finite() => Ok(f), + Val::Int(i) => Ok(i as f64), + _ => Err(err(diag::OPERAND, line, format!("the angle {v} is not a finite number known on reading"))), + } + } + + fn bool(&self, arg: &str, st: &Path, line: usize) -> Result<(Cond, Vec<(u32, u32)>), Diagnostic> { + match self.value(arg_value(arg), st, line)? { + Val::Bool(c, d) => Ok((c, d)), + Val::Int(i) => Ok((Cond::Const(i & 1 == 1), vec![])), + _ => Err(err(diag::OPERAND, line, format!("{arg}: not a boolean"))), + } + } + + /// One instruction of a block (not its terminator). + fn instruction(&mut self, l: &Line, st: &mut Path, out: &mut Vec) -> Result<(), Diagnostic> { + let t = l.text; + let line = l.no; + if let Some(call) = parse_call(t) { + return self.call(&call, line, st, out); + } + let Some((def, rhs)) = t.split_once('=').filter(|(d, _)| d.trim().starts_with('%')) else { + return Err(err(diag::INSTRUCTION_UNKNOWN, line, format!("`{t}`: an instruction the reader does not know"))); + }; + let def = def.trim().to_string(); + let rhs = rhs.trim(); + let op = rhs.split_whitespace().next().unwrap_or(""); + let is_base = self.allowed.profile == Profile::Base; + match op { + "phi" => return Err(if is_base { self.forbidden(line, "a `phi`") } else { self.not_taken(line, "a `phi` (a value that depends on the path taken)") }), + "alloca" | "load" | "store" | "getelementptr" | "extractvalue" | "insertvalue" => { + return Err(if self.allowed.arrays && !is_base { self.not_taken(line, &format!("`{op}` (arrays)")) } else { self.forbidden(line, &format!("`{op}`")) }); + } + _ => {} + } + if is_base { + return Err(self.forbidden(line, &format!("the classical instruction `{op}`"))); + } + let rest = rhs[op.len()..].trim(); + // The type is the first word after any flags. + let flags = ["nuw", "nsw", "exact", "fast", "nnan", "ninf", "nsz", "arcp", "contract", "afn", "reassoc", "disjoint"]; + let mut words = rest.split_whitespace().skip_while(|w| flags.contains(w)); + let (pred, ty) = if op == "icmp" || op == "fcmp" { + let p = words.next().unwrap_or(""); + (p, words.next().unwrap_or("")) + } else { + ("", words.next().unwrap_or("")) + }; + let after_ty = rest.find(ty).map_or("", |i| &rest[i + ty.len()..]).trim(); + let operands = split_top(after_ty); + let val = |r: &Self, s: &str| r.value(s.split(" to ").next().unwrap_or(s), st, line); + // `i1` logic: allowed in every adaptive program. + let bool_select = op == "select" && split_top(rest).get(1).is_some_and(|a| a.trim_start().starts_with("i1 ")); + if ty == "i1" && matches!(op, "and" | "or" | "xor" | "icmp") || bool_select { + let get = |r: &Self, s: &str| -> Result<(Cond, Vec<(u32, u32)>), Diagnostic> { + let s = s.trim().trim_start_matches("i1 ").trim(); + match r.value(s, st, line)? { + Val::Bool(c, d) => Ok((c, d)), + Val::Int(i) => Ok((Cond::Const(i & 1 == 1), vec![])), + _ => Err(err(diag::OPERAND, line, format!("{s}: not a boolean"))), + } + }; + let v = match op { + "select" => { + let parts = split_top(&rest[rest.find("i1").unwrap_or(0) + 2..]); + if parts.len() != 3 { + return Err(err(diag::FORMAT_UNREAD, line, "a `select` that does not read")); + } + let (c, mut d) = get(self, parts[0])?; + let (a, da) = get(self, parts[1].trim_start_matches("i1").trim())?; + let (b, db) = get(self, parts[2].trim_start_matches("i1").trim())?; + d.extend(da); + d.extend(db); + (Cond::or(Cond::and(c.clone(), a), Cond::and(Cond::not(c), b)), d) + } + _ => { + if operands.len() != 2 { + return Err(err(diag::FORMAT_UNREAD, line, format!("`{op}` with {} operands", operands.len()))); + } + let (a, mut d) = get(self, operands[0])?; + let (b, db) = get(self, operands[1])?; + d.extend(db); + let c = match (op, pred) { + ("and", _) => Cond::and(a, b), + ("or", _) => Cond::or(a, b), + ("xor", _) | ("icmp", "ne") => Cond::xor(a, b), + ("icmp", "eq") => Cond::not(Cond::xor(a, b)), + _ => return Err(self.not_taken(line, &format!("`icmp {pred}` on booleans"))), + }; + (c, d) + } + }; + if !emit::cond_fits(&v.0) { + return Err(self.not_taken(line, "a condition of more than 4096 terms")); + } + st.env.insert(def, Val::Bool(v.0, v.1)); + return Ok(()); + } + // Integer and float computation, where the flags declare its type. + let float_op = matches!(op, "fadd" | "fsub" | "fmul" | "fdiv" | "fneg" | "fcmp" | "fpext" | "fptrunc" | "sitofp" | "uitofp"); + let int_op = matches!(op, "add" | "sub" | "mul" | "udiv" | "sdiv" | "urem" | "srem" | "and" | "or" | "xor" | "shl" | "lshr" | "ashr" | "icmp" | "zext" | "sext" | "trunc" | "fptosi" | "fptoui"); + if !float_op && !int_op { + return Err(err(diag::INSTRUCTION_UNKNOWN, line, format!("`{op}`: an instruction the adaptive profile does not list"))); + } + let (declared, kind) = if float_op { (&self.allowed.floats, "floats") } else { (&self.allowed.ints, "integers") }; + let ty_name = ty.trim_end_matches(','); + if !declared.iter().any(|d| d == ty_name) { + return Err(self.forbidden(line, &format!("computation on {kind} of type {ty_name} (its module flags do not declare it)"))); + } + let vals: Vec = operands.iter().map(|o| val(self, o)).collect::>().map_err(|_| self.not_taken(line, "a classical value computed from a measurement, used anywhere but a condition"))?; + let f = |v: &Val| match v { + Val::Float(f) => Some(*f), + Val::Int(i) => Some(*i as f64), + _ => None, + }; + let i = |v: &Val| match v { + Val::Int(i) => Some(*i), + Val::Bool(Cond::Const(b), _) => Some(i128::from(*b)), + _ => None, + }; + let need = |o: Option| o.ok_or_else(|| self.not_taken(line, &format!("`{op}` on a value not known on reading"))); + let v = match op { + "fadd" | "fsub" | "fmul" | "fdiv" => { + let (Some(a), Some(b)) = (vals.first().and_then(f), vals.get(1).and_then(f)) else { return Err(need(None).unwrap_err()) }; + Val::Float(match op { + "fadd" => a + b, + "fsub" => a - b, + "fmul" => a * b, + _ => a / b, + }) + } + "fneg" => need(vals.first().and_then(f).map(|a| Val::Float(-a)))?, + "fpext" | "sitofp" | "uitofp" => need(vals.first().and_then(f).map(Val::Float))?, + "fptrunc" => need(vals.first().and_then(f).map(|a| Val::Float(f64::from(a as f32))))?, + "fptosi" | "fptoui" => need(vals.first().and_then(f).map(|a| Val::Int(a as i128)))?, + "zext" | "sext" | "trunc" => need(vals.first().and_then(i).map(Val::Int))?, + "icmp" => { + let (Some(a), Some(b)) = (vals.first().and_then(i), vals.get(1).and_then(i)) else { return Err(need(None).unwrap_err()) }; + let r = match pred { + "eq" => a == b, + "ne" => a != b, + "slt" | "ult" => a < b, + "sle" | "ule" => a <= b, + "sgt" | "ugt" => a > b, + "sge" | "uge" => a >= b, + _ => return Err(err(diag::FORMAT_UNREAD, line, format!("`icmp {pred}`"))), + }; + Val::Bool(Cond::Const(r), vec![]) + } + _ => { + let (Some(a), Some(b)) = (vals.first().and_then(i), vals.get(1).and_then(i)) else { return Err(need(None).unwrap_err()) }; + Val::Int(match op { + "add" => a.wrapping_add(b), + "sub" => a.wrapping_sub(b), + "mul" => a.wrapping_mul(b), + "udiv" | "sdiv" | "urem" | "srem" if b == 0 => return Err(err(diag::OPERAND, line, "division by zero")), + "udiv" | "sdiv" => a / b, + "urem" | "srem" => a % b, + "and" => a & b, + "or" => a | b, + "xor" => a ^ b, + "shl" => a.checked_shl(b as u32).unwrap_or(0), + _ => a.checked_shr(b as u32).unwrap_or(0), + }) + } + }; + st.env.insert(def, v); + Ok(()) + } + + fn call(&mut self, c: &Call, line: usize, st: &mut Path, out: &mut Vec) -> Result<(), Diagnostic> { + let base = self.allowed.profile == Profile::Base; + match callee(c.name) { + Callee::Qis(rest) => { + let (stem, form) = match rest.rsplit_once("__") { + Some((s, f)) => (s, f), + None => (rest, "body"), + }; + match stem { + "mz" | "m" | "mresetz" => { + if c.args.len() != 2 { + return Err(err(diag::FORMAT_UNREAD, line, format!("{} takes a qubit and a result", c.name))); + } + let q = self.qubit(arg_value(c.args[0]), line)?; + let r = self.result(arg_value(c.args[1]), line)?; + if base && *st.measured(q) { + return Err(self.forbidden(line, &format!("measuring qubit {q} twice"))); + } + *st.measured(q) = true; + *st.generation(r) += 1; + out.push(Stmt::Measure { qubit: q, bit: r }); + if stem == "mresetz" { + if base { + return Err(self.forbidden(line, "a reset after a measurement (`mresetz`)")); + } + out.push(Stmt::Reset(q)); + } + self.emitted += 1; + } + "reset" => { + let q = self.qubit(arg_value(c.args.first().copied().unwrap_or("")), line)?; + if base && (*st.touched(q) || *st.measured(q)) { + return Err(self.forbidden(line, &format!("a reset of qubit {q} after a gate or a measurement"))); + } + if !base || *st.touched(q) { + out.push(Stmt::Reset(q)); + } + *st.measured(q) = false; + self.emitted += 1; + } + "read_result" => return self.read_result(c, line, st), + "barrier" => { + self.notes.push(err(diag::WRITE_DROPPED, line, "a barrier changes nothing computed: left out")); + } + _ => { + let Some(&(_, name, n_params, n_qubits)) = GATES.iter().find(|g| g.0 == stem) else { + return Err(err(diag::INSTRUCTION_UNKNOWN, line, format!("{}: a quantum intrinsic the reader does not know; it reads {}", c.name, GATES.iter().map(|g| g.0).collect::>().join(", ")))); + }; + let inv = match form { + "body" => false, + "adj" => true, + "ctl" | "ctladj" => return Err(self.not_taken(line, &format!("{}: a controlled form, whose controls come in an array", c.name))), + f => return Err(err(diag::INSTRUCTION_UNKNOWN, line, format!("{}: the form `{f}` is not one the reader knows", c.name))), + }; + if c.args.len() != n_params + n_qubits { + return Err(err(diag::FORMAT_UNREAD, line, format!("{} takes {} arguments, not {}", c.name, n_params + n_qubits, c.args.len()))); + } + let mut params = Vec::new(); + for a in &c.args[..n_params] { + params.push(self.angle(a, st, line)?); + } + let mut qubits = Vec::new(); + for a in &c.args[n_params..] { + let q = self.qubit(arg_value(a), line)?; + if qubits.contains(&q) { + return Err(err(diag::OPERAND, line, format!("{}: qubit {q} named twice", c.name))); + } + if base && *st.measured(q) { + return Err(self.forbidden(line, &format!("a gate on qubit {q} after its measurement"))); + } + qubits.push(q); + } + for &q in &qubits { + *st.touched(q) = true; + } + for &p in ¶ms { + if !is_dyadic(p) { + self.arbitrary += 1; + } + } + let inv = inv && !matches!(name, "x" | "y" | "z" | "h" | "cx" | "cy" | "cz" | "swap" | "ccx" | "ccz"); + let stmts = portable(name, ¶ms, &qubits, inv); + self.emitted += stmts.len(); + out.extend(stmts); + } + } + } + Callee::Rt(rest) => match rest { + "initialize" => {} + "read_result" => return self.read_result(c, line, st), + "result_get_one" => { + if base { + return Err(self.forbidden(line, "reading a result")); + } + if let Some(d) = c.def { + st.env.insert(d.to_string(), Val::One); + } + } + "result_get_zero" => return Err(self.not_taken(line, "__quantum__rt__result_get_zero")), + "result_equal" => { + if base { + return Err(self.forbidden(line, "comparing results")); + } + let vals: Vec<&str> = c.args.iter().map(|a| arg_value(a)).collect(); + let (r, one) = match vals.as_slice() { + [a, b] if st.env.get(*b) == Some(&Val::One) => (*a, true), + [a, b] if st.env.get(*a) == Some(&Val::One) => (*b, true), + _ => ("", false), + }; + if !one { + return Err(self.not_taken(line, "__quantum__rt__result_equal between two measured results")); + } + let r = self.result(r, line)?; + if let Some(d) = c.def { + let g = *st.generation(r); + st.env.insert(d.to_string(), Val::Bool(Cond::Bit(r), vec![(r, g)])); + } + } + "result_record_output" => { + let r = self.result(arg_value(c.args.first().copied().unwrap_or("")), line)?; + self.records.push(r); + } + "tuple_record_output" | "array_record_output" => {} + "bool_record_output" | "int_record_output" | "double_record_output" | "float_record_output" => { + self.notes.push(err(diag::WRITE_DROPPED, line, format!("{}: a classical value recorded as output is not carried; the program's bits are its results", c.name))); + } + "qubit_allocate" | "qubit_release" | "qubit_array_allocate" | "qubit_array_release" | "result_allocate" | "result_release" | "result_array_allocate" | "result_array_release" | "result_array_record_output" => { + let declared = if rest.starts_with("qubit") { self.allowed.dyn_qubits } else if rest.contains("array") { self.allowed.arrays } else { self.allowed.dyn_results }; + return Err(if declared && !base { self.not_taken(line, &format!("{} (dynamic allocation)", c.name)) } else { self.forbidden(line, &format!("{} (its module flags do not declare dynamic management)", c.name)) }); + } + _ => return Err(err(diag::INSTRUCTION_UNKNOWN, line, format!("{}: a runtime function the reader does not take", c.name))), + }, + Callee::Other(name) => { + let defined = self.m.defines.iter().any(|f| f.name == name); + return Err(if defined { + if self.allowed.ir_functions && !base { self.not_taken(line, &format!("a call of the IR-defined function @{name}")) } else { self.forbidden(line, &format!("a call of the IR-defined function @{name} (its module flags do not declare IR functions)")) } + } else { + err(diag::INSTRUCTION_UNKNOWN, line, format!("@{name}: a function the reader does not know")) + }); + } + } + Ok(()) + } + + fn read_result(&mut self, c: &Call, line: usize, st: &mut Path) -> Result<(), Diagnostic> { + if self.allowed.profile == Profile::Base { + return Err(self.forbidden(line, "reading a result")); + } + let r = self.result(arg_value(c.args.first().copied().unwrap_or("")), line)?; + let g = *st.generation(r); + if let Some(d) = c.def { + st.env.insert(d.to_string(), Val::Bool(Cond::Bit(r), vec![(r, g)])); + } + Ok(()) + } + + fn terminator<'b>(&self, b: usize) -> Result<(Term<'b>, usize), Diagnostic> + where + 'a: 'b, + 'm: 'b, + { + let f: &'m Func<'a> = self.f; + let (label, lines) = &f.blocks[b]; + let Some(last) = lines.last() else { + return Err(err(diag::FORMAT_UNREAD, self.f.line, format!("the block {label} is empty"))); + }; + let t: &'b str = last.text; + let line = last.no; + let label_of = |s: &'b str| -> &'b str { s.trim().trim_start_matches("label").trim().trim_start_matches('%').trim_matches('"') }; + if t.starts_with("ret") { + return Ok((Term::Ret, line)); + } + if let Some(r) = t.strip_prefix("br ") { + let parts = split_top(r); + return match parts.as_slice() { + [l] => Ok((Term::Br(label_of(l)), line)), + [c, a, b] => Ok((Term::CondBr(c.trim(), label_of(a), label_of(b)), line)), + _ => Err(err(diag::FORMAT_UNREAD, line, "a `br` that does not read")), + }; + } + if t.starts_with("switch ") { + return Err(if self.allowed.multi_target && self.allowed.profile == Profile::Adaptive { self.not_taken(line, "`switch` (multiple-target branching)") } else { self.forbidden(line, "`switch` (its module flags do not declare multiple-target branching)") }); + } + if t.starts_with("unreachable") || t.starts_with("indirectbr") || t.starts_with("invoke") { + return Err(err(diag::INSTRUCTION_UNKNOWN, line, format!("`{t}`: a terminator the reader does not take"))); + } + Err(err(diag::FORMAT_UNREAD, line, format!("the block {label} does not end with `br` or `ret`"))) + } + + fn block(&self, label: &str, line: usize) -> Result { + self.labels.get(label).copied().ok_or_else(|| err(diag::FORMAT_UNREAD, line, format!("a branch to %{label}, which is not a block of the entry point"))) + } + + /// Writes the blocks from `b` until `stop` (or a return) into `out`. + fn emit_seq(&mut self, mut b: usize, stop: usize, ipdom: &[usize], st: &mut Path, out: &mut Vec) -> Result<(), Diagnostic> { + let exit = self.f.blocks.len(); + loop { + if b == stop { + return Ok(()); + } + self.visits += 1; + if self.visits > MAX_VISITS || self.emitted > MAX_STATEMENTS { + return Err(self.not_taken(self.f.blocks[b].1.first().map_or(self.f.line, |l| l.no), "branches that would be written out to more statements than the reader holds")); + } + let lines = self.f.blocks[b].1.clone(); + for l in &lines[..lines.len() - 1] { + self.instruction(l, st, out)?; + } + let (term, line) = self.terminator(b)?; + match term { + Term::Ret => return Ok(()), + Term::Br(l) => b = self.block(l, line)?, + Term::CondBr(v, t, f) => { + if self.allowed.profile == Profile::Base { + return Err(self.forbidden(line, "a conditional branch")); + } + let (cond, deps) = self.bool(v, st, line)?; + for (r, g) in deps { + if *st.generation(r) != g { + return Err(err(diag::STALE_READ, line, format!("{v} reads result {r}, which is measured again before this branch: the branch would read the later measurement"))); + } + } + let (tb, fb) = (self.block(t, line)?, self.block(f, line)?); + let j = ipdom[b]; + match cond { + Cond::Const(c) => { + b = if c { tb } else { fb }; + continue; + } + cond => { + let (mut st_t, mut st_f) = (st.clone(), st.clone()); + let (mut then, mut other) = (Vec::new(), Vec::new()); + self.emit_seq(tb, j, ipdom, &mut st_t, &mut then)?; + self.emit_seq(fb, j, ipdom, &mut st_f, &mut other)?; + *st = merge(st_t, st_f); + if then.is_empty() && other.is_empty() { + } else if then.is_empty() { + out.push(Stmt::If { cond: Cond::not(cond), then: other, other: Vec::new() }); + } else { + out.push(Stmt::If { cond, then, other }); + } + self.emitted += 1; + if j == exit { + return Ok(()); + } + b = j; + } + } + } + } + } + } +} + +impl Path { + fn grow(&mut self, n: usize) { + if self.gens.len() < n { + self.gens.resize(n, 0); + self.measured.resize(n, false); + self.touched.resize(n, false); + } + } + fn generation(&mut self, r: u32) -> &mut u32 { + self.grow(r as usize + 1); + &mut self.gens[r as usize] + } + fn measured(&mut self, q: u32) -> &mut bool { + self.grow(q as usize + 1); + &mut self.measured[q as usize] + } + fn touched(&mut self, q: u32) -> &mut bool { + self.grow(q as usize + 1); + &mut self.touched[q as usize] + } +} + +/// A gate as statements of the standard library alone, so the program reads +/// in any OpenQASM 3 reader: `ccz` under two controls, and the two-qubit +/// Pauli rotations by their exact basis changes and parity. +pub(super) fn portable(name: &'static str, params: &[f64], q: &[u32], inv: bool) -> Vec { + let g = |n: &'static str, p: Vec, qs: Vec| Stmt::gate(n, p, qs); + match name { + "ccz" => vec![Stmt::Gate { name: "z", params: vec![], controls: vec![q[0], q[1]], qubits: vec![q[2]], inv: false, pow: 1 }], + "rxx" | "ryy" | "rzz" => { + let t = if inv { -params[0] } else { params[0] }; + let (a, b) = (q[0], q[1]); + let (pre, post): (Vec, Vec) = match name { + "rxx" => (vec![g("h", vec![], vec![a]), g("h", vec![], vec![b])], vec![g("h", vec![], vec![a]), g("h", vec![], vec![b])]), + "ryy" => ( + vec![g("sdg", vec![], vec![a]), g("h", vec![], vec![a]), g("sdg", vec![], vec![b]), g("h", vec![], vec![b])], + vec![g("h", vec![], vec![a]), g("s", vec![], vec![a]), g("h", vec![], vec![b]), g("s", vec![], vec![b])], + ), + _ => (vec![], vec![]), + }; + let mut v = pre; + v.push(g("cx", vec![], vec![a, b])); + v.push(g("rz", vec![t], vec![b])); + v.push(g("cx", vec![], vec![a, b])); + v.extend(post); + v + } + _ => vec![Stmt::Gate { name, params: params.to_vec(), controls: Vec::new(), qubits: q.to_vec(), inv, pow: 1 }], + } +} + +/// Two paths' states where they meet: what both hold, and a result measured +/// differently on the two counted as measured again. +fn merge(mut a: Path, mut b: Path) -> Path { + let n = a.gens.len().max(b.gens.len()); + a.grow(n); + b.grow(n); + let env = a.env.into_iter().filter(|(k, v)| b.env.get(k) == Some(v)).collect(); + let gens = a.gens.iter().zip(&b.gens).map(|(&x, &y)| if x == y { x } else { x.max(y) + 1 }).collect(); + let measured = a.measured.iter().zip(&b.measured).map(|(&x, &y)| x || y).collect(); + let touched = a.touched.iter().zip(&b.touched).map(|(&x, &y)| x || y).collect(); + Path { env, gens, measured, touched } +} + +/// Whether `theta` is a dyadic multiple of 2π the bit-exact method takes. +fn is_dyadic(theta: f64) -> bool { + crate::qasm::dyadic(theta).is_some() +} + +/// Reads a QIR module (LLVM text) under its profile. +pub fn read(src: &str) -> Result { + if src.len() > 64 << 20 { + return Err(err(diag::FORMAT_UNREAD, 0, "a module larger than the 64 MiB the reader takes")); + } + let m = parse_module(src)?; + let mut notes = Vec::new(); + let entries: Vec<&Func> = m.defines.iter().filter(|f| f.attrs.iter().any(|a| a == "entry_point" || m.groups.get(a).is_some_and(|g| g.iter().any(|(n, _)| n == "entry_point")))).collect(); + let f = match entries.as_slice() { + [f] => *f, + [] if m.defines.len() == 1 => { + notes.push(err(diag::FORMAT_INCOMPLETE, m.defines[0].line, format!("@{} has no `entry_point` attribute; read as the entry point, the module's only function", m.defines[0].name))); + &m.defines[0] + } + [] => return Err(err(diag::FORMAT_UNREAD, 0, "no function has the `entry_point` attribute")), + _ => return Err(err(diag::FORMAT_UNREAD, entries[1].line, "more than one entry point")), + }; + let attrs: Vec<(String, Option)> = f.attrs.iter().filter_map(|a| m.groups.get(a)).flatten().cloned().collect(); + let attr = |name: &str| attrs.iter().find(|(n, _)| n == name).and_then(|(_, v)| v.clone()); + // The version. + let major = flag_int(&m, "qir_major_version"); + let minor = flag_int(&m, "qir_minor_version"); + match major { + Some(1 | 2) => {} + Some(v) => return Err(err(diag::FORMAT_VERSION, 0, format!("qir_major_version {v}: the reader takes QIR 1.x and 2.x"))), + None => notes.push(err(diag::FORMAT_INCOMPLETE, 0, "no qir_major_version module flag: read as QIR 2.x")), + } + // The profile. + let profile = match attr("qir_profiles") { + Some(p) => { + let names: Vec<&str> = p.split(',').map(str::trim).collect(); + if names.contains(&"adaptive_profile") { + Profile::Adaptive + } else if names.contains(&"base_profile") { + Profile::Base + } else { + return Err(err(diag::PROFILE_UNKNOWN, f.line, format!("qir_profiles=\"{p}\": the reader takes base_profile and adaptive_profile"))); + } + } + None => { + // The profile the module's instructions need. + let branches = f.blocks.iter().any(|(_, ls)| ls.iter().any(|l| l.text.contains("read_result") || l.text.starts_with("br i1"))); + let p = if branches { Profile::Adaptive } else { Profile::Base }; + notes.push(err(diag::FORMAT_INCOMPLETE, f.line, format!("@{} names no qir_profiles: read under the {} profile, which its instructions need", f.name, p.name()))); + p + } + }; + let allowed = Allowed { + profile, + ints: flag_strings(&m, "int_computations").unwrap_or_default(), + floats: flag_strings(&m, "float_computations").unwrap_or_default(), + ir_functions: flag_bool(&m, "ir_functions").unwrap_or(false), + backwards: flag_int(&m, "backwards_branching").unwrap_or(0), + multi_target: flag_bool(&m, "multiple_target_branching").unwrap_or(false), + multi_return: flag_bool(&m, "multiple_return_points").unwrap_or(false), + dyn_qubits: flag_bool(&m, "dynamic_qubit_management").unwrap_or(false), + dyn_results: flag_bool(&m, "dynamic_result_management").unwrap_or(false), + arrays: flag_bool(&m, "arrays").unwrap_or(false), + }; + if profile == Profile::Base && (allowed.dyn_qubits || allowed.dyn_results) { + return Err(err(diag::PROFILE_FORBIDS, 0, "the base profile forbids dynamic qubit or result management, which the module flags declare")); + } + let count = |name: &str| -> Result, Diagnostic> { + match attr(name) { + None => Ok(None), + Some(v) => v.parse::().ok().filter(|&n| n <= u64::from(crate::qasm::MAX_QUBITS)).map(|n| Some(n as u32)).ok_or_else(|| err(diag::OPERAND, f.line, format!("{name}=\"{v}\": not a count the reader takes (at most {})", crate::qasm::MAX_QUBITS))), + } + }; + let (nq, nr) = (count("required_num_qubits")?, count("required_num_results")?); + for (name, v) in [("required_num_qubits", nq), ("required_num_results", nr)] { + if v.is_none() && !(allowed.dyn_qubits || allowed.dyn_results) { + notes.push(err(diag::FORMAT_INCOMPLETE, f.line, format!("@{} gives no {name}: the count is the highest index used, plus one", f.name))); + } + } + if f.blocks.is_empty() { + return Err(err(diag::FORMAT_UNREAD, f.line, format!("@{} has no blocks", f.name))); + } + let labels: BTreeMap<&str, usize> = f.blocks.iter().enumerate().map(|(i, (l, _))| (l.as_str(), i)).collect(); + + let mut r = Reader { + m: &m, + f, + allowed: allowed.clone(), + n_qubits: nq.unwrap_or(0), + n_results: nr.unwrap_or(0), + declared_qubits: nq.is_some(), + declared_results: nr.is_some(), + labels, + notes: Vec::new(), + records: Vec::new(), + arbitrary: 0, + emitted: 0, + visits: 0, + used_qubits: 0, + used_results: 0, + }; + + // The control-flow graph: forward only, unless loops are declared (and + // then this reader does not take them). + let exit = f.blocks.len(); + let mut succ: Vec> = Vec::with_capacity(exit); + let mut rets = 0; + for b in 0..exit { + let (t, line) = r.terminator(b)?; + succ.push(match t { + Term::Ret => { + rets += 1; + vec![exit] + } + Term::Br(l) => vec![r.block(l, line)?], + Term::CondBr(_, a, c) => vec![r.block(a, line)?, r.block(c, line)?], + }); + } + // Depth-first from the entry block: a back edge is a loop. + let mut state = vec![0u8; exit]; + let mut order = Vec::new(); + let mut stack: Vec<(usize, usize)> = vec![(0, 0)]; + state[0] = 1; + while let Some(&mut (n, ref mut k)) = stack.last_mut() { + if *k < succ[n].len() { + let s = succ[n][*k]; + *k += 1; + if s == exit { + continue; + } + match state[s] { + 0 => { + state[s] = 1; + stack.push((s, 0)); + } + 1 => { + let line = f.blocks[n].1.last().map_or(f.line, |l| l.no); + return Err(if allowed.backwards > 0 && profile == Profile::Adaptive { r.not_taken(line, "a loop (backwards branching)") } else { r.forbidden(line, "a loop (its module flags do not declare backwards branching)") }); + } + _ => {} + } + } else { + state[n] = 2; + order.push(n); + stack.pop(); + } + } + let reachable_rets = order.iter().filter(|&&n| succ[n].contains(&exit)).count(); + if reachable_rets > 1 && !(allowed.multi_return && profile == Profile::Adaptive) { + let line = order.iter().filter(|&&n| succ[n].contains(&exit)).map(|&n| f.blocks[n].1.last().map_or(f.line, |l| l.no)).max().unwrap_or(f.line); + return Err(r.forbidden(line, "more than one return (its module flags do not declare multiple return points)")); + } + let _ = rets; + // Immediate post-dominators, in reverse topological order (`order` is a post-order). + let mut ipdom = vec![usize::MAX; exit + 1]; + let mut depth = vec![0usize; exit + 1]; + ipdom[exit] = exit; + for &n in &order { + let lca = |mut a: usize, mut b: usize, ipdom: &[usize], depth: &[usize]| { + while a != b { + if depth[a] >= depth[b] && a != exit { + a = ipdom[a]; + } else { + b = ipdom[b]; + } + } + a + }; + let mut x = succ[n][0]; + for &s in &succ[n][1..] { + x = lca(x, s, &ipdom, &depth); + } + ipdom[n] = x; + depth[n] = depth[x] + 1; + } + + let mut st = Path::default(); + st.grow(r.n_qubits.max(r.n_results) as usize); + let mut body = Vec::new(); + r.emit_seq(0, exit, &ipdom, &mut st, &mut body)?; + + let n_qubits = if r.declared_qubits { r.n_qubits } else { r.used_qubits }; + let n_results = if r.declared_results { r.n_results } else { r.used_results }; + if n_qubits == 0 { + return Err(err(diag::NO_QUBITS, f.line, format!("@{} uses no qubits", f.name))); + } + if r.arbitrary > 0 { + r.notes.push(err(diag::ARBITRARY_ANGLE, 0, format!("{} rotation{} at angles that are not dyadic multiples of 2π: the double-precision methods take them; the bit-exact method refuses them (Q500)", r.arbitrary, if r.arbitrary == 1 { "" } else { "s" }))); + } + notes.extend(std::mem::take(&mut r.notes)); + let circuit = Circuit { n_qubits, n_bits: n_results, body }; + let version = match (major, minor) { + (Some(a), Some(b)) => format!("{a}.{b}"), + (Some(a), None) => format!("{a}"), + _ => "unstated".into(), + }; + let qasm = circuit.to_qasm(&format!("Read from QIR {version}, {} profile, entry point @{}: qubit i is q[i], result r is c[r].", profile.name(), f.name)); + let mut caps = Vec::new(); + if !allowed.ints.is_empty() { + caps.push(json!({ "int_computations": allowed.ints })); + } + if !allowed.floats.is_empty() { + caps.push(json!({ "float_computations": allowed.floats })); + } + for (name, on) in [("ir_functions", allowed.ir_functions), ("multiple_target_branching", allowed.multi_target), ("multiple_return_points", allowed.multi_return), ("dynamic_qubit_management", allowed.dyn_qubits), ("dynamic_result_management", allowed.dyn_results), ("arrays", allowed.arrays)] { + if on { + caps.push(json!(name)); + } + } + if allowed.backwards > 0 { + caps.push(json!({ "backwards_branching": allowed.backwards })); + } + let summary = json!({ + "format": "qir", + "version": version, + "profile": profile.name(), + "entry_point": f.name, + "qubits": n_qubits, + "results": n_results, + "capabilities": caps, + "recorded": r.records, + "typed_pointers": m.typed_pointers, + "arbitrary_angles": r.arbitrary, + }); + Ok(Reading { qasm, notes, summary }) +} + +// --------------------------------------------------------------------------- +// Writing +// --------------------------------------------------------------------------- + +/// The intrinsic calls a program's op is written as: `(intrinsic, its +/// angles, its qubits)`. Exact, up to a global phase where the op has no +/// intrinsic of its own. +fn lower(kind: Kind, q: &[u32], p: &[f64]) -> Vec<(&'static str, Vec, Vec)> { + use std::f64::consts::FRAC_PI_4; + let one = |n: &'static str, a: u32| (n, vec![], vec![a]); + let rot = |n: &'static str, t: f64, a: u32| (n, vec![t], vec![a]); + let cx = |c: u32, t: u32| ("cx", vec![], vec![c, t]); + match kind { + Kind::Id => vec![], + Kind::X => vec![one("x", q[0])], + Kind::Y => vec![one("y", q[0])], + Kind::Z => vec![one("z", q[0])], + Kind::H => vec![one("h", q[0])], + Kind::S => vec![one("s", q[0])], + Kind::Sdg => vec![one("s__adj", q[0])], + Kind::T => vec![one("t", q[0])], + Kind::Tdg => vec![one("t__adj", q[0])], + Kind::Sx => vec![one("h", q[0]), one("s", q[0]), one("h", q[0])], + Kind::Sxdg => vec![one("h", q[0]), one("s__adj", q[0]), one("h", q[0])], + Kind::Rx => vec![rot("rx", p[0], q[0])], + Kind::Ry => vec![rot("ry", p[0], q[0])], + Kind::Rz => vec![rot("rz", p[0], q[0])], + Kind::P => { + // A phase at a multiple of π/4 is written in Clifford+T gates, so a + // Clifford+T program stays one. + let k = (p[0] / FRAC_PI_4).round(); + if (k * FRAC_PI_4).to_bits() == p[0].to_bits() || p[0] == 0.0 { + let k = (k as i64).rem_euclid(8); + let mut v = Vec::new(); + if k & 4 != 0 { + v.push(one("z", q[0])); + } + if k & 2 != 0 { + v.push(one("s", q[0])); + } + if k & 1 != 0 { + v.push(one("t", q[0])); + } + v + } else { + vec![rot("rz", p[0], q[0])] + } + } + Kind::U => vec![rot("rz", p[2], q[0]), rot("ry", p[0], q[0]), rot("rz", p[1], q[0])], + Kind::Cx => vec![cx(q[0], q[1])], + Kind::Cy => vec![one("s__adj", q[1]), cx(q[0], q[1]), one("s", q[1])], + Kind::Cz => vec![("cz", vec![], vec![q[0], q[1]])], + Kind::Ch => vec![one("h", q[1]), one("s__adj", q[1]), cx(q[0], q[1]), one("h", q[1]), one("t", q[1]), cx(q[0], q[1]), one("t", q[1]), one("h", q[1]), one("s", q[1]), one("x", q[1]), one("s", q[0])], + Kind::Cp => { + let mut v = lower(Kind::P, &[q[0]], &[p[0] / 2.0]); + v.push(cx(q[0], q[1])); + v.extend(lower(Kind::P, &[q[1]], &[-p[0] / 2.0])); + v.push(cx(q[0], q[1])); + v.extend(lower(Kind::P, &[q[1]], &[p[0] / 2.0])); + v + } + Kind::Crz => vec![rot("rz", p[0] / 2.0, q[1]), cx(q[0], q[1]), rot("rz", -p[0] / 2.0, q[1]), cx(q[0], q[1])], + Kind::Cry => vec![rot("ry", p[0] / 2.0, q[1]), cx(q[0], q[1]), rot("ry", -p[0] / 2.0, q[1]), cx(q[0], q[1])], + Kind::Crx => { + let mut v = vec![one("h", q[1])]; + v.extend(lower(Kind::Crz, q, p)); + v.push(one("h", q[1])); + v + } + Kind::Cu => { + // cu(θ, φ, λ, γ) = controlled e^{iγ} U(θ, φ, λ), as the standard library writes it. + let (t, ph, l, g) = (p[0], p[1], p[2], p[3]); + let mut v = lower(Kind::P, &[q[0]], &[g]); + v.extend(lower(Kind::P, &[q[0]], &[(l + ph) / 2.0])); + v.extend(lower(Kind::P, &[q[1]], &[(l - ph) / 2.0])); + v.push(cx(q[0], q[1])); + v.extend(lower(Kind::U, &[q[1]], &[-t / 2.0, 0.0, -(ph + l) / 2.0])); + v.push(cx(q[0], q[1])); + v.extend(lower(Kind::U, &[q[1]], &[t / 2.0, ph, 0.0])); + v + } + Kind::Swap => vec![("swap", vec![], vec![q[0], q[1]])], + Kind::Iswap => vec![one("s", q[0]), one("s", q[1]), one("h", q[0]), cx(q[0], q[1]), cx(q[1], q[0]), one("h", q[1])], + Kind::Rxx => vec![("rxx", vec![p[0]], vec![q[0], q[1]])], + Kind::Ryy => vec![("ryy", vec![p[0]], vec![q[0], q[1]])], + Kind::Rzz => vec![("rzz", vec![p[0]], vec![q[0], q[1]])], + Kind::Ccx => vec![("ccx", vec![], vec![q[0], q[1], q[2]])], + Kind::Ccz => vec![one("h", q[2]), ("ccx", vec![], vec![q[0], q[1], q[2]]), one("h", q[2])], + Kind::Cswap => vec![cx(q[2], q[1]), ("ccx", vec![], vec![q[0], q[1], q[2]]), cx(q[2], q[1])], + } +} + +fn ptr(i: u32) -> String { + if i == 0 { "ptr null".to_string() } else { format!("ptr inttoptr (i64 {i} to ptr)") } +} + +fn hexf(v: f64) -> String { + format!("0x{:016X}", v.to_bits()) +} + +/// The intrinsic's full name: `s__adj` is `__quantum__qis__s__adj`, `h` is `__quantum__qis__h__body`. +fn intrinsic(n: &str) -> String { + if n.ends_with("__adj") { format!("__quantum__qis__{n}") } else { format!("__quantum__qis__{n}__body") } +} + +struct Writer { + lines: Vec, + declared: Vec<(String, String)>, +} + +impl Writer { + fn declare(&mut self, name: String, sig: &str) { + if !self.declared.iter().any(|(n, _)| *n == name) { + self.declared.push((name, sig.to_string())); + } + } + fn gate(&mut self, name: &'static str, params: &[f64], qubits: &[u32]) { + let full = intrinsic(name); + let sig: Vec<&str> = params.iter().map(|_| "double").chain(qubits.iter().map(|_| "ptr")).collect(); + self.declare(full.clone(), &format!("void @{full}({})", sig.join(", "))); + let args: Vec = params.iter().map(|&p| format!("double {}", hexf(p))).chain(qubits.iter().map(|&q| ptr(q))).collect(); + self.lines.push(format!(" call void @{full}({})", args.join(", "))); + } + fn op(&mut self, p: &Program, i: usize) { + let op = &p.ops[i]; + for (n, a, q) in lower(op.kind, &op.qubits, &op.params) { + self.gate(n, &a, &q); + } + } + fn measure(&mut self, q: u32, r: u32) { + self.declare("__quantum__qis__mz__body".into(), "void @__quantum__qis__mz__body(ptr, ptr writeonly) #1"); + self.lines.push(format!(" call void @__quantum__qis__mz__body({}, {})", ptr(q), ptr(r).replacen("ptr", "ptr writeonly", 1))); + } +} + +/// Writes `p` as QIR 2.0 under `profile`: the base profile when none is +/// given and the program's measurements all come at the end, else the +/// adaptive profile. A program the base profile cannot hold is refused when +/// that profile is asked for. What QIR has no place for (timing, +/// calibrations, outputs) is listed in the notes. +pub fn write(p: &Program, profile: Option) -> Result<(String, Vec), Diagnostic> { + let mut notes = Vec::new(); + dropped(p, &mut notes); + let profile = match (profile, &p.dynamic) { + (Some(Profile::Base), Some(_)) => return Err(Diagnostic::new(diag::WRITE_CANNOT, None, "this program measures in the middle, resets a qubit after a gate, or branches on measured bits: the base profile cannot hold it; write the adaptive profile")), + (Some(pr), _) => pr, + (None, None) => Profile::Base, + (None, Some(_)) => Profile::Adaptive, + }; + let mut w = Writer { lines: Vec::new(), declared: Vec::new() }; + w.declare("__quantum__rt__initialize".into(), "void @__quantum__rt__initialize(ptr)"); + let mut body = vec!["entry:".to_string(), " call void @__quantum__rt__initialize(ptr null)".to_string()]; + let n_results; + match &p.dynamic { + None => { + body.push(" br label %body".into()); + body.push("body:".into()); + for i in 0..p.ops.len() { + w.op(p, i); + } + body.append(&mut w.lines); + body.push(" br label %measurements".into()); + body.push("measurements:".into()); + for &(q, b) in &p.measured { + w.measure(q, b); + } + body.append(&mut w.lines); + body.push(" br label %output".into()); + n_results = p.n_bits; + } + Some(steps) => { + let mut bits = p.n_bits; + for s in steps { + match s { + Instr::Measure { bit, .. } => bits = bits.max(bit + 1), + Instr::JumpUnless(c, _) => bits = bits.max(c.bits.iter().max().map_or(0, |b| b + 1)), + _ => {} + } + } + n_results = bits; + let end = steps.len(); + let mut target = vec![false; end + 1]; + for (i, s) in steps.iter().enumerate() { + match s { + Instr::Jump(t) | Instr::JumpUnless(_, t) => { + if *t <= i { + return Err(Diagnostic::new(diag::WRITE_CANNOT, None, "this program's steps jump backwards (a loop): QIR's adaptive profile is written here with forward branches only")); + } + target[(*t).min(end)] = true; + target[i + 1] = true; + } + _ => {} + } + } + let label = |i: usize| if i >= end { "output".to_string() } else { format!("s{i}") }; + body.push(" br label %s0".into()); + body.push("s0:".into()); + let mut chain = 0usize; + let mut reads = 0usize; + for (i, s) in steps.iter().enumerate() { + if i > 0 && target[i] { + if !body.last().is_some_and(|l| l.starts_with(" br ")) { + body.append(&mut w.lines); + body.push(format!(" br label %{}", label(i))); + } + body.append(&mut w.lines); + body.push(format!("{}:", label(i))); + } + match s { + Instr::Gate(k) => w.op(p, *k), + Instr::Measure { qubit, bit } => w.measure(*qubit, *bit), + Instr::Reset(q) => { + w.declare("__quantum__qis__reset__body".into(), "void @__quantum__qis__reset__body(ptr)"); + w.lines.push(format!(" call void @__quantum__qis__reset__body({})", ptr(*q))); + } + Instr::Jump(t) => { + body.append(&mut w.lines); + body.push(format!(" br label %{}", label(*t))); + } + Instr::JumpUnless(c, t) => { + // Read each bit and branch on it: the condition holds when every bit + // matches its place in `value` (or, for `!=`, when one does not). + w.declare("__quantum__rt__read_result".into(), "i1 @__quantum__rt__read_result(ptr readonly)"); + let (all_match, mismatch) = if c.equal { (label(i + 1), label(*t)) } else { (label(*t), label(i + 1)) }; + body.append(&mut w.lines); + for (k, &b) in c.bits.iter().enumerate() { + if k > 0 { + body.push(format!("c{chain}_{k}:")); + } + let next = if k + 1 == c.bits.len() { all_match.clone() } else { format!("c{chain}_{}", k + 1) }; + body.push(format!(" %r{reads} = call i1 @__quantum__rt__read_result({})", ptr(b).replacen("ptr", "ptr readonly", 1))); + if c.value >> k & 1 == 1 { + body.push(format!(" br i1 %r{reads}, label %{next}, label %{mismatch}")); + } else { + body.push(format!(" br i1 %r{reads}, label %{mismatch}, label %{next}")); + } + reads += 1; + } + if c.bits.is_empty() { + let to = if c.value == 0 { all_match } else { mismatch }; + body.push(format!(" br label %{to}")); + } + chain += 1; + } + } + } + body.append(&mut w.lines); + if !body.last().is_some_and(|l| l.starts_with(" br ")) { + body.push(" br label %output".into()); + } + } + } + body.push("output:".into()); + w.declare("__quantum__rt__array_record_output".into(), "void @__quantum__rt__array_record_output(i64, ptr)"); + w.declare("__quantum__rt__result_record_output".into(), "void @__quantum__rt__result_record_output(ptr, ptr)"); + let mut globals = vec![label_global(0, "c")]; + body.push(format!(" call void @__quantum__rt__array_record_output(i64 {}, ptr @0)", p.n_bits)); + for b in 0..p.n_bits { + globals.push(label_global(b as usize + 1, &format!("c{b}"))); + body.push(format!(" call void @__quantum__rt__result_record_output({}, ptr @{})", ptr(b), b + 1)); + } + body.push(" ret i64 0".into()); + + let mut s = String::new(); + s.push_str("; QIR 2.0 written by wai-quantum: the same bytes on every platform.\n"); + s.push_str("source_filename = \"wai_quantum\"\n\n"); + s.push_str("%Qubit = type opaque\n%Result = type opaque\n\n"); + for g in &globals { + s.push_str(g); + s.push('\n'); + } + s.push_str("\ndefine i64 @main() #0 {\n"); + for l in &body { + s.push_str(l); + s.push('\n'); + } + s.push_str("}\n\n"); + for (_, sig) in &w.declared { + s.push_str(&format!("declare {sig}\n")); + } + s.push_str(&format!( + "\nattributes #0 = {{ \"entry_point\" \"output_labeling_schema\"=\"wai_quantum\" \"qir_profiles\"=\"{}\" \"required_num_qubits\"=\"{}\" \"required_num_results\"=\"{}\" }}\n", + profile.attribute(), + p.n_qubits, + n_results + )); + s.push_str("attributes #1 = { \"irreversible\" }\n\n"); + match profile { + Profile::Base => { + s.push_str("!llvm.module.flags = !{!0, !1, !2, !3}\n\n"); + } + Profile::Adaptive => { + s.push_str("!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7}\n\n"); + } + } + s.push_str("!0 = !{i32 1, !\"qir_major_version\", i32 2}\n"); + s.push_str("!1 = !{i32 7, !\"qir_minor_version\", i32 0}\n"); + s.push_str("!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n"); + s.push_str("!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n"); + if profile == Profile::Adaptive { + s.push_str("!4 = !{i32 1, !\"ir_functions\", i1 false}\n"); + s.push_str("!5 = !{i32 1, !\"backwards_branching\", i2 0}\n"); + s.push_str("!6 = !{i32 1, !\"multiple_target_branching\", i1 false}\n"); + s.push_str("!7 = !{i32 1, !\"multiple_return_points\", i1 false}\n"); + } + Ok((s, notes)) +} + +/// `@n = internal constant [k x i8] c"text\00"`. +fn label_global(n: usize, text: &str) -> String { + format!("@{n} = internal constant [{} x i8] c\"{text}\\00\"", text.len() + 1) +} + +/// What a program carries that QIR and jeff have no place for. +pub(super) fn dropped(p: &Program, notes: &mut Vec) { + let x = &p.extra; + if !x.timing.is_empty() { + notes.push(Diagnostic::new(diag::WRITE_DROPPED, None, format!("{} timing statement{} (delays, boxes, barriers, gate durations) left out: the format has no place for them, and they change nothing a method computes", x.timing.len(), if x.timing.len() == 1 { "" } else { "s" }))); + } + if !x.calibrations.is_empty() { + notes.push(Diagnostic::new(diag::WRITE_DROPPED, None, format!("{} calibration statement{} left out", x.calibrations.len(), if x.calibrations.len() == 1 { "" } else { "s" }))); + } + if !x.outputs.is_empty() { + notes.push(Diagnostic::new(diag::WRITE_DROPPED, None, format!("{} `output` declaration{} left out: the bits are written as the program's results", x.outputs.len(), if x.outputs.len() == 1 { "" } else { "s" }))); + } +} diff --git a/wai/wai-quantum/src/engine/formats/tests.rs b/wai/wai-quantum/src/engine/formats/tests.rs new file mode 100644 index 000000000..01256027d --- /dev/null +++ b/wai/wai-quantum/src/engine/formats/tests.rs @@ -0,0 +1,715 @@ +//! Ground truth first: small programs written by hand in each format, with +//! the unitary or the outcomes they must have, and every refusal with its +//! code and its place. + +use super::capnp::{Out, Ptr}; +use super::*; +use crate::engine::equiv; +use crate::qasm; +use serde_json::json; + +fn qasm_of(format: Format, bytes: &[u8]) -> String { + read(format, bytes).unwrap_or_else(|d| panic!("{} does not read: {d}", format.name())).qasm +} + +/// The ladder's verdict on two programs' texts, and how it decided. +fn verdict(a: &str, b: &str) -> (String, String) { + let r = equiv::equivalent(a, b); + (r["verdict"].as_str().unwrap().to_string(), r["how"].as_str().unwrap().to_string()) +} + +fn assert_equivalent(got: &str, want: &str) { + let (v, how) = verdict(got, want); + assert_eq!(v, "equivalent", "not the reference ({how}):\n--- read\n{got}\n--- reference\n{want}"); +} + +/// The counts a program gives over `shots` shots. +fn counts(text: &str, shots: u64) -> serde_json::Map { + let req = json!({ "qasm": text, "shots": shots, "seed": 7 }); + let plan = crate::engine::catalog::plan(&req, 1 << 30).unwrap_or_else(|e| panic!("{e}\n{text}")); + let out = crate::engine::run::run(&req, &plan).unwrap_or_else(|e| panic!("{e}\n{text}")); + out["counts"].as_object().cloned().unwrap_or_else(|| panic!("no counts in {out}")) +} + +fn code(d: &Diagnostic) -> &'static str { + d.code +} + +// --------------------------------------------------------------------------- +// QIR, read +// --------------------------------------------------------------------------- + +const BELL_BASE: &str = r#" +@0 = internal constant [3 x i8] c"r0\00" +@1 = internal constant [3 x i8] c"r1\00" + +define i64 @Bell() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body + +body: ; preds = %entry + call void @__quantum__qis__h__body(ptr null) + call void @__quantum__qis__cnot__body(ptr null, ptr inttoptr (i64 1 to ptr)) + br label %measurements + +measurements: ; preds = %body + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) + call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr)) + br label %output + +output: ; preds = %measurements + call void @__quantum__rt__result_record_output(ptr null, ptr @0) + call void @__quantum__rt__result_record_output(ptr inttoptr (i64 1 to ptr), ptr @1) + ret i64 0 +} + +declare void @__quantum__qis__h__body(ptr) +declare void @__quantum__qis__cnot__body(ptr, ptr) +declare void @__quantum__qis__mz__body(ptr, ptr writeonly) #1 +declare void @__quantum__rt__initialize(ptr) +declare void @__quantum__rt__result_record_output(ptr, ptr) + +attributes #0 = { "entry_point" "qir_profiles"="base_profile" "output_labeling_schema"="schema_id" "required_num_qubits"="2" "required_num_results"="2" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} + +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 0} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} +"#; + +/// A base-profile module with `body` as its gate block and `meas` as its +/// measurements, `flags` added to its module flags. +fn base(nq: u32, nr: u32, body: &str, meas: &str) -> String { + format!( + "define i64 @main() #0 {{\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n{body}\n br label %m\nm:\n{meas}\n ret i64 0\n}}\n\nattributes #0 = {{ \"entry_point\" \"qir_profiles\"=\"base_profile\" \"output_labeling_schema\"=\"x\" \"required_num_qubits\"=\"{nq}\" \"required_num_results\"=\"{nr}\" }}\nattributes #1 = {{ \"irreversible\" }}\n!llvm.module.flags = !{{!0, !1, !2, !3}}\n!0 = !{{i32 1, !\"qir_major_version\", i32 2}}\n!1 = !{{i32 7, !\"qir_minor_version\", i32 1}}\n!2 = !{{i32 1, !\"dynamic_qubit_management\", i1 false}}\n!3 = !{{i32 1, !\"dynamic_result_management\", i1 false}}\n" + ) +} + +/// An adaptive-profile module with one block of instructions, `extra` +/// module flags after the mandatory four. +fn adaptive(nq: u32, nr: u32, blocks: &str, extra: &[&str]) -> String { + let mut flags = String::new(); + let mut refs = vec!["!0".to_string(), "!1".into(), "!2".into(), "!3".into()]; + for (i, f) in extra.iter().enumerate() { + refs.push(format!("!{}", 4 + i)); + flags.push_str(&format!("!{} = {f}\n", 4 + i)); + } + format!( + "define i64 @main() #0 {{\nentry:\n call void @__quantum__rt__initialize(ptr null)\n{blocks}\n}}\n\nattributes #0 = {{ \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"x\" \"required_num_qubits\"=\"{nq}\" \"required_num_results\"=\"{nr}\" }}\n!llvm.module.flags = !{{{}}}\n!0 = !{{i32 1, !\"qir_major_version\", i32 2}}\n!1 = !{{i32 7, !\"qir_minor_version\", i32 1}}\n!2 = !{{i32 1, !\"dynamic_qubit_management\", i1 false}}\n!3 = !{{i32 1, !\"dynamic_result_management\", i1 false}}\n{flags}", + refs.join(", ") + ) +} + +fn q(i: u32) -> String { + if i == 0 { "ptr null".into() } else { format!("ptr inttoptr (i64 {i} to ptr)") } +} + +#[test] +fn a_base_profile_bell_module_reads_to_the_bell_circuit() { + let r = read(Format::Qir, BELL_BASE.as_bytes()).unwrap(); + assert!(r.notes.is_empty(), "{:?}", r.notes); + assert_eq!((r.summary["profile"].as_str(), r.summary["version"].as_str(), r.summary["entry_point"].as_str()), (Some("base"), Some("2.0"), Some("Bell"))); + assert_eq!(r.summary["recorded"], json!([0, 1])); + assert_eq!(verdict(&r.qasm, "OPENQASM 3.0; qubit[2] q; bit[2] c; h q[0]; cx q[0], q[1]; c = measure q;"), ("equivalent".into(), "tableau".into())); + let c = counts(&r.qasm, 400); + assert_eq!(c.keys().cloned().collect::>(), vec!["00".to_string(), "11".to_string()], "{c:?}"); +} + +/// Every intrinsic the reader takes, each written once at an angle that is +/// not dyadic where it takes one, against the OpenQASM gates it means. +#[test] +fn every_intrinsic_reads_as_the_gate_it_names() { + let (a, b) = (0.3f64, -1.1f64); + let calls = [ + format!("call void @__quantum__qis__h__body({})", q(0)), + format!("call void @__quantum__qis__h__body({})", q(1)), + format!("call void @__quantum__qis__h__body({})", q(2)), + format!("call void @__quantum__qis__x__body({})", q(0)), + format!("call void @__quantum__qis__y__body({})", q(1)), + format!("call void @__quantum__qis__z__body({})", q(2)), + format!("call void @__quantum__qis__s__body({})", q(0)), + format!("call void @__quantum__qis__s__adj({})", q(1)), + format!("call void @__quantum__qis__t__body({})", q(2)), + format!("call void @__quantum__qis__t__adj({})", q(0)), + format!("call void @__quantum__qis__sx__body({})", q(1)), + format!("call void @__quantum__qis__rx__body(double {a:e}, {})", q(0)), + format!("call void @__quantum__qis__ry__body(double 0x{:016X}, {})", b.to_bits(), q(1)), + format!("call void @__quantum__qis__rz__body(double 2.5, {})", q(2)), + format!("call void @__quantum__qis__rz__adj(double 0.7, {})", q(0)), + format!("call void @__quantum__qis__r1__body(double 0.9, {})", q(1)), + format!("call void @__quantum__qis__cnot__body({}, {})", q(0), q(1)), + format!("call void @__quantum__qis__cx__body({}, {})", q(1), q(2)), + format!("call void @__quantum__qis__cy__body({}, {})", q(2), q(0)), + format!("call void @__quantum__qis__cz__body({}, {})", q(0), q(2)), + format!("call void @__quantum__qis__swap__body({}, {})", q(1), q(2)), + format!("call void @__quantum__qis__ccx__body({}, {}, {})", q(0), q(1), q(2)), + format!("call void @__quantum__qis__ccz__body({}, {}, {})", q(2), q(1), q(0)), + format!("call void @__quantum__qis__rxx__body(double 0.4, {}, {})", q(0), q(1)), + format!("call void @__quantum__qis__ryy__body(double -0.6, {}, {})", q(1), q(2)), + format!("call void @__quantum__qis__rzz__body(double 1.3, {}, {})", q(2), q(0)), + ]; + let body: String = calls.iter().map(|c| format!(" {c}\n")).collect(); + let module = base(3, 0, &body, ""); + let r = read(Format::Qir, module.as_bytes()).unwrap(); + let want = format!( + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nh q[0]; h q[1]; h q[2];\nx q[0]; y q[1]; z q[2]; s q[0]; sdg q[1]; t q[2]; tdg q[0]; sx q[1];\nrx({a}) q[0]; ry({b}) q[1]; rz(2.5) q[2]; rz(-0.7) q[0]; p(0.9) q[1];\ncx q[0], q[1]; cx q[1], q[2]; cy q[2], q[0]; cz q[0], q[2]; swap q[1], q[2]; ccx q[0], q[1], q[2]; ctrl(2) @ z q[2], q[1], q[0];\nrxx(0.4) q[0], q[1]; ryy(-0.6) q[1], q[2]; rzz(1.3) q[2], q[0];\n" + ); + assert_eq!(verdict(&r.qasm, &want), ("equivalent".into(), "exact".into()), "{}", r.qasm); + // The text it gives reads in any reader of the standard library: no gate outside it. + for w in ["rxx", "ryy", "rzz", "ccz"] { + assert!(!r.qasm.contains(&format!("{w}(")) && !r.qasm.contains(&format!("{w} ")), "{w} in\n{}", r.qasm); + } + // Rotations at arbitrary angles read, and say so. + assert!(r.notes.iter().any(|n| n.code == diag::ARBITRARY_ANGLE), "{:?}", r.notes); +} + +/// The module this crate wrote before QIR 2 (typed pointers, QIR 1.0) reads +/// to the circuit it was written from, for every algorithm of the catalog. +#[test] +fn qir_one_modules_with_typed_pointers_read() { + use crate::quantum_toolchain as qt; + let mut n = 0; + for (id, _, _) in qt::catalog() { + let Some(c) = qt::build_algorithm(id, 3, 5) else { continue }; + let ir = crate::quantum_qir::to_qir(&c, true); + let r = read(Format::Qir, ir.as_bytes()).unwrap_or_else(|d| panic!("{id}: {d}")); + assert_eq!(r.summary["typed_pointers"], json!(true)); + assert_eq!(r.summary["version"], json!("1.0")); + assert_equivalent(&r.qasm, &qt::to_qasm(&c)); + n += 1; + } + assert!(n >= 8, "{n}"); +} + +/// The adaptive profile's own example, a teleport chain: four measurements +/// in the middle, each followed by a correction on the bit it measured. A +/// state teleported along the chain arrives: with the source flipped to |1⟩ +/// (and the chain's other halves undone), the last qubit always reads 1. +#[test] +fn the_adaptive_teleport_chain_reads_and_teleports() { + let module = r#" +define i64 @TeleportChain() local_unnamed_addr #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body + +body: + tail call void @__quantum__qis__x__body(ptr null) + tail call void @__quantum__qis__h__body(ptr nonnull inttoptr (i64 1 to ptr)) + tail call void @__quantum__qis__cnot__body(ptr nonnull inttoptr (i64 1 to ptr), ptr nonnull inttoptr (i64 2 to ptr)) + tail call void @__quantum__qis__cnot__body(ptr null, ptr nonnull inttoptr (i64 1 to ptr)) + tail call void @__quantum__qis__h__body(ptr null) + tail call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) + tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 1 to ptr), ptr writeonly nonnull inttoptr (i64 1 to ptr)) + %0 = tail call i1 @__quantum__rt__read_result(ptr readonly nonnull inttoptr (i64 1 to ptr)) + br i1 %0, label %then__1, label %continue__1 + +then__1: + tail call void @__quantum__qis__x__body(ptr nonnull inttoptr (i64 2 to ptr)) + br label %continue__1 + +continue__1: + %1 = tail call i1 @__quantum__rt__read_result(ptr readonly null) + br i1 %1, label %then__2, label %continue__2 + +then__2: + tail call void @__quantum__qis__z__body(ptr nonnull inttoptr (i64 2 to ptr)) + br label %continue__2 + +continue__2: + tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 2 to ptr), ptr writeonly nonnull inttoptr (i64 2 to ptr)) + br label %exit + +exit: + call void @__quantum__rt__result_record_output(ptr nonnull inttoptr (i64 2 to ptr), ptr @0) + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "output_labeling_schema"="schema_id" "required_num_qubits"="3" "required_num_results"="3" } + +!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 0} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} +!4 = !{i32 5, !"int_computations", !10} +!5 = !{i32 5, !"float_computations", !11} +!6 = !{i32 1, !"ir_functions", i1 false} +!7 = !{i32 1, !"backwards_branching", i2 0} +!8 = !{i32 1, !"multiple_target_branching", i1 false} +!9 = !{i32 1, !"multiple_return_points", i1 false} +!10 = !{!"i32", !"i64"} +!11 = !{!"float", !"double"} +"#; + let r = read(Format::Qir, module.as_bytes()).unwrap(); + assert_eq!(r.summary["profile"], json!("adaptive")); + let p = qasm::parse(&r.qasm).unwrap(); + assert!(p.dynamic.is_some(), "{}", r.qasm); + // c[2] (the teleported qubit) is always 1, whatever the two corrections measured. + let c = counts(&r.qasm, 300); + assert_eq!(c.len(), 4, "{c:?}"); + for k in c.keys() { + assert!(k.starts_with('1'), "the teleported bit is c[2], first: {k} in {c:?}"); + } +} + +/// Classical computation the flags declare is computed on reading where its +/// operands are constants; a condition built from results with `i1` logic +/// is a condition on the bits. +#[test] +fn declared_computation_and_boolean_logic_read() { + let blocks = format!( + " %a = fadd double 0.25, 0.5\n %b = fmul double %a, 2.0\n call void @__quantum__qis__ry__body(double %b, {q0})\n call void @__quantum__qis__h__body({q1})\n call void @__quantum__qis__mz__body({q0}, ptr writeonly null)\n call void @__quantum__qis__mz__body({q1}, ptr writeonly inttoptr (i64 1 to ptr))\n %r0 = call i1 @__quantum__rt__read_result(ptr readonly null)\n %r1 = call i1 @__quantum__rt__read_result(ptr readonly inttoptr (i64 1 to ptr))\n %x = xor i1 %r0, %r1\n %n = xor i1 %x, true\n br i1 %n, label %same, label %out\nsame:\n call void @__quantum__qis__x__body({q2})\n br label %out\nout:\n call void @__quantum__qis__mz__body({q2}, ptr writeonly inttoptr (i64 2 to ptr))\n ret i64 0", + q0 = q(0), + q1 = q(1), + q2 = q(2) + ); + let flags = ["!{i32 5, !\"float_computations\", !10}", "!{i32 1, !\"backwards_branching\", i2 0}"]; + let mut module = adaptive(3, 3, &blocks, &flags); + module.push_str("!10 = !{!\"double\"}\n"); + let r = read(Format::Qir, module.as_bytes()).unwrap_or_else(|d| panic!("{d}\n{module}")); + assert!(r.qasm.contains("ry(1.5) q[0];"), "{}", r.qasm); + // c[2] is 1 exactly when c[0] equals c[1]. + let c = counts(&r.qasm, 400); + for k in c.keys() { + let b: Vec = k.chars().collect(); // c[2] c[1] c[0] + assert_eq!(b[0] == '1', b[1] == b[2], "{k} in {c:?}\n{}", r.qasm); + } + // Without the flag, the same computation is what the profile forbids, on its line. + let bare = adaptive(3, 3, &blocks, &[]); + let d = read(Format::Qir, bare.as_bytes()).unwrap_err(); + assert_eq!((code(&d), d.line()), (diag::PROFILE_FORBIDS, Some(4)), "{d}"); +} + +#[test] +fn what_each_profile_forbids_is_refused_with_its_code_and_line() { + // Base: a branch on a result. + let m = base(1, 1, &format!(" call void @__quantum__qis__h__body({})", q(0)), " call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)\n %r = call i1 @__quantum__rt__read_result(ptr readonly null)"); + let d = read(Format::Qir, m.as_bytes()).unwrap_err(); + assert_eq!((code(&d), d.line()), (diag::PROFILE_FORBIDS, Some(10)), "{d}"); + assert!(d.message.contains("base profile"), "{d}"); + // Base: a gate after a measurement. + let m = base(1, 1, " call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)", &format!(" call void @__quantum__qis__x__body({})", q(0))); + let d = read(Format::Qir, m.as_bytes()).unwrap_err(); + assert_eq!((code(&d), d.line()), (diag::PROFILE_FORBIDS, Some(9)), "{d}"); + // Adaptive: a loop with no flag forbids it; with the flag, the reader does not take it. + let looped = format!(" br label %l\nl:\n call void @__quantum__qis__mz__body({}, ptr writeonly null)\n %r = call i1 @__quantum__rt__read_result(ptr readonly null)\n br i1 %r, label %l, label %done\ndone:\n ret i64 0", q(0)); + let d = read(Format::Qir, adaptive(1, 1, &looped, &[]).as_bytes()).unwrap_err(); + assert_eq!((code(&d), d.line()), (diag::PROFILE_FORBIDS, Some(8)), "{d}"); + let d = read(Format::Qir, adaptive(1, 1, &looped, &["!{i32 1, !\"backwards_branching\", i2 2}"]).as_bytes()).unwrap_err(); + assert_eq!(code(&d), diag::CAPABILITY_NOT_TAKEN, "{d}"); + // Adaptive: a result read, measured again, then branched on. + let stale = format!(" call void @__quantum__qis__mz__body({}, ptr writeonly null)\n %r = call i1 @__quantum__rt__read_result(ptr readonly null)\n call void @__quantum__qis__mz__body({}, ptr writeonly null)\n br i1 %r, label %a, label %b\na:\n br label %b\nb:\n ret i64 0", q(0), q(1)); + let d = read(Format::Qir, adaptive(2, 1, &stale, &[]).as_bytes()).unwrap_err(); + assert_eq!((code(&d), d.line()), (diag::STALE_READ, Some(7)), "{d}"); + // An intrinsic the reader does not know; an index past the declared count. + let d = read(Format::Qir, base(1, 0, &format!(" call void @__quantum__qis__foo__body({})", q(0)), "").as_bytes()).unwrap_err(); + assert_eq!((code(&d), d.line()), (diag::INSTRUCTION_UNKNOWN, Some(6)), "{d}"); + let d = read(Format::Qir, base(1, 0, &format!(" call void @__quantum__qis__h__body({})", q(4)), "").as_bytes()).unwrap_err(); + assert_eq!(code(&d), diag::OPERAND, "{d}"); + // A profile the reader does not take; a major version it does not take. + let d = read(Format::Qir, base(1, 0, "", "").replace("base_profile", "custom").as_bytes()).unwrap_err(); + assert_eq!(code(&d), diag::PROFILE_UNKNOWN, "{d}"); + let d = read(Format::Qir, base(1, 0, "", "").replace("i32 2}", "i32 3}").as_bytes()).unwrap_err(); + assert_eq!(code(&d), diag::FORMAT_VERSION, "{d}"); + // A module that names no profile reads under the one its instructions need, and says so. + let r = read(Format::Qir, base(1, 0, &format!(" call void @__quantum__qis__h__body({})", q(0)), "").replace(" \"qir_profiles\"=\"base_profile\"", "").as_bytes()).unwrap(); + assert!(r.notes.iter().any(|n| n.code == diag::FORMAT_INCOMPLETE), "{:?}", r.notes); + // Not QIR at all. + assert_eq!(code(&read(Format::Qir, b"this is not llvm").unwrap_err()), diag::FORMAT_UNREAD); +} + +// --------------------------------------------------------------------------- +// QIR, written +// --------------------------------------------------------------------------- + +/// Every gate of the language, alone on qubits in a superposition, written +/// as QIR and read back: the same unitary (decided exactly, on every basis +/// state). +#[test] +fn every_gate_is_written_exactly() { + let gates = [ + "id q[0];", "x q[0];", "y q[1];", "z q[2];", "h q[0];", "s q[1];", "sdg q[2];", "t q[0];", "tdg q[1];", "sx q[2];", "sxdg q[0];", + "rx(0.3) q[0];", "ry(-0.7) q[1];", "rz(1.9) q[2];", "p(0.45) q[0];", "p(pi/4) q[1];", "p(-pi/2) q[2];", "U(0.2, 0.9, -1.4) q[1];", + "cx q[0], q[1];", "cy q[1], q[2];", "cz q[2], q[0];", "ch q[0], q[2];", "cp(0.8) q[1], q[0];", "crx(0.6) q[0], q[1];", "cry(-1.2) q[2], q[1];", + "crz(2.2) q[1], q[2];", "cu(0.3, 0.4, 0.5, 0.6) q[0], q[2];", "swap q[0], q[2];", "iswap q[1], q[0];", "rxx(0.7) q[0], q[1];", "ryy(1.1) q[1], q[2];", + "rzz(-0.4) q[2], q[0];", "ccx q[0], q[1], q[2];", "ccz q[2], q[0], q[1];", "cswap q[1], q[0], q[2];", + ]; + for g in gates { + let src = format!("OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nry(0.37) q[1];\nrx(1.1) q[2];\n{g}\nc = measure q;\n"); + let p = qasm::read(&src).unwrap().program; + let e = write(Format::Qir, &p, None).unwrap(); + let back = qasm_of(Format::Qir, &e.bytes); + let (v, how) = verdict(&back, &src); + assert_eq!((v.as_str(), how.as_str()), ("equivalent", "exact"), "{g} written as\n{}\nread back as\n{back}", String::from_utf8_lossy(&e.bytes)); + // And through jeff. + let j = write(Format::Jeff, &p, None).unwrap(); + let back = qasm_of(Format::Jeff, &j.bytes); + let (v, how) = verdict(&back, &src); + assert_eq!((v.as_str(), how.as_str()), ("equivalent", "exact"), "{g} through jeff reads back as\n{back}"); + } +} + +/// A Clifford+T program is written in Clifford+T intrinsics, with no +/// rotation, and reads back to the bit-exact method; a module written twice +/// is the same bytes. +#[test] +fn clifford_t_stays_clifford_t_and_writing_is_deterministic() { + let src = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\np(pi/4) q[1];\np(3*pi/4) q[2];\ncx q[0], q[1];\nccx q[0], q[1], q[2];\ns q[2];\nc = measure q;\n"; + let p = qasm::read(src).unwrap().program; + let e = write(Format::Qir, &p, None).unwrap(); + let text = String::from_utf8(e.bytes.clone()).unwrap(); + assert!(!text.contains("__rz__") && !text.contains("__rx__") && !text.contains("__ry__"), "{text}"); + assert!(text.contains("\"qir_profiles\"=\"base_profile\"") && text.contains("!{i32 1, !\"qir_major_version\", i32 2}"), "{text}"); + let back = qasm_of(Format::Qir, &e.bytes); + qasm::exact_circuit(&back).unwrap_or_else(|d| panic!("{d}\n{back}")); + assert_eq!(write(Format::Qir, &p, None).unwrap().bytes, e.bytes); + assert_eq!(write(Format::Jeff, &p, None).unwrap().bytes, write(Format::Jeff, &p, None).unwrap().bytes); +} + +/// A program that measures in the middle and branches is written under the +/// adaptive profile and reads back step for step; the base profile refuses it. +#[test] +fn a_program_that_branches_is_written_under_the_adaptive_profile() { + let src = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nx q[0];\nh q[1];\ncx q[1], q[2];\ncx q[0], q[1];\nh q[0];\nc[0] = measure q[0];\nc[1] = measure q[1];\nif (c[1]) {\n x q[2];\n}\nif (c[0] == 1) {\n z q[2];\n} else {\n reset q[0];\n}\nif (c == 2) {\n y q[0];\n}\nc[2] = measure q[2];\n"; + let p = qasm::read(src).unwrap().program; + assert!(p.dynamic.is_some()); + let d = write(Format::Qir, &p, Some(Profile::Base)).unwrap_err(); + assert_eq!(code(&d), diag::WRITE_CANNOT); + let e = write(Format::Qir, &p, None).unwrap(); + assert_eq!(e.summary["profile"], json!("adaptive")); + let text = String::from_utf8(e.bytes.clone()).unwrap(); + assert!(text.contains("@__quantum__rt__read_result") && text.contains("\"qir_profiles\"=\"adaptive_profile\""), "{text}"); + let back = qasm_of(Format::Qir, &e.bytes); + let c = convert(Format::Qasm, Format::Qir, src.as_bytes(), None, true).unwrap(); + assert_eq!(c.check["verdict"]["verdict"], json!("equivalent"), "{}\n{back}", c.check); + // The teleported qubit reads 1 on every shot, as the source does. + for k in counts(&back, 300).keys() { + assert!(k.starts_with('1'), "{k}\n{back}"); + } + // jeff writes no branches. + assert_eq!(code(&write(Format::Jeff, &p, None).unwrap_err()), diag::WRITE_CANNOT); +} + +// --------------------------------------------------------------------------- +// jeff, built by hand +// --------------------------------------------------------------------------- + +/// A jeff module built field by field from the schema, independent of the +/// writer: one function, its values' types and its operations. +struct Hand { + values: Vec, + ops: Vec, + strings: Vec, + sources: Vec, + targets: Vec, + version: (u32, u32), +} + +const QUBIT: u16 = 0; +const INT: u16 = 2; +const FLOAT: u16 = 4; + +impl Hand { + fn new() -> Hand { + Hand { values: vec![], ops: vec![], strings: vec!["main".into()], sources: vec![], targets: vec![], version: (0, 3) } + } + fn value(&mut self, kind: u16, width: u8) -> u32 { + let mut t = Out::new(2, 0); + t.set_u16(0, kind); + match kind { + INT => t.set_u8(2, width), + FLOAT => t.set_u16(1, 1), + _ => {} + } + let mut v = Out::new(0, 2); + v.ptrs[0] = Ptr::Struct(Box::new(t)); + self.values.push(v); + self.values.len() as u32 - 1 + } + fn op(&mut self, inputs: Vec, outputs: Vec, kind: u16, body: Out) { + let mut o = Out::new(1, 4); + o.set_u16(0, kind); + o.ptrs[0] = Ptr::U32s(inputs); + o.ptrs[1] = Ptr::U32s(outputs); + o.ptrs[3] = Ptr::Struct(Box::new(body)); + self.ops.push(o); + } + fn alloc(&mut self) -> u32 { + let v = self.value(QUBIT, 0); + let mut b = Out::new(1, 1); + b.set_u16(0, 0); + self.op(vec![], vec![v], 0, b); + v + } + fn float(&mut self, x: f64) -> u32 { + let v = self.value(FLOAT, 0); + let mut b = Out::new(2, 0); + b.set_u16(2, 1); + b.set_f64(1, x); + self.op(vec![], vec![v], 4, b); + v + } + /// A gate: `which` 0 well-known (`code` its enum value), 1 custom (`code` + /// its name's string), 2 a Pauli-product rotation (`paulis`). + #[allow(clippy::too_many_arguments)] + fn gate(&mut self, which: u16, code: u16, paulis: Vec, qubits: &[u32], controls: &[u32], params: &[u32], adjoint: bool, power: u8, custom: (u8, u8)) -> Vec { + let mut g = Out::new(2, 1); + g.set_u16(1, which); + g.set_u16(0, code); + g.set_u8(4, custom.0); + g.set_u8(5, custom.1); + g.set_u8(6, controls.len() as u8); + g.set_bool(56, adjoint); + g.set_u8(8, power); + if which == 2 { + g.ptrs[0] = Ptr::U16s(paulis); + } + let mut b = Out::new(1, 1); + b.set_u16(0, 6); + b.ptrs[0] = Ptr::Struct(Box::new(g)); + let inputs: Vec = qubits.iter().chain(controls).chain(params).copied().collect(); + let outputs: Vec = qubits.iter().chain(controls).map(|_| self.value(QUBIT, 0)).collect(); + self.op(inputs, outputs.clone(), 0, b); + outputs + } + fn measure(&mut self, q: u32) -> u32 { + let r = self.value(INT, 1); + let mut b = Out::new(1, 1); + b.set_u16(0, 3); + self.op(vec![q], vec![r], 0, b); + r + } + fn bytes(&self) -> Vec { + let mut region = Out::new(0, 4); + region.ptrs[0] = Ptr::U32s(self.sources.clone()); + region.ptrs[1] = Ptr::U32s(self.targets.clone()); + region.ptrs[2] = Ptr::Structs { data_words: 1, ptr_count: 4, items: self.ops.clone() }; + let mut f = Out::new(1, 3); + f.ptrs[0] = Ptr::Struct(Box::new(region)); + f.ptrs[1] = Ptr::Structs { data_words: 0, ptr_count: 2, items: self.values.clone() }; + let mut m = Out::new(2, 5); + m.set_u32(0, self.version.0); + m.set_u32(2, self.version.1); + m.ptrs[0] = Ptr::Structs { data_words: 1, ptr_count: 3, items: vec![f] }; + m.ptrs[1] = Ptr::Texts(self.strings.clone()); + super::capnp::write(&m) + } +} + +/// Well-known gates with their controls after their targets, as the schema +/// says: `x` with target q0 and control q1 is `cx q[1], q[0]`; `adjoint` is +/// the inverse and `power` repeats it; `r1` is the phase gate and `u` is +/// OpenQASM's `U`. +#[test] +fn well_known_gates_read_with_the_schemas_operand_order() { + let mut h = Hand::new(); + let (a, b, c) = (h.alloc(), h.alloc(), h.alloc()); + let a = h.gate(0, 9, vec![], &[a], &[], &[], false, 0, (0, 0))[0]; // h q0 + let o = h.gate(0, 0, vec![], &[b], &[a], &[], false, 0, (0, 0)); // x on q1, control q0 + let (b, a) = (o[0], o[1]); + let t = h.float(0.3); + let c = h.gate(0, 6, vec![], &[c], &[], &[t], true, 3, (0, 0))[0]; // (rx(0.3)†)³ on q2 + let s = h.gate(0, 3, vec![], &[a], &[], &[], true, 1, (0, 0))[0]; // s† on q0 + let (t1, t2, t3) = (h.float(0.2), h.float(0.4), h.float(-0.6)); + let u = h.gate(0, 10, vec![], &[b], &[], &[t1, t2, t3], false, 0, (0, 0))[0]; // U on q1 + let ph = h.float(0.9); + let o = h.gate(0, 5, vec![], &[c], &[s, u], &[ph], false, 0, (0, 0)); // p(0.9) on q2 under q0, q1 + let g = h.float(0.25); + h.gate(0, 13, vec![], &[], &[o[1]], &[g], false, 0, (0, 0)); // gphase under q0: a phase on q0 + let _ = o; + let r = read(Format::Jeff, &h.bytes()).unwrap(); + let want = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nh q[0];\ncx q[0], q[1];\nrx(-0.9) q[2];\nsdg q[0];\nU(0.2, 0.4, -0.6) q[1];\nctrl(2) @ p(0.9) q[0], q[1], q[2];\np(0.25) q[0];\n"; + assert_eq!(verdict(&r.qasm, want), ("equivalent".into(), "exact".into()), "{}", r.qasm); + assert_eq!(r.summary["qubits"], json!(3)); +} + +/// A Pauli-product rotation is `exp(iθP)`, as the schema defines it: its +/// unitary, on every basis state, is `cos θ I + i sin θ P`. +#[test] +fn a_pauli_product_rotation_is_exp_i_theta_p() { + use crate::gates::complex; + let theta = 0.37; + for paulis in [vec![3u16], vec![1], vec![2], vec![1, 2], vec![3, 0, 1], vec![2, 2, 3], vec![0, 0]] { + let n = paulis.len() as u32; + let mut h = Hand::new(); + let qs: Vec = (0..n).map(|_| h.alloc()).collect(); + let t = h.float(theta); + h.gate(2, 0, paulis.clone(), &qs, &[], &[t], false, 0, (0, 0)); + let r = read(Format::Jeff, &h.bytes()).unwrap(); + let p = qasm::read(&r.qasm).unwrap().program; + // The reference, entry by entry: (cos θ I + i sin θ P)|k⟩. + let one = |p: u16, bit: usize| -> (usize, (f64, f64)) { + match p { + 0 => (bit, (1.0, 0.0)), + 1 => (bit ^ 1, (1.0, 0.0)), + 2 => (bit ^ 1, if bit == 0 { (0.0, 1.0) } else { (0.0, -1.0) }), + _ => (bit, if bit == 0 { (1.0, 0.0) } else { (-1.0, 0.0) }), + } + }; + let gphase_only = paulis.iter().all(|&x| x == 0); + for k in 0..1usize << n { + let mut sv = crate::quantum_sv::StateVector::zero(n).unwrap(); + sv.re[0] = 0.0; + sv.re[k] = 1.0; + for g in p.matrices() { + sv.apply(&g).unwrap(); + } + // P|k⟩: qubit 0 is the most significant bit. + let (mut idx, mut amp) = (0usize, (1.0f64, 0.0f64)); + for (q, &pq) in paulis.iter().enumerate() { + let bit = k >> (n as usize - 1 - q) & 1; + let (b, a) = one(pq, bit); + idx |= b << (n as usize - 1 - q); + amp = (amp.0 * a.0 - amp.1 * a.1, amp.0 * a.1 + amp.1 * a.0); + } + let (c, s) = (theta.cos(), theta.sin()); + let mut want = vec![complex(0.0, 0.0); 1 << n]; + want[k].re += c; + // i sin θ · amp + want[idx].re += -s * amp.1; + want[idx].im += s * amp.0; + if gphase_only { + // A global phase is dropped by the reader: compare up to it. + continue; + } + for (i, w) in want.iter().enumerate() { + assert!((sv.re[i] - w.re).abs() < 1e-12 && (sv.im[i] - w.im).abs() < 1e-12, "{paulis:?} |{k}⟩ entry {i}: ({}, {}) against ({}, {})\n{}", sv.re[i], sv.im[i], w.re, w.im, r.qasm); + } + } + assert!(r.notes.iter().any(|d| d.code == diag::JEFF_PPR), "{:?}", r.notes); + } +} + +/// Custom gates read only when they name a gate of the standard library with +/// its qubits and parameters, and say so; the rest are refused. +#[test] +fn custom_gates_are_read_by_a_standard_name_or_refused() { + let mut h = Hand::new(); + h.strings.extend(["H".to_string(), "CX".into(), "Rx".into(), "Hadamard".into()]); + let (a, b) = (h.alloc(), h.alloc()); + let a = h.gate(1, 1, vec![], &[a], &[], &[], false, 0, (1, 0))[0]; + let o = h.gate(1, 2, vec![], &[a, b], &[], &[], false, 0, (2, 0)); + let t = h.float(0.5); + h.gate(1, 3, vec![], &[o[1]], &[], &[t], false, 0, (1, 1)); + let r = read(Format::Jeff, &h.bytes()).unwrap(); + assert_equivalent(&r.qasm, "OPENQASM 3.0; include \"stdgates.inc\"; qubit[2] q; h q[0]; cx q[0], q[1]; rx(0.5) q[1];"); + assert_eq!(r.notes.iter().filter(|d| d.code == diag::JEFF_CUSTOM_NAMED).count(), 3, "{:?}", r.notes); + // A name the library does not have, and a library name with the wrong qubits. + for (name, qubits) in [(4u16, 1u8), (2, 1)] { + let mut h2 = Hand::new(); + h2.strings.extend(["H".to_string(), "CX".into(), "Rx".into(), "Hadamard".into()]); + let x = h2.alloc(); + h2.gate(1, name, vec![], &[x], &[], &[], false, 0, (qubits, 0)); + let d = read(Format::Jeff, &h2.bytes()).unwrap_err(); + assert_eq!(code(&d), diag::JEFF_CUSTOM, "{d}"); + assert!(d.message.contains("operation 1"), "where it stands: {d}"); + } +} + +/// The bits the entry point returns are the program's, in its order; a +/// switch on a measured bit is a condition. +#[test] +fn returned_bits_and_a_switch_on_a_measurement() { + let mut h = Hand::new(); + let (a, b) = (h.alloc(), h.alloc()); + let a = h.gate(0, 9, vec![], &[a], &[], &[], false, 0, (0, 0))[0]; + let b = h.gate(0, 0, vec![], &[b], &[], &[], false, 0, (0, 0))[0]; // x on q1: always 1 + let rb = h.measure(b); // measured first: bit 0 in measurement order + let ra = h.measure(a); + h.targets = vec![ra, rb]; // returned as (a, b): c[0] is a, c[1] is b + let r = read(Format::Jeff, &h.bytes()).unwrap(); + assert_eq!(r.summary["bits_returned"], json!(true)); + for k in counts(&r.qasm, 200).keys() { + assert!(k.starts_with('1'), "c[1] holds q1, always 1: {k}\n{}", r.qasm); + } +} + +#[test] +fn jeff_refusals_name_their_code_and_place() { + // A qubit used twice. + let mut h = Hand::new(); + let a = h.alloc(); + h.gate(0, 9, vec![], &[a], &[], &[], false, 0, (0, 0)); + h.gate(0, 9, vec![], &[a], &[], &[], false, 0, (0, 0)); + let d = read(Format::Jeff, &h.bytes()).unwrap_err(); + assert_eq!(code(&d), diag::JEFF_LINEAR, "{d}"); + assert!(d.message.contains("operation 2") && d.message.contains("`main`"), "{d}"); + // A version the reader does not take. + let mut h = Hand::new(); + h.alloc(); + h.version = (0, 1); + assert_eq!(code(&read(Format::Jeff, &h.bytes()).unwrap_err()), diag::FORMAT_VERSION); + // Not a message; every prefix of a module refused, never read past its end. + assert_eq!(code(&read(Format::Jeff, b"\x00\x00").unwrap_err()), diag::FORMAT_UNREAD); + let p = qasm::read("OPENQASM 3.0; include \"stdgates.inc\"; qubit[2] q; bit[2] c; h q[0]; cx q[0], q[1]; rz(0.2) q[1]; c = measure q;").unwrap().program; + let bytes = write(Format::Jeff, &p, None).unwrap().bytes; + for n in 0..bytes.len() { + assert!(read(Format::Jeff, &bytes[..n]).is_err(), "a prefix of {n} bytes read"); + } + // Any byte changed: refused or read, never a panic. + for i in 0..bytes.len() { + for x in [0x01u8, 0x80, 0xff] { + let mut b = bytes.clone(); + b[i] ^= x; + let _ = read(Format::Jeff, &b); + } + } +} + +// --------------------------------------------------------------------------- +// Conversion, as every surface asks for it +// --------------------------------------------------------------------------- + +#[test] +fn convert_json_carries_a_program_between_every_pair_and_checks_it() { + let src = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\ncx q[0], q[1];\nrz(0.123) q[1];\nccx q[0], q[1], q[2];\nc = measure q;\n"; + let to_qir = convert_json(&json!({ "program": src, "to": "qir" })).unwrap(); + assert_eq!((to_qir["format"].as_str(), to_qir["from"].as_str(), to_qir["check"]["verdict"]["verdict"].as_str()), (Some(FORMAT), Some("qasm"), Some("equivalent")), "{to_qir}"); + let to_jeff = convert_json(&json!({ "program": to_qir["program"], "to": "jeff" })).unwrap(); + assert_eq!(to_jeff["from"], json!("qir"), "found from the bytes"); + assert_eq!(to_jeff["check"]["verdict"]["verdict"], json!("equivalent"), "{to_jeff}"); + let back = convert_json(&json!({ "program_base64": to_jeff["program_base64"], "to": "qasm" })).unwrap(); + assert_eq!(back["from"], json!("jeff")); + assert_eq!(back["check"]["verdict"]["verdict"], json!("equivalent"), "{back}"); + assert_equivalent(back["program"].as_str().unwrap(), src); + // Requests that do not read are the job layer's refusals. + for bad in [json!({ "program": src }), json!({ "program": src, "to": "pdf" }), json!({ "to": "qir" }), json!({ "program": src, "to": "jeff", "profile": "base" })] { + let e = convert_json(&bad).unwrap_err(); + assert!(e.ends_with("]"), "{e}"); + } + // A job may carry its program in another format. + let job = expand(&json!({ "source": { "format": "qir", "program": to_qir["program"] }, "shots": 10 })).unwrap().unwrap(); + assert_equivalent(job["qasm"].as_str().unwrap(), src); + assert!(expand(&json!({ "qasm": src })).unwrap().is_none()); + assert!(expand(&json!({ "qasm": src, "source": { "program": "x" } })).is_err()); +} + +#[test] +fn base64_round_trips() { + for n in 0..40usize { + let b: Vec = (0..n).map(|i| (i * 37 + 11) as u8).collect(); + let e = encode_base64(&b); + assert_eq!(e.len(), n.div_ceil(3) * 4); + assert_eq!(decode_base64(&e).unwrap(), b); + } + assert_eq!(encode_base64(b"jeff"), "amVmZg=="); + assert!(decode_base64("am*f").is_err()); +} + +/// The sniffer and the names agree with the formats. +#[test] +fn formats_are_named_and_found() { + for &f in Format::all() { + assert_eq!(Format::parse(f.name()), Some(f)); + assert_eq!(Format::from_extension(&format!("x.{}", f.extension())), Some(f)); + } + assert_eq!(Format::sniff(BELL_BASE.as_bytes()), Format::Qir); + assert_eq!(Format::sniff(b"OPENQASM 3.0; qubit q;"), Format::Qasm); + let p = qasm::read("OPENQASM 3.0; qubit[1] q; h q[0];").unwrap().program; + assert_eq!(Format::sniff(&write(Format::Jeff, &p, None).unwrap().bytes), Format::Jeff); +} diff --git a/wai/wai-quantum/src/engine/mod.rs b/wai/wai-quantum/src/engine/mod.rs index 1a71286a5..8d710d86b 100644 --- a/wai/wai-quantum/src/engine/mod.rs +++ b/wai/wai-quantum/src/engine/mod.rs @@ -75,6 +75,10 @@ //! frontier between qubits and time; every estimate sealed in the //! resource-estimate receipt family, with the program's digest and its ε //! budget. The logical-counts and routine documents go in and out. +//! - [`formats`]: programs in other formats, QIR (under its base and +//! adaptive profiles) and jeff, read to the one program and written from +//! it, every construct a reader cannot hold refused with its code and its +//! place, and each conversion checked by the equivalence ladder. //! - [`rewrite`]: rewrites that keep a circuit's meaning, each checked on //! the qubits it touches: a gate and its inverse cancel, rotations merge, //! Hadamards conjugate, a gate is carried past what it commutes with. A @@ -196,6 +200,7 @@ pub mod discover; pub mod energy; pub mod equiv; pub mod estimate; +pub mod formats; pub mod guide; pub mod latex; #[cfg(feature = "quantum_noise")] diff --git a/wai/wai-quantum/src/qasm.rs b/wai/wai-quantum/src/qasm.rs index deb47dc3a..2c56d3846 100644 --- a/wai/wai-quantum/src/qasm.rs +++ b/wai/wai-quantum/src/qasm.rs @@ -716,7 +716,7 @@ const DYADIC_TOLERANCE: f64 = 1e-14; /// `2πm/2^k` for the smallest `k ≤ 32` within [`DYADIC_TOLERANCE`] turns: /// `m mod 2^k` scaled to denominator `2^32`. -fn dyadic(angle: f64) -> Option { +pub(crate) fn dyadic(angle: f64) -> Option { let t = (angle / std::f64::consts::TAU).rem_euclid(1.0); for k in 0..=32u32 { let scale = (1u64 << k) as f64; diff --git a/wai/wai-quantum/src/qasm/diag.rs b/wai/wai-quantum/src/qasm/diag.rs index ebf0b9a77..7cc9f3b0c 100644 --- a/wai/wai-quantum/src/qasm/diag.rs +++ b/wai/wai-quantum/src/qasm/diag.rs @@ -274,6 +274,27 @@ table! { // Outputs. OUTPUT = "Q600", Carried, "An `output` declaration, carried into the result: its bits (their places in each outcome) or its value."; OUTPUT_UNKNOWN = "Q601", Refused, "An `output` that is not a bit and whose value depends on a measurement at the end."; + + // Other formats: QIR and jeff, read to a program and written from one (engine::formats). + FORMAT_UNREAD = "Q700", Refused, "A file that does not read in the format named: QIR that is not LLVM text with one entry point, or jeff that is not a Cap'n Proto message of the jeff schema (a pointer outside the message, a struct or list of the wrong shape, more than the reader takes); the message says what and where."; + FORMAT_VERSION = "Q701", Refused, "A format version the reader does not take: QIR whose `qir_major_version` is not 1 or 2, or jeff other than 0.2 and 0.3."; + FORMAT_INCOMPLETE = "Q702", Warned, "A QIR module that leaves out what its profile requires (the `qir_profiles` attribute, the version flags, `required_num_qubits`, `required_num_results`, the `entry_point` attribute of its only function): read from what it holds, and said so."; + PROFILE_UNKNOWN = "Q703", Refused, "A QIR profile the reader does not take: `qir_profiles` names neither `base_profile` nor `adaptive_profile`."; + PROFILE_FORBIDS = "Q704", Refused, "What the module's own QIR profile forbids: under the base profile a branch, a classical instruction, reading a result, or a gate or reset on a qubit after its measurement; under the adaptive profile an optional capability its module flags do not declare (computation on integers or floats of a type, IR-defined functions, loops, `switch`, several returns, dynamic qubits or results, arrays)."; + CAPABILITY_NOT_TAKEN = "Q705", Refused, "A QIR capability the module may use that this reader does not take: loops, `switch`, `phi`, IR-defined functions, dynamic allocation of qubits or results, arrays, controlled forms that take their controls in an array, or a classical value computed from a measurement anywhere but a branch's condition."; + INSTRUCTION_UNKNOWN = "Q706", Refused, "An instruction or function the reader does not know: a quantum intrinsic outside the gates it reads (`docs/formats.md` lists them), a runtime function it does not take, an LLVM instruction no profile lists, or a function a module declares and does not define."; + OPERAND = "Q707", Refused, "A QIR operand that does not read: a qubit or result that is not a constant index, an index past `required_num_qubits` or `required_num_results`, or an angle that is not a finite number known on reading."; + STALE_READ = "Q708", Refused, "A value whose meaning the program's text cannot keep: in QIR a result read, measured again and only then branched on; in jeff a qubit the two sides of a branch on a measurement leave in different places."; + ARBITRARY_ANGLE = "Q709", Warned, "Rotations at angles that are not dyadic multiples of 2π, read at their exact doubles: the double-precision methods take them; the bit-exact method refuses them (Q500)."; + JEFF_CUSTOM = "Q710", Refused, "A jeff custom gate the reader does not know: its name is not a gate of the standard library (in any case), or its qubits or parameters are not that gate's."; + JEFF_CUSTOM_NAMED = "Q711", Warned, "A jeff custom gate named as a gate of the standard library (`H`, `CX`, `Rx`, in any case) with that gate's qubits and parameters: read as that gate, by its name alone."; + JEFF_PPR = "Q712", Lowered, "A jeff Pauli-product rotation, `exp(iθP)` as its schema defines it, lowered exactly to basis changes, a parity ladder and one `rz`."; + JEFF_LINEAR = "Q713", Refused, "A jeff value used where it cannot be: a qubit or register used twice, a value used before it is defined or never defined, a cycle, or a value of the wrong type or count for its operation."; + JEFF_NOT_KNOWN = "Q714", Refused, "A jeff value the reader needs on reading and cannot know: a gate angle, a loop's bounds or a `while` condition from a measurement, a register's size or index, a `switch` on a value that is neither known nor a measured bit, or integer arithmetic on a measured bit beyond not, and, or, xor, eq and select."; + JEFF_STRUCTURED = "Q715", Lowered, "jeff's structured control flow: a `for` or `while` decided on reading unrolled, a `switch` on a known value decided, a `switch` on a measured bit written as `if … else`, and a call of a function the module defines inlined."; + JEFF_ENTRY_QUBITS = "Q716", Warned, "A jeff entry point that takes qubits: they are the program's first qubits, starting in |0⟩, and the qubits it returns are not measured."; + WRITE_CANNOT = "Q720", Refused, "A program the format written cannot hold as it is: under QIR's base profile a measurement in the middle, a reset after a gate or a condition (write the adaptive profile), a loop in QIR, or in jeff a branch on a measured bit."; + WRITE_DROPPED = "Q721", Warned, "What the format read or written has no place for, left out and said so: delays, boxes, barriers and gate durations, calibrations, `output` declarations, and classical values recorded as output."; } #[cfg(test)] -- 2.54.0 (Apple Git-157) From 5fe6f317c1ef7882aa742feafcf0dd96a2beb447 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Fri, 30 Oct 2026 10:27:28 -0400 Subject: [PATCH 02/10] quantum-conformance: other formats, natively and under wasm32-wasip2 wai/quantum-conformance/formats/ holds a module for each construct, each with the program it must read to (a reference held by the equivalence ladder), the outcomes it gives, a round trip back to its format, the formats it writes to, and every refusal with its code and its place: the QIR specification's two example programs, every intrinsic, a QIR 1 module with typed pointers, the profiles' refusals; jeff modules built field by field from the schema; the jeff project's own structured benchmarks and examples, unchanged; and OpenQASM sources written to each format, the QIR written pinned by its BLAKE3. CI runs it in wai-quantum-formats.yml, with the readers' and writers' unit tests. The jeff reader now converts integers to floats and floats to integers, and a QIR conversion is checked against the type it gives. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/wai-quantum-formats.yml | 146 +++ wai/quantum-conformance/formats/README.md | 48 + wai/quantum-conformance/formats/bundle.json | 709 +++++++++++++++ .../formats/jeff/controls_after_targets.jeff | Bin 0 -> 2056 bytes .../jeff/controls_after_targets.ref.qasm | 10 + .../formats/jeff/custom_named.jeff | Bin 0 -> 952 bytes .../formats/jeff/custom_named.ref.qasm | 6 + .../formats/jeff/pauli_product.jeff | Bin 0 -> 776 bytes .../formats/jeff/pauli_product.ref.qasm | 15 + .../jeff/refuse/angle_from_measurement.jeff | Bin 0 -> 832 bytes .../formats/jeff/refuse/custom_unknown.jeff | Bin 0 -> 472 bytes .../jeff/refuse/custom_wrong_qubits.jeff | Bin 0 -> 472 bytes .../formats/jeff/refuse/old_version.jeff | Bin 0 -> 272 bytes .../formats/jeff/refuse/qubit_twice.jeff | Bin 0 -> 528 bytes .../formats/jeff/refuse/truncated.jeff | Bin 0 -> 200 bytes .../controlled-multiplication-modulo-n_3.jeff | Bin 0 -> 30360 bytes .../upstream/example-entangled-calls.jeff | Bin 0 -> 5752 bytes .../upstream/example-entangled-qubits.jeff | Bin 0 -> 3120 bytes .../example-entangled-qubits.ref.qasm | 11 + .../jeff/upstream/example-optimisation.jeff | Bin 0 -> 1048 bytes .../formats/jeff/upstream/example-qubits.jeff | Bin 0 -> 1224 bytes .../jeff/upstream/example-qubits.ref.qasm | 9 + .../jeff/upstream/example-rewrite.jeff | Bin 0 -> 2968 bytes .../formats/jeff/upstream/example-simple.jeff | Bin 0 -> 1384 bytes .../formats/jeff/upstream/ghz-linear_3.jeff | Bin 0 -> 2952 bytes .../jeff/upstream/ghz-linear_3.ref.qasm | 9 + .../formats/jeff/upstream/ghz-star_3.jeff | Bin 0 -> 2848 bytes .../formats/jeff/upstream/ghz-star_3.ref.qasm | 9 + .../formats/jeff/upstream/grover_3.jeff | Bin 0 -> 5792 bytes .../formats/jeff/upstream/iqft_3.jeff | Bin 0 -> 3688 bytes .../formats/jeff/upstream/iqpe_3.jeff | Bin 0 -> 4936 bytes .../formats/jeff/upstream/multiplexer_3.jeff | Bin 0 -> 4192 bytes .../jeff/upstream/qft-adder-classical_3.jeff | Bin 0 -> 8248 bytes .../jeff/upstream/qft-adder-quantum_3.jeff | Bin 0 -> 11672 bytes .../formats/jeff/upstream/qft_3.jeff | Bin 0 -> 4624 bytes .../formats/jeff/upstream/qft_3.ref.qasm | 14 + .../formats/jeff/upstream/qpe_3.jeff | Bin 0 -> 6248 bytes .../jeff/upstream/repeat-until-success_3.jeff | Bin 0 -> 7104 bytes .../formats/jeff/upstream/teleportation.jeff | Bin 0 -> 3352 bytes wai/quantum-conformance/formats/manifest.json | 845 ++++++++++++++++++ .../formats/qasm/clifford_t.qasm | 14 + .../formats/qasm/every_gate.qasm | 17 + .../formats/qasm/measure_reset.qasm | 13 + .../formats/qasm/teleport.qasm | 20 + .../formats/qasm/timing.qasm | 11 + .../formats/qir/bell.ref.qasm | 7 + .../formats/qir/bell_base.ll | 52 ++ .../formats/qir/clifford_t.ll | 30 + .../formats/qir/clifford_t.ref.qasm | 11 + .../formats/qir/computed.ll | 35 + .../formats/qir/every_intrinsic.ll | 51 ++ .../formats/qir/every_intrinsic.ref.qasm | 10 + .../formats/qir/no_profile.ll | 24 + .../formats/qir/qft3_typed.ll | 51 ++ .../formats/qir/qft3_typed.ref.qasm | 12 + .../formats/qir/refuse/base_branch.ll | 25 + .../qir/refuse/base_gate_after_measure.ll | 24 + .../formats/qir/refuse/dynamic_qubits.ll | 18 + .../formats/qir/refuse/loop_declared.ll | 23 + .../formats/qir/refuse/loop_undeclared.ll | 22 + .../formats/qir/refuse/major_version.ll | 24 + .../formats/qir/refuse/not_llvm.ll | 3 + .../formats/qir/refuse/qubit_past_count.ll | 24 + .../formats/qir/refuse/stale_read.ll | 24 + .../formats/qir/refuse/switch_declared.ll | 24 + .../formats/qir/refuse/undeclared_float.ll | 32 + .../formats/qir/refuse/unknown_intrinsic.ll | 24 + .../formats/qir/refuse/unknown_profile.ll | 24 + .../formats/qir/teleport_chain.ll | 121 +++ wai/wai-quantum/Cargo.toml | 7 +- wai/wai-quantum/src/engine/formats/jeff.rs | 34 +- wai/wai-quantum/src/engine/formats/qir.rs | 6 +- wai/wai-quantum/src/engine/formats/tests.rs | 130 ++- wai/wai-quantum/tests/formats_conformance.rs | 211 +++++ 74 files changed, 2959 insertions(+), 30 deletions(-) create mode 100644 .github/workflows/wai-quantum-formats.yml create mode 100644 wai/quantum-conformance/formats/README.md create mode 100644 wai/quantum-conformance/formats/bundle.json create mode 100644 wai/quantum-conformance/formats/jeff/controls_after_targets.jeff create mode 100644 wai/quantum-conformance/formats/jeff/controls_after_targets.ref.qasm create mode 100644 wai/quantum-conformance/formats/jeff/custom_named.jeff create mode 100644 wai/quantum-conformance/formats/jeff/custom_named.ref.qasm create mode 100644 wai/quantum-conformance/formats/jeff/pauli_product.jeff create mode 100644 wai/quantum-conformance/formats/jeff/pauli_product.ref.qasm create mode 100644 wai/quantum-conformance/formats/jeff/refuse/angle_from_measurement.jeff create mode 100644 wai/quantum-conformance/formats/jeff/refuse/custom_unknown.jeff create mode 100644 wai/quantum-conformance/formats/jeff/refuse/custom_wrong_qubits.jeff create mode 100644 wai/quantum-conformance/formats/jeff/refuse/old_version.jeff create mode 100644 wai/quantum-conformance/formats/jeff/refuse/qubit_twice.jeff create mode 100644 wai/quantum-conformance/formats/jeff/refuse/truncated.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/controlled-multiplication-modulo-n_3.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/example-entangled-calls.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/example-entangled-qubits.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/example-entangled-qubits.ref.qasm create mode 100644 wai/quantum-conformance/formats/jeff/upstream/example-optimisation.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/example-qubits.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/example-qubits.ref.qasm create mode 100644 wai/quantum-conformance/formats/jeff/upstream/example-rewrite.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/example-simple.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/ghz-linear_3.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/ghz-linear_3.ref.qasm create mode 100644 wai/quantum-conformance/formats/jeff/upstream/ghz-star_3.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/ghz-star_3.ref.qasm create mode 100644 wai/quantum-conformance/formats/jeff/upstream/grover_3.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/iqft_3.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/iqpe_3.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/multiplexer_3.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/qft-adder-classical_3.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/qft-adder-quantum_3.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/qft_3.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/qft_3.ref.qasm create mode 100644 wai/quantum-conformance/formats/jeff/upstream/qpe_3.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/repeat-until-success_3.jeff create mode 100644 wai/quantum-conformance/formats/jeff/upstream/teleportation.jeff create mode 100644 wai/quantum-conformance/formats/manifest.json create mode 100644 wai/quantum-conformance/formats/qasm/clifford_t.qasm create mode 100644 wai/quantum-conformance/formats/qasm/every_gate.qasm create mode 100644 wai/quantum-conformance/formats/qasm/measure_reset.qasm create mode 100644 wai/quantum-conformance/formats/qasm/teleport.qasm create mode 100644 wai/quantum-conformance/formats/qasm/timing.qasm create mode 100644 wai/quantum-conformance/formats/qir/bell.ref.qasm create mode 100644 wai/quantum-conformance/formats/qir/bell_base.ll create mode 100644 wai/quantum-conformance/formats/qir/clifford_t.ll create mode 100644 wai/quantum-conformance/formats/qir/clifford_t.ref.qasm create mode 100644 wai/quantum-conformance/formats/qir/computed.ll create mode 100644 wai/quantum-conformance/formats/qir/every_intrinsic.ll create mode 100644 wai/quantum-conformance/formats/qir/every_intrinsic.ref.qasm create mode 100644 wai/quantum-conformance/formats/qir/no_profile.ll create mode 100644 wai/quantum-conformance/formats/qir/qft3_typed.ll create mode 100644 wai/quantum-conformance/formats/qir/qft3_typed.ref.qasm create mode 100644 wai/quantum-conformance/formats/qir/refuse/base_branch.ll create mode 100644 wai/quantum-conformance/formats/qir/refuse/base_gate_after_measure.ll create mode 100644 wai/quantum-conformance/formats/qir/refuse/dynamic_qubits.ll create mode 100644 wai/quantum-conformance/formats/qir/refuse/loop_declared.ll create mode 100644 wai/quantum-conformance/formats/qir/refuse/loop_undeclared.ll create mode 100644 wai/quantum-conformance/formats/qir/refuse/major_version.ll create mode 100644 wai/quantum-conformance/formats/qir/refuse/not_llvm.ll create mode 100644 wai/quantum-conformance/formats/qir/refuse/qubit_past_count.ll create mode 100644 wai/quantum-conformance/formats/qir/refuse/stale_read.ll create mode 100644 wai/quantum-conformance/formats/qir/refuse/switch_declared.ll create mode 100644 wai/quantum-conformance/formats/qir/refuse/undeclared_float.ll create mode 100644 wai/quantum-conformance/formats/qir/refuse/unknown_intrinsic.ll create mode 100644 wai/quantum-conformance/formats/qir/refuse/unknown_profile.ll create mode 100644 wai/quantum-conformance/formats/qir/teleport_chain.ll create mode 100644 wai/wai-quantum/tests/formats_conformance.rs diff --git a/.github/workflows/wai-quantum-formats.yml b/.github/workflows/wai-quantum-formats.yml new file mode 100644 index 000000000..a342b1aef --- /dev/null +++ b/.github/workflows/wai-quantum-formats.yml @@ -0,0 +1,146 @@ +name: wai-quantum other formats + +# The readers and writers of other formats (wai/wai-quantum/src/engine/formats/): +# QIR under its base and adaptive profiles and jeff through the crate's own +# Cap'n Proto reader and writer. Their unit tests (the wire format, the +# OpenQASM they write, every intrinsic and gate held to its matrix, each +# profile's refusals with their lines, the Pauli-product rotation against +# exp(iθP), every prefix of a module refused), and the conformance set +# (wai/quantum-conformance/formats/: the QIR specification's examples, the +# jeff project's benchmarks and examples, hand-built modules and OpenQASM +# sources written to each format, each with what the engine must do with it, +# the QIR written pinned by its BLAKE3). +# +# The set runs natively, where a second test holds its bundle equal to its +# files, and under wasm32-wasip2 by wasmtime, where the cases come from the +# bundle the test compiles in and the QIR written must be the same bytes. + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + push: + branches: [main] + paths: + - 'wai/wai-quantum/**' + - 'wai/quantum-conformance/formats/**' + - 'rust-toolchain.toml' + - '.github/workflows/wai-quantum-formats.yml' + pull_request: + paths: + - 'wai/wai-quantum/**' + - 'wai/quantum-conformance/formats/**' + - 'rust-toolchain.toml' + - '.github/workflows/wai-quantum-formats.yml' + +jobs: + relevant: + name: does this change touch it + runs-on: linux-arm64 + outputs: + run: ${{ steps.gate.outputs.run }} + steps: + - uses: actions/checkout@v5 + with: + fetch-depth: 0 + - id: gate + run: | + set -euo pipefail + r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-quantum-formats.yml \ + "${{ github.event.pull_request.base.sha }}" \ + "${{ github.event.pull_request.head.sha }}") + echo "this change touches the readers of other formats: $r" + echo "run=$r" >> "$GITHUB_OUTPUT" + + native: + name: readers, writers and the set · linux + needs: relevant + if: needs.relevant.outputs.run == 'true' + runs-on: linux + timeout-minutes: 60 + steps: + - uses: actions/checkout@v5 + - name: Install Rust 1.98.0 + shell: bash + run: | + export PATH="$HOME/.cargo/bin:$PATH" + command -v rustup >/dev/null 2>&1 || \ + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.98.0 + rustup toolchain install 1.98.0 --profile minimal + rustup default 1.98.0 + echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" + # A module filter is satisfied by any one test of the module, so each + # module's tests are required by name to have run. + - name: The readers' and writers' unit tests + shell: bash + working-directory: wai/wai-quantum + run: | + set -euo pipefail + cargo test --locked --release --features full --lib engine::formats 2>&1 | tee "$RUNNER_TEMP/formats.log" + grep -qE '^test result: ok\. [1-9][0-9]+ passed; 0 failed' "$RUNNER_TEMP/formats.log" || { echo "::error::the formats' tests did not pass"; exit 1; } + for m in engine::formats::capnp::tests engine::formats::emit::tests engine::formats::tests::every_gate_is_written_exactly engine::formats::tests::a_pauli_product_rotation_is_exp_i_theta_p engine::formats::tests::the_conformance_set_holds_these_modules; do + grep -q "^test $m.* \.\.\. ok$" "$RUNNER_TEMP/formats.log" || { echo "::error::no test of $m ran"; exit 1; } + done + - name: The conformance set + shell: bash + working-directory: wai/wai-quantum + run: | + set -euo pipefail + cargo test --locked --release --features full --test formats_conformance 2>&1 | tee "$RUNNER_TEMP/set.log" + grep -q 'test result: ok. 2 passed; 0 failed' "$RUNNER_TEMP/set.log" || { echo "::error::the conformance set did not pass"; exit 1; } + - name: The readers without the receipts + shell: bash + working-directory: wai/wai-quantum + run: | + set -euo pipefail + cargo check --locked --no-default-features --features quantum_engine --lib + + wasip2: + name: the set under wasm32-wasip2 (wasmtime) + needs: relevant + if: needs.relevant.outputs.run == 'true' + runs-on: linux + timeout-minutes: 60 + steps: + - uses: actions/checkout@v5 + - name: Install Rust 1.98.0 with the wasm32-wasip2 target + shell: bash + run: | + export PATH="$HOME/.cargo/bin:$PATH" + command -v rustup >/dev/null 2>&1 || \ + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.98.0 + rustup toolchain install 1.98.0 --profile minimal + rustup default 1.98.0 + rustup target add --toolchain 1.98.0 wasm32-wasip2 + echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" + # The release binary, pinned by version and SHA-256, rather than a build from source. + - name: Install wasmtime 49.0.2 + shell: bash + run: | + set -euo pipefail + [ "$(uname -m)" = x86_64 ] || { echo "::error::the pinned wasmtime archive is x86_64; this host is $(uname -m)"; exit 1; } + if ! command -v xz >/dev/null 2>&1; then + SUDO=""; [ "$(id -u)" -ne 0 ] && SUDO="sudo" + $SUDO apt-get update -qq + $SUDO apt-get install -y -qq xz-utils + fi + v=49.0.2 + f="wasmtime-v$v-x86_64-linux.tar.xz" + curl --proto '=https' --tlsv1.2 -sSfL -o "$RUNNER_TEMP/$f" \ + "https://github.com/bytecodealliance/wasmtime/releases/download/v$v/$f" + echo "a4d6e9e3a5a60f527cf7793d674c48930c80c2e8977995b8a275cad3254b9322 $RUNNER_TEMP/$f" | sha256sum -c - + tar -xJf "$RUNNER_TEMP/$f" -C "$RUNNER_TEMP" + echo "$RUNNER_TEMP/wasmtime-v$v-x86_64-linux" >> "$GITHUB_PATH" + # A debug build, so integer overflow panics instead of wrapping. The test + # that reads the set's files has no wasip2 build; the one that runs the + # cases from the compiled-in bundle does. + - name: The conformance set under wasmtime + shell: bash + working-directory: wai/wai-quantum + env: + CARGO_TARGET_WASM32_WASIP2_RUNNER: wasmtime + run: | + set -euo pipefail + cargo test --locked --target wasm32-wasip2 --features full --test formats_conformance 2>&1 | tee "$RUNNER_TEMP/wasip2.log" + grep -q 'test result: ok. 1 passed; 0 failed' "$RUNNER_TEMP/wasip2.log" || { echo "::error::the conformance set did not pass under wasip2"; exit 1; } diff --git a/wai/quantum-conformance/formats/README.md b/wai/quantum-conformance/formats/README.md new file mode 100644 index 000000000..c4be55341 --- /dev/null +++ b/wai/quantum-conformance/formats/README.md @@ -0,0 +1,48 @@ +# Other formats: conformance + +Programs in QIR and jeff, each with what the engine's readers and writers +(`wai_quantum::engine::formats`, [docs](../../wai-quantum/docs/formats.md)) +must do with it: read it to the program a reference gives, give the outcomes +it must give, write it back to its own format and read it again unchanged, +or refuse it with a code and its place. OpenQASM sources are written to each +format and read back. + +| Path | What | +|---|---| +| `qir/` | QIR modules: the specification's two example programs (the base profile's Bell pair and the adaptive profile's teleport chain, MIT, as the specification publishes its code), every intrinsic the reader takes, a Clifford+T module, a QIR 1 module with typed pointers from the crate's earlier writer, computation the module flags declare, and a module that names no profile. `*.ref.qasm` are the references, written by hand. | +| `qir/refuse/` | A module for each thing a profile forbids, each capability declared and not taken, a stale read, an unknown intrinsic, a qubit past its count, an unknown profile, QIR 3, and text that is not LLVM: each with its code and line. | +| `jeff/` | jeff modules built field by field from the schema (not by the writer): well-known gates with their controls after their targets, custom gates named as standard gates, a Pauli-product rotation; `refuse/` the refusals. A unit test (`engine::formats::tests::the_conformance_set_holds_these_modules`) makes them and holds the files to them. | +| `jeff/upstream/` | The jeff project's own structured benchmarks (GHZ, QFT, QPE, Grover, teleportation, iterative QFT and QPE, multiplexer, QFT adders, a controlled modular multiplier: loops over registers, switches on measured bits, several segments) and its examples, unchanged (Apache-2.0), from the jeff repository at commit `97f73829caa2de0b6fbf925d1f97f9eb10c158ad`; the examples renamed by what they show. `*.ref.qasm` are references written by hand from each benchmark's source. Those the readers cannot hold exactly are refused with their codes. | +| `qasm/` | OpenQASM sources written to QIR and jeff and read back: every gate of the language, Clifford+T, a branching program, a measurement in the middle with a reset, and timing neither format holds. The QIR written is pinned by its BLAKE3, the same bytes natively and under wasm32-wasip2. | +| `manifest.json` | Each case: its file, its format, what it covers, its reference, and its expectations (the manifest's `expectations` says what each kind means). | +| `bundle.json` | The manifest's cases with each file inline (text, or base64 for jeff), for builds that read no files. | + +## Native + +```sh +cd wai/wai-quantum +cargo test --release --features full --test formats_conformance +cargo test --release --features full --lib engine::formats +``` + +Two tests: every case against its expectations, and the bundle held equal to +the files. After changing a file or the manifest, write the bundle: + +```sh +WAI_FORMATS_BUNDLE_WRITE=1 cargo test --release --features full --test formats_conformance the_bundle_is_the_corpus +``` + +and after changing the hand-built jeff modules, +`WAI_FORMATS_VECTORS_WRITE=1 cargo test --release --features full --lib the_conformance_set_holds_these_modules`. + +## WASI (the edge) + +```sh +CARGO_TARGET_WASM32_WASIP2_RUNNER=wasmtime \ + cargo test --target wasm32-wasip2 --features full --test formats_conformance +``` + +The cases come from the bundle compiled into the test. + +CI: `wai-quantum-formats.yml` (the readers' and writers' unit tests, and the +set natively and under wasm32-wasip2). diff --git a/wai/quantum-conformance/formats/bundle.json b/wai/quantum-conformance/formats/bundle.json new file mode 100644 index 000000000..e63dd57cf --- /dev/null +++ b/wai/quantum-conformance/formats/bundle.json @@ -0,0 +1,709 @@ +{ + "format": "wai.formats-conformance/1", + "about": "The manifest's cases with each file inline (text, or base64 for jeff), for builds that read no files.", + "cases": [ + { + "file": "qir/bell_base.ll", + "format": "qir", + "expect": { + "profile": "base", + "notes": [], + "how": "tableau", + "outcomes": [ + "00", + "11" + ], + "round_trip": true + }, + "text": "; The base profile's own example program (the QIR specification, MIT): two\n; qubits entangled, measured, and a tuple of the two results recorded.\n\n@0 = internal constant [3 x i8] c\"r1\\00\"\n@1 = internal constant [3 x i8] c\"r2\\00\"\n@2 = internal constant [3 x i8] c\"t0\\00\"\n\ndefine i64 @Entry_Point_Name() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\n\nbody: ; preds = %entry\n call void @__quantum__qis__h__body(ptr null)\n call void @__quantum__qis__cnot__body(ptr null, ptr inttoptr (i64 1 to ptr))\n br label %measurements\n\nmeasurements: ; preds = %body\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)\n call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr))\n br label %output\n\noutput: ; preds = %measurements\n call void @__quantum__rt__tuple_record_output(i64 2, ptr @2)\n call void @__quantum__rt__result_record_output(ptr null, ptr @0)\n call void @__quantum__rt__result_record_output(ptr inttoptr (i64 1 to ptr), ptr @1)\n\n ret i64 0\n}\n\ndeclare void @__quantum__qis__h__body(ptr)\n\ndeclare void @__quantum__qis__cnot__body(ptr, ptr)\n\ndeclare void @__quantum__qis__mz__body(ptr, ptr writeonly) #1\n\ndeclare void @__quantum__rt__initialize(ptr)\n\ndeclare void @__quantum__rt__tuple_record_output(i64, ptr)\n\ndeclare void @__quantum__rt__result_record_output(ptr, ptr)\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"base_profile\" \"output_labeling_schema\"=\"schema_id\" \"required_num_qubits\"=\"2\" \"required_num_results\"=\"2\" }\n\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 0}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n" + }, + { + "file": "qir/every_intrinsic.ll", + "format": "qir", + "expect": { + "profile": "base", + "notes": [ + "Q709" + ], + "how": "exact", + "round_trip": true + }, + "text": "; Every intrinsic the reader takes, once each, at angles that are not dyadic where an intrinsic takes one (decimal, exponent and hex), then every qubit measured.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n call void @__quantum__qis__h__body(ptr null)\n call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__h__body(ptr inttoptr (i64 2 to ptr))\n call void @__quantum__qis__x__body(ptr null)\n call void @__quantum__qis__y__body(ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__z__body(ptr inttoptr (i64 2 to ptr))\n call void @__quantum__qis__s__body(ptr null)\n call void @__quantum__qis__s__adj(ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__t__body(ptr inttoptr (i64 2 to ptr))\n call void @__quantum__qis__t__adj(ptr null)\n call void @__quantum__qis__sx__body(ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__rx__body(double 3.000000e-01, ptr null)\n call void @__quantum__qis__ry__body(double 0xBFF199999999999A, ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__rz__body(double 2.5, ptr inttoptr (i64 2 to ptr))\n call void @__quantum__qis__rz__adj(double 0.7, ptr null)\n call void @__quantum__qis__r1__body(double 0.9, ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__cnot__body(ptr null, ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__cx__body(ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr))\n call void @__quantum__qis__cy__body(ptr inttoptr (i64 2 to ptr), ptr null)\n call void @__quantum__qis__cz__body(ptr null, ptr inttoptr (i64 2 to ptr))\n call void @__quantum__qis__swap__body(ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr))\n call void @__quantum__qis__ccx__body(ptr null, ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr))\n call void @__quantum__qis__ccz__body(ptr inttoptr (i64 2 to ptr), ptr inttoptr (i64 1 to ptr), ptr null)\n call void @__quantum__qis__rxx__body(double 0.4, ptr null, ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__ryy__body(double -0.6, ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr))\n call void @__quantum__qis__rzz__body(double 1.3, ptr inttoptr (i64 2 to ptr), ptr null)\n br label %measurements\nmeasurements:\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1\n call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr)) #1\n call void @__quantum__qis__mz__body(ptr inttoptr (i64 2 to ptr), ptr writeonly inttoptr (i64 2 to ptr)) #1\n br label %output\noutput:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"base_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"3\" \"required_num_results\"=\"3\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0]; h q[1]; h q[2];\nx q[0]; y q[1]; z q[2]; s q[0]; sdg q[1]; t q[2]; tdg q[0]; sx q[1];\nrx(0.3) q[0]; ry(-1.1) q[1]; rz(2.5) q[2]; rz(-0.7) q[0]; p(0.9) q[1];\ncx q[0], q[1]; cx q[1], q[2]; cy q[2], q[0]; cz q[0], q[2]; swap q[1], q[2]; ccx q[0], q[1], q[2]; ctrl(2) @ z q[2], q[1], q[0];\nrxx(0.4) q[0], q[1]; ryy(-0.6) q[1], q[2]; rzz(1.3) q[2], q[0];\nc = measure q;\n" + }, + { + "file": "qir/clifford_t.ll", + "format": "qir", + "expect": { + "profile": "base", + "notes": [], + "how": "exact", + "exact": true, + "round_trip": true + }, + "text": "; A Clifford+T module, one rotation by exactly pi/4 in hex: it reads with no note and runs on the bit-exact method.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n call void @__quantum__qis__h__body(ptr null)\n call void @__quantum__qis__t__body(ptr null)\n call void @__quantum__qis__cx__body(ptr null, ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__s__adj(ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, ptr inttoptr (i64 1 to ptr))\n br label %measurements\nmeasurements:\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1\n call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr)) #1\n br label %output\noutput:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"base_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"2\" \"required_num_results\"=\"2\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nsdg q[1];\nh q[1];\nrz(pi/4) q[1];\nc = measure q;\n" + }, + { + "file": "qir/qft3_typed.ll", + "format": "qir", + "expect": { + "profile": "base", + "notes": [], + "how": "exact", + "exact": true, + "round_trip": true + }, + "text": "; QIR emitted by wai-quantum — deterministic, byte-identical on every target.\nsource_filename = \"wai_quantum\"\n\n%Qubit = type opaque\n%Result = type opaque\n\ndeclare void @__quantum__qis__h__body(%Qubit*)\ndeclare void @__quantum__qis__rz__body(double, %Qubit*)\ndeclare void @__quantum__qis__cnot__body(%Qubit*, %Qubit*)\ndeclare void @__quantum__qis__mz__body(%Qubit*, %Result*)\ndeclare void @__quantum__rt__result_record_output(%Result*, i8*)\n\ndefine void @main() #0 {\nentry:\n call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, %Qubit* inttoptr (i64 1 to %Qubit*))\n call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__rz__body(double 0xBFE921FB54442D18, %Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, %Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__rz__body(double 0x3FD921FB54442D18, %Qubit* inttoptr (i64 2 to %Qubit*))\n call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__rz__body(double 0xBFD921FB54442D18, %Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__rz__body(double 0x3FD921FB54442D18, %Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))\n call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, %Qubit* inttoptr (i64 2 to %Qubit*))\n call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))\n call void @__quantum__qis__rz__body(double 0xBFE921FB54442D18, %Qubit* inttoptr (i64 1 to %Qubit*))\n call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))\n call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, %Qubit* inttoptr (i64 1 to %Qubit*))\n call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*))\n call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*))\n call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*))\n call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))\n call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))\n call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 2 to %Result*))\n call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)\n call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)\n call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 2 to %Result*), i8* null)\n ret void\n}\n\nattributes #0 = { \"entry_point\" \"output_labeling_schema\" \"qir_profiles\"=\"base_profile\" \"required_num_qubits\"=\"3\" \"required_num_results\"=\"3\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 1}\n!1 = !{i32 7, !\"qir_minor_version\", i32 0}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nh q[0];\ncp(2*pi/4) q[1],q[0];\ncp(2*pi/8) q[2],q[0];\nh q[1];\ncp(2*pi/4) q[2],q[1];\nh q[2];\ncx q[0],q[2];\ncx q[2],q[0];\ncx q[0],q[2];\n" + }, + { + "file": "qir/teleport_chain.ll", + "format": "qir", + "expect": { + "profile": "adaptive", + "notes": [], + "dynamic": true, + "qubits": 6, + "equal_bits": [ + [ + 4, + 5 + ] + ], + "round_trip": true + }, + "text": "; The adaptive profile's own example program (the QIR specification, MIT): a\n; Bell pair whose second half is teleported twice, each step measuring in\n; the middle and correcting on the result. The pair ends between qubits 0 and\n; 5, so results 4 and 5 are equal on every shot.\n\n@0 = internal constant [5 x i8] c\"0_t0\\00\"\n@1 = internal constant [5 x i8] c\"0_t1\\00\"\n\ndefine i64 @TeleportChain() local_unnamed_addr #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\n\nbody: ; preds = %entry\n tail call void @__quantum__qis__h__body(ptr null)\n tail call void @__quantum__qis__cnot__body(ptr null, ptr nonnull inttoptr (i64 1 to ptr))\n tail call void @__quantum__qis__h__body(ptr nonnull inttoptr (i64 2 to ptr))\n tail call void @__quantum__qis__cnot__body(ptr nonnull inttoptr (i64 2 to ptr), ptr nonnull inttoptr (i64 4 to ptr))\n tail call void @__quantum__qis__h__body(ptr nonnull inttoptr (i64 3 to ptr))\n tail call void @__quantum__qis__cnot__body(ptr nonnull inttoptr (i64 3 to ptr), ptr nonnull inttoptr (i64 5 to ptr))\n tail call void @__quantum__qis__cnot__body(ptr nonnull inttoptr (i64 1 to ptr), ptr nonnull inttoptr (i64 2 to ptr))\n tail call void @__quantum__qis__h__body(ptr nonnull inttoptr (i64 1 to ptr))\n tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 1 to ptr), ptr writeonly null)\n tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 1 to ptr))\n %0 = tail call i1 @__quantum__rt__read_result(ptr readonly null)\n br i1 %0, label %then__1, label %continue__1\n\n; conditional quantum gate (only one in this block, but many can appear and the full quantum instruction set should be usable)\nthen__1: ; preds = %body\n tail call void @__quantum__qis__z__body(ptr nonnull inttoptr (i64 4 to ptr))\n br label %continue__1\n\ncontinue__1: ; preds = %then__1, %body\n tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 2 to ptr), ptr writeonly nonnull inttoptr (i64 1 to ptr))\n tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 2 to ptr))\n %1 = tail call i1 @__quantum__rt__read_result(ptr readonly nonnull inttoptr (i64 1 to ptr))\n br i1 %1, label %then__2, label %continue__2\n\nthen__2: ; preds = %continue__1\n tail call void @__quantum__qis__x__body(ptr nonnull inttoptr (i64 4 to ptr))\n br label %continue__2\n\ncontinue__2: ; preds = %then__2, %continue__1\n tail call void @__quantum__qis__cnot__body(ptr nonnull inttoptr (i64 4 to ptr), ptr nonnull inttoptr (i64 3 to ptr))\n tail call void @__quantum__qis__h__body(ptr nonnull inttoptr (i64 4 to ptr))\n tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 4 to ptr), ptr writeonly nonnull inttoptr (i64 2 to ptr))\n tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 4 to ptr))\n %2 = tail call i1 @__quantum__rt__read_result(ptr readonly nonnull inttoptr (i64 2 to ptr))\n br i1 %2, label %then__3, label %continue__3\n\nthen__3: ; preds = %continue__2\n tail call void @__quantum__qis__z__body(ptr nonnull inttoptr (i64 5 to ptr))\n br label %continue__3\n\ncontinue__3: ; preds = %then__3, %continue__2\n tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 3 to ptr), ptr writeonly nonnull inttoptr (i64 3 to ptr))\n tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 3 to ptr))\n %3 = tail call i1 @__quantum__rt__read_result(ptr readonly nonnull inttoptr (i64 3 to ptr))\n br i1 %3, label %then__4, label %continue__4\n\nthen__4: ; preds = %continue__3\n tail call void @__quantum__qis__x__body(ptr nonnull inttoptr (i64 5 to ptr))\n br label %continue__4\n\ncontinue__4: ; preds = %continue__3, %then__4\n tail call void @__quantum__qis__mz__body(ptr null, ptr writeonly nonnull inttoptr (i64 4 to ptr))\n tail call void @__quantum__qis__reset__body(ptr null)\n tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 5 to ptr), ptr writeonly nonnull inttoptr (i64 5 to ptr))\n tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 5 to ptr))\n br label %exit\n\nexit:\n call void @__quantum__rt__result_record_output(ptr nonnull inttoptr (i64 4 to ptr), ptr @0)\n call void @__quantum__rt__result_record_output(ptr nonnull inttoptr (i64 5 to ptr), ptr @1)\n ret i64 0\n}\n\n; declarations of QIS functions\n\ndeclare void @__quantum__qis__cnot__body(ptr, ptr) local_unnamed_addr\n\ndeclare void @__quantum__qis__h__body(ptr) local_unnamed_addr\n\ndeclare void @__quantum__qis__x__body(ptr) local_unnamed_addr\n\ndeclare void @__quantum__qis__z__body(ptr) local_unnamed_addr\n\ndeclare void @__quantum__qis__reset__body(ptr) local_unnamed_addr\n\ndeclare void @__quantum__qis__mz__body(ptr, ptr writeonly) #1\n\n; declarations of runtime functions\n\ndeclare void @__quantum__rt__initialize(ptr)\n\ndeclare i1 @__quantum__rt__read_result(ptr readonly)\n\ndeclare void @__quantum__rt__result_record_output(ptr, ptr)\n\n; attributes\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"schema_id\" \"required_num_qubits\"=\"6\" \"required_num_results\"=\"6\" }\n\nattributes #1 = { \"irreversible\" }\n\n; module flags\n\n!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9}\n\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 0}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n!4 = !{i32 5, !\"int_computations\", !10}\n!5 = !{i32 5, !\"float_computations\", !11}\n!6 = !{i32 1, !\"ir_functions\", i1 false}\n!7 = !{i32 1, !\"backwards_branching\", i2 0}\n!8 = !{i32 1, !\"multiple_target_branching\", i1 false}\n!9 = !{i32 1, !\"multiple_return_points\", i1 false}\n!10 = !{!\"i32\", !\"i64\"}\n!11 = !{!\"float\", !\"double\"}\n" + }, + { + "file": "qir/computed.ll", + "format": "qir", + "expect": { + "profile": "adaptive", + "notes": [ + "Q709" + ], + "dynamic": true, + "outcomes": [ + "001", + "010", + "100", + "111" + ], + "round_trip": true + }, + "text": "; Adaptive: an angle computed from constants under float_computations, and a branch on two results combined by i1 logic: result 2 is 1 exactly when results 0 and 1 agree.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n %a = fadd double 0.25, 0.5\n %b = fmul double %a, 2.0\n call void @__quantum__qis__ry__body(double %b, ptr null)\n call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)\n call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr))\n %r0 = call i1 @__quantum__rt__read_result(ptr readonly null)\n %r1 = call i1 @__quantum__rt__read_result(ptr readonly inttoptr (i64 1 to ptr))\n %x = xor i1 %r0, %r1\n %n = xor i1 %x, true\n br i1 %n, label %same, label %out\nsame:\n call void @__quantum__qis__x__body(ptr inttoptr (i64 2 to ptr))\n br label %out\nout:\n call void @__quantum__qis__mz__body(ptr inttoptr (i64 2 to ptr), ptr writeonly inttoptr (i64 2 to ptr))\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"3\" \"required_num_results\"=\"3\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3, !4, !5}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n!4 = !{i32 5, !\"float_computations\", !10}\n!5 = !{i32 1, !\"backwards_branching\", i2 0}\n!10 = !{!\"double\"}\n" + }, + { + "file": "qir/no_profile.ll", + "format": "qir", + "expect": { + "profile": "base", + "notes": [ + "Q702" + ], + "outcomes": [ + "0", + "1" + ] + }, + "text": "; A module that names no profile: read under the profile its instructions need (base), and said so.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n call void @__quantum__qis__h__body(ptr null)\n br label %measurements\nmeasurements:\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1\n br label %output\noutput:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"1\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/base_branch.ll", + "format": "qir", + "expect": { + "refused": "Q704", + "line": 12 + }, + "text": "; Under the base profile, reading a result: forbidden.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n call void @__quantum__qis__h__body(ptr null)\n br label %measurements\nmeasurements:\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1\n %r = call i1 @__quantum__rt__read_result(ptr readonly null)\n br label %output\noutput:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"base_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"1\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/base_gate_after_measure.ll", + "format": "qir", + "expect": { + "refused": "Q704", + "line": 11 + }, + "text": "; Under the base profile, a gate on a qubit after its measurement: forbidden.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1\n br label %measurements\nmeasurements:\n call void @__quantum__qis__x__body(ptr null)\n br label %output\noutput:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"base_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"1\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/undeclared_float.ll", + "format": "qir", + "expect": { + "refused": "Q704", + "line": 6 + }, + "text": "; The same computation with no float_computations flag: what the adaptive profile forbids, refused on its line.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n %a = fadd double 0.25, 0.5\n %b = fmul double %a, 2.0\n call void @__quantum__qis__ry__body(double %b, ptr null)\n call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)\n call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr))\n %r0 = call i1 @__quantum__rt__read_result(ptr readonly null)\n %r1 = call i1 @__quantum__rt__read_result(ptr readonly inttoptr (i64 1 to ptr))\n %x = xor i1 %r0, %r1\n %n = xor i1 %x, true\n br i1 %n, label %same, label %out\nsame:\n call void @__quantum__qis__x__body(ptr inttoptr (i64 2 to ptr))\n br label %out\nout:\n call void @__quantum__qis__mz__body(ptr inttoptr (i64 2 to ptr), ptr writeonly inttoptr (i64 2 to ptr))\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"3\" \"required_num_results\"=\"3\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/loop_undeclared.ll", + "format": "qir", + "expect": { + "refused": "Q704", + "line": 10 + }, + "text": "; A loop (a branch back to an earlier block) with no backwards_branching flag: forbidden.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %l\nl:\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)\n %r = call i1 @__quantum__rt__read_result(ptr readonly null)\n br i1 %r, label %l, label %done\ndone:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"1\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/loop_declared.ll", + "format": "qir", + "expect": { + "refused": "Q705", + "line": 10 + }, + "text": "; The same loop with backwards_branching declared: a capability this reader does not take.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %l\nl:\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)\n %r = call i1 @__quantum__rt__read_result(ptr readonly null)\n br i1 %r, label %l, label %done\ndone:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"1\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3, !4}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n!4 = !{i32 1, !\"backwards_branching\", i2 2}\n" + }, + { + "file": "qir/refuse/switch_declared.ll", + "format": "qir", + "expect": { + "refused": "Q705", + "line": 8 + }, + "text": "; A switch on a result, multiple-target branching declared: a capability this reader does not take.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)\n %r = call i1 @__quantum__rt__read_result(ptr readonly null)\n switch i1 %r, label %done [ i1 true, label %one ]\none:\n call void @__quantum__qis__x__body(ptr null)\n br label %done\ndone:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"1\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3, !4}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n!4 = !{i32 1, !\"multiple_target_branching\", i1 true}\n" + }, + { + "file": "qir/refuse/dynamic_qubits.ll", + "format": "qir", + "expect": { + "refused": "Q705", + "line": 6 + }, + "text": "; A qubit allocated at run time, with dynamic qubit management declared: a capability this reader does not take.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n %q = call ptr @__quantum__rt__qubit_allocate()\n call void @__quantum__qis__h__body(ptr %q)\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"0\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 true}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/stale_read.ll", + "format": "qir", + "expect": { + "refused": "Q708", + "line": 9 + }, + "text": "; A result read, measured again into the same result, and only then branched on: the branch would read the later measurement.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)\n %r = call i1 @__quantum__rt__read_result(ptr readonly null)\n call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly null)\n br i1 %r, label %a, label %b\na:\n call void @__quantum__qis__x__body(ptr inttoptr (i64 1 to ptr))\n br label %b\nb:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"2\" \"required_num_results\"=\"1\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/unknown_intrinsic.ll", + "format": "qir", + "expect": { + "refused": "Q706", + "line": 8 + }, + "text": "; An intrinsic the reader does not know.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n call void @__quantum__qis__foo__body(ptr null)\n br label %measurements\nmeasurements:\n\n br label %output\noutput:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"base_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"0\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/qubit_past_count.ll", + "format": "qir", + "expect": { + "refused": "Q707", + "line": 8 + }, + "text": "; Qubit 4 in a module that declares one qubit.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n call void @__quantum__qis__h__body(ptr inttoptr (i64 4 to ptr))\n br label %measurements\nmeasurements:\n\n br label %output\noutput:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"base_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"0\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/unknown_profile.ll", + "format": "qir", + "expect": { + "refused": "Q703", + "line": 3 + }, + "text": "; A profile the reader does not take.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n call void @__quantum__qis__h__body(ptr null)\n br label %measurements\nmeasurements:\n\n br label %output\noutput:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"custom_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"0\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/major_version.ll", + "format": "qir", + "expect": { + "refused": "Q701" + }, + "text": "; A QIR major version the reader does not take.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n call void @__quantum__qis__h__body(ptr null)\n br label %measurements\nmeasurements:\n\n br label %output\noutput:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"base_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"0\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 3}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/not_llvm.ll", + "format": "qir", + "expect": { + "refused": "Q700" + }, + "text": "; Not LLVM text: no function, no entry point.\nOPENQASM 3.0;\nqubit q;\n" + }, + { + "file": "jeff/controls_after_targets.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q709" + ], + "how": "exact", + "round_trip": true + }, + "base64": "AAAAAAABAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAOUDAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQAlQIAADcBAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAAhwIAAAAAAAAAAAAAQAAAAAEABAAAAAAAAAAAADkBAAAEAAAANQEAAAwAAAAAAAAAAAAAADABAAABAAEAAAAAAAAAAAAxAQAABAAAAC0BAAAMAAAAAAAAAAAAAAAoAQAAAQABAAAAAAAAAAAAKQEAAAQAAAAlAQAADAAAAAAAAAAAAAAAIAEAAAEAAQAAAAAAAAAAACEBAAAMAAAAIQEAAAwAAAAAAAAAAAAAABwBAAABAAEAAAAAAAAAAAApAQAAFAAAACkBAAAUAAAAAAAAAAAAAAAkAQAAAQABAAQAAAAAAAAAMQEAAAQAAAAtAQAADAAAAAAAAAAAAAAAKAEAAAIAAAAAAAAAAAAAACkBAAAUAAAAKQEAAAwAAAAAAAAAAAAAACQBAAABAAEAAAAAAAAAAAAxAQAADAAAADEBAAAMAAAAAAAAAAAAAAAsAQAAAQABAAQAAAAAAAAAOQEAAAQAAAA1AQAADAAAAAAAAAAAAAAAMAEAAAIAAAAEAAAAAAAAADEBAAAEAAAALQEAAAwAAAAAAAAAAAAAACgBAAACAAAABAAAAAAAAAApAQAABAAAACUBAAAMAAAAAAAAAAAAAAAgAQAAAgAAAAAAAAAAAAAAIQEAACQAAAAlAQAADAAAAAAAAAAAAAAAIAEAAAEAAQAEAAAAAAAAAC0BAAAEAAAAKQEAAAwAAAAAAAAAAAAAACQBAAACAAAAAAAAAAAAAAAlAQAAJAAAACkBAAAcAAAAAAAAAAAAAAAoAQAAAQABAAQAAAAAAAAANQEAAAQAAAAxAQAADAAAAAAAAAAAAAAALAEAAAIAAAAAAAAAAAAAAC0BAAAUAAAALQEAAAwAAAAAAAAAAAAAACgBAAABAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAwAAAAQAAAAFAAAABgAAAAAAAAAAAAAAAgABAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAEAAAAzMzMzMzPTPwIAAAAGAAAABwAAAAAAAAAGAAAAAAAAAAAAAAACAAEABgAAAAAAAAEDAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAIAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQADAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAJAAAAAAAAAAAAAAABAAAAmpmZmZmZyT8KAAAAAAAAAAAAAAABAAAAmpmZmZmZ2T8LAAAAAAAAAAAAAAABAAAAMzMzMzMz478EAAAACQAAAAoAAAALAAAADAAAAAAAAAAGAAAAAAAAAAAAAAACAAEACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQAAAAAAAAAAAAAAAQAAAM3MzMzMzOw/BwAAAAgAAAAMAAAADQAAAA4AAAAPAAAAEAAAAAAAAAAGAAAAAAAAAAAAAAACAAEABQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAQAAAAAAAAAAANA/DwAAABEAAAASAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQANAAAAAAABAAAAAAAAAAAAAAAAAAAAAABMAAAAAAACAJQAAAACAAAAAAAAAAAAAACUAAAAAgAAAAAAAAAAAAAAlAAAAAIAAAAAAAAAAAAAAJQAAAACAAAAAAAAAAAAAACUAAAAAgAAAAAAAAAAAAAAlAAAAAIAAAAAAAAAAAAAAJQAAAACAAAAAAAAAAAAAACUAAAAAgAAAAAAAAAAAAAAlAAAAAIAAAAAAAAAAAAAAJQAAAACAAAAAAAAAAAAAACUAAAAAgAAAAAAAAAAAAAAlAAAAAIAAAAAAAAAAAAAAJQAAAACAAAAAAAAAAAAAACUAAAAAgAAAAAAAAAAAAAAlAAAAAIAAAAAAAAAAAAAAJQAAAACAAAAAAAAAAAAAACUAAAAAgAAAAAAAAAAAAAAlAAAAAIAAAAAAAAAAAAAAJQAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAACoAAABtYWluAAAAAA==", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nh q[0];\ncx q[0], q[1];\nrx(-0.9) q[2];\nsdg q[0];\nU(0.2, 0.4, -0.6) q[1];\nctrl(2) @ p(0.9) q[0], q[1], q[2];\np(0.25) q[0];\n" + }, + { + "file": "jeff/custom_named.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q709", + "Q711" + ], + "how": "exact", + "round_trip": true + }, + "base64": "AAAAAHYAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAJkBAAAuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQACQEAAHcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAA9wAAAAAAAAAAAAAAGAAAAAEABAAAAAAAAAAAAHEAAAAEAAAAbQAAAAwAAAAAAAAAAAAAAGgAAAABAAEAAAAAAAAAAABpAAAABAAAAGUAAAAMAAAAAAAAAAAAAABgAAAAAQABAAAAAAAAAAAAYQAAAAwAAABhAAAADAAAAAAAAAAAAAAAXAAAAAEAAQAAAAAAAAAAAGkAAAAUAAAAaQAAABQAAAAAAAAAAAAAAGQAAAABAAEABAAAAAAAAABxAAAABAAAAG0AAAAMAAAAAAAAAAAAAABoAAAAAgAAAAAAAAAAAAAAaQAAABQAAABpAAAADAAAAAAAAAAAAAAAZAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABgAAAAAAAAAAAAAAAgABAAEAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAABAAAAAwAAAAQAAAAGAAAAAAAAAAAAAAACAAEAAgABAAIAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAQAAAAAAAAAAAOA/BAAAAAUAAAAGAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQADAAEAAQEAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAACADQAAAACAAAAAAAAAAAAAAA0AAAAAgAAAAAAAAAAAAAANAAAAAIAAAAAAAAAAAAAADQAAAACAAAAAAAAAAAAAAA0AAAAAgAAAAAAAAAAAAAANAAAAAIAAAAAAAAAAAAAADQAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAACoAAAARAAAAEgAAABEAAAAaAAAAEQAAABoAAAARAAAASgAAAG1haW4AAAAASAAAAAAAAABDWAAAAAAAAFJ4AAAAAAAASGFkYW1hcmQAAAAAAAAAAA==", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nh q[0];\ncx q[0], q[1];\nrx(0.5) q[1];\n" + }, + { + "file": "jeff/pauli_product.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q709", + "Q712" + ], + "how": "exact", + "round_trip": true + }, + "base64": "AAAAAGAAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAGUBAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQA1QAAAHcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAAzwAAAAAAAAAAAAAAFAAAAAEABAAAAAAAAAAAAF0AAAAEAAAAWQAAAAwAAAAAAAAAAAAAAFQAAAABAAEAAAAAAAAAAABVAAAABAAAAFEAAAAMAAAAAAAAAAAAAABMAAAAAQABAAAAAAAAAAAATQAAAAQAAABJAAAADAAAAAAAAAAAAAAARAAAAAEAAQAEAAAAAAAAAEUAAAAEAAAAQQAAAAwAAAAAAAAAAAAAADwAAAACAAAAAAAAAAAAAAA9AAAAJAAAAEEAAAAcAAAAAAAAAAAAAABAAAAAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAEAAACuR+F6FK7XPwAAAAABAAAAAgAAAAMAAAAEAAAABQAAAAYAAAAAAAAABgAAAAAAAAAAAAAAAgABAAAAAgAAAAAAAAAAAAAAAAABAAAAGwAAAAEAAgADAAAAHAAAAAAAAgA0AAAAAgAAAAAAAAAAAAAANAAAAAIAAAAAAAAAAAAAADQAAAACAAAAAAAAAAAAAAA0AAAAAgAAAAAAAAAAAAAANAAAAAIAAAAAAAAAAAAAADQAAAACAAAAAAAAAAAAAAA0AAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAqAAAAbWFpbgAAAAA=", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\n// exp(0.37i X⊗Y⊗Z), with its parity carried along a chain rather than gathered on the last qubit.\nqubit[3] q;\nh q[0];\nsdg q[1];\nh q[1];\ncx q[0], q[1];\ncx q[1], q[2];\nrz(-0.74) q[2];\ncx q[1], q[2];\ncx q[0], q[1];\nh q[1];\ns q[1];\nh q[0];\n" + }, + { + "file": "jeff/refuse/custom_unknown.jeff", + "format": "jeff", + "expect": { + "refused": "Q710", + "at": "operation 1" + }, + "base64": "AAAAADoAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAKkAAAAuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQAaQAAACcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAAVwAAAAAAAAAAAAAACAAAAAEABAAAAAAAAAAAACEAAAAEAAAAHQAAAAwAAAAAAAAAAAAAABgAAAABAAEAAAAAAAAAAAAZAAAADAAAABkAAAAMAAAAAAAAAAAAAAAUAAAAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAGAAAAAAAAAAAAAAACAAEABAABAAEAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAgAMAAAAAgAAAAAAAAAAAAAADAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAACoAAAARAAAAEgAAABEAAAAaAAAAEQAAABoAAAARAAAASgAAAG1haW4AAAAASAAAAAAAAABDWAAAAAAAAFJ4AAAAAAAASGFkYW1hcmQAAAAAAAAAAA==" + }, + { + "file": "jeff/refuse/custom_wrong_qubits.jeff", + "format": "jeff", + "expect": { + "refused": "Q710", + "at": "operation 1" + }, + "base64": "AAAAADoAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAKkAAAAuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQAaQAAACcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAAVwAAAAAAAAAAAAAACAAAAAEABAAAAAAAAAAAACEAAAAEAAAAHQAAAAwAAAAAAAAAAAAAABgAAAABAAEAAAAAAAAAAAAZAAAADAAAABkAAAAMAAAAAAAAAAAAAAAUAAAAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAGAAAAAAAAAAAAAAACAAEAAgABAAEAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAgAMAAAAAgAAAAAAAAAAAAAADAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAACoAAAARAAAAEgAAABEAAAAaAAAAEQAAABoAAAARAAAASgAAAG1haW4AAAAASAAAAAAAAABDWAAAAAAAAFJ4AAAAAAAASGFkYW1hcmQAAAAAAAAAAA==" + }, + { + "file": "jeff/refuse/qubit_twice.jeff", + "format": "jeff", + "expect": { + "refused": "Q713", + "at": "operation 2" + }, + "base64": "AAAAAEEAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAOkAAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQAmQAAADcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAAfwAAAAAAAAAAAAAADAAAAAEABAAAAAAAAAAAADUAAAAEAAAAMQAAAAwAAAAAAAAAAAAAACwAAAABAAEAAAAAAAAAAAAtAAAADAAAAC0AAAAMAAAAAAAAAAAAAAAoAAAAAQABAAAAAAAAAAAANQAAAAwAAAA1AAAADAAAAAAAAAAAAAAAMAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAABgAAAAAAAAAAAAAAAgABAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAGAAAAAAAAAAAAAAACAAEACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAgAUAAAAAgAAAAAAAAAAAAAAFAAAAAIAAAAAAAAAAAAAABQAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAKgAAAG1haW4AAAAA" + }, + { + "file": "jeff/refuse/old_version.jeff", + "format": "jeff", + "expect": { + "refused": "Q701" + }, + "base64": "AAAAACEAAAAAAAAAAgAFAAAAAAAAAAAAAQAAAAAAAAARAAAAJwAAAGkAAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQAOQAAABcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAALwAAAAAAAAAAAAAABAAAAAEABAAAAAAAAAAAAA0AAAAEAAAACQAAAAwAAAAAAAAAAAAAAAQAAAABAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAgAEAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAqAAAAbWFpbgAAAAA=" + }, + { + "file": "jeff/refuse/angle_from_measurement.jeff", + "format": "jeff", + "expect": { + "refused": "Q714", + "at": "operation 4" + }, + "base64": "AAAAAGcAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAIEBAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQAAQEAAGcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAA9wAAAAAAAAAAAAAAGAAAAAEABAAAAAAAAAAAAHEAAAAEAAAAbQAAAAwAAAAAAAAAAAAAAGgAAAABAAEAAAAAAAAAAABpAAAABAAAAGUAAAAMAAAAAAAAAAAAAABgAAAAAQABAAAAAAAAAAAAYQAAAAwAAABhAAAADAAAAAAAAAAAAAAAXAAAAAEAAQAAAAAAAAAAAGkAAAAMAAAAaQAAAAwAAAAAAAAAAAAAAGQAAAABAAEAAgAAAAAAAABlAAAADAAAAGUAAAAMAAAAAAAAAAAAAABgAAAAAgAAAAAAAAAAAAAAYQAAABQAAABhAAAADAAAAAAAAAAAAAAAXAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABgAAAAAAAAAAAAAAAgABAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAEAAAAAAAAAAAAIgAAAAAAAAAAAAAAAAABAAAABAAAAAUAAAAAAAAABgAAAAAAAAAAAAAAAgABAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAAAIALAAAAAIAAAAAAAAAAAAAACwAAAACAAAAAAAAAAAAAAAsAAAAAgAAAAAAAAAAAAAALAAAAAIAAAAAAAAAAAAAACwAAAACAAAAAAAAAAAAAAAsAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAACoAAABtYWluAAAAAA==" + }, + { + "file": "jeff/refuse/truncated.jeff", + "format": "jeff", + "expect": { + "refused": "Q700" + }, + "base64": "AAAAAHYAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAJkBAAAuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQACQEAAHcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAA9wAAAAAAAAAAAAAAGAAAAAEABAAAAAAAAAAAAHEAAAAEAAAAbQAAAAwAAAAAAAAAAAAAAGgAAAABAAEAAAAAAAAAAAA=" + }, + { + "file": "jeff/upstream/ghz-linear_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "how": "tableau", + "outcomes": [ + "000", + "111" + ], + "round_trip": true + }, + "base64": "AAAAAHABAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAAZAAAAJwAAAA0AAAAOAAAAAAAAAAAAAACdBQAAOgAAAJ0FAAAyAAAAAQAAACoAAABtYWluAAAAAAQAAAABAAMAAAAAAAAAAAAIAAAAAAAEAF0DAAAnAgAAAAAAAAAAAAANAAAABAAAAE0DAAAMAAAABQAAAL8BAAAAAAAAAAAAACwAAAABAAQAAgAAAAAAAADdAAAABAAAANkAAAAMAAAAAAAAAAAAAADIAAAAAgAAAAIAAAAAAAAA1QAAAAQAAADRAAAADAAAAAAAAAAAAAAAwAAAAAIAAAACAAAAAAAAAM0AAAAEAAAAyQAAAAwAAAAAAAAAAAAAALgAAAACAAAAAQAAAAAAAADBAAAADAAAAMEAAAAMAAAAAAAAAAAAAACwAAAAAQAAAAMAAAAAAAAAvQAAAAwAAAC9AAAADAAAAAAAAAAAAAAAqAAAAAEAAQABAAAAAAAAALUAAAAUAAAAtQAAABQAAAAAAAAAAAAAAKQAAAABAAAAAAAAAAAAAAC9AAAADAAAAL0AAAAMAAAAAAAAAAAAAACcAAAAAQABAAEAAAAAAAAAtQAAABwAAAC5AAAADAAAAAAAAAAAAAAApAAAAAEAAAAGAAAAAAAAAK0AAAAsAAAAtQAAABQAAAAAAAAAAAAAALAAAAABAAIABgAAAAAAAACxAQAALAAAALkBAAAUAAAAAAAAAAAAAAC0AQAAAQACAAEAAAAAAAAAbQIAAAwAAABtAgAABAAAAAAAAAAAAAAAXAIAAAEAAAAAAAMAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAFAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAIAAAAAAAAAAwAAAAIAAAAFAAAABgAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAcAAAAAAAAAAwAAAAAAAAAFAAAAAgAAAAcAAAAAAAAACAAAAAAAAAABAAAAAAAAAAEAAAAIAAAAAQAAAAAAAAAJAAAACgAAAAEAAAAAAAAABAAAAAAABAAAAAAAAAAAAA0AAAAcAAAA8QAAABQAAAANAAAA9wAAAAAAAAAAAAAACwAAAAwAAAANAAAAAAAAABgAAAABAAQAAgAAAAAAAAB5AAAAFAAAAHkAAAAMAAAAAAAAAAAAAABkAAAAAgAAAAEAAAAAAAAAcQAAABQAAABxAAAAFAAAAAAAAAAAAAAAYAAAAAEAAAABAAAAAAAAAGkAAAAUAAAAaQAAABQAAAAAAAAAAAAAAFgAAAABAAAAAAAAAAAAAABxAAAAFAAAAHEAAAAUAAAAAAAAAAAAAABQAAAAAQABAAEAAAAAAAAAaQAAABwAAABtAAAADAAAAAAAAAAAAAAAWAAAAAEAAAABAAAAAAAAAGUAAAAcAAAAaQAAAAwAAAAAAAAAAAAAAFQAAAABAAAAAAAGAAAAAAAAAAAAAAAAAAsAAAANAAAADgAAAAAAAAACAAAAAAAAAAwAAAAOAAAADwAAABAAAAACAAAAAAAAAA8AAAALAAAAEQAAABIAAAAGAAAAAAAAAAAAAAACAAEAAAAAAAAAAQABAAAAAAAAAAAAAAAAAAAAEgAAABAAAAATAAAAFAAAAAMAAAAAAAAAEQAAAAsAAAATAAAAAAAAABUAAAAAAAAAAwAAAAAAAAAVAAAADgAAABQAAAAAAAAAFgAAAAAAAAAWAAAADQAAAAIAAAAAAAAAAQAAAAkAAAAEAAAAAAAAABcAAAAYAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABwAAAClAAAAFAAAAA0AAACnAAAAAAAAAAAAAAAZAAAAGgAAABsAAAAAAAAAEAAAAAEABAABAAAAAAAAAE0AAAAUAAAATQAAABQAAAAAAAAAAAAAADwAAAABAAAAAAAAAAAAAABJAAAADAAAAEkAAAAUAAAAAAAAAAAAAAA0AAAAAQABAAEAAAAAAAAAQQAAABwAAABFAAAADAAAAAAAAAAAAAAAMAAAAAEAAAADAAAAAAAAAEEAAAAcAAAARQAAAAwAAAAAAAAAAAAAACwAAAABAAEAAgAAAAAAAAAaAAAAGQAAABwAAAAdAAAABAAAAAAAAAAAAAAAAAAAAB0AAAAAAAAAHgAAAB8AAAADAAAAAAAAABwAAAAZAAAAHgAAAAAAAAAgAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAbAAAAGQAAAB8AAAAAAAAAIQAAAAAAAAAgAAAAIQAAAAEAAAAAAAAAFwAAAAAAAAAYAAAAAAAAAIgAAAAAAAIADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAABtcXQtY2MAADQuMC4wAAAA", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\n// From the benchmark's source: h on the first qubit, then each qubit controls x on the next.\nqubit[3] q;\nbit[3] c;\nh q[0];\ncx q[0], q[1];\ncx q[1], q[2];\nc = measure q;\n" + }, + { + "file": "jeff/upstream/ghz-star_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "how": "tableau", + "outcomes": [ + "000", + "111" + ], + "round_trip": true + }, + "base64": "AAAAAGMBAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAAZAAAAJwAAAA0AAAAOAAAAAAAAAAAAAABpBQAAOgAAAGkFAAAyAAAAAQAAACoAAABtYWluAAAAAAQAAAABAAMAAAAAAAAAAAAIAAAAAAAEADkDAAAXAgAAAAAAAAAAAAANAAAABAAAACkDAAAMAAAABQAAAL8BAAAAAAAAAAAAACwAAAABAAQAAgAAAAAAAADdAAAABAAAANkAAAAMAAAAAAAAAAAAAADIAAAAAgAAAAIAAAAAAAAA1QAAAAQAAADRAAAADAAAAAAAAAAAAAAAwAAAAAIAAAACAAAAAAAAAM0AAAAEAAAAyQAAAAwAAAAAAAAAAAAAALgAAAACAAAAAQAAAAAAAADBAAAADAAAAMEAAAAMAAAAAAAAAAAAAACwAAAAAQAAAAMAAAAAAAAAvQAAAAwAAAC9AAAADAAAAAAAAAAAAAAAqAAAAAEAAQABAAAAAAAAALUAAAAUAAAAtQAAABQAAAAAAAAAAAAAAKQAAAABAAAAAAAAAAAAAAC9AAAADAAAAL0AAAAMAAAAAAAAAAAAAACcAAAAAQABAAEAAAAAAAAAtQAAABwAAAC5AAAADAAAAAAAAAAAAAAApAAAAAEAAAAGAAAAAAAAAK0AAAAsAAAAtQAAABQAAAAAAAAAAAAAALAAAAABAAIABgAAAAAAAACNAQAALAAAAJUBAAAUAAAAAAAAAAAAAACQAQAAAQACAAEAAAAAAAAASQIAAAwAAABJAgAABAAAAAAAAAAAAAAAOAIAAAEAAAAAAAMAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAFAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAIAAAAAAAAAAwAAAAIAAAAFAAAABgAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAcAAAAAAAAAAwAAAAAAAAAFAAAAAgAAAAcAAAAAAAAACAAAAAAAAAABAAAAAAAAAAEAAAAIAAAAAgAAAAAAAAAJAAAACgAAAAEAAAAAAAAABAAAAAAABAAAAAAAAAAAAA0AAAAcAAAAzQAAABQAAAANAAAAzwAAAAAAAAAAAAAACwAAAAwAAAANAAAAAAAAABQAAAABAAQAAQAAAAAAAABhAAAAFAAAAGEAAAAUAAAAAAAAAAAAAABQAAAAAQAAAAEAAAAAAAAAWQAAABQAAABZAAAAFAAAAAAAAAAAAAAASAAAAAEAAAAAAAAAAAAAAGEAAAAUAAAAYQAAABQAAAAAAAAAAAAAAEAAAAABAAEAAQAAAAAAAABZAAAAHAAAAF0AAAAMAAAAAAAAAAAAAABIAAAAAQAAAAEAAAAAAAAAVQAAABwAAABZAAAADAAAAAAAAAAAAAAARAAAAAEAAAACAAAAAAAAAAwAAAANAAAADgAAAA8AAAACAAAAAAAAAA4AAAALAAAAEAAAABEAAAAGAAAAAAAAAAAAAAACAAEAAAAAAAAAAQABAAAAAAAAAAAAAAAAAAAAEQAAAA8AAAASAAAAEwAAAAMAAAAAAAAAEAAAAAsAAAASAAAAAAAAABQAAAAAAAAAAwAAAAAAAAAUAAAADQAAABMAAAAAAAAAFQAAAAAAAAAVAAAADQAAAAIAAAAAAAAAAQAAAAkAAAAEAAAAAAAAABYAAAAXAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABwAAAClAAAAFAAAAA0AAACnAAAAAAAAAAAAAAAYAAAAGQAAABoAAAAAAAAAEAAAAAEABAABAAAAAAAAAE0AAAAUAAAATQAAABQAAAAAAAAAAAAAADwAAAABAAAAAAAAAAAAAABJAAAADAAAAEkAAAAUAAAAAAAAAAAAAAA0AAAAAQABAAEAAAAAAAAAQQAAABwAAABFAAAADAAAAAAAAAAAAAAAMAAAAAEAAAADAAAAAAAAAEEAAAAcAAAARQAAAAwAAAAAAAAAAAAAACwAAAABAAEAAgAAAAAAAAAZAAAAGAAAABsAAAAcAAAABAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAHQAAAB4AAAADAAAAAAAAABsAAAAYAAAAHQAAAAAAAAAfAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAaAAAAGAAAAB4AAAAAAAAAIAAAAAAAAAAfAAAAIAAAAAEAAAAAAAAAFgAAAAAAAAAXAAAAAAAAAIQAAAAAAAIABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAbXF0LWNjAAA0LjAuMAAAAA==", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\n// From the benchmark's source: h on the first qubit, which controls x on each other.\nqubit[3] q;\nbit[3] c;\nh q[0];\ncx q[0], q[1];\ncx q[0], q[2];\nc = measure q;\n" + }, + { + "file": "jeff/upstream/qft_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "how": "exact", + "round_trip": true + }, + "base64": "AAAAAEECAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAAZAAAAJwAAAA0AAAAOAAAAAAAAAAAAAADhCAAAOgAAAOEIAAAyAAAAAQAAACoAAABtYWluAAAAAAQAAAABAAMAAAAAAAAAAAAIAAAAAAAEAOEEAADnAwAAAAAAAAAAAAANAAAABAAAANEEAAAMAAAABQAAAL8BAAAAAAAAAAAAACwAAAABAAQABAAAAAAAAADdAAAABAAAANkAAAAMAAAAAAAAAAAAAADIAAAAAgAAAAQAAAAAAAAA1QAAAAQAAADRAAAADAAAAAAAAAAAAAAAwAAAAAIAAAACAAAAAAAAAM0AAAAEAAAAyQAAAAwAAAAAAAAAAAAAALgAAAACAAAAAgAAAAAAAADFAAAABAAAAMEAAAAMAAAAAAAAAAAAAACwAAAAAgAAAAIAAAAAAAAAvQAAAAQAAAC5AAAADAAAAAAAAAAAAAAAqAAAAAIAAAACAAAAAAAAALUAAAAEAAAAsQAAAAwAAAAAAAAAAAAAAKAAAAACAAAAAQAAAAAAAACpAAAADAAAAKkAAAAMAAAAAAAAAAAAAACYAAAAAQAAAAMAAAAAAAAApQAAAAwAAAClAAAADAAAAAAAAAAAAAAAkAAAAAEAAQAGAAAAAAAAAJkAAABMAAAAqQAAADQAAAAAAAAAAAAAAKwAAAABAAIABgAAAAAAAAAJAwAANAAAABEDAAAcAAAAAAAAAAAAAAAQAwAAAQACAAEAAAAAAAAA8QMAAAwAAADxAwAABAAAAAAAAAAAAAAA4AMAAAEAAAAAAAAAAQAAAAAAAAAAAOA/AAAAAAAAAAAAAAAAAQAAABgtRFT7Ifk/AQAAAAAAAAAAAAMAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAMAAQAAAAAAAAAAAAAABAAAAAAAAAAAAAMAAwAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAUAAAAAAAAABgAAAAAAAAAFAAEAAAAAAAAAAAAAAAAABQAAAAAAAAAHAAAAAAAAAAMAAAAFAAAABAAAAAYAAAACAAAABAAAAAAAAAADAAAAAQAAAAAAAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAABAAAAAAAAAAQAAAAAAAQAAAAAAAAAAAANAAAAPAAAAEUCAAA0AAAAFQAAAPcAAAAAAAAAAAAAAA4AAAAPAAAAEAAAABEAAAASAAAAEwAAABQAAAAAAAAAGAAAAAEABAACAAAAAAAAAHkAAAAUAAAAeQAAAAwAAAAAAAAAAAAAAGQAAAACAAAAAQAAAAAAAABxAAAAFAAAAHEAAAAUAAAAAAAAAAAAAABgAAAAAQAAAAAAAAAAAAAAeQAAAAwAAAB5AAAADAAAAAAAAAAAAAAAWAAAAAEAAQABAAAAAAAAAHEAAAAcAAAAdQAAAAwAAAAAAAAAAAAAAGAAAAABAAAAAgAAAAAAAABxAAAAFAAAAHEAAAAMAAAAAAAAAAAAAABcAAAAAgAAAAYAAAAAAAAAZQAAAEQAAABxAAAALAAAAAAAAAAAAAAAdAAAAAEAAgAAAAYAAAAAAAAAAAAAAAAAEAAAAA4AAAAVAAAAAAAAAAIAAAAAAAAADwAAABUAAAAWAAAAFwAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAXAAAAAAAAABgAAAAAAAAAAwAAAAAAAAAWAAAAFQAAABgAAAAAAAAAGQAAAAAAAAAAAAYAAAAAAAAAAAAAAAAAEQAAAA4AAAAaAAAAAAAAABMAAAAVAAAAEQAAABQAAAAZAAAAGgAAABUAAAASAAAAGwAAABwAAAAdAAAAHgAAAB8AAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAADQAAAAdAQAALAAAABEAAAAfAQAAAAAAAAAAAAAgAAAAIQAAACIAAAAjAAAAJAAAACUAAAAcAAAAAQAEAAIAAAAAAAAAjQAAABQAAACNAAAADAAAAAAAAAAAAAAAeAAAAAIAAAABAAAAAAAAAIUAAAAUAAAAhQAAABQAAAAAAAAAAAAAAHQAAAABAAAAAQAAAAAAAAB9AAAAFAAAAH0AAAAUAAAAAAAAAAAAAABsAAAAAQAAAAAAAAAAAAAAhQAAABwAAACJAAAAFAAAAAAAAAAAAAAAZAAAAAEAAQABAAAAAAAAAIEAAAAcAAAAhQAAAAwAAAAAAAAAAAAAAHAAAAABAAAAAQAAAAAAAAB9AAAAHAAAAIEAAAAMAAAAAAAAAAAAAABsAAAAAQAAAAQAAAAAAAAAfQAAABQAAAB9AAAADAAAAAAAAAAAAAAAaAAAAAIAAAAAAAYAAAAAAAAAAAAAAAAAIwAAACAAAAAmAAAAAAAAAAIAAAAAAAAAIgAAACYAAAAnAAAAKAAAAAIAAAAAAAAAJwAAACQAAAApAAAAKgAAAAYAAAAAAAAAAAAAAAIAAQAFAAAAAAABAAEAAAAAAAAAAAAAAAAAAAAqAAAAKAAAACEAAAAAAAAAKwAAACwAAAADAAAAAAAAACkAAAAkAAAAKwAAAAAAAAAtAAAAAAAAAAMAAAAAAAAALQAAACYAAAAsAAAAAAAAAC4AAAAAAAAAAAAAAAQAAAAAAAAAAAAAACEAAAAlAAAALwAAAAAAAAAvAAAALgAAACMAAAAkAAAAJQAAAAAAAAAcAAAAEAAAABEAAAASAAAAEwAAABQAAAADAAAABQAAAAQAAAAIAAAAAgAAAAcAAAAwAAAAMQAAADIAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAACQAAADJAAAAHAAAAA0AAADPAAAAAAAAAAAAAAAzAAAANAAAADUAAAA2AAAAFAAAAAEABAACAAAAAAAAAGUAAAAUAAAAZQAAAAwAAAAAAAAAAAAAAFAAAAACAAAAAQAAAAAAAABdAAAAFAAAAF0AAAAUAAAAAAAAAAAAAABMAAAAAQAAAAAAAAAAAAAAWQAAAAwAAABZAAAAFAAAAAAAAAAAAAAARAAAAAEAAQABAAAAAAAAAFEAAAAcAAAAVQAAAAwAAAAAAAAAAAAAAEAAAAABAAAAAwAAAAAAAABRAAAAHAAAAFUAAAAMAAAAAAAAAAAAAAA8AAAAAQABAAAABgAAAAAAAAAAAAAAAAA1AAAAMwAAADcAAAAAAAAAAgAAAAAAAAA0AAAAMwAAADgAAAA5AAAABAAAAAAAAAAAAAAAAAAAADkAAAAAAAAAOgAAADsAAAADAAAAAAAAADgAAAAzAAAAOgAAAAAAAAA8AAAAAAAAAAcAAAAAAAAAAAAAAAAAAAA2AAAANwAAADsAAAAAAAAAPQAAAAAAAAA8AAAANQAAAD0AAAAAAAAAAQAAAAAAAAAwAAAAAAAAADIAAAAAAAAA+AAAAAAAAgDsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAwABAAEAAAADAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAbXF0LWNjAAA0LjAuMAAAAA==", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\n// From the benchmark's source: the textbook QFT on three qubits, qubit i measured into bit 2 - i.\nqubit[3] q;\nbit[3] c;\nh q[2];\ncp(pi/2) q[1], q[2];\ncp(pi/4) q[0], q[2];\nh q[1];\ncp(pi/2) q[0], q[1];\nh q[0];\nc[2] = measure q[0];\nc[1] = measure q[1];\nc[0] = measure q[2];\n" + }, + { + "file": "jeff/upstream/grover_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "outcomes": [ + "11" + ], + "round_trip": true + }, + "base64": "AAAAANMCAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAAZAAAAJwAAAA0AAAAOAAAAAAAAAAAAAAApCwAAOgAAACkLAAAyAAAAAQAAACoAAABtYWluAAAAAAQAAAABAAMAAAAAAAAAAAAIAAAAAAAEANkGAAA3BAAAAAAAAAAAAAANAAAABAAAAMkGAAAMAAAABQAAAHcDAAAAAAAAAAAAAFgAAAABAAQAAgAAAAAAAAC5AQAABAAAALUBAAAMAAAAAAAAAAAAAACkAQAAAgAAAAIAAAAAAAAAsQEAAAQAAACtAQAADAAAAAAAAAAAAAAAnAEAAAIAAAACAAAAAAAAAKkBAAAEAAAApQEAAAwAAAAAAAAAAAAAAJQBAAACAAAAAQAAAAAAAACdAQAADAAAAJ0BAAAMAAAAAAAAAAAAAACMAQAAAQAAAAMAAAAAAAAAmQEAAAwAAACZAQAADAAAAAAAAAAAAAAAhAEAAAEAAQAGAAAAAAAAAI0BAAAkAAAAkQEAAAwAAAAAAAAAAAAAAIwBAAABAAIAAQAAAAAAAAAlAgAAFAAAACUCAAAUAAAAAAAAAAAAAAAUAgAAAQAAAAEAAAAAAAAAHQIAABQAAAAdAgAAFAAAAAAAAAAAAAAADAIAAAEAAAAAAAAAAAAAACUCAAAUAAAAJQIAABQAAAAAAAAAAAAAAAQCAAABAAEAAQAAAAAAAAAdAgAAHAAAACECAAAMAAAAAAAAAAAAAAAMAgAAAQAAAAEAAAAAAAAAGQIAABwAAAAdAgAADAAAAAAAAAAAAAAACAIAAAEAAAAGAAAAAAAAABECAAAkAAAAFQIAAAwAAAAAAAAAAAAAABACAAABAAIABgAAAAAAAAClAgAAJAAAAKkCAAAMAAAAAAAAAAAAAACkAgAAAQACAAEAAAAAAAAAPQMAABQAAAA9AwAAFAAAAAAAAAAAAAAALAMAAAEAAAABAAAAAAAAADUDAAAUAAAANQMAABQAAAAAAAAAAAAAACQDAAABAAAAAAAAAAAAAAA9AwAAFAAAAD0DAAAUAAAAAAAAAAAAAAAcAwAAAQABAAEAAAAAAAAANQMAABwAAAA5AwAADAAAAAAAAAAAAAAAJAMAAAEAAAABAAAAAAAAADEDAAAcAAAANQMAAAwAAAAAAAAAAAAAACADAAABAAAABgAAAAAAAAApAwAAJAAAAC0DAAAMAAAAAAAAAAAAAAAoAwAAAQACAAYAAAAAAAAAvQMAACQAAADBAwAADAAAAAAAAAAAAAAAvAMAAAEAAgAGAAAAAAAAAFEEAAAsAAAAWQQAABQAAAAAAAAAAAAAAFQEAAABAAIAAQAAAAAAAAANBQAADAAAAA0FAAAEAAAAAAAAAAAAAAD8BAAAAQAAAAAAAwACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwABAAAAAAAAAAAAAAABAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAUAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAgAAAAAAAAABAAAAAwAAAAUAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABQAAACFAAAADAAAAAkAAAB/AAAAAAAAAAAAAAAGAAAABwAAAAwAAAABAAQAAQAAAAAAAAA5AAAAFAAAADkAAAAUAAAAAAAAAAAAAAAoAAAAAQAAAAAAAAAAAAAAQQAAAAwAAABBAAAADAAAAAAAAAAAAAAAIAAAAAEAAQABAAAAAAAAADkAAAAcAAAAPQAAAAwAAAAAAAAAAAAAACgAAAABAAAAAgAAAAAAAAAHAAAABgAAAAgAAAAJAAAABgAAAAAAAAAAAAAAAgABAAkAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAkAAAAAAAAACgAAAAAAAAADAAAAAAAAAAgAAAAGAAAACgAAAAAAAAALAAAAAAAAAAsAAAAAAAAAAgAAAAAAAAAFAAAAAgAAAAwAAAANAAAAAgAAAAAAAAAMAAAAAQAAAA4AAAAPAAAABgAAAAAAAAAAAAAAAgABAAIAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAA8AAAANAAAAEAAAABEAAAADAAAAAAAAAA4AAAABAAAAEAAAAAAAAAASAAAAAAAAAAMAAAAAAAAAEgAAAAIAAAARAAAAAAAAABMAAAAAAAAAAgAAAAAAAAABAAAAEwAAABQAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABQAAACFAAAADAAAAAkAAAB/AAAAAAAAAAAAAAAVAAAAFgAAAAwAAAABAAQAAQAAAAAAAAA5AAAAFAAAADkAAAAUAAAAAAAAAAAAAAAoAAAAAQAAAAAAAAAAAAAAQQAAAAwAAABBAAAADAAAAAAAAAAAAAAAIAAAAAEAAQABAAAAAAAAADkAAAAcAAAAPQAAAAwAAAAAAAAAAAAAACgAAAABAAAAAgAAAAAAAAAWAAAAFQAAABcAAAAYAAAABgAAAAAAAAAAAAAAAgABAAkAAAAAAAAAAQAAAAAAAAAAAAAAAAAAABgAAAAAAAAAGQAAAAAAAAADAAAAAAAAABcAAAAVAAAAGQAAAAAAAAAaAAAAAAAAABoAAAAAAAAAAgAAAAAAAAABAAAAFAAAABsAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABQAAACFAAAADAAAAAkAAAB/AAAAAAAAAAAAAAAcAAAAHQAAAAwAAAABAAQAAQAAAAAAAAA5AAAAFAAAADkAAAAUAAAAAAAAAAAAAAAoAAAAAQAAAAAAAAAAAAAAQQAAAAwAAABBAAAADAAAAAAAAAAAAAAAIAAAAAEAAQABAAAAAAAAADkAAAAcAAAAPQAAAAwAAAAAAAAAAAAAACgAAAABAAAAAgAAAAAAAAAdAAAAHAAAAB4AAAAfAAAABgAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAB8AAAAAAAAAIAAAAAAAAAADAAAAAAAAAB4AAAAcAAAAIAAAAAAAAAAhAAAAAAAAACEAAAAAAAAAAgAAAAAAAAAbAAAAAgAAACIAAAAjAAAAAgAAAAAAAAAiAAAAAQAAACQAAAAlAAAABgAAAAAAAAAAAAAAAgABAAIAAAAAAAEAAQAAAAAAAAAAAAAAAAAAACUAAAAjAAAAJgAAACcAAAADAAAAAAAAACQAAAABAAAAJgAAAAAAAAAoAAAAAAAAAAMAAAAAAAAAKAAAAAIAAAAnAAAAAAAAACkAAAAAAAAAAgAAAAAAAAABAAAAKQAAACoAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABQAAACFAAAADAAAAAkAAAB/AAAAAAAAAAAAAAArAAAALAAAAAwAAAABAAQAAQAAAAAAAAA5AAAAFAAAADkAAAAUAAAAAAAAAAAAAAAoAAAAAQAAAAAAAAAAAAAAQQAAAAwAAABBAAAADAAAAAAAAAAAAAAAIAAAAAEAAQABAAAAAAAAADkAAAAcAAAAPQAAAAwAAAAAAAAAAAAAACgAAAABAAAAAgAAAAAAAAAsAAAAKwAAAC0AAAAuAAAABgAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAC4AAAAAAAAALwAAAAAAAAADAAAAAAAAAC0AAAArAAAALwAAAAAAAAAwAAAAAAAAADAAAAAAAAAAAgAAAAAAAAABAAAAKgAAADEAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABQAAACFAAAADAAAAAkAAAB/AAAAAAAAAAAAAAAyAAAAMwAAAAwAAAABAAQAAQAAAAAAAAA5AAAAFAAAADkAAAAUAAAAAAAAAAAAAAAoAAAAAQAAAAAAAAAAAAAAQQAAAAwAAABBAAAADAAAAAAAAAAAAAAAIAAAAAEAAQABAAAAAAAAADkAAAAcAAAAPQAAAAwAAAAAAAAAAAAAACgAAAABAAAAAgAAAAAAAAAzAAAAMgAAADQAAAA1AAAABgAAAAAAAAAAAAAAAgABAAkAAAAAAAAAAQAAAAAAAAAAAAAAAAAAADUAAAAAAAAANgAAAAAAAAADAAAAAAAAADQAAAAyAAAANgAAAAAAAAA3AAAAAAAAADcAAAAAAAAAAgAAAAAAAAABAAAAMQAAAAQAAAAAAAAAOAAAADkAAAABAAAAAAAAAAQAAAAAAAQAAAAAAAAAAAANAAAAHAAAAKUAAAAUAAAADQAAAKcAAAAAAAAAAAAAADoAAAA7AAAAPAAAAAAAAAAQAAAAAQAEAAEAAAAAAAAATQAAABQAAABNAAAAFAAAAAAAAAAAAAAAPAAAAAEAAAAAAAAAAAAAAEkAAAAMAAAASQAAABQAAAAAAAAAAAAAADQAAAABAAEAAQAAAAAAAABBAAAAHAAAAEUAAAAMAAAAAAAAAAAAAAAwAAAAAQAAAAMAAAAAAAAAQQAAABwAAABFAAAADAAAAAAAAAAAAAAALAAAAAEAAQACAAAAAAAAADsAAAA6AAAAPQAAAD4AAAAEAAAAAAAAAAAAAAAAAAAAPgAAAAAAAAA/AAAAQAAAAAMAAAAAAAAAPQAAADoAAAA/AAAAAAAAAEEAAAAAAAAABwAAAAAAAAAAAAAAAAAAADwAAAA6AAAAQAAAAAAAAABCAAAAAAAAAEEAAABCAAAAAQAAAAAAAAA4AAAAAAAAADkAAAAAAAAADAEAAAAAAgAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAMAAQABAAAAAgAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAwABAAEAAAACAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAwABAAEAAAACAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAwABAAEAAAACAAAAAAAAAG1xdC1jYwAANC4wLjAAAAA=" + }, + { + "file": "jeff/upstream/teleportation.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "dynamic": true, + "outcomes": [ + "000", + "010", + "100", + "110" + ], + "writes": [ + { + "to": "qir", + "profile": "adaptive", + "notes": [ + "Q715" + ] + }, + { + "to": "jeff", + "refused": "Q720" + } + ] + }, + "base64": "AAAAAKIBAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAAZAAAAJwAAAA0AAAAOAAAAAAAAAAAAAABlBgAAOgAAAGUGAAAyAAAAAQAAACoAAABtYWluAAAAAAQAAAABAAMAAAAAAAAAAAAIAAAAAAAEAFUEAAD3AQAAAAAAAAAAAAANAAAABAAAAEUEAAAMAAAABQAAAE8DAAAAAAAAAAAAAFQAAAABAAQAAgAAAAAAAAClAQAABAAAAKEBAAAMAAAAAAAAAAAAAACQAQAAAgAAAAAAAAAAAAAAnQEAAAQAAACZAQAADAAAAAAAAAAAAAAAiAEAAAEAAQAAAAAAAAAAAJUBAAAEAAAAkQEAAAwAAAAAAAAAAAAAAIABAAABAAEAAAAAAAAAAACNAQAABAAAAIkBAAAMAAAAAAAAAAAAAAB4AQAAAQABAAIAAAAAAAAAhQEAAAQAAACBAQAADAAAAAAAAAAAAAAAcAEAAAIAAAADAAAAAAAAAH0BAAAMAAAAfQEAAAwAAAAAAAAAAAAAAGgBAAABAAEAAAAAAAAAAACFAQAADAAAAIUBAAAMAAAAAAAAAAAAAABkAQAAAQABAAAAAAAAAAAAjQEAAAwAAACNAQAADAAAAAAAAAAAAAAAbAEAAAEAAQAAAAAAAAAAAJUBAAAUAAAAlQEAABQAAAAAAAAAAAAAAHQBAAABAAEAAAAAAAAAAACdAQAAFAAAAJ0BAAAUAAAAAAAAAAAAAAB8AQAAAQABAAAAAAAAAAAApQEAAAwAAAClAQAADAAAAAAAAAAAAAAAhAEAAAEAAQAAAAAAAAAAAKEBAAAMAAAAoQEAABQAAAAAAAAAAAAAAIwBAAABAAEAAAAAAAAAAACdAQAADAAAAJ0BAAAUAAAAAAAAAAAAAACIAQAAAQABAAYAAAAAAAAAkQEAABQAAACRAQAADAAAAAAAAAAAAAAAjAEAAAEAAgAGAAAAAAAAABkCAAAUAAAAGQIAAAwAAAAAAAAAAAAAABQCAAABAAIAAAAAAAAAAAC1AgAADAAAALUCAAAMAAAAAAAAAAAAAACUAgAAAQABAAAAAAAAAAAAsQIAAAwAAACxAgAAFAAAAAAAAAAAAAAAnAIAAAEAAQADAAAAAAAAAK0CAAAcAAAAsQIAAAwAAAAAAAAAAAAAAJgCAAABAAEAAAAAAAAAAACtAgAADAAAAK0CAAAEAAAAAAAAAAAAAACYAgAAAQABAAAAAAAAAAAApQIAAAwAAAClAgAABAAAAAAAAAAAAAAAkAIAAAEAAQAAAAAAAAAAAJ0CAAAMAAAAnQIAAAQAAAAAAAAAAAAAAIgCAAABAAEAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAADAAEAAAAAAAAAAAAAAAQAAAAAAAAABQABAAAAAAAAAAAAAAAAAAQAAAAAAAAABQAAAAAAAAAGAAAAAAAAAAAAAAACAAEACQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAGAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAcAAAAAAAAABgAAAAAAAAAAAAAAAgABAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAMAAAAHAAAACAAAAAkAAAAGAAAAAAAAAAAAAAACAAEAAAAAAAAAAQABAAAAAAAAAAAAAAAAAAAACQAAAAYAAAAKAAAACwAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAADQAAAA4AAAAEAAAAAAAAAAAAAAAAAAAACgAAAAAAAAAPAAAAEAAAABAAAAAIAAAAEQAAAAAAAAAAAAAAAAAAAAUAAABHAAAAbAAAAAAABAAIAAAAAAAEAB0AAAAMAAAAIQAAAAwAAAAZAAAABwAAAAAAAAAAAAAAGQAAAAwAAABNAAAADAAAABUAAAAvAAAAAAAAAAAAAAASAAAAAAAAAAAAAAABAAQAEgAAAAAAAAATAAAAAAAAAAQAAAABAAQAAAAAAAAAAAAhAAAADAAAACEAAAAMAAAAAAAAAAAAAAAAAAAAAQABAAYAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAATAAAAAAAAABQAAAAAAAAAFAAAAAAAAAANAAAADAAAABEAAAAMAAAACQAAAAcAAAAAAAAAAAAAABUAAAAAAAAAAAAAAAEABAAVAAAAAAAAAA4AAAARAAAAFgAAAAAAAAAAAAAAAAAAAAUAAABHAAAAbAAAAAAABAAIAAAAAAAEAB0AAAAMAAAAIQAAAAwAAAAZAAAABwAAAAAAAAAAAAAAGQAAAAwAAABNAAAADAAAABUAAAAvAAAAAAAAAAAAAAAXAAAAAAAAAAAAAAABAAQAFwAAAAAAAAAYAAAAAAAAAAQAAAABAAQAAAAAAAAAAAAhAAAADAAAACEAAAAMAAAAAAAAAAAAAAAAAAAAAQABAAYAAAAAAAAAAAAAAAIAAQACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAYAAAAAAAAABkAAAAAAAAAGQAAAAAAAAANAAAADAAAABEAAAAMAAAACQAAAAcAAAAAAAAAAAAAABoAAAAAAAAAAAAAAAEABAAaAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAWAAAAAAAAABsAAAAAAAAABAAAAAAAAAAAAAAAAAAAABsAAAAAAAAAHAAAAB0AAAAHAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAdAAAAAAAAAB4AAAAAAAAAAgAAAAAAAAAAAAAAAAAAAA0AAAAAAAAAAgAAAAAAAAAAAAAAAAAAAA8AAAAAAAAAAgAAAAAAAAAAAAAAAAAAABwAAAAAAAAAHgAAAAAAAAB8AAAAAAACAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAADAAEAAQAAAAEAAAAAAAAAbXF0LWNjAAA0LjAuMAAAAA==" + }, + { + "file": "jeff/upstream/iqft_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "dynamic": true, + "writes": [ + { + "to": "qir", + "profile": "adaptive", + "notes": [ + "Q715" + ] + }, + { + "to": "jeff", + "refused": "Q720" + } + ] + }, + "base64": "AAAAAMwBAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAAZAAAAJwAAAA0AAAAOAAAAAAAAAAAAAAANBwAAOgAAAA0HAAAyAAAAAQAAACoAAABtYWluAAAAAAQAAAABAAMAAAAAAAAAAAAIAAAAAAAEAN0DAAAXAwAAAAAAAAAAAAANAAAABAAAAM0DAAAMAAAABQAAAG8BAAAAAAAAAAAAACQAAAABAAQABAAAAAAAAAC1AAAABAAAALEAAAAMAAAAAAAAAAAAAACgAAAAAgAAAAQAAAAAAAAArQAAAAQAAACpAAAADAAAAAAAAAAAAAAAmAAAAAIAAAACAAAAAAAAAKUAAAAEAAAAoQAAAAwAAAAAAAAAAAAAAJAAAAACAAAAAgAAAAAAAACdAAAABAAAAJkAAAAMAAAAAAAAAAAAAACIAAAAAgAAAAIAAAAAAAAAlQAAAAQAAACRAAAADAAAAAAAAAAAAAAAgAAAAAIAAAAAAAAAAAAAAI0AAAAEAAAAiQAAAAwAAAAAAAAAAAAAAHgAAAABAAEAAwAAAAAAAACFAAAADAAAAIUAAAAMAAAAAAAAAAAAAABwAAAAAQABAAYAAAAAAAAAeQAAAEwAAACJAAAANAAAAAAAAAAAAAAAjAAAAAEAAgAAAAAAAAAAABUDAAAMAAAAFQMAAAQAAAAAAAAAAAAAAAADAAABAAEAAAAAAAEAAAAAAAAAAADgPwAAAAAAAAAAAAAAAAEAAAAYLURU+yH5PwEAAAAAAAAAAAADAAEAAAAAAAAAAAAAAAIAAAAAAAAAAAADAAMAAAAAAAAAAAAAAAMAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAABQABAAAAAAAAAAAAAAAAAAMAAAAAAAAABgAAAAAAAAAEAAAAAwAAAAIAAAAFAAAAAgAAAAYAAAAAAAAABAAAAAEAAAAAAAAABwAAAAgAAAAJAAAACgAAAAsAAAAMAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAADwAAABpAgAANAAAABUAAAD3AAAAAAAAAAAAAAANAAAADgAAAA8AAAAQAAAAEQAAABIAAAATAAAAAAAAABgAAAABAAQAAgAAAAAAAAB5AAAAFAAAAHkAAAAMAAAAAAAAAAAAAABkAAAAAgAAAAYAAAAAAAAAbQAAAEQAAAB5AAAALAAAAAAAAAAAAAAAfAAAAAEAAgAAAAAAAAAAAOEBAAAMAAAA4QEAAAwAAAAAAAAAAAAAAMABAAABAAEAAAAAAAAAAADdAQAADAAAAN0BAAAUAAAAAAAAAAAAAADIAQAAAQABAAMAAAAAAAAA2QEAABwAAADdAQAADAAAAAAAAAAAAAAAxAEAAAEAAQAAAAAAAAAAANkBAAAMAAAA2QEAAAwAAAAAAAAAAAAAAMQBAAABAAEAAAAGAAAAAAAAAAAAAAAAAA0AAAAPAAAAFAAAAAAAAAASAAAADQAAAA8AAAATAAAADgAAABQAAAAQAAAAEQAAABUAAAAWAAAAFwAAABgAAAAZAAAAAAAAAAEAAAAAAAAABAAAAAAABAAAAAAAAAAAAA0AAAA0AAAAOQEAACwAAAARAAAApwAAAAAAAAAAAAAAGgAAABsAAAAcAAAAHQAAAB4AAAAfAAAAEAAAAAEABAACAAAAAAAAAFEAAAAUAAAAUQAAAAwAAAAAAAAAAAAAADwAAAACAAAAAwAAAAAAAABNAAAAFAAAAE0AAAAMAAAAAAAAAAAAAAA4AAAAAQABAAYAAAAAAAAAQQAAABwAAABFAAAAFAAAAAAAAAAAAAAAQAAAAAEAAgAEAAAAAAAAANUAAAAUAAAA1QAAAAwAAAAAAAAAAAAAAMAAAAACAAAAAAAGAAAAAAAAAAAAAAAAAB0AAAAaAAAAIAAAAAAAAAAGAAAAAAAAAAAAAAAAAAAAHgAAACAAAAAhAAAAAAAAACEAAAAcAAAAGwAAAAAAAAAiAAAAIwAAAAAAAAAAAAAABQAAAEcAAABsAAAAAAAEAAgAAAAAAAQAHQAAABQAAAAhAAAAFAAAABkAAAAHAAAAAAAAAAAAAAAZAAAAFAAAAE0AAAAUAAAAFQAAAC8AAAAAAAAAAAAAACQAAAAlAAAAAAAAAAEABAAkAAAAJQAAACYAAAAnAAAABAAAAAEABAAAAAAAAAAAACEAAAAUAAAAIQAAAAwAAAAAAAAAAAAAAAAAAAABAAEABgAAAAAAAAAAAAAAAgABAAUAAAAAAAAAAQAAAAAAAAAAAAAAAAAAACYAAAAnAAAAKAAAAAAAAAAoAAAAJwAAAA0AAAAUAAAAEQAAABQAAAAJAAAABwAAAAAAAAAAAAAAKQAAACoAAAAAAAAAAQAEACkAAAAqAAAAAAAAAAQAAAAAAAAAAAAAABsAAAAfAAAAKwAAAAAAAAArAAAAIgAAAB0AAAAeAAAAHwAAAAAAAAAGAAAAAAAAAAAAAAACAAEACQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAFgAAAAAAAAAsAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAsAAAAAAAAAC0AAAAuAAAABwAAAAAAAAAAAAAAAAAAABgAAAANAAAALgAAAAAAAAAvAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAtAAAAAAAAADAAAAAAAAAAMAAAAA8AAAAvAAAAEQAAABIAAAATAAAAAgAAAAAAAAAAAAAAAAAAAAcAAAAAAAAACQAAAAAAAADEAAAAAAACAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAwABAAEAAAADAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbXF0LWNjAAA0LjAuMAAAAA==" + }, + { + "file": "jeff/upstream/iqpe_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "dynamic": true, + "writes": [ + { + "to": "qir", + "profile": "adaptive", + "notes": [ + "Q715" + ] + }, + { + "to": "jeff", + "refused": "Q720" + } + ] + }, + "base64": "AAAAAGgCAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAAZAAAAJwAAAA0AAAAOAAAAAAAAAAAAAAB9CQAAOgAAAH0JAAAyAAAAAQAAACoAAABtYWluAAAAAAQAAAABAAMAAAAAAAAAAAAIAAAAAAAEAF0FAAAHBAAAAAAAAAAAAAANAAAABAAAAE0FAAAMAAAABQAAADcCAAAAAAAAAAAAADgAAAABAAQABAAAAAAAAAAZAQAABAAAABUBAAAMAAAAAAAAAAAAAAAEAQAAAgAAAAQAAAAAAAAAEQEAAAQAAAANAQAADAAAAAAAAAAAAAAA/AAAAAIAAAAFAAAAAAAAABUBAAAEAAAAEQEAAAwAAAAAAAAAAAAAAPQAAAABAAEAAgAAAAAAAAANAQAABAAAAAkBAAAMAAAAAAAAAAAAAAD4AAAAAgAAAAIAAAAAAAAABQEAAAQAAAABAQAADAAAAAAAAAAAAAAA8AAAAAIAAAACAAAAAAAAAP0AAAAEAAAA+QAAAAwAAAAAAAAAAAAAAOgAAAACAAAAAgAAAAAAAAD1AAAABAAAAPEAAAAMAAAAAAAAAAAAAADgAAAAAgAAAAAAAAAAAAAA7QAAAAQAAADpAAAADAAAAAAAAAAAAAAA2AAAAAEAAQAAAAAAAAAAAOUAAAAEAAAA4QAAAAwAAAAAAAAAAAAAANAAAAABAAEAAwAAAAAAAADdAAAADAAAAN0AAAAMAAAAAAAAAAAAAADIAAAAAQABAAAAAAAAAAAA5QAAAAwAAADlAAAADAAAAAAAAAAAAAAAxAAAAAEAAQAGAAAAAAAAANkAAABkAAAA7QAAAEwAAAAAAAAAAAAAAPgAAAABAAIAAAAAAAAAAAA5BAAADAAAADkEAAAEAAAAAAAAAAAAAAAkBAAAAQABAAAAAAAAAAAAMQQAAAwAAAAxBAAABAAAAAAAAAAAAAAAHAQAAAEAAQAAAAAAAQAAAAAAAAAAAOA/AAAAAAAAAAAAAAAAAQAAABgtRFT7Ifm/AQAAAAAAAAABAAAAAAAAAAEAAAAdAAAA0iEzf3zZ8j/SITN/fNkCQNIhM3982RJAAgAAAAAAAAAAAAMAAgAAAAAAAAAAAAAAAwAAAAAAAAAAAAMAAQAAAAAAAAAAAAAABAAAAAAAAAAAAAMAAwAAAAAAAAAAAAAABQAAAAAAAAAAAAMAAAAAAAAAAAAAAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAFAAEAAAAAAAAAAAAAAAAABQAAAAAAAAAJAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAoAAAAAAAAABgAAAAUAAAAEAAAABwAAAAoAAAADAAAAAgAAAAQAAAAJAAAAAAAAAAYAAAABAAAACwAAAAwAAAANAAAADgAAAA8AAAAQAAAAEQAAABIAAAATAAAAAAAAAAEAAAAAAAAABAAAAAAABAAAAAAAAAAAAA0AAABUAAAAGQMAAEwAAAAZAAAAlwEAAAAAAAAAAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAcAAAAHQAAACgAAAABAAQAAgAAAAAAAADJAAAAFAAAAMkAAAAMAAAAAAAAAAAAAAC0AAAAAgAAAAUAAAAAAAAAxQAAABQAAADFAAAADAAAAAAAAAAAAAAAsAAAAAEAAQAAAAAAAAAAAM0AAAAMAAAAzQAAAAwAAAAAAAAAAAAAAKwAAAABAAEAAAAAAAAAAADVAAAAHAAAANkAAAAUAAAAAAAAAAAAAAC0AAAAAQABAAIAAAAAAAAA1QAAABQAAADVAAAADAAAAAAAAAAAAAAAwAAAAAIAAAAGAAAAAAAAAMkAAABEAAAA1QAAACwAAAAAAAAAAAAAANgAAAABAAIAAAAAAAAAAAA9AgAADAAAAD0CAAAMAAAAAAAAAAAAAAAcAgAAAQABAAAAAAAAAAAAOQIAAAwAAAA5AgAAFAAAAAAAAAAAAAAAJAIAAAEAAQADAAAAAAAAADUCAAAcAAAAOQIAAAwAAAAAAAAAAAAAACACAAABAAEAAAAAAAAAAAA1AgAADAAAADUCAAAMAAAAAAAAAAAAAAAgAgAAAQABAAAABgAAAAAAAAAAAAAAAAAXAAAAFAAAAB4AAAAAAAAAAwAAAAAAAAAAAAAAAAAAABgAAAAeAAAAHwAAAAAAAAAGAAAAAAAAAAAAAAACAAEACQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAFQAAAAAAAAAgAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQAFAAAAAAABAAEAAAAAAAAAAAAAAAAAAAAWAAAAIAAAAB8AAAAAAAAAIQAAACIAAAAAAAYAAAAAAAAAAAAAAAAAFAAAABkAAAAjAAAAAAAAABwAAAAUAAAAGQAAAB0AAAAiAAAAIwAAABoAAAAbAAAAJAAAACUAAAAmAAAAJwAAACgAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAADQAAAA5AQAALAAAABEAAACnAAAAAAAAAAAAAAApAAAAKgAAACsAAAAsAAAALQAAAC4AAAAQAAAAAQAEAAIAAAAAAAAAUQAAABQAAABRAAAADAAAAAAAAAAAAAAAPAAAAAIAAAADAAAAAAAAAE0AAAAUAAAATQAAAAwAAAAAAAAAAAAAADgAAAABAAEABgAAAAAAAABBAAAAHAAAAEUAAAAUAAAAAAAAAAAAAABAAAAAAQACAAQAAAAAAAAA1QAAABQAAADVAAAADAAAAAAAAAAAAAAAwAAAAAIAAAAAAAYAAAAAAAAAAAAAAAAALAAAACkAAAAvAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAtAAAALwAAADAAAAAAAAAAMAAAACsAAAAqAAAAAAAAADEAAAAyAAAAAAAAAAAAAAAFAAAARwAAAGwAAAAAAAQACAAAAAAABAAdAAAAFAAAACEAAAAUAAAAGQAAAAcAAAAAAAAAAAAAABkAAAAUAAAATQAAABQAAAAVAAAALwAAAAAAAAAAAAAAMwAAADQAAAAAAAAAAQAEADMAAAA0AAAANQAAADYAAAAEAAAAAQAEAAAAAAAAAAAAIQAAABQAAAAhAAAADAAAAAAAAAAAAAAAAAAAAAEAAQAGAAAAAAAAAAAAAAACAAEABQAAAAAAAAABAAAAAAAAAAAAAAAAAAAANQAAADYAAAA3AAAAAAAAADcAAAA2AAAADQAAABQAAAARAAAAFAAAAAkAAAAHAAAAAAAAAAAAAAA4AAAAOQAAAAAAAAABAAQAOAAAADkAAAAAAAAABAAAAAAAAAAAAAAAKgAAAC4AAAA6AAAAAAAAADoAAAAxAAAALAAAAC0AAAAuAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAlAAAAAAAAADsAAAAAAAAABAAAAAAAAAAAAAAAAAAAADsAAAAAAAAAPAAAAD0AAAAHAAAAAAAAAAAAAAAAAAAAJwAAABQAAAA9AAAAAAAAAD4AAAAAAAAABQAAAAAAAAAAAAAAAAAAADwAAAAAAAAAPwAAAAAAAAA/AAAAIQAAABcAAAAYAAAAGQAAAD4AAAAbAAAAHAAAAB0AAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAsAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAEAAAAAAAAAAAAQAAAAACAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAUAAQABAAAAAwAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAUAAQABAAAAAwAAAAAAAAACACAAAAAAAAAAAAAAAAAAAwABAAEAAAADAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAFAAEAAQAAAAMAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbXF0LWNjAAA0LjAuMAAAAA==" + }, + { + "file": "jeff/upstream/qpe_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "dynamic": false, + "round_trip": true + }, + "base64": "AAAAAAwDAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAAZAAAAJwAAAA0AAAAOAAAAAAAAAAAAAAANDAAAOgAAAA0MAAAyAAAAAQAAACoAAABtYWluAAAAAAQAAAABAAMAAAAAAAAAAAAIAAAAAAAEAN0GAAAXBQAAAAAAAAAAAAANAAAABAAAAM0GAAAMAAAABQAAAIcCAAAAAAAAAAAAAEAAAAABAAQABAAAAAAAAABBAQAABAAAAD0BAAAMAAAAAAAAAAAAAAAsAQAAAgAAAAQAAAAAAAAAOQEAAAQAAAA1AQAADAAAAAAAAAAAAAAAJAEAAAIAAAAFAAAAAAAAADkBAAAEAAAANQEAAAwAAAAAAAAAAAAAABwBAAABAAEAAgAAAAAAAAAxAQAABAAAAC0BAAAMAAAAAAAAAAAAAAAcAQAAAgAAAAIAAAAAAAAAKQEAAAQAAAAlAQAADAAAAAAAAAAAAAAAFAEAAAIAAAACAAAAAAAAACEBAAAEAAAAHQEAAAwAAAAAAAAAAAAAAAwBAAACAAAAAQAAAAAAAAAVAQAADAAAABUBAAAMAAAAAAAAAAAAAAAEAQAAAQAAAAAAAAAAAAAAEQEAAAQAAAANAQAADAAAAAAAAAAAAAAA/AAAAAEAAQADAAAAAAAAAAkBAAAMAAAACQEAAAwAAAAAAAAAAAAAAPQAAAABAAEABgAAAAAAAAD9AAAAJAAAAAEBAAAMAAAAAAAAAAAAAAD8AAAAAQACAAAAAAAAAAAApQEAAAwAAAClAQAADAAAAAAAAAAAAAAAhAEAAAEAAQAGAAAAAAAAAJkBAAA8AAAApQEAACQAAAAAAAAAAAAAAKQBAAABAAIABgAAAAAAAACRAgAARAAAAJ0CAAAsAAAAAAAAAAAAAACgAgAAAQACAAYAAAAAAAAA1QQAACwAAADdBAAAFAAAAAAAAAAAAAAA2AQAAAEAAgAAAAAAAAAAAJUFAAAMAAAAlQUAAAQAAAAAAAAAAAAAAIAFAAABAAEAAQAAAAAAAACJBQAADAAAAIkFAAAEAAAAAAAAAAAAAAB4BQAAAQAAAAAAAAABAAAAAAAAAAAA4D8AAAAAAAAAAAAAAAABAAAAGC1EVPsh+b8BAAAAAAAAAAEAAAAAAAAAAQAAABUAAADSITN/fNnyP9IhM3982QJAAgAAAAAAAAAAAAMAAQAAAAAAAAAAAAAAAwAAAAAAAAAAAAMAAgAAAAAAAAAAAAAABAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAFAAEAAAAAAAAAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAUAAAAEAAAAAwAAAAYAAAAJAAAAAAAAAAEAAAAAAAAABAAAAAAABAAAAAAAAAAAAA0AAAAUAAAAhQAAAAwAAAAJAAAAfwAAAAAAAAAAAAAACgAAAAsAAAAMAAAAAQAEAAEAAAAAAAAAOQAAABQAAAA5AAAAFAAAAAAAAAAAAAAAKAAAAAEAAAAAAAAAAAAAAEEAAAAMAAAAQQAAAAwAAAAAAAAAAAAAACAAAAABAAEAAQAAAAAAAAA5AAAAHAAAAD0AAAAMAAAAAAAAAAAAAAAoAAAAAQAAAAIAAAAAAAAACwAAAAoAAAAMAAAADQAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAANAAAAAAAAAA4AAAAAAAAAAwAAAAAAAAAMAAAACgAAAA4AAAAAAAAADwAAAAAAAAAPAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAHAAAAAAAAABAAAAAAAAAABQAAAAQAAAADAAAACQAAABAAAAACAAAAAwAAAAAAAAARAAAAEgAAABMAAAAUAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAACwAAADZAAAAJAAAABEAAADPAAAAAAAAAAAAAAAVAAAAFgAAABcAAAAYAAAAGQAAAAAAAAAUAAAAAQAEAAUAAAAAAAAAZQAAABQAAABlAAAADAAAAAAAAAAAAAAAUAAAAAEAAQACAAAAAAAAAGEAAAAUAAAAYQAAAAwAAAAAAAAAAAAAAEwAAAACAAAAAQAAAAAAAABZAAAAFAAAAFkAAAAUAAAAAAAAAAAAAABIAAAAAQAAAAAAAAAAAAAAYQAAABwAAABlAAAAFAAAAAAAAAAAAAAAQAAAAAEAAQABAAAAAAAAAF0AAAAcAAAAYQAAAAwAAAAAAAAAAAAAAEwAAAABAAAAAwAAAAAAAAAAAAAAAAAAABgAAAAVAAAA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EgAAAAAAAAABAAAAAAAAAEYAAAAAAAAARwAAAAAAAABEAQAAAAACAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABQABAAEAAAACAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwABAAEAAAACAAAAAAAAAAEAAQACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAEAAQAAAAIAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAQABAAAAAgAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAwABAAEAAAACAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAwABAAEAAAACAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAwABAAEAAAACAAAAAAAAAG1xdC1jYwAANC4wLjAAAAA=" + }, + { + "file": "jeff/upstream/multiplexer_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "dynamic": false, + "round_trip": true + }, + "base64": "AAAAAAsCAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAAZAAAAJwAAAA0AAAAOAAAAAAAAAAAAAAAJCAAAOgAAAAkIAAAyAAAAAQAAACoAAABtYWluAAAAAAQAAAABAAMAAAAAAAAAAAAIAAAAAAAEALkEAAA3AwAAAAAAAAAAAAANAAAABAAAAKkEAAAMAAAABQAAAIcCAAAAAAAAAAAAAEAAAAABAAQABAAAAAAAAABBAQAABAAAAD0BAAAMAAAAAAAAAAAAAAAsAQAAAgAAAAQAAAAAAAAAOQEAAAQAAAA1AQAADAAAAAAAAAAAAAAAJAEAAAIAAAACAAAAAAAAADEBAAAEAAAALQEAAAwAAAAAAAAAAAAAABwBAAACAAAAAgAAAAAAAAApAQAABAAAACUBAAAMAAAAAAAAAAAAAAAUAQAAAgAAAAIAAAAAAAAAIQEAAAQAAAAdAQAADAAAAAAAAAAAAAAADAEAAAIAAAABAAAAAAAAABUBAAAMAAAAFQEAAAwAAAAAAAAAAAAAAAQBAAABAAAAAAAAAAAAAAARAQAABAAAAA0BAAAMAAAAAAAAAAAAAAD8AAAAAQABAAIAAAAAAAAACQEAAAQAAAAFAQAADAAAAAAAAAAAAAAA9AAAAAIAAAADAAAAAAAAAAEBAAAMAAAAAQEAAAwAAAAAAAAAAAAAAOwAAAABAAEABgAAAAAAAAD1AAAAJAAAAPkAAAAMAAAAAAAAAAAAAAD0AAAAAQACAAYAAAAAAAAAiQEAAEQAAACVAQAALAAAAAAAAAAAAAAAmAEAAAEAAgAAAAAAAAAAAJECAAAMAAAAkQIAABQAAAAAAAAAAAAAAHwCAAABAAEAAwAAAAAAAACNAgAAHAAAAJECAAAMAAAAAAAAAAAAAAB4AgAAAQABAAYAAAAAAAAAhQIAADQAAACNAgAAHAAAAAAAAAAAAAAAjAIAAAEAAgAAAAAAAAAAAHEDAAAMAAAAcQMAAAQAAAAAAAAAAAAAAFwDAAABAAEAAQAAAAAAAABlAwAADAAAAGUDAAAEAAAAAAAAAAAAAABUAwAAAQAAAAAAAAABAAAAAAAAAAAA4D8AAAAAAAAAAAAAAAABAAAAGC1EVPsh+T8BAAAAAAAAAAAAAwACAAAAAAAAAAAAAAACAAAAAAAAAAAAAwABAAAAAAAAAAAAAAADAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAwADAAAAAAAAAAAAAAAHAAAAAAAAAAUAAQAAAAAAAAAAAAAAAAAHAAAAAAAAAAgAAAAAAAAABAAAAAIAAAADAAAABQAAAAkAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABQAAACFAAAADAAAAAkAAAB/AAAAAAAAAAAAAAAKAAAACwAAAAwAAAABAAQAAQAAAAAAAAA5AAAAFAAAADkAAAAUAAAAAAAAAAAAAAAoAAAAAQAAAAAAAAAAAAAAQQAAAAwAAABBAAAADAAAAAAAAAAAAAAAIAAAAAEAAQABAAAAAAAAADkAAAAcAAAAPQAAAAwAAAAAAAAAAAAAACgAAAABAAAAAgAAAAAAAAALAAAACgAAAAwAAAANAAAABgAAAAAAAAAAAAAAAgABAAkAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA0AAAAAAAAADgAAAAAAAAADAAAAAAAAAAwAAAAKAAAADgAAAAAAAAAPAAAAAAAAAA8AAAAAAAAABAAAAAIAAAADAAAAAQAAAAkAAAAGAAAAAwAAAAAAAAAQAAAAEQAAABIAAAATAAAAFAAAAAAAAAABAAAAAAAAAAQAAAAAAAQAAAAAAAAAAAANAAAANAAAANkAAAAsAAAAEQAAAM8AAAAAAAAAAAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABQAAAABAAQAAgAAAAAAAABlAAAAFAAAAGUAAAAMAAAAAAAAAAAAAABQAAAAAgAAAAEAAAAAAAAAXQAAABQAAABdAAAAFAAAAAAAAAAAAAAATAAAAAEAAAAAAAAAAAAAAGUAAAAcAAAAaQAAABQAAAAAAAAAAAAAAEQAAAABAAEAAQAAAAAAAABhAAAAHAAAAGUAAAAMAAAAAAAAAAAAAABQAAAAAQAAAAQAAAAAAAAAYQAAABQAAABhAAAADAAAAAAAAAAAAAAATAAAAAIAAAAAAAYAAAAAAAAAAAAAAAAAGQAAABUAAAAbAAAAAAAAAAIAAAAAAAAAFwAAABsAAAAcAAAAHQAAAAYAAAAAAAAAAAAAAAIAAQAHAAAAAAABAAEAAAAAAAAAAAAAAAAAAAAYAAAAHQAAABYAAAAAAAAAHgAAAB8AAAADAAAAAAAAABwAAAAbAAAAHwAAAAAAAAAgAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAWAAAAGgAAACEAAAAAAAAAIQAAACAAAAAeAAAAGQAAABoAAAAAAAAABAAAAAAAAAAAAAAAAAAAABIAAAAAAAAAIgAAACMAAAAHAAAAAAAAAAAAAAAAAAAACAAAAAQAAAAjAAAAAAAAACQAAAAAAAAABAAAAAIAAAADAAAAEQAAAAMAAAAkAAAAJQAAACYAAAAnAAAAAAAAAAEAAAAAAAAABAAAAAAABAAAAAAAAAAAAA0AAAAkAAAAyQAAABwAAAANAAAAzwAAAAAAAAAAAAAAKAAAACkAAAAqAAAAKwAAABQAAAABAAQAAgAAAAAAAABlAAAAFAAAAGUAAAAMAAAAAAAAAAAAAABQAAAAAgAAAAEAAAAAAAAAXQAAABQAAABdAAAAFAAAAAAAAAAAAAAATAAAAAEAAAAAAAAAAAAAAFkAAAAMAAAAWQAAABQAAAAAAAAAAAAAAEQAAAABAAEAAQAAAAAAAABRAAAAHAAAAFUAAAAMAAAAAAAAAAAAAABAAAAAAQAAAAMAAAAAAAAAUQAAABwAAABVAAAADAAAAAAAAAAAAAAAPAAAAAEAAQAAAAUAAAAAAAAAAAAAAAAAKAAAACoAAAAsAAAAAAAAAAIAAAAAAAAAKQAAACgAAAAtAAAALgAAAAQAAAAAAAAAAAAAAAAAAAAuAAAAAAAAAC8AAAAwAAAAAwAAAAAAAAAtAAAAKAAAAC8AAAAAAAAAMQAAAAAAAAAHAAAAAAAAAAAAAAAAAAAAKwAAACwAAAAwAAAAAAAAADIAAAAAAAAAMQAAACoAAAAyAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAiAAAAAAAAAAEAAAAAAAAAJQAAAAAAAAAnAAAAAAAAAMwAAAAAAAIAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAwABAAEAAAADAAAAAAAAAAEAAQACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAwABAAEAAAADAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAbXF0LWNjAAA0LjAuMAAAAA==" + }, + { + "file": "jeff/upstream/qft-adder-classical_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "dynamic": false, + "round_trip": true + }, + "base64": "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AAAAAGsAAAAAAAAAbAAAAG0AAAADAAAAAAAAAGoAAABnAAAAbAAAAAAAAABuAAAAAAAAAAcAAAAAAAAAAAAAAAAAAABpAAAAZwAAAG0AAAAAAAAAbwAAAAAAAABuAAAAbwAAAAEAAAAAAAAAZQAAAAAAAABmAAAAAAAAAMABAAAAAAIAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAAgAAAAEAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAFAAEAAQAAAAMAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAFAAEAAQAAAAMAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAFAAEAAQAAAAMAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAG1xdC1jYwAAAAAAAAIAAAADAAEAAQAAAAMAAAAAAAAAAQAAADIAAAA0LjAuMAAAAA==" + }, + { + "file": "jeff/upstream/qft-adder-quantum_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "dynamic": false, + "round_trip": true + }, + "base64": "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AAAAAKMAAACcAAAApAAAAAAAAAABAAAAAAAAAJcAAAAAAAAAAQAAAAAAAACMAAAAAAAAAJkAAAAAAAAAbXF0LWNjAAA0LjAuMAAAAAEAAABXCgAAlAIAAAAAAgAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAwABAAEAAAAGAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAYAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAYAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAYAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAYAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAwABAAEAAAAGAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAMAAQABAAAABgAAAAAAAAA=" + }, + { + "file": "jeff/upstream/controlled-multiplication-modulo-n_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "dynamic": false, + "round_trip": true + }, + "base64": "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CgAAAAAAAAAAAAMAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAADQAAAAAAAAAAAAAAAAAAAAYAAAAAAAAADgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAAAAAAAAAAMACAAAAAAAAAAAAAAAEAAAAAAAAAAFAAEAAAAAAAAAAAAAAAAAEAAAAAAAAAARAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAMAAAAAAAAABIAAAAAAAAACwAAAAoAAAAJAAAADQAAABMAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABQAAACFAAAADAAAAAkAAAB/AAAAAAAAAAAAAAAUAAAAFQAAAAwAAAABAAQAAQAAAAAAAAA5AAAAFAAAADkAAAAUAAAAAAAAAAAAAAAoAAAAAQAAAAAAAAAAAAAAQQAAAAwAAABBAAAADAAAAAAAAAAAAAAAIAAAAAEAAQABAAAAAAAAADkAAAAcAAAAPQAAAAwAAAAAAAAAAAAAACgAAAABAAAAAgAAAAAAAAAVAAAAFAAAABYAAAAXAAAABgAAAAAAAAAAAAAAAgABAAkAAAAAAAAAAQAAAAAAAAAAAAAAAAAAABcAAAAAAAAAGAAAAAAAAAADAAAAAAAAABYAAAAUAAAAGAAAAAAAAAAZAAAAAAAAABkAAAAAAAAACwAAAAYAAAAJAAAADgAAAAoAAAAAAAAABwAAAAsAAAAJAAAACAAAABoAAAAbAAAAHAAAAB0AAAAeAAAAHwAAACAAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAAEQAAABFAgAAPAAAABUAAAD3AAAAAAAAAAAAAAAhAAAAIgAAACMAAAAkAAAAJQAAACYAAAAnAAAAKAAAABgAAAABAAQAAgAAAAAAAAB5AAAAFAAAAHkAAAAMAAAAAAAAAAAAAABkAAAAAgAAAAEAAAAAAAAAcQAAABQAAABxAAAAFAAAAAAAAAAAAAAAYAAAAAEAAAAAAAAAAAAAAHkAAAAMAAAAeQAAAAwAAAAAAAAAAAAAAFgAAAABAAEAAQAAAAAAAABxAAAAHAAAAHUAAAAMAAAAAAAAAAAAAABgAAAA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zA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAFAAEAAQAAABAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAUAAQABAAAAEAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAFAAEAAQAAABAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAFAAEAAQAAABAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABQABAAEAAAAQAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABQABAAEAAAAQAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAFAAEAAQAAABAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAFAAEAAQAAABAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAUAAQABAAAAEAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAUAAQABAAAAEAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABQABAAEAAAAQAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABQABAAEAAAAQAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAUAAQABAAAAEAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAMAAQABAAAACAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAMAAQABAAAACAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAMAAQABAAAACAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAACAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAgAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAwABAAEAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAwABAAEAAAAIAAAAAAAAAAEAAAA6AAAAbXF0LWNjAAABAAAAMgAAADQuMC4wAAAA" + }, + { + "file": "jeff/upstream/repeat-until-success_3.jeff", + "format": "jeff", + "expect": { + "refused": "Q714", + "at": "operation 10" + }, + "base64": "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CgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAFAAAABwAAAAVAAAAFgAAAB0AAAAAAAAAFwAAABgAAAAZAAAAEwAAAB0AAAAAAAAAHgAAAB8AAAABAAAAAAAAAAQAAAAAAAQAAAAAAAAAAAANAAAAHAAAAIkAAAAUAAAADQAAAH8AAAAAAAAAAAAAACAAAAAhAAAAIgAAAAAAAAAMAAAAAQAEAAEAAAAAAAAAOQAAABQAAAA5AAAAFAAAAAAAAAAAAAAAKAAAAAEAAAAAAAAAAAAAAEEAAAAUAAAAQQAAABQAAAAAAAAAAAAAACAAAAABAAEAAQAAAAAAAAA5AAAAHAAAAD0AAAAMAAAAAAAAAAAAAAAoAAAAAQAAAAIAAAAAAAAAIQAAACAAAAAjAAAAJAAAAAYAAAAAAAAAAAAAAAIAAQAAAAAAAAABAAEAAAAAAAAAAAAAAAAAAAAkAAAAIgAAACUAAAAmAAAAAwAAAAAAAAAjAAAAIAAAACUAAAAAAAAAJwAAAAAAAAAnAAAAJgAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAfAAAAAAAAACgAAAAAAAAAFwAAABgAAAAZAAAAHgAAACgAAAAAAAAAKQAAACoAAAABAAAAAAAAAAQAAAAAAAQAAAAAAAAAAAANAAAAHAAAAIkAAAAUAAAADQAAAH8AAAAAAAAAAAAAACsAAAAsAAAALQAAAAAAAAAMAAAAAQAEAAEAAAAAAAAAOQAAABQAAAA5AAAAFAAAAAAAAAAAAAAAKAAAAAEAAAAAAAAAAAAAAEEAAAAUAAAAQQAAABQAAAAAAAAAAAAAACAAAAABAAEAAQAAAAAAAAA5AAAAHAAAAD0AAAAMAAAAAAAAAAAAAAAoAAAAAQAAAAIAAAAAAAAALAAAACsAAAAuAAAALwAAAAYAAAAAAAAAAAAAAAIAAQAAAAAAAAABAAEAAAAAAAAAAAAAAAAAAAAvAAAALQAAADAAAAAxAAAAAwAAAAAAAAAuAAAAKwAAADAAAAAAAAAAMgAAAAAAAAAyAAAAMQAAAAYAAAAAAAAAAAAAAAIAAQAKAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAqAAAAHAAAABoAAAAbAAAAMwAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAMwAAAAAAAAA0AAAANQAAADUAAAApAAAANAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAANAAAATAAAAFEAAABMAAAAGQAAAC8AAAAAAAAAAAAAADYAAAA3AAAAOAAAADkAAAA6AAAAOwAAADwAAAA9AAAAPgAAAAAAAAAEAAAAAQAEAAAAAAAAAAAAIQAAAAwAAAAhAAAADAAAAAAAAAAAAAAAAAAAAAEAAQAGAAAAAAAAAAAAAAACAAEAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAANwAAAAAAAAA/AAAAAAAAADYAAAA/AAAAOAAAADkAAAA6AAAAOwAAADwAAAA9AAAAPgAAAAAAAAACAAAAAAAAAAoAAAAGAAAAQAAAAEEAAAAGAAAAAAAAAAAAAAACAAEAAwAAAAAAAAEBAAAAAAAAAAAAAAAAAAAAQQAAAAAAAABCAAAAAAAAAAMAAAAAAAAAQAAAAAYAAABCAAAAAAAAAEMAAAAAAAAABgAAAAUAAAAEAAAAQwAAAEQAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABQAAACFAAAADAAAAAkAAAB/AAAAAAAAAAAAAABFAAAARgAAAAwAAAABAAQAAQAAAAAAAAA5AAAAFAAAADkAAAAUAAAAAAAAAAAAAAAoAAAAAQAAAAAAAAAAAAAAQQAAAAwAAABBAAAADAAAAAAAAAAAAAAAIAAAAAEAAQABAAAAAAAAADkAAAAcAAAAPQAAAAwAAAAAAAAAAAAAACgAAAABAAAAAgAAAAAAAABGAAAARQAAAEcAAABIAAAABgAAAAAAAAAAAAAAAgABAAkAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAEgAAAAAAAAASQAAAAAAAAADAAAAAAAAAEcAAABFAAAASQAAAAAAAABKAAAAAAAAAEoAAAAAAAAABAAAAAUAAAAEAAAARAAAAAYAAAAAAAAASwAAAEwAAAABAAAAAAAAAAQAAAAAAAQAAAAAAAAAAAANAAAAHAAAAM0AAAAUAAAADQAAAM8AAAAAAAAAAAAAAE0AAABOAAAATwAAAAAAAAAUAAAAAQAEAAEAAAAAAAAAYQAAABQAAABhAAAAFAAAAAAAAAAAAAAAUAAAAAEAAAABAAAAAAAAAFkAAAAUAAAAWQAAABQAAAAAAAAAAAAAAEgAAAABAAAAAAAAAAAAAABhAAAAFAAAAGEAAAAUAAAAAAAAAAAAAABAAAAAAQABAAEAAAAAAAAAWQAAABwAAABdAAAADAAAAAAAAAAAAAAASAAAAAEAAAABAAAAAAAAAFUAAAAcAAAAWQAAAAwAAAAAAAAAAAAAAEQAAAABAAAAAgAAAAAAAABOAAAATQAAAFAAAABRAAAAAgAAAAAAAABQAAAATwAAAFIAAABTAAAABgAAAAAAAAAAAAAAAgABAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAFMAAABRAAAAVAAAAFUAAAADAAAAAAAAAFIAAABPAAAAVAAAAAAAAABWAAAAAAAAAAMAAAAAAAAAVgAAAE0AAABVAAAAAAAAAFcAAAAAAAAAVwAAAE8AAAACAAAAAAAAAEsAAAAGAAAAWAAAAFkAAAAEAAAAAAAAAAAAAAAAAAAAWQAAAAAAAABaAAAAWwAAAAMAAAAAAAAAWAAAAAYAAABaAAAAAAAAAFwAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAkAAAAGAAAAWwAAAAAAAABdAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAEAAAAAAAAAXAAAAAAAAABdAAAAAAAAAHgBAAAAAAIA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAMAAQABAAAAAQAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAMAAQABAAAAAQAAAAAAAABtcXQtY2MAADQuMC4wAAAA" + }, + { + "file": "jeff/upstream/example-entangled-qubits.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q711" + ], + "how": "tableau", + "round_trip": true + }, + "base64": "AAAAAIUBAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAOkFAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQA6QMAAOcBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAFwQAAAAAAAAAAAAAaAAAAAEABAAAAAAAAAAAAAAAAAAAAAAA/QEAAAwAAAAAAAAAAAAAAPgBAAABAAEAAAAAAAAAAAAAAAAAAAAAAPUBAAAMAAAAAAAAAAAAAADwAQAAAQABAAAAAAAAAAAAAAAAAAAAAADtAQAADAAAAAAAAAAAAAAA6AEAAAEAAQAAAAAAAAAAAAAAAAAAAAAA5QEAAAwAAAAAAAAAAAAAAOABAAABAAEAAAAAAAAAAAAAAAAAAAAAAN0BAAAMAAAAAAAAAAAAAADYAQAAAQABAAAAAAAAAAAA2QEAAAwAAADZAQAADAAAAAAAAAAAAAAA1AEAAAEAAQAAAAAAAAAAAOEBAAAUAAAA4QEAABQAAAAAAAAAAAAAANwBAAABAAEAAAAAAAAAAADpAQAAFAAAAOkBAAAUAAAAAAAAAAAAAADkAQAAAQABAAAAAAAAAAAA8QEAABQAAADxAQAAFAAAAAAAAAAAAAAA7AEAAAEAAQAAAAAAAAAAAPkBAAAUAAAA+QEAABQAAAAAAAAAAAAAAPQBAAABAAEAAwAAAAAAAAAAAAAAAAAAAP0BAAAMAAAAAAAAAAAAAAD4AQAAAQABAAAAAAAAAAAA/QEAAAwAAAD9AQAADAAAAAAAAAAAAAAA+AEAAAEAAQACAAAAAAAAAAAAAAAAAAAA9QEAAAwAAAAAAAAAAAAAAPABAAACAAAAAwAAAAAAAADxAQAAHAAAAPUBAAAMAAAAAAAAAAAAAADwAQAAAQABAAAAAAAAAAAA8QEAAAwAAADxAQAADAAAAAAAAAAAAAAA7AEAAAEAAQACAAAAAAAAAAAAAAAAAAAA6QEAAAwAAAAAAAAAAAAAAOQBAAACAAAAAwAAAAAAAADlAQAAHAAAAOkBAAAMAAAAAAAAAAAAAADkAQAAAQABAAAAAAAAAAAA5QEAAAwAAADlAQAADAAAAAAAAAAAAAAA4AEAAAEAAQACAAAAAAAAAAAAAAAAAAAA3QEAAAwAAAAAAAAAAAAAANgBAAACAAAAAwAAAAAAAADZAQAAHAAAAN0BAAAMAAAAAAAAAAAAAADYAQAAAQABAAAAAAAAAAAA2QEAAAwAAADZAQAADAAAAAAAAAAAAAAA1AEAAAEAAQACAAAAAAAAAAAAAAAAAAAA0QEAAAwAAAAAAAAAAAAAAMwBAAACAAAAAwAAAAAAAADNAQAAHAAAANEBAAAMAAAAAAAAAAAAAADMAQAAAQABAAAAAAAAAAAAzQEAAAwAAADNAQAADAAAAAAAAAAAAAAAyAEAAAEAAQACAAAAAAAAAAAAAAAAAAAAxQEAAAwAAAAAAAAAAAAAAMABAAACAAAAAwAAAAAAAADBAQAAHAAAAMUBAAAMAAAAAAAAAAAAAADAAQAAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAGAAAAAAAAAAAAAAACAAEAAQABAAEAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAEAAAAGAAAABwAAAAYAAAAAAAAAAAAAAAIAAQACAAEAAQABAAAAAAAAAAAAAAAAAAAAAAAHAAAAAgAAAAgAAAAJAAAABgAAAAAAAAAAAAAAAgABAAIAAQABAAEAAAAAAAAAAAAAAAAAAAAAAAkAAAADAAAACgAAAAsAAAAGAAAAAAAAAAAAAAACAAEAAgABAAEAAQAAAAAAAAAAAAAAAAAAAAAACwAAAAQAAAAMAAAADQAAAAYAAAAAAAAAAAAAAAIAAQACAAEAAQABAAAAAAAAAAAAAAAAAAAAAAAOAAAAAAAAAAAAAAAAAAAAAQAAACkAAAAAAAAAAAAAAAYAAAAAAAAADwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAADgAAABAAAAAPAAAAAAAAABEAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAEgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAEwAAAAAAAAAAAAMAAQAAAAAAAAAAAAAAEQAAABMAAAASAAAAAAAAABQAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAFQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAFgAAAAAAAAAAAAMAAgAAAAAAAAAAAAAAFAAAABYAAAAVAAAAAAAAABcAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAGAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAGQAAAAAAAAAAAAMAAwAAAAAAAAAAAAAAFwAAABkAAAAYAAAAAAAAABoAAAAAAAAABwAAAAAAAAAAAAAAAAAAAA0AAAAAAAAAGwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAHAAAAAAAAAAAAAMABAAAAAAAAAAAAAAAGgAAABwAAAAbAAAAAAAAAB0AAAAAAAAABwAAAAAAAAAAAAAAAAAAAHgAAAAAAAIA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAEAAQAAAAUAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAUAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAUAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAUAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAUAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAUAAAAAAAAACQAAADoAAAAJAAAAEgAAAAkAAAASAAAAcWFycmF5AABIAAAAAAAAAFgAAAAAAAAA", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\n// From the example's text: x on each qubit (custom X) with one control given after its target, as the schema orders a controlled gate's qubits: qubit k + 1 controls qubit k.\nqubit[5] q;\nbit[5] c;\nh q[0];\ncx q[1], q[0];\ncx q[2], q[1];\ncx q[3], q[2];\ncx q[4], q[3];\nc = measure q;\n" + }, + { + "file": "jeff/upstream/example-qubits.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q709", + "Q711", + "Q716" + ], + "how": "exact", + "round_trip": true + }, + "base64": "AAAAAJgAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAACUCAAAuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwADAAAAAAAAAAgAAAAAAAQAVQEAALcAAAAAAAAAAAAAAA0AAAAUAAAADQAAABwAAAARAAAAHwEAAAAAAAAAAAAAAAAAAAEAAAACAAAAAwAAAAQAAAAAAAAAHAAAAAEABAAAAAAAAAAAAAAAAAAAAAAAgQAAAAwAAAAAAAAAAAAAAHwAAAABAAEAAAAAAAAAAAB9AAAADAAAAH0AAAAMAAAAAAAAAAAAAAB4AAAAAQABAAAAAAAAAAAAhQAAAAwAAACFAAAADAAAAAAAAAAAAAAAgAAAAAEAAQAAAAAAAAAAAI0AAAAUAAAAjQAAABQAAAAAAAAAAAAAAIgAAAABAAEAAAAAAAAAAACVAAAAFAAAAJUAAAAUAAAAAAAAAAAAAACQAAAAAQABAAQAAAAAAAAAAAAAAAAAAACZAAAADAAAAAAAAAAAAAAAlAAAAAIAAAAAAAAAAAAAAJUAAAAUAAAAlQAAAAwAAAAAAAAAAAAAAJAAAAABAAEABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQABAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAcAAAAAAAAABgAAAAAAAAAAAAAAAgABAAAAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAGAAAACAAAAAkAAAAGAAAAAAAAAAAAAAACAAEAAgABAAIAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAgAAAADAAAABAAAAAYAAAAAAAAAAAAAAAIAAQACAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAABAAAAAAAAAAAA4D8JAAAACgAAAAIAAAAAAAAABgAAAAAAAAAAAAAAAgABAAQAAQABAQAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAIAVAAAAAIAAAAAAAAAAAAAAFQAAAACAAAAAAAAAAAAAABUAAAAAgAAAAAAAAAAAAAAVAAAAAIAAAAAAAAAAAAAAFQAAAACAAAAAAAAAAAAAABUAAAAAgAAAAAAAAAAAAAAVAAAAAIAAAAAAAAAAAAAAFQAAAACAAAAAAAAAAAAAABUAAAAAgAAAAAAAAAAAAAAVAAAAAIAAAAAAAAAAAAAAFQAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAABEAAAASAAAAEQAAABIAAAARAAAAGgAAABEAAABCAAAAEQAAABoAAABIAAAAAAAAAFgAAAAAAAAAQ1gAAAAAAABDaXJjdWl0AFJ4AAAAAAAA", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\n// From the example's text: two qubit arguments and one allocated; custom X, H, CX (control first, as its name says) and Rx.\nqubit[3] q;\nx q[0];\nh q[1];\ncx q[2], q[0];\ncx q[1], q[2];\nrx(0.5) q[0];\n" + }, + { + "file": "jeff/upstream/example-entangled-calls.jeff", + "format": "jeff", + "expect": { + "refused": "Q714", + "at": "operation 29" + }, + "base64": "AAAAAM4CAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAhwAAANEKAAA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAEAAwAAAAAAAAAAADgAAAAAAAQA1QYAACcDAAAAAAAAAAAAAAEAAAAAAAAA7AkAAAAABAAZCgAAFwAAAAAAAAAAAAAAAgAAAAAAAAAgCgAAAAAEAFUKAAAXAAAAAAAAAAAAAAADAAAAAAAAAFwKAAAAAAQAcQoAABcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAwAAAAJAAAANwcAAAAAAAAAAAAAMQAAAAAAAAC4AAAAAQAEAAIAAAAAAAAAAAAAAAAAAACNAwAADAAAAAAAAAAAAAAAiAMAAAIAAAAAAAAAAAAAAAAAAAAAAAAAhQMAAAwAAAAAAAAAAAAAAIADAAABAAEAAAAAAAAAAAAAAAAAAAAAAH0DAAAMAAAAAAAAAAAAAAB4AwAAAQABAAAAAAAAAAAAAAAAAAAAAAB1AwAADAAAAAAAAAAAAAAAcAMAAAEAAQAAAAAAAAAAAAAAAAAAAAAAbQMAAAwAAAAAAAAAAAAAAGgDAAABAAEAAAAAAAAAAAAAAAAAAAAAAGUDAAAMAAAAAAAAAAAAAABgAwAAAQABAAAAAAAAAAAAYQMAAAwAAABhAwAADAAAAAAAAAAAAAAAXAMAAAEAAQAAAAAAAAAAAGkDAAAUAAAAaQMAABQAAAAAAAAAAAAAAGQDAAABAAEAAAAAAAAAAABxAwAAFAAAAHEDAAAUAAAAAAAAAAAAAABsAwAAAQABAAAAAAAAAAAAeQMAABQAAAB5AwAAFAAAAAAAAAAAAAAAdAMAAAEAAQAAAAAAAAAAAIEDAAAUAAAAgQMAABQAAAAAAAAAAAAAAHwDAAABAAEAAwAAAAAAAAAAAAAAAAAAAIUDAAAMAAAAAAAAAAAAAACAAwAAAQABAAAAAAAAAAAAhQMAAAwAAACFAwAADAAAAAAAAAAAAAAAgAMAAAEAAQACAAAAAAAAAAAAAAAAAAAAfQMAAAwAAAAAAAAAAAAAAHgDAAACAAAAAwAAAAAAAAB5AwAAHAAAAH0DAAAMAAAAAAAAAAAAAAB4AwAAAQABAAAAAAAAAAAAeQMAAAwAAAB5AwAADAAAAAAAAAAAAAAAdAMAAAEAAQACAAAAAAAAAAAAAAAAAAAAcQMAAAwAAAAAAAAAAAAAAGwDAAACAAAAAwAAAAAAAABtAwAAHAAAAHEDAAAMAAAAAAAAAAAAAABsAwAAAQABAAAAAAAAAAAAbQMAAAwAAABtAwAADAAAAAAAAAAAAAAAaAMAAAEAAQACAAAAAAAAAAAAAAAAAAAAZQMAAAwAAAAAAAAAAAAAAGADAAACAAAAAwAAAAAAAABhAwAAHAAAAGUDAAAMAAAAAAAAAAAAAABgAwAAAQABAAAAAAAAAAAAYQMAAAwAAABhAwAADAAAAAAAAAAAAAAAXAMAAAEAAQACAAAAAAAAAAAAAAAAAAAAWQMAAAwAAAAAAAAAAAAAAFQDAAACAAAAAwAAAAAAAABVAwAAHAAAAFkDAAAMAAAAAAAAAAAAAABUAwAAAQABAAAAAAAAAAAAVQMAAAwAAABVAwAADAAAAAAAAAAAAAAAUAMAAAEAAQACAAAAAAAAAAAAAAAAAAAATQMAAAwAAAAAAAAAAAAAAEgDAAACAAAAAwAAAAAAAABJAwAAHAAAAE0DAAAMAAAAAAAAAAAAAABIAwAAAQABAAIAAAAAAAAAAAAAAAAAAABFAwAADAAAAAAAAAAAAAAAQAMAAAIAAAADAAAAAAAAAEEDAAAUAAAAQQMAAAwAAAAAAAAAAAAAADwDAAABAAEAAgAAAAAAAAA9AwAAFAAAAD0DAAAMAAAAAAAAAAAAAAA4AwAAAgAAAAIAAAAAAAAAAAAAAAAAAAA1AwAADAAAAAAAAAAAAAAAMAMAAAIAAAADAAAAAAAAADEDAAAUAAAAMQMAAAwAAAAAAAAAAAAAACwDAAABAAEAAgAAAAAAAAAtAwAAFAAAAC0DAAAMAAAAAAAAAAAAAAAoAwAAAgAAAAIAAAAAAAAAKQMAABQAAAApAwAADAAAAAAAAAAAAAAAJAMAAAIAAAACAAAAAAAAAAAAAAAAAAAAIQMAAAwAAAAAAAAAAAAAABwDAAACAAAAAwAAAAAAAAAdAwAAFAAAAB0DAAAMAAAAAAAAAAAAAAAYAwAAAQABAAIAAAAAAAAAGQMAABQAAAAZAwAADAAAAAAAAAAAAAAAFAMAAAIAAAACAAAAAAAAABUDAAAUAAAAFQMAAAwAAAAAAAAAAAAAABADAAACAAAAAgAAAAAAAAAAAAAAAAAAAA0DAAAMAAAAAAAAAAAAAAAIAwAAAgAAAAMAAAAAAAAACQMAABQAAAAJAwAADAAAAAAAAAAAAAAABAMAAAEAAQACAAAAAAAAAAUDAAAUAAAABQMAAAwAAAAAAAAAAAAAAAADAAACAAAAAgAAAAAAAAABAwAAFAAAAAEDAAAMAAAAAAAAAAAAAAD8AgAAAgAAAAIAAAAAAAAAAAAAAAAAAAD5AgAADAAAAAAAAAAAAAAA9AIAAAIAAAADAAAAAAAAAPUCAAAUAAAA9QIAAAwAAAAAAAAAAAAAAPACAAABAAEAAgAAAAAAAADxAgAAFAAAAPECAAAMAAAAAAAAAAAAAADsAgAAAgAAAAIAAAAAAAAA7QIAABQAAADtAgAADAAAAAAAAAAAAAAA6AIAAAIAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAYAAAAAAAAABgAAAAAAAAAAAAAAAgABAAQAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAACAAAABwAAAAgAAAAGAAAAAAAAAAAAAAACAAEABQABAAEAAQAAAAAAAAAAAAAAAAAAAAAACAAAAAMAAAAJAAAACgAAAAYAAAAAAAAAAAAAAAIAAQAFAAEAAQABAAAAAAAAAAAAAAAAAAAAAAAKAAAABAAAAAsAAAAMAAAABgAAAAAAAAAAAAAAAgABAAUAAQABAAEAAAAAAAAAAAAAAAAAAAAAAAwAAAAFAAAADQAAAA4AAAAGAAAAAAAAAAAAAAACAAEABQABAAEAAQAAAAAAAAAAAAAAAAAAAAAADwAAAAAAAAAAAAAAAAAAAAEAAAApAAAAAAAAAAAAAAAHAAAAAAAAABAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAADAAAAAAAAAAAAAAAAAA8AAAARAAAAEAAAAAAAAAASAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAJAAAAAAAAABMAAAAAAAAAAwAAAAAAAAAAAAAAAAAAABQAAAAAAAAAAAADAAEAAAAAAAAAAAAAABIAAAAUAAAAEwAAAAAAAAAVAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAALAAAAAAAAABYAAAAAAAAAAwAAAAAAAAAAAAAAAAAAABcAAAAAAAAAAAADAAIAAAAAAAAAAAAAABUAAAAXAAAAFgAAAAAAAAAYAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAANAAAAAAAAABkAAAAAAAAAAwAAAAAAAAAAAAAAAAAAABoAAAAAAAAAAAADAAMAAAAAAAAAAAAAABgAAAAaAAAAGQAAAAAAAAAbAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAOAAAAAAAAABwAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAB0AAAAAAAAAAAADAAQAAAAAAAAAAAAAABsAAAAdAAAAHAAAAAAAAAAeAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAfAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAeAAAAHwAAACAAAAAAAAAABgAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIQAAAAAAAAAAABsAAAAAAAAAAAAAAAAAIgAAAAAAAAAAAAMAAQAAAAAAAAAAAAAAHgAAACIAAAAjAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAhAAAAIwAAACQAAAAAAAAAAAAFAAAAAAAAAAAAAAAAACQAAAAAAAAAJQAAAAAAAAAAABsAAAAAAAAAAAAAAAAAJgAAAAAAAAAAAAMAAgAAAAAAAAAAAAAAHgAAACYAAAAnAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAlAAAAJwAAACgAAAAAAAAAAAAFAAAAAAAAAAAAAAAAACgAAAAAAAAAKQAAAAAAAAAAABsAAAAAAAAAAAAAAAAAKgAAAAAAAAAAAAMAAwAAAAAAAAAAAAAAHgAAACoAAAArAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAApAAAAKwAAACwAAAAAAAAAAAAFAAAAAAAAAAAAAAAAACwAAAAAAAAALQAAAAAAAAAAABsAAAAAAAAAAAAAAAAALgAAAAAAAAAAAAMABAAAAAAAAAAAAAAAHgAAAC4AAAAvAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAtAAAALwAAADAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAADAAAAAAAAAAMQAAAAAAAAAAABsAAAAAAAAAAAAAAAAAyAAAAAAAAgCMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAQABAAAABQAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAABQAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAABQAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAABQAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAABQAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAABQAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAADQAAAAQAAAAJAAAABAAAAAUAAAAvAAAAAAAAAAAAAAAEAAAAAQAEAAcAAAAAAAAAAAAAAAAAAAAJAAAADAAAAAAAAAAAAAAABAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAgAEAAAAAgAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAwAAAAJAAAALwAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAQAEAAIAAAAAAAAAAAAAAAAAAAAJAAAADAAAAAAAAAAAAAAABAAAAAIAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAEAAAAAAACAAQAAAACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAADQAAAAwAAAANAAAABAAAAAkAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAQABAAAAAAAAgAEAAAAAgAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAABUAAABqAQAAKQAAALIAAAAxAAAAWgAAADUAAABiAAAAOQAAABIAAAA5AAAAEgAAAF9fbnZxcHBfX21saXJnZW5fX2Z1bmN0aW9uX3NhbXBsZS5fWjZzYW1wbGV2AAAAAF9fbnZxcHBfX21saXJnZW5fX2doegAAAF9aNnNhbXBsZXYAAAAAAABfWk4zZ2h6Y2xFdgAAAAAASAAAAAAAAABYAAAAAAAAAA==" + }, + { + "file": "jeff/upstream/example-optimisation.jeff", + "format": "jeff", + "expect": { + "refused": "Q714" + }, + "base64": "AAAAAIIAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAOUBAAAOAAAAAAAAAAAAAADlAQAACgAAAOUBAAAKAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQARQEAAIcAAAAAAAAAAAAAAA0AAAAMAAAADQAAAAQAAAAJAAAAzwAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAQAEAAAAAAAAAAAAXQAAAAQAAABZAAAADAAAAAAAAAAAAAAAVAAAAAEAAQAAAAAAAAAAAFUAAAAMAAAAVQAAAAwAAAAAAAAAAAAAAFAAAAABAAEAAgAAAAAAAABdAAAADAAAAF0AAAAMAAAAAAAAAAAAAABYAAAAAgAAAAYAAAAAAAAAWQAAABQAAABZAAAADAAAAAAAAAAAAAAAVAAAAAEAAgAAAAAAAAAAAMUAAAAMAAAAxQAAAAQAAAAAAAAAAAAAALwAAAABAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAOAAAAAAAAAAAAAAAAAAMAAAACAAAABAAAAAAAAAAAAAAAAAAAAAUAAAAnAAAAUAAAAAAABAAEAAAAAAAEAA0AAAAMAAAADQAAAAwAAAANAAAALwAAAAAAAAAAAAAABQAAAAAAAAAGAAAAAAAAAAQAAAABAAQAAAAAAAAAAAANAAAADAAAAA0AAAAMAAAAAAAAAAAAAAAIAAAAAQABAAUAAAAAAAAABgAAAAAAAAAGAAAAAAAAAAAAAAACAAEACQAAAAAAAAABAAAAAAAAAAAAAAAAAAAADQAAAAwAAAANAAAADAAAAA0AAAAHAAAAAAAAAAAAAAAHAAAAAAAAAAcAAAAAAAAAAAAAAAEABAAEAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAgAAAAAAACADwAAAACAAAAAAAAAAAAAAA8AAAAAgAAAAAAAAAAAAAAPAAAAAIAAAAAAAAAAAAAADwAAAACAAAAAAAAAAAAAAA8AAAAAgAAAAAAAAAAAAAAPAAAAAIAAAAAAAAAAAAAADwAAAACAAAAAAAAAAAAAAA8AAAAAgAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAKgAAAG1haW4AAAAAAAAAAAAAAAAAAAAAAAAAAA==" + }, + { + "file": "jeff/upstream/example-rewrite.jeff", + "format": "jeff", + "expect": { + "refused": "Q713", + "at": "operation 5" + }, + "base64": "AAAAAHIBAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAHkFAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQAWQMAAAcCAAAAAAAAAAAAAA0AAAAEAAAACQAAABQAAAAJAAAAJwMAAAAAAAAAAAAAGwAAAB0AAABQAAAAAQAEAAIAAAAAAAAAiQEAAAQAAACFAQAADAAAAAAAAAAAAAAAgAEAAAIAAAACAAAAAAAAAIEBAAAEAAAAfQEAAAwAAAAAAAAAAAAAAHgBAAACAAAAAgAAAAAAAAB5AQAABAAAAHUBAAAMAAAAAAAAAAAAAABwAQAAAgAAAAEAAAAAAAAAcQEAAAwAAABxAQAADAAAAAAAAAAAAAAAbAEAAAEAAAABAAAAAAAAAGkBAAAUAAAAaQEAABQAAAAAAAAAAAAAAGQBAAABAAAAAQAAAAAAAABhAQAAFAAAAGEBAAAUAAAAAAAAAAAAAABcAQAAAQAAAAAAAAAAAAAAWQEAABQAAABZAQAAFAAAAAAAAAAAAAAAVAEAAAEAAQAAAAAAAAAAAGEBAAAMAAAAYQEAAAwAAAAAAAAAAAAAAFwBAAABAAEAAAAAAAAAAABpAQAAFAAAAGkBAAAUAAAAAAAAAAAAAABkAQAAAQABAAIAAAAAAAAAcQEAAAQAAABtAQAADAAAAAAAAAAAAAAAaAEAAAIAAAACAAAAAAAAAGkBAAAEAAAAZQEAAAwAAAAAAAAAAAAAAGABAAACAAAAAgAAAAAAAABhAQAABAAAAF0BAAAMAAAAAAAAAAAAAABYAQAAAgAAAAYAAAAAAAAAWQEAACwAAABhAQAAFAAAAAAAAAAAAAAAXAEAAAEAAgAAAAAAAAAAALEBAAAUAAAAsQEAABQAAAAAAAAAAAAAAKwBAAABAAEAAAAAAAAAAAC5AQAADAAAALkBAAAMAAAAAAAAAAAAAAC0AQAAAQABAAAAAAAAAAAAwQEAAAwAAADBAQAAFAAAAAAAAAAAAAAAvAEAAAEAAQAAAAAAAAAAAL0BAAAMAAAAvQEAABQAAAAAAAAAAAAAALgBAAABAAEAAQAAAAAAAAC5AQAAHAAAAL0BAAAMAAAAAAAAAAAAAAC4AQAAAQAAAAEAAAAAAAAAtQEAABwAAAC5AQAADAAAAAAAAAAAAAAAtAEAAAEAAAABAAAAAAAAALEBAAAMAAAAsQEAAAQAAAAAAAAAAAAAAKgBAAABAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAMAAgAAAAAAAAAAAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAEAAAABQAAAAIAAAAAAAAABAAAAAEAAAAGAAAABwAAAAIAAAAAAAAABQAAAAcAAAAIAAAACQAAAAYAAAAAAAAAAAAAAAIAAQABAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAJAAAAAAAAAAoAAAAAAAAABgAAAAAAAAAAAAAAAgABAAIAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAKAAAACwAAAAwAAAAGAAAAAAAAAAAAAAACAAEAAwABAAIAAAAAAAAAAAAAAAAAAAAAAAAADQAAAAAAAAAAAAMAAAAAAAAAAAAAAAAADgAAAAAAAAAAAAMAAwAAAAAAAAAAAAAADwAAAAAAAAAAAAMAAQAAAAAAAAAAAAAADQAAAA4AAAAPAAAACwAAAAwAAAAAAAAAFQAAABYAAAABAAAAAAAAAAQAAAAAAAQAAAAAAAAAAAANAAAAHAAAABEAAAAUAAAAEQAAAC8AAAAAAAAAAAAAABAAAAARAAAAEgAAAAAAAAATAAAAFAAAAAQAAAABAAQAAAAAAAAAAAANAAAAFAAAAA0AAAAUAAAAAAAAAAAAAAAIAAAAAQABABEAAAASAAAAEwAAABQAAAAGAAAAAAAAAAAAAAACAAEABAABAAIAAAAAAAAAAAAAAAAAAAAAAAAAFQAAABYAAAAXAAAAGAAAAAYAAAAAAAAAAAAAAAIAAQAFAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAABkAAAAAAAAABgAAAAAAAAAAAAAAAgABAAYAAQABAAAAAAAAAAAAAAAAAAAAAAAAABcAAAAAAAAAGgAAABsAAAAEAAAAAAAAAAAAAAAAAAAAGQAAAAAAAAAcAAAAHQAAAAQAAAAAAAAAAAAAAAAAAAAGAAAAAAAAABoAAAAAAAAAHgAAAAAAAAADAAAAAAAAAB4AAAABAAAAHAAAAAAAAAAfAAAAAAAAAAMAAAAAAAAAHwAAAAAAAAAKAAAAAAAAAIAAAAAAAAIA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAABkAAABiAAAAHQAAABoAAAAdAAAAEgAAAB0AAAAaAAAAHQAAACoAAAAdAAAAGgAAAB0AAAASAAAAb3B0aW1pemVfbWUAAAAAAENYAAAAAAAAVAAAAAAAAABDWAAAAAAAAFNXQVAAAAAAQ1gAAAAAAABUAAAAAAAAAA==" + }, + { + "file": "jeff/upstream/example-simple.jeff", + "format": "jeff", + "expect": { + "refused": "Q710", + "at": "operation 5" + }, + "base64": "AAAAAKwAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAARwAAAHkCAAAmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAEAAwAAAAAAAAAAABgAAAAAAAQAkQEAALcAAAAAAAAAAAAAAAMAAAAAAAAAOAIAAAAABABJAgAABwAAAAAAAAAAAAAADQAAAAQAAAAJAAAADAAAAAkAAACXAQAAAAAAAAAAAAAJAAAAAAAAACgAAAABAAQAAgAAAAAAAADBAAAABAAAAL0AAAAMAAAAAAAAAAAAAAC4AAAAAgAAAAIAAAAAAAAAuQAAAAQAAAC1AAAADAAAAAAAAAAAAAAAsAAAAAIAAAAEAAAAAAAAALEAAAAEAAAArQAAAAwAAAAAAAAAAAAAAKgAAAACAAAAAQAAAAAAAACpAAAADAAAAKkAAAAMAAAAAAAAAAAAAACkAAAAAQAAAAEAAAAAAAAAoQAAABQAAAChAAAAFAAAAAAAAAAAAAAAnAAAAAEAAAAAAAAAAAAAAJkAAAAMAAAAmQAAAAwAAAAAAAAAAAAAAJQAAAABAAEAAAAAAAAAAAChAAAAFAAAAKEAAAAMAAAAAAAAAAAAAACcAAAAAQABAAAAAAAAAAAAqQAAAAwAAACpAAAAFAAAAAAAAAAAAAAApAAAAAEAAQABAAAAAAAAAKUAAAAcAAAAqQAAAAwAAAAAAAAAAAAAAKQAAAABAAAAAQAAAAAAAAChAAAADAAAAKEAAAAEAAAAAAAAAAAAAACYAAAAAQAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAADAAUAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAEAAACamZmZmZm5PwEAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAMAAAAAAAAABAAAAAUAAAACAAAAAAAAAAUAAAAAAAAABgAAAAAAAAAGAAAAAAAAAAAAAAACAAEAAQABAAEAAAABAAAAAAAAAAAAAAAAAAAABgAAAAIAAAAHAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQACAAEAAQEAAAEAAAAAAAAAAAAAAAAAAAAHAAAAAAAAAAgAAAAJAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAKAAAAAAAAAAMAAAAAAAAACgAAAAAAAAAKAAAAAAAAACwAAAAAAAIAVAAAAAIAAAAAAAAAAAAAAFQAAAACAAAAAAAAAAAAAABUAAAAAgAAAAAAAAAAAAAAVAAAAAIAAAAAAAAAAAAAAFQAAAACAAAAAAAAAAAAAABUAAAAAgAAAAAAAAAAAAAAVAAAAAIAAAAAAAAAAAAAAFQAAAACAAAAAAAAAAAAAABUAAAAAgAAAAAAAAAAAAAAVAAAAAIAAAAAAAAAAAAAAFQAAAACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAABwAAAAAAAAAAAAAAAAAAAAEABAAAAAAAAAACAA0AAAAyAAAADQAAAEoAAAARAAAAGgAAABEAAAAyAAAAaGVsbG8AAABIYWRhbWFyZAAAAAAAAAAAUlkAAAAAAAB3b3JsZAAAAA==" + }, + { + "file": "qasm/every_gate.qasm", + "format": "qasm", + "expect": { + "writes": [ + { + "to": "qir", + "profile": "base", + "how": "exact", + "blake3": "1ec96352d855778f5a41c09d96fd0d260632663f03213e225255f074e8a4c022" + }, + { + "to": "jeff", + "how": "exact" + } + ] + }, + "text": "// Every gate of the language once, on qubits put in superpositions first,\n// then every qubit measured: written to QIR and jeff, each read back the same\n// circuit.\nOPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nry(0.37) q[1];\nrx(1.1) q[2];\nid q[0]; x q[0]; y q[1]; z q[2]; h q[0]; s q[1]; sdg q[2]; t q[0]; tdg q[1]; sx q[2]; sxdg q[0];\nrx(0.3) q[0]; ry(-0.7) q[1]; rz(1.9) q[2]; p(0.45) q[0]; p(pi/4) q[1]; p(-pi/2) q[2]; U(0.2, 0.9, -1.4) q[1];\ncx q[0], q[1]; cy q[1], q[2]; cz q[2], q[0]; ch q[0], q[2]; cp(0.8) q[1], q[0];\ncrx(0.6) q[0], q[1]; cry(-1.2) q[2], q[1]; crz(2.2) q[1], q[2]; cu(0.3, 0.4, 0.5, 0.6) q[0], q[2];\nswap q[0], q[2]; iswap q[1], q[0]; rxx(0.7) q[0], q[1]; ryy(1.1) q[1], q[2]; rzz(-0.4) q[2], q[0];\nccx q[0], q[1], q[2]; ccz q[2], q[0], q[1]; cswap q[1], q[0], q[2];\nc = measure q;\n" + }, + { + "file": "qasm/clifford_t.qasm", + "format": "qasm", + "expect": { + "writes": [ + { + "to": "qir", + "profile": "base", + "how": "exact", + "blake3": "967659b446471c62697f49df341972a45f47bef59d3b2fe26bfefc6670f6a50d" + }, + { + "to": "jeff", + "how": "exact" + } + ] + }, + "text": "// A Clifford+T program: written to QIR in Clifford+T intrinsics alone, no\n// rotation, and read back to the bit-exact method.\nOPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\np(pi/4) q[1];\np(3*pi/4) q[2];\ncx q[0], q[1];\nccx q[0], q[1], q[2];\ns q[2];\nc = measure q;\n" + }, + { + "file": "qasm/teleport.qasm", + "format": "qasm", + "expect": { + "dynamic": true, + "outcomes": [ + "100", + "101", + "110", + "111" + ], + "writes": [ + { + "to": "qir", + "profile": "adaptive", + "blake3": "4ac7bea67cb849aac1093b126a2da4a1edc5240176b6d0f42579760713d3c36f" + }, + { + "to": "qir", + "profile_asked": "base", + "refused": "Q720" + }, + { + "to": "jeff", + "refused": "Q720" + } + ] + }, + "text": "// Teleportation of |1>, corrected on what was measured: written to QIR under\n// the adaptive profile, every shot reads 1 on c[2]. jeff writes no branch.\nOPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nx q[0];\nh q[1];\ncx q[1], q[2];\ncx q[0], q[1];\nh q[0];\nc[0] = measure q[0];\nc[1] = measure q[1];\nif (c[1]) {\n x q[2];\n}\nif (c[0] == 1) {\n z q[2];\n}\nc[2] = measure q[2];\n" + }, + { + "file": "qasm/measure_reset.qasm", + "format": "qasm", + "expect": { + "dynamic": true, + "outcomes": [ + "010", + "111" + ], + "writes": [ + { + "to": "jeff" + }, + { + "to": "qir", + "profile": "adaptive", + "blake3": "fcb9b8d9517b3e76ae4f0a800222ca1624cc59d6a594f51781515d661bd169c2" + } + ] + }, + "text": "// A measurement in the middle and a reset, with no branch: written to jeff\n// with measureNd and reset, and to QIR under the adaptive profile.\nOPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[3] c;\nh q[0];\ncx q[0], q[1];\nc[0] = measure q[0];\nreset q[0];\nx q[0];\nc[1] = measure q[0];\nc[2] = measure q[1];\n" + }, + { + "file": "qasm/timing.qasm", + "format": "qasm", + "expect": { + "outcomes": [ + "00", + "11" + ], + "writes": [ + { + "to": "qir", + "profile": "base", + "notes": [ + "Q721" + ] + }, + { + "to": "jeff", + "notes": [ + "Q721" + ] + } + ] + }, + "text": "// A barrier and a delay: neither format has a place for them, so they are\n// left out, and the conversion says so.\nOPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\nbarrier q;\ndelay[20ns] q[1];\ncx q[0], q[1];\nc = measure q;\n" + } + ] +} diff --git a/wai/quantum-conformance/formats/jeff/controls_after_targets.jeff b/wai/quantum-conformance/formats/jeff/controls_after_targets.jeff new file mode 100644 index 0000000000000000000000000000000000000000..82c75d98ceef7504141c808ee127b38d70c35a97 GIT binary patch literal 2056 zcmeH{J5B^K42Cnq@_r-AEkFYC5J(_|5Y=!1ESl2MZIL_-y8E&QXZ2#-_epvthO7|MxT3arU?^=5< UkN5trbyqw$Z&%~$;_@1pF literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/controls_after_targets.ref.qasm b/wai/quantum-conformance/formats/jeff/controls_after_targets.ref.qasm new file mode 100644 index 000000000..9028c604e --- /dev/null +++ b/wai/quantum-conformance/formats/jeff/controls_after_targets.ref.qasm @@ -0,0 +1,10 @@ +OPENQASM 3.0; +include "stdgates.inc"; +qubit[3] q; +h q[0]; +cx q[0], q[1]; +rx(-0.9) q[2]; +sdg q[0]; +U(0.2, 0.4, -0.6) q[1]; +ctrl(2) @ p(0.9) q[0], q[1], q[2]; +p(0.25) q[0]; diff --git a/wai/quantum-conformance/formats/jeff/custom_named.jeff b/wai/quantum-conformance/formats/jeff/custom_named.jeff new file mode 100644 index 0000000000000000000000000000000000000000..138d5b2e4fb78cef9d66276d8c5369fad17801bb GIT binary patch literal 952 zcmcIjJx{|h5Iv`9MI{)D7%Ie$#L$VoLzjrL6GI0q1&Izpf&uzh_;>sZR^B`NY$pm# z?L~L*KHi<}I1v%~fKn=%1_yQ$y#??V`sGAcF*gP^k}h?LoGGc6Ir=_}h70)Q&LeWg zAE?O^j#Ea+yaCAXpywf__664AG1;6-62Q1utAUzR$9bjWc(uOGzi09Pxq}ifBVG^i0deQ|uK;Oi UAe+a4+e5&{x4!f5{9IIi0rz?ps{jB1 literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/custom_named.ref.qasm b/wai/quantum-conformance/formats/jeff/custom_named.ref.qasm new file mode 100644 index 000000000..96207b931 --- /dev/null +++ b/wai/quantum-conformance/formats/jeff/custom_named.ref.qasm @@ -0,0 +1,6 @@ +OPENQASM 3.0; +include "stdgates.inc"; +qubit[2] q; +h q[0]; +cx q[0], q[1]; +rx(0.5) q[1]; diff --git a/wai/quantum-conformance/formats/jeff/pauli_product.jeff b/wai/quantum-conformance/formats/jeff/pauli_product.jeff new file mode 100644 index 0000000000000000000000000000000000000000..6956a311a0c598b448978f1806f3216ebcaaba7a GIT binary patch literal 776 zcmcIhJr4mv5S?8|NRUXbqm)S0N`i!lghE20kWeTTv=Tq+{)$S}{)zYYgFO`2n9G}a zJM(7d_D)1(3(&OWErHKDRzqw7dnHnJxnChdQoXuFibfR44e=Z_RKTe%xyX%Iz*`KW zjKXF%2-L5EvP~JkgjaaHtw5+-0HJP%bEum;-Sk5@ayrhSjREo@5YFr3tO0L5kS0J~ z!zU33K)5$38Tx+nbS0b}R{Z~9j=jm`afy?A-=En>4d0pHv;fe2B-f@hy@9jpfI^l1 g_6}M)Mi1xg-)FxkYmCfYvZ}vzymuQo?hYqbpOe@SL;wH) literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/pauli_product.ref.qasm b/wai/quantum-conformance/formats/jeff/pauli_product.ref.qasm new file mode 100644 index 000000000..644902aab --- /dev/null +++ b/wai/quantum-conformance/formats/jeff/pauli_product.ref.qasm @@ -0,0 +1,15 @@ +OPENQASM 3.0; +include "stdgates.inc"; +// exp(0.37i X⊗Y⊗Z), with its parity carried along a chain rather than gathered on the last qubit. +qubit[3] q; +h q[0]; +sdg q[1]; +h q[1]; +cx q[0], q[1]; +cx q[1], q[2]; +rz(-0.74) q[2]; +cx q[1], q[2]; +cx q[0], q[1]; +h q[1]; +s q[1]; +h q[0]; diff --git a/wai/quantum-conformance/formats/jeff/refuse/angle_from_measurement.jeff b/wai/quantum-conformance/formats/jeff/refuse/angle_from_measurement.jeff new file mode 100644 index 0000000000000000000000000000000000000000..bfcfbe48da2603827fdf35ffc26af326ea45e1f4 GIT binary patch literal 832 zcmb_ay9&ZU5ZsFo6jTr_tZfAGlPn@AY7xOt@T+Z|*}F-WXkp>X?##|jZsT3d%q~bq zWur_`2Zb$xTjVpDEsFLHh7eQxl-a~hLrZv0`%{`D>75qj#1~Sr21)M7$^$@ugPdi_ z>lHQOr?`R8cSc5EjXwI0WnGBYiSR-Gp*N(pMEh^Mvx%Ao5V-yj8*Ye*L-h z#_0ZGR6DL2<4(fdh>m8t;wKpBww^$d&iX6WM$9#>>C^ YZs*?^&ngDK*w}t>uI`Y{@pk9p8?CJk`2YX_ literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/refuse/custom_unknown.jeff b/wai/quantum-conformance/formats/jeff/refuse/custom_unknown.jeff new file mode 100644 index 0000000000000000000000000000000000000000..b23b6fb86f7c20fc9910006753754c9ef808b21c GIT binary patch literal 472 zcmZvYzY4-I5XQg6DhfI%4q9+<5*&O0SC``GqHCZ_hdOlfm3<%jG1LT>0*If19)b zaHYgNrW(*sQnV&d)R(%XS_u`4?!N`h<4}c;lzp=m1DRbU%85_PO!i!y(8`gJ`2)4o zQuI4|$H#2XK>9|jZyDrsRv**eG|L)3-lV*(-{<3*KHHHyB|@Bjb+ literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/refuse/custom_wrong_qubits.jeff b/wai/quantum-conformance/formats/jeff/refuse/custom_wrong_qubits.jeff new file mode 100644 index 0000000000000000000000000000000000000000..7ec2f84e8f71cdecec8136b758cf586bd2373789 GIT binary patch literal 472 zcmZvYJqp4=5QS&gC<D*qSrnb9@)4L*0R!I1ZRg2+|juE!hl YF10T}YC72Usl0!bb!t+V-i>A=Kh*&W?f?J) literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/refuse/old_version.jeff b/wai/quantum-conformance/formats/jeff/refuse/old_version.jeff new file mode 100644 index 0000000000000000000000000000000000000000..2b13893d24fb67ef2acca187fcc8e66425ff3db9 GIT binary patch literal 272 zcmZurF$#b%5KC(X1r?l}odg$WpX1m;|38}RT}wd^atXPR764X4#-mli?M%(&jfli# z-X8^;4ox|ANd=tbrMSRIE!o~!UOb3A+9O-1&JWonYcBt?I+)Z3pK|_tE{-(p#C^p_ F%ok*Q1E2r^ literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/refuse/qubit_twice.jeff b/wai/quantum-conformance/formats/jeff/refuse/qubit_twice.jeff new file mode 100644 index 0000000000000000000000000000000000000000..5d82f0db72a8578fe98665297be373a6e41104c6 GIT binary patch literal 528 zcma)1K?(vf5KHSS3MvTxK@kN#h-aVT(Sv@$clbnqVX~b_Swwf1G})P?LjmASs4|8{ z@En9%kXOWum`Cp$HEgC&3K+Mh22b*-3e2eGodj#+gK$kLQtK$4BT4=Pp_yw-k@&fG z6q=jeUsKdR-6x;fzJ15%ewV#*R1GpW=smwG$FOg%yftIVoU^*(-+!#>O?uCUxLvMy Fh2ADL2TcF~ literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/refuse/truncated.jeff b/wai/quantum-conformance/formats/jeff/refuse/truncated.jeff new file mode 100644 index 0000000000000000000000000000000000000000..5dd0fcb0b5560b146cd7cc67e9912e26bcc6c532 GIT binary patch literal 200 zcmZQzU|=W%Vh~_rV1+P%Br}vH2&C14cqStQgC0~28^rMKXm szzbx9)N(>;kd5Dg7zQMO3`VdDh)5xj0;$ghVjhSPn9Kl@j6etj0J$dwr2qf` literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/upstream/controlled-multiplication-modulo-n_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/controlled-multiplication-modulo-n_3.jeff new file mode 100644 index 0000000000000000000000000000000000000000..0cbcdbc34c1f02d63466dd41a61ba8a5d43e504a GIT binary patch literal 30360 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 literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/upstream/example-entangled-qubits.jeff b/wai/quantum-conformance/formats/jeff/upstream/example-entangled-qubits.jeff new file mode 100644 index 0000000000000000000000000000000000000000..b64ddb2a8cab18c0a1f8dbc8a025c6a88b1732a6 GIT binary patch literal 3120 zcmeHI$x1^}3{79_Jdbt8o%joWfuG>EJGXYFAK-uKLU5rA!G(f?f}({A3JSUq%y}=T zr12K%wwUsIn&c$8Lz*Wda#+$Yl}!4IANpOkUsH-3`ZUzFX0{Ym)L3doi45kXiZnFQ zljTpvp<)%mCcBCyaR#%0YmQNp6hBoj=_krZ;JtR>y#(HK2i{}g-F4vI2HsWRU7MfC zbzcVFz3Q{-B$9U%cn#wb$$JRAH{%h>YX;tz@rdNT8;|`!`(O7#Kap{~w~PkE)V+bj zx>)~;>M5S@1Mtu%L`yH+Z>)>+^L>T$^L26msGH*Xy6h7o>#|>B-BiE$y66+sP4RqP z_7jnH*;lddMcD6I*zdX3P4RqP@NjNK);(1_c(Lw@@yR<<-S78Ub?i6A^K}_d)Uc$) z`XsTRs1tur9C`}mWbAk5EZ=E*b>L%d;13uojit=bOx?_!ff%}CtKWh5AwptIJ|!#I2SL)`$c5hjO|%OW?JNO zeh*ZU4?f0W)$u7A<&xK-~%KKK~7 zWW1OkcL`Z8pe+YX_^%aGZhPAzAAF2kHCD{OpW8EOD*FBZ>%DazcKtmIm=2g-obQ;g jUNTwts{W69i;wrlmOentMB-kpUauX9Y^x2jQ-J&c$M0vP literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/upstream/example-entangled-qubits.ref.qasm b/wai/quantum-conformance/formats/jeff/upstream/example-entangled-qubits.ref.qasm new file mode 100644 index 000000000..0f169c58a --- /dev/null +++ b/wai/quantum-conformance/formats/jeff/upstream/example-entangled-qubits.ref.qasm @@ -0,0 +1,11 @@ +OPENQASM 3.0; +include "stdgates.inc"; +// From the example's text: x on each qubit (custom X) with one control given after its target, as the schema orders a controlled gate's qubits: qubit k + 1 controls qubit k. +qubit[5] q; +bit[5] c; +h q[0]; +cx q[1], q[0]; +cx q[2], q[1]; +cx q[3], q[2]; +cx q[4], q[3]; +c = measure q; diff --git a/wai/quantum-conformance/formats/jeff/upstream/example-optimisation.jeff b/wai/quantum-conformance/formats/jeff/upstream/example-optimisation.jeff new file mode 100644 index 0000000000000000000000000000000000000000..a1f1c4a2d60fc23d90414d4fddba8e5052d85687 GIT binary patch literal 1048 zcmchVJx&BM423n{5v>p`eTzDz{>LSjH4`6>%;UK&F$%;6F6hqvK_nz#$tOTc0 zkep-br?iy|Il9Eik&OWUlazeRy^Kzu;Pu5pJ$T0Ajz<}Amc_w&^fG;} zt3`Z>Lp_i_Ejt;UJ9g+uM*CgK=MwFe#rq)U<=wb?PQRy<=SR)D7qXZz^W;5<=J=52 zLI3r9t`jFO{xjSkdG&niJnC5Z54e4s?>g}Qx@TLL{UJ$3Y#VXYm+#%a+dAd-aU=Ab Z)^I literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/upstream/example-qubits.jeff b/wai/quantum-conformance/formats/jeff/upstream/example-qubits.jeff new file mode 100644 index 0000000000000000000000000000000000000000..66e6e60c57998a942d3e19498ab9f1dac4a3fc11 GIT binary patch literal 1224 zcmd5*Jxc^p44hrpPqa|14#7%n#J?bTjkPG)Uv;gVV4oTxZ|3toJ{W5U7WWZmp8M4|=Zbk;E1^+g8!g8P2(=Zi`%tX9j-Hbhe&(hr@oI?p_Pr0w>|M vxJtE3EqfeeFR{_Sv*^3uqh=hH_`IOv3##O%2aJm|p59LGrZZgXL#F%z$h;YI literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/upstream/example-qubits.ref.qasm b/wai/quantum-conformance/formats/jeff/upstream/example-qubits.ref.qasm new file mode 100644 index 000000000..0c07932a9 --- /dev/null +++ b/wai/quantum-conformance/formats/jeff/upstream/example-qubits.ref.qasm @@ -0,0 +1,9 @@ +OPENQASM 3.0; +include "stdgates.inc"; +// From the example's text: two qubit arguments and one allocated; custom X, H, CX (control first, as its name says) and Rx. +qubit[3] q; +x q[0]; +h q[1]; +cx q[2], q[0]; +cx q[1], q[2]; +rx(0.5) q[0]; diff --git a/wai/quantum-conformance/formats/jeff/upstream/example-rewrite.jeff b/wai/quantum-conformance/formats/jeff/upstream/example-rewrite.jeff new file mode 100644 index 0000000000000000000000000000000000000000..3ce229074b5ab56beea0ef5d3cc3d9957aaee0cf GIT binary patch literal 2968 zcmeH}%W70X5Qcj$Nxa|h_gz2)-#`#wKrmneiWeMkz`-aAMiAn{g>T}*ji8{Q3*XCy z|986n(`PypXA@UiOrO82>aXhV>OOrYrIeoc^pNz^Lhk4}h~8o0vf74R*TOanhBEfr zsFZd!q+wc9qNRR`*sm_!-D<}Impz#DvufwnZmRXfbm)7{k+^rleL~BobU5a{4c?oI z_p0J;1n*_VTMwR~k!RsIhB-Zv9gcZpby$zlyhj!9LGX;`-3gxcwcr_TUuk%Hgph}N z7;Vq_3eWR0nwRI-YwO24sq-ZAn)g_Gx6b5&H>!B|D<1QLcemoL25&J2V4YXgpIsO4 zZc3lTqt3<%PakFPwY!nhH|f;HXx`_F_cM4Pc`e^7-jCpcUEbH=2^wL%bLyEN=9GEm zynG4X?D-WQ>tr1ub+mktu3twwFOjUpH*GJK9JG5*65FTQ>>e%6+ z^ZxUWdwy(>FN9Os1lmGcB%RkIvY2iuV_92LULb)U|IoqXMMNnNP5 zb2z@P&v2Xic>X7q=agC|#9H!EGrkYC-}56L zxsajVLv;3nc;sS+)^FYCZ?p0D(=VaVJf{Epzg3TZy2Q4(GEevW?togi-8j#0-cI4X z`Oow3)I9!H9=;QQlG(RZ*NLoraRvSnw Q8Cw0;?d$y2ZFT#91LI9q1^@s6 literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/upstream/example-simple.jeff b/wai/quantum-conformance/formats/jeff/upstream/example-simple.jeff new file mode 100644 index 0000000000000000000000000000000000000000..e8db19e620af7196747cca4bb43add641401e798 GIT binary patch literal 1384 zcmd5+Jxc^J5S`7fA17J}79ti_Vqs-vXX!^NHdij3U~zJAjrcPJK~HQ11qB5yM6a;$ zpZQ+0Z<7sHRt~&tvsBsF7|0pSpJ- z)xFjlj?vx#`Y%ALdqm7$TGCTJE=K6>9$#vtyMw<3h~av>#a9|8#sKFNGe^4wibSbC zhU{d@YLdI_Y`uyFj`Z2%DJWw7Edd zt|%67`f+DRVEq$Q|2=lL#$`5^$7Qy2`99`x)98;5&nvZdWR|`!xcU z1RaG!p-}iER9esP%slsF$!lJQ4I_vFBx>I4d39S-CHhwn%$pmXx-WRQ8sL=lc=F058}jB^ zLU_qf#o&KP!COAd0b|^E`QW}uYurc0HSVioa9^Y~?p?vTluu>eR$<61jbPlb_wwPf zl^gOxV=sO=ek*fOYMZkuJ(c5Uk~5F3agPHBV-|Q$NY|zHKFqf@!_rIHp|pK&-bX(f z_gci@)@7;F@!H%Z8;q81s?dE&-c4q4-Yx>!M~%5bA7Hdzh9i+FDd=z zz4$G)?36>F$s;5}3vR&Wuk5<>tEidaky0EywH}VgjJv%-C literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/upstream/ghz-linear_3.ref.qasm b/wai/quantum-conformance/formats/jeff/upstream/ghz-linear_3.ref.qasm new file mode 100644 index 000000000..d74da1f91 --- /dev/null +++ b/wai/quantum-conformance/formats/jeff/upstream/ghz-linear_3.ref.qasm @@ -0,0 +1,9 @@ +OPENQASM 3.0; +include "stdgates.inc"; +// From the benchmark's source: h on the first qubit, then each qubit controls x on the next. +qubit[3] q; +bit[3] c; +h q[0]; +cx q[0], q[1]; +cx q[1], q[2]; +c = measure q; diff --git a/wai/quantum-conformance/formats/jeff/upstream/ghz-star_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/ghz-star_3.jeff new file mode 100644 index 0000000000000000000000000000000000000000..e3a5722dbd478d88cebc517cbd206fd9aa301f9a GIT binary patch literal 2848 zcmeH|J8P6d6ozMa6Yuw1jMsExSVIsC6$=FgEd(123yZK-iU@)~!B()bvsAPR*a%vP z+G!zTVPWBa@qOmZ`+dXUvOhpiICC!Z&O2w$%(qD?rO7~pWSHi|(KssJqU;&zZt0$q z!>kK>x!CivP%KL~*47`$q&exJV@YWnNo6mIcwv}>JirU~tn#~Lso-O&M{XKR2SRws zZ^ht$Nx@sb$N^*APx;_}NNe0D#Wn7mVsKxjHSS%(xs=ak-d16jHyXjXUmxVdV=Fhy zYmI~a7x7z}LsHwwru0IN_atW?TjQQ*9E>6GoRGek*5@$amVl*A*|D_UH_kCk#yt@+ zxM#A|X?d!6pbmQ9TWTizVd7xP zmYs6wGkF9I--Y?sO3oGxaz_frxO?(hYb!U)O^wEoFpq5pX}Bt_-!VMYeP2p_7W0|& zt|q{NuW{EEgK?jHMt*DdR(WE2uur;Q>U)u^wce%w9>^nL;W;2ZDD}I62R`zM-?-;} z2dqI3hsl;ZaJ}rz-TEev$X}4UqG|X1QW)>?O1|Z&oJHxelAzA|o^LB=oo#`oD;eiV z-=kc5iS^l-y&$dc@0w!n<%YDrm(`5x`!x+W&NzM%La3cH9GAlDzR1l&4ml}3C3U~S zvyMD+I!yO{l`!`O9BYxKJO&=$edit!?gDw7$6=Ziz_pV4AOG9^7g>UEELT1gk_%?3W&x+fA%{otuZ~5DL+PMGU Y5Hs!imVe{nqjQsqekWH}SNIM517YPgO#lD@ literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/upstream/ghz-star_3.ref.qasm b/wai/quantum-conformance/formats/jeff/upstream/ghz-star_3.ref.qasm new file mode 100644 index 000000000..af5082c23 --- /dev/null +++ b/wai/quantum-conformance/formats/jeff/upstream/ghz-star_3.ref.qasm @@ -0,0 +1,9 @@ +OPENQASM 3.0; +include "stdgates.inc"; +// From the benchmark's source: h on the first qubit, which controls x on each other. +qubit[3] q; +bit[3] c; +h q[0]; +cx q[0], q[1]; +cx q[0], q[2]; +c = measure q; diff --git a/wai/quantum-conformance/formats/jeff/upstream/grover_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/grover_3.jeff new file mode 100644 index 0000000000000000000000000000000000000000..39a785934102c059ee171c8eb87f15adb60ae066 GIT binary patch literal 5792 zcmeI0y>m`M6vp>`69hp*g7{2GNJv6_e}By=6bds6N|QQCyGdsXPo!!Is5F{vuE$_y%9nP-jsM72}0!y}hzoZ-2q-iM(dTo6Een1#dRw@wF*ywK{Z@FVgtJF2r(b;d?V6$8`pS|=c} zwOxON(v4VA8(Akh7mp`CV|+7tzQi*--A}CdiG+2nmOPRe)O(eLKJF98IW3!U)@nwK z>en0`mnDp~#s>x^JRRl|hv(zG1is;TUKhEL6Zayy`gu?@?h-!WxXDUf+H11I^&}24 z?AP@iVeNUTBW-fy?zKPFCEf8G2F<>mIU(OLezRdu!z(UW_ zL5&%gER}sH7jl}AOiIc+%N61E+J*}bB~{gtIK;F6aPGHG` zG4Wgva%T;EKU|QwE^0>L0~aNiB#sZCargoA0lqWppf2z(W#^Tw;nOB(z&-%>0(8fY zX5jA*{j0ESFV|!Fd8m%V^7$z4hh`XT6-TXhcKt47Ab)Lg_4pYp`^$J`e`yo*Zz%bt zU5;(nU-C=49N*4f?Da18eC_H}_P4X!)wAqh?Da18eC2s+H-E`5>(#FQt^aXK%yN7g Tf9C#!iRo#5hfW@zEvGMzzbLQT&2XPA@jT!jvoSFG{&YU@Och@=R-WPgEYOXgd z^z4bNA?Z<}Pgos&sJ^An9gX^sG%5BA?Q^pirCePo$}G+;!zu5A5)W8%dB_X*gM6!` zso+wqM@u&%6k@0?d#H~1@cWqAL^b^ zl&5g6KlT}Iz4CO~m zL$klhyU4TK_#}I)g2p%AeB_x8k9|#>zv5Z$sRPaVOqc=g$NF?eYk26C zcUX4%KAw=xo+kzPCdakl9&OS-$|jCZ^UcW)v(wp6&cK-(0@4``=WED6Oi+TR4tjv> z6n6Plc<|1g5-xbNUVcxr4|VZ*H21(eWP**~`oX;&w4n5eushoH+3Y>?jS8F}zlrUG zPWwPS?JIUo9_mJlpvtG^-y1z>_G9L#m-#^RJ&>MfpKN}=5b8?ps>;wu=I4NC22D@X z*&>wQ3-z)elGn?uBV$1&9SIKeH)Xbjv}PE*Bb<#GVs~Ku30~*;JTe}X*SweT>4|-J zg_(;aDcjHLpZB4fyQPDq+(mn_AOG__)U!7`w`;CZy;NFtvpdK#_?N|Zhy4!a@jPG7 z?^aK@_`lvSujg;?|EKfwo&PK8cD~<@=X>Y*ay#eec7C3GzH)y1%GKt~jQ%^f##{X6 F`~a8pU8?{9 literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/upstream/iqpe_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/iqpe_3.jeff new file mode 100644 index 0000000000000000000000000000000000000000..886149e8e439a86a8f3b517912b43a57d9640b4b GIT binary patch literal 4936 zcmeH~&u>jp5XaAZEmagPMJZZJ{c6>3MX97>qYFv57E%iml}Id!Kf|A3V_{*>f`o-c zBoY=977`W`ghZnf-_Jc~-kqLQU!;kJbeK0Y=X}qcIWu$ay$vCROI7`ds$oG?^xKfM z4#{3|i+EWYL)>f*;b@ZkBrD>b;)x5FuS$eQai!)ZghdQ$^t`rcVlokaF-JANRFVh| zr+B1>BjSn>wxmNFp|?wdH)@p4GQ+hdTuX+V5(b=|6hEv1@BMw#SVjLV6#Rf|&Tv08 z2F7p;60VZrK4-XJvhg=5_WORwaNiT|ONM)!a1sT5(l7o#ioL&AvN?y*dnw@Fi9PPQ zY;achH)-Ca%z~ciH}{7h{Ca_X8?V2fjIuf$W!tdTnHy5-<$Xaj zU+h`N)= zgtIf|{o`winEKiCV!8Ac;#P5+c%>M!ePe#gEU|7(+@VeA6P`b}4J>_?xLv$jyhglM z3}&5py|`0M{kDsFZdi?;%4RK+_>AsL0b})eEFB!uH^dD=Ov%c(+zE!+U%=?u7ge>lv?!f`g z4+=n?xj%QrMmwbNzTG8ezE9jQ=C1j^KPQ{GkkrNKxD=JD_DmX;4i4#YM`V*F%lj$W z^ucK{c%xw{bhF*$di|W|lQVFpJz~iAi&i`za|3Ar$=S&Pv{%e`h!OQYBfe4$G}gX9 zs10#bS0we~xn_iIc|g)f7898L;sYom@@)1&*@wiOpK&AG2kG<5yw58({T%y}x#?v7 z52s-y`?ZLPlm0+*AN+Z!-=G*GE^n7n#yrwLeA7Wla!OsSx1>(1%WU_LUWblF;W82j zY@9wE68o4z^oltTbI^$>YWYcKU_E3ncqF;@?*`WTcZ1BL_mM`tw;=9>C#xCvs-U7U z)i5RLvGewPKIZ$uE=l{_59`dx#qiGJb&xo{|%Y@#;$k1xz6W% f=KgX!$LDr_o;)8pe&X8ozVUH=3-%B6^DX)t^Vp;z literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/upstream/multiplexer_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/multiplexer_3.jeff new file mode 100644 index 0000000000000000000000000000000000000000..89e7eca05ce35420607bef1af9d6ef8a946b3a12 GIT binary patch literal 4192 zcmeH~F;A315Qg^-PDMaPK|w(e1rfbNFdBoA0EwlACRmtgK?nr}CL|UXCRW6lXkns> zf5D&NPcSjDw(|#AYC*K{efHboTZU6Yq8&rtZ+2$iotd58@9qd8gwCoSl4__G13lZ) zXh3>Q=oStoAFAU(2se_xB3%(K3ag7NkEBAoP}z(Ug6PrvK@-<4xIDy#`(BSOX)1W0 z>d`RF2^BHmAoEJZct#q&NkcZvj5nQl*Boyo@hrtu;x!#_(DB9-Z`AR69d9`Ch8(Xe z@uZ^krFqlI9opqL4!#~4?5R8PjrU7~ivDaL9f?EGHg1=Pg8D!7*+KUuV;@sPOkUZ!P>B-k2>p zZJTEUsjR4NGbet9Pg0I>^Y7!Xbh#HIh%hvno$=l!}P8;<3Fn0TgTM!Bu0 zac~fa{;vqO2MyzALViw*^1*ABmw0xw;1LI%*GJxB5BbRz`(*E@1jP19AMlZLNXS=l z-o^bd-+xh}&^!3_5;-lL5$xQEM_hbJJwA(v?E#DR^nVybg02h1v$N4~bKi^+?gMgG zI49V9iVxv^B@c7RNTH&Sy;CQ51I^s1a9$V_kV>Ia-m+ruIsA_Td&JJ~7w^-!z<2y5 z;eX$&`^v%jqyKiV?rNTT?+N*RnUl?4=>zg7-c8LFx1bhz_F%v4wUHclujm1DO<_VP zJ19ef54kK%3YH%}>+mB}h1zz$^p>2gMfh!nz^5LwtsdqO?h9h)%ef_UrwAJfGhdx$O4*Jm2@r*W>4RyFRbA)!6UH=lA(m S*B&>Qmh`{b#N-6uVZQ+<=V00Z literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/upstream/qft-adder-classical_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/qft-adder-classical_3.jeff new file mode 100644 index 0000000000000000000000000000000000000000..e8c63dbe55c6def3de6fb41ad04c56b0e95fc7e4 GIT binary patch literal 8248 zcmeI1Pj6O56vl5`E21EvsGuUne^70)Xw{-%i?&*nDriwqz+zh!qzFk((CC5%i7c8x zq6?QU2yx*DaHU&UF5T*CM7Q;l|`c{k*EGo{+v>=ty`#dhY14lTw71-%+aSI-K%;ufeiq!3YoH z!u>}671HGMW2YWT#-%&NfP);Q|-d@>o zyzb$|J6Q0>3*P?3+gI>L63^>SB;M|VH&pP(5^q<*8z^`?6K{LL+md)vLAIrN>*TI_ zVc)TTtLW`9GItKb8x!bKZmwdQ_7KrQur+$h2eoFF?#x6k`UV~n zuTC7|*-y=p?zXJ!^|@~l-XTZU=NZ;Yk8sR44X2CUyJhS6HxkK|~ z3Qk|%BK#s)=ox&s3bzUC1nyYwQ3RX&&h`6HHt(RlHw~*90gm%HtuZ`n4TDVSX$}s@ zvBrlS5cs~#B@W-~2SUE?lx#T8&+DOc-l4?t`OeB772x#=mU#{MZrVE`)`$Jvk%(T8 z+TkNNz7NOOP*FNUUorMR7$b0zyP|6K4{?ZR|Hd@m9yy*B`Shgu_0qh@8FF^K*CXKL z3^xgP3-<{33ik_CbOC^L|;7O`hod zJkH3bh7Ng7V|dmY2AS6rj(MMG49{A_ASd)R2M2M;<-7pjGNVD>S7&9zgP-SjD)8!< ztOpN%&hxsioby8Gymw{Cp2<5T){a{C35ffOy5j2>wq|~8YW@i>RP|A<506GFbOeqnF;$#nRM0*CP(`>>p-Qf+mOjoH!fS<6Zp7h_Fj|T6jhn6%f~GvGb!(Ht6u_vo*pGf@MsD zXNBj4ae=x$FK}l0eKVVU=)9hLWxKC5tPTp~1dsJBe5d*EO9IwtE94}Q+`y0L!s&!4|~&rd2IwR}xLs0l({ z@f{H8N&B&>_3OeLLf#dBcF)5l4rhiO5)KRgu8Bun{K%20TJzZC!#l{EQH7`XjzN6- zU`jxy1i0+eJ)&W2KlJ37a8!6pDEG5mJ|sU|Y`DmDRINOTLp=LAZvrTek| z9RBy;19zK$CbF&iH;K4O{*K7Je-3w6J#ivEOMYpW{3e^#ycSE8cH82M7$u*--sbt2 z^G*3Q?Wbw}zg};V61BOS@+Q2o9r@vX_xyi=l^c} s@;+|bf62eveCq!r88a{6pF2I@KRq4i_*wYJ_2S>42e%Gv<=?!&1LK|9z5oCK literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/upstream/qft-adder-quantum_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/qft-adder-quantum_3.jeff new file mode 100644 index 0000000000000000000000000000000000000000..955bb7e7de08adade5899a0851727a7e859f1da4 GIT binary patch literal 11672 zcmeI1-?Nre6vv-)ItQgFlv3%$AyNs)k0_@&Q4}H)#Yu&z6h$G5q9_WbkO~bh%y3~w zGmPoNMHi+k_x=PIW@xzZ{p`Kh`|Ptir+40war3U}`>egz{_eHcUVA_Lecw|qlA9K8Nm}D)B6_1^=hK8lx`443(FQPSuRyH2@RDfMR6KV zd6^Eb331^LR~-Ed8SJykWzQ2D#DIhRtiiO8NW(YzPByWP_fz5>cD%10?}x-YEP7k?-0+dGLDb;d*C&6 zi8oBc2Bi5dg2j_N2=j6#dOO~qiU-GzT65|*9Y+_%Z)sku%Q4wAQoJ@JN%z(G21PX9 z5>3k4T_@d<@ixlNd8;*m{teP&a-Qs*cStV~{fDKgqsjNObKc6{ML}L`rKyj}8rkra zevr9+;=ZAkQbnf8hGV>$iHFX46BCbta0id=nElttV?Sb>l>Xk6EgpSk``32%z@+3K zYqx!2hjLDtL$54OyfMbHS$;MjI_J$x9NYKBvO5HLV+4~`YH*Kr zJt46^)T%WRZ9Zy;?;_!1!Q!i_C=JKG*Z2{*$f&3)`%4_+nV>cCi!#7eGEnFtB*0I7k0ikwCQ}(3Ed&qeiE&^i)2qTQF_T|EO;R=E8XIBaE z<5?`v%QpEq2k3l1*2vD!%cjILEi*{mk2247@)FNd5_DEXQt~uQafsSwSd^UqOSO^5w6Yr*wp$u zVSl?PnnuxAM0t#1(QULhWF@gozXs`O)%4`Yx?QH7_i9fSC7!i^Xb zG~E1*;O8uMcjx5yAfFN3cf@C%)P{c6XnWnR_GDp-aFcMe&>>6}ZV`+Zb1XCEe9sVm z5dr=XA^$wRRrWMtx^SCty8wTtaEEZGfUw8;{m5E5Z|Hpgc^}CiAnXS`bk1v)4afF% ztlH!UZ;W8Vesp(Fkt@01WyDIP=wKc6&r1EQhqkTB*39@B!rj6>!ffGQ;XVO5QC->p zp6-n~>f>&oD?A`PD9jUv33(m2$)-N&ygv1NLj{pgH)=T_Lt^KOy5f6CcsTQ0tvlsg zAlUg*Q)7F+H{cS7JwqN577BK6bgE4*_>o0XRr<+;d>Df)jw(ExvNrKK2ahHn8ZPnd zjL>Cgv8Q*E`+>D_M@&wd+TceX6S#|)2u}!43P{!StRTF91B9atNU~IVnXp`VN?0MR z6ga#2d1fv6(D`}hJtIHc5B87u9Uk+UkaPa~ir@9bGi^M6-`iU7+209F8;{@j`R5>U zcnA5rL7rb3&oYb=ST}2aI+|hK=41WTgg$)Kuv&Olcuqhp*LW7n6VXq7Z9-4? z2=~BR;d$W&;YDGcP`^jwxu6#LK5~9IXXQx?MyMUNd?}iV6YGkP&&2gY92;$Fy+L?I z$WO(I?h&}_1@;ViRoE!lJwiO<;zu?`Rq4kjAI2cBMHQY+S)2H*{q^KS!_ChKUA7+1 zBxiK9z;{Kz9=?Mhc21)exwPL%4qFFth-dycQ$O0@5p;1cF=w;D-84u*whDaq*}QR$ z9Q~!ak1XCy*^D!tXyJRGaq#i|hjWC6Z_=hlet%4n zO+Hfv_$K4kAfDByC*GLAi{EG%OYW>;hwzS2u3Ux$AM&p7o?!9eGY&tpGpfq{pUhAf zc#K7MMHTq8^L=TT%^qzN-WPTYdj!O4)!qKIs{e>UUD%(ag2@MJd?=meOEuR-Q z@BB2@w9Ftg^fV5RxdQuwW-gNsHS&F&C7XQtu9xrQWZA^CI`_nz7yg zP=6ol@52e+hw>L&B-Ls+7A;wHzKhjn^V`6_a}&thKA!LQc9&$jF+9JT-A#xtEaS-|ypj+w;BcKW}~gtMPrmpWoa3 bpF}QxywAt?`}{oL_n++XdG6h{YhB+zz;z;& literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/upstream/qft_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/qft_3.jeff new file mode 100644 index 0000000000000000000000000000000000000000..7b525dcc5b49d11e7b24c38d7b4bb1c43fc7c546 GIT binary patch literal 4624 zcmeH~O^B9N6vv-;#>tN|ozm3GoUu&HnaOJUFbxf+oeKqnf&xPYg^Yv=a?zqi3l~Al z+PMh|TGS$_MO2GG7ZEO8vDN)+3NRQ(={o4i zdzFb_BRC%8%Dv3vHnbUhW3VDK=m88l%Fn>@Pe6XiMFt)tcvs5!GP>q{i!HAlx%+{! zYc7GB_hsP(@B700uHk*w@V+j*uNvOR4eyJ>`@G>@D7+}aCuKe~W|9wh%862b#Mt~p zGfeV6&jEk86#GN;Az-}u;!}MJ22>?@s|oDG+nMb=}>i~O!(yRSNGFZGwi%Zq}G z_dxda{>`%sNB^Au<3P_DAKs0usDFRbyq54q#)rA+=yMn!)<(a5RZG}$-iakqdj86? z$o*VneAk#)?B!hKZ=bIQYrw6*+SubUpV!37Jhz|u=Lu-NH-g_mh@ZK2U_F=wcYr&= z2B2&Pem!%z-drDRW&M=c&)Vs`3v2=rpCOzh~L%6#rFv)-BOCV_Q#opbuU(|dsR+Y0Uj+kg^z-KcNoncoV* z`WSaxkQq;bX|NsK4;}zJfP2*a@*cMNsO$T93)>n-$afjb3(YXeB2PKNdz-Pm&hrsCSLN5X)(-){59?_5+XTLR!Za8@JQ84-`c)o!bjLNS)-za`t&vyN& z`I{ZTjn~#<)O^ct?RI@@x94x?+j_M9+RoqX_3e3DyZ_HT&$Rou`JOquID7m!zq#i2 I%=sPv7wQ+4<^TWy literal 0 HcmV?d00001 diff --git a/wai/quantum-conformance/formats/jeff/upstream/qft_3.ref.qasm b/wai/quantum-conformance/formats/jeff/upstream/qft_3.ref.qasm new file mode 100644 index 000000000..bc88a2464 --- /dev/null +++ b/wai/quantum-conformance/formats/jeff/upstream/qft_3.ref.qasm @@ -0,0 +1,14 @@ +OPENQASM 3.0; +include "stdgates.inc"; +// From the benchmark's source: the textbook QFT on three qubits, qubit i measured into bit 2 - i. +qubit[3] q; +bit[3] c; +h q[2]; +cp(pi/2) q[1], q[2]; +cp(pi/4) q[0], q[2]; +h q[1]; +cp(pi/2) q[0], q[1]; +h q[0]; +c[2] = measure q[0]; +c[1] = measure q[1]; +c[0] = measure q[2]; diff --git a/wai/quantum-conformance/formats/jeff/upstream/qpe_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/qpe_3.jeff new file mode 100644 index 0000000000000000000000000000000000000000..e04782527591d525fa7e70f41048e00ad86f0ee4 GIT binary patch literal 6248 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, corrected on what was measured: written to QIR under +// the adaptive profile, every shot reads 1 on c[2]. jeff writes no branch. +OPENQASM 3.0; +include "stdgates.inc"; +qubit[3] q; +bit[3] c; +x q[0]; +h q[1]; +cx q[1], q[2]; +cx q[0], q[1]; +h q[0]; +c[0] = measure q[0]; +c[1] = measure q[1]; +if (c[1]) { + x q[2]; +} +if (c[0] == 1) { + z q[2]; +} +c[2] = measure q[2]; diff --git a/wai/quantum-conformance/formats/qasm/timing.qasm b/wai/quantum-conformance/formats/qasm/timing.qasm new file mode 100644 index 000000000..0e8730725 --- /dev/null +++ b/wai/quantum-conformance/formats/qasm/timing.qasm @@ -0,0 +1,11 @@ +// A barrier and a delay: neither format has a place for them, so they are +// left out, and the conversion says so. +OPENQASM 3.0; +include "stdgates.inc"; +qubit[2] q; +bit[2] c; +h q[0]; +barrier q; +delay[20ns] q[1]; +cx q[0], q[1]; +c = measure q; diff --git a/wai/quantum-conformance/formats/qir/bell.ref.qasm b/wai/quantum-conformance/formats/qir/bell.ref.qasm new file mode 100644 index 000000000..0a46d8280 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/bell.ref.qasm @@ -0,0 +1,7 @@ +OPENQASM 3.0; +include "stdgates.inc"; +qubit[2] q; +bit[2] c; +h q[0]; +cx q[0], q[1]; +c = measure q; diff --git a/wai/quantum-conformance/formats/qir/bell_base.ll b/wai/quantum-conformance/formats/qir/bell_base.ll new file mode 100644 index 000000000..e7c842090 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/bell_base.ll @@ -0,0 +1,52 @@ +; The base profile's own example program (the QIR specification, MIT): two +; qubits entangled, measured, and a tuple of the two results recorded. + +@0 = internal constant [3 x i8] c"r1\00" +@1 = internal constant [3 x i8] c"r2\00" +@2 = internal constant [3 x i8] c"t0\00" + +define i64 @Entry_Point_Name() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body + +body: ; preds = %entry + call void @__quantum__qis__h__body(ptr null) + call void @__quantum__qis__cnot__body(ptr null, ptr inttoptr (i64 1 to ptr)) + br label %measurements + +measurements: ; preds = %body + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) + call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr)) + br label %output + +output: ; preds = %measurements + call void @__quantum__rt__tuple_record_output(i64 2, ptr @2) + call void @__quantum__rt__result_record_output(ptr null, ptr @0) + call void @__quantum__rt__result_record_output(ptr inttoptr (i64 1 to ptr), ptr @1) + + ret i64 0 +} + +declare void @__quantum__qis__h__body(ptr) + +declare void @__quantum__qis__cnot__body(ptr, ptr) + +declare void @__quantum__qis__mz__body(ptr, ptr writeonly) #1 + +declare void @__quantum__rt__initialize(ptr) + +declare void @__quantum__rt__tuple_record_output(i64, ptr) + +declare void @__quantum__rt__result_record_output(ptr, ptr) + +attributes #0 = { "entry_point" "qir_profiles"="base_profile" "output_labeling_schema"="schema_id" "required_num_qubits"="2" "required_num_results"="2" } + +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} + +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 0} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/clifford_t.ll b/wai/quantum-conformance/formats/qir/clifford_t.ll new file mode 100644 index 000000000..4324e73c3 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/clifford_t.ll @@ -0,0 +1,30 @@ +; A Clifford+T module, one rotation by exactly pi/4 in hex: it reads with no note and runs on the bit-exact method. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body +body: + call void @__quantum__qis__h__body(ptr null) + call void @__quantum__qis__t__body(ptr null) + call void @__quantum__qis__cx__body(ptr null, ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__s__adj(ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, ptr inttoptr (i64 1 to ptr)) + br label %measurements +measurements: + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1 + call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr)) #1 + br label %output +output: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="base_profile" "output_labeling_schema"="labels" "required_num_qubits"="2" "required_num_results"="2" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/clifford_t.ref.qasm b/wai/quantum-conformance/formats/qir/clifford_t.ref.qasm new file mode 100644 index 000000000..a7cf7acc6 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/clifford_t.ref.qasm @@ -0,0 +1,11 @@ +OPENQASM 3.0; +include "stdgates.inc"; +qubit[2] q; +bit[2] c; +h q[0]; +t q[0]; +cx q[0], q[1]; +sdg q[1]; +h q[1]; +rz(pi/4) q[1]; +c = measure q; diff --git a/wai/quantum-conformance/formats/qir/computed.ll b/wai/quantum-conformance/formats/qir/computed.ll new file mode 100644 index 000000000..31105a747 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/computed.ll @@ -0,0 +1,35 @@ +; Adaptive: an angle computed from constants under float_computations, and a branch on two results combined by i1 logic: result 2 is 1 exactly when results 0 and 1 agree. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + %a = fadd double 0.25, 0.5 + %b = fmul double %a, 2.0 + call void @__quantum__qis__ry__body(double %b, ptr null) + call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) + call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr)) + %r0 = call i1 @__quantum__rt__read_result(ptr readonly null) + %r1 = call i1 @__quantum__rt__read_result(ptr readonly inttoptr (i64 1 to ptr)) + %x = xor i1 %r0, %r1 + %n = xor i1 %x, true + br i1 %n, label %same, label %out +same: + call void @__quantum__qis__x__body(ptr inttoptr (i64 2 to ptr)) + br label %out +out: + call void @__quantum__qis__mz__body(ptr inttoptr (i64 2 to ptr), ptr writeonly inttoptr (i64 2 to ptr)) + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "output_labeling_schema"="labels" "required_num_qubits"="3" "required_num_results"="3" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3, !4, !5} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} +!4 = !{i32 5, !"float_computations", !10} +!5 = !{i32 1, !"backwards_branching", i2 0} +!10 = !{!"double"} diff --git a/wai/quantum-conformance/formats/qir/every_intrinsic.ll b/wai/quantum-conformance/formats/qir/every_intrinsic.ll new file mode 100644 index 000000000..2f979b413 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/every_intrinsic.ll @@ -0,0 +1,51 @@ +; Every intrinsic the reader takes, once each, at angles that are not dyadic where an intrinsic takes one (decimal, exponent and hex), then every qubit measured. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body +body: + call void @__quantum__qis__h__body(ptr null) + call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__h__body(ptr inttoptr (i64 2 to ptr)) + call void @__quantum__qis__x__body(ptr null) + call void @__quantum__qis__y__body(ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__z__body(ptr inttoptr (i64 2 to ptr)) + call void @__quantum__qis__s__body(ptr null) + call void @__quantum__qis__s__adj(ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__t__body(ptr inttoptr (i64 2 to ptr)) + call void @__quantum__qis__t__adj(ptr null) + call void @__quantum__qis__sx__body(ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__rx__body(double 3.000000e-01, ptr null) + call void @__quantum__qis__ry__body(double 0xBFF199999999999A, ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__rz__body(double 2.5, ptr inttoptr (i64 2 to ptr)) + call void @__quantum__qis__rz__adj(double 0.7, ptr null) + call void @__quantum__qis__r1__body(double 0.9, ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__cnot__body(ptr null, ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__cx__body(ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr)) + call void @__quantum__qis__cy__body(ptr inttoptr (i64 2 to ptr), ptr null) + call void @__quantum__qis__cz__body(ptr null, ptr inttoptr (i64 2 to ptr)) + call void @__quantum__qis__swap__body(ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr)) + call void @__quantum__qis__ccx__body(ptr null, ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr)) + call void @__quantum__qis__ccz__body(ptr inttoptr (i64 2 to ptr), ptr inttoptr (i64 1 to ptr), ptr null) + call void @__quantum__qis__rxx__body(double 0.4, ptr null, ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__ryy__body(double -0.6, ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr)) + call void @__quantum__qis__rzz__body(double 1.3, ptr inttoptr (i64 2 to ptr), ptr null) + br label %measurements +measurements: + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1 + call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr)) #1 + call void @__quantum__qis__mz__body(ptr inttoptr (i64 2 to ptr), ptr writeonly inttoptr (i64 2 to ptr)) #1 + br label %output +output: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="base_profile" "output_labeling_schema"="labels" "required_num_qubits"="3" "required_num_results"="3" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/every_intrinsic.ref.qasm b/wai/quantum-conformance/formats/qir/every_intrinsic.ref.qasm new file mode 100644 index 000000000..b21a91cba --- /dev/null +++ b/wai/quantum-conformance/formats/qir/every_intrinsic.ref.qasm @@ -0,0 +1,10 @@ +OPENQASM 3.0; +include "stdgates.inc"; +qubit[3] q; +bit[3] c; +h q[0]; h q[1]; h q[2]; +x q[0]; y q[1]; z q[2]; s q[0]; sdg q[1]; t q[2]; tdg q[0]; sx q[1]; +rx(0.3) q[0]; ry(-1.1) q[1]; rz(2.5) q[2]; rz(-0.7) q[0]; p(0.9) q[1]; +cx q[0], q[1]; cx q[1], q[2]; cy q[2], q[0]; cz q[0], q[2]; swap q[1], q[2]; ccx q[0], q[1], q[2]; ctrl(2) @ z q[2], q[1], q[0]; +rxx(0.4) q[0], q[1]; ryy(-0.6) q[1], q[2]; rzz(1.3) q[2], q[0]; +c = measure q; diff --git a/wai/quantum-conformance/formats/qir/no_profile.ll b/wai/quantum-conformance/formats/qir/no_profile.ll new file mode 100644 index 000000000..865767611 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/no_profile.ll @@ -0,0 +1,24 @@ +; A module that names no profile: read under the profile its instructions need (base), and said so. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body +body: + call void @__quantum__qis__h__body(ptr null) + br label %measurements +measurements: + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1 + br label %output +output: + ret i64 0 +} + +attributes #0 = { "entry_point" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="1" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/qft3_typed.ll b/wai/quantum-conformance/formats/qir/qft3_typed.ll new file mode 100644 index 000000000..350c8340e --- /dev/null +++ b/wai/quantum-conformance/formats/qir/qft3_typed.ll @@ -0,0 +1,51 @@ +; QIR emitted by wai-quantum — deterministic, byte-identical on every target. +source_filename = "wai_quantum" + +%Qubit = type opaque +%Result = type opaque + +declare void @__quantum__qis__h__body(%Qubit*) +declare void @__quantum__qis__rz__body(double, %Qubit*) +declare void @__quantum__qis__cnot__body(%Qubit*, %Qubit*) +declare void @__quantum__qis__mz__body(%Qubit*, %Result*) +declare void @__quantum__rt__result_record_output(%Result*, i8*) + +define void @main() #0 { +entry: + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0xBFE921FB54442D18, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0x3FD921FB54442D18, %Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0xBFD921FB54442D18, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0x3FD921FB54442D18, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, %Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0xBFE921FB54442D18, %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 2 to %Result*)) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 2 to %Result*), i8* null) + ret void +} + +attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="3" "required_num_results"="3" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 1} +!1 = !{i32 7, !"qir_minor_version", i32 0} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/qft3_typed.ref.qasm b/wai/quantum-conformance/formats/qir/qft3_typed.ref.qasm new file mode 100644 index 000000000..fbb19a005 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/qft3_typed.ref.qasm @@ -0,0 +1,12 @@ +OPENQASM 3.0; +include "stdgates.inc"; +qubit[3] q; +h q[0]; +cp(2*pi/4) q[1],q[0]; +cp(2*pi/8) q[2],q[0]; +h q[1]; +cp(2*pi/4) q[2],q[1]; +h q[2]; +cx q[0],q[2]; +cx q[2],q[0]; +cx q[0],q[2]; diff --git a/wai/quantum-conformance/formats/qir/refuse/base_branch.ll b/wai/quantum-conformance/formats/qir/refuse/base_branch.ll new file mode 100644 index 000000000..6ba97f8dc --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/base_branch.ll @@ -0,0 +1,25 @@ +; Under the base profile, reading a result: forbidden. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body +body: + call void @__quantum__qis__h__body(ptr null) + br label %measurements +measurements: + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1 + %r = call i1 @__quantum__rt__read_result(ptr readonly null) + br label %output +output: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="base_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="1" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/refuse/base_gate_after_measure.ll b/wai/quantum-conformance/formats/qir/refuse/base_gate_after_measure.ll new file mode 100644 index 000000000..6b20fc7ba --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/base_gate_after_measure.ll @@ -0,0 +1,24 @@ +; Under the base profile, a gate on a qubit after its measurement: forbidden. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body +body: + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1 + br label %measurements +measurements: + call void @__quantum__qis__x__body(ptr null) + br label %output +output: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="base_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="1" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/refuse/dynamic_qubits.ll b/wai/quantum-conformance/formats/qir/refuse/dynamic_qubits.ll new file mode 100644 index 000000000..baa4b1698 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/dynamic_qubits.ll @@ -0,0 +1,18 @@ +; A qubit allocated at run time, with dynamic qubit management declared: a capability this reader does not take. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + %q = call ptr @__quantum__rt__qubit_allocate() + call void @__quantum__qis__h__body(ptr %q) + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="0" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 true} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/refuse/loop_declared.ll b/wai/quantum-conformance/formats/qir/refuse/loop_declared.ll new file mode 100644 index 000000000..08c2627bf --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/loop_declared.ll @@ -0,0 +1,23 @@ +; The same loop with backwards_branching declared: a capability this reader does not take. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %l +l: + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) + %r = call i1 @__quantum__rt__read_result(ptr readonly null) + br i1 %r, label %l, label %done +done: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="1" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3, !4} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} +!4 = !{i32 1, !"backwards_branching", i2 2} diff --git a/wai/quantum-conformance/formats/qir/refuse/loop_undeclared.ll b/wai/quantum-conformance/formats/qir/refuse/loop_undeclared.ll new file mode 100644 index 000000000..e15e48d18 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/loop_undeclared.ll @@ -0,0 +1,22 @@ +; A loop (a branch back to an earlier block) with no backwards_branching flag: forbidden. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %l +l: + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) + %r = call i1 @__quantum__rt__read_result(ptr readonly null) + br i1 %r, label %l, label %done +done: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="1" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/refuse/major_version.ll b/wai/quantum-conformance/formats/qir/refuse/major_version.ll new file mode 100644 index 000000000..df3a0d118 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/major_version.ll @@ -0,0 +1,24 @@ +; A QIR major version the reader does not take. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body +body: + call void @__quantum__qis__h__body(ptr null) + br label %measurements +measurements: + + br label %output +output: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="base_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="0" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 3} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/refuse/not_llvm.ll b/wai/quantum-conformance/formats/qir/refuse/not_llvm.ll new file mode 100644 index 000000000..32e441bca --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/not_llvm.ll @@ -0,0 +1,3 @@ +; Not LLVM text: no function, no entry point. +OPENQASM 3.0; +qubit q; diff --git a/wai/quantum-conformance/formats/qir/refuse/qubit_past_count.ll b/wai/quantum-conformance/formats/qir/refuse/qubit_past_count.ll new file mode 100644 index 000000000..0cb8fe7b0 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/qubit_past_count.ll @@ -0,0 +1,24 @@ +; Qubit 4 in a module that declares one qubit. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body +body: + call void @__quantum__qis__h__body(ptr inttoptr (i64 4 to ptr)) + br label %measurements +measurements: + + br label %output +output: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="base_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="0" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/refuse/stale_read.ll b/wai/quantum-conformance/formats/qir/refuse/stale_read.ll new file mode 100644 index 000000000..1de2fb66e --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/stale_read.ll @@ -0,0 +1,24 @@ +; A result read, measured again into the same result, and only then branched on: the branch would read the later measurement. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) + %r = call i1 @__quantum__rt__read_result(ptr readonly null) + call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly null) + br i1 %r, label %a, label %b +a: + call void @__quantum__qis__x__body(ptr inttoptr (i64 1 to ptr)) + br label %b +b: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "output_labeling_schema"="labels" "required_num_qubits"="2" "required_num_results"="1" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/refuse/switch_declared.ll b/wai/quantum-conformance/formats/qir/refuse/switch_declared.ll new file mode 100644 index 000000000..0437f6284 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/switch_declared.ll @@ -0,0 +1,24 @@ +; A switch on a result, multiple-target branching declared: a capability this reader does not take. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) + %r = call i1 @__quantum__rt__read_result(ptr readonly null) + switch i1 %r, label %done [ i1 true, label %one ] +one: + call void @__quantum__qis__x__body(ptr null) + br label %done +done: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="1" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3, !4} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} +!4 = !{i32 1, !"multiple_target_branching", i1 true} diff --git a/wai/quantum-conformance/formats/qir/refuse/undeclared_float.ll b/wai/quantum-conformance/formats/qir/refuse/undeclared_float.ll new file mode 100644 index 000000000..0131a4d63 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/undeclared_float.ll @@ -0,0 +1,32 @@ +; The same computation with no float_computations flag: what the adaptive profile forbids, refused on its line. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + %a = fadd double 0.25, 0.5 + %b = fmul double %a, 2.0 + call void @__quantum__qis__ry__body(double %b, ptr null) + call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) + call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr)) + %r0 = call i1 @__quantum__rt__read_result(ptr readonly null) + %r1 = call i1 @__quantum__rt__read_result(ptr readonly inttoptr (i64 1 to ptr)) + %x = xor i1 %r0, %r1 + %n = xor i1 %x, true + br i1 %n, label %same, label %out +same: + call void @__quantum__qis__x__body(ptr inttoptr (i64 2 to ptr)) + br label %out +out: + call void @__quantum__qis__mz__body(ptr inttoptr (i64 2 to ptr), ptr writeonly inttoptr (i64 2 to ptr)) + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "output_labeling_schema"="labels" "required_num_qubits"="3" "required_num_results"="3" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/refuse/unknown_intrinsic.ll b/wai/quantum-conformance/formats/qir/refuse/unknown_intrinsic.ll new file mode 100644 index 000000000..db7becd4b --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/unknown_intrinsic.ll @@ -0,0 +1,24 @@ +; An intrinsic the reader does not know. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body +body: + call void @__quantum__qis__foo__body(ptr null) + br label %measurements +measurements: + + br label %output +output: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="base_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="0" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/refuse/unknown_profile.ll b/wai/quantum-conformance/formats/qir/refuse/unknown_profile.ll new file mode 100644 index 000000000..66eb7df8b --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/unknown_profile.ll @@ -0,0 +1,24 @@ +; A profile the reader does not take. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body +body: + call void @__quantum__qis__h__body(ptr null) + br label %measurements +measurements: + + br label %output +output: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="custom_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="0" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/teleport_chain.ll b/wai/quantum-conformance/formats/qir/teleport_chain.ll new file mode 100644 index 000000000..c28fdf49a --- /dev/null +++ b/wai/quantum-conformance/formats/qir/teleport_chain.ll @@ -0,0 +1,121 @@ +; The adaptive profile's own example program (the QIR specification, MIT): a +; Bell pair whose second half is teleported twice, each step measuring in +; the middle and correcting on the result. The pair ends between qubits 0 and +; 5, so results 4 and 5 are equal on every shot. + +@0 = internal constant [5 x i8] c"0_t0\00" +@1 = internal constant [5 x i8] c"0_t1\00" + +define i64 @TeleportChain() local_unnamed_addr #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body + +body: ; preds = %entry + tail call void @__quantum__qis__h__body(ptr null) + tail call void @__quantum__qis__cnot__body(ptr null, ptr nonnull inttoptr (i64 1 to ptr)) + tail call void @__quantum__qis__h__body(ptr nonnull inttoptr (i64 2 to ptr)) + tail call void @__quantum__qis__cnot__body(ptr nonnull inttoptr (i64 2 to ptr), ptr nonnull inttoptr (i64 4 to ptr)) + tail call void @__quantum__qis__h__body(ptr nonnull inttoptr (i64 3 to ptr)) + tail call void @__quantum__qis__cnot__body(ptr nonnull inttoptr (i64 3 to ptr), ptr nonnull inttoptr (i64 5 to ptr)) + tail call void @__quantum__qis__cnot__body(ptr nonnull inttoptr (i64 1 to ptr), ptr nonnull inttoptr (i64 2 to ptr)) + tail call void @__quantum__qis__h__body(ptr nonnull inttoptr (i64 1 to ptr)) + tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 1 to ptr), ptr writeonly null) + tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 1 to ptr)) + %0 = tail call i1 @__quantum__rt__read_result(ptr readonly null) + br i1 %0, label %then__1, label %continue__1 + +; conditional quantum gate (only one in this block, but many can appear and the full quantum instruction set should be usable) +then__1: ; preds = %body + tail call void @__quantum__qis__z__body(ptr nonnull inttoptr (i64 4 to ptr)) + br label %continue__1 + +continue__1: ; preds = %then__1, %body + tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 2 to ptr), ptr writeonly nonnull inttoptr (i64 1 to ptr)) + tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 2 to ptr)) + %1 = tail call i1 @__quantum__rt__read_result(ptr readonly nonnull inttoptr (i64 1 to ptr)) + br i1 %1, label %then__2, label %continue__2 + +then__2: ; preds = %continue__1 + tail call void @__quantum__qis__x__body(ptr nonnull inttoptr (i64 4 to ptr)) + br label %continue__2 + +continue__2: ; preds = %then__2, %continue__1 + tail call void @__quantum__qis__cnot__body(ptr nonnull inttoptr (i64 4 to ptr), ptr nonnull inttoptr (i64 3 to ptr)) + tail call void @__quantum__qis__h__body(ptr nonnull inttoptr (i64 4 to ptr)) + tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 4 to ptr), ptr writeonly nonnull inttoptr (i64 2 to ptr)) + tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 4 to ptr)) + %2 = tail call i1 @__quantum__rt__read_result(ptr readonly nonnull inttoptr (i64 2 to ptr)) + br i1 %2, label %then__3, label %continue__3 + +then__3: ; preds = %continue__2 + tail call void @__quantum__qis__z__body(ptr nonnull inttoptr (i64 5 to ptr)) + br label %continue__3 + +continue__3: ; preds = %then__3, %continue__2 + tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 3 to ptr), ptr writeonly nonnull inttoptr (i64 3 to ptr)) + tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 3 to ptr)) + %3 = tail call i1 @__quantum__rt__read_result(ptr readonly nonnull inttoptr (i64 3 to ptr)) + br i1 %3, label %then__4, label %continue__4 + +then__4: ; preds = %continue__3 + tail call void @__quantum__qis__x__body(ptr nonnull inttoptr (i64 5 to ptr)) + br label %continue__4 + +continue__4: ; preds = %continue__3, %then__4 + tail call void @__quantum__qis__mz__body(ptr null, ptr writeonly nonnull inttoptr (i64 4 to ptr)) + tail call void @__quantum__qis__reset__body(ptr null) + tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 5 to ptr), ptr writeonly nonnull inttoptr (i64 5 to ptr)) + tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 5 to ptr)) + br label %exit + +exit: + call void @__quantum__rt__result_record_output(ptr nonnull inttoptr (i64 4 to ptr), ptr @0) + call void @__quantum__rt__result_record_output(ptr nonnull inttoptr (i64 5 to ptr), ptr @1) + ret i64 0 +} + +; declarations of QIS functions + +declare void @__quantum__qis__cnot__body(ptr, ptr) local_unnamed_addr + +declare void @__quantum__qis__h__body(ptr) local_unnamed_addr + +declare void @__quantum__qis__x__body(ptr) local_unnamed_addr + +declare void @__quantum__qis__z__body(ptr) local_unnamed_addr + +declare void @__quantum__qis__reset__body(ptr) local_unnamed_addr + +declare void @__quantum__qis__mz__body(ptr, ptr writeonly) #1 + +; declarations of runtime functions + +declare void @__quantum__rt__initialize(ptr) + +declare i1 @__quantum__rt__read_result(ptr readonly) + +declare void @__quantum__rt__result_record_output(ptr, ptr) + +; attributes + +attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "output_labeling_schema"="schema_id" "required_num_qubits"="6" "required_num_results"="6" } + +attributes #1 = { "irreversible" } + +; module flags + +!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9} + +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 0} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} +!4 = !{i32 5, !"int_computations", !10} +!5 = !{i32 5, !"float_computations", !11} +!6 = !{i32 1, !"ir_functions", i1 false} +!7 = !{i32 1, !"backwards_branching", i2 0} +!8 = !{i32 1, !"multiple_target_branching", i1 false} +!9 = !{i32 1, !"multiple_return_points", i1 false} +!10 = !{!"i32", !"i64"} +!11 = !{!"float", !"double"} diff --git a/wai/wai-quantum/Cargo.toml b/wai/wai-quantum/Cargo.toml index af2da66c2..e1571b114 100644 --- a/wai/wai-quantum/Cargo.toml +++ b/wai/wai-quantum/Cargo.toml @@ -12,9 +12,10 @@ keywords = ["quantum", "simulation", "wasm", "deterministic", "qec"] categories = ["science", "simulation", "wasm", "mathematics"] readme = "README.md" # Hold files outside this package (quantum-conformance/fabric/expected.json, -# quantum-conformance/qasm3/, quantum-conformance/qec-text/ and the answer -# layer's conformance set) to the crate; they run in the repository only. -exclude = ["tests/fabric_expected.rs", "tests/qasm3_conformance.rs", "tests/qec_text_conformance.rs", "tests/answer_conformance.rs"] +# quantum-conformance/qasm3/, quantum-conformance/qec-text/, the answer +# layer's conformance set and quantum-conformance/formats/) to the crate; +# they run in the repository only. +exclude = ["tests/fabric_expected.rs", "tests/qasm3_conformance.rs", "tests/qec_text_conformance.rs", "tests/answer_conformance.rs", "tests/formats_conformance.rs"] # docs.rs builds with DEFAULT features only, which would document 3 of 21 # modules and hide the entire operational + ML layers. Document everything. diff --git a/wai/wai-quantum/src/engine/formats/jeff.rs b/wai/wai-quantum/src/engine/formats/jeff.rs index a2453396b..e41a924fd 100644 --- a/wai/wai-quantum/src/engine/formats/jeff.rs +++ b/wai/wai-quantum/src/engine/formats/jeff.rs @@ -493,7 +493,13 @@ impl<'m> Runner<'m> { Ins::Qureg(d) => self.qureg_op(*d, ins, &at)?, Ins::Int(d, c) => vec![self.int_op(fr, o, *d, *c, &ins, &at)?], Ins::IntArray(d, consts, w) => vec![self.int_array_op(fr, o, *d, consts, *w, ins, &at)?], - Ins::Float(d, c) => vec![self.float_op(*d, *c, &ins, &at)?], + Ins::Float(d, c) => { + let w = match o.outputs.first().and_then(|&v| fr.values.get(v as usize)) { + Some(Ty::Int(w)) => Some(*w), + _ => None, + }; + vec![self.float_op(*d, *c, &ins, w, &at)?] + } Ins::FloatArray(d, consts) => vec![self.float_array_op(*d, consts, ins, &at)?], Ins::Switch(branches, default) => { let mut args: Vec = o.inputs.iter().map(|&v| self.take(fr, v, &at)).collect::>()?; @@ -857,6 +863,18 @@ impl<'m> Runner<'m> { } fn int_op(&mut self, fr: &Frame, o: &Op, d: u16, c: u64, ins: &[V], at: &str) -> Result { + // An integer converted to a float: signed or unsigned, at its width. + if d == 33 || d == 34 { + return match ins.first() { + Some(V::Int(w, IntV::Known(v))) => { + let v = *v & mask(*w); + let s = if *w == 0 || *w >= 64 { v as i64 } else { ((v << (64 - w)) as i64) >> (64 - w) }; + Ok(V::Float(if d == 33 { s as f64 } else { v as f64 })) + } + Some(V::Int(_, IntV::Cond(_))) => Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a float computed from a measured bit"))), + _ => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: an integer operand is expected"))), + }; + } let w = match o.outputs.first().and_then(|&v| fr.values.get(v as usize)) { Some(Ty::Int(w)) => *w, _ => return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: an integer operation whose output is not an integer"))), @@ -971,7 +989,6 @@ impl<'m> Runner<'m> { } 30 => sx(k(0)?, in_w) as u64, 31 | 32 => k(0)? & mask(in_w), - 33 | 34 => return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: an integer converted to a float has a float output"))), k => return Err(unread(format!("{at}: an integer operation of kind {k}, which the schema does not have"))), }; Ok(V::Int(w, IntV::Known(v & mask(w)))) @@ -1024,7 +1041,7 @@ impl<'m> Runner<'m> { }) } - fn float_op(&mut self, d: u16, c: f64, ins: &[V], at: &str) -> Result { + fn float_op(&mut self, d: u16, c: f64, ins: &[V], int_out: Option, at: &str) -> Result { use crate::qasm::value as m; let f = |k: usize| -> Result { match ins.get(k) { @@ -1091,7 +1108,16 @@ impl<'m> Runner<'m> { }, 34 => V::Float(f(0)?), 35 => V::Float(f64::from(f(0)? as f32)), - 36 | 37 => return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a float converted to an integer has an integer output"))), + // A float converted to an integer of the output's width, toward zero. + 36 | 37 => { + let w = int_out.ok_or_else(|| Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a float converted to an integer has an integer output")))?; + let x = f(0)?; + if !x.is_finite() { + return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: {x} converted to an integer"))); + } + let v = if d == 36 { x as i64 as u64 } else { x as u64 }; + V::Int(w, IntV::Known(v & mask(w))) + } k => return Err(unread(format!("{at}: a float operation of kind {k}, which the schema does not have"))), }) } diff --git a/wai/wai-quantum/src/engine/formats/qir.rs b/wai/wai-quantum/src/engine/formats/qir.rs index 7e550eef9..d993eb4cb 100644 --- a/wai/wai-quantum/src/engine/formats/qir.rs +++ b/wai/wai-quantum/src/engine/formats/qir.rs @@ -681,7 +681,11 @@ impl<'m, 'a> Reader<'m, 'a> { return Err(err(diag::INSTRUCTION_UNKNOWN, line, format!("`{op}`: an instruction the adaptive profile does not list"))); } let (declared, kind) = if float_op { (&self.allowed.floats, "floats") } else { (&self.allowed.ints, "integers") }; - let ty_name = ty.trim_end_matches(','); + // A conversion is computation on the type it gives. + let ty_name = match rest.rsplit_once(" to ") { + Some((_, to)) if matches!(op, "zext" | "sext" | "trunc" | "fpext" | "fptrunc" | "sitofp" | "uitofp" | "fptosi" | "fptoui") => to.trim(), + _ => ty.trim_end_matches(','), + }; if !declared.iter().any(|d| d == ty_name) { return Err(self.forbidden(line, &format!("computation on {kind} of type {ty_name} (its module flags do not declare it)"))); } diff --git a/wai/wai-quantum/src/engine/formats/tests.rs b/wai/wai-quantum/src/engine/formats/tests.rs index 01256027d..eed1dcec9 100644 --- a/wai/wai-quantum/src/engine/formats/tests.rs +++ b/wai/wai-quantum/src/engine/formats/tests.rs @@ -505,8 +505,10 @@ impl Hand { /// says: `x` with target q0 and control q1 is `cx q[1], q[0]`; `adjoint` is /// the inverse and `power` repeats it; `r1` is the phase gate and `u` is /// OpenQASM's `U`. -#[test] -fn well_known_gates_read_with_the_schemas_operand_order() { +const WELL_KNOWN_REF: &str = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nh q[0];\ncx q[0], q[1];\nrx(-0.9) q[2];\nsdg q[0];\nU(0.2, 0.4, -0.6) q[1];\nctrl(2) @ p(0.9) q[0], q[1], q[2];\np(0.25) q[0];\n"; + +/// The module of `well_known_gates_read_with_the_schemas_operand_order`. +fn well_known_module() -> Vec { let mut h = Hand::new(); let (a, b, c) = (h.alloc(), h.alloc(), h.alloc()); let a = h.gate(0, 9, vec![], &[a], &[], &[], false, 0, (0, 0))[0]; // h q0 @@ -521,9 +523,13 @@ fn well_known_gates_read_with_the_schemas_operand_order() { let o = h.gate(0, 5, vec![], &[c], &[s, u], &[ph], false, 0, (0, 0)); // p(0.9) on q2 under q0, q1 let g = h.float(0.25); h.gate(0, 13, vec![], &[], &[o[1]], &[g], false, 0, (0, 0)); // gphase under q0: a phase on q0 - let _ = o; - let r = read(Format::Jeff, &h.bytes()).unwrap(); - let want = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nh q[0];\ncx q[0], q[1];\nrx(-0.9) q[2];\nsdg q[0];\nU(0.2, 0.4, -0.6) q[1];\nctrl(2) @ p(0.9) q[0], q[1], q[2];\np(0.25) q[0];\n"; + h.bytes() +} + +#[test] +fn well_known_gates_read_with_the_schemas_operand_order() { + let r = read(Format::Jeff, &well_known_module()).unwrap(); + let want = WELL_KNOWN_REF; assert_eq!(verdict(&r.qasm, want), ("equivalent".into(), "exact".into()), "{}", r.qasm); assert_eq!(r.summary["qubits"], json!(3)); } @@ -587,8 +593,7 @@ fn a_pauli_product_rotation_is_exp_i_theta_p() { /// Custom gates read only when they name a gate of the standard library with /// its qubits and parameters, and say so; the rest are refused. -#[test] -fn custom_gates_are_read_by_a_standard_name_or_refused() { +fn custom_module() -> Vec { let mut h = Hand::new(); h.strings.extend(["H".to_string(), "CX".into(), "Rx".into(), "Hadamard".into()]); let (a, b) = (h.alloc(), h.alloc()); @@ -596,16 +601,65 @@ fn custom_gates_are_read_by_a_standard_name_or_refused() { let o = h.gate(1, 2, vec![], &[a, b], &[], &[], false, 0, (2, 0)); let t = h.float(0.5); h.gate(1, 3, vec![], &[o[1]], &[], &[t], false, 0, (1, 1)); - let r = read(Format::Jeff, &h.bytes()).unwrap(); + h.bytes() +} + +/// A custom gate the library does not have (`name` 4, `Hadamard`), or one of +/// its names with the wrong qubits (`CX` on one). +fn custom_refused_module(name: u16, qubits: u8) -> Vec { + let mut h2 = Hand::new(); + h2.strings.extend(["H".to_string(), "CX".into(), "Rx".into(), "Hadamard".into()]); + let x = h2.alloc(); + h2.gate(1, name, vec![], &[x], &[], &[], false, 0, (qubits, 0)); + h2.bytes() +} + +fn qubit_twice_module() -> Vec { + let mut h = Hand::new(); + let a = h.alloc(); + h.gate(0, 9, vec![], &[a], &[], &[], false, 0, (0, 0)); + h.gate(0, 9, vec![], &[a], &[], &[], false, 0, (0, 0)); + h.bytes() +} + +fn old_version_module() -> Vec { + let mut h = Hand::new(); + h.alloc(); + h.version = (0, 1); + h.bytes() +} + +/// An angle computed from a measured bit: not known on reading. +fn angle_from_measurement_module() -> Vec { + let mut h = Hand::new(); + let (a, b) = (h.alloc(), h.alloc()); + let a = h.gate(0, 9, vec![], &[a], &[], &[], false, 0, (0, 0))[0]; + let r = h.measure(a); + let f = h.value(FLOAT, 0); + let mut conv = Out::new(2, 0); + conv.set_u16(1, 34); + h.op(vec![r], vec![f], 2, conv); + h.gate(0, 6, vec![], &[b], &[], &[f], false, 0, (0, 0)); + h.bytes() +} + +/// A Pauli-product rotation on three qubits, `exp(0.37i X⊗Y⊗Z)`. +fn ppr_module() -> Vec { + let mut h = Hand::new(); + let qs: Vec = (0..3).map(|_| h.alloc()).collect(); + let t = h.float(0.37); + h.gate(2, 0, vec![1, 2, 3], &qs, &[], &[t], false, 0, (0, 0)); + h.bytes() +} + +#[test] +fn custom_gates_are_read_by_a_standard_name_or_refused() { + let r = read(Format::Jeff, &custom_module()).unwrap(); assert_equivalent(&r.qasm, "OPENQASM 3.0; include \"stdgates.inc\"; qubit[2] q; h q[0]; cx q[0], q[1]; rx(0.5) q[1];"); assert_eq!(r.notes.iter().filter(|d| d.code == diag::JEFF_CUSTOM_NAMED).count(), 3, "{:?}", r.notes); // A name the library does not have, and a library name with the wrong qubits. for (name, qubits) in [(4u16, 1u8), (2, 1)] { - let mut h2 = Hand::new(); - h2.strings.extend(["H".to_string(), "CX".into(), "Rx".into(), "Hadamard".into()]); - let x = h2.alloc(); - h2.gate(1, name, vec![], &[x], &[], &[], false, 0, (qubits, 0)); - let d = read(Format::Jeff, &h2.bytes()).unwrap_err(); + let d = read(Format::Jeff, &custom_refused_module(name, qubits)).unwrap_err(); assert_eq!(code(&d), diag::JEFF_CUSTOM, "{d}"); assert!(d.message.contains("operation 1"), "where it stands: {d}"); } @@ -632,18 +686,13 @@ fn returned_bits_and_a_switch_on_a_measurement() { #[test] fn jeff_refusals_name_their_code_and_place() { // A qubit used twice. - let mut h = Hand::new(); - let a = h.alloc(); - h.gate(0, 9, vec![], &[a], &[], &[], false, 0, (0, 0)); - h.gate(0, 9, vec![], &[a], &[], &[], false, 0, (0, 0)); - let d = read(Format::Jeff, &h.bytes()).unwrap_err(); + let d = read(Format::Jeff, &qubit_twice_module()).unwrap_err(); assert_eq!(code(&d), diag::JEFF_LINEAR, "{d}"); assert!(d.message.contains("operation 2") && d.message.contains("`main`"), "{d}"); // A version the reader does not take. - let mut h = Hand::new(); - h.alloc(); - h.version = (0, 1); - assert_eq!(code(&read(Format::Jeff, &h.bytes()).unwrap_err()), diag::FORMAT_VERSION); + assert_eq!(code(&read(Format::Jeff, &old_version_module()).unwrap_err()), diag::FORMAT_VERSION); + // An angle from a measurement. + assert_eq!(code(&read(Format::Jeff, &angle_from_measurement_module()).unwrap_err()), diag::JEFF_NOT_KNOWN); // Not a message; every prefix of a module refused, never read past its end. assert_eq!(code(&read(Format::Jeff, b"\x00\x00").unwrap_err()), diag::FORMAT_UNREAD); let p = qasm::read("OPENQASM 3.0; include \"stdgates.inc\"; qubit[2] q; bit[2] c; h q[0]; cx q[0], q[1]; rz(0.2) q[1]; c = measure q;").unwrap().program; @@ -713,3 +762,40 @@ fn formats_are_named_and_found() { let p = qasm::read("OPENQASM 3.0; qubit[1] q; h q[0];").unwrap().program; assert_eq!(Format::sniff(&write(Format::Jeff, &p, None).unwrap().bytes), Format::Jeff); } + +/// The conformance set's modules made here (`wai/quantum-conformance/formats/`): +/// the hand-built jeff modules, and a QIR 1 module with typed pointers from +/// the crate's earlier writer, with its reference from the toolchain's own +/// OpenQASM export. With `WAI_FORMATS_VECTORS_WRITE=1` this writes them; +/// otherwise it holds the files to them, so neither can drift. +#[cfg(not(target_os = "wasi"))] +#[test] +fn the_conformance_set_holds_these_modules() { + use crate::quantum_toolchain as qt; + let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../quantum-conformance/formats"); + let qft = qt::build_algorithm("qft", 3, 0).unwrap(); + let files: Vec<(&str, Vec)> = vec![ + ("jeff/controls_after_targets.jeff", well_known_module()), + ("jeff/controls_after_targets.ref.qasm", WELL_KNOWN_REF.as_bytes().to_vec()), + ("jeff/custom_named.jeff", custom_module()), + ("jeff/pauli_product.jeff", ppr_module()), + ("jeff/refuse/custom_unknown.jeff", custom_refused_module(4, 1)), + ("jeff/refuse/custom_wrong_qubits.jeff", custom_refused_module(2, 1)), + ("jeff/refuse/qubit_twice.jeff", qubit_twice_module()), + ("jeff/refuse/old_version.jeff", old_version_module()), + ("jeff/refuse/angle_from_measurement.jeff", angle_from_measurement_module()), + ("jeff/refuse/truncated.jeff", custom_module()[..200].to_vec()), + ("qir/qft3_typed.ll", crate::quantum_qir::to_qir(&qft, true).into_bytes()), + ("qir/qft3_typed.ref.qasm", qt::to_qasm(&qft).into_bytes()), + ]; + let write = std::env::var_os("WAI_FORMATS_VECTORS_WRITE").is_some(); + for (f, bytes) in files { + let p = dir.join(f); + if write { + std::fs::create_dir_all(p.parent().unwrap()).unwrap(); + std::fs::write(&p, &bytes).unwrap(); + } else { + assert_eq!(std::fs::read(&p).unwrap_or_default(), bytes, "{f} differs from what this test makes; write it with WAI_FORMATS_VECTORS_WRITE=1"); + } + } +} diff --git a/wai/wai-quantum/tests/formats_conformance.rs b/wai/wai-quantum/tests/formats_conformance.rs new file mode 100644 index 000000000..eb9d7ec32 --- /dev/null +++ b/wai/wai-quantum/tests/formats_conformance.rs @@ -0,0 +1,211 @@ +//! The conformance set for other formats (`wai/quantum-conformance/formats/`): +//! every QIR and jeff module read, and every OpenQASM source written, held +//! to what its manifest says: the program it reads to (the same circuit by +//! the equivalence ladder), the outcomes it gives, the notes, a round trip +//! back to its own format, the formats it writes to (the bytes of QIR +//! written pinned by their BLAKE3), and each refusal with its code and its +//! place. +//! +//! The cases come from `bundle.json`, which holds each file inline (text, or +//! base64 for jeff), so the same test runs where no file can be read (under +//! wasm32-wasip2, by wasmtime); natively, a second test holds the bundle +//! equal to the files. Run with `WAI_FORMATS_BUNDLE_WRITE=1` to write the +//! bundle from the files. + +#![cfg(feature = "quantum_engine")] + +use serde_json::{Value, json}; +use std::collections::BTreeSet; +use wai_quantum::engine::formats::{self, Format, Profile}; +use wai_quantum::engine::{equiv, job}; +use wai_quantum::qasm; + +const BUNDLE: &str = include_str!("../../quantum-conformance/formats/bundle.json"); + +fn input(case: &Value) -> Result, String> { + match (case["text"].as_str(), case["base64"].as_str()) { + (Some(t), None) => Ok(t.as_bytes().to_vec()), + (None, Some(b)) => formats::decode_base64(b), + _ => Err("a case holds its file as text or base64".into()), + } +} + +fn codes(notes: &[wai_quantum::qasm::Diagnostic]) -> Vec { + let mut v: Vec = notes.iter().map(|d| d.code.to_string()).collect(); + v.sort(); + v.dedup(); + v +} + +fn strings(v: &Value) -> Vec { + v.as_array().map(|a| a.iter().filter_map(Value::as_str).map(String::from).collect()).unwrap_or_default() +} + +fn check(case: &Value) -> Result<(), String> { + let format = Format::parse(case["format"].as_str().ok_or("no format")?).ok_or("an unknown format")?; + let bytes = input(case)?; + let e = &case["expect"]; + let reading = formats::read(format, &bytes); + if let Some(code) = e["refused"].as_str() { + let d = match reading { + Err(d) => d, + Ok(_) => return Err(format!("read, where it must be refused with {code}")), + }; + if d.code != code { + return Err(format!("refused with {}, not {code}: {d}", d.code)); + } + if let Some(l) = e["line"].as_u64() + && d.line() != Some(l as usize) + { + return Err(format!("refused on line {:?}, not {l}: {d}", d.line())); + } + if let Some(at) = e["at"].as_str() + && !d.message.contains(at) + { + return Err(format!("the refusal does not say where ({at}): {d}")); + } + return Ok(()); + } + let r = reading.map_err(|d| format!("does not read: {d}"))?; + let (got, want) = (codes(&r.notes), strings(&e["notes"])); + if got != want { + return Err(format!("notes {got:?}, not {want:?}: {:?}", r.notes.iter().map(ToString::to_string).collect::>())); + } + if let Some(p) = e["profile"].as_str() + && r.summary["profile"] != json!(p) + { + return Err(format!("read under the {} profile, not {p}", r.summary["profile"])); + } + let p = qasm::read(&r.qasm).map_err(|d| format!("the program read does not read: {d}"))?.program; + if let Some(d) = e["dynamic"].as_bool() + && p.dynamic.is_some() != d + { + return Err(format!("dynamic is {}, not {d}", p.dynamic.is_some())); + } + if let Some(n) = e["qubits"].as_u64() + && u64::from(p.n_qubits) != n + { + return Err(format!("{} qubits, not {n}", p.n_qubits)); + } + if let Some(reference) = case["reference"].as_str() { + let v = equiv::equivalent(&r.qasm, reference); + if v["verdict"] != "equivalent" { + return Err(format!("not the reference: {v}\n{}", r.qasm)); + } + if let Some(how) = e["how"].as_str() + && v["how"] != json!(how) + { + return Err(format!("the reference decided by {}, not {how}", v["how"])); + } + } + if e["exact"] == json!(true) { + qasm::exact_circuit(&r.qasm).map_err(|d| format!("the bit-exact method does not take it: {d}"))?; + } + if e.get("outcomes").is_some() || e.get("equal_bits").is_some() { + let (_, res) = job::plan_and_run(&json!({ "qasm": r.qasm, "shots": 512, "seed": 7 }), 1 << 30)?; + let seen: BTreeSet = res["counts"].as_object().ok_or("no counts")?.keys().cloned().collect(); + if let Some(o) = e.get("outcomes") { + let want: BTreeSet = strings(o).into_iter().collect(); + if seen != want { + return Err(format!("outcomes {seen:?}, not {want:?}")); + } + } + for pair in e["equal_bits"].as_array().into_iter().flatten() { + let (a, b) = (pair[0].as_u64().ok_or("a bit")? as usize, pair[1].as_u64().ok_or("a bit")? as usize); + for k in &seen { + let bit = |i: usize| k.as_bytes()[k.len() - 1 - i]; + if bit(a) != bit(b) { + return Err(format!("bits {a} and {b} differ in {k}")); + } + } + } + } + if e["round_trip"] == json!(true) { + let c = formats::convert(format, Format::Qasm, &bytes, None, true).map_err(|d| d.to_string())?; + if c.check["verdict"]["verdict"] != "equivalent" { + return Err(format!("written back to {} and read again, not the same: {}", format.name(), c.check)); + } + } + for w in e["writes"].as_array().into_iter().flatten() { + let to = Format::parse(w["to"].as_str().ok_or("writes: to")?).ok_or("writes: an unknown format")?; + let profile = w["profile_asked"].as_str().map(|s| Profile::parse(s).ok_or("an unknown profile")).transpose()?; + let c = formats::convert(format, to, &bytes, profile, true); + if let Some(code) = w["refused"].as_str() { + match c { + Err(d) if d.code == code => continue, + Err(d) => return Err(format!("writing {}: refused with {}, not {code}: {d}", to.name(), d.code)), + Ok(_) => return Err(format!("writing {}: written, where it must be refused with {code}", to.name())), + } + } + let c = c.map_err(|d| format!("writing {}: {d}", to.name()))?; + if c.check["verdict"]["verdict"] != "equivalent" { + return Err(format!("written to {} and read back, not the same: {}", to.name(), c.check)); + } + if let Some(how) = w["how"].as_str() + && c.check["verdict"]["how"] != json!(how) + { + return Err(format!("written to {}: checked by {}, not {how}", to.name(), c.check["verdict"]["how"])); + } + if let Some(p) = w["profile"].as_str() + && c.written["profile"] != json!(p) + { + return Err(format!("written under the {} profile, not {p}", c.written["profile"])); + } + let got = codes(&c.notes); + let want = strings(&w["notes"]); + if got != want { + return Err(format!("writing {}: notes {got:?}, not {want:?}", to.name())); + } + if let Some(h) = w["blake3"].as_str() { + let got = blake3::hash(&c.output).to_hex().to_string(); + if got != h { + return Err(format!("written to {}: BLAKE3 {got}, not {h}", to.name())); + } + } + } + Ok(()) +} + +#[test] +fn every_module_reads_and_writes_as_its_manifest_says() { + let bundle: Value = serde_json::from_str(BUNDLE).expect("bundle.json reads"); + let cases = bundle["cases"].as_array().expect("cases"); + assert!(cases.len() >= 40, "{} cases", cases.len()); + let failures: Vec = cases.iter().filter_map(|c| check(c).err().map(|e| format!("{}: {e}", c["file"].as_str().unwrap_or("?")))).collect(); + assert!(failures.is_empty(), "{} of {} cases:\n{}", failures.len(), cases.len(), failures.join("\n")); +} + +/// The bundle is the files: each case's file (text, or base64 for jeff) and +/// reference as the manifest names them, and the manifest's expectations. +#[cfg(not(target_os = "wasi"))] +#[test] +fn the_bundle_is_the_corpus() { + let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../quantum-conformance/formats"); + let manifest: Value = serde_json::from_str(&std::fs::read_to_string(dir.join("manifest.json")).unwrap()).unwrap(); + let cases: Vec = manifest["cases"] + .as_array() + .unwrap() + .iter() + .map(|c| { + let f = c["file"].as_str().unwrap(); + let bytes = std::fs::read(dir.join(f)).unwrap_or_else(|e| panic!("{f}: {e}")); + let mut d = json!({ "file": f, "format": c["format"], "expect": c["expect"] }); + if c["format"] == "jeff" { + d["base64"] = json!(formats::encode_base64(&bytes)); + } else { + d["text"] = json!(String::from_utf8(bytes).unwrap()); + } + if let Some(r) = c["reference"].as_str() { + d["reference"] = json!(std::fs::read_to_string(dir.join(r)).unwrap()); + } + d + }) + .collect(); + if std::env::var_os("WAI_FORMATS_BUNDLE_WRITE").is_some() { + let bundle = json!({ "format": manifest["format"], "about": "The manifest's cases with each file inline (text, or base64 for jeff), for builds that read no files.", "cases": cases }); + std::fs::write(dir.join("bundle.json"), serde_json::to_string_pretty(&bundle).unwrap() + "\n").unwrap(); + return; + } + let bundle: Value = serde_json::from_str(BUNDLE).unwrap(); + assert_eq!(bundle["cases"], Value::Array(cases), "bundle.json differs from the files; write it with WAI_FORMATS_BUNDLE_WRITE=1"); +} -- 2.54.0 (Apple Git-157) From 421a165493849c2f200ab83c8351c49f241b3a7a Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Fri, 30 Oct 2026 10:42:25 -0400 Subject: [PATCH 03/10] wai-quantum: other formats on every surface The command line converts with `wai-quantum convert FILE --to qasm|qir|jeff [--profile base|adaptive] [-o FILE] [--json]`, the conversion checked and its notes on standard error, and every verb that reads a program reads QIR (.ll, .qir) and jeff (.jeff) by its extension, or by the global --from. Both MCP servers serve convert_program (the one definition, from the engine), its answer the tools schema's new convert; the job service answers POST /v1/convert within its bounds, and a job may carry its program as {"source": {"format", "program" | "program_base64"}} in place of qasm, read before anything else reads it. The Studio's engine has engine_convert. The agent docs are regenerated; both smoke scripts convert, run a QIR module and check a profile's refusal; the wasm32 and native pins in wai-web-wasm.yml move to 19 and 27. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/wai-web-wasm.yml | 17 +- wai/wai-quantum-cli/docs/llms-full.txt | 534 ++++++++++++++++ wai/wai-quantum-cli/docs/llms.txt | 2 + wai/wai-quantum-cli/docs/local-mcp.json | 442 +++++++++++++ wai/wai-quantum-cli/docs/service-mcp.json | 579 ++++++++++++++++++ wai/wai-quantum-cli/locales/zh-CN.json | 1 + .../skills/wai-quantum/reference/tools.md | 2 + wai/wai-quantum-cli/smoke.sh | 16 + wai/wai-quantum-cli/src/main.rs | 126 +++- wai/wai-quantum-cli/src/mcp.rs | 9 +- .../schemas/service-tools.v1.json | 10 + wai/wai-quantum-service/smoke.sh | 8 + wai/wai-quantum-service/src/analysis.rs | 47 +- wai/wai-quantum-service/src/discovery.rs | 7 +- wai/wai-quantum-service/src/http.rs | 18 +- wai/wai-quantum-service/src/mcp.rs | 24 +- wai/wai-quantum/schemas/job-request.v1.json | 29 + wai/wai-quantum/schemas/tools.v1.json | 132 ++++ wai/wai-quantum/src/engine/formats/mod.rs | 29 +- wai/wai-web/src/quantum_engine.rs | 53 ++ 20 files changed, 2063 insertions(+), 22 deletions(-) diff --git a/.github/workflows/wai-web-wasm.yml b/.github/workflows/wai-web-wasm.yml index 7c8dbd883..781946185 100644 --- a/.github/workflows/wai-web-wasm.yml +++ b/.github/workflows/wai-web-wasm.yml @@ -178,9 +178,10 @@ jobs: # engine_verdict, a repair there, the answer conformance set # (wai/quantum-conformance/answers/bundle.json) read to its codes, root and # order, its stored answers' receipts verified by running them again, a - # course check whose bytes match the native build's pinned digest, and a - # pulse run whose final density matrix is the native build's bits (its - # pinned digest). The count is the four tests above and these fifteen. + # course check whose bytes match the native build's pinned digest, a pulse + # run whose final density matrix is the native build's bits (its pinned + # digest), and a program converted between formats to the QIR bytes the + # conformance set pins. The count is the four tests above and these sixteen. - name: The job engine in a page (wasm32 tests under Node) shell: bash working-directory: wai/wai-web @@ -188,7 +189,7 @@ jobs: set -euo pipefail CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER=wasm-bindgen-test-runner \ cargo test --locked --target wasm32-unknown-unknown --features quantum_engine 2>&1 | tee "$RUNNER_TEMP/wai-web-engine-wasm.log" - grep -q "test result: ok. 19 passed; 0 failed" "$RUNNER_TEMP/wai-web-engine-wasm.log" + grep -q "test result: ok. 20 passed; 0 failed" "$RUNNER_TEMP/wai-web-engine-wasm.log" # Its native half: the service's form and receipt, any angle on the state # vector, what does not run saying why, plan, lint, draw and methods as JSON, # critique, equivalence and variants as JSON, the classical shortcuts, the @@ -204,15 +205,17 @@ jobs: # sealed there and worked out again, a repair held to the flagged # lines there, an answer in the answer view layer streamed in, its calls # run and its seal verified there, a correctness record and its corpus made - # there, the circuits course read and checked there, and a pulse program's - # schedule drawn, its run as a job to the pinned bits and its check there. + # there, the circuits course read and checked there, a pulse program's + # schedule drawn, its run as a job to the pinned bits and its check there, + # and a program carried between OpenQASM, QIR and jeff there, each + # conversion checked. - name: The job engine in a page (native unit tests) shell: bash working-directory: wai/wai-web run: | set -euo pipefail cargo test --locked --lib --features quantum_engine quantum_engine:: 2>&1 | tee "$RUNNER_TEMP/wai-web-engine.log" - grep -q "test result: ok. 27 passed; 0 failed" "$RUNNER_TEMP/wai-web-engine.log" + grep -q "test result: ok. 28 passed; 0 failed" "$RUNNER_TEMP/wai-web-engine.log" # The crate's native unit tests, run on the host: the pure-Rust halves of the # bindings, which wasm_tests does not run. They decode the committed integer # payloads to their sidecars' SHA-256 (int_payload::tests) and check that the diff --git a/wai/wai-quantum-cli/docs/llms-full.txt b/wai/wai-quantum-cli/docs/llms-full.txt index 739c20fc9..1eb53d4dc 100644 --- a/wai/wai-quantum-cli/docs/llms-full.txt +++ b/wai/wai-quantum-cli/docs/llms-full.txt @@ -122,6 +122,18 @@ Parameters: - `b` (string, required): The second program. - `inputs` (object): Values for a generator's integer inputs, as {"n": 5}: the program is read as its family's circuit at those values. +### `convert_program`: Convert a program between formats + +Carry a program between OpenQASM 3, QIR (LLVM text, read under its base and adaptive profiles, written as QIR 2.0) and jeff (its Cap'n Proto message, as base64). The answer is the program written, the program as OpenQASM 3 (what every other tool takes), each note with its code (a rotation at an arbitrary angle, a custom gate read by its name, what a format leaves out), what was read and written, and the check: the output read back and held to the program by the equivalence ladder. A construct a reader cannot hold exactly is refused with its code (Q700 to Q721) and its place: quote the code, and never guess around it. + +Parameters: +- `program` (string): The program's text: OpenQASM or QIR. +- `program_base64` (string): The program's bytes as base64: jeff. +- `from` (string, one of qasm, qir, jeff): Its format; found from its bytes when not given. +- `to` (string, required, one of qasm, qir, jeff): The format to write. +- `profile` (string, one of base, adaptive): QIR's profile to write: by default base when the program's measurements all come at the end, else adaptive. +- `check` (boolean): false: write it without the check. + ### `improve_qasm`: Improve an OpenQASM program The program with its smells fixed in its own text (all of them, and each alone), each with its measures and the equivalence check's verdict against the original. A candidate the check finds different is never returned. @@ -597,6 +609,18 @@ Parameters: - `a` (string, required) - `b` (string, required) +### `convert_program`: Convert a program between formats + +Carry a program between OpenQASM 3, QIR (LLVM text, read under its base and adaptive profiles, written as QIR 2.0) and jeff (its Cap'n Proto message, as base64). The answer is the program written, the program as OpenQASM 3 (what every other tool takes), each note with its code (a rotation at an arbitrary angle, a custom gate read by its name, what a format leaves out), what was read and written, and the check: the output read back and held to the program by the equivalence ladder. A construct a reader cannot hold exactly is refused with its code (Q700 to Q721) and its place: quote the code, and never guess around it. A conversion larger than the service takes is refused with the command that runs it locally. + +Parameters: +- `program` (string): The program's text: OpenQASM or QIR. +- `program_base64` (string): The program's bytes as base64: jeff. +- `from` (string, one of qasm, qir, jeff): Its format; found from its bytes when not given. +- `to` (string, required, one of qasm, qir, jeff): The format to write. +- `profile` (string, one of base, adaptive): QIR's profile to write: by default base when the program's measurements all come at the end, else adaptive. +- `check` (boolean): false: write it without the check. + ### `improve_qasm`: Improve a quantum program The program with its smells fixed in its own text (all of them, and each alone), each candidate with its measures and the equivalence check's verdict against the program. A candidate the check finds different is never returned. @@ -1068,6 +1092,25 @@ Every code: | `Q502` | refused | A program that measures a qubit and then acts on it, resets one after its first gate, or acts on measured bits: the bit-exact circuit takes programs whose measurements all come at the end. | | `Q600` | carried | An `output` declaration, carried into the result: its bits (their places in each outcome) or its value. | | `Q601` | refused | An `output` that is not a bit and whose value depends on a measurement at the end. | +| `Q700` | refused | A file that does not read in the format named: QIR that is not LLVM text with one entry point, or jeff that is not a Cap'n Proto message of the jeff schema (a pointer outside the message, a struct or list of the wrong shape, more than the reader takes); the message says what and where. | +| `Q701` | refused | A format version the reader does not take: QIR whose `qir_major_version` is not 1 or 2, or jeff other than 0.2 and 0.3. | +| `Q702` | warned | A QIR module that leaves out what its profile requires (the `qir_profiles` attribute, the version flags, `required_num_qubits`, `required_num_results`, the `entry_point` attribute of its only function): read from what it holds, and said so. | +| `Q703` | refused | A QIR profile the reader does not take: `qir_profiles` names neither `base_profile` nor `adaptive_profile`. | +| `Q704` | refused | What the module's own QIR profile forbids: under the base profile a branch, a classical instruction, reading a result, or a gate or reset on a qubit after its measurement; under the adaptive profile an optional capability its module flags do not declare (computation on integers or floats of a type, IR-defined functions, loops, `switch`, several returns, dynamic qubits or results, arrays). | +| `Q705` | refused | A QIR capability the module may use that this reader does not take: loops, `switch`, `phi`, IR-defined functions, dynamic allocation of qubits or results, arrays, controlled forms that take their controls in an array, or a classical value computed from a measurement anywhere but a branch's condition. | +| `Q706` | refused | An instruction or function the reader does not know: a quantum intrinsic outside the gates it reads (`docs/formats.md` lists them), a runtime function it does not take, an LLVM instruction no profile lists, or a function a module declares and does not define. | +| `Q707` | refused | A QIR operand that does not read: a qubit or result that is not a constant index, an index past `required_num_qubits` or `required_num_results`, or an angle that is not a finite number known on reading. | +| `Q708` | refused | A value whose meaning the program's text cannot keep: in QIR a result read, measured again and only then branched on; in jeff a qubit the two sides of a branch on a measurement leave in different places. | +| `Q709` | warned | Rotations at angles that are not dyadic multiples of 2π, read at their exact doubles: the double-precision methods take them; the bit-exact method refuses them (Q500). | +| `Q710` | refused | A jeff custom gate the reader does not know: its name is not a gate of the standard library (in any case), or its qubits or parameters are not that gate's. | +| `Q711` | warned | A jeff custom gate named as a gate of the standard library (`H`, `CX`, `Rx`, in any case) with that gate's qubits and parameters: read as that gate, by its name alone. | +| `Q712` | lowered | A jeff Pauli-product rotation, `exp(iθP)` as its schema defines it, lowered exactly to basis changes, a parity ladder and one `rz`. | +| `Q713` | refused | A jeff value used where it cannot be: a qubit or register used twice, a value used before it is defined or never defined, a cycle, or a value of the wrong type or count for its operation. | +| `Q714` | refused | A jeff value the reader needs on reading and cannot know: a gate angle, a loop's bounds or a `while` condition from a measurement, a register's size or index, a `switch` on a value that is neither known nor a measured bit, or integer arithmetic on a measured bit beyond not, and, or, xor, eq and select. | +| `Q715` | lowered | jeff's structured control flow: a `for` or `while` decided on reading unrolled, a `switch` on a known value decided, a `switch` on a measured bit written as `if … else`, and a call of a function the module defines inlined. | +| `Q716` | warned | A jeff entry point that takes qubits: they are the program's first qubits, starting in \|0⟩, and the qubits it returns are not measured. | +| `Q720` | refused | A program the format written cannot hold as it is: under QIR's base profile a measurement in the middle, a reset after a gate or a condition (write the adaptive profile), a loop in QIR, or in jeff a branch on a measured bit. | +| `Q721` | warned | What the format read or written has no place for, left out and said so: delays, boxes, barriers and gate durations, calibrations, `output` declarations, and classical values recorded as output. | ### `wai-quantum://docs/methods`: the methods and their accuracy classes @@ -1335,6 +1378,10 @@ Commands: run Run a program here, planned as the job service plans it, and sign the result plan Show the method a program would run on and why, without running it instance Write a generator out at its `--input` values, as plain OpenQASM + convert Carry a program between formats (OpenQASM 3, QIR as LLVM text, jeff), every + conversion checked: what is written is read back and held to the program by the + equivalence ladder; notes and the check go to standard error (exit 1 refused, 2 the + check found it different) lint Check a program and print each problem with its line draw Draw a program's circuit: as text, or as an SVG image, LaTeX (plain TikZ) or words debug Step through a program on the engine's exact methods and look into the state at a @@ -1444,6 +1491,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -1477,6 +1528,10 @@ Options: --seed The seed shots are sampled with (1 if not given) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --method A method by name, or auto (the default) for the strongest the program admits; `wai-quantum methods` lists them @@ -1607,6 +1662,10 @@ Options: --seed The seed shots are sampled with (1 if not given) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --method A method by name, or auto (the default) for the strongest the program admits; `wai-quantum methods` lists them @@ -1707,6 +1766,61 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + + -h, --help + Print help + + -V, --version + Print version +``` + +### `wai-quantum convert` + +```text +Carry a program between formats (OpenQASM 3, QIR as LLVM text, jeff), every conversion checked: what +is written is read back and held to the program by the equivalence ladder; notes and the check go to +standard error (exit 1 refused, 2 the check found it different) + +Usage: wai-quantum convert [OPTIONS] --to [FILE] + +Arguments: + [FILE] + The program; `-` or nothing reads standard input; its format is --from, else its + extension, else what its bytes look like + +Options: + --input + A value for a generator's integer `input`, as n=5 (repeat for each input): the program is + read as its family's circuit at those values + + --to + The format to write: qasm, qir or jeff + + --lang + The language of the words the command line writes itself, en or zh-CN (by default the one + LANG names); results, codes and the engine's own messages stay in English + + --profile + QIR's profile, base or adaptive: by default the base profile when the program's + measurements all come at the end, else the adaptive one + + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + + -o, --output + Write the program to this file (jeff is binary: give a file, or --json for its base64) + + --json + Print the conversion as JSON: the program (base64 for jeff), the program as OpenQASM, the + notes, what was read and written, and the check + + --no-check + Write it without the check + -h, --help Print help @@ -1737,6 +1851,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -1772,6 +1890,10 @@ Options: --svg The drawing as a standalone SVG image, the Studio's picture + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --latex The drawing as LaTeX: one picture in plain TikZ, which needs only \usepackage{tikz} @@ -1813,6 +1935,10 @@ Options: --line Stop before the first step on this line + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --outcomes The outcome of each measurement (and reset) the path meets, in order, as 0,1,1; the likelier when not given @@ -1867,6 +1993,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -1898,6 +2028,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -1932,6 +2066,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -1964,6 +2102,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -1999,6 +2141,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -2046,6 +2192,10 @@ Options: [default: clifford_t] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --twirl Twirl each two-qubit Clifford gate with Paulis drawn from this seed (one instance; checked to be the same gate) @@ -2113,6 +2263,10 @@ Options: [default: 0.001] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --qubit A reference qubit (us-e3, us-e4, ns-e3, ns-e4, meas-e4, meas-e6), `all` of them, or a JSON file describing one @@ -2189,6 +2343,10 @@ Options: Round each angle the bit-exact method refuses (Q500) to the nearest multiple of 2π/2^K, its rounding stated on its line + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --size A register's size, for one used before it is declared (Q102), as q=3 (repeatable) @@ -2236,6 +2394,10 @@ Options: --seed The seed shots are sampled with (1 if not given) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --method A method by name, or auto (the default) for the strongest the program admits; `wai-quantum methods` lists them @@ -2373,6 +2535,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --service The service's address @@ -2413,6 +2579,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --service The service's address @@ -2453,6 +2623,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --service The service's address @@ -2497,6 +2671,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --service The service's address @@ -2542,6 +2720,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --service The service's address @@ -2586,6 +2768,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --service The service's address @@ -2631,6 +2817,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --service The service's address @@ -2675,6 +2865,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --service The service's address @@ -2715,6 +2909,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --service The service's address @@ -2762,6 +2960,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -2792,6 +2994,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -2819,6 +3025,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -2850,6 +3060,10 @@ Options: [default: 16] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -2877,6 +3091,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -2909,6 +3127,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --memory-gb The memory a job may take, GiB @@ -2956,6 +3178,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --memory-gb The memory a job may take, GiB, when not the server's default @@ -2991,6 +3217,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --name The name the entry goes under @@ -3043,6 +3273,10 @@ Options: --write Write each file back formatted + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3080,6 +3314,10 @@ Options: Sign with the published demo key: the receipt then shows a result is unaltered, not who signed it + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --stdio Accepted for the editors that pass it: standard input and output is the server's only transport @@ -3119,6 +3357,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3156,6 +3398,10 @@ Options: Sign with the published demo key: the receipt then shows a result is unaltered, not who signed it + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3191,6 +3437,10 @@ Options: [default: 1] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --max-edits Edits made to a parent before it is evaluated, at most @@ -3249,6 +3499,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3285,6 +3539,10 @@ Options: --parent The id of the archive entry it came from + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --proposer Who wrote it @@ -3336,6 +3594,10 @@ Options: --source Print one entry's generator, by id + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --json Print the summary as JSON @@ -3369,6 +3631,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3406,6 +3672,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3444,6 +3714,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3482,6 +3756,10 @@ Options: [default: person] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --name Which one: a model's or an agent's name, the rewrite, where the program came from @@ -3528,6 +3806,10 @@ Options: --locks The rules a proposal must keep (default: beside the program, as NAME.locks.json, if there) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --parent The version it was changed from (default: the head) @@ -3585,6 +3867,10 @@ Options: --locks The rules a proposal must keep (default: beside the program, as NAME.locks.json, if there) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --parent @@ -3630,6 +3916,10 @@ Options: --accept The changes accepted: their numbers from `changes` (as 0,2), all, or none + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --parent @@ -3695,6 +3985,10 @@ Options: [default: person] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --name Which one: a model's or an agent's name, the rewrite, where the program came from @@ -3741,6 +4035,10 @@ Options: --locks The rules a proposal must keep (default: beside the program, as NAME.locks.json, if there) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --json @@ -3780,6 +4078,10 @@ Options: --locks The rules a proposal must keep (default: beside the program, as NAME.locks.json, if there) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --json @@ -3817,6 +4119,10 @@ Options: --locks The rules a proposal must keep (default: beside the program, as NAME.locks.json, if there) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --job A job from `run --json --history` or the service @@ -3855,6 +4161,10 @@ Options: The program it is meant to improve on: the measures it beats it on that --weigh does not name + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --weigh The measures the cost weighs, comma-separated (as depth,two_qubit) @@ -3890,6 +4200,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3926,6 +4240,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3956,6 +4274,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3989,6 +4311,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4023,6 +4349,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4060,6 +4390,10 @@ Options: Sign with the published demo key: the receipt then shows a result is unaltered, not who signed it + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4083,6 +4417,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4113,6 +4451,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4154,6 +4496,10 @@ Options: Sign with the published demo key: the receipt then shows a result is unaltered, not who signed it + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --json @@ -4188,6 +4534,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4222,6 +4572,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4258,6 +4612,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4290,6 +4648,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4320,6 +4682,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4353,6 +4719,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4383,6 +4753,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4416,6 +4790,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4449,6 +4827,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4487,6 +4869,10 @@ Options: [default: equivalent] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --profile The profile's name: one person can keep several (for teaching, for one device) @@ -4526,6 +4912,10 @@ Options: --taste The file the profiles are kept in + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --json @@ -4562,6 +4952,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --novelty The novelty dial: −1 toward the typical, 1 toward the novel @@ -4612,6 +5006,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --task `prepares` or `equivalent` (a specification's own task by default) @@ -4657,6 +5055,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4693,6 +5095,10 @@ Options: --check Check this certificate (a sealed answer or the certificate alone) against the code + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --json Print the answer sealed with this user's key, as JSON (`wai-quantum verify` checks it) @@ -4735,6 +5141,10 @@ Options: --error The flipped qubits, as 3,12 (its syndrome is decoded and its residual judged) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --memory The rotated surface-code memory experiment, as DISTANCE,ROUNDS,P @@ -4801,6 +5211,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --p Physical rates, as 0.002,0.004 @@ -4974,6 +5388,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5006,6 +5424,10 @@ Options: The model server: a chat-completions endpoint on this machine or a private network, as http://127.0.0.1:8080/v1 + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --name The model's name as its server knows it (default: the server's own) @@ -5077,6 +5499,10 @@ Options: -o, --out Write the corpus here (default: standard output) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5105,6 +5531,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5141,6 +5571,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5177,6 +5611,10 @@ Options: [default: 16] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --junit Also write the verdict as a JUnit report to this file, one test case for each intent @@ -5221,6 +5659,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5267,6 +5709,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help (see a summary with '-h') @@ -5304,6 +5750,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --json Print the course and your progress as JSON @@ -5337,6 +5787,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5370,6 +5824,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5404,6 +5862,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5440,6 +5902,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5474,6 +5940,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5503,6 +5973,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5537,6 +6011,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5574,6 +6052,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --provider Read one of your providers' describe answers instead of a file (`wai-quantum providers list`) @@ -5610,6 +6092,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5643,6 +6129,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --view Print what the Studio draws: each qubit's place and errors, each pair's error @@ -5673,6 +6163,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5696,6 +6190,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5735,6 +6233,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5773,6 +6275,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --set A setting that is not a secret, as backend=NAME (repeatable) @@ -5813,6 +6319,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5840,6 +6350,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5871,6 +6385,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5905,6 +6423,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5937,6 +6459,10 @@ Options: [default: 16] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5965,6 +6491,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5992,6 +6522,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help diff --git a/wai/wai-quantum-cli/docs/llms.txt b/wai/wai-quantum-cli/docs/llms.txt index 996556f9f..b1f634681 100644 --- a/wai/wai-quantum-cli/docs/llms.txt +++ b/wai/wai-quantum-cli/docs/llms.txt @@ -47,6 +47,7 @@ Runs on this machine, signed with the user's key; nothing leaves the machine. - `repair_qasm`: Repair a program on its flagged lines - `critique_qasm`: Critique an OpenQASM program - `check_equivalent`: Check two programs are the same circuit +- `convert_program`: Convert a program between formats - `improve_qasm`: Improve an OpenQASM program - `classical_shortcuts`: Find a program's classical shortcuts - `compile_qasm`: Compile a program, every pass checked @@ -95,6 +96,7 @@ Runs on this machine, signed with the user's key; nothing leaves the machine. - `verify_quantum_receipt`: Verify a result's receipt - `critique_qasm`: Critique a quantum program - `check_equivalent`: Check two quantum programs are the same circuit + - `convert_program`: Convert a program between formats - `improve_qasm`: Improve a quantum program - `classical_shortcuts`: Find a quantum program's classical shortcuts - `compile_qasm`: Compile a quantum program, every pass checked diff --git a/wai/wai-quantum-cli/docs/local-mcp.json b/wai/wai-quantum-cli/docs/local-mcp.json index 8416d4036..b9649dc2b 100644 --- a/wai/wai-quantum-cli/docs/local-mcp.json +++ b/wai/wai-quantum-cli/docs/local-mcp.json @@ -542,6 +542,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -549,6 +554,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -3368,6 +3397,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -3375,6 +3409,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -12570,6 +12628,274 @@ ] } }, + { + "name": "convert_program", + "title": "Convert a program between formats", + "description": "Carry a program between OpenQASM 3, QIR (LLVM text, read under its base and adaptive profiles, written as QIR 2.0) and jeff (its Cap'n Proto message, as base64). The answer is the program written, the program as OpenQASM 3 (what every other tool takes), each note with its code (a rotation at an arbitrary angle, a custom gate read by its name, what a format leaves out), what was read and written, and the check: the output read back and held to the program by the equivalence ladder. A construct a reader cannot hold exactly is refused with its code (Q700 to Q721) and its place: quote the code, and never guess around it.", + "inputSchema": { + "type": "object", + "required": [ + "to" + ], + "properties": { + "program": { + "type": "string", + "description": "The program's text: OpenQASM or QIR." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64: jeff." + }, + "from": { + "type": "string", + "enum": [ + "qasm", + "qir", + "jeff" + ], + "description": "Its format; found from its bytes when not given." + }, + "to": { + "type": "string", + "enum": [ + "qasm", + "qir", + "jeff" + ], + "description": "The format to write." + }, + "profile": { + "type": "string", + "enum": [ + "base", + "adaptive" + ], + "description": "QIR's profile to write: by default base when the program's measurements all come at the end, else adaptive." + }, + "check": { + "type": "boolean", + "description": "false: write it without the check." + } + } + }, + "outputSchema": { + "$id": "wai-quantum://schemas/convert_program/1", + "type": "object", + "description": "A program carried from one format to another (OpenQASM 3, QIR as LLVM text, jeff as its Cap'n Proto message) and checked (engine::formats::convert_json): the program written, the program as read in OpenQASM 3, the readers' and writer's notes with their codes, what was read and written, and the check that holds the output to the input.", + "anyOf": [ + { + "type": "object", + "description": "A program carried from one format to another (OpenQASM 3, QIR as LLVM text, jeff as its Cap'n Proto message) and checked (engine::formats::convert_json): the program written, the program as read in OpenQASM 3, the readers' and writer's notes with their codes, what was read and written, and the check that holds the output to the input.", + "required": [ + "ok", + "format", + "from", + "to", + "qasm", + "notes", + "read", + "written", + "check" + ], + "properties": { + "ok": { + "const": true + }, + "format": { + "const": "wai.quantum.convert/1" + }, + "from": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "to": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program written, for a text format (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program written, for a binary format (jeff), as standard base64." + }, + "qasm": { + "type": "string", + "description": "The program as read, as OpenQASM 3: what every other tool takes." + }, + "notes": { + "type": "array", + "items": { + "type": "object", + "description": "One of the reader's diagnostics: its code, what happens to the construct it is about, its message (without the line or the code), and where it stands.", + "required": [ + "code", + "outcome", + "message" + ], + "properties": { + "code": { + "type": "string" + }, + "outcome": { + "enum": [ + "refused", + "lowered", + "carried", + "preserved", + "warned" + ] + }, + "message": { + "type": "string" + }, + "line": { + "type": "integer", + "minimum": 1 + }, + "column": { + "type": "integer", + "minimum": 1 + }, + "length": { + "type": "integer", + "minimum": 0 + } + } + }, + "description": "Each warning and note, with its code (Q7xx for the formats) and, for QIR, its line." + }, + "read": { + "type": "object", + "required": [ + "format" + ], + "description": "What was read: the format, its version, a QIR module's profile, entry point, qubits, results and declared capabilities, a jeff module's tool, entry point, qubits and bits." + }, + "written": { + "type": "object", + "required": [ + "format" + ], + "description": "What was written: the format, its version, and QIR's profile." + }, + "check": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "object", + "required": [ + "what", + "verdict" + ], + "properties": { + "what": { + "type": "string", + "description": "What was held to what." + }, + "verdict": { + "type": "object", + "required": [ + "verdict", + "how", + "why" + ], + "properties": { + "verdict": { + "enum": [ + "equivalent", + "different", + "unchecked" + ] + }, + "how": { + "enum": [ + "tableau", + "exact", + "rewrites", + "random", + "state", + "shape", + "steps", + "distribution", + "identity", + "none" + ], + "description": "The equivalence ladder's check (tableau, exact and rewrites are proofs; random is evidence); steps: the same steps one for one; distribution: every outcome's probability the same (evidence); identity: OpenQASM passed through; none: unchecked." + }, + "why": { + "type": "string" + }, + "global_phase": { + "type": [ + "number", + "null" + ] + } + } + } + } + } + ], + "description": "The output read back and held to the program read (or, for a program read into OpenQASM, written back to its format and read again), or null when no check was asked." + } + } + }, + { + "type": "object", + "description": "A refusal as data: the message, every code in it (the last is the code), its outcome and meaning as the code's table gives them. A host that refuses for its own reasons gives the error alone.", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "code": { + "type": [ + "string", + "null" + ] + }, + "codes": { + "type": "array", + "items": { + "type": "string" + } + }, + "outcome": { + "enum": [ + "refused", + "retry", + "failed", + "lowered", + "carried", + "preserved", + "warned", + "dropped", + "defaulted" + ] + }, + "meaning": { + "type": "string" + } + } + } + ] + } + }, { "name": "improve_qasm", "title": "Improve an OpenQASM program", @@ -27294,6 +27620,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -27301,6 +27632,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -30120,6 +30475,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -30127,6 +30487,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -36482,6 +36866,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -36489,6 +36878,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -39308,6 +39721,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -39315,6 +39733,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, diff --git a/wai/wai-quantum-cli/docs/service-mcp.json b/wai/wai-quantum-cli/docs/service-mcp.json index 287f33231..b0a0ad027 100644 --- a/wai/wai-quantum-cli/docs/service-mcp.json +++ b/wai/wai-quantum-cli/docs/service-mcp.json @@ -547,6 +547,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -554,6 +559,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -3373,6 +3402,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -3380,6 +3414,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -8723,6 +8781,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -8730,6 +8793,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -11549,6 +11636,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -11556,6 +11648,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -17903,6 +18019,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -17910,6 +18031,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -20729,6 +20874,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -20736,6 +20886,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -28702,6 +28876,324 @@ ] } }, + { + "name": "convert_program", + "title": "Convert a program between formats", + "description": "Carry a program between OpenQASM 3, QIR (LLVM text, read under its base and adaptive profiles, written as QIR 2.0) and jeff (its Cap'n Proto message, as base64). The answer is the program written, the program as OpenQASM 3 (what every other tool takes), each note with its code (a rotation at an arbitrary angle, a custom gate read by its name, what a format leaves out), what was read and written, and the check: the output read back and held to the program by the equivalence ladder. A construct a reader cannot hold exactly is refused with its code (Q700 to Q721) and its place: quote the code, and never guess around it. A conversion larger than the service takes is refused with the command that runs it locally.", + "inputSchema": { + "type": "object", + "required": [ + "to" + ], + "properties": { + "program": { + "type": "string", + "description": "The program's text: OpenQASM or QIR." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64: jeff." + }, + "from": { + "type": "string", + "enum": [ + "qasm", + "qir", + "jeff" + ], + "description": "Its format; found from its bytes when not given." + }, + "to": { + "type": "string", + "enum": [ + "qasm", + "qir", + "jeff" + ], + "description": "The format to write." + }, + "profile": { + "type": "string", + "enum": [ + "base", + "adaptive" + ], + "description": "QIR's profile to write: by default base when the program's measurements all come at the end, else adaptive." + }, + "check": { + "type": "boolean", + "description": "false: write it without the check." + } + } + }, + "annotations": { + "readOnlyHint": true + }, + "outputSchema": { + "$id": "wai-quantum://schemas/convert_program/1", + "type": "object", + "description": "What convert_program answers.", + "anyOf": [ + { + "anyOf": [ + { + "type": "object", + "description": "A program carried from one format to another (OpenQASM 3, QIR as LLVM text, jeff as its Cap'n Proto message) and checked (engine::formats::convert_json): the program written, the program as read in OpenQASM 3, the readers' and writer's notes with their codes, what was read and written, and the check that holds the output to the input.", + "required": [ + "ok", + "format", + "from", + "to", + "qasm", + "notes", + "read", + "written", + "check" + ], + "properties": { + "ok": { + "const": true + }, + "format": { + "const": "wai.quantum.convert/1" + }, + "from": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "to": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program written, for a text format (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program written, for a binary format (jeff), as standard base64." + }, + "qasm": { + "type": "string", + "description": "The program as read, as OpenQASM 3: what every other tool takes." + }, + "notes": { + "type": "array", + "items": { + "type": "object", + "description": "One of the reader's diagnostics: its code, what happens to the construct it is about, its message (without the line or the code), and where it stands.", + "required": [ + "code", + "outcome", + "message" + ], + "properties": { + "code": { + "type": "string" + }, + "outcome": { + "enum": [ + "refused", + "lowered", + "carried", + "preserved", + "warned" + ] + }, + "message": { + "type": "string" + }, + "line": { + "type": "integer", + "minimum": 1 + }, + "column": { + "type": "integer", + "minimum": 1 + }, + "length": { + "type": "integer", + "minimum": 0 + } + } + }, + "description": "Each warning and note, with its code (Q7xx for the formats) and, for QIR, its line." + }, + "read": { + "type": "object", + "required": [ + "format" + ], + "description": "What was read: the format, its version, a QIR module's profile, entry point, qubits, results and declared capabilities, a jeff module's tool, entry point, qubits and bits." + }, + "written": { + "type": "object", + "required": [ + "format" + ], + "description": "What was written: the format, its version, and QIR's profile." + }, + "check": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "object", + "required": [ + "what", + "verdict" + ], + "properties": { + "what": { + "type": "string", + "description": "What was held to what." + }, + "verdict": { + "type": "object", + "required": [ + "verdict", + "how", + "why" + ], + "properties": { + "verdict": { + "enum": [ + "equivalent", + "different", + "unchecked" + ] + }, + "how": { + "enum": [ + "tableau", + "exact", + "rewrites", + "random", + "state", + "shape", + "steps", + "distribution", + "identity", + "none" + ], + "description": "The equivalence ladder's check (tableau, exact and rewrites are proofs; random is evidence); steps: the same steps one for one; distribution: every outcome's probability the same (evidence); identity: OpenQASM passed through; none: unchecked." + }, + "why": { + "type": "string" + }, + "global_phase": { + "type": [ + "number", + "null" + ] + } + } + } + } + } + ], + "description": "The output read back and held to the program read (or, for a program read into OpenQASM, written back to its format and read again), or null when no check was asked." + } + } + }, + { + "type": "object", + "description": "A refusal as data: the message, every code in it (the last is the code), its outcome and meaning as the code's table gives them. A host that refuses for its own reasons gives the error alone.", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "code": { + "type": [ + "string", + "null" + ] + }, + "codes": { + "type": "array", + "items": { + "type": "string" + } + }, + "outcome": { + "enum": [ + "refused", + "retry", + "failed", + "lowered", + "carried", + "preserved", + "warned", + "dropped", + "defaulted" + ] + }, + "meaning": { + "type": "string" + } + } + } + ] + }, + { + "type": "object", + "description": "A refusal as data: the message, every code in it (the last is the code), its outcome and meaning as the code's table gives them. A host that refuses for its own reasons gives the error alone.", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "code": { + "type": [ + "string", + "null" + ] + }, + "codes": { + "type": "array", + "items": { + "type": "string" + } + }, + "outcome": { + "enum": [ + "refused", + "retry", + "failed", + "lowered", + "carried", + "preserved", + "warned", + "dropped", + "defaulted" + ] + }, + "meaning": { + "type": "string" + } + } + } + ] + } + }, { "name": "improve_qasm", "title": "Improve a quantum program", @@ -37611,6 +38103,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -37618,6 +38115,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -40437,6 +40958,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -40444,6 +40970,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -43443,6 +43993,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -43450,6 +44005,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, diff --git a/wai/wai-quantum-cli/locales/zh-CN.json b/wai/wai-quantum-cli/locales/zh-CN.json index d97c1f501..4c2311b45 100644 --- a/wai/wai-quantum-cli/locales/zh-CN.json +++ b/wai/wai-quantum-cli/locales/zh-CN.json @@ -37,6 +37,7 @@ "run": { "source": "Run a program here, planned as the job service plans it, and sign the result", "text": "在本机运行程序,按作业服务的方式规划,并为结果签名" }, "plan": { "source": "Show the method a program would run on and why, without running it", "text": "显示程序将在哪种方法上运行及其原因,而不运行它" }, "instance": { "source": "Write a generator out at its `--input` values, as plain OpenQASM", "text": "按 `--input` 给出的取值,把生成器写成普通的 OpenQASM" }, + "convert": { "source": "Carry a program between formats (OpenQASM 3, QIR as LLVM text, jeff), every conversion checked: what is written is read back and held to the program by the equivalence ladder; notes and the check go to standard error (exit 1 refused, 2 the check found it different)", "text": "在格式之间转换程序(OpenQASM 3、以 LLVM 文本表示的 QIR、jeff),每次转换都经过检查:写出的结果会被读回,并由等价判定阶梯与原程序比对;说明和检查结果输出到标准错误(退出状态 1 表示被拒绝,2 表示检查发现两者不同)" }, "lint": { "source": "Check a program and print each problem with its line", "text": "检查程序,并打印每个问题及其所在的行" }, "draw": { "source": "Draw a program's circuit: as text, or as an SVG image, LaTeX (plain TikZ) or words", "text": "绘制程序的线路:以文本、SVG 图像、LaTeX(纯 TikZ)或文字描述的形式" }, "debug": { "source": "Step through a program on the engine's exact methods and look into the state at a position or before a line, or list every branch at its measurements in the middle with its weight", "text": "在引擎的精确方法上单步执行程序,查看某个位置或某一行之前的状态,或列出中途测量处的每个分支及其权重" }, diff --git a/wai/wai-quantum-cli/skills/wai-quantum/reference/tools.md b/wai/wai-quantum-cli/skills/wai-quantum/reference/tools.md index 7e479769b..0681b76dd 100644 --- a/wai/wai-quantum-cli/skills/wai-quantum/reference/tools.md +++ b/wai/wai-quantum-cli/skills/wai-quantum/reference/tools.md @@ -10,6 +10,7 @@ Generated by `wai-quantum docs skill` from the servers' own lists; `wai-quantum - `repair_qasm`: Repair a program on its flagged lines - `critique_qasm`: Critique an OpenQASM program - `check_equivalent`: Check two programs are the same circuit +- `convert_program`: Convert a program between formats - `improve_qasm`: Improve an OpenQASM program - `classical_shortcuts`: Find a program's classical shortcuts - `compile_qasm`: Compile a program, every pass checked @@ -57,6 +58,7 @@ Generated by `wai-quantum docs skill` from the servers' own lists; `wai-quantum - `verify_quantum_receipt`: Verify a result's receipt - `critique_qasm`: Critique a quantum program - `check_equivalent`: Check two quantum programs are the same circuit +- `convert_program`: Convert a program between formats - `improve_qasm`: Improve a quantum program - `classical_shortcuts`: Find a quantum program's classical shortcuts - `compile_qasm`: Compile a quantum program, every pass checked diff --git a/wai/wai-quantum-cli/smoke.sh b/wai/wai-quantum-cli/smoke.sh index 5f94c4401..ec8fc02a0 100644 --- a/wai/wai-quantum-cli/smoke.sh +++ b/wai/wai-quantum-cli/smoke.sh @@ -126,6 +126,22 @@ if "$W" equivalent "$tmp/twice.qasm" "$tmp/ghz.qasm" > "$tmp/differ"; then fail has "$tmp/differ" "^different: one has 2 qubits and the other 3" echo "critique, improve, equivalent: ok" +# Other formats: OpenQASM written as QIR and jeff, each read back and checked, +# every verb reading them, and a module its profile forbids refused on its line. +"$W" convert "$tmp/twice.qasm" --to qir > "$tmp/twice.ll" 2> "$tmp/convert" +has "$tmp/convert" "^check: equivalent (tableau)" +has "$tmp/twice.ll" '"qir_profiles"="base_profile"' +"$W" convert "$tmp/twice.ll" --to jeff -o "$tmp/twice.jeff" 2> "$tmp/convert" +has "$tmp/convert" "^check: equivalent (tableau)" +"$W" equivalent "$tmp/twice.jeff" "$tmp/twice.qasm" > "$tmp/same" 2>/dev/null +has "$tmp/same" "^equivalent" +"$W" run "$tmp/twice.ll" --shots 64 --json > "$tmp/fromqir.json" +"$W" verify "$tmp/fromqir.json" > /dev/null || fail "a run of a QIR module did not verify" +printf 'define i64 @main() #0 {\nentry:\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)\n call void @__quantum__qis__x__body(ptr null)\n ret i64 0\n}\nattributes #0 = { "entry_point" "qir_profiles"="base_profile" "required_num_qubits"="1" "required_num_results"="1" }\n' > "$tmp/forbidden.ll" +if "$W" convert "$tmp/forbidden.ll" --to qasm > /dev/null 2> "$tmp/refused"; then fail "a gate after a measurement under the base profile was read"; fi +has "$tmp/refused" "line 4: the base profile forbids a gate on qubit 0 after its measurement \\[Q704\\]" +echo "convert: QIR and jeff, written, read back and checked; a profile's refusal on its line: ok" + "$W" shortcuts "$tmp/ghz.qasm" > "$tmp/shortcuts" has "$tmp/shortcuts" "^Classically easy at any width" printf 'OPENQASM 3.0;\nqubit[8] q;\nh q;\ncz q[0], q[1];\ncz q[1], q[2];\ncz q[2], q[3];\ncz q[3], q[4];\ncz q[4], q[5];\ncz q[5], q[6];\ncz q[6], q[7];\ncz q[7], q[0];\nrz(0.3) q;\n' > "$tmp/ring.qasm" diff --git a/wai/wai-quantum-cli/src/main.rs b/wai/wai-quantum-cli/src/main.rs index b862f319b..606172909 100644 --- a/wai/wai-quantum-cli/src/main.rs +++ b/wai/wai-quantum-cli/src/main.rs @@ -24,6 +24,8 @@ //! wai-quantum repair prog.qasm > fixed.qasm # the mechanical fixes, on the lines the diagnostics flag alone //! wai-quantum run ghz.qasm --input n=40 # a generator, read at n = 40 //! wai-quantum instance qft.qasm --input n=5 # a generator written out as plain OpenQASM +//! wai-quantum convert prog.qasm --to qir # QIR 2.0 (or jeff, or OpenQASM 3), the conversion checked +//! wai-quantum run prog.ll --shots 1000 # every verb reads QIR (.ll) and jeff (.jeff); --from names one //! wai-quantum discover new ghz.json # the workbench: an archive for a specification //! wai-quantum discover search ghz.json # cheaper generators, each checked at every size //! wai-quantum history blame prog.qasm # whose hand each line and gate is in @@ -129,6 +131,9 @@ struct Cli { /// The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English #[arg(long, global = true, value_name = "LOCALE")] lang: Option, + /// The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + #[arg(long, global = true, value_name = "FORMAT", value_parser = format_arg)] + from: Option, #[command(subcommand)] command: Command, } @@ -160,6 +165,26 @@ enum Command { /// The program; `-` or nothing reads standard input file: Option, }, + /// Carry a program between formats (OpenQASM 3, QIR as LLVM text, jeff), every conversion checked: what is written is read back and held to the program by the equivalence ladder; notes and the check go to standard error (exit 1 refused, 2 the check found it different) + Convert { + /// The program; `-` or nothing reads standard input; its format is --from, else its extension, else what its bytes look like + file: Option, + /// The format to write: qasm, qir or jeff + #[arg(long, value_name = "FORMAT", value_parser = format_arg)] + to: wai_quantum::engine::formats::Format, + /// QIR's profile, base or adaptive: by default the base profile when the program's measurements all come at the end, else the adaptive one + #[arg(long)] + profile: Option, + /// Write the program to this file (jeff is binary: give a file, or --json for its base64) + #[arg(short = 'o', long, value_name = "FILE")] + output: Option, + /// Print the conversion as JSON: the program (base64 for jeff), the program as OpenQASM, the notes, what was read and written, and the check + #[arg(long)] + json: bool, + /// Write it without the check + #[arg(long)] + no_check: bool, + }, /// Check a program and print each problem with its line Lint { /// The program; `-` or nothing reads standard input @@ -559,7 +584,52 @@ fn die(msg: &str) -> ! { /// (a program with integer `input`s) is written out at the `--input` values, /// so every command reads one circuit of its family. fn read_input(file: Option<&Path>) -> Result { - at_inputs(read_text(file)?, &INPUTS.get().cloned().unwrap_or_default()) + at_inputs(read_program(file)?, &INPUTS.get().cloned().unwrap_or_default()) +} + +/// The format `--from` gives, for every program read. +static FROM: std::sync::OnceLock> = std::sync::OnceLock::new(); + +fn format_arg(s: &str) -> Result { + wai_quantum::engine::formats::Format::parse(s).ok_or_else(|| format!("{s}: the formats are qasm, qir and jeff")) +} + +/// The bytes of `file`, or standard input for `-` or none. +fn read_bytes(file: Option<&Path>) -> Result, String> { + match file { + Some(p) if p != Path::new("-") => std::fs::read(p).map_err(|e| format!("{}: {e}", p.display())), + _ => { + use std::io::Read as _; + let mut b = Vec::new(); + std::io::stdin().read_to_end(&mut b).map_err(|e| format!("standard input: {e}"))?; + Ok(b) + } + } +} + +/// The format a program is in: `--from`, else its file's extension (`.ll` +/// and `.qir` are QIR, `.jeff` is jeff), else OpenQASM. +fn format_of(file: Option<&Path>) -> wai_quantum::engine::formats::Format { + use wai_quantum::engine::formats::Format; + if let Some(Some(f)) = FROM.get() { + return *f; + } + file.and_then(|p| Format::from_extension(&p.to_string_lossy())).unwrap_or(Format::Qasm) +} + +/// The program in `file` as OpenQASM: one in another format is read to it, +/// its notes on standard error. +fn read_program(file: Option<&Path>) -> Result { + use wai_quantum::engine::formats::{self, Format}; + let f = format_of(file); + if f == Format::Qasm { + return read_text(file); + } + let r = formats::read(f, &read_bytes(file)?).map_err(|d| d.to_string())?; + for n in &r.notes { + eprintln!("wai-quantum: {}: {n}", f.name()); + } + Ok(r.qasm) } /// `text` as one circuit: a generator written out at `inputs`, any other @@ -588,7 +658,7 @@ fn read_text(file: Option<&Path>) -> Result { /// request carries the history's account of the version the file holds; /// with `--intent`, what the program must do. fn job_request(job: &Job) -> Result { - let text = read_text(job.file.as_deref())?; + let text = read_program(job.file.as_deref())?; let inputs = INPUTS.get().cloned().unwrap_or_default(); // A sweep sends the program with its inputs, which the sweep gives values. if !job.sweep.is_empty() && !inputs.is_empty() { @@ -599,7 +669,7 @@ fn job_request(job: &Job) -> Result { if !job.programs.is_empty() { let mut all = vec![req["qasm"].take()]; for p in &job.programs { - all.push(json!(at_inputs(read_text(Some(p))?, &inputs)?)); + all.push(json!(at_inputs(read_program(Some(p))?, &inputs)?)); } req.as_object_mut().map(|o| o.remove("qasm")); req["programs"] = Value::Array(all); @@ -940,6 +1010,54 @@ fn print_dry_run(p: &Value) { println!("you {} active, {} submissions left in this window", p["client"]["active_jobs"], p["client"]["submissions_left"]); } +/// `convert`: the program in `file` written as `to`, to `output` or standard +/// output, its notes and the check on standard error (or all of it as JSON). +fn convert(file: Option<&Path>, to: wai_quantum::engine::formats::Format, profile: Option<&str>, output: Option<&Path>, as_json: bool, no_check: bool) { + use std::io::{IsTerminal, Write as _}; + use wai_quantum::engine::formats::{self, Format, Profile}; + let bytes = read_bytes(file).unwrap_or_else(|e| die(&e)); + let from = match FROM.get() { + Some(Some(f)) => *f, + _ => file.and_then(|p| Format::from_extension(&p.to_string_lossy())).unwrap_or_else(|| Format::sniff(&bytes)), + }; + let profile = profile.map(|p| Profile::parse(p).unwrap_or_else(|| die(&format!("{p}: the QIR profiles are base and adaptive")))); + let c = formats::convert(from, to, &bytes, profile, !no_check).unwrap_or_else(|d| die(&d.to_string())); + let verdict = c.check["verdict"]["verdict"].as_str().unwrap_or(""); + if as_json { + let mut v = json!({ + "ok": true, "format": formats::FORMAT, "from": from.name(), "to": to.name(), "qasm": c.qasm, + "notes": c.notes.iter().map(|d| d.to_json()).collect::>(), "read": c.read, "written": c.written, "check": c.check, + }); + if to.is_binary() { + v["program_base64"] = json!(formats::encode_base64(&c.output)); + } else { + v["program"] = json!(String::from_utf8_lossy(&c.output)); + } + if let Some(o) = output { + std::fs::write(o, &c.output).unwrap_or_else(|e| die(&format!("{}: {e}", o.display()))); + } + println!("{v}"); + } else { + for n in &c.notes { + eprintln!("note: {n}"); + } + if !c.check.is_null() { + eprintln!("check: {} ({}): {}", verdict, c.check["verdict"]["how"].as_str().unwrap_or(""), c.check["what"].as_str().unwrap_or("")); + } + match output { + Some(o) => std::fs::write(o, &c.output).unwrap_or_else(|e| die(&format!("{}: {e}", o.display()))), + None if to.is_binary() && std::io::stdout().is_terminal() => die("jeff is binary: give -o FILE, or --json for its base64"), + None => { + let mut out = std::io::stdout(); + out.write_all(&c.output).and_then(|_| out.flush()).unwrap_or_else(|e| die(&format!("standard output: {e}"))); + } + } + } + if verdict == "different" { + std::process::exit(2); + } +} + fn main() { // The help a parse prints is in the person's language, so the language is read first. let args: Vec = std::env::args().collect(); @@ -947,6 +1065,7 @@ fn main() { locale::set(loc); let cli = Cli::from_arg_matches(&locale::localize(Cli::command(), loc).get_matches_from(&args)).unwrap_or_else(|e| e.exit()); let _ = INPUTS.set(cli.inputs.iter().cloned().collect()); + let _ = FROM.set(cli.from); match cli.command { #[cfg(feature = "providers")] Command::Run { job, out, key, provider } if provider.provider.is_some() => providers::run(&job, &out, &key, &provider), @@ -1027,6 +1146,7 @@ fn main() { Command::Instance { file } => { print!("{}", read_input(file.as_deref()).unwrap_or_else(|e| die(&e))); } + Command::Convert { file, to, profile, output, json, no_check } => convert(file.as_deref(), to, profile.as_deref(), output.as_deref(), json, no_check), Command::Lint { file, json } => { let name = file.as_deref().filter(|p| *p != Path::new("-")).map(|p| p.display().to_string()).unwrap_or_else(|| "".into()); let src = read_input(file.as_deref()).unwrap_or_else(|e| die(&e)); diff --git a/wai/wai-quantum-cli/src/mcp.rs b/wai/wai-quantum-cli/src/mcp.rs index dac5659e7..0f09611a9 100644 --- a/wai/wai-quantum-cli/src/mcp.rs +++ b/wai/wai-quantum-cli/src/mcp.rs @@ -67,6 +67,7 @@ fn output_of(tool: &str) -> Option<&'static str> { "lint_qasm" => "tools.v1.json#/$defs/lint", "critique_qasm" => "tools.v1.json#/$defs/critique", "check_equivalent" => "tools.v1.json#/$defs/equivalence", + "convert_program" => "tools.v1.json#/$defs/convert", "improve_qasm" => "tools.v1.json#/$defs/improve", "classical_shortcuts" => "tools.v1.json#/$defs/shortcuts", "draw_circuit" => "tools.v1.json#/$defs/draw", @@ -267,6 +268,7 @@ fn core_tools() -> Value { "description": "Whether two programs are the same circuit up to a global phase, and how the engine knows: equal tableaus for Clifford circuits at any size, every basis state to 8 qubits, checked rewrites of one followed by the other's inverse to nothing (a proof at any width), random product states to 22 qubits (evidence, not proof), or unchecked with the reason. Its `verdict` is that decision; `verification` is the level it reached, as every tool's verdict states it.", "inputSchema": { "type": "object", "required": ["a", "b"], "properties": { "a": { "type": "string", "description": "The first program." }, "b": { "type": "string", "description": "The second program." }, "inputs": inputs_schema() } }, }, + wai_quantum::engine::formats::tool(), { "name": "improve_qasm", "title": "Improve an OpenQASM program", @@ -475,6 +477,7 @@ fn call(name: &str, args: &Value, memory_gb: u64, key: &SigningKey, client: &Cli let raw = args; // The debugger reads a generator at its inputs itself, so its lines are the generator's. match name { + "convert_program" => return wai_quantum::engine::formats::convert_json(raw), "debug_step" => return wai_quantum::engine::debug::call("step", raw), "debug_branches" => return wai_quantum::engine::debug::call("branches", raw), _ => {} @@ -855,7 +858,7 @@ mod tests { let list = handle(&json!({ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }), 1, &key(), &mut Client::default()).unwrap(); let names: Vec<&str> = list["result"]["tools"].as_array().unwrap().iter().map(|t| t["name"].as_str().unwrap()).collect(); #[allow(unused_mut)] - let mut want = vec!["run_quantum_circuit", "plan_quantum_circuit", "lint_qasm", "repair_qasm", "critique_qasm", "check_equivalent", "improve_qasm", "classical_shortcuts", "compile_qasm", "estimate_resources", "draw_circuit", "debug_step", "debug_branches", "verify_quantum_result", "discover_evaluate", "discover_submit", "discover_archive", "novelty_qasm", "discover_novelty", "rewrite_qasm", "converge_cohort", "converge_history", "study_measure", "study_cohort", "taste_rank", "taste_next", "history_commit", "history_changes", "history_blame", "history_log", "list_methods", "qasm_diagnostics", "list_targets", "show_target", "run_on_target", "qec_code", "qec_decode", "qec_threshold", "pulse_schedule", "pulse_check"]; + let mut want = vec!["run_quantum_circuit", "plan_quantum_circuit", "lint_qasm", "repair_qasm", "critique_qasm", "check_equivalent", "convert_program", "improve_qasm", "classical_shortcuts", "compile_qasm", "estimate_resources", "draw_circuit", "debug_step", "debug_branches", "verify_quantum_result", "discover_evaluate", "discover_submit", "discover_archive", "novelty_qasm", "discover_novelty", "rewrite_qasm", "converge_cohort", "converge_history", "study_measure", "study_cohort", "taste_rank", "taste_next", "history_commit", "history_changes", "history_blame", "history_log", "list_methods", "qasm_diagnostics", "list_targets", "show_target", "run_on_target", "qec_code", "qec_decode", "qec_threshold", "pulse_schedule", "pulse_check"]; #[cfg(feature = "providers")] want.extend(["providers_list", "provider_estimate", "run_on_provider"]); assert_eq!(names, want); @@ -1304,6 +1307,10 @@ mod tests { call_tool("critique_qasm", json!({ "qasm": draft }), false, &mut called, &mut hold); call_tool("check_equivalent", json!({ "a": draft, "b": "OPENQASM 3.0;\nqubit[2] q;\ncx q[0], q[1];\nt q[1];\n" }), false, &mut called, &mut hold); call_tool("improve_qasm", json!({ "qasm": draft }), false, &mut called, &mut hold); + let qir = call_tool("convert_program", json!({ "program": draft, "to": "qir" }), false, &mut called, &mut hold); + let jeff = call_tool("convert_program", json!({ "program": qir["program"], "to": "jeff" }), false, &mut called, &mut hold); + call_tool("convert_program", json!({ "program_base64": jeff["program_base64"], "to": "qasm" }), false, &mut called, &mut hold); + call_tool("convert_program", json!({ "program": "define void @main() {\nentry:\n call void @__quantum__qis__foo__body(ptr null)\n ret void\n}\n", "from": "qir", "to": "qasm" }), true, &mut called, &mut hold); call_tool("classical_shortcuts", json!({ "qasm": draft }), false, &mut called, &mut hold); call_tool("compile_qasm", json!({ "qasm": "OPENQASM 3.0;\nqubit[2] q;\nh q[0];\nrz(0.3) q[0];\ncx q[0], q[1];\n", "epsilon": 1e-3 }), false, &mut called, &mut hold); call_tool("compile_qasm", json!({ "qasm": draft, "basis": "standard", "level": 2, "stages": true }), false, &mut called, &mut hold); diff --git a/wai/wai-quantum-service/schemas/service-tools.v1.json b/wai/wai-quantum-service/schemas/service-tools.v1.json index 1c6227399..f629f3cf5 100644 --- a/wai/wai-quantum-service/schemas/service-tools.v1.json +++ b/wai/wai-quantum-service/schemas/service-tools.v1.json @@ -82,6 +82,16 @@ } ] }, + "convert": { + "anyOf": [ + { + "$ref": "https://git.transaction.science/Transaction-Science/open-standards/raw/branch/main/wai/wai-quantum/schemas/tools.v1.json#/$defs/convert" + }, + { + "$ref": "#/$defs/refused" + } + ] + }, "improve": { "anyOf": [ { diff --git a/wai/wai-quantum-service/smoke.sh b/wai/wai-quantum-service/smoke.sh index 950de0600..b6d5dce2c 100644 --- a/wai/wai-quantum-service/smoke.sh +++ b/wai/wai-quantum-service/smoke.sh @@ -344,6 +344,14 @@ py 'import json,sys; c=json.load(open(sys.argv[1]))["candidates"][0]; assert c[" py 'import json; print(json.dumps({"a": "OPENQASM 3.0;\nqubit[2] q;\nh q[0];\nh q[0];\ncx q[0], q[1];\n", "b": "OPENQASM 3.0;\nqubit[2] q;\ncx q[0], q[1];\n"}))' > "$tmp/pair.json" post /v1/equivalent "$tmp/pair.json" > "$tmp/equivalent.out" py 'import json,sys; d=json.load(open(sys.argv[1])); assert d["verdict"]=="equivalent" and d["how"]=="tableau", d; print("equivalent: ok, by tableau")' "$tmp/equivalent.out" +# A program carried to QIR and back, each conversion checked, and a job +# that carries its program as QIR. +py 'import json; print(json.dumps({"program": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", "to": "qir"}))' > "$tmp/convert.json" +post /v1/convert "$tmp/convert.json" > "$tmp/convert.out" +py 'import json,sys; d=json.load(open(sys.argv[1])); assert d["ok"] and d["to"]=="qir" and d["written"]["profile"]=="base" and d["check"]["verdict"]["verdict"]=="equivalent", d; print("convert: ok, to QIR,", d["check"]["verdict"]["how"])' "$tmp/convert.out" +py 'import json,sys; d=json.load(open(sys.argv[1])); print(json.dumps({"source": {"format": "qir", "program": d["program"]}, "shots": 50}))' "$tmp/convert.out" > "$tmp/fromqir.json" +post /v1/run "$tmp/fromqir.json" > "$tmp/fromqir.out" +py 'import json,sys; d=json.load(open(sys.argv[1])); r=d["result"]; assert d["status"]=="done" and set(r["counts"]) <= {"00","11"} and d["request"]["source"]["format"]=="qir", d; print("a job from QIR: ok,", r["method"])' "$tmp/fromqir.out" py 'import json; print(json.dumps({"qasm": "qubit[1] q;", "thorough": True}))' > "$tmp/thorough.json" code=$(curl -sS -o "$tmp/thorough.out" -w '%{http_code}' -X POST "$U/v1/shortcuts" -H 'content-type: application/json' --data-binary "@$tmp/thorough.json") [ "$code" = 422 ] || { echo "a thorough search got HTTP $code"; exit 1; } diff --git a/wai/wai-quantum-service/src/analysis.rs b/wai/wai-quantum-service/src/analysis.rs index 7dcf4be0d..ab7f3d061 100644 --- a/wai/wai-quantum-service/src/analysis.rs +++ b/wai/wai-quantum-service/src/analysis.rs @@ -25,7 +25,10 @@ //! ([`intent_bounds`]), so a check that would take long is refused before //! the job is queued, with the command that runs it locally; //! - a repair ([`Check::Repair`]) is bounded by the program's size: it reads -//! the program once a repair, at most `engine::repair::MAX_ROUNDS` times. +//! the program once a repair, at most `engine::repair::MAX_ROUNDS` times; +//! - a conversion between formats ([`Check::Convert`]) reads at most +//! [`CONVERT_MAX_BYTES`], and its check is held to the equivalence check's +//! bound. //! //! Every refusal ends with its code: J031 for what is larger than the //! service takes, J032 when every check is running. @@ -71,6 +74,7 @@ pub enum Check { Shortcuts, Compile, Repair, + Convert, } fn program<'a>(body: &'a Value, key: &str) -> Result<&'a str, (u16, String)> { @@ -283,9 +287,44 @@ pub fn bounds(check: Check, body: &Value) -> Result<(), (u16, String)> { } Check::Compile => compile_bounds(program(body, "qasm")?, body), Check::Repair => program(body, "qasm").map(|_| ()), + Check::Convert => convert_bounds(body), } } +/// The most bytes of a program a conversion here reads. +pub const CONVERT_MAX_BYTES: usize = 4 << 20; + +/// Whether the service converts `body`: a program within +/// [`CONVERT_MAX_BYTES`], whose check (the output read back and held to it) +/// is within the equivalence check's bound. The conversion's own refusals +/// are the engine's, in its answer. +pub fn convert_bounds(body: &Value) -> Result<(), (u16, String)> { + let local = "run `wai-quantum convert` on your machine"; + let size = body.get("program").and_then(Value::as_str).map_or(0, str::len) + body.get("program_base64").and_then(Value::as_str).map_or(0, |b| b.len() / 4 * 3); + if size > CONVERT_MAX_BYTES { + return Err((422, tag(refusal::TOO_LARGE_HERE, format!("a program of {size} bytes, more than a conversion here reads ({CONVERT_MAX_BYTES}); {local}")))); + } + if body.get("check") == Some(&Value::Bool(false)) { + return Ok(()); + } + // The check runs the equivalence ladder on the program read, twice at most. + let Ok(bytes) = (match (body.get("program").and_then(Value::as_str), body.get("program_base64").and_then(Value::as_str)) { + (Some(t), None) => Ok(t.as_bytes().to_vec()), + (None, Some(b)) => wai_quantum::engine::formats::decode_base64(b), + _ => Err(String::new()), + }) else { + return Ok(()); + }; + let from = body.get("from").and_then(Value::as_str).and_then(wai_quantum::engine::formats::Format::parse).unwrap_or_else(|| wai_quantum::engine::formats::Format::sniff(&bytes)); + if let Ok(r) = wai_quantum::engine::formats::read(from, &bytes) { + let work = 2.0 * equivalence_work(&r.qasm, &r.qasm); + if work > EQUIV_MAX_WORK { + return Err((422, tag(refusal::TOO_LARGE_HERE, format!("the conversion's check would apply about {work:.1e} gate amplitudes, more than the service takes ({EQUIV_MAX_WORK:.0e}); {local}, or ask with \"check\": false")))); + } + } + Ok(()) +} + /// Whether the service checks a job's intents beside its run: each /// reference within the equivalence check's bound, or the reason it does /// not (422, with the command that runs it locally). @@ -315,6 +354,12 @@ pub fn answer(check: Check, body: &Value) -> Value { Check::Improve => critique::variants(s("qasm")), Check::Shortcuts => shortcuts::shortcuts(s("qasm"), false), Check::Compile => compile::call(body).unwrap_or_else(|e| serde_json::json!({ "ok": false, "error": e })), + // A refusal as data, as both servers give one: its message and codes. + Check::Convert => wai_quantum::engine::formats::convert_json(body).unwrap_or_else(|e| { + let mut r = refusal::refusal_json(&e); + r["error"] = serde_json::json!(e); + r + }), Check::Repair => { let mut opts = body.clone(); if let Some(o) = opts.as_object_mut() { diff --git a/wai/wai-quantum-service/src/discovery.rs b/wai/wai-quantum-service/src/discovery.rs index 9e1b5868e..a89433126 100644 --- a/wai/wai-quantum-service/src/discovery.rs +++ b/wai/wai-quantum-service/src/discovery.rs @@ -286,6 +286,11 @@ pub fn openapi(server: &str) -> Value { "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["a", "b"], "properties": { "a": { "type": "string" }, "b": { "type": "string" } } } } } }, "responses": { "200": { "description": "{\"verdict\": \"equivalent\" | \"different\" | \"unchecked\", \"how\", \"why\"}" }, "422": { "description": "A check longer than the service takes; the reason names the command that runs it locally" }, "429": { "description": "Past the per-client limits" }, "503": { "description": "Every check is running" } } } }, + "/v1/convert": { "post": { + "summary": "A program carried between formats (OpenQASM 3, QIR as LLVM text, jeff as base64), the output read back and held to it by the equivalence ladder", + "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["to"], "properties": { "program": { "type": "string", "description": "The program's text: OpenQASM or QIR." }, "program_base64": { "type": "string", "description": "The program's bytes as base64: jeff." }, "from": { "enum": ["qasm", "qir", "jeff"] }, "to": { "enum": ["qasm", "qir", "jeff"] }, "profile": { "enum": ["base", "adaptive"] }, "check": { "type": "boolean" } } } } } }, + "responses": { "200": { "description": "{\"ok\": true, \"format\", \"from\", \"to\", \"program\" | \"program_base64\", \"qasm\", \"notes\", \"read\", \"written\", \"check\"}, or the refusal {\"error\", \"code\", \"codes\", \"outcome\", \"meaning\"} with the reader's code (Q700 to Q721) and place" }, "422": { "description": "A program or a check larger than the service takes" }, "429": { "description": "Past the per-client limits" }, "503": { "description": "Every check is running" } } + } }, "/v1/improve": { "post": { "summary": "The program with its smells fixed, each candidate checked the same circuit", "requestBody": { "required": true, "content": { "application/json": { "schema": prog.clone() } } }, @@ -448,7 +453,7 @@ mod tests { let d = openapi("https://example.org/q"); - for p in ["/v1", "/v1/methods", "/v1/examples", "/v1/targets", "/v1/run", "/v1/jobs", "/v1/jobs/{id}", "/v1/jobs/{id}/events", "/.well-known/wai-quantum-receipt-keys", "/v1/verify", "/v1/compile", "/v1/estimate", "/v1/qec/code", "/v1/qec/decode", "/v1/qec/threshold", "/v1/pulse/schedule", "/v1/pulse/check", "/mcp"] { + for p in ["/v1", "/v1/methods", "/v1/examples", "/v1/targets", "/v1/run", "/v1/jobs", "/v1/jobs/{id}", "/v1/jobs/{id}/events", "/.well-known/wai-quantum-receipt-keys", "/v1/verify", "/v1/compile", "/v1/estimate", "/v1/convert", "/v1/qec/code", "/v1/qec/decode", "/v1/qec/threshold", "/v1/pulse/schedule", "/v1/pulse/check", "/mcp"] { assert!(d["paths"].get(p).is_some(), "{p}"); } assert_eq!(d["openapi"], "3.1.0"); diff --git a/wai/wai-quantum-service/src/http.rs b/wai/wai-quantum-service/src/http.rs index 9e2a743b7..8f9bbd4fa 100644 --- a/wai/wai-quantum-service/src/http.rs +++ b/wai/wai-quantum-service/src/http.rs @@ -29,6 +29,8 @@ //! POST /v1/repair the program repaired on the lines its diagnostics //! flag alone, a proposed repair held to them, and //! what needs a person +//! POST /v1/convert the program carried between OpenQASM, QIR and +//! jeff, the output read back and held to it //! POST /v1/qec/code an error-correcting code, its distance certified //! POST /v1/qec/decode a syndrome, or a circuit's detectors, decoded //! POST /v1/qec/threshold logical failure rates over distances and rates, @@ -208,7 +210,7 @@ async fn describe(State(app): State) -> Response { "receipt_key": app.signer.public_key(), "receipt_keys": format!("{}/.well-known/wai-quantum-receipt-keys", app.base), "api_keys": app.keys.is_some(), - "endpoints": ["GET /v1", "GET /v1/health", "GET /v1/methods", "GET /v1/examples", "GET /v1/targets", "GET /v1/openapi.json", "GET /.well-known/wai-quantum-receipt-keys", "POST /v1/jobs", "GET /v1/jobs/{id}", "GET /v1/jobs/{id}/events", "DELETE /v1/jobs/{id}", "POST /v1/run", "POST /v1/verify", "POST /v1/critique", "POST /v1/equivalent", "POST /v1/improve", "POST /v1/shortcuts", "POST /v1/compile", "POST /v1/estimate", "POST /v1/repair", "POST /v1/qec/code", "POST /v1/qec/decode", "POST /v1/qec/threshold", "POST /mcp"], + "endpoints": ["GET /v1", "GET /v1/health", "GET /v1/methods", "GET /v1/examples", "GET /v1/targets", "GET /v1/openapi.json", "GET /.well-known/wai-quantum-receipt-keys", "POST /v1/jobs", "GET /v1/jobs/{id}", "GET /v1/jobs/{id}/events", "DELETE /v1/jobs/{id}", "POST /v1/run", "POST /v1/verify", "POST /v1/critique", "POST /v1/equivalent", "POST /v1/improve", "POST /v1/shortcuts", "POST /v1/compile", "POST /v1/estimate", "POST /v1/repair", "POST /v1/convert", "POST /v1/qec/code", "POST /v1/qec/decode", "POST /v1/qec/threshold", "POST /mcp"], "mcp": format!("{}/mcp", app.base), "console": app.console, }), @@ -270,10 +272,21 @@ pub(crate) fn plan_job(app: &App, request: &Value) -> Result<(catalog::Plan, Wor /// Plans and admits `request` for `who`: the job's id, or the refusal's /// status and reason. pub(crate) fn admit(app: &App, who: &str, request: Value) -> Result { + let request = with_source(request)?; let (plan, work) = plan_job(app, &request)?; app.service.submit_work(who, request, plan, work).map_err(|Refused(code, why)| (code, why)) } +/// A job that carries its program in another format (`"source"`), with its +/// `"qasm"` read from it (`engine::formats::expand`); any other as it is. +pub(crate) fn with_source(request: Value) -> Result { + match wai_quantum::engine::formats::expand(&request) { + Ok(Some(r)) => Ok(r), + Ok(None) => Ok(request), + Err(e) => Err((400, e)), + } +} + /// Whether a request asks only for its plan. pub(crate) fn is_dry_run(request: &Value) -> bool { request.get("dry_run") == Some(&Value::Bool(true)) @@ -285,6 +298,8 @@ pub(crate) fn is_dry_run(request: &Value) -> bool { /// the refusal it would give. It counts as one submission, as a check does. pub(crate) fn dry_run(app: &App, who: &str, request: &Value) -> Result { app.service.count(who).map_err(|Refused(code, why)| (code, why))?; + let expanded = with_source(request.clone())?; + let request = &expanded; let window = app.service.window(who); let (active, held) = app.service.holding(who); let mut out = json!({ @@ -774,6 +789,7 @@ pub fn router(app: Shared) -> Router { .route("/v1/pulse/check", post(|State(a): State, ConnectInfo(p): ConnectInfo, h: HeaderMap, b| pulse_route(a, p, h, b, "check"))) .route("/v1/estimate", post(estimate_route)) .route("/v1/repair", post(|State(a): State, ConnectInfo(p): ConnectInfo, h: HeaderMap, b| analysis(a, p, h, b, crate::analysis::Check::Repair))) + .route("/v1/convert", post(|State(a): State, ConnectInfo(p): ConnectInfo, h: HeaderMap, b| analysis(a, p, h, b, crate::analysis::Check::Convert))) .route("/v1/examples", get(examples)) .route("/v1/targets", get(targets)) .route("/v1/openapi.json", get(openapi)) diff --git a/wai/wai-quantum-service/src/mcp.rs b/wai/wai-quantum-service/src/mcp.rs index 7da681260..2239379cb 100644 --- a/wai/wai-quantum-service/src/mcp.rs +++ b/wai/wai-quantum-service/src/mcp.rs @@ -63,6 +63,7 @@ fn output_of(tool: &str) -> Option<&'static str> { "verify_quantum_receipt" => "service-tools.v1.json#/$defs/verify", "critique_qasm" => "service-tools.v1.json#/$defs/critique", "check_equivalent" => "service-tools.v1.json#/$defs/equivalence", + "convert_program" => "service-tools.v1.json#/$defs/convert", "improve_qasm" => "service-tools.v1.json#/$defs/improve", "classical_shortcuts" => "service-tools.v1.json#/$defs/shortcuts", "quantum_service_info" => "service-tools.v1.json#/$defs/info", @@ -377,6 +378,12 @@ fn tool_list() -> Value { } ]); if let Some(a) = list.as_array_mut() { + // The conversion between formats, the command line's server's tool, after the equivalence check. + let mut t = wai_quantum::engine::formats::tool(); + t["annotations"] = json!({ "readOnlyHint": true }); + t["description"] = json!(format!("{} A conversion larger than the service takes is refused with the command that runs it locally.", t["description"].as_str().unwrap_or(""))); + let at = a.iter().position(|t| t["name"] == "check_equivalent").map_or(a.len(), |i| i + 1); + a.insert(at, t); a.extend(wai_quantum::engine::qec::tools("the service's key").as_array().cloned().unwrap_or_default()); a.extend(wai_quantum::engine::pulse::tools("the service's key").as_array().cloned().unwrap_or_default()); } @@ -567,7 +574,10 @@ async fn call(app: &App, who: &str, name: &str, args: Value, base: &str) -> Valu "run_quantum_set" if !wai_quantum::engine::catalog::is_set(&args) => tool_result("refused: a set gives \"programs\" (a list of programs or jobs) or \"qasm\" with a \"sweep\" [J007]".into(), json!({ "error": "a set gives \"programs\" or a \"sweep\" [J007]" }), true), "run_quantum_circuit" | "run_on_target" | "run_quantum_set" => { // Every run an agent asks for states its verdict. - let mut args = args; + let mut args = match crate::http::with_source(args) { + Ok(a) => a, + Err((_, why)) => return tool_result(format!("refused: {why}"), json!({ "error": why }), true), + }; if !verdict::asked(&args) { args["verdict"] = json!(true); } @@ -738,18 +748,19 @@ async fn call(app: &App, who: &str, name: &str, args: Value, base: &str) -> Valu } Err((_, why)) => tool_result(format!("refused: {why}"), json!({ "error": why }), true), }, - "critique_qasm" | "check_equivalent" | "improve_qasm" | "classical_shortcuts" => { + "critique_qasm" | "check_equivalent" | "improve_qasm" | "classical_shortcuts" | "convert_program" => { use crate::analysis::Check; let check = match name { "critique_qasm" => Check::Critique, "check_equivalent" => Check::Equivalent, "improve_qasm" => Check::Improve, + "convert_program" => Check::Convert, _ => Check::Shortcuts, }; match crate::analysis::analyse(app, who, check, args).await { Ok(v) => { let text = v["summary"].as_str().map(str::to_string).unwrap_or_else(|| v.to_string()); - let failed = v["ok"] == false; + let failed = v["ok"] == false || (v["ok"].is_null() && v["error"].is_string()); tool_result(text, v, failed) } Err((_, why)) => tool_result(format!("refused: {why}"), json!({ "error": why }), true), @@ -807,7 +818,7 @@ mod tests { #[test] fn the_tools_are_these() { let names: Vec = tools().as_array().unwrap().iter().map(|t| t["name"].as_str().unwrap().to_string()).collect(); - assert_eq!(names, ["run_quantum_circuit", "run_quantum_set", "dry_run_quantum_job", "get_quantum_job", "verify_quantum_receipt", "critique_qasm", "check_equivalent", "improve_qasm", "classical_shortcuts", "compile_qasm", "estimate_resources", "repair_qasm", "quantum_service_info", "qasm_diagnostics", "list_targets", "show_target", "run_on_target", "quantum_circuit_examples", "qec_code", "qec_decode", "qec_threshold", "pulse_schedule", "pulse_check"]); + assert_eq!(names, ["run_quantum_circuit", "run_quantum_set", "dry_run_quantum_job", "get_quantum_job", "verify_quantum_receipt", "critique_qasm", "check_equivalent", "convert_program", "improve_qasm", "classical_shortcuts", "compile_qasm", "estimate_resources", "repair_qasm", "quantum_service_info", "qasm_diagnostics", "list_targets", "show_target", "run_on_target", "quantum_circuit_examples", "qec_code", "qec_decode", "qec_threshold", "pulse_schedule", "pulse_check"]); let run = &tools()[0]["inputSchema"]["properties"]["compile"]["properties"]; assert!(run.get("epsilon").is_some() && run.get("basis").is_some() && run.get("target").is_none(), "the compile's gate set is its basis; target is a device: {run}"); } @@ -822,7 +833,7 @@ mod tests { } let all = tools(); let names: Vec<&str> = all.as_array().unwrap().iter().map(|t| t["name"].as_str().unwrap()).collect(); - assert_eq!(names, ["run_quantum_circuit", "run_quantum_set", "dry_run_quantum_job", "get_quantum_job", "verify_quantum_receipt", "critique_qasm", "check_equivalent", "improve_qasm", "classical_shortcuts", "compile_qasm", "estimate_resources", "repair_qasm", "quantum_service_info", "qasm_diagnostics", "list_targets", "show_target", "run_on_target", "quantum_circuit_examples", "qec_code", "qec_decode", "qec_threshold", "pulse_schedule", "pulse_check"]); + assert_eq!(names, ["run_quantum_circuit", "run_quantum_set", "dry_run_quantum_job", "get_quantum_job", "verify_quantum_receipt", "critique_qasm", "check_equivalent", "convert_program", "improve_qasm", "classical_shortcuts", "compile_qasm", "estimate_resources", "repair_qasm", "quantum_service_info", "qasm_diagnostics", "list_targets", "show_target", "run_on_target", "quantum_circuit_examples", "qec_code", "qec_decode", "qec_threshold", "pulse_schedule", "pulse_check"]); // Each declares its output, versioned; a tool that reads a program requires its verdict. for t in all.as_array().unwrap() { let (name, o) = (t["name"].as_str().unwrap(), &t["outputSchema"]); @@ -951,6 +962,9 @@ mod tests { ("critique_qasm", json!({ "qasm": bell }), false), ("critique_qasm", json!({}), true), ("check_equivalent", json!({ "a": bell, "b": "OPENQASM 3.0;\nqubit[2] q;\ncx q[0], q[1];\n" }), false), + ("convert_program", json!({ "program": bell, "to": "qir" }), false), + ("convert_program", json!({ "program": bell, "to": "jeff", "check": false }), false), + ("convert_program", json!({ "program": "define void @main() {\nentry:\n call void @__quantum__qis__foo__body(ptr null)\n ret void\n}\n", "from": "qir", "to": "qasm" }), true), ("improve_qasm", json!({ "qasm": bell }), false), ("classical_shortcuts", json!({ "qasm": bell }), false), ("classical_shortcuts", json!({ "qasm": bell, "thorough": true }), true), diff --git a/wai/wai-quantum/schemas/job-request.v1.json b/wai/wai-quantum/schemas/job-request.v1.json index 8fc6d70c0..ee8ae8154 100644 --- a/wai/wai-quantum/schemas/job-request.v1.json +++ b/wai/wai-quantum/schemas/job-request.v1.json @@ -14,6 +14,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -21,6 +26,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, diff --git a/wai/wai-quantum/schemas/tools.v1.json b/wai/wai-quantum/schemas/tools.v1.json index b3d5c3ff5..77542fdcd 100644 --- a/wai/wai-quantum/schemas/tools.v1.json +++ b/wai/wai-quantum/schemas/tools.v1.json @@ -2095,6 +2095,138 @@ "type": "integer" } } + }, + "convert": { + "type": "object", + "description": "A program carried from one format to another (OpenQASM 3, QIR as LLVM text, jeff as its Cap'n Proto message) and checked (engine::formats::convert_json): the program written, the program as read in OpenQASM 3, the readers' and writer's notes with their codes, what was read and written, and the check that holds the output to the input.", + "required": [ + "ok", + "format", + "from", + "to", + "qasm", + "notes", + "read", + "written", + "check" + ], + "properties": { + "ok": { + "const": true + }, + "format": { + "const": "wai.quantum.convert/1" + }, + "from": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "to": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program written, for a text format (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program written, for a binary format (jeff), as standard base64." + }, + "qasm": { + "type": "string", + "description": "The program as read, as OpenQASM 3: what every other tool takes." + }, + "notes": { + "type": "array", + "items": { + "$ref": "#/$defs/diagnostic" + }, + "description": "Each warning and note, with its code (Q7xx for the formats) and, for QIR, its line." + }, + "read": { + "type": "object", + "required": [ + "format" + ], + "description": "What was read: the format, its version, a QIR module's profile, entry point, qubits, results and declared capabilities, a jeff module's tool, entry point, qubits and bits." + }, + "written": { + "type": "object", + "required": [ + "format" + ], + "description": "What was written: the format, its version, and QIR's profile." + }, + "check": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "object", + "required": [ + "what", + "verdict" + ], + "properties": { + "what": { + "type": "string", + "description": "What was held to what." + }, + "verdict": { + "type": "object", + "required": [ + "verdict", + "how", + "why" + ], + "properties": { + "verdict": { + "enum": [ + "equivalent", + "different", + "unchecked" + ] + }, + "how": { + "enum": [ + "tableau", + "exact", + "rewrites", + "random", + "state", + "shape", + "steps", + "distribution", + "identity", + "none" + ], + "description": "The equivalence ladder's check (tableau, exact and rewrites are proofs; random is evidence); steps: the same steps one for one; distribution: every outcome's probability the same (evidence); identity: OpenQASM passed through; none: unchecked." + }, + "why": { + "type": "string" + }, + "global_phase": { + "type": [ + "number", + "null" + ] + } + } + } + } + } + ], + "description": "The output read back and held to the program read (or, for a program read into OpenQASM, written back to its format and read again), or null when no check was asked." + } + } } } } diff --git a/wai/wai-quantum/src/engine/formats/mod.rs b/wai/wai-quantum/src/engine/formats/mod.rs index 179862e41..d788876e5 100644 --- a/wai/wai-quantum/src/engine/formats/mod.rs +++ b/wai/wai-quantum/src/engine/formats/mod.rs @@ -373,9 +373,9 @@ fn notes_json(notes: &[Diagnostic]) -> Value { /// A conversion as JSON: the request is `{"to", "program" | "program_base64", /// "from"?, "profile"?, "check"?}` (`from` is found from the bytes when it is -/// not given; `check` is on unless false), and the answer `{"format", "from", -/// "to", "program" | "program_base64", "qasm", "notes", "read", "written", -/// "check"}`. A refusal is the reader's or writer's diagnostic, with its code +/// not given; `check` is on unless false), and the answer `{"ok", "format", +/// "from", "to", "program" | "program_base64", "qasm", "notes", "read", +/// "written", "check"}`. A refusal is the reader's or writer's diagnostic, with its code /// last (`… [Q704]`), or the job layer's for a request that does not read. pub fn convert_json(req: &Value) -> Result { use crate::engine::refusal::{BAD_FIELD, tag}; @@ -397,6 +397,7 @@ pub fn convert_json(req: &Value) -> Result { }; let c = convert(from, to, &bytes, profile, check).map_err(String::from)?; let mut out = json!({ + "ok": true, "format": FORMAT, "from": c.from.name(), "to": c.to.name(), @@ -414,6 +415,28 @@ pub fn convert_json(req: &Value) -> Result { Ok(out) } +/// The MCP tool both servers serve the conversion as (`convert_program`), +/// its answer [`convert_json`]'s. +pub fn tool() -> Value { + json!({ + "name": "convert_program", + "title": "Convert a program between formats", + "description": "Carry a program between OpenQASM 3, QIR (LLVM text, read under its base and adaptive profiles, written as QIR 2.0) and jeff (its Cap'n Proto message, as base64). The answer is the program written, the program as OpenQASM 3 (what every other tool takes), each note with its code (a rotation at an arbitrary angle, a custom gate read by its name, what a format leaves out), what was read and written, and the check: the output read back and held to the program by the equivalence ladder. A construct a reader cannot hold exactly is refused with its code (Q700 to Q721) and its place: quote the code, and never guess around it.", + "inputSchema": { + "type": "object", + "required": ["to"], + "properties": { + "program": { "type": "string", "description": "The program's text: OpenQASM or QIR." }, + "program_base64": { "type": "string", "description": "The program's bytes as base64: jeff." }, + "from": { "type": "string", "enum": ["qasm", "qir", "jeff"], "description": "Its format; found from its bytes when not given." }, + "to": { "type": "string", "enum": ["qasm", "qir", "jeff"], "description": "The format to write." }, + "profile": { "type": "string", "enum": ["base", "adaptive"], "description": "QIR's profile to write: by default base when the program's measurements all come at the end, else adaptive." }, + "check": { "type": "boolean", "description": "false: write it without the check." } + } + }, + }) +} + /// A job whose program is in another format, with `"qasm"` read from its /// `"source"`: `{"source": {"format", "program" | "program_base64"}}`. A /// request with no `source` is `None`, to be read as it is; one with both diff --git a/wai/wai-web/src/quantum_engine.rs b/wai/wai-web/src/quantum_engine.rs index 9513ee879..3020d7659 100644 --- a/wai/wai-web/src/quantum_engine.rs +++ b/wai/wai-web/src/quantum_engine.rs @@ -204,6 +204,24 @@ pub fn engine_equivalent(a: &str, b: &str) -> String { equiv::equivalent(a, b).to_string() } +/// A program carried between formats, OpenQASM 3, QIR (LLVM text) and jeff +/// (as base64), and checked: what is written is read back and held to the +/// program by the equivalence ladder +/// ([`wai::engine::formats::convert_json`]). `request` is +/// `{"to", "program" | "program_base64", "from"?, "profile"?, "check"?}`; a +/// refusal is `{"error", "code", "codes", "outcome", "meaning"}`, its code the +/// reader's (Q700 to Q721) and the place in its message. The Studio opens a +/// QIR or jeff file through it. +#[wasm_bindgen] +pub fn engine_convert(request: &str) -> String { + let answer = serde_json::from_str::(request).map_err(|e| format!("the request is not JSON: {e}")).and_then(|r| wai::engine::formats::convert_json(&r)); + answer.map(|v| v.to_string()).unwrap_or_else(|e| { + let mut r = refusal::refusal_json(&e); + r["error"] = json!(e); + r.to_string() + }) +} + /// The variants a program's fixes make, each checked equivalent to it /// ([`critique::variants`]). #[wasm_bindgen] @@ -1124,6 +1142,30 @@ mod tests { assert_eq!(body["result"]["counts"], json!({ "00": 50 }), "{body}"); } + /// The QIR the conformance set pins for its Clifford+T program + /// (`wai/quantum-conformance/formats/qasm/clifford_t.qasm`), which every + /// build writes to the same bytes. + pub(super) const CLIFFORD_T: &str = include_str!("../../quantum-conformance/formats/qasm/clifford_t.qasm"); + pub(super) const CLIFFORD_T_QIR: &str = "967659b446471c62697f49df341972a45f47bef59d3b2fe26bfefc6670f6a50d"; + + /// A program carried between every pair of formats in the page, each + /// conversion checked, and a refusal with its code and line. + #[test] + fn a_program_converts_between_formats_in_the_page() { + let qir: Value = serde_json::from_str(&engine_convert(&json!({ "program": CLIFFORD_T, "to": "qir" }).to_string())).unwrap(); + assert_eq!((qir["written"]["profile"].as_str(), qir["check"]["verdict"]["verdict"].as_str()), (Some("base"), Some("equivalent")), "{qir}"); + assert_eq!(blake3::hash(qir["program"].as_str().unwrap().as_bytes()).to_hex().as_str(), CLIFFORD_T_QIR); + let jeff: Value = serde_json::from_str(&engine_convert(&json!({ "program": qir["program"], "to": "jeff" }).to_string())).unwrap(); + assert_eq!((jeff["from"].as_str(), jeff["check"]["verdict"]["verdict"].as_str()), (Some("qir"), Some("equivalent")), "{jeff}"); + let back: Value = serde_json::from_str(&engine_convert(&json!({ "program_base64": jeff["program_base64"], "to": "qasm" }).to_string())).unwrap(); + assert_eq!(back["from"], "jeff", "{back}"); + let e: Value = serde_json::from_str(&engine_equivalent(back["program"].as_str().unwrap(), CLIFFORD_T)).unwrap(); + assert_eq!(e["verdict"], "equivalent", "{e}"); + let r: Value = serde_json::from_str(&engine_convert(&json!({ "program": "define void @main() {\nentry:\n call void @__quantum__qis__foo__body(ptr null)\n ret void\n}\n", "from": "qir", "to": "qasm" }).to_string())).unwrap(); + assert_eq!(r["code"], "Q706", "{r}"); + assert!(r["error"].as_str().unwrap().starts_with("line 3: "), "{r}"); + } + #[test] fn critique_equivalence_and_variants_answer_as_json() { let src = "qubit[2] q;\nh q[0];\nh q[0];\ncx q[0], q[1];\n"; @@ -1261,6 +1303,17 @@ mod wasm_tests { assert_eq!(v["valid"], true, "{v}"); } + /// The conversion the native test makes, to the same QIR bytes. + #[wasm_bindgen_test] + fn a_program_converts_between_formats_in_wasm_to_the_native_bytes() { + use super::tests::{CLIFFORD_T, CLIFFORD_T_QIR}; + let qir: Value = serde_json::from_str(&engine_convert(&json!({ "program": CLIFFORD_T, "to": "qir" }).to_string())).unwrap(); + assert_eq!(qir["check"]["verdict"]["verdict"], "equivalent", "{qir}"); + assert_eq!(blake3::hash(qir["program"].as_str().unwrap().as_bytes()).to_hex().as_str(), CLIFFORD_T_QIR); + let jeff: Value = serde_json::from_str(&engine_convert(&json!({ "program": qir["program"], "to": "jeff" }).to_string())).unwrap(); + assert_eq!(jeff["check"]["verdict"]["verdict"], "equivalent", "{jeff}"); + } + #[wasm_bindgen_test] fn critique_equivalence_and_variants_run_in_wasm() { let c: Value = serde_json::from_str(&engine_critique("qubit[2] q; h q[0]; rz(0.3) q[1]; cx q[0], q[1];")).unwrap(); -- 2.54.0 (Apple Git-157) From 2c6bc9e980c908100abf40574d7a2c5166b0a304 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Fri, 30 Oct 2026 11:05:20 -0400 Subject: [PATCH 04/10] wai-quantum: other formats documented, and a jeff module's gates capped The engine, command line and service READMEs (and their translations) describe the readers and writers, convert, --from, convert_program, POST /v1/convert and a job's source; docs/formats.md, whose commands now run under the documentation check, says what each reader takes; the changelog has the section. A jeff module that unrolls to more gates than the reader holds is refused (Q210) instead of growing without bound. Co-Authored-By: Claude Opus 5.5 --- wai/CHANGELOG.md | 37 ++++++++++++++++++++++ wai/quantum-conformance/README.md | 5 ++- wai/tools/quantum-docs/docs.json | 3 +- wai/wai-quantum-cli/README.md | 13 ++++++++ wai/wai-quantum-cli/README.zh-CN.md | 13 ++++++++ wai/wai-quantum-service/README.md | 6 ++-- wai/wai-quantum/README.md | 4 +-- wai/wai-quantum/README.zh-CN.md | 2 +- wai/wai-quantum/docs/README.zh-CN.md | 1 + wai/wai-quantum/docs/formats.md | 19 +++++------ wai/wai-quantum/src/engine/formats/jeff.rs | 16 +++++++++- 11 files changed, 102 insertions(+), 17 deletions(-) diff --git a/wai/CHANGELOG.md b/wai/CHANGELOG.md index f8d8971c6..cd567f5a4 100644 --- a/wai/CHANGELOG.md +++ b/wai/CHANGELOG.md @@ -178,6 +178,43 @@ wire format may change and such changes are recorded here. implementation, `channel_lineup_verify.py`, from the text alone, agrees on every vector, and a CI step runs both. +### wai-quantum: QIR under its profiles and jeff, read and written, every conversion checked + +- **QIR** (`engine::formats::qir`): a module read as LLVM text, QIR 1.x (typed pointers) and 2.x (opaque pointers), + under the profile its entry point names. Under the base profile a branch, a classical instruction, reading a result, + or a gate or reset after a measurement is refused as what the profile forbids (`Q704`), on its line. Under the + adaptive profile, measurements anywhere and forward branches on results (`__quantum__rt__read_result`, or QIR 1's + `result_equal`) with the `i1` logic the profile allows are read as `if … else`, each branch to the block where its + paths meet; an optional capability the module flags do not declare is forbidden, and one they declare that the reader + does not take (loops, `switch`, `phi`, IR-defined functions, dynamic qubits or results, arrays) is refused as such + (`Q705`). Integer and float computation the flags declare is computed on reading where its operands are constants. + Rotations at any angle are read at their exact doubles, with a note (`Q709`) that the double-precision methods take + them and the bit-exact method does not; a Clifford+T module stays on the bit-exact path. A program is written as QIR + 2.0: the base profile, or the adaptive one (forward branches on results with `read_result` and `br` alone) for a + program that measures in the middle, resets or branches; Clifford+T is written as Clifford+T intrinsics and every + angle as its exact hex double. The crate's bit-exact `quantum_qir` is unchanged. +- **jeff** (`engine::formats::jeff`, format 0.3): read and written through a small Cap'n Proto reader and writer of + the crate's own, with no dependency (segments, far pointers, every element size, each pointer bounds-checked, a limit + on the words visited and the depth). The entry point's dataflow graph is run in an order its values allow: registers + extracted, inserted, sliced, split and joined by index, `for` and `while` decided on reading unrolled, a `switch` on a + measured bit read as a condition, calls inlined, classical values computed the same on every platform. Well-known + gates take their controls after their targets, as the schema orders them; a Pauli-product rotation `exp(iθP)` is + lowered exactly; a custom gate is read only by a standard gate's name (`Q711`). The writer gives one function, every + gate well-known and exact with its global phase, the bits a returned array; a program that branches is refused + (`Q720`). +- **Codes** `Q700` to `Q721` in the reader's table (`docs/qasm-diagnostics.md`): every refusal names its place (a + QIR line, a jeff function and operation). `docs/formats.md` says what each reader takes. +- **Every surface:** `wai-quantum convert FILE --to qasm|qir|jeff [--profile] [-o] [--json]`, each conversion checked + by reading the output back and holding it to the program with the equivalence ladder (a program that branches, step + for step or by every outcome's probability); every verb reads `.ll`, `.qir` and `.jeff` files, or `--from`. Both MCP + servers serve `convert_program` (`tools.v1.json#/$defs/convert`); the job service answers `POST /v1/convert`, and a + job may carry `{"source": {"format", "program" | "program_base64"}}` in place of `qasm` (`job-request.v1.json`). + The Studio's engine has `engine_convert`. +- **Conformance** (`wai/quantum-conformance/formats/`): the QIR specification's two examples, every intrinsic, a QIR 1 + module, each profile's refusals, hand-built jeff modules, the jeff project's own structured benchmarks and examples + unchanged, and OpenQASM written to each format with the QIR bytes pinned, run natively and under wasm32-wasip2 + (`wai-quantum-formats.yml`). `wai-web-wasm.yml`'s pins move to 19 (wasm32) and 27 (native). + ### wai-quantum: pulse and dynamics, simulated, the same bits everywhere, its receipts binding the calibrations - **OpenPulse read** (`qasm::pulse`): a pulse program's `cal` and `defcal` blocks on physical qubits run in diff --git a/wai/quantum-conformance/README.md b/wai/quantum-conformance/README.md index b28ba1d0d..f87283d9e 100644 --- a/wai/quantum-conformance/README.md +++ b/wai/quantum-conformance/README.md @@ -20,7 +20,10 @@ The OpenQASM 3 reader's corpus, programs with what the reader must do with each, is `qasm3/`, with a README of its own. The error-correction text formats (noisy Clifford circuits and detector error models, read and written back byte for byte, with what each must count and what must be refused) are `qec-text/`, -with a README of its own. +with a README of its own. Programs in other formats (QIR under its profiles, +and jeff), each with the program it must read to, its round trip and its +refusals, and OpenQASM written to each, are `formats/`, with a README of its +own. An entry with no `capability` is a `wai.quantum.circuit` vector. The trees are the one reference all three surfaces are checked against, rather than being checked diff --git a/wai/tools/quantum-docs/docs.json b/wai/tools/quantum-docs/docs.json index f604c6f5d..c0ca55d7c 100644 --- a/wai/tools/quantum-docs/docs.json +++ b/wai/tools/quantum-docs/docs.json @@ -1,6 +1,6 @@ { "schema": "wai.quantum.docs-manifest/1", - "about": "The documents check_docs.py runs: the WAI Quantum READMEs and docs pages (engine, command line, service, Python, Studio, desktop, editors, providers, the compiler, estimates, verdicts, error correction, the feature matrix and the version policy; the service's self-hosting, scheduler and provenance pages; the views' engine), the agent skill, the course's lessons, and their translations.", + "about": "The documents check_docs.py runs: the WAI Quantum READMEs and docs pages (engine, command line, service, Python, Studio, desktop, editors, providers, the compiler, estimates, verdicts, error correction, other formats, the feature matrix and the version policy; the service's self-hosting, scheduler and provenance pages; the views' engine), the agent skill, the course's lessons, and their translations.", "docs": [ "wai/wai-quantum/README.md", "wai/wai-quantum/README.zh-CN.md", @@ -10,6 +10,7 @@ "wai/wai-quantum/docs/estimate.md", "wai/wai-quantum/docs/verdicts.md", "wai/wai-quantum/docs/job-refusals.md", + "wai/wai-quantum/docs/formats.md", "wai/wai-quantum/docs/qasm-diagnostics.md", "wai/wai-quantum/docs/qasm-feature-matrix.md", "wai/wai-quantum/docs/qec.md", diff --git a/wai/wai-quantum-cli/README.md b/wai/wai-quantum-cli/README.md index 2c571345b..12c2d149a 100644 --- a/wai/wai-quantum-cli/README.md +++ b/wai/wai-quantum-cli/README.md @@ -222,6 +222,18 @@ The critique's figures are computed, not estimated: gates by kind, two-qubit gat A cheap route proves a design classically easy. None being cheap proves nothing about hardness: other methods, approximate ones above all, may cost far less. +## Other formats + +```sh runnable +wai-quantum convert prog.qasm --to qir > prog.ll # QIR 2.0, the base profile (adaptive for a program that branches) +wai-quantum convert prog.ll --to jeff -o prog.jeff # jeff; the format read is found from the file +wai-quantum convert prog.jeff --to qasm # back to OpenQASM 3 +wai-quantum run prog.ll --shots 100 # every verb reads QIR (.ll, .qir) and jeff (.jeff) +wai-quantum equivalent prog.jeff prog.qasm # exit 0: the same circuit +``` + +`convert` carries a program between OpenQASM 3, QIR (LLVM text) and jeff, and checks every conversion: what it writes is read back and held to the program by the equivalence check, and `check:` on standard error says how (`--no-check` skips it; `--json` gives everything as data, jeff as base64). QIR is read under the profile its entry point names, base or adaptive, and what the profile forbids is refused with its line; `--profile` chooses the profile QIR is written under. A rotation at any angle is read at its exact double, and a note says the bit-exact method refuses it. Every other verb reads QIR and jeff by the file's extension, or by `--from qir` or `--from jeff`, the reader's notes on standard error. A construct a reader cannot hold exactly is refused with a code from `Q700` and its place; `wai-quantum diagnostics` lists them, and [the formats page](../wai-quantum/docs/formats.md) says what each reader takes. + ## Your own hardware Bring your own quantum: your device, your account with a provider, or a simulator you trust, listed in your registry with its credential in the operating system's keychain ([the guide](../wai-quantum-qpu/bring-your-own-quantum.md), [the protocol](../extensions/quantum-providers.md)). @@ -354,6 +366,7 @@ wai-quantum verify sweep.json # the set's receipt, each item's, the r - `repair_qasm` (verdicts and repairs, above) - `critique_qasm` - `check_equivalent` +- `convert_program` - `improve_qasm` - `classical_shortcuts` - `compile_qasm` (compiling, above) diff --git a/wai/wai-quantum-cli/README.zh-CN.md b/wai/wai-quantum-cli/README.zh-CN.md index 4c89b3d9b..52a47fe88 100644 --- a/wai/wai-quantum-cli/README.zh-CN.md +++ b/wai/wai-quantum-cli/README.zh-CN.md @@ -220,6 +220,18 @@ wai-quantum shortcuts prog.qasm # 模拟它的每一条精确经典 存在一条便宜的途径,就证明该设计在经典上是容易的。没有一条途径便宜,并不能证明任何关于其困难性的结论:其他方法,尤其是近似方法,代价可能低得多。 +## 其他格式 + +```sh runnable +wai-quantum convert prog.qasm --to qir > prog.ll # QIR 2.0,基础配置文件(分支的程序则用自适应配置文件) +wai-quantum convert prog.ll --to jeff -o prog.jeff # jeff;所读格式由文件判断 +wai-quantum convert prog.jeff --to qasm # 转回 OpenQASM 3 +wai-quantum run prog.ll --shots 100 # 每个命令都能读取 QIR(.ll、.qir)和 jeff(.jeff) +wai-quantum equivalent prog.jeff prog.qasm # 退出状态 0:同一条线路 +``` + +`convert` 在 OpenQASM 3、QIR(LLVM 文本)和 jeff 之间转换程序,并检查每一次转换:写出的结果会被读回,并由等价检查与原程序比对,标准错误上的 `check:` 说明是如何判定的(`--no-check` 跳过检查;`--json` 以数据形式给出全部内容,jeff 以 base64 给出)。QIR 按其入口点所声明的配置文件(base 或 adaptive)读取,配置文件禁止的内容按行拒绝;`--profile` 选择写出 QIR 时所用的配置文件。任意角度的旋转按其精确的双精度值读取,并附注说明逐位精确的方法不接受它。其他每个命令都按文件扩展名读取 QIR 和 jeff,或按 `--from qir`、`--from jeff` 读取,读取器的说明输出到标准错误。读取器无法精确表示的构造会以 `Q700` 起的代码及其位置拒绝;`wai-quantum diagnostics` 列出这些代码,[格式说明页](../wai-quantum/docs/formats.md)说明每个读取器接受什么。 + ## 你自己的硬件 自带你的量子资源:你的设备、你在某个提供方处的账户,或你信任的模拟器,登记在你的注册表中,其凭据存放在操作系统的钥匙串里([指南](../wai-quantum-qpu/bring-your-own-quantum.md)、[协议](../extensions/quantum-providers.md))。 @@ -352,6 +364,7 @@ wai-quantum verify sweep.json # 程序集的回执、每一项的回 - `repair_qasm`(判定与修复,见上文) - `critique_qasm` - `check_equivalent` +- `convert_program` - `improve_qasm` - `classical_shortcuts` - `compile_qasm`(编译,见上文) diff --git a/wai/wai-quantum-service/README.md b/wai/wai-quantum-service/README.md index 5cb2203f6..3ab5c7984 100644 --- a/wai/wai-quantum-service/README.md +++ b/wai/wai-quantum-service/README.md @@ -19,7 +19,8 @@ A job is JSON: | field | meaning | |---|---| -| `qasm` | the program: OpenQASM 3, or OpenQASM 2 (required) | +| `qasm` | the program: OpenQASM 3, or OpenQASM 2 (required, unless `source` gives it) | +| `source` | the program in another format, in place of `qasm`: `{"format": "qir", "program": "…"}` (QIR as LLVM text) or `{"format": "jeff", "program_base64": "…"}`; read to the job's `qasm` before anything else reads the job, the request keeping its source, and a module that does not read refused with its code (`Q700` to `Q721`) and place | | `method` | `auto` (the default) or a method below | | `shots` | measurement shots (default 1024 when nothing else is asked for; at most 1,000,000) | | `seed` | the sampling seed (default 1); the same job and seed give the same counts | @@ -146,6 +147,7 @@ Counts are always seeded samples. | `POST /v1/verify` | `{"request": ..., "result": ...}`: whether the result's receipt seals that request and that result | | `POST /v1/critique` | `{"qasm"}`: the program's measures (gates by kind, two-qubit gates, depth, T count, whether it is Clifford, a modelled fidelity) and its smells, each with its lines | | `POST /v1/equivalent` | `{"a", "b"}`: whether two programs are the same circuit up to a global phase, and how that was decided | +| `POST /v1/convert` | `{"to", "program" \| "program_base64", "from"?, "profile"?, "check"?}`: the program carried between OpenQASM 3, QIR and jeff (as base64), with the program as OpenQASM 3, each note with its code, what was read and written, and the check (the output read back and held to the program by the equivalence ladder); a refusal is the reader's, with its code and place ([the formats page](../wai-quantum/docs/formats.md)) | | `POST /v1/improve` | `{"qasm"}`: the program with its smells fixed, each candidate with its measures and checked the same circuit | | `POST /v1/shortcuts` | `{"qasm"}`: each exact classical route to simulating the program, priced on it, and the cheapest for each question | | `POST /v1/compile` | `{"qasm", "epsilon", "level", "basis"}`: the program compiled through checked passes (to Clifford+T within `epsilon`, by default `1e-6`), with a certificate of every pass and the distance, sealed with the service's key in a job's form so `POST /v1/verify` checks it ([the compiler page](../wai-quantum/docs/compiler.md)) | @@ -194,7 +196,7 @@ The six checks answer at once, in the service's own process. Each counts against ## For agents -`POST /mcp` serves the service to an agent as Model Context Protocol tools: `run_quantum_circuit`, `run_quantum_set` (a set or a sweep), `dry_run_quantum_job` (the plan, running nothing), `get_quantum_job`, `verify_quantum_receipt`, `critique_qasm`, `check_equivalent`, `improve_qasm`, `classical_shortcuts`, `compile_qasm`, `estimate_resources`, `repair_qasm`, `quantum_service_info`, `qasm_diagnostics`, `list_targets`, `show_target`, `run_on_target`, `quantum_circuit_examples`, `qec_code`, `qec_decode`, `qec_threshold`, `pulse_schedule` and `pulse_check`. A run it makes asks for its verdict, so the request its receipt seals is the job's own `request`; every answer about a program carries its verdict, every refusal its codes, and every tool a versioned output schema. `verify_quantum_receipt` re-runs an estimate. It also serves the engine's resources and prompts, the same as the command line's own server (`wai-quantum mcp`): +`POST /mcp` serves the service to an agent as Model Context Protocol tools: `run_quantum_circuit`, `run_quantum_set` (a set or a sweep), `dry_run_quantum_job` (the plan, running nothing), `get_quantum_job`, `verify_quantum_receipt`, `critique_qasm`, `check_equivalent`, `convert_program`, `improve_qasm`, `classical_shortcuts`, `compile_qasm`, `estimate_resources`, `repair_qasm`, `quantum_service_info`, `qasm_diagnostics`, `list_targets`, `show_target`, `run_on_target`, `quantum_circuit_examples`, `qec_code`, `qec_decode`, `qec_threshold`, `pulse_schedule` and `pulse_check`. A run it makes asks for its verdict, so the request its receipt seals is the job's own `request`; every answer about a program carries its verdict, every refusal its codes, and every tool a versioned output schema. `verify_quantum_receipt` re-runs an estimate. It also serves the engine's resources and prompts, the same as the command line's own server (`wai-quantum mcp`): - resources: the language the engine reads (`wai-quantum://docs/language`), its diagnostics, the methods with their accuracy classes (their reach in a job's memory here), and worked examples, each with what it must do; - prompts: writing and verifying a circuit for an intent, explaining a result and its receipt, and repairing a program from its diagnostics. Each names this server's own tools. diff --git a/wai/wai-quantum/README.md b/wai/wai-quantum/README.md index bbd60a79a..f242cbd10 100644 --- a/wai/wai-quantum/README.md +++ b/wai/wai-quantum/README.md @@ -69,7 +69,7 @@ never creates information. | `quantum_toolchain` | algorithm library, backend recommendation, **OpenQASM 2/3 import through the one reader** (`from_qasm`): everything the reader takes, under the bit-exact circuit's restriction, dyadic angles and measurements at the end, refused with the reader's codes otherwise (non-dyadic angles are refused, never rounded); a controlled gate the modifiers write is held whole, so `to_qasm`'s output reads back gate for gate; **device-ready OpenQASM 2/3** (`device::device_qasm`): every gate lowered exactly to its dialect's standard library, multi-controlled gates included, every qubit measured into `c[i]`, and the service's counts read back onto the crate's basis; **native programs** (`native::compile`): a processor's own basis (`rz`/`sx`/`x`/`cz`; `prx`/`cz`; or `gpi`/`gpi2`/`zz` with angles in turns, written exactly; the Z rotations carried in the phases of the last two) on its own qubits, routed over its coupling map, and checked against the circuit by simulation before it is sent — Bloch / entanglement / purity analysis | | `quantum_lang` | **the OpenQASM reader, one grammar for every surface** (`qasm::read`, `qasm::parse`): the job service, the command line, both MCP servers, the Studio, the desktop app, the edge component and the in-browser IDE all read through it. OpenQASM 3.1 and the OpenQASM 2 forms in common use, read in three stages with the source position of everything: registers, aliases and slices; gate definitions; every modifier (`ctrl @`, `negctrl @`, `inv @`, `pow(k) @`, exact fractional powers) on any gate, lowered exactly; `def` subroutines, inlined, a returned measurement written into the caller's bit; classical types (`int`, `uint`, `float`, `angle[n]`, `complex`, `bool`, `bit`, arrays), their arithmetic, casts and built-in functions, computed on reading with the same bits on every platform; `for`, `while` (unrolled when known, or run at most `@wai.max_iterations N` times on measured bits), `switch`, `break`, `continue`, `end`; conditions on measured bits with comparisons, `&&`, `||` and `!`, compiled to steps that branch forward; `output` carried into the result; `delay`, `box`, `stretch` and `barrier` carried as timing and labelled when a method ignores durations; `defcal` and `cal` kept verbatim, never run. Generators: integer `input`s, so one program describes a family of circuits (`qasm::read_with`, `qasm::instantiate`). **Every refusal and warning carries a stable code** (`line 3: unknown gate foo [Q103]`, and as data with its line, column and length): [the table of codes](docs/qasm-diagnostics.md) (`qasm::diag::codes()`, `wai-quantum diagnostics`), and [what the reader does with each construct of OpenQASM 3.1](docs/qasm-feature-matrix.md). The [conformance corpus](../quantum-conformance/qasm3/) holds the specification's examples and a program for each construct, run natively, under wasm32-wasip2 and in the browser | | `quantum_qasm` | the reader's program lowered to each method's input, exactly up to a global phase: dense matrices, two-qubit matrices for the matrix product state, Pauli rotations, Clifford gates, and the dyadic circuit (`Program::dyadic`); `qasm::dyadic_circuit` gives the exact state vector's circuit for a program whose measurements all come at the end, and refuses any other with its code | -| `quantum_engine` | **the quantum job service's engine, for every host**: a job (an OpenQASM program and what is asked of it: shots, Pauli expectation values, amplitudes, noise) planned onto the strongest method it admits, with the reason and the memory and work it will take; run; answered in the service's JSON with its accuracy class; and sealed over the request and the result in their canonical JSON (`engine::job`, `engine::receipt`), so a result from the service, the command line or a browser tab checks the same way. Also a program's circuit drawn as text (`engine::draw`) and as a standalone SVG drawing, the Studio's picture with each gate marked by whose hand it is in (`engine::svg`, which the Python package draws in notebooks: `wai/wai-quantum-py`); the specification-and-evaluator workbench (`engine::discover`: a specification, the engine's verdict on a generator at every size, and an archive of the cheapest verified generator in every cell of two measures, sealed); a program's history, attributed (`engine::versions`: versions in a sealed chain, each with who made it and how, proposals reviewed change by change, and each line and gate in the hand that wrote it, which a job's receipt can bind); the person's rules for proposals (`engine::locks`: held blocks, the gates allowed, the qubit declarations, what to avoid and a region, checked change by change); what is new in a program (`engine::novelty`: its structural distance to the known constructions and to programs given, each known construction it is proved to be and whether by a different route, and the strengths a cost did not ask for; a workbench candidate that breaks only a rule is an aberration, with the cost it would have); a study (`engine::study`: a protocol sealed before any data, each participant's condition fixed by their key, their record sealed with it, the measures computed by the engine from what they made, and a cohort report with seeded bootstrap intervals and Cronbach's alpha); convergence (`engine::converge`: along a program's history, how far the person's versions are built from a model's proposals and the model's share of the gates, with the versions made on their own set beside the rest; and a cohort's diversity, its Vendi score, centroid and narrowing over time); rewrites that keep a circuit's meaning (`engine::rewrite`: cancellations, merges, Hadamard conjugations and gates carried past what they commute with, each checked on the qubits it touches, so a chain of them proves two programs equal at any width, explains an optimisation, and offers a person one rewrite at a time); a person's taste (`engine::taste`: a Bradley–Terry model of their own choices between programs the engine proves equal, part by part of a rubric, with typicality shown beside it and moved only by a dial, and how well it predicts them measured); what an agent is told (`engine::guide`: the language the reader takes, its diagnostics, each held to the reader by a test, the methods and their accuracy classes, worked examples each with its intent, and the rules an agent keeps, which both MCP servers serve as resources and prompts); the language service an editor needs (`engine::editor`: the reader's refusal and the critique's smells as diagnostics on their lines, completion by what the cursor's place takes, hover with each standard gate's matrix written exactly and held to the engine's own, a user gate's computed from its gates, and each gate's cost as the engine counts it, definitions, references, renames, an outline, folding, and a formatter with one layout that keeps every token, all pure and with no protocol in it); compilation with a certificate (`engine::compile`: checked passes, every rotation written in Clifford+T within an ε budget by `engine::synth`, one sealed record of each pass and the distance; see [docs/compiler.md](docs/compiler.md)); resource estimation from programs (`engine::estimate`: logical counts with each rotation at its synthesised `T` count, the physical estimate with the code model fitted to the crate's own decoders, the frontier, every estimate sealed; see [docs/estimate.md](docs/estimate.md)); the debugger (`engine::debug`: a program stepped forward and back one operation at a time on the bit-exact or double-precision state vector, or the density matrix under noise beside its noiseless state, stopped before a line, and probed anywhere for its amplitudes and phases, each qubit's Bloch vector and purity, the reduced density matrix of chosen qubits and the entanglement across a cut; every branch at its measurements in the middle with its weight, exact fractions by the tableau for a noiseless Clifford program; a recorded path replayed; a state seen twice the same bits, and the exact method's state after the last gate the exact run's, by its hash; refused past 16 qubits, or 8 under noise); the circuit as LaTeX in plain TikZ (`engine::latex`) and in words for a screen reader (`engine::describe`); the circuit edited by its drawing, a gate put on a wire, moved or deleted as an edit to the program's own text through its syntax tree (`engine::visual`); Pearson's chi-squared test and the G-test of sampled outcomes against a stated distribution, the p-value the same bits everywhere (`engine::stats`); sets and sweeps (`engine::sets`: a list of programs, or one program over a grid of its integer, float and angle inputs, run as plain jobs, each item sealed and one receipt over the head that holds the Merkle root of every item's leaf, with each item's path to it); a result's provenance under the categories provenance surveys use (`engine::run::run_with_provenance`: the program's digest, the method, each pass a compile ran with its check's verdict and the certificate's digest, the shots, seed and plan, the engine's version); and the energy a job spent (`engine::energy`: the CPU energy the kernel counts for one process on macOS, and on Linux the processor packages' counters over a window, read only when live and labelled with no attribution to the job). It builds for `wasm32-unknown-unknown`, where it runs on one thread and claims no time | +| `quantum_engine` | **the quantum job service's engine, for every host**: a job (an OpenQASM program and what is asked of it: shots, Pauli expectation values, amplitudes, noise) planned onto the strongest method it admits, with the reason and the memory and work it will take; run; answered in the service's JSON with its accuracy class; and sealed over the request and the result in their canonical JSON (`engine::job`, `engine::receipt`), so a result from the service, the command line or a browser tab checks the same way. Also a program's circuit drawn as text (`engine::draw`) and as a standalone SVG drawing, the Studio's picture with each gate marked by whose hand it is in (`engine::svg`, which the Python package draws in notebooks: `wai/wai-quantum-py`); the specification-and-evaluator workbench (`engine::discover`: a specification, the engine's verdict on a generator at every size, and an archive of the cheapest verified generator in every cell of two measures, sealed); a program's history, attributed (`engine::versions`: versions in a sealed chain, each with who made it and how, proposals reviewed change by change, and each line and gate in the hand that wrote it, which a job's receipt can bind); the person's rules for proposals (`engine::locks`: held blocks, the gates allowed, the qubit declarations, what to avoid and a region, checked change by change); what is new in a program (`engine::novelty`: its structural distance to the known constructions and to programs given, each known construction it is proved to be and whether by a different route, and the strengths a cost did not ask for; a workbench candidate that breaks only a rule is an aberration, with the cost it would have); a study (`engine::study`: a protocol sealed before any data, each participant's condition fixed by their key, their record sealed with it, the measures computed by the engine from what they made, and a cohort report with seeded bootstrap intervals and Cronbach's alpha); convergence (`engine::converge`: along a program's history, how far the person's versions are built from a model's proposals and the model's share of the gates, with the versions made on their own set beside the rest; and a cohort's diversity, its Vendi score, centroid and narrowing over time); rewrites that keep a circuit's meaning (`engine::rewrite`: cancellations, merges, Hadamard conjugations and gates carried past what they commute with, each checked on the qubits it touches, so a chain of them proves two programs equal at any width, explains an optimisation, and offers a person one rewrite at a time); a person's taste (`engine::taste`: a Bradley–Terry model of their own choices between programs the engine proves equal, part by part of a rubric, with typicality shown beside it and moved only by a dial, and how well it predicts them measured); what an agent is told (`engine::guide`: the language the reader takes, its diagnostics, each held to the reader by a test, the methods and their accuracy classes, worked examples each with its intent, and the rules an agent keeps, which both MCP servers serve as resources and prompts); the language service an editor needs (`engine::editor`: the reader's refusal and the critique's smells as diagnostics on their lines, completion by what the cursor's place takes, hover with each standard gate's matrix written exactly and held to the engine's own, a user gate's computed from its gates, and each gate's cost as the engine counts it, definitions, references, renames, an outline, folding, and a formatter with one layout that keeps every token, all pure and with no protocol in it); compilation with a certificate (`engine::compile`: checked passes, every rotation written in Clifford+T within an ε budget by `engine::synth`, one sealed record of each pass and the distance; see [docs/compiler.md](docs/compiler.md)); resource estimation from programs (`engine::estimate`: logical counts with each rotation at its synthesised `T` count, the physical estimate with the code model fitted to the crate's own decoders, the frontier, every estimate sealed; see [docs/estimate.md](docs/estimate.md)); the debugger (`engine::debug`: a program stepped forward and back one operation at a time on the bit-exact or double-precision state vector, or the density matrix under noise beside its noiseless state, stopped before a line, and probed anywhere for its amplitudes and phases, each qubit's Bloch vector and purity, the reduced density matrix of chosen qubits and the entanglement across a cut; every branch at its measurements in the middle with its weight, exact fractions by the tableau for a noiseless Clifford program; a recorded path replayed; a state seen twice the same bits, and the exact method's state after the last gate the exact run's, by its hash; refused past 16 qubits, or 8 under noise); the circuit as LaTeX in plain TikZ (`engine::latex`) and in words for a screen reader (`engine::describe`); the circuit edited by its drawing, a gate put on a wire, moved or deleted as an edit to the program's own text through its syntax tree (`engine::visual`); Pearson's chi-squared test and the G-test of sampled outcomes against a stated distribution, the p-value the same bits everywhere (`engine::stats`); sets and sweeps (`engine::sets`: a list of programs, or one program over a grid of its integer, float and angle inputs, run as plain jobs, each item sealed and one receipt over the head that holds the Merkle root of every item's leaf, with each item's path to it); a result's provenance under the categories provenance surveys use (`engine::run::run_with_provenance`: the program's digest, the method, each pass a compile ran with its check's verdict and the certificate's digest, the shots, seed and plan, the engine's version); and the energy a job spent (`engine::energy`: the CPU energy the kernel counts for one process on macOS, and on Linux the processor packages' counters over a window, read only when live and labelled with no attribution to the job).; and programs in other formats (`engine::formats`): QIR read under the base and adaptive profiles its entry point names (1.x and 2.x, what a profile forbids refused on its line, a rotation at any angle read at its exact double) and written as QIR 2.0, and jeff read and written through the crate's own Cap'n Proto reader and writer (loops over registers unrolled, a switch on a measured bit read as a condition, Pauli-product rotations lowered exactly), every construct a reader cannot hold refused with a code (`Q700` to `Q721`) and its place and each conversion checked by the equivalence ladder ([the formats page](docs/formats.md), [the conformance set](../quantum-conformance/formats/)). It builds for `wasm32-unknown-unknown`, where it runs on one thread and claims no time | | `quantum_stabilizer` | stabilizer (CHP) tableau — `O(n²)`, scales far past statevector | | `quantum_mps` | tensor-network (matrix-product-state) backend | | `quantum_pauli` | sparse-Pauli / Heisenberg observable propagation over the dyadic gate set (up to 64 qubits) | @@ -100,7 +100,7 @@ never creates information. | `quantum_sv` | **the dense state vector at any angle**: one-, two- and `k`-qubit kernels with a diagonal fast path, gate fusion around multi-qubit gates, threads over independent blocks; every kernel path computes each amplitude in the same order, so threading never changes a bit. Agrees with an independent simulator to 2·10⁻¹⁶ | | `quantum_compile` | Clifford routing + stabilizer-tableau equivalence proof | | `quantum_atom` | neutral-atom register preparation (Hungarian / LSAP) | -| `quantum_qir` | **QIR export + ingest** — emit and parse QIR (the QIR Alliance's LLVM-based IR); emitted modules validated with `llvm-as`, and 9 of 10 catalog algorithms re-ingest to a bit-identical statevector | +| `quantum_qir` | **QIR export + ingest** for the bit-exact circuit — emit and parse QIR (the QIR Alliance's LLVM-based IR) in Clifford+T and dyadic phases; emitted modules validated with `llvm-as`, and 9 of 10 catalog algorithms re-ingest to a bit-identical statevector. The engine's own QIR reader and writer (`engine::formats::qir`, with `quantum_engine`) take any angle and both profiles | | `quantum_comm` | **key distribution and security**: BB84 with an intercept-resend eavesdropper (a quiet channel gives a perfect key; interception costs a quarter of it), and the no-cloning theorem priced | | `quantum_qudit` | **qubits and qutrits**: the Weyl–Heisenberg group, the Fourier gate over `Z_d`, the controlled sum — exact for `d = 2` and `3` | | `quantum_info` | **mixed states**: density matrices, the partial trace, purity, exact Pauli expectations, and CHSH against the classical and Tsirelson bounds | diff --git a/wai/wai-quantum/README.zh-CN.md b/wai/wai-quantum/README.zh-CN.md index 14dd7e3be..d3cb0c017 100644 --- a/wai/wai-quantum/README.zh-CN.md +++ b/wai/wai-quantum/README.zh-CN.md @@ -52,7 +52,7 @@ wai-quantum lab qutrit # d = 3,不只是量子比特 | `quantum_toolchain` | 算法库、后端推荐、**通过唯一的读取器导入 OpenQASM 2/3**(`from_qasm`):读取器接受的一切内容,只要符合逐位精确线路的限制——二进角度、测量位于末尾——都可导入,否则以读取器的代码拒绝(非二进角度会被拒绝,绝不舍入);由修饰符写出的受控门保持为一个整体,因此 `to_qasm` 的输出能逐门读回;**可直接用于设备的 OpenQASM 2/3**(`device::device_qasm`):每个门都被精确降级(lower)到其方言的标准库,包括多控制门,每个量子比特都测量到 `c[i]` 中,服务返回的计数再读回到本 crate 的基上;**原生程序**(`native::compile`):处理器自己的基(`rz`/`sx`/`x`/`cz`;`prx`/`cz`;或角度以圈为单位、精确写出的 `gpi`/`gpi2`/`zz`;Z 旋转承载在后两者的相位中),作用于处理器自身的量子比特,在其耦合图上路由,并在发送之前通过模拟与原线路核对——Bloch / 纠缠 / 纯度分析 | | `quantum_lang` | **OpenQASM 读取器,所有入口共用一套语法**(`qasm::read`、`qasm::parse`):作业服务、命令行、两个 MCP 服务器、Studio、桌面应用、边缘组件和浏览器内 IDE 都通过它读取。支持 OpenQASM 3.1 以及常用的 OpenQASM 2 写法,分三个阶段读取,并保留每个元素的源代码位置:寄存器、别名与切片;门定义;任意门上的每一种修饰符(`ctrl @`、`negctrl @`、`inv @`、`pow(k) @`、精确的分数次幂),均精确降级;`def` 子程序,内联展开,返回的测量结果写入调用方的比特;经典类型(`int`、`uint`、`float`、`angle[n]`、`complex`、`bool`、`bit`、数组)及其算术、类型转换和内置函数,在读取时求值,在所有平台上得到相同的位;`for`、`while`(次数已知时展开,否则基于测量比特最多运行 `@wai.max_iterations N` 次)、`switch`、`break`、`continue`、`end`;基于测量比特、带比较运算以及 `&&`、`||` 和 `!` 的条件,编译为向前分支的步骤;`output` 带入结果;`delay`、`box`、`stretch` 和 `barrier` 作为时序信息保留,并在某种方法忽略时长时加以标注;`defcal` 和 `cal` 原样保留,从不执行。生成器:整数 `input`,使一个程序可以描述一族线路(`qasm::read_with`、`qasm::instantiate`)。**每一条拒绝和警告都带有稳定的代码**(`line 3: unknown gate foo [Q103]`,也以数据形式给出,附带行、列和长度):[代码表](docs/qasm-diagnostics.md)(`qasm::diag::codes()`、`wai-quantum diagnostics`),以及[读取器如何处理 OpenQASM 3.1 的每种构造](docs/qasm-feature-matrix.md)。[一致性语料库](../quantum-conformance/qasm3/)收录了规范中的示例以及针对每种构造的程序,分别在原生环境、wasm32-wasip2 下和浏览器中运行 | | `quantum_qasm` | 把读取器得到的程序降级为各方法的输入,在相差一个全局相位的意义下精确:稠密矩阵、供矩阵乘积态使用的两量子比特矩阵、Pauli 旋转、Clifford 门,以及二进线路(`Program::dyadic`);对于所有测量都位于末尾的程序,`qasm::dyadic_circuit` 给出精确态矢量所用的线路,其他程序则以相应代码拒绝 | -| `quantum_engine` | **量子作业服务的引擎,适用于所有宿主**:一个作业(一个 OpenQASM 程序以及对它的要求:采样次数(shots)、Pauli 期望值、振幅、噪声)被规划到它所允许的最强方法上,并给出理由以及将占用的内存和工作量;然后运行;以服务的 JSON 返回结果并附上精度等级;再以请求和结果的规范 JSON 加以封存(`engine::job`、`engine::receipt`),因此无论结果来自服务、命令行还是浏览器标签页,都以同样的方式校验。此外还有:以文本绘制程序的线路(`engine::draw`),以及绘制为独立的 SVG 图——即 Studio 中的那幅图,每个门都标明出自谁之手(`engine::svg`,Python 包在 notebook 中用它绘图:`wai/wai-quantum-py`);规范与评估器工作台(`engine::discover`:一份规范,引擎对某个生成器在每个规模上的判定,以及一个存档,记录由两项度量划分出的每个格子中已验证的最廉价生成器,并加以封存);带署名的程序历史(`engine::versions`:版本构成一条封存的链,每个版本记录由谁、以何种方式产生,提案逐项变更地审阅,每一行、每个门都归于写下它的人,作业的回执可以绑定这些信息);使用者为提案设定的规则(`engine::locks`:锁定的代码块、允许的门、量子比特声明、需要避免的内容以及一个区域,逐项变更地检查);程序中有什么新东西(`engine::novelty`:它与已知构造及给定程序之间的结构距离,它被证明等同于哪些已知构造、是否经由不同的路径,以及代价函数并未要求的长处;只违反某条规则的工作台候选被视为偏离(aberration),并附上它本应具有的代价);研究(`engine::study`:在获得任何数据之前就封存的研究方案,每位参与者的实验条件由其密钥固定,其记录也用该密钥封存,各项度量由引擎根据参与者的产出计算,以及带有固定种子的 bootstrap 区间和 Cronbach's alpha 的群组报告);趋同(`engine::converge`:沿着程序的历史,使用者的版本在多大程度上建立在模型的提案之上、模型在门中所占的份额,并把使用者独立完成的版本与其余版本并列;以及一个群组的多样性、其 Vendi 分数、质心和随时间的收窄);保持线路含义的重写(`engine::rewrite`:消去、合并、Hadamard 共轭,以及把门移过与之对易的门,每一步都在它触及的量子比特上加以检查,因此一连串重写可以在任意宽度上证明两个程序相等、解释一次优化,并且每次向使用者提供一个重写);使用者的偏好(`engine::taste`:针对使用者在引擎证明等价的程序之间所作的选择,按评分细则逐项建立的 Bradley–Terry 模型,典型性显示在旁边且只能通过一个调节旋钮改变,并测量它对使用者选择的预测效果);告诉智能体的内容(`engine::guide`:读取器接受的语言、它的诊断信息——每一项都由测试保证与读取器一致——各种方法及其精度等级、每个都附有意图的示例,以及智能体须遵守的规则,两个 MCP 服务器都将其作为资源和提示词提供);编辑器所需的语言服务(`engine::editor`:读取器的拒绝和评审(critique)发现的代码异味作为诊断显示在相应的行上,根据光标所在位置可接受的内容进行补全,悬停提示中每个标准门的矩阵精确写出并与引擎自身的矩阵核对、用户定义门的矩阵由其组成门计算得出,以及引擎所计的每个门的代价,定义、引用、重命名、大纲、折叠,以及只有一种排版、保留每个词法单元的格式化器,全部是纯逻辑,不含任何协议);带证书的编译(`engine::compile`:经过检查的各遍处理(pass),由 `engine::synth` 在 ε 预算内把每个旋转写成 Clifford+T,每遍处理及距离都记录在一份封存的记录中;参见 [docs/compiler.md](docs/compiler.md));基于程序的资源估算(`engine::estimate`:逻辑计数,其中每个旋转按综合后的 `T` 门数计;物理估算,其码模型拟合自本 crate 自己的解码器;前沿(frontier);每个估算都加以封存;参见 [docs/estimate.md](docs/estimate.md));调试器(`engine::debug`:在逐位精确或双精度的态矢量上,或在有噪声的密度矩阵上(与其无噪声的态并列),逐个操作地向前和向后单步执行程序,在某一行之前停下,并在任意位置探查其振幅和相位、每个量子比特的 Bloch 矢量和纯度、所选量子比特的约化密度矩阵以及跨某个切分的纠缠;在中途测量处给出每个分支及其权重,对于无噪声的 Clifford 程序则由稳定子表给出精确分数;重放已录制的路径;同一个态看两次,位完全相同,而精确方法在最后一个门之后的态就是精确运行所得的态,以其哈希值为证;超过 16 个量子比特(有噪声时超过 8 个)则拒绝);以纯 TikZ 写成 LaTeX 的线路(`engine::latex`),以及供屏幕阅读器使用的文字描述(`engine::describe`);通过线路图编辑线路:在一根线上放置、移动或删除一个门,都会经由语法树转化为对程序文本本身的编辑(`engine::visual`);针对给定分布、对采样结果进行的 Pearson 卡方检验和 G 检验,p 值在任何地方都是相同的位(`engine::stats`);以及操作系统内核为单个进程统计的 CPU 能耗(`engine::energy`)。它可以为 `wasm32-unknown-unknown` 构建,在该目标上单线程运行,且不声称任何耗时 | +| `quantum_engine` | **量子作业服务的引擎,适用于所有宿主**:一个作业(一个 OpenQASM 程序以及对它的要求:采样次数(shots)、Pauli 期望值、振幅、噪声)被规划到它所允许的最强方法上,并给出理由以及将占用的内存和工作量;然后运行;以服务的 JSON 返回结果并附上精度等级;再以请求和结果的规范 JSON 加以封存(`engine::job`、`engine::receipt`),因此无论结果来自服务、命令行还是浏览器标签页,都以同样的方式校验。此外还有:以文本绘制程序的线路(`engine::draw`),以及绘制为独立的 SVG 图——即 Studio 中的那幅图,每个门都标明出自谁之手(`engine::svg`,Python 包在 notebook 中用它绘图:`wai/wai-quantum-py`);规范与评估器工作台(`engine::discover`:一份规范,引擎对某个生成器在每个规模上的判定,以及一个存档,记录由两项度量划分出的每个格子中已验证的最廉价生成器,并加以封存);带署名的程序历史(`engine::versions`:版本构成一条封存的链,每个版本记录由谁、以何种方式产生,提案逐项变更地审阅,每一行、每个门都归于写下它的人,作业的回执可以绑定这些信息);使用者为提案设定的规则(`engine::locks`:锁定的代码块、允许的门、量子比特声明、需要避免的内容以及一个区域,逐项变更地检查);程序中有什么新东西(`engine::novelty`:它与已知构造及给定程序之间的结构距离,它被证明等同于哪些已知构造、是否经由不同的路径,以及代价函数并未要求的长处;只违反某条规则的工作台候选被视为偏离(aberration),并附上它本应具有的代价);研究(`engine::study`:在获得任何数据之前就封存的研究方案,每位参与者的实验条件由其密钥固定,其记录也用该密钥封存,各项度量由引擎根据参与者的产出计算,以及带有固定种子的 bootstrap 区间和 Cronbach's alpha 的群组报告);趋同(`engine::converge`:沿着程序的历史,使用者的版本在多大程度上建立在模型的提案之上、模型在门中所占的份额,并把使用者独立完成的版本与其余版本并列;以及一个群组的多样性、其 Vendi 分数、质心和随时间的收窄);保持线路含义的重写(`engine::rewrite`:消去、合并、Hadamard 共轭,以及把门移过与之对易的门,每一步都在它触及的量子比特上加以检查,因此一连串重写可以在任意宽度上证明两个程序相等、解释一次优化,并且每次向使用者提供一个重写);使用者的偏好(`engine::taste`:针对使用者在引擎证明等价的程序之间所作的选择,按评分细则逐项建立的 Bradley–Terry 模型,典型性显示在旁边且只能通过一个调节旋钮改变,并测量它对使用者选择的预测效果);告诉智能体的内容(`engine::guide`:读取器接受的语言、它的诊断信息——每一项都由测试保证与读取器一致——各种方法及其精度等级、每个都附有意图的示例,以及智能体须遵守的规则,两个 MCP 服务器都将其作为资源和提示词提供);编辑器所需的语言服务(`engine::editor`:读取器的拒绝和评审(critique)发现的代码异味作为诊断显示在相应的行上,根据光标所在位置可接受的内容进行补全,悬停提示中每个标准门的矩阵精确写出并与引擎自身的矩阵核对、用户定义门的矩阵由其组成门计算得出,以及引擎所计的每个门的代价,定义、引用、重命名、大纲、折叠,以及只有一种排版、保留每个词法单元的格式化器,全部是纯逻辑,不含任何协议);带证书的编译(`engine::compile`:经过检查的各遍处理(pass),由 `engine::synth` 在 ε 预算内把每个旋转写成 Clifford+T,每遍处理及距离都记录在一份封存的记录中;参见 [docs/compiler.md](docs/compiler.md));基于程序的资源估算(`engine::estimate`:逻辑计数,其中每个旋转按综合后的 `T` 门数计;物理估算,其码模型拟合自本 crate 自己的解码器;前沿(frontier);每个估算都加以封存;参见 [docs/estimate.md](docs/estimate.md));调试器(`engine::debug`:在逐位精确或双精度的态矢量上,或在有噪声的密度矩阵上(与其无噪声的态并列),逐个操作地向前和向后单步执行程序,在某一行之前停下,并在任意位置探查其振幅和相位、每个量子比特的 Bloch 矢量和纯度、所选量子比特的约化密度矩阵以及跨某个切分的纠缠;在中途测量处给出每个分支及其权重,对于无噪声的 Clifford 程序则由稳定子表给出精确分数;重放已录制的路径;同一个态看两次,位完全相同,而精确方法在最后一个门之后的态就是精确运行所得的态,以其哈希值为证;超过 16 个量子比特(有噪声时超过 8 个)则拒绝);以纯 TikZ 写成 LaTeX 的线路(`engine::latex`),以及供屏幕阅读器使用的文字描述(`engine::describe`);通过线路图编辑线路:在一根线上放置、移动或删除一个门,都会经由语法树转化为对程序文本本身的编辑(`engine::visual`);针对给定分布、对采样结果进行的 Pearson 卡方检验和 G 检验,p 值在任何地方都是相同的位(`engine::stats`);以及操作系统内核为单个进程统计的 CPU 能耗(`engine::energy`)。它可以为 `wasm32-unknown-unknown` 构建,在该目标上单线程运行,且不声称任何耗时;以及其他格式的程序(`engine::formats`):按入口点所声明的基础(base)或自适应(adaptive)配置文件读取 QIR(1.x 和 2.x;配置文件禁止的内容按行拒绝,任意角度的旋转按其精确的双精度值读取),并写出为 QIR 2.0;通过本 crate 自带的 Cap'n Proto 读写器读写 jeff。读取器无法精确表示的每种构造都以代码(`Q700` 到 `Q721`)及其位置拒绝,每次转换都由等价判定阶梯检查([格式说明](docs/formats.md)、[一致性测试集](../quantum-conformance/formats/)) | | `quantum_stabilizer` | 稳定子(CHP)表——`O(n²)`,可扩展的规模远超态矢量 | | `quantum_mps` | 张量网络(矩阵乘积态)后端 | | `quantum_pauli` | 在二进门集上进行稀疏 Pauli / Heisenberg 绘景的可观测量传播(最多 64 个量子比特) | diff --git a/wai/wai-quantum/docs/README.zh-CN.md b/wai/wai-quantum/docs/README.zh-CN.md index 3e01f27af..3e060889d 100644 --- a/wai/wai-quantum/docs/README.zh-CN.md +++ b/wai/wai-quantum/docs/README.zh-CN.md @@ -20,6 +20,7 @@ - [编译器](compiler.md):各遍处理、ε 预算和证书。 - [资源估算](estimate.md):估算中的每个数字及其假设。 - [读取器的诊断](qasm-diagnostics.md),以及[它如何处理每种构造](qasm-feature-matrix.md)。 +- [其他格式](formats.md):按配置文件读写的 QIR 和 jeff,每次转换都经过检查。 - [作业层的拒绝代码](job-refusals.md)。 - [纠错与误差缓解](qec.md):码及其经过认证的距离、解码器、阈值,以及作业中的误差缓解。 - [脉冲与动力学](pulse.md):读入为帧上时序的脉冲程序,在主方程下积分并给出误差上界,在每个平台上比特一致,以及其回执所绑定的内容。 diff --git a/wai/wai-quantum/docs/formats.md b/wai/wai-quantum/docs/formats.md index 28c8b2e65..93f0701ac 100644 --- a/wai/wai-quantum/docs/formats.md +++ b/wai/wai-quantum/docs/formats.md @@ -15,14 +15,15 @@ line of a QIR module, or a function and an operation of a jeff module. ## Converting -```text -wai-quantum convert prog.qasm --to qir > prog.ll # QIR 2.0, base profile -wai-quantum convert prog.qasm --to qir --profile adaptive -wai-quantum convert prog.ll --to jeff -o prog.jeff # the format read is found from the bytes -wai-quantum convert prog.jeff --to qasm # as OpenQASM 3 -wai-quantum convert prog.jeff --to qasm --json # with the notes, what was read and the check -wai-quantum run prog.ll --shots 1000 # every verb that reads a program reads these -wai-quantum run prog.bin --from jeff # a name the extension does not give +```sh runnable +wai-quantum convert prog.qasm --to qir > prog.ll # QIR 2.0, base profile +wai-quantum convert teleport.qasm --to qir > teleport.ll # it branches: the adaptive profile +wai-quantum convert prog.ll --to jeff -o prog.jeff # the format read is found from the file +wai-quantum convert prog.jeff --to qasm # as OpenQASM 3 +wai-quantum convert prog.jeff --to qasm --json > back.json # with the notes, what was read and the check +wai-quantum run prog.ll --shots 1000 # every verb that reads a program reads these +cp prog.jeff prog.bin +wai-quantum lint --from jeff prog.bin # a name the extension does not give ``` Every conversion is checked. What is written is read back and held to the @@ -147,7 +148,7 @@ A value used twice, before it is defined or as the wrong type is refused (`Q713`); a value the reader needs on reading and cannot know is refused (`Q714`), as is a qubit the two sides of a branch leave in different places (`Q708`). Metadata is not read. The reader takes up to 20 000 000 operations -run (`Q312`), 65 536 qubits and 64 MiB. +run (`Q312`), 2 000 000 gates written (`Q210`), 65 536 qubits and 64 MiB. **The wire format.** A Cap'n Proto message, read and written by a reader and writer of its own (no dependency): segments, far pointers and every diff --git a/wai/wai-quantum/src/engine/formats/jeff.rs b/wai/wai-quantum/src/engine/formats/jeff.rs index e41a924fd..f7cdb6311 100644 --- a/wai/wai-quantum/src/engine/formats/jeff.rs +++ b/wai/wai-quantum/src/engine/formats/jeff.rs @@ -360,6 +360,8 @@ struct Runner<'m> { custom: BTreeMap, ppr: usize, arbitrary: usize, + /// Gates, measurements and resets written so far. + emitted: usize, } /// A function's values as it runs. @@ -445,6 +447,15 @@ impl<'m> Runner<'m> { Ok(self.n_qubits - 1) } + /// One more gate, measurement or reset written, within what the reader holds. + fn wrote(&mut self, at: &str) -> Result<(), Diagnostic> { + self.emitted += 1; + if self.emitted > crate::qasm::MAX_OPS { + return Err(Diagnostic::new(diag::TOO_MANY_GATES, None, format!("{at}: the module runs to more than the {} gates the reader holds", crate::qasm::MAX_OPS))); + } + Ok(()) + } + fn step(&mut self, at: &str) -> Result<(), Diagnostic> { self.steps += 1; if self.steps > MAX_STEPS { @@ -630,6 +641,7 @@ impl<'m> Runner<'m> { let q = self.qubit(ins.into_iter().next().expect("one"), at)?; let b = self.n_bits; self.n_bits += 1; + self.wrote(at)?; out.push(Stmt::Measure { qubit: q, bit: b }); let bit = V::Int(1, IntV::Cond(Cond::Bit(b))); if d == 3 { vec![bit] } else { vec![V::Qubit(q), bit] } @@ -637,6 +649,7 @@ impl<'m> Runner<'m> { 5 => { arity(1)?; let q = self.qubit(ins.into_iter().next().expect("one"), at)?; + self.wrote(at)?; out.push(Stmt::Reset(q)); vec![V::Qubit(q)] } @@ -677,6 +690,7 @@ impl<'m> Runner<'m> { return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: one qubit given to a gate twice"))); } let pow = u32::from(g.power.max(1)); + self.wrote(at)?; for &p in ¶ms { if crate::qasm::dyadic(p).is_none() { self.arbitrary += 1; @@ -1250,7 +1264,7 @@ pub fn read(bytes: &[u8]) -> Result { Some((_, Func::Declaration)) => return Err(Diagnostic::new(diag::INSTRUCTION_UNKNOWN, None, "the entry point is declared and not defined")), None => return Err(unread(format!("the entry point is function {entry}, which the module does not have"))), }; - let mut r = Runner { m: &m, n_qubits: 0, n_bits: 0, steps: 0, notes: BTreeMap::new(), custom: BTreeMap::new(), ppr: 0, arbitrary: 0 }; + let mut r = Runner { m: &m, n_qubits: 0, n_bits: 0, steps: 0, notes: BTreeMap::new(), custom: BTreeMap::new(), ppr: 0, arbitrary: 0, emitted: 0 }; let mut fr = Frame { f: entry, values, vals: vec![None; values.len()] }; // The entry point's arguments: qubits, from |0⟩. let mut args = Vec::new(); -- 2.54.0 (Apple Git-157) From cb059d7a0bebce368ac74370ae2188b83b9bd367 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Fri, 30 Oct 2026 11:49:42 -0400 Subject: [PATCH 05/10] wai-quantum: other formats bounded against hostile input, in CI A jeff register's size reached Vec::with_capacity before the qubit cap, so one of 2^32 - 1 qubits asked for 34 GB: under wasm32-wasip2 the test written first trapped in qureg_op. It is now refused (Q111) before anything is made. The audit of every allocation and loop sized by the input: - the wire reader bounds every pointer, segment and list in 64 bits (a segment count of u32::MAX overflowed a 32-bit usize), and counts each list element against the words it may visit; - the jeff reader counts every register slot, array element (an array copied by setIndex included), call frame and condition term against one budget, caps arrays, values per function, operations run and gates written, shares conditions between copies, and no longer truncates a 64-bit size, index, selector or shift to fit; - the QIR reader no longer copies a block's lines on each visit, bounds the nesting of branches, the instructions run over every path and the state each path copies, and dedups a condition's reads; - a condition is at most 1 024 terms and 32 deep once written, a run of one operator written flat; - a conversion's check is not run past 262 144 gates applied or 4e9 amplitudes, and says so; both readers take modules of up to 16 MiB. tests/formats_mutation.rs mutates every QIR and jeff file of the conformance set from a fixed seed (45 153 inputs: bits flipped, files cut short and spliced, every length, count, offset and 32-bit field inflated) and holds each to converting or a refusal with a code of the table, within 20 s and 1 GiB measured by a counting allocator. wai-quantum-formats.yml runs it natively and under wasm32-wasip2 (a release build with overflow checks), the count pinned. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/wai-quantum-formats.yml | 32 +- wai/CHANGELOG.md | 10 + wai/quantum-conformance/formats/README.md | 17 +- wai/quantum-conformance/formats/bundle.json | 9 + .../jeff/refuse/register_too_large.jeff | Bin 0 -> 368 bytes wai/quantum-conformance/formats/manifest.json | 11 + wai/wai-quantum/Cargo.toml | 2 +- wai/wai-quantum/docs/formats.md | 31 +- wai/wai-quantum/src/engine/formats/capnp.rs | 58 +-- wai/wai-quantum/src/engine/formats/emit.rs | 57 ++- wai/wai-quantum/src/engine/formats/jeff.rs | 256 ++++++++---- wai/wai-quantum/src/engine/formats/mod.rs | 50 +++ wai/wai-quantum/src/engine/formats/qir.rs | 68 ++- wai/wai-quantum/src/engine/formats/tests.rs | 41 ++ wai/wai-quantum/tests/formats_mutation.rs | 388 ++++++++++++++++++ 15 files changed, 891 insertions(+), 139 deletions(-) create mode 100644 wai/quantum-conformance/formats/jeff/refuse/register_too_large.jeff create mode 100644 wai/wai-quantum/tests/formats_mutation.rs diff --git a/.github/workflows/wai-quantum-formats.yml b/.github/workflows/wai-quantum-formats.yml index a342b1aef..89cc3e93d 100644 --- a/.github/workflows/wai-quantum-formats.yml +++ b/.github/workflows/wai-quantum-formats.yml @@ -9,7 +9,9 @@ name: wai-quantum other formats # (wai/quantum-conformance/formats/: the QIR specification's examples, the # jeff project's benchmarks and examples, hand-built modules and OpenQASM # sources written to each format, each with what the engine must do with it, -# the QIR written pinned by its BLAKE3). +# the QIR written pinned by its BLAKE3), and hostile inputs: every file +# of the set mutated, each converted or refused with a code within its +# bounds, natively and under wasm32-wasip2. # # The set runs natively, where a second test holds its bundle equal to its # files, and under wasm32-wasip2 by wasmtime, where the cases come from the @@ -79,7 +81,7 @@ jobs: set -euo pipefail cargo test --locked --release --features full --lib engine::formats 2>&1 | tee "$RUNNER_TEMP/formats.log" grep -qE '^test result: ok\. [1-9][0-9]+ passed; 0 failed' "$RUNNER_TEMP/formats.log" || { echo "::error::the formats' tests did not pass"; exit 1; } - for m in engine::formats::capnp::tests engine::formats::emit::tests engine::formats::tests::every_gate_is_written_exactly engine::formats::tests::a_pauli_product_rotation_is_exp_i_theta_p engine::formats::tests::the_conformance_set_holds_these_modules; do + for m in engine::formats::capnp::tests engine::formats::emit::tests engine::formats::tests::every_gate_is_written_exactly engine::formats::tests::a_pauli_product_rotation_is_exp_i_theta_p engine::formats::tests::the_conformance_set_holds_these_modules engine::formats::tests::a_register_too_large_is_refused_before_it_is_allocated; do grep -q "^test $m.* \.\.\. ok$" "$RUNNER_TEMP/formats.log" || { echo "::error::no test of $m ran"; exit 1; } done - name: The conformance set @@ -89,6 +91,18 @@ jobs: set -euo pipefail cargo test --locked --release --features full --test formats_conformance 2>&1 | tee "$RUNNER_TEMP/set.log" grep -q 'test result: ok. 2 passed; 0 failed' "$RUNNER_TEMP/set.log" || { echo "::error::the conformance set did not pass"; exit 1; } + # Hostile inputs: every file of the set mutated by a seeded generator + # (bits flipped, files cut short and spliced, and every length, count, + # offset and 32-bit field inflated), each converted or refused with a + # code within its time and memory bounds. The count of inputs is pinned. + - name: Hostile inputs + shell: bash + working-directory: wai/wai-quantum + run: | + set -euo pipefail + cargo test --locked --release --features full --test formats_mutation -- --nocapture 2>&1 | tee "$RUNNER_TEMP/mutation.log" + grep -q 'test result: ok. 1 passed; 0 failed' "$RUNNER_TEMP/mutation.log" || { echo "::error::a hostile input was not refused cleanly"; exit 1; } + grep -q '45153 inputs: ' "$RUNNER_TEMP/mutation.log" || { echo "::error::the count of hostile inputs is not the pinned 45153"; exit 1; } - name: The readers without the receipts shell: bash working-directory: wai/wai-quantum @@ -144,3 +158,17 @@ jobs: set -euo pipefail cargo test --locked --target wasm32-wasip2 --features full --test formats_conformance 2>&1 | tee "$RUNNER_TEMP/wasip2.log" grep -q 'test result: ok. 1 passed; 0 failed' "$RUNNER_TEMP/wasip2.log" || { echo "::error::the conformance set did not pass under wasip2"; exit 1; } + # The same hostile inputs where an allocation larger than the address + # space panics: a release build with overflow checks on, as it is slow + # enough otherwise to outlast the job. + - name: Hostile inputs under wasmtime + shell: bash + working-directory: wai/wai-quantum + env: + CARGO_TARGET_WASM32_WASIP2_RUNNER: wasmtime + CARGO_PROFILE_RELEASE_OVERFLOW_CHECKS: "true" + run: | + set -euo pipefail + cargo test --locked --release --target wasm32-wasip2 --features full --test formats_mutation -- --nocapture 2>&1 | tee "$RUNNER_TEMP/wasip2-mutation.log" + grep -q 'test result: ok. 1 passed; 0 failed' "$RUNNER_TEMP/wasip2-mutation.log" || { echo "::error::a hostile input was not refused cleanly under wasip2"; exit 1; } + grep -q '45153 inputs: ' "$RUNNER_TEMP/wasip2-mutation.log" || { echo "::error::the count of hostile inputs under wasip2 is not the pinned 45153"; exit 1; } diff --git a/wai/CHANGELOG.md b/wai/CHANGELOG.md index cd567f5a4..f7712e03e 100644 --- a/wai/CHANGELOG.md +++ b/wai/CHANGELOG.md @@ -210,6 +210,16 @@ wire format may change and such changes are recorded here. servers serve `convert_program` (`tools.v1.json#/$defs/convert`); the job service answers `POST /v1/convert`, and a job may carry `{"source": {"format", "program" | "program_base64"}}` in place of `qasm` (`job-request.v1.json`). The Studio's engine has `engine_convert`. +- **Hostile input:** every size, count, index and bound a file gives is held to a cap before anything is made from + it (a register's size before its qubits: a jeff register of 2^32 − 1 qubits is refused, `Q111`, where it once asked + for 34 GB and trapped in a page); the wire reader bounds every pointer in 64 bits and counts each list element + against the words it visits; the jeff reader counts every register slot, array element, call frame and condition + term against one budget; the QIR reader bounds its branches' nesting, the instructions it runs over every path and + the state each path copies; a condition is at most 1 024 terms and 32 deep once written. A conversion's check is + not run past 262 144 gates applied or 4·10⁹ amplitudes, and says so. `tests/formats_mutation.rs` mutates every file + of the conformance set from a fixed seed (45 153 inputs: bits flipped, files cut and spliced, every length, count, + offset and 32-bit field inflated) and holds each to converting or a refusal with a code, within 20 s and 1 GiB, + natively and under wasm32-wasip2 in `wai-quantum-formats.yml`. - **Conformance** (`wai/quantum-conformance/formats/`): the QIR specification's two examples, every intrinsic, a QIR 1 module, each profile's refusals, hand-built jeff modules, the jeff project's own structured benchmarks and examples unchanged, and OpenQASM written to each format with the QIR bytes pinned, run natively and under wasm32-wasip2 diff --git a/wai/quantum-conformance/formats/README.md b/wai/quantum-conformance/formats/README.md index c4be55341..1329bfb35 100644 --- a/wai/quantum-conformance/formats/README.md +++ b/wai/quantum-conformance/formats/README.md @@ -35,6 +35,15 @@ WAI_FORMATS_BUNDLE_WRITE=1 cargo test --release --features full --test formats_c and after changing the hand-built jeff modules, `WAI_FORMATS_VECTORS_WRITE=1 cargo test --release --features full --lib the_conformance_set_holds_these_modules`. +The same files are the seed of the hostile-input test: every QIR and jeff +file mutated (bits flipped, cut short, spliced with another, and every +length, count, offset and 32-bit field inflated), 45 153 inputs from a fixed +seed, each converted or refused with a code within 20 s and 1 GiB: + +```sh +cargo test --release --features full --test formats_mutation -- --nocapture +``` + ## WASI (the edge) ```sh @@ -42,7 +51,13 @@ CARGO_TARGET_WASM32_WASIP2_RUNNER=wasmtime \ cargo test --target wasm32-wasip2 --features full --test formats_conformance ``` -The cases come from the bundle compiled into the test. +The cases come from the bundle compiled into the test. The hostile-input +test runs there too, as a release build with overflow checks on: + +```sh +CARGO_PROFILE_RELEASE_OVERFLOW_CHECKS=true CARGO_TARGET_WASM32_WASIP2_RUNNER=wasmtime \ + cargo test --release --target wasm32-wasip2 --features full --test formats_mutation +``` CI: `wai-quantum-formats.yml` (the readers' and writers' unit tests, and the set natively and under wasm32-wasip2). diff --git a/wai/quantum-conformance/formats/bundle.json b/wai/quantum-conformance/formats/bundle.json index e63dd57cf..89216dc98 100644 --- a/wai/quantum-conformance/formats/bundle.json +++ b/wai/quantum-conformance/formats/bundle.json @@ -318,6 +318,15 @@ }, "base64": "AAAAAHYAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAJkBAAAuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQACQEAAHcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAA9wAAAAAAAAAAAAAAGAAAAAEABAAAAAAAAAAAAHEAAAAEAAAAbQAAAAwAAAAAAAAAAAAAAGgAAAABAAEAAAAAAAAAAAA=" }, + { + "file": "jeff/refuse/register_too_large.jeff", + "format": "jeff", + "expect": { + "refused": "Q111", + "at": "operation 1" + }, + "base64": "AAAAAC0AAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAJkAAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQAWQAAACcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAAVwAAAAAAAAAAAAAACAAAAAEABAACAAAAAAAAACEAAAAEAAAAHQAAAAwAAAAAAAAAAAAAABgAAAACAAAAAQAAAAAAAAAZAAAADAAAABkAAAAMAAAAAAAAAAAAAAAUAAAAAQAAAAAAAAAAAAAAAAADAP////8AAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAgAAAAAAAIADAAAAAIAAAAAAAAAAAAAAAwAAAACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAqAAAAbWFpbgAAAAA=" + }, { "file": "jeff/upstream/ghz-linear_3.jeff", "format": "jeff", diff --git a/wai/quantum-conformance/formats/jeff/refuse/register_too_large.jeff b/wai/quantum-conformance/formats/jeff/refuse/register_too_large.jeff new file mode 100644 index 0000000000000000000000000000000000000000..bf2a4c0dc6764475d66c4be970f9c304e7a3e1ac GIT binary patch literal 368 zcmZvW!3x4a3`DbQg$gPN9_*o@C!x^q5sw}{=>Pb&`evI{EghJhWOlL}GqWBF*_K1W zx$tU-p5Y5_-J6pNGmTSbbqQ5=MKvGTP$;`{)sGh#&|D&s-8iAjw{Y#AZ sA1Y7S!^jka9p7N6zvq7@_Rr u64 { u64::from_le_bytes(seg[byte..byte + 8].try_into().expect("eight bytes")) } +/// Whether `bytes` from byte `start` lie within a segment of `len` bytes, +/// in 64 bits, so no size a message gives can overflow a 32-bit address. +fn within(start: usize, bytes: u64, len: usize) -> bool { + (start as u64).checked_add(bytes).is_some_and(|e| e <= len as u64) +} + impl<'a> Message<'a> { /// Reads a message's segment table. pub fn read(bytes: &'a [u8]) -> Result, String> { if bytes.len() < 8 { return Err(format!("{} bytes: shorter than a segment table", bytes.len())); } - let count = u32::from_le_bytes(bytes[0..4].try_into().expect("four bytes")) as usize + 1; - if count > MAX_SEGMENTS { + let count = u64::from(u32::from_le_bytes(bytes[0..4].try_into().expect("four bytes"))) + 1; + if count > MAX_SEGMENTS as u64 { return Err(format!("{count} segments: more than the {MAX_SEGMENTS} a message may have here")); } + let count = count as usize; let table = (4 + 4 * count).div_ceil(8) * 8; if bytes.len() < table { return Err("the segment table runs past the end of the file".into()); @@ -86,8 +95,11 @@ impl<'a> Message<'a> { let mut segments = Vec::with_capacity(count); let mut at = table; for i in 0..count { - let words = u32::from_le_bytes(bytes[4 + 4 * i..8 + 4 * i].try_into().expect("four bytes")) as usize; - let end = words.checked_mul(8).and_then(|b| b.checked_add(at)).filter(|&e| e <= bytes.len()).ok_or_else(|| format!("segment {i} ({words} words) runs past the end of the file"))?; + let words = u64::from(u32::from_le_bytes(bytes[4 + 4 * i..8 + 4 * i].try_into().expect("four bytes"))); + if !within(at, words * 8, bytes.len()) { + return Err(format!("segment {i} ({words} words) runs past the end of the file")); + } + let end = at + (words * 8) as usize; segments.push(&bytes[at..end]); at = end; } @@ -119,7 +131,7 @@ impl<'a> Message<'a> { /// word that describes it (a struct's or list's own). fn resolve(&'a self, seg: usize, at: usize) -> Result, String> { let s = self.segments[seg]; - if at + 8 > s.len() { + if !within(at, 8, s.len()) { return Err(format!("a pointer at word {} of segment {seg} is past its end", at / 8)); } let p = word(s, at); @@ -130,29 +142,27 @@ impl<'a> Message<'a> { 0 | 1 => { let off = ((p as u32 as i32) >> 2) as i64; let target = (at as i64 + 8) + off * 8; - if target < 0 || target as usize > s.len() { + if target < 0 || target as u64 > s.len() as u64 { return Err(format!("a pointer at word {} of segment {seg} points outside it", at / 8)); } Ok(Some((seg, target as usize, p))) } 2 => { let double = p >> 2 & 1 == 1; - let pad = ((p as u32) >> 3) as usize * 8; + let pad = u64::from((p as u32) >> 3) * 8; let tseg = (p >> 32) as usize; let ts = *self.segments.get(tseg).ok_or_else(|| format!("a far pointer names segment {tseg}, which the message does not have"))?; + if !within(0, pad + if double { 16 } else { 8 }, ts.len()) { + return Err("a far pointer's landing pad is past its segment's end".into()); + } + let pad = pad as usize; if !double { - if pad + 8 > ts.len() { - return Err("a far pointer's landing pad is past its segment's end".into()); - } let inner = word(ts, pad); if inner & 3 == 2 { return Err("a far pointer lands on another far pointer".into()); } return self.resolve(tseg, pad); } - if pad + 16 > ts.len() { - return Err("a double-far pointer's landing pad is past its segment's end".into()); - } let far = word(ts, pad); let tag = word(ts, pad + 8); if far & 7 != 2 { @@ -160,11 +170,11 @@ impl<'a> Message<'a> { } let cseg = (far >> 32) as usize; let cs = *self.segments.get(cseg).ok_or_else(|| format!("a far pointer names segment {cseg}, which the message does not have"))?; - let content = ((far as u32) >> 3) as usize * 8; - if content > cs.len() || tag & 3 > 1 || (tag as u32) >> 2 != 0 { + let content = u64::from((far as u32) >> 3) * 8; + if !within(0, content, cs.len()) || tag & 3 > 1 || (tag as u32) >> 2 != 0 { return Err("a double-far pointer's tag or content does not read".into()); } - Ok(Some((cseg, content, tag))) + Ok(Some((cseg, content as usize, tag))) } _ => Err("a capability pointer: a message on disk holds none".into()), } @@ -181,7 +191,7 @@ impl<'a> Message<'a> { let data_words = (p >> 32) as u16; let ptr_count = (p >> 48) as u16; let words = data_words as usize + ptr_count as usize; - if start + words * 8 > self.segments[seg].len() { + if !within(start, words as u64 * 8, self.segments[seg].len()) { return Err("a struct runs past the end of its segment".into()); } self.spend(words.max(1) as u64)?; @@ -201,7 +211,7 @@ impl<'a> Message<'a> { let s = self.segments[seg]; if size == 7 { // `count` is the words of every element; the tag before them says how many. - if start + 8 > s.len() { + if !within(start, 8, s.len()) { return Err("a list's tag is past the end of its segment".into()); } let tag = word(s, start); @@ -214,7 +224,7 @@ impl<'a> Message<'a> { if len.checked_mul(per).is_none_or(|w| w > count as usize) { return Err("a composite list's elements do not fit the words it gives".into()); } - if start + 8 + count as usize * 8 > s.len() { + if !within(start, 8 + u64::from(count) * 8, s.len()) { return Err("a list runs past the end of its segment".into()); } // An element of no words still costs a word to visit. @@ -230,11 +240,11 @@ impl<'a> Message<'a> { 5 | 6 => 64, _ => unreachable!(), }; - let bytes = (count as u64 * bits).div_ceil(8) as usize; - if start + bytes > s.len() { + let bytes = (u64::from(count) * bits).div_ceil(8); + if !within(start, bytes, s.len()) { return Err("a list runs past the end of its segment".into()); } - self.spend((bytes as u64).div_ceil(8).max(count as u64 / 64).max(1))?; + self.spend(bytes.div_ceil(8).max(u64::from(count)).max(1))?; Ok(Some(List { msg: self, seg, start, size, len: count, data_words: 0, ptr_count: 0, depth })) } } diff --git a/wai/wai-quantum/src/engine/formats/emit.rs b/wai/wai-quantum/src/engine/formats/emit.rs index 01ef1e233..f891a9232 100644 --- a/wai/wai-quantum/src/engine/formats/emit.rs +++ b/wai/wai-quantum/src/engine/formats/emit.rs @@ -40,13 +40,44 @@ impl Cond { pub(crate) fn xor(a: Cond, b: Cond) -> Cond { Cond::or(Cond::and(a.clone(), Cond::not(b.clone())), Cond::and(Cond::not(a), b)) } - fn size(&self) -> usize { + /// Its terms. + pub(crate) fn size(&self) -> usize { match self { Cond::Const(_) | Cond::Bit(_) => 1, Cond::Not(x) => 1 + x.size(), Cond::And(a, b) | Cond::Or(a, b) => 1 + a.size() + b.size(), } } + + /// How deep its parentheses nest once written, a run of one operator + /// (`a && b && c`) written flat: what a reader of the text recurses on. + pub(crate) fn depth(&self) -> usize { + match self { + Cond::Const(_) | Cond::Bit(_) => 0, + Cond::Not(x) => 1 + x.depth(), + Cond::And(..) | Cond::Or(..) => { + let mut terms = Vec::new(); + self.flat(&mut terms); + 1 + terms.iter().map(|t| t.depth()).max().unwrap_or(0) + } + } + } + + /// The operands of a run of one operator, in order. + fn flat<'c>(&'c self, out: &mut Vec<&'c Cond>) { + match self { + Cond::And(a, b) | Cond::Or(a, b) => { + for t in [a, b] { + if std::mem::discriminant(&**t) == std::mem::discriminant(self) { + t.flat(out); + } else { + out.push(t); + } + } + } + x => out.push(x), + } + } fn text(&self, s: &mut String) { match self { Cond::Const(b) => s.push_str(if *b { "true" } else { "false" }), @@ -56,19 +87,27 @@ impl Cond { x.text(s); s.push(')'); } - Cond::And(a, b) | Cond::Or(a, b) => { + Cond::And(..) | Cond::Or(..) => { + let mut terms = Vec::new(); + self.flat(&mut terms); + let op = if matches!(self, Cond::And(..)) { " && " } else { " || " }; s.push('('); - a.text(s); - s.push_str(if matches!(self, Cond::And(..)) { " && " } else { " || " }); - b.text(s); + for (i, t) in terms.iter().enumerate() { + if i > 0 { + s.push_str(op); + } + t.text(s); + } s.push(')'); } } } } -/// The most nodes a condition may have once written out. -pub(crate) const MAX_COND: usize = 4096; +/// The most terms a condition may have. +pub(crate) const MAX_COND: usize = 1024; +/// The deepest a condition's parentheses may nest once written. +pub(crate) const MAX_COND_DEPTH: usize = 32; /// One statement. #[derive(Clone, Debug, PartialEq)] @@ -174,9 +213,9 @@ fn write_body(s: &mut String, body: &[Stmt], depth: usize) { } } -/// Whether a condition is small enough to write. +/// Whether a condition is small and shallow enough to write and read back. pub(crate) fn cond_fits(c: &Cond) -> bool { - c.size() <= MAX_COND + c.size() <= MAX_COND && c.depth() <= MAX_COND_DEPTH } #[cfg(test)] diff --git a/wai/wai-quantum/src/engine/formats/jeff.rs b/wai/wai-quantum/src/engine/formats/jeff.rs index f7cdb6311..6d0abe3d6 100644 --- a/wai/wai-quantum/src/engine/formats/jeff.rs +++ b/wai/wai-quantum/src/engine/formats/jeff.rs @@ -48,6 +48,7 @@ use crate::qasm::diag::{self, Diagnostic}; use crate::qasm::{Instr, Kind, Program}; use serde_json::{Value, json}; use std::collections::BTreeMap; +use std::rc::Rc; /// The format version written. pub const VERSION: (u32, u32, u32) = (0, 3, 1); @@ -282,7 +283,11 @@ fn decode(bytes: &[u8]) -> Result { let func = match f.u16(1) { 0 => { let body = region_at(&f, 0)?.unwrap_or(Region { sources: vec![], targets: vec![], ops: vec![] }); - Func::Definition { body, values: values(&f, 1)? } + let values = values(&f, 1)?; + if values.len() > MAX_VALUES { + return Err(format!("a function of {} values, more than the reader takes ({MAX_VALUES})", values.len())); + } + Func::Definition { body, values } } 1 => Func::Declaration, k => return Err(format!("a function of kind {k}, which the schema does not have")), @@ -320,7 +325,19 @@ enum V { #[derive(Clone, Debug, PartialEq)] enum IntV { Known(u64), - Cond(Cond), + /// Shared, so an array copied by `setIndex` copies no condition. + Cond(Rc), +} + +/// What a value holds, in the units the reader's budget counts: one, and +/// one more for each slot of a register or element of an array. +fn weight(v: &V) -> usize { + 1 + match v { + V::Qureg(r) => r.len(), + V::IntArray(_, a) => a.len(), + V::FloatArray(a) => a.len(), + _ => 0, + } } const WELL_KNOWN: [(&str, &str, usize, usize); 14] = [ @@ -346,8 +363,22 @@ const STANDARD: &[&str] = &[ "id", "x", "y", "z", "h", "s", "sdg", "t", "tdg", "sx", "sxdg", "rx", "ry", "rz", "p", "phase", "u1", "u2", "u3", "u", "cx", "cnot", "cy", "cz", "ch", "cp", "cphase", "cu1", "crx", "cry", "crz", "cu3", "cu", "swap", "iswap", "rxx", "ryy", "rzz", "ccx", "toffoli", "ccz", "cswap", "fredkin", ]; +/// The largest module read, in bytes. +pub const MAX_BYTES: usize = 16 << 20; /// The most operations run, unrolled loops and inlined calls included. -const MAX_STEPS: usize = 20_000_000; +pub const MAX_STEPS: usize = 1 << 22; +/// The most gates, measurements and resets written. +pub const MAX_WRITTEN: usize = 1 << 20; +/// The most elements one classical array holds. +pub const MAX_ARRAY: usize = 1 << 16; +/// The most values one function declares. +pub const MAX_VALUES: usize = 1 << 20; +/// What the reader may hold, in all, over a module's run: every qubit, every +/// register slot and array element made or copied, every value of every +/// call's frame and every term of every condition built count against it, +/// so no module makes the reader hold more than a bounded amount, whatever +/// its sizes say. +pub const MAX_UNITS: usize = 1 << 22; /// The deepest calls and regions are nested. const MAX_NESTING: u32 = 32; @@ -362,6 +393,8 @@ struct Runner<'m> { arbitrary: usize, /// Gates, measurements and resets written so far. emitted: usize, + /// What the run has made so far, against [`MAX_UNITS`]. + units: usize, } /// A function's values as it runs. @@ -450,16 +483,60 @@ impl<'m> Runner<'m> { /// One more gate, measurement or reset written, within what the reader holds. fn wrote(&mut self, at: &str) -> Result<(), Diagnostic> { self.emitted += 1; - if self.emitted > crate::qasm::MAX_OPS { - return Err(Diagnostic::new(diag::TOO_MANY_GATES, None, format!("{at}: the module runs to more than the {} gates the reader holds", crate::qasm::MAX_OPS))); + if self.emitted > MAX_WRITTEN { + return Err(Diagnostic::new(diag::TOO_MANY_GATES, None, format!("{at}: the module runs to more than the {MAX_WRITTEN} gates the reader writes"))); } Ok(()) } + /// `n` more units made, within [`MAX_UNITS`]: checked before what they + /// count is allocated. + fn charge(&mut self, n: usize, at: &str) -> Result<(), Diagnostic> { + self.units = self.units.saturating_add(n); + if self.units > MAX_UNITS { + return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: the module makes more registers, arrays, values and conditions than the reader holds ({MAX_UNITS} in all)"))); + } + Ok(()) + } + + /// A condition built from measured bits, within the reader's bounds on + /// one condition and on all of them. + fn cond(&mut self, c: Cond, at: &str) -> Result { + if !emit::cond_fits(&c) { + return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a condition of more than {} terms or nested deeper than {}", emit::MAX_COND, emit::MAX_COND_DEPTH))); + } + self.charge(c.size(), at)?; + Ok(IntV::Cond(Rc::new(c))) + } + + /// A register of `k` new qubits, or the refusal when they are more than + /// the reader takes: checked before the register is made. + fn fresh_register(&mut self, k: usize, at: &str) -> Result>, Diagnostic> { + if k > (crate::qasm::MAX_QUBITS - self.n_qubits) as usize { + return Err(Diagnostic::new(diag::TOO_MANY_QUBITS, None, format!("{at}: a register of {k} qubits, beside {} already, is more than the reader takes ({})", self.n_qubits, crate::qasm::MAX_QUBITS))); + } + self.charge(k, at)?; + let mut r = Vec::with_capacity(k); + for _ in 0..k { + r.push(Some(self.fresh_qubit(at)?)); + } + Ok(r) + } + + /// An array of `n` elements, or the refusal when it is longer than the + /// reader holds: checked before the array is made. + fn array_len(&mut self, n: usize, at: &str) -> Result { + if n > MAX_ARRAY { + return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: an array of {n} elements, more than the reader holds ({MAX_ARRAY})"))); + } + self.charge(n, at)?; + Ok(n) + } + fn step(&mut self, at: &str) -> Result<(), Diagnostic> { self.steps += 1; if self.steps > MAX_STEPS { - return Err(Diagnostic::new(diag::UNROLL_LIMIT, None, format!("{at}: the module runs to more than {MAX_STEPS} operations"))); + return Err(Diagnostic::new(diag::UNROLL_LIMIT, None, format!("{at}: the module runs to more than the {MAX_STEPS} operations the reader runs"))); } Ok(()) } @@ -521,23 +598,25 @@ impl<'m> Runner<'m> { self.note(diag::JEFF_STRUCTURED, "structured control flow decided on reading, unrolled, or written as conditions".into()); match sel { V::Int(_, IntV::Known(v)) => { - let r = branches.get(v as usize).or(default.as_deref()).ok_or_else(|| Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a `switch` on {v} with no such branch and no default")))?; + let r = usize::try_from(v).ok().and_then(|i| branches.get(i)).or(default.as_deref()).ok_or_else(|| Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a `switch` on {v} with no such branch and no default")))?; self.region(fr, r, args, out, depth + 1)? } V::Int(1, IntV::Cond(c)) => { let zero = branches.first().ok_or_else(|| Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a `switch` with no branches")))?; let one = branches.get(1).or(default.as_deref()).ok_or_else(|| Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a `switch` on a measured bit with no branch for 1")))?; + // Each side takes its own copy of what the switch passes it. + self.charge(args.iter().map(weight).sum(), &at)?; let (mut then, mut other) = (Vec::new(), Vec::new()); let a = self.region(fr, one, args.clone(), &mut then, depth + 1)?; let b = self.region(fr, zero, args, &mut other, depth + 1)?; if a.len() != b.len() { return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a `switch` whose branches give different numbers of values"))); } - let merged = a.into_iter().zip(b).map(|(x, y)| join(&c, x, y)).collect::, String>>().map_err(|e| Diagnostic::new(diag::STALE_READ, None, format!("{at}: {e}")))?; - if !emit::cond_fits(&c) { - return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a condition of more than {} terms", emit::MAX_COND))); + let mut merged = Vec::with_capacity(a.len()); + for (x, y) in a.into_iter().zip(b) { + merged.push(self.join(&c, x, y, &at)?); } - out.push(Stmt::If { cond: c, then, other }); + out.push(Stmt::If { cond: (*c).clone(), then, other }); merged } _ => return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a `switch` on a value that is neither known on reading nor a measured bit"))), @@ -611,6 +690,8 @@ impl<'m> Runner<'m> { let m = self.m; match m.functions.get(f as usize) { Some((_, Func::Definition { body, values })) => { + // The callee's values, made for this call, count. + self.charge(values.len(), at)?; let mut fr = Frame { f, values, vals: vec![None; values.len()] }; self.region(&mut fr, body, args, out, depth) } @@ -643,7 +724,7 @@ impl<'m> Runner<'m> { self.n_bits += 1; self.wrote(at)?; out.push(Stmt::Measure { qubit: q, bit: b }); - let bit = V::Int(1, IntV::Cond(Cond::Bit(b))); + let bit = V::Int(1, IntV::Cond(Rc::new(Cond::Bit(b)))); if d == 3 { vec![bit] } else { vec![V::Qubit(q), bit] } } 5 => { @@ -770,7 +851,8 @@ impl<'m> Runner<'m> { _ => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a qubit register is expected"))), } }; - let n = |r: &Self, x: &V, what: &str| -> Result { r.known_int(x, at, what).map(|v| v as u32 as usize) }; + // A size or an index as it is: one past what any register holds is refused below, never cut to fit. + let n = |r: &Self, x: &V, what: &str| -> Result { r.known_int(x, at, what).map(|v| usize::try_from(v).unwrap_or(usize::MAX)) }; let want = |k: usize| -> Result<(), Diagnostic> { if ins.len() != k { return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: the register operation takes {k} values and is given {}", ins.len()))); @@ -787,11 +869,7 @@ impl<'m> Runner<'m> { 0 => { want(1)?; let k = n(self, &ins[0], "a register's size")?; - let mut r = Vec::with_capacity(k); - for _ in 0..k { - r.push(Some(self.fresh_qubit(at)?)); - } - vec![V::Qureg(r)] + vec![V::Qureg(self.fresh_register(k, at)?)] } 1 | 10 => { want(1)?; @@ -912,7 +990,7 @@ impl<'m> Runner<'m> { }; // A bit from a measurement, in the logic a condition can hold. let as_cond = |v: &IntV| match v { - IntV::Cond(c) => Some(c.clone()), + IntV::Cond(c) => Some((**c).clone()), IntV::Known(k) => Some(Cond::Const(k & 1 == 1)), }; let any_cond = ins.iter().any(|x| matches!(x, V::Int(_, IntV::Cond(_)))); @@ -936,10 +1014,7 @@ impl<'m> Runner<'m> { 30 | 31 if in_w == 1 && w >= 1 => return Ok(V::Int(w, arg(0)?.clone())), _ => return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: integer arithmetic on a measured bit: the reader holds a measured bit only as a condition (not, and, or, xor, eq, select)"))), }; - if !emit::cond_fits(&c) { - return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a condition of more than {} terms", emit::MAX_COND))); - } - return Ok(V::Int(w, IntV::Cond(c))); + return Ok(V::Int(w, self.cond(c, at)?)); } let k = |i: usize| -> Result { match arg(i)? { @@ -992,8 +1067,8 @@ impl<'m> Runner<'m> { 22 => u64::from(k(0)? & mask(in_w) < k(1)? & mask(in_w)), 23 => u64::from(k(0)? & mask(in_w) <= k(1)? & mask(in_w)), 24 => sx(k(0)?, in_w).unsigned_abs(), - 27 => k(0)?.checked_shl(k(1)? as u32).unwrap_or(0), - 28 => (k(0)? & mask(in_w)).checked_shr(k(1)? as u32).unwrap_or(0), + 27 => u32::try_from(k(1)?).ok().and_then(|n| k(0).ok()?.checked_shl(n)).unwrap_or(0), + 28 => u32::try_from(k(1)?).ok().and_then(|n| (k(0).ok()? & mask(in_w)).checked_shr(n)).unwrap_or(0), 29 => { if k(0)? & 1 == 1 { k(1)? @@ -1016,26 +1091,32 @@ impl<'m> Runner<'m> { _ => 64, }; Ok(match d { - 0..=4 => V::IntArray(w, consts.iter().map(|&c| IntV::Known(c & mask(w))).collect()), + 0..=4 => { + self.array_len(consts.len(), at)?; + V::IntArray(w, consts.iter().map(|&c| IntV::Known(c & mask(w))).collect()) + } 5 => { - let n = self.known_int(ins.first().unwrap_or(&V::Used), at, "an array's length")? as usize; - if n > crate::qasm::MAX_OPS { - return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: an array of {n}"))); - } + let n = self.known_int(ins.first().unwrap_or(&V::Used), at, "an array's length")?; + let n = self.array_len(usize::try_from(n).unwrap_or(usize::MAX), at)?; V::IntArray(zw, vec![IntV::Known(0); n]) } 6 => { let (V::IntArray(aw, a), Some(i)) = (&ins[0], ins.get(1)) else { return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: getIndex takes an array and an index"))) }; - let i = self.known_int(i, at, "an index")? as usize; + let i = usize::try_from(self.known_int(i, at, "an index")?).unwrap_or(usize::MAX); let x = a.get(i).cloned().ok_or_else(|| Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: index {i} of an array of {}", a.len())))?; V::Int(*aw, x) } 7 => { let (V::IntArray(aw, a), Some(i), Some(V::Int(_, x))) = (&ins[0], ins.get(1), ins.get(2)) else { return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: setIndex takes an array, an index and a value"))) }; - let i = self.known_int(i, at, "an index")? as usize; - let mut a = a.clone(); + let i = usize::try_from(self.known_int(i, at, "an index")?).unwrap_or(usize::MAX); let n = a.len(); - *a.get_mut(i).ok_or_else(|| Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: index {i} of an array of {n}")))? = x.clone(); + if i >= n { + return Err(Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: index {i} of an array of {n}"))); + } + // The array is copied (the old value may still be read), and the copy counts. + self.array_len(n, at)?; + let mut a = a.clone(); + a[i] = x.clone(); V::IntArray(*aw, a) } 8 => match &ins[0] { @@ -1044,7 +1125,7 @@ impl<'m> Runner<'m> { }, 9 => V::IntArray( w, - ins.iter() + self.array_len(ins.len(), at).map(|_| ins.iter())? .map(|x| match x { V::Int(_, v) => Ok(v.clone()), _ => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: an array of integers is made of integers"))), @@ -1138,70 +1219,80 @@ impl<'m> Runner<'m> { fn float_array_op(&mut self, d: u16, consts: &[f64], ins: Vec, at: &str) -> Result { Ok(match d { - 0 | 1 => V::FloatArray(consts.to_vec()), + 0 | 1 => { + self.array_len(consts.len(), at)?; + V::FloatArray(consts.to_vec()) + } 2 => { - let n = self.known_int(ins.first().unwrap_or(&V::Used), at, "an array's length")? as usize; - if n > crate::qasm::MAX_OPS { - return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: an array of {n}"))); - } + let n = self.known_int(ins.first().unwrap_or(&V::Used), at, "an array's length")?; + let n = self.array_len(usize::try_from(n).unwrap_or(usize::MAX), at)?; V::FloatArray(vec![0.0; n]) } 3 => { let (V::FloatArray(a), Some(i)) = (&ins[0], ins.get(1)) else { return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: getIndex takes an array and an index"))) }; - let i = self.known_int(i, at, "an index")? as usize; + let i = usize::try_from(self.known_int(i, at, "an index")?).unwrap_or(usize::MAX); V::Float(*a.get(i).ok_or_else(|| Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: index {i} of an array of {}", a.len())))?) } 4 => { let (V::FloatArray(a), Some(i), Some(V::Float(x))) = (&ins[0], ins.get(1), ins.get(2)) else { return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: setIndex takes an array, an index and a float"))) }; - let i = self.known_int(i, at, "an index")? as usize; - let mut a = a.clone(); + let i = usize::try_from(self.known_int(i, at, "an index")?).unwrap_or(usize::MAX); let n = a.len(); - *a.get_mut(i).ok_or_else(|| Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: index {i} of an array of {n}")))? = *x; + if i >= n { + return Err(Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: index {i} of an array of {n}"))); + } + self.array_len(n, at)?; + let mut a = a.clone(); + a[i] = *x; V::FloatArray(a) } 5 => match &ins[0] { V::FloatArray(a) => V::Int(32, IntV::Known(a.len() as u64)), _ => return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: length of something that is not an array"))), }, - 6 => V::FloatArray(ins.iter().map(|x| self.float(x, at)).collect::>()?), + 6 => { + self.array_len(ins.len(), at)?; + V::FloatArray(ins.iter().map(|x| self.float(x, at)).collect::>()?) + } k => return Err(unread(format!("{at}: a float-array operation of kind {k}, which the schema does not have"))), }) } + + /// What a value is after a branch on `c`: the same on both sides, or (a + /// bit, or an array of them) one side's bit or the other's, as a condition. + fn join(&mut self, c: &Cond, a: V, b: V, at: &str) -> Result { + if a == b { + return Ok(a); + } + match (a, b) { + (V::Int(1, x), V::Int(1, y)) => { + let as_c = |v: IntV| match v { + IntV::Cond(c) => Rc::try_unwrap(c).unwrap_or_else(|rc| (*rc).clone()), + IntV::Known(k) => Cond::Const(k & 1 == 1), + }; + let merged = Cond::or(Cond::and(c.clone(), as_c(x)), Cond::and(Cond::not(c.clone()), as_c(y))); + Ok(V::Int(1, self.cond(merged, at)?)) + } + (V::Qubit(_), V::Qubit(_)) | (V::Qureg(_), V::Qureg(_)) => Err(Diagnostic::new(diag::STALE_READ, None, format!("{at}: a qubit leaves the two sides of a branch in different places"))), + (V::IntArray(w, x), V::IntArray(_, y)) if w == 1 && x.len() == y.len() => { + self.array_len(x.len(), at)?; + let mut out = Vec::with_capacity(x.len()); + for (p, q) in x.into_iter().zip(y) { + match self.join(c, V::Int(1, p), V::Int(1, q), at)? { + V::Int(_, v) => out.push(v), + _ => unreachable!("two bits join to a bit"), + } + } + Ok(V::IntArray(1, out)) + } + _ => Ok(V::Unknown("it differs between the two sides of a branch on a measurement".into())), + } + } } fn mask(w: u8) -> u64 { if w >= 64 { u64::MAX } else { (1u64 << w) - 1 } } -/// What a value is after a branch on `c`: the same on both sides, or (a bit) -/// the one side's bit or the other's. -fn join(c: &Cond, a: V, b: V) -> Result { - if a == b { - return Ok(a); - } - match (a, b) { - (V::Int(1, x), V::Int(1, y)) => { - let as_c = |v: IntV| match v { - IntV::Cond(c) => c, - IntV::Known(k) => Cond::Const(k & 1 == 1), - }; - Ok(V::Int(1, IntV::Cond(Cond::or(Cond::and(c.clone(), as_c(x)), Cond::and(Cond::not(c.clone()), as_c(y)))))) - } - (V::Qubit(_), V::Qubit(_)) | (V::Qureg(_), V::Qureg(_)) => Err("a qubit leaves the two sides of a branch in different places".into()), - (V::IntArray(w, x), V::IntArray(_, y)) if w == 1 && x.len() == y.len() => { - let mut out = Vec::with_capacity(x.len()); - for (p, q) in x.into_iter().zip(y) { - match join(c, V::Int(1, p), V::Int(1, q))? { - V::Int(_, v) => out.push(v), - _ => unreachable!(), - } - } - Ok(V::IntArray(1, out)) - } - _ => Ok(V::Unknown("it differs between the two sides of a branch on a measurement".into())), - } -} - /// An order to run a region's operations in: the order they are listed in /// where that defines every value before its use, else an order their values /// allow (the first ready operation each time). @@ -1250,8 +1341,8 @@ fn inputs_of(o: &Op) -> Vec { /// Reads a jeff module. pub fn read(bytes: &[u8]) -> Result { - if bytes.len() > 64 << 20 { - return Err(unread("a module larger than the 64 MiB the reader takes")); + if bytes.len() > MAX_BYTES { + return Err(unread(format!("a module of {} bytes, larger than the {MAX_BYTES} the reader takes", bytes.len()))); } let m = decode(bytes).map_err(|e| unread(format!("not a jeff module: {e}")))?; let (maj, min, patch) = m.version; @@ -1264,7 +1355,8 @@ pub fn read(bytes: &[u8]) -> Result { Some((_, Func::Declaration)) => return Err(Diagnostic::new(diag::INSTRUCTION_UNKNOWN, None, "the entry point is declared and not defined")), None => return Err(unread(format!("the entry point is function {entry}, which the module does not have"))), }; - let mut r = Runner { m: &m, n_qubits: 0, n_bits: 0, steps: 0, notes: BTreeMap::new(), custom: BTreeMap::new(), ppr: 0, arbitrary: 0, emitted: 0 }; + let mut r = Runner { m: &m, n_qubits: 0, n_bits: 0, steps: 0, notes: BTreeMap::new(), custom: BTreeMap::new(), ppr: 0, arbitrary: 0, emitted: 0, units: 0 }; + r.charge(values.len(), "the entry point's values")?; let mut fr = Frame { f: entry, values, vals: vec![None; values.len()] }; // The entry point's arguments: qubits, from |0⟩. let mut args = Vec::new(); @@ -1273,10 +1365,7 @@ pub fn read(bytes: &[u8]) -> Result { match values.get(s as usize) { Some(Ty::Qubit) => args.push(V::Qubit(r.fresh_qubit(&at)?)), Some(Ty::Qureg(Some(n))) => { - let mut q = Vec::new(); - for _ in 0..*n { - q.push(Some(r.fresh_qubit(&at)?)); - } + let q = r.fresh_register(*n as usize, &at)?; args.push(V::Qureg(q)); } Some(t) => return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: an entry point that takes {t:?}, which is not known on reading"))), @@ -1303,7 +1392,10 @@ pub fn read(bytes: &[u8]) -> Result { for e in entries { match e { IntV::Known(0) => returned.push(None), - IntV::Cond(Cond::Bit(b)) if !returned.contains(&Some(b)) => returned.push(Some(b)), + IntV::Cond(c) => match *c { + Cond::Bit(b) if !returned.contains(&Some(b)) => returned.push(Some(b)), + _ => plain = false, + }, _ => plain = false, } } diff --git a/wai/wai-quantum/src/engine/formats/mod.rs b/wai/wai-quantum/src/engine/formats/mod.rs index d788876e5..76ae92d54 100644 --- a/wai/wai-quantum/src/engine/formats/mod.rs +++ b/wai/wai-quantum/src/engine/formats/mod.rs @@ -251,6 +251,9 @@ pub fn convert(from: Format, to: Format, input: &[u8], profile: Option, /// the middle with its weight, and each leaf's final measurements, from the /// debugger. fn held(a: &Program, a_text: &str, b: &Program, b_text: &str) -> Value { + if let Some(why) = too_much_to_check(a, b) { + return json!({ "verdict": "unchecked", "how": "none", "why": why }); + } if a.dynamic.is_none() && b.dynamic.is_none() { return super::equiv::equivalent_programs(a, b); } @@ -275,6 +278,53 @@ fn held(a: &Program, a_text: &str, b: &Program, b_text: &str) -> Value { } } +/// The most a conversion's check spends, in gate applications times the +/// amplitudes (or tableau rows) each touches: past it the check is not run, +/// and the conversion says so. +pub const CHECK_WORK: f64 = 4e9; +/// The most gates a conversion's check applies, over every input it runs +/// (each basis state, each random input, each branch): each costs what +/// preparing it costs, whatever its width. +pub const CHECK_APPLICATIONS: f64 = 262_144.0; +/// The widest pair the check compares by their tableaus (whose memory grows +/// as the square of the qubits). +pub const CHECK_MAX_TABLEAU: u32 = 2048; + +/// Why the check of `a` against `b` is not run, when it would cost more than +/// [`CHECK_WORK`] or [`CHECK_APPLICATIONS`]: the tableau's rows per gate for +/// a Clifford pair; every basis state to 8 qubits, or three random inputs to +/// 22, through every gate; every branch of a program that branches (the +/// debugger's, at most 16 qubits and 1024 branches). +fn too_much_to_check(a: &Program, b: &Program) -> Option { + let n = a.n_qubits.max(b.n_qubits); + let gates = (a.ops.len() + b.ops.len()) as f64; + let amplitudes = f64::from(n.min(64)).exp2(); + let (applications, work) = if a.dynamic.is_some() || b.dynamic.is_some() { + let mids = |p: &Program| p.dynamic.as_ref().map_or(0, |s| s.iter().filter(|i| matches!(i, crate::qasm::Instr::Measure { .. } | crate::qasm::Instr::Reset(_))).count()); + let branches = f64::from(1u32 << mids(a).max(mids(b)).min(10)); + if n <= 16 { (branches * gates, branches * gates * amplitudes) } else { (0.0, 0.0) } + } else { + if n > CHECK_MAX_TABLEAU { + return Some(format!("{n} qubits: wider than a conversion's check compares ({CHECK_MAX_TABLEAU}); `wai-quantum equivalent` runs it on its own")); + } + let clifford = a.clifford().is_ok() && b.clifford().is_ok(); + if clifford { + (0.0, gates * f64::from(n)) + } else if n <= super::equiv::EXACT_MAX_QUBITS && gates * amplitudes * amplitudes <= 2e8 { + (gates * amplitudes, gates * amplitudes * amplitudes) + } else if n <= super::equiv::RANDOM_MAX_QUBITS { + let k = super::equiv::RANDOM_INPUTS as f64; + (k * gates, k * gates * amplitudes) + } else { + (0.0, 0.0) + } + }; + if work > CHECK_WORK || applications > CHECK_APPLICATIONS { + return Some(format!("the check would apply {applications:.0} gates to about {work:.1e} amplitudes, more than a conversion spends ({CHECK_APPLICATIONS:.0} gates, {CHECK_WORK:.0e} amplitudes); `wai-quantum equivalent` runs it on its own")); + } + None +} + /// The probability of every outcome of a program that branches, from the /// debugger's branches: `None` when it cannot enumerate them all. fn distribution(text: &str) -> Option> { diff --git a/wai/wai-quantum/src/engine/formats/qir.rs b/wai/wai-quantum/src/engine/formats/qir.rs index d993eb4cb..bb2d3c767 100644 --- a/wai/wai-quantum/src/engine/formats/qir.rs +++ b/wai/wai-quantum/src/engine/formats/qir.rs @@ -491,15 +491,26 @@ struct Reader<'m, 'a> { arbitrary: usize, emitted: usize, visits: usize, + instructions: usize, + copied: usize, used_qubits: u32, used_results: u32, } /// The most statements the branches are written out to (a block reached by /// many paths is written once per path). -const MAX_STATEMENTS: usize = crate::qasm::MAX_OPS; +pub const MAX_STATEMENTS: usize = 1 << 20; /// The most blocks visited, over every path. -const MAX_VISITS: usize = 1 << 20; +pub const MAX_VISITS: usize = 1 << 20; +/// The most instructions run, over every path. +pub const MAX_INSTRUCTIONS: usize = 1 << 22; +/// The deepest branches nest. +pub const MAX_NESTING: usize = 64; +/// The most a walk copies of the state along its paths (a result's count, a +/// qubit's flags, each value and its terms), over every branch. +pub const MAX_COPIED: usize = 1 << 26; +/// The largest module read, in bytes. +pub const MAX_BYTES: usize = 16 << 20; enum Term<'a> { Ret, @@ -669,9 +680,12 @@ impl<'m, 'a> Reader<'m, 'a> { } }; if !emit::cond_fits(&v.0) { - return Err(self.not_taken(line, "a condition of more than 4096 terms")); + return Err(self.not_taken(line, &format!("a condition of more than {} terms or nested deeper than {}", emit::MAX_COND, emit::MAX_COND_DEPTH))); } - st.env.insert(def, Val::Bool(v.0, v.1)); + let mut deps = v.1; + deps.sort_unstable(); + deps.dedup(); + st.env.insert(def, Val::Bool(v.0, deps)); return Ok(()); } // Integer and float computation, where the flags declare its type. @@ -741,8 +755,8 @@ impl<'m, 'a> Reader<'m, 'a> { "and" => a & b, "or" => a | b, "xor" => a ^ b, - "shl" => a.checked_shl(b as u32).unwrap_or(0), - _ => a.checked_shr(b as u32).unwrap_or(0), + "shl" => u32::try_from(b).ok().and_then(|k| a.checked_shl(k)).unwrap_or(0), + _ => u32::try_from(b).ok().and_then(|k| a.checked_shr(k)).unwrap_or(0), }) } }; @@ -944,17 +958,22 @@ impl<'m, 'a> Reader<'m, 'a> { } /// Writes the blocks from `b` until `stop` (or a return) into `out`. - fn emit_seq(&mut self, mut b: usize, stop: usize, ipdom: &[usize], st: &mut Path, out: &mut Vec) -> Result<(), Diagnostic> { + fn emit_seq(&mut self, mut b: usize, stop: usize, ipdom: &[usize], st: &mut Path, out: &mut Vec, depth: usize) -> Result<(), Diagnostic> { let exit = self.f.blocks.len(); + let f: &'m Func<'a> = self.f; + if depth > MAX_NESTING { + return Err(self.not_taken(f.blocks[b].1.first().map_or(f.line, |l| l.no), &format!("branches nested deeper than {MAX_NESTING}"))); + } loop { if b == stop { return Ok(()); } self.visits += 1; - if self.visits > MAX_VISITS || self.emitted > MAX_STATEMENTS { - return Err(self.not_taken(self.f.blocks[b].1.first().map_or(self.f.line, |l| l.no), "branches that would be written out to more statements than the reader holds")); + let lines = &f.blocks[b].1; + self.instructions = self.instructions.saturating_add(lines.len()); + if self.visits > MAX_VISITS || self.emitted > MAX_STATEMENTS || self.instructions > MAX_INSTRUCTIONS { + return Err(self.not_taken(lines.first().map_or(f.line, |l| l.no), &format!("branches that would be written out to more than the reader holds ({MAX_STATEMENTS} statements, {MAX_INSTRUCTIONS} instructions run over every path)"))); } - let lines = self.f.blocks[b].1.clone(); for l in &lines[..lines.len() - 1] { self.instruction(l, st, out)?; } @@ -980,10 +999,14 @@ impl<'m, 'a> Reader<'m, 'a> { continue; } cond => { + self.copied = self.copied.saturating_add(2 * st.weight()); + if self.copied > MAX_COPIED { + return Err(self.not_taken(line, &format!("branches whose paths would copy more state than the reader does ({MAX_COPIED})"))); + } let (mut st_t, mut st_f) = (st.clone(), st.clone()); let (mut then, mut other) = (Vec::new(), Vec::new()); - self.emit_seq(tb, j, ipdom, &mut st_t, &mut then)?; - self.emit_seq(fb, j, ipdom, &mut st_f, &mut other)?; + self.emit_seq(tb, j, ipdom, &mut st_t, &mut then, depth + 1)?; + self.emit_seq(fb, j, ipdom, &mut st_f, &mut other, depth + 1)?; *st = merge(st_t, st_f); if then.is_empty() && other.is_empty() { } else if then.is_empty() { @@ -1005,6 +1028,19 @@ impl<'m, 'a> Reader<'m, 'a> { } impl Path { + /// What copying it copies. + fn weight(&self) -> usize { + self.gens.len() + + self + .env + .values() + .map(|v| match v { + Val::Bool(c, d) => 1 + c.size() + d.len(), + _ => 1, + }) + .sum::() + } + fn grow(&mut self, n: usize) { if self.gens.len() < n { self.gens.resize(n, 0); @@ -1075,8 +1111,8 @@ fn is_dyadic(theta: f64) -> bool { /// Reads a QIR module (LLVM text) under its profile. pub fn read(src: &str) -> Result { - if src.len() > 64 << 20 { - return Err(err(diag::FORMAT_UNREAD, 0, "a module larger than the 64 MiB the reader takes")); + if src.len() > MAX_BYTES { + return Err(err(diag::FORMAT_UNREAD, 0, format!("a module of {} bytes, larger than the {MAX_BYTES} the reader takes", src.len()))); } let m = parse_module(src)?; let mut notes = Vec::new(); @@ -1166,6 +1202,8 @@ pub fn read(src: &str) -> Result { arbitrary: 0, emitted: 0, visits: 0, + instructions: 0, + copied: 0, used_qubits: 0, used_results: 0, }; @@ -1247,7 +1285,7 @@ pub fn read(src: &str) -> Result { let mut st = Path::default(); st.grow(r.n_qubits.max(r.n_results) as usize); let mut body = Vec::new(); - r.emit_seq(0, exit, &ipdom, &mut st, &mut body)?; + r.emit_seq(0, exit, &ipdom, &mut st, &mut body, 0)?; let n_qubits = if r.declared_qubits { r.n_qubits } else { r.used_qubits }; let n_results = if r.declared_results { r.n_results } else { r.used_results }; diff --git a/wai/wai-quantum/src/engine/formats/tests.rs b/wai/wai-quantum/src/engine/formats/tests.rs index eed1dcec9..660dad0bb 100644 --- a/wai/wai-quantum/src/engine/formats/tests.rs +++ b/wai/wai-quantum/src/engine/formats/tests.rs @@ -484,6 +484,28 @@ impl Hand { self.op(vec![q], vec![r], 0, b); r } + /// A 32-bit integer constant. + fn int32(&mut self, v: u32) -> u32 { + let x = self.value(INT, 32); + let mut b = Out::new(2, 0); + b.set_u16(1, 3); + b.set_u32(1, v); + self.op(vec![], vec![x], 2, b); + x + } + /// A register of `size` qubits (`size` a value), allocated. + fn qureg_alloc(&mut self, size: u32) -> u32 { + let mut t = Out::new(2, 0); + t.set_u16(0, 1); + let mut v = Out::new(0, 2); + v.ptrs[0] = Ptr::Struct(Box::new(t)); + self.values.push(v); + let r = self.values.len() as u32 - 1; + let mut b = Out::new(1, 0); + b.set_u16(0, 0); + self.op(vec![size], vec![r], 1, b); + r + } fn bytes(&self) -> Vec { let mut region = Out::new(0, 4); region.ptrs[0] = Ptr::U32s(self.sources.clone()); @@ -622,6 +644,24 @@ fn qubit_twice_module() -> Vec { h.bytes() } +/// A register whose size is the largest 32-bit integer. +fn huge_register_module() -> Vec { + let mut h = Hand::new(); + let n = h.int32(u32::MAX); + h.qureg_alloc(n); + h.bytes() +} + +/// A register's size reaches no allocation before the qubit cap: one of +/// 2^32 − 1 qubits is refused as more qubits than the reader takes, not +/// allocated (34 GB, which aborts a process and panics in a page). +#[test] +fn a_register_too_large_is_refused_before_it_is_allocated() { + let d = read(Format::Jeff, &huge_register_module()).unwrap_err(); + assert_eq!(code(&d), diag::TOO_MANY_QUBITS, "{d}"); + assert!(d.message.contains("operation 1"), "{d}"); +} + fn old_version_module() -> Vec { let mut h = Hand::new(); h.alloc(); @@ -785,6 +825,7 @@ fn the_conformance_set_holds_these_modules() { ("jeff/refuse/old_version.jeff", old_version_module()), ("jeff/refuse/angle_from_measurement.jeff", angle_from_measurement_module()), ("jeff/refuse/truncated.jeff", custom_module()[..200].to_vec()), + ("jeff/refuse/register_too_large.jeff", huge_register_module()), ("qir/qft3_typed.ll", crate::quantum_qir::to_qir(&qft, true).into_bytes()), ("qir/qft3_typed.ref.qasm", qt::to_qasm(&qft).into_bytes()), ]; diff --git a/wai/wai-quantum/tests/formats_mutation.rs b/wai/wai-quantum/tests/formats_mutation.rs new file mode 100644 index 000000000..b9c95b60e --- /dev/null +++ b/wai/wai-quantum/tests/formats_mutation.rs @@ -0,0 +1,388 @@ +//! Hostile inputs for the readers of other formats: every QIR and jeff file +//! of the conformance set (`wai/quantum-conformance/formats/`), mutated by a +//! seeded generator, converted to OpenQASM with its check. Every input must +//! convert or be refused with a code of the reader's table, never panic or +//! abort, finish within [`MAX_SECONDS`], and allocate at most [`MAX_PEAK`] +//! bytes beyond what the test held before it. +//! +//! The mutations: bits flipped; a file cut short; the start of one file +//! spliced to the end of another; and every length, count and offset the +//! readers parse set to its largest and to values just past their bounds: +//! in a jeff module the segment table, each pointer's offset, struct sizes +//! and list counts, and each 16-, 32- and 64-bit field (register sizes, +//! loop bounds, array lengths, indices, angles); in a QIR module each number +//! (indices, counts, versions, angles), lines repeated and branches turned +//! back on themselves. +//! +//! The same cases run natively and under wasm32-wasip2, where an allocation +//! larger than the address space panics rather than failing lazily: the +//! seed and the count are fixed, so both builds see the same inputs. Under +//! `WAI_FORMATS_MUTATION_REPORT=1` it prints the slowest inputs. + +#![cfg(feature = "quantum_engine")] + +use serde_json::Value; +use std::alloc::{GlobalAlloc, Layout, System}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use wai_quantum::engine::formats::{self, Format}; + +/// The allocator, counting what is live and the most it has been. +struct Counting; + +static LIVE: AtomicUsize = AtomicUsize::new(0); +static PEAK: AtomicUsize = AtomicUsize::new(0); + +unsafe impl GlobalAlloc for Counting { + unsafe fn alloc(&self, l: Layout) -> *mut u8 { + let p = unsafe { System.alloc(l) }; + if !p.is_null() { + let now = LIVE.fetch_add(l.size(), Ordering::Relaxed) + l.size(); + PEAK.fetch_max(now, Ordering::Relaxed); + } + p + } + unsafe fn dealloc(&self, p: *mut u8, l: Layout) { + unsafe { System.dealloc(p, l) }; + LIVE.fetch_sub(l.size(), Ordering::Relaxed); + } + unsafe fn alloc_zeroed(&self, l: Layout) -> *mut u8 { + let p = unsafe { System.alloc_zeroed(l) }; + if !p.is_null() { + let now = LIVE.fetch_add(l.size(), Ordering::Relaxed) + l.size(); + PEAK.fetch_max(now, Ordering::Relaxed); + } + p + } + unsafe fn realloc(&self, p: *mut u8, l: Layout, size: usize) -> *mut u8 { + let q = unsafe { System.realloc(p, l, size) }; + if !q.is_null() { + if size >= l.size() { + let now = LIVE.fetch_add(size - l.size(), Ordering::Relaxed) + (size - l.size()); + PEAK.fetch_max(now, Ordering::Relaxed); + } else { + LIVE.fetch_sub(l.size() - size, Ordering::Relaxed); + } + } + q + } +} + +#[global_allocator] +static ALLOCATOR: Counting = Counting; + +const BUNDLE: &str = include_str!("../../quantum-conformance/formats/bundle.json"); + +/// The generator's seed. +const SEED: u64 = 0x5eed_1e55_f0f0_0018; +/// Mutated inputs made from each file. +const PER_FILE: usize = 24; +/// The jeff modules whose every 32-bit field is inflated in turn: those of +/// at most this many bytes. +const SWEEP_BYTES: usize = 12_288; +/// The longest one input may take, in seconds (a release build; the wasm32 +/// leg of CI is a release build with overflow checks on). +const MAX_SECONDS: f64 = 20.0; +/// The most one input may allocate beyond what was live before it. +const MAX_PEAK: usize = 1 << 30; + +struct Rng(u64); + +impl Rng { + fn next(&mut self) -> u64 { + self.0 = self.0.wrapping_add(0x9e37_79b9_7f4a_7c15); + let mut z = self.0; + z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9); + z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb); + z ^ (z >> 31) + } + fn below(&mut self, n: usize) -> usize { + if n == 0 { 0 } else { (self.next() % n as u64) as usize } + } +} + +/// The values a length, count, size, index or bound is set to: the largest +/// it holds, and just past each bound the readers keep. +const BIG32: [u32; 9] = [u32::MAX, 0x7fff_ffff, 0x8000_0000, 1 << 16, (1 << 16) + 1, 1 << 20, (1 << 22) + 1, 1 << 24, 65_535]; +const BIG64: [u64; 6] = [u64::MAX, i64::MAX as u64, 1 << 32, (1 << 32) + 1, 0x7ff0_0000_0000_0000, 0x7ff8_0000_0000_0001]; + +/// A jeff module's words: each 8-byte word after the segment table. +fn jeff_mutation(rng: &mut Rng, b: &[u8], other: &[u8]) -> (String, Vec) { + let mut v = b.to_vec(); + let words = v.len() / 8; + match rng.below(9) { + 0 => { + let flips = 1 + rng.below(8); + for _ in 0..flips { + let i = rng.below(v.len().max(1)); + if let Some(x) = v.get_mut(i) { + *x ^= 1 << rng.below(8); + } + } + (format!("{flips} bits flipped"), v) + } + 1 => { + let n = rng.below(v.len()); + v.truncate(n); + (format!("cut to {n} bytes"), v) + } + 2 => { + let (i, j) = (rng.below(v.len()), rng.below(other.len())); + let mut s = v[..i].to_vec(); + s.extend_from_slice(&other[j..]); + (format!("the first {i} bytes and another file's from {j}"), s) + } + 3 => { + // The segment table: the count, or a segment's size. + let i = 4 * rng.below(3); + let x = BIG32[rng.below(BIG32.len())]; + if i + 4 <= v.len() { + v[i..i + 4].copy_from_slice(&x.to_le_bytes()); + } + (format!("segment-table word {} set to {x}", i / 4), v) + } + 4 | 5 => { + // A pointer: its offset, its struct's sizes, or its list's count. + let w = 1 + rng.below(words.saturating_sub(1).max(1)); + let at = 8 * w; + if at + 8 > v.len() { + return ("nothing".into(), v); + } + let mut p = u64::from_le_bytes(v[at..at + 8].try_into().unwrap()); + let what = match (p & 3, rng.below(3)) { + (0, 0) => { + p |= 0xffff_u64 << 32; + "a struct's data size at its largest" + } + (0, 1) => { + p |= 0xffff_u64 << 48; + "a struct's pointer count at its largest" + } + (1, 0) | (1, 1) => { + p = (p & 0x7_ffff_ffff) | (u64::from(BIG32[rng.below(BIG32.len())] >> 3) << 35); + "a list's count past its bounds" + } + _ => { + p = (p & !0xffff_fffc) | (u64::from(BIG32[rng.below(BIG32.len())]) & 0xffff_fffc); + "a pointer's offset past its bounds" + } + }; + v[at..at + 8].copy_from_slice(&p.to_le_bytes()); + (format!("word {w}: {what}"), v) + } + 6 | 7 => { + // A 32-bit field: a register's size, a loop's bound, an index, a length. + let i = 8 + 4 * rng.below(v.len().saturating_sub(8) / 4); + let x = BIG32[rng.below(BIG32.len())]; + if i + 4 <= v.len() { + v[i..i + 4].copy_from_slice(&x.to_le_bytes()); + } + (format!("the 32 bits at {i} set to {x}"), v) + } + _ => { + // A 64-bit field: a 64-bit constant, or an angle. + let i = 8 + 8 * rng.below(words.saturating_sub(1)); + let x = BIG64[rng.below(BIG64.len())]; + if i + 8 <= v.len() { + v[i..i + 8].copy_from_slice(&x.to_le_bytes()); + } + (format!("the 64 bits at {i} set to {x:#x}"), v) + } + } +} + +const QIR_NUMBERS: [&str; 10] = ["4294967295", "18446744073709551615", "65536", "65537", "99999999999999999999999", "-1", "1e308", "nan", "0x7FF8000000000001", "2147483648"]; + +/// A QIR module's text: its numbers, its lines and its branches. +fn qir_mutation(rng: &mut Rng, t: &str, other: &str) -> (String, Vec) { + let lines: Vec<&str> = t.lines().collect(); + match rng.below(7) { + 0 | 1 => { + // A number: an index, a count, a version, an angle. + let spans: Vec<(usize, usize)> = { + let b = t.as_bytes(); + let mut out = Vec::new(); + let mut i = 0; + while i < b.len() { + if b[i].is_ascii_digit() { + let s = i; + while i < b.len() && (b[i].is_ascii_alphanumeric() || b[i] == b'.') { + i += 1; + } + out.push((s, i)); + } else { + i += 1; + } + } + out + }; + if spans.is_empty() { + return ("nothing".into(), t.as_bytes().to_vec()); + } + let (s, e) = spans[rng.below(spans.len())]; + let x = QIR_NUMBERS[rng.below(QIR_NUMBERS.len())]; + (format!("the number at {s} set to {x}"), format!("{}{x}{}", &t[..s], &t[e..]).into_bytes()) + } + 2 => { + // A line repeated many times. + let i = rng.below(lines.len()); + let k = [2usize, 64, 4096, 70_000][rng.below(4)]; + let mut out: Vec<&str> = lines[..i].to_vec(); + for _ in 0..k { + out.push(lines[i]); + } + out.extend_from_slice(&lines[i..]); + (format!("line {} repeated {k} times", i + 1), out.join("\n").into_bytes()) + } + 3 => { + // A branch turned back to the first block, or to itself. + let labels: Vec<&str> = lines.iter().filter_map(|l| l.trim().strip_suffix(':').filter(|x| !x.contains(' '))).collect(); + let to = labels.get(rng.below(labels.len().max(1))).copied().unwrap_or("entry"); + let i = rng.below(lines.len()); + let mut out: Vec = lines.iter().map(|l| l.to_string()).collect(); + out.insert(i, format!(" br i1 true, label %{to}, label %{to}")); + (format!("a branch to %{to} at line {}", i + 1), out.join("\n").into_bytes()) + } + 4 => { + let n = rng.below(t.len()); + let n = (0..=n).rev().find(|&k| t.is_char_boundary(k)).unwrap_or(0); + (format!("cut to {n} bytes"), t[..n].as_bytes().to_vec()) + } + 5 => { + let (i, j) = (rng.below(lines.len()), rng.below(other.lines().count().max(1))); + let mut out: Vec<&str> = lines[..i].to_vec(); + out.extend(other.lines().skip(j)); + (format!("the first {i} lines and another file's from line {}", j + 1), out.join("\n").into_bytes()) + } + _ => { + let mut b = t.as_bytes().to_vec(); + let flips = 1 + rng.below(4); + for _ in 0..flips { + let i = rng.below(b.len()); + b[i] ^= 1 << rng.below(7); + } + (format!("{flips} bits flipped"), b) + } + } +} + +thread_local! { + static CURRENT: std::cell::RefCell = const { std::cell::RefCell::new(String::new()) }; +} + +#[test] +fn every_mutated_module_converts_or_is_refused_with_a_code() { + let bundle: Value = serde_json::from_str(BUNDLE).expect("bundle.json reads"); + let mut files: Vec<(String, Format, Vec)> = Vec::new(); + for c in bundle["cases"].as_array().expect("cases") { + let name = c["file"].as_str().unwrap().to_string(); + match c["format"].as_str() { + Some("jeff") => files.push((name, Format::Jeff, formats::decode_base64(c["base64"].as_str().unwrap()).unwrap())), + Some("qir") => files.push((name, Format::Qir, c["text"].as_str().unwrap().as_bytes().to_vec())), + _ => {} + } + } + assert!(files.len() >= 40, "{} files", files.len()); + // A panic says which input it was, even where a panic aborts. + let hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |info| { + CURRENT.with(|c| eprintln!("the input that panicked: {}", c.borrow())); + hook(info); + })); + let mut rng = Rng(SEED); + let (mut converted, mut refused, mut inputs) = (0usize, 0usize, 0usize); + let mut slowest: Vec<(f64, usize, String)> = Vec::new(); + let mut worst_peak = (0usize, String::new()); + let mut failures = Vec::new(); + for (k, (name, format, bytes)) in files.iter().enumerate() { + // Each file unchanged, then its mutations. + for m in 0..=PER_FILE { + let (what, input) = if m == 0 { + ("unchanged".to_string(), bytes.clone()) + } else { + let others: Vec<&(String, Format, Vec)> = files.iter().filter(|f| f.1 == *format).collect(); + let other = &others[rng.below(others.len())].2; + match format { + Format::Jeff => jeff_mutation(&mut rng, bytes, other), + _ => qir_mutation(&mut rng, std::str::from_utf8(bytes).unwrap(), std::str::from_utf8(other).unwrap()), + } + }; + let label = format!("{name} #{m}: {what}"); + CURRENT.with(|c| *c.borrow_mut() = label.clone()); + let before = LIVE.load(Ordering::Relaxed); + PEAK.store(before, Ordering::Relaxed); + let start = std::time::Instant::now(); + let out = formats::convert(*format, Format::Qasm, &input, None, true); + let secs = start.elapsed().as_secs_f64(); + let peak = PEAK.load(Ordering::Relaxed).saturating_sub(before); + inputs += 1; + match out { + Ok(_) => converted += 1, + Err(d) if wai_quantum::qasm::diag::lookup(d.code).is_some() => refused += 1, + Err(d) => failures.push(format!("{label}: refused without a code of the table: {d}")), + } + if secs > MAX_SECONDS { + failures.push(format!("{label}: took {secs:.2} s, more than {MAX_SECONDS} s")); + } + if peak > MAX_PEAK { + failures.push(format!("{label}: allocated {peak} bytes at its peak, more than {MAX_PEAK}")); + } + if peak > worst_peak.0 { + worst_peak = (peak, label.clone()); + } + slowest.push((secs, k, label)); + } + } + // Every 32-bit field of each small jeff module set to its largest and to + // one past the register's cap, so that each loop bound, register size, + // array length and index the module holds is inflated in turn. + let mut swept = 0usize; + for (k, (name, format, bytes)) in files.iter().enumerate() { + if *format != Format::Jeff || bytes.len() > SWEEP_BYTES { + continue; + } + for i in (8..bytes.len().saturating_sub(3)).step_by(4) { + for x in [u32::MAX, (1 << 16) + 1] { + let mut input = bytes.clone(); + input[i..i + 4].copy_from_slice(&x.to_le_bytes()); + let label = format!("{name}: the 32 bits at {i} set to {x}"); + CURRENT.with(|c| *c.borrow_mut() = label.clone()); + let before = LIVE.load(Ordering::Relaxed); + PEAK.store(before, Ordering::Relaxed); + let start = std::time::Instant::now(); + let out = formats::convert(*format, Format::Qasm, &input, None, true); + let secs = start.elapsed().as_secs_f64(); + let peak = PEAK.load(Ordering::Relaxed).saturating_sub(before); + inputs += 1; + swept += 1; + match out { + Ok(_) => converted += 1, + Err(d) if wai_quantum::qasm::diag::lookup(d.code).is_some() => refused += 1, + Err(d) => failures.push(format!("{label}: refused without a code of the table: {d}")), + } + if secs > MAX_SECONDS { + failures.push(format!("{label}: took {secs:.2} s, more than {MAX_SECONDS} s")); + } + if peak > MAX_PEAK { + failures.push(format!("{label}: allocated {peak} bytes at its peak, more than {MAX_PEAK}")); + } + if peak > worst_peak.0 { + worst_peak = (peak, label.clone()); + } + slowest.push((secs, k, label)); + } + } + } + slowest.sort_by(|a, b| b.0.total_cmp(&a.0)); + eprintln!( + "{inputs} inputs: {converted} converted, {refused} refused with a code; slowest {:.3} s ({}); largest peak {} bytes ({})", + slowest[0].0, slowest[0].2, worst_peak.0, worst_peak.1 + ); + if std::env::var_os("WAI_FORMATS_MUTATION_REPORT").is_some() { + for (s, _, l) in slowest.iter().take(15) { + eprintln!(" {s:.3} s {l}"); + } + } + assert_eq!(inputs - swept, files.len() * (PER_FILE + 1), "the count of inputs is pinned by the files and PER_FILE"); + eprintln!("{swept} of them every 32-bit field of the jeff modules of at most {SWEEP_BYTES} bytes, inflated"); + assert!(failures.is_empty(), "{} of {inputs} inputs:\n{}", failures.len(), failures.join("\n")); +} -- 2.54.0 (Apple Git-157) From f72c8d1250aa4a6c8f0dc25c7bedec04da0c8988 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Fri, 30 Oct 2026 11:59:46 -0400 Subject: [PATCH 06/10] wai-quantum-cli: the stdio MCP run tools take a program's source run_quantum_circuit, plan_quantum_circuit and run_on_target on the command line's server take {"source": {"format", "program" | "program_base64"}} in place of qasm, read to the job's qasm first, as the job service's run tools now declare it too: one schema (engine::formats::source_schema) and one refusal, the reader's code before the job layer's. Reading a source is idempotent: a request whose qasm is what its source reads to (one read already, or sealed) reads as it is, and the service no longer refused its own expanded runs. The tools-list tests call each with a source and with a refusal; the agent docs are re-blessed. Co-Authored-By: Claude Opus 5.5 --- wai/CHANGELOG.md | 4 +- wai/wai-quantum-cli/README.md | 2 +- wai/wai-quantum-cli/README.zh-CN.md | 2 +- wai/wai-quantum-cli/docs/llms-full.txt | 220 +- wai/wai-quantum-cli/docs/local-mcp.json | 123 +- wai/wai-quantum-cli/docs/service-mcp.json | 4796 +------------------ wai/wai-quantum-cli/src/mcp.rs | 42 +- wai/wai-quantum-cli/src/targets.rs | 5 +- wai/wai-quantum-service/src/mcp.rs | 11 +- wai/wai-quantum/src/engine/formats/mod.rs | 36 +- wai/wai-quantum/src/engine/formats/tests.rs | 5 + 11 files changed, 326 insertions(+), 4920 deletions(-) diff --git a/wai/CHANGELOG.md b/wai/CHANGELOG.md index f7712e03e..48ef6c517 100644 --- a/wai/CHANGELOG.md +++ b/wai/CHANGELOG.md @@ -208,7 +208,9 @@ wire format may change and such changes are recorded here. by reading the output back and holding it to the program with the equivalence ladder (a program that branches, step for step or by every outcome's probability); every verb reads `.ll`, `.qir` and `.jeff` files, or `--from`. Both MCP servers serve `convert_program` (`tools.v1.json#/$defs/convert`); the job service answers `POST /v1/convert`, and a - job may carry `{"source": {"format", "program" | "program_base64"}}` in place of `qasm` (`job-request.v1.json`). + job may carry `{"source": {"format", "program" | "program_base64"}}` in place of `qasm` (`job-request.v1.json`), as + both MCP servers' run tools take it (`run_quantum_circuit`, `plan_quantum_circuit`, `run_on_target` and the dry run), + one schema and one refusal; a request whose `qasm` is what its `source` reads to is read as it is. The Studio's engine has `engine_convert`. - **Hostile input:** every size, count, index and bound a file gives is held to a cap before anything is made from it (a register's size before its qubits: a jeff register of 2^32 − 1 qubits is refused, `Q111`, where it once asked diff --git a/wai/wai-quantum-cli/README.md b/wai/wai-quantum-cli/README.md index 12c2d149a..61baf34d9 100644 --- a/wai/wai-quantum-cli/README.md +++ b/wai/wai-quantum-cli/README.md @@ -360,7 +360,7 @@ wai-quantum verify sweep.json # the set's receipt, each item's, the r `wai-quantum mcp` is a Model Context Protocol server on standard input and output. Its tools are: -- `run_quantum_circuit` (with `intent` and `repair`; its verdict in every answer) +- `run_quantum_circuit` (with `intent` and `repair`; its verdict in every answer; its program as `qasm`, or in another format as `source`, as the job service takes it) - `plan_quantum_circuit` - `lint_qasm` - `repair_qasm` (verdicts and repairs, above) diff --git a/wai/wai-quantum-cli/README.zh-CN.md b/wai/wai-quantum-cli/README.zh-CN.md index 52a47fe88..ec39a8c06 100644 --- a/wai/wai-quantum-cli/README.zh-CN.md +++ b/wai/wai-quantum-cli/README.zh-CN.md @@ -358,7 +358,7 @@ wai-quantum verify sweep.json # 程序集的回执、每一项的回 `wai-quantum mcp` 是一个基于标准输入输出的 Model Context Protocol(MCP)服务器。它的工具有: -- `run_quantum_circuit`(带 `intent` 和 `repair`;每个回答中都有其判定) +- `run_quantum_circuit`(带 `intent` 和 `repair`;每个回答中都有其判定;程序以 `qasm` 给出,或以其他格式作为 `source` 给出,与作业服务的接受方式相同) - `plan_quantum_circuit` - `lint_qasm` - `repair_qasm`(判定与修复,见上文) diff --git a/wai/wai-quantum-cli/docs/llms-full.txt b/wai/wai-quantum-cli/docs/llms-full.txt index 1eb53d4dc..34df5e21e 100644 --- a/wai/wai-quantum-cli/docs/llms-full.txt +++ b/wai/wai-quantum-cli/docs/llms-full.txt @@ -46,11 +46,12 @@ Runs on this machine, signed with the user's key; nothing leaves the machine. Run an OpenQASM program on this machine, planned as the quantum job service plans it: measurement counts, Pauli expectation values and/or amplitudes, the method chosen and why, its accuracy class, and a receipt signed with this user's key. Give `intent` (an exact distribution, or a reference program it must equal or whose state it must prepare) and the engine checks it with the run. Every answer carries `verdict`: the level the engine checked to (proof, evidence, executed, parsed, refused), whether the intent holds and a plain statement, sealed in the receipt. Quote the verdict and the numbers; do not estimate them or claim more. Parameters: -- `qasm` (string, required): The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in "inputs"). while, inv and pow are refused with their line. +- `source` (object): The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source. +- `qasm` (string): The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in "inputs"). while, inv and pow are refused with their line. - `inputs` (object): Values for a generator's integer inputs, as {"n": 5}: the program is read as its family's circuit at those values. - `shots` (integer): Shots to sample; 1024 when nothing else is asked. - `seed` (integer): The sampling seed (default 1). -- `method` (string): auto (the default: the strongest method the program admits), or one of list_methods' names; pulse runs a pulse program (OpenPulse calibrations on physical qubits, see pulse_schedule) under the pulse options, its error bounded. +- `method` (string): auto (the default: the strongest method the program admits), or one of list_methods' names. - `observables` (array): Pauli observables, as "Z0 Z1" or "0.5 X2". - `amplitudes` (array): Basis states whose amplitudes to return, as bit strings with the highest qubit first. - `noise` (object): Channel probabilities @@ -60,18 +61,18 @@ Parameters: - `intent` (any): What the program must do, checked by the engine with the run: the result's "verdict" states how far it checked (proof, evidence, executed, parsed, refused) and whether the intent holds, sealed by the receipt. - `repair` (any): Repair the program first, on the lines its diagnostics flag alone (as repair_qasm does): true, or the choices {"max_iterations": N, "dyadic_bits": K, "sizes": {"q": 3}}. The result's "repaired" holds the program that ran and each repair. - `mitigate` (any): Mitigate the job's expectation values (or, for readout, its counts) under its own noise: zne (the whole program folded U(U†U)^k and extrapolated), pea (the noise learned as a sparse Pauli–Lindblad model and amplified), pec (the learned channels inverted and sampled), cdr (a line fitted over near-Clifford training circuits), vd (two copies), readout (the counts' flips inverted). Needs noise and observables (shots for readout), at most 8 qubits. The result's `mitigation` gives raw and mitigated values with standard errors, the ideal value (known because this is a simulation), the method's bias and variance assumptions and its cost. A method's name, or an object. -- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. ### `plan_quantum_circuit`: Plan a quantum program The method a program would run on and why, the memory and work it would take, without running it. Parameters: -- `qasm` (string, required): The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in "inputs"). while, inv and pow are refused with their line. +- `source` (object): The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source. +- `qasm` (string): The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in "inputs"). while, inv and pow are refused with their line. - `inputs` (object): Values for a generator's integer inputs, as {"n": 5}: the program is read as its family's circuit at those values. - `shots` (integer): Shots to sample; 1024 when nothing else is asked. - `seed` (integer): The sampling seed (default 1). -- `method` (string): auto (the default: the strongest method the program admits), or one of list_methods' names; pulse runs a pulse program (OpenPulse calibrations on physical qubits, see pulse_schedule) under the pulse options, its error bounded. +- `method` (string): auto (the default: the strongest method the program admits), or one of list_methods' names. - `observables` (array): Pauli observables, as "Z0 Z1" or "0.5 X2". - `amplitudes` (array): Basis states whose amplitudes to return, as bit strings with the highest qubit first. - `noise` (object): Channel probabilities @@ -81,7 +82,6 @@ Parameters: - `intent` (any): What the program must do, checked by the engine with the run: the result's "verdict" states how far it checked (proof, evidence, executed, parsed, refused) and whether the intent holds, sealed by the receipt. - `repair` (any): Repair the program first, on the lines its diagnostics flag alone (as repair_qasm does): true, or the choices {"max_iterations": N, "dyadic_bits": K, "sizes": {"q": 3}}. The result's "repaired" holds the program that ran and each repair. - `mitigate` (any): Mitigate the job's expectation values (or, for readout, its counts) under its own noise: zne (the whole program folded U(U†U)^k and extrapolated), pea (the noise learned as a sparse Pauli–Lindblad model and amplified), pec (the learned channels inverted and sampled), cdr (a line fitted over near-Clifford training circuits), vd (two copies), readout (the counts' flips inverted). Needs noise and observables (shots for readout), at most 8 qubits. The result's `mitigation` gives raw and mitigated values with standard errors, the ideal value (known because this is a simulation), the method's bias and variance assumptions and its cost. A method's name, or an object. -- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. ### `lint_qasm`: Check an OpenQASM program @@ -408,11 +408,12 @@ Parameters: Run an OpenQASM program here as it would run on a device target: compiled exactly to the target's native gates, placed on the qubits and routes most likely to succeed (the expected success given beside the fewest-swaps layout's), checked against the circuit, and simulated under the noise its calibration gives (each gate's error, T1, T2 and durations, idling, and each qubit's readout), on the density matrix or by trajectories. Counts and expectation values, their distance from the ideal distribution, the noise derived, and a receipt signed with this user's key that binds the target's hash. A model of the device, not a run on it: quote it as such. Every angle must be a dyadic multiple of 2π. Parameters: -- `qasm` (string, required): The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in "inputs"). while, inv and pow are refused with their line. +- `source` (object): The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source. +- `qasm` (string): The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in "inputs"). while, inv and pow are refused with their line. - `inputs` (object): Values for a generator's integer inputs, as {"n": 5}: the program is read as its family's circuit at those values. - `shots` (integer): Shots to sample; 1024 when nothing else is asked. - `seed` (integer): The sampling seed (default 1). -- `method` (string): auto (the default: the strongest method the program admits), or one of list_methods' names; pulse runs a pulse program (OpenPulse calibrations on physical qubits, see pulse_schedule) under the pulse options, its error bounded. +- `method` (string): auto (the default: the strongest method the program admits), or one of list_methods' names. - `observables` (array): Pauli observables, as "Z0 Z1" or "0.5 X2". - `amplitudes` (array): Basis states whose amplitudes to return, as bit strings with the highest qubit first. - `noise` (object): Channel probabilities @@ -422,7 +423,6 @@ Parameters: - `intent` (any): What the program must do, checked by the engine with the run: the result's "verdict" states how far it checked (proof, evidence, executed, parsed, refused) and whether the intent holds, sealed by the receipt. - `repair` (any): Repair the program first, on the lines its diagnostics flag alone (as repair_qasm does): true, or the choices {"max_iterations": N, "dyadic_bits": K, "sizes": {"q": 3}}. The result's "repaired" holds the program that ran and each repair. - `mitigate` (any): Mitigate the job's expectation values (or, for readout, its counts) under its own noise: zne (the whole program folded U(U†U)^k and extrapolated), pea (the noise learned as a sparse Pauli–Lindblad model and amplified), pec (the learned channels inverted and sampled), cdr (a line fitted over near-Clifford training circuits), vd (two copies), readout (the counts' flips inverted). Needs noise and observables (shots for readout), at most 8 qubits. The result's `mitigation` gives raw and mitigated values with standard errors, the ideal value (known because this is a simulation), the method's bias and variance assumptions and its cost. A method's name, or an object. -- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. ### `qec_code`: Show an error-correcting code and certify its distance @@ -465,22 +465,6 @@ Parameters: - `rounds` (integer): surface: rounds (default the distance). - `seed` (integer) -### `pulse_schedule`: Read a pulse program into its schedule - -Read a pulse program (OpenPulse calibrations: ports, frames, waveforms, play, delay, shift_phase, set_phase, shift_frequency, set_frequency, captures and barriers) into its schedule on frames: every instruction at its sample, each waveform's samples (gaussian, drag, gaussian_square, constant, sech, sine, or explicit), each gate call with the samples its calibration spans, and the digests a pulse run's receipt binds (the program, its calibrations, the schedule). Durations are whole samples of dt (pulse.dt_ns, default 0.5 ns) or refused. A construct the reader does not take is refused with a Q code and its line. Sealed with this user's key. To run it, give the program to run_quantum_circuit with method "pulse". - -Parameters: -- `qasm` (string, required): A pulse program: OpenQASM 3 with `defcalgrammar "openpulse";`, `cal` blocks declaring ports (d0 drives qubit 0, m0 is its measurement port, a0 its acquisition port), frames (newframe(port, frequency Hz, phase)) and waveforms, a `defcal` for every gate it calls on physical qubits ($0), and the calls. -- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. - -### `pulse_check`: Check a pulse run by running it again - -Check a pulse job as a host returned it ({"request", "result"}, the result with its receipt): the receipt's signature, the program's digest against the request's text, and the request run again here with the result held to the one given field by field (the density matrix's digest among them), each difference named. A pulse run is the same bits on every platform, so a result that does not reproduce was not made from that request by this engine. Sealed with this user's key. Quote `verified` and the statement. - -Parameters: -- `request` (object, required): The pulse job's request. -- `result` (object, required): Its result, with its receipt. - ### `providers_list`: List the person's quantum providers The person's own quantum providers, as their registry lists them: each one's name, kind, protocol, endpoint or command, and where its credential is kept (never the credential itself). With free: true, also what anyone can use at no cost: the engine's methods on this machine with their limits, the free job service, and each provider whose own description states free use (this asks each provider to describe itself). @@ -520,12 +504,12 @@ Streamable HTTP, no account and no key. Run an OpenQASM program and return measurement counts, Pauli expectation values and/or amplitudes. The service chooses the method (bit-exact or double-precision state vector up to 32 qubits, single precision on a GPU to 33, stabilizer tableau for Clifford circuits up to 32768 qubits, matrix product states up to 4096, sparse Pauli dynamics for expectation values up to 1024 with an error bound, an exact density matrix or trajectories for noise), reports why and how accurate, and signs the result. Give `intent` (an exact distribution, or a reference program it must equal or whose state it must prepare) and the engine checks it with the run. Every answer carries `verdict`: the level the engine checked to (proof, evidence, executed, parsed, refused), whether the intent holds and a plain statement, sealed in the receipt. Waits up to 50 s; a longer job answers with status running and an id for get_quantum_job. Parameters: -- `qasm` (string, required): The program, OpenQASM 3 (or 2), read as the whole language: registers, aliases and slices; the standard gates, `U` and `gphase`; user `gate` definitions and `def` subroutines, inlined; every modifier (`ctrl @`, `negctrl @`, `inv @`, `pow(k) @`) on any gate, lowered exactly; classical types (int, uint, float, angle, complex, bool, bit, arrays), their arithmetic and built-in functions, computed on reading; `for`, `while`, `switch`, `break`, `continue`; measurement and `reset` anywhere; classical control on measured bits (`if (c[0]) x q[1];`, `if (c >= 2) { … } else { … }`, `&&`, `||`); a `while` on measured bits takes `@wai.max_iterations N` before it. `output` declarations come back in the result; `delay`, `box` and `stretch` are carried and labelled as not applied; `defcal` and `cal` are kept, not run. A program that measures in the middle or acts on what it measured runs on statevector or stabilizer: counts split their shots at each measurement; expectation values are exact, of the state before the final measurements. What the reader refuses comes back with a code, as `[Q103]`; qasm_diagnostics says what each code means. +- `qasm` (string): The program, OpenQASM 3 (or 2), read as the whole language: registers, aliases and slices; the standard gates, `U` and `gphase`; user `gate` definitions and `def` subroutines, inlined; every modifier (`ctrl @`, `negctrl @`, `inv @`, `pow(k) @`) on any gate, lowered exactly; classical types (int, uint, float, angle, complex, bool, bit, arrays), their arithmetic and built-in functions, computed on reading; `for`, `while`, `switch`, `break`, `continue`; measurement and `reset` anywhere; classical control on measured bits (`if (c[0]) x q[1];`, `if (c >= 2) { … } else { … }`, `&&`, `||`); a `while` on measured bits takes `@wai.max_iterations N` before it. `output` declarations come back in the result; `delay`, `box` and `stretch` are carried and labelled as not applied; `defcal` and `cal` are kept, not run. A program that measures in the middle or acts on what it measured runs on statevector or stabilizer: counts split their shots at each measurement; expectation values are exact, of the state before the final measurements. What the reader refuses comes back with a code, as `[Q103]`; qasm_diagnostics says what each code means. - `shots` (integer): Measurement shots. Default 1024 when no observables or amplitudes are asked for. - `observables` (array): Pauli strings to take expectation values of, e.g. ["Z0", "Z0 Z1", "X2 Y3"]. Qubits count from 0. - `amplitudes` (array): Basis states to give amplitudes of: one character per qubit, qubit n-1 first, e.g. "0011". - `noise` (object): Optional noise, each a probability: depolarizing_1q, depolarizing_2q, amplitude_damping, dephasing (after each gate on its qubits), readout (each measured bit). -- `method` (string, one of auto, exact, statevector, statevector_single, stabilizer, pauli, mps, tensor_network, cutrank, cutrank_single, density_matrix, trajectories, pulse): Leave as auto unless you need a particular method; pulse runs a pulse program (OpenPulse calibrations on physical qubits, see pulse_schedule) under the pulse options, its error bounded. +- `method` (string, one of auto, exact, statevector, statevector_single, stabilizer, pauli, mps, tensor_network, cutrank, cutrank_single, density_matrix, trajectories): Leave as auto unless you need a particular method. - `seed` (integer): Sampling seed (default 1); the same job and seed give the same counts. - `options` (object): Advanced, per method: max_bond (mps), threshold and max_terms (pauli), fusion (statevector), trajectories. - `compile` (object): Compile the program first (as compile_qasm does) and run the compiled circuit: with basis clifford_t it runs bit-exact, and the result's `compiled` states its distance from the program. @@ -533,7 +517,7 @@ Parameters: - `intent` (any): What the program must do, checked by the engine with the run: the result's `verdict` states how far it checked (proof, evidence, executed, parsed, refused) and whether the intent holds, sealed by the receipt. - `repair` (any): Repair the program first, on the lines its diagnostics flag alone (as repair_qasm does): true, or the choices {"max_iterations": N, "dyadic_bits": K, "sizes": {"q": 3}}. The result's `repaired` holds the program that ran and each repair. - `mitigate` (any): Mitigate the job's expectation values (or, for readout, its counts) under its own noise: zne (the whole program folded U(U†U)^k and extrapolated), pea (the noise learned as a sparse Pauli–Lindblad model and amplified), pec (the learned channels inverted and sampled), cdr (a line fitted over near-Clifford training circuits), vd (two copies), readout (the counts' flips inverted). Needs noise and observables (shots for readout), at most 8 qubits. The result's `mitigation` gives raw and mitigated values with standard errors, the ideal value (known because this is a simulation), the method's bias and variance assumptions and its cost. A method's name, or an object. -- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. +- `source` (object): The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source. ### `run_quantum_set`: Run a set of quantum programs, or one over a grid @@ -545,7 +529,7 @@ Parameters: - `observables` (array): Pauli strings to take expectation values of, e.g. ["Z0", "Z0 Z1", "X2 Y3"]. Qubits count from 0. - `amplitudes` (array): Basis states to give amplitudes of: one character per qubit, qubit n-1 first, e.g. "0011". - `noise` (object): Optional noise, each a probability: depolarizing_1q, depolarizing_2q, amplitude_damping, dephasing (after each gate on its qubits), readout (each measured bit). -- `method` (string, one of auto, exact, statevector, statevector_single, stabilizer, pauli, mps, tensor_network, cutrank, cutrank_single, density_matrix, trajectories, pulse): Leave as auto unless you need a particular method; pulse runs a pulse program (OpenPulse calibrations on physical qubits, see pulse_schedule) under the pulse options, its error bounded. +- `method` (string, one of auto, exact, statevector, statevector_single, stabilizer, pauli, mps, tensor_network, cutrank, cutrank_single, density_matrix, trajectories): Leave as auto unless you need a particular method. - `seed` (integer): Sampling seed (default 1); the same job and seed give the same counts. - `options` (object): Advanced, per method: max_bond (mps), threshold and max_terms (pauli), fusion (statevector), trajectories. - `compile` (object): Compile the program first (as compile_qasm does) and run the compiled circuit: with basis clifford_t it runs bit-exact, and the result's `compiled` states its distance from the program. @@ -553,7 +537,6 @@ Parameters: - `intent` (any): What the program must do, checked by the engine with the run: the result's `verdict` states how far it checked (proof, evidence, executed, parsed, refused) and whether the intent holds, sealed by the receipt. - `repair` (any): Repair the program first, on the lines its diagnostics flag alone (as repair_qasm does): true, or the choices {"max_iterations": N, "dyadic_bits": K, "sizes": {"q": 3}}. The result's `repaired` holds the program that ran and each repair. - `mitigate` (any): Mitigate the job's expectation values (or, for readout, its counts) under its own noise: zne (the whole program folded U(U†U)^k and extrapolated), pea (the noise learned as a sparse Pauli–Lindblad model and amplified), pec (the learned channels inverted and sampled), cdr (a line fitted over near-Clifford training circuits), vd (two copies), readout (the counts' flips inverted). Needs noise and observables (shots for readout), at most 8 qubits. The result's `mitigation` gives raw and mitigated values with standard errors, the ideal value (known because this is a simulation), the method's bias and variance assumptions and its cost. A method's name, or an object. -- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. - `programs` (array): The set's programs: each a program's text, or a job ({"qasm", …}) whose own fields win over the set's. - `sweep` (object): Each input of qasm and its values: every combination runs, the names in sorted order, the first varying slowest; an integer input's values are whole numbers. @@ -567,7 +550,7 @@ Parameters: - `observables` (array): Pauli strings to take expectation values of, e.g. ["Z0", "Z0 Z1", "X2 Y3"]. Qubits count from 0. - `amplitudes` (array): Basis states to give amplitudes of: one character per qubit, qubit n-1 first, e.g. "0011". - `noise` (object): Optional noise, each a probability: depolarizing_1q, depolarizing_2q, amplitude_damping, dephasing (after each gate on its qubits), readout (each measured bit). -- `method` (string, one of auto, exact, statevector, statevector_single, stabilizer, pauli, mps, tensor_network, cutrank, cutrank_single, density_matrix, trajectories, pulse): Leave as auto unless you need a particular method; pulse runs a pulse program (OpenPulse calibrations on physical qubits, see pulse_schedule) under the pulse options, its error bounded. +- `method` (string, one of auto, exact, statevector, statevector_single, stabilizer, pauli, mps, tensor_network, cutrank, cutrank_single, density_matrix, trajectories): Leave as auto unless you need a particular method. - `seed` (integer): Sampling seed (default 1); the same job and seed give the same counts. - `options` (object): Advanced, per method: max_bond (mps), threshold and max_terms (pauli), fusion (statevector), trajectories. - `compile` (object): Compile the program first (as compile_qasm does) and run the compiled circuit: with basis clifford_t it runs bit-exact, and the result's `compiled` states its distance from the program. @@ -575,9 +558,9 @@ Parameters: - `intent` (any): What the program must do, checked by the engine with the run: the result's `verdict` states how far it checked (proof, evidence, executed, parsed, refused) and whether the intent holds, sealed by the receipt. - `repair` (any): Repair the program first, on the lines its diagnostics flag alone (as repair_qasm does): true, or the choices {"max_iterations": N, "dyadic_bits": K, "sizes": {"q": 3}}. The result's `repaired` holds the program that ran and each repair. - `mitigate` (any): Mitigate the job's expectation values (or, for readout, its counts) under its own noise: zne (the whole program folded U(U†U)^k and extrapolated), pea (the noise learned as a sparse Pauli–Lindblad model and amplified), pec (the learned channels inverted and sampled), cdr (a line fitted over near-Clifford training circuits), vd (two copies), readout (the counts' flips inverted). Needs noise and observables (shots for readout), at most 8 qubits. The result's `mitigation` gives raw and mitigated values with standard errors, the ideal value (known because this is a simulation), the method's bias and variance assumptions and its cost. A method's name, or an object. -- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. - `programs` (array): The set's programs: each a program's text, or a job ({"qasm", …}) whose own fields win over the set's. - `sweep` (object): Each input of qasm and its values: every combination runs, the names in sorted order, the first varying slowest; an integer input's values are whole numbers. +- `source` (object): The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source. ### `get_quantum_job`: Read a quantum job @@ -701,12 +684,12 @@ Parameters: Run an OpenQASM program as it would run on a device target: compiled exactly to its gates, placed on its most reliable qubits and routes (the expected success given), checked against the circuit, and simulated under the noise its calibration gives, on the density matrix or by trajectories. Counts and expectation values with their distance from the ideal distribution, the noise derived, and a receipt that binds the target's hash. A model of the device, not a run on it. Every angle must be a dyadic multiple of 2π. Waits up to 50 s, as run_quantum_circuit does. Parameters: -- `qasm` (string, required): The program, OpenQASM 3 (or 2), read as the whole language: registers, aliases and slices; the standard gates, `U` and `gphase`; user `gate` definitions and `def` subroutines, inlined; every modifier (`ctrl @`, `negctrl @`, `inv @`, `pow(k) @`) on any gate, lowered exactly; classical types (int, uint, float, angle, complex, bool, bit, arrays), their arithmetic and built-in functions, computed on reading; `for`, `while`, `switch`, `break`, `continue`; measurement and `reset` anywhere; classical control on measured bits (`if (c[0]) x q[1];`, `if (c >= 2) { … } else { … }`, `&&`, `||`); a `while` on measured bits takes `@wai.max_iterations N` before it. `output` declarations come back in the result; `delay`, `box` and `stretch` are carried and labelled as not applied; `defcal` and `cal` are kept, not run. A program that measures in the middle or acts on what it measured runs on statevector or stabilizer: counts split their shots at each measurement; expectation values are exact, of the state before the final measurements. What the reader refuses comes back with a code, as `[Q103]`; qasm_diagnostics says what each code means. +- `qasm` (string): The program, OpenQASM 3 (or 2), read as the whole language: registers, aliases and slices; the standard gates, `U` and `gphase`; user `gate` definitions and `def` subroutines, inlined; every modifier (`ctrl @`, `negctrl @`, `inv @`, `pow(k) @`) on any gate, lowered exactly; classical types (int, uint, float, angle, complex, bool, bit, arrays), their arithmetic and built-in functions, computed on reading; `for`, `while`, `switch`, `break`, `continue`; measurement and `reset` anywhere; classical control on measured bits (`if (c[0]) x q[1];`, `if (c >= 2) { … } else { … }`, `&&`, `||`); a `while` on measured bits takes `@wai.max_iterations N` before it. `output` declarations come back in the result; `delay`, `box` and `stretch` are carried and labelled as not applied; `defcal` and `cal` are kept, not run. A program that measures in the middle or acts on what it measured runs on statevector or stabilizer: counts split their shots at each measurement; expectation values are exact, of the state before the final measurements. What the reader refuses comes back with a code, as `[Q103]`; qasm_diagnostics says what each code means. - `shots` (integer): Measurement shots. Default 1024 when no observables or amplitudes are asked for. - `observables` (array): Pauli strings to take expectation values of, e.g. ["Z0", "Z0 Z1", "X2 Y3"]. Qubits count from 0. - `amplitudes` (array): Basis states to give amplitudes of: one character per qubit, qubit n-1 first, e.g. "0011". - `noise` (object): Optional noise, each a probability: depolarizing_1q, depolarizing_2q, amplitude_damping, dephasing (after each gate on its qubits), readout (each measured bit). -- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. +- `source` (object): The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source. - `seed` (integer): Sampling seed (default 1); the same job and seed give the same counts. - `options` (object): Advanced, per method: max_bond (mps), threshold and max_terms (pauli), fusion (statevector), trajectories. - `compile` (object): Compile the program first (as compile_qasm does) and run the compiled circuit: with basis clifford_t it runs bit-exact, and the result's `compiled` states its distance from the program. @@ -760,22 +743,6 @@ Parameters: - `rounds` (integer): surface: rounds (default the distance). - `seed` (integer) -### `pulse_schedule`: Read a pulse program into its schedule - -Read a pulse program (OpenPulse calibrations: ports, frames, waveforms, play, delay, shift_phase, set_phase, shift_frequency, set_frequency, captures and barriers) into its schedule on frames: every instruction at its sample, each waveform's samples (gaussian, drag, gaussian_square, constant, sech, sine, or explicit), each gate call with the samples its calibration spans, and the digests a pulse run's receipt binds (the program, its calibrations, the schedule). Durations are whole samples of dt (pulse.dt_ns, default 0.5 ns) or refused. A construct the reader does not take is refused with a Q code and its line. Sealed with the service's key. To run it, give the program to run_quantum_circuit with method "pulse". - -Parameters: -- `qasm` (string, required): A pulse program: OpenQASM 3 with `defcalgrammar "openpulse";`, `cal` blocks declaring ports (d0 drives qubit 0, m0 is its measurement port, a0 its acquisition port), frames (newframe(port, frequency Hz, phase)) and waveforms, a `defcal` for every gate it calls on physical qubits ($0), and the calls. -- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. - -### `pulse_check`: Check a pulse run by running it again - -Check a pulse job as a host returned it ({"request", "result"}, the result with its receipt): the receipt's signature, the program's digest against the request's text, and the request run again here with the result held to the one given field by field (the density matrix's digest among them), each difference named. A pulse run is the same bits on every platform, so a result that does not reproduce was not made from that request by this engine. Sealed with the service's key. Quote `verified` and the statement. - -Parameters: -- `request` (object, required): The pulse job's request. -- `result` (object, required): Its result, with its receipt. - ## Resources, on both servers ### `wai-quantum://docs/language`: the OpenQASM the engine reads @@ -927,15 +894,9 @@ Each construct below is supported (read as the language defines it), lowered (re | 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. +| `defcalgrammar "openpulse";` | preserved | Q452 | +| `cal { … }` | preserved, never run | Q451 | +| `defcal …` | preserved, never run; the gate's matrix comes from its definition or the library | Q450, Q126 | #### The bit-exact method @@ -1077,16 +1038,6 @@ Every code: | `Q450` | preserved | `defcal`: kept verbatim for export, never executed; the gate's matrix comes from its `gate` definition or the standard library. | | `Q451` | preserved | `cal { … }`: kept verbatim for export, never executed. | | `Q452` | preserved | `defcalgrammar`: kept verbatim for export. | -| `Q453` | lowered | In a pulse run, a `cal` or `defcal` body read as OpenPulse and lowered to the schedule on frames (plays, delays, phase and frequency changes, captures and barriers) that the pulse simulator integrates. | -| `Q454` | refused | In a pulse run, a `defcalgrammar` other than `"openpulse"`: the pulse reader reads OpenPulse alone. | -| `Q455` | refused | A statement in a calibration that the pulse reader does not take: classical control, a frame's attributes or `get_phase`, an `extern` function, a frame made inside a `defcal`, a nested calibration, or a `defcal` over qubit arguments (the message names it and what to write instead). | -| `Q456` | refused | A waveform that does not read: a template the reader does not know, arguments of the wrong number or kind, a length or a width that is not positive, a sample that is not a number, or more samples than it holds. | -| `Q457` | refused | A frame or a port that does not read: a play, a delay or a phase or frequency change on a name that is not a frame, `newframe` on a name that is not a port, a frequency that is not a number, or a port that reaches no qubit. | -| `Q458` | refused | A duration in a calibration that is not a whole, non-negative number of samples at the sample period (`dt`), or a schedule longer than the reader holds. | -| `Q459` | refused | In a pulse run, a gate or a measurement on physical qubits that no `defcal` calibrates for those qubits and those parameters. | -| `Q460` | refused | In a pulse run, a statement outside the calibrations that the pulse reader does not take: declared qubits (a pulse program names physical qubits, `$0`), a modifier, `reset`, classical control, a subroutine, or a measurement into something other than a bit. | -| `Q461` | refused | A capture that does not read: not on a frame of an acquisition port, without a duration or a waveform, or its result put somewhere other than a bit. | -| `Q462` | warned | A play on a measurement port: scheduled and timed, its effect on the readout not simulated (a capture reads the qubit's state). | | `Q500` | refused | An angle that is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32): the bit-exact method refuses it, never rounds it; the methods in double precision take it. | | `Q501` | refused | More than 255 qubits for the bit-exact circuit. | | `Q502` | refused | A program that measures a qubit and then acts on it, resets one after its first gate, or acts on measured bits: the bit-exact circuit takes programs whose measurements all come at the end. | @@ -1127,7 +1078,6 @@ Every code: | `cutrank_single` | single | as for cutrank; the table on a GPU, its arithmetic in single precision | counts, amplitudes | | `density_matrix` | exact | any, with noise | counts, expectations | | `trajectories` | estimate | any, with noise | counts, expectations | -| `pulse` | bounded | pulse programs: OpenPulse calibrations (cal, defcal) on physical qubits, every gate called with its calibration | counts, populations, captures | The accuracy classes: @@ -1454,9 +1404,6 @@ Commands: qec Error correction: a code and its certified distance, a syndrome or a circuit's detectors decoded, a threshold measured, each answer signed (`wai-quantum qec --help`) - pulse Pulse programs: a program's schedule on frames drawn as a timeline, and a pulse job - checked by running it again; a pulse program runs with `run --method pulse` - (`wai-quantum pulse --help`) eval Score a model on a task set with the engine: whether its program reads, the verdict's level and whether the intent holds, and the joules per solved task where its server's energy is metered; or write each task's answer three ways for a token count @@ -1598,10 +1545,6 @@ Options: pea, pec, cdr, vd or readout, or its options as JSON ({"method": "pea", "factors": [1, 1.5, 2]}) - --pulse - A pulse run's options (with --method pulse), as JSON or a file of it: dt_ns, levels, - tolerance, substeps, qubits, couplings and ports (`wai-quantum pulse --help`) - --json Print the job as the service returns it: request, plan, result and receipt @@ -1732,10 +1675,6 @@ Options: pea, pec, cdr, vd or readout, or its options as JSON ({"method": "pea", "factors": [1, 1.5, 2]}) - --pulse - A pulse run's options (with --method pulse), as JSON or a file of it: dt_ns, levels, - tolerance, substeps, qubits, couplings and ports (`wai-quantum pulse --help`) - --json Print the plan as JSON @@ -2464,10 +2403,6 @@ Options: pea, pec, cdr, vd or readout, or its options as JSON ({"method": "pea", "factors": [1, 1.5, 2]}) - --pulse - A pulse run's options (with --method pulse), as JSON or a file of it: dt_ns, levels, - tolerance, substeps, qubits, couplings and ports (`wai-quantum pulse --help`) - --json Print the job as the service returns it: request, plan, result and receipt @@ -5247,121 +5182,6 @@ Options: Print version ``` -### `wai-quantum pulse` - -```text -Pulse programs: a program's schedule on frames drawn as a timeline, and a pulse job checked by -running it again; a pulse program runs with `run --method pulse` (`wai-quantum pulse --help`) - -Usage: wai-quantum pulse [OPTIONS] - -Commands: - schedule A pulse program's schedule: each frame's timeline, every instruction at its sample, the - waveforms and the gate calls - check A pulse job (`run --method pulse --json`) checked: its receipt, its program's digest, - and the request run again here, its result held to the one given - help Print this message or the help of the given subcommand(s) - -Options: - --input - A value for a generator's integer `input`, as n=5 (repeat for each input): the program is - read as its family's circuit at those values - - --lang - The language of the words the command line writes itself, en or zh-CN (by default the one - LANG names); results, codes and the engine's own messages stay in English - - -h, --help - Print help - - -V, --version - Print version -``` - -### `wai-quantum pulse schedule` - -```text -A pulse program's schedule: each frame's timeline, every instruction at its sample, the waveforms -and the gate calls - -Usage: wai-quantum pulse schedule [OPTIONS] [FILE] - -Arguments: - [FILE] - The program; `-` or nothing reads standard input - -Options: - --input - A value for a generator's integer `input`, as n=5 (repeat for each input): the program is - read as its family's circuit at those values - - --pulse - The pulse options, as JSON or a file of it (only dt_ns and ports matter here) - - --lang - The language of the words the command line writes itself, en or zh-CN (by default the one - LANG names); results, codes and the engine's own messages stay in English - - --width - The timeline's width, in columns - - [default: 60] - - --json - Print the answer sealed with this user's key, as JSON (`wai-quantum verify` checks it) - - --key - Sign with the key in this file (64 hex digits) - - --demo-key - Sign with the published demo key: the receipt then shows a result is unaltered, not who - signed it - - -h, --help - Print help - - -V, --version - Print version -``` - -### `wai-quantum pulse check` - -```text -A pulse job (`run --method pulse --json`) checked: its receipt, its program's digest, and the -request run again here, its result held to the one given - -Usage: wai-quantum pulse check [OPTIONS] [FILE] - -Arguments: - [FILE] - The job, as `run --json` or the service returns it; `-` reads standard input - -Options: - --input - A value for a generator's integer `input`, as n=5 (repeat for each input): the program is - read as its family's circuit at those values - - --json - Print the answer sealed with this user's key, as JSON (`wai-quantum verify` checks it) - - --key - Sign with the key in this file (64 hex digits) - - --lang - The language of the words the command line writes itself, en or zh-CN (by default the one - LANG names); results, codes and the engine's own messages stay in English - - --demo-key - Sign with the published demo key: the receipt then shows a result is unaltered, not who - signed it - - -h, --help - Print help - - -V, --version - Print version -``` - ### `wai-quantum eval` ```text diff --git a/wai/wai-quantum-cli/docs/local-mcp.json b/wai/wai-quantum-cli/docs/local-mcp.json index b9649dc2b..813589482 100644 --- a/wai/wai-quantum-cli/docs/local-mcp.json +++ b/wai/wai-quantum-cli/docs/local-mcp.json @@ -7,10 +7,45 @@ "description": "Run an OpenQASM program on this machine, planned as the quantum job service plans it: measurement counts, Pauli expectation values and/or amplitudes, the method chosen and why, its accuracy class, and a receipt signed with this user's key. Give `intent` (an exact distribution, or a reference program it must equal or whose state it must prepare) and the engine checks it with the run. Every answer carries `verdict`: the level the engine checked to (proof, evidence, executed, parsed, refused), whether the intent holds and a plain statement, sealed in the receipt. Quote the verdict and the numbers; do not estimate them or claim more.", "inputSchema": { "type": "object", - "required": [ - "qasm" + "anyOf": [ + { + "required": [ + "qasm" + ] + }, + { + "required": [ + "source" + ] + } ], "properties": { + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source.", + "properties": { + "format": { + "type": "string", + "enum": [ + "qasm", + "qir", + "jeff" + ], + "description": "Its format." + }, + "program": { + "type": "string", + "description": "The program's text: QIR (LLVM text) or OpenQASM." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64: jeff." + } + } + }, "qasm": { "type": "string", "description": "The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in \"inputs\"). while, inv and pow are refused with their line." @@ -6853,10 +6888,45 @@ "description": "The method a program would run on and why, the memory and work it would take, without running it.", "inputSchema": { "type": "object", - "required": [ - "qasm" + "anyOf": [ + { + "required": [ + "qasm" + ] + }, + { + "required": [ + "source" + ] + } ], "properties": { + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source.", + "properties": { + "format": { + "type": "string", + "enum": [ + "qasm", + "qir", + "jeff" + ], + "description": "Its format." + }, + "program": { + "type": "string", + "description": "The program's text: QIR (LLVM text) or OpenQASM." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64: jeff." + } + } + }, "qasm": { "type": "string", "description": "The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in \"inputs\"). while, inv and pow are refused with their line." @@ -27084,11 +27154,45 @@ "description": "Run an OpenQASM program here as it would run on a device target: compiled exactly to the target's native gates, placed on the qubits and routes most likely to succeed (the expected success given beside the fewest-swaps layout's), checked against the circuit, and simulated under the noise its calibration gives (each gate's error, T1, T2 and durations, idling, and each qubit's readout), on the density matrix or by trajectories. Counts and expectation values, their distance from the ideal distribution, the noise derived, and a receipt signed with this user's key that binds the target's hash. A model of the device, not a run on it: quote it as such. Every angle must be a dyadic multiple of 2π.", "inputSchema": { "type": "object", - "required": [ - "qasm", - "target" + "anyOf": [ + { + "required": [ + "qasm" + ] + }, + { + "required": [ + "source" + ] + } ], "properties": { + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source.", + "properties": { + "format": { + "type": "string", + "enum": [ + "qasm", + "qir", + "jeff" + ], + "description": "Its format." + }, + "program": { + "type": "string", + "description": "The program's text: QIR (LLVM text) or OpenQASM." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64: jeff." + } + } + }, "qasm": { "type": "string", "description": "The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in \"inputs\"). while, inv and pow are refused with their line." @@ -27550,7 +27654,10 @@ } } } - } + }, + "required": [ + "target" + ] }, "outputSchema": { "$id": "wai-quantum://schemas/run_on_target/1", diff --git a/wai/wai-quantum-cli/docs/service-mcp.json b/wai/wai-quantum-cli/docs/service-mcp.json index b0a0ad027..12649d598 100644 --- a/wai/wai-quantum-cli/docs/service-mcp.json +++ b/wai/wai-quantum-cli/docs/service-mcp.json @@ -7,8 +7,17 @@ "description": "Run an OpenQASM program and return measurement counts, Pauli expectation values and/or amplitudes. The service chooses the method (bit-exact or double-precision state vector up to 32 qubits, single precision on a GPU to 33, stabilizer tableau for Clifford circuits up to 32768 qubits, matrix product states up to 4096, sparse Pauli dynamics for expectation values up to 1024 with an error bound, an exact density matrix or trajectories for noise), reports why and how accurate, and signs the result. Give `intent` (an exact distribution, or a reference program it must equal or whose state it must prepare) and the engine checks it with the run. Every answer carries `verdict`: the level the engine checked to (proof, evidence, executed, parsed, refused), whether the intent holds and a plain statement, sealed in the receipt. Waits up to 50 s; a longer job answers with status running and an id for get_quantum_job.", "inputSchema": { "type": "object", - "required": [ - "qasm" + "anyOf": [ + { + "required": [ + "qasm" + ] + }, + { + "required": [ + "source" + ] + } ], "properties": { "qasm": { @@ -70,10 +79,9 @@ "cutrank", "cutrank_single", "density_matrix", - "trajectories", - "pulse" + "trajectories" ], - "description": "Leave as auto unless you need a particular method; pulse runs a pulse program (OpenPulse calibrations on physical qubits, see pulse_schedule) under the pulse options, its error bounded." + "description": "Leave as auto unless you need a particular method." }, "seed": { "type": "integer", @@ -354,118 +362,29 @@ } ] }, - "pulse": { + "source": { "type": "object", - "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", - "additionalProperties": false, + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source.", "properties": { - "dt_ns": { - "type": "number", - "exclusiveMinimum": 0, - "maximum": 1000, - "description": "The sample period, ns (default 0.5)." - }, - "levels": { - "type": "integer", + "format": { + "type": "string", "enum": [ - 2, - 3 + "qasm", + "qir", + "jeff" ], - "description": "Levels kept per qubit (default 2; 3 shows leakage)." + "description": "Its format." }, - "tolerance": { - "type": "number", - "exclusiveMinimum": 0, - "exclusiveMaximum": 1, - "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." + "program": { + "type": "string", + "description": "The program's text: QIR (LLVM text) or OpenQASM." }, - "substeps": { - "type": "integer", - "minimum": 1, - "maximum": 4096, - "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." - }, - "qubits": { - "type": "object", - "description": "Each qubit's parameters by its index, over the target's and the defaults.", - "additionalProperties": { - "type": "object", - "additionalProperties": false, - "properties": { - "frequency_ghz": { - "type": "number", - "description": "Its frequency, GHz." - }, - "rabi_mhz": { - "type": "number", - "description": "Its Rabi frequency at full amplitude, MHz." - }, - "anharmonicity_mhz": { - "type": "number", - "description": "Its anharmonicity, MHz." - }, - "t1_us": { - "type": "number", - "description": "Its relaxation time, µs." - }, - "t2_us": { - "type": "number", - "description": "Its coherence time, µs (at most 2·T1)." - } - } - } - }, - "couplings": { - "type": "array", - "description": "Static ZZ couplings.", - "items": { - "type": "object", - "required": [ - "qubits", - "zz_khz" - ], - "additionalProperties": false, - "properties": { - "qubits": { - "type": "array", - "items": { - "type": "integer", - "minimum": 0 - }, - "minItems": 2, - "maxItems": 2 - }, - "zz_khz": { - "type": "number", - "description": "The coupling's rate, kHz." - } - } - } - }, - "ports": { - "type": "object", - "description": "Ports by name: what each is for and the qubit it reaches.", - "additionalProperties": { - "type": "object", - "required": [ - "kind", - "qubit" - ], - "additionalProperties": false, - "properties": { - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "qubit": { - "type": "integer", - "minimum": 0 - } - } - } + "program_base64": { + "type": "string", + "description": "The program's bytes as base64: jeff." } } } @@ -597,7 +516,7 @@ }, "method": { "type": "string", - "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it; pulse runs a pulse program (OpenPulse calibrations) under the pulse options." + "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it." }, "observables": { "type": "array", @@ -1151,121 +1070,6 @@ ] } ] - }, - "pulse": { - "type": "object", - "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", - "additionalProperties": false, - "properties": { - "dt_ns": { - "type": "number", - "exclusiveMinimum": 0, - "maximum": 1000, - "description": "The sample period, ns (default 0.5)." - }, - "levels": { - "type": "integer", - "enum": [ - 2, - 3 - ], - "description": "Levels kept per qubit (default 2; 3 shows leakage)." - }, - "tolerance": { - "type": "number", - "exclusiveMinimum": 0, - "exclusiveMaximum": 1, - "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." - }, - "substeps": { - "type": "integer", - "minimum": 1, - "maximum": 4096, - "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." - }, - "qubits": { - "type": "object", - "description": "Each qubit's parameters by its index, over the target's and the defaults.", - "additionalProperties": { - "type": "object", - "additionalProperties": false, - "properties": { - "frequency_ghz": { - "type": "number", - "description": "Its frequency, GHz." - }, - "rabi_mhz": { - "type": "number", - "description": "Its Rabi frequency at full amplitude, MHz." - }, - "anharmonicity_mhz": { - "type": "number", - "description": "Its anharmonicity, MHz." - }, - "t1_us": { - "type": "number", - "description": "Its relaxation time, µs." - }, - "t2_us": { - "type": "number", - "description": "Its coherence time, µs (at most 2·T1)." - } - } - } - }, - "couplings": { - "type": "array", - "description": "Static ZZ couplings.", - "items": { - "type": "object", - "required": [ - "qubits", - "zz_khz" - ], - "additionalProperties": false, - "properties": { - "qubits": { - "type": "array", - "items": { - "type": "integer", - "minimum": 0 - }, - "minItems": 2, - "maxItems": 2 - }, - "zz_khz": { - "type": "number", - "description": "The coupling's rate, kHz." - } - } - } - }, - "ports": { - "type": "object", - "description": "Ports by name: what each is for and the qubit it reaches.", - "additionalProperties": { - "type": "object", - "required": [ - "kind", - "qubit" - ], - "additionalProperties": false, - "properties": { - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "qubit": { - "type": "integer", - "minimum": 0 - } - } - } - } - } } } }, @@ -1387,24 +1191,6 @@ }, "batch": { "type": "integer" - }, - "bound": { - "type": "number", - "description": "bounded (a pulse run): every probability within this of the exact solution of the model's master equation, the truncation bound and the rounding estimate together." - }, - "truncation_bound": { - "type": "number", - "description": "A pulse run: the proved bound, in the trace norm, on what the integrator's series leaves out." - }, - "rounding_estimate": { - "type": "number", - "description": "A pulse run: an estimate of floating-point rounding beside the bound, not a proof." - }, - "tolerance": { - "type": "number" - }, - "norm": { - "type": "string" } } }, @@ -2918,237 +2704,6 @@ } } } - }, - "pulse": { - "type": "object", - "description": "A pulse run (method pulse, engine::pulse): what was read and its distribution, the final populations, each capture's reading, the final density matrix's digest, the schedule's length, the integrator and its step, the device and where each parameter came from, and what the receipt binds (calibration).", - "required": [ - "read", - "outcome_bits", - "populations", - "simulated_qubits", - "density_hash", - "integrator", - "device", - "calibration" - ], - "properties": { - "read": { - "type": "string", - "description": "What the distribution reads: the captures together (into the program's bits when each writes one), or the final state." - }, - "outcome_bits": { - "type": "array", - "items": { - "type": "string" - }, - "description": "What each character of an outcome is, left to right: a program bit (c[0]) or a qubit ($0)." - }, - "distribution": { - "type": [ - "object", - "null" - ], - "additionalProperties": { - "type": "number" - }, - "description": "The probability of each outcome, which the counts sample; null when the captures start at different samples." - }, - "populations": { - "type": "object", - "additionalProperties": { - "type": "number" - }, - "description": "The final state read on every simulated qubit, the highest qubit first, a level above 0 read as 1." - }, - "simulated_qubits": { - "type": "array", - "items": { - "type": "integer" - } - }, - "captures": { - "type": "array", - "items": { - "type": "object", - "properties": { - "frame": { - "type": "string" - }, - "qubit": { - "type": "integer" - }, - "start": { - "type": "integer" - }, - "start_ns": { - "type": "number" - }, - "bit": { - "type": [ - "string", - "null" - ] - }, - "p1": { - "type": "number" - }, - "p2": { - "type": "number" - } - } - } - }, - "leakage": { - "type": "array", - "items": { - "type": "object", - "properties": { - "qubit": { - "type": "integer" - }, - "p2": { - "type": "number" - } - } - }, - "description": "With three levels: each qubit's final probability of level 2." - }, - "trace": { - "type": "number" - }, - "density_hash": { - "type": "string", - "description": "The BLAKE3 of the final density matrix's bits: the same on every platform." - }, - "duration": { - "type": "integer" - }, - "duration_ns": { - "type": "number" - }, - "integrator": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "order": { - "type": "integer" - }, - "dt_ns": { - "type": "number" - }, - "substeps": { - "type": "integer" - }, - "step_ns": { - "type": "number" - }, - "steps": { - "type": "integer" - }, - "norm_max": { - "type": "number" - }, - "frame": { - "type": "string" - } - } - }, - "device": { - "type": "object", - "properties": { - "levels": { - "type": "integer" - }, - "dt_ns": { - "type": "number" - }, - "qubits": { - "type": "array", - "items": { - "type": "object", - "properties": { - "qubit": { - "type": "integer" - }, - "frequency_hz": { - "type": "number" - }, - "rabi_hz": { - "type": "number" - }, - "anharmonicity_hz": { - "type": "number" - }, - "t1_ns": { - "type": "number" - }, - "t2_ns": { - "type": "number" - }, - "from": { - "type": "object", - "additionalProperties": { - "enum": [ - "pulse", - "target", - "default", - "none" - ] - } - } - } - } - }, - "couplings": { - "type": "array" - }, - "model": { - "type": "string" - } - } - }, - "calibration": { - "type": "object", - "description": "What the receipt binds: the program's, its calibrations', the schedule's and the device's digests, and the target snapshot.", - "properties": { - "program": { - "type": "string" - }, - "calibrations": { - "type": "string" - }, - "schedule": { - "type": "string" - }, - "device": { - "type": "string" - }, - "target": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "calibration": { - "type": "object" - } - } - } - } - }, - "not_modelled": { - "type": "array", - "items": { - "type": "string" - } - } - } } } }, @@ -3452,7 +3007,7 @@ }, "method": { "type": "string", - "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it; pulse runs a pulse program (OpenPulse calibrations) under the pulse options." + "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it." }, "observables": { "type": "array", @@ -4006,121 +3561,6 @@ ] } ] - }, - "pulse": { - "type": "object", - "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", - "additionalProperties": false, - "properties": { - "dt_ns": { - "type": "number", - "exclusiveMinimum": 0, - "maximum": 1000, - "description": "The sample period, ns (default 0.5)." - }, - "levels": { - "type": "integer", - "enum": [ - 2, - 3 - ], - "description": "Levels kept per qubit (default 2; 3 shows leakage)." - }, - "tolerance": { - "type": "number", - "exclusiveMinimum": 0, - "exclusiveMaximum": 1, - "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." - }, - "substeps": { - "type": "integer", - "minimum": 1, - "maximum": 4096, - "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." - }, - "qubits": { - "type": "object", - "description": "Each qubit's parameters by its index, over the target's and the defaults.", - "additionalProperties": { - "type": "object", - "additionalProperties": false, - "properties": { - "frequency_ghz": { - "type": "number", - "description": "Its frequency, GHz." - }, - "rabi_mhz": { - "type": "number", - "description": "Its Rabi frequency at full amplitude, MHz." - }, - "anharmonicity_mhz": { - "type": "number", - "description": "Its anharmonicity, MHz." - }, - "t1_us": { - "type": "number", - "description": "Its relaxation time, µs." - }, - "t2_us": { - "type": "number", - "description": "Its coherence time, µs (at most 2·T1)." - } - } - } - }, - "couplings": { - "type": "array", - "description": "Static ZZ couplings.", - "items": { - "type": "object", - "required": [ - "qubits", - "zz_khz" - ], - "additionalProperties": false, - "properties": { - "qubits": { - "type": "array", - "items": { - "type": "integer", - "minimum": 0 - }, - "minItems": 2, - "maxItems": 2 - }, - "zz_khz": { - "type": "number", - "description": "The coupling's rate, kHz." - } - } - } - }, - "ports": { - "type": "object", - "description": "Ports by name: what each is for and the qubit it reaches.", - "additionalProperties": { - "type": "object", - "required": [ - "kind", - "qubit" - ], - "additionalProperties": false, - "properties": { - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "qubit": { - "type": "integer", - "minimum": 0 - } - } - } - } - } } } }, @@ -4240,24 +3680,6 @@ }, "batch": { "type": "integer" - }, - "bound": { - "type": "number", - "description": "bounded (a pulse run): every probability within this of the exact solution of the model's master equation, the truncation bound and the rounding estimate together." - }, - "truncation_bound": { - "type": "number", - "description": "A pulse run: the proved bound, in the trace norm, on what the integrator's series leaves out." - }, - "rounding_estimate": { - "type": "number", - "description": "A pulse run: an estimate of floating-point rounding beside the bound, not a proof." - }, - "tolerance": { - "type": "number" - }, - "norm": { - "type": "string" } } }, @@ -5771,237 +5193,6 @@ } } } - }, - "pulse": { - "type": "object", - "description": "A pulse run (method pulse, engine::pulse): what was read and its distribution, the final populations, each capture's reading, the final density matrix's digest, the schedule's length, the integrator and its step, the device and where each parameter came from, and what the receipt binds (calibration).", - "required": [ - "read", - "outcome_bits", - "populations", - "simulated_qubits", - "density_hash", - "integrator", - "device", - "calibration" - ], - "properties": { - "read": { - "type": "string", - "description": "What the distribution reads: the captures together (into the program's bits when each writes one), or the final state." - }, - "outcome_bits": { - "type": "array", - "items": { - "type": "string" - }, - "description": "What each character of an outcome is, left to right: a program bit (c[0]) or a qubit ($0)." - }, - "distribution": { - "type": [ - "object", - "null" - ], - "additionalProperties": { - "type": "number" - }, - "description": "The probability of each outcome, which the counts sample; null when the captures start at different samples." - }, - "populations": { - "type": "object", - "additionalProperties": { - "type": "number" - }, - "description": "The final state read on every simulated qubit, the highest qubit first, a level above 0 read as 1." - }, - "simulated_qubits": { - "type": "array", - "items": { - "type": "integer" - } - }, - "captures": { - "type": "array", - "items": { - "type": "object", - "properties": { - "frame": { - "type": "string" - }, - "qubit": { - "type": "integer" - }, - "start": { - "type": "integer" - }, - "start_ns": { - "type": "number" - }, - "bit": { - "type": [ - "string", - "null" - ] - }, - "p1": { - "type": "number" - }, - "p2": { - "type": "number" - } - } - } - }, - "leakage": { - "type": "array", - "items": { - "type": "object", - "properties": { - "qubit": { - "type": "integer" - }, - "p2": { - "type": "number" - } - } - }, - "description": "With three levels: each qubit's final probability of level 2." - }, - "trace": { - "type": "number" - }, - "density_hash": { - "type": "string", - "description": "The BLAKE3 of the final density matrix's bits: the same on every platform." - }, - "duration": { - "type": "integer" - }, - "duration_ns": { - "type": "number" - }, - "integrator": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "order": { - "type": "integer" - }, - "dt_ns": { - "type": "number" - }, - "substeps": { - "type": "integer" - }, - "step_ns": { - "type": "number" - }, - "steps": { - "type": "integer" - }, - "norm_max": { - "type": "number" - }, - "frame": { - "type": "string" - } - } - }, - "device": { - "type": "object", - "properties": { - "levels": { - "type": "integer" - }, - "dt_ns": { - "type": "number" - }, - "qubits": { - "type": "array", - "items": { - "type": "object", - "properties": { - "qubit": { - "type": "integer" - }, - "frequency_hz": { - "type": "number" - }, - "rabi_hz": { - "type": "number" - }, - "anharmonicity_hz": { - "type": "number" - }, - "t1_ns": { - "type": "number" - }, - "t2_ns": { - "type": "number" - }, - "from": { - "type": "object", - "additionalProperties": { - "enum": [ - "pulse", - "target", - "default", - "none" - ] - } - } - } - } - }, - "couplings": { - "type": "array" - }, - "model": { - "type": "string" - } - } - }, - "calibration": { - "type": "object", - "description": "What the receipt binds: the program's, its calibrations', the schedule's and the device's digests, and the target snapshot.", - "properties": { - "program": { - "type": "string" - }, - "calibrations": { - "type": "string" - }, - "schedule": { - "type": "string" - }, - "device": { - "type": "string" - }, - "target": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "calibration": { - "type": "object" - } - } - } - } - }, - "not_modelled": { - "type": "array", - "items": { - "type": "string" - } - } - } } } }, @@ -8274,10 +7465,9 @@ "cutrank", "cutrank_single", "density_matrix", - "trajectories", - "pulse" + "trajectories" ], - "description": "Leave as auto unless you need a particular method; pulse runs a pulse program (OpenPulse calibrations on physical qubits, see pulse_schedule) under the pulse options, its error bounded." + "description": "Leave as auto unless you need a particular method." }, "seed": { "type": "integer", @@ -8558,121 +7748,6 @@ } ] }, - "pulse": { - "type": "object", - "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", - "additionalProperties": false, - "properties": { - "dt_ns": { - "type": "number", - "exclusiveMinimum": 0, - "maximum": 1000, - "description": "The sample period, ns (default 0.5)." - }, - "levels": { - "type": "integer", - "enum": [ - 2, - 3 - ], - "description": "Levels kept per qubit (default 2; 3 shows leakage)." - }, - "tolerance": { - "type": "number", - "exclusiveMinimum": 0, - "exclusiveMaximum": 1, - "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." - }, - "substeps": { - "type": "integer", - "minimum": 1, - "maximum": 4096, - "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." - }, - "qubits": { - "type": "object", - "description": "Each qubit's parameters by its index, over the target's and the defaults.", - "additionalProperties": { - "type": "object", - "additionalProperties": false, - "properties": { - "frequency_ghz": { - "type": "number", - "description": "Its frequency, GHz." - }, - "rabi_mhz": { - "type": "number", - "description": "Its Rabi frequency at full amplitude, MHz." - }, - "anharmonicity_mhz": { - "type": "number", - "description": "Its anharmonicity, MHz." - }, - "t1_us": { - "type": "number", - "description": "Its relaxation time, µs." - }, - "t2_us": { - "type": "number", - "description": "Its coherence time, µs (at most 2·T1)." - } - } - } - }, - "couplings": { - "type": "array", - "description": "Static ZZ couplings.", - "items": { - "type": "object", - "required": [ - "qubits", - "zz_khz" - ], - "additionalProperties": false, - "properties": { - "qubits": { - "type": "array", - "items": { - "type": "integer", - "minimum": 0 - }, - "minItems": 2, - "maxItems": 2 - }, - "zz_khz": { - "type": "number", - "description": "The coupling's rate, kHz." - } - } - } - }, - "ports": { - "type": "object", - "description": "Ports by name: what each is for and the qubit it reaches.", - "additionalProperties": { - "type": "object", - "required": [ - "kind", - "qubit" - ], - "additionalProperties": false, - "properties": { - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "qubit": { - "type": "integer", - "minimum": 0 - } - } - } - } - } - }, "programs": { "type": "array", "minItems": 1, @@ -8831,7 +7906,7 @@ }, "method": { "type": "string", - "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it; pulse runs a pulse program (OpenPulse calibrations) under the pulse options." + "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it." }, "observables": { "type": "array", @@ -9385,121 +8460,6 @@ ] } ] - }, - "pulse": { - "type": "object", - "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", - "additionalProperties": false, - "properties": { - "dt_ns": { - "type": "number", - "exclusiveMinimum": 0, - "maximum": 1000, - "description": "The sample period, ns (default 0.5)." - }, - "levels": { - "type": "integer", - "enum": [ - 2, - 3 - ], - "description": "Levels kept per qubit (default 2; 3 shows leakage)." - }, - "tolerance": { - "type": "number", - "exclusiveMinimum": 0, - "exclusiveMaximum": 1, - "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." - }, - "substeps": { - "type": "integer", - "minimum": 1, - "maximum": 4096, - "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." - }, - "qubits": { - "type": "object", - "description": "Each qubit's parameters by its index, over the target's and the defaults.", - "additionalProperties": { - "type": "object", - "additionalProperties": false, - "properties": { - "frequency_ghz": { - "type": "number", - "description": "Its frequency, GHz." - }, - "rabi_mhz": { - "type": "number", - "description": "Its Rabi frequency at full amplitude, MHz." - }, - "anharmonicity_mhz": { - "type": "number", - "description": "Its anharmonicity, MHz." - }, - "t1_us": { - "type": "number", - "description": "Its relaxation time, µs." - }, - "t2_us": { - "type": "number", - "description": "Its coherence time, µs (at most 2·T1)." - } - } - } - }, - "couplings": { - "type": "array", - "description": "Static ZZ couplings.", - "items": { - "type": "object", - "required": [ - "qubits", - "zz_khz" - ], - "additionalProperties": false, - "properties": { - "qubits": { - "type": "array", - "items": { - "type": "integer", - "minimum": 0 - }, - "minItems": 2, - "maxItems": 2 - }, - "zz_khz": { - "type": "number", - "description": "The coupling's rate, kHz." - } - } - } - }, - "ports": { - "type": "object", - "description": "Ports by name: what each is for and the qubit it reaches.", - "additionalProperties": { - "type": "object", - "required": [ - "kind", - "qubit" - ], - "additionalProperties": false, - "properties": { - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "qubit": { - "type": "integer", - "minimum": 0 - } - } - } - } - } } } }, @@ -9621,24 +8581,6 @@ }, "batch": { "type": "integer" - }, - "bound": { - "type": "number", - "description": "bounded (a pulse run): every probability within this of the exact solution of the model's master equation, the truncation bound and the rounding estimate together." - }, - "truncation_bound": { - "type": "number", - "description": "A pulse run: the proved bound, in the trace norm, on what the integrator's series leaves out." - }, - "rounding_estimate": { - "type": "number", - "description": "A pulse run: an estimate of floating-point rounding beside the bound, not a proof." - }, - "tolerance": { - "type": "number" - }, - "norm": { - "type": "string" } } }, @@ -11152,237 +10094,6 @@ } } } - }, - "pulse": { - "type": "object", - "description": "A pulse run (method pulse, engine::pulse): what was read and its distribution, the final populations, each capture's reading, the final density matrix's digest, the schedule's length, the integrator and its step, the device and where each parameter came from, and what the receipt binds (calibration).", - "required": [ - "read", - "outcome_bits", - "populations", - "simulated_qubits", - "density_hash", - "integrator", - "device", - "calibration" - ], - "properties": { - "read": { - "type": "string", - "description": "What the distribution reads: the captures together (into the program's bits when each writes one), or the final state." - }, - "outcome_bits": { - "type": "array", - "items": { - "type": "string" - }, - "description": "What each character of an outcome is, left to right: a program bit (c[0]) or a qubit ($0)." - }, - "distribution": { - "type": [ - "object", - "null" - ], - "additionalProperties": { - "type": "number" - }, - "description": "The probability of each outcome, which the counts sample; null when the captures start at different samples." - }, - "populations": { - "type": "object", - "additionalProperties": { - "type": "number" - }, - "description": "The final state read on every simulated qubit, the highest qubit first, a level above 0 read as 1." - }, - "simulated_qubits": { - "type": "array", - "items": { - "type": "integer" - } - }, - "captures": { - "type": "array", - "items": { - "type": "object", - "properties": { - "frame": { - "type": "string" - }, - "qubit": { - "type": "integer" - }, - "start": { - "type": "integer" - }, - "start_ns": { - "type": "number" - }, - "bit": { - "type": [ - "string", - "null" - ] - }, - "p1": { - "type": "number" - }, - "p2": { - "type": "number" - } - } - } - }, - "leakage": { - "type": "array", - "items": { - "type": "object", - "properties": { - "qubit": { - "type": "integer" - }, - "p2": { - "type": "number" - } - } - }, - "description": "With three levels: each qubit's final probability of level 2." - }, - "trace": { - "type": "number" - }, - "density_hash": { - "type": "string", - "description": "The BLAKE3 of the final density matrix's bits: the same on every platform." - }, - "duration": { - "type": "integer" - }, - "duration_ns": { - "type": "number" - }, - "integrator": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "order": { - "type": "integer" - }, - "dt_ns": { - "type": "number" - }, - "substeps": { - "type": "integer" - }, - "step_ns": { - "type": "number" - }, - "steps": { - "type": "integer" - }, - "norm_max": { - "type": "number" - }, - "frame": { - "type": "string" - } - } - }, - "device": { - "type": "object", - "properties": { - "levels": { - "type": "integer" - }, - "dt_ns": { - "type": "number" - }, - "qubits": { - "type": "array", - "items": { - "type": "object", - "properties": { - "qubit": { - "type": "integer" - }, - "frequency_hz": { - "type": "number" - }, - "rabi_hz": { - "type": "number" - }, - "anharmonicity_hz": { - "type": "number" - }, - "t1_ns": { - "type": "number" - }, - "t2_ns": { - "type": "number" - }, - "from": { - "type": "object", - "additionalProperties": { - "enum": [ - "pulse", - "target", - "default", - "none" - ] - } - } - } - } - }, - "couplings": { - "type": "array" - }, - "model": { - "type": "string" - } - } - }, - "calibration": { - "type": "object", - "description": "What the receipt binds: the program's, its calibrations', the schedule's and the device's digests, and the target snapshot.", - "properties": { - "program": { - "type": "string" - }, - "calibrations": { - "type": "string" - }, - "schedule": { - "type": "string" - }, - "device": { - "type": "string" - }, - "target": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "calibration": { - "type": "object" - } - } - } - } - }, - "not_modelled": { - "type": "array", - "items": { - "type": "string" - } - } - } } } }, @@ -11686,7 +10397,7 @@ }, "method": { "type": "string", - "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it; pulse runs a pulse program (OpenPulse calibrations) under the pulse options." + "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it." }, "observables": { "type": "array", @@ -12240,121 +10951,6 @@ ] } ] - }, - "pulse": { - "type": "object", - "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", - "additionalProperties": false, - "properties": { - "dt_ns": { - "type": "number", - "exclusiveMinimum": 0, - "maximum": 1000, - "description": "The sample period, ns (default 0.5)." - }, - "levels": { - "type": "integer", - "enum": [ - 2, - 3 - ], - "description": "Levels kept per qubit (default 2; 3 shows leakage)." - }, - "tolerance": { - "type": "number", - "exclusiveMinimum": 0, - "exclusiveMaximum": 1, - "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." - }, - "substeps": { - "type": "integer", - "minimum": 1, - "maximum": 4096, - "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." - }, - "qubits": { - "type": "object", - "description": "Each qubit's parameters by its index, over the target's and the defaults.", - "additionalProperties": { - "type": "object", - "additionalProperties": false, - "properties": { - "frequency_ghz": { - "type": "number", - "description": "Its frequency, GHz." - }, - "rabi_mhz": { - "type": "number", - "description": "Its Rabi frequency at full amplitude, MHz." - }, - "anharmonicity_mhz": { - "type": "number", - "description": "Its anharmonicity, MHz." - }, - "t1_us": { - "type": "number", - "description": "Its relaxation time, µs." - }, - "t2_us": { - "type": "number", - "description": "Its coherence time, µs (at most 2·T1)." - } - } - } - }, - "couplings": { - "type": "array", - "description": "Static ZZ couplings.", - "items": { - "type": "object", - "required": [ - "qubits", - "zz_khz" - ], - "additionalProperties": false, - "properties": { - "qubits": { - "type": "array", - "items": { - "type": "integer", - "minimum": 0 - }, - "minItems": 2, - "maxItems": 2 - }, - "zz_khz": { - "type": "number", - "description": "The coupling's rate, kHz." - } - } - } - }, - "ports": { - "type": "object", - "description": "Ports by name: what each is for and the qubit it reaches.", - "additionalProperties": { - "type": "object", - "required": [ - "kind", - "qubit" - ], - "additionalProperties": false, - "properties": { - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "qubit": { - "type": "integer", - "minimum": 0 - } - } - } - } - } } } }, @@ -12474,24 +11070,6 @@ }, "batch": { "type": "integer" - }, - "bound": { - "type": "number", - "description": "bounded (a pulse run): every probability within this of the exact solution of the model's master equation, the truncation bound and the rounding estimate together." - }, - "truncation_bound": { - "type": "number", - "description": "A pulse run: the proved bound, in the trace norm, on what the integrator's series leaves out." - }, - "rounding_estimate": { - "type": "number", - "description": "A pulse run: an estimate of floating-point rounding beside the bound, not a proof." - }, - "tolerance": { - "type": "number" - }, - "norm": { - "type": "string" } } }, @@ -14005,237 +12583,6 @@ } } } - }, - "pulse": { - "type": "object", - "description": "A pulse run (method pulse, engine::pulse): what was read and its distribution, the final populations, each capture's reading, the final density matrix's digest, the schedule's length, the integrator and its step, the device and where each parameter came from, and what the receipt binds (calibration).", - "required": [ - "read", - "outcome_bits", - "populations", - "simulated_qubits", - "density_hash", - "integrator", - "device", - "calibration" - ], - "properties": { - "read": { - "type": "string", - "description": "What the distribution reads: the captures together (into the program's bits when each writes one), or the final state." - }, - "outcome_bits": { - "type": "array", - "items": { - "type": "string" - }, - "description": "What each character of an outcome is, left to right: a program bit (c[0]) or a qubit ($0)." - }, - "distribution": { - "type": [ - "object", - "null" - ], - "additionalProperties": { - "type": "number" - }, - "description": "The probability of each outcome, which the counts sample; null when the captures start at different samples." - }, - "populations": { - "type": "object", - "additionalProperties": { - "type": "number" - }, - "description": "The final state read on every simulated qubit, the highest qubit first, a level above 0 read as 1." - }, - "simulated_qubits": { - "type": "array", - "items": { - "type": "integer" - } - }, - "captures": { - "type": "array", - "items": { - "type": "object", - "properties": { - "frame": { - "type": "string" - }, - "qubit": { - "type": "integer" - }, - "start": { - "type": "integer" - }, - "start_ns": { - "type": "number" - }, - "bit": { - "type": [ - "string", - "null" - ] - }, - "p1": { - "type": "number" - }, - "p2": { - "type": "number" - } - } - } - }, - "leakage": { - "type": "array", - "items": { - "type": "object", - "properties": { - "qubit": { - "type": "integer" - }, - "p2": { - "type": "number" - } - } - }, - "description": "With three levels: each qubit's final probability of level 2." - }, - "trace": { - "type": "number" - }, - "density_hash": { - "type": "string", - "description": "The BLAKE3 of the final density matrix's bits: the same on every platform." - }, - "duration": { - "type": "integer" - }, - "duration_ns": { - "type": "number" - }, - "integrator": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "order": { - "type": "integer" - }, - "dt_ns": { - "type": "number" - }, - "substeps": { - "type": "integer" - }, - "step_ns": { - "type": "number" - }, - "steps": { - "type": "integer" - }, - "norm_max": { - "type": "number" - }, - "frame": { - "type": "string" - } - } - }, - "device": { - "type": "object", - "properties": { - "levels": { - "type": "integer" - }, - "dt_ns": { - "type": "number" - }, - "qubits": { - "type": "array", - "items": { - "type": "object", - "properties": { - "qubit": { - "type": "integer" - }, - "frequency_hz": { - "type": "number" - }, - "rabi_hz": { - "type": "number" - }, - "anharmonicity_hz": { - "type": "number" - }, - "t1_ns": { - "type": "number" - }, - "t2_ns": { - "type": "number" - }, - "from": { - "type": "object", - "additionalProperties": { - "enum": [ - "pulse", - "target", - "default", - "none" - ] - } - } - } - } - }, - "couplings": { - "type": "array" - }, - "model": { - "type": "string" - } - } - }, - "calibration": { - "type": "object", - "description": "What the receipt binds: the program's, its calibrations', the schedule's and the device's digests, and the target snapshot.", - "properties": { - "program": { - "type": "string" - }, - "calibrations": { - "type": "string" - }, - "schedule": { - "type": "string" - }, - "device": { - "type": "string" - }, - "target": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "calibration": { - "type": "object" - } - } - } - } - }, - "not_modelled": { - "type": "array", - "items": { - "type": "string" - } - } - } } } }, @@ -16508,10 +14855,9 @@ "cutrank", "cutrank_single", "density_matrix", - "trajectories", - "pulse" + "trajectories" ], - "description": "Leave as auto unless you need a particular method; pulse runs a pulse program (OpenPulse calibrations on physical qubits, see pulse_schedule) under the pulse options, its error bounded." + "description": "Leave as auto unless you need a particular method." }, "seed": { "type": "integer", @@ -16792,121 +15138,6 @@ } ] }, - "pulse": { - "type": "object", - "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", - "additionalProperties": false, - "properties": { - "dt_ns": { - "type": "number", - "exclusiveMinimum": 0, - "maximum": 1000, - "description": "The sample period, ns (default 0.5)." - }, - "levels": { - "type": "integer", - "enum": [ - 2, - 3 - ], - "description": "Levels kept per qubit (default 2; 3 shows leakage)." - }, - "tolerance": { - "type": "number", - "exclusiveMinimum": 0, - "exclusiveMaximum": 1, - "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." - }, - "substeps": { - "type": "integer", - "minimum": 1, - "maximum": 4096, - "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." - }, - "qubits": { - "type": "object", - "description": "Each qubit's parameters by its index, over the target's and the defaults.", - "additionalProperties": { - "type": "object", - "additionalProperties": false, - "properties": { - "frequency_ghz": { - "type": "number", - "description": "Its frequency, GHz." - }, - "rabi_mhz": { - "type": "number", - "description": "Its Rabi frequency at full amplitude, MHz." - }, - "anharmonicity_mhz": { - "type": "number", - "description": "Its anharmonicity, MHz." - }, - "t1_us": { - "type": "number", - "description": "Its relaxation time, µs." - }, - "t2_us": { - "type": "number", - "description": "Its coherence time, µs (at most 2·T1)." - } - } - } - }, - "couplings": { - "type": "array", - "description": "Static ZZ couplings.", - "items": { - "type": "object", - "required": [ - "qubits", - "zz_khz" - ], - "additionalProperties": false, - "properties": { - "qubits": { - "type": "array", - "items": { - "type": "integer", - "minimum": 0 - }, - "minItems": 2, - "maxItems": 2 - }, - "zz_khz": { - "type": "number", - "description": "The coupling's rate, kHz." - } - } - } - }, - "ports": { - "type": "object", - "description": "Ports by name: what each is for and the qubit it reaches.", - "additionalProperties": { - "type": "object", - "required": [ - "kind", - "qubit" - ], - "additionalProperties": false, - "properties": { - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "qubit": { - "type": "integer", - "minimum": 0 - } - } - } - } - } - }, "programs": { "type": "array", "minItems": 1, @@ -16936,6 +15167,32 @@ } }, "description": "Each input of qasm and its values: every combination runs, the names in sorted order, the first varying slowest; an integer input's values are whole numbers." + }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source.", + "properties": { + "format": { + "type": "string", + "enum": [ + "qasm", + "qir", + "jeff" + ], + "description": "Its format." + }, + "program": { + "type": "string", + "description": "The program's text: QIR (LLVM text) or OpenQASM." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64: jeff." + } + } } } }, @@ -18069,7 +16326,7 @@ }, "method": { "type": "string", - "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it; pulse runs a pulse program (OpenPulse calibrations) under the pulse options." + "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it." }, "observables": { "type": "array", @@ -18623,121 +16880,6 @@ ] } ] - }, - "pulse": { - "type": "object", - "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", - "additionalProperties": false, - "properties": { - "dt_ns": { - "type": "number", - "exclusiveMinimum": 0, - "maximum": 1000, - "description": "The sample period, ns (default 0.5)." - }, - "levels": { - "type": "integer", - "enum": [ - 2, - 3 - ], - "description": "Levels kept per qubit (default 2; 3 shows leakage)." - }, - "tolerance": { - "type": "number", - "exclusiveMinimum": 0, - "exclusiveMaximum": 1, - "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." - }, - "substeps": { - "type": "integer", - "minimum": 1, - "maximum": 4096, - "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." - }, - "qubits": { - "type": "object", - "description": "Each qubit's parameters by its index, over the target's and the defaults.", - "additionalProperties": { - "type": "object", - "additionalProperties": false, - "properties": { - "frequency_ghz": { - "type": "number", - "description": "Its frequency, GHz." - }, - "rabi_mhz": { - "type": "number", - "description": "Its Rabi frequency at full amplitude, MHz." - }, - "anharmonicity_mhz": { - "type": "number", - "description": "Its anharmonicity, MHz." - }, - "t1_us": { - "type": "number", - "description": "Its relaxation time, µs." - }, - "t2_us": { - "type": "number", - "description": "Its coherence time, µs (at most 2·T1)." - } - } - } - }, - "couplings": { - "type": "array", - "description": "Static ZZ couplings.", - "items": { - "type": "object", - "required": [ - "qubits", - "zz_khz" - ], - "additionalProperties": false, - "properties": { - "qubits": { - "type": "array", - "items": { - "type": "integer", - "minimum": 0 - }, - "minItems": 2, - "maxItems": 2 - }, - "zz_khz": { - "type": "number", - "description": "The coupling's rate, kHz." - } - } - } - }, - "ports": { - "type": "object", - "description": "Ports by name: what each is for and the qubit it reaches.", - "additionalProperties": { - "type": "object", - "required": [ - "kind", - "qubit" - ], - "additionalProperties": false, - "properties": { - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "qubit": { - "type": "integer", - "minimum": 0 - } - } - } - } - } } } }, @@ -18859,24 +17001,6 @@ }, "batch": { "type": "integer" - }, - "bound": { - "type": "number", - "description": "bounded (a pulse run): every probability within this of the exact solution of the model's master equation, the truncation bound and the rounding estimate together." - }, - "truncation_bound": { - "type": "number", - "description": "A pulse run: the proved bound, in the trace norm, on what the integrator's series leaves out." - }, - "rounding_estimate": { - "type": "number", - "description": "A pulse run: an estimate of floating-point rounding beside the bound, not a proof." - }, - "tolerance": { - "type": "number" - }, - "norm": { - "type": "string" } } }, @@ -20390,237 +18514,6 @@ } } } - }, - "pulse": { - "type": "object", - "description": "A pulse run (method pulse, engine::pulse): what was read and its distribution, the final populations, each capture's reading, the final density matrix's digest, the schedule's length, the integrator and its step, the device and where each parameter came from, and what the receipt binds (calibration).", - "required": [ - "read", - "outcome_bits", - "populations", - "simulated_qubits", - "density_hash", - "integrator", - "device", - "calibration" - ], - "properties": { - "read": { - "type": "string", - "description": "What the distribution reads: the captures together (into the program's bits when each writes one), or the final state." - }, - "outcome_bits": { - "type": "array", - "items": { - "type": "string" - }, - "description": "What each character of an outcome is, left to right: a program bit (c[0]) or a qubit ($0)." - }, - "distribution": { - "type": [ - "object", - "null" - ], - "additionalProperties": { - "type": "number" - }, - "description": "The probability of each outcome, which the counts sample; null when the captures start at different samples." - }, - "populations": { - "type": "object", - "additionalProperties": { - "type": "number" - }, - "description": "The final state read on every simulated qubit, the highest qubit first, a level above 0 read as 1." - }, - "simulated_qubits": { - "type": "array", - "items": { - "type": "integer" - } - }, - "captures": { - "type": "array", - "items": { - "type": "object", - "properties": { - "frame": { - "type": "string" - }, - "qubit": { - "type": "integer" - }, - "start": { - "type": "integer" - }, - "start_ns": { - "type": "number" - }, - "bit": { - "type": [ - "string", - "null" - ] - }, - "p1": { - "type": "number" - }, - "p2": { - "type": "number" - } - } - } - }, - "leakage": { - "type": "array", - "items": { - "type": "object", - "properties": { - "qubit": { - "type": "integer" - }, - "p2": { - "type": "number" - } - } - }, - "description": "With three levels: each qubit's final probability of level 2." - }, - "trace": { - "type": "number" - }, - "density_hash": { - "type": "string", - "description": "The BLAKE3 of the final density matrix's bits: the same on every platform." - }, - "duration": { - "type": "integer" - }, - "duration_ns": { - "type": "number" - }, - "integrator": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "order": { - "type": "integer" - }, - "dt_ns": { - "type": "number" - }, - "substeps": { - "type": "integer" - }, - "step_ns": { - "type": "number" - }, - "steps": { - "type": "integer" - }, - "norm_max": { - "type": "number" - }, - "frame": { - "type": "string" - } - } - }, - "device": { - "type": "object", - "properties": { - "levels": { - "type": "integer" - }, - "dt_ns": { - "type": "number" - }, - "qubits": { - "type": "array", - "items": { - "type": "object", - "properties": { - "qubit": { - "type": "integer" - }, - "frequency_hz": { - "type": "number" - }, - "rabi_hz": { - "type": "number" - }, - "anharmonicity_hz": { - "type": "number" - }, - "t1_ns": { - "type": "number" - }, - "t2_ns": { - "type": "number" - }, - "from": { - "type": "object", - "additionalProperties": { - "enum": [ - "pulse", - "target", - "default", - "none" - ] - } - } - } - } - }, - "couplings": { - "type": "array" - }, - "model": { - "type": "string" - } - } - }, - "calibration": { - "type": "object", - "description": "What the receipt binds: the program's, its calibrations', the schedule's and the device's digests, and the target snapshot.", - "properties": { - "program": { - "type": "string" - }, - "calibrations": { - "type": "string" - }, - "schedule": { - "type": "string" - }, - "device": { - "type": "string" - }, - "target": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "calibration": { - "type": "object" - } - } - } - } - }, - "not_modelled": { - "type": "array", - "items": { - "type": "string" - } - } - } } } }, @@ -20924,7 +18817,7 @@ }, "method": { "type": "string", - "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it; pulse runs a pulse program (OpenPulse calibrations) under the pulse options." + "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it." }, "observables": { "type": "array", @@ -21478,121 +19371,6 @@ ] } ] - }, - "pulse": { - "type": "object", - "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", - "additionalProperties": false, - "properties": { - "dt_ns": { - "type": "number", - "exclusiveMinimum": 0, - "maximum": 1000, - "description": "The sample period, ns (default 0.5)." - }, - "levels": { - "type": "integer", - "enum": [ - 2, - 3 - ], - "description": "Levels kept per qubit (default 2; 3 shows leakage)." - }, - "tolerance": { - "type": "number", - "exclusiveMinimum": 0, - "exclusiveMaximum": 1, - "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." - }, - "substeps": { - "type": "integer", - "minimum": 1, - "maximum": 4096, - "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." - }, - "qubits": { - "type": "object", - "description": "Each qubit's parameters by its index, over the target's and the defaults.", - "additionalProperties": { - "type": "object", - "additionalProperties": false, - "properties": { - "frequency_ghz": { - "type": "number", - "description": "Its frequency, GHz." - }, - "rabi_mhz": { - "type": "number", - "description": "Its Rabi frequency at full amplitude, MHz." - }, - "anharmonicity_mhz": { - "type": "number", - "description": "Its anharmonicity, MHz." - }, - "t1_us": { - "type": "number", - "description": "Its relaxation time, µs." - }, - "t2_us": { - "type": "number", - "description": "Its coherence time, µs (at most 2·T1)." - } - } - } - }, - "couplings": { - "type": "array", - "description": "Static ZZ couplings.", - "items": { - "type": "object", - "required": [ - "qubits", - "zz_khz" - ], - "additionalProperties": false, - "properties": { - "qubits": { - "type": "array", - "items": { - "type": "integer", - "minimum": 0 - }, - "minItems": 2, - "maxItems": 2 - }, - "zz_khz": { - "type": "number", - "description": "The coupling's rate, kHz." - } - } - } - }, - "ports": { - "type": "object", - "description": "Ports by name: what each is for and the qubit it reaches.", - "additionalProperties": { - "type": "object", - "required": [ - "kind", - "qubit" - ], - "additionalProperties": false, - "properties": { - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "qubit": { - "type": "integer", - "minimum": 0 - } - } - } - } - } } } }, @@ -21712,24 +19490,6 @@ }, "batch": { "type": "integer" - }, - "bound": { - "type": "number", - "description": "bounded (a pulse run): every probability within this of the exact solution of the model's master equation, the truncation bound and the rounding estimate together." - }, - "truncation_bound": { - "type": "number", - "description": "A pulse run: the proved bound, in the trace norm, on what the integrator's series leaves out." - }, - "rounding_estimate": { - "type": "number", - "description": "A pulse run: an estimate of floating-point rounding beside the bound, not a proof." - }, - "tolerance": { - "type": "number" - }, - "norm": { - "type": "string" } } }, @@ -23243,237 +21003,6 @@ } } } - }, - "pulse": { - "type": "object", - "description": "A pulse run (method pulse, engine::pulse): what was read and its distribution, the final populations, each capture's reading, the final density matrix's digest, the schedule's length, the integrator and its step, the device and where each parameter came from, and what the receipt binds (calibration).", - "required": [ - "read", - "outcome_bits", - "populations", - "simulated_qubits", - "density_hash", - "integrator", - "device", - "calibration" - ], - "properties": { - "read": { - "type": "string", - "description": "What the distribution reads: the captures together (into the program's bits when each writes one), or the final state." - }, - "outcome_bits": { - "type": "array", - "items": { - "type": "string" - }, - "description": "What each character of an outcome is, left to right: a program bit (c[0]) or a qubit ($0)." - }, - "distribution": { - "type": [ - "object", - "null" - ], - "additionalProperties": { - "type": "number" - }, - "description": "The probability of each outcome, which the counts sample; null when the captures start at different samples." - }, - "populations": { - "type": "object", - "additionalProperties": { - "type": "number" - }, - "description": "The final state read on every simulated qubit, the highest qubit first, a level above 0 read as 1." - }, - "simulated_qubits": { - "type": "array", - "items": { - "type": "integer" - } - }, - "captures": { - "type": "array", - "items": { - "type": "object", - "properties": { - "frame": { - "type": "string" - }, - "qubit": { - "type": "integer" - }, - "start": { - "type": "integer" - }, - "start_ns": { - "type": "number" - }, - "bit": { - "type": [ - "string", - "null" - ] - }, - "p1": { - "type": "number" - }, - "p2": { - "type": "number" - } - } - } - }, - "leakage": { - "type": "array", - "items": { - "type": "object", - "properties": { - "qubit": { - "type": "integer" - }, - "p2": { - "type": "number" - } - } - }, - "description": "With three levels: each qubit's final probability of level 2." - }, - "trace": { - "type": "number" - }, - "density_hash": { - "type": "string", - "description": "The BLAKE3 of the final density matrix's bits: the same on every platform." - }, - "duration": { - "type": "integer" - }, - "duration_ns": { - "type": "number" - }, - "integrator": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "order": { - "type": "integer" - }, - "dt_ns": { - "type": "number" - }, - "substeps": { - "type": "integer" - }, - "step_ns": { - "type": "number" - }, - "steps": { - "type": "integer" - }, - "norm_max": { - "type": "number" - }, - "frame": { - "type": "string" - } - } - }, - "device": { - "type": "object", - "properties": { - "levels": { - "type": "integer" - }, - "dt_ns": { - "type": "number" - }, - "qubits": { - "type": "array", - "items": { - "type": "object", - "properties": { - "qubit": { - "type": "integer" - }, - "frequency_hz": { - "type": "number" - }, - "rabi_hz": { - "type": "number" - }, - "anharmonicity_hz": { - "type": "number" - }, - "t1_ns": { - "type": "number" - }, - "t2_ns": { - "type": "number" - }, - "from": { - "type": "object", - "additionalProperties": { - "enum": [ - "pulse", - "target", - "default", - "none" - ] - } - } - } - } - }, - "couplings": { - "type": "array" - }, - "model": { - "type": "string" - } - } - }, - "calibration": { - "type": "object", - "description": "What the receipt binds: the program's, its calibrations', the schedule's and the device's digests, and the target snapshot.", - "properties": { - "program": { - "type": "string" - }, - "calibrations": { - "type": "string" - }, - "schedule": { - "type": "string" - }, - "device": { - "type": "string" - }, - "target": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "calibration": { - "type": "object" - } - } - } - } - }, - "not_modelled": { - "type": "array", - "items": { - "type": "string" - } - } - } } } }, @@ -37582,9 +35111,20 @@ "inputSchema": { "type": "object", "required": [ - "qasm", "target" ], + "anyOf": [ + { + "required": [ + "qasm" + ] + }, + { + "required": [ + "source" + ] + } + ], "properties": { "qasm": { "type": "string", @@ -37631,118 +35171,29 @@ } } }, - "pulse": { + "source": { "type": "object", - "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", - "additionalProperties": false, + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source.", "properties": { - "dt_ns": { - "type": "number", - "exclusiveMinimum": 0, - "maximum": 1000, - "description": "The sample period, ns (default 0.5)." - }, - "levels": { - "type": "integer", + "format": { + "type": "string", "enum": [ - 2, - 3 + "qasm", + "qir", + "jeff" ], - "description": "Levels kept per qubit (default 2; 3 shows leakage)." + "description": "Its format." }, - "tolerance": { - "type": "number", - "exclusiveMinimum": 0, - "exclusiveMaximum": 1, - "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." + "program": { + "type": "string", + "description": "The program's text: QIR (LLVM text) or OpenQASM." }, - "substeps": { - "type": "integer", - "minimum": 1, - "maximum": 4096, - "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." - }, - "qubits": { - "type": "object", - "description": "Each qubit's parameters by its index, over the target's and the defaults.", - "additionalProperties": { - "type": "object", - "additionalProperties": false, - "properties": { - "frequency_ghz": { - "type": "number", - "description": "Its frequency, GHz." - }, - "rabi_mhz": { - "type": "number", - "description": "Its Rabi frequency at full amplitude, MHz." - }, - "anharmonicity_mhz": { - "type": "number", - "description": "Its anharmonicity, MHz." - }, - "t1_us": { - "type": "number", - "description": "Its relaxation time, µs." - }, - "t2_us": { - "type": "number", - "description": "Its coherence time, µs (at most 2·T1)." - } - } - } - }, - "couplings": { - "type": "array", - "description": "Static ZZ couplings.", - "items": { - "type": "object", - "required": [ - "qubits", - "zz_khz" - ], - "additionalProperties": false, - "properties": { - "qubits": { - "type": "array", - "items": { - "type": "integer", - "minimum": 0 - }, - "minItems": 2, - "maxItems": 2 - }, - "zz_khz": { - "type": "number", - "description": "The coupling's rate, kHz." - } - } - } - }, - "ports": { - "type": "object", - "description": "Ports by name: what each is for and the qubit it reaches.", - "additionalProperties": { - "type": "object", - "required": [ - "kind", - "qubit" - ], - "additionalProperties": false, - "properties": { - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "qubit": { - "type": "integer", - "minimum": 0 - } - } - } + "program_base64": { + "type": "string", + "description": "The program's bytes as base64: jeff." } } }, @@ -38153,7 +35604,7 @@ }, "method": { "type": "string", - "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it; pulse runs a pulse program (OpenPulse calibrations) under the pulse options." + "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it." }, "observables": { "type": "array", @@ -38707,121 +36158,6 @@ ] } ] - }, - "pulse": { - "type": "object", - "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", - "additionalProperties": false, - "properties": { - "dt_ns": { - "type": "number", - "exclusiveMinimum": 0, - "maximum": 1000, - "description": "The sample period, ns (default 0.5)." - }, - "levels": { - "type": "integer", - "enum": [ - 2, - 3 - ], - "description": "Levels kept per qubit (default 2; 3 shows leakage)." - }, - "tolerance": { - "type": "number", - "exclusiveMinimum": 0, - "exclusiveMaximum": 1, - "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." - }, - "substeps": { - "type": "integer", - "minimum": 1, - "maximum": 4096, - "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." - }, - "qubits": { - "type": "object", - "description": "Each qubit's parameters by its index, over the target's and the defaults.", - "additionalProperties": { - "type": "object", - "additionalProperties": false, - "properties": { - "frequency_ghz": { - "type": "number", - "description": "Its frequency, GHz." - }, - "rabi_mhz": { - "type": "number", - "description": "Its Rabi frequency at full amplitude, MHz." - }, - "anharmonicity_mhz": { - "type": "number", - "description": "Its anharmonicity, MHz." - }, - "t1_us": { - "type": "number", - "description": "Its relaxation time, µs." - }, - "t2_us": { - "type": "number", - "description": "Its coherence time, µs (at most 2·T1)." - } - } - } - }, - "couplings": { - "type": "array", - "description": "Static ZZ couplings.", - "items": { - "type": "object", - "required": [ - "qubits", - "zz_khz" - ], - "additionalProperties": false, - "properties": { - "qubits": { - "type": "array", - "items": { - "type": "integer", - "minimum": 0 - }, - "minItems": 2, - "maxItems": 2 - }, - "zz_khz": { - "type": "number", - "description": "The coupling's rate, kHz." - } - } - } - }, - "ports": { - "type": "object", - "description": "Ports by name: what each is for and the qubit it reaches.", - "additionalProperties": { - "type": "object", - "required": [ - "kind", - "qubit" - ], - "additionalProperties": false, - "properties": { - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "qubit": { - "type": "integer", - "minimum": 0 - } - } - } - } - } } } }, @@ -38943,24 +36279,6 @@ }, "batch": { "type": "integer" - }, - "bound": { - "type": "number", - "description": "bounded (a pulse run): every probability within this of the exact solution of the model's master equation, the truncation bound and the rounding estimate together." - }, - "truncation_bound": { - "type": "number", - "description": "A pulse run: the proved bound, in the trace norm, on what the integrator's series leaves out." - }, - "rounding_estimate": { - "type": "number", - "description": "A pulse run: an estimate of floating-point rounding beside the bound, not a proof." - }, - "tolerance": { - "type": "number" - }, - "norm": { - "type": "string" } } }, @@ -40474,237 +37792,6 @@ } } } - }, - "pulse": { - "type": "object", - "description": "A pulse run (method pulse, engine::pulse): what was read and its distribution, the final populations, each capture's reading, the final density matrix's digest, the schedule's length, the integrator and its step, the device and where each parameter came from, and what the receipt binds (calibration).", - "required": [ - "read", - "outcome_bits", - "populations", - "simulated_qubits", - "density_hash", - "integrator", - "device", - "calibration" - ], - "properties": { - "read": { - "type": "string", - "description": "What the distribution reads: the captures together (into the program's bits when each writes one), or the final state." - }, - "outcome_bits": { - "type": "array", - "items": { - "type": "string" - }, - "description": "What each character of an outcome is, left to right: a program bit (c[0]) or a qubit ($0)." - }, - "distribution": { - "type": [ - "object", - "null" - ], - "additionalProperties": { - "type": "number" - }, - "description": "The probability of each outcome, which the counts sample; null when the captures start at different samples." - }, - "populations": { - "type": "object", - "additionalProperties": { - "type": "number" - }, - "description": "The final state read on every simulated qubit, the highest qubit first, a level above 0 read as 1." - }, - "simulated_qubits": { - "type": "array", - "items": { - "type": "integer" - } - }, - "captures": { - "type": "array", - "items": { - "type": "object", - "properties": { - "frame": { - "type": "string" - }, - "qubit": { - "type": "integer" - }, - "start": { - "type": "integer" - }, - "start_ns": { - "type": "number" - }, - "bit": { - "type": [ - "string", - "null" - ] - }, - "p1": { - "type": "number" - }, - "p2": { - "type": "number" - } - } - } - }, - "leakage": { - "type": "array", - "items": { - "type": "object", - "properties": { - "qubit": { - "type": "integer" - }, - "p2": { - "type": "number" - } - } - }, - "description": "With three levels: each qubit's final probability of level 2." - }, - "trace": { - "type": "number" - }, - "density_hash": { - "type": "string", - "description": "The BLAKE3 of the final density matrix's bits: the same on every platform." - }, - "duration": { - "type": "integer" - }, - "duration_ns": { - "type": "number" - }, - "integrator": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "order": { - "type": "integer" - }, - "dt_ns": { - "type": "number" - }, - "substeps": { - "type": "integer" - }, - "step_ns": { - "type": "number" - }, - "steps": { - "type": "integer" - }, - "norm_max": { - "type": "number" - }, - "frame": { - "type": "string" - } - } - }, - "device": { - "type": "object", - "properties": { - "levels": { - "type": "integer" - }, - "dt_ns": { - "type": "number" - }, - "qubits": { - "type": "array", - "items": { - "type": "object", - "properties": { - "qubit": { - "type": "integer" - }, - "frequency_hz": { - "type": "number" - }, - "rabi_hz": { - "type": "number" - }, - "anharmonicity_hz": { - "type": "number" - }, - "t1_ns": { - "type": "number" - }, - "t2_ns": { - "type": "number" - }, - "from": { - "type": "object", - "additionalProperties": { - "enum": [ - "pulse", - "target", - "default", - "none" - ] - } - } - } - } - }, - "couplings": { - "type": "array" - }, - "model": { - "type": "string" - } - } - }, - "calibration": { - "type": "object", - "description": "What the receipt binds: the program's, its calibrations', the schedule's and the device's digests, and the target snapshot.", - "properties": { - "program": { - "type": "string" - }, - "calibrations": { - "type": "string" - }, - "schedule": { - "type": "string" - }, - "device": { - "type": "string" - }, - "target": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "calibration": { - "type": "object" - } - } - } - } - }, - "not_modelled": { - "type": "array", - "items": { - "type": "string" - } - } - } } } }, @@ -41008,7 +38095,7 @@ }, "method": { "type": "string", - "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it; pulse runs a pulse program (OpenPulse calibrations) under the pulse options." + "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it." }, "observables": { "type": "array", @@ -41562,121 +38649,6 @@ ] } ] - }, - "pulse": { - "type": "object", - "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", - "additionalProperties": false, - "properties": { - "dt_ns": { - "type": "number", - "exclusiveMinimum": 0, - "maximum": 1000, - "description": "The sample period, ns (default 0.5)." - }, - "levels": { - "type": "integer", - "enum": [ - 2, - 3 - ], - "description": "Levels kept per qubit (default 2; 3 shows leakage)." - }, - "tolerance": { - "type": "number", - "exclusiveMinimum": 0, - "exclusiveMaximum": 1, - "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." - }, - "substeps": { - "type": "integer", - "minimum": 1, - "maximum": 4096, - "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." - }, - "qubits": { - "type": "object", - "description": "Each qubit's parameters by its index, over the target's and the defaults.", - "additionalProperties": { - "type": "object", - "additionalProperties": false, - "properties": { - "frequency_ghz": { - "type": "number", - "description": "Its frequency, GHz." - }, - "rabi_mhz": { - "type": "number", - "description": "Its Rabi frequency at full amplitude, MHz." - }, - "anharmonicity_mhz": { - "type": "number", - "description": "Its anharmonicity, MHz." - }, - "t1_us": { - "type": "number", - "description": "Its relaxation time, µs." - }, - "t2_us": { - "type": "number", - "description": "Its coherence time, µs (at most 2·T1)." - } - } - } - }, - "couplings": { - "type": "array", - "description": "Static ZZ couplings.", - "items": { - "type": "object", - "required": [ - "qubits", - "zz_khz" - ], - "additionalProperties": false, - "properties": { - "qubits": { - "type": "array", - "items": { - "type": "integer", - "minimum": 0 - }, - "minItems": 2, - "maxItems": 2 - }, - "zz_khz": { - "type": "number", - "description": "The coupling's rate, kHz." - } - } - } - }, - "ports": { - "type": "object", - "description": "Ports by name: what each is for and the qubit it reaches.", - "additionalProperties": { - "type": "object", - "required": [ - "kind", - "qubit" - ], - "additionalProperties": false, - "properties": { - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "qubit": { - "type": "integer", - "minimum": 0 - } - } - } - } - } } } }, @@ -41796,24 +38768,6 @@ }, "batch": { "type": "integer" - }, - "bound": { - "type": "number", - "description": "bounded (a pulse run): every probability within this of the exact solution of the model's master equation, the truncation bound and the rounding estimate together." - }, - "truncation_bound": { - "type": "number", - "description": "A pulse run: the proved bound, in the trace norm, on what the integrator's series leaves out." - }, - "rounding_estimate": { - "type": "number", - "description": "A pulse run: an estimate of floating-point rounding beside the bound, not a proof." - }, - "tolerance": { - "type": "number" - }, - "norm": { - "type": "string" } } }, @@ -43327,237 +40281,6 @@ } } } - }, - "pulse": { - "type": "object", - "description": "A pulse run (method pulse, engine::pulse): what was read and its distribution, the final populations, each capture's reading, the final density matrix's digest, the schedule's length, the integrator and its step, the device and where each parameter came from, and what the receipt binds (calibration).", - "required": [ - "read", - "outcome_bits", - "populations", - "simulated_qubits", - "density_hash", - "integrator", - "device", - "calibration" - ], - "properties": { - "read": { - "type": "string", - "description": "What the distribution reads: the captures together (into the program's bits when each writes one), or the final state." - }, - "outcome_bits": { - "type": "array", - "items": { - "type": "string" - }, - "description": "What each character of an outcome is, left to right: a program bit (c[0]) or a qubit ($0)." - }, - "distribution": { - "type": [ - "object", - "null" - ], - "additionalProperties": { - "type": "number" - }, - "description": "The probability of each outcome, which the counts sample; null when the captures start at different samples." - }, - "populations": { - "type": "object", - "additionalProperties": { - "type": "number" - }, - "description": "The final state read on every simulated qubit, the highest qubit first, a level above 0 read as 1." - }, - "simulated_qubits": { - "type": "array", - "items": { - "type": "integer" - } - }, - "captures": { - "type": "array", - "items": { - "type": "object", - "properties": { - "frame": { - "type": "string" - }, - "qubit": { - "type": "integer" - }, - "start": { - "type": "integer" - }, - "start_ns": { - "type": "number" - }, - "bit": { - "type": [ - "string", - "null" - ] - }, - "p1": { - "type": "number" - }, - "p2": { - "type": "number" - } - } - } - }, - "leakage": { - "type": "array", - "items": { - "type": "object", - "properties": { - "qubit": { - "type": "integer" - }, - "p2": { - "type": "number" - } - } - }, - "description": "With three levels: each qubit's final probability of level 2." - }, - "trace": { - "type": "number" - }, - "density_hash": { - "type": "string", - "description": "The BLAKE3 of the final density matrix's bits: the same on every platform." - }, - "duration": { - "type": "integer" - }, - "duration_ns": { - "type": "number" - }, - "integrator": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "order": { - "type": "integer" - }, - "dt_ns": { - "type": "number" - }, - "substeps": { - "type": "integer" - }, - "step_ns": { - "type": "number" - }, - "steps": { - "type": "integer" - }, - "norm_max": { - "type": "number" - }, - "frame": { - "type": "string" - } - } - }, - "device": { - "type": "object", - "properties": { - "levels": { - "type": "integer" - }, - "dt_ns": { - "type": "number" - }, - "qubits": { - "type": "array", - "items": { - "type": "object", - "properties": { - "qubit": { - "type": "integer" - }, - "frequency_hz": { - "type": "number" - }, - "rabi_hz": { - "type": "number" - }, - "anharmonicity_hz": { - "type": "number" - }, - "t1_ns": { - "type": "number" - }, - "t2_ns": { - "type": "number" - }, - "from": { - "type": "object", - "additionalProperties": { - "enum": [ - "pulse", - "target", - "default", - "none" - ] - } - } - } - } - }, - "couplings": { - "type": "array" - }, - "model": { - "type": "string" - } - } - }, - "calibration": { - "type": "object", - "description": "What the receipt binds: the program's, its calibrations', the schedule's and the device's digests, and the target snapshot.", - "properties": { - "program": { - "type": "string" - }, - "calibrations": { - "type": "string" - }, - "schedule": { - "type": "string" - }, - "device": { - "type": "string" - }, - "target": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "calibration": { - "type": "object" - } - } - } - } - }, - "not_modelled": { - "type": "array", - "items": { - "type": "string" - } - } - } } } }, @@ -44043,7 +40766,7 @@ }, "method": { "type": "string", - "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it; pulse runs a pulse program (OpenPulse calibrations) under the pulse options." + "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it." }, "observables": { "type": "array", @@ -44597,121 +41320,6 @@ ] } ] - }, - "pulse": { - "type": "object", - "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", - "additionalProperties": false, - "properties": { - "dt_ns": { - "type": "number", - "exclusiveMinimum": 0, - "maximum": 1000, - "description": "The sample period, ns (default 0.5)." - }, - "levels": { - "type": "integer", - "enum": [ - 2, - 3 - ], - "description": "Levels kept per qubit (default 2; 3 shows leakage)." - }, - "tolerance": { - "type": "number", - "exclusiveMinimum": 0, - "exclusiveMaximum": 1, - "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." - }, - "substeps": { - "type": "integer", - "minimum": 1, - "maximum": 4096, - "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." - }, - "qubits": { - "type": "object", - "description": "Each qubit's parameters by its index, over the target's and the defaults.", - "additionalProperties": { - "type": "object", - "additionalProperties": false, - "properties": { - "frequency_ghz": { - "type": "number", - "description": "Its frequency, GHz." - }, - "rabi_mhz": { - "type": "number", - "description": "Its Rabi frequency at full amplitude, MHz." - }, - "anharmonicity_mhz": { - "type": "number", - "description": "Its anharmonicity, MHz." - }, - "t1_us": { - "type": "number", - "description": "Its relaxation time, µs." - }, - "t2_us": { - "type": "number", - "description": "Its coherence time, µs (at most 2·T1)." - } - } - } - }, - "couplings": { - "type": "array", - "description": "Static ZZ couplings.", - "items": { - "type": "object", - "required": [ - "qubits", - "zz_khz" - ], - "additionalProperties": false, - "properties": { - "qubits": { - "type": "array", - "items": { - "type": "integer", - "minimum": 0 - }, - "minItems": 2, - "maxItems": 2 - }, - "zz_khz": { - "type": "number", - "description": "The coupling's rate, kHz." - } - } - } - }, - "ports": { - "type": "object", - "description": "Ports by name: what each is for and the qubit it reaches.", - "additionalProperties": { - "type": "object", - "required": [ - "kind", - "qubit" - ], - "additionalProperties": false, - "properties": { - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "qubit": { - "type": "integer", - "minimum": 0 - } - } - } - } - } } } } @@ -46903,1214 +43511,6 @@ } ] } - }, - { - "name": "pulse_schedule", - "title": "Read a pulse program into its schedule", - "description": "Read a pulse program (OpenPulse calibrations: ports, frames, waveforms, play, delay, shift_phase, set_phase, shift_frequency, set_frequency, captures and barriers) into its schedule on frames: every instruction at its sample, each waveform's samples (gaussian, drag, gaussian_square, constant, sech, sine, or explicit), each gate call with the samples its calibration spans, and the digests a pulse run's receipt binds (the program, its calibrations, the schedule). Durations are whole samples of dt (pulse.dt_ns, default 0.5 ns) or refused. A construct the reader does not take is refused with a Q code and its line. Sealed with the service's key. To run it, give the program to run_quantum_circuit with method \"pulse\".", - "inputSchema": { - "type": "object", - "required": [ - "qasm" - ], - "properties": { - "qasm": { - "type": "string", - "description": "A pulse program: OpenQASM 3 with `defcalgrammar \"openpulse\";`, `cal` blocks declaring ports (d0 drives qubit 0, m0 is its measurement port, a0 its acquisition port), frames (newframe(port, frequency Hz, phase)) and waveforms, a `defcal` for every gate it calls on physical qubits ($0), and the calls." - }, - "pulse": { - "type": "object", - "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", - "additionalProperties": false, - "properties": { - "dt_ns": { - "type": "number", - "exclusiveMinimum": 0, - "maximum": 1000, - "description": "The sample period, ns (default 0.5)." - }, - "levels": { - "type": "integer", - "enum": [ - 2, - 3 - ], - "description": "Levels kept per qubit (default 2; 3 shows leakage)." - }, - "tolerance": { - "type": "number", - "exclusiveMinimum": 0, - "exclusiveMaximum": 1, - "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." - }, - "substeps": { - "type": "integer", - "minimum": 1, - "maximum": 4096, - "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." - }, - "qubits": { - "type": "object", - "description": "Each qubit's parameters by its index, over the target's and the defaults.", - "additionalProperties": { - "type": "object", - "additionalProperties": false, - "properties": { - "frequency_ghz": { - "type": "number", - "description": "Its frequency, GHz." - }, - "rabi_mhz": { - "type": "number", - "description": "Its Rabi frequency at full amplitude, MHz." - }, - "anharmonicity_mhz": { - "type": "number", - "description": "Its anharmonicity, MHz." - }, - "t1_us": { - "type": "number", - "description": "Its relaxation time, µs." - }, - "t2_us": { - "type": "number", - "description": "Its coherence time, µs (at most 2·T1)." - } - } - } - }, - "couplings": { - "type": "array", - "description": "Static ZZ couplings.", - "items": { - "type": "object", - "required": [ - "qubits", - "zz_khz" - ], - "additionalProperties": false, - "properties": { - "qubits": { - "type": "array", - "items": { - "type": "integer", - "minimum": 0 - }, - "minItems": 2, - "maxItems": 2 - }, - "zz_khz": { - "type": "number", - "description": "The coupling's rate, kHz." - } - } - } - }, - "ports": { - "type": "object", - "description": "Ports by name: what each is for and the qubit it reaches.", - "additionalProperties": { - "type": "object", - "required": [ - "kind", - "qubit" - ], - "additionalProperties": false, - "properties": { - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "qubit": { - "type": "integer", - "minimum": 0 - } - } - } - } - } - } - } - }, - "annotations": { - "readOnlyHint": true, - "idempotentHint": true, - "openWorldHint": false - }, - "outputSchema": { - "$id": "wai-quantum://schemas/pulse_schedule/1", - "type": "object", - "description": "A pulse answer as every host gives it (engine::pulse: pulse_schedule and pulse_check on both servers, wai-quantum pulse, POST /v1/pulse/*, the Studio): the request, and the result sealed in a job's form. A pulse program's schedule on frames, or a pulse job checked by running it again. A pulse run itself is a job (method pulse) and follows job-result.v1.json. A result names the engine that made it and this schema (engine and schema). docs/pulse.md explains each.", - "anyOf": [ - { - "title": "wai.quantum.pulse/1", - "description": "A pulse answer as every host gives it (engine::pulse: pulse_schedule and pulse_check on both servers, wai-quantum pulse, POST /v1/pulse/*, the Studio): the request, and the result sealed in a job's form. A pulse program's schedule on frames, or a pulse job checked by running it again. A pulse run itself is a job (method pulse) and follows job-result.v1.json. A result names the engine that made it and this schema (engine and schema). docs/pulse.md explains each.", - "type": "object", - "required": [ - "request", - "result" - ], - "properties": { - "request": { - "type": "object", - "description": "The call's request, as given." - }, - "result": { - "description": "A schedule or a check, with its receipt.", - "anyOf": [ - { - "type": "object", - "description": "A pulse program's schedule (pulse_schedule, wai-quantum pulse schedule, POST /v1/pulse/schedule): its frames and ports, every instruction at its sample, each waveform's samples, the gate calls, the reader's notes, and the digests a run binds.", - "required": [ - "answer", - "dt_ns", - "duration", - "frames", - "waveforms", - "instructions", - "calls", - "calibration" - ], - "properties": { - "engine": { - "type": "object", - "description": "The engine that made the result: the wai-quantum crate's name and version.", - "required": [ - "name", - "version" - ], - "properties": { - "name": { - "const": "wai-quantum" - }, - "version": { - "type": "string" - } - } - }, - "schema": { - "const": "wai.quantum.pulse/1" - }, - "answer": { - "const": "schedule" - }, - "dt_ns": { - "type": "number", - "description": "The sample period, ns." - }, - "duration": { - "type": "integer", - "description": "The latest time of any frame, samples." - }, - "duration_ns": { - "type": "number" - }, - "grammar": { - "type": [ - "string", - "null" - ] - }, - "ports": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "qubit": { - "type": "integer" - } - } - } - }, - "frames": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "port": { - "type": "string" - }, - "qubit": { - "type": "integer" - }, - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "frequency_hz": { - "type": "number" - }, - "phase": { - "type": "number" - } - } - } - }, - "waveforms": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "length": { - "type": "integer" - }, - "samples": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" - }, - "minItems": 2, - "maxItems": 2 - } - }, - "samples_every": { - "type": "integer", - "description": "Each k-th sample is drawn, when the schedule holds more than the view writes out." - } - } - } - }, - "instructions": { - "type": "array", - "items": { - "type": "object", - "required": [ - "frame", - "op", - "start", - "length" - ], - "properties": { - "frame": { - "type": "integer" - }, - "op": { - "enum": [ - "play", - "delay", - "shift_phase", - "set_phase", - "shift_frequency", - "set_frequency", - "capture", - "barrier" - ] - }, - "start": { - "type": "integer" - }, - "length": { - "type": "integer" - }, - "line": { - "type": "integer" - }, - "waveform": { - "type": "integer" - }, - "phase": { - "type": "number" - }, - "frequency_hz": { - "type": "number" - }, - "carrier": { - "type": "number", - "description": "The carrier's phase at the play's first sample, cycles in [0, 1)." - }, - "value": { - "type": "number" - }, - "bit": { - "type": [ - "string", - "null" - ] - } - } - } - }, - "calls": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "qubits": { - "type": "array", - "items": { - "type": "integer" - } - }, - "params": { - "type": "array", - "items": { - "type": "number" - } - }, - "start": { - "type": "integer" - }, - "end": { - "type": "integer" - }, - "line": { - "type": "integer" - } - } - } - }, - "bits": { - "type": "array", - "items": { - "type": "string" - } - }, - "qubits": { - "type": "array", - "items": { - "type": "integer" - } - }, - "decimated": { - "type": "string" - }, - "diagnostics": { - "type": "array", - "items": { - "type": "object", - "description": "A reader's note or warning (qasm::diag).", - "properties": { - "code": { - "type": "string" - }, - "outcome": { - "type": "string" - }, - "message": { - "type": "string" - }, - "line": { - "type": "integer" - }, - "column": { - "type": "integer" - }, - "length": { - "type": "integer" - } - } - } - }, - "calibration": { - "type": "object", - "description": "What a pulse run's receipt binds of the program: the BLAKE3 of the control program's text, of its calibrations as written (every cal, defcal and defcalgrammar, joined by newlines), and of the schedule's JSON; a run adds the device's and the target snapshot's.", - "required": [ - "program", - "calibrations", - "schedule" - ], - "properties": { - "program": { - "type": "string" - }, - "calibrations": { - "type": "string" - }, - "schedule": { - "type": "string" - }, - "device": { - "type": "string" - }, - "target": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "calibration": { - "type": "object" - } - } - } - } - }, - "receipt": { - "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", - "type": "object", - "required": [ - "alg", - "canon", - "key", - "digest", - "signature" - ], - "properties": { - "alg": { - "const": "ed25519" - }, - "canon": { - "const": "rfc8785", - "description": "The canonical form the digest is taken over." - }, - "key": { - "type": "string", - "minLength": 64, - "maxLength": 64, - "description": "The signing key, 32 bytes in hex." - }, - "digest": { - "type": "string", - "minLength": 64, - "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." - }, - "signature": { - "type": "string", - "minLength": 128, - "maxLength": 128, - "description": "The signature, 64 bytes in hex." - } - } - } - } - }, - { - "type": "object", - "description": "A pulse job checked (pulse_check, wai-quantum pulse check, POST /v1/pulse/check): its receipt's signature, its program's digest, and the request run again with the result held to the one given.", - "required": [ - "answer", - "verified", - "signature", - "program", - "reproduced", - "differences", - "statement" - ], - "properties": { - "engine": { - "type": "object", - "description": "The engine that made the result: the wai-quantum crate's name and version.", - "required": [ - "name", - "version" - ], - "properties": { - "name": { - "const": "wai-quantum" - }, - "version": { - "type": "string" - } - } - }, - "schema": { - "const": "wai.quantum.pulse/1" - }, - "answer": { - "const": "check" - }, - "verified": { - "type": "boolean" - }, - "signature": { - "type": "boolean" - }, - "key": {}, - "program": { - "type": "boolean" - }, - "reproduced": { - "type": "boolean" - }, - "differences": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Each field (or field.member) whose value differs from the run here." - }, - "density_hash": { - "type": "object", - "properties": { - "given": {}, - "recomputed": {} - } - }, - "calibration": {}, - "statement": { - "type": "string" - }, - "receipt": { - "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", - "type": "object", - "required": [ - "alg", - "canon", - "key", - "digest", - "signature" - ], - "properties": { - "alg": { - "const": "ed25519" - }, - "canon": { - "const": "rfc8785", - "description": "The canonical form the digest is taken over." - }, - "key": { - "type": "string", - "minLength": 64, - "maxLength": 64, - "description": "The signing key, 32 bytes in hex." - }, - "digest": { - "type": "string", - "minLength": 64, - "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." - }, - "signature": { - "type": "string", - "minLength": 128, - "maxLength": 128, - "description": "The signature, 64 bytes in hex." - } - } - } - } - } - ] - } - } - }, - { - "type": "object", - "description": "A refusal as data: the message, every code in it (the last is the code), its outcome and meaning as the code's table gives them. A host that refuses for its own reasons gives the error alone.", - "required": [ - "error" - ], - "properties": { - "error": { - "type": "string" - }, - "message": { - "type": "string" - }, - "code": { - "type": [ - "string", - "null" - ] - }, - "codes": { - "type": "array", - "items": { - "type": "string" - } - }, - "outcome": { - "enum": [ - "refused", - "retry", - "failed", - "lowered", - "carried", - "preserved", - "warned", - "dropped", - "defaulted" - ] - }, - "meaning": { - "type": "string" - } - } - } - ] - } - }, - { - "name": "pulse_check", - "title": "Check a pulse run by running it again", - "description": "Check a pulse job as a host returned it ({\"request\", \"result\"}, the result with its receipt): the receipt's signature, the program's digest against the request's text, and the request run again here with the result held to the one given field by field (the density matrix's digest among them), each difference named. A pulse run is the same bits on every platform, so a result that does not reproduce was not made from that request by this engine. Sealed with the service's key. Quote `verified` and the statement.", - "inputSchema": { - "type": "object", - "required": [ - "request", - "result" - ], - "properties": { - "request": { - "type": "object", - "description": "The pulse job's request." - }, - "result": { - "type": "object", - "description": "Its result, with its receipt." - } - } - }, - "annotations": { - "readOnlyHint": true, - "idempotentHint": true, - "openWorldHint": false - }, - "outputSchema": { - "$id": "wai-quantum://schemas/pulse_check/1", - "type": "object", - "description": "A pulse answer as every host gives it (engine::pulse: pulse_schedule and pulse_check on both servers, wai-quantum pulse, POST /v1/pulse/*, the Studio): the request, and the result sealed in a job's form. A pulse program's schedule on frames, or a pulse job checked by running it again. A pulse run itself is a job (method pulse) and follows job-result.v1.json. A result names the engine that made it and this schema (engine and schema). docs/pulse.md explains each.", - "anyOf": [ - { - "title": "wai.quantum.pulse/1", - "description": "A pulse answer as every host gives it (engine::pulse: pulse_schedule and pulse_check on both servers, wai-quantum pulse, POST /v1/pulse/*, the Studio): the request, and the result sealed in a job's form. A pulse program's schedule on frames, or a pulse job checked by running it again. A pulse run itself is a job (method pulse) and follows job-result.v1.json. A result names the engine that made it and this schema (engine and schema). docs/pulse.md explains each.", - "type": "object", - "required": [ - "request", - "result" - ], - "properties": { - "request": { - "type": "object", - "description": "The call's request, as given." - }, - "result": { - "description": "A schedule or a check, with its receipt.", - "anyOf": [ - { - "type": "object", - "description": "A pulse program's schedule (pulse_schedule, wai-quantum pulse schedule, POST /v1/pulse/schedule): its frames and ports, every instruction at its sample, each waveform's samples, the gate calls, the reader's notes, and the digests a run binds.", - "required": [ - "answer", - "dt_ns", - "duration", - "frames", - "waveforms", - "instructions", - "calls", - "calibration" - ], - "properties": { - "engine": { - "type": "object", - "description": "The engine that made the result: the wai-quantum crate's name and version.", - "required": [ - "name", - "version" - ], - "properties": { - "name": { - "const": "wai-quantum" - }, - "version": { - "type": "string" - } - } - }, - "schema": { - "const": "wai.quantum.pulse/1" - }, - "answer": { - "const": "schedule" - }, - "dt_ns": { - "type": "number", - "description": "The sample period, ns." - }, - "duration": { - "type": "integer", - "description": "The latest time of any frame, samples." - }, - "duration_ns": { - "type": "number" - }, - "grammar": { - "type": [ - "string", - "null" - ] - }, - "ports": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "qubit": { - "type": "integer" - } - } - } - }, - "frames": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "port": { - "type": "string" - }, - "qubit": { - "type": "integer" - }, - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "frequency_hz": { - "type": "number" - }, - "phase": { - "type": "number" - } - } - } - }, - "waveforms": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "length": { - "type": "integer" - }, - "samples": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" - }, - "minItems": 2, - "maxItems": 2 - } - }, - "samples_every": { - "type": "integer", - "description": "Each k-th sample is drawn, when the schedule holds more than the view writes out." - } - } - } - }, - "instructions": { - "type": "array", - "items": { - "type": "object", - "required": [ - "frame", - "op", - "start", - "length" - ], - "properties": { - "frame": { - "type": "integer" - }, - "op": { - "enum": [ - "play", - "delay", - "shift_phase", - "set_phase", - "shift_frequency", - "set_frequency", - "capture", - "barrier" - ] - }, - "start": { - "type": "integer" - }, - "length": { - "type": "integer" - }, - "line": { - "type": "integer" - }, - "waveform": { - "type": "integer" - }, - "phase": { - "type": "number" - }, - "frequency_hz": { - "type": "number" - }, - "carrier": { - "type": "number", - "description": "The carrier's phase at the play's first sample, cycles in [0, 1)." - }, - "value": { - "type": "number" - }, - "bit": { - "type": [ - "string", - "null" - ] - } - } - } - }, - "calls": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "qubits": { - "type": "array", - "items": { - "type": "integer" - } - }, - "params": { - "type": "array", - "items": { - "type": "number" - } - }, - "start": { - "type": "integer" - }, - "end": { - "type": "integer" - }, - "line": { - "type": "integer" - } - } - } - }, - "bits": { - "type": "array", - "items": { - "type": "string" - } - }, - "qubits": { - "type": "array", - "items": { - "type": "integer" - } - }, - "decimated": { - "type": "string" - }, - "diagnostics": { - "type": "array", - "items": { - "type": "object", - "description": "A reader's note or warning (qasm::diag).", - "properties": { - "code": { - "type": "string" - }, - "outcome": { - "type": "string" - }, - "message": { - "type": "string" - }, - "line": { - "type": "integer" - }, - "column": { - "type": "integer" - }, - "length": { - "type": "integer" - } - } - } - }, - "calibration": { - "type": "object", - "description": "What a pulse run's receipt binds of the program: the BLAKE3 of the control program's text, of its calibrations as written (every cal, defcal and defcalgrammar, joined by newlines), and of the schedule's JSON; a run adds the device's and the target snapshot's.", - "required": [ - "program", - "calibrations", - "schedule" - ], - "properties": { - "program": { - "type": "string" - }, - "calibrations": { - "type": "string" - }, - "schedule": { - "type": "string" - }, - "device": { - "type": "string" - }, - "target": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "calibration": { - "type": "object" - } - } - } - } - }, - "receipt": { - "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", - "type": "object", - "required": [ - "alg", - "canon", - "key", - "digest", - "signature" - ], - "properties": { - "alg": { - "const": "ed25519" - }, - "canon": { - "const": "rfc8785", - "description": "The canonical form the digest is taken over." - }, - "key": { - "type": "string", - "minLength": 64, - "maxLength": 64, - "description": "The signing key, 32 bytes in hex." - }, - "digest": { - "type": "string", - "minLength": 64, - "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." - }, - "signature": { - "type": "string", - "minLength": 128, - "maxLength": 128, - "description": "The signature, 64 bytes in hex." - } - } - } - } - }, - { - "type": "object", - "description": "A pulse job checked (pulse_check, wai-quantum pulse check, POST /v1/pulse/check): its receipt's signature, its program's digest, and the request run again with the result held to the one given.", - "required": [ - "answer", - "verified", - "signature", - "program", - "reproduced", - "differences", - "statement" - ], - "properties": { - "engine": { - "type": "object", - "description": "The engine that made the result: the wai-quantum crate's name and version.", - "required": [ - "name", - "version" - ], - "properties": { - "name": { - "const": "wai-quantum" - }, - "version": { - "type": "string" - } - } - }, - "schema": { - "const": "wai.quantum.pulse/1" - }, - "answer": { - "const": "check" - }, - "verified": { - "type": "boolean" - }, - "signature": { - "type": "boolean" - }, - "key": {}, - "program": { - "type": "boolean" - }, - "reproduced": { - "type": "boolean" - }, - "differences": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Each field (or field.member) whose value differs from the run here." - }, - "density_hash": { - "type": "object", - "properties": { - "given": {}, - "recomputed": {} - } - }, - "calibration": {}, - "statement": { - "type": "string" - }, - "receipt": { - "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", - "type": "object", - "required": [ - "alg", - "canon", - "key", - "digest", - "signature" - ], - "properties": { - "alg": { - "const": "ed25519" - }, - "canon": { - "const": "rfc8785", - "description": "The canonical form the digest is taken over." - }, - "key": { - "type": "string", - "minLength": 64, - "maxLength": 64, - "description": "The signing key, 32 bytes in hex." - }, - "digest": { - "type": "string", - "minLength": 64, - "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." - }, - "signature": { - "type": "string", - "minLength": 128, - "maxLength": 128, - "description": "The signature, 64 bytes in hex." - } - } - } - } - } - ] - } - } - }, - { - "type": "object", - "description": "A refusal as data: the message, every code in it (the last is the code), its outcome and meaning as the code's table gives them. A host that refuses for its own reasons gives the error alone.", - "required": [ - "error" - ], - "properties": { - "error": { - "type": "string" - }, - "message": { - "type": "string" - }, - "code": { - "type": [ - "string", - "null" - ] - }, - "codes": { - "type": "array", - "items": { - "type": "string" - } - }, - "outcome": { - "enum": [ - "refused", - "retry", - "failed", - "lowered", - "carried", - "preserved", - "warned", - "dropped", - "defaulted" - ] - }, - "meaning": { - "type": "string" - } - } - } - ] - } } ] } diff --git a/wai/wai-quantum-cli/src/mcp.rs b/wai/wai-quantum-cli/src/mcp.rs index 0f09611a9..a77e9dd54 100644 --- a/wai/wai-quantum-cli/src/mcp.rs +++ b/wai/wai-quantum-cli/src/mcp.rs @@ -100,8 +100,9 @@ fn output_of(tool: &str) -> Option<&'static str> { fn job_schema() -> Value { json!({ "type": "object", - "required": ["qasm"], + "anyOf": wai_quantum::engine::formats::program_required(), "properties": { + "source": wai_quantum::engine::formats::source_schema(), "qasm": { "type": "string", "description": "The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in \"inputs\"). while, inv and pow are refused with their line." }, "inputs": inputs_schema(), "shots": { "type": "integer", "minimum": 0, "description": "Shots to sample; 1024 when nothing else is asked." }, @@ -473,6 +474,19 @@ fn call(name: &str, args: &Value, memory_gb: u64, key: &SigningKey, client: &Cli let result = wai_quantum::engine::pulse::call(op, args)?; return Ok(wai_quantum::engine::pulse::seal(args, result, key)); } + // A run's program may come in another format, read to its qasm first, as + // the job service reads it. + let expanded; + let args = match name { + "run_quantum_circuit" | "plan_quantum_circuit" | "run_on_target" => match wai_quantum::engine::formats::expand(args)? { + Some(e) => { + expanded = e; + &expanded + } + None => args, + }, + _ => args, + }; // A tool that reads a generator at its inputs itself takes them as given. let raw = args; // The debugger reads a generator at its inputs itself, so its lines are the generator's. @@ -843,6 +857,30 @@ mod tests { SigningKey::from_bytes(&[5; 32]) } + /// A run takes its program in another format as the job service's run + /// tools take it: the same `source`, read to its qasm first, and the same + /// refusal, the reader's code before the job layer's. + #[test] + fn a_run_takes_its_program_as_a_source_as_the_service_does() { + let listed = tools(); + for name in ["run_quantum_circuit", "plan_quantum_circuit", "run_on_target"] { + let t = listed.as_array().unwrap().iter().find(|t| t["name"] == name).unwrap(); + assert_eq!(t["inputSchema"]["properties"]["source"], wai_quantum::engine::formats::source_schema(), "{name}"); + } + let bell = "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n"; + let qir = wai_quantum::engine::formats::convert_json(&json!({ "program": bell, "to": "qir" })).unwrap(); + let call_run = |args: Value| handle(&json!({ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "run_quantum_circuit", "arguments": args } }), 1, &key(), &mut Client::default()).unwrap()["result"].clone(); + let r = call_run(json!({ "source": { "format": "qir", "program": qir["program"] }, "shots": 64 })); + assert_eq!(r["isError"], false, "{r}"); + let counts = r["structuredContent"]["result"]["counts"].as_object().unwrap(); + assert!(counts.keys().all(|k| k == "00" || k == "11"), "{r}"); + assert_eq!(r["structuredContent"]["request"]["source"]["format"], "qir", "the request keeps its source"); + let bad = call_run(json!({ "source": { "format": "qir", "program": "define void @main() {\nentry:\n call void @__quantum__qis__foo__body(ptr null)\n ret void\n}\n" } })); + assert_eq!((bad["isError"].as_bool(), bad["structuredContent"]["codes"].clone()), (Some(true), json!(["Q706", "J001"])), "{bad}"); + let both = call_run(json!({ "qasm": bell, "source": { "format": "qir", "program": qir["program"] } })); + assert_eq!(both["structuredContent"]["code"], "J003", "{both}"); + } + #[test] fn an_agent_initializes_lists_the_tools_and_runs_a_circuit() { let init = handle(&json!({ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": PROTOCOL } }), 1, &key(), &mut Client::default()).unwrap(); @@ -1308,6 +1346,8 @@ mod tests { call_tool("check_equivalent", json!({ "a": draft, "b": "OPENQASM 3.0;\nqubit[2] q;\ncx q[0], q[1];\nt q[1];\n" }), false, &mut called, &mut hold); call_tool("improve_qasm", json!({ "qasm": draft }), false, &mut called, &mut hold); let qir = call_tool("convert_program", json!({ "program": draft, "to": "qir" }), false, &mut called, &mut hold); + call_tool("run_quantum_circuit", json!({ "source": { "format": "qir", "program": qir["program"] }, "shots": 20 }), false, &mut called, &mut hold); + call_tool("run_quantum_circuit", json!({ "source": { "format": "qir", "program": "define void @main() {\nentry:\n call void @__quantum__qis__foo__body(ptr null)\n ret void\n}\n" } }), true, &mut called, &mut hold); let jeff = call_tool("convert_program", json!({ "program": qir["program"], "to": "jeff" }), false, &mut called, &mut hold); call_tool("convert_program", json!({ "program_base64": jeff["program_base64"], "to": "qasm" }), false, &mut called, &mut hold); call_tool("convert_program", json!({ "program": "define void @main() {\nentry:\n call void @__quantum__qis__foo__body(ptr null)\n ret void\n}\n", "from": "qir", "to": "qasm" }), true, &mut called, &mut hold); diff --git a/wai/wai-quantum-cli/src/targets.rs b/wai/wai-quantum-cli/src/targets.rs index 743f7e8c2..c01aee03d 100644 --- a/wai/wai-quantum-cli/src/targets.rs +++ b/wai/wai-quantum-cli/src/targets.rs @@ -242,8 +242,9 @@ pub fn main(cmd: Cmd) { /// The agent's tools for targets ([`crate::mcp::tools`]). pub fn mcp_tools(job_schema: Value) -> Vec { let mut run = job_schema; - if let Some(r) = run["required"].as_array_mut() { - r.push(json!("target")); + match run["required"].as_array_mut() { + Some(r) => r.push(json!("target")), + None => run["required"] = json!(["target"]), } vec![ json!({ diff --git a/wai/wai-quantum-service/src/mcp.rs b/wai/wai-quantum-service/src/mcp.rs index 2239379cb..163814b63 100644 --- a/wai/wai-quantum-service/src/mcp.rs +++ b/wai/wai-quantum-service/src/mcp.rs @@ -230,23 +230,26 @@ fn tool_list() -> Value { "intent": intent_schema(), "repair": { "description": "Repair the program first, on the lines its diagnostics flag alone (as repair_qasm does): true, or the choices {\"max_iterations\": N, \"dyadic_bits\": K, \"sizes\": {\"q\": 3}}. The result's `repaired` holds the program that ran and each repair." }, "mitigate": wai_quantum::engine::mitigate::schema(), - "pulse": wai_quantum::engine::pulse::options_schema() + "pulse": wai_quantum::engine::pulse::options_schema(), + "source": wai_quantum::engine::formats::source_schema() }); - let mut on_target = json!({ "type": "object", "required": ["qasm", "target"], "properties": job_props.clone() }); + let mut on_target = json!({ "type": "object", "required": ["target"], "anyOf": wai_quantum::engine::formats::program_required(), "properties": job_props.clone() }); on_target["properties"].as_object_mut().map(|o| o.remove("method")); // A set: the job's fields, shared by every item, and its programs or its sweep. let mut set_props = job_props.clone(); + set_props.as_object_mut().map(|o| o.remove("source")); set_props["qasm"] = json!({ "type": "string", "description": "With sweep: the program, which declares the inputs the sweep gives values (`input uint n;`, `input float theta;`, `input angle phi;`)." }); set_props["programs"] = json!({ "type": "array", "minItems": 1, "maxItems": wai_quantum::engine::sets::MAX_ITEMS, "items": { "anyOf": [{ "type": "string" }, { "type": "object", "required": ["qasm"] }] }, "description": "The set's programs: each a program's text, or a job ({\"qasm\", …}) whose own fields win over the set's." }); set_props["sweep"] = json!({ "type": "object", "additionalProperties": { "type": "array", "minItems": 1, "items": { "type": "number" } }, "description": "Each input of qasm and its values: every combination runs, the names in sorted order, the first varying slowest; an integer input's values are whole numbers." }); let mut plan_props = set_props.clone(); plan_props["qasm"] = job_props["qasm"].clone(); + plan_props["source"] = job_props["source"].clone(); let mut list = json!([ { "name": "run_quantum_circuit", "title": "Run a quantum circuit", "description": "Run an OpenQASM program and return measurement counts, Pauli expectation values and/or amplitudes. The service chooses the method (bit-exact or double-precision state vector up to 32 qubits, single precision on a GPU to 33, stabilizer tableau for Clifford circuits up to 32768 qubits, matrix product states up to 4096, sparse Pauli dynamics for expectation values up to 1024 with an error bound, an exact density matrix or trajectories for noise), reports why and how accurate, and signs the result. Give `intent` (an exact distribution, or a reference program it must equal or whose state it must prepare) and the engine checks it with the run. Every answer carries `verdict`: the level the engine checked to (proof, evidence, executed, parsed, refused), whether the intent holds and a plain statement, sealed in the receipt. Waits up to 50 s; a longer job answers with status running and an id for get_quantum_job.", - "inputSchema": { "type": "object", "required": ["qasm"], "properties": job_props }, + "inputSchema": { "type": "object", "anyOf": wai_quantum::engine::formats::program_required(), "properties": job_props }, "annotations": { "readOnlyHint": false, "idempotentHint": true, "openWorldHint": false } }, { @@ -963,6 +966,8 @@ mod tests { ("critique_qasm", json!({}), true), ("check_equivalent", json!({ "a": bell, "b": "OPENQASM 3.0;\nqubit[2] q;\ncx q[0], q[1];\n" }), false), ("convert_program", json!({ "program": bell, "to": "qir" }), false), + ("run_quantum_circuit", json!({ "source": { "format": "qir", "program": wai_quantum::engine::formats::convert_json(&json!({ "program": bell, "to": "qir" })).unwrap()["program"] }, "shots": 10 }), false), + ("run_quantum_circuit", json!({ "source": { "format": "qir", "program": "define void @main() {\nentry:\n call void @__quantum__qis__foo__body(ptr null)\n ret void\n}\n" } }), true), ("convert_program", json!({ "program": bell, "to": "jeff", "check": false }), false), ("convert_program", json!({ "program": "define void @main() {\nentry:\n call void @__quantum__qis__foo__body(ptr null)\n ret void\n}\n", "from": "qir", "to": "qasm" }), true), ("improve_qasm", json!({ "qasm": bell }), false), diff --git a/wai/wai-quantum/src/engine/formats/mod.rs b/wai/wai-quantum/src/engine/formats/mod.rs index 76ae92d54..9d489c195 100644 --- a/wai/wai-quantum/src/engine/formats/mod.rs +++ b/wai/wai-quantum/src/engine/formats/mod.rs @@ -487,22 +487,48 @@ pub fn tool() -> Value { }) } +/// The schema of a job's `source`, as both servers' run tools take it: the +/// program in another format, in place of `qasm`. +pub fn source_schema() -> Value { + json!({ + "type": "object", + "required": ["format"], + "description": "The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source.", + "properties": { + "format": { "type": "string", "enum": ["qasm", "qir", "jeff"], "description": "Its format." }, + "program": { "type": "string", "description": "The program's text: QIR (LLVM text) or OpenQASM." }, + "program_base64": { "type": "string", "description": "The program's bytes as base64: jeff." } + } + }) +} + +/// What a run tool's input requires: its program, as `qasm` or as `source`. +pub fn program_required() -> Value { + json!([{ "required": ["qasm"] }, { "required": ["source"] }]) +} + /// A job whose program is in another format, with `"qasm"` read from its /// `"source"`: `{"source": {"format", "program" | "program_base64"}}`. A -/// request with no `source` is `None`, to be read as it is; one with both -/// `source` and `qasm` is refused, as is a source that does not read. +/// request with no `source` is `None`, to be read as it is, and so is one +/// whose `qasm` is what its `source` reads to (read already); one whose +/// `qasm` is something else is refused, as is a source that does not read. pub fn expand(req: &Value) -> Result, String> { use crate::engine::refusal::{BAD_FIELD, NOT_A_JOB, tag}; let Some(src) = req.get("source").filter(|s| !s.is_null()) else { return Ok(None) }; - if req.get("qasm").is_some_and(|q| !q.is_null()) { - return Err(tag(BAD_FIELD, "a job gives its program as \"qasm\" or as \"source\", not both")); - } + let given = req.get("qasm").filter(|q| !q.is_null()); if !src.is_object() { return Err(tag(BAD_FIELD, "\"source\" is {\"format\", \"program\" or \"program_base64\"}")); } let bytes = program_bytes(src)?; let format = format_field(src, "format")?.unwrap_or_else(|| Format::sniff(&bytes)); let imported = read(format, &bytes).map_err(|d| tag(NOT_A_JOB, format!("the source does not read as {}: {d}", format.name())))?; + // A request read already (one a receipt sealed) holds both, alike. + if let Some(q) = given { + if q.as_str() == Some(imported.qasm.as_str()) { + return Ok(None); + } + return Err(tag(BAD_FIELD, "a job gives its program as \"qasm\" or as \"source\": this one gives both, and the qasm is not what the source reads to")); + } let mut out = req.clone(); out["qasm"] = json!(imported.qasm); Ok(Some(out)) diff --git a/wai/wai-quantum/src/engine/formats/tests.rs b/wai/wai-quantum/src/engine/formats/tests.rs index 660dad0bb..3241a9ec3 100644 --- a/wai/wai-quantum/src/engine/formats/tests.rs +++ b/wai/wai-quantum/src/engine/formats/tests.rs @@ -776,6 +776,11 @@ fn convert_json_carries_a_program_between_every_pair_and_checks_it() { assert_equivalent(job["qasm"].as_str().unwrap(), src); assert!(expand(&json!({ "qasm": src })).unwrap().is_none()); assert!(expand(&json!({ "qasm": src, "source": { "program": "x" } })).is_err()); + // A job read already reads as it is; one whose qasm is another program is refused. + assert!(expand(&job).unwrap().is_none()); + let mut other = job.clone(); + other["qasm"] = json!(src); + assert!(expand(&other).unwrap_err().ends_with("[J003]")); } #[test] -- 2.54.0 (Apple Git-157) From f55e7c03a0fa4444300275fc7dd73d3fbc760151 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Fri, 30 Oct 2026 12:10:52 -0400 Subject: [PATCH 07/10] quantum-studio: open QIR and jeff files through the engine "Open" takes .ll, .qir and .jeff beside .qasm: the engine reads the file to OpenQASM 3 in the page (engine_convert, in the worker), the program goes into the editor as a version from the file, and the line under it names the format and its version, the check (written back to its format and read again) and each of the reader's notes with its code. A module the reader refuses leaves the editor as it was, the refusal and its code in the status line. The desktop app answers the same request with a convert command. The Studio e2e opens a QIR module, a jeff module and a module its profile forbids; the desktop's test is pinned in CI. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/wai-quantum-studio.yml | 1 + wai/CHANGELOG.md | 4 ++- wai/quantum-studio/README.md | 2 ++ wai/quantum-studio/README.zh-CN.md | 2 ++ wai/quantum-studio/locales/en/frame.json | 3 +++ wai/quantum-studio/locales/zh-CN/frame.json | 3 +++ wai/quantum-studio/src/engine.js | 5 ++++ wai/quantum-studio/src/main.js | 29 ++++++++++++++++++--- wai/quantum-studio/src/worker.js | 1 + wai/quantum-studio/test/studio.test.mjs | 21 +++++++++++++++ wai/wai-quantum-desktop/src/main.rs | 25 +++++++++++++++++- 11 files changed, 91 insertions(+), 5 deletions(-) diff --git a/.github/workflows/wai-quantum-studio.yml b/.github/workflows/wai-quantum-studio.yml index 98ab1177f..22539eef9 100644 --- a/.github/workflows/wai-quantum-studio.yml +++ b/.github/workflows/wai-quantum-studio.yml @@ -172,6 +172,7 @@ jobs: for t in an_update_is_held_to_this_build_s_key_and_the_version_it_was_signed_for only_a_copy_installed_by_its_own_installer_updates_itself the_updater_asks_the_release_s_manifest_and_requires_a_signed_version; do grep -q "^test tests::$t \.\.\. ok\$" "$RUNNER_TEMP/test.log" || { echo "::error::the update test $t did not run and pass"; exit 1; } done + grep -q '^test tests::a_program_in_another_format_opens_in_the_app \.\.\. ok$' "$RUNNER_TEMP/test.log" || { echo "::error::the formats test did not run and pass"; exit 1; } nice -n 10 cargo clippy --locked --release --all-targets -- -D warnings nice -n 10 cargo build --locked --release ./target/release/wai-quantum-desktop check diff --git a/wai/CHANGELOG.md b/wai/CHANGELOG.md index 48ef6c517..60c89c489 100644 --- a/wai/CHANGELOG.md +++ b/wai/CHANGELOG.md @@ -211,7 +211,9 @@ wire format may change and such changes are recorded here. job may carry `{"source": {"format", "program" | "program_base64"}}` in place of `qasm` (`job-request.v1.json`), as both MCP servers' run tools take it (`run_quantum_circuit`, `plan_quantum_circuit`, `run_on_target` and the dry run), one schema and one refusal; a request whose `qasm` is what its `source` reads to is read as it is. - The Studio's engine has `engine_convert`. + The Studio opens `.ll`, `.qir` and `.jeff` files through `engine_convert` (the desktop app through its `convert` + command): the program goes into the editor as OpenQASM 3, with the format, the check and each note under it, and a + refusal leaves the editor as it was with its code. - **Hostile input:** every size, count, index and bound a file gives is held to a cap before anything is made from it (a register's size before its qubits: a jeff register of 2^32 − 1 qubits is refused, `Q111`, where it once asked for 34 GB and trapped in a page); the wire reader bounds every pointer in 64 bits and counts each list element diff --git a/wai/quantum-studio/README.md b/wai/quantum-studio/README.md index 639e34e01..6bcbbcba2 100644 --- a/wai/quantum-studio/README.md +++ b/wai/quantum-studio/README.md @@ -65,6 +65,8 @@ The engine makes each edit in the program's own text, through its syntax tree, s The diagram's description, which a screen reader reads, is the engine's account of the circuit: its qubits and bits, then each column, each gate with its role on each qubit, each measurement with its bit, and each condition as the legend writes it. "SVG" saves the drawing as the engine writes it, a standalone image that follows the colour of the text it is placed in, and "LaTeX" saves it as one picture in plain TikZ that needs only `\usepackage{tikz}`. +"Open" takes a program in OpenQASM, or in QIR (`.ll`, `.qir`) or jeff (`.jeff`), which the engine reads to OpenQASM 3 in the page (`engine_convert`; [the formats page](../wai-quantum/docs/formats.md)): the program goes into the editor, and the line under it names the format and its version, the check (the program written back to its format and read again) and each of the reader's notes with its code. A module the reader refuses leaves the editor as it was, and the status line gives the refusal with its code and its place. + ## Device Under **Device**, a device target: one of the built-in targets (synthetic, in the shapes of published formats), one imported here (**Import…** reads a calibration or target snapshot in any format the engine reads, kept in this browser), or, in the desktop app, one kept with `wai-quantum targets import`. The coupling map is drawn with each pair coloured by its two-qubit error and each qubit by its readout error, one-qubit error or T1. The colours are five bins of one hue, by quantile of the device's own values; the poorest pairs are drawn thicker, every mark gives its numbers on hover, and **As a table** lists them. With a device chosen, a run is compiled to its gates, placed on the qubits and routes most likely to succeed, and simulated under its noise; the noise fields wait, since the noise is the device's own. The layout the plan chose is drawn on the map before the run. The result's **Device** block names the target and its hash (in the receipt), the placement and its expected success beside the fewest swaps', the noise, and how far the counts are from the ideal ones. It is a model of the device, and it says so. On the service, a built-in target goes by name and any other as its document. diff --git a/wai/quantum-studio/README.zh-CN.md b/wai/quantum-studio/README.zh-CN.md index 6eb4b51e3..50f877fb6 100644 --- a/wai/quantum-studio/README.zh-CN.md +++ b/wai/quantum-studio/README.zh-CN.md @@ -69,6 +69,8 @@ Ctrl+Enter(macOS 上为 ⌘+Enter)运行程序,Escape 取消运行。程 线路图的描述(供屏幕阅读器朗读)是引擎对线路的叙述:先是其量子比特和比特,然后是每一列,每个门及其在每个量子比特上的角色,每次测量及其比特,以及按图例写法给出的每个条件。“SVG”按引擎写出的样子保存线路图,这是一张独立的图像,颜色随其所在位置的文字颜色而变;“LaTeX”则把它保存为一幅纯 TikZ 图,只需要 `\usepackage{tikz}`。 +“打开”(Open)接受 OpenQASM 程序,也接受 QIR(`.ll`、`.qir`)或 jeff(`.jeff`)文件,由引擎在页面中读取为 OpenQASM 3(`engine_convert`;[格式说明页](../wai-quantum/docs/formats.md)):程序放入编辑器,其下的一行说明格式及其版本、检查结果(程序写回原格式后再读取一次)以及读取器的每条说明及其代码。读取器拒绝的模块不会改变编辑器中的内容,状态行给出拒绝原因及其代码和位置。 + ## 设备 在**设备**(Device)下选择一个设备目标:内置目标之一(合成的,采用已公开格式的形态)、在此导入的目标(**导入…**(Import…)读取引擎能读取的任何格式的校准快照或目标快照,保存在这个浏览器中),或者在桌面应用中,用 `wai-quantum targets import` 保存的目标。耦合图中,每个耦合对按其双量子比特门误差着色,每个量子比特按其读出误差、单量子比特门误差或 T1 着色。颜色为同一色调的五档,按该设备自身数值的分位数划分;最差的耦合对画得更粗,每个标记在悬停时给出其数值,**以表格显示**(As a table)则把它们列出。选定设备后,运行会被编译为该设备的门,放置到最可能成功的量子比特和路由上,并在其噪声下模拟;噪声字段暂不起作用,因为噪声取自设备本身。计划选定的布局会在运行之前画在耦合图上。结果中的**设备**区块列出目标及其哈希(写入回执)、放置方式及其预期成功率(与交换次数最少的放置方式的预期成功率并列)、噪声,以及计数与理想计数相差多少。这是设备的一个模型,结果也如此注明。在作业服务上,内置目标按名称传递,其他目标则以其文档传递。 diff --git a/wai/quantum-studio/locales/en/frame.json b/wai/quantum-studio/locales/en/frame.json index e1c9666bc..4840d68d6 100644 --- a/wai/quantum-studio/locales/en/frame.json +++ b/wai/quantum-studio/locales/en/frame.json @@ -151,6 +151,9 @@ "frame.fixed": "Fixed, as {version}. Undo brings the lines back.", "frame.saved_job": "Saved {name}. wai-quantum verify checks it.", "frame.opened": "Opened {name}.", + "frame.opened_from": "Opened {name}, read from {format} as OpenQASM 3 (written back and checked: {check}). Notes: {notes}.", + "frame.open_refused": "{name} does not read as {format}: {error}", + "frame.no_notes": "none", "frame.copied": "Copied a link with the program in its fragment, which no server sees.", "frame.from_history": "From the history: run on {where}, {when}.", "frame.history_empty": "Jobs you run appear here, kept on this device.", diff --git a/wai/quantum-studio/locales/zh-CN/frame.json b/wai/quantum-studio/locales/zh-CN/frame.json index 3517dfb0e..4608b940b 100644 --- a/wai/quantum-studio/locales/zh-CN/frame.json +++ b/wai/quantum-studio/locales/zh-CN/frame.json @@ -151,6 +151,9 @@ "frame.fixed": "已修正,记为 {version}。撤销即可恢复这些行。", "frame.saved_job": "已保存 {name}。可用 wai-quantum verify 检查它。", "frame.opened": "已打开 {name}。", + "frame.opened_from": "已打开 {name},从 {format} 读取为 OpenQASM 3(写回后检查:{check})。说明:{notes}。", + "frame.open_refused": "{name} 无法按 {format} 读取:{error}", + "frame.no_notes": "无", "frame.copied": "已复制一个把程序放在片段中的链接,任何服务器都看不到它。", "frame.from_history": "来自历史:在{where}上运行,{when}。", "frame.history_empty": "你运行的作业会出现在这里,保存在这台设备上。", diff --git a/wai/quantum-studio/src/engine.js b/wai/quantum-studio/src/engine.js index 3c2ced4c4..ca1e18acd 100644 --- a/wai/quantum-studio/src/engine.js +++ b/wai/quantum-studio/src/engine.js @@ -64,6 +64,9 @@ export class TabEngine { verify(job) { return this.call('verify', { job }); } critique(qasm) { return this.call('critique', { qasm }); } equivalent(a, b) { return this.call('equivalent', { a, b }); } + // A program carried between formats (engine::formats): {to, program | program_base64, from?} in, the + // conversion (or its refusal as data, {error, code}) out. + convert(request) { return this.call('convert', { request }); } variants(qasm) { return this.call('variants', { qasm }); } shortcuts(qasm, thorough = false) { return this.call('shortcuts', { qasm, thorough }); } // A compile ({qasm, epsilon, level, basis, stages}): sealed with this browser's key. @@ -198,6 +201,8 @@ export class DesktopEngine { // A critique names a problem as {ok: false, error}, as a lint does. async critique(qasm) { return JSON.parse(await this.invoke('critique', { qasm })); } equivalent(a, b) { return this.call('equivalent', { a, b }); } + // A refusal comes back as data ({error, code}), as the tab's does. + async convert(request) { return JSON.parse(await this.invoke('convert', { request: JSON.stringify(request) })); } variants(qasm) { return this.call('variants', { qasm }); } shortcuts(qasm, thorough = false) { return this.call('shortcuts', { qasm, thorough }); } // A compile, sealed with this user's key. diff --git a/wai/quantum-studio/src/main.js b/wai/quantum-studio/src/main.js index d0ef918ca..4bebd3b07 100644 --- a/wai/quantum-studio/src/main.js +++ b/wai/quantum-studio/src/main.js @@ -110,6 +110,13 @@ const handsField = StateField.define({ }); const handGutter = gutter({ class: 'qs-hand-gutter', markers: (v) => v.state.field(handsField), initialSpacer: () => new HandMark('spacer', '') }); +// Bytes as standard base64, for a binary program sent to the engine. +function toBase64(bytes) { + let s = ''; + for (let i = 0; i < bytes.length; i += 0x8000) s += String.fromCharCode(...bytes.subarray(i, i + 0x8000)); + return btoa(s); +} + function mount(root) { const desktop = onDesktop(); // A host page with a heading of its own mounts the Studio with data-embedded, and its name is not a second h1. @@ -195,7 +202,7 @@ function mount(root) { // ---- the program ---- const lintBadge = el('span', { class: 'qs-badge', id: 'qs-lint', text: '…' }); - const fileIn = el('input', { type: 'file', accept: '.qasm,.txt,text/plain', hidden: true, 'aria-hidden': 'true' }); + const fileIn = el('input', { type: 'file', id: 'qs-open-file', accept: '.qasm,.ll,.qir,.jeff,.txt,text/plain', hidden: true, 'aria-hidden': 'true' }); const shows = el('p', { class: 'qs-shows', id: 'qs-shows' }); const reviewBox = el('div', { class: 'qs-review', id: 'qs-review', hidden: true, role: 'region', 'aria-label': tr('frame.review_label') }); const editorHost = el('div', { class: 'qs-editor', id: 'qs-editor' }); @@ -823,9 +830,25 @@ function mount(root) { openBtn.addEventListener('click', () => fileIn.click()); fileIn.addEventListener('change', async () => { const f = fileIn.files?.[0]; if (!f) return; - const body = await f.text(); - if (await from(`the file ${f.name}`, () => setText(body))) shows.textContent = tr('frame.opened', { name: f.name }); fileIn.value = ''; + // QIR and jeff are read by the engine to OpenQASM 3, with what it noted. + const format = /\.(ll|qir)$/i.test(f.name) ? 'qir' : /\.jeff$/i.test(f.name) ? 'jeff' : null; + if (!format) { + const body = await f.text(); + if (await from(`the file ${f.name}`, () => setText(body))) shows.textContent = tr('frame.opened', { name: f.name }); + return; + } + let r; + try { + const request = { from: format, to: 'qasm' }; + if (format === 'jeff') request.program_base64 = toBase64(new Uint8Array(await f.arrayBuffer())); + else request.program = await f.text(); + r = await local.convert(request); + } catch (e) { r = { error: e.message }; } + if (r.error) { setStatus(tr('frame.open_refused', { name: f.name, format: format === 'qir' ? 'QIR' : 'jeff', error: r.error }), 'bad'); return; } + if (!(await from(`the file ${f.name}`, () => setText(r.program)))) return; + const notes = (r.notes || []).map((n) => `${n.message} [${n.code}]`); + shows.textContent = tr('frame.opened_from', { name: f.name, format: format === 'qir' ? `QIR ${r.read?.version ?? ''}`.trim() : `jeff ${r.read?.version ?? ''}`.trim(), check: r.check?.verdict?.verdict ?? 'unchecked', notes: notes.length ? notes.join('; ') : tr('frame.no_notes') }); }); shareBtn.addEventListener('click', () => { const u = new URL(location.href); u.hash = 'qasm=' + encodeURIComponent(text()); u.search = ''; diff --git a/wai/quantum-studio/src/worker.js b/wai/quantum-studio/src/worker.js index 622363cf4..fba6efab8 100644 --- a/wai/quantum-studio/src/worker.js +++ b/wai/quantum-studio/src/worker.js @@ -39,6 +39,7 @@ self.onmessage = async (ev) => { case 'verify': text = mod.engine_verify(JSON.stringify(ev.data.job)); break; case 'critique': text = mod.engine_critique(ev.data.qasm); break; case 'equivalent': text = mod.engine_equivalent(ev.data.a, ev.data.b); break; + case 'convert': text = mod.engine_convert(JSON.stringify(ev.data.request)); break; case 'variants': text = mod.engine_variants(ev.data.qasm); break; case 'shortcuts': text = mod.engine_shortcuts(ev.data.qasm, !!ev.data.thorough); break; case 'compile': text = e().compile(JSON.stringify(ev.data.request)); break; diff --git a/wai/quantum-studio/test/studio.test.mjs b/wai/quantum-studio/test/studio.test.mjs index 04f7ab5d8..e2815250d 100644 --- a/wai/quantum-studio/test/studio.test.mjs +++ b/wai/quantum-studio/test/studio.test.mjs @@ -12,6 +12,8 @@ // - "Your hardware" says, in a browser tab, that such a run goes through the desktop // app or the command line, since a tab must not hold a provider's credential; // - an error in the program is marked on its line; +// - a QIR module and a jeff module open as OpenQASM 3 through the engine, with its +// notes and check, and a module its profile forbids is refused with its code; // - a rotation runs on the state vector, ⟨Z0⟩ = cos 0.3; // - a device target: its coupling map with each pair and qubit coloured by its error, // a run placed on it and simulated under its noise with the target's hash in the @@ -212,6 +214,25 @@ await page.keyboard.press('ControlOrMeta+End'); for (let i = 0; i < 10; i++) await page.keyboard.press('Backspace'); await page.waitForFunction(() => /qubits/.test(document.querySelector('#qs-lint')?.textContent ?? ''), null, { timeout: LONG }); +// A program in another format: QIR and jeff opened, read by the engine to +// OpenQASM 3 with its notes and its check; a module the reader refuses keeps +// the editor as it was and says why, with its code. +{ + const formats = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..', 'quantum-conformance', 'formats'); + await page.setInputFiles('#qs-open-file', { name: 'bell.ll', mimeType: 'text/plain', buffer: readFileSync(join(formats, 'qir', 'bell_base.ll')) }); + await page.waitForFunction(() => /read from QIR/.test(document.querySelector('#qs-shows')?.textContent ?? ''), null, { timeout: LONG }); + const fromQir = await page.evaluate(() => window.quantumStudio.view.state.doc.toString()); + check(/^\/\/ Read from QIR 2\.0, base profile/m.test(fromQir) && /OPENQASM 3\.0;/.test(fromQir) && /cx q\[0\], q\[1\];/.test(fromQir) && /checked: equivalent/.test(await page.textContent('#qs-shows')), `a QIR module opens as OpenQASM 3, checked (${(await page.textContent('#qs-shows')).slice(0, 120)}…)`); + await page.setInputFiles('#qs-open-file', { name: 'ghz.jeff', mimeType: 'application/octet-stream', buffer: readFileSync(join(formats, 'jeff', 'upstream', 'ghz-linear_3.jeff')) }); + await page.waitForFunction(() => /read from jeff/.test(document.querySelector('#qs-shows')?.textContent ?? ''), null, { timeout: LONG }); + const fromJeff = await page.evaluate(() => window.quantumStudio.view.state.doc.toString()); + check(/Read from jeff 0\.3/.test(fromJeff) && /qubit\[3\] q;/.test(fromJeff) && /\[Q715\]/.test(await page.textContent('#qs-shows')), `a jeff module opens as OpenQASM 3 with the reader's notes (${(await page.textContent('#qs-shows')).slice(0, 160)}…)`); + await page.waitForFunction(() => /qubits/.test(document.querySelector('#qs-lint')?.textContent ?? ''), null, { timeout: LONG }); + await page.setInputFiles('#qs-open-file', { name: 'forbidden.ll', mimeType: 'text/plain', buffer: readFileSync(join(formats, 'qir', 'refuse', 'base_branch.ll')) }); + await page.waitForFunction(() => /does not read as QIR/.test(document.querySelector('.qs-status')?.textContent ?? ''), null, { timeout: LONG }); + check(/\[Q704\]/.test(await page.textContent('.qs-status')) && (await page.evaluate(() => window.quantumStudio.view.state.doc.toString())) === fromJeff, `a module its profile forbids is refused with its code, the editor kept (${await page.textContent('.qs-status')})`); +} + // A rotation: any angle, on the state vector. await page.selectOption('#qs-example', 'rotation'); await page.waitForFunction(() => /State vector/.test(document.querySelector('#qs-plan')?.textContent ?? ''), null, { timeout: LONG }); diff --git a/wai/wai-quantum-desktop/src/main.rs b/wai/wai-quantum-desktop/src/main.rs index 074aa754c..992053657 100644 --- a/wai/wai-quantum-desktop/src/main.rs +++ b/wai/wai-quantum-desktop/src/main.rs @@ -214,6 +214,18 @@ fn equivalent(a: String, b: String) -> String { equiv::equivalent(&a, &b).to_string() } +/// A program carried between formats (OpenQASM 3, QIR, jeff as base64), +/// checked; a refusal as data, `{error, code, codes, outcome, meaning}`. +#[tauri::command] +fn convert(request: String) -> String { + let answer = serde_json::from_str::(&request).map_err(|e| format!("the request is not JSON: {e}")).and_then(|r| wai_quantum::engine::formats::convert_json(&r)); + answer.map(|v| v.to_string()).unwrap_or_else(|e| { + let mut r = wai_quantum::engine::refusal::refusal_json(&e); + r["error"] = serde_json::json!(e); + r.to_string() + }) +} + #[tauri::command] fn variants(qasm: String) -> String { critique::variants(&qasm).to_string() @@ -964,7 +976,7 @@ fn main() { .plugin(tauri_plugin_updater::Builder::new().build()) .manage(Studio { key, job_bytes: job_bytes(), running: Arc::new(Mutex::new(None)), cancelled: Arc::new(Mutex::new(false)) }) .manage(PendingUpdate(Mutex::new(None))) - .invoke_handler(tauri::generate_handler![run_job, cancel, plan_job, methods, lint, critique, equivalent, variants, shortcuts, compile, verdict, repair, course, course_lesson, course_check, course_hint, estimate, estimate_verify, qec, pulse, draw, layout, debug, visual_edit, picture, verify, signer, save_job, save_program, chat, models, discover_start, discover_submit, discover_search, discover_evaluate, discover_novelty, instantiate, history, locks, novelty, rewrite, taste, converge, study, providers_list, provider_estimate, run_on_provider, targets, target, target_place, answer, telemetry, update_check, update_install]) + .invoke_handler(tauri::generate_handler![run_job, cancel, plan_job, methods, lint, critique, equivalent, convert, variants, shortcuts, compile, verdict, repair, course, course_lesson, course_check, course_hint, estimate, estimate_verify, qec, pulse, draw, layout, debug, visual_edit, picture, verify, signer, save_job, save_program, chat, models, discover_start, discover_submit, discover_search, discover_evaluate, discover_novelty, instantiate, history, locks, novelty, rewrite, taste, converge, study, providers_list, provider_estimate, run_on_provider, targets, target, target_place, answer, telemetry, update_check, update_install]) .run(tauri::generate_context!()) .expect("the window opens"); } @@ -1254,6 +1266,17 @@ mod tests { assert!(s["summary"].as_str().unwrap().starts_with("Classically easy"), "{s}"); } + /// A QIR module opened in the app reads as OpenQASM 3, checked; one the + /// reader does not take is refused as data with its code. + #[test] + fn a_program_in_another_format_opens_in_the_app() { + let qir: Value = serde_json::from_str(&convert(json!({ "program": "qubit[2] q; h q[0]; cx q[0], q[1];", "to": "qir" }).to_string())).unwrap(); + let back: Value = serde_json::from_str(&convert(json!({ "program": qir["program"], "from": "qir", "to": "qasm" }).to_string())).unwrap(); + assert_eq!((back["from"].as_str(), back["check"]["verdict"]["verdict"].as_str()), (Some("qir"), Some("equivalent")), "{back}"); + let bad: Value = serde_json::from_str(&convert(json!({ "program": "define void @main() {\nentry:\n call void @__quantum__qis__foo__body(ptr null)\n ret void\n}\n", "from": "qir", "to": "qasm" }).to_string())).unwrap(); + assert_eq!(bad["code"], "Q706", "{bad}"); + } + #[test] fn the_history_records_with_the_app_s_key_and_reads_back() { let key = SigningKey::from_bytes(&[3; 32]); -- 2.54.0 (Apple Git-157) From 3d0760fa712e775258c771bd7564f833c38dbd16 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Fri, 30 Oct 2026 14:38:06 -0400 Subject: [PATCH 08/10] wai-quantum views: the page rebuilt with the Studio's words for other formats, in the image CI rebuilds it in The Studio's file open names QIR and jeff in locales/*/frame.json, which the views page is built from, so build-views --check refused the page. Rebuilt with wai/wai-quantum-views/build.sh in catthehacker/ubuntu:act-22.04 with Rust 1.98.0, as the views workflow rebuilds it. Co-Authored-By: Claude Opus 5.5 --- wai/wai-quantum/views/manifest.json | 14 +++++++------- wai/wai-quantum/views/wai-quantum-views.html | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/wai/wai-quantum/views/manifest.json b/wai/wai-quantum/views/manifest.json index 951273c3d..2dda39fad 100644 --- a/wai/wai-quantum/views/manifest.json +++ b/wai/wai-quantum/views/manifest.json @@ -4,19 +4,19 @@ "placeholder": "__WAI_QUANTUM_VIEW__", "limit_bytes": 1048576, "bytes": { - "page": 804708, - "script": 94216, + "page": 804920, + "script": 94428, "styles": 50804, "engine_wasm": 193526, "engine_wasm_base64": 258036, "engine_js": 401202 }, "sha256": { - "page": "9b412e847dde188381d790b7d50aaaee11985ea3c5ba61a2dcac46d0fe1ba170", - "script": "136d99215a1a8cc7a1302a107a222823272fe7371949ffa2816cbb4f9599776a", + "page": "3f0893586575dad6fa934ce6774141e45397d4120d044ed7353b3571415c698a", + "script": "47c5c3d1921b34a8f5fbf3e6d65f9436f8e6ed5f216615f4c0544e40847b3c02", "styles": "11ebcd4416c68e9eaf1156ca6f7c92b9a96744895ed13c377eba98fdd8dca9a6", - "engine_wasm": "8373ee77c3603419a70387e01ad7f906d4d93b4345b5674a7785dfb37e573c0a", - "engine_js": "d5ecf0d52b1295077c60af6411fdba0757d9f3b6861e0b51bc30b97d21307f5c" + "engine_wasm": "1620f0db3d5a48183a7ddf07759bd970ae28e39b0e53cd75df2ff87dc16d3ffb", + "engine_js": "b13874380b21d7285c11b04595e3f03b631ccf75ddfa3db70d6d103cfce1c99f" }, "sources": { "wai/quantum-studio/build-views.mjs": "da5b161829a990b3d1bb4c1b3ec7c8f254ff7b5078476156e504ae322bc4f010", @@ -32,7 +32,7 @@ "wai/quantum-studio/locales/en/estimate_view.json": "b20c94f96751eee548b011ea19f6ded9ecd8648cae32885b6b71b7d5e631b2b1", "wai/quantum-studio/locales/en/examples.json": "dd2f2f4aef033b00b3700c6eb7151c18dd1b43537ef0beeb17a8ffc069a564b2", "wai/quantum-studio/locales/en/flags.json": "d2beeef25cee8ec742d692e424ec54bdc821449733ae98b2882cc97711efffdd", - "wai/quantum-studio/locales/en/frame.json": "9a7b2174c2ed2ac10f0c9c8dd393df72a97ad70b2eb58072f6622321b6101e3e", + "wai/quantum-studio/locales/en/frame.json": "608565c19197c6210b37a917ed27bbb3dbf6338cbeff23eebae2219140a3650e", "wai/quantum-studio/locales/en/gallery.json": "5d0c2992e39ea0f82541e0491b46f4fd724085d95a988f8c15a1e3657d2eb149", "wai/quantum-studio/locales/en/generators.json": "85eb826cc6353cf12bc3fa45bbb8721210a311f145b9b46493c3e2bb44e7a3c2", "wai/quantum-studio/locales/en/hardware.json": "745786e872062468680655326362f49ae85dabae6ae9021145072dd4dbd7fbf1", diff --git a/wai/wai-quantum/views/wai-quantum-views.html b/wai/wai-quantum/views/wai-quantum-views.html index 785ff3d72..2d8ffdacf 100644 --- a/wai/wai-quantum/views/wai-quantum-views.html +++ b/wai/wai-quantum/views/wai-quantum-views.html @@ -11,10 +11,10 @@
- - -- 2.54.0 (Apple Git-157) From a80fb98c323f7bac7c782beba81480c0cb738649 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Fri, 30 Oct 2026 14:53:30 -0400 Subject: [PATCH 09/10] wai-quantum-service smoke: paced within the client's window; the agent docs for pulse and other formats together With the pulse and the other-format checks both in it, the smoke makes more submissions than a default window's 30, and the service refused the last with J030. Before each POST the script now reads where the client stands from GET /v1/health (RateLimit: r left, t seconds until one more is), and with none left it waits for one. Against production it takes longer and checks the same things. CI starts each instance it smokes with WAI_QSVC_CLIENT_BURST=60, so it never waits there. The agent docs (llms-full.txt, service-mcp.json) are blessed again from both branches' tools. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/wai-quantum-service.yml | 6 +- wai/wai-quantum-cli/docs/llms-full.txt | 216 +- wai/wai-quantum-cli/docs/service-mcp.json | 4728 ++++++++++++++++++++- wai/wai-quantum-service/smoke.sh | 42 +- 4 files changed, 4960 insertions(+), 32 deletions(-) diff --git a/.github/workflows/wai-quantum-service.yml b/.github/workflows/wai-quantum-service.yml index d2bd69cf5..0ad4982ed 100644 --- a/.github/workflows/wai-quantum-service.yml +++ b/.github/workflows/wai-quantum-service.yml @@ -108,7 +108,7 @@ jobs: key="$RUNNER_TEMP/service.key"; rm -f "$key" "$RUNNER_TEMP/service.keys.json" store="$RUNNER_TEMP/store"; rm -rf "$store" cargo build --locked --release - WAI_QSVC_KEY="$key" WAI_QSVC_STORE="$store" WAI_QSVC_ADDR=127.0.0.1:18241 WAI_QSVC_BUDGET_GB=8 WAI_QSVC_JOB_GB=4 WAI_QSVC_WORKERS=2 \ + WAI_QSVC_KEY="$key" WAI_QSVC_STORE="$store" WAI_QSVC_ADDR=127.0.0.1:18241 WAI_QSVC_BUDGET_GB=8 WAI_QSVC_JOB_GB=4 WAI_QSVC_WORKERS=2 WAI_QSVC_CLIENT_BURST=60 \ ./target/release/wai-quantum-service serve 2> "$RUNNER_TEMP/service.log" & pid=$! trap 'kill $pid 2>/dev/null || true; cat "$RUNNER_TEMP/service.log"' EXIT @@ -168,7 +168,7 @@ jobs: key="$RUNNER_TEMP/service.key"; rm -f "$key" "$RUNNER_TEMP/service.keys.json" store="$RUNNER_TEMP/store"; rm -rf "$store" cargo build --locked --release - WAI_QSVC_KEY="$key" WAI_QSVC_STORE="$store" WAI_QSVC_ADDR=127.0.0.1:18241 WAI_QSVC_BUDGET_GB=8 WAI_QSVC_JOB_GB=4 WAI_QSVC_WORKERS=2 \ + WAI_QSVC_KEY="$key" WAI_QSVC_STORE="$store" WAI_QSVC_ADDR=127.0.0.1:18241 WAI_QSVC_BUDGET_GB=8 WAI_QSVC_JOB_GB=4 WAI_QSVC_WORKERS=2 WAI_QSVC_CLIENT_BURST=60 \ ./target/release/wai-quantum-service serve 2> "$RUNNER_TEMP/service.log" & pid=$! trap 'kill $pid 2>/dev/null || true; cat "$RUNNER_TEMP/service.log"' EXIT @@ -224,7 +224,7 @@ jobs: export CARGO_TARGET_DIR="$HOME/hw-target/wai-quantum-service" nice -n 10 cargo build --locked --release t=$(mktemp -d) - WAI_QSVC_KEY="$t/service.key" WAI_QSVC_STORE="$t/store" WAI_QSVC_ADDR=127.0.0.1:18244 WAI_QSVC_BUDGET_GB=4 WAI_QSVC_JOB_GB=2 WAI_QSVC_WORKERS=1 \ + WAI_QSVC_KEY="$t/service.key" WAI_QSVC_STORE="$t/store" WAI_QSVC_ADDR=127.0.0.1:18244 WAI_QSVC_BUDGET_GB=4 WAI_QSVC_JOB_GB=2 WAI_QSVC_WORKERS=1 WAI_QSVC_CLIENT_BURST=60 \ "$CARGO_TARGET_DIR/release/wai-quantum-service" serve 2> "$t/service.log" & pid=$! trap 'kill $pid 2>/dev/null || true; cat "$t/service.log"; rm -rf "$t"' EXIT diff --git a/wai/wai-quantum-cli/docs/llms-full.txt b/wai/wai-quantum-cli/docs/llms-full.txt index 34df5e21e..92845372d 100644 --- a/wai/wai-quantum-cli/docs/llms-full.txt +++ b/wai/wai-quantum-cli/docs/llms-full.txt @@ -51,7 +51,7 @@ Parameters: - `inputs` (object): Values for a generator's integer inputs, as {"n": 5}: the program is read as its family's circuit at those values. - `shots` (integer): Shots to sample; 1024 when nothing else is asked. - `seed` (integer): The sampling seed (default 1). -- `method` (string): auto (the default: the strongest method the program admits), or one of list_methods' names. +- `method` (string): auto (the default: the strongest method the program admits), or one of list_methods' names; pulse runs a pulse program (OpenPulse calibrations on physical qubits, see pulse_schedule) under the pulse options, its error bounded. - `observables` (array): Pauli observables, as "Z0 Z1" or "0.5 X2". - `amplitudes` (array): Basis states whose amplitudes to return, as bit strings with the highest qubit first. - `noise` (object): Channel probabilities @@ -61,6 +61,7 @@ Parameters: - `intent` (any): What the program must do, checked by the engine with the run: the result's "verdict" states how far it checked (proof, evidence, executed, parsed, refused) and whether the intent holds, sealed by the receipt. - `repair` (any): Repair the program first, on the lines its diagnostics flag alone (as repair_qasm does): true, or the choices {"max_iterations": N, "dyadic_bits": K, "sizes": {"q": 3}}. The result's "repaired" holds the program that ran and each repair. - `mitigate` (any): Mitigate the job's expectation values (or, for readout, its counts) under its own noise: zne (the whole program folded U(U†U)^k and extrapolated), pea (the noise learned as a sparse Pauli–Lindblad model and amplified), pec (the learned channels inverted and sampled), cdr (a line fitted over near-Clifford training circuits), vd (two copies), readout (the counts' flips inverted). Needs noise and observables (shots for readout), at most 8 qubits. The result's `mitigation` gives raw and mitigated values with standard errors, the ideal value (known because this is a simulation), the method's bias and variance assumptions and its cost. A method's name, or an object. +- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. ### `plan_quantum_circuit`: Plan a quantum program @@ -72,7 +73,7 @@ Parameters: - `inputs` (object): Values for a generator's integer inputs, as {"n": 5}: the program is read as its family's circuit at those values. - `shots` (integer): Shots to sample; 1024 when nothing else is asked. - `seed` (integer): The sampling seed (default 1). -- `method` (string): auto (the default: the strongest method the program admits), or one of list_methods' names. +- `method` (string): auto (the default: the strongest method the program admits), or one of list_methods' names; pulse runs a pulse program (OpenPulse calibrations on physical qubits, see pulse_schedule) under the pulse options, its error bounded. - `observables` (array): Pauli observables, as "Z0 Z1" or "0.5 X2". - `amplitudes` (array): Basis states whose amplitudes to return, as bit strings with the highest qubit first. - `noise` (object): Channel probabilities @@ -82,6 +83,7 @@ Parameters: - `intent` (any): What the program must do, checked by the engine with the run: the result's "verdict" states how far it checked (proof, evidence, executed, parsed, refused) and whether the intent holds, sealed by the receipt. - `repair` (any): Repair the program first, on the lines its diagnostics flag alone (as repair_qasm does): true, or the choices {"max_iterations": N, "dyadic_bits": K, "sizes": {"q": 3}}. The result's "repaired" holds the program that ran and each repair. - `mitigate` (any): Mitigate the job's expectation values (or, for readout, its counts) under its own noise: zne (the whole program folded U(U†U)^k and extrapolated), pea (the noise learned as a sparse Pauli–Lindblad model and amplified), pec (the learned channels inverted and sampled), cdr (a line fitted over near-Clifford training circuits), vd (two copies), readout (the counts' flips inverted). Needs noise and observables (shots for readout), at most 8 qubits. The result's `mitigation` gives raw and mitigated values with standard errors, the ideal value (known because this is a simulation), the method's bias and variance assumptions and its cost. A method's name, or an object. +- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. ### `lint_qasm`: Check an OpenQASM program @@ -413,7 +415,7 @@ Parameters: - `inputs` (object): Values for a generator's integer inputs, as {"n": 5}: the program is read as its family's circuit at those values. - `shots` (integer): Shots to sample; 1024 when nothing else is asked. - `seed` (integer): The sampling seed (default 1). -- `method` (string): auto (the default: the strongest method the program admits), or one of list_methods' names. +- `method` (string): auto (the default: the strongest method the program admits), or one of list_methods' names; pulse runs a pulse program (OpenPulse calibrations on physical qubits, see pulse_schedule) under the pulse options, its error bounded. - `observables` (array): Pauli observables, as "Z0 Z1" or "0.5 X2". - `amplitudes` (array): Basis states whose amplitudes to return, as bit strings with the highest qubit first. - `noise` (object): Channel probabilities @@ -423,6 +425,7 @@ Parameters: - `intent` (any): What the program must do, checked by the engine with the run: the result's "verdict" states how far it checked (proof, evidence, executed, parsed, refused) and whether the intent holds, sealed by the receipt. - `repair` (any): Repair the program first, on the lines its diagnostics flag alone (as repair_qasm does): true, or the choices {"max_iterations": N, "dyadic_bits": K, "sizes": {"q": 3}}. The result's "repaired" holds the program that ran and each repair. - `mitigate` (any): Mitigate the job's expectation values (or, for readout, its counts) under its own noise: zne (the whole program folded U(U†U)^k and extrapolated), pea (the noise learned as a sparse Pauli–Lindblad model and amplified), pec (the learned channels inverted and sampled), cdr (a line fitted over near-Clifford training circuits), vd (two copies), readout (the counts' flips inverted). Needs noise and observables (shots for readout), at most 8 qubits. The result's `mitigation` gives raw and mitigated values with standard errors, the ideal value (known because this is a simulation), the method's bias and variance assumptions and its cost. A method's name, or an object. +- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. ### `qec_code`: Show an error-correcting code and certify its distance @@ -465,6 +468,22 @@ Parameters: - `rounds` (integer): surface: rounds (default the distance). - `seed` (integer) +### `pulse_schedule`: Read a pulse program into its schedule + +Read a pulse program (OpenPulse calibrations: ports, frames, waveforms, play, delay, shift_phase, set_phase, shift_frequency, set_frequency, captures and barriers) into its schedule on frames: every instruction at its sample, each waveform's samples (gaussian, drag, gaussian_square, constant, sech, sine, or explicit), each gate call with the samples its calibration spans, and the digests a pulse run's receipt binds (the program, its calibrations, the schedule). Durations are whole samples of dt (pulse.dt_ns, default 0.5 ns) or refused. A construct the reader does not take is refused with a Q code and its line. Sealed with this user's key. To run it, give the program to run_quantum_circuit with method "pulse". + +Parameters: +- `qasm` (string, required): A pulse program: OpenQASM 3 with `defcalgrammar "openpulse";`, `cal` blocks declaring ports (d0 drives qubit 0, m0 is its measurement port, a0 its acquisition port), frames (newframe(port, frequency Hz, phase)) and waveforms, a `defcal` for every gate it calls on physical qubits ($0), and the calls. +- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. + +### `pulse_check`: Check a pulse run by running it again + +Check a pulse job as a host returned it ({"request", "result"}, the result with its receipt): the receipt's signature, the program's digest against the request's text, and the request run again here with the result held to the one given field by field (the density matrix's digest among them), each difference named. A pulse run is the same bits on every platform, so a result that does not reproduce was not made from that request by this engine. Sealed with this user's key. Quote `verified` and the statement. + +Parameters: +- `request` (object, required): The pulse job's request. +- `result` (object, required): Its result, with its receipt. + ### `providers_list`: List the person's quantum providers The person's own quantum providers, as their registry lists them: each one's name, kind, protocol, endpoint or command, and where its credential is kept (never the credential itself). With free: true, also what anyone can use at no cost: the engine's methods on this machine with their limits, the free job service, and each provider whose own description states free use (this asks each provider to describe itself). @@ -509,7 +528,7 @@ Parameters: - `observables` (array): Pauli strings to take expectation values of, e.g. ["Z0", "Z0 Z1", "X2 Y3"]. Qubits count from 0. - `amplitudes` (array): Basis states to give amplitudes of: one character per qubit, qubit n-1 first, e.g. "0011". - `noise` (object): Optional noise, each a probability: depolarizing_1q, depolarizing_2q, amplitude_damping, dephasing (after each gate on its qubits), readout (each measured bit). -- `method` (string, one of auto, exact, statevector, statevector_single, stabilizer, pauli, mps, tensor_network, cutrank, cutrank_single, density_matrix, trajectories): Leave as auto unless you need a particular method. +- `method` (string, one of auto, exact, statevector, statevector_single, stabilizer, pauli, mps, tensor_network, cutrank, cutrank_single, density_matrix, trajectories, pulse): Leave as auto unless you need a particular method; pulse runs a pulse program (OpenPulse calibrations on physical qubits, see pulse_schedule) under the pulse options, its error bounded. - `seed` (integer): Sampling seed (default 1); the same job and seed give the same counts. - `options` (object): Advanced, per method: max_bond (mps), threshold and max_terms (pauli), fusion (statevector), trajectories. - `compile` (object): Compile the program first (as compile_qasm does) and run the compiled circuit: with basis clifford_t it runs bit-exact, and the result's `compiled` states its distance from the program. @@ -517,6 +536,7 @@ Parameters: - `intent` (any): What the program must do, checked by the engine with the run: the result's `verdict` states how far it checked (proof, evidence, executed, parsed, refused) and whether the intent holds, sealed by the receipt. - `repair` (any): Repair the program first, on the lines its diagnostics flag alone (as repair_qasm does): true, or the choices {"max_iterations": N, "dyadic_bits": K, "sizes": {"q": 3}}. The result's `repaired` holds the program that ran and each repair. - `mitigate` (any): Mitigate the job's expectation values (or, for readout, its counts) under its own noise: zne (the whole program folded U(U†U)^k and extrapolated), pea (the noise learned as a sparse Pauli–Lindblad model and amplified), pec (the learned channels inverted and sampled), cdr (a line fitted over near-Clifford training circuits), vd (two copies), readout (the counts' flips inverted). Needs noise and observables (shots for readout), at most 8 qubits. The result's `mitigation` gives raw and mitigated values with standard errors, the ideal value (known because this is a simulation), the method's bias and variance assumptions and its cost. A method's name, or an object. +- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. - `source` (object): The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source. ### `run_quantum_set`: Run a set of quantum programs, or one over a grid @@ -529,7 +549,7 @@ Parameters: - `observables` (array): Pauli strings to take expectation values of, e.g. ["Z0", "Z0 Z1", "X2 Y3"]. Qubits count from 0. - `amplitudes` (array): Basis states to give amplitudes of: one character per qubit, qubit n-1 first, e.g. "0011". - `noise` (object): Optional noise, each a probability: depolarizing_1q, depolarizing_2q, amplitude_damping, dephasing (after each gate on its qubits), readout (each measured bit). -- `method` (string, one of auto, exact, statevector, statevector_single, stabilizer, pauli, mps, tensor_network, cutrank, cutrank_single, density_matrix, trajectories): Leave as auto unless you need a particular method. +- `method` (string, one of auto, exact, statevector, statevector_single, stabilizer, pauli, mps, tensor_network, cutrank, cutrank_single, density_matrix, trajectories, pulse): Leave as auto unless you need a particular method; pulse runs a pulse program (OpenPulse calibrations on physical qubits, see pulse_schedule) under the pulse options, its error bounded. - `seed` (integer): Sampling seed (default 1); the same job and seed give the same counts. - `options` (object): Advanced, per method: max_bond (mps), threshold and max_terms (pauli), fusion (statevector), trajectories. - `compile` (object): Compile the program first (as compile_qasm does) and run the compiled circuit: with basis clifford_t it runs bit-exact, and the result's `compiled` states its distance from the program. @@ -537,6 +557,7 @@ Parameters: - `intent` (any): What the program must do, checked by the engine with the run: the result's `verdict` states how far it checked (proof, evidence, executed, parsed, refused) and whether the intent holds, sealed by the receipt. - `repair` (any): Repair the program first, on the lines its diagnostics flag alone (as repair_qasm does): true, or the choices {"max_iterations": N, "dyadic_bits": K, "sizes": {"q": 3}}. The result's `repaired` holds the program that ran and each repair. - `mitigate` (any): Mitigate the job's expectation values (or, for readout, its counts) under its own noise: zne (the whole program folded U(U†U)^k and extrapolated), pea (the noise learned as a sparse Pauli–Lindblad model and amplified), pec (the learned channels inverted and sampled), cdr (a line fitted over near-Clifford training circuits), vd (two copies), readout (the counts' flips inverted). Needs noise and observables (shots for readout), at most 8 qubits. The result's `mitigation` gives raw and mitigated values with standard errors, the ideal value (known because this is a simulation), the method's bias and variance assumptions and its cost. A method's name, or an object. +- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. - `programs` (array): The set's programs: each a program's text, or a job ({"qasm", …}) whose own fields win over the set's. - `sweep` (object): Each input of qasm and its values: every combination runs, the names in sorted order, the first varying slowest; an integer input's values are whole numbers. @@ -550,7 +571,7 @@ Parameters: - `observables` (array): Pauli strings to take expectation values of, e.g. ["Z0", "Z0 Z1", "X2 Y3"]. Qubits count from 0. - `amplitudes` (array): Basis states to give amplitudes of: one character per qubit, qubit n-1 first, e.g. "0011". - `noise` (object): Optional noise, each a probability: depolarizing_1q, depolarizing_2q, amplitude_damping, dephasing (after each gate on its qubits), readout (each measured bit). -- `method` (string, one of auto, exact, statevector, statevector_single, stabilizer, pauli, mps, tensor_network, cutrank, cutrank_single, density_matrix, trajectories): Leave as auto unless you need a particular method. +- `method` (string, one of auto, exact, statevector, statevector_single, stabilizer, pauli, mps, tensor_network, cutrank, cutrank_single, density_matrix, trajectories, pulse): Leave as auto unless you need a particular method; pulse runs a pulse program (OpenPulse calibrations on physical qubits, see pulse_schedule) under the pulse options, its error bounded. - `seed` (integer): Sampling seed (default 1); the same job and seed give the same counts. - `options` (object): Advanced, per method: max_bond (mps), threshold and max_terms (pauli), fusion (statevector), trajectories. - `compile` (object): Compile the program first (as compile_qasm does) and run the compiled circuit: with basis clifford_t it runs bit-exact, and the result's `compiled` states its distance from the program. @@ -558,6 +579,7 @@ Parameters: - `intent` (any): What the program must do, checked by the engine with the run: the result's `verdict` states how far it checked (proof, evidence, executed, parsed, refused) and whether the intent holds, sealed by the receipt. - `repair` (any): Repair the program first, on the lines its diagnostics flag alone (as repair_qasm does): true, or the choices {"max_iterations": N, "dyadic_bits": K, "sizes": {"q": 3}}. The result's `repaired` holds the program that ran and each repair. - `mitigate` (any): Mitigate the job's expectation values (or, for readout, its counts) under its own noise: zne (the whole program folded U(U†U)^k and extrapolated), pea (the noise learned as a sparse Pauli–Lindblad model and amplified), pec (the learned channels inverted and sampled), cdr (a line fitted over near-Clifford training circuits), vd (two copies), readout (the counts' flips inverted). Needs noise and observables (shots for readout), at most 8 qubits. The result's `mitigation` gives raw and mitigated values with standard errors, the ideal value (known because this is a simulation), the method's bias and variance assumptions and its cost. A method's name, or an object. +- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. - `programs` (array): The set's programs: each a program's text, or a job ({"qasm", …}) whose own fields win over the set's. - `sweep` (object): Each input of qasm and its values: every combination runs, the names in sorted order, the first varying slowest; an integer input's values are whole numbers. - `source` (object): The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source. @@ -697,6 +719,7 @@ Parameters: - `intent` (any): What the program must do, checked by the engine with the run: the result's `verdict` states how far it checked (proof, evidence, executed, parsed, refused) and whether the intent holds, sealed by the receipt. - `repair` (any): Repair the program first, on the lines its diagnostics flag alone (as repair_qasm does): true, or the choices {"max_iterations": N, "dyadic_bits": K, "sizes": {"q": 3}}. The result's `repaired` holds the program that ran and each repair. - `mitigate` (any): Mitigate the job's expectation values (or, for readout, its counts) under its own noise: zne (the whole program folded U(U†U)^k and extrapolated), pea (the noise learned as a sparse Pauli–Lindblad model and amplified), pec (the learned channels inverted and sampled), cdr (a line fitted over near-Clifford training circuits), vd (two copies), readout (the counts' flips inverted). Needs noise and observables (shots for readout), at most 8 qubits. The result's `mitigation` gives raw and mitigated values with standard errors, the ideal value (known because this is a simulation), the method's bias and variance assumptions and its cost. A method's name, or an object. +- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. ### `quantum_circuit_examples`: Ready-to-run quantum examples @@ -743,6 +766,22 @@ Parameters: - `rounds` (integer): surface: rounds (default the distance). - `seed` (integer) +### `pulse_schedule`: Read a pulse program into its schedule + +Read a pulse program (OpenPulse calibrations: ports, frames, waveforms, play, delay, shift_phase, set_phase, shift_frequency, set_frequency, captures and barriers) into its schedule on frames: every instruction at its sample, each waveform's samples (gaussian, drag, gaussian_square, constant, sech, sine, or explicit), each gate call with the samples its calibration spans, and the digests a pulse run's receipt binds (the program, its calibrations, the schedule). Durations are whole samples of dt (pulse.dt_ns, default 0.5 ns) or refused. A construct the reader does not take is refused with a Q code and its line. Sealed with the service's key. To run it, give the program to run_quantum_circuit with method "pulse". + +Parameters: +- `qasm` (string, required): A pulse program: OpenQASM 3 with `defcalgrammar "openpulse";`, `cal` blocks declaring ports (d0 drives qubit 0, m0 is its measurement port, a0 its acquisition port), frames (newframe(port, frequency Hz, phase)) and waveforms, a `defcal` for every gate it calls on physical qubits ($0), and the calls. +- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. + +### `pulse_check`: Check a pulse run by running it again + +Check a pulse job as a host returned it ({"request", "result"}, the result with its receipt): the receipt's signature, the program's digest against the request's text, and the request run again here with the result held to the one given field by field (the density matrix's digest among them), each difference named. A pulse run is the same bits on every platform, so a result that does not reproduce was not made from that request by this engine. Sealed with the service's key. Quote `verified` and the statement. + +Parameters: +- `request` (object, required): The pulse job's request. +- `result` (object, required): Its result, with its receipt. + ## Resources, on both servers ### `wai-quantum://docs/language`: the OpenQASM the engine reads @@ -894,9 +933,15 @@ Each construct below is supported (read as the language defines it), lowered (re | Construct | Status | Codes | |---|---|---| -| `defcalgrammar "openpulse";` | preserved | Q452 | -| `cal { … }` | preserved, never run | Q451 | -| `defcal …` | preserved, never run; the gate's matrix comes from its definition or the library | Q450, Q126 | +| `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 @@ -1038,6 +1083,16 @@ Every code: | `Q450` | preserved | `defcal`: kept verbatim for export, never executed; the gate's matrix comes from its `gate` definition or the standard library. | | `Q451` | preserved | `cal { … }`: kept verbatim for export, never executed. | | `Q452` | preserved | `defcalgrammar`: kept verbatim for export. | +| `Q453` | lowered | In a pulse run, a `cal` or `defcal` body read as OpenPulse and lowered to the schedule on frames (plays, delays, phase and frequency changes, captures and barriers) that the pulse simulator integrates. | +| `Q454` | refused | In a pulse run, a `defcalgrammar` other than `"openpulse"`: the pulse reader reads OpenPulse alone. | +| `Q455` | refused | A statement in a calibration that the pulse reader does not take: classical control, a frame's attributes or `get_phase`, an `extern` function, a frame made inside a `defcal`, a nested calibration, or a `defcal` over qubit arguments (the message names it and what to write instead). | +| `Q456` | refused | A waveform that does not read: a template the reader does not know, arguments of the wrong number or kind, a length or a width that is not positive, a sample that is not a number, or more samples than it holds. | +| `Q457` | refused | A frame or a port that does not read: a play, a delay or a phase or frequency change on a name that is not a frame, `newframe` on a name that is not a port, a frequency that is not a number, or a port that reaches no qubit. | +| `Q458` | refused | A duration in a calibration that is not a whole, non-negative number of samples at the sample period (`dt`), or a schedule longer than the reader holds. | +| `Q459` | refused | In a pulse run, a gate or a measurement on physical qubits that no `defcal` calibrates for those qubits and those parameters. | +| `Q460` | refused | In a pulse run, a statement outside the calibrations that the pulse reader does not take: declared qubits (a pulse program names physical qubits, `$0`), a modifier, `reset`, classical control, a subroutine, or a measurement into something other than a bit. | +| `Q461` | refused | A capture that does not read: not on a frame of an acquisition port, without a duration or a waveform, or its result put somewhere other than a bit. | +| `Q462` | warned | A play on a measurement port: scheduled and timed, its effect on the readout not simulated (a capture reads the qubit's state). | | `Q500` | refused | An angle that is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32): the bit-exact method refuses it, never rounds it; the methods in double precision take it. | | `Q501` | refused | More than 255 qubits for the bit-exact circuit. | | `Q502` | refused | A program that measures a qubit and then acts on it, resets one after its first gate, or acts on measured bits: the bit-exact circuit takes programs whose measurements all come at the end. | @@ -1078,6 +1133,7 @@ Every code: | `cutrank_single` | single | as for cutrank; the table on a GPU, its arithmetic in single precision | counts, amplitudes | | `density_matrix` | exact | any, with noise | counts, expectations | | `trajectories` | estimate | any, with noise | counts, expectations | +| `pulse` | bounded | pulse programs: OpenPulse calibrations (cal, defcal) on physical qubits, every gate called with its calibration | counts, populations, captures | The accuracy classes: @@ -1404,6 +1460,9 @@ Commands: qec Error correction: a code and its certified distance, a syndrome or a circuit's detectors decoded, a threshold measured, each answer signed (`wai-quantum qec --help`) + pulse Pulse programs: a program's schedule on frames drawn as a timeline, and a pulse job + checked by running it again; a pulse program runs with `run --method pulse` + (`wai-quantum pulse --help`) eval Score a model on a task set with the engine: whether its program reads, the verdict's level and whether the intent holds, and the joules per solved task where its server's energy is metered; or write each task's answer three ways for a token count @@ -1545,6 +1604,10 @@ Options: pea, pec, cdr, vd or readout, or its options as JSON ({"method": "pea", "factors": [1, 1.5, 2]}) + --pulse + A pulse run's options (with --method pulse), as JSON or a file of it: dt_ns, levels, + tolerance, substeps, qubits, couplings and ports (`wai-quantum pulse --help`) + --json Print the job as the service returns it: request, plan, result and receipt @@ -1675,6 +1738,10 @@ Options: pea, pec, cdr, vd or readout, or its options as JSON ({"method": "pea", "factors": [1, 1.5, 2]}) + --pulse + A pulse run's options (with --method pulse), as JSON or a file of it: dt_ns, levels, + tolerance, substeps, qubits, couplings and ports (`wai-quantum pulse --help`) + --json Print the plan as JSON @@ -2403,6 +2470,10 @@ Options: pea, pec, cdr, vd or readout, or its options as JSON ({"method": "pea", "factors": [1, 1.5, 2]}) + --pulse + A pulse run's options (with --method pulse), as JSON or a file of it: dt_ns, levels, + tolerance, substeps, qubits, couplings and ports (`wai-quantum pulse --help`) + --json Print the job as the service returns it: request, plan, result and receipt @@ -5182,6 +5253,133 @@ Options: Print version ``` +### `wai-quantum pulse` + +```text +Pulse programs: a program's schedule on frames drawn as a timeline, and a pulse job checked by +running it again; a pulse program runs with `run --method pulse` (`wai-quantum pulse --help`) + +Usage: wai-quantum pulse [OPTIONS] + +Commands: + schedule A pulse program's schedule: each frame's timeline, every instruction at its sample, the + waveforms and the gate calls + check A pulse job (`run --method pulse --json`) checked: its receipt, its program's digest, + and the request run again here, its result held to the one given + help Print this message or the help of the given subcommand(s) + +Options: + --input + A value for a generator's integer `input`, as n=5 (repeat for each input): the program is + read as its family's circuit at those values + + --lang + The language of the words the command line writes itself, en or zh-CN (by default the one + LANG names); results, codes and the engine's own messages stay in English + + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + + -h, --help + Print help + + -V, --version + Print version +``` + +### `wai-quantum pulse schedule` + +```text +A pulse program's schedule: each frame's timeline, every instruction at its sample, the waveforms +and the gate calls + +Usage: wai-quantum pulse schedule [OPTIONS] [FILE] + +Arguments: + [FILE] + The program; `-` or nothing reads standard input + +Options: + --input + A value for a generator's integer `input`, as n=5 (repeat for each input): the program is + read as its family's circuit at those values + + --pulse + The pulse options, as JSON or a file of it (only dt_ns and ports matter here) + + --lang + The language of the words the command line writes itself, en or zh-CN (by default the one + LANG names); results, codes and the engine's own messages stay in English + + --width + The timeline's width, in columns + + [default: 60] + + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + + --json + Print the answer sealed with this user's key, as JSON (`wai-quantum verify` checks it) + + --key + Sign with the key in this file (64 hex digits) + + --demo-key + Sign with the published demo key: the receipt then shows a result is unaltered, not who + signed it + + -h, --help + Print help + + -V, --version + Print version +``` + +### `wai-quantum pulse check` + +```text +A pulse job (`run --method pulse --json`) checked: its receipt, its program's digest, and the +request run again here, its result held to the one given + +Usage: wai-quantum pulse check [OPTIONS] [FILE] + +Arguments: + [FILE] + The job, as `run --json` or the service returns it; `-` reads standard input + +Options: + --input + A value for a generator's integer `input`, as n=5 (repeat for each input): the program is + read as its family's circuit at those values + + --json + Print the answer sealed with this user's key, as JSON (`wai-quantum verify` checks it) + + --key + Sign with the key in this file (64 hex digits) + + --lang + The language of the words the command line writes itself, en or zh-CN (by default the one + LANG names); results, codes and the engine's own messages stay in English + + --demo-key + Sign with the published demo key: the receipt then shows a result is unaltered, not who + signed it + + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + + -h, --help + Print help + + -V, --version + Print version +``` + ### `wai-quantum eval` ```text diff --git a/wai/wai-quantum-cli/docs/service-mcp.json b/wai/wai-quantum-cli/docs/service-mcp.json index 12649d598..b6629f0d7 100644 --- a/wai/wai-quantum-cli/docs/service-mcp.json +++ b/wai/wai-quantum-cli/docs/service-mcp.json @@ -79,9 +79,10 @@ "cutrank", "cutrank_single", "density_matrix", - "trajectories" + "trajectories", + "pulse" ], - "description": "Leave as auto unless you need a particular method." + "description": "Leave as auto unless you need a particular method; pulse runs a pulse program (OpenPulse calibrations on physical qubits, see pulse_schedule) under the pulse options, its error bounded." }, "seed": { "type": "integer", @@ -362,6 +363,121 @@ } ] }, + "pulse": { + "type": "object", + "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", + "additionalProperties": false, + "properties": { + "dt_ns": { + "type": "number", + "exclusiveMinimum": 0, + "maximum": 1000, + "description": "The sample period, ns (default 0.5)." + }, + "levels": { + "type": "integer", + "enum": [ + 2, + 3 + ], + "description": "Levels kept per qubit (default 2; 3 shows leakage)." + }, + "tolerance": { + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 1, + "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." + }, + "substeps": { + "type": "integer", + "minimum": 1, + "maximum": 4096, + "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." + }, + "qubits": { + "type": "object", + "description": "Each qubit's parameters by its index, over the target's and the defaults.", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "frequency_ghz": { + "type": "number", + "description": "Its frequency, GHz." + }, + "rabi_mhz": { + "type": "number", + "description": "Its Rabi frequency at full amplitude, MHz." + }, + "anharmonicity_mhz": { + "type": "number", + "description": "Its anharmonicity, MHz." + }, + "t1_us": { + "type": "number", + "description": "Its relaxation time, µs." + }, + "t2_us": { + "type": "number", + "description": "Its coherence time, µs (at most 2·T1)." + } + } + } + }, + "couplings": { + "type": "array", + "description": "Static ZZ couplings.", + "items": { + "type": "object", + "required": [ + "qubits", + "zz_khz" + ], + "additionalProperties": false, + "properties": { + "qubits": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + }, + "minItems": 2, + "maxItems": 2 + }, + "zz_khz": { + "type": "number", + "description": "The coupling's rate, kHz." + } + } + } + }, + "ports": { + "type": "object", + "description": "Ports by name: what each is for and the qubit it reaches.", + "additionalProperties": { + "type": "object", + "required": [ + "kind", + "qubit" + ], + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "qubit": { + "type": "integer", + "minimum": 0 + } + } + } + } + } + }, "source": { "type": "object", "required": [ @@ -516,7 +632,7 @@ }, "method": { "type": "string", - "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it." + "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it; pulse runs a pulse program (OpenPulse calibrations) under the pulse options." }, "observables": { "type": "array", @@ -1070,6 +1186,121 @@ ] } ] + }, + "pulse": { + "type": "object", + "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", + "additionalProperties": false, + "properties": { + "dt_ns": { + "type": "number", + "exclusiveMinimum": 0, + "maximum": 1000, + "description": "The sample period, ns (default 0.5)." + }, + "levels": { + "type": "integer", + "enum": [ + 2, + 3 + ], + "description": "Levels kept per qubit (default 2; 3 shows leakage)." + }, + "tolerance": { + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 1, + "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." + }, + "substeps": { + "type": "integer", + "minimum": 1, + "maximum": 4096, + "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." + }, + "qubits": { + "type": "object", + "description": "Each qubit's parameters by its index, over the target's and the defaults.", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "frequency_ghz": { + "type": "number", + "description": "Its frequency, GHz." + }, + "rabi_mhz": { + "type": "number", + "description": "Its Rabi frequency at full amplitude, MHz." + }, + "anharmonicity_mhz": { + "type": "number", + "description": "Its anharmonicity, MHz." + }, + "t1_us": { + "type": "number", + "description": "Its relaxation time, µs." + }, + "t2_us": { + "type": "number", + "description": "Its coherence time, µs (at most 2·T1)." + } + } + } + }, + "couplings": { + "type": "array", + "description": "Static ZZ couplings.", + "items": { + "type": "object", + "required": [ + "qubits", + "zz_khz" + ], + "additionalProperties": false, + "properties": { + "qubits": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + }, + "minItems": 2, + "maxItems": 2 + }, + "zz_khz": { + "type": "number", + "description": "The coupling's rate, kHz." + } + } + } + }, + "ports": { + "type": "object", + "description": "Ports by name: what each is for and the qubit it reaches.", + "additionalProperties": { + "type": "object", + "required": [ + "kind", + "qubit" + ], + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "qubit": { + "type": "integer", + "minimum": 0 + } + } + } + } + } } } }, @@ -1191,6 +1422,24 @@ }, "batch": { "type": "integer" + }, + "bound": { + "type": "number", + "description": "bounded (a pulse run): every probability within this of the exact solution of the model's master equation, the truncation bound and the rounding estimate together." + }, + "truncation_bound": { + "type": "number", + "description": "A pulse run: the proved bound, in the trace norm, on what the integrator's series leaves out." + }, + "rounding_estimate": { + "type": "number", + "description": "A pulse run: an estimate of floating-point rounding beside the bound, not a proof." + }, + "tolerance": { + "type": "number" + }, + "norm": { + "type": "string" } } }, @@ -2704,6 +2953,237 @@ } } } + }, + "pulse": { + "type": "object", + "description": "A pulse run (method pulse, engine::pulse): what was read and its distribution, the final populations, each capture's reading, the final density matrix's digest, the schedule's length, the integrator and its step, the device and where each parameter came from, and what the receipt binds (calibration).", + "required": [ + "read", + "outcome_bits", + "populations", + "simulated_qubits", + "density_hash", + "integrator", + "device", + "calibration" + ], + "properties": { + "read": { + "type": "string", + "description": "What the distribution reads: the captures together (into the program's bits when each writes one), or the final state." + }, + "outcome_bits": { + "type": "array", + "items": { + "type": "string" + }, + "description": "What each character of an outcome is, left to right: a program bit (c[0]) or a qubit ($0)." + }, + "distribution": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "number" + }, + "description": "The probability of each outcome, which the counts sample; null when the captures start at different samples." + }, + "populations": { + "type": "object", + "additionalProperties": { + "type": "number" + }, + "description": "The final state read on every simulated qubit, the highest qubit first, a level above 0 read as 1." + }, + "simulated_qubits": { + "type": "array", + "items": { + "type": "integer" + } + }, + "captures": { + "type": "array", + "items": { + "type": "object", + "properties": { + "frame": { + "type": "string" + }, + "qubit": { + "type": "integer" + }, + "start": { + "type": "integer" + }, + "start_ns": { + "type": "number" + }, + "bit": { + "type": [ + "string", + "null" + ] + }, + "p1": { + "type": "number" + }, + "p2": { + "type": "number" + } + } + } + }, + "leakage": { + "type": "array", + "items": { + "type": "object", + "properties": { + "qubit": { + "type": "integer" + }, + "p2": { + "type": "number" + } + } + }, + "description": "With three levels: each qubit's final probability of level 2." + }, + "trace": { + "type": "number" + }, + "density_hash": { + "type": "string", + "description": "The BLAKE3 of the final density matrix's bits: the same on every platform." + }, + "duration": { + "type": "integer" + }, + "duration_ns": { + "type": "number" + }, + "integrator": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "order": { + "type": "integer" + }, + "dt_ns": { + "type": "number" + }, + "substeps": { + "type": "integer" + }, + "step_ns": { + "type": "number" + }, + "steps": { + "type": "integer" + }, + "norm_max": { + "type": "number" + }, + "frame": { + "type": "string" + } + } + }, + "device": { + "type": "object", + "properties": { + "levels": { + "type": "integer" + }, + "dt_ns": { + "type": "number" + }, + "qubits": { + "type": "array", + "items": { + "type": "object", + "properties": { + "qubit": { + "type": "integer" + }, + "frequency_hz": { + "type": "number" + }, + "rabi_hz": { + "type": "number" + }, + "anharmonicity_hz": { + "type": "number" + }, + "t1_ns": { + "type": "number" + }, + "t2_ns": { + "type": "number" + }, + "from": { + "type": "object", + "additionalProperties": { + "enum": [ + "pulse", + "target", + "default", + "none" + ] + } + } + } + } + }, + "couplings": { + "type": "array" + }, + "model": { + "type": "string" + } + } + }, + "calibration": { + "type": "object", + "description": "What the receipt binds: the program's, its calibrations', the schedule's and the device's digests, and the target snapshot.", + "properties": { + "program": { + "type": "string" + }, + "calibrations": { + "type": "string" + }, + "schedule": { + "type": "string" + }, + "device": { + "type": "string" + }, + "target": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "calibration": { + "type": "object" + } + } + } + } + }, + "not_modelled": { + "type": "array", + "items": { + "type": "string" + } + } + } } } }, @@ -3007,7 +3487,7 @@ }, "method": { "type": "string", - "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it." + "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it; pulse runs a pulse program (OpenPulse calibrations) under the pulse options." }, "observables": { "type": "array", @@ -3561,6 +4041,121 @@ ] } ] + }, + "pulse": { + "type": "object", + "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", + "additionalProperties": false, + "properties": { + "dt_ns": { + "type": "number", + "exclusiveMinimum": 0, + "maximum": 1000, + "description": "The sample period, ns (default 0.5)." + }, + "levels": { + "type": "integer", + "enum": [ + 2, + 3 + ], + "description": "Levels kept per qubit (default 2; 3 shows leakage)." + }, + "tolerance": { + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 1, + "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." + }, + "substeps": { + "type": "integer", + "minimum": 1, + "maximum": 4096, + "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." + }, + "qubits": { + "type": "object", + "description": "Each qubit's parameters by its index, over the target's and the defaults.", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "frequency_ghz": { + "type": "number", + "description": "Its frequency, GHz." + }, + "rabi_mhz": { + "type": "number", + "description": "Its Rabi frequency at full amplitude, MHz." + }, + "anharmonicity_mhz": { + "type": "number", + "description": "Its anharmonicity, MHz." + }, + "t1_us": { + "type": "number", + "description": "Its relaxation time, µs." + }, + "t2_us": { + "type": "number", + "description": "Its coherence time, µs (at most 2·T1)." + } + } + } + }, + "couplings": { + "type": "array", + "description": "Static ZZ couplings.", + "items": { + "type": "object", + "required": [ + "qubits", + "zz_khz" + ], + "additionalProperties": false, + "properties": { + "qubits": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + }, + "minItems": 2, + "maxItems": 2 + }, + "zz_khz": { + "type": "number", + "description": "The coupling's rate, kHz." + } + } + } + }, + "ports": { + "type": "object", + "description": "Ports by name: what each is for and the qubit it reaches.", + "additionalProperties": { + "type": "object", + "required": [ + "kind", + "qubit" + ], + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "qubit": { + "type": "integer", + "minimum": 0 + } + } + } + } + } } } }, @@ -3680,6 +4275,24 @@ }, "batch": { "type": "integer" + }, + "bound": { + "type": "number", + "description": "bounded (a pulse run): every probability within this of the exact solution of the model's master equation, the truncation bound and the rounding estimate together." + }, + "truncation_bound": { + "type": "number", + "description": "A pulse run: the proved bound, in the trace norm, on what the integrator's series leaves out." + }, + "rounding_estimate": { + "type": "number", + "description": "A pulse run: an estimate of floating-point rounding beside the bound, not a proof." + }, + "tolerance": { + "type": "number" + }, + "norm": { + "type": "string" } } }, @@ -5193,6 +5806,237 @@ } } } + }, + "pulse": { + "type": "object", + "description": "A pulse run (method pulse, engine::pulse): what was read and its distribution, the final populations, each capture's reading, the final density matrix's digest, the schedule's length, the integrator and its step, the device and where each parameter came from, and what the receipt binds (calibration).", + "required": [ + "read", + "outcome_bits", + "populations", + "simulated_qubits", + "density_hash", + "integrator", + "device", + "calibration" + ], + "properties": { + "read": { + "type": "string", + "description": "What the distribution reads: the captures together (into the program's bits when each writes one), or the final state." + }, + "outcome_bits": { + "type": "array", + "items": { + "type": "string" + }, + "description": "What each character of an outcome is, left to right: a program bit (c[0]) or a qubit ($0)." + }, + "distribution": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "number" + }, + "description": "The probability of each outcome, which the counts sample; null when the captures start at different samples." + }, + "populations": { + "type": "object", + "additionalProperties": { + "type": "number" + }, + "description": "The final state read on every simulated qubit, the highest qubit first, a level above 0 read as 1." + }, + "simulated_qubits": { + "type": "array", + "items": { + "type": "integer" + } + }, + "captures": { + "type": "array", + "items": { + "type": "object", + "properties": { + "frame": { + "type": "string" + }, + "qubit": { + "type": "integer" + }, + "start": { + "type": "integer" + }, + "start_ns": { + "type": "number" + }, + "bit": { + "type": [ + "string", + "null" + ] + }, + "p1": { + "type": "number" + }, + "p2": { + "type": "number" + } + } + } + }, + "leakage": { + "type": "array", + "items": { + "type": "object", + "properties": { + "qubit": { + "type": "integer" + }, + "p2": { + "type": "number" + } + } + }, + "description": "With three levels: each qubit's final probability of level 2." + }, + "trace": { + "type": "number" + }, + "density_hash": { + "type": "string", + "description": "The BLAKE3 of the final density matrix's bits: the same on every platform." + }, + "duration": { + "type": "integer" + }, + "duration_ns": { + "type": "number" + }, + "integrator": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "order": { + "type": "integer" + }, + "dt_ns": { + "type": "number" + }, + "substeps": { + "type": "integer" + }, + "step_ns": { + "type": "number" + }, + "steps": { + "type": "integer" + }, + "norm_max": { + "type": "number" + }, + "frame": { + "type": "string" + } + } + }, + "device": { + "type": "object", + "properties": { + "levels": { + "type": "integer" + }, + "dt_ns": { + "type": "number" + }, + "qubits": { + "type": "array", + "items": { + "type": "object", + "properties": { + "qubit": { + "type": "integer" + }, + "frequency_hz": { + "type": "number" + }, + "rabi_hz": { + "type": "number" + }, + "anharmonicity_hz": { + "type": "number" + }, + "t1_ns": { + "type": "number" + }, + "t2_ns": { + "type": "number" + }, + "from": { + "type": "object", + "additionalProperties": { + "enum": [ + "pulse", + "target", + "default", + "none" + ] + } + } + } + } + }, + "couplings": { + "type": "array" + }, + "model": { + "type": "string" + } + } + }, + "calibration": { + "type": "object", + "description": "What the receipt binds: the program's, its calibrations', the schedule's and the device's digests, and the target snapshot.", + "properties": { + "program": { + "type": "string" + }, + "calibrations": { + "type": "string" + }, + "schedule": { + "type": "string" + }, + "device": { + "type": "string" + }, + "target": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "calibration": { + "type": "object" + } + } + } + } + }, + "not_modelled": { + "type": "array", + "items": { + "type": "string" + } + } + } } } }, @@ -7465,9 +8309,10 @@ "cutrank", "cutrank_single", "density_matrix", - "trajectories" + "trajectories", + "pulse" ], - "description": "Leave as auto unless you need a particular method." + "description": "Leave as auto unless you need a particular method; pulse runs a pulse program (OpenPulse calibrations on physical qubits, see pulse_schedule) under the pulse options, its error bounded." }, "seed": { "type": "integer", @@ -7748,6 +8593,121 @@ } ] }, + "pulse": { + "type": "object", + "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", + "additionalProperties": false, + "properties": { + "dt_ns": { + "type": "number", + "exclusiveMinimum": 0, + "maximum": 1000, + "description": "The sample period, ns (default 0.5)." + }, + "levels": { + "type": "integer", + "enum": [ + 2, + 3 + ], + "description": "Levels kept per qubit (default 2; 3 shows leakage)." + }, + "tolerance": { + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 1, + "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." + }, + "substeps": { + "type": "integer", + "minimum": 1, + "maximum": 4096, + "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." + }, + "qubits": { + "type": "object", + "description": "Each qubit's parameters by its index, over the target's and the defaults.", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "frequency_ghz": { + "type": "number", + "description": "Its frequency, GHz." + }, + "rabi_mhz": { + "type": "number", + "description": "Its Rabi frequency at full amplitude, MHz." + }, + "anharmonicity_mhz": { + "type": "number", + "description": "Its anharmonicity, MHz." + }, + "t1_us": { + "type": "number", + "description": "Its relaxation time, µs." + }, + "t2_us": { + "type": "number", + "description": "Its coherence time, µs (at most 2·T1)." + } + } + } + }, + "couplings": { + "type": "array", + "description": "Static ZZ couplings.", + "items": { + "type": "object", + "required": [ + "qubits", + "zz_khz" + ], + "additionalProperties": false, + "properties": { + "qubits": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + }, + "minItems": 2, + "maxItems": 2 + }, + "zz_khz": { + "type": "number", + "description": "The coupling's rate, kHz." + } + } + } + }, + "ports": { + "type": "object", + "description": "Ports by name: what each is for and the qubit it reaches.", + "additionalProperties": { + "type": "object", + "required": [ + "kind", + "qubit" + ], + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "qubit": { + "type": "integer", + "minimum": 0 + } + } + } + } + } + }, "programs": { "type": "array", "minItems": 1, @@ -7906,7 +8866,7 @@ }, "method": { "type": "string", - "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it." + "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it; pulse runs a pulse program (OpenPulse calibrations) under the pulse options." }, "observables": { "type": "array", @@ -8460,6 +9420,121 @@ ] } ] + }, + "pulse": { + "type": "object", + "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", + "additionalProperties": false, + "properties": { + "dt_ns": { + "type": "number", + "exclusiveMinimum": 0, + "maximum": 1000, + "description": "The sample period, ns (default 0.5)." + }, + "levels": { + "type": "integer", + "enum": [ + 2, + 3 + ], + "description": "Levels kept per qubit (default 2; 3 shows leakage)." + }, + "tolerance": { + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 1, + "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." + }, + "substeps": { + "type": "integer", + "minimum": 1, + "maximum": 4096, + "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." + }, + "qubits": { + "type": "object", + "description": "Each qubit's parameters by its index, over the target's and the defaults.", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "frequency_ghz": { + "type": "number", + "description": "Its frequency, GHz." + }, + "rabi_mhz": { + "type": "number", + "description": "Its Rabi frequency at full amplitude, MHz." + }, + "anharmonicity_mhz": { + "type": "number", + "description": "Its anharmonicity, MHz." + }, + "t1_us": { + "type": "number", + "description": "Its relaxation time, µs." + }, + "t2_us": { + "type": "number", + "description": "Its coherence time, µs (at most 2·T1)." + } + } + } + }, + "couplings": { + "type": "array", + "description": "Static ZZ couplings.", + "items": { + "type": "object", + "required": [ + "qubits", + "zz_khz" + ], + "additionalProperties": false, + "properties": { + "qubits": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + }, + "minItems": 2, + "maxItems": 2 + }, + "zz_khz": { + "type": "number", + "description": "The coupling's rate, kHz." + } + } + } + }, + "ports": { + "type": "object", + "description": "Ports by name: what each is for and the qubit it reaches.", + "additionalProperties": { + "type": "object", + "required": [ + "kind", + "qubit" + ], + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "qubit": { + "type": "integer", + "minimum": 0 + } + } + } + } + } } } }, @@ -8581,6 +9656,24 @@ }, "batch": { "type": "integer" + }, + "bound": { + "type": "number", + "description": "bounded (a pulse run): every probability within this of the exact solution of the model's master equation, the truncation bound and the rounding estimate together." + }, + "truncation_bound": { + "type": "number", + "description": "A pulse run: the proved bound, in the trace norm, on what the integrator's series leaves out." + }, + "rounding_estimate": { + "type": "number", + "description": "A pulse run: an estimate of floating-point rounding beside the bound, not a proof." + }, + "tolerance": { + "type": "number" + }, + "norm": { + "type": "string" } } }, @@ -10094,6 +11187,237 @@ } } } + }, + "pulse": { + "type": "object", + "description": "A pulse run (method pulse, engine::pulse): what was read and its distribution, the final populations, each capture's reading, the final density matrix's digest, the schedule's length, the integrator and its step, the device and where each parameter came from, and what the receipt binds (calibration).", + "required": [ + "read", + "outcome_bits", + "populations", + "simulated_qubits", + "density_hash", + "integrator", + "device", + "calibration" + ], + "properties": { + "read": { + "type": "string", + "description": "What the distribution reads: the captures together (into the program's bits when each writes one), or the final state." + }, + "outcome_bits": { + "type": "array", + "items": { + "type": "string" + }, + "description": "What each character of an outcome is, left to right: a program bit (c[0]) or a qubit ($0)." + }, + "distribution": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "number" + }, + "description": "The probability of each outcome, which the counts sample; null when the captures start at different samples." + }, + "populations": { + "type": "object", + "additionalProperties": { + "type": "number" + }, + "description": "The final state read on every simulated qubit, the highest qubit first, a level above 0 read as 1." + }, + "simulated_qubits": { + "type": "array", + "items": { + "type": "integer" + } + }, + "captures": { + "type": "array", + "items": { + "type": "object", + "properties": { + "frame": { + "type": "string" + }, + "qubit": { + "type": "integer" + }, + "start": { + "type": "integer" + }, + "start_ns": { + "type": "number" + }, + "bit": { + "type": [ + "string", + "null" + ] + }, + "p1": { + "type": "number" + }, + "p2": { + "type": "number" + } + } + } + }, + "leakage": { + "type": "array", + "items": { + "type": "object", + "properties": { + "qubit": { + "type": "integer" + }, + "p2": { + "type": "number" + } + } + }, + "description": "With three levels: each qubit's final probability of level 2." + }, + "trace": { + "type": "number" + }, + "density_hash": { + "type": "string", + "description": "The BLAKE3 of the final density matrix's bits: the same on every platform." + }, + "duration": { + "type": "integer" + }, + "duration_ns": { + "type": "number" + }, + "integrator": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "order": { + "type": "integer" + }, + "dt_ns": { + "type": "number" + }, + "substeps": { + "type": "integer" + }, + "step_ns": { + "type": "number" + }, + "steps": { + "type": "integer" + }, + "norm_max": { + "type": "number" + }, + "frame": { + "type": "string" + } + } + }, + "device": { + "type": "object", + "properties": { + "levels": { + "type": "integer" + }, + "dt_ns": { + "type": "number" + }, + "qubits": { + "type": "array", + "items": { + "type": "object", + "properties": { + "qubit": { + "type": "integer" + }, + "frequency_hz": { + "type": "number" + }, + "rabi_hz": { + "type": "number" + }, + "anharmonicity_hz": { + "type": "number" + }, + "t1_ns": { + "type": "number" + }, + "t2_ns": { + "type": "number" + }, + "from": { + "type": "object", + "additionalProperties": { + "enum": [ + "pulse", + "target", + "default", + "none" + ] + } + } + } + } + }, + "couplings": { + "type": "array" + }, + "model": { + "type": "string" + } + } + }, + "calibration": { + "type": "object", + "description": "What the receipt binds: the program's, its calibrations', the schedule's and the device's digests, and the target snapshot.", + "properties": { + "program": { + "type": "string" + }, + "calibrations": { + "type": "string" + }, + "schedule": { + "type": "string" + }, + "device": { + "type": "string" + }, + "target": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "calibration": { + "type": "object" + } + } + } + } + }, + "not_modelled": { + "type": "array", + "items": { + "type": "string" + } + } + } } } }, @@ -10397,7 +11721,7 @@ }, "method": { "type": "string", - "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it." + "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it; pulse runs a pulse program (OpenPulse calibrations) under the pulse options." }, "observables": { "type": "array", @@ -10951,6 +12275,121 @@ ] } ] + }, + "pulse": { + "type": "object", + "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", + "additionalProperties": false, + "properties": { + "dt_ns": { + "type": "number", + "exclusiveMinimum": 0, + "maximum": 1000, + "description": "The sample period, ns (default 0.5)." + }, + "levels": { + "type": "integer", + "enum": [ + 2, + 3 + ], + "description": "Levels kept per qubit (default 2; 3 shows leakage)." + }, + "tolerance": { + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 1, + "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." + }, + "substeps": { + "type": "integer", + "minimum": 1, + "maximum": 4096, + "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." + }, + "qubits": { + "type": "object", + "description": "Each qubit's parameters by its index, over the target's and the defaults.", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "frequency_ghz": { + "type": "number", + "description": "Its frequency, GHz." + }, + "rabi_mhz": { + "type": "number", + "description": "Its Rabi frequency at full amplitude, MHz." + }, + "anharmonicity_mhz": { + "type": "number", + "description": "Its anharmonicity, MHz." + }, + "t1_us": { + "type": "number", + "description": "Its relaxation time, µs." + }, + "t2_us": { + "type": "number", + "description": "Its coherence time, µs (at most 2·T1)." + } + } + } + }, + "couplings": { + "type": "array", + "description": "Static ZZ couplings.", + "items": { + "type": "object", + "required": [ + "qubits", + "zz_khz" + ], + "additionalProperties": false, + "properties": { + "qubits": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + }, + "minItems": 2, + "maxItems": 2 + }, + "zz_khz": { + "type": "number", + "description": "The coupling's rate, kHz." + } + } + } + }, + "ports": { + "type": "object", + "description": "Ports by name: what each is for and the qubit it reaches.", + "additionalProperties": { + "type": "object", + "required": [ + "kind", + "qubit" + ], + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "qubit": { + "type": "integer", + "minimum": 0 + } + } + } + } + } } } }, @@ -11070,6 +12509,24 @@ }, "batch": { "type": "integer" + }, + "bound": { + "type": "number", + "description": "bounded (a pulse run): every probability within this of the exact solution of the model's master equation, the truncation bound and the rounding estimate together." + }, + "truncation_bound": { + "type": "number", + "description": "A pulse run: the proved bound, in the trace norm, on what the integrator's series leaves out." + }, + "rounding_estimate": { + "type": "number", + "description": "A pulse run: an estimate of floating-point rounding beside the bound, not a proof." + }, + "tolerance": { + "type": "number" + }, + "norm": { + "type": "string" } } }, @@ -12583,6 +14040,237 @@ } } } + }, + "pulse": { + "type": "object", + "description": "A pulse run (method pulse, engine::pulse): what was read and its distribution, the final populations, each capture's reading, the final density matrix's digest, the schedule's length, the integrator and its step, the device and where each parameter came from, and what the receipt binds (calibration).", + "required": [ + "read", + "outcome_bits", + "populations", + "simulated_qubits", + "density_hash", + "integrator", + "device", + "calibration" + ], + "properties": { + "read": { + "type": "string", + "description": "What the distribution reads: the captures together (into the program's bits when each writes one), or the final state." + }, + "outcome_bits": { + "type": "array", + "items": { + "type": "string" + }, + "description": "What each character of an outcome is, left to right: a program bit (c[0]) or a qubit ($0)." + }, + "distribution": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "number" + }, + "description": "The probability of each outcome, which the counts sample; null when the captures start at different samples." + }, + "populations": { + "type": "object", + "additionalProperties": { + "type": "number" + }, + "description": "The final state read on every simulated qubit, the highest qubit first, a level above 0 read as 1." + }, + "simulated_qubits": { + "type": "array", + "items": { + "type": "integer" + } + }, + "captures": { + "type": "array", + "items": { + "type": "object", + "properties": { + "frame": { + "type": "string" + }, + "qubit": { + "type": "integer" + }, + "start": { + "type": "integer" + }, + "start_ns": { + "type": "number" + }, + "bit": { + "type": [ + "string", + "null" + ] + }, + "p1": { + "type": "number" + }, + "p2": { + "type": "number" + } + } + } + }, + "leakage": { + "type": "array", + "items": { + "type": "object", + "properties": { + "qubit": { + "type": "integer" + }, + "p2": { + "type": "number" + } + } + }, + "description": "With three levels: each qubit's final probability of level 2." + }, + "trace": { + "type": "number" + }, + "density_hash": { + "type": "string", + "description": "The BLAKE3 of the final density matrix's bits: the same on every platform." + }, + "duration": { + "type": "integer" + }, + "duration_ns": { + "type": "number" + }, + "integrator": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "order": { + "type": "integer" + }, + "dt_ns": { + "type": "number" + }, + "substeps": { + "type": "integer" + }, + "step_ns": { + "type": "number" + }, + "steps": { + "type": "integer" + }, + "norm_max": { + "type": "number" + }, + "frame": { + "type": "string" + } + } + }, + "device": { + "type": "object", + "properties": { + "levels": { + "type": "integer" + }, + "dt_ns": { + "type": "number" + }, + "qubits": { + "type": "array", + "items": { + "type": "object", + "properties": { + "qubit": { + "type": "integer" + }, + "frequency_hz": { + "type": "number" + }, + "rabi_hz": { + "type": "number" + }, + "anharmonicity_hz": { + "type": "number" + }, + "t1_ns": { + "type": "number" + }, + "t2_ns": { + "type": "number" + }, + "from": { + "type": "object", + "additionalProperties": { + "enum": [ + "pulse", + "target", + "default", + "none" + ] + } + } + } + } + }, + "couplings": { + "type": "array" + }, + "model": { + "type": "string" + } + } + }, + "calibration": { + "type": "object", + "description": "What the receipt binds: the program's, its calibrations', the schedule's and the device's digests, and the target snapshot.", + "properties": { + "program": { + "type": "string" + }, + "calibrations": { + "type": "string" + }, + "schedule": { + "type": "string" + }, + "device": { + "type": "string" + }, + "target": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "calibration": { + "type": "object" + } + } + } + } + }, + "not_modelled": { + "type": "array", + "items": { + "type": "string" + } + } + } } } }, @@ -14855,9 +16543,10 @@ "cutrank", "cutrank_single", "density_matrix", - "trajectories" + "trajectories", + "pulse" ], - "description": "Leave as auto unless you need a particular method." + "description": "Leave as auto unless you need a particular method; pulse runs a pulse program (OpenPulse calibrations on physical qubits, see pulse_schedule) under the pulse options, its error bounded." }, "seed": { "type": "integer", @@ -15138,6 +16827,121 @@ } ] }, + "pulse": { + "type": "object", + "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", + "additionalProperties": false, + "properties": { + "dt_ns": { + "type": "number", + "exclusiveMinimum": 0, + "maximum": 1000, + "description": "The sample period, ns (default 0.5)." + }, + "levels": { + "type": "integer", + "enum": [ + 2, + 3 + ], + "description": "Levels kept per qubit (default 2; 3 shows leakage)." + }, + "tolerance": { + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 1, + "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." + }, + "substeps": { + "type": "integer", + "minimum": 1, + "maximum": 4096, + "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." + }, + "qubits": { + "type": "object", + "description": "Each qubit's parameters by its index, over the target's and the defaults.", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "frequency_ghz": { + "type": "number", + "description": "Its frequency, GHz." + }, + "rabi_mhz": { + "type": "number", + "description": "Its Rabi frequency at full amplitude, MHz." + }, + "anharmonicity_mhz": { + "type": "number", + "description": "Its anharmonicity, MHz." + }, + "t1_us": { + "type": "number", + "description": "Its relaxation time, µs." + }, + "t2_us": { + "type": "number", + "description": "Its coherence time, µs (at most 2·T1)." + } + } + } + }, + "couplings": { + "type": "array", + "description": "Static ZZ couplings.", + "items": { + "type": "object", + "required": [ + "qubits", + "zz_khz" + ], + "additionalProperties": false, + "properties": { + "qubits": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + }, + "minItems": 2, + "maxItems": 2 + }, + "zz_khz": { + "type": "number", + "description": "The coupling's rate, kHz." + } + } + } + }, + "ports": { + "type": "object", + "description": "Ports by name: what each is for and the qubit it reaches.", + "additionalProperties": { + "type": "object", + "required": [ + "kind", + "qubit" + ], + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "qubit": { + "type": "integer", + "minimum": 0 + } + } + } + } + } + }, "programs": { "type": "array", "minItems": 1, @@ -16326,7 +18130,7 @@ }, "method": { "type": "string", - "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it." + "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it; pulse runs a pulse program (OpenPulse calibrations) under the pulse options." }, "observables": { "type": "array", @@ -16880,6 +18684,121 @@ ] } ] + }, + "pulse": { + "type": "object", + "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", + "additionalProperties": false, + "properties": { + "dt_ns": { + "type": "number", + "exclusiveMinimum": 0, + "maximum": 1000, + "description": "The sample period, ns (default 0.5)." + }, + "levels": { + "type": "integer", + "enum": [ + 2, + 3 + ], + "description": "Levels kept per qubit (default 2; 3 shows leakage)." + }, + "tolerance": { + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 1, + "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." + }, + "substeps": { + "type": "integer", + "minimum": 1, + "maximum": 4096, + "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." + }, + "qubits": { + "type": "object", + "description": "Each qubit's parameters by its index, over the target's and the defaults.", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "frequency_ghz": { + "type": "number", + "description": "Its frequency, GHz." + }, + "rabi_mhz": { + "type": "number", + "description": "Its Rabi frequency at full amplitude, MHz." + }, + "anharmonicity_mhz": { + "type": "number", + "description": "Its anharmonicity, MHz." + }, + "t1_us": { + "type": "number", + "description": "Its relaxation time, µs." + }, + "t2_us": { + "type": "number", + "description": "Its coherence time, µs (at most 2·T1)." + } + } + } + }, + "couplings": { + "type": "array", + "description": "Static ZZ couplings.", + "items": { + "type": "object", + "required": [ + "qubits", + "zz_khz" + ], + "additionalProperties": false, + "properties": { + "qubits": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + }, + "minItems": 2, + "maxItems": 2 + }, + "zz_khz": { + "type": "number", + "description": "The coupling's rate, kHz." + } + } + } + }, + "ports": { + "type": "object", + "description": "Ports by name: what each is for and the qubit it reaches.", + "additionalProperties": { + "type": "object", + "required": [ + "kind", + "qubit" + ], + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "qubit": { + "type": "integer", + "minimum": 0 + } + } + } + } + } } } }, @@ -17001,6 +18920,24 @@ }, "batch": { "type": "integer" + }, + "bound": { + "type": "number", + "description": "bounded (a pulse run): every probability within this of the exact solution of the model's master equation, the truncation bound and the rounding estimate together." + }, + "truncation_bound": { + "type": "number", + "description": "A pulse run: the proved bound, in the trace norm, on what the integrator's series leaves out." + }, + "rounding_estimate": { + "type": "number", + "description": "A pulse run: an estimate of floating-point rounding beside the bound, not a proof." + }, + "tolerance": { + "type": "number" + }, + "norm": { + "type": "string" } } }, @@ -18514,6 +20451,237 @@ } } } + }, + "pulse": { + "type": "object", + "description": "A pulse run (method pulse, engine::pulse): what was read and its distribution, the final populations, each capture's reading, the final density matrix's digest, the schedule's length, the integrator and its step, the device and where each parameter came from, and what the receipt binds (calibration).", + "required": [ + "read", + "outcome_bits", + "populations", + "simulated_qubits", + "density_hash", + "integrator", + "device", + "calibration" + ], + "properties": { + "read": { + "type": "string", + "description": "What the distribution reads: the captures together (into the program's bits when each writes one), or the final state." + }, + "outcome_bits": { + "type": "array", + "items": { + "type": "string" + }, + "description": "What each character of an outcome is, left to right: a program bit (c[0]) or a qubit ($0)." + }, + "distribution": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "number" + }, + "description": "The probability of each outcome, which the counts sample; null when the captures start at different samples." + }, + "populations": { + "type": "object", + "additionalProperties": { + "type": "number" + }, + "description": "The final state read on every simulated qubit, the highest qubit first, a level above 0 read as 1." + }, + "simulated_qubits": { + "type": "array", + "items": { + "type": "integer" + } + }, + "captures": { + "type": "array", + "items": { + "type": "object", + "properties": { + "frame": { + "type": "string" + }, + "qubit": { + "type": "integer" + }, + "start": { + "type": "integer" + }, + "start_ns": { + "type": "number" + }, + "bit": { + "type": [ + "string", + "null" + ] + }, + "p1": { + "type": "number" + }, + "p2": { + "type": "number" + } + } + } + }, + "leakage": { + "type": "array", + "items": { + "type": "object", + "properties": { + "qubit": { + "type": "integer" + }, + "p2": { + "type": "number" + } + } + }, + "description": "With three levels: each qubit's final probability of level 2." + }, + "trace": { + "type": "number" + }, + "density_hash": { + "type": "string", + "description": "The BLAKE3 of the final density matrix's bits: the same on every platform." + }, + "duration": { + "type": "integer" + }, + "duration_ns": { + "type": "number" + }, + "integrator": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "order": { + "type": "integer" + }, + "dt_ns": { + "type": "number" + }, + "substeps": { + "type": "integer" + }, + "step_ns": { + "type": "number" + }, + "steps": { + "type": "integer" + }, + "norm_max": { + "type": "number" + }, + "frame": { + "type": "string" + } + } + }, + "device": { + "type": "object", + "properties": { + "levels": { + "type": "integer" + }, + "dt_ns": { + "type": "number" + }, + "qubits": { + "type": "array", + "items": { + "type": "object", + "properties": { + "qubit": { + "type": "integer" + }, + "frequency_hz": { + "type": "number" + }, + "rabi_hz": { + "type": "number" + }, + "anharmonicity_hz": { + "type": "number" + }, + "t1_ns": { + "type": "number" + }, + "t2_ns": { + "type": "number" + }, + "from": { + "type": "object", + "additionalProperties": { + "enum": [ + "pulse", + "target", + "default", + "none" + ] + } + } + } + } + }, + "couplings": { + "type": "array" + }, + "model": { + "type": "string" + } + } + }, + "calibration": { + "type": "object", + "description": "What the receipt binds: the program's, its calibrations', the schedule's and the device's digests, and the target snapshot.", + "properties": { + "program": { + "type": "string" + }, + "calibrations": { + "type": "string" + }, + "schedule": { + "type": "string" + }, + "device": { + "type": "string" + }, + "target": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "calibration": { + "type": "object" + } + } + } + } + }, + "not_modelled": { + "type": "array", + "items": { + "type": "string" + } + } + } } } }, @@ -18817,7 +20985,7 @@ }, "method": { "type": "string", - "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it." + "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it; pulse runs a pulse program (OpenPulse calibrations) under the pulse options." }, "observables": { "type": "array", @@ -19371,6 +21539,121 @@ ] } ] + }, + "pulse": { + "type": "object", + "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", + "additionalProperties": false, + "properties": { + "dt_ns": { + "type": "number", + "exclusiveMinimum": 0, + "maximum": 1000, + "description": "The sample period, ns (default 0.5)." + }, + "levels": { + "type": "integer", + "enum": [ + 2, + 3 + ], + "description": "Levels kept per qubit (default 2; 3 shows leakage)." + }, + "tolerance": { + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 1, + "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." + }, + "substeps": { + "type": "integer", + "minimum": 1, + "maximum": 4096, + "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." + }, + "qubits": { + "type": "object", + "description": "Each qubit's parameters by its index, over the target's and the defaults.", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "frequency_ghz": { + "type": "number", + "description": "Its frequency, GHz." + }, + "rabi_mhz": { + "type": "number", + "description": "Its Rabi frequency at full amplitude, MHz." + }, + "anharmonicity_mhz": { + "type": "number", + "description": "Its anharmonicity, MHz." + }, + "t1_us": { + "type": "number", + "description": "Its relaxation time, µs." + }, + "t2_us": { + "type": "number", + "description": "Its coherence time, µs (at most 2·T1)." + } + } + } + }, + "couplings": { + "type": "array", + "description": "Static ZZ couplings.", + "items": { + "type": "object", + "required": [ + "qubits", + "zz_khz" + ], + "additionalProperties": false, + "properties": { + "qubits": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + }, + "minItems": 2, + "maxItems": 2 + }, + "zz_khz": { + "type": "number", + "description": "The coupling's rate, kHz." + } + } + } + }, + "ports": { + "type": "object", + "description": "Ports by name: what each is for and the qubit it reaches.", + "additionalProperties": { + "type": "object", + "required": [ + "kind", + "qubit" + ], + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "qubit": { + "type": "integer", + "minimum": 0 + } + } + } + } + } } } }, @@ -19490,6 +21773,24 @@ }, "batch": { "type": "integer" + }, + "bound": { + "type": "number", + "description": "bounded (a pulse run): every probability within this of the exact solution of the model's master equation, the truncation bound and the rounding estimate together." + }, + "truncation_bound": { + "type": "number", + "description": "A pulse run: the proved bound, in the trace norm, on what the integrator's series leaves out." + }, + "rounding_estimate": { + "type": "number", + "description": "A pulse run: an estimate of floating-point rounding beside the bound, not a proof." + }, + "tolerance": { + "type": "number" + }, + "norm": { + "type": "string" } } }, @@ -21003,6 +23304,237 @@ } } } + }, + "pulse": { + "type": "object", + "description": "A pulse run (method pulse, engine::pulse): what was read and its distribution, the final populations, each capture's reading, the final density matrix's digest, the schedule's length, the integrator and its step, the device and where each parameter came from, and what the receipt binds (calibration).", + "required": [ + "read", + "outcome_bits", + "populations", + "simulated_qubits", + "density_hash", + "integrator", + "device", + "calibration" + ], + "properties": { + "read": { + "type": "string", + "description": "What the distribution reads: the captures together (into the program's bits when each writes one), or the final state." + }, + "outcome_bits": { + "type": "array", + "items": { + "type": "string" + }, + "description": "What each character of an outcome is, left to right: a program bit (c[0]) or a qubit ($0)." + }, + "distribution": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "number" + }, + "description": "The probability of each outcome, which the counts sample; null when the captures start at different samples." + }, + "populations": { + "type": "object", + "additionalProperties": { + "type": "number" + }, + "description": "The final state read on every simulated qubit, the highest qubit first, a level above 0 read as 1." + }, + "simulated_qubits": { + "type": "array", + "items": { + "type": "integer" + } + }, + "captures": { + "type": "array", + "items": { + "type": "object", + "properties": { + "frame": { + "type": "string" + }, + "qubit": { + "type": "integer" + }, + "start": { + "type": "integer" + }, + "start_ns": { + "type": "number" + }, + "bit": { + "type": [ + "string", + "null" + ] + }, + "p1": { + "type": "number" + }, + "p2": { + "type": "number" + } + } + } + }, + "leakage": { + "type": "array", + "items": { + "type": "object", + "properties": { + "qubit": { + "type": "integer" + }, + "p2": { + "type": "number" + } + } + }, + "description": "With three levels: each qubit's final probability of level 2." + }, + "trace": { + "type": "number" + }, + "density_hash": { + "type": "string", + "description": "The BLAKE3 of the final density matrix's bits: the same on every platform." + }, + "duration": { + "type": "integer" + }, + "duration_ns": { + "type": "number" + }, + "integrator": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "order": { + "type": "integer" + }, + "dt_ns": { + "type": "number" + }, + "substeps": { + "type": "integer" + }, + "step_ns": { + "type": "number" + }, + "steps": { + "type": "integer" + }, + "norm_max": { + "type": "number" + }, + "frame": { + "type": "string" + } + } + }, + "device": { + "type": "object", + "properties": { + "levels": { + "type": "integer" + }, + "dt_ns": { + "type": "number" + }, + "qubits": { + "type": "array", + "items": { + "type": "object", + "properties": { + "qubit": { + "type": "integer" + }, + "frequency_hz": { + "type": "number" + }, + "rabi_hz": { + "type": "number" + }, + "anharmonicity_hz": { + "type": "number" + }, + "t1_ns": { + "type": "number" + }, + "t2_ns": { + "type": "number" + }, + "from": { + "type": "object", + "additionalProperties": { + "enum": [ + "pulse", + "target", + "default", + "none" + ] + } + } + } + } + }, + "couplings": { + "type": "array" + }, + "model": { + "type": "string" + } + } + }, + "calibration": { + "type": "object", + "description": "What the receipt binds: the program's, its calibrations', the schedule's and the device's digests, and the target snapshot.", + "properties": { + "program": { + "type": "string" + }, + "calibrations": { + "type": "string" + }, + "schedule": { + "type": "string" + }, + "device": { + "type": "string" + }, + "target": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "calibration": { + "type": "object" + } + } + } + } + }, + "not_modelled": { + "type": "array", + "items": { + "type": "string" + } + } + } } } }, @@ -35475,6 +38007,121 @@ } } ] + }, + "pulse": { + "type": "object", + "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", + "additionalProperties": false, + "properties": { + "dt_ns": { + "type": "number", + "exclusiveMinimum": 0, + "maximum": 1000, + "description": "The sample period, ns (default 0.5)." + }, + "levels": { + "type": "integer", + "enum": [ + 2, + 3 + ], + "description": "Levels kept per qubit (default 2; 3 shows leakage)." + }, + "tolerance": { + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 1, + "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." + }, + "substeps": { + "type": "integer", + "minimum": 1, + "maximum": 4096, + "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." + }, + "qubits": { + "type": "object", + "description": "Each qubit's parameters by its index, over the target's and the defaults.", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "frequency_ghz": { + "type": "number", + "description": "Its frequency, GHz." + }, + "rabi_mhz": { + "type": "number", + "description": "Its Rabi frequency at full amplitude, MHz." + }, + "anharmonicity_mhz": { + "type": "number", + "description": "Its anharmonicity, MHz." + }, + "t1_us": { + "type": "number", + "description": "Its relaxation time, µs." + }, + "t2_us": { + "type": "number", + "description": "Its coherence time, µs (at most 2·T1)." + } + } + } + }, + "couplings": { + "type": "array", + "description": "Static ZZ couplings.", + "items": { + "type": "object", + "required": [ + "qubits", + "zz_khz" + ], + "additionalProperties": false, + "properties": { + "qubits": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + }, + "minItems": 2, + "maxItems": 2 + }, + "zz_khz": { + "type": "number", + "description": "The coupling's rate, kHz." + } + } + } + }, + "ports": { + "type": "object", + "description": "Ports by name: what each is for and the qubit it reaches.", + "additionalProperties": { + "type": "object", + "required": [ + "kind", + "qubit" + ], + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "qubit": { + "type": "integer", + "minimum": 0 + } + } + } + } + } } } }, @@ -35604,7 +38251,7 @@ }, "method": { "type": "string", - "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it." + "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it; pulse runs a pulse program (OpenPulse calibrations) under the pulse options." }, "observables": { "type": "array", @@ -36158,6 +38805,121 @@ ] } ] + }, + "pulse": { + "type": "object", + "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", + "additionalProperties": false, + "properties": { + "dt_ns": { + "type": "number", + "exclusiveMinimum": 0, + "maximum": 1000, + "description": "The sample period, ns (default 0.5)." + }, + "levels": { + "type": "integer", + "enum": [ + 2, + 3 + ], + "description": "Levels kept per qubit (default 2; 3 shows leakage)." + }, + "tolerance": { + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 1, + "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." + }, + "substeps": { + "type": "integer", + "minimum": 1, + "maximum": 4096, + "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." + }, + "qubits": { + "type": "object", + "description": "Each qubit's parameters by its index, over the target's and the defaults.", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "frequency_ghz": { + "type": "number", + "description": "Its frequency, GHz." + }, + "rabi_mhz": { + "type": "number", + "description": "Its Rabi frequency at full amplitude, MHz." + }, + "anharmonicity_mhz": { + "type": "number", + "description": "Its anharmonicity, MHz." + }, + "t1_us": { + "type": "number", + "description": "Its relaxation time, µs." + }, + "t2_us": { + "type": "number", + "description": "Its coherence time, µs (at most 2·T1)." + } + } + } + }, + "couplings": { + "type": "array", + "description": "Static ZZ couplings.", + "items": { + "type": "object", + "required": [ + "qubits", + "zz_khz" + ], + "additionalProperties": false, + "properties": { + "qubits": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + }, + "minItems": 2, + "maxItems": 2 + }, + "zz_khz": { + "type": "number", + "description": "The coupling's rate, kHz." + } + } + } + }, + "ports": { + "type": "object", + "description": "Ports by name: what each is for and the qubit it reaches.", + "additionalProperties": { + "type": "object", + "required": [ + "kind", + "qubit" + ], + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "qubit": { + "type": "integer", + "minimum": 0 + } + } + } + } + } } } }, @@ -36279,6 +39041,24 @@ }, "batch": { "type": "integer" + }, + "bound": { + "type": "number", + "description": "bounded (a pulse run): every probability within this of the exact solution of the model's master equation, the truncation bound and the rounding estimate together." + }, + "truncation_bound": { + "type": "number", + "description": "A pulse run: the proved bound, in the trace norm, on what the integrator's series leaves out." + }, + "rounding_estimate": { + "type": "number", + "description": "A pulse run: an estimate of floating-point rounding beside the bound, not a proof." + }, + "tolerance": { + "type": "number" + }, + "norm": { + "type": "string" } } }, @@ -37792,6 +40572,237 @@ } } } + }, + "pulse": { + "type": "object", + "description": "A pulse run (method pulse, engine::pulse): what was read and its distribution, the final populations, each capture's reading, the final density matrix's digest, the schedule's length, the integrator and its step, the device and where each parameter came from, and what the receipt binds (calibration).", + "required": [ + "read", + "outcome_bits", + "populations", + "simulated_qubits", + "density_hash", + "integrator", + "device", + "calibration" + ], + "properties": { + "read": { + "type": "string", + "description": "What the distribution reads: the captures together (into the program's bits when each writes one), or the final state." + }, + "outcome_bits": { + "type": "array", + "items": { + "type": "string" + }, + "description": "What each character of an outcome is, left to right: a program bit (c[0]) or a qubit ($0)." + }, + "distribution": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "number" + }, + "description": "The probability of each outcome, which the counts sample; null when the captures start at different samples." + }, + "populations": { + "type": "object", + "additionalProperties": { + "type": "number" + }, + "description": "The final state read on every simulated qubit, the highest qubit first, a level above 0 read as 1." + }, + "simulated_qubits": { + "type": "array", + "items": { + "type": "integer" + } + }, + "captures": { + "type": "array", + "items": { + "type": "object", + "properties": { + "frame": { + "type": "string" + }, + "qubit": { + "type": "integer" + }, + "start": { + "type": "integer" + }, + "start_ns": { + "type": "number" + }, + "bit": { + "type": [ + "string", + "null" + ] + }, + "p1": { + "type": "number" + }, + "p2": { + "type": "number" + } + } + } + }, + "leakage": { + "type": "array", + "items": { + "type": "object", + "properties": { + "qubit": { + "type": "integer" + }, + "p2": { + "type": "number" + } + } + }, + "description": "With three levels: each qubit's final probability of level 2." + }, + "trace": { + "type": "number" + }, + "density_hash": { + "type": "string", + "description": "The BLAKE3 of the final density matrix's bits: the same on every platform." + }, + "duration": { + "type": "integer" + }, + "duration_ns": { + "type": "number" + }, + "integrator": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "order": { + "type": "integer" + }, + "dt_ns": { + "type": "number" + }, + "substeps": { + "type": "integer" + }, + "step_ns": { + "type": "number" + }, + "steps": { + "type": "integer" + }, + "norm_max": { + "type": "number" + }, + "frame": { + "type": "string" + } + } + }, + "device": { + "type": "object", + "properties": { + "levels": { + "type": "integer" + }, + "dt_ns": { + "type": "number" + }, + "qubits": { + "type": "array", + "items": { + "type": "object", + "properties": { + "qubit": { + "type": "integer" + }, + "frequency_hz": { + "type": "number" + }, + "rabi_hz": { + "type": "number" + }, + "anharmonicity_hz": { + "type": "number" + }, + "t1_ns": { + "type": "number" + }, + "t2_ns": { + "type": "number" + }, + "from": { + "type": "object", + "additionalProperties": { + "enum": [ + "pulse", + "target", + "default", + "none" + ] + } + } + } + } + }, + "couplings": { + "type": "array" + }, + "model": { + "type": "string" + } + } + }, + "calibration": { + "type": "object", + "description": "What the receipt binds: the program's, its calibrations', the schedule's and the device's digests, and the target snapshot.", + "properties": { + "program": { + "type": "string" + }, + "calibrations": { + "type": "string" + }, + "schedule": { + "type": "string" + }, + "device": { + "type": "string" + }, + "target": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "calibration": { + "type": "object" + } + } + } + } + }, + "not_modelled": { + "type": "array", + "items": { + "type": "string" + } + } + } } } }, @@ -38095,7 +41106,7 @@ }, "method": { "type": "string", - "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it." + "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it; pulse runs a pulse program (OpenPulse calibrations) under the pulse options." }, "observables": { "type": "array", @@ -38649,6 +41660,121 @@ ] } ] + }, + "pulse": { + "type": "object", + "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", + "additionalProperties": false, + "properties": { + "dt_ns": { + "type": "number", + "exclusiveMinimum": 0, + "maximum": 1000, + "description": "The sample period, ns (default 0.5)." + }, + "levels": { + "type": "integer", + "enum": [ + 2, + 3 + ], + "description": "Levels kept per qubit (default 2; 3 shows leakage)." + }, + "tolerance": { + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 1, + "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." + }, + "substeps": { + "type": "integer", + "minimum": 1, + "maximum": 4096, + "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." + }, + "qubits": { + "type": "object", + "description": "Each qubit's parameters by its index, over the target's and the defaults.", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "frequency_ghz": { + "type": "number", + "description": "Its frequency, GHz." + }, + "rabi_mhz": { + "type": "number", + "description": "Its Rabi frequency at full amplitude, MHz." + }, + "anharmonicity_mhz": { + "type": "number", + "description": "Its anharmonicity, MHz." + }, + "t1_us": { + "type": "number", + "description": "Its relaxation time, µs." + }, + "t2_us": { + "type": "number", + "description": "Its coherence time, µs (at most 2·T1)." + } + } + } + }, + "couplings": { + "type": "array", + "description": "Static ZZ couplings.", + "items": { + "type": "object", + "required": [ + "qubits", + "zz_khz" + ], + "additionalProperties": false, + "properties": { + "qubits": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + }, + "minItems": 2, + "maxItems": 2 + }, + "zz_khz": { + "type": "number", + "description": "The coupling's rate, kHz." + } + } + } + }, + "ports": { + "type": "object", + "description": "Ports by name: what each is for and the qubit it reaches.", + "additionalProperties": { + "type": "object", + "required": [ + "kind", + "qubit" + ], + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "qubit": { + "type": "integer", + "minimum": 0 + } + } + } + } + } } } }, @@ -38768,6 +41894,24 @@ }, "batch": { "type": "integer" + }, + "bound": { + "type": "number", + "description": "bounded (a pulse run): every probability within this of the exact solution of the model's master equation, the truncation bound and the rounding estimate together." + }, + "truncation_bound": { + "type": "number", + "description": "A pulse run: the proved bound, in the trace norm, on what the integrator's series leaves out." + }, + "rounding_estimate": { + "type": "number", + "description": "A pulse run: an estimate of floating-point rounding beside the bound, not a proof." + }, + "tolerance": { + "type": "number" + }, + "norm": { + "type": "string" } } }, @@ -40281,6 +43425,237 @@ } } } + }, + "pulse": { + "type": "object", + "description": "A pulse run (method pulse, engine::pulse): what was read and its distribution, the final populations, each capture's reading, the final density matrix's digest, the schedule's length, the integrator and its step, the device and where each parameter came from, and what the receipt binds (calibration).", + "required": [ + "read", + "outcome_bits", + "populations", + "simulated_qubits", + "density_hash", + "integrator", + "device", + "calibration" + ], + "properties": { + "read": { + "type": "string", + "description": "What the distribution reads: the captures together (into the program's bits when each writes one), or the final state." + }, + "outcome_bits": { + "type": "array", + "items": { + "type": "string" + }, + "description": "What each character of an outcome is, left to right: a program bit (c[0]) or a qubit ($0)." + }, + "distribution": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "number" + }, + "description": "The probability of each outcome, which the counts sample; null when the captures start at different samples." + }, + "populations": { + "type": "object", + "additionalProperties": { + "type": "number" + }, + "description": "The final state read on every simulated qubit, the highest qubit first, a level above 0 read as 1." + }, + "simulated_qubits": { + "type": "array", + "items": { + "type": "integer" + } + }, + "captures": { + "type": "array", + "items": { + "type": "object", + "properties": { + "frame": { + "type": "string" + }, + "qubit": { + "type": "integer" + }, + "start": { + "type": "integer" + }, + "start_ns": { + "type": "number" + }, + "bit": { + "type": [ + "string", + "null" + ] + }, + "p1": { + "type": "number" + }, + "p2": { + "type": "number" + } + } + } + }, + "leakage": { + "type": "array", + "items": { + "type": "object", + "properties": { + "qubit": { + "type": "integer" + }, + "p2": { + "type": "number" + } + } + }, + "description": "With three levels: each qubit's final probability of level 2." + }, + "trace": { + "type": "number" + }, + "density_hash": { + "type": "string", + "description": "The BLAKE3 of the final density matrix's bits: the same on every platform." + }, + "duration": { + "type": "integer" + }, + "duration_ns": { + "type": "number" + }, + "integrator": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "order": { + "type": "integer" + }, + "dt_ns": { + "type": "number" + }, + "substeps": { + "type": "integer" + }, + "step_ns": { + "type": "number" + }, + "steps": { + "type": "integer" + }, + "norm_max": { + "type": "number" + }, + "frame": { + "type": "string" + } + } + }, + "device": { + "type": "object", + "properties": { + "levels": { + "type": "integer" + }, + "dt_ns": { + "type": "number" + }, + "qubits": { + "type": "array", + "items": { + "type": "object", + "properties": { + "qubit": { + "type": "integer" + }, + "frequency_hz": { + "type": "number" + }, + "rabi_hz": { + "type": "number" + }, + "anharmonicity_hz": { + "type": "number" + }, + "t1_ns": { + "type": "number" + }, + "t2_ns": { + "type": "number" + }, + "from": { + "type": "object", + "additionalProperties": { + "enum": [ + "pulse", + "target", + "default", + "none" + ] + } + } + } + } + }, + "couplings": { + "type": "array" + }, + "model": { + "type": "string" + } + } + }, + "calibration": { + "type": "object", + "description": "What the receipt binds: the program's, its calibrations', the schedule's and the device's digests, and the target snapshot.", + "properties": { + "program": { + "type": "string" + }, + "calibrations": { + "type": "string" + }, + "schedule": { + "type": "string" + }, + "device": { + "type": "string" + }, + "target": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "calibration": { + "type": "object" + } + } + } + } + }, + "not_modelled": { + "type": "array", + "items": { + "type": "string" + } + } + } } } }, @@ -40766,7 +44141,7 @@ }, "method": { "type": "string", - "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it." + "description": "auto (the default: the strongest method the program admits), or a method by its name as the methods list names it; pulse runs a pulse program (OpenPulse calibrations) under the pulse options." }, "observables": { "type": "array", @@ -41320,6 +44695,121 @@ ] } ] + }, + "pulse": { + "type": "object", + "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", + "additionalProperties": false, + "properties": { + "dt_ns": { + "type": "number", + "exclusiveMinimum": 0, + "maximum": 1000, + "description": "The sample period, ns (default 0.5)." + }, + "levels": { + "type": "integer", + "enum": [ + 2, + 3 + ], + "description": "Levels kept per qubit (default 2; 3 shows leakage)." + }, + "tolerance": { + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 1, + "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." + }, + "substeps": { + "type": "integer", + "minimum": 1, + "maximum": 4096, + "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." + }, + "qubits": { + "type": "object", + "description": "Each qubit's parameters by its index, over the target's and the defaults.", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "frequency_ghz": { + "type": "number", + "description": "Its frequency, GHz." + }, + "rabi_mhz": { + "type": "number", + "description": "Its Rabi frequency at full amplitude, MHz." + }, + "anharmonicity_mhz": { + "type": "number", + "description": "Its anharmonicity, MHz." + }, + "t1_us": { + "type": "number", + "description": "Its relaxation time, µs." + }, + "t2_us": { + "type": "number", + "description": "Its coherence time, µs (at most 2·T1)." + } + } + } + }, + "couplings": { + "type": "array", + "description": "Static ZZ couplings.", + "items": { + "type": "object", + "required": [ + "qubits", + "zz_khz" + ], + "additionalProperties": false, + "properties": { + "qubits": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + }, + "minItems": 2, + "maxItems": 2 + }, + "zz_khz": { + "type": "number", + "description": "The coupling's rate, kHz." + } + } + } + }, + "ports": { + "type": "object", + "description": "Ports by name: what each is for and the qubit it reaches.", + "additionalProperties": { + "type": "object", + "required": [ + "kind", + "qubit" + ], + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "qubit": { + "type": "integer", + "minimum": 0 + } + } + } + } + } } } } @@ -43511,6 +47001,1214 @@ } ] } + }, + { + "name": "pulse_schedule", + "title": "Read a pulse program into its schedule", + "description": "Read a pulse program (OpenPulse calibrations: ports, frames, waveforms, play, delay, shift_phase, set_phase, shift_frequency, set_frequency, captures and barriers) into its schedule on frames: every instruction at its sample, each waveform's samples (gaussian, drag, gaussian_square, constant, sech, sine, or explicit), each gate call with the samples its calibration spans, and the digests a pulse run's receipt binds (the program, its calibrations, the schedule). Durations are whole samples of dt (pulse.dt_ns, default 0.5 ns) or refused. A construct the reader does not take is refused with a Q code and its line. Sealed with the service's key. To run it, give the program to run_quantum_circuit with method \"pulse\".", + "inputSchema": { + "type": "object", + "required": [ + "qasm" + ], + "properties": { + "qasm": { + "type": "string", + "description": "A pulse program: OpenQASM 3 with `defcalgrammar \"openpulse\";`, `cal` blocks declaring ports (d0 drives qubit 0, m0 is its measurement port, a0 its acquisition port), frames (newframe(port, frequency Hz, phase)) and waveforms, a `defcal` for every gate it calls on physical qubits ($0), and the calls." + }, + "pulse": { + "type": "object", + "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", + "additionalProperties": false, + "properties": { + "dt_ns": { + "type": "number", + "exclusiveMinimum": 0, + "maximum": 1000, + "description": "The sample period, ns (default 0.5)." + }, + "levels": { + "type": "integer", + "enum": [ + 2, + 3 + ], + "description": "Levels kept per qubit (default 2; 3 shows leakage)." + }, + "tolerance": { + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 1, + "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." + }, + "substeps": { + "type": "integer", + "minimum": 1, + "maximum": 4096, + "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." + }, + "qubits": { + "type": "object", + "description": "Each qubit's parameters by its index, over the target's and the defaults.", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "frequency_ghz": { + "type": "number", + "description": "Its frequency, GHz." + }, + "rabi_mhz": { + "type": "number", + "description": "Its Rabi frequency at full amplitude, MHz." + }, + "anharmonicity_mhz": { + "type": "number", + "description": "Its anharmonicity, MHz." + }, + "t1_us": { + "type": "number", + "description": "Its relaxation time, µs." + }, + "t2_us": { + "type": "number", + "description": "Its coherence time, µs (at most 2·T1)." + } + } + } + }, + "couplings": { + "type": "array", + "description": "Static ZZ couplings.", + "items": { + "type": "object", + "required": [ + "qubits", + "zz_khz" + ], + "additionalProperties": false, + "properties": { + "qubits": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + }, + "minItems": 2, + "maxItems": 2 + }, + "zz_khz": { + "type": "number", + "description": "The coupling's rate, kHz." + } + } + } + }, + "ports": { + "type": "object", + "description": "Ports by name: what each is for and the qubit it reaches.", + "additionalProperties": { + "type": "object", + "required": [ + "kind", + "qubit" + ], + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "qubit": { + "type": "integer", + "minimum": 0 + } + } + } + } + } + } + } + }, + "annotations": { + "readOnlyHint": true, + "idempotentHint": true, + "openWorldHint": false + }, + "outputSchema": { + "$id": "wai-quantum://schemas/pulse_schedule/1", + "type": "object", + "description": "A pulse answer as every host gives it (engine::pulse: pulse_schedule and pulse_check on both servers, wai-quantum pulse, POST /v1/pulse/*, the Studio): the request, and the result sealed in a job's form. A pulse program's schedule on frames, or a pulse job checked by running it again. A pulse run itself is a job (method pulse) and follows job-result.v1.json. A result names the engine that made it and this schema (engine and schema). docs/pulse.md explains each.", + "anyOf": [ + { + "title": "wai.quantum.pulse/1", + "description": "A pulse answer as every host gives it (engine::pulse: pulse_schedule and pulse_check on both servers, wai-quantum pulse, POST /v1/pulse/*, the Studio): the request, and the result sealed in a job's form. A pulse program's schedule on frames, or a pulse job checked by running it again. A pulse run itself is a job (method pulse) and follows job-result.v1.json. A result names the engine that made it and this schema (engine and schema). docs/pulse.md explains each.", + "type": "object", + "required": [ + "request", + "result" + ], + "properties": { + "request": { + "type": "object", + "description": "The call's request, as given." + }, + "result": { + "description": "A schedule or a check, with its receipt.", + "anyOf": [ + { + "type": "object", + "description": "A pulse program's schedule (pulse_schedule, wai-quantum pulse schedule, POST /v1/pulse/schedule): its frames and ports, every instruction at its sample, each waveform's samples, the gate calls, the reader's notes, and the digests a run binds.", + "required": [ + "answer", + "dt_ns", + "duration", + "frames", + "waveforms", + "instructions", + "calls", + "calibration" + ], + "properties": { + "engine": { + "type": "object", + "description": "The engine that made the result: the wai-quantum crate's name and version.", + "required": [ + "name", + "version" + ], + "properties": { + "name": { + "const": "wai-quantum" + }, + "version": { + "type": "string" + } + } + }, + "schema": { + "const": "wai.quantum.pulse/1" + }, + "answer": { + "const": "schedule" + }, + "dt_ns": { + "type": "number", + "description": "The sample period, ns." + }, + "duration": { + "type": "integer", + "description": "The latest time of any frame, samples." + }, + "duration_ns": { + "type": "number" + }, + "grammar": { + "type": [ + "string", + "null" + ] + }, + "ports": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "qubit": { + "type": "integer" + } + } + } + }, + "frames": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "port": { + "type": "string" + }, + "qubit": { + "type": "integer" + }, + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "frequency_hz": { + "type": "number" + }, + "phase": { + "type": "number" + } + } + } + }, + "waveforms": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "length": { + "type": "integer" + }, + "samples": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 2 + } + }, + "samples_every": { + "type": "integer", + "description": "Each k-th sample is drawn, when the schedule holds more than the view writes out." + } + } + } + }, + "instructions": { + "type": "array", + "items": { + "type": "object", + "required": [ + "frame", + "op", + "start", + "length" + ], + "properties": { + "frame": { + "type": "integer" + }, + "op": { + "enum": [ + "play", + "delay", + "shift_phase", + "set_phase", + "shift_frequency", + "set_frequency", + "capture", + "barrier" + ] + }, + "start": { + "type": "integer" + }, + "length": { + "type": "integer" + }, + "line": { + "type": "integer" + }, + "waveform": { + "type": "integer" + }, + "phase": { + "type": "number" + }, + "frequency_hz": { + "type": "number" + }, + "carrier": { + "type": "number", + "description": "The carrier's phase at the play's first sample, cycles in [0, 1)." + }, + "value": { + "type": "number" + }, + "bit": { + "type": [ + "string", + "null" + ] + } + } + } + }, + "calls": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "qubits": { + "type": "array", + "items": { + "type": "integer" + } + }, + "params": { + "type": "array", + "items": { + "type": "number" + } + }, + "start": { + "type": "integer" + }, + "end": { + "type": "integer" + }, + "line": { + "type": "integer" + } + } + } + }, + "bits": { + "type": "array", + "items": { + "type": "string" + } + }, + "qubits": { + "type": "array", + "items": { + "type": "integer" + } + }, + "decimated": { + "type": "string" + }, + "diagnostics": { + "type": "array", + "items": { + "type": "object", + "description": "A reader's note or warning (qasm::diag).", + "properties": { + "code": { + "type": "string" + }, + "outcome": { + "type": "string" + }, + "message": { + "type": "string" + }, + "line": { + "type": "integer" + }, + "column": { + "type": "integer" + }, + "length": { + "type": "integer" + } + } + } + }, + "calibration": { + "type": "object", + "description": "What a pulse run's receipt binds of the program: the BLAKE3 of the control program's text, of its calibrations as written (every cal, defcal and defcalgrammar, joined by newlines), and of the schedule's JSON; a run adds the device's and the target snapshot's.", + "required": [ + "program", + "calibrations", + "schedule" + ], + "properties": { + "program": { + "type": "string" + }, + "calibrations": { + "type": "string" + }, + "schedule": { + "type": "string" + }, + "device": { + "type": "string" + }, + "target": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "calibration": { + "type": "object" + } + } + } + } + }, + "receipt": { + "title": "wai.quantum.receipt/2", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "type": "object", + "required": [ + "alg", + "canon", + "key", + "digest", + "signature" + ], + "properties": { + "alg": { + "const": "ed25519" + }, + "canon": { + "const": "rfc8785", + "description": "The canonical form the digest is taken over." + }, + "key": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "description": "The signing key, 32 bytes in hex." + }, + "digest": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "description": "The digest signed, 32 bytes in hex." + }, + "signature": { + "type": "string", + "minLength": 128, + "maxLength": 128, + "description": "The signature, 64 bytes in hex." + } + } + } + } + }, + { + "type": "object", + "description": "A pulse job checked (pulse_check, wai-quantum pulse check, POST /v1/pulse/check): its receipt's signature, its program's digest, and the request run again with the result held to the one given.", + "required": [ + "answer", + "verified", + "signature", + "program", + "reproduced", + "differences", + "statement" + ], + "properties": { + "engine": { + "type": "object", + "description": "The engine that made the result: the wai-quantum crate's name and version.", + "required": [ + "name", + "version" + ], + "properties": { + "name": { + "const": "wai-quantum" + }, + "version": { + "type": "string" + } + } + }, + "schema": { + "const": "wai.quantum.pulse/1" + }, + "answer": { + "const": "check" + }, + "verified": { + "type": "boolean" + }, + "signature": { + "type": "boolean" + }, + "key": {}, + "program": { + "type": "boolean" + }, + "reproduced": { + "type": "boolean" + }, + "differences": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Each field (or field.member) whose value differs from the run here." + }, + "density_hash": { + "type": "object", + "properties": { + "given": {}, + "recomputed": {} + } + }, + "calibration": {}, + "statement": { + "type": "string" + }, + "receipt": { + "title": "wai.quantum.receipt/2", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "type": "object", + "required": [ + "alg", + "canon", + "key", + "digest", + "signature" + ], + "properties": { + "alg": { + "const": "ed25519" + }, + "canon": { + "const": "rfc8785", + "description": "The canonical form the digest is taken over." + }, + "key": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "description": "The signing key, 32 bytes in hex." + }, + "digest": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "description": "The digest signed, 32 bytes in hex." + }, + "signature": { + "type": "string", + "minLength": 128, + "maxLength": 128, + "description": "The signature, 64 bytes in hex." + } + } + } + } + } + ] + } + } + }, + { + "type": "object", + "description": "A refusal as data: the message, every code in it (the last is the code), its outcome and meaning as the code's table gives them. A host that refuses for its own reasons gives the error alone.", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "code": { + "type": [ + "string", + "null" + ] + }, + "codes": { + "type": "array", + "items": { + "type": "string" + } + }, + "outcome": { + "enum": [ + "refused", + "retry", + "failed", + "lowered", + "carried", + "preserved", + "warned", + "dropped", + "defaulted" + ] + }, + "meaning": { + "type": "string" + } + } + } + ] + } + }, + { + "name": "pulse_check", + "title": "Check a pulse run by running it again", + "description": "Check a pulse job as a host returned it ({\"request\", \"result\"}, the result with its receipt): the receipt's signature, the program's digest against the request's text, and the request run again here with the result held to the one given field by field (the density matrix's digest among them), each difference named. A pulse run is the same bits on every platform, so a result that does not reproduce was not made from that request by this engine. Sealed with the service's key. Quote `verified` and the statement.", + "inputSchema": { + "type": "object", + "required": [ + "request", + "result" + ], + "properties": { + "request": { + "type": "object", + "description": "The pulse job's request." + }, + "result": { + "type": "object", + "description": "Its result, with its receipt." + } + } + }, + "annotations": { + "readOnlyHint": true, + "idempotentHint": true, + "openWorldHint": false + }, + "outputSchema": { + "$id": "wai-quantum://schemas/pulse_check/1", + "type": "object", + "description": "A pulse answer as every host gives it (engine::pulse: pulse_schedule and pulse_check on both servers, wai-quantum pulse, POST /v1/pulse/*, the Studio): the request, and the result sealed in a job's form. A pulse program's schedule on frames, or a pulse job checked by running it again. A pulse run itself is a job (method pulse) and follows job-result.v1.json. A result names the engine that made it and this schema (engine and schema). docs/pulse.md explains each.", + "anyOf": [ + { + "title": "wai.quantum.pulse/1", + "description": "A pulse answer as every host gives it (engine::pulse: pulse_schedule and pulse_check on both servers, wai-quantum pulse, POST /v1/pulse/*, the Studio): the request, and the result sealed in a job's form. A pulse program's schedule on frames, or a pulse job checked by running it again. A pulse run itself is a job (method pulse) and follows job-result.v1.json. A result names the engine that made it and this schema (engine and schema). docs/pulse.md explains each.", + "type": "object", + "required": [ + "request", + "result" + ], + "properties": { + "request": { + "type": "object", + "description": "The call's request, as given." + }, + "result": { + "description": "A schedule or a check, with its receipt.", + "anyOf": [ + { + "type": "object", + "description": "A pulse program's schedule (pulse_schedule, wai-quantum pulse schedule, POST /v1/pulse/schedule): its frames and ports, every instruction at its sample, each waveform's samples, the gate calls, the reader's notes, and the digests a run binds.", + "required": [ + "answer", + "dt_ns", + "duration", + "frames", + "waveforms", + "instructions", + "calls", + "calibration" + ], + "properties": { + "engine": { + "type": "object", + "description": "The engine that made the result: the wai-quantum crate's name and version.", + "required": [ + "name", + "version" + ], + "properties": { + "name": { + "const": "wai-quantum" + }, + "version": { + "type": "string" + } + } + }, + "schema": { + "const": "wai.quantum.pulse/1" + }, + "answer": { + "const": "schedule" + }, + "dt_ns": { + "type": "number", + "description": "The sample period, ns." + }, + "duration": { + "type": "integer", + "description": "The latest time of any frame, samples." + }, + "duration_ns": { + "type": "number" + }, + "grammar": { + "type": [ + "string", + "null" + ] + }, + "ports": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "qubit": { + "type": "integer" + } + } + } + }, + "frames": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "port": { + "type": "string" + }, + "qubit": { + "type": "integer" + }, + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "frequency_hz": { + "type": "number" + }, + "phase": { + "type": "number" + } + } + } + }, + "waveforms": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "length": { + "type": "integer" + }, + "samples": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 2 + } + }, + "samples_every": { + "type": "integer", + "description": "Each k-th sample is drawn, when the schedule holds more than the view writes out." + } + } + } + }, + "instructions": { + "type": "array", + "items": { + "type": "object", + "required": [ + "frame", + "op", + "start", + "length" + ], + "properties": { + "frame": { + "type": "integer" + }, + "op": { + "enum": [ + "play", + "delay", + "shift_phase", + "set_phase", + "shift_frequency", + "set_frequency", + "capture", + "barrier" + ] + }, + "start": { + "type": "integer" + }, + "length": { + "type": "integer" + }, + "line": { + "type": "integer" + }, + "waveform": { + "type": "integer" + }, + "phase": { + "type": "number" + }, + "frequency_hz": { + "type": "number" + }, + "carrier": { + "type": "number", + "description": "The carrier's phase at the play's first sample, cycles in [0, 1)." + }, + "value": { + "type": "number" + }, + "bit": { + "type": [ + "string", + "null" + ] + } + } + } + }, + "calls": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "qubits": { + "type": "array", + "items": { + "type": "integer" + } + }, + "params": { + "type": "array", + "items": { + "type": "number" + } + }, + "start": { + "type": "integer" + }, + "end": { + "type": "integer" + }, + "line": { + "type": "integer" + } + } + } + }, + "bits": { + "type": "array", + "items": { + "type": "string" + } + }, + "qubits": { + "type": "array", + "items": { + "type": "integer" + } + }, + "decimated": { + "type": "string" + }, + "diagnostics": { + "type": "array", + "items": { + "type": "object", + "description": "A reader's note or warning (qasm::diag).", + "properties": { + "code": { + "type": "string" + }, + "outcome": { + "type": "string" + }, + "message": { + "type": "string" + }, + "line": { + "type": "integer" + }, + "column": { + "type": "integer" + }, + "length": { + "type": "integer" + } + } + } + }, + "calibration": { + "type": "object", + "description": "What a pulse run's receipt binds of the program: the BLAKE3 of the control program's text, of its calibrations as written (every cal, defcal and defcalgrammar, joined by newlines), and of the schedule's JSON; a run adds the device's and the target snapshot's.", + "required": [ + "program", + "calibrations", + "schedule" + ], + "properties": { + "program": { + "type": "string" + }, + "calibrations": { + "type": "string" + }, + "schedule": { + "type": "string" + }, + "device": { + "type": "string" + }, + "target": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "calibration": { + "type": "object" + } + } + } + } + }, + "receipt": { + "title": "wai.quantum.receipt/2", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "type": "object", + "required": [ + "alg", + "canon", + "key", + "digest", + "signature" + ], + "properties": { + "alg": { + "const": "ed25519" + }, + "canon": { + "const": "rfc8785", + "description": "The canonical form the digest is taken over." + }, + "key": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "description": "The signing key, 32 bytes in hex." + }, + "digest": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "description": "The digest signed, 32 bytes in hex." + }, + "signature": { + "type": "string", + "minLength": 128, + "maxLength": 128, + "description": "The signature, 64 bytes in hex." + } + } + } + } + }, + { + "type": "object", + "description": "A pulse job checked (pulse_check, wai-quantum pulse check, POST /v1/pulse/check): its receipt's signature, its program's digest, and the request run again with the result held to the one given.", + "required": [ + "answer", + "verified", + "signature", + "program", + "reproduced", + "differences", + "statement" + ], + "properties": { + "engine": { + "type": "object", + "description": "The engine that made the result: the wai-quantum crate's name and version.", + "required": [ + "name", + "version" + ], + "properties": { + "name": { + "const": "wai-quantum" + }, + "version": { + "type": "string" + } + } + }, + "schema": { + "const": "wai.quantum.pulse/1" + }, + "answer": { + "const": "check" + }, + "verified": { + "type": "boolean" + }, + "signature": { + "type": "boolean" + }, + "key": {}, + "program": { + "type": "boolean" + }, + "reproduced": { + "type": "boolean" + }, + "differences": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Each field (or field.member) whose value differs from the run here." + }, + "density_hash": { + "type": "object", + "properties": { + "given": {}, + "recomputed": {} + } + }, + "calibration": {}, + "statement": { + "type": "string" + }, + "receipt": { + "title": "wai.quantum.receipt/2", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "type": "object", + "required": [ + "alg", + "canon", + "key", + "digest", + "signature" + ], + "properties": { + "alg": { + "const": "ed25519" + }, + "canon": { + "const": "rfc8785", + "description": "The canonical form the digest is taken over." + }, + "key": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "description": "The signing key, 32 bytes in hex." + }, + "digest": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "description": "The digest signed, 32 bytes in hex." + }, + "signature": { + "type": "string", + "minLength": 128, + "maxLength": 128, + "description": "The signature, 64 bytes in hex." + } + } + } + } + } + ] + } + } + }, + { + "type": "object", + "description": "A refusal as data: the message, every code in it (the last is the code), its outcome and meaning as the code's table gives them. A host that refuses for its own reasons gives the error alone.", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "code": { + "type": [ + "string", + "null" + ] + }, + "codes": { + "type": "array", + "items": { + "type": "string" + } + }, + "outcome": { + "enum": [ + "refused", + "retry", + "failed", + "lowered", + "carried", + "preserved", + "warned", + "dropped", + "defaulted" + ] + }, + "meaning": { + "type": "string" + } + } + } + ] + } } ] } diff --git a/wai/wai-quantum-service/smoke.sh b/wai/wai-quantum-service/smoke.sh index b6d5dce2c..411adb66f 100644 --- a/wai/wai-quantum-service/smoke.sh +++ b/wai/wai-quantum-service/smoke.sh @@ -13,7 +13,28 @@ U=${U%/} tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT -post() { curl -sS --max-time 120 -X POST "$U$1" -H 'content-type: application/json' --data-binary "@$2"; } +# A client may submit `q` jobs in a window (RateLimit-Policy), and the script +# makes more than a default window's 30. So before each POST it reads where it +# stands (RateLimit: `r` left, `t` seconds until one more is) from GET /v1/health, +# which submits nothing, and with none left it waits for one. CI starts the +# service with a larger WAI_QSVC_CLIENT_BURST, so it never waits there; against +# production it takes longer, and checks the same things. A dry run counts as a +# submission and then answers whether a real one would be taken now, so before +# one the script waits for two (`pace 2`). +pace() { + local need=${1:-1} h r t + for _ in $(seq 1 80); do + h=$(curl -sS --max-time 30 -D - -o /dev/null "$U/v1/health" | tr -d '\r' | sed -n 's/^[Rr]ate[Ll]imit: //p') + r=$(printf '%s' "$h" | sed -n 's/.*r=\([0-9]*\).*/\1/p') + t=$(printf '%s' "$h" | sed -n 's/.*t=\([0-9]*\).*/\1/p') + if [ -z "$r" ] || [ "$r" -ge "$need" ]; then return 0; fi + echo "pace: $r of the $need submissions needed left in this client's window; waiting ${t:-10} s" >&2 + sleep "$(( ${t:-10} < 1 ? 1 : ${t:-10} ))" + done + echo "FAIL: no submission came free in this client's window" >&2 + exit 1 +} +post() { pace; curl -sS --max-time 120 -X POST "$U$1" -H 'content-type: application/json' --data-binary "@$2"; } py() { python3 -I -c "$@"; } # POST /v1/run, then follow a job still queued or running past the service's # wait through GET /v1/jobs/{id}, as a client would: on a loaded host a job @@ -219,8 +240,9 @@ print("examples and openapi: ok,", len(e), "examples,", len(o["paths"]), "paths" # An agent over the Model Context Protocol: initialize, list the tools, run # the first example, and check its receipt. The run states its verdict, so the # request it seals (the job's own `request`) asks for one. -mcp() { curl -sS --max-time 120 -X POST "$U/mcp" -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' --data-binary "$1"; } +mcp() { pace; curl -sS --max-time 120 -X POST "$U/mcp" -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' --data-binary "$1"; } mcp '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"smoke","version":"1"}}}' > "$tmp/mcp-init.json" +pace code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 30 -X POST "$U/mcp" -H 'content-type: application/json' --data-binary '{"jsonrpc":"2.0","method":"notifications/initialized"}') mcp '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' > "$tmp/mcp-tools.json" py 'import json,sys @@ -241,16 +263,20 @@ req=json.load(open(sys.argv[2]))["examples"][0]["job"] assert job["request"]==dict(req, verdict=True) and job["verdict"]["level"]=="executed" and job["verdict"]==job["result"]["verdict"], job req=job["request"] print(json.dumps({"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"verify_quantum_receipt","arguments":{"request":req,"result":job["result"]}}}))' "$tmp/mcp-run.json" "$tmp/examples.json" > "$tmp/mcp-verify.json" +pace curl -sS --max-time 60 -X POST "$U/mcp" -H 'content-type: application/json' --data-binary "@$tmp/mcp-verify.json" > "$tmp/mcp-verified.json" py 'import json,sys v=json.load(open(sys.argv[1]))["result"]; assert v["structuredContent"]["valid"] is True, v print("mcp run + verify: ok,", json.load(open(sys.argv[2]))["result"]["content"][0]["text"].splitlines()[1])' "$tmp/mcp-verified.json" "$tmp/mcp-run.json" # A host that draws views: the session id that says so, the view's own tool kept from the model, and a page. +pace curl -sS -D "$tmp/mcp-views.h" --max-time 60 -X POST "$U/mcp" -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' \ --data-binary '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{"extensions":{"io.modelcontextprotocol/ui":{"mimeTypes":["text/html;profile=mcp-app"]}}}}}' > /dev/null sid=$(tr -d '\r' < "$tmp/mcp-views.h" | awk 'tolower($1)=="mcp-session-id:" {print $2}') case "$sid" in wq-views-*) ;; *) echo "no views session id: $sid"; exit 1 ;; esac +pace curl -sS --max-time 60 -X POST "$U/mcp" -H 'content-type: application/json' -H "mcp-session-id: $sid" --data-binary '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' > "$tmp/mcp-views-tools.json" +pace curl -sS --max-time 60 -X POST "$U/mcp" -H 'content-type: application/json' -H "mcp-session-id: $sid" --data-binary '{"jsonrpc":"2.0","id":3,"method":"resources/read","params":{"uri":"ui://wai-quantum/result"}}' > "$tmp/mcp-views-page.json" py 'import json,sys t={x["name"]: x for x in json.load(open(sys.argv[1]))["result"]["tools"]} @@ -328,6 +354,7 @@ print("device target: ok, placed on", p["layout"], "expected success", round(p[" # A refusal says why, what to write instead, and ends with its code. py 'import json; print(json.dumps({"qasm": "qubit[1] q; bit c; c = measure q; while (c) { x q; c = measure q; }"}))' > "$tmp/bad.json" +pace code=$(curl -sS -o "$tmp/bad.out" -w '%{http_code}' -X POST "$U/v1/jobs" -H 'content-type: application/json' --data-binary "@$tmp/bad.json") [ "$code" = 400 ] || { echo "a bad program got HTTP $code"; exit 1; } py 'import json,sys; d=json.load(open(sys.argv[1])); assert d["code"]=="Q310" and "@wai.max_iterations" in d["error"], d; print("refusal: ok,", d["error"])' "$tmp/bad.out" @@ -353,6 +380,7 @@ py 'import json,sys; d=json.load(open(sys.argv[1])); print(json.dumps({"source": post /v1/run "$tmp/fromqir.json" > "$tmp/fromqir.out" py 'import json,sys; d=json.load(open(sys.argv[1])); r=d["result"]; assert d["status"]=="done" and set(r["counts"]) <= {"00","11"} and d["request"]["source"]["format"]=="qir", d; print("a job from QIR: ok,", r["method"])' "$tmp/fromqir.out" py 'import json; print(json.dumps({"qasm": "qubit[1] q;", "thorough": True}))' > "$tmp/thorough.json" +pace code=$(curl -sS -o "$tmp/thorough.out" -w '%{http_code}' -X POST "$U/v1/shortcuts" -H 'content-type: application/json' --data-binary "@$tmp/thorough.json") [ "$code" = 422 ] || { echo "a thorough search got HTTP $code"; exit 1; } py 'import json,sys; d=json.load(open(sys.argv[1])); assert "wai-quantum shortcuts --thorough" in d["error"], d; print("thorough: ok, sent to the command line")' "$tmp/thorough.out" @@ -376,6 +404,7 @@ d=json.load(open(sys.argv[1])); r=d["result"] assert d["status"]=="done" and d["method"]=="exact" and r["accuracy"]["class"]=="bit-exact" and r["compiled"]["distance"] <= 1e-4, d print("compiled job: ok, bit-exact, within", r["compiled"]["distance"])' "$tmp/compiled-job.out" py 'import json; print(json.dumps({"qasm": "qubit[1] q;\nrz(0.3) q[0];\nh q[0];\nrz(0.4) q[0];\n", "epsilon": 1e-13}))' > "$tmp/fine.json" +pace code=$(curl -sS -o "$tmp/fine.out" -w '%{http_code}' -X POST "$U/v1/compile" -H 'content-type: application/json' --data-binary "@$tmp/fine.json") [ "$code" = 422 ] || { echo "a too-fine compile got HTTP $code"; exit 1; } py 'import json,sys; d=json.load(open(sys.argv[1])); assert "wai-quantum compile" in d["error"], d; print("fine compile: ok, sent to the command line")' "$tmp/fine.out" @@ -397,6 +426,7 @@ py 'import json; print(json.dumps({"family": "color", "distances": [3, 5], "p": post /v1/qec/threshold "$tmp/qec-threshold.json" > "$tmp/qec-threshold.out" py 'import json,sys; r=json.load(open(sys.argv[1]))["result"]; assert len(r["points"])==2 and r["below_threshold"][0]["distance_helps"], r; print("qec threshold: ok")' "$tmp/qec-threshold.out" py 'import json; print(json.dumps({"code": "bb:288"}))' > "$tmp/qec-far.json" +pace code=$(curl -sS -o "$tmp/qec-far.out" -w '%{http_code}' -X POST "$U/v1/qec/code" -H 'content-type: application/json' --data-binary "@$tmp/qec-far.json") [ "$code" = 422 ] || { echo "a long certification got HTTP $code"; exit 1; } py 'import json,sys; d=json.load(open(sys.argv[1])); assert "wai-quantum qec code bb:288" in d["error"], d; print("long certification: ok, sent to the command line")' "$tmp/qec-far.out" @@ -423,6 +453,7 @@ print("estimate: ok,", r["estimate"]["physical_qubits"], "physical qubits,", len post /v1/verify "$tmp/estimate.out" > "$tmp/estimate-verify.out" py 'import json,sys; v=json.load(open(sys.argv[1])); assert v["valid"] is True and v["reproduced"] is True and v["receipts"] is True, v; print("estimate verifies and re-runs: ok,", v["estimates"], "receipts")' "$tmp/estimate-verify.out" py 'import json; print(json.dumps({"counts": {"numQubits": 4, "rotationCount": 1000000, "rotationDepth": 10}, "budget": 1e-6}))' > "$tmp/fine-est.json" +pace code=$(curl -sS -o "$tmp/fine-est.out" -w '%{http_code}' -X POST "$U/v1/estimate" -H 'content-type: application/json' --data-binary "@$tmp/fine-est.json") [ "$code" = 422 ] || { echo "a too-fine estimate got HTTP $code"; exit 1; } py 'import json,sys; d=json.load(open(sys.argv[1])); assert "wai-quantum estimate" in d["error"], d; print("fine estimate: ok, sent to the command line")' "$tmp/fine-est.out" @@ -448,13 +479,13 @@ post /v1/repair "$tmp/proposal.json" | py 'import json,sys; d=json.load(sys.stdi py 'import json; print(json.dumps({"qasm": "OPENQASM 3.0;\nqubit[1] q;\nrx(0.3) q[0];\n", "method": "stabilizer"}))' > "$tmp/refused.json" post /v1/run "$tmp/refused.json" | py 'import json,sys; d=json.load(sys.stdin); assert d["error"].endswith("[J011]"), d; print("refusal: ok, ends with its code")' -# The whole script makes 29 submissions (30 on a host with a GPU), within the -# 30 a client may make in a window: a check added anywhere must take the place -# of one. What follows makes five. +# The script makes more submissions than a default window's 30; `pace` (above) +# keeps it within whatever window the service gives this client. # # A dry run: the plan, its estimates with their basis, the bounds, and whether # the service would take it; nothing runs. py 'import json; print(json.dumps({"qasm": "OPENQASM 3.0; qubit[30] q; h q[0]; cx q[0], q[29];", "shots": 100, "dry_run": True}))' > "$tmp/dry.json" +pace 2 post /v1/run "$tmp/dry.json" > "$tmp/dry.out" py 'import json,sys d=json.load(open(sys.argv[1])) @@ -513,6 +544,7 @@ print("sweep (mcp run_quantum_set): ok,", [round(i["result"]["expectations"][0][ # refusal comes with its code and the verdict of what was refused (the # program reads; the method named cannot take it). py 'import json; print(json.dumps({"jsonrpc":"2.0","id":7,"method":"tools/call","params":{"name":"dry_run_quantum_job","arguments":{"qasm":"OPENQASM 3.0; qubit[40] q; rx(0.3) q;","amplitudes":["0"*40],"method":"statevector"}}}))' > "$tmp/mcp-dry.json" +pace 2 mcp "@$tmp/mcp-dry.json" | py 'import json,sys; r=json.load(sys.stdin)["result"]; sc=r["structuredContent"]; assert not r["isError"] and sc["accepted"] is False and sc["refusal"]["code"]=="J011" and sc["verdict"]["level"]=="parsed" and sc["verdict"]["refusal"]["code"]=="J011", r; print("mcp dry run refused: ok,", r["content"][0]["text"][:60]+"…")' echo "smoke: every check held at $U" -- 2.54.0 (Apple Git-157) From 3cafd3f5322821023283a0f187c6971f8f53576a Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Fri, 30 Oct 2026 20:22:12 -0400 Subject: [PATCH 10/10] wai-quantum: the tests that need the dyadic QIR writer or the receipts built only with them Two of the other formats' tests write QIR 1 with crate::quantum_qir, which quantum_engine does not include, so the contract workflow's test build (quantum_engine with quantum_receipt, quantum_qec and quantum_noise) did not compile. They now carry #[cfg(feature = "quantum_qir")] and still run in the full build, where the formats workflow runs them. Two pulse tests seal their answers, so they carry #[cfg(feature = "quantum_receipt")], as error correction's sealing test does. Co-Authored-By: Claude Opus 5.5 --- wai/wai-quantum/src/engine/formats/tests.rs | 4 ++++ wai/wai-quantum/src/engine/pulse/tests.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/wai/wai-quantum/src/engine/formats/tests.rs b/wai/wai-quantum/src/engine/formats/tests.rs index 3241a9ec3..dfbc29422 100644 --- a/wai/wai-quantum/src/engine/formats/tests.rs +++ b/wai/wai-quantum/src/engine/formats/tests.rs @@ -169,6 +169,8 @@ fn every_intrinsic_reads_as_the_gate_it_names() { /// The module this crate wrote before QIR 2 (typed pointers, QIR 1.0) reads /// to the circuit it was written from, for every algorithm of the catalog. +// It writes QIR 1 with the dyadic writer, which `quantum_qir` holds. +#[cfg(feature = "quantum_qir")] #[test] fn qir_one_modules_with_typed_pointers_read() { use crate::quantum_toolchain as qt; @@ -814,6 +816,8 @@ fn formats_are_named_and_found() { /// OpenQASM export. With `WAI_FORMATS_VECTORS_WRITE=1` this writes them; /// otherwise it holds the files to them, so neither can drift. #[cfg(not(target_os = "wasi"))] +// It writes QIR 1 with the dyadic writer, which `quantum_qir` holds. +#[cfg(feature = "quantum_qir")] #[test] fn the_conformance_set_holds_these_modules() { use crate::quantum_toolchain as qt; diff --git a/wai/wai-quantum/src/engine/pulse/tests.rs b/wai/wai-quantum/src/engine/pulse/tests.rs index 4c581eba9..5e5ac1d38 100644 --- a/wai/wai-quantum/src/engine/pulse/tests.rs +++ b/wai/wai-quantum/src/engine/pulse/tests.rs @@ -122,6 +122,8 @@ fn what_a_pulse_job_cannot_be_is_refused_with_its_code() { } } +// It seals its answers, so it needs the receipts. +#[cfg(feature = "quantum_receipt")] #[test] fn a_pulse_job_is_checked_by_running_it_again() { let req = job_of(json!({})); @@ -149,6 +151,8 @@ fn a_pulse_job_is_checked_by_running_it_again() { ok(Registry::engine().check("pulse.v1.json", &seal(&body, call("check", &body).unwrap(), &key())), "the check"); } +// It seals its answers, so it needs the receipts. +#[cfg(feature = "quantum_receipt")] #[test] fn a_schedule_is_drawn_with_its_digests() { let req = json!({ "qasm": XM, "pulse": { "dt_ns": 0.5 } }); -- 2.54.0 (Apple Git-157)