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, Mutex};
16use std::time::{Duration, Instant};
17
18#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
20pub enum SimulatorBackend {
21 StateVector {
23 max_qubits: usize,
25 use_gpu: bool,
27 memory_optimization: MemoryOptimization,
29 },
30 Stabilizer {
32 support_magic: bool,
34 use_compression: bool,
36 },
37 MatrixProductState {
39 max_bond_dim: usize,
41 compression_threshold: f64,
43 use_cuda: bool,
45 },
46 DensityMatrix {
48 noise_support: bool,
50 max_size: usize,
52 },
53 TensorNetwork {
55 contraction_strategy: ContractionStrategy,
57 memory_limit: f64,
59 },
60 External {
62 name: String,
64 endpoint: Option<String>,
66 auth_token: Option<String>,
68 },
69}
70
71#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
73pub enum MemoryOptimization {
74 None,
75 Basic,
76 Aggressive,
77 CustomThreshold(f64),
78}
79
80#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
82pub enum ContractionStrategy {
83 Greedy,
84 DynamicProgramming,
85 SimulatedAnnealing,
86 Kahypar,
87 Custom(String),
88}
89
90#[derive(Debug, Clone)]
92pub struct CompilationTarget {
93 pub backend: SimulatorBackend,
95 pub optimization_level: OptimizationLevel,
97 pub instruction_set: InstructionSet,
99 pub parallel_execution: bool,
101 pub batch_size: Option<usize>,
103}
104
105#[derive(Debug, Clone, PartialEq, Eq)]
107pub enum OptimizationLevel {
108 None,
110 Basic,
112 Advanced,
114 Aggressive,
116}
117
118#[derive(Debug, Clone, PartialEq, Eq)]
120pub enum InstructionSet {
121 Universal,
123 Clifford,
125 Native { gates: Vec<String> },
127 Custom {
129 single_qubit: Vec<String>,
130 two_qubit: Vec<String>,
131 multi_qubit: Vec<String>,
132 },
133}
134
135#[derive(Debug, Clone)]
137pub struct CompiledCircuit {
138 pub metadata: CircuitMetadata,
140 pub instructions: Vec<CompiledInstruction>,
142 pub resources: ResourceRequirements,
144 pub stats: CompilationStats,
146 pub backend_data: BackendData,
148}
149
150#[derive(Debug, Clone)]
152pub struct CircuitMetadata {
153 pub num_qubits: usize,
155 pub depth: usize,
157 pub gate_counts: HashMap<String, usize>,
159 pub created_at: std::time::SystemTime,
161 pub target: CompilationTarget,
163}
164
165#[derive(Debug, Clone)]
167pub enum CompiledInstruction {
168 Gate {
170 name: String,
171 qubits: Vec<usize>,
172 parameters: Vec<f64>,
173 id: usize,
175 },
176 Batch {
178 instructions: Vec<Self>,
179 parallel: bool,
180 },
181 Measure { qubit: usize, classical_bit: usize },
183 Conditional {
185 condition: ClassicalCondition,
186 instruction: Box<Self>,
187 },
188 Barrier { qubits: Vec<usize> },
190 Native { opcode: String, operands: Vec<u8> },
192}
193
194#[derive(Debug, Clone)]
196pub struct ClassicalCondition {
197 pub register: String,
198 pub value: u64,
199 pub comparison: ComparisonOp,
200}
201
202#[derive(Debug, Clone, PartialEq, Eq)]
204pub enum ComparisonOp {
205 Equal,
206 NotEqual,
207 Greater,
208 Less,
209 GreaterEqual,
210 LessEqual,
211}
212
213#[derive(Debug, Clone)]
215pub struct ResourceRequirements {
216 pub memory_bytes: usize,
218 pub estimated_time: Duration,
220 pub gpu_memory_bytes: Option<usize>,
222 pub cpu_cores: usize,
224 pub disk_space_bytes: Option<usize>,
226}
227
228#[derive(Debug, Clone)]
230pub struct CompilationStats {
231 pub compilation_time: Duration,
233 pub original_gates: usize,
235 pub compiled_gates: usize,
237 pub optimization_passes: Vec<String>,
239 pub warnings: Vec<String>,
241}
242
243#[derive(Debug, Clone)]
245pub enum BackendData {
246 StateVector {
247 initial_state: Option<Vec<f64>>,
249 measurement_strategy: MeasurementStrategy,
251 },
252 Stabilizer {
253 initial_tableau: Option<Vec<u8>>,
255 },
256 MatrixProductState {
257 tensors: Vec<Vec<f64>>,
259 bond_dims: Vec<usize>,
261 },
262 TensorNetwork {
263 network_topology: String,
265 contraction_order: Vec<usize>,
267 },
268 External {
269 serialized_circuit: String,
271 format: String,
273 },
274}
275
276#[derive(Debug, Clone, PartialEq, Eq)]
278pub enum MeasurementStrategy {
279 EndMeasurement,
281 MidCircuitMeasurement,
283 DeferredMeasurement,
285}
286
287pub struct CircuitCompiler {
289 targets: Vec<CompilationTarget>,
291 optimization_passes: Vec<Box<dyn OptimizationPass>>,
293 cache: Arc<Mutex<HashMap<String, CompiledCircuit>>>,
295 stats_collector: Arc<Mutex<GlobalCompilationStats>>,
297}
298
299#[derive(Debug, Default)]
301pub struct GlobalCompilationStats {
302 pub total_compilations: usize,
303 pub cache_hits: usize,
304 pub average_compilation_time: Duration,
305 pub backend_usage: HashMap<String, usize>,
306}
307
308pub trait OptimizationPass: Send + Sync {
310 fn apply(&self, circuit: &mut CompiledCircuit) -> QuantRS2Result<()>;
312
313 fn name(&self) -> &str;
315
316 fn modifies_structure(&self) -> bool;
318}
319
320pub struct GateFusionPass {
322 pub max_fusion_size: usize,
324 pub fusable_gates: HashSet<String>,
326}
327
328impl OptimizationPass for GateFusionPass {
329 fn apply(&self, circuit: &mut CompiledCircuit) -> QuantRS2Result<()> {
330 let mut optimized_instructions = Vec::new();
331 let mut current_batch = Vec::new();
332
333 for instruction in &circuit.instructions {
334 match instruction {
335 CompiledInstruction::Gate { name, qubits, .. }
336 if self.fusable_gates.contains(name) && qubits.len() == 1 =>
337 {
338 current_batch.push(instruction.clone());
339
340 if current_batch.len() >= self.max_fusion_size {
341 if current_batch.len() > 1 {
342 optimized_instructions.push(CompiledInstruction::Batch {
343 instructions: current_batch,
344 parallel: false,
345 });
346 } else {
347 optimized_instructions.extend(current_batch);
348 }
349 current_batch = Vec::new();
350 }
351 }
352 _ => {
353 if !current_batch.is_empty() {
355 if current_batch.len() > 1 {
356 optimized_instructions.push(CompiledInstruction::Batch {
357 instructions: current_batch,
358 parallel: false,
359 });
360 } else {
361 optimized_instructions.extend(current_batch);
362 }
363 current_batch = Vec::new();
364 }
365 optimized_instructions.push(instruction.clone());
366 }
367 }
368 }
369
370 if !current_batch.is_empty() {
372 if current_batch.len() > 1 {
373 optimized_instructions.push(CompiledInstruction::Batch {
374 instructions: current_batch,
375 parallel: false,
376 });
377 } else {
378 optimized_instructions.extend(current_batch);
379 }
380 }
381
382 circuit.instructions = optimized_instructions;
383 Ok(())
384 }
385
386 fn name(&self) -> &'static str {
387 "GateFusion"
388 }
389
390 fn modifies_structure(&self) -> bool {
391 true
392 }
393}
394
395impl Default for CircuitCompiler {
396 fn default() -> Self {
397 Self::new()
398 }
399}
400
401impl CircuitCompiler {
402 #[must_use]
404 pub fn new() -> Self {
405 Self {
406 targets: Vec::new(),
407 optimization_passes: Vec::new(),
408 cache: Arc::new(Mutex::new(HashMap::new())),
409 stats_collector: Arc::new(Mutex::new(GlobalCompilationStats::default())),
410 }
411 }
412
413 pub fn add_target(&mut self, target: CompilationTarget) {
415 self.targets.push(target);
416 }
417
418 pub fn add_optimization_pass(&mut self, pass: Box<dyn OptimizationPass>) {
420 self.optimization_passes.push(pass);
421 }
422
423 pub fn compile<const N: usize>(&self, circuit: &Circuit<N>) -> QuantRS2Result<CompiledCircuit> {
425 let start_time = Instant::now();
426
427 let cache_key = self.generate_cache_key(circuit);
429
430 if let Ok(cache) = self.cache.lock() {
432 if let Some(cached) = cache.get(&cache_key) {
433 self.update_stats(true, start_time.elapsed());
434 return Ok(cached.clone());
435 }
436 }
437
438 let target = self.select_target(circuit)?;
440
441 let mut compiled = self.compile_for_target(circuit, &target)?;
443
444 for pass in &self.optimization_passes {
446 if target.optimization_level != OptimizationLevel::None {
447 pass.apply(&mut compiled)?;
448 }
449 }
450
451 compiled.stats.compilation_time = start_time.elapsed();
453
454 if let Ok(mut cache) = self.cache.lock() {
456 cache.insert(cache_key, compiled.clone());
457 }
458
459 self.update_stats(false, start_time.elapsed());
460 Ok(compiled)
461 }
462
463 pub fn compile_for_target<const N: usize>(
465 &self,
466 circuit: &Circuit<N>,
467 target: &CompilationTarget,
468 ) -> QuantRS2Result<CompiledCircuit> {
469 let metadata = self.generate_metadata(circuit, target);
470 let instructions = self.compile_instructions(circuit, target)?;
471 let resources = self.estimate_resources(&instructions, target);
472 let backend_data = self.generate_backend_data(circuit, target)?;
473
474 let stats = CompilationStats {
475 compilation_time: Duration::from_millis(0), original_gates: circuit.gates().len(),
477 compiled_gates: instructions.len(),
478 optimization_passes: Vec::new(),
479 warnings: Vec::new(),
480 };
481
482 Ok(CompiledCircuit {
483 metadata,
484 instructions,
485 resources,
486 stats,
487 backend_data,
488 })
489 }
490
491 fn select_target<const N: usize>(
499 &self,
500 _circuit: &Circuit<N>,
501 ) -> QuantRS2Result<CompilationTarget> {
502 self.targets.first().cloned().ok_or_else(|| {
503 QuantRS2Error::InvalidInput("No compilation targets available".to_string())
504 })
505 }
506
507 fn compile_instructions<const N: usize>(
509 &self,
510 circuit: &Circuit<N>,
511 target: &CompilationTarget,
512 ) -> QuantRS2Result<Vec<CompiledInstruction>> {
513 let mut instructions = Vec::new();
514 let mut instruction_id = 0;
515
516 for gate in circuit.gates() {
517 let compiled_gate = self.compile_gate(gate.as_ref(), target, instruction_id)?;
518 instructions.push(compiled_gate);
519 instruction_id += 1;
520 }
521
522 if let Some(batch_size) = target.batch_size {
524 instructions = self.apply_batching(instructions, batch_size);
525 }
526
527 Ok(instructions)
528 }
529
530 fn compile_gate(
532 &self,
533 gate: &dyn GateOp,
534 target: &CompilationTarget,
535 id: usize,
536 ) -> QuantRS2Result<CompiledInstruction> {
537 let name = gate.name().to_string();
538 let qubits: Vec<usize> = gate.qubits().iter().map(|q| q.id() as usize).collect();
539 let parameters = self.extract_gate_parameters(gate);
540
541 if !self.is_gate_supported(&name, &target.instruction_set) {
543 return Err(QuantRS2Error::InvalidInput(format!(
544 "Gate {name} not supported by instruction set"
545 )));
546 }
547
548 Ok(CompiledInstruction::Gate {
549 name,
550 qubits,
551 parameters,
552 id,
553 })
554 }
555
556 fn extract_gate_parameters(&self, gate: &dyn GateOp) -> Vec<f64> {
565 use quantrs2_core::gate::{global, multi, single};
566
567 if !gate.is_parameterized() {
569 return Vec::new();
570 }
571
572 let any = gate.as_any();
573
574 if let Some(g) = any.downcast_ref::<single::RotationX>() {
576 return vec![g.theta];
577 }
578 if let Some(g) = any.downcast_ref::<single::RotationY>() {
579 return vec![g.theta];
580 }
581 if let Some(g) = any.downcast_ref::<single::RotationZ>() {
582 return vec![g.theta];
583 }
584
585 if let Some(g) = any.downcast_ref::<multi::CRX>() {
587 return vec![g.theta];
588 }
589 if let Some(g) = any.downcast_ref::<multi::CRY>() {
590 return vec![g.theta];
591 }
592 if let Some(g) = any.downcast_ref::<multi::CRZ>() {
593 return vec![g.theta];
594 }
595 if let Some(g) = any.downcast_ref::<multi::RXX>() {
596 return vec![g.theta];
597 }
598 if let Some(g) = any.downcast_ref::<multi::RYY>() {
599 return vec![g.theta];
600 }
601 if let Some(g) = any.downcast_ref::<multi::RZZ>() {
602 return vec![g.theta];
603 }
604 if let Some(g) = any.downcast_ref::<multi::RZX>() {
605 return vec![g.theta];
606 }
607
608 if let Some(g) = any.downcast_ref::<multi::XXPlusYY>() {
610 return vec![g.theta, g.beta];
611 }
612 if let Some(g) = any.downcast_ref::<multi::XXMinusYY>() {
613 return vec![g.theta, g.beta];
614 }
615 if let Some(g) = any.downcast_ref::<global::RGate>() {
616 return vec![g.theta, g.phi];
617 }
618
619 if let Some(g) = any.downcast_ref::<global::GlobalPhase>() {
621 return vec![g.phase];
622 }
623
624 Vec::new()
627 }
628
629 fn is_gate_supported(&self, gate_name: &str, instruction_set: &InstructionSet) -> bool {
631 match instruction_set {
632 InstructionSet::Universal => true,
633 InstructionSet::Clifford => {
634 matches!(gate_name, "H" | "S" | "CNOT" | "X" | "Y" | "Z")
635 }
636 InstructionSet::Native { gates } => gates.contains(&gate_name.to_string()),
637 InstructionSet::Custom {
638 single_qubit,
639 two_qubit,
640 multi_qubit,
641 } => {
642 single_qubit.contains(&gate_name.to_string())
643 || two_qubit.contains(&gate_name.to_string())
644 || multi_qubit.contains(&gate_name.to_string())
645 }
646 }
647 }
648
649 fn apply_batching(
651 &self,
652 instructions: Vec<CompiledInstruction>,
653 batch_size: usize,
654 ) -> Vec<CompiledInstruction> {
655 let mut batched = Vec::new();
656 let mut current_batch = Vec::new();
657
658 for instruction in instructions {
659 current_batch.push(instruction);
660
661 if current_batch.len() >= batch_size {
662 batched.push(CompiledInstruction::Batch {
663 instructions: current_batch,
664 parallel: true,
665 });
666 current_batch = Vec::new();
667 }
668 }
669
670 if !current_batch.is_empty() {
672 if current_batch.len() == 1 {
673 batched.extend(current_batch);
674 } else {
675 batched.push(CompiledInstruction::Batch {
676 instructions: current_batch,
677 parallel: true,
678 });
679 }
680 }
681
682 batched
683 }
684
685 fn generate_metadata<const N: usize>(
687 &self,
688 circuit: &Circuit<N>,
689 target: &CompilationTarget,
690 ) -> CircuitMetadata {
691 let mut gate_counts = HashMap::new();
692 for gate in circuit.gates() {
693 *gate_counts.entry(gate.name().to_string()).or_insert(0) += 1;
694 }
695
696 CircuitMetadata {
697 num_qubits: N,
698 depth: circuit.gates().len(), gate_counts,
700 created_at: std::time::SystemTime::now(),
701 target: target.clone(),
702 }
703 }
704
705 fn estimate_resources(
707 &self,
708 instructions: &[CompiledInstruction],
709 target: &CompilationTarget,
710 ) -> ResourceRequirements {
711 let instruction_count = instructions.len();
712
713 let (memory_bytes, estimated_time, gpu_memory) = match &target.backend {
715 SimulatorBackend::StateVector {
716 max_qubits,
717 use_gpu,
718 ..
719 } => {
720 let memory = if *max_qubits <= 30 {
721 (1usize << max_qubits) * 16 } else {
723 usize::MAX };
725 let time = Duration::from_millis(instruction_count as u64);
726 let gpu_mem = if *use_gpu { Some(memory) } else { None };
727 (memory, time, gpu_mem)
728 }
729 SimulatorBackend::Stabilizer { .. } => {
730 let memory = instruction_count * instruction_count * 8;
732 let time = Duration::from_millis(instruction_count as u64 / 10);
733 (memory, time, None)
734 }
735 SimulatorBackend::MatrixProductState { max_bond_dim, .. } => {
736 let memory = instruction_count * max_bond_dim * max_bond_dim * 16;
737 let time = Duration::from_millis(instruction_count as u64 * 2);
738 (memory, time, None)
739 }
740 _ => {
741 let memory = instruction_count * 1024;
743 let time = Duration::from_millis(instruction_count as u64);
744 (memory, time, None)
745 }
746 };
747
748 ResourceRequirements {
749 memory_bytes,
750 estimated_time,
751 gpu_memory_bytes: gpu_memory,
752 cpu_cores: 1,
753 disk_space_bytes: None,
754 }
755 }
756
757 fn generate_backend_data<const N: usize>(
759 &self,
760 circuit: &Circuit<N>,
761 target: &CompilationTarget,
762 ) -> QuantRS2Result<BackendData> {
763 match &target.backend {
764 SimulatorBackend::StateVector { .. } => Ok(BackendData::StateVector {
765 initial_state: None,
766 measurement_strategy: MeasurementStrategy::EndMeasurement,
767 }),
768 SimulatorBackend::Stabilizer { .. } => Ok(BackendData::Stabilizer {
769 initial_tableau: None,
770 }),
771 SimulatorBackend::MatrixProductState { max_bond_dim, .. } => {
772 Ok(BackendData::MatrixProductState {
773 tensors: Vec::new(),
774 bond_dims: vec![1; N + 1],
775 })
776 }
777 SimulatorBackend::TensorNetwork { .. } => Ok(BackendData::TensorNetwork {
778 network_topology: "linear".to_string(),
779 contraction_order: (0..N).collect(),
780 }),
781 SimulatorBackend::External { name, .. } => Ok(BackendData::External {
782 serialized_circuit: format!("circuit_for_{name}"),
783 format: "qasm".to_string(),
784 }),
785 SimulatorBackend::DensityMatrix { .. } => Ok(BackendData::StateVector {
786 initial_state: None,
787 measurement_strategy: MeasurementStrategy::EndMeasurement,
788 }),
789 }
790 }
791
792 fn generate_cache_key<const N: usize>(&self, circuit: &Circuit<N>) -> String {
794 use std::collections::hash_map::DefaultHasher;
795 use std::hash::{Hash, Hasher};
796
797 let mut hasher = DefaultHasher::new();
798
799 N.hash(&mut hasher);
801 circuit.gates().len().hash(&mut hasher);
802
803 for gate in circuit.gates() {
805 gate.name().hash(&mut hasher);
806 for qubit in gate.qubits() {
807 qubit.id().hash(&mut hasher);
808 }
809 }
810
811 format!("{:x}", hasher.finish())
812 }
813
814 fn update_stats(&self, cache_hit: bool, compilation_time: Duration) {
816 if let Ok(mut stats) = self.stats_collector.lock() {
817 stats.total_compilations += 1;
818 if cache_hit {
819 stats.cache_hits += 1;
820 }
821
822 let total_time =
824 stats.average_compilation_time.as_nanos() * (stats.total_compilations - 1) as u128;
825 let new_total = total_time + compilation_time.as_nanos();
826 stats.average_compilation_time =
827 Duration::from_nanos((new_total / stats.total_compilations as u128) as u64);
828 }
829 }
830
831 #[must_use]
833 pub fn get_stats(&self) -> GlobalCompilationStats {
834 self.stats_collector
835 .lock()
836 .map(|stats| GlobalCompilationStats {
837 total_compilations: stats.total_compilations,
838 cache_hits: stats.cache_hits,
839 average_compilation_time: stats.average_compilation_time,
840 backend_usage: stats.backend_usage.clone(),
841 })
842 .unwrap_or_default()
843 }
844
845 pub fn clear_cache(&self) {
847 if let Ok(mut cache) = self.cache.lock() {
848 cache.clear();
849 }
850 }
851}
852
853pub struct CircuitExecutor {
855 backends: HashMap<String, Box<dyn SimulatorExecutor>>,
857}
858
859pub trait SimulatorExecutor: Send + Sync {
861 fn execute(&self, circuit: &CompiledCircuit) -> QuantRS2Result<ExecutionResult>;
863
864 fn name(&self) -> &str;
866
867 fn is_compatible(&self, circuit: &CompiledCircuit) -> bool;
869}
870
871#[derive(Debug, Clone)]
873pub struct ExecutionResult {
874 pub measurements: HashMap<usize, Vec<u8>>,
876 pub final_state: Option<Vec<f64>>,
878 pub execution_stats: ExecutionStats,
880 pub backend_results: HashMap<String, String>,
882}
883
884#[derive(Debug, Clone)]
886pub struct ExecutionStats {
887 pub execution_time: Duration,
889 pub memory_used: usize,
891 pub shots: usize,
893 pub success_rate: f64,
895}
896
897#[cfg(test)]
898mod tests {
899 use super::*;
900 use quantrs2_core::gate::multi::CNOT;
901 use quantrs2_core::gate::single::Hadamard;
902
903 #[test]
904 fn test_compiler_creation() {
905 let compiler = CircuitCompiler::new();
906 assert_eq!(compiler.targets.len(), 0);
907 }
908
909 #[test]
910 fn test_compilation_target() {
911 let target = CompilationTarget {
912 backend: SimulatorBackend::StateVector {
913 max_qubits: 20,
914 use_gpu: false,
915 memory_optimization: MemoryOptimization::Basic,
916 },
917 optimization_level: OptimizationLevel::Basic,
918 instruction_set: InstructionSet::Universal,
919 parallel_execution: true,
920 batch_size: Some(10),
921 };
922
923 assert!(matches!(
924 target.backend,
925 SimulatorBackend::StateVector { .. }
926 ));
927 }
928
929 #[test]
930 fn test_gate_support_checking() {
931 let compiler = CircuitCompiler::new();
932
933 assert!(compiler.is_gate_supported("H", &InstructionSet::Universal));
935 assert!(compiler.is_gate_supported("CNOT", &InstructionSet::Universal));
936
937 assert!(compiler.is_gate_supported("H", &InstructionSet::Clifford));
939 assert!(!compiler.is_gate_supported("T", &InstructionSet::Clifford));
940 }
941
942 #[test]
943 fn test_extract_gate_parameters() {
944 use quantrs2_core::gate::single::RotationX;
945
946 let compiler = CircuitCompiler::new();
947
948 let rx = RotationX {
950 target: QubitId(0),
951 theta: 0.75,
952 };
953 let params = compiler.extract_gate_parameters(&rx);
954 assert_eq!(params.len(), 1);
955 assert!((params[0] - 0.75).abs() < 1e-12);
956
957 let h = Hadamard { target: QubitId(0) };
959 assert!(compiler.extract_gate_parameters(&h).is_empty());
960 }
961
962 #[test]
963 fn test_resource_estimation() {
964 let compiler = CircuitCompiler::new();
965 let instructions = vec![
966 CompiledInstruction::Gate {
967 name: "H".to_string(),
968 qubits: vec![0],
969 parameters: vec![],
970 id: 0,
971 },
972 CompiledInstruction::Gate {
973 name: "CNOT".to_string(),
974 qubits: vec![0, 1],
975 parameters: vec![],
976 id: 1,
977 },
978 ];
979
980 let target = CompilationTarget {
981 backend: SimulatorBackend::StateVector {
982 max_qubits: 10,
983 use_gpu: false,
984 memory_optimization: MemoryOptimization::None,
985 },
986 optimization_level: OptimizationLevel::None,
987 instruction_set: InstructionSet::Universal,
988 parallel_execution: false,
989 batch_size: None,
990 };
991
992 let resources = compiler.estimate_resources(&instructions, &target);
993 assert!(resources.memory_bytes > 0);
994 assert!(resources.estimated_time > Duration::from_millis(0));
995 }
996
997 #[test]
998 fn test_cache_key_generation() {
999 let compiler = CircuitCompiler::new();
1000
1001 let mut circuit1 = Circuit::<2>::new();
1002 circuit1
1003 .add_gate(Hadamard { target: QubitId(0) })
1004 .expect("add H gate to circuit1");
1005
1006 let mut circuit2 = Circuit::<2>::new();
1007 circuit2
1008 .add_gate(Hadamard { target: QubitId(0) })
1009 .expect("add H gate to circuit2");
1010
1011 let key1 = compiler.generate_cache_key(&circuit1);
1012 let key2 = compiler.generate_cache_key(&circuit2);
1013
1014 assert_eq!(key1, key2); }
1016
1017 #[test]
1018 fn test_gate_fusion_pass() {
1019 let mut fusable_gates = HashSet::new();
1020 fusable_gates.insert("H".to_string());
1021 fusable_gates.insert("X".to_string());
1022
1023 let pass = GateFusionPass {
1024 max_fusion_size: 3,
1025 fusable_gates,
1026 };
1027
1028 assert_eq!(pass.name(), "GateFusion");
1029 assert!(pass.modifies_structure());
1030 }
1031}