Bellman–Ford algorithm

StructureWeighted directed edge#✓fully proved

A weighted directed edge on the vertices \(0, \dots, n-1\) consists of a source vertex, a target vertex and an integer weight.

Bellman-Ford takes a list of such edges, so each entry is one edge occurrence.

public structure Edge (n : Nat) where
  /-- The source vertex. -/
  source : Fin n
  /-- The target vertex. -/
  target : Fin n
  /-- The integer edge weight. -/
  weight : Int
deriving DecidableEq
Used by 60
Used byBellman–Ford algorithm · Witness cost of a returned negative cycle · Bellman-Ford spends no witness steps when it returns distances · Bellman-Ford spends at most \(3n\) witness steps · Bellman-Ford leaves exactly the unreachable vertices at infinite distance · Bellman-Ford distances are lower bounds on walk weights · Bellman-Ford distances are realized by walks · Bellman-Ford path reconstruction fails exactly at infinite distances · Bellman-Ford reconstructed paths are simple and realize the distances · Bellman-Ford predecessor edges are tight · Relaxation checks of Bellman-Ford pass by pass · Bellman-Ford makes exactly \(n \cdot |E|\) relaxation checks · Bellman-Ford succeeds exactly when no negative cycle is reachable · Bellman-Ford reports a negative cycle exactly when one is reachable · Bellman-Ford gives the source no predecessor · Bellman-Ford makes at most \(4 n |E|\) counted steps · Bellman-Ford distance table · Edges of the weight-forgetting graph · Existence of a reachable negative cycle · Weighted path in an edge list · Appending an edge to a weighted path · Tail of a weighted path · Weighted paths give walks of the same length · Bellman-Ford path reconstruction · Predecessor paths realize the distances under a decreasing rank · Vertices of a path · Bellman–Ford path weight · Weight of a path with an edge appended · Reachability by weighted paths · Reachable negative cycle witness · Bellman–Ford result · Weight-forgetting directed graph · Johnson's algorithm · Correctness of Johnson's algorithm · Johnson's algorithm fails exactly on reachable negative cycles · Cost bound for Johnson's algorithm · Exact cost of a successful run of Johnson's algorithm · Shortest paths in a DAG with a topology check · Unreachable vertices are exactly those at infinite DAG distance · DAG shortest-path distances are shortest-path weights · Failure output of DAG shortest paths · Successful output of DAG shortest paths · A failed DAG shortest-path run exhibits a cycle · Extracted DAG shortest paths are shortest paths · The DAG shortest-path algorithm succeeds exactly on acyclic graphs · One-pass DAG shortest-path relaxation · Infinite DAG distances are exactly unreachable vertices · Finite DAG distances are at most every path weight · Finite DAG distances are path weights · Distance updates are bounded by edge relaxations · Edge relaxation count of the DAG pass · Finite non-source vertices have predecessors · Unreached vertices have no DAG shortest-path predecessor · Predecessor extraction fails exactly at infinite distances · Paths extracted after topological relaxation are shortest paths · Predecessor edges of the DAG shortest-path pass · The DAG shortest-path pass keeps its source · The DAG shortest-path pass gives the source distance zero · The source has no DAG shortest-path predecessor · The DAG shortest-path pass is charged one vertex visit per vertex

DefinitionWeight-forgetting directed graph#✓fully proved

For a list of weighted edges on the vertices \(\{0, \dots, n-1\}\), the associated ordered directed graph enumerates its vertices as \(0, 1, \dots, n-1\), and the outgoing neighbor list of a vertex \(u\) is the list of targets of the edges of the list with source \(u\), in the order in which they occur and with repetitions.

@[expose] public def toDirectedAdjacency {n : Nat}
    (edges : List (Edge n)) : DirectedAdjacency (Fin n) where
  vertices := List.finRange n
  vertices_nodup := List.nodup_finRange n
  vertices_complete := List.mem_finRange
  adjacency source :=
    (edges.filter fun edge => decide (edge.source = source)).map Edge.target
Uses 2 · Used by 27

DefinitionVertices of a path#✓fully proved

For a vertex \(s\) of \(\{0, \dots, n-1\}\) and a list of weighted edges \(e_1, \dots, e_k\) on these vertices, the vertices of the path from \(s\) along this list are the list \(s, t_1, \dots, t_k\), where \(t_i\) is the target of \(e_i\).

The edges are not required to be consecutive.

@[expose] public def pathVertices {n : Nat}
    (source : Fin n) (path : List (Edge n)) : List (Fin n) :=
  source :: path.map Edge.target
Uses 1 · Used by 17

DefinitionBellman–Ford path weight#✓fully proved

The weight of a list of integer-weighted edges is the sum of the weights of its entries, an edge occurring several times being counted each time.

