Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/schemes/elliptic_curves/isogeny_class.py
8821 views
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 <[email protected]>
11
# William Stein <[email protected]>
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_rational_field import EllipticCurve_rational_field
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 `\QQ` there is a unique
40
reduced minimal model in each isomorphism class.
41
42
EXAMPLES::
43
44
45
"""
46
def __init__(self, E, label=None, empty=False):
47
"""
48
Over `\QQ` 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()
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()) # 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()) # 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") # 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") # 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_rational_field):
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() == EE.isogeny_class() # 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()
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() # 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()
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()
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()
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_rational_field):
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()
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()
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 `\QQ` [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()
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()
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))
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
.. note:
503
504
This is currently redundant; it used to be required when mwrank was used.
505
"""
506
pass
507
508
@abstract_method
509
def _compute_matrix(self):
510
"""
511
For algorithms that don't compute the isogeny matrix at first,
512
this function should fill in ``self._mat``.
513
514
EXAMPLES::
515
516
sage: EllipticCurve('11a1').isogeny_class('database').matrix() # indirect doctest
517
[ 1 5 5]
518
[ 5 1 25]
519
[ 5 25 1]
520
"""
521
pass
522
523
@abstract_method
524
def _compute_isogenies(self):
525
"""
526
For algorithms that don't compute the isogenies at first, this
527
function should fill in ``self._maps``.
528
529
.. note:
530
531
This is currently redundant; it used to be required when mwrank was used.
532
"""
533
pass
534
535
class IsogenyClass_EC_Rational(IsogenyClass_EC):
536
"""
537
Isogeny classes for elliptic curves over `\QQ`.
538
"""
539
def __init__(self, E, algorithm="sage", label=None, empty=False):
540
"""
541
INPUT:
542
543
- ``E`` -- an elliptic curve over Q.
544
545
- ``algorithm`` -- a string (default "sage"). One of the
546
following:
547
548
- "sage" -- Use sage's implementation to compute the curves,
549
matrix and isogenies
550
551
- "database" -- Use the Cremona database (only works if the
552
curve is in the database)
553
554
- ``label`` -- a string, the label of this isogeny class
555
(e.g. '15a' or '37.b'). Used in printing.
556
557
- ``empty`` -- don't compute the curves right now (used when reordering)
558
559
EXAMPLES::
560
561
sage: isocls = EllipticCurve('389a1').isogeny_class(); isocls
562
Elliptic curve isogeny class 389a
563
sage: E = EllipticCurve([0, 0, 0, 0, 1001]) # conductor 108216108
564
sage: E.isogeny_class(order='database')
565
Traceback (most recent call last):
566
...
567
RuntimeError: unable to to find Elliptic Curve defined by y^2 = x^3 + 1001 over Rational Field in the database
568
sage: TestSuite(isocls).run()
569
"""
570
self._algorithm = algorithm
571
IsogenyClass_EC.__init__(self, E, label=label, empty=empty)
572
573
def copy(self):
574
"""
575
Returns a copy (mostly used in reordering).
576
577
EXAMPLES::
578
579
sage: isocls = EllipticCurve('389a1').isogeny_class()
580
sage: isocls2 = isocls.copy()
581
sage: isocls is isocls2
582
False
583
sage: isocls == isocls2
584
True
585
"""
586
ans = IsogenyClass_EC_Rational(self.E, self._algorithm, self._label, empty=True)
587
# The following isn't needed internally, but it will keep
588
# things from breaking if this is used for something other
589
# than reordering.
590
ans.curves = self.curves
591
ans._mat = None
592
ans._maps = None
593
return ans
594
595
def _compute(self):
596
"""
597
Computes the list of curves, and possibly the matrix and
598
prime-degree isogenies (depending on the algorithm selected).
599
600
EXAMPLES::
601
602
sage: isocls = EllipticCurve('48a1').isogeny_class('sage').copy()
603
sage: isocls._mat
604
sage: isocls._compute(); isocls._mat
605
[0 2 2 2 0 0]
606
[2 0 0 0 2 2]
607
[2 0 0 0 0 0]
608
[2 0 0 0 0 0]
609
[0 2 0 0 0 0]
610
[0 2 0 0 0 0]
611
"""
612
algorithm = self._algorithm
613
from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix, unfill_isogeny_matrix
614
from sage.matrix.all import MatrixSpace
615
self._maps = None
616
if algorithm == "database":
617
try:
618
label = self.E.cremona_label(space=False)
619
except RuntimeError:
620
raise RuntimeError("unable to to find %s in the database"%self.E)
621
db = sage.databases.cremona.CremonaDatabase()
622
curves = db.isogeny_class(label)
623
if len(curves) == 0:
624
raise RuntimeError("unable to to find %s in the database"%self.E)
625
# All curves will have the same conductor and isogeny class,
626
# and there are are most 8 of them, so lexicographic sorting is okay.
627
self.curves = tuple(sorted(curves, key = lambda E: E.cremona_label()))
628
self._mat = None
629
elif algorithm == "sage":
630
curves = [self.E.minimal_model()]
631
ijl_triples = []
632
l_list = None
633
i = 0
634
while i<len(curves):
635
E = curves[i]
636
isogs = E.isogenies_prime_degree(l_list)
637
for phi in isogs:
638
Edash = phi.codomain()
639
l = phi.degree()
640
# look to see if Edash is new. Note that the
641
# curves returned by isogenies_prime_degree() are
642
# standard minimal models, so it suffices to check
643
# equality rather than isomorphism here.
644
try:
645
j = curves.index(Edash)
646
except ValueError:
647
j = len(curves)
648
curves.append(Edash)
649
ijl_triples.append((i,j,l,phi))
650
if l_list is None:
651
l_list = [l for l in set([ZZ(f.degree()) for f in isogs])]
652
i = i+1
653
self.curves = tuple(curves)
654
ncurves = len(curves)
655
self._mat = MatrixSpace(ZZ,ncurves)(0)
656
self._maps = [[0]*ncurves for i in range(ncurves)]
657
for i,j,l,phi in ijl_triples:
658
self._mat[i,j] = l
659
self._maps[i][j]=phi
660
else:
661
raise ValueError, "unknown algorithm '%s'"%algorithm
662
663
def _compute_matrix(self):
664
"""
665
Computes the matrix, assuming that the list of curves is computed.
666
667
EXAMPLES::
668
669
sage: isocls = EllipticCurve('1225h1').isogeny_class('database')
670
sage: isocls._mat
671
sage: isocls._compute_matrix(); isocls._mat
672
[ 0 37]
673
[37 0]
674
"""
675
self._mat = self.E.isogeny_class(order=self.curves)._mat
676
677
def _compute_isogenies(self):
678
"""
679
EXAMPLES::
680
681
sage: E = EllipticCurve('15a1')
682
sage: isocls = E.isogeny_class()
683
sage: maps = isocls.isogenies() # indirect doctest
684
sage: f = maps[0][1]
685
sage: f.domain() == isocls[0] and f.codomain() == isocls[1]
686
True
687
"""
688
recomputed = self.E.isogeny_class(order=self.curves)
689
self._mat = recomputed._mat
690
# The domains and codomains here will be equal, but not the same Python object.
691
self._maps = recomputed._maps
692
693