MaxLeafFormal.CCRate

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

Statements of the paper formalised here: Lemma 11.12, Lemma D.9.8.

import MaxLeafFormal.CCData
import MaxLeafFormal.EncloseH

/-!
# Graph Motif: the rate certificate for `c = 2`, and the checks for `c = 3`

`lem:cc-cert` of the paper: every
`σ ∈ (0, 1]` satisfies (R1), (R2) or (R3) of `cor:cc-rate` with `c = 2`, `ω = 0.71755`, `Γ₀ = 16`,
and `0.71755 < log₂ 1.6444` (`Real.logb 2`). For `c = 3`, `cc_c3` proves the arithmetic facts that
`ancillary/component_count_rate_interval.py` checks: `spr(6/5) = 3`, `(6/5)/3 < 3`, `1 + 2·(6/5)/3 = 9/5`,
`3^64 < 5^45` and `log₂(9/5) < H(26/45)`. It does not state `cor:cc-T`(b) itself; the manuscript
proves (B2) and (B3) by hand.
-/

namespace MaxLeafFormal.CCRate

open Enclose

/-- `spr(M_ζ) = (1 + 2ζ + √(1 + 4ζ²))/2`. -/
noncomputable def spr (ζ : ℝ) : ℝ := (1 + 2 * ζ + Real.sqrt (1 + 4 * ζ^2)) / 2

/-- `φ_{c,σ}(y) = log(1+y) - σ log(spr(M_{cy/2})/c)` (`eq:cc-phi`). -/
noncomputable def phi (c σ y : ℝ) : ℝ := Real.logb 2 (1 + y) - σ * Real.logb 2 (spr (c * y / 2) / c)

def R1 (c ω σ : ℝ) : Prop := σ * Real.logb 2 c ≤ ω

def R2 (ω σ : ℝ) : Prop := MLS.H2 σ ≤ ω

def R3 (c ω Γ₀ σ : ℝ) : Prop :=
  ∃ y > 0, phi c σ y ≤ ω ∧ spr (c * y / 2) / min 1 (c * y / 2) ≤ Γ₀

/-! ### The checker (over `ℚ`) -/

/-- An enclosure of `spr(ζ)` for rational `ζ ≥ 0`. -/
def sprQ (ζ : ℚ) : QI := ((QI.pt (1 + 2 * ζ)).add (sqrtQ (1 + 4 * ζ ^ 2) 80)).smul (1 / 2)

/-- The interval `[a, a + 10⁻⁴]` with parameter `y` (`c = 2`, so `ζ = y`): `y > 0`,
`max(φ_{2,a}(y), φ_{2,a+10⁻⁴}(y)) < 0.71755` with enclosures, and `spr(y)/min(1, y) ≤ 16`. -/
def intervalOK (a y : ℚ) : Bool :=
  let b := a + 1 / 10 ^ 4
  let S := sprQ y
  let L1 := log2Q (1 + y) 60 80
  let LS := log2I (S.smul (1 / 2)) 60 80
  decide (0 < y) && decide (0 < S.lo) &&
    decide (L1.hi - a * LS.lo < 71755 / 10 ^ 5) && decide (L1.hi - b * LS.lo < 71755 / 10 ^ 5) &&
    decide (0 ≤ a) && decide (S.hi ≤ 16 * min 1 y)

/-- Consecutive intervals of width `10⁻⁴` starting at `a`. -/
def checkIntervals : ℚ → List ℚ → Bool
  | _, [] => true
  | a, y :: rest => intervalOK a y && checkIntervals (a + 1 / 10 ^ 4) rest

/-! The kernel check, in chunks of 100 intervals (chunk `k` starts at `0.7175 + 100k/10^4`). -/
set_option maxRecDepth 100000 in

theorem intervals_ok_0 :
    checkIntervals (7175 / 10 ^ 4 + 0 / 10 ^ 4) ((ys.drop 0).take 100) = true := by
  decide +kernel
set_option maxRecDepth 100000 in

theorem intervals_ok_1 :
    checkIntervals (7175 / 10 ^ 4 + 100 / 10 ^ 4) ((ys.drop 100).take 100) = true := by
  decide +kernel
set_option maxRecDepth 100000 in