@[expose] public def pathWeight {n : Nat} (path : List (Edge n)) : Int :=
  (path.map Edge.weight).sum
Uses 1 · Used by 33
Used byBellman–Ford algorithm · Witness cost of a returned negative cycle · Bellman-Ford spends no witness steps when it returns distances · Bellman-Ford spends at most \(3n\) witness steps · Bellman-Ford leaves exactly the unreachable vertices at infinite distance · Bellman-Ford distances are lower bounds on walk weights · Bellman-Ford distances are realized by walks · Bellman-Ford path reconstruction fails exactly at infinite distances · Bellman-Ford reconstructed paths are simple and realize the distances · Bellman-Ford predecessor edges are tight · Relaxation checks of Bellman-Ford pass by pass · Bellman-Ford succeeds exactly when no negative cycle is reachable · Bellman-Ford reports a negative cycle exactly when one is reachable · Bellman-Ford gives the source no predecessor · Bellman-Ford makes at most \(4 n |E|\) counted steps · Predecessor paths realize the distances under a decreasing rank · Weight of a path with an edge appended · Reachable negative cycle witness · Correctness of Johnson's algorithm · Johnson's algorithm fails exactly on reachable negative cycles · DAG shortest-path distances are shortest-path weights · Extracted DAG shortest paths are shortest paths · Infinite DAG distances are exactly unreachable vertices · Finite DAG distances are at most every path weight · Finite DAG distances are path weights · Finite non-source vertices have predecessors · Unreached vertices have no DAG shortest-path predecessor · Predecessor extraction fails exactly at infinite distances · Paths extracted after topological relaxation are shortest paths · Predecessor edges of the DAG shortest-path pass · The DAG shortest-path pass keeps its source · The DAG shortest-path pass gives the source distance zero · The source has no DAG shortest-path predecessor

DefinitionWeighted path in an edge list#✓fully proved

Let \(E\) be a list of weighted directed edges on the vertices \(0, \dots, n-1\). A list \(P = (e_1, \dots, e_k)\) of weighted directed edges is a weighted path in \(E\) from \(s\) to \(t\) when every \(e_i\) belongs to \(E\), the target of \(e_i\) is the source of \(e_{i+1}\) for every \(i < k\), and either \(k = 0\) and \(s = t\), or \(k \ge 1\), the source of \(e_1\) is \(s\) and the target of \(e_k\) is \(t\).

@[expose] public def IsWeightedPath {n : Nat} (edges : List (Edge n)) (source target : Fin n)
    (path : List (Edge n)) : Prop :=
  (∀ edge ∈ path, edge ∈ edges) ∧
    path.IsChain (fun left right => left.target = right.source) ∧
    match path with
    | [] => source = target
    | first :: rest =>
        first.source = source ∧ (first :: rest).getLast?.map Edge.target = some target
Uses 1 · Used by 36
Used byBellman–Ford algorithm · Witness cost of a returned negative cycle · Bellman-Ford spends no witness steps when it returns distances · Bellman-Ford spends at most \(3n\) witness steps · Bellman-Ford leaves exactly the unreachable vertices at infinite distance · Bellman-Ford distances are lower bounds on walk weights · Bellman-Ford distances are realized by walks · Bellman-Ford path reconstruction fails exactly at infinite distances · Bellman-Ford reconstructed paths are simple and realize the distances · Bellman-Ford predecessor edges are tight · Relaxation checks of Bellman-Ford pass by pass · Bellman-Ford succeeds exactly when no negative cycle is reachable · Bellman-Ford reports a negative cycle exactly when one is reachable · Bellman-Ford gives the source no predecessor · Bellman-Ford makes at most \(4 n |E|\) counted steps · Appending an edge to a weighted path · Tail of a weighted path · Weighted paths give walks of the same length · Predecessor paths realize the distances under a decreasing rank · Reachability by weighted paths · Reachable negative cycle witness · Correctness of Johnson's algorithm · Johnson's algorithm fails exactly on reachable negative cycles · DAG shortest-path distances are shortest-path weights · Extracted DAG shortest paths are shortest paths · Infinite DAG distances are exactly unreachable vertices · Finite DAG distances are at most every path weight · Finite DAG distances are path weights · Finite non-source vertices have predecessors · Unreached vertices have no DAG shortest-path predecessor · Predecessor extraction fails exactly at infinite distances · Paths extracted after topological relaxation are shortest paths · Predecessor edges of the DAG shortest-path pass · The DAG shortest-path pass keeps its source · The DAG shortest-path pass gives the source distance zero · The source has no DAG shortest-path predecessor

TheoremEdges of the weight-forgetting graph#✓fully proved

For a list \(E\) of weighted directed edges on the vertices \(0, \dots, n-1\) and vertices \(u\), \(v\), the graph obtained from \(E\) by forgetting weights has an edge from \(u\) to \(v\) if and only if some edge in \(E\) has source \(u\) and target \(v\).

