1use ndarray::Array2;
9use num_complex::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.get(0).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.get(0).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.get(0).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: &QuantumGate) -> 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 fn check_specific_commutation(&self, other: &QuantumGate) -> 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: &QuantumGate) -> 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 fn check_two_qubit_fusion_compatibility(&self, other: &QuantumGate) -> 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();
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 {} not found in qubit list",
469 qubit
470 )));
471 }
472 }
473
474 for i in 0..full_size {
476 for j in 0..full_size {
477 let mut gate_i = 0;
479 let mut gate_j = 0;
480 let mut valid = true;
481
482 for (gate_pos, &all_pos) in &qubit_mapping {
483 let bit_i = (i >> (num_all_qubits - 1 - all_pos)) & 1;
484 let bit_j = (j >> (num_all_qubits - 1 - all_pos)) & 1;
485
486 gate_i |= bit_i << (gate_qubits.len() - 1 - gate_pos);
487 gate_j |= bit_j << (gate_qubits.len() - 1 - gate_pos);
488 }
489
490 for all_pos in 0..num_all_qubits {
492 if !qubit_mapping.values().any(|&pos| pos == all_pos) {
493 let bit_i = (i >> (num_all_qubits - 1 - all_pos)) & 1;
494 let bit_j = (j >> (num_all_qubits - 1 - all_pos)) & 1;
495 if bit_i != bit_j {
496 valid = false;
497 break;
498 }
499 }
500 }
501
502 if valid {
503 expanded[[i, j]] = gate_matrix[[gate_i, gate_j]];
504 } else {
505 expanded[[i, j]] = if i == j {
506 Complex64::new(1.0, 0.0)
507 } else {
508 Complex64::new(0.0, 0.0)
509 };
510 }
511 }
512 }
513
514 Ok(expanded)
515 }
516
517 fn estimate_fused_cost(matrix: &Array2<Complex64>) -> f64 {
519 let size = matrix.nrows();
521 let base_cost = (size as f64).log2() * size as f64;
522
523 let fusion_overhead = 0.1 * base_cost;
525
526 base_cost + fusion_overhead
527 }
528
529 pub fn is_beneficial(&self) -> bool {
531 self.improvement_factor > 1.1 }
533}
534
535#[derive(Debug, Clone, Serialize, Deserialize)]
537pub struct CircuitAnalysis {
538 pub original_gate_count: usize,
540 pub fused_gate_count: usize,
542 pub fusion_blocks: usize,
544 pub performance_improvement: f64,
546 pub gate_distribution: HashMap<String, usize>,
548 pub fusion_opportunities: Vec<FusionOpportunity>,
550 pub circuit_depth: (usize, usize),
552}
553
554#[derive(Debug, Clone, Serialize, Deserialize)]
556pub struct FusionOpportunity {
557 pub description: String,
559 pub qubits: Vec<usize>,
561 pub gate_types: Vec<String>,
563 pub estimated_benefit: f64,
565 pub confidence: f64,
567}
568
569pub struct AdaptiveGateFusion {
571 config: AdaptiveFusionConfig,
573 backend: Option<SciRS2Backend>,
575 fusion_cache: HashMap<FusionPatternKey, CachedFusionResult>,
577 learning_history: Vec<FusionExperiment>,
579 #[cfg(feature = "advanced_math")]
581 optimizer: Option<Box<dyn std::any::Any>>, ml_predictor: Option<MLFusionPredictor>,
584 cache_hits: usize,
586 cache_misses: usize,
587 pattern_analyzer: CircuitPatternAnalyzer,
589}
590
591#[derive(Debug, Clone, PartialEq, Eq, Hash)]
593pub struct FusionPatternKey {
594 gate_types: Vec<GateType>,
596 num_qubits: usize,
598 parameter_hash: u64,
600}
601
602impl FusionPatternKey {
603 pub fn from_gates(gates: &[QuantumGate]) -> Result<Self> {
605 let gate_types: Vec<GateType> = gates.iter().map(|g| g.gate_type.clone()).collect();
606
607 let mut qubits = std::collections::HashSet::new();
609 for gate in gates {
610 for &qubit in &gate.qubits {
611 qubits.insert(qubit);
612 }
613 }
614 let num_qubits = qubits.len();
615
616 let mut parameter_hash = 0u64;
618 for gate in gates {
619 for ¶m in &gate.parameters {
620 parameter_hash = parameter_hash.wrapping_add((param * 1000.0) as u64);
621 }
622 }
623
624 Ok(FusionPatternKey {
625 gate_types,
626 num_qubits,
627 parameter_hash,
628 })
629 }
630}
631
632#[derive(Debug, Clone)]
634pub struct CachedFusionResult {
635 fused_block: FusedGateBlock,
637 benefit: f64,
639 usage_count: usize,
641 last_accessed: std::time::Instant,
643}
644
645pub struct MLFusionPredictor {
647 feature_weights: HashMap<String, f64>,
649 training_data: Vec<MLTrainingExample>,
651 accuracy: f64,
653}
654
655#[derive(Debug, Clone)]
657pub struct MLTrainingExample {
658 features: Vec<f64>,
660 benefit: f64,
662 observed_benefit: Option<f64>,
664}
665
666pub struct CircuitPatternAnalyzer {
668 beneficial_patterns: HashMap<String, f64>,
670 pattern_history: Vec<PatternRecognitionResult>,
672}
673
674#[derive(Debug, Clone)]
676pub struct PatternRecognitionResult {
677 pub pattern: String,
679 pub confidence: f64,
681 pub expected_benefit: f64,
683}
684
685#[derive(Debug, Clone)]
687pub struct FusionResult {
688 pub should_fuse: bool,
690 pub confidence: f64,
692 pub expected_speedup: f64,
694 pub estimated_error: f64,
696}
697
698#[derive(Debug, Clone)]
700struct FusionExperiment {
701 circuit_fingerprint: u64,
702 fusion_strategy: FusionStrategy,
703 performance_improvement: f64,
704 execution_time_ms: f64,
705}
706
707impl AdaptiveGateFusion {
708 pub fn new(config: AdaptiveFusionConfig) -> Result<Self> {
710 Ok(Self {
711 config,
712 backend: None,
713 fusion_cache: HashMap::new(),
714 learning_history: Vec::new(),
715 ml_predictor: None,
716 cache_hits: 0,
717 cache_misses: 0,
718 pattern_analyzer: CircuitPatternAnalyzer::new(),
719 #[cfg(feature = "advanced_math")]
720 optimizer: None,
721 })
722 }
723
724 pub fn with_backend(mut self) -> Result<Self> {
726 self.backend = Some(SciRS2Backend::new());
727
728 #[cfg(feature = "advanced_math")]
729 {
730 let strategy = match self.config.strategy {
731 FusionStrategy::Aggressive => OptimizationStrategy::MinimizeTime,
732 FusionStrategy::Conservative => OptimizationStrategy::MinimizeLength,
733 FusionStrategy::Balanced => OptimizationStrategy::Balanced,
734 FusionStrategy::Adaptive => OptimizationStrategy::MinimizeCrossings,
735 FusionStrategy::Custom => OptimizationStrategy::Balanced,
736 };
737
738 self.optimizer = Some(Box::new(strategy));
740 }
741
742 Ok(self)
743 }
744
745 pub fn analyze_circuit(&mut self, gates: &[QuantumGate]) -> Result<CircuitAnalysis> {
747 let start_time = std::time::Instant::now();
748
749 let original_gate_count = gates.len();
751 let original_depth = self.calculate_circuit_depth(gates);
752
753 let fusion_opportunities = self.identify_fusion_opportunities(gates)?;
755
756 let (fused_blocks, remaining_gates) = self.perform_fusion(gates)?;
758 let fused_gate_count = fused_blocks.len() + remaining_gates.len();
759 let fused_depth = self.calculate_fused_circuit_depth(&fused_blocks, &remaining_gates);
760
761 let original_cost: f64 = gates.iter().map(|g| g.cost).sum();
763 let fused_cost: f64 = fused_blocks.iter().map(|b| b.cost).sum::<f64>()
764 + remaining_gates.iter().map(|g| g.cost).sum::<f64>();
765 let performance_improvement = original_cost / fused_cost;
766
767 let mut gate_distribution = HashMap::new();
769 for gate in gates {
770 let gate_name = format!("{:?}", gate.gate_type);
771 *gate_distribution.entry(gate_name).or_insert(0) += 1;
772 }
773
774 let analysis = CircuitAnalysis {
775 original_gate_count,
776 fused_gate_count,
777 fusion_blocks: fused_blocks.len(),
778 performance_improvement,
779 gate_distribution,
780 fusion_opportunities,
781 circuit_depth: (original_depth, fused_depth),
782 };
783
784 if self.config.enable_ml_predictions {
786 let experiment = FusionExperiment {
787 circuit_fingerprint: self.calculate_circuit_fingerprint(gates),
788 fusion_strategy: self.config.strategy,
789 performance_improvement,
790 execution_time_ms: start_time.elapsed().as_secs_f64() * 1000.0,
791 };
792 self.learning_history.push(experiment);
793 }
794
795 Ok(analysis)
796 }
797
798 pub fn fuse_circuit(&mut self, gates: &[QuantumGate]) -> Result<Vec<FusedGateBlock>> {
800 let (fused_blocks, _) = self.perform_fusion(gates)?;
801 Ok(fused_blocks)
802 }
803
804 fn identify_fusion_opportunities(
806 &self,
807 gates: &[QuantumGate],
808 ) -> Result<Vec<FusionOpportunity>> {
809 let mut opportunities = Vec::new();
810
811 for window_size in 2..=self.config.max_fusion_size {
813 for window in gates.windows(window_size) {
814 if let Some(opportunity) = self.analyze_gate_window(window)? {
815 opportunities.push(opportunity);
816 }
817 }
818 }
819
820 opportunities.sort_by(|a, b| {
822 b.estimated_benefit
823 .partial_cmp(&a.estimated_benefit)
824 .unwrap()
825 });
826 self.remove_overlapping_opportunities(opportunities)
827 }
828
829 fn analyze_gate_window(&self, window: &[QuantumGate]) -> Result<Option<FusionOpportunity>> {
831 if window.len() < 2 {
832 return Ok(None);
833 }
834
835 let can_fuse = self.can_fuse_gate_sequence(window)?;
837 if !can_fuse {
838 return Ok(None);
839 }
840
841 let benefit = self.estimate_fusion_benefit(window)?;
843 if benefit < self.config.min_benefit_threshold {
844 return Ok(None);
845 }
846
847 let qubits: HashSet<usize> = window.iter().flat_map(|g| &g.qubits).cloned().collect();
849 let gate_types: Vec<String> = window
850 .iter()
851 .map(|g| format!("{:?}", g.gate_type))
852 .collect();
853
854 let confidence = self.calculate_fusion_confidence(window);
855
856 let opportunity = FusionOpportunity {
857 description: format!("Fuse {} gates on qubits {:?}", window.len(), qubits),
858 qubits: qubits.into_iter().collect(),
859 gate_types,
860 estimated_benefit: benefit,
861 confidence,
862 };
863
864 Ok(Some(opportunity))
865 }
866
867 fn can_fuse_gate_sequence(&self, gates: &[QuantumGate]) -> Result<bool> {
869 if gates.is_empty() {
870 return Ok(false);
871 }
872
873 for i in 0..gates.len() - 1 {
875 if !gates[i].can_fuse_with(&gates[i + 1]) {
876 if !gates[i].commutes_with(&gates[i + 1]) {
878 return Ok(false);
879 }
880 }
881 }
882
883 let fusion_block = FusedGateBlock::new(gates.to_vec())?;
885 Ok(fusion_block.is_beneficial())
886 }
887
888 fn estimate_fusion_benefit(&self, gates: &[QuantumGate]) -> Result<f64> {
890 let individual_cost: f64 = gates.iter().map(|g| g.cost).sum();
891
892 let fusion_block = FusedGateBlock::new(gates.to_vec())?;
894 let fused_cost = fusion_block.cost;
895
896 Ok(individual_cost / fused_cost)
897 }
898
899 fn calculate_fusion_confidence(&self, gates: &[QuantumGate]) -> f64 {
901 let mut confidence: f64 = 1.0;
902
903 let gate_types: HashSet<_> = gates.iter().map(|g| &g.gate_type).collect();
905 if gate_types.len() > 1 {
906 confidence *= 0.8;
907 }
908
909 let all_qubits: HashSet<_> = gates.iter().flat_map(|g| &g.qubits).collect();
911 if all_qubits.len() > 3 {
912 confidence *= 0.6;
913 }
914
915 if self.is_known_beneficial_pattern(gates) {
917 confidence *= 1.2;
918 }
919
920 confidence.min(1.0)
921 }
922
923 fn is_known_beneficial_pattern(&self, gates: &[QuantumGate]) -> bool {
925 if gates.len() == 2 {
927 if let (Some(g1), Some(g2)) = (gates.get(0), gates.get(1)) {
929 match (&g1.gate_type, &g2.gate_type) {
930 (GateType::RotationX, GateType::RotationX)
931 | (GateType::RotationY, GateType::RotationY)
932 | (GateType::RotationZ, GateType::RotationZ) => return true,
933 _ => {}
934 }
935 }
936 }
937
938 if gates.len() == 3 {
940 if matches!(gates[0].gate_type, GateType::CNOT)
942 && gates[1].qubits.len() == 1
943 && matches!(gates[2].gate_type, GateType::CNOT)
944 {
945 return true;
946 }
947 }
948
949 false
950 }
951
952 fn remove_overlapping_opportunities(
954 &self,
955 opportunities: Vec<FusionOpportunity>,
956 ) -> Result<Vec<FusionOpportunity>> {
957 let mut result = Vec::new();
958 let mut used_positions = HashSet::new();
959
960 for opportunity in opportunities {
961 let overlaps = opportunity
963 .qubits
964 .iter()
965 .any(|q| used_positions.contains(q));
966
967 if !overlaps {
968 for &qubit in &opportunity.qubits {
970 used_positions.insert(qubit);
971 }
972 result.push(opportunity);
973 }
974 }
975
976 Ok(result)
977 }
978
979 fn perform_fusion(
981 &mut self,
982 gates: &[QuantumGate],
983 ) -> Result<(Vec<FusedGateBlock>, Vec<QuantumGate>)> {
984 let start_time = std::time::Instant::now();
985
986 #[cfg(feature = "advanced_math")]
987 {
988 if self.optimizer.is_some() {
989 return self.perform_manual_fusion(gates);
991 }
992 }
993
994 self.perform_manual_fusion(gates)
996 }
997
998 #[cfg(feature = "advanced_math")]
999 fn perform_scirs2_fusion(
1000 &mut self,
1001 gates: &[QuantumGate],
1002 _optimizer: &mut Box<dyn std::any::Any>,
1003 ) -> Result<(Vec<FusedGateBlock>, Vec<QuantumGate>)> {
1004 self.perform_manual_fusion(gates)
1007 }
1008
1009 fn perform_manual_fusion(
1011 &mut self,
1012 gates: &[QuantumGate],
1013 ) -> Result<(Vec<FusedGateBlock>, Vec<QuantumGate>)> {
1014 let mut fused_blocks = Vec::new();
1015 let mut remaining_gates = Vec::new();
1016 let mut i = 0;
1017
1018 while i < gates.len() {
1019 let mut best_block_size = 1;
1021 let mut best_benefit = 0.0;
1022
1023 for block_size in 2..=(self.config.max_fusion_size.min(gates.len() - i)) {
1024 let window = &gates[i..i + block_size];
1025
1026 if let Ok(can_fuse) = self.can_fuse_gate_sequence(window) {
1027 if can_fuse {
1028 if let Ok(benefit) = self.estimate_fusion_benefit(window) {
1029 if benefit > best_benefit
1030 && benefit >= self.config.min_benefit_threshold
1031 {
1032 best_block_size = block_size;
1033 best_benefit = benefit;
1034 }
1035 }
1036 }
1037 }
1038 }
1039
1040 if best_block_size > 1 {
1041 let block_gates = gates[i..i + best_block_size].to_vec();
1043
1044 let pattern_key = FusionPatternKey::from_gates(&block_gates)?;
1046
1047 if let Some(cached_result) = self.fusion_cache.get(&pattern_key) {
1049 fused_blocks.push(cached_result.fused_block.clone());
1050 self.cache_hits += 1;
1051 } else {
1052 let fused_block = FusedGateBlock::new(block_gates.clone())?;
1053
1054 let cached_result = CachedFusionResult {
1056 fused_block: fused_block.clone(),
1057 benefit: 1.0, usage_count: 1,
1059 last_accessed: std::time::Instant::now(),
1060 };
1061
1062 if self.fusion_cache.len() < self.config.fusion_cache_size {
1064 self.fusion_cache.insert(pattern_key, cached_result);
1065 }
1066
1067 self.cache_misses += 1;
1068 fused_blocks.push(fused_block);
1069 }
1070
1071 i += best_block_size;
1072 } else {
1073 remaining_gates.push(gates[i].clone());
1075 i += 1;
1076 }
1077 }
1078
1079 Ok((fused_blocks, remaining_gates))
1080 }
1081
1082 fn calculate_circuit_depth(&self, gates: &[QuantumGate]) -> usize {
1084 if gates.is_empty() {
1085 return 0;
1086 }
1087
1088 let mut qubit_last_gate = HashMap::new();
1090 let mut gate_depths = vec![0; gates.len()];
1091
1092 for (i, gate) in gates.iter().enumerate() {
1093 let mut max_dependency_depth = 0;
1094
1095 for &qubit in &gate.qubits {
1096 if let Some(&last_gate_idx) = qubit_last_gate.get(&qubit) {
1097 max_dependency_depth = max_dependency_depth.max(gate_depths[last_gate_idx]);
1098 }
1099 qubit_last_gate.insert(qubit, i);
1100 }
1101
1102 gate_depths[i] = max_dependency_depth + 1;
1103 }
1104
1105 gate_depths.into_iter().max().unwrap_or(0)
1106 }
1107
1108 fn calculate_fused_circuit_depth(
1110 &self,
1111 blocks: &[FusedGateBlock],
1112 gates: &[QuantumGate],
1113 ) -> usize {
1114 blocks.len() + gates.len()
1116 }
1117
1118 fn calculate_circuit_fingerprint(&self, gates: &[QuantumGate]) -> u64 {
1120 use std::collections::hash_map::DefaultHasher;
1121
1122 let mut hasher = DefaultHasher::new();
1123 gates.hash(&mut hasher);
1124 hasher.finish()
1125 }
1126
1127 pub fn get_fusion_stats(&self) -> FusionStats {
1129 FusionStats {
1130 cache_size: self.fusion_cache.len(),
1131 cache_hit_rate: self.calculate_cache_hit_rate(),
1132 learning_experiments: self.learning_history.len(),
1133 average_improvement: self.calculate_average_improvement(),
1134 }
1135 }
1136
1137 fn calculate_cache_hit_rate(&self) -> f64 {
1138 0.0 }
1141
1142 fn calculate_average_improvement(&self) -> f64 {
1143 if self.learning_history.is_empty() {
1144 return 0.0;
1145 }
1146
1147 let total: f64 = self
1148 .learning_history
1149 .iter()
1150 .map(|e| e.performance_improvement)
1151 .sum();
1152 total / self.learning_history.len() as f64
1153 }
1154
1155 pub fn fuse_gates(
1157 &mut self,
1158 gates: &[QuantumGate],
1159 ) -> crate::error::Result<(Vec<FusedGateBlock>, Vec<QuantumGate>)> {
1160 let mut fused_blocks = Vec::new();
1161
1162 if gates.is_empty() {
1163 return Ok((fused_blocks, Vec::new()));
1164 }
1165
1166 let mut i = 0;
1167 while i < gates.len() {
1168 if i + 1 < gates.len() {
1169 let should_fuse = self.should_fuse_basic(&gates[i], &gates[i + 1]);
1171
1172 if should_fuse {
1173 let mut qubits = gates[i].qubits.clone();
1175 qubits.extend_from_slice(&gates[i + 1].qubits);
1176 qubits.sort_unstable();
1177 qubits.dedup();
1178
1179 let fused_block = FusedGateBlock {
1180 gates: vec![gates[i].clone(), gates[i + 1].clone()],
1181 combined_matrix: self
1182 .calculate_combined_matrix(&gates[i], &gates[i + 1])?,
1183 qubits,
1184 cost: 0.5, improvement_factor: 1.5,
1186 };
1187
1188 fused_blocks.push(fused_block);
1189 i += 2; } else {
1191 let fused_block = FusedGateBlock {
1193 gates: vec![gates[i].clone()],
1194 combined_matrix: gates[i].matrix.clone(),
1195 qubits: gates[i].qubits.clone(),
1196 cost: 1.0,
1197 improvement_factor: 1.0,
1198 };
1199
1200 fused_blocks.push(fused_block);
1201 i += 1;
1202 }
1203 } else {
1204 let fused_block = FusedGateBlock {
1206 gates: vec![gates[i].clone()],
1207 combined_matrix: gates[i].matrix.clone(),
1208 qubits: gates[i].qubits.clone(),
1209 cost: 1.0,
1210 improvement_factor: 1.0,
1211 };
1212
1213 fused_blocks.push(fused_block);
1214 i += 1;
1215 }
1216 }
1217
1218 Ok((fused_blocks, Vec::new()))
1219 }
1220
1221 fn should_fuse_basic(&self, gate1: &QuantumGate, gate2: &QuantumGate) -> bool {
1223 let overlapping_qubits = gate1.qubits.iter().any(|&q| gate2.qubits.contains(&q));
1224 overlapping_qubits && gate1.qubits.len() <= 2 && gate2.qubits.len() <= 2
1225 }
1226
1227 fn calculate_combined_matrix(
1229 &self,
1230 gate1: &QuantumGate,
1231 gate2: &QuantumGate,
1232 ) -> crate::error::Result<Array2<Complex64>> {
1233 Ok(gate1.matrix.clone())
1236 }
1237}
1238
1239#[derive(Debug, Clone, Serialize, Deserialize)]
1241pub struct FusionStats {
1242 pub cache_size: usize,
1244 pub cache_hit_rate: f64,
1246 pub learning_experiments: usize,
1248 pub average_improvement: f64,
1250}
1251
1252impl MLFusionPredictor {
1253 pub fn new() -> Self {
1255 let mut feature_weights = HashMap::new();
1256
1257 feature_weights.insert("rotation_similarity".to_string(), 0.8);
1259 feature_weights.insert("gate_locality".to_string(), 0.7);
1260 feature_weights.insert("commutation_potential".to_string(), 0.6);
1261 feature_weights.insert("matrix_sparsity".to_string(), 0.5);
1262
1263 Self {
1264 feature_weights,
1265 training_data: Vec::new(),
1266 accuracy: 0.5, }
1268 }
1269
1270 pub fn predict_benefit(&self, gates: &[QuantumGate]) -> f64 {
1272 let features = self.extract_features(gates);
1273
1274 let mut prediction = 0.0;
1275 for (i, &feature_value) in features.iter().enumerate() {
1276 let weight_key = match i {
1277 0 => "rotation_similarity",
1278 1 => "gate_locality",
1279 2 => "commutation_potential",
1280 3 => "matrix_sparsity",
1281 _ => "default",
1282 };
1283
1284 if let Some(&weight) = self.feature_weights.get(weight_key) {
1285 prediction += feature_value * weight;
1286 }
1287 }
1288
1289 1.0 / (1.0 + (-prediction).exp())
1291 }
1292
1293 fn extract_features(&self, gates: &[QuantumGate]) -> Vec<f64> {
1295 let mut features = [0.0; 4];
1296
1297 if gates.len() < 2 {
1298 return features.to_vec();
1299 }
1300
1301 features[0] = self.calculate_rotation_similarity(gates);
1303
1304 features[1] = self.calculate_gate_locality(gates);
1306
1307 features[2] = self.calculate_commutation_potential(gates);
1309
1310 features[3] = self.calculate_matrix_sparsity(gates);
1312
1313 features.to_vec()
1314 }
1315
1316 fn calculate_rotation_similarity(&self, gates: &[QuantumGate]) -> f64 {
1317 let rotation_gates: Vec<_> = gates
1318 .iter()
1319 .filter(|g| {
1320 matches!(
1321 g.gate_type,
1322 GateType::RotationX | GateType::RotationY | GateType::RotationZ
1323 )
1324 })
1325 .collect();
1326
1327 if rotation_gates.len() < 2 {
1328 return 0.0;
1329 }
1330
1331 let mut similarity_score = 0.0;
1333 for i in 0..rotation_gates.len() - 1 {
1334 for j in i + 1..rotation_gates.len() {
1335 if rotation_gates[i].gate_type == rotation_gates[j].gate_type
1336 && rotation_gates[i].qubits == rotation_gates[j].qubits
1337 {
1338 similarity_score += 1.0;
1339 }
1340 }
1341 }
1342
1343 similarity_score / (rotation_gates.len() as f64)
1344 }
1345
1346 fn calculate_gate_locality(&self, gates: &[QuantumGate]) -> f64 {
1347 let mut locality_score = 0.0;
1348 let mut adjacent_count = 0;
1349
1350 for i in 0..gates.len() - 1 {
1351 let current_qubits: HashSet<_> = gates[i].qubits.iter().cloned().collect();
1352 let next_qubits: HashSet<_> = gates[i + 1].qubits.iter().cloned().collect();
1353
1354 if current_qubits.intersection(&next_qubits).count() > 0 {
1355 locality_score += 1.0;
1356 }
1357 adjacent_count += 1;
1358 }
1359
1360 if adjacent_count > 0 {
1361 locality_score / adjacent_count as f64
1362 } else {
1363 0.0
1364 }
1365 }
1366
1367 fn calculate_commutation_potential(&self, gates: &[QuantumGate]) -> f64 {
1368 let mut commutation_score = 0.0;
1369 let mut pair_count = 0;
1370
1371 for i in 0..gates.len() - 1 {
1372 for j in i + 1..gates.len() {
1373 let qubits_i: HashSet<_> = gates[i].qubits.iter().cloned().collect();
1374 let qubits_j: HashSet<_> = gates[j].qubits.iter().cloned().collect();
1375
1376 if qubits_i.intersection(&qubits_j).count() == 0 {
1378 commutation_score += 1.0;
1379 }
1380 pair_count += 1;
1381 }
1382 }
1383
1384 if pair_count > 0 {
1385 commutation_score / pair_count as f64
1386 } else {
1387 0.0
1388 }
1389 }
1390
1391 fn calculate_matrix_sparsity(&self, gates: &[QuantumGate]) -> f64 {
1392 let mut total_sparsity = 0.0;
1393
1394 for gate in gates {
1395 let matrix = &gate.matrix;
1396 let zero_count = matrix.iter().filter(|&&x| x.norm() < 1e-10).count();
1397 let sparsity = zero_count as f64 / (matrix.len() as f64);
1398 total_sparsity += sparsity;
1399 }
1400
1401 total_sparsity / gates.len() as f64
1402 }
1403
1404 pub fn add_training_example(&mut self, example: MLTrainingExample) {
1406 self.training_data.push(example);
1407
1408 if self.training_data.len() % 10 == 0 {
1410 self.update_weights();
1411 }
1412 }
1413
1414 fn update_weights(&mut self) {
1415 let learning_rate = 0.01;
1417
1418 for example in &self.training_data {
1419 if let Some(observed) = example.observed_benefit {
1420 let predicted = self.predict_benefit_from_features(&example.features);
1421 let error = observed - predicted;
1422
1423 for (i, &feature_value) in example.features.iter().enumerate() {
1425 let weight_key = match i {
1426 0 => "rotation_similarity",
1427 1 => "gate_locality",
1428 2 => "commutation_potential",
1429 3 => "matrix_sparsity",
1430 _ => continue,
1431 };
1432
1433 if let Some(weight) = self.feature_weights.get_mut(weight_key) {
1434 *weight += learning_rate * error * feature_value;
1435 }
1436 }
1437 }
1438 }
1439 }
1440
1441 fn predict_benefit_from_features(&self, features: &[f64]) -> f64 {
1442 let mut prediction = 0.0;
1443 for (i, &feature_value) in features.iter().enumerate() {
1444 let weight_key = match i {
1445 0 => "rotation_similarity",
1446 1 => "gate_locality",
1447 2 => "commutation_potential",
1448 3 => "matrix_sparsity",
1449 _ => "default",
1450 };
1451
1452 if let Some(&weight) = self.feature_weights.get(weight_key) {
1453 prediction += feature_value * weight;
1454 }
1455 }
1456
1457 1.0 / (1.0 + (-prediction).exp())
1458 }
1459
1460 fn should_fuse_gates(&self, gate1: &QuantumGate, gate2: &QuantumGate) -> FusionResult {
1462 let overlapping_qubits = gate1.qubits.iter().any(|&q| gate2.qubits.contains(&q));
1464 let should_fuse = overlapping_qubits && gate1.qubits.len() <= 2 && gate2.qubits.len() <= 2;
1465
1466 FusionResult {
1467 should_fuse,
1468 confidence: if should_fuse { 0.8 } else { 0.2 },
1469 expected_speedup: if should_fuse { 1.5 } else { 1.0 },
1470 estimated_error: 0.01,
1471 }
1472 }
1473}
1474
1475impl CircuitPatternAnalyzer {
1476 pub fn new() -> Self {
1478 let mut beneficial_patterns = HashMap::new();
1479
1480 beneficial_patterns.insert("RX-RX".to_string(), 0.9);
1482 beneficial_patterns.insert("RY-RY".to_string(), 0.9);
1483 beneficial_patterns.insert("RZ-RZ".to_string(), 0.9);
1484 beneficial_patterns.insert("H-CNOT-H".to_string(), 0.8);
1485 beneficial_patterns.insert("CNOT-RZ-CNOT".to_string(), 0.7);
1486
1487 Self {
1488 beneficial_patterns,
1489 pattern_history: Vec::new(),
1490 }
1491 }
1492
1493 pub fn analyze_pattern(&mut self, gates: &[QuantumGate]) -> PatternRecognitionResult {
1495 let pattern_string = self.create_pattern_string(gates);
1496
1497 let (confidence, expected_benefit) = if let Some(&benefit) =
1498 self.beneficial_patterns.get(&pattern_string)
1499 {
1500 (0.9, benefit)
1501 } else {
1502 let (partial_confidence, partial_benefit) = self.find_partial_matches(&pattern_string);
1504 (partial_confidence, partial_benefit)
1505 };
1506
1507 let result = PatternRecognitionResult {
1508 pattern: pattern_string,
1509 confidence,
1510 expected_benefit,
1511 };
1512
1513 self.pattern_history.push(result.clone());
1514 result
1515 }
1516
1517 fn create_pattern_string(&self, gates: &[QuantumGate]) -> String {
1518 gates
1519 .iter()
1520 .map(|g| format!("{:?}", g.gate_type))
1521 .collect::<Vec<_>>()
1522 .join("-")
1523 }
1524
1525 fn find_partial_matches(&self, pattern: &str) -> (f64, f64) {
1526 let mut best_confidence = 0.0;
1527 let mut best_benefit = 0.0;
1528
1529 for (known_pattern, &benefit) in &self.beneficial_patterns {
1530 let similarity = self.calculate_pattern_similarity(pattern, known_pattern);
1531 if similarity > best_confidence {
1532 best_confidence = similarity;
1533 best_benefit = benefit * similarity; }
1535 }
1536
1537 (best_confidence, best_benefit)
1538 }
1539
1540 fn calculate_pattern_similarity(&self, pattern1: &str, pattern2: &str) -> f64 {
1541 let gates1: Vec<&str> = pattern1.split('-').collect();
1542 let gates2: Vec<&str> = pattern2.split('-').collect();
1543
1544 let max_len = gates1.len().max(gates2.len()) as f64;
1545 if max_len == 0.0 {
1546 return 0.0;
1547 }
1548
1549 let common_count = gates1.iter().filter(|&g| gates2.contains(g)).count() as f64;
1550
1551 common_count / max_len
1552 }
1553
1554 pub fn learn_pattern(&mut self, pattern: String, observed_benefit: f64) {
1556 let alpha = 0.1; if let Some(current_benefit) = self.beneficial_patterns.get_mut(&pattern) {
1560 *current_benefit = (1.0 - alpha) * *current_benefit + alpha * observed_benefit;
1561 } else {
1562 self.beneficial_patterns.insert(pattern, observed_benefit);
1563 }
1564 }
1565}
1566
1567pub struct FusionUtils;
1569
1570impl FusionUtils {
1571 pub fn create_test_sequence(sequence_type: &str, num_qubits: usize) -> Vec<QuantumGate> {
1573 match sequence_type {
1574 "rotation_chain" => (0..num_qubits)
1575 .flat_map(|q| {
1576 vec![
1577 QuantumGate::new(
1578 GateType::RotationX,
1579 vec![q],
1580 vec![std::f64::consts::PI / 4.0],
1581 ),
1582 QuantumGate::new(
1583 GateType::RotationY,
1584 vec![q],
1585 vec![std::f64::consts::PI / 3.0],
1586 ),
1587 QuantumGate::new(
1588 GateType::RotationZ,
1589 vec![q],
1590 vec![std::f64::consts::PI / 6.0],
1591 ),
1592 ]
1593 })
1594 .collect(),
1595 "cnot_ladder" => (0..num_qubits - 1)
1596 .map(|q| QuantumGate::new(GateType::CNOT, vec![q, q + 1], vec![]))
1597 .collect(),
1598 "mixed_gates" => {
1599 let mut gates = Vec::new();
1600 for q in 0..num_qubits {
1601 gates.push(QuantumGate::new(GateType::Hadamard, vec![q], vec![]));
1602 if q > 0 {
1603 gates.push(QuantumGate::new(GateType::CNOT, vec![q - 1, q], vec![]));
1604 }
1605 gates.push(QuantumGate::new(
1606 GateType::RotationZ,
1607 vec![q],
1608 vec![std::f64::consts::PI / 8.0],
1609 ));
1610 }
1611 gates
1612 }
1613 _ => vec![QuantumGate::new(GateType::Identity, vec![0], vec![])],
1614 }
1615 }
1616
1617 pub fn benchmark_fusion_strategies(
1619 gates: &[QuantumGate],
1620 strategies: &[FusionStrategy],
1621 ) -> Result<HashMap<String, CircuitAnalysis>> {
1622 let mut results = HashMap::new();
1623
1624 for &strategy in strategies {
1625 let config = AdaptiveFusionConfig {
1626 strategy,
1627 ..Default::default()
1628 };
1629
1630 let mut fusion_engine = AdaptiveGateFusion::new(config)?;
1631 let analysis = fusion_engine.analyze_circuit(gates)?;
1632
1633 results.insert(format!("{:?}", strategy), analysis);
1634 }
1635
1636 Ok(results)
1637 }
1638
1639 pub fn estimate_fusion_potential(gates: &[QuantumGate]) -> f64 {
1641 if gates.len() < 2 {
1642 return 0.0;
1643 }
1644
1645 let mut potential_fusions = 0;
1646 let mut total_gates = gates.len();
1647
1648 for window in gates.windows(2) {
1650 if window[0].can_fuse_with(&window[1]) {
1651 potential_fusions += 1;
1652 }
1653 }
1654
1655 potential_fusions as f64 / total_gates as f64
1656 }
1657}
1658
1659#[cfg(test)]
1660mod tests {
1661 use super::*;
1662 use approx::assert_abs_diff_eq;
1663
1664 #[test]
1665 fn test_quantum_gate_creation() {
1666 let gate = QuantumGate::new(GateType::PauliX, vec![0], vec![]);
1667 assert_eq!(gate.gate_type, GateType::PauliX);
1668 assert_eq!(gate.qubits, vec![0]);
1669 assert!(gate.cost > 0.0);
1670 }
1671
1672 #[test]
1673 fn test_gate_commutation() {
1674 let gate1 = QuantumGate::new(GateType::PauliX, vec![0], vec![]);
1675 let gate2 = QuantumGate::new(GateType::PauliY, vec![1], vec![]);
1676 let gate3 = QuantumGate::new(GateType::PauliX, vec![0], vec![]);
1677
1678 assert!(gate1.commutes_with(&gate2)); assert!(gate1.commutes_with(&gate3)); }
1681
1682 #[test]
1683 fn test_gate_fusion_compatibility() {
1684 let gate1 = QuantumGate::new(GateType::RotationX, vec![0], vec![0.5]);
1685 let gate2 = QuantumGate::new(GateType::RotationX, vec![0], vec![0.3]);
1686 let gate3 = QuantumGate::new(GateType::RotationY, vec![1], vec![0.2]);
1687
1688 assert!(gate1.can_fuse_with(&gate2)); assert!(!gate1.can_fuse_with(&gate3)); }
1691
1692 #[test]
1693 fn test_fused_gate_block() {
1694 let gates = vec![
1695 QuantumGate::new(GateType::RotationX, vec![0], vec![0.5]),
1696 QuantumGate::new(GateType::RotationX, vec![0], vec![0.3]),
1697 ];
1698
1699 let block = FusedGateBlock::new(gates).unwrap();
1700 assert_eq!(block.qubits, vec![0]);
1701 assert!(block.improvement_factor > 0.0);
1702 }
1703
1704 #[test]
1705 fn test_adaptive_fusion_config() {
1706 let config = AdaptiveFusionConfig::default();
1707 assert_eq!(config.strategy, FusionStrategy::Adaptive);
1708 assert_eq!(config.max_fusion_size, 8);
1709 assert!(config.enable_cross_qubit_fusion);
1710 }
1711
1712 #[test]
1713 fn test_circuit_analysis() {
1714 let gates = FusionUtils::create_test_sequence("rotation_chain", 2);
1715
1716 let config = AdaptiveFusionConfig::default();
1717 let mut fusion_engine = AdaptiveGateFusion::new(config).unwrap();
1718
1719 let analysis = fusion_engine.analyze_circuit(&gates).unwrap();
1720 assert_eq!(analysis.original_gate_count, gates.len());
1721 assert!(!analysis.fusion_opportunities.is_empty());
1722 }
1723
1724 #[test]
1725 fn test_fusion_utils_test_sequences() {
1726 let rotation_chain = FusionUtils::create_test_sequence("rotation_chain", 2);
1727 assert_eq!(rotation_chain.len(), 6); let cnot_ladder = FusionUtils::create_test_sequence("cnot_ladder", 3);
1730 assert_eq!(cnot_ladder.len(), 2); let mixed_gates = FusionUtils::create_test_sequence("mixed_gates", 2);
1733 assert!(!mixed_gates.is_empty());
1734 }
1735
1736 #[test]
1737 fn test_fusion_potential_estimation() {
1738 let gates = vec![
1739 QuantumGate::new(GateType::RotationX, vec![0], vec![0.1]),
1740 QuantumGate::new(GateType::RotationX, vec![0], vec![0.2]),
1741 QuantumGate::new(GateType::RotationY, vec![1], vec![0.3]),
1742 ];
1743
1744 let potential = FusionUtils::estimate_fusion_potential(&gates);
1745 assert!(potential > 0.0);
1746 assert!(potential <= 1.0);
1747 }
1748
1749 #[test]
1750 fn test_gate_matrix_generation() {
1751 let pauli_x = QuantumGate::new(GateType::PauliX, vec![0], vec![]);
1752 assert_eq!(pauli_x.matrix.shape(), &[2, 2]);
1753
1754 assert_abs_diff_eq!(pauli_x.matrix[[0, 1]].re, 1.0, epsilon = 1e-10);
1756 assert_abs_diff_eq!(pauli_x.matrix[[1, 0]].re, 1.0, epsilon = 1e-10);
1757 }
1758
1759 #[test]
1760 fn test_circuit_depth_calculation() {
1761 let gates = vec![
1762 QuantumGate::new(GateType::Hadamard, vec![0], vec![]),
1763 QuantumGate::new(GateType::CNOT, vec![0, 1], vec![]),
1764 QuantumGate::new(GateType::RotationZ, vec![1], vec![0.5]),
1765 ];
1766
1767 let config = AdaptiveFusionConfig::default();
1768 let fusion_engine = AdaptiveGateFusion::new(config).unwrap();
1769
1770 let depth = fusion_engine.calculate_circuit_depth(&gates);
1771 assert!(depth > 0);
1772 }
1773}