Zulip Chat Archive

Stream: new members

Topic: Playing with polynomials


Lucas Allen (Sep 20 2020 at 06:26):

Hello, in an effort to play with polynomials in Lean I came up with the following,

import data.polynomial

open polynomial

noncomputable def P : polynomial  := 3 * X^2 + 1
noncomputable def Q : polynomial  := 4 * X^2 + 7 * X + 6

#check P + Q
#check P - Q
#check P * Q
#check P / Q

#eval P + Q
#eval P - Q
#eval P * Q
#eval P / Q

The #eval lines give the error "code generation failed, VM does not have code for 'classical.choice'", and the definitions of P and Q demand the noncomputable. Is there some way around the noncomputability of polynomials, so that #eval P + Q gives 7 * X^2 + 7 * X + 7?

Kenny Lau (Sep 20 2020 at 06:43):

"If you want to calculate, use Python"

Kenny Lau (Sep 20 2020 at 06:44):

(or use an older version of Lean/mathlib before we decided to make everything noncomputable)

Kenny Lau (Sep 20 2020 at 06:44):

probably some version in 2018

Lucas Allen (Sep 20 2020 at 06:57):

Ok.

Reid Barton (Sep 20 2020 at 10:51):

Or use a future version of Lean/mathlib with the norm_poly tactic

Kevin Buzzard (Sep 20 2020 at 11:44):

@Lucas Allen can you please leave 2020, you've come to the wrong year. Please recalibrate a couple of years in either direction.

Mario Carneiro (Sep 20 2020 at 11:52):

I need a telephone booth emoji

Kevin Buzzard (Sep 20 2020 at 12:06):

Polynomials used to be computable but this caused trouble with reasoning. Now they're noncomputable but apparently this causes problems with computation. Is Reid right that this can be fixed with a tactic?

Mario Carneiro (Sep 20 2020 at 12:08):

yes

Mario Carneiro (Sep 20 2020 at 12:08):

just like norm_num but for polynomials

Reid Barton (Sep 20 2020 at 12:09):

The trick is the kind of "computation" you want is not the same as the noncomputable kind. For example, norm_num works on the reals too

Kenny Lau (Sep 20 2020 at 12:58):

that there are two meanings of "computation": the Lean meaning, which is that you write a program that does it; the mathematical meaning, which is that you reduce something to a normal form.

Kevin Buzzard (Sep 20 2020 at 14:38):

There's some trickery involved though, isn't there? Because norm_num doesn't _really_ work with reals, right? It can only make progress if all the reals are rational.

Reid Barton (Sep 20 2020 at 14:48):

It really works with exprs that represent reals

Kevin Buzzard (Sep 20 2020 at 14:48):

Aah!

Kevin Buzzard (Sep 20 2020 at 14:49):

So why don't we teach it that sqrt(x)*sqrt(x)=x :-)

Reid Barton (Sep 20 2020 at 14:51):

Like this?

import data.real.basic
import tactic.norm_num

example : real.sqrt 5 * real.sqrt 5 = 5 := by norm_num

Kevin Buzzard (Sep 20 2020 at 14:54):

Thanks!

Kevin Buzzard (Sep 20 2020 at 14:55):

(I never knew it did that -- I had the wrong mental model)

Reid Barton (Sep 20 2020 at 14:57):

I think the main obstruction to turning norm_num into something more like a computer algebra system is that it's not obvious (at least to me) what kind of organizational framework is appropriate

Reid Barton (Sep 20 2020 at 14:57):

along with perhaps a lack of specific applications at the moment

Kevin Buzzard (Sep 20 2020 at 14:58):

I want to prove that x24x^2-4 is not irreduible without doing any work.

Reid Barton (Sep 20 2020 at 14:58):

but for any computation you can do in a CAS, provided you can give a proof that the computation is correct (when it succeeds), there's no reason that something like norm_num couldn't be extended to do it

Kevin Buzzard (Sep 20 2020 at 14:59):

Actually I don't really want to prove this at all, I just want to prove stuff about DVRs etc. I don't have a specific application.

