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