public theorem edge_toDirectedAdjacency_iff {n : Nat} {edges : List (Edge n)} {u v : Fin n} :
    (toDirectedAdjacency edges).Edge u v ↔
      ∃ edge, edge ∈ edges ∧ edge.source = u ∧ edge.target = v
Uses 3 · Used by 11

TheoremTail of a weighted path#✓fully proved

Let \(E\) be a list of weighted directed edges on the vertices \(0, \dots, n-1\) and let \(s\) and \(t\) be vertices. If the list consisting of an edge \(e\) followed by a list \(R\) of edges is a weighted path in \(E\) from \(s\) to \(t\), then \(R\) is a weighted path in \(E\) from the target of \(e\) to \(t\).

public theorem isWeightedPath_tail {n : Nat} {edges : List (Edge n)}
    {source target : Fin n} {first : Edge n} {rest : List (Edge n)}
    (path : IsWeightedPath edges source target (first :: rest)) :
    IsWeightedPath edges first.target target rest
Uses 2 · Used by 4

TheoremAppending an edge to a weighted path#✓fully proved

Let \(E\) be a list of weighted directed edges on the vertices \(0, \dots, n-1\) and let \(s\), \(m\) and \(t\) be vertices. If \(P\) is a weighted path in \(E\) from \(s\) to \(m\) and \(e\) is an edge belonging to \(E\) with source \(m\) and target \(t\), then the list \(P\) followed by \(e\) is a weighted path in \(E\) from \(s\) to \(t\).

public theorem isWeightedPath_append_edge {n : Nat} {edges : List (Edge n)}
    {source middle target : Fin n} {path : List (Edge n)} {edge : Edge n}
    (path_valid : IsWeightedPath edges source middle path)
    (edge_mem : edge ∈ edges) (edge_source : edge.source = middle)
    (edge_target : edge.target = target) :
    IsWeightedPath edges source target (path ++ [edge])
Uses 2 · Used by 10

TheoremWeight of a path with an edge appended#✓fully proved

For every list \(P\) of weighted directed edges on the vertices \(0, \dots, n-1\) and every weighted directed edge \(e\) on these vertices, the weight of the list \(P\) followed by \(e\) is the weight of \(P\) plus the weight of \(e\).

public theorem pathWeight_append_edge {n : Nat} (path : List (Edge n)) (edge : Edge n) :
    pathWeight (path ++ [edge]) = pathWeight path + edge.weight
Uses 2 · Used by 10

TheoremWeighted paths give walks of the same length#✓fully proved

Let \(E\) be a list of weighted directed edges on the vertices \(0, \dots, n-1\) and let \(s\) and \(t\) be vertices. If a list \(P\) of \(k\) edges is a weighted path in \(E\) from \(s\) to \(t\), then there is a walk of length \(k\) from \(s\) to \(t\) in the directed graph obtained from \(E\) by forgetting the weights.

public theorem isWeightedPath_walk {n : Nat} {edges : List (Edge n)}
    {source target : Fin n} {path : List (Edge n)}
    (path_valid : IsWeightedPath edges source target path) :
    (toDirectedAdjacency edges).Walk path.length source target
Uses 6 · Used by 9

TheoremReachability by weighted paths#✓fully proved

For a list of weighted edges on the vertices \(\{0, \dots, n-1\}\) and vertices \(s\) and \(t\), the vertex \(t\) is reachable from \(s\) in the directed graph obtained from the list by forgetting the weights if and only if there is a weighted path from \(s\) to \(t\) using edges of the list.

public theorem reachable_iff_exists_isWeightedPath {n : Nat} {edges : List (Edge n)}
    {source target : Fin n} :
    (toDirectedAdjacency edges).Reachable source target ↔
      ∃ path, IsWeightedPath edges source target path
Uses 9 · Used by 3

StructureReachable negative cycle witness#✓fully proved

For a list of weighted edges on the vertices \(\{0, \dots, n-1\}\) and a source vertex \(s\), a reachable negative cycle consists of a base vertex \(b\), a weighted path \(p\) from \(s\) to \(b\) using edges of the list whose vertices along \(p\) from \(s\) are pairwise distinct, and a nonempty weighted path \(c\) from \(b\) to \(b\) using edges of the list whose edges have pairwise distinct sources and whose weight is negative.

