#!/usr/bin/env python3 """Referee for quantum-conformance/: both capabilities of the quantum-sim extension (extensions/quantum-sim.md), `wai.quantum.circuit` (WQC1) and `wai.quantum.circuit2` (WQC2), recomputed with integers only. Written from the extension's rules. It shares no code with wai-rs or wai-quantum, which generate the corpus: the phase tables, the container writer and reader, the fixed-point simulator, the shot sampler and BLAKE3 are all written here. Standard library only, and no floating point anywhere. It checks, in this order, and reports every failure rather than stopping at one: 1. BLAKE3 against the published test vectors. 2. The phase tables. circuit/1's is derived by its recurrence (§3.1) and circuit2's by an interval-certified procedure in exact integers (§3.2): each step keeps a true enclosure of 2^62·cos(2π/2^k) and 2^62·sin(2π/2^k), and both ends of every enclosure round to the same integer far from a tie, so that integer is the nearest one. Each derived table must equal its literal table (restated below) and its BLAKE3 digest, and the extension's Appendix B must state those same tables and digests. 3. The vector trees: `./` (circuit/1) and `v2/` (circuit2). Every file is rebuilt byte for byte from the circuit definitions below and read back by the reader here, and every manifest entry must equal the entry computed here: n_qubits, n_ops, the circuit, statevector and histogram hashes, the measurement, and in `v2/` the capability. The four original circuit/1 vectors must also keep their frozen hashes. 4. The reject tree: `reject.json` must list exactly the cases below, the directory must hold exactly the listed files, and the reader here must refuse each one with its listed class and by the rule its name states. 5. The accept tree: `accept.json` must list exactly the cases below, the directory must hold exactly the listed files, and the reader here must accept each one and read it as listed: its capability, n_qubits, op count, circuit hash and measurement. 6. Mutation completeness: under each table, a change of ±1 to either component of any P(1..32), with H following re P(3), must change dyadic_ladder's statevector hash. It was written from the same reading of the text as the Rust reference, so a misreading the two share would pass both. python3 -I referee.py [quantum-conformance dir [quantum-sim.md]] The extension defaults to ../extensions/quantum-sim.md beside the corpus directory. Exit status 0 only when every check passes. """ import json import os import struct import sys from math import isqrt # ---------------------------------------------------------------- BLAKE3 (hash mode) _IV = [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19] _PERM = [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8] _CHUNK_START, _CHUNK_END, _PARENT, _ROOT = 1, 2, 4, 8 _M32 = 0xFFFFFFFF _CHUNK = 1024 _BLOCK = 64 def _rotr(x, n): return ((x >> n) | (x << (32 - n))) & _M32 def _g(s, a, b, c, d, x, y): s[a] = (s[a] + s[b] + x) & _M32 s[d] = _rotr(s[d] ^ s[a], 16) s[c] = (s[c] + s[d]) & _M32 s[b] = _rotr(s[b] ^ s[c], 12) s[a] = (s[a] + s[b] + y) & _M32 s[d] = _rotr(s[d] ^ s[a], 8) s[c] = (s[c] + s[d]) & _M32 s[b] = _rotr(s[b] ^ s[c], 7) def _compress(cv, block, counter, block_len, flags): m = list(struct.unpack("<16I", block)) s = list(cv) + _IV[:4] + [counter & _M32, (counter >> 32) & _M32, block_len, flags] for r in range(7): _g(s, 0, 4, 8, 12, m[0], m[1]) _g(s, 1, 5, 9, 13, m[2], m[3]) _g(s, 2, 6, 10, 14, m[4], m[5]) _g(s, 3, 7, 11, 15, m[6], m[7]) _g(s, 0, 5, 10, 15, m[8], m[9]) _g(s, 1, 6, 11, 12, m[10], m[11]) _g(s, 2, 7, 8, 13, m[12], m[13]) _g(s, 3, 4, 9, 14, m[14], m[15]) if r < 6: m = [m[i] for i in _PERM] return [s[i] ^ s[i + 8] for i in range(8)] + [s[i + 8] ^ cv[i] for i in range(8)] def _chunk_output(chunk, counter): """The last block of a chunk, ready to compress: (cv, block, counter, len, flags).""" cv = _IV n_blocks = max(1, (len(chunk) + _BLOCK - 1) // _BLOCK) for b in range(n_blocks): block = chunk[b * _BLOCK:(b + 1) * _BLOCK] flags = _CHUNK_START if b == 0 else 0 if b == n_blocks - 1: return (cv, block.ljust(_BLOCK, b"\0"), counter, len(block), flags | _CHUNK_END) cv = _compress(cv, block, counter, _BLOCK, flags)[:8] def _cv(output): cv, block, counter, block_len, flags = output return _compress(cv, block, counter, block_len, flags)[:8] def _parent(left_cv, right_cv): return (_IV, struct.pack("<8I", *left_cv) + struct.pack("<8I", *right_cv), 0, _BLOCK, _PARENT) def blake3(data): """The 32-byte BLAKE3 hash of `data`.""" chunks = [data[i:i + _CHUNK] for i in range(0, len(data), _CHUNK)] or [b""] stack = [] for i, chunk in enumerate(chunks[:-1]): cv = _cv(_chunk_output(chunk, i)) total = i + 1 while total & 1 == 0: cv = _cv(_parent(stack.pop(), cv)) total >>= 1 stack.append(cv) node = _chunk_output(chunks[-1], len(chunks) - 1) while stack: node = _parent(stack.pop(), _cv(node)) cv, block, counter, block_len, flags = node return struct.pack("<8I", *_compress(cv, block, counter, block_len, flags | _ROOT)[:8]) # The published BLAKE3 test vectors (input byte i is i mod 251): input length, hash. BLAKE3_VECTORS = [ (0, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262"), (1, "2d3adedff11b61f14c886e35afa036736dcd87a74d27b5c1510225d0f592e213"), (63, "e9bc37a594daad83be9470df7f7b3798297c3d834ce80ba85d6e207627b7db7b"), (64, "4eed7141ea4a5cd4b788606bd23f46e212af9cacebacdc7d1f4c6dc7f2511b98"), (65, "de1e5fa0be70df6d2be8fffd0e99ceaa8eb6e8c93a63f2d8d1c30ecb6b263dee"), (1023, "10108970eeda3eb932baac1428c7a2163b0e924c9a9e25b35bba72b28f70bd11"), (1024, "42214739f095a406f3fc83deb889744ac00df831c10daa55189b5d121c855af7"), (1025, "d00278ae47eb27b34faecf67b4fe263f82d5412916c1ffd97c8cb7fb814b8444"), (2048, "e776b6028c7cd22a4d0ba182a8bf62205d2ef576467e838ed6f2529b85fba24a"), (2049, "5f4d72f40d7a5f82b15ca2b2e44b1de3c2ef86c426c95c1af0b6879522563030"), (3072, "b98cb0ff3623be03326b373de6b9095218513e64f1ee2edd2525c7ad1e5cffd2"), (3073, "7124b49501012f81cc7f11ca069ec9226cecb8a2c850cfe644e327d22d3e1cd3"), (4096, "015094013f57a5277b59d8475c0501042c0b642e531b0a1c8f58d2163229e969"), (4097, "9b4052b38f1c5fc8b1f9ff7ac7b27cd242487b3d890d15c96a1c25b8aa0fb995"), (8193, "bab6c09cb8ce8cf459261398d2e7aef35700bf488116ceb94a36d0f5f1b7bc3b"), (16384, "f875d6646de28985646f34ee13be9a576fd515f76b5b0a26bb324735041ddde4"), (31744, "62b6960e1a44bcc1eb1a611a8d6235b6b4b78f32e7abc4fb4c6cdcce94895c47"), ] # ---------------------------------------------------------------- the phase tables FRAC = 30 ONE = 1 << FRAC HALF = 1 << (FRAC - 1) DYADIC_MAX = 32 # Appendix B.1: circuit/1, (re, im) for k = 1..32. P(19..32) apply no phase at all. PHASE_V1 = [None] + [ (-1073741824, 0), (0, 1073741824), (759250124, 759250124), (992008094, 410903207), (1053110175, 209476638), (1068571463, 105245102), (1072448454, 52686012), (1073418432, 26350953), (1073660972, 13176485), (1073721610, 6588405), (1073736770, 3294284), (1073740560, 1647223), (1073741507, 823774), (1073741744, 411887), (1073741803, 207243), (1073741818, 103621), (1073741822, 56755), (1073741823, 32768), ] + [(1073741823, 0)] * 14 PHASE_V1_DIGEST = "3fa9805ff96feb819a1cf371fedcd3b5228f93eddf933d0edcf401cbf8c28466" # Appendix B.2: circuit2, the integers nearest 2^30·cos(2π/2^k) and 2^30·sin(2π/2^k). PHASE_V2 = [None] + [ (-1073741824, 0), (0, 1073741824), (759250125, 759250125), (992008094, 410903207), (1053110176, 209476638), (1068571464, 105245103), (1072448455, 52686014), (1073418433, 26350943), (1073660973, 13176464), (1073721611, 6588356), (1073736771, 3294193), (1073740561, 1647099), (1073741508, 823550), (1073741745, 411775), (1073741804, 205887), (1073741819, 102944), (1073741823, 51472), ] + [(ONE, im) for im in (25736, 12868, 6434, 3217, 1608, 804, 402, 201, 101, 50, 25, 13, 6, 3, 2)] PHASE_V2_DIGEST = "d34cb793715348e7c92db027c0bc3dddb6008f5730470bafaed978ea7fb23510" def derive_v1(): """circuit/1's table by its recurrence: with c = re P(k-1), re P(k) = isqrt(floor((2^30 + c) / 2) · 2^30), im P(k) = isqrt(floor((2^30 - c) / 2) · 2^30).""" t = [None, (-ONE, 0), (0, ONE)] for k in range(3, DYADIC_MAX + 1): c = t[k - 1][0] t.append((isqrt(((ONE + c) // 2) << FRAC), isqrt(((ONE - c) // 2) << FRAC))) return t def derive_v2(): """circuit2's table by enclosures at scale 2^62, and the reasons it is not certified. cos(θ/2) = √((1 + cos θ)/2) and sin(θ/2) = sin θ / (2 cos(θ/2)), each bound rounded outward, starting from (cos, sin)(π/2) = (0, 1), which is exact.""" s = 1 << 62 def ceil_isqrt(x): r = isqrt(x) return r if r * r == x else r + 1 def ceil_div(a, b): return -((-a) // b) def nearest(x): # From 2^-62 to 2^-30, half up. return (x + (1 << 31)) >> 32 problems = [] c_lo, c_hi, s_lo, s_hi = 0, 0, s, s t = [None, (-ONE, 0), (0, ONE)] for k in range(3, DYADIC_MAX + 1): c_lo2 = isqrt(((s + c_lo) * s) >> 1) c_hi2 = ceil_isqrt(ceil_div((s + c_hi) * s, 2)) s_lo2 = (s_lo * s) // (2 * c_hi2) s_hi2 = ceil_div(s_hi * s, 2 * c_lo2) if nearest(c_lo2) != nearest(c_hi2) or nearest(s_lo2) != nearest(s_hi2): problems.append("k=%d: the enclosure's ends round apart" % k) for x in (c_lo2, c_hi2, s_lo2, s_hi2): if abs((x % (1 << 32)) - (1 << 31)) <= (1 << 20): problems.append("k=%d: an end lies within 2^20 of a tie" % k) t.append((nearest(c_lo2), nearest(s_lo2))) c_lo, c_hi, s_lo, s_hi = c_lo2, c_hi2, s_lo2, s_hi2 return t, problems def table_digest(t): """BLAKE3 over i64le(re) ‖ i64le(im) for k = 1..32, no domain prefix.""" return blake3(b"".join(struct.pack(" 18: # a section holds at most 2^32 digits, so the sign alone decides return not exponent.startswith("-") e = -int(e or "0") if exponent.startswith("-") else int(e or "0") k = len(whole) - (len(digits) - len(digits.lstrip("0"))) + e if k != len(RANGE_LIMIT): return k > len(RANGE_LIMIT) n = max(len(significant), len(RANGE_LIMIT)) return significant.ljust(n, "0") >= RANGE_LIMIT.ljust(n, "0") def strict_json(raw): """`raw` read as I-JSON (§2), or NotIJson naming the rule it breaks. An integer reads as a Python int, so `-0` and any number with a fraction or exponent read as ("number", literal): never an unsigned integer, and never a float.""" def number(text): if out_of_range(text): raise NotIJson("number range") return ("number", text) def integer(text): if out_of_range(text): raise NotIJson("number range") return ("number", text) if text == "-0" else int(text) def no_constant(name): raise NotIJson("syntax") def unique(pairs): names = [k for k, _ in pairs] if len(set(names)) != len(names): raise NotIJson("duplicate member") return dict(pairs) def text(s): for ch in s: o = ord(ch) if 0xD800 <= o <= 0xDFFF: raise NotIJson("surrogate") if 0xFDD0 <= o <= 0xFDEF or o & 0xFFFE == 0xFFFE: raise NotIJson("noncharacter") def walk(v, depth): if isinstance(v, (dict, list)): if depth + 1 > MAX_DEPTH: raise NotIJson("depth") items = v.items() if isinstance(v, dict) else enumerate(v) for k, x in items: if isinstance(k, str): text(k) walk(x, depth + 1) elif isinstance(v, str): text(v) try: decoded = raw.decode("utf-8") except UnicodeDecodeError: raise NotIJson("utf-8") try: v = json.loads(decoded, parse_constant=no_constant, parse_float=number, parse_int=integer, object_pairs_hook=unique) except RecursionError: raise NotIJson("depth") except ValueError: # json.JSONDecodeError among them raise NotIJson("syntax") walk(v, 0) return v def unsigned(v, below): """Whether `v` is a JSON unsigned integer less than `below` (not a float, not a boolean).""" return type(v) is int and 0 <= v < below def read_contract(gs, raw): """The contract's n_qubits, under the capability the magic names.""" try: cv = strict_json(raw) except NotIJson as e: raise Refused("contract", "not JSON: " + e.rule) if not isinstance(cv, dict): raise Refused("contract", "not an object") def n_qubits(): n = cv.get("n_qubits") if not unsigned(n, 256): raise Refused("malformed", "n_qubits") return n if gs is V1: # The four values, in any formatting, with any other members. for key, value in (("ext", gs.ext), ("numeric", gs.numeric)): if cv.get(key) != value: raise Refused("contract", key) if not (unsigned(cv.get("frac"), 1 << 64) and cv["frac"] == FRAC): raise Refused("contract", "frac") if cv.get("gateset") != gs.name: raise Refused("contract", "gateset") return n_qubits() n = n_qubits() if raw != gs.contract(n): raise Refused("contract", "canonical contract") return n def read_oplog(raw): """The ops and the bytes they took; bytes after the last op are the caller's to judge.""" if len(raw) < 4: raise Refused("malformed", "op-log header") n_ops = struct.unpack_from(" P: raise Refused("malformed", "opcode") param = struct.unpack_from("= n_qubits: raise Refused("invalid", "target") if len(controls) > 255: raise Refused("invalid", "control count") for c in controls: if c >= n_qubits: raise Refused("invalid", "control") if c == target: raise Refused("invalid", "control is target") if gs is V2 and len(set(controls)) != len(controls): raise Refused("invalid", "repeated control") if op == P: if not 1 <= param <= DYADIC_MAX: raise Refused("invalid", "phase index") elif gs is V2 and param != 0: raise Refused("invalid", "param") def read_measure(gs, raw): """circuit/1: JSON, a missing or non-u64 seed or shots reads as 0. circuit2: u64 seed and shots, and exactly the canonical bytes.""" try: mv = strict_json(raw) except NotIJson as e: raise Refused("malformed", "measure JSON: " + e.rule) if gs is V1: def field(key): v = mv.get(key) if isinstance(mv, dict) else None return v if unsigned(v, 1 << 64) else 0 return (field("seed"), field("shots")) for key in ("seed", "shots"): if not (isinstance(mv, dict) and unsigned(mv.get(key), 1 << 64)): raise Refused("malformed", "measure " + key) m = (mv["seed"], mv["shots"]) if raw != measure_bytes(*m): raise Refused("malformed", "canonical measure") return m def read_wqc(data): """(gateset, n_qubits, ops, measure or None), or Refused.""" if len(data) < 6 or data[:4] not in BY_MAGIC: raise Refused("malformed", "magic") gs = BY_MAGIC[data[:4]] n_sections = struct.unpack_from(" len(data): raise Refused("malformed", "section table") sections = {} for i in range(n_sections): p = 6 + 9 * i kind = data[p] offset, length = struct.unpack_from(" len(data): raise Refused("malformed", "section bounds") if kind not in (SECT_CONTRACT, SECT_OPLOG, SECT_MEASURE): continue # unknown kinds are ignored if kind in sections: raise Refused("malformed", "duplicate section") sections[kind] = data[start:end] if SECT_CONTRACT not in sections: raise Refused("malformed", "missing contract") if SECT_OPLOG not in sections: raise Refused("malformed", "missing op-log") n_qubits = read_contract(gs, sections[SECT_CONTRACT]) ops, used = read_oplog(sections[SECT_OPLOG]) if gs is V2 and used != len(sections[SECT_OPLOG]): raise Refused("malformed", "trailing bytes") check_valid(gs, n_qubits, ops) measure = read_measure(gs, sections[SECT_MEASURE]) if SECT_MEASURE in sections else None return gs, n_qubits, ops, measure # ---------------------------------------------------------------- simulator (§3.0) def fx(a, b): """(a·b + 2^29) >> 30, exact, with an arithmetic shift.""" return (a * b + HALF) >> FRAC def cmul(a, b): return (fx(a[0], b[0]) - fx(a[1], b[1]), fx(a[0], b[1]) + fx(a[1], b[0])) def cadd(a, b): return (a[0] + b[0], a[1] + b[1]) def matrix(op, param, table, h): z, one, i, minus_i = (0, 0), (ONE, 0), (0, ONE), (0, -ONE) p3 = table[3] return { I: ((one, z), (z, one)), X: ((z, one), (one, z)), Y: ((z, minus_i), (i, z)), Z: ((one, z), (z, (-ONE, 0))), H: (((h, 0), (h, 0)), ((h, 0), (-h, 0))), S: ((one, z), (z, i)), SDG: ((one, z), (z, minus_i)), T: ((one, z), (z, p3)), TDG: ((one, z), (z, (p3[0], -p3[1]))), P: ((one, z), (z, table[param] if op == P else z)), }[op] def simulate(n_qubits, ops, table): """The statevector from |0…0⟩. H is re P(3) of the same table.""" h = table[3][0] dim = 1 << n_qubits amps = [(0, 0)] * dim amps[0] = (ONE, 0) for op, controls, target, param in ops: (m00, m01), (m10, m11) = matrix(op, param, table, h) tbit = 1 << target cmask = 0 for c in controls: cmask |= 1 << c for i in range(dim): if i & tbit == 0 and i & cmask == cmask: j = i | tbit a, b = amps[i], amps[j] amps[i] = cadd(cmul(m00, a), cmul(m01, b)) amps[j] = cadd(cmul(m10, a), cmul(m11, b)) return amps # ---------------------------------------------------------------- the hashes (§4) def circuit_hash(gs, n_qubits, ops): return blake3(b"wai:quantum-circuit\x01" + gs.contract(n_qubits) + oplog_bytes(ops)).hex() def statevector_hash(n_qubits, amps): out = bytearray(b"wai:quantum-statevector\x01") + bytes([n_qubits]) + struct.pack("> 30)) * 0xBF58476D1CE4E5B9) & 0xFFFFFFFFFFFFFFFF z = ((z ^ (z >> 27)) * 0x94D049BB133111EB) & 0xFFFFFFFFFFFFFFFF return state, z ^ (z >> 31) def histogram_hash(n_qubits, amps, seed, shots): """Each shot draws r = splitmix64 mod W, W the sum of the weights re² + im², and counts the first basis state whose cumulative weight exceeds r.""" cumulative, total = [], 0 for re, im in amps: total += re * re + im * im cumulative.append(total) counts = [0] * len(amps) state = seed for _ in range(shots): state, r = splitmix64(state) r %= total lo, hi = 0, len(cumulative) while lo < hi: mid = (lo + hi) // 2 if cumulative[mid] > r: hi = mid else: lo = mid + 1 counts[min(lo, len(counts) - 1)] += 1 out = bytearray(b"wai:quantum-histogram\x01") + bytes([n_qubits]) + struct.pack(" j; then the reversal swaps, each as CX(a→b), CX(b→a), CX(a→b).""" ops = [] for j in range(n): ops.append(gate(H, j)) for l in range(j + 1, n): ops.append(gate(P, j, (l,), l - j + 1)) for j in range(n // 2): a, b = j, n - 1 - j ops += [gate(X, b, (a,)), gate(X, a, (b,)), gate(X, b, (a,))] return ops def dyadic_ladder_ops(): ops = [gate(X, 0)] + [gate(op, 1) for op in (Y, Z, S, SDG, T, TDG, I)] chain = [gate(P, 1, (0,), k) for k in range(1, DYADIC_MAX + 1)] return ops + chain + chain # name: (n_qubits, ops, measure (seed, shots) or None). Both trees hold all of them. VECTORS = { "bell": (2, [gate(H, 0), gate(X, 1, (0,))], (12648430, 100000)), "ghz3": (3, [gate(H, 0), gate(X, 1, (0,)), gate(X, 2, (1,))], None), "qft3": (3, qft_ops(3), None), "qft5": (5, qft_ops(5), None), "qft5_x22": (5, [gate(X, 1), gate(X, 2), gate(X, 4)] + qft_ops(5), None), "dyadic_ladder": (2, dyadic_ladder_ops(), None), "qft8_ones": (8, [gate(X, q) for q in range(8)] + qft_ops(8), None), } # The four original circuit/1 vectors are frozen: circuit, statevector, histogram hash. FROZEN_V1 = { "bell": ("894d9a9582b19f27b8d08d06938357237fc00f7f30bf4bfc2be0c04fa5e47a4e", "bd5df69662f4e61878d17baee1f3cf5705c3726d6740d1a4bf7be5133c099efb", "0893f48ef32ca9f2865f6617537f0a4a5334f67e3c944bba845beb2f377acdb3"), "ghz3": ("34e44f7d68037cbb3aee677f56b894dc48df6d408aa068a16d3b18076e5e7c5c", "08c5e6fb50b91a00d28795506cc5d99ce1e9a3a8b4c41e4bfd05f5d829af9542", None), "qft3": ("7e8414be4410557a05b52bcc1084e1c5f93b6caa0f28fa45f4d2ab661efd2a60", "0fe1e5b8e2ac8e9afbfa926b3877967ebfead92efb7ed8e1ff6b117668926d6e", None), "qft5": ("f36a9af99a251cae6f42144b2be3c57523f15e1e2ab3492cf15cac709fc048be", "c268d77f2ddf84fecb028e80ce989123912870f04e5b9a1e3d885c9ce235c27d", None), } # The reject corpus: file stem → (class, the rule that refuses it here). REJECTS = { "magic_wqc3": ("malformed", "magic"), "v1_ext_other": ("contract", "ext"), "v1_numeric_other": ("contract", "numeric"), "v1_frac_31": ("contract", "frac"), "v1_frac_float": ("contract", "frac"), "v1_gateset_rn": ("contract", "gateset"), "v1_contract2_in_wqc1": ("contract", "ext"), "v1_fields_missing": ("contract", "ext"), "v1_contract_not_object": ("contract", "not an object"), "v1_n_qubits_256": ("malformed", "n_qubits"), "v1_n_qubits_258": ("malformed", "n_qubits"), "v1_contract_twice": ("malformed", "duplicate section"), "v1_oplog_twice": ("malformed", "duplicate section"), "v1_contract_duplicate_member": ("contract", "not JSON: duplicate member"), "v1_contract_lone_surrogate": ("contract", "not JSON: surrogate"), "v1_contract_too_deep": ("contract", "not JSON: depth"), "v1_n_qubits_minus0": ("malformed", "n_qubits"), "v1_contract_lone_low_surrogate": ("contract", "not JSON: surrogate"), "v1_contract_noncharacter": ("contract", "not JSON: noncharacter"), "v1_contract_raw_noncharacter": ("contract", "not JSON: noncharacter"), "v1_contract_integer_rounds_to_infinity": ("contract", "not JSON: number range"), "v1_contract_number_rounds_to_infinity": ("contract", "not JSON: number range"), "v1_contract_long_literal": ("contract", "not JSON: number range"), "v1_contract_bom": ("contract", "not JSON: syntax"), "v1_contract_nan": ("contract", "not JSON: syntax"), "v1_contract_control_character": ("contract", "not JSON: syntax"), "v1_measure_duplicate_member": ("malformed", "measure JSON: duplicate member"), "v1_contract_not_utf8": ("contract", "not JSON: utf-8"), "v1_contract_number_out_of_range": ("contract", "not JSON: number range"), "v1_n_qubits_fraction": ("malformed", "n_qubits"), "v1_n_qubits_missing": ("malformed", "n_qubits"), "v1_measure_not_json": ("malformed", "measure JSON: syntax"), "short_payload": ("malformed", "magic"), "table_past_end": ("malformed", "section table"), "section_past_end": ("malformed", "section bounds"), "unknown_section_past_end": ("malformed", "section bounds"), "no_contract": ("malformed", "missing contract"), "no_oplog": ("malformed", "missing op-log"), "v1_oplog_short": ("malformed", "op-log header"), "v1_op_truncated_controls": ("malformed", "op controls"), "v1_opcode_10": ("malformed", "opcode"), "v1_target_out_of_range": ("invalid", "target"), "v1_control_out_of_range": ("invalid", "control"), "v1_control_is_target": ("invalid", "control is target"), "v1_phase_0": ("invalid", "phase index"), "v1_phase_33": ("invalid", "phase index"), "v1_n_ops_u32max": ("malformed", "op header"), "v1_offset_wrap": ("malformed", "section bounds"), "v2_contract_whitespace": ("contract", "canonical contract"), "v2_contract_key_order": ("contract", "canonical contract"), "v2_contract_extra_member": ("contract", "canonical contract"), "v2_contract1_in_wqc2": ("contract", "canonical contract"), "v2_n_qubits_256": ("malformed", "n_qubits"), "v2_contract_not_object": ("contract", "not an object"), "v2_n_qubits_rounds_to_infinity": ("contract", "not JSON: number range"), "v2_n_qubits_largest_finite": ("malformed", "n_qubits"), "v2_phase_0": ("invalid", "phase index"), "v2_measure_twice": ("malformed", "duplicate section"), "v2_measure_seed_signed": ("malformed", "measure seed"), "v2_duplicate_controls": ("invalid", "repeated control"), "v2_param_on_h": ("invalid", "param"), "v2_oplog_trailing_byte": ("malformed", "trailing bytes"), "v2_opcode_10": ("malformed", "opcode"), "v2_measure_spaced": ("malformed", "canonical measure"), "v2_measure_no_shots": ("malformed", "measure shots"), "v2_measure_basis_x": ("malformed", "canonical measure"), } # ---------------------------------------------------------------- the checks class Report: def __init__(self): self.failures = 0 def check(self, label, problem): """`problem` is None for a pass, else why it failed.""" if problem is None: print("PASS", label) else: self.failures += 1 print("FAIL %s: %s" % (label, problem)) def check_blake3(rep): bad = [n for n, want in BLAKE3_VECTORS if blake3(bytes(i % 251 for i in range(n))).hex() != want] rep.check("BLAKE3 against the published test vectors", "lengths %s differ" % bad if bad else None) def check_tables(rep): v1 = derive_v1() rep.check("circuit/1 table: the recurrence gives Appendix B.1", None if v1 == PHASE_V1 else "k = %s differ" % [k for k in range(1, 33) if v1[k] != PHASE_V1[k]]) d1 = table_digest(PHASE_V1) rep.check("circuit/1 table digest", None if d1 == PHASE_V1_DIGEST else "got " + d1) v2, problems = derive_v2() rep.check("circuit2 table: every enclosure certifies its nearest integer", "; ".join(problems) or None) rep.check("circuit2 table: the certified integers are Appendix B.2", None if v2 == PHASE_V2 else "k = %s differ" % [k for k in range(1, 33) if v2[k] != PHASE_V2[k]]) d2 = table_digest(PHASE_V2) rep.check("circuit2 table digest", None if d2 == PHASE_V2_DIGEST else "got " + d2) APPENDIX_BEGIN = "" APPENDIX_END = "" def read_appendix(path): """Appendix B of the extension as {subsection: (capability, table, digest)}, or the reason it cannot be read. A subsection is a `### B.n `capability`` heading, rows `| k | re | im |`, and a `Digest: `hex`` line.""" try: with open(path, encoding="utf-8") as f: text = f.read() except OSError as e: return None, str(e) begin, end = text.find(APPENDIX_BEGIN), text.find(APPENDIX_END) if begin < 0 or end < begin: return None, "no generated Appendix B" out, current = {}, None for line in text[begin + len(APPENDIX_BEGIN):end].splitlines(): line = line.strip() if line.startswith("### B."): sub, _, cap = line[4:].partition(" ") current = sub out[sub] = (cap.strip("`"), [None], None) elif current and line.startswith("| ") and line[2:3].isdigit(): try: k, re_, im = (int(c.strip()) for c in line.strip("|").split("|")) except ValueError: return None, "%s: unreadable row %r" % (current, line) table = out[current][1] if k != len(table): return None, "%s: row k = %d out of order" % (current, k) table.append((re_, im)) elif current and line.startswith("Digest: `"): out[current] = (out[current][0], out[current][1], line[len("Digest: `"):].rstrip("`")) return out, None def check_appendix(rep, path): """The extension's Appendix B states the literal tables and digests above.""" appendix, why = read_appendix(path) if appendix is None: rep.check("extension Appendix B (%s)" % path, why) return for sub, gs, digest in (("B.1", V1, PHASE_V1_DIGEST), ("B.2", V2, PHASE_V2_DIGEST)): got = appendix.get(sub) if got is None: rep.check("extension Appendix %s" % sub, "missing") continue cap, table, stated = got problems = [] if cap != gs.capability: problems.append("names %s" % cap) if table != gs.table: problems.append("k = %s differ" % [k for k in range(1, DYADIC_MAX + 1) if k >= len(table) or table[k] != gs.table[k]]) if stated != digest: problems.append("digest %s" % stated) rep.check("extension Appendix %s is the %s table and digest" % (sub, gs.label), "; ".join(problems) or None) extra = sorted(set(appendix) - {"B.1", "B.2"}) rep.check("extension Appendix B has no other table", "has %s" % extra if extra else None) def read_object(path): """A JSON object from `path`, or the reason it is not one.""" try: with open(path, "rb") as f: v = strict_json(f.read()) except OSError as e: return None, str(e) if not isinstance(v, dict): return None, "not a JSON object" return v, None def listed_and_present(listed, directory): """None when `directory` holds exactly the `.wqc` files `listed` names.""" try: present = {f for f in os.listdir(directory) if f.endswith(".wqc")} except OSError as e: return str(e) listed = set(listed) if listed == present: return None return "listed but missing %s, present but unlisted %s" % (sorted(listed - present), sorted(present - listed)) def expected_entry(gs, name, label_capability): """The manifest entry for vector `name` in `gs`, computed here, and the hashes.""" n, ops, measure = VECTORS[name] amps = simulate(n, ops, gs.table) entry = {"n_qubits": n, "n_ops": len(ops), "circuit_hash": circuit_hash(gs, n, ops), "statevector_hash": statevector_hash(n, amps)} if label_capability: entry["capability"] = gs.capability if measure is not None: entry["measure"] = {"seed": measure[0], "shots": measure[1], "histogram_hash": histogram_hash(n, amps, *measure)} return entry def check_tree(rep, root, sub, gs, label_capability): tree = os.path.join(root, sub) if sub else root prefix = sub + "/" if sub else "" manifest, why = read_object(os.path.join(tree, "expected_hashes.json")) if manifest is None: rep.check(prefix + "expected_hashes.json", why) return names = ["%s.wqc" % v for v in VECTORS] rep.check(prefix + "expected_hashes.json lists every vector and no other", None if set(manifest) == set(names) else "lists %s" % sorted(manifest)) rep.check(prefix + "vectors/ holds exactly the listed files", listed_and_present(names, os.path.join(tree, "vectors"))) for name, (n, ops, measure) in VECTORS.items(): label = "%s%s.wqc (%s)" % (prefix, name, gs.label) try: with open(os.path.join(tree, "vectors", name + ".wqc"), "rb") as f: data = f.read() except OSError as e: rep.check(label, str(e)) continue problems = [] if data != write_wqc(gs, n, ops, measure): problems.append("the file is not the bytes rebuilt here") try: if read_wqc(data) != (gs, n, ops, measure): problems.append("the file reads back as another circuit") except Refused as r: problems.append("refused (%s)" % r) want = expected_entry(gs, name, label_capability) got = manifest.get(name + ".wqc") if got != want: differ = sorted(k for k in set(want) | set(got or {}) if (got or {}).get(k) != want.get(k)) problems.append("manifest members %s differ from the values computed here" % differ) if gs is V1 and name in FROZEN_V1: frozen = FROZEN_V1[name] computed = (want["circuit_hash"], want["statevector_hash"], want["measure"]["histogram_hash"] if "measure" in want else None) if computed != frozen: problems.append("a frozen circuit/1 hash changed") rep.check(label, "; ".join(problems) or None) def check_rejects(rep, root): tree = os.path.join(root, "reject") listing, why = read_object(os.path.join(tree, "reject.json")) if listing is None: rep.check("reject/reject.json", why) return want = {"%s.wqc" % stem: kind for stem, (kind, _) in REJECTS.items()} rep.check("reject/reject.json lists every reject case with its class", None if listing == want else "differs in %s" % sorted( f for f in set(listing) | set(want) if listing.get(f) != want.get(f))) rep.check("reject/ holds exactly the listed files", listed_and_present(listing, tree)) for stem, (kind, rule) in REJECTS.items(): label = "reject/%s.wqc" % stem try: with open(os.path.join(tree, stem + ".wqc"), "rb") as f: data = f.read() except OSError as e: rep.check(label, str(e)) continue try: read_wqc(data) rep.check(label, "accepted; it must be refused as %s (%s)" % (kind, rule)) except Refused as r: rep.check(label + " refused as %s (%s)" % (kind, rule), None if (r.kind, r.rule) == (kind, rule) else "refused as %s (%s)" % (r.kind, r.rule)) # The accept corpus: the file stems `accept.json` must list. Each file's reading is # recomputed here and compared with the listing. ACCEPTS = [ "v1_members_reordered", "v1_whitespace", "v1_further_members", "v1_escapes_and_characters", "v1_depth_127", "v1_numbers_largest_finite", "v1_numbers_long_literals", "v1_numbers_toward_zero", "v1_n_qubits_255", "v1_measure_lax", "v1_measure_missing_shots", "v1_measure_u64_max", "v1_measure_not_u64", "v1_measure_other_types", "v1_measure_not_object", "v1_oplog_trailing_bytes", "v1_param_on_h", "v1_repeated_control", "v1_unknown_sections", "v2_unknown_sections", "v2_n_qubits_255", "v2_measure_u64_max", ] def check_accepts(rep, root): tree = os.path.join(root, "accept") listing, why = read_object(os.path.join(tree, "accept.json")) if listing is None: rep.check("accept/accept.json", why) return want = sorted("%s.wqc" % stem for stem in ACCEPTS) rep.check("accept/accept.json lists every accept case", None if sorted(listing) == want else "differs in %s" % sorted(set(listing) ^ set(want))) rep.check("accept/ holds exactly the listed files", listed_and_present(listing, tree)) for file in want: label = "accept/" + file try: with open(os.path.join(tree, file), "rb") as f: data = f.read() gs, n_qubits, ops, measure = read_wqc(data) except OSError as e: rep.check(label, str(e)) continue except Refused as r: rep.check(label, "refused as %s (%s); it must be accepted" % (r.kind, r.rule)) continue got = { "capability": gs.capability, "n_qubits": n_qubits, "n_ops": len(ops), "circuit_hash": circuit_hash(gs, n_qubits, ops), "measure": None if measure is None else {"seed": measure[0], "shots": measure[1]}, } rep.check(label + " accepted and read as listed", None if got == listing.get(file) else "read as %s, listed as %s" % (got, listing.get(file))) def check_mutations(rep): n, ops, _ = VECTORS["dyadic_ladder"] for gs in (V1, V2): base = statevector_hash(n, simulate(n, ops, gs.table)) missed = [] for k in range(1, DYADIC_MAX + 1): for component in (0, 1): for delta in (-1, 1): table = list(gs.table) entry = list(table[k]) entry[component] += delta table[k] = tuple(entry) if statevector_hash(n, simulate(n, ops, table)) == base: missed.append("P(%d).%s%+d" % (k, "re" if component == 0 else "im", delta)) rep.check("dyadic_ladder sees every ±1 change of P(1..32) in the %s table" % gs.label, "missed %s" % missed if missed else None) def main(): root = sys.argv[1] if len(sys.argv) > 1 else os.path.join(os.path.dirname(os.path.abspath(__file__)), "..") extension = sys.argv[2] if len(sys.argv) > 2 else os.path.join(root, "..", "extensions", "quantum-sim.md") rep = Report() check_blake3(rep) check_tables(rep) check_appendix(rep, extension) check_tree(rep, root, "", V1, False) check_tree(rep, root, "v2", V2, True) check_rejects(rep, root) check_accepts(rep, root) check_mutations(rep) if rep.failures: print("%d failed — NOT conformant" % rep.failures) sys.exit(1) print("referee: every check passes") if __name__ == "__main__": main()