MaxLeafFormal.CCGibbs

Source file MaxLeafFormal/CCGibbs.lean of the Lean project (how it is built).

Statements of the paper formalised here: Lemma 11.2, Lemma 11.3.

import MaxLeafFormal.CCRate
import MaxLeafFormal.Rooted
import MaxLeafFormal.Antinorm3

/-!
# The tree partition function: `lem:cc-gibbs` and `lem:cc-path`

The paper's `def:cc-Z`, `lem:cc-gibbs` and `lem:cc-path`.

Proof routes. `cc_gibbs` follows this route: the subtree sums `g0`, `g1` of a
rooted tree (the subtree structure of `Antinorm3`), and the logarithmic potential
`Ψ = π₀ log g(0) + π₁ log g(1)`, which gains `log λ - π₁ log ζ` at every edge by two weighted
Jensen steps for `log` (`key_step`), with the stochastic matrix and the stationary law of the
Doob transform of `M_ζ`. `cc_path` uses the same split along the path and a scalar recursion for
the gap `(1 + ζ) λ^m - 𝒵`.
-/

namespace MaxLeafFormal.CCGibbs

open Finset SimpleGraph
open MaxLeafFormal.CCRate (spr spr_root)

variable {V : Type*} [Fintype V] [DecidableEq V]

open Classical in
/-- `e(F[X])`: the number of edges of `F` with both ends in `X` (`def:cc-Z`). -/
noncomputable def eIn (F : SimpleGraph V) (X : Finset V) : ℕ :=
  (F.edgeFinset.filter (fun e => ∀ v ∈ e, v ∈ X)).card

/-- `𝒵_F(ζ) = ∑_{X ⊆ V(F)} ζ^{|X|} 2^{e(F[X])}` (`def:cc-Z`). -/
noncomputable def Zpart (F : SimpleGraph V) (ζ : ℝ) : ℝ :=
  ∑ X ∈ (univ : Finset V).powerset, ζ ^ X.card * (2 : ℝ) ^ eIn F X

/-! ### Edge counts -/

omit [Fintype V] [DecidableEq V] in

theorem sym2_forall_mem_iff {p : V → Prop} (a b : V) :
    (∀ x ∈ s(a, b), p x) ↔ p a ∧ p b := by
  constructor
  · intro h
    exact ⟨h a (Sym2.mem_mk_left a b), h b (Sym2.mem_mk_right a b)⟩
  · rintro ⟨ha, hb⟩ x hx
    rcases Sym2.mem_iff.mp hx with rfl | rfl
    · exact ha
    · exact hb

theorem eIn_empty (F : SimpleGraph V) : eIn F ∅ = 0 := by
  classical
  unfold eIn
  rw [Finset.card_eq_zero, Finset.filter_eq_empty_iff]
  intro e _ h
  induction e using Sym2.ind with
  | _ a b => simpa using h a (Sym2.mem_mk_left a b)

theorem eIn_singleton (F : SimpleGraph V) (v : V) : eIn F {v} = 0 := by
  classical
  unfold eIn
  rw [Finset.card_eq_zero, Finset.filter_eq_empty_iff]
  intro e he h
  induction e using Sym2.ind with
  | _ a b =>
    have ha : a ∈ ({v} : Finset V) := h a (Sym2.mem_mk_left a b)
    have hb : b ∈ ({v} : Finset V) := h b (Sym2.mem_mk_right a b)
    rw [Finset.mem_singleton] at ha hb
    have hadj : F.Adj a b := by simpa using he
    rw [ha, hb] at hadj
    simp at hadj

/-- Edge count of a union of two pieces `Y ⊆ A`, `Z ⊆ B` of disjoint sets joined by the single
edge `v w`. -/
theorem eIn_union {F : SimpleGraph V} {A B Y Z : Finset V} {v w : V} (hAB : Disjoint A B)
    (hY : Y ⊆ A) (hZ : Z ⊆ B) (hv : v ∈ A) (hw : w ∈ B) (hvw : F.Adj v w)
    (hedge : ∀ a ∈ A, ∀ b ∈ B, F.Adj a b → a = v ∧ b = w) :
    eIn F (Y ∪ Z) = eIn F Y + eIn F Z + if v ∈ Y ∧ w ∈ Z then 1 else 0 := by
  classical
  unfold eIn
  have hdisj : Disjoint (F.edgeFinset.filter (fun e => ∀ x ∈ e, x ∈ Y))
      (F.edgeFinset.filter (fun e => ∀ x ∈ e, x ∈ Z)) := by
    rw [Finset.disjoint_left]
    intro e he1 he2
    rw [Finset.mem_filter] at he1 he2
    induction e using Sym2.ind with
    | _ a b =>
      exact Finset.disjoint_left.mp hAB (hY (he1.2 a (Sym2.mem_mk_left a b)))
        (hZ (he2.2 a (Sym2.mem_mk_left a b)))
  have hsplit : F.edgeFinset.filter (fun e => ∀ x ∈ e, x ∈ Y ∪ Z) =
      (F.edgeFinset.filter (fun e => ∀ x ∈ e, x ∈ Y) ∪
        F.edgeFinset.filter (fun e => ∀ x ∈ e, x ∈ Z)) ∪
      (if v ∈ Y ∧ w ∈ Z then {s(v, w)} else ∅) := by
    ext e
    induction e using Sym2.ind with
    | _ a b =>
      by_cases hc : v ∈ Y ∧ w ∈ Z
      · simp only [Finset.mem_filter, Finset.mem_union, SimpleGraph.mem_edgeFinset,
          SimpleGraph.mem_edgeSet, sym2_forall_mem_iff, if_pos hc, Finset.mem_singleton]
        constructor
        · rintro ⟨hadj, ha | ha, hb | hb⟩
          · exact Or.inl (Or.inl ⟨hadj, ha, hb⟩)
          · obtain ⟨h1, h2⟩ := hedge a (hY ha) b (hZ hb) hadj
            exact Or.inr (by rw [h1, h2])
          · obtain ⟨h1, h2⟩ := hedge b (hY hb) a (hZ ha) hadj.symm
            exact Or.inr (by rw [← h1, ← h2]; exact Sym2.eq_swap)
          · exact Or.inl (Or.inr ⟨hadj, ha, hb⟩)
        · rintro ((⟨hadj, ha, hb⟩ | ⟨hadj, ha, hb⟩) | heq)
          · exact ⟨hadj, Or.inl ha, Or.inl hb⟩
          · exact ⟨hadj, Or.inr ha, Or.inr hb⟩
          · rcases Sym2.eq_iff.mp heq with ⟨h1, h2⟩ | ⟨h1, h2⟩
            · subst h1
              subst h2
              exact ⟨hvw, Or.inl hc.1, Or.inr hc.2⟩
            · subst h1
              subst h2
              exact ⟨hvw.symm, Or.inr hc.2, Or.inl hc.1⟩
      · simp only [Finset.mem_filter, Finset.mem_union, SimpleGraph.mem_edgeFinset,
          SimpleGraph.mem_edgeSet, sym2_forall_mem_iff, if_neg hc]
        constructor
        · rintro ⟨hadj, ha | ha, hb | hb⟩
          · exact Or.inl (Or.inl ⟨hadj, ha, hb⟩)
          · obtain ⟨h1, h2⟩ := hedge a (hY ha) b (hZ hb) hadj
            exact (hc ⟨by rw [← h1]; exact ha, by rw [← h2]; exact hb⟩).elim
          · obtain ⟨h1, h2⟩ := hedge b (hY hb) a (hZ ha) hadj.symm
            exact (hc ⟨by rw [← h1]; exact hb, by rw [← h2]; exact ha⟩).elim
          · exact Or.inl (Or.inr ⟨hadj, ha, hb⟩)
        · rintro ((⟨hadj, ha, hb⟩ | ⟨hadj, ha, hb⟩) | heq)
          · exact ⟨hadj, Or.inl ha, Or.inl hb⟩
          · exact ⟨hadj, Or.inr ha, Or.inr hb⟩
          · simp at heq
  have hdisjR : Disjoint
      (F.edgeFinset.filter (fun e => ∀ x ∈ e, x ∈ Y) ∪
        F.edgeFinset.filter (fun e => ∀ x ∈ e, x ∈ Z))
      (if v ∈ Y ∧ w ∈ Z then {s(v, w)} else ∅) := by
    split_ifs with hc
    · rw [Finset.disjoint_singleton_right]
      intro h
      rcases Finset.mem_union.mp h with h | h
      · rw [Finset.mem_filter] at h
        exact Finset.disjoint_left.mp hAB (hY (h.2 w (Sym2.mem_mk_right v w))) hw
      · rw [Finset.mem_filter] at h
        exact Finset.disjoint_left.mp hAB hv (hZ (h.2 v (Sym2.mem_mk_left v w)))
    · simp
  rw [hsplit, Finset.card_union_of_disjoint hdisjR, Finset.card_union_of_disjoint hdisj]
  split_ifs <;> simp

