@@ -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