theorem intervals_ok_2 :
    checkIntervals (7175 / 10 ^ 4 + 200 / 10 ^ 4) ((ys.drop 200).take 100) = true := by
  decide +kernel
set_option maxRecDepth 100000 in

theorem intervals_ok_3 :
    checkIntervals (7175 / 10 ^ 4 + 300 / 10 ^ 4) ((ys.drop 300).take 100) = true := by
  decide +kernel
set_option maxRecDepth 100000 in

theorem intervals_ok_4 :
    checkIntervals (7175 / 10 ^ 4 + 400 / 10 ^ 4) ((ys.drop 400).take 100) = true := by
  decide +kernel
set_option maxRecDepth 100000 in

theorem intervals_ok_5 :
    checkIntervals (7175 / 10 ^ 4 + 500 / 10 ^ 4) ((ys.drop 500).take 100) = true := by
  decide +kernel
set_option maxRecDepth 100000 in

theorem intervals_ok_6 :
    checkIntervals (7175 / 10 ^ 4 + 600 / 10 ^ 4) ((ys.drop 600).take 100) = true := by
  decide +kernel
set_option maxRecDepth 100000 in

theorem intervals_ok_7 :
    checkIntervals (7175 / 10 ^ 4 + 700 / 10 ^ 4) ((ys.drop 700).take 100) = true := by
  decide +kernel
set_option maxRecDepth 100000 in

theorem intervals_ok_8 :
    checkIntervals (7175 / 10 ^ 4 + 800 / 10 ^ 4) ((ys.drop 800).take 100) = true := by
  decide +kernel
set_option maxRecDepth 100000 in

theorem intervals_ok_9 :
    checkIntervals (7175 / 10 ^ 4 + 900 / 10 ^ 4) ((ys.drop 900).take 100) = true := by
  decide +kernel

theorem ys_length : ys.length = 925 := by decide +kernel

/-! ### Soundness and the theorems -/

/-- Every entry of a checked list is an accepted interval. -/
theorem checkIntervals_sound (l : List ℚ) : ∀ (a : ℚ), checkIntervals a l = true →
    ∀ i, i < l.length → intervalOK (a + (i : ℚ) / 10 ^ 4) (l.getD i 0) = true := by
  induction l with
  | nil => intro a _ i hi; exact absurd hi (Nat.not_lt_zero _)
  | cons y rest ih =>
    intro a h i hi
    simp only [checkIntervals, Bool.and_eq_true] at h
    cases i with
    | zero => simpa using h.1
    | succ i =>
      have ih' := ih (a + 1 / 10 ^ 4) h.2 i (by simpa using hi)
      have e : a + ((i + 1 : ℕ) : ℚ) / 10 ^ 4 = a + 1 / 10 ^ 4 + (i : ℚ) / 10 ^ 4 := by
        push_cast; ring
      rw [e]
      simpa using ih'

/-- A chunk `s` covers the indices `s ≤ j < s + 100`. -/
theorem chunk (s : ℕ) (a : ℚ) (ha : a = 7175 / 10 ^ 4 + (s : ℚ) / 10 ^ 4)
    (hs : checkIntervals a ((ys.drop s).take 100) = true) (j : ℕ)
    (h1 : s ≤ j) (h2 : j < s + 100) (hj : j < 925) :
    intervalOK (7175 / 10 ^ 4 + (j : ℚ) / 10 ^ 4) (ys.getD j 0) = true := by
  have hlen : j - s < ((ys.drop s).take 100).length := by
    simp only [List.length_take, List.length_drop, ys_length]; omega
  have key := checkIntervals_sound _ a hs (j - s) hlen
  have e1 : a + ((j - s : ℕ) : ℚ) / 10 ^ 4 = 7175 / 10 ^ 4 + (j : ℚ) / 10 ^ 4 := by
    rw [ha, Nat.cast_sub h1]; ring
  have e2 : ((ys.drop s).take 100).getD (j - s) 0 = ys.getD j 0 := by
    rw [List.getD_eq_getElem?_getD, List.getD_eq_getElem?_getD, List.getElem?_take,
      ite_eq_left (show j - s < 100 by omega), List.getElem?_drop, show s + (j - s) = j by omega]
  rw [e1, e2] at key
  exact key

