open-standards/wai/quantum-cross-simulator/tools/compare.py
Transaction Science 55208351c3 wai-quantum: the rest of the review's fixes on the reader-scaling branch
- A named method that needs standard gates reads them, so a program they
  cannot hold is refused as the reader refuses it: on its line, with Q208
  or Q210 and no J011. Both refusals past 16 controls say how a job holds
  the gate, and no longer say the bit-exact method does so by default.
- A verdict with no intent reads the program as it ran, so a gate past 16
  controls runs over MCP, as the docs say; an intent is checked in
  standard gates, and a job with one is refused there, on its line.
- lint's dynamic_steps counts standard steps, beside gates.
- options.controls is read: another value, or "whole" on a job that runs
  standard gates, is refused (J003), and the schema, methods list, API
  description, README and both MCP servers name it.
- accuracy.global_phase is stated against method statevector's state, not
  the language's, whose U and gphase differ from it; below_table and
  moved_angles say they count phase gates, at 1e-14 of a turn.
- operations is an engine member.
- Gates held whole may take 2,000,000 qubits in all (Q210), which holds
  nothing the standard form holds.
- A metered plan says `metered`, its work what the meter allows, and such a
  job does not set the method's measured rate.
- The refusal, service and QEC pages and the corpus say what changed; the
  tests are named in CI; the conformance set holds the new text forms; the
  service snapshot and the agent docs are regenerated.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-07 01:44:51 -05:00

