rust_igraph/algorithms/paths/
get_shortest_paths_dijkstra.rs1use crate::core::graph::EdgeId;
10use crate::core::{Graph, IgraphResult, VertexId};
11
12use super::dijkstra::{DijkstraMode, DijkstraPaths, dijkstra_paths, dijkstra_paths_with_mode};
13
14#[derive(Debug, Clone, PartialEq)]
17pub struct ShortestPathsDijkstra {
18 pub vertex_paths: Vec<Vec<VertexId>>,
22 pub edge_paths: Vec<Vec<EdgeId>>,
25}
26
27pub fn get_shortest_paths_dijkstra(
55 graph: &Graph,
56 source: VertexId,
57 weights: &[f64],
58) -> IgraphResult<ShortestPathsDijkstra> {
59 let dp = dijkstra_paths(graph, source, weights)?;
60 Ok(build_all_paths(graph, source, &dp))
61}
62
63pub fn get_shortest_paths_dijkstra_with_mode(
82 graph: &Graph,
83 source: VertexId,
84 weights: &[f64],
85 mode: DijkstraMode,
86) -> IgraphResult<ShortestPathsDijkstra> {
87 let dp = dijkstra_paths_with_mode(graph, source, weights, mode)?;
88 Ok(build_all_paths(graph, source, &dp))
89}
90
91fn build_all_paths(graph: &Graph, source: VertexId, dp: &DijkstraPaths) -> ShortestPathsDijkstra {
92 let n = graph.vcount() as usize;
93 let mut vertex_paths: Vec<Vec<VertexId>> = Vec::with_capacity(n);
94 let mut edge_paths: Vec<Vec<EdgeId>> = Vec::with_capacity(n);
95
96 for v in 0..n {
97 #[allow(clippy::cast_possible_truncation)]
98 let v_id = v as u32;
99 if dp.distances[v].is_none() {
100 vertex_paths.push(Vec::new());
101 edge_paths.push(Vec::new());
102 continue;
103 }
104 if v_id == source {
105 vertex_paths.push(vec![source]);
106 edge_paths.push(Vec::new());
107 continue;
108 }
109 let mut vs = Vec::new();
110 let mut es = Vec::new();
111 let mut cur = v_id;
112 while cur != source {
113 vs.push(cur);
114 if let Some(eid) = dp.inbound_edges[cur as usize] {
115 es.push(eid);
116 }
117 match dp.parents.get(cur as usize).copied().flatten() {
118 Some(p) => cur = p,
119 None => break,
120 }
121 }
122 vs.push(source);
123 vs.reverse();
124 es.reverse();
125 vertex_paths.push(vs);
126 edge_paths.push(es);
127 }
128
129 ShortestPathsDijkstra {
130 vertex_paths,
131 edge_paths,
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn singleton() {
141 let g = Graph::with_vertices(1);
142 let r = get_shortest_paths_dijkstra(&g, 0, &[]).unwrap();
143 assert_eq!(r.vertex_paths, vec![vec![0]]);
144 assert_eq!(r.edge_paths, vec![vec![]]);
145 }
146
147 #[test]
148 fn path_graph_unit_weights() {
149 let mut g = Graph::with_vertices(4);
150 g.add_edge(0, 1).unwrap();
151 g.add_edge(1, 2).unwrap();
152 g.add_edge(2, 3).unwrap();
153 let r = get_shortest_paths_dijkstra(&g, 0, &[1.0, 1.0, 1.0]).unwrap();
154 assert_eq!(r.vertex_paths[0], vec![0]);
155 assert_eq!(r.vertex_paths[1], vec![0, 1]);
156 assert_eq!(r.vertex_paths[2], vec![0, 1, 2]);
157 assert_eq!(r.vertex_paths[3], vec![0, 1, 2, 3]);
158 assert_eq!(r.edge_paths[0], vec![]);
159 assert_eq!(r.edge_paths[1], vec![0]);
160 assert_eq!(r.edge_paths[2], vec![0, 1]);
161 assert_eq!(r.edge_paths[3], vec![0, 1, 2]);
162 }
163
164 #[test]
165 fn shortcut_with_high_weight() {
166 let mut g = Graph::with_vertices(4);
167 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(2, 3).unwrap(); g.add_edge(0, 3).unwrap(); let r = get_shortest_paths_dijkstra(&g, 0, &[1.0, 1.0, 1.0, 10.0]).unwrap();
172 assert_eq!(r.vertex_paths[3], vec![0, 1, 2, 3]);
173 assert_eq!(r.edge_paths[3], vec![0, 1, 2]);
174 }
175
176 #[test]
177 fn shortcut_with_low_weight() {
178 let mut g = Graph::with_vertices(4);
179 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(2, 3).unwrap(); g.add_edge(0, 3).unwrap(); let r = get_shortest_paths_dijkstra(&g, 0, &[5.0, 5.0, 5.0, 1.0]).unwrap();
184 assert_eq!(r.vertex_paths[3], vec![0, 3]);
185 assert_eq!(r.edge_paths[3], vec![3]);
186 }
187
188 #[test]
189 fn disconnected_graph() {
190 let mut g = Graph::with_vertices(4);
191 g.add_edge(0, 1).unwrap();
192 g.add_edge(2, 3).unwrap();
193 let r = get_shortest_paths_dijkstra(&g, 0, &[1.0, 1.0]).unwrap();
194 assert_eq!(r.vertex_paths[0], vec![0]);
195 assert_eq!(r.vertex_paths[1], vec![0, 1]);
196 assert!(r.vertex_paths[2].is_empty());
197 assert!(r.vertex_paths[3].is_empty());
198 assert!(r.edge_paths[2].is_empty());
199 assert!(r.edge_paths[3].is_empty());
200 }
201
202 #[test]
203 fn source_out_of_range() {
204 let g = Graph::with_vertices(3);
205 assert!(get_shortest_paths_dijkstra(&g, 5, &[]).is_err());
206 }
207
208 #[test]
209 fn directed_out() {
210 let mut g = Graph::new(3, true).unwrap();
211 g.add_edge(0, 1).unwrap();
212 g.add_edge(1, 2).unwrap();
213 let r =
214 get_shortest_paths_dijkstra_with_mode(&g, 0, &[1.0, 1.0], DijkstraMode::Out).unwrap();
215 assert_eq!(r.vertex_paths[2], vec![0, 1, 2]);
216 assert_eq!(r.edge_paths[2], vec![0, 1]);
217 }
218
219 #[test]
220 fn directed_in() {
221 let mut g = Graph::new(3, true).unwrap();
222 g.add_edge(0, 1).unwrap();
223 g.add_edge(1, 2).unwrap();
224 let r =
225 get_shortest_paths_dijkstra_with_mode(&g, 2, &[1.0, 1.0], DijkstraMode::In).unwrap();
226 assert_eq!(r.vertex_paths[0], vec![2, 1, 0]);
227 assert_eq!(r.edge_paths[0], vec![1, 0]);
228 }
229
230 #[test]
231 fn directed_unreachable() {
232 let mut g = Graph::new(3, true).unwrap();
233 g.add_edge(0, 1).unwrap();
234 g.add_edge(1, 2).unwrap();
235 let r =
236 get_shortest_paths_dijkstra_with_mode(&g, 2, &[1.0, 1.0], DijkstraMode::Out).unwrap();
237 assert_eq!(r.vertex_paths[2], vec![2]);
238 assert!(r.vertex_paths[0].is_empty());
239 assert!(r.vertex_paths[1].is_empty());
240 }
241
242 #[test]
243 fn triangle_nonuniform_weights() {
244 let mut g = Graph::with_vertices(3);
245 g.add_edge(0, 1).unwrap(); g.add_edge(0, 2).unwrap(); g.add_edge(1, 2).unwrap(); let r = get_shortest_paths_dijkstra(&g, 0, &[1.0, 4.0, 2.0]).unwrap();
249 assert_eq!(r.vertex_paths[2], vec![0, 1, 2]);
251 assert_eq!(r.edge_paths[2], vec![0, 2]);
252 }
253
254 #[test]
255 fn edge_path_lengths_consistent() {
256 let mut g = Graph::with_vertices(5);
257 g.add_edge(0, 1).unwrap();
258 g.add_edge(1, 2).unwrap();
259 g.add_edge(2, 3).unwrap();
260 g.add_edge(3, 4).unwrap();
261 let r = get_shortest_paths_dijkstra(&g, 0, &[1.0; 4]).unwrap();
262 for v in 0..5 {
263 if r.vertex_paths[v].is_empty() {
264 assert!(r.edge_paths[v].is_empty());
265 } else {
266 assert_eq!(
267 r.edge_paths[v].len() + 1,
268 r.vertex_paths[v].len(),
269 "edge count should be vertex count - 1 for vertex {v}"
270 );
271 }
272 }
273 }
274}