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 fn validate_semantics(
519 &self,
520 source: &SourceCircuit,
521 target: &TargetCode,
522 ) -> QuantRS2Result<bool> {
523 let source_gate_counts = extract_gate_token_counts(&source.code);
524 let target_gate_counts = extract_gate_token_counts(&target.code);
525 let similarity = gate_histogram_similarity(&source_gate_counts, &target_gate_counts);
526
527 Ok(similarity >= SEMANTIC_SIMILARITY_THRESHOLD)
528 }
529
530 pub fn validate_resources(
535 &self,
536 target: &TargetCode,
537 platform: TargetPlatform,
538 ) -> QuantRS2Result<bool> {
539 let estimated_qubits = estimate_qubit_count_from_code(&target.code);
540 let platform_capacity = platform_max_qubits(platform);
541
542 Ok(estimated_qubits <= platform_capacity)
543 }
544
545 pub fn estimate_fidelity(
555 &self,
556 source: &SourceCircuit,
557 target: &TargetCode,
558 ) -> QuantRS2Result<f64> {
559 let target_gate_counts = extract_gate_token_counts(&target.code);
560 let (single_qubit_fidelity, two_qubit_fidelity) = platform_gate_fidelities(target.platform);
561
562 let single_qubit_gate_count: i32 = SINGLE_QUBIT_GATE_TOKENS
563 .iter()
564 .map(|name| *target_gate_counts.get(*name).unwrap_or(&0) as i32)
565 .sum();
566 let two_qubit_gate_count: i32 = TWO_QUBIT_GATE_TOKENS
567 .iter()
568 .map(|name| *target_gate_counts.get(*name).unwrap_or(&0) as i32)
569 .sum();
570
571 let gate_composition_fidelity = single_qubit_fidelity.powi(single_qubit_gate_count)
572 * two_qubit_fidelity.powi(two_qubit_gate_count);
573
574 let source_gate_counts = extract_gate_token_counts(&source.code);
575 let structural_similarity =
576 gate_histogram_similarity(&source_gate_counts, &target_gate_counts);
577
578 Ok((gate_composition_fidelity * structural_similarity).clamp(0.0, 1.0))
579 }
580}
581
582const SINGLE_QUBIT_GATE_TOKENS: [&str; 13] = [
585 "h", "x", "y", "z", "s", "sdg", "t", "tdg", "rx", "ry", "rz", "u1", "u2",
586];
587const TWO_QUBIT_GATE_TOKENS: [&str; 6] = ["cx", "cnot", "cz", "swap", "iswap", "ch"];
588const MULTI_QUBIT_GATE_TOKENS: [&str; 4] = ["ccx", "toffoli", "cswap", "fredkin"];
589
590fn extract_gate_token_counts(code: &str) -> HashMap<String, usize> {
593 let mut counts = HashMap::new();
594 for raw_token in code.split(|c: char| !c.is_ascii_alphanumeric()) {
595 if raw_token.is_empty() {
596 continue;
597 }
598 let token = raw_token.to_ascii_lowercase();
599 let is_known_gate = SINGLE_QUBIT_GATE_TOKENS.contains(&token.as_str())
600 || TWO_QUBIT_GATE_TOKENS.contains(&token.as_str())
601 || MULTI_QUBIT_GATE_TOKENS.contains(&token.as_str());
602 if is_known_gate {
603 *counts.entry(token).or_insert(0_usize) += 1;
604 }
605 }
606 counts
607}
608
609fn gate_histogram_similarity(a: &HashMap<String, usize>, b: &HashMap<String, usize>) -> f64 {
614 let all_tokens = SINGLE_QUBIT_GATE_TOKENS
615 .iter()
616 .chain(TWO_QUBIT_GATE_TOKENS.iter())
617 .chain(MULTI_QUBIT_GATE_TOKENS.iter());
618
619 let mut dot_product = 0.0_f64;
620 let mut norm_a = 0.0_f64;
621 let mut norm_b = 0.0_f64;
622 for token in all_tokens {
623 let a_count = *a.get(*token).unwrap_or(&0) as f64;
624 let b_count = *b.get(*token).unwrap_or(&0) as f64;
625 dot_product += a_count * b_count;
626 norm_a += a_count * a_count;
627 norm_b += b_count * b_count;
628 }
629
630 if norm_a == 0.0 && norm_b == 0.0 {
631 1.0
632 } else if norm_a == 0.0 || norm_b == 0.0 {
633 0.0
634 } else {
635 dot_product / (norm_a.sqrt() * norm_b.sqrt())
636 }
637}
638
639const SEMANTIC_SIMILARITY_THRESHOLD: f64 = 0.5;
642
643fn estimate_qubit_count_from_code(code: &str) -> usize {
649 let mut max_index: Option<usize> = None;
650 let mut digits = String::new();
651 let mut chars = code.chars().peekable();
652
653 while let Some(c) = chars.next() {
654 if c == '[' || c == '(' {
655 digits.clear();
656 while let Some(&next) = chars.peek() {
657 if next.is_ascii_digit() {
658 digits.push(next);
659 chars.next();
660 } else {
661 break;
662 }
663 }
664 if let Ok(index) = digits.parse::<usize>() {
665 max_index = Some(max_index.map_or(index, |current| current.max(index)));
666 }
667 }
668 }
669
670 max_index.map_or(0, |index| index + 1)
671}
672
673fn estimate_circuit_depth(ir: &QuantumIR) -> usize {
678 let mut qubit_layer: HashMap<usize, usize> = HashMap::new();
679 let mut max_layer = 0_usize;
680
681 for op in &ir.operations {
682 let touched_qubits: Vec<usize> = match &op.operation_type {
683 IROperationType::Gate(_) => {
684 let mut qubits = op.qubits.clone();
685 qubits.extend_from_slice(&op.controls);
686 qubits
687 }
688 IROperationType::Measurement(qubits, _)
689 | IROperationType::Reset(qubits)
690 | IROperationType::Barrier(qubits) => qubits.clone(),
691 };
692
693 if touched_qubits.is_empty() {
694 continue;
695 }
696
697 let current_layer = touched_qubits
698 .iter()
699 .map(|q| qubit_layer.get(q).copied().unwrap_or(0))
700 .max()
701 .unwrap_or(0);
702 let new_layer = current_layer + 1;
703
704 for q in &touched_qubits {
705 qubit_layer.insert(*q, new_layer);
706 }
707 max_layer = max_layer.max(new_layer);
708 }
709
710 max_layer
711}
712
713const fn platform_max_qubits(platform: TargetPlatform) -> usize {
716 match platform {
717 TargetPlatform::IBMQuantum => 127,
718 TargetPlatform::GoogleSycamore => 70,
719 TargetPlatform::IonQ => 32,
720 TargetPlatform::Rigetti => 80,
721 TargetPlatform::Honeywell => 32,
722 TargetPlatform::AWSBraket => 34,
723 TargetPlatform::AzureQuantum => 40,
724 TargetPlatform::Simulator => 1_000,
725 }
726}
727
728const fn platform_gate_fidelities(platform: TargetPlatform) -> (f64, f64) {
732 match platform {
733 TargetPlatform::IBMQuantum => (0.9999, 0.99),
734 TargetPlatform::GoogleSycamore => (0.9998, 0.995),
735 TargetPlatform::IonQ => (0.9995, 0.998),
736 TargetPlatform::Rigetti => (0.999, 0.98),
737 TargetPlatform::Honeywell => (0.9999, 0.998),
738 TargetPlatform::AWSBraket => (0.999, 0.99),
739 TargetPlatform::AzureQuantum => (0.999, 0.99),
740 TargetPlatform::Simulator => (1.0, 1.0),
741 }
742}
743
744pub struct MLOptimizationStrategy {
746 pub transformations: Vec<IRTransformation>,
747 pub confidence: f64,
748}
749
750pub struct IRTransformation {
752 pub transform_type: TransformationType,
753 pub parameters: HashMap<String, f64>,
754}
755
756pub enum TransformationType {
758 GateFusion,
759 RotationMerging,
760 Commutation,
761 Decomposition,
762}
763
764pub struct CompilationModel {
766 }
768
769impl CompilationModel {
770 pub const fn new() -> Self {
771 Self {}
772 }
773
774 pub fn predict_strategy(
782 &self,
783 features: &CompilationFeatures,
784 ) -> QuantRS2Result<MLOptimizationStrategy> {
785 let total_gates = features.circuit_features.get(1).copied().unwrap_or(0.0);
786 let two_qubit_gates = features.circuit_features.get(3).copied().unwrap_or(0.0);
787 let rotation_gates = features.circuit_features.get(5).copied().unwrap_or(0.0);
788 let compound_gates = features.circuit_features.get(6).copied().unwrap_or(0.0);
789
790 let mut transformations = Vec::new();
791 if rotation_gates > 0.0 {
792 transformations.push(IRTransformation {
793 transform_type: TransformationType::RotationMerging,
794 parameters: HashMap::new(),
795 });
796 }
797 if two_qubit_gates > 0.0 || total_gates > 1.0 {
798 transformations.push(IRTransformation {
799 transform_type: TransformationType::GateFusion,
800 parameters: HashMap::new(),
801 });
802 transformations.push(IRTransformation {
803 transform_type: TransformationType::Commutation,
804 parameters: HashMap::new(),
805 });
806 }
807 if compound_gates > 0.0 {
808 transformations.push(IRTransformation {
809 transform_type: TransformationType::Decomposition,
810 parameters: HashMap::new(),
811 });
812 }
813
814 let confidence = if total_gates <= 0.0 {
819 0.5
820 } else {
821 (0.5 + 0.5 * (total_gates / (total_gates + 10.0))).min(0.99)
822 };
823
824 Ok(MLOptimizationStrategy {
825 transformations,
826 confidence,
827 })
828 }
829}
830
831impl Default for CompilationModel {
832 fn default() -> Self {
833 Self::new()
834 }
835}
836
837pub struct CompilationFeatureExtractor {
839 }
841
842impl CompilationFeatureExtractor {
843 pub const fn new() -> Self {
844 Self {}
845 }
846
847 pub fn extract_features(
858 &self,
859 ir: &QuantumIR,
860 target: TargetPlatform,
861 ) -> QuantRS2Result<CompilationFeatures> {
862 let mut single_qubit_gates = 0.0_f64;
863 let mut two_qubit_gates = 0.0_f64;
864 let mut multi_qubit_gates = 0.0_f64;
865 let mut rotation_gates = 0.0_f64;
866 let mut compound_gates = 0.0_f64;
867 let mut total_gates = 0.0_f64;
868
869 for op in &ir.operations {
870 if let IROperationType::Gate(gate) = &op.operation_type {
871 total_gates += 1.0;
872 match op.qubits.len() {
873 1 => single_qubit_gates += 1.0,
874 2 => two_qubit_gates += 1.0,
875 _ => multi_qubit_gates += 1.0,
876 }
877 if matches!(
878 gate,
879 IRGate::RX(_)
880 | IRGate::RY(_)
881 | IRGate::RZ(_)
882 | IRGate::U1(_)
883 | IRGate::U2(_, _)
884 | IRGate::U3(_, _, _)
885 ) {
886 rotation_gates += 1.0;
887 }
888 if matches!(gate, IRGate::Toffoli | IRGate::Fredkin | IRGate::SWAP) {
889 compound_gates += 1.0;
890 }
891 }
892 }
893
894 let estimated_depth = estimate_circuit_depth(ir) as f64;
895 let (single_qubit_fidelity, two_qubit_fidelity) = platform_gate_fidelities(target);
896 let platform_capacity = platform_max_qubits(target) as f64;
897
898 let gate_density = if ir.num_qubits > 0 {
899 total_gates / ir.num_qubits as f64
900 } else {
901 0.0
902 };
903 let two_qubit_ratio = if total_gates > 0.0 {
904 two_qubit_gates / total_gates
905 } else {
906 0.0
907 };
908
909 Ok(CompilationFeatures {
910 circuit_features: vec![
911 ir.num_qubits as f64,
912 total_gates,
913 single_qubit_gates,
914 two_qubit_gates,
915 multi_qubit_gates,
916 rotation_gates,
917 compound_gates,
918 ],
919 target_features: vec![platform_capacity, single_qubit_fidelity, two_qubit_fidelity],
920 complexity_features: vec![gate_density, two_qubit_ratio, estimated_depth],
921 })
922 }
923}
924
925impl Default for CompilationFeatureExtractor {
926 fn default() -> Self {
927 Self::new()
928 }
929}
930
931pub struct CompilationFeatures {
933 pub circuit_features: Vec<f64>,
934 pub target_features: Vec<f64>,
935 pub complexity_features: Vec<f64>,
936}
937
938pub struct CompilationMetrics {
940 pub gate_count: usize,
941 pub circuit_depth: usize,
942 pub optimization_count: usize,
943}
944
945impl CompilationMetrics {
946 pub const fn new() -> Self {
947 Self {
948 gate_count: 0,
949 circuit_depth: 0,
950 optimization_count: 0,
951 }
952 }
953
954 pub fn update(&mut self, ir: &QuantumIR) -> QuantRS2Result<()> {
955 self.gate_count = ir.operations.len();
956 Ok(())
958 }
959
960 pub const fn detect_anomaly(&self) -> bool {
961 false
963 }
964}
965
966impl Default for CompilationMetrics {
967 fn default() -> Self {
968 Self::new()
969 }
970}
971
972pub struct TargetSpecification {
974 pub native_gates: Vec<IRGate>,
975 pub connectivity: Vec<(usize, usize)>,
976 pub error_rates: HashMap<String, f64>,
977}
978
979pub struct CompilationCache {
981 pub cache: HashMap<(String, TargetPlatform), super::types::CrossCompilationResult>,
982}
983
984impl CompilationCache {
985 pub fn new() -> Self {
986 Self {
987 cache: HashMap::new(),
988 }
989 }
990}
991
992impl Default for CompilationCache {
993 fn default() -> Self {
994 Self::new()
995 }
996}
997
998#[cfg(test)]
999mod tests {
1000 use super::*;
1001 use std::collections::HashMap;
1002
1003 fn build_ir(num_qubits: usize, ops: Vec<IROperation>) -> QuantumIR {
1005 QuantumIR {
1006 num_qubits,
1007 num_classical_bits: 0,
1008 operations: ops,
1009 classical_operations: vec![],
1010 metadata: HashMap::new(),
1011 }
1012 }
1013
1014 fn single_gate(gate: IRGate, qubit: usize) -> IROperation {
1016 IROperation {
1017 operation_type: IROperationType::Gate(gate),
1018 qubits: vec![qubit],
1019 controls: vec![],
1020 parameters: vec![],
1021 }
1022 }
1023
1024 fn two_qubit_gate(gate: IRGate, q0: usize, q1: usize) -> IROperation {
1026 IROperation {
1027 operation_type: IROperationType::Gate(gate),
1028 qubits: vec![q0, q1],
1029 controls: vec![],
1030 parameters: vec![],
1031 }
1032 }
1033
1034 fn three_qubit_gate(gate: IRGate, q0: usize, q1: usize, q2: usize) -> IROperation {
1036 IROperation {
1037 operation_type: IROperationType::Gate(gate),
1038 qubits: vec![q0, q1, q2],
1039 controls: vec![],
1040 parameters: vec![],
1041 }
1042 }
1043
1044 #[test]
1049 fn test_rotation_merging_combines_rx_angles() {
1050 let ir = build_ir(
1051 1,
1052 vec![
1053 single_gate(IRGate::RX(0.5), 0),
1054 single_gate(IRGate::RX(0.3), 0),
1055 ],
1056 );
1057 let result = MLCompilationOptimizer::apply_rotation_merging_transform(&ir).unwrap();
1058 assert_eq!(
1059 result.operations.len(),
1060 1,
1061 "two RX gates should merge to one"
1062 );
1063 match &result.operations[0].operation_type {
1064 IROperationType::Gate(IRGate::RX(angle)) => {
1065 let expected = (0.5f64 + 0.3).rem_euclid(2.0 * std::f64::consts::PI);
1066 assert!(
1067 (angle - expected).abs() < 1e-9,
1068 "merged angle should be 0.8, got {angle}"
1069 );
1070 }
1071 other => panic!("expected RX gate, got {other:?}"),
1072 }
1073 }
1074
1075 #[test]
1076 fn test_rotation_merging_removes_cancelling_rx() {
1077 let angle = std::f64::consts::PI;
1078 let ir = build_ir(
1079 1,
1080 vec![
1081 single_gate(IRGate::RX(angle), 0),
1082 single_gate(IRGate::RX(-angle), 0),
1083 ],
1084 );
1085 let result = MLCompilationOptimizer::apply_rotation_merging_transform(&ir).unwrap();
1086 assert_eq!(
1087 result.operations.len(),
1088 0,
1089 "RX(π) + RX(-π) should cancel to zero gates"
1090 );
1091 }
1092
1093 #[test]
1094 fn test_rotation_merging_different_qubits_unchanged() {
1095 let ir = build_ir(
1096 2,
1097 vec![
1098 single_gate(IRGate::RX(0.5), 0),
1099 single_gate(IRGate::RX(0.5), 1), ],
1101 );
1102 let result = MLCompilationOptimizer::apply_rotation_merging_transform(&ir).unwrap();
1103 assert_eq!(
1104 result.operations.len(),
1105 2,
1106 "gates on different qubits must not merge"
1107 );
1108 }
1109
1110 #[test]
1111 fn test_rotation_merging_different_types_unchanged() {
1112 let ir = build_ir(
1113 1,
1114 vec![
1115 single_gate(IRGate::RX(0.5), 0),
1116 single_gate(IRGate::RY(0.5), 0), ],
1118 );
1119 let result = MLCompilationOptimizer::apply_rotation_merging_transform(&ir).unwrap();
1120 assert_eq!(
1121 result.operations.len(),
1122 2,
1123 "RX + RY on same qubit must not merge"
1124 );
1125 }
1126
1127 #[test]
1132 fn test_gate_fusion_reduces_same_type_rotations() {
1133 let ir = build_ir(
1134 1,
1135 vec![
1136 single_gate(IRGate::RZ(1.0), 0),
1137 single_gate(IRGate::RZ(0.5), 0),
1138 ],
1139 );
1140 let result = MLCompilationOptimizer::apply_gate_fusion_transform(&ir).unwrap();
1141 assert_eq!(
1142 result.operations.len(),
1143 1,
1144 "consecutive RZ on same qubit should fuse to 1 gate"
1145 );
1146 }
1147
1148 #[test]
1149 fn test_gate_fusion_cancels_h_h() {
1150 let ir = build_ir(
1152 1,
1153 vec![single_gate(IRGate::H, 0), single_gate(IRGate::H, 0)],
1154 );
1155 let result = MLCompilationOptimizer::apply_gate_fusion_transform(&ir).unwrap();
1156 assert_eq!(
1157 result.operations.len(),
1158 0,
1159 "H followed by H should cancel to zero gates"
1160 );
1161 }
1162
1163 #[test]
1164 fn test_gate_fusion_cancels_x_x() {
1165 let ir = build_ir(
1166 1,
1167 vec![single_gate(IRGate::X, 0), single_gate(IRGate::X, 0)],
1168 );
1169 let result = MLCompilationOptimizer::apply_gate_fusion_transform(&ir).unwrap();
1170 assert_eq!(result.operations.len(), 0, "X ∘ X should cancel");
1171 }
1172
1173 #[test]
1178 fn test_commutation_reorders_disjoint_qubits() {
1179 let ir = build_ir(
1187 2,
1188 vec![
1189 single_gate(IRGate::RX(0.5), 0),
1190 single_gate(IRGate::RX(0.5), 1),
1191 single_gate(IRGate::RX(0.3), 0),
1192 ],
1193 );
1194 let result = MLCompilationOptimizer::apply_commutation_transform(&ir).unwrap();
1195 assert_eq!(
1197 result.operations.len(),
1198 3,
1199 "commutation preserves gate count"
1200 );
1201 }
1202
1203 #[test]
1204 fn test_commutation_enables_downstream_fusion() {
1205 let ir = build_ir(
1209 2,
1210 vec![
1211 single_gate(IRGate::RX(0.5), 0),
1212 single_gate(IRGate::RX(0.5), 1), single_gate(IRGate::RX(0.3), 0),
1214 ],
1215 );
1216 let commuted = MLCompilationOptimizer::apply_commutation_transform(&ir).unwrap();
1217 let fused = MLCompilationOptimizer::apply_rotation_merging_transform(&commuted).unwrap();
1220 assert_eq!(
1221 fused.operations.len(),
1222 2,
1223 "commutation + rotation-merge should collapse two RX(q=0) into one"
1224 );
1225 }
1226
1227 #[test]
1232 fn test_decomposition_toffoli_produces_15_gates() {
1233 let ir = build_ir(3, vec![three_qubit_gate(IRGate::Toffoli, 0, 1, 2)]);
1234 let result = MLCompilationOptimizer::apply_decomposition_transform(&ir).unwrap();
1235 assert_eq!(
1236 result.operations.len(),
1237 15,
1238 "Toffoli should decompose into exactly 15 primitive gates"
1239 );
1240 }
1241
1242 #[test]
1243 fn test_decomposition_swap_produces_3_cnots() {
1244 let ir = build_ir(2, vec![two_qubit_gate(IRGate::SWAP, 0, 1)]);
1245 let result = MLCompilationOptimizer::apply_decomposition_transform(&ir).unwrap();
1246 assert_eq!(
1247 result.operations.len(),
1248 3,
1249 "SWAP should decompose into exactly 3 CNOT gates"
1250 );
1251 for op in &result.operations {
1252 assert!(
1253 matches!(&op.operation_type, IROperationType::Gate(IRGate::CNOT)),
1254 "each SWAP decomposition gate should be a CNOT, got {:?}",
1255 op.operation_type
1256 );
1257 }
1258 }
1259
1260 #[test]
1261 fn test_decomposition_non_compound_passes_through() {
1262 let ir = build_ir(
1263 1,
1264 vec![single_gate(IRGate::H, 0), single_gate(IRGate::RX(1.0), 0)],
1265 );
1266 let result = MLCompilationOptimizer::apply_decomposition_transform(&ir).unwrap();
1267 assert_eq!(
1268 result.operations.len(),
1269 2,
1270 "non-compound gates should pass through unchanged"
1271 );
1272 }
1273
1274 #[test]
1279 fn test_apply_ml_optimizations_fallback_path() {
1280 let strategy = MLOptimizationStrategy {
1282 transformations: vec![],
1283 confidence: 0.9,
1284 };
1285 let ir = build_ir(
1286 1,
1287 vec![
1288 single_gate(IRGate::RX(0.5), 0),
1289 single_gate(IRGate::RX(0.5), 0),
1290 ],
1291 );
1292 let result = MLCompilationOptimizer::apply_ml_optimizations(&ir, &strategy).unwrap();
1293 assert_eq!(
1295 result.operations.len(),
1296 1,
1297 "fallback path should apply rotation merging and fuse the two RX gates"
1298 );
1299 }
1300
1301 fn make_source(code: &str) -> SourceCircuit {
1308 SourceCircuit {
1309 framework: crate::scirs2_cross_compilation_enhanced::QuantumFramework::OpenQASM,
1310 code: code.to_string(),
1311 metadata: HashMap::new(),
1312 }
1313 }
1314
1315 fn make_target(code: &str, platform: TargetPlatform) -> TargetCode {
1316 TargetCode {
1317 platform,
1318 code: code.to_string(),
1319 format: crate::scirs2_cross_compilation_enhanced::CodeFormat::QASM,
1320 metadata: HashMap::new(),
1321 }
1322 }
1323
1324 fn validator() -> CompilationValidator {
1325 CompilationValidator::new(EnhancedCrossCompilationConfig::default())
1326 }
1327
1328 #[test]
1329 fn test_validate_semantics_rejects_dissimilar_gate_content() {
1330 let v = validator();
1331
1332 let source = make_source("h q[0]; cx q[0], q[1]; h q[1];");
1334 let target = make_target("OPENQASM 2.0;\nqreg q[2];\n", TargetPlatform::IBMQuantum);
1335
1336 let valid = v.validate_semantics(&source, &target).unwrap();
1337 assert!(
1338 !valid,
1339 "empty target code must not be judged semantically equivalent to a non-trivial source"
1340 );
1341 }
1342
1343 #[test]
1344 fn test_validate_semantics_accepts_matching_gate_content() {
1345 let v = validator();
1346
1347 let source = make_source("h q[0]; cx q[0], q[1];");
1348 let target = make_target(
1349 "OPENQASM 2.0;\nqreg q[2];\nh q[0];\ncx q[0], q[1];\n",
1350 TargetPlatform::IBMQuantum,
1351 );
1352
1353 let valid = v.validate_semantics(&source, &target).unwrap();
1354 assert!(
1355 valid,
1356 "matching gate histograms between source and target should validate as semantically consistent"
1357 );
1358 }
1359
1360 #[test]
1361 fn test_validate_semantics_both_empty_is_trivially_valid() {
1362 let v = validator();
1363 let source = make_source("// comment only, no gates");
1364 let target = make_target("// no gates emitted", TargetPlatform::Simulator);
1365
1366 let valid = v.validate_semantics(&source, &target).unwrap();
1367 assert!(valid, "two gateless programs are trivially consistent");
1368 }
1369
1370 #[test]
1371 fn test_validate_resources_rejects_oversized_circuit_for_platform() {
1372 let v = validator();
1373 let target = make_target("qreg q[100];\nh q[99];\n", TargetPlatform::IonQ);
1375
1376 let valid = v.validate_resources(&target, TargetPlatform::IonQ).unwrap();
1377 assert!(
1378 !valid,
1379 "a circuit using 100 qubits must be rejected for a 32-qubit platform"
1380 );
1381 }
1382
1383 #[test]
1384 fn test_validate_resources_accepts_small_circuit() {
1385 let v = validator();
1386 let target = make_target(
1387 "qreg q[2];\nh q[0];\ncx q[0], q[1];\n",
1388 TargetPlatform::IonQ,
1389 );
1390
1391 let valid = v.validate_resources(&target, TargetPlatform::IonQ).unwrap();
1392 assert!(
1393 valid,
1394 "a 2-qubit circuit must fit within any supported platform"
1395 );
1396 }
1397
1398 #[test]
1399 fn test_estimate_fidelity_decreases_with_more_two_qubit_gates() {
1400 let v = validator();
1401 let source = make_source("h q[0]; cx q[0], q[1];");
1402
1403 let few_two_qubit_gates =
1404 make_target("h q[0];\ncx q[0], q[1];\n", TargetPlatform::IBMQuantum);
1405 let many_two_qubit_gates = make_target(
1406 "h q[0];\ncx q[0], q[1];\ncx q[1], q[0];\ncx q[0], q[1];\ncx q[1], q[0];\ncx q[0], q[1];\n",
1407 TargetPlatform::IBMQuantum,
1408 );
1409
1410 let fidelity_few = v.estimate_fidelity(&source, &few_two_qubit_gates).unwrap();
1411 let fidelity_many = v.estimate_fidelity(&source, &many_two_qubit_gates).unwrap();
1412
1413 assert!((0.0..=1.0).contains(&fidelity_few));
1414 assert!((0.0..=1.0).contains(&fidelity_many));
1415 assert!(
1416 fidelity_many < fidelity_few,
1417 "more two-qubit gates must yield a lower fidelity estimate: few={fidelity_few}, many={fidelity_many}"
1418 );
1419 }
1420
1421 #[test]
1422 fn test_estimate_fidelity_not_hardcoded_constant() {
1423 let v = validator();
1424 let source = make_source("h q[0];");
1425 let target = make_target("h q[0];\ncx q[0], q[1];\n", TargetPlatform::IonQ);
1426
1427 let fidelity = v.estimate_fidelity(&source, &target).unwrap();
1428 assert!(
1429 (fidelity - 0.99).abs() > 1e-9,
1430 "fidelity must be computed from the actual gate composition, not the old hardcoded 0.99"
1431 );
1432 }
1433
1434 #[test]
1435 fn test_validate_compilation_can_actually_fail() {
1436 let v = validator();
1437 let source = make_source("h q[0]; cx q[0], q[1]; h q[1]; cx q[1], q[0];");
1438 let target = make_target("qreg q[64];\n", TargetPlatform::IonQ);
1440
1441 let result = v
1442 .validate_compilation(&source, &target, TargetPlatform::IonQ)
1443 .unwrap();
1444 assert!(
1445 !result.is_valid,
1446 "comprehensive validation must be able to reject a broken/mismatched compilation"
1447 );
1448 }
1449
1450 #[test]
1455 fn test_extract_features_reflects_actual_circuit_content() {
1456 let extractor = CompilationFeatureExtractor::new();
1457 let ir = build_ir(
1458 2,
1459 vec![
1460 single_gate(IRGate::H, 0),
1461 single_gate(IRGate::RX(0.5), 0),
1462 two_qubit_gate(IRGate::CNOT, 0, 1),
1463 ],
1464 );
1465
1466 let features = extractor
1467 .extract_features(&ir, TargetPlatform::IBMQuantum)
1468 .unwrap();
1469
1470 assert_eq!(features.circuit_features.len(), 7);
1471 assert_eq!(features.circuit_features[0], 2.0, "num_qubits");
1472 assert_eq!(features.circuit_features[1], 3.0, "total_gates");
1473 assert_eq!(features.circuit_features[3], 1.0, "two_qubit_gates");
1474 assert_eq!(features.circuit_features[5], 1.0, "rotation_gates");
1475 assert!(!features.target_features.is_empty());
1476 assert!(!features.complexity_features.is_empty());
1477
1478 let empty_ir = build_ir(1, vec![]);
1479 let empty_features = extractor
1480 .extract_features(&empty_ir, TargetPlatform::IBMQuantum)
1481 .unwrap();
1482 assert_ne!(
1483 features.circuit_features, empty_features.circuit_features,
1484 "feature vectors must depend on the actual circuit content"
1485 );
1486 }
1487
1488 #[test]
1489 fn test_predict_strategy_selects_real_transformations() {
1490 let model = CompilationModel::new();
1491 let extractor = CompilationFeatureExtractor::new();
1492
1493 let ir_with_rotations = build_ir(
1494 1,
1495 vec![
1496 single_gate(IRGate::RX(0.5), 0),
1497 single_gate(IRGate::RX(0.5), 0),
1498 ],
1499 );
1500 let features = extractor
1501 .extract_features(&ir_with_rotations, TargetPlatform::IBMQuantum)
1502 .unwrap();
1503 let strategy = model.predict_strategy(&features).unwrap();
1504 assert!(
1505 !strategy.transformations.is_empty(),
1506 "a circuit with rotation gates must yield a non-empty strategy"
1507 );
1508 assert!(
1509 strategy
1510 .transformations
1511 .iter()
1512 .any(|t| matches!(t.transform_type, TransformationType::RotationMerging)),
1513 "rotation-heavy circuits should select RotationMerging"
1514 );
1515
1516 let empty_ir = build_ir(1, vec![]);
1517 let empty_features = extractor
1518 .extract_features(&empty_ir, TargetPlatform::IBMQuantum)
1519 .unwrap();
1520 let empty_strategy = model.predict_strategy(&empty_features).unwrap();
1521 assert!(
1522 empty_strategy.transformations.is_empty(),
1523 "an empty circuit should not select any transformations"
1524 );
1525 assert!((empty_strategy.confidence - 0.5).abs() < 1e-9);
1526 }
1527}