1use super::*;
4
5#[cfg(feature = "scirs2")]
11use scirs2_graph::planarity::is_planar;
12#[cfg(feature = "scirs2")]
13use scirs2_graph::spectral::{laplacian, LaplacianType};
14#[cfg(feature = "scirs2")]
15use scirs2_graph::{
16 chromatic_number as scirs2_chromatic_number, connected_components, is_bipartite, modularity,
17 pagerank_centrality,
18};
19
20pub struct SciRS2QubitMapper {
22 config: SciRS2MappingConfig,
24 device_topology: HardwareTopology,
26 calibration: Option<DeviceCalibration>,
28
29 logical_graph: Option<Graph<usize, f64>>,
31 physical_graph: Option<Graph<usize, f64>>,
32 spectral_cache: Option<SpectralAnalysisResult>,
33 community_cache: Option<CommunityAnalysisResult>,
34 centrality_cache: Option<CentralityAnalysisResult>,
35}
36
37impl SciRS2QubitMapper {
38 pub fn new(
40 config: SciRS2MappingConfig,
41 device_topology: HardwareTopology,
42 calibration: Option<DeviceCalibration>,
43 ) -> Self {
44 Self {
45 config,
46 device_topology,
47 calibration,
48 logical_graph: None,
49 physical_graph: None,
50 spectral_cache: None,
51 community_cache: None,
52 centrality_cache: None,
53 }
54 }
55
56 #[cfg(feature = "scirs2")]
58 pub fn map_circuit<const N: usize>(
59 &mut self,
60 circuit: &Circuit<N>,
61 ) -> DeviceResult<SciRS2MappingResult> {
62 let start_time = std::time::Instant::now();
63
64 let logical_graph = self.build_logical_graph(circuit)?;
66 self.logical_graph = None;
68
69 let physical_graph = self.build_physical_graph()?;
71 self.physical_graph = None;
73
74 let graph_analysis = self.analyze_graphs(&logical_graph, &physical_graph)?;
76
77 let spectral_analysis = if self.config.enable_spectral_analysis {
79 Some(self.perform_spectral_analysis(&logical_graph, &physical_graph)?)
80 } else {
81 None
82 };
83
84 let community_analysis =
86 self.perform_community_analysis(&logical_graph, &physical_graph)?;
87
88 let centrality_analysis = if self.config.enable_centrality_optimization {
90 self.perform_centrality_analysis(&logical_graph, &physical_graph)?
91 } else {
92 CentralityAnalysisResult {
93 betweenness_centrality: HashMap::new(),
94 closeness_centrality: HashMap::new(),
95 eigenvector_centrality: HashMap::new(),
96 pagerank_centrality: HashMap::new(),
97 centrality_correlations: Array2::zeros((0, 0)),
98 centrality_statistics: CentralityStatistics {
99 max_betweenness: 0.0,
100 max_closeness: 0.0,
101 max_eigenvector: 0.0,
102 max_pagerank: 0.0,
103 mean_betweenness: 0.0,
104 mean_closeness: 0.0,
105 mean_eigenvector: 0.0,
106 mean_pagerank: 0.0,
107 },
108 }
109 };
110
111 let initial_mapping = self.generate_initial_mapping(
113 &logical_graph,
114 &physical_graph,
115 &graph_analysis,
116 spectral_analysis.as_ref(),
117 &community_analysis,
118 ¢rality_analysis,
119 )?;
120
121 let (final_mapping, swap_operations, optimization_metrics) = self.optimize_mapping(
123 circuit,
124 initial_mapping.clone(),
125 &logical_graph,
126 &physical_graph,
127 )?;
128
129 let performance_predictions = if self.config.enable_ml_predictions {
131 Some(self.predict_performance(&final_mapping, circuit, &graph_analysis)?)
132 } else {
133 None
134 };
135
136 let realtime_analytics = self.generate_realtime_analytics(&optimization_metrics)?;
138
139 let ml_performance = if self.config.ml_config.enable_ml {
141 Some(self.analyze_ml_performance(&final_mapping, &optimization_metrics)?)
142 } else {
143 None
144 };
145
146 let adaptive_insights = self.generate_adaptive_insights(&optimization_metrics)?;
148
149 let optimization_recommendations = self.generate_optimization_recommendations(
151 &graph_analysis,
152 &optimization_metrics,
153 spectral_analysis.as_ref(),
154 &community_analysis,
155 )?;
156
157 Ok(SciRS2MappingResult {
158 initial_mapping,
159 final_mapping,
160 swap_operations,
161 graph_analysis,
162 spectral_analysis,
163 community_analysis,
164 centrality_analysis,
165 optimization_metrics,
166 performance_predictions,
167 realtime_analytics,
168 ml_performance,
169 adaptive_insights,
170 optimization_recommendations,
171 })
172 }
173
174 #[cfg(not(feature = "scirs2"))]
176 pub fn map_circuit<const N: usize>(
177 &mut self,
178 circuit: &Circuit<N>,
179 ) -> DeviceResult<SciRS2MappingResult> {
180 let mut initial_mapping = HashMap::new();
182 let mut final_mapping = HashMap::new();
183
184 for i in 0..N.min(self.device_topology.num_qubits()) {
186 initial_mapping.insert(i, i);
187 final_mapping.insert(i, i);
188 }
189
190 Ok(SciRS2MappingResult {
191 initial_mapping,
192 final_mapping,
193 swap_operations: Vec::new(),
194 graph_analysis: GraphAnalysisResult {
195 density: 0.5,
196 clustering_coefficient: 0.3,
197 diameter: 4,
198 radius: 2,
199 average_path_length: 2.5,
200 connectivity_stats: ConnectivityStats {
201 edge_connectivity: 2,
202 vertex_connectivity: 1,
203 algebraic_connectivity: 0.5,
204 is_connected: true,
205 num_components: 1,
206 largest_component_size: N,
207 },
208 topological_properties: TopologicalProperties {
209 is_planar: true,
210 is_bipartite: false,
211 is_tree: false,
212 is_forest: false,
213 has_cycles: true,
214 girth: 3,
215 chromatic_number: 3,
216 independence_number: 5,
217 },
218 },
219 spectral_analysis: None,
220 community_analysis: CommunityAnalysisResult {
221 communities: HashMap::new(),
222 modularity: 0.4,
223 num_communities: 1,
224 community_sizes: vec![N],
225 inter_community_edges: 0,
226 quality_metrics: CommunityQualityMetrics {
227 silhouette_score: 0.7,
228 conductance: 0.3,
229 coverage: 0.8,
230 performance: 0.75,
231 },
232 },
233 centrality_analysis: CentralityAnalysisResult {
234 betweenness_centrality: HashMap::new(),
235 closeness_centrality: HashMap::new(),
236 eigenvector_centrality: HashMap::new(),
237 pagerank_centrality: HashMap::new(),
238 centrality_correlations: Array2::zeros((0, 0)),
239 centrality_statistics: CentralityStatistics {
240 max_betweenness: 0.0,
241 max_closeness: 0.0,
242 max_eigenvector: 0.0,
243 max_pagerank: 0.0,
244 mean_betweenness: 0.0,
245 mean_closeness: 0.0,
246 mean_eigenvector: 0.0,
247 mean_pagerank: 0.0,
248 },
249 },
250 optimization_metrics: OptimizationMetrics {
251 optimization_time: Duration::from_millis(1),
252 iterations: 1,
253 converged: true,
254 final_objective: 0.0,
255 best_objective: 0.0,
256 improvement_ratio: 0.0,
257 constraint_violations: 0.0,
258 algorithm_metrics: HashMap::new(),
259 resource_usage: ResourceUsageMetrics {
260 peak_memory: 1024,
261 average_cpu: 1.0,
262 energy_consumption: None,
263 network_overhead: None,
264 },
265 },
266 performance_predictions: None,
267 realtime_analytics: RealtimeAnalyticsResult {
268 current_metrics: HashMap::new(),
269 performance_trends: HashMap::new(),
270 anomalies: Vec::new(),
271 resource_utilization: ResourceUtilization {
272 cpu_usage: 1.0,
273 memory_usage: 5.0,
274 disk_io: 0.0,
275 network_usage: 0.0,
276 gpu_usage: None,
277 },
278 quality_assessments: Vec::new(),
279 },
280 ml_performance: None,
281 adaptive_insights: AdaptiveMappingInsights {
282 learning_progress: HashMap::new(),
283 adaptation_effectiveness: HashMap::new(),
284 performance_trends: HashMap::new(),
285 recommended_adjustments: Vec::new(),
286 },
287 optimization_recommendations: OptimizationRecommendations {
288 algorithm_recommendations: Vec::new(),
289 parameter_suggestions: Vec::new(),
290 hardware_optimizations: Vec::new(),
291 improvement_predictions: HashMap::new(),
292 },
293 })
294 }
295
296 #[cfg(feature = "scirs2")]
298 fn build_logical_graph<const N: usize>(
299 &self,
300 circuit: &Circuit<N>,
301 ) -> DeviceResult<Graph<usize, f64>> {
302 let mut graph = Graph::new();
303
304 let mut node_map: HashMap<usize, usize> = HashMap::new();
306 for i in 0..N {
307 let node = graph.add_node(i);
308 node_map.insert(i, node.index());
309 }
310
311 for gate in circuit.gates() {
313 let qubits = gate.qubits();
314 if qubits.len() == 2 {
315 let q1 = qubits[0].id() as usize;
316 let q2 = qubits[1].id() as usize;
317
318 if let (Some(&node1), Some(&node2)) = (node_map.get(&q1), node_map.get(&q2)) {
319 let weight = self.calculate_gate_weight(gate.as_ref());
322 let _ = graph.add_edge(node1, node2, weight);
323 }
324 }
325 }
326
327 Ok(graph)
328 }
329
330 #[cfg(feature = "scirs2")]
332 fn build_physical_graph(&self) -> DeviceResult<Graph<usize, f64>> {
333 let mut graph = Graph::new();
334
335 let mut node_map: HashMap<usize, usize> = HashMap::new();
337 for i in 0..self.device_topology.num_qubits() {
338 let node = graph.add_node(i);
339 node_map.insert(i, node.index());
340 }
341
342 for (q1, q2) in self.device_topology.connectivity() {
344 if let (Some(&node1), Some(&node2)) = (node_map.get(&q1), node_map.get(&q2)) {
345 let weight = self.get_connection_weight(q1, q2);
347 let _ = graph.add_edge(node1, node2, weight);
348 }
349 }
350
351 Ok(graph)
352 }
353
354 fn calculate_gate_weight(&self, _gate: &dyn GateOp) -> f64 {
356 1.0
358 }
359
360 fn get_connection_weight(&self, q1: usize, q2: usize) -> f64 {
362 if let Some(calibration) = &self.calibration {
363 calibration.gate_fidelity(q1, q2).unwrap_or(1.0)
365 } else {
366 1.0
367 }
368 }
369
370 fn calculate_objective<const N: usize>(
372 &self,
373 mapping: &HashMap<usize, usize>,
374 circuit: &Circuit<N>,
375 ) -> DeviceResult<f64> {
376 let mut objective = 0.0;
377
378 match self.config.optimization_objective {
379 OptimizationObjective::MinimizeSwaps => {
380 for gate in circuit.gates() {
382 let qubits = gate.qubits();
383 if qubits.len() == 2 {
384 let logical_q1 = qubits[0].id() as usize;
385 let logical_q2 = qubits[1].id() as usize;
386
387 if let (Some(&physical_q1), Some(&physical_q2)) =
388 (mapping.get(&logical_q1), mapping.get(&logical_q2))
389 {
390 if !self.device_topology.are_connected(physical_q1, physical_q2) {
391 objective += 1.0;
393 }
394 }
395 }
396 }
397 }
398 OptimizationObjective::MinimizeDepth => {
399 objective = circuit.gates().len() as f64;
401 }
402 OptimizationObjective::MaximizeFidelity => {
403 if let Some(calibration) = &self.calibration {
405 let mut total_fidelity = 0.0;
406 let mut gate_count = 0;
407
408 for gate in circuit.gates() {
409 let qubits = gate.qubits();
410 if qubits.len() == 1 {
411 let q = qubits[0].id() as usize;
412 if let Some(&physical_q) = mapping.get(&q) {
413 total_fidelity += calibration
414 .single_qubit_fidelity(physical_q)
415 .unwrap_or(0.99);
416 gate_count += 1;
417 }
418 } else if qubits.len() == 2 {
419 let q1 = qubits[0].id() as usize;
420 let q2 = qubits[1].id() as usize;
421 if let (Some(&pq1), Some(&pq2)) = (mapping.get(&q1), mapping.get(&q2)) {
422 total_fidelity +=
423 calibration.gate_fidelity(pq1, pq2).unwrap_or(0.95);
424 gate_count += 1;
425 }
426 }
427 }
428
429 objective = -(total_fidelity / gate_count.max(1) as f64); } else {
431 objective = -0.95; }
433 }
434 _ => {
435 objective = 0.0;
437 }
438 }
439
440 Ok(objective)
441 }
442
443 #[cfg(feature = "scirs2")]
452 fn analyze_graphs(
453 &self,
454 _logical_graph: &Graph<usize, f64>,
455 physical_graph: &Graph<usize, f64>,
456 ) -> DeviceResult<GraphAnalysisResult> {
457 let n = physical_graph.node_count();
458
459 let density = if n >= 2 {
460 graph_density(physical_graph).unwrap_or(0.0)
461 } else {
462 0.0
463 };
464
465 let clustering_coefficient = clustering_coefficient(physical_graph)
466 .map(|per_node| {
467 if per_node.is_empty() {
468 0.0
469 } else {
470 per_node.values().sum::<f64>() / per_node.len() as f64
471 }
472 })
473 .unwrap_or(0.0);
474
475 let diameter_f = diameter(physical_graph).unwrap_or(0.0);
476 let radius_f = radius(physical_graph).unwrap_or(0.0);
477 let average_path_length = Self::average_shortest_path_length(physical_graph);
478
479 let components = connected_components(physical_graph);
480 let num_components = components.len();
481 let largest_component_size = components.iter().map(|c| c.len()).max().unwrap_or(0);
482 let is_connected = num_components <= 1;
483 let algebraic_connectivity = Self::algebraic_connectivity(physical_graph).unwrap_or(0.0);
484 let (edge_connectivity, vertex_connectivity) =
485 Self::min_degree_connectivity_bounds(physical_graph);
486
487 let bipartite_result = is_bipartite(physical_graph);
488 let edge_count = physical_graph.edge_count();
489 let is_forest = num_components > 0 && edge_count == n.saturating_sub(num_components);
490 let is_tree = is_connected && is_forest && n > 0;
491 let has_cycles = !is_forest;
492 let girth = Self::compute_girth(physical_graph).unwrap_or(0);
493 let max_degree = Self::max_degree(physical_graph);
494 let chromatic_number = if n == 0 {
495 0
496 } else if n <= 40 {
497 scirs2_chromatic_number(physical_graph, max_degree + 1).unwrap_or(max_degree + 1)
498 } else {
499 max_degree + 1
503 };
504 let independence_number = Self::greedy_independence_number(physical_graph);
505 let edges: Vec<(usize, usize)> = Self::edge_list(physical_graph);
506 let is_planar = n == 0 || is_planar(&edges, n);
507
508 Ok(GraphAnalysisResult {
509 density,
510 clustering_coefficient,
511 diameter: diameter_f.round().max(0.0) as usize,
512 radius: radius_f.round().max(0.0) as usize,
513 average_path_length,
514 connectivity_stats: ConnectivityStats {
515 edge_connectivity,
516 vertex_connectivity,
517 algebraic_connectivity,
518 is_connected,
519 num_components,
520 largest_component_size,
521 },
522 topological_properties: TopologicalProperties {
523 is_planar,
524 is_bipartite: bipartite_result.is_bipartite,
525 is_tree,
526 is_forest,
527 has_cycles,
528 girth,
529 chromatic_number,
530 independence_number,
531 },
532 })
533 }
534
535 fn perform_spectral_analysis(
542 &self,
543 _logical_graph: &Graph<usize, f64>,
544 physical_graph: &Graph<usize, f64>,
545 ) -> DeviceResult<SpectralAnalysisResult> {
546 let n = physical_graph.node_count();
547 if n == 0 {
548 return Ok(SpectralAnalysisResult {
549 laplacian_eigenvalues: Array1::zeros(0),
550 embedding_vectors: Array2::zeros((0, 0)),
551 spectral_radius: 0.0,
552 algebraic_connectivity: 0.0,
553 spectral_gap: 0.0,
554 embedding_quality: EmbeddingQuality {
555 stress: 0.0,
556 distortion: 0.0,
557 preservation_ratio: 0.0,
558 embedding_dimension: 0,
559 },
560 });
561 }
562
563 let laplacian_matrix = laplacian(physical_graph, LaplacianType::Standard)
564 .map_err(|e| DeviceError::GraphAnalysisError(format!("laplacian failed: {e}")))?;
565 let (eigenvalues_complex, eigenvectors_complex) = eig(&laplacian_matrix.view(), None)
566 .map_err(|e| DeviceError::GraphAnalysisError(format!("eig failed: {e}")))?;
567
568 let mut order: Vec<usize> = (0..n).collect();
569 order.sort_by(|&a, &b| {
570 eigenvalues_complex[a]
571 .re
572 .partial_cmp(&eigenvalues_complex[b].re)
573 .unwrap_or(std::cmp::Ordering::Equal)
574 });
575 let sorted_eigenvalues: Vec<f64> =
576 order.iter().map(|&i| eigenvalues_complex[i].re).collect();
577 let laplacian_eigenvalues = Array1::from_vec(sorted_eigenvalues.clone());
578
579 let spectral_radius_value = sorted_eigenvalues
580 .iter()
581 .cloned()
582 .fold(0.0_f64, |acc, v| acc.max(v.abs()));
583 let algebraic_connectivity = if n >= 2 {
584 sorted_eigenvalues[1].max(0.0)
585 } else {
586 0.0
587 };
588 let spectral_gap = if n >= 2 {
589 (sorted_eigenvalues[n - 1] - sorted_eigenvalues[n.saturating_sub(2)]).abs()
590 } else {
591 0.0
592 };
593
594 let embedding_dimension = (n.saturating_sub(1)).min(2);
597 let mut embedding_vectors = Array2::<f64>::zeros((n, embedding_dimension));
598 for (col, &eig_idx) in order.iter().skip(1).take(embedding_dimension).enumerate() {
599 for row in 0..n {
600 embedding_vectors[[row, col]] = eigenvectors_complex[[row, eig_idx]].re;
601 }
602 }
603
604 let (stress, distortion, preservation_ratio) =
605 Self::embedding_quality_metrics(physical_graph, &embedding_vectors);
606
607 Ok(SpectralAnalysisResult {
608 laplacian_eigenvalues,
609 embedding_vectors,
610 spectral_radius: spectral_radius_value,
611 algebraic_connectivity,
612 spectral_gap,
613 embedding_quality: EmbeddingQuality {
614 stress,
615 distortion,
616 preservation_ratio,
617 embedding_dimension,
618 },
619 })
620 }
621
622 fn perform_community_analysis(
627 &self,
628 logical_graph: &Graph<usize, f64>,
629 physical_graph: &Graph<usize, f64>,
630 ) -> DeviceResult<CommunityAnalysisResult> {
631 let graph = if logical_graph.node_count() > 0 {
632 logical_graph
633 } else {
634 physical_graph
635 };
636 let n = graph.node_count();
637 if n == 0 {
638 return Ok(CommunityAnalysisResult {
639 communities: HashMap::new(),
640 modularity: 0.0,
641 num_communities: 0,
642 community_sizes: Vec::new(),
643 inter_community_edges: 0,
644 quality_metrics: CommunityQualityMetrics {
645 silhouette_score: 0.0,
646 conductance: 0.0,
647 coverage: 0.0,
648 performance: 0.0,
649 },
650 });
651 }
652
653 let result = louvain_communities_result(graph);
654 let communities: HashMap<usize, usize> = result.node_communities.clone();
655 let community_sizes: Vec<usize> = result.communities.iter().map(|c| c.len()).collect();
656 let num_communities = result.num_communities;
657 let modularity_score = modularity(graph, &communities);
658
659 let mut inter_community_edges = 0usize;
660 let mut intra_community_edges = 0usize;
661 for node in graph.nodes() {
662 if let Ok(neighbors) = graph.neighbors(node) {
663 for neighbor in neighbors {
664 if communities.get(node) != communities.get(&neighbor) {
665 inter_community_edges += 1;
666 } else {
667 intra_community_edges += 1;
668 }
669 }
670 }
671 }
672 inter_community_edges /= 2;
674 intra_community_edges /= 2;
675 let total_edges = inter_community_edges + intra_community_edges;
676
677 let coverage = if total_edges > 0 {
678 intra_community_edges as f64 / total_edges as f64
679 } else {
680 0.0
681 };
682
683 let mut community_boundary: HashMap<usize, (usize, usize)> = HashMap::new();
686 for node in graph.nodes() {
687 let Some(&community) = communities.get(node) else {
688 continue;
689 };
690 let entry = community_boundary.entry(community).or_insert((0, 0));
691 if let Ok(neighbors) = graph.neighbors(node) {
692 for neighbor in neighbors {
693 entry.1 += 1;
694 if communities.get(&neighbor) != Some(&community) {
695 entry.0 += 1;
696 }
697 }
698 }
699 }
700 let conductance = if community_boundary.is_empty() {
701 0.0
702 } else {
703 community_boundary
704 .values()
705 .map(|&(boundary, total)| {
706 if total > 0 {
707 boundary as f64 / total as f64
708 } else {
709 0.0
710 }
711 })
712 .sum::<f64>()
713 / community_boundary.len() as f64
714 };
715
716 let performance = Self::community_performance(graph, &communities);
719
720 let silhouette_score = Self::community_silhouette_proxy(graph, &communities);
725
726 Ok(CommunityAnalysisResult {
727 communities,
728 modularity: modularity_score,
729 num_communities,
730 community_sizes,
731 inter_community_edges,
732 quality_metrics: CommunityQualityMetrics {
733 silhouette_score,
734 conductance,
735 coverage,
736 performance,
737 },
738 })
739 }
740
741 fn perform_centrality_analysis(
746 &self,
747 _logical_graph: &Graph<usize, f64>,
748 physical_graph: &Graph<usize, f64>,
749 ) -> DeviceResult<CentralityAnalysisResult> {
750 let n = physical_graph.node_count();
751 if n == 0 {
752 return Ok(CentralityAnalysisResult {
753 betweenness_centrality: HashMap::new(),
754 closeness_centrality: HashMap::new(),
755 eigenvector_centrality: HashMap::new(),
756 pagerank_centrality: HashMap::new(),
757 centrality_correlations: Array2::zeros((4, 4)),
758 centrality_statistics: CentralityStatistics {
759 max_betweenness: 0.0,
760 max_closeness: 0.0,
761 max_eigenvector: 0.0,
762 max_pagerank: 0.0,
763 mean_betweenness: 0.0,
764 mean_closeness: 0.0,
765 mean_eigenvector: 0.0,
766 mean_pagerank: 0.0,
767 },
768 });
769 }
770
771 let betweenness = betweenness_centrality(physical_graph, true);
772 let closeness = closeness_centrality(physical_graph, true);
773 let eigenvector = eigenvector_centrality(physical_graph, 200, 1e-9).unwrap_or_default();
774 let pagerank_map = pagerank_centrality(physical_graph, 0.85, 1e-9).unwrap_or_default();
775
776 let nodes: Vec<usize> = physical_graph.nodes().into_iter().cloned().collect();
777 let mut data = Array2::<f64>::zeros((nodes.len(), 4));
778 for (row, node) in nodes.iter().enumerate() {
779 data[[row, 0]] = *betweenness.get(node).unwrap_or(&0.0);
780 data[[row, 1]] = *closeness.get(node).unwrap_or(&0.0);
781 data[[row, 2]] = *eigenvector.get(node).unwrap_or(&0.0);
782 data[[row, 3]] = *pagerank_map.get(node).unwrap_or(&0.0);
783 }
784 let centrality_correlations = corrcoef(&data.view(), "pearson")
785 .unwrap_or_else(|_| Array2::zeros((4, 4)))
786 .mapv(|v: f64| if v.is_finite() { v } else { 0.0 });
787
788 let mean_or_zero = |values: &HashMap<usize, f64>| {
789 if values.is_empty() {
790 0.0
791 } else {
792 values.values().sum::<f64>() / values.len() as f64
793 }
794 };
795 let max_or_zero =
796 |values: &HashMap<usize, f64>| values.values().cloned().fold(0.0_f64, f64::max);
797
798 Ok(CentralityAnalysisResult {
799 betweenness_centrality: betweenness.clone(),
800 closeness_centrality: closeness.clone(),
801 eigenvector_centrality: eigenvector.clone(),
802 pagerank_centrality: pagerank_map.clone(),
803 centrality_correlations,
804 centrality_statistics: CentralityStatistics {
805 max_betweenness: max_or_zero(&betweenness),
806 max_closeness: max_or_zero(&closeness),
807 max_eigenvector: max_or_zero(&eigenvector),
808 max_pagerank: max_or_zero(&pagerank_map),
809 mean_betweenness: mean_or_zero(&betweenness),
810 mean_closeness: mean_or_zero(&closeness),
811 mean_eigenvector: mean_or_zero(&eigenvector),
812 mean_pagerank: mean_or_zero(&pagerank_map),
813 },
814 })
815 }
816
817 fn generate_initial_mapping(
826 &self,
827 logical_graph: &Graph<usize, f64>,
828 physical_graph: &Graph<usize, f64>,
829 _graph_analysis: &GraphAnalysisResult,
830 _spectral_analysis: Option<&SpectralAnalysisResult>,
831 _community_analysis: &CommunityAnalysisResult,
832 centrality_analysis: &CentralityAnalysisResult,
833 ) -> DeviceResult<HashMap<usize, usize>> {
834 let num_physical = self.device_topology.num_qubits();
835 let num_logical = logical_graph.node_count().max(physical_graph.node_count());
836
837 if num_physical == 0 || num_logical == 0 {
838 return Ok(HashMap::new());
839 }
840
841 let mut logical_qubits: Vec<usize> = logical_graph.nodes().into_iter().cloned().collect();
844 if logical_qubits.is_empty() {
845 logical_qubits = (0..num_logical).collect();
846 }
847 logical_qubits.sort_by(|&a, &b| {
848 let deg_a = logical_graph.neighbors(&a).map(|v| v.len()).unwrap_or(0);
849 let deg_b = logical_graph.neighbors(&b).map(|v| v.len()).unwrap_or(0);
850 deg_b.cmp(°_a).then(a.cmp(&b))
851 });
852
853 let mut physical_qubits: Vec<usize> = (0..num_physical).collect();
856 physical_qubits.sort_by(|&a, &b| {
857 let ca = centrality_analysis
858 .betweenness_centrality
859 .get(&a)
860 .copied()
861 .unwrap_or(0.0);
862 let cb = centrality_analysis
863 .betweenness_centrality
864 .get(&b)
865 .copied()
866 .unwrap_or(0.0);
867 cb.partial_cmp(&ca)
868 .unwrap_or(std::cmp::Ordering::Equal)
869 .then(a.cmp(&b))
870 });
871
872 let mut mapping = HashMap::new();
873 for (logical, physical) in logical_qubits.into_iter().zip(physical_qubits) {
874 mapping.insert(logical, physical);
875 }
876 Ok(mapping)
877 }
878
879 fn optimize_mapping<const N: usize>(
886 &self,
887 circuit: &Circuit<N>,
888 initial_mapping: HashMap<usize, usize>,
889 _logical_graph: &Graph<usize, f64>,
890 _physical_graph: &Graph<usize, f64>,
891 ) -> DeviceResult<(
892 HashMap<usize, usize>,
893 Vec<SwapOperation>,
894 OptimizationMetrics,
895 )> {
896 let start_time = Instant::now();
897
898 let mut router = crate::routing_advanced::AdvancedQubitRouter::new(
899 self.device_topology.clone(),
900 crate::routing_advanced::AdvancedRoutingStrategy::SABRE {
901 heuristic_weight: 0.5,
902 },
903 42,
904 );
905 let routing_result = router.route_circuit(circuit)?;
906
907 let optimization_time = start_time.elapsed();
908 let final_mapping = if routing_result.final_mapping.is_empty() {
909 initial_mapping.clone()
910 } else {
911 routing_result.final_mapping
912 };
913 let swap_operations = routing_result.swap_sequence;
914
915 let initial_objective = self.calculate_objective(&initial_mapping, circuit)?;
916 let objective_value = self.calculate_objective(&final_mapping, circuit)?;
917 let improvement_ratio = if initial_objective.abs() > f64::EPSILON {
918 ((initial_objective - objective_value) / initial_objective.abs()).clamp(-1.0, 1.0)
919 } else {
920 0.0
921 };
922
923 let mut algorithm_metrics = HashMap::new();
924 algorithm_metrics.insert("swap_count".to_string(), swap_operations.len() as f64);
925 algorithm_metrics.insert(
926 "routing_time_ms".to_string(),
927 routing_result.routing_time as f64,
928 );
929 algorithm_metrics.insert(
930 "states_explored".to_string(),
931 routing_result.metrics.states_explored as f64,
932 );
933 algorithm_metrics.insert(
934 "depth_overhead".to_string(),
935 routing_result.depth_overhead as f64,
936 );
937
938 let metrics = OptimizationMetrics {
939 optimization_time,
940 iterations: routing_result.metrics.iterations.max(1),
941 converged: true,
942 final_objective: objective_value,
943 best_objective: objective_value,
944 improvement_ratio,
945 constraint_violations: 0.0,
946 algorithm_metrics,
947 resource_usage: ResourceUsageMetrics {
948 peak_memory: (final_mapping.len() + swap_operations.len()) * 64,
952 average_cpu: if optimization_time.as_micros() > 0 {
953 100.0
954 } else {
955 0.0
956 },
957 energy_consumption: None,
958 network_overhead: None,
959 },
960 };
961
962 Ok((final_mapping, swap_operations, metrics))
963 }
964
965 fn predict_performance<const N: usize>(
971 &self,
972 mapping: &HashMap<usize, usize>,
973 circuit: &Circuit<N>,
974 _graph_analysis: &GraphAnalysisResult,
975 ) -> DeviceResult<PerformancePredictions> {
976 let mut predicted_swaps = 0.0;
977 let mut fidelity_total = 0.0;
978 let mut fidelity_count = 0usize;
979 for gate in circuit.gates() {
980 let qubits = gate.qubits();
981 if qubits.len() != 2 {
982 continue;
983 }
984 let logical_q1 = qubits[0].id() as usize;
985 let logical_q2 = qubits[1].id() as usize;
986 if let (Some(&physical_q1), Some(&physical_q2)) =
987 (mapping.get(&logical_q1), mapping.get(&logical_q2))
988 {
989 if !self.device_topology.are_connected(physical_q1, physical_q2) {
990 predicted_swaps += 1.0;
991 }
992 if let Some(calibration) = &self.calibration {
993 fidelity_total += calibration
994 .gate_fidelity(physical_q1, physical_q2)
995 .unwrap_or(0.95);
996 fidelity_count += 1;
997 }
998 }
999 }
1000 let gate_count = circuit.gates().len() as f64;
1001 let predicted_time = gate_count + predicted_swaps * 3.0;
1002 let predicted_fidelity = if fidelity_count > 0 {
1003 fidelity_total / fidelity_count as f64
1004 } else {
1005 0.95
1006 };
1007
1008 Ok(PerformancePredictions {
1009 predicted_swaps,
1010 predicted_time,
1011 predicted_fidelity,
1012 confidence_intervals: HashMap::new(),
1013 uncertainty_estimates: HashMap::new(),
1014 })
1015 }
1016
1017 fn generate_realtime_analytics(
1018 &self,
1019 _metrics: &OptimizationMetrics,
1020 ) -> DeviceResult<RealtimeAnalyticsResult> {
1021 Ok(RealtimeAnalyticsResult {
1022 current_metrics: HashMap::new(),
1023 performance_trends: HashMap::new(),
1024 anomalies: Vec::new(),
1025 resource_utilization: ResourceUtilization {
1026 cpu_usage: 25.0,
1027 memory_usage: 40.0,
1028 disk_io: 10.0,
1029 network_usage: 5.0,
1030 gpu_usage: None,
1031 },
1032 quality_assessments: Vec::new(),
1033 })
1034 }
1035
1036 fn analyze_ml_performance(
1037 &self,
1038 _mapping: &HashMap<usize, usize>,
1039 _metrics: &OptimizationMetrics,
1040 ) -> DeviceResult<MLPerformanceResult> {
1041 Ok(MLPerformanceResult {
1042 model_accuracy: HashMap::new(),
1043 feature_importance: HashMap::new(),
1044 prediction_reliability: 0.9,
1045 training_history: Vec::new(),
1046 })
1047 }
1048
1049 fn generate_adaptive_insights(
1050 &self,
1051 _metrics: &OptimizationMetrics,
1052 ) -> DeviceResult<AdaptiveMappingInsights> {
1053 Ok(AdaptiveMappingInsights {
1054 learning_progress: HashMap::new(),
1055 adaptation_effectiveness: HashMap::new(),
1056 performance_trends: HashMap::new(),
1057 recommended_adjustments: Vec::new(),
1058 })
1059 }
1060
1061 fn generate_optimization_recommendations(
1062 &self,
1063 _graph_analysis: &GraphAnalysisResult,
1064 _metrics: &OptimizationMetrics,
1065 _spectral_analysis: Option<&SpectralAnalysisResult>,
1066 _community_analysis: &CommunityAnalysisResult,
1067 ) -> DeviceResult<OptimizationRecommendations> {
1068 Ok(OptimizationRecommendations {
1069 algorithm_recommendations: Vec::new(),
1070 parameter_suggestions: Vec::new(),
1071 hardware_optimizations: Vec::new(),
1072 improvement_predictions: HashMap::new(),
1073 })
1074 }
1075
1076 fn average_shortest_path_length(graph: &Graph<usize, f64>) -> f64 {
1084 let nodes: Vec<usize> = graph.nodes().into_iter().cloned().collect();
1085 let n = nodes.len();
1086 if n < 2 {
1087 return 0.0;
1088 }
1089 let mut total = 0.0;
1090 let mut count = 0usize;
1091 for i in 0..n {
1092 for j in (i + 1)..n {
1093 if let Ok(Some(path)) = dijkstra_path(graph, &nodes[i], &nodes[j]) {
1094 total += path.total_weight;
1095 count += 1;
1096 }
1097 }
1098 }
1099 if count > 0 {
1100 total / count as f64
1101 } else {
1102 0.0
1103 }
1104 }
1105
1106 fn algebraic_connectivity(graph: &Graph<usize, f64>) -> Option<f64> {
1109 let n = graph.node_count();
1110 if n < 2 {
1111 return Some(0.0);
1112 }
1113 let lap = laplacian(graph, LaplacianType::Standard).ok()?;
1114 let (eigenvalues, _) = eig(&lap.view(), None).ok()?;
1115 let mut vals: Vec<f64> = (0..n).map(|i| eigenvalues[i].re).collect();
1116 vals.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1117 Some(vals[1].max(0.0))
1118 }
1119
1120 fn min_degree_connectivity_bounds(graph: &Graph<usize, f64>) -> (usize, usize) {
1126 let min_degree = graph
1127 .nodes()
1128 .into_iter()
1129 .map(|node| graph.neighbors(node).map(|n| n.len()).unwrap_or(0))
1130 .min()
1131 .unwrap_or(0);
1132 (min_degree, min_degree)
1133 }
1134
1135 fn max_degree(graph: &Graph<usize, f64>) -> usize {
1137 graph
1138 .nodes()
1139 .into_iter()
1140 .map(|node| graph.neighbors(node).map(|n| n.len()).unwrap_or(0))
1141 .max()
1142 .unwrap_or(0)
1143 }
1144
1145 fn compute_girth(graph: &Graph<usize, f64>) -> Option<usize> {
1151 let nodes: Vec<usize> = graph.nodes().into_iter().cloned().collect();
1152 let mut best: Option<usize> = None;
1153 for &start in &nodes {
1154 let mut dist: HashMap<usize, usize> = HashMap::new();
1155 let mut parent: HashMap<usize, usize> = HashMap::new();
1156 dist.insert(start, 0);
1157 let mut queue = VecDeque::new();
1158 queue.push_back(start);
1159 while let Some(u) = queue.pop_front() {
1160 let neighbors = graph.neighbors(&u).unwrap_or_default();
1161 let dist_u = dist[&u];
1162 for v in neighbors {
1163 if let std::collections::hash_map::Entry::Vacant(e) = dist.entry(v) {
1164 e.insert(dist_u + 1);
1165 parent.insert(v, u);
1166 queue.push_back(v);
1167 } else if parent.get(&u) != Some(&v) {
1168 let cycle_len = dist_u + dist[&v] + 1;
1169 best = Some(best.map_or(cycle_len, |b| b.min(cycle_len)));
1170 }
1171 }
1172 }
1173 }
1174 best
1175 }
1176
1177 fn greedy_independence_number(graph: &Graph<usize, f64>) -> usize {
1181 let mut remaining: HashSet<usize> = graph.nodes().into_iter().cloned().collect();
1182 let mut count = 0usize;
1183 while !remaining.is_empty() {
1184 let pick = *remaining
1185 .iter()
1186 .min_by_key(|&&v| {
1187 graph
1188 .neighbors(&v)
1189 .map(|n| n.into_iter().filter(|w| remaining.contains(w)).count())
1190 .unwrap_or(0)
1191 })
1192 .expect("remaining is non-empty");
1193 count += 1;
1194 let neighbors: Vec<usize> = graph.neighbors(&pick).unwrap_or_default();
1195 remaining.remove(&pick);
1196 for neighbor in neighbors {
1197 remaining.remove(&neighbor);
1198 }
1199 }
1200 count
1201 }
1202
1203 #[cfg(feature = "scirs2")]
1207 fn edge_list(graph: &Graph<usize, f64>) -> Vec<(usize, usize)> {
1208 use petgraph::visit::EdgeRef;
1209 graph
1210 .inner()
1211 .edge_references()
1212 .map(|edge| (graph.inner()[edge.source()], graph.inner()[edge.target()]))
1213 .collect()
1214 }
1215
1216 fn embedding_quality_metrics(
1220 graph: &Graph<usize, f64>,
1221 embedding: &Array2<f64>,
1222 ) -> (f64, f64, f64) {
1223 let nodes: Vec<usize> = graph.nodes().into_iter().cloned().collect();
1224 let n = nodes.len();
1225 if n < 2 || embedding.ncols() == 0 {
1226 return (0.0, 0.0, 0.0);
1227 }
1228 let mut sum_sq_diff = 0.0;
1229 let mut sum_sq_graph = 0.0;
1230 let mut sum_rel_diff = 0.0;
1231 let mut count = 0usize;
1232 for i in 0..n {
1233 for j in (i + 1)..n {
1234 let Ok(Some(path)) = dijkstra_path(graph, &nodes[i], &nodes[j]) else {
1235 continue;
1236 };
1237 let d_graph = path.total_weight;
1238 if d_graph <= 0.0 {
1239 continue;
1240 }
1241 let row_i = embedding.row(i);
1242 let row_j = embedding.row(j);
1243 let d_embed = row_i
1244 .iter()
1245 .zip(row_j.iter())
1246 .map(|(a, b)| (a - b).powi(2))
1247 .sum::<f64>()
1248 .sqrt();
1249 sum_sq_diff += (d_graph - d_embed).powi(2);
1250 sum_sq_graph += d_graph.powi(2);
1251 sum_rel_diff += (d_embed - d_graph).abs() / d_graph;
1252 count += 1;
1253 }
1254 }
1255 if count == 0 || sum_sq_graph <= 0.0 {
1256 return (0.0, 0.0, 0.0);
1257 }
1258 let stress = (sum_sq_diff / sum_sq_graph).sqrt();
1259 let distortion = sum_rel_diff / count as f64;
1260 let preservation_ratio = (1.0 - stress).clamp(0.0, 1.0);
1261 (stress, distortion, preservation_ratio)
1262 }
1263
1264 fn community_performance(
1269 graph: &Graph<usize, f64>,
1270 communities: &HashMap<usize, usize>,
1271 ) -> f64 {
1272 let nodes: Vec<usize> = graph.nodes().into_iter().cloned().collect();
1273 let n = nodes.len();
1274 if n < 2 {
1275 return 0.0;
1276 }
1277 let mut correct = 0u64;
1278 let mut total = 0u64;
1279 for i in 0..n {
1280 for j in (i + 1)..n {
1281 let same_community = communities.get(&nodes[i]) == communities.get(&nodes[j]);
1282 let connected = graph
1283 .neighbors(&nodes[i])
1284 .map(|neighbors| neighbors.contains(&nodes[j]))
1285 .unwrap_or(false);
1286 if same_community == connected {
1287 correct += 1;
1288 }
1289 total += 1;
1290 }
1291 }
1292 if total > 0 {
1293 correct as f64 / total as f64
1294 } else {
1295 0.0
1296 }
1297 }
1298
1299 fn community_silhouette_proxy(
1309 graph: &Graph<usize, f64>,
1310 communities: &HashMap<usize, usize>,
1311 ) -> f64 {
1312 let mut scores = Vec::new();
1313 for node in graph.nodes() {
1314 let Some(&own_community) = communities.get(node) else {
1315 continue;
1316 };
1317 let neighbors = graph.neighbors(node).unwrap_or_default();
1318 if neighbors.is_empty() {
1319 continue;
1320 }
1321 let intra = neighbors
1322 .iter()
1323 .filter(|neighbor| communities.get(*neighbor) == Some(&own_community))
1324 .count() as f64;
1325 let total = neighbors.len() as f64;
1326 let a = intra / total;
1327 let b = 1.0 - a;
1328 let denom = a.max(b);
1329 if denom > 0.0 {
1330 scores.push((a - b) / denom);
1331 }
1332 }
1333 if scores.is_empty() {
1334 0.0
1335 } else {
1336 scores.iter().sum::<f64>() / scores.len() as f64
1337 }
1338 }
1339}
1340
1341#[cfg(test)]
1342mod real_analysis_tests {
1343 use super::*;
1344 use crate::mapping_scirs2::utils::{create_standard_topology, generate_random_circuit};
1345
1346 fn mapper_for(topology_type: &str, num_qubits: usize) -> SciRS2QubitMapper {
1347 let topology = create_standard_topology(topology_type, num_qubits)
1348 .expect("standard topology should be constructible");
1349 SciRS2QubitMapper::new(SciRS2MappingConfig::default(), topology, None)
1350 }
1351
1352 #[test]
1353 fn test_graph_analysis_reflects_real_topology_not_fixed_constants() {
1354 let mut complete_mapper = mapper_for("complete", 5);
1358 let mut linear_mapper = mapper_for("linear", 5);
1359 let circuit = generate_random_circuit::<5>(6, 0.6);
1360
1361 let complete_result = complete_mapper
1362 .map_circuit(&circuit)
1363 .expect("mapping should succeed");
1364 let linear_result = linear_mapper
1365 .map_circuit(&circuit)
1366 .expect("mapping should succeed");
1367
1368 assert!(
1369 (complete_result.graph_analysis.density - 1.0).abs() < 1e-9,
1370 "complete graph on 5 nodes must have density 1.0, got {}",
1371 complete_result.graph_analysis.density
1372 );
1373 assert!(
1374 linear_result.graph_analysis.density < complete_result.graph_analysis.density,
1375 "linear chain density ({}) must be lower than complete graph density ({})",
1376 linear_result.graph_analysis.density,
1377 complete_result.graph_analysis.density
1378 );
1379 assert!((linear_result.graph_analysis.density - 0.5).abs() > 1e-9);
1382
1383 assert_eq!(complete_result.graph_analysis.diameter, 1);
1386 assert_eq!(linear_result.graph_analysis.diameter, 4);
1387 assert!(
1391 !complete_result
1392 .graph_analysis
1393 .topological_properties
1394 .is_planar,
1395 "the complete graph on 5 nodes (K5) is not planar"
1396 );
1397 assert!(
1398 linear_result
1399 .graph_analysis
1400 .topological_properties
1401 .is_planar,
1402 "a 5-node linear chain is a tree and therefore planar"
1403 );
1404 assert!(
1405 complete_result
1406 .graph_analysis
1407 .connectivity_stats
1408 .is_connected
1409 );
1410 }
1411
1412 #[test]
1413 fn test_centrality_analysis_identifies_real_hub_qubit() {
1414 let mut mapper = mapper_for("star", 5);
1417 let circuit = generate_random_circuit::<5>(6, 0.6);
1418 let result = mapper
1419 .map_circuit(&circuit)
1420 .expect("mapping should succeed");
1421
1422 let hub_centrality = *result
1423 .centrality_analysis
1424 .betweenness_centrality
1425 .get(&0)
1426 .unwrap_or(&0.0);
1427 for leaf in 1..5 {
1428 let leaf_centrality = *result
1429 .centrality_analysis
1430 .betweenness_centrality
1431 .get(&leaf)
1432 .unwrap_or(&0.0);
1433 assert!(
1434 hub_centrality > leaf_centrality,
1435 "hub (qubit 0) centrality {hub_centrality} must exceed leaf {leaf} centrality {leaf_centrality}"
1436 );
1437 }
1438 }
1439
1440 #[test]
1441 fn test_optimize_mapping_actually_routes_disconnected_circuit() {
1442 let mut mapper = mapper_for("linear", 5);
1450 let mut circuit = Circuit::<5>::new();
1451 let _ = circuit.cnot(QubitId(0), QubitId(4));
1453
1454 let result = mapper
1455 .map_circuit(&circuit)
1456 .expect("mapping should succeed");
1457
1458 let final_objective = result.optimization_metrics.final_objective;
1462 assert!(
1463 final_objective <= 1.0,
1464 "final objective should reflect at most the one long-range interaction, got {final_objective}"
1465 );
1466 assert!(result
1467 .optimization_metrics
1468 .algorithm_metrics
1469 .contains_key("swap_count"));
1470 }
1471
1472 #[test]
1473 fn test_community_analysis_varies_with_real_interaction_graph() {
1474 let mut mapper_a = mapper_for("grid", 6);
1479 let mut mapper_b = mapper_for("grid", 6);
1480 let sparse_circuit = generate_random_circuit::<6>(2, 0.2);
1481 let dense_circuit = generate_random_circuit::<6>(30, 0.9);
1482
1483 let result_a = mapper_a
1484 .map_circuit(&sparse_circuit)
1485 .expect("mapping should succeed");
1486 let result_b = mapper_b
1487 .map_circuit(&dense_circuit)
1488 .expect("mapping should succeed");
1489
1490 assert!(
1495 result_a.community_analysis.communities.len()
1496 <= result_b.community_analysis.communities.len()
1497 || result_a.community_analysis.inter_community_edges
1498 != result_b.community_analysis.inter_community_edges
1499 );
1500 }
1501}