Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/schemes/elliptic_curves/ell_curve_isogeny.py
4156 views
1
r"""
2
Isogenies
3
4
An isogeny `\varphi: E_1\to E_2` between two elliptic curves `E_1` and
5
`E_2` is a morphism of curves that sends the origin of `E_1` to the
6
origin of `E_2`. Such a morphism is automatically a morphism of group
7
schemes and the kernel is a finite subgroup scheme of `E_1`. Such a
8
subscheme can either be given by a list of generators, which have to
9
be torsion points, or by a polynomial in the coordinate `x` of the
10
Weierstrass equation of `E_1`.
11
12
The usual way to create and work with isogenies is illustrated with
13
the following example::
14
15
sage: k = GF(11)
16
sage: E = EllipticCurve(k,[1,1])
17
sage: Q = E(6,5)
18
sage: phi = E.isogeny(Q)
19
sage: phi
20
Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + x + 1 over Finite Field of size 11 to Elliptic Curve defined by y^2 = x^3 + 7*x + 8 over Finite Field of size 11
21
sage: P = E(4,5)
22
sage: phi(P)
23
(10 : 0 : 1)
24
sage: phi.codomain()
25
Elliptic Curve defined by y^2 = x^3 + 7*x + 8 over Finite Field of size 11
26
sage: phi.rational_maps()
27
((x^7 + 4*x^6 - 3*x^5 - 2*x^4 - 3*x^3 + 3*x^2 + x - 2)/(x^6 + 4*x^5 - 4*x^4 - 5*x^3 + 5*x^2), (x^9*y - 5*x^8*y - x^7*y + x^5*y - x^4*y - 5*x^3*y - 5*x^2*y - 2*x*y - 5*y)/(x^9 - 5*x^8 + 4*x^6 - 3*x^4 + 2*x^3))
28
29
The functions directly accessible from an elliptic curve ``E`` over a
30
field are ``isogeny`` and ``isogeny_codomain``.
31
32
The most useful functions that apply to isogenies are
33
34
- ``codomain``
35
- ``degree``
36
- ``domain``
37
- ``dual``
38
- ``rational_maps``
39
- ``kernel_polynomial``
40
41
.. Warning::
42
43
Only cyclic isogenies are implemented (except for [2]). Some
44
algorithms may need the isogeny to be normalized.
45
46
AUTHORS:
47
48
- Daniel Shumow <[email protected]>: 2009-04-19: initial version
49
50
- Chris Wuthrich : 7/09: changes: add check of input, not the full list is needed.
51
10/09: eliminating some bugs.
52
53
- John Cremona and Jenny Cooley: 2009-07..11: implement `l`-isogenies
54
for `l` = 2, 3, 5, 7 13 (the genus 0 cases) and also for `l` = 11,
55
17, 19, 37, 43, 67 or 163 over `\QQ` (the sporadic cases with only
56
finitely many `j`-invariants each).
57
"""
58
59
#*****************************************************************************
60
# Copyright (C) 2009 Daniel Shumow <[email protected]>
61
#
62
# Distributed under the terms of the GNU General Public License (GPL)
63
# http://www.gnu.org/licenses/
64
#*****************************************************************************
65
66
from copy import deepcopy, copy
67
68
from sage.categories import homset
69
70
from sage.categories.morphism import Morphism
71
72
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
73
from sage.rings.polynomial.polynomial_ring import polygen
74
from sage.rings.all import Integer, ZZ
75
from sage.rings.laurent_series_ring import LaurentSeriesRing
76
from sage.rings.polynomial.all import is_Polynomial
77
from sage.schemes.elliptic_curves.all import EllipticCurve
78
from sage.schemes.elliptic_curves.all import is_EllipticCurve
79
80
from sage.rings.number_field.number_field_base import is_NumberField
81
82
from sage.rings.rational_field import is_RationalField, QQ
83
84
from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
85
86
from sage.sets.set import Set
87
88
from sage.misc.cachefunc import cached_function
89
90
#
91
# Private function for parsing input to determine the type of
92
# algorithm
93
#
94
def isogeny_determine_algorithm(E, kernel, codomain, degree, model):
95
r"""
96
Helper function that allows the various isogeny functions to infer
97
the algorithm type from the parameters passed in.
98
99
If ``kernel`` is a list of points on the EllipticCurve `E`, then
100
we assume the algorithm to use is Velu.
101
102
If ``kernel`` is a list of coefficients or a univariate polynomial
103
we try to use the Kohel's algorithms.
104
105
EXAMPLES:
106
107
This helper function will be implicitly called by the following examples::
108
109
sage: R.<x> = GF(5)[]
110
sage: E = EllipticCurve(GF(5), [0,0,0,1,0])
111
sage: phi = EllipticCurveIsogeny(E, x+3)
112
sage: phi2 = EllipticCurveIsogeny(E, [GF(5)(3),GF(5)(1)])
113
sage: phi == phi2
114
True
115
sage: phi3 = EllipticCurveIsogeny(E, E((2,0)) )
116
sage: phi3 == phi2
117
True
118
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import isogeny_determine_algorithm
119
sage: isogeny_determine_algorithm(E, x+3, None, None, None)
120
'kohel'
121
sage: isogeny_determine_algorithm(E, [3, 1], None, None, None)
122
'kohel'
123
sage: isogeny_determine_algorithm(E, E((2,0)), None, None, None)
124
'velu'
125
126
"""
127
128
kernel_is_list = (type(kernel) == list)
129
130
if not kernel_is_list and kernel in E :
131
kernel = [kernel]
132
kernel_is_list = True
133
134
if (is_Polynomial(kernel) or ( kernel_is_list) and (kernel[0] in E.base_ring()) ):
135
algorithm = "kohel"
136
elif (kernel_is_list) and (kernel[0] in E): # note that if kernel[0] is on an extension of E this condition will be false
137
algorithm = "velu"
138
else:
139
raise ValueError, "Invalid Parameters to EllipticCurveIsogeny constructor."
140
141
return algorithm
142
143
144
def isogeny_codomain_from_kernel(E, kernel, degree=None):
145
r"""
146
This function computes the isogeny codomain given a kernel.
147
148
INPUT:
149
150
- ``E`` - The domain elliptic curve.
151
152
- ``kernel`` - Either a list of points in the kernel of the isogeny, or a
153
kernel polynomial (specified as a either a univariate
154
polynomial or a coefficient list.)
155
156
- ``degree`` - an integer, (default:``None``) optionally specified degree
157
of the kernel.
158
159
OUTPUT:
160
161
(elliptic curve) the codomain of the separable normalized isogeny
162
from this kernel
163
164
EXAMPLES::
165
166
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import isogeny_codomain_from_kernel
167
sage: E = EllipticCurve(GF(7), [1,0,1,0,1])
168
sage: R.<x> = GF(7)[]
169
sage: isogeny_codomain_from_kernel(E, [4,1], degree=3)
170
Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x + 6 over Finite Field of size 7
171
sage: EllipticCurveIsogeny(E, [4,1]).codomain() == isogeny_codomain_from_kernel(E, [4,1], degree=3)
172
True
173
sage: isogeny_codomain_from_kernel(E, x^3 + x^2 + 4*x + 3)
174
Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x + 6 over Finite Field of size 7
175
sage: isogeny_codomain_from_kernel(E, x^3 + 2*x^2 + 4*x + 3)
176
Elliptic Curve defined by y^2 + x*y + y = x^3 + 5*x + 2 over Finite Field of size 7
177
178
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
179
sage: kernel_list = [E((15,10)), E((10,3)),E((6,5))]
180
sage: isogeny_codomain_from_kernel(E, kernel_list)
181
Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 3*x + 15 over Finite Field of size 19
182
183
"""
184
185
algorithm = isogeny_determine_algorithm(E, kernel, None, degree, None);
186
187
if ("velu"==algorithm):
188
# if we are using Velu's formula, just instantiate the isogeny
189
# and return the codomain
190
codomain = EllipticCurveIsogeny(E, kernel).codomain()
191
elif ("kohel"==algorithm):
192
codomain = compute_codomain_kohel(E, kernel, degree)
193
194
return codomain
195
196
197
def compute_codomain_formula(E, v, w):
198
r"""
199
Given parameters `v` and `w` (as in Velu / Kohel / etc formulas)
200
computes the codomain curve.
201
202
EXAMPLES:
203
204
This formula is used by every Isogeny Instantiation::
205
206
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
207
sage: phi = EllipticCurveIsogeny(E, E((1,2)) )
208
sage: phi.codomain()
209
Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 9*x + 13 over Finite Field of size 19
210
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_codomain_formula
211
sage: v = phi._EllipticCurveIsogeny__v
212
sage: w = phi._EllipticCurveIsogeny__w
213
sage: compute_codomain_formula(E, v, w)
214
Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 9*x + 13 over Finite Field of size 19
215
216
"""
217
218
a1,a2,a3,a4,a6 = E.ainvs()
219
220
A4 = a4 - 5*v
221
A6 = a6 - (a1**2 + 4*a2)*v - 7*w
222
223
return EllipticCurve([a1, a2, a3, A4, A6])
224
225
226
def compute_vw_kohel_even_deg1(x0, y0, a1, a2, a4):
227
r"""
228
The formula for computing `v` and `w` using Kohel's formulas for
229
isogenies of degree 2.
230
231
EXAMPLES:
232
233
This function will be implicitly called by the following example::
234
235
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
236
sage: phi = EllipticCurveIsogeny(E, [9,1])
237
sage: phi
238
Isogeny of degree 2 from Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Finite Field of size 19 to Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 9*x + 8 over Finite Field of size 19
239
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_vw_kohel_even_deg1
240
sage: a1,a2,a3,a4,a6 = E.ainvs()
241
sage: x0 = -9
242
sage: y0 = -(a1*x0 + a3)/2
243
sage: compute_vw_kohel_even_deg1(x0, y0, a1, a2, a4)
244
(18, 9)
245
246
"""
247
248
v = (3*x0**2 + 2*a2*x0 + a4 - a1*y0)
249
w = x0*v
250
251
return (v,w)
252
253
254
def compute_vw_kohel_even_deg3(b2,b4,s1,s2,s3):
255
r"""
256
The formula for computing `v` and `w` using Kohel's formulas for
257
isogenies of degree 3.
258
259
EXAMPLES:
260
261
This function will be implicitly called by the following example::
262
263
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
264
sage: R.<x> = GF(19)[]
265
sage: phi = EllipticCurveIsogeny(E, x^3 + 7*x^2 + 15*x + 12)
266
sage: phi
267
Isogeny of degree 4 from Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Finite Field of size 19 to Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 3*x + 15 over Finite Field of size 19
268
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_vw_kohel_even_deg3
269
sage: (b2,b4) = (E.b2(), E.b4())
270
sage: (s1, s2, s3) = (-7, 15, -12)
271
sage: compute_vw_kohel_even_deg3(b2, b4, s1, s2, s3)
272
(4, 7)
273
274
"""
275
276
temp1 = (s1**2 - 2*s2)
277
v = 3*temp1 + b2*s1/2 + 3*b4/2
278
w = 3*(s1**3 - 3*s1*s2 + 3*s3) + b2*temp1/2 + b4*s1/2
279
280
return (v,w)
281
282
283
def compute_vw_kohel_odd(b2,b4,b6,s1,s2,s3,n):
284
r"""
285
This function computes the `v` and `w` according to Kohel's formulas.
286
287
EXAMPLES:
288
289
This function will be implicitly called by the following example::
290
291
sage: E = EllipticCurve(GF(19), [18,17,16,15,14])
292
sage: R.<x> = GF(19)[]
293
sage: phi = EllipticCurveIsogeny(E, x^3 + 14*x^2 + 3*x + 11)
294
sage: phi
295
Isogeny of degree 7 from Elliptic Curve defined by y^2 + 18*x*y + 16*y = x^3 + 17*x^2 + 15*x + 14 over Finite Field of size 19 to Elliptic Curve defined by y^2 + 18*x*y + 16*y = x^3 + 17*x^2 + 18*x + 18 over Finite Field of size 19
296
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_vw_kohel_odd
297
sage: (b2,b4,b6) = (E.b2(), E.b4(), E.b6())
298
sage: (s1,s2,s3) = (-14,3,-11)
299
sage: compute_vw_kohel_odd(b2,b4,b6,s1,s2,s3,3)
300
(7, 1)
301
302
"""
303
304
v = 6*(s1**2 - 2*s2) + b2*s1 + n*b4
305
w = 10*(s1**3 - 3*s1*s2 + 3*s3) + 2*b2*(s1**2 - 2*s2) + 3*b4*s1 + n*b6
306
307
return (v,w)
308
309
310
def compute_codomain_kohel(E, kernel, degree):
311
r"""
312
This function computes the codomain from the kernel polynomial as
313
per Kohel's formulas.
314
315
EXAMPLES::
316
317
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_codomain_kohel
318
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
319
sage: phi = EllipticCurveIsogeny(E, [9,1])
320
sage: phi.codomain() == isogeny_codomain_from_kernel(E, [9,1])
321
True
322
sage: compute_codomain_kohel(E, [9,1], 2)
323
Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 9*x + 8 over Finite Field of size 19
324
sage: R.<x> = GF(19)[]
325
sage: E = EllipticCurve(GF(19), [18,17,16,15,14])
326
sage: phi = EllipticCurveIsogeny(E, x^3 + 14*x^2 + 3*x + 11)
327
sage: phi.codomain() == isogeny_codomain_from_kernel(E, x^3 + 14*x^2 + 3*x + 11)
328
True
329
sage: compute_codomain_kohel(E, x^3 + 14*x^2 + 3*x + 11, 7)
330
Elliptic Curve defined by y^2 + 18*x*y + 16*y = x^3 + 17*x^2 + 18*x + 18 over Finite Field of size 19
331
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
332
sage: phi = EllipticCurveIsogeny(E, x^3 + 7*x^2 + 15*x + 12)
333
sage: isogeny_codomain_from_kernel(E, x^3 + 7*x^2 + 15*x + 12) == phi.codomain()
334
True
335
sage: compute_codomain_kohel(E, x^3 + 7*x^2 + 15*x + 12,4)
336
Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 3*x + 15 over Finite Field of size 19
337
338
NOTES:
339
340
This function uses the formulas of Section 2.4 of [K96].
341
342
REFERENCES:
343
344
- [K96] Kohel, "Endomorphism Rings of Elliptic Curves over Finite Fields"
345
346
"""
347
348
# First set up the polynomial ring
349
350
base_field = E.base_ring()
351
352
if (is_Polynomial(kernel)):
353
psi = kernel
354
kernel_list = psi.list()
355
poly_ring = psi.parent()
356
x = psi.variables()[0]
357
elif (list == type(kernel)) and (kernel[0] in base_field):
358
kernel_list = kernel
359
poly_ring = base_field.polynomial_ring()
360
psi = poly_ring(kernel_list)
361
x = poly_ring.gen()
362
else:
363
raise ValueError, "input not of correct type"
364
365
366
# next determine the even / odd part of the isogeny
367
psi_2tor = two_torsion_part(E, poly_ring, psi, degree)
368
369
if (0 != psi_2tor.degree()): # even degree case
370
371
psi_quo = poly_ring(psi/psi_2tor)
372
373
if (0 != psi_quo.degree()):
374
raise ArithmeticError, "For basic Kohel's algorithm, if the kernel degree is even then the kernel must be contained in the two torsion."
375
376
n = psi_2tor.degree()
377
378
if (1 == n):
379
380
a1,a2,a3,a4,a6 = E.ainvs()
381
382
x0 = -psi_2tor.constant_coefficient()
383
384
# determine y0
385
if (2 == base_field.characteristic()):
386
y0 = (x0**3 + a2*x0**2 + a4*x0 + a6).sqrt()
387
else:
388
y0 = -(a1*x0 + a3)/2
389
390
(v,w) = compute_vw_kohel_even_deg1(x0,y0,a1,a2,a4)
391
392
elif (3 == n):
393
394
b2 = E.b2()
395
b4 = E.b4()
396
397
s = psi_2tor.list()
398
s1 = -s[n-1]
399
s2 = s[n-2]
400
s3 = -s[n-3]
401
402
(v,w) = compute_vw_kohel_even_deg3(b2,b4,s1,s2,s3)
403
404
else:
405
406
n = psi.degree()
407
408
b2 = E.b2()
409
b4 = E.b4()
410
b6 = E.b6()
411
412
s1 = 0; s2 = 0; s3 = 0
413
414
if (1 <= n):
415
s1 = -kernel_list[n-1]
416
417
if (2 <= n):
418
s2 = kernel_list[n-2]
419
420
if (3 <= n):
421
s3 = -kernel_list[n-3]
422
423
# initializing these allows us to calculate E2.
424
(v,w) = compute_vw_kohel_odd(b2,b4,b6,s1,s2,s3,n);
425
426
return compute_codomain_formula(E, v, w)
427
428
429
def two_torsion_part(E, poly_ring, psi, degree):
430
r"""
431
432
Returns the greatest common divisor of ``psi`` and the 2 torsion
433
polynomial of `E`.
434
435
EXAMPLES:
436
437
Every function that computes the kernel polynomial via Kohel's
438
formulas will call this function::
439
440
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
441
sage: R.<x> = GF(19)[]
442
sage: phi = EllipticCurveIsogeny(E, x + 13)
443
sage: isogeny_codomain_from_kernel(E, x + 13) == phi.codomain()
444
True
445
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import two_torsion_part
446
sage: two_torsion_part(E, R, x+13, 2)
447
x + 13
448
449
"""
450
if (None==degree) or (0 == degree % 2):
451
452
x = poly_ring.gens()[0]
453
psi_2 = E.two_division_polynomial(x)
454
psi_G = poly_ring(psi.gcd(psi_2))
455
456
else:
457
458
psi_G = poly_ring(1)
459
460
return psi_G
461
462
463
class EllipticCurveIsogeny(Morphism):
464
r"""
465
Class Implementing Isogenies of Elliptic Curves
466
467
This class implements cyclic, separable, normalized isogenies of
468
elliptic curves.
469
470
Several different algorithms for computing isogenies are
471
available. These include:
472
473
- Velu's Formulas: Velu's original formulas for computing
474
isogenies. This algorithm is selected by giving as the
475
``kernel`` parameter a list of points which generate a finite
476
subgroup.
477
478
- Kohel's Formulas: Kohel's original formulas for computing
479
isogenies. This algorithm is selected by giving as the
480
``kernel`` parameter a monic polynomial (or a coefficient list
481
(little endian)) which will define the kernel of the isogeny.
482
483
INPUT:
484
485
- ``E`` - an elliptic curve, the domain of the isogeny to
486
initialize.
487
488
- ``kernel`` - a kernel, either a point in ``E``, a list of points
489
in ``E``, a monic kernel polynomial, or ``None``.
490
If initializing from a domain/codomain, this must be
491
set to None.
492
493
- ``codomain`` - an elliptic curve (default:``None``). If ``kernel``
494
is ``None``, then this must be the codomain of a cyclic,
495
separable, normalized isogeny, furthermore, ``degree``
496
must be the degree of the isogeny from ``E`` to
497
``codomain``. If ``kernel`` is not ``None``, then this
498
must be isomorphic to the codomain of the cyclic normalized
499
separable isogeny defined by ``kernel``, in this case, the
500
isogeny is post composed with an isomorphism so that this
501
parameter is the codomain.
502
503
- ``degree`` - an integer (default:``None``).
504
If ``kernel`` is ``None``, then this is the degree of the
505
isogeny from ``E`` to ``codomain``.
506
If ``kernel`` is not ``None``, then this is used to determine
507
whether or not to skip a gcd of the kernel polynomial with the
508
two torsion polynomial of ``E``.
509
510
- ``model`` - a string (default:``None``). Only supported variable is
511
``minimal``, in which case if ``E`` is a curve over the
512
rationals, then the codomain is set to be the unique global
513
minimum model.
514
515
- ``check`` (default: ``True``) checks if the input is valid to define an isogeny
516
517
EXAMPLES:
518
519
A simple example of creating an isogeny of a field of small
520
characteristic::
521
522
sage: E = EllipticCurve(GF(7), [0,0,0,1,0])
523
sage: phi = EllipticCurveIsogeny(E, E((0,0)) ); phi
524
Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 3*x over Finite Field of size 7
525
sage: phi.degree() == 2
526
True
527
sage: phi.kernel_polynomial()
528
x
529
sage: phi.rational_maps()
530
((x^2 + 1)/x, (x^2*y - y)/x^2)
531
sage: phi == loads(dumps(phi)) # optional - pickling http://trac.sagemath.org/sage_trac/ticket/11599
532
True
533
534
A more complicated example of a characteristic 2 field::
535
536
sage: E = EllipticCurve(GF(2^4,'alpha'), [0,0,1,0,1])
537
sage: P = E((1,1))
538
sage: phi_v = EllipticCurveIsogeny(E, P); phi_v
539
Isogeny of degree 3 from Elliptic Curve defined by y^2 + y = x^3 + 1 over Finite Field in alpha of size 2^4 to Elliptic Curve defined by y^2 + y = x^3 over Finite Field in alpha of size 2^4
540
sage: phi_ker_poly = phi_v.kernel_polynomial()
541
sage: phi_ker_poly
542
x + 1
543
sage: ker_poly_list = phi_ker_poly.list()
544
sage: phi_k = EllipticCurveIsogeny(E, ker_poly_list)
545
sage: phi_k == phi_v
546
True
547
sage: phi_k.rational_maps()
548
((x^3 + x + 1)/(x^2 + 1), (x^3*y + x^2*y + x*y + x + y)/(x^3 + x^2 + x + 1))
549
sage: phi_v.rational_maps()
550
((x^3 + x + 1)/(x^2 + 1), (x^3*y + x^2*y + x*y + x + y)/(x^3 + x^2 + x + 1))
551
sage: phi_k.degree() == phi_v.degree()
552
True
553
sage: phi_k.degree()
554
3
555
sage: phi_k.is_separable()
556
True
557
sage: phi_v(E(0))
558
(0 : 1 : 0)
559
sage: alpha = E.base_field().gen()
560
sage: Q = E((0, alpha*(alpha + 1)))
561
sage: phi_v(Q)
562
(1 : alpha^2 + alpha : 1)
563
sage: phi_v(P) == phi_k(P)
564
True
565
sage: phi_k(P) == phi_v.codomain()(0)
566
True
567
568
We can create an isogeny that has kernel equal to the full 2
569
torsion::
570
571
sage: E = EllipticCurve(GF(3), [0,0,0,1,1])
572
sage: ker_list = E.division_polynomial(2).list()
573
sage: phi = EllipticCurveIsogeny(E, ker_list); phi
574
Isogeny of degree 4 from Elliptic Curve defined by y^2 = x^3 + x + 1 over Finite Field of size 3 to Elliptic Curve defined by y^2 = x^3 + x + 1 over Finite Field of size 3
575
sage: phi(E(0))
576
(0 : 1 : 0)
577
sage: phi(E((0,1)))
578
(1 : 0 : 1)
579
sage: phi(E((0,2)))
580
(1 : 0 : 1)
581
sage: phi(E((1,0)))
582
(0 : 1 : 0)
583
sage: phi.degree()
584
4
585
586
We can also create trivial isogenies with the trivial kernel::
587
588
sage: E = EllipticCurve(GF(17), [11, 11, 4, 12, 10])
589
sage: phi_v = EllipticCurveIsogeny(E, E(0))
590
sage: phi_v.degree()
591
1
592
sage: phi_v.rational_maps()
593
(x, y)
594
sage: E == phi_v.codomain()
595
True
596
sage: P = E.random_point()
597
sage: phi_v(P) == P
598
True
599
600
sage: E = EllipticCurve(GF(31), [23, 1, 22, 7, 18])
601
sage: phi_k = EllipticCurveIsogeny(E, [1])
602
sage: phi_k
603
Isogeny of degree 1 from Elliptic Curve defined by y^2 + 23*x*y + 22*y = x^3 + x^2 + 7*x + 18 over Finite Field of size 31 to Elliptic Curve defined by y^2 + 23*x*y + 22*y = x^3 + x^2 + 7*x + 18 over Finite Field of size 31
604
sage: phi_k.degree()
605
1
606
sage: phi_k.rational_maps()
607
(x, y)
608
sage: phi_k.codomain() == E
609
True
610
sage: phi_k.kernel_polynomial()
611
1
612
sage: P = E.random_point(); P == phi_k(P)
613
True
614
615
Velu and Kohel also work in characteristic 0::
616
617
sage: E = EllipticCurve(QQ, [0,0,0,3,4])
618
sage: P_list = E.torsion_points()
619
sage: phi = EllipticCurveIsogeny(E, P_list)
620
sage: phi
621
Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 3*x + 4 over Rational Field to Elliptic Curve defined by y^2 = x^3 - 27*x + 46 over Rational Field
622
sage: P = E((0,2))
623
sage: phi(P)
624
(6 : -10 : 1)
625
sage: phi_ker_poly = phi.kernel_polynomial()
626
sage: phi_ker_poly
627
x + 1
628
sage: ker_poly_list = phi_ker_poly.list()
629
sage: phi_k = EllipticCurveIsogeny(E, ker_poly_list); phi_k
630
Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 3*x + 4 over Rational Field to Elliptic Curve defined by y^2 = x^3 - 27*x + 46 over Rational Field
631
sage: phi_k(P) == phi(P)
632
True
633
sage: phi_k == phi
634
True
635
sage: phi_k.degree()
636
2
637
sage: phi_k.is_separable()
638
True
639
640
A more complicated example over the rationals (of odd degree)::
641
642
sage: E = EllipticCurve('11a1')
643
sage: P_list = E.torsion_points()
644
sage: phi_v = EllipticCurveIsogeny(E, P_list); phi_v
645
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
646
sage: P = E((16,-61))
647
sage: phi_v(P)
648
(0 : 1 : 0)
649
sage: ker_poly = phi_v.kernel_polynomial(); ker_poly
650
x^2 - 21*x + 80
651
sage: ker_poly_list = ker_poly.list()
652
sage: phi_k = EllipticCurveIsogeny(E, ker_poly_list); phi_k
653
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
654
sage: phi_k == phi_v
655
True
656
sage: phi_v(P) == phi_k(P)
657
True
658
sage: phi_k.is_separable()
659
True
660
661
We can also do this same example over the number field defined by
662
the irreducible two torsion polynomial of `E`::
663
664
sage: E = EllipticCurve('11a1')
665
sage: P_list = E.torsion_points()
666
sage: K.<alpha> = NumberField(x^3 - 2* x^2 - 40*x - 158)
667
sage: EK = E.change_ring(K)
668
sage: P_list = [EK(P) for P in P_list]
669
sage: phi_v = EllipticCurveIsogeny(EK, P_list); phi_v
670
Isogeny of degree 5 from Elliptic Curve defined by y^2 + y = x^3 + (-1)*x^2 + (-10)*x + (-20) over Number Field in alpha with defining polynomial x^3 - 2*x^2 - 40*x - 158 to Elliptic Curve defined by y^2 + y = x^3 + (-1)*x^2 + (-7820)*x + (-263580) over Number Field in alpha with defining polynomial x^3 - 2*x^2 - 40*x - 158
671
sage: P = EK((alpha/2,-1/2))
672
sage: phi_v(P)
673
(122/121*alpha^2 + 1633/242*alpha - 3920/121 : -1/2 : 1)
674
sage: ker_poly = phi_v.kernel_polynomial()
675
sage: ker_poly
676
x^2 - 21*x + 80
677
sage: ker_poly_list = ker_poly.list()
678
sage: phi_k = EllipticCurveIsogeny(EK, ker_poly_list)
679
sage: phi_k
680
Isogeny of degree 5 from Elliptic Curve defined by y^2 + y = x^3 + (-1)*x^2 + (-10)*x + (-20) over Number Field in alpha with defining polynomial x^3 - 2*x^2 - 40*x - 158 to Elliptic Curve defined by y^2 + y = x^3 + (-1)*x^2 + (-7820)*x + (-263580) over Number Field in alpha with defining polynomial x^3 - 2*x^2 - 40*x - 158
681
sage: phi_v == phi_k
682
True
683
sage: phi_k(P) == phi_v(P)
684
True
685
sage: phi_k == phi_v
686
True
687
sage: phi_k.degree()
688
5
689
sage: phi_v.is_separable()
690
True
691
692
The following example shows how to specify an isogeny from domain
693
and codomain::
694
695
sage: E = EllipticCurve('11a1')
696
sage: R.<x> = QQ[]
697
sage: f = x^2 - 21*x + 80
698
sage: phi = E.isogeny(f)
699
sage: E2 = phi.codomain()
700
sage: phi_s = EllipticCurveIsogeny(E, None, E2, 5)
701
sage: phi_s
702
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
703
sage: phi_s == phi
704
True
705
sage: phi_s.rational_maps() == phi.rational_maps()
706
True
707
708
However only cyclic normalized isogenies can be constructed this
709
way. So it won't find the isogeny [3]::
710
711
sage: E.isogeny(None, codomain=E,degree=9)
712
Traceback (most recent call last):
713
...
714
ValueError: The two curves are not linked by a cyclic normalized isogeny of degree 9
715
716
Also the presumed isogeny between the domain and codomain must be
717
normalized::
718
719
sage: E2.isogeny(None,codomain=E,degree=5)
720
Traceback (most recent call last):
721
...
722
ValueError: The two curves are not linked by a cyclic normalized isogeny of degree 5
723
sage: phi.dual()
724
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
725
sage: phi.dual().is_normalized()
726
False
727
728
Here an example of a construction of a endomorphisms with cyclic
729
kernel on a CM-curve::
730
731
sage: K.<i> = NumberField(x^2+1)
732
sage: E = EllipticCurve(K, [1,0])
733
sage: RK.<X> = K[]
734
sage: f = X^2 - 2/5*i + 1/5
735
sage: phi= E.isogeny(f)
736
sage: isom = phi.codomain().isomorphism_to(E)
737
sage: phi.set_post_isomorphism(isom)
738
sage: phi.codomain() == phi.domain()
739
True
740
sage: phi.rational_maps()
741
(((-4/25*i - 3/25)*x^5 + (-4/5*i + 2/5)*x^3 + x)/(x^4 + (-4/5*i + 2/5)*x^2 + (-4/25*i - 3/25)),
742
((2/125*i - 11/125)*x^6*y + (64/125*i + 23/125)*x^4*y + (162/125*i - 141/125)*x^2*y + (-4/25*i - 3/25)*y)/(x^6 + (-6/5*i + 3/5)*x^4 + (-12/25*i - 9/25)*x^2 + (2/125*i - 11/125)))
743
744
"""
745
746
####################
747
# member variables
748
####################
749
750
__check = None
751
#
752
# variables common to all algorithms
753
#
754
__E1 = None # domain curve
755
__E2 = None # codomain curve
756
757
__degree = None
758
759
__separable = True # This class only implements separable isogenies (for now.)
760
761
__algorithm = None
762
763
__this_hash = None
764
765
__check = None
766
#
767
# pre isomorphism
768
#
769
__intermediate_domain = None
770
__pre_isomorphism = None
771
__prei_x_coord_ratl_map = None
772
__prei_y_coord_ratl_map = None
773
774
#
775
# post isomorphism
776
#
777
778
__intermediate_codomain = None
779
__post_isomorphism = None
780
__posti_x_coord_ratl_map = None
781
__posti_y_coord_ratl_map = None
782
783
#
784
# algebraic structs
785
#
786
__base_field = None
787
__poly_ring = None
788
__x_var = None
789
__y_var = None
790
791
#
792
# Rational Maps
793
#
794
__rational_maps_initialized = False
795
__X_coord_rational_map = None
796
__Y_coord_rational_map = None
797
798
#
799
# The dual
800
#
801
__dual = None
802
803
#
804
# Kernel Data
805
#
806
807
__kernel_list = None # list of elements in the kernel
808
809
__kernel_polynomial_list = None # polynomial stored as a little endian list of coefficients
810
811
__kernel_polynomial = None # polynomial with roots at x values for x-coordinate of points in the kernel
812
813
__inner_kernel_polynomial = None # the inner kernel polynomial (ignoring preisomorphism)
814
815
__n = None
816
817
818
#
819
# member variables common to Velu's formula
820
#
821
822
# we keep track of the 2 torsion and non2torsion separately
823
__kernel_2tor = None
824
__kernel_non2tor = None
825
826
# variables used in Velu's formula (as well as Kohel's variant)
827
__v = None
828
__w = None
829
830
831
#
832
# member variables specific to Kohel's algorithm.
833
#
834
835
__psi = None # psi polynomial
836
__phi = None # phi polynomial
837
__omega = None # omega polynomial
838
839
840
#
841
# Python Special Functions
842
#
843
844
def __init__(self, E, kernel, codomain=None, degree=None, model=None, check=True):
845
r"""
846
Constructor for EllipticCurveIsogeny class.
847
848
EXAMPLES::
849
850
sage: E = EllipticCurve(GF(2), [0,0,1,0,1])
851
sage: phi = EllipticCurveIsogeny(E, [1,1])
852
sage: phi
853
Isogeny of degree 3 from Elliptic Curve defined by y^2 + y = x^3 + 1 over Finite Field of size 2 to Elliptic Curve defined by y^2 + y = x^3 over Finite Field of size 2
854
855
sage: E = EllipticCurve(GF(31), [0,0,0,1,0])
856
sage: P = E((2,17))
857
sage: phi = EllipticCurveIsogeny(E, P)
858
sage: phi
859
Isogeny of degree 8 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 31 to Elliptic Curve defined by y^2 = x^3 + 10*x + 28 over Finite Field of size 31
860
861
sage: E = EllipticCurve('17a1')
862
sage: phi = EllipticCurveIsogeny(E, [41/3, -55, -1, -1, 1])
863
sage: phi
864
Isogeny of degree 9 from Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - x - 14 over Rational Field to Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 56*x - 10124 over Rational Field
865
866
sage: E = EllipticCurve('37a1')
867
sage: triv = EllipticCurveIsogeny(E, E(0))
868
sage: triv
869
Isogeny of degree 1 from Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field to Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
870
sage: triv.rational_maps()
871
(x, y)
872
873
sage: E = EllipticCurve('49a3')
874
sage: R.<X> = QQ[]
875
sage: EllipticCurveIsogeny(E,X^3-13*X^2-58*X+503,check=False)
876
Isogeny of degree 7 from Elliptic Curve defined by y^2 + x*y = x^3 - x^2 - 107*x + 552 over Rational Field to Elliptic Curve defined by y^2 + x*y = x^3 - x^2 - 5252*x - 178837 over Rational Field
877
878
"""
879
880
if not is_EllipticCurve(E):
881
raise ValueError, "E parameter must be an EllipticCurve."
882
883
if type(kernel) != type([1,1]) and kernel in E :
884
# a single point was given, we put it in a list
885
# the first condition assures that [1,1] is treated as x+1
886
kernel = [kernel]
887
888
# if the kernel is None and the codomain isn't
889
# calculate the kernel polynomial
890
pre_isom = None
891
post_isom = None
892
893
self.__check = check
894
895
if (kernel is None) and (codomain is not None):
896
897
if (degree is None):
898
raise ValueError, "If specifying isogeny by domain and codomain, degree parameter must be set."
899
900
# save the domain/codomain: really used now (trac #7096)
901
old_domain = E
902
old_codomain = codomain
903
904
(pre_isom, post_isom, E, codomain, kernel) = compute_sequence_of_maps(E, codomain, degree)
905
906
self.__init_algebraic_structs(E)
907
908
algorithm = isogeny_determine_algorithm(E, kernel, codomain, degree, model);
909
910
self.__algorithm = algorithm
911
912
if ("velu"==algorithm):
913
self.__init_from_kernel_list(kernel)
914
elif ("kohel"==algorithm):
915
self.__init_from_kernel_polynomial(kernel, degree)
916
917
self.__compute_E2()
918
919
self.__setup_post_isomorphism(codomain, model)
920
921
if (pre_isom is not None):
922
self.set_pre_isomorphism(pre_isom)
923
924
if (post_isom is not None):
925
self.__set_post_isomorphism(old_codomain, post_isom) #(trac #7096)
926
927
# Inheritance house keeping
928
929
self.__perform_inheritance_housekeeping()
930
931
return
932
933
934
def __call__(self, P, output_base_ring=None):
935
r"""
936
Function that implements the call-ability of elliptic curve
937
isogenies.
938
939
EXAMPLES::
940
941
sage: E = EllipticCurve(GF(17), [1, 9, 5, 4, 3])
942
sage: phi = EllipticCurveIsogeny(E, [6,13,1])
943
sage: phi(E((1,0)))
944
(15 : 13 : 1)
945
946
sage: E = EllipticCurve(GF(23), [0,0,0,1,0])
947
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
948
sage: phi(E((1,5)))
949
(2 : 0 : 1)
950
sage: phi(E(15,20), output_base_ring=GF(23^2,'alpha'))
951
(12 : 1 : 1)
952
953
sage: E = EllipticCurve(QQ, [0,0,0,3,0])
954
sage: P = E((1,2))
955
sage: phi = EllipticCurveIsogeny(E, [0,1])
956
sage: phi(P)
957
(4 : -4 : 1)
958
sage: phi(-P)
959
(4 : 4 : 1)
960
961
sage: E = EllipticCurve(GF(17), [0,-1,0,-3,-1])
962
sage: Q = E((16,0))
963
sage: tau = E.isogeny([Q],E)
964
sage: tau(Q)
965
(0 : 1 : 0)
966
967
TESTS (trac 10888)::
968
969
sage: K.<th> = NumberField(x^2+3)
970
sage: E = EllipticCurve(K,[7,0])
971
sage: phi = E.isogeny(E(0,0))
972
sage: P = E(-3,4*th)
973
sage: phi(P)
974
(-16/3 : 8/9*th : 1)
975
sage: Q = phi(P)
976
sage: phihat = phi.dual()
977
sage: phihat(Q)
978
(-1/48 : 127/576*th : 1)
979
980
"""
981
E1 = self.__E1
982
E_P = P.curve()
983
change_output_ring = False
984
985
# check that the parent curve of the input point is this curve
986
# or that the point is on the same curve but whose base ring
987
# is an extension of this ring
988
if (E1 != E_P):
989
if (E1.a_invariants() != E_P.a_invariants()) :
990
raise ValueError, "P must be on a curve with same Weierstrass model as the domain curve of this isogeny."
991
change_output_ring = True
992
993
994
if(P.is_zero()):
995
return self.__E2(0)
996
997
(xP, yP) = P.xy()
998
999
if not self.__E1.is_on_curve(xP,yP):
1000
raise InputError, "Input point must be on the domain curve of this isogeny."
1001
1002
# if there is a pre isomorphism, apply it
1003
if (self.__pre_isomorphism is not None):
1004
temp_xP = self.__prei_x_coord_ratl_map(xP, yP)
1005
temp_yP = self.__prei_y_coord_ratl_map(xP, yP)
1006
(xP, yP) = (temp_xP, temp_yP)
1007
1008
if ("velu" == self.__algorithm):
1009
outP = self.__compute_via_velu_numeric(xP, yP)
1010
elif ("kohel" == self.__algorithm):
1011
outP = self.__compute_via_kohel_numeric(xP,yP)
1012
1013
# the intermediate functions return the point at infinity
1014
# if the input point is in the kernel
1015
if (outP == self.__intermediate_codomain(0)):
1016
return self.__E2(0)
1017
1018
# if there is a post isomorphism, apply it
1019
if (self.__post_isomorphism is not None):
1020
tempX = self.__posti_x_coord_ratl_map(outP[0], outP[1])
1021
tempY = self.__posti_y_coord_ratl_map(outP[0], outP[1])
1022
outP = [tempX, tempY]
1023
1024
if change_output_ring:
1025
if (output_base_ring is None):
1026
output_base_ring = E_P.base_ring()
1027
outE2 = self.__E2.change_ring(output_base_ring)
1028
else:
1029
output_base_ring = self.__E2.base_ring()
1030
outE2 = self.__E2
1031
outP = self.__E2.point(outP,check=False)
1032
1033
R = output_base_ring
1034
1035
return outE2.point([R(outP[0]), R(outP[1]), R(1)], check=False)
1036
1037
1038
def __getitem__(self, i):
1039
self.__initialize_rational_maps()
1040
if (i < 0) or (i > 2):
1041
raise IndexError
1042
1043
if i == 0:
1044
return self.__X_coord_rational_map
1045
else:
1046
return self.__Y_coord_rational_map
1047
1048
def __iter__(self):
1049
self.__initialize_rational_maps()
1050
return iter((self.__X_coord_rational_map, self.__Y_coord_rational_map))
1051
1052
def __hash__(self):
1053
r"""
1054
Function that implements the hash ability of Isogeny objects.
1055
1056
This hashes the underlying kernel polynomial so that equal
1057
isogeny objects have the same hash value. Also, this hashes
1058
the base field, and domain and codomain curves as well, so
1059
that isogenies with the same kernel polynomial (over different
1060
base fields / curves) hash to different values.
1061
1062
EXAMPLES::
1063
1064
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
1065
sage: phi_v = EllipticCurveIsogeny(E, E((0,0)))
1066
sage: phi_k = EllipticCurveIsogeny(E, [0,1])
1067
sage: phi_k.__hash__() == phi_v.__hash__()
1068
True
1069
sage: E_F17 = EllipticCurve(GF(17), [0,0,0,1,1])
1070
sage: phi_p = EllipticCurveIsogeny(E_F17, E_F17([0,1]))
1071
sage: phi_p.__hash__() == phi_v.__hash__()
1072
False
1073
1074
sage: E = EllipticCurve('49a3')
1075
sage: R.<X> = QQ[]
1076
sage: EllipticCurveIsogeny(E,X^3-13*X^2-58*X+503,check=False)
1077
Isogeny of degree 7 from Elliptic Curve defined by y^2 + x*y = x^3 - x^2 - 107*x + 552 over Rational Field to Elliptic Curve defined by y^2 + x*y = x^3 - x^2 - 5252*x - 178837 over Rational Field
1078
1079
"""
1080
1081
if (self.__this_hash is not None):
1082
return self.__this_hash
1083
1084
ker_poly_list = self.__kernel_polynomial_list
1085
1086
if (ker_poly_list is None):
1087
ker_poly_list = self.__init_kernel_polynomial()
1088
1089
this_hash = 0
1090
1091
for a in ker_poly_list:
1092
this_hash = this_hash.__xor__(hash(a))
1093
1094
this_hash = this_hash.__xor__(hash(self.__E1))
1095
this_hash = this_hash.__xor__(hash(self.__E2))
1096
this_hash = this_hash.__xor__(hash(self.__base_field))
1097
1098
self.__this_hash = this_hash
1099
1100
return self.__this_hash
1101
1102
1103
def __cmp__(self, other):
1104
r"""
1105
Function that implements comparisons between isogeny objects.
1106
1107
This function works by comparing the underlying kernel
1108
objects.
1109
1110
EXAMPLES::
1111
1112
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
1113
sage: phi_v = EllipticCurveIsogeny(E, E((0,0)))
1114
sage: phi_k = EllipticCurveIsogeny(E, [0,1])
1115
sage: phi_k == phi_v
1116
True
1117
sage: E_F17 = EllipticCurve(GF(17), [0,0,0,1,0])
1118
sage: phi_p = EllipticCurveIsogeny(E_F17, [0,1])
1119
sage: phi_p == phi_v
1120
False
1121
sage: E = EllipticCurve('11a1')
1122
sage: phi = E.isogeny(E(5,5))
1123
sage: psi = E.isogeny(phi.kernel_polynomial())
1124
sage: phi == psi
1125
True
1126
sage: phi.dual() == psi.dual()
1127
True
1128
1129
1130
"""
1131
if (not isinstance(other, EllipticCurveIsogeny)):
1132
return -1
1133
1134
if (self.__kernel_polynomial is None):
1135
self.__init_kernel_polynomial()
1136
1137
return cmp(self.__kernel_polynomial, other.kernel_polynomial())
1138
1139
1140
def __neg__(self):
1141
r"""
1142
Function to implement unary negation (-) operator on
1143
isogenies. Returns a copy of this isogeny that has been
1144
negated.
1145
1146
EXAMPLES:
1147
1148
The following examples inherently exercise this function::
1149
1150
sage: E = EllipticCurve(j=GF(17)(0))
1151
sage: phi = EllipticCurveIsogeny(E, E((-1,0)) )
1152
sage: negphi = -phi
1153
sage: phi(E((0,1))) + negphi(E((0,1)))
1154
(0 : 1 : 0)
1155
1156
sage: E = EllipticCurve(j=GF(19)(1728))
1157
sage: R.<x> = GF(19)[]
1158
sage: phi = EllipticCurveIsogeny(E, x)
1159
sage: negphi = -phi
1160
sage: phi(E((3,7))) + negphi(E((3,12))) == phi(2*E((3,7)))
1161
True
1162
sage: negphi(E((18,6)))
1163
(17 : 0 : 1)
1164
1165
sage: R.<x> = QQ[]
1166
sage: E = EllipticCurve('17a1')
1167
sage: R.<x> = QQ[]
1168
sage: f = x - 11/4
1169
sage: phi = EllipticCurveIsogeny(E, f)
1170
sage: negphi = -phi
1171
sage: phi.rational_maps()[0] == negphi.rational_maps()[0]
1172
True
1173
sage: P = E((7,13))
1174
sage: phi(P) + negphi(P)
1175
(0 : 1 : 0)
1176
1177
"""
1178
# save off the kernel lists
1179
kernel_list = self.__kernel_list
1180
self.__kernel_list = None
1181
1182
output = deepcopy(self)
1183
1184
# reset the kernel lists
1185
output.__kernel_list = copy(kernel_list)
1186
self.__kernel_list = kernel_list
1187
1188
output.switch_sign()
1189
return output
1190
1191
1192
1193
#
1194
# Sage Special Functions
1195
#
1196
1197
def _repr_(self):
1198
r"""
1199
Special sage specific function that implement the
1200
functionality to display the isogeny self as a string.
1201
1202
EXAMPLES::
1203
1204
sage: E = EllipticCurve(GF(31), [1,0,1,1,0])
1205
sage: phi = EllipticCurveIsogeny(E, E((0,0)) )
1206
sage: phi._repr_()
1207
'Isogeny of degree 17 from Elliptic Curve defined by y^2 + x*y + y = x^3 + x over Finite Field of size 31 to Elliptic Curve defined by y^2 + x*y + y = x^3 + 14*x + 9 over Finite Field of size 31'
1208
1209
sage: E = EllipticCurve(QQ, [1,0,0,1,9])
1210
sage: phi = EllipticCurveIsogeny(E, [2,1])
1211
sage: phi._repr_()
1212
'Isogeny of degree 2 from Elliptic Curve defined by y^2 + x*y = x^3 + x + 9 over Rational Field to Elliptic Curve defined by y^2 + x*y = x^3 - 59*x + 165 over Rational Field'
1213
1214
"""
1215
return 'Isogeny of degree ' + self.__degree.__repr__() + ' from ' + \
1216
self.__E1.__repr__() + ' to ' + self.__E2.__repr__()
1217
1218
1219
def _latex_(self):
1220
r"""
1221
Special sage specific function that implements functionality
1222
to display an isogeny object as a latex string.
1223
1224
This function returns a latex string representing the isogeny
1225
self as the `x` and `y` coordinate rational functions.
1226
1227
EXAMPLES::
1228
1229
sage: E = EllipticCurve(QQ, [0,0,0,1,-1])
1230
sage: phi = EllipticCurveIsogeny(E, E(0))
1231
sage: phi._latex_()
1232
'\\left( x , y \\right)'
1233
1234
sage: E = EllipticCurve(GF(17), [0,0,0,1,-1])
1235
sage: R.<X> = GF(17)[]
1236
sage: phi = EllipticCurveIsogeny(E, X+11)
1237
sage: phi._latex_()
1238
'\\left( \\frac{x^{2} + 11 x + 7}{x + 11} , \\frac{x^{2} y + 5 x y + 12 y}{x^{2} + 5 x + 2} \\right)'
1239
1240
1241
"""
1242
ratl_maps = self.rational_maps()
1243
return '\\left( %s , %s \\right)' % (ratl_maps[0]._latex_(), ratl_maps[1]._latex_())
1244
1245
1246
###########################
1247
# Private Common Functions
1248
###########################
1249
1250
# delete the hash value
1251
def __clear_cached_values(self):
1252
r"""
1253
A private function to clear the hash if the codomain has been
1254
modified by a pre or post isomorphism.
1255
1256
EXAMPLES::
1257
1258
sage: F = GF(7);
1259
sage: E = EllipticCurve(j=F(0))
1260
sage: phi = EllipticCurveIsogeny(E, [E((0,-1)), E((0,1))])
1261
sage: old_hash = hash(phi)
1262
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
1263
sage: phi.set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(), (-1,2,-3,4)))
1264
sage: hash(phi) == old_hash
1265
False
1266
1267
sage: R.<x> = QQ[]
1268
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
1269
sage: phi = EllipticCurveIsogeny(E, x)
1270
sage: old_ratl_maps = phi.rational_maps()
1271
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
1272
sage: phi.set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(), (-1,0,0,0)))
1273
sage: old_ratl_maps == phi.rational_maps()
1274
False
1275
sage: old_ratl_maps[1] == -phi.rational_maps()[1]
1276
True
1277
1278
sage: F = GF(127); R.<x> = F[]
1279
sage: E = EllipticCurve(j=F(1728))
1280
sage: f = x^5 + 43*x^4 + 97*x^3 + 81*x^2 + 42*x + 82
1281
sage: phi = EllipticCurveIsogeny(E, f)
1282
sage: old_hash = hash(phi)
1283
sage: old_ratl_maps = phi.rational_maps()
1284
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
1285
sage: phi.set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(), (-13,13,-13,13)))
1286
sage: old_hash == hash(phi)
1287
False
1288
sage: old_ratl_maps == phi.rational_maps()
1289
False
1290
sage: phi._EllipticCurveIsogeny__clear_cached_values()
1291
1292
"""
1293
self.__this_hash = None
1294
self.__rational_maps_initialized = False
1295
self.__X_coord_rational_map = None
1296
self.__Y_coord_rational_map = None
1297
self.__dual
1298
1299
1300
# performs the inheritance house keeping
1301
def __perform_inheritance_housekeeping(self):
1302
r"""
1303
Internal helper function, sets values on the super classes of
1304
this class.
1305
1306
EXAMPLES:
1307
1308
The following examples will implicitly exercise this
1309
function::
1310
1311
sage: E = EllipticCurve(GF(43), [2,3,5,7,11])
1312
sage: R.<x> = GF(43)[]; f = x + 42
1313
sage: phi = EllipticCurveIsogeny(E, f)
1314
sage: phi._EllipticCurveIsogeny__perform_inheritance_housekeeping()
1315
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
1316
sage: E2 = phi.codomain()
1317
sage: post_isom = WeierstrassIsomorphism(E2, (41, 37, 31, 29))
1318
sage: phi.set_post_isomorphism(post_isom)
1319
sage: E1pr = WeierstrassIsomorphism(E, (-1, 2, -3, 4)).codomain().codomain()
1320
sage: pre_isom = E1pr.isomorphism_to(E)
1321
sage: phi.set_pre_isomorphism(pre_isom)
1322
1323
"""
1324
1325
# one of the superclasses uses these fields
1326
self._domain = self.__E1
1327
self._codomain = self.__E2
1328
1329
# sets up the parent
1330
parent = homset.Hom(self.__E1(0).parent(), self.__E2(0).parent())
1331
Morphism.__init__(self, parent)
1332
1333
return
1334
1335
1336
# initializes the base field
1337
def __init_algebraic_structs(self, E):
1338
r"""
1339
An internal function for EllipticCurveIsogeny objects that
1340
sets up the member variables necessary for algebra.
1341
1342
EXAMPLES:
1343
1344
The following tests inherently exercise this function::
1345
1346
sage: E = EllipticCurve(j=GF(17)(0))
1347
sage: phi = EllipticCurveIsogeny(E, E((-1,0)))
1348
sage: phi._EllipticCurveIsogeny__init_algebraic_structs(E)
1349
1350
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
1351
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
1352
sage: phi._EllipticCurveIsogeny__init_algebraic_structs(E)
1353
1354
sage: F = GF(19); R.<x> = F[]
1355
sage: E = EllipticCurve(j=GF(19)(0))
1356
sage: phi = EllipticCurveIsogeny(E, x)
1357
sage: phi._EllipticCurveIsogeny__init_algebraic_structs(E)
1358
1359
"""
1360
self.__E1 = E
1361
self.__base_field = E.base_ring()
1362
1363
poly_ring = self.__poly_ring = PolynomialRing(self.__base_field, ['x','y'])
1364
1365
self.__x_var = poly_ring('x')
1366
self.__y_var = poly_ring('y')
1367
1368
self.__intermediate_domain = E
1369
1370
return
1371
1372
1373
def __compute_E2(self):
1374
r"""
1375
Private function that computes and sets the isogeny codomain.
1376
1377
EXAMPLES:
1378
1379
These examples inherently exercise this function::
1380
1381
sage: E = EllipticCurve(j=GF(7)(1728))
1382
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
1383
sage: phi.codomain()
1384
Elliptic Curve defined by y^2 = x^3 + 3*x over Finite Field of size 7
1385
sage: phi._EllipticCurveIsogeny__compute_E2()
1386
1387
sage: R.<x> = GF(7)[]
1388
sage: phi = EllipticCurveIsogeny(E, x)
1389
sage: phi.codomain()
1390
Elliptic Curve defined by y^2 = x^3 + 3*x over Finite Field of size 7
1391
sage: phi._EllipticCurveIsogeny__compute_E2()
1392
1393
"""
1394
1395
if ("velu" == self.__algorithm):
1396
E2 = self.__compute_E2_via_velu()
1397
elif ("kohel" == self.__algorithm):
1398
E2 = self.__compute_E2_via_kohel()
1399
1400
self.__E2 = E2
1401
self.__intermediate_codomain = E2
1402
1403
return
1404
1405
1406
# initializes the rational maps fields
1407
def __initialize_rational_maps(self, precomputed_maps=None):
1408
r"""
1409
Private function that computes and initializes the rational
1410
maps.
1411
1412
INPUT:
1413
1414
- ``
1415
1416
EXAMPLES:
1417
1418
The following examples inherently exercise this function::
1419
1420
sage: E = EllipticCurve(j=GF(7)(1728))
1421
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
1422
sage: phi._EllipticCurveIsogeny__initialize_rational_maps()
1423
sage: phi.rational_maps()
1424
((x^2 + 1)/x, (x^2*y - y)/x^2)
1425
1426
sage: R.<x> = GF(7)[]
1427
sage: phi = EllipticCurveIsogeny(E, x)
1428
sage: phi = EllipticCurveIsogeny(E, x)
1429
sage: phi.rational_maps()
1430
((x^2 + 1)/x, (x^2*y - y)/x^2)
1431
sage: phi._EllipticCurveIsogeny__initialize_rational_maps()
1432
1433
sage: E = EllipticCurve([1,2,3,4,5])
1434
sage: Eshort = E.short_weierstrass_model()
1435
sage: phi = E.isogeny(E(0), Eshort)
1436
sage: phiX, phiY = phi.rational_maps()
1437
sage: phiX(1,2), phiY(1,2)
1438
(63, 864)
1439
"""
1440
if self.__rational_maps_initialized:
1441
return
1442
1443
if precomputed_maps is None:
1444
if ("velu"==self.__algorithm):
1445
(X_map, Y_map) = self.__initialize_rational_maps_via_velu()
1446
if ("kohel"==self.__algorithm):
1447
(X_map, Y_map) = self.__initialize_rational_maps_via_kohel()
1448
else:
1449
X_map, Y_map = precomputed_maps
1450
1451
if self.__prei_x_coord_ratl_map is not None:
1452
prei_X_map = self.__prei_x_coord_ratl_map
1453
prei_Y_map = self.__prei_y_coord_ratl_map
1454
X_map, Y_map = X_map.subs(x=prei_X_map, y=prei_Y_map), \
1455
Y_map.subs(x=prei_X_map, y=prei_Y_map)
1456
1457
if self.__posti_x_coord_ratl_map is not None:
1458
X_map, Y_map = \
1459
self.__posti_x_coord_ratl_map.subs(x=X_map, y=Y_map), \
1460
self.__posti_y_coord_ratl_map.subs(x=X_map, y=Y_map)
1461
1462
self.__X_coord_rational_map = X_map
1463
self.__Y_coord_rational_map = Y_map
1464
self.__rational_maps_initialized = True
1465
1466
1467
def __init_kernel_polynomial(self):
1468
r"""
1469
Private function that initializes the kernel polynomial (if
1470
the algorithm does not take it as a parameter.)
1471
1472
EXAMPLES:
1473
1474
The following examples inherently exercise this function::
1475
1476
sage: E = EllipticCurve(j=GF(7)(1728))
1477
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
1478
sage: phi.kernel_polynomial()
1479
x
1480
sage: phi._EllipticCurveIsogeny__init_kernel_polynomial()
1481
[0, 1]
1482
1483
"""
1484
1485
if (self.__kernel_polynomial_list is not None):
1486
return self.__kernel_polynomial_list
1487
1488
if ("velu" == self.__algorithm):
1489
ker_poly_list = self.__init_kernel_polynomial_velu()
1490
else:
1491
raise InputError, "The kernel polynomial should already be defined!"
1492
1493
return ker_poly_list
1494
1495
1496
def __set_pre_isomorphism(self, domain, isomorphism):
1497
r"""
1498
Private function to set the pre isomorphism and domain (and
1499
keep track of the domain of the isogeny.)
1500
1501
EXAMPLES::
1502
1503
sage: E = EllipticCurve(GF(43), [2,3,5,7,11])
1504
sage: R.<x> = GF(43)[]; f = x + 42
1505
sage: phi = EllipticCurveIsogeny(E, f)
1506
sage: phi._EllipticCurveIsogeny__perform_inheritance_housekeeping()
1507
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
1508
sage: E1pr = WeierstrassIsomorphism(E, (-1, 2, -3, 4)).codomain().codomain()
1509
sage: pre_isom = E1pr.isomorphism_to(E)
1510
sage: phi.set_pre_isomorphism(pre_isom)
1511
sage: phi._EllipticCurveIsogeny__set_pre_isomorphism(E, WeierstrassIsomorphism(E, (-1, 3, -3, 4)))
1512
sage: E == phi.domain()
1513
True
1514
1515
"""
1516
1517
self.__E1 = domain
1518
1519
# set the isomorphism
1520
self.__pre_isomorphism = isomorphism
1521
1522
# calculate the isomorphism as a rational map.
1523
1524
(u, r, s, t) = isomorphism.tuple()
1525
1526
x = self.__x_var;
1527
y = self.__y_var;
1528
1529
self.__prei_x_coord_ratl_map = (x - r)/u**2
1530
self.__prei_y_coord_ratl_map = (y - s*(x-r) - t)/u**3
1531
1532
if (self.__kernel_polynomial is not None):
1533
ker_poly = self.__kernel_polynomial
1534
ker_poly = ker_poly.subs(x=self.__prei_x_coord_ratl_map)
1535
kp_lc = ker_poly.univariate_polynomial().leading_coefficient()
1536
ker_poly = (1/kp_lc)*ker_poly
1537
self.__kernel_polynomial = ker_poly
1538
1539
self.__perform_inheritance_housekeeping()
1540
1541
return;
1542
1543
1544
def __set_post_isomorphism(self, codomain, isomorphism):
1545
r"""
1546
Private function to set the post isomorphism and codomain (and
1547
keep track of the codomain of the isogeny.)
1548
1549
EXAMPLES:
1550
1551
The following examples inherently exercise this function::
1552
1553
sage: E = EllipticCurve(j=GF(7)(1728))
1554
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
1555
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
1556
sage: E2 = phi.codomain()
1557
sage: isom = WeierstrassIsomorphism(E2, (-1,2,-3,4))
1558
sage: phi.set_post_isomorphism(isom)
1559
sage: phi._EllipticCurveIsogeny__set_post_isomorphism(E2, WeierstrassIsomorphism(phi.codomain(), (1,-2,3,-4)))
1560
sage: E2 == phi.codomain()
1561
True
1562
1563
"""
1564
1565
# set the codomains
1566
self.__E2 = codomain
1567
1568
# set the isomorphism
1569
self.__post_isomorphism = isomorphism
1570
1571
# calculate the isomorphism as a rational map.
1572
1573
(u, r, s, t) = isomorphism.tuple()
1574
1575
x = self.__x_var;
1576
y = self.__y_var;
1577
1578
self.__posti_x_coord_ratl_map = (x - r)/u**2
1579
self.__posti_y_coord_ratl_map = (y - s*(x-r) - t)/u**3
1580
1581
self.__perform_inheritance_housekeeping()
1582
1583
return;
1584
1585
1586
def __setup_post_isomorphism(self, codomain, model):
1587
r"""
1588
Private function to set up the post isomorphism given the
1589
codomain.
1590
1591
EXAMPLES:
1592
1593
The following examples inherently exercise this function::
1594
1595
sage: E = EllipticCurve(j=GF(7)(1728))
1596
sage: E2 = EllipticCurve(GF(7), [0,0,0,5,0])
1597
sage: phi = EllipticCurveIsogeny(E, E((0,0)), E2); phi
1598
Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 5*x over Finite Field of size 7
1599
sage: E3 = EllipticCurve(GF(7), [0,0,0,6,0])
1600
sage: phi._EllipticCurveIsogeny__setup_post_isomorphism(E3, None)
1601
sage: phi
1602
Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7
1603
1604
sage: R.<x> = QQ[]
1605
sage: E = EllipticCurve(j=1728)
1606
sage: f = x^3 - x
1607
sage: phi = EllipticCurveIsogeny(E, f, model='minimal'); phi
1608
Isogeny of degree 4 from Elliptic Curve defined by y^2 = x^3 - x over Rational Field to Elliptic Curve defined by y^2 = x^3 - x over Rational Field
1609
1610
sage: phi = EllipticCurveIsogeny(E, f, model=None)
1611
sage: phi._EllipticCurveIsogeny__setup_post_isomorphism(None, 'minimal')
1612
sage: phi
1613
Isogeny of degree 4 from Elliptic Curve defined by y^2 = x^3 - x over Rational Field to Elliptic Curve defined by y^2 = x^3 - x over Rational Field
1614
1615
"""
1616
1617
# TODO: add checks to make sure that
1618
# codomain and model parameters are consistent with the
1619
# algorithm used.
1620
1621
post_isom = None
1622
newE2 = None
1623
1624
oldE2 = self.__E2
1625
1626
if (model is not None):
1627
1628
if (codomain is not None):
1629
raise ValueError, "Cannot specify a codomain and model flag simultaneously."
1630
1631
if ('minimal' == model):
1632
1633
if (not is_RationalField(oldE2.base_field())):
1634
raise ValueError, "specifying minimal for model flag only valid with curves over the rational numbers."
1635
1636
newE2 = oldE2.minimal_model()
1637
post_isom = oldE2.isomorphism_to(newE2)
1638
1639
else:
1640
raise ValueError, "Unknown value of model flag."
1641
1642
elif (codomain is not None):
1643
if (not is_EllipticCurve(codomain)):
1644
raise ValueError, "Codomain parameter must be an elliptic curve."
1645
1646
if (not oldE2.is_isomorphic(codomain)):
1647
raise ValueError, "Codomain parameter must be isomorphic to computed codomain isogeny"
1648
1649
newE2 = codomain
1650
post_isom = oldE2.isomorphism_to(newE2)
1651
1652
if (post_isom is not None):
1653
self.__set_post_isomorphism(newE2, post_isom)
1654
1655
return
1656
1657
1658
###########################
1659
# Velu's Formula Functions
1660
###########################
1661
1662
#
1663
# Setup function for Velu's formula
1664
#
1665
1666
def __init_from_kernel_list(self, kernel_gens):
1667
r"""
1668
Private function that initializes the isogeny from a list of
1669
points which generate the kernel (For Velu's formulas.)
1670
1671
EXAMPLES:
1672
1673
The following example inherently exercises this function::
1674
1675
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
1676
sage: phi = EllipticCurveIsogeny(E, E((0,0))); phi
1677
Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 4*x over Finite Field of size 7
1678
sage: phi._EllipticCurveIsogeny__init_from_kernel_list([E(0), E((0,0))])
1679
1680
"""
1681
if self.__check :
1682
for P in kernel_gens:
1683
if not P.has_finite_order():
1684
raise ValueError, "The points in the kernel must be of finite order."
1685
# work around the current implementation of torsion points. When they are done better this could be
1686
# reduced but it won't speed things up too much.
1687
kernel_list = Set([self.__E1(0)])
1688
for P in kernel_gens:
1689
points_to_add = []
1690
for j in range(P.order()):
1691
for Q in kernel_list:
1692
points_to_add.append(j*P+Q)
1693
kernel_list += Set(points_to_add)
1694
1695
self.__kernel_list = kernel_list.list()
1696
self.__kernel_2tor = {}
1697
self.__kernel_non2tor = {}
1698
1699
self.__degree = len(kernel_list)
1700
1701
self.__sort_kernel_list()
1702
1703
return
1704
1705
1706
#
1707
# Precompute the values in Velu's Formula.
1708
#
1709
def __sort_kernel_list(self):
1710
r"""
1711
Private function that sorts the list of points in the kernel
1712
(For Velu's formulas). Sorts out the 2 torsion points, and
1713
puts them in a dictionary.
1714
1715
EXAMPLES:
1716
1717
The following example inherently exercises this function::
1718
1719
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
1720
sage: P = E((4,2))
1721
sage: phi = EllipticCurveIsogeny(E, P); phi
1722
Isogeny of degree 4 from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7
1723
sage: phi._EllipticCurveIsogeny__kernel_2tor = {}
1724
sage: phi._EllipticCurveIsogeny__kernel_non2tor = {}
1725
sage: phi._EllipticCurveIsogeny__sort_kernel_list()
1726
1727
"""
1728
1729
a1,a2,a3,a4,a6 = self.__E1.ainvs()
1730
1731
v = 0
1732
w = 0
1733
1734
for Q in self.__kernel_list:
1735
1736
if Q.is_zero():
1737
continue
1738
1739
(xQ,yQ) = Q.xy()
1740
1741
gxQ = 3*xQ**2 + 2*a2*xQ + a4 - a1*yQ
1742
gyQ = -2*yQ - a1*xQ - a3
1743
1744
uQ = gyQ**2
1745
1746
# sort torsion points:
1747
if (2*yQ == -a1*xQ - a3): # Q is 2-torsion
1748
vQ = gxQ
1749
self.__kernel_2tor[xQ] = (xQ,yQ,gxQ,gyQ,vQ,uQ)
1750
v = v + vQ
1751
w = w + (uQ + xQ*vQ)
1752
elif (not self.__kernel_non2tor.has_key(xQ)): # Q is not a 2-torsion
1753
vQ = 2*gxQ - a1*gyQ
1754
self.__kernel_non2tor[xQ] = (xQ,yQ,gxQ,gyQ,vQ,uQ)
1755
v = v + vQ
1756
w = w + (uQ + xQ*vQ)
1757
1758
self.__v = v
1759
self.__w = w
1760
1761
return
1762
1763
1764
#
1765
# Velu's formula computing the codomain curve
1766
#
1767
def __compute_E2_via_velu(self):
1768
r"""
1769
Private function that computes the codomain via Velu's
1770
formulas.
1771
1772
EXAMPLES:
1773
1774
The following example inherently exercises this function::
1775
1776
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
1777
sage: P = E((4,2))
1778
sage: phi = EllipticCurveIsogeny(E, P);
1779
sage: phi.codomain()
1780
Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7
1781
sage: phi._EllipticCurveIsogeny__compute_E2_via_velu()
1782
Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7
1783
1784
"""
1785
v = self.__v
1786
w = self.__w
1787
1788
return compute_codomain_formula(self.__E1, v,w)
1789
1790
1791
def __velu_sum_helper(self, Qvalues, a1, a3, x, y):
1792
r"""
1793
Private function for Velu's formulas, helper function to help
1794
perform the summation.
1795
1796
EXAMPLES:
1797
1798
The following example inherently exercises this function::
1799
1800
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
1801
sage: P = E((4,2))
1802
sage: phi = EllipticCurveIsogeny(E, P);
1803
sage: Q = E((0,0)); phi(Q)
1804
(0 : 0 : 1)
1805
sage: phi.rational_maps()
1806
((x^4 - 2*x^3 + x^2 - 3*x)/(x^3 - 2*x^2 + 3*x - 2),
1807
(x^5*y - 2*x^3*y - x^2*y - 2*x*y + 2*y)/(x^5 + 3*x^3 + 3*x^2 + x - 1))
1808
1809
sage: E = EllipticCurve(GF(7), [0,0,0,1,0])
1810
sage: phi = EllipticCurveIsogeny(E, E((0,0)) )
1811
sage: Qvals = phi._EllipticCurveIsogeny__kernel_2tor[0]
1812
sage: phi._EllipticCurveIsogeny__velu_sum_helper(Qvals, 0, 0, 5, 5)
1813
(3, 3)
1814
sage: R.<x,y> = GF(7)[]
1815
sage: phi._EllipticCurveIsogeny__velu_sum_helper(Qvals, 0, 0, x, y)
1816
(1/x, y/x^2)
1817
1818
"""
1819
xQ = Qvalues[0]
1820
yQ = Qvalues[1]
1821
gxQ = Qvalues[2]
1822
gyQ = Qvalues[3]
1823
vQ = Qvalues[4]
1824
uQ = Qvalues[5]
1825
1826
t1 = x - xQ
1827
inv_t1 = t1**-1
1828
inv_t1_2 = inv_t1**2
1829
inv_t1_3 = inv_t1_2*inv_t1
1830
1831
tX = vQ*inv_t1 + uQ*(inv_t1_2)
1832
1833
tY0 = uQ*(2*y + a1*x + a3)
1834
tY1 = vQ*(a1*t1 + y - yQ)
1835
tY2 = a1*uQ - gxQ*gyQ
1836
1837
tY = ( tY0*inv_t1_3 + (tY1 + tY2)*inv_t1_2 )
1838
1839
return (tX, tY)
1840
1841
1842
def __compute_via_velu_numeric(self, xP, yP):
1843
r"""
1844
Private function that sorts the list of points in the kernel
1845
(for Velu's formulas). Sorts out the 2 torsion points, and
1846
puts them in a diction
1847
1848
EXAMPLES:
1849
1850
The following example inherently exercises this function::
1851
1852
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
1853
sage: P = E((4,2))
1854
sage: phi = EllipticCurveIsogeny(E, P);
1855
sage: Q = E((0,0)); phi(Q)
1856
(0 : 0 : 1)
1857
sage: Q = E((-1,0)); phi(Q)
1858
(0 : 0 : 1)
1859
sage: phi._EllipticCurveIsogeny__compute_via_velu_numeric(0, 0)
1860
(0, 0)
1861
sage: phi._EllipticCurveIsogeny__compute_via_velu_numeric(-1, 0)
1862
(0, 0)
1863
1864
"""
1865
# first check if the point is in the kernel
1866
if ( self.__kernel_2tor.has_key(xP) or self.__kernel_non2tor.has_key(xP) ) :
1867
return self.__intermediate_codomain(0)
1868
1869
outP = self.__compute_via_velu(xP,yP)
1870
1871
return outP
1872
1873
1874
def __compute_via_velu(self, xP, yP):
1875
r"""
1876
Private function for Velu's formulas, to perform the
1877
summation.
1878
1879
EXAMPLES:
1880
1881
The following example inherently exercises this function::
1882
1883
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
1884
sage: P = E((4,2))
1885
sage: phi = EllipticCurveIsogeny(E, P);
1886
sage: Q = E((0,0)); phi(Q)
1887
(0 : 0 : 1)
1888
sage: phi.rational_maps()
1889
((x^4 - 2*x^3 + x^2 - 3*x)/(x^3 - 2*x^2 + 3*x - 2),
1890
(x^5*y - 2*x^3*y - x^2*y - 2*x*y + 2*y)/(x^5 + 3*x^3 + 3*x^2 + x - 1))
1891
sage: phi._EllipticCurveIsogeny__compute_via_velu(0, 0)
1892
(0, 0)
1893
sage: R.<x,y> = GF(7)[]
1894
sage: phi._EllipticCurveIsogeny__compute_via_velu(x, y)
1895
((x^4 - 2*x^3 + x^2 - 3*x)/(x^3 - 2*x^2 + 3*x - 2),
1896
(x^5*y - 2*x^3*y - x^2*y - 2*x*y + 2*y)/(x^5 + 3*x^3 + 3*x^2 + x - 1))
1897
1898
"""
1899
1900
ker_2tor = self.__kernel_2tor
1901
ker_non2tor = self.__kernel_non2tor
1902
1903
X = 0
1904
Y = 0
1905
1906
a1 = self.__E1.a1()
1907
a3 = self.__E1.a3()
1908
1909
# next iterate over the 2torsion points of the kernel
1910
for Qvalues in ker_2tor.itervalues():
1911
(tX, tY) = self.__velu_sum_helper(Qvalues, a1, a3, xP, yP)
1912
X = X + tX
1913
Y = Y + tY
1914
1915
for Qvalues in ker_non2tor.itervalues():
1916
(tX, tY) = self.__velu_sum_helper(Qvalues, a1, a3, xP, yP)
1917
X = X + tX
1918
Y = Y + tY
1919
1920
X = xP + X
1921
Y = yP - Y
1922
1923
return (X,Y)
1924
1925
1926
def __initialize_rational_maps_via_velu(self):
1927
r"""
1928
Private function for Velu's formulas, helper function to initialize the rational maps.
1929
1930
EXAMPLES:
1931
1932
The following example inherently exercises this function::
1933
1934
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
1935
sage: P = E((4,2))
1936
sage: phi = EllipticCurveIsogeny(E, P);
1937
sage: phi.rational_maps()
1938
((x^4 - 2*x^3 + x^2 - 3*x)/(x^3 - 2*x^2 + 3*x - 2),
1939
(x^5*y - 2*x^3*y - x^2*y - 2*x*y + 2*y)/(x^5 + 3*x^3 + 3*x^2 + x - 1))
1940
sage: phi._EllipticCurveIsogeny__initialize_rational_maps_via_velu()
1941
((x^4 - 2*x^3 + x^2 - 3*x)/(x^3 - 2*x^2 + 3*x - 2),
1942
(x^5*y - 2*x^3*y - x^2*y - 2*x*y + 2*y)/(x^5 + 3*x^3 + 3*x^2 + x - 1))
1943
1944
"""
1945
1946
x = self.__x_var
1947
y = self.__y_var
1948
1949
return self.__compute_via_velu(x,y)
1950
1951
1952
def __init_kernel_polynomial_velu(self):
1953
r"""
1954
Private function for Velu's formulas, helper function to
1955
initialize the rational maps.
1956
1957
EXAMPLES:
1958
1959
The following example inherently exercises this function::
1960
1961
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
1962
sage: P = E((4,2))
1963
sage: phi = EllipticCurveIsogeny(E, P);
1964
sage: phi.kernel_polynomial()
1965
x^2 + 2*x + 4
1966
sage: phi._EllipticCurveIsogeny__init_kernel_polynomial_velu()
1967
[4, 2, 1]
1968
1969
"""
1970
1971
poly_ring = self.__poly_ring
1972
x = self.__x_var
1973
1974
invX = 0
1975
1976
if (self.__pre_isomorphism is not None):
1977
pre_isom = self.__pre_isomorphism
1978
u = pre_isom.u
1979
r = pre_isom.r
1980
invX = (u**2)*x + r
1981
else:
1982
invX = x
1983
1984
psi = poly_ring(1)
1985
1986
for Qvalues in self.__kernel_2tor.itervalues():
1987
xQ = invX(x=Qvalues[0])
1988
psi = psi*(x - xQ)
1989
1990
for Qvalues in self.__kernel_non2tor.itervalues():
1991
xQ = invX(x=Qvalues[0])
1992
psi = psi*(x - xQ)
1993
1994
ker_poly_list = psi.univariate_polynomial().list()
1995
1996
self.__kernel_polynomial_list = ker_poly_list
1997
self.__kernel_polynomial = psi
1998
1999
return ker_poly_list
2000
2001
2002
2003
###################################
2004
# Kohel's Variant of Velu's Formula
2005
###################################
2006
2007
def __init_from_kernel_polynomial(self, kernel_polynomial, degree=None):
2008
r"""
2009
Private function that initializes the isogeny from a kernel
2010
polynomial.
2011
2012
EXAMPLES:
2013
2014
These examples inherently exercise this private function::
2015
2016
sage: R.<x> = GF(7)[]
2017
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
2018
sage: phi = EllipticCurveIsogeny(E, x);phi
2019
Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 4*x over Finite Field of size 7
2020
2021
sage: phi._EllipticCurveIsogeny__init_from_kernel_polynomial(x)
2022
2023
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2024
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3); phi
2025
Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 1 over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 4*x + 2 over Finite Field of size 7
2026
2027
sage: phi._EllipticCurveIsogeny__init_from_kernel_polynomial(x+6, degree=3)
2028
2029
"""
2030
2031
poly_ring = self.__poly_ring
2032
x = self.__x_var
2033
2034
E = self.__E1
2035
2036
if(is_Polynomial(kernel_polynomial)):
2037
kernel_polynomial = kernel_polynomial.list()
2038
2039
n = len(kernel_polynomial)-1
2040
2041
if kernel_polynomial[-1] != 1:
2042
raise ValueError, "The kernel polynomial must be monic."
2043
2044
self.__kernel_polynomial_list = kernel_polynomial
2045
2046
psi = 0
2047
for j in xrange(len(kernel_polynomial)):
2048
psi = psi*x + kernel_polynomial[n-j]
2049
2050
2051
#
2052
# Determine if kernel polynomial is entirely a two torsion
2053
#
2054
psi_G = two_torsion_part(E, poly_ring, psi, degree);
2055
2056
# force this polynomial to be monic:
2057
psi_G = psi_G/psi_G.univariate_polynomial().leading_coefficient()
2058
2059
if (0 != psi_G.degree()): # even degree case
2060
2061
psi_quo = poly_ring(psi/psi_G)
2062
2063
if (0 != psi_quo.degree()):
2064
raise NotImplementedError, "For basic Kohel's algorithm, if the kernel degree is even then the kernel must be contained in the two torsion."
2065
2066
(phi, omega, v, w, n, d) = self.__init_even_kernel_polynomial(E, psi_G)
2067
2068
else: # odd degree case
2069
2070
(phi, omega, v, w, n, d) = self.__init_odd_kernel_polynomial(E, psi)
2071
2072
2073
#
2074
# Set up the necessary instance variables
2075
#
2076
2077
self.__kernel_polynomial = psi
2078
self.__inner_kernel_polynomial = psi
2079
2080
self.__n = n
2081
self.__degree = d
2082
2083
self.__psi = psi
2084
self.__phi = phi
2085
self.__omega = omega
2086
2087
self.__v = v
2088
self.__w = w
2089
2090
return
2091
2092
2093
def __init_even_kernel_polynomial(self, E, psi_G):
2094
r"""
2095
Private function that initializes the isogeny from a kernel
2096
polynomial, for Kohel's algorithm in the even degree case.
2097
2098
EXAMPLES:
2099
2100
These examples inherently exercise this private function::
2101
2102
sage: R.<x> = GF(7)[]
2103
sage: E = EllipticCurve(GF(7), [-1,0])
2104
sage: phi = EllipticCurveIsogeny(E, x);phi
2105
Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 4*x over Finite Field of size 7
2106
2107
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import two_torsion_part
2108
sage: psig = two_torsion_part(E,R,x,None)(phi._EllipticCurveIsogeny__x_var)
2109
sage: phi._EllipticCurveIsogeny__init_even_kernel_polynomial(E,psig)
2110
(x^3 - x, x^3*y + x*y, 6, 0, 1, 2)
2111
2112
sage: F = GF(2^4, 'alpha'); R.<x> = F[]
2113
sage: E = EllipticCurve(F, [1,1,0,1,0])
2114
sage: phi = EllipticCurveIsogeny(E, x); phi
2115
Isogeny of degree 2 from Elliptic Curve defined by y^2 + x*y = x^3 + x^2 + x over Finite Field in alpha of size 2^4 to Elliptic Curve defined by y^2 + x*y = x^3 + x^2 + 1 over Finite Field in alpha of size 2^4
2116
2117
sage: psig = two_torsion_part(E,R,x,None)(phi._EllipticCurveIsogeny__x_var)
2118
sage: phi._EllipticCurveIsogeny__init_even_kernel_polynomial(E,psig)
2119
(x^3 + x, x^3*y + x^2 + x*y, 1, 0, 1, 2)
2120
2121
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2122
sage: R.<x> = GF(7)[]
2123
sage: f = x^3 + 6*x^2 + 1
2124
sage: phi = EllipticCurveIsogeny(E, f); phi
2125
Isogeny of degree 4 from Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 1 over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 2*x + 5 over Finite Field of size 7
2126
sage: psig = two_torsion_part(E,R,f,None)
2127
sage: psig = two_torsion_part(E,R,f,None)(phi._EllipticCurveIsogeny__x_var)
2128
sage: phi._EllipticCurveIsogeny__init_even_kernel_polynomial(E,psig)
2129
(x^7 - 2*x^6 + 2*x^5 - x^4 + 3*x^3 - 2*x^2 - x + 3,
2130
x^9*y - 3*x^8*y + 2*x^7*y - 3*x^3*y + 2*x^2*y + x*y - y,
2131
1,
2132
6,
2133
3,
2134
4)
2135
2136
2137
"""
2138
2139
2140
#check if the polynomial really divides the two_torsion_polynomial
2141
if self.__check and E.division_polynomial(2, x=self.__x_var) % psi_G != 0 :
2142
raise ValueError, "The polynomial does not define a finite subgroup of the elliptic curve."
2143
2144
n = psi_G.degree()
2145
d = n+1
2146
2147
base_field = self.__base_field
2148
char = base_field.characteristic()
2149
2150
x = self.__x_var
2151
y = self.__y_var
2152
2153
a1,a2,a3,a4,a6 = E.ainvs()
2154
2155
b2 = E.b2()
2156
b4 = E.b4()
2157
2158
if (1 == n):
2159
x0 = -psi_G.constant_coefficient()
2160
2161
# determine y0
2162
if (2 == char):
2163
y0 = (x0**3 + a2*x0**2 + a4*x0 + a6).sqrt()
2164
else:
2165
y0 = -(a1*x0 + a3)/2
2166
2167
(v,w) = compute_vw_kohel_even_deg1(x0,y0,a1,a2,a4)
2168
2169
phi = (x*psi_G + v)*psi_G
2170
omega = (y*psi_G**2 - v*(a1*psi_G + (y - y0)))*psi_G
2171
2172
elif (3 == n):
2173
s = psi_G.univariate_polynomial().list()
2174
s1 = -s[n-1]
2175
s2 = s[n-2]
2176
s3 = -s[n-3]
2177
2178
psi_G_pr = psi_G.derivative(x)
2179
psi_G_prpr = psi_G_pr.derivative(x)
2180
2181
phi = (psi_G_pr**2) + (-2*psi_G_prpr + (4*x - s1))*psi_G
2182
2183
phi_pr = phi.derivative(x)
2184
2185
psi_2 = 2*y + a1*x + a3
2186
2187
omega = (psi_2*(phi_pr*psi_G - phi*psi_G_pr) - (a1*phi + a3*psi_G)*psi_G)/2
2188
2189
phi = phi*psi_G
2190
omega = omega*psi_G
2191
2192
(v,w) = compute_vw_kohel_even_deg3(b2,b4,s1,s2,s3)
2193
2194
else:
2195
raise ValueError, "input polynomial must be of degree 1 or 3, not %d" % n
2196
2197
return (phi, omega, v, w, n, d)
2198
2199
2200
def __init_odd_kernel_polynomial(self, E, psi):
2201
r"""
2202
Private function that initializes the isogeny from a kernel
2203
polynomial.
2204
2205
EXAMPLES:
2206
2207
These examples inherently exercise this private function::
2208
2209
sage: R.<x> = GF(7)[]
2210
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2211
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3); phi
2212
Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 1 over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 4*x + 2 over Finite Field of size 7
2213
2214
sage: R.<x,y> = GF(7)[]
2215
sage: phi._EllipticCurveIsogeny__init_odd_kernel_polynomial(E, x+6)
2216
(x^3 - 2*x^2 + 3*x + 2, x^3*y - 3*x^2*y + x*y, 2, 6, 1, 3)
2217
2218
sage: F = GF(2^4, 'alpha'); R.<x> = F[]
2219
sage: alpha = F.gen()
2220
sage: E = EllipticCurve(F, [1,1,F.gen(),F.gen()^2+1,1])
2221
sage: f = x + alpha^2 + 1
2222
sage: phi = EllipticCurveIsogeny(E, f); phi
2223
Isogeny of degree 3 from Elliptic Curve defined by y^2 + x*y + alpha*y = x^3 + x^2 + (alpha^2+1)*x + 1 over Finite Field in alpha of size 2^4 to Elliptic Curve defined by y^2 + x*y + alpha*y = x^3 + x^2 + alpha*x + alpha^3 over Finite Field in alpha of size 2^4
2224
2225
sage: R.<x,y> = F[]
2226
sage: f = x + alpha^2 + 1
2227
sage: phi._EllipticCurveIsogeny__init_odd_kernel_polynomial(E, f)
2228
(x^3 + (alpha^2 + 1)*x + (alpha^3 + alpha^2 + alpha),
2229
x^3*y + (alpha^2 + 1)*x^2*y + (alpha^2 + alpha + 1)*x^2 + (alpha^2 + 1)*x*y + (alpha^2 + alpha)*x + (alpha)*y + (alpha),
2230
alpha^2 + alpha + 1,
2231
alpha^3 + alpha^2 + alpha,
2232
1,
2233
3)
2234
2235
sage: E = EllipticCurve(j=-262537412640768000)
2236
sage: f = (E.isogenies_prime_degree()[0]).kernel_polynomial()
2237
sage: f.degree()
2238
81
2239
sage: E.isogeny(kernel=f) # long time (21s)
2240
Isogeny of degree 163 from Elliptic Curve defined by y^2 + y = x^3 - 2174420*x + 1234136692 over Rational Field to Elliptic Curve defined by y^2 + y = x^3 - 57772164980*x - 5344733777551611 over Rational Field
2241
2242
"""
2243
n = psi.degree()
2244
d = 2*n + 1
2245
2246
# check if the polynomial really divides the torsion polynomial :
2247
if self.__check:
2248
alpha = psi.parent().quotient(psi).gen()
2249
if not E.division_polynomial(d, x=alpha).is_zero():
2250
raise ValueError, "The polynomial does not define a finite subgroup of the elliptic curve."
2251
2252
x = self.__x_var
2253
2254
b2 = E.b2()
2255
b4 = E.b4()
2256
b6 = E.b6()
2257
2258
psi_coeffs = psi.univariate_polynomial().list()
2259
2260
s1 = 0; s2 = 0; s3 = 0
2261
2262
if (1 <= n):
2263
s1 = -psi_coeffs[n-1]
2264
2265
if (2 <= n):
2266
s2 = psi_coeffs[n-2]
2267
2268
if (3 <= n):
2269
s3 = -psi_coeffs[n-3]
2270
2271
# initializing these allows us to calculate E2.
2272
(v,w) = compute_vw_kohel_odd(b2,b4,b6,s1,s2,s3,n);
2273
2274
# initialize the polynomial temporary variables
2275
2276
psi_pr = psi.derivative(x)
2277
psi_prpr = psi_pr.derivative(x)
2278
2279
phi = (4*x**3 + b2*x**2 + 2*b4*x + b6)*(psi_pr**2 - psi_prpr*psi) - \
2280
(6*x**2 + b2*x + b4)*psi_pr*psi + (d*x - 2*s1)*psi**2
2281
2282
phi_pr = phi.derivative(x)
2283
2284
omega = 0
2285
if (2 != self.__base_field.characteristic()):
2286
omega = self.__compute_omega_fast(E, psi, psi_pr, phi, phi_pr)
2287
else:
2288
omega = self.__compute_omega_general(E, psi, psi_pr, phi, phi_pr)
2289
2290
return (phi, omega, v, w, n, d)
2291
2292
2293
#
2294
# This is the fast omega computation that works when characteristic is not 2
2295
#
2296
def __compute_omega_fast(self, E, psi, psi_pr, phi, phi_pr):
2297
r"""
2298
Private function that initializes the omega polynomial (from
2299
Kohel's formulas) in the case that the characteristic of the
2300
underlying field is not 2.
2301
2302
EXAMPLES:
2303
2304
These examples inherently exercise this private function::
2305
2306
sage: R.<x> = GF(7)[]
2307
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2308
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3); phi
2309
Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 1 over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 4*x + 2 over Finite Field of size 7
2310
2311
sage: R.<x,y> = GF(7)[]
2312
sage: psi = phi._EllipticCurveIsogeny__psi
2313
sage: psi_pr = psi.derivative(x)
2314
sage: fi = phi._EllipticCurveIsogeny__phi
2315
sage: fi_pr = fi.derivative(x)
2316
sage: phi._EllipticCurveIsogeny__compute_omega_fast(E, psi, psi_pr, fi, fi_pr)
2317
x^3*y - 3*x^2*y + x*y
2318
2319
"""
2320
2321
a1 = E.a1()
2322
a3 = E.a3()
2323
2324
x = self.__x_var; # 'x'
2325
y = self.__y_var; # 'y'
2326
2327
psi_2 = 2*y + a1*x + a3
2328
2329
# note, the formula below is correct
2330
# the formula in Kohel's thesis has some typos
2331
# notably the first plus sign should be a minus
2332
# as it is here below.
2333
2334
omega = phi_pr*psi*psi_2/2 - phi*psi_pr*psi_2 - \
2335
(a1*phi + a3*psi**2)*psi/2
2336
2337
return omega
2338
2339
2340
def __compute_omega_general(self, E, psi, psi_pr, phi, phi_pr):
2341
r"""
2342
Private function that initializes the omega polynomial (from
2343
Kohel's formulas) in the case of general characteristic of the
2344
underlying field.
2345
2346
EXAMPLES:
2347
2348
These examples inherently exercise this private function::
2349
2350
sage: F = GF(2^4, 'alpha'); R.<x> = F[]
2351
sage: alpha = F.gen()
2352
sage: E = EllipticCurve(F, [1,1,F.gen(),F.gen()^2+1,1])
2353
sage: f = x + alpha^2 + 1
2354
sage: phi = EllipticCurveIsogeny(E, f); phi
2355
Isogeny of degree 3 from Elliptic Curve defined by y^2 + x*y + alpha*y = x^3 + x^2 + (alpha^2+1)*x + 1 over Finite Field in alpha of size 2^4 to Elliptic Curve defined by y^2 + x*y + alpha*y = x^3 + x^2 + alpha*x + alpha^3 over Finite Field in alpha of size 2^4
2356
2357
sage: R.<x,y> = F[]
2358
sage: psi = phi._EllipticCurveIsogeny__psi
2359
sage: psi_pr = psi.derivative(x)
2360
sage: fi = phi._EllipticCurveIsogeny__phi
2361
sage: fi_pr = fi.derivative(x)
2362
sage: phi._EllipticCurveIsogeny__compute_omega_general(E, psi, psi_pr, fi, fi_pr)
2363
x^3*y + (alpha^2 + 1)*x^2*y + (alpha^2 + alpha + 1)*x^2 + (alpha^2 + 1)*x*y + (alpha^2 + alpha)*x + (alpha)*y + (alpha)
2364
2365
A bug fixed in ticket #7907::
2366
2367
sage: F = GF(128,'a')
2368
sage: a = F.gen()
2369
sage: E = EllipticCurve([1,0,0,0,(a**6+a**4+a**2+a)])
2370
sage: x = polygen(F)
2371
sage: ker = (x^6 + (a^6 + a^5 + a^4 + a^3 + a^2 + a)*x^5 + (a^6 + a^5 + a^2 + 1)*x^4 + (a^6 + a^5 + a^4 + a^3 + a^2 + 1)*x^3 + (a^6 + a^3 + a)*x^2 + (a^4 + a^3 + 1)*x + a^5 + a^4 + a)
2372
sage: E.isogeny(ker)
2373
Isogeny of degree 13 from Elliptic Curve defined by y^2 + x*y = x^3 + (a^6+a^4+a^2+a) over Finite Field in a of size 2^7 to Elliptic Curve defined by y^2 + x*y = x^3 + (a^6+a^5+a^4+a^3+a^2+a)*x + (a^5+a^3) over Finite Field in a of size 2^7
2374
2375
2376
"""
2377
2378
a1,a2,a3,a4,a6 = E.ainvs()
2379
2380
b2 = E.b2()
2381
b4 = E.b4()
2382
2383
n = psi.degree()
2384
d = 2*n+1
2385
2386
x = self.__x_var
2387
y = self.__y_var
2388
2389
psi_2 = 2*y + a1*x + a3
2390
2391
psi_coeffs = psi.univariate_polynomial().list()
2392
2393
if (0 < n):
2394
s1 = -psi_coeffs[n-1]
2395
else:
2396
s1 = 0
2397
2398
psi_prpr = 0
2399
cur_x_pow = 1
2400
2401
#
2402
# Note: we now get the "derivatives" of psi
2403
# these are not actually the derivatives
2404
# furthermore, the formulas in Kohel's
2405
# thesis are wrong, the correct formulas
2406
# are coded below
2407
#
2408
from sage.rings.arith import binomial
2409
2410
for j in xrange(0,n-1):
2411
psi_prpr = psi_prpr + \
2412
binomial(j+2,2)*psi_coeffs[(j+2)]*cur_x_pow
2413
cur_x_pow = x*cur_x_pow
2414
2415
psi_prprpr = 0
2416
cur_x_pow = 1
2417
2418
for j in xrange(0,n-2):
2419
psi_prprpr = psi_prprpr + \
2420
(3*binomial(j+3,3))*psi_coeffs[(j+3)]*cur_x_pow
2421
cur_x_pow = x*cur_x_pow
2422
2423
2424
omega = phi_pr*psi*y - phi*psi_pr*psi_2 + \
2425
((a1*x + a3)*(psi_2**2)*(psi_prpr*psi_pr-psi_prprpr*psi) + \
2426
(a1*psi_2**2 - 3*(a1*x + a3)*(6*x**2 + b2*x + b4))*psi_prpr*psi + \
2427
(a1*x**3 + 3*a3*x**2 + (2*a2*a3 - a1*a4)*x + (a3*a4 - 2*a1*a6))*psi_pr**2 + \
2428
(-(3*a1*x**2 + 6*a3*x + (-a1*a4 + 2*a2*a3)) + \
2429
(a1*x + a3)*(d*x - 2*s1) )*psi_pr*psi + (a1*s1 + a3*n)*psi**2)*psi
2430
2431
return omega
2432
2433
2434
def __compute_via_kohel_numeric(self, xP, yP):
2435
r"""
2436
Private function that computes a numeric result of this
2437
isogeny (via Kohel's formulas.)
2438
2439
EXAMPLES:
2440
2441
These examples inherently exercise this private function::
2442
2443
sage: R.<x> = GF(7)[]
2444
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2445
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3)
2446
sage: P = E((0,1)); phi(P)
2447
(2 : 0 : 1)
2448
sage: P = E((1,1)); phi(P)
2449
(0 : 1 : 0)
2450
sage: phi._EllipticCurveIsogeny__compute_via_kohel_numeric(0, 1)
2451
(2, 0)
2452
sage: phi._EllipticCurveIsogeny__compute_via_kohel_numeric(1, 1)
2453
(0 : 1 : 0)
2454
2455
"""
2456
2457
# first check if this is a kernel point
2458
# to avoid a divide by 0 error later
2459
if(0 == self.__inner_kernel_polynomial(x=xP)):
2460
return self.__intermediate_codomain(0)
2461
2462
(xP_out, yP_out) = self.__compute_via_kohel(xP,yP)
2463
2464
# for some dang reason in some cases
2465
# when the base_field is a number field
2466
# xP_out and yP_out do not get evaluated to field elements
2467
# but rather constant polynomials.
2468
# So in this case, we do some explicit casting to make sure
2469
# everything comes out right
2470
2471
if is_NumberField(self.__base_field) and (1 < self.__base_field.degree()) :
2472
xP_out = self.__poly_ring(xP_out).constant_coefficient()
2473
yP_out = self.__poly_ring(yP_out).constant_coefficient()
2474
2475
return (xP_out,yP_out)
2476
2477
2478
def __compute_via_kohel(self, xP, yP):
2479
r"""
2480
Private function that applies Kohel's formulas.
2481
2482
EXAMPLES:
2483
2484
These examples inherently exercise this private function::
2485
2486
sage: R.<x> = GF(7)[]
2487
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2488
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3)
2489
sage: P = E((0,1)); phi(P)
2490
(2 : 0 : 1)
2491
sage: phi.rational_maps()
2492
((x^3 - 2*x^2 + 3*x + 2)/(x^2 - 2*x + 1),
2493
(x^3*y - 3*x^2*y + x*y)/(x^3 - 3*x^2 + 3*x - 1))
2494
sage: phi._EllipticCurveIsogeny__compute_via_kohel(0,1)
2495
(2, 0)
2496
sage: R.<x,y> = GF(7)[]
2497
sage: phi._EllipticCurveIsogeny__compute_via_kohel(x,y)
2498
((x^3 - 2*x^2 + 3*x + 2)/(x^2 - 2*x + 1),
2499
(x^3*y - 3*x^2*y + x*y)/(x^3 - 3*x^2 + 3*x - 1))
2500
2501
"""
2502
2503
x = self.__x_var
2504
y = self.__y_var
2505
2506
psi_out = self.__psi(xP,yP)
2507
phi_out = self.__phi(xP,yP)
2508
omega_out =self.__omega(xP, yP)
2509
2510
psi_inv_out = 1/psi_out
2511
2512
psi_inv_sq_out = psi_inv_out**2
2513
2514
X_out = phi_out*(psi_inv_sq_out)
2515
Y_out = omega_out*(psi_inv_sq_out*psi_inv_out)
2516
2517
return (X_out, Y_out)
2518
2519
2520
def __initialize_rational_maps_via_kohel(self):
2521
r"""
2522
Private function that computes and initializes the rational
2523
maps of this isogeny.
2524
2525
EXAMPLES:
2526
2527
These examples inherently exercise this private function::
2528
2529
sage: R.<x> = GF(7)[]
2530
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2531
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3)
2532
sage: phi.rational_maps()
2533
((x^3 - 2*x^2 + 3*x + 2)/(x^2 - 2*x + 1),
2534
(x^3*y - 3*x^2*y + x*y)/(x^3 - 3*x^2 + 3*x - 1))
2535
sage: phi._EllipticCurveIsogeny__initialize_rational_maps_via_kohel()
2536
((x^3 - 2*x^2 + 3*x + 2)/(x^2 - 2*x + 1),
2537
(x^3*y - 3*x^2*y + x*y)/(x^3 - 3*x^2 + 3*x - 1))
2538
2539
2540
"""
2541
x = self.__x_var
2542
y = self.__y_var
2543
2544
(X,Y) = self.__compute_via_kohel(x,y)
2545
2546
return (X,Y)
2547
2548
2549
#
2550
# Kohel's formula computing the codomain curve
2551
#
2552
def __compute_E2_via_kohel(self):
2553
r"""
2554
Private function that computes and initializes the codomain of
2555
the isogeny (via Kohel's.)
2556
2557
EXAMPLES:
2558
2559
These examples inherently exercise this private function::
2560
2561
sage: R.<x> = GF(7)[]
2562
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2563
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3)
2564
sage: phi.codomain()
2565
Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 4*x + 2 over Finite Field of size 7
2566
sage: phi._EllipticCurveIsogeny__compute_E2_via_kohel()
2567
Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 4*x + 2 over Finite Field of size 7
2568
2569
"""
2570
2571
v = self.__v
2572
w = self.__w
2573
2574
return compute_codomain_formula(self.__E1, v,w)
2575
2576
2577
2578
#
2579
# public isogeny methods
2580
#
2581
2582
def domain(self):
2583
r"""
2584
Returns the domain curve of this isogeny.
2585
2586
EXAMPLES::
2587
2588
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
2589
sage: phi = EllipticCurveIsogeny(E, E(0,0))
2590
sage: phi.domain() == E
2591
True
2592
2593
sage: E = EllipticCurve(GF(31), [1,0,0,1,2])
2594
sage: phi = EllipticCurveIsogeny(E, [17, 1])
2595
sage: phi.domain()
2596
Elliptic Curve defined by y^2 + x*y = x^3 + x + 2 over Finite Field of size 31
2597
2598
"""
2599
return self.__E1
2600
2601
2602
def codomain(self):
2603
r"""
2604
Returns the codomain (range) curve of this isogeny.
2605
2606
EXAMPLES::
2607
2608
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
2609
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
2610
sage: phi.codomain()
2611
Elliptic Curve defined by y^2 = x^3 - 4*x over Rational Field
2612
2613
sage: E = EllipticCurve(GF(31), [1,0,0,1,2])
2614
sage: phi = EllipticCurveIsogeny(E, [17, 1])
2615
sage: phi.codomain()
2616
Elliptic Curve defined by y^2 + x*y = x^3 + 24*x + 6 over Finite Field of size 31
2617
2618
"""
2619
return self.__E2
2620
2621
2622
def degree(self):
2623
r"""
2624
Returns the degree of this isogeny.
2625
2626
EXAMPLES::
2627
2628
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
2629
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
2630
sage: phi.degree()
2631
2
2632
sage: phi = EllipticCurveIsogeny(E, [0,1,0,1])
2633
sage: phi.degree()
2634
4
2635
2636
sage: E = EllipticCurve(GF(31), [1,0,0,1,2])
2637
sage: phi = EllipticCurveIsogeny(E, [17, 1])
2638
sage: phi.degree()
2639
3
2640
2641
"""
2642
return self.__degree
2643
2644
2645
def rational_maps(self):
2646
r"""
2647
This function returns this isogeny as a pair of rational maps.
2648
2649
EXAMPLES::
2650
2651
sage: E = EllipticCurve(QQ, [0,2,0,1,-1])
2652
sage: phi = EllipticCurveIsogeny(E, [1])
2653
sage: phi.rational_maps()
2654
(x, y)
2655
2656
sage: E = EllipticCurve(GF(17), [0,0,0,3,0])
2657
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
2658
sage: phi.rational_maps()
2659
((x^2 + 3)/x, (x^2*y - 3*y)/x^2)
2660
2661
2662
"""
2663
if (not self.__rational_maps_initialized):
2664
self.__initialize_rational_maps()
2665
return (self.__X_coord_rational_map, self.__Y_coord_rational_map)
2666
2667
2668
def is_separable(self):
2669
r"""
2670
This function returns a bool indicating whether or not this
2671
isogeny is separable.
2672
2673
This function always returns ``True`` as currently this class
2674
only implements separable isogenies.
2675
2676
EXAMPLES::
2677
2678
sage: E = EllipticCurve(GF(17), [0,0,0,3,0])
2679
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
2680
sage: phi.is_separable()
2681
True
2682
2683
sage: E = EllipticCurve('11a1')
2684
sage: phi = EllipticCurveIsogeny(E, E.torsion_points())
2685
sage: phi.is_separable()
2686
True
2687
2688
2689
"""
2690
return self.__separable
2691
2692
2693
def kernel_polynomial(self):
2694
r"""
2695
Returns the kernel polynomial of this isogeny.
2696
2697
EXAMPLES::
2698
2699
sage: E = EllipticCurve(QQ, [0,0,0,2,0])
2700
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
2701
sage: phi.kernel_polynomial()
2702
x
2703
2704
sage: E = EllipticCurve('11a1')
2705
sage: phi = EllipticCurveIsogeny(E, E.torsion_points())
2706
sage: phi.kernel_polynomial()
2707
x^2 - 21*x + 80
2708
2709
sage: E = EllipticCurve(GF(17), [1,-1,1,-1,1])
2710
sage: phi = EllipticCurveIsogeny(E, [1])
2711
sage: phi.kernel_polynomial()
2712
1
2713
2714
sage: E = EllipticCurve(GF(31), [0,0,0,3,0])
2715
sage: phi = EllipticCurveIsogeny(E, [0,3,0,1])
2716
sage: phi.kernel_polynomial()
2717
x^3 + 3*x
2718
2719
2720
"""
2721
if (self.__kernel_polynomial is None):
2722
self.__init_kernel_polynomial()
2723
2724
return self.__kernel_polynomial.univariate_polynomial()
2725
2726
2727
def set_pre_isomorphism(self, preWI):
2728
r"""
2729
Modifies this isogeny object to pre compose with the given
2730
Weierstrass isomorphism.
2731
2732
EXAMPLES::
2733
2734
sage: E = EllipticCurve(GF(31), [1,1,0,1,-1])
2735
sage: R.<x> = GF(31)[]
2736
sage: f = x^3 + 9*x^2 + x + 30
2737
sage: phi = EllipticCurveIsogeny(E, f)
2738
sage: Epr = E.short_weierstrass_model()
2739
sage: isom = Epr.isomorphism_to(E)
2740
sage: phi.set_pre_isomorphism(isom)
2741
sage: phi.rational_maps()
2742
((-6*x^4 - 3*x^3 + 12*x^2 + 10*x - 1)/(x^3 + x - 12),
2743
(3*x^7 + x^6*y - 14*x^6 - 3*x^5 + 5*x^4*y + 7*x^4 + 8*x^3*y - 8*x^3 - 5*x^2*y + 5*x^2 - 14*x*y + 14*x - 6*y - 6)/(x^6 + 2*x^4 + 7*x^3 + x^2 + 7*x - 11))
2744
sage: phi(Epr((0,22)))
2745
(13 : 21 : 1)
2746
sage: phi(Epr((3,7)))
2747
(14 : 17 : 1)
2748
2749
sage: E = EllipticCurve(GF(29), [0,0,0,1,0])
2750
sage: R.<x> = GF(29)[]
2751
sage: f = x^2 + 5
2752
sage: phi = EllipticCurveIsogeny(E, f)
2753
sage: phi
2754
Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 29 to Elliptic Curve defined by y^2 = x^3 + 20*x over Finite Field of size 29
2755
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
2756
sage: inv_isom = WeierstrassIsomorphism(E, (1,-2,5,10))
2757
sage: Epr = inv_isom.codomain().codomain()
2758
sage: isom = Epr.isomorphism_to(E)
2759
sage: phi.set_pre_isomorphism(isom); phi
2760
Isogeny of degree 5 from Elliptic Curve defined by y^2 + 10*x*y + 20*y = x^3 + 27*x^2 + 6 over Finite Field of size 29 to Elliptic Curve defined by y^2 = x^3 + 20*x over Finite Field of size 29
2761
sage: phi(Epr((12,1)))
2762
(26 : 0 : 1)
2763
sage: phi(Epr((2,9)))
2764
(0 : 0 : 1)
2765
sage: phi(Epr((21,12)))
2766
(3 : 0 : 1)
2767
sage: phi.rational_maps()[0]
2768
(x^5 - 10*x^4 - 6*x^3 - 7*x^2 - x + 3)/(x^4 - 8*x^3 + 5*x^2 - 14*x - 6)
2769
2770
sage: E = EllipticCurve('11a1')
2771
sage: R.<x> = QQ[]
2772
sage: f = x^2 - 21*x + 80
2773
sage: phi = EllipticCurveIsogeny(E, f); phi
2774
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
2775
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
2776
sage: Epr = E.short_weierstrass_model()
2777
sage: isom = Epr.isomorphism_to(E)
2778
sage: phi.set_pre_isomorphism(isom)
2779
sage: phi
2780
Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 - 13392*x - 1080432 over Rational Field to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field
2781
sage: phi(Epr((168,1188)))
2782
(0 : 1 : 0)
2783
2784
"""
2785
2786
WIdom = preWI.domain().codomain()
2787
WIcod = preWI.codomain().codomain()
2788
2789
if (type(preWI) != WeierstrassIsomorphism):
2790
raise ValueError, "Invalid parameter: isomorphism must be of type Weierstrass isomorphism."
2791
2792
if (self.__E1 != WIcod):
2793
raise ValueError, "Invalid parameter: isomorphism must have codomain curve equal to this isogenies' domain."
2794
2795
if (self.__pre_isomorphism is None):
2796
isom = preWI
2797
domain = WIdom
2798
else:
2799
isom = self.__pre_isomorphism*preWI
2800
domain = WIdom
2801
2802
self.__clear_cached_values()
2803
2804
self.__set_pre_isomorphism(domain, isom)
2805
2806
return
2807
2808
2809
def set_post_isomorphism(self, postWI):
2810
r"""
2811
Modifies this isogeny object to post compose with the given
2812
Weierstrass isomorphism.
2813
2814
EXAMPLES::
2815
2816
sage: E = EllipticCurve(j=GF(31)(0))
2817
sage: R.<x> = GF(31)[]
2818
sage: phi = EllipticCurveIsogeny(E, x+18)
2819
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
2820
sage: phi.set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(), (6,8,10,12)))
2821
sage: phi
2822
Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 31 to Elliptic Curve defined by y^2 + 24*x*y + 7*y = x^3 + 22*x^2 + 16*x + 20 over Finite Field of size 31
2823
2824
sage: E = EllipticCurve(j=GF(47)(0))
2825
sage: f = E.torsion_polynomial(3)/3
2826
sage: phi = EllipticCurveIsogeny(E, f)
2827
sage: E2 = phi.codomain()
2828
sage: post_isom = E2.isomorphism_to(E)
2829
sage: phi.set_post_isomorphism(post_isom)
2830
sage: phi.rational_maps() == E.multiplication_by_m(3)
2831
False
2832
sage: phi.switch_sign()
2833
sage: phi.rational_maps() == E.multiplication_by_m(3)
2834
True
2835
2836
Example over a number field::
2837
2838
sage: R.<x> = QQ[]
2839
sage: K.<a> = NumberField(x^2 + 2)
2840
sage: E = EllipticCurve(j=K(1728))
2841
sage: ker_list = E.torsion_points()
2842
sage: phi = EllipticCurveIsogeny(E, ker_list)
2843
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
2844
sage: post_isom = WeierstrassIsomorphism(phi.codomain(), (a,2,3,5))
2845
sage: phi
2846
Isogeny of degree 4 from Elliptic Curve defined by y^2 = x^3 + x over Number Field in a with defining polynomial x^2 + 2 to Elliptic Curve defined by y^2 = x^3 + (-44)*x + 112 over Number Field in a with defining polynomial x^2 + 2
2847
2848
"""
2849
2850
WIdom = postWI.domain().codomain()
2851
WIcod = postWI.codomain().codomain()
2852
2853
if (type(postWI) != WeierstrassIsomorphism):
2854
raise ValueError, "Invalid parameter: isomorphism must be of type Weierstrass isomorphism."
2855
2856
if (self.__E2 != WIdom):
2857
raise ValueError, "Invalid parameter: isomorphism must have domain curve equal to this isogenies' codomain."
2858
2859
if (self.__post_isomorphism is None):
2860
isom = postWI
2861
codomain = WIcod
2862
else:
2863
isom = postWI*self.__post_isomorphism
2864
codomain = WIcod
2865
2866
self.__clear_cached_values()
2867
2868
self.__set_post_isomorphism(codomain, isom)
2869
2870
return
2871
2872
2873
def get_pre_isomorphism(self):
2874
r"""
2875
Returns the pre-isomorphism of this isogeny. If there has
2876
been no pre-isomorphism set, this returns ``None``.
2877
2878
EXAMPLES::
2879
2880
sage: E = EllipticCurve(GF(31), [1,1,0,1,-1])
2881
sage: R.<x> = GF(31)[]
2882
sage: f = x^3 + 9*x^2 + x + 30
2883
sage: phi = EllipticCurveIsogeny(E, f)
2884
sage: phi.get_post_isomorphism()
2885
sage: Epr = E.short_weierstrass_model()
2886
sage: isom = Epr.isomorphism_to(E)
2887
sage: phi.set_pre_isomorphism(isom)
2888
sage: isom == phi.get_pre_isomorphism()
2889
True
2890
2891
sage: E = EllipticCurve(GF(83), [1,0,1,1,0])
2892
sage: R.<x> = GF(83)[]; f = x+24
2893
sage: phi = EllipticCurveIsogeny(E, f)
2894
sage: E2 = phi.codomain()
2895
sage: phi2 = EllipticCurveIsogeny(E, None, E2, 2)
2896
sage: phi2.get_pre_isomorphism()
2897
Generic morphism:
2898
From: Abelian group of points on Elliptic Curve defined by y^2 + x*y + y = x^3 + x over Finite Field of size 83
2899
To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 62*x + 74 over Finite Field of size 83
2900
Via: (u,r,s,t) = (1, 76, 41, 3)
2901
2902
2903
2904
"""
2905
return self.__pre_isomorphism
2906
2907
2908
def get_post_isomorphism(self):
2909
r"""
2910
Returns the post-isomorphism of this isogeny. If there has
2911
been no post-isomorphism set, this returns ``None``.
2912
2913
EXAMPLES::
2914
2915
sage: E = EllipticCurve(j=GF(31)(0))
2916
sage: R.<x> = GF(31)[]
2917
sage: phi = EllipticCurveIsogeny(E, x+18)
2918
sage: phi.get_post_isomorphism()
2919
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
2920
sage: isom = WeierstrassIsomorphism(phi.codomain(), (6,8,10,12))
2921
sage: phi.set_post_isomorphism(isom)
2922
sage: isom == phi.get_post_isomorphism()
2923
True
2924
2925
sage: E = EllipticCurve(GF(83), [1,0,1,1,0])
2926
sage: R.<x> = GF(83)[]; f = x+24
2927
sage: phi = EllipticCurveIsogeny(E, f)
2928
sage: E2 = phi.codomain()
2929
sage: phi2 = EllipticCurveIsogeny(E, None, E2, 2)
2930
sage: phi2.get_post_isomorphism()
2931
Generic morphism:
2932
From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 65*x + 69 over Finite Field of size 83
2933
To: Abelian group of points on Elliptic Curve defined by y^2 + x*y + 77*y = x^3 + 49*x + 28 over Finite Field of size 83
2934
Via: (u,r,s,t) = (1, 7, 42, 80)
2935
2936
"""
2937
return self.__post_isomorphism
2938
2939
2940
def switch_sign(self):
2941
r"""
2942
This function composes the isogeny with `[-1]` (flipping the
2943
coefficient between +/-1 on the `y` coordinate rational map).
2944
2945
EXAMPLES::
2946
2947
sage: E = EllipticCurve(GF(23), [0,0,0,1,0])
2948
sage: f = E.torsion_polynomial(3)/3
2949
sage: phi = EllipticCurveIsogeny(E, f, E)
2950
sage: phi.rational_maps() == E.multiplication_by_m(3)
2951
False
2952
sage: phi.switch_sign()
2953
sage: phi.rational_maps() == E.multiplication_by_m(3)
2954
True
2955
2956
sage: E = EllipticCurve(GF(17), [-2, 3, -5, 7, -11])
2957
sage: R.<x> = GF(17)[]
2958
sage: f = x+6
2959
sage: phi = EllipticCurveIsogeny(E, f)
2960
sage: phi
2961
Isogeny of degree 2 from Elliptic Curve defined by y^2 + 15*x*y + 12*y = x^3 + 3*x^2 + 7*x + 6 over Finite Field of size 17 to Elliptic Curve defined by y^2 + 15*x*y + 12*y = x^3 + 3*x^2 + 4*x + 8 over Finite Field of size 17
2962
sage: phi.rational_maps()
2963
((x^2 + 6*x + 4)/(x + 6), (x^2*y - 5*x*y + 8*x - 2*y)/(x^2 - 5*x + 2))
2964
sage: phi.switch_sign()
2965
sage: phi
2966
Isogeny of degree 2 from Elliptic Curve defined by y^2 + 15*x*y + 12*y = x^3 + 3*x^2 + 7*x + 6 over Finite Field of size 17 to Elliptic Curve defined by y^2 + 15*x*y + 12*y = x^3 + 3*x^2 + 4*x + 8 over Finite Field of size 17
2967
sage: phi.rational_maps()
2968
((x^2 + 6*x + 4)/(x + 6),
2969
(2*x^3 - x^2*y - 5*x^2 + 5*x*y - 4*x + 2*y + 7)/(x^2 - 5*x + 2))
2970
2971
sage: E = EllipticCurve('11a1')
2972
sage: R.<x> = QQ[]
2973
sage: f = x^2 - 21*x + 80
2974
sage: phi = EllipticCurveIsogeny(E, f)
2975
sage: (xmap1, ymap1) = phi.rational_maps()
2976
sage: phi.switch_sign()
2977
sage: (xmap2, ymap2) = phi.rational_maps()
2978
sage: xmap1 == xmap2
2979
True
2980
sage: ymap1 == -ymap2 - E.a1()*xmap2 - E.a3()
2981
True
2982
2983
sage: K.<a> = NumberField(x^2 + 1)
2984
sage: E = EllipticCurve(K, [0,0,0,1,0])
2985
sage: R.<x> = K[]
2986
sage: phi = EllipticCurveIsogeny(E, x-a)
2987
sage: phi.rational_maps()
2988
((x^2 + (-a)*x - 2)/(x + (-a)), (x^2*y + (-2*a)*x*y + y)/(x^2 + (-2*a)*x - 1))
2989
sage: phi.switch_sign()
2990
sage: phi.rational_maps()
2991
((x^2 + (-a)*x - 2)/(x + (-a)), (-x^2*y + (2*a)*x*y - y)/(x^2 + (-2*a)*x - 1))
2992
2993
"""
2994
self.set_post_isomorphism(WeierstrassIsomorphism(self.__E2, (-1,0,-self.__E2.a1(),-self.__E2.a3())))
2995
2996
def is_normalized(self, via_formal=True, check_by_pullback=True):
2997
r"""
2998
Returns ``True`` if this isogeny is normalized. An isogeny
2999
`\varphi\colon E\to E_2` between two given Weierstrass
3000
equations is said to be normalized if the constant `c` is `1`
3001
in `\varphi*(\omega_2) = c\cdot\omega`, where `\omega` and
3002
`omega_2` are the invariant differentials on `E` and `E_2`
3003
corresponding to the given equation.
3004
3005
INPUT:
3006
3007
- ``via_formal`` - (default: ``True``) If ``True`` it simply checks if
3008
the leading term of the formal series is 1. Otherwise
3009
it uses a deprecated algorithm involving the second
3010
optional argument.
3011
3012
- ``check_by_pullback`` - (default:``True``) Deprecated.
3013
3014
EXAMPLES::
3015
3016
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
3017
sage: E = EllipticCurve(GF(7), [0,0,0,1,0])
3018
sage: R.<x> = GF(7)[]
3019
sage: phi = EllipticCurveIsogeny(E, x)
3020
sage: phi.is_normalized()
3021
True
3022
sage: isom = WeierstrassIsomorphism(phi.codomain(), (3, 0, 0, 0))
3023
sage: phi.set_post_isomorphism(isom)
3024
sage: phi.is_normalized()
3025
False
3026
sage: isom = WeierstrassIsomorphism(phi.codomain(), (5, 0, 0, 0))
3027
sage: phi.set_post_isomorphism(isom)
3028
sage: phi.is_normalized()
3029
True
3030
sage: isom = WeierstrassIsomorphism(phi.codomain(), (1, 1, 1, 1))
3031
sage: phi.set_post_isomorphism(isom)
3032
sage: phi.is_normalized()
3033
True
3034
3035
sage: F = GF(2^5, 'alpha'); alpha = F.gen()
3036
sage: E = EllipticCurve(F, [1,0,1,1,1])
3037
sage: R.<x> = F[]
3038
sage: phi = EllipticCurveIsogeny(E, x+1)
3039
sage: isom = WeierstrassIsomorphism(phi.codomain(), (alpha, 0, 0, 0))
3040
sage: phi.is_normalized()
3041
True
3042
sage: phi.set_post_isomorphism(isom)
3043
sage: phi.is_normalized()
3044
False
3045
sage: isom = WeierstrassIsomorphism(phi.codomain(), (1/alpha, 0, 0, 0))
3046
sage: phi.set_post_isomorphism(isom)
3047
sage: phi.is_normalized()
3048
True
3049
sage: isom = WeierstrassIsomorphism(phi.codomain(), (1, 1, 1, 1))
3050
sage: phi.set_post_isomorphism(isom)
3051
sage: phi.is_normalized()
3052
True
3053
3054
sage: E = EllipticCurve('11a1')
3055
sage: R.<x> = QQ[]
3056
sage: f = x^3 - x^2 - 10*x - 79/4
3057
sage: phi = EllipticCurveIsogeny(E, f)
3058
sage: isom = WeierstrassIsomorphism(phi.codomain(), (2, 0, 0, 0))
3059
sage: phi.is_normalized()
3060
True
3061
sage: phi.set_post_isomorphism(isom)
3062
sage: phi.is_normalized()
3063
False
3064
sage: isom = WeierstrassIsomorphism(phi.codomain(), (1/2, 0, 0, 0))
3065
sage: phi.set_post_isomorphism(isom)
3066
sage: phi.is_normalized()
3067
True
3068
sage: isom = WeierstrassIsomorphism(phi.codomain(), (1, 1, 1, 1))
3069
sage: phi.set_post_isomorphism(isom)
3070
sage: phi.is_normalized()
3071
True
3072
3073
"""
3074
# easy algorithm using the formal expansion.
3075
if via_formal:
3076
phi_formal = self.formal(prec=5)
3077
return phi_formal[1] == 1
3078
3079
# this is the old algorithm. it should be deprecated.
3080
check_prepost_isomorphism = False
3081
3082
f_normalized = True
3083
3084
if (check_by_pullback):
3085
3086
(Xmap, Ymap) = self.rational_maps()
3087
3088
E1 = self.__E1
3089
E2 = self.__E2
3090
3091
a1 = E1.a1()
3092
a3 = E1.a3()
3093
3094
a1pr = E2.a1()
3095
a3pr = E2.a3()
3096
3097
x = self.__x_var
3098
y = self.__y_var
3099
3100
Xmap_pr = Xmap.derivative(x)
3101
3102
domain_inv_diff = 1/(2*y + a1*x + a3)
3103
codomain_inv_diff = Xmap_pr/(2*Ymap + a1pr*Xmap + a3pr)
3104
3105
inv_diff_quo = domain_inv_diff/codomain_inv_diff
3106
3107
if (1 == inv_diff_quo):
3108
f_normalized = True
3109
else:
3110
# For some reason, in certain cases, when the isogeny
3111
# is pre or post composed with a translation the
3112
# resulting rational functions are too complicated for
3113
# sage to simplify down to a constant in this case, we
3114
# do some cheating by checking if the post-composition
3115
# by isogeny has a non 1 scaling factor
3116
if ( inv_diff_quo.numerator().is_constant() and (inv_diff_quo.denominator().is_constant) ):
3117
f_normalized = False
3118
else:
3119
check_prepost_isomorphism = True
3120
else:
3121
check_prepost_isomorphism = True
3122
3123
# If we skip checking by the pullback of the invariant
3124
# differential OR if that was inconclusive We explicitly check
3125
# if there is a post isomorphism and if it has a non 1 scaling
3126
# factor or if it is a just a translation. NOTE: This only
3127
# works because we are using algorithms for calculating the
3128
# isogenies that calculate a separable normalized isogeny, if
3129
# this changes, this check will no longer be correct.
3130
#
3131
if (check_prepost_isomorphism):
3132
post_isom = self.__post_isomorphism
3133
if (post_isom is not None):
3134
if (1 == self.__base_field(post_isom.u)):
3135
f_post_normalized = True
3136
else:
3137
f_post_normalized = False
3138
else:
3139
f_post_normalized = True
3140
3141
pre_isom = self.__pre_isomorphism
3142
if (pre_isom is not None):
3143
if (1 == self.__base_field(pre_isom.u)):
3144
f_pre_normalized = True
3145
else:
3146
f_pre_normalized = False
3147
else:
3148
f_pre_normalized = True
3149
3150
f_normalized = f_pre_normalized and f_post_normalized
3151
3152
return f_normalized
3153
3154
def dual(self):
3155
r"""
3156
Computes and returns the dual isogeny of this isogeny. If
3157
`\varphi\colon E \to E_2` is the given isogeny, then the dual
3158
is by definition the unique isogeny `\hat\varphi\colon E_2\to
3159
E` such that the compositions `\hat\varphi\circ\varphi` and
3160
`\varphi\circ\hat\varphi` are the multiplication `[n]` by the
3161
degree of `\varphi` on `E` and `E_2` respectively.
3162
3163
EXAMPLES::
3164
3165
sage: E = EllipticCurve('11a1')
3166
sage: R.<x> = QQ[]
3167
sage: f = x^2 - 21*x + 80
3168
sage: phi = EllipticCurveIsogeny(E, f)
3169
sage: phi_hat = phi.dual()
3170
sage: phi_hat.domain() == phi.codomain()
3171
True
3172
sage: phi_hat.codomain() == phi.domain()
3173
True
3174
sage: (X, Y) = phi.rational_maps()
3175
sage: (Xhat, Yhat) = phi_hat.rational_maps()
3176
sage: Xm = Xhat.subs(x=X, y=Y)
3177
sage: Ym = Yhat.subs(x=X, y=Y)
3178
sage: (Xm, Ym) == E.multiplication_by_m(5)
3179
True
3180
3181
sage: E = EllipticCurve(GF(37), [0,0,0,1,8])
3182
sage: R.<x> = GF(37)[]
3183
sage: f = x^3 + x^2 + 28*x + 33
3184
sage: phi = EllipticCurveIsogeny(E, f)
3185
sage: phi_hat = phi.dual()
3186
sage: phi_hat.codomain() == phi.domain()
3187
True
3188
sage: phi_hat.domain() == phi.codomain()
3189
True
3190
sage: (X, Y) = phi.rational_maps()
3191
sage: (Xhat, Yhat) = phi_hat.rational_maps()
3192
sage: Xm = Xhat.subs(x=X, y=Y)
3193
sage: Ym = Yhat.subs(x=X, y=Y)
3194
sage: (Xm, Ym) == E.multiplication_by_m(7)
3195
True
3196
3197
sage: E = EllipticCurve(GF(31), [0,0,0,1,8])
3198
sage: R.<x> = GF(31)[]
3199
sage: f = x^2 + 17*x + 29
3200
sage: phi = EllipticCurveIsogeny(E, f)
3201
sage: phi_hat = phi.dual()
3202
sage: phi_hat.codomain() == phi.domain()
3203
True
3204
sage: phi_hat.domain() == phi.codomain()
3205
True
3206
sage: (X, Y) = phi.rational_maps()
3207
sage: (Xhat, Yhat) = phi_hat.rational_maps()
3208
sage: Xm = Xhat.subs(x=X, y=Y)
3209
sage: Ym = Yhat.subs(x=X, y=Y)
3210
sage: (Xm, Ym) == E.multiplication_by_m(5)
3211
True
3212
3213
Test (for trac ticket 7096)::
3214
3215
sage: E = EllipticCurve('11a1')
3216
sage: phi = E.isogeny(E(5,5))
3217
sage: phi.dual().dual() == phi
3218
True
3219
3220
sage: k = GF(103)
3221
sage: E = EllipticCurve(k,[11,11])
3222
sage: phi = E.isogeny(E(4,4))
3223
sage: phi
3224
Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 + 11*x + 11 over Finite Field of size 103 to Elliptic Curve defined by y^2 = x^3 + 25*x + 80 over Finite Field of size 103
3225
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
3226
sage: phi.set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(),(5,0,1,2)))
3227
sage: phi.dual().dual() == phi
3228
True
3229
3230
sage: E = EllipticCurve(GF(103),[1,0,0,1,-1])
3231
sage: phi = E.isogeny(E(60,85))
3232
sage: phi.dual()
3233
Isogeny of degree 7 from Elliptic Curve defined by y^2 + x*y = x^3 + 84*x + 34 over Finite Field of size 103 to Elliptic Curve defined by y^2 + x*y = x^3 + x + 102 over Finite Field of size 103
3234
3235
"""
3236
3237
if (self.__base_field.characteristic() in [2,3]):
3238
raise NotImplemented
3239
3240
if (self.__dual is not None):
3241
return self.__dual
3242
3243
# trac 7096
3244
(E1, E2pr, pre_isom, post_isom) = compute_intermediate_curves(self.codomain(), self.domain())
3245
3246
F = self.__base_field
3247
3248
d = self.__degree
3249
3250
# trac 7096
3251
if F(d) == 0:
3252
raise NotImplementedError, "The dual isogeny is not separable, but only separable isogenies are implemented so far"
3253
3254
# trac 7096
3255
# this should take care of the case when the isogeny is not normalized.
3256
u = self.formal(prec=5)[1]
3257
isom = WeierstrassIsomorphism(E2pr, (u/F(d), 0, 0, 0))
3258
3259
E2 = isom.codomain().codomain()
3260
3261
pre_isom = self.__E2.isomorphism_to(E1)
3262
post_isom = E2.isomorphism_to(self.__E1)
3263
3264
phi_hat = EllipticCurveIsogeny(E1, None, E2, d)
3265
3266
phi_hat.set_pre_isomorphism(pre_isom)
3267
phi_hat.set_post_isomorphism(post_isom)
3268
phi_hat.__perform_inheritance_housekeeping()
3269
3270
assert phi_hat.codomain() == self.domain()
3271
3272
# trac 7096 : this adjust a posteriori the automorphism
3273
# on the codomain of the dual isogeny.
3274
# we used _a_ Weierstrass isomorphism to get to the original
3275
# curve, but we may have to change it my an automorphism.
3276
# we impose that the composition has the degree
3277
# as a leading coefficient in the formal expansion.
3278
3279
phi_sc = self.formal(prec=5)[1]
3280
phihat_sc = phi_hat.formal(prec=5)[1]
3281
3282
sc = phi_sc * phihat_sc/F(d)
3283
3284
if sc != 1:
3285
auts = phi_hat.codomain().automorphsims()
3286
aut = [a for a in auts if a.u == sc]
3287
if len(aut) != 1:
3288
raise ValueError, "There is a bug in dual()."
3289
phi_hat.set_post_isomorphism(a[0])
3290
3291
self.__dual = phi_hat
3292
3293
return phi_hat
3294
3295
def formal(self,prec=20):
3296
r"""
3297
Computes the formal isogeny as a power series in the variable
3298
`t=-x/y` on the domain curve.
3299
3300
INPUT:
3301
3302
- ``prec`` - (default = 20), the precision with which the computations
3303
in the formal group are carried out.
3304
3305
EXAMPLES::
3306
3307
sage: E = EllipticCurve(GF(13),[1,7])
3308
sage: phi = E.isogeny(E(10,4))
3309
sage: phi.formal()
3310
t + 12*t^13 + 2*t^17 + 8*t^19 + 2*t^21 + O(t^23)
3311
3312
sage: E = EllipticCurve([0,1])
3313
sage: phi = E.isogeny(E(2,3))
3314
sage: phi.formal(prec=10)
3315
t + 54*t^5 + 255*t^7 + 2430*t^9 + 19278*t^11 + O(t^13)
3316
3317
sage: E = EllipticCurve('11a2')
3318
sage: R.<x> = QQ[]
3319
sage: phi = E.isogeny(x^2 + 101*x + 12751/5)
3320
sage: phi.formal(prec=7)
3321
t - 2724/5*t^5 + 209046/5*t^7 - 4767/5*t^8 + 29200946/5*t^9 + O(t^10)
3322
3323
3324
"""
3325
Eh = self.__E1.formal()
3326
f, g = self.rational_maps()
3327
xh = Eh.x(prec=prec)
3328
yh = Eh.y(prec=prec)
3329
fh = f(xh,yh)
3330
gh = g(xh,yh)
3331
return -fh/gh
3332
3333
#
3334
# Overload Morphism methods that we want to
3335
#
3336
3337
def _composition_(self, right, homset):
3338
r"""
3339
Composition operator function inherited from morphism class.
3340
3341
EXAMPLES::
3342
3343
sage: E = EllipticCurve(j=GF(7)(0))
3344
sage: phi = EllipticCurveIsogeny(E, [E(0), E((0,1)), E((0,-1))])
3345
sage: phi*phi
3346
Traceback (most recent call last):
3347
...
3348
NotImplementedError
3349
sage: phi._composition_(phi, phi.parent())
3350
Traceback (most recent call last):
3351
...
3352
NotImplementedError
3353
3354
"""
3355
raise NotImplementedError
3356
3357
3358
def is_injective(self):
3359
r"""
3360
Method inherited from the morphism class. Returns ``True`` if
3361
and only if this isogeny has trivial kernel.
3362
3363
EXAMPLES::
3364
3365
sage: E = EllipticCurve('11a1')
3366
sage: R.<x> = QQ[]
3367
sage: f = x^2 + x - 29/5
3368
sage: phi = EllipticCurveIsogeny(E, f)
3369
sage: phi.is_injective()
3370
False
3371
sage: phi = EllipticCurveIsogeny(E, R(1))
3372
sage: phi.is_injective()
3373
True
3374
3375
sage: F = GF(7)
3376
sage: E = EllipticCurve(j=F(0))
3377
sage: phi = EllipticCurveIsogeny(E, [ E((0,-1)), E((0,1))])
3378
sage: phi.is_injective()
3379
False
3380
sage: phi = EllipticCurveIsogeny(E, E(0))
3381
sage: phi.is_injective()
3382
True
3383
3384
"""
3385
3386
if (1 < self.__degree): return False
3387
return True
3388
3389
3390
def is_surjective(self):
3391
r"""
3392
For elliptic curve isogenies, always returns ``True`` (as a
3393
non-constant map of algebraic curves must be surjective).
3394
3395
EXAMPLES::
3396
3397
sage: E = EllipticCurve('11a1')
3398
sage: R.<x> = QQ[]
3399
sage: f = x^2 + x - 29/5
3400
sage: phi = EllipticCurveIsogeny(E, f)
3401
sage: phi.is_surjective()
3402
True
3403
3404
sage: E = EllipticCurve(GF(7), [0,0,0,1,0])
3405
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
3406
sage: phi.is_surjective()
3407
True
3408
3409
sage: F = GF(2^5, 'omega')
3410
sage: E = EllipticCurve(j=F(0))
3411
sage: R.<x> = F[]
3412
sage: phi = EllipticCurveIsogeny(E, x)
3413
sage: phi.is_surjective()
3414
True
3415
3416
"""
3417
return True
3418
3419
def is_zero(self):
3420
r"""
3421
Member function inherited from morphism class.
3422
3423
EXAMPLES::
3424
3425
sage: E = EllipticCurve(j=GF(7)(0))
3426
sage: phi = EllipticCurveIsogeny(E, [ E((0,1)), E((0,-1))])
3427
sage: phi.is_zero()
3428
Traceback (most recent call last):
3429
...
3430
NotImplementedError
3431
"""
3432
raise NotImplementedError
3433
3434
def post_compose(self, left):
3435
r"""
3436
Member function inherited from morphism class.
3437
3438
EXAMPLES::
3439
3440
sage: E = EllipticCurve(j=GF(7)(0))
3441
sage: phi = EllipticCurveIsogeny(E, [ E((0,1)), E((0,-1))])
3442
sage: phi.post_compose(phi)
3443
Traceback (most recent call last):
3444
...
3445
NotImplementedError
3446
3447
"""
3448
raise NotImplementedError
3449
3450
3451
def pre_compose(self, right):
3452
r"""
3453
Member function inherited from morphism class.
3454
3455
EXAMPLES::
3456
3457
sage: E = EllipticCurve(j=GF(7)(0))
3458
sage: phi = EllipticCurveIsogeny(E, [ E((0,1)), E((0,-1))])
3459
sage: phi.pre_compose(phi)
3460
Traceback (most recent call last):
3461
...
3462
NotImplementedError
3463
3464
"""
3465
raise NotImplementedError
3466
3467
3468
def n(self):
3469
r"""
3470
Numerical Approximation inherited from Map (through morphism),
3471
nonsensical for isogenies.
3472
3473
EXAMPLES::
3474
3475
sage: E = EllipticCurve(j=GF(7)(0))
3476
sage: phi = EllipticCurveIsogeny(E, [ E((0,1)), E((0,-1))])
3477
sage: phi.n()
3478
Traceback (most recent call last):
3479
...
3480
NotImplementedError: Numerical approximations do not make sense for Elliptic Curve Isogenies
3481
3482
"""
3483
raise NotImplementedError, "Numerical approximations do not make sense for Elliptic Curve Isogenies"
3484
3485
# no longer needed (trac 7096)
3486
# def starks_find_r_and_t(T, Z):
3487
3488
def compute_isogeny_starks(E1, E2, ell):
3489
r"""
3490
Computes the degree ``ell`` isogeny between ``E1`` and ``E2`` via
3491
Stark's algorithm. There must be a degree ``ell``, separable,
3492
normalized cyclic isogeny from ``E1`` to ``E2``.
3493
3494
INPUT:
3495
3496
- ``E1`` - an elliptic curve in short Weierstrass form.
3497
- ``E2`` - an elliptic curve in short Weierstrass form.
3498
- ``ell`` - the degree of the isogeny from E1 to E2.
3499
3500
OUTPUT:
3501
3502
polynomial -- over the field of definition of ``E1``, ``E2``, that is the
3503
kernel polynomial of the isogeny from ``E1`` to ``E2``.
3504
3505
ALGORITHM:
3506
3507
This function uses Starks Algorithm as presented in section 6.2 of
3508
[BMSS].
3509
3510
.. note::
3511
3512
As published there, the algorithm is incorrect, and a correct
3513
version (with slightly different notation) can be found in
3514
[M09]. The algorithm originates in [S72]
3515
3516
REFERENCES:
3517
3518
- [BMSS] Boston, Morain, Salvy, Schost, "Fast Algorithms for Isogenies."
3519
- [M09] Moody, "The Diffie-Hellman Problem and Generalization of Verheul's Theorem"
3520
- [S72] Stark, "Class-numbers of complex quadratic fields."
3521
3522
EXAMPLES::
3523
3524
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_starks, compute_sequence_of_maps
3525
3526
sage: E = EllipticCurve(GF(97), [1,0,1,1,0])
3527
sage: R.<x> = GF(97)[]; f = x^5 + 27*x^4 + 61*x^3 + 58*x^2 + 28*x + 21
3528
sage: phi = EllipticCurveIsogeny(E, f)
3529
sage: E2 = phi.codomain()
3530
sage: (isom1, isom2, E1pr, E2pr, ker_poly) = compute_sequence_of_maps(E, E2, 11)
3531
sage: compute_isogeny_starks(E1pr, E2pr, 11)
3532
x^10 + 37*x^9 + 53*x^8 + 66*x^7 + 66*x^6 + 17*x^5 + 57*x^4 + 6*x^3 + 89*x^2 + 53*x + 8
3533
3534
sage: E = EllipticCurve(GF(37), [0,0,0,1,8])
3535
sage: R.<x> = GF(37)[]
3536
sage: f = (x + 14) * (x + 30)
3537
sage: phi = EllipticCurveIsogeny(E, f)
3538
sage: E2 = phi.codomain()
3539
sage: compute_isogeny_starks(E, E2, 5)
3540
x^4 + 14*x^3 + x^2 + 34*x + 21
3541
sage: f**2
3542
x^4 + 14*x^3 + x^2 + 34*x + 21
3543
3544
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
3545
sage: R.<x> = QQ[]
3546
sage: f = x
3547
sage: phi = EllipticCurveIsogeny(E, f)
3548
sage: E2 = phi.codomain()
3549
sage: compute_isogeny_starks(E, E2, 2)
3550
x
3551
3552
"""
3553
3554
K = E1.base_field()
3555
R = PolynomialRing(K, 'x')
3556
x = R.gen()
3557
3558
wp1 = E1.weierstrass_p(prec=4*ell+4) #BMSS claim 2*ell is enough, but it is not M09
3559
wp2 = E2.weierstrass_p(prec=4*ell+4)
3560
3561
# viewed them as power series in Z = z^2
3562
S = LaurentSeriesRing(K, 'Z')
3563
Z = S.gen()
3564
pe1 = 1/Z
3565
pe2 = 1/Z
3566
for i in xrange(2*ell+1):
3567
pe1 += wp1[2*i] * Z**i
3568
pe2 += wp2[2*i] * Z**i
3569
pe1 = pe1.add_bigoh(2*ell+2)
3570
pe2 = pe2.add_bigoh(2*ell+2)
3571
3572
#print 'wps = ',pe1
3573
#print 'wps2 = ',pe2
3574
3575
n = 1
3576
q = [R(1), R(0)]
3577
#p = [R(0), R(1)]
3578
T = pe2
3579
3580
while ( q[n].degree() < (ell-1) ):
3581
#print 'n=', n
3582
3583
n += 1
3584
a_n = 0
3585
r = -T.valuation()
3586
while ( 0 <= r and T != 0):
3587
t_r = T[-r]
3588
#print ' r=',r
3589
#print ' t_r=',t_r
3590
#print ' T=',T
3591
a_n = a_n + t_r * x**r
3592
T = T - t_r*pe1**r
3593
r = -T.valuation()
3594
3595
3596
q_n = a_n*q[n-1] + q[n-2]
3597
q.append(q_n)
3598
#p_n = a_n*p[n-1] + q[n-2]
3599
#p.append(p_n)
3600
3601
if (n == ell+1 or T==0):
3602
if T.valuation()<2:
3603
raise ValueError, "The two curves are not linked by a cyclic normalized isogeny of degree %s"%ell
3604
#print 'breaks here'
3605
break
3606
3607
T = 1/T
3608
#print ' a_n=', a_n
3609
#print ' q_n=', q_n
3610
#print ' p_n=', p_n
3611
#print ' T = ', T
3612
3613
qn = q[n]
3614
#pn= p[n]
3615
#print 'final T = ', T
3616
#print ' f =', pn/qn
3617
3618
qn = (1/qn.leading_coefficient())*qn
3619
#pn = (1/qn.leading_coefficient())*pn
3620
3621
return qn
3622
3623
def split_kernel_polynomial(E1, ker_poly, ell):
3624
r"""
3625
Internal helper function for ``compute_isogeny_kernel_polynomial``.
3626
3627
Given a full kernel polynomial (where two torsion `x`-coordinates
3628
are roots of multiplicity 1, and all other roots have multiplicity
3629
2.) of degree `\ell-1`, returns the maximum separable divisor.
3630
(i.e. the kernel polynomial with roots of multiplicity at most 1).
3631
3632
EXAMPLES:
3633
3634
The following example implicitly exercises this function::
3635
3636
sage: E = EllipticCurve(GF(37), [0,0,0,1,8])
3637
sage: R.<x> = GF(37)[]
3638
sage: f = (x + 10) * (x + 12) * (x + 16)
3639
sage: phi = EllipticCurveIsogeny(E, f)
3640
sage: E2 = phi.codomain()
3641
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_starks
3642
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import split_kernel_polynomial
3643
sage: ker_poly = compute_isogeny_starks(E, E2, 7); ker_poly
3644
x^6 + 2*x^5 + 20*x^4 + 11*x^3 + 36*x^2 + 35*x + 16
3645
sage: split_kernel_polynomial(E, ker_poly, 7)
3646
x^3 + x^2 + 28*x + 33
3647
3648
"""
3649
3650
poly_ring = ker_poly.parent()
3651
3652
z = poly_ring.gen(0)
3653
3654
ker_poly_2tor = two_torsion_part(E1, poly_ring, ker_poly, ell)
3655
ker_poly_quo = poly_ring(ker_poly/ker_poly_2tor)
3656
ker_poly_quo_sqrt = ker_poly_quo.gcd(ker_poly_quo.derivative(z))
3657
ker_poly = ker_poly_2tor*ker_poly_quo_sqrt
3658
ker_poly = (1/ker_poly.leading_coefficient())*ker_poly
3659
3660
return ker_poly
3661
3662
3663
def compute_isogeny_kernel_polynomial(E1, E2, ell, algorithm="starks"):
3664
r"""
3665
Computes the kernel polynomial of the degree ``ell`` isogeny
3666
between ``E1`` and ``E2``. There must be a degree ``ell``,
3667
cyclic, separable, normalized isogeny from ``E1`` to ``E2``.
3668
3669
INPUT:
3670
3671
- ``E1`` - an elliptic curve in short Weierstrass form.
3672
3673
- ``E2`` - an elliptic curve in short Weierstrass form.
3674
3675
- ``ell`` - the degree of the isogeny from ``E1`` to ``E2``.
3676
3677
- ``algorithm`` - currently only ``starks`` (default) is implemented.
3678
3679
OUTPUT:
3680
3681
polynomial -- over the field of definition of ``E1``, ``E2``, that is the
3682
kernel polynomial of the isogeny from ``E1`` to ``E2``.
3683
3684
EXAMPLES::
3685
3686
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_kernel_polynomial
3687
3688
sage: E = EllipticCurve(GF(37), [0,0,0,1,8])
3689
sage: R.<x> = GF(37)[]
3690
sage: f = (x + 14) * (x + 30)
3691
sage: phi = EllipticCurveIsogeny(E, f)
3692
sage: E2 = phi.codomain()
3693
sage: compute_isogeny_kernel_polynomial(E, E2, 5)
3694
x^2 + 7*x + 13
3695
sage: f
3696
x^2 + 7*x + 13
3697
3698
sage: R.<x> = QQ[]
3699
sage: K.<i> = NumberField(x^2 + 1)
3700
sage: E = EllipticCurve(K, [0,0,0,1,0])
3701
sage: E2 = EllipticCurve(K, [0,0,0,16,0])
3702
sage: compute_isogeny_kernel_polynomial(E, E2, 4)
3703
x^3 + x
3704
3705
"""
3706
3707
ker_poly = compute_isogeny_starks(E1, E2, ell)
3708
ker_poly = split_kernel_polynomial(E1, ker_poly, ell)
3709
3710
return ker_poly
3711
3712
3713
def compute_intermediate_curves(E1, E2):
3714
r"""
3715
Computes isomorphism from ``E1`` to an intermediate domain and an
3716
isomorphism from an intermediate codomain to ``E2``.
3717
3718
Intermediate domain and intermediate codomain, are in short
3719
Weierstrass form.
3720
3721
This is used so we can compute `\wp` functions from the short
3722
Weierstrass model more easily.
3723
3724
The underlying field must be of characteristic not equal to 2,3.
3725
3726
INPUT:
3727
3728
- ``E1`` - an elliptic curve
3729
- ``E2`` - an elliptic curve
3730
3731
OUTPUT:
3732
3733
tuple -- (``pre_isomorphism``, ``post_isomorphism``, ``intermediate_domain``,
3734
``intermediate_codomain``):
3735
3736
- ``intermediate_domain``: a short Weierstrass model isomorphic to ``E1``
3737
- ``intermediate_codomain``: a short Weierstrass model isomorphic to ``E2``
3738
- ``pre_isomorphism``: normalized isomorphism from ``E1`` to intermediate_domain
3739
- ``post_isomorphism``: normalized isomorphism from intermediate_codomain to ``E2``
3740
3741
EXAMPLES::
3742
3743
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_intermediate_curves
3744
sage: E = EllipticCurve(GF(83), [1,0,1,1,0])
3745
sage: R.<x> = GF(83)[]; f = x+24
3746
sage: phi = EllipticCurveIsogeny(E, f)
3747
sage: E2 = phi.codomain()
3748
sage: compute_intermediate_curves(E, E2)
3749
(Elliptic Curve defined by y^2 = x^3 + 62*x + 74 over Finite Field of size 83,
3750
Elliptic Curve defined by y^2 = x^3 + 65*x + 69 over Finite Field of size 83,
3751
Generic morphism:
3752
From: Abelian group of points on Elliptic Curve defined by y^2 + x*y + y = x^3 + x over Finite Field of size 83
3753
To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 62*x + 74 over Finite Field of size 83
3754
Via: (u,r,s,t) = (1, 76, 41, 3),
3755
Generic morphism:
3756
From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 65*x + 69 over Finite Field of size 83
3757
To: Abelian group of points on Elliptic Curve defined by y^2 + x*y + 77*y = x^3 + 49*x + 28 over Finite Field of size 83
3758
Via: (u,r,s,t) = (1, 7, 42, 80))
3759
3760
sage: R.<x> = QQ[]
3761
sage: K.<i> = NumberField(x^2 + 1)
3762
sage: E = EllipticCurve(K, [0,0,0,1,0])
3763
sage: E2 = EllipticCurve(K, [0,0,0,16,0])
3764
sage: compute_intermediate_curves(E, E2)
3765
(Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1,
3766
Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1,
3767
Generic morphism:
3768
From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1
3769
To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1
3770
Via: (u,r,s,t) = (1, 0, 0, 0),
3771
Generic morphism:
3772
From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1
3773
To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1
3774
Via: (u,r,s,t) = (1, 0, 0, 0))
3775
3776
"""
3777
3778
if (E1.base_ring().characteristic() in [2,3]):
3779
raise NotImplemented
3780
3781
# compute the r,s,t values that clear the denominator of E1
3782
a1 = E1.a1()
3783
a2 = E1.a2()
3784
a3 = E1.a3()
3785
3786
s1 = -a1/2
3787
r1 = (s1**2 + s1*a1 - a2)/3
3788
t1 = (-r1*a1 - a3)/2
3789
3790
# compute the isomorphism from E1 to intermediate_domain
3791
pre_isom = WeierstrassIsomorphism(E1, (1, r1, s1, t1))
3792
3793
intermediate_domain = pre_isom.codomain().codomain()
3794
3795
# compute the r,s,t values that clear the denominator of E2
3796
a1pr = E2.a1()
3797
a2pr = E2.a2()
3798
a3pr = E2.a3()
3799
3800
s2 = -a1pr/2
3801
r2 = (s2**2 + s2*a1pr - a2pr)/3
3802
t2 = (-r2*a1pr - a3pr)/2
3803
3804
post_isom_inv = WeierstrassIsomorphism(E2, (1, r2, s2, t2))
3805
intermediate_codomain = post_isom_inv.codomain().codomain()
3806
3807
post_isom = WeierstrassIsomorphism(intermediate_codomain, (1, -r2, -s2, -t2))
3808
3809
return (intermediate_domain, intermediate_codomain, pre_isom, post_isom)
3810
3811
3812
def compute_sequence_of_maps(E1, E2, ell):
3813
r"""
3814
Given domain ``E1`` and codomain ``E2`` such that there is a
3815
degree ``ell`` separable normalized isogeny from ``E1`` to ``E2``,
3816
returns pre/post isomorphism, as well as intermediate domain and
3817
codomain, and kernel polynomial.
3818
3819
EXAMPLES::
3820
3821
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_sequence_of_maps
3822
sage: E = EllipticCurve('11a1')
3823
sage: R.<x> = QQ[]; f = x^2 - 21*x + 80
3824
sage: phi = EllipticCurveIsogeny(E, f)
3825
sage: E2 = phi.codomain()
3826
sage: compute_sequence_of_maps(E, E2, 5)
3827
(Generic morphism:
3828
From: Abelian group of points on Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field
3829
To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 - 31/3*x - 2501/108 over Rational Field
3830
Via: (u,r,s,t) = (1, 1/3, 0, -1/2),
3831
Generic morphism:
3832
From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 - 23461/3*x - 28748141/108 over Rational Field
3833
To: Abelian group of points on Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field
3834
Via: (u,r,s,t) = (1, -1/3, 0, 1/2),
3835
Elliptic Curve defined by y^2 = x^3 - 31/3*x - 2501/108 over Rational Field,
3836
Elliptic Curve defined by y^2 = x^3 - 23461/3*x - 28748141/108 over Rational Field,
3837
x^2 - 61/3*x + 658/9)
3838
3839
sage: K.<i> = NumberField(x^2 + 1)
3840
sage: E = EllipticCurve(K, [0,0,0,1,0])
3841
sage: E2 = EllipticCurve(K, [0,0,0,16,0])
3842
sage: compute_sequence_of_maps(E, E2, 4)
3843
(Generic morphism:
3844
From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1
3845
To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1
3846
Via: (u,r,s,t) = (1, 0, 0, 0),
3847
Generic morphism:
3848
From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1
3849
To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1
3850
Via: (u,r,s,t) = (1, 0, 0, 0),
3851
Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1,
3852
Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1,
3853
x^3 + x)
3854
3855
sage: E = EllipticCurve(GF(97), [1,0,1,1,0])
3856
sage: R.<x> = GF(97)[]; f = x^5 + 27*x^4 + 61*x^3 + 58*x^2 + 28*x + 21
3857
sage: phi = EllipticCurveIsogeny(E, f)
3858
sage: E2 = phi.codomain()
3859
sage: compute_sequence_of_maps(E, E2, 11)
3860
(Generic morphism:
3861
From: Abelian group of points on Elliptic Curve defined by y^2 + x*y + y = x^3 + x over Finite Field of size 97
3862
To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 52*x + 31 over Finite Field of size 97
3863
Via: (u,r,s,t) = (1, 8, 48, 44),
3864
Generic morphism:
3865
From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 41*x + 66 over Finite Field of size 97
3866
To: Abelian group of points on Elliptic Curve defined by y^2 + x*y + 9*y = x^3 + 83*x + 6 over Finite Field of size 97
3867
Via: (u,r,s,t) = (1, 89, 49, 53),
3868
Elliptic Curve defined by y^2 = x^3 + 52*x + 31 over Finite Field of size 97,
3869
Elliptic Curve defined by y^2 = x^3 + 41*x + 66 over Finite Field of size 97,
3870
x^5 + 67*x^4 + 13*x^3 + 35*x^2 + 77*x + 69)
3871
3872
"""
3873
3874
(E1pr, E2pr, pre_isom, post_isom) = compute_intermediate_curves(E1, E2)
3875
3876
ker_poly = compute_isogeny_kernel_polynomial(E1pr, E2pr, ell)
3877
3878
return (pre_isom, post_isom, E1pr, E2pr, ker_poly)
3879
3880
3881
##########################################################################
3882
# The following section is all about computing l-isogenies, where l is
3883
# a prime. The genus 0 cases `l` = 2, 3, 5, 7 and 13 are
3884
# implemented over any field of characteristic not 2, 3 or `l`; over
3885
# `\QQ` the "sporadic" cases `l` = 11, 17, 19, 37, 43, 67 or 163 with
3886
# only finitely many `j`-invariants each. are also implemented.
3887
##########################################################################
3888
3889
@cached_function
3890
def Fricke_polynomial(l):
3891
r"""
3892
Fricke polynomial for ``l`` =2,3,5,7,13.
3893
3894
For these primes (and these only) the modular curve `X_0(l)` has
3895
genus zero, and its field is generated by a single modular
3896
function called the Fricke module (or Hauptmodul), `t`. There is
3897
a classical choice of such a generator `t` in each case, and the
3898
`j`-function is a rational function of `t` of degree `l+1` of the
3899
form `P(t)/t` where `P` is a polynomial of degree `l+1`. Up to
3900
scaling, `t` is determined by the condition that the ramification
3901
points above `j=\infty` are `t=0` (with ramification degree `1`)
3902
and `t=\infty` (with degree `l`). The ramification above `j=0`
3903
and `j=1728` may be seen in the factorizations of `j(t)` and
3904
`k(t)` where `k=j-1728`.
3905
3906
OUTPUT:
3907
3908
The polynomial `P(t)` as an element of `\ZZ[t]`.
3909
3910
TESTS::
3911
3912
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import Fricke_polynomial
3913
sage: Fricke_polynomial(2)
3914
t^3 + 48*t^2 + 768*t + 4096
3915
sage: Fricke_polynomial(3)
3916
t^4 + 36*t^3 + 270*t^2 + 756*t + 729
3917
sage: Fricke_polynomial(5)
3918
t^6 + 30*t^5 + 315*t^4 + 1300*t^3 + 1575*t^2 + 750*t + 125
3919
sage: Fricke_polynomial(7)
3920
t^8 + 28*t^7 + 322*t^6 + 1904*t^5 + 5915*t^4 + 8624*t^3 + 4018*t^2 + 748*t + 49
3921
sage: Fricke_polynomial(13)
3922
t^14 + 26*t^13 + 325*t^12 + 2548*t^11 + 13832*t^10 + 54340*t^9 + 157118*t^8 + 333580*t^7 + 509366*t^6 + 534820*t^5 + 354536*t^4 + 124852*t^3 + 15145*t^2 + 746*t + 13
3923
"""
3924
Zt = PolynomialRing(ZZ,'t')
3925
t = Zt.gen()
3926
if l==2: return (t+16)**3
3927
elif l==3: return (t+3)**3*(t+27)
3928
elif l==5: return (t**2+10*t+5)**3
3929
elif l==7: return (t**2+5*t+1)**3 * (t**2+13*t+49)
3930
elif l==13: return (t**2+5*t+13)*(t**4+7*t**3+20*t**2+19*t+1)**3
3931
else:
3932
raise ValueError, "The only genus zero primes are 2, 3, 5, 7 or 13."
3933
3934
@cached_function
3935
def Fricke_module(l):
3936
r"""
3937
Fricke module for ``l`` =2,3,5,7,13.
3938
3939
For these primes (and these only) the modular curve `X_0(l)` has
3940
genus zero, and its field is generated by a single modular
3941
function called the Fricke module (or Hauptmodul), `t`. There is
3942
a classical choice of such a generator `t` in each case, and the
3943
`j`-function is a rational function of `t` of degree `l+1` of the
3944
form `P(t)/t` where `P` is a polynomial of degree `l+1`. Up to
3945
scaling, `t` is determined by the condition that the ramification
3946
points above `j=\infty` are `t=0` (with ramification degree `1`)
3947
and `t=\infty` (with degree `l`). The ramification above `j=0`
3948
and `j=1728` may be seen in the factorizations of `j(t)` and
3949
`k(t)` where `k=j-1728`.
3950
3951
OUTPUT:
3952
3953
The rational function `P(t)/t`.
3954
3955
TESTS::
3956
3957
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import Fricke_module
3958
sage: Fricke_module(2)
3959
(t^3 + 48*t^2 + 768*t + 4096)/t
3960
sage: Fricke_module(3)
3961
(t^4 + 36*t^3 + 270*t^2 + 756*t + 729)/t
3962
sage: Fricke_module(5)
3963
(t^6 + 30*t^5 + 315*t^4 + 1300*t^3 + 1575*t^2 + 750*t + 125)/t
3964
sage: Fricke_module(7)
3965
(t^8 + 28*t^7 + 322*t^6 + 1904*t^5 + 5915*t^4 + 8624*t^3 + 4018*t^2 + 748*t + 49)/t
3966
sage: Fricke_module(13)
3967
(t^14 + 26*t^13 + 325*t^12 + 2548*t^11 + 13832*t^10 + 54340*t^9 + 157118*t^8 + 333580*t^7 + 509366*t^6 + 534820*t^5 + 354536*t^4 + 124852*t^3 + 15145*t^2 + 746*t + 13)/t
3968
"""
3969
try:
3970
t = PolynomialRing(QQ,'t').gen()
3971
return Fricke_polynomial(l) / t
3972
except ValueError:
3973
raise ValueError, "The only genus zero primes are 2, 3, 5, 7 or 13."
3974
3975
@cached_function
3976
def Psi(l, use_stored=True):
3977
r"""
3978
Generic kernel polynomial for genus one primes.
3979
3980
For each of the primes `l` for which `X_0(l)` has genus zero
3981
(namely `l=2,3,5,7,13`), we may define an elliptic curve `E_t`
3982
over `\QQ(t)`, with coefficients in `\ZZ[t]`, which has good
3983
reduction except at `t=0` and `t=\infty` (which lie above
3984
`j=\infty`) and at certain other values of `t` above `j=0` when
3985
`l=3` (one value) or `l\equiv1\pmod{3}` (two values) and above
3986
`j=1728` when `l=2` (one value) or `l\equiv1 \pmod{4}` (two
3987
values). (These exceptional values correspond to endomorphisms of
3988
`E_t` of degree `l`.) The `l`-division polynomial of `E_t` has a
3989
unique factor of degree `(l-1)/2` (or 1 when `l=2`), with
3990
coefficients in `\ZZ[t]`, which we call the Generic Kernel
3991
Polynomial for `l`. These are used, by specialising `t`, in the
3992
function :meth:`isogenies_prime_degree_genus_0`, which also has to
3993
take into account the twisting factor between `E_t` for a specific
3994
value of `t` and the short Weierstrass form of an elliptic curve
3995
with `j`-invariant `j(t)`. This enables the computation of the
3996
kernel polynomials of isogenies without having to compute and
3997
factor division polynomials.
3998
3999
All of this data is quickly computed from the Fricke modules, except
4000
that for `l=13` the factorization of the Generic Division Polynomial
4001
takes a long time, so the value have been precomputed and cached; by
4002
default the cached values are used, but the code here will recompute
4003
them when ``use_stored`` is ``False``, as in the doctests.
4004
4005
INPUT:
4006
4007
- ``l`` -- either 2, 3, 5, 7, or 13.
4008
4009
- ``use_stored`` (boolean, default True) -- If True, use
4010
precomputed values, otherwise compute them on the fly.
4011
4012
.. note::
4013
4014
This computation takes a negligible time for `l=2,3,5,7'
4015
but more than 100s for `l=13`. The reason
4016
for allowing dynamic computation here instead of just using
4017
precomputed values is for testing.
4018
4019
TESTS::
4020
4021
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import Fricke_module, Psi
4022
sage: assert Psi(2, use_stored=True) == Psi(2, use_stored=False)
4023
sage: assert Psi(3, use_stored=True) == Psi(3, use_stored=False)
4024
sage: assert Psi(5, use_stored=True) == Psi(5, use_stored=False)
4025
sage: assert Psi(7, use_stored=True) == Psi(7, use_stored=False)
4026
sage: assert Psi(13, use_stored=True) == Psi(13, use_stored=False) # not tested (very long time)
4027
"""
4028
if not l in [2,3,5,7,13]:
4029
raise ValueError, "Genus zero primes are 2, 3, 5, 7 or 13."
4030
4031
R = PolynomialRing(ZZ,2,'Xt')
4032
X,t = R.gens()
4033
4034
if use_stored:
4035
if l==2:
4036
return X + t + 64
4037
if l==3:
4038
return X + t + 27
4039
if l==5:
4040
return X**2 + 2*X*(t**2 + 22*t + 125)+ (t**2 + 22*t + 89) * (t**2 + 22*t + 125)
4041
if l==7:
4042
return (X**3 + 3*(t**2 + 13*t + 49)*X**2
4043
+ 3*(t**2 + 13*t + 33)*(t**2 + 13*t + 49)*X
4044
+ (t**2 + 13*t + 49)*(t**4 + 26*t**3 + 219*t**2 + 778*t + 881))
4045
if l==13:
4046
return (t**24 + 66*t**23 + 2091*t**22 + 6*X*t**20 + 42582*t**21 + 330*X*t**19 + 627603*t**20 + 8700*X*t**18 + 7134744*t**19 + 15*X**2*t**16 + 146886*X*t**17 + 65042724*t**18 + 660*X**2*t**15 + 1784532*X*t**16 + 487778988*t**17 + 13890*X**2*t**14 + 16594230*X*t**15 + 3061861065*t**16 + 20*X**3*t**12 + 186024*X**2*t**13 + 122552328*X*t**14 + 16280123754*t**15 + 660*X**3*t**11 + 1774887*X**2*t**12 + 735836862*X*t**13 + 73911331425*t**14 + 10380*X**3*t**10 + 12787272*X**2*t**11 + 3646188342*X*t**12 + 287938949178*t**13 + 15*X**4*t**8 + 102576*X**3*t**9 + 71909658*X**2*t**10 + 15047141292*X*t**11 + 964903805434*t**12 + 330*X**4*t**7 + 707604*X**3*t**8 + 321704316*X**2*t**9 + 51955096824*X*t**10 + 2781843718722*t**11 + 3435*X**4*t**6 + 3582876*X**3*t**7 + 1155971196*X**2*t**8 + 150205315932*X*t**9 + 6885805359741*t**10 + 6*X**5*t**4 + 21714*X**4*t**5 + 13632168*X**3*t**6 + 3343499244*X**2*t**7 + 362526695094*X*t**8 + 14569390179114*t**9 + 66*X**5*t**3 + 90660*X**4*t**4 + 39215388*X**3*t**5 + 7747596090*X**2*t**6 + 725403501318*X*t**7 + 26165223178293*t**8 + 336*X**5*t**2 + 255090*X**4*t**3 + 84525732*X**3*t**4 + 14206132008*X**2*t**5 + 1189398495432*X*t**6 + 39474479008356*t**7 + X**6 + 858*X**5*t + 472143*X**4*t**2 + 132886992*X**3*t**3 + 20157510639*X**2*t**4 + 1569568001646*X*t**5 + 49303015587132*t**6 + 1014*X**5 + 525954*X**4*t + 144222780*X**3*t**2 + 21320908440*X**2*t**3 + 1622460290100*X*t**4 + 49941619724976*t**5 + 272259*X**4 + 96482100*X**3*t + 15765293778*X**2*t**2 + 1260038295438*X*t**3 + 39836631701295*t**4 + 29641924*X**3 + 7210949460*X**2*t + 686651250012*X*t**2 + 23947528862166*t**3 + 1506392823*X**2 + 231462513906*X*t + 10114876838391*t**2 + 35655266790*X + 2644809206442*t + 317295487717)
4047
# The coefficients for l=13 are:
4048
# X**6: 1
4049
# X**5: (6) * (t**2 + 5*t + 13) * (t**2 + 6*t + 13)
4050
# X**4: (3) * (t**2 + 5*t + 13) * (t**2 + 6*t + 13) * (5*t**4 + 55*t**3 + 260*t**2 + 583*t + 537)
4051
# X**3: (4) * (t**2 + 5*t + 13) * (t**2 + 6*t + 13)**2 * (5*t**6 + 80*t**5 + 560*t**4 + 2214*t**3 + 5128*t**2 + 6568*t + 3373)
4052
# X**2: (3) * (t**2 + 5*t + 13)**2 * (t**2 + 6*t + 13)**2 * (5*t**8 + 110*t**7 + 1045*t**6 + 5798*t**5 + 20508*t**4 + 47134*t**3 + 67685*t**2 + 54406*t + 17581)
4053
# X**1: (6) * (t**2 + 5*t + 13)**2 * (t**2 + 6*t + 13)**3 * (t**10 + 27*t**9 + 316*t**8 + 2225*t**7 + 10463*t**6 + 34232*t**5 + 78299*t**4 + 122305*t**3 + 122892*t**2 + 69427*t + 16005)
4054
# X**0: (t**2 + 5*t + 13)**2 * (t**2 + 6*t + 13)**3 * (t**14 + 38*t**13 + 649*t**12 + 6844*t**11 + 50216*t**10 + 271612*t**9 + 1115174*t**8 + 3520132*t**7 + 8549270*t**6 + 15812476*t**5 + 21764840*t**4 + 21384124*t**3 + 13952929*t**2 + 5282630*t + 854569)
4055
#
4056
4057
# Here the generic kernel polynomials are actually calculated:
4058
j = Fricke_module(l)
4059
k = j-1728
4060
from sage.misc.all import prod
4061
f = prod( [p for p,e in j.factor() if e==3]
4062
+[p for p,e in k.factor() if e==2])
4063
A4 = -3*t**2*j*k // f**2
4064
A6 = -2*t**3*j*k**2 // f**3
4065
E = EllipticCurve([0,0,0,A4,A6])
4066
assert E.j_invariant() == j
4067
return E.division_polynomial(l,X).factor()[0][0]
4068
4069
4070
def isogenies_prime_degree_genus_0(E, l=None):
4071
"""
4072
Returns list of ``l`` -isogenies with domain ``E``.
4073
4074
INPUT:
4075
4076
- ``E`` -- an elliptic curve.
4077
4078
- ``l`` -- either None or 2, 3, 5, 7, or 13.
4079
4080
OUTPUT:
4081
4082
(list) When ``l`` is None a list of all isogenies of degree 2, 3,
4083
5, 7 and 13, otherwise a list of isogenies of the given degree.
4084
4085
.. note::
4086
4087
This function would normally be invoked indirectly via
4088
``E.isogenies_prime_degree(l)``, which automatically calls the
4089
appropriate function.
4090
4091
EXAMPLES::
4092
4093
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import isogenies_prime_degree_genus_0
4094
sage: E = EllipticCurve([0,12])
4095
sage: isogenies_prime_degree_genus_0(E, 5)
4096
[]
4097
4098
sage: E = EllipticCurve('1450c1')
4099
sage: isogenies_prime_degree_genus_0(E)
4100
[Isogeny of degree 3 from Elliptic Curve defined by y^2 + x*y = x^3 + x^2 + 300*x - 1000 over Rational Field to Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 5950*x - 182250 over Rational Field]
4101
4102
sage: E = EllipticCurve('50a1')
4103
sage: isogenies_prime_degree_genus_0(E)
4104
[Isogeny of degree 3 from Elliptic Curve defined by y^2 + x*y + y = x^3 - x - 2 over Rational Field to Elliptic Curve defined by y^2 + x*y + y = x^3 - 126*x - 552 over Rational Field,
4105
Isogeny of degree 5 from Elliptic Curve defined by y^2 + x*y + y = x^3 - x - 2 over Rational Field to Elliptic Curve defined by y^2 + x*y + y = x^3 - 76*x + 298 over Rational Field]
4106
"""
4107
if not l in [2, 3, 5, 7, 13, None]:
4108
raise ValueError, "Isogenies must be of degree 2, 3, 5, 7 or 13."
4109
F = E.base_ring()
4110
j = E.j_invariant()
4111
if F.characteristic() in [2, 3, l]:
4112
raise NotImplementedError, "2, 3, 5, 7 and 13-isogenies are not yet implemented in characteristic 2 and 3, and when the characteristic is the same as the degree of the isogeny."
4113
if l==2:
4114
return isogenies_2(E)
4115
if l==3:
4116
return isogenies_3(E)
4117
if j==F(0):
4118
if l==5:
4119
return isogenies_5_0(E)
4120
if l==7:
4121
return isogenies_7_0(E)
4122
if l==13:
4123
return isogenies_13_0(E)
4124
if j==F(1728):
4125
if l==5:
4126
return isogenies_5_1728(E)
4127
if l==7:
4128
return isogenies_7_1728(E)
4129
if l==13:
4130
return isogenies_13_1728(E)
4131
4132
if l != None:
4133
R = PolynomialRing(F,'t')
4134
t = R.gen()
4135
f = R(Fricke_polynomial(l))
4136
t_list = (f-j*t).roots(multiplicities=False)
4137
t_list.sort()
4138
# The generic kernel polynomial applies to a standard curve
4139
# E_t with the correct j-invariant; we must compute the
4140
# appropriate twising factor to scale X by:
4141
c4, c6 = E.c_invariants()
4142
T = c4/(3*c6)
4143
jt = Fricke_module(l)
4144
kt = jt-1728
4145
from sage.misc.all import prod
4146
psi = Psi(l)
4147
X = t
4148
f = R(prod( [p for p,e in jt.factor() if e==3]
4149
+[p for p,e in kt.factor() if e==2]))
4150
kernels = [R(psi(X*T*(j-1728)*t0/f(t0),t0)) for t0 in t_list]
4151
kernels = [ker.monic() for ker in kernels]
4152
E1 = EllipticCurve([-27*c4,-54*c6])
4153
w = E.isomorphism_to(E1)
4154
model = "minimal" if F is QQ else None
4155
isogs = [E1.isogeny(kernel=ker, model=model) for ker in kernels]
4156
[isog.set_pre_isomorphism(w) for isog in isogs]
4157
return isogs
4158
4159
if l == None:
4160
return sum([isogenies_prime_degree_genus_0(E, l) for l in [2,3,5,7,13]],[])
4161
4162
4163
# The following is data to be used in isogenies_sporadic_Q. Over Q
4164
# there are only finitely many j-invariants of curves with l-isogenies
4165
# where l is not equal to 2, 3, 5, 7 or 13. In these cases l is equal
4166
# to 11, 17, 19, 37, 43, 67 or 163. We refer to these l as "sporadic".
4167
#
4168
# isog_table is indexed by the possible sporadic (l,j) pairs and lists
4169
# ((a4,a6),f) where (a4,a6) are the invariants of a short Weierstrass
4170
# model of one curve with that j-invariant,and f is the factor of
4171
# degree (l-1)/2 of the l-division polynomial of that curve. Then
4172
# whenever we have a curve of that j-invariant, we can compute the
4173
# corresponding l-isogeny by just scaling f by the right twisting
4174
# factor and using the result as a kernel-polynomial.
4175
4176
# Sample code to precompute this data. Note that working over ZZ
4177
# instead of QQ is a lot faster (in Sage 4.2):
4178
#
4179
#sage: j = -297756989/2; l = 17
4180
#sage: E = EllipticCurve(ZZ,EllipticCurve(j=j).short_weierstrass_model().ainvs())
4181
#sage: E.division_polynomial(l,polygen(ZZ)).factor()[1][0].coefficients()
4182
#
4183
#sage: j = -884736000; l = 43
4184
#sage: E = EllipticCurve(ZZ,EllipticCurve(j=j).short_weierstrass_model().ainvs())
4185
#sage: E.division_polynomial(l,polygen(ZZ)).factor()[1][0].coefficients()
4186
4187
4188
isog_table = dict()
4189
4190
isog_table[(11,QQ('-32768'))] = ([-9504, 365904], [1294672896, -92835072, 1463616, 7920, -264, 1])
4191
4192
isog_table[(11,QQ('-121'))] = ([-3267, -280962], [1480352841, -56169531, -2829222, 10890, 429, 1])
4193
4194
isog_table[(11,QQ('-24729001'))] = ([-38907, -2953962], [-20349931239, -424530315, -134838, 53658, 429, 1])
4195
4196
isog_table[(17,QQ('-297756989/2'))] = ([-3940515, 3010787550], [-6458213126940667330314375, 34699336325466068070000, -72461450055340471500, 68342601718080000, -15140380554450, -25802960400, 23981220, -8160, 1])
4197
4198
isog_table[(17,QQ('-882216989/131072'))] = ([-856035, -341748450], [103687510635057329105625, 961598491955315190000, 1054634146768300500, -6553122389064000, -14554350284850, -2046589200, 13185540, 8160, 1])
4199
4200
isog_table[(19,QQ('-884736'))] = ([-608, 5776], [-34162868224, -8540717056, 6405537792, -1123778560, 84283392, -2033152, -92416, 6992, -152, 1])
4201
4202
isog_table[(37,QQ('-9317'))] = ([-10395, 444150],[-38324677699334121599624973029296875, -17868327793500376961572310472656250, 2569568362004197901139023084765625, -95128267987528547588017818750000, -822168183291347061312510937500, 134395594560592096297190625000, -2881389756919344324888937500, -2503855007083401977250000, 922779077075655997443750, -11503912310262102937500, -18237870962450291250, 1457822151548910000, -10087015556047500, -13677678063000, 490243338900, -2461460400, 5198445, -4410, 1])
4203
4204
isog_table[(37,QQ('-162677523113838677'))] = ([-269675595, -1704553285050], [-31653754873248632711650187487655160190139073510876609346911928661154296875/37, -1469048260972089939455942042937882262144594798448952781325533511718750, -1171741935131505774747142644126089902595908234671576131857702734375, -574934780393177024547076427530739751753985644656221274606250000, -193516922725803688001809624711400287605136013195315374687500, -47085563820928456130325308223963045033502182349693125000, -8472233937388712980597845725196873697064639957437500, -1124815211213953261752081095348112305023653750000, -105684015609077608033913080859605951322531250, -5911406027236569746089675554748135312500, 22343907270397352965399097794968750, 43602171843758666292581116410000, 5054350766002463251474186500, 350135768194635636171000, 16633063574896677300, 549939627039600, 12182993865, 163170, 1])
4205
4206
isog_table[(43,QQ('-884736000'))] = ([-13760, 621264],[-1961864562041980324821547425314935668736, 784270445793223959453256359333693751296, -120528107728500223255333768387027271680, 10335626145581464192664472924270362624, -568426570575654606865505142156820480, 21261993723422650574629752537088000, -544630471727787626557612832587776, 8870521306520473088172555763712, -54993059067301585878494740480, -1434261324709904840432549888, 50978938193065926383894528, -845761855773797582372864, 8627493611216601088000, -48299605284169187328, -32782260293713920, 3415534989828096, -34580115625984, 199359712512, -730488128, 1658080, -2064, 1])
4207
4208
isog_table[(67,QQ('-147197952000'))] = ([-117920, 15585808], [426552448394636714720553816389274308035895411389805883034985546818882031845376, -55876556222880738651382959148329502876096075327084935039031884373558741172224, 3393295715290183821010552313572221545212247684503012173117764703828786020352, -125729166452196578653551230178028570067747190427221869867485520072257044480, 3121342502030777257351089270834971957072933779704445667351054593298530304, -52544031605544530265465344472543470442324636919759253720520768014516224, 532110915869155495738137756847596184665209453108323879594125221167104, -399031158106622651277981701966309467713625045637309782055519780864, -101914346170769215732007802723651742508893380955930030421292613632, 2296526155500449624398016447877283594461904009374321659789443072, -31950871094301541469458501953701002806003991982768349794795520, 329792235011603804948028315065667439678526339671142107709440, -2655636715955021784085217734679612378726442691190553837568, 16825164648840434987220620681420687654501026066872664064, -81705027839007003131400500185224450729843244954288128, 273656504606483403474090105104132405333665144373248, -320807702482945680116212224172370503903312084992, -3166683390779345463318656135338172047199043584, 27871349428383710305216046431806697565585408, -132774697798318602604125735604528772808704, 436096215568182871014215818309741314048, -964687143341252402362763535357837312, 942144169187362941776488535425024, 2794850106281773765892648206336, -17236916236678037389276086272, 50979778712911923486851072, -105035658611718440992768, 161833913559276412928, -188675698610077696, 163929317513984, -102098677888, 42387952, -10184, 1])
4209
4210
isog_table[(163,QQ('-262537412640768000'))] = ([-34790720, 78984748304], [11937033609701771608400008302859181021419097217821452062564326844817505228663098831008549807845273606080544999996020379100442085010011180910986311230744539492209201267851475053850145465949031314131282944262561686476431332761205504055553653209756637748416809823347727611717499380045401030656, -195130071447306655273524569473852352290010571088570084255929596426827972913675959924834214553822436039185267072655230231264389277855757579340840680919752147359277865429131554516271296434318501723193704667609983954393732283396979741770275596479169906167026623493287956522391758781645062144, 1427502734760154784443139998885173873805698542265553947708985997014264802221336771918193375050938963016560184673026895611200993837000754187703921657388546609660768979949394789838000919808563779734300559653270446650866261801622261730386746625133970663551831816512698564779980290036596736, -5599207432852992026535855102327743354503965526118767584653806869099469434869389394471452436350209911401734358692516268244363981178003440912521398279843764610451103952982463228121628686179444592844803463454880601969041617668281227800515971090274318968755339891485627349912827843313664, 7275289443663173375117187980461451723630939807433676133488193878209459535146604043302393215325736700753114238886326617501903060793779318898498537340122928915051396994852249905774785383484964157718761647679761326405167986820403416875550489904598260809039588027484880575324789669888, 53852767252251176711316610474871670511285949778037067647446607721301053062542360857644949076379793988972999080753585666268471181395581176311086709516236139859119228114940726664020344711163961450649040900179940667704933096423082541578086102752168151408016128448757608609516355584, -441024601763314150109608522830440149169771622404264936395215000306188259683627792348927267455527449622616337194882061278315837263834842622422058957336768392927319786456429412773335490683756389242358011775192147290260088212928183893496023497385031567935590156495818971603271680, 1923729652470036657640234466480856403679261373466564217770510758620007251234737653848912112257887106718711179570266106782165127910894792855741763009046440188368346201617134183692667813264354753020558715833289878752200363206184231817927249353441775148372373391040829093576704, -6174291905252822700639099970860178217904525535465573253756653994759145320917644436646210500761314537511653465341404223911052812759676375813287317189557074876672717009913962857515263325087926781282727274494599622098970326546685474959128901831166082055481492871737587531776, 15963605433939241348215386312076502855685142708851007052060264896693306401589916424547272907372369971200908960298559196028000249867821387055883338894215055448441454208145184664251082508258481820123154014612667230187589409024581605410594578299015578613903351914203971584, -34687777075075826936035422448136670540047824478867437603900549472003349053758981331083701038247602826434094223697740838012005963350495346494192086319952577715870471857062004211165110944749546489357024333327851492892063261902845789001287880518909468079710544890691584, 64935614565701660925102862834870557968917923758048315817978089985628103203452108238400473842703675144415856702414743965076964752315718880263247146806212144113325233966196430978981658754610129252819800656290540439882400228191972297000590950499022332532802841477120, -106450113192928986500166510746460290010686605565802553549898115364953461571938149159855780359758326598170956839310849727549767334328481520722457671184262149140997347199434067316119593873464084542168958564857207310255104210252802288265107074620056433232296542208, 154588468341182819097156049664780175805765082709542906853062319968823210709884467518965950031344875777123989196305422245441384128571495551803479155842444104959551491130118809781140758556182128829570214830557657320822341658937375265326185627024588353675722752, -200559814675071715715147513664537685696543665835231560123119674695431140901874837885144270078673500682009909076723885595600964688749788075855566556738418513204701882392037282897429720984011892144438563658741417367819609826485149957684720592798005782904832, 233913230287794067984901680255098952718268709897211662836962202594844321932231309396660409956864579218254273924904394011519168528453166798219739516110916032144001430976170021740673624911101610999174313953331179592523559209806130064362331035786304028672, -246353703535830799051965068435640665615241585380168769205605419959931340172791084855084136413845436489354260626697043416419953596294256639939636661074827002386688825389366314922889935515780006872313799807871034941783710991661439846743406825846079488, 234982742638283161239147075499923192184558615040750093346909554959954534765162320677404212311317141015056956519607730625264033505832417237335526289717986324622989530401755659861160278474288472483456460761582961131223878005034852988312569782468608, -203278213565303083958289848507904983111914464281937008996800600743049033135780053019308180654889993715847194782810638377233384956677984229396984592095978852141937402724743415866647478390224972086303117541277057454016741537801255285401042550784, 159421011266364165773350759598961923468646295930442671951663830439105229019095602850806206849694626149081055860876320252885592409781300283802068036284087012054926328984117466238728138017296876920468741929209733382315316048593848804000661504, -113020918346823224387675962727710849438448560054728764538889949910907956081636084818673918939857811981198128625134001275445247927733612860826192033364144709296859805908448480126055187856540001252440723696556907592809391568682658983575552, 71945777186858148520189111385851657591994785437936790663752048596268908397761761228530149720647914698227096033298596468433406211022425872184679773706985835273196359946527329295352840585601589987654250252509940697181497393634479177728, -40553506248279853197899380016510447069205875473858373944916667311741237594752103405195544436215283638784303466518029972315361472909380024525230365389883507930702477745185560842399992120410631726800835247081534888734998176190169088, 19639696188973552762819015734509385187497753279018359326954702329472344393424778261565226791972589248949076879073752940457430177635689762236418319921157145929450429275277598105096782299310906043071624518383647513981402864418816, -7557145635717782395604678688732309739123765964398036300935902895029921544449817739871127195837816246027098669576710973724603305169613035210748794125702623638061784934800621663541549758981147547220745886014319375033833619456, 1657074863449131529294393850366914004166071540911557375298352670995793916631379837217699843183161870412953109096989706473414891817476345269315000071815674837414249807862563049048940888990780356828116926372826050377809920, 594537607406738614488647166334439665406200193881374352383582637957395810395355986219181323336507140663007282377906531198753011484726171889166159296232442266402815351908351664480142997422997116094990105400004717838336, -1063381335685557601789471169771716160614658527547206527560235661204306306147122486589145738731952820149525220010993135210540125800587297277176980780550749659568216404307556497848427558893515024809067113577642459136, 866504016710397283549081797838552191017551170056714418172658679484342314216337494688411296133019156758665490378562995489461120013841310945167448587950589192882728019730184100760635093600626150305226434294054912, -544456451437710104677464982275658569122639544706468906698603670935572135268235880234260773620839923609980624261711876187191086008709319550421652238153751371247896403810296918434808016324776218527300472274944, 291391938923983436973777049837536733140215048414562274102609644110225500962890424001140034799254161420207705596799832439286323749581485058440291289292376834767289200150488260103720502388531610043446984704, -137669336373928330264003425607593379960726875901008061728935571651808147336578784033262243606118618305155146994101199276509296485578362621442124562716722538959248783675765430495753630524650420015464448, 58305039769563586190846386880024028458953483779423358562963969531061253550565868881984384427294827940260948562007946246585988242539388915757370599713569828414708265704775708180369817440224330907648, -22263674613297409152475382098514675408629524088165884591926938276387287183647211206514841583522457779552325379611699721363009213935995055122374833256328128159685068070573439567341889102047870976, 7659525572873653643686667682470156611730287715779213959462404612107764289631014622079262434509991525269350939684751076168111178931547830737285816290499346814316593688339716060537705476390912, -2356369509601030092354164626593105564281508081035914666378162646456624099153984023946457193322861690246151591627985138670141283491665875837349056368982626874320059053912723460560567402496, 636306064623878566247238117586667093960745761448319133509756604897082306674457051834692637631208787669480579961449146859307914219525683813169045631019945325543755784485150162136596480, -144354698667964959131302121388611435649331689922451077357726958728526160763985021377919777057073652734054824995380136026354322489788621439866623621032883911971992887755746217820160, 24121955586756417060840806312875152536841537469465641066665477185642959713399102667004561835703554642486033432650174169972215286732914841507194929969333501923213681889898397696, -1062982554332400020436047718307967606435230424325029173087078414691100152298424407470637592479633445244263771003475924500470457802708388802132021409732119617604143984672768, -1289959173517591900879403877417447464323361120695839905879179383576917611515563011791770601288187258710836960962551744314331281738572022125094897455138775637822772609024, 713094034093241133139975542207369737377005379966568397923649557581354608722973890056780743426440880397949894405054289149004564424971813787111450018625163532324306944, -254627379967901645020979419858451704766383325190491161773871149760504423262065255127251316663797380198173202687716259997416292667070368594859769612820620832145408, 73141763506085097918144459824978126154049420184880822723900482544151820781804249152002290712777719695036970656528265835764223704583688284269989955443519127552, -17838537225679516940483486695156689114251551141417084228356667456567223074797422210814066353881812352204911178711536306766419580650031647883778868599848960, 3732949472404613028683276858400771022649020140479952475050562287824228475488013407594597423014344735572239887680502683815016952233794807477380489150464, -658090831088275932021697572468169724648153295038942023393017242911882324988827102897240587742441233130939688197251981440661637431663180512231424000, 91044773494274344573228085560493548892450695165631132364575161840790371925870648417274049055693061297980952723608086693629684733411639066361856, -7206875978668204173369106020317806614830036116031258178074085756310402308591937698714169573782276745187407867676712962154996289706219536384, -819751497058465540849522914081577930969395072622202448379227754265195552229316711754131018363430793506957620878700584807196119659446272, 524492219141524596329431820120551446539246304466694008535436881375050446937946483408452355745347329681867559263476448413099237572608, -144831995016562693238485974645571530330971707229081912571899541756458417275452663670185243929333997861822834344607487043313860608, 29914869637979316056234691641068533246353548074478964560456384907200155525359490145390698951538414992573246875654471802683392, -5051703199007242317980870828294675665554938059871421101373455640671888804716466166733214891733545898164243959713215021056, 704266224450794229753813389400889235502895246267920454892105495898823500254340026026030392861415807391779452916596736, -77090914220067457257437168730743598007998214624866646138957596697882331770536104168830311427039146990575867133952, 5185529715527985800186976473360862373518426289807725150611458977619779754244941008386724289180811582624497664, 253997773097193639890870939442154039645526260966991421810387625142025920490445698037959549218977356972032, -167663051891371188948811201282888303918118872228509878481620659513761819092806840088343319460416847872, 37111139278046773379558801627698801653586358926939257719148812112681156722739468064234356797865984, -6094794232647488071603134809336879629321484740792083961518604911291776122012806633249006682112, 837291566508665017874331015912241564512130987193765470835615200761060354034220082158108672, -100296887606046826616864046733353580911852480578758327196473644505408067503452896362496, 10671531126308719182671886696772003009468978048122050876385337250105289881860177920, -1017660211057522997139542974021461951590281854561469513981031668621068390629376, 87341651749945752980300268787472991750253192293865191008561736741703122944, -6755055811837604655292473395831392832731215030180150346004905802596352, 470435697396489049890276884918878813522261509046808135502735081472, -29431936259973770189795237369002643735145492537132570738950144, 1647955729708147681608319291051493831298410618099083509760, -82151504501901440318090519089275042334656836617109504, 3621394962905672351516373383975280471835697741824, -139946424433346723333934826799658073276284928, 4689146702972667351768128807176661303296, -134326059738838559898371882636836864, 3230090486862617839209120497664, -63628315178835325152092160, 992573003659549935360, -11668560578458368, 95539651616, -472048, 1])
4211
4212
4213
4214
def isogenies_sporadic_Q(E, l=None):
4215
"""
4216
Returns list of ``l`` -isogenies with domain ``E`` (defined over `\QQ`).
4217
4218
Returns a list of sporadic l-isogenies from E (l = 11, 17, 19, 37,
4219
43, 67 or 163). Only for elliptic curves over `\QQ`.
4220
4221
INPUT:
4222
4223
- ``E`` -- an elliptic curve defined over `\QQ`.
4224
4225
- ``l`` -- either None or a prime number.
4226
4227
OUTPUT:
4228
4229
(list) If ``l`` is None, a list of all isogenies with domain ``E``
4230
and of degree 11, 17, 19, 37, 43, 67 or 163; otherwise a list of
4231
isogenies of the given degree.
4232
4233
.. note::
4234
4235
This function would normally be invoked indirectly via
4236
``E.isogenies_prime_degree(l)``, which automatically calls the appropriate
4237
function.
4238
4239
EXAMPLES::
4240
4241
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import isogenies_sporadic_Q
4242
sage: E = EllipticCurve('121a1')
4243
sage: isogenies_sporadic_Q(E, 11)
4244
[Isogeny of degree 11 from Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 30*x - 76 over Rational Field to Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 305*x + 7888 over Rational Field]
4245
sage: isogenies_sporadic_Q(E, 13)
4246
[]
4247
sage: isogenies_sporadic_Q(E, 17)
4248
[]
4249
sage: isogenies_sporadic_Q(E)
4250
[Isogeny of degree 11 from Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 30*x - 76 over Rational Field to Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 305*x + 7888 over Rational Field]
4251
4252
sage: E = EllipticCurve([1, 1, 0, -660, -7600])
4253
sage: isogenies_sporadic_Q(E, 17)
4254
[Isogeny of degree 17 from Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 660*x - 7600 over Rational Field to Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 878710*x + 316677750 over Rational Field]
4255
sage: isogenies_sporadic_Q(E)
4256
[Isogeny of degree 17 from Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 660*x - 7600 over Rational Field to Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 878710*x + 316677750 over Rational Field]
4257
sage: isogenies_sporadic_Q(E, 11)
4258
[]
4259
4260
sage: E = EllipticCurve([0, 0, 1, -1862, -30956])
4261
sage: isogenies_sporadic_Q(E, 11)
4262
[]
4263
sage: isogenies_sporadic_Q(E, 19)
4264
[Isogeny of degree 19 from Elliptic Curve defined by y^2 + y = x^3 - 1862*x - 30956 over Rational Field to Elliptic Curve defined by y^2 + y = x^3 - 672182*x + 212325489 over Rational Field]
4265
sage: isogenies_sporadic_Q(E)
4266
[Isogeny of degree 19 from Elliptic Curve defined by y^2 + y = x^3 - 1862*x - 30956 over Rational Field to Elliptic Curve defined by y^2 + y = x^3 - 672182*x + 212325489 over Rational Field]
4267
4268
sage: E = EllipticCurve([0, -1, 0, -6288, 211072])
4269
sage: E.conductor()
4270
19600
4271
sage: isogenies_sporadic_Q(E,37)
4272
[Isogeny of degree 37 from Elliptic Curve defined by y^2 = x^3 - x^2 - 6288*x + 211072 over Rational Field to Elliptic Curve defined by y^2 = x^3 - x^2 - 163137088*x - 801950801728 over Rational Field]
4273
4274
sage: E = EllipticCurve([1, 1, 0, -25178045, 48616918750])
4275
sage: E.conductor()
4276
148225
4277
sage: isogenies_sporadic_Q(E,37)
4278
[Isogeny of degree 37 from Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 25178045*x + 48616918750 over Rational Field to Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 970*x - 13075 over Rational Field]
4279
4280
sage: E = EllipticCurve([-3440, 77658])
4281
sage: E.conductor()
4282
118336
4283
sage: isogenies_sporadic_Q(E,43)
4284
[Isogeny of degree 43 from Elliptic Curve defined by y^2 = x^3 - 3440*x + 77658 over Rational Field to Elliptic Curve defined by y^2 = x^3 - 6360560*x - 6174354606 over Rational Field]
4285
4286
sage: E = EllipticCurve([-29480, -1948226])
4287
sage: E.conductor()
4288
287296
4289
sage: isogenies_sporadic_Q(E,67)
4290
[Isogeny of degree 67 from Elliptic Curve defined by y^2 = x^3 - 29480*x - 1948226 over Rational Field to Elliptic Curve defined by y^2 = x^3 - 132335720*x + 585954296438 over Rational Field]
4291
4292
sage: E = EllipticCurve([-34790720, -78984748304])
4293
sage: E.conductor()
4294
425104
4295
sage: isogenies_sporadic_Q(E,163)
4296
[Isogeny of degree 163 from Elliptic Curve defined by y^2 = x^3 - 34790720*x - 78984748304 over Rational Field to Elliptic Curve defined by y^2 = x^3 - 924354639680*x + 342062961763303088 over Rational Field]
4297
"""
4298
if E.base_ring() != QQ:
4299
raise ValueError, "The elliptic curve must be defined over QQ."
4300
j = E.j_invariant()
4301
j = QQ(j)
4302
if l != None:
4303
if not l.is_prime():
4304
raise ValueError("%s is not prime."%l)
4305
if not isog_table.has_key((l,j)):
4306
return []
4307
Ew = E.short_weierstrass_model()
4308
E_to_Ew = E.isomorphism_to(Ew)
4309
c4, c6 = Ew.c_invariants()
4310
(a4,a6), f = isog_table[(l,j)]
4311
d = (c6*a4)/(18*c4*a6) # twisting factor
4312
R = PolynomialRing(E.base_field(),'X')
4313
n = len(f)
4314
ker = R([d**(n-i-1) * f[i] for i in range(n)])
4315
isog = Ew.isogeny(kernel=ker, degree=l, model="minimal", check=False)
4316
isog.set_pre_isomorphism(E_to_Ew)
4317
return [isog]
4318
if l is None:
4319
if isog_table.has_key((11,j)):
4320
return isogenies_sporadic_Q(E, Integer(11))
4321
if isog_table.has_key((17,j)):
4322
return isogenies_sporadic_Q(E, Integer(17))
4323
if isog_table.has_key((19,j)):
4324
return isogenies_sporadic_Q(E, Integer(19))
4325
if isog_table.has_key((37,j)):
4326
return isogenies_sporadic_Q(E, Integer(37))
4327
if isog_table.has_key((43,j)):
4328
return isogenies_sporadic_Q(E, Integer(43))
4329
if isog_table.has_key((67,j)):
4330
return isogenies_sporadic_Q(E, Integer(67))
4331
if isog_table.has_key((163,j)):
4332
return isogenies_sporadic_Q(E, Integer(163))
4333
else:
4334
return []
4335
4336
def isogenies_2(E):
4337
"""
4338
Returns a list of all 2-isogenies with domain ``E``.
4339
4340
INPUT:
4341
4342
- ``E`` -- an elliptic curve.
4343
4344
OUTPUT:
4345
4346
(list) 2-isogenies with domain ``E``. In general these are
4347
normalised, but over `\QQ` the codomain is a minimal model.
4348
4349
EXAMPLES::
4350
4351
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import isogenies_2
4352
sage: E = EllipticCurve('14a1'); E
4353
Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x - 6 over Rational Field
4354
sage: [phi.codomain().ainvs() for phi in isogenies_2(E)]
4355
[(1, 0, 1, -36, -70)]
4356
4357
sage: E = EllipticCurve([1,2,3,4,5]); E
4358
Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Rational Field
4359
sage: [phi.codomain().ainvs() for phi in isogenies_2(E)]
4360
[]
4361
sage: E = EllipticCurve(QQbar, [9,8]); E
4362
Elliptic Curve defined by y^2 = x^3 + 9*x + 8 over Algebraic Field
4363
sage: isogenies_2(E) # not implemented
4364
"""
4365
f2 = E.division_polynomial(2)
4366
x2 = f2.roots(multiplicities=False)
4367
x2.sort()
4368
x = f2.parent().gen()
4369
ff = [x-x2i for x2i in x2]
4370
model = "minimal" if E.base_field() is QQ else None
4371
isogs = [E.isogeny(f, model=model) for f in ff]
4372
return isogs
4373
4374
def isogenies_3(E):
4375
"""
4376
Returns a list of all 3-isogenies with domain ``E``.
4377
4378
INPUT:
4379
4380
- ``E`` -- an elliptic curve.
4381
4382
OUTPUT:
4383
4384
(list) 3-isogenies with domain ``E``. In general these are
4385
normalised, but over `\QQ` the codomain is a minimal model.
4386
4387
EXAMPLES::
4388
4389
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import isogenies_3
4390
sage: E = EllipticCurve(GF(17), [1,1])
4391
sage: [phi.codomain().ainvs() for phi in isogenies_3(E)]
4392
[(0, 0, 0, 9, 7), (0, 0, 0, 0, 1)]
4393
4394
sage: E = EllipticCurve(GF(17^2,'a'), [1,1])
4395
sage: [phi.codomain().ainvs() for phi in isogenies_3(E)]
4396
[(0, 0, 0, 9, 7), (0, 0, 0, 0, 1), (0, 0, 0, 5*a + 1, a + 13), (0, 0, 0, 12*a + 6, 16*a + 14)]
4397
4398
sage: E = EllipticCurve('19a1')
4399
sage: [phi.codomain().ainvs() for phi in isogenies_3(E)]
4400
[(0, 1, 1, 1, 0), (0, 1, 1, -769, -8470)]
4401
4402
sage: E = EllipticCurve([1,1])
4403
sage: [phi.codomain().ainvs() for phi in isogenies_3(E)]
4404
[]
4405
"""
4406
f3 = E.division_polynomial(3)
4407
x3 = f3.roots(multiplicities=False)
4408
x3.sort()
4409
x = f3.parent().gen()
4410
ff = [x-x3i for x3i in x3]
4411
model = "minimal" if E.base_field() is QQ else None
4412
isogs = [E.isogeny(f, model=model) for f in ff]
4413
return isogs
4414
4415
# 6 special cases: `l` = 5, 7, 13 and `j` = 0, 1728.
4416
4417
def isogenies_5_0(E):
4418
"""
4419
Returns a list of all the 5-isogenies with domain ``E`` when the
4420
j-invariant is 0.
4421
4422
OUTPUT:
4423
4424
(list) 5-isogenies with codomain E. In general these are
4425
normalised, but over `\QQ` the codomain is a minimal model.
4426
4427
.. note::
4428
4429
This implementation requires that the characteristic is not 2,
4430
3 or 5.
4431
4432
.. note::
4433
4434
This function would normally be invoked indirectly via ``E.isogenies_prime_degree(5)``.
4435
4436
EXAMPLES::
4437
4438
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import isogenies_5_0
4439
sage: E = EllipticCurve([0,12])
4440
sage: isogenies_5_0(E)
4441
[]
4442
4443
sage: E = EllipticCurve(GF(13^2,'a'),[0,-3])
4444
sage: isogenies_5_0(E)
4445
[Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2 to Elliptic Curve defined by y^2 = x^3 + (4*a+6)*x + (2*a+10) over Finite Field in a of size 13^2, Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2 to Elliptic Curve defined by y^2 = x^3 + (12*a+5)*x + (2*a+10) over Finite Field in a of size 13^2, Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2 to Elliptic Curve defined by y^2 = x^3 + (10*a+2)*x + (2*a+10) over Finite Field in a of size 13^2, Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2 to Elliptic Curve defined by y^2 = x^3 + (3*a+12)*x + (11*a+12) over Finite Field in a of size 13^2, Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2 to Elliptic Curve defined by y^2 = x^3 + (a+4)*x + (11*a+12) over Finite Field in a of size 13^2, Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2 to Elliptic Curve defined by y^2 = x^3 + (9*a+10)*x + (11*a+12) over Finite Field in a of size 13^2]
4446
4447
sage: K.<a> = NumberField(x**6-320*x**3-320)
4448
sage: E = EllipticCurve(K,[0,0,1,0,0])
4449
sage: isogenies_5_0(E)
4450
[Isogeny of degree 5 from Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^6 - 320*x^3 - 320 to Elliptic Curve defined by y^2 = x^3 + (a^5-400*a^2)*x + (280*a^3-3120) over Number Field in a with defining polynomial x^6 - 320*x^3 - 320,
4451
Isogeny of degree 5 from Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^6 - 320*x^3 - 320 to Elliptic Curve defined by y^2 = x^3 + (23/2*a^5-3700*a^2)*x + (-280*a^3+86480) over Number Field in a with defining polynomial x^6 - 320*x^3 - 320]
4452
4453
"""
4454
F = E.base_field()
4455
if E.j_invariant() != 0:
4456
raise ValueError, "j-invariant must be 0."
4457
if F.characteristic() in [2,3,5]:
4458
raise NotImplementedError, "Not implemented in characteristic 2, 3 or 5."
4459
if not F(5).is_square():
4460
return []
4461
Ew = E.short_weierstrass_model()
4462
a = Ew.a6()
4463
x = polygen(F)
4464
betas = (x**6-160*a*x**3-80*a**2).roots(multiplicities=False)
4465
betas.sort()
4466
if len(betas)==0:
4467
return []
4468
gammas = [(beta**2 *(beta**3-140*a))/(120*a) for beta in betas]
4469
model = "minimal" if F is QQ else None
4470
isogs = [Ew.isogeny(x**2+beta*x+gamma, model=model) for beta,gamma in zip(betas,gammas)]
4471
iso = E.isomorphism_to(Ew)
4472
[isog.set_pre_isomorphism(iso) for isog in isogs]
4473
return isogs
4474
4475
def isogenies_5_1728(E):
4476
"""
4477
Returns a list of 5-isogenies with domain ``E`` when the j-invariant is
4478
1728.
4479
4480
OUTPUT:
4481
4482
(list) 5-isogenies with codomain E. In general these are
4483
normalised; but if `-1` is a square then there are two
4484
endomorphisms of degree `5`, for which the codomain is the same as
4485
the domain curve; and over `\QQ`, the codomain is a minimal model.
4486
4487
.. note::
4488
4489
This implementation requires that the characteristic is not 2,
4490
3 or 5.
4491
4492
.. note::
4493
4494
This function would normally be invoked indirectly via ``E.isogenies_prime_degree(5)``.
4495
4496
EXAMPLES::
4497
4498
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import isogenies_5_1728
4499
sage: E = EllipticCurve([7,0])
4500
sage: isogenies_5_1728(E)
4501
[]
4502
4503
sage: E = EllipticCurve(GF(13),[11,0])
4504
sage: isogenies_5_1728(E)
4505
[Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 + 11*x over Finite Field of size 13 to Elliptic Curve defined by y^2 = x^3 + 11*x over Finite Field of size 13,
4506
Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 + 11*x over Finite Field of size 13 to Elliptic Curve defined by y^2 = x^3 + 11*x over Finite Field of size 13]
4507
4508
An example of endomorphisms of degree 5::
4509
4510
sage: K.<i> = QuadraticField(-1)
4511
sage: E = EllipticCurve(K,[0,0,0,1,0])
4512
sage: isogenies_5_1728(E)
4513
[Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1 to Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1,
4514
Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1 to Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1]
4515
sage: _[0].rational_maps()
4516
(((-4/25*i - 3/25)*x^5 + (-4/5*i + 2/5)*x^3 + x)/(x^4 + (-4/5*i + 2/5)*x^2 + (-4/25*i - 3/25)),
4517
((2/125*i - 11/125)*x^6*y + (64/125*i + 23/125)*x^4*y + (162/125*i - 141/125)*x^2*y + (-4/25*i - 3/25)*y)/(x^6 + (-6/5*i + 3/5)*x^4 + (-12/25*i - 9/25)*x^2 + (2/125*i - 11/125)))
4518
4519
An example of 5-isogenies over a number field::
4520
4521
sage: K.<a> = NumberField(x**4+20*x**2-80)
4522
sage: K(5).is_square() #necessary but not sufficient!
4523
True
4524
sage: E = EllipticCurve(K,[0,0,0,1,0])
4525
sage: isogenies_5_1728(E)
4526
[Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 + x over Number Field in a with defining polynomial x^4 + 20*x^2 - 80 to Elliptic Curve defined by y^2 = x^3 + (-20*a^2-39)*x + (35*a^3+112*a) over Number Field in a with defining polynomial x^4 + 20*x^2 - 80,
4527
Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 + x over Number Field in a with defining polynomial x^4 + 20*x^2 - 80 to Elliptic Curve defined by y^2 = x^3 + (-20*a^2-39)*x + (-35*a^3-112*a) over Number Field in a with defining polynomial x^4 + 20*x^2 - 80]
4528
4529
"""
4530
F = E.base_field()
4531
if E.j_invariant() != 1728:
4532
raise ValueError, "j-invariant must be 1728."
4533
if F.characteristic() in [2,3,5]:
4534
raise NotImplementedError, "Not implemented in characteristic 2, 3 or 5."
4535
model = "minimal" if F is QQ else None
4536
# quick test for a negative answer (from Fricke module)
4537
square5 = F(5).is_square()
4538
square1 = F(-1).is_square()
4539
if not square5 and not square1:
4540
return []
4541
Ew = E.short_weierstrass_model()
4542
iso = E.isomorphism_to(Ew)
4543
a = Ew.a4()
4544
x = polygen(F)
4545
isogs = []
4546
# 2 cases
4547
# Type 1: if -1 is a square we have 2 endomorphisms
4548
if square1:
4549
i = F(-1).sqrt()
4550
isogs = [Ew.isogeny(f) for f in [x**2+a/(1+2*i), x**2+a/(1-2*i)]]
4551
[isog.set_post_isomorphism(isog.codomain().isomorphism_to(E)) for isog in isogs]
4552
# Type 2: if 5 is a square we have up to 4 (non-endomorphism) isogenies
4553
if square5:
4554
betas = (x**4+20*a*x**2-80*a**2).roots(multiplicities=False)
4555
betas.sort()
4556
gammas = [a*(beta**2-2)/6 for beta in betas]
4557
isogs += [Ew.isogeny(x**2+beta*x+gamma, model=model) for beta,gamma in zip(betas,gammas)]
4558
[isog.set_pre_isomorphism(iso) for isog in isogs]
4559
return isogs
4560
4561
def isogenies_7_0(E):
4562
"""
4563
Returns list of all 7-isogenies from E when the j-invariant is 0.
4564
4565
OUTPUT:
4566
4567
(list) 7-isogenies with codomain E. In general these are
4568
normalised; but if `-3` is a square then there are two
4569
endomorphisms of degree `7`, for which the codomain is the same as
4570
the domain; and over `\QQ`, the codomain is a minimal model.
4571
4572
.. note::
4573
4574
This implementation requires that the characteristic is not 2,
4575
3 or 7.
4576
4577
.. note::
4578
4579
This function would normally be invoked indirectly via ``E.isogenies_prime_degree(7)``.
4580
4581
EXAMPLES:
4582
4583
First some examples of endomorphisms::
4584
4585
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import isogenies_7_0
4586
sage: K.<r> = QuadraticField(-3)
4587
sage: E = EllipticCurve(K, [0,1])
4588
sage: isogenies_7_0(E)
4589
[Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + 1 over Number Field in r with defining polynomial x^2 + 3 to Elliptic Curve defined by y^2 = x^3 + 1 over Number Field in r with defining polynomial x^2 + 3,
4590
Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + 1 over Number Field in r with defining polynomial x^2 + 3 to Elliptic Curve defined by y^2 = x^3 + 1 over Number Field in r with defining polynomial x^2 + 3]
4591
4592
sage: E = EllipticCurve(GF(13^2,'a'),[0,-3])
4593
sage: isogenies_7_0(E)
4594
[Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2 to Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2, Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2 to Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2]
4595
4596
Now some examples of 7-isogenies which are not endomorphisms::
4597
4598
sage: K = GF(101)
4599
sage: E = EllipticCurve(K, [0,1])
4600
sage: isogenies_7_0(E)
4601
[Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 101 to Elliptic Curve defined by y^2 = x^3 + 55*x + 100 over Finite Field of size 101, Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 101 to Elliptic Curve defined by y^2 = x^3 + 83*x + 26 over Finite Field of size 101]
4602
4603
Examples over a number field::
4604
4605
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import isogenies_7_0
4606
sage: E = EllipticCurve('27a1').change_ring(QuadraticField(-3,'r'))
4607
sage: isogenies_7_0(E)
4608
[Isogeny of degree 7 from Elliptic Curve defined by y^2 + y = x^3 + (-7) over Number Field in r with defining polynomial x^2 + 3 to Elliptic Curve defined by y^2 + y = x^3 + (-7) over Number Field in r with defining polynomial x^2 + 3,
4609
Isogeny of degree 7 from Elliptic Curve defined by y^2 + y = x^3 + (-7) over Number Field in r with defining polynomial x^2 + 3 to Elliptic Curve defined by y^2 + y = x^3 + (-7) over Number Field in r with defining polynomial x^2 + 3]
4610
4611
sage: K.<a> = NumberField(x^6 + 1512*x^3 - 21168)
4612
sage: E = EllipticCurve(K, [0,1])
4613
sage: isogs = isogenies_7_0(E)
4614
sage: [phi.codomain().a_invariants() for phi in isogs]
4615
[(0, 0, 0, -5/294*a^5 - 300/7*a^2, -55/2*a^3 - 1133),
4616
(0, 0, 0, -295/1176*a^5 - 5385/14*a^2, 55/2*a^3 + 40447)]
4617
sage: [phi.codomain().j_invariant() for phi in isogs]
4618
[158428486656000/7*a^3 - 313976217600000,
4619
-158428486656000/7*a^3 - 34534529335296000]
4620
"""
4621
if E.j_invariant()!=0:
4622
raise ValueError, "j-invariant must be 0."
4623
F = E.base_field()
4624
if F.characteristic() in [2,3,7]:
4625
raise NotImplementedError, "Not implemented when the characteristic of the base field is 2, 3 or 7."
4626
x = polygen(F)
4627
Ew = E.short_weierstrass_model()
4628
iso = E.isomorphism_to(Ew)
4629
a = Ew.a6()
4630
model = "minimal" if F is QQ else None
4631
4632
# there will be 2 endomorphisms if -3 is a square:
4633
4634
ts = (x**2+3).roots(multiplicities=False)
4635
ts.sort()
4636
kers = [7*x-(2+6*t) for t in ts]
4637
kers = [k(x**3/a).monic() for k in kers]
4638
isogs = [Ew.isogeny(k,model=model) for k in kers]
4639
if len(isogs)>0:
4640
[endo.set_post_isomorphism(endo.codomain().isomorphism_to(E)) for endo in isogs]
4641
4642
# we may have up to 6 other isogenies:
4643
ts = (x**2-21).roots(multiplicities=False)
4644
for t0 in ts:
4645
s3 = a/(28+6*t0)
4646
ss = (x**3-s3).roots(multiplicities=False)
4647
ss.sort()
4648
ker = x**3 - 2*t0*x**2 - 4*t0*x + 4*t0 + 28
4649
kers = [ker(x/s).monic() for s in ss]
4650
isogs += [Ew.isogeny(k, model=model) for k in kers]
4651
4652
[isog.set_pre_isomorphism(iso) for isog in isogs]
4653
return isogs
4654
4655
def isogenies_7_1728(E):
4656
"""
4657
Returns list of all 7-isogenies from E when the j-invariant is 1728.
4658
4659
OUTPUT:
4660
4661
(list) 7-isogenies with codomain E. In general these are
4662
normalised; but over `\QQ` the codomain is a minimal model.
4663
4664
.. note::
4665
4666
This implementation requires that the characteristic is not 2,
4667
3, or 7.
4668
4669
.. note::
4670
4671
This function would normally be invoked indirectly via ``E.isogenies_prime_degree(7)``.
4672
4673
EXAMPLES::
4674
4675
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import isogenies_7_1728
4676
sage: E = EllipticCurve(GF(47), [1, 0])
4677
sage: isogenies_7_1728(E)
4678
[Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 47 to Elliptic Curve defined by y^2 = x^3 + 26 over Finite Field of size 47,
4679
Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 47 to Elliptic Curve defined by y^2 = x^3 + 21 over Finite Field of size 47]
4680
4681
An example in characteristic 53 (for which an earlier implementation did not work)::
4682
4683
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import isogenies_7_1728
4684
sage: E = EllipticCurve(GF(53), [1, 0])
4685
sage: isogenies_7_1728(E)
4686
[]
4687
sage: E = EllipticCurve(GF(53^2,'a'), [1, 0])
4688
sage: [iso.codomain().ainvs() for iso in isogenies_7_1728(E)]
4689
[(0, 0, 0, 36, 19*a + 15), (0, 0, 0, 36, 34*a + 38), (0, 0, 0, 33, 39*a + 28), (0, 0, 0, 33, 14*a + 25), (0, 0, 0, 19, 45*a + 16), (0, 0, 0, 19, 8*a + 37), (0, 0, 0, 3, 45*a + 16), (0, 0, 0, 3, 8*a + 37)]
4690
4691
::
4692
4693
sage: K.<a> = NumberField(x^8 + 84*x^6 - 1890*x^4 + 644*x^2 - 567)
4694
sage: E = EllipticCurve(K, [1, 0])
4695
sage: isogs = isogenies_7_1728(E)
4696
sage: [phi.codomain().a_invariants() for phi in isogs]
4697
[(0,
4698
0,
4699
0,
4700
35/636*a^6 + 55/12*a^4 - 79135/636*a^2 + 1127/212,
4701
155/636*a^7 + 245/12*a^5 - 313355/636*a^3 - 3577/636*a),
4702
(0,
4703
0,
4704
0,
4705
35/636*a^6 + 55/12*a^4 - 79135/636*a^2 + 1127/212,
4706
-155/636*a^7 - 245/12*a^5 + 313355/636*a^3 + 3577/636*a)]
4707
sage: [phi.codomain().j_invariant() for phi in isogs]
4708
[-526110256146528/53*a^6 + 183649373229024*a^4 - 3333881559996576/53*a^2 + 2910267397643616/53,
4709
-526110256146528/53*a^6 + 183649373229024*a^4 - 3333881559996576/53*a^2 + 2910267397643616/53]
4710
sage: E1 = isogs[0].codomain()
4711
sage: E2 = isogs[1].codomain()
4712
sage: E1.is_isomorphic(E2)
4713
False
4714
sage: E1.is_quadratic_twist(E2)
4715
-1
4716
"""
4717
if E.j_invariant()!=1728:
4718
raise ValueError, "j_invariant must be 1728 (in base field)."
4719
F = E.base_field()
4720
if F.characteristic() in [2,3,7]:
4721
raise NotImplementedError, "Not implemented when the characteristic of the base field is 2, 3 or 7."
4722
Ew = E.short_weierstrass_model()
4723
iso = E.isomorphism_to(Ew)
4724
a = Ew.a4()
4725
4726
ts = (Fricke_module(7)-1728).numerator().roots(F,multiplicities=False)
4727
if len(ts)==0:
4728
return []
4729
ts.sort()
4730
isogs = []
4731
model = "minimal" if F is QQ else None
4732
x = polygen(F)
4733
for t0 in ts:
4734
s2 = a/t0
4735
ss = (x**2-s2).roots(multiplicities=False)
4736
ss.sort()
4737
ker = 9*x**3 + (-3*t0**3 - 36*t0**2 - 123*t0)*x**2 + (-8*t0**3 - 101*t0**2 - 346*t0 + 35)*x - 7*t0**3 - 88*t0**2 - 296*t0 + 28
4738
4739
kers = [ker(x/s) for s in ss]
4740
isogs += [Ew.isogeny(k.monic(), model=model) for k in kers]
4741
[isog.set_pre_isomorphism(iso) for isog in isogs]
4742
return isogs
4743
4744
def isogenies_13_0(E):
4745
"""
4746
Returns list of all 13-isogenies from E when the j-invariant is 0.
4747
4748
OUTPUT:
4749
4750
(list) 13-isogenies with codomain E. In general these are
4751
normalised; but if `-3` is a square then there are two
4752
endomorphisms of degree `13`, for which the codomain is the same
4753
as the domain.
4754
4755
.. note::
4756
4757
This implementation requires that the characteristic is not 2,
4758
3 or 13.
4759
4760
.. note::
4761
4762
This function would normally be invoked indirectly via ``E.isogenies_prime_degree(13)``.
4763
4764
EXAMPLES::
4765
4766
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import isogenies_13_0
4767
4768
Endomorphisms of degree 13 will exist when -3 is a square::
4769
4770
sage: K.<r> = QuadraticField(-3)
4771
sage: E = EllipticCurve(K, [0, r]); E
4772
Elliptic Curve defined by y^2 = x^3 + r over Number Field in r with defining polynomial x^2 + 3
4773
sage: isogenies_13_0(E)
4774
[Isogeny of degree 13 from Elliptic Curve defined by y^2 = x^3 + r over Number Field in r with defining polynomial x^2 + 3 to Elliptic Curve defined by y^2 = x^3 + r over Number Field in r with defining polynomial x^2 + 3,
4775
Isogeny of degree 13 from Elliptic Curve defined by y^2 = x^3 + r over Number Field in r with defining polynomial x^2 + 3 to Elliptic Curve defined by y^2 = x^3 + r over Number Field in r with defining polynomial x^2 + 3]
4776
sage: isogenies_13_0(E)[0].rational_maps()
4777
(((-4/169*r - 11/169)*x^13 + (-128/13*r - 456/13)*x^10 + (-1224/13*r - 2664/13)*x^7 + (-2208/13*r + 5472/13)*x^4 + (1152/13*r - 8064/13)*x)/(x^12 + (4*r - 36)*x^9 + (-1080/13*r + 3816/13)*x^6 + (2112/13*r + 5184/13)*x^3 + (17280/169*r - 1152/169)), ((18/2197*r - 35/2197)*x^18*y + (-23142/2197*r + 35478/2197)*x^15*y + (-1127520/2197*r + 1559664/2197)*x^12*y + (87744/2197*r + 5992704/2197)*x^9*y + (-6625152/2197*r + 9085824/2197)*x^6*y + (28919808/2197*r - 2239488/2197)*x^3*y + (-1990656/2197*r + 3870720/2197)*y)/(x^18 + (6*r - 54)*x^15 + (-3024/13*r + 11808/13)*x^12 + (31296/13*r - 51840/13)*x^9 + (-487296/169*r - 2070144/169)*x^6 + (-940032/169*r - 248832/169)*x^3 + (-1990656/2197*r + 3870720/2197)))
4778
4779
An example of endomorphisms over a finite field::
4780
4781
sage: K = GF(19^2,'a')
4782
sage: E = EllipticCurve(j=K(0)); E
4783
Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 19^2
4784
sage: isogenies_13_0(E)
4785
[Isogeny of degree 13 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 19^2 to Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 19^2,
4786
Isogeny of degree 13 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 19^2 to Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 19^2]
4787
sage: isogenies_13_0(E)[0].rational_maps()
4788
((6*x^13 - 6*x^10 - 3*x^7 + 6*x^4 + x)/(x^12 - 5*x^9 - 9*x^6 - 7*x^3 + 5), (-8*x^18*y - 9*x^15*y + 9*x^12*y - 5*x^9*y + 5*x^6*y - 7*x^3*y + 7*y)/(x^18 + 2*x^15 + 3*x^12 - x^9 + 8*x^6 - 9*x^3 + 7))
4789
4790
A previous implementation did not work in some characteristics::
4791
4792
sage: K = GF(29)
4793
sage: E = EllipticCurve(j=K(0))
4794
sage: isogenies_13_0(E)
4795
[Isogeny of degree 13 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 29 to Elliptic Curve defined by y^2 = x^3 + 26*x + 12 over Finite Field of size 29, Isogeny of degree 13 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 29 to Elliptic Curve defined by y^2 = x^3 + 16*x + 28 over Finite Field of size 29]
4796
4797
::
4798
4799
sage: K = GF(101)
4800
sage: E = EllipticCurve(j=K(0)); E.ainvs()
4801
(0, 0, 0, 0, 1)
4802
sage: [phi.codomain().ainvs() for phi in isogenies_13_0(E)]
4803
[(0, 0, 0, 64, 36), (0, 0, 0, 42, 66)]
4804
4805
::
4806
4807
sage: x = polygen(QQ)
4808
sage: f = x^12 + 78624*x^9 - 130308048*x^6 + 2270840832*x^3 - 54500179968
4809
sage: K.<a> = NumberField(f)
4810
sage: E = EllipticCurve(j=K(0)); E.ainvs()
4811
(0, 0, 0, 0, 1)
4812
sage: [phi.codomain().ainvs() for phi in isogenies_13_0(E)]
4813
[(0, 0, 0, -739946459/23857162861049856*a^11 - 2591641747/1062017577504*a^8 + 16583647773233/4248070310016*a^5 - 14310911337/378211388*a^2, 26146225/4248070310016*a^9 + 7327668845/14750244132*a^6 + 174618431365/756422776*a^3 - 378332499709/94552847), (0, 0, 0, 3501275/5964290715262464*a^11 + 24721025/531008788752*a^8 - 47974903745/1062017577504*a^5 - 6773483100/94552847*a^2, 6699581/4248070310016*a^9 + 1826193509/14750244132*a^6 - 182763866047/756422776*a^3 - 321460597/94552847)]
4814
"""
4815
if E.j_invariant()!=0:
4816
raise ValueError, "j-invariant must be 0."
4817
F = E.base_field()
4818
if F.characteristic() in [2,3,13]:
4819
raise NotImplementedError, "Not implemented when the characteristic of the base field is 2, 3 or 13."
4820
Ew = E.short_weierstrass_model()
4821
iso = E.isomorphism_to(Ew)
4822
a = Ew.a6()
4823
model = "minimal" if F is QQ else None
4824
x = polygen(F)
4825
4826
# there will be 2 endomorphisms if -3 is a square:
4827
ts = (x**2+3).roots(multiplicities=False)
4828
ts.sort()
4829
kers = [13*x**2 + (78*t + 26)*x + 24*t + 40 for t in ts]
4830
kers = [k(x**3/a).monic() for k in kers]
4831
isogs = [Ew.isogeny(k,model=model) for k in kers]
4832
if len(isogs)>0:
4833
[endo.set_post_isomorphism(endo.codomain().isomorphism_to(E)) for endo in isogs]
4834
4835
# we may have up to 12 other isogenies:
4836
ts = (x**4 + 7*x**3 + 20*x**2 + 19*x + 1).roots(multiplicities=False)
4837
ts.sort()
4838
for t0 in ts:
4839
s3 = a / (6*t0**3 + 32*t0**2 + 68*t0 + 4)
4840
ss = (x**3-s3).roots(multiplicities=False)
4841
ss.sort()
4842
ker = (x**6 + (20*t0**3 + 106*t0**2 + 218*t0 + 4)*x**5
4843
+ (-826*t0**3 - 4424*t0**2 - 9244*t0 - 494)*x**4
4844
+ (13514*t0**3 + 72416*t0**2 + 151416*t0 + 8238)*x**3
4845
+ (-101948*t0**3 - 546304*t0**2 - 1142288*t0 - 62116)*x**2
4846
+ (354472*t0**3 + 1899488*t0**2 + 3971680*t0 + 215960)*x
4847
- 459424*t0**3 - 2461888*t0**2 - 5147648*t0 - 279904)
4848
kers = [ker(x/s).monic() for s in ss]
4849
isogs += [Ew.isogeny(k, model=model) for k in kers]
4850
4851
[isog.set_pre_isomorphism(iso) for isog in isogs]
4852
4853
return isogs
4854
4855
def isogenies_13_1728(E):
4856
"""
4857
Returns list of all 13-isogenies from E when the j-invariant is 1728.
4858
4859
OUTPUT:
4860
4861
(list) 13-isogenies with codomain E. In general these are
4862
normalised; but if `-1` is a square then there are two
4863
endomorphisms of degree `13`, for which the codomain is the same
4864
as the domain; and over `\QQ`, the codomain is a minimal model.
4865
4866
.. note::
4867
4868
This implementation requires that the characteristic is not
4869
2, 3 or 13.
4870
4871
.. note::
4872
4873
This function would normally be invoked indirectly via ``E.isogenies_prime_degree(13)``.
4874
4875
EXAMPLES::
4876
4877
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import isogenies_13_1728
4878
4879
sage: K.<i> = QuadraticField(-1)
4880
sage: E = EllipticCurve([0,0,0,i,0]); E.ainvs()
4881
(0, 0, 0, i, 0)
4882
sage: isogenies_13_1728(E)
4883
[Isogeny of degree 13 from Elliptic Curve defined by y^2 = x^3 + i*x over Number Field in i with defining polynomial x^2 + 1 to Elliptic Curve defined by y^2 = x^3 + i*x over Number Field in i with defining polynomial x^2 + 1,
4884
Isogeny of degree 13 from Elliptic Curve defined by y^2 = x^3 + i*x over Number Field in i with defining polynomial x^2 + 1 to Elliptic Curve defined by y^2 = x^3 + i*x over Number Field in i with defining polynomial x^2 + 1]
4885
4886
::
4887
4888
sage: K = GF(83)
4889
sage: E = EllipticCurve(K, [0,0,0,5,0]); E.ainvs()
4890
(0, 0, 0, 5, 0)
4891
sage: isogenies_13_1728(E)
4892
[]
4893
sage: K = GF(89)
4894
sage: E = EllipticCurve(K, [0,0,0,5,0]); E.ainvs()
4895
(0, 0, 0, 5, 0)
4896
sage: isogenies_13_1728(E)
4897
[Isogeny of degree 13 from Elliptic Curve defined by y^2 = x^3 + 5*x over Finite Field of size 89 to Elliptic Curve defined by y^2 = x^3 + 5*x over Finite Field of size 89,
4898
Isogeny of degree 13 from Elliptic Curve defined by y^2 = x^3 + 5*x over Finite Field of size 89 to Elliptic Curve defined by y^2 = x^3 + 5*x over Finite Field of size 89]
4899
4900
::
4901
4902
sage: K = GF(23)
4903
sage: E = EllipticCurve(K, [1,0])
4904
sage: isogenies_13_1728(E)
4905
[Isogeny of degree 13 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 23 to Elliptic Curve defined by y^2 = x^3 + 16 over Finite Field of size 23, Isogeny of degree 13 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 23 to Elliptic Curve defined by y^2 = x^3 + 7 over Finite Field of size 23]
4906
4907
::
4908
4909
sage: x = polygen(QQ)
4910
sage: f = x^12 + 1092*x^10 - 432432*x^8 + 6641024*x^6 - 282896640*x^4 - 149879808*x^2 - 349360128
4911
sage: K.<a> = NumberField(f)
4912
sage: E = EllipticCurve(K, [1,0])
4913
sage: [phi.codomain().ainvs() for phi in isogenies_13_1728(E)]
4914
[(0,
4915
0,
4916
0,
4917
11090413835/20943727039698624*a^10 + 32280103535965/55849938772529664*a^8 - 355655987835845/1551387188125824*a^6 + 19216954517530195/5235931759924656*a^4 - 1079766118721735/5936430566808*a^2 + 156413528482727/8080141604822,
4918
214217013065/82065216155553792*a^11 + 1217882637605/427423000810176*a^9 - 214645003230565/189965778137856*a^7 + 22973355421236025/1282269002430528*a^5 - 2059145797340695/2544184528632*a^3 - 23198483147321/989405094468*a),
4919
(0,
4920
0,
4921
0,
4922
11090413835/20943727039698624*a^10 + 32280103535965/55849938772529664*a^8 - 355655987835845/1551387188125824*a^6 + 19216954517530195/5235931759924656*a^4 - 1079766118721735/5936430566808*a^2 + 156413528482727/8080141604822,
4923
-214217013065/82065216155553792*a^11 - 1217882637605/427423000810176*a^9 + 214645003230565/189965778137856*a^7 - 22973355421236025/1282269002430528*a^5 + 2059145797340695/2544184528632*a^3 + 23198483147321/989405094468*a)]
4924
4925
"""
4926
if E.j_invariant()!=1728:
4927
raise ValueError, "j-invariant must be 1728."
4928
F = E.base_field()
4929
if F.characteristic() in [2, 3, 7, 13, 167, 233, 271, 1117]:
4930
raise NotImplementedError, "Not implemented when the characteristic of the base field is 2, 3 or 13."
4931
Ew = E.short_weierstrass_model()
4932
iso = E.isomorphism_to(Ew)
4933
a = Ew.a4()
4934
model = "minimal" if F is QQ else None
4935
x = polygen(F)
4936
4937
# we will have two endomorphisms if -1 is a square:
4938
ts = (x**2+1).roots(multiplicities=False)
4939
ts.sort()
4940
kers = [13*x**3 + (-26*i - 13)*x**2 + (-52*i - 13)*x - 2*i - 3 for i in ts]
4941
kers = [k(x**2/a).monic() for k in kers]
4942
isogs = [Ew.isogeny(k,model=model) for k in kers]
4943
if len(isogs)>0:
4944
[endo.set_post_isomorphism(endo.codomain().isomorphism_to(E)) for endo in isogs]
4945
4946
# we may have up to 12 other isogenies:
4947
4948
ts = (x**6 + 10*x**5 + 46*x**4 + 108*x**3 + 122*x**2 + 38*x - 1).roots(multiplicities=False)
4949
ts.sort()
4950
for t0 in ts:
4951
s2 = a/(66*t0**5 + 630*t0**4 + 2750*t0**3 + 5882*t0**2 + 5414*t0 + 162)
4952
ss = (x**2-s2).roots(multiplicities=False)
4953
ss.sort()
4954
ker = (x**6 + (-66*t0**5 - 630*t0**4 - 2750*t0**3 - 5882*t0**2
4955
- 5414*t0 - 162)*x**5 + (-21722*t0**5 - 205718*t0**4 -
4956
890146*t0**3 - 1873338*t0**2 - 1652478*t0 + 61610)*x**4
4957
+ (-3391376*t0**5 - 32162416*t0**4 - 139397232*t0**3 -
4958
294310576*t0**2 - 261885968*t0 + 6105552)*x**3 +
4959
(-241695080*t0**5 - 2291695976*t0**4 - 9930313256*t0**3
4960
- 20956609720*t0**2 - 18625380856*t0 + 469971320)*x**2 +
4961
(-8085170432*t0**5 - 76663232384*t0**4 -
4962
332202985024*t0**3 - 701103233152*t0**2 -
4963
623190845440*t0 + 15598973056)*x - 101980510208*t0**5 -
4964
966973468160*t0**4 - 4190156868352*t0**3 -
4965
8843158270336*t0**2 - 7860368751232*t0 + 196854655936)
4966
4967
kers = [ker(x/s).monic() for s in ss]
4968
isogs += [Ew.isogeny(k, model=model) for k in kers]
4969
4970
[isog.set_pre_isomorphism(iso) for isog in isogs]
4971
4972
return isogs
4973
4974
# Utility function for manipulating isogeny degree matrices
4975
4976
def fill_isogeny_matrix(M):
4977
"""
4978
Returns a filled isogeny matrix giving all degrees from one giving only prime degrees.
4979
4980
INPUT:
4981
4982
- ``M`` -- a square symmetric matrix whose off-diagonal `i`, `j`
4983
entry is either a prime `l` (if the `i`'th and `j`'th curves
4984
have an `l`-isogeny between them), otherwise is 0.
4985
4986
OUTPUT:
4987
4988
(matrix) a square matrix with entries `1` on the diagonal, and in
4989
general the `i`, `j` entry is `d>0` if `d` is the minimal degree
4990
of an isogeny from the `i`'th to the `j`'th curve,
4991
4992
EXAMPLES::
4993
4994
sage: M = Matrix([[0, 2, 3, 3, 0, 0], [2, 0, 0, 0, 3, 3], [3, 0, 0, 0, 2, 0], [3, 0, 0, 0, 0, 2], [0, 3, 2, 0, 0, 0], [0, 3, 0, 2, 0, 0]]); M
4995
[0 2 3 3 0 0]
4996
[2 0 0 0 3 3]
4997
[3 0 0 0 2 0]
4998
[3 0 0 0 0 2]
4999
[0 3 2 0 0 0]
5000
[0 3 0 2 0 0]
5001
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix
5002
sage: fill_isogeny_matrix(M)
5003
[ 1 2 3 3 6 6]
5004
[ 2 1 6 6 3 3]
5005
[ 3 6 1 9 2 18]
5006
[ 3 6 9 1 18 2]
5007
[ 6 3 2 18 1 9]
5008
[ 6 3 18 2 9 1]
5009
"""
5010
from sage.matrix.all import Matrix
5011
from sage.rings.infinity import Infinity
5012
5013
n = M.nrows()
5014
M0 = copy(M)
5015
for i in range(n):
5016
M0[i,i]=1
5017
5018
def fix(d):
5019
if d==0: return Infinity
5020
return d
5021
5022
def fix2(d):
5023
if d==Infinity: return 0
5024
return d
5025
5026
def pr(M1,M2):
5027
return Matrix([[fix2(min([fix(M1[i,k]*M2[k,j]) for k in range(n)])) for i in range(n)] for j in range(n)])
5028
5029
M1 = M0
5030
M2 = pr(M0,M1)
5031
while M1!=M2:
5032
M1 = M2
5033
M2 = pr(M0,M1)
5034
5035
return M1
5036
5037
def unfill_isogeny_matrix(M):
5038
"""
5039
Reverses the action of ``fill_isogeny_matrix``.
5040
5041
INPUT:
5042
5043
- ``M`` -- a square symmetric matrix of integers.
5044
5045
OUTPUT:
5046
5047
(matrix) a square symmetric matrix obtained from ``M`` by
5048
replacing non-prime entries with `0`.
5049
5050
EXAMPLES::
5051
5052
sage: M = Matrix([[0, 2, 3, 3, 0, 0], [2, 0, 0, 0, 3, 3], [3, 0, 0, 0, 2, 0], [3, 0, 0, 0, 0, 2], [0, 3, 2, 0, 0, 0], [0, 3, 0, 2, 0, 0]]); M
5053
[0 2 3 3 0 0]
5054
[2 0 0 0 3 3]
5055
[3 0 0 0 2 0]
5056
[3 0 0 0 0 2]
5057
[0 3 2 0 0 0]
5058
[0 3 0 2 0 0]
5059
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix, unfill_isogeny_matrix
5060
sage: M1 = fill_isogeny_matrix(M); M1
5061
[ 1 2 3 3 6 6]
5062
[ 2 1 6 6 3 3]
5063
[ 3 6 1 9 2 18]
5064
[ 3 6 9 1 18 2]
5065
[ 6 3 2 18 1 9]
5066
[ 6 3 18 2 9 1]
5067
sage: unfill_isogeny_matrix(M1)
5068
[0 2 3 3 0 0]
5069
[2 0 0 0 3 3]
5070
[3 0 0 0 2 0]
5071
[3 0 0 0 0 2]
5072
[0 3 2 0 0 0]
5073
[0 3 0 2 0 0]
5074
sage: unfill_isogeny_matrix(M1) == M
5075
True
5076
"""
5077
from sage.matrix.all import Matrix
5078
from sage.rings.infinity import Infinity
5079
5080
n = M.nrows()
5081
M1 = copy(M)
5082
zero = Integer(0)
5083
for i in range(n):
5084
M1[i,i] = zero
5085
for j in range(i):
5086
if not M1[i,j].is_prime():
5087
M1[i,j] = zero
5088
M1[j,i] = zero
5089
return M1
5090
5091