CubicPathwidth.Alg.Proofs.C3Connector

Source file CubicPathwidth/Alg/Proofs/C3Connector.lean of the Lean project (how it is built).

import CubicPathwidth.Alg.Spec

/-!
# C3: `ConnectorOrderMem` (set membership of `connector_order`)

Only membership is proved, so no duplicate-freeness is used.
Structure:
* `greedy_props`: a greedy order of a component `c` contains `c` and only elements of
  `c` or of items (loop invariant `present ⊆ ordRev`, `ordRev ⊆ c ∪ items`, generalised
  over the fuel, stack and state).
* `connDfs_ok`, `connOuter_ok`: the DFS and the outer scan of `connectorComponents`
  produce components inside `Qs` covering `Qs` (the fallback part is the complement of
  the assigned elements, so the cover is complete in every branch).
* `order_mem`: the output is the concatenation of the greedy orders of all components,
  taken along a permutation of the component indices (`mergeSort_perm`).
-/

namespace CubicPathwidth.Alg

namespace C3

/-! ## Basic facts -/

/-- An element of `items.getD k []` lies in some item. -/
theorem mem_getD_items {items : List (List Nat)} {k z : Nat} (h : z ∈ items.getD k []) :
    ∃ e ∈ items, z ∈ e := by
  rw [List.getD_eq_getElem?_getD] at h
  cases hk : items[k]? with
  | none => simp [hk] at h
  | some e =>
    refine ⟨e, List.mem_of_getElem? hk, ?_⟩
    simpa [hk] using h

/-- Every element of a member of `comps.getD i []` lies in `Qs`. -/
theorem comps_getD_sub {Qs : List Nat} {comps : List (List Nat)}
    (hC : ∀ c ∈ comps, ∀ y ∈ c, y ∈ Qs) (i : Nat) : ∀ y ∈ comps.getD i [], y ∈ Qs := by
  intro y hy
  rw [List.getD_eq_getElem?_getD] at hy
  cases hi : comps[i]? with
  | none => simp [hi] at hy
  | some c => exact hC c (List.mem_of_getElem? hi) y (by simpa [hi] using hy)

/-! ## Greedy order (`connectorGreedy`) -/

/-- Loop invariant of `connectorGreedy.loop`, for an arbitrary fuel, stack and state.
The output is `mk P O F` for the final state `(P, O)`; `P ⊆ O`, `O ⊆ c ∪ items`, and
the initial `ordRev` survives in `O`. -/
theorem greedy_loop (items : List (List Nat)) (inc : Std.HashMap Nat (List Nat))
    (c : List Nat) (mk : Std.HashSet Nat → List Nat → Bool → List Nat × Bool) :
    ∀ (fuel : Nat) (stack : List Nat) (present : Std.HashSet Nat) (ordRev : List Nat)
      (fl : Bool),
      (∀ z, present.contains z = true → z ∈ ordRev) →
      (∀ z ∈ ordRev, z ∈ c ∨ ∃ e ∈ items, z ∈ e) →
      ∃ P O F, connectorGreedy.loop items inc c mk fuel stack present ordRev fl = mk P O F ∧
        (∀ z, P.contains z = true → z ∈ O) ∧
        (∀ z ∈ O, z ∈ c ∨ ∃ e ∈ items, z ∈ e) ∧
        (∀ z ∈ ordRev, z ∈ O) := by
  intro fuel
  induction fuel with
  | zero =>
    intro stack present ordRev fl hI hJ
    exact ⟨present, ordRev, _, by rw [connectorGreedy.loop.eq_1], hI, hJ, fun z hz => hz⟩
  | succ f ih =>
    intro stack present ordRev fl hI hJ
    cases stack with
    | nil =>
      exact ⟨present, ordRev, _, by rw [connectorGreedy.loop], hI, hJ, fun z hz => hz⟩
    | cons k stack =>
      rw [connectorGreedy.loop]
      split
      · exact ⟨present, ordRev, _, rfl, hI, hJ, fun z hz => hz⟩
      · dsimp only
        split
        · rename_i z hfilt hlen2
          have hzmem : z ∈ items.getD k [] := by
            have : z ∈ (items.getD k []).filter (fun z => !present.contains z) := by
              rw [hfilt]; exact List.mem_singleton_self z
            exact (List.mem_filter.mp this).1
          obtain ⟨e, he, hze⟩ := mem_getD_items hzmem
          obtain ⟨P, O, F, heq, hI', hJ', hO⟩ := ih ((inc.getD z []).reverse ++ stack)
            (present.insert z) (z :: ordRev) fl
            (by
              intro w hw
              rw [Std.HashSet.contains_insert] at hw
              simp only [Bool.or_eq_true, beq_iff_eq] at hw
              rcases hw with rfl | hw
              · exact List.mem_cons_self
              · exact List.mem_cons_of_mem _ (hI w hw))
            (by
              intro w hw
              rcases List.mem_cons.mp hw with rfl | hw
              · exact Or.inr ⟨e, he, hze⟩
              · exact hJ w hw)
          exact ⟨P, O, F, heq, hI', hJ', fun w hw => hO w (List.mem_cons_of_mem _ hw)⟩
        · exact ih stack present ordRev fl hI hJ

