From 0e73e331c1ad554bb9c35dc43a7c7b8568345d9f Mon Sep 17 00:00:00 2001 From: meandmytram Date: Mon, 14 Sep 2026 19:27:49 -0400 Subject: [PATCH] Correct figures in the comments and docs added by #543 An audit of the claims #543 added, against the result files and fresh measurements, found five wrong figures: - svd() comment and the convergence test's docstring: the chi_max=400 [[72,12,6]] failure was 21 of 22 shots with a non-trivial error, not "24 of 24" (that count mixed repeated runs of one shot with a second). - svd() comment: the pre-reduction saved at most 7% and nothing measurable on most workloads, rather than "0-7%". - _to_numpy docstring: the per-call cupy import cost about 11,000 failed imports per decode, between roughly a tenth and a third of a decode (surface_bitflip 4-13%, dem_d3 about a third, timed on pre-#543 main), not a flat "~18%". - _contract_cached docstring: a shape-keyed cache misses 1-2% of zip-up calls on the DEM workloads and 23% on the RCM surface workload, not "7-14% on large codes". - test_convergence docstring: the test takes about 20 minutes on an idle laptop, not 30-60 minutes per decode. The benchmark README's classical_ldpc row now says three LDPC codes. Claims checked and left as they are: Apple M5 10 cores, the ~1700-site DEM chain (1677 mechanisms), the DEM workload parameters, the opt_einsum overhead at small chi (42% of a zip step at chi=32), and agreement of pre- and post-#543 decoders at chi=1e5 (1.6e-14 on 22 posteriors). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01S6mbxc9eJDQMVx7tjvYwud --- benchmarks/README.md | 2 +- mdopt/contractor/contractor.py | 5 +++-- mdopt/utils/utils.py | 11 +++++++---- tests/decoding/test_convergence.py | 7 ++++--- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/benchmarks/README.md b/benchmarks/README.md index b72896e7..57229d1f 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -10,7 +10,7 @@ and a change is only accepted if the fingerprints still match. | `surface_bitflip` | 5x5 surface code, bit-flip noise, 6 shots, χ = 64, natural qubit order | verdicts exact, posterior entries within 1e-2 | | `css_optimised` | the same code under `qubit_order_strategy="Optimised"` (reverse Cuthill-McKee) | as above | | `shor_depolarising` | Shor code, depolarising noise, 40 shots, χ = 128: the dense-readout path | as above | -| `classical_ldpc` | random (3,4) LDPC code, XOR constraints + dephasing-DMRG readout | overlaps exact to 1e-10 | +| `classical_ldpc` | three random (3,4) LDPC codes, XOR constraints + dephasing-DMRG readout | overlaps exact to 1e-10 | | `dmrg_ground_state` | DMRG on a 24-site transverse-field Ising chain | energy exact to 1e-10 | | `dem_d3` | circuit-level detector error model, d=3 r=3 p=0.8%, eight busiest syndromes, χ = 32 | verdicts exact, class masses within 1e-2 | | `dem_d5` | the same at d=5 r=5 p=0.5%, busiest syndrome, χ = 32 | as above | diff --git a/mdopt/contractor/contractor.py b/mdopt/contractor/contractor.py index 25f297a9..79d23de8 100644 --- a/mdopt/contractor/contractor.py +++ b/mdopt/contractor/contractor.py @@ -23,8 +23,9 @@ def _contract_cached(subscripts, path, backend, *tensors): rebuilds path metadata on every call even when ``optimize`` is explicit. An expression with an explicit path is shape-independent, so it is cached per (subscripts, path) only -- keying on operand shapes made - truncation's data-dependent bond dimensions miss 7-14% of calls on - large codes -- and built from whatever shapes the first call carries. + truncation's data-dependent bond dimensions miss the cache (1-2% of the + zip-up calls on the circuit-level DEM workloads, 23% on the RCM + surface-code one) -- and built from whatever shapes the first call carries. """ key = (subscripts, path) expression = _EXPRESSIONS.get(key) diff --git a/mdopt/utils/utils.py b/mdopt/utils/utils.py index 667aaa90..e5f3189e 100644 --- a/mdopt/utils/utils.py +++ b/mdopt/utils/utils.py @@ -16,7 +16,8 @@ def _to_numpy(a): Resolved through the backend's own transfer, which honours MDOPT_BACKEND and the CUDA device probe; a per-call ``import cupy`` here once cost - ~18% of a decoding run. + about 11,000 failed imports per decode, between roughly a tenth and + a third of the decode depending on the workload. """ host = xp.to_host(a) try: @@ -93,12 +94,14 @@ def svd( # (the macOS 14+ arm64 wheels) it corrupted memory on the # decoders' rank-deficient matrices: numpy.linalg.qr died # with SIGBUS, and a [[72,12,6]] decode at chi_max=400 - # returned wrong verdicts on 24 of 24 shots while every - # unit test and benchmark fingerprint still passed; + # returned wrong verdicts on 21 of 22 shots with a + # non-trivial error, while every unit test and benchmark + # fingerprint still passed; # - with OpenBLAS it is memory-safe but not equivalent under # truncation: it moved a chi_max=64 surface-code posterior # entry by 0.017, beyond the benchmark suite's tolerance; - # - it saved 0-7% on the benchmark workloads. + # - it saved at most 7% on the benchmark workloads, and + # nothing measurable on most of them. # Reintroducing it needs `benchmarks/bench_suite.py --check` # and tests/decoding/test_convergence.py to pass. # No finiteness pre-scan: a non-finite input makes the SVD diff --git a/tests/decoding/test_convergence.py b/tests/decoding/test_convergence.py index 25c5ed98..7d72bc6d 100644 --- a/tests/decoding/test_convergence.py +++ b/tests/decoding/test_convergence.py @@ -29,9 +29,10 @@ def test_bb_72_12_6_natural_order_single_error_converges_in_chi(): This instance exposed the QR pre-reduction of ``svd`` (since removed): with it, the decode returned a flat or wrongly peaked - posterior at chi_max=400 on NumPy/Accelerate builds -- 24 of 24 shots - -- while every unit test and benchmark fingerprint still passed. Each - chi_max=400 decode takes 30-60 minutes on a laptop. + posterior at chi_max=400 on NumPy/Accelerate builds, as did 21 of the 22 + shots with a non-trivial error in the natural-order experiment, while + every unit test and benchmark fingerprint still passed. The test takes + about 20 minutes on an idle laptop. """ code = create_bb_code(6, 6, "x**3 + y + y**2", "y**3 + x + x**2") error = "I" * 58 + "Z" + "I" * 13