Cslib.Algorithms.Lean.BellmanFord.ReachableNegativeCycle · 19 lines
public structure ReachableNegativeCycle {n : Nat} (edges : List (Edge n)) (source : Fin n) where
  /-- The distinguished cycle vertex reached by the prefix. -/
  base : Fin n
  /-- A source-to-base path. -/
  «prefix» : List (Edge n)
  /-- A nonempty closed path based at `base`. -/
  cycle : List (Edge n)
  /-- The prefix uses input edges and has the declared endpoints. -/
  prefix_valid : IsWeightedPath edges source base «prefix»
  /-- The prefix visits no vertex twice. -/
  prefix_simple : (pathVertices source «prefix»).Nodup
  /-- The cycle uses input edges and closes at `base`. -/
  cycle_valid : IsWeightedPath edges base base cycle
  /-- The cycle has at least one edge. -/
  cycle_nonempty : cycle ≠ []
  /-- The edge sources around the cycle are pairwise distinct. -/
  cycle_simple : (cycle.map Edge.source).Nodup
  /-- The cycle has negative total weight. -/
  cycle_negative : pathWeight cycle < 0
Uses 4 · Used by 22

DefinitionExistence of a reachable negative cycle#✓fully proved

Let \(E\) be a list of weighted directed edges on the vertices \(0, \dots, n-1\) and \(s\) a vertex. Then \(E\) has a negative cycle reachable from \(s\) when there is a reachable negative cycle witness for \(E\) and \(s\): a base vertex \(b\), a weighted path in \(E\) from \(s\) to \(b\) that visits no vertex twice, and a nonempty weighted path in \(E\) from \(b\) to \(b\) whose edge sources are pairwise distinct and whose total weight is negative.

@[expose] public def HasReachableNegativeCycle {n : Nat}
    (edges : List (Edge n)) (source : Fin n) : Prop :=
  Nonempty (ReachableNegativeCycle edges source)
Uses 2 · Used by 3

StructureBellman-Ford distance table#✓fully proved

For a graph on the vertices \(0, \dots, n-1\), the output of a successful Bellman-Ford run consists of a source vertex \(s\), for every vertex a distance that is either an integer or \(\infty\), and for every vertex an optional predecessor edge (a weighted directed edge, or none).

The structure only records these values; that they are shortest-path data is proved separately.

public structure Distances (n : Nat) where
  /-- The source used for the run. -/
  source : Fin n
  /-- Finite shortest weights, with `none` denoting infinity. -/
  distance : Vector (Option Int) n
  /-- The final predecessor edge for every vertex. -/
  predecessor : Vector (Option (Edge n)) n
Uses 1 · Used by 46
Used byBellman–Ford algorithm · Witness cost of a returned negative cycle · Bellman-Ford spends no witness steps when it returns distances · Bellman-Ford spends at most \(3n\) witness steps · Bellman-Ford leaves exactly the unreachable vertices at infinite distance · Bellman-Ford distances are lower bounds on walk weights · Bellman-Ford distances are realized by walks · Bellman-Ford path reconstruction fails exactly at infinite distances · Bellman-Ford reconstructed paths are simple and realize the distances · Bellman-Ford predecessor edges are tight · Relaxation checks of Bellman-Ford pass by pass · Bellman-Ford succeeds exactly when no negative cycle is reachable · Bellman-Ford reports a negative cycle exactly when one is reachable · Bellman-Ford gives the source no predecessor · Bellman-Ford makes at most \(4 n |E|\) counted steps · Bellman-Ford path reconstruction · Predecessor paths realize the distances under a decreasing rank · Bellman–Ford result · Johnson's algorithm · Correctness of Johnson's algorithm · Johnson's algorithm fails exactly on reachable negative cycles · Cost bound for Johnson's algorithm · Exact cost of a successful run of Johnson's algorithm · Shortest paths in a DAG with a topology check · Unreachable vertices are exactly those at infinite DAG distance · DAG shortest-path distances are shortest-path weights · Failure output of DAG shortest paths · Successful output of DAG shortest paths · A failed DAG shortest-path run exhibits a cycle · Extracted DAG shortest paths are shortest paths · The DAG shortest-path algorithm succeeds exactly on acyclic graphs · One-pass DAG shortest-path relaxation · Infinite DAG distances are exactly unreachable vertices · Finite DAG distances are at most every path weight · Finite DAG distances are path weights · Distance updates are bounded by edge relaxations · Edge relaxation count of the DAG pass · Finite non-source vertices have predecessors · Unreached vertices have no DAG shortest-path predecessor · Predecessor extraction fails exactly at infinite distances · Paths extracted after topological relaxation are shortest paths · Predecessor edges of the DAG shortest-path pass · The DAG shortest-path pass keeps its source · The DAG shortest-path pass gives the source distance zero · The source has no DAG shortest-path predecessor · The DAG shortest-path pass is charged one vertex visit per vertex

DefinitionBellman-Ford path reconstruction#✓fully proved

