CubicPathwidth.Alg.Proofs.G6UnionAll
Source file CubicPathwidth/Alg/Proofs/G6UnionAll.lean of the Lean project (how it is built).
import CubicPathwidth.Alg.Spec
/-!
# G6: `UnionAllSpec`
`unionAll gs = gs.foldl addGraph empty`, where `addGraph R J` adds the nodes of `J`
(`addNode`) and then the edges of `J.edgeList` (`addEdge`). We prove:
* `addNode_spec`: `addNode` keeps well-formedness, adds exactly the new node, and does
not change adjacency;
* `edgeList_sound`, `edgeList_complete`: `J.edgeList` lists only edges of `J` and, for
every edge `x y` of `J`, the pair `(x, y)` or `(y, x)` (the earlier endpoint in
`J.nodes` emits the pair);
* `addEdges_spec`, `addGraph_spec`, `unionAll_gen`: folds of `addEdge` (using the
hypothesis `AddEdgeSpec`), of `addGraph`, and `unionAll` on a general start graph.
The theorem `unionAllSpec_of` takes `WFBasics` and `AddEdgeSpec` as hypotheses.
-/
noncomputable section
namespace CubicPathwidth.Alg
namespace G6
open Graph
/-! ## 1. `addNode` -/
theorem fresh_nbrs (G : Graph) (v : Nat) (x : Nat) :
(⟨G.nodes ++ [v], G.adj.insert v []⟩ : Graph).nbrs x
= if x = v then [] else G.nbrs x := by
show (G.adj.insert v []).getD x [] = if x = v then [] else G.adj.getD x []
rw [Std.HashMap.getD_insert]
by_cases hx : x = v
· subst hx; simp
· rw [if_neg hx]
simp [Ne.symm hx]
theorem fresh_wf (G : Graph) (hG : G.WF) (v : Nat) (hv : v ∉ G.nodes) (hnb : G.nbrs v = []) :
(⟨G.nodes ++ [v], G.adj.insert v []⟩ : Graph).WF := by
have hmem : ∀ x, x ∈ G.nodes ++ [v] ↔ x ∈ G.nodes ∨ x = v := by
intro x; simp
refine ⟨?_, ?_, ?_, ?_, ?_, ?_⟩
· show (G.nodes ++ [v]).Nodup
rw [List.nodup_append]
refine ⟨hG.nodup, by simp, ?_⟩
intro a ha b hb
simp only [List.mem_singleton] at hb
subst hb
exact fun h => hv (h ▸ ha)
· intro w
show (G.adj.insert v []).contains w = true ↔ w ∈ G.nodes ++ [v]
rw [Std.HashMap.contains_insert, hmem]
have h := hG.mem_iff w
simp only [Graph.mem] at h
rw [← h]
simp only [Bool.or_eq_true, beq_iff_eq]
tauto
· intro w
rw [fresh_nbrs]
by_cases hw : w = v
· simp [hw]
· simp only [hw, ite_false]
exact hG.nbrs_nodup w
· intro u w h
rw [fresh_nbrs] at h
rw [hmem]
by_cases hu : u = v
· simp [hu] at h
· simp only [hu, ite_false] at h
exact Or.inl (hG.nbrs_sub u w h)
· intro u w h
rw [fresh_nbrs] at h ⊢
by_cases hu : u = v
· simp [hu] at h
· simp only [hu, ite_false] at h
have hwv : w ≠ v := by
intro hwv
subst hwv
exact hv (hG.nbrs_sub u w h)
simp only [hwv, ite_false]
exact hG.symm u w h
· intro w h
rw [fresh_nbrs] at h
by_cases hw : w = v
· simp [hw] at h
· simp only [hw, ite_false] at h
exact hG.loopless w h
theorem addNode_spec (G : Graph) (hG : G.WF) (v : Nat) :
(G.addNode v).WF ∧ (∀ x, x ∈ (G.addNode v).nodes ↔ x ∈ G.nodes ∨ x = v) ∧
(∀ x y, (G.addNode v).Adj x y ↔ G.Adj x y) := by
by_cases hm : G.mem v = true
· have hv : v ∈ G.nodes := (hG.mem_iff v).1 hm
have heq : G.addNode v = G := by simp [Graph.addNode, hm]
rw [heq]
refine ⟨hG, fun x => ?_, fun x y => Iff.rfl⟩
constructor
· intro h; exact Or.inl h
· rintro (h | h)
· exact h
· rw [h]; exact hv
· have hv : v ∉ G.nodes := fun h => hm ((hG.mem_iff v).2 h)
have hnb : G.nbrs v = [] := by
unfold Graph.nbrs
apply Std.HashMap.getD_eq_fallback_of_contains_eq_false
simpa [Graph.mem] using hm
have hfresh : G.addNode v = ⟨G.nodes ++ [v], G.adj.insert v []⟩ := by
simp [Graph.addNode, hm]
rw [hfresh]
have hW := fresh_wf G hG v hv hnb
refine ⟨hW, fun x => ?_, fun x y => ?_⟩
· show x ∈ G.nodes ++ [v] ↔ _
simp
· show y ∈ (⟨G.nodes ++ [v], G.adj.insert v []⟩ : Graph).nbrs x ↔ y ∈ G.nbrs x
rw [fresh_nbrs]
by_cases hx : x = v
· rw [if_pos hx, hx, hnb]
· rw [if_neg hx]
theorem addNodes_spec (ns : List Nat) : ∀ R : Graph, R.WF →
(ns.foldl addNode R).WF ∧
(∀ x, x ∈ (ns.foldl addNode R).nodes ↔ x ∈ R.nodes ∨ x ∈ ns) ∧
(∀ x y, (ns.foldl addNode R).Adj x y ↔ R.Adj x y) := by
induction ns with
| nil => intro R hR; exact ⟨hR, fun x => by simp, fun x y => by simp⟩
| cons v ns ih =>
intro R hR
obtain ⟨h1, h2, h3⟩ := addNode_spec R hR v
obtain ⟨h4, h5, h6⟩ := ih (R.addNode v) h1
rw [List.foldl_cons]
refine ⟨h4, fun x => ?_, fun x y => ?_⟩
· rw [h5, h2]
simp only [List.mem_cons]
tauto
· rw [h6, h3]
/-! ## 2. `edgeList` -/
/-- One step of the fold defining `edgeList`, as a named function. -/
def stepE (G : Graph) (acc : Std.HashSet Nat × Array (Nat × Nat)) (u : Nat) :
Std.HashSet Nat × Array (Nat × Nat) :=
(acc.1.insert u,
(G.nbrs u).foldl (fun o v => if acc.1.contains v then o else o.push (u, v)) acc.2)
theorem edgeList_eq (G : Graph) :
G.edgeList = (G.nodes.foldl (stepE G)
((∅ : Std.HashSet Nat), (#[] : Array (Nat × Nat)))).2.toList := rfl
/-- The pairs emitted when processing the list `p` with `S` already seen (in order). -/
def E (G : Graph) : List Nat → List Nat → List (Nat × Nat)
| _, [] => []
| S, u :: p => ((G.nbrs u).filter (fun v => !S.contains v)).map (fun v => (u, v))
++ E G (u :: S) p
theorem nbrs_fold (u : Nat) (seen : Std.HashSet Nat) (S : List Nat)
(hS : ∀ z, seen.contains z = true ↔ z ∈ S) :
∀ (l : List Nat) (out : Array (Nat × Nat)),
(l.foldl (fun o v => if seen.contains v then o else o.push (u, v)) out).toList
= out.toList ++ (l.filter (fun v => !S.contains v)).map (fun v => (u, v)) := by
intro l
induction l with
| nil => intro out; simp
| cons v l ih =>
intro out
simp only [List.foldl_cons]
by_cases hv : seen.contains v = true
· have hvS : v ∈ S := (hS v).1 hv
have hvS' : (!S.contains v) = false := by simp [hvS]
rw [if_pos hv, ih, List.filter_cons, hvS']
simp
· have hvS : v ∉ S := fun h => hv ((hS v).2 h)
have hvS' : (!S.contains v) = true := by simp [hvS]
rw [if_neg hv, ih, List.filter_cons, hvS']
simp [Array.toList_push]
theorem foldl_E (G : Graph) : ∀ (p S : List Nat) (acc : Std.HashSet Nat × Array (Nat × Nat)),
(∀ z, acc.1.contains z = true ↔ z ∈ S) →
(p.foldl (stepE G) acc).2.toList = acc.2.toList ++ E G S p := by
intro p
induction p with
| nil => intro S acc _; simp [E]
| cons u p ih =>
intro S acc hS
rw [List.foldl_cons]
have h1 : (stepE G acc u).2.toList
= acc.2.toList ++ ((G.nbrs u).filter (fun v => !S.contains v)).map (fun v => (u, v)) := by
unfold stepE
exact nbrs_fold u acc.1 S hS (G.nbrs u) acc.2
have h2 : ∀ z, (stepE G acc u).1.contains z = true ↔ z ∈ u :: S := by
intro z
show (acc.1.insert u).contains z = true ↔ z ∈ u :: S
rw [Std.HashSet.contains_insert, Bool.or_eq_true, beq_iff_eq, hS z, List.mem_cons]
exact or_congr eq_comm Iff.rfl
rw [ih (u :: S) (stepE G acc u) h2, h1]
simp only [E, List.append_assoc]
theorem edgeList_eq_E (G : Graph) : G.edgeList = E G [] G.nodes := by
rw [edgeList_eq]
have := foldl_E G G.nodes [] ((∅ : Std.HashSet Nat), (#[] : Array (Nat × Nat)))
(by intro z; simp)
simpa using this
theorem E_sound (G : Graph) : ∀ (p S : List Nat) (u v : Nat),
(u, v) ∈ E G S p → u ∈ p ∧ v ∈ G.nbrs u := by
intro p
induction p with
| nil => intro S u v h; simp [E] at h
| cons w p ih =>
intro S u v h
simp only [E, List.mem_append, List.mem_map, List.mem_filter] at h
rcases h with ⟨x, ⟨hx, _⟩, hxe⟩ | h
· simp only [Prod.mk.injEq] at hxe
obtain ⟨rfl, rfl⟩ := hxe
exact ⟨List.mem_cons_self, hx⟩
· obtain ⟨h1, h2⟩ := ih (w :: S) u v h
exact ⟨List.mem_cons_of_mem w h1, h2⟩
theorem E_mem_of (G : Graph) : ∀ (a S b : List Nat) (x y : Nat),
y ∈ G.nbrs x → y ∉ S → y ∉ a → (x, y) ∈ E G S (a ++ x :: b) := by
intro a
induction a with
| nil =>
intro S b x y hy hS _
simp only [List.nil_append, E, List.mem_append, List.mem_map, List.mem_filter]
left
exact ⟨y, ⟨hy, by simp [hS]⟩, rfl⟩
| cons c a ih =>
intro S b x y hy hS hya
simp only [List.cons_append, E, List.mem_append]
right
have hc : y ≠ c := fun h => hya (by simp [h])
have hya' : y ∉ a := fun h => hya (List.mem_cons_of_mem c h)
apply ih (c :: S) b x y hy
· simp only [List.mem_cons, not_or]
exact ⟨hc, hS⟩
· exact hya'
theorem nodup_split {a b : List Nat} {x : Nat} (h : (a ++ x :: b).Nodup) :
∀ y ∈ b, y ∉ a := by
intro y hyb hya
exact (List.nodup_append.1 h).2.2 y hya y (List.mem_cons_of_mem x hyb) rfl
/-- Every listed pair is an edge of `J` between two nodes. -/
theorem edgeList_sound (J : Graph) (hJ : J.WF) (u v : Nat) (h : (u, v) ∈ J.edgeList) :
J.Adj u v ∧ u ∈ J.nodes ∧ v ∈ J.nodes := by
rw [edgeList_eq_E] at h
obtain ⟨hu, hv⟩ := E_sound J J.nodes [] u v h
exact ⟨hv, hu, hJ.nbrs_sub u v hv⟩
/-- For every edge `x y` of a well-formed graph, one of the two pairs is listed. -/
theorem edgeList_complete (J : Graph) (hJ : J.WF) (hW : WFBasics) (x y : Nat)
(hxy : J.Adj x y) : (x, y) ∈ J.edgeList ∨ (y, x) ∈ J.edgeList := by
rw [edgeList_eq_E]
have hne : x ≠ y := (hW J hJ x y hxy).2.2.2
have hy : y ∈ J.nodes := hJ.nbrs_sub x y hxy
have hyx : x ∈ J.nbrs y := hJ.symm x y hxy
have hx : x ∈ J.nodes := hJ.nbrs_sub y x hyx
obtain ⟨a, b, hab⟩ := List.append_of_mem hx
have hnd : (a ++ x :: b).Nodup := hab ▸ hJ.nodup
by_cases hyb : y ∈ b
· left
rw [hab]
exact E_mem_of J a [] b x y hxy (by simp) (nodup_split hnd y hyb)
· right
have hya : y ∈ a := by
rw [hab] at hy
rcases List.mem_append.1 hy with h | h
· exact h
· rcases List.mem_cons.1 h with h | h
· exact absurd h.symm hne
· exact absurd h hyb
obtain ⟨a1, a2, hsplit⟩ := List.append_of_mem hya
have hJ' : J.nodes = a1 ++ y :: (a2 ++ x :: b) := by
rw [hab, hsplit]; simp
have hnd' : (a1 ++ y :: (a2 ++ x :: b)).Nodup := hJ' ▸ hJ.nodup
rw [hJ']
have hxa1 : x ∉ a1 := nodup_split hnd' x (by simp)
exact E_mem_of J a1 [] (a2 ++ x :: b) y x hyx (by simp) hxa1
/-! ## 3. Folds of `addEdge` and `addGraph` -/
theorem addEdges_spec (hAdd : AddEdgeSpec) (es : List (Nat × Nat)) : ∀ R : Graph, R.WF →
(∀ e ∈ es, e.1 ≠ e.2) →
(es.foldl (fun R e => R.addEdge e.1 e.2) R).WF ∧
(∀ x, x ∈ (es.foldl (fun R e => R.addEdge e.1 e.2) R).nodes ↔
x ∈ R.nodes ∨ ∃ e ∈ es, x = e.1 ∨ x = e.2) ∧
(∀ x y, (es.foldl (fun R e => R.addEdge e.1 e.2) R).Adj x y ↔
R.Adj x y ∨ ∃ e ∈ es, (x = e.1 ∧ y = e.2 ∨ x = e.2 ∧ y = e.1)) := by
induction es with
| nil => intro R hR _; exact ⟨hR, fun x => by simp, fun x y => by simp⟩
| cons e es ih =>
intro R hR hne
obtain ⟨h1, h2, h3⟩ := hAdd R e.1 e.2 hR (hne e List.mem_cons_self)
obtain ⟨h4, h5, h6⟩ := ih (R.addEdge e.1 e.2) h1
(fun e' he' => hne e' (List.mem_cons_of_mem e he'))
refine ⟨?_, fun x => ?_, fun x y => ?_⟩
· rw [List.foldl_cons]; exact h4
· rw [List.foldl_cons, h5, h2]
simp only [List.mem_cons, exists_eq_or_imp, or_assoc]
· rw [List.foldl_cons, h6, h3]
simp only [List.mem_cons, exists_eq_or_imp, or_assoc]
theorem edgeList_adj_iff (J : Graph) (hJ : J.WF) (hW : WFBasics) (x y : Nat) :
(∃ e ∈ J.edgeList, (x = e.1 ∧ y = e.2 ∨ x = e.2 ∧ y = e.1)) ↔ J.Adj x y := by
constructor
· rintro ⟨e, he, h | h⟩
· obtain ⟨hadj, _, _⟩ := edgeList_sound J hJ e.1 e.2 he
obtain ⟨rfl, rfl⟩ := h
exact hadj
· obtain ⟨hadj, _, _⟩ := edgeList_sound J hJ e.1 e.2 he
obtain ⟨rfl, rfl⟩ := h
exact hJ.symm _ _ hadj
· intro h
rcases edgeList_complete J hJ hW x y h with h1 | h1
· exact ⟨(x, y), h1, Or.inl ⟨rfl, rfl⟩⟩
· exact ⟨(y, x), h1, Or.inr ⟨rfl, rfl⟩⟩
theorem addGraph_spec (hAdd : AddEdgeSpec) (hW : WFBasics) (R J : Graph) (hR : R.WF)
(hJ : J.WF) :
(addGraph R J).WF ∧ (∀ x, x ∈ (addGraph R J).nodes ↔ x ∈ R.nodes ∨ x ∈ J.nodes) ∧
(∀ x y, (addGraph R J).Adj x y ↔ R.Adj x y ∨ J.Adj x y) := by
obtain ⟨h1, h2, h3⟩ := addNodes_spec J.nodes R hR
have hne : ∀ e ∈ J.edgeList, e.1 ≠ e.2 := by
intro e he
exact (hW J hJ e.1 e.2 (edgeList_sound J hJ e.1 e.2 he).1).2.2.2
obtain ⟨h4, h5, h6⟩ := addEdges_spec hAdd J.edgeList _ h1 hne
unfold Graph.addGraph
refine ⟨h4, fun x => ?_, fun x y => ?_⟩
· rw [h5, h2]
constructor
· rintro (h | ⟨e, he, h | h⟩)
· exact h
· right; rw [h]; exact (edgeList_sound J hJ e.1 e.2 he).2.1
· right; rw [h]; exact (edgeList_sound J hJ e.1 e.2 he).2.2
· rintro (h | h)
· exact Or.inl (Or.inl h)
· exact Or.inl (Or.inr h)
· rw [h6, h3]
constructor
· rintro (h | h)
· exact Or.inl h
· exact Or.inr ((edgeList_adj_iff J hJ hW x y).1 h)
· rintro (h | h)
· exact Or.inl h
· exact Or.inr ((edgeList_adj_iff J hJ hW x y).2 h)
theorem unionAll_gen (hAdd : AddEdgeSpec) (hW : WFBasics) (gs : List Graph) :
∀ R : Graph, R.WF → (∀ J ∈ gs, J.WF) →
(gs.foldl addGraph R).WF ∧
(∀ x, x ∈ (gs.foldl addGraph R).nodes ↔ x ∈ R.nodes ∨ ∃ J ∈ gs, x ∈ J.nodes) ∧
(∀ x y, (gs.foldl addGraph R).Adj x y ↔ R.Adj x y ∨ ∃ J ∈ gs, J.Adj x y) := by
induction gs with
| nil => intro R hR _; exact ⟨hR, fun x => by simp, fun x y => by simp⟩
| cons J gs ih =>
intro R hR hgs
obtain ⟨h1, h2, h3⟩ := addGraph_spec hAdd hW R J hR (hgs J List.mem_cons_self)
obtain ⟨h4, h5, h6⟩ := ih (addGraph R J) h1 (fun J' hJ' => hgs J' (List.mem_cons_of_mem J hJ'))
refine ⟨?_, fun x => ?_, fun x y => ?_⟩
· rw [List.foldl_cons]; exact h4
· rw [List.foldl_cons, h5, h2]
simp only [List.mem_cons, exists_eq_or_imp, or_assoc]
· rw [List.foldl_cons, h6, h3]
simp only [List.mem_cons, exists_eq_or_imp, or_assoc]
theorem empty_wf : (Graph.empty).WF := by
refine ⟨?_, ?_, ?_, ?_, ?_, ?_⟩
· exact List.nodup_nil
· intro v; simp [Graph.empty, Graph.mem]
· intro v; simp [Graph.empty, Graph.nbrs]
· intro u v h; simp [Graph.empty, Graph.nbrs] at h
· intro u v h; simp [Graph.empty, Graph.nbrs] at h
· intro v h; simp [Graph.empty, Graph.nbrs] at h
end G6
/-- G6: `unionAll` satisfies `UnionAllSpec` (from the hypotheses `WFBasics` and
`AddEdgeSpec`, proved in other files). -/
theorem unionAllSpec_of (hWF : WFBasics) (hAdd : AddEdgeSpec) : UnionAllSpec := by
unfold UnionAllSpec
intro gs hgs
obtain ⟨h1, h2, h3⟩ := G6.unionAll_gen hAdd hWF gs Graph.empty G6.empty_wf hgs
unfold Graph.unionAll
refine ⟨h1, fun x => ?_, fun x y => ?_⟩
· rw [h2 x]; simp [Graph.empty]
· rw [h3 x y]; simp [Graph.empty, Graph.Adj, Graph.nbrs]
end CubicPathwidth.Alg
#print axioms CubicPathwidth.Alg.unionAllSpec_of
Back to top