1use petgraph::visit::EdgeRef;
7use scirs2_core::random::prelude::*;
8use serde::{Deserialize, Serialize};
9use std::cmp::Ordering;
10use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
11
12use crate::topology::HardwareTopology;
13use crate::topology_analysis::{AllocationStrategy, TopologyAnalyzer};
14use crate::{DeviceError, DeviceResult};
15use quantrs2_circuit::prelude::*;
16
17#[derive(Debug, Clone, Copy)]
19pub enum AdvancedRoutingStrategy {
20 SABRE { heuristic_weight: f64 },
22 AStarLookahead { lookahead_depth: usize },
24 TokenSwapping,
26 Hybrid,
28 MLGuided,
30}
31
32#[derive(Debug, Clone)]
34struct GateDependency {
35 gate_id: usize,
36 gate_type: String,
37 qubits: Vec<usize>,
38 predecessors: HashSet<usize>,
39 successors: HashSet<usize>,
40 scheduled: bool,
41}
42
43#[derive(Debug, Clone)]
45struct RoutingState {
46 mapping: HashMap<usize, usize>,
48 reverse_mapping: HashMap<usize, usize>,
50 scheduled_gates: HashSet<usize>,
52 front_layer: HashSet<usize>,
54 cost: usize,
56 swaps: Vec<SwapOperation>,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct SwapOperation {
63 pub phys_qubit1: usize,
65 pub phys_qubit2: usize,
66 pub log_qubit1: Option<usize>,
68 pub log_qubit2: Option<usize>,
69 pub position: usize,
71 pub cost: f64,
73}
74
75#[derive(Debug, Clone)]
77pub struct AdvancedRoutingResult {
78 pub initial_mapping: HashMap<usize, usize>,
80 pub final_mapping: HashMap<usize, usize>,
82 pub swap_sequence: Vec<SwapOperation>,
84 pub total_cost: f64,
86 pub depth_overhead: usize,
88 pub gate_overhead: usize,
90 pub routing_time: u128,
92 pub metrics: RoutingMetrics,
94}
95
96#[derive(Debug, Clone)]
98pub struct RoutingMetrics {
99 pub iterations: usize,
101 pub states_explored: usize,
103 pub avg_lookahead: f64,
105 pub swap_chain_lengths: Vec<usize>,
107 pub critical_path_increase: f64,
109}
110
111pub struct AdvancedQubitRouter {
113 topology: HardwareTopology,
114 analyzer: TopologyAnalyzer,
115 strategy: AdvancedRoutingStrategy,
116 rng: StdRng,
117}
118
119impl AdvancedQubitRouter {
120 pub fn new(topology: HardwareTopology, strategy: AdvancedRoutingStrategy, seed: u64) -> Self {
122 let analyzer = TopologyAnalyzer::new(topology.clone());
123 Self {
124 topology,
125 analyzer,
126 strategy,
127 rng: StdRng::seed_from_u64(seed),
128 }
129 }
130
131 pub fn route_circuit<const N: usize>(
133 &mut self,
134 circuit: &Circuit<N>,
135 ) -> DeviceResult<AdvancedRoutingResult> {
136 let start_time = std::time::Instant::now();
137
138 let dependencies = self.build_dependency_graph(circuit)?;
140
141 let initial_mapping = self.find_optimal_initial_mapping(&dependencies, N)?;
143
144 let result = match self.strategy {
146 AdvancedRoutingStrategy::SABRE { heuristic_weight } => {
147 self.route_sabre(dependencies, initial_mapping.clone(), heuristic_weight)?
148 }
149 AdvancedRoutingStrategy::AStarLookahead { lookahead_depth } => {
150 self.route_astar(dependencies, initial_mapping.clone(), lookahead_depth)?
151 }
152 AdvancedRoutingStrategy::TokenSwapping => {
153 self.route_token_swapping(dependencies, initial_mapping.clone())?
154 }
155 AdvancedRoutingStrategy::Hybrid => {
156 self.route_hybrid(dependencies, initial_mapping.clone())?
157 }
158 AdvancedRoutingStrategy::MLGuided => {
159 self.route_sabre(dependencies, initial_mapping.clone(), 0.5)?
161 }
162 };
163
164 let routing_time = start_time.elapsed().as_millis();
165
166 Ok(AdvancedRoutingResult {
167 initial_mapping,
168 final_mapping: result.final_mapping,
169 swap_sequence: result.swap_sequence,
170 total_cost: result.total_cost,
171 depth_overhead: result.depth_overhead,
172 gate_overhead: result.gate_overhead,
173 routing_time,
174 metrics: result.metrics,
175 })
176 }
177
178 fn build_dependency_graph<const N: usize>(
180 &self,
181 circuit: &Circuit<N>,
182 ) -> DeviceResult<Vec<GateDependency>> {
183 let mut dependencies: Vec<GateDependency> = Vec::new();
184 let mut last_gate_on_qubit: HashMap<usize, usize> = HashMap::new();
185
186 for (gate_id, gate) in circuit.gates().iter().enumerate() {
187 let qubits: Vec<usize> = gate.qubits().iter().map(|q| q.id() as usize).collect();
188 let mut predecessors = HashSet::new();
189 let mut successors = HashSet::new();
190
191 for &qubit in &qubits {
193 if let Some(&pred_id) = last_gate_on_qubit.get(&qubit) {
194 predecessors.insert(pred_id);
195 dependencies[pred_id].successors.insert(gate_id);
196 }
197 last_gate_on_qubit.insert(qubit, gate_id);
198 }
199
200 dependencies.push(GateDependency {
201 gate_id,
202 gate_type: gate.name().to_string(),
203 qubits,
204 predecessors,
205 successors,
206 scheduled: false,
207 });
208 }
209
210 Ok(dependencies)
211 }
212
213 fn find_optimal_initial_mapping(
215 &mut self,
216 dependencies: &[GateDependency],
217 num_qubits: usize,
218 ) -> DeviceResult<HashMap<usize, usize>> {
219 let mut interaction_counts: HashMap<(usize, usize), usize> = HashMap::new();
221
222 for dep in dependencies {
223 if dep.qubits.len() == 2 {
224 let (q1, q2) = (dep.qubits[0], dep.qubits[1]);
225 let key = if q1 < q2 { (q1, q2) } else { (q2, q1) };
226 *interaction_counts.entry(key).or_insert(0) += 1;
227 }
228 }
229
230 let physical_qubits = self
232 .analyzer
233 .allocate_qubits(num_qubits, AllocationStrategy::Balanced)?;
234
235 let mut logical_graph = petgraph::graph::UnGraph::<usize, usize>::new_undirected();
237 let mut node_map = HashMap::new();
238
239 for i in 0..num_qubits {
240 let node = logical_graph.add_node(i);
241 node_map.insert(i, node);
242 }
243
244 for ((q1, q2), &count) in &interaction_counts {
245 if let (Some(&n1), Some(&n2)) = (node_map.get(q1), node_map.get(q2)) {
246 logical_graph.add_edge(n1, n2, count);
247 }
248 }
249
250 self.graph_matching_mapping(&logical_graph, &physical_qubits)
252 }
253
254 fn graph_matching_mapping(
256 &self,
257 logical_graph: &petgraph::graph::UnGraph<usize, usize>,
258 physical_qubits: &[u32],
259 ) -> DeviceResult<HashMap<usize, usize>> {
260 let mut mapping = HashMap::new();
261 let mut used_physical = HashSet::new();
262
263 for node in logical_graph.node_indices() {
265 let logical_qubit = logical_graph[node];
266
267 let mut best_physical = None;
269 let mut best_score = f64::NEG_INFINITY;
270
271 for &phys in physical_qubits {
272 if used_physical.contains(&phys) {
273 continue;
274 }
275
276 let score = self.calculate_mapping_score(
278 logical_qubit,
279 phys as usize,
280 &mapping,
281 logical_graph,
282 );
283
284 if score > best_score {
285 best_score = score;
286 best_physical = Some(phys as usize);
287 }
288 }
289
290 if let Some(phys) = best_physical {
291 mapping.insert(logical_qubit, phys);
292 used_physical.insert(phys as u32);
293 }
294 }
295
296 Ok(mapping)
297 }
298
299 fn calculate_mapping_score(
301 &self,
302 logical_qubit: usize,
303 physical_qubit: usize,
304 current_mapping: &HashMap<usize, usize>,
305 logical_graph: &petgraph::graph::UnGraph<usize, usize>,
306 ) -> f64 {
307 let mut score = 0.0;
308
309 if let Some(log_node) = logical_graph
311 .node_indices()
312 .find(|&n| logical_graph[n] == logical_qubit)
313 {
314 for neighbor in logical_graph.neighbors(log_node) {
315 let neighbor_logical = logical_graph[neighbor];
316
317 if let Some(&neighbor_physical) = current_mapping.get(&neighbor_logical) {
318 if self.are_connected(physical_qubit, neighbor_physical) {
320 score += 10.0;
321 } else {
322 let dist = self.get_distance(physical_qubit, neighbor_physical);
324 score -= dist as f64;
325 }
326 }
327 }
328 }
329
330 score
331 }
332
333 fn are_connected(&self, q1: usize, q2: usize) -> bool {
335 self.topology
336 .gate_properties
337 .contains_key(&(q1 as u32, q2 as u32))
338 || self
339 .topology
340 .gate_properties
341 .contains_key(&(q2 as u32, q1 as u32))
342 }
343
344 fn get_distance(&self, q1: usize, q2: usize) -> usize {
346 use petgraph::algo::dijkstra;
348
349 if let (Some(n1), Some(n2)) = (
350 self.topology
351 .connectivity
352 .node_indices()
353 .find(|&n| self.topology.connectivity[n] == q1 as u32),
354 self.topology
355 .connectivity
356 .node_indices()
357 .find(|&n| self.topology.connectivity[n] == q2 as u32),
358 ) {
359 let result = dijkstra(&self.topology.connectivity, n1, Some(n2), |_| 1);
360
361 result.get(&n2).copied().unwrap_or(usize::MAX)
362 } else {
363 usize::MAX
364 }
365 }
366
367 fn route_sabre(
369 &mut self,
370 mut dependencies: Vec<GateDependency>,
371 initial_mapping: HashMap<usize, usize>,
372 heuristic_weight: f64,
373 ) -> DeviceResult<InternalRoutingResult> {
374 let mut state = RoutingState {
375 mapping: initial_mapping.clone(),
376 reverse_mapping: initial_mapping.iter().map(|(&k, &v)| (v, k)).collect(),
377 scheduled_gates: HashSet::new(),
378 front_layer: self.get_front_layer(&dependencies),
379 cost: 0,
380 swaps: Vec::new(),
381 };
382
383 let mut metrics = RoutingMetrics {
384 iterations: 0,
385 states_explored: 0,
386 avg_lookahead: 0.0,
387 swap_chain_lengths: Vec::new(),
388 critical_path_increase: 0.0,
389 };
390
391 let mut position = 0;
392
393 while state.scheduled_gates.len() < dependencies.len() {
394 metrics.iterations += 1;
395
396 let executable = self.get_executable_gates(&state, &dependencies);
398
399 if !executable.is_empty() {
400 for gate_id in executable {
402 state.scheduled_gates.insert(gate_id);
403 dependencies[gate_id].scheduled = true;
404 }
405
406 state.front_layer = self.update_front_layer(&state, &dependencies);
408 position += 1;
409 } else {
410 let best_swap = self.find_best_swap_sabre(
412 &state,
413 &dependencies,
414 heuristic_weight,
415 &mut metrics,
416 )?;
417
418 self.apply_swap(&mut state, &best_swap, position);
420 metrics.swap_chain_lengths.push(1);
421 }
422 }
423
424 Ok(InternalRoutingResult {
425 final_mapping: state.mapping,
426 swap_sequence: state.swaps,
427 total_cost: state.cost as f64,
428 depth_overhead: state.cost * 3, gate_overhead: state.cost * 3,
430 metrics,
431 })
432 }
433
434 fn get_front_layer(&self, dependencies: &[GateDependency]) -> HashSet<usize> {
436 dependencies
437 .iter()
438 .filter(|dep| dep.predecessors.is_empty() && !dep.scheduled)
439 .map(|dep| dep.gate_id)
440 .collect()
441 }
442
443 fn get_executable_gates(
445 &self,
446 state: &RoutingState,
447 dependencies: &[GateDependency],
448 ) -> Vec<usize> {
449 let mut executable = Vec::new();
450
451 for &gate_id in &state.front_layer {
452 let dep = &dependencies[gate_id];
453
454 if self.can_execute_gate(dep, &state.mapping) {
456 executable.push(gate_id);
457 }
458 }
459
460 executable
461 }
462
463 fn can_execute_gate(&self, dep: &GateDependency, mapping: &HashMap<usize, usize>) -> bool {
465 if dep.qubits.len() == 1 {
466 return true; }
468
469 if dep.qubits.len() == 2 {
470 let phys1 = mapping.get(&dep.qubits[0]).copied().unwrap_or(usize::MAX);
471 let phys2 = mapping.get(&dep.qubits[1]).copied().unwrap_or(usize::MAX);
472
473 return self.are_connected(phys1, phys2);
474 }
475
476 false }
478
479 fn update_front_layer(
481 &self,
482 state: &RoutingState,
483 dependencies: &[GateDependency],
484 ) -> HashSet<usize> {
485 let mut new_front = HashSet::new();
486
487 for (gate_id, dep) in dependencies.iter().enumerate() {
488 if !dep.scheduled {
489 if dep
491 .predecessors
492 .iter()
493 .all(|&pred| state.scheduled_gates.contains(&pred))
494 {
495 new_front.insert(gate_id);
496 }
497 }
498 }
499
500 new_front
501 }
502
503 fn find_best_swap_sabre(
505 &self,
506 state: &RoutingState,
507 dependencies: &[GateDependency],
508 heuristic_weight: f64,
509 metrics: &mut RoutingMetrics,
510 ) -> DeviceResult<SwapOperation> {
511 let mut best_swap = None;
512 let mut best_score = f64::INFINITY;
513
514 for edge in self.topology.connectivity.edge_references() {
516 let phys1 = self.topology.connectivity[edge.source()] as usize;
517 let phys2 = self.topology.connectivity[edge.target()] as usize;
518
519 let mut temp_state = state.clone();
521 let swap = SwapOperation {
522 phys_qubit1: phys1,
523 phys_qubit2: phys2,
524 log_qubit1: temp_state.reverse_mapping.get(&phys1).copied(),
525 log_qubit2: temp_state.reverse_mapping.get(&phys2).copied(),
526 position: 0,
527 cost: 1.0,
528 };
529
530 self.apply_swap(&mut temp_state, &swap, 0);
531
532 let score = self.sabre_heuristic(
534 &temp_state,
535 dependencies,
536 &state.front_layer,
537 heuristic_weight,
538 );
539
540 if score < best_score {
541 best_score = score;
542 best_swap = Some(swap);
543 }
544
545 metrics.states_explored += 1;
546 }
547
548 best_swap.ok_or(DeviceError::RoutingError("No valid swap found".to_string()))
549 }
550
551 fn sabre_heuristic(
553 &self,
554 state: &RoutingState,
555 dependencies: &[GateDependency],
556 front_layer: &HashSet<usize>,
557 weight: f64,
558 ) -> f64 {
559 let mut score = 0.0;
560
561 let executable_count = front_layer
563 .iter()
564 .filter(|&&gate_id| self.can_execute_gate(&dependencies[gate_id], &state.mapping))
565 .count();
566 score -= executable_count as f64;
567
568 let extended_layer = self.get_extended_front_layer(front_layer, dependencies);
570
571 for &gate_id in &extended_layer {
572 let dep = &dependencies[gate_id];
573 if dep.qubits.len() == 2 {
574 let phys1 = state.mapping.get(&dep.qubits[0]).copied().unwrap_or(0);
575 let phys2 = state.mapping.get(&dep.qubits[1]).copied().unwrap_or(0);
576 let dist = self.get_distance(phys1, phys2);
577 score += weight * dist as f64;
578 }
579 }
580
581 score
582 }
583
584 fn get_extended_front_layer(
586 &self,
587 front_layer: &HashSet<usize>,
588 dependencies: &[GateDependency],
589 ) -> HashSet<usize> {
590 let mut extended = front_layer.clone();
591
592 for &gate_id in front_layer {
594 for &succ in &dependencies[gate_id].successors {
595 extended.insert(succ);
596 }
597 }
598
599 extended
600 }
601
602 fn apply_swap(&self, state: &mut RoutingState, swap: &SwapOperation, position: usize) {
604 if let (Some(log1), Some(log2)) = (swap.log_qubit1, swap.log_qubit2) {
606 state.mapping.insert(log1, swap.phys_qubit2);
607 state.mapping.insert(log2, swap.phys_qubit1);
608 state.reverse_mapping.insert(swap.phys_qubit1, log2);
609 state.reverse_mapping.insert(swap.phys_qubit2, log1);
610 } else if let Some(log1) = swap.log_qubit1 {
611 state.mapping.insert(log1, swap.phys_qubit2);
612 state.reverse_mapping.remove(&swap.phys_qubit1);
613 state.reverse_mapping.insert(swap.phys_qubit2, log1);
614 } else if let Some(log2) = swap.log_qubit2 {
615 state.mapping.insert(log2, swap.phys_qubit1);
616 state.reverse_mapping.remove(&swap.phys_qubit2);
617 state.reverse_mapping.insert(swap.phys_qubit1, log2);
618 }
619
620 state.cost += 1;
622 let mut swap_with_position = swap.clone();
623 swap_with_position.position = position;
624 state.swaps.push(swap_with_position);
625 }
626
627 fn route_astar(
629 &mut self,
630 dependencies: Vec<GateDependency>,
631 initial_mapping: HashMap<usize, usize>,
632 lookahead_depth: usize,
633 ) -> DeviceResult<InternalRoutingResult> {
634 #[derive(Clone)]
636 struct AStarState {
637 routing_state: RoutingState,
638 f_score: f64,
639 g_score: f64,
640 h_score: f64,
641 }
642
643 impl PartialEq for AStarState {
644 fn eq(&self, other: &Self) -> bool {
645 self.f_score == other.f_score
646 }
647 }
648
649 impl Eq for AStarState {}
650
651 impl Ord for AStarState {
652 fn cmp(&self, other: &Self) -> Ordering {
653 other
654 .f_score
655 .partial_cmp(&self.f_score)
656 .unwrap_or(Ordering::Equal)
657 }
658 }
659
660 impl PartialOrd for AStarState {
661 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
662 Some(self.cmp(other))
663 }
664 }
665
666 let initial_state = RoutingState {
668 mapping: initial_mapping.clone(),
669 reverse_mapping: initial_mapping.iter().map(|(&k, &v)| (v, k)).collect(),
670 scheduled_gates: HashSet::new(),
671 front_layer: self.get_front_layer(&dependencies),
672 cost: 0,
673 swaps: Vec::new(),
674 };
675
676 let h_score = self.astar_heuristic(&initial_state, &dependencies, lookahead_depth);
677
678 let mut open_set = BinaryHeap::new();
679 open_set.push(AStarState {
680 routing_state: initial_state,
681 f_score: h_score,
682 g_score: 0.0,
683 h_score,
684 });
685
686 let mut metrics = RoutingMetrics {
687 iterations: 0,
688 states_explored: 0,
689 avg_lookahead: 0.0,
690 swap_chain_lengths: Vec::new(),
691 critical_path_increase: 0.0,
692 };
693
694 while let Some(current) = open_set.pop() {
696 metrics.iterations += 1;
697
698 if current.routing_state.scheduled_gates.len() == dependencies.len() {
700 return Ok(InternalRoutingResult {
701 final_mapping: current.routing_state.mapping,
702 swap_sequence: current.routing_state.swaps,
703 total_cost: current.g_score,
704 depth_overhead: current.routing_state.cost * 3,
705 gate_overhead: current.routing_state.cost * 3,
706 metrics,
707 });
708 }
709
710 let neighbors =
712 self.generate_astar_neighbors(¤t.routing_state, &dependencies, &mut metrics);
713
714 for (neighbor_state, action_cost) in neighbors {
715 let g_score = current.g_score + action_cost;
716 let h_score = self.astar_heuristic(&neighbor_state, &dependencies, lookahead_depth);
717 let f_score = g_score + h_score;
718
719 open_set.push(AStarState {
720 routing_state: neighbor_state,
721 f_score,
722 g_score,
723 h_score,
724 });
725
726 metrics.states_explored += 1;
727 }
728 }
729
730 Err(DeviceError::RoutingError(
731 "A* search failed to find solution".to_string(),
732 ))
733 }
734
735 fn generate_astar_neighbors(
737 &self,
738 state: &RoutingState,
739 dependencies: &[GateDependency],
740 metrics: &mut RoutingMetrics,
741 ) -> Vec<(RoutingState, f64)> {
742 let mut neighbors = Vec::new();
743
744 let executable = self.get_executable_gates(state, dependencies);
746 if !executable.is_empty() {
747 let mut new_state = state.clone();
748 for gate_id in executable {
749 new_state.scheduled_gates.insert(gate_id);
750 }
751 new_state.front_layer = self.update_front_layer(&new_state, dependencies);
752 neighbors.push((new_state, 0.0)); } else {
754 for edge in self.topology.connectivity.edge_references() {
756 let phys1 = self.topology.connectivity[edge.source()] as usize;
757 let phys2 = self.topology.connectivity[edge.target()] as usize;
758
759 let mut new_state = state.clone();
760 let swap = SwapOperation {
761 phys_qubit1: phys1,
762 phys_qubit2: phys2,
763 log_qubit1: new_state.reverse_mapping.get(&phys1).copied(),
764 log_qubit2: new_state.reverse_mapping.get(&phys2).copied(),
765 position: 0,
766 cost: 1.0,
767 };
768
769 self.apply_swap(&mut new_state, &swap, 0);
770 neighbors.push((new_state, 1.0)); }
772 }
773
774 neighbors
775 }
776
777 fn astar_heuristic(
779 &self,
780 state: &RoutingState,
781 dependencies: &[GateDependency],
782 lookahead_depth: usize,
783 ) -> f64 {
784 let mut score = 0.0;
785 let mut current_layer = state.front_layer.clone();
786
787 for depth in 0..lookahead_depth {
789 let mut min_swaps_needed = 0;
790
791 for &gate_id in ¤t_layer {
792 if state.scheduled_gates.contains(&gate_id) {
793 continue;
794 }
795
796 let dep = &dependencies[gate_id];
797 if dep.qubits.len() == 2 {
798 let phys1 = state.mapping.get(&dep.qubits[0]).copied().unwrap_or(0);
799 let phys2 = state.mapping.get(&dep.qubits[1]).copied().unwrap_or(0);
800
801 if !self.are_connected(phys1, phys2) {
802 let dist = self.get_distance(phys1, phys2);
803 min_swaps_needed += (dist - 1).max(0);
804 }
805 }
806 }
807
808 score += min_swaps_needed as f64 * (0.9_f64).powi(depth as i32);
809
810 let mut next_layer = HashSet::new();
812 for &gate_id in ¤t_layer {
813 for &succ in &dependencies[gate_id].successors {
814 if !state.scheduled_gates.contains(&succ) {
815 next_layer.insert(succ);
816 }
817 }
818 }
819
820 if next_layer.is_empty() {
821 break;
822 }
823 current_layer = next_layer;
824 }
825
826 score
827 }
828
829 fn route_token_swapping(
831 &mut self,
832 dependencies: Vec<GateDependency>,
833 initial_mapping: HashMap<usize, usize>,
834 ) -> DeviceResult<InternalRoutingResult> {
835 let mut state = RoutingState {
837 mapping: initial_mapping.clone(),
838 reverse_mapping: initial_mapping.iter().map(|(&k, &v)| (v, k)).collect(),
839 scheduled_gates: HashSet::new(),
840 front_layer: HashSet::new(),
841 cost: 0,
842 swaps: Vec::new(),
843 };
844
845 let mut metrics = RoutingMetrics {
846 iterations: 0,
847 states_explored: 0,
848 avg_lookahead: 0.0,
849 swap_chain_lengths: Vec::new(),
850 critical_path_increase: 0.0,
851 };
852
853 for (position, dep) in dependencies.iter().enumerate() {
855 if dep.qubits.len() == 2 {
856 let log1 = dep.qubits[0];
857 let log2 = dep.qubits[1];
858 let phys1 = state.mapping[&log1];
859 let phys2 = state.mapping[&log2];
860
861 if !self.are_connected(phys1, phys2) {
862 let swap_path = self.find_swap_path(phys1, phys2)?;
864
865 for i in 0..swap_path.len() - 1 {
866 let swap = SwapOperation {
867 phys_qubit1: swap_path[i],
868 phys_qubit2: swap_path[i + 1],
869 log_qubit1: state.reverse_mapping.get(&swap_path[i]).copied(),
870 log_qubit2: state.reverse_mapping.get(&swap_path[i + 1]).copied(),
871 position,
872 cost: 1.0,
873 };
874
875 self.apply_swap(&mut state, &swap, position);
876 metrics.iterations += 1;
877 }
878
879 metrics.swap_chain_lengths.push(swap_path.len() - 1);
880 }
881 }
882
883 state.scheduled_gates.insert(dep.gate_id);
884 }
885
886 Ok(InternalRoutingResult {
887 final_mapping: state.mapping,
888 swap_sequence: state.swaps,
889 total_cost: state.cost as f64,
890 depth_overhead: state.cost * 3,
891 gate_overhead: state.cost * 3,
892 metrics,
893 })
894 }
895
896 fn find_swap_path(&self, start: usize, target: usize) -> DeviceResult<Vec<usize>> {
898 use petgraph::algo::astar;
899
900 let start_node = self
901 .topology
902 .connectivity
903 .node_indices()
904 .find(|&n| self.topology.connectivity[n] == start as u32)
905 .ok_or(DeviceError::RoutingError(
906 "Start qubit not found".to_string(),
907 ))?;
908
909 let target_node = self
910 .topology
911 .connectivity
912 .node_indices()
913 .find(|&n| self.topology.connectivity[n] == target as u32)
914 .ok_or(DeviceError::RoutingError(
915 "Target qubit not found".to_string(),
916 ))?;
917
918 let result = astar(
919 &self.topology.connectivity,
920 start_node,
921 |n| n == target_node,
922 |_| 1,
923 |_| 0,
924 );
925
926 if let Some((_, path)) = result {
927 Ok(path
928 .into_iter()
929 .map(|n| self.topology.connectivity[n] as usize)
930 .collect())
931 } else {
932 Err(DeviceError::RoutingError(
933 "No path found between qubits".to_string(),
934 ))
935 }
936 }
937
938 fn route_hybrid(
940 &mut self,
941 dependencies: Vec<GateDependency>,
942 initial_mapping: HashMap<usize, usize>,
943 ) -> DeviceResult<InternalRoutingResult> {
944 let strategies = [
946 (
947 AdvancedRoutingStrategy::SABRE {
948 heuristic_weight: 0.5,
949 },
950 0.4,
951 ),
952 (
953 AdvancedRoutingStrategy::AStarLookahead { lookahead_depth: 3 },
954 0.4,
955 ),
956 (AdvancedRoutingStrategy::TokenSwapping, 0.2),
957 ];
958
959 let mut best_result = None;
960 let mut best_cost = f64::INFINITY;
961
962 for (strategy, weight) in strategies {
963 let mut temp_router =
964 AdvancedQubitRouter::new(self.topology.clone(), strategy, thread_rng().gen());
965
966 if let Ok(result) = match strategy {
967 AdvancedRoutingStrategy::SABRE { heuristic_weight } => temp_router.route_sabre(
968 dependencies.clone(),
969 initial_mapping.clone(),
970 heuristic_weight,
971 ),
972 AdvancedRoutingStrategy::AStarLookahead { lookahead_depth } => temp_router
973 .route_astar(
974 dependencies.clone(),
975 initial_mapping.clone(),
976 lookahead_depth,
977 ),
978 AdvancedRoutingStrategy::TokenSwapping => {
979 temp_router.route_token_swapping(dependencies.clone(), initial_mapping.clone())
980 }
981 _ => continue,
982 } {
983 let weighted_cost = result.total_cost * weight;
984 if weighted_cost < best_cost {
985 best_cost = weighted_cost;
986 best_result = Some(result);
987 }
988 }
989 }
990
991 best_result.ok_or(DeviceError::RoutingError(
992 "Hybrid routing failed".to_string(),
993 ))
994 }
995}
996
997struct InternalRoutingResult {
999 final_mapping: HashMap<usize, usize>,
1000 swap_sequence: Vec<SwapOperation>,
1001 total_cost: f64,
1002 depth_overhead: usize,
1003 gate_overhead: usize,
1004 metrics: RoutingMetrics,
1005}
1006
1007#[cfg(test)]
1008mod tests {
1009 use super::*;
1010 use crate::topology_analysis::create_standard_topology;
1011
1012 #[test]
1013 fn test_sabre_routing() {
1014 let topology = create_standard_topology("grid", 9).unwrap();
1015 let mut router = AdvancedQubitRouter::new(
1016 topology,
1017 AdvancedRoutingStrategy::SABRE {
1018 heuristic_weight: 0.5,
1019 },
1020 42,
1021 );
1022
1023 let mut circuit = Circuit::<4>::new();
1025 circuit
1026 .add_gate(quantrs2_core::gate::multi::CNOT {
1027 control: quantrs2_core::qubit::QubitId(0),
1028 target: quantrs2_core::qubit::QubitId(2),
1029 })
1030 .unwrap();
1031 circuit
1032 .add_gate(quantrs2_core::gate::multi::CNOT {
1033 control: quantrs2_core::qubit::QubitId(1),
1034 target: quantrs2_core::qubit::QubitId(3),
1035 })
1036 .unwrap();
1037
1038 let result = router.route_circuit(&circuit).unwrap();
1039
1040 assert!(!result.swap_sequence.is_empty());
1041 assert!(result.total_cost > 0.0);
1042 }
1043
1044 #[test]
1045 fn test_astar_routing() {
1046 let topology = create_standard_topology("linear", 5).unwrap();
1047 let mut router = AdvancedQubitRouter::new(
1048 topology,
1049 AdvancedRoutingStrategy::AStarLookahead { lookahead_depth: 2 },
1050 42,
1051 );
1052
1053 let mut circuit = Circuit::<3>::new();
1054 circuit
1055 .add_gate(quantrs2_core::gate::multi::CNOT {
1056 control: quantrs2_core::qubit::QubitId(0),
1057 target: quantrs2_core::qubit::QubitId(2),
1058 })
1059 .unwrap();
1060
1061 let result = router.route_circuit(&circuit).unwrap();
1062
1063 assert!(result.metrics.iterations > 0);
1064 assert!(result.metrics.states_explored > 0);
1065 }
1066
1067 #[test]
1068 fn test_token_swapping() {
1069 let topology = create_standard_topology("linear", 4).unwrap();
1070 let mut router =
1071 AdvancedQubitRouter::new(topology, AdvancedRoutingStrategy::TokenSwapping, 42);
1072
1073 let mut circuit = Circuit::<4>::new();
1074 circuit
1075 .add_gate(quantrs2_core::gate::multi::CNOT {
1076 control: quantrs2_core::qubit::QubitId(0),
1077 target: quantrs2_core::qubit::QubitId(3),
1078 })
1079 .unwrap();
1080
1081 let result = router.route_circuit(&circuit).unwrap();
1082
1083 assert!(!result.swap_sequence.is_empty());
1085 }
1086
1087 #[test]
1088 fn test_hybrid_routing() {
1089 let topology = create_standard_topology("grid", 9).unwrap();
1090 let mut router = AdvancedQubitRouter::new(topology, AdvancedRoutingStrategy::Hybrid, 42);
1091
1092 let mut circuit = Circuit::<4>::new();
1093 circuit
1094 .add_gate(quantrs2_core::gate::single::Hadamard {
1095 target: quantrs2_core::qubit::QubitId(0),
1096 })
1097 .unwrap();
1098 circuit
1099 .add_gate(quantrs2_core::gate::multi::CNOT {
1100 control: quantrs2_core::qubit::QubitId(0),
1101 target: quantrs2_core::qubit::QubitId(1),
1102 })
1103 .unwrap();
1104
1105 let result = router.route_circuit(&circuit).unwrap();
1106
1107 assert_eq!(result.initial_mapping.len(), 4);
1110 }
1111}