sssp_lib/
graph.rs

1use std::collections::{BinaryHeap, HashMap};
2use std::cmp::Ordering;
3
4/// Represents an edge in the graph.
5///
6/// An edge has a target node and a weight.
7#[derive(Debug, Clone, PartialEq)]
8pub struct Edge {
9    /// The target node index.
10    pub to: usize,
11    /// The weight of the edge.
12    pub weight: f64,
13}
14
15#[derive(Copy, Clone, PartialEq)]
16struct State {
17    node: usize,
18    dist: f64,
19}
20
21impl Eq for State {}
22impl Ord for State {
23    fn cmp(&self, other: &Self) -> Ordering {
24        // Min-heap: sort by increasing distance
25        other.dist.partial_cmp(&self.dist).unwrap_or(Ordering::Equal)
26    }
27}
28impl PartialOrd for State {
29    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
30        Some(self.cmp(other))
31    }
32}
33
34/// Represents a graph for SSSP algorithms.
35///
36/// The graph is stored as an adjacency list.
37pub struct SSSPGraph {
38    /// Adjacency list: adj\[u\] contains edges from node u.
39    pub adj: Vec<Vec<Edge>>,
40    /// Number of nodes in the graph.
41    pub n: usize,
42}
43
44impl SSSPGraph {
45    /// Creates a new empty graph with `n` nodes.
46    ///
47    /// # Arguments
48    ///
49    /// * `n` - The number of nodes.
50    ///
51    /// # Examples
52    ///
53    /// ```
54    /// use sssp_lib::graph::SSSPGraph;
55    ///
56    /// let graph = SSSPGraph::new(5);
57    /// ```
58    pub fn new(n: usize) -> Self {
59        Self {
60            adj: vec![vec![]; n],
61            n,
62        }
63    }
64
65    /// Adds a directed edge from node `u` to node `v` with weight `w`.
66    ///
67    /// If `u` or `v` is out of bounds, the edge is ignored.
68    ///
69    /// # Arguments
70    ///
71    /// * `u` - The source node index.
72    /// * `v` - The target node index.
73    /// * `w` - The edge weight.
74    ///
75    /// # Examples
76    ///
77    /// ```
78    /// use sssp_lib::graph::SSSPGraph;
79    ///
80    /// let mut graph = SSSPGraph::new(3);
81    /// graph.add_edge(0, 1, 1.0);
82    /// ```
83    pub fn add_edge(&mut self, u: usize, v: usize, w: f64) {
84        if u < self.n && v < self.n {
85            self.adj[u].push(Edge { to: v, weight: w });
86        }
87    }
88
89    /// Runs the Bounded Memory Shortest Path (BMSSP) algorithm.
90    ///
91    /// This is an internal method used by the SSSP algorithm with bounds on distance and work.
92    ///
93    /// # Arguments
94    ///
95    /// * `sources` - A vector of (node, initial_distance) pairs.
96    /// * `b_bound` - The maximum distance bound.
97    /// * `u_bound` - The maximum work bound (number of relaxations).
98    ///
99    /// # Returns
100    ///
101    /// A tuple of (distances, parents) HashMaps.
102    pub fn bmssp(
103        &self,
104        sources: Vec<(usize, f64)>,
105        b_bound: f64,
106        u_bound: usize,
107    ) -> (HashMap<usize, f64>, HashMap<usize, usize>) {
108    let mut dists = HashMap::new();
109    let mut parents = HashMap::new(); // Store who relaxed whom
110    let mut heap = BinaryHeap::new();
111    let mut work_done = 0;
112
113    for (node, d) in sources {
114        dists.insert(node, d);
115        heap.push(State { node, dist: d });
116    }
117
118    while let Some(State { node, dist }) = heap.pop() {
119        if dist > *dists.get(&node).unwrap_or(&f64::INFINITY) { continue; }
120        if work_done >= u_bound { break; }
121
122        for edge in &self.adj[node] {
123            let new_dist = dist + edge.weight;
124            if new_dist <= b_bound {
125                let current_dist = dists.get(&edge.to).cloned().unwrap_or(f64::INFINITY);
126                if new_dist < current_dist {
127                    dists.insert(edge.to, new_dist);
128                    parents.insert(edge.to, node); // <--- Store the parent
129                    heap.push(State { node: edge.to, dist: new_dist });
130                    work_done += 1;
131                }
132            }
133        }
134    }
135    (dists, parents)
136}}