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

Commit 0f9c153

Browse files
MultramateMultramatejcommelin
committed
feat(algebra/cubic_discriminant): basics of cubic polynomials and their discriminants (#11483)
Co-authored-by: Multramate <dka316@ic.ac.uk> Co-authored-by: Johan Commelin <johan@commelin.net>
1 parent 39b1262 commit 0f9c153

File tree

6 files changed

+433
-17
lines changed

6 files changed

+433
-17
lines changed
Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
/-
2+
Copyright (c) 2022 David Kurniadi Angdinata. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: David Kurniadi Angdinata
5+
-/
6+
7+
import field_theory.splitting_field
8+
9+
/-!
10+
# Cubics and discriminants
11+
12+
This file defines cubic polynomials over a semiring and their discriminants over a splitting field.
13+
14+
## Main definitions
15+
16+
* `cubic`: the structure representing a cubic polynomial.
17+
* `disc`: the discriminant of a cubic polynomial.
18+
19+
## Main statements
20+
21+
* `disc_ne_zero_iff_roots_nodup`: the cubic discriminant is not equal to zero if and only if
22+
the cubic has no duplicate roots.
23+
24+
## References
25+
26+
* https://en.wikipedia.org/wiki/Cubic_equation
27+
* https://en.wikipedia.org/wiki/Discriminant
28+
29+
## Tags
30+
31+
cubic, discriminant, polynomial, root
32+
-/
33+
34+
noncomputable theory
35+
36+
/-- The structure representing a cubic polynomial. -/
37+
@[ext] structure cubic (R : Type*) := (a b c d : R)
38+
39+
namespace cubic
40+
41+
open cubic polynomial
42+
43+
variables {R S F K : Type*}
44+
45+
instance [inhabited R] : inhabited (cubic R) := ⟨⟨default, default, default, default⟩⟩
46+
47+
instance [has_zero R] : has_zero (cubic R) := ⟨⟨0, 0, 0, 0⟩⟩
48+
49+
section basic
50+
51+
variables {P : cubic R} [semiring R]
52+
53+
/-- Convert a cubic polynomial to a polynomial. -/
54+
def to_poly (P : cubic R) : polynomial R := C P.a * X ^ 3 + C P.b * X ^ 2 + C P.c * X + C P.d
55+
56+
/-! ### Coefficients -/
57+
58+
section coeff
59+
60+
private lemma coeffs :
61+
(∀ n > 3, P.to_poly.coeff n = 0) ∧ P.to_poly.coeff 3 = P.a ∧ P.to_poly.coeff 2 = P.b
62+
∧ P.to_poly.coeff 1 = P.c ∧ P.to_poly.coeff 0 = P.d :=
63+
begin
64+
simp only [to_poly, coeff_add, coeff_C, coeff_C_mul_X, coeff_C_mul_X_pow],
65+
norm_num,
66+
intros n hn,
67+
repeat { rw [if_neg] },
68+
any_goals { linarith only [hn] },
69+
repeat { rw [zero_add] }
70+
end
71+
72+
@[simp] lemma coeff_gt_three (n : ℕ) (hn : 3 < n) : P.to_poly.coeff n = 0 := coeffs.1 n hn
73+
74+
@[simp] lemma coeff_three : P.to_poly.coeff 3 = P.a := coeffs.2.1
75+
76+
@[simp] lemma coeff_two : P.to_poly.coeff 2 = P.b := coeffs.2.2.1
77+
78+
@[simp] lemma coeff_one : P.to_poly.coeff 1 = P.c := coeffs.2.2.2.1
79+
80+
@[simp] lemma coeff_zero : P.to_poly.coeff 0 = P.d := coeffs.2.2.2.2
81+
82+
lemma a_of_eq {Q : cubic R} (h : P.to_poly = Q.to_poly) : P.a = Q.a :=
83+
by rw [← coeff_three, h, coeff_three]
84+
85+
lemma b_of_eq {Q : cubic R} (h : P.to_poly = Q.to_poly) : P.b = Q.b :=
86+
by rw [← coeff_two, h, coeff_two]
87+
88+
lemma c_of_eq {Q : cubic R} (h : P.to_poly = Q.to_poly) : P.c = Q.c :=
89+
by rw [← coeff_one, h, coeff_one]
90+
91+
lemma d_of_eq {Q : cubic R} (h : P.to_poly = Q.to_poly) : P.d = Q.d :=
92+
by rw [← coeff_zero, h, coeff_zero]
93+
94+
@[simp] lemma to_poly_injective (P Q : cubic R) : P.to_poly = Q.to_poly ↔ P = Q :=
95+
⟨λ h, cubic.ext _ _ (a_of_eq h) (b_of_eq h) (c_of_eq h) (d_of_eq h), congr_arg _⟩
96+
97+
@[simp] lemma of_a_eq_zero (ha : P.a = 0) : P.to_poly = C P.b * X ^ 2 + C P.c * X + C P.d :=
98+
by rw [to_poly, C_eq_zero.mpr ha, zero_mul, zero_add]
99+
100+
@[simp] lemma of_a_b_eq_zero (ha : P.a = 0) (hb : P.b = 0) : P.to_poly = C P.c * X + C P.d :=
101+
by rw [of_a_eq_zero ha, C_eq_zero.mpr hb, zero_mul, zero_add]
102+
103+
@[simp] lemma of_a_b_c_eq_zero (ha : P.a = 0) (hb : P.b = 0) (hc : P.c = 0) : P.to_poly = C P.d :=
104+
by rw [of_a_b_eq_zero ha hb, C_eq_zero.mpr hc, zero_mul, zero_add]
105+
106+
@[simp] lemma of_zero (ha : P.a = 0) (hb : P.b = 0) (hc : P.c = 0) (hd : P.d = 0) : P.to_poly = 0 :=
107+
by rw [of_a_b_c_eq_zero ha hb hc, C_eq_zero.mpr hd]
108+
109+
@[simp] lemma zero : (0 : cubic R).to_poly = 0 := of_zero rfl rfl rfl rfl
110+
111+
@[simp] lemma eq_zero_iff : P.to_poly = 0 ↔ P = 0 := by rw [← zero, to_poly_injective]
112+
113+
lemma ne_zero (h0 : ¬P.a = 0 ∨ ¬P.b = 0 ∨ ¬P.c = 0 ∨ ¬P.d = 0) : P.to_poly ≠ 0 :=
114+
by { contrapose! h0, rw [eq_zero_iff.mp h0], exact ⟨rfl, rfl, rfl, rfl⟩ }
115+
116+
lemma ne_zero_of_a_ne_zero (ha : P.a ≠ 0) : P.to_poly ≠ 0 := (or_imp_distrib.mp ne_zero).1 ha
117+
118+
lemma ne_zero_of_b_ne_zero (hb : P.b ≠ 0) : P.to_poly ≠ 0 :=
119+
(or_imp_distrib.mp (or_imp_distrib.mp ne_zero).2).1 hb
120+
121+
lemma ne_zero_of_c_ne_zero (hc : P.c ≠ 0) : P.to_poly ≠ 0 :=
122+
(or_imp_distrib.mp (or_imp_distrib.mp (or_imp_distrib.mp ne_zero).2).2).1 hc
123+
124+
lemma ne_zero_of_d_ne_zero (hd : P.d ≠ 0) : P.to_poly ≠ 0 :=
125+
(or_imp_distrib.mp (or_imp_distrib.mp (or_imp_distrib.mp ne_zero).2).2).2 hd
126+
127+
end coeff
128+
129+
/-! ### Degrees -/
130+
131+
section degree
132+
133+
/-- The equivalence between cubic polynomials and polynomials of degree at most three. -/
134+
@[simps] def equiv : cubic R ≃ {p : polynomial R // p.degree ≤ 3} :=
135+
{ to_fun := λ P, ⟨P.to_poly, degree_cubic_le⟩,
136+
inv_fun := λ f, ⟨coeff f 3, coeff f 2, coeff f 1, coeff f 0⟩,
137+
left_inv := λ P, by ext; simp only [subtype.coe_mk, coeffs],
138+
right_inv := λ f,
139+
begin
140+
ext (_ | _ | _ | _ | n); simp only [subtype.coe_mk, coeffs],
141+
have h3 : 3 < n + 4 := by linarith only,
142+
rw [coeff_gt_three _ h3,
143+
(degree_le_iff_coeff_zero (f : polynomial R) 3).mp f.2 _ $ with_bot.coe_lt_coe.mpr h3]
144+
end }
145+
146+
lemma degree (ha : P.a ≠ 0) : P.to_poly.degree = 3 := degree_cubic ha
147+
148+
lemma degree_of_a_eq_zero (ha : P.a = 0) (hb : P.b ≠ 0) : P.to_poly.degree = 2 :=
149+
by rw [of_a_eq_zero ha, degree_quadratic hb]
150+
151+
lemma degree_of_a_b_eq_zero (ha : P.a = 0) (hb : P.b = 0) (hc : P.c ≠ 0) : P.to_poly.degree = 1 :=
152+
by rw [of_a_b_eq_zero ha hb, degree_linear hc]
153+
154+
lemma degree_of_a_b_c_eq_zero (ha : P.a = 0) (hb : P.b = 0) (hc : P.c = 0) (hd : P.d ≠ 0) :
155+
P.to_poly.degree = 0 :=
156+
by rw [of_a_b_c_eq_zero ha hb hc, degree_C hd]
157+
158+
lemma degree_of_zero (ha : P.a = 0) (hb : P.b = 0) (hc : P.c = 0) (hd : P.d = 0) :
159+
P.to_poly.degree = ⊥ :=
160+
by rw [of_zero ha hb hc hd, degree_zero]
161+
162+
lemma leading_coeff (ha : P.a ≠ 0) : P.to_poly.leading_coeff = P.a := leading_coeff_cubic ha
163+
164+
lemma leading_coeff_of_a_eq_zero (ha : P.a = 0) (hb : P.b ≠ 0) : P.to_poly.leading_coeff = P.b :=
165+
by rw [of_a_eq_zero ha, leading_coeff_quadratic hb]
166+
167+
lemma leading_coeff_of_a_b_eq_zero (ha : P.a = 0) (hb : P.b = 0) (hc : P.c ≠ 0) :
168+
P.to_poly.leading_coeff = P.c :=
169+
by rw [of_a_b_eq_zero ha hb, leading_coeff_linear hc]
170+
171+
lemma leading_coeff_of_a_b_c_eq_zero (ha : P.a = 0) (hb : P.b = 0) (hc : P.c = 0) :
172+
P.to_poly.leading_coeff = P.d :=
173+
by rw [of_a_b_c_eq_zero ha hb hc, leading_coeff_C]
174+
175+
end degree
176+
177+
/-! ### Map across a homomorphism -/
178+
179+
section map
180+
181+
variables [semiring S] {φ : R →+* S}
182+
183+
/-- Map a cubic polynomial across a semiring homomorphism. -/
184+
def map (φ : R →+* S) (P : cubic R) : cubic S := ⟨φ P.a, φ P.b, φ P.c, φ P.d⟩
185+
186+
lemma map_to_poly : (map φ P).to_poly = polynomial.map φ P.to_poly :=
187+
by simp only [map, to_poly, map_C, map_X, polynomial.map_add, polynomial.map_mul,
188+
polynomial.map_pow]
189+
190+
end map
191+
192+
end basic
193+
194+
section roots
195+
196+
open multiset
197+
198+
/-! ### Roots over an extension -/
199+
200+
section extension
201+
202+
variables {P : cubic R} [comm_ring R] [comm_ring S] {φ : R →+* S}
203+
204+
/-- The roots of a cubic polynomial. -/
205+
def roots [is_domain R] (P : cubic R) : multiset R := P.to_poly.roots
206+
207+
lemma map_roots [is_domain S] : (map φ P).roots = (polynomial.map φ P.to_poly).roots :=
208+
by rw [roots, map_to_poly]
209+
210+
theorem mem_roots_iff [is_domain R] (h0 : P.to_poly ≠ 0) (x : R) :
211+
x ∈ P.roots ↔ P.a * x ^ 3 + P.b * x ^ 2 + P.c * x + P.d = 0 :=
212+
begin
213+
rw [roots, mem_roots h0, is_root, to_poly],
214+
simp only [eval_C, eval_X, eval_add, eval_mul, eval_pow]
215+
end
216+
217+
theorem card_roots_le [is_domain R] [decidable_eq R] : P.roots.to_finset.card ≤ 3 :=
218+
begin
219+
apply (to_finset_card_le P.to_poly.roots).trans,
220+
by_cases hP : P.to_poly = 0,
221+
{ exact (card_roots' P.to_poly).trans (by { rw [hP, nat_degree_zero], exact zero_le 3 }) },
222+
{ simpa only [← @with_bot.coe_le_coe _ _ _ 3] using (card_roots hP).trans degree_cubic_le }
223+
end
224+
225+
end extension
226+
227+
variables {P : cubic F} [field F] [field K] {φ : F →+* K} {x y z : K}
228+
229+
/-! ### Roots over a splitting field -/
230+
231+
section split
232+
233+
theorem splits_iff_card_roots (ha : P.a ≠ 0) : splits φ P.to_poly ↔ (map φ P).roots.card = 3 :=
234+
begin
235+
replace ha : (map φ P).a ≠ 0 := (ring_hom.map_ne_zero φ).mpr ha,
236+
nth_rewrite_lhs 0 [← ring_hom.id_comp φ],
237+
rw [roots, ← splits_map_iff, ← map_to_poly, splits_iff_card_roots,
238+
← ((degree_eq_iff_nat_degree_eq $ ne_zero_of_a_ne_zero ha).mp $ degree ha : _ = 3)]
239+
end
240+
241+
theorem splits_iff_roots_eq_three (ha : P.a ≠ 0) :
242+
splits φ P.to_poly ↔ ∃ x y z : K, (map φ P).roots = {x, y, z} :=
243+
by rw [splits_iff_card_roots ha, card_eq_three]
244+
245+
theorem eq_prod_three_roots (ha : P.a ≠ 0) (h3 : (map φ P).roots = {x, y, z}) :
246+
(map φ P).to_poly = C (φ P.a) * (X - C x) * (X - C y) * (X - C z) :=
247+
begin
248+
rw [map_to_poly, eq_prod_roots_of_splits $ (splits_iff_roots_eq_three ha).mpr $ exists.intro x $
249+
exists.intro y $ exists.intro z h3, leading_coeff ha, ← map_roots, h3],
250+
change C (φ P.a) * ((X - C x) ::ₘ (X - C y) ::ₘ {X - C z}).prod = _,
251+
rw [prod_cons, prod_cons, prod_singleton, mul_assoc, mul_assoc]
252+
end
253+
254+
theorem eq_sum_three_roots (ha : P.a ≠ 0) (h3 : (map φ P).roots = {x, y, z}) :
255+
map φ P = ⟨φ P.a, φ P.a * -(x + y + z), φ P.a * (x * y + x * z + y * z), φ P.a * -(x * y * z)⟩ :=
256+
begin
257+
apply_fun to_poly,
258+
any_goals { exact λ P Q, (to_poly_injective P Q).mp },
259+
rw [eq_prod_three_roots ha h3, to_poly],
260+
simp only [C_neg, C_add, C_mul],
261+
ring1
262+
end
263+
264+
theorem b_eq_three_roots (ha : P.a ≠ 0) (h3 : (map φ P).roots = {x, y, z}) :
265+
φ P.b = φ P.a * -(x + y + z) :=
266+
by injection eq_sum_three_roots ha h3
267+
268+
theorem c_eq_three_roots (ha : P.a ≠ 0) (h3 : (map φ P).roots = {x, y, z}) :
269+
φ P.c = φ P.a * (x * y + x * z + y * z) :=
270+
by injection eq_sum_three_roots ha h3
271+
272+
theorem d_eq_three_roots (ha : P.a ≠ 0) (h3 : (map φ P).roots = {x, y, z}) :
273+
φ P.d = φ P.a * -(x * y * z) :=
274+
by injection eq_sum_three_roots ha h3
275+
276+
end split
277+
278+
/-! ### Discriminant over a splitting field -/
279+
280+
section discriminant
281+
282+
/-- The discriminant of a cubic polynomial. -/
283+
def disc {R : Type*} [ring R] (P : cubic R) : R :=
284+
P.b ^ 2 * P.c ^ 2 - 4 * P.a * P.c ^ 3 - 4 * P.b ^ 3 * P.d - 27 * P.a ^ 2 * P.d ^ 2
285+
+ 18 * P.a * P.b * P.c * P.d
286+
287+
theorem disc_eq_prod_three_roots (ha : P.a ≠ 0) (h3 : (map φ P).roots = {x, y, z}) :
288+
φ P.disc = (φ P.a * φ P.a * (x - y) * (x - z) * (y - z)) ^ 2 :=
289+
begin
290+
simp only [disc, ring_hom.map_add, ring_hom.map_sub, ring_hom.map_mul, map_pow],
291+
simp only [ring_hom.map_one, map_bit0, map_bit1],
292+
rw [b_eq_three_roots ha h3, c_eq_three_roots ha h3, d_eq_three_roots ha h3],
293+
ring1
294+
end
295+
296+
theorem disc_ne_zero_iff_roots_ne (ha : P.a ≠ 0) (h3 : (map φ P).roots = {x, y, z}) :
297+
P.disc ≠ 0 ↔ x ≠ y ∧ x ≠ z ∧ y ≠ z :=
298+
begin
299+
rw [← ring_hom.map_ne_zero φ, disc_eq_prod_three_roots ha h3, pow_two],
300+
simp only [mul_ne_zero_iff, sub_ne_zero],
301+
rw [ring_hom.map_ne_zero],
302+
tautology
303+
end
304+
305+
theorem disc_ne_zero_iff_roots_nodup (ha : P.a ≠ 0) (h3 : (map φ P).roots = {x, y, z}) :
306+
P.disc ≠ 0 ↔ (map φ P).roots.nodup :=
307+
begin
308+
rw [disc_ne_zero_iff_roots_ne ha h3, h3],
309+
change _ ↔ (x ::ₘ y ::ₘ {z}).nodup,
310+
rw [nodup_cons, nodup_cons, mem_cons, mem_singleton, mem_singleton],
311+
simp only [nodup_singleton],
312+
tautology
313+
end
314+
315+
theorem card_roots_of_disc_ne_zero [decidable_eq K] (ha : P.a ≠ 0)
316+
(h3 : (map φ P).roots = {x, y, z}) (hd : P.disc ≠ 0) : (map φ P).roots.to_finset.card = 3 :=
317+
begin
318+
rw [to_finset_card_of_nodup $ (disc_ne_zero_iff_roots_nodup ha h3).mp hd,
319+
← splits_iff_card_roots ha, splits_iff_roots_eq_three ha],
320+
exact ⟨x, ⟨y, ⟨z, h3⟩⟩⟩
321+
end
322+
323+
end discriminant
324+
325+
end roots
326+
327+
end cubic

src/data/polynomial/coeff.lean

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,13 @@ by simp
100100
lemma coeff_X_mul_zero (p : polynomial R) : coeff (X * p) 0 = 0 :=
101101
by simp
102102

103-
lemma coeff_C_mul_X (x : R) (k n : ℕ) :
103+
lemma coeff_C_mul_X_pow (x : R) (k n : ℕ) :
104104
coeff (C x * X^k : polynomial R) n = if n = k then x else 0 :=
105105
by { rw [← monomial_eq_C_mul_X, coeff_monomial], congr' 1, simp [eq_comm] }
106106

107+
lemma coeff_C_mul_X (x : R) (n : ℕ) : coeff (C x * X : polynomial R) n = if n = 1 then x else 0 :=
108+
by rw [← pow_one X, coeff_C_mul_X_pow]
109+
107110
@[simp] lemma coeff_C_mul (p : polynomial R) : coeff (C a * p) n = a * coeff p n :=
108111
by { rcases p, simp only [C, monomial, monomial_fun, mul_to_finsupp, ring_hom.coe_mk,
109112
coeff, add_monoid_algebra.single_zero_mul_apply p a n] }
@@ -118,7 +121,7 @@ by { rcases p, simp only [C, monomial, monomial_fun, mul_to_finsupp, ring_hom.co
118121

119122
lemma coeff_X_pow (k n : ℕ) :
120123
coeff (X^k : polynomial R) n = if n = k then 1 else 0 :=
121-
by simp only [one_mul, ring_hom.map_one, ← coeff_C_mul_X]
124+
by simp only [one_mul, ring_hom.map_one, ← coeff_C_mul_X_pow]
122125

123126
@[simp]
124127
lemma coeff_X_pow_self (n : ℕ) :
@@ -228,7 +231,7 @@ lemma update_eq_add_sub_coeff {R : Type*} [ring R] (p : polynomial R) (n : ℕ)
228231
p.update n a = p + (polynomial.C (a - p.coeff n) * polynomial.X ^ n) :=
229232
begin
230233
ext,
231-
rw [coeff_update_apply, coeff_add, coeff_C_mul_X],
234+
rw [coeff_update_apply, coeff_add, coeff_C_mul_X_pow],
232235
split_ifs with h;
233236
simp [h]
234237
end

0 commit comments

Comments
 (0)