CubicPathwidth.Alg.Proofs2.L1PlainBags

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

import CubicPathwidth.Alg.Plan2
import CubicPathwidth.Alg.Proofs.C1Perm
import CubicPathwidth.Alg.Proofs.C2PlainLayout

/-!
# L1: the bags of the plain sweep (`PlainLayoutBags`)

Task L1 of the formalisation. Imports `Plan2` and the Stage 1 helpers
`C1Perm` (`plainOrderPerm`) and `C2PlainLayout` (`C2.idxOf`, `C2.lastF`, `C2.bagsFrom`).
It does not import any other Stage 2 file.

Idea. `plainLayout G Y = C2.bagsFrom G (C2.idxOf order) order.length` with `order` the
nodup permutation `plainOrder G Y` of the nodes. For a node `v` with position `p`,
`v` lies in bag `i` iff `p ≤ i` and `i ≤ last v`, where `last v` is the largest position
among `v` and its neighbours. Hence `v` is in bag `i` iff `p = i` (then `v = order[i]`)
or `p < i` and some neighbour sits at a position `≥ i` (then `v ∈ take i` and that
neighbour is outside `take i`).
-/

namespace CubicPathwidth.Alg

open Graph

namespace L1

/-- The position map of a list, read off the `zipIdx` fold, for a nodup list. -/
theorem idxMap_getD (l : List Nat) (hl : l.Nodup) :
    ∀ (off : Nat) (m : Std.HashMap Nat Nat) (k : Nat),
      ((l.zipIdx off).foldl (fun m p => m.insert p.1 p.2) m).getD k 0 =
        (if k ∈ l then off + l.idxOf k else m.getD k 0) := by
  induction l with
  | nil => intro off m k; simp
  | cons a l ih =>
    intro off m k
    rw [List.nodup_cons] at hl
    obtain ⟨ha, hl⟩ := hl
    rw [List.zipIdx_cons, List.foldl_cons, ih hl (off + 1) (m.insert a off) k]
    by_cases hka : k = a
    · subst hka
      have hk : k ∉ l := ha
      simp [hk]
    · have hk : (a == k) = false := by simp [Ne.symm hka]
      simp [List.idxOf_cons, Std.HashMap.getD_insert, hk, hka]
      split_ifs <;> simp_all
      all_goals omega

/-- `C2.idxOf` of a nodup list is the position of its members. -/
theorem idxOf_mem (order : List Nat) (hnd : order.Nodup) (v : Nat) (hv : v ∈ order) :
    C2.idxOf order v = order.idxOf v := by
  unfold C2.idxOf
  rw [idxMap_getD order hnd 0 ∅ v]
  simp [hv]

/-- The position of `order[i]` is `i`. -/
theorem idxOf_getElem_pos (order : List Nat) (hnd : order.Nodup) (i : Nat)
    (hi : i < order.length) : C2.idxOf order order[i] = i := by
  rw [idxOf_mem order hnd _ (List.getElem_mem hi)]
  exact hnd.idxOf_getElem i hi

/-- Membership in a prefix, read off the position. -/
theorem mem_take_iff_idxOf (order : List Nat) (hnd : order.Nodup) (v : Nat)
    (hv : v ∈ order) (i : Nat) : v ∈ order.take i ↔ order.idxOf v < i := by
  rw [List.mem_take_iff_getElem]
  constructor
  · rintro ⟨j, hj, rfl⟩
    rw [hnd.idxOf_getElem j (lt_of_lt_of_le hj (min_le_right _ _))]
    exact lt_of_lt_of_le hj (min_le_left _ _)
  · intro h
    have hlt : order.idxOf v < order.length := List.idxOf_lt_length_of_mem hv
    refine ⟨order.idxOf v, lt_min h hlt, ?_⟩
    exact List.getElem_idxOf hlt

/-- `v = order[i]` exactly when `v` sits at position `i`. -/
theorem eq_getElem_iff_idxOf (order : List Nat) (hnd : order.Nodup) (v : Nat)
    (hv : v ∈ order) (i : Nat) (hi : i < order.length) :
    v = order[i] ↔ order.idxOf v = i := by
  constructor
  · rintro rfl
    exact hnd.idxOf_getElem i hi
  · intro h
    have hlt : order.idxOf v < order.length := List.idxOf_lt_length_of_mem hv
    subst h
    exact (List.getElem_idxOf hlt).symm

/-- Position of a bag condition through the `lastF` maximum. -/
theorem le_foldl_max_iff (f : Nat → Nat) (i : Nat) : ∀ (l : List Nat) (a : Nat),
    i ≤ l.foldl (fun m u => max m (f u)) a ↔ i ≤ a ∨ ∃ u ∈ l, i ≤ f u
  | [], a => by simp
  | u :: l, a => by
    rw [List.foldl_cons, le_foldl_max_iff f i l (max a (f u))]
    constructor
    · rintro (h | ⟨x, hx, h⟩)
      · rcases le_max_iff.1 h with h | h
        · exact Or.inl h
        · exact Or.inr ⟨u, List.mem_cons_self, h⟩
      · exact Or.inr ⟨x, List.mem_cons_of_mem _ hx, h⟩
    · rintro (h | ⟨x, hx, h⟩)
      · exact Or.inl (le_max_of_le_left h)
      · rcases List.mem_cons.1 hx with rfl | hx
        · exact Or.inl (le_max_of_le_right h)
        · exact Or.inr ⟨x, hx, h⟩

