1use crate::algorithms::paths::dijkstra::{DijkstraMode, dijkstra_path_to_with_mode};
15use crate::core::graph::EdgeId;
16use crate::core::{Graph, IgraphError, IgraphResult, VertexId};
17
18const REMOVED_WEIGHT: f64 = f64::MAX / 4.0;
21
22#[derive(Debug, Clone)]
24pub struct KShortestPath {
25 pub vertices: Vec<VertexId>,
27 pub edges: Vec<EdgeId>,
29 pub weight: f64,
31}
32
33#[allow(clippy::too_many_lines)]
65pub fn k_shortest_paths(
66 graph: &Graph,
67 source: VertexId,
68 target: VertexId,
69 weights: &[f64],
70 k: usize,
71 mode: DijkstraMode,
72) -> IgraphResult<Vec<KShortestPath>> {
73 let n = graph.vcount();
74 let m = graph.ecount();
75
76 if source >= n {
77 return Err(IgraphError::VertexOutOfRange { id: source, n });
78 }
79 if target >= n {
80 return Err(IgraphError::VertexOutOfRange { id: target, n });
81 }
82 if weights.len() != m {
83 return Err(IgraphError::InvalidArgument(format!(
84 "k_shortest_paths: weights length ({}) != ecount ({m})",
85 weights.len()
86 )));
87 }
88 for (i, &w) in weights.iter().enumerate() {
89 if w < 0.0 {
90 return Err(IgraphError::InvalidArgument(format!(
91 "k_shortest_paths: negative weight ({w}) at edge {i}"
92 )));
93 }
94 }
95
96 if k == 0 {
97 return Ok(Vec::new());
98 }
99
100 let first = dijkstra_path_to_with_mode(graph, source, target, weights, mode)?;
102 let Some((init_vertices, init_edges)) = first else {
103 return Ok(Vec::new()); };
105
106 if has_infinite_edge(&init_edges, weights) {
107 return Ok(Vec::new());
108 }
109
110 let first_weight = path_weight(&init_edges, weights);
111 let mut result: Vec<KShortestPath> = Vec::with_capacity(k);
112 result.push(KShortestPath {
113 vertices: init_vertices,
114 edges: init_edges,
115 weight: first_weight,
116 });
117
118 if k == 1 {
119 return Ok(result);
120 }
121
122 let mut candidates: Vec<(Vec<EdgeId>, f64)> = Vec::new();
124
125 let mut cur_weights: Vec<f64> = weights.to_vec();
127
128 for i_path in 1..k {
129 let prev_edges = &result[i_path - 1].edges;
130 let prev_len = prev_edges.len();
131
132 for spur_idx in 0..prev_len {
133 let spur_vertex = edge_source_vertex(graph, prev_edges, spur_idx, mode)?;
135
136 let root_path = &prev_edges[..spur_idx];
138
139 let mut removed_edges: Vec<usize> = Vec::new();
141 for found in &result {
142 if found.edges.len() > spur_idx && found.edges[..spur_idx] == *root_path {
143 let eid = found.edges[spur_idx] as usize;
144 if cur_weights[eid] != REMOVED_WEIGHT {
145 cur_weights[eid] = REMOVED_WEIGHT;
146 removed_edges.push(eid);
147 }
148 }
149 }
150
151 for root_idx in 0..spur_idx {
153 let root_vertex = edge_source_vertex(graph, prev_edges, root_idx, mode)?;
154 semi_delete_vertex(
155 graph,
156 &mut cur_weights,
157 root_vertex,
158 &mut removed_edges,
159 mode,
160 )?;
161 }
162
163 let spur_result =
165 dijkstra_path_to_with_mode(graph, spur_vertex, target, &cur_weights, mode)?;
166
167 if let Some((_spur_vs, spur_es)) = spur_result {
168 if !has_removed_edge(&spur_es, &cur_weights) {
169 let mut total_edges: Vec<EdgeId> = root_path.to_vec();
171 total_edges.extend_from_slice(&spur_es);
172
173 let already_exists = candidates.iter().any(|(e, _)| *e == total_edges);
175 if !already_exists {
176 let w = path_weight(&total_edges, weights);
177 candidates.push((total_edges, w));
178 }
179 }
180 }
181
182 for &eid in &removed_edges {
184 cur_weights[eid] = weights[eid];
185 }
186 }
187
188 if candidates.is_empty() {
190 break;
191 }
192
193 let mut best_idx = 0;
194 let mut best_weight = candidates[0].1;
195 for (idx, &(_, w)) in candidates.iter().enumerate().skip(1) {
196 if w < best_weight {
197 best_weight = w;
198 best_idx = idx;
199 }
200 }
201
202 let (best_edges, best_w) = candidates.swap_remove(best_idx);
203 let best_vs = edge_path_to_vertices(graph, source, &best_edges, mode)?;
204 result.push(KShortestPath {
205 vertices: best_vs,
206 edges: best_edges,
207 weight: best_w,
208 });
209 }
210
211 Ok(result)
212}
213
214fn has_infinite_edge(edges: &[EdgeId], weights: &[f64]) -> bool {
215 edges.iter().any(|&e| weights[e as usize] == f64::INFINITY)
216}
217
218fn has_removed_edge(edges: &[EdgeId], weights: &[f64]) -> bool {
219 edges.iter().any(|&e| weights[e as usize] >= REMOVED_WEIGHT)
220}
221
222fn path_weight(edges: &[EdgeId], weights: &[f64]) -> f64 {
223 edges.iter().map(|&e| weights[e as usize]).sum()
224}
225
226fn edge_source_vertex(
227 graph: &Graph,
228 edge_path: &[EdgeId],
229 idx: usize,
230 mode: DijkstraMode,
231) -> IgraphResult<VertexId> {
232 let (from, to) = graph.edge(edge_path[idx])?;
233 match mode {
234 DijkstraMode::Out => Ok(from),
235 DijkstraMode::In => Ok(to),
236 DijkstraMode::All => {
237 if idx == 0 {
240 if edge_path.len() > 1 {
242 let (nf, nt) = graph.edge(edge_path[1])?;
243 if from == nf || from == nt {
244 Ok(to)
245 } else {
246 Ok(from)
247 }
248 } else {
249 Ok(from)
254 }
255 } else {
256 let (pf, pt) = graph.edge(edge_path[idx - 1])?;
257 if from == pf || from == pt {
260 Ok(from)
261 } else {
262 Ok(to)
263 }
264 }
265 }
266 }
267}
268
269fn semi_delete_vertex(
270 graph: &Graph,
271 cur_weights: &mut [f64],
272 vertex: VertexId,
273 removed_edges: &mut Vec<usize>,
274 mode: DijkstraMode,
275) -> IgraphResult<()> {
276 let incident = incident_for_mode_yen(graph, vertex, mode)?;
277 for eid in incident {
278 let idx = eid as usize;
279 if cur_weights[idx] != REMOVED_WEIGHT {
280 cur_weights[idx] = REMOVED_WEIGHT;
281 removed_edges.push(idx);
282 }
283 }
284 Ok(())
285}
286
287fn incident_for_mode_yen(
288 graph: &Graph,
289 v: VertexId,
290 mode: DijkstraMode,
291) -> IgraphResult<Vec<EdgeId>> {
292 if !graph.is_directed() || mode == DijkstraMode::All {
293 let mut out = graph.incident(v)?;
294 if graph.is_directed() {
295 out.extend(graph.incident_in(v)?);
296 }
297 return Ok(out);
298 }
299 match mode {
300 DijkstraMode::Out => graph.incident(v),
301 DijkstraMode::In => graph.incident_in(v),
302 DijkstraMode::All => unreachable!(),
303 }
304}
305
306fn edge_path_to_vertices(
307 graph: &Graph,
308 source: VertexId,
309 edge_path: &[EdgeId],
310 mode: DijkstraMode,
311) -> IgraphResult<Vec<VertexId>> {
312 if edge_path.is_empty() {
313 return Ok(vec![source]);
314 }
315
316 let mut vertices = Vec::with_capacity(edge_path.len() + 1);
317 vertices.push(source);
318
319 let mut cur = source;
320 for &eid in edge_path {
321 let (from, to) = graph.edge(eid)?;
322 let next = match mode {
323 DijkstraMode::Out => to,
324 DijkstraMode::In => from,
325 DijkstraMode::All => {
326 if from == cur {
327 to
328 } else {
329 from
330 }
331 }
332 };
333 vertices.push(next);
334 cur = next;
335 }
336
337 Ok(vertices)
338}
339
340#[cfg(test)]
341mod tests {
342 use super::*;
343
344 #[test]
345 fn no_path() {
346 let g = Graph::with_vertices(3);
347 let w = vec![]; let paths = k_shortest_paths(&g, 0, 2, &w, 5, DijkstraMode::All).unwrap();
349 assert!(paths.is_empty());
350 }
351
352 #[test]
353 fn k_zero() {
354 let mut g = Graph::with_vertices(2);
355 g.add_edge(0, 1).unwrap();
356 let paths = k_shortest_paths(&g, 0, 1, &[1.0], 0, DijkstraMode::All).unwrap();
357 assert!(paths.is_empty());
358 }
359
360 #[test]
361 fn single_edge() {
362 let mut g = Graph::with_vertices(2);
363 g.add_edge(0, 1).unwrap();
364 let paths = k_shortest_paths(&g, 0, 1, &[3.0], 5, DijkstraMode::All).unwrap();
365 assert_eq!(paths.len(), 1);
366 assert_eq!(paths[0].vertices, vec![0, 1]);
367 assert_eq!(paths[0].edges, vec![0]);
368 assert!((paths[0].weight - 3.0).abs() < 1e-12);
369 }
370
371 #[test]
372 fn diamond_two_paths() {
373 let mut g = Graph::with_vertices(4);
375 g.add_edge(0, 1).unwrap(); g.add_edge(1, 3).unwrap(); g.add_edge(0, 2).unwrap(); g.add_edge(2, 3).unwrap(); let w = vec![1.0; 4];
380 let paths = k_shortest_paths(&g, 0, 3, &w, 5, DijkstraMode::All).unwrap();
381 assert_eq!(paths.len(), 2);
382 assert!((paths[0].weight - 2.0).abs() < 1e-12);
383 assert!((paths[1].weight - 2.0).abs() < 1e-12);
384 }
385
386 #[test]
387 fn diamond_different_weights() {
388 let mut g = Graph::with_vertices(4);
390 g.add_edge(0, 1).unwrap(); g.add_edge(1, 3).unwrap(); g.add_edge(0, 2).unwrap(); g.add_edge(2, 3).unwrap(); let w = vec![2.0, 1.0, 1.0, 1.0];
395 let paths = k_shortest_paths(&g, 0, 3, &w, 2, DijkstraMode::All).unwrap();
396 assert_eq!(paths.len(), 2);
397 assert!((paths[0].weight - 2.0).abs() < 1e-12); assert!((paths[1].weight - 3.0).abs() < 1e-12); }
400
401 #[test]
402 fn path_graph_single_path() {
403 let mut g = Graph::with_vertices(4);
405 g.add_edge(0, 1).unwrap();
406 g.add_edge(1, 2).unwrap();
407 g.add_edge(2, 3).unwrap();
408 let w = vec![1.0; 3];
409 let paths = k_shortest_paths(&g, 0, 3, &w, 3, DijkstraMode::All).unwrap();
410 assert_eq!(paths.len(), 1);
411 assert_eq!(paths[0].vertices, vec![0, 1, 2, 3]);
412 }
413
414 #[test]
415 fn three_paths() {
416 let mut g = Graph::with_vertices(4);
418 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(0, 2).unwrap(); g.add_edge(1, 3).unwrap(); g.add_edge(3, 2).unwrap(); let w = vec![1.0, 1.0, 3.0, 1.0, 1.0];
424 let paths = k_shortest_paths(&g, 0, 2, &w, 5, DijkstraMode::All).unwrap();
425 assert!(paths.len() >= 3);
426 assert!((paths[0].weight - 2.0).abs() < 1e-12);
428 assert!((paths[1].weight - 3.0).abs() < 1e-12);
430 assert!((paths[2].weight - 3.0).abs() < 1e-12);
431 }
432
433 #[test]
434 fn source_equals_target() {
435 let mut g = Graph::with_vertices(3);
436 g.add_edge(0, 1).unwrap();
437 g.add_edge(1, 2).unwrap();
438 let w = vec![1.0; 2];
439 let paths = k_shortest_paths(&g, 0, 0, &w, 1, DijkstraMode::All).unwrap();
440 assert_eq!(paths.len(), 1);
441 assert_eq!(paths[0].vertices, vec![0]);
442 assert!(paths[0].edges.is_empty());
443 assert!((paths[0].weight - 0.0).abs() < 1e-12);
444 }
445
446 #[test]
447 fn directed_out_mode() {
448 let mut g = Graph::new(3, true).unwrap();
450 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(0, 2).unwrap(); let w = vec![1.0, 1.0, 5.0];
454 let paths = k_shortest_paths(&g, 0, 2, &w, 3, DijkstraMode::Out).unwrap();
455 assert_eq!(paths.len(), 2);
456 assert!((paths[0].weight - 2.0).abs() < 1e-12); assert!((paths[1].weight - 5.0).abs() < 1e-12); }
459
460 #[test]
461 fn invalid_source() {
462 let g = Graph::with_vertices(3);
463 assert!(k_shortest_paths(&g, 99, 0, &[], 1, DijkstraMode::All).is_err());
464 }
465
466 #[test]
467 fn negative_weight_error() {
468 let mut g = Graph::with_vertices(2);
469 g.add_edge(0, 1).unwrap();
470 assert!(k_shortest_paths(&g, 0, 1, &[-1.0], 1, DijkstraMode::All).is_err());
471 }
472}