Ticket #12768: 12768.patch
File 12768.patch, 68.6 KB (added by , 11 years ago) |
---|
-
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 280 280 label = chr(k)*int(n//26 + 1) 281 281 return label 282 282 283 cremona_label_regex = re.compile(r'(\d+)([a-z]*)(\d*)') 284 lmfdb_label_regex = re.compile(r'(\d+)\.([a-z]+)(\d*)') 285 283 286 def parse_cremona_label(label): 284 287 """ 285 288 Given a Cremona label that defines an elliptic 286 curve, e.g., 11 A1 or 37B3, parse the label and return the289 curve, e.g., 11a1 or 37b3, parse the label and return the 287 290 conductor, isogeny class label, and number. 288 291 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 just291 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. 293 296 294 297 INPUT: 295 298 … … 311 314 sage: parse_cremona_label('10bb2') 312 315 (10, 'bb', 2) 313 316 """ 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 327 def 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) 328 381 329 382 def split_code(key): 330 383 """ … … 384 437 if d!=0: return d 385 438 return cmp(cu1,cu2) 386 439 440 def 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 500 def 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 387 551 class MiniCremonaDatabase(SQLDatabase): 388 552 """ 389 553 The Cremona database of elliptic curves. … … 608 772 609 773 INPUT: 610 774 611 - ``label`` - str (Cremona label)775 - ``label`` - str (Cremona or LMFDB label) 612 776 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`. 614 784 615 785 EXAMPLES:: 616 786 … … 623 793 Traceback (most recent call last): 624 794 ... 625 795 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 626 801 """ 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) 628 812 label = str(N)+iso+str(num) 629 813 if self.get_skeleton() == _miniCremonaSkeleton: 630 814 q = self.__connection__.cursor().execute("SELECT eqn,rank,tors " \ … … 640 824 F._set_rank(c[1]) 641 825 F._set_torsion_order(c[2]) 642 826 F._set_conductor(N) 827 if lmfdb_label: 828 F._lmfdb_label = lmfdb_label 643 829 if len(c) > 3: 644 830 if num == 1: 645 831 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 449 449 if BSD.curve.j_invariant() in non_max_j_invs: # is this possible for optimal curves? 450 450 if verbosity > 0: 451 451 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): 453 453 if E.j_invariant() not in non_max_j_invs: 454 454 BSD.curve = E 455 455 break … … 659 659 kolyvagin_primes.append(p) 660 660 # Stein et al. 661 661 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)]) 663 663 for p in BSD.primes: 664 664 if p in kolyvagin_primes or p == 2: continue 665 665 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 463 463 8 464 464 465 465 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)] 467 467 [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)] 469 469 [1/4, 1/4, 1/4, 1/4, 1/4, 1/4, 1/4, 1/4] 470 470 471 471 Currently, the interface for negative modular symbols in eclib is not yet written:: … … 603 603 1 604 604 605 605 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)] 607 607 [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)] 609 609 [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)] 611 611 [1, 1, 1, 1, 1, 1, 1, 1] 612 612 613 613 """ -
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 170 170 Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Rational Field 171 171 172 172 Constructor from `[a_4,a_6]` sets `a_1=a_2=a_3=0`:: 173 173 174 174 sage: EllipticCurve([4,5]).ainvs() 175 175 (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 181 186 182 187 TESTS: 183 188 … … 185 190 label, we must be careful that the copied generators have the 186 191 right curve (see #10999: the following used not to work when 187 192 the large database was installed):: 188 193 189 194 sage: E=EllipticCurve('389a1') 190 195 sage: [P.curve() is E for P in E.gens()] 191 196 [True, True] … … 193 198 """ 194 199 if extra != None: # possibility of two arguments (the first would be the field) 195 200 ainvs = extra 201 self.__np = {} 202 self.__gens = {} 203 self.__rank = {} 204 self.__regulator = {} 205 self._isoclass = {} 196 206 if isinstance(ainvs, str): 197 207 label = ainvs 198 208 X = sage.databases.cremona.CremonaDatabase()[label] 199 209 EllipticCurve_number_field.__init__(self, Q, list(X.a_invariants())) 200 self.__np = {}201 self.__gens = {}202 self.__rank = {}203 self.__regulator = {}204 210 for attr in ['rank', 'torsion_order', 'cremona_label', 'conductor', 205 211 'modular_degree', 'gens', 'regulator']: 206 212 s = "_EllipticCurve_rational_field__"+attr … … 212 218 setattr(self, s, gens_dict) 213 219 else: 214 220 setattr(self, s, getattr(X, s)) 221 if hasattr(X,'_lmfdb_label'): 222 self._lmfdb_label = X._lmfdb_label 215 223 return 216 224 EllipticCurve_number_field.__init__(self, Q, ainvs) 217 self.__np = {}218 self.__gens = {}219 self.__rank = {}220 self.__regulator = {}221 225 if self.base_ring() != Q: 222 226 raise TypeError, "Base field (=%s) must be the Rational Field."%self.base_ring() 223 227 … … 3744 3748 ########################################################## 3745 3749 # Isogeny class 3746 3750 ########################################################## 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""" 3749 3753 Returns all curves over `\QQ` isogenous to this elliptic curve. 3750 3754 3751 3755 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 3753 3766 - ``verbose`` -- bool (default: False): ignored unless 3754 3767 ``algorithm`` == "mwrank", in which case it is passed to the 3755 3768 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 3766 3770 - ``fill_matrix`` -- bool (default: True): See below. 3767 3771 3768 3772 - ``return_maps`` -- bool (default: False): Ignored unless … … 3771 3775 `(i,j)` entry is the isogeny from curve `i` to curve `j` if 3772 3776 that isogeny has prime degree, else 0. 3773 3777 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, 3777 3808 matrix of integers, matrix of isogenies). The sorted list of 3778 3809 all curves isogenous to self is returned. If ``algorithm`` is 3779 3810 not "database", the isogeny matrix is also returned, otherwise … … 3786 3817 ``isogeny_graph()`` function). 3787 3818 3788 3819 .. 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:: 3797 3824 3798 3825 With ``algorithm`` "mwrank", the result is *not* 3799 3826 provably correct, in the sense that when the numbers are … … 3809 3836 degree. 3810 3837 3811 3838 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 3823 3852 :: 3824 3825 sage: I, _ = EllipticCurve('37b').isogeny_class('database'); I3826 [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, 3827 3856 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 3830 3859 This is an example of a curve with a `37`-isogeny:: 3831 3860 3832 3861 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 3837 3871 This curve had numerous `2`-isogenies:: 3838 3872 3839 3873 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] 3842 3876 [2 1 2 2 4 4] 3843 3877 [4 2 1 4 8 8] 3844 3878 [4 2 4 1 2 2] 3845 3879 [8 4 8 2 1 4] 3846 [8 4 8 2 4 1] )3847 3880 [8 4 8 2 4 1] 3881 3848 3882 See http://math.harvard.edu/~elkies/nature.html for more 3849 3883 interesting examples of isogeny structures. 3850 3884 3851 3885 :: 3852 3886 3853 3887 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 3857 3894 3858 3895 For large examples, the "mwrank" algorithm may fail to find 3859 3896 some isogenies since it works in fixed precision:: 3860 3897 3861 3898 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] 3880 3908 sage: E1.conductor() 3881 3909 18433092966712063653330496 3882 3910 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] 3888 3916 [ 2 1 6 6 3 3] 3889 3917 [ 3 6 1 9 2 18] 3890 3918 [ 3 6 9 1 18 2] 3891 3919 [ 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 :: 3893 3944 3894 3945 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] 3897 3948 [ 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 """ 3904 3953 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 3970 that has methods producing the isogeny graph and list of lists of isogenies. 3971 use_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) 3970 3978 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 3982 3980 3983 3981 def isogenies_prime_degree(self, l=None): 3984 3982 r""" … … 4126 4124 if not proof: 4127 4125 return True 4128 4126 else: 4129 return E2 in E1.isogeny_class( )[0]4127 return E2 in E1.isogeny_class(use_tuple=False).curves 4130 4128 4131 4129 def isogeny_degree(self, other): 4132 4130 """ … … 4237 4235 if not E1.is_isogenous(E2, proof=False): 4238 4236 return Integer(0) 4239 4237 4240 cl, mat = E1.isogeny_class()4238 isocls = E1.isogeny_class(use_tuple=False) 4241 4239 try: 4242 return mat[0,cl.index(E2)]4240 return isocls.matrix(fill=True)[0,isocls.index(E2)] 4243 4241 except ValueError: 4244 4242 return Integer(0) 4245 4243 … … 4309 4307 if optimal_label == label: return self 4310 4308 return constructor.EllipticCurve(optimal_label) 4311 4309 4312 def isogeny_graph(self ):4310 def isogeny_graph(self, order=None): 4313 4311 r""" 4314 4312 Returns a graph representing the isogeny class of this elliptic 4315 4313 curve, where the vertices are isogenous curves over 4316 4314 `\QQ` and the edges are prime degree isogenies 4317 4315 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 4318 4321 EXAMPLES:: 4319 4322 4320 4323 sage: LL = [] … … 4336 4339 sage: G = E.isogeny_graph() 4337 4340 sage: for v in G: print v, G.get_vertex(v) 4338 4341 ... 4339 0Elliptic Curve defined by y^2 + x*y = x^3 - 110*x + 435 over Rational Field4340 1Elliptic Curve defined by y^2 + x*y = x^3 - 115*x + 392 over Rational Field4341 2Elliptic Curve defined by y^2 + x*y = x^3 + 210*x + 2277 over Rational Field4342 3Elliptic Curve defined by y^2 + x*y = x^3 - 520*x - 4225 over Rational Field4343 4Elliptic Curve defined by y^2 + x*y = x^3 + 605*x - 19750 over Rational Field4344 5Elliptic Curve defined by y^2 + x*y = x^3 - 8125*x - 282568 over Rational Field4345 6Elliptic Curve defined by y^2 + x*y = x^3 - 7930*x - 296725 over Rational Field4346 7Elliptic Curve defined by y^2 + x*y = x^3 - 130000*x - 18051943 over Rational Field4342 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 4347 4350 sage: G.plot(edge_labels=True) 4348 4351 """ 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() 4354 4353 4355 4354 def manin_constant(self): 4356 4355 r""" … … 4423 4422 EXAMPLES:: 4424 4423 4425 4424 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, 4427 4426 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), 4429 4428 {0: 0, 1: 5, 2: 5}) 4430 4429 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, 4432 4431 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), 4434 4433 {0: 0, 1: 5, 2: 25}) 4435 4434 """ 4436 4435 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) 4439 4438 # see trac #4889 for nebulous M.list() --> M.entries() change... 4440 4439 # Take logs here since shortest path minimizes the *sum* of the weights -- not the product. 4441 4440 M = M.parent()([a.log() if a else 0 for a in M.list()]) 4442 4441 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()])) 4444 4443 v = G.shortest_path_lengths(0, by_weight=True, weight_sums=True) 4445 4444 # Now exponentiate and round to get degrees of isogenies 4446 4445 v = dict([(i, j.exp().round() if j else 0) for i,j in v.iteritems()]) 4447 return L, v4446 return isocls.curves, v 4448 4447 4449 4448 def _multiple_of_degree_of_isogeny_to_optimal_curve(self): 4450 4449 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 423 423 if t == True: 424 424 self.__is_reducible[p] = False 425 425 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) 427 427 v = isogeny_matrix.row(0) # first row 428 428 for a in v: 429 429 if a != 0 and a % p == 0: … … 474 474 return self.__reducible_primes 475 475 except AttributeError: 476 476 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()) 479 479 R = [p for p in X if arith.is_prime(p)] 480 480 self.__reducible_primes = R 481 481 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 """ 2 This file defines a class for an isogeny class of an elliptic curve. 3 4 AUTHORS: 5 6 David 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 25 from sage.structure.sage_object import SageObject 26 from sage.misc.lazy_attribute import lazy_attribute 27 import constructor 28 import sage.databases.cremona 29 from sage.rings.all import ZZ 30 from sage.misc.cachefunc import cached_method 31 from sage.misc.abstract_method import abstract_method 32 from sage.schemes.elliptic_curves.ell_generic import EllipticCurve_generic 33 34 class 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 540 class 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