CubicPathwidth.Alg.Proofs2.L2PlainWidth

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

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

/-!
# L2: width of the plain sweep (`PlainLayoutWidth`)

Task L2 of the formalisation. Imports `Plan2` and the Stage 1 permutation
lemma `plainOrderPerm`. It does NOT import `L1PlainBags`: the bag description is taken
as the hypothesis `hB : PlainLayoutBags`.

Idea. Let `order = plainOrder G Y` (nodes by decreasing label, injective labels). Then
`order[i]` has the largest label among the entries at positions `≥ i`, and every entry
of `order.take i` has a strictly larger label than `order[i]`. Bag `i` consists of
`order[i]` and of vertices of `take i` with a neighbour outside `take i`; each such
vertex lies above `order[i]` and has a neighbour at or below it, i.e. it is in
`outerFr G Y (Y order[i])`. So each bag has at most `1 + |outerFr|` elements.
-/

namespace CubicPathwidth.Alg

open Graph

namespace L2

/-- The comparator of `plainOrder`, named. -/
def cmp (Y : Labels) (a b : Nat) : Bool :=
  decide (Y b < Y a) || (decide (Y a = Y b) && decide (a ≤ b))

theorem cmp_trans (Y : Labels) : ∀ a b c : Nat,
    cmp Y a b = true → cmp Y b c = true → cmp Y a c = true := by
  intro a b c h1 h2
  simp only [cmp, Bool.or_eq_true, Bool.and_eq_true, decide_eq_true_eq] at h1 h2 ⊢
  rcases h1 with h1 | ⟨h1, h1'⟩ <;> rcases h2 with h2 | ⟨h2, h2'⟩
  · exact Or.inl (lt_trans h2 h1)
  · exact Or.inl (by linarith)
  · exact Or.inl (by linarith)
  · exact Or.inr ⟨h1.trans h2, Nat.le_trans h1' h2'⟩

