CubicPathwidth.Alg.Proofs2.U1PdTw2Width

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

import CubicPathwidth.Alg.Spec2
import CubicPathwidth.Alg.Proofs.Targets

/-!
# U1: width of `pd_tw2` (`PdTw2Width`)

Induction on the fuel, generalizing the graph. Cases:
* `N = 0`: no bags. `N ≤ 3`: the single bag `H.nodes` has `N ≤ 3 ⌊log₂ N⌋ + 1` vertices.
* `N ≥ 4`, fuel `f + 1`, flag `false`: the reduction reached the empty graph, the
  elimination bag `X = v :: nb` is found, every component `c` of `H[V \ X]` satisfies
  `2|c| ≤ N`, and every recursive call has flag `false`. An elimination bag has
  `|X| ≤ 3` (only nodes of degree `≤ 2` are recorded, so `|nb| ≤ 2`). For a component
  `c`, `H[c]` has `|c|` nodes and `log₂|c| + 1 ≤ log₂ N` (from `2|c| ≤ N`), so each
  output bag `B ++ X` has size `≤ 3 log₂|c| + 4 ≤ 3 log₂ N + 1`. If the output is
  empty the single bag `X` has size `≤ 3`.
-/

namespace CubicPathwidth.Alg

open Graph

namespace U1

/-- Every recorded reduction of `reduceLoop` has at most two neighbours. -/
theorem reduceLoop_red_len : ∀ (fuel : Nat) (G : Graph) (stack : List Nat) (opsRev : List Op),
    (∀ o ∈ opsRev, ∀ v nb, o = Op.red v nb → nb.length ≤ 2) →
    ∀ o ∈ (reduceLoop fuel G stack opsRev).2.1, ∀ v nb, o = Op.red v nb → nb.length ≤ 2
  | fuel, G, [], opsRev, h => by
    intro o ho v nb hov
    have e : (reduceLoop fuel G [] opsRev).2.1 = opsRev.reverse := by
      cases fuel <;> simp [reduceLoop]
    rw [e] at ho
    exact h o (List.mem_reverse.1 ho) v nb hov
  | 0, G, v :: rest, opsRev, h => by
    intro o ho v' nb hov
    have e : (reduceLoop 0 G (v :: rest) opsRev).2.1 = opsRev.reverse := by
      simp [reduceLoop]
    rw [e] at ho
    exact h o (List.mem_reverse.1 ho) v' nb hov
  | fuel + 1, G, v :: rest, opsRev, h => by
    unfold reduceLoop
    split
    · exact reduceLoop_red_len fuel G rest opsRev h
    · rename_i hv
      refine reduceLoop_red_len fuel _ _ _ ?_
      intro o ho v' nb hov
      rcases List.mem_cons.1 ho with rfl | ho
      · obtain ⟨rfl, rfl⟩ := Op.red.inj hov
        have hd : G.degree v ≤ 2 := by
          by_contra hc
          exact hv (by simp [show 2 < G.degree v by omega])
        exact hd
      · exact h o ho v' nb hov

/-- The elimination bags of `reduceRecordF` have at most `3` elements. -/
theorem reduceRecordF_bag_card (G : Graph) (X : Bag)
    (hX : X ∈ elimBags (reduceRecordF G).2.1) : X.toFinset.card ≤ 3 := by
  unfold elimBags at hX
  rw [List.mem_filterMap] at hX
  obtain ⟨o, ho, hXo⟩ := hX
  cases o with
  | del w => simp at hXo
  | red v nb =>
    simp only [Option.some.injEq] at hXo
    subst hXo
    have hlen : nb.length ≤ 2 := by
      unfold reduceRecordF at ho
      exact reduceLoop_red_len _ _ _ [] (by simp) _ ho v nb rfl
    have h1 : (v :: nb).toFinset.card ≤ (v :: nb).length := List.toFinset_card_le _
    simp only [List.length_cons] at h1
    omega

/-- `maxBagCard` is at most `K` as soon as every bag has at most `K` elements. -/
theorem maxBagCard_le_of (bags : List Bag) (K : Nat) (h : ∀ B ∈ bags, B.toFinset.card ≤ K) :
    maxBagCard bags ≤ K := by
  induction bags with
  | nil => simp [maxBagCard]
  | cons B bs ih =>
    have hB : B.toFinset.card ≤ K := h B (List.mem_cons_self)
    have hbs : maxBagCard bs ≤ K := ih fun B' hB' => h B' (List.mem_cons_of_mem _ hB')
    show max B.toFinset.card (maxBagCard bs) ≤ K
    exact max_le hB hbs