Let the output of a Bellman-Ford run on \(n\) vertices have source \(s\). The path to a vertex \(t\) is built backwards from \(t\): at a vertex \(v \ne s\), take the recorded predecessor edge \(e\) of \(v\), put it in front of the edges collected so far, and continue from the source of \(e\). When \(s\) is reached within at most \(n\) such steps, the result is the collected list of edges (empty if \(t = s\)). If some vertex on the way has no predecessor edge, or \(s\) is not reached within \(n\) steps, there is no result.

public def Distances.pathTo? {n : Nat} (result : Distances n) (target : Fin n) :
    Option (List (Edge n)) := by
  let rec follow (current : Fin n) : Nat → List (Edge n) → Option (List (Edge n))
    | 0, suffix => if current = result.source then some suffix else none
    | fuel + 1, suffix =>
        if current = result.source then
          some suffix
        else
          match result.predecessor.get current with
          | none => none
          | some edge => follow edge.source fuel (edge :: suffix)
  exact follow target n []
Uses 2 · Used by 6

StructureBellman-Ford cost counters#✓fully proved

The cost of a Bellman-Ford run is a pair of natural numbers: the number of edge occurrences checked by relaxation passes, and the number of predecessor edges read or emitted while reconstructing a negative-cycle witness.

public structure Cost where
  /-- Edge occurrences checked by relaxation passes. -/
  relaxationChecks : Nat
  /-- Predecessor edges read or emitted while reconstructing a failure witness. -/
  cycleWitnessSteps : Nat
deriving DecidableEq
Used by 22

DefinitionTotal Bellman–Ford cost#✓fully proved

The total of a Bellman–Ford cost record is its number of edge relaxation checks plus its number of negative-cycle witness reconstruction steps.

@[expose] public def Cost.total (cost : Cost) : Nat :=
  cost.relaxationChecks + cost.cycleWitnessSteps
Uses 1 · Used by 6

InductiveBellman–Ford result#✓fully proved

For a list of weighted edges on the vertices \(\{0, \dots, n-1\}\) and a source vertex \(s\), a Bellman–Ford result is either a distance record (a source vertex, an optional integer distance for each vertex and an optional predecessor edge for each vertex) or a reachable negative cycle witness for the list and \(s\).

The type itself does not require the distances to be correct.

public inductive Result {n : Nat} (edges : List (Edge n)) (source : Fin n) where
  /-- Exact shortest distances and predecessor edges. -/
  | distances (result : Distances n)
  /-- A concrete reachable negative cycle. -/
  | negativeCycle (witness : ReachableNegativeCycle edges source)
Uses 3 · Used by 21

DefinitionBellman–Ford algorithm#✓fully proved

Let \(E\) be a list of integer-weighted directed edges on the vertices \(\{0, \dots, n-1\}\) and let \(s\) be a vertex. Bellman–Ford run on \(E\) from \(s\) starts with distance \(d(s) = 0\) and \(d(v) = \infty\) for every other vertex \(v\), and makes \(n - 1\) passes over \(E\) followed by one final pass. A pass goes through the edges of \(E\) in list order, charging one relaxation check per edge occurrence; an edge from \(u\) to \(v\) of weight \(w\) with \(d(u)\) finite sets \(d(v)\) to \(d(u) + w\) and records the edge as the predecessor edge of \(v\) when \(d(v) = \infty\) or \(d(u) + w < d(v)\). If the final pass changes no distance, the result is the distances and predecessor edges. Otherwise, starting from the vertex whose distance the final pass changed last, the algorithm follows \(n\) predecessor edges back to a vertex \(b\); it then collects the cycle of predecessor edges through \(b\), following at most \(n\) predecessor edges back from \(b\) until it reaches an edge with source \(b\), and collects a path from \(s\) to \(b\) of at most \(n - 1\) edges by following back from \(b\) the edges by which vertices first received a finite distance. Each lookup of a recorded edge in these three steps is charged as one witness step, also when no edge is recorded. If all three steps succeed and give a reachable negative cycle witness for \(E\) and \(s\), that witness is the result; otherwise the result is the distances and predecessor edges.

public def bellmanFord {n : Nat} (edges : List (Edge n)) (source : Fin n) :
    TimeM Cost (Result edges source) :=
  Internal.bellmanFord edges source
Uses 8 · Used by 20

TheoremBellman-Ford succeeds exactly when no negative cycle is reachable#✓fully proved

Let \(E\) be a list of integer-weighted directed edges on the vertices \(\{0, \dots, n-1\}\) and let \(s\) be a vertex. Bellman-Ford run on \(E\) from \(s\) returns distances if and only if there is no negative cycle reachable from \(s\), that is, no simple path from \(s\) to a vertex \(b\) together with a nonempty cycle through \(b\) along edges of \(E\) whose edge sources are pairwise distinct and whose total weight is negative.

