Skip to content
This repository was archived by the owner on Jan 30, 2023. It is now read-only.

Commit e19ef2a

Browse files
committed
Add ROth Ruckenstein algorithm
1 parent ced566c commit e19ef2a

File tree

1 file changed

+157
-1
lines changed

1 file changed

+157
-1
lines changed

src/sage/rings/polynomial/polynomial_ring.py

+157-1
Original file line numberDiff line numberDiff line change
@@ -2090,6 +2090,162 @@ def irreducible_element(self, n, algorithm=None):
20902090
else:
20912091
raise ValueError("no such algorithm for finding an irreducible polynomial: %s" % algorithm)
20922092

2093+
def _roth_ruckenstein(self, Q, degree_bound, precision):
2094+
r"""
2095+
Returns all polynomials which are a solution to the, possibly modular,
2096+
root-finding problem.
2097+
2098+
This is the core of Roth-Ruckenstein's algorithm where all conversions,
2099+
checks and parent-extraction have been done.
2100+
2101+
INPUT:
2102+
2103+
- ``Q`` -- a nonzero polynomial over ``F[x][y]``, represented as an ``F[x]``
2104+
list. The polynomial ``Q`` should be first truncated to ``precision``
2105+
2106+
- ``degree_bound`` -- a bound on the degree of the roots of ``Q`` that
2107+
the algorithm computes
2108+
2109+
- ``precision`` -- a non-negative integer or `None`. If given, it is the
2110+
sought precision for modular roots of `Q`. Otherwise, the algorithm
2111+
computes unconditional roots.
2112+
2113+
OUTPUT:
2114+
2115+
- a list containing all `F[x]` roots of `Q(y)`, possibly modular. If
2116+
``precision`` is given, the algorithm returns a list of pairs `(f, h)`,
2117+
where `f` is a polynomial and `h` is a non-negative integer such that
2118+
`Q(f + x^{h}*g) \equiv 0 \mod x^{d}` for any `g \in F[x]`, where `d` is
2119+
``precision``.
2120+
2121+
EXAMPLES::
2122+
2123+
sage: F = GF(17)
2124+
sage: Px.<x> = F[]
2125+
sage: Pxy.<y> = Px[]
2126+
sage: p = (y - (x**2 + x + 1)) * (y**2 - x + 1) * (y - (x**3 + 4*x + 16))
2127+
sage: Q = p.coefficients()
2128+
sage: Px._roth_ruckenstein(Q, 3, None)
2129+
[x^3 + 4*x + 16, x^2 + x + 1]
2130+
sage: Px._roth_ruckenstein(Q, 2, None)
2131+
[x^2 + x + 1]
2132+
sage: Px._roth_ruckenstein(Q, 1, 2)
2133+
[(4*x + 16, 2), (2*x + 13, 2), (15*x + 4, 2), (x + 1, 2)]
2134+
"""
2135+
def roth_rec(Q, lam, k):
2136+
r"""
2137+
Recursive core method for Roth-Ruckenstein algorithm.
2138+
2139+
INPUT:
2140+
2141+
- ``Q`` -- the current value of the polynomial
2142+
- ``lam`` -- is the power of x whose coefficient is being computed
2143+
- ``k`` -- the remaining precision to handle (if ``precision`` is given)
2144+
"""
2145+
def pascal_triangle(n, gamma):
2146+
"""
2147+
Compute a modified Pascal triangle, where the entry ``(i,j)``
2148+
equals ``binomial(j,i) * gamma^(j-i)``.
2149+
"""
2150+
res = [[F.one()]]
2151+
for _ in range(1, n+1):
2152+
res.append([gamma * res[-1][0]])
2153+
for i in range(1, n+1):
2154+
for j in range(1, i):
2155+
res[i].append(res[i-1][j-1] + gamma * res[i-1][j])
2156+
res[i].append(F.one())
2157+
return res
2158+
2159+
if precision and k <= 0:
2160+
solutions.append((self(g[:lam]), lam))
2161+
return
2162+
val = min(c.valuation() for c in Q)
2163+
T = [c.shift(-val) for c in Q]
2164+
if precision:
2165+
k = k - val
2166+
Ty = self([ p[0] for p in T ])
2167+
if Ty.is_zero() or (precision and k <= 0):
2168+
if precision:
2169+
solutions.append((self(g[:lam]), lam))
2170+
else:
2171+
solutions.append(self(g[:lam]))
2172+
return
2173+
roots = Ty.roots(multiplicities=False)
2174+
for gamma in roots:
2175+
g[lam] = gamma
2176+
if lam < degree_bound:
2177+
# Construct T(y=x*y + gamma)
2178+
ell = len(T)-1
2179+
yc = pascal_triangle(ell + 1, gamma)
2180+
Tg = []
2181+
for t in range(ell+1):
2182+
Tg.append(sum(yc[s][t] * T[s] for s in range(t, ell+1)).shift(t))
2183+
roth_rec(Tg , lam+1, k)
2184+
else:
2185+
if precision:
2186+
solutions.append((self(g[:lam+1]), lam+1))
2187+
elif sum( Q[t] * gamma**t for t in range(len(Q)) ).is_zero():
2188+
solutions.append(self(g[:lam+1]))
2189+
return
2190+
2191+
x = self.gen()
2192+
F = self.base_ring()
2193+
solutions = []
2194+
g = [F.zero()] * (degree_bound+1)
2195+
2196+
roth_rec(Q, 0, precision)
2197+
return solutions
2198+
2199+
def _roots_univariate_polynomial(self, p, ring=None, multiplicities=False, algorithm=None, degree_bound=None):
2200+
"""
2201+
Return the list of roots of ``p``.
2202+
2203+
INPUT:
2204+
2205+
- ``p`` -- the polynomial whose roots are computed
2206+
- ``ring`` -- the ring to find roots (default is the base ring of ``p``)
2207+
- ``multiplicities`` -- bool (default: True): currently, only roots
2208+
without multiplicities are computed.
2209+
- ``algorithm`` -- the algorithm to use; currently, the only supported
2210+
algorithm is ``"Roth-Ruckenstein"``
2211+
- ``degree_bound``-- if not ``None``, return only roots of degree at
2212+
most ``degree_bound``
2213+
2214+
EXAMPLES::
2215+
2216+
sage: R.<x> = GF(13)[]
2217+
sage: S.<y> = R[]
2218+
sage: p = y^2 + (12*x^2 + x + 11)*y + x^3 + 12*x^2 + 12*x + 1
2219+
sage: p.roots(multiplicities=False)
2220+
[x^2 + 11*x + 1, x + 1]
2221+
sage: p.roots(multiplicities=False, degree_bound=1)
2222+
[x + 1]
2223+
"""
2224+
if multiplicities:
2225+
raise NotImplementedError("Use multiplicities=False")
2226+
2227+
Q = p.list()
2228+
2229+
if degree_bound is None:
2230+
degree_bound = p.degree() # TODO; replace this by next lines once fixed
2231+
#l = len(Q) - 1
2232+
#dl = Q[l].degree()
2233+
#degree_bound = min((Q[i].degree() - dl)//(l - i) for i in range(l) if Q[i])
2234+
2235+
if algorithm is None:
2236+
algorithm = "Roth-Ruckenstein"
2237+
2238+
if algorithm == "Roth-Ruckenstein":
2239+
Q = p.list()
2240+
return self._roth_ruckenstein(Q, degree_bound, None)
2241+
2242+
elif algorithm == "Alekhnovich":
2243+
raise NotImplementedError("Alekhnovich algorithm is not implemented yet")
2244+
2245+
else:
2246+
raise ValueError("unknown algorithm '{}'".format(algorithm))
2247+
2248+
20932249

20942250
class PolynomialRing_cdvr(PolynomialRing_integral_domain):
20952251
r"""
@@ -2520,7 +2676,7 @@ def irreducible_element(self, n, algorithm=None):
25202676
x^33 + x^10 + 1
25212677
25222678
In degree 1::
2523-
2679+
25242680
sage: GF(97)['x'].irreducible_element(1)
25252681
x + 96
25262682
sage: GF(97)['x'].irreducible_element(1, algorithm="conway")

0 commit comments

Comments
 (0)