| """JAX-accelerated Graph Neural Network and Neural Belief Propagation Decoders. |
| |
| This module provides high-performance JAX implementations of the Neural BP and |
| GNN decoders for qLDPC codes. By leveraging `jax.vmap` and `jax.jit`, these |
| decoders achieve extremely fast parallel execution on GPUs and TPUs. |
| |
| References: |
| - "Decoding Quantum LDPC Codes Using Graph Neural Networks" (2024) |
| - "Machine learning message-passing for the scalable decoding of QLDPC codes" (Nature, 2025) |
| """ |
|
|
| from typing import Tuple, Optional |
| import numpy as np |
|
|
| try: |
| import jax |
| import jax.numpy as jnp |
| from jax.experimental import sparse |
| HAS_JAX = True |
| except ImportError: |
| HAS_JAX = False |
|
|
|
|
| class JAXNeuralBPDecoder: |
| """JAX implementation of Neural Belief Propagation for qLDPC codes.""" |
| |
| def __init__(self, num_vars: int, num_checks: int, max_iter: int = 15): |
| if not HAS_JAX: |
| raise ImportError("JAX is required. Run: pip install jax jaxlib") |
| |
| self.num_vars = num_vars |
| self.num_checks = num_checks |
| self.max_iter = max_iter |
| |
| |
| self.init_params = { |
| "w_cv": jnp.array([1.0], dtype=jnp.float32), |
| "w_vc": jnp.array([1.0], dtype=jnp.float32), |
| "damping": jnp.array([0.5], dtype=jnp.float32) |
| } |
| |
| |
| self._jit_decode = jax.jit(self._decode_step, static_argnames=['num_vars', 'num_checks', 'max_iter']) |
| self._jit_batch_decode = jax.jit( |
| jax.vmap(self._decode_step, in_axes=(None, 0, None, None, None, None, None)), |
| static_argnames=['num_vars', 'num_checks', 'max_iter'] |
| ) |
|
|
| @staticmethod |
| def _decode_step( |
| params: dict, |
| syndrome: jnp.ndarray, |
| H_indices: jnp.ndarray, |
| H_values: jnp.ndarray, |
| num_vars: int, |
| num_checks: int, |
| max_iter: int = 15 |
| ) -> jnp.ndarray: |
| """Single-shot Neural BP decoding step for JAX JIT compilation.""" |
| |
| |
| H = sparse.BCOO((H_values, H_indices), shape=(num_checks, num_vars)) |
| H_t = sparse.BCOO((H_values, H_indices[:, ::-1]), shape=(num_vars, num_checks)) |
| |
| |
| v_beliefs = jnp.zeros(num_vars, dtype=jnp.float32) |
| |
| |
| s_sign = 1.0 - 2.0 * syndrome |
| |
| w_cv = params["w_cv"] |
| w_vc = params["w_vc"] |
| damping = params["damping"] |
| |
| def bp_iter(i, val): |
| v_bel = val |
| |
| |
| v_to_c = w_vc * (H @ v_bel) |
| |
| |
| c_vals = jnp.tanh(v_to_c / 2.0) |
| c_msg = s_sign * c_vals |
| |
| |
| c_to_v = w_cv * (H_t @ c_msg) |
| |
| |
| v_bel_new = damping * v_bel + (1.0 - damping) * c_to_v |
| return v_bel_new |
| |
| final_beliefs = jax.lax.fori_loop(0, max_iter, bp_iter, v_beliefs) |
| |
| return jax.nn.sigmoid(-final_beliefs) |
|
|
| def decode_batch(self, params: dict, syndrome: np.ndarray, H_dense: np.ndarray) -> np.ndarray: |
| """Run batched decoding. |
| |
| Args: |
| params: Dictionary of Neural BP weights. |
| syndrome: (batch_size, num_checks) numpy array. |
| H_dense: (num_checks, num_vars) parity matrix. |
| """ |
| |
| rows, cols = np.nonzero(H_dense) |
| H_indices = jnp.column_stack((rows, cols)) |
| H_values = jnp.ones(len(rows), dtype=jnp.float32) |
| |
| syndrome_jax = jnp.array(syndrome, dtype=jnp.float32) |
| |
| probs = self._jit_batch_decode( |
| params, |
| syndrome_jax, |
| H_indices, |
| H_values, |
| self.num_vars, |
| self.num_checks, |
| self.max_iter |
| ) |
| return np.array(probs) |
|
|