prav_core/
qec_engine.rs

1//! High-level QEC decoder API.
2//!
3//! This module provides [`QecEngine`], a convenient wrapper around [`DecodingState`]
4//! that handles the common decode cycle: reset, load syndromes, decode.
5
6use crate::arena::Arena;
7use crate::decoder::{DecodingState, EdgeCorrection};
8use crate::topology::Topology;
9
10/// High-level quantum error correction decoder engine.
11///
12/// `QecEngine` wraps a [`DecodingState`] and provides a simple interface for
13/// repeated decoding cycles. It automatically handles the sparse reset between
14/// cycles for optimal performance.
15///
16/// # Type Parameters
17///
18/// - `'a` - Lifetime of the backing arena memory
19/// - `T: Topology` - The lattice topology (e.g., [`SquareGrid`](crate::SquareGrid))
20/// - `STRIDE_Y` - Compile-time Y stride (must match grid dimensions)
21///
22/// # Example
23///
24/// ```ignore
25/// use prav_core::{Arena, QecEngine, SquareGrid, EdgeCorrection};
26///
27/// // Setup
28/// let mut buffer = [0u8; 1024 * 1024];
29/// let mut arena = Arena::new(&mut buffer);
30/// let mut engine: QecEngine<SquareGrid, 32> = QecEngine::new(&mut arena, 32, 32, 1);
31///
32/// // Decode cycle
33/// let mut corrections = [EdgeCorrection::default(); 512];
34/// let syndromes: &[u64] = &[/* syndrome data */];
35///
36/// let num_corrections = engine.process_cycle_dense(syndromes, &mut corrections);
37///
38/// // Apply corrections[0..num_corrections] to your physical qubits
39/// ```
40///
41/// # Performance
42///
43/// For repeated decoding, `QecEngine` uses sparse reset internally, which only
44/// resets modified blocks. This is much faster than full reinitialization at
45/// typical error rates.
46pub struct QecEngine<'a, T: Topology, const STRIDE_Y: usize> {
47    /// Internal decoder state.
48    decoder: DecodingState<'a, T, STRIDE_Y>,
49}
50
51impl<'a, T: Topology + Sync, const STRIDE_Y: usize> QecEngine<'a, T, STRIDE_Y> {
52    /// Creates a new QEC engine for the given grid dimensions.
53    ///
54    /// # Arguments
55    ///
56    /// * `arena` - Arena allocator for all internal allocations.
57    /// * `width` - Grid width in nodes.
58    /// * `height` - Grid height in nodes.
59    /// * `depth` - Grid depth (1 for 2D codes, >1 for 3D codes).
60    ///
61    /// # Panics
62    ///
63    /// Panics if `STRIDE_Y` doesn't match the calculated stride for the dimensions.
64    /// The stride is `max(width, height, depth).next_power_of_two()`.
65    pub fn new(arena: &mut Arena<'a>, width: usize, height: usize, depth: usize) -> Self {
66        Self {
67            decoder: DecodingState::new(arena, width, height, depth),
68        }
69    }
70
71    /// Processes a complete decoding cycle with dense syndrome input.
72    ///
73    /// This is the main entry point for decoding. It performs:
74    /// 1. Sparse reset of modified state from previous cycle
75    /// 2. Syndrome loading from dense bitarray
76    /// 3. Cluster growth
77    /// 4. Correction extraction via peeling
78    ///
79    /// # Arguments
80    ///
81    /// * `dense_defects` - Syndrome measurements as dense bitarray. Each u64
82    ///   represents 64 nodes, where bit `i` indicates node `(blk * 64 + i)` has
83    ///   a syndrome.
84    /// * `out_corrections` - Output buffer for edge corrections. Should be sized
85    ///   to hold the maximum expected corrections (typically `num_nodes / 2`).
86    ///
87    /// # Returns
88    ///
89    /// Number of corrections written to `out_corrections`.
90    ///
91    /// # Example
92    ///
93    /// ```ignore
94    /// let syndromes = generate_syndromes(error_rate);
95    /// let mut corrections = [EdgeCorrection::default(); 1024];
96    ///
97    /// let count = engine.process_cycle_dense(&syndromes, &mut corrections);
98    /// for i in 0..count {
99    ///     apply_correction(corrections[i]);
100    /// }
101    /// ```
102    pub fn process_cycle_dense(
103        &mut self,
104        dense_defects: &[u64],
105        out_corrections: &mut [EdgeCorrection],
106    ) -> usize {
107        self.decoder.sparse_reset();
108
109        self.decoder.load_dense_syndromes(dense_defects);
110
111        self.decoder.decode(out_corrections)
112    }
113
114    /// Loads erasure information for the next decoding cycle.
115    ///
116    /// Erasures indicate qubits that were lost (e.g., photon loss in optical
117    /// systems). Erased qubits are excluded from cluster growth.
118    ///
119    /// # Arguments
120    ///
121    /// * `erasures` - Dense bitarray where bit `i` in `erasures[blk]` indicates
122    ///   node `(blk * 64 + i)` is erased.
123    ///
124    /// # Note
125    ///
126    /// Call this before `process_cycle_dense` if your system has erasures.
127    /// The erasure mask persists until changed.
128    pub fn load_erasures(&mut self, erasures: &[u64]) {
129        self.decoder.load_erasures(erasures);
130    }
131}