theorem all_ok (j : ℕ) (hj : j < 925) :
    intervalOK (7175 / 10 ^ 4 + (j : ℚ) / 10 ^ 4) (ys.getD j 0) = true := by
  obtain ⟨k, hk⟩ : ∃ k, j / 100 = k := ⟨_, rfl⟩
  have hk10 : k < 10 := by omega
  interval_cases k
  · exact chunk 0 _ (by norm_num) intervals_ok_0 j (by omega) (by omega) hj
  · exact chunk 100 _ (by norm_num) intervals_ok_1 j (by omega) (by omega) hj
  · exact chunk 200 _ (by norm_num) intervals_ok_2 j (by omega) (by omega) hj
  · exact chunk 300 _ (by norm_num) intervals_ok_3 j (by omega) (by omega) hj
  · exact chunk 400 _ (by norm_num) intervals_ok_4 j (by omega) (by omega) hj
  · exact chunk 500 _ (by norm_num) intervals_ok_5 j (by omega) (by omega) hj
  · exact chunk 600 _ (by norm_num) intervals_ok_6 j (by omega) (by omega) hj
  · exact chunk 700 _ (by norm_num) intervals_ok_7 j (by omega) (by omega) hj
  · exact chunk 800 _ (by norm_num) intervals_ok_8 j (by omega) (by omega) hj
  · exact chunk 900 _ (by norm_num) intervals_ok_9 j (by omega) (by omega) hj

theorem sprQ_sound (ζ : ℚ) : (sprQ ζ).Contains (spr (ζ : ℝ)) := by
  have h := QI.smul_sound (1 / 2 : ℚ)
    (QI.add_sound (QI.pt_sound (1 + 2 * ζ)) (sqrtQ_sound (q := 1 + 4 * ζ ^ 2) (by positivity) 80))
  have e : spr (ζ : ℝ) = ((1 / 2 : ℚ) : ℝ) *
      (((1 + 2 * ζ : ℚ) : ℝ) + Real.sqrt ((1 + 4 * ζ ^ 2 : ℚ) : ℝ)) := by
    unfold spr; push_cast; ring
  rw [e]
  exact h

/-- The real core of `R3` for one interval: `σ ∈ [a, b]`, affine in `σ`. -/
theorem R3_real {σ a b y L1 LS : ℝ} (hσ0 : 0 ≤ σ) (hσ1 : a ≤ σ) (hσ2 : σ ≤ b)
    (hA : Real.logb 2 (1 + y) ≤ L1) (hB : LS ≤ Real.logb 2 (spr y / 2))
    (hC : L1 - a * LS < 71755 / 10 ^ 5) (hD : L1 - b * LS < 71755 / 10 ^ 5) :
    Real.logb 2 (1 + y) - σ * Real.logb 2 (spr y / 2) ≤ 71755 / 10 ^ 5 := by
  have h2 := mul_le_mul_of_nonneg_left hB hσ0
  rcases le_total 0 LS with hL | hL
  · have h1 := mul_le_mul_of_nonneg_right hσ1 hL
    linarith
  · have h1 := mul_le_mul_of_nonpos_right hσ2 hL
    linarith