theorem sym2_pair_map {α β : Type*} (f : α → β) (x y : α) :
    Sym2.map f s(x, y) = s(f x, f y) := rfl

omit [Fintype V] [DecidableEq V] in

theorem sym2_map_mem_edgeSet_iff {W : Type*} {F : SimpleGraph V} {G : SimpleGraph W}
    (σ : V ≃ W) (hσ : ∀ a b, F.Adj a b ↔ G.Adj (σ a) (σ b)) (a : Sym2 V) :
    Sym2.map σ a ∈ G.edgeSet ↔ a ∈ F.edgeSet := by
  refine Sym2.inductionOn a (fun x y => ?_)
  simp only [sym2_pair_map, SimpleGraph.mem_edgeSet]
  exact (hσ x y).symm

omit [Fintype V] [DecidableEq V] in

theorem sym2_map_symm_map {W : Type*} (σ : V ≃ W) (b : Sym2 W) :
    Sym2.map σ (Sym2.map σ.symm b) = b := by
  refine Sym2.inductionOn b (fun x y => ?_)
  simp only [sym2_pair_map, Equiv.apply_symm_apply]

omit [Fintype V] [DecidableEq V] in

theorem sym2_symm_map_map {W : Type*} (σ : V ≃ W) (a : Sym2 V) :
    Sym2.map σ.symm (Sym2.map σ a) = a := by
  refine Sym2.inductionOn a (fun x y => ?_)
  simp only [sym2_pair_map, Equiv.symm_apply_apply]

open Classical in
theorem eIn_map_equiv {W : Type*} [Fintype W] [DecidableEq W]
    {F : SimpleGraph V} {G : SimpleGraph W}
    (σ : V ≃ W) (hσ : ∀ a b, F.Adj a b ↔ G.Adj (σ a) (σ b)) (X : Finset V) :
    eIn G (X.map σ.toEmbedding) = eIn F X := by
  unfold eIn
  symm
  refine Finset.card_nbij' (Sym2.map σ) (Sym2.map σ.symm) ?_ ?_ ?_ ?_
  · intro a ha
    obtain ⟨ha1, ha2⟩ := Finset.mem_filter.mp ha
    apply Finset.mem_filter.mpr
    refine ⟨SimpleGraph.mem_edgeFinset.mpr
      ((sym2_map_mem_edgeSet_iff σ hσ a).mpr (SimpleGraph.mem_edgeFinset.mp ha1)),
      fun v hv => ?_⟩
    obtain ⟨c, hc, rfl⟩ := Sym2.mem_map.mp hv
    exact Finset.mem_map_of_mem _ (ha2 c hc)
  · intro b hb
    obtain ⟨hb1, hb2⟩ := Finset.mem_filter.mp hb
    apply Finset.mem_filter.mpr
    have hb' : Sym2.map σ.symm b ∈ F.edgeSet :=
      (sym2_map_mem_edgeSet_iff σ hσ (Sym2.map σ.symm b)).mp (by
        rw [sym2_map_symm_map]
        exact SimpleGraph.mem_edgeFinset.mp hb1)
    refine ⟨SimpleGraph.mem_edgeFinset.mpr hb', fun v hv => ?_⟩
    obtain ⟨c, hc, rfl⟩ := Sym2.mem_map.mp hv
    obtain ⟨x, hx, hxc⟩ := Finset.mem_map.mp (hb2 c hc)
    rw [← hxc]
    simpa using hx
  · intro a _
    exact sym2_symm_map_map σ a
  · intro b _
    exact sym2_map_symm_map σ b

theorem pg_two_edge (e : Sym2 (Fin 2)) : e ∈ (pathGraph 2).edgeSet → e = s(0, 1) := by
  refine Sym2.inductionOn e (fun x y => ?_)
  intro he
  have hadj : (pathGraph 2).Adj x y := (SimpleGraph.mem_edgeSet _).mp he
  have hne : x ≠ y := hadj.ne
  fin_cases x <;> fin_cases y <;>
    first
    | exact (hne rfl).elim
    | rfl
    | exact Sym2.eq_swap

open Classical in
/-- Filter membership for the edge filter of `eIn`, stated generically. -/
theorem mem_filter_edges (s : Finset (Sym2 V)) (X : Finset V) (e : Sym2 V) :
    e ∈ s.filter (fun e => ∀ v ∈ e, v ∈ X) ↔ e ∈ s ∧ ∀ v ∈ e, v ∈ X :=
  Finset.mem_filter

open Classical in
/-- `eIn` depends only on the edge set, given as a finset. -/
theorem eIn_eq_filter_of_edges (F : SimpleGraph V) (s : Finset (Sym2 V))
    (hs : ∀ e, e ∈ F.edgeSet ↔ e ∈ s) (X : Finset V) :
    eIn F X = (s.filter (fun e => ∀ v ∈ e, v ∈ X)).card := by
  unfold eIn
  congr 1
  ext e
  rw [mem_filter_edges, mem_filter_edges, SimpleGraph.mem_edgeFinset, hs]

open Classical in
theorem eIn_pg2 (X : Finset (Fin 2)) :
    eIn (pathGraph 2) X = if (0 ∈ X ∧ 1 ∈ X) then 1 else 0 := by
  have adj01 : (pathGraph 2).Adj 0 1 := by simp [SimpleGraph.pathGraph_adj]
  have hs : ∀ e, e ∈ (pathGraph 2).edgeSet ↔ e ∈ ({s(0, 1)} : Finset (Sym2 (Fin 2))) := by
    intro e
    rw [Finset.mem_singleton]
    constructor
    · exact pg_two_edge e
    · rintro rfl
      exact (SimpleGraph.mem_edgeSet _).mpr adj01
  rw [eIn_eq_filter_of_edges _ _ hs X]
  split_ifs with h
  · rw [Finset.card_eq_one]
    refine ⟨s(0, 1), ?_⟩
    ext e
    rw [mem_filter_edges]
    simp only [Finset.mem_singleton]
    constructor
    · rintro ⟨h', _⟩
      exact h'
    · rintro rfl
      refine ⟨rfl, fun v hv => ?_⟩
      rcases Sym2.mem_iff.mp hv with rfl | rfl
      · exact h.1
      · exact h.2
  · rw [Finset.card_eq_zero]
    ext e
    rw [mem_filter_edges, Finset.mem_singleton]
    constructor
    · rintro ⟨rfl, hP⟩
      exact (h ⟨hP 0 (Sym2.mem_mk_left _ _), hP 1 (Sym2.mem_mk_right _ _)⟩).elim
    · intro hm
      simp at hm

