use crate::algo::GraphProjection;
use crate::algo::algorithms::{Algorithm, bfs_levels, dijkstra_distances};
use rayon::prelude::*;
use uni_common::core::id::Vid;
pub struct AllPairsShortestPath;
#[derive(Debug, Clone, Default)]
pub struct AllPairsShortestPathConfig;
pub struct AllPairsShortestPathResult {
pub distances: Vec<(Vid, Vid, f64)>,
}
impl Algorithm for AllPairsShortestPath {
type Config = AllPairsShortestPathConfig;
type Result = AllPairsShortestPathResult;
fn name() -> &'static str {
"allPairsShortestPath"
}
fn run(graph: &GraphProjection, _config: Self::Config) -> Self::Result {
let n = graph.vertex_count();
if n == 0 {
return AllPairsShortestPathResult {
distances: Vec::new(),
};
}
let weighted = graph.has_weights();
let distances = (0..n as u32)
.into_par_iter()
.flat_map_iter(|s| {
let src_vid = graph.to_vid(s);
let row: Vec<(Vid, Vid, f64)> = if weighted {
dijkstra_distances(graph, s)
.into_iter()
.enumerate()
.filter(|&(_, dist)| dist.is_finite() && dist > 0.0)
.map(|(tgt, dist)| (src_vid, graph.to_vid(tgt as u32), dist))
.collect()
} else {
bfs_levels(graph, s)
.into_iter()
.enumerate()
.filter(|&(_, dist)| dist > 0)
.map(|(tgt, dist)| (src_vid, graph.to_vid(tgt as u32), dist as f64))
.collect()
};
row.into_iter()
})
.collect();
AllPairsShortestPathResult { distances }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::algo::test_utils::build_test_graph;
use std::collections::HashMap;
#[test]
fn weighted_apsp_uses_distance_not_hops() {
let vids = vec![Vid::from(0), Vid::from(1), Vid::from(2)];
let edges = vec![
(Vid::from(0), Vid::from(1)),
(Vid::from(0), Vid::from(2)),
(Vid::from(2), Vid::from(1)),
];
let mut graph = build_test_graph(vids, edges);
graph.out_weights = Some(vec![10.0, 1.0, 1.0]);
let result = AllPairsShortestPath::run(&graph, AllPairsShortestPathConfig);
let dist: HashMap<(u64, u64), f64> = result
.distances
.iter()
.map(|(s, t, d)| ((s.as_u64(), t.as_u64()), *d))
.collect();
assert_eq!(
dist.get(&(0, 1)),
Some(&2.0),
"weighted dist(0,1) should be 2.0 (0->2->1), not the hop count 1"
);
assert_eq!(dist.get(&(0, 2)), Some(&1.0));
assert_eq!(dist.get(&(2, 1)), Some(&1.0));
}
#[test]
fn unweighted_apsp_returns_hop_counts() {
let vids = vec![Vid::from(0), Vid::from(1), Vid::from(2)];
let edges = vec![
(Vid::from(0), Vid::from(1)),
(Vid::from(0), Vid::from(2)),
(Vid::from(2), Vid::from(1)),
];
let graph = build_test_graph(vids, edges);
let result = AllPairsShortestPath::run(&graph, AllPairsShortestPathConfig);
let dist: HashMap<(u64, u64), f64> = result
.distances
.iter()
.map(|(s, t, d)| ((s.as_u64(), t.as_u64()), *d))
.collect();
assert_eq!(dist.get(&(0, 1)), Some(&1.0));
assert_eq!(dist.get(&(0, 2)), Some(&1.0));
}
}