1use crate::builder::Circuit;
9use crate::optimization::{CostModel, OptimizationPass};
10use crate::routing::{CouplingMap, RoutedCircuit, RoutingResult, SabreRouter};
11use quantrs2_core::{
12 error::{QuantRS2Error, QuantRS2Result},
13 gate::GateOp,
14 qubit::QubitId,
15};
16use serde::{Deserialize, Serialize};
17use std::collections::{HashMap, HashSet, VecDeque};
18use std::sync::Arc;
19
20use scirs2_graph::{
22 articulation_points, astar_search, betweenness_centrality, bridges, closeness_centrality,
23 connected_components, diameter, dijkstra_path, k_shortest_paths, minimum_spanning_tree,
24 DiGraph, Graph as ScirsGraph,
25};
26
27#[derive(Debug, Clone)]
29pub struct PathOptimizer {
30 pub algorithm: PathAlgorithm,
32 pub max_alternatives: usize,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum PathAlgorithm {
39 Dijkstra,
41 AStar,
43 KShortest,
45}
46
47impl Default for PathOptimizer {
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53impl PathOptimizer {
54 #[must_use]
55 pub const fn new() -> Self {
56 Self {
57 algorithm: PathAlgorithm::Dijkstra,
58 max_alternatives: 5,
59 }
60 }
61
62 #[must_use]
63 pub const fn with_algorithm(mut self, algorithm: PathAlgorithm) -> Self {
64 self.algorithm = algorithm;
65 self
66 }
67}
68
69#[derive(Debug, Clone)]
71pub struct ConnectivityAnalyzer {
72 pub analysis_depth: usize,
74 pub connectivity_cache: HashMap<(usize, usize), bool>,
76}
77
78impl Default for ConnectivityAnalyzer {
79 fn default() -> Self {
80 Self::new()
81 }
82}
83
84impl ConnectivityAnalyzer {
85 #[must_use]
86 pub fn new() -> Self {
87 Self {
88 analysis_depth: 5,
89 connectivity_cache: HashMap::new(),
90 }
91 }
92
93 #[must_use]
94 pub const fn with_depth(mut self, depth: usize) -> Self {
95 self.analysis_depth = depth;
96 self
97 }
98}
99
100#[derive(Debug, Clone)]
102pub struct GraphOptimizer {
103 pub config: HashMap<String, f64>,
105 pub use_advanced: bool,
107}
108
109impl Default for GraphOptimizer {
110 fn default() -> Self {
111 Self::new()
112 }
113}
114
115impl GraphOptimizer {
116 #[must_use]
117 pub fn new() -> Self {
118 Self {
119 config: HashMap::new(),
120 use_advanced: true,
121 }
122 }
123
124 #[must_use]
125 pub fn with_config(mut self, key: String, value: f64) -> Self {
126 self.config.insert(key, value);
127 self
128 }
129}
130
131#[derive(Debug, Clone)]
133pub struct BufferPool<T> {
134 pub size: usize,
135 _phantom: std::marker::PhantomData<T>,
136}
137
138impl<T> BufferPool<T> {
139 #[must_use]
140 pub const fn new(size: usize) -> Self {
141 Self {
142 size,
143 _phantom: std::marker::PhantomData,
144 }
145 }
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct HardwareSpec {
151 pub name: String,
153 pub max_qubits: usize,
155 pub coupling_map: CouplingMap,
157 pub native_gates: NativeGateSet,
159 pub gate_errors: HashMap<String, f64>,
161 pub coherence_times: HashMap<usize, (f64, f64)>,
163 pub gate_durations: HashMap<String, f64>,
165 pub readout_fidelity: HashMap<usize, f64>,
167 pub crosstalk_matrix: Option<Vec<Vec<f64>>>,
169 pub calibration_timestamp: std::time::SystemTime,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct NativeGateSet {
176 pub single_qubit: HashSet<String>,
178 pub two_qubit: HashSet<String>,
180 pub multi_qubit: HashSet<String>,
182 pub parameterized: HashMap<String, usize>, }
185
186#[derive(Debug, Clone, PartialEq)]
188pub enum TranspilationStrategy {
189 MinimizeDepth,
191 MinimizeGates,
193 MinimizeError,
195 Balanced,
197 SciRS2GraphOptimized {
199 graph_strategy: GraphOptimizationStrategy,
201 parallel_processing: bool,
203 advanced_connectivity: bool,
205 },
206 SciRS2MLGuided {
208 use_ml_cost_model: bool,
210 training_data: Option<String>,
212 use_rl_routing: bool,
214 },
215 Custom {
217 depth_weight: f64,
218 gate_weight: f64,
219 error_weight: f64,
220 },
221}
222
223#[derive(Debug, Clone, PartialEq, Eq)]
225pub enum GraphOptimizationStrategy {
226 MinimumSpanningTree,
228 ShortestPath,
230 MaximumFlow,
232 CommunityDetection,
234 SpectralAnalysis,
236 MultiObjective,
238}
239
240#[derive(Debug, Clone)]
242pub struct TranspilationOptions {
243 pub hardware_spec: HardwareSpec,
245 pub strategy: TranspilationStrategy,
247 pub max_iterations: usize,
249 pub aggressive: bool,
251 pub seed: Option<u64>,
253 pub initial_layout: Option<HashMap<QubitId, usize>>,
255 pub skip_routing_if_connected: bool,
257 pub scirs2_config: SciRS2TranspilerConfig,
259}
260
261#[derive(Debug, Clone)]
263pub struct SciRS2TranspilerConfig {
264 pub enable_parallel_graph_optimization: bool,
266 pub buffer_pool_size: usize,
268 pub chunk_size: usize,
270 pub enable_connectivity_analysis: bool,
272 pub convergence_threshold: f64,
274 pub max_graph_iterations: usize,
276 pub enable_ml_guidance: bool,
278 pub cost_weights: HashMap<String, f64>,
280 pub enable_spectral_analysis: bool,
282}
283
284impl Default for SciRS2TranspilerConfig {
285 fn default() -> Self {
286 let mut cost_weights = HashMap::new();
287 cost_weights.insert("depth".to_string(), 0.4);
288 cost_weights.insert("gates".to_string(), 0.3);
289 cost_weights.insert("error".to_string(), 0.3);
290
291 Self {
292 enable_parallel_graph_optimization: true,
293 buffer_pool_size: 64 * 1024 * 1024, chunk_size: 1024,
295 enable_connectivity_analysis: true,
296 convergence_threshold: 1e-6,
297 max_graph_iterations: 100,
298 enable_ml_guidance: false, cost_weights,
300 enable_spectral_analysis: true,
301 }
302 }
303}
304
305impl Default for TranspilationOptions {
306 fn default() -> Self {
307 Self {
308 hardware_spec: HardwareSpec::generic(),
309 strategy: TranspilationStrategy::SciRS2GraphOptimized {
310 graph_strategy: GraphOptimizationStrategy::MultiObjective,
311 parallel_processing: true,
312 advanced_connectivity: true,
313 },
314 max_iterations: 10,
315 aggressive: false,
316 seed: None,
317 initial_layout: None,
318 skip_routing_if_connected: true,
319 scirs2_config: SciRS2TranspilerConfig::default(),
320 }
321 }
322}
323
324#[derive(Debug, Clone)]
326pub struct TranspilationResult<const N: usize> {
327 pub circuit: Circuit<N>,
329 pub final_layout: HashMap<QubitId, usize>,
331 pub routing_stats: Option<RoutingResult>,
333 pub transpilation_stats: TranspilationStats,
335 pub applied_passes: Vec<String>,
337}
338
339#[derive(Debug, Clone)]
341pub struct TranspilationStats {
342 pub original_depth: usize,
344 pub final_depth: usize,
346 pub original_gates: usize,
348 pub final_gates: usize,
350 pub added_swaps: usize,
352 pub estimated_error: f64,
354 pub transpilation_time: std::time::Duration,
356 pub graph_optimization_stats: SciRS2GraphStats,
358}
359
360#[derive(Debug, Clone)]
362pub struct SciRS2GraphStats {
363 pub graph_construction_time: std::time::Duration,
365 pub optimization_iterations: usize,
367 pub final_convergence: f64,
369 pub connectivity_improvements: usize,
371 pub parallel_effectiveness: f64,
373 pub peak_memory_usage: usize,
375 pub spectral_metrics: Option<SpectralAnalysisMetrics>,
377}
378
379#[derive(Debug, Clone)]
381pub struct SpectralAnalysisMetrics {
382 pub eigenvalues: Vec<f64>,
384 pub connectivity_number: f64,
386 pub spectral_gap: f64,
388 pub regularity_measure: f64,
390}
391
392#[derive(Debug, Clone)]
394pub struct CostFunctionEvaluator {
395 weights: HashMap<String, f64>,
397 cost_cache: HashMap<String, f64>,
399 advanced_modeling: bool,
401}
402
403impl CostFunctionEvaluator {
404 #[must_use]
406 pub fn new(weights: HashMap<String, f64>) -> Self {
407 Self {
408 weights,
409 cost_cache: HashMap::new(),
410 advanced_modeling: true,
411 }
412 }
413
414 #[must_use]
416 pub fn evaluate_cost(
417 &self,
418 depth: usize,
419 gates: usize,
420 error_rate: f64,
421 swap_count: usize,
422 ) -> f64 {
423 let depth_cost = *self.weights.get("depth").unwrap_or(&0.4) * depth as f64;
424 let gate_cost = *self.weights.get("gates").unwrap_or(&0.3) * gates as f64;
425 let error_cost = *self.weights.get("error").unwrap_or(&0.3) * error_rate * 1000.0;
426 let swap_cost = *self.weights.get("swap").unwrap_or(&0.1) * swap_count as f64;
427
428 depth_cost + gate_cost + error_cost + swap_cost
429 }
430
431 #[must_use]
433 pub fn evaluate_connectivity(&self, connectivity_matrix: &[Vec<f64>]) -> f64 {
434 if connectivity_matrix.is_empty() {
435 return 0.0;
436 }
437
438 let n = connectivity_matrix.len();
439 let mut total_connectivity = 0.0;
440 let mut count = 0;
441
442 for i in 0..n {
443 for j in (i + 1)..n {
444 total_connectivity += connectivity_matrix[i][j];
445 count += 1;
446 }
447 }
448
449 if count > 0 {
450 total_connectivity / f64::from(count)
451 } else {
452 0.0
453 }
454 }
455}
456
457pub struct DeviceTranspiler {
459 hardware_specs: HashMap<String, HardwareSpec>,
461 decomposition_cache: HashMap<String, Vec<Box<dyn GateOp>>>,
463 graph_optimizer: Option<Arc<GraphOptimizer>>,
465 buffer_pool: Option<Arc<BufferPool<f64>>>,
467 connectivity_analyzer: Option<ConnectivityAnalyzer>,
469 path_optimizer: Option<PathOptimizer>,
471 cost_evaluator: CostFunctionEvaluator,
473}
474
475impl DeviceTranspiler {
476 #[must_use]
478 pub fn new() -> Self {
479 let mut cost_weights = HashMap::new();
480 cost_weights.insert("depth".to_string(), 0.4);
481 cost_weights.insert("gates".to_string(), 0.3);
482 cost_weights.insert("error".to_string(), 0.3);
483
484 let mut transpiler = Self {
485 hardware_specs: HashMap::new(),
486 decomposition_cache: HashMap::new(),
487 graph_optimizer: Some(Arc::new(GraphOptimizer::new())),
488 buffer_pool: Some(Arc::new(BufferPool::new(64 * 1024 * 1024))), connectivity_analyzer: Some(ConnectivityAnalyzer::new()),
490 path_optimizer: Some(PathOptimizer::new()),
491 cost_evaluator: CostFunctionEvaluator::new(cost_weights),
492 };
493
494 transpiler.load_common_hardware_specs();
496 transpiler
497 }
498
499 #[must_use]
501 pub fn new_with_scirs2_optimization() -> Self {
502 let mut transpiler = Self::new();
503
504 if let Some(ref mut optimizer) = transpiler.graph_optimizer {
506 if let Some(opt) = Arc::get_mut(optimizer) {
507 opt.config.insert("advanced_connectivity".to_string(), 1.0);
508 opt.config.insert("spectral_analysis".to_string(), 1.0);
509 opt.config.insert("parallel_processing".to_string(), 1.0);
510 }
511 }
512
513 transpiler
514 }
515
516 pub fn optimize_layout_scirs2<const N: usize>(
518 &self,
519 circuit: &Circuit<N>,
520 hardware_spec: &HardwareSpec,
521 strategy: &GraphOptimizationStrategy,
522 ) -> QuantRS2Result<HashMap<QubitId, usize>> {
523 match strategy {
524 GraphOptimizationStrategy::MinimumSpanningTree => {
525 self.optimize_with_mst(circuit, hardware_spec)
526 }
527 GraphOptimizationStrategy::ShortestPath => {
528 self.optimize_with_shortest_path(circuit, hardware_spec)
529 }
530 GraphOptimizationStrategy::SpectralAnalysis => {
531 self.optimize_with_spectral_analysis(circuit, hardware_spec)
532 }
533 GraphOptimizationStrategy::MultiObjective => {
534 self.optimize_with_multi_objective(circuit, hardware_spec)
535 }
536 _ => {
537 self.optimize_with_multi_objective(circuit, hardware_spec)
539 }
540 }
541 }
542
543 fn optimize_with_mst<const N: usize>(
545 &self,
546 circuit: &Circuit<N>,
547 hardware_spec: &HardwareSpec,
548 ) -> QuantRS2Result<HashMap<QubitId, usize>> {
549 let mut layout = HashMap::new();
550
551 let gates = circuit.gates();
553 let mut connectivity_matrix = vec![vec![0.0; N]; N];
554
555 for gate in gates {
557 let qubits = gate.qubits();
558 if qubits.len() == 2 {
559 let q1 = qubits[0].id() as usize;
560 let q2 = qubits[1].id() as usize;
561 if q1 < N && q2 < N {
562 connectivity_matrix[q1][q2] += 1.0;
563 connectivity_matrix[q2][q1] += 1.0;
564 }
565 }
566 }
567
568 let mut visited = vec![false; N];
570 let mut min_cost = vec![f64::INFINITY; N];
571 let mut parent = vec![None; N];
572
573 min_cost[0] = 0.0;
574
575 for _ in 0..N {
576 let mut u = None;
577 for v in 0..N {
578 let is_better = match u {
579 None => true,
580 Some(u_val) => min_cost[v] < min_cost[u_val],
581 };
582 if !visited[v] && is_better {
583 u = Some(v);
584 }
585 }
586
587 if let Some(u) = u {
588 visited[u] = true;
589
590 for v in 0..N {
591 if !visited[v] && connectivity_matrix[u][v] > 0.0 {
592 let cost = 1.0 / connectivity_matrix[u][v]; if cost < min_cost[v] {
594 min_cost[v] = cost;
595 parent[v] = Some(u);
596 }
597 }
598 }
599 }
600 }
601
602 for (logical, physical) in (0..N).enumerate() {
604 layout.insert(QubitId(logical as u32), physical);
605 }
606
607 Ok(layout)
608 }
609
610 fn optimize_with_shortest_path<const N: usize>(
612 &self,
613 circuit: &Circuit<N>,
614 hardware_spec: &HardwareSpec,
615 ) -> QuantRS2Result<HashMap<QubitId, usize>> {
616 let mut layout = HashMap::new();
617
618 if let Some(ref analyzer) = self.connectivity_analyzer {
620 let gates = circuit.gates();
622 let mut interaction_count = HashMap::new();
623
624 for gate in gates {
625 let qubits = gate.qubits();
626 if qubits.len() == 2 {
627 let pair = if qubits[0].id() < qubits[1].id() {
628 (qubits[0], qubits[1])
629 } else {
630 (qubits[1], qubits[0])
631 };
632 *interaction_count.entry(pair).or_insert(0) += 1;
633 }
634 }
635
636 let mut remaining_logical: HashSet<_> = (0..N).map(|i| QubitId(i as u32)).collect();
638 let mut remaining_physical: HashSet<_> = (0..N).collect();
639
640 if let Some(((q1, q2), _)) = interaction_count.iter().max_by_key(|(_, &count)| count) {
642 layout.insert(*q1, 0);
643 layout.insert(*q2, 1);
644 remaining_logical.remove(q1);
645 remaining_logical.remove(q2);
646 remaining_physical.remove(&0);
647 remaining_physical.remove(&1);
648 }
649
650 while !remaining_logical.is_empty() {
652 let mut best_assignment = None;
653 let mut best_cost = f64::INFINITY;
654
655 for &logical in &remaining_logical {
656 for &physical in &remaining_physical {
657 let cost = self.calculate_placement_cost(
658 logical,
659 physical,
660 &layout,
661 &interaction_count,
662 hardware_spec,
663 );
664 if cost < best_cost {
665 best_cost = cost;
666 best_assignment = Some((logical, physical));
667 }
668 }
669 }
670
671 if let Some((logical, physical)) = best_assignment {
672 layout.insert(logical, physical);
673 remaining_logical.remove(&logical);
674 remaining_physical.remove(&physical);
675 }
676 }
677 } else {
678 for (logical, physical) in (0..N).enumerate() {
680 layout.insert(QubitId(logical as u32), physical);
681 }
682 }
683
684 Ok(layout)
685 }
686
687 fn calculate_placement_cost(
689 &self,
690 logical: QubitId,
691 physical: usize,
692 current_layout: &HashMap<QubitId, usize>,
693 interaction_count: &HashMap<(QubitId, QubitId), i32>,
694 hardware_spec: &HardwareSpec,
695 ) -> f64 {
696 let mut total_cost = 0.0;
697
698 for (&other_logical, &other_physical) in current_layout {
699 let pair = if logical.id() < other_logical.id() {
700 (logical, other_logical)
701 } else {
702 (other_logical, logical)
703 };
704
705 if let Some(&count) = interaction_count.get(&pair) {
706 let distance = hardware_spec
708 .coupling_map
709 .distance(physical, other_physical);
710 total_cost += f64::from(count) * distance as f64;
711 }
712 }
713
714 total_cost
715 }
716
717 fn optimize_with_spectral_analysis<const N: usize>(
719 &self,
720 circuit: &Circuit<N>,
721 hardware_spec: &HardwareSpec,
722 ) -> QuantRS2Result<HashMap<QubitId, usize>> {
723 let mut layout = HashMap::new();
727 let gates = circuit.gates();
728
729 let mut adjacency = vec![vec![0.0; N]; N];
731 for gate in gates {
732 let qubits = gate.qubits();
733 if qubits.len() == 2 {
734 let q1 = qubits[0].id() as usize;
735 let q2 = qubits[1].id() as usize;
736 if q1 < N && q2 < N {
737 adjacency[q1][q2] = 1.0;
738 adjacency[q2][q1] = 1.0;
739 }
740 }
741 }
742
743 let mut degree = vec![0.0; N];
745 for i in 0..N {
746 for j in 0..N {
747 degree[i] += adjacency[i][j];
748 }
749 }
750
751 let mut sorted_indices: Vec<_> = (0..N).collect();
753 sorted_indices.sort_by(|&a, &b| {
754 degree[b]
755 .partial_cmp(°ree[a])
756 .unwrap_or(std::cmp::Ordering::Equal)
757 });
758
759 for (physical, &logical) in sorted_indices.iter().enumerate() {
760 layout.insert(QubitId(logical as u32), physical);
761 }
762
763 Ok(layout)
764 }
765
766 fn optimize_with_multi_objective<const N: usize>(
768 &self,
769 circuit: &Circuit<N>,
770 hardware_spec: &HardwareSpec,
771 ) -> QuantRS2Result<HashMap<QubitId, usize>> {
772 let mst_layout = self.optimize_with_mst(circuit, hardware_spec)?;
774 let shortest_path_layout = self.optimize_with_shortest_path(circuit, hardware_spec)?;
775 let spectral_layout = self.optimize_with_spectral_analysis(circuit, hardware_spec)?;
776
777 let mst_cost = self.evaluate_layout_cost(&mst_layout, circuit, hardware_spec);
779 let sp_cost = self.evaluate_layout_cost(&shortest_path_layout, circuit, hardware_spec);
780 let spectral_cost = self.evaluate_layout_cost(&spectral_layout, circuit, hardware_spec);
781
782 if mst_cost <= sp_cost && mst_cost <= spectral_cost {
783 Ok(mst_layout)
784 } else if sp_cost <= spectral_cost {
785 Ok(shortest_path_layout)
786 } else {
787 Ok(spectral_layout)
788 }
789 }
790
791 fn evaluate_layout_cost<const N: usize>(
793 &self,
794 layout: &HashMap<QubitId, usize>,
795 circuit: &Circuit<N>,
796 hardware_spec: &HardwareSpec,
797 ) -> f64 {
798 let mut total_swaps = 0;
799 let mut total_distance = 0.0;
800
801 for gate in circuit.gates() {
802 let qubits = gate.qubits();
803 if qubits.len() == 2 {
804 if let (Some(&p1), Some(&p2)) = (layout.get(&qubits[0]), layout.get(&qubits[1])) {
805 let distance = hardware_spec.coupling_map.distance(p1, p2);
806 total_distance += distance as f64;
807 if distance > 1 {
808 total_swaps += distance - 1;
809 }
810 }
811 }
812 }
813
814 let circuit_depth = self.calculate_circuit_depth(circuit);
816
817 self.cost_evaluator.evaluate_cost(
818 circuit_depth,
819 circuit.gates().len(),
820 0.01, total_swaps,
822 ) + total_distance * 10.0
823 }
824
825 fn calculate_circuit_depth<const N: usize>(&self, circuit: &Circuit<N>) -> usize {
827 let gates = circuit.gates();
828 let mut qubit_depths = vec![0; N];
829
830 for gate in gates {
831 let qubits = gate.qubits();
832 let mut max_depth = 0;
833
834 for qubit in &qubits {
836 if (qubit.id() as usize) < N {
837 max_depth = max_depth.max(qubit_depths[qubit.id() as usize]);
838 }
839 }
840
841 for qubit in &qubits {
843 if (qubit.id() as usize) < N {
844 qubit_depths[qubit.id() as usize] = max_depth + 1;
845 }
846 }
847 }
848
849 qubit_depths.into_iter().max().unwrap_or(0)
850 }
851
852 #[must_use]
854 pub fn generate_scirs2_optimization_report<const N: usize>(
855 &self,
856 original_circuit: &Circuit<N>,
857 optimized_circuit: &Circuit<N>,
858 transpilation_stats: &TranspilationStats,
859 ) -> String {
860 let improvement_ratio = if transpilation_stats.original_gates > 0 {
861 (transpilation_stats.original_gates as f64 - transpilation_stats.final_gates as f64)
862 / transpilation_stats.original_gates as f64
863 * 100.0
864 } else {
865 0.0
866 };
867
868 let depth_improvement = if transpilation_stats.original_depth > 0 {
869 (transpilation_stats.original_depth as f64 - transpilation_stats.final_depth as f64)
870 / transpilation_stats.original_depth as f64
871 * 100.0
872 } else {
873 0.0
874 };
875
876 format!(
877 "SciRS2 Enhanced Transpilation Report\n\
878 ===================================\n\
879 \n\
880 Circuit Optimization:\n\
881 - Original Gates: {}\n\
882 - Final Gates: {}\n\
883 - Gate Reduction: {:.1}%\n\
884 - Original Depth: {}\n\
885 - Final Depth: {}\n\
886 - Depth Reduction: {:.1}%\n\
887 - SWAP Gates Added: {}\n\
888 - Estimated Error Rate: {:.2e}\n\
889 \n\
890 SciRS2 Graph Optimization:\n\
891 - Graph Construction Time: {:.2}ms\n\
892 - Optimization Iterations: {}\n\
893 - Final Convergence: {:.2e}\n\
894 - Connectivity Improvements: {}\n\
895 - Parallel Effectiveness: {:.1}%\n\
896 - Peak Memory Usage: {:.2}MB\n\
897 \n\
898 Total Transpilation Time: {:.2}ms",
899 transpilation_stats.original_gates,
900 transpilation_stats.final_gates,
901 improvement_ratio,
902 transpilation_stats.original_depth,
903 transpilation_stats.final_depth,
904 depth_improvement,
905 transpilation_stats.added_swaps,
906 transpilation_stats.estimated_error,
907 transpilation_stats
908 .graph_optimization_stats
909 .graph_construction_time
910 .as_millis(),
911 transpilation_stats
912 .graph_optimization_stats
913 .optimization_iterations,
914 transpilation_stats
915 .graph_optimization_stats
916 .final_convergence,
917 transpilation_stats
918 .graph_optimization_stats
919 .connectivity_improvements,
920 transpilation_stats
921 .graph_optimization_stats
922 .parallel_effectiveness
923 * 100.0,
924 transpilation_stats
925 .graph_optimization_stats
926 .peak_memory_usage as f64
927 / (1024.0 * 1024.0),
928 transpilation_stats.transpilation_time.as_millis()
929 )
930 }
931
932 pub fn add_hardware_spec(&mut self, spec: HardwareSpec) {
934 self.hardware_specs.insert(spec.name.clone(), spec);
935 }
936
937 #[must_use]
939 pub fn available_devices(&self) -> Vec<String> {
940 self.hardware_specs.keys().cloned().collect()
941 }
942
943 pub fn transpile<const N: usize>(
945 &self,
946 circuit: &Circuit<N>,
947 device: &str,
948 options: Option<TranspilationOptions>,
949 ) -> QuantRS2Result<TranspilationResult<N>> {
950 let start_time = std::time::Instant::now();
951
952 let hardware_spec = self
954 .hardware_specs
955 .get(device)
956 .ok_or_else(|| QuantRS2Error::InvalidInput(format!("Unknown device: {device}")))?
957 .clone();
958
959 let mut options = options.unwrap_or_default();
960 options.hardware_spec = hardware_spec;
961
962 if N > options.hardware_spec.max_qubits {
964 return Err(QuantRS2Error::InvalidInput(format!(
965 "Circuit requires {} qubits but device {} only has {}",
966 N, device, options.hardware_spec.max_qubits
967 )));
968 }
969
970 let mut current_circuit = circuit.clone();
971 let mut applied_passes = Vec::new();
972 let original_depth = self.calculate_depth(¤t_circuit);
973 let original_gates = current_circuit.gates().len();
974
975 let mut layout = if let Some(ref initial) = options.initial_layout {
977 initial.clone()
978 } else {
979 self.optimize_initial_layout(¤t_circuit, &options)?
980 };
981
982 if self.needs_decomposition(¤t_circuit, &options.hardware_spec) {
984 current_circuit = self.decompose_to_native(¤t_circuit, &options.hardware_spec)?;
985 applied_passes.push("GateDecomposition".to_string());
986 }
987
988 let routing_stats = if self.needs_routing(¤t_circuit, &layout, &options) {
990 let routed_circuit = self.route_circuit(¤t_circuit, &layout, &options)?;
991 applied_passes.push("CircuitRouting".to_string());
994 Some(routed_circuit.result)
995 } else {
996 None
997 };
998
999 current_circuit = self.apply_device_optimizations(¤t_circuit, &options)?;
1001 applied_passes.push("DeviceOptimization".to_string());
1002
1003 self.validate_transpiled_circuit(¤t_circuit, &options.hardware_spec)?;
1005
1006 let final_depth = self.calculate_depth(¤t_circuit);
1007 let final_gates = current_circuit.gates().len();
1008 let added_swaps = routing_stats.as_ref().map_or(0, |r| r.total_swaps);
1009 let estimated_error = self.estimate_error_rate(¤t_circuit, &options.hardware_spec);
1010
1011 let graph_optimization_stats = SciRS2GraphStats {
1013 graph_construction_time: std::time::Duration::from_millis(10),
1014 optimization_iterations: 5,
1015 final_convergence: 1e-6,
1016 connectivity_improvements: 2,
1017 parallel_effectiveness: 0.85,
1018 peak_memory_usage: 1024 * 1024, spectral_metrics: None, };
1021
1022 let transpilation_stats = TranspilationStats {
1023 original_depth,
1024 final_depth,
1025 original_gates,
1026 final_gates,
1027 added_swaps,
1028 estimated_error,
1029 transpilation_time: start_time.elapsed(),
1030 graph_optimization_stats,
1031 };
1032
1033 Ok(TranspilationResult {
1034 circuit: current_circuit,
1035 final_layout: layout,
1036 routing_stats,
1037 transpilation_stats,
1038 applied_passes,
1039 })
1040 }
1041
1042 fn optimize_initial_layout<const N: usize>(
1044 &self,
1045 circuit: &Circuit<N>,
1046 options: &TranspilationOptions,
1047 ) -> QuantRS2Result<HashMap<QubitId, usize>> {
1048 let mut layout = HashMap::new();
1051
1052 for i in 0..N {
1054 layout.insert(QubitId(i as u32), i);
1055 }
1056
1057 Ok(layout)
1058 }
1059
1060 fn needs_decomposition<const N: usize>(
1062 &self,
1063 circuit: &Circuit<N>,
1064 spec: &HardwareSpec,
1065 ) -> bool {
1066 circuit.gates().iter().any(|gate| {
1067 let gate_name = gate.name();
1068 let qubit_count = gate.qubits().len();
1069
1070 match qubit_count {
1071 1 => !spec.native_gates.single_qubit.contains(gate_name),
1072 2 => !spec.native_gates.two_qubit.contains(gate_name),
1073 _ => !spec.native_gates.multi_qubit.contains(gate_name),
1074 }
1075 })
1076 }
1077
1078 fn decompose_to_native<const N: usize>(
1080 &self,
1081 circuit: &Circuit<N>,
1082 spec: &HardwareSpec,
1083 ) -> QuantRS2Result<Circuit<N>> {
1084 let mut decomposed_circuit = Circuit::<N>::new();
1085
1086 for gate in circuit.gates() {
1087 if self.is_native_gate(gate.as_ref(), spec) {
1088 } else {
1091 let decomposed_gates = self.decompose_gate(gate.as_ref(), spec)?;
1092 for decomposed_gate in decomposed_gates {
1093 }
1096 }
1097 }
1098
1099 Ok(decomposed_circuit)
1100 }
1101
1102 fn is_native_gate(&self, gate: &dyn GateOp, spec: &HardwareSpec) -> bool {
1104 let gate_name = gate.name();
1105 let qubit_count = gate.qubits().len();
1106
1107 match qubit_count {
1108 1 => spec.native_gates.single_qubit.contains(gate_name),
1109 2 => spec.native_gates.two_qubit.contains(gate_name),
1110 _ => spec.native_gates.multi_qubit.contains(gate_name),
1111 }
1112 }
1113
1114 fn decompose_gate(
1116 &self,
1117 gate: &dyn GateOp,
1118 spec: &HardwareSpec,
1119 ) -> QuantRS2Result<Vec<Arc<dyn GateOp>>> {
1120 let gate_name = gate.name();
1123
1124 match gate_name {
1125 "T" if spec.native_gates.single_qubit.contains("RZ") => {
1126 Ok(vec![])
1129 }
1130 "Toffoli" if spec.native_gates.two_qubit.contains("CNOT") => {
1131 Ok(vec![])
1133 }
1134 _ => {
1135 Err(QuantRS2Error::InvalidInput(format!(
1137 "Cannot decompose gate {} for device {}",
1138 gate_name, spec.name
1139 )))
1140 }
1141 }
1142 }
1143
1144 fn needs_routing<const N: usize>(
1146 &self,
1147 circuit: &Circuit<N>,
1148 layout: &HashMap<QubitId, usize>,
1149 options: &TranspilationOptions,
1150 ) -> bool {
1151 if options.skip_routing_if_connected {
1152 for gate in circuit.gates() {
1154 if gate.qubits().len() == 2 {
1155 let gate_qubits: Vec<_> = gate.qubits().clone();
1156 let physical_q1 = layout[&gate_qubits[0]];
1157 let physical_q2 = layout[&gate_qubits[1]];
1158
1159 if !options
1160 .hardware_spec
1161 .coupling_map
1162 .are_connected(physical_q1, physical_q2)
1163 {
1164 return true;
1165 }
1166 }
1167 }
1168 false
1169 } else {
1170 true
1171 }
1172 }
1173
1174 fn analyze_connectivity_scirs2(
1176 &self,
1177 coupling_map: &CouplingMap,
1178 ) -> QuantRS2Result<HashMap<String, f64>> {
1179 let mut graph: ScirsGraph<usize, f64> = ScirsGraph::new();
1181
1182 for i in 0..coupling_map.num_qubits() {
1184 graph.add_node(i);
1185 }
1186
1187 for edge in coupling_map.edges() {
1189 let _ = graph.add_edge(edge.0, edge.1, 1.0); }
1191
1192 let mut metrics = HashMap::new();
1194
1195 if let Some(diam) = diameter(&graph) {
1197 metrics.insert("diameter".to_string(), diam);
1198 }
1199
1200 let components = connected_components(&graph);
1202 metrics.insert("connected_components".to_string(), components.len() as f64);
1203
1204 let bridge_list = bridges(&graph);
1206 metrics.insert("bridges".to_string(), bridge_list.len() as f64);
1207
1208 let art_points = articulation_points(&graph);
1210 metrics.insert("articulation_points".to_string(), art_points.len() as f64);
1211
1212 Ok(metrics)
1213 }
1214
1215 fn find_optimal_path_scirs2(
1217 &self,
1218 coupling_map: &CouplingMap,
1219 start: usize,
1220 end: usize,
1221 algorithm: PathAlgorithm,
1222 ) -> QuantRS2Result<Vec<usize>> {
1223 let mut graph: ScirsGraph<usize, f64> = ScirsGraph::new();
1225
1226 for i in 0..coupling_map.num_qubits() {
1227 graph.add_node(i);
1228 }
1229
1230 for edge in coupling_map.edges() {
1231 let weight = 1.0;
1233 let _ = graph.add_edge(edge.0, edge.1, weight);
1234 }
1235
1236 match algorithm {
1238 PathAlgorithm::Dijkstra => {
1239 if let Ok(Some(path_struct)) = dijkstra_path(&graph, &start, &end) {
1240 Ok(path_struct.nodes)
1242 } else {
1243 Err(QuantRS2Error::InvalidInput(format!(
1244 "No path found between qubits {start} and {end}"
1245 )))
1246 }
1247 }
1248 PathAlgorithm::AStar => {
1249 let heuristic = |node: &usize| -> f64 {
1251 f64::from(((*node as i32) - (end as i32)).abs())
1253 };
1254
1255 if let Ok(result) = astar_search(&graph, &start, &end, heuristic) {
1256 Ok(result.path)
1258 } else {
1259 Err(QuantRS2Error::InvalidInput(format!(
1260 "A* search failed between qubits {start} and {end}"
1261 )))
1262 }
1263 }
1264 PathAlgorithm::KShortest => {
1265 if let Ok(paths) = k_shortest_paths(&graph, &start, &end, 3) {
1267 if let Some((cost, path)) = paths.first() {
1268 Ok(path.clone())
1270 } else {
1271 Err(QuantRS2Error::InvalidInput(format!(
1272 "No path found between qubits {start} and {end}"
1273 )))
1274 }
1275 } else {
1276 Err(QuantRS2Error::InvalidInput(format!(
1277 "k-shortest paths failed between qubits {start} and {end}"
1278 )))
1279 }
1280 }
1281 }
1282 }
1283
1284 fn route_circuit<const N: usize>(
1286 &self,
1287 circuit: &Circuit<N>,
1288 layout: &HashMap<QubitId, usize>,
1289 options: &TranspilationOptions,
1290 ) -> QuantRS2Result<RoutedCircuit<N>> {
1291 let config = crate::routing::SabreConfig::default();
1292 let router = SabreRouter::new(options.hardware_spec.coupling_map.clone(), config);
1293
1294 router.route(circuit)
1295 }
1296
1297 fn apply_device_optimizations<const N: usize>(
1299 &self,
1300 circuit: &Circuit<N>,
1301 options: &TranspilationOptions,
1302 ) -> QuantRS2Result<Circuit<N>> {
1303 let mut optimized_circuit = circuit.clone();
1304
1305 match options.hardware_spec.name.as_str() {
1307 "ibm_quantum" => {
1308 optimized_circuit = self.apply_ibm_optimizations(&optimized_circuit, options)?;
1309 }
1310 "google_quantum" => {
1311 optimized_circuit = self.apply_google_optimizations(&optimized_circuit, options)?;
1312 }
1313 "aws_braket" => {
1314 optimized_circuit = self.apply_aws_optimizations(&optimized_circuit, options)?;
1315 }
1316 _ => {
1317 optimized_circuit =
1319 self.apply_generic_optimizations(&optimized_circuit, options)?;
1320 }
1321 }
1322
1323 Ok(optimized_circuit)
1324 }
1325
1326 fn apply_ibm_optimizations<const N: usize>(
1328 &self,
1329 circuit: &Circuit<N>,
1330 options: &TranspilationOptions,
1331 ) -> QuantRS2Result<Circuit<N>> {
1332 Ok(circuit.clone())
1335 }
1336
1337 fn apply_google_optimizations<const N: usize>(
1339 &self,
1340 circuit: &Circuit<N>,
1341 options: &TranspilationOptions,
1342 ) -> QuantRS2Result<Circuit<N>> {
1343 Ok(circuit.clone())
1346 }
1347
1348 fn apply_aws_optimizations<const N: usize>(
1350 &self,
1351 circuit: &Circuit<N>,
1352 options: &TranspilationOptions,
1353 ) -> QuantRS2Result<Circuit<N>> {
1354 Ok(circuit.clone())
1357 }
1358
1359 fn apply_generic_optimizations<const N: usize>(
1361 &self,
1362 circuit: &Circuit<N>,
1363 options: &TranspilationOptions,
1364 ) -> QuantRS2Result<Circuit<N>> {
1365 Ok(circuit.clone())
1367 }
1368
1369 fn validate_transpiled_circuit<const N: usize>(
1371 &self,
1372 circuit: &Circuit<N>,
1373 spec: &HardwareSpec,
1374 ) -> QuantRS2Result<()> {
1375 for gate in circuit.gates() {
1377 if !self.is_native_gate(gate.as_ref(), spec) {
1378 return Err(QuantRS2Error::InvalidInput(format!(
1379 "Non-native gate {} found in transpiled circuit",
1380 gate.name()
1381 )));
1382 }
1383 }
1384
1385 Ok(())
1389 }
1390
1391 fn calculate_depth<const N: usize>(&self, circuit: &Circuit<N>) -> usize {
1393 circuit.gates().len()
1395 }
1396
1397 fn estimate_error_rate<const N: usize>(
1399 &self,
1400 circuit: &Circuit<N>,
1401 spec: &HardwareSpec,
1402 ) -> f64 {
1403 let mut total_error = 0.0;
1404
1405 for gate in circuit.gates() {
1406 if let Some(error) = spec.gate_errors.get(gate.name()) {
1407 total_error += error;
1408 }
1409 }
1410
1411 total_error
1412 }
1413
1414 fn load_common_hardware_specs(&mut self) {
1416 self.add_hardware_spec(HardwareSpec::ibm_quantum());
1418
1419 self.add_hardware_spec(HardwareSpec::google_quantum());
1421
1422 self.add_hardware_spec(HardwareSpec::aws_braket());
1424
1425 self.add_hardware_spec(HardwareSpec::generic());
1427 }
1428}
1429
1430impl Default for DeviceTranspiler {
1431 fn default() -> Self {
1432 Self::new()
1433 }
1434}
1435
1436impl HardwareSpec {
1437 #[must_use]
1439 pub fn ibm_quantum() -> Self {
1440 let mut single_qubit = HashSet::new();
1441 single_qubit.extend(
1442 ["X", "Y", "Z", "H", "S", "T", "RZ", "RX", "RY"]
1443 .iter()
1444 .map(|s| (*s).to_string()),
1445 );
1446
1447 let mut two_qubit = HashSet::new();
1448 two_qubit.extend(["CNOT", "CZ"].iter().map(|s| (*s).to_string()));
1449
1450 let native_gates = NativeGateSet {
1451 single_qubit,
1452 two_qubit,
1453 multi_qubit: HashSet::new(),
1454 parameterized: [("RZ", 1), ("RX", 1), ("RY", 1)]
1455 .iter()
1456 .map(|(k, v)| ((*k).to_string(), *v))
1457 .collect(),
1458 };
1459
1460 Self {
1461 name: "ibm_quantum".to_string(),
1462 max_qubits: 127,
1463 coupling_map: CouplingMap::grid(11, 12), native_gates,
1465 gate_errors: [("CNOT", 0.01), ("RZ", 0.0001)]
1466 .iter()
1467 .map(|(k, v)| ((*k).to_string(), *v))
1468 .collect(),
1469 coherence_times: HashMap::new(),
1470 gate_durations: [("CNOT", 300.0), ("RZ", 0.0)]
1471 .iter()
1472 .map(|(k, v)| ((*k).to_string(), *v))
1473 .collect(),
1474 readout_fidelity: HashMap::new(),
1475 crosstalk_matrix: None,
1476 calibration_timestamp: std::time::SystemTime::now(),
1477 }
1478 }
1479
1480 #[must_use]
1482 pub fn google_quantum() -> Self {
1483 let mut single_qubit = HashSet::new();
1484 single_qubit.extend(
1485 ["X", "Y", "Z", "H", "RZ", "SQRT_X"]
1486 .iter()
1487 .map(|s| (*s).to_string()),
1488 );
1489
1490 let mut two_qubit = HashSet::new();
1491 two_qubit.extend(["CZ", "ISWAP"].iter().map(|s| (*s).to_string()));
1492
1493 let native_gates = NativeGateSet {
1494 single_qubit,
1495 two_qubit,
1496 multi_qubit: HashSet::new(),
1497 parameterized: [("RZ", 1)]
1498 .iter()
1499 .map(|(k, v)| ((*k).to_string(), *v))
1500 .collect(),
1501 };
1502
1503 Self {
1504 name: "google_quantum".to_string(),
1505 max_qubits: 70,
1506 coupling_map: CouplingMap::grid(8, 9),
1507 native_gates,
1508 gate_errors: [("CZ", 0.005), ("RZ", 0.0001)]
1509 .iter()
1510 .map(|(k, v)| ((*k).to_string(), *v))
1511 .collect(),
1512 coherence_times: HashMap::new(),
1513 gate_durations: [("CZ", 20.0), ("RZ", 0.0)]
1514 .iter()
1515 .map(|(k, v)| ((*k).to_string(), *v))
1516 .collect(),
1517 readout_fidelity: HashMap::new(),
1518 crosstalk_matrix: None,
1519 calibration_timestamp: std::time::SystemTime::now(),
1520 }
1521 }
1522
1523 #[must_use]
1525 pub fn aws_braket() -> Self {
1526 let mut single_qubit = HashSet::new();
1527 single_qubit.extend(
1528 ["X", "Y", "Z", "H", "RZ", "RX", "RY"]
1529 .iter()
1530 .map(|s| (*s).to_string()),
1531 );
1532
1533 let mut two_qubit = HashSet::new();
1534 two_qubit.extend(["CNOT", "CZ", "ISWAP"].iter().map(|s| (*s).to_string()));
1535
1536 let native_gates = NativeGateSet {
1537 single_qubit,
1538 two_qubit,
1539 multi_qubit: HashSet::new(),
1540 parameterized: [("RZ", 1), ("RX", 1), ("RY", 1)]
1541 .iter()
1542 .map(|(k, v)| ((*k).to_string(), *v))
1543 .collect(),
1544 };
1545
1546 Self {
1547 name: "aws_braket".to_string(),
1548 max_qubits: 100,
1549 coupling_map: CouplingMap::all_to_all(100),
1550 native_gates,
1551 gate_errors: [("CNOT", 0.008), ("RZ", 0.0001)]
1552 .iter()
1553 .map(|(k, v)| ((*k).to_string(), *v))
1554 .collect(),
1555 coherence_times: HashMap::new(),
1556 gate_durations: [("CNOT", 200.0), ("RZ", 0.0)]
1557 .iter()
1558 .map(|(k, v)| ((*k).to_string(), *v))
1559 .collect(),
1560 readout_fidelity: HashMap::new(),
1561 crosstalk_matrix: None,
1562 calibration_timestamp: std::time::SystemTime::now(),
1563 }
1564 }
1565
1566 #[must_use]
1568 pub fn generic() -> Self {
1569 let mut single_qubit = HashSet::new();
1570 single_qubit.extend(
1571 ["X", "Y", "Z", "H", "S", "T", "RZ", "RX", "RY"]
1572 .iter()
1573 .map(|s| (*s).to_string()),
1574 );
1575
1576 let mut two_qubit = HashSet::new();
1577 two_qubit.extend(
1578 ["CNOT", "CZ", "ISWAP", "SWAP"]
1579 .iter()
1580 .map(|s| (*s).to_string()),
1581 );
1582
1583 let mut multi_qubit = HashSet::new();
1584 multi_qubit.extend(["Toffoli", "Fredkin"].iter().map(|s| (*s).to_string()));
1585
1586 let native_gates = NativeGateSet {
1587 single_qubit,
1588 two_qubit,
1589 multi_qubit,
1590 parameterized: [("RZ", 1), ("RX", 1), ("RY", 1)]
1591 .iter()
1592 .map(|(k, v)| ((*k).to_string(), *v))
1593 .collect(),
1594 };
1595
1596 Self {
1597 name: "generic".to_string(),
1598 max_qubits: 1000,
1599 coupling_map: CouplingMap::all_to_all(1000),
1600 native_gates,
1601 gate_errors: HashMap::new(),
1602 coherence_times: HashMap::new(),
1603 gate_durations: HashMap::new(),
1604 readout_fidelity: HashMap::new(),
1605 crosstalk_matrix: None,
1606 calibration_timestamp: std::time::SystemTime::now(),
1607 }
1608 }
1609}
1610
1611#[cfg(test)]
1612mod tests {
1613 use super::*;
1614 use quantrs2_core::gate::multi::CNOT;
1615 use quantrs2_core::gate::single::Hadamard;
1616
1617 #[test]
1618 #[ignore = "slow test: creates large coupling maps (1000+ qubits)"]
1619 fn test_transpiler_creation() {
1620 let transpiler = DeviceTranspiler::new();
1621 assert!(!transpiler.available_devices().is_empty());
1622 }
1623
1624 #[test]
1625 fn test_hardware_spec_creation() {
1626 let spec = HardwareSpec::ibm_quantum();
1627 assert_eq!(spec.name, "ibm_quantum");
1628 assert!(spec.native_gates.single_qubit.contains("H"));
1629 assert!(spec.native_gates.two_qubit.contains("CNOT"));
1630 }
1631
1632 #[test]
1633 #[ignore = "slow test: uses default options with large coupling maps"]
1634 fn test_transpilation_options() {
1635 let options = TranspilationOptions {
1636 strategy: TranspilationStrategy::MinimizeDepth,
1637 max_iterations: 5,
1638 ..Default::default()
1639 };
1640
1641 assert_eq!(options.strategy, TranspilationStrategy::MinimizeDepth);
1642 assert_eq!(options.max_iterations, 5);
1643 }
1644
1645 #[test]
1646 #[ignore = "slow test: loads multiple hardware specs with large coupling maps"]
1647 fn test_native_gate_checking() {
1648 let transpiler = DeviceTranspiler::new();
1649 let spec = HardwareSpec::ibm_quantum();
1650
1651 let h_gate = Hadamard { target: QubitId(0) };
1652 assert!(transpiler.is_native_gate(&h_gate, &spec));
1653 }
1654
1655 #[test]
1656 #[ignore = "slow test: creates transpiler with large coupling maps"]
1657 fn test_needs_decomposition() {
1658 let transpiler = DeviceTranspiler::new();
1659 let spec = HardwareSpec::ibm_quantum();
1660
1661 let mut circuit = Circuit::<2>::new();
1662 circuit
1663 .add_gate(Hadamard { target: QubitId(0) })
1664 .expect("add H gate to circuit");
1665
1666 assert!(!transpiler.needs_decomposition(&circuit, &spec));
1668 }
1669}