public theorem bellmanFord_returns_distances_iff {n : Nat} (edges : List (Edge n))
    (source : Fin n) :
    (∃ result, (bellmanFord edges source).ret = Result.distances result) ↔
      ¬HasReachableNegativeCycle edges source
Uses 10 · Used by 1

TheoremBellman-Ford reports a negative cycle exactly when one is reachable#✓fully proved

Let \(E\) be a list of integer-weighted directed edges on the vertices \(\{0, \dots, n-1\}\) and let \(s\) be a vertex. Bellman-Ford run on \(E\) from \(s\) returns a reachable negative cycle witness if and only if a negative cycle is reachable from \(s\), that is, there are a simple path from \(s\) to a vertex \(b\) and a nonempty cycle through \(b\) along edges of \(E\) whose edge sources are pairwise distinct and whose total weight is negative.

public theorem bellmanFord_returns_negativeCycle_iff {n : Nat} (edges : List (Edge n))
    (source : Fin n) :
    (∃ witness, (bellmanFord edges source).ret = Result.negativeCycle witness) ↔
      HasReachableNegativeCycle edges source
Uses 10

TheoremBellman-Ford distances are realized by walks#✓fully proved

Let \(E\) be a list of integer-weighted directed edges on the vertices \(\{0, \dots, n-1\}\) and let \(s\) be a vertex. If Bellman-Ford run on \(E\) from \(s\) returns distances \(d\) and the distance \(d(t)\) of a vertex \(t\) is a finite integer \(\delta\), then there is a walk from \(s\) to \(t\) along edges of \(E\) whose total weight is \(\delta\).

public theorem bellmanFord_distance_realized {n : Nat} {edges : List (Edge n)}
    {source target : Fin n}
    {result : Distances n} {distance : Int}
    (hRun : (bellmanFord edges source).ret = Result.distances result)
    (hDistance : result.distance.get target = some distance) :
    ∃ path, IsWeightedPath edges source target path ∧ pathWeight path = distance
Uses 9 · Used by 1

TheoremBellman-Ford distances are lower bounds on walk weights#✓fully proved

Let \(E\) be a list of integer-weighted directed edges on the vertices \(\{0, \dots, n-1\}\) and let \(s\) be a vertex. If Bellman-Ford run on \(E\) from \(s\) returns distances \(d\), the distance \(d(t)\) of a vertex \(t\) is a finite integer \(\delta\), and \(P\) is a walk from \(s\) to \(t\) along edges of \(E\) (vertices may repeat), then \(\delta\) is at most the total weight of \(P\).

public theorem bellmanFord_distance_minimal {n : Nat} {edges : List (Edge n)}
    {source target : Fin n} {result : Distances n} {distance : Int}
    {path : List (Edge n)}
    (hRun : (bellmanFord edges source).ret = Result.distances result)
    (hDistance : result.distance.get target = some distance)
    (hPath : IsWeightedPath edges source target path) :
    distance ≤ pathWeight path
Uses 9 · Used by 1

TheoremBellman-Ford leaves exactly the unreachable vertices at infinite distance#✓fully proved

Let \(E\) be a list of integer-weighted directed edges on the vertices \(\{0, \dots, n-1\}\) and let \(s\) be a vertex. If Bellman-Ford run on \(E\) from \(s\) returns distances \(d\), with \(\infty\) marking a vertex that has no finite distance, then for every vertex \(t\), \(d(t) = \infty\) if and only if \(t\) is not reachable from \(s\) in the directed graph obtained from \(E\) by forgetting the weights.

public theorem bellmanFord_distance_eq_none_iff {n : Nat} {edges : List (Edge n)}
    {source target : Fin n} {result : Distances n}
    (hRun : (bellmanFord edges source).ret = Result.distances result) :
    result.distance.get target = none ↔
      ¬(toDirectedAdjacency edges).Reachable source target
Uses 12 · Used by 1

TheoremBellman-Ford predecessor edges are tight#✓fully proved

Let \(E\) be a list of integer-weighted directed edges on the vertices \(\{0, \dots, n-1\}\) and let \(s\) be a vertex. Suppose Bellman-Ford run on \(E\) from \(s\) returns distances \(d\) together with predecessor edges, and the predecessor edge of a vertex \(t\) is an edge \(e\) from \(u\) to \(v\) of weight \(w\). Then \(e\) belongs to \(E\), \(v = t\), and \(d(u)\) and \(d(t)\) are both finite with \(d(t) = d(u) + w\).

public theorem bellmanFord_predecessor_spec {n : Nat} {edges : List (Edge n)}
    {source target : Fin n} {result : Distances n} {edge : Edge n}
    (hRun : (bellmanFord edges source).ret = Result.distances result)
    (hPredecessor : result.predecessor.get target = some edge) :
    edge ∈ edges ∧ edge.target = target ∧
      ∃ sourceDistance targetDistance,
        result.distance.get edge.source = some sourceDistance ∧
          result.distance.get target = some targetDistance ∧
          targetDistance = sourceDistance + edge.weight
