Johnson's algorithm

DefinitionJohnson's algorithm#✓fully proved

Let \(E\) be a list of directed edges on the vertices \(\{0, \dots, n-1\}\) with integer weights. Johnson's algorithm adds a new vertex \(s\) with an edge of weight \(0\) to every original vertex and runs Bellman–Ford from \(s\). If Bellman–Ford reports a negative cycle, the algorithm returns no table. Otherwise let \(h(v)\) be the Bellman–Ford distance of \(v\) (taken to be \(0\) if \(v\) is unreached). Each edge from \(u\) to \(v\) of weight \(w\) gets the new weight \(\max(0, w + h(u) - h(v))\). For each original vertex \(u\) the algorithm runs Dijkstra from \(u\) on the original vertices, where the weight from \(x\) to \(y\) is the least new weight of an edge from \(x\) to \(y\). It returns the table whose entry at \((u, v)\) is \(\infty\) if Dijkstra does not reach \(v\), and \(d'(u, v) + h(v) - h(u)\) otherwise, where \(d'(u, v)\) is the Dijkstra distance.

The cost counts the selected construction, scan, relaxation and decoding actions, not full RAM or bit complexity.

public def johnson {n : Nat} (edges : List (Edge n)) :
    TimeM Nat (Option (Fin n → Fin n → WithTop Int)) := do
  let augmented ← augment edges
  let run := bellmanFord augmented (Fin.last n)
  let result ← ({ ret := run.ret, time := run.time.total } : TimeM Nat _)
  match result with
  | .negativeCycle _ => pure none
  | .distances distances => do
      let potential ← readPotentials distances
      let reweighted ← reweightEdges potential augmented
      let prepared ← prepare edges reweighted
      let rows ← buildVector n (runRow prepared potential)
      pure (some fun source target => (rows.get source).get target)
Uses 12 · Used by 4

TheoremJohnson's algorithm fails exactly on reachable negative cycles#✓fully proved

For every list \(E\) of directed edges on the vertices \(\{0, \dots, n-1\}\) with integer weights, Johnson's algorithm on \(E\) returns no table if and only if there is a vertex \(s\) from which a negative cycle of \(E\) is reachable.

public theorem johnson_eq_none_iff {n : Nat} (edges : List (Edge n)) :
    (johnson edges).ret = none ↔ ∃ source : Fin n, HasReachableNegativeCycle edges source
Uses 19

TheoremCorrectness of Johnson's algorithm#✓fully proved

Let \(E\) be a list of directed edges on the vertices \(\{0, \dots, n-1\}\) with integer weights, and suppose Johnson's algorithm on \(E\) returns a table \(d\). Then for all vertices \(s\) and \(t\), \(d(s, t) = \infty\) if and only if \(t\) is not reachable from \(s\) along edges of \(E\). Moreover, if \(d(s, t)\) is an integer \(x\), then some weighted path in \(E\) from \(s\) to \(t\) has weight \(x\), and every weighted path in \(E\) from \(s\) to \(t\) has weight at least \(x\).

public theorem johnson_distance_spec {n : Nat} (edges : List (Edge n))
    (distance : Fin n → Fin n → WithTop Int) (run : (johnson edges).ret = some distance)
    (source target : Fin n) :
    (distance source target = ⊤ ↔ ¬(toDirectedAdjacency edges).Reachable source target) ∧
      ∀ value : Int, distance source target = (value : WithTop Int) →
        (∃ path, IsWeightedPath edges source target path ∧ pathWeight path = value) ∧
          ∀ path, IsWeightedPath edges source target path → value ≤ pathWeight path
Uses 32

TheoremExact cost of a successful run of Johnson's algorithm#✓fully proved

Let \(E\) be a list of \(m\) directed edges on the vertices \(\{0, \dots, n-1\}\) with integer weights. If Johnson's algorithm on \(E\) returns a table, then its counted cost is exactly \(n^3 + nm + (n+1)(m+n) + 2n^2 + 4m + 5n + 1\).

public theorem johnson_time_of_success {n : Nat} (edges : List (Edge n))
    (distance : Fin n → Fin n → WithTop Int) (run : (johnson edges).ret = some distance) :
    (johnson edges).time = n^3 + n*edges.length + (n+1)*(edges.length+n) +
      2*n^2 + 4*edges.length + 5*n + 1
Uses 17

TheoremCost bound for Johnson's algorithm#✓fully proved

For every list \(E\) of \(m\) directed edges on the vertices \(\{0, \dots, n-1\}\) with integer weights, the counted cost of Johnson's algorithm on \(E\), including the case where it extracts a negative cycle, is at most \(n^3 + nm + (n+1)(m+n) + 2n^2 + 4m + 5n + 1 + 3(n+1)\).

public theorem johnson_time_le {n : Nat} (edges : List (Edge n)) :
    (johnson edges).time ≤ n^3 + n*edges.length + (n+1)*(edges.length+n) +
      2*n^2 + 4*edges.length + 5*n + 1 + 3*(n+1)
Uses 18
5 fully proved

The dependency explorer needs JavaScript and its graph data.

Download the complete graph data.