Skip to content

Commit 646fb08

Browse files
Clean-up 🧼 of the SplitFold.lean (#670)
Co-authored-by: Aristotle (Harmonic) <aristotle-harmonic@harmonic.fun>
1 parent e7e3825 commit 646fb08

2 files changed

Lines changed: 113 additions & 218 deletions

File tree

β€ŽArkLib/Data/Polynomial/SplitFold.leanβ€Ž

Lines changed: 112 additions & 217 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/-
22
Copyright (c) 2024-2025 ArkLib Contributors. All rights reserved.
33
Released under Apache 2.0 license as described in the file LICENSE.
4-
Authors: Julian Sutherland, Ilia Vlasov
4+
Authors: Julian Sutherland, Ilia Vlasov, Aristotle (Harmonic)
55
-/
66
import Mathlib.Algebra.Polynomial.BigOperators
77

@@ -33,7 +33,7 @@ open Polynomial
3333

3434
namespace Polynomial
3535

36-
variable {𝔽 : Type} [CommSemiring 𝔽] [NoZeroDivisors 𝔽]
36+
variable {𝔽 : Type} [CommSemiring 𝔽]
3737

3838
/--
3939
Splits a polynomial into `n` component polynomials based on coefficient indices modulo `n`.
@@ -42,186 +42,116 @@ For a polynomial `f = βˆ‘β±Ό aβ±Ό XΚ²` and index `i : Fin n`, returns the polyno
4242
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
-/
45-
def splitNth (f : 𝔽[X]) (n : β„•) [inst : NeZero n] : Fin n β†’ 𝔽[X] :=
46-
fun i ↦
47-
let sup :=
45+
def splitNth (f : 𝔽[X]) (n : β„•) (i : Fin n) : 𝔽[X] :=
46+
if hn : n = 0 then f else -- contradictory case
47+
Polynomial.ofFinsupp
48+
⟨
4849
Finset.filterMap (fun x ↦ if x % n = i.1 then .some (x / n) else .none)
4950
f.support
5051
(fun a a' b ↦ by
5152
have := Nat.div_add_mod' a n
5253
have := Nat.div_add_mod' a' n
53-
aesop)
54-
Polynomial.ofFinsupp
55-
⟨
56-
sup,
57-
fun e ↦ f.coeff (e * n + i.1),
58-
fun a ↦ by
59-
simp only [Finset.mem_filterMap, mem_support_iff, ne_eq, Option.ite_none_right_eq_some,
60-
Option.some.injEq, sup]
61-
constructor
62-
· rintro ⟨a', g⟩
63-
have : a' = a * n + i.1 := by
64-
have := Nat.div_add_mod' a' n
65-
aesop
54+
aesop),
55+
fun e ↦ f.coeff (e * n + i.1),
56+
fun a ↦ by
57+
simp only [Finset.mem_filterMap, mem_support_iff, ne_eq, Option.ite_none_right_eq_some,
58+
Option.some.injEq]
59+
constructor
60+
· rintro ⟨a', g⟩
61+
have : a' = a * n + i.1 := by
62+
have := Nat.div_add_mod' a' n
6663
aesop
67-
Β· intros h
68-
exists (a * n + i.1)
69-
have {a b : β„•} : (a * n + b) / n = a + (b / n) := by
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])
74-
⟩
64+
aesop
65+
Β· intros h
66+
exists (a * n + i.1)
67+
have {a b : β„•} : (a * n + b) / n = a + (b / n) := by
68+
have := Nat.zero_lt_of_ne_zero hn
69+
have := Nat.mod_lt b this
70+
aesop (add simp [Nat.add_div])
71+
aesop (add simp [Nat.mul_add_mod_self_right, Nat.mod_eq_of_lt])
72+
⟩
73+
74+
/-- Non-computable helper definition. -/
75+
private noncomputable def splitNthNoncomputable (f : 𝔽[X]) (n : β„•) (i : Fin n) : 𝔽[X] :=
76+
if n = 0 then f else
77+
βˆ‘ k ∈ f.support,
78+
if k % n = i.1 then Polynomial.C (f.coeff k) * Polynomial.X ^ (k / n) else 0
79+
80+
@[simp]
81+
lemma splitNthNoncomputable_of_nz {f : 𝔽[X]} {n : β„•} [inst : NeZero n] {i : Fin n} :
82+
splitNthNoncomputable f n i =
83+
βˆ‘ k ∈ f.support,
84+
if k % n = i.1 then Polynomial.C (f.coeff k) * Polynomial.X ^ (k / n) else 0 := by
85+
have := inst.out
86+
aesop (add simp splitNthNoncomputable)
87+
88+
/-- Coefficient formula for `splitNth`: the `e`-th coefficient of the `i`-th component
89+
is the coefficient of `f` at position `e * n + i`. -/
90+
@[simp]
91+
lemma splitNth_coeff {n : β„•} {f : 𝔽[X]} (i : Fin n) (m : β„•) :
92+
(splitNth f n i).coeff m = f.coeff (m * n + i.1) := by
93+
aesop
94+
(add unsafe [cases Fin])
95+
(add simp [splitNth, Polynomial.coeff_ofFinsupp])
96+
97+
@[simp]
98+
private lemma splitNthNoncomputable_coeff {n : β„•} {f : 𝔽[X]} (i : Fin n) (m : β„•) :
99+
(splitNthNoncomputable f n i).coeff m = f.coeff (m * n + i.1) := by
100+
by_cases! hn : n β‰  0
101+
Β· simp only [splitNthNoncomputable, hn, ↓reduceIte, finsetSum_coeff]
102+
have hi : i.1 < n := i.2
103+
have hdiv : (m * n + i.1) / n = m := by
104+
rw [mul_comm, Nat.mul_add_div (Nat.pos_of_ne_zero hn), Nat.div_eq_of_lt hi, Nat.add_zero]
105+
have hmod : (m * n + i.1) % n = i.1 := by
106+
rw [mul_comm, Nat.mul_add_mod, Nat.mod_eq_of_lt hi]
107+
have key : βˆ€ k ∈ f.support,
108+
(if k % n = i.1 then Polynomial.C (f.coeff k) * Polynomial.X ^ (k / n) else 0).coeff m
109+
= if k = m * n + i.1 then f.coeff k else 0 := by
110+
intro k hk
111+
have hdm : n * (k / n) + k % n = k := Nat.div_add_mod k n
112+
by_cases h : k % n = i.1
113+
Β· simp only [h, if_true]
114+
rw [Polynomial.coeff_C_mul, Polynomial.coeff_X_pow]
115+
by_cases hm : m = k / n <;> grind
116+
Β· aesop
117+
rw [Finset.sum_congr rfl key]
118+
by_cases hmem : m * n + i.1 ∈ f.support <;>
119+
aesop (add simp [Finset.sum_eq_single,
120+
Finset.sum_eq_zero,
121+
Polynomial.mem_support_iff])
122+
Β· aesop (add safe [cases Fin, (by omega)])
123+
124+
private lemma splitNthNoncomputable_eq_splitNth {n : β„•} {f : 𝔽[X]} :
125+
splitNth f n = splitNthNoncomputable f n := by aesop
75126