Uses 9

TheoremBellman-Ford gives the source no predecessor#✓fully proved

Let \(E\) be a list of integer-weighted directed edges on the vertices \(\{0, \dots, n-1\}\) and let \(s\) be a vertex. If Bellman-Ford run on \(E\) from \(s\) returns distances together with predecessor edges, then \(s\) has no predecessor edge.

public theorem bellmanFord_source_predecessor_eq_none {n : Nat} {edges : List (Edge n)}
    {source : Fin n} {result : Distances n}
    (hRun : (bellmanFord edges source).ret = Result.distances result) :
    result.predecessor.get source = none
Uses 9

TheoremBellman-Ford path reconstruction fails exactly at infinite distances#✓fully proved

Let \(E\) be a list of integer-weighted directed edges on the vertices \(\{0, \dots, n-1\}\) and let \(s\) be a vertex. If Bellman-Ford run on \(E\) from \(s\) returns distances \(d\), with \(\infty\) marking a vertex that has no finite distance, together with predecessor edges, then for every vertex \(t\), reconstructing a path to \(t\) (following predecessor edges back from \(t\) for at most \(n\) steps until \(s\) is reached) fails if and only if \(d(t) = \infty\).

public theorem bellmanFord_pathTo?_eq_none_iff {n : Nat} {edges : List (Edge n)}
    {source target : Fin n} {result : Distances n}
    (hRun : (bellmanFord edges source).ret = Result.distances result) :
    result.pathTo? target = none ↔ result.distance.get target = none
Uses 10

TheoremBellman-Ford reconstructed paths are simple and realize the distances#✓fully proved

Let \(E\) be a list of integer-weighted directed edges on the vertices \(\{0, \dots, n-1\}\) and let \(s\) be a vertex. Suppose Bellman-Ford run on \(E\) from \(s\) returns distances \(d\) together with predecessor edges, and that reconstructing a path to a vertex \(t\) (following predecessor edges back from \(t\) for at most \(n\) steps until \(s\) is reached) yields the edge sequence \(P\). Then \(P\) is a walk from \(s\) to \(t\) along edges of \(E\), \(d(t)\) is finite and equals the total weight of \(P\), the vertex \(s\) and the targets of the edges of \(P\) are pairwise distinct, and \(P\) has fewer than \(n\) edges.

public theorem bellmanFord_pathTo?_spec {n : Nat} {edges : List (Edge n)}
    {source target : Fin n} {result : Distances n} {path : List (Edge n)}
    (hRun : (bellmanFord edges source).ret = Result.distances result)
    (hPath : result.pathTo? target = some path) :
    IsWeightedPath edges source target path ∧
      result.distance.get target = some (pathWeight path) ∧
      (pathVertices source path).Nodup ∧ path.length < n
Uses 10

TheoremPredecessor paths realize the distances under a decreasing rank#✓fully proved

Let \(R\) be a distances record on \(n\) vertices with source \(s\), distance table \(d\) (each \(d(v)\) is an integer or infinite) and predecessor table \(p\); let \(E\) be a list of weighted edges; and let \(r\) assign to each vertex \(v\) a natural number \(r(v) < n\). Suppose that \(d(s) = 0\), that every vertex \(v\) with \(d(v)\) infinite has no predecessor, that every vertex \(v \ne s\) with \(d(v)\) finite has a predecessor, and that whenever \(p(v) = e\), the edge \(e\) occurs in \(E\), ends at \(v\), and its start \(u\) has \(d(u)\) finite, \(d(v) = d(u) + w(e)\) where \(w(e)\) is the weight of \(e\), and \(r(u) < r(v)\). Then for every vertex \(t\), following predecessors from \(t\) back to \(s\) for at most \(n\) edges fails exactly when \(d(t)\) is infinite; and whenever it returns a path \(P\) and \(d(t)\) is finite, \(P\) is a path from \(s\) to \(t\) made of edges of \(E\), its total weight is \(d(t)\), and it has fewer than \(n\) edges.

