Skip to content

Commit 5e107ea

Browse files
committed
fix: example_batch.py used weight-4 surface code with weight<=2-only decoders
CPUBatchDecoder/OpenCLBatchDecoder/CUDABatchDecoder are Union-Find-based and only support weight <= 2 checks. generate_surface_code_checks() produces weight-4 (four-body) checks, which these decoders correctly reject at construction. Switched the demo to generate_ring_code_checks(), the natural weight-2 graph-like code family for this decoder class. Verified: examples/example_batch.py runs cleanly end-to-end (CPU path, OpenCL/CUDA skip gracefully when unavailable). python/tests/test_examples.py now passes (1 passed in 154.39s).
1 parent f7e264e commit 5e107ea

1 file changed

Lines changed: 25 additions & 17 deletions

File tree

examples/example_batch.py

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,72 +21,79 @@
2121
CUDABatchDecoder,
2222
cuda_is_available,
2323
opencl_is_available,
24-
generate_surface_code_checks,
24+
generate_ring_code_checks,
2525
)
2626

27+
2728
def main():
28-
# Use distance 5 surface code
29-
checks, n_qubits = generate_surface_code_checks(5)
29+
# CPUBatchDecoder / OpenCLBatchDecoder / CUDABatchDecoder are all
30+
# Union-Find-based and only support weight <= 2 checks (graph-like
31+
# codes). Surface-code stabilizers are weight-4 and are NOT supported
32+
# here -- use BlossomDecoder, SparseBlossomDecoder, or BPOSDDecoder for
33+
# those instead. A ring code (each check ties 2 neighboring qubits in a
34+
# cycle) is the natural weight-2 code family for this decoder family.
35+
distance = 50
36+
checks, n_qubits = generate_ring_code_checks(distance)
3037
n_checks = len(checks)
3138
batch_size = 10000 # larger batch size to show GPU speedup
32-
39+
3340
# Generate random syndromes using NumPy
3441
rng = np.random.default_rng(42)
3542
syndromes = rng.integers(0, 2, size=(batch_size, n_checks), dtype=np.uint8)
36-
43+
3744
print("=" * 60)
3845
print("QECTOR v3 - Batch & GPU Demo")
3946
print("=" * 60)
40-
print(f"Code: Surface Code d=5 ({n_qubits} qubits, {n_checks} checks)")
47+
print(f"Code: Ring Code d={distance} ({n_qubits} qubits, {n_checks} checks)")
4148
print(f"Batch size: {batch_size}")
42-
49+
4350
# 1. CPU Batch decoder
4451
print("\n1. CPUBatchDecoder (CPU parallel execution):")
4552
cpu_batch = CPUBatchDecoder(checks, n_qubits)
4653
# Warm up
4754
_ = cpu_batch.batch_decode(syndromes[:10])
48-
55+
4956
t0 = time.perf_counter()
5057
results_cpu = cpu_batch.batch_decode(syndromes)
5158
t1 = time.perf_counter()
5259
cpu_time = (t1 - t0) * 1000
5360
print(f" Time: {cpu_time:.2f} ms")
5461
print(f" Throughput: {batch_size / (cpu_time / 1000):.0f} dec/s")
55-
62+
5663
# 2. GPU OpenCL Batch decoder (if available)
5764
cl_avail = opencl_is_available()
5865
print("\n2. OpenCLBatchDecoder (GPU OpenCL):")
5966
print(f" OpenCL available: {cl_avail}")
60-
67+
6168
if cl_avail:
6269
gpu_cl = OpenCLBatchDecoder(checks, n_qubits)
6370
# Warm up
6471
_ = gpu_cl.batch_decode(syndromes[:10])
65-
72+
6673
t0 = time.perf_counter()
6774
results_gpu_cl = gpu_cl.batch_decode(syndromes)
6875
t1 = time.perf_counter()
6976
cl_time = (t1 - t0) * 1000
7077
print(f" Time: {cl_time:.2f} ms")
7178
print(f" Throughput: {batch_size / (cl_time / 1000):.0f} dec/s")
7279
print(f" Speedup vs CPU: {cpu_time / cl_time:.2f}x")
73-
80+
7481
# Verify correctness
7582
matches = np.array_equal(results_cpu, results_gpu_cl)
7683
print(f" Output matches CPU: {'yes' if matches else 'no'}")
7784
else:
7885
print(" (Skipped — OpenCL GPU not available)")
79-
86+
8087
# 3. GPU CUDA Batch decoder (if available)
8188
cuda_avail = cuda_is_available()
8289
print("\n3. CUDABatchDecoder (GPU Native CUDA):")
8390
print(f" CUDA available: {cuda_avail}")
84-
91+
8592
if cuda_avail and CUDABatchDecoder is not None:
8693
gpu_cuda = CUDABatchDecoder(checks, n_qubits)
8794
# Warm up
8895
_ = gpu_cuda.batch_decode(syndromes[:10])
89-
96+
9097
t0 = time.perf_counter()
9198
results_gpu_cuda = gpu_cuda.batch_decode(syndromes)
9299
t1 = time.perf_counter()
@@ -96,16 +103,17 @@ def main():
96103
print(f" Time: {cuda_time:.2f} ms")
97104
print(f" Throughput: {batch_size / (cuda_time / 1000):.0f} dec/s")
98105
print(f" Speedup vs CPU: {cpu_time / cuda_time:.2f}x")
99-
106+
100107
# Verify correctness
101108
matches = np.array_equal(results_cpu, results_gpu_cuda)
102109
print(f" Output matches CPU: {'yes' if matches else 'no'}")
103110
else:
104111
print(" (Skipped — CUDA not available or not built)")
105-
112+
106113
print("\n" + "=" * 60)
107114
print("Demo complete!")
108115
print("=" * 60)
109116

117+
110118
if __name__ == "__main__":
111119
main()

0 commit comments

Comments
 (0)