Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit 56a59d3

Browse files
committed
feat(data/polynomial/hasse_deriv): Hasse derivatives (#8998)
1 parent 42dda89 commit 56a59d3

File tree

2 files changed

+207
-0
lines changed

2 files changed

+207
-0
lines changed

src/data/polynomial/coeff.lean

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,18 @@ begin
4949
simp [hi]
5050
end
5151

52+
/-- `polynomial.sum` as a linear map. -/
53+
@[simps] def lsum {R A M : Type*} [semiring R] [semiring A] [add_comm_monoid M]
54+
[module R A] [module R M] (f : ℕ → A →ₗ[R] M) :
55+
polynomial A →ₗ[R] M :=
56+
{ to_fun := λ p, p.sum (λ n r, f n r),
57+
map_add' := λ p q, sum_add_index p q _ (λ n, (f n).map_zero) (λ n _ _, (f n).map_add _ _),
58+
map_smul' := λ c p,
59+
begin
60+
rw [sum_eq_of_subset _ (λ n r, f n r) (λ n, (f n).map_zero) _ (support_smul c p)],
61+
simp only [sum_def, finset.smul_sum, coeff_smul, linear_map.map_smul],
62+
end }
63+
5264
variable (R)
5365
/-- The nth coefficient, as a linear map. -/
5466
def lcoeff (n : ℕ) : polynomial R →ₗ[R] R :=
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/-
2+
Copyright (c) 2021 Johan Commelin. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Johan Commelin
5+
-/
6+
7+
import data.polynomial.derivative
8+
import data.nat.choose.vandermonde
9+
10+
/-!
11+
# Hasse derivative of polynomials
12+
13+
The `k`th Hasse derivative of a polynomial `∑ a_i X^i` is `∑ (i.choose k) a_i X^(i-k)`.
14+
It is a variant of the usual derivative, and satisfies `k! * (hasse_deriv k f) = derivative^[k] f`.
15+
The main benefit is that is gives an atomic way of talking about expressions such as
16+
`(derivative^[k] f).eval r / k!`, that occur in Taylor expansions, for example.
17+
18+
## Main declarations
19+
20+
In the following, we write `D k` for the `k`-th Hasse derivative `hasse_deriv k`.
21+
22+
* `polynomial.hasse_deriv`: the `k`-th Hasse derivative of a polynomial
23+
* `polynomial.hasse_deriv_zero`: the `0`th Hasse derivative is the identity
24+
* `polynomial.hasse_deriv_one`: the `1`st Hasse derivative is the usual derivative
25+
* `polynomial.factorial_smul_hasse_deriv`: the identity `k! • (D k f) = derivative^[k] f`
26+
* `polynomial.hasse_deriv_comp`: the identity `(D k).comp (D l) = (k+l).choose k • D (k+l)`
27+
* `polynomial.hasse_deriv_mul`:
28+
the "Leibniz rule" `D k (f * g) = ∑ ij in antidiagonal k, D ij.1 f * D ij.2 g`
29+
30+
TODO: Prove the identity principle: a polynomial is 0 iff all its Hasse derivatives are zero.
31+
32+
## Reference
33+
34+
https://math.fontein.de/2009/08/12/the-hasse-derivative/
35+
36+
-/
37+
38+
noncomputable theory
39+
40+
namespace polynomial
41+
42+
open_locale nat big_operators
43+
open function nat (hiding nsmul_eq_mul)
44+
45+
variables {R : Type*} [semiring R] (k : ℕ) (f : polynomial R)
46+
47+
/-- The `k`th Hasse derivative of a polynomial `∑ a_i X^i` is `∑ (i.choose k) a_i X^(i-k)`.
48+
It satisfies `k! * (hasse_deriv k f) = derivative^[k] f`. -/
49+
def hasse_deriv (k : ℕ) : polynomial R →ₗ[R] polynomial R :=
50+
lsum (λ i, (monomial (i-k)) ∘ₗ distrib_mul_action.to_linear_map R R (i.choose k))
51+
52+
lemma hasse_deriv_apply :
53+
hasse_deriv k f = f.sum (λ i r, monomial (i - k) (↑(i.choose k) * r)) :=
54+
by simpa only [← nsmul_eq_mul]
55+
56+
lemma hasse_deriv_coeff (n : ℕ) :
57+
(hasse_deriv k f).coeff n = (n + k).choose k * f.coeff (n + k) :=
58+
begin
59+
rw [hasse_deriv_apply, coeff_sum, sum_def, finset.sum_eq_single (n + k), coeff_monomial],
60+
{ simp only [if_true, nat.add_sub_cancel, eq_self_iff_true], },
61+
{ intros i hi hink,
62+
rw [coeff_monomial],
63+
by_cases hik : i < k,
64+
{ simp only [nat.choose_eq_zero_of_lt hik, if_t_t, nat.cast_zero, zero_mul], },
65+
{ push_neg at hik, rw if_neg, contrapose! hink, exact (nat.sub_eq_iff_eq_add hik).mp hink, } },
66+
{ intro h, simp only [not_mem_support_iff.mp h, monomial_zero_right, mul_zero, coeff_zero] }
67+
end
68+
69+
lemma hasse_deriv_zero' : hasse_deriv 0 f = f :=
70+
by simp only [hasse_deriv_apply, nat.sub_zero, nat.choose_zero_right,
71+
nat.cast_one, one_mul, sum_monomial_eq]
72+
73+
@[simp] lemma hasse_deriv_zero : @hasse_deriv R _ 0 = linear_map.id :=
74+
linear_map.ext $ hasse_deriv_zero'
75+
76+
lemma hasse_deriv_one' : hasse_deriv 1 f = derivative f :=
77+
by simp only [hasse_deriv_apply, derivative_apply, monomial_eq_C_mul_X, nat.choose_one_right,
78+
(nat.cast_commute _ _).eq]
79+
80+
@[simp] lemma hasse_deriv_one : @hasse_deriv R _ 1 = derivative :=
81+
linear_map.ext $ hasse_deriv_one'
82+
83+
@[simp] lemma hasse_deriv_monomial (n : ℕ) (r : R) :
84+
hasse_deriv k (monomial n r) = monomial (n - k) (↑(n.choose k) * r) :=
85+
begin
86+
ext i,
87+
simp only [hasse_deriv_coeff, coeff_monomial],
88+
by_cases hnik : n = i + k,
89+
{ rw [if_pos hnik, if_pos, ← hnik], apply nat.sub_eq_of_eq_add, rwa add_comm },
90+
{ rw [if_neg hnik, mul_zero],
91+
by_cases hkn : k ≤ n,
92+
{ rw [← nat.sub_eq_iff_eq_add hkn] at hnik, rw [if_neg hnik] },
93+
{ push_neg at hkn, rw [nat.choose_eq_zero_of_lt hkn, nat.cast_zero, zero_mul, if_t_t] } }
94+
end
95+
96+
lemma hasse_deriv_C (r : R) (hk : 0 < k) : hasse_deriv k (C r) = 0 :=
97+
by rw [← monomial_zero_left, hasse_deriv_monomial, nat.choose_eq_zero_of_lt hk,
98+
nat.cast_zero, zero_mul, monomial_zero_right]
99+
100+
lemma hasse_deriv_apply_one (hk : 0 < k) : hasse_deriv k (1 : polynomial R) = 0 :=
101+
by rw [← C_1, hasse_deriv_C k _ hk]
102+
103+
lemma hasse_deriv_X (hk : 1 < k) : hasse_deriv k (X : polynomial R) = 0 :=
104+
by rw [← monomial_one_one_eq_X, hasse_deriv_monomial, nat.choose_eq_zero_of_lt hk,
105+
nat.cast_zero, zero_mul, monomial_zero_right]
106+
107+
lemma factorial_smul_hasse_deriv :
108+
⇑(k! • @hasse_deriv R _ k) = ((@derivative R _)^[k]) :=
109+
begin
110+
induction k with k ih,
111+
{ rw [hasse_deriv_zero, factorial_zero, iterate_zero, one_smul, linear_map.id_coe], },
112+
ext f n : 2,
113+
rw [iterate_succ_apply', ← ih],
114+
simp only [linear_map.smul_apply, coeff_smul, linear_map.map_smul_of_tower, coeff_derivative,
115+
hasse_deriv_coeff, ← @choose_symm_add _ k],
116+
simp only [nsmul_eq_mul, factorial_succ, mul_assoc, succ_eq_add_one, ← add_assoc,
117+
add_right_comm n 1 k, ← cast_succ],
118+
rw ← (cast_commute (n+1) (f.coeff (n + k + 1))).eq,
119+
simp only [← mul_assoc], norm_cast, congr' 2,
120+
apply @cast_injective ℚ,
121+
have h1 : n + 1 ≤ n + k + 1 := succ_le_succ le_self_add,
122+
have h2 : k + 1 ≤ n + k + 1 := succ_le_succ le_add_self,
123+
have H : ∀ (n : ℕ), (n! : ℚ) ≠ 0, { exact_mod_cast factorial_ne_zero },
124+
-- why can't `field_simp` help me here?
125+
simp only [cast_mul, cast_choose ℚ, h1, h2, -one_div, -mul_eq_zero,
126+
succ_sub_succ_eq_sub, nat.add_sub_cancel, add_sub_cancel_left] with field_simps,
127+
rw [eq_div_iff_mul_eq (mul_ne_zero (H _) (H _)), eq_comm, div_mul_eq_mul_div,
128+
eq_div_iff_mul_eq (mul_ne_zero (H _) (H _))],
129+
norm_cast,
130+
simp only [factorial_succ, succ_eq_add_one], ring,
131+
end
132+
133+
lemma hasse_deriv_comp (k l : ℕ) :
134+
(@hasse_deriv R _ k).comp (hasse_deriv l) = (k+l).choose k • hasse_deriv (k+l) :=
135+
begin
136+
ext i : 2,
137+
simp only [linear_map.smul_apply, comp_app, linear_map.coe_comp, smul_monomial,
138+
hasse_deriv_apply, mul_one, monomial_eq_zero_iff, sum_monomial_index, mul_zero,
139+
nat.sub_sub, add_comm l k],
140+
rw_mod_cast nsmul_eq_mul,
141+
congr' 2,
142+
by_cases hikl : i < k + l,
143+
{ rw [choose_eq_zero_of_lt hikl, mul_zero],
144+
by_cases hil : i < l,
145+
{ rw [choose_eq_zero_of_lt hil, mul_zero] },
146+
{ push_neg at hil, rw [← nat.sub_lt_right_iff_lt_add hil] at hikl,
147+
rw [choose_eq_zero_of_lt hikl , zero_mul], }, },
148+
push_neg at hikl, apply @cast_injective ℚ,
149+
have h1 : l ≤ i := nat.le_of_add_le_right hikl,
150+
have h2 : k ≤ i - l := nat.le_sub_right_of_add_le hikl,
151+
have h3 : k ≤ k + l := le_self_add,
152+
have H : ∀ (n : ℕ), (n! : ℚ) ≠ 0, { exact_mod_cast factorial_ne_zero },
153+
-- why can't `field_simp` help me here?
154+
simp only [cast_mul, cast_choose ℚ, h1, h2, h3, hikl, -one_div, -mul_eq_zero,
155+
succ_sub_succ_eq_sub, nat.add_sub_cancel, add_sub_cancel_left] with field_simps,
156+
rw [eq_div_iff_mul_eq, eq_comm, div_mul_eq_mul_div, eq_div_iff_mul_eq, nat.sub_sub, add_comm l k],
157+
{ ring, },
158+
all_goals { apply_rules [mul_ne_zero, H] }
159+
end
160+
161+
section
162+
open add_monoid_hom finset.nat
163+
164+
lemma hasse_deriv_mul (f g : polynomial R) :
165+
hasse_deriv k (f * g) = ∑ ij in antidiagonal k, hasse_deriv ij.1 f * hasse_deriv ij.2 g :=
166+
begin
167+
let D := λ k, (@hasse_deriv R _ k).to_add_monoid_hom,
168+
let Φ := @add_monoid_hom.mul (polynomial R) _,
169+
show (comp_hom (D k)).comp Φ f g =
170+
∑ (ij : ℕ × ℕ) in antidiagonal k, ((comp_hom.comp ((comp_hom Φ) (D ij.1))).flip (D ij.2) f) g,
171+
simp only [← finset_sum_apply],
172+
congr' 2, clear f g,
173+
ext m r n s : 4,
174+
simp only [finset_sum_apply, coe_mul_left, coe_comp, flip_apply, comp_app,
175+
hasse_deriv_monomial, linear_map.to_add_monoid_hom_coe, comp_hom_apply_apply, coe_mul,
176+
monomial_mul_monomial],
177+
have aux : ∀ (x : ℕ × ℕ), x ∈ antidiagonal k →
178+
monomial (m - x.1 + (n - x.2)) (↑(m.choose x.1) * r * (↑(n.choose x.2) * s)) =
179+
monomial (m + n - k) (↑(m.choose x.1) * ↑(n.choose x.2) * (r * s)),
180+
{ intros x hx, rw [finset.nat.mem_antidiagonal] at hx, subst hx,
181+
by_cases hm : m < x.1,
182+
{ simp only [nat.choose_eq_zero_of_lt hm, nat.cast_zero, zero_mul, monomial_zero_right], },
183+
by_cases hn : n < x.2,
184+
{ simp only [nat.choose_eq_zero_of_lt hn, nat.cast_zero,
185+
zero_mul, mul_zero, monomial_zero_right], },
186+
push_neg at hm hn,
187+
rw [← nat.sub_add_comm hm, ← nat.add_sub_assoc hn, nat.sub_sub, add_comm x.2 x.1, mul_assoc,
188+
← mul_assoc r, ← (nat.cast_commute _ r).eq, mul_assoc, mul_assoc], },
189+
conv_rhs { apply_congr, skip, rw aux _ H, },
190+
rw_mod_cast [← linear_map.map_sum, ← finset.sum_mul, ← nat.add_choose_eq],
191+
end
192+
193+
end
194+
195+
end polynomial

0 commit comments

Comments
 (0)