/-- Membership facts of the greedy order of one component. -/
theorem greedy_props (items : List (List Nat)) (inc : Std.HashMap Nat (List Nat))
    (c : List Nat) :
    (∀ x ∈ c, x ∈ (connectorGreedy items inc c).1) ∧
    (∀ y ∈ (connectorGreedy items inc c).1, y ∈ c ∨ ∃ e ∈ items, y ∈ e) := by
  cases c with
  | nil => simp [connectorGreedy]
  | cons start c' =>
    have key := greedy_loop items inc (start :: c')
      (fun present ordRev fl =>
        (ordRev.reverse ++ (start :: c').filter (fun z => !present.contains z), fl))
      ((items.map List.length).sum + 1) ((inc.getD start []).reverse)
      ((∅ : Std.HashSet Nat).insert start) [start] false
      (by
        intro z hz
        rw [Std.HashSet.contains_insert, Std.HashSet.contains_empty] at hz
        simp only [Bool.or_false, beq_iff_eq] at hz
        simp [hz])
      (by
        intro z hz
        left
        rw [List.mem_singleton] at hz
        simp [hz])
    obtain ⟨P, O, F, heq, hI, hJ, _⟩ := key
    have hres : connectorGreedy items inc (start :: c') =
        (O.reverse ++ (start :: c').filter (fun z => !P.contains z), F) := by
      unfold connectorGreedy
      exact heq
    rw [hres]
    constructor
    · intro x hx
      by_cases hP : P.contains x = true
      · exact List.mem_append_left _ (List.mem_reverse.mpr (hI x hP))
      · exact List.mem_append_right _ (List.mem_filter.mpr ⟨hx, by simpa using hP⟩)
    · intro y hy
      rcases List.mem_append.mp hy with h | h
      · exact hJ y (List.mem_reverse.mp h)
      · exact Or.inl (List.mem_filter.mp h).1

/-! ## Components (`connectorComponents`) -/

/-- Postcondition of the DFS `connDfs` from the start state `(A0, M0)`: old assigned
elements stay assigned; newly assigned elements are recorded in the member list; the old
members are kept; every member lies in `all`. -/
def DfsOK (all : List Nat) (A0 : Std.HashSet Nat) (M0 : List Nat)
    (r : List Nat × Std.HashSet Nat × List Nat) : Prop :=
  (∀ w, A0.contains w = true → r.2.1.contains w = true) ∧
  (∀ w, r.2.1.contains w = true → w ∈ r.2.2 ∨ A0.contains w = true) ∧
  (∀ w ∈ M0, w ∈ r.2.2) ∧
  (∀ w ∈ r.2.2, w ∈ all)

theorem dfsOK_self {all : List Nat} {A : Std.HashSet Nat} {M : List Nat} (stk : List Nat)
    (hM : ∀ w ∈ M, w ∈ all) : DfsOK all A M (stk, A, M) :=
  ⟨fun _ hw => hw, fun _ hw => Or.inr hw, fun _ hw => hw, hM⟩

theorem dfsOK_trans {all : List Nat} {A0 : Std.HashSet Nat} {M0 : List Nat}
    {r1 r2 : List Nat × Std.HashSet Nat × List Nat}
    (h1 : DfsOK all A0 M0 r1) (h2 : DfsOK all r1.2.1 r1.2.2 r2) : DfsOK all A0 M0 r2 := by
  obtain ⟨a1, b1, c1, _⟩ := h1
  obtain ⟨a2, b2, c2, d2⟩ := h2
  refine ⟨fun w hw => a2 w (a1 w hw), fun w hw => ?_, fun w hw => c2 _ (c1 w hw), d2⟩
  rcases b2 w hw with h | h
  · exact Or.inl h
  · rcases b1 w h with h' | h'
    · exact Or.inl (c2 _ h')
    · exact Or.inr h'

/-- One inner fold step of `connDfs` over a list of candidates. -/
theorem fold_inner_ok {all : List Nat} (L : List Nat) (hL : ∀ z ∈ L, z ∈ all) :
    ∀ acc : List Nat × Std.HashSet Nat × List Nat, (∀ w ∈ acc.2.2, w ∈ all) →
      DfsOK all acc.2.1 acc.2.2 (L.foldl (fun (acc : List Nat × Std.HashSet Nat × List Nat)
        (z : Nat) => if acc.2.1.contains z then acc
          else (z :: acc.1, acc.2.1.insert z, z :: acc.2.2)) acc) := by
  induction L with
  | nil =>
    intro acc hM
    exact dfsOK_self _ hM
  | cons z L ih =>
    intro acc hM
    simp only [List.foldl_cons]
    by_cases hz : acc.2.1.contains z = true
    · simp only [hz, ↓reduceIte]
      exact ih (fun z' hz' => hL z' (List.mem_cons_of_mem _ hz')) acc hM
    · simp only [hz, Bool.false_eq_true]
      have hzall : z ∈ all := hL z (List.mem_cons_self)
      have h1 : DfsOK all acc.2.1 acc.2.2 (z :: acc.1, acc.2.1.insert z, z :: acc.2.2) := by
        refine ⟨fun w hw => ?_, fun w hw => ?_, fun w hw => List.mem_cons_of_mem _ hw, ?_⟩
        · rw [Std.HashSet.contains_insert]
          simp [hw]
        · rw [Std.HashSet.contains_insert] at hw
          simp only [Bool.or_eq_true, beq_iff_eq] at hw
          rcases hw with rfl | hw
          · exact Or.inl List.mem_cons_self
          · exact Or.inr hw
        · intro w hw
          rcases List.mem_cons.mp hw with rfl | hw
          · exact hzall
          · exact hM w hw
      have hM' : ∀ w ∈ (z :: acc.1, acc.2.1.insert z, z :: acc.2.2).2.2, w ∈ all := by
        intro w hw
        rcases List.mem_cons.mp hw with rfl | hw
        · exact hzall
        · exact hM w hw
      exact dfsOK_trans h1
        (ih (fun z' hz' => hL z' (List.mem_cons_of_mem _ hz')) _ hM')

/-- Folding over the item indices of `inc.getD y`. -/
theorem fold_items_ok {all : List Nat} (items : List (List Nat))
    (hitems : ∀ e ∈ items, ∀ z ∈ e, z ∈ all) :
    ∀ (ks : List Nat) (acc : List Nat × Std.HashSet Nat × List Nat),
      (∀ w ∈ acc.2.2, w ∈ all) →
      DfsOK all acc.2.1 acc.2.2 (ks.foldl (fun (acc : List Nat × Std.HashSet Nat × List Nat)
        (k : Nat) => (items.getD k []).foldl (fun (acc : List Nat × Std.HashSet Nat × List Nat)
          (z : Nat) => if acc.2.1.contains z then acc
            else (z :: acc.1, acc.2.1.insert z, z :: acc.2.2)) acc) acc) := by
  intro ks
  induction ks with
  | nil =>
    intro acc hM
    exact dfsOK_self _ hM
  | cons k ks ih =>
    intro acc hM
    simp only [List.foldl_cons]
    have hk : ∀ z ∈ items.getD k [], z ∈ all := by
      intro z hz
      obtain ⟨e, he, hze⟩ := mem_getD_items hz
      exact hitems e he z hze
    have h1 := fold_inner_ok (items.getD k []) hk acc hM
    exact dfsOK_trans h1 (ih _ h1.2.2.2)

/-- `connDfs` is correct for every fuel, stack and start state. -/
theorem connDfs_ok (items : List (List Nat)) (inc : Std.HashMap Nat (List Nat))
    (all : List Nat) (hitems : ∀ e ∈ items, ∀ z ∈ e, z ∈ all) :
    ∀ (fuel : Nat) (stack : List Nat) (A : Std.HashSet Nat) (M : List Nat),
      (∀ w ∈ M, w ∈ all) →
      DfsOK all A M (connectorComponents.connDfs items inc fuel stack A M) := by
  intro fuel
  induction fuel with
  | zero =>
    intro stack A M hM
    rw [connectorComponents.connDfs.eq_1]
    exact dfsOK_self _ hM
  | succ f ih =>
    intro stack A M hM
    cases stack with
    | nil =>
      rw [connectorComponents.connDfs]
      exact dfsOK_self _ hM
    | cons y rest =>
      rw [connectorComponents.connDfs]
      have h1 := fold_items_ok items hitems (inc.getD y []) (rest, A, M) hM
      exact dfsOK_trans h1 (ih _ _ _ h1.2.2.2)

/-- `connOuter` covers the remaining list `qs`, keeps the given components, and every
component lies in `all`. Induction on `qs` for an arbitrary assigned set and component
list; the fallback branch keeps the complement of the assigned elements as last part. -/
theorem connOuter_ok (items : List (List Nat)) (inc : Std.HashMap Nat (List Nat))
    (all : List Nat) (hitems : ∀ e ∈ items, ∀ z ∈ e, z ∈ all) (fuel : Nat) :
    ∀ (qs : List Nat) (A : Std.HashSet Nat) (comps : List (List Nat)),
      (∀ y ∈ qs, y ∈ all) → (∀ c ∈ comps, ∀ y ∈ c, y ∈ all) →
      (∀ y, A.contains y = true → ∃ c ∈ comps, y ∈ c) →
      (∀ c ∈ connectorComponents.connOuter items inc all fuel qs A comps, ∀ y ∈ c, y ∈ all) ∧
      (∀ y ∈ qs, ∃ c ∈ connectorComponents.connOuter items inc all fuel qs A comps, y ∈ c) ∧
      (∀ c ∈ comps, c ∈ connectorComponents.connOuter items inc all fuel qs A comps) := by
  intro qs
  induction qs with
  | nil =>
    intro A comps _ hcomps _
    rw [connectorComponents.connOuter]
    refine ⟨fun c hc y hy => hcomps c (List.mem_reverse.mp hc) y hy,
      fun y hy => by simp at hy, fun c hc => List.mem_reverse.mpr hc⟩
  | cons x qs ih =>
    intro A comps hqs hcomps hA
    have hxall : x ∈ all := hqs x List.mem_cons_self
    have hqs' : ∀ y ∈ qs, y ∈ all := fun y hy => hqs y (List.mem_cons_of_mem _ hy)
    rw [connectorComponents.connOuter.eq_2]
    by_cases hx : A.contains x = true
    · simp only [hx, ↓reduceIte]
      obtain ⟨h1, h2, h3⟩ := ih A comps hqs' hcomps hA
      refine ⟨h1, ?_, h3⟩
      intro y hy
      rcases List.mem_cons.mp hy with rfl | hy
      · obtain ⟨c, hc, hyc⟩ := hA y hx
        exact ⟨c, h3 c hc, hyc⟩
      · exact h2 y hy
    · simp only [hx, Bool.false_eq_true, ↓reduceIte]
      have hr := connDfs_ok items inc all hitems fuel [x] (A.insert x) [x]
        (by intro w hw; rw [List.mem_singleton] at hw; rw [hw]; exact hxall)
      generalize connectorComponents.connDfs items inc fuel [x] (A.insert x) [x] = r at hr ⊢
      obtain ⟨ra, rb, rc, rd⟩ := hr
      have hxr : x ∈ r.2.2 := rc x List.mem_cons_self
      -- every element assigned in `r` lies in one of the new components
      have hcov' : ∀ y, r.2.1.contains y = true →
          ∃ c ∈ (r.2.2.reverse :: comps), y ∈ c := by
        intro y hy
        rcases rb y hy with h | h
        · exact ⟨r.2.2.reverse, List.mem_cons_self, List.mem_reverse.mpr h⟩
        · rw [Std.HashSet.contains_insert] at h
          simp only [Bool.or_eq_true, beq_iff_eq] at h
          rcases h with rfl | h
          · exact ⟨r.2.2.reverse, List.mem_cons_self, List.mem_reverse.mpr hxr⟩
          · obtain ⟨c, hc, hyc⟩ := hA y h
            exact ⟨c, List.mem_cons_of_mem _ hc, hyc⟩
      have hcomps' : ∀ c ∈ (r.2.2.reverse :: comps), ∀ y ∈ c, y ∈ all := by
        intro c hc y hy
        rcases List.mem_cons.mp hc with rfl | hc
        · exact rd y (List.mem_reverse.mp hy)
        · exact hcomps c hc y hy
      by_cases he : r.1.isEmpty = true
      · simp only [he, ↓reduceIte]
        obtain ⟨h1, h2, h3⟩ := ih r.2.1 (r.2.2.reverse :: comps) hqs' hcomps' hcov'
        refine ⟨h1, ?_, fun c hc => h3 c (List.mem_cons_of_mem _ hc)⟩
        intro y hy
        rcases List.mem_cons.mp hy with rfl | hy
        · exact ⟨r.2.2.reverse, h3 _ List.mem_cons_self, List.mem_reverse.mpr hxr⟩
        · exact h2 y hy
      · simp only [he, Bool.false_eq_true, ↓reduceIte]
        generalize hrest : List.filter (fun y => !r.2.1.contains y) all = rest
        have hrest_mem : ∀ y ∈ rest, y ∈ all ∧ r.2.1.contains y = false := by
          intro y hy
          rw [← hrest, List.mem_filter] at hy
          exact ⟨hy.1, by simpa using hy.2⟩
        -- the list of components to return, before reversal
        have hP1 : ∀ c ∈ (r.2.2.reverse :: comps), c ∈ (if rest.isEmpty = true then
            (r.2.2.reverse :: comps) else rest :: (r.2.2.reverse :: comps)) := by
          intro c hc
          split
          · exact hc
          · exact List.mem_cons_of_mem _ hc
        have hP2 : ∀ c ∈ (if rest.isEmpty = true then (r.2.2.reverse :: comps)
            else rest :: (r.2.2.reverse :: comps)), c ∈ (r.2.2.reverse :: comps) ∨ c = rest := by
          intro c hc
          split at hc
          · exact Or.inl hc
          · rcases List.mem_cons.mp hc with h | h
            · exact Or.inr h
            · exact Or.inl h
        have hP3 : rest ≠ [] → rest ∈ (if rest.isEmpty = true then (r.2.2.reverse :: comps)
            else rest :: (r.2.2.reverse :: comps)) := by
          intro hne
          have : ¬ rest.isEmpty = true := by simpa [List.isEmpty_iff] using hne
          simp only [this]
          exact List.mem_cons_self
        refine ⟨?_, ?_, ?_⟩
        · intro c hc y hy
          rw [List.mem_reverse] at hc
          rcases hP2 c hc with h | h
          · exact hcomps' c h y hy
          · subst h
            exact (hrest_mem y hy).1
        · intro y hy
          have hyall := hqs y hy
          by_cases hyA : r.2.1.contains y = true
          · obtain ⟨c, hc, hyc⟩ := hcov' y hyA
            exact ⟨c, List.mem_reverse.mpr (hP1 c hc), hyc⟩
          · have hyr : y ∈ rest := by
              rw [← hrest, List.mem_filter]
              exact ⟨hyall, by simp [hyA]⟩
            exact ⟨rest, List.mem_reverse.mpr (hP3 (List.ne_nil_of_mem hyr)), hyr⟩
        · intro c hc
          rw [List.mem_reverse]
          exact hP1 c (List.mem_cons_of_mem _ hc)

/-- Components lie in `Qs` and cover `Qs` (for `items ⊆ Qs`). -/
theorem components_props (Qs : List Nat) (items : List (List Nat))
    (inc : Std.HashMap Nat (List Nat)) (hitems : ∀ e ∈ items, ∀ z ∈ e, z ∈ Qs) :
    (∀ c ∈ connectorComponents Qs items inc, ∀ y ∈ c, y ∈ Qs) ∧
    (∀ x ∈ Qs, ∃ c ∈ connectorComponents Qs items inc, x ∈ c) := by
  have h := connOuter_ok items inc Qs hitems
    (Qs.length + (items.map List.length).sum + 1) Qs ∅ []
    (fun y hy => hy) (fun c hc => by simp at hc) (fun y hy => by simp at hy)
  unfold connectorComponents
  exact ⟨h.1, fun x hx => h.2.1 x hx⟩

/-- The order: a permutation of the component indices, each contributing its greedy
order (membership form). -/
theorem order_mem (Qs : List Nat) (items : List (List Nat)) (inc : Std.HashMap Nat (List Nat))
    (comps : List (List Nat)) (hC : ∀ c ∈ comps, ∀ y ∈ c, y ∈ Qs)
    (hcov : ∀ x ∈ Qs, ∃ c ∈ comps, x ∈ c) (hitems : ∀ e ∈ items, ∀ z ∈ e, z ∈ Qs)
    (x : Nat) :
    x ∈ ((((List.range comps.length).zip (connectorExcess items comps)).mergeSort
        (fun a b => decide (a.2 ≤ b.2))).map
          (fun (p : Nat × Int) => connectorGreedy items inc (comps.getD p.1 []))).flatMap
            (fun (g : List Nat × Bool) => g.1) ↔ x ∈ Qs := by
  set exc := connectorExcess items comps with hexc
  set ordered := ((List.range comps.length).zip exc).mergeSort
    (fun a b => decide (a.2 ≤ b.2)) with hordered
  have hlen : exc.length = comps.length := by
    simp [hexc, connectorExcess]
  have hperm : (ordered.map Prod.fst).Perm (List.range comps.length) := by
    have h1 := (List.mergeSort_perm ((List.range comps.length).zip exc)
      (fun a b => decide (a.2 ≤ b.2))).map Prod.fst
    rw [List.map_fst_zip (by simp [hlen])] at h1
    exact h1
  have hmemj : ∀ j ∈ List.range comps.length, ∃ p ∈ ordered, p.1 = j := by
    intro j hj
    have hj' : j ∈ ordered.map Prod.fst := hperm.symm.mem_iff.mp hj
    obtain ⟨p, hp, hpj⟩ := List.mem_map.mp hj'
    exact ⟨p, hp, hpj⟩
  constructor
  · intro hx
    rw [List.mem_flatMap] at hx
    obtain ⟨a, ha, hxa⟩ := hx
    obtain ⟨p, _, rfl⟩ := List.mem_map.mp ha
    rcases (greedy_props items inc (comps.getD p.1 [])).2 x hxa with h | ⟨e, he, hxe⟩
    · exact comps_getD_sub hC p.1 x h
    · exact hitems e he x hxe
  · intro hx
    obtain ⟨c, hc, hxc⟩ := hcov x hx
    obtain ⟨j, hj, hcj⟩ := List.mem_iff_getElem.mp hc
    obtain ⟨p, hp, hpj⟩ := hmemj j (List.mem_range.mpr hj)
    have hgetD : comps.getD p.1 [] = c := by
      rw [hpj, List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hj]
      simp [hcj]
    rw [List.mem_flatMap]
    refine ⟨connectorGreedy items inc (comps.getD p.1 []), List.mem_map.mpr ⟨p, hp, rfl⟩, ?_⟩
    rw [hgetD]
    exact (greedy_props items inc c).1 x hxc

end C3

/-- C3: `connector_order` has exactly the elements of `Qs` (for items inside `Qs`). -/
theorem connectorOrderMem : ConnectorOrderMem := by
  unfold ConnectorOrderMem
  intro Qs items hitems x
  have hcp := C3.components_props Qs items (connectorIncidence Qs items) hitems
  unfold connectorOrder connectorOrderF
  exact C3.order_mem Qs items (connectorIncidence Qs items)
    (connectorComponents Qs items (connectorIncidence Qs items)) hcp.1 hcp.2 hitems x

end CubicPathwidth.Alg

#print axioms CubicPathwidth.Alg.connectorOrderMem
Back to top