Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add method to compute integer roots #72

Merged
merged 11 commits into from
Sep 7, 2023
50 changes: 43 additions & 7 deletions src/flint/flint_base/flint_base.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,50 @@ cdef class flint_poly(flint_elem):

def roots(self):
"""
Deprecated function.

To recover roots of a polynomial, first convert to acb:

acb_poly(input_poly).roots()
Computes all the roots in the base ring of the polynomial.
Returns a list of all pairs (*v*, *m*) where *v* is the
integer root and *m* is the multiplicity of the root

>>> from flint import fmpz_poly
>>> fmpz_poly([]).roots()
[]
>>> fmpz_poly([1]).roots()
[]
>>> fmpz_poly([2, 1]).roots()
[(-2, 1)]
>>> fmpz_poly([1, 2]).roots()
[]
>>> fmpz_poly([12, 7, 1]).roots()
[(-3, 1), (-4, 1)]
>>> (fmpz_poly([1, 2]) * fmpz_poly([-3,1]) * fmpz_poly([1, 2, 3]) * fmpz_poly([12, 7, 1])).roots()
[(-3, 1), (-4, 1), (3, 1)]
>>> from flint import nmod_poly
>>> nmod_poly([1], 11).roots()
[]
>>> nmod_poly([1, 2, 3], 11).roots()
[(8, 1), (6, 1)]
>>> nmod_poly([1, 6, 1, 8], 11).roots()
[(5, 3)]
>>> from flint import fmpq_poly
>>> fmpq_poly([1,2]).roots()
[(-1/2, 1)]
>>> fmpq_poly([2,1]).roots()
[(-2, 1)]
>>> f = fmpq_poly([fmpq(1,3), fmpq(3,5)]) * fmpq_poly([fmpq(4,11), fmpq(9)])
>>> f.roots()
[(-4/99, 1), (-5/9, 1)]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to add most of these examples just to the tests rather than the docstring. The primary purpose of the docstring should be for users to read as documentation rather than to function as a test suite. In that sense it is probably also good to mention how to find complex roots here because I expect some users would come to this function expecting it to do that.

"""
raise NotImplementedError('This method is no longer supported. To recover the complex roots first convert to acb_poly')

factor_fn = getattr(self, "factor", None)
if not callable(factor_fn):
raise NotImplementedError("Polynomial has no factor method, roots cannot be determined")

roots = []
factors = self.factor()
for fac, m in factors[1]:
if fac.degree() == fac[1] == 1:
v = - fac[0]
roots.append((v, m))
return roots


cdef class flint_mpoly(flint_elem):
Expand Down
14 changes: 10 additions & 4 deletions src/flint/test/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,11 +438,17 @@ def test_fmpz_poly():
assert Z([1,2,2]).sqrt() is None
assert Z([1,0,2,0,3]).deflation() == (Z([1,2,3]), 2)
assert Z([1,1]).deflation() == (Z([1,1]), 1)
[(r,m)] = Z([1,1]).roots()
[(r,m)] = Z([1,1]).complex_roots()
assert m == 1
assert r.overlaps(-1)
assert Z([]).complex_roots() == []
assert Z([1]).complex_roots() == []
[(r,m)] = Z([1,1]).roots()
assert m == 1
assert r == -1
assert Z([]).roots() == []
assert Z([1]).roots() == []
assert Z([1, 2]).roots() == []

def test_fmpz_poly_factor():
Z = flint.fmpz_poly
Expand Down Expand Up @@ -985,9 +991,9 @@ def set_bad():
assert Q.bernoulli_poly(3) == Q([0,1,-3,2],2)
assert Q.euler_poly(3) == Q([1,0,-6,4],4)
assert Q.legendre_p(3) == Q([0,-3,0,5],2)
assert Q([]).roots() == []
assert Q([1]).roots() == []
[(r,m)] = Q([1,1]).roots()
assert Q([]).complex_roots() == []
assert Q([1]).complex_roots() == []
[(r,m)] = Q([1,1]).complex_roots()
assert m == 1
assert r.overlaps(-1)

Expand Down
6 changes: 3 additions & 3 deletions src/flint/types/fmpq_poly.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -384,16 +384,16 @@ cdef class fmpq_poly(flint_poly):
fac[i] = (base, exp)
return c / self.denom(), fac

def roots(self, **kwargs):
def complex_roots(self, **kwargs):
"""
Computes the complex roots of this polynomial. See
:meth:`.fmpz_poly.roots`.

>>> from flint import fmpq
>>> fmpq_poly([fmpq(2,3),1]).roots()
>>> fmpq_poly([fmpq(2,3),1]).complex_roots()
[([-0.666666666666667 +/- 3.34e-16], 1)]
"""
return self.numer().roots(**kwargs)
return self.numer().complex_roots(**kwargs)

@staticmethod
def bernoulli_poly(n):
Expand Down
10 changes: 5 additions & 5 deletions src/flint/types/fmpz_poly.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -343,19 +343,19 @@ cdef class fmpz_poly(flint_poly):
fmpz_poly_factor_clear(fac)
return c, res

def roots(self, bint verbose=False):
def complex_roots(self, bint verbose=False):
"""
Computes all the complex roots of this polynomial.
Returns a list of pairs (*c*, *m*) where *c* is the root
as an *acb* and *m* is the multiplicity of the root.

>>> fmpz_poly([]).roots()
>>> fmpz_poly([]).complex_roots()
[]
>>> fmpz_poly([1]).roots()
>>> fmpz_poly([1]).complex_roots()
[]
>>> fmpz_poly([2,0,1]).roots()
>>> fmpz_poly([2,0,1]).complex_roots()
[([1.41421356237310 +/- 4.96e-15]j, 1), ([-1.41421356237310 +/- 4.96e-15]j, 1)]
>>> for c, m in (fmpz_poly([2,3,4]) * fmpz_poly([5,6,7,11])**3).roots():
>>> for c, m in (fmpz_poly([2,3,4]) * fmpz_poly([5,6,7,11])**3).complex_roots():
... print((c,m))
...
([-0.375000000000000 +/- 1.0e-19] + [0.599478940414090 +/- 5.75e-17]j, 1)
Expand Down
6 changes: 6 additions & 0 deletions src/flint/types/nmod.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ cdef class nmod(flint_scalar):
return res
else:
return not res
elif typecheck(s, nmod) and typecheck(t, int):
res = s.val == (t % s.mod.n)
if op == 2:
return res
else:
return not res
return NotImplemented

def __nonzero__(self):
Expand Down