1use scirs2_core::ndarray::Array2;
9use scirs2_core::Complex64;
10use serde::{Deserialize, Serialize};
11use std::collections::{HashMap, HashSet};
12use std::hash::{Hash, Hasher};
13
14use crate::error::{Result, SimulatorError};
15use crate::scirs2_integration::SciRS2Backend;
16
17#[cfg(feature = "advanced_math")]
18use quantrs2_circuit::prelude::*;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub enum FusionStrategy {
23 Aggressive,
25 Conservative,
27 Balanced,
29 Adaptive,
31 Custom,
33}
34
35#[derive(Debug, Clone)]
37pub struct AdaptiveFusionConfig {
38 pub strategy: FusionStrategy,
40 pub max_fusion_size: usize,
42 pub min_benefit_threshold: f64,
44 pub enable_cross_qubit_fusion: bool,
46 pub enable_temporal_fusion: bool,
48 pub max_analysis_depth: usize,
50 pub enable_ml_predictions: bool,
52 pub fusion_cache_size: usize,
54 pub parallel_analysis: bool,
56}
57
58impl Default for AdaptiveFusionConfig {
59 fn default() -> Self {
60 Self {
61 strategy: FusionStrategy::Adaptive,
62 max_fusion_size: 8,
63 min_benefit_threshold: 1.1,
64 enable_cross_qubit_fusion: true,
65 enable_temporal_fusion: true,
66 max_analysis_depth: 100,
67 enable_ml_predictions: true,
68 fusion_cache_size: 10_000,
69 parallel_analysis: true,
70 }
71 }
72}
73
74#[derive(Debug, Clone)]
76pub struct QuantumGate {
77 pub gate_type: GateType,
79 pub qubits: Vec<usize>,
81 pub parameters: Vec<f64>,
83 pub matrix: Array2<Complex64>,
85 pub position: usize,
87 pub cost: f64,
89}
90
91#[derive(Debug, Clone, PartialEq, Eq, Hash)]
93pub enum GateType {
94 Identity,
96 PauliX,
97 PauliY,
98 PauliZ,
99 Hadamard,
100 Phase,
101 T,
102 RotationX,
103 RotationY,
104 RotationZ,
105 CNOT,
107 CZ,
108 SWAP,
109 ISwap,
110 Toffoli,
112 Fredkin,
113 Custom(String),
115}
116
117impl QuantumGate {
118 #[must_use]
120 pub fn new(gate_type: GateType, qubits: Vec<usize>, parameters: Vec<f64>) -> Self {
121 let matrix = Self::gate_matrix(&gate_type, ¶meters);
122 let cost = Self::estimate_cost(&gate_type, qubits.len());
123
124 Self {
125 gate_type,
126 qubits,
127 parameters,
128 matrix,
129 position: 0,
130 cost,
131 }
132 }
133
134 fn gate_matrix(gate_type: &GateType, parameters: &[f64]) -> Array2<Complex64> {
136 match gate_type {
137 GateType::Identity => Array2::eye(2),
138 GateType::PauliX => Array2::from_shape_vec(
139 (2, 2),
140 vec![
141 Complex64::new(0.0, 0.0),
142 Complex64::new(1.0, 0.0),
143 Complex64::new(1.0, 0.0),
144 Complex64::new(0.0, 0.0),
145 ],
146 )
147 .expect("Pauli X matrix shape is always valid"),
148 GateType::PauliY => Array2::from_shape_vec(
149 (2, 2),
150 vec![
151 Complex64::new(0.0, 0.0),
152 Complex64::new(0.0, -1.0),
153 Complex64::new(0.0, 1.0),
154 Complex64::new(0.0, 0.0),
155 ],
156 )
157 .expect("Pauli Y matrix shape is always valid"),
158 GateType::PauliZ => Array2::from_shape_vec(
159 (2, 2),
160 vec![
161 Complex64::new(1.0, 0.0),
162 Complex64::new(0.0, 0.0),
163 Complex64::new(0.0, 0.0),
164 Complex64::new(-1.0, 0.0),
165 ],
166 )
167 .expect("Pauli Z matrix shape is always valid"),
168 GateType::Hadamard => {
169 let inv_sqrt2 = 1.0 / 2.0_f64.sqrt();
170 Array2::from_shape_vec(
171 (2, 2),
172 vec![
173 Complex64::new(inv_sqrt2, 0.0),
174 Complex64::new(inv_sqrt2, 0.0),
175 Complex64::new(inv_sqrt2, 0.0),
176 Complex64::new(-inv_sqrt2, 0.0),
177 ],
178 )
179 .expect("Hadamard matrix shape is always valid")
180 }
181 GateType::RotationX => {
182 let theta = parameters.first().copied().unwrap_or(0.0);
183 let cos_half = (theta / 2.0).cos();
184 let sin_half = (theta / 2.0).sin();
185 Array2::from_shape_vec(
186 (2, 2),
187 vec![
188 Complex64::new(cos_half, 0.0),
189 Complex64::new(0.0, -sin_half),
190 Complex64::new(0.0, -sin_half),
191 Complex64::new(cos_half, 0.0),
192 ],
193 )
194 .expect("Rotation X matrix shape is always valid")
195 }
196 GateType::RotationY => {
197 let theta = parameters.first().copied().unwrap_or(0.0);
198 let cos_half = (theta / 2.0).cos();
199 let sin_half = (theta / 2.0).sin();
200 Array2::from_shape_vec(
201 (2, 2),
202 vec![
203 Complex64::new(cos_half, 0.0),
204 Complex64::new(-sin_half, 0.0),
205 Complex64::new(sin_half, 0.0),
206 Complex64::new(cos_half, 0.0),
207 ],
208 )
209 .expect("Rotation Y matrix shape is always valid")
210 }
211 GateType::RotationZ => {
212 let theta = parameters.first().copied().unwrap_or(0.0);
213 let exp_neg = Complex64::new(0.0, -theta / 2.0).exp();
214 let exp_pos = Complex64::new(0.0, theta / 2.0).exp();
215 Array2::from_shape_vec(
216 (2, 2),
217 vec![
218 exp_neg,
219 Complex64::new(0.0, 0.0),
220 Complex64::new(0.0, 0.0),
221 exp_pos,
222 ],
223 )
224 .expect("Rotation Z matrix shape is always valid")
225 }
226 GateType::CNOT => Array2::from_shape_vec(
227 (4, 4),
228 vec![
229 Complex64::new(1.0, 0.0),
230 Complex64::new(0.0, 0.0),
231 Complex64::new(0.0, 0.0),
232 Complex64::new(0.0, 0.0),
233 Complex64::new(0.0, 0.0),
234 Complex64::new(1.0, 0.0),
235 Complex64::new(0.0, 0.0),
236 Complex64::new(0.0, 0.0),
237 Complex64::new(0.0, 0.0),
238 Complex64::new(0.0, 0.0),
239 Complex64::new(0.0, 0.0),
240 Complex64::new(1.0, 0.0),
241 Complex64::new(0.0, 0.0),
242 Complex64::new(0.0, 0.0),
243 Complex64::new(1.0, 0.0),
244 Complex64::new(0.0, 0.0),
245 ],
246 )
247 .expect("CNOT matrix shape is always valid"),
248 _ => Array2::eye(2), }
250 }
251
252 fn estimate_cost(gate_type: &GateType, num_qubits: usize) -> f64 {
254 let base_cost = match gate_type {
255 GateType::Identity => 0.1,
256 GateType::PauliX | GateType::PauliY | GateType::PauliZ => 1.0,
257 GateType::Hadamard => 1.2,
258 GateType::Phase | GateType::T => 1.1,
259 GateType::RotationX | GateType::RotationY | GateType::RotationZ => 1.5,
260 GateType::CNOT | GateType::CZ => 2.0,
261 GateType::SWAP | GateType::ISwap => 2.5,
262 GateType::Toffoli => 4.0,
263 GateType::Fredkin => 4.5,
264 GateType::Custom(_) => 3.0,
265 };
266
267 base_cost * f64::from(1 << num_qubits)
269 }
270
271 #[must_use]
273 pub fn commutes_with(&self, other: &Self) -> bool {
274 let self_qubits: HashSet<_> = self.qubits.iter().collect();
276 let other_qubits: HashSet<_> = other.qubits.iter().collect();
277
278 if self_qubits.is_disjoint(&other_qubits) {
279 return true;
280 }
281
282 if self.qubits == other.qubits {
284 return self.check_specific_commutation(other);
285 }
286
287 false
288 }
289
290 const fn check_specific_commutation(&self, other: &Self) -> bool {
292 match (&self.gate_type, &other.gate_type) {
293 (GateType::PauliX, GateType::PauliX)
295 | (GateType::PauliY, GateType::PauliY)
296 | (GateType::PauliZ, GateType::PauliZ) => true,
297 (GateType::PauliZ, GateType::RotationZ) | (GateType::RotationZ, GateType::PauliZ) => {
299 true
300 }
301 (GateType::PauliX, GateType::RotationX) | (GateType::RotationX, GateType::PauliX) => {
303 true
304 }
305 (GateType::PauliY, GateType::RotationY) | (GateType::RotationY, GateType::PauliY) => {
307 true
308 }
309 (GateType::RotationX, GateType::RotationX)
311 | (GateType::RotationY, GateType::RotationY)
312 | (GateType::RotationZ, GateType::RotationZ) => true,
313 (GateType::Identity, _) | (_, GateType::Identity) => true,
315 _ => false,
316 }
317 }
318
319 #[must_use]
321 pub fn can_fuse_with(&self, other: &Self) -> bool {
322 if self.qubits != other.qubits {
324 return false;
325 }
326
327 if self.qubits.len() == 1 && other.qubits.len() == 1 {
329 return true;
330 }
331
332 if self.qubits.len() == 2 && other.qubits.len() == 2 {
334 return self.check_two_qubit_fusion_compatibility(other);
335 }
336
337 false
338 }
339
340 const fn check_two_qubit_fusion_compatibility(&self, other: &Self) -> bool {
342 match (&self.gate_type, &other.gate_type) {
343 (GateType::CNOT, GateType::CNOT) => true,
345 (GateType::CZ, GateType::CZ) => true,
347 (GateType::CNOT, GateType::CZ) | (GateType::CZ, GateType::CNOT) => true,
349 _ => false,
350 }
351 }
352}
353
354impl Hash for QuantumGate {
355 fn hash<H: Hasher>(&self, state: &mut H) {
356 self.gate_type.hash(state);
357 self.qubits.hash(state);
358 for ¶m in &self.parameters {
360 ((param * 1000.0).round() as i64).hash(state);
361 }
362 }
363}
364
365impl PartialEq for QuantumGate {
366 fn eq(&self, other: &Self) -> bool {
367 self.gate_type == other.gate_type
368 && self.qubits == other.qubits
369 && self.parameters.len() == other.parameters.len()
370 && self
371 .parameters
372 .iter()
373 .zip(other.parameters.iter())
374 .all(|(&a, &b)| (a - b).abs() < 1e-10)
375 }
376}
377
378impl Eq for QuantumGate {}
379
380#[derive(Debug, Clone)]
382pub struct FusedGateBlock {
383 pub gates: Vec<QuantumGate>,
385 pub combined_matrix: Array2<Complex64>,
387 pub qubits: Vec<usize>,
389 pub cost: f64,
391 pub improvement_factor: f64,
393}
394
395impl FusedGateBlock {
396 pub fn new(gates: Vec<QuantumGate>) -> Result<Self> {
398 if gates.is_empty() {
399 return Err(SimulatorError::InvalidInput(
400 "Cannot create empty gate block".to_string(),
401 ));
402 }
403
404 let mut qubits = HashSet::new();
406 for gate in &gates {
407 qubits.extend(&gate.qubits);
408 }
409 let mut qubit_vec: Vec<usize> = qubits.into_iter().collect();
410 qubit_vec.sort_unstable();
411
412 let combined_matrix = Self::calculate_combined_matrix(&gates, &qubit_vec)?;
414
415 let individual_cost: f64 = gates.iter().map(|g| g.cost).sum();
417 let fused_cost = Self::estimate_fused_cost(&combined_matrix);
418 let improvement_factor = individual_cost / fused_cost;
419
420 Ok(Self {
421 gates,
422 combined_matrix,
423 qubits: qubit_vec,
424 cost: fused_cost,
425 improvement_factor,
426 })
427 }
428
429 fn calculate_combined_matrix(
431 gates: &[QuantumGate],
432 qubits: &[usize],
433 ) -> Result<Array2<Complex64>> {
434 let num_qubits = qubits.len();
435 let matrix_size = 1 << num_qubits;
436 let mut combined = Array2::eye(matrix_size);
437
438 for gate in gates {
439 let gate_matrix = Self::expand_gate_matrix(&gate.matrix, &gate.qubits, qubits)?;
440 combined = gate_matrix.dot(&combined);
441 }
442
443 Ok(combined)
444 }
445
446 fn expand_gate_matrix(
448 gate_matrix: &Array2<Complex64>,
449 gate_qubits: &[usize],
450 all_qubits: &[usize],
451 ) -> Result<Array2<Complex64>> {
452 let num_all_qubits = all_qubits.len();
453 let full_size = 1 << num_all_qubits;
454 let gate_size = 1 << gate_qubits.len();
455
456 if gate_matrix.nrows() != gate_size || gate_matrix.ncols() != gate_size {
457 return Err(SimulatorError::DimensionMismatch(
458 "Gate matrix size doesn't match number of qubits".to_string(),
459 ));
460 }
461
462 let mut expanded = Array2::eye(full_size);
463
464 let mut qubit_mapping = HashMap::new();
466 for (gate_pos, &qubit) in gate_qubits.iter().enumerate() {
467 if let Some(all_pos) = all_qubits.iter().position(|&q| q == qubit) {
468 qubit_mapping.insert(gate_pos, all_pos);
469 } else {
470 return Err(SimulatorError::InvalidInput(format!(
471 "Gate qubit {qubit} not found in qubit list"
472 )));
473 }
474 }
475
476 for i in 0..full_size {
478 for j in 0..full_size {
479 let mut gate_i = 0;
481 let mut gate_j = 0;
482 let mut valid = true;
483
484 for (gate_pos, &all_pos) in &qubit_mapping {
485 let bit_i = (i >> (num_all_qubits - 1 - all_pos)) & 1;
486 let bit_j = (j >> (num_all_qubits - 1 - all_pos)) & 1;
487
488 gate_i |= bit_i << (gate_qubits.len() - 1 - gate_pos);
489 gate_j |= bit_j << (gate_qubits.len() - 1 - gate_pos);
490 }
491
492 for all_pos in 0..num_all_qubits {
494 if !qubit_mapping.values().any(|&pos| pos == all_pos) {
495 let bit_i = (i >> (num_all_qubits - 1 - all_pos)) & 1;
496 let bit_j = (j >> (num_all_qubits - 1 - all_pos)) & 1;
497 if bit_i != bit_j {
498 valid = false;
499 break;
500 }
501 }
502 }
503
504 if valid {
505 expanded[[i, j]] = gate_matrix[[gate_i, gate_j]];
506 } else {
507 expanded[[i, j]] = if i == j {
508 Complex64::new(1.0, 0.0)
509 } else {
510 Complex64::new(0.0, 0.0)
511 };
512 }
513 }
514 }
515
516 Ok(expanded)
517 }
518
519 fn estimate_fused_cost(matrix: &Array2<Complex64>) -> f64 {
521 let size = matrix.nrows();
523 let base_cost = (size as f64).log2() * size as f64;
524
525 let fusion_overhead = 0.1 * base_cost;
527
528 base_cost + fusion_overhead
529 }
530
531 #[must_use]
533 pub fn is_beneficial(&self) -> bool {
534 self.improvement_factor > 1.1 }
536}
537
538#[derive(Debug, Clone, Serialize, Deserialize)]
540pub struct CircuitAnalysis {
541 pub original_gate_count: usize,
543 pub fused_gate_count: usize,
545 pub fusion_blocks: usize,
547 pub performance_improvement: f64,
549 pub gate_distribution: HashMap<String, usize>,
551 pub fusion_opportunities: Vec<FusionOpportunity>,
553 pub circuit_depth: (usize, usize),
555}
556
557#[derive(Debug, Clone, Serialize, Deserialize)]
559pub struct FusionOpportunity {
560 pub description: String,
562 pub qubits: Vec<usize>,
564 pub gate_types: Vec<String>,
566 pub estimated_benefit: f64,
568 pub confidence: f64,
570}
571
572pub struct AdaptiveGateFusion {
574 config: AdaptiveFusionConfig,
576 backend: Option<SciRS2Backend>,
578 fusion_cache: HashMap<FusionPatternKey, CachedFusionResult>,
580 learning_history: Vec<FusionExperiment>,
582 #[cfg(feature = "advanced_math")]
584 optimizer: Option<Box<dyn std::any::Any>>, ml_predictor: Option<MLFusionPredictor>,
587 cache_hits: usize,
589 cache_misses: usize,
590 pattern_analyzer: CircuitPatternAnalyzer,
592}
593
594#[derive(Debug, Clone, PartialEq, Eq, Hash)]
596pub struct FusionPatternKey {
597 gate_types: Vec<GateType>,
599 num_qubits: usize,
601 parameter_hash: u64,
603}
604
605impl FusionPatternKey {
606 pub fn from_gates(gates: &[QuantumGate]) -> Result<Self> {
608 let gate_types: Vec<GateType> = gates.iter().map(|g| g.gate_type.clone()).collect();
609
610 let mut qubits = std::collections::HashSet::new();
612 for gate in gates {
613 for &qubit in &gate.qubits {
614 qubits.insert(qubit);
615 }
616 }
617 let num_qubits = qubits.len();
618
619 let mut parameter_hash = 0u64;
621 for gate in gates {
622 for ¶m in &gate.parameters {
623 parameter_hash = parameter_hash.wrapping_add((param * 1000.0) as u64);
624 }
625 }
626
627 Ok(Self {
628 gate_types,
629 num_qubits,
630 parameter_hash,
631 })
632 }
633}
634
635#[derive(Debug, Clone)]
637pub struct CachedFusionResult {
638 fused_block: FusedGateBlock,
640 benefit: f64,
642 usage_count: usize,
644 last_accessed: std::time::Instant,
646}
647
648pub struct MLFusionPredictor {
650 feature_weights: HashMap<String, f64>,
652 training_data: Vec<MLTrainingExample>,
654 accuracy: f64,
656}
657
658#[derive(Debug, Clone)]
660pub struct MLTrainingExample {
661 features: Vec<f64>,
663 benefit: f64,
665 observed_benefit: Option<f64>,
667}
668
669pub struct CircuitPatternAnalyzer {
671 beneficial_patterns: HashMap<String, f64>,
673 pattern_history: Vec<PatternRecognitionResult>,
675}
676
677#[derive(Debug, Clone)]
679pub struct PatternRecognitionResult {
680 pub pattern: String,
682 pub confidence: f64,
684 pub expected_benefit: f64,
686}
687
688#[derive(Debug, Clone)]
690pub struct FusionResult {
691 pub should_fuse: bool,
693 pub confidence: f64,
695 pub expected_speedup: f64,
697 pub estimated_error: f64,
699}
700
701#[derive(Debug, Clone)]
703struct FusionExperiment {
704 circuit_fingerprint: u64,
705 fusion_strategy: FusionStrategy,
706 performance_improvement: f64,
707 execution_time_ms: f64,
708}
709
710impl AdaptiveGateFusion {
711 pub fn new(config: AdaptiveFusionConfig) -> Result<Self> {
713 Ok(Self {
714 config,
715 backend: None,
716 fusion_cache: HashMap::new(),
717 learning_history: Vec::new(),
718 ml_predictor: None,
719 cache_hits: 0,
720 cache_misses: 0,
721 pattern_analyzer: CircuitPatternAnalyzer::new(),
722 #[cfg(feature = "advanced_math")]
723 optimizer: None,
724 })
725 }
726
727 pub fn with_backend(mut self) -> Result<Self> {
729 self.backend = Some(SciRS2Backend::new());
730
731 #[cfg(feature = "advanced_math")]
732 {
733 let strategy = match self.config.strategy {
734 FusionStrategy::Aggressive => OptimizationStrategy::MinimizeTime,
735 FusionStrategy::Conservative => OptimizationStrategy::MinimizeLength,
736 FusionStrategy::Balanced => OptimizationStrategy::Balanced,
737 FusionStrategy::Adaptive => OptimizationStrategy::MinimizeCrossings,
738 FusionStrategy::Custom => OptimizationStrategy::Balanced,
739 };
740
741 self.optimizer = Some(Box::new(strategy));
743 }
744
745 Ok(self)
746 }
747
748 pub fn analyze_circuit(&mut self, gates: &[QuantumGate]) -> Result<CircuitAnalysis> {
750 let start_time = std::time::Instant::now();
751
752 let original_gate_count = gates.len();
754 let original_depth = self.calculate_circuit_depth(gates);
755
756 let fusion_opportunities = self.identify_fusion_opportunities(gates)?;
758
759 let (fused_blocks, remaining_gates) = self.perform_fusion(gates)?;
761 let fused_gate_count = fused_blocks.len() + remaining_gates.len();
762 let fused_depth = self.calculate_fused_circuit_depth(&fused_blocks, &remaining_gates);
763
764 let original_cost: f64 = gates.iter().map(|g| g.cost).sum();
766 let fused_cost: f64 = fused_blocks.iter().map(|b| b.cost).sum::<f64>()
767 + remaining_gates.iter().map(|g| g.cost).sum::<f64>();
768 let performance_improvement = original_cost / fused_cost;
769
770 let mut gate_distribution = HashMap::new();
772 for gate in gates {
773 let gate_name = format!("{:?}", gate.gate_type);
774 *gate_distribution.entry(gate_name).or_insert(0) += 1;
775 }
776
777 let analysis = CircuitAnalysis {
778 original_gate_count,
779 fused_gate_count,
780 fusion_blocks: fused_blocks.len(),
781 performance_improvement,
782 gate_distribution,
783 fusion_opportunities,
784 circuit_depth: (original_depth, fused_depth),
785 };
786
787 if self.config.enable_ml_predictions {
789 let experiment = FusionExperiment {
790 circuit_fingerprint: self.calculate_circuit_fingerprint(gates),
791 fusion_strategy: self.config.strategy,
792 performance_improvement,
793 execution_time_ms: start_time.elapsed().as_secs_f64() * 1000.0,
794 };
795 self.learning_history.push(experiment);
796 }
797
798 Ok(analysis)
799 }
800
801 pub fn fuse_circuit(&mut self, gates: &[QuantumGate]) -> Result<Vec<FusedGateBlock>> {
803 let (fused_blocks, _) = self.perform_fusion(gates)?;
804 Ok(fused_blocks)
805 }
806
807 fn identify_fusion_opportunities(
809 &self,
810 gates: &[QuantumGate],
811 ) -> Result<Vec<FusionOpportunity>> {
812 let mut opportunities = Vec::new();
813
814 for window_size in 2..=self.config.max_fusion_size {
816 for window in gates.windows(window_size) {
817 if let Some(opportunity) = self.analyze_gate_window(window)? {
818 opportunities.push(opportunity);
819 }
820 }
821 }
822
823 opportunities.sort_by(|a, b| {
825 b.estimated_benefit
826 .partial_cmp(&a.estimated_benefit)
827 .unwrap_or(std::cmp::Ordering::Equal)
828 });
829 self.remove_overlapping_opportunities(opportunities)
830 }
831
832 fn analyze_gate_window(&self, window: &[QuantumGate]) -> Result<Option<FusionOpportunity>> {
834 if window.len() < 2 {
835 return Ok(None);
836 }
837
838 let can_fuse = self.can_fuse_gate_sequence(window)?;
840 if !can_fuse {
841 return Ok(None);
842 }
843
844 let benefit = self.estimate_fusion_benefit(window)?;
846 if benefit < self.config.min_benefit_threshold {
847 return Ok(None);
848 }
849
850 let qubits: HashSet<usize> = window.iter().flat_map(|g| &g.qubits).copied().collect();
852 let gate_types: Vec<String> = window
853 .iter()
854 .map(|g| format!("{:?}", g.gate_type))
855 .collect();
856
857 let confidence = self.calculate_fusion_confidence(window);
858
859 let opportunity = FusionOpportunity {
860 description: format!("Fuse {} gates on qubits {:?}", window.len(), qubits),
861 qubits: qubits.into_iter().collect(),
862 gate_types,
863 estimated_benefit: benefit,
864 confidence,
865 };
866
867 Ok(Some(opportunity))
868 }
869
870 fn can_fuse_gate_sequence(&self, gates: &[QuantumGate]) -> Result<bool> {
872 if gates.is_empty() {
873 return Ok(false);
874 }
875
876 for i in 0..gates.len() - 1 {
878 if !gates[i].can_fuse_with(&gates[i + 1]) {
879 if !gates[i].commutes_with(&gates[i + 1]) {
881 return Ok(false);
882 }
883 }
884 }
885
886 let fusion_block = FusedGateBlock::new(gates.to_vec())?;
888 Ok(fusion_block.is_beneficial())
889 }
890
891 fn estimate_fusion_benefit(&self, gates: &[QuantumGate]) -> Result<f64> {
893 let individual_cost: f64 = gates.iter().map(|g| g.cost).sum();
894
895 let fusion_block = FusedGateBlock::new(gates.to_vec())?;
897 let fused_cost = fusion_block.cost;
898
899 Ok(individual_cost / fused_cost)
900 }
901
902 fn calculate_fusion_confidence(&self, gates: &[QuantumGate]) -> f64 {
904 let mut confidence: f64 = 1.0;
905
906 let gate_types: HashSet<_> = gates.iter().map(|g| &g.gate_type).collect();
908 if gate_types.len() > 1 {
909 confidence *= 0.8;
910 }
911
912 let all_qubits: HashSet<_> = gates.iter().flat_map(|g| &g.qubits).collect();
914 if all_qubits.len() > 3 {
915 confidence *= 0.6;
916 }
917
918 if self.is_known_beneficial_pattern(gates) {
920 confidence *= 1.2;
921 }
922
923 confidence.min(1.0)
924 }
925
926 fn is_known_beneficial_pattern(&self, gates: &[QuantumGate]) -> bool {
928 if gates.len() == 2 {
930 if let (Some(g1), Some(g2)) = (gates.first(), gates.get(1)) {
932 match (&g1.gate_type, &g2.gate_type) {
933 (GateType::RotationX, GateType::RotationX)
934 | (GateType::RotationY, GateType::RotationY)
935 | (GateType::RotationZ, GateType::RotationZ) => return true,
936 _ => {}
937 }
938 }
939 }
940
941 if gates.len() == 3 {
943 if matches!(gates[0].gate_type, GateType::CNOT)
945 && gates[1].qubits.len() == 1
946 && matches!(gates[2].gate_type, GateType::CNOT)
947 {
948 return true;
949 }
950 }
951
952 false
953 }
954
955 fn remove_overlapping_opportunities(
957 &self,
958 opportunities: Vec<FusionOpportunity>,
959 ) -> Result<Vec<FusionOpportunity>> {
960 let mut result = Vec::new();
961 let mut used_positions = HashSet::new();
962
963 for opportunity in opportunities {
964 let overlaps = opportunity
966 .qubits
967 .iter()
968 .any(|q| used_positions.contains(q));
969
970 if !overlaps {
971 for &qubit in &opportunity.qubits {
973 used_positions.insert(qubit);
974 }
975 result.push(opportunity);
976 }
977 }
978
979 Ok(result)
980 }
981
982 fn perform_fusion(
984 &mut self,
985 gates: &[QuantumGate],
986 ) -> Result<(Vec<FusedGateBlock>, Vec<QuantumGate>)> {
987 let start_time = std::time::Instant::now();
988
989 #[cfg(feature = "advanced_math")]
990 {
991 if self.optimizer.is_some() {
992 return self.perform_manual_fusion(gates);
994 }
995 }
996
997 self.perform_manual_fusion(gates)
999 }
1000
1001 #[cfg(feature = "advanced_math")]
1002 fn perform_scirs2_fusion(
1003 &mut self,
1004 gates: &[QuantumGate],
1005 _optimizer: &mut Box<dyn std::any::Any>,
1006 ) -> Result<(Vec<FusedGateBlock>, Vec<QuantumGate>)> {
1007 self.perform_manual_fusion(gates)
1010 }
1011
1012 fn perform_manual_fusion(
1014 &mut self,
1015 gates: &[QuantumGate],
1016 ) -> Result<(Vec<FusedGateBlock>, Vec<QuantumGate>)> {
1017 let mut fused_blocks = Vec::new();
1018 let mut remaining_gates = Vec::new();
1019 let mut i = 0;
1020
1021 while i < gates.len() {
1022 let mut best_block_size = 1;
1024 let mut best_benefit = 0.0;
1025
1026 for block_size in 2..=(self.config.max_fusion_size.min(gates.len() - i)) {
1027 let window = &gates[i..i + block_size];
1028
1029 if let Ok(can_fuse) = self.can_fuse_gate_sequence(window) {
1030 if can_fuse {
1031 if let Ok(benefit) = self.estimate_fusion_benefit(window) {
1032 if benefit > best_benefit
1033 && benefit >= self.config.min_benefit_threshold
1034 {
1035 best_block_size = block_size;
1036 best_benefit = benefit;
1037 }
1038 }
1039 }
1040 }
1041 }
1042
1043 if best_block_size > 1 {
1044 let block_gates = gates[i..i + best_block_size].to_vec();
1046
1047 let pattern_key = FusionPatternKey::from_gates(&block_gates)?;
1049
1050 if let Some(cached_result) = self.fusion_cache.get(&pattern_key) {
1052 fused_blocks.push(cached_result.fused_block.clone());
1053 self.cache_hits += 1;
1054 } else {
1055 let fused_block = FusedGateBlock::new(block_gates.clone())?;
1056
1057 let cached_result = CachedFusionResult {
1059 fused_block: fused_block.clone(),
1060 benefit: 1.0, usage_count: 1,
1062 last_accessed: std::time::Instant::now(),
1063 };
1064
1065 if self.fusion_cache.len() < self.config.fusion_cache_size {
1067 self.fusion_cache.insert(pattern_key, cached_result);
1068 }
1069
1070 self.cache_misses += 1;
1071 fused_blocks.push(fused_block);
1072 }
1073
1074 i += best_block_size;
1075 } else {
1076 remaining_gates.push(gates[i].clone());
1078 i += 1;
1079 }
1080 }
1081
1082 Ok((fused_blocks, remaining_gates))
1083 }
1084
1085 fn calculate_circuit_depth(&self, gates: &[QuantumGate]) -> usize {
1087 if gates.is_empty() {
1088 return 0;
1089 }
1090
1091 let mut qubit_last_gate = HashMap::new();
1093 let mut gate_depths = vec![0; gates.len()];
1094
1095 for (i, gate) in gates.iter().enumerate() {
1096 let mut max_dependency_depth = 0;
1097
1098 for &qubit in &gate.qubits {
1099 if let Some(&last_gate_idx) = qubit_last_gate.get(&qubit) {
1100 max_dependency_depth = max_dependency_depth.max(gate_depths[last_gate_idx]);
1101 }
1102 qubit_last_gate.insert(qubit, i);
1103 }
1104
1105 gate_depths[i] = max_dependency_depth + 1;
1106 }
1107
1108 gate_depths.into_iter().max().unwrap_or(0)
1109 }
1110
1111 const fn calculate_fused_circuit_depth(
1113 &self,
1114 blocks: &[FusedGateBlock],
1115 gates: &[QuantumGate],
1116 ) -> usize {
1117 blocks.len() + gates.len()
1119 }
1120
1121 fn calculate_circuit_fingerprint(&self, gates: &[QuantumGate]) -> u64 {
1123 use std::collections::hash_map::DefaultHasher;
1124
1125 let mut hasher = DefaultHasher::new();
1126 gates.hash(&mut hasher);
1127 hasher.finish()
1128 }
1129
1130 #[must_use]
1132 pub fn get_fusion_stats(&self) -> FusionStats {
1133 FusionStats {
1134 cache_size: self.fusion_cache.len(),
1135 cache_hit_rate: self.calculate_cache_hit_rate(),
1136 learning_experiments: self.learning_history.len(),
1137 average_improvement: self.calculate_average_improvement(),
1138 }
1139 }
1140
1141 const fn calculate_cache_hit_rate(&self) -> f64 {
1142 0.0 }
1145
1146 fn calculate_average_improvement(&self) -> f64 {
1147 if self.learning_history.is_empty() {
1148 return 0.0;
1149 }
1150
1151 let total: f64 = self
1152 .learning_history
1153 .iter()
1154 .map(|e| e.performance_improvement)
1155 .sum();
1156 total / self.learning_history.len() as f64
1157 }
1158
1159 pub fn fuse_gates(
1161 &mut self,
1162 gates: &[QuantumGate],
1163 ) -> crate::error::Result<(Vec<FusedGateBlock>, Vec<QuantumGate>)> {
1164 let mut fused_blocks = Vec::new();
1165
1166 if gates.is_empty() {
1167 return Ok((fused_blocks, Vec::new()));
1168 }
1169
1170 let mut i = 0;
1171 while i < gates.len() {
1172 if i + 1 < gates.len() {
1173 let should_fuse = self.should_fuse_basic(&gates[i], &gates[i + 1]);
1175
1176 if should_fuse {
1177 let mut qubits = gates[i].qubits.clone();
1179 qubits.extend_from_slice(&gates[i + 1].qubits);
1180 qubits.sort_unstable();
1181 qubits.dedup();
1182
1183 let fused_block = FusedGateBlock {
1184 gates: vec![gates[i].clone(), gates[i + 1].clone()],
1185 combined_matrix: self
1186 .calculate_combined_matrix(&gates[i], &gates[i + 1])?,
1187 qubits,
1188 cost: 0.5, improvement_factor: 1.5,
1190 };
1191
1192 fused_blocks.push(fused_block);
1193 i += 2; } else {
1195 let fused_block = FusedGateBlock {
1197 gates: vec![gates[i].clone()],
1198 combined_matrix: gates[i].matrix.clone(),
1199 qubits: gates[i].qubits.clone(),
1200 cost: 1.0,
1201 improvement_factor: 1.0,
1202 };
1203
1204 fused_blocks.push(fused_block);
1205 i += 1;
1206 }
1207 } else {
1208 let fused_block = FusedGateBlock {
1210 gates: vec![gates[i].clone()],
1211 combined_matrix: gates[i].matrix.clone(),
1212 qubits: gates[i].qubits.clone(),
1213 cost: 1.0,
1214 improvement_factor: 1.0,
1215 };
1216
1217 fused_blocks.push(fused_block);
1218 i += 1;
1219 }
1220 }
1221
1222 Ok((fused_blocks, Vec::new()))
1223 }
1224
1225 fn should_fuse_basic(&self, gate1: &QuantumGate, gate2: &QuantumGate) -> bool {
1227 let overlapping_qubits = gate1.qubits.iter().any(|&q| gate2.qubits.contains(&q));
1228 overlapping_qubits && gate1.qubits.len() <= 2 && gate2.qubits.len() <= 2
1229 }
1230
1231 fn calculate_combined_matrix(
1233 &self,
1234 gate1: &QuantumGate,
1235 gate2: &QuantumGate,
1236 ) -> crate::error::Result<Array2<Complex64>> {
1237 Ok(gate1.matrix.clone())
1240 }
1241}
1242
1243#[derive(Debug, Clone, Serialize, Deserialize)]
1245pub struct FusionStats {
1246 pub cache_size: usize,
1248 pub cache_hit_rate: f64,
1250 pub learning_experiments: usize,
1252 pub average_improvement: f64,
1254}
1255
1256impl Default for MLFusionPredictor {
1257 fn default() -> Self {
1258 Self::new()
1259 }
1260}
1261
1262impl MLFusionPredictor {
1263 #[must_use]
1265 pub fn new() -> Self {
1266 let mut feature_weights = HashMap::new();
1267
1268 feature_weights.insert("rotation_similarity".to_string(), 0.8);
1270 feature_weights.insert("gate_locality".to_string(), 0.7);
1271 feature_weights.insert("commutation_potential".to_string(), 0.6);
1272 feature_weights.insert("matrix_sparsity".to_string(), 0.5);
1273
1274 Self {
1275 feature_weights,
1276 training_data: Vec::new(),
1277 accuracy: 0.5, }
1279 }
1280
1281 #[must_use]
1283 pub fn predict_benefit(&self, gates: &[QuantumGate]) -> f64 {
1284 let features = self.extract_features(gates);
1285
1286 let mut prediction = 0.0;
1287 for (i, &feature_value) in features.iter().enumerate() {
1288 let weight_key = match i {
1289 0 => "rotation_similarity",
1290 1 => "gate_locality",
1291 2 => "commutation_potential",
1292 3 => "matrix_sparsity",
1293 _ => "default",
1294 };
1295
1296 if let Some(&weight) = self.feature_weights.get(weight_key) {
1297 prediction += feature_value * weight;
1298 }
1299 }
1300
1301 1.0 / (1.0 + (-prediction).exp())
1303 }
1304
1305 fn extract_features(&self, gates: &[QuantumGate]) -> Vec<f64> {
1307 let mut features = [0.0; 4];
1308
1309 if gates.len() < 2 {
1310 return features.to_vec();
1311 }
1312
1313 features[0] = self.calculate_rotation_similarity(gates);
1315
1316 features[1] = self.calculate_gate_locality(gates);
1318
1319 features[2] = self.calculate_commutation_potential(gates);
1321
1322 features[3] = self.calculate_matrix_sparsity(gates);
1324
1325 features.to_vec()
1326 }
1327
1328 fn calculate_rotation_similarity(&self, gates: &[QuantumGate]) -> f64 {
1329 let rotation_gates: Vec<_> = gates
1330 .iter()
1331 .filter(|g| {
1332 matches!(
1333 g.gate_type,
1334 GateType::RotationX | GateType::RotationY | GateType::RotationZ
1335 )
1336 })
1337 .collect();
1338
1339 if rotation_gates.len() < 2 {
1340 return 0.0;
1341 }
1342
1343 let mut similarity_score = 0.0;
1345 for i in 0..rotation_gates.len() - 1 {
1346 for j in i + 1..rotation_gates.len() {
1347 if rotation_gates[i].gate_type == rotation_gates[j].gate_type
1348 && rotation_gates[i].qubits == rotation_gates[j].qubits
1349 {
1350 similarity_score += 1.0;
1351 }
1352 }
1353 }
1354
1355 similarity_score / (rotation_gates.len() as f64)
1356 }
1357
1358 fn calculate_gate_locality(&self, gates: &[QuantumGate]) -> f64 {
1359 let mut locality_score = 0.0;
1360 let mut adjacent_count = 0;
1361
1362 for i in 0..gates.len() - 1 {
1363 let current_qubits: HashSet<_> = gates[i].qubits.iter().copied().collect();
1364 let next_qubits: HashSet<_> = gates[i + 1].qubits.iter().copied().collect();
1365
1366 if current_qubits.intersection(&next_qubits).count() > 0 {
1367 locality_score += 1.0;
1368 }
1369 adjacent_count += 1;
1370 }
1371
1372 if adjacent_count > 0 {
1373 locality_score / f64::from(adjacent_count)
1374 } else {
1375 0.0
1376 }
1377 }
1378
1379 fn calculate_commutation_potential(&self, gates: &[QuantumGate]) -> f64 {
1380 let mut commutation_score = 0.0;
1381 let mut pair_count = 0;
1382
1383 for i in 0..gates.len() - 1 {
1384 for j in i + 1..gates.len() {
1385 let qubits_i: HashSet<_> = gates[i].qubits.iter().copied().collect();
1386 let qubits_j: HashSet<_> = gates[j].qubits.iter().copied().collect();
1387
1388 if qubits_i.intersection(&qubits_j).count() == 0 {
1390 commutation_score += 1.0;
1391 }
1392 pair_count += 1;
1393 }
1394 }
1395
1396 if pair_count > 0 {
1397 commutation_score / f64::from(pair_count)
1398 } else {
1399 0.0
1400 }
1401 }
1402
1403 fn calculate_matrix_sparsity(&self, gates: &[QuantumGate]) -> f64 {
1404 let mut total_sparsity = 0.0;
1405
1406 for gate in gates {
1407 let matrix = &gate.matrix;
1408 let zero_count = matrix.iter().filter(|&&x| x.norm() < 1e-10).count();
1409 let sparsity = zero_count as f64 / (matrix.len() as f64);
1410 total_sparsity += sparsity;
1411 }
1412
1413 total_sparsity / gates.len() as f64
1414 }
1415
1416 pub fn add_training_example(&mut self, example: MLTrainingExample) {
1418 self.training_data.push(example);
1419
1420 if self.training_data.len() % 10 == 0 {
1422 self.update_weights();
1423 }
1424 }
1425
1426 fn update_weights(&mut self) {
1427 let learning_rate = 0.01;
1429
1430 for example in &self.training_data {
1431 if let Some(observed) = example.observed_benefit {
1432 let predicted = self.predict_benefit_from_features(&example.features);
1433 let error = observed - predicted;
1434
1435 for (i, &feature_value) in example.features.iter().enumerate() {
1437 let weight_key = match i {
1438 0 => "rotation_similarity",
1439 1 => "gate_locality",
1440 2 => "commutation_potential",
1441 3 => "matrix_sparsity",
1442 _ => continue,
1443 };
1444
1445 if let Some(weight) = self.feature_weights.get_mut(weight_key) {
1446 *weight += learning_rate * error * feature_value;
1447 }
1448 }
1449 }
1450 }
1451 }
1452
1453 fn predict_benefit_from_features(&self, features: &[f64]) -> f64 {
1454 let mut prediction = 0.0;
1455 for (i, &feature_value) in features.iter().enumerate() {
1456 let weight_key = match i {
1457 0 => "rotation_similarity",
1458 1 => "gate_locality",
1459 2 => "commutation_potential",
1460 3 => "matrix_sparsity",
1461 _ => "default",
1462 };
1463
1464 if let Some(&weight) = self.feature_weights.get(weight_key) {
1465 prediction += feature_value * weight;
1466 }
1467 }
1468
1469 1.0 / (1.0 + (-prediction).exp())
1470 }
1471
1472 fn should_fuse_gates(&self, gate1: &QuantumGate, gate2: &QuantumGate) -> FusionResult {
1474 let overlapping_qubits = gate1.qubits.iter().any(|&q| gate2.qubits.contains(&q));
1476 let should_fuse = overlapping_qubits && gate1.qubits.len() <= 2 && gate2.qubits.len() <= 2;
1477
1478 FusionResult {
1479 should_fuse,
1480 confidence: if should_fuse { 0.8 } else { 0.2 },
1481 expected_speedup: if should_fuse { 1.5 } else { 1.0 },
1482 estimated_error: 0.01,
1483 }
1484 }
1485}
1486
1487impl Default for CircuitPatternAnalyzer {
1488 fn default() -> Self {
1489 Self::new()
1490 }
1491}
1492
1493impl CircuitPatternAnalyzer {
1494 #[must_use]
1496 pub fn new() -> Self {
1497 let mut beneficial_patterns = HashMap::new();
1498
1499 beneficial_patterns.insert("RX-RX".to_string(), 0.9);
1501 beneficial_patterns.insert("RY-RY".to_string(), 0.9);
1502 beneficial_patterns.insert("RZ-RZ".to_string(), 0.9);
1503 beneficial_patterns.insert("H-CNOT-H".to_string(), 0.8);
1504 beneficial_patterns.insert("CNOT-RZ-CNOT".to_string(), 0.7);
1505
1506 Self {
1507 beneficial_patterns,
1508 pattern_history: Vec::new(),
1509 }
1510 }
1511
1512 pub fn analyze_pattern(&mut self, gates: &[QuantumGate]) -> PatternRecognitionResult {
1514 let pattern_string = self.create_pattern_string(gates);
1515
1516 let (confidence, expected_benefit) = if let Some(&benefit) =
1517 self.beneficial_patterns.get(&pattern_string)
1518 {
1519 (0.9, benefit)
1520 } else {
1521 let (partial_confidence, partial_benefit) = self.find_partial_matches(&pattern_string);
1523 (partial_confidence, partial_benefit)
1524 };
1525
1526 let result = PatternRecognitionResult {
1527 pattern: pattern_string,
1528 confidence,
1529 expected_benefit,
1530 };
1531
1532 self.pattern_history.push(result.clone());
1533 result
1534 }
1535
1536 fn create_pattern_string(&self, gates: &[QuantumGate]) -> String {
1537 gates
1538 .iter()
1539 .map(|g| format!("{:?}", g.gate_type))
1540 .collect::<Vec<_>>()
1541 .join("-")
1542 }
1543
1544 fn find_partial_matches(&self, pattern: &str) -> (f64, f64) {
1545 let mut best_confidence = 0.0;
1546 let mut best_benefit = 0.0;
1547
1548 for (known_pattern, &benefit) in &self.beneficial_patterns {
1549 let similarity = self.calculate_pattern_similarity(pattern, known_pattern);
1550 if similarity > best_confidence {
1551 best_confidence = similarity;
1552 best_benefit = benefit * similarity; }
1554 }
1555
1556 (best_confidence, best_benefit)
1557 }
1558
1559 fn calculate_pattern_similarity(&self, pattern1: &str, pattern2: &str) -> f64 {
1560 let gates1: Vec<&str> = pattern1.split('-').collect();
1561 let gates2: Vec<&str> = pattern2.split('-').collect();
1562
1563 let max_len = gates1.len().max(gates2.len()) as f64;
1564 if max_len == 0.0 {
1565 return 0.0;
1566 }
1567
1568 let common_count = gates1.iter().filter(|&g| gates2.contains(g)).count() as f64;
1569
1570 common_count / max_len
1571 }
1572
1573 pub fn learn_pattern(&mut self, pattern: String, observed_benefit: f64) {
1575 let alpha: f64 = 0.1; if let Some(current_benefit) = self.beneficial_patterns.get_mut(&pattern) {
1579 *current_benefit = (1.0 - alpha).mul_add(*current_benefit, alpha * observed_benefit);
1580 } else {
1581 self.beneficial_patterns.insert(pattern, observed_benefit);
1582 }
1583 }
1584}
1585
1586pub struct FusionUtils;
1588
1589impl FusionUtils {
1590 #[must_use]
1592 pub fn create_test_sequence(sequence_type: &str, num_qubits: usize) -> Vec<QuantumGate> {
1593 match sequence_type {
1594 "rotation_chain" => (0..num_qubits)
1595 .flat_map(|q| {
1596 vec![
1597 QuantumGate::new(
1598 GateType::RotationX,
1599 vec![q],
1600 vec![std::f64::consts::PI / 4.0],
1601 ),
1602 QuantumGate::new(
1603 GateType::RotationY,
1604 vec![q],
1605 vec![std::f64::consts::PI / 3.0],
1606 ),
1607 QuantumGate::new(
1608 GateType::RotationZ,
1609 vec![q],
1610 vec![std::f64::consts::PI / 6.0],
1611 ),
1612 ]
1613 })
1614 .collect(),
1615 "cnot_ladder" => (0..num_qubits - 1)
1616 .map(|q| QuantumGate::new(GateType::CNOT, vec![q, q + 1], vec![]))
1617 .collect(),
1618 "mixed_gates" => {
1619 let mut gates = Vec::new();
1620 for q in 0..num_qubits {
1621 gates.push(QuantumGate::new(GateType::Hadamard, vec![q], vec![]));
1622 if q > 0 {
1623 gates.push(QuantumGate::new(GateType::CNOT, vec![q - 1, q], vec![]));
1624 }
1625 gates.push(QuantumGate::new(
1626 GateType::RotationZ,
1627 vec![q],
1628 vec![std::f64::consts::PI / 8.0],
1629 ));
1630 }
1631 gates
1632 }
1633 _ => vec![QuantumGate::new(GateType::Identity, vec![0], vec![])],
1634 }
1635 }
1636
1637 pub fn benchmark_fusion_strategies(
1639 gates: &[QuantumGate],
1640 strategies: &[FusionStrategy],
1641 ) -> Result<HashMap<String, CircuitAnalysis>> {
1642 let mut results = HashMap::new();
1643
1644 for &strategy in strategies {
1645 let config = AdaptiveFusionConfig {
1646 strategy,
1647 ..Default::default()
1648 };
1649
1650 let mut fusion_engine = AdaptiveGateFusion::new(config)?;
1651 let analysis = fusion_engine.analyze_circuit(gates)?;
1652
1653 results.insert(format!("{strategy:?}"), analysis);
1654 }
1655
1656 Ok(results)
1657 }
1658
1659 #[must_use]
1661 pub fn estimate_fusion_potential(gates: &[QuantumGate]) -> f64 {
1662 if gates.len() < 2 {
1663 return 0.0;
1664 }
1665
1666 let mut potential_fusions = 0;
1667 let mut total_gates = gates.len();
1668
1669 for window in gates.windows(2) {
1671 if window[0].can_fuse_with(&window[1]) {
1672 potential_fusions += 1;
1673 }
1674 }
1675
1676 f64::from(potential_fusions) / total_gates as f64
1677 }
1678}
1679
1680#[cfg(test)]
1681mod tests {
1682 use super::*;
1683 use approx::assert_abs_diff_eq;
1684
1685 #[test]
1686 fn test_quantum_gate_creation() {
1687 let gate = QuantumGate::new(GateType::PauliX, vec![0], vec![]);
1688 assert_eq!(gate.gate_type, GateType::PauliX);
1689 assert_eq!(gate.qubits, vec![0]);
1690 assert!(gate.cost > 0.0);
1691 }
1692
1693 #[test]
1694 fn test_gate_commutation() {
1695 let gate1 = QuantumGate::new(GateType::PauliX, vec![0], vec![]);
1696 let gate2 = QuantumGate::new(GateType::PauliY, vec![1], vec![]);
1697 let gate3 = QuantumGate::new(GateType::PauliX, vec![0], vec![]);
1698
1699 assert!(gate1.commutes_with(&gate2)); assert!(gate1.commutes_with(&gate3)); }
1702
1703 #[test]
1704 fn test_gate_fusion_compatibility() {
1705 let gate1 = QuantumGate::new(GateType::RotationX, vec![0], vec![0.5]);
1706 let gate2 = QuantumGate::new(GateType::RotationX, vec![0], vec![0.3]);
1707 let gate3 = QuantumGate::new(GateType::RotationY, vec![1], vec![0.2]);
1708
1709 assert!(gate1.can_fuse_with(&gate2)); assert!(!gate1.can_fuse_with(&gate3)); }
1712
1713 #[test]
1714 fn test_fused_gate_block() {
1715 let gates = vec![
1716 QuantumGate::new(GateType::RotationX, vec![0], vec![0.5]),
1717 QuantumGate::new(GateType::RotationX, vec![0], vec![0.3]),
1718 ];
1719
1720 let block =
1721 FusedGateBlock::new(gates).expect("Fused gate block creation should succeed in test");
1722 assert_eq!(block.qubits, vec![0]);
1723 assert!(block.improvement_factor > 0.0);
1724 }
1725
1726 #[test]
1727 fn test_adaptive_fusion_config() {
1728 let config = AdaptiveFusionConfig::default();
1729 assert_eq!(config.strategy, FusionStrategy::Adaptive);
1730 assert_eq!(config.max_fusion_size, 8);
1731 assert!(config.enable_cross_qubit_fusion);
1732 }
1733
1734 #[test]
1735 fn test_circuit_analysis() {
1736 let gates = FusionUtils::create_test_sequence("rotation_chain", 2);
1737
1738 let config = AdaptiveFusionConfig::default();
1739 let mut fusion_engine =
1740 AdaptiveGateFusion::new(config).expect("Fusion engine creation should succeed in test");
1741
1742 let analysis = fusion_engine
1743 .analyze_circuit(&gates)
1744 .expect("Circuit analysis should succeed in test");
1745 assert_eq!(analysis.original_gate_count, gates.len());
1746 assert!(!analysis.fusion_opportunities.is_empty());
1747 }
1748
1749 #[test]
1750 fn test_fusion_utils_test_sequences() {
1751 let rotation_chain = FusionUtils::create_test_sequence("rotation_chain", 2);
1752 assert_eq!(rotation_chain.len(), 6); let cnot_ladder = FusionUtils::create_test_sequence("cnot_ladder", 3);
1755 assert_eq!(cnot_ladder.len(), 2); let mixed_gates = FusionUtils::create_test_sequence("mixed_gates", 2);
1758 assert!(!mixed_gates.is_empty());
1759 }
1760
1761 #[test]
1762 fn test_fusion_potential_estimation() {
1763 let gates = vec![
1764 QuantumGate::new(GateType::RotationX, vec![0], vec![0.1]),
1765 QuantumGate::new(GateType::RotationX, vec![0], vec![0.2]),
1766 QuantumGate::new(GateType::RotationY, vec![1], vec![0.3]),
1767 ];
1768
1769 let potential = FusionUtils::estimate_fusion_potential(&gates);
1770 assert!(potential > 0.0);
1771 assert!(potential <= 1.0);
1772 }
1773
1774 #[test]
1775 fn test_gate_matrix_generation() {
1776 let pauli_x = QuantumGate::new(GateType::PauliX, vec![0], vec![]);
1777 assert_eq!(pauli_x.matrix.shape(), &[2, 2]);
1778
1779 assert_abs_diff_eq!(pauli_x.matrix[[0, 1]].re, 1.0, epsilon = 1e-10);
1781 assert_abs_diff_eq!(pauli_x.matrix[[1, 0]].re, 1.0, epsilon = 1e-10);
1782 }
1783
1784 #[test]
1785 fn test_circuit_depth_calculation() {
1786 let gates = vec![
1787 QuantumGate::new(GateType::Hadamard, vec![0], vec![]),
1788 QuantumGate::new(GateType::CNOT, vec![0, 1], vec![]),
1789 QuantumGate::new(GateType::RotationZ, vec![1], vec![0.5]),
1790 ];
1791
1792 let config = AdaptiveFusionConfig::default();
1793 let fusion_engine =
1794 AdaptiveGateFusion::new(config).expect("Fusion engine creation should succeed in test");
1795
1796 let depth = fusion_engine.calculate_circuit_depth(&gates);
1797 assert!(depth > 0);
1798 }
1799}