Skip to content

Commit 65f01ff

Browse files
Some folding niceties + small clean up of proofs (#664)
Co-authored-by: Aristotle (Harmonic) <aristotle-harmonic@harmonic.fun>
1 parent 58a5cf1 commit 65f01ff

3 files changed

Lines changed: 111 additions & 56 deletions

File tree

ArkLib/Data/CodingTheory/ProximityGap/Folding.lean

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,16 @@ theorem foldWord_codeword {d : ℕ}
356356
rw [eval_comm, interpolate_eq_folding_poly_eval hk (by simp)]
357357
aesop
358358

359+
theorem foldWord_evalOnPoints {α : F} {p : Polynomial F}
360+
(hk : k ≤ n) (hp_deg : p.degree < 2 ^ n) :
361+
foldWord domain (evalOnPoints domain p) k α =
362+
evalOnPoints (domain.subdomain k)
363+
(FoldingPolynomial.polyFold p (2 ^ k) α) := by
364+
let f := evalOnPoints (domain : Fin (2 ^ n) ↪ F) p
365+
have hcode : f ∈ code domain (2 ^ n) := by simp_all [evalOnPoints_mem_code_of_degree_lt, f]
366+
rw [show evalOnPoints _ _ = (⟨f, hcode⟩ : code _ _) by rfl, foldWord_codeword hk]
367+
simp_all [toPolynomial_evalWord_of_degree_lt, f]
368+
359369
/-- Perfect completeness of folding: if a word belongs to an RS-code
360370
then its `foldWord` belongs to a folded RS-code.
361371
-/

ArkLib/Data/CodingTheory/ReedSolomon.lean

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,21 @@ lemma toPolynomial_lt_min_deg_card (c : ReedSolomon.code domain deg) :
668668
exact lt_of_lt_of_le (Lagrange.degree_interpolate_lt _
669669
(by aesop (add safe cases Function.Embedding))) (by simp)
670670

671+
lemma toPolynomial_evalWord_of_degree_lt
672+
{p : F[X]} (hp_deg : p.degree < deg) (hdeg : deg ≤ Fintype.card ι)
673+
{hcode : evalOnPoints domain p ∈ ReedSolomon.code domain deg} :
674+
toPolynomial ⟨evalOnPoints domain p, hcode⟩ = p := by
675+
classical
676+
rcases domain with ⟨domain, hdomain_inj⟩
677+
apply Polynomial.eq_of_degrees_lt_of_eval_index_eq (v := domain) (s := univ)
678+
· simp_all
679+
· exact Lagrange.degree_interpolate_lt _ (by simp_all)
680+
· exact lt_of_lt_of_le hp_deg (by simp [hdeg])
681+
· aesop
682+
(add safe cases Function.Embedding)
683+
(erase simp Lagrange.interpolate_apply)
684+
(add simp [Lagrange.eval_interpolate_at_node, toPolynomial_def])
685+
671686
lemma toPolynomial_eval_at_domain
672687
{c : ReedSolomon.code domain deg} {i : ι} :
673688
(toPolynomial c).eval (domain i) = c.1 i := by

ArkLib/Data/Polynomial/SplitFold.lean

Lines changed: 86 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -43,46 +43,34 @@ coefficients are extracted from positions `j ≡ i (mod n)`, reindexed by `j / n
4343
Formally: `splitNth f n i = ∑_{j ≡ i (mod n)} aⱼ X^(j/n)`.
4444
-/
4545
def splitNth (f : 𝔽[X]) (n : ℕ) [inst : NeZero n] : Fin n → 𝔽[X] :=
46-
fun i =>
46+
fun i
4747
let sup :=
48-
Finset.filterMap (fun x => if x % n = i.1 then .some (x / n) else .none)
48+
Finset.filterMap (fun x if x % n = i.1 then .some (x / n) else .none)
4949
f.support
50-
(by
51-
intros a a' b
52-
simp only [Option.mem_def, Option.ite_none_right_eq_some, Option.some.injEq, and_imp]
53-
intros h g h' g'
54-
rw [Eq.symm (Nat.div_add_mod' a n), Eq.symm (Nat.div_add_mod' a' n)]
55-
rw [h, g, h', g'])
50+
(fun a a' b ↦ by
51+
have := Nat.div_add_mod' a n
52+
have := Nat.div_add_mod' a' n
53+
aesop)
5654
Polynomial.ofFinsupp
5755
5856
sup,
59-
fun e => f.coeff (e * n + i.1),
60-
by
61-
intros a
62-
dsimp [sup]
57+
fun e ↦ f.coeff (e * n + i.1),
58+
fun a ↦ by
6359
simp only [Finset.mem_filterMap, mem_support_iff, ne_eq, Option.ite_none_right_eq_some,
64-
Option.some.injEq]
65-
apply Iff.intro
60+
Option.some.injEq, sup]
61+
constructor
6662
· rintro ⟨a', g⟩
6763
have : a' = a * n + i.1 := by
68-
rw [Eq.symm (Nat.div_add_mod' a' n)]
69-
rw [g.2.1, g.2.2]
70-
rw [this.symm]
71-
exact g.1
64+
have := Nat.div_add_mod' a' n
65+
aesop
66+
aesop
7267
· intros h
7368
exists (a * n + i.1)
74-
apply And.intro h
75-
rw [Nat.mul_add_mod_self_right, Nat.mod_eq_of_lt i.2]
76-
apply And.intro rfl
7769
have {a b : ℕ} : (a * n + b) / n = a + (b / n) := by
78-
have := inst.out
79-
have ne_zero : 0 < n := by omega
80-
rw [Nat.add_div ne_zero, Nat.mul_mod_left, zero_add, Nat.mul_div_cancel a ne_zero]
81-
have : ¬ (n ≤ b % n) := by
82-
simp only [not_le]
83-
exact Nat.mod_lt b ne_zero
84-
simp [this]
85-
simp [this]
70+
have := Nat.zero_lt_of_ne_zero inst.out
71+
have := Nat.mod_lt b this
72+
aesop (add simp [Nat.add_div])
73+
aesop (add simp [Nat.mul_add_mod_self_right, Nat.mod_eq_of_lt])
8674
8775

8876
/- Proof of key identity `splitNth` has to satisfy. -/
@@ -234,23 +222,20 @@ lemma splitNth_def (n : ℕ) (f : 𝔽[X]) [inst : NeZero n] :
234222

235223
/- Lemma bounding degree of each `n`-split polynomial. -/
236224
omit [NoZeroDivisors 𝔽] in
237-
lemma splitNth_degree_le {n : ℕ} {f : 𝔽[X]} [inst : NeZero n] :
238-
∀ {i}, (splitNth f n i).natDegree ≤ f.natDegree / n := by
239-
intros i
240-
unfold splitNth Polynomial.natDegree Polynomial.degree
241-
simp only [support_ofFinsupp]
242-
rw [WithBot.unbotD_le_iff (by simp)]
243-
simp only [Finset.max_le_iff, Finset.mem_filterMap, mem_support_iff, ne_eq,
244-
Option.ite_none_right_eq_some, Option.some.injEq, WithBot.coe_le_coe, forall_exists_index,
245-
and_imp]
246-
intros _ _ h _ h'
247-
rw [←h']
248-
refine Nat.div_le_div ?_ (Nat.le_refl n) inst.out
249-
exact le_natDegree_of_ne_zero h
225+
lemma splitNth_degree_le {n : ℕ} {f : 𝔽[X]} [inst : NeZero n] {i : Fin n} :
226+
(splitNth f n i).natDegree ≤ f.natDegree / n := by
227+
have hn := inst.out
228+
rw [Polynomial.natDegree_le_iff_coeff_eq_zero]
229+
intro j hj
230+
have hjn : j * n - 1 < j * n := Nat.sub_one_lt (by aesop)
231+
rw [Nat.div_lt_iff_lt_mul (by omega),
232+
Nat.lt_iff_le_pred (by omega),
233+
Polynomial.natDegree_le_iff_coeff_eq_zero] at hj
234+
exact hj _ (by omega)
250235

251236
/-- `foldingPolynomial` in terms of `splitNth`
252237
when `q = X ^ n`. -/
253-
@[simp]
238+
@[simp low]
254239
lemma folding_polynomial_eq_sum_splitNth {𝔽 : Type} [Field 𝔽]
255240
{f : Polynomial 𝔽} {n : ℕ}
256241
[inst : NeZero n] :
@@ -297,20 +282,68 @@ lemma folding_polynomial_eq_sum_splitNth {𝔽 : Type} [Field 𝔽]
297282
omega
298283

299284
/-- `polyFold` in terms of `splitNth`. -/
300-
@[simp]
285+
@[simp low]
301286
lemma polyFold_eq_sum_of_splitNth {𝔽 : Type} [Field 𝔽]
302287
{f : 𝔽[X]} {n : ℕ} {r : 𝔽}
303288
[inst : NeZero n] :
304289
FoldingPolynomial.polyFold f n r =
305290
∑ i, C (r ^ i.val) * splitNth f n i := by
306-
simp only [FoldingPolynomial.polyFold, folding_polynomial_eq_sum_splitNth, map_pow]
307-
rw [Polynomial.eval_finsetSum]
308-
simp only [eval_mul, eval_C, eval_pow, eval_X]
309-
conv =>
310-
lhs
311-
rhs
312-
ext x
313-
rw [mul_comm]
291+
aesop
292+
(add simp [FoldingPolynomial.polyFold, Polynomial.eval_finsetSum])
293+
(add safe (by grind))
294+
295+
omit [NoZeroDivisors 𝔽] in
296+
/-- Coefficient formula for `splitNth`: the `e`-th coefficient of the `i`-th component
297+
is the coefficient of `f` at position `e * n + i`. -/
298+
@[simp]
299+
lemma splitNth_coeff {n : ℕ} [NeZero n] {g : 𝔽[X]} {i : Fin n} {e : ℕ} :
300+
(splitNth g n i).coeff e = g.coeff (e * n + i.1) := by simp [splitNth]
301+
302+
omit [NoZeroDivisors 𝔽] in
303+
/-- `splitNth` is the left inverse of the `n`-way recombination: splitting the polynomial
304+
`∑ j, X^j * (u j)(X^n)` recovers `u i` for each component `i`. -/
305+
@[simp]
306+
lemma splitNth_of_sum_comp {n : ℕ} [inst : NeZero n] (u : Fin n → 𝔽[X]) (i : Fin n) :
307+
splitNth (∑ j : Fin n, X ^ (j : ℕ) * (u j).comp (X ^ n)) n i = u i := by
308+
have hn : 0 < n := Nat.pos_of_ne_zero inst.out
309+
ext e
310+
rw [splitNth_coeff, finsetSum_coeff, Finset.sum_eq_single i]
311+
· aesop (add unsafe (by rw [←expand_eq_comp_X_pow]))
312+
· intro j _ hj
313+
rw [coeff_X_pow_mul']
314+
by_cases hle : (j : ℕ) ≤ e * n + i
315+
· rw [if_pos hle, ←expand_eq_comp_X_pow, coeff_expand hn, if_neg]
316+
intro hdvd
317+
have hmod := (Nat.modEq_iff_dvd' hle).mpr hdvd
318+
aesop
319+
(add safe cases Fin)
320+
(add simp [Nat.ModEq, Nat.mod_eq_of_lt])
321+
· simp_all
322+
· aesop
323+
324+
/-- `foldingPolynomial` of an `n`-way recombination `∑ i, X^i * (u i)(X^n)` is the
325+
bivariate polynomial `∑ i, X^i * C (u i)`, i.e. its `Y`-coefficients are exactly the
326+
components `u i`. -/
327+
@[simp high]
328+
theorem foldingPolynomial_sum {𝔽 : Type} [Field 𝔽]
329+
{n : ℕ} {u : Fin n → 𝔽[X]} [inst : NeZero n] :
330+
FoldingPolynomial.foldingPolynomial (X ^ n)
331+
(∑ i, Polynomial.X ^ i.val * (u i).comp (Polynomial.X ^ n)) =
332+
∑ i, Polynomial.X ^ i.val * C (u i) := by simp_all
333+
334+
/-- `polyFold` of an `n`-way recombination `∑ i, X^i * (u i)(X^n)` is the
335+
polynomial `∑ i, r^i * u i`. -/
336+
@[simp high]
337+
theorem polyFold_sum {𝔽 : Type} [Field 𝔽] {r : 𝔽}
338+
{n : ℕ} {u : Fin n → 𝔽[X]} [inst : NeZero n] :
339+
FoldingPolynomial.polyFold
340+
(∑ i, Polynomial.X ^ i.val * (u i).comp (Polynomial.X ^ n)) n r =
341+
∑ i, r ^ i.val • (u i) := by
342+
aesop
343+
(add simp [FoldingPolynomial.polyFold,
344+
Polynomial.eval_finsetSum,
345+
Polynomial.smul_eq_C_mul])
346+
(add safe (by grind))
314347

315348
omit [NoZeroDivisors 𝔽] in
316349
/--
@@ -322,9 +355,6 @@ lemma splitNth_eval_comp_pow {n : ℕ} [NeZero n] (f : 𝔽[X]) (x : 𝔽) (i :
322355
rw [eval₂_eq_sum]
323356
unfold Polynomial.eval
324357
rw [Polynomial.eval₂_sum, eval₂_eq_sum]
325-
congr
326-
ext e a
327-
rw [← eval]
328-
simp
358+
simp_all
329359

330360
end Polynomial

0 commit comments

Comments
 (0)