/-- Each bag is at most `maxBagCard`. -/
theorem card_le_maxBagCard (bags : List Bag) (B : Bag) (hB : B ∈ bags) :
    B.toFinset.card ≤ maxBagCard bags := by
  induction bags with
  | nil => simp at hB
  | cons B' bs ih =>
    show B.toFinset.card ≤ max B'.toFinset.card (maxBagCard bs)
    rcases List.mem_cons.1 hB with rfl | hB
    · exact le_max_left _ _
    · exact le_trans (ih hB) (le_max_right _ _)

/-- The logarithm step: `2m ≤ N` and `2 ≤ N` give `log₂ m + 1 ≤ log₂ N`. -/
theorem log_half (m N : Nat) (hN : 2 ≤ N) (hm : 2 * m ≤ N) :
    Nat.log 2 m + 1 ≤ Nat.log 2 N := by
  by_cases h0 : m = 0
  · subst h0
    have := Nat.log_pos (b := 2) (n := N) (by norm_num) hN
    simp only [Nat.log_zero_right]
    omega
  · have e : Nat.log 2 (m * 2) = Nat.log 2 m + 1 :=
      Nat.log_mul_base (by norm_num) h0
    have hmono : Nat.log 2 (m * 2) ≤ Nat.log 2 N :=
      Nat.log_mono_right (by omega)
    omega

/-- A single bag of at most three nodes. -/
theorem le_three_log (N : Nat) (hN : N ≤ 3) : N ≤ 3 * Nat.log 2 N + 1 := by
  by_cases h1 : N ≤ 1
  · omega
  · have := Nat.log_pos (b := 2) (n := N) (by norm_num) (by omega)
    omega

/-- The single bag `[H.nodes]` of a graph with at most three nodes. -/
theorem single_bound (H : Graph) (hH : H.WF) (h3 : H.numNodes ≤ 3) :
    maxBagCard [H.nodes] ≤ 3 * Nat.log 2 H.numNodes + 1 := by
  have hc : maxBagCard [H.nodes] = H.numNodes := by
    unfold maxBagCard
    simp only [List.map_cons, List.map_nil, List.foldr_cons, List.foldr_nil]
    rw [List.toFinset_card_of_nodup hH.nodup]
    unfold numNodes
    omega
  rw [hc]
  exact le_three_log _ h3

