mod coupling_map;
mod lookahead;
mod routing_pass;
mod sabre;
mod swap_network;
pub use coupling_map::{CouplingMap, Distance};
pub use lookahead::{LookaheadConfig, LookaheadRouter};
pub use routing_pass::{RoutedCircuit, RoutingPassType, RoutingResult, RoutingStatistics};
pub use sabre::{SabreConfig, SabreRouter};
pub use swap_network::{SwapLayer, SwapNetwork};
use crate::builder::Circuit;
use quantrs2_core::error::QuantRS2Result;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RoutingStrategy {
Sabre,
Lookahead { depth: usize },
Basic,
Stochastic { trials: usize },
}
pub struct CircuitRouter {
strategy: RoutingStrategy,
coupling_map: CouplingMap,
}
impl CircuitRouter {
#[must_use]
pub const fn new(strategy: RoutingStrategy, coupling_map: CouplingMap) -> Self {
Self {
strategy,
coupling_map,
}
}
#[must_use]
pub fn for_backend(backend: &str) -> Self {
let coupling_map = match backend {
"ibm_lagos" => CouplingMap::ibm_lagos(),
"ibm_nairobi" => CouplingMap::ibm_nairobi(),
"google_sycamore" => CouplingMap::google_sycamore(),
_ => CouplingMap::linear(5), };
Self {
strategy: RoutingStrategy::Sabre,
coupling_map,
}
}
pub fn route<const N: usize>(&self, circuit: &Circuit<N>) -> QuantRS2Result<RoutedCircuit<N>> {
match self.strategy {
RoutingStrategy::Sabre => {
let config = SabreConfig::default();
let router = SabreRouter::new(self.coupling_map.clone(), config);
router.route(circuit)
}
RoutingStrategy::Lookahead { depth } => {
let config = LookaheadConfig::new(depth);
let router = LookaheadRouter::new(self.coupling_map.clone(), config);
router.route(circuit)
}
RoutingStrategy::Basic => {
let config = SabreConfig::basic();
let router = SabreRouter::new(self.coupling_map.clone(), config);
router.route(circuit)
}
RoutingStrategy::Stochastic { trials } => {
let mut best_result = None;
let mut best_cost = f64::INFINITY;
for _ in 0..trials {
let config = SabreConfig::stochastic();
let router = SabreRouter::new(self.coupling_map.clone(), config);
let result = router.route(circuit)?;
let cost = result.total_cost();
if cost < best_cost {
best_cost = cost;
best_result = Some(result);
}
}
best_result.ok_or_else(|| {
quantrs2_core::error::QuantRS2Error::RoutingError(
"Failed to find valid routing".to_string(),
)
})
}
}
}
#[must_use]
pub const fn coupling_map(&self) -> &CouplingMap {
&self.coupling_map
}
}
pub mod analysis {
use super::{Circuit, CouplingMap};
use crate::dag::{circuit_to_dag, CircuitDag};
pub struct RoutingAnalyzer {
coupling_map: CouplingMap,
}
impl RoutingAnalyzer {
#[must_use]
pub const fn new(coupling_map: CouplingMap) -> Self {
Self { coupling_map }
}
#[must_use]
pub fn estimate_swaps<const N: usize>(&self, circuit: &Circuit<N>) -> usize {
let dag = circuit_to_dag(circuit);
let mut swap_count = 0;
for node in dag.nodes() {
if node.gate.qubits().len() == 2 {
let q1 = node.gate.qubits()[0];
let q2 = node.gate.qubits()[1];
if !self
.coupling_map
.are_connected(q1.id() as usize, q2.id() as usize)
{
let distance = self
.coupling_map
.distance(q1.id() as usize, q2.id() as usize);
swap_count += distance.saturating_sub(1);
}
}
}
swap_count
}
#[must_use]
pub fn interaction_density<const N: usize>(&self, circuit: &Circuit<N>) -> f64 {
let mut interactions = std::collections::HashSet::new();
for gate in circuit.gates() {
if gate.qubits().len() == 2 {
let q1 = gate.qubits()[0].id() as usize;
let q2 = gate.qubits()[1].id() as usize;
interactions.insert((q1.min(q2), q1.max(q2)));
}
}
let max_interactions = (N * (N - 1)) / 2;
if max_interactions == 0 {
0.0
} else {
interactions.len() as f64 / max_interactions as f64
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use quantrs2_core::gate::{multi::CNOT, single::Hadamard};
use quantrs2_core::qubit::QubitId;
#[test]
fn test_basic_routing() {
let coupling_map = CouplingMap::linear(3);
let router = CircuitRouter::new(RoutingStrategy::Basic, coupling_map);
let mut circuit = Circuit::<3>::new();
circuit
.add_gate(Hadamard { target: QubitId(0) })
.expect("add H gate to circuit");
circuit
.add_gate(CNOT {
control: QubitId(0),
target: QubitId(2),
})
.expect("add CNOT gate to circuit");
let result = router.route(&circuit);
assert!(result.is_ok());
}
}