floyd_warshall/lib.rs
1//! This crate contains an implementation of the Floyd-Warshall algorithm to solve the all-pairs-shortest-paths problem in undirected graphs.
2
3#![deny(missing_docs)]
4#![feature(conservative_impl_trait)]
5
6extern crate petgraph;
7
8#[cfg(test)]
9extern crate rand;
10
11#[cfg(test)]
12#[macro_use]
13extern crate text_io;
14
15#[cfg(test)]
16mod tests;
17
18mod matrices;
19pub use matrices::*;
20
21use petgraph::graph::NodeIndex;
22use petgraph::visit::NodeRef;
23use petgraph::visit::Data;
24use petgraph::visit::GraphBase;
25use petgraph::visit::NodeCount;
26use petgraph::visit::IntoNodeIdentifiers;
27use petgraph::visit::IntoNodeReferences;
28use petgraph::visit::IntoEdgeReferences;
29use petgraph::visit::EdgeRef;
30use petgraph::visit::GraphProp;
31
32/// This function computes a distance matrix containing the shortest paths between every two nodes in the graph.
33/// By using the Floyd-Warshall algorithm, this is computed in **O(V^(3))** runtime.
34pub fn floyd_warshall<G>(g: G) -> PathMatrix<G::NodeWeight>
35where
36 G: Data
37 + GraphBase<NodeId = NodeIndex>
38 + NodeCount
39 + IntoNodeIdentifiers<NodeId = NodeIndex>
40 + IntoNodeReferences
41 + IntoEdgeReferences
42 + GraphProp,
43 G::NodeWeight: Clone,
44 G::EdgeWeight: Clone + Into<usize>,
45{
46 // We currently only support directed graphs.
47 assert!(!g.is_directed());
48
49 let mut m = PathMatrix::new(g.node_count());
50
51 // Each node has a distance of 0 to itself.
52 // Note, that this sets the distance of every node to itself to 0, due to the matrix representation.
53 m.set_path_len(0, 0, 0);
54
55 // Update the matrix to represent the actual edges in the graph.
56 for e in g.edge_references() {
57 let n1 = e.source().index();
58 let n2 = e.target().index();
59 let w: G::EdgeWeight = e.weight().clone();
60 let w: usize = w.into();
61 m.set_path_len(n1, n2, w);
62 }
63
64 // k is the "intermediate" node which is currently considered.
65 for k in g.node_references() {
66 let kw = k.weight();
67 let k = k.id().index();
68
69 // For every pair (n1, n2) of two disjunct nodes in the graph check, if the path over k is shorter than the previously found one.
70 for n1 in g.node_identifiers() {
71 let n1 = n1.index();
72
73 for n2 in g.node_identifiers() {
74 let n2 = n2.index();
75
76 // No need to do this for identical nodes.
77 if n1 == n2 {
78 continue;
79 }
80
81 // No need to do this for both triangles in the matrix.
82 if n1 > n2 {
83 continue;
84 }
85
86 // No need to do this for k == n1 or k == n2
87 if n1 == k || n2 == k {
88 continue;
89 }
90
91 // These are the two options in this round to reach from node 1 to node 2:
92 // - v1, which is (if it exists) the saved path from n1 to n2, which is eiter a direct edge or a path using any intermediate nodes less than k.
93 let mut v1 = None;
94 if m.does_path_exist(n1, n2) {
95 v1 = Some(m.get_path_len(n1, n2));
96 }
97
98 // - v2, which is the path from node 1 to node k to node 2 (if such a path exists, which means, that k is reachable from n1 and n2 is reachable from k).
99 let v2_exists = m.does_path_exist(n1, k);
100 let v2_exists = v2_exists && m.does_path_exist(k, n2);
101
102 let mut v2 = None;
103 if v2_exists {
104 let part1 = m.get_path_len(n1, k);
105 let part2 = m.get_path_len(k, n2);
106
107 // .saturating_add is a relict of a time, when a path was usize::MAX as a sign for "there is no path here".
108 // But as any other .add doesn't make any more sense, it will stay.
109 v2 = Some(part1.saturating_add(part2));
110 }
111
112 // Whichever of these is minimal, can be used to reach from node 1 to node 2.
113 if v2.is_some() && (v1.is_none() || v2.unwrap() < v1.unwrap()) {
114 let v2 = v2.unwrap();
115
116 // Update the matrix to the minimum of these two.
117 m.set_path_len(n1, n2, v2);
118
119 // TODO: reuse vector here.
120 let mut v: Vec<G::NodeWeight> = Vec::new();
121
122 // Reverse path, if n1 < k or k < n2 not fulfilled:
123 if n1 <= k {
124 v.extend(m.get_path_iter(n1, k).cloned());
125 } else {
126 v.extend(m.get_path_iter(n1, k).rev().cloned());
127 }
128
129 // Push k in the middle of the path here.
130 v.push(kw.clone());
131
132 if k <= n2 {
133 v.extend(m.get_path_iter(k, n2).cloned());
134 } else {
135 v.extend(m.get_path_iter(k, n2).rev().cloned());
136 }
137
138 // Save the path as new optimal path from node 1 to node 2.
139 m.get_path_mut(n1, n2).set_vector(v);
140 }
141 }
142 }
143 }
144
145 m
146}