1use std::time::Instant;
27
28#[derive(Debug, Clone, PartialEq)]
34pub struct StabilizerMeasurement {
35 pub x: u32,
37 pub y: u32,
39 pub round: u32,
41 pub value: bool,
43}
44
45#[derive(Debug, Clone)]
47pub struct SyndromeData {
48 pub stabilizers: Vec<StabilizerMeasurement>,
50 pub code_distance: u32,
52 pub num_rounds: u32,
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
58pub enum PauliType {
59 X,
61 Z,
63}
64
65#[derive(Debug, Clone)]
67pub struct Correction {
68 pub pauli_corrections: Vec<(u32, PauliType)>,
70 pub logical_outcome: bool,
72 pub confidence: f64,
74 pub decode_time_ns: u64,
76}
77
78pub trait SurfaceCodeDecoder: Send + Sync {
87 fn decode(&self, syndrome: &SyndromeData) -> Correction;
89
90 fn name(&self) -> &str;
92}
93
94#[derive(Debug, Clone)]
101struct UnionFind {
102 parent: Vec<usize>,
103 rank: Vec<usize>,
104 parity: Vec<bool>,
106}
107
108impl UnionFind {
109 fn new(n: usize) -> Self {
110 Self {
111 parent: (0..n).collect(),
112 rank: vec![0; n],
113 parity: vec![false; n],
114 }
115 }
116
117 fn find(&mut self, mut x: usize) -> usize {
118 while self.parent[x] != x {
119 let next = self.parent[x];
121 self.parent[x] = self.parent[next];
122 x = next;
123 }
124 x
125 }
126
127 fn union(&mut self, a: usize, b: usize) {
128 let ra = self.find(a);
129 let rb = self.find(b);
130 if ra == rb {
131 return;
132 }
133 let (big, small) = if self.rank[ra] >= self.rank[rb] {
135 (ra, rb)
136 } else {
137 (rb, ra)
138 };
139 self.parent[small] = big;
140 self.parity[big] = self.parity[big] ^ self.parity[small];
141 if self.rank[big] == self.rank[small] {
142 self.rank[big] += 1;
143 }
144 }
145
146 fn set_parity(&mut self, node: usize, is_defect: bool) {
147 let root = self.find(node);
148 self.parity[root] = self.parity[root] ^ is_defect;
149 }
150
151 fn cluster_parity(&mut self, node: usize) -> bool {
152 let root = self.find(node);
153 self.parity[root]
154 }
155}
156
157#[derive(Debug, Clone)]
159struct Defect {
160 x: u32,
161 y: u32,
162 round: u32,
163 node_index: usize,
164}
165
166pub struct UnionFindDecoder {
184 max_growth_radius: u32,
186}
187
188impl UnionFindDecoder {
189 pub fn new(max_growth_radius: u32) -> Self {
195 Self { max_growth_radius }
196 }
197
198 fn extract_defects(&self, syndrome: &SyndromeData) -> Vec<Defect> {
204 let d = syndrome.code_distance;
205 let num_rounds = syndrome.num_rounds;
206
207 let grid_w = if d > 1 { d - 1 } else { 1 };
210 let grid_h = if d > 1 { d - 1 } else { 1 };
211 let grid_size = (grid_w * grid_h * num_rounds) as usize;
212 let mut grid = vec![false; grid_size];
213
214 for s in &syndrome.stabilizers {
215 if s.x < grid_w && s.y < grid_h && s.round < num_rounds {
216 let idx = (s.round * grid_w * grid_h + s.y * grid_w + s.x) as usize;
217 if idx < grid.len() {
218 grid[idx] = s.value;
219 }
220 }
221 }
222
223 let mut defects = Vec::new();
224 let mut node_idx = 0usize;
225
226 for r in 0..num_rounds {
227 for y in 0..grid_h {
228 for x in 0..grid_w {
229 let curr_idx = (r * grid_w * grid_h + y * grid_w + x) as usize;
230 let curr = grid[curr_idx];
231
232 let prev = if r > 0 {
234 let prev_idx =
235 ((r - 1) * grid_w * grid_h + y * grid_w + x) as usize;
236 grid[prev_idx]
237 } else {
238 false
239 };
240
241 if curr != prev {
243 defects.push(Defect {
244 x,
245 y,
246 round: r,
247 node_index: node_idx,
248 });
249 }
250 node_idx += 1;
251 }
252 }
253 }
254
255 defects
256 }
257
258 fn manhattan_distance(a: &Defect, b: &Defect) -> u32 {
260 let dx = (a.x as i64 - b.x as i64).unsigned_abs() as u32;
261 let dy = (a.y as i64 - b.y as i64).unsigned_abs() as u32;
262 let dr = (a.round as i64 - b.round as i64).unsigned_abs() as u32;
263 dx + dy + dr
264 }
265
266 fn boundary_distance(defect: &Defect, code_distance: u32) -> u32 {
268 let grid_w = if code_distance > 1 {
269 code_distance - 1
270 } else {
271 1
272 };
273 let grid_h = if code_distance > 1 {
274 code_distance - 1
275 } else {
276 1
277 };
278 let dx_min = defect.x.min(grid_w.saturating_sub(1).saturating_sub(defect.x));
279 let dy_min = defect.y.min(grid_h.saturating_sub(1).saturating_sub(defect.y));
280 dx_min.min(dy_min)
281 }
282
283 fn grow_and_merge(
286 &self,
287 defects: &[Defect],
288 total_nodes: usize,
289 code_distance: u32,
290 ) -> UnionFind {
291 let mut uf = UnionFind::new(total_nodes);
292
293 for d in defects {
295 uf.set_parity(d.node_index, true);
296 }
297
298 if defects.is_empty() {
299 return uf;
300 }
301
302 let max_radius = if self.max_growth_radius > 0 {
303 self.max_growth_radius
304 } else {
305 code_distance
306 };
307
308 for radius in 1..=max_radius {
310 let mut merged_any = false;
311 for i in 0..defects.len() {
312 if !uf.cluster_parity(defects[i].node_index) {
313 continue; }
315 for j in (i + 1)..defects.len() {
316 if !uf.cluster_parity(defects[j].node_index) {
317 continue;
318 }
319 if Self::manhattan_distance(&defects[i], &defects[j]) <= 2 * radius {
320 uf.union(defects[i].node_index, defects[j].node_index);
321 merged_any = true;
322 }
323 }
324 }
325 if !merged_any {
326 break;
327 }
328 let all_even = defects
330 .iter()
331 .all(|d| !uf.cluster_parity(d.node_index));
332 if all_even {
333 break;
334 }
335 }
336
337 uf
338 }
339
340 fn corrections_from_clusters(
343 &self,
344 defects: &[Defect],
345 uf: &mut UnionFind,
346 code_distance: u32,
347 ) -> Vec<(u32, PauliType)> {
348 let mut corrections = Vec::new();
349
350 let mut odd_roots: Vec<&Defect> = Vec::new();
352 for d in defects {
353 let root = uf.find(d.node_index);
354 if uf.parity[root] && root == d.node_index {
355 odd_roots.push(d);
356 }
357 }
358
359 for defect in &odd_roots {
361 let path = self.path_to_boundary(defect, code_distance);
362 corrections.extend(path);
363 }
364
365 let mut paired: Vec<bool> = vec![false; defects.len()];
369 for i in 0..defects.len() {
370 if paired[i] {
371 continue;
372 }
373 let root_i = uf.find(defects[i].node_index);
374 for j in (i + 1)..defects.len() {
375 if paired[j] {
376 continue;
377 }
378 let root_j = uf.find(defects[j].node_index);
379 if root_i == root_j && !uf.parity[root_i] {
380 let path = self.path_between(&defects[i], &defects[j], code_distance);
382 corrections.extend(path);
383 paired[i] = true;
384 paired[j] = true;
385 break;
386 }
387 }
388 }
389
390 corrections
391 }
392
393 fn path_to_boundary(&self, defect: &Defect, code_distance: u32) -> Vec<(u32, PauliType)> {
396 let mut corrections = Vec::new();
397 let grid_w = if code_distance > 1 {
398 code_distance - 1
399 } else {
400 1
401 };
402
403 let dist_left = defect.x;
406 let dist_right = grid_w.saturating_sub(defect.x + 1);
407
408 if dist_left <= dist_right {
409 for step in 0..=defect.x {
411 let data_qubit = defect.y * code_distance + (defect.x - step);
412 corrections.push((data_qubit, PauliType::X));
413 }
414 } else {
415 for step in 0..=(grid_w - defect.x - 1) {
417 let data_qubit = defect.y * code_distance + (defect.x + step + 1);
418 corrections.push((data_qubit, PauliType::X));
419 }
420 }
421
422 corrections
423 }
424
425 fn path_between(
428 &self,
429 a: &Defect,
430 b: &Defect,
431 code_distance: u32,
432 ) -> Vec<(u32, PauliType)> {
433 let mut corrections = Vec::new();
434
435 let (mut cx, mut cy) = (a.x as i64, a.y as i64);
436 let (tx, ty) = (b.x as i64, b.y as i64);
437
438 while cx != tx {
440 let step = if tx > cx { 1i64 } else { -1 };
441 let data_x = if step > 0 { cx + 1 } else { cx };
442 let data_qubit = cy as u32 * code_distance + data_x as u32;
443 corrections.push((data_qubit, PauliType::X));
444 cx += step;
445 }
446 while cy != ty {
447 let step = if ty > cy { 1i64 } else { -1 };
448 let data_y = if step > 0 { cy + 1 } else { cy };
449 let data_qubit = data_y as u32 * code_distance + cx as u32;
450 corrections.push((data_qubit, PauliType::Z));
451 cy += step;
452 }
453
454 corrections
455 }
456
457 fn infer_logical_outcome(corrections: &[(u32, PauliType)]) -> bool {
461 let x_count = corrections
464 .iter()
465 .filter(|(_, p)| *p == PauliType::X)
466 .count();
467 x_count % 2 == 1
468 }
469}
470
471impl SurfaceCodeDecoder for UnionFindDecoder {
472 fn decode(&self, syndrome: &SyndromeData) -> Correction {
473 let start = Instant::now();
474
475 let defects = self.extract_defects(syndrome);
476
477 if defects.is_empty() {
478 let elapsed = start.elapsed().as_nanos() as u64;
479 return Correction {
480 pauli_corrections: Vec::new(),
481 logical_outcome: false,
482 confidence: 1.0,
483 decode_time_ns: elapsed,
484 };
485 }
486
487 let d = syndrome.code_distance;
488 let grid_w = if d > 1 { d - 1 } else { 1 };
489 let grid_h = if d > 1 { d - 1 } else { 1 };
490 let total_nodes = (grid_w * grid_h * syndrome.num_rounds) as usize;
491
492 let mut uf = self.grow_and_merge(&defects, total_nodes, d);
493 let pauli_corrections = self.corrections_from_clusters(&defects, &mut uf, d);
494 let logical_outcome = Self::infer_logical_outcome(&pauli_corrections);
495
496 let defect_density = defects.len() as f64 / (d as f64 * d as f64);
499 let confidence = (1.0 - defect_density).max(0.0).min(1.0);
500
501 let elapsed = start.elapsed().as_nanos() as u64;
502
503 Correction {
504 pauli_corrections,
505 logical_outcome,
506 confidence,
507 decode_time_ns: elapsed,
508 }
509 }
510
511 fn name(&self) -> &str {
512 "UnionFindDecoder"
513 }
514}
515
516pub struct PartitionedDecoder {
533 tile_size: u32,
534 inner_decoder: Box<dyn SurfaceCodeDecoder>,
535}
536
537impl PartitionedDecoder {
538 pub fn new(tile_size: u32, inner_decoder: Box<dyn SurfaceCodeDecoder>) -> Self {
543 assert!(tile_size > 0, "tile_size must be positive");
544 Self {
545 tile_size,
546 inner_decoder,
547 }
548 }
549
550 fn partition_syndrome(&self, syndrome: &SyndromeData) -> Vec<SyndromeData> {
552 let d = syndrome.code_distance;
553 let grid_w = if d > 1 { d - 1 } else { 1 };
554 let grid_h = if d > 1 { d - 1 } else { 1 };
555
556 let tiles_x = (grid_w + self.tile_size - 1) / self.tile_size;
557 let tiles_y = (grid_h + self.tile_size - 1) / self.tile_size;
558
559 let mut tiles = Vec::with_capacity((tiles_x * tiles_y) as usize);
560
561 for ty in 0..tiles_y {
562 for tx in 0..tiles_x {
563 let x_min = tx * self.tile_size;
564 let y_min = ty * self.tile_size;
565 let x_max = ((tx + 1) * self.tile_size).min(grid_w);
566 let y_max = ((ty + 1) * self.tile_size).min(grid_h);
567 let tile_w = x_max - x_min;
568 let tile_h = y_max - y_min;
569 let tile_d = tile_w.max(tile_h) + 1;
570
571 let tile_stabs: Vec<StabilizerMeasurement> = syndrome
572 .stabilizers
573 .iter()
574 .filter(|s| s.x >= x_min && s.x < x_max && s.y >= y_min && s.y < y_max)
575 .map(|s| StabilizerMeasurement {
576 x: s.x - x_min,
577 y: s.y - y_min,
578 round: s.round,
579 value: s.value,
580 })
581 .collect();
582
583 tiles.push(SyndromeData {
584 stabilizers: tile_stabs,
585 code_distance: tile_d,
586 num_rounds: syndrome.num_rounds,
587 });
588 }
589 }
590
591 tiles
592 }
593
594 fn merge_tile_corrections(
596 &self,
597 tile_corrections: &[Correction],
598 syndrome: &SyndromeData,
599 ) -> Correction {
600 let d = syndrome.code_distance;
601 let grid_w = if d > 1 { d - 1 } else { 1 };
602
603 let tiles_x = (grid_w + self.tile_size - 1) / self.tile_size;
604
605 let mut all_corrections = Vec::new();
606 let mut total_confidence = 0.0;
607 let mut logical_outcome = false;
608
609 for (idx, tile_corr) in tile_corrections.iter().enumerate() {
610 let tx = idx as u32 % tiles_x;
611 let ty = idx as u32 / tiles_x;
612 let x_offset = tx * self.tile_size;
613 let y_offset = ty * self.tile_size;
614
615 for &(qubit, pauli) in &tile_corr.pauli_corrections {
616 let local_y = qubit / (d.max(1));
618 let local_x = qubit % (d.max(1));
619 let global_qubit =
620 (local_y + y_offset) * d + (local_x + x_offset);
621 all_corrections.push((global_qubit, pauli));
622 }
623
624 total_confidence += tile_corr.confidence;
625 logical_outcome ^= tile_corr.logical_outcome;
626 }
627
628 let avg_confidence = if tile_corrections.is_empty() {
629 1.0
630 } else {
631 total_confidence / tile_corrections.len() as f64
632 };
633
634 all_corrections.sort_by(|a, b| a.0.cmp(&b.0).then(format!("{:?}", a.1).cmp(&format!("{:?}", b.1))));
637 let mut deduped: Vec<(u32, PauliType)> = Vec::new();
638 let mut i = 0;
639 while i < all_corrections.len() {
640 let mut count = 1usize;
641 while i + count < all_corrections.len()
642 && all_corrections[i + count].0 == all_corrections[i].0
643 && all_corrections[i + count].1 == all_corrections[i].1
644 {
645 count += 1;
646 }
647 if count % 2 == 1 {
649 deduped.push(all_corrections[i]);
650 }
651 i += count;
652 }
653
654 Correction {
655 pauli_corrections: deduped,
656 logical_outcome,
657 confidence: avg_confidence,
658 decode_time_ns: 0, }
660 }
661}
662
663impl SurfaceCodeDecoder for PartitionedDecoder {
664 fn decode(&self, syndrome: &SyndromeData) -> Correction {
665 let start = Instant::now();
666
667 let tiles = self.partition_syndrome(syndrome);
668
669 let tile_corrections: Vec<Correction> =
672 tiles.iter().map(|t| self.inner_decoder.decode(t)).collect();
673
674 let mut correction = self.merge_tile_corrections(&tile_corrections, syndrome);
675 correction.decode_time_ns = start.elapsed().as_nanos() as u64;
676
677 correction
678 }
679
680 fn name(&self) -> &str {
681 "PartitionedDecoder"
682 }
683}
684
685#[derive(Debug, Clone)]
700pub struct AdaptiveCodeDistance {
701 current_distance: u32,
702 min_distance: u32,
703 max_distance: u32,
704 error_history: Vec<f64>,
705 window_size: usize,
706}
707
708impl AdaptiveCodeDistance {
709 pub fn new(initial: u32, min: u32, max: u32) -> Self {
714 assert!(min <= max, "min_distance must be <= max_distance");
715 assert!(
716 initial >= min && initial <= max,
717 "initial distance must be in [min, max]"
718 );
719 let initial = if initial % 2 == 0 {
721 initial + 1
722 } else {
723 initial
724 };
725 Self {
726 current_distance: initial.min(max),
727 min_distance: min,
728 max_distance: max,
729 error_history: Vec::new(),
730 window_size: 100,
731 }
732 }
733
734 pub fn record_error_rate(&mut self, rate: f64) {
736 self.error_history.push(rate.clamp(0.0, 1.0));
737 if self.error_history.len() > self.window_size * 2 {
738 let drain_to = self.error_history.len() - self.window_size;
740 self.error_history.drain(..drain_to);
741 }
742 }
743
744 pub fn recommended_distance(&self) -> u32 {
746 if self.should_increase() {
747 let next = self.current_distance + 2; next.min(self.max_distance)
749 } else if self.should_decrease() {
750 let next = self.current_distance.saturating_sub(2);
751 next.max(self.min_distance)
752 } else {
753 self.current_distance
754 }
755 }
756
757 pub fn should_increase(&self) -> bool {
762 if self.current_distance >= self.max_distance {
763 return false;
764 }
765 let avg = self.average_error_rate();
766 if avg.is_nan() {
767 return false;
768 }
769 let threshold = 10.0_f64.powf(-(self.current_distance as f64) / 3.0);
772 avg > threshold
773 }
774
775 pub fn should_decrease(&self) -> bool {
780 if self.current_distance <= self.min_distance {
781 return false;
782 }
783 let avg = self.average_error_rate();
784 if avg.is_nan() {
785 return false;
786 }
787 if self.error_history.len() < self.window_size {
789 return false;
790 }
791 let lower_d = self.current_distance - 2;
792 let threshold = 10.0_f64.powf(-(lower_d as f64) / 3.0);
793 avg < threshold * 0.1
795 }
796
797 fn average_error_rate(&self) -> f64 {
799 if self.error_history.is_empty() {
800 return f64::NAN;
801 }
802 let window_start = self
803 .error_history
804 .len()
805 .saturating_sub(self.window_size);
806 let window = &self.error_history[window_start..];
807 let sum: f64 = window.iter().sum();
808 sum / window.len() as f64
809 }
810}
811
812#[derive(Debug, Clone)]
818pub struct SurfaceCodePatch {
819 pub logical_id: u32,
821 pub physical_qubits: Vec<u32>,
823 pub code_distance: u32,
825 pub x_origin: u32,
827 pub y_origin: u32,
829}
830
831pub struct LogicalQubitAllocator {
840 total_physical: u32,
841 code_distance: u32,
842 allocated_patches: Vec<SurfaceCodePatch>,
843 next_logical_id: u32,
844}
845
846impl LogicalQubitAllocator {
847 pub fn new(total_physical: u32, code_distance: u32) -> Self {
850 Self {
851 total_physical,
852 code_distance,
853 allocated_patches: Vec::new(),
854 next_logical_id: 0,
855 }
856 }
857
858 pub fn max_logical_qubits(&self) -> u32 {
862 let d = self.code_distance as u64;
863 let qubits_per_logical = 2 * d * d - 2 * d + 1;
864 if qubits_per_logical == 0 {
865 return 0;
866 }
867 (self.total_physical as u64 / qubits_per_logical) as u32
868 }
869
870 pub fn allocate(&mut self) -> Option<SurfaceCodePatch> {
874 let max = self.max_logical_qubits();
875 if self.allocated_patches.len() as u32 >= max {
876 return None;
877 }
878
879 let d = self.code_distance;
880 let patch_idx = self.allocated_patches.len() as u32;
881
882 let grid_side = (self.total_physical as f64).sqrt() as u32;
885 let patches_per_row = if d > 0 { grid_side / d } else { 0 };
886 let patches_per_row = patches_per_row.max(1);
887
888 let x_origin = (patch_idx % patches_per_row) * d;
889 let y_origin = (patch_idx / patches_per_row) * d;
890
891 let qubits_per_logical = 2 * d * d - 2 * d + 1;
893 let start_qubit = patch_idx * qubits_per_logical;
894 let physical_qubits: Vec<u32> =
895 (start_qubit..start_qubit + qubits_per_logical).collect();
896
897 let logical_id = self.next_logical_id;
898 self.next_logical_id += 1;
899
900 let patch = SurfaceCodePatch {
901 logical_id,
902 physical_qubits,
903 code_distance: d,
904 x_origin,
905 y_origin,
906 };
907
908 self.allocated_patches.push(patch.clone());
909 Some(patch)
910 }
911
912 pub fn deallocate(&mut self, logical_id: u32) {
914 self.allocated_patches
915 .retain(|p| p.logical_id != logical_id);
916 }
917
918 pub fn utilization(&self) -> f64 {
920 let d = self.code_distance as u64;
921 let qubits_per_logical = 2 * d * d - 2 * d + 1;
922 let used = self.allocated_patches.len() as u64 * qubits_per_logical;
923 if self.total_physical == 0 {
924 return 0.0;
925 }
926 used as f64 / self.total_physical as f64
927 }
928
929 pub fn patches(&self) -> &[SurfaceCodePatch] {
931 &self.allocated_patches
932 }
933}
934
935#[derive(Debug, Clone)]
941pub struct DecoderBenchmark {
942 pub total_syndromes: u64,
944 pub total_decode_time_ns: u64,
946 pub correct_corrections: u64,
948 pub logical_error_rate: f64,
950}
951
952impl DecoderBenchmark {
953 pub fn avg_decode_time_ns(&self) -> f64 {
955 if self.total_syndromes == 0 {
956 return 0.0;
957 }
958 self.total_decode_time_ns as f64 / self.total_syndromes as f64
959 }
960
961 pub fn throughput(&self) -> f64 {
963 if self.total_decode_time_ns == 0 {
964 return 0.0;
965 }
966 self.total_syndromes as f64 / (self.total_decode_time_ns as f64 * 1e-9)
967 }
968}
969
970pub fn benchmark_decoder(
981 decoder: &dyn SurfaceCodeDecoder,
982 distance: u32,
983 error_rate: f64,
984 rounds: u32,
985) -> DecoderBenchmark {
986 use std::collections::hash_map::DefaultHasher;
987 use std::hash::{Hash, Hasher};
988
989 let grid_w = if distance > 1 { distance - 1 } else { 1 };
990 let grid_h = if distance > 1 { distance - 1 } else { 1 };
991
992 let mut total_decode_time_ns = 0u64;
993 let mut correct_corrections = 0u64;
994 let mut total_syndromes = 0u64;
995
996 let mut seed: u64 = 0xDEAD_BEEF_CAFE_BABE;
998 let next_rand = |s: &mut u64| -> f64 {
999 let mut hasher = DefaultHasher::new();
1000 s.hash(&mut hasher);
1001 *s = hasher.finish();
1002 (*s as f64) / (u64::MAX as f64)
1004 };
1005
1006 for _ in 0..rounds {
1007 let num_syndrome_rounds = 1u32;
1008 let mut stabilizers = Vec::new();
1009 let mut expected_defect_count = 0usize;
1010
1011 for r in 0..num_syndrome_rounds {
1012 for y in 0..grid_h {
1013 for x in 0..grid_w {
1014 let val = next_rand(&mut seed) < error_rate;
1015 if val {
1016 expected_defect_count += 1;
1017 }
1018 stabilizers.push(StabilizerMeasurement {
1019 x,
1020 y,
1021 round: r,
1022 value: val,
1023 });
1024 }
1025 }
1026 }
1027
1028 let syndrome = SyndromeData {
1029 stabilizers,
1030 code_distance: distance,
1031 num_rounds: num_syndrome_rounds,
1032 };
1033
1034 let correction = decoder.decode(&syndrome);
1035 total_decode_time_ns += correction.decode_time_ns;
1036 total_syndromes += 1;
1037
1038 let expected_logical = expected_defect_count >= distance as usize;
1043 if correction.logical_outcome == expected_logical {
1044 correct_corrections += 1;
1045 }
1046 }
1047
1048 let logical_error_rate = if total_syndromes == 0 {
1049 0.0
1050 } else {
1051 1.0 - (correct_corrections as f64 / total_syndromes as f64)
1052 };
1053
1054 DecoderBenchmark {
1055 total_syndromes,
1056 total_decode_time_ns,
1057 correct_corrections,
1058 logical_error_rate,
1059 }
1060}
1061
1062#[cfg(test)]
1067mod tests {
1068 use super::*;
1069
1070 #[test]
1073 fn test_stabilizer_measurement_creation() {
1074 let m = StabilizerMeasurement {
1075 x: 3,
1076 y: 5,
1077 round: 2,
1078 value: true,
1079 };
1080 assert_eq!(m.x, 3);
1081 assert_eq!(m.y, 5);
1082 assert_eq!(m.round, 2);
1083 assert!(m.value);
1084 }
1085
1086 #[test]
1087 fn test_stabilizer_measurement_clone() {
1088 let m = StabilizerMeasurement {
1089 x: 1,
1090 y: 2,
1091 round: 0,
1092 value: false,
1093 };
1094 let m2 = m.clone();
1095 assert_eq!(m, m2);
1096 }
1097
1098 #[test]
1101 fn test_syndrome_data_empty() {
1102 let s = SyndromeData {
1103 stabilizers: Vec::new(),
1104 code_distance: 3,
1105 num_rounds: 1,
1106 };
1107 assert!(s.stabilizers.is_empty());
1108 assert_eq!(s.code_distance, 3);
1109 }
1110
1111 #[test]
1114 fn test_pauli_type_equality() {
1115 assert_eq!(PauliType::X, PauliType::X);
1116 assert_eq!(PauliType::Z, PauliType::Z);
1117 assert_ne!(PauliType::X, PauliType::Z);
1118 }
1119
1120 #[test]
1123 fn test_correction_no_errors() {
1124 let c = Correction {
1125 pauli_corrections: Vec::new(),
1126 logical_outcome: false,
1127 confidence: 1.0,
1128 decode_time_ns: 100,
1129 };
1130 assert!(c.pauli_corrections.is_empty());
1131 assert!(!c.logical_outcome);
1132 assert_eq!(c.confidence, 1.0);
1133 }
1134
1135 #[test]
1138 fn test_union_find_basic() {
1139 let mut uf = UnionFind::new(5);
1140 assert_ne!(uf.find(0), uf.find(1));
1141 uf.union(0, 1);
1142 assert_eq!(uf.find(0), uf.find(1));
1143 uf.union(2, 3);
1144 assert_eq!(uf.find(2), uf.find(3));
1145 assert_ne!(uf.find(0), uf.find(2));
1146 uf.union(1, 3);
1147 assert_eq!(uf.find(0), uf.find(3));
1148 }
1149
1150 #[test]
1151 fn test_union_find_parity() {
1152 let mut uf = UnionFind::new(4);
1153 uf.set_parity(0, true);
1154 assert!(uf.cluster_parity(0));
1155 uf.set_parity(1, true);
1156 uf.union(0, 1);
1157 assert!(!uf.cluster_parity(0));
1159 }
1160
1161 #[test]
1162 fn test_union_find_path_compression() {
1163 let mut uf = UnionFind::new(10);
1164 for i in 0..4 {
1166 uf.union(i, i + 1);
1167 }
1168 let root = uf.find(0);
1170 assert_eq!(uf.find(4), root);
1171 }
1172
1173 #[test]
1176 fn test_uf_decoder_no_errors() {
1177 let decoder = UnionFindDecoder::new(0);
1178 let syndrome = SyndromeData {
1179 stabilizers: vec![
1180 StabilizerMeasurement { x: 0, y: 0, round: 0, value: false },
1181 StabilizerMeasurement { x: 1, y: 0, round: 0, value: false },
1182 StabilizerMeasurement { x: 0, y: 1, round: 0, value: false },
1183 StabilizerMeasurement { x: 1, y: 1, round: 0, value: false },
1184 ],
1185 code_distance: 3,
1186 num_rounds: 1,
1187 };
1188
1189 let correction = decoder.decode(&syndrome);
1190 assert!(
1191 correction.pauli_corrections.is_empty(),
1192 "No defects should produce no corrections"
1193 );
1194 assert!(!correction.logical_outcome);
1195 assert_eq!(correction.confidence, 1.0);
1196 }
1197
1198 #[test]
1199 fn test_uf_decoder_single_defect() {
1200 let decoder = UnionFindDecoder::new(0);
1201 let syndrome = SyndromeData {
1202 stabilizers: vec![
1203 StabilizerMeasurement { x: 0, y: 0, round: 0, value: true },
1204 StabilizerMeasurement { x: 1, y: 0, round: 0, value: false },
1205 StabilizerMeasurement { x: 0, y: 1, round: 0, value: false },
1206 StabilizerMeasurement { x: 1, y: 1, round: 0, value: false },
1207 ],
1208 code_distance: 3,
1209 num_rounds: 1,
1210 };
1211
1212 let correction = decoder.decode(&syndrome);
1213 assert!(
1215 !correction.pauli_corrections.is_empty(),
1216 "Single defect should produce corrections"
1217 );
1218 }
1219
1220 #[test]
1221 fn test_uf_decoder_paired_defects() {
1222 let decoder = UnionFindDecoder::new(0);
1223 let syndrome = SyndromeData {
1225 stabilizers: vec![
1226 StabilizerMeasurement { x: 0, y: 0, round: 0, value: true },
1227 StabilizerMeasurement { x: 1, y: 0, round: 0, value: true },
1228 StabilizerMeasurement { x: 0, y: 1, round: 0, value: false },
1229 StabilizerMeasurement { x: 1, y: 1, round: 0, value: false },
1230 ],
1231 code_distance: 3,
1232 num_rounds: 1,
1233 };
1234
1235 let correction = decoder.decode(&syndrome);
1236 assert!(
1238 !correction.pauli_corrections.is_empty(),
1239 "Paired defects should produce corrections"
1240 );
1241 }
1242
1243 #[test]
1244 fn test_uf_decoder_name() {
1245 let decoder = UnionFindDecoder::new(5);
1246 assert_eq!(decoder.name(), "UnionFindDecoder");
1247 }
1248
1249 #[test]
1250 fn test_uf_decoder_extract_defects_empty_syndrome() {
1251 let decoder = UnionFindDecoder::new(0);
1252 let syndrome = SyndromeData {
1253 stabilizers: Vec::new(),
1254 code_distance: 3,
1255 num_rounds: 1,
1256 };
1257 let defects = decoder.extract_defects(&syndrome);
1258 assert!(defects.is_empty());
1259 }
1260
1261 #[test]
1262 fn test_uf_decoder_extract_defects_all_false() {
1263 let decoder = UnionFindDecoder::new(0);
1264 let mut stabs = Vec::new();
1265 for y in 0..2 {
1266 for x in 0..2 {
1267 stabs.push(StabilizerMeasurement {
1268 x,
1269 y,
1270 round: 0,
1271 value: false,
1272 });
1273 }
1274 }
1275 let syndrome = SyndromeData {
1276 stabilizers: stabs,
1277 code_distance: 3,
1278 num_rounds: 1,
1279 };
1280 let defects = decoder.extract_defects(&syndrome);
1281 assert!(defects.is_empty(), "All-false syndrome should have no defects");
1282 }
1283
1284 #[test]
1285 fn test_uf_decoder_extract_defects_with_flip() {
1286 let decoder = UnionFindDecoder::new(0);
1287 let syndrome = SyndromeData {
1288 stabilizers: vec![
1289 StabilizerMeasurement { x: 0, y: 0, round: 0, value: false },
1291 StabilizerMeasurement { x: 1, y: 0, round: 0, value: true },
1292 ],
1293 code_distance: 3,
1294 num_rounds: 1,
1295 };
1296 let defects = decoder.extract_defects(&syndrome);
1297 assert_eq!(defects.len(), 1);
1300 assert_eq!(defects[0].x, 1);
1301 assert_eq!(defects[0].y, 0);
1302 }
1303
1304 #[test]
1305 fn test_uf_decoder_manhattan_distance() {
1306 let a = Defect { x: 0, y: 0, round: 0, node_index: 0 };
1307 let b = Defect { x: 3, y: 4, round: 1, node_index: 1 };
1308 assert_eq!(UnionFindDecoder::manhattan_distance(&a, &b), 8);
1309 }
1310
1311 #[test]
1312 fn test_uf_decoder_boundary_distance() {
1313 let d = Defect { x: 0, y: 0, round: 0, node_index: 0 };
1314 assert_eq!(UnionFindDecoder::boundary_distance(&d, 5), 0);
1315
1316 let d2 = Defect { x: 2, y: 2, round: 0, node_index: 0 };
1317 assert_eq!(UnionFindDecoder::boundary_distance(&d2, 5), 1);
1318 }
1319
1320 #[test]
1321 fn test_uf_decoder_multi_round() {
1322 let decoder = UnionFindDecoder::new(0);
1323 let syndrome = SyndromeData {
1324 stabilizers: vec![
1325 StabilizerMeasurement { x: 0, y: 0, round: 0, value: true },
1326 StabilizerMeasurement { x: 0, y: 0, round: 1, value: false },
1327 ],
1328 code_distance: 3,
1329 num_rounds: 2,
1330 };
1331 let defects = decoder.extract_defects(&syndrome);
1332 assert_eq!(defects.len(), 2);
1335 }
1336
1337 #[test]
1338 fn test_uf_decoder_confidence_decreases_with_errors() {
1339 let decoder = UnionFindDecoder::new(0);
1340
1341 let syndrome_low = SyndromeData {
1343 stabilizers: vec![
1344 StabilizerMeasurement { x: 0, y: 0, round: 0, value: true },
1345 StabilizerMeasurement { x: 1, y: 0, round: 0, value: false },
1346 StabilizerMeasurement { x: 0, y: 1, round: 0, value: false },
1347 StabilizerMeasurement { x: 1, y: 1, round: 0, value: false },
1348 ],
1349 code_distance: 3,
1350 num_rounds: 1,
1351 };
1352 let corr_low = decoder.decode(&syndrome_low);
1353
1354 let syndrome_high = SyndromeData {
1356 stabilizers: vec![
1357 StabilizerMeasurement { x: 0, y: 0, round: 0, value: true },
1358 StabilizerMeasurement { x: 1, y: 0, round: 0, value: true },
1359 StabilizerMeasurement { x: 0, y: 1, round: 0, value: true },
1360 StabilizerMeasurement { x: 1, y: 1, round: 0, value: true },
1361 ],
1362 code_distance: 3,
1363 num_rounds: 1,
1364 };
1365 let corr_high = decoder.decode(&syndrome_high);
1366
1367 assert!(
1368 corr_low.confidence >= corr_high.confidence,
1369 "More defects should reduce confidence: {} >= {}",
1370 corr_low.confidence,
1371 corr_high.confidence
1372 );
1373 }
1374
1375 #[test]
1376 fn test_uf_decoder_decode_time_recorded() {
1377 let decoder = UnionFindDecoder::new(0);
1378 let syndrome = SyndromeData {
1379 stabilizers: vec![
1380 StabilizerMeasurement { x: 0, y: 0, round: 0, value: true },
1381 ],
1382 code_distance: 3,
1383 num_rounds: 1,
1384 };
1385 let correction = decoder.decode(&syndrome);
1386 let _ = correction.decode_time_ns;
1389 }
1390
1391 #[test]
1394 fn test_partitioned_decoder_no_errors() {
1395 let inner = Box::new(UnionFindDecoder::new(0));
1396 let decoder = PartitionedDecoder::new(4, inner);
1397
1398 let mut stabs = Vec::new();
1399 for y in 0..4 {
1400 for x in 0..4 {
1401 stabs.push(StabilizerMeasurement {
1402 x,
1403 y,
1404 round: 0,
1405 value: false,
1406 });
1407 }
1408 }
1409
1410 let syndrome = SyndromeData {
1411 stabilizers: stabs,
1412 code_distance: 5,
1413 num_rounds: 1,
1414 };
1415
1416 let correction = decoder.decode(&syndrome);
1417 assert!(correction.pauli_corrections.is_empty());
1418 }
1419
1420 #[test]
1421 fn test_partitioned_decoder_name() {
1422 let inner = Box::new(UnionFindDecoder::new(0));
1423 let decoder = PartitionedDecoder::new(4, inner);
1424 assert_eq!(decoder.name(), "PartitionedDecoder");
1425 }
1426
1427 #[test]
1428 fn test_partitioned_decoder_single_tile() {
1429 let inner = Box::new(UnionFindDecoder::new(0));
1431 let decoder = PartitionedDecoder::new(100, inner);
1432
1433 let syndrome = SyndromeData {
1434 stabilizers: vec![
1435 StabilizerMeasurement { x: 0, y: 0, round: 0, value: true },
1436 StabilizerMeasurement { x: 1, y: 0, round: 0, value: false },
1437 ],
1438 code_distance: 3,
1439 num_rounds: 1,
1440 };
1441
1442 let correction = decoder.decode(&syndrome);
1443 assert!(!correction.pauli_corrections.is_empty());
1444 }
1445
1446 #[test]
1447 fn test_partitioned_decoder_multi_tile() {
1448 let inner = Box::new(UnionFindDecoder::new(0));
1449 let decoder = PartitionedDecoder::new(2, inner);
1450
1451 let mut stabs = Vec::new();
1452 for y in 0..6 {
1453 for x in 0..6 {
1454 stabs.push(StabilizerMeasurement {
1455 x,
1456 y,
1457 round: 0,
1458 value: false,
1459 });
1460 }
1461 }
1462 stabs[0].value = true;
1464
1465 let syndrome = SyndromeData {
1466 stabilizers: stabs,
1467 code_distance: 7,
1468 num_rounds: 1,
1469 };
1470
1471 let correction = decoder.decode(&syndrome);
1472 assert!(!correction.pauli_corrections.is_empty());
1473 }
1474
1475 #[test]
1476 fn test_partitioned_decoder_partition_count() {
1477 let inner = Box::new(UnionFindDecoder::new(0));
1478 let decoder = PartitionedDecoder::new(2, inner);
1479
1480 let syndrome = SyndromeData {
1481 stabilizers: Vec::new(),
1482 code_distance: 5,
1483 num_rounds: 1,
1484 };
1485
1486 let tiles = decoder.partition_syndrome(&syndrome);
1487 assert_eq!(tiles.len(), 4);
1489 }
1490
1491 #[test]
1492 #[should_panic(expected = "tile_size must be positive")]
1493 fn test_partitioned_decoder_zero_tile_size() {
1494 let inner = Box::new(UnionFindDecoder::new(0));
1495 let _decoder = PartitionedDecoder::new(0, inner);
1496 }
1497
1498 #[test]
1501 fn test_adaptive_code_distance_creation() {
1502 let acd = AdaptiveCodeDistance::new(5, 3, 15);
1503 assert_eq!(acd.current_distance, 5);
1504 assert_eq!(acd.min_distance, 3);
1505 assert_eq!(acd.max_distance, 15);
1506 }
1507
1508 #[test]
1509 fn test_adaptive_code_distance_even_initial() {
1510 let acd = AdaptiveCodeDistance::new(4, 3, 15);
1512 assert_eq!(acd.current_distance, 5);
1513 }
1514
1515 #[test]
1516 fn test_adaptive_code_distance_no_data() {
1517 let acd = AdaptiveCodeDistance::new(5, 3, 15);
1518 assert_eq!(acd.recommended_distance(), 5);
1519 assert!(!acd.should_increase());
1520 assert!(!acd.should_decrease());
1521 }
1522
1523 #[test]
1524 fn test_adaptive_code_distance_increase() {
1525 let mut acd = AdaptiveCodeDistance::new(3, 3, 15);
1526 for _ in 0..200 {
1528 acd.record_error_rate(0.5);
1529 }
1530 assert!(acd.should_increase());
1531 assert_eq!(acd.recommended_distance(), 5);
1532 }
1533
1534 #[test]
1535 fn test_adaptive_code_distance_decrease() {
1536 let mut acd = AdaptiveCodeDistance::new(9, 3, 15);
1537 for _ in 0..200 {
1539 acd.record_error_rate(1e-10);
1540 }
1541 assert!(acd.should_decrease());
1542 assert_eq!(acd.recommended_distance(), 7);
1543 }
1544
1545 #[test]
1546 fn test_adaptive_code_distance_stable() {
1547 let mut acd = AdaptiveCodeDistance::new(5, 3, 15);
1548 for _ in 0..200 {
1552 acd.record_error_rate(0.001);
1553 }
1554 assert!(!acd.should_increase());
1557 }
1558
1559 #[test]
1560 fn test_adaptive_code_distance_at_max() {
1561 let mut acd = AdaptiveCodeDistance::new(15, 3, 15);
1562 for _ in 0..200 {
1563 acd.record_error_rate(0.9);
1564 }
1565 assert!(!acd.should_increase(), "Cannot increase past max");
1566 assert_eq!(acd.recommended_distance(), 15);
1567 }
1568
1569 #[test]
1570 fn test_adaptive_code_distance_at_min() {
1571 let mut acd = AdaptiveCodeDistance::new(3, 3, 15);
1572 for _ in 0..200 {
1573 acd.record_error_rate(1e-15);
1574 }
1575 assert!(!acd.should_decrease(), "Cannot decrease past min");
1576 }
1577
1578 #[test]
1579 fn test_adaptive_code_distance_record_clamps() {
1580 let mut acd = AdaptiveCodeDistance::new(5, 3, 15);
1581 acd.record_error_rate(2.0);
1582 acd.record_error_rate(-1.0);
1583 assert_eq!(acd.error_history.len(), 2);
1585 assert_eq!(acd.error_history[0], 1.0);
1586 assert_eq!(acd.error_history[1], 0.0);
1587 }
1588
1589 #[test]
1590 fn test_adaptive_code_distance_window_trimming() {
1591 let mut acd = AdaptiveCodeDistance::new(5, 3, 15);
1592 for i in 0..500 {
1593 acd.record_error_rate(i as f64 * 0.001);
1594 }
1595 assert!(acd.error_history.len() <= acd.window_size * 2);
1597 }
1598
1599 #[test]
1600 #[should_panic(expected = "min_distance must be <= max_distance")]
1601 fn test_adaptive_code_distance_invalid_range() {
1602 let _acd = AdaptiveCodeDistance::new(5, 10, 3);
1603 }
1604
1605 #[test]
1608 fn test_surface_code_patch_creation() {
1609 let patch = SurfaceCodePatch {
1610 logical_id: 0,
1611 physical_qubits: vec![0, 1, 2, 3, 4],
1612 code_distance: 3,
1613 x_origin: 0,
1614 y_origin: 0,
1615 };
1616 assert_eq!(patch.logical_id, 0);
1617 assert_eq!(patch.physical_qubits.len(), 5);
1618 }
1619
1620 #[test]
1623 fn test_allocator_creation() {
1624 let alloc = LogicalQubitAllocator::new(1000, 3);
1625 assert_eq!(alloc.total_physical, 1000);
1626 assert_eq!(alloc.code_distance, 3);
1627 assert!(alloc.patches().is_empty());
1628 }
1629
1630 #[test]
1631 fn test_allocator_max_logical_qubits() {
1632 let alloc = LogicalQubitAllocator::new(100, 3);
1634 assert_eq!(alloc.max_logical_qubits(), 7); }
1636
1637 #[test]
1638 fn test_allocator_max_logical_qubits_d5() {
1639 let alloc = LogicalQubitAllocator::new(1000, 5);
1641 assert_eq!(alloc.max_logical_qubits(), 24); }
1643
1644 #[test]
1645 fn test_allocator_allocate_and_deallocate() {
1646 let mut alloc = LogicalQubitAllocator::new(100, 3);
1647 let patch = alloc.allocate().unwrap();
1648 assert_eq!(patch.logical_id, 0);
1649 assert_eq!(patch.code_distance, 3);
1650 assert_eq!(patch.physical_qubits.len(), 13);
1651 assert_eq!(alloc.patches().len(), 1);
1652
1653 alloc.deallocate(0);
1654 assert!(alloc.patches().is_empty());
1655 }
1656
1657 #[test]
1658 fn test_allocator_multiple_allocations() {
1659 let mut alloc = LogicalQubitAllocator::new(100, 3);
1660 let max = alloc.max_logical_qubits();
1661 for i in 0..max {
1662 let patch = alloc.allocate();
1663 assert!(patch.is_some(), "Should allocate patch {}", i);
1664 }
1665 assert!(alloc.allocate().is_none(), "Should be out of space");
1667 }
1668
1669 #[test]
1670 fn test_allocator_utilization() {
1671 let mut alloc = LogicalQubitAllocator::new(100, 3);
1672 assert_eq!(alloc.utilization(), 0.0);
1673
1674 alloc.allocate();
1675 let expected = 13.0 / 100.0;
1676 assert!((alloc.utilization() - expected).abs() < 1e-10);
1677 }
1678
1679 #[test]
1680 fn test_allocator_deallocate_nonexistent() {
1681 let mut alloc = LogicalQubitAllocator::new(100, 3);
1682 alloc.allocate();
1683 alloc.deallocate(999); assert_eq!(alloc.patches().len(), 1);
1685 }
1686
1687 #[test]
1688 fn test_allocator_utilization_zero_physical() {
1689 let alloc = LogicalQubitAllocator::new(0, 3);
1690 assert_eq!(alloc.utilization(), 0.0);
1691 assert_eq!(alloc.max_logical_qubits(), 0);
1692 }
1693
1694 #[test]
1695 fn test_allocator_reallocate_after_dealloc() {
1696 let mut alloc = LogicalQubitAllocator::new(26, 3);
1697 let p0 = alloc.allocate().unwrap();
1699 let _p1 = alloc.allocate().unwrap();
1700 assert!(alloc.allocate().is_none());
1701
1702 alloc.deallocate(p0.logical_id);
1703 let p2 = alloc.allocate();
1705 assert!(p2.is_some());
1706 }
1707
1708 #[test]
1711 fn test_decoder_benchmark_empty() {
1712 let b = DecoderBenchmark {
1713 total_syndromes: 0,
1714 total_decode_time_ns: 0,
1715 correct_corrections: 0,
1716 logical_error_rate: 0.0,
1717 };
1718 assert_eq!(b.avg_decode_time_ns(), 0.0);
1719 assert_eq!(b.throughput(), 0.0);
1720 }
1721
1722 #[test]
1723 fn test_decoder_benchmark_avg_time() {
1724 let b = DecoderBenchmark {
1725 total_syndromes: 100,
1726 total_decode_time_ns: 1_000_000,
1727 correct_corrections: 95,
1728 logical_error_rate: 0.05,
1729 };
1730 assert!((b.avg_decode_time_ns() - 10_000.0).abs() < 1e-6);
1731 }
1732
1733 #[test]
1734 fn test_decoder_benchmark_throughput() {
1735 let b = DecoderBenchmark {
1736 total_syndromes: 1000,
1737 total_decode_time_ns: 1_000_000_000, correct_corrections: 999,
1739 logical_error_rate: 0.001,
1740 };
1741 assert!((b.throughput() - 1000.0).abs() < 1e-6);
1742 }
1743
1744 #[test]
1745 fn test_benchmark_decoder_runs() {
1746 let decoder = UnionFindDecoder::new(0);
1747 let result = benchmark_decoder(&decoder, 3, 0.01, 10);
1748 assert_eq!(result.total_syndromes, 10);
1749 assert!(result.logical_error_rate >= 0.0);
1750 assert!(result.logical_error_rate <= 1.0);
1751 }
1752
1753 #[test]
1754 fn test_benchmark_decoder_zero_error_rate() {
1755 let decoder = UnionFindDecoder::new(0);
1756 let result = benchmark_decoder(&decoder, 3, 0.0, 20);
1757 assert_eq!(result.total_syndromes, 20);
1758 assert_eq!(result.correct_corrections, 20);
1761 assert_eq!(result.logical_error_rate, 0.0);
1762 }
1763
1764 #[test]
1765 fn test_benchmark_decoder_high_error_rate() {
1766 let decoder = UnionFindDecoder::new(0);
1767 let result = benchmark_decoder(&decoder, 3, 0.9, 50);
1768 assert_eq!(result.total_syndromes, 50);
1769 assert!(result.logical_error_rate >= 0.0);
1772 }
1773
1774 #[test]
1775 fn test_benchmark_decoder_zero_rounds() {
1776 let decoder = UnionFindDecoder::new(0);
1777 let result = benchmark_decoder(&decoder, 3, 0.01, 0);
1778 assert_eq!(result.total_syndromes, 0);
1779 assert_eq!(result.logical_error_rate, 0.0);
1780 }
1781
1782 #[test]
1785 fn test_uf_decoder_distance_5() {
1786 let decoder = UnionFindDecoder::new(0);
1787 let mut stabs = Vec::new();
1788 for y in 0..4 {
1789 for x in 0..4 {
1790 stabs.push(StabilizerMeasurement {
1791 x,
1792 y,
1793 round: 0,
1794 value: false,
1795 });
1796 }
1797 }
1798 stabs[5].value = true; let syndrome = SyndromeData {
1802 stabilizers: stabs,
1803 code_distance: 5,
1804 num_rounds: 1,
1805 };
1806 let correction = decoder.decode(&syndrome);
1807 assert!(!correction.pauli_corrections.is_empty());
1808 }
1809
1810 #[test]
1811 fn test_partitioned_matches_uf_small() {
1812 let syndrome = SyndromeData {
1815 stabilizers: vec![
1816 StabilizerMeasurement { x: 0, y: 0, round: 0, value: true },
1817 StabilizerMeasurement { x: 1, y: 0, round: 0, value: false },
1818 StabilizerMeasurement { x: 0, y: 1, round: 0, value: false },
1819 StabilizerMeasurement { x: 1, y: 1, round: 0, value: false },
1820 ],
1821 code_distance: 3,
1822 num_rounds: 1,
1823 };
1824
1825 let uf = UnionFindDecoder::new(0);
1826 let corr_uf = uf.decode(&syndrome);
1827
1828 let partitioned = PartitionedDecoder::new(10, Box::new(UnionFindDecoder::new(0)));
1829 let corr_part = partitioned.decode(&syndrome);
1830
1831 assert_eq!(
1833 corr_uf.pauli_corrections.is_empty(),
1834 corr_part.pauli_corrections.is_empty()
1835 );
1836 }
1837
1838 #[test]
1839 fn test_decoder_trait_object() {
1840 let decoders: Vec<Box<dyn SurfaceCodeDecoder>> = vec![
1842 Box::new(UnionFindDecoder::new(0)),
1843 Box::new(PartitionedDecoder::new(4, Box::new(UnionFindDecoder::new(0)))),
1844 ];
1845
1846 let syndrome = SyndromeData {
1847 stabilizers: vec![
1848 StabilizerMeasurement { x: 0, y: 0, round: 0, value: false },
1849 ],
1850 code_distance: 3,
1851 num_rounds: 1,
1852 };
1853
1854 for decoder in &decoders {
1855 let correction = decoder.decode(&syndrome);
1856 assert!(!decoder.name().is_empty());
1857 assert!(correction.confidence >= 0.0);
1858 }
1859 }
1860
1861 #[test]
1862 fn test_logical_outcome_parity() {
1863 assert!(!UnionFindDecoder::infer_logical_outcome(&[
1865 (0, PauliType::X),
1866 (1, PauliType::X),
1867 ]));
1868 assert!(UnionFindDecoder::infer_logical_outcome(&[
1870 (0, PauliType::X),
1871 ]));
1872 assert!(!UnionFindDecoder::infer_logical_outcome(&[
1874 (0, PauliType::Z),
1875 (1, PauliType::Z),
1876 (2, PauliType::Z),
1877 ]));
1878 }
1879
1880 #[test]
1881 fn test_distance_1_code() {
1882 let decoder = UnionFindDecoder::new(0);
1884 let syndrome = SyndromeData {
1885 stabilizers: vec![
1886 StabilizerMeasurement { x: 0, y: 0, round: 0, value: true },
1887 ],
1888 code_distance: 1,
1889 num_rounds: 1,
1890 };
1891 let correction = decoder.decode(&syndrome);
1892 let _ = correction; }
1894
1895 #[test]
1896 fn test_large_code_distance() {
1897 let decoder = UnionFindDecoder::new(0);
1898 let d = 11u32;
1899 let grid = d - 1;
1900 let mut stabs = Vec::new();
1901 for y in 0..grid {
1902 for x in 0..grid {
1903 stabs.push(StabilizerMeasurement {
1904 x,
1905 y,
1906 round: 0,
1907 value: false,
1908 });
1909 }
1910 }
1911 stabs[0].value = true;
1913 stabs[(grid * grid - 1) as usize].value = true;
1914
1915 let syndrome = SyndromeData {
1916 stabilizers: stabs,
1917 code_distance: d,
1918 num_rounds: 1,
1919 };
1920 let correction = decoder.decode(&syndrome);
1921 assert!(!correction.pauli_corrections.is_empty());
1922 }
1923}