Skip to content

Commit ce5653b

Browse files
Release Managervbraun
Release Manager
authored andcommittedJun 24, 2017
Trac #16516: Faster roots computation for sparse polynomials over ZZ
Algorithms exist for the computation of the roots of sparse polynomials, which are much faster than the "generic" algorithms which work for dense polynomials. In this ticket, I implement one of these algorithms, for integer roots of sparse polynomials over ZZ. URL: https://trac.sagemath.org/16516 Reported by: bruno Ticket author(s): Bruno Grenet Reviewer(s): Vincent Delecroix, Travis Scrimshaw, Jeroen Demeyer
2 parents ec4b5bb + 686d2b3 commit ce5653b

File tree

4 files changed

+343
-91
lines changed

4 files changed

+343
-91
lines changed
 

‎src/doc/en/reference/references/index.rst

+4
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,10 @@ REFERENCES:
396396
the minimum number of vertices", Topology 40 (2001),
397397
753--772.
398398
399+
.. [CKS1999] Felipe Cucker, Pascal Koiran, and Stephen Smale. *A polynomial-time
400+
algorithm for diophantine equations in one variable*, J. Symbolic
401+
Computation 27 (1), 21-29, 1999.
402+
399403
.. [CK2015] \J. Campbell and V. Knight. *On testing degeneracy of
400404
bi-matrix
401405
games*. http://vknight.org/unpeudemath/code/2015/06/25/on_testing_degeneracy_of_games/ (2015)

‎src/sage/rings/integer_ring.pyx

+237-4
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ import sage.rings.ideal
6262
from sage.categories.basic import EuclideanDomains
6363
from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets
6464
from sage.structure.coerce cimport is_numpy_type
65+
from sage.structure.element cimport parent
6566
from sage.structure.parent_gens import ParentWithGens
6667
from sage.structure.parent cimport Parent
6768
from sage.structure.richcmp cimport rich_to_bool
@@ -429,9 +430,9 @@ cdef class IntegerRing_class(PrincipalIdealDomain):
429430
if x in self:
430431
return self
431432

432-
from sage.rings.number_field.number_field_element import is_NumberFieldElement
433-
if is_NumberFieldElement(x):
434-
K, from_K = x.parent().subfield(x)
433+
from sage.rings.number_field.number_field_element import NumberFieldElement
434+
if isinstance(x, NumberFieldElement):
435+
K, from_K = parent(x).subfield(x)
435436
return K.order(K.gen())
436437