Reid Barton (Sep 20 2020 at 14:59):

This example is pretty trivial because ring can already do the correctness proof

Kevin Buzzard (Sep 20 2020 at 14:59):

I guess sometimes I want to prove that y^2=x^3-4x has rank 0 (or 1 or whatever).

Reid Barton (Sep 20 2020 at 14:59):

so you can "just" do the factorization in an untrusted program

Kevin Buzzard (Sep 20 2020 at 15:00):

and Cremona's software is not spitting out a formally verifiable thing here

Reid Barton (Sep 20 2020 at 15:00):

Right, I think you skipped over a large range of difficulties there :upside_down:

Kevin Buzzard (Sep 20 2020 at 15:00):

Nobody can prove the algorithm terminates, for starters

Reid Barton (Sep 20 2020 at 15:01):

Well that's not actually a problem

Kevin Buzzard (Sep 20 2020 at 15:03):

Try telling that to the equation compiler

Kevin Buzzard (Sep 20 2020 at 15:03):

Of course I see what you mean, tactics are meta

Reid Barton (Sep 20 2020 at 15:06):

For a procedure like this, there are two relevant outcomes:

  • the procedure succeeds in producing an answer along with a correct proof
  • all other behavior: nontermination, answer but no certificate, certificate but it doesn't type check somehow

Reid Barton (Sep 20 2020 at 15:08):

If you can build a procedure which provably always has the first behavior then great, but in practice it's fine to have tactics that can fail, as long as they work when you need them to

Reid Barton (Sep 20 2020 at 15:09):

since our goal is to compute the ranks of elliptic curves (or whatever), not prove an internal theorem "there exists an algorithm which computes the rank of every elliptic curve"

Kevin Buzzard (Sep 20 2020 at 15:10):

which is good because that's an open problem

Mario Carneiro (Sep 20 2020 at 20:24):

FYI, this was a surprise to me as well. norm_num doesn't know anything about square roots, that's all simp's work

Reid Barton (Sep 20 2020 at 20:27):

aha!

Bryan Gin-ge Chen (Sep 20 2020 at 20:42):

Returning to the initial question, right now #simp [P,Q] P + Q just returns 3 * X ^ 2 + 1 + (4 * X ^ 2 + 7 * X + 6), but maybe it's possible to add some simp lemmas (or make a simp set?) to make it more "useful".

Lucas Allen (Sep 21 2020 at 04:40):

Kevin Buzzard said:

Lucas Allen can you please leave 2020, you've come to the wrong year. Please recalibrate a couple of years in either direction.

I think practicing the art of impeccable timing is starting to pay off.

Lucas Allen (Sep 21 2020 at 04:42):

Bryan Gin-ge Chen said:

Returning to the initial question, right now #simp [P,Q] P + Q just returns 3 * X ^ 2 + 1 + (4 * X ^ 2 + 7 * X + 6), but maybe it's possible to add some simp lemmas (or make a simp set?) to make it more "useful".

I had a go at adding a simp lemma to see if it worked,

@[simp] lemma X_add : (X : polynomial ) + X = 2 * X := sorry

#simp [X] X + X

It gives "monomial 1 1 + monomial 1 1", but I want "2 * X". I also tried #simp [(X : polynomial ℤ)] X + X but this gives "simplify tactic failed to simplify." Is there some way to get this to work?

Johan Commelin (Sep 21 2020 at 04:43):

Ooh, you don't want to put X in the simpset, rather put X_add between the []

Lucas Allen (Sep 21 2020 at 04:47):

#simp [X_add] X + X gives "simplify tactic failed to simplify". Do I need to say something about it being a polynomial?

Johan Commelin (Sep 21 2020 at 04:48):

Aah, I guess you do

Johan Commelin (Sep 21 2020 at 04:48):

@[simp] lemma X_add : (X : polynomial ) + X = 2 * X := sorry

#simp [X_add] (X : polynomial ) + X

does this work?

Lucas Allen (Sep 21 2020 at 04:49):

Yes!

Lucas Allen (Sep 21 2020 at 04:58):