1309 lines
66 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""Run every engine on the corpus, hold each answer to its reference within its tolerance, and
say what passed, what failed and by how much.
compare.py --tier ci|full [--out RESULTS.json] [--jobs N] [--inject FAULT] [--only ID,...]
Exit status 0 when every check passes, 1 when one fails, 2 when the run itself could not be
made. `--inject` corrupts one engine's answers after it gives them, or makes the engine under
test refuse, crash, hang or accept what it must not, the way a wrong engine would, so that a run
can be seen to fail; FAULTS lists what it can do.
Every tolerance is derived, never fitted, and tolerance.py says from what. A check another engine
cannot make (an engine without the gate, a method that does not take the circuit) is recorded as
`not run`, with the engine's own words, and is not a pass. The engine under test is held to more:
a crash, a time-out, or a refusal that corpus/expected-refusals.json does not list in those words
fails the run, and so does an answer to a circuit that file says it must refuse.
"""
import argparse
import hashlib
import itertools
import json
import math
import os
import platform
import shutil
import signal
import subprocess
import sys
import tempfile
import threading
import time
from concurrent.futures import ThreadPoolExecutor
from fractions import Fraction
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
import corpus # noqa: E402
import exact # noqa: E402
import tolerance as tol # noqa: E402
ROOT = os.path.dirname(HERE)
WAI = os.path.dirname(ROOT)
STATE_ENGINES = ["wai.exact", "wai.statevector", "aer.statevector", "aer.qasm_import", "quimb.dense", "quimb.tensor_network", "ddsim.statevector", "qsim.statevector"]
COUNT_ENGINES = ["wai.auto", "wai.statevector", "aer.statevector", "ddsim.statevector", "qsim.statevector"]
CLIFFORD = {"id", "x", "y", "z", "h", "s", "sdg", "sx", "sxdg", "cx", "cy", "cz", "swap"}
# Every sampled test of a run (counts, error-correction rates, stabilizer outcomes) shares this:
# a correct engine fails a run on sampling with probability at most this, a third to each kind.
FAMILY_WISE_ALPHA = 1e-6
# The closed-form checks of the integer reference, in units of 2^-256: far above its own rounding
# (a few units a gate), far below anything an engine is held to.
REFERENCE_UNITS = 2**40
# mqt.ddsim's angle tolerance (its parameter_tolerance setting): an angle below it may be read as 0.
DDSIM_PARAMETER_TOLERANCE = 1e-13
FAULTS = {
"amplitude": "aer.statevector: one amplitude of every state moved by 1e-9",
"bit_order": "wai.statevector: every state read with its qubits in reverse order",
"counts": "stim.tableau: 5% of the shots of the most frequent outcome moved to the least frequent outcome that can occur",
"counts_spread": "aer.statevector: every outcome's count moved by a tenth of itself, up and down in turn, the total kept (an L1 distance of about 0.1)",
"dem": "wai.qec: the most likely mechanism's probability raised by one part in 10^6",
"qec_rate": "wai.qec: the sampler's most frequent detector count raised by a quarter",
"stabilizer": "stim.tableau: the sign of the last stabilizer flipped",
"fixed_point": "wai.exact: the lowest bit of one fixed-point amplitude flipped",
"drop_gate": "wai.exact: the op-log's last phase dropped, and the integers and hash made again from what is left",
"drop_phase": "wai.statevector: the state of the circuit with its small-angle gate left out",
"refuse": "wai.mps: the engine refuses every job",
"crash": "wai.auto: the engine exits with status 101 and no answer",
"hang": "wai.qec: the engine's sampler never answers",
"accept": "wai.exact: every angle moved to the nearest m/2^32 of a turn before the engine reads it",
"qec_words": "wai.qec: the engine refuses each model with a line number, in words no list holds",
"qec_crash": "wai.qec: the engine crashes on each model",
"exact_words": "wai.exact: the engine refuses each state, in words other than its list's",
"qec_short": "wai.qec: the sampler's answer lists no observable",
"exact_turn": "wai.exact: every state turned by a global phase of 0.5 rad",
"phase_flip": "wai.statevector: every state turned by a half turn, as a phase applied with the wrong sign, or not at all",
"mps_phase": "aer.mps: every amplitude turned by 0.5 rad",
"counts_collapse": "stim.tableau: every shot given to one outcome",
}
# The faults the engine's adapter carries out (engines.py), rather than this file.
DRIVER_FAULTS = {"refuse": "wai.mps", "crash": "wai.auto", "hang": "wai.qec", "accept": "wai.exact", "qec_words": "wai.qec", "qec_crash": "wai.qec", "exact_words": "wai.exact"}
def is_clifford(e):
for s in e["steps"]:
for x in s.get("then", [s]):
if "g" in x and x["g"] not in CLIFFORD:
return False
return True
def has_dynamic(e):
k = len(e["steps"])
while k and "measure" in e["steps"][k - 1]:
k -= 1
return any("g" not in s for s in e["steps"][:k]) or k < len(e["steps"]) and e["expect"] != "distribution"
class Runner:
def __init__(self, work, python, timeout, inject):
self.work = work
self.python = python
self.timeout = timeout
self.inject = inject
self.counter = itertools.count(1)
self.lock = threading.Lock()
def run(self, engine, job):
"""One engine on one job in a process group of its own: its answer and wall time. On a
time-out the whole group is killed, the engine's driver with its adapter."""
with self.lock:
k = next(self.counter)
tag = "%s-%s-%d" % (job.get("id", "job"), engine, k)
jp = os.path.join(self.work, tag + ".job.json")
ap = os.path.join(self.work, tag + ".answer.json")
if job["kind"] == "state" and "state_out" not in job:
job = dict(job, state_out=os.path.join(self.work, tag + ".state"))
if self.inject in DRIVER_FAULTS and DRIVER_FAULTS[self.inject] == engine:
job = dict(job, inject=self.inject)
with open(jp, "w") as f:
json.dump(job, f)
t0 = time.perf_counter()
p = subprocess.Popen([self.python, "-I", os.path.join(HERE, "engines.py"), engine, jp, ap], stdout=subprocess.PIPE, stderr=subprocess.PIPE, start_new_session=True)
try:
_, err = p.communicate(timeout=self.timeout)
except subprocess.TimeoutExpired:
try:
os.killpg(p.pid, signal.SIGKILL)
except ProcessLookupError:
pass
p.communicate()
return {"ok": False, "timeout": self.timeout}
wall = time.perf_counter() - t0
try:
ans = json.load(open(ap))
except Exception: # noqa: BLE001
return {"ok": False, "error": "no answer; exit %d: %s" % (p.returncode, err.decode(errors="replace")[-1500:])}
ans["wall_seconds"] = wall
ans["job"] = job
return self.corrupt(engine, ans)
def corrupt(self, engine, ans):
"""The injected fault, applied as the engine's own answer."""
f = self.inject
if not f or not ans.get("ok"):
return ans
import numpy as np
job = ans["job"]
if f == "amplitude" and engine == "aer.statevector" and job["kind"] == "state":
v = np.fromfile(job["state_out"], dtype=np.complex128)
v[len(v) // 2] += 1e-9
v.tofile(job["state_out"])
elif f == "bit_order" and engine == "wai.statevector" and job["kind"] == "state" and job["n"] > 1:
import engines
v = np.fromfile(job["state_out"], dtype=np.complex128)
engines.reverse_qubits(v, job["n"]).tofile(job["state_out"])
elif f == "counts" and engine == "stim.tableau" and job["kind"] == "counts":
c = ans["counts"]
possible = [k for k, p in exact.distribution(job["n"], job["bits"], job["steps"]).items() if p > 0]
top = max(c, key=c.get)
dest = min((k for k in possible if k != top), key=lambda k: c.get(k, 0), default=None)
if dest is not None:
moved = c[top] // 20
c[top] -= moved
c[dest] = c.get(dest, 0) + moved
elif f == "counts_spread" and engine == "aer.statevector" and job["kind"] == "counts":
c = ans["counts"]
keys = sorted(c)
total = sum(c.values())
for i, k in enumerate(keys):
c[k] += (c[k] // 10) * (1 if i % 2 == 0 else -1)
c[keys[0]] += total - sum(c.values())
elif f == "dem" and engine == "wai.qec" and job["kind"] == "qec_model" and ans["mechanisms"]:
m = max(ans["mechanisms"], key=lambda x: x[0])
m[0] *= 1 + 1e-6
elif f == "qec_rate" and engine == "wai.qec" and job["kind"] == "qec_sample" and ans.get("detectors"):
d = ans["detectors"]
i = max(range(len(d)), key=lambda j: d[j])
d[i] += d[i] // 4
elif f == "stabilizer" and engine == "stim.tableau" and job["kind"] == "stabilizers":
last = ans["stabilizers"][-1]
ans["stabilizers"][-1] = ("-" if last[0] == "+" else "+") + last[1:]
elif f == "fixed_point" and engine == "wai.exact" and job["kind"] == "state":
fx = ans["settings"]["fixed"]
a = np.load(fx)
a[0, 0] ^= 1
np.save(fx, a)
elif f == "drop_gate" and engine == "wai.exact" and job["kind"] == "state":
ops = ans["ops"]
last = max((i for i, op in enumerate(ops) if op[0] == 9), default=None)
if last is not None:
del ops[last]
ref = referee()
amps = ref.simulate(job["n"], [ref.gate(op, t, tuple(c), p) for op, c, t, p in ops], ref.derive_v2()[0])
ints = np.array([[int(a), int(b)] for a, b in amps], dtype=np.int64)
np.save(ans["settings"]["fixed"], ints)
digest = ref.statevector_hash(job["n"], amps)
ans["state_hash"] = digest.hex() if isinstance(digest, (bytes, bytearray)) else digest
((ints[:, 0] / float(1 << 30)) + 1j * (ints[:, 1] / float(1 << 30))).tofile(job["state_out"])
elif f == "qec_short" and engine == "wai.qec" and job["kind"] == "qec_sample":
ans["observables"] = []
elif f == "exact_turn" and engine == "wai.exact" and job["kind"] == "state":
fx = ans["settings"]["fixed"]
a = np.load(fx)
z = (a[:, 0] + 1j * a[:, 1]) * complex(math.cos(0.5), math.sin(0.5))
a[:, 0], a[:, 1] = np.round(z.real).astype(np.int64), np.round(z.imag).astype(np.int64)
np.save(fx, a)
((a[:, 0] / float(1 << 30)) + 1j * (a[:, 1] / float(1 << 30))).tofile(job["state_out"])
elif f == "phase_flip" and engine == "wai.statevector" and job["kind"] == "state":
v = np.fromfile(job["state_out"], dtype=np.complex128)
(-v).tofile(job["state_out"])
elif f == "mps_phase" and engine == "aer.mps" and job["kind"] == "amplitudes":
t = complex(math.cos(0.5), math.sin(0.5))
ans["amplitudes"] = {b: [(complex(*a) * t).real, (complex(*a) * t).imag] for b, a in ans["amplitudes"].items()}
elif f == "counts_collapse" and engine == "stim.tableau" and job["kind"] == "counts":
c = ans["counts"]
ans["counts"] = {sorted(c)[0]: sum(c.values())}
elif f == "drop_phase" and engine == "wai.statevector" and job["kind"] == "state" and job.get("probe") is not None:
steps = [s for i, s in enumerate(job["steps"]) if i != job["probe"]]
w = exact.run_unitary(job["n"], steps)
np.array([complex(float(Fraction(r, exact.ONE)), float(Fraction(i, exact.ONE))) for r, i in zip(w.re, w.im)]).tofile(job["state_out"])
return ans
_REFEREE = None
def referee():
"""The conformance corpus's independent integer reader of the bit-exact method (section 3.0)."""
global _REFEREE
if _REFEREE is None:
import importlib.util
spec = importlib.util.spec_from_file_location("referee", os.path.join(WAI, "quantum-conformance", "tools", "referee.py"))
_REFEREE = importlib.util.module_from_spec(spec)
spec.loader.exec_module(_REFEREE)
return _REFEREE
class Report:
def __init__(self):
self.checks = []
def add(self, entry, engine, quantity, measured, tolerance, ok, note=None, status=None):
c = {"entry": entry, "engine": engine, "quantity": quantity, "measured": measured, "tolerance": tolerance, "status": status or ("pass" if ok else "fail")}
if note:
c["note"] = note
self.checks.append(c)
return c
def not_run(self, entry, engine, quantity, why):
return self.add(entry, engine, quantity, None, None, True, note=why, status="not run")
# ------------------------------------------------------------------ the engine under test, held to its answers
class Expected:
"""corpus/expected-refusals.json: the refusals by the engine under test a run accepts."""
def __init__(self):
self.rows = json.load(open(os.path.join(ROOT, "corpus", "expected-refusals.json")))["refusals"]
self.by = {(r["entry"], r["engine"]): r for r in self.rows}
self.answered = set()
def get(self, entry, engine):
return self.by.get((entry, engine))
EXPECTED = None
def under_test(engine):
return engine.startswith("wai.")
def words(ans):
return ans.get("refused") or ans.get("unsupported") or ans.get("error") or ("did not finish within %s s" % ans.get("timeout"))
def no_answer(rep, entry, eng, ans, quantity, fail_on_error=False):
"""An engine gave no answer. From the engine under test, a refusal that expected-refusals.json
lists for this entry, in its listed words, is recorded as `refused`; anything else (a crash, a
time-out, a refusal not listed, or in other words) fails the run. From another engine it is
`not run`, with its words, or a failure where it crashed on a check it must make."""
why = str(words(ans))
if under_test(eng):
x = EXPECTED.get(entry, eng)
if x and ans.get("refused") and x["text"] in ans["refused"]:
return rep.add(entry, eng, "refuses, as listed: " + x["reason"], ans["refused"], x["text"], True, status="refused")
kind = "it crashed" if ans.get("error") else "it did not finish" if ans.get("timeout") else "a refusal no list holds" if not x else "a refusal in other words than its list's"
return rep.add(entry, eng, "%s: the engine under test gave no answer (%s)" % (quantity, kind), why[:1500], None, False)
if fail_on_error and ans.get("error"):
return rep.add(entry, eng, quantity, None, None, False, note=why)
return rep.not_run(entry, eng, quantity, why)
def answered(rep, entry, eng):
"""The engine under test answered. Where it must refuse, that fails the run."""
if under_test(eng):
x = EXPECTED.get(entry, eng)
if x:
EXPECTED.answered.add((entry, eng))
if x["must"] == "refuse":
rep.add(entry, eng, "accepts a circuit it must refuse: " + x["reason"], True, False, False)
return False
return True
# ------------------------------------------------------------------ states
def load_state(path):
import numpy as np
return np.fromfile(path, dtype=np.complex128)
def exact_distance(ref, vec):
"""(strict, modulo a global phase, the phase) between the integer reference and a state."""
re = exact.ints_from_floats([float(x) for x in vec.real])
im = exact.ints_from_floats([float(x) for x in vec.imag])
d, dm, phase, _, _ = exact.distance(ref.re, ref.im, re, im)
return d, dm, phase
def float_distance(a, b):
"""(strict, modulo a global phase) between two double states, the sums taken exactly enough:
math.fsum over the squares."""
import numpy as np
def norm(v):
return math.sqrt(math.fsum((v.real**2).tolist()) + math.fsum((v.imag**2).tolist()))
d = norm(a - b)
# Turn b by the phase that best aligns it with a, then measure the difference itself:
# sqrt(|a|^2 + |b|^2 - 2|<b|a>|) would lose half its digits to cancellation.
ip = np.vdot(b, a)
turn = ip / abs(ip) if abs(ip) > 0 else 1.0
dm = norm(a - turn * b)
return d, dm
def ref_vector(ref):
import numpy as np
return np.array([complex(float(Fraction(r, exact.ONE)), float(Fraction(i, exact.ONE))) for r, i in zip(ref.re, ref.im)])
def state_tolerance(engine, ans, e):
gates = tol.gate_count(e["steps"])
n = e["n"]
if engine == "wai.exact":
return tol.fixed_point_bound(n, ans["ops"]), "the extension's published bound (quantum-sim.md section 5), summed over the op-log the engine ran"
if engine in ("wai.statevector",):
return tol.double_state(gates, n, width=3), "double precision, fusion width 3"
if engine in ("aer.statevector", "aer.qasm_import"):
g = max(gates, ans.get("settings", {}).get("gates", gates))
width = 5 if n >= 14 else 2
return tol.double_state(g, n, width=width), "double precision, fusion up to %d qubits from 14 qubits" % 5
if engine == "quimb.dense":
return tol.double_state(max(gates, ans.get("gates", gates)), n, width=3), "double precision, gate by gate"
if engine == "quimb.tensor_network":
w = ans.get("settings", {}).get("contraction_width") or n
return tol.contraction(ans.get("gates", gates) + n, min(float(w), 40.0)), "double precision, a contraction of width %.1f" % float(w)
if engine == "ddsim.statevector":
return tol.decision_diagram(gates, n), "decision-diagram edge weights merged within 2^-42"
if engine == "qsim.statevector":
return tol.single_state(gates, n, width=2), "single precision, fusion width 2"
raise KeyError(engine)
def run_oplog(n, ops):
"""The bit-exact method's op-log run in double precision: its ideal state, to a double's
rounding. Base gates are quantum-sim.md's (section 3.0): I X Y Z H S S† T T† P = 0..9, P(k)
the phase e^(2πi/2^k); qubit 0 is the lowest bit of the index."""
import numpy as np
r = 1 / math.sqrt(2)
fixed = {0: ((1, 0), (0, 1)), 1: ((0, 1), (1, 0)), 2: ((0, -1j), (1j, 0)), 3: ((1, 0), (0, -1)), 4: ((r, r), (r, -r)), 5: ((1, 0), (0, 1j)), 6: ((1, 0), (0, -1j)), 7: ((1, 0), (0, complex(r, r))), 8: ((1, 0), (0, complex(r, -r)))}
v = np.zeros(1 << n, dtype=np.complex128)
v[0] = 1
idx = np.arange(1 << n)
pairs = {}
for base, controls, target, param in ops:
m = ((1, 0), (0, np.exp(2j * math.pi / (1 << param)) if param > 2 else (-1 if param == 1 else 1j))) if base == 9 else fixed[base]
key = (tuple(controls), target)
if key not in pairs:
mask = 0
for c in controls:
mask |= 1 << c
lo = idx[((idx & mask) == mask) & ((idx >> target) & 1 == 0)]
pairs[key] = (lo, lo | (1 << target))
lo, hi = pairs[key]
a, b = v[lo], v[hi]
v[lo], v[hi] = m[0][0] * a + m[0][1] * b, m[1][0] * a + m[1][1] * b
return v
def oplog_check(rep, e, n, ops, want):
"""The op-log the bit-exact method ran is the circuit: the op-log's ideal state against the
circuit's, modulo the global phase the lowering drops. Within the double rounding of that run,
the distance the reader's rule moves angles (corpus.reader_class: a phase moved by δ turns
changes a state by at most 2πδ), and 2^-50 for the reference's conversion to double. The
referee then ties the integers to the op-log, so this ties them to the circuit."""
cls, moved = corpus.reader_class(e)
_, dm = float_distance(want, run_oplog(n, ops))
bound = tol.double_state(len(ops), n, width=1) + 2 * math.pi * float(moved) + 2.0**-50
note = "the reader moved its angles by %.3g of a turn in all, as its rule has it" % float(moved) if cls == "moved" else None
rep.add(e["id"], "wai.exact", "the op-log is the circuit: the op-log's ideal state against the circuit's, modulo a global phase", dm, bound, dm <= bound, note=note)
def referee_hash(n, ops, fixed, engine_hash):
"""The referee's integer simulator run on the engine's op-log must give the integers the
engine wrote, and the referee's BLAKE3 of them the hash the engine reported."""
ref = referee()
table = ref.derive_v2()[0]
oplist = [ref.gate(op, t, tuple(c), p) for op, c, t, p in ops]
amps = ref.simulate(n, oplist, table)
mine = [(int(a), int(b)) for a, b in fixed]
same = [tuple(x) for x in amps] == mine
digest = ref.statevector_hash(n, amps)
digest = digest.hex() if isinstance(digest, (bytes, bytearray)) else digest
if not same:
return {"match": False, "note": "the referee's state differs from the engine's"}
if digest != engine_hash:
return {"match": False, "note": "the referee's hash %s, the engine's %s" % (digest, engine_hash)}
return {"match": True}
def exact_answer(rep, e, n, ans, want, quantity="state"):
"""The bit-exact method's checks beside its distance: the op-log is the circuit, and the
referee reruns the op-log to the engine's integers and hash (to 14 qubits)."""
import numpy as np
fixed = np.load(ans["settings"]["fixed"])
oplog_check(rep, e, n, ans["ops"], want)
if n <= 14:
h = referee_hash(n, ans["ops"], fixed, ans["state_hash"])
rep.add(e["id"], "wai.exact", "state hash, recomputed by the referee from the op-log and from the integers written", h["match"], True, h["match"], note=h.get("note"))
return fixed
def reference_checks(e, ref, rep):
"""A closed form, where there is one, holds the integer reference itself."""
n = e["n"]
unit = "units of 2^-256"
if "qft_x" in e:
worst = max(abs(exact.qft_amplitude(n, e["qft_x"], y)[0] - ref.re[y]) + abs(exact.qft_amplitude(n, e["qft_x"], y)[1] - ref.im[y]) for y in range(1 << n))
rep.add(e["id"], "exact", "integer reference against the closed form, " + unit, worst, REFERENCE_UNITS, worst <= REFERENCE_UNITS)
if e["family"] == "ghz":
r = exact.rsqrt2()
worst = max(abs(ref.re[i] - (r if i in (0, (1 << n) - 1) else 0)) + abs(ref.im[i]) for i in range(1 << n))
rep.add(e["id"], "exact", "integer reference against the closed form, " + unit, worst, REFERENCE_UNITS, worst <= REFERENCE_UNITS)
if "grover" in e:
m, k = e["grover"]
p = ref.re[m] ** 2 + ref.im[m] ** 2
err = abs(p - exact.grover_law(n, k)) >> exact.PREC
rep.add(e["id"], "exact", "integer reference: P(marked) against sin²((2k+1)θ), computed in integers, " + unit, err, REFERENCE_UNITS, err <= REFERENCE_UNITS)
if "qpe" in e:
m, bits = e["qpe"]
i = m | (1 << bits)
err = abs(ref.re[i] ** 2 + ref.im[i] ** 2 - (1 << (2 * exact.PREC))) >> exact.PREC
rep.add(e["id"], "exact", "integer reference: P(m) against 1, the closed form, " + unit, err, REFERENCE_UNITS, err <= REFERENCE_UNITS)
if "qpe_third" in e:
bits = e["qpe_third"]
worst = 0
for y in range(1 << bits):
i = y | (1 << bits)
worst = max(worst, abs(ref.re[i] ** 2 + ref.im[i] ** 2 - exact.qpe_third_law(bits, y)) >> exact.PREC)
rep.add(e["id"], "exact", "integer reference: every outcome's probability against sin²(π2^tδ)/(2^2t sin²(πδ)), " + unit, worst, REFERENCE_UNITS, worst <= REFERENCE_UNITS)
def probe_states(e):
"""The circuit with its probed gate left out, and that gate's angle in radians."""
steps = [s for i, s in enumerate(e["steps"]) if i != e["probe"]]
return exact.run_unitary(e["n"], steps), abs(exact.float_radians(e["steps"][e["probe"]]["a"][0]))
def phase_held(rep, e, eng, ref, probe, re_ints, im_ints):
"""Whether an engine holds the probed gate or drops it: its distance alone cannot tell them
apart once the gate's effect is below the engine's bound. Exactly, in integers, modulo a global
phase: it holds the gate when its answer is within half the gate's effect of the ideal state, and
drops it when within half of the state without the gate. When it is within half of neither, its
own rounding is larger than the gate's effect, and the answer says nothing either way: that is
recorded and not judged. The share of the step it took (exact.held_share) is recorded beside."""
without, theta = probe
share = exact.held_share(ref, without, re_ints, im_ints)
step = exact.distance(ref.re, ref.im, without.re, without.im)[1]
to_ideal = exact.distance(ref.re, ref.im, re_ints, im_ints)[1]
to_without = exact.distance(without.re, without.im, re_ints, im_ints)[1]
verdict = "holds it" if to_ideal <= step / 2 else "drops it" if to_without <= step / 2 else None
q = "holds the phase of its small-angle gate: the share of the step from the state without that gate to the ideal (1 holds it, 0 drops it)"
if verdict is None:
c = rep.add(e["id"], eng, q, share, [0.5, 1.5], True, status="info", note="neither can be told: its distance from the ideal state, %.3g, is more than half the gate's effect, %.3g" % (to_ideal, step))
elif eng == "ddsim.statevector" and theta < DDSIM_PARAMETER_TOLERANCE:
c = rep.add(e["id"], eng, q, share, [0.5, 1.5], True, status="info", note="%s: the angle, %.3g rad, is below its parameter tolerance of %g rad, where it may read an angle as 0" % (verdict, theta, DDSIM_PARAMETER_TOLERANCE))
else:
c = rep.add(e["id"], eng, q, share, [0.5, 1.5], verdict == "holds it", note=verdict)
c["step"] = step
def extension_figure(rep, e, fixed):
"""quantum-sim.md section 5: P2(19) applied 2^18 times to |1> ends at modulus 1.0000189 and
within 10^-5 rad of the half turn. The modulus is stated to seven decimals."""
re, im = int(fixed[1][0]), int(fixed[1][1])
modulus = math.hypot(re, im) / (1 << 30)
off = abs(math.remainder(math.atan2(im, re) - math.pi, 2 * math.pi))
rep.add(e["id"], "wai.exact", "modulus of the half turn's amplitude against the extension's figure, 1.0000189 (section 5)", modulus, [1.00001885, 1.00001895], 1.00001885 <= modulus <= 1.00001895)
rep.add(e["id"], "wai.exact", "its angle from the half turn, in radians, which the extension states is within 1e-5", off, 1e-5, off <= 1e-5)
def check_state_entry(e, runner, rep, engines):
import numpy as np
n = e["n"]
ref = exact.run_unitary(n, e["steps"])
want = ref_vector(ref)
reference_checks(e, ref, rep)
strict = bool(e.get("phase_is_result"))
probe = probe_states(e) if "probe" in e else None
exact_ops = None
jobs = {}
for eng in engines:
if eng == "aer.qasm_import" and n > 14:
continue
if eng == "quimb.tensor_network" and n > 12:
continue
jobs[eng] = {"id": e["id"], "kind": "state", "n": n, "steps": e["steps"], "qasm": e["qasm"], "probe": e.get("probe")}
answers = run_all(runner, jobs)
quantity = "distance from the exact state" + (", with its phase" if strict else ", modulo a global phase")
for eng, ans in answers.items():
if not ans.get("ok"):
no_answer(rep, e["id"], eng, ans, "state", fail_on_error=True)
continue
if not answered(rep, e["id"], eng):
continue
vec = load_state(ans["job"]["state_out"])
if len(vec) != 1 << n:
rep.add(e["id"], eng, "state length", len(vec), 1 << n, False)
continue
if eng == "wai.exact":
fixed = np.load(ans["settings"]["fixed"])
re = exact.ints_from_fixed([int(x) for x in fixed[:, 0]])
im = exact.ints_from_fixed([int(x) for x in fixed[:, 1]])
d, dm, phase, _, _ = exact.distance(ref.re, ref.im, re, im)
bound, why = state_tolerance(eng, ans, e)
measured = d if strict else dm
c = rep.add(e["id"], eng, quantity, measured, bound, measured <= bound, note=why)
c["strict"] = d
c["global_phase"] = phase
# The phase the method's result states: with it, the state is the reference's, which writes
# each gate as method statevector does, nothing fitted.
turn = complex(math.cos(ans.get("global_phase", 0.0)), math.sin(ans.get("global_phase", 0.0)))
stated = exact.distance(ref.re, ref.im, *[exact.ints_from_floats([float(x) for x in part]) for part in ((vec * turn).real, (vec * turn).imag)])[0]
rep.add(e["id"], eng, "distance from the exact state, with the global phase its result states", stated, bound + 8 * tol.U_DOUBLE, stated <= bound + 8 * tol.U_DOUBLE)
exact_ops = ans["ops"]
exact_answer(rep, e, n, ans, want)
if e.get("extension_figure"):
extension_figure(rep, e, fixed)
continue
bound, why = state_tolerance(eng, ans, e)
d, dm, phase = exact_distance(ref, vec)
measured = d if strict else dm
c = rep.add(e["id"], eng, quantity, measured, bound, measured <= bound, note=why)
c["strict"] = d
c["global_phase"] = phase
c["seconds"] = ans.get("seconds")
if ans.get("notes"):
c["adapter"] = ans["notes"]
if probe:
phase_held(rep, e, eng, ref, probe, exact.ints_from_floats([float(x) for x in vec.real]), exact.ints_from_floats([float(x) for x in vec.imag]))
if is_clifford(e) and e["expect"] == "exact_state":
check_stabilizer_entry(e, runner, rep)
# Through the engine's whole path, as a user asks: chosen amplitudes, method auto.
picks = sorted({0, (1 << n) - 1, 1 % (1 << n)} | {max(range(1 << n), key=lambda i: ref.re[i] ** 2 + ref.im[i] ** 2)})
bits = [format(i, "0%db" % n) for i in picks]
ans = runner.run("wai.auto", {"id": e["id"], "kind": "amplitudes", "n": n, "steps": e["steps"], "bitstrings": bits})
if not ans.get("ok"):
no_answer(rep, e["id"], "wai.auto", ans, "chosen amplitudes through the job path")
return
planned = ans.get("planned")
# The bit-exact method lowers rz to a phase gate, and so on; its result states the phase that
# drops, and its amplitudes are held to the reference's with that phase, nothing fitted.
turn = 1
if planned == "exact":
stated_phase = ((ans.get("accuracy") or {}).get("global_phase") or {}).get("radians", 0.0)
turn = complex(math.cos(stated_phase), math.sin(stated_phase))
worst = 0.0
for b in bits:
a = complex(*ans["amplitudes"][b]) * turn
worst = max(worst, exact.amplitude_error((ref.re[int(b, 2)], ref.im[int(b, 2)]), a.real, a.imag))
if planned == "exact" and exact_ops is None:
# The bound is the op-log's; the bit-exact method gives it.
a2 = runner.run("wai.exact", {"id": e["id"], "kind": "state", "n": n, "steps": e["steps"]})
exact_ops = a2.get("ops") if a2.get("ok") else None
if planned == "exact" and exact_ops is not None:
bound = tol.fixed_point_bound(n, exact_ops) + 4 * tol.U_DOUBLE
else:
bound = tol.double_state(tol.gate_count(e["steps"]), n, width=3)
rep.add(e["id"], "wai.auto", "chosen amplitudes through the job path, worst error" + (", with the global phase the result states" if planned == "exact" else ""), worst, bound, worst <= bound, note="planned %s" % planned)
# ------------------------------------------------------------------ stabilizers
def check_stabilizer_entry(e, runner, rep):
n = e["n"]
tb = exact.Tableau(n)
for s in e["steps"]:
if "g" in s:
tb.gate(s["g"], s["q"])
rows, rank, x_rank = exact.reduced_group(tb.stabilizers(), n)
texts = [exact.pauli_text(r, n) for r in rows]
digest = hashlib.sha256("\n".join(texts).encode()).hexdigest()
rep.add(e["id"], "exact", "the integer tableau's group has full rank", rank, n, rank == n)
jobs = {"stim.tableau": {"id": e["id"], "kind": "stabilizers", "n": n, "steps": e["steps"]}, "wai.stabilizer": {"id": e["id"], "kind": "stabilizers", "n": n, "steps": e["steps"], "paulis": [t[1:] for t in texts]}}
if n <= 1000:
jobs["aer.stabilizer"] = {"id": e["id"], "kind": "stabilizers", "n": n, "steps": e["steps"]}
answers = run_all(runner, jobs)
for eng, ans in answers.items():
if not ans.get("ok"):
no_answer(rep, e["id"], eng, ans, "stabilizer group", fail_on_error=True)
continue
if not answered(rep, e["id"], eng):
continue
if eng == "wai.stabilizer":
want = [-1 if t[0] == "-" else 1 for t in texts]
wrong = sum(1 for a, b in zip(ans["values"], want) if a != b)
c = rep.add(e["id"], eng, "generators of the reference group whose expectation is not their sign", wrong, 0, wrong == 0 and len(ans["values"]) == n)
c["seconds"] = ans.get("seconds")
rep.add(e["id"], eng, "outcome dimension against the rank of the group's X part", ans["outcome_dimension"], x_rank, ans["outcome_dimension"] == x_rank)
continue
theirs = [exact.row_from_text(s) for s in ans["stabilizers"]]
r2, rank2, _ = exact.reduced_group(theirs, n)
d2 = hashlib.sha256("\n".join(exact.pauli_text(r, n) for r in r2).encode()).hexdigest()
c = rep.add(e["id"], eng, "reduced stabilizer group equals the reference's", d2 == digest, True, d2 == digest and rank2 == n)
c["seconds"] = ans.get("seconds")
def fixed_bits(z_masks, n):
"""The bits whose value the affine space fixes: bit i is fixed when e_i lies in the span of
the Z rows' masks (GF(2) elimination)."""
basis = {}
for m in z_masks:
while m:
top = m.bit_length() - 1
if top not in basis:
basis[top] = m
break
m ^= basis[top]
out = set()
for i in range(n):
v = 1 << i
while v:
top = v.bit_length() - 1
if top not in basis:
break
v ^= basis[top]
if v == 0:
out.add(i)
return out
def check_stabilizer_counts(e, runner, rep, alpha):
"""Outcomes of a Clifford circuit measured in Z: uniform on an affine space of dimension x_rank.
Every shot must lie in it; no outcome may come up more often than its probability 2^-x_rank
allows (by Bernstein, over all 2^x_rank of them); and each bit the space leaves free must read
1 half the time. A sampler confined to a smaller space that leaves every bit free is not seen at
this width; the README says so."""
n = e["n"]
tb = exact.Tableau(n)
gates = [s for s in e["steps"] if "g" in s]
for s in gates:
tb.gate(s["g"], s["q"])
rows, _, x_rank = exact.reduced_group(tb.stabilizers(), n)
z_rows = [r for r in rows if r[0] == 0]
free = [i for i in range(n) if i not in fixed_bits([r[1] for r in z_rows], n)]
shots = e["shots"]
p = 2.0**-x_rank
jobs = {eng: {"id": e["id"], "kind": "counts", "n": n, "bits": e["bits"], "steps": e["steps"], "shots": shots, "seed": 7} for eng in ("wai.stabilizer", "stim.tableau", "aer.stabilizer")}
for eng, ans in run_all(runner, jobs).items():
if not ans.get("ok"):
no_answer(rep, e["id"], eng, ans, "outcomes inside the stabilizer's affine space")
continue
if not answered(rep, e["id"], eng):
continue
counts = ans["counts"]
got = sum(counts.values())
rep.add(e["id"], eng, "shots returned", got, shots, got == shots)
outside = 0
for k, v in counts.items():
x = int(k, 2)
for zr in z_rows:
if ((zr[1] & x).bit_count() + zr[2]) % 2:
outside += v
break
rep.add(e["id"], eng, "shots outside the affine space the stabilizers fix (dimension %d)" % x_rank, outside, 0, outside == 0)
a = alpha / 3 / 2
t = tol.bernstein(shots, p, a / 2.0**x_rank)
most = max(counts.values(), default=0)
rep.add(e["id"], eng, "the most frequent outcome's count above shots·2^-%d, in units of its Bernstein bound over all 2^%d outcomes" % (x_rank, x_rank), (most - shots * p) / t, 1.0, most - shots * p <= t)
worst = 0.0
bad = []
for i in free:
ones = sum(v for k, v in counts.items() if int(k, 2) >> i & 1)
tb_ = tol.bernstein(shots, 0.5, a / max(len(free), 1))
worst = max(worst, abs(ones - shots / 2) / tb_)
if abs(ones - shots / 2) > tb_:
bad.append("bit %d: %d ones of %d" % (i, ones, shots))
rep.add(e["id"], eng, "each of the %d bits the space leaves free against 1/2, largest deviation in units of its Bernstein bound" % len(free), worst, 1.0, not bad, note="; ".join(bad[:4]) if bad else None)
rep.add(e["id"], eng, "distinct outcomes, of %d shots over 2^%d" % (shots, x_rank), len(counts), None, True, status="info")
# ------------------------------------------------------------------ sampled distributions
def check_counts_entry(e, runner, rep, plan):
dist = exact.distribution(e["n"], e["bits"], e["steps"])
engines = list(COUNT_ENGINES)
if is_clifford(e):
engines += ["wai.stabilizer", "stim.tableau"]
jobs = {eng: {"id": e["id"], "kind": "counts", "n": e["n"], "bits": e["bits"], "steps": e["steps"], "shots": e["shots"], "seed": 11} for eng in engines}
for eng, ans in run_all(runner, jobs).items():
if not ans.get("ok"):
no_answer(rep, e["id"], eng, ans, "counts", fail_on_error=True)
continue
if not answered(rep, e["id"], eng):
continue
plan.append((e, eng, ans, dist))
def judge_counts(plan, rep, alpha):
"""Every outcome's count against its exact probability, by Bernstein's inequality, and the
whole distribution at once against the exact one, in L1 distance (tolerance.py); each test
at a share of `alpha`, so that a correct engine fails them with probability at most alpha."""
tests = sum(len(set(d) | set(a["counts"])) + 1 for _, _, a, d in plan)
a = alpha / max(tests, 1)
for e, eng, ans, dist in plan:
shots = sum(ans["counts"].values())
if shots != e["shots"]:
rep.add(e["id"], eng, "shots returned", shots, e["shots"], False)
continue
worst = 0.0
bad = []
for k in sorted(set(dist) | set(ans["counts"])):
p = float(dist.get(k, 0))
x = ans["counts"].get(k, 0)
if p == 0:
if x:
bad.append("%s: %d shots, probability 0" % (k, x))
continue
t = tol.bernstein(shots, p, a)
dev = abs(x - shots * p)
worst = max(worst, dev / t)
if dev > t:
bad.append("%s: %d shots, expected %.1f ± %.1f" % (k, x, shots * p, t))
c = rep.add(e["id"], eng, "largest deviation of an outcome's count, in units of its Bernstein bound", worst, 1.0, not bad, note="; ".join(bad[:4]) if bad else None)
if ans.get("planned"):
c["planned"] = ans["planned"]
support = sum(1 for v in dist.values() if v > 0)
l1 = math.fsum(abs(ans["counts"].get(k, 0) / shots - float(dist.get(k, 0))) for k in set(dist) | set(ans["counts"]))
eps = tol.l1_distance(shots, support + 1, a)
rep.add(e["id"], eng, "L1 distance from the exact distribution, Σ|count/shots − p| over every outcome (twice the total variation), against its bound", l1, eps, l1 <= eps)
# ------------------------------------------------------------------ wider circuits
def check_mirror_entry(e, runner, rep):
import numpy as np
n = e["n"]
engines = ["wai.exact", "wai.statevector", "aer.statevector", "quimb.dense", "ddsim.statevector", "qsim.statevector"]
jobs = {eng: {"id": e["id"], "kind": "state", "n": n, "steps": e["steps"]} for eng in engines}
for eng, ans in run_all(runner, jobs).items():
if not ans.get("ok"):
no_answer(rep, e["id"], eng, ans, "distance from |0…0>")
continue
if not answered(rep, e["id"], eng):
continue
if eng == "wai.exact":
fixed = np.load(ans["settings"]["fixed"])
v = fixed[:, 0].astype(np.float64) / (1 << 30) + 1j * fixed[:, 1].astype(np.float64) / (1 << 30)
else:
v = load_state(ans["job"]["state_out"])
target = np.zeros_like(v)
target[0] = 1
d, dm = float_distance(v, target)
bound, why = state_tolerance(eng, ans, e)
c = rep.add(e["id"], eng, "distance from |0…0>, modulo a global phase", dm, bound, dm <= bound, note=why)
c["strict"] = d
c["seconds"] = ans.get("seconds")
if eng == "wai.exact":
exact_answer(rep, e, n, ans, target)
def check_pairwise_entry(e, runner, rep):
import numpy as np
n = e["n"]
engines = ["wai.statevector", "aer.statevector", "quimb.dense", "ddsim.statevector", "qsim.statevector"]
if corpus.reader_class(e)[0] == "exact":
engines.insert(0, "wai.exact")
jobs = {eng: {"id": e["id"], "kind": "state", "n": n, "steps": e["steps"]} for eng in engines}
got = {}
for eng, ans in run_all(runner, jobs).items():
if not ans.get("ok"):
no_answer(rep, e["id"], eng, ans, "state, against the other engines")
continue
if not answered(rep, e["id"], eng):
continue
if eng == "wai.exact":
fixed = np.load(ans["settings"]["fixed"])
v = fixed[:, 0].astype(np.float64) / (1 << 30) + 1j * fixed[:, 1].astype(np.float64) / (1 << 30)
else:
v = load_state(ans["job"]["state_out"])
got[eng] = (v, state_tolerance(eng, ans, e)[0], ans)
names = sorted(got)
for i, a in enumerate(names):
for b in names[i + 1:]:
_, dm = float_distance(got[a][0], got[b][0])
bound = got[a][1] + got[b][1]
rep.add(e["id"], "%s ~ %s" % (a, b), "distance between the two states, modulo a global phase", dm, bound, dm <= bound)
if "qft_x" in e:
r = corpus.Stream("pick/" + e["id"])
ys = [r.below(1 << n) for _ in range(64)]
for eng in names:
v = got[eng][0]
worst = max(exact.amplitude_error(exact.qft_amplitude(n, e["qft_x"], y), float(v[y].real), float(v[y].imag)) for y in ys)
rep.add(e["id"], eng, "64 amplitudes against the closed form, worst error, modulo nothing", worst, got[eng][1], worst <= got[eng][1])
def wide_amplitudes(e):
n = e["n"]
r = corpus.Stream("wide/" + e["id"])
if e.get("wide_kind") == "ghz":
bits = ["0" * n, "1" * n, "".join(str(r.below(2)) for _ in range(n))]
ref = {bits[0]: (exact.rsqrt2(), 0), bits[1]: (exact.rsqrt2(), 0), bits[2]: (0, 0)}
return bits, ref
if e.get("wide_kind") == "qft":
ys = [r.below(1 << n) for _ in range(6)]
bits = [format(y, "0%db" % n) for y in ys]
return bits, {b: exact.qft_amplitude(n, e["qft_x"], int(b, 2)) for b in bits}
if e["expect"] == "wide_mirror":
b = "0" * n
return [b], {b: (exact.ONE, 0)}
bits = ["".join(str(r.below(2)) for _ in range(n)) for _ in range(8)]
return bits, None
def mps_bound(eng, ans, e):
"""tolerance.mps for each engine's matrix product state, charged what that engine reports it
truncated: the engine under test its summed discarded weight over its truncations, the
simulator with a log its every truncation's weight and its two-site updates, and the library
with an absolute cutoff the most that cutoff drops at each update (engines.py)."""
steps = tol.svd_steps(e["steps"])
acc = ans.get("accuracy") or {}
if eng == "wai.mps":
chi = acc.get("max_bond_used") or acc.get("max_bond") or 64
trunc = tol.truncation_from(total=acc.get("discarded_weight", 0.0), count=acc.get("truncations", 0))
why = "%d two-site updates at bond %d; truncation from its reported discarded weight" % (steps, chi)
elif eng == "aer.mps":
log = ans["settings"]["mps_log"]
steps, chi = log["updates"], log["max_bond"]
trunc = tol.truncation_from(weights=log["discarded"])
why = "%d two-site updates at bond %d, and %d truncations, from its log" % (steps, chi, len(log["discarded"]))
else:
s = ans["settings"]
chi = s.get("max_bond_used") or 1
trunc = 2 * steps * math.sqrt(s["cutoff"])
why = "%d two-site updates at bond %d; its cutoff drops at most %g of weight at each" % (steps, chi, s["cutoff"])
return tol.mps(steps, chi, trunc), why
def check_wide_entry(e, runner, rep):
n = e["n"]
bits, ref = wide_amplitudes(e)
engines = ["wai.mps", "wai.tensor_network", "aer.mps", "quimb.mps", "quimb.tensor_network"]
if n <= 26:
engines += ["wai.statevector", "aer.statevector"]
engines = e.get("only_engines") or engines
jobs = {eng: {"id": e["id"], "kind": "amplitudes", "n": n, "steps": e["steps"], "bitstrings": bits, "memory_gb": 24, "max_bond": e.get("max_bond")} for eng in engines}
got = {}
gates = tol.gate_count(e["steps"])
smallest = min((math.hypot(*ref[b]) / exact.ONE for b in bits if ref and any(ref[b])), default=None) if ref else None
for eng, ans in run_all(runner, jobs).items():
if not ans.get("ok"):
no_answer(rep, e["id"], eng, ans, "amplitudes")
continue
if not answered(rep, e["id"], eng):
continue
if eng in ("wai.mps", "aer.mps", "quimb.mps"):
bound, why = mps_bound(eng, ans, e)
elif eng in ("wai.tensor_network", "quimb.tensor_network"):
if "contraction_flops" in ans:
w = math.log2(max(ans["contraction_flops"], 2.0))
else:
w = float(ans.get("settings", {}).get("contraction_width") or n)
bound = tol.contraction(gates + n, min(w, 40.0))
why = "contraction of width %.1f" % w
else:
bound = tol.double_state(gates, n, width=5)
why = "double precision state vector"
got[eng] = (ans["amplitudes"], bound, ans)
if ref is not None:
worst = max(exact.amplitude_error(ref[b], *ans["amplitudes"][b]) for b in bits)
# A bound as large as half the smallest amplitude checked cannot see a wrong phase on it.
if smallest is not None and bound >= smallest / 2:
c = rep.add(e["id"], eng, "amplitudes against the closed form, worst error", worst, bound, True, status="info", note=why + "; this bound is not below half the amplitudes it checks (%.3g), so it cannot tell a wrong phase" % smallest)
else:
c = rep.add(e["id"], eng, "amplitudes against the closed form, worst error", worst, bound, worst <= bound, note=why)
c["seconds"] = ans.get("seconds")
if ans.get("accuracy"):
c["accuracy"] = ans["accuracy"]
if ref is None:
names = sorted(got)
for i, a in enumerate(names):
for b in names[i + 1:]:
worst = max(abs(complex(*got[a][0][x]) - complex(*got[b][0][x])) for x in bits)
bound = got[a][1] + got[b][1]
rep.add(e["id"], "%s ~ %s" % (a, b), "chosen amplitudes, worst difference", worst, bound, worst <= bound)
def check_expectation_entry(e, runner, rep):
kind, p, k = e["closed"]
p = Fraction(p)
if kind == "depol1":
ref = (-1) ** k * (1 - 4 * p / 3) ** k
else:
ref = (1 - 16 * p / 15) ** k
jobs = {eng: {"id": e["id"], "kind": "expectation", "n": e["n"], "steps": e["steps"], "noise": e["noise"], "observable": e["observable"]} for eng in ("wai.density_matrix", "aer.density_matrix")}
for eng, ans in run_all(runner, jobs).items():
if not ans.get("ok"):
no_answer(rep, e["id"], eng, ans, "expectation under noise")
continue
if not answered(rep, e["id"], eng):
continue
d = abs(Fraction(ans["value"]) - ref)
bound = tol.density(k, e["n"])
rep.add(e["id"], eng, "<%s> against the closed form" % e["observable"], float(d), bound, float(d) <= bound)
# ------------------------------------------------------------------ error correction
def qec_text(q, runner, rep):
"""The committed circuit, which the pinned generator must still write byte for byte."""
text = open(os.path.join(ROOT, "corpus", "qec", q["id"] + ".circuit")).read()
ans = runner.run("stim.generate", dict(q, kind="generate"))
same = ans.get("ok") and ans.get("text") == text
rep.add(q["id"], "stim.generate", "the pinned generator writes the committed circuit, byte for byte", bool(same), True, bool(same), note=None if same else (ans.get("error") or "the text differs"))
return text
def merged(mechs):
out = {}
for p, ds, ls in mechs:
key = (tuple(ds), tuple(ls))
q = out.get(key, 0.0)
out[key] = q + p - 2 * q * p
return out
def check_qec_entry(q, text, runner, rep, qec_plan):
jobs = {eng: {"id": q["id"], "kind": "qec_model", "text": text} for eng in ("wai.qec", "stim.tableau")}
models = run_all(runner, jobs)
a, b = models["wai.qec"], models["stim.tableau"]
if not a.get("ok"):
no_answer(rep, q["id"], "wai.qec", a, "detector error model")
if not b.get("ok"):
rep.not_run(q["id"], "stim.tableau", "detector error model", str(words(b))[:300])
return
if not answered(rep, q["id"], "wai.qec"):
return
if not b.get("ok"):
# A channel the other engine gives no exact model of (a general Pauli channel): its
# sampler still holds the engine under test's model, by the rates below.
rep.not_run(q["id"], "stim.tableau", "detector error model", "gives no model of this circuit, which the engine under test models: %s" % str(words(b))[:300])
shots = 200000 if q["tier"] == "ci" else 1000000
sj = {eng: {"id": q["id"], "kind": "qec_sample", "text": text, "shots": shots, "seed": 5} for eng in ("wai.qec", "stim.tableau")}
qec_plan.append((q, merged(a["mechanisms"]), a["detectors"], a["observables"], run_all(runner, sj), shots))
return
rep.add(q["id"], "wai.qec ~ stim.tableau", "detectors and observables", [a["detectors"], a["observables"]], [b["detectors"], b["observables"]], [a["detectors"], a["observables"]] == [b["detectors"], b["observables"]])
ma, mb = merged(a["mechanisms"]), merged(b["mechanisms"])
only_a = sorted(set(ma) - set(mb))
only_b = sorted(set(mb) - set(ma))
sites = text_noise_sites(text)
bound = tol.dem(sites)
worst = max([abs(ma[k] - mb[k]) for k in set(ma) & set(mb)] or [0.0])
ok = not only_a and not only_b and worst <= bound
note = None
if only_a or only_b:
note = "symptom sets only in the engine under test: %d, only in the other: %d (first: %s / %s)" % (len(only_a), len(only_b), only_a[:1], only_b[:1])
c = rep.add(q["id"], "wai.qec ~ stim.tableau", "mechanisms: same symptom sets, probabilities within the bound (largest difference)", worst, bound, ok, note=note)
c["mechanisms"] = [len(ma), len(mb)]
c["seconds"] = [a.get("seconds"), b.get("seconds")]
shots = 200000 if q["tier"] == "ci" else 1000000
sj = {eng: {"id": q["id"], "kind": "qec_sample", "text": text, "shots": shots, "seed": 5} for eng in ("wai.qec", "stim.tableau")}
samples = run_all(runner, sj)
qec_plan.append((q, ma, a["detectors"], a["observables"], samples, shots))
def text_noise_sites(text):
"""How many noise channels a circuit applies, its blocks unrolled: an upper bound on how many
channels one mechanism can merge."""
import re
total = 0
stack = [1]
for line in text.splitlines():
s = line.strip()
if s.startswith("REPEAT"):
stack.append(stack[-1] * int(s.split()[1]))
continue
if s == "}":
stack.pop()
continue
m = re.match(r"([A-Z_0-9]+)(\[[^\]]*\])?\(([^)]*)\)\s*(.*)", s)
if m and (m.group(1).endswith("ERROR") or m.group(1).startswith(("DEPOLARIZE", "PAULI_CHANNEL")) or m.group(1) in ("E", "M", "MX", "MR", "MRX", "MY")):
total += stack[-1] * max(len(m.group(4).split()), 1)
return total
def judge_qec(qec_plan, rep, alpha):
"""Each detector's and observable's firing count against the rate the model gives,
by Bernstein's inequality at a share of `alpha`."""
tests = sum(d + o for _, _, d, o, _, _ in qec_plan) * 2
a = alpha / max(tests, 1)
for q, model, nd, no, samples, shots in qec_plan:
# The rate at which a detector fires: an odd number of its mechanisms, each independent.
prod_d = [1.0] * nd
prod_o = [1.0] * no
for (ds, ls), p in model.items():
for d in ds:
prod_d[d] *= 1 - 2 * p
for o in ls:
prod_o[o] *= 1 - 2 * p
rates = [(1 - x) / 2 for x in prod_d] + [(1 - x) / 2 for x in prod_o]
for eng, ans in samples.items():
if not ans.get("ok"):
no_answer(rep, q["id"], eng, ans, "detection rates")
continue
shape = [len(ans.get("detectors") or []), len(ans.get("observables") or []), ans.get("shots")]
if shape != [nd, no, shots]:
rep.add(q["id"], eng, "detectors, observables and shots returned", shape, [nd, no, shots], False)
continue
counts = ans["detectors"] + ans["observables"]
worst = 0.0
bad = []
for i, (x, r) in enumerate(zip(counts, rates)):
if r == 0:
if x:
bad.append("%d fired %d times at rate 0" % (i, x))
continue
t = tol.bernstein(shots, r, a)
dev = abs(x - shots * r)
worst = max(worst, dev / t)
if dev > t:
bad.append("%s %d: %d, expected %.1f ± %.1f" % ("D" if i < nd else "L", i if i < nd else i - nd, x, shots * r, t))
c = rep.add(q["id"], eng, "detection and flip rates against the model, largest deviation in units of its Bernstein bound", worst, 1.0, not bad, note="; ".join(bad[:3]) if bad else None)
c["seconds"] = ans.get("seconds")
# ------------------------------------------------------------------ the run
def run_all(runner, jobs):
with ThreadPoolExecutor(max_workers=runner.jobs) as ex:
futs = {eng: ex.submit(runner.run, eng, job) for eng, job in jobs.items()}
return {eng: f.result() for eng, f in futs.items()}
def host():
info = {"system": platform.system(), "machine": platform.machine(), "release": platform.release()}
try:
if platform.system() == "Darwin":
info["cpu"] = subprocess.run(["sysctl", "-n", "machdep.cpu.brand_string"], capture_output=True, text=True).stdout.strip()
info["memory_bytes"] = int(subprocess.run(["sysctl", "-n", "hw.memsize"], capture_output=True, text=True).stdout)
info["os"] = "macOS " + platform.mac_ver()[0]
else:
for line in open("/proc/cpuinfo"):
if line.startswith("model name"):
info["cpu"] = line.split(":", 1)[1].strip()
break
for line in open("/proc/meminfo"):
if line.startswith("MemTotal"):
info["memory_bytes"] = int(line.split()[1]) * 1024
try:
for line in open("/etc/os-release"):
if line.startswith("PRETTY_NAME="):
info["os"] = line.split("=", 1)[1].strip().strip('"')
except OSError:
pass
info["logical_cpus"] = os.cpu_count()
info["load_average"] = list(os.getloadavg())
except Exception: # noqa: BLE001
pass
return info
def run_reproducer(path):
"""A committed reproducer of another engine's disagreement: it prints one JSON object per
line, each a claim, what it saw, and whether the claim holds."""
out = subprocess.run([sys.executable, "-I", os.path.join(ROOT, path)], capture_output=True, text=True, timeout=600)
claims = []
for line in out.stdout.splitlines():
line = line.strip()
if line.startswith("{"):
claims.append(json.loads(line))
if out.returncode != 0 and not claims:
claims.append({"claim": "the reproducer runs", "observed": out.stderr[-500:], "holds": False})
return claims
def apply_known(rep, entries_run):
"""A disagreement of another engine with the exact reference, found and examined before, is
listed in corpus/known-disagreements.json with what was found and a reproducer committed beside
it. A failing check that matches one is reported as `known` rather than as a failure of this
run, and the reproducer's claims are rerun and recorded; one listed that no longer shows is
reported too. Only other engines may be listed: a failure of the engine under test is always a
failure."""
known = json.load(open(os.path.join(ROOT, "corpus", "known-disagreements.json")))["disagreements"]
seen = set()
for i, k in enumerate(known):
if k["engine"].startswith("wai."):
rep.add(k["entry"], k["engine"], "a known disagreement listed for the engine under test", True, False, False, note="only other engines may be listed")
seen.add(i)
if not os.path.exists(os.path.join(ROOT, k.get("reproducer", ""))) or not k.get("reproducer"):
rep.add(k["entry"], k["engine"], "a known disagreement has a reproducer committed beside it", k.get("reproducer"), True, False)
seen.add(i)
for c in rep.checks:
if c["status"] != "fail":
continue
for i, k in enumerate(known):
quantities = k["quantity"] if isinstance(k["quantity"], list) else [k["quantity"]]
if i not in seen and c["entry"] == k["entry"] and c["engine"] == k["engine"] and any(x in c["quantity"] for x in quantities):
c["status"] = "known"
c["note"] = ((c.get("note") or "") + " | known: " + k["finding"]).strip(" |")
c["right"] = k["right"]
k["_matched"] = True
done = set()
for k in known:
if k["entry"] not in entries_run or k["engine"].startswith("wai."):
continue
if not k.get("_matched"):
rep.add(k["entry"], k["engine"], "a listed disagreement no longer shows", False, True, True, status="info", note=k["finding"])
if k.get("reproducer") and k["reproducer"] not in done and os.path.exists(os.path.join(ROOT, k["reproducer"])):
done.add(k["reproducer"])
for cl in run_reproducer(k["reproducer"]):
rep.add(k["entry"], k["engine"], "reproducer (%s): %s" % (k["reproducer"], cl["claim"]), cl["observed"], cl.get("expected"), True, status="known" if cl["holds"] else "info", note=None if cl["holds"] else "the claim no longer holds")
def check_expected(rep, entries_run):
"""The expected-refusals file is what corpus.py's rule and list give, and a listed refusal that
no longer comes is reported (a `may` that the engine now answers)."""
want = corpus.expected_refusals()
rep.add("corpus", "corpus.py", "corpus/expected-refusals.json is the reader's rule and corpus.py's list", EXPECTED.rows == want, True, EXPECTED.rows == want)
for entry, eng in sorted(EXPECTED.answered):
x = EXPECTED.get(entry, eng)
if x["must"] == "may" and entry in entries_run:
rep.add(entry, eng, "a listed refusal no longer comes: the engine answered", True, False, True, status="info", note=x["reason"])
def phase_table_check(rep):
"""quantum-sim.md section 3.2's table: P(k) for k = 1..32, cos and sin of 2π/2^k rounded to the
nearest 2^-30. The integer reference must give the referee's table, entry for entry."""
table = referee().derive_v2()[0]
half = 1 << (exact.PREC - 31)
wrong = []
for k in range(1, 33):
c, s = exact.cos_sin("t:1/%d" % k)
mine = ((c + half) >> (exact.PREC - 30), (s + half) >> (exact.PREC - 30))
if mine != tuple(table[k]):
wrong.append(k)
rep.add("corpus", "exact", "the extension's phase table P(1..32): the integer reference's cos and sin, rounded to the nearest 2^-30, against the referee's", wrong, [], not wrong)
def manifest_check(es, rep):
"""The generator still gives the bytes the manifest pins."""
m = json.load(open(os.path.join(ROOT, "corpus", "manifest.json")))
pinned = {r["id"]: r.get("sha256") for r in m["circuits"]}
drift = [e["id"] for e in es if pinned.get(e["id"]) != hashlib.sha256(e["qasm"].encode()).hexdigest()]
for q in corpus.qec_entries():
text = open(os.path.join(ROOT, "corpus", "qec", q["id"] + ".circuit")).read()
if pinned.get(q["id"]) != hashlib.sha256(text.encode()).hexdigest():
drift.append(q["id"])
rep.add("corpus", "corpus.py", "circuits whose text differs from the manifest", drift, [], not drift)
for e in es:
p = os.path.join(ROOT, "corpus", "qasm", e["id"] + ".qasm")
if os.path.exists(p) and open(p).read() != e["qasm"]:
rep.add(e["id"], "corpus.py", "the committed program equals the generator's", False, True, False)
CI_HAND = ("qec_inverted_and_tagged", "qec_correlated", "qec_refused_random_detector")
def main():
global EXPECTED
ap = argparse.ArgumentParser()
ap.add_argument("--tier", default="ci", choices=["ci", "full"])
ap.add_argument("--out")
ap.add_argument("--jobs", type=int, default=2)
ap.add_argument("--timeout", type=float, default=900)
ap.add_argument("--inject", choices=sorted(FAULTS))
ap.add_argument("--only")
ap.add_argument("--keep", action="store_true")
a = ap.parse_args()
if not os.environ.get("WAI_CROSS_SIMULATOR"):
print("compare.py: WAI_CROSS_SIMULATOR must name the engine's driver (reproduce.sh builds it)", file=sys.stderr)
sys.exit(2)
work = tempfile.mkdtemp(prefix="qcorpus-")
runner = Runner(work, sys.executable, a.timeout, a.inject)
runner.jobs = a.jobs
rep = Report()
EXPECTED = Expected()
started = time.time()
es = corpus.build()
manifest_check(es, rep)
phase_table_check(rep)
if a.tier == "ci":
es = [e for e in es if e["tier"] == "ci"]
only = set(a.only.split(",")) if a.only else None
if only:
es = [e for e in es if e["id"] in only]
count_plan = []
stab_counts = sum(1 for e in es if e["expect"] == "stabilizer_counts")
for e in es:
print("·", e["id"], file=sys.stderr, flush=True)
x = e["expect"]
if x in ("exact_state", "known_state"):
check_state_entry(e, runner, rep, e.get("engines") or STATE_ENGINES)
elif x == "distribution":
check_counts_entry(e, runner, rep, count_plan)
elif x == "stabilizer":
check_stabilizer_entry(e, runner, rep)
elif x == "stabilizer_counts":
check_stabilizer_counts(e, runner, rep, FAMILY_WISE_ALPHA / 3 / max(stab_counts, 1))
elif x == "mirror":
check_mirror_entry(e, runner, rep)
elif x == "pairwise":
check_pairwise_entry(e, runner, rep)
elif x in ("amplitudes", "wide_pairwise", "wide_mirror"):
check_wide_entry(e, runner, rep)
elif x == "expectation":
check_expectation_entry(e, runner, rep)
judge_counts(count_plan, rep, FAMILY_WISE_ALPHA / 3)
qplan = []
qs = [q for q in corpus.qec_entries() if a.tier == "full" or q["tier"] == "ci"]
hand = json.load(open(os.path.join(ROOT, "corpus", "qec-hand-written.json")))
if only:
qs = [q for q in qs if q["id"] in only]
hand = {k: v for k, v in hand.items() if k in only}
for q in qs:
print("·", q["id"], file=sys.stderr, flush=True)
text = qec_text(q, runner, rep)
check_qec_entry(q, text, runner, rep, qplan)
if q["code"] == "surface_code:rotated_memory_z" and os.environ.get("WAI_QUANTUM"):
mine = subprocess.run([os.environ["WAI_QUANTUM"], "lab", "qec", "--circuit", "-d", str(q["distance"]), "--rounds", str(q["rounds"]), "--p", repr(q["p"])], capture_output=True, text=True).stdout
rep.add(q["id"], "wai.qec", "the engine's own generator writes the same text, byte for byte", mine == text, True, mine == text)
for k, v in hand.items():
if a.tier == "ci" and k not in CI_HAND:
continue
print("·", k, file=sys.stderr, flush=True)
check_qec_entry({"id": k, "tier": a.tier}, v["text"], runner, rep, qplan)
judge_qec(qplan, rep, FAMILY_WISE_ALPHA / 3)
entries_run = {e["id"] for e in es} | {q["id"] for q in qs} | {k for k in hand if a.tier == "full" or k in CI_HAND}
apply_known(rep, entries_run)
check_expected(rep, entries_run)
vers = json.loads(subprocess.run([sys.executable, "-I", os.path.join(HERE, "engines.py"), "--versions"], capture_output=True, text=True).stdout)
statuses = {}
for c in rep.checks:
statuses[c["status"]] = statuses.get(c["status"], 0) + 1
out = {
"about": "One run of the cross-simulator corpus: every check, its measured value and its tolerance. tools/compare.py writes it.",
"tier": a.tier,
"inject": a.inject,
"host": host(),
"versions": vers,
"engine_under_test": {"driver": os.path.basename(os.environ["WAI_CROSS_SIMULATOR"]), "build": os.environ.get("WAI_BUILD_INFO", "")},
"seconds": round(time.time() - started, 1),
"summary": statuses,
"checks": rep.checks,
}
if a.out:
with open(a.out, "w") as f:
json.dump(out, f, indent=1, sort_keys=False, default=str)
f.write("\n")
fails = [c for c in rep.checks if c["status"] == "fail"]
for c in rep.checks:
if c["status"] in ("fail", "refused", "not run", "known"):
print("%-8s %-34s %-34s %s | measured %s, tolerance %s%s" % (c["status"].upper(), c["entry"][:34], c["engine"][:34], c["quantity"][:110], fmt(c["measured"]), fmt(c["tolerance"]), (" — " + c["note"][:240]) if c.get("note") else ""))
print("summary: %s; %.0f s" % (", ".join("%s %d" % kv for kv in sorted(statuses.items())), time.time() - started))
print("FAIL" if fails else "PASS")
if not a.keep:
shutil.rmtree(work, ignore_errors=True)
sys.exit(1 if fails else 0)
def fmt(x):
if isinstance(x, float):
return "%.3g" % x
return str(x)[:80]
if __name__ == "__main__":
main()