Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# -*- coding: utf-8 -*- Isogeny class of elliptic curves over number fields
AUTHORS:
- David Roe (2012-03-29) -- initial version. - John Cremona (2014-08) -- extend to number fields. """
############################################################################## # Copyright (C) 2012-2014 David Roe <roed.math@gmail.com> # John Cremona <john.cremona@gmail.com> # William Stein <wstein@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) # # This code is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # The full text of the GPL is available at: # # http://www.gnu.org/licenses/ ##############################################################################
r""" Isogeny class of an elliptic curve.
.. note::
The current implementation chooses a curve from each isomorphism class in the isogeny class. Over `\QQ` this is a unique reduced minimal model in each isomorphism class. Over number fields the model chosen may change in future. """
r""" Over `\QQ` we use curves since minimal models exist and there is a canonical choice of one.
INPUT:
- ``label`` -- string or None, a Cremona or LMFDB label, used in printing. Ignored if base field is not `\QQ`.
EXAMPLES::
sage: cls = EllipticCurve('1011b1').isogeny_class() sage: print("\n".join([repr(E) for E in cls.curves])) Elliptic Curve defined by y^2 + x*y = x^3 - 8*x - 9 over Rational Field Elliptic Curve defined by y^2 + x*y = x^3 - 23*x + 30 over Rational Field """
""" The number of curves in the class.
EXAMPLES::
sage: E = EllipticCurve('15a') sage: len(E.isogeny_class()) # indirect doctest 8 """
""" Iterator over curves in the class.
EXAMPLES::
sage: E = EllipticCurve('15a') sage: all(C.conductor() == 15 for C in E.isogeny_class()) # indirect doctest True """
""" Returns the `i`th curve in the class.
EXAMPLES::
sage: E = EllipticCurve('990j1') sage: iso = E.isogeny_class(order="lmfdb") # orders lexicographically on a-invariants sage: iso[2] == E # indirect doctest True """
""" Returns the index of a curve in this class.
INPUT:
- ``C`` -- an elliptic curve in this isogeny class.
OUTPUT:
- ``i`` -- an integer so that the ``i`` th curve in the class is isomorphic to ``C``
EXAMPLES::
sage: E = EllipticCurve('990j1') sage: iso = E.isogeny_class(order="lmfdb") # orders lexicographically on a-invariants sage: iso.index(E.short_weierstrass_model()) 2 """ # This will need updating once we start talking about curves # over more general number fields raise ValueError("x not in isogeny class") raise ValueError("%s is not in isogeny class %s" % (C,self))
""" Compare self and other.
If they are different, compares the sorted underlying lists of curves.
Note that two isogeny classes with different orderings will compare as the same. If you want to include the ordering, just compare the list of curves.
EXAMPLES::
sage: E = EllipticCurve('990j1') sage: EE = EllipticCurve('990j4') sage: E.isogeny_class() == EE.isogeny_class() # indirect doctest True """ return NotImplemented
""" Hash is based on the a-invariants of the sorted list of minimal models.
EXAMPLES::
sage: E = EllipticCurve('990j1') sage: C = E.isogeny_class() sage: hash(C) == hash(tuple(sorted([curve.a_invariants() for curve in C.curves]))) # indirect doctest True """
""" The string representation of this isogeny class.
.. note::
Over `\QQ`, the string representation depends on whether an LMFDB or Cremona label for the curve is known when this isogeny class is constructed. Over general number fields, instead of labels the representation uses that of the curve initially used to create the class.
EXAMPLES:
If the curve is constructed from an LMFDB label then that label is used::
sage: E = EllipticCurve('462.f3') sage: E.isogeny_class() # indirect doctest Elliptic curve isogeny class 462.f
If the curve is constructed from a Cremona label then that label is used::
sage: E = EllipticCurve('990j1') sage: E.isogeny_class() Elliptic curve isogeny class 990j
Otherwise, including curves whose base field is not `\QQ`,the representation is determined from the curve used to create the class::
sage: E = EllipticCurve([1,2,3,4,5]) sage: E.isogeny_class() Isogeny class of Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Rational Field
sage: K.<i> = QuadraticField(-1) sage: E = EllipticCurve(K, [0,0,0,0,1]); E Elliptic Curve defined by y^2 = x^3 + 1 over Number Field in i with defining polynomial x^2 + 1 sage: C = E.isogeny_class() sage: C Isogeny class of Elliptic Curve defined by y^2 = x^3 + 1 over Number Field in i with defining polynomial x^2 + 1 sage: C.curves [Elliptic Curve defined by y^2 = x^3 + (-27) over Number Field in i with defining polynomial x^2 + 1, Elliptic Curve defined by y^2 = x^3 + 1 over Number Field in i with defining polynomial x^2 + 1, Elliptic Curve defined by y^2 + (i+1)*x*y + (i+1)*y = x^3 + i*x^2 + (-i+3)*x + 4*i over Number Field in i with defining polynomial x^2 + 1, Elliptic Curve defined by y^2 + (i+1)*x*y + (i+1)*y = x^3 + i*x^2 + (-i+33)*x + (-58*i) over Number Field in i with defining polynomial x^2 + 1] """ else:
""" INPUT:
- ``x`` -- a Python object.
OUTPUT:
- boolean -- ``True`` iff ``x`` is an elliptic curve in this isogeny class.
.. note::
If the input is isomorphic but not identical to a curve in the class, then ``False`` will be returned.
EXAMPLES::
sage: cls = EllipticCurve('15a3').isogeny_class() sage: E = EllipticCurve('15a7'); E in cls True sage: E.short_weierstrass_model() in cls True """ return False
""" Returns the matrix whose entries give the minimal degrees of isogenies between curves in this class.
INPUT:
- ``fill`` -- boolean (default True). If False then the matrix will contain only zeros and prime entries; if True it will fill in the other degrees.
EXAMPLES::
sage: isocls = EllipticCurve('15a3').isogeny_class() sage: isocls.matrix() [ 1 2 2 2 4 4 8 8] [ 2 1 4 4 8 8 16 16] [ 2 4 1 4 8 8 16 16] [ 2 4 4 1 2 2 4 4] [ 4 8 8 2 1 4 8 8] [ 4 8 8 2 4 1 2 2] [ 8 16 16 4 8 2 1 4] [ 8 16 16 4 8 2 4 1] sage: isocls.matrix(fill=False) [0 2 2 2 0 0 0 0] [2 0 0 0 0 0 0 0] [2 0 0 0 0 0 0 0] [2 0 0 0 2 2 0 0] [0 0 0 2 0 0 0 0] [0 0 0 2 0 0 2 2] [0 0 0 0 0 2 0 0] [0 0 0 0 0 2 0 0] """ self._compute_matrix()
def qf_matrix(self): """ Returns the array whose entries are quadratic forms representing the degrees of isogenies between curves in this class (CM case only).
OUTPUT:
a `2x2` array (list of lists) of list, each of the form [2] or [2,1,3] representing the coefficients of an integral quadratic form in 1 or 2 variables whose values are the possible isogeny degrees between the i'th and j'th curve in the class.
EXAMPLES::
sage: pol = PolynomialRing(QQ,'x')([1,0,3,0,1]) sage: K.<c> = NumberField(pol) sage: j = 1480640+565760*c^2 sage: E = EllipticCurve(j=j) sage: C = E.isogeny_class() sage: C.qf_matrix() [[[1], [2, 2, 3]], [[2, 2, 3], [1]]]
""" raise ValueError("qf_matrix only defined for isogeny classes with rational CM") else:
r""" Returns a list of lists of isogenies and 0s, corresponding to the entries of :meth:`matrix`
INPUT:
- ``fill`` -- boolean (default False). Whether to only return prime degree isogenies. Currently only implemented for ``fill=False``.
OUTPUT:
- a list of lists, where the ``j`` th entry of the ``i`` th list is either zero or a prime degree isogeny from the ``i`` th curve in this class to the ``j`` th curve.
.. WARNING::
The domains and codomains of the isogenies will have the same Weierstrass equation as the curves in this class, but they may not be identical python objects in the current implementation.
EXAMPLES::
sage: isocls = EllipticCurve('15a3').isogeny_class() sage: f = isocls.isogenies()[0][1]; f Isogeny of degree 2 from Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 5*x + 2 over Rational Field to Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 80*x + 242 over Rational Field sage: f.domain() == isocls.curves[0] and f.codomain() == isocls.curves[1] True """ raise NotImplementedError self._compute_isogenies() isogenies = self._maps
def graph(self): """ Returns a graph whose vertices correspond to curves in this class, and whose edges correspond to prime degree isogenies.
.. note::
There are only finitely many possible isogeny graphs for curves over `\QQ` [M78]. This function tries to lay out the graph nicely by special casing each isogeny graph. This could also be done over other number fields, such as quadratic fields.
.. note::
The vertices are labeled 1 to n rather than 0 to n-1 to match LMFDB and Cremona labels for curves over `\QQ`.
EXAMPLES::
sage: isocls = EllipticCurve('15a3').isogeny_class() sage: G = isocls.graph() sage: sorted(G._pos.items()) [(1, [-0.8660254, 0.5]), (2, [-0.8660254, 1.5]), (3, [-1.7320508, 0]), (4, [0, 0]), (5, [0, -1]), (6, [0.8660254, 0.5]), (7, [0.8660254, 1.5]), (8, [1.7320508, 0])]
REFERENCES:
.. [M78] \B. Mazur. Rational Isogenies of Prime Degree. *Inventiones mathematicae* 44,129-162 (1978). """
# The maximum degree classifies the shape of the isogeny # graph, though the number of vertices is often enough. # This only holds over Q, so this code will need to change # once other isogeny classes are implemented.
# one vertex pass # one edge, two vertices. We align horizontally and put # the lower number on the left vertex. G.set_pos(pos={0:[-0.5,0],1:[0.5,0]}) else: # o--o--o centervert = [i for i in range(3) if max(N.row(i)) < maxdegree][0] other = [i for i in range(3) if i != centervert] G.set_pos(pos={centervert:[0,0],other[0]:[-1,0],other[1]:[1,0]}) # o--o<8 centervert = [i for i in range(4) if max(N.row(i)) < maxdegree][0] other = [i for i in range(4) if i != centervert] G.set_pos(pos={centervert:[0,0],other[0]:[0,1],other[1]:[-0.8660254,-0.5],other[2]:[0.8660254,-0.5]}) # o--o--o--o centers = [i for i in range(4) if list(N.row(i)).count(3) == 2] left = [j for j in range(4) if N[centers[0],j] == 3 and j not in centers][0] right = [j for j in range(4) if N[centers[1],j] == 3 and j not in centers][0] G.set_pos(pos={left:[-1.5,0],centers[0]:[-0.5,0],centers[1]:[0.5,0],right:[1.5,0]}) # square opp = [i for i in range(1,4) if not N[0,i].is_prime()][0] other = [i for i in range(1,4) if i != opp] G.set_pos(pos={0:[1,1],other[0]:[-1,1],opp:[-1,-1],other[1]:[1,-1]}) # 8>o--o<8 centers = [i for i in range(6) if list(N.row(i)).count(2) == 3] left = [j for j in range(6) if N[centers[0],j] == 2 and j not in centers] right = [j for j in range(6) if N[centers[1],j] == 2 and j not in centers] G.set_pos(pos={centers[0]:[-0.5,0],left[0]:[-1,0.8660254],left[1]:[-1,-0.8660254],centers[1]:[0.5,0],right[0]:[1,0.8660254],right[1]:[1,-0.8660254]}) # two squares joined on an edge centers = [i for i in range(6) if list(N.row(i)).count(3) == 2] top = [j for j in range(6) if N[centers[0],j] == 3] bl = [j for j in range(6) if N[top[0],j] == 2][0] br = [j for j in range(6) if N[top[1],j] == 2][0] G.set_pos(pos={centers[0]:[0,0.5],centers[1]:[0,-0.5],top[0]:[-1,0.5],top[1]:[1,0.5],bl:[-1,-0.5],br:[1,-0.5]}) # tree from bottom, 3 regular except for the leaves. elif maxdegree == 12: # tent centers = [i for i in range(8) if list(N.row(i)).count(2) == 3] left = [j for j in range(8) if N[centers[0],j] == 2] right = [] for i in range(3): right.append([j for j in range(8) if N[centers[1],j] == 2 and N[left[i],j] == 3][0]) G.set_pos(pos={centers[0]:[-0.75,0],centers[1]:[0.75,0],left[0]:[-0.75,1],right[0]:[0.75,1],left[1]:[-1.25,-0.75],right[1]:[0.25,-0.75],left[2]:[-0.25,-0.25],right[2]:[1.25,-0.25]})
def reorder(self, order): r""" Return a new isogeny class with the curves reordered.
INPUT:
- ``order`` -- None, a string or an iterable over all curves in this class. See :meth:`sage.schemes.elliptic_curves.ell_rational_field.EllipticCurve_rational_field.isogeny_class` for more details.
OUTPUT:
- Another :class:`IsogenyClass_EC` with the curves reordered (and matrices and maps changed as appropriate)
EXAMPLES::
sage: isocls = EllipticCurve('15a1').isogeny_class() sage: print("\n".join([repr(C) for C in isocls.curves])) Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 10*x - 10 over Rational Field Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 5*x + 2 over Rational Field Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 + 35*x - 28 over Rational Field Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 135*x - 660 over Rational Field Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 80*x + 242 over Rational Field Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 over Rational Field Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 110*x - 880 over Rational Field Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 2160*x - 39540 over Rational Field sage: isocls2 = isocls.reorder('lmfdb') sage: print("\n".join([repr(C) for C in isocls2.curves])) Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 2160*x - 39540 over Rational Field Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 135*x - 660 over Rational Field Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 110*x - 880 over Rational Field Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 80*x + 242 over Rational Field Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 10*x - 10 over Rational Field Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 5*x + 2 over Rational Field Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 over Rational Field Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 + 35*x - 28 over Rational Field """ return self else: raise ValueError("Incorrect length") else: raise TypeError("order parameter should be a string, list of curves or isogeny class") except ValueError: try: j = self.curves.index(E.minimal_model()) except ValueError: raise ValueError("order does not yield a permutation of curves") else:
""" Isogeny classes for elliptic curves over number fields. """ """ INPUT:
- ``E`` -- an elliptic curve over a number field.
EXAMPLES::
sage: K.<i> = QuadraticField(-1) sage: E = EllipticCurve(K, [0,0,0,0,1]) sage: C = E.isogeny_class(); C Isogeny class of Elliptic Curve defined by y^2 = x^3 + 1 over Number Field in i with defining polynomial x^2 + 1
The curves in the class (sorted)::
sage: [E1.ainvs() for E1 in C] [(0, 0, 0, 0, -27), (0, 0, 0, 0, 1), (i + 1, i, i + 1, -i + 3, 4*i), (i + 1, i, i + 1, -i + 33, -58*i)]
The matrix of degrees of cyclic isogenies between curves::
sage: C.matrix() [1 3 6 2] [3 1 2 6] [6 2 1 3] [2 6 3 1]
The array of isogenies themselves is not filled out but only contains those used to construct the class, the other entries containing the integer 0. This will be changed when the class :class:`EllipticCurveIsogeny` allowed composition. In this case we used `2`-isogenies to go from 0 to 2 and from 1 to 3, and `3`-isogenies to go from 0 to 1 and from 2 to 3::
sage: isogs = C.isogenies() sage: [((i,j),isogs[i][j].degree()) for i in range(4) for j in range(4) if isogs[i][j]!=0] [((0, 1), 3), ((0, 3), 2), ((1, 0), 3), ((1, 2), 2), ((2, 1), 2), ((2, 3), 3), ((3, 0), 2), ((3, 2), 3)] sage: [((i,j),isogs[i][j].x_rational_map()) for i in range(4) for j in range(4) if isogs[i][j]!=0] [((0, 1), (1/9*x^3 - 12)/x^2), ((0, 3), (-1/2*i*x^2 + i*x - 12*i)/(x - 3)), ((1, 0), (x^3 + 4)/x^2), ((1, 2), (-1/2*i*x^2 - i*x - 2*i)/(x + 1)), ((2, 1), (1/2*i*x^2 - x)/(x + 3/2*i)), ((2, 3), (x^3 + 4*i*x^2 - 10*x - 10*i)/(x^2 + 4*i*x - 4)), ((3, 0), (1/2*i*x^2 + x + 4*i)/(x - 5/2*i)), ((3, 2), (1/9*x^3 - 4/3*i*x^2 - 34/3*x + 226/9*i)/(x^2 - 8*i*x - 16))]
sage: K.<i> = QuadraticField(-1) sage: E = EllipticCurve([1+i, -i, i, 1, 0]) sage: C = E.isogeny_class(); C Isogeny class of Elliptic Curve defined by y^2 + (i+1)*x*y + i*y = x^3 + (-i)*x^2 + x over Number Field in i with defining polynomial x^2 + 1 sage: len(C) 6 sage: C.matrix() [ 1 3 9 18 6 2] [ 3 1 3 6 2 6] [ 9 3 1 2 6 18] [18 6 2 1 3 9] [ 6 2 6 3 1 3] [ 2 6 18 9 3 1] sage: [E1.ainvs() for E1 in C] [(i + 1, i - 1, i, -i - 1, -i + 1), (i + 1, i - 1, i, 14*i + 4, 7*i + 14), (i + 1, i - 1, i, 59*i + 99, 372*i - 410), (i + 1, -i, i, -240*i - 399, 2869*i + 2627), (i + 1, -i, i, -5*i - 4, 2*i + 5), (i + 1, -i, i, 1, 0)]
An example with CM by `\sqrt{-5}`::
sage: pol = PolynomialRing(QQ,'x')([1,0,3,0,1]) sage: K.<c> = NumberField(pol) sage: j = 1480640+565760*c^2 sage: E = EllipticCurve(j=j) sage: E.has_cm() True sage: E.has_rational_cm() True sage: E.cm_discriminant() -20 sage: C = E.isogeny_class() sage: len(C) 2 sage: C.matrix() [1 2] [2 1] sage: [E.ainvs() for E in C] [(0, 0, 0, 83490*c^2 - 147015, -64739840*c^2 - 84465260), (0, 0, 0, -161535*c^2 + 70785, -62264180*c^3 + 6229080*c)] sage: C.isogenies()[0][1] Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + (83490*c^2-147015)*x + (-64739840*c^2-84465260) over Number Field in c with defining polynomial x^4 + 3*x^2 + 1 to Elliptic Curve defined by y^2 = x^3 + (-161535*c^2+70785)*x + (-62264180*c^3+6229080*c) over Number Field in c with defining polynomial x^4 + 3*x^2 + 1
TESTS::
sage: TestSuite(C).run() """
""" Returns a copy (mostly used in reordering).
EXAMPLES::
sage: K.<i> = QuadraticField(-1) sage: E = EllipticCurve(K, [0,0,0,0,1]) sage: C = E.isogeny_class() sage: C2 = C.copy() sage: C is C2 False sage: C == C2 True """ # The following isn't needed internally, but it will keep # things from breaking if this is used for something other # than reordering.
""" Computes the list of curves, the matrix and prime-degree isogenies.
EXAMPLES::
sage: K.<i> = QuadraticField(-1) sage: E = EllipticCurve(K, [0,0,0,0,1]) sage: C = E.isogeny_class() sage: C2 = C.copy() sage: C2._mat sage: C2._compute() sage: C2._mat [1 3 6 2] [3 1 2 6] [6 2 1 3] [2 6 3 1]
sage: C2._compute(verbose=True) possible isogeny degrees: [2, 3] -actual isogeny degrees: {2, 3} -added curve #1 (degree 2)... -added tuple [0, 1, 2]... -added tuple [1, 0, 2]... -added curve #2 (degree 3)... -added tuple [0, 2, 3]... -added tuple [2, 0, 3]...... relevant degrees: [2, 3]... -now completing the isogeny class... -processing curve #1... -added tuple [1, 0, 2]... -added tuple [0, 1, 2]... -added curve #3... -added tuple [1, 3, 3]... -added tuple [3, 1, 3]... -processing curve #2... -added tuple [2, 3, 2]... -added tuple [3, 2, 2]... -added tuple [2, 0, 3]... -added tuple [0, 2, 3]... -processing curve #3... -added tuple [3, 2, 2]... -added tuple [2, 3, 2]... -added tuple [3, 1, 3]... -added tuple [1, 3, 3]...... isogeny class has size 4 Sorting permutation = {0: 1, 1: 2, 2: 0, 3: 3} Matrix = [1 3 6 2] [3 1 2 6] [6 2 1 3] [2 6 3 1]
TESTS:
Check that :trac:`19030` is fixed (codomains of reverse isogenies were wrong)::
sage: K.<i> = NumberField(x^2+1) sage: E = EllipticCurve([1, i + 1, 1, -72*i + 8, 95*i + 146]) sage: C = E.isogeny_class() sage: curves = C.curves sage: isos = C.isogenies() sage: isos[0][3].codomain() == curves[3] True
"""
# Add all new codomains to the list and collect degrees: # tuples (i,j,l,phi) where curve i is l-isogenous to curve j via phi
else:
# key function for sorting else:
# In the CM case, we will have found some "horizontal" # isogenies of composite degree and would like to replace them # by isogenies of prime degree, mainly to make the isogeny # graph look better. We also construct a matrix whose entries # are not degrees of cyclic isogenies, but rather quadratic # forms (in 1 or 2 variables) representing the isogeny # degrees. For this we take a short cut: properly speaking, # when `\text{End}(E_1)=\text{End}(E_2)=O`, the set # `\text{Hom}(E_1,E_2)` is a rank `1` projective `O`-module, # hence has a well-defined ideal class associated to it, and # hence (using an identification between the ideal class group # and the group of classes of primitive quadratic forms of the # same discriminant) an equivalence class of quadratic forms. # But we currently only care about the numbers represented by # the form, i.e. which genus it is in rather than the exact # class. So it suffices to find one form of the correct # discriminant which represents one isogeny degree from `E_1` # to `E_2` in order to obtain a form which represents all such # degrees.
print("Creating degree matrix (CM case)")
# values: lists of equivalence classes of # primitive forms of discriminant d
# now test which of the Qs represents n raise ValueError("No form of discriminant %d represents %s" %(d,n))
# mat[i,j] already 1 else: # mat[i,j] already unique else: # horizontal isogeny
print("new matrix = %s" % mat) print("matrix of forms = %s" % qfmat)
""" Computes the matrix, assuming that the list of curves is computed.
EXAMPLES::
sage: isocls = EllipticCurve('1225h1').isogeny_class('database') sage: isocls._mat sage: isocls._compute_matrix(); isocls._mat [ 0 37] [37 0] """
""" EXAMPLES::
sage: E = EllipticCurve('15a1') sage: isocls = E.isogeny_class() sage: maps = isocls.isogenies() # indirect doctest sage: f = maps[0][1] sage: f.domain() == isocls[0] and f.codomain() == isocls[1] True """ recomputed = self.E.isogeny_class(order=self.curves) self._mat = recomputed._mat # The domains and codomains here will be equal, but not the same Python object. self._maps = recomputed._maps
""" Isogeny classes for elliptic curves over `\QQ`. """ """ INPUT:
- ``E`` -- an elliptic curve over `\QQ`.
- ``algorithm`` -- a string (default "sage"). One of the following:
- "sage" -- Use sage's implementation to compute the curves, matrix and isogenies
- "database" -- Use the Cremona database (only works if the curve is in the database)
- ``label`` -- a string, the label of this isogeny class (e.g. '15a' or '37.b'). Used in printing.
- ``empty`` -- don't compute the curves right now (used when reordering)
EXAMPLES::
sage: isocls = EllipticCurve('389a1').isogeny_class(); isocls Elliptic curve isogeny class 389a sage: E = EllipticCurve([0, 0, 0, 0, 1001]) # conductor 108216108 sage: E.isogeny_class(order='database') Traceback (most recent call last): ... LookupError: Cremona database does not contain entry for Elliptic Curve defined by y^2 = x^3 + 1001 over Rational Field sage: TestSuite(isocls).run() """
""" Returns a copy (mostly used in reordering).
EXAMPLES::
sage: E = EllipticCurve('11a1') sage: C = E.isogeny_class() sage: C2 = C.copy() sage: C is C2 False sage: C == C2 True """ # The following isn't needed internally, but it will keep # things from breaking if this is used for something other # than reordering.
""" Computes the list of curves, and possibly the matrix and prime-degree isogenies (depending on the algorithm selected).
EXAMPLES::
sage: isocls = EllipticCurve('48a1').isogeny_class('sage').copy() sage: isocls._mat sage: isocls._compute(); isocls._mat [0 2 2 2 0 0] [2 0 0 0 2 2] [2 0 0 0 0 0] [2 0 0 0 0 0] [0 2 0 0 0 0] [0 2 0 0 0 0] """ raise RuntimeError("unable to find %s in the database" % self.E) raise RuntimeError("unable to find %s in the database" % self.E) # All curves will have the same conductor and isogeny class, # and there are most 8 of them, so lexicographic sorting is okay. # look to see if Edash is new. Note that the # curves returned by isogenies_prime_degree() are # standard minimal models, so it suffices to check # equality rather than isomorphism here. else: raise ValueError("unknown algorithm '%s'" % algorithm)
r""" Return a list of primes `\ell` sufficient to generate the isogeny class of `E`.
INPUT:
- ``E`` -- An elliptic curve defined over a number field.
OUTPUT:
A finite list of primes `\ell` such that every curve isogenous to this curve can be obtained by a finite sequence of isogenies of degree one of the primes in the list.
ALGORITHM:
For curves without CM, the set may be taken to be the finite set of primes at which the Galois representation is not surjective, since the existence of an `\ell`-isogeny is equivalent to the image of the mod-`\ell` Galois representation being contained in a Borel subgroup.
For curves with CM by the order `O` of discriminant `d`, the Galois representation is always non-surjective and the curve will admit `\ell`-isogenies for infinitely many primes `\ell`, but there are (of course) only finitely many codomains `E'`. The primes can be divided according to the discriminant `d'` of the CM order `O'` associated to `E`: either `O=O'`, or one contains the other with index `\ell`, since `\ell O\subset O'` and vice versa.
Case (1): `O=O'`. The degrees of all isogenies between `E` and `E'` are precisely the integers represented by one of the classes of binary quadratic forms `Q` of discriminant `d`. Hence to obtain all possible isomorphism classes of codomain `E'`, we need only use one prime `\ell` represented by each such class `Q`. It would in fact suffice to use primes represented by forms which generate the class group. Here we simply omit the principal class and one from each pair of inverse classes, and include a prime represented by each of the remaining forms.
Case (2): `[O':O]=\ell`: so `d=\ell^2d;`. We include all prime divisors of `d`.
Case (3): `[O:O']=\ell`: we may assume that `\ell` does not divide `d` as we have already included these, so `\ell` either splits or is inert in `O`; the class numbers satisfy `h(O')=(\ell\pm1)h(O)` accordingly. We include all primes `\ell` such that `\ell\pm1` divides the degree `[K:\QQ]`.
For curves with only potential CM we proceed as in the CM case, using `2[K:\QQ]` instead of `[K:\QQ]`.
EXAMPLES:
For curves without CM we determine the primes at which the mod `p` Galois representation is reducible, i.e. contained in a Borel subgroup::
sage: from sage.schemes.elliptic_curves.isogeny_class import possible_isogeny_degrees sage: E = EllipticCurve('11a1') sage: possible_isogeny_degrees(E) [5]
We check that in this case `E` really does have rational `5`-isogenies::
sage: [phi.degree() for phi in E.isogenies_prime_degree()] [5, 5]
Over an extension field::
sage: E3 = E.change_ring(CyclotomicField(3)) sage: possible_isogeny_degrees(E3) [5] sage: [phi.degree() for phi in E3.isogenies_prime_degree()] [5, 5]
For curves with CM by a quadratic order of class number greater than `1`, we use the structure of the class group to only give one prime in each ideal class::
sage: pol = PolynomialRing(QQ,'x')([1,-3,5,-5,5,-3,1]) sage: L.<a> = NumberField(pol) sage: j = hilbert_class_polynomial(-23).roots(L,multiplicities=False)[0] sage: E = EllipticCurve(j=j) sage: from sage.schemes.elliptic_curves.isogeny_class import possible_isogeny_degrees sage: possible_isogeny_degrees(E, verbose=True) CM case, discriminant = -23 initial primes: {2} upward primes: {} downward ramified primes: {} downward split primes: {2, 3} downward inert primes: {5} primes generating the class group: [2] Complete set of primes: {2, 3, 5} [2, 3, 5] """
# This has 4 components: the class number, class group # structure (ignored), class group generators (as quadratic # forms) and regulator (=1 since d<0, ignored).
# We must have 2*h dividing n, and will need the quotient so # see if the j-invariants of any proper sub-orders could lie # in the same field
# Collect possible primes. First put in 2, and also 3 for # discriminant -3 (special case because of units):
# Step 1: "vertical" primes l such that the isogenous curve # has CM by an order whose index is l or 1/l times the index # of the order O of discriminant d. The latter case can only # happen when l^2 divides d.
# Compute the ramified primes
# if the CM is not rational we include all ramified primes, # which is simpler than using the class group later:
print("ramified primes: %s" % L1)
else:
# Find the "upward" primes (index divided by l):
# Find the "downward" primes (index multiplied by l, class # number multiplied by l-kronecker_symbol(d,l)):
# (a) ramified primes; the suborder has class number l*h, so l # must divide n/2h:
# (b) split primes; the suborder has class number (l-1)*h, so # l-1 must divide n/2h:
if (lm1+1).is_prime() and kronecker_symbol(d,lm1+1)==+1])
# (c) inert primes; the suborder has class number (l+1)*h, so # l+1 must divide n/2h:
if (lp1-1).is_prime() and kronecker_symbol(d,lp1-1)==-1])
# Now find primes represented by each form of discriminant d. # In the rational CM case, we use all forms associated to # generators of the class group, otherwise only forms of order # 2:
# Return sorted list
# Non-CM case
print("Non-CM case, using Galois representation")
|