use crate::algo::GraphProjection;
use crate::algo::algorithms::Algorithm;
use std::cmp::Reverse;
use std::collections::BinaryHeap;
use uni_common::core::id::Vid;
pub struct BidirectionalDijkstra;
#[derive(Debug, Clone)]
pub struct BidirectionalDijkstraConfig {
pub source: Vid,
pub target: Vid,
}
impl Default for BidirectionalDijkstraConfig {
fn default() -> Self {
Self {
source: Vid::from(0),
target: Vid::from(0),
}
}
}
pub struct BidirectionalDijkstraResult {
pub distance: Option<f64>,
}
impl Algorithm for BidirectionalDijkstra {
type Config = BidirectionalDijkstraConfig;
type Result = BidirectionalDijkstraResult;
fn name() -> &'static str {
"bidirectional_dijkstra"
}
fn run(graph: &GraphProjection, config: Self::Config) -> Self::Result {
if !graph.has_reverse() {
panic!("Bidirectional Dijkstra requires graph projection with reverse edges");
}
let source_slot = match graph.to_slot(config.source) {
Some(s) => s,
None => return BidirectionalDijkstraResult { distance: None },
};
let target_slot = match graph.to_slot(config.target) {
Some(s) => s,
None => return BidirectionalDijkstraResult { distance: None },
};
if source_slot == target_slot {
return BidirectionalDijkstraResult {
distance: Some(0.0),
};
}
let n = graph.vertex_count();
let mut dist_fwd = vec![f64::INFINITY; n];
let mut dist_bwd = vec![f64::INFINITY; n];
let mut heap_fwd = BinaryHeap::new();
let mut heap_bwd = BinaryHeap::new();
dist_fwd[source_slot as usize] = 0.0;
heap_fwd.push(Reverse((0.0f64.to_bits(), source_slot)));
dist_bwd[target_slot as usize] = 0.0;
heap_bwd.push(Reverse((0.0f64.to_bits(), target_slot)));
let mut mu = f64::INFINITY;
while !heap_fwd.is_empty() && !heap_bwd.is_empty() {
let min_fwd = f64::from_bits(heap_fwd.peek().unwrap().0.0);
let min_bwd = f64::from_bits(heap_bwd.peek().unwrap().0.0);
if min_fwd + min_bwd >= mu {
break;
}
if heap_fwd.len() <= heap_bwd.len() {
let Reverse((d_bits, u)) = heap_fwd.pop().unwrap();
let d = f64::from_bits(d_bits);
if d > dist_fwd[u as usize] {
continue;
}
for (i, &v) in graph.out_neighbors(u).iter().enumerate() {
let weight = if graph.has_weights() {
graph.out_weight(u, i)
} else {
1.0
};
let new_dist = d + weight;
if new_dist < dist_fwd[v as usize] {
dist_fwd[v as usize] = new_dist;
heap_fwd.push(Reverse((new_dist.to_bits(), v)));
if dist_bwd[v as usize] < f64::INFINITY {
mu = mu.min(new_dist + dist_bwd[v as usize]);
}
}
}
} else {
let Reverse((d_bits, u)) = heap_bwd.pop().unwrap();
let d = f64::from_bits(d_bits);
if d > dist_bwd[u as usize] {
continue;
}
for &v in graph.in_neighbors(u) {
let weight = if graph.has_weights() {
let mut w = 1.0;
let neighbors = graph.out_neighbors(v);
for (idx, &n) in neighbors.iter().enumerate() {
if n == u {
w = graph.out_weight(v, idx);
break;
}
}
w
} else {
1.0
};
let new_dist = d + weight;
if new_dist < dist_bwd[v as usize] {
dist_bwd[v as usize] = new_dist;
heap_bwd.push(Reverse((new_dist.to_bits(), v)));
if dist_fwd[v as usize] < f64::INFINITY {
mu = mu.min(dist_fwd[v as usize] + new_dist);
}
}
}
}
}
BidirectionalDijkstraResult {
distance: if mu < f64::INFINITY { Some(mu) } else { None },
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::algo::{GraphProjection, IdMap};
fn build_graph_with_reverse(vids: Vec<Vid>, edges: Vec<(Vid, Vid)>) -> GraphProjection {
let mut id_map = IdMap::with_capacity(vids.len());
for vid in &vids {
id_map.insert(*vid);
}
let vertex_count = id_map.len();
let mut out_adj: Vec<Vec<u32>> = vec![Vec::new(); vertex_count];
let mut in_adj: Vec<Vec<u32>> = vec![Vec::new(); vertex_count];
for (src, dst) in &edges {
let u = id_map.to_slot(*src).unwrap();
let v = id_map.to_slot(*dst).unwrap();
out_adj[u as usize].push(v);
in_adj[v as usize].push(u);
}
let mut out_offsets = vec![0u32; vertex_count + 1];
let mut out_neighbors = Vec::new();
for i in 0..vertex_count {
out_offsets[i + 1] = out_offsets[i] + out_adj[i].len() as u32;
out_neighbors.extend_from_slice(&out_adj[i]);
}
let mut in_offsets = vec![0u32; vertex_count + 1];
let mut in_neighbors = Vec::new();
for i in 0..vertex_count {
in_offsets[i + 1] = in_offsets[i] + in_adj[i].len() as u32;
in_neighbors.extend_from_slice(&in_adj[i]);
}
GraphProjection {
vertex_count,
out_offsets,
out_neighbors,
in_offsets,
in_neighbors,
out_weights: None,
id_map,
_node_labels: Vec::new(),
_edge_types: Vec::new(),
}
}
#[test]
fn test_bi_dijkstra_simple() {
let vids = vec![Vid::from(0), Vid::from(1), Vid::from(2)];
let edges = vec![(Vid::from(0), Vid::from(1)), (Vid::from(1), Vid::from(2))];
let graph = build_graph_with_reverse(vids, edges);
let config = BidirectionalDijkstraConfig {
source: Vid::from(0),
target: Vid::from(2),
};
let result = BidirectionalDijkstra::run(&graph, config);
assert_eq!(result.distance, Some(2.0));
}
}