Skip to content

Commit ef98995

Browse files
author
ArkLib Agent
committed
fix(coding-theory): Johnson bound sum_comm via conv_lhs
1 parent cbbbe69 commit ef98995

1 file changed

Lines changed: 262 additions & 0 deletions

File tree

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
/-
2+
Copyright (c) 2026 ArkLib Contributors. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: ArkLib Contributors
5+
-/
6+
import Mathlib.InformationTheory.Hamming
7+
import Mathlib.Algebra.Order.Chebyshev
8+
import Mathlib.Algebra.BigOperators.Ring.Finset
9+
import Mathlib.Tactic.Linarith
10+
import Mathlib.Tactic.Positivity
11+
import Mathlib.Tactic.Ring
12+
import Mathlib.Tactic.NormNum
13+
14+
/-!
15+
# The Johnson list-size bound (combinatorial, isolated)
16+
17+
This file proves the classical **q-ary Johnson bound** on the number of codewords of a code that lie
18+
within a given Hamming radius of a fixed word — the foundational list-decoding bound underlying the
19+
correlated-agreement / proximity-gap "true form" (the Johnson radius `δ < 1 - √ρ`), which is
20+
**absent from mathlib**.
21+
22+
It is deliberately **self-contained**: it imports only `Mathlib` (Hamming distance, big operators,
23+
the Cauchy–Schwarz sum inequality), so it builds independently of the rest of ArkLib. The
24+
"universal-over-all-codes" form of the proximity gap is false; the correct form lives at the Johnson
25+
radius, whose combinatorial heart is the bound below.
26+
27+
## Main statement
28+
29+
`card_mul_johnsonDenom_le`: for a finite set of codewords `C` with pairwise distance `≥ d`, all
30+
within distance `e` of a word `w`, writing `n = |ι|`:
31+
32+
`(C.card) · ((n - e)² - n·(n - d)) ≤ n · d`.
33+
34+
In the **Johnson regime** `(n - e)² > n·(n - d)` this yields `card_le_div` :
35+
`C.card ≤ n·d / ((n - e)² - n·(n - d))`.
36+
37+
## Proof
38+
39+
Double counting. For each coordinate `i`, `colCount i` is the number of codewords agreeing with `w`
40+
at `i`. Then `∑ i, colCount i = ∑ c, agree(c, w) ≥ |C|·(n - e)` and, by Cauchy–Schwarz,
41+
`(∑ i, colCount i)² ≤ n · ∑ i, (colCount i)²`. Expanding `∑ i, (colCount i)²` over ordered pairs of
42+
codewords, the diagonal contributes `∑ c, agree(c, w) ≤ |C|·n` and each off-diagonal pair `(c, c')`
43+
contributes `agree(c, c') ≤ n - d`. Combining and cancelling one factor of `|C|` gives the bound.
44+
-/
45+
46+
open scoped BigOperators
47+
48+
namespace ArkLib.JohnsonBound
49+
50+
variable {ι : Type*} [Fintype ι] [DecidableEq ι]
51+
variable {Sigma : Type*} [DecidableEq Sigma]
52+
53+
/-- The number of coordinates on which `c` and `w` agree. -/
54+
def agree (c w : ι → Sigma) : ℕ := (Finset.univ.filter (fun i => c i = w i)).card
55+
56+
/-- Agreement plus Hamming distance equals the block length. -/
57+
theorem agree_add_hammingDist (c w : ι → Sigma) :
58+
agree c w + hammingDist c w = Fintype.card ι := by
59+
classical
60+
have h := Finset.filter_card_add_filter_neg_card_eq_card
61+
(s := (Finset.univ : Finset ι)) (p := fun i => c i = w i)
62+
simpa only [agree, hammingDist, Finset.card_univ, ne_eq] using h
63+
64+
/-- Agreement is at least `n - e` when `c` is within distance `e` of `w` (as naturals). -/
65+
theorem natSub_le_agree {c w : ι → Sigma} {e : ℕ} (h : hammingDist c w ≤ e) :
66+
Fintype.card ι - e ≤ agree c w := by
67+
have := agree_add_hammingDist c w; omega
68+
69+
/-- Agreement is at most `n - d` for two words at distance `≥ d` (as naturals). -/
70+
theorem agree_le_natSub {c c' : ι → Sigma} {d : ℕ} (h : d ≤ hammingDist c c') :
71+
agree c c' ≤ Fintype.card ι - d := by
72+
have := agree_add_hammingDist c c'; omega
73+
74+
/-! ### Real-valued indicators and the counting identities -/
75+
76+
/-- `0/1` indicator (in `ℚ`) that `c` agrees with `w` at coordinate `i`. -/
77+
noncomputable def ind (c w : ι → Sigma) (i : ι) : ℚ := if c i = w i then 1 else 0
78+
79+
theorem ind_nonneg (c w : ι → Sigma) (i : ι) : 0 ≤ ind c w i := by
80+
unfold ind; split <;> norm_num
81+
82+
/-- Summing the indicator over coordinates gives the agreement count. -/
83+
theorem sum_ind_eq_agree (c w : ι → Sigma) : ∑ i, ind c w i = (agree c w : ℚ) := by
84+
classical
85+
unfold ind agree
86+
rw [Finset.sum_boole]
87+
88+
/-- Product of two agreement indicators is the "both agree with `w`" indicator. -/
89+
theorem ind_mul_ind (c c' w : ι → Sigma) (i : ι) :
90+
ind c w i * ind c' w i = if c i = w i ∧ c' i = w i then 1 else 0 := by
91+
unfold ind; split_ifs with h1 h2 h3 <;> first | rfl | simp_all
92+
93+
/-- The number of codewords in `C` agreeing with `w` at coordinate `i` (in `ℚ`). -/
94+
noncomputable def colCount (C : Finset (ι → Sigma)) (w : ι → Sigma) (i : ι) : ℚ :=
95+
∑ c ∈ C, ind c w i
96+
97+
theorem colCount_nonneg (C : Finset (ι → Sigma)) (w : ι → Sigma) (i : ι) :
98+
0 ≤ colCount C w i :=
99+
Finset.sum_nonneg (fun c _ => ind_nonneg c w i)
100+
101+
/-- `∑ i, colCount i = ∑ c, agree(c, w)`. -/
102+
theorem sum_colCount (C : Finset (ι → Sigma)) (w : ι → Sigma) :
103+
∑ i, colCount C w i = ∑ c ∈ C, (agree c w : ℚ) := by
104+
unfold colCount
105+
rw [Finset.sum_comm]
106+
exact Finset.sum_congr rfl (fun c _ => sum_ind_eq_agree c w)
107+
108+
/-- The "common agreement" count of `c, c'` against `w`, in `ℚ`. -/
109+
noncomputable def common (c c' w : ι → Sigma) : ℚ := ∑ i, ind c w i * ind c' w i
110+
111+
/-- `common c c w = agree(c, w)`. -/
112+
theorem common_self (c w : ι → Sigma) : common c c w = (agree c w : ℚ) := by
113+
unfold common
114+
rw [← sum_ind_eq_agree c w]
115+
refine Finset.sum_congr rfl (fun i _ => ?_)
116+
unfold ind; split <;> norm_num
117+
118+
/-- `common c c' w ≤ agree(c, c')`: coordinates where both agree with `w` are coordinates where
119+
`c` and `c'` agree with each other. -/
120+
theorem common_le_agree (c c' w : ι → Sigma) : common c c' w ≤ (agree c c' : ℚ) := by
121+
classical
122+
unfold common
123+
have hpt : ∀ i, ind c w i * ind c' w i ≤ ind c c' i := by
124+
intro i; rw [ind_mul_ind]; unfold ind
125+
split_ifs with h hcc <;> first | norm_num | (exfalso; apply hcc; rw [h.1, h.2])
126+
calc ∑ i, ind c w i * ind c' w i ≤ ∑ i, ind c c' i := Finset.sum_le_sum (fun i _ => hpt i)
127+
_ = (agree c c' : ℚ) := sum_ind_eq_agree c c'
128+
129+
/-- `∑ i, (colCount i)² = ∑ c, ∑ c', common(c, c')`: expansion over ordered pairs. -/
130+
theorem sum_colCount_sq (C : Finset (ι → Sigma)) (w : ι → Sigma) :
131+
∑ i, (colCount C w i) ^ 2 = ∑ c ∈ C, ∑ c' ∈ C, common c c' w := by
132+
unfold colCount common
133+
have step : ∀ i, (∑ c ∈ C, ind c w i) ^ 2 = ∑ c ∈ C, ∑ c' ∈ C, ind c w i * ind c' w i := by
134+
intro i; rw [sq, Finset.sum_mul_sum]
135+
simp_rw [step]
136+
conv_lhs => rw [Finset.sum_comm]
137+
refine Finset.sum_congr rfl fun c _ => ?_
138+
rw [Finset.sum_comm]
139+
140+
/-- Cauchy–Schwarz specialised: `(∑ i, colCount i)² ≤ n · ∑ i, (colCount i)²`. -/
141+
theorem sq_sum_colCount_le (C : Finset (ι → Sigma)) (w : ι → Sigma) :
142+
(∑ i, colCount C w i) ^ 2 ≤ (Fintype.card ι : ℚ) * ∑ i, (colCount C w i) ^ 2 := by
143+
have h := sq_sum_le_card_mul_sum_sq (s := (Finset.univ : Finset ι))
144+
(f := fun i => colCount C w i)
145+
simpa [Finset.card_univ] using h
146+
147+
/-- Each agreement count, in `ℚ`, is at most the block length. -/
148+
theorem agree_le_card (c w : ι → Sigma) : (agree c w : ℚ) ≤ (Fintype.card ι : ℚ) := by
149+
have : agree c w ≤ Fintype.card ι := by
150+
unfold agree; rw [← Finset.card_univ]; exact Finset.card_filter_le _ _
151+
exact_mod_cast this
152+
153+
/-- The **Johnson denominator** `(n - e)² - n·(n - d)`. The Johnson regime is where it is positive. -/
154+
noncomputable def johnsonDenom (n d e : ℕ) : ℚ := ((n : ℚ) - e) ^ 2 - (n : ℚ) * ((n : ℚ) - d)
155+
156+
/-! ### The Johnson bound -/
157+
158+
/-- **Johnson bound (product form).** For a finite set of codewords `C` with pairwise Hamming
159+
distance at least `d`, all within distance `e` of a word `w` (with `e, d ≤ n := |ι|`):
160+
161+
`(C.card) · ((n - e)² - n·(n - d)) ≤ n · d`. -/
162+
theorem card_mul_johnsonDenom_le
163+
(C : Finset (ι → Sigma)) (w : ι → Sigma) (d e : ℕ)
164+
(hd : ∀ c ∈ C, ∀ c' ∈ C, c ≠ c' → d ≤ hammingDist c c')
165+
(he : ∀ c ∈ C, hammingDist c w ≤ e)
166+
(hen : e ≤ Fintype.card ι) (hdn : d ≤ Fintype.card ι) :
167+
(C.card : ℚ) * johnsonDenom (Fintype.card ι) d e ≤ (Fintype.card ι : ℚ) * d := by
168+
classical
169+
set n := Fintype.card ι with hn
170+
set L : ℚ := (C.card : ℚ) with hL
171+
have hLnn : 0 ≤ L := by positivity
172+
set S1 : ℚ := ∑ c ∈ C, (agree c w : ℚ) with hS1def
173+
set S2 : ℚ := ∑ c ∈ C, ∑ c' ∈ C, common c c' w with hS2def
174+
have hne : (0 : ℚ) ≤ (n : ℚ) - e := by
175+
have h : (e : ℚ) ≤ (n : ℚ) := by exact_mod_cast hen
176+
linarith
177+
have hnd : (0 : ℚ) ≤ (n : ℚ) - d := by
178+
have h : (d : ℚ) ≤ (n : ℚ) := by exact_mod_cast hdn
179+
linarith
180+
-- (1) lower bound on S1
181+
have hS1lower : L * ((n : ℚ) - e) ≤ S1 := by
182+
have hcast : ∀ c ∈ C, ((n : ℚ) - e) ≤ (agree c w : ℚ) := by
183+
intro c hc
184+
have hge : (n - e : ℕ) ≤ agree c w := natSub_le_agree (he c hc)
185+
have : ((n - e : ℕ) : ℚ) ≤ (agree c w : ℚ) := by exact_mod_cast hge
186+
rwa [Nat.cast_sub hen] at this
187+
calc L * ((n : ℚ) - e) = ∑ _c ∈ C, ((n : ℚ) - e) := by
188+
rw [Finset.sum_const, nsmul_eq_mul]
189+
_ ≤ S1 := Finset.sum_le_sum hcast
190+
-- (2) upper bound on S1
191+
have hS1upper : S1 ≤ L * (n : ℚ) := by
192+
calc S1 ≤ ∑ _c ∈ C, (n : ℚ) := Finset.sum_le_sum (fun c _ => agree_le_card c w)
193+
_ = L * (n : ℚ) := by rw [Finset.sum_const, nsmul_eq_mul]
194+
-- (3) upper bound on S2
195+
have hS2upper : S2 ≤ S1 + L * (L - 1) * ((n : ℚ) - d) := by
196+
have hrow : ∀ c ∈ C,
197+
∑ c' ∈ C, common c c' w ≤ (agree c w : ℚ) + (L - 1) * ((n : ℚ) - d) := by
198+
intro c hc
199+
have hsplit : ∑ c' ∈ C, common c c' w
200+
= common c c w + ∑ c' ∈ C.erase c, common c c' w :=
201+
(Finset.add_sum_erase C (fun c' => common c c' w) hc).symm
202+
have hoff : ∑ c' ∈ C.erase c, common c c' w ≤ (L - 1) * ((n : ℚ) - d) := by
203+
have hbound : ∀ c' ∈ C.erase c, common c c' w ≤ (n : ℚ) - d := by
204+
intro c' hc'
205+
have hne' : c' ≠ c := Finset.ne_of_mem_erase hc'
206+
have hc'mem : c' ∈ C := Finset.mem_of_mem_erase hc'
207+
have hag : agree c c' ≤ (n - d : ℕ) :=
208+
agree_le_natSub (hd c hc c' hc'mem (Ne.symm hne'))
209+
have : (agree c c' : ℚ) ≤ ((n - d : ℕ) : ℚ) := by exact_mod_cast hag
210+
rw [Nat.cast_sub hdn] at this
211+
exact (common_le_agree c c' w).trans this
212+
calc ∑ c' ∈ C.erase c, common c c' w
213+
≤ ∑ _c' ∈ C.erase c, ((n : ℚ) - d) := Finset.sum_le_sum hbound
214+
_ = ((C.erase c).card : ℚ) * ((n : ℚ) - d) := by rw [Finset.sum_const, nsmul_eq_mul]
215+
_ = (L - 1) * ((n : ℚ) - d) := by
216+
rw [Finset.card_erase_of_mem hc,
217+
Nat.cast_sub (Finset.one_le_card.mpr ⟨c, hc⟩)]
218+
push_cast; ring
219+
rw [hsplit, common_self]
220+
linarith
221+
calc S2 ≤ ∑ c ∈ C, ((agree c w : ℚ) + (L - 1) * ((n : ℚ) - d)) := Finset.sum_le_sum hrow
222+
_ = S1 + L * (L - 1) * ((n : ℚ) - d) := by
223+
rw [Finset.sum_add_distrib, Finset.sum_const, nsmul_eq_mul, ← hS1def]; ring
224+
-- (4) Cauchy–Schwarz
225+
have hCS : S1 ^ 2 ≤ (n : ℚ) * S2 := by
226+
have h1 := sq_sum_colCount_le C w
227+
rw [sum_colCount C w, ← hS1def] at h1
228+
rw [sum_colCount_sq C w, ← hS2def] at h1
229+
exact h1
230+
-- (5) `(L (n-e))² ≤ S1²`
231+
have hsqle : (L * ((n : ℚ) - e)) ^ 2 ≤ S1 ^ 2 :=
232+
pow_le_pow_left₀ (mul_nonneg hLnn hne) hS1lower 2
233+
-- KEY inequality
234+
have hKEY : (L * ((n : ℚ) - e)) ^ 2 ≤ (n : ℚ) * (L * (n : ℚ) + L * (L - 1) * ((n : ℚ) - d)) := by
235+
have hnpos : (0 : ℚ) ≤ (n : ℚ) := by positivity
236+
calc (L * ((n : ℚ) - e)) ^ 2 ≤ S1 ^ 2 := hsqle
237+
_ ≤ (n : ℚ) * S2 := hCS
238+
_ ≤ (n : ℚ) * (S1 + L * (L - 1) * ((n : ℚ) - d)) := by
239+
apply mul_le_mul_of_nonneg_left _ hnpos; linarith [hS2upper]
240+
_ ≤ (n : ℚ) * (L * (n : ℚ) + L * (L - 1) * ((n : ℚ) - d)) := by
241+
apply mul_le_mul_of_nonneg_left _ hnpos; linarith [hS1upper]
242+
-- cancel a factor of `L`
243+
rcases eq_or_lt_of_le hLnn with hL0 | hLpos
244+
· rw [← hL0, zero_mul]; positivity
245+
· have hLLX : L * (L * johnsonDenom n d e) ≤ L * ((n : ℚ) * d) := by
246+
unfold johnsonDenom
247+
nlinarith [hKEY, hLpos, hne, hnd]
248+
exact le_of_mul_le_mul_left hLLX hLpos
249+
250+
/-- **Johnson bound (list-size form).** In the Johnson regime `(n - e)² > n·(n - d)`, the number of
251+
codewords of `C` within distance `e` of `w` is at most `n·d / ((n - e)² - n·(n - d))`. -/
252+
theorem card_le_div
253+
(C : Finset (ι → Sigma)) (w : ι → Sigma) (d e : ℕ)
254+
(hd : ∀ c ∈ C, ∀ c' ∈ C, c ≠ c' → d ≤ hammingDist c c')
255+
(he : ∀ c ∈ C, hammingDist c w ≤ e)
256+
(hen : e ≤ Fintype.card ι) (hdn : d ≤ Fintype.card ι)
257+
(hJohnson : 0 < johnsonDenom (Fintype.card ι) d e) :
258+
(C.card : ℚ) ≤ (Fintype.card ι : ℚ) * d / johnsonDenom (Fintype.card ι) d e := by
259+
rw [le_div_iff₀ hJohnson]
260+
exact card_mul_johnsonDenom_le C w d e hd he hen hdn
261+
262+
end ArkLib.JohnsonBound

0 commit comments

Comments
 (0)