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.
bench/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ """Accuracy-vs-throughput harness (accuracy half is CPU-runnable; see docs/DESIGN.md §9).
2
+
3
+ Throughput benchmarking belongs to scripted rented-GPU sessions and never runs
4
+ here. The *accuracy* characterization (relative error vs size and conditioning
5
+ against the mpmath reference, docs/DESIGN.md §6) is pure CPU work and lives in this
6
+ package.
7
+ """
bench/_inputs.py ADDED
@@ -0,0 +1,348 @@
1
+ """Input families for accuracy characterization and stress testing.
2
+
3
+ Single source of truth shared by the harness and the test suite, so the
4
+ cancellation family used to *measure* the FP64 boundary is exactly the one the
5
+ tests assert against.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import numpy as np
11
+
12
+ __all__ = [
13
+ "random_complex",
14
+ "unit_modulus_complex",
15
+ "pm1_matrix",
16
+ "make_cancellation_matrix",
17
+ "cancellation_hafnian",
18
+ "cancellation_loop_hafnian",
19
+ "cancellation_torontonian",
20
+ "physical_permanent",
21
+ "physical_hafnian",
22
+ "physical_loop_hafnian",
23
+ "physical_torontonian",
24
+ "loss_permanent",
25
+ "loss_hafnian",
26
+ "loss_loop_hafnian",
27
+ "loss_torontonian",
28
+ "adversarial_permanent",
29
+ "adversarial_hafnian",
30
+ "adversarial_loop_hafnian",
31
+ "adversarial_torontonian",
32
+ "haar_unitary",
33
+ "bench_batch",
34
+ ]
35
+
36
+
37
+ def random_complex(n: int, seed: int, scale: float = 1.0) -> np.ndarray:
38
+ g = np.random.default_rng(seed)
39
+ return scale * (g.uniform(-1, 1, (n, n)) + 1j * g.uniform(-1, 1, (n, n)))
40
+
41
+
42
+ def unit_modulus_complex(n: int, seed: int) -> np.ndarray:
43
+ """Entries ``e^{i theta}``, theta ~ U(0, 2pi).
44
+
45
+ A naturally cancellation-heavy family: every entry has modulus 1, so the
46
+ Glynn terms grow like ``n^{n/2}`` while the permanent stays near
47
+ ``sqrt(n!)`` -- the summation condition number rises ~exponentially in ``n``.
48
+ """
49
+ g = np.random.default_rng(seed)
50
+ theta = g.uniform(0.0, 2.0 * np.pi, (n, n))
51
+ return np.exp(1j * theta)
52
+
53
+
54
+ def pm1_matrix(n: int, seed: int) -> np.ndarray:
55
+ """+/-1 matrix. Integer-valued permanent -> exact ground truth at small n."""
56
+ g = np.random.default_rng(seed)
57
+ return np.where(g.random((n, n)) < 0.5, -1.0, 1.0)
58
+
59
+
60
+ def make_cancellation_matrix(n: int, delta: float, seed: int) -> np.ndarray:
61
+ """A matrix with a *tunable* Glynn cancellation, kappa ~ O(1/delta).
62
+
63
+ Direct-sum of a 2x2 near-singular-permanent block with a well-conditioned
64
+ random remainder. The block ``B = [[2, 1], [-1, 0.5 + delta]]`` has
65
+ ``perm(B) = 2*delta`` while its Glynn terms are O(1) (they cancel ~1.5 down
66
+ to ``2*delta``), so the whole matrix has ``perm = 2*delta * perm(R)`` with a
67
+ condition number that scales as ``1/delta`` -- the knob that drives FP64 off
68
+ the accuracy cliff while the mpmath reference stays exact (docs/DESIGN.md §6/sec.8).
69
+ Requires ``n >= 2``.
70
+ """
71
+ if n < 2:
72
+ raise ValueError("cancellation matrix needs n >= 2")
73
+ B = np.array([[2.0, 1.0], [-1.0, 0.5 + delta]], dtype=np.complex128)
74
+ A = np.zeros((n, n), dtype=np.complex128)
75
+ A[:2, :2] = B
76
+ if n > 2:
77
+ A[2:, 2:] = random_complex(n - 2, seed=seed)
78
+ return A
79
+
80
+
81
+ def cancellation_hafnian(delta: float, seed: int) -> np.ndarray:
82
+ """Symmetric matrix whose hafnian nearly cancels to ``delta`` (8x8).
83
+
84
+ A 4x4 block with ``haf = a*f + b*e + c*d = 2 + (delta-2) = delta`` (three O(1)
85
+ matchings cancel) direct-summed with a well-conditioned remainder, so
86
+ ``haf = delta * haf(R)`` is tiny while the power-trace terms are O(1). Same
87
+ family as ``core/check_hafnian_dd.cu``.
88
+ """
89
+ a = f = b = e = c = 1.0
90
+ d = delta - 2.0
91
+ B = np.array([[0, a, b, c], [a, 0, d, e], [b, d, 0, f], [c, e, f, 0]], dtype=np.complex128)
92
+ g = np.random.default_rng(seed)
93
+ G = g.standard_normal((4, 4))
94
+ R = G + G.T
95
+ np.fill_diagonal(R, 0.0) # ordinary hafnian ignores the diagonal
96
+ A = np.zeros((8, 8), dtype=np.complex128)
97
+ A[:4, :4] = B
98
+ A[4:, 4:] = R
99
+ return A
100
+
101
+
102
+ def cancellation_loop_hafnian(delta: float, seed: int) -> np.ndarray:
103
+ """Symmetric matrix whose loop hafnian nearly cancels to ``delta`` (6x6).
104
+
105
+ A 2x2 block ``[[1,2],[2,delta-2]]`` has ``lhaf = h + g*k = 2 + (delta-2) =
106
+ delta``, direct-summed with a well-conditioned remainder (nonzero diagonal =
107
+ loops). Same family as ``core/check_loop_hafnian_dd.cu``.
108
+ """
109
+ B = np.array([[1.0, 2.0], [2.0, delta - 2.0]], dtype=np.complex128)
110
+ g = np.random.default_rng(seed)
111
+ G = g.standard_normal((4, 4))
112
+ R = (G + G.T).astype(np.complex128) # diagonal kept (loop weights)
113
+ A = np.zeros((6, 6), dtype=np.complex128)
114
+ A[:2, :2] = B
115
+ A[2:, 2:] = R
116
+ return A
117
+
118
+
119
+ def cancellation_torontonian(a: float) -> np.ndarray:
120
+ """Single-mode O = diag(a, a): ``tor = a/(1-a)``, but the kernel computes
121
+ ``1/sqrt(det(I-O)) - 1`` with a catastrophic ``(1+a+...) - 1`` cancellation as
122
+ ``a -> 0``. Same family as ``core/check_torontonian_dd.cu``. Real-domain."""
123
+ return np.array([[a, 0.0], [0.0, a]], dtype=np.complex128)
124
+
125
+
126
+ # --- physical (realistic, well-conditioned) inputs -------------------------
127
+ # Representative of how each function is actually used in photonic sampling, and
128
+ # well-conditioned (FP64 is accurate here) -- the complement of the adversarial
129
+ # cancellation families above.
130
+
131
+ def physical_permanent(n: int, seed: int) -> np.ndarray:
132
+ """A Haar-random interferometer ``U`` (n x n): ``perm(U)`` is a standard
133
+ boson-sampling amplitude. Well-conditioned (unit-modulus singular values)."""
134
+ return haar_unitary(n, seed)
135
+
136
+
137
+ def physical_hafnian(n: int, seed: int) -> np.ndarray:
138
+ """A Gaussian-boson-sampling kernel ``B = U diag(tanh r) U^T`` (n x n complex
139
+ symmetric, modest squeezing) -- the matrix whose hafnian gives a GBS
140
+ amplitude. ``n`` should be even (haf of odd size is 0)."""
141
+ U = haar_unitary(n, seed)
142
+ g = np.random.default_rng(seed + 99)
143
+ r = g.uniform(0.1, 0.5, n)
144
+ return (U @ np.diag(np.tanh(r)) @ U.T).astype(np.complex128)
145
+
146
+
147
+ def physical_loop_hafnian(n: int, seed: int) -> np.ndarray:
148
+ """A GBS kernel with a displacement-like diagonal (the loop weights) -- the
149
+ loop-hafnian (displaced GBS) input. ``n`` even."""
150
+ B = physical_hafnian(n, seed).copy()
151
+ g = np.random.default_rng(seed + 7)
152
+ np.fill_diagonal(B, g.uniform(-0.3, 0.3, n) + 1j * g.uniform(-0.3, 0.3, n))
153
+ return B
154
+
155
+
156
+ def physical_torontonian(n_modes: int, seed: int, scale: float = 0.1) -> np.ndarray:
157
+ """A small-norm real symmetric ``O`` (2n x 2n) -- a physical threshold-detector
158
+ matrix (``I - O_S`` stays positive definite). Real domain."""
159
+ g = np.random.default_rng(seed)
160
+ M = g.standard_normal((2 * n_modes, 2 * n_modes)) * scale
161
+ return ((M + M.T) / 2).astype(np.complex128)
162
+
163
+
164
+ # --- loss / mixed-state inputs ---------------------------------------------
165
+ # A pure squeezed + interferometer state passed through a uniform loss channel
166
+ # (transmission eta < 1) becomes MIXED: its Husimi covariance has det(Q) > 1, and the
167
+ # matrices the kernels consume (the A-matrix block for the (loop) hafnian, O = I - Q^-1
168
+ # for the torontonian) are structurally different from the pure case -- the regime a
169
+ # real lossy GBS experiment produces. This is the third input regime alongside
170
+ # "physical" (pure, well-conditioned) and "adversarial" (tunable cancellation).
171
+
172
+ def _qmat(cov: np.ndarray, hbar: float = 2.0) -> np.ndarray:
173
+ """Husimi Q covariance from an xxpp Wigner covariance (matches sampling.gbs._qmat)."""
174
+ N = len(cov) // 2
175
+ x = cov[:N, :N] * 2 / hbar
176
+ xp = cov[:N, N:] * 2 / hbar
177
+ p = cov[N:, N:] * 2 / hbar
178
+ aidaj = (x + p + 1j * (xp - xp.T) - 2 * np.eye(N)) / 4
179
+ aiaj = (x - p + 1j * (xp + xp.T)) / 4
180
+ return np.block([[aidaj, aiaj.conj()], [aiaj, aidaj.conj()]]) + np.identity(2 * N)
181
+
182
+
183
+ def _lossy_cov(modes: int, seed: int, eta: float = 0.6, hbar: float = 2.0) -> np.ndarray:
184
+ """xxpp Wigner covariance of a pure squeezed + interferometer state after a uniform
185
+ loss channel of transmission ``eta`` (eta=1 is the pure state; eta<1 is mixed)."""
186
+ g = np.random.default_rng(seed)
187
+ r = g.uniform(0.3, 0.6, modes)
188
+ z = (g.standard_normal((modes, modes)) + 1j * g.standard_normal((modes, modes))) / np.sqrt(2.0)
189
+ U, rr = np.linalg.qr(z); ph = np.diagonal(rr).copy(); ph /= np.abs(ph); U = U * ph
190
+ sq = np.block([[np.diag(np.exp(-r)), np.zeros((modes, modes))],
191
+ [np.zeros((modes, modes)), np.diag(np.exp(r))]])
192
+ interf = np.block([[U.real, -U.imag], [U.imag, U.real]])
193
+ S = interf @ sq
194
+ pure = (hbar / 2.0) * S @ S.T
195
+ return eta * pure + (1.0 - eta) * (hbar / 2.0) * np.eye(2 * modes) # loss -> mixed
196
+
197
+
198
+ def _amat(cov: np.ndarray, hbar: float = 2.0) -> np.ndarray:
199
+ """A-matrix ``X (I - Q^-1)`` of a Gaussian state (the (loop) hafnian's matrix)."""
200
+ m = cov.shape[0] // 2
201
+ Q = _qmat(cov, hbar)
202
+ X = np.block([[np.zeros((m, m)), np.eye(m)], [np.eye(m), np.zeros((m, m))]])
203
+ return X @ (np.eye(2 * m) - np.linalg.inv(Q))
204
+
205
+
206
+ def loss_hafnian(n: int, seed: int, eta: float = 0.6) -> np.ndarray:
207
+ """The A-matrix block (n x n complex symmetric) of a lossy/mixed GBS state -- the
208
+ matrix the hafnian consumes in the mixed regime. ``n`` even."""
209
+ A = _amat(_lossy_cov(n, seed, eta))
210
+ B = A[:n, :n]
211
+ return np.ascontiguousarray(0.5 * (B + B.T)).astype(np.complex128) # enforce symmetry
212
+
213
+
214
+ def loss_loop_hafnian(n: int, seed: int, eta: float = 0.6) -> np.ndarray:
215
+ """The lossy A-block with displacement-derived loop weights on the diagonal -- the
216
+ loop hafnian's input for a lossy displaced state. ``n`` even."""
217
+ B = loss_hafnian(n, seed, eta).copy()
218
+ g = np.random.default_rng(seed + 3)
219
+ np.fill_diagonal(B, g.uniform(-0.3, 0.3, n) + 1j * g.uniform(-0.3, 0.3, n))
220
+ return B
221
+
222
+
223
+ def loss_torontonian(n_modes: int, seed: int, eta: float = 0.6) -> np.ndarray:
224
+ """``O = I - Q^-1`` (2n x 2n, real) of a lossy/mixed Gaussian state -- the threshold-
225
+ detector matrix a real lossy GBS experiment produces."""
226
+ Q = _qmat(_lossy_cov(n_modes, seed, eta))
227
+ O = np.eye(2 * n_modes) - np.linalg.inv(Q)
228
+ return np.ascontiguousarray(np.real(O)).astype(np.complex128) # physical real domain
229
+
230
+
231
+ def haar_unitary(m: int, seed: int) -> np.ndarray:
232
+ """Haar-random m x m unitary (QR of a complex Ginibre matrix, phase-fixed)."""
233
+ g = np.random.default_rng(seed)
234
+ z = (g.standard_normal((m, m)) + 1j * g.standard_normal((m, m))) / np.sqrt(2.0)
235
+ q, r = np.linalg.qr(z)
236
+ ph = np.diagonal(r).copy()
237
+ ph /= np.abs(ph)
238
+ return q * ph # columns scaled by the phases of diag(R)
239
+
240
+
241
+ def loss_permanent(n: int, seed: int) -> np.ndarray:
242
+ """The top-left ``n x n`` block of a Haar-random ``2n x 2n`` unitary -- a *sub-unitary*
243
+ (a contraction, singular values <= 1): the linear-optical map of a LOSSY interferometer,
244
+ the permanent's mixed-state analog (boson sampling with loss). Well-conditioned."""
245
+ U = haar_unitary(2 * n, seed)
246
+ return np.ascontiguousarray(U[:n, :n])
247
+
248
+
249
+ # --- adversarial (tunable cancellation) inputs, generalized to any dim -------
250
+ # The canonical near-cancelling block (+) a well-conditioned remainder. perm/haf/tor all
251
+ # factorize over a direct sum (perm(A(+)B)=perm A * perm B; same for haf; tor(O1(+)O2)=
252
+ # tor O1 * tor O2), so the small block sets the whole value's cancellation while the
253
+ # remainder fills out the requested size. A MODERATE strength (kappa ~ 1e6) -- enough to
254
+ # show FP64 shedding digits in the achieved-error column, but not so ill-conditioned that
255
+ # the GPU and CPU determinant/linear-algebra routines legitimately diverge (which would
256
+ # false-trip the public-path checksum gate). The accuracy STUDY (bench.accuracy) sweeps the
257
+ # strength all the way to the FP64 cliff; here we want a representative stressed throughput
258
+ # input on which GPU and CPU still agree.
259
+ _ADV_DELTA = 1e-6
260
+ _ADV_TOR_A = 1e-6
261
+
262
+
263
+ def adversarial_permanent(n: int, seed: int) -> np.ndarray:
264
+ """``make_cancellation_matrix`` at the benchmark strength (2x2 block (+) remainder)."""
265
+ return make_cancellation_matrix(n, _ADV_DELTA, seed)
266
+
267
+
268
+ def adversarial_hafnian(n: int, seed: int) -> np.ndarray:
269
+ """4x4 near-cancelling hafnian block (+) a well-conditioned ``(n-4)`` symmetric
270
+ remainder. ``n`` even, ``>= 4``. Generalizes ``cancellation_hafnian`` to any dim."""
271
+ if n < 4 or n % 2:
272
+ raise ValueError("adversarial hafnian needs even n >= 4")
273
+ a = f = b = e = c = 1.0
274
+ d = _ADV_DELTA - 2.0
275
+ A = np.zeros((n, n), dtype=np.complex128)
276
+ A[:4, :4] = np.array([[0, a, b, c], [a, 0, d, e], [b, d, 0, f], [c, e, f, 0]],
277
+ dtype=np.complex128)
278
+ if n > 4:
279
+ g = np.random.default_rng(seed)
280
+ G = g.standard_normal((n - 4, n - 4))
281
+ R = G + G.T
282
+ np.fill_diagonal(R, 0.0) # ordinary hafnian ignores the diagonal
283
+ A[4:, 4:] = R
284
+ return A
285
+
286
+
287
+ def adversarial_loop_hafnian(n: int, seed: int) -> np.ndarray:
288
+ """2x2 near-cancelling loop-hafnian block (+) a ``(n-2)`` symmetric remainder (diagonal
289
+ kept = loops). ``n`` even, ``>= 2``. Generalizes ``cancellation_loop_hafnian``."""
290
+ if n < 2 or n % 2:
291
+ raise ValueError("adversarial loop hafnian needs even n >= 2")
292
+ A = np.zeros((n, n), dtype=np.complex128)
293
+ A[:2, :2] = np.array([[1.0, 2.0], [2.0, _ADV_DELTA - 2.0]], dtype=np.complex128)
294
+ if n > 2:
295
+ g = np.random.default_rng(seed)
296
+ G = g.standard_normal((n - 2, n - 2))
297
+ A[2:, 2:] = (G + G.T).astype(np.complex128)
298
+ return A
299
+
300
+
301
+ def adversarial_torontonian(n_modes: int, seed: int) -> np.ndarray:
302
+ """One catastrophic-cancellation mode ``diag(a, a)`` (``tor_1 = 1/sqrt(1-a) - 1`` with a
303
+ ``(1 + a/2 + ...) - 1`` cancellation as ``a -> 0``) (+) ``n_modes-1`` physical small-norm
304
+ modes. Real, ``I - O_S`` positive definite for every S; ``dim = 2*n_modes``."""
305
+ O = np.zeros((2 * n_modes, 2 * n_modes))
306
+ if n_modes > 1:
307
+ P = np.real(physical_torontonian(n_modes - 1, seed)) # 2(n-1) x 2(n-1)
308
+ idx = list(range(1, n_modes)) + list(range(n_modes + 1, 2 * n_modes))
309
+ O[np.ix_(idx, idx)] = P
310
+ O[0, 0] = _ADV_TOR_A
311
+ O[n_modes, n_modes] = _ADV_TOR_A # mode 0's a / a-dagger entries
312
+ return np.ascontiguousarray(O).astype(np.complex128)
313
+
314
+
315
+ # --- the single shared benchmark workload generator ------------------------
316
+
317
+ _BENCH_FAMILIES = {
318
+ "physical": {"perm": lambda d, s: physical_permanent(d, s),
319
+ "haf": lambda d, s: physical_hafnian(d, s),
320
+ "lhaf": lambda d, s: physical_loop_hafnian(d, s),
321
+ "tor": lambda d, s: physical_torontonian(d // 2, s)},
322
+ "loss": {"perm": lambda d, s: loss_permanent(d, s),
323
+ "haf": lambda d, s: loss_hafnian(d, s),
324
+ "lhaf": lambda d, s: loss_loop_hafnian(d, s),
325
+ "tor": lambda d, s: loss_torontonian(d // 2, s)},
326
+ "adversarial": {"perm": lambda d, s: adversarial_permanent(d, s),
327
+ "haf": lambda d, s: adversarial_hafnian(d, s),
328
+ "lhaf": lambda d, s: adversarial_loop_hafnian(d, s),
329
+ "tor": lambda d, s: adversarial_torontonian(d // 2, s)},
330
+ }
331
+ BENCH_REGIMES = tuple(_BENCH_FAMILIES)
332
+
333
+
334
+ def bench_batch(func: str, dim: int, batch: int, regime: str = "physical",
335
+ seed: int = 0) -> np.ndarray:
336
+ """A uniform ``(batch, dim, dim)`` complex128 stack for ``func`` in ``regime`` -- THE
337
+ single workload generator shared by the GPU, CPU, and The Walrus benchmarks, so a
338
+ throughput comparison is *same-input* (docs/DESIGN.md §9). ``regime`` in
339
+ ``{physical, loss, adversarial}``; for the torontonian ``dim = 2*modes``. Element ``k``
340
+ uses ``seed + k`` so the batch is varied yet reproducible: the SAME
341
+ ``(func, dim, regime, seed)`` yields the SAME matrices for every engine."""
342
+ if regime not in _BENCH_FAMILIES:
343
+ raise ValueError(f"unknown regime {regime!r}; expected one of {BENCH_REGIMES}")
344
+ fam = _BENCH_FAMILIES[regime]
345
+ if func not in fam:
346
+ raise ValueError(f"unknown func {func!r}; expected one of {tuple(fam)}")
347
+ gen = fam[func]
348
+ return np.ascontiguousarray(np.stack([gen(dim, seed + k) for k in range(batch)]))
bench/_provenance.py ADDED
@@ -0,0 +1,104 @@
1
+ """Shared provenance for every benchmark artifact (the frozen-experiment record).
2
+
3
+ Anchor §9 / the reproducibility contract: *every* artifact -- throughput, end-to-end,
4
+ accuracy, sampler -- carries the same provenance block so a result can be reproduced
5
+ exactly: the code **commit**, the pinned **container digest**, the GPU, and when it ran.
6
+
7
+ The rented box has no ``.git`` (rsync excludes it) and its image is pinned by digest,
8
+ so both are passed in by the session script as environment variables captured on the
9
+ host / from the container before the run:
10
+
11
+ * ``GBS_COMMIT`` -- ``git rev-parse --short HEAD`` of the uploaded tree.
12
+ * ``GBS_CONTAINER_DIGEST`` -- the image digest the instance was launched from
13
+ (e.g. ``nvidia/cuda:12.4.1-devel-ubuntu22.04@sha256:...``); pins the toolchain.
14
+
15
+ Both fall back to a local probe (git; ``/etc/gbs_container_digest`` if present) and to
16
+ ``None`` -- recorded honestly rather than faked.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import os
22
+ import platform
23
+ import re
24
+ import subprocess
25
+ from datetime import datetime, timezone
26
+ from typing import Any
27
+
28
+
29
+ def _gpu_info() -> dict[str, Any] | None:
30
+ """GPU model / compute capability / driver / memory via nvidia-smi (None off-device)."""
31
+ try:
32
+ out = subprocess.run(
33
+ ["nvidia-smi", "--query-gpu=name,compute_cap,driver_version,memory.total",
34
+ "--format=csv,noheader"], capture_output=True, text=True, timeout=10).stdout.strip()
35
+ if out:
36
+ name, cc, drv, mem = (x.strip() for x in out.splitlines()[0].split(","))
37
+ return {"name": name, "compute_cap": cc, "driver": drv, "memory_total": mem}
38
+ except Exception:
39
+ pass
40
+ return None
41
+
42
+
43
+ def _cuda_version() -> str | None:
44
+ try:
45
+ out = subprocess.run(["nvcc", "--version"], capture_output=True, text=True, timeout=10).stdout
46
+ m = re.search(r"release (\d+\.\d+)", out)
47
+ return m.group(1) if m else None
48
+ except Exception:
49
+ return None
50
+
51
+
52
+ def environment() -> dict[str, Any]:
53
+ """The machine the artifact was produced on, so a throughput/accuracy number is
54
+ self-describing: the GPU (model/driver/CUDA), the CPU + logical cores, and the BLAS/
55
+ OpenMP thread caps (which materially affect the CPU-side timings; see the OpenBLAS
56
+ many-core-host segfault mitigation in gpu_session.sh)."""
57
+ return {
58
+ "gpu": _gpu_info(),
59
+ "cuda": _cuda_version(),
60
+ "cpu": {"processor": platform.processor() or None,
61
+ "machine": platform.machine(), "logical_cores": os.cpu_count()},
62
+ "blas_threads": {v: os.environ.get(v) for v in
63
+ ("OPENBLAS_NUM_THREADS", "OMP_NUM_THREADS", "MKL_NUM_THREADS", "GOTO_NUM_THREADS")},
64
+ "platform": platform.platform(),
65
+ "python": platform.python_version(),
66
+ }
67
+
68
+
69
+ def commit() -> str | None:
70
+ env = os.environ.get("GBS_COMMIT", "").strip()
71
+ if env:
72
+ return env
73
+ try:
74
+ return subprocess.run(["git", "rev-parse", "--short", "HEAD"],
75
+ capture_output=True, text=True).stdout.strip() or None
76
+ except Exception:
77
+ return None
78
+
79
+
80
+ def container_digest() -> str | None:
81
+ """The pinned container image digest the run was launched from (or None)."""
82
+ env = os.environ.get("GBS_CONTAINER_DIGEST", "").strip()
83
+ if env:
84
+ return env
85
+ try: # a session may drop the digest here on the box
86
+ p = "/etc/gbs_container_digest"
87
+ if os.path.exists(p):
88
+ with open(p) as f:
89
+ return f.read().strip() or None
90
+ except Exception:
91
+ pass
92
+ return None
93
+
94
+
95
+ def provenance() -> dict[str, Any]:
96
+ """The common provenance block embedded in every artifact (commit + container digest +
97
+ machine environment), so a result reproduces from the file alone (docs/DESIGN.md §9)."""
98
+ return {
99
+ "commit": commit(),
100
+ "container_digest": container_digest(),
101
+ "hostname": platform.node() or None,
102
+ "captured_utc": datetime.now(timezone.utc).isoformat(),
103
+ "environment": environment(),
104
+ }
bench/accuracy.py ADDED
@@ -0,0 +1,182 @@
1
+ """Accuracy characterization for ALL FOUR functions (the measured boundary, §6).
2
+
3
+ For each of permanent / hafnian / loop hafnian / torontonian, sweep a tunable
4
+ cancellation family and record, against the independent ``mpmath`` reference:
5
+
6
+ * ``rel_err_fp64`` -- the native double-precision relative error (the boundary:
7
+ it grows as the result cancels);
8
+ * ``rel_err_dd`` -- the double-double relative error, when the compiled GPU
9
+ extension is available (the *real* DD kernels, host-emulated through the
10
+ host-shim build or run on a GPU). DD arithmetic is **internal**: the kernels
11
+ collapse the double-double result back to ``complex128`` on output, so this
12
+ measures DD's ability to recover a correct FP64 answer after cancellation, not
13
+ 31-digit output.
14
+
15
+ Writes a self-describing JSON artifact to ``results/accuracy/`` (append-only).
16
+ This generalizes ``accuracy_permanent.py`` (which keeps the detailed
17
+ condition-number sweep for the permanent) to the whole library.
18
+
19
+ uv run python -m bench.accuracy --dps 60
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import argparse
25
+ import json
26
+ import platform
27
+ from datetime import datetime, timezone
28
+ from importlib.metadata import version
29
+ from pathlib import Path
30
+ from typing import Any, Callable
31
+
32
+ import mpmath
33
+ import numpy as np
34
+
35
+ import gbskernels
36
+ import highprec_ref
37
+ from bench import _inputs, _provenance
38
+
39
+ DEFAULT_OUT = Path(__file__).resolve().parent.parent / "results" / "accuracy"
40
+
41
+ # mpmath ground-truth reference per function.
42
+ _REF = {
43
+ "perm": highprec_ref.permanent_mp, "haf": highprec_ref.hafnian_mp,
44
+ "lhaf": highprec_ref.loop_hafnian_mp, "tor": highprec_ref.torontonian_mp,
45
+ }
46
+
47
+ # ADVERSARIAL: a tunable cancellation family (knob -> matrix) + the knob grid.
48
+ _ADVERSARIAL: dict[str, tuple[Callable[[float], np.ndarray], list[float]]] = {
49
+ "perm": (lambda d: _inputs.make_cancellation_matrix(6, d, seed=1),
50
+ [1e-1, 1e-2, 1e-4, 1e-6, 1e-8, 1e-10, 1e-12, 1e-14]),
51
+ "haf": (lambda d: _inputs.cancellation_hafnian(d, seed=1), [1e-2, 1e-4, 1e-6, 1e-8, 1e-10]),
52
+ "lhaf": (lambda d: _inputs.cancellation_loop_hafnian(d, seed=1), [1e-2, 1e-4, 1e-6, 1e-8, 1e-10]),
53
+ "tor": (lambda d: _inputs.cancellation_torontonian(d), [1e-2, 1e-4, 1e-6, 1e-8, 1e-10]),
54
+ }
55
+
56
+ # PHYSICAL: realistic well-conditioned inputs (size -> matrix) + the size grid
57
+ # (capped so the DD kernels and the mpmath reference stay feasible).
58
+ _PHYSICAL: dict[str, tuple[Callable[[int, int], np.ndarray], list[int]]] = {
59
+ "perm": (_inputs.physical_permanent, [4, 8, 12]),
60
+ "haf": (_inputs.physical_hafnian, [4, 8, 12]),
61
+ "lhaf": (_inputs.physical_loop_hafnian, [4, 8, 10]),
62
+ "tor": (_inputs.physical_torontonian, [2, 4, 6]), # n = modes (matrix 2n)
63
+ }
64
+
65
+ # LOSS / mixed-state regime: matrices from a lossy (mixed) Gaussian state -- the third
66
+ # realistic regime (the permanent is a unitary boson-sampling amplitude, with no
67
+ # Gaussian loss analog, so it has no loss section).
68
+ _LOSS: dict[str, tuple[Callable[[int, int], np.ndarray], list[int]]] = {
69
+ "haf": (_inputs.loss_hafnian, [4, 8]),
70
+ "lhaf": (_inputs.loss_loop_hafnian, [4, 8]),
71
+ "tor": (_inputs.loss_torontonian, [2, 4]), # n = modes (matrix 2n)
72
+ }
73
+
74
+
75
+ def _rel_err_mp(approx: complex, exact_mp: Any, dps: int) -> float:
76
+ with mpmath.workdps(dps):
77
+ a = mpmath.mpc(complex(approx).real, complex(approx).imag)
78
+ return float(abs(a - exact_mp) / abs(exact_mp))
79
+
80
+
81
+ def _errors(func: str, A: np.ndarray, dps: int, have_dd: bool) -> dict[str, float]:
82
+ """FP64 and (when the GPU backend is present) DD relative error vs mpmath.
83
+
84
+ FP64 is evaluated on the SAME backend as DD when the extension is present, so
85
+ the artifact records the FP64<->DD crossover on the device (not a CPU FP64 vs
86
+ GPU DD comparison). With no extension, FP64 falls back to the CPU reference."""
87
+ backend = "gpu" if have_dd else "cpu"
88
+ fp = getattr(gbskernels, func)(A, backend=backend)
89
+ with mpmath.workdps(dps):
90
+ exact = _REF[func](A, dps=dps)
91
+ out = {"rel_err_fp64": _rel_err_mp(fp, exact, dps)}
92
+ if have_dd:
93
+ dd = getattr(gbskernels, func)(A, precision="dd", backend="gpu")
94
+ out["rel_err_dd"] = _rel_err_mp(dd, exact, dps)
95
+ return out
96
+
97
+
98
+ def run(dps: int = 60, out_dir: Path | None = None) -> tuple[dict[str, Any], Path]:
99
+ have_dd = gbskernels.gpu_available()
100
+ dps = max(dps, 80)
101
+ sweeps: dict[str, Any] = {}
102
+ for func in _REF:
103
+ family, deltas = _ADVERSARIAL[func]
104
+ adversarial = [dict(delta=d, **_errors(func, family(d), dps, have_dd)) for d in deltas]
105
+ pfamily, sizes = _PHYSICAL[func]
106
+ physical = [dict(n=n, **_errors(func, pfamily(n, seed=1), dps, have_dd)) for n in sizes]
107
+ sweeps[func] = {"adversarial": adversarial, "physical": physical}
108
+ if func in _LOSS: # third regime: lossy / mixed-state inputs (Gaussian only)
109
+ lfamily, lsizes = _LOSS[func]
110
+ sweeps[func]["loss"] = [dict(n=n, **_errors(func, lfamily(n, seed=1), dps, have_dd))
111
+ for n in lsizes]
112
+
113
+ # Whether the DD numbers were produced on a real GPU or host-emulated (the
114
+ # same kernel source either way; the distinction matters for the "GPU-vs-mpmath"
115
+ # claim, so it is recorded honestly). The signal is the extension's own
116
+ # compile-time build flag -- NOT the host OS -- so a no-GPU CI runner is
117
+ # correctly "host-shim", never mislabelled "gpu".
118
+ dd_device = gbskernels.gpu_backend_kind()
119
+ # FP64 was measured on the same backend as DD when the extension is present
120
+ # (so the FP64<->DD crossover is established on-device), else the CPU reference.
121
+ fp64_device = dd_device if have_dd else "cpu"
122
+
123
+ artifact = {
124
+ "kind": "accuracy_all_functions",
125
+ "created_utc": datetime.now(timezone.utc).isoformat(),
126
+ **_provenance.provenance(), # commit + container_digest + hostname (every artifact)
127
+ "dd_measured": have_dd,
128
+ "fp64_backend": fp64_device, # where the FP64 numbers came from
129
+ "dd_backend": dd_device, # "gpu" (real device) | "host-shim" (CPU emulation)
130
+ "note": ("Work-precision study for all four functions across THREE input regimes vs an "
131
+ "independent mpmath reference: PHYSICAL (pure, well-conditioned), ADVERSARIAL "
132
+ "(tunable cancellation), and LOSS (lossy/mixed-state matrices; Gaussian "
133
+ "functions only). DD is an INTERNAL precision tier: kernels collapse the "
134
+ "double-double result to complex128 on output, recovering a correct FP64 "
135
+ "answer under cancellation (not exposing 31-digit results)."),
136
+ "env": {"python": platform.python_version(), "numpy": version("numpy"),
137
+ "mpmath": version("mpmath"), "platform": platform.platform()},
138
+ "params": {"dps": dps, "dtype": "complex128",
139
+ "sections": "physical (size sweep) + adversarial (cancellation sweep) "
140
+ "+ loss (mixed-state, haf/lhaf/tor)"},
141
+ "sweeps": sweeps,
142
+ }
143
+ out_dir = Path(out_dir) if out_dir is not None else DEFAULT_OUT
144
+ out_dir.mkdir(parents=True, exist_ok=True)
145
+ stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
146
+ path = out_dir / f"accuracy_all_{stamp}.json"
147
+ path.write_text(json.dumps(artifact, indent=2))
148
+ return artifact, path
149
+
150
+
151
+ def _print(artifact: dict[str, Any], path: Path) -> None:
152
+ dd = artifact["dd_measured"]
153
+ print(f"# accuracy: all four functions, physical + adversarial -> {path}")
154
+ print(f"# DD measured: {dd} (backend: {artifact.get('dd_backend')}) | the work-precision study, §6")
155
+ for func, sec in artifact["sweeps"].items():
156
+ print(f"\n {func} -- physical (well-conditioned): {'n':>3} {'rel_err_fp64':>13}" +
157
+ (" rel_err_dd" if dd else ""))
158
+ for r in sec["physical"]:
159
+ line = f" {r['n']:>3} {r['rel_err_fp64']:>13.2e}"
160
+ if "rel_err_dd" in r:
161
+ line += f" {r['rel_err_dd']:>10.2e}"
162
+ print(line)
163
+ print(f" {func} -- adversarial (cancellation): {'delta':>8} {'rel_err_fp64':>13}" +
164
+ (" rel_err_dd" if dd else ""))
165
+ for r in sec["adversarial"]:
166
+ line = f" {r['delta']:>8.0e} {r['rel_err_fp64']:>13.2e}"
167
+ if "rel_err_dd" in r:
168
+ line += f" {r['rel_err_dd']:>10.2e}"
169
+ print(line)
170
+
171
+
172
+ def main() -> None:
173
+ p = argparse.ArgumentParser(description=__doc__)
174
+ p.add_argument("--dps", type=int, default=60)
175
+ p.add_argument("--out", type=Path, default=None)
176
+ args = p.parse_args()
177
+ artifact, path = run(args.dps, args.out)
178
+ _print(artifact, path)
179
+
180
+
181
+ if __name__ == "__main__":
182
+ main()