|
| 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