quantrs2_circuit/
simulator_interface.rs

1//! Efficient circuit-to-simulator interfaces
2//!
3//! This module provides optimized interfaces for converting quantum circuits
4//! to various simulator formats, with support for batching, compilation,
5//! and execution across different quantum simulation backends.
6
7use 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/// Simulator backend types
19#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
20pub enum SimulatorBackend {
21    /// State vector simulator
22    StateVector {
23        /// Maximum number of qubits
24        max_qubits: usize,
25        /// Use GPU acceleration
26        use_gpu: bool,
27        /// Memory optimization level
28        memory_optimization: MemoryOptimization,
29    },
30    /// Stabilizer tableau simulator
31    Stabilizer {
32        /// Support for magic states
33        support_magic: bool,
34        /// Tableau compression
35        use_compression: bool,
36    },
37    /// Matrix Product State simulator
38    MatrixProductState {
39        /// Maximum bond dimension
40        max_bond_dim: usize,
41        /// Compression threshold
42        compression_threshold: f64,
43        /// Use CUDA for GPU acceleration
44        use_cuda: bool,
45    },
46    /// Density matrix simulator
47    DensityMatrix {
48        /// Noise model support
49        noise_support: bool,
50        /// Maximum density matrix size
51        max_size: usize,
52    },
53    /// Tensor network simulator
54    TensorNetwork {
55        /// Contraction strategy
56        contraction_strategy: ContractionStrategy,
57        /// Memory limit in GB
58        memory_limit: f64,
59    },
60    /// External simulator (via API)
61    External {
62        /// Simulator name/identifier
63        name: String,
64        /// API endpoint
65        endpoint: Option<String>,
66        /// Authentication token
67        auth_token: Option<String>,
68    },
69}
70
71/// Memory optimization strategies
72#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
73pub enum MemoryOptimization {
74    None,
75    Basic,
76    Aggressive,
77    CustomThreshold(f64),
78}
79
80/// Tensor contraction strategies
81#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
82pub enum ContractionStrategy {
83    Greedy,
84    DynamicProgramming,
85    SimulatedAnnealing,
86    Kahypar,
87    Custom(String),
88}
89
90/// Compilation target for circuits
91#[derive(Debug, Clone)]
92pub struct CompilationTarget {
93    /// Target backend
94    pub backend: SimulatorBackend,
95    /// Optimization level
96    pub optimization_level: OptimizationLevel,
97    /// Target instruction set
98    pub instruction_set: InstructionSet,
99    /// Enable parallel execution
100    pub parallel_execution: bool,
101    /// Batch size for gate operations
102    pub batch_size: Option<usize>,
103}
104
105/// Circuit optimization levels for compilation
106#[derive(Debug, Clone, PartialEq)]
107pub enum OptimizationLevel {
108    /// No optimization
109    None,
110    /// Basic gate fusion and cancellation
111    Basic,
112    /// Advanced optimization with reordering
113    Advanced,
114    /// Aggressive optimization with synthesis
115    Aggressive,
116}
117
118/// Supported instruction sets
119#[derive(Debug, Clone, PartialEq)]
120pub enum InstructionSet {
121    /// Universal gate set
122    Universal,
123    /// Clifford gates only
124    Clifford,
125    /// Native gate set for specific hardware
126    Native { gates: Vec<String> },
127    /// Custom instruction set
128    Custom {
129        single_qubit: Vec<String>,
130        two_qubit: Vec<String>,
131        multi_qubit: Vec<String>,
132    },
133}
134
135/// Compiled circuit representation
136#[derive(Debug, Clone)]
137pub struct CompiledCircuit {
138    /// Original circuit metadata
139    pub metadata: CircuitMetadata,
140    /// Compiled instructions
141    pub instructions: Vec<CompiledInstruction>,
142    /// Resource requirements
143    pub resources: ResourceRequirements,
144    /// Compilation statistics
145    pub stats: CompilationStats,
146    /// Backend-specific data
147    pub backend_data: BackendData,
148}
149
150/// Circuit metadata
151#[derive(Debug, Clone)]
152pub struct CircuitMetadata {
153    /// Number of qubits
154    pub num_qubits: usize,
155    /// Circuit depth
156    pub depth: usize,
157    /// Gate count by type
158    pub gate_counts: HashMap<String, usize>,
159    /// Creation timestamp
160    pub created_at: std::time::SystemTime,
161    /// Compilation target
162    pub target: CompilationTarget,
163}
164
165/// Compiled instruction
166#[derive(Debug, Clone)]
167pub enum CompiledInstruction {
168    /// Single gate operation
169    Gate {
170        name: String,
171        qubits: Vec<usize>,
172        parameters: Vec<f64>,
173        /// Instruction ID for debugging
174        id: usize,
175    },
176    /// Batched operations
177    Batch {
178        instructions: Vec<CompiledInstruction>,
179        parallel: bool,
180    },
181    /// Measurement
182    Measure { qubit: usize, classical_bit: usize },
183    /// Conditional operation
184    Conditional {
185        condition: ClassicalCondition,
186        instruction: Box<CompiledInstruction>,
187    },
188    /// Barrier/synchronization
189    Barrier { qubits: Vec<usize> },
190    /// Backend-specific instruction
191    Native { opcode: String, operands: Vec<u8> },
192}
193
194/// Classical condition for conditional operations
195#[derive(Debug, Clone)]
196pub struct ClassicalCondition {
197    pub register: String,
198    pub value: u64,
199    pub comparison: ComparisonOp,
200}
201
202/// Comparison operators for classical conditions
203#[derive(Debug, Clone, PartialEq)]
204pub enum ComparisonOp {
205    Equal,
206    NotEqual,
207    Greater,
208    Less,
209    GreaterEqual,
210    LessEqual,
211}
212
213/// Resource requirements for execution
214#[derive(Debug, Clone)]
215pub struct ResourceRequirements {
216    /// Memory requirement in bytes
217    pub memory_bytes: usize,
218    /// Estimated execution time
219    pub estimated_time: Duration,
220    /// GPU memory requirement
221    pub gpu_memory_bytes: Option<usize>,
222    /// CPU cores recommended
223    pub cpu_cores: usize,
224    /// Disk space for intermediate results
225    pub disk_space_bytes: Option<usize>,
226}
227
228/// Compilation statistics
229#[derive(Debug, Clone)]
230pub struct CompilationStats {
231    /// Time taken to compile
232    pub compilation_time: Duration,
233    /// Original gate count
234    pub original_gates: usize,
235    /// Compiled gate count
236    pub compiled_gates: usize,
237    /// Optimization passes applied
238    pub optimization_passes: Vec<String>,
239    /// Warnings encountered
240    pub warnings: Vec<String>,
241}
242
243/// Backend-specific data
244#[derive(Debug, Clone)]
245pub enum BackendData {
246    StateVector {
247        /// Initial state preparation
248        initial_state: Option<Vec<f64>>,
249        /// Measurement strategy
250        measurement_strategy: MeasurementStrategy,
251    },
252    Stabilizer {
253        /// Initial tableau
254        initial_tableau: Option<Vec<u8>>,
255    },
256    MatrixProductState {
257        /// MPS representation
258        tensors: Vec<Vec<f64>>,
259        /// Bond dimensions
260        bond_dims: Vec<usize>,
261    },
262    TensorNetwork {
263        /// Network topology
264        network_topology: String,
265        /// Contraction order
266        contraction_order: Vec<usize>,
267    },
268    External {
269        /// Serialized circuit format
270        serialized_circuit: String,
271        /// Format specification
272        format: String,
273    },
274}
275
276/// Measurement strategies
277#[derive(Debug, Clone, PartialEq)]
278pub enum MeasurementStrategy {
279    /// Measure all qubits at the end
280    EndMeasurement,
281    /// Support mid-circuit measurements
282    MidCircuitMeasurement,
283    /// Deferred measurement
284    DeferredMeasurement,
285}
286
287/// Circuit compiler for different backends
288pub struct CircuitCompiler {
289    /// Compilation targets by priority
290    targets: Vec<CompilationTarget>,
291    /// Optimization passes
292    optimization_passes: Vec<Box<dyn OptimizationPass>>,
293    /// Compilation cache
294    cache: Arc<Mutex<HashMap<String, CompiledCircuit>>>,
295    /// Statistics collector
296    stats_collector: Arc<Mutex<GlobalCompilationStats>>,
297}
298
299/// Global compilation statistics
300#[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
308/// Optimization pass trait
309pub trait OptimizationPass: Send + Sync {
310    /// Apply optimization to compiled circuit
311    fn apply(&self, circuit: &mut CompiledCircuit) -> QuantRS2Result<()>;
312
313    /// Pass name
314    fn name(&self) -> &str;
315
316    /// Whether this pass modifies the circuit structure
317    fn modifies_structure(&self) -> bool;
318}
319
320/// Gate fusion optimization pass
321pub struct GateFusionPass {
322    /// Maximum gates to fuse
323    pub max_fusion_size: usize,
324    /// Supported gate types for fusion
325    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                    // Flush current batch
354                    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        // Flush remaining batch
371        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) -> &str {
387        "GateFusion"
388    }
389
390    fn modifies_structure(&self) -> bool {
391        true
392    }
393}
394
395impl CircuitCompiler {
396    /// Create a new circuit compiler
397    pub fn new() -> Self {
398        Self {
399            targets: Vec::new(),
400            optimization_passes: Vec::new(),
401            cache: Arc::new(Mutex::new(HashMap::new())),
402            stats_collector: Arc::new(Mutex::new(GlobalCompilationStats::default())),
403        }
404    }
405
406    /// Add a compilation target
407    pub fn add_target(&mut self, target: CompilationTarget) {
408        self.targets.push(target);
409    }
410
411    /// Add an optimization pass
412    pub fn add_optimization_pass(&mut self, pass: Box<dyn OptimizationPass>) {
413        self.optimization_passes.push(pass);
414    }
415
416    /// Compile circuit for the best available target
417    pub fn compile<const N: usize>(&self, circuit: &Circuit<N>) -> QuantRS2Result<CompiledCircuit> {
418        let start_time = Instant::now();
419
420        // Generate cache key
421        let cache_key = self.generate_cache_key(circuit);
422
423        // Check cache
424        if let Ok(cache) = self.cache.lock() {
425            if let Some(cached) = cache.get(&cache_key) {
426                self.update_stats(true, start_time.elapsed());
427                return Ok(cached.clone());
428            }
429        }
430
431        // Select best target
432        let target = self.select_target(circuit)?;
433
434        // Compile circuit
435        let mut compiled = self.compile_for_target(circuit, &target)?;
436
437        // Apply optimization passes
438        for pass in &self.optimization_passes {
439            if target.optimization_level != OptimizationLevel::None {
440                pass.apply(&mut compiled)?;
441            }
442        }
443
444        // Update statistics
445        compiled.stats.compilation_time = start_time.elapsed();
446
447        // Cache result
448        if let Ok(mut cache) = self.cache.lock() {
449            cache.insert(cache_key, compiled.clone());
450        }
451
452        self.update_stats(false, start_time.elapsed());
453        Ok(compiled)
454    }
455
456    /// Compile circuit for specific target
457    pub fn compile_for_target<const N: usize>(
458        &self,
459        circuit: &Circuit<N>,
460        target: &CompilationTarget,
461    ) -> QuantRS2Result<CompiledCircuit> {
462        let metadata = self.generate_metadata(circuit, target);
463        let instructions = self.compile_instructions(circuit, target)?;
464        let resources = self.estimate_resources(&instructions, target);
465        let backend_data = self.generate_backend_data(circuit, target)?;
466
467        let stats = CompilationStats {
468            compilation_time: Duration::from_millis(0), // Will be updated later
469            original_gates: circuit.gates().len(),
470            compiled_gates: instructions.len(),
471            optimization_passes: Vec::new(),
472            warnings: Vec::new(),
473        };
474
475        Ok(CompiledCircuit {
476            metadata,
477            instructions,
478            resources,
479            stats,
480            backend_data,
481        })
482    }
483
484    /// Select the best compilation target for a circuit
485    fn select_target<const N: usize>(
486        &self,
487        circuit: &Circuit<N>,
488    ) -> QuantRS2Result<CompilationTarget> {
489        if self.targets.is_empty() {
490            return Err(QuantRS2Error::InvalidInput(
491                "No compilation targets available".to_string(),
492            ));
493        }
494
495        // For now, select the first target
496        // In a more sophisticated implementation, this would analyze the circuit
497        // and select the most appropriate target based on various factors
498        Ok(self.targets[0].clone())
499    }
500
501    /// Compile circuit instructions
502    fn compile_instructions<const N: usize>(
503        &self,
504        circuit: &Circuit<N>,
505        target: &CompilationTarget,
506    ) -> QuantRS2Result<Vec<CompiledInstruction>> {
507        let mut instructions = Vec::new();
508        let mut instruction_id = 0;
509
510        for gate in circuit.gates() {
511            let compiled_gate = self.compile_gate(gate.as_ref(), target, instruction_id)?;
512            instructions.push(compiled_gate);
513            instruction_id += 1;
514        }
515
516        // Apply batching if enabled
517        if let Some(batch_size) = target.batch_size {
518            instructions = self.apply_batching(instructions, batch_size);
519        }
520
521        Ok(instructions)
522    }
523
524    /// Compile a single gate
525    fn compile_gate(
526        &self,
527        gate: &dyn GateOp,
528        target: &CompilationTarget,
529        id: usize,
530    ) -> QuantRS2Result<CompiledInstruction> {
531        let name = gate.name().to_string();
532        let qubits: Vec<usize> = gate.qubits().iter().map(|q| q.id() as usize).collect();
533        let parameters = self.extract_gate_parameters(gate);
534
535        // Check if gate is supported by instruction set
536        if !self.is_gate_supported(&name, &target.instruction_set) {
537            return Err(QuantRS2Error::InvalidInput(format!(
538                "Gate {} not supported by instruction set",
539                name
540            )));
541        }
542
543        Ok(CompiledInstruction::Gate {
544            name,
545            qubits,
546            parameters,
547            id,
548        })
549    }
550
551    /// Extract parameters from a gate
552    fn extract_gate_parameters(&self, gate: &dyn GateOp) -> Vec<f64> {
553        // This would need to access gate-specific parameter methods
554        // For now, return empty parameters
555        Vec::new()
556    }
557
558    /// Check if gate is supported by instruction set
559    fn is_gate_supported(&self, gate_name: &str, instruction_set: &InstructionSet) -> bool {
560        match instruction_set {
561            InstructionSet::Universal => true,
562            InstructionSet::Clifford => {
563                matches!(gate_name, "H" | "S" | "CNOT" | "X" | "Y" | "Z")
564            }
565            InstructionSet::Native { gates } => gates.contains(&gate_name.to_string()),
566            InstructionSet::Custom {
567                single_qubit,
568                two_qubit,
569                multi_qubit,
570            } => {
571                single_qubit.contains(&gate_name.to_string())
572                    || two_qubit.contains(&gate_name.to_string())
573                    || multi_qubit.contains(&gate_name.to_string())
574            }
575        }
576    }
577
578    /// Apply batching to instructions
579    fn apply_batching(
580        &self,
581        instructions: Vec<CompiledInstruction>,
582        batch_size: usize,
583    ) -> Vec<CompiledInstruction> {
584        let mut batched = Vec::new();
585        let mut current_batch = Vec::new();
586
587        for instruction in instructions {
588            current_batch.push(instruction);
589
590            if current_batch.len() >= batch_size {
591                batched.push(CompiledInstruction::Batch {
592                    instructions: current_batch,
593                    parallel: true,
594                });
595                current_batch = Vec::new();
596            }
597        }
598
599        // Add remaining instructions
600        if !current_batch.is_empty() {
601            if current_batch.len() == 1 {
602                batched.extend(current_batch);
603            } else {
604                batched.push(CompiledInstruction::Batch {
605                    instructions: current_batch,
606                    parallel: true,
607                });
608            }
609        }
610
611        batched
612    }
613
614    /// Generate circuit metadata
615    fn generate_metadata<const N: usize>(
616        &self,
617        circuit: &Circuit<N>,
618        target: &CompilationTarget,
619    ) -> CircuitMetadata {
620        let mut gate_counts = HashMap::new();
621        for gate in circuit.gates() {
622            *gate_counts.entry(gate.name().to_string()).or_insert(0) += 1;
623        }
624
625        CircuitMetadata {
626            num_qubits: N,
627            depth: circuit.gates().len(), // Simplified depth calculation
628            gate_counts,
629            created_at: std::time::SystemTime::now(),
630            target: target.clone(),
631        }
632    }
633
634    /// Estimate resource requirements
635    fn estimate_resources(
636        &self,
637        instructions: &[CompiledInstruction],
638        target: &CompilationTarget,
639    ) -> ResourceRequirements {
640        let instruction_count = instructions.len();
641
642        // Simple estimation based on backend type
643        let (memory_bytes, estimated_time, gpu_memory) = match &target.backend {
644            SimulatorBackend::StateVector {
645                max_qubits,
646                use_gpu,
647                ..
648            } => {
649                let memory = if *max_qubits <= 30 {
650                    (1usize << max_qubits) * 16 // 16 bytes per complex number
651                } else {
652                    usize::MAX // Too large
653                };
654                let time = Duration::from_millis(instruction_count as u64);
655                let gpu_mem = if *use_gpu { Some(memory) } else { None };
656                (memory, time, gpu_mem)
657            }
658            SimulatorBackend::Stabilizer { .. } => {
659                // Stabilizer tableau grows quadratically
660                let memory = instruction_count * instruction_count * 8;
661                let time = Duration::from_millis(instruction_count as u64 / 10);
662                (memory, time, None)
663            }
664            SimulatorBackend::MatrixProductState { max_bond_dim, .. } => {
665                let memory = instruction_count * max_bond_dim * max_bond_dim * 16;
666                let time = Duration::from_millis(instruction_count as u64 * 2);
667                (memory, time, None)
668            }
669            _ => {
670                // Default estimates
671                let memory = instruction_count * 1024;
672                let time = Duration::from_millis(instruction_count as u64);
673                (memory, time, None)
674            }
675        };
676
677        ResourceRequirements {
678            memory_bytes,
679            estimated_time,
680            gpu_memory_bytes: gpu_memory,
681            cpu_cores: 1,
682            disk_space_bytes: None,
683        }
684    }
685
686    /// Generate backend-specific data
687    fn generate_backend_data<const N: usize>(
688        &self,
689        circuit: &Circuit<N>,
690        target: &CompilationTarget,
691    ) -> QuantRS2Result<BackendData> {
692        match &target.backend {
693            SimulatorBackend::StateVector { .. } => Ok(BackendData::StateVector {
694                initial_state: None,
695                measurement_strategy: MeasurementStrategy::EndMeasurement,
696            }),
697            SimulatorBackend::Stabilizer { .. } => Ok(BackendData::Stabilizer {
698                initial_tableau: None,
699            }),
700            SimulatorBackend::MatrixProductState { max_bond_dim, .. } => {
701                Ok(BackendData::MatrixProductState {
702                    tensors: Vec::new(),
703                    bond_dims: vec![1; N + 1],
704                })
705            }
706            SimulatorBackend::TensorNetwork { .. } => Ok(BackendData::TensorNetwork {
707                network_topology: "linear".to_string(),
708                contraction_order: (0..N).collect(),
709            }),
710            SimulatorBackend::External { name, .. } => Ok(BackendData::External {
711                serialized_circuit: format!("circuit_for_{}", name),
712                format: "qasm".to_string(),
713            }),
714            _ => Ok(BackendData::StateVector {
715                initial_state: None,
716                measurement_strategy: MeasurementStrategy::EndMeasurement,
717            }),
718        }
719    }
720
721    /// Generate cache key for circuit
722    fn generate_cache_key<const N: usize>(&self, circuit: &Circuit<N>) -> String {
723        use std::collections::hash_map::DefaultHasher;
724        use std::hash::{Hash, Hasher};
725
726        let mut hasher = DefaultHasher::new();
727
728        // Hash circuit structure
729        N.hash(&mut hasher);
730        circuit.gates().len().hash(&mut hasher);
731
732        // Hash gate sequence (simplified)
733        for gate in circuit.gates() {
734            gate.name().hash(&mut hasher);
735            for qubit in gate.qubits() {
736                qubit.id().hash(&mut hasher);
737            }
738        }
739
740        format!("{:x}", hasher.finish())
741    }
742
743    /// Update compilation statistics
744    fn update_stats(&self, cache_hit: bool, compilation_time: Duration) {
745        if let Ok(mut stats) = self.stats_collector.lock() {
746            stats.total_compilations += 1;
747            if cache_hit {
748                stats.cache_hits += 1;
749            }
750
751            // Update average compilation time (simple moving average)
752            let total_time =
753                stats.average_compilation_time.as_nanos() * (stats.total_compilations - 1) as u128;
754            let new_total = total_time + compilation_time.as_nanos();
755            stats.average_compilation_time =
756                Duration::from_nanos((new_total / stats.total_compilations as u128) as u64);
757        }
758    }
759
760    /// Get compilation statistics
761    pub fn get_stats(&self) -> GlobalCompilationStats {
762        self.stats_collector
763            .lock()
764            .map(|stats| GlobalCompilationStats {
765                total_compilations: stats.total_compilations,
766                cache_hits: stats.cache_hits,
767                average_compilation_time: stats.average_compilation_time,
768                backend_usage: stats.backend_usage.clone(),
769            })
770            .unwrap_or_default()
771    }
772
773    /// Clear compilation cache
774    pub fn clear_cache(&self) {
775        if let Ok(mut cache) = self.cache.lock() {
776            cache.clear();
777        }
778    }
779}
780
781/// Execution interface for compiled circuits
782pub struct CircuitExecutor {
783    /// Active backends
784    backends: HashMap<String, Box<dyn SimulatorExecutor>>,
785}
786
787/// Simulator executor trait
788pub trait SimulatorExecutor: Send + Sync {
789    /// Execute compiled circuit
790    fn execute(&self, circuit: &CompiledCircuit) -> QuantRS2Result<ExecutionResult>;
791
792    /// Backend name
793    fn name(&self) -> &str;
794
795    /// Check if circuit is compatible
796    fn is_compatible(&self, circuit: &CompiledCircuit) -> bool;
797}
798
799/// Execution result
800#[derive(Debug, Clone)]
801pub struct ExecutionResult {
802    /// Measurement outcomes
803    pub measurements: HashMap<usize, Vec<u8>>,
804    /// Final state (if available)
805    pub final_state: Option<Vec<f64>>,
806    /// Execution statistics
807    pub execution_stats: ExecutionStats,
808    /// Backend-specific results
809    pub backend_results: HashMap<String, String>,
810}
811
812/// Execution statistics
813#[derive(Debug, Clone)]
814pub struct ExecutionStats {
815    /// Execution time
816    pub execution_time: Duration,
817    /// Memory used
818    pub memory_used: usize,
819    /// Number of shots
820    pub shots: usize,
821    /// Success rate
822    pub success_rate: f64,
823}
824
825#[cfg(test)]
826mod tests {
827    use super::*;
828    use quantrs2_core::gate::multi::CNOT;
829    use quantrs2_core::gate::single::Hadamard;
830
831    #[test]
832    fn test_compiler_creation() {
833        let compiler = CircuitCompiler::new();
834        assert_eq!(compiler.targets.len(), 0);
835    }
836
837    #[test]
838    fn test_compilation_target() {
839        let target = CompilationTarget {
840            backend: SimulatorBackend::StateVector {
841                max_qubits: 20,
842                use_gpu: false,
843                memory_optimization: MemoryOptimization::Basic,
844            },
845            optimization_level: OptimizationLevel::Basic,
846            instruction_set: InstructionSet::Universal,
847            parallel_execution: true,
848            batch_size: Some(10),
849        };
850
851        assert!(matches!(
852            target.backend,
853            SimulatorBackend::StateVector { .. }
854        ));
855    }
856
857    #[test]
858    fn test_gate_support_checking() {
859        let compiler = CircuitCompiler::new();
860
861        // Universal instruction set should support all gates
862        assert!(compiler.is_gate_supported("H", &InstructionSet::Universal));
863        assert!(compiler.is_gate_supported("CNOT", &InstructionSet::Universal));
864
865        // Clifford set should only support Clifford gates
866        assert!(compiler.is_gate_supported("H", &InstructionSet::Clifford));
867        assert!(!compiler.is_gate_supported("T", &InstructionSet::Clifford));
868    }
869
870    #[test]
871    fn test_resource_estimation() {
872        let compiler = CircuitCompiler::new();
873        let instructions = vec![
874            CompiledInstruction::Gate {
875                name: "H".to_string(),
876                qubits: vec![0],
877                parameters: vec![],
878                id: 0,
879            },
880            CompiledInstruction::Gate {
881                name: "CNOT".to_string(),
882                qubits: vec![0, 1],
883                parameters: vec![],
884                id: 1,
885            },
886        ];
887
888        let target = CompilationTarget {
889            backend: SimulatorBackend::StateVector {
890                max_qubits: 10,
891                use_gpu: false,
892                memory_optimization: MemoryOptimization::None,
893            },
894            optimization_level: OptimizationLevel::None,
895            instruction_set: InstructionSet::Universal,
896            parallel_execution: false,
897            batch_size: None,
898        };
899
900        let resources = compiler.estimate_resources(&instructions, &target);
901        assert!(resources.memory_bytes > 0);
902        assert!(resources.estimated_time > Duration::from_millis(0));
903    }
904
905    #[test]
906    fn test_cache_key_generation() {
907        let compiler = CircuitCompiler::new();
908
909        let mut circuit1 = Circuit::<2>::new();
910        circuit1.add_gate(Hadamard { target: QubitId(0) }).unwrap();
911
912        let mut circuit2 = Circuit::<2>::new();
913        circuit2.add_gate(Hadamard { target: QubitId(0) }).unwrap();
914
915        let key1 = compiler.generate_cache_key(&circuit1);
916        let key2 = compiler.generate_cache_key(&circuit2);
917
918        assert_eq!(key1, key2); // Same circuits should have same keys
919    }
920
921    #[test]
922    fn test_gate_fusion_pass() {
923        let mut fusable_gates = HashSet::new();
924        fusable_gates.insert("H".to_string());
925        fusable_gates.insert("X".to_string());
926
927        let pass = GateFusionPass {
928            max_fusion_size: 3,
929            fusable_gates,
930        };
931
932        assert_eq!(pass.name(), "GateFusion");
933        assert!(pass.modifies_structure());
934    }
935}