Why is this an invalid simp lemma?

lemma X_add (m : ) (a b : ) : (a * X : polynomial ) + (b * X) = (a + b) * X :=
begin
  exact (add_mul a b X).symm,
end

Lucas Allen (Sep 21 2020 at 04:59):

Adding @[simp] gives a red squiggle.

Johan Commelin (Sep 21 2020 at 05:29):

@Lucas Allen because the statement doesn't use m

Lucas Allen (Sep 21 2020 at 05:33):

Oh no.

Lucas Allen (Sep 21 2020 at 05:33):

sorry.

Lucas Allen (Sep 21 2020 at 05:39):

I get a deterministic timeout with the following

@[simp] lemma X_add (a b : ) : (a * X : polynomial ) + (b * X) = (a + b) * X :=
begin
  exact (add_mul a b X).symm,
end

lemma test : (3 * X : polynomial ) + (5 * X) = 8 * X :=
begin
  apply X_add,
end

I don't know why, and I hope it's not obvious again...

Johan Commelin (Sep 21 2020 at 05:42):

What happens if you rw instead of apply?

Johan Commelin (Sep 21 2020 at 05:43):

I would have expected the apply to work though.

Lucas Allen (Sep 21 2020 at 05:52):

"rewrite tactic failed, did not find instance of the pattern in the target expression
↑?m_1 * X + ↑?m_2 * X
state:
⊢ 3 * X + 5 * X = 8 * X"

no deterministic timeout though.

Lucas Allen (Sep 21 2020 at 05:54):

I'm wondering if it has something to do with the fact that a * X has type polynomial , and it's not (a : ℤ) * X.

Lucas Allen (Sep 21 2020 at 05:54):

But (a : ℤ) * X doesn't work.

Lucas Allen (Sep 21 2020 at 05:57):

lemma X_add (a b : ℤ) : a * (X : polynomial ℤ) + b * X = (a + b) * X :=gives red squiggles under the *'s. It likes lemma test : 3 * (X : polynomial ℤ) + 5 * (X) = 8 * X := however.

Johan Commelin (Sep 21 2020 at 05:57):

Try (3 : \Z) instead

Johan Commelin (Sep 21 2020 at 05:57):

but rather, I think you should use C to create constant polynomials

Johan Commelin (Sep 21 2020 at 05:58):

Arguably, we should consider making a \bu X the standard way of multiplying by scalars, instead of C a * X.

Lucas Allen (Sep 21 2020 at 05:58):

lemma xxx : (3 : ℤ) * (X : polynomial ℤ) + (5 : ℤ) * (X) = (8 : ℤ) * X := gives red squiggles under the *'s type mismatch error.

Johan Commelin (Sep 21 2020 at 05:58):

((3 : \Z) * X : ...) ?

Lucas Allen (Sep 21 2020 at 06:00):

I think it was (3 : \Z) * (X : ...)

Lucas Allen (Sep 21 2020 at 06:01):

It likes the \bu. Everything works now.

@[simp] lemma X_add (a b : ) : a  (X : polynomial ) + b  X = (a + b)  X :=
begin
  exact (add_smul a b X).symm
end

lemma xxx : (3 : )  (X : polynomial ) + (5 : )  (X) = (8 : )  X :=
begin
  apply X_add,
end

#simp [X_add] (3 : )  (X : polynomial ) + (5 : )  (X) --(3 + 5) • X

Stanislas Polu (Jun 23 2021 at 14:38):

This is the best I could come up with but I have a feeling it's not idiomatic (or correct?). Is there a way to eval directly into polynomial R ?

theorem aopsbook_v2_c6_em2
  (f : polynomial )
  (a : ) :
  polynomial.mod f (polynomial.X - polynomial.C a) = polynomial.C (polynomial.eval a f) :=
begin
  sorry
end

Johan Commelin (Jun 23 2021 at 14:39):

There is eval\_2, I think. It takes a ring hom as argument

Johan Commelin (Jun 23 2021 at 14:40):

Maybe

open polynomial

... = aeval f (C a)

Stanislas Polu (Jun 23 2021 at 14:40):

