Skip to main content

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