Skip to content

Commit 4b7bab3

Browse files
Merge pull request #72 from GiacomoPope/new_roots
Add method to compute integer roots
2 parents 68249c8 + ce633dc commit 4b7bab3

File tree

7 files changed

+69
-19
lines changed

7 files changed

+69
-19
lines changed

src/flint/flint_base/flint_base.pyx

+31-7
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,38 @@ cdef class flint_poly(flint_elem):
6666

6767
def roots(self):
6868
"""
69-
Deprecated function.
70-
71-
To recover roots of a polynomial, first convert to acb:
72-
73-
acb_poly(input_poly).roots()
69+
Computes all the roots in the base ring of the polynomial.
70+
Returns a list of all pairs (*v*, *m*) where *v* is the
71+
integer root and *m* is the multiplicity of the root.
72+
73+
To compute complex roots of a polynomial, instead use
74+
the `.complex_roots()` method, which is available on
75+
certain polynomial rings.
76+
77+
>>> from flint import fmpz_poly
78+
>>> fmpz_poly([1, 2]).roots()
79+
[]
80+
>>> fmpz_poly([2, 1]).roots()
81+
[(-2, 1)]
82+
>>> fmpz_poly([12, 7, 1]).roots()
83+
[(-3, 1), (-4, 1)]
84+
>>> (fmpz_poly([-5,1]) * fmpz_poly([-5,1]) * fmpz_poly([-3,1])).roots()
85+
[(3, 1), (5, 2)]
7486
"""
75-
raise NotImplementedError('This method is no longer supported. To recover the complex roots first convert to acb_poly')
76-
87+
factor_fn = getattr(self, "factor", None)
88+
if not callable(factor_fn):
89+
raise NotImplementedError("Polynomial has no factor method, roots cannot be determined")
90+
91+
roots = []
92+
factors = self.factor()
93+
for fac, m in factors[1]:
94+
if fac.degree() == fac[1] == 1:
95+
v = - fac[0]
96+
roots.append((v, m))
97+
return roots
98+
99+
def complex_roots(self):
100+
raise AttributeError("Complex roots are not supported for this polynomial")
77101

78102

79103
cdef class flint_mpoly(flint_elem):

src/flint/test/test.py

+15-4
Original file line numberDiff line numberDiff line change
@@ -438,11 +438,17 @@ def test_fmpz_poly():
438438
assert Z([1,2,2]).sqrt() is None
439439
assert Z([1,0,2,0,3]).deflation() == (Z([1,2,3]), 2)
440440
assert Z([1,1]).deflation() == (Z([1,1]), 1)
441-
[(r,m)] = Z([1,1]).roots()
441+
[(r,m)] = Z([1,1]).complex_roots()
442442
assert m == 1
443443
assert r.overlaps(-1)
444+
assert Z([]).complex_roots() == []
445+
assert Z([1]).complex_roots() == []
446+
[(r,m)] = Z([1,1]).roots()
447+
assert m == 1
448+
assert r == -1
444449
assert Z([]).roots() == []
445450
assert Z([1]).roots() == []
451+
assert Z([1, 2]).roots() == []
446452

447453
def test_fmpz_poly_factor():
448454
Z = flint.fmpz_poly
@@ -985,11 +991,13 @@ def set_bad():
985991
assert Q.bernoulli_poly(3) == Q([0,1,-3,2],2)
986992
assert Q.euler_poly(3) == Q([1,0,-6,4],4)
987993
assert Q.legendre_p(3) == Q([0,-3,0,5],2)
988-
assert Q([]).roots() == []
989-
assert Q([1]).roots() == []
990-
[(r,m)] = Q([1,1]).roots()
994+
assert Q([]).complex_roots() == []
995+
assert Q([1]).complex_roots() == []
996+
[(r,m)] = Q([1,1]).complex_roots()
991997
assert m == 1
992998
assert r.overlaps(-1)
999+
assert str(Q([1,2]).roots()) == "[(-1/2, 1)]"
1000+
assert Q([2,1]).roots() == [(-2, 1)]
9931001

