CubicPathwidth.Alg.Proofs2.L3Connected
Source file CubicPathwidth/Alg/Proofs2/L3Connected.lean of the Lean project (how it is built).
import CubicPathwidth.Alg.Plan2
import CubicPathwidth.Alg.Proofs.G5Components
/-!
# L3: `components` is connected (`ComponentsConnected`, Plan2 §6.1)
Route. For a component found from the start `v` (a `some r` branch), every element is
reachable from `v` (`G5.reachAux_spec`), so two elements are connected through `v`
(symmetry of `Adj`). The `none` branch is unreachable for a well-formed graph:
1. `unseen G s` counts the nodes not in `s`. One pop of `reachAux` with the neighbour fold
conserves `stack.length + unseen seen`, except that the pop removes one stack entry.
This is `fold_count` and `unseen_insert`.
2. Hence `reachAux_some`: if `stack.length + unseen seen ≤ fuel`, the search returns.
The start `[v]`, `{v}` has measure `≤ 1 + numNodes`, so fuel `numNodes + 1` suffices.
3. `go_conn`: every component in the output of `components.go` is connected.
Helpers live in `CubicPathwidth.Alg.L3`.
-/
namespace CubicPathwidth.Alg
open Graph
namespace L3
/-- Number of nodes outside the set `s`. -/
def unseen (G : Graph) (s : Std.HashSet Nat) : Nat :=
(G.nodes.filter (fun x => !s.contains x)).length
theorem unseen_le (G : Graph) (s : Std.HashSet Nat) : unseen G s ≤ G.numNodes :=
List.length_filter_le _ _
/-- Inserting a new element of a duplicate-free list lowers the count by one. -/
theorem countIns (y : Nat) (s : Std.HashSet Nat) : ∀ (L : List Nat), L.Nodup → y ∈ L →
s.contains y = false →
(L.filter (fun x => !(s.insert y).contains x)).length + 1 =
(L.filter (fun x => !s.contains x)).length := by
intro L
induction L with
| nil => intro _ hy _; exact absurd hy (by simp)
| cons a L ih =>
intro hnd hy hs
rw [List.nodup_cons] at hnd
obtain ⟨haL, hndL⟩ := hnd
by_cases hay : a = y
· subst hay
have hfilt : L.filter (fun x => !(s.insert a).contains x) =
L.filter (fun x => !s.contains x) := by
apply List.filter_congr
intro x hx
have hxa : x ≠ a := fun h => haL (h ▸ hx)
rw [Std.HashSet.contains_insert, beq_eq_false_iff_ne.2 (Ne.symm hxa), Bool.false_or]
have ha1 : (!(s.insert a).contains a) = false := by simp [Std.HashSet.contains_insert]
have ha2 : (!s.contains a) = true := by simp [hs]
simp only [List.filter_cons, ha1, ha2, hfilt]
simp
· have hyL : y ∈ L := by
rcases List.mem_cons.1 hy with h | h
· exact absurd h.symm hay
· exact h
have hIH := ih hndL hyL hs
have hpa : (!(s.insert y).contains a) = (!s.contains a) := by
rw [Std.HashSet.contains_insert, beq_eq_false_iff_ne.2 (Ne.symm hay), Bool.false_or]
simp only [List.filter_cons, hpa]
split_ifs with h
· simp only [List.length_cons]
omega
· exact hIH
theorem unseen_insert (G : Graph) (hnd : G.nodes.Nodup) (s : Std.HashSet Nat) (y : Nat)
(hy : y ∈ G.nodes) (hs : s.contains y = false) :
unseen G (s.insert y) + 1 = unseen G s :=
countIns y s G.nodes hnd hy hs
/-- One neighbour fold conserves `stack.length + unseen seen` up to the pushes, which are
exactly the new seen elements. -/
theorem fold_count (G : Graph) (hnd : G.nodes.Nodup) : ∀ (L : List Nat),
(∀ y ∈ L, y ∈ G.nodes) → ∀ (s : Std.HashSet Nat) (st : List Nat),
(L.foldl (fun (acc : Std.HashSet Nat × List Nat) y =>
if acc.1.contains y then acc else (acc.1.insert y, y :: acc.2)) (s, st)).2.length +
unseen G (L.foldl (fun (acc : Std.HashSet Nat × List Nat) y =>
if acc.1.contains y then acc else (acc.1.insert y, y :: acc.2)) (s, st)).1 =
st.length + unseen G s := by
intro L
induction L with
| nil => intro _ s st; simp
| cons y L ih =>
intro hL s st
rw [List.foldl_cons]
have hyG : y ∈ G.nodes := hL y List.mem_cons_self
have hL' : ∀ z ∈ L, z ∈ G.nodes := fun z hz => hL z (List.mem_cons_of_mem _ hz)
by_cases hc : s.contains y = true
· simp only [hc, ite_true]
exact ih hL' s st
· have hc' : s.contains y = false := by simpa using hc
simp only [hc', Bool.false_eq_true, ite_false]
rw [ih hL' (s.insert y) (y :: st)]
have hu := unseen_insert G hnd s y hyG hc'
simp only [List.length_cons]
omega
/-- The search returns whenever the fuel covers `stack.length + unseen seen`. -/
theorem reachAux_some (G : Graph) (hG : G.WF) : ∀ (fuel : Nat) (stack : List Nat)
(seen : Std.HashSet Nat), stack.length + unseen G seen ≤ fuel →
∃ r, G.reachAux fuel stack seen = some r := by
intro fuel
induction fuel with
| zero =>
intro stack seen h
cases stack with
| nil => exact ⟨seen, G5.reachAux_nil G 0 seen⟩
| cons x rest =>
simp only [List.length_cons] at h
omega
| succ n ih =>
intro stack seen h
cases stack with
| nil => exact ⟨seen, G5.reachAux_nil G (n + 1) seen⟩
| cons x rest =>
have hL : ∀ y ∈ G.nbrs x, y ∈ G.nodes := fun y hy => hG.nbrs_sub x y hy
have hf := fold_count G hG.nodup (G.nbrs x) hL seen rest
simp only [List.length_cons] at h
rw [Graph.reachAux]
exact ih _ _ (by omega)
end L3
open L3 in
/-- Every component found by `components.go` from a start `v` has a root connected to all of
its elements (for a well-formed graph; the `none` branch is excluded). -/
theorem go_conn (G : Graph) (hG : G.WF) : ∀ (vs : List Nat) (assigned : Std.HashSet Nat)
(acc : List (List Nat)),
(∀ c ∈ acc, ∃ w ∈ c, ∀ x ∈ c, Relation.ReflTransGen G.Adj w x) →
(∀ x ∈ vs, x ∈ G.nodes) →
∀ c ∈ components.go G vs assigned acc, ∃ w ∈ c, ∀ x ∈ c, Relation.ReflTransGen G.Adj w x := by
intro vs
induction vs with
| nil =>
intro assigned acc hacc _ c hc
rw [components.go] at hc
exact hacc c (List.mem_reverse.1 hc)
| cons v vs ih =>
intro assigned acc hacc hvs c hc
rw [components.go] at hc
have hv : v ∈ G.nodes := hvs v List.mem_cons_self
have hvs' : ∀ x ∈ vs, x ∈ G.nodes := fun x hx => hvs x (List.mem_cons_of_mem _ hx)
by_cases hA : assigned.contains v = true
· simp only [hA, ite_true] at hc
exact ih assigned acc hacc hvs' c hc
· rw [if_neg hA] at hc
split at hc
· rename_i r hr
obtain ⟨hr1, hr2, _⟩ :=
G5.reachAux_spec G v (G.numNodes + 1) [v] _ r (G5.searchInv_init G v) hr
have hsv : ((∅ : Std.HashSet Nat).insert v).contains v = true :=
(G5.contains_single v v).2 rfl
have hvc : v ∈ G.nodes.filter r.contains :=
List.mem_filter.2 ⟨hv, hr2 v hsv⟩
have key : ∀ c' ∈ (G.nodes.filter r.contains) :: acc,
∃ w ∈ c', ∀ x ∈ c', Relation.ReflTransGen G.Adj w x := by
intro c' hc'
rcases List.mem_cons.1 hc' with h | h
· rw [h]
exact ⟨v, hvc, fun x hx => hr1 x (List.mem_filter.1 hx).2⟩
· exact hacc c' h
exact ih _ _ key hvs' c hc
· rename_i hr
exfalso
have hb : ([v] : List Nat).length + unseen G ((∅ : Std.HashSet Nat).insert v) ≤
G.numNodes + 1 := by
have := unseen_le G ((∅ : Std.HashSet Nat).insert v)
simp only [List.length_singleton]
omega
obtain ⟨r, hr'⟩ := reachAux_some G hG (G.numNodes + 1) [v]
((∅ : Std.HashSet Nat).insert v) hb
rw [hr] at hr'
exact absurd hr' (by simp)
/-- `ComponentsConnected` (Plan2 §6.1): every component of a well-formed graph is connected. -/
theorem componentsConnected : ComponentsConnected := by
intro G hG c hc x hx y hy
have hc' : c ∈ components.go G G.nodes ∅ [] := hc
have hsymm : ∀ u w, G.Adj u w → G.Adj w u := fun u w h => hG.symm u w h
obtain ⟨v, _, hall⟩ := go_conn G hG G.nodes ∅ [] (by simp) (fun x hx => hx) c hc'
exact (G5.rtg_symm G hsymm (hall x hx)).trans (hall y hy)
#print axioms CubicPathwidth.Alg.componentsConnected
Back to top