theorem intervalOK_R3 {a y : ℚ} (h : intervalOK a y = true) {σ : ℝ}
    (hσ1 : (a : ℝ) ≤ σ) (hσ2 : σ ≤ ((a + 1 / 10 ^ 4 : ℚ) : ℝ)) :
    R3 2 (71755 / 10 ^ 5) 16 σ := by
  simp only [intervalOK, Bool.and_eq_true, decide_eq_true_eq] at h
  obtain ⟨⟨⟨⟨⟨hy, hS0⟩, hL2⟩, hL3⟩, ha0⟩, hS1⟩ := h
  have hc : ((sprQ y).smul (1 / 2)).lo = 1 / 2 * (sprQ y).lo := by
    unfold QI.smul; rw [ite_eq_left (show (0:ℚ) ≤ 1 / 2 by norm_num)]
  have hpos : 0 < ((sprQ y).smul (1 / 2)).lo := by
    rw [hc]; exact mul_pos (by norm_num) hS0
  have hLS := log2I_sound (QI.smul_sound (1 / 2 : ℚ) (sprQ_sound y)) hpos 60 80
  have hA := log2Q_sound (q := 1 + y) (by linarith) 60 80
  have hS := sprQ_sound y
  simp only [QI.Contains] at hLS hA hS
  have e2 : ((1 / 2 : ℚ) : ℝ) * spr (y : ℝ) = spr (y : ℝ) / 2 := by push_cast; ring
  rw [e2] at hLS
  have hA' : Real.logb 2 (1 + (y : ℝ)) ≤ ((log2Q (1 + y) 60 80).hi : ℝ) := by
    have h := hA.2; push_cast at h; exact h
  have hσ2' : σ ≤ (a : ℝ) + 1 / 10 ^ 4 := by push_cast at hσ2; exact hσ2
  have hC : ((log2Q (1 + y) 60 80).hi : ℝ) - (a : ℝ) *
      ((log2I ((sprQ y).smul (1 / 2)) 60 80).lo : ℝ) < 71755 / 10 ^ 5 := by
    have := (Rat.cast_lt (K := ℝ)).mpr hL2
    push_cast at this; linarith
  have hD : ((log2Q (1 + y) 60 80).hi : ℝ) - ((a : ℝ) + 1 / 10 ^ 4) *
      ((log2I ((sprQ y).smul (1 / 2)) 60 80).lo : ℝ) < 71755 / 10 ^ 5 := by
    have := (Rat.cast_lt (K := ℝ)).mpr hL3
    push_cast at this; linarith
  have hσ0 : (0 : ℝ) ≤ σ := le_trans (Rat.cast_nonneg.mpr ha0) hσ1
  unfold R3
  refine ⟨(y : ℝ), by exact_mod_cast hy, ?_, ?_⟩
  · unfold phi
    rw [show (2 : ℝ) * (y : ℝ) / 2 = (y : ℝ) by ring]
    exact R3_real hσ0 hσ1 hσ2' hA' hLS.1 hC hD
  · rw [show (2 : ℝ) * (y : ℝ) / 2 = (y : ℝ) by ring]
    have hm : (0 : ℝ) < min 1 (y : ℝ) := lt_min one_pos (by exact_mod_cast hy)
    have hS1' : ((sprQ y).hi : ℝ) ≤ 16 * min 1 (y : ℝ) := by
      have := (Rat.cast_le (K := ℝ)).mpr hS1
      push_cast at this; exact this
    rw [div_le_iff₀ hm]
    linarith [hS.2]

/-- `spr(M_ζ)` is a root of `λ² - (1 + 2ζ)λ + ζ` (the characteristic polynomial of `M_ζ`). -/
theorem spr_root (ζ : ℝ) : spr ζ ^ 2 - (1 + 2 * ζ) * spr ζ + ζ = 0 := by
  have hs : Real.sqrt (1 + 4 * ζ ^ 2) ^ 2 = 1 + 4 * ζ ^ 2 :=
    Real.sq_sqrt (by positivity)
  unfold spr
  linear_combination (1 / 4 : ℝ) * hs

