Skip to content

Commit c4d7730

Browse files
Release Managervbraun
Release Manager
authored andcommitted
Trac #18589: isogeny efficiency improvement
Computation of isogenies of prime degree p is expensive when the degree is neither a "genus zero" prime [2,3,5,7,13] or a "hyperelliptic prime" [11, 17, 19, 23, 29, 31, 41, 47, 59, 71] (for these there is special code written). In one situation we can save time, after factoring the degree {{{(p^2-1)/2}}} division polynomial, if there is exactly one factor of degree (p-1)/2, or one subset of factors whose product has that degree, then the factor of degree (p-1)/2 must be a kernel polynomial. Then we do not need to check consistency, which is very expensive. The example which led me to this was with p=89 over a quadratic number field, where E.isogeny_class() was taking days. After the change here that goes down to 3 hours. (There are 4 curves in the isogeny class and the code requires factoring the 89-division polynomial of each!) I used a less extreme example for a doctest: a 37-isogeny. URL: http://trac.sagemath.org/18589 Reported by: cremona Ticket author(s): John Cremona Reviewer(s): Jeroen Demeyer
2 parents 46bf8a2 + 3d687e5 commit c4d7730

File tree

1 file changed

+99
-17
lines changed

1 file changed

+99
-17
lines changed

src/sage/schemes/elliptic_curves/isogeny_small_degree.py

