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, Eq, 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, Eq)]
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, Eq)]
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<Self>,
179        parallel: bool,
180    },
181    /// Measurement
182    Measure { qubit: usize, classical_bit: usize },
183    /// Conditional operation
184    Conditional {
185        condition: ClassicalCondition,
186        instruction: Box<Self>,
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, Eq)]
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, Eq)]
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) -> &'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    /// Create a new circuit compiler
403    #[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    /// Add a compilation target
414    pub fn add_target(&mut self, target: CompilationTarget) {
415        self.targets.push(target);
416    }
417
418    /// Add an optimization pass
419    pub fn add_optimization_pass(&mut self, pass: Box<dyn OptimizationPass>) {
420        self.optimization_passes.push(pass);
421    }
422
423    /// Compile circuit for the best available target
424    pub fn compile<const N: usize>(&self, circuit: &Circuit<N>) -> QuantRS2Result<CompiledCircuit> {
425        let start_time = Instant::now();
426
427        // Generate cache key
428        let cache_key = self.generate_cache_key(circuit);
429
430        // Check cache
431        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        // Select best target
439        let target = self.select_target(circuit)?;
440
441        // Compile circuit
442        let mut compiled = self.compile_for_target(circuit, &target)?;
443
444        // Apply optimization passes
445        for pass in &self.optimization_passes {
446            if target.optimization_level != OptimizationLevel::None {
447                pass.apply(&mut compiled)?;
448            }
449        }
450
451        // Update statistics
452        compiled.stats.compilation_time = start_time.elapsed();
453
454        // Cache result
455        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    /// Compile circuit for specific target
464    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), // Will be updated later
476            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    /// Select the best compilation target for a circuit
492    fn select_target<const N: usize>(
493        &self,
494        circuit: &Circuit<N>,
495    ) -> QuantRS2Result<CompilationTarget> {
496        if self.targets.is_empty() {
497            return Err(QuantRS2Error::InvalidInput(
498                "No compilation targets available".to_string(),
499            ));
500        }
501
502        // For now, select the first target
503        // In a more sophisticated implementation, this would analyze the circuit
504        // and select the most appropriate target based on various factors
505        Ok(self.targets[0].clone())
506    }
507
508    /// Compile circuit instructions
509    fn compile_instructions<const N: usize>(
510        &self,
511        circuit: &Circuit<N>,
512        target: &CompilationTarget,
513    ) -> QuantRS2Result<Vec<CompiledInstruction>> {
514        let mut instructions = Vec::new();
515        let mut instruction_id = 0;
516
517        for gate in circuit.gates() {
518            let compiled_gate = self.compile_gate(gate.as_ref(), target, instruction_id)?;
519            instructions.push(compiled_gate);
520            instruction_id += 1;
521        }
522
523        // Apply batching if enabled
524        if let Some(batch_size) = target.batch_size {
525            instructions = self.apply_batching(instructions, batch_size);
526        }
527
528        Ok(instructions)
529    }
530
531    /// Compile a single gate
532    fn compile_gate(
533        &self,
534        gate: &dyn GateOp,
535        target: &CompilationTarget,
536        id: usize,
537    ) -> QuantRS2Result<CompiledInstruction> {
538        let name = gate.name().to_string();
539        let qubits: Vec<usize> = gate.qubits().iter().map(|q| q.id() as usize).collect();
540        let parameters = self.extract_gate_parameters(gate);
541
542        // Check if gate is supported by instruction set
543        if !self.is_gate_supported(&name, &target.instruction_set) {
544            return Err(QuantRS2Error::InvalidInput(format!(
545                "Gate {name} not supported by instruction set"
546            )));
547        }
548
549        Ok(CompiledInstruction::Gate {
550            name,
551            qubits,
552            parameters,
553            id,
554        })
555    }
556
557    /// Extract parameters from a gate
558    fn extract_gate_parameters(&self, gate: &dyn GateOp) -> Vec<f64> {
559        // This would need to access gate-specific parameter methods
560        // For now, return empty parameters
561        Vec::new()
562    }
563
564    /// Check if gate is supported by instruction set
565    fn is_gate_supported(&self, gate_name: &str, instruction_set: &InstructionSet) -> bool {
566        match instruction_set {
567            InstructionSet::Universal => true,
568            InstructionSet::Clifford => {
569                matches!(gate_name, "H" | "S" | "CNOT" | "X" | "Y" | "Z")
570            }
571            InstructionSet::Native { gates } => gates.contains(&gate_name.to_string()),
572            InstructionSet::Custom {
573                single_qubit,
574                two_qubit,
575                multi_qubit,
576            } => {
577                single_qubit.contains(&gate_name.to_string())
578                    || two_qubit.contains(&gate_name.to_string())
579                    || multi_qubit.contains(&gate_name.to_string())
580            }
581        }
582    }
583
584    /// Apply batching to instructions
585    fn apply_batching(
586        &self,
587        instructions: Vec<CompiledInstruction>,
588        batch_size: usize,
589    ) -> Vec<CompiledInstruction> {
590        let mut batched = Vec::new();
591        let mut current_batch = Vec::new();
592
593        for instruction in instructions {
594            current_batch.push(instruction);
595
596            if current_batch.len() >= batch_size {
597                batched.push(CompiledInstruction::Batch {
598                    instructions: current_batch,
599                    parallel: true,
600                });
601                current_batch = Vec::new();
602            }
603        }
604
605        // Add remaining instructions
606        if !current_batch.is_empty() {
607            if current_batch.len() == 1 {
608                batched.extend(current_batch);
609            } else {
610                batched.push(CompiledInstruction::Batch {
611                    instructions: current_batch,
612                    parallel: true,
613                });
614            }
615        }
616
617        batched
618    }
619
620    /// Generate circuit metadata
621    fn generate_metadata<const N: usize>(
622        &self,
623        circuit: &Circuit<N>,
624        target: &CompilationTarget,
625    ) -> CircuitMetadata {
626        let mut gate_counts = HashMap::new();
627        for gate in circuit.gates() {
628            *gate_counts.entry(gate.name().to_string()).or_insert(0) += 1;
629        }
630
631        CircuitMetadata {
632            num_qubits: N,
633            depth: circuit.gates().len(), // Simplified depth calculation
634            gate_counts,
635            created_at: std::time::SystemTime::now(),
636            target: target.clone(),
637        }
638    }
639
640    /// Estimate resource requirements
641    fn estimate_resources(
642        &self,
643        instructions: &[CompiledInstruction],
644        target: &CompilationTarget,
645    ) -> ResourceRequirements {
646        let instruction_count = instructions.len();
647
648        // Simple estimation based on backend type
649        let (memory_bytes, estimated_time, gpu_memory) = match &target.backend {
650            SimulatorBackend::StateVector {
651                max_qubits,
652                use_gpu,
653                ..
654            } => {
655                let memory = if *max_qubits <= 30 {
656                    (1usize << max_qubits) * 16 // 16 bytes per complex number
657                } else {
658                    usize::MAX // Too large
659                };
660                let time = Duration::from_millis(instruction_count as u64);
661                let gpu_mem = if *use_gpu { Some(memory) } else { None };
662                (memory, time, gpu_mem)
663            }
664            SimulatorBackend::Stabilizer { .. } => {
665                // Stabilizer tableau grows quadratically
666                let memory = instruction_count * instruction_count * 8;
667                let time = Duration::from_millis(instruction_count as u64 / 10);
668                (memory, time, None)
669            }
670            SimulatorBackend::MatrixProductState { max_bond_dim, .. } => {
671                let memory = instruction_count * max_bond_dim * max_bond_dim * 16;
672                let time = Duration::from_millis(instruction_count as u64 * 2);
673                (memory, time, None)
674            }
675            _ => {
676                // Default estimates
677                let memory = instruction_count * 1024;
678                let time = Duration::from_millis(instruction_count as u64);
679                (memory, time, None)
680            }
681        };
682
683        ResourceRequirements {
684            memory_bytes,
685            estimated_time,
686            gpu_memory_bytes: gpu_memory,
687            cpu_cores: 1,
688            disk_space_bytes: None,
689        }
690    }
691
692    /// Generate backend-specific data
693    fn generate_backend_data<const N: usize>(
694        &self,
695        circuit: &Circuit<N>,
696        target: &CompilationTarget,
697    ) -> QuantRS2Result<BackendData> {
698        match &target.backend {
699            SimulatorBackend::StateVector { .. } => Ok(BackendData::StateVector {
700                initial_state: None,
701                measurement_strategy: MeasurementStrategy::EndMeasurement,
702            }),
703            SimulatorBackend::Stabilizer { .. } => Ok(BackendData::Stabilizer {
704                initial_tableau: None,
705            }),
706            SimulatorBackend::MatrixProductState { max_bond_dim, .. } => {
707                Ok(BackendData::MatrixProductState {
708                    tensors: Vec::new(),
709                    bond_dims: vec![1; N + 1],
710                })
711            }
712            SimulatorBackend::TensorNetwork { .. } => Ok(BackendData::TensorNetwork {
713                network_topology: "linear".to_string(),
714                contraction_order: (0..N).collect(),
715            }),
716            SimulatorBackend::External { name, .. } => Ok(BackendData::External {
717                serialized_circuit: format!("circuit_for_{name}"),
718                format: "qasm".to_string(),
719            }),
720            SimulatorBackend::DensityMatrix { .. } => Ok(BackendData::StateVector {
721                initial_state: None,
722                measurement_strategy: MeasurementStrategy::EndMeasurement,
723            }),
724        }
725    }
726
727    /// Generate cache key for circuit
728    fn generate_cache_key<const N: usize>(&self, circuit: &Circuit<N>) -> String {
729        use std::collections::hash_map::DefaultHasher;
730        use std::hash::{Hash, Hasher};
731
732        let mut hasher = DefaultHasher::new();
733
734        // Hash circuit structure
735        N.hash(&mut hasher);
736        circuit.gates().len().hash(&mut hasher);
737
738        // Hash gate sequence (simplified)
739        for gate in circuit.gates() {
740            gate.name().hash(&mut hasher);
741            for qubit in gate.qubits() {
742                qubit.id().hash(&mut hasher);
743            }
744        }
745
746        format!("{:x}", hasher.finish())
747    }
748
749    /// Update compilation statistics
750    fn update_stats(&self, cache_hit: bool, compilation_time: Duration) {
751        if let Ok(mut stats) = self.stats_collector.lock() {
752            stats.total_compilations += 1;
753            if cache_hit {
754                stats.cache_hits += 1;
755            }
756
757            // Update average compilation time (simple moving average)
758            let total_time =
759                stats.average_compilation_time.as_nanos() * (stats.total_compilations - 1) as u128;
760            let new_total = total_time + compilation_time.as_nanos();
761            stats.average_compilation_time =
762                Duration::from_nanos((new_total / stats.total_compilations as u128) as u64);
763        }
764    }
765
766    /// Get compilation statistics
767    #[must_use]
768    pub fn get_stats(&self) -> GlobalCompilationStats {
769        self.stats_collector
770            .lock()
771            .map(|stats| GlobalCompilationStats {
772                total_compilations: stats.total_compilations,
773                cache_hits: stats.cache_hits,
774                average_compilation_time: stats.average_compilation_time,
775                backend_usage: stats.backend_usage.clone(),
776            })
777            .unwrap_or_default()
778    }
779
780    /// Clear compilation cache
781    pub fn clear_cache(&self) {
782        if let Ok(mut cache) = self.cache.lock() {
783            cache.clear();
784        }
785    }
786}
787
788/// Execution interface for compiled circuits
789pub struct CircuitExecutor {
790    /// Active backends
791    backends: HashMap<String, Box<dyn SimulatorExecutor>>,
792}
793
794/// Simulator executor trait
795pub trait SimulatorExecutor: Send + Sync {
796    /// Execute compiled circuit
797    fn execute(&self, circuit: &CompiledCircuit) -> QuantRS2Result<ExecutionResult>;
798
799    /// Backend name
800    fn name(&self) -> &str;
801
802    /// Check if circuit is compatible
803    fn is_compatible(&self, circuit: &CompiledCircuit) -> bool;
804}
805
806/// Execution result
807#[derive(Debug, Clone)]
808pub struct ExecutionResult {
809    /// Measurement outcomes
810    pub measurements: HashMap<usize, Vec<u8>>,
811    /// Final state (if available)
812    pub final_state: Option<Vec<f64>>,
813    /// Execution statistics
814    pub execution_stats: ExecutionStats,
815    /// Backend-specific results
816    pub backend_results: HashMap<String, String>,
817}
818
819/// Execution statistics
820#[derive(Debug, Clone)]
821pub struct ExecutionStats {
822    /// Execution time
823    pub execution_time: Duration,
824    /// Memory used
825    pub memory_used: usize,
826    /// Number of shots
827    pub shots: usize,
828    /// Success rate
829    pub success_rate: f64,
830}
831
832#[cfg(test)]
833mod tests {
834    use super::*;
835    use quantrs2_core::gate::multi::CNOT;
836    use quantrs2_core::gate::single::Hadamard;
837
838    #[test]
839    fn test_compiler_creation() {
840        let compiler = CircuitCompiler::new();
841        assert_eq!(compiler.targets.len(), 0);
842    }
843
844    #[test]
845    fn test_compilation_target() {
846        let target = CompilationTarget {
847            backend: SimulatorBackend::StateVector {
848                max_qubits: 20,
849                use_gpu: false,
850                memory_optimization: MemoryOptimization::Basic,
851            },
852            optimization_level: OptimizationLevel::Basic,
853            instruction_set: InstructionSet::Universal,
854            parallel_execution: true,
855            batch_size: Some(10),
856        };
857
858        assert!(matches!(
859            target.backend,
860            SimulatorBackend::StateVector { .. }
861        ));
862    }
863
864    #[test]
865    fn test_gate_support_checking() {
866        let compiler = CircuitCompiler::new();
867
868        // Universal instruction set should support all gates
869        assert!(compiler.is_gate_supported("H", &InstructionSet::Universal));
870        assert!(compiler.is_gate_supported("CNOT", &InstructionSet::Universal));
871
872        // Clifford set should only support Clifford gates
873        assert!(compiler.is_gate_supported("H", &InstructionSet::Clifford));
874        assert!(!compiler.is_gate_supported("T", &InstructionSet::Clifford));
875    }
876
877    #[test]
878    fn test_resource_estimation() {
879        let compiler = CircuitCompiler::new();
880        let instructions = vec![
881            CompiledInstruction::Gate {
882                name: "H".to_string(),
883                qubits: vec![0],
884                parameters: vec![],
885                id: 0,
886            },
887            CompiledInstruction::Gate {
888                name: "CNOT".to_string(),
889                qubits: vec![0, 1],
890                parameters: vec![],
891                id: 1,
892            },
893        ];
894
895        let target = CompilationTarget {
896            backend: SimulatorBackend::StateVector {
897                max_qubits: 10,
898                use_gpu: false,
899                memory_optimization: MemoryOptimization::None,
900            },
901            optimization_level: OptimizationLevel::None,
902            instruction_set: InstructionSet::Universal,
903            parallel_execution: false,
904            batch_size: None,
905        };
906
907        let resources = compiler.estimate_resources(&instructions, &target);
908        assert!(resources.memory_bytes > 0);
909        assert!(resources.estimated_time > Duration::from_millis(0));
910    }
911
912    #[test]
913    fn test_cache_key_generation() {
914        let compiler = CircuitCompiler::new();
915
916        let mut circuit1 = Circuit::<2>::new();
917        circuit1
918            .add_gate(Hadamard { target: QubitId(0) })
919            .expect("add H gate to circuit1");
920
921        let mut circuit2 = Circuit::<2>::new();
922        circuit2
923            .add_gate(Hadamard { target: QubitId(0) })
924            .expect("add H gate to circuit2");
925
926        let key1 = compiler.generate_cache_key(&circuit1);
927        let key2 = compiler.generate_cache_key(&circuit2);
928
929        assert_eq!(key1, key2); // Same circuits should have same keys
930    }
931
932    #[test]
933    fn test_gate_fusion_pass() {
934        let mut fusable_gates = HashSet::new();
935        fusable_gates.insert("H".to_string());
936        fusable_gates.insert("X".to_string());
937
938        let pass = GateFusionPass {
939            max_fusion_size: 3,
940            fusable_gates,
941        };
942
943        assert_eq!(pass.name(), "GateFusion");
944        assert!(pass.modifies_structure());
945    }
946}