Zulip Chat Archive
Stream: mathlib4
Topic: Roots of quadratic polynomials
Christopher Hoskin (Jun 13 2025 at 19:18):
In #25605 I lift the result for finding the solution to a quadratic equation to finding the roots of a quadratic polynomial.
I was hoping to show
variable [Field R]
variable {a b c : R}
theorem quadratic_roots_of_discrim_eq_sq [DecidableEq R] [NeZero (2 : R)] (ha : a ≠ 0) {s : R}
(h : discrim a b c = s * s) :
(a • X ^ 2 + b • X + C c).roots = {(-b + s) / (2 * a), (-b - s) / (2 * a)} := sorry
But the closest I could get was:
(a • X ^ 2 + b • X + C c).roots.toFinset = {(-b + s) / (2 * a), (-b - s) / (2 * a)}
Not being very familiar with multisets, I couldn't see what to do next.
Is providing this result for polynomials a useful thing to do in mathlib?
Christopher
Kenny Lau (Jun 13 2025 at 19:56):
@Christopher Hoskin you need to use docs#Polynomial.roots_mul after factorising the polynomial
Kenny Lau (Jun 13 2025 at 19:56):
let me know if you want more explicit instructions
Christopher Hoskin (Jun 13 2025 at 20:19):
So, something like:
theorem quadratic_roots_of_discrim_eq_sq [DecidableEq R] [NeZero (2 : R)] (ha : a ≠ 0) {s : R}
(h : discrim a b c = s * s) :
(a • X ^ 2 + b • X + C c).roots = {(-b + s) / (2 * a), (-b - s) / (2 * a)} := by
have e1 : a • X ^ 2 + b • X + C c =
a • (X - C ((-b + s) / (2 * a))) * (X - C ((-b - s) / (2 * a))) := by
sorry
rw [e1, Polynomial.roots_mul (by rw [← e1]; exact quadratic_ne_zero ha)]
simp_all only [ne_eq, Algebra.smul_mul_assoc, not_false_eq_true, roots_smul_nonzero,
roots_X_sub_C, Multiset.singleton_add, Multiset.insert_eq_cons]
?
Kenny Lau (Jun 13 2025 at 20:22):
@Christopher Hoskin precisely!
Christopher Hoskin (Jun 13 2025 at 21:30):
This is where I've got to:
section
variable {K : Type*} [Field K] [NeZero (2 : K)] {a b c : K}
theorem quadratic_factor (ha : a ≠ 0) {s : K} (h : discrim a b c = s * s) (x : K) :
a * (x * x) + b * x + c = a * (x - (-b + s) / (2 * a)) * (x - (-b - s) / (2 * a)) := by
ring_nf
rw [sq s, ← h, discrim]
ring_nf
have p2 : (2 : K) ^ 2 = (4 : K) := by norm_num
rw [add_comm _ (a * x ^ 2), mul_assoc, inv_mul_cancel₀ two_ne_zero, mul_one, mul_comm _ a⁻¹,
← mul_assoc, ← mul_assoc, inv_mul_cancel₀ ha, one_mul, ← p2, mul_assoc, ← mul_pow,
inv_mul_cancel₀ two_ne_zero, one_pow, mul_one, mul_comm _ c, mul_assoc, ← mul_pow,
mul_inv_cancel₀ ha, one_pow, mul_one]
end
section
variable [Field R]
variable {a b c : R}
theorem quadratic_roots_of_discrim_eq_sq [DecidableEq R] [NeZero (2 : R)] (ha : a ≠ 0) {s : R}
(h : discrim a b c = s * s) :
(a • X ^ 2 + b • X + C c).roots = {(-b + s) / (2 * a), (-b - s) / (2 * a)} := by
have e0 (x : R) : eval x (a • X ^ 2 + b • X + C c) =
eval x (a • (X - C ((-b + s) / (2 * a))) * (X - C ((-b - s) / (2 * a)))) := by
simp only [eval_add, eval_smul, eval_pow, eval_X, smul_eq_mul, eval_C, Algebra.smul_mul_assoc,
eval_mul, eval_sub]
rw [sq]
rw [quadratic_factor ha h]
rw [mul_assoc]
have e1 : a • X ^ 2 + b • X + C c =
a • (X - C ((-b + s) / (2 * a))) * (X - C ((-b - s) / (2 * a))) := by
convert e0
sorry
rw [e1, Polynomial.roots_mul (by rw [← e1]; exact quadratic_ne_zero ha)]
simp_all only [ne_eq, Algebra.smul_mul_assoc, not_false_eq_true, roots_smul_nonzero,
roots_X_sub_C, Multiset.singleton_add, Multiset.insert_eq_cons]
end
I seem to be missing an `ext` like result to remove the sorry.
Christopher
end
Kenny Lau (Jun 13 2025 at 21:45):
@Christopher Hoskin that ext result is injectivity of evaluation, which is not a trivial result and more importantly is not good algebraically (for example, it doesn't work for all rings, not even all fields)!
Kenny Lau (Jun 13 2025 at 21:45):
you should not rely on evaluation
Kenny Lau (Jun 13 2025 at 21:45):
you can directly prove that the two polynomials are equal
Christopher Hoskin (Jun 14 2025 at 18:13):
@Kenny Lau this is what I came up with: https://github.com/leanprover-community/mathlib4/pull/25605/files#diff-a9afed80ac129a470049fe0c31179050f0585e2c5c7228c49cee9331db88182aR146-R175
Kenny Lau (Jun 14 2025 at 19:36):
@Christopher Hoskin Well done! I've golfed it and added as suggestion.
Kenny Lau (Jun 14 2025 at 19:37):
(if you want some explanations, basically right now ring doesn't know about polynomials, so it can't "group X terms together etc.", so I've helped it do that manually first, and then the goals will discharge much easier
Kenny Lau (Jun 14 2025 at 19:38):
it's certainly going to get better in the future once we have more tactics to deal with polynomials
Christopher Hoskin (Jun 15 2025 at 12:20):
I've migrated the PR with the scripts/migrate_to_fork.py script, so it's now #25905.
Kenny Lau (Jun 15 2025 at 13:43):
@Christopher Hoskin I have golfed all your proofs and changed the names to conform to the current style
Kenny Lau (Jun 15 2025 at 21:11):
@Christopher Hoskin after rereading the whole file I found that there might be some refactoring that might be beneficial, namely roots_quadratic_eq_pair_iff_of_ne_zero seems useful, and the step roots_of_ne_zero_of_vieta inside it could be factored out, and the step inside about factorising the quadratic should also be factored out, and this would make a better flow of the theorems you proved as well, would you mind if I PR to your PR?
Kenny Lau (Jun 15 2025 at 21:13):
I'll duplicate this comment inside your PR with more instructions.
Kenny Lau (Jun 15 2025 at 21:26):
Honestly the entire first section might benefit from some refactoring. I don't have a concrete plan yet, but this idea is just sitting in front of my brain.
Kenny Lau (Jun 15 2025 at 21:28):
And I apologise in advance for bring more work to you :melt:
Last updated: Dec 20 2025 at 21:32 UTC