/-! ### The closed form `λ = spr ζ` and the weights of the potential -/

theorem one_add_le_spr (ζ : ℝ) : 1 + ζ ≤ spr ζ := by
  have h1 : Real.sqrt 1 ≤ Real.sqrt (1 + 4 * ζ ^ 2) :=
    Real.sqrt_le_sqrt (by nlinarith [sq_nonneg ζ])
  rw [Real.sqrt_one] at h1
  unfold spr
  linarith

theorem spr_pos_of_nonneg {ζ : ℝ} (hζ : 0 ≤ ζ) : 0 < spr ζ := by
  have := one_add_le_spr ζ
  linarith

lemma spr_gt {ζ : ℝ} (hζ : 0 < ζ) : 1 < spr ζ := by
  linarith [one_add_le_spr ζ]

/-- π₀ -/
noncomputable def piw (ζ : ℝ) : ℝ := ζ / (ζ + (spr ζ - 1) ^ 2)

/-- π₁ -/
noncomputable def piv (ζ : ℝ) : ℝ := (spr ζ - 1) ^ 2 / (ζ + (spr ζ - 1) ^ 2)

lemma pi_den_pos {ζ : ℝ} (hζ : 0 < ζ) : 0 < ζ + (spr ζ - 1) ^ 2 := by
  nlinarith [sq_nonneg (spr ζ - 1)]