+99-17
Original file line numberDiff line numberDiff line change
@@ -1848,7 +1848,7 @@ def isogenies_prime_degree_general(E, l):
18481848
ALGORITHM:
18491849
18501850
This algorithm factors the ``l``-division polynomial, then
1851-
combines its factors to otain kernels. See [KT2013]_, Chapter 3.
1851+
combines its factors to obtain kernels. See [KT2013]_, Chapter 3.
18521852
18531853
.. note::
18541854
@@ -1907,6 +1907,33 @@ def isogenies_prime_degree_general(E, l):
19071907
[Isogeny of degree 43 from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 42*x + 42 over Finite Field of size 43 to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 36 over Finite Field of size 43]
19081908
[Isogeny of degree 47 from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 46*x + 46 over Finite Field of size 47 to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 42*x + 34 over Finite Field of size 47]
19091909
1910+
Note that not all factors of degree (l-1)/2 of the l-division
1911+
polynomial are kernel polynomials. In this example, the
1912+
13-division polynomial factors as a product of 14 irreducible
1913+
factors of degree 6 each, but only two those are kernel
1914+
polynomials::
1915+
1916+
sage: F3 = GF(3)
1917+
sage: E = EllipticCurve(F3,[0,0,0,-1,0])
1918+
sage: Psi13 = E.division_polynomial(13)
1919+
sage: len([f for f,e in Psi13.factor() if f.degree()==6])
1920+
14
1921+
sage: len(E.isogenies_prime_degree(13))
1922+
2
1923+
1924+
Over GF(9) the other factors of degree 6 split into pairs of
1925+
cubics which can be rearranged to give the remaining 12 kernel
1926+
polynomials::
1927+
1928+
sage: len(E.change_ring(GF(3^2,'a')).isogenies_prime_degree(13))
1929+
14
1930+
1931+
See :trac:`18589`: the following example took 20s before, now only 4s::
1932+
1933+
sage: K.<i> = QuadraticField(-1)
1934+
sage: E = EllipticCurve(K,[0,0,0,1,0])
1935+
sage: [phi.codomain().ainvs() for phi in E.isogenies_prime_degree(37)] # long time
1936+
[(0, 0, 0, -840*i + 1081, 0), (0, 0, 0, 840*i + 1081, 0)]
19101937
"""
19111938
if not l.is_prime():
19121939
raise ValueError("%s is not prime."%l)
@@ -1916,28 +1943,83 @@ def isogenies_prime_degree_general(E, l):
19161943
return isogenies_3(E)
19171944

19181945
psi_l = E.division_polynomial(l)
1919-
factors = [h for h,e in psi_l.factor() if (l-1)/2 % h.degree() == 0]
1946+
1947+
# Every kernel polynomial is a product of irreducible factors of
1948+
# the division polynomial of the same degree, where this degree is
1949+
# a divisor of (l-1)/2, so we keep only such factors:
1950+
1951+
l2 = (l-1)//2
1952+
factors = [h for h,e in psi_l.factor()]
1953+
factors_by_degree = dict([(d,[f for f in factors if f.degree()==d])
1954+
for d in l2.divisors()])
1955+
1956+
ker = [] # will store all kernel polynomials found
1957+
1958+
# If for some d dividing (l-1)/2 there are exactly (l-1)/2d
1959+
# divisors of degree d, then their product is a kernel poly, which
1960+
# we add to the list and remove the factors used.
1961+
1962+
from sage.misc.all import prod
1963+
for d in factors_by_degree.keys():
1964+
if d*len(factors_by_degree[d]) == l2:
1965+
ker.append(prod(factors_by_degree.pop(d)))
1966+
1967+
# Exit now if all factors have been used already:
1968+
1969+
if all(factors == [] for factors in factors_by_degree.values()):
1970+
return [E.isogeny(k) for k in ker]
1971+
1972+
# In general we look for products of factors of the same degree d
1973+
# which can be kernel polynomials
1974+
1975+
from sage.rings.arith import gcd
19201976
a = _least_semi_primitive(l)
19211977
m = E.multiplication_by_m(a, x_only=True)
19221978
F = psi_l.parent()
19231979
x = F.gen()
1924-
ker = []
1925-
from sage.rings.arith import gcd
1926-
from sage.misc.all import prod
1980+
d = F(m.denominator())
1981+
n = F(m.numerator())
1982+
1983+
# This function permutes the factors of a given degree, replacing
1984+
# the factor with roots alpha with the one whose roots are
1985+
# m(alpha), where m(x) is the rational function giving the
1986+
# multiplcation-by-a map for a which generates (Z/lZ)^* / <-1>:
1987+
# a is a so-called semi-primitive root.
1988+
19271989
def mult(f):
19281990
return gcd(F(f(m(x)).numerator()),psi_l).monic()
1929-
while len(factors) > 0:
1930-
f = factors[0]
1931-
factors.remove(f)
1932-
d = f.degree()
1933-
S = [f]
1934-
for i in range((l-1)/(2*d)-1):
1935-
g = mult(S[i])
1936-
S.append(g)
1937-
if g in factors:
1938-
factors.remove(g)
1939-
if mult(S[-1]) == f:
1940-
ker.append(prod(S))
1991+
1992+
#assert all([mult(f) in factors for f in factors])
1993+
1994+
# Equivalent function, but not faster:
1995+
# R2 = PolynomialRing(F.base_ring(),2,['x2','y2'])
1996+
# x2, y2 = R2.gens()
1997+
# M = n(x2)-y2*d(x2)
1998+
# def mult2(f):
1999+
# return M.resultant(f(y2),y2)([x,0]).gcd(psi_l).monic()
2000+
#
2001+
#assert all([mult2(f) in factors for f in factors])
2002+
2003+
# kernel polynomials are the products of factors of degree d in
2004+
# one orbit under mult, provided that the orbit has length
2005+
# (l-1)/2d. Otherwise the orbit will be longer.
2006+
2007+
for d in factors_by_degree:
2008+
factors = factors_by_degree[d]
2009+
while len(factors) > 0:
2010+
# Compute an orbit under mult:
2011+
f = factors[0]
2012+
factors.remove(f)
2013+
orbit = [f]
2014+
f = mult(f)
2015+
while not f==orbit[0]:
2016+
orbit.append(f)
2017+
factors.remove(f)
2018+
f = mult(f)
2019+
# Check orbit length:
2020+
if d*len(orbit)==l2:
2021+
ker.append(prod(orbit))
2022+
19412023
return [E.isogeny(k) for k in ker]
19422024

19432025
def isogenies_prime_degree(E, l):

0 commit comments

Comments
 (0)