/-- The node count of an induced subgraph on a component: `|H[c]| = |c|`. -/
theorem numNodes_induced_comp (H : Graph) (hH : H.WF) (X : Bag) (c : List Nat)
    (hc : c ∈ (H.induced fun v => !X.contains v).components) :
    (H.induced c.contains).numNodes = c.length := by
  have hK := inducedSpec H (fun v => !X.contains v) hH
  have hcs := componentsSpec _ hK.1
  have hflat := hcs.1
  have hcSub : ∀ x ∈ c, x ∈ H.nodes := by
    intro x hx
    have hx' : x ∈ (H.induced fun v => !X.contains v).nodes :=
      hflat.mem_iff.1 (List.mem_flatten.2 ⟨c, hc, hx⟩)
    exact ((hK.2.1 x).1 hx').1
  have hcNodup : c.Nodup := by
    have hn := (List.nodup_flatten.1 (hflat.nodup_iff.2 hK.1.nodup)).1 c hc
    exact hn
  have hI := inducedSpec H c.contains hH
  have hperm : (H.induced c.contains).nodes.Perm c := by
    rw [List.perm_ext_iff_of_nodup hI.1.nodup hcNodup]
    intro x
    rw [hI.2.1 x]
    constructor
    · intro hx
      simpa using hx.2
    · intro hx
      exact ⟨hcSub x hx, by simpa using hx⟩
  unfold numNodes
  exact hperm.length_eq

/-- Every output bag of the gluing step has at most `3 log₂ N + 1` elements. -/
theorem out_card (H : Graph) (hH : H.WF) (f : Nat) (X : Bag)
    (hX3 : X.toFinset.card ≤ 3) (hN : 4 ≤ H.numNodes)
    (hsz : ∀ c ∈ (H.induced fun v => !X.contains v).components, 2 * c.length ≤ H.numNodes)
    (hfl : ∀ c ∈ (H.induced fun v => !X.contains v).components,
      (pdTw2Fuel f (H.induced c.contains)).2 = false)
    (ih : ∀ K : Graph, K.WF → (pdTw2Fuel f K).2 = false →
      maxBagCard (pdTw2Fuel f K).1 ≤ 3 * Nat.log 2 K.numNodes + 1)
    (B : Bag)
    (hB : B ∈ (((H.induced fun v => !X.contains v).components.map
      fun c => pdTw2Fuel f (H.induced c.contains)).flatMap
        fun p => p.1.map fun B => bagUnion B X)) :
    B.toFinset.card ≤ 3 * Nat.log 2 H.numNodes + 1 := by
  rw [List.mem_flatMap] at hB
  obtain ⟨p, hp, hBp⟩ := hB
  rw [List.mem_map] at hp
  obtain ⟨c, hc, rfl⟩ := hp
  rw [List.mem_map] at hBp
  obtain ⟨B0, hB0, rfl⟩ := hBp
  have hnum := numNodes_induced_comp H hH X c hc
  have hih := ih (H.induced c.contains) (inducedSpec H _ hH).1 (hfl c hc)
  have hB0le : B0.toFinset.card ≤ 3 * Nat.log 2 (H.induced c.contains).numNodes + 1 :=
    le_trans (card_le_maxBagCard _ B0 hB0) hih
  rw [hnum] at hB0le
  have hlog := log_half c.length H.numNodes (by omega) (hsz c hc)
  show (B0 ++ X).toFinset.card ≤ _
  rw [List.toFinset_append]
  have := Finset.card_union_le B0.toFinset X.toFinset
  omega

/-- Main induction on the fuel. -/
theorem main_fuel (fuel : Nat) : ∀ H : Graph, H.WF → (pdTw2Fuel fuel H).2 = false →
    maxBagCard (pdTw2Fuel fuel H).1 ≤ 3 * Nat.log 2 H.numNodes + 1 := by
  induction fuel with
  | zero =>
    intro H hH hfl
    by_cases h0 : H.numNodes = 0
    · have e : pdTw2Fuel 0 H = ([], false) := by
        unfold pdTw2Fuel
        rw [if_pos h0]
      rw [e]
      simp [maxBagCard]
    by_cases h3 : H.numNodes ≤ 3
    · have e : pdTw2Fuel 0 H = ([H.nodes], false) := by
        unfold pdTw2Fuel
        rw [if_neg h0, if_pos h3]
      rw [e]
      exact single_bound H hH h3
    · have e : pdTw2Fuel 0 H = ([H.nodes], true) := by
        unfold pdTw2Fuel
        rw [if_neg h0, if_neg h3]
      rw [e] at hfl
      exact absurd hfl (by simp)
  | succ f ih =>
    intro H hH hfl
    by_cases h0 : H.numNodes = 0
    · have e : pdTw2Fuel (f + 1) H = ([], false) := by
        unfold pdTw2Fuel
        rw [if_pos h0]
      rw [e]
      simp [maxBagCard]
    by_cases h3 : H.numNodes ≤ 3
    · have e : pdTw2Fuel (f + 1) H = ([H.nodes], false) := by
        unfold pdTw2Fuel
        rw [if_neg h0, if_pos h3]
      rw [e]
      exact single_bound H hH h3
    have hN : 4 ≤ H.numNodes := by omega
    rcases hres : pdTw2Fuel (f + 1) H with ⟨out, fl⟩
    rw [hres] at hfl
    dsimp only at hfl
    unfold pdTw2Fuel at hres
    rw [if_neg h0, if_neg h3] at hres
    dsimp only at hres
    split at hres
    · -- the reduction does not reach the empty graph: flag true
      simp only [Prod.mk.injEq] at hres
      obtain ⟨-, rfl⟩ := hres
      exact absurd hfl (by simp)
    · split at hres
      · -- no balanced bag: flag true
        simp only [Prod.mk.injEq] at hres
        obtain ⟨-, rfl⟩ := hres
        exact absurd hfl (by simp)
      · rename_i X hfind
        have hXm := List.mem_of_find?_eq_some hfind
        have hXp := List.find?_some hfind
        have hsz : ∀ c ∈ (H.induced fun v => !X.contains v).components,
            2 * c.length ≤ H.numNodes := by
          intro c hc
          have := List.all_eq_true.1 hXp c hc
          simpa using this
        have hX3 := reduceRecordF_bag_card H.copy X hXm
        simp only [Prod.mk.injEq] at hres
        obtain ⟨hout, hfl'⟩ := hres
        rw [← hfl'] at hfl
        simp only [Bool.or_eq_false_iff] at hfl
        have hpart : ∀ c ∈ (H.induced fun v => !X.contains v).components,
            (pdTw2Fuel f (H.induced c.contains)).2 = false := by
          intro c hc
          have := List.any_eq_false.1 hfl.2 _ (List.mem_map_of_mem hc)
          simpa using this
        subst hout
        dsimp only
        split_ifs with hem
        · apply maxBagCard_le_of
          intro B hB
          simp only [List.mem_singleton] at hB
          subst hB
          have := Nat.log_pos (b := 2) (n := H.numNodes) (by norm_num) (by omega)
          omega
        · exact maxBagCard_le_of _ _ (fun B hB =>
            out_card H hH f X hX3 hN hsz hpart ih B hB)

end U1

/-- Theorem `PdTw2Width` (`pd_tw2` width bound). -/
theorem pdTw2Width : PdTw2Width := by
  intro fuel H hH hfl
  exact U1.main_fuel fuel H hH hfl

#print axioms CubicPathwidth.Alg.pdTw2Width

end CubicPathwidth.Alg
Back to top