Skip to content

Commit b397659

Browse files
author
Release Manager
committed
gh-38493: compute endomorphism orders for elliptic curves over finite fields (rank-2 case) For an elliptic curve E over a finite field with an endomorphism algebra of rank two over ℚ, the endomorphism ring is a superorder of the "Frobenius order" ℤ[π]. This patch adds a simple method to compute the exact imaginary-quadratic order containing ℤ[π] which is isomorphic to the endomorphism ring of the curve, and generalizes the algorithm to supersingular elliptic curves with a (rational) endomorphism algebra of rank 2. URL: #38493 Reported by: Lorenz Panny Reviewer(s): John Cremona, Lorenz Panny
2 parents 7888c42 + 284dbfa commit b397659

1 file changed

Lines changed: 131 additions & 19 deletions

File tree

src/sage/schemes/elliptic_curves/ell_finite_field.py

Lines changed: 131 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -682,27 +682,31 @@ def frobenius_order(self):
682682
This computes the curve cardinality, which may be
683683
time-consuming.
684684
685+
.. SEEALSO::
686+
687+
:meth:`endomorphism_order`
688+
685689
EXAMPLES::
686690
687691
sage: E = EllipticCurve(GF(11),[3,3])
688692
sage: E.frobenius_order()
689-
Order of conductor 2 generated by phi
690-
in Number Field in phi with defining polynomial x^2 - 4*x + 11
693+
Order of conductor 2 generated by pi
694+
in Number Field in pi with defining polynomial x^2 - 4*x + 11
691695
692-
For some supersingular curves, Frobenius is in Z and the Frobenius
693-
order is Z::
696+
For some supersingular curves, Frobenius is in `\ZZ` and the Frobenius
697+
order is `\ZZ`::
694698
695699
sage: # needs sage.rings.finite_rings
696700
sage: E = EllipticCurve(GF(25,'a'),[0,0,0,0,1])
697701
sage: R = E.frobenius_order()
698702
sage: R
699703
Order generated by []
700-
in Number Field in phi with defining polynomial x + 5
704+
in Number Field in pi with defining polynomial x + 5
701705
sage: R.degree()
702706
1
703707
"""
704708
f = self.frobenius_polynomial().factor()[0][0]
705-
return ZZ.extension(f,names='phi')
709+
return ZZ.extension(f, names='pi')
706710

707711
def frobenius(self):
708712
r"""
@@ -719,7 +723,7 @@ def frobenius(self):
719723
720724
sage: E = EllipticCurve(GF(11),[3,3])
721725
sage: E.frobenius()
722-
phi
726+
pi
723727
sage: E.frobenius().minpoly()
724728
x^2 - 4*x + 11
725729
@@ -1619,7 +1623,11 @@ def _fetch_cached_order(self, other):
16191623