theorem cc_cert :
    (∀ σ ∈ Set.Ioc (0:ℝ) 1, R1 2 (71755/10^5) σ ∨ R2 (71755/10^5) σ ∨ R3 2 (71755/10^5) 16 σ) ∧
    (71755/10^5 : ℝ) < Real.logb 2 (16444/10^4) := by
  refine ⟨?_, ?_⟩
  · intro σ hσ
    obtain ⟨hσ0, hσ1⟩ := Set.mem_Ioc.mp hσ
    rcases le_total σ (7175 / 10 ^ 4) with h1 | h1
    · left
      unfold R1
      rw [Real.logb_self_eq_one (by norm_num)]
      linarith
    rcases lt_or_ge σ (81 / 100) with h2 | h2
    · right; right
      have ht0 : 0 ≤ (σ - 7175 / 10 ^ 4) * 10 ^ 4 := mul_nonneg (by linarith) (by norm_num)
      obtain ⟨j, hj⟩ : ∃ j : ℕ, j = ⌊(σ - 7175 / 10 ^ 4) * 10 ^ 4⌋₊ := ⟨_, rfl⟩
      have hf1 : (j : ℝ) ≤ (σ - 7175 / 10 ^ 4) * 10 ^ 4 := by
        rw [hj]; exact Nat.floor_le ht0
      have hf2 : (σ - 7175 / 10 ^ 4) * 10 ^ 4 < (j : ℝ) + 1 := by
        rw [hj]; exact Nat.lt_floor_add_one _
      have hj925 : j < 925 := by
        have : (j : ℝ) < 925 := by linarith
        exact_mod_cast this
      have hσa : ((7175 / 10 ^ 4 + (j : ℚ) / 10 ^ 4 : ℚ) : ℝ) ≤ σ := by
        push_cast; linarith
      have hσb : σ ≤ ((7175 / 10 ^ 4 + (j : ℚ) / 10 ^ 4 + 1 / 10 ^ 4 : ℚ) : ℝ) := by
        push_cast; linarith
      exact intervalOK_R3 (all_ok j hj925) hσa hσb
    · right; left
      unfold R2
      have hanti : Real.binEntropy σ ≤ Real.binEntropy (81 / 100) :=
        Real.binEntropy_strictAntiOn.antitoneOn
          ⟨by norm_num, by norm_num⟩ ⟨le_trans (by norm_num) h2, hσ1⟩ h2
      have hH : MLS.H2 σ ≤ MLS.H2 (81 / 100) := by
        unfold MLS.H2
        exact div_le_div_of_nonneg_right hanti (Real.log_nonneg (by norm_num))
      have hQ := H2Q_sound (x := 81 / 100) (by norm_num) (by norm_num) 60 80
      have hb : (H2Q (81 / 100) 60 80).hi < 71755 / 10 ^ 5 := by decide +kernel
      have hc : ((81 / 100 : ℚ) : ℝ) = 81 / 100 := by norm_num
      simp only [QI.Contains] at hQ
      rw [hc] at hQ
      have hbR := (Rat.cast_lt (K := ℝ)).mpr hb
      push_cast at hbR
      linarith [hQ.2]
  · have hL := log2Q_sound (q := 16444 / 10 ^ 4) (by norm_num) 60 80
    have hlo : (71755 / 10 ^ 5 : ℚ) < (log2Q (16444 / 10 ^ 4) 60 80).lo := by
      decide +kernel
    have hlo' := (Rat.cast_lt (K := ℝ)).mpr hlo
    push_cast at hlo'
    have hc : ((16444 / 10 ^ 4 : ℚ) : ℝ) = 16444 / 10 ^ 4 := by norm_num
    simp only [QI.Contains] at hL
    rw [hc] at hL
    linarith [hL.1]

theorem cc_c3 :
    spr (6/5) = 3 ∧ (6/5 : ℝ) / 3 < 3 ∧ (1 : ℝ) + 2 * (6/5) / 3 = 9/5 ∧
    (3:ℕ) ^ 64 < 5 ^ 45 ∧ Real.logb 2 (9/5) < MLS.H2 (26/45) := by
  have hsq : Real.sqrt (1 + 4 * (6 / 5 : ℝ) ^ 2) = 13 / 5 := by
    rw [show (1 : ℝ) + 4 * (6 / 5) ^ 2 = (13 / 5) ^ 2 by norm_num]
    exact Real.sqrt_sq (by norm_num)
  have hspr : spr (6 / 5) = 3 := by
    unfold spr; rw [hsq]; norm_num
  refine ⟨hspr, by norm_num, by norm_num, by norm_num, ?_⟩
  have hL := log2Q_sound (q := 9 / 5) (by norm_num) 60 80
  have hH := H2Q_sound (x := 26 / 45) (by norm_num) (by norm_num) 60 80
  have hcmp : (log2Q (9 / 5) 60 80).hi < (H2Q (26 / 45) 60 80).lo := by decide +kernel
  have hcmpR : (((log2Q (9 / 5) 60 80).hi : ℚ) : ℝ) < (((H2Q (26 / 45) 60 80).lo : ℚ) : ℝ) := by
    exact_mod_cast hcmp
  have e1 : ((9 / 5 : ℚ) : ℝ) = 9 / 5 := by norm_num
  have e2 : ((26 / 45 : ℚ) : ℝ) = 26 / 45 := by norm_num
  simp only [QI.Contains] at hL hH
  rw [e1] at hL
  rw [e2] at hH
  linarith [hL.2, hH.1]

end MaxLeafFormal.CCRate
Back to top