Breadth-first search
StructureResult of breadth-first search#✓fully proved
A breadth-first search result on a vertex type \(V\) consists of a distance function, assigning to each vertex either a natural number or none, and a list of vertices. They are meant to record the shortest distance from the source (none when the vertex is unreachable) and the vertices in first-discovery order.
structure BFSResult (V : Type u) where
/-- The shortest distance from the source, or `none` when unreachable. -/
distance : V → Option Nat
/-- Vertices in first-discovery order. -/
order : List (V)
Used by 37
DefinitionList model of breadth-first search#✓fully proved
Given a finite directed graph \(G\) with ordered adjacency lists and a source vertex \(s\), the list model keeps a list of processed pairs and a queue of pairs \((v, d)\) of a vertex and a distance, starting with no processed pairs and the queue \([(s, 0)]\). It performs one round per vertex of \(G\): if the queue is empty, nothing changes; otherwise the front pair \((u, d)\) is removed from the queue and recorded as processed, and then each entry \(w\) of the adjacency list of \(u\), from left to right, is appended to the queue as \((w, d + 1)\) unless \(w\) already occurs in a processed or queued pair. The reported distance of a vertex \(v\) is the distance in the first pair for \(v\) in the list of processed pairs in processing order followed by the queue, or none if there is no such pair; the reported order lists the processed vertices in processing order.
The model can be evaluated by kernel reduction, which is used to check concrete results.
def bfsModel (g : DirectedAdjacency V) (s : V) : BFSResult V :=
let initial : List (V × Nat) × List (V × Nat) := ([], [(s, 0)])
let final := g.vertices.foldr (fun _ state =>
match state.2 with
| [] => state
| entry :: queue =>
(g.adjacency entry.1).foldl (fun state vertex =>
if ((state.1.reverse ++ state.2).map Prod.fst).contains vertex then state
else (state.1, state.2 ++ [(vertex, entry.2 + 1)]))
(entry :: state.1, queue)) initial
let entries := final.1.reverse ++ final.2
{ distance := fun v => (entries.find? fun entry => entry.1 == v).map Prod.snd
order := final.1.reverse.map Prod.fst }
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
structure BFSResult (V : Type u) where
/-- The shortest distance from the source, or `none` when unreachable. -/
distance : V → Option Nat
/-- Vertices in first-discovery order. -/
order : List (V)
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch.lean:496
Uses 2 · Used by 1
DefinitionBreadth-first search#✓fully proved
Given a finite directed graph \(G\) with ordered adjacency lists and a source vertex \(s\), breadth-first search keeps a FIFO queue of pairs \((v, d)\) of a vertex and a distance and a table of discovered vertices with their distances, starting with the queue \([(s, 0)]\) and \(s\) discovered at distance \(0\). It performs one round per vertex of \(G\): if the queue is empty, nothing changes; otherwise it dequeues the front pair \((u, d)\), records \(u\) as processed, and scans the adjacency list of \(u\) from left to right, enqueueing each entry \(w\) that is not yet discovered as \((w, d + 1)\) and discovering it at distance \(d + 1\). The result reports for each vertex its discovered distance, or none if it was never discovered, and lists the processed vertices in processing order.
The table is a hash map, so the search runs in expected time linear in the number of vertices plus the total length of the adjacency lists.
Cslib.Algorithms.Lean.BreadthFirstSearch.bfs · 16 lines
def bfs (g : DirectedAdjacency V) (s : V) : BFSResult V :=
let initial : List (V × Nat) × (Std.Queue (V × Nat) × Std.HashMap V Nat) :=
([], ((Std.Queue.empty : Std.Queue (V × Nat)).enqueue (s, 0),
(Std.HashMap.emptyWithCapacity 1).insert s 0))
let final := Nat.fold g.vertices.length (fun _ _ state =>
match state.2.1.dequeue? with
| none => state
| some (entry, queue) =>
(g.adjacency entry.1).foldl (fun state vertex =>
if state.2.2.contains vertex then state
else (state.1,
(state.2.1.enqueue (vertex, entry.2 + 1),
state.2.2.insert vertex (entry.2 + 1))))
(entry :: state.1, (queue, state.2.2))) initial
{ distance := fun v => final.2.2[v]?
order := final.1.reverse.map Prod.fst }
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
structure BFSResult (V : Type u) where
/-- The shortest distance from the source, or `none` when unreachable. -/
distance : V → Option Nat
/-- Vertices in first-discovery order. -/
order : List (V)
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch.lean:496
Uses 2 · Used by 30
TheoremThe executable BFS agrees with the list model#✓fully proved
For every finite directed graph \(G\) with ordered adjacency lists and every source vertex \(s\), the result of the executable breadth-first search from \(s\) equals the result of the list model of breadth-first search from \(s\): both report the same distance (or none) for every vertex and the same first-discovery order.
theorem bfs_eq_bfsModel (g : DirectedAdjacency V) (s : V) :
bfs g s = bfsModel g s
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
def bfs (g : DirectedAdjacency V) (s : V) : BFSResult V :=
let initial : List (V × Nat) × (Std.Queue (V × Nat) × Std.HashMap V Nat) :=
([], ((Std.Queue.empty : Std.Queue (V × Nat)).enqueue (s, 0),
(Std.HashMap.emptyWithCapacity 1).insert s 0))
let final := Nat.fold g.vertices.length (fun _ _ state =>
match state.2.1.dequeue? with
| none => state
| some (entry, queue) =>
(g.adjacency entry.1).foldl (fun state vertex =>
if state.2.2.contains vertex then state
else (state.1,
(state.2.1.enqueue (vertex, entry.2 + 1),
⋯
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch.lean:935
def bfsModel (g : DirectedAdjacency V) (s : V) : BFSResult V :=
let initial : List (V × Nat) × List (V × Nat) := ([], [(s, 0)])
let final := g.vertices.foldr (fun _ state =>
match state.2 with
| [] => state
| entry :: queue =>
(g.adjacency entry.1).foldl (fun state vertex =>
if ((state.1.reverse ++ state.2).map Prod.fst).contains vertex then state
else (state.1, state.2 ++ [(vertex, entry.2 + 1)]))
(entry :: state.1, queue)) initial
let entries := final.1.reverse ++ final.2
{ distance := fun v => (entries.find? fun entry => entry.1 == v).map Prod.snd
⋯
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch.lean:504
Uses 10
DefinitionBreadth-first search on the vertices \(0, \dots, n-1\)#✓fully proved
Given a natural number \(n\), an adjacency function assigning to each vertex \(i < n\) an ordered list of vertices among \(0, \dots, n-1\), and a source vertex \(s < n\), this is the result of breadth-first search from \(s\) on the finite directed graph with vertex enumeration \(0, 1, \dots, n-1\) and these adjacency lists.
def bfsFin {n : Nat} (adjacency : Fin n → List (Fin n)) (source : Fin n) :
BFSResult (Fin n) :=
bfs
{ vertices := List.finRange n
vertices_nodup := List.nodup_finRange n
vertices_complete := List.mem_finRange
adjacency }
source
structure BFSResult (V : Type u) where
/-- The shortest distance from the source, or `none` when unreachable. -/
distance : V → Option Nat
/-- Vertices in first-discovery order. -/
order : List (V)
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch.lean:496
def bfs (g : DirectedAdjacency V) (s : V) : BFSResult V :=
let initial : List (V × Nat) × (Std.Queue (V × Nat) × Std.HashMap V Nat) :=
([], ((Std.Queue.empty : Std.Queue (V × Nat)).enqueue (s, 0),
(Std.HashMap.emptyWithCapacity 1).insert s 0))
let final := Nat.fold g.vertices.length (fun _ _ state =>
match state.2.1.dequeue? with
| none => state
| some (entry, queue) =>
(g.adjacency entry.1).foldl (fun state vertex =>
if state.2.2.contains vertex then state
else (state.1,
(state.2.1.enqueue (vertex, entry.2 + 1),
⋯
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch.lean:935
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:40
Uses 3
TheoremBFS reports a distance exactly for the reachable vertices#✓fully proved
For every finite directed graph \(G\) with ordered adjacency lists and all vertices \(s\) and \(v\), breadth-first search from \(s\) reports some distance for \(v\) (rather than none) if and only if \(v\) is reachable from \(s\) in \(G\).
theorem distance_present_iff_reachable (g : DirectedAdjacency V) (s v : V) :
(∃ d, (bfs g s).distance v = some d) ↔ g.Reachable s v
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
def bfs (g : DirectedAdjacency V) (s : V) : BFSResult V :=
let initial : List (V × Nat) × (Std.Queue (V × Nat) × Std.HashMap V Nat) :=
([], ((Std.Queue.empty : Std.Queue (V × Nat)).enqueue (s, 0),
(Std.HashMap.emptyWithCapacity 1).insert s 0))
let final := Nat.fold g.vertices.length (fun _ _ state =>
match state.2.1.dequeue? with
| none => state
| some (entry, queue) =>
(g.adjacency entry.1).foldl (fun state vertex =>
if state.2.2.contains vertex then state
else (state.1,
(state.2.1.enqueue (vertex, entry.2 + 1),
⋯
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch.lean:935
Uses 11 · Used by 6
TheoremBFS lists exactly the reachable vertices#✓fully proved
For every finite directed graph \(G\) with ordered adjacency lists and all vertices \(s\) and \(v\), the vertex \(v\) occurs in the first-discovery order of breadth-first search from \(s\) if and only if \(v\) is reachable from \(s\) in \(G\).
theorem order_mem_iff_reachable (g : DirectedAdjacency V) (s v : V) :
v ∈ (bfs g s).order ↔ g.Reachable s v
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
def bfs (g : DirectedAdjacency V) (s : V) : BFSResult V :=
let initial : List (V × Nat) × (Std.Queue (V × Nat) × Std.HashMap V Nat) :=
([], ((Std.Queue.empty : Std.Queue (V × Nat)).enqueue (s, 0),
(Std.HashMap.emptyWithCapacity 1).insert s 0))
let final := Nat.fold g.vertices.length (fun _ _ state =>
match state.2.1.dequeue? with
| none => state
| some (entry, queue) =>
(g.adjacency entry.1).foldl (fun state vertex =>
if state.2.2.contains vertex then state
else (state.1,
(state.2.1.enqueue (vertex, entry.2 + 1),
⋯
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch.lean:935
Uses 11 · Used by 7
TheoremBFS discovers each vertex at most once#✓fully proved
For every finite directed graph \(G\) with ordered adjacency lists and every vertex \(s\), no vertex occurs twice in the first-discovery order of breadth-first search from \(s\).
theorem order_nodup (g : DirectedAdjacency V) (s : V) :
(bfs g s).order.Nodup
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
def bfs (g : DirectedAdjacency V) (s : V) : BFSResult V :=
let initial : List (V × Nat) × (Std.Queue (V × Nat) × Std.HashMap V Nat) :=
([], ((Std.Queue.empty : Std.Queue (V × Nat)).enqueue (s, 0),
(Std.HashMap.emptyWithCapacity 1).insert s 0))
let final := Nat.fold g.vertices.length (fun _ _ state =>
match state.2.1.dequeue? with
| none => state
| some (entry, queue) =>
(g.adjacency entry.1).foldl (fun state vertex =>
if state.2.2.contains vertex then state
else (state.1,
(state.2.1.enqueue (vertex, entry.2 + 1),
⋯
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch.lean:935
Uses 9 · Used by 1
TheoremBFS reports exactly the shortest walk length#✓fully proved
For every finite directed graph \(G\) with ordered adjacency lists, all vertices \(s\) and \(v\), and every natural number \(d\), breadth-first search from \(s\) reports distance \(d\) for \(v\) if and only if there is a walk of exactly \(d\) edges from \(s\) to \(v\) in \(G\) and \(d \le k\) for every natural number \(k\) such that there is a walk of exactly \(k\) edges from \(s\) to \(v\).
theorem distance_eq_some_iff_shortest (g : DirectedAdjacency V) (s v : V) (d : Nat) :
(bfs g s).distance v = some d ↔
Walk g d s v ∧ ∀ k, Walk g k s v → d ≤ k
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
def bfs (g : DirectedAdjacency V) (s : V) : BFSResult V :=
let initial : List (V × Nat) × (Std.Queue (V × Nat) × Std.HashMap V Nat) :=
([], ((Std.Queue.empty : Std.Queue (V × Nat)).enqueue (s, 0),
(Std.HashMap.emptyWithCapacity 1).insert s 0))
let final := Nat.fold g.vertices.length (fun _ _ state =>
match state.2.1.dequeue? with
| none => state
| some (entry, queue) =>
(g.adjacency entry.1).foldl (fun state vertex =>
if state.2.2.contains vertex then state
else (state.1,
(state.2.1.enqueue (vertex, entry.2 + 1),
⋯
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch.lean:935
abbrev DirectedAdjacency.Walk {V : Type u} (graph : DirectedAdjacency V)
(length : Nat) (source target : V) : Prop :=
Relation.RelatesInSteps graph.Edge source target length
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:48
Uses 11 · Used by 3
TheoremBreadth-first search places the source at distance zero#✓fully proved
For every finite directed graph \(G\) given by ordered adjacency lists and every vertex \(s\) of \(G\), breadth-first search in \(G\) from \(s\) reports a distance for \(s\) itself, and that distance is \(0\) edges.
theorem source_distance (g : DirectedAdjacency V) (s : V) :
(bfs g s).distance s = some 0
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
def bfs (g : DirectedAdjacency V) (s : V) : BFSResult V :=
let initial : List (V × Nat) × (Std.Queue (V × Nat) × Std.HashMap V Nat) :=
([], ((Std.Queue.empty : Std.Queue (V × Nat)).enqueue (s, 0),
(Std.HashMap.emptyWithCapacity 1).insert s 0))
let final := Nat.fold g.vertices.length (fun _ _ state =>
match state.2.1.dequeue? with
| none => state
| some (entry, queue) =>
(g.adjacency entry.1).foldl (fun state vertex =>
if state.2.2.contains vertex then state
else (state.1,
(state.2.1.enqueue (vertex, entry.2 + 1),
⋯
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch.lean:935
TheoremBFS discovers vertices in nondecreasing distance#✓fully proved
For every finite directed graph \(G\) with ordered adjacency lists and every vertex \(s\), the distances reported by breadth-first search from \(s\), listed along its first-discovery order, form a nondecreasing sequence, where a vertex without a reported distance counts as \(0\).
theorem order_distances_nondecreasing (g : DirectedAdjacency V) (s : V) :
(((bfs g s).order.map fun v => (bfs g s).distance v |>.getD 0).Pairwise (· ≤ ·))
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
def bfs (g : DirectedAdjacency V) (s : V) : BFSResult V :=
let initial : List (V × Nat) × (Std.Queue (V × Nat) × Std.HashMap V Nat) :=
([], ((Std.Queue.empty : Std.Queue (V × Nat)).enqueue (s, 0),
(Std.HashMap.emptyWithCapacity 1).insert s 0))
let final := Nat.fold g.vertices.length (fun _ _ state =>
match state.2.1.dequeue? with
| none => state
| some (entry, queue) =>
(g.adjacency entry.1).foldl (fun state vertex =>
if state.2.2.contains vertex then state
else (state.1,
(state.2.1.enqueue (vertex, entry.2 + 1),
⋯
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch.lean:935
Uses 9
StructureBreadth-first search forest#✓fully proved
A breadth-first search forest on a vertex type \(V\) consists of a breadth-first search result, namely a partial distance function from \(V\) to the natural numbers and a list of vertices, together with a partial predecessor function from \(V\) to \(V\). For a search from a source vertex \(s\), these are meant to give the distance from \(s\) of each reachable vertex, the reachable vertices in first-discovery order, and the vertex from which each reachable vertex other than \(s\) was first discovered, with \(s\) and the unreachable vertices having no predecessor; the structure itself imposes none of these properties.
public structure BFSForest (V : Type u) extends BFSResult V where
/-- The source and unreachable vertices have no predecessor. -/
predecessor : V → Option V
structure BFSResult (V : Type u) where
/-- The shortest distance from the source, or `none` when unreachable. -/
distance : V → Option Nat
/-- Vertices in first-discovery order. -/
order : List (V)
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch.lean:496
Uses 1 · Used by 17
DefinitionBreadth-first search with parent pointers#✓fully proved
Given a finite directed graph \(G\) on a vertex type \(V\), with ordered adjacency lists, and a source vertex \(s\), this runs first-in-first-out breadth-first search from \(s\) for \(|V|\) rounds. The queue starts with \(s\) at distance \(0\). Each round tries to dequeue a vertex \(u\); if it succeeds, it scans the neighbors of \(u\) in adjacency-list order, and each neighbor not yet discovered gets distance one more than \(u\), records \(u\) as its parent and is enqueued. An already discovered vertex keeps its distance and parent. The result is the search forest: the distance of each discovered vertex, the vertices in the order they were dequeued, and the parent of each discovered vertex other than \(s\). The cost counts one unit for each dequeue attempt, each check of an adjacency entry against the discovered set, each enqueue (including that of \(s\)) and each parent write.
public def bfsWithParents {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source : V) : TimeM Nat (BFSForest V) := do
let initial ← parentInitial source
let final ← parentRun graph graph.vertices.length initial
return {
toBFSResult := {
distance := fun vertex => final.core.2.2[vertex]?
order := final.core.1.reverse.map Prod.fst }
predecessor := fun vertex => final.parents[vertex]? }
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
public structure BFSForest (V : Type u) extends BFSResult V where
/-- The source and unreachable vertices have no predecessor. -/
predecessor : V → Option V
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:38
@[no_expose] private def parentInitial {V : Type u} [BEq V] [Hashable V]
(source : V) : TimeM Nat (ParentState V) := do
✓ let queue := (Std.Queue.empty : Std.Queue (V × Nat)).enqueue (source, 0)
return ⟨([], (queue, (Std.HashMap.emptyWithCapacity 1).insert source 0)),
Std.HashMap.emptyWithCapacity 1⟩
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:76
@[no_expose] private def parentRun {V : Type u} [BEq V] [Hashable V]
(graph : DirectedAdjacency V) (fuel : Nat) (state : ParentState V) :
TimeM Nat (ParentState V) :=
Nat.fold fuel (fun _ _ computation => do
let current ← computation
parentStep graph current) (pure state)
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:69
Uses 3 · Used by 14
DefinitionPath reconstruction from BFS parents#✓fully proved
Let \(p\) be the predecessor function of a breadth-first search forest, let \(n\) be the length of its discovery order, and let \(s\) and \(t\) be vertices. Starting from \(v_k = t\), this walks back along \(p\), setting \(v_{i-1} = p(v_i)\), until it reaches a vertex \(v_0\) that tests equal to \(s\), and returns the list \(v_0, v_1, \dots, v_k\). It returns no list if the walk meets a vertex that does not test equal to \(s\) and has no predecessor, or if none of the first \(n + 1\) vertices \(t, p(t), \dots\) of the walk tests equal to \(s\). The cost counts one unit for each test against \(s\), each predecessor lookup and each vertex prepended to the list.
The bound \(n\) makes the walk terminate even when the predecessor table contains a cycle.
public def BFSForest.pathTo? {V : Type u} [BEq V]
(forest : BFSForest V) (source target : V) : TimeM Nat (Option (List V)) :=
followParents forest.predecessor source forest.order.length target []
public structure BFSForest (V : Type u) extends BFSResult V where
/-- The source and unreachable vertices have no predecessor. -/
predecessor : V → Option V
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:38
@[no_expose] private def followParents {V : Type u} [BEq V]
(predecessor : V → Option V) (source : V) :
Nat → V → List V → TimeM Nat (Option (List V))
| fuel, current, suffix => do
✓ let atSource := current == source
if atSource then
✓ return some (current :: suffix)
else
match fuel with
| 0 => return none
| fuel + 1 =>
✓ let previous := predecessor current
⋯
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:93
Uses 2 · Used by 5
TheoremBreadth-first search with parents returns the ordinary BFS result#✓fully proved
For every finite directed graph \(G\) with ordered adjacency lists and every source vertex \(s\), the output of breadth-first search with parents from \(s\) in \(G\), with its cost and its predecessor table forgotten, equals the result of ordinary FIFO breadth-first search from \(s\) in \(G\): the same distance table and the same first-discovery order.
public theorem bfsWithParents_ret {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source : V) :
(bfsWithParents graph source).ret.toBFSResult = bfs graph source
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
public def bfsWithParents {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source : V) : TimeM Nat (BFSForest V) := do
let initial ← parentInitial source
let final ← parentRun graph graph.vertices.length initial
return {
toBFSResult := {
distance := fun vertex => final.core.2.2[vertex]?
order := final.core.1.reverse.map Prod.fst }
predecessor := fun vertex => final.parents[vertex]? }
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:83
def bfs (g : DirectedAdjacency V) (s : V) : BFSResult V :=
let initial : List (V × Nat) × (Std.Queue (V × Nat) × Std.HashMap V Nat) :=
([], ((Std.Queue.empty : Std.Queue (V × Nat)).enqueue (s, 0),
(Std.HashMap.emptyWithCapacity 1).insert s 0))
let final := Nat.fold g.vertices.length (fun _ _ state =>
match state.2.1.dequeue? with
| none => state
| some (entry, queue) =>
(g.adjacency entry.1).foldl (fun state vertex =>
if state.2.2.contains vertex then state
else (state.1,
(state.2.1.enqueue (vertex, entry.2 + 1),
⋯
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch.lean:935
Uses 5 · Used by 9
TheoremExactly the source and the unreachable vertices have no BFS parent#✓fully proved
Let \(G\) be a finite directed graph with ordered adjacency lists, \(s\) a vertex, and \(F\) the predecessor forest computed by breadth-first search with parents from \(s\) in \(G\). A vertex \(v\) has no recorded predecessor in \(F\) if and only if \(v = s\) or \(v\) is not reachable from \(s\) along directed edges of \(G\).
public theorem predecessor_eq_none_iff {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source vertex : V) :
(bfsWithParents graph source).ret.predecessor vertex = none ↔
vertex = source ∨ ¬ graph.Reachable source vertex
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
public def bfsWithParents {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source : V) : TimeM Nat (BFSForest V) := do
let initial ← parentInitial source
let final ← parentRun graph graph.vertices.length initial
return {
toBFSResult := {
distance := fun vertex => final.core.2.2[vertex]?
order := final.core.1.reverse.map Prod.fst }
predecessor := fun vertex => final.parents[vertex]? }
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:83
Uses 12 · Used by 2
TheoremBFS parents are joined by graph edges#✓fully proved
Let \(G\) be a finite directed graph with ordered adjacency lists, \(s\) a vertex, and \(F\) the predecessor forest computed by breadth-first search with parents from \(s\) in \(G\). If \(F\) records \(p\) as the predecessor of a vertex \(v\), then \(G\) has a directed edge from \(p\) to \(v\), that is, \(v\) occurs in the adjacency list of \(p\).
public theorem predecessor_edge {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source vertex parent : V)
(found : (bfsWithParents graph source).ret.predecessor vertex = some parent) :
graph.Edge parent vertex
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
public def bfsWithParents {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source : V) : TimeM Nat (BFSForest V) := do
let initial ← parentInitial source
let final ← parentRun graph graph.vertices.length initial
return {
toBFSResult := {
distance := fun vertex => final.core.2.2[vertex]?
order := final.core.1.reverse.map Prod.fst }
predecessor := fun vertex => final.parents[vertex]? }
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:83
TheoremA BFS parent is one step closer to the source#✓fully proved
Let \(G\) be a finite directed graph with ordered adjacency lists, \(s\) a vertex, and \(F\) the predecessor forest computed by breadth-first search with parents from \(s\) in \(G\). If \(F\) records \(p\) as the predecessor of a vertex \(v\), then there is a natural number \(d\) such that \(F\) records distance \(d\) for \(p\) and distance \(d + 1\) for \(v\).
public theorem predecessor_distance {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source vertex parent : V)
(found : (bfsWithParents graph source).ret.predecessor vertex = some parent) :
∃ distance : Nat,
(bfsWithParents graph source).ret.distance parent = some distance ∧
(bfsWithParents graph source).ret.distance vertex = some (distance + 1)
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
public def bfsWithParents {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source : V) : TimeM Nat (BFSForest V) := do
let initial ← parentInitial source
let final ← parentRun graph graph.vertices.length initial
return {
toBFSResult := {
distance := fun vertex => final.core.2.2[vertex]?
order := final.core.1.reverse.map Prod.fst }
predecessor := fun vertex => final.parents[vertex]? }
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:83
TheoremThe BFS parent is the first discovered in-neighbour#✓fully proved
Let \(G\) be a finite directed graph with ordered adjacency lists, \(s\) a vertex, and \(F\) the predecessor forest computed by breadth-first search with parents from \(s\) in \(G\). For every vertex \(v \ne s\), the predecessor that \(F\) records for \(v\) is the first vertex \(u\), in the first-discovery order of ordinary FIFO breadth-first search from \(s\) in \(G\), whose adjacency list contains \(v\); if no vertex in that order has \(v\) in its adjacency list, \(F\) records no predecessor for \(v\).
public theorem predecessor_eq_find? {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source vertex : V) (nonsource : vertex ≠ source) :
(bfsWithParents graph source).ret.predecessor vertex =
(bfs graph source).order.find?
(fun parent => (graph.adjacency parent).contains vertex)
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
public def bfsWithParents {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source : V) : TimeM Nat (BFSForest V) := do
let initial ← parentInitial source
let final ← parentRun graph graph.vertices.length initial
return {
toBFSResult := {
distance := fun vertex => final.core.2.2[vertex]?
order := final.core.1.reverse.map Prod.fst }
predecessor := fun vertex => final.parents[vertex]? }
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:83
def bfs (g : DirectedAdjacency V) (s : V) : BFSResult V :=
let initial : List (V × Nat) × (Std.Queue (V × Nat) × Std.HashMap V Nat) :=
([], ((Std.Queue.empty : Std.Queue (V × Nat)).enqueue (s, 0),
(Std.HashMap.emptyWithCapacity 1).insert s 0))
let final := Nat.fold g.vertices.length (fun _ _ state =>
match state.2.1.dequeue? with
| none => state
| some (entry, queue) =>
(g.adjacency entry.1).foldl (fun state vertex =>
if state.2.2.contains vertex then state
else (state.1,
(state.2.1.enqueue (vertex, entry.2 + 1),
⋯
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch.lean:935
TheoremEach reachable vertex has a unique saved-parent chain#✓fully proved
Let \(G\) be a finite directed graph given by adjacency lists, and let \(s\) and \(v\) be vertices with \(v\) reachable from \(s\) in \(G\). There is exactly one list of vertices that starts at \(s\), ends at \(v\), and in which each vertex after the first has the preceding vertex as its saved parent in the breadth-first search forest of \(G\) from \(s\).
public theorem predecessor_chain_unique {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source vertex : V)
(reachable : graph.Reachable source vertex) :
∃! chain : List V, chain.IsChainFromTo
(fun parent child => (bfsWithParents graph source).ret.predecessor child = some parent)
source vertex
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
public def bfsWithParents {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source : V) : TimeM Nat (BFSForest V) := do
let initial ← parentInitial source
let final ← parentRun graph graph.vertices.length initial
return {
toBFSResult := {
distance := fun vertex => final.core.2.2[vertex]?
order := final.core.1.reverse.map Prod.fst }
predecessor := fun vertex => final.parents[vertex]? }
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:83
Uses 12
TheoremSaved-parent chains are simple#✓fully proved
Let \(G\) be a finite directed graph given by adjacency lists and let \(s\) and \(v\) be vertices. If a list of vertices starts at \(s\), ends at \(v\), and each of its vertices after the first has the preceding vertex as its saved parent in the breadth-first search forest of \(G\) from \(s\), then no vertex occurs twice in the list.
public theorem predecessor_chain_nodup {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source vertex : V) (chain : List V)
(linked : chain.IsChainFromTo
(fun parent child => (bfsWithParents graph source).ret.predecessor child = some parent)
source vertex) :
chain.Nodup
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
public def bfsWithParents {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source : V) : TimeM Nat (BFSForest V) := do
let initial ← parentInitial source
let final ← parentRun graph graph.vertices.length initial
return {
toBFSResult := {
distance := fun vertex => final.core.2.2[vertex]?
order := final.core.1.reverse.map Prod.fst }
predecessor := fun vertex => final.parents[vertex]? }
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:83
Uses 13 · Used by 3
TheoremThe saved-parent relation is acyclic#✓fully proved
Let \(G\) be a finite directed graph given by adjacency lists and let \(s\) and \(v\) be vertices. In the breadth-first search forest of \(G\) from \(s\), there is no sequence \(v = x_0, x_1, \dots, x_k = v\) with \(k \ge 1\) in which each \(x_i\) is the saved parent of \(x_{i+1}\).
public theorem predecessor_relation_acyclic {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source vertex : V) :
¬ Relation.TransGen
(fun parent child => (bfsWithParents graph source).ret.predecessor child = some parent)
vertex vertex
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
public def bfsWithParents {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source : V) : TimeM Nat (BFSForest V) := do
let initial ← parentInitial source
let final ← parentRun graph graph.vertices.length initial
return {
toBFSResult := {
distance := fun vertex => final.core.2.2[vertex]?
order := final.core.1.reverse.map Prod.fst }
predecessor := fun vertex => final.parents[vertex]? }
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:83
TheoremPath reconstruction at the source#✓fully proved
For every breadth-first search forest \(F\) and every vertex \(s\), following the saved parents of \(F\) from \(s\) back to \(s\) returns the one-vertex path \([s]\). This holds for every forest, including one whose discovery order is empty.
public theorem BFSForest.pathTo?_source {V : Type u} [BEq V] [ReflBEq V]
(forest : BFSForest V) (source : V) :
(forest.pathTo? source source).ret = some [source]
public structure BFSForest (V : Type u) extends BFSResult V where
/-- The source and unreachable vertices have no predecessor. -/
predecessor : V → Option V
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:38
Uses 3
TheoremPath reconstruction fails exactly at unreachable vertices#✓fully proved
Let \(G\) be a finite directed graph given by adjacency lists, and let \(s\) and \(v\) be vertices. Following the saved parents of the breadth-first search forest of \(G\) from \(s\) back from \(v\) towards \(s\) returns no path if and only if \(v\) is not reachable from \(s\) in \(G\).
public theorem BFSForest.pathTo?_eq_none_iff {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source vertex : V) :
((bfsWithParents graph source).ret.pathTo? source vertex).ret = none ↔
¬ graph.Reachable source vertex
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
public def bfsWithParents {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source : V) : TimeM Nat (BFSForest V) := do
let initial ← parentInitial source
let final ← parentRun graph graph.vertices.length initial
return {
toBFSResult := {
distance := fun vertex => final.core.2.2[vertex]?
order := final.core.1.reverse.map Prod.fst }
predecessor := fun vertex => final.parents[vertex]? }
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:83
Uses 17 · Used by 1
TheoremReconstructed BFS paths are shortest paths#✓fully proved
Let \(G\) be a finite directed graph given by adjacency lists, let \(s\) and \(v\) be vertices, and suppose that following the saved parents of the breadth-first search forest of \(G\) from \(s\) back from \(v\) returns a list \(P\) of vertices. Then \(P\) starts at \(s\) and ends at \(v\), each vertex of \(P\) after the first has the preceding vertex as its saved parent, \(G\) has an edge from each vertex of \(P\) to the next, no vertex occurs twice in \(P\), breadth-first search from \(s\) reports a distance \(d\) for \(v\) and \(P\) has \(d + 1\) vertices, and every list of vertices from \(s\) to \(v\) such that \(G\) has an edge from each of its vertices to the next has at least as many vertices as \(P\).
public theorem BFSForest.pathTo?_spec {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source vertex : V) (chain : List V)
(returned : ((bfsWithParents graph source).ret.pathTo? source vertex).ret = some chain) :
chain.IsChainFromTo
(fun parent child => (bfsWithParents graph source).ret.predecessor child = some parent)
source vertex ∧
chain.IsChainFromTo graph.Edge source vertex ∧ chain.Nodup ∧
(∃ distance : Nat, (bfs graph source).distance vertex = some distance ∧
chain.length = distance + 1) ∧
∀ other : List V, other.IsChainFromTo graph.Edge source vertex →
chain.length ≤ other.length
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
public def bfsWithParents {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source : V) : TimeM Nat (BFSForest V) := do
let initial ← parentInitial source
let final ← parentRun graph graph.vertices.length initial
return {
toBFSResult := {
distance := fun vertex => final.core.2.2[vertex]?
order := final.core.1.reverse.map Prod.fst }
predecessor := fun vertex => final.parents[vertex]? }
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:83
def bfs (g : DirectedAdjacency V) (s : V) : BFSResult V :=
let initial : List (V × Nat) × (Std.Queue (V × Nat) × Std.HashMap V Nat) :=
([], ((Std.Queue.empty : Std.Queue (V × Nat)).enqueue (s, 0),
(Std.HashMap.emptyWithCapacity 1).insert s 0))
let final := Nat.fold g.vertices.length (fun _ _ state =>
match state.2.1.dequeue? with
| none => state
| some (entry, queue) =>
(g.adjacency entry.1).foldl (fun state vertex =>
if state.2.2.contains vertex then state
else (state.1,
(state.2.1.enqueue (vertex, entry.2 + 1),
⋯
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch.lean:935
Uses 19
TheoremCost of path reconstruction for a reachable vertex#✓fully proved
Let \(G\) be a finite directed graph given by adjacency lists, let \(s\) and \(v\) be vertices, and suppose breadth-first search from \(s\) reports distance \(d\) for \(v\). Following the saved parents of the breadth-first search forest of \(G\) from \(s\) back from \(v\) costs exactly \(3d + 2\), counting one unit for each comparison with \(s\), each parent lookup and each vertex prepended to the path.
public theorem BFSForest.pathTo?_time {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source vertex : V) (distance : Nat)
(found : (bfs graph source).distance vertex = some distance) :
((bfsWithParents graph source).ret.pathTo? source vertex).time =
3 * distance + 2
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
def bfs (g : DirectedAdjacency V) (s : V) : BFSResult V :=
let initial : List (V × Nat) × (Std.Queue (V × Nat) × Std.HashMap V Nat) :=
([], ((Std.Queue.empty : Std.Queue (V × Nat)).enqueue (s, 0),
(Std.HashMap.emptyWithCapacity 1).insert s 0))
let final := Nat.fold g.vertices.length (fun _ _ state =>
match state.2.1.dequeue? with
| none => state
| some (entry, queue) =>
(g.adjacency entry.1).foldl (fun state vertex =>
if state.2.2.contains vertex then state
else (state.1,
(state.2.1.enqueue (vertex, entry.2 + 1),
⋯
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch.lean:935
public def bfsWithParents {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source : V) : TimeM Nat (BFSForest V) := do
let initial ← parentInitial source
let final ← parentRun graph graph.vertices.length initial
return {
toBFSResult := {
distance := fun vertex => final.core.2.2[vertex]?
order := final.core.1.reverse.map Prod.fst }
predecessor := fun vertex => final.parents[vertex]? }
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:83
Uses 15
TheoremCost of path reconstruction for an unreachable vertex#✓fully proved
Let \(G\) be a finite directed graph given by adjacency lists, and let \(s\) and \(v\) be vertices with \(v\) not reachable from \(s\) in \(G\). Following the saved parents of the breadth-first search forest of \(G\) from \(s\) back from \(v\) costs exactly \(2\), counting one unit for each comparison with \(s\), each parent lookup and each vertex prepended to the path: one comparison of \(v\) with \(s\) and one lookup of the missing parent of \(v\).
public theorem BFSForest.pathTo?_time_of_not_reachable
{V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source vertex : V)
(unreachable : ¬ graph.Reachable source vertex) :
((bfsWithParents graph source).ret.pathTo? source vertex).time = 2
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
public def bfsWithParents {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source : V) : TimeM Nat (BFSForest V) := do
let initial ← parentInitial source
let final ← parentRun graph graph.vertices.length initial
return {
toBFSResult := {
distance := fun vertex => final.core.2.2[vertex]?
order := final.core.1.reverse.map Prod.fst }
predecessor := fun vertex => final.parents[vertex]? }
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:83
TheoremExact cost of BFS with parents#✓fully proved
Let \(G\) be a finite directed graph with vertex list \(V\) and adjacency lists \(\mathrm{adj}(u)\), let \(s\) be a vertex, and let \(L\) be the list of vertices in the order in which breadth-first search from \(s\) discovers them. The cost of breadth-first search with parents from \(s\), counting dequeue attempts, adjacency checks, enqueues and parent writes, equals \(|V| + \sum_{u \in L} |\mathrm{adj}(u)| + |L| + (|L| - 1)\), where adjacency lists are counted with repetitions and \(|L| - 1\) is truncated at \(0\).
public theorem bfsWithParents_time {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source : V) :
(bfsWithParents graph source).time =
graph.vertices.length +
((bfs graph source).order.map fun vertex => (graph.adjacency vertex).length).sum +
(bfs graph source).order.length + ((bfs graph source).order.length - 1)
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
public def bfsWithParents {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source : V) : TimeM Nat (BFSForest V) := do
let initial ← parentInitial source
let final ← parentRun graph graph.vertices.length initial
return {
toBFSResult := {
distance := fun vertex => final.core.2.2[vertex]?
order := final.core.1.reverse.map Prod.fst }
predecessor := fun vertex => final.parents[vertex]? }
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:83
def bfs (g : DirectedAdjacency V) (s : V) : BFSResult V :=
let initial : List (V × Nat) × (Std.Queue (V × Nat) × Std.HashMap V Nat) :=
([], ((Std.Queue.empty : Std.Queue (V × Nat)).enqueue (s, 0),
(Std.HashMap.emptyWithCapacity 1).insert s 0))
let final := Nat.fold g.vertices.length (fun _ _ state =>
match state.2.1.dequeue? with
| none => state
| some (entry, queue) =>
(g.adjacency entry.1).foldl (fun state vertex =>
if state.2.2.contains vertex then state
else (state.1,
(state.2.1.enqueue (vertex, entry.2 + 1),
⋯
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch.lean:935
Uses 11 · Used by 1
TheoremLinear bound on the cost of BFS with parents#✓fully proved
Let \(G\) be a finite directed graph with vertex list \(V\) and adjacency lists \(\mathrm{adj}(u)\), and let \(s\) be a vertex. The cost of breadth-first search with parents from \(s\), counting dequeue attempts, adjacency checks, enqueues and parent writes, is at most \(3|V| + \sum_{u \in V} |\mathrm{adj}(u)|\), where adjacency lists are counted with repetitions.
public theorem bfsWithParents_time_le {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source : V) :
(bfsWithParents graph source).time ≤
3 * graph.vertices.length +
(graph.vertices.map fun vertex => (graph.adjacency vertex).length).sum
structure DirectedAdjacency (V : Type u) where
/-- The complete deterministic enumeration of vertices. -/
vertices : List V
/-- The vertex enumeration contains no duplicates. -/
vertices_nodup : vertices.Nodup
/-- Every vertex occurs in the enumeration. -/
vertices_complete : ∀ vertex, vertex ∈ vertices
/-- The ordered outgoing neighbors of each vertex. Entries may repeat. -/
adjacency : V → List V
CSLibExt/Algorithms/Lean/Graph/DirectedAdjacency.lean:32
public def bfsWithParents {V : Type u} [BEq V] [Hashable V] [LawfulBEq V]
(graph : DirectedAdjacency V) (source : V) : TimeM Nat (BFSForest V) := do
let initial ← parentInitial source
let final ← parentRun graph graph.vertices.length initial
return {
toBFSResult := {
distance := fun vertex => final.core.2.2[vertex]?
order := final.core.1.reverse.map Prod.fst }
predecessor := fun vertex => final.parents[vertex]? }
CSLibExt/Algorithms/Lean/Graph/BreadthFirstSearch/Parents/Internal.lean:83
Loading dependency explorer…
The dependency explorer needs JavaScript and its graph data.