9941002
def test_fmpq_mat():
9951003
Q = flint.fmpq_mat
@@ -1411,6 +1419,9 @@ def set_bad2():
14111419
for alg in [None, 'berlekamp', 'cantor-zassenhaus']:
14121420
assert p3.factor(alg) == f3
14131421
assert p3.factor(algorithm=alg) == f3
1422+
assert P([1], 11).roots() == []
1423+
assert P([1, 2, 3], 11).roots() == [(8, 1), (6, 1)]
1424+
assert P([1, 6, 1, 8], 11).roots() == [(5, 3)]
14141425

14151426
def test_nmod_mat():
14161427
M = flint.nmod_mat

src/flint/types/acb_poly.pyx

+2
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,8 @@ cdef class acb_poly(flint_poly):
412412

413413
return pyroots
414414

415+
complex_roots = roots
416+
415417
def root_bound(self):
416418
"""Returns an upper bound for the absolute value of
417419
the roots of self."""

src/flint/types/arb_poly.pyx

+7
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,13 @@ cdef class arb_poly(flint_poly):
113113
libc.stdlib.free(xs)
114114
return u
115115

116+
def complex_roots(self, **kwargs):
117+
"""
118+
Compute the complex roots of the polynomial by converting
119+
from arb_poly to acb_poly
120+
"""
121+
return acb_poly(self).roots(**kwargs)
122+
116123
def evaluate(self, xs, algorithm='fast'):
117124
"""
118125
Multipoint evaluation: evaluates *self* at the list of

src/flint/types/fmpq_poly.pyx

+3-3
Original file line numberDiff line numberDiff line change
@@ -384,16 +384,16 @@ cdef class fmpq_poly(flint_poly):
384384
fac[i] = (base, exp)
385385
return c / self.denom(), fac
386386

387-
def roots(self, **kwargs):
387+
def complex_roots(self, **kwargs):
388388
"""
389389
Computes the complex roots of this polynomial. See
390390
:meth:`.fmpz_poly.roots`.
391391
392392
>>> from flint import fmpq
393-
>>> fmpq_poly([fmpq(2,3),1]).roots()
393+
>>> fmpq_poly([fmpq(2,3),1]).complex_roots()
394394
[([-0.666666666666667 +/- 3.34e-16], 1)]
395395
"""
396-
return self.numer().roots(**kwargs)
396+
return self.numer().complex_roots(**kwargs)
397397

398398
@staticmethod
399399
def bernoulli_poly(n):

src/flint/types/fmpz_poly.pyx

+5-5
Original file line numberDiff line numberDiff line change
@@ -343,19 +343,19 @@ cdef class fmpz_poly(flint_poly):
343343
fmpz_poly_factor_clear(fac)
344344
return c, res
345345

346-
def roots(self, bint verbose=False):
346+
def complex_roots(self, bint verbose=False):
347347
"""
348348
Computes all the complex roots of this polynomial.
349349
Returns a list of pairs (*c*, *m*) where *c* is the root
350350
as an *acb* and *m* is the multiplicity of the root.
351351
352-
>>> fmpz_poly([]).roots()
352+
>>> fmpz_poly([]).complex_roots()
353353
[]
354-
>>> fmpz_poly([1]).roots()
354+
>>> fmpz_poly([1]).complex_roots()
355355
[]
356-
>>> fmpz_poly([2,0,1]).roots()
356+
>>> fmpz_poly([2,0,1]).complex_roots()
357357
[([1.41421356237310 +/- 4.96e-15]j, 1), ([-1.41421356237310 +/- 4.96e-15]j, 1)]
358-
>>> for c, m in (fmpz_poly([2,3,4]) * fmpz_poly([5,6,7,11])**3).roots():
358+
>>> for c, m in (fmpz_poly([2,3,4]) * fmpz_poly([5,6,7,11])**3).complex_roots():
359359
... print((c,m))
360360
...
361361
([-0.375000000000000 +/- 1.0e-19] + [0.599478940414090 +/- 5.75e-17]j, 1)

src/flint/types/nmod.pyx

+6
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ cdef class nmod(flint_scalar):
8181
return res
8282
else:
8383
return not res
84+
elif typecheck(s, nmod) and typecheck(t, int):
85+
res = s.val == (t % s.mod.n)
86+
if op == 2:
87+
return res
88+
else:
89+
return not res
8490
return NotImplemented
8591

8692
def __nonzero__(self):

0 commit comments

Comments
 (0)