Well from the docs#polynomial.eval:

polynomial.eval = polynomial.eval₂ (ring_hom.id R)

so it should land in R as well?

Johan Commelin (Jun 23 2021 at 14:41):

No, if you pass it something different from ring_hom.id R... then you can land in another ring

Stanislas Polu (Jun 23 2021 at 14:42):

Hmmm aeval f (C a) typechecks.

Stanislas Polu (Jun 23 2021 at 14:42):

theorem aopsbook_v2_c6_em2
  (f : polynomial )
  (a : ) :
  polynomial.mod f (polynomial.X - polynomial.C a) = polynomial.aeval f (polynomial.C a) :=
begin
  sorry
end

Anne Baanen (Jun 23 2021 at 14:43):

Doesn't aeval f (C a) mean "evaluate the constant polynomial C a at the point f? docs#polynomial.aeval

Johan Commelin (Jun 23 2021 at 14:43):

Ooh, so maybe you need to swap the arguments :see_no_evil:

Anne Baanen (Jun 23 2021 at 14:44):

Yes, we want instead aeval (C a) f, which is the same as C (aeval a f) = C (eval a f). (See e.g. my PR #8038)

Stanislas Polu (Jun 23 2021 at 14:45):

Fantastic!

Stanislas Polu (Jun 24 2021 at 15:09):

Follow up question. I'd like to state the rational root theorem using a non generalized form (polynomial with integer coefficients and consider rational roots).

I therefore need to state that the polynomial has integer coefficient and that a fraction in is a root of the polynomial but that does not typechecks because I'm no authorized to evaluate the polynomial in if it's defined as polynomial ℤ. Example:

theorem aopsbook_v2_c6_em6
  (P : polynomial )
  (p q : )
  (h₀ : p.gcd q = 1)
  (h₁ : is_root P (p:/q)) :
  p  P.coeff 0  q  P.leading_coeff :=
begin
  sorry
end

What exactly does polynomial R mean? Does it mean that the coeffs are in R? Can one define a polynomial whose coefficient are in R1 but codomain/domain R2 ?

Adam Topaz (Jun 24 2021 at 15:14):

I think docs#polynomial.aeval should work?

Stanislas Polu (Jun 24 2021 at 15:14):

Rephrasing, I'm surprised that eval : R → polynomial R → R or is_root : polynomial R → R → Prop and not is_root: polynomial R → S → Prop ?

Stanislas Polu (Jun 24 2021 at 15:15):

@Adam Topaz what would be the syntax to use docs#polynomial.aeval here?

Stanislas Polu (Jun 24 2021 at 15:15):

(and I would use that to specify that p/q is a root) ?

Adam Topaz (Jun 24 2021 at 15:15):

One sec I'll try to sketch some code

Riccardo Brasca (Jun 24 2021 at 15:16):

You can use docs#polynomial.map to interpret a polynomial with integer coefficients as a polynomial with rational coefficients

Adam Topaz (Jun 24 2021 at 15:17):

Sure, you can do that too, but I think you can just use aeval in this case.

Stanislas Polu (Jun 24 2021 at 15:18):

Interesting, and out of curiousity how do I define (f : R →+* S) for and ?

Stanislas Polu (Jun 24 2021 at 15:18):

I presume it's fraction_map ℤ ℚ assuming that type checks ? :grimacing:

Anne Baanen (Jun 24 2021 at 15:19):

Almost, algebra_map ℤ ℚ

Riccardo Brasca (Jun 24 2021 at 15:19):

But I agree with Adam that aeval Is better

Anne Baanen (Jun 24 2021 at 15:20):

In mathlib we tend to define an algebra R S instance if there's a canonical map algebra_map R S : R →+* S.

Adam Topaz (Jun 24 2021 at 15:20):

Something like this is what I had in mind:

import ring_theory.polynomial.basic

#check @polynomial.aeval

example : algebra   := by apply_instance

variables (f : polynomial ) (p q : ) (h : p.gcd q = 1)

example (hh : polynomial.aeval ((p : ) / q) f  = 0) : true := sorry

Adam Topaz (Jun 24 2021 at 15:21):

(sorry, the #check and first example were just a sanity check for myself ;) )

Riccardo Brasca (Jun 24 2021 at 15:21):

We also have docs#int.cast_ring_hom

Anne Baanen (Jun 24 2021 at 15:22):

docs#fraction_map is more like ring_hom, in that fraction_map ℤ ℚ is the type of maps ℤ → ℚ that display as the fraction field of

Stanislas Polu (Jun 24 2021 at 15:23):

Thanks @Adam Topaz!

Stanislas Polu (Jun 24 2021 at 15:23):

And thanks @Riccardo Brasca and @Anne Baanen things are getting much clearer to me \o/

Adam Topaz (Jun 24 2021 at 15:24):

Unfortunately it looks like we can't use dot notation for polynomial.aeval because it gives the evaluation as a ring hom.

Adam Topaz (Jun 24 2021 at 15:24):

I.e. f.aeval ... doesn't work

Kevin Buzzard (Jun 24 2021 at 15:26):

Try it in Lean 4!

Stanislas Polu (Jun 24 2021 at 15:52):

So just to make sure I understand, aeval infers the mapping based on the types of the arguments and the right mapping is defined somewhere for it to pick it up automatically?

Eric Wieser (Jun 24 2021 at 15:58):

Yes, there's a long list of "canonical" mappings at docs#algebra

Eric Wieser (Jun 24 2021 at 15:59):

(under > Instances)

Adam Topaz (Jun 24 2021 at 16:07):

@Stanislas Polu the a in aeval stands for algebra. So it infers the right mapping based on an algebra instance. Fortunately for you, any ring has a unique structure of a Z-algebra and I think this is an actual instance in mathlib. So presumably you should be able to use aeval to evaluate an integral polynomial at an element in any ring.

Stanislas Polu (Jun 24 2021 at 16:32):

Thanks! Makes sense :+1:

Stanislas Polu (Jun 28 2021 at 12:51):

Hi! Me again :wave: What would be an elegant way to define the k-th symmetric sum of the roots of a polynomial to prove that it's (-1)^k*a_{n-k}/a_n ?

Stanislas Polu (Jun 28 2021 at 12:51):

I've been looking for that theorem to no avail, and have no good idea on how to define such a summation?

Johan Commelin (Jun 28 2021 at 12:56):

@Stanislas Polu Note that there is src/ring_theory/polynomial/symmetric.lean, but I don't think it contains exactly what you want.

Stanislas Polu (Jun 28 2021 at 13:13):

Thanks! will look into it :+1:

Stanislas Polu (Jun 28 2021 at 13:14):

(Seems quite unrelated indeed)

Johan Commelin (Jun 28 2021 at 13:15):

Well, a more fleshed out version of that file would hopefully make it very easy to talk about the k-th symmetric sum, etc...

Stanislas Polu (Jun 28 2021 at 13:51):

Relatedly how can I describe a sum on a multiset (to describe a sum that depends on the roots of a polynomial)?

Anne Baanen (Jun 28 2021 at 13:52):

If you have s : multiset α and you have a term f x for each x ∈ s that you want to add up, then you can use (s.map f).sum (see docs#multiset.map, docs#multiset.sum).

Stanislas Polu (Jun 28 2021 at 13:54):

@Anne Baanen Thanks!

Stanislas Polu (Jun 28 2021 at 13:56):

Fantastic!

import data.real.basic
import data.polynomial
open polynomial

theorem aopsbook_v2_c6_ex7
  (P : polynomial )
  (f :  -> )
  (h₀ : P = (C 3)*X^3 - (C 14)*X^2 + X + C 62)
  (h₁ :  r, f(r) = 1/(r+3)) :
  (P.roots.map f).sum = 83/74 :=
begin
  sorry
end

Kevin Buzzard (Jun 28 2021 at 19:17):

This does not look true to me. The cubic only has one real root and P.roots looks to me to be the multiset of real roots. Have I made a mistake?


Last updated: Dec 20 2023 at 11:08 UTC