Skip to content

Commit f02f014

Browse files
committed
Speed up roots for all finite fields when multiplicities=False
- Add `_roots_univariate_polynomial` to the finite field base class - `IntegerModRing`s will also use this method to speed up root finding for its polynomials
1 parent 755f48b commit f02f014

File tree

2 files changed

+81
-8
lines changed

2 files changed

+81
-8
lines changed

src/sage/rings/finite_rings/finite_field_base.pyx

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2120,6 +2120,84 @@ cdef class FiniteField(Field):
21202120
python_int = int.from_bytes(input_bytes, byteorder=byteorder)
21212121
return self.from_integer(python_int)
21222122

2123+
def _roots_univariate_polynomial(self, f, ring, multiplicities, **kwargs):
2124+
r"""
2125+
Return the roots of the univariate polynomial ``f``.
2126+
2127+
INPUT:
2128+
2129+
- ``f`` - a polynomial defined over this field
2130+
2131+
- ``ring`` - the ring to find roots in.
2132+
2133+
- ``multiplicities`` - bool (default: ``True``). If ``True``, return
2134+
list of pairs `(r, n)`, where `r` is a root and `n` is its
2135+
multiplicity. If ``False``, just return the unique roots, with no
2136+
information about multiplicities.
2137+
2138+
- ``kwargs`` - ignored
2139+
2140+
TESTS::
2141+
We can take the roots of a polynomial defined over a finite field
2142+
sage: set_random_seed(31337)
2143+
sage: p = random_prime(2^128)
2144+
sage: R.<x> = Zmod(p)[]
2145+
sage: f = R.random_element(degree=15)
2146+
sage: f.roots()
2147+
[(117558869610275297997958296126212805270, 1)]
2148+
2149+
We can take the roots of a polynomial defined over a finite field without multiplicities
2150+
sage: set_random_seed(31337)
2151+
sage: p = random_prime(2^128)
2152+
sage: R.<x> = Zmod(p)[]
2153+
sage: f = R.random_element(degree=150)
2154+
sage: f.roots(multiplicities=False)
2155+
[116560079209701720510648792531840294827]
2156+
2157+
We can take the roots of a polynomial defined over a finite field extension
2158+
sage: set_random_seed(31337)
2159+
sage: F.<a> = GF((2, 10))
2160+
sage: R.<x> = F[]
2161+
sage: f = R.random_element(degree=10)
2162+
sage: f.roots()
2163+
[(a^9 + a^8 + a^6 + a^4 + a^2, 1)]
2164+
sage: f.roots(multiplicities=False)
2165+
[a^9 + a^8 + a^6 + a^4 + a^2]
2166+
2167+
We can take the roots of a high degree polynomial in a reasonable time
2168+
sage: set_random_seed(31337)
2169+
sage: p = random_prime(2^128)
2170+
sage: F = GF(p)
2171+
sage: R.<x> = F[]
2172+
sage: f = R.random_element(degree=10000)
2173+
sage: f.roots(multiplicities=False)
2174+
[65940671326230628578511607550463701471]
2175+
"""
2176+
2177+
K = f.base_ring()
2178+
L = K if ring is None else ring
2179+
2180+
if K != L:
2181+
try:
2182+
f_L = f.change_ring(L)
2183+
except (TypeError, ValueError):
2184+
if L.is_exact() and L.is_subring(K):
2185+
return f._roots_in_subring(L, multiplicities)
2186+
else:
2187+
raise
2188+
return f_L.roots(multiplicities=multiplicities)
2189+
2190+
if multiplicities:
2191+
return f._roots_from_factorization(f.factor(), multiplicities)
2192+
else:
2193+
R = f.parent()
2194+
x = R.gen()
2195+
p = K.order()
2196+
g = pow(x, p, f) - x
2197+
g = f.gcd(g)
2198+
return g._roots_from_factorization(g.factor(), False)
2199+
2200+
21232201

21242202
def unpickle_FiniteField_ext(_type, order, variable_name, modulus, kwargs):
21252203
r"""

src/sage/rings/finite_rings/integer_mod_ring.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1916,7 +1916,7 @@ def _roots_univariate_polynomial(self, f, ring=None, multiplicities=True, algori
19161916
19171917
sage: set_random_seed(31337)
19181918
sage: p = random_prime(2^128)
1919-
sage: R.<x> = GF(p)[]
1919+
sage: R.<x> = Zmod(p)[]
19201920
sage: f = R.random_element(degree=5000)
19211921
sage: f.roots(multiplicities=False)
19221922
[107295314027801680550847462044796892009, 75545907600948005385964943744536832524]
@@ -1935,7 +1935,7 @@ def _roots_univariate_polynomial(self, f, ring=None, multiplicities=True, algori
19351935
" implemented (try the multiplicities=False option)"
19361936
)
19371937
# Roots of non-zero polynomial over finite fields by factorization
1938-
return f._roots_from_factorization(f.factor(), multiplicities)
1938+
return f.change_ring(f.base_ring().field()).roots(multiplicities=multiplicities)
19391939

19401940
# Zero polynomial is a base case
19411941
if deg < 0:
@@ -1944,12 +1944,7 @@ def _roots_univariate_polynomial(self, f, ring=None, multiplicities=True, algori
19441944

19451945
# Finite fields are a base case
19461946
if self.is_field():
1947-
R = f.parent()
1948-
x = R.gen()
1949-
p = R.modulus()
1950-
g = pow(x, p, f) - x
1951-
g = f.gcd(g)
1952-
return g._roots_from_factorization(g.factor(), False)
1947+
return f.change_ring(f.base_ring().field()).roots(multiplicities=False)
19531948

19541949
# Otherwise, find roots modulo each prime power
19551950
fac = self.factored_order()

0 commit comments

Comments
 (0)