theorem pi_facts {ζ : ℝ} (hζ : 0 < ζ) : 0 ≤ piw ζ ∧ 0 ≤ piv ζ ∧ piw ζ + piv ζ = 1 := by
  have hN := pi_den_pos hζ
  unfold piw piv
  refine ⟨div_nonneg hζ.le hN.le, div_nonneg (sq_nonneg _) hN.le, ?_⟩
  exact ((add_div _ _ _).symm).trans (div_self hN.ne')

/-- Two-point concavity of `log` (weighted Jensen). -/
theorem log_two_point {a b x y : ℝ} (ha : 0 ≤ a) (hb : 0 ≤ b) (hab : a + b = 1) (hx : 0 < x)
    (hy : 0 < y) : a * Real.log x + b * Real.log y ≤ Real.log (a * x + b * y) := by
  have h := (strictConcaveOn_log_Ioi.concaveOn).2 (Set.mem_Ioi.mpr hx) (Set.mem_Ioi.mpr hy) ha hb hab
  simpa only [smul_eq_mul] using h

lemma row0_log {l m b0 b1 : ℝ} (hl : 0 < l) (hm : 0 < m) (hsum : 1 / l + m / l = 1)
    (h0 : 0 < b0) (h1 : 0 < b1) :
    Real.log l + (1 / l) * Real.log b0 + (m / l) * Real.log b1 - (m / l) * Real.log m
      ≤ Real.log (b0 + b1) := by
  have hl0 : l ≠ 0 := hl.ne'
  have hm0 : m ≠ 0 := hm.ne'
  have key := log_two_point (a := 1 / l) (b := m / l) (x := l * b0) (y := l * b1 / m)
    (div_nonneg zero_le_one hl.le) (div_nonneg hm.le hl.le) hsum
    (mul_pos hl h0) (div_pos (mul_pos hl h1) hm)
  have e : 1 / l * (l * b0) + m / l * (l * b1 / m) = b0 + b1 := by
    field_simp
  rw [e, Real.log_mul hl0 h0.ne', Real.log_div (mul_ne_zero hl0 h1.ne') hm0,
    Real.log_mul hl0 h1.ne'] at key
  have hlog : Real.log l = (1 / l + m / l) * Real.log l := by rw [hsum, one_mul]
  have eq : Real.log l + (1 / l) * Real.log b0 + (m / l) * Real.log b1 - (m / l) * Real.log m
      = 1 / l * (Real.log l + Real.log b0) + m / l * (Real.log l + Real.log b1 - Real.log m) := by
    linear_combination hlog
  linarith [key, eq]

lemma row1_log {ζ l m b0 b1 : ℝ} (hζ : 0 < ζ) (hl : 0 < l) (hm : 0 < m)
    (hsum : ζ / (l * m) + 2 * ζ / l = 1) (h0 : 0 < b0) (h1 : 0 < b1) :
    Real.log l - Real.log ζ + (ζ / (l * m)) * Real.log m + (ζ / (l * m)) * Real.log b0
      + (2 * ζ / l) * Real.log b1 ≤ Real.log (b0 + 2 * b1) := by
  have hl0 : l ≠ 0 := hl.ne'
  have hm0 : m ≠ 0 := hm.ne'
  have hζ0 : ζ ≠ 0 := hζ.ne'
  have key := log_two_point (a := ζ / (l * m)) (b := 2 * ζ / l) (x := l * m * b0 / ζ)
    (y := l * b1 / ζ)
    (div_nonneg hζ.le (mul_pos hl hm).le) (div_nonneg (by linarith) hl.le) hsum
    (div_pos (mul_pos (mul_pos hl hm) h0) hζ) (div_pos (mul_pos hl h1) hζ)
  have e : ζ / (l * m) * (l * m * b0 / ζ) + 2 * ζ / l * (l * b1 / ζ) = b0 + 2 * b1 := by
    field_simp
  rw [e, Real.log_div (mul_ne_zero (mul_ne_zero hl0 hm0) h0.ne') hζ0,
    Real.log_mul (mul_ne_zero hl0 hm0) h0.ne', Real.log_mul hl0 hm0,
    Real.log_div (mul_ne_zero hl0 h1.ne') hζ0, Real.log_mul hl0 h1.ne'] at key
  have eq : ζ / (l * m) * (Real.log l + Real.log m + Real.log b0 - Real.log ζ)
      + 2 * ζ / l * (Real.log l + Real.log b1 - Real.log ζ)
      = Real.log l - Real.log ζ + (ζ / (l * m)) * Real.log m + (ζ / (l * m)) * Real.log b0
        + (2 * ζ / l) * Real.log b1 := by
    linear_combination (Real.log l - Real.log ζ) * hsum
  linarith [key, eq]

lemma coef_facts {ζ l : ℝ} (hζ : 0 < ζ) (hl : 1 < l) (hL : l ^ 2 - (1 + 2 * ζ) * l + ζ = 0) :
    ζ / (ζ + (l - 1) ^ 2) * (1 / l) + (l - 1) ^ 2 / (ζ + (l - 1) ^ 2) * (ζ / (l * (l - 1)))
        = ζ / (ζ + (l - 1) ^ 2) ∧
    ζ / (ζ + (l - 1) ^ 2) * ((l - 1) / l) + (l - 1) ^ 2 / (ζ + (l - 1) ^ 2) * (2 * ζ / l)
        = (l - 1) ^ 2 / (ζ + (l - 1) ^ 2) ∧
    -(ζ / (ζ + (l - 1) ^ 2) * ((l - 1) / l))
        + (l - 1) ^ 2 / (ζ + (l - 1) ^ 2) * (ζ / (l * (l - 1))) = 0 := by
  have hN : 0 < ζ + (l - 1) ^ 2 := by nlinarith [sq_nonneg (l - 1)]
  have hN0 : ζ + (l - 1) ^ 2 ≠ 0 := hN.ne'
  have hl0 : l ≠ 0 := (by linarith : (0:ℝ) < l).ne'
  have hm0 : l - 1 ≠ 0 := (by linarith : (0:ℝ) < l - 1).ne'
  refine ⟨?_, ?_, ?_⟩
  · field_simp <;> ring
  · rw [div_mul_div_comm, div_mul_div_comm, ← add_div,
      div_eq_div_iff (mul_ne_zero hN0 hl0) hN0]
    linear_combination (-(ζ + (l - 1) ^ 2) * (l - 1)) * hL
  · field_simp <;> ring

lemma jensen_combine {w v lL lz lb0 lb1 lm L0 L1 p00 p01 p10 p11 : ℝ}
    (hw : 0 ≤ w) (hv : 0 ≤ v) (hwv : w + v = 1)
    (c0 : w * p00 + v * p10 = w) (c1 : w * p01 + v * p11 = v)
    (c2 : -(w * p01) + v * p10 = 0)
    (R0 : lL + p00 * lb0 + p01 * lb1 - p01 * lm ≤ L0)
    (R1 : lL - lz + p10 * lm + p10 * lb0 + p11 * lb1 ≤ L1) :
    lL - v * lz + (w * lb0 + v * lb1) ≤ w * L0 + v * L1 := by
  have A := mul_le_mul_of_nonneg_left R0 hw
  have B := mul_le_mul_of_nonneg_left R1 hv
  have E : lL - v * lz + (w * lb0 + v * lb1)
      = w * (lL + p00 * lb0 + p01 * lb1 - p01 * lm)
        + v * (lL - lz + p10 * lm + p10 * lb0 + p11 * lb1) := by
    linear_combination (-lL) * hwv - lb0 * c0 - lb1 * c1 - lm * c2
  linarith [A, B, E]

theorem key_step {ζ : ℝ} (hζ : 0 < ζ) {b0 b1 : ℝ} (h0 : 0 < b0) (h1 : 0 < b1) :
    Real.log (spr ζ) - piv ζ * Real.log ζ + (piw ζ * Real.log b0 + piv ζ * Real.log b1)
      ≤ piw ζ * Real.log (b0 + b1) + piv ζ * Real.log (b0 + 2 * b1) := by
  have hgt := spr_gt hζ
  have hl : 0 < spr ζ := by linarith
  have hm : 0 < spr ζ - 1 := by linarith
  have hL := spr_root ζ
  obtain ⟨hw, hv, hwv⟩ := pi_facts hζ
  have hsum0 : 1 / spr ζ + (spr ζ - 1) / spr ζ = 1 := by
    rw [← add_div, div_eq_one_iff_eq hl.ne']
    ring
  have hsum1 : ζ / (spr ζ * (spr ζ - 1)) + 2 * ζ / spr ζ = 1 := by
    rw [div_add_div _ _ (mul_ne_zero hl.ne' hm.ne') hl.ne',
      div_eq_one_iff_eq (mul_ne_zero (mul_ne_zero hl.ne' hm.ne') hl.ne')]
    linear_combination (-(spr ζ)) * hL
  have R0 := row0_log hl hm hsum0 h0 h1
  have R1 := row1_log hζ hl hm hsum1 h0 h1
  have c0 : piw ζ * (1 / spr ζ) + piv ζ * (ζ / (spr ζ * (spr ζ - 1))) = piw ζ := by
    unfold piw piv
    exact (coef_facts hζ hgt hL).1
  have c1 : piw ζ * ((spr ζ - 1) / spr ζ) + piv ζ * (2 * ζ / spr ζ) = piv ζ := by
    unfold piw piv
    exact (coef_facts hζ hgt hL).2.1
  have c2 : -(piw ζ * ((spr ζ - 1) / spr ζ)) + piv ζ * (ζ / (spr ζ * (spr ζ - 1))) = 0 := by
    unfold piw piv
    exact (coef_facts hζ hgt hL).2.2
  exact jensen_combine hw hv hwv c0 c1 c2 R0 R1

theorem root_step {ζ : ℝ} (hζ : 0 < ζ) {p0 p1 : ℝ} (h0 : 0 < p0) (h1 : 0 < p1) :
    piw ζ * Real.log p0 + piv ζ * Real.log p1 ≤ Real.log (p0 + p1) := by
  obtain ⟨hw, hv, hwv⟩ := pi_facts hζ
  have hgt := spr_gt hζ
  have hm : 0 < spr ζ - 1 := by linarith
  have hN := pi_den_pos hζ
  have hwp : 0 < piw ζ := by unfold piw; exact div_pos hζ hN
  have hvp : 0 < piv ζ := by unfold piv; exact div_pos (pow_pos hm 2) hN
  have J := log_two_point hw hv hwv h0 h1
  have hw1 : piw ζ ≤ 1 := by linarith
  have hv1 : piv ζ ≤ 1 := by linarith
  have hle : piw ζ * p0 + piv ζ * p1 ≤ p0 + p1 := by
    nlinarith [mul_le_mul_of_nonneg_right hw1 h0.le, mul_le_mul_of_nonneg_right hv1 h1.le]
  have hpos : 0 < piw ζ * p0 + piv ζ * p1 := add_pos (mul_pos hwp h0) (mul_pos hvp h1)
  have hlog := Real.log_le_log hpos hle
  linarith [J, hlog]

theorem min_step {ζ : ℝ} (hζ : 0 < ζ) : Real.log (min 1 ζ) ≤ piv ζ * Real.log ζ := by
  obtain ⟨hw, hv, hwv⟩ := pi_facts hζ
  rcases le_total 1 ζ with h | h
  · rw [min_eq_left h, Real.log_one]
    exact mul_nonneg hv (Real.log_nonneg h)
  · rw [min_eq_right h]
    have hlog : Real.log ζ ≤ 0 := Real.log_nonpos hζ.le h
    have h3 : 0 ≤ piw ζ * (-Real.log ζ) := mul_nonneg hw (by linarith)
    have hp : piw ζ = 1 - piv ζ := by linarith
    rw [hp] at h3
    nlinarith [h3]

/-! ### Sums over the subsets of a vertex set, split on one vertex -/

section Sums

variable (F : SimpleGraph V) (ζ : ℝ)

/-- The weight `ζ^{|X|} 2^{e(F[X])}` of a set `X`. -/
noncomputable def tw (X : Finset V) : ℝ := ζ ^ X.card * (2 : ℝ) ^ eIn F X

/-- The sum of the weights of the subsets of `S` that avoid `v`. -/
noncomputable def g0 (S : Finset V) (v : V) : ℝ :=
  ∑ X ∈ S.powerset, if v ∈ X then 0 else tw F ζ X

/-- The sum of the weights of the subsets of `S` that contain `v`. -/
noncomputable def g1 (S : Finset V) (v : V) : ℝ :=
  ∑ X ∈ S.powerset, if v ∈ X then tw F ζ X else 0

theorem Zpart_eq_g (r : V) : Zpart F ζ = g0 F ζ univ r + g1 F ζ univ r := by
  unfold Zpart g0 g1
  rw [← sum_add_distrib]
  refine sum_congr rfl (fun X _ => ?_)
  unfold tw
  split_ifs <;> simp

theorem tw_nonneg (hζ : 0 ≤ ζ) (X : Finset V) : 0 ≤ tw F ζ X := by
  unfold tw; positivity

theorem tw_empty : tw F ζ ∅ = 1 := by simp [tw, eIn_empty]

theorem tw_singleton (v : V) : tw F ζ {v} = ζ := by simp [tw, eIn_singleton]

omit [Fintype V] in

theorem powerset_singleton' (v : V) : ({v} : Finset V).powerset = {∅, {v}} := by
  ext X; simp [subset_singleton_iff]

theorem g0_single (v : V) : g0 F ζ {v} v = 1 := by
  rw [g0, powerset_singleton', sum_pair (singleton_ne_empty v).symm]
  simp [tw_empty]

theorem g1_single (v : V) : g1 F ζ {v} v = ζ := by
  rw [g1, powerset_singleton', sum_pair (singleton_ne_empty v).symm]
  simp [tw_singleton]

theorem g0_pos (hζ : 0 ≤ ζ) (S : Finset V) (v : V) : 0 < g0 F ζ S v := by
  unfold g0
  have h := single_le_sum (f := fun X => if v ∈ X then 0 else tw F ζ X)
    (fun X _ => by split_ifs; exacts [le_rfl, tw_nonneg F ζ hζ X])
    (empty_mem_powerset S)
  simp only [notMem_empty, if_false, tw_empty] at h
  exact lt_of_lt_of_le one_pos h

theorem g1_pos (hζ : 0 < ζ) (S : Finset V) (v : V) (hv : v ∈ S) : 0 < g1 F ζ S v := by
  unfold g1
  have h := single_le_sum (f := fun X => if v ∈ X then tw F ζ X else 0)
    (fun X _ => by split_ifs; exacts [tw_nonneg F ζ hζ.le X, le_rfl])
    (mem_powerset.2 (singleton_subset_iff.2 hv))
  simp only [mem_singleton, if_true, tw_singleton] at h
  exact lt_of_lt_of_le hζ h

variable {F ζ}
variable {A B : Finset V} {v w : V} (hAB : Disjoint A B) (hv : v ∈ A) (hw : w ∈ B)
  (hvw : F.Adj v w) (hedge : ∀ a ∈ A, ∀ b ∈ B, F.Adj a b → a = v ∧ b = w)

include hAB hv hw hvw hedge in

theorem tw_union {Y Z : Finset V} (hY : Y ⊆ A) (hZ : Z ⊆ B) :
    tw F ζ (Y ∪ Z) = tw F ζ Y * tw F ζ Z * (if v ∈ Y ∧ w ∈ Z then 2 else 1) := by
  have hYZ : Disjoint Y Z := disjoint_of_subset_left hY (disjoint_of_subset_right hZ hAB)
  unfold tw
  rw [card_union_of_disjoint hYZ, eIn_union hAB hY hZ hv hw hvw hedge]
  simp only [pow_add]
  split_ifs <;> ring

include hAB hv hw hvw hedge in

/-- Adding the part `B` (tracked vertex `w`) below the vertex `v` of the part `A`. -/
theorem g_union :
    g0 F ζ (A ∪ B) v = g0 F ζ A v * (g0 F ζ B w + g1 F ζ B w) ∧
    g1 F ζ (A ∪ B) v = g1 F ζ A v * (g0 F ζ B w + 2 * g1 F ζ B w) := by
  have hvB : v ∉ B := fun h => disjoint_left.1 hAB hv h
  constructor
  · unfold g0 g1
    rw [Antinorm3.sum_powerset_union hAB, ← sum_add_distrib, sum_mul_sum]
    refine sum_congr rfl (fun Y hY => sum_congr rfl (fun Z hZ => ?_))
    rw [mem_powerset] at hY hZ
    have hvZ : v ∉ Z := fun h => hvB (hZ h)
    rw [tw_union hAB hv hw hvw hedge hY hZ]
    by_cases hvY : v ∈ Y <;> by_cases hwZ : w ∈ Z <;>
      simp only [mem_union, hvY, hwZ, hvZ, and_self, and_true, true_and, and_false,
        false_and, or_true, true_or, or_false, false_or, ↓reduceIte] <;> ring
  · unfold g0 g1
    rw [Antinorm3.sum_powerset_union hAB, mul_sum, ← sum_add_distrib, sum_mul_sum]
    refine sum_congr rfl (fun Y hY => sum_congr rfl (fun Z hZ => ?_))
    rw [mem_powerset] at hY hZ
    have hvZ : v ∉ Z := fun h => hvB (hZ h)
    rw [tw_union hAB hv hw hvw hedge hY hZ]
    by_cases hvY : v ∈ Y <;> by_cases hwZ : w ∈ Z <;>
      simp only [mem_union, hvY, hwZ, hvZ, and_self, and_true, true_and, and_false,
        false_and, or_true, true_or, or_false, false_or, ↓reduceIte] <;> ring

end Sums

/-! ### `lem:cc-gibbs`: the subtree recursion with a logarithmic potential -/

section Gibbs

open MaxLeafFormal.Antinorm3 (D children mem_children D_eq not_mem_D_child D_disjoint D_edge
  self_mem_D card_D_child_lt D_root)
open MaxLeafFormal.CDS (par par_spec)

/-- The potential `Ψ_{S,v} = π₀ log g_{S,v}(0) + π₁ log g_{S,v}(1)`. -/
noncomputable def psi (F : SimpleGraph V) (ζ : ℝ) (S : Finset V) (v : V) : ℝ :=
  piw ζ * Real.log (g0 F ζ S v) + piv ζ * Real.log (g1 F ζ S v)

theorem psi_union {F : SimpleGraph V} {ζ : ℝ} (hζ : 0 < ζ) {A B : Finset V} {v w : V}
    (hAB : Disjoint A B) (hv : v ∈ A) (hw : w ∈ B) (hvw : F.Adj v w)
    (hedge : ∀ a ∈ A, ∀ b ∈ B, F.Adj a b → a = v ∧ b = w) :
    psi F ζ A v + psi F ζ B w + (Real.log (spr ζ) - piv ζ * Real.log ζ)
      ≤ psi F ζ (A ∪ B) v := by
  obtain ⟨h0, h1⟩ := g_union hAB hv hw hvw hedge
  have a0 := g0_pos F ζ hζ.le A v
  have a1 := g1_pos F ζ hζ A v hv
  have b0 := g0_pos F ζ hζ.le B w
  have b1 := g1_pos F ζ hζ B w hw
  have K := key_step hζ b0 b1
  unfold psi
  rw [h0, h1, Real.log_mul a0.ne' (add_pos b0 b1).ne',
    Real.log_mul a1.ne' (by linarith : (0:ℝ) < g0 F ζ B w + 2 * g1 F ζ B w).ne',
    mul_add, mul_add]
  linarith

theorem psi_D {F : SimpleGraph V} {ζ : ℝ} (hζ : 0 < ζ) (hF : F.IsTree) (r v : V) :
    ((D hF r v).card : ℝ) * Real.log (spr ζ) - (Real.log (spr ζ) - piv ζ * Real.log ζ)
      ≤ psi F ζ (D hF r v) v := by
  induction hn : (D hF r v).card using Nat.strong_induction_on generalizing v with
  | _ n ih =>
    subst hn
    have key : ∀ C ⊆ children hF r v,
        ((insert v (C.biUnion (D hF r))).card : ℝ) * Real.log (spr ζ)
            - (Real.log (spr ζ) - piv ζ * Real.log ζ)
          ≤ psi F ζ (insert v (C.biUnion (D hF r))) v := by
      intro C
      induction C using Finset.induction_on with
      | empty =>
        intro _
        simp only [biUnion_empty, insert_empty, card_singleton]
        unfold psi
        rw [g0_single, g1_single, Real.log_one]
        apply le_of_eq
        push_cast
        ring
      | insert w C hwC ihC =>
        intro hC
        have hw : w ∈ children hF r v := hC (mem_insert_self w C)
        have hCv : C ⊆ children hF r v := fun c hc => hC (mem_insert_of_mem hc)
        obtain ⟨hwr, hpw⟩ := (mem_children hF r).1 hw
        set A := insert v (C.biUnion (D hF r)) with hAdef
        have hAB : Disjoint A (D hF r w) := by
          rw [hAdef, disjoint_insert_left, disjoint_biUnion_left]
          refine ⟨not_mem_D_child hF r hw, fun c hc => D_disjoint hF r (hCv hc) hw ?_⟩
          rintro rfl; exact hwC hc
        have hU : insert v ((insert w C).biUnion (D hF r)) = A ∪ D hF r w := by
          rw [biUnion_insert, hAdef]
          ext u; simp only [mem_insert, mem_union]; tauto
        have hedge : ∀ a ∈ A, ∀ b ∈ D hF r w, F.Adj a b → a = v ∧ b = w := by
          intro a ha b hb hab
          obtain ⟨h1, h2⟩ := D_edge hF r hwr hb (fun h => disjoint_left.1 hAB ha h) hab
          exact ⟨h2.trans hpw, h1⟩
        have hvw : F.Adj v w := by rw [← hpw]; exact (par_spec hF r hwr).1.symm
        have hstep := psi_union hζ hAB (mem_insert_self v _) (self_mem_D hF r w) hvw hedge
        have h1 := ihC hCv
        have h2 := ih _ (card_D_child_lt hF r hw) w rfl
        rw [hU, card_union_of_disjoint hAB, Nat.cast_add, add_mul]
        linarith
    have := key (children hF r v) subset_rfl
    rwa [← D_eq] at this

end Gibbs

/-! ### `lem:cc-path`: the transfer recursion along a path -/

section Path

/-- The pair `(g_{P_m}(0), g_{P_m}(1))` of the path on `m + 1` vertices, tracked at its end. -/
noncomputable def tr (ζ : ℝ) : ℕ → ℝ × ℝ
  | 0 => (1, ζ)
  | m + 1 => ((tr ζ m).1 + (tr ζ m).2, ζ * ((tr ζ m).1 + 2 * (tr ζ m).2))

theorem path_g (n : ℕ) (ζ : ℝ) : ∀ m (hm : m < n + 1),
    g0 (pathGraph (n + 1)) ζ (univ.filter (fun i : Fin (n + 1) => i.val ≤ m)) ⟨m, hm⟩
        = (tr ζ m).1 ∧
    g1 (pathGraph (n + 1)) ζ (univ.filter (fun i : Fin (n + 1) => i.val ≤ m)) ⟨m, hm⟩
        = (tr ζ m).2 := by
  intro m
  induction m with
  | zero =>
    intro hm
    have hS : (univ.filter (fun i : Fin (n + 1) => i.val ≤ 0)) = {⟨0, hm⟩} := by
      ext i
      simp only [mem_filter, mem_univ, true_and, mem_singleton, Fin.ext_iff]
      omega
    rw [hS, g0_single, g1_single]
    simp [tr]
  | succ m ih =>
    intro hm
    obtain ⟨ih0, ih1⟩ := ih (by omega)
    have hS : univ.filter (fun i : Fin (n + 1) => i.val ≤ m + 1)
        = {(⟨m + 1, hm⟩ : Fin (n + 1))} ∪ univ.filter (fun i : Fin (n + 1) => i.val ≤ m) := by
      ext i
      simp only [mem_filter, mem_univ, true_and, mem_union, mem_singleton, Fin.ext_iff]
      omega
    have hAB : Disjoint ({(⟨m + 1, hm⟩ : Fin (n + 1))} : Finset (Fin (n + 1)))
        (univ.filter (fun i : Fin (n + 1) => i.val ≤ m)) := by
      simp
    have hvw : (pathGraph (n + 1)).Adj (⟨m + 1, hm⟩ : Fin (n + 1)) ⟨m, by omega⟩ :=
      pathGraph_adj.2 (Or.inr rfl)
    have hw : (⟨m, by omega⟩ : Fin (n + 1)) ∈ univ.filter (fun i : Fin (n + 1) => i.val ≤ m) :=
      mem_filter.2 ⟨mem_univ _, le_rfl⟩
    have hedge : ∀ a ∈ ({(⟨m + 1, hm⟩ : Fin (n + 1))} : Finset (Fin (n + 1))),
        ∀ b ∈ univ.filter (fun i : Fin (n + 1) => i.val ≤ m),
        (pathGraph (n + 1)).Adj a b → a = ⟨m + 1, hm⟩ ∧ b = ⟨m, by omega⟩ := by
      intro a ha b hb hab
      rw [mem_singleton] at ha
      subst ha
      have hb' := (mem_filter.1 hb).2
      rw [pathGraph_adj] at hab
      refine ⟨rfl, Fin.ext ?_⟩
      dsimp only at hab ⊢
      omega
    obtain ⟨e0, e1⟩ := g_union hAB (mem_singleton_self _) hw hvw hedge
    rw [hS, e0, e1, g0_single, g1_single, ih0, ih1]
    simp [tr]

theorem path_Z (n : ℕ) (ζ : ℝ) :
    Zpart (pathGraph (n + 1)) ζ = (tr ζ n).1 + (tr ζ n).2 := by
  rw [Zpart_eq_g _ ζ ⟨n, by omega⟩]
  have hU : (univ : Finset (Fin (n + 1))) = univ.filter (fun i : Fin (n + 1) => i.val ≤ n) :=
    (filter_true_of_mem (fun i _ => Fin.is_le i)).symm
  obtain ⟨h0, h1⟩ := path_g n ζ n (by omega)
  rw [hU, h0, h1]

/-- The gap `(1 + ζ) λ^m - 𝒵_{P_{m+1}}`. -/
noncomputable def Ep (ζ : ℝ) (m : ℕ) : ℝ := (1 + ζ) * spr ζ ^ m - ((tr ζ m).1 + (tr ζ m).2)

theorem Ep_one_nonneg {ζ : ℝ} (hζ : 0 ≤ ζ) : 0 ≤ Ep ζ 1 := by
  have hR := Real.sq_sqrt (show (0:ℝ) ≤ 1 + 4 * ζ ^ 2 by positivity)
  have hR0 := Real.sqrt_nonneg (1 + 4 * ζ ^ 2)
  have hX0 : 0 ≤ (1 + ζ) * Real.sqrt (1 + 4 * ζ ^ 2) := mul_nonneg (by linarith) hR0
  have hB0 : 0 ≤ 1 + ζ + 2 * ζ ^ 2 := by positivity
  have key : (1 + ζ + 2 * ζ ^ 2) ^ 2 ≤ ((1 + ζ) * Real.sqrt (1 + 4 * ζ ^ 2)) ^ 2 := by
    rw [mul_pow, hR]
    nlinarith [pow_nonneg hζ 3]
  have hle : 1 + ζ + 2 * ζ ^ 2 ≤ (1 + ζ) * Real.sqrt (1 + 4 * ζ ^ 2) := by
    nlinarith [key, hX0, hB0]
  simp only [Ep, tr, spr, pow_one]
  nlinarith [hle]

theorem tr_bound {ζ : ℝ} (hζ : 0 ≤ ζ) (n : ℕ) :
    (tr ζ n).1 + (tr ζ n).2 ≤ (1 + ζ) * spr ζ ^ n := by
  have hl := spr_pos_of_nonneg hζ
  have hz : ζ / spr ζ = 1 + 2 * ζ - spr ζ := by
    rw [div_eq_iff hl.ne']
    linear_combination spr_root ζ
  have hsum : spr ζ + ζ / spr ζ = 1 + 2 * ζ := by rw [hz]; ring
  have hprod : spr ζ * (ζ / spr ζ) = ζ := by rw [hz]; linear_combination -(spr_root ζ)
  have hμ : 0 ≤ ζ / spr ζ := div_nonneg hζ hl.le
  have hrec : ∀ m, Ep ζ (m + 1 + 1) = (1 + 2 * ζ) * Ep ζ (m + 1) - ζ * Ep ζ m := by
    intro m
    simp only [Ep, tr]
    linear_combination (1 + ζ) * spr ζ ^ m * spr_root ζ
  have hind : ∀ m, 0 ≤ Ep ζ m ∧ ζ / spr ζ * Ep ζ m ≤ Ep ζ (m + 1) := by
    intro m
    induction m with
    | zero =>
      have h0 : Ep ζ 0 = 0 := by simp [Ep, tr]
      rw [h0, mul_zero]
      exact ⟨le_rfl, Ep_one_nonneg hζ⟩
    | succ m ih =>
      obtain ⟨h0, h1⟩ := ih
      have hid : Ep ζ (m + 1 + 1) - ζ / spr ζ * Ep ζ (m + 1)
          = spr ζ * (Ep ζ (m + 1) - ζ / spr ζ * Ep ζ m) := by
        rw [hrec m]
        linear_combination (-(Ep ζ (m + 1))) * hsum + Ep ζ m * hprod
      refine ⟨by nlinarith [mul_nonneg hμ h0], ?_⟩
      nlinarith [mul_nonneg hl.le (sub_nonneg.2 h1)]
  have := (hind n).1
  unfold Ep at this
  linarith

end Path

/-! ### The fixed statements -/

/-- The partition function is invariant under graph isomorphism. -/
theorem Zpart_iso {W : Type*} [Fintype W] [DecidableEq W]
    {F : SimpleGraph V} {G : SimpleGraph W} (e : F ≃g G) (ζ : ℝ) :
    Zpart F ζ = Zpart G ζ := by
  unfold Zpart
  refine Finset.sum_nbij' (fun X => X.map e.toEquiv.toEmbedding)
    (fun Y => Y.map e.toEquiv.symm.toEmbedding) ?_ ?_ ?_ ?_ ?_
  · intro X _
    exact Finset.mem_powerset.mpr (Finset.subset_univ _)
  · intro Y _
    exact Finset.mem_powerset.mpr (Finset.subset_univ _)
  · intro X _
    have hid : e.toEquiv.toEmbedding.trans e.toEquiv.symm.toEmbedding
        = Function.Embedding.refl V := by
      ext a
      exact e.toEquiv.symm_apply_apply a
    rw [Finset.map_map, hid, Finset.map_refl]
  · intro Y _
    have hid : e.toEquiv.symm.toEmbedding.trans e.toEquiv.toEmbedding
        = Function.Embedding.refl W := by
      ext a
      exact e.toEquiv.apply_symm_apply a
    rw [Finset.map_map, hid, Finset.map_refl]
  · intro X _
    rw [Finset.card_map, eIn_map_equiv e.toEquiv (fun a b => e.map_adj_iff.symm) X]

/-- `lem:cc-gibbs` (Gibbs bound, after Csikvári and Lin). `k = Fintype.card V ≥ 1` by `hF`. -/
theorem cc_gibbs (F : SimpleGraph V) (hF : F.IsTree) (ζ : ℝ) (hζ : 0 < ζ) :
    min 1 ζ * spr ζ ^ (Fintype.card V - 1) ≤ Zpart F ζ := by
  obtain ⟨r⟩ := hF.connected.nonempty
  have hk : 1 ≤ Fintype.card V := Fintype.card_pos_iff.2 ⟨r⟩
  have hl : 0 < spr ζ := spr_pos_of_nonneg hζ.le
  have hpsi := psi_D hζ hF r r
  rw [Antinorm3.D_root hF r, card_univ] at hpsi
  have p0 := g0_pos F ζ hζ.le univ r
  have p1 := g1_pos F ζ hζ univ r (mem_univ r)
  have hroot := root_step hζ p0 p1
  have hmin := min_step hζ
  have hm : 0 < min 1 ζ := lt_min one_pos hζ
  unfold psi at hpsi
  rw [Zpart_eq_g F ζ r, ← Real.log_le_log_iff (mul_pos hm (pow_pos hl _)) (add_pos p0 p1),
    Real.log_mul hm.ne' (pow_pos hl _).ne', Real.log_pow, Nat.cast_sub hk, Nat.cast_one,
    sub_mul, one_mul]
  linarith

/-- `lem:cc-path` (Paths). -/
theorem cc_path (k : ℕ) (hk : 1 ≤ k) (F : SimpleGraph V) (e : F ≃g SimpleGraph.pathGraph k)
    (ζ : ℝ) (hζ : 0 ≤ ζ) :
    Zpart F ζ ≤ (1 + ζ) * spr ζ ^ (k - 1) ∧ (1 + ζ) * spr ζ ^ (k - 1) ≤ 2 * spr ζ ^ k := by
  obtain ⟨n, rfl⟩ : ∃ n, k = n + 1 := ⟨k - 1, by omega⟩
  have hl := spr_pos_of_nonneg hζ
  have h1 := one_add_le_spr ζ
  refine ⟨?_, ?_⟩
  · rw [Zpart_iso e, path_Z, Nat.add_sub_cancel]
    exact tr_bound hζ n
  · rw [Nat.add_sub_cancel, pow_succ]
    nlinarith [mul_le_mul_of_nonneg_left h1 (pow_pos hl n).le, pow_pos hl n]

/-! ### Optional parts -/

/-- The remarks after `def:cc-Z`. -/
theorem spr_facts :
    spr 0 = 1 ∧ ContinuousOn spr (Set.Ici 0) ∧ StrictMonoOn spr (Set.Ici 0) ∧
    (∀ ζ : ℝ, 0 < ζ → 1 < spr ζ) ∧ (∀ ζ : ℝ, (1 + 2 * ζ) / 2 ≤ spr ζ) := by
  refine ⟨?_, ?_, ?_, ?_, ?_⟩
  · unfold spr
    norm_num
  · have hc : Continuous spr := by
      unfold spr
      fun_prop
    exact hc.continuousOn
  · intro a ha b _ hab
    simp only [Set.mem_Ici] at ha
    have h1 : Real.sqrt (1 + 4 * a ^ 2) ≤ Real.sqrt (1 + 4 * b ^ 2) :=
      Real.sqrt_le_sqrt (by nlinarith)
    unfold spr
    linarith
  · intro ζ hζ
    have := one_add_le_spr ζ
    linarith
  · intro ζ
    have h1 := Real.sqrt_nonneg (1 + 4 * ζ ^ 2)
    unfold spr
    linarith

/-- `M_ζ` of `def:cc-Z`. -/
noncomputable def Mz (ζ : ℝ) : Matrix (Fin 2) (Fin 2) ℝ := !![1, ζ; 1, 2 * ζ]

/-- `M_ζ` has characteristic polynomial `X² - (1 + 2ζ)X + ζ`, which factors over `ℝ` as
`(X - spr ζ)(X - ζ / spr ζ)` with `0 ≤ ζ / spr ζ ≤ spr ζ`: both eigenvalues are real and
nonnegative, and `spr ζ` is the largest. Every complex root of the characteristic polynomial has
modulus at most `spr ζ`, so `spr ζ` is also the spectral radius (`def:cc-Z`). -/
theorem spr_eigen (ζ : ℝ) (hζ : 0 ≤ ζ) :
    (Mz ζ).charpoly = Polynomial.X ^ 2 - Polynomial.C (1 + 2 * ζ) * Polynomial.X + Polynomial.C ζ ∧
    (Mz ζ).charpoly =
      (Polynomial.X - Polynomial.C (spr ζ)) * (Polynomial.X - Polynomial.C (ζ / spr ζ)) ∧
    0 ≤ ζ / spr ζ ∧ ζ / spr ζ ≤ spr ζ ∧
    ∀ μ : ℂ, Polynomial.aeval μ (Mz ζ).charpoly = 0 → ‖μ‖ ≤ spr ζ := by
  have hpos : 0 < spr ζ := spr_pos_of_nonneg hζ
  have hl : spr ζ ≠ 0 := ne_of_gt hpos
  have hz : ζ / spr ζ = 1 + 2 * ζ - spr ζ := by
    rw [div_eq_iff hl]
    linear_combination spr_root ζ
  have hsum : spr ζ + ζ / spr ζ = 1 + 2 * ζ := by
    rw [hz]
    ring
  have hprod : spr ζ * (ζ / spr ζ) = ζ := by
    rw [hz]
    linear_combination -(spr_root ζ)
  have h1 : (Mz ζ).charpoly =
      Polynomial.X ^ 2 - Polynomial.C (1 + 2 * ζ) * Polynomial.X + Polynomial.C ζ := by
    rw [Matrix.charpoly, Matrix.det_fin_two]
    simp [Mz, map_ofNat Polynomial.C 2]
    ring
  have hfac : (Mz ζ).charpoly =
      (Polynomial.X - Polynomial.C (spr ζ)) * (Polynomial.X - Polynomial.C (ζ / spr ζ)) := by
    rw [h1]
    have e1 : Polynomial.C (1 + 2 * ζ) = Polynomial.C (spr ζ) + Polynomial.C (ζ / spr ζ) := by
      rw [← Polynomial.C_add, hsum]
    have e2 : Polynomial.C ζ = Polynomial.C (spr ζ) * Polynomial.C (ζ / spr ζ) := by
      rw [← Polynomial.C_mul, hprod]
    rw [e1, e2]
    ring
  have hm0 : 0 ≤ ζ / spr ζ := div_nonneg hζ hpos.le
  have hm1 : ζ / spr ζ ≤ spr ζ := by
    rw [div_le_iff₀ hpos]
    nlinarith [mul_le_mul (one_add_le_spr ζ) (one_add_le_spr ζ) (by linarith) hpos.le,
      sq_nonneg ζ]
  have key : ∀ r : ℝ, 0 ≤ r → ‖algebraMap ℝ ℂ r‖ = r := fun r hr => by
    simp [abs_of_nonneg hr]
  refine ⟨h1, hfac, hm0, hm1, ?_⟩
  intro μ hμ
  rw [hfac] at hμ
  simp only [map_mul, map_sub, Polynomial.aeval_X, Polynomial.aeval_C, mul_eq_zero,
    sub_eq_zero] at hμ
  rcases hμ with h | h
  · have := key _ hpos.le
    rw [← h] at this
    exact this.le
  · have := key _ hm0
    rw [← h] at this
    linarith

open Classical in
/-- `eIn F X = |E(F[X])|` for Mathlib's induced subgraph. -/
theorem eIn_eq_induce (F : SimpleGraph V) (X : Finset V) :
    eIn F X = (F.induce (X : Set V)).edgeFinset.card := by
  unfold eIn
  symm
  refine Finset.card_bij (fun b _ => Sym2.map Subtype.val b) ?_ ?_ ?_
  · intro b
    refine Sym2.inductionOn b (fun x y => ?_)
    intro hb
    have h1 : (F.induce (X : Set V)).Adj x y :=
      (SimpleGraph.mem_edgeSet _).mp (SimpleGraph.mem_edgeFinset.mp hb)
    have hadj : F.Adj x.1 y.1 := h1
    simp only [Finset.mem_filter, SimpleGraph.mem_edgeFinset, sym2_pair_map,
      SimpleGraph.mem_edgeSet]
    refine ⟨hadj, fun v hv => ?_⟩
    rcases Sym2.mem_iff.mp hv with rfl | rfl
    · exact Finset.mem_coe.mp x.2
    · exact Finset.mem_coe.mp y.2
  · intro a₁ _ a₂ _ h
    exact Sym2.map.injective Subtype.val_injective h
  · intro a
    refine Sym2.inductionOn a (fun x y => ?_)
    intro ha
    have hF := Finset.mem_filter.mp ha
    have hx : x ∈ X := hF.2 x (Sym2.mem_mk_left x y)
    have hy : y ∈ X := hF.2 y (Sym2.mem_mk_right x y)
    have hadj : F.Adj x y :=
      (SimpleGraph.mem_edgeSet _).mp (SimpleGraph.mem_edgeFinset.mp hF.1)
    refine ⟨s(⟨x, hx⟩, ⟨y, hy⟩), ?_, ?_⟩
    · rw [SimpleGraph.mem_edgeFinset, SimpleGraph.mem_edgeSet]
      exact hadj
    · rfl

/-- Paths minimize the partition function among trees with the same number of vertices, up to
the factor `min 1 ζ / (1 + ζ)` (`lem:cc-gibbs` and `lem:cc-path` combined). -/
theorem cc_path_min (F : SimpleGraph V) (hF : F.IsTree) (ζ : ℝ) (hζ : 0 < ζ) :
    min 1 ζ / (1 + ζ) * Zpart (SimpleGraph.pathGraph (Fintype.card V)) ζ ≤ Zpart F ζ := by
  obtain ⟨r⟩ := hF.connected.nonempty
  have hk : 1 ≤ Fintype.card V := Fintype.card_pos_iff.2 ⟨r⟩
  obtain ⟨hp, -⟩ := cc_path (Fintype.card V) hk (SimpleGraph.pathGraph (Fintype.card V))
    SimpleGraph.Iso.refl ζ hζ.le
  have hg := cc_gibbs F hF ζ hζ
  have hm : 0 ≤ min 1 ζ := le_min zero_le_one hζ.le
  have h1 : (0 : ℝ) < 1 + ζ := by linarith
  calc min 1 ζ / (1 + ζ) * Zpart (SimpleGraph.pathGraph (Fintype.card V)) ζ
      ≤ min 1 ζ / (1 + ζ) * ((1 + ζ) * spr ζ ^ (Fintype.card V - 1)) :=
        mul_le_mul_of_nonneg_left hp (div_nonneg hm h1.le)
    _ = min 1 ζ * spr ζ ^ (Fintype.card V - 1) := by field_simp
    _ ≤ Zpart F ζ := hg

/-- Sanity check of the definitions: computed directly from `eIn` and
`Zpart`, without the transfer recursion. -/
theorem Zpart_pathGraph_two (ζ : ℝ) :
    Zpart (SimpleGraph.pathGraph 2) ζ = 1 + 2 * ζ + 2 * ζ ^ 2 := by
  have hpow : (univ : Finset (Fin 2)).powerset = {∅, {0}, {1}, {0, 1}} := by decide
  have h1 : (∅ : Finset (Fin 2)) ∉ ({{0}, {1}, {0, 1}} : Finset (Finset (Fin 2))) := by
    decide
  have h2 : ({0} : Finset (Fin 2)) ∉ ({{1}, {0, 1}} : Finset (Finset (Fin 2))) := by
    decide
  have h3 : ({1} : Finset (Fin 2)) ∉ ({{0, 1}} : Finset (Finset (Fin 2))) := by decide
  have c01 : ({0, 1} : Finset (Fin 2)).card = 2 := by decide
  have v0 : eIn (SimpleGraph.pathGraph 2) ∅ = 0 := by
    rw [eIn_pg2]; decide
  have v1 : eIn (SimpleGraph.pathGraph 2) {0} = 0 := by
    rw [eIn_pg2]; decide
  have v2 : eIn (SimpleGraph.pathGraph 2) {1} = 0 := by
    rw [eIn_pg2]; decide
  have v3 : eIn (SimpleGraph.pathGraph 2) {0, 1} = 1 := by
    rw [eIn_pg2]; decide
  unfold Zpart
  rw [hpow, Finset.sum_insert h1, Finset.sum_insert h2, Finset.sum_insert h3,
    Finset.sum_singleton]
  simp only [v0, v1, v2, v3, Finset.card_empty, Finset.card_singleton, c01]
  ring

end MaxLeafFormal.CCGibbs
Back to top