quantrs2_core/error_correction/
mod.rs

1//! Quantum error correction codes and decoders
2//!
3//! This module provides comprehensive quantum error correction functionality including:
4//! - Pauli operators and strings
5//! - Stabilizer codes (repetition, 5-qubit, Steane)
6//! - Surface codes and topological codes
7//! - Syndrome decoders (lookup table, MWPM, ML)
8//! - Color codes and concatenated codes
9//! - LDPC and hypergraph product codes
10//! - Real-time error correction
11//! - Logical gate synthesis
12//! - Adaptive threshold estimation
13
14mod adaptive_threshold;
15mod color_code;
16mod concatenated;
17mod decoders;
18mod hypergraph;
19mod ldpc;
20mod logical_gates;
21mod ml_decoder;
22mod pauli;
23pub mod real_time;
24mod stabilizer;
25mod surface_code;
26mod toric_code;
27
28pub use adaptive_threshold::*;
29pub use color_code::*;
30pub use concatenated::*;
31pub use decoders::*;
32pub use hypergraph::*;
33pub use ldpc::*;
34pub use logical_gates::*;
35pub use ml_decoder::*;
36pub use pauli::*;
37pub use stabilizer::*;
38pub use surface_code::*;
39pub use toric_code::*;
40
41use crate::error::QuantRS2Result;
42
43/// Trait for syndrome decoders
44pub trait SyndromeDecoder {
45    /// Decode a syndrome to find the most likely error
46    fn decode(&self, syndrome: &[bool]) -> QuantRS2Result<PauliString>;
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52    use scirs2_core::ndarray::Array2;
53    use scirs2_core::Complex64;
54
55    #[test]
56    fn test_pauli_multiplication() {
57        let (phase, result) = Pauli::X.multiply(&Pauli::Y);
58        assert_eq!(result, Pauli::Z);
59        assert_eq!(phase, Complex64::new(0.0, 1.0));
60    }
61
62    #[test]
63    fn test_pauli_string_commutation() {
64        let ps1 = PauliString::new(vec![Pauli::X, Pauli::I]);
65        let ps2 = PauliString::new(vec![Pauli::Z, Pauli::I]);
66        assert!(!ps1
67            .commutes_with(&ps2)
68            .expect("Commutation check should succeed"));
69
70        let ps3 = PauliString::new(vec![Pauli::X, Pauli::I]);
71        let ps4 = PauliString::new(vec![Pauli::I, Pauli::Z]);
72        assert!(ps3
73            .commutes_with(&ps4)
74            .expect("Commutation check should succeed"));
75    }
76
77    #[test]
78    fn test_repetition_code() {
79        let code = StabilizerCode::repetition_code();
80        assert_eq!(code.n, 3);
81        assert_eq!(code.k, 1);
82        assert_eq!(code.d, 1);
83
84        // Test syndrome for X error on first qubit
85        let error = PauliString::new(vec![Pauli::X, Pauli::I, Pauli::I]);
86        let syndrome = code
87            .syndrome(&error)
88            .expect("Syndrome extraction should succeed");
89        // X error anti-commutes with Z stabilizer on first two qubits
90        assert_eq!(syndrome, vec![true, false]);
91    }
92
93    #[test]
94    fn test_steane_code() {
95        let code = StabilizerCode::steane_code();
96        assert_eq!(code.n, 7);
97        assert_eq!(code.k, 1);
98        assert_eq!(code.d, 3);
99
100        // Test that stabilizers commute
101        for i in 0..code.stabilizers.len() {
102            for j in i + 1..code.stabilizers.len() {
103                assert!(code.stabilizers[i]
104                    .commutes_with(&code.stabilizers[j])
105                    .expect("Stabilizer commutation check should succeed"));
106            }
107        }
108    }
109
110    #[test]
111    fn test_surface_code() {
112        let surface = SurfaceCode::new(3, 3);
113        assert_eq!(surface.distance(), 3);
114
115        let code = surface
116            .to_stabilizer_code()
117            .expect("Surface code conversion should succeed");
118        assert_eq!(code.n, 9);
119        // For a 3x3 lattice, we have 2 X stabilizers and 2 Z stabilizers
120        assert_eq!(code.stabilizers.len(), 4);
121    }
122
123    #[test]
124    fn test_lookup_decoder() {
125        let code = StabilizerCode::repetition_code();
126        let decoder = LookupDecoder::new(&code).expect("Lookup decoder creation should succeed");
127
128        // Test decoding trivial syndrome (no error)
129        let trivial_syndrome = vec![false, false];
130        let decoded = decoder
131            .decode(&trivial_syndrome)
132            .expect("Decoding trivial syndrome should succeed");
133        assert_eq!(decoded.weight(), 0); // Should be identity
134
135        // Test single bit flip error
136        let error = PauliString::new(vec![Pauli::X, Pauli::I, Pauli::I]);
137        let syndrome = code
138            .syndrome(&error)
139            .expect("Syndrome extraction should succeed");
140
141        // The decoder should be able to decode this syndrome
142        if let Ok(decoded_error) = decoder.decode(&syndrome) {
143            // Decoder should find a low-weight error
144            assert!(decoded_error.weight() <= 1);
145        }
146    }
147
148    #[test]
149    fn test_concatenated_codes() {
150        let inner_code = StabilizerCode::repetition_code();
151        let outer_code = StabilizerCode::repetition_code();
152        let concat_code = ConcatenatedCode::new(inner_code, outer_code);
153
154        assert_eq!(concat_code.total_qubits(), 9); // 3 * 3
155        assert_eq!(concat_code.logical_qubits(), 1);
156        assert_eq!(concat_code.distance(), 1); // min(1, 1) = 1
157
158        // Test encoding and decoding
159        let logical_state = vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)];
160        let encoded = concat_code
161            .encode(&logical_state)
162            .expect("Encoding should succeed");
163        assert_eq!(encoded.len(), 512); // 2^9
164
165        // Test error correction capability
166        let error = PauliString::new(vec![
167            Pauli::X,
168            Pauli::I,
169            Pauli::I,
170            Pauli::I,
171            Pauli::I,
172            Pauli::I,
173            Pauli::I,
174            Pauli::I,
175            Pauli::I,
176        ]);
177        let corrected = concat_code
178            .correct_error(&encoded, &error)
179            .expect("Error correction should succeed");
180
181        // Verify the state is corrected (simplified check)
182        assert_eq!(corrected.len(), 512);
183    }
184
185    #[test]
186    fn test_hypergraph_product_codes() {
187        // Create small classical codes for testing
188        let h1 = Array2::from_shape_vec((2, 3), vec![1, 1, 0, 0, 1, 1])
189            .expect("Array creation for h1 should succeed");
190        let h2 = Array2::from_shape_vec((2, 3), vec![1, 0, 1, 1, 1, 0])
191            .expect("Array creation for h2 should succeed");
192
193        let hpc = HypergraphProductCode::new(h1, h2);
194
195        // Check dimensions
196        assert_eq!(hpc.n, 12); // n1*m2 + m1*n2 = 3*2 + 2*3 = 12
197        assert_eq!(hpc.k, 1); // k = n1*k2 + k1*n2 - k1*k2 = 3*1 + 1*3 - 1*1 = 5 for this example, but simplified
198
199        let stab_code = hpc
200            .to_stabilizer_code()
201            .expect("Hypergraph product code conversion should succeed");
202        assert!(!stab_code.stabilizers.is_empty());
203    }
204
205    #[test]
206    fn test_quantum_ldpc_codes() {
207        let qldpc = QuantumLDPCCode::bicycle_code(3, 4);
208        assert_eq!(qldpc.n, 24); // 2 * 3 * 4
209        assert_eq!(qldpc.k, 2);
210
211        let stab_code = qldpc
212            .to_stabilizer_code()
213            .expect("Quantum LDPC code conversion should succeed");
214        assert!(!stab_code.stabilizers.is_empty());
215
216        // Test that stabilizers have bounded weight
217        for stabilizer in &stab_code.stabilizers {
218            assert!(stabilizer.weight() <= qldpc.max_weight);
219        }
220    }
221
222    #[test]
223    fn test_topological_codes() {
224        let toric = ToricCode::new(2, 2);
225        assert_eq!(toric.logical_qubits(), 2);
226        assert_eq!(toric.distance(), 2);
227
228        let stab_code = toric
229            .to_stabilizer_code()
230            .expect("Toric code conversion should succeed");
231        assert_eq!(stab_code.n, 8); // 2 * 2 * 2
232        assert_eq!(stab_code.k, 2);
233
234        // Test that all stabilizers commute
235        for i in 0..stab_code.stabilizers.len() {
236            for j in i + 1..stab_code.stabilizers.len() {
237                assert!(stab_code.stabilizers[i]
238                    .commutes_with(&stab_code.stabilizers[j])
239                    .expect("Stabilizer commutation check should succeed"));
240            }
241        }
242    }
243
244    #[test]
245    fn test_ml_decoder() {
246        let surface = SurfaceCode::new(3, 3);
247        let decoder = MLDecoder::new(
248            surface
249                .to_stabilizer_code()
250                .expect("Surface code conversion should succeed"),
251        );
252
253        // Test decoding with simple syndrome
254        let syndrome = vec![true, false, true, false];
255        let decoded = decoder.decode(&syndrome);
256
257        // Should succeed for correctable errors
258        assert!(decoded.is_ok() || syndrome.iter().filter(|&&x| x).count() % 2 == 1);
259    }
260
261    #[test]
262    fn test_real_time_mock_hardware() {
263        use crate::error_correction::real_time::*;
264        use std::time::Duration;
265
266        let hardware = MockQuantumHardware::new(0.01, Duration::from_micros(10), 4);
267
268        // Test syndrome measurement
269        let syndrome = hardware
270            .measure_syndromes()
271            .expect("Syndrome measurement should succeed");
272        assert_eq!(syndrome.len(), 4);
273
274        // Test error characteristics
275        let characteristics = hardware
276            .get_error_characteristics()
277            .expect("Getting error characteristics should succeed");
278        assert_eq!(characteristics.single_qubit_error_rates.len(), 4);
279
280        // Test latency stats
281        let stats = hardware
282            .get_latency_stats()
283            .expect("Getting latency stats should succeed");
284        assert!(stats.throughput_hz > 0.0);
285
286        assert!(hardware.is_ready());
287    }
288
289    #[test]
290    fn test_real_time_performance_monitor() {
291        use crate::error_correction::real_time::*;
292        use std::time::Duration;
293
294        let mut monitor = PerformanceMonitor::new();
295
296        // Record some cycles
297        monitor.record_cycle(Duration::from_micros(10), true);
298        monitor.record_cycle(Duration::from_micros(20), false);
299        monitor.record_cycle(Duration::from_micros(15), true);
300
301        assert_eq!(monitor.cycles_processed, 3);
302        assert_eq!(monitor.errors_corrected, 2);
303        assert_eq!(monitor.error_correction_rate(), 2.0 / 3.0);
304        assert!(monitor.average_latency().as_micros() > 10);
305        assert!(monitor.current_throughput() > 0.0);
306    }
307
308    #[test]
309    fn test_real_time_adaptive_decoder() {
310        use crate::error_correction::real_time::*;
311        use std::sync::Arc;
312
313        let code = StabilizerCode::repetition_code();
314        let base_decoder =
315            Arc::new(LookupDecoder::new(&code).expect("Lookup decoder creation should succeed"));
316        let characteristics = HardwareErrorCharacteristics {
317            single_qubit_error_rates: vec![0.01; 3],
318            two_qubit_error_rates: vec![0.1; 1],
319            measurement_error_rates: vec![0.001; 3],
320            correlated_errors: Vec::new(),
321            temporal_variation: 0.01,
322        };
323
324        let mut adaptive_decoder = AdaptiveThresholdDecoder::new(base_decoder, characteristics);
325
326        // Test initial threshold
327        let initial_threshold = adaptive_decoder.current_threshold();
328        assert_eq!(initial_threshold, 1.0); // Default when no history
329
330        // Adapt thresholds based on feedback (use no-error syndromes)
331        adaptive_decoder.adapt_thresholds(&[false, false], true); // No error, successful correction
332
333        let new_threshold = adaptive_decoder.current_threshold();
334        assert!(new_threshold != initial_threshold); // Should change from default 1.0 to 0.0
335
336        // Test decoding (use no-error syndrome which should always be valid)
337        let syndrome = vec![false, false]; // No error syndrome
338        let result = adaptive_decoder.decode(&syndrome);
339        assert!(result.is_ok(), "Decoding failed: {:?}", result.err());
340    }
341
342    #[test]
343    fn test_real_time_parallel_decoder() {
344        use crate::error_correction::real_time::*;
345        use std::sync::Arc;
346
347        let code = StabilizerCode::repetition_code();
348        let base_decoder =
349            Arc::new(LookupDecoder::new(&code).expect("Lookup decoder creation should succeed"));
350        let parallel_decoder = ParallelSyndromeDecoder::new(base_decoder, 2);
351
352        // Test single syndrome decoding (use no-error syndrome)
353        let syndrome = vec![false, false]; // No error syndrome
354        let result = parallel_decoder.decode(&syndrome);
355        assert!(result.is_ok(), "Decoding failed: {:?}", result.err());
356
357        // Test batch decoding (use only no-error syndromes for safety)
358        let syndromes = vec![
359            vec![false, false], // No error syndromes
360            vec![false, false],
361            vec![false, false],
362            vec![false, false],
363        ];
364
365        let results = parallel_decoder.decode_batch(&syndromes);
366        assert!(results.is_ok());
367        let corrections = results.expect("Batch decoding should succeed");
368        assert_eq!(corrections.len(), 4);
369    }
370
371    #[test]
372    fn test_real_time_syndrome_stream_processor() {
373        use crate::error_correction::real_time::*;
374        use std::sync::Arc;
375        use std::time::Duration;
376
377        let code = StabilizerCode::repetition_code();
378        let decoder =
379            Arc::new(LookupDecoder::new(&code).expect("Lookup decoder creation should succeed"));
380        let hardware = Arc::new(MockQuantumHardware::new(0.01, Duration::from_micros(1), 3));
381        let config = RealTimeConfig {
382            max_latency: Duration::from_millis(1),
383            buffer_size: 10,
384            parallel_workers: 1,
385            adaptive_threshold: false,
386            hardware_feedback: false,
387            performance_logging: true,
388        };
389
390        let processor = SyndromeStreamProcessor::new(decoder, hardware, config);
391
392        // Test buffer status
393        let (current, max) = processor.get_buffer_status();
394        assert_eq!(current, 0);
395        assert_eq!(max, 10);
396
397        // Test performance stats (initial state)
398        let stats = processor.get_performance_stats();
399        assert_eq!(stats.cycles_processed, 0);
400        assert_eq!(stats.errors_corrected, 0);
401    }
402
403    #[test]
404    fn test_logical_gate_synthesizer() {
405        use crate::error_correction::logical_gates::*;
406
407        let code = StabilizerCode::repetition_code();
408        let synthesizer = LogicalGateSynthesizer::new(0.01);
409
410        // Test logical X gate synthesis
411        let logical_x = synthesizer.synthesize_logical_x(&code, 0);
412        assert!(logical_x.is_ok());
413
414        let x_gate = logical_x.expect("Logical X synthesis should succeed");
415        assert_eq!(x_gate.logical_qubits, vec![0]);
416        assert_eq!(x_gate.physical_operations.len(), 1);
417        assert!(!x_gate.error_propagation.single_qubit_propagation.is_empty());
418
419        // Test logical Z gate synthesis
420        let logical_z = synthesizer.synthesize_logical_z(&code, 0);
421        assert!(logical_z.is_ok());
422
423        let z_gate = logical_z.expect("Logical Z synthesis should succeed");
424        assert_eq!(z_gate.logical_qubits, vec![0]);
425        assert_eq!(z_gate.physical_operations.len(), 1);
426
427        // Test logical H gate synthesis
428        let logical_h = synthesizer.synthesize_logical_h(&code, 0);
429        assert!(logical_h.is_ok());
430
431        let h_gate = logical_h.expect("Logical H synthesis should succeed");
432        assert_eq!(h_gate.logical_qubits, vec![0]);
433        assert_eq!(h_gate.physical_operations.len(), 1);
434        assert_eq!(h_gate.physical_operations[0].error_correction_rounds, 2);
435
436        // Test invalid logical qubit index
437        let invalid_gate = synthesizer.synthesize_logical_x(&code, 5);
438        assert!(invalid_gate.is_err());
439    }
440
441    #[test]
442    fn test_logical_circuit_synthesizer() {
443        use crate::error_correction::logical_gates::*;
444
445        let code = StabilizerCode::repetition_code();
446        let synthesizer = LogicalCircuitSynthesizer::new(0.01);
447
448        // Test simple circuit synthesis
449        let gate_sequence = vec![("x", vec![0]), ("h", vec![0]), ("z", vec![0])];
450
451        let circuit = synthesizer.synthesize_circuit(&code, &gate_sequence);
452        assert!(circuit.is_ok());
453
454        let logical_circuit = circuit.expect("Circuit synthesis should succeed");
455        assert_eq!(logical_circuit.len(), 3);
456
457        // Test resource estimation
458        let resources = synthesizer.estimate_resources(&logical_circuit);
459        assert!(resources.total_physical_operations > 0);
460        assert!(resources.total_error_correction_rounds > 0);
461        assert_eq!(resources.estimated_depth, 3);
462
463        // Test invalid gate name
464        let invalid_sequence = vec![("invalid_gate", vec![0])];
465        let invalid_circuit = synthesizer.synthesize_circuit(&code, &invalid_sequence);
466        assert!(invalid_circuit.is_err());
467
468        // Test CNOT gate with wrong number of targets
469        let wrong_cnot = vec![("cnot", vec![0])]; // CNOT needs 2 targets
470        let wrong_circuit = synthesizer.synthesize_circuit(&code, &wrong_cnot);
471        assert!(wrong_circuit.is_err());
472    }
473
474    #[test]
475    fn test_logical_t_gate_synthesis() {
476        use crate::error_correction::logical_gates::*;
477
478        let code = StabilizerCode::repetition_code();
479        let synthesizer = LogicalGateSynthesizer::new(0.01);
480
481        // Test T gate synthesis (requires magic state distillation)
482        let logical_t = synthesizer.synthesize_logical_t(&code, 0);
483        assert!(logical_t.is_ok());
484
485        let t_gate = logical_t.expect("Logical T synthesis should succeed");
486        assert_eq!(t_gate.logical_qubits, vec![0]);
487        assert_eq!(t_gate.physical_operations.len(), 2); // Magic state prep + injection
488
489        // Check that magic state prep has more error correction rounds
490        assert!(t_gate.physical_operations[0].error_correction_rounds >= 5);
491    }
492
493    #[test]
494    fn test_error_propagation_analysis() {
495        use crate::error_correction::logical_gates::*;
496
497        let code = StabilizerCode::repetition_code();
498        let synthesizer = LogicalGateSynthesizer::new(0.01);
499
500        let logical_x = synthesizer
501            .synthesize_logical_x(&code, 0)
502            .expect("Logical X synthesis should succeed");
503
504        // Check error propagation analysis
505        let analysis = &logical_x.error_propagation;
506        assert!(!analysis.single_qubit_propagation.is_empty());
507        // max_error_weight is usize, so it's always >= 0
508        assert_eq!(analysis.fault_tolerance_threshold, 0.01);
509
510        // Check that some errors are marked as correctable
511        let correctable_count = analysis
512            .single_qubit_propagation
513            .iter()
514            .filter(|path| path.correctable)
515            .count();
516        assert!(correctable_count > 0);
517    }
518
519    #[test]
520    fn test_pauli_string_weight() {
521        let identity_string = PauliString::new(vec![Pauli::I, Pauli::I, Pauli::I]);
522        assert_eq!(identity_string.weight(), 0);
523
524        let single_error = PauliString::new(vec![Pauli::X, Pauli::I, Pauli::I]);
525        assert_eq!(single_error.weight(), 1);
526
527        let multi_error = PauliString::new(vec![Pauli::X, Pauli::Y, Pauli::Z]);
528        assert_eq!(multi_error.weight(), 3);
529    }
530
531    #[test]
532    fn test_logical_circuit_with_multiple_gates() {
533        use crate::error_correction::logical_gates::*;
534
535        let code = StabilizerCode::repetition_code();
536        let synthesizer = LogicalCircuitSynthesizer::new(0.01);
537
538        // Test a more complex circuit
539        let gate_sequence = vec![
540            ("h", vec![0]), // Hadamard on logical qubit 0
541            ("x", vec![0]), // X on logical qubit 0
542            ("z", vec![0]), // Z on logical qubit 0
543            ("h", vec![0]), // Another Hadamard
544        ];
545
546        let circuit = synthesizer.synthesize_circuit(&code, &gate_sequence);
547        assert!(circuit.is_ok());
548
549        let logical_circuit = circuit.expect("Circuit synthesis should succeed");
550        assert_eq!(logical_circuit.len(), 4);
551
552        // Check that all gates target the correct logical qubit
553        for gate in &logical_circuit {
554            assert_eq!(gate.logical_qubits, vec![0]);
555        }
556
557        // Estimate resources for this circuit
558        let resources = synthesizer.estimate_resources(&logical_circuit);
559        assert_eq!(resources.estimated_depth, 4);
560        assert!(resources.total_error_correction_rounds >= 4); // At least one round per gate
561    }
562
563    #[test]
564    fn test_adaptive_threshold_estimator() {
565        use crate::error_correction::adaptive_threshold::*;
566
567        let noise_model = NoiseModel::default();
568        let algorithm = ThresholdEstimationAlgorithm::Bayesian {
569            prior_strength: 1.0,
570            update_rate: 0.1,
571        };
572        let config = AdaptiveConfig::default();
573
574        let mut estimator = AdaptiveThresholdEstimator::new(noise_model, algorithm, config);
575
576        // Test initial threshold estimation
577        let syndrome = vec![true, false];
578        let env = EnvironmentalConditions::default();
579        let threshold = estimator.estimate_threshold(&syndrome, &env);
580        assert!(threshold > 0.0);
581        assert!(threshold < 1.0);
582
583        // Test adding observations
584        let observation = ErrorObservation {
585            syndrome: syndrome.clone(),
586            correction: PauliString::new(vec![Pauli::X, Pauli::I]),
587            success: true,
588            observed_error_rate: 0.01,
589            timestamp: std::time::Instant::now(),
590            environment: env.clone(),
591        };
592
593        estimator.add_observation(observation);
594
595        // Test threshold recommendation
596        let recommendation = estimator.get_threshold_recommendation(&syndrome);
597        assert!(recommendation.threshold > 0.0);
598        assert!(recommendation.confidence >= 0.0 && recommendation.confidence <= 1.0);
599        assert!(recommendation.predicted_error_rate >= 0.0);
600    }
601
602    #[test]
603    fn test_performance_tracker() {
604        use crate::error_correction::adaptive_threshold::*;
605
606        let mut tracker = PerformanceTracker::new();
607
608        // Test initial state
609        assert_eq!(tracker.successful_corrections, 0);
610        assert_eq!(tracker.failed_corrections, 0);
611        assert_eq!(tracker.precision(), 1.0); // Default when no data
612        assert_eq!(tracker.recall(), 1.0);
613        assert_eq!(tracker.f1_score(), 1.0); // Perfect when precision and recall are both 1.0
614
615        // Simulate some corrections
616        tracker.successful_corrections = 8;
617        tracker.failed_corrections = 2;
618        tracker.false_positives = 1;
619        tracker.false_negatives = 1;
620
621        // Test metrics
622        assert_eq!(tracker.precision(), 8.0 / 9.0); // 8 / (8 + 1)
623        assert_eq!(tracker.recall(), 8.0 / 9.0); // 8 / (8 + 1)
624        assert!(tracker.f1_score() > 0.0);
625    }
626
627    #[test]
628    fn test_environmental_conditions() {
629        use crate::error_correction::adaptive_threshold::*;
630
631        let mut env = EnvironmentalConditions::default();
632        assert_eq!(env.temperature, 300.0); // Room temperature
633        assert_eq!(env.magnetic_field, 0.0);
634
635        // Test modification
636        env.temperature = 310.0; // Higher temperature
637        env.vibration_level = 0.1;
638
639        let noise_model = NoiseModel::default();
640        let algorithm = ThresholdEstimationAlgorithm::ExponentialAverage { alpha: 0.5 };
641        let config = AdaptiveConfig::default();
642
643        let estimator = AdaptiveThresholdEstimator::new(noise_model, algorithm, config);
644
645        // Test that environmental conditions affect threshold
646        let syndrome = vec![false, false];
647        let threshold_normal =
648            estimator.estimate_threshold(&syndrome, &EnvironmentalConditions::default());
649        let threshold_hot = estimator.estimate_threshold(&syndrome, &env);
650
651        // Thresholds may be different due to environmental factors
652        assert!(threshold_normal >= 0.0);
653        assert!(threshold_hot >= 0.0);
654    }
655
656    #[test]
657    fn test_different_threshold_algorithms() {
658        use crate::error_correction::adaptive_threshold::*;
659
660        let noise_model = NoiseModel::default();
661        let config = AdaptiveConfig::default();
662
663        // Test Bayesian algorithm
664        let bayesian_alg = ThresholdEstimationAlgorithm::Bayesian {
665            prior_strength: 1.0,
666            update_rate: 0.1,
667        };
668        let bayesian_estimator =
669            AdaptiveThresholdEstimator::new(noise_model.clone(), bayesian_alg, config.clone());
670
671        // Test Kalman filter algorithm
672        let kalman_alg = ThresholdEstimationAlgorithm::KalmanFilter {
673            process_noise: 0.01,
674            measurement_noise: 0.1,
675        };
676        let kalman_estimator =
677            AdaptiveThresholdEstimator::new(noise_model.clone(), kalman_alg, config.clone());
678
679        // Test exponential average algorithm
680        let exp_alg = ThresholdEstimationAlgorithm::ExponentialAverage { alpha: 0.3 };
681        let exp_estimator =
682            AdaptiveThresholdEstimator::new(noise_model.clone(), exp_alg, config.clone());
683
684        // Test ML algorithm
685        let ml_alg = ThresholdEstimationAlgorithm::MachineLearning {
686            model_type: MLModelType::LinearRegression,
687            training_window: 50,
688        };
689        let ml_estimator = AdaptiveThresholdEstimator::new(noise_model, ml_alg, config);
690
691        let syndrome = vec![true, false];
692        let env = EnvironmentalConditions::default();
693
694        // All algorithms should produce valid thresholds
695        let bayesian_threshold = bayesian_estimator.estimate_threshold(&syndrome, &env);
696        let kalman_threshold = kalman_estimator.estimate_threshold(&syndrome, &env);
697        let exp_threshold = exp_estimator.estimate_threshold(&syndrome, &env);
698        let ml_threshold = ml_estimator.estimate_threshold(&syndrome, &env);
699
700        assert!(bayesian_threshold > 0.0);
701        assert!(kalman_threshold > 0.0);
702        assert!(exp_threshold > 0.0);
703        assert!(ml_threshold > 0.0);
704    }
705
706    #[test]
707    fn test_noise_model_updates() {
708        use crate::error_correction::adaptive_threshold::*;
709
710        let noise_model = NoiseModel::default();
711        let algorithm = ThresholdEstimationAlgorithm::Bayesian {
712            prior_strength: 1.0,
713            update_rate: 0.1,
714        };
715        let config = AdaptiveConfig {
716            min_observations: 2, // Low threshold for testing
717            real_time_adaptation: true,
718            ..AdaptiveConfig::default()
719        };
720
721        let mut estimator = AdaptiveThresholdEstimator::new(noise_model, algorithm, config);
722
723        // Add multiple observations to trigger model updates
724        for i in 0..5 {
725            let observation = ErrorObservation {
726                syndrome: vec![i % 2 == 0, i % 3 == 0],
727                correction: PauliString::new(vec![Pauli::X, Pauli::I]),
728                success: i % 4 != 0, // Most succeed
729                observed_error_rate: 0.01,
730                timestamp: std::time::Instant::now(),
731                environment: EnvironmentalConditions::default(),
732            };
733            estimator.add_observation(observation);
734        }
735
736        // The estimator should have updated its internal model
737        let recommendation = estimator.get_threshold_recommendation(&[true, false]);
738        assert!(recommendation.confidence > 0.0);
739        assert!(recommendation.recommendation_quality > 0.0);
740    }
741}