Cslib.Algorithms.Lean.BellmanFord.Distances.pathTo?_spec_of_rank · 18 lines
public theorem Distances.pathTo?_spec_of_rank {n : Nat} (result : Distances n)
    (edges : List (Edge n)) (rank : Fin n → Nat)
    (rank_bound : ∀ vertex, rank vertex < n)
    (source_zero : result.distance.get result.source = some 0)
    (infinite_predecessor : ∀ vertex, result.distance.get vertex = none →
      result.predecessor.get vertex = none)
    (finite_predecessor : ∀ vertex distance, result.distance.get vertex = some distance →
      vertex ≠ result.source → ∃ edge, result.predecessor.get vertex = some edge)
    (predecessor_step : ∀ vertex edge, result.predecessor.get vertex = some edge →
      edge ∈ edges ∧ edge.target = vertex ∧ ∃ sourceDistance,
        result.distance.get edge.source = some sourceDistance ∧
        result.distance.get vertex = some (sourceDistance + edge.weight) ∧
        rank edge.source < rank vertex) :
    ∀ target, (result.pathTo? target = none ↔ result.distance.get target = none) ∧
      ∀ path distance, result.pathTo? target = some path →
        result.distance.get target = some distance →
        IsWeightedPath edges result.source target path ∧
        pathWeight path = distance ∧ path.length < n
Uses 5 · Used by 2

TheoremRelaxation checks of Bellman-Ford pass by pass#✓fully proved

For every list \(E\) of weighted edges on \(n\) vertices and every source \(s\), Bellman-Ford run on \(E\) from \(s\) charges exactly \((n - 1) \cdot |E| + |E|\) relaxation checks, where \(|E|\) is the length of \(E\).

The two terms count one check per edge occurrence in each of the \(n - 1\) ordinary passes and in the final pass.

public theorem bellmanFord_relaxationChecks {n : Nat} (edges : List (Edge n)) (source : Fin n) :
    (bellmanFord edges source).time.relaxationChecks =
      (n - 1) * edges.length + edges.length
Uses 9 · Used by 1

TheoremBellman-Ford makes exactly \(n \cdot |E|\) relaxation checks#✓fully proved

For every list \(E\) of weighted edges on \(n\) vertices and every source \(s\), Bellman-Ford run on \(E\) from \(s\) charges exactly \(n \cdot |E|\) relaxation checks, where \(|E|\) is the length of \(E\) counting repeated edges.

public theorem bellmanFord_relaxationChecks_eq_mul {n : Nat} (edges : List (Edge n))
    (source : Fin n) :
    (bellmanFord edges source).time.relaxationChecks = n * edges.length
Uses 5 · Used by 3

TheoremBellman-Ford spends at most \(3n\) witness steps#✓fully proved

For every list \(E\) of weighted edges on \(n\) vertices and every source \(s\), Bellman-Ford run on \(E\) from \(s\) charges at most \(3n\) negative-cycle witness reconstruction steps.

public theorem bellmanFord_cycleWitnessSteps_le {n : Nat} (edges : List (Edge n))
    (source : Fin n) :
    (bellmanFord edges source).time.cycleWitnessSteps ≤ 3 * n
Uses 9 · Used by 2

TheoremBellman-Ford spends no witness steps when it returns distances#✓fully proved

For every list \(E\) of weighted edges on \(n\) vertices and every source \(s\), if Bellman-Ford run on \(E\) from \(s\) returns distances rather than a negative cycle, then the run charges zero negative-cycle witness reconstruction steps.

public theorem bellmanFord_cycleWitnessSteps_eq_zero {n : Nat} (edges : List (Edge n))
    (source : Fin n) (result : Distances n)
    (hRun : (bellmanFord edges source).ret = Result.distances result) :
    (bellmanFord edges source).time.cycleWitnessSteps = 0
Uses 9 · Used by 2

TheoremWitness cost of a returned negative cycle#✓fully proved

For every list \(E\) of weighted edges on \(n\) vertices and every source \(s\), if Bellman-Ford run on \(E\) from \(s\) returns a reachable negative cycle whose cycle has \(c\) edges and whose path from \(s\) to the cycle has \(q\) edges, then the run charges exactly \(n + c + q\) negative-cycle witness reconstruction steps.

public theorem bellmanFord_cycleWitnessSteps_eq {n : Nat} (edges : List (Edge n))
    (source : Fin n) (witness : ReachableNegativeCycle edges source)
    (hRun : (bellmanFord edges source).ret = Result.negativeCycle witness) :
    (bellmanFord edges source).time.cycleWitnessSteps =
      n + witness.cycle.length + witness.prefix.length
Uses 9

TheoremBellman-Ford makes at most \(4 n |E|\) counted steps#✓fully proved

For every list \(E\) of weighted edges on \(n\) vertices and every source \(s\), the total cost of Bellman-Ford run on \(E\) from \(s\) (its relaxation checks plus its negative-cycle witness reconstruction steps) is at most \(4 \cdot n \cdot |E|\), where \(|E|\) is the length of \(E\).

public theorem bellmanFord_total_le {n : Nat} (edges : List (Edge n)) (source : Fin n) :
    (bellmanFord edges source).time.total ≤ 4 * n * edges.length
Uses 12
35 fully proved

The dependency explorer needs JavaScript and its graph data.

Download the complete graph data.