theorem le_lastF_iff (G : Graph) (idx : Nat → Nat) (i v : Nat) :
    i ≤ C2.lastF G idx v ↔ i ≤ idx v ∨ ∃ u ∈ G.nbrs v, i ≤ idx u :=
  le_foldl_max_iff idx i (G.nbrs v) (idx v)

theorem plainLayout_eq (G : Graph) (Y : Labels) :
    plainLayout G Y = C2.bagsFrom G (C2.idxOf (plainOrder G Y)) (plainOrder G Y).length :=
  rfl

/-- `PlainLayoutBags` for one graph and labeling (the two conjuncts are proved together). -/
theorem plainLayoutBags_aux (G : Graph) (hG : G.WF) (Y : Labels) :
    (plainLayout G Y).length = G.numNodes ∧
    ∀ i, i < G.numNodes → ∀ v,
      v ∈ (plainLayout G Y).getD i [] ↔
        v = (plainOrder G Y).getD i 0 ∨
          (v ∈ (plainOrder G Y).take i ∧ ∃ x ∈ G.nbrs v, x ∉ (plainOrder G Y).take i) := by
  have hperm : (plainOrder G Y).Perm G.nodes := plainOrderPerm G Y
  have hnd : (plainOrder G Y).Nodup := hperm.nodup_iff.2 hG.nodup
  have hlen : (plainOrder G Y).length = G.numNodes := hperm.length_eq
  have hmem : ∀ v, v ∈ plainOrder G Y ↔ v ∈ G.nodes := fun v => hperm.mem_iff
  have hidx : ∀ w, w ∈ plainOrder G Y → C2.idxOf (plainOrder G Y) w =
      (plainOrder G Y).idxOf w := fun w hw => idxOf_mem _ hnd w hw
  refine ⟨?_, ?_⟩
  · rw [plainLayout_eq, C2.bagsFrom, List.length_map, List.length_range, hlen]
  · intro i hi v
    have hi' : i < (plainOrder G Y).length := by rw [hlen]; exact hi
    have hget : (plainOrder G Y).getD i 0 = (plainOrder G Y)[i] := by
      simp [List.getD_eq_getElem?_getD, hi']
    have hbag : (plainLayout G Y).getD i [] =
        G.nodes.filter fun w => C2.idxOf (plainOrder G Y) w ≤ i &&
          i ≤ C2.lastF G (C2.idxOf (plainOrder G Y)) w := by
      simp [plainLayout_eq, C2.bagsFrom, List.getD_eq_getElem?_getD, hi']
    -- neighbours of a node lie in the order; they are outside `take i` iff at position `≥ i`
    have hnb : ∀ x ∈ G.nbrs v, x ∈ plainOrder G Y ∧
        (x ∉ (plainOrder G Y).take i ↔ i ≤ (plainOrder G Y).idxOf x) := by
      intro x hx
      have hxo : x ∈ plainOrder G Y := (hmem x).2 (hG.nbrs_sub v x hx)
      refine ⟨hxo, ?_⟩
      rw [mem_take_iff_idxOf _ hnd x hxo i, not_lt]
    rw [hbag, hget]
    simp only [List.mem_filter, Bool.and_eq_true, decide_eq_true_eq]
    constructor
    · rintro ⟨hvn, hle1, hle2⟩
      have hvo : v ∈ plainOrder G Y := (hmem v).2 hvn
      rw [hidx v hvo] at hle1
      rw [le_lastF_iff] at hle2
      rcases Nat.lt_or_ge ((plainOrder G Y).idxOf v) i with hlt | hge
      · right
        refine ⟨(mem_take_iff_idxOf _ hnd v hvo i).2 hlt, ?_⟩
        rcases hle2 with h | ⟨x, hx, h⟩
        · rw [hidx v hvo] at h
          omega
        · refine ⟨x, hx, ?_⟩
          rw [hidx x (hnb x hx).1] at h
          exact (hnb x hx).2.2 h
      · left
        have hvi : v = (plainOrder G Y)[i] := by
          rw [eq_getElem_iff_idxOf _ hnd v hvo i hi']
          omega
        exact hvi
    · rintro (rfl | ⟨hvt, x, hx, hxt⟩)
      · have hvo : (plainOrder G Y)[i] ∈ plainOrder G Y := List.getElem_mem hi'
        refine ⟨(hmem _).1 hvo, ?_, ?_⟩
        · rw [hidx _ hvo, hnd.idxOf_getElem i hi']
        · rw [le_lastF_iff]
          left
          rw [hidx _ hvo, hnd.idxOf_getElem i hi']
      · have hvo : v ∈ plainOrder G Y := List.mem_of_mem_take hvt
        refine ⟨(hmem v).1 hvo, ?_, ?_⟩
        · have hlt := (mem_take_iff_idxOf _ hnd v hvo i).1 hvt
          rw [hidx v hvo]
          omega
        · rw [le_lastF_iff]
          right
          refine ⟨x, hx, ?_⟩
          rw [hidx x (hnb x hx).1]
          exact (hnb x hx).2.1 hxt

end L1

/-- Task L1: the exact bags of the plain sweep (`Spec2.lean` §6.1 interface). -/
theorem plainLayoutBags : PlainLayoutBags := by
  intro G hG Y
  exact L1.plainLayoutBags_aux G hG Y

#print axioms CubicPathwidth.Alg.plainLayoutBags

end CubicPathwidth.Alg
Back to top