1use crate::error::GraphError;
5use crate::graph::Graph;
6use core::cmp::Reverse;
7use sim_lib_discrete_algebra::{AlgebraLimits, BoolRing, Matrix, MinPlus};
8use std::collections::BinaryHeap;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct PathResult<W> {
13 pub distances: Vec<Option<W>>,
15 pub predecessors: Vec<Option<usize>>,
17}
18
19fn out_arcs<N, W: Clone>(graph: &Graph<N, W>, node: usize) -> Vec<(usize, W)> {
21 let undirected = !graph.is_directed();
22 let mut arcs = Vec::new();
23 for e in &graph.edges {
24 if e.source == node {
25 arcs.push((e.target, e.weight.clone()));
26 } else if undirected && e.target == node {
27 arcs.push((e.source, e.weight.clone()));
28 }
29 }
30 arcs
31}
32
33pub fn dijkstra<N>(graph: &Graph<N, u64>, source: usize) -> Result<PathResult<u64>, GraphError> {
53 graph.validate()?;
54 let n = graph.node_count();
55 if source >= n {
56 return Err(GraphError::NodeOutOfRange {
57 node: source,
58 count: n,
59 });
60 }
61 let mut dist = vec![None; n];
62 let mut pred = vec![None; n];
63 let mut heap: BinaryHeap<Reverse<(u64, usize)>> = BinaryHeap::new();
64 dist[source] = Some(0);
65 heap.push(Reverse((0, source)));
66 while let Some(Reverse((d, u))) = heap.pop() {
67 if dist[u].is_some_and(|best| d > best) {
68 continue;
69 }
70 for (v, w) in out_arcs(graph, u) {
71 let nd = d + w;
72 if dist[v].is_none_or(|best| nd < best) {
73 dist[v] = Some(nd);
74 pred[v] = Some(u);
75 heap.push(Reverse((nd, v)));
76 }
77 }
78 }
79 Ok(PathResult {
80 distances: dist,
81 predecessors: pred,
82 })
83}
84
85pub fn bellman_ford<N>(
88 graph: &Graph<N, i64>,
89 source: usize,
90) -> Result<(PathResult<i64>, bool), GraphError> {
91 graph.validate()?;
92 let n = graph.node_count();
93 if source >= n {
94 return Err(GraphError::NodeOutOfRange {
95 node: source,
96 count: n,
97 });
98 }
99 let undirected = !graph.is_directed();
100 let mut arcs: Vec<(usize, usize, i64)> = Vec::new();
102 for e in &graph.edges {
103 arcs.push((e.source, e.target, e.weight));
104 if undirected {
105 arcs.push((e.target, e.source, e.weight));
106 }
107 }
108 let mut dist: Vec<Option<i64>> = vec![None; n];
109 let mut pred = vec![None; n];
110 dist[source] = Some(0);
111 for _ in 0..n.saturating_sub(1) {
112 let mut changed = false;
113 for &(a, b, w) in &arcs {
114 if let Some(da) = dist[a] {
115 let nd = da + w;
116 if dist[b].is_none_or(|best| nd < best) {
117 dist[b] = Some(nd);
118 pred[b] = Some(a);
119 changed = true;
120 }
121 }
122 }
123 if !changed {
124 break;
125 }
126 }
127 let mut negative_cycle = false;
128 for &(a, b, w) in &arcs {
129 if let Some(da) = dist[a]
130 && dist[b].is_none_or(|best| da + w < best)
131 {
132 negative_cycle = true;
133 break;
134 }
135 }
136 Ok((
137 PathResult {
138 distances: dist,
139 predecessors: pred,
140 },
141 negative_cycle,
142 ))
143}
144
145pub fn all_pairs_shortest_paths<N>(graph: &Graph<N, i64>) -> Result<Matrix<MinPlus>, GraphError> {
148 graph.validate()?;
149 let n = graph.node_count();
150 let undirected = !graph.is_directed();
151 let mut m = Matrix::filled(n, n, MinPlus::Inf);
152 for e in &graph.edges {
153 m.data[e.source * n + e.target] = min_plus_add(m.data[e.source * n + e.target], e.weight);
155 if undirected {
156 m.data[e.target * n + e.source] =
157 min_plus_add(m.data[e.target * n + e.source], e.weight);
158 }
159 }
160 Ok(m.closure(AlgebraLimits::default())?)
161}
162
163fn min_plus_add(cur: MinPlus, w: i64) -> MinPlus {
164 use sim_lib_discrete_algebra::Semiring;
165 cur.add(&MinPlus::Fin(w))
166}
167
168pub fn reachability<N, W>(graph: &Graph<N, W>) -> Result<Matrix<BoolRing>, GraphError> {
170 graph.validate()?;
171 let n = graph.node_count();
172 let undirected = !graph.is_directed();
173 let mut m = Matrix::filled(n, n, BoolRing(false));
174 for e in &graph.edges {
175 m.data[e.source * n + e.target] = BoolRing(true);
176 if undirected {
177 m.data[e.target * n + e.source] = BoolRing(true);
178 }
179 }
180 Ok(m.closure(AlgebraLimits::default())?)
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186 use crate::edge::Directedness;
187
188 #[test]
189 fn dijkstra_row_equals_all_pairs_row() {
190 let edges = [(0usize, 1usize, 1u64), (1, 2, 2), (0, 2, 5), (2, 3, 1)];
192 let mut gu: Graph<u8, u64> = Graph::with_nodes(vec![0, 1, 2, 3], Directedness::Directed);
193 let mut gi: Graph<u8, i64> = Graph::with_nodes(vec![0, 1, 2, 3], Directedness::Directed);
194 for &(s, t, w) in &edges {
195 gu.add_edge(s, t, w).unwrap();
196 gi.add_edge(s, t, w as i64).unwrap();
197 }
198 let dj = dijkstra(&gu, 0).unwrap();
199 let ap = all_pairs_shortest_paths(&gi).unwrap();
200 for j in 0..4 {
201 let from_closure = match ap.data[j] {
202 MinPlus::Fin(d) => Some(d as u64),
203 MinPlus::Inf => None,
204 };
205 assert_eq!(dj.distances[j], from_closure, "node {j}");
206 }
207 }
208
209 #[test]
210 fn bellman_ford_handles_negative_edge_without_cycle() {
211 let mut g: Graph<u8, i64> = Graph::with_nodes(vec![0, 1, 2], Directedness::Directed);
212 g.add_edge(0, 1, 4).unwrap();
213 g.add_edge(0, 2, 5).unwrap();
214 g.add_edge(2, 1, -3).unwrap(); let (res, neg) = bellman_ford(&g, 0).unwrap();
216 assert!(!neg);
217 assert_eq!(res.distances[1], Some(2));
218 }
219
220 #[test]
221 fn bellman_ford_detects_negative_cycle() {
222 let mut g: Graph<u8, i64> = Graph::with_nodes(vec![0, 1], Directedness::Directed);
223 g.add_edge(0, 1, 1).unwrap();
224 g.add_edge(1, 0, -2).unwrap(); let (_res, neg) = bellman_ford(&g, 0).unwrap();
226 assert!(neg);
227 }
228
229 #[test]
230 fn reachability_is_transitive() {
231 let mut g: Graph<u8, u64> = Graph::with_nodes(vec![0, 1, 2], Directedness::Directed);
232 g.add_edge(0, 1, 1).unwrap();
233 g.add_edge(1, 2, 1).unwrap();
234 let r = reachability(&g).unwrap();
235 assert_eq!(r.data[2], BoolRing(true)); assert_eq!(r.data[6], BoolRing(false)); }
238}