437438
return PrincipalIdealDomain.__getitem__(self, x)
@@ -1216,6 +1217,238 @@ cdef class IntegerRing_class(PrincipalIdealDomain):
12161217
"""
12171218
return self(1)
12181219

1220+
def _roots_univariate_polynomial(self, p, ring=None, multiplicities=True, algorithm=None):
1221+
r"""
1222+
Return the roots of the univariate polynomial ``p``.
1223+
1224+
INPUT:
1225+
1226+
- ``p`` -- univariate integer polynomial
1227+
1228+
- ``ring`` -- ring (default: ``None``); a ring containing `\ZZ` to
1229+
compute the roots in; ``None`` is equivalent to ``ZZ``
1230+
1231+
- ``multiplicities`` -- boolean (default: ``True``); whether to
1232+
compute the multiplicities
1233+
1234+
- ``algorithm`` -- ``"dense"``, ``"sparse"`` or ``None`` (default:
1235+
``None``); the algorithm to use
1236+
1237+
OUTPUT:
1238+
1239+
- If ``multiplicities=True``, the list of pairs `(r, n)` where
1240+
`r` is a root and `n` the corresponding multiplicity;
1241+
1242+
- If ``multiplicities=False``, the list of distincts roots with no
1243+
information about the multiplicities.
1244+
1245+
ALGORITHM:
1246+
1247+
If ``algorithm`` is ``"dense"`, the roots are computed using
1248+
:meth:`_roots_from_factorization`.
1249+
1250+
If ``algorithm`` is ``"sparse"``, the roots are computed using the
1251+
algorithm described in [CKS1999]_.
1252+
1253+
If ``algorithm`` is ``None``, use the ``"dense"`` algorithm for
1254+
polynomials of degree at most `100`, and ``"sparse"`` otherwise.
1255+
1256+
.. NOTE::
1257+
1258+
This is a helper method for
1259+
:meth:`sage.rings.polynomial.polynomial_element.Polynomial.roots`.
1260+
1261+
TESTS::
1262+
1263+
sage: R.<x> = PolynomialRing(ZZ, sparse=True)
1264+
sage: p = (x + 1)^23 * (x - 1)^23 * (x - 100) * (x + 5445)^5
1265+
sage: ZZ._roots_univariate_polynomial(p)
1266+
[(100, 1), (-5445, 5), (1, 23), (-1, 23)]
1267+
sage: p *= (1 + x^3458645 - 76*x^3435423343 + x^45346567867756556)
1268+
sage: ZZ._roots_univariate_polynomial(p)
1269+
[(1, 23), (-1, 23), (100, 1), (-5445, 5)]
1270+
sage: p *= x^156468451540687043504386074354036574634735074
1271+
sage: ZZ._roots_univariate_polynomial(p)
1272+
[(0, 156468451540687043504386074354036574634735074),
1273+
(1, 23),
1274+
(-1, 23),
1275+
(100, 1),
1276+
(-5445, 5)]
1277+
sage: ZZ._roots_univariate_polynomial(p, multiplicities=False)
1278+
[0, 1, -1, 100, -5445]
1279+
1280+
sage: R.<x> = PolynomialRing(ZZ, sparse=False)
1281+
sage: p = (x + 1)^23 * (x - 1)^23 * (x - 100) * (x + 5445)^5
1282+
sage: ZZ._roots_univariate_polynomial(p)
1283+
[(100, 1), (-5445, 5), (1, 23), (-1, 23)]
1284+
sage: ZZ._roots_univariate_polynomial(p, multiplicities=False)
1285+
[100, -5445, 1, -1]
1286+
1287+
sage: ZZ._roots_univariate_polynomial(p, algorithm="sparse")
1288+
[(100, 1), (-5445, 5), (1, 23), (-1, 23)]
1289+
sage: ZZ._roots_univariate_polynomial(p, algorithm="dense")
1290+
[(100, 1), (-5445, 5), (1, 23), (-1, 23)]
1291+
sage: ZZ._roots_univariate_polynomial(p, algorithm="foobar")
1292+
Traceback (most recent call last):
1293+
...
1294+
ValueError: unknown algorithm 'foobar'
1295+
1296+
sage: p = x^20 * p
1297+
sage: ZZ._roots_univariate_polynomial(p, algorithm="sparse")
1298+
[(0, 20), (100, 1), (-5445, 5), (1, 23), (-1, 23)]
1299+
sage: ZZ._roots_univariate_polynomial(p, algorithm="dense")
1300+
[(100, 1), (-5445, 5), (0, 20), (1, 23), (-1, 23)]
1301+
"""
1302+
deg = p.degree()
1303+
if deg < 0:
1304+
raise ValueError("roots of 0 are not defined")
1305+
1306+
# A specific algorithm is available only for integer roots of integer polynomials
1307+
if ring is not self and ring is not None:
1308+
raise NotImplementedError
1309+
1310+
# Automatic choice of algorithm
1311+
if algorithm is None:
1312+
if deg > 100:
1313+
algorithm = "sparse"
1314+
else:
1315+
algorithm = "dense"
1316+
1317+
if algorithm != "dense" and algorithm != "sparse":
1318+
raise ValueError("unknown algorithm '{}'".format(algorithm))
1319+
1320+
# Check if the polynomial is a constant, in which case there are
1321+
# no roots. Note that the polynomial is not 0.
1322+
if deg == 0:
1323+
return []
1324+
1325+
# The dense algorithm is to compute the roots from the factorization.
1326+
if algorithm == "dense":
1327+
#NOTE: the content sometimes return an ideal sometimes a number...
1328+
if parent(p).is_sparse():
1329+
cont = p.content().gen()
1330+
else:
1331+
cont = p.content()
1332+
if not cont.is_unit():
1333+
p = p.map_coefficients(lambda c: c // cont)
1334+
return p._roots_from_factorization(p.factor(), multiplicities)
1335+
1336+
v = p.valuation()
1337+
deg -= v
1338+
cdef list roots
1339+
1340+
# Root 0
1341+
if v > 0:
1342+
roots = [(self.zero(), v)] if multiplicities else [self.zero()]
1343+
if deg == 0: # The shifted polynomial will be constant
1344+
return roots
1345+
else:
1346+
roots = []
1347+
1348+
p = p.shift(-v)
1349+
cdef list e = p.exponents()
1350+
cdef int i_min, i, j, k = len(e)
1351+
1352+
# totally dense polynomial
1353+
if k == 1 + deg:
1354+
return roots + p._roots_from_factorization(p.factor(), multiplicities)
1355+
1356+
#TODO: the content sometimes return an ideal sometimes a number... this
1357+
# should be corrected. See
1358+
# <https://groups.google.com/forum/#!topic/sage-devel/DP_R3rl0vH0>
1359+
if parent(p).is_sparse():
1360+
cont = p.content().gen()
1361+
else:
1362+
cont = p.content()
1363+
if not cont.is_unit():
1364+
p = p.map_coefficients(lambda c: c // cont)
1365+
1366+
cdef list c = p.coefficients()
1367+
1368+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
1369+
R = PolynomialRing(p.base_ring(), p.variable_name(), sparse=False)
1370+
c_max_nbits = c[0].nbits()
1371+
i_min = 0
1372+
g = R.zero()
1373+
1374+
# Looking for "large" gaps in the exponents
1375+
# These gaps split the polynomial into lower degree components
1376+
# Roots of modulus > 1 are common roots of the components
1377+
for i in range(1, k):
1378+
if e[i] - e[i-1] > c_max_nbits:
1379+
g = g.gcd(R( {e[j] - e[i_min]: c[j] for j in range(i_min, i)} ))
1380+
if g.is_one():
1381+
break
1382+
i_min = i
1383+
c_max_nbits = c[i].nbits()
1384+
else:
1385+
c_max_nbits = max(c[i].nbits(), c_max_nbits)
1386+
1387+
# if no gap, directly return the roots of p
1388+
if g.is_zero():
1389+
roots.extend(p._roots_from_factorization(p.factor(), multiplicities))
1390+
return roots
1391+
1392+
g = g.gcd(R( {e[j] - e[i_min]: c[j] for j in range(i_min, k)} ))
1393+
1394+
1395+
cdef list cc
1396+
cdef list ee
1397+
cdef int m1, m2
1398+
cdef bint b1, b2
1399+
# Computation of the roots of modulus 1, without multiplicities
1400+
# 1 is root iff p(1) == 0 ; -1 iff p(-1) == 0
1401+
if not multiplicities:
1402+
if not sum(c):
1403+
roots.append(self.one())
1404+
s = 0
1405+
for j in range(k):
1406+
s += -c[j] if (e[j] % 2) else c[j]
1407+
if not s:
1408+
roots.append(-self.one())
1409+
1410+
# Computation of the roots of modulus 1, with multiplicities
1411+
# For the multiplicities, take the derivatives
1412+
else:
1413+
cc = c
1414+
ee = e
1415+
m1 = m2 = 0
1416+
b1 = b2 = True
1417+
1418+
for i in range(k):
1419+
s1 = s2 = 0
1420+
for j in range(k-i):
1421+
if b1: s1 += cc[j]
1422+
if b2: s2 += -cc[j] if (ee[j] % 2) else cc[j]
1423+
if b1 and s1:
1424+
m1 = i
1425+
b1 = False
1426+
if b2 and s2:
1427+
m2 = i
1428+
b2 = False
1429+
# Stop asap
1430+
if not (b1 or b2):
1431+
break
1432+
1433+
# Sparse derivative, that is (p/x^v)' where v = p.val():
1434+
ee = [ee[j] - ee[0] - 1 for j in range(1,k-i)]
1435+
cc = [(ee[j] + 1) * cc[j+1] for j in range(k-i-1)]
1436+
1437+
if m1 > 0:
1438+
roots.append((self.one(), m1))
1439+
if m2 > 0:
1440+
roots.append((-self.one(), m2))
1441+
1442+
# Add roots of modulus > 1 to `roots`:
1443+
if multiplicities:
1444+
roots.extend(r for r in g._roots_from_factorization(g.factor(), True)
1445+
if r[0].abs() > 1)
1446+
else:
1447+
roots.extend(r for r in g._roots_from_factorization(g.factor(), False)
1448+
if r.abs() > 1)
1449+
1450+
return roots
1451+
12191452

12201453
#################################
12211454
## Coercions to interfaces
@@ -1363,7 +1596,7 @@ def crt_basis(X, xgcd=None):
13631596

13641597
Y = []
13651598
# 2. Compute extended GCD's
1366-
ONE = X[0].parent().one()
1599+
ONE = parent(X[0]).one()
13671600
for i in range(len(X)):
13681601
p = X[i]
13691602
others = P // p

‎src/sage/rings/polynomial/polynomial_element.pyx

+84-77
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
387387
# Note that we are guaranteed that right is in the base ring, so this could be fast.
388388
if not left:
389389
return self._parent.zero()
390-
return self.parent()(left) * self
390+
return self._parent(left) * self
391391

392392
cpdef _rmul_(self, Element right):
393393
"""
@@ -407,7 +407,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
407407
# Note that we are guaranteed that right is in the base ring, so this could be fast.
408408
if not right:
409409
return self._parent.zero()
410-
return self * self.parent()(right)
410+
return self * self._parent(right)
411411

412412
def subs(self, *x, **kwds):
413413
r"""
@@ -433,7 +433,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
433433
TypeError: keys do not match self's parent
434434
"""
435435
if len(x) == 1 and isinstance(x[0], dict):
436-
g = self.parent().gen()
436+
g = self._parent.gen()
437437
if g in x[0]:
438438
return self(x[0][g])
439439
elif len(x[0]) > 0:
@@ -1279,7 +1279,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
12791279
sage: f.variables()
12801280
(w,)
12811281
"""
1282-
d = dict([(repr(g), R.var(g)) for g in self.parent().gens()])
1282+
d = dict([(repr(g), R.var(g)) for g in self._parent.gens()])
12831283
return self.subs(**d)
12841284

12851285
def __invert__(self):
@@ -1293,7 +1293,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
12931293
sage: ~f
12941294
1/(x - 90283)
12951295
"""
1296-
return self.parent().one()/self
1296+
return self.parent().one() / self
12971297

12981298
def inverse_of_unit(self):
12991299
"""
@@ -1304,18 +1304,18 @@ cdef class Polynomial(CommutativeAlgebraElement):
13041304
sage: f.inverse_of_unit()
13051305
Traceback (most recent call last):
13061306
...
1307-
ValueError: self is not a unit.
1307+
ValueError: self is not a unit
13081308
sage: f = R(-90283); g = f.inverse_of_unit(); g
13091309
-1/90283
13101310
sage: parent(g)
13111311
Univariate Polynomial Ring in x over Rational Field
13121312
"""
13131313
if self.degree() > 0:
13141314
if not self.is_unit():
1315-
raise ValueError("self is not a unit.")
1315+
raise ValueError("self is not a unit")
13161316
else:
13171317
raise NotImplementedError("polynomial inversion over non-integral domains not implemented")
1318-
return self.parent()(~(self[0]))
1318+
return self._parent(~(self[0]))
13191319

13201320
def inverse_mod(a, m):
13211321
"""
@@ -1767,7 +1767,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
17671767
except NotImplementedError:
17681768
f = self.factor()
17691769

1770-
u = self.parent().base_ring()(f.unit())
1770+
u = self._parent.base_ring()(f.unit())
17711771

17721772
if all(a[1] % 2 == 0 for a in f) and u.is_square():
17731773
g = u.sqrt()
@@ -1899,7 +1899,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
18991899
raise NotImplementedError
19001900
allowed_deg_mult = Integer(ring.factored_order()[0][1]) # generally it will be the quotient of this by the degree of the base ring.
19011901
if degree is None:
1902-
x = self.parent().gen()
1902+
x = self._parent.gen()
19031903
if allowed_deg_mult == 1:
19041904
xq = pow(x,q,self)
19051905
self = self.gcd(xq-x)
@@ -1954,7 +1954,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
19541954
break
19551955
if degree == 0:
19561956
raise ValueError("degree should be nonzero")
1957-
R = self.parent()
1957+
R = self._parent
19581958
x = R.gen()
19591959
if degree > 0:
19601960
xq = x
@@ -2326,12 +2326,12 @@ cdef class Polynomial(CommutativeAlgebraElement):
23262326
def _pow(self, right):
23272327
# TODO: fit __pow__ into the arithmetic structure
23282328
if self.degree() <= 0:
2329-
return self.parent()(self[0]**right)
2329+
return self._parent(self[0]**right)
23302330
if right < 0:
23312331
return (~self)**(-right)
2332-
if (<Polynomial>self) == self.parent().gen(): # special case x**n should be faster!
2332+
if (<Polynomial>self) == self._parent.gen(): # special case x**n should be faster!
23332333
v = [0]*right + [1]
2334-
return self.parent()(v, check=True)
2334+
return self._parent(v, check=True)
23352335
return generic_power(self, right)
23362336

23372337
def _repr(self, name=None):
@@ -2371,8 +2371,8 @@ cdef class Polynomial(CommutativeAlgebraElement):
23712371
s = " "
23722372
m = self.degree() + 1
23732373
if name is None:
2374-
name = self.parent().variable_name()
2375-
atomic_repr = self.parent().base_ring()._repr_option('element_is_atomic')
2374+
name = self._parent.variable_name()
2375+
atomic_repr = self._parent.base_ring()._repr_option('element_is_atomic')
23762376
coeffs = self.list(copy=False)
23772377
for n in reversed(xrange(m)):
23782378
x = coeffs[n]
@@ -2458,8 +2458,8 @@ cdef class Polynomial(CommutativeAlgebraElement):
24582458
coeffs = self.list(copy=False)
24592459
m = len(coeffs)
24602460
if name is None:
2461-
name = self.parent().latex_variable_names()[0]
2462-
atomic_repr = self.parent().base_ring()._repr_option('element_is_atomic')
2461+
name = self._parent.latex_variable_names()[0]
2462+
atomic_repr = self._parent.base_ring()._repr_option('element_is_atomic')
24632463
for n in reversed(xrange(m)):
24642464
x = coeffs[n]
24652465
x = y = latex(x)
@@ -2535,7 +2535,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
25352535
{binop:- {binop:** {gen:x {constr_parent: {subscr: {atomic:ZZ}[{atomic:'x'}]} with gens: ('x',)}} {atomic:2}} {atomic:1}}
25362536
"""
25372537
if self.degree() > 0:
2538-
gen = sib.gen(self.parent())
2538+
gen = sib.gen(self._parent)
25392539
coeffs = self.list(copy=False)
25402540
terms = []
25412541
for i in range(len(coeffs)-1, -1, -1):
@@ -2551,7 +2551,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
25512551
elif coerced:
25522552
return sib(self.constant_coefficient(), True)
25532553
else:
2554-
return sib(self.parent())(sib(self.constant_coefficient(), True))
2554+
return sib(self._parent)(sib(self.constant_coefficient(), True))
25552555

25562556
def __setitem__(self, n, value):
25572557
"""
@@ -2791,7 +2791,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
27912791
27922792
- Didier Deshommes (2006-05-25)
27932793
"""
2794-
return self.parent()(polynomial_fateman._mul_fateman_mul(self,right))
2794+
return self._parent(polynomial_fateman._mul_fateman_mul(self,right))
27952795

27962796
@cython.boundscheck(False)
27972797
@cython.wraparound(False)
@@ -2976,7 +2976,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
29762976
sage: (2*x+3).base_ring()
29772977
Integer Ring
29782978
"""
2979-
return self.parent().base_ring()
2979+
return self._parent.base_ring()
29802980

29812981
cpdef base_extend(self, R):
29822982
"""
@@ -2994,7 +2994,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
29942994
sage: f.change_ring(GF(7))
29952995
x^3 + 4*x + 3
29962996
"""
2997-
S = self.parent().base_extend(R)
2997+
S = self._parent.base_extend(R)
29982998
return S(self)
29992999

30003000
def change_variable_name(self, var):
@@ -3010,7 +3010,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
30103010
sage: f.change_variable_name('theta')
30113011
-2/7*theta^3 + 2/3*theta - 19/993
30123012
"""
3013-
R = self.parent().base_ring()[var]
3013+
R = self._parent.base_ring()[var]
30143014
return R(self.list())
30153015

30163016
def change_ring(self, R):
@@ -3040,10 +3040,10 @@ cdef class Polynomial(CommutativeAlgebraElement):
30403040
if isinstance(R, Morphism):
30413041
# we're given a hom of the base ring extend to a poly hom
30423042
if R.domain() == self.base_ring():
3043-
R = self.parent().hom(R, self.parent().change_ring(R.codomain()))
3043+
R = self._parent.hom(R, self._parent.change_ring(R.codomain()))
30443044
return R(self)
30453045
else:
3046-
return self.parent().change_ring(R)(self)
3046+
return self._parent.change_ring(R)(self)
30473047

30483048
def _mpoly_dict_recursive(self, variables=None, base_ring=None):
30493049
"""
@@ -3062,9 +3062,9 @@ cdef class Polynomial(CommutativeAlgebraElement):
30623062
if not self:
30633063
return {}
30643064

3065-
var = self.parent().variable_name()
3065+
var = self._parent.variable_name()
30663066
if variables is None:
3067-
variables = self.parent().variable_names_recursive()
3067+
variables = self._parent.variable_names_recursive()
30683068
if not var in variables:
30693069
x = base_ring(self) if base_ring else self
30703070
const_ix = ETuple((0,)*len(variables))
@@ -4039,7 +4039,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
40394039
except NotImplementedError:
40404040
pass
40414041

4042-
R = self.parent().base_ring()
4042+
R = self._parent.base_ring()
40434043
if hasattr(R, '_factor_univariate_polynomial'):
40444044
return R._factor_univariate_polynomial(self, **kwargs)
40454045

@@ -4069,7 +4069,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
40694069
from_M, to_M = M.structure()
40704070
g = M['x']([to_M(x) for x in self.list()])
40714071
F = g.factor()
4072-
S = self.parent()
4072+
S = self._parent
40734073
v = [(S([from_M(x) for x in f.list()]), e) for f, e in F]
40744074
return Factorization(v, from_M(F.unit()))
40754075

@@ -4107,7 +4107,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
41074107
if R.characteristic() > 1<<29:
41084108
raise NotImplementedError("Factorization of multivariate polynomials over prime fields with characteristic > 2^29 is not implemented.")
41094109

4110-
P = self.parent()
4110+
P = self._parent
41114111
P._singular_().set_ring()
41124112
S = self._singular_().factorize()
41134113
factors = S[1]
@@ -4166,7 +4166,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
41664166
"""
41674167
pols = G[0]
41684168
exps = G[1]
4169-
R = self.parent()
4169+
R = self._parent
41704170
F = [(R(f), int(e)) for f, e in zip(pols, exps)]
41714171

41724172
if unit is None:
@@ -4401,7 +4401,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
44014401
raise ZeroDivisionError("Pseudo-division by zero is not possible")
44024402

44034403
# if other is a constant, then R = 0 and Q = self * other^(deg(self))
4404-
if other in self.parent().base_ring():
4404+
if other in self._parent.base_ring():
44054405
return (self * other**(self.degree()), self._parent.zero())
44064406

44074407
R = self
@@ -4413,7 +4413,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
44134413
while not R.degree() < B.degree():
44144414
c = R.leading_coefficient()
44154415
diffdeg = R.degree() - B.degree()
4416-
Q = d*Q + self.parent()(c).shift(diffdeg)
4416+
Q = d*Q + self._parent(c).shift(diffdeg)
44174417
R = d*R - c*B.shift(diffdeg)
44184418
e -= 1
44194419

@@ -4669,7 +4669,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
46694669
if n is None:
46704670
q = self.base_ring().order()
46714671
n = q ** self.degree() - 1
4672-
y = self.parent().quo(self).gen()
4672+
y = self._parent.quo(self).gen()
46734673
from sage.groups.generic import order_from_multiple
46744674
return n == order_from_multiple(y, n, n_prime_divs, operation="*")
46754675
else:
@@ -4836,7 +4836,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
48364836
if sage.rings.rational_field.is_RationalField(R) or is_NumberField(R):
48374837
return NumberField(self, names)
48384838

4839-
return R.fraction_field()[self.parent().variable_name()].quotient(self, names)
4839+
return R.fraction_field()[self._parent.variable_name()].quotient(self, names)
48404840

48414841
def sylvester_matrix(self, right, variable = None):
48424842
"""
@@ -4959,16 +4959,16 @@ cdef class Polynomial(CommutativeAlgebraElement):
49594959
# This code is almost exactly the same as that of
49604960
# sylvester_matrix() in multi_polynomial.pyx.
49614961

4962-
if self.parent() != right.parent():
4962+
if self._parent != right.parent():
49634963
a, b = coercion_model.canonical_coercion(self,right)
49644964
variable = a.parent()(self.variables()[0])
49654965
#We add the variable to cover the case that right is a multivariate
49664966
#polynomial
49674967
return a.sylvester_matrix(b, variable)
49684968

49694969
if variable:
4970-
if variable.parent() != self.parent():
4971-
variable = self.parent()(variable)
4970+
if variable.parent() != self._parent:
4971+
variable = self._parent(variable)
49724972

49734973
from sage.matrix.constructor import matrix
49744974

@@ -5283,7 +5283,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
52835283
if self.is_monic():
52845284
return self
52855285
a = ~self.leading_coefficient()
5286-
R = self.parent()
5286+
R = self._parent
52875287
if a.parent() != R.base_ring():
52885288
S = R.base_extend(a.parent())
52895289
return a*S(self)
@@ -5306,7 +5306,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
53065306
sage: f.coefficients(sparse=False)
53075307
[1, 0, 2, 0, 1]
53085308
"""
5309-
zero = self.parent().base_ring().zero()
5309+
zero = self._parent.base_ring().zero()
53105310
if (sparse):
53115311
return [c for c in self.list() if c != zero]
53125312
else:
@@ -5323,7 +5323,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
53235323
sage: f.exponents()
53245324
[0, 2, 4]
53255325
"""
5326-
zero = self.parent().base_ring().zero()
5326+
zero = self._parent.base_ring().zero()
53275327
l = self.list()
53285328
return [i for i in range(len(l)) if l[i] != zero]
53295329

@@ -5488,15 +5488,15 @@ cdef class Polynomial(CommutativeAlgebraElement):
54885488
sage: f.monomial_coefficient(x^3)
54895489
0
54905490
"""
5491-
if not m.parent() is self.parent():
5491+
if not m.parent() is self._parent:
54925492
raise TypeError("monomial must have same parent as self.")
54935493

54945494
d = m.degree()
54955495
coeffs = self.list()
54965496
if 0 <= d < len(coeffs):
54975497
return coeffs[d]
54985498
else:
5499-
return self.parent().base_ring().zero()
5499+
return self._parent.base_ring().zero()
55005500

55015501
def monomials(self):
55025502
"""
@@ -5529,8 +5529,8 @@ cdef class Polynomial(CommutativeAlgebraElement):
55295529
"""
55305530
if self.is_zero():
55315531
return []
5532-
v = self.parent().gen()
5533-
zero = self.parent().base_ring().zero()
5532+
v = self._parent.gen()
5533+
zero = self._parent.base_ring().zero()
55345534
coeffs = self.list()
55355535
return [v**i for i in range(self.degree(), -1, -1) if coeffs[i] != zero]
55365536

@@ -5570,7 +5570,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
55705570
"""
55715571
n = sage.rings.integer.Integer(n)
55725572
df = self.derivative()
5573-
K = self.parent().base_ring()
5573+
K = self._parent.base_ring()
55745574
a = K(x0)
55755575
L = []
55765576
for i in range(n):
@@ -5835,7 +5835,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
58355835
sage: pari(a*x + 10*x^3)
58365836
Mod(2, 8)*x^3 + Mod(1, 8)*a*x
58375837
"""
5838-
return self._pari_with_name(self.parent().variable_name())
5838+
return self._pari_with_name(self._parent.variable_name())
58395839

58405840
def _pari_or_constant(self, name=None):
58415841
r"""
@@ -5870,7 +5870,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
58705870
if self.is_constant():
58715871
return self[0].__pari__()
58725872
if name is None:
5873-
name = self.parent().variable_name()
5873+
name = self._parent.variable_name()
58745874
return self._pari_with_name(name)
58755875

58765876
def _pari_with_name(self, name='x'):
@@ -5931,7 +5931,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
59315931
a*W^20 + a^7*s*t
59325932
"""
59335933
# Get a reference to Magma version of parent.
5934-
R = magma(self.parent())
5934+
R = magma(self._parent)
59355935
# Get list of coefficients.
59365936
v = ','.join([a._magma_init_(magma) for a in self.list()])
59375937
return '%s![%s]'%(R.name(), v)
@@ -5977,7 +5977,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
59775977
sage: f.factor()
59785978
(y + 5) * (y + 1)^2
59795979
"""
5980-
R = gap(self.parent())
5980+
R = gap(self._parent)
59815981
var = list(R.IndeterminatesOfPolynomialRing())[0]
59825982
return self(var)
59835983

@@ -6102,7 +6102,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
61026102
variable = self.variable_name()
61036103
try:
61046104
res = self.__pari__().polresultant(other, variable)
6105-
return self.parent().base_ring()(res)
6105+
return self._parent.base_ring()(res)
61066106
except (TypeError, ValueError, PariError, NotImplementedError):
61076107
return self.sylvester_matrix(other).det()
61086108

@@ -6450,7 +6450,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
64506450
(x - c^3) * (x - b^3) * (x - a^3)
64516451
64526452
"""
6453-
u, v = PolynomialRing(self.parent().base_ring(), ['u', 'v']).gens()
6453+
u, v = PolynomialRing(self._parent.base_ring(), ['u', 'v']).gens()
64546454
R = (u - v**n).resultant(self(v), v)
64556455
R = R([self.variables()[0], 0])
64566456
if monic:
@@ -6531,7 +6531,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
65316531
g = g.monic()
65326532
return g
65336533

6534-
fkn = fkd = self.parent().one()
6534+
fkn = fkd = self._parent.one()
65356535
for j in range(1, k + 1):
65366536
g = star(rpow(self, j), self.symmetric_power(k - j))
65376537
if j % 2:
@@ -6665,7 +6665,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
66656665
if self.is_zero():
66666666
return self._parent.zero()
66676667
n = self.degree()
6668-
base_ring = self.parent().base_ring()
6668+
base_ring = self._parent.base_ring()
66696669
if (is_MPolynomialRing(base_ring) or
66706670
is_PowerSeriesRing(base_ring)):
66716671
# It is often cheaper to compute discriminant of simple
@@ -7325,9 +7325,16 @@ cdef class Polynomial(CommutativeAlgebraElement):
73257325
(0.500000000000000 - 0.866025403784439*I, 5),
73267326
(0.500000000000000 + 0.866025403784439*I, 5)]
73277327
"""
7328-
K = self.parent().base_ring()
7328+
K = self._parent.base_ring()
7329+
# If the base ring has a method _roots_univariate_polynomial,
7330+
# try to use it. An exception is raised if the method does not
7331+
# handle the current parameters
73297332
if hasattr(K, '_roots_univariate_polynomial'):
7330-
return K._roots_univariate_polynomial(self, ring=ring, multiplicities=multiplicities, algorithm=algorithm, **kwds)
7333+
try:
7334+
return K._roots_univariate_polynomial(self, ring=ring, multiplicities=multiplicities, algorithm=algorithm, **kwds)
7335+
except NotImplementedError:
7336+
# This does not handle something, so keep calm and continue on
7337+
pass
73317338

73327339
if kwds:
73337340
raise TypeError("roots() got unexpected keyword argument(s): {}".format(kwds.keys()))
@@ -7545,11 +7552,11 @@ cdef class Polynomial(CommutativeAlgebraElement):
75457552
[2, 0, 1/2]
75467553
"""
75477554
seq = []
7548-
K = self.parent().base_ring()
7555+
K = self._parent.base_ring()
75497556
for fac in F:
75507557
g = fac[0]
75517558
if g.degree() == 1:
7552-
rt = -g[0]/g[1]
7559+
rt = -g[0] / g[1]
75537560
# We need to check that this root is actually in K;
75547561
# otherwise we'd return roots in the fraction field of K.
75557562
if rt in K:
@@ -7780,7 +7787,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
77807787
sage: f.variable_name()
77817788
't'
77827789
"""
7783-
return self.parent().variable_name()
7790+
return self._parent.variable_name()
77847791

77857792
@coerce_binop
77867793
def xgcd(self, other):
@@ -7924,16 +7931,16 @@ cdef class Polynomial(CommutativeAlgebraElement):
79247931
if self[k]:
79257932
return ZZ(k)
79267933
if isinstance(p, Polynomial):
7927-
p = self.parent().coerce(p)
7928-
elif is_Ideal(p) and p.ring() is self.parent(): # eventually need to handle fractional ideals in the fraction field
7929-
if self.parent().base_ring().is_field(): # common case
7934+
p = self._parent.coerce(p)
7935+
elif is_Ideal(p) and p.ring() is self._parent: # eventually need to handle fractional ideals in the fraction field
7936+
if self._parent.base_ring().is_field(): # common case
79307937
p = p.gen()
79317938
else:
79327939
raise NotImplementedError
79337940
else:
79347941
from sage.rings.fraction_field import is_FractionField
7935-
if is_FractionField(p.parent()) and self.parent().has_coerce_map_from(p.parent().ring()):
7936-
p = self.parent().coerce(p.parent().ring()(p)) # here we require that p be integral.
7942+
if is_FractionField(p.parent()) and self._parent.has_coerce_map_from(p.parent().ring()):
7943+
p = self._parent.coerce(p.parent().ring()(p)) # here we require that p be integral.
79377944
else:
79387945
raise TypeError("The polynomial, p, must have the same parent as self.")
79397946

@@ -7974,7 +7981,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
79747981
sage: f.add_bigoh(2).parent()
79757982
Power Series Ring in x over Integer Ring
79767983
"""
7977-
return self.parent().completion(self.parent().gen())(self).add_bigoh(prec)
7984+
return self._parent.completion(self._parent.gen())(self).add_bigoh(prec)
79787985

79797986
@cached_method
79807987
def is_irreducible(self):
@@ -8275,7 +8282,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
82758282
sage: del(QQbar._is_squarefree_univariate_polynomial)
82768283
82778284
"""
8278-
B = self.parent().base_ring()
8285+
B = self._parent.base_ring()
82798286
if B not in sage.categories.integral_domains.IntegralDomains():
82808287
raise TypeError("is_squarefree() is not defined for polynomials over {}".format(B))
82818288

@@ -8304,7 +8311,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
83048311
# a square-free polynomial has a square-free content
83058312
if not B.is_field():
83068313
content = self.content()
8307-
if content not in self.parent().base_ring():
8314+
if content not in self._parent.base_ring():
83088315
content = content.gen()
83098316
if not content.is_squarefree():
83108317
return False
@@ -8350,7 +8357,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
83508357
sage: (x^3 + x^2).radical()
83518358
x^2 + x
83528359
"""
8353-
P = self.parent()
8360+
P = self._parent
83548361
R = P.base_ring()
83558362
p = R.characteristic()
83568363
if p == 0 or p > self.degree():
@@ -8542,7 +8549,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
85428549
sage: g.parent()
85438550
Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL)
85448551
"""
8545-
R = self.parent()
8552+
R = self._parent
85468553
if new_base_ring is not None:
85478554
R = R.change_ring(new_base_ring)
85488555
elif isinstance(f, Map):
@@ -8690,7 +8697,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
86908697
if not self.is_monic():
86918698
return False
86928699

8693-
P = self.parent()
8700+
P = self._parent
86948701
gen = P.gen()
86958702

86968703
if self == gen - 1: # the first cyc. pol. is treated apart
@@ -8815,7 +8822,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
88158822
raise NotImplementedError("not implemented in non-zero characteristic")
88168823
if not S.is_exact():
88178824
raise NotImplementedError("not implemented for inexact base rings")
8818-
R = self.parent()
8825+
R = self._parent
88198826
x = R.gen()
88208827
# Extract Phi_n when n is odd.
88218828
t1 = self
@@ -9098,7 +9105,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
90989105
i = self.valuation()
90999106
if i%m:
91009107
raise ValueError("not a %s power"%m.ordinal_str())
9101-
S = self.parent()
9108+
S = self._parent
91029109
return S.gen()**(i//m) * (self>>i).nth_root(m)
91039110
else:
91049111
c = R.characteristic()
@@ -9112,7 +9119,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
91129119
if i%cc:
91139120
raise ValueError("not a %s power"%m.ordinal_str())
91149121
ans[i//cc] = self[i].nth_root(cc)
9115-
p = self.parent()(ans)
9122+
p = self._parent(ans)
91169123
m = m // cc
91179124
if m.is_one():
91189125
return p
@@ -9174,7 +9181,7 @@ cdef class Polynomial(CommutativeAlgebraElement):
91749181
raise ValueError("either the dictionary or the specialization must be provided")
91759182
else:
91769183
from sage.rings.polynomial.flatten import SpecializationMorphism
9177-
phi = SpecializationMorphism(self.parent(),D)
9184+
phi = SpecializationMorphism(self._parent,D)
91789185
return phi(self)
91799186

91809187
def _log_series(self, long n):
@@ -10052,13 +10059,13 @@ cdef class Polynomial_generic_dense(Polynomial):
1005210059
if self.is_zero():
1005310060
return self, self
1005410061

10055-
R = self.parent().base_ring()
10062+
R = self._parent.base_ring()
1005610063
x = (<Polynomial_generic_dense>self).__coeffs[:] # make a copy
1005710064
y = (<Polynomial_generic_dense>other).__coeffs
1005810065
m = len(x) # deg(self)=m-1
1005910066
n = len(y) # deg(other)=n-1
1006010067
if m < n:
10061-
return self.parent().zero(), self
10068+
return self._parent.zero(), self
1006210069

1006310070
quo = list()
1006410071
for k from m-n >= k >= 0:

‎src/sage/rings/polynomial/polynomial_element_generic.py

+18-10
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def dict(self):
141141
"""
142142
return dict(self.__coeffs)
143143

144-
def coefficients(self,sparse=True):
144+
def coefficients(self, sparse=True):
145145
"""
146146
Return the coefficients of the monomials appearing in ``self``.
147147
@@ -152,12 +152,22 @@ def coefficients(self,sparse=True):
152152
7*w^10000 + w^1997 + 5
153153
sage: f.coefficients()
154154
[5, 1, 7]
155+
156+
TESTS:
157+
158+
Check that all coefficients are in the base ring::
159+
160+
sage: S.<x> = PolynomialRing(QQ, sparse=True)
161+
sage: f = x^4
162+
sage: all(c.parent() is QQ for c in f.coefficients(False))
163+
True
155164
"""
156165
if sparse:
157-
return [c[1] for c in sorted(six.iteritems(self.__coeffs))]
166+
return [self.__coeffs[e] for e in self.exponents()]
158167
else:
159-
return [self.__coeffs[i] if i in self.__coeffs else 0
160-
for i in range(self.degree() + 1)]
168+
zero = self.parent().base_ring().zero()
169+
return [self.__coeffs[i] if i in self.__coeffs else zero
170+
for i in range(self.degree() + 1)]
161171

162172
def exponents(self):
163173
"""
@@ -190,10 +200,9 @@ def valuation(self):
190200
sage: R(0).valuation()
191201
+Infinity
192202
"""
193-
c = self.__coeffs.keys()
194-
if len(c) == 0:
203+
if not self.__coeffs:
195204
return infinity
196-
return ZZ(min(self.__coeffs.keys()))
205+
return ZZ(min(self.__coeffs))
197206

198207
def _derivative(self, var=None):
199208
"""
@@ -511,10 +520,9 @@ def degree(self, gen=None):
511520
sage: f.degree()
512521
50000
513522
"""
514-
v = self.__coeffs.keys()
515-
if len(v) == 0:
523+
if not self.__coeffs:
516524
return -1
517-
return max(v)
525+
return max(self.__coeffs)
518526

519527
def _add_(self, right):
520528
r"""

0 commit comments

Comments
 (0)
Please sign in to comment.