gbskernels 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,204 @@
1
+ """End-to-end (public-API) throughput: the GPU binding path vs the CPU backend.
2
+
3
+ This measures the **public** call path -- `gbskernels.X_batched(stack,
4
+ backend=...)` -- which for the GPU backend includes the numpy -> host_api ->
5
+ H2D -> launch -> sync -> D2H -> numpy round trip on *every* call (the binding has
6
+ no device-resident workspace; see the README "Known limitations"). It is
7
+ deliberately separate from the kernel-only timing in `core/bench_kernels.cu`:
8
+ together they bound how much of the cost is data movement vs compute.
9
+
10
+ Benchmark hygiene (docs/DESIGN.md §9): n>=repeats timed runs per cell reported as
11
+ median + IQR; **randomized execution order** across (backend, func, size) cells;
12
+ a post-call **checksum honesty guard** that also cross-checks the GPU and CPU
13
+ backends agree; raw data appended to `results/throughput/`; no composite score.
14
+
15
+ Runs wherever the GPU extension is importable -- on a CUDA box it times the real
16
+ device, on a host-shim build it times the CPU emulation (the timing is then only
17
+ indicative, but the harness and the GPU/CPU agreement are validated). The
18
+ accuracy-normalized vs-The-Walrus comparison lives in `bench/throughput.py`.
19
+
20
+ uv run python -m bench.throughput_end_to_end --batch 1024 --repeats 7
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import argparse
26
+ import json
27
+ import platform
28
+ import random
29
+ import sys
30
+ import time
31
+ from datetime import datetime, timezone
32
+ from importlib.metadata import version
33
+ from pathlib import Path
34
+ from typing import Any
35
+
36
+ import numpy as np
37
+
38
+ import gbskernels
39
+ from bench import _inputs, _provenance
40
+
41
+ DEFAULT_OUT = Path(__file__).resolve().parent.parent / "results" / "throughput"
42
+
43
+ # func -> (batched fn, matrix sizes within the GPU kernels' caps). The matrices come from
44
+ # the SINGLE shared workload generator (bench._inputs.bench_batch) so the GPU, CPU, and the
45
+ # same-instance The Walrus baseline are all timed on the *same input* per (func, dim, regime)
46
+ # -- an apples-to-apples, accuracy-normalizable comparison (docs/DESIGN.md §9). For the
47
+ # torontonian the matrix dim is 2*modes.
48
+ _SPECS = {
49
+ "perm": (gbskernels.perm_batched, [8, 12, 16]),
50
+ "haf": (gbskernels.haf_batched, [8, 12, 16]),
51
+ "lhaf": (gbskernels.lhaf_batched, [8, 12]),
52
+ "tor": (gbskernels.tor_batched, [8, 12, 16]),
53
+ }
54
+
55
+
56
+ def _checksum(v: np.ndarray) -> float:
57
+ """Order-independent magnitude checksum (the post-sync honesty guard)."""
58
+ return float(np.sum(np.abs(np.asarray(v, dtype=np.complex128))))
59
+
60
+
61
+ def _quantile(sorted_vals: list[float], q: float) -> float:
62
+ x = q * (len(sorted_vals) - 1)
63
+ lo = int(x)
64
+ frac = x - lo
65
+ return (sorted_vals[lo] * (1 - frac) + sorted_vals[lo + 1] * frac
66
+ if lo + 1 < len(sorted_vals) else sorted_vals[lo])
67
+
68
+
69
+
70
+
71
+ def run(batch: int, repeats: int, seed: int = 0, warmup: int = 2,
72
+ regime: str = "physical", out_dir: Path | None = None) -> tuple[dict[str, Any], Path]:
73
+ if not gbskernels.gpu_available():
74
+ raise RuntimeError(
75
+ "GPU extension not importable; build bindings/ (or the host-shim) first."
76
+ )
77
+ backends = ["gpu", "cpu"]
78
+ cells = [(eng, func, d, rep)
79
+ for eng in backends for func, (_, sizes) in _SPECS.items()
80
+ for d in sizes for rep in range(repeats)]
81
+ random.Random(seed).shuffle(cells)
82
+
83
+ # one shared workload per (func, dim) from bench._inputs (regime-aware): GPU, CPU, and
84
+ # the Walrus baseline all consume the SAME matrices -> same-input comparison.
85
+ stacks = {(func, d): _inputs.bench_batch(func, d, batch, regime, 1000 + d)
86
+ for func, (_, sizes) in _SPECS.items() for d in sizes}
87
+
88
+ # The CPU backend loops the Python reference, so it is timed on a capped
89
+ # sub-batch; evals/sec is per-eval and stays comparable to the GPU full batch.
90
+ cpu_cap = 128
91
+
92
+ # Warm-up policy (docs/DESIGN.md §9): each (backend, func, size) cell is run `warmup` times
93
+ # UNTIMED before any timed run, to reach steady-state GPU clocks and prime caches /
94
+ # the kernel JIT. The cold first run is never a headline number -- this is what
95
+ # tames the large cold-start IQRs. Warm-up runs are discarded.
96
+ for eng, func, d in {(e, f, dd) for e, f, dd, _ in cells}:
97
+ fn, n = _SPECS[func][0], (batch if eng == "gpu" else min(batch, cpu_cap))
98
+ for _ in range(warmup):
99
+ fn(stacks[(func, d)][:n], backend=eng)
100
+
101
+ raw: list[dict[str, Any]] = []
102
+ for eng, func, d, rep in cells:
103
+ fn = _SPECS[func][0]
104
+ n = batch if eng == "gpu" else min(batch, cpu_cap)
105
+ A = stacks[(func, d)][:n]
106
+ t0 = time.perf_counter()
107
+ out = fn(A, backend=eng)
108
+ elapsed = time.perf_counter() - t0 # GPU path syncs inside host_api
109
+ # checksum over the first cpu_cap results (the same matrices both backends
110
+ # compute) so GPU and CPU are comparable despite different timed batch sizes.
111
+ raw.append({"backend": eng, "func": func, "matrix_dim": d, "repeat": rep,
112
+ "batch_timed": n, "seconds": elapsed,
113
+ "evals_per_sec": n / elapsed if elapsed else float("inf"),
114
+ "checksum": _checksum(out[:min(batch, cpu_cap)])})
115
+
116
+ summary = _summarize(raw)
117
+ # The public-path honesty gate: on a real device the GPU and CPU backends must agree to
118
+ # FP64 tolerance on every well-conditioned (physical) cell. A single disagreement means a
119
+ # kernel/binding bug, and NO timing from this run is publishable -- the caller (main / the
120
+ # GPU session) turns this into a non-zero exit. (Adversarial inputs may legitimately
121
+ # diverge; gate on the physical regime.)
122
+ all_agree = all(r["backends_agree"] for r in summary)
123
+ artifact = {
124
+ "kind": "throughput_end_to_end",
125
+ "created_utc": datetime.now(timezone.utc).isoformat(),
126
+ **_provenance.provenance(), # commit + container_digest + hostname
127
+ "path": "public gbskernels.*_batched(backend=...) incl. H2D/launch/sync/D2H",
128
+ # build-flag provenance, not host OS: a no-GPU runner is "host-shim".
129
+ "gpu_backend": gbskernels.gpu_backend_kind(),
130
+ "env": {"platform": platform.platform(), "numpy": version("numpy")},
131
+ "params": {"batch": batch, "repeats": repeats, "warmup": warmup, "precision": "fp64",
132
+ "regime": regime, "seed": seed,
133
+ "metric": "evals/sec median + IQR; randomized cell order; "
134
+ "warm-up discarded; checksum guard"},
135
+ "all_backends_agree": all_agree,
136
+ "summary": summary,
137
+ "raw": raw,
138
+ }
139
+ out_dir = Path(out_dir) if out_dir is not None else DEFAULT_OUT
140
+ out_dir.mkdir(parents=True, exist_ok=True)
141
+ stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
142
+ path = out_dir / f"throughput_e2e_{stamp}.json"
143
+ path.write_text(json.dumps(artifact, indent=2))
144
+ return artifact, path
145
+
146
+
147
+ def _summarize(raw: list[dict[str, Any]]) -> list[dict[str, Any]]:
148
+ rows = []
149
+ keys = sorted({(r["func"], r["matrix_dim"]) for r in raw})
150
+ for func, d in keys:
151
+ row: dict[str, Any] = {"func": func, "matrix_dim": d}
152
+ checks = {}
153
+ for eng in ("gpu", "cpu"):
154
+ eps = sorted(r["evals_per_sec"] for r in raw
155
+ if r["backend"] == eng and r["func"] == func and r["matrix_dim"] == d)
156
+ cs = [r["checksum"] for r in raw
157
+ if r["backend"] == eng and r["func"] == func and r["matrix_dim"] == d]
158
+ checks[eng] = cs[0] if cs else None
159
+ if eps:
160
+ row[eng] = {"evals_per_sec_median": _quantile(eps, 0.5),
161
+ "evals_per_sec_iqr": _quantile(eps, 0.75) - _quantile(eps, 0.25)}
162
+ # GPU vs CPU agree to FP64 tolerance -- NOT bit-exact: the two backends use
163
+ # different determinant/linear-algebra routines, so mildly-conditioned
164
+ # inputs legitimately differ at ~1e-11 (a strict string check would flag it).
165
+ g, c = checks.get("gpu"), checks.get("cpu")
166
+ row["backends_agree"] = (g is not None and c is not None
167
+ and abs(g - c) <= 1e-7 * max(abs(c), 1e-300))
168
+ row["checksum_gpu"], row["checksum_cpu"] = g, c
169
+ rows.append(row)
170
+ return rows
171
+
172
+
173
+ def main() -> None:
174
+ p = argparse.ArgumentParser(description=__doc__)
175
+ p.add_argument("--batch", type=int, default=1024)
176
+ p.add_argument("--repeats", type=int, default=7)
177
+ p.add_argument("--seed", type=int, default=0)
178
+ p.add_argument("--out", type=Path, default=None)
179
+ p.add_argument("--warmup", type=int, default=2, help="untimed warm-up runs per cell")
180
+ p.add_argument("--regime", default="physical", choices=list(_inputs.BENCH_REGIMES),
181
+ help="shared input family for every (func,dim) cell")
182
+ args = p.parse_args()
183
+ artifact, path = run(args.batch, args.repeats, args.seed, args.warmup,
184
+ regime=args.regime, out_dir=args.out)
185
+ print(f"# end-to-end throughput ({artifact['gpu_backend']}) -> {path}")
186
+ print(f"# public binding path; commit {artifact['commit']}; digest "
187
+ f"{artifact['container_digest']}; batch={args.batch} repeats={args.repeats} "
188
+ f"warmup={args.warmup} regime={args.regime}")
189
+ print(f" {'func':>5} {'dim':>4} {'gpu med':>12} {'cpu med':>12} agree")
190
+ for r in artifact["summary"]:
191
+ gm = r.get("gpu", {}).get("evals_per_sec_median", float("nan"))
192
+ cm = r.get("cpu", {}).get("evals_per_sec_median", float("nan"))
193
+ print(f" {r['func']:>5} {r['matrix_dim']:>4} {gm:>12.3e} {cm:>12.3e} "
194
+ f"{'ok' if r['backends_agree'] else 'MISMATCH'}")
195
+ # P0.8: a public-path checksum disagreement fails the run (non-zero exit), so the GPU
196
+ # session aborts instead of publishing a number from a backend mismatch.
197
+ if not artifact["all_backends_agree"]:
198
+ print("\n[FAIL] GPU and CPU disagree on a public-path cell -- no timing from this run "
199
+ "is publishable. Investigate the kernel; rerun on the physical regime.")
200
+ sys.exit(2)
201
+
202
+
203
+ if __name__ == "__main__":
204
+ main()
@@ -0,0 +1,126 @@
1
+ """GPU throughput driver — runs the compiled CUDA timing harness with provenance.
2
+
3
+ Thin wrapper (docs/DESIGN.md §9/sec.10) around ``core/build/bench_kernels`` (built by
4
+ CMake under nvcc in a rented-GPU session). It runs the harness, captures the
5
+ per-(func, size) evals/sec + honesty checksums it emits, attaches GPU/driver/clock
6
+ provenance from ``nvidia-smi``, and writes an append-only artifact to
7
+ ``results/throughput/``. No composite "winner" number; raw data retained.
8
+
9
+ This module does **not** compile or run anything on a GPU by itself — it requires
10
+ that ``core/build/bench_kernels`` already exists (the session's manifest builds
11
+ it). Invoked as ``python -m bench.throughput_gpu`` from ``scripts/session.py``.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import argparse
17
+ import json
18
+ import platform
19
+ import subprocess
20
+ from datetime import datetime, timezone
21
+ from pathlib import Path
22
+ from typing import Any
23
+
24
+ from bench import _provenance
25
+
26
+ REPO = Path(__file__).resolve().parent.parent
27
+ DEFAULT_BIN = REPO / "core" / "build" / "bench_kernels"
28
+ DEFAULT_OUT = REPO / "results" / "throughput"
29
+
30
+
31
+ def _nvidia_smi() -> dict[str, Any]:
32
+ """GPU name, driver, and clocks for provenance (empty dict if unavailable)."""
33
+ q = ("name,driver_version,memory.total,memory.used,temperature.gpu,"
34
+ "clocks.sm,clocks.mem,clocks.max.sm,power.draw")
35
+ try:
36
+ out = subprocess.run(
37
+ ["nvidia-smi", f"--query-gpu={q}", "--format=csv,noheader"],
38
+ capture_output=True, text=True, check=True,
39
+ ).stdout.strip()
40
+ fields = [f.strip() for f in out.split(",")]
41
+ keys = ["name", "driver_version", "memory_total", "memory_used", "temperature_c",
42
+ "clock_sm", "clock_mem", "clock_sm_max", "power_draw"]
43
+ return dict(zip(keys, fields))
44
+ except Exception:
45
+ return {}
46
+
47
+
48
+ def run(bench_bin: Path, batch: int, repeats: int, out_dir: Path) -> tuple[dict[str, Any], Path]:
49
+ if not bench_bin.exists():
50
+ raise FileNotFoundError(
51
+ f"{bench_bin} not found — build it first (cmake --build core/build) in the "
52
+ "rented-GPU session; this driver only runs the compiled harness."
53
+ )
54
+ proc = subprocess.run(
55
+ [str(bench_bin), str(batch), str(repeats)],
56
+ capture_output=True, text=True, check=True,
57
+ )
58
+
59
+ def _parse(line: str) -> dict[str, Any]:
60
+ # C printf emits bare nan/inf; rewrite to the JSON tokens json accepts so
61
+ # an anomalous row is recorded (as NaN/inf) rather than crashing the run.
62
+ import re
63
+ line = re.sub(r":\s*-inf\b", ": -Infinity", line)
64
+ line = re.sub(r":\s*inf\b", ": Infinity", line)
65
+ line = re.sub(r":\s*nan\b", ": NaN", line)
66
+ return json.loads(line)
67
+
68
+ rows = [_parse(line) for line in proc.stdout.splitlines() if line.strip().startswith("{")]
69
+
70
+ artifact = {
71
+ "kind": "throughput",
72
+ "tier": "gpu",
73
+ "created_utc": datetime.now(timezone.utc).isoformat(),
74
+ **_provenance.provenance(), # commit + container_digest + hostname
75
+ "gpu": _nvidia_smi(), # name/driver/memory + temperature & live clocks at run time
76
+ "host": {"platform": platform.platform()},
77
+ "params": {"batch": batch, "repeats": repeats, "precision": "fp64+dd",
78
+ "metric": "evals_per_sec median + IQR over repeats; bench_kernels.cu "
79
+ "warms each cell before timing; post-sync checksum/row"},
80
+ "methodology_caveats": (
81
+ "Toward docs/DESIGN.md §9 bar: the container digest + commit are now recorded and "
82
+ "the kernel timing is warmed up. Still single-instance per run (sweep the 4090 "
83
+ "AND a datacenter GPU across runs); the on-the-same-instance The Walrus "
84
+ "comparison runs in the GPU session (bench.walrus_baseline)."
85
+ ),
86
+ "rows": rows,
87
+ }
88
+ out_dir.mkdir(parents=True, exist_ok=True)
89
+ stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
90
+ gpu_tag = (artifact["gpu"].get("name", "gpu") or "gpu").replace(" ", "_")
91
+ path = out_dir / f"throughput_{gpu_tag}_{stamp}.json"
92
+ path.write_text(json.dumps(artifact, indent=2))
93
+ return artifact, path
94
+
95
+
96
+ def main() -> None:
97
+ p = argparse.ArgumentParser(description=__doc__)
98
+ p.add_argument("--bin", type=Path, default=DEFAULT_BIN)
99
+ p.add_argument("--batch", type=int, default=4096)
100
+ p.add_argument("--repeats", type=int, default=7)
101
+ p.add_argument("--out", type=Path, default=DEFAULT_OUT)
102
+ # accepted for manifest-compatibility with the CPU harness; the compiled
103
+ # binary fixes its own size grid, so these are informational here.
104
+ p.add_argument("--func", default=None)
105
+ p.add_argument("--sizes", default=None)
106
+ args = p.parse_args()
107
+
108
+ artifact, path = run(args.bin, args.batch, args.repeats, args.out)
109
+ print(f"# gpu throughput -> {path}")
110
+ g = artifact["gpu"]
111
+ print(f"# GPU: {g.get('name','?')} | driver {g.get('driver_version','?')} | "
112
+ f"{g.get('temperature_c','?')}C | clk {g.get('clock_sm','?')}")
113
+ print(f"# commit {artifact.get('commit')} | container {artifact.get('container_digest')} | "
114
+ f"batch={args.batch} repeats={args.repeats}")
115
+ print(f" {'func':>9} {'matrix_dim':>10} {'evals/sec median':>17} {'IQR':>11}")
116
+ for r in artifact["rows"]:
117
+ med = r.get("evals_per_sec_median", r.get("evals_per_sec_best", float("nan")))
118
+ iqr = r.get("evals_per_sec_iqr", float("nan"))
119
+ # the cooperative permanent rows carry a "groups" width; surface it so the
120
+ # perm-vs-perm_coop crossover is legible (raw rows keep the field regardless).
121
+ label = r["func"] + (f"/g{r['groups']}" if "groups" in r else "")
122
+ print(f" {label:>9} {r['matrix_dim']:>10} {med:>17.3e} {iqr:>11.2e}")
123
+
124
+
125
+ if __name__ == "__main__":
126
+ main()
bench/tightness.py ADDED
@@ -0,0 +1,174 @@
1
+ """Certified-bound tightness distributions -- the numerics figure.
2
+
3
+ For a CS/numerics reviewer, "the enclosure is always safe" is table stakes; the
4
+ question is *how tight*. This measures, across ensembles rather than single
5
+ worst cases, the distribution of three quantities for each certified function:
6
+
7
+ rel_bound = bound / |value| -- what the certificate PROMISES (≈ κ·u)
8
+ rel_err = |value − exact| / |value| -- what actually happened (vs mpmath)
9
+ tightness = bound / max(|value−exact|, u·|value|)
10
+ -- the bound's slack over the true
11
+ error, floored at the fp64 ulp
12
+ (you cannot resolve error below u·|v|)
13
+
14
+ The hard invariant (`rel_err ≤ rel_bound` on every sample -- the enclosure) is
15
+ checked here too and must never fail. The story the distribution tells: on the
16
+ physical ensemble the bound is tight (small, bounded tightness) and on the
17
+ adversarial cancellation families it widens *correctly* -- because those inputs
18
+ are genuinely ill-conditioned, and the certificate reports that honestly rather
19
+ than hiding it. That is the difference between a rigorous certificate and a
20
+ heuristic κ.
21
+
22
+ uv run python -m bench.tightness --samples 150 [--plot]
23
+
24
+ Writes results/tightness/tightness_<utc>.json (per function×regime distribution
25
+ stats + enclosure check) and, with --plot, results/tightness/tightness.png.
26
+ CPU-only; references are mpmath. Sizes are kept modest so the reference is
27
+ affordable -- the point is the distribution shape, not large N.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import argparse
33
+ import json
34
+ import time
35
+ from pathlib import Path
36
+ from typing import Any, Callable
37
+
38
+ import numpy as np
39
+
40
+ import cpu_ref
41
+ import highprec_ref as href
42
+ from bench import _inputs as inp
43
+ from bench._provenance import provenance
44
+
45
+ RESULTS = Path(__file__).resolve().parent.parent / "results" / "tightness"
46
+ U = 2.0 ** -53
47
+
48
+ # func -> (mpmath reference, {regime: sampler(seed) -> matrix})
49
+ _CASES: dict[str, tuple[Callable, dict[str, Callable]]] = {
50
+ "perm": (href.permanent_mp, {
51
+ "physical": lambda s: inp.physical_permanent(10, s),
52
+ "adversarial": lambda s: inp.pm1_matrix(10, s), # ±1: heavy cancellation
53
+ }),
54
+ "haf": (href.hafnian_mp, {
55
+ "physical": lambda s: inp.physical_hafnian(10, s),
56
+ "adversarial": lambda s: inp.cancellation_hafnian(10.0 ** -(3 + s % 9), s),
57
+ }),
58
+ "lhaf": (href.loop_hafnian_mp, {
59
+ "physical": lambda s: inp.physical_loop_hafnian(10, s),
60
+ "adversarial": lambda s: inp.cancellation_loop_hafnian(10.0 ** -(3 + s % 9), s),
61
+ }),
62
+ "tor": (href.torontonian_mp, {
63
+ "physical": lambda s: np.real(inp.physical_torontonian(4, s)),
64
+ "adversarial": lambda s: inp.cancellation_torontonian(10.0 ** -(1 + s % 6)),
65
+ }),
66
+ }
67
+
68
+
69
+ def _quantiles(x: np.ndarray) -> dict[str, float]:
70
+ q = np.quantile(x, [0.05, 0.25, 0.5, 0.75, 0.95])
71
+ return {"q05": float(q[0]), "q25": float(q[1]), "median": float(q[2]),
72
+ "q75": float(q[3]), "q95": float(q[4]), "max": float(x.max()),
73
+ "min": float(x.min())}
74
+
75
+
76
+ def _collect(func: str, regime: str, gen: Callable, ref: Callable,
77
+ samples: int) -> dict[str, Any]:
78
+ rel_bound, rel_err, tight = [], [], []
79
+ enclosure_fails = 0
80
+ for s in range(samples):
81
+ A = gen(s)
82
+ value, bound = cpu_ref.certified(func, A)
83
+ av = abs(value)
84
+ if av == 0.0 or not np.isfinite(bound):
85
+ continue
86
+ exact = complex(ref(A, dps=50))
87
+ err = abs(complex(value) - exact)
88
+ rb, re = bound / av, err / av
89
+ if re > rb * (1 + 1e-9): # the enclosure invariant -- must hold
90
+ enclosure_fails += 1
91
+ rel_bound.append(rb)
92
+ rel_err.append(re)
93
+ tight.append(bound / max(err, U * av))
94
+ return {"n": len(tight), "enclosure_fails": enclosure_fails,
95
+ "rel_bound": _quantiles(np.array(rel_bound)),
96
+ "rel_err": _quantiles(np.array(rel_err)),
97
+ "tightness": _quantiles(np.array(tight)),
98
+ "_raw_tightness": [float(x) for x in tight],
99
+ "_raw_rel_bound": [float(x) for x in rel_bound]}
100
+
101
+
102
+ def run(samples: int) -> dict[str, Any]:
103
+ out: dict[str, Any] = {}
104
+ print(f"{'func/regime':>18} {'N':>4} {'encl':>5} "
105
+ f"{'rel_bound med':>14} {'rel_err med':>12} {'tightness med (q95)':>22}")
106
+ for func, (ref, regimes) in _CASES.items():
107
+ out[func] = {}
108
+ for regime, gen in regimes.items():
109
+ d = _collect(func, regime, gen, ref, samples)
110
+ out[func][regime] = d
111
+ t = d["tightness"]
112
+ print(f"{func + '/' + regime:>18} {d['n']:>4} {d['enclosure_fails']:>5} "
113
+ f"{d['rel_bound']['median']:>14.2e} {d['rel_err']['median']:>12.2e} "
114
+ f"{t['median']:>10.1f} ({t['q95']:.1f})")
115
+ total_fails = sum(out[f][r]["enclosure_fails"]
116
+ for f in out for r in out[f])
117
+ print(f"\nenclosure violations across all ensembles: {total_fails} "
118
+ f"(MUST be 0 -- the certificate is only meaningful if it never under-claims)")
119
+ return {"kind": "certified_tightness", **provenance(),
120
+ "params": {"samples": samples, "sizes": "perm/haf/lhaf n=10, tor 2n=8"},
121
+ "enclosure_violations_total": total_fails, "by_function": out}
122
+
123
+
124
+ def plot(art: dict[str, Any], path: Path) -> None:
125
+ import matplotlib
126
+ matplotlib.use("Agg")
127
+ import matplotlib.pyplot as plt
128
+
129
+ funcs = list(art["by_function"])
130
+ fig, ax = plt.subplots(figsize=(9, 5))
131
+ positions, data, colors, labels = [], [], [], []
132
+ pos = 0
133
+ for func in funcs:
134
+ for regime, col in (("physical", "#2a7"), ("adversarial", "#d63")):
135
+ raw = art["by_function"][func][regime].get("_raw_tightness", [])
136
+ if raw:
137
+ positions.append(pos)
138
+ data.append(np.log10(np.maximum(raw, 1.0)))
139
+ colors.append(col)
140
+ labels.append(f"{func}\n{regime}")
141
+ pos += 1
142
+ pos += 0.6
143
+ parts = ax.violinplot(data, positions=positions, showmedians=True, widths=0.8)
144
+ for pc, c in zip(parts["bodies"], colors):
145
+ pc.set_facecolor(c)
146
+ pc.set_alpha(0.6)
147
+ ax.set_xticks(positions)
148
+ ax.set_xticklabels(labels, fontsize=8)
149
+ ax.set_ylabel(r"$\log_{10}$ tightness = bound / max(actual error, $u\,|v|$)")
150
+ ax.set_title("Certified-bound tightness distribution (rigorous, never < 1)\n"
151
+ "physical ensembles tight; adversarial families widen honestly")
152
+ ax.axhline(0, color="k", lw=0.5, ls=":")
153
+ fig.tight_layout()
154
+ fig.savefig(path, dpi=140)
155
+ print(f"-> {path}")
156
+
157
+
158
+ def main() -> None:
159
+ ap = argparse.ArgumentParser(description=__doc__)
160
+ ap.add_argument("--samples", type=int, default=150)
161
+ ap.add_argument("--plot", action="store_true")
162
+ args = ap.parse_args()
163
+ art = run(args.samples)
164
+ RESULTS.mkdir(parents=True, exist_ok=True)
165
+ stamp = time.strftime("%Y%m%dT%H%M%SZ", time.gmtime())
166
+ out = RESULTS / f"tightness_{stamp}.json"
167
+ out.write_text(json.dumps(art, indent=1))
168
+ print(f"-> {out}")
169
+ if args.plot:
170
+ plot(art, RESULTS / "tightness.png")
171
+
172
+
173
+ if __name__ == "__main__":
174
+ main()
@@ -0,0 +1,148 @@
1
+ """Same-instance The Walrus baseline (docs/DESIGN.md §9 / the reproducibility contract).
2
+
3
+ Times Xanadu's The Walrus (the canonical CPU reference) for the four functions on the
4
+ **same GPU instance** that ran our kernels, with the same hygiene -- warm-up, median +
5
+ IQR over repeats, randomized cell order, the shared provenance block -- so the
6
+ GPU-vs-The-Walrus comparison is apples-to-apples on one machine (not our GPU on a
7
+ rented box vs The Walrus on a laptop). The crossover analysis (`bench.crossover`) pairs
8
+ this artifact with the kernel throughput artifact from the same session.
9
+
10
+ The Walrus is one-evaluation-at-a-time, so each cell loops a small batch and reports
11
+ per-eval evals/sec, directly comparable to our batched per-eval rate. Runs wherever
12
+ ``thewalrus`` is importable (the GPU session installs it); skips with a clear error
13
+ otherwise.
14
+
15
+ uv run python -m bench.walrus_baseline --batch 64 --repeats 7
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import argparse
21
+ import json
22
+ import platform
23
+ import random
24
+ import time
25
+ from datetime import datetime, timezone
26
+ from importlib.metadata import version
27
+ from pathlib import Path
28
+ from typing import Any
29
+
30
+ import numpy as np
31
+
32
+ from bench import _inputs, _provenance
33
+
34
+ # The Walrus's high-level paths use np.find_common_type (removed in NumPy 2.0); the
35
+ # low-level perm/hafnian/tor used here do not, but apply the NumPy-2.0 compat shim
36
+ # defensively so the import + any internal use is safe.
37
+ if not hasattr(np, "find_common_type"):
38
+ np.find_common_type = lambda a, s: np.result_type(*a, *s)
39
+
40
+ DEFAULT_OUT = Path(__file__).resolve().parent.parent / "results" / "throughput"
41
+
42
+ # func -> (The Walrus call, matrix sizes). The matrices come from the SAME shared generator
43
+ # (bench._inputs.bench_batch) and the SAME seed convention as bench.throughput_end_to_end, so
44
+ # The Walrus is timed on the *identical inputs* as our GPU/CPU path -- a same-input baseline
45
+ # (docs/DESIGN.md §9). For the torontonian the matrix dim is 2*modes (so [8,12,16] = 4..8 modes).
46
+ _SPECS: dict[str, Any] = {
47
+ "perm": (lambda tw, M: tw.perm(M), [8, 12, 16]),
48
+ "haf": (lambda tw, M: tw.hafnian(M), [8, 12, 16]),
49
+ "lhaf": (lambda tw, M: tw.hafnian(M, loop=True), [8, 12]),
50
+ "tor": (lambda tw, M: tw.tor(np.real(M)), [8, 12, 16]),
51
+ }
52
+
53
+
54
+ def _quantile(xs: list[float], q: float) -> float:
55
+ s = sorted(xs)
56
+ x = q * (len(s) - 1)
57
+ lo = int(x); frac = x - lo
58
+ return s[lo] * (1 - frac) + s[lo + 1] * frac if lo + 1 < len(s) else s[lo]
59
+
60
+
61
+ def run(batch: int, repeats: int, seed: int = 0, warmup: int = 2,
62
+ regime: str = "physical", out_dir: Path | None = None) -> tuple[dict[str, Any], Path]:
63
+ try:
64
+ import thewalrus as tw
65
+ except Exception as e: # pragma: no cover - depends on the box
66
+ raise RuntimeError(
67
+ "thewalrus is not importable; install it on the instance to record the "
68
+ "same-instance baseline (the GPU session does this)."
69
+ ) from e
70
+
71
+ cells = [(func, d, rep) for func, (_, sizes) in _SPECS.items()
72
+ for d in sizes for rep in range(repeats)]
73
+ random.Random(seed).shuffle(cells)
74
+
75
+ # the SAME shared workload (and seed convention 1000+d) as throughput_end_to_end, so the
76
+ # Walrus baseline is timed on the identical matrices our GPU/CPU path is.
77
+ mats = {(func, d): list(_inputs.bench_batch(func, d, batch, regime, 1000 + d))
78
+ for func, (_, sizes) in _SPECS.items() for d in sizes}
79
+
80
+ call = {func: _SPECS[func][0] for func in _SPECS}
81
+ # warm-up (untimed) per distinct cell -- steady state before timing
82
+ for func, d in {(f, dd) for f, dd, _ in cells}:
83
+ for _ in range(warmup):
84
+ for M in mats[(func, d)][: min(batch, 8)]:
85
+ call[func](tw, M)
86
+
87
+ raw: list[dict[str, Any]] = []
88
+ for func, d, rep in cells:
89
+ batch_mats = mats[(func, d)]
90
+ t0 = time.perf_counter()
91
+ s = 0.0
92
+ for M in batch_mats:
93
+ s += abs(complex(call[func](tw, M)))
94
+ elapsed = time.perf_counter() - t0
95
+ raw.append({"func": func, "matrix_dim": d, "repeat": rep, "batch": batch,
96
+ "seconds": elapsed, "evals_per_sec": batch / elapsed if elapsed else float("inf"),
97
+ "checksum": s})
98
+
99
+ rows = []
100
+ for func, (_, sizes) in _SPECS.items():
101
+ for d in sizes:
102
+ eps = [r["evals_per_sec"] for r in raw if r["func"] == func and r["matrix_dim"] == d]
103
+ rows.append({"func": func, "matrix_dim": d,
104
+ "evals_per_sec_median": _quantile(eps, 0.5),
105
+ "evals_per_sec_iqr": _quantile(eps, 0.75) - _quantile(eps, 0.25)})
106
+
107
+ artifact = {
108
+ "kind": "walrus_baseline",
109
+ "created_utc": datetime.now(timezone.utc).isoformat(),
110
+ **_provenance.provenance(),
111
+ "library": "thewalrus", "library_version": version("thewalrus"),
112
+ "metric": "per-eval evals/sec (The Walrus, one-at-a-time, looped over a batch); "
113
+ "median + IQR; randomized cell order; warm-up discarded",
114
+ "params": {"batch": batch, "repeats": repeats, "warmup": warmup, "precision": "fp64",
115
+ "regime": regime, "seed": seed},
116
+ "env": {"platform": platform.platform(), "numpy": version("numpy")},
117
+ "rows": rows, "raw": raw,
118
+ }
119
+ out_dir = Path(out_dir) if out_dir is not None else DEFAULT_OUT
120
+ out_dir.mkdir(parents=True, exist_ok=True)
121
+ stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
122
+ path = out_dir / f"walrus_baseline_{stamp}.json"
123
+ path.write_text(json.dumps(artifact, indent=2))
124
+ return artifact, path
125
+
126
+
127
+ def main() -> None:
128
+ p = argparse.ArgumentParser(description=__doc__)
129
+ p.add_argument("--batch", type=int, default=64)
130
+ p.add_argument("--repeats", type=int, default=7)
131
+ p.add_argument("--warmup", type=int, default=2)
132
+ p.add_argument("--regime", default="physical", choices=list(_inputs.BENCH_REGIMES),
133
+ help="shared input family (same generator as throughput_end_to_end)")
134
+ p.add_argument("--out", type=Path, default=None)
135
+ args = p.parse_args()
136
+ artifact, path = run(args.batch, args.repeats, warmup=args.warmup,
137
+ regime=args.regime, out_dir=args.out)
138
+ print(f"# The Walrus baseline (same instance) -> {path}")
139
+ print(f"# thewalrus {artifact['library_version']}; commit {artifact['commit']}; "
140
+ f"container {artifact['container_digest']}")
141
+ print(f" {'func':>5} {'dim':>4} {'evals/sec median':>17} {'IQR':>11}")
142
+ for r in artifact["rows"]:
143
+ print(f" {r['func']:>5} {r['matrix_dim']:>4} {r['evals_per_sec_median']:>17.3e} "
144
+ f"{r['evals_per_sec_iqr']:>11.2e}")
145
+
146
+
147
+ if __name__ == "__main__":
148
+ main()