N or P
Contents Glossary
10

Distance from everywhere to everywhere

Three nested loops and a table. When the network is small and you want every distance, this is the answer.

Some questions require knowing the distance between every pair of points in a network, not just from one start. Travel-time matrices for delivery planning. Latency tables between data centers. Reachability matrices for org charts. The Floyd-Warshall algorithm fills the entire table at once and runs in time proportional to the cube of the number of points. This sounds expensive but is competitive for networks up to about a thousand nodes — fast enough to recompute on demand for many business uses. Rust's petgraph implements it directly. When your network is larger than a few thousand nodes and you need all pairs, run the single-source router (Dijkstra) once per starting point in parallel instead. When you need a few specific pairs, run it once per pair.

  1. 01
    floyd_warshall fills the entire pair-wise distance table in one call. On the petgraph API it returns a HashMap keyed by (NodeIndex, NodeIndex) — perfect for caching a delivery-zone travel matrix you precompute once and serve from forever.
    use petgraph::algo::floyd_warshall;
    use petgraph::graph::{DiGraph, NodeIndex};
    use std::collections::HashMap;
    
    fn travel_matrix(zones: &DiGraph<&str, u32>)
        -> HashMap<(NodeIndex, NodeIndex), u32>
    {
        floyd_warshall(zones, |e| *e.weight())
            .expect("no negative cycles in travel times")
    }
  2. 02
    If NodeIndex has been showing up since page 7 without an introduction, here it is. A NodeIndex is petgraph's handle for a node — a small, cheap-to-copy ID the library hands you when you add a node, which you then pass back to every subsequent operation that needs to refer to that node. It is NOT the node's payload (the &str label, the Customer struct, whatever you stored). Internally, petgraph keeps nodes in a Vec<T> — that's Rust's growable list type (we saw it back in the schedule checker), a contiguous block of values indexed by integers 0, 1, 2, …. When people say "vector" in Rust they mean Vec, not the math-class arrow. NodeIndex is a wrapper around the integer position into that Vec. That's why every graph operation is O(1), why the stored values don't need to be hashable or unique, and why this page can key a HashMap<(NodeIndex, NodeIndex), u32> on node-pair tuples to a distance. Two named nodes can carry identical labels; their NodeIndex handles are still distinct.
    use petgraph::graph::{DiGraph, NodeIndex};
    use std::collections::HashMap;
    
    let mut g = DiGraph::<&str, u32>::new();
    
    // add_node returns the handle for the node you just stored.
    let a: NodeIndex = g.add_node("Alice");    // NodeIndex(0)
    let b: NodeIndex = g.add_node("Bob");      // NodeIndex(1)
    let c: NodeIndex = g.add_node("Carol");    // NodeIndex(2)
    
    // Every subsequent operation takes the handle, never the label.
    g.add_edge(a, b, 5);
    g.add_edge(b, c, 3);
    
    // Look up the stored payload from the handle:
    let name = g[a];                            // → "Alice"
    
    // NodeIndex is Copy + Eq + Hash, so it works as a HashMap key.
    let mut distances: HashMap<NodeIndex, u32> = HashMap::new();
    distances.insert(a, 0);
    distances.insert(b, 5);
    
    
    // Mental model:
    //
    //   internal Vec of node payloads:
    //     index 0  →  "Alice"
    //     index 1  →  "Bob"
    //     index 2  →  "Carol"
    //
    //   NodeIndex(1)  ─is the address of─►  index 1  ←  "Bob"'s payload
    //
    // The label is data the graph happens to be storing at that slot.
    // The NodeIndex is how you refer to the slot.
Floyd, R. (1962) Algorithm 97: Shortest Path. Warshall, S. (1962). Source →
In plain terms

The Floyd-Warshall algorithm is one of the prettiest algorithms in computer science. It is three nested loops and one assignment in the middle. When you run it on a network, it fills in a complete table of distances from every point to every other point.

The business uses come up more often than you might expect. Logistics planning often needs a complete travel-time matrix between warehouses or delivery zones. Multi-tenant systems need latency tables between data centers. Knowledge graphs need reachability between concepts. When the network is small enough — up to about a thousand nodes — Floyd-Warshall fills the entire table in seconds.

The cost is that work grows with the cube of the number of nodes. Doubling the network multiplies the work by eight. This is fine for small networks and infeasible for large ones. The crossover where running the single-source router (Dijkstra) once per starting point becomes faster depends on graph density — for sparse networks, Dijkstra-times-n is faster around a few hundred nodes; for dense networks, Floyd-Warshall holds up longer.

The reason to prefer Floyd-Warshall when it fits is simplicity. The algorithm has no data structures to maintain — just a flat matrix of distances. It is trivial to parallelize across CPUs. It is trivial to cache. When the network does not change often, you compute the table once and look up distances forever after.

In Rust, petgraph's floyd_warshall function takes a graph and an edge-weight function and returns the distance matrix. Your team should not be writing this themselves.

For larger networks, the right pattern is to precompute the distance matrix in batch (overnight, in a worker process), store it in a database or a key-value store, and serve queries from the precomputed table. The recomputation is expensive; the lookups are free.

When the question is "every distance to every other place," the answer is in the toolbox.

← 09 Routing when paths can have negative costs
10/39
11 Cheapest network that connects everything →