//! The conformance vectors (`wai/quantum-conformance/wasmq/`) hold on every engine of this //! build, and are what this file makes: every committed vector is compared with the bytes //! made here, and every refusal is made beside the same module or bundle without its one //! defect, which is accepted. `WASMQ_BLESS=1` writes the files; without it the tests only //! read them. use serde_json::{Value, json}; use std::path::{Path, PathBuf}; use wai_quantum::quantum_qec::CssCode; use wai_wasmq::envelope::{Bundle, Callout, EXACT, EXACT_MAX_QUBITS, MAGIC, Module, Run, from_parts, pack, parts, write_unchecked}; use wai_wasmq::reference; use wai_wasmq::run::EngineKind; fn dir() -> PathBuf { Path::new(env!("CARGO_MANIFEST_DIR")).join("../quantum-conformance/wasmq") } fn wasm(wat: &str) -> Vec { wat::parse_str(wat).unwrap() } const PROGRAM: &str = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nextern f(bit[8]) -> bit[8];\nqubit[8] q;\nbit[8] b;\nbit[8] r;\nh q;\nb = measure q;\nr = f(b);\n"; const LIST: &str = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nextern f(bit[72]) -> bit[72];\nqubit[72] q;\nbit[72] b;\nbit[72] r;\nh q;\nb = measure q;\nr = f(b);\n"; const TWO: &str = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nextern f(bit[8]) -> bit[8];\nextern g(bit[8]) -> bit[8];\nqubit[8] q;\nbit[8] b;\nbit[8] r;\nh q;\nb = measure q;\nr = f(b);\nr = g(r);\n"; /// A reset whose outcome is random, then resets of qubits still in |0>, then measurements /// (§6.1): which of them take a value from the shot's stream. const RESETS: &str = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nextern f(bit[8]) -> bit[8];\nqubit[8] q;\nqubit a;\nbit[8] b;\nbit[8] r;\nh a;\nreset a;\nreset q;\nh q;\nb = measure q;\nr = f(b);\n"; const IDENTITY: &str = r#"(module (func (export "f") (param i64) (result i64) local.get 0))"#; const BOTH: &str = r#"(module (func (export "f") (param i64) (result i64) local.get 0) (func (export "g") (param i64) (result i64) local.get 0))"#; /// A list-form identity: the result is the argument's bytes where the host wrote them. const ECHO: &str = r#"(module (memory (export "memory") 1 1) (func (export "cabi_realloc") (param i32 i32 i32 i32) (result i32) i32.const 1024) (func (export "f") (param i32 i32) (result i32) i32.const 2048 local.get 0 i32.store i32.const 2052 local.get 1 i32.store i32.const 2048))"#; fn one(program: &str, module: Vec, run: Run) -> Bundle { Bundle { program: program.into(), inputs: Default::default(), modules: vec![Module { name: "m".into(), bytes: module, priors: vec![] }], callouts: vec![Callout { name: "f".into(), module: 0, export: "f".into() }], run } } fn runs() -> Vec<(&'static str, Bundle)> { let d = std::fs::read(dir().join("modules/qldpc_decoder.wasm")).unwrap(); let p = std::fs::read(dir().join("modules/atom_planner.wasm")).unwrap(); let s3 = CssCode::surface(3); let s5 = CssCode::surface(5); let g = CssCode::gross(); let mut job = Run::new(16, 77); job.state = "job".into(); let mut tight = Run::new(32, 9); tight.per_call = 400; let spin = r#"(module (func (export "f") (param i64) (result i64) (local i64) block loop local.get 0 i64.eqz br_if 1 local.get 0 i64.const 1 i64.sub local.set 0 br 0 end end local.get 1))"#; let deep = r#"(module (func $r (param i64) (result i64) local.get 0 i64.const 1 i64.add call $r) (func (export "f") (param i64) (result i64) local.get 0 call $r))"#; let nan = r#"(module (func (export "f") (param i64) (result i64) f64.const nan:0x4000000000abc local.get 0 f64.convert_i64_u f64.add i64.reinterpret_f64 i64.const 255 i64.and))"#; let grow = r#"(module (memory 1 3) (func (export "f") (param i64) (result i64) local.get 0 i32.wrap_i64 i32.const 7 i32.and memory.grow i64.extend_i32_s i64.const 255 i64.and))"#; let divide = r#"(module (func (export "f") (param i64) (result i64) i64.const 1 local.get 0 i64.const 1 i64.and i64.div_u))"#; let wide = r#"(module (func (export "f") (param i64) (result i64) local.get 0 i64.const 256 i64.or))"#; vec![ ("surface3-decode64.wai", reference::qec_bundle(&d, s3.n, &s3.checks, 1, Run::new(32, 1234))), ("surface5-decode64-job.wai", reference::qec_bundle(&d, s5.n, &s5.checks, 2, job)), ("gross-decode.wai", reference::qec_bundle(&d, g.n, &g.checks, 3, Run::new(4, 5))), ("atoms4x4-plan64.wai", reference::atom_bundle(&p, 4, 4, 2, 2, Run::new(32, 99))), ("atoms9x9-plan.wai", reference::atom_bundle(&p, 9, 9, 3, 3, Run::new(6, 99))), ("list-echo.wai", one(LIST, wasm(ECHO), Run::new(8, 3))), ("reset-draws.wai", one(RESETS, wasm(IDENTITY), Run::new(64, 11))), ("nan-canonical.wai", one(PROGRAM, wasm(nan), Run::new(8, 2))), ("grow-past-maximum.wai", one(PROGRAM, wasm(grow), Run::new(16, 5))), ("fault-fuel.wai", one(PROGRAM, wasm(spin), tight)), ("fault-stack.wai", one(PROGRAM, wasm(deep), Run::new(2, 1))), ("fault-trap.wai", one(PROGRAM, wasm(divide), Run::new(64, 4))), ("fault-bad-result.wai", one(PROGRAM, wasm(wide), Run::new(2, 1))), ] .into_iter() .chain(more_runs()) .chain(second_runs()) .collect() } /// The profile's second version (§11): a T gate and a Toffoli by the `exact` method; a /// rotation a module computes, applied as its bits' phases; angle arguments, one known on /// reading and one from measured bits; the tableau under the second version's names; the /// state vector given a Clifford program, whose draws are the tableau's; and a reset by the /// state vector, which pins its stream. fn second_runs() -> Vec<(&'static str, Bundle)> { let exact = |shots: u64, seed: u64| { let mut r = Run::second(shots, seed); r.method = EXACT.into(); r }; let t_toffoli = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nextern f(bit[3]) -> bit[3];\nqubit[3] q;\nbit[3] b;\nbit[3] r;\nh q[0];\nt q[0];\nh q[0];\nx q[1];\nccx q[0], q[1], q[2];\nb = measure q;\nr = f(b);\n"; let feedback = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nextern f(bit[1]) -> bit[3];\nqubit[2] q;\nbit[1] m;\nbit[3] th;\nbit[1] out;\nh q[0];\nm[0] = measure q[0];\nth = f(m);\nh q[1];\nif (th[0]) p(pi / 4) q[1];\nif (th[1]) p(pi / 2) q[1];\nif (th[2]) p(pi) q[1];\nh q[1];\nout[0] = measure q[1];\n"; let thrice = wasm(r#"(module (func (export "f") (param i64) (result i64) local.get 0 i64.const 3 i64.mul))"#); let angles = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nextern f(angle[8], uint[8]) -> bit[16];\nextern g(angle[4]) -> bit[4];\nqubit[4] q;\nbit[4] m;\nbit[16] r;\nbit[4] s;\nh q;\nm = measure q;\nr = f(pi / 4, 3);\ns = g(m);\n"; let mut angle = one(angles, wasm(r#"(module (func (export "f") (param i64 i64) (result i64) local.get 0 i64.const 8 i64.shl local.get 1 i64.or) (func (export "g") (param i64) (result i64) local.get 0))"#), Run::second(16, 3)); angle.callouts.push(Callout { name: "g".into(), module: 0, export: "g".into() }); // A reset whose outcome is random, at an angle the state vector reads at three sixteenths // of a turn from the stream, then rotations of every kind the method takes. let rotations = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nextern f(bit[4]) -> bit[4];\nqubit[4] q;\nbit[4] b;\nbit[4] r;\nrx(3 * pi / 8) q[0];\nreset q[0];\nry(pi / 8) q[0];\nrz(pi / 16) q[0];\nh q[1];\ncp(pi / 2) q[1], q[2];\ncrz(pi / 4) q[1], q[3];\nrzz(pi / 8) q[2], q[3];\nch q[0], q[3];\ncswap q[1], q[2], q[3];\nccz q[0], q[1], q[2];\ntdg q[2];\nsx q[3];\nb = measure q;\nr = f(b);\n"; vec![ ("v2-t-toffoli.wai", one(t_toffoli, wasm(IDENTITY), Run::second(64, 5))), ("v2-feedback-rotation.wai", one(feedback, thrice, Run::second(64, 9))), ("v2-angle-arguments.wai", angle), ("v2-stabilizer.wai", one(PROGRAM, wasm(IDENTITY), Run::second(8, 2))), ("v2-exact-of-a-clifford-program.wai", one(PROGRAM, wasm(IDENTITY), exact(8, 2))), ("v2-rotations-and-a-reset.wai", one(rotations, wasm(IDENTITY), Run::second(64, 13))), ] } /// A module of one export `f` of `(i64) -> i64`, its body `body`, with `extra` beside it. fn scalar(extra: &str, body: &str) -> Vec { wasm(&format!(r#"(module {extra} (func (export "f") (param i64) (result i64) {body}))"#)) } /// The list-form identity of `LIST` with its result changed: `ret` the address it returns, /// and `after` what it does to the result's bytes before it returns. fn echo_with(ret: u32, after: &str) -> Vec { wasm(&format!( r#"(module (memory (export "memory") 1 1) (func (export "cabi_realloc") (param i32 i32 i32 i32) (result i32) i32.const 1024) (func (export "f") (param i32 i32) (result i32) i32.const 2048 local.get 0 i32.store i32.const 2052 local.get 1 i32.store {after} i32.const {ret}))"# )) } /// Runs that show what the rest of the text says a run does (§5, §6): every reason a fault /// has, every phase and every budget it stops in, the calls a host makes into a module, the /// lowering of a signed argument, the order modules are made in, and a program that /// measures nothing. fn more_runs() -> Vec<(&'static str, Bundle)> { let mut out = Vec::new(); // Each trap, as a fault of its own reason. let ty = "(type $t (func (param i64) (result i64)))"; for (name, extra, body) in [ ("fault-unreachable.wai", "", "unreachable"), ("fault-memory-out-of-bounds.wai", "(memory 1 1)", "i32.const 70000 i64.load"), ("fault-table-out-of-bounds.wai", &*format!("{ty} (table 1 1 funcref)"), "local.get 0 i32.const 5 call_indirect (type $t)"), ("fault-indirect-call-null.wai", &*format!("{ty} (table 1 1 funcref)"), "local.get 0 i32.const 0 call_indirect (type $t)"), ("fault-indirect-call-type.wai", &*format!("{ty} (table 1 1 funcref) (func $g) (elem (i32.const 0) func $g)"), "local.get 0 i32.const 0 call_indirect (type $t)"), ("fault-integer-overflow.wai", "", "i64.const 0x8000000000000000 i64.const -1 i64.div_s"), ("fault-invalid-conversion.wai", "", "f64.const nan i64.trunc_f64_s"), ] { out.push((name, one(PROGRAM, scalar(extra, body), Run::new(4, 1)))); } // A fault in each phase of making an instance (§5.3). out.push(("fault-in-seed.wai", one(PROGRAM, scalar(r#"(func (export "wasmq-seed") (param i64) unreachable)"#, "local.get 0"), Run::new(4, 1)))); out.push(("fault-in-init.wai", one(PROGRAM, scalar(r#"(func (export "init") unreachable)"#, "local.get 0"), Run::new(4, 1)))); let mut prior = one( PROGRAM, scalar(r#"(memory (export "memory") 1 1) (func (export "cabi_realloc") (param i32 i32 i32 i32) (result i32) i32.const 1024) (func (export "wasmq-prior") (param i32 i32 i32) unreachable)"#, "local.get 0"), Run::new(4, 1), ); prior.modules[0].priors.push(("weights".into(), vec![1, 2, 3, 4, 5])); out.push(("fault-in-prior.wai", prior)); // Each budget a fuel fault can be of (§4.2): an instance's, and what is left of the run's. let mut instance = Run::new(4, 1); instance.per_init = 300; out.push(("fault-fuel-instance.wai", one(PROGRAM, scalar(r#"(func (export "init") loop br 0 end)"#, "local.get 0"), instance))); let mut run = Run::new(8, 1); run.per_job = 50; let sum = "local.get 0 ".to_string() + &"local.get 0 i64.add ".repeat(10) + "drop local.get 0"; out.push(("fault-fuel-run.wai", one(PROGRAM, scalar("", &sum), run))); // The calls a host makes into a module around a list (§5.2): `cabi_post_f` after the // result is read, which uses fuel of the call's. let post = wasm( r#"(module (memory (export "memory") 1 1) (func (export "cabi_realloc") (param i32 i32 i32 i32) (result i32) i32.const 1024) (func (export "f") (param i32 i32) (result i32) i32.const 2048 local.get 0 i32.store i32.const 2052 local.get 1 i32.store i32.const 2048) (func (export "cabi_post_f") (param i32) (local i32) loop local.get 1 i32.const 1 i32.add local.tee 1 i32.const 5 i32.lt_u br_if 0 end))"#, ); out.push(("list-post.wai", one(LIST, post, Run::new(8, 3)))); // Each way a list result is not one (§5.2). for (name, m) in [ // The pointer and length written at 2050, so that only the address's alignment is wrong. ("fault-list-misaligned.wai", echo_with(2050, "i32.const 2050 local.get 0 i32.store i32.const 2054 local.get 1 i32.store")), ("fault-list-outside.wai", echo_with(70000, "")), ("fault-list-length.wai", echo_with(2048, "i32.const 2052 i32.const 5 i32.store")), ] { out.push((name, one(LIST, m, Run::new(4, 3)))); } // Seventy bits take nine bytes, the last two bits of the last byte zero. let seventy = LIST.replace("72", "70"); out.push(("fault-list-past-its-bits.wai", one(&seventy, echo_with(2048, "i32.const 1032 i32.const 255 i32.store8"), Run::new(4, 3)))); // A signed argument, sign-extended (§5.2): `f(-3, 200)` gives back its two arguments. let signed = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nextern f(int[8], uint[8]) -> bit[64];\nqubit[1] q;\nbit[64] r;\nr = f(-3, 200);\n"; out.push(("signed-argument.wai", one(signed, wasm(r#"(module (func (export "f") (param i64 i64) (result i64) local.get 0 i64.const 8 i64.shl local.get 1 i64.or))"#), Run::new(2, 1)))); // A program that measures nothing: its outcome is its bits, which its call writes, and // no qubit is read for it (§6.1). let unmeasured = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nextern f(bit[1]) -> bit[1];\nqubit[3] q;\nbit[1] d;\nd = f(d);\nh q;\n"; out.push(("measures-nothing.wai", one(unmeasured, wasm(IDENTITY), Run::new(16, 7)))); // Bits nothing writes are in the outcome all the same, as 0 (§6.1): `a`'s three, then // the call's two, so every outcome is `11000`. let unwritten = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nextern f(uint[8]) -> bit[2];\nqubit[1] q;\nbit[3] a;\nbit[2] r;\nr = f(3);\n"; out.push(("bits-nothing-writes.wai", one(unwritten, wasm(IDENTITY), Run::new(2, 7)))); // A block's own bit is no bit of the outcome (§6.1, Q322): the loop's `t` is measured and // read, and the outcome is `a` and `b` alone, `11`. let block = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nextern f(bit[1]) -> bit[1];\nqubit[1] q;\nbit[1] a;\nx q[0];\nfor uint i in [0:0] {\n bit t;\n t = measure q[0];\n if (t) { a = measure q[0]; }\n}\nbit[1] b;\nb = f(a);\n"; out.push(("block-bit-not-in-outcome.wai", one(block, wasm(IDENTITY), Run::new(2, 7)))); // Two modules, each failing in its init: they are made in the contract's order (§5.3). let fails = |m: &str| wasm(&format!(r#"(module (func (export "init") unreachable) (func (export "{m}") (param i64) (result i64) local.get 0))"#)); let mut two = one(TWO, fails("f"), Run::new(4, 1)); two.modules[0].name = "first".into(); two.modules.push(Module { name: "second".into(), bytes: fails("g"), priors: vec![] }); two.callouts = vec![Callout { name: "f".into(), module: 0, export: "f".into() }, Callout { name: "g".into(), module: 1, export: "g".into() }]; out.push(("two-modules-in-order.wai", two)); // A program that declares no qubit and names physical ones: its qubits are one more than // the highest index it names (§2.2), here 3; and its bits are numbered in the order it // declares them (§6.1), so the measured `b[1]` is bit 2 and every outcome is `100`. let physical = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nextern f(bit[1]) -> bit[1];\nbit[1] a;\nbit[2] b;\nx $2;\nb[1] = measure $2;\na = f(a);\n"; out.push(("physical-qubits.wai", one(physical, wasm(IDENTITY), Run::new(4, 7)))); out } fn leb(mut v: u64) -> Vec { let mut out = Vec::new(); loop { let b = (v & 0x7f) as u8; v >>= 7; if v == 0 { out.push(b); return out; } out.push(b | 0x80); } } fn raw(sections: &[(u8, Vec)]) -> Vec { let mut m = b"\0asm\x01\0\0\0".to_vec(); for (id, body) in sections { m.push(*id); m.extend(leb(body.len() as u64)); m.extend_from_slice(body); } m } /// A module a runner refuses: its name, its code, the module, the same module without /// the defect, and its text when it has one. struct Refused { name: &'static str, code: &'static str, module: Vec, repaired: Vec, wat: Option, } fn module_refusals() -> Vec { let f = r#"(func (export "f") (param i64) (result i64) local.get 0)"#; let text = |name: &'static str, code: &'static str, defect: String, repaired: String| Refused { name, code, module: wasm(&defect), repaired: wasm(&repaired), wat: Some(defect) }; let with = |name: &'static str, code: &'static str, defect: &str, repaired: &str| text(name, code, format!("(module {defect} {f})"), format!("(module {repaired} {f})")); let body = |name: &'static str, code: &'static str, defect: &str, repaired: &str| { text(name, code, format!(r#"(module (memory 1 1) (table 1 funcref) (func (export "f") (param i64) (result i64) {defect}))"#), format!(r#"(module (memory 1 1) (table 1 funcref) (func (export "f") (param i64) (result i64) {repaired}))"#)) }; let exports = |n: u32| format!("(module (func) {})", (0..n).map(|k| format!("(export \"e{k}\" (func 0)) ")).collect::()); let locals = |n: usize| format!("(module (func (local {})))", "i64 ".repeat(n)); // One type `() -> ()`, one function of it, and a body: `i32.const 0`, `drop`, and an `end` or none. let unfinished = |end: bool| { let mut code = vec![0x01, if end { 0x05 } else { 0x04 }, 0x00, 0x41, 0x00, 0x1a]; if end { code.push(0x0b); } raw(&[(1, vec![0x01, 0x60, 0x00, 0x00]), (3, vec![0x01, 0x00]), (10, code)]) }; // One type, and one function of type `index`. let typed = |index: u8| raw(&[(1, vec![0x01, 0x60, 0x00, 0x00]), (3, vec![0x01, index]), (10, vec![0x01, 0x02, 0x00, 0x0b])]); vec![ Refused { name: "body-without-end", code: "W001", module: unfinished(false), repaired: unfinished(true), wat: None }, Refused { name: "function-of-no-type", code: "W001", module: typed(5), repaired: typed(0), wat: None }, with("import-clock", "W002", r#"(import "wasi:clocks/monotonic-clock" "now" (func (result i64)))"#, ""), with("import-random", "W002", r#"(import "wasi_snapshot_preview1" "random_get" (func (param i32 i32) (result i32)))"#, ""), with("memory-unbounded", "W003", "(memory 1)", "(memory 1 1)"), with("memory-too-large", "W003", "(memory 1 4097)", "(memory 1 4096)"), with("start-function", "W004", "(func $s) (start $s)", "(func $s)"), with("shared-memory", "W005", "(memory 1 1 shared)", "(memory 1 1)"), body("atomic", "W005", "i32.const 0 local.get 0 i64.atomic.rmw.add", "i32.const 0 i64.load local.get 0 i64.add"), body("simd", "W006", "local.get 0 i64x2.splat i64x2.extract_lane 0", "local.get 0"), body("relaxed-simd", "W006", "local.get 0 i64x2.splat local.get 0 i64x2.splat local.get 0 i64x2.splat f32x4.relaxed_madd i64x2.extract_lane 0", "local.get 0"), with("tail-call", "W007", "(func $g (param i64) (result i64) local.get 0) (func (param i64) (result i64) local.get 0 return_call $g)", "(func $g (param i64) (result i64) local.get 0) (func (param i64) (result i64) local.get 0 call $g)"), body("ref-null", "W007", "ref.null func ref.is_null i64.extend_i32_u", "i32.const 1 i64.extend_i32_u"), with("reference-type", "W007", "(func (param externref))", "(func (param i64))"), body("table-grow", "W008", "unreachable table.grow 0 i64.extend_i32_u", "unreachable i64.extend_i32_u"), body("table-size", "W008", "table.size 0 i64.extend_i32_u", "memory.size i64.extend_i32_u"), text("frame-too-large", "W009", locals(4097), locals(4096)), text("too-many-exports", "W010", exports(1025), exports(1024)), with("data-past-memory", "W011", r#"(memory 1 1) (data (i32.const 65530) "0123456789")"#, r#"(memory 1 1) (data (i32.const 65526) "0123456789")"#), with("reserved-export", "W012", r#"(global (export "__wasmq_fuel") (mut i64) (i64.const 0))"#, r#"(global (export "fuel") (mut i64) (i64.const 0))"#), with("seed-of-another-type", "W012", r#"(func (export "wasmq-seed") (param i32))"#, r#"(func (export "wasmq-seed") (param i64))"#), ] } /// A bundle that is well formed and breaks a rule: its name, its code, the bundle, and /// the same bundle without the defect. fn bundle_refusals() -> Vec<(&'static str, &'static str, Bundle, Bundle)> { let good = || one(PROGRAM, wasm(IDENTITY), Run::new(4, 1)); let two = || { let mut b = one(TWO, wasm(BOTH), Run::new(4, 1)); b.callouts.push(Callout { name: "g".into(), module: 0, export: "g".into() }); b }; let mut out = Vec::new(); let mut add = |name: &'static str, code: &'static str, defect: Bundle, repaired: Bundle| out.push((name, code, defect, repaired)); let mut swapped = two(); swapped.callouts.reverse(); add("callouts-out-of-order", "W015", swapped, two()); let mut twice = good(); twice.callouts.push(Callout { name: "f".into(), module: 0, export: "f".into() }); add("two-callouts-for-one-extern", "W015", twice, good()); add("callout-of-another-type", "W015", one(PROGRAM, wasm(r#"(module (func (export "f") (param i32) (result i32) local.get 0))"#), Run::new(4, 1)), good()); add("memory-not-exported", "W015", one(LIST, wasm(&ECHO.replace("(memory (export \"memory\") 1 1)", "(memory 1 1)")), Run::new(4, 1)), one(LIST, wasm(ECHO), Run::new(4, 1))); add("gate-outside-the-clifford-gates", "W016", one(&PROGRAM.replace("h q;", "h q;\nt q[0];"), wasm(IDENTITY), Run::new(4, 1)), good()); add("program-calls-nothing", "W016", one(&PROGRAM.replace("r = f(b);", "r = measure q;"), wasm(IDENTITY), Run::new(4, 1)), good()); // A measurement that names no bit, in a program that calls externs (§6.1, Q324): its // outcome is the bits it declares, and this one declares none for it. let bare = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nextern f(uint[8]);\nqubit[4] q;\nf(1);\nx q[3];\nmeasure q[3];\n"; let named = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nextern f(uint[8]);\nqubit[4] q;\nbit[4] c;\nf(1);\nx q[3];\nc[3] = measure q[3];\n"; let sink = || wasm(r#"(module (func (export "f") (param i64)))"#); add("program-measure-with-no-bit", "W016", one(bare, sink(), Run::new(2, 7)), one(named, sink(), Run::new(2, 7))); add("no-shots", "W017", one(PROGRAM, wasm(IDENTITY), Run::new(0, 1)), good()); let mut state = good(); state.run.state = "forever".into(); add("instances-of-no-kind", "W017", state, good()); let mut seed = good(); seed.run.seed = 1 << 53; add("seed-a-double-cannot-hold", "W017", seed, good()); // What a host must hold and do that fuel does not count (§4.7, §4.8). let of_memory = |pages: u32| { let m = format!(r#"(module (memory (export "memory") {pages} {pages}) (func (export "f") (param i64) (result i64) local.get 0) (func (export "g") (param i64) (result i64) local.get 0))"#); let mut b = one(TWO, wasm(&m), Run::new(4, 1)); b.modules.push(Module { name: "n".into(), bytes: wasm(&m), priors: vec![] }); b.callouts = vec![Callout { name: "f".into(), module: 0, export: "f".into() }, Callout { name: "g".into(), module: 1, export: "g".into() }]; b }; add("memory-past-a-run-s-among-its-modules", "W003", of_memory(2049), of_memory(2048)); // The most bits a program holds, a shot more than results may hold. let wide = PROGRAM.replace("bit[8] r;", &format!("bit[8] r;\nbit[{}] pad;", (1 << 20) - 16)); add("results-past-what-a-host-holds", "W017", one(&wide, wasm(IDENTITY), Run::new((1 << 8) + 1, 1)), one(&wide, wasm(IDENTITY), Run::new(1 << 8, 1))); let measures = PROGRAM.replace("qubit[8] q;", "qubit[32768] q;").replace("h q;\nb = measure q;", "b[0] = measure q[0];"); // Eight gates, eight measurements and a call, and resets up to the steps a program takes. let resets = |n: u64| PROGRAM.replace("r = f(b);", &format!("r = f(b);\nfor uint i in [0:{}] {{ reset q[0]; }}", n - 1)); add("program-past-its-steps", "W016", one(&resets((1 << 21) - 16), wasm(IDENTITY), Run::new(1, 1)), one(&resets((1 << 21) - 17), wasm(IDENTITY), Run::new(1, 1))); add("work-past-what-a-run-may-ask", "W017", one(&measures, wasm(IDENTITY), Run::new(1 << 20, 1)), one(&measures, wasm(IDENTITY), Run::new(4, 1))); // The second version (§11): what the `exact` method does not take, and angle arguments // the first version does not take or that are not whole numbers of their turns. let exact = |p: &str, shots: u64| { let mut r = Run::second(shots, 1); r.method = EXACT.into(); one(p, wasm(IDENTITY), r) }; let with = |gate: &str| PROGRAM.replace("h q;", &format!("h q;\n{gate}")); add("v2-angle-not-dyadic", "W016", exact(&with("rz(0.1) q[0];"), 4), exact(&with("rz(pi / 8) q[0];"), 4)); add("v2-angle-off-the-double-s-rounding", "W016", exact(&with("rz(pi / 4 + 2e-14) q[0];"), 4), exact(&with("rz(pi / 4) q[0];"), 4)); add("v2-gate-outside-the-exact-gates", "W016", exact(&with("u(pi / 2, 0, pi) q[0];"), 4), exact(&with("ry(pi / 2) q[0];"), 4)); add("v2-t-gate-by-the-stabilizer-method", "W016", one(&with("t q[0];"), wasm(IDENTITY), Run { method: "stabilizer".into(), ..Run::second(4, 1) }), exact(&with("t q[0];"), 4)); let wide = |n: u32| PROGRAM.replace("qubit[8] q;", &format!("qubit[{n}] q;")).replace("h q;", "t q[0];").replace("b = measure q;", "b = measure q[0:7];"); add("v2-past-the-exact-method-s-qubits", "W016", exact(&wide(EXACT_MAX_QUBITS + 1), 4), exact(&wide(EXACT_MAX_QUBITS), 4)); // 2^20 shots of a 20-qubit state: 2^41 operations for its fresh states alone, and // thirty-seven times that with its gate and its eight measurements, past 2^42 (§11.7). add("v2-state-vector-work-past-what-a-run-may-ask", "W017", exact(&wide(20), 1 << 20), exact(&wide(20), 1 << 10)); let angled = |arg: &str| format!("OPENQASM 3.0;\ninclude \"stdgates.inc\";\nextern f(angle[2]) -> bit[2];\nqubit[1] q;\nbit[2] r;\nr = f({arg});\n"); add("v2-angle-argument-off-its-turns", "W016", one(&angled("pi / 4"), wasm(IDENTITY), Run::second(4, 1)), one(&angled("pi / 2"), wasm(IDENTITY), Run::second(4, 1))); add("v1-angle-argument", "W016", one(&angled("pi / 2"), wasm(IDENTITY), Run::new(4, 1)), one(&angled("pi / 2"), wasm(IDENTITY), Run::second(4, 1))); out } /// Envelopes one edit away from one that opens: each name, its code, and its bytes. fn edited(base: &[u8]) -> Vec<(&'static str, &'static str, Vec)> { let (manifest, contract, frames) = parts(base).unwrap(); let text = String::from_utf8(contract.clone()).unwrap(); let with = |manifest: &[u8], magic: &[u8; 4], contract: &[u8], frames: &[u8]| from_parts(manifest, magic, contract, frames).unwrap(); let flip = |find: &[u8], with: &[u8]| { let at = base.windows(find.len()).position(|w| w == find).unwrap(); let mut e = base.to_vec(); e[at..at + with.len()].copy_from_slice(with); e }; let prior_at = base.windows(15).position(|w| w == b"wai:qec-checks\x01").unwrap(); let mut wrong_prior = base.to_vec(); wrong_prior[prior_at + 15 + 8 + 4] ^= 1; let module_at = base.windows(8).position(|w| w == b"\0asm\x01\0\0\0").unwrap(); let mut wrong_module = base.to_vec(); wrong_module[module_at + 200] ^= 0x40; let mut trailing = base.to_vec(); trailing.push(0); let mut more = frames.clone(); more.push(0); let shots = text.replacen("\"shots\":32", "\"shots\":33", 1); let extra = text.replacen(",\"modules\":", ",\"extra\":0,\"modules\":", 1); assert!(shots != text && extra != text); let m = String::from_utf8(manifest.clone()).unwrap(); vec![ ("prior-hash-mismatch", "W014", wrong_prior), ("module-hash-mismatch", "W014", wrong_module), ("program-hash-mismatch", "W014", flip(b"h c0;", b"x c0;")), ("contract-run-changed", "W013", with(&manifest, MAGIC, shots.as_bytes(), &frames)), ("contract-extra-member", "W013", with(&manifest, MAGIC, extra.as_bytes(), &frames)), ("contract-not-canonical", "W013", with(&manifest, MAGIC, text.replacen('{', "{ ", 1).as_bytes(), &frames)), ("payload-of-another-kind", "W013", with(&manifest, b"WQM2", &contract, &frames)), ("byte-after-the-frames", "W013", with(&manifest, MAGIC, &contract, &more)), ("manifest-changed", "W013", with(m.replace("\"shots\":32", "\"shots\":33").as_bytes(), MAGIC, &contract, &frames)), ("other-capability", "W013", with(m.replace("wai.quantum.wasmq", "wai.quantum.wasmx").as_bytes(), MAGIC, &contract, &frames)), ("trailing-byte", "W013", trailing), ("truncated", "W013", base[..base.len() - 1].to_vec()), ] } /// Every vector file this test makes, by its path under the vectors' directory, and the /// index that lists them. fn made() -> (Vec<(String, Vec)>, Value) { let key = wai_wasmq::vectors::key(); let mut files = Vec::new(); let mut index_runs = Vec::new(); let mut base = Vec::new(); for (name, b) in runs() { let env = pack(&b).unwrap_or_else(|e| panic!("{name}: {e}")); let o = wai_wasmq::open(&env).unwrap(); let (body, _) = wai_wasmq::run(&o, EngineKind::Interpreter, &key, false).unwrap(); index_runs.push(json!({ "file": format!("envelopes/{name}"), "envelope_blake3": wai_wasmq::envelope::blake3_hex(&env), "expect": wai_wasmq::vectors::pins(&body) })); if name == "surface3-decode64.wai" { files.push(("envelopes/surface3-decode64.job.json".to_string(), (serde_json::to_string_pretty(&body).unwrap() + "\n").into_bytes())); files.push(("envelopes/surface3-decode64.qasm".to_string(), b.program.clone().into_bytes())); files.push(("envelopes/surface3-decode64.checks.bin".to_string(), b.modules[0].priors[0].1.clone())); base = env.clone(); } files.push((format!("envelopes/{name}"), env)); } let mut index_refuse = Vec::new(); for r in module_refusals() { if let Some(wat) = &r.wat { files.push((format!("refuse/{}.wat", r.name), format!("{wat}\n").into_bytes())); } files.push((format!("refuse/{}.wasm", r.name), r.module)); index_refuse.push(json!({ "file": format!("refuse/{}.wasm", r.name), "code": r.code })); } for (name, code, defect, _) in bundle_refusals() { let qubits = wai_quantum::qasm::read_callouts(&defect.program, &defect.inputs).map(|h| h.reading.program.n_qubits).unwrap_or(8); files.push((format!("refuse/{name}.wai"), write_unchecked(&defect, method_of(&defect), qubits).unwrap())); index_refuse.push(json!({ "file": format!("refuse/{name}.wai"), "code": code })); } for (name, code, bytes) in edited(&base) { files.push((format!("refuse/{name}.wai"), bytes)); index_refuse.push(json!({ "file": format!("refuse/{name}.wai"), "code": code })); } let index = json!({ "profiles": [wai_wasmq::profile::PROFILE, wai_wasmq::envelope::PROFILE2], "note": "every run vector gives exactly `expect` on every engine and every host; every refusal is refused with its code (extensions/quantum-wasmq.md §9)", "runs": index_runs, "refuse": index_refuse, }); (files, index) } /// The method a refused bundle's envelope is written with: the one its run asks, or the /// first version's. fn method_of(b: &Bundle) -> &str { if b.run.method.is_empty() { wai_wasmq::envelope::METHOD } else { &b.run.method } } fn kinds() -> Vec { wai_wasmq::engine::available().iter().map(|s| EngineKind::parse(s).unwrap()).collect() } #[test] fn the_vectors_are_the_ones_made_here_and_hold_on_every_engine_of_this_build() { let d = dir(); let (files, index) = made(); let index_text = serde_json::to_string_pretty(&index).unwrap() + "\n"; if std::env::var("WASMQ_BLESS").is_ok() { for sub in ["envelopes", "refuse"] { let _ = std::fs::remove_dir_all(d.join(sub)); std::fs::create_dir_all(d.join(sub)).unwrap(); } for (path, bytes) in &files { std::fs::write(d.join(path), bytes).unwrap(); } std::fs::write(d.join("vectors.json"), &index_text).unwrap(); } // The committed files are these, and there are no others. for (path, bytes) in &files { assert!(std::fs::read(d.join(path)).is_ok_and(|have| &have == bytes), "{path} is not the file this test makes: run it with WASMQ_BLESS=1"); } assert_eq!(std::fs::read_to_string(d.join("vectors.json")).unwrap(), index_text, "vectors.json is not the index this test makes"); for sub in ["envelopes", "refuse"] { for entry in std::fs::read_dir(d.join(sub)).unwrap() { let name = format!("{sub}/{}", entry.unwrap().file_name().to_string_lossy()); assert!(files.iter().any(|(p, _)| *p == name), "{name} is a file this test does not make"); } } let (lines, problems) = wai_wasmq::vectors::verify(&d, &kinds()).unwrap(); assert!(problems.is_empty(), "{problems:#?}"); assert_eq!(lines.len(), index["runs"].as_array().unwrap().len() + index["refuse"].as_array().unwrap().len()); // Every code of §3.3 has a vector. let codes: std::collections::BTreeSet<&str> = index["refuse"].as_array().unwrap().iter().filter_map(|r| r["code"].as_str()).collect(); let all: std::collections::BTreeSet<&str> = wai_wasmq::profile::CODES.iter().map(|c| c.0).collect(); assert_eq!(codes, all, "the codes with a refusal vector"); // And every reason a fault has, every phase and every budget it stops in, is shown. let shown = |member: &str| -> std::collections::BTreeSet { index["runs"].as_array().unwrap().iter().filter_map(|r| r["expect"]["fault"][member].as_str().map(str::to_string)).collect() }; let want = |v: &[&str]| -> std::collections::BTreeSet { v.iter().map(|s| s.to_string()).collect() }; assert_eq!(shown("reason"), want(&wai_wasmq::run::REASONS), "the reasons a run vector ends in"); assert_eq!(shown("phase"), want(&["seed", "prior", "init", "call"]), "the phases a run vector stops in"); assert_eq!(shown("budget"), want(&["call", "instance", "run"]), "the budgets a fuel fault of a run vector is of"); } #[test] fn each_refusal_is_refused_for_its_one_defect() { // A module vector: the module has its code, and the same module without the defect passes. for r in module_refusals() { assert_eq!(wai_wasmq::profile::check(&r.module).err().map(|e| e.code), Some(r.code), "{}", r.name); if let Err(e) = wai_wasmq::profile::check(&r.repaired) { panic!("{}: without its defect the module is still refused: {e}", r.name); } } // A bundle vector: the bundle has its code, and the same bundle without the defect packs. for (name, code, defect, repaired) in bundle_refusals() { let qubits = wai_quantum::qasm::read_callouts(&defect.program, &defect.inputs).map(|h| h.reading.program.n_qubits).unwrap_or(8); let env = write_unchecked(&defect, method_of(&defect), qubits).unwrap(); assert_eq!(wai_wasmq::open(&env).err().map(|e| e.code), Some(code), "{name}"); if let Err(e) = pack(&repaired) { panic!("{name}: without its defect the bundle is still refused: {e}"); } } // An edited envelope: one edit away from an envelope that opens. let base = pack(&runs().remove(0).1).unwrap(); assert!(wai_wasmq::open(&base).is_ok()); for (name, code, bytes) in edited(&base) { assert_eq!(wai_wasmq::open(&bytes).err().map(|e| e.code), Some(code), "{name}"); } } /// What a receipt shows of its envelope and its transcript is what the writer wrote: each run /// vector's envelope is as long as its job says (rule 31), and where no call returned, its /// transcript is the one rule 35 reads. #[test] fn a_job_shows_its_envelope_s_length_and_a_transcript_of_no_call() { let key = wai_wasmq::vectors::key(); let mut bare = 0; for (name, b) in runs() { let env = pack(&b).unwrap_or_else(|e| panic!("{name}: {e}")); let (body, _) = wai_wasmq::run(&wai_wasmq::open(&env).unwrap(), EngineKind::Interpreter, &key, false).unwrap(); let (c, r) = (&body["request"]["wasmq"], &body["result"]); assert_eq!(wai_quantum::engine::wasmq::envelope_bytes(Some(c), r).0, env.len() as u128, "{name}"); if let Some(t) = wai_quantum::engine::wasmq::bare_transcript(r) { assert_eq!(r["transcript_blake3"].as_str(), Some(t.as_str()), "{name}"); bare += 1; } } assert!(bare >= 4, "the run vectors with no call that returned: {bare}"); } /// The envelopes the crate carries to start from (`wai_wasmq::examples`) are the conformance /// set's own, byte for byte: what a surface runs from them is what `vectors.json` pins. #[test] fn the_crate_s_examples_are_the_conformance_set_s_envelopes() { for e in &wai_wasmq::examples::ALL { let committed = std::fs::read(dir().join("envelopes").join(format!("{}.wai", e.name))).unwrap_or_else(|err| panic!("{}: {err}", e.name)); assert!(committed == e.bytes, "wai-wasmq/envelopes/{}.wai is not quantum-conformance/wasmq/envelopes/{}.wai: copy it again", e.name, e.name); } }