2121 CUDABatchDecoder ,
2222 cuda_is_available ,
2323 opencl_is_available ,
24- generate_surface_code_checks ,
24+ generate_ring_code_checks ,
2525)
2626
27+
2728def 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 ("\n 1. 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 ("\n 2. 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 ("\n 3. 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+
110118if __name__ == "__main__" :
111119 main ()
0 commit comments