theorem cmp_total (Y : Labels) : ∀ a b : Nat, (cmp Y a b || cmp Y b a) = true := by
  intro a b
  simp only [cmp, Bool.or_eq_true, Bool.and_eq_true, decide_eq_true_eq]
  rcases lt_trichotomy (Y a) (Y b) with h | h | h
  · exact Or.inr (Or.inl h)
  · rcases le_total a b with h' | h'
    · exact Or.inl (Or.inr ⟨h, h'⟩)
    · exact Or.inr (Or.inr ⟨h.symm, h'⟩)
  · exact Or.inl (Or.inl h)

/-- A list of naturals whose entries are all `≤ c` (real) has a maximum `≤ c`. -/
theorem foldr_max_cast_le (c : ℝ) (hc : 0 ≤ c) : ∀ l : List Nat,
    (∀ x ∈ l, (x : ℝ) ≤ c) → ((l.foldr max 0 : ℕ) : ℝ) ≤ c
  | [], _ => by simpa using hc
  | x :: l, h => by
    rw [List.foldr_cons, Nat.cast_max]
    exact max_le (h x List.mem_cons_self)
      (foldr_max_cast_le c hc l fun y hy => h y (List.mem_cons_of_mem _ hy))

/-- The core: under the bag description and the sorting, every bag has at most
`1 + |outerFr G Y (Y v)|` elements, where `v` is the vertex at its index. -/
theorem plainLayoutWidth_aux (hB : PlainLayoutBags) (G : Graph) (hG : G.WF) (Y : Labels)
    (hinj : LabelsInjOn G Y) (w : ℝ)
    (hw : ∀ t : ℚ, ((outerFr G Y t).length : ℝ) ≤ w) :
    pdWidth (plainLayout G Y) ≤ w := by
  have hw0 : 0 ≤ w := le_trans (Nat.cast_nonneg _) (hw 0)
  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 hord : plainOrder G Y = G.nodes.mergeSort (cmp Y) := rfl
  have hpair : (plainOrder G Y).Pairwise (fun a b => cmp Y a b = true) := by
    rw [hord]
    exact List.pairwise_mergeSort (cmp_trans Y) (cmp_total Y) G.nodes
  have hpw : ∀ i j (hi : i < (plainOrder G Y).length) (hj : j < (plainOrder G Y).length),
      i < j → cmp Y (plainOrder G Y)[i] (plainOrder G Y)[j] = true := fun i j hi hj hij =>
    (List.pairwise_iff_getElem.1 hpair) i j hi hj hij
  -- entries strictly before position `i` have strictly larger labels than entry `i`
  have hAbove : ∀ i (hi : i < (plainOrder G Y).length), ∀ u ∈ (plainOrder G Y).take i,
      Y ((plainOrder G Y)[i]) < Y u := by
    intro i hi u hu
    obtain ⟨j, hj, rfl⟩ := List.mem_take_iff_getElem.1 hu
    have hji : j < i := lt_of_lt_of_le hj (min_le_left _ _)
    have hjl : j < (plainOrder G Y).length := lt_of_lt_of_le hj (min_le_right _ _)
    have hc := hpw j i hjl hi hji
    simp only [cmp, Bool.or_eq_true, Bool.and_eq_true, decide_eq_true_eq] at hc
    rcases hc with hc | ⟨hc, _⟩
    · exact hc
    · exfalso
      have hmj : (plainOrder G Y)[j] ∈ G.nodes := (hmem _).1 (List.getElem_mem hjl)
      have hmi : (plainOrder G Y)[i] ∈ G.nodes := (hmem _).1 (List.getElem_mem hi)
      have heq : (plainOrder G Y)[j] = (plainOrder G Y)[i] :=
        hinj _ hmj _ hmi hc
      have := (hnd.getElem_inj_iff).1 heq
      omega
  -- entries outside `take i` have labels at most that of entry `i`
  have hBelow : ∀ i (hi : i < (plainOrder G Y).length), ∀ x ∈ G.nodes,
      x ∉ (plainOrder G Y).take i → Y x ≤ Y ((plainOrder G Y)[i]) := by
    intro i hi x hx hxt
    have hxo : x ∈ plainOrder G Y := (hmem x).2 hx
    obtain ⟨j, hj, hjx⟩ := List.mem_iff_getElem.1 hxo
    subst hjx
    by_cases hij : j < i
    · exact absurd (List.mem_take_iff_getElem.2 ⟨j, lt_min hij hj, rfl⟩) hxt
    · rcases Nat.lt_or_ge i j with h | h
      · have hc := hpw i j hi hj h
        simp only [cmp, Bool.or_eq_true, Bool.and_eq_true, decide_eq_true_eq] at hc
        rcases hc with hc | ⟨hc, _⟩
        · exact le_of_lt hc
        · exact le_of_eq hc.symm
      · have hij' : i = j := by omega
        subst hij'
        exact le_refl _
  -- the bag at index `i` has at most `1 + |outerFr|` elements
  have hbagcard : ∀ i (hi : i < G.numNodes),
      (((plainLayout G Y).getD i []).toFinset.card : ℝ) ≤ w + 1 := by
    intro i hi
    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 := (hB G hG Y).2 i hi
    rw [hget] at hbag
    have hsub : ((plainLayout G Y).getD i []).toFinset ⊆
        insert ((plainOrder G Y)[i])
          (outerFr G Y (Y ((plainOrder G Y)[i]))).toFinset := by
      intro u hu
      rw [List.mem_toFinset] at hu
      rw [Finset.mem_insert, List.mem_toFinset]
      rcases (hbag u).1 hu with h | ⟨hut, x, hx, hxt⟩
      · exact Or.inl h
      · right
        have hun : u ∈ G.nodes := (hmem u).1 (List.mem_of_mem_take hut)
        simp only [outerFr, List.mem_filter, Bool.and_eq_true, decide_eq_true_eq,
          List.any_eq_true]
        exact ⟨hun, hAbove i hi' u hut, x, hx, hBelow i hi' x (hG.nbrs_sub u x hx) hxt⟩
    have hc1 := Finset.card_le_card hsub
    have hc2 := Finset.card_insert_le ((plainOrder G Y)[i])
      (outerFr G Y (Y ((plainOrder G Y)[i]))).toFinset
    have hc3 := List.toFinset_card_le (outerFr G Y (Y ((plainOrder G Y)[i])))
    have hw' := hw (Y ((plainOrder G Y)[i]))
    have hnat : ((plainLayout G Y).getD i []).toFinset.card ≤
        (outerFr G Y (Y ((plainOrder G Y)[i]))).length + 1 := by omega
    have hreal : (((plainLayout G Y).getD i []).toFinset.card : ℝ) ≤
        ((outerFr G Y (Y ((plainOrder G Y)[i]))).length : ℝ) + 1 := by
      exact_mod_cast hnat
    linarith
  -- all bags
  have hmax : (maxBagCard (plainLayout G Y) : ℝ) ≤ w + 1 := by
    unfold maxBagCard
    apply foldr_max_cast_le (w + 1) (by linarith)
    intro x hx
    obtain ⟨B, hB1, hxB⟩ := List.mem_map.1 hx
    obtain ⟨i, hi, hiB⟩ := List.mem_iff_getElem.1 hB1
    have hi2 : i < G.numNodes := by
      rw [(hB G hG Y).1] at hi
      exact hi
    have hg : (plainLayout G Y).getD i [] = B := by
      rw [List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hi, Option.getD_some, hiB]
    rw [← hxB, ← hg]
    exact hbagcard i hi2
  unfold pdWidth
  linarith

end L2

/-- Task L2: the plain sweep has width at most the largest outer frontier (given the
exact bag description `PlainLayoutBags` of task L1, taken as a hypothesis). -/
theorem plainLayoutWidth_of (hB : PlainLayoutBags) : PlainLayoutWidth := by
  intro G hG Y hinj w hw
  exact L2.plainLayoutWidth_aux hB G hG Y hinj w hw

#print axioms CubicPathwidth.Alg.plainLayoutWidth_of

end CubicPathwidth.Alg
Back to top