1pub mod dijkstra;
5
6pub mod error;
8
9pub mod neg_cycle;
11
12pub mod parametric;
14
15pub mod utils;
17
18pub use error::NetOptimError;
19pub use utils::*;
20
21#[cfg(test)]
22mod integration_tests;
23
24#[cfg(feature = "std")]
26pub mod logging;
27
28use petgraph::prelude::*;
29
30use petgraph::algo::{FloatMeasure, NegativeCycle};
31use petgraph::visit::{
32 IntoEdges, IntoNodeIdentifiers, NodeCount, NodeIndexable, VisitMap, Visitable,
33};
34
35#[derive(Debug, Clone)]
40pub struct Paths<NodeId, EdgeWeight> {
41 pub distances: Vec<EdgeWeight>,
42 pub predecessors: Vec<Option<NodeId>>,
43}
44
45pub fn bellman_ford<G>(
112 g: G,
113 source: G::NodeId,
114) -> Result<Paths<G::NodeId, G::EdgeWeight>, NegativeCycle>
115where
116 G: NodeCount + IntoNodeIdentifiers + IntoEdges + NodeIndexable,
117 G::EdgeWeight: FloatMeasure,
118{
119 let ix = |i| g.to_index(i);
120
121 let (distances, predecessors) = bellman_ford_initialize_relax(g, source);
123
124 for i in g.node_identifiers() {
126 for edge in g.edges(i) {
127 let j = edge.target();
128 let w = *edge.weight();
129 if distances[ix(i)] + w < distances[ix(j)] {
130 return Err(NegativeCycle(()));
131 }
132 }
133 }
134
135 Ok(Paths {
136 distances,
137 predecessors,
138 })
139}
140
141pub fn find_negative_cycle<G>(g: G, source: G::NodeId) -> Option<Vec<G::NodeId>>
189where
190 G: NodeCount + IntoNodeIdentifiers + IntoEdges + NodeIndexable + Visitable,
191 G::EdgeWeight: FloatMeasure,
192{
193 let ix = |i| g.to_index(i);
194 let mut path = Vec::<G::NodeId>::new();
195
196 let (distance, predecessor) = bellman_ford_initialize_relax(g, source);
198
199 'outer: for i in g.node_identifiers() {
201 for edge in g.edges(i) {
202 let j = edge.target();
203 let w = *edge.weight();
204 if distance[ix(i)] + w < distance[ix(j)] {
205 let start = j;
207 let mut node = start;
208 let mut visited = g.visit_map();
209 loop {
211 let ancestor = match predecessor[ix(node)] {
212 Some(predecessor_node) => predecessor_node,
213 None => node, };
215 if ancestor == start {
218 path.push(ancestor);
219 break;
220 }
221 else if visited.is_visited(&ancestor) {
223 let pos = path
225 .iter()
226 .position(|&p| p == ancestor)
227 .expect("we should always have a position");
228 path = path[pos..path.len()].to_vec();
229
230 break;
231 }
232
233 path.push(ancestor);
235 visited.visit(ancestor);
236 node = ancestor;
237 }
238 break 'outer;
240 }
241 }
242 }
243 if !path.is_empty() {
244 path.reverse();
247 Some(path)
248 } else {
249 None
250 }
251}
252
253#[inline(always)]
255fn bellman_ford_initialize_relax<G>(
256 g: G,
257 source: G::NodeId,
258) -> (Vec<G::EdgeWeight>, Vec<Option<G::NodeId>>)
259where
260 G: NodeCount + IntoNodeIdentifiers + IntoEdges + NodeIndexable,
261 G::EdgeWeight: FloatMeasure,
262{
263 let mut predecessor = vec![None; g.node_bound()];
265 let mut distance = vec![<_>::infinite(); g.node_bound()];
266 let ix = |i| g.to_index(i);
267 distance[ix(source)] = <_>::zero();
268
269 for _ in 1..g.node_count() {
271 let mut did_update = false;
272 for i in g.node_identifiers() {
273 for edge in g.edges(i) {
274 let j = edge.target();
275 let w = *edge.weight();
276 if distance[ix(i)] + w < distance[ix(j)] {
277 distance[ix(j)] = distance[ix(i)] + w;
278 predecessor[ix(j)] = Some(i);
279 did_update = true;
280 }
281 }
282 }
283 if !did_update {
284 break;
285 }
286 }
287 (distance, predecessor)
288}
289
290#[cfg(test)]
291mod tests {
292 use super::*;
293 use petgraph::Graph;
294
295 #[test]
296 fn test_bellman_ford_negative_cycle() {
297 let graph_with_neg_cycle =
298 Graph::<(), f32, Directed>::from_edges([(0, 1, 1.0), (1, 2, 1.0), (2, 0, -3.0)]);
299 let result = bellman_ford(&graph_with_neg_cycle, NodeIndex::new(0));
300 assert!(result.is_err());
301 }
302
303 #[test]
304 fn test_bellman_ford_no_edges() {
305 let mut graph = Graph::<(), f32, Directed>::new();
306 let n0 = graph.add_node(());
307 let result = bellman_ford(&graph, n0);
308 assert!(result.is_ok());
309 let paths = result.unwrap();
310 assert_eq!(paths.distances, vec![0.0]);
311 assert_eq!(paths.predecessors, vec![None]);
312 }
313
314 #[test]
315 fn test_bellman_ford_disconnected_components() {
316 let mut graph = Graph::<(), f32, Directed>::new();
317 let n0 = graph.add_node(());
318 let n1 = graph.add_node(());
319 let n2 = graph.add_node(());
320 graph.add_edge(n0, n1, 1.0);
321
322 let result = bellman_ford(&graph, n0);
323 assert!(result.is_ok());
324 let paths = result.unwrap();
325 assert_eq!(paths.distances.len(), 3);
327 assert_eq!(paths.distances[n0.index()], 0.0);
328 assert_eq!(paths.distances[n1.index()], 1.0);
329 assert!(paths.distances[n2.index()].is_infinite());
330 assert_eq!(paths.predecessors, vec![None, Some(n0), None]);
331 }
332
333 #[test]
334 fn test_find_negative_cycle_exists() {
335 let graph_with_neg_cycle =
336 Graph::<(), f32, Directed>::from_edges([(0, 1, 1.0), (1, 2, 1.0), (2, 0, -3.0)]);
337 let result = find_negative_cycle(&graph_with_neg_cycle, NodeIndex::new(0));
338 assert!(result.is_some());
339 let cycle = result.unwrap();
340 assert_eq!(cycle.len(), 3);
341 assert!(cycle.contains(&NodeIndex::new(0)));
342 assert!(cycle.contains(&NodeIndex::new(1)));
343 assert!(cycle.contains(&NodeIndex::new(2)));
344 }
345
346 #[test]
347 fn test_find_negative_cycle_none() {
348 let graph = Graph::<(), f32, Directed>::from_edges([(0, 1, 1.0), (1, 2, 1.0), (2, 3, 1.0)]);
349 let result = find_negative_cycle(&graph, NodeIndex::new(0));
350 assert!(result.is_none());
351 }
352
353 #[test]
354 fn test_find_negative_cycle_unreachable() {
355 let graph =
356 Graph::<(), f32, Directed>::from_edges([(0, 1, 1.0), (2, 3, -1.0), (3, 2, -1.0)]);
357 let result = find_negative_cycle(&graph, NodeIndex::new(0));
358 assert!(result.is_none());
359 }
360 use crate::neg_cycle::NegCycleFinder;
361 use crate::parametric::{MaxParametricSolver, ParametricAPI};
362 use num::rational::Ratio;
363 use petgraph::graph::{DiGraph, EdgeReference};
364
365 #[test]
366 fn test_neg_cycle_multiple_neg_cycles() {
367 let digraph = DiGraph::<(), Ratio<i32>>::from_edges([
368 (0, 1, Ratio::new(1, 1)),
369 (1, 0, Ratio::new(-2, 1)), (2, 3, Ratio::new(1, 1)),
371 (3, 2, Ratio::new(-3, 1)), ]);
373
374 let mut ncf = NegCycleFinder::new(&digraph);
375 let mut dist = [
376 Ratio::new(0, 1),
377 Ratio::new(0, 1),
378 Ratio::new(0, 1),
379 Ratio::new(0, 1),
380 ];
381 let result = ncf.howard(&mut dist, |e| *e.weight());
382 assert!(result.is_some());
383 let cycle = result.unwrap();
384 let cycle_weight: Ratio<i32> = cycle.iter().map(|e| *e.weight()).sum();
385 assert!(cycle_weight < Ratio::new(0, 1));
386 }
387
388 #[test]
389 fn test_neg_cycle_not_reachable() {
390 let digraph = DiGraph::<(), Ratio<i32>>::from_edges([
391 (0, 1, Ratio::new(1, 1)),
392 (2, 3, Ratio::new(-1, 1)),
393 (3, 2, Ratio::new(-1, 1)),
394 ]);
395
396 let mut ncf = NegCycleFinder::new(&digraph);
397 let mut dist = [
398 Ratio::new(0, 1),
399 Ratio::new(0, 1),
400 Ratio::new(0, 1),
401 Ratio::new(0, 1),
402 ];
403 let result = ncf.howard(&mut dist, |e| *e.weight());
404 assert!(result.is_some());
405 }
406
407 struct TestParametricAPI;
408
409 impl ParametricAPI<(), Ratio<i32>> for TestParametricAPI {
410 fn distance(&self, ratio: &Ratio<i32>, edge: &EdgeReference<Ratio<i32>>) -> Ratio<i32> {
411 *edge.weight() - *ratio
412 }
413
414 fn zero_cancel(&self, cycle: &[EdgeReference<Ratio<i32>>]) -> Ratio<i32> {
415 let mut sum_a = Ratio::new(0, 1);
416 let mut sum_b = Ratio::new(0, 1);
417 for edge in cycle {
418 sum_a += *edge.weight();
419 sum_b += Ratio::new(1, 1);
420 }
421 sum_a / sum_b
422 }
423 }
424
425 #[test]
426 fn test_max_parametric_solver_no_neg_cycle() {
427 let digraph = DiGraph::<(), Ratio<i32>>::from_edges([
428 (0, 1, Ratio::new(1, 1)),
429 (1, 2, Ratio::new(1, 1)),
430 (2, 0, Ratio::new(1, 1)),
431 ]);
432
433 let mut solver = MaxParametricSolver::new(&digraph, TestParametricAPI);
434 let mut dist = [Ratio::new(0, 1), Ratio::new(0, 1), Ratio::new(0, 1)];
435 let mut ratio = Ratio::new(0, 1);
436
437 let cycle = solver.run(&mut dist, &mut ratio);
438 assert!(cycle.is_empty());
439 }
440
441 #[test]
442 fn test_max_parametric_solver_multiple_neg_cycles() {
443 let digraph = DiGraph::<(), Ratio<i32>>::from_edges([
444 (0, 1, Ratio::new(1, 1)),
445 (1, 0, Ratio::new(-2, 1)), (2, 3, Ratio::new(1, 1)),
447 (3, 2, Ratio::new(-4, 1)), ]);
449
450 let mut solver = MaxParametricSolver::new(&digraph, TestParametricAPI);
451 let mut dist = [
452 Ratio::new(0, 1),
453 Ratio::new(0, 1),
454 Ratio::new(0, 1),
455 Ratio::new(0, 1),
456 ];
457 let mut ratio = Ratio::new(0, 1);
458
459 let cycle = solver.run(&mut dist, &mut ratio);
460 assert!(!cycle.is_empty());
461 assert_eq!(ratio, Ratio::new(-3, 2));
462 }
463
464 #[test]
465 fn test_bellman_ford_neg_cycle() {
466 let graph_with_neg_cycle =
467 Graph::<(), f32, Directed>::from_edges([(0, 1, 1.0), (1, 2, 1.0), (2, 0, -3.0)]);
468 let result = bellman_ford(&graph_with_neg_cycle, NodeIndex::new(0));
469 assert!(result.is_err());
470 }
471
472 #[test]
473 fn test_bellman_ford_no_edge() {
474 let mut graph = Graph::<(), f32, Directed>::new();
475 let n0 = graph.add_node(());
476 let result = bellman_ford(&graph, n0);
477 assert!(result.is_ok());
478 let paths = result.unwrap();
479 assert_eq!(paths.distances, vec![0.0]);
480 assert_eq!(paths.predecessors, vec![None]);
481 }
482
483 #[test]
484 fn test_bellman_ford_disconnected() {
485 let mut graph = Graph::<(), f32, Directed>::new();
486 let n0 = graph.add_node(());
487 let n1 = graph.add_node(());
488 let n2 = graph.add_node(());
489 graph.add_edge(n0, n1, 1.0);
490
491 let result = bellman_ford(&graph, n0);
492 assert!(result.is_ok());
493 let paths = result.unwrap();
494 assert_eq!(paths.distances.len(), 3);
496 assert_eq!(paths.distances[n0.index()], 0.0);
497 assert_eq!(paths.distances[n1.index()], 1.0);
498 assert!(paths.distances[n2.index()].is_infinite());
499 assert_eq!(paths.predecessors, vec![None, Some(n0), None]);
500 }
501
502 #[test]
503 fn test_find_negative_cycle_multiple() {
504 let graph_with_neg_cycle = Graph::<(), f32, Directed>::from_edges([
505 (0, 1, 1.0),
506 (1, 0, -2.0),
507 (2, 3, 1.0),
508 (3, 2, -3.0),
509 ]);
510 let result = find_negative_cycle(&graph_with_neg_cycle, NodeIndex::new(0));
511 assert!(result.is_some());
512 }
513
514 #[test]
515 fn test_find_negative_cycle_no_neg_cycle() {
516 let graph = Graph::<(), f32, Directed>::from_edges([(0, 1, 1.0), (1, 2, 1.0), (2, 3, 1.0)]);
517 let result = find_negative_cycle(&graph, NodeIndex::new(0));
518 assert!(result.is_none());
519 }
520
521 #[test]
522 fn test_find_negative_cycle_unreachable_neg_cycle() {
523 let graph =
524 Graph::<(), f32, Directed>::from_edges([(0, 1, 1.0), (2, 3, -1.0), (3, 2, -1.0)]);
525 let result = find_negative_cycle(&graph, NodeIndex::new(0));
526 assert!(result.is_none());
527 }
528}