16201624
def height_above_floor(self, ell, e):
16211625
r"""
1622-
Return the height of the `j`-invariant of this ordinary elliptic curve on its `\ell`-volcano.
1626+
Return the height of the `j`-invariant of this elliptic curve on its `\ell`-volcano.
1627+
1628+
The curve must have a rational endomorphism ring of rank 2: This includes all
1629+
ordinary elliptic curves over finite fields as well as those supersingular
1630+
elliptic curves with Frobenius not in `\ZZ`.
16231631
16241632
INPUT:
16251633
@@ -1630,12 +1638,12 @@ def height_above_floor(self, ell, e):
16301638
16311639
.. NOTE::
16321640
1633-
For an ordinary `E/\GF{q}`, and a prime `\ell`, the height
1634-
`e` of the `\ell`-volcano containing `j(E)` is the `\ell`-adic
1641+
For a suitable `E/\GF{q}`, and a prime `\ell`, the height
1642+
`e` of the `\ell`-volcano containing `E` is the `\ell`-adic
16351643
valuation of the conductor of the order generated by the
1636-
Frobenius `\pi_E`; the height of `j(E)` on its
1644+
`\GF{q}`-Frobenius `\pi_E`; the height of `E` on its
16371645
ell-volcano is the `\ell`-adic valuation of the conductor
1638-
of the order `\text{End}(E)`.
1646+
of the order `\text{End}_{\GF{q}}(E)`.
16391647
16401648
ALGORITHM:
16411649
@@ -1652,30 +1660,71 @@ def height_above_floor(self, ell, e):
16521660
sage: E.height_above_floor(2,8)
16531661
5
16541662
"""
1663+
pi = self.frobenius()
1664+
if pi in ZZ:
1665+
raise ValueError("{} has a (rational) endomorphism ring of rank 4".format(self))
1666+
1667+
e = ZZ(e)
1668+
if not e:
1669+
return ZZ.zero()
1670+
16551671
if self.is_supersingular():
1656-
raise ValueError("{} is not ordinary".format(self))
1657-
if e == 0:
1658-
return 0
1672+
if ell == self.base_field().characteristic():
1673+
# In this (exceptional) case, the Frobenius can always be divided
1674+
# by the maximal possible power of the characteristic. The reason
1675+
# is that Frobenius must be of the form phi o [p^k] where phi is
1676+
# a purely inseparable isogeny of degree 1 or p, hence this [p^k]
1677+
# can always be divided out while retaining an endomorphism.
1678+
assert self.base_field().cardinality().valuation(ell) >= 2*e
1679+
return e
1680+
1681+
# In the supersingular case, the j-invariant alone does not determine
1682+
# the level in the volcano. (The underlying reason is that there can
1683+
# be multiple non-F_q-isomorphic curves with a given j-invariant in
1684+
# the isogeny graph.)
1685+
# Example: y^2 = x^3 ± x over F_p with p congruent to 3 modulo 4 have
1686+
# distinct (rational) endomorphism rings.
1687+
# Thus we run the "probing the depths" algorithm with F_q-isomorphism
1688+
# classes of curves instead.
1689+
E0 = [self] * 3
1690+
E1 = [phi.codomain() for phi in self.isogenies_prime_degree(ell)]
1691+
assert E1
1692+
if len(E1) == 1:
1693+
return ZZ.zero()
1694+
assert len(E1) == ell + 1
1695+
h = ZZ.one()
1696+
while True:
1697+
for i in range(3):
1698+
isogs = E1[i].isogenies_prime_degree(ell)
1699+
try:
1700+
step = next(phi for phi in isogs if not phi.codomain().is_isomorphic(E0[i]))
1701+
except StopIteration:
1702+
return h
1703+
E0[i], E1[i] = step.domain(), step.codomain()
1704+
h += 1
1705+
assert h <= e
1706+
raise AssertionError('unreachable code -- this is a bug')
1707+
16591708
j = self.j_invariant()
16601709
if j in [0, 1728]:
16611710
return e
16621711
F = j.parent()
16631712
x = polygen(F)
16641713
from sage.rings.polynomial.polynomial_ring import polygens
16651714
from sage.schemes.elliptic_curves.mod_poly import classical_modular_polynomial
1666-
X, Y = polygens(F, "X, Y", 2)
1715+
X, Y = polygens(F, 'X,Y')
16671716
phi = classical_modular_polynomial(ell)(X, Y)
16681717
j1 = phi([x,j]).roots(multiplicities=False)
16691718
nj1 = len(j1)
16701719
on_floor = self.two_torsion_rank() < 2 if ell == 2 else nj1 <= ell
16711720
if on_floor:
1672-
return 0
1721+
return ZZ.zero()
16731722
if e == 1 or nj1 != ell+1: # double roots can only happen at the surface
16741723
return e
16751724
if nj1 < 3:
1676-
return 0
1725+
return ZZ.zero()
16771726
j0 = [j,j,j]
1678-
h = 1
1727+
h = ZZ.one()
16791728
while True:
16801729
for i in range(3):
16811730
r = (phi([x,j1[i]])//(x-j0[i])).roots(multiplicities=False)
@@ -1760,6 +1809,69 @@ def endomorphism_discriminant_from_class_number(self, h):
17601809
return (v//cs[0])**2 * D0
17611810
raise ValueError("Incorrect class number {}".format(h))
17621811

1812+
def endomorphism_order(self):
1813+
r"""
1814+
Return a quadratic order isomorphic to the endomorphism ring
1815+
of this elliptic curve, assuming the order has rank two.
1816+
1817+
.. NOTE::
1818+
1819+
In the future, this method will hopefully be extended to return a
1820+
:class:`~sage.algebras.quatalg.quaternion_algebra.QuaternionOrder`
1821+
object in the rank-4 case, but this has not been implemented yet.
1822+
1823+
.. SEEALSO::
1824+
1825+
:meth:`frobenius_order`
1826+
1827+
EXAMPLES::
1828+
1829+
sage: E = EllipticCurve(GF(11), [3,3])
1830+
sage: E.endomorphism_order()
1831+
Maximal Order generated by 1/2*pi + 1/2 in Number Field in pi with defining polynomial x^2 - 4*x + 11
1832+
1833+
It also works for supersingular elliptic curves provided that Frobenius
1834+
is not in `\ZZ`::
1835+
1836+
sage: E = EllipticCurve(GF(11), [1,0])
1837+
sage: E.is_supersingular()
1838+
True
1839+
sage: E.endomorphism_order()
1840+
Order of conductor 2 generated by pi in Number Field in pi with defining polynomial x^2 + 11
1841+
1842+
::
1843+
1844+
sage: E = EllipticCurve(GF(11), [-1,0])
1845+
sage: E.is_supersingular()
1846+
True
1847+
sage: E.endomorphism_order()
1848+
Maximal Order generated by 1/2*pi + 1/2 in Number Field in pi with defining polynomial x^2 + 11
1849+
1850+
There are some exceptional cases where Frobenius itself is divisible
1851+
by the characteristic::
1852+
1853+
sage: EllipticCurve([GF(7^2).gen(), 0]).endomorphism_order()
1854+
Gaussian Integers generated by 1/7*pi in Number Field in pi with defining polynomial x^2 + 49
1855+
sage: EllipticCurve(GF(3^5), [1, 0]).endomorphism_order()
1856+
Order of conductor 2 generated by 1/9*pi in Number Field in pi with defining polynomial x^2 + 243
1857+
sage: EllipticCurve(GF(7^3), [-1, 0]).endomorphism_order()
1858+
Maximal Order generated by 1/14*pi + 1/2 in Number Field in pi with defining polynomial x^2 + 343
1859+
"""
1860+
pi = self.frobenius()
1861+
if pi in ZZ:
1862+
raise NotImplementedError('the rank-4 case is not supported yet')
1863+
1864+
O = self.frobenius_order()
1865+
f0 = O.conductor()
1866+
1867+
f = 1
1868+
for l,e in f0.factor():
1869+
h = self.height_above_floor(l, e)
1870+
f *= l**(e-h)
1871+
1872+
K = O.number_field()
1873+
return K.order_of_conductor(f)
1874+
17631875
def twists(self):
17641876
r"""
17651877
Return a list of `k`-isomorphism representatives of all

0 commit comments

Comments
 (0)