Ticket #12768: 12768.patch

File 12768.patch, 68.6 KB (added by roed, 10 years ago)

Ready for review

  • sage/databases/cremona.py

    # HG changeset patch
    # User David Roe <roed.math@gmail.com>
    # Date 1333666108 25200
    # Node ID 78ede397675813442c5a0afbe082faa16cd95edb
    # Parent  bcf3992cc582036a4668e3541bb6e59b1db7a4d3
    #12768: new class for isogeny classes of elliptic curves.  Better plotting for the graph.
    
    diff --git a/sage/databases/cremona.py b/sage/databases/cremona.py
    a b  
    280280    label = chr(k)*int(n//26 + 1)
    281281    return label
    282282
     283cremona_label_regex = re.compile(r'(\d+)([a-z]*)(\d*)')
     284lmfdb_label_regex = re.compile(r'(\d+)\.([a-z]+)(\d*)')
     285
    283286def parse_cremona_label(label):
    284287    """
    285288    Given a Cremona label that defines an elliptic
    286     curve, e.g., 11A1 or 37B3, parse the label and return the
     289    curve, e.g., 11a1 or 37b3, parse the label and return the
    287290    conductor, isogeny class label, and number.
    288291
    289     The isogeny number may be omitted, in which case it defaults to 1.
    290     If the isogeny number and letter are both omitted, so label is just
    291     a string representing a conductor, then the label defaults to 'A'
    292     and the number to 1.
     292    For this function, the curve number may be omitted, in which case
     293    it defaults to 1.  If the curve number and isogeny class are both
     294    omitted (label is just a string representing a conductor), then
     295    the isogeny class defaults to 'a' and the number to 1.
    293296
    294297    INPUT:
    295298
     
    311314        sage: parse_cremona_label('10bb2')
    312315        (10, 'bb', 2)
    313316    """
    314     if not isinstance(label, str):
    315         label = str(label)
    316     i=0
    317     while i<len(label) and label[i]>='0' and label[i]<='9':
    318         i += 1
    319     j=i+1
    320     if j>len(label):
    321         label += "a"
    322     while j<len(label) and not (label[j]>='0' and label[j]<='9'):
    323         j += 1
    324     if j>=len(label):
    325         label += "1"
    326     conductor, iso, num = int(label[:i]), label[i:j], int(label[j:])
    327     return conductor, iso, num
     317    m = cremona_label_regex.match(str(label).lower())
     318    if m is None:
     319        raise ValueError(label + " is not a valid Cremona label")
     320    conductor, iso, num = m.groups()
     321    if len(iso) == 0:
     322        iso = "a"
     323    if len(num) == 0:
     324        num = "1"
     325    return int(conductor), iso, int(num)
     326
     327def parse_lmfdb_label(label):
     328    """
     329    Given an LMFDB label that defines an elliptic curve, e.g., 11.a1
     330    or 37.b3, parse the label and return the conductor, isogeny class
     331    label, and number.
     332
     333    The LMFDB label (named after the L-functions and modular forms
     334    database), is determined by the following two orders:
     335
     336    - Isogeny classes with the same conductor are ordered
     337      lexicographically by the coefficients in the q-expansion of the
     338      associated modular form.
     339
     340    - Curves within the same isogeny class are ordered
     341      lexicographically by the a-invariants of the minimal model.
     342
     343    The format is <conductor>.<iso><curve>, where the isogeny class is
     344    encoded using the same base-26 encoding into letters used in
     345    Cremona's labels.  For example, 990.h3 is the same as Cremona's 990j1
     346
     347    For this function, the curve number may be omitted, in which case
     348    it defaults to 1.  If the curve number and isogeny class are both
     349    omitted (label is just a string representing a conductor), then
     350    the isogeny class defaults to 'a' and the number to 1.
     351
     352    INPUT:
     353
     354    -  ``label`` - str
     355
     356    OUTPUT:
     357
     358    -  ``int`` - the conductor
     359    -  ``str`` - the isogeny class label
     360    -  ``int`` - the number
     361
     362    EXAMPLES::
     363
     364        sage: from sage.databases.cremona import parse_lmfdb_label
     365        sage: parse_cremona_label('37.a2')
     366        (37, 'a', 2)
     367        sage: parse_cremona_label('37.b')
     368        (37, 'b', 1)
     369        sage: parse_cremona_label('10.bb2')
     370        (10, 'bb', 2)
     371    """
     372    m = lmfdb_label_regex.match(str(label).lower())
     373    if m is None:
     374        raise ValueError(label + " is not a valid LMFDB label")
     375    conductor, iso, num = m.groups()
     376    if len(iso) == 0:
     377        iso = "a"
     378    if len(num) == 0:
     379        num = "1"
     380    return int(conductor), iso, int(num)
    328381
    329382def split_code(key):
    330383    """
     
    384437    if d!=0:  return d
    385438    return cmp(cu1,cu2)
    386439
     440def cremona_to_lmfdb(cremona_label, CDB=None):
     441    """
     442    Converts a Cremona label into an LMFDB label.
     443
     444    See :func:`parse_lmfdb_label` for an explanation of LMFDB labels.
     445
     446    INPUT:
     447
     448    - ``cremona_label`` -- a string, the Cremona label of a curve.
     449      This can be the label of a curve (e.g. '990j1') or of an isogeny
     450      class (e.g. '990j')
     451    - ``CDB`` -- the Cremona database in which to look up the isogeny
     452      classes of the same conductor.
     453
     454    OUTPUT:
     455
     456    - ``lmfdb_label`` -- a string, the corresponding LMFDB label.
     457
     458    EXAMPLES::
     459
     460        sage: from sage.databases.cremona import cremona_to_lmfdb, lmfdb_to_cremona
     461        sage: cremona_to_lmfdb('990j1')
     462        '990.h3'
     463        sage: lmfdb_to_cremona('990.h3')
     464        '990j1'
     465
     466    TESTS::
     467
     468        sage: for label in ['5077a1','66a3','102b','420c2']:
     469        ...       assert(lmfdb_to_cremona(cremona_to_lmfdb(label)) == label)
     470        sage: for label in ['438.c2','306.b','462.f3']:
     471        ...       assert(cremona_to_lmfdb(lmfdb_to_cremona(label)) == label)
     472    """
     473    from sage.libs.pari.all import pari
     474    m = cremona_label_regex.match(cremona_label)
     475    if m is None:
     476        raise ValueError("Invalid Cremona label")
     477    N, cremona_iso, cremona_number = m.groups()
     478    if CDB is None:
     479        CDB = CremonaDatabase()
     480    classes = CDB.isogeny_classes(N)
     481    ft = int(53)
     482    tff = int(255) # This should be enough to distinguish between curves (using heuristics from Sato-Tate for example)
     483    isos = []
     484    for i, iso in enumerate(classes):
     485        alist = iso[0][0]
     486        E = pari(alist).ellinit(precision=ft)
     487        isos.append((E.ellan(tff, python_ints=True), cremona_letter_code(i)))
     488    isos.sort()
     489    sorted_letters = [iso[1] for iso in isos]
     490    lmfdb_iso = cremona_letter_code(sorted_letters.index(cremona_iso))
     491    if len(cremona_number) > 0:
     492        iso_class = [(curve[0],str(i+1)) for i,curve in enumerate(classes[class_to_int(cremona_iso)])]
     493        iso_class.sort()
     494        sorted_numbers = [curve[1] for curve in iso_class]
     495        lmfdb_number = str(sorted_numbers.index(cremona_number)+1)
     496        return N + '.' + lmfdb_iso + lmfdb_number
     497    else:
     498        return N + '.' + lmfdb_iso
     499
     500def lmfdb_to_cremona(lmfdb_label, CDB=None):
     501    """
     502    Converts an LMFDB labe into a Cremona label.
     503
     504    See :func:`parse_lmfdb_label` for an explanation of LMFDB labels.
     505
     506    INPUT:
     507
     508    - ``lmfdb_label`` -- a string, the LMFDB label of a curve.
     509      This can be the label of a curve (e.g. '990.j1') or of an isogeny
     510      class (e.g. '990.j')
     511    - ``CDB`` -- the Cremona database in which to look up the isogeny
     512      classes of the same conductor.
     513
     514    OUTPUT:
     515
     516    - ``cremona_label`` -- a string, the corresponding Cremona label.
     517
     518    EXAMPLES::
     519
     520        sage: from sage.databases.cremona import cremona_to_lmfdb, lmfdb_to_cremona
     521        sage: lmfdb_to_cremona('990.h3')
     522        '990j1'
     523        sage: cremona_to_lmfdb('990j1')
     524        '990.h3'
     525    """
     526    from sage.libs.pari.all import pari
     527    m = lmfdb_label_regex.match(lmfdb_label)
     528    if m is None:
     529        raise ValueError("Invalid LMFDB label")
     530    N, lmfdb_iso, lmfdb_number = m.groups()
     531    if CDB is None:
     532        CDB = CremonaDatabase()
     533    classes = CDB.isogeny_classes(N)
     534    ft = int(53)
     535    tff = int(255) # This should be enough to distinguish between curves (using heuristics from Sato-Tate for example)
     536    isos = []
     537    for i, iso in enumerate(classes):
     538        alist = iso[0][0]
     539        E = pari(alist).ellinit(precision=ft)
     540        isos.append((E.ellan(tff, python_ints=True), cremona_letter_code(i)))
     541    isos.sort()
     542    cremona_iso = isos[class_to_int(lmfdb_iso)][1]
     543    if len(lmfdb_number) > 0:
     544        iso_class = [(curve[0],i+1) for i,curve in enumerate(classes[class_to_int(cremona_iso)])]
     545        iso_class.sort()
     546        cremona_number = str(iso_class[int(lmfdb_number)-1][1])
     547        return N + cremona_iso + cremona_number
     548    else:
     549        return N + cremona_iso
     550
    387551class MiniCremonaDatabase(SQLDatabase):
    388552    """
    389553    The Cremona database of elliptic curves.
     
    608772
    609773        INPUT:
    610774
    611         -  ``label`` - str (Cremona label)
     775        -  ``label`` - str (Cremona or LMFDB label)
    612776
    613         OUTPUT: EllipticCurve
     777        OUTPUT:
     778
     779        - an :class:`sage.schemes.elliptic_curves.ell_rational_field.EllipticCurve_rational_field`
     780
     781        .. note::
     782
     783            For more details on LMFDB labels see :func:`parse_lmfdb_label`.
    614784
    615785        EXAMPLES::
    616786
     
    623793            Traceback (most recent call last):
    624794            ...
    625795            ValueError: There is no elliptic curve with label 48c1 in the database (note: use lower case letters!)
     796
     797        You can also use LMFDB labels::
     798
     799            sage: c.elliptic_curve('462.f3')
     800            Elliptic Curve defined by y^2 + x*y = x^3 - 363*x + 1305 over Rational Field
    626801        """
    627         N, iso, num = parse_cremona_label(label)
     802        # There are two possible strings: the Cremona label and the LMFDB label.
     803        # They are distinguished by the presence of a period.
     804        if label.find('.') == -1:
     805            cremona_label = label
     806            lmfdb_label = None
     807        else:
     808            cremona_label = lmfdb_to_cremona(label)
     809            lmfdb_label = label
     810
     811        N, iso, num = parse_cremona_label(cremona_label)
    628812        label = str(N)+iso+str(num)
    629813        if self.get_skeleton() == _miniCremonaSkeleton:
    630814            q = self.__connection__.cursor().execute("SELECT eqn,rank,tors " \
     
    640824            F._set_rank(c[1])
    641825            F._set_torsion_order(c[2])
    642826            F._set_conductor(N)
     827            if lmfdb_label:
     828                F._lmfdb_label = lmfdb_label
    643829            if len(c) > 3:
    644830                if num == 1:
    645831                    F._set_modular_degree(c[3])
  • sage/schemes/elliptic_curves/BSD.py

    diff --git a/sage/schemes/elliptic_curves/BSD.py b/sage/schemes/elliptic_curves/BSD.py
    a b  
    449449        if BSD.curve.j_invariant() in non_max_j_invs: # is this possible for optimal curves?
    450450            if verbosity > 0:
    451451                print 'CM by non maximal order: switching curves'
    452             for E in BSD.curve.isogeny_class()[0]:
     452            for E in BSD.curve.isogeny_class(use_tuple=False):
    453453                if E.j_invariant() not in non_max_j_invs:
    454454                    BSD.curve = E
    455455                    break
     
    659659            kolyvagin_primes.append(p)
    660660    # Stein et al.
    661661    if not BSD.curve.has_cm():
    662         L = arith.lcm([F.torsion_order() for F in BSD.curve.isogeny_class()[0]])
     662        L = arith.lcm([F.torsion_order() for F in BSD.curve.isogeny_class(use_tuple=False)])
    663663        for p in BSD.primes:
    664664            if p in kolyvagin_primes or p == 2: continue
    665665            if L%p != 0:
  • sage/schemes/elliptic_curves/ell_modular_symbols.py

    diff --git a/sage/schemes/elliptic_curves/ell_modular_symbols.py b/sage/schemes/elliptic_curves/ell_modular_symbols.py
    a b  
    463463            8
    464464
    465465            sage: E = EllipticCurve('15a1')
    466             sage: [C.modular_symbol(use_eclib=True,normalize='L_ratio')(0) for C in E.isogeny_class()[0]]
     466            sage: [C.modular_symbol(use_eclib=True,normalize='L_ratio')(0) for C in E.isogeny_class(use_tuple=False)]
    467467            [1/4, 1/8, 1/4, 1/2, 1/8, 1/16, 1/2, 1]
    468             sage: [C.modular_symbol(use_eclib=True,normalize='none')(0) for C in E.isogeny_class()[0]]
     468            sage: [C.modular_symbol(use_eclib=True,normalize='none')(0) for C in E.isogeny_class(use_tuple=False)]
    469469            [1/4, 1/4, 1/4, 1/4, 1/4, 1/4, 1/4, 1/4]
    470470           
    471471        Currently, the interface for negative modular symbols in eclib is not yet written::
     
    603603            1
    604604
    605605            sage: E = EllipticCurve('15a1')
    606             sage: [C.modular_symbol(use_eclib=False, normalize='L_ratio')(0) for C in E.isogeny_class()[0]]
     606            sage: [C.modular_symbol(use_eclib=False, normalize='L_ratio')(0) for C in E.isogeny_class(use_tuple=False)]
    607607            [1/4, 1/8, 1/4, 1/2, 1/8, 1/16, 1/2, 1]
    608             sage: [C.modular_symbol(use_eclib=False, normalize='period')(0) for C in E.isogeny_class()[0]]
     608            sage: [C.modular_symbol(use_eclib=False, normalize='period')(0) for C in E.isogeny_class(use_tuple=False)]
    609609            [1/8, 1/16, 1/8, 1/4, 1/16, 1/32, 1/4, 1/2]
    610             sage: [C.modular_symbol(use_eclib=False, normalize='none')(0) for C in E.isogeny_class()[0]]
     610            sage: [C.modular_symbol(use_eclib=False, normalize='none')(0) for C in E.isogeny_class(use_tuple=False)]
    611611            [1, 1, 1, 1, 1, 1, 1, 1]
    612612       
    613613        """
  • sage/schemes/elliptic_curves/ell_rational_field.py

    diff --git a/sage/schemes/elliptic_curves/ell_rational_field.py b/sage/schemes/elliptic_curves/ell_rational_field.py
    a b  
    170170            Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Rational Field
    171171       
    172172        Constructor from `[a_4,a_6]` sets `a_1=a_2=a_3=0`::
    173        
     173
    174174            sage: EllipticCurve([4,5]).ainvs()
    175175            (0, 0, 0, 4, 5)
    176        
    177         Constructor from a label::
    178 
    179           sage: EllipticCurve('389a1')
    180           Elliptic Curve defined by y^2 + y = x^3 + x^2 - 2*x over Rational Field
     176
     177        Constructor from a Cremona label::
     178
     179            sage: EllipticCurve('389a1')
     180            Elliptic Curve defined by y^2 + y = x^3 + x^2 - 2*x over Rational Field
     181
     182        Constructor from an LMFDB label::
     183
     184            sage: EllipticCurve('462.f3')
     185            Elliptic Curve defined by y^2 + x*y = x^3 - 363*x + 1305 over Rational Field
    181186
    182187        TESTS:
    183188
     
    185190        label, we must be careful that the copied generators have the
    186191        right curve (see #10999: the following used not to work when
    187192        the large database was installed)::
    188        
     193
    189194            sage: E=EllipticCurve('389a1')
    190195            sage: [P.curve() is E for P in E.gens()]
    191196            [True, True]
     
    193198        """
    194199        if extra != None:   # possibility of two arguments (the first would be the field)
    195200            ainvs = extra
     201        self.__np = {}
     202        self.__gens = {}
     203        self.__rank = {}
     204        self.__regulator = {}
     205        self._isoclass = {}
    196206        if isinstance(ainvs, str):
    197207            label = ainvs
    198208            X = sage.databases.cremona.CremonaDatabase()[label]
    199209            EllipticCurve_number_field.__init__(self, Q, list(X.a_invariants()))
    200             self.__np = {}
    201             self.__gens = {}
    202             self.__rank = {}
    203             self.__regulator = {}
    204210            for attr in ['rank', 'torsion_order', 'cremona_label', 'conductor',
    205211                         'modular_degree', 'gens', 'regulator']:
    206212                s = "_EllipticCurve_rational_field__"+attr
     
    212218                            setattr(self, s, gens_dict)
    213219                    else:
    214220                        setattr(self, s, getattr(X, s))
     221            if hasattr(X,'_lmfdb_label'):
     222                self._lmfdb_label = X._lmfdb_label
    215223            return
    216224        EllipticCurve_number_field.__init__(self, Q, ainvs)
    217         self.__np = {}
    218         self.__gens = {}
    219         self.__rank = {}
    220         self.__regulator = {}
    221225        if self.base_ring() != Q:
    222226            raise TypeError, "Base field (=%s) must be the Rational Field."%self.base_ring()
    223227       
     
    37443748    ##########################################################
    37453749    # Isogeny class
    37463750    ##########################################################
    3747     def isogeny_class(self, algorithm="sage", verbose=False, fill_matrix=True, return_maps=False):
    3748         r""" 
     3751    def isogeny_class(self, algorithm="sage", verbose=False, fill_matrix=True, return_maps=False, order=None, use_tuple=True):
     3752        r"""
    37493753        Returns all curves over `\QQ` isogenous to this elliptic curve.
    3750        
     3754
    37513755        INPUT:
    3752        
     3756
     3757        -  ``algorithm`` - string: one of the following:
     3758
     3759           - "mwrank" - use the mwrank C++ library
     3760
     3761           - "database" - use the Cremona database (only works if
     3762             curve is isomorphic to a curve in the database)
     3763
     3764           - "sage" (default) - use the native Sage implementation.
     3765
    37533766        - ``verbose`` -- bool (default: False): ignored unless
    37543767          ``algorithm`` == "mwrank", in which case it is passed to the
    37553768          isogeny class function in mwrank.
    3756        
    3757         -  ``algorithm`` - string: one of the following:
    3758        
    3759            - "mwrank" - (default) use the mwrank C++ library
    3760        
    3761            - "database" - use the Cremona database (only works if
    3762              curve is isomorphic to a curve in the database)
    3763            
    3764            - "sage" - use the native Sage implementation. 
    3765              
     3769
    37663770        - ``fill_matrix`` -- bool (default: True): See below.
    37673771
    37683772        - ``return_maps`` -- bool (default: False): Ignored unless
     
    37713775          `(i,j)` entry is the isogeny from curve `i` to curve `j` if
    37723776          that isogeny has prime degree, else 0.
    37733777
    3774         OUTPUT:
    3775 
    3776         Tuple (list of curves, matrix of integers) or (list of curves,
     3778        - ``order`` -- None, string, or list of curves (default:
     3779          None): If not None then the curves in the class are
     3780          reordered after being computed.  Note that if the order is
     3781          None then the resulting order will depend on the algorithm.
     3782
     3783          - if ``order`` is "mwrank", "database", or "sage" then the
     3784            reordering is so that the order of curves lines up with
     3785            the order produced by that algorithm.
     3786
     3787          - if ``order`` is "lmfdb" then the curves are sorted
     3788            lexicographically by a-invariants.
     3789
     3790          - if ``order`` is a list of curves, then the curves in the
     3791            class are reordered to be isogenous with the specified
     3792            list of curves.
     3793
     3794        - ``use_tuple`` -- bool (default: True).  Controls the output
     3795          format: see below.  ``use_tuple==True`` is deprecated.
     3796
     3797        OUTPUT:
     3798
     3799        If ``use_tuple`` is False, returns a
     3800        :class:`sage.schemes.elliptic_curves.isogeny_class.IsogenyClass_EC_Rational`
     3801        instace.  This object models a list of minimal models (with
     3802        containment, index, etc based on isomorphism classes).  It
     3803        also has methods for computing the isogeny matrix and the list
     3804        of isogenies between curves in this class.
     3805
     3806        If ``use_tuple`` is True (deprecated), then returns a
     3807        tuple (isogeny class, matrix of integers) or (isogeny class,
    37773808        matrix of integers, matrix of isogenies). The sorted list of
    37783809        all curves isogenous to self is returned. If ``algorithm`` is
    37793810        not "database", the isogeny matrix is also returned, otherwise
     
    37863817        ``isogeny_graph()`` function).
    37873818
    37883819        .. note::
    3789        
    3790            The ordering depends on which algorithm is used.
    3791 
    3792         .. note::
    3793 
    3794             The curves returned are all standard minimal models.
    3795        
    3796         .. warning::
     3820
     3821            The curves in the isogeny class are all standard minimal models.
     3822
     3823        .. warning::
    37973824
    37983825            With ``algorithm`` "mwrank", the result is *not*
    37993826            provably correct, in the sense that when the numbers are
     
    38093836           degree.
    38103837
    38113838        EXAMPLES::
    3812        
    3813             sage: I, A = EllipticCurve('37b').isogeny_class('mwrank') 
    3814             sage: I   # randomly ordered
    3815             [Elliptic Curve defined by y^2 + y = x^3 + x^2 - 23*x - 50 over Rational Field,
    3816              Elliptic Curve defined by y^2 + y = x^3 + x^2 - 1873*x - 31833 over Rational Field,
    3817              Elliptic Curve defined by y^2 + y = x^3 + x^2 - 3*x +1 over Rational Field]
    3818             sage: A
    3819             [1 3 3]
    3820             [3 1 9]
    3821             [3 9 1]
    3822        
     3839
     3840            sage: isocls = EllipticCurve('37b').isogeny_class('mwrank',order="lmfdb",use_tuple=False)
     3841            sage: isocls
     3842            Elliptic curve isogeny class 37b
     3843            sage: isocls.curves
     3844            (Elliptic Curve defined by y^2 + y = x^3 + x^2 - 1873*x - 31833 over Rational Field,
     3845             Elliptic Curve defined by y^2 + y = x^3 + x^2 - 23*x - 50 over Rational Field,
     3846             Elliptic Curve defined by y^2 + y = x^3 + x^2 - 3*x + 1 over Rational Field)
     3847            sage: isocls.matrix()
     3848            [1 3 9]
     3849            [3 1 3]
     3850            [9 3 1]
     3851
    38233852        ::
    3824        
    3825             sage: I, _ = EllipticCurve('37b').isogeny_class('database'); I
    3826             [Elliptic Curve defined by y^2 + y = x^3 + x^2 - 1873*x - 31833 over Rational Field,
     3853
     3854            sage: isocls = EllipticCurve('37b').isogeny_class('database', order="lmfdb", use_tuple=False); isocls.curves
     3855            (Elliptic Curve defined by y^2 + y = x^3 + x^2 - 1873*x - 31833 over Rational Field,
    38273856             Elliptic Curve defined by y^2 + y = x^3 + x^2 - 23*x - 50 over Rational Field,
    3828              Elliptic Curve defined by y^2 + y = x^3 + x^2 - 3*x + 1 over Rational Field]
    3829        
     3857             Elliptic Curve defined by y^2 + y = x^3 + x^2 - 3*x + 1 over Rational Field)
     3858
    38303859        This is an example of a curve with a `37`-isogeny::
    3831        
     3860
    38323861            sage: E = EllipticCurve([1,1,1,-8,6])
    3833             sage: E.isogeny_class ()
    3834             ([Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 8*x + 6 over Rational Field, Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 208083*x - 36621194 over Rational Field], [ 1 37]
    3835             [37  1])
    3836        
     3862            sage: isocls = E.isogeny_class(use_tuple=False); isocls
     3863            Isogeny class of Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 8*x + 6 over Rational Field
     3864            sage: isocls.matrix()
     3865            [ 1 37]
     3866            [37  1]
     3867            sage: print "\n".join([repr(E) for E in isocls.curves])
     3868            Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 8*x + 6 over Rational Field
     3869            Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 208083*x - 36621194 over Rational Field
     3870
    38373871        This curve had numerous `2`-isogenies::
    3838        
     3872
    38393873            sage: e=EllipticCurve([1,0,0,-39,90])
    3840             sage: e.isogeny_class ()
    3841             ([Elliptic Curve defined by y^2 + x*y = x^3 - 39*x + 90 over Rational Field, Elliptic Curve defined by y^2 + x*y = x^3 - 4*x - 1 over Rational Field, Elliptic Curve defined by y^2 + x*y = x^3 + x over Rational Field, Elliptic Curve defined by y^2 + x*y = x^3 - 49*x - 136 over Rational Field, Elliptic Curve defined by y^2 + x*y = x^3 - 34*x - 217 over Rational Field, Elliptic Curve defined by y^2 + x*y = x^3 - 784*x - 8515 over Rational Field], [1 2 4 4 8 8]
     3874            sage: isocls = e.isogeny_class(use_tuple=False); isocls.matrix()
     3875            [1 2 4 4 8 8]
    38423876            [2 1 2 2 4 4]
    38433877            [4 2 1 4 8 8]
    38443878            [4 2 4 1 2 2]
    38453879            [8 4 8 2 1 4]
    3846             [8 4 8 2 4 1])
    3847        
     3880            [8 4 8 2 4 1]
     3881
    38483882        See http://math.harvard.edu/~elkies/nature.html for more
    38493883        interesting examples of isogeny structures.
    38503884
    38513885        ::
    38523886
    38533887            sage: E = EllipticCurve(j = -262537412640768000)
    3854             sage: E.isogeny_class(algorithm="sage")
    3855             ([Elliptic Curve defined by y^2 + y = x^3 - 2174420*x + 1234136692 over Rational Field, Elliptic Curve defined by y^2 + y = x^3 - 57772164980*x - 5344733777551611 over Rational Field], [  1 163]
    3856             [163   1])
     3888            sage: isocls = E.isogeny_class(algorithm="sage", use_tuple=False); isocls.matrix()
     3889            [  1 163]
     3890            [163   1]
     3891            sage: print "\n".join([repr(C) for C in isocls.curves])
     3892            Elliptic Curve defined by y^2 + y = x^3 - 2174420*x + 1234136692 over Rational Field
     3893            Elliptic Curve defined by y^2 + y = x^3 - 57772164980*x - 5344733777551611 over Rational Field
    38573894
    38583895        For large examples, the "mwrank" algorithm may fail to find
    38593896        some isogenies since it works in fixed precision::
    38603897
    38613898            sage: E1 = E.quadratic_twist(6584935282)
    3862             sage: E1.isogeny_class(algorithm="mwrank")
    3863             ([Elliptic Curve defined by y^2 = x^3 - 94285835957031797981376080*x + 352385311612420041387338054224547830898 over Rational Field],
    3864             [1])
    3865 
    3866         Since the result is cached, this looks no different::   
    3867 
    3868             sage: E1.isogeny_class(algorithm="sage")
    3869             ([Elliptic Curve defined by y^2 = x^3 - 94285835957031797981376080*x + 352385311612420041387338054224547830898 over Rational Field],
    3870             [1])
    3871 
    3872         But resetting the curve shows that the native algorithm is better::
    3873 
    3874              sage: E1 = E.quadratic_twist(6584935282)
    3875              sage: E1.isogeny_class(algorithm="sage")
    3876              ([Elliptic Curve defined by y^2 = x^3 - 94285835957031797981376080*x + 352385311612420041387338054224547830898 over Rational Field,
    3877              Elliptic Curve defined by y^2 = x^3 - 2505080375542377840567181069520*x - 1526091631109553256978090116318797845018020806 over Rational Field],
    3878              [  1 163]
    3879              [163   1])
     3899            sage: E1.isogeny_class(algorithm="mwrank", use_tuple=False).matrix()
     3900            [1]
     3901
     3902        Using algorithm="sage" gives another isogeny (even though
     3903        results are cached: the cache depends on the algorithm)::
     3904
     3905            sage: isocls = E1.isogeny_class(algorithm="sage", use_tuple=False); isocls.matrix()
     3906            [  1 163]
     3907            [163   1]
    38803908            sage: E1.conductor()
    38813909            18433092966712063653330496
    38823910
    3883         ::   
    3884 
    3885             sage: E = EllipticCurve('14a1')                 
    3886             sage: E.isogeny_class(algorithm="sage")           
    3887             ([Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x - 6 over Rational Field, Elliptic Curve defined by y^2 + x*y + y = x^3 - 36*x - 70 over Rational Field, Elliptic Curve defined by y^2 + x*y + y = x^3 - x over Rational Field, Elliptic Curve defined by y^2 + x*y + y = x^3 - 171*x - 874 over Rational Field, Elliptic Curve defined by y^2 + x*y + y = x^3 - 11*x + 12 over Rational Field, Elliptic Curve defined by y^2 + x*y + y = x^3 - 2731*x - 55146 over Rational Field], [ 1  2  3  3  6  6]
     3911        ::
     3912
     3913            sage: E = EllipticCurve('14a1')
     3914            sage: isocls = E.isogeny_class(algorithm="sage", use_tuple=False); isocls.matrix()
     3915            [ 1  2  3  3  6  6]
    38883916            [ 2  1  6  6  3  3]
    38893917            [ 3  6  1  9  2 18]
    38903918            [ 3  6  9  1 18  2]
    38913919            [ 6  3  2 18  1  9]
    3892             [ 6  3 18  2  9  1])
     3920            [ 6  3 18  2  9  1]
     3921            sage: print "\n".join([repr(C) for C in isocls.curves])
     3922            Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x - 6 over Rational Field
     3923            Elliptic Curve defined by y^2 + x*y + y = x^3 - 36*x - 70 over Rational Field
     3924            Elliptic Curve defined by y^2 + x*y + y = x^3 - x over Rational Field
     3925            Elliptic Curve defined by y^2 + x*y + y = x^3 - 171*x - 874 over Rational Field
     3926            Elliptic Curve defined by y^2 + x*y + y = x^3 - 11*x + 12 over Rational Field
     3927            Elliptic Curve defined by y^2 + x*y + y = x^3 - 2731*x - 55146 over Rational Field
     3928            sage: isocls2 = isocls.reorder('lmfdb'); isocls2.matrix()
     3929            [ 1  2  3  9 18  6]
     3930            [ 2  1  6 18  9  3]
     3931            [ 3  6  1  3  6  2]
     3932            [ 9 18  3  1  2  6]
     3933            [18  9  6  2  1  3]
     3934            [ 6  3  2  6  3  1]
     3935            sage: print "\n".join([repr(C) for C in isocls2.curves])
     3936            Elliptic Curve defined by y^2 + x*y + y = x^3 - 2731*x - 55146 over Rational Field
     3937            Elliptic Curve defined by y^2 + x*y + y = x^3 - 171*x - 874 over Rational Field
     3938            Elliptic Curve defined by y^2 + x*y + y = x^3 - 36*x - 70 over Rational Field
     3939            Elliptic Curve defined by y^2 + x*y + y = x^3 - 11*x + 12 over Rational Field
     3940            Elliptic Curve defined by y^2 + x*y + y = x^3 - x over Rational Field
     3941            Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x - 6 over Rational Field
     3942
     3943        ::
    38933944
    38943945            sage: E = EllipticCurve('11a1')
    3895             sage: E.isogeny_class(algorithm="sage", return_maps=True)
    3896             ([Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field, Elliptic Curve defined by y^2 + y = x^3 - x^2 over Rational Field, Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field], [ 1  5  5]
     3946            sage: isocls = E.isogeny_class(algorithm="sage", use_tuple=False); isocls.matrix()
     3947            [ 1  5  5]
    38973948            [ 5  1 25]
    3898             [ 5 25  1], [[0, Isogeny of degree 5 from Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field to Elliptic Curve defined by y^2 + y = x^3 - x^2 over Rational Field, Isogeny of degree 5 from Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field], [Isogeny of degree 5 from Elliptic Curve defined by y^2 + y = x^3 - x^2 over Rational Field to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field, 0, 0], [Isogeny of degree 5 from Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field, 0, 0]])
    3899 
    3900         """
    3901         from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix, unfill_isogeny_matrix
    3902         from sage.matrix.all import MatrixSpace
    3903 
     3949            [ 5 25  1]
     3950            sage: f = isocls.isogenies()[0][1]; f.kernel_polynomial()
     3951            x^2 + x - 29/5
     3952        """
    39043953        try:
    3905             curves, M = self.__isogeny_class
    3906             if fill_matrix and M[0,0]==0:
    3907                 M = fill_isogeny_matrix(M)
    3908             elif not fill_matrix and M[0,0]==1:
    3909                 M = unfill_isogeny_matrix(M)
    3910             return curves, M
    3911         except AttributeError:
    3912             pass
    3913 
    3914         if algorithm == "mwrank":
    3915             try:
    3916                 E = self.mwrank_curve()
    3917             except ValueError:
    3918                 E = self.minimal_model().mwrank_curve()
    3919             I, A = E.isogeny_class(verbose=verbose)
    3920             M = matrix.MatrixSpace(rings.IntegerRing(), len(A))(A)
    3921             curves = [constructor.EllipticCurve(ainvs) for ainvs in I]
    3922        
    3923         elif algorithm == "database":
    3924            
    3925             try:
    3926                 label = self.cremona_label(space=False)
    3927             except RuntimeError:
    3928                 raise RuntimeError, "unable to to find %s in the database"%self
    3929             db = sage.databases.cremona.CremonaDatabase()
    3930             curves = db.isogeny_class(label)
    3931             if len(curves) == 0:
    3932                 raise RuntimeError, "unable to to find %s in the database"%self
    3933             curves.sort()
    3934             M = None
    3935 
    3936         elif algorithm == "sage":
    3937 
    3938             curves = [self.minimal_model()]
    3939             ijl_triples = []
    3940             l_list = None
    3941 
    3942             i = 0
    3943             while i<len(curves):
    3944                 E = curves[i]
    3945                 isogs = E.isogenies_prime_degree(l_list)
    3946                 for phi in isogs:
    3947                     Edash = phi.codomain()
    3948                     l = phi.degree()
    3949                     # look to see if Edash is new.  Note that the
    3950                     # curves returned by isogenies_prime_degree() are
    3951                     # standard minimal models, so it suffices to check
    3952                     # equality rather than isomorphism here.
    3953                     try:
    3954                         j = curves.index(Edash)
    3955                     except ValueError:
    3956                         j = len(curves)
    3957                         curves.append(Edash)
    3958                     ijl_triples.append((i,j,l,phi))
    3959                 if l_list is None:
    3960                     l_list = [l for l in Set([ZZ(f.degree()) for f in isogs])]
    3961                 i = i+1
    3962 
    3963             ncurves = len(curves)
    3964             M = MatrixSpace(IntegerRing(),ncurves)(0)
    3965             maps = [[0]*ncurves for i in range(ncurves)]
    3966             for i,j,l,phi in ijl_triples:
    3967                 M[i,j] = l
    3968                 maps[i][j]=phi
    3969 
     3954            isoclass = self._isoclass[algorithm]
     3955        except KeyError:
     3956            from sage.schemes.elliptic_curves.isogeny_class import IsogenyClass_EC_Rational
     3957            if hasattr(self, "_lmfdb_label") and self._lmfdb_label:
     3958                label = self._lmfdb_label[:-1]
     3959            elif hasattr(self, "_EllipticCurve_rational_field__cremona_label") and self.__cremona_label:
     3960                label = self.__cremona_label[:-1]
     3961            else:
     3962                label = None
     3963            isoclass = IsogenyClass_EC_Rational(self, algorithm, verbose, label)
     3964            self._isoclass[algorithm] = isoclass
     3965        if order:
     3966            isoclass = isoclass.reorder(order)
     3967        if use_tuple:
     3968            from sage.misc.misc import deprecation
     3969            deprecation("""For elliptic curves E over Q, isogeny_class(use_tuple=False) returns a class
     3970that has methods producing the isogeny graph and list of lists of isogenies.
     3971use_tuple=True (currently default) is deprecated.""", "Sage Version 5.0")
     3972            # After a year or so we should switch the default to use_tuple=False as the default and deprecate
     3973            # the keyword argument
     3974            if return_maps:
     3975                return isoclass, isoclass.matrix(fill_matrix), isoclass.isogenies()
     3976            else:
     3977                return isoclass, isoclass.matrix(fill_matrix)
    39703978        else:
    3971             raise ValueError, "unknown algorithm '%s'"%algorithm
    3972 
    3973         if fill_matrix and not M is None:
    3974             from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix
    3975             M = fill_isogeny_matrix(M)
    3976 
    3977         if not algorithm=="database":
    3978             self.__isogeny_class = (curves, M)
    3979         if return_maps:
    3980             return curves, M, maps
    3981         return curves, M
     3979            return isoclass
    39823980
    39833981    def isogenies_prime_degree(self, l=None):
    39843982        r"""
     
    41264124        if not proof:
    41274125            return True
    41284126        else:
    4129             return  E2 in E1.isogeny_class()[0]
     4127            return  E2 in E1.isogeny_class(use_tuple=False).curves
    41304128 
    41314129    def isogeny_degree(self, other):
    41324130        """
     
    42374235        if not E1.is_isogenous(E2, proof=False):
    42384236            return Integer(0)
    42394237
    4240         cl, mat = E1.isogeny_class()
     4238        isocls = E1.isogeny_class(use_tuple=False)
    42414239        try:
    4242             return mat[0,cl.index(E2)]
     4240            return isocls.matrix(fill=True)[0,isocls.index(E2)]
    42434241        except ValueError:
    42444242            return Integer(0)
    42454243
     
    43094307        if optimal_label == label: return self
    43104308        return constructor.EllipticCurve(optimal_label)
    43114309   
    4312     def isogeny_graph(self):
     4310    def isogeny_graph(self, order=None):
    43134311        r"""
    43144312        Returns a graph representing the isogeny class of this elliptic
    43154313        curve, where the vertices are isogenous curves over
    43164314        `\QQ` and the edges are prime degree isogenies
    43174315
     4316        .. note:
     4317
     4318            The vertices are labeled 1 to n rather than 0 to n-1 to
     4319            correspond to LMFDB and Cremona labels.
     4320
    43184321        EXAMPLES::
    43194322       
    43204323            sage: LL = []
     
    43364339            sage: G = E.isogeny_graph()
    43374340            sage: for v in G: print v, G.get_vertex(v)
    43384341            ...
    4339             0 Elliptic Curve defined by y^2 + x*y  = x^3 - 110*x + 435 over Rational Field
    4340             1 Elliptic Curve defined by y^2 + x*y  = x^3 - 115*x + 392 over Rational Field
    4341             2 Elliptic Curve defined by y^2 + x*y  = x^3 + 210*x + 2277 over Rational Field
    4342             3 Elliptic Curve defined by y^2 + x*y  = x^3 - 520*x - 4225 over Rational Field
    4343             4 Elliptic Curve defined by y^2 + x*y  = x^3 + 605*x - 19750 over Rational Field
    4344             5 Elliptic Curve defined by y^2 + x*y  = x^3 - 8125*x - 282568 over Rational Field
    4345             6 Elliptic Curve defined by y^2 + x*y  = x^3 - 7930*x - 296725 over Rational Field
    4346             7 Elliptic Curve defined by y^2 + x*y  = x^3 - 130000*x - 18051943 over Rational Field
     4342            1 Elliptic Curve defined by y^2 + x*y  = x^3 - 110*x + 435 over Rational Field
     4343            2 Elliptic Curve defined by y^2 + x*y  = x^3 - 115*x + 392 over Rational Field
     4344            3 Elliptic Curve defined by y^2 + x*y  = x^3 + 210*x + 2277 over Rational Field
     4345            4 Elliptic Curve defined by y^2 + x*y  = x^3 - 520*x - 4225 over Rational Field
     4346            5 Elliptic Curve defined by y^2 + x*y  = x^3 + 605*x - 19750 over Rational Field
     4347            6 Elliptic Curve defined by y^2 + x*y  = x^3 - 8125*x - 282568 over Rational Field
     4348            7 Elliptic Curve defined by y^2 + x*y  = x^3 - 7930*x - 296725 over Rational Field
     4349            8 Elliptic Curve defined by y^2 + x*y  = x^3 - 130000*x - 18051943 over Rational Field
    43474350            sage: G.plot(edge_labels=True)
    43484351        """
    4349         from sage.graphs.graph import Graph
    4350         L, M = self.isogeny_class(algorithm='mwrank', fill_matrix=False)
    4351         G = Graph(M, format='weighted_adjacency_matrix')
    4352         G.set_vertices(dict([(v,L[v]) for v in G.vertices()]))
    4353         return G
     4352        return self.isogeny_class(algorithm='mwrank', order=order, use_tuple=False).graph()
    43544353
    43554354    def manin_constant(self):
    43564355        r"""
     
    44234422        EXAMPLES::
    44244423
    44254424            sage: EllipticCurve('11a1')._shortest_paths()
    4426             ([Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field,
     4425            ((Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field,
    44274426              Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field,
    4428               Elliptic Curve defined by y^2 + y = x^3 - x^2 over Rational Field],
     4427              Elliptic Curve defined by y^2 + y = x^3 - x^2 over Rational Field),
    44294428             {0: 0, 1: 5, 2: 5})
    44304429            sage: EllipticCurve('11a2')._shortest_paths()
    4431             ([Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field,
     4430            ((Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field,
    44324431              Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field,
    4433               Elliptic Curve defined by y^2 + y = x^3 - x^2 over Rational Field],
     4432              Elliptic Curve defined by y^2 + y = x^3 - x^2 over Rational Field),
    44344433             {0: 0, 1: 5, 2: 25})
    44354434        """
    44364435        from sage.graphs.graph import Graph
    4437         L, M = self.isogeny_class(algorithm='mwrank')
    4438         M = M.change_ring(rings.RR)
     4436        isocls = self.isogeny_class(algorithm='mwrank',use_tuple=False)
     4437        M = isocls.matrix(fill=True).change_ring(rings.RR)
    44394438        # see trac #4889 for nebulous M.list() --> M.entries() change...
    44404439        # Take logs here since shortest path minimizes the *sum* of the weights -- not the product.
    44414440        M = M.parent()([a.log() if a else 0 for a in M.list()])
    44424441        G = Graph(M, format='weighted_adjacency_matrix')
    4443         G.set_vertices(dict([(v,L[v]) for v in G.vertices()]))
     4442        G.set_vertices(dict([(v,isocls[v]) for v in G.vertices()]))
    44444443        v = G.shortest_path_lengths(0, by_weight=True, weight_sums=True)
    44454444        # Now exponentiate and round to get degrees of isogenies
    44464445        v = dict([(i, j.exp().round() if j else 0) for i,j in v.iteritems()])
    4447         return L, v
     4446        return isocls.curves, v
    44484447
    44494448    def _multiple_of_degree_of_isogeny_to_optimal_curve(self):
    44504449        r"""
  • sage/schemes/elliptic_curves/gal_reps.py

    diff --git a/sage/schemes/elliptic_curves/gal_reps.py b/sage/schemes/elliptic_curves/gal_reps.py
    a b  
    423423        if t == True:
    424424            self.__is_reducible[p] = False
    425425            return False  # definitely not reducible
    426         isogeny_matrix = self.E.isogeny_class()[ 1 ]
     426        isogeny_matrix = self.E.isogeny_class(use_tuple=False).matrix(fill=True)
    427427        v = isogeny_matrix.row(0) # first row
    428428        for a in v:
    429429            if a != 0 and a % p == 0:
     
    474474            return self.__reducible_primes
    475475        except AttributeError:
    476476            pass
    477         C, I = self.E.isogeny_class(algorithm='sage')
    478         X = set(I.list())
     477        isocls = self.E.isogeny_class(algorithm='sage', use_tuple=False)
     478        X = set(isocls.matrix().list())
    479479        R = [p for p in X if arith.is_prime(p)]
    480480        self.__reducible_primes = R
    481481        return R
  • new file sage/schemes/elliptic_curves/isogeny_class.py

    diff --git a/sage/schemes/elliptic_curves/isogeny_class.py b/sage/schemes/elliptic_curves/isogeny_class.py
    new file mode 100644
    - +  
     1"""
     2This file defines a class for an isogeny class of an elliptic curve.
     3
     4AUTHORS:
     5
     6David Roe (2012-03-29) -- initial version.
     7"""
     8
     9##############################################################################
     10#       Copyright (C) 2012 David Roe <roed.math@gmail.com>
     11#                          William Stein <wstein@gmail.com>
     12#
     13#  Distributed under the terms of the GNU General Public License (GPL)
     14#
     15#    This code is distributed in the hope that it will be useful,
     16#    but WITHOUT ANY WARRANTY; without even the implied warranty of
     17#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     18#    General Public License for more details.
     19#
     20#  The full text of the GPL is available at:
     21#
     22#                  http://www.gnu.org/licenses/
     23##############################################################################
     24
     25from sage.structure.sage_object import SageObject
     26from sage.misc.lazy_attribute import lazy_attribute
     27import constructor
     28import sage.databases.cremona
     29from sage.rings.all import ZZ
     30from sage.misc.cachefunc import cached_method
     31from sage.misc.abstract_method import abstract_method
     32from sage.schemes.elliptic_curves.ell_generic import EllipticCurve_generic
     33
     34class IsogenyClass_EC(SageObject):
     35    """
     36    Isogeny class of an elliptic curve.
     37
     38    The current implementation chooses a curve from each isomorphism
     39    class in the isogeny class, since over Q there is a unique minimal
     40    model in each isomorphism class.
     41
     42    EXAMPLES::
     43
     44       
     45    """
     46    def __init__(self, E, label=None, empty=False):
     47        """
     48        Over Q we use curves since minimal models exist and there is a canonical choice of one.
     49
     50        INPUT:
     51
     52        - ``label`` -- string or None, a Cremona or LMFDB label, used
     53          in printing
     54
     55        EXAMPLES::
     56
     57            sage: cls = EllipticCurve('1011b1').isogeny_class(use_tuple=False)
     58            sage: print "\n".join([repr(E) for E in cls.curves])
     59            Elliptic Curve defined by y^2 + x*y = x^3 - 8*x - 9 over Rational Field
     60            Elliptic Curve defined by y^2 + x*y = x^3 - 23*x + 30 over Rational Field
     61        """
     62        self.E = E
     63        self._label = label
     64        if not empty:
     65            self._compute()
     66
     67    def __len__(self):
     68        """
     69        The length is just the number of curves in the class.
     70
     71        EXAMPLES::
     72
     73            sage: E = EllipticCurve('15a')
     74            sage: len(E.isogeny_class(use_tuple=False)) # indirect doctest
     75            8
     76        """
     77        return len(self.curves)
     78
     79    def __iter__(self):
     80        """
     81        Iterator over curves in the class.
     82
     83        EXAMPLES::
     84
     85            sage: E = EllipticCurve('15a')
     86            sage: all(C.conductor() == 15 for C in E.isogeny_class(use_tuple=False)) # indirect doctest
     87            True
     88        """
     89        return iter(self.curves)
     90
     91    def __getitem__(self, i):
     92        """
     93        Gets the `i`th curve in the class.
     94
     95        EXAMPLES::
     96
     97            sage: E = EllipticCurve('990j1')
     98            sage: iso = E.isogeny_class(order="lmfdb",use_tuple=False) # orders lexicographically on a-invariants
     99            sage: iso[2] == E # indirect doctest
     100            True
     101        """
     102        return self.curves[i]
     103
     104    def index(self, C):
     105        """
     106        Returns the index of a curve in this class.
     107
     108        INPUT:
     109
     110        - ``C`` -- an elliptic curve in this isogeny class.
     111
     112        OUTPUT:
     113
     114        - ``i`` -- an integer so that the ``i``th curve in the class
     115          is isomorphic to ``C``
     116
     117        EXAMPLES::
     118
     119            sage: E = EllipticCurve('990j1')
     120            sage: iso = E.isogeny_class(order="lmfdb", use_tuple=False) # orders lexicographically on a-invariants
     121            sage: iso.index(E.short_weierstrass_model())
     122            2
     123        """
     124        # This will need updating once we start talking about curves over more general number fields
     125        if not isinstance(C, EllipticCurve_generic):
     126            raise ValueError("x not in isogeny class")
     127        return self.curves.index(C.minimal_model())
     128
     129    def __cmp__(self, other):
     130        """
     131        Returns 0 if self and other are the same isogeny class.
     132
     133        If they are different, compares the sorted underlying lists of
     134        curves.
     135
     136        Note that two isogeny classes with different orderings will
     137        compare as the same.  If you want to include the ordering,
     138        just compare the list of curves.
     139
     140        EXAMPLES::
     141
     142            sage: E = EllipticCurve('990j1')
     143            sage: EE = EllipticCurve('990j4')
     144            sage: E.isogeny_class(use_tuple=False) == EE.isogeny_class(use_tuple=False) # indirect doctest
     145            True
     146        """
     147        # This will need updating once we start talking about curves over more general number fields
     148        if isinstance(other, IsogenyClass_EC):
     149            return cmp(sorted(self.curves), sorted(other.curves))
     150        return cmp(type(self), type(other))
     151
     152    def __hash__(self):
     153        """
     154        Hash is based on the a-invariants of the sorted list of
     155        minimal models.
     156
     157        EXAMPLES::
     158
     159            sage: E = EllipticCurve('990j1')
     160            sage: C = E.isogeny_class(use_tuple=False)
     161            sage: hash(C) == hash(tuple(sorted([curve.a_invariants() for curve in C.curves]))) # indirect doctest
     162            True
     163        """
     164        try:
     165            return self._hash
     166        except AttributeError:
     167            self._hash = hash(tuple(sorted([E.a_invariants() for E in self.curves])))
     168            return self._hash
     169
     170    def _repr_(self):
     171        """
     172        The string representation depends on whether an LMFDB or
     173        Cremona label for the curve is known when this isogeny class
     174        is constructed.
     175
     176        EXAMPLES:
     177
     178        If the curve is constructed from an LMFDB label then that
     179        label is used::
     180
     181            sage: E = EllipticCurve('462.f3')
     182            sage: E.isogeny_class(use_tuple=False) # indirect doctest
     183            Elliptic curve isogeny class 462.f
     184
     185        If the curve is constructed from a Cremona label then that
     186        label is used::
     187
     188            sage: E = EllipticCurve('990j1')
     189            sage: E.isogeny_class(use_tuple=False)
     190            Elliptic curve isogeny class 990j
     191
     192        Otherwise the representation is determined from the first
     193        curve in the class::
     194
     195            sage: E = EllipticCurve([1,2,3,4,5])
     196            sage: E.isogeny_class(use_tuple=False)
     197            Isogeny class of Elliptic Curve defined by y^2 + x*y = x^3 - x^2 + 4*x + 3 over Rational Field
     198        """
     199        if self._label:
     200            return "Elliptic curve isogeny class %s"%(self._label)
     201        else:
     202            return "Isogeny class of %r"%(self.curves[0])
     203
     204    def __contains__(self, x):
     205        """
     206        INPUT:
     207
     208        - ``x`` -- a Python object.
     209
     210        OUTPUT:
     211
     212        - boolean -- True iff ``x`` is an elliptic curve in this isogeny class.
     213
     214        EXAMPLES::
     215
     216            sage: cls = EllipticCurve('15a3').isogeny_class(use_tuple=False)
     217            sage: E = EllipticCurve('15a7'); E in cls
     218            True
     219            sage: E.short_weierstrass_model() in cls
     220            True
     221        """
     222        if not isinstance(x, EllipticCurve_generic):
     223            return False
     224        return x.minimal_model() in self.curves
     225
     226    @cached_method
     227    def matrix(self, fill=True):
     228        """
     229        Returns the matrix whose entries give the minimal degrees of
     230        isogenies between curves in this class.
     231
     232        INPUT:
     233
     234        - ``fill`` -- boolean (default True).  If False then the
     235          matrix will contain only zeros and prime entries; if True it
     236          will fill in the other degrees.
     237
     238        EXAMPLES::
     239
     240            sage: isocls = EllipticCurve('15a3').isogeny_class(use_tuple=False)
     241            sage: isocls.matrix()
     242            [ 1  2  2  2  4  4  8  8]
     243            [ 2  1  4  4  8  8 16 16]
     244            [ 2  4  1  4  8  8 16 16]
     245            [ 2  4  4  1  2  2  4  4]
     246            [ 4  8  8  2  1  4  8  8]
     247            [ 4  8  8  2  4  1  2  2]
     248            [ 8 16 16  4  8  2  1  4]
     249            [ 8 16 16  4  8  2  4  1]
     250            sage: isocls.matrix(fill=False)
     251            [0 2 2 2 0 0 0 0]
     252            [2 0 0 0 0 0 0 0]
     253            [2 0 0 0 0 0 0 0]
     254            [2 0 0 0 2 2 0 0]
     255            [0 0 0 2 0 0 0 0]
     256            [0 0 0 2 0 0 2 2]
     257            [0 0 0 0 0 2 0 0]
     258            [0 0 0 0 0 2 0 0]
     259        """
     260        if self._mat is None:
     261            self._compute_matrix()
     262        mat = self._mat
     263        if fill and mat[0,0] == 0:
     264            from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix
     265            mat = fill_isogeny_matrix(mat)
     266        if not fill and mat[0,0] == 1:
     267            from sage.schemes.elliptic_curves.ell_curve_isogeny import unfill_isogeny_matrix
     268            mat = unfill_isogeny_matrix(mat)
     269        return mat
     270
     271    @cached_method
     272    def isogenies(self, fill=False):
     273        """
     274        Returns a list of lists of isogenies and 0s, corresponding to the entries of :meth:`matrix`
     275
     276        INPUT:
     277
     278        - ``fill`` -- boolean (default False).  Whether to only return
     279          prime degree isogenies.  Currently only implemented for
     280          ``fill=False``.
     281
     282        OUTPUT:
     283
     284        - a list of lists, where the ``j``th entry of the ``i``th list
     285          is either zero or a prime degree isogeny from ``i``th curve
     286          in this class to the ``j``th curve.
     287
     288        WARNING:
     289
     290        - The domains and codmains of the isogenies will have the same
     291          Weierstrass equation as the curves in this class, but they
     292          may not be identical python objects in the current
     293          implementation.
     294
     295        EXAMPLES::
     296
     297            sage: isocls = EllipticCurve('15a3').isogeny_class(use_tuple=False)
     298            sage: f = isocls.isogenies()[0][1]; f
     299            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
     300            sage: f.domain() == isocls.curves[0] and f.codomain() == isocls.curves[1]
     301            True
     302        """
     303        if fill:
     304            raise NotImplementedError
     305        isogenies = self._maps
     306        if isogenies is None:
     307            self._compute_isogenies()
     308            isogenies = self._maps
     309        return isogenies
     310
     311    @cached_method
     312    def graph(self):
     313        """
     314        Returns a graph whose vertices correspond to curves in this class, and whose edges correspond to prime degree isogenies.
     315
     316        .. note:
     317
     318            There are only finitely many possible isogeny graphs for
     319            curves over Q [M78].  This function tries to lay out the graph
     320            nicely by special casing each isogeny graph.
     321
     322        .. note:
     323
     324            The vertices are labeled 1 to n rather than 0 to n-1 to
     325            correspond to LMFDB and Cremona labels.
     326
     327        EXAMPLES::
     328
     329            sage: isocls = EllipticCurve('15a3').isogeny_class(use_tuple=False)
     330            sage: G = isocls.graph()
     331            sage: sorted(G._pos.items())
     332            [(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])]
     333
     334        REFERENCES:
     335
     336        .. [M78] B. Mazur.  Rational Isogenies of Prime Degree.
     337          *Inventiones mathematicae* 44,129-162 (1978).
     338        """
     339        from sage.graphs.graph import Graph
     340        M = self.matrix(fill = False)
     341        n = M.nrows() # = M.ncols()
     342        G = Graph(M, format='weighted_adjacency_matrix')
     343        N = self.matrix(fill = True)
     344        D = dict([(v,self.curves[v]) for v in G.vertices()])
     345        # The maximum degree classifies the shape of the isogeny
     346        # graph, though the number of vertices is often enough.
     347        # This only holds over Q, so this code will need to change
     348        # once other isogeny classes are implemented.
     349        if n == 1:
     350            # one vertex
     351            pass
     352        elif n == 2:
     353            # one edge, two vertices.  We align horizontally and put
     354            # the lower number on the left vertex.
     355            G.set_pos(pos={0:[-0.5,0],1:[0.5,0]})
     356        else:
     357            maxdegree = max(max(N))
     358            if n == 3:
     359                # o--o--o
     360                centervert = [i for i in range(3) if max(N.row(i)) < maxdegree][0]
     361                other = [i for i in range(3) if i != centervert]
     362                G.set_pos(pos={centervert:[0,0],other[0]:[-1,0],other[1]:[1,0]})
     363            elif maxdegree == 4:
     364                # o--o<8
     365                centervert = [i for i in range(4) if max(N.row(i)) < maxdegree][0]
     366                other = [i for i in range(4) if i != centervert]
     367                G.set_pos(pos={centervert:[0,0],other[0]:[0,1],other[1]:[-0.8660254,-0.5],other[2]:[0.8660254,-0.5]})
     368            elif maxdegree == 27:
     369                # o--o--o--o
     370                centers = [i for i in range(4) if list(N.row(i)).count(3) == 2]
     371                left = [j for j in range(4) if N[centers[0],j] == 3 and j not in centers][0]
     372                right = [j for j in range(4) if N[centers[1],j] == 3 and j not in centers][0]
     373                G.set_pos(pos={left:[-1.5,0],centers[0]:[-0.5,0],centers[1]:[0.5,0],right:[1.5,0]})
     374            elif n == 4:
     375                # square
     376                opp = [i for i in range(1,4) if not N[0,i].is_prime()][0]
     377                other = [i for i in range(1,4) if i != opp]
     378                G.set_pos(pos={0:[1,1],other[0]:[-1,1],opp:[-1,-1],other[1]:[1,-1]})
     379            elif maxdegree == 8:
     380                # 8>o--o<8
     381                centers = [i for i in range(6) if list(N.row(i)).count(2) == 3]
     382                left = [j for j in range(6) if N[centers[0],j] == 2 and j not in centers]
     383                right = [j for j in range(6) if N[centers[1],j] == 2 and j not in centers]
     384                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]})
     385            elif maxdegree == 18:
     386                # two squares joined on an edge
     387                centers = [i for i in range(6) if list(N.row(i)).count(3) == 2]
     388                top = [j for j in range(6) if N[centers[0],j] == 3]
     389                bl = [j for j in range(6) if N[top[0],j] == 2][0]
     390                br = [j for j in range(6) if N[top[1],j] == 2][0]
     391                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]})
     392            elif maxdegree == 16:
     393                # tree from bottom, 3 regular except for the leaves.
     394                centers = [i for i in range(8) if list(N.row(i)).count(2) == 3]
     395                center = [i for i in centers if len([j for j in centers if N[i,j] == 2]) == 2][0]
     396                centers.remove(center)
     397                bottom = [j for j in range(8) if N[center,j] == 2 and j not in centers][0]
     398                left = [j for j in range(8) if N[centers[0],j] == 2 and j != center]
     399                right = [j for j in range(8) if N[centers[1],j] == 2 and j != center]
     400                G.set_pos(pos={center:[0,0],bottom:[0,-1],centers[0]:[-0.8660254,0.5],centers[1]:[0.8660254,0.5],left[0]:[-0.8660254,1.5],right[0]:[0.8660254,1.5],left[1]:[-1.7320508,0],right[1]:[1.7320508,0]})
     401            elif maxdegree == 12:
     402                # tent
     403                centers = [i for i in range(8) if list(N.row(i)).count(2) == 3]
     404                left = [j for j in range(8) if N[centers[0],j] == 2]
     405                right = []
     406                for i in range(3):
     407                    right.append([j for j in range(8) if N[centers[1],j] == 2 and N[left[i],j] == 3][0])
     408                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]})
     409        G.set_vertices(D)
     410        G.relabel(range(1,n+1))
     411        return G
     412
     413    @cached_method
     414    def reorder(self, order):
     415        """
     416        Return a new isogeny class with the curves reordered.
     417
     418        INPUT:
     419
     420        - ``order`` -- None, a string or an iterable over all curves
     421          in this class.  See
     422          :meth:`sage.schemes.elliptic_curves.ell_rational_field.EllipticCurve_rational_field.isogeny_class`
     423          for more details.
     424
     425        OUTPUT:
     426
     427        - Another :class:`IsogenyClass_EC` with the curves reordered
     428          (and matrices and maps changed as appropriate)
     429
     430        EXAMPLES::
     431
     432            sage: isocls = EllipticCurve('15a1').isogeny_class(use_tuple=False)
     433            sage: print "\n".join([repr(C) for C in isocls.curves])
     434            Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 10*x - 10 over Rational Field
     435            Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 5*x + 2 over Rational Field
     436            Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 + 35*x - 28 over Rational Field
     437            Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 135*x - 660 over Rational Field
     438            Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 80*x + 242 over Rational Field
     439            Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 over Rational Field
     440            Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 110*x - 880 over Rational Field
     441            Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 2160*x - 39540 over Rational Field
     442            sage: isocls2 = isocls.reorder('lmfdb')
     443            sage: print "\n".join([repr(C) for C in isocls2.curves])
     444            Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 2160*x - 39540 over Rational Field
     445            Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 135*x - 660 over Rational Field
     446            Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 110*x - 880 over Rational Field
     447            Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 80*x + 242 over Rational Field
     448            Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 10*x - 10 over Rational Field
     449            Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 5*x + 2 over Rational Field
     450            Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 over Rational Field
     451            Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 + 35*x - 28 over Rational Field
     452        """
     453        if order is None or isinstance(order, basestring) and order == self._algorithm:
     454            return self
     455        if isinstance(order, basestring):
     456            if order == "lmfdb":
     457                reordered_curves = sorted(self.curves, key = lambda E: E.a_invariants())
     458            else:
     459                reordered_curves = list(self.E.isogeny_class(algorithm=order, use_tuple=False))
     460        elif isinstance(order, (list, tuple, IsogenyClass_EC)):
     461            reordered_curves = list(order)
     462            if len(reordered_curves) != len(self.curves):
     463                raise ValueError("Incorrect length")
     464        else:
     465            raise TypeError("order parameter should be a string, list of curves or isogeny class")
     466        need_perm = self._mat is not None
     467        cpy = self.copy()
     468        curves = []
     469        perm = []
     470        for E in reordered_curves:
     471            try:
     472                j = self.curves.index(E)
     473            except ValueError:
     474                try:
     475                    j = self.curves.index(E.minimal_model())
     476                except ValueError:
     477                    raise ValueError("order does not yield a permutation of curves")
     478            curves.append(self.curves[j])
     479            if need_perm: perm.append(j+1)
     480        cpy.curves = tuple(curves)
     481        if need_perm:
     482            from sage.groups.perm_gps.permgroup_named import SymmetricGroup
     483            perm = SymmetricGroup(len(self.curves))(perm)
     484            cpy._mat = perm.matrix() * self._mat * (~perm).matrix()
     485            if self._maps is not None:
     486                n = len(self._maps)
     487                cpy._maps = [self._maps[perm(i+1)-1] for i in range(n)]
     488                for i in range(n):
     489                    cpy._maps[i] = [cpy._maps[i][perm(j+1)-1] for j in range(n)]
     490        else:
     491            cpy._mat = None
     492            cpy._maps = None
     493        return cpy
     494
     495    @abstract_method
     496    def _compute(self):
     497        """
     498        This function is called during initialization and should fill
     499        in ``self.curves``, and possibly ``self._mat`` and
     500        ``self._maps`` as well, depending on the algorithm.
     501
     502        EXAMPLES::
     503
     504            sage: EllipticCurve('11a1').isogeny_class('mwrank',use_tuple=False).matrix() # indirect doctest
     505            [ 1  5  5]
     506            [ 5  1 25]
     507            [ 5 25  1]
     508        """
     509        pass
     510
     511    @abstract_method
     512    def _compute_matrix(self):
     513        """
     514        For algorithms that don't compute the isogeny matrix at first,
     515        this function should fill in ``self._mat``.
     516
     517        EXAMPLES::
     518
     519            sage: EllipticCurve('11a1').isogeny_class('database',use_tuple=False).matrix() # indirect doctest
     520            [ 1  5  5]
     521            [ 5  1 25]
     522            [ 5 25  1]
     523        """
     524        pass
     525
     526    @abstract_method
     527    def _compute_isogenies(self):
     528        """
     529        For algorithms that don't compute the isogenies at first, this
     530        function should fill in ``self._maps``.
     531
     532        EXAMPLES::
     533
     534            sage: f = EllipticCurve('11a1').isogeny_class('mwrank',use_tuple=False).isogenies()[0][1] # indirect doctest
     535            sage: f.domain()
     536            Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field
     537        """
     538        pass
     539
     540class IsogenyClass_EC_Rational(IsogenyClass_EC):
     541    """
     542    Isogeny classes for elliptic curves over Q.
     543    """
     544    def __init__(self, E, algorithm="sage", verbose=False, label=None, empty=False):
     545        """
     546        INPUT:
     547
     548        - ``E`` -- an elliptic curve over Q.
     549
     550        - ``algorithm`` -- a string (default "sage").  One of the
     551          following:
     552
     553          - "sage" -- Use sage's implementation to compute the curves,
     554            matrix and isogenies
     555
     556          - "mwrank" -- Use the mwrank C++ library
     557
     558          - "database" -- Use the Cremona database (only works if the
     559            curve is in the database)
     560
     561        - ``verbose`` -- boolean (default False).  Only relevant for
     562          algorithm="mwrank", controls verbosity of output while
     563          running mwrank.
     564
     565        - ``label`` -- a string, the label of this isogeny class
     566          (e.g. '15a' or '37.b').  Used in printing.
     567
     568        - ``empty`` -- don't compute the curves right now (used when reordering)
     569
     570        EXAMPLES::
     571
     572            sage: isocls = EllipticCurve('389a1').isogeny_class(use_tuple=False); isocls
     573            Elliptic curve isogeny class 389a
     574            sage: TestSuite(isocls).run()
     575        """
     576        self._algorithm = algorithm
     577        self._verbose = verbose
     578        IsogenyClass_EC.__init__(self, E, label=label, empty=empty)
     579
     580    def copy(self):
     581        """
     582        Returns a copy (mostly used in reordering).
     583
     584        EXAMPLES::
     585
     586            sage: isocls = EllipticCurve('389a1').isogeny_class(use_tuple=False)
     587            sage: isocls2 = isocls.copy()
     588            sage: isocls is isocls2
     589            False
     590            sage: isocls == isocls2
     591            True
     592        """
     593        ans = IsogenyClass_EC_Rational(self.E, self._algorithm, self._verbose, self._label, empty=True)
     594        # The following isn't needed internally, but it will keep
     595        # things from breaking if this is used for something other
     596        # than reordering.
     597        ans.curves = self.curves
     598        ans._mat = None
     599        ans._maps = None
     600        return ans
     601
     602    def _compute(self):
     603        """
     604        Computes the list of curves, and possibly the matrix and
     605        prime-degree isogenies (depending on the algorithm selected).
     606
     607        EXAMPLES::
     608
     609            sage: isocls = EllipticCurve('48a1').isogeny_class('sage',use_tuple=False).copy()
     610            sage: isocls._mat
     611            sage: isocls._compute(); isocls._mat
     612            [0 2 2 2 0 0]
     613            [2 0 0 0 2 2]
     614            [2 0 0 0 0 0]
     615            [2 0 0 0 0 0]
     616            [0 2 0 0 0 0]
     617            [0 2 0 0 0 0]
     618        """
     619        algorithm = self._algorithm
     620        from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix, unfill_isogeny_matrix
     621        from sage.matrix.all import MatrixSpace
     622        self._maps = None
     623        if algorithm == "mwrank":
     624            try:
     625                E = self.E.mwrank_curve()
     626            except ValueError:
     627                E = self.E.minimal_model().mwrank_curve()
     628            I, A = E.isogeny_class(verbose=self._verbose)
     629            self._mat = MatrixSpace(ZZ, len(A))(A)
     630            self.curves = tuple([constructor.EllipticCurve(ainvs) for ainvs in I])
     631        elif algorithm == "database":
     632            try:
     633                label = self.E.cremona_label(space=False)
     634            except RuntimeError:
     635                raise RuntimeError("unable to to find %s in the database"%self)
     636            db = sage.databases.cremona.CremonaDatabase()
     637            curves = db.isogeny_class(label)
     638            if len(curves) == 0:
     639                raise RuntimeError("unable to to find %s in the database"%self)
     640            # All curves will have the same conductor and isogeny class,
     641            # and there are are most 8 of them, so lexicographic sorting is okay.
     642            self.curves = tuple(sorted(curves, key = lambda E: E.cremona_label()))
     643            self._mat = None
     644        elif algorithm == "sage":
     645            curves = [self.E.minimal_model()]
     646            ijl_triples = []
     647            l_list = None
     648            i = 0
     649            while i<len(curves):
     650                E = curves[i]
     651                isogs = E.isogenies_prime_degree(l_list)
     652                for phi in isogs:
     653                    Edash = phi.codomain()
     654                    l = phi.degree()
     655                    # look to see if Edash is new.  Note that the
     656                    # curves returned by isogenies_prime_degree() are
     657                    # standard minimal models, so it suffices to check
     658                    # equality rather than isomorphism here.
     659                    try:
     660                        j = curves.index(Edash)
     661                    except ValueError:
     662                        j = len(curves)
     663                        curves.append(Edash)
     664                    ijl_triples.append((i,j,l,phi))
     665                if l_list is None:
     666                    l_list = [l for l in set([ZZ(f.degree()) for f in isogs])]
     667                i = i+1
     668            self.curves = tuple(curves)
     669            ncurves = len(curves)
     670            self._mat = MatrixSpace(ZZ,ncurves)(0)
     671            self._maps = [[0]*ncurves for i in range(ncurves)]
     672            for i,j,l,phi in ijl_triples:
     673                self._mat[i,j] = l
     674                self._maps[i][j]=phi
     675        else:
     676            raise ValueError, "unknown algorithm '%s'"%algorithm
     677
     678    def _compute_matrix(self):
     679        """
     680        Computes the matrix, assuming that the list of curves is computed.
     681
     682        EXAMPLES::
     683
     684            sage: isocls = EllipticCurve('1225h1').isogeny_class('database',use_tuple=False)
     685            sage: isocls._mat
     686            sage: isocls._compute_matrix(); isocls._mat
     687            [ 0 37]
     688            [37  0]
     689        """
     690        try:
     691            self._mat = self.E.isogeny_class(algorithm="mwrank",order=self.curves,use_tuple=False)._mat
     692        except ValueError: # incorrect length because mwrank didn't have enough precision
     693            self._mat = self.E.isogeny_class(algorithm="sage",order=self.curves,use_tuple=False)._mat
     694
     695    def _compute_isogenies(self):
     696        """
     697        EXAMPLES::
     698
     699            sage: E = EllipticCurve('15a1')
     700            sage: isocls = E.isogeny_class(algorithm='mwrank',use_tuple=False)
     701            sage: maps = isocls.isogenies() # indirect doctest
     702            sage: f = maps[0][1]
     703            sage: f.domain() == isocls[0] and f.codomain() == isocls[1]
     704            True
     705        """
     706        recomputed = self.E.isogeny_class(algorithm="sage",order=self.curves,use_tuple=False)
     707        self._mat = recomputed._mat
     708        # The domains and codomains here will be equal, but not the same Python object.
     709        self._maps = recomputed._maps