76127
/- Proof of key identity `splitNth` has to satisfy. -/
77-
omit [NoZeroDivisors 𝔽] in
78-
lemma splitNth_def (n : β„•) (f : 𝔽[X]) [inst : NeZero n] :
128+
lemma eq_sum_splitNth (n : β„•) [inst : NeZero n] (f : 𝔽[X]) :
79129
f =
80130
βˆ‘ i : Fin n,
81131
(Polynomial.X ^ i.1) *
82132
Polynomial.evalβ‚‚ Polynomial.C (Polynomial.X ^ n) (splitNth f n i) := by
83-
ext e
84-
rw [Polynomial.finsetSum_coeff]
85-
have hβ‚€ {b e : β„•} {f : 𝔽[X]} : (X ^ b * f).coeff e = if e < b then 0 else f.coeff (e - b) := by
86-
rw [Polynomial.coeff_X_pow_mul' f b e]
133+
rw [splitNthNoncomputable_eq_splitNth]
134+
have hn : 0 < n := Nat.pos_of_ne_zero inst.out
135+
conv_lhs => rw [Polynomial.as_sum_support_C_mul_X_pow f]
136+
simp only [splitNthNoncomputable_of_nz, evalβ‚‚_finsetSum, Finset.mul_sum]
137+
rw [Finset.sum_comm]
138+
apply Finset.sum_congr rfl
139+
intro k hk
140+
have hstep : βˆ€ i : Fin n, X ^ i.1 * evalβ‚‚ C (X ^ n)
141+
(if k % n = i.1 then C (f.coeff k) * X ^ (k / n) else 0)
142+
= if k % n = i.1 then C (f.coeff k) * X ^ k else (0 : 𝔽[X]) := fun i ↦ by
143+
have := Nat.div_add_mod k n
144+
have : X ^ i.1 * (C (f.coeff k) * X ^ (n * (k / n))) =
145+
C (f.coeff k) * X ^ (i.1 + n * (k / n)) := by ring
87146
aesop
88-
have h₁ {e : β„•} {f : 𝔽[X]} :
89-
(evalβ‚‚ C (X ^ n) f).coeff e =
90-
if e % n = 0
91-
then f.coeff (e / n)
92-
else 0 := by
93-
rw [Polynomial.evalβ‚‚_def, Polynomial.coeff_sum, Polynomial.sum_def]
94-
conv =>
95-
lhs
96-
congr
97-
Β· skip
98-
ext n
99-
rw [←pow_mul, Polynomial.coeff_C_mul_X_pow]
100-
by_cases h : e % n = 0 <;> simp only [h, ↓reduceIte]
101-
Β· rw [Finset.sum_eq_single (e / n)]
102-
Β· have : e = n * (e / n) :=
103-
Nat.eq_mul_of_div_eq_right
104-
(Nat.dvd_of_mod_eq_zero h) rfl
105-
rw [if_pos]
106-
exact this
107-
Β· intros b hβ‚€ h₁
108-
have : Β¬ (e = n * b) := by
109-
intros h'
110-
apply h₁
111-
rw [h']
112-
exact Nat.eq_div_of_mul_eq_right inst.out rfl
113-
simp [this]
114-
Β· intros h'
115-
split_ifs with h''
116-
Β· exact notMem_support_iff.mp h'
117-
Β· rfl
118-
Β· have {Ξ± : Type} {a b : Ξ±} : βˆ€ m, (if e = n * m then a else b) = b := by aesop
119-
conv =>
120-
lhs
121-
congr
122-
Β· skip
123-
ext m
124-
rw [this m]
125-
rw [Finset.sum_const_zero]
126-
conv =>
127-
rhs
128-
congr
129-
Β· skip
130-
Β· ext b
131-
rw [hβ‚€, h₁]
132-
unfold splitNth
133-
simp only [coeff_ofFinsupp, Finsupp.coe_mk]
134-
rw [Finset.sum_eq_single ⟨e % n, by refine Nat.mod_lt e (by have := inst.out; omega)⟩]
135-
Β· simp only
136-
have h₁ : Β¬ (e < e % n) := by
137-
by_cases h : e < n
138-
Β· rw [Nat.mod_eq_of_lt h]
139-
simp
140-
· simp only [not_lt] at h ⊒
141-
exact Nat.mod_le e n
142-
have hβ‚‚ : (e - e % n) % n = 0 := Nat.sub_mod_eq_zero_of_mod_eq (by simp)
143-
simp only [h₁, hβ‚‚, Eq.symm Nat.div_eq_sub_mod_div, Nat.div_add_mod' e n, ↓reduceIte]
144-
· rintro ⟨b, h⟩ _
145-
simp only [ne_eq, Fin.mk.injEq, ite_eq_left_iff, not_lt, ite_eq_right_iff]
146-
intros hβ‚€ h₁ hβ‚‚
147-
exfalso
148-
apply hβ‚€
149-
have : e % n = b % n := by
150-
have h₁' := h₁
151-
rw [←Nat.div_add_mod' e n, ←Nat.div_add_mod' b n] at h₁ hβ‚‚
152-
by_cases h' : e % n β‰₯ b % n
153-
Β· have : e / n * n + e % n - (b / n * n + b % n) =
154-
((e / n - b / n) * n) + (e % n - b % n) := by
155-
have : e / n * n + e % n - (b / n * n + b % n) =
156-
e / n * n + e % n - b / n * n - b % n := by
157-
omega
158-
rw [this]
159-
have : e / n * n + e % n - b / n * n = ((e / n) - (b / n)) * n + e % n := by
160-
have : e / n * n + e % n - b / n * n = (e / n * n - b / n * n) + e % n :=
161-
Nat.sub_add_comm (Nat.mul_le_mul (Nat.div_le_div_right h₁') (by rfl))
162-
rw [this, ←Nat.sub_mul]
163-
rw [this]
164-
exact Nat.add_sub_assoc h' ((e / n - b / n) * n)
165-
rw [
166-
this, Nat.mul_add_mod_self_right,
167-
Nat.mod_eq_of_lt (Nat.sub_lt_of_lt (Nat.mod_lt _ (by linarith)))
168-
] at hβ‚‚
169-
omega
170-
Β· simp only [ge_iff_le, not_le] at h'
171-
have : e / n * n + e % n - (b / n * n + b % n) =
172-
((e / n - b / n - 1) * n) + (n - (b % n - e % n)) := by
173-
have : e / n * n + e % n - (b / n * n + b % n) =
174-
e / n * n + e % n - b / n * n - b % n := by
175-
omega
176-
rw [this]
177-
have : e / n * n + e % n - b / n * n = ((e / n) - (b / n)) * n + e % n := by
178-
have : e / n * n + e % n - b / n * n = (e / n * n - b / n * n) + e % n :=
179-
Nat.sub_add_comm (Nat.mul_le_mul (Nat.div_le_div_right h₁') (by rfl))
180-
rw [this, ←Nat.sub_mul]
181-
rw [this]
182-
have : e / n - b / n = (e / n - b / n - 1) + 1 := by
183-
refine Eq.symm (Nat.sub_add_cancel ?_)
184-
rw [Nat.one_le_iff_ne_zero]
185-
intros h
186-
have h := Nat.le_of_sub_eq_zero h
187-
nlinarith
188-
rw (occs := .pos [1]) [this]
189-
rw
190-
[
191-
right_distrib, one_mul, add_assoc,
192-
Nat.add_sub_assoc (Nat.le_add_right_of_le (Nat.le_of_lt (Nat.mod_lt_of_lt h)))
193-
]
194-
congr 1
195-
grind
196-
rw [this, Nat.mul_add_mod_self_right] at hβ‚‚
197-
have {a : β„•} : (n - a) % n = 0 ∧ a < n β†’ a = 0 := by
198-
intros h
199-
rcases exists_eq_mul_left_of_dvd (Nat.dvd_of_mod_eq_zero h.1) with ⟨c, h'⟩
200-
have : a = (1 - c)*n := by
201-
have : n = a + c * n := by omega
202-
have : n - c * n = a := by omega
203-
rw [←this]
204-
have : n = 1 * n := by rw [one_mul]
205-
rewrite (occs := .pos [1]) [this]
206-
exact Eq.symm (Nat.sub_mul 1 c n)
207-
have h' := this β–Έ h.2
208-
rw [this]
209-
have : 1 - c = 0 := by
210-
have : n = 1 * n := by rw [one_mul]
211-
rw (occs := .pos [2]) [this] at h'
212-
have h' := Nat.lt_of_mul_lt_mul_right h'
213-
omega
214-
simp [this]
215-
exfalso
216-
have hβ‚‚ := this ⟨hβ‚‚, by apply Nat.sub_lt_of_lt; apply Nat.mod_lt; linarith⟩
217-
omega
218-
rw [this]
219-
exact Eq.symm (Nat.mod_eq_of_lt h)
220-
Β· intros h
221-
simp at h
147+
(add simp [evalβ‚‚_mul, evalβ‚‚_C, evalβ‚‚_X_pow])
148+
(add unsafe (by rw [← pow_mul]))
149+
(add safe (by omega))
150+
rw [Finset.sum_congr rfl (fun i _ => hstep i),
151+
Finset.sum_eq_single (⟨k % n, Nat.mod_lt k hn⟩ : Fin n)] <;> aesop
222152

223153
/- Lemma bounding degree of each `n`-split polynomial. -/
224-
omit [NoZeroDivisors 𝔽] in
154+
@[simp]
225155
lemma splitNth_degree_le {n : β„•} {f : 𝔽[X]} [inst : NeZero n] {i : Fin n} :
226156
(splitNth f n i).natDegree ≀ f.natDegree / n := by
227157
have hn := inst.out
@@ -231,75 +161,41 @@ lemma splitNth_degree_le {n : β„•} {f : 𝔽[X]} [inst : NeZero n] {i : Fin n} :
231161
rw [Nat.div_lt_iff_lt_mul (by omega),
232162
Nat.lt_iff_le_pred (by omega),
233163
Polynomial.natDegree_le_iff_coeff_eq_zero] at hj
164+
simp only [splitNth_coeff]
234165
exact hj _ (by omega)
235166

236167
/-- `foldingPolynomial` in terms of `splitNth`
237168
when `q = X ^ n`. -/
238169
@[simp low]
239-
lemma folding_polynomial_eq_sum_splitNth {𝔽 : Type} [Field 𝔽]
240-
{f : Polynomial 𝔽} {n : β„•}
241-
[inst : NeZero n] :
170+
lemma folding_polynomial_eq_sum_splitNth {𝔽 : Type} [Field 𝔽] {f : Polynomial 𝔽}
171+
{n : β„•} [inst : NeZero n] :
242172
FoldingPolynomial.foldingPolynomial (X ^ n) f =
243173
βˆ‘ i, C (splitNth f n i) * (X ^ i.val) := by
244174
symm
245175
apply FoldingPolynomial.folding_polynomial_is_unique'
246-
Β· conv =>
247-
rhs
248-
rw [splitNth_def (f := f) (inst := inst)]
249-
rw [
250-
Polynomial.map_sum,
251-
Polynomial.eval_finsetSum]
252-
simp only [Polynomial.map_mul, map_C, coe_compRingHom, Polynomial.map_pow, map_X,
253-
eval_mul, eval_C, eval_pow, eval_X]
254-
simp only [comp]
255-
conv =>
256-
lhs
257-
rhs
258-
ext x
259-
rw [mul_comm]
260-
rfl
261-
Β· simp only [Bivariate.degreeX, finsetSum_coeff, coeff_C_mul, coeff_X_pow, mul_ite, mul_one,
262-
mul_zero, natDegree_pow, natDegree_X]
263-
simp only [Finset.sup_le_iff, mem_support_iff, finsetSum_coeff, coeff_C_mul, coeff_X_pow,
264-
mul_ite, mul_one, mul_zero, ne_eq]
265-
intro b hb
266-
apply natDegree_sum_le_of_forall_le
267-
rintro ⟨i, hi⟩ _
268-
by_cases heq: b = i
269-
Β· simp only [heq, ↓reduceIte]
270-
exact splitNth_degree_le
271-
Β· simp [heq]
176+
Β· conv_rhs => rw [eq_sum_splitNth (f := f) (inst := inst)]
177+
rw [Polynomial.map_sum, Polynomial.eval_finsetSum]
178+
aesop
179+
(add simp [comp])
180+
(add safe (by ac_nf))
181+
Β· aesop
182+
(add simp [Bivariate.degreeX])
183+
(add safe natDegree_sum_le_of_forall_le)
272184
Β· simp only [Bivariate.natDegreeY, natDegree_pow, natDegree_X, mul_one]
273-
apply Nat.lt_of_le_pred (by {
274-
apply Nat.zero_lt_of_ne_zero
275-
aesop
276-
})
277-
apply Polynomial.natDegree_sum_le_of_forall_le
278-
intro i _
279-
apply Nat.le_trans Polynomial.natDegree_mul_le
280-
rcases i with ⟨i, hi⟩
281-
simp
282-
omega
185+
exact Nat.lt_of_le_pred (by aesop (add unsafe Nat.zero_lt_of_ne_zero)) <| by
186+
exact Polynomial.natDegree_sum_le_of_forall_le _ _ <| fun i _ ↦
187+
Nat.le_trans Polynomial.natDegree_mul_le <| by aesop (add safe (by omega))
283188

284189
/-- `polyFold` in terms of `splitNth`. -/
285190
@[simp low]
286191
lemma polyFold_eq_sum_of_splitNth {𝔽 : Type} [Field 𝔽]
287-
{f : 𝔽[X]} {n : β„•} {r : 𝔽}
288-
[inst : NeZero n] :
192+
{f : 𝔽[X]} {n : β„•} {r : 𝔽} [inst : NeZero n] :
289193
FoldingPolynomial.polyFold f n r =
290194
βˆ‘ i, C (r ^ i.val) * splitNth f n i := by
291195
aesop
292196
(add simp [FoldingPolynomial.polyFold, Polynomial.eval_finsetSum])
293197
(add safe (by grind))
294198

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
303199
/-- `splitNth` is the left inverse of the `n`-way recombination: splitting the polynomial
304200
`βˆ‘ j, X^j * (u j)(X^n)` recovers `u i` for each component `i`. -/
305201
@[simp]
@@ -345,7 +241,6 @@ theorem polyFold_sum {𝔽 : Type} [Field 𝔽] {r : 𝔽}
345241
Polynomial.smul_eq_C_mul])
346242
(add safe (by grind))
347243

348-
omit [NoZeroDivisors 𝔽] in
349244
/--
350245
Lemma bridges the coefficient-level identity `splitNth_def` and
351246
evaluation-level reasoning about `splitNth` and `foldNth`.

0 commit comments

Comments
Β (0)