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: 10000,
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 pub fn new(gate_type: GateType, qubits: Vec<usize>, parameters: Vec<f64>) -> Self {
120 let matrix = Self::gate_matrix(&gate_type, ¶meters);
121 let cost = Self::estimate_cost(&gate_type, qubits.len());
122
123 Self {
124 gate_type,
125 qubits,
126 parameters,
127 matrix,
128 position: 0,
129 cost,
130 }
131 }
132
133 fn gate_matrix(gate_type: &GateType, parameters: &[f64]) -> Array2<Complex64> {
135 match gate_type {
136 GateType::Identity => Array2::eye(2),
137 GateType::PauliX => Array2::from_shape_vec(
138 (2, 2),
139 vec![
140 Complex64::new(0.0, 0.0),
141 Complex64::new(1.0, 0.0),
142 Complex64::new(1.0, 0.0),
143 Complex64::new(0.0, 0.0),
144 ],
145 )
146 .unwrap(),
147 GateType::PauliY => Array2::from_shape_vec(
148 (2, 2),
149 vec![
150 Complex64::new(0.0, 0.0),
151 Complex64::new(0.0, -1.0),
152 Complex64::new(0.0, 1.0),
153 Complex64::new(0.0, 0.0),
154 ],
155 )
156 .unwrap(),
157 GateType::PauliZ => Array2::from_shape_vec(
158 (2, 2),
159 vec![
160 Complex64::new(1.0, 0.0),
161 Complex64::new(0.0, 0.0),
162 Complex64::new(0.0, 0.0),
163 Complex64::new(-1.0, 0.0),
164 ],
165 )
166 .unwrap(),
167 GateType::Hadamard => {
168 let inv_sqrt2 = 1.0 / 2.0_f64.sqrt();
169 Array2::from_shape_vec(
170 (2, 2),
171 vec![
172 Complex64::new(inv_sqrt2, 0.0),
173 Complex64::new(inv_sqrt2, 0.0),
174 Complex64::new(inv_sqrt2, 0.0),
175 Complex64::new(-inv_sqrt2, 0.0),
176 ],
177 )
178 .unwrap()
179 }
180 GateType::RotationX => {
181 let theta = parameters.first().copied().unwrap_or(0.0);
182 let cos_half = (theta / 2.0).cos();
183 let sin_half = (theta / 2.0).sin();
184 Array2::from_shape_vec(
185 (2, 2),
186 vec![
187 Complex64::new(cos_half, 0.0),
188 Complex64::new(0.0, -sin_half),
189 Complex64::new(0.0, -sin_half),
190 Complex64::new(cos_half, 0.0),
191 ],
192 )
193 .unwrap()
194 }
195 GateType::RotationY => {
196 let theta = parameters.first().copied().unwrap_or(0.0);
197 let cos_half = (theta / 2.0).cos();
198 let sin_half = (theta / 2.0).sin();
199 Array2::from_shape_vec(
200 (2, 2),
201 vec![
202 Complex64::new(cos_half, 0.0),
203 Complex64::new(-sin_half, 0.0),
204 Complex64::new(sin_half, 0.0),
205 Complex64::new(cos_half, 0.0),
206 ],
207 )
208 .unwrap()
209 }
210 GateType::RotationZ => {
211 let theta = parameters.first().copied().unwrap_or(0.0);
212 let exp_neg = Complex64::new(0.0, -theta / 2.0).exp();
213 let exp_pos = Complex64::new(0.0, theta / 2.0).exp();
214 Array2::from_shape_vec(
215 (2, 2),
216 vec![
217 exp_neg,
218 Complex64::new(0.0, 0.0),
219 Complex64::new(0.0, 0.0),
220 exp_pos,
221 ],
222 )
223 .unwrap()
224 }
225 GateType::CNOT => Array2::from_shape_vec(
226 (4, 4),
227 vec![
228 Complex64::new(1.0, 0.0),
229 Complex64::new(0.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(1.0, 0.0),
234 Complex64::new(0.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(1.0, 0.0),
240 Complex64::new(0.0, 0.0),
241 Complex64::new(0.0, 0.0),
242 Complex64::new(1.0, 0.0),
243 Complex64::new(0.0, 0.0),
244 ],
245 )
246 .unwrap(),
247 _ => Array2::eye(2), }
249 }
250
251 fn estimate_cost(gate_type: &GateType, num_qubits: usize) -> f64 {
253 let base_cost = match gate_type {
254 GateType::Identity => 0.1,
255 GateType::PauliX | GateType::PauliY | GateType::PauliZ => 1.0,
256 GateType::Hadamard => 1.2,
257 GateType::Phase | GateType::T => 1.1,
258 GateType::RotationX | GateType::RotationY | GateType::RotationZ => 1.5,
259 GateType::CNOT | GateType::CZ => 2.0,
260 GateType::SWAP | GateType::ISwap => 2.5,
261 GateType::Toffoli => 4.0,
262 GateType::Fredkin => 4.5,
263 GateType::Custom(_) => 3.0,
264 };
265
266 base_cost * (1 << num_qubits) as f64
268 }
269
270 pub fn commutes_with(&self, other: &Self) -> bool {
272 let self_qubits: HashSet<_> = self.qubits.iter().collect();
274 let other_qubits: HashSet<_> = other.qubits.iter().collect();
275
276 if self_qubits.is_disjoint(&other_qubits) {
277 return true;
278 }
279
280 if self.qubits == other.qubits {
282 return self.check_specific_commutation(other);
283 }
284
285 false
286 }
287
288 const fn check_specific_commutation(&self, other: &Self) -> bool {
290 match (&self.gate_type, &other.gate_type) {
291 (GateType::PauliX, GateType::PauliX)
293 | (GateType::PauliY, GateType::PauliY)
294 | (GateType::PauliZ, GateType::PauliZ) => true,
295 (GateType::PauliZ, GateType::RotationZ) | (GateType::RotationZ, GateType::PauliZ) => {
297 true
298 }
299 (GateType::PauliX, GateType::RotationX) | (GateType::RotationX, GateType::PauliX) => {
301 true
302 }
303 (GateType::PauliY, GateType::RotationY) | (GateType::RotationY, GateType::PauliY) => {
305 true
306 }
307 (GateType::RotationX, GateType::RotationX)
309 | (GateType::RotationY, GateType::RotationY)
310 | (GateType::RotationZ, GateType::RotationZ) => true,
311 (GateType::Identity, _) | (_, GateType::Identity) => true,
313 _ => false,
314 }
315 }
316
317 pub fn can_fuse_with(&self, other: &Self) -> bool {
319 if self.qubits != other.qubits {
321 return false;
322 }
323
324 if self.qubits.len() == 1 && other.qubits.len() == 1 {
326 return true;
327 }
328
329 if self.qubits.len() == 2 && other.qubits.len() == 2 {
331 return self.check_two_qubit_fusion_compatibility(other);
332 }
333
334 false
335 }
336
337 const fn check_two_qubit_fusion_compatibility(&self, other: &Self) -> bool {
339 match (&self.gate_type, &other.gate_type) {
340 (GateType::CNOT, GateType::CNOT) => true,
342 (GateType::CZ, GateType::CZ) => true,
344 (GateType::CNOT, GateType::CZ) | (GateType::CZ, GateType::CNOT) => true,
346 _ => false,
347 }
348 }
349}
350
351impl Hash for QuantumGate {
352 fn hash<H: Hasher>(&self, state: &mut H) {
353 self.gate_type.hash(state);
354 self.qubits.hash(state);
355 for ¶m in &self.parameters {
357 ((param * 1000.0).round() as i64).hash(state);
358 }
359 }
360}
361
362impl PartialEq for QuantumGate {
363 fn eq(&self, other: &Self) -> bool {
364 self.gate_type == other.gate_type
365 && self.qubits == other.qubits
366 && self.parameters.len() == other.parameters.len()
367 && self
368 .parameters
369 .iter()
370 .zip(other.parameters.iter())
371 .all(|(&a, &b)| (a - b).abs() < 1e-10)
372 }
373}
374
375impl Eq for QuantumGate {}
376
377#[derive(Debug, Clone)]
379pub struct FusedGateBlock {
380 pub gates: Vec<QuantumGate>,
382 pub combined_matrix: Array2<Complex64>,
384 pub qubits: Vec<usize>,
386 pub cost: f64,
388 pub improvement_factor: f64,
390}
391
392impl FusedGateBlock {
393 pub fn new(gates: Vec<QuantumGate>) -> Result<Self> {
395 if gates.is_empty() {
396 return Err(SimulatorError::InvalidInput(
397 "Cannot create empty gate block".to_string(),
398 ));
399 }
400
401 let mut qubits = HashSet::new();
403 for gate in &gates {
404 qubits.extend(&gate.qubits);
405 }
406 let mut qubit_vec: Vec<usize> = qubits.into_iter().collect();
407 qubit_vec.sort_unstable();
408
409 let combined_matrix = Self::calculate_combined_matrix(&gates, &qubit_vec)?;
411
412 let individual_cost: f64 = gates.iter().map(|g| g.cost).sum();
414 let fused_cost = Self::estimate_fused_cost(&combined_matrix);
415 let improvement_factor = individual_cost / fused_cost;
416
417 Ok(Self {
418 gates,
419 combined_matrix,
420 qubits: qubit_vec,
421 cost: fused_cost,
422 improvement_factor,
423 })
424 }
425
426 fn calculate_combined_matrix(
428 gates: &[QuantumGate],
429 qubits: &[usize],
430 ) -> Result<Array2<Complex64>> {
431 let num_qubits = qubits.len();
432 let matrix_size = 1 << num_qubits;
433 let mut combined = Array2::eye(matrix_size);
434
435 for gate in gates {
436 let gate_matrix = Self::expand_gate_matrix(&gate.matrix, &gate.qubits, qubits)?;
437 combined = gate_matrix.dot(&combined);
438 }
439
440 Ok(combined)
441 }
442
443 fn expand_gate_matrix(
445 gate_matrix: &Array2<Complex64>,
446 gate_qubits: &[usize],
447 all_qubits: &[usize],
448 ) -> Result<Array2<Complex64>> {
449 let num_all_qubits = all_qubits.len();
450 let full_size = 1 << num_all_qubits;
451 let gate_size = 1 << gate_qubits.len();
452
453 if gate_matrix.nrows() != gate_size || gate_matrix.ncols() != gate_size {
454 return Err(SimulatorError::DimensionMismatch(
455 "Gate matrix size doesn't match number of qubits".to_string(),
456 ));
457 }
458
459 let mut expanded = Array2::eye(full_size);
460
461 let mut qubit_mapping = HashMap::new();
463 for (gate_pos, &qubit) in gate_qubits.iter().enumerate() {
464 if let Some(all_pos) = all_qubits.iter().position(|&q| q == qubit) {
465 qubit_mapping.insert(gate_pos, all_pos);
466 } else {
467 return Err(SimulatorError::InvalidInput(format!(
468 "Gate qubit {qubit} not found in qubit list"
469 )));
470 }
471 }
472
473 for i in 0..full_size {
475 for j in 0..full_size {
476 let mut gate_i = 0;
478 let mut gate_j = 0;
479 let mut valid = true;
480
481 for (gate_pos, &all_pos) in &qubit_mapping {
482 let bit_i = (i >> (num_all_qubits - 1 - all_pos)) & 1;
483 let bit_j = (j >> (num_all_qubits - 1 - all_pos)) & 1;
484
485 gate_i |= bit_i << (gate_qubits.len() - 1 - gate_pos);
486 gate_j |= bit_j << (gate_qubits.len() - 1 - gate_pos);
487 }
488
489 for all_pos in 0..num_all_qubits {
491 if !qubit_mapping.values().any(|&pos| pos == all_pos) {
492 let bit_i = (i >> (num_all_qubits - 1 - all_pos)) & 1;
493 let bit_j = (j >> (num_all_qubits - 1 - all_pos)) & 1;
494 if bit_i != bit_j {
495 valid = false;
496 break;
497 }
498 }
499 }
500
501 if valid {
502 expanded[[i, j]] = gate_matrix[[gate_i, gate_j]];
503 } else {
504 expanded[[i, j]] = if i == j {
505 Complex64::new(1.0, 0.0)
506 } else {
507 Complex64::new(0.0, 0.0)
508 };
509 }
510 }
511 }
512
513 Ok(expanded)
514 }
515
516 fn estimate_fused_cost(matrix: &Array2<Complex64>) -> f64 {
518 let size = matrix.nrows();
520 let base_cost = (size as f64).log2() * size as f64;
521
522 let fusion_overhead = 0.1 * base_cost;
524
525 base_cost + fusion_overhead
526 }
527
528 pub fn is_beneficial(&self) -> bool {
530 self.improvement_factor > 1.1 }
532}
533
534#[derive(Debug, Clone, Serialize, Deserialize)]
536pub struct CircuitAnalysis {
537 pub original_gate_count: usize,
539 pub fused_gate_count: usize,
541 pub fusion_blocks: usize,
543 pub performance_improvement: f64,
545 pub gate_distribution: HashMap<String, usize>,
547 pub fusion_opportunities: Vec<FusionOpportunity>,
549 pub circuit_depth: (usize, usize),
551}
552
553#[derive(Debug, Clone, Serialize, Deserialize)]
555pub struct FusionOpportunity {
556 pub description: String,
558 pub qubits: Vec<usize>,
560 pub gate_types: Vec<String>,
562 pub estimated_benefit: f64,
564 pub confidence: f64,
566}
567
568pub struct AdaptiveGateFusion {
570 config: AdaptiveFusionConfig,
572 backend: Option<SciRS2Backend>,
574 fusion_cache: HashMap<FusionPatternKey, CachedFusionResult>,
576 learning_history: Vec<FusionExperiment>,
578 #[cfg(feature = "advanced_math")]
580 optimizer: Option<Box<dyn std::any::Any>>, ml_predictor: Option<MLFusionPredictor>,
583 cache_hits: usize,
585 cache_misses: usize,
586 pattern_analyzer: CircuitPatternAnalyzer,
588}
589
590#[derive(Debug, Clone, PartialEq, Eq, Hash)]
592pub struct FusionPatternKey {
593 gate_types: Vec<GateType>,
595 num_qubits: usize,
597 parameter_hash: u64,
599}
600
601impl FusionPatternKey {
602 pub fn from_gates(gates: &[QuantumGate]) -> Result<Self> {
604 let gate_types: Vec<GateType> = gates.iter().map(|g| g.gate_type.clone()).collect();
605
606 let mut qubits = std::collections::HashSet::new();
608 for gate in gates {
609 for &qubit in &gate.qubits {
610 qubits.insert(qubit);
611 }
612 }
613 let num_qubits = qubits.len();
614
615 let mut parameter_hash = 0u64;
617 for gate in gates {
618 for ¶m in &gate.parameters {
619 parameter_hash = parameter_hash.wrapping_add((param * 1000.0) as u64);
620 }
621 }
622
623 Ok(Self {
624 gate_types,
625 num_qubits,
626 parameter_hash,
627 })
628 }
629}
630
631#[derive(Debug, Clone)]
633pub struct CachedFusionResult {
634 fused_block: FusedGateBlock,
636 benefit: f64,
638 usage_count: usize,
640 last_accessed: std::time::Instant,
642}
643
644pub struct MLFusionPredictor {
646 feature_weights: HashMap<String, f64>,
648 training_data: Vec<MLTrainingExample>,
650 accuracy: f64,
652}
653
654#[derive(Debug, Clone)]
656pub struct MLTrainingExample {
657 features: Vec<f64>,
659 benefit: f64,
661 observed_benefit: Option<f64>,
663}
664
665pub struct CircuitPatternAnalyzer {
667 beneficial_patterns: HashMap<String, f64>,
669 pattern_history: Vec<PatternRecognitionResult>,
671}
672
673#[derive(Debug, Clone)]
675pub struct PatternRecognitionResult {
676 pub pattern: String,
678 pub confidence: f64,
680 pub expected_benefit: f64,
682}
683
684#[derive(Debug, Clone)]
686pub struct FusionResult {
687 pub should_fuse: bool,
689 pub confidence: f64,
691 pub expected_speedup: f64,
693 pub estimated_error: f64,
695}
696
697#[derive(Debug, Clone)]
699struct FusionExperiment {
700 circuit_fingerprint: u64,
701 fusion_strategy: FusionStrategy,
702 performance_improvement: f64,
703 execution_time_ms: f64,
704}
705
706impl AdaptiveGateFusion {
707 pub fn new(config: AdaptiveFusionConfig) -> Result<Self> {
709 Ok(Self {
710 config,
711 backend: None,
712 fusion_cache: HashMap::new(),
713 learning_history: Vec::new(),
714 ml_predictor: None,
715 cache_hits: 0,
716 cache_misses: 0,
717 pattern_analyzer: CircuitPatternAnalyzer::new(),
718 #[cfg(feature = "advanced_math")]
719 optimizer: None,
720 })
721 }
722
723 pub fn with_backend(mut self) -> Result<Self> {
725 self.backend = Some(SciRS2Backend::new());
726
727 #[cfg(feature = "advanced_math")]
728 {
729 let strategy = match self.config.strategy {
730 FusionStrategy::Aggressive => OptimizationStrategy::MinimizeTime,
731 FusionStrategy::Conservative => OptimizationStrategy::MinimizeLength,
732 FusionStrategy::Balanced => OptimizationStrategy::Balanced,
733 FusionStrategy::Adaptive => OptimizationStrategy::MinimizeCrossings,
734 FusionStrategy::Custom => OptimizationStrategy::Balanced,
735 };
736
737 self.optimizer = Some(Box::new(strategy));
739 }
740
741 Ok(self)
742 }
743
744 pub fn analyze_circuit(&mut self, gates: &[QuantumGate]) -> Result<CircuitAnalysis> {
746 let start_time = std::time::Instant::now();
747
748 let original_gate_count = gates.len();
750 let original_depth = self.calculate_circuit_depth(gates);
751
752 let fusion_opportunities = self.identify_fusion_opportunities(gates)?;
754
755 let (fused_blocks, remaining_gates) = self.perform_fusion(gates)?;
757 let fused_gate_count = fused_blocks.len() + remaining_gates.len();
758 let fused_depth = self.calculate_fused_circuit_depth(&fused_blocks, &remaining_gates);
759
760 let original_cost: f64 = gates.iter().map(|g| g.cost).sum();
762 let fused_cost: f64 = fused_blocks.iter().map(|b| b.cost).sum::<f64>()
763 + remaining_gates.iter().map(|g| g.cost).sum::<f64>();
764 let performance_improvement = original_cost / fused_cost;
765
766 let mut gate_distribution = HashMap::new();
768 for gate in gates {
769 let gate_name = format!("{:?}", gate.gate_type);
770 *gate_distribution.entry(gate_name).or_insert(0) += 1;
771 }
772
773 let analysis = CircuitAnalysis {
774 original_gate_count,
775 fused_gate_count,
776 fusion_blocks: fused_blocks.len(),
777 performance_improvement,
778 gate_distribution,
779 fusion_opportunities,
780 circuit_depth: (original_depth, fused_depth),
781 };
782
783 if self.config.enable_ml_predictions {
785 let experiment = FusionExperiment {
786 circuit_fingerprint: self.calculate_circuit_fingerprint(gates),
787 fusion_strategy: self.config.strategy,
788 performance_improvement,
789 execution_time_ms: start_time.elapsed().as_secs_f64() * 1000.0,
790 };
791 self.learning_history.push(experiment);
792 }
793
794 Ok(analysis)
795 }
796
797 pub fn fuse_circuit(&mut self, gates: &[QuantumGate]) -> Result<Vec<FusedGateBlock>> {
799 let (fused_blocks, _) = self.perform_fusion(gates)?;
800 Ok(fused_blocks)
801 }
802
803 fn identify_fusion_opportunities(
805 &self,
806 gates: &[QuantumGate],
807 ) -> Result<Vec<FusionOpportunity>> {
808 let mut opportunities = Vec::new();
809
810 for window_size in 2..=self.config.max_fusion_size {
812 for window in gates.windows(window_size) {
813 if let Some(opportunity) = self.analyze_gate_window(window)? {
814 opportunities.push(opportunity);
815 }
816 }
817 }
818
819 opportunities.sort_by(|a, b| {
821 b.estimated_benefit
822 .partial_cmp(&a.estimated_benefit)
823 .unwrap()
824 });
825 self.remove_overlapping_opportunities(opportunities)
826 }
827
828 fn analyze_gate_window(&self, window: &[QuantumGate]) -> Result<Option<FusionOpportunity>> {
830 if window.len() < 2 {
831 return Ok(None);
832 }
833
834 let can_fuse = self.can_fuse_gate_sequence(window)?;
836 if !can_fuse {
837 return Ok(None);
838 }
839
840 let benefit = self.estimate_fusion_benefit(window)?;
842 if benefit < self.config.min_benefit_threshold {
843 return Ok(None);
844 }
845
846 let qubits: HashSet<usize> = window.iter().flat_map(|g| &g.qubits).copied().collect();
848 let gate_types: Vec<String> = window
849 .iter()
850 .map(|g| format!("{:?}", g.gate_type))
851 .collect();
852
853 let confidence = self.calculate_fusion_confidence(window);
854
855 let opportunity = FusionOpportunity {
856 description: format!("Fuse {} gates on qubits {:?}", window.len(), qubits),
857 qubits: qubits.into_iter().collect(),
858 gate_types,
859 estimated_benefit: benefit,
860 confidence,
861 };
862
863 Ok(Some(opportunity))
864 }
865
866 fn can_fuse_gate_sequence(&self, gates: &[QuantumGate]) -> Result<bool> {
868 if gates.is_empty() {
869 return Ok(false);
870 }
871
872 for i in 0..gates.len() - 1 {
874 if !gates[i].can_fuse_with(&gates[i + 1]) {
875 if !gates[i].commutes_with(&gates[i + 1]) {
877 return Ok(false);
878 }
879 }
880 }
881
882 let fusion_block = FusedGateBlock::new(gates.to_vec())?;
884 Ok(fusion_block.is_beneficial())
885 }
886
887 fn estimate_fusion_benefit(&self, gates: &[QuantumGate]) -> Result<f64> {
889 let individual_cost: f64 = gates.iter().map(|g| g.cost).sum();
890
891 let fusion_block = FusedGateBlock::new(gates.to_vec())?;
893 let fused_cost = fusion_block.cost;
894
895 Ok(individual_cost / fused_cost)
896 }
897
898 fn calculate_fusion_confidence(&self, gates: &[QuantumGate]) -> f64 {
900 let mut confidence: f64 = 1.0;
901
902 let gate_types: HashSet<_> = gates.iter().map(|g| &g.gate_type).collect();
904 if gate_types.len() > 1 {
905 confidence *= 0.8;
906 }
907
908 let all_qubits: HashSet<_> = gates.iter().flat_map(|g| &g.qubits).collect();
910 if all_qubits.len() > 3 {
911 confidence *= 0.6;
912 }
913
914 if self.is_known_beneficial_pattern(gates) {
916 confidence *= 1.2;
917 }
918
919 confidence.min(1.0)
920 }
921
922 fn is_known_beneficial_pattern(&self, gates: &[QuantumGate]) -> bool {
924 if gates.len() == 2 {
926 if let (Some(g1), Some(g2)) = (gates.first(), gates.get(1)) {
928 match (&g1.gate_type, &g2.gate_type) {
929 (GateType::RotationX, GateType::RotationX)
930 | (GateType::RotationY, GateType::RotationY)
931 | (GateType::RotationZ, GateType::RotationZ) => return true,
932 _ => {}
933 }
934 }
935 }
936
937 if gates.len() == 3 {
939 if matches!(gates[0].gate_type, GateType::CNOT)
941 && gates[1].qubits.len() == 1
942 && matches!(gates[2].gate_type, GateType::CNOT)
943 {
944 return true;
945 }
946 }
947
948 false
949 }
950
951 fn remove_overlapping_opportunities(
953 &self,
954 opportunities: Vec<FusionOpportunity>,
955 ) -> Result<Vec<FusionOpportunity>> {
956 let mut result = Vec::new();
957 let mut used_positions = HashSet::new();
958
959 for opportunity in opportunities {
960 let overlaps = opportunity
962 .qubits
963 .iter()
964 .any(|q| used_positions.contains(q));
965
966 if !overlaps {
967 for &qubit in &opportunity.qubits {
969 used_positions.insert(qubit);
970 }
971 result.push(opportunity);
972 }
973 }
974
975 Ok(result)
976 }
977
978 fn perform_fusion(
980 &mut self,
981 gates: &[QuantumGate],
982 ) -> Result<(Vec<FusedGateBlock>, Vec<QuantumGate>)> {
983 let start_time = std::time::Instant::now();
984
985 #[cfg(feature = "advanced_math")]
986 {
987 if self.optimizer.is_some() {
988 return self.perform_manual_fusion(gates);
990 }
991 }
992
993 self.perform_manual_fusion(gates)
995 }
996
997 #[cfg(feature = "advanced_math")]
998 fn perform_scirs2_fusion(
999 &mut self,
1000 gates: &[QuantumGate],
1001 _optimizer: &mut Box<dyn std::any::Any>,
1002 ) -> Result<(Vec<FusedGateBlock>, Vec<QuantumGate>)> {
1003 self.perform_manual_fusion(gates)
1006 }
1007
1008 fn perform_manual_fusion(
1010 &mut self,
1011 gates: &[QuantumGate],
1012 ) -> Result<(Vec<FusedGateBlock>, Vec<QuantumGate>)> {
1013 let mut fused_blocks = Vec::new();
1014 let mut remaining_gates = Vec::new();
1015 let mut i = 0;
1016
1017 while i < gates.len() {
1018 let mut best_block_size = 1;
1020 let mut best_benefit = 0.0;
1021
1022 for block_size in 2..=(self.config.max_fusion_size.min(gates.len() - i)) {
1023 let window = &gates[i..i + block_size];
1024
1025 if let Ok(can_fuse) = self.can_fuse_gate_sequence(window) {
1026 if can_fuse {
1027 if let Ok(benefit) = self.estimate_fusion_benefit(window) {
1028 if benefit > best_benefit
1029 && benefit >= self.config.min_benefit_threshold
1030 {
1031 best_block_size = block_size;
1032 best_benefit = benefit;
1033 }
1034 }
1035 }
1036 }
1037 }
1038
1039 if best_block_size > 1 {
1040 let block_gates = gates[i..i + best_block_size].to_vec();
1042
1043 let pattern_key = FusionPatternKey::from_gates(&block_gates)?;
1045
1046 if let Some(cached_result) = self.fusion_cache.get(&pattern_key) {
1048 fused_blocks.push(cached_result.fused_block.clone());
1049 self.cache_hits += 1;
1050 } else {
1051 let fused_block = FusedGateBlock::new(block_gates.clone())?;
1052
1053 let cached_result = CachedFusionResult {
1055 fused_block: fused_block.clone(),
1056 benefit: 1.0, usage_count: 1,
1058 last_accessed: std::time::Instant::now(),
1059 };
1060
1061 if self.fusion_cache.len() < self.config.fusion_cache_size {
1063 self.fusion_cache.insert(pattern_key, cached_result);
1064 }
1065
1066 self.cache_misses += 1;
1067 fused_blocks.push(fused_block);
1068 }
1069
1070 i += best_block_size;
1071 } else {
1072 remaining_gates.push(gates[i].clone());
1074 i += 1;
1075 }
1076 }
1077
1078 Ok((fused_blocks, remaining_gates))
1079 }
1080
1081 fn calculate_circuit_depth(&self, gates: &[QuantumGate]) -> usize {
1083 if gates.is_empty() {
1084 return 0;
1085 }
1086
1087 let mut qubit_last_gate = HashMap::new();
1089 let mut gate_depths = vec![0; gates.len()];
1090
1091 for (i, gate) in gates.iter().enumerate() {
1092 let mut max_dependency_depth = 0;
1093
1094 for &qubit in &gate.qubits {
1095 if let Some(&last_gate_idx) = qubit_last_gate.get(&qubit) {
1096 max_dependency_depth = max_dependency_depth.max(gate_depths[last_gate_idx]);
1097 }
1098 qubit_last_gate.insert(qubit, i);
1099 }
1100
1101 gate_depths[i] = max_dependency_depth + 1;
1102 }
1103
1104 gate_depths.into_iter().max().unwrap_or(0)
1105 }
1106
1107 const fn calculate_fused_circuit_depth(
1109 &self,
1110 blocks: &[FusedGateBlock],
1111 gates: &[QuantumGate],
1112 ) -> usize {
1113 blocks.len() + gates.len()
1115 }
1116
1117 fn calculate_circuit_fingerprint(&self, gates: &[QuantumGate]) -> u64 {
1119 use std::collections::hash_map::DefaultHasher;
1120
1121 let mut hasher = DefaultHasher::new();
1122 gates.hash(&mut hasher);
1123 hasher.finish()
1124 }
1125
1126 pub fn get_fusion_stats(&self) -> FusionStats {
1128 FusionStats {
1129 cache_size: self.fusion_cache.len(),
1130 cache_hit_rate: self.calculate_cache_hit_rate(),
1131 learning_experiments: self.learning_history.len(),
1132 average_improvement: self.calculate_average_improvement(),
1133 }
1134 }
1135
1136 const fn calculate_cache_hit_rate(&self) -> f64 {
1137 0.0 }
1140
1141 fn calculate_average_improvement(&self) -> f64 {
1142 if self.learning_history.is_empty() {
1143 return 0.0;
1144 }
1145
1146 let total: f64 = self
1147 .learning_history
1148 .iter()
1149 .map(|e| e.performance_improvement)
1150 .sum();
1151 total / self.learning_history.len() as f64
1152 }
1153
1154 pub fn fuse_gates(
1156 &mut self,
1157 gates: &[QuantumGate],
1158 ) -> crate::error::Result<(Vec<FusedGateBlock>, Vec<QuantumGate>)> {
1159 let mut fused_blocks = Vec::new();
1160
1161 if gates.is_empty() {
1162 return Ok((fused_blocks, Vec::new()));
1163 }
1164
1165 let mut i = 0;
1166 while i < gates.len() {
1167 if i + 1 < gates.len() {
1168 let should_fuse = self.should_fuse_basic(&gates[i], &gates[i + 1]);
1170
1171 if should_fuse {
1172 let mut qubits = gates[i].qubits.clone();
1174 qubits.extend_from_slice(&gates[i + 1].qubits);
1175 qubits.sort_unstable();
1176 qubits.dedup();
1177
1178 let fused_block = FusedGateBlock {
1179 gates: vec![gates[i].clone(), gates[i + 1].clone()],
1180 combined_matrix: self
1181 .calculate_combined_matrix(&gates[i], &gates[i + 1])?,
1182 qubits,
1183 cost: 0.5, improvement_factor: 1.5,
1185 };
1186
1187 fused_blocks.push(fused_block);
1188 i += 2; } else {
1190 let fused_block = FusedGateBlock {
1192 gates: vec![gates[i].clone()],
1193 combined_matrix: gates[i].matrix.clone(),
1194 qubits: gates[i].qubits.clone(),
1195 cost: 1.0,
1196 improvement_factor: 1.0,
1197 };
1198
1199 fused_blocks.push(fused_block);
1200 i += 1;
1201 }
1202 } else {
1203 let fused_block = FusedGateBlock {
1205 gates: vec![gates[i].clone()],
1206 combined_matrix: gates[i].matrix.clone(),
1207 qubits: gates[i].qubits.clone(),
1208 cost: 1.0,
1209 improvement_factor: 1.0,
1210 };
1211
1212 fused_blocks.push(fused_block);
1213 i += 1;
1214 }
1215 }
1216
1217 Ok((fused_blocks, Vec::new()))
1218 }
1219
1220 fn should_fuse_basic(&self, gate1: &QuantumGate, gate2: &QuantumGate) -> bool {
1222 let overlapping_qubits = gate1.qubits.iter().any(|&q| gate2.qubits.contains(&q));
1223 overlapping_qubits && gate1.qubits.len() <= 2 && gate2.qubits.len() <= 2
1224 }
1225
1226 fn calculate_combined_matrix(
1228 &self,
1229 gate1: &QuantumGate,
1230 gate2: &QuantumGate,
1231 ) -> crate::error::Result<Array2<Complex64>> {
1232 Ok(gate1.matrix.clone())
1235 }
1236}
1237
1238#[derive(Debug, Clone, Serialize, Deserialize)]
1240pub struct FusionStats {
1241 pub cache_size: usize,
1243 pub cache_hit_rate: f64,
1245 pub learning_experiments: usize,
1247 pub average_improvement: f64,
1249}
1250
1251impl Default for MLFusionPredictor {
1252 fn default() -> Self {
1253 Self::new()
1254 }
1255}
1256
1257impl MLFusionPredictor {
1258 pub fn new() -> Self {
1260 let mut feature_weights = HashMap::new();
1261
1262 feature_weights.insert("rotation_similarity".to_string(), 0.8);
1264 feature_weights.insert("gate_locality".to_string(), 0.7);
1265 feature_weights.insert("commutation_potential".to_string(), 0.6);
1266 feature_weights.insert("matrix_sparsity".to_string(), 0.5);
1267
1268 Self {
1269 feature_weights,
1270 training_data: Vec::new(),
1271 accuracy: 0.5, }
1273 }
1274
1275 pub fn predict_benefit(&self, gates: &[QuantumGate]) -> f64 {
1277 let features = self.extract_features(gates);
1278
1279 let mut prediction = 0.0;
1280 for (i, &feature_value) in features.iter().enumerate() {
1281 let weight_key = match i {
1282 0 => "rotation_similarity",
1283 1 => "gate_locality",
1284 2 => "commutation_potential",
1285 3 => "matrix_sparsity",
1286 _ => "default",
1287 };
1288
1289 if let Some(&weight) = self.feature_weights.get(weight_key) {
1290 prediction += feature_value * weight;
1291 }
1292 }
1293
1294 1.0 / (1.0 + (-prediction).exp())
1296 }
1297
1298 fn extract_features(&self, gates: &[QuantumGate]) -> Vec<f64> {
1300 let mut features = [0.0; 4];
1301
1302 if gates.len() < 2 {
1303 return features.to_vec();
1304 }
1305
1306 features[0] = self.calculate_rotation_similarity(gates);
1308
1309 features[1] = self.calculate_gate_locality(gates);
1311
1312 features[2] = self.calculate_commutation_potential(gates);
1314
1315 features[3] = self.calculate_matrix_sparsity(gates);
1317
1318 features.to_vec()
1319 }
1320
1321 fn calculate_rotation_similarity(&self, gates: &[QuantumGate]) -> f64 {
1322 let rotation_gates: Vec<_> = gates
1323 .iter()
1324 .filter(|g| {
1325 matches!(
1326 g.gate_type,
1327 GateType::RotationX | GateType::RotationY | GateType::RotationZ
1328 )
1329 })
1330 .collect();
1331
1332 if rotation_gates.len() < 2 {
1333 return 0.0;
1334 }
1335
1336 let mut similarity_score = 0.0;
1338 for i in 0..rotation_gates.len() - 1 {
1339 for j in i + 1..rotation_gates.len() {
1340 if rotation_gates[i].gate_type == rotation_gates[j].gate_type
1341 && rotation_gates[i].qubits == rotation_gates[j].qubits
1342 {
1343 similarity_score += 1.0;
1344 }
1345 }
1346 }
1347
1348 similarity_score / (rotation_gates.len() as f64)
1349 }
1350
1351 fn calculate_gate_locality(&self, gates: &[QuantumGate]) -> f64 {
1352 let mut locality_score = 0.0;
1353 let mut adjacent_count = 0;
1354
1355 for i in 0..gates.len() - 1 {
1356 let current_qubits: HashSet<_> = gates[i].qubits.iter().copied().collect();
1357 let next_qubits: HashSet<_> = gates[i + 1].qubits.iter().copied().collect();
1358
1359 if current_qubits.intersection(&next_qubits).count() > 0 {
1360 locality_score += 1.0;
1361 }
1362 adjacent_count += 1;
1363 }
1364
1365 if adjacent_count > 0 {
1366 locality_score / adjacent_count as f64
1367 } else {
1368 0.0
1369 }
1370 }
1371
1372 fn calculate_commutation_potential(&self, gates: &[QuantumGate]) -> f64 {
1373 let mut commutation_score = 0.0;
1374 let mut pair_count = 0;
1375
1376 for i in 0..gates.len() - 1 {
1377 for j in i + 1..gates.len() {
1378 let qubits_i: HashSet<_> = gates[i].qubits.iter().copied().collect();
1379 let qubits_j: HashSet<_> = gates[j].qubits.iter().copied().collect();
1380
1381 if qubits_i.intersection(&qubits_j).count() == 0 {
1383 commutation_score += 1.0;
1384 }
1385 pair_count += 1;
1386 }
1387 }
1388
1389 if pair_count > 0 {
1390 commutation_score / pair_count as f64
1391 } else {
1392 0.0
1393 }
1394 }
1395
1396 fn calculate_matrix_sparsity(&self, gates: &[QuantumGate]) -> f64 {
1397 let mut total_sparsity = 0.0;
1398
1399 for gate in gates {
1400 let matrix = &gate.matrix;
1401 let zero_count = matrix.iter().filter(|&&x| x.norm() < 1e-10).count();
1402 let sparsity = zero_count as f64 / (matrix.len() as f64);
1403 total_sparsity += sparsity;
1404 }
1405
1406 total_sparsity / gates.len() as f64
1407 }
1408
1409 pub fn add_training_example(&mut self, example: MLTrainingExample) {
1411 self.training_data.push(example);
1412
1413 if self.training_data.len() % 10 == 0 {
1415 self.update_weights();
1416 }
1417 }
1418
1419 fn update_weights(&mut self) {
1420 let learning_rate = 0.01;
1422
1423 for example in &self.training_data {
1424 if let Some(observed) = example.observed_benefit {
1425 let predicted = self.predict_benefit_from_features(&example.features);
1426 let error = observed - predicted;
1427
1428 for (i, &feature_value) in example.features.iter().enumerate() {
1430 let weight_key = match i {
1431 0 => "rotation_similarity",
1432 1 => "gate_locality",
1433 2 => "commutation_potential",
1434 3 => "matrix_sparsity",
1435 _ => continue,
1436 };
1437
1438 if let Some(weight) = self.feature_weights.get_mut(weight_key) {
1439 *weight += learning_rate * error * feature_value;
1440 }
1441 }
1442 }
1443 }
1444 }
1445
1446 fn predict_benefit_from_features(&self, features: &[f64]) -> f64 {
1447 let mut prediction = 0.0;
1448 for (i, &feature_value) in features.iter().enumerate() {
1449 let weight_key = match i {
1450 0 => "rotation_similarity",
1451 1 => "gate_locality",
1452 2 => "commutation_potential",
1453 3 => "matrix_sparsity",
1454 _ => "default",
1455 };
1456
1457 if let Some(&weight) = self.feature_weights.get(weight_key) {
1458 prediction += feature_value * weight;
1459 }
1460 }
1461
1462 1.0 / (1.0 + (-prediction).exp())
1463 }
1464
1465 fn should_fuse_gates(&self, gate1: &QuantumGate, gate2: &QuantumGate) -> FusionResult {
1467 let overlapping_qubits = gate1.qubits.iter().any(|&q| gate2.qubits.contains(&q));
1469 let should_fuse = overlapping_qubits && gate1.qubits.len() <= 2 && gate2.qubits.len() <= 2;
1470
1471 FusionResult {
1472 should_fuse,
1473 confidence: if should_fuse { 0.8 } else { 0.2 },
1474 expected_speedup: if should_fuse { 1.5 } else { 1.0 },
1475 estimated_error: 0.01,
1476 }
1477 }
1478}
1479
1480impl Default for CircuitPatternAnalyzer {
1481 fn default() -> Self {
1482 Self::new()
1483 }
1484}
1485
1486impl CircuitPatternAnalyzer {
1487 pub fn new() -> Self {
1489 let mut beneficial_patterns = HashMap::new();
1490
1491 beneficial_patterns.insert("RX-RX".to_string(), 0.9);
1493 beneficial_patterns.insert("RY-RY".to_string(), 0.9);
1494 beneficial_patterns.insert("RZ-RZ".to_string(), 0.9);
1495 beneficial_patterns.insert("H-CNOT-H".to_string(), 0.8);
1496 beneficial_patterns.insert("CNOT-RZ-CNOT".to_string(), 0.7);
1497
1498 Self {
1499 beneficial_patterns,
1500 pattern_history: Vec::new(),
1501 }
1502 }
1503
1504 pub fn analyze_pattern(&mut self, gates: &[QuantumGate]) -> PatternRecognitionResult {
1506 let pattern_string = self.create_pattern_string(gates);
1507
1508 let (confidence, expected_benefit) = if let Some(&benefit) =
1509 self.beneficial_patterns.get(&pattern_string)
1510 {
1511 (0.9, benefit)
1512 } else {
1513 let (partial_confidence, partial_benefit) = self.find_partial_matches(&pattern_string);
1515 (partial_confidence, partial_benefit)
1516 };
1517
1518 let result = PatternRecognitionResult {
1519 pattern: pattern_string,
1520 confidence,
1521 expected_benefit,
1522 };
1523
1524 self.pattern_history.push(result.clone());
1525 result
1526 }
1527
1528 fn create_pattern_string(&self, gates: &[QuantumGate]) -> String {
1529 gates
1530 .iter()
1531 .map(|g| format!("{:?}", g.gate_type))
1532 .collect::<Vec<_>>()
1533 .join("-")
1534 }
1535
1536 fn find_partial_matches(&self, pattern: &str) -> (f64, f64) {
1537 let mut best_confidence = 0.0;
1538 let mut best_benefit = 0.0;
1539
1540 for (known_pattern, &benefit) in &self.beneficial_patterns {
1541 let similarity = self.calculate_pattern_similarity(pattern, known_pattern);
1542 if similarity > best_confidence {
1543 best_confidence = similarity;
1544 best_benefit = benefit * similarity; }
1546 }
1547
1548 (best_confidence, best_benefit)
1549 }
1550
1551 fn calculate_pattern_similarity(&self, pattern1: &str, pattern2: &str) -> f64 {
1552 let gates1: Vec<&str> = pattern1.split('-').collect();
1553 let gates2: Vec<&str> = pattern2.split('-').collect();
1554
1555 let max_len = gates1.len().max(gates2.len()) as f64;
1556 if max_len == 0.0 {
1557 return 0.0;
1558 }
1559
1560 let common_count = gates1.iter().filter(|&g| gates2.contains(g)).count() as f64;
1561
1562 common_count / max_len
1563 }
1564
1565 pub fn learn_pattern(&mut self, pattern: String, observed_benefit: f64) {
1567 let alpha: f64 = 0.1; if let Some(current_benefit) = self.beneficial_patterns.get_mut(&pattern) {
1571 *current_benefit = (1.0 - alpha).mul_add(*current_benefit, alpha * observed_benefit);
1572 } else {
1573 self.beneficial_patterns.insert(pattern, observed_benefit);
1574 }
1575 }
1576}
1577
1578pub struct FusionUtils;
1580
1581impl FusionUtils {
1582 pub fn create_test_sequence(sequence_type: &str, num_qubits: usize) -> Vec<QuantumGate> {
1584 match sequence_type {
1585 "rotation_chain" => (0..num_qubits)
1586 .flat_map(|q| {
1587 vec![
1588 QuantumGate::new(
1589 GateType::RotationX,
1590 vec![q],
1591 vec![std::f64::consts::PI / 4.0],
1592 ),
1593 QuantumGate::new(
1594 GateType::RotationY,
1595 vec![q],
1596 vec![std::f64::consts::PI / 3.0],
1597 ),
1598 QuantumGate::new(
1599 GateType::RotationZ,
1600 vec![q],
1601 vec![std::f64::consts::PI / 6.0],
1602 ),
1603 ]
1604 })
1605 .collect(),
1606 "cnot_ladder" => (0..num_qubits - 1)
1607 .map(|q| QuantumGate::new(GateType::CNOT, vec![q, q + 1], vec![]))
1608 .collect(),
1609 "mixed_gates" => {
1610 let mut gates = Vec::new();
1611 for q in 0..num_qubits {
1612 gates.push(QuantumGate::new(GateType::Hadamard, vec![q], vec![]));
1613 if q > 0 {
1614 gates.push(QuantumGate::new(GateType::CNOT, vec![q - 1, q], vec![]));
1615 }
1616 gates.push(QuantumGate::new(
1617 GateType::RotationZ,
1618 vec![q],
1619 vec![std::f64::consts::PI / 8.0],
1620 ));
1621 }
1622 gates
1623 }
1624 _ => vec![QuantumGate::new(GateType::Identity, vec![0], vec![])],
1625 }
1626 }
1627
1628 pub fn benchmark_fusion_strategies(
1630 gates: &[QuantumGate],
1631 strategies: &[FusionStrategy],
1632 ) -> Result<HashMap<String, CircuitAnalysis>> {
1633 let mut results = HashMap::new();
1634
1635 for &strategy in strategies {
1636 let config = AdaptiveFusionConfig {
1637 strategy,
1638 ..Default::default()
1639 };
1640
1641 let mut fusion_engine = AdaptiveGateFusion::new(config)?;
1642 let analysis = fusion_engine.analyze_circuit(gates)?;
1643
1644 results.insert(format!("{strategy:?}"), analysis);
1645 }
1646
1647 Ok(results)
1648 }
1649
1650 pub fn estimate_fusion_potential(gates: &[QuantumGate]) -> f64 {
1652 if gates.len() < 2 {
1653 return 0.0;
1654 }
1655
1656 let mut potential_fusions = 0;
1657 let mut total_gates = gates.len();
1658
1659 for window in gates.windows(2) {
1661 if window[0].can_fuse_with(&window[1]) {
1662 potential_fusions += 1;
1663 }
1664 }
1665
1666 potential_fusions as f64 / total_gates as f64
1667 }
1668}
1669
1670#[cfg(test)]
1671mod tests {
1672 use super::*;
1673 use approx::assert_abs_diff_eq;
1674
1675 #[test]
1676 fn test_quantum_gate_creation() {
1677 let gate = QuantumGate::new(GateType::PauliX, vec![0], vec![]);
1678 assert_eq!(gate.gate_type, GateType::PauliX);
1679 assert_eq!(gate.qubits, vec![0]);
1680 assert!(gate.cost > 0.0);
1681 }
1682
1683 #[test]
1684 fn test_gate_commutation() {
1685 let gate1 = QuantumGate::new(GateType::PauliX, vec![0], vec![]);
1686 let gate2 = QuantumGate::new(GateType::PauliY, vec![1], vec![]);
1687 let gate3 = QuantumGate::new(GateType::PauliX, vec![0], vec![]);
1688
1689 assert!(gate1.commutes_with(&gate2)); assert!(gate1.commutes_with(&gate3)); }
1692
1693 #[test]
1694 fn test_gate_fusion_compatibility() {
1695 let gate1 = QuantumGate::new(GateType::RotationX, vec![0], vec![0.5]);
1696 let gate2 = QuantumGate::new(GateType::RotationX, vec![0], vec![0.3]);
1697 let gate3 = QuantumGate::new(GateType::RotationY, vec![1], vec![0.2]);
1698
1699 assert!(gate1.can_fuse_with(&gate2)); assert!(!gate1.can_fuse_with(&gate3)); }
1702
1703 #[test]
1704 fn test_fused_gate_block() {
1705 let gates = vec![
1706 QuantumGate::new(GateType::RotationX, vec![0], vec![0.5]),
1707 QuantumGate::new(GateType::RotationX, vec![0], vec![0.3]),
1708 ];
1709
1710 let block = FusedGateBlock::new(gates).unwrap();
1711 assert_eq!(block.qubits, vec![0]);
1712 assert!(block.improvement_factor > 0.0);
1713 }
1714
1715 #[test]
1716 fn test_adaptive_fusion_config() {
1717 let config = AdaptiveFusionConfig::default();
1718 assert_eq!(config.strategy, FusionStrategy::Adaptive);
1719 assert_eq!(config.max_fusion_size, 8);
1720 assert!(config.enable_cross_qubit_fusion);
1721 }
1722
1723 #[test]
1724 fn test_circuit_analysis() {
1725 let gates = FusionUtils::create_test_sequence("rotation_chain", 2);
1726
1727 let config = AdaptiveFusionConfig::default();
1728 let mut fusion_engine = AdaptiveGateFusion::new(config).unwrap();
1729
1730 let analysis = fusion_engine.analyze_circuit(&gates).unwrap();
1731 assert_eq!(analysis.original_gate_count, gates.len());
1732 assert!(!analysis.fusion_opportunities.is_empty());
1733 }
1734
1735 #[test]
1736 fn test_fusion_utils_test_sequences() {
1737 let rotation_chain = FusionUtils::create_test_sequence("rotation_chain", 2);
1738 assert_eq!(rotation_chain.len(), 6); let cnot_ladder = FusionUtils::create_test_sequence("cnot_ladder", 3);
1741 assert_eq!(cnot_ladder.len(), 2); let mixed_gates = FusionUtils::create_test_sequence("mixed_gates", 2);
1744 assert!(!mixed_gates.is_empty());
1745 }
1746
1747 #[test]
1748 fn test_fusion_potential_estimation() {
1749 let gates = vec![
1750 QuantumGate::new(GateType::RotationX, vec![0], vec![0.1]),
1751 QuantumGate::new(GateType::RotationX, vec![0], vec![0.2]),
1752 QuantumGate::new(GateType::RotationY, vec![1], vec![0.3]),
1753 ];
1754
1755 let potential = FusionUtils::estimate_fusion_potential(&gates);
1756 assert!(potential > 0.0);
1757 assert!(potential <= 1.0);
1758 }
1759
1760 #[test]
1761 fn test_gate_matrix_generation() {
1762 let pauli_x = QuantumGate::new(GateType::PauliX, vec![0], vec![]);
1763 assert_eq!(pauli_x.matrix.shape(), &[2, 2]);
1764
1765 assert_abs_diff_eq!(pauli_x.matrix[[0, 1]].re, 1.0, epsilon = 1e-10);
1767 assert_abs_diff_eq!(pauli_x.matrix[[1, 0]].re, 1.0, epsilon = 1e-10);
1768 }
1769
1770 #[test]
1771 fn test_circuit_depth_calculation() {
1772 let gates = vec![
1773 QuantumGate::new(GateType::Hadamard, vec![0], vec![]),
1774 QuantumGate::new(GateType::CNOT, vec![0, 1], vec![]),
1775 QuantumGate::new(GateType::RotationZ, vec![1], vec![0.5]),
1776 ];
1777
1778 let config = AdaptiveFusionConfig::default();
1779 let fusion_engine = AdaptiveGateFusion::new(config).unwrap();
1780
1781 let depth = fusion_engine.calculate_circuit_depth(&gates);
1782 assert!(depth > 0);
1783 }
1784}