1use super::config::{EnhancedCrossCompilationConfig, TargetPlatform};
7use super::types::{IRGate, IROperation, IROperationType, QuantumIR, SourceCircuit, TargetCode};
8use quantrs2_core::error::{QuantRS2Error, QuantRS2Result};
9use std::collections::HashMap;
10use std::f64::consts::PI;
11use std::sync::{Arc, Mutex};
12
13pub struct MLCompilationOptimizer {
15 config: EnhancedCrossCompilationConfig,
16 model: Arc<Mutex<CompilationModel>>,
17 feature_extractor: Arc<CompilationFeatureExtractor>,
18}
19
20impl MLCompilationOptimizer {
21 pub fn new(config: EnhancedCrossCompilationConfig) -> Self {
22 Self {
23 config,
24 model: Arc::new(Mutex::new(CompilationModel::new())),
25 feature_extractor: Arc::new(CompilationFeatureExtractor::new()),
26 }
27 }
28
29 pub fn optimize(&self, ir: &QuantumIR, target: TargetPlatform) -> QuantRS2Result<QuantumIR> {
30 let features = self.feature_extractor.extract_features(ir, target)?;
31
32 let strategy = {
34 let model = self
35 .model
36 .lock()
37 .map_err(|e| QuantRS2Error::RuntimeError(format!("Model lock poisoned: {e}")))?;
38 model.predict_strategy(&features)?
39 };
40
41 let optimized = Self::apply_ml_optimizations(ir, &strategy)?;
43
44 Ok(optimized)
45 }
46
47 fn apply_ml_optimizations(
53 ir: &QuantumIR,
54 strategy: &MLOptimizationStrategy,
55 ) -> QuantRS2Result<QuantumIR> {
56 if strategy.transformations.is_empty() {
57 let ir = Self::apply_rotation_merging_transform(ir)?;
59 let ir = Self::apply_gate_fusion_transform(&ir)?;
60 let ir = Self::apply_commutation_transform(&ir)?;
61 let ir = Self::apply_decomposition_transform(&ir)?;
62 return Ok(ir);
63 }
64
65 let mut current = ir.clone();
66 for transform in &strategy.transformations {
67 current = match transform.transform_type {
68 TransformationType::GateFusion => Self::apply_gate_fusion_transform(¤t)?,
69 TransformationType::RotationMerging => {
70 Self::apply_rotation_merging_transform(¤t)?
71 }
72 TransformationType::Commutation => Self::apply_commutation_transform(¤t)?,
73 TransformationType::Decomposition => Self::apply_decomposition_transform(¤t)?,
74 };
75 }
76 Ok(current)
77 }
78
79 fn is_single_qubit_gate(gate: &IRGate) -> bool {
85 matches!(
86 gate,
87 IRGate::H
88 | IRGate::X
89 | IRGate::Y
90 | IRGate::Z
91 | IRGate::S
92 | IRGate::T
93 | IRGate::RX(_)
94 | IRGate::RY(_)
95 | IRGate::RZ(_)
96 | IRGate::U1(_)
97 | IRGate::U2(_, _)
98 | IRGate::U3(_, _, _)
99 )
100 }
101
102 fn op_qubits(op: &IROperation) -> Vec<usize> {
104 let mut q = op.qubits.clone();
105 q.extend_from_slice(&op.controls);
106 q.sort_unstable();
107 q.dedup();
108 q
109 }
110
111 fn qubits_are_disjoint(a: &IROperation, b: &IROperation) -> bool {
113 let qa = Self::op_qubits(a);
114 let qb = Self::op_qubits(b);
115 !qa.iter().any(|q| qb.contains(q))
116 }
117
118 fn apply_rotation_merging_transform(ir: &QuantumIR) -> QuantRS2Result<QuantumIR> {
126 const EPSILON: f64 = 1e-9;
127 let ops = &ir.operations;
128 let mut result: Vec<IROperation> = Vec::with_capacity(ops.len());
129
130 for op in ops {
131 let merged = if let Some(last) = result.last_mut() {
132 if last.qubits.len() == 1 && op.qubits.len() == 1 && last.qubits[0] == op.qubits[0]
134 {
135 Self::try_merge_rotations(&last.operation_type, &op.operation_type)
136 } else {
137 None
138 }
139 } else {
140 None
141 };
142
143 match merged {
144 Some(Some(merged_type)) => {
145 let last = result.last_mut().ok_or_else(|| {
147 QuantRS2Error::RuntimeError("Internal merge error".to_string())
148 })?;
149 last.operation_type = merged_type;
150 }
151 Some(None) => {
152 result.pop();
154 }
155 None => {
156 result.push(op.clone());
157 }
158 }
159 }
160
161 let mut out = ir.clone();
162 out.operations = result;
163 Ok(out)
164 }
165
166 fn try_merge_rotations(
173 a: &IROperationType,
174 b: &IROperationType,
175 ) -> Option<Option<IROperationType>> {
176 const EPSILON: f64 = 1e-9;
177 let two_pi = 2.0 * PI;
178
179 match (a, b) {
180 (IROperationType::Gate(IRGate::RX(t1)), IROperationType::Gate(IRGate::RX(t2))) => {
181 let sum = (t1 + t2).rem_euclid(two_pi);
182 if sum.abs() < EPSILON || (sum - two_pi).abs() < EPSILON {
183 Some(None)
184 } else {
185 Some(Some(IROperationType::Gate(IRGate::RX(sum))))
186 }
187 }
188 (IROperationType::Gate(IRGate::RY(t1)), IROperationType::Gate(IRGate::RY(t2))) => {
189 let sum = (t1 + t2).rem_euclid(two_pi);
190 if sum.abs() < EPSILON || (sum - two_pi).abs() < EPSILON {
191 Some(None)
192 } else {
193 Some(Some(IROperationType::Gate(IRGate::RY(sum))))
194 }
195 }
196 (IROperationType::Gate(IRGate::RZ(t1)), IROperationType::Gate(IRGate::RZ(t2))) => {
197 let sum = (t1 + t2).rem_euclid(two_pi);
198 if sum.abs() < EPSILON || (sum - two_pi).abs() < EPSILON {
199 Some(None)
200 } else {
201 Some(Some(IROperationType::Gate(IRGate::RZ(sum))))
202 }
203 }
204 (IROperationType::Gate(IRGate::U1(t1)), IROperationType::Gate(IRGate::U1(t2))) => {
205 let sum = (t1 + t2).rem_euclid(two_pi);
206 if sum.abs() < EPSILON || (sum - two_pi).abs() < EPSILON {
207 Some(None)
208 } else {
209 Some(Some(IROperationType::Gate(IRGate::U1(sum))))
210 }
211 }
212 _ => None,
213 }
214 }
215
216 fn apply_gate_fusion_transform(ir: &QuantumIR) -> QuantRS2Result<QuantumIR> {
226 const EPSILON: f64 = 1e-9;
231 let ops = &ir.operations;
232 let mut result: Vec<IROperation> = Vec::with_capacity(ops.len());
233
234 for op in ops {
235 let action = if let Some(last) = result.last() {
236 if last.qubits.len() == 1 && op.qubits.len() == 1 && last.qubits[0] == op.qubits[0]
237 {
238 let rotation_merge =
240 Self::try_merge_rotations(&last.operation_type, &op.operation_type);
241 if rotation_merge.is_some() {
242 rotation_merge.map(|inner| ("rotation", inner))
243 } else {
244 Self::try_fuse_self_inverse(&last.operation_type, &op.operation_type)
246 .map(|_| ("cancel", None))
247 }
248 } else {
249 None
250 }
251 } else {
252 None
253 };
254
255 match action {
256 Some(("rotation", Some(merged_type))) => {
257 let last = result.last_mut().ok_or_else(|| {
258 QuantRS2Error::RuntimeError("Internal fusion error".to_string())
259 })?;
260 last.operation_type = merged_type;
261 }
262 Some((_, None)) => {
263 result.pop();
265 }
266 _ => {
267 result.push(op.clone());
268 }
269 }
270 }
271
272 let mut out = ir.clone();
273 out.operations = result;
274 Ok(out)
275 }
276
277 fn try_fuse_self_inverse(a: &IROperationType, b: &IROperationType) -> Option<()> {
279 match (a, b) {
280 (IROperationType::Gate(IRGate::H), IROperationType::Gate(IRGate::H))
281 | (IROperationType::Gate(IRGate::X), IROperationType::Gate(IRGate::X))
282 | (IROperationType::Gate(IRGate::Y), IROperationType::Gate(IRGate::Y))
283 | (IROperationType::Gate(IRGate::Z), IROperationType::Gate(IRGate::Z))
284 | (IROperationType::Gate(IRGate::CNOT), IROperationType::Gate(IRGate::CNOT))
285 | (IROperationType::Gate(IRGate::CZ), IROperationType::Gate(IRGate::CZ)) => Some(()),
286 _ => None,
287 }
288 }
289
290 fn apply_commutation_transform(ir: &QuantumIR) -> QuantRS2Result<QuantumIR> {
301 let mut ops = ir.operations.clone();
302 let n = ops.len();
303
304 let mut i = 1;
305 while i < n {
306 let commutes = Self::qubits_are_disjoint(&ops[i - 1], &ops[i]);
307 if commutes {
308 let enables_fusion = i >= 2
311 && ops[i].qubits == ops[i - 2].qubits
312 && std::mem::discriminant(&ops[i].operation_type)
313 == std::mem::discriminant(&ops[i - 2].operation_type);
314 if enables_fusion {
315 ops.swap(i - 1, i);
316 }
317 }
318 i += 1;
319 }
320
321 let mut out = ir.clone();
322 out.operations = ops;
323 Ok(out)
324 }
325
326 fn apply_decomposition_transform(ir: &QuantumIR) -> QuantRS2Result<QuantumIR> {
339 let mut out_ops: Vec<IROperation> = Vec::new();
340
341 for op in &ir.operations {
342 match &op.operation_type {
343 IROperationType::Gate(IRGate::Toffoli) if op.qubits.len() >= 3 => {
344 let (c1, c2, t) = (op.qubits[0], op.qubits[1], op.qubits[2]);
345 out_ops.extend(Self::decompose_toffoli(c1, c2, t));
346 }
347 IROperationType::Gate(IRGate::SWAP) if op.qubits.len() >= 2 => {
348 let (a, b) = (op.qubits[0], op.qubits[1]);
349 out_ops.extend(Self::decompose_swap(a, b));
350 }
351 IROperationType::Gate(IRGate::Fredkin) if op.qubits.len() >= 3 => {
352 let (ctrl, a, b) = (op.qubits[0], op.qubits[1], op.qubits[2]);
353 out_ops.extend(Self::decompose_fredkin(ctrl, a, b));
354 }
355 _ => {
356 out_ops.push(op.clone());
357 }
358 }
359 }
360
361 let mut result = ir.clone();
362 result.operations = out_ops;
363 Ok(result)
364 }
365
366 fn single_qubit_op(gate: IRGate, qubit: usize) -> IROperation {
368 IROperation {
369 operation_type: IROperationType::Gate(gate),
370 qubits: vec![qubit],
371 controls: vec![],
372 parameters: vec![],
373 }
374 }
375
376 fn two_qubit_op(gate: IRGate, q0: usize, q1: usize) -> IROperation {
378 IROperation {
379 operation_type: IROperationType::Gate(gate),
380 qubits: vec![q0, q1],
381 controls: vec![],
382 parameters: vec![],
383 }
384 }
385
386 fn decompose_toffoli(c1: usize, c2: usize, t: usize) -> Vec<IROperation> {
391 let tdg = |q| Self::single_qubit_op(IRGate::U1(-PI / 4.0), q);
392 let tgate = |q| Self::single_qubit_op(IRGate::T, q);
393 let hgate = |q| Self::single_qubit_op(IRGate::H, q);
394 let cnot = |ctrl, tgt| Self::two_qubit_op(IRGate::CNOT, ctrl, tgt);
395
396 vec![
397 hgate(t),
398 cnot(c2, t),
399 tdg(t),
400 cnot(c1, t),
401 tgate(t),
402 cnot(c2, t),
403 tdg(t),
404 cnot(c1, t),
405 tgate(c2),
406 tgate(t),
407 hgate(t),
408 cnot(c1, c2),
409 tgate(c1),
410 tdg(c2),
411 cnot(c1, c2),
412 ]
413 }
414
415 fn decompose_swap(a: usize, b: usize) -> Vec<IROperation> {
417 vec![
418 Self::two_qubit_op(IRGate::CNOT, a, b),
419 Self::two_qubit_op(IRGate::CNOT, b, a),
420 Self::two_qubit_op(IRGate::CNOT, a, b),
421 ]
422 }
423
424 fn decompose_fredkin(ctrl: usize, a: usize, b: usize) -> Vec<IROperation> {
426 let mut ops = vec![Self::two_qubit_op(IRGate::CNOT, b, a)];
427 ops.extend(Self::decompose_toffoli(ctrl, a, b));
428 ops.push(Self::two_qubit_op(IRGate::CNOT, b, a));
429 ops
430 }
431}
432
433pub struct CompilationMonitor {
435 config: EnhancedCrossCompilationConfig,
436 metrics: Arc<Mutex<CompilationMetrics>>,
437}
438
439impl CompilationMonitor {
440 pub fn new(config: EnhancedCrossCompilationConfig) -> Self {
441 Self {
442 config,
443 metrics: Arc::new(Mutex::new(CompilationMetrics::new())),
444 }
445 }
446
447 pub fn update_optimization_progress(&self, ir: &QuantumIR) -> QuantRS2Result<()> {
448 let anomaly = {
449 let mut metrics = self
450 .metrics
451 .lock()
452 .map_err(|e| QuantRS2Error::RuntimeError(format!("Metrics lock poisoned: {e}")))?;
453 metrics.update(ir)?;
454 metrics.detect_anomaly()
455 }; if anomaly {
459 }
461
462 Ok(())
463 }
464}
465
466pub struct CompilationValidator {
468 config: EnhancedCrossCompilationConfig,
469}
470
471impl CompilationValidator {
472 pub const fn new(config: EnhancedCrossCompilationConfig) -> Self {
473 Self { config }
474 }
475
476 pub fn validate_compilation(
477 &self,
478 source: &SourceCircuit,
479 target_code: &TargetCode,
480 platform: TargetPlatform,
481 ) -> QuantRS2Result<super::types::ValidationResult> {
482 let mut result = super::types::ValidationResult::new();
483
484 if self.config.base_config.preserve_semantics {
486 let semantic_valid = self.validate_semantics(source, target_code)?;
487 result.semantic_validation = Some(semantic_valid);
488 }
489
490 let resource_valid = self.validate_resources(target_code, platform)?;
492 result.resource_validation = Some(resource_valid);
493
494 let fidelity = self.estimate_fidelity(source, target_code)?;
496 result.fidelity_estimate = Some(fidelity);
497
498 result.is_valid = result.semantic_validation.unwrap_or(true)
499 && result.resource_validation.unwrap_or(true)
500 && fidelity >= self.config.base_config.validation_threshold;
501
502 Ok(result)
503 }
504
505 pub const fn validate_semantics(
506 &self,
507 _source: &SourceCircuit,
508 _target: &TargetCode,
509 ) -> QuantRS2Result<bool> {
510 Ok(true)
512 }
513
514 pub const fn validate_resources(
515 &self,
516 _target: &TargetCode,
517 _platform: TargetPlatform,
518 ) -> QuantRS2Result<bool> {
519 Ok(true)
521 }
522
523 pub const fn estimate_fidelity(
524 &self,
525 _source: &SourceCircuit,
526 _target: &TargetCode,
527 ) -> QuantRS2Result<f64> {
528 Ok(0.99)
530 }
531}
532
533pub struct MLOptimizationStrategy {
535 pub transformations: Vec<IRTransformation>,
536 pub confidence: f64,
537}
538
539pub struct IRTransformation {
541 pub transform_type: TransformationType,
542 pub parameters: HashMap<String, f64>,
543}
544
545pub enum TransformationType {
547 GateFusion,
548 RotationMerging,
549 Commutation,
550 Decomposition,
551}
552
553pub struct CompilationModel {
555 }
557
558impl CompilationModel {
559 pub const fn new() -> Self {
560 Self {}
561 }
562
563 pub const fn predict_strategy(
564 &self,
565 _features: &CompilationFeatures,
566 ) -> QuantRS2Result<MLOptimizationStrategy> {
567 Ok(MLOptimizationStrategy {
569 transformations: vec![],
570 confidence: 0.9,
571 })
572 }
573}
574
575impl Default for CompilationModel {
576 fn default() -> Self {
577 Self::new()
578 }
579}
580
581pub struct CompilationFeatureExtractor {
583 }
585
586impl CompilationFeatureExtractor {
587 pub const fn new() -> Self {
588 Self {}
589 }
590
591 pub const fn extract_features(
592 &self,
593 _ir: &QuantumIR,
594 _target: TargetPlatform,
595 ) -> QuantRS2Result<CompilationFeatures> {
596 Ok(CompilationFeatures {
597 circuit_features: vec![],
598 target_features: vec![],
599 complexity_features: vec![],
600 })
601 }
602}
603
604impl Default for CompilationFeatureExtractor {
605 fn default() -> Self {
606 Self::new()
607 }
608}
609
610pub struct CompilationFeatures {
612 pub circuit_features: Vec<f64>,
613 pub target_features: Vec<f64>,
614 pub complexity_features: Vec<f64>,
615}
616
617pub struct CompilationMetrics {
619 pub gate_count: usize,
620 pub circuit_depth: usize,
621 pub optimization_count: usize,
622}
623
624impl CompilationMetrics {
625 pub const fn new() -> Self {
626 Self {
627 gate_count: 0,
628 circuit_depth: 0,
629 optimization_count: 0,
630 }
631 }
632
633 pub fn update(&mut self, ir: &QuantumIR) -> QuantRS2Result<()> {
634 self.gate_count = ir.operations.len();
635 Ok(())
637 }
638
639 pub const fn detect_anomaly(&self) -> bool {
640 false
642 }
643}
644
645impl Default for CompilationMetrics {
646 fn default() -> Self {
647 Self::new()
648 }
649}
650
651pub struct TargetSpecification {
653 pub native_gates: Vec<IRGate>,
654 pub connectivity: Vec<(usize, usize)>,
655 pub error_rates: HashMap<String, f64>,
656}
657
658pub struct CompilationCache {
660 pub cache: HashMap<(String, TargetPlatform), super::types::CrossCompilationResult>,
661}
662
663impl CompilationCache {
664 pub fn new() -> Self {
665 Self {
666 cache: HashMap::new(),
667 }
668 }
669}
670
671impl Default for CompilationCache {
672 fn default() -> Self {
673 Self::new()
674 }
675}
676
677#[cfg(test)]
678mod tests {
679 use super::*;
680 use std::collections::HashMap;
681
682 fn build_ir(num_qubits: usize, ops: Vec<IROperation>) -> QuantumIR {
684 QuantumIR {
685 num_qubits,
686 num_classical_bits: 0,
687 operations: ops,
688 classical_operations: vec![],
689 metadata: HashMap::new(),
690 }
691 }
692
693 fn single_gate(gate: IRGate, qubit: usize) -> IROperation {
695 IROperation {
696 operation_type: IROperationType::Gate(gate),
697 qubits: vec![qubit],
698 controls: vec![],
699 parameters: vec![],
700 }
701 }
702
703 fn two_qubit_gate(gate: IRGate, q0: usize, q1: usize) -> IROperation {
705 IROperation {
706 operation_type: IROperationType::Gate(gate),
707 qubits: vec![q0, q1],
708 controls: vec![],
709 parameters: vec![],
710 }
711 }
712
713 fn three_qubit_gate(gate: IRGate, q0: usize, q1: usize, q2: usize) -> IROperation {
715 IROperation {
716 operation_type: IROperationType::Gate(gate),
717 qubits: vec![q0, q1, q2],
718 controls: vec![],
719 parameters: vec![],
720 }
721 }
722
723 #[test]
728 fn test_rotation_merging_combines_rx_angles() {
729 let ir = build_ir(
730 1,
731 vec![
732 single_gate(IRGate::RX(0.5), 0),
733 single_gate(IRGate::RX(0.3), 0),
734 ],
735 );
736 let result = MLCompilationOptimizer::apply_rotation_merging_transform(&ir).unwrap();
737 assert_eq!(
738 result.operations.len(),
739 1,
740 "two RX gates should merge to one"
741 );
742 match &result.operations[0].operation_type {
743 IROperationType::Gate(IRGate::RX(angle)) => {
744 let expected = (0.5f64 + 0.3).rem_euclid(2.0 * std::f64::consts::PI);
745 assert!(
746 (angle - expected).abs() < 1e-9,
747 "merged angle should be 0.8, got {angle}"
748 );
749 }
750 other => panic!("expected RX gate, got {other:?}"),
751 }
752 }
753
754 #[test]
755 fn test_rotation_merging_removes_cancelling_rx() {
756 let angle = std::f64::consts::PI;
757 let ir = build_ir(
758 1,
759 vec![
760 single_gate(IRGate::RX(angle), 0),
761 single_gate(IRGate::RX(-angle), 0),
762 ],
763 );
764 let result = MLCompilationOptimizer::apply_rotation_merging_transform(&ir).unwrap();
765 assert_eq!(
766 result.operations.len(),
767 0,
768 "RX(π) + RX(-π) should cancel to zero gates"
769 );
770 }
771
772 #[test]
773 fn test_rotation_merging_different_qubits_unchanged() {
774 let ir = build_ir(
775 2,
776 vec![
777 single_gate(IRGate::RX(0.5), 0),
778 single_gate(IRGate::RX(0.5), 1), ],
780 );
781 let result = MLCompilationOptimizer::apply_rotation_merging_transform(&ir).unwrap();
782 assert_eq!(
783 result.operations.len(),
784 2,
785 "gates on different qubits must not merge"
786 );
787 }
788
789 #[test]
790 fn test_rotation_merging_different_types_unchanged() {
791 let ir = build_ir(
792 1,
793 vec![
794 single_gate(IRGate::RX(0.5), 0),
795 single_gate(IRGate::RY(0.5), 0), ],
797 );
798 let result = MLCompilationOptimizer::apply_rotation_merging_transform(&ir).unwrap();
799 assert_eq!(
800 result.operations.len(),
801 2,
802 "RX + RY on same qubit must not merge"
803 );
804 }
805
806 #[test]
811 fn test_gate_fusion_reduces_same_type_rotations() {
812 let ir = build_ir(
813 1,
814 vec![
815 single_gate(IRGate::RZ(1.0), 0),
816 single_gate(IRGate::RZ(0.5), 0),
817 ],
818 );
819 let result = MLCompilationOptimizer::apply_gate_fusion_transform(&ir).unwrap();
820 assert_eq!(
821 result.operations.len(),
822 1,
823 "consecutive RZ on same qubit should fuse to 1 gate"
824 );
825 }
826
827 #[test]
828 fn test_gate_fusion_cancels_h_h() {
829 let ir = build_ir(
831 1,
832 vec![single_gate(IRGate::H, 0), single_gate(IRGate::H, 0)],
833 );
834 let result = MLCompilationOptimizer::apply_gate_fusion_transform(&ir).unwrap();
835 assert_eq!(
836 result.operations.len(),
837 0,
838 "H followed by H should cancel to zero gates"
839 );
840 }
841
842 #[test]
843 fn test_gate_fusion_cancels_x_x() {
844 let ir = build_ir(
845 1,
846 vec![single_gate(IRGate::X, 0), single_gate(IRGate::X, 0)],
847 );
848 let result = MLCompilationOptimizer::apply_gate_fusion_transform(&ir).unwrap();
849 assert_eq!(result.operations.len(), 0, "X ∘ X should cancel");
850 }
851
852 #[test]
857 fn test_commutation_reorders_disjoint_qubits() {
858 let ir = build_ir(
866 2,
867 vec![
868 single_gate(IRGate::RX(0.5), 0),
869 single_gate(IRGate::RX(0.5), 1),
870 single_gate(IRGate::RX(0.3), 0),
871 ],
872 );
873 let result = MLCompilationOptimizer::apply_commutation_transform(&ir).unwrap();
874 assert_eq!(
876 result.operations.len(),
877 3,
878 "commutation preserves gate count"
879 );
880 }
881
882 #[test]
883 fn test_commutation_enables_downstream_fusion() {
884 let ir = build_ir(
888 2,
889 vec![
890 single_gate(IRGate::RX(0.5), 0),
891 single_gate(IRGate::RX(0.5), 1), single_gate(IRGate::RX(0.3), 0),
893 ],
894 );
895 let commuted = MLCompilationOptimizer::apply_commutation_transform(&ir).unwrap();
896 let fused = MLCompilationOptimizer::apply_rotation_merging_transform(&commuted).unwrap();
899 assert_eq!(
900 fused.operations.len(),
901 2,
902 "commutation + rotation-merge should collapse two RX(q=0) into one"
903 );
904 }
905
906 #[test]
911 fn test_decomposition_toffoli_produces_15_gates() {
912 let ir = build_ir(3, vec![three_qubit_gate(IRGate::Toffoli, 0, 1, 2)]);
913 let result = MLCompilationOptimizer::apply_decomposition_transform(&ir).unwrap();
914 assert_eq!(
915 result.operations.len(),
916 15,
917 "Toffoli should decompose into exactly 15 primitive gates"
918 );
919 }
920
921 #[test]
922 fn test_decomposition_swap_produces_3_cnots() {
923 let ir = build_ir(2, vec![two_qubit_gate(IRGate::SWAP, 0, 1)]);
924 let result = MLCompilationOptimizer::apply_decomposition_transform(&ir).unwrap();
925 assert_eq!(
926 result.operations.len(),
927 3,
928 "SWAP should decompose into exactly 3 CNOT gates"
929 );
930 for op in &result.operations {
931 assert!(
932 matches!(&op.operation_type, IROperationType::Gate(IRGate::CNOT)),
933 "each SWAP decomposition gate should be a CNOT, got {:?}",
934 op.operation_type
935 );
936 }
937 }
938
939 #[test]
940 fn test_decomposition_non_compound_passes_through() {
941 let ir = build_ir(
942 1,
943 vec![single_gate(IRGate::H, 0), single_gate(IRGate::RX(1.0), 0)],
944 );
945 let result = MLCompilationOptimizer::apply_decomposition_transform(&ir).unwrap();
946 assert_eq!(
947 result.operations.len(),
948 2,
949 "non-compound gates should pass through unchanged"
950 );
951 }
952
953 #[test]
958 fn test_apply_ml_optimizations_fallback_path() {
959 let strategy = MLOptimizationStrategy {
961 transformations: vec![],
962 confidence: 0.9,
963 };
964 let ir = build_ir(
965 1,
966 vec![
967 single_gate(IRGate::RX(0.5), 0),
968 single_gate(IRGate::RX(0.5), 0),
969 ],
970 );
971 let result = MLCompilationOptimizer::apply_ml_optimizations(&ir, &strategy).unwrap();
972 assert_eq!(
974 result.operations.len(),
975 1,
976 "fallback path should apply rotation merging and fuse the two RX gates"
977 );
978 }
979}