4040 def cuda_is_available ():
4141 return False
4242
43+
4344try :
4445 from .qector_decoder_v3 import opencl_is_available
4546except ImportError :
47+
4648 def opencl_is_available ():
4749 return False
4850
51+
4952try :
5053 from .qector_decoder_v3 import run_grpc_server
5154except ImportError :
@@ -64,10 +67,9 @@ def opencl_is_available():
6467 __version__ = "0.5.0"
6568
6669
67-
6870class UnionFindDecoder :
6971 """Production-ready Union-Find quantum error correction decoder.
70-
72+
7173 Rust core with PyO3 bindings. Zero-copy NumPy interop.
7274 GIL is released during decode for true parallelism.
7375 """
@@ -242,7 +244,7 @@ def current_round(self):
242244
243245class StreamingDecoder :
244246 """Streaming decoder that accumulates syndromes over multiple rounds.
245-
247+
246248 Rust core with circular history buffer and OR accumulation.
247249 """
248250
@@ -281,7 +283,7 @@ def n_checks(self):
281283
282284class BatchDecoder :
283285 """Parallel batch decoder using Rayon (Rust data parallelism).
284-
286+
285287 Distributes batch decoding across all CPU cores.
286288 """
287289
@@ -486,7 +488,7 @@ def is_available():
486488
487489class SparseBlossomDecoder :
488490 """Region-growing Sparse Blossom decoder with RadixHeap.
489-
491+
490492 Supports dynamic weight overrides from GNN Pre-Decoder for enriched decoding.
491493 """
492494
@@ -506,11 +508,11 @@ def decode(self, syndrome):
506508
507509 def decode_with_weights (self , syndrome , weights ):
508510 """Decode with per-qubit dynamic weight overrides.
509-
511+
510512 Args:
511513 syndrome: np.ndarray of shape (n_checks,) with dtype uint8.
512514 weights: List of (qubit_id, weight) tuples.
513-
515+
514516 Returns:
515517 np.ndarray of shape (n_qubits,) with correction.
516518 """
@@ -542,7 +544,7 @@ def n_checks(self):
542544
543545class BPOSDDecoder :
544546 """Belief Propagation + Ordered Statistics Decoding.
545-
547+
546548 Min-sum BP with OSD stage for improved LER on complex codes.
547549 """
548550
@@ -630,9 +632,9 @@ class GNNPredecoder:
630632 """Graph Neural Network Pre-Decoder for dynamic edge weight prediction.
631633
632634 MPNN 3 layers + MLP readout. Predicts adjusted edge weights for SparseBlossom.
633-
635+
634636 **v2.0** : Full backpropagation through MPNN layers (P0). All layers are trainable.
635-
637+
636638 Dimensions must match the DetectorGraph:
637639 - node_feat_dim = 10 (NodeFeatures::DIM)
638640 - edge_feat_dim = 8 (EdgeFeatures::DIM)
@@ -644,7 +646,7 @@ class GNNPredecoder:
644646
645647 def __init__ (self , node_feat_dim = None , edge_feat_dim = None , hidden_size = 16 , n_layers = 2 ):
646648 """Create a GNNPredecoder.
647-
649+
648650 If node_feat_dim and edge_feat_dim are not provided, uses the standard
649651 dimensions matching DetectorGraph (10 and 8).
650652 """
@@ -763,19 +765,28 @@ def generate_dataset(self, n_samples):
763765
764766class HybridDecoder :
765767 """GNN Pre-Decoder + SparseBlossom hybrid decoder.
766-
768+
767769 Uses a lightweight MPNN to estimate dynamic edge weights, then passes
768770 them to SparseBlossom for enriched region-growing decoding.
769771 """
770772
771- def __init__ (self , check_to_qubits , n_qubits = None , check_positions = None ,
772- check_types = None , base_weights = None , gnn_hidden_size = 64 , gnn_n_layers = 3 ):
773+ def __init__ (
774+ self ,
775+ check_to_qubits ,
776+ n_qubits = None ,
777+ check_positions = None ,
778+ check_types = None ,
779+ base_weights = None ,
780+ gnn_hidden_size = 64 ,
781+ gnn_n_layers = 3 ,
782+ ):
773783 if not check_to_qubits :
774784 raise ValueError ("check_to_qubits must be non-empty" )
775785 c2q = [[int (q ) for q in check ] for check in check_to_qubits ]
776786 nq = None if n_qubits is None else int (n_qubits )
777- self ._inner = _RustHybridDecoder (c2q , nq , check_positions , check_types ,
778- base_weights , gnn_hidden_size , gnn_n_layers )
787+ self ._inner = _RustHybridDecoder (
788+ c2q , nq , check_positions , check_types , base_weights , gnn_hidden_size , gnn_n_layers
789+ )
779790
780791 def decode_hybrid (self , syndrome ):
781792 if not isinstance (syndrome , np .ndarray ):
@@ -965,12 +976,14 @@ def __init__(self, check_to_qubits, n_qubits=None, n_samples=10000, seed=42):
965976
966977 def run (self ):
967978 import json
979+
968980 raw = self ._inner .run ()
969981 return json .loads (raw )
970982
971983 def save (self , path , results ):
972984 import json
973985 from pathlib import Path
986+
974987 Path (path ).write_text (json .dumps (results , indent = 2 ), encoding = "utf-8" )
975988
976989
0 commit comments