1use crate::builder::Circuit;
8use quantrs2_core::{
9 error::{QuantRS2Error, QuantRS2Result},
10 gate::GateOp,
11 qubit::QubitId,
12};
13use serde::{Deserialize, Serialize};
14use std::collections::{HashMap, HashSet, VecDeque};
15use std::sync::Arc;
16
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
19pub enum QECCode {
20 SurfaceCode { distance: usize },
22 ColorCode { distance: usize },
24 RepetitionCode { distance: usize },
26 SteaneCode,
28 ShorCode,
30 BaconShorCode { m: usize, n: usize },
32 ConcatenatedCode {
34 inner_code: Box<Self>,
35 outer_code: Box<Self>,
36 levels: usize,
37 },
38}
39
40impl QECCode {
41 #[must_use]
43 pub fn physical_qubits(&self) -> usize {
44 match self {
45 Self::SurfaceCode { distance } => distance * distance,
46 Self::ColorCode { distance } => 2 * distance * distance - 2 * distance + 1,
47 Self::RepetitionCode { distance } => *distance,
48 Self::SteaneCode => 7,
49 Self::ShorCode => 9,
50 Self::BaconShorCode { m, n } => m * n,
51 Self::ConcatenatedCode {
52 inner_code, levels, ..
53 } => {
54 let base_qubits = inner_code.physical_qubits();
55 (0..*levels).fold(1, |acc, _| acc * base_qubits)
56 }
57 }
58 }
59
60 #[must_use]
62 pub fn distance(&self) -> usize {
63 match self {
64 Self::SurfaceCode { distance }
65 | Self::ColorCode { distance }
66 | Self::RepetitionCode { distance } => *distance,
67 Self::SteaneCode | Self::ShorCode => 3,
68 Self::BaconShorCode { m, n } => (*m).min(*n),
69 Self::ConcatenatedCode {
70 inner_code, levels, ..
71 } => {
72 let base_distance = inner_code.distance();
73 (0..*levels).fold(1, |acc, _| acc * base_distance)
74 }
75 }
76 }
77
78 #[must_use]
80 pub fn can_correct(&self, t: usize) -> bool {
81 self.distance() > 2 * t
82 }
83}
84
85#[derive(Debug, Clone)]
87pub struct LogicalQubit {
88 pub id: usize,
90 pub physical_qubits: Vec<usize>,
92 pub code: QECCode,
94 pub syndrome: Option<Vec<u8>>,
96 pub error_count: usize,
98}
99
100impl LogicalQubit {
101 #[must_use]
103 pub fn new(id: usize, code: QECCode) -> Self {
104 let num_physical = code.physical_qubits();
105 let physical_qubits = (id * num_physical..(id + 1) * num_physical).collect();
106
107 Self {
108 id,
109 physical_qubits,
110 code,
111 syndrome: None,
112 error_count: 0,
113 }
114 }
115
116 #[must_use]
118 pub fn data_qubits(&self) -> Vec<usize> {
119 match &self.code {
120 QECCode::SurfaceCode { distance } => {
121 let mut data_qubits = Vec::new();
123 for i in 0..*distance {
124 for j in 0..*distance {
125 if (i + j) % 2 == 0 {
126 data_qubits.push(self.physical_qubits[i * distance + j]);
128 }
129 }
130 }
131 data_qubits
132 }
133 QECCode::SteaneCode => {
134 self.physical_qubits[0..4].to_vec()
136 }
137 QECCode::ShorCode => {
138 vec![
140 self.physical_qubits[0],
141 self.physical_qubits[3],
142 self.physical_qubits[6],
143 ]
144 }
145 _ => {
146 let half = self.physical_qubits.len() / 2;
148 self.physical_qubits[0..half].to_vec()
149 }
150 }
151 }
152
153 #[must_use]
155 pub fn ancilla_qubits(&self) -> Vec<usize> {
156 let data_qubits = self.data_qubits();
157 self.physical_qubits
158 .iter()
159 .filter(|&&q| !data_qubits.contains(&q))
160 .copied()
161 .collect()
162 }
163}
164
165#[derive(Debug, Clone)]
167pub struct FaultTolerantGate {
168 pub name: String,
170 pub logical_qubits: Vec<usize>,
172 pub physical_gates: Vec<PhysicalGate>,
174 pub syndrome_measurements: Vec<SyndromeMeasurement>,
176 pub magic_states: usize,
178 pub correction_overhead: f64,
180}
181
182#[derive(Debug, Clone)]
184pub struct PhysicalGate {
185 pub gate_type: String,
187 pub qubits: Vec<usize>,
189 pub parameters: Vec<f64>,
191 pub time: Option<f64>,
193}
194
195#[derive(Debug, Clone)]
197pub struct SyndromeMeasurement {
198 pub measurement_type: SyndromeType,
200 pub qubits: Vec<usize>,
202 pub ancilla: usize,
204 pub expected_value: u8,
206}
207
208#[derive(Debug, Clone, PartialEq, Eq)]
210pub enum SyndromeType {
211 XStabilizer,
213 ZStabilizer,
215 XZStabilizer,
217}
218
219#[derive(Debug, Clone, PartialEq)]
221pub enum MagicState {
222 TState,
224 YState,
226 CCZState,
228 Custom { name: String, fidelity: f64 },
230}
231
232impl MagicState {
233 #[must_use]
235 pub const fn fidelity_threshold(&self) -> f64 {
236 match self {
237 Self::TState | Self::YState => 0.95,
238 Self::CCZState => 0.99,
239 Self::Custom { fidelity, .. } => *fidelity,
240 }
241 }
242
243 #[must_use]
245 pub const fn distillation_overhead(&self) -> f64 {
246 match self {
247 Self::TState => 15.0, Self::YState => 10.0,
249 Self::CCZState => 50.0,
250 Self::Custom { .. } => 20.0,
251 }
252 }
253}
254
255pub struct FaultTolerantCompiler {
257 default_code: QECCode,
259 magic_state_factory: MagicStateFactory,
261 error_threshold: f64,
263 options: CompilationOptions,
265}
266
267#[derive(Debug, Clone)]
269pub struct MagicStateFactory {
270 supported_states: Vec<MagicState>,
272 production_rate: HashMap<MagicState, f64>,
274 distillation_protocols: HashMap<MagicState, DistillationProtocol>,
276}
277
278#[derive(Debug, Clone)]
280pub struct DistillationProtocol {
281 pub name: String,
283 pub input_fidelity: f64,
285 pub output_fidelity: f64,
287 pub success_probability: f64,
289 pub qubits_required: usize,
291 pub time_overhead: f64,
293}
294
295#[derive(Debug, Clone)]
297pub struct CompilationOptions {
298 pub optimize_space: bool,
300 pub optimize_time: bool,
302 pub recycle_magic_states: bool,
304 pub syndrome_frequency: usize,
306 pub correction_strategy: CorrectionStrategy,
308}
309
310#[derive(Debug, Clone, PartialEq)]
312pub enum CorrectionStrategy {
313 Immediate,
315 Deferred,
317 Adaptive { threshold: f64 },
319}
320
321impl Default for CompilationOptions {
322 fn default() -> Self {
323 Self {
324 optimize_space: false,
325 optimize_time: true,
326 recycle_magic_states: true,
327 syndrome_frequency: 1,
328 correction_strategy: CorrectionStrategy::Immediate,
329 }
330 }
331}
332
333impl FaultTolerantCompiler {
334 #[must_use]
336 pub fn new(code: QECCode) -> Self {
337 let magic_state_factory = MagicStateFactory {
338 supported_states: vec![MagicState::TState, MagicState::YState],
339 production_rate: HashMap::new(),
340 distillation_protocols: HashMap::new(),
341 };
342
343 Self {
344 default_code: code,
345 magic_state_factory,
346 error_threshold: 1e-3,
347 options: CompilationOptions::default(),
348 }
349 }
350
351 pub fn compile<const N: usize>(
353 &self,
354 logical_circuit: &Circuit<N>,
355 ) -> QuantRS2Result<FaultTolerantCircuit> {
356 let logical_qubits = self.create_logical_qubits(N)?;
358
359 let mut ft_gates = Vec::new();
361 let mut magic_state_count = 0;
362
363 for gate in logical_circuit.gates() {
364 let ft_gate = self.compile_gate(gate.as_ref(), &logical_qubits)?;
365 magic_state_count += ft_gate.magic_states;
366 ft_gates.push(ft_gate);
367 }
368
369 let syndrome_circuits = self.generate_syndrome_circuits(&logical_qubits)?;
371
372 let total_physical_qubits: usize = logical_qubits
374 .iter()
375 .map(|lq| lq.physical_qubits.len())
376 .sum();
377
378 let ancilla_qubits = syndrome_circuits
379 .iter()
380 .map(|sc| sc.ancilla_qubits.len())
381 .sum::<usize>();
382
383 Ok(FaultTolerantCircuit {
384 logical_qubits,
385 ft_gates,
386 syndrome_circuits,
387 magic_state_requirements: magic_state_count,
388 physical_qubit_count: total_physical_qubits + ancilla_qubits,
389 error_threshold: self.error_threshold,
390 code: self.default_code.clone(),
391 })
392 }
393
394 fn create_logical_qubits(&self, num_logical: usize) -> QuantRS2Result<Vec<LogicalQubit>> {
396 let mut logical_qubits = Vec::new();
397
398 for i in 0..num_logical {
399 let logical_qubit = LogicalQubit::new(i, self.default_code.clone());
400 logical_qubits.push(logical_qubit);
401 }
402
403 Ok(logical_qubits)
404 }
405
406 fn compile_gate(
408 &self,
409 gate: &dyn GateOp,
410 logical_qubits: &[LogicalQubit],
411 ) -> QuantRS2Result<FaultTolerantGate> {
412 let gate_name = gate.name();
413 let logical_targets: Vec<_> = gate.qubits().iter().map(|q| q.id() as usize).collect();
414
415 match gate_name {
416 "H" => self.compile_hadamard_gate(&logical_targets, logical_qubits),
417 "CNOT" => self.compile_cnot_gate(&logical_targets, logical_qubits),
418 "T" => self.compile_t_gate(&logical_targets, logical_qubits),
419 "S" => self.compile_s_gate(&logical_targets, logical_qubits),
420 "X" | "Y" | "Z" => self.compile_pauli_gate(gate_name, &logical_targets, logical_qubits),
421 _ => Err(QuantRS2Error::InvalidInput(format!(
422 "Gate {gate_name} not supported in fault-tolerant compilation"
423 ))),
424 }
425 }
426
427 fn compile_hadamard_gate(
429 &self,
430 targets: &[usize],
431 logical_qubits: &[LogicalQubit],
432 ) -> QuantRS2Result<FaultTolerantGate> {
433 if targets.len() != 1 {
434 return Err(QuantRS2Error::InvalidInput(
435 "Hadamard gate requires exactly one target".to_string(),
436 ));
437 }
438
439 let target_lq = &logical_qubits[targets[0]];
440 let mut physical_gates = Vec::new();
441
442 match &target_lq.code {
444 QECCode::SurfaceCode { .. } => {
445 for &data_qubit in &target_lq.data_qubits() {
447 physical_gates.push(PhysicalGate {
448 gate_type: "H".to_string(),
449 qubits: vec![data_qubit],
450 parameters: vec![],
451 time: Some(1.0),
452 });
453 }
454 }
455 _ => {
456 physical_gates.push(PhysicalGate {
458 gate_type: "H".to_string(),
459 qubits: target_lq.data_qubits(),
460 parameters: vec![],
461 time: Some(1.0),
462 });
463 }
464 }
465
466 Ok(FaultTolerantGate {
467 name: "H".to_string(),
468 logical_qubits: targets.to_vec(),
469 physical_gates,
470 syndrome_measurements: vec![],
471 magic_states: 0,
472 correction_overhead: 1.0,
473 })
474 }
475
476 fn compile_cnot_gate(
478 &self,
479 targets: &[usize],
480 logical_qubits: &[LogicalQubit],
481 ) -> QuantRS2Result<FaultTolerantGate> {
482 if targets.len() != 2 {
483 return Err(QuantRS2Error::InvalidInput(
484 "CNOT gate requires exactly two targets".to_string(),
485 ));
486 }
487
488 let control_lq = &logical_qubits[targets[0]];
489 let target_lq = &logical_qubits[targets[1]];
490 let mut physical_gates = Vec::new();
491
492 if let (QECCode::SurfaceCode { .. }, QECCode::SurfaceCode { .. }) =
494 (&control_lq.code, &target_lq.code)
495 {
496 let control_data = control_lq.data_qubits();
498 let target_data = target_lq.data_qubits();
499
500 for (i, (&c_qubit, &t_qubit)) in control_data.iter().zip(target_data.iter()).enumerate()
503 {
504 physical_gates.push(PhysicalGate {
505 gate_type: "CNOT".to_string(),
506 qubits: vec![c_qubit, t_qubit],
507 parameters: vec![],
508 time: Some(10.0), });
510 }
511 } else {
512 let control_data = control_lq.data_qubits();
514 let target_data = target_lq.data_qubits();
515
516 for (&c_qubit, &t_qubit) in control_data.iter().zip(target_data.iter()) {
517 physical_gates.push(PhysicalGate {
518 gate_type: "CNOT".to_string(),
519 qubits: vec![c_qubit, t_qubit],
520 parameters: vec![],
521 time: Some(2.0),
522 });
523 }
524 }
525
526 Ok(FaultTolerantGate {
527 name: "CNOT".to_string(),
528 logical_qubits: targets.to_vec(),
529 physical_gates,
530 syndrome_measurements: vec![],
531 magic_states: 0,
532 correction_overhead: 2.0,
533 })
534 }
535
536 fn compile_t_gate(
538 &self,
539 targets: &[usize],
540 logical_qubits: &[LogicalQubit],
541 ) -> QuantRS2Result<FaultTolerantGate> {
542 if targets.len() != 1 {
543 return Err(QuantRS2Error::InvalidInput(
544 "T gate requires exactly one target".to_string(),
545 ));
546 }
547
548 let target_lq = &logical_qubits[targets[0]];
549 let mut physical_gates = Vec::new();
550 let mut syndrome_measurements = Vec::new();
551
552 let data_qubits = target_lq.data_qubits();
554 let ancilla_qubits = target_lq.ancilla_qubits();
555
556 for (&data_qubit, &ancilla_qubit) in data_qubits.iter().zip(ancilla_qubits.iter()) {
558 physical_gates.push(PhysicalGate {
560 gate_type: "CNOT".to_string(),
561 qubits: vec![ancilla_qubit, data_qubit], parameters: vec![],
563 time: Some(1.0),
564 });
565
566 syndrome_measurements.push(SyndromeMeasurement {
568 measurement_type: SyndromeType::XStabilizer,
569 qubits: vec![ancilla_qubit],
570 ancilla: ancilla_qubit,
571 expected_value: 0,
572 });
573 }
574
575 Ok(FaultTolerantGate {
576 name: "T".to_string(),
577 logical_qubits: targets.to_vec(),
578 physical_gates,
579 syndrome_measurements,
580 magic_states: 1,
581 correction_overhead: 15.0, })
583 }
584
585 fn compile_s_gate(
587 &self,
588 targets: &[usize],
589 logical_qubits: &[LogicalQubit],
590 ) -> QuantRS2Result<FaultTolerantGate> {
591 if targets.len() != 1 {
592 return Err(QuantRS2Error::InvalidInput(
593 "S gate requires exactly one target".to_string(),
594 ));
595 }
596
597 let target_lq = &logical_qubits[targets[0]];
598 let mut physical_gates = Vec::new();
599
600 for &data_qubit in &target_lq.data_qubits() {
602 physical_gates.push(PhysicalGate {
603 gate_type: "S".to_string(),
604 qubits: vec![data_qubit],
605 parameters: vec![],
606 time: Some(1.0),
607 });
608 }
609
610 Ok(FaultTolerantGate {
611 name: "S".to_string(),
612 logical_qubits: targets.to_vec(),
613 physical_gates,
614 syndrome_measurements: vec![],
615 magic_states: 0,
616 correction_overhead: 1.0,
617 })
618 }
619
620 fn compile_pauli_gate(
622 &self,
623 gate_name: &str,
624 targets: &[usize],
625 logical_qubits: &[LogicalQubit],
626 ) -> QuantRS2Result<FaultTolerantGate> {
627 if targets.len() != 1 {
628 return Err(QuantRS2Error::InvalidInput(
629 "Pauli gate requires exactly one target".to_string(),
630 ));
631 }
632
633 let target_lq = &logical_qubits[targets[0]];
634 let mut physical_gates = Vec::new();
635
636 for &data_qubit in &target_lq.data_qubits() {
638 physical_gates.push(PhysicalGate {
639 gate_type: gate_name.to_string(),
640 qubits: vec![data_qubit],
641 parameters: vec![],
642 time: Some(1.0),
643 });
644 }
645
646 Ok(FaultTolerantGate {
647 name: gate_name.to_string(),
648 logical_qubits: targets.to_vec(),
649 physical_gates,
650 syndrome_measurements: vec![],
651 magic_states: 0,
652 correction_overhead: 1.0,
653 })
654 }
655
656 fn generate_syndrome_circuits(
658 &self,
659 logical_qubits: &[LogicalQubit],
660 ) -> QuantRS2Result<Vec<SyndromeCircuit>> {
661 let mut circuits = Vec::new();
662
663 for logical_qubit in logical_qubits {
664 match &logical_qubit.code {
665 QECCode::SurfaceCode { distance } => {
666 circuits.push(self.generate_surface_code_syndrome(logical_qubit, *distance)?);
667 }
668 QECCode::SteaneCode => {
669 circuits.push(self.generate_steane_syndrome(logical_qubit)?);
670 }
671 _ => {
672 circuits.push(self.generate_generic_syndrome(logical_qubit)?);
674 }
675 }
676 }
677
678 Ok(circuits)
679 }
680
681 fn generate_surface_code_syndrome(
683 &self,
684 logical_qubit: &LogicalQubit,
685 distance: usize,
686 ) -> QuantRS2Result<SyndromeCircuit> {
687 let mut measurements = Vec::new();
688 let mut ancilla_qubits = Vec::new();
689
690 for i in 0..distance - 1 {
692 for j in 0..distance {
693 if (i + j) % 2 == 1 {
694 let ancilla = logical_qubit.physical_qubits.len() + ancilla_qubits.len();
696 ancilla_qubits.push(ancilla);
697
698 let involved_qubits = vec![
700 logical_qubit.physical_qubits[i * distance + j],
701 logical_qubit.physical_qubits[(i + 1) * distance + j],
702 ];
703
704 measurements.push(SyndromeMeasurement {
705 measurement_type: SyndromeType::XStabilizer,
706 qubits: involved_qubits,
707 ancilla,
708 expected_value: 0,
709 });
710 }
711 }
712 }
713
714 for i in 0..distance {
716 for j in 0..distance - 1 {
717 if (i + j) % 2 == 0 {
718 let ancilla = logical_qubit.physical_qubits.len() + ancilla_qubits.len();
720 ancilla_qubits.push(ancilla);
721
722 let involved_qubits = vec![
723 logical_qubit.physical_qubits[i * distance + j],
724 logical_qubit.physical_qubits[i * distance + j + 1],
725 ];
726
727 measurements.push(SyndromeMeasurement {
728 measurement_type: SyndromeType::ZStabilizer,
729 qubits: involved_qubits,
730 ancilla,
731 expected_value: 0,
732 });
733 }
734 }
735 }
736
737 Ok(SyndromeCircuit {
738 logical_qubit_id: logical_qubit.id,
739 measurements: measurements.clone(),
740 ancilla_qubits,
741 syndrome_length: measurements.len(),
742 })
743 }
744
745 fn generate_steane_syndrome(
747 &self,
748 logical_qubit: &LogicalQubit,
749 ) -> QuantRS2Result<SyndromeCircuit> {
750 let mut measurements = Vec::new();
751 let ancilla_qubits = vec![7, 8, 9, 10, 11, 12]; let x_stabilizers = [
755 [0, 1, 2, 3], [1, 2, 5, 6], [0, 3, 4, 5], ];
759
760 for (i, stabilizer) in x_stabilizers.iter().enumerate() {
761 measurements.push(SyndromeMeasurement {
762 measurement_type: SyndromeType::XStabilizer,
763 qubits: stabilizer
764 .iter()
765 .map(|&q| logical_qubit.physical_qubits[q])
766 .collect(),
767 ancilla: ancilla_qubits[i],
768 expected_value: 0,
769 });
770 }
771
772 let z_stabilizers = [
774 [0, 1, 4, 6], [1, 3, 4, 5], [0, 2, 3, 6], ];
778
779 for (i, stabilizer) in z_stabilizers.iter().enumerate() {
780 measurements.push(SyndromeMeasurement {
781 measurement_type: SyndromeType::ZStabilizer,
782 qubits: stabilizer
783 .iter()
784 .map(|&q| logical_qubit.physical_qubits[q])
785 .collect(),
786 ancilla: ancilla_qubits[i + 3],
787 expected_value: 0,
788 });
789 }
790
791 Ok(SyndromeCircuit {
792 logical_qubit_id: logical_qubit.id,
793 measurements,
794 ancilla_qubits,
795 syndrome_length: 6,
796 })
797 }
798
799 fn generate_generic_syndrome(
801 &self,
802 logical_qubit: &LogicalQubit,
803 ) -> QuantRS2Result<SyndromeCircuit> {
804 let measurements = vec![SyndromeMeasurement {
805 measurement_type: SyndromeType::ZStabilizer,
806 qubits: logical_qubit.data_qubits(),
807 ancilla: logical_qubit.ancilla_qubits()[0],
808 expected_value: 0,
809 }];
810
811 Ok(SyndromeCircuit {
812 logical_qubit_id: logical_qubit.id,
813 measurements: measurements.clone(),
814 ancilla_qubits: logical_qubit.ancilla_qubits(),
815 syndrome_length: measurements.len(),
816 })
817 }
818}
819
820#[derive(Debug, Clone)]
822pub struct SyndromeCircuit {
823 pub logical_qubit_id: usize,
825 pub measurements: Vec<SyndromeMeasurement>,
827 pub ancilla_qubits: Vec<usize>,
829 pub syndrome_length: usize,
831}
832
833#[derive(Debug, Clone)]
835pub struct FaultTolerantCircuit {
836 pub logical_qubits: Vec<LogicalQubit>,
838 pub ft_gates: Vec<FaultTolerantGate>,
840 pub syndrome_circuits: Vec<SyndromeCircuit>,
842 pub magic_state_requirements: usize,
844 pub physical_qubit_count: usize,
846 pub error_threshold: f64,
848 pub code: QECCode,
850}
851
852impl FaultTolerantCircuit {
853 #[must_use]
855 pub fn execution_time(&self) -> f64 {
856 let gate_time: f64 = self
857 .ft_gates
858 .iter()
859 .flat_map(|gate| &gate.physical_gates)
860 .filter_map(|pg| pg.time)
861 .sum();
862
863 let syndrome_time = self.syndrome_circuits.len() as f64 * 10.0; gate_time + syndrome_time
866 }
867
868 #[must_use]
870 pub fn resource_overhead(&self, logical_gates: usize) -> ResourceOverhead {
871 let space_overhead = self.physical_qubit_count as f64 / self.logical_qubits.len() as f64;
872
873 let physical_gates: usize = self
874 .ft_gates
875 .iter()
876 .map(|gate| gate.physical_gates.len())
877 .sum();
878 let time_overhead = physical_gates as f64 / logical_gates as f64;
879
880 ResourceOverhead {
881 space_overhead,
882 time_overhead,
883 magic_state_overhead: self.magic_state_requirements as f64,
884 }
885 }
886}
887
888#[derive(Debug, Clone)]
890pub struct ResourceOverhead {
891 pub space_overhead: f64,
893 pub time_overhead: f64,
895 pub magic_state_overhead: f64,
897}
898
899#[cfg(test)]
900mod tests {
901 use super::*;
902 use quantrs2_core::gate::multi::CNOT;
903 use quantrs2_core::gate::single::{Hadamard, PauliX};
904
905 #[test]
906 fn test_qec_code_properties() {
907 let surface_code = QECCode::SurfaceCode { distance: 3 };
908 assert_eq!(surface_code.physical_qubits(), 9);
909 assert_eq!(surface_code.distance(), 3);
910 assert!(surface_code.can_correct(1));
911
912 let steane_code = QECCode::SteaneCode;
913 assert_eq!(steane_code.physical_qubits(), 7);
914 assert_eq!(steane_code.distance(), 3);
915 }
916
917 #[test]
918 fn test_logical_qubit_creation() {
919 let code = QECCode::SurfaceCode { distance: 3 };
920 let logical_qubit = LogicalQubit::new(0, code);
921
922 assert_eq!(logical_qubit.id, 0);
923 assert_eq!(logical_qubit.physical_qubits.len(), 9);
924 assert!(!logical_qubit.data_qubits().is_empty());
925 }
926
927 #[test]
928 fn test_ft_compiler_creation() {
929 let code = QECCode::SteaneCode;
930 let compiler = FaultTolerantCompiler::new(code);
931
932 assert!(matches!(compiler.default_code, QECCode::SteaneCode));
933 assert!(compiler.error_threshold > 0.0);
934 }
935
936 #[test]
937 fn test_magic_state_properties() {
938 let t_state = MagicState::TState;
939 assert!(t_state.fidelity_threshold() > 0.9);
940 assert!(t_state.distillation_overhead() > 1.0);
941
942 let custom_state = MagicState::Custom {
943 name: "Test".to_string(),
944 fidelity: 0.98,
945 };
946 assert_eq!(custom_state.fidelity_threshold(), 0.98);
947 }
948
949 #[test]
950 fn test_syndrome_measurement() {
951 let measurement = SyndromeMeasurement {
952 measurement_type: SyndromeType::XStabilizer,
953 qubits: vec![0, 1, 2],
954 ancilla: 3,
955 expected_value: 0,
956 };
957
958 assert_eq!(measurement.qubits.len(), 3);
959 assert_eq!(measurement.measurement_type, SyndromeType::XStabilizer);
960 }
961
962 #[test]
963 fn test_ft_circuit_properties() {
964 let code = QECCode::SteaneCode;
965 let logical_qubits = vec![LogicalQubit::new(0, code.clone())];
966
967 let circuit = FaultTolerantCircuit {
968 logical_qubits,
969 ft_gates: vec![],
970 syndrome_circuits: vec![],
971 magic_state_requirements: 5,
972 physical_qubit_count: 20,
973 error_threshold: 1e-3,
974 code,
975 };
976
977 assert_eq!(circuit.magic_state_requirements, 5);
978 assert_eq!(circuit.physical_qubit_count, 20);
979
980 let overhead = circuit.resource_overhead(10);
981 assert!(overhead.space_overhead > 1.0);
982 }
983
984 #[test]
985 fn test_compilation_options() {
986 let options = CompilationOptions::default();
987 assert!(options.optimize_time);
988 assert!(options.recycle_magic_states);
989 assert_eq!(options.syndrome_frequency, 1);
990 }
991}