Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py
8820 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, separable 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
"""
54
55
#*****************************************************************************
56
# Copyright (C) 2009 Daniel Shumow <[email protected]>
57
#
58
# Distributed under the terms of the GNU General Public License (GPL)
59
# http://www.gnu.org/licenses/
60
#*****************************************************************************
61
62
from copy import deepcopy, copy
63
64
from sage.categories import homset
65
66
from sage.categories.morphism import Morphism
67
68
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
69
from sage.rings.polynomial.polynomial_ring import polygen
70
from sage.rings.all import Integer, ZZ
71
from sage.rings.laurent_series_ring import LaurentSeriesRing
72
from sage.rings.polynomial.all import is_Polynomial
73
from sage.schemes.elliptic_curves.all import EllipticCurve
74
from sage.schemes.elliptic_curves.all import is_EllipticCurve
75
76
from sage.rings.number_field.number_field_base import is_NumberField
77
78
from sage.rings.rational_field import is_RationalField, QQ
79
80
from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
81
82
from sage.sets.set import Set
83
84
from sage.misc.cachefunc import cached_function
85
86
#
87
# Private function for parsing input to determine the type of
88
# algorithm
89
#
90
def isogeny_determine_algorithm(E, kernel, codomain, degree, model):
91
r"""
92
Helper function that allows the various isogeny functions to infer
93
the algorithm type from the parameters passed in.
94
95
If ``kernel`` is a list of points on the EllipticCurve `E`, then
96
we assume the algorithm to use is Velu.
97
98
If ``kernel`` is a list of coefficients or a univariate polynomial
99
we try to use the Kohel's algorithms.
100
101
EXAMPLES:
102
103
This helper function will be implicitly called by the following examples::
104
105
sage: R.<x> = GF(5)[]
106
sage: E = EllipticCurve(GF(5), [0,0,0,1,0])
107
sage: phi = EllipticCurveIsogeny(E, x+3)
108
sage: phi2 = EllipticCurveIsogeny(E, [GF(5)(3),GF(5)(1)])
109
sage: phi == phi2
110
True
111
sage: phi3 = EllipticCurveIsogeny(E, E((2,0)) )
112
sage: phi3 == phi2
113
True
114
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import isogeny_determine_algorithm
115
sage: isogeny_determine_algorithm(E, x+3, None, None, None)
116
'kohel'
117
sage: isogeny_determine_algorithm(E, [3, 1], None, None, None)
118
'kohel'
119
sage: isogeny_determine_algorithm(E, E((2,0)), None, None, None)
120
'velu'
121
122
"""
123
124
kernel_is_list = (type(kernel) == list)
125
126
if not kernel_is_list and kernel in E :
127
kernel = [kernel]
128
kernel_is_list = True
129
130
if (is_Polynomial(kernel) or ( kernel_is_list) and (kernel[0] in E.base_ring()) ):
131
algorithm = "kohel"
132
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
133
algorithm = "velu"
134
else:
135
raise ValueError, "Invalid Parameters to EllipticCurveIsogeny constructor."
136
137
return algorithm
138
139
140
def isogeny_codomain_from_kernel(E, kernel, degree=None):
141
r"""
142
This function computes the isogeny codomain given a kernel.
143
144
INPUT:
145
146
- ``E`` - The domain elliptic curve.
147
148
- ``kernel`` - Either a list of points in the kernel of the isogeny, or a
149
kernel polynomial (specified as a either a univariate
150
polynomial or a coefficient list.)
151
152
- ``degree`` - an integer, (default:``None``) optionally specified degree
153
of the kernel.
154
155
OUTPUT:
156
157
(elliptic curve) the codomain of the separable normalized isogeny
158
from this kernel
159
160
EXAMPLES::
161
162
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import isogeny_codomain_from_kernel
163
sage: E = EllipticCurve(GF(7), [1,0,1,0,1])
164
sage: R.<x> = GF(7)[]
165
sage: isogeny_codomain_from_kernel(E, [4,1], degree=3)
166
Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x + 6 over Finite Field of size 7
167
sage: EllipticCurveIsogeny(E, [4,1]).codomain() == isogeny_codomain_from_kernel(E, [4,1], degree=3)
168
True
169
sage: isogeny_codomain_from_kernel(E, x^3 + x^2 + 4*x + 3)
170
Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x + 6 over Finite Field of size 7
171
sage: isogeny_codomain_from_kernel(E, x^3 + 2*x^2 + 4*x + 3)
172
Elliptic Curve defined by y^2 + x*y + y = x^3 + 5*x + 2 over Finite Field of size 7
173
174
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
175
sage: kernel_list = [E((15,10)), E((10,3)),E((6,5))]
176
sage: isogeny_codomain_from_kernel(E, kernel_list)
177
Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 3*x + 15 over Finite Field of size 19
178
179
"""
180
181
algorithm = isogeny_determine_algorithm(E, kernel, None, degree, None);
182
183
if ("velu"==algorithm):
184
# if we are using Velu's formula, just instantiate the isogeny
185
# and return the codomain
186
codomain = EllipticCurveIsogeny(E, kernel).codomain()
187
elif ("kohel"==algorithm):
188
codomain = compute_codomain_kohel(E, kernel, degree)
189
190
return codomain
191
192
193
def compute_codomain_formula(E, v, w):
194
r"""
195
Given parameters `v` and `w` (as in Velu / Kohel / etc formulas)
196
computes the codomain curve.
197
198
EXAMPLES:
199
200
This formula is used by every Isogeny Instantiation::
201
202
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
203
sage: phi = EllipticCurveIsogeny(E, E((1,2)) )
204
sage: phi.codomain()
205
Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 9*x + 13 over Finite Field of size 19
206
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_codomain_formula
207
sage: v = phi._EllipticCurveIsogeny__v
208
sage: w = phi._EllipticCurveIsogeny__w
209
sage: compute_codomain_formula(E, v, w)
210
Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 9*x + 13 over Finite Field of size 19
211
212
"""
213
214
a1,a2,a3,a4,a6 = E.ainvs()
215
216
A4 = a4 - 5*v
217
A6 = a6 - (a1**2 + 4*a2)*v - 7*w
218
219
return EllipticCurve([a1, a2, a3, A4, A6])
220
221
222
def compute_vw_kohel_even_deg1(x0, y0, a1, a2, a4):
223
r"""
224
The formula for computing `v` and `w` using Kohel's formulas for
225
isogenies of degree 2.
226
227
EXAMPLES:
228
229
This function will be implicitly called by the following example::
230
231
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
232
sage: phi = EllipticCurveIsogeny(E, [9,1])
233
sage: phi
234
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
235
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_vw_kohel_even_deg1
236
sage: a1,a2,a3,a4,a6 = E.ainvs()
237
sage: x0 = -9
238
sage: y0 = -(a1*x0 + a3)/2
239
sage: compute_vw_kohel_even_deg1(x0, y0, a1, a2, a4)
240
(18, 9)
241
242
"""
243
244
v = (3*x0**2 + 2*a2*x0 + a4 - a1*y0)
245
w = x0*v
246
247
return (v,w)
248
249
250
def compute_vw_kohel_even_deg3(b2,b4,s1,s2,s3):
251
r"""
252
The formula for computing `v` and `w` using Kohel's formulas for
253
isogenies of degree 3.
254
255
EXAMPLES:
256
257
This function will be implicitly called by the following example::
258
259
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
260
sage: R.<x> = GF(19)[]
261
sage: phi = EllipticCurveIsogeny(E, x^3 + 7*x^2 + 15*x + 12)
262
sage: phi
263
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
264
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_vw_kohel_even_deg3
265
sage: (b2,b4) = (E.b2(), E.b4())
266
sage: (s1, s2, s3) = (-7, 15, -12)
267
sage: compute_vw_kohel_even_deg3(b2, b4, s1, s2, s3)
268
(4, 7)
269
270
"""
271
272
temp1 = (s1**2 - 2*s2)
273
v = 3*temp1 + b2*s1/2 + 3*b4/2
274
w = 3*(s1**3 - 3*s1*s2 + 3*s3) + b2*temp1/2 + b4*s1/2
275
276
return (v,w)
277
278
279
def compute_vw_kohel_odd(b2,b4,b6,s1,s2,s3,n):
280
r"""
281
This function computes the `v` and `w` according to Kohel's formulas.
282
283
EXAMPLES:
284
285
This function will be implicitly called by the following example::
286
287
sage: E = EllipticCurve(GF(19), [18,17,16,15,14])
288
sage: R.<x> = GF(19)[]
289
sage: phi = EllipticCurveIsogeny(E, x^3 + 14*x^2 + 3*x + 11)
290
sage: phi
291
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
292
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_vw_kohel_odd
293
sage: (b2,b4,b6) = (E.b2(), E.b4(), E.b6())
294
sage: (s1,s2,s3) = (-14,3,-11)
295
sage: compute_vw_kohel_odd(b2,b4,b6,s1,s2,s3,3)
296
(7, 1)
297
298
"""
299
300
v = 6*(s1**2 - 2*s2) + b2*s1 + n*b4
301
w = 10*(s1**3 - 3*s1*s2 + 3*s3) + 2*b2*(s1**2 - 2*s2) + 3*b4*s1 + n*b6
302
303
return (v,w)
304
305
306
def compute_codomain_kohel(E, kernel, degree):
307
r"""
308
This function computes the codomain from the kernel polynomial as
309
per Kohel's formulas.
310
311
EXAMPLES::
312
313
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_codomain_kohel
314
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
315
sage: phi = EllipticCurveIsogeny(E, [9,1])
316
sage: phi.codomain() == isogeny_codomain_from_kernel(E, [9,1])
317
True
318
sage: compute_codomain_kohel(E, [9,1], 2)
319
Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 9*x + 8 over Finite Field of size 19
320
sage: R.<x> = GF(19)[]
321
sage: E = EllipticCurve(GF(19), [18,17,16,15,14])
322
sage: phi = EllipticCurveIsogeny(E, x^3 + 14*x^2 + 3*x + 11)
323
sage: phi.codomain() == isogeny_codomain_from_kernel(E, x^3 + 14*x^2 + 3*x + 11)
324
True
325
sage: compute_codomain_kohel(E, x^3 + 14*x^2 + 3*x + 11, 7)
326
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
327
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
328
sage: phi = EllipticCurveIsogeny(E, x^3 + 7*x^2 + 15*x + 12)
329
sage: isogeny_codomain_from_kernel(E, x^3 + 7*x^2 + 15*x + 12) == phi.codomain()
330
True
331
sage: compute_codomain_kohel(E, x^3 + 7*x^2 + 15*x + 12,4)
332
Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 3*x + 15 over Finite Field of size 19
333
334
NOTES:
335
336
This function uses the formulas of Section 2.4 of [K96].
337
338
REFERENCES:
339
340
- [K96] Kohel, "Endomorphism Rings of Elliptic Curves over Finite Fields"
341
342
"""
343
344
# First set up the polynomial ring
345
346
base_field = E.base_ring()
347
348
if (is_Polynomial(kernel)):
349
psi = kernel
350
kernel_list = psi.list()
351
poly_ring = psi.parent()
352
x = psi.variables()[0]
353
elif (list == type(kernel)) and (kernel[0] in base_field):
354
kernel_list = kernel
355
poly_ring = base_field.polynomial_ring()
356
psi = poly_ring(kernel_list)
357
x = poly_ring.gen()
358
else:
359
raise ValueError, "input not of correct type"
360
361
362
# next determine the even / odd part of the isogeny
363
psi_2tor = two_torsion_part(E, poly_ring, psi, degree)
364
365
if (0 != psi_2tor.degree()): # even degree case
366
367
psi_quo = poly_ring(psi/psi_2tor)
368
369
if (0 != psi_quo.degree()):
370
raise ArithmeticError, "For basic Kohel's algorithm, if the kernel degree is even then the kernel must be contained in the two torsion."
371
372
n = psi_2tor.degree()
373
374
if (1 == n):
375
376
a1,a2,a3,a4,a6 = E.ainvs()
377
378
x0 = -psi_2tor.constant_coefficient()
379
380
# determine y0
381
if (2 == base_field.characteristic()):
382
y0 = (x0**3 + a2*x0**2 + a4*x0 + a6).sqrt()
383
else:
384
y0 = -(a1*x0 + a3)/2
385
386
(v,w) = compute_vw_kohel_even_deg1(x0,y0,a1,a2,a4)
387
388
elif (3 == n):
389
390
b2 = E.b2()
391
b4 = E.b4()
392
393
s = psi_2tor.list()
394
s1 = -s[n-1]
395
s2 = s[n-2]
396
s3 = -s[n-3]
397
398
(v,w) = compute_vw_kohel_even_deg3(b2,b4,s1,s2,s3)
399
400
else:
401
402
n = psi.degree()
403
404
b2 = E.b2()
405
b4 = E.b4()
406
b6 = E.b6()
407
408
s1 = 0; s2 = 0; s3 = 0
409
410
if (1 <= n):
411
s1 = -kernel_list[n-1]
412
413
if (2 <= n):
414
s2 = kernel_list[n-2]
415
416
if (3 <= n):
417
s3 = -kernel_list[n-3]
418
419
# initializing these allows us to calculate E2.
420
(v,w) = compute_vw_kohel_odd(b2,b4,b6,s1,s2,s3,n);
421
422
return compute_codomain_formula(E, v, w)
423
424
425
def two_torsion_part(E, poly_ring, psi, degree):
426
r"""
427
428
Returns the greatest common divisor of ``psi`` and the 2 torsion
429
polynomial of `E`.
430
431
EXAMPLES:
432
433
Every function that computes the kernel polynomial via Kohel's
434
formulas will call this function::
435
436
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
437
sage: R.<x> = GF(19)[]
438
sage: phi = EllipticCurveIsogeny(E, x + 13)
439
sage: isogeny_codomain_from_kernel(E, x + 13) == phi.codomain()
440
True
441
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import two_torsion_part
442
sage: two_torsion_part(E, R, x+13, 2)
443
x + 13
444
445
"""
446
if (None==degree) or (0 == degree % 2):
447
448
x = poly_ring.gens()[0]
449
psi_2 = E.two_division_polynomial(x)
450
psi_G = poly_ring(psi.gcd(psi_2))
451
452
else:
453
454
psi_G = poly_ring(1)
455
456
return psi_G
457
458
459
class EllipticCurveIsogeny(Morphism):
460
r"""
461
Class Implementing Isogenies of Elliptic Curves
462
463
This class implements cyclic, separable, normalized isogenies of
464
elliptic curves.
465
466
Several different algorithms for computing isogenies are
467
available. These include:
468
469
- Velu's Formulas: Velu's original formulas for computing
470
isogenies. This algorithm is selected by giving as the
471
``kernel`` parameter a list of points which generate a finite
472
subgroup.
473
474
- Kohel's Formulas: Kohel's original formulas for computing
475
isogenies. This algorithm is selected by giving as the
476
``kernel`` parameter a monic polynomial (or a coefficient list
477
(little endian)) which will define the kernel of the isogeny.
478
479
INPUT:
480
481
- ``E`` - an elliptic curve, the domain of the isogeny to
482
initialize.
483
484
- ``kernel`` - a kernel, either a point in ``E``, a list of points
485
in ``E``, a monic kernel polynomial, or ``None``.
486
If initializing from a domain/codomain, this must be
487
set to None.
488
489
- ``codomain`` - an elliptic curve (default:``None``). If ``kernel``
490
is ``None``, then this must be the codomain of a cyclic,
491
separable, normalized isogeny, furthermore, ``degree``
492
must be the degree of the isogeny from ``E`` to
493
``codomain``. If ``kernel`` is not ``None``, then this
494
must be isomorphic to the codomain of the cyclic normalized
495
separable isogeny defined by ``kernel``, in this case, the
496
isogeny is post composed with an isomorphism so that this
497
parameter is the codomain.
498
499
- ``degree`` - an integer (default:``None``).
500
If ``kernel`` is ``None``, then this is the degree of the
501
isogeny from ``E`` to ``codomain``.
502
If ``kernel`` is not ``None``, then this is used to determine
503
whether or not to skip a gcd of the kernel polynomial with the
504
two torsion polynomial of ``E``.
505
506
- ``model`` - a string (default:``None``). Only supported variable is
507
``minimal``, in which case if ``E`` is a curve over the
508
rationals, then the codomain is set to be the unique global
509
minimum model.
510
511
- ``check`` (default: ``True``) checks if the input is valid to define an isogeny
512
513
EXAMPLES:
514
515
A simple example of creating an isogeny of a field of small
516
characteristic::
517
518
sage: E = EllipticCurve(GF(7), [0,0,0,1,0])
519
sage: phi = EllipticCurveIsogeny(E, E((0,0)) ); phi
520
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
521
sage: phi.degree() == 2
522
True
523
sage: phi.kernel_polynomial()
524
x
525
sage: phi.rational_maps()
526
((x^2 + 1)/x, (x^2*y - y)/x^2)
527
sage: phi == loads(dumps(phi)) # known bug
528
True
529
530
A more complicated example of a characteristic 2 field::
531
532
sage: E = EllipticCurve(GF(2^4,'alpha'), [0,0,1,0,1])
533
sage: P = E((1,1))
534
sage: phi_v = EllipticCurveIsogeny(E, P); phi_v
535
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
536
sage: phi_ker_poly = phi_v.kernel_polynomial()
537
sage: phi_ker_poly
538
x + 1
539
sage: ker_poly_list = phi_ker_poly.list()
540
sage: phi_k = EllipticCurveIsogeny(E, ker_poly_list)
541
sage: phi_k == phi_v
542
True
543
sage: phi_k.rational_maps()
544
((x^3 + x + 1)/(x^2 + 1), (x^3*y + x^2*y + x*y + x + y)/(x^3 + x^2 + x + 1))
545
sage: phi_v.rational_maps()
546
((x^3 + x + 1)/(x^2 + 1), (x^3*y + x^2*y + x*y + x + y)/(x^3 + x^2 + x + 1))
547
sage: phi_k.degree() == phi_v.degree()
548
True
549
sage: phi_k.degree()
550
3
551
sage: phi_k.is_separable()
552
True
553
sage: phi_v(E(0))
554
(0 : 1 : 0)
555
sage: alpha = E.base_field().gen()
556
sage: Q = E((0, alpha*(alpha + 1)))
557
sage: phi_v(Q)
558
(1 : alpha^2 + alpha : 1)
559
sage: phi_v(P) == phi_k(P)
560
True
561
sage: phi_k(P) == phi_v.codomain()(0)
562
True
563
564
We can create an isogeny that has kernel equal to the full 2
565
torsion::
566
567
sage: E = EllipticCurve(GF(3), [0,0,0,1,1])
568
sage: ker_list = E.division_polynomial(2).list()
569
sage: phi = EllipticCurveIsogeny(E, ker_list); phi
570
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
571
sage: phi(E(0))
572
(0 : 1 : 0)
573
sage: phi(E((0,1)))
574
(1 : 0 : 1)
575
sage: phi(E((0,2)))
576
(1 : 0 : 1)
577
sage: phi(E((1,0)))
578
(0 : 1 : 0)
579
sage: phi.degree()
580
4
581
582
We can also create trivial isogenies with the trivial kernel::
583
584
sage: E = EllipticCurve(GF(17), [11, 11, 4, 12, 10])
585
sage: phi_v = EllipticCurveIsogeny(E, E(0))
586
sage: phi_v.degree()
587
1
588
sage: phi_v.rational_maps()
589
(x, y)
590
sage: E == phi_v.codomain()
591
True
592
sage: P = E.random_point()
593
sage: phi_v(P) == P
594
True
595
596
sage: E = EllipticCurve(GF(31), [23, 1, 22, 7, 18])
597
sage: phi_k = EllipticCurveIsogeny(E, [1])
598
sage: phi_k
599
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
600
sage: phi_k.degree()
601
1
602
sage: phi_k.rational_maps()
603
(x, y)
604
sage: phi_k.codomain() == E
605
True
606
sage: phi_k.kernel_polynomial()
607
1
608
sage: P = E.random_point(); P == phi_k(P)
609
True
610
611
Velu and Kohel also work in characteristic 0::
612
613
sage: E = EllipticCurve(QQ, [0,0,0,3,4])
614
sage: P_list = E.torsion_points()
615
sage: phi = EllipticCurveIsogeny(E, P_list)
616
sage: phi
617
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
618
sage: P = E((0,2))
619
sage: phi(P)
620
(6 : -10 : 1)
621
sage: phi_ker_poly = phi.kernel_polynomial()
622
sage: phi_ker_poly
623
x + 1
624
sage: ker_poly_list = phi_ker_poly.list()
625
sage: phi_k = EllipticCurveIsogeny(E, ker_poly_list); phi_k
626
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
627
sage: phi_k(P) == phi(P)
628
True
629
sage: phi_k == phi
630
True
631
sage: phi_k.degree()
632
2
633
sage: phi_k.is_separable()
634
True
635
636
A more complicated example over the rationals (of odd degree)::
637
638
sage: E = EllipticCurve('11a1')
639
sage: P_list = E.torsion_points()
640
sage: phi_v = EllipticCurveIsogeny(E, P_list); phi_v
641
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
642
sage: P = E((16,-61))
643
sage: phi_v(P)
644
(0 : 1 : 0)
645
sage: ker_poly = phi_v.kernel_polynomial(); ker_poly
646
x^2 - 21*x + 80
647
sage: ker_poly_list = ker_poly.list()
648
sage: phi_k = EllipticCurveIsogeny(E, ker_poly_list); phi_k
649
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
650
sage: phi_k == phi_v
651
True
652
sage: phi_v(P) == phi_k(P)
653
True
654
sage: phi_k.is_separable()
655
True
656
657
We can also do this same example over the number field defined by
658
the irreducible two torsion polynomial of `E`::
659
660
sage: E = EllipticCurve('11a1')
661
sage: P_list = E.torsion_points()
662
sage: K.<alpha> = NumberField(x^3 - 2* x^2 - 40*x - 158)
663
sage: EK = E.change_ring(K)
664
sage: P_list = [EK(P) for P in P_list]
665
sage: phi_v = EllipticCurveIsogeny(EK, P_list); phi_v
666
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
667
sage: P = EK((alpha/2,-1/2))
668
sage: phi_v(P)
669
(122/121*alpha^2 + 1633/242*alpha - 3920/121 : -1/2 : 1)
670
sage: ker_poly = phi_v.kernel_polynomial()
671
sage: ker_poly
672
x^2 - 21*x + 80
673
sage: ker_poly_list = ker_poly.list()
674
sage: phi_k = EllipticCurveIsogeny(EK, ker_poly_list)
675
sage: phi_k
676
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
677
sage: phi_v == phi_k
678
True
679
sage: phi_k(P) == phi_v(P)
680
True
681
sage: phi_k == phi_v
682
True
683
sage: phi_k.degree()
684
5
685
sage: phi_v.is_separable()
686
True
687
688
The following example shows how to specify an isogeny from domain
689
and codomain::
690
691
sage: E = EllipticCurve('11a1')
692
sage: R.<x> = QQ[]
693
sage: f = x^2 - 21*x + 80
694
sage: phi = E.isogeny(f)
695
sage: E2 = phi.codomain()
696
sage: phi_s = EllipticCurveIsogeny(E, None, E2, 5)
697
sage: phi_s
698
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
699
sage: phi_s == phi
700
True
701
sage: phi_s.rational_maps() == phi.rational_maps()
702
True
703
704
However only cyclic normalized isogenies can be constructed this
705
way. So it won't find the isogeny [3]::
706
707
sage: E.isogeny(None, codomain=E,degree=9)
708
Traceback (most recent call last):
709
...
710
ValueError: The two curves are not linked by a cyclic normalized isogeny of degree 9
711
712
Also the presumed isogeny between the domain and codomain must be
713
normalized::
714
715
sage: E2.isogeny(None,codomain=E,degree=5)
716
Traceback (most recent call last):
717
...
718
ValueError: The two curves are not linked by a cyclic normalized isogeny of degree 5
719
sage: phi.dual()
720
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
721
sage: phi.dual().is_normalized()
722
False
723
724
Here an example of a construction of a endomorphisms with cyclic
725
kernel on a CM-curve::
726
727
sage: K.<i> = NumberField(x^2+1)
728
sage: E = EllipticCurve(K, [1,0])
729
sage: RK.<X> = K[]
730
sage: f = X^2 - 2/5*i + 1/5
731
sage: phi= E.isogeny(f)
732
sage: isom = phi.codomain().isomorphism_to(E)
733
sage: phi.set_post_isomorphism(isom)
734
sage: phi.codomain() == phi.domain()
735
True
736
sage: phi.rational_maps()
737
(((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)),
738
((11/125*i + 2/125)*x^6*y + (-23/125*i + 64/125)*x^4*y + (141/125*i + 162/125)*x^2*y + (3/25*i - 4/25)*y)/(x^6 + (-6/5*i + 3/5)*x^4 + (-12/25*i - 9/25)*x^2 + (2/125*i - 11/125)))
739
"""
740
741
####################
742
# member variables
743
####################
744
745
__check = None
746
#
747
# variables common to all algorithms
748
#
749
__E1 = None # domain curve
750
__E2 = None # codomain curve
751
752
__degree = None
753
754
__separable = True # This class only implements separable isogenies (for now.)
755
756
__algorithm = None
757
758
__this_hash = None
759
760
__check = None
761
#
762
# pre isomorphism
763
#
764
__intermediate_domain = None
765
__pre_isomorphism = None
766
__prei_x_coord_ratl_map = None
767
__prei_y_coord_ratl_map = None
768
769
#
770
# post isomorphism
771
#
772
773
__intermediate_codomain = None
774
__post_isomorphism = None
775
__posti_x_coord_ratl_map = None
776
__posti_y_coord_ratl_map = None
777
778
#
779
# algebraic structs
780
#
781
__base_field = None
782
__poly_ring = None
783
__x_var = None
784
__y_var = None
785
786
#
787
# Rational Maps
788
#
789
__rational_maps_initialized = False
790
__X_coord_rational_map = None
791
__Y_coord_rational_map = None
792
793
#
794
# The dual
795
#
796
__dual = None
797
798
#
799
# Kernel Data
800
#
801
802
__kernel_list = None # list of elements in the kernel
803
804
__kernel_polynomial_list = None # polynomial stored as a little endian list of coefficients
805
806
__kernel_polynomial = None # polynomial with roots at x values for x-coordinate of points in the kernel
807
808
__inner_kernel_polynomial = None # the inner kernel polynomial (ignoring preisomorphism)
809
810
__n = None
811
812
813
#
814
# member variables common to Velu's formula
815
#
816
817
# we keep track of the 2 torsion and non2torsion separately
818
__kernel_2tor = None
819
__kernel_non2tor = None
820
821
# variables used in Velu's formula (as well as Kohel's variant)
822
__v = None
823
__w = None
824
825
826
#
827
# member variables specific to Kohel's algorithm.
828
#
829
830
__psi = None # psi polynomial
831
__phi = None # phi polynomial
832
__omega = None # omega polynomial
833
834
835
#
836
# Python Special Functions
837
#
838
839
def __init__(self, E, kernel, codomain=None, degree=None, model=None, check=True):
840
r"""
841
Constructor for EllipticCurveIsogeny class.
842
843
EXAMPLES::
844
845
sage: E = EllipticCurve(GF(2), [0,0,1,0,1])
846
sage: phi = EllipticCurveIsogeny(E, [1,1])
847
sage: phi
848
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
849
850
sage: E = EllipticCurve(GF(31), [0,0,0,1,0])
851
sage: P = E((2,17))
852
sage: phi = EllipticCurveIsogeny(E, P)
853
sage: phi
854
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
855
856
sage: E = EllipticCurve('17a1')
857
sage: phi = EllipticCurveIsogeny(E, [41/3, -55, -1, -1, 1])
858
sage: phi
859
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
860
861
sage: E = EllipticCurve('37a1')
862
sage: triv = EllipticCurveIsogeny(E, E(0))
863
sage: triv
864
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
865
sage: triv.rational_maps()
866
(x, y)
867
868
sage: E = EllipticCurve('49a3')
869
sage: R.<X> = QQ[]
870
sage: EllipticCurveIsogeny(E,X^3-13*X^2-58*X+503,check=False)
871
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
872
873
"""
874
875
if not is_EllipticCurve(E):
876
raise ValueError, "E parameter must be an EllipticCurve."
877
878
if type(kernel) != type([1,1]) and kernel in E :
879
# a single point was given, we put it in a list
880
# the first condition assures that [1,1] is treated as x+1
881
kernel = [kernel]
882
883
# if the kernel is None and the codomain isn't
884
# calculate the kernel polynomial
885
pre_isom = None
886
post_isom = None
887
888
self.__check = check
889
890
if (kernel is None) and (codomain is not None):
891
892
if (degree is None):
893
raise ValueError, "If specifying isogeny by domain and codomain, degree parameter must be set."
894
895
# save the domain/codomain: really used now (trac #7096)
896
old_domain = E
897
old_codomain = codomain
898
899
(pre_isom, post_isom, E, codomain, kernel) = compute_sequence_of_maps(E, codomain, degree)
900
901
self.__init_algebraic_structs(E)
902
903
algorithm = isogeny_determine_algorithm(E, kernel, codomain, degree, model);
904
905
self.__algorithm = algorithm
906
907
if ("velu"==algorithm):
908
self.__init_from_kernel_list(kernel)
909
elif ("kohel"==algorithm):
910
self.__init_from_kernel_polynomial(kernel, degree)
911
912
self.__compute_E2()
913
914
self.__setup_post_isomorphism(codomain, model)
915
916
if (pre_isom is not None):
917
self.set_pre_isomorphism(pre_isom)
918
919
if (post_isom is not None):
920
self.__set_post_isomorphism(old_codomain, post_isom) #(trac #7096)
921
922
# Inheritance house keeping
923
924
self.__perform_inheritance_housekeeping()
925
926
return
927
928
929
def __call__(self, P, output_base_ring=None):
930
r"""
931
Function that implements the call-ability of elliptic curve
932
isogenies.
933
934
EXAMPLES::
935
936
sage: E = EllipticCurve(GF(17), [1, 9, 5, 4, 3])
937
sage: phi = EllipticCurveIsogeny(E, [6,13,1])
938
sage: phi(E((1,0)))
939
(15 : 13 : 1)
940
941
sage: E = EllipticCurve(GF(23), [0,0,0,1,0])
942
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
943
sage: phi(E((1,5)))
944
(2 : 0 : 1)
945
sage: phi(E(15,20), output_base_ring=GF(23^2,'alpha'))
946
(12 : 1 : 1)
947
948
sage: E = EllipticCurve(QQ, [0,0,0,3,0])
949
sage: P = E((1,2))
950
sage: phi = EllipticCurveIsogeny(E, [0,1])
951
sage: phi(P)
952
(4 : -4 : 1)
953
sage: phi(-P)
954
(4 : 4 : 1)
955
956
sage: E = EllipticCurve(GF(17), [0,-1,0,-3,-1])
957
sage: Q = E((16,0))
958
sage: tau = E.isogeny([Q],E)
959
sage: tau(Q)
960
(0 : 1 : 0)
961
962
TESTS (trac 10888)::
963
964
sage: K.<th> = NumberField(x^2+3)
965
sage: E = EllipticCurve(K,[7,0])
966
sage: phi = E.isogeny(E(0,0))
967
sage: P = E(-3,4*th)
968
sage: phi(P)
969
(-16/3 : 8/9*th : 1)
970
sage: Q = phi(P)
971
sage: phihat = phi.dual()
972
sage: phihat(Q)
973
(-1/48 : 127/576*th : 1)
974
975
"""
976
E1 = self.__E1
977
E_P = P.curve()
978
change_output_ring = False
979
980
# check that the parent curve of the input point is this curve
981
# or that the point is on the same curve but whose base ring
982
# is an extension of this ring
983
if (E1 != E_P):
984
if (E1.a_invariants() != E_P.a_invariants()) :
985
raise ValueError, "P must be on a curve with same Weierstrass model as the domain curve of this isogeny."
986
change_output_ring = True
987
988
989
if(P.is_zero()):
990
return self.__E2(0)
991
992
(xP, yP) = P.xy()
993
994
if not self.__E1.is_on_curve(xP,yP):
995
raise InputError, "Input point must be on the domain curve of this isogeny."
996
997
# if there is a pre isomorphism, apply it
998
if (self.__pre_isomorphism is not None):
999
temp_xP = self.__prei_x_coord_ratl_map(xP, yP)
1000
temp_yP = self.__prei_y_coord_ratl_map(xP, yP)
1001
(xP, yP) = (temp_xP, temp_yP)
1002
1003
if ("velu" == self.__algorithm):
1004
outP = self.__compute_via_velu_numeric(xP, yP)
1005
elif ("kohel" == self.__algorithm):
1006
outP = self.__compute_via_kohel_numeric(xP,yP)
1007
1008
# the intermediate functions return the point at infinity
1009
# if the input point is in the kernel
1010
if (outP == self.__intermediate_codomain(0)):
1011
return self.__E2(0)
1012
1013
# if there is a post isomorphism, apply it
1014
if (self.__post_isomorphism is not None):
1015
tempX = self.__posti_x_coord_ratl_map(outP[0], outP[1])
1016
tempY = self.__posti_y_coord_ratl_map(outP[0], outP[1])
1017
outP = [tempX, tempY]
1018
1019
if change_output_ring:
1020
if (output_base_ring is None):
1021
output_base_ring = E_P.base_ring()
1022
outE2 = self.__E2.change_ring(output_base_ring)
1023
else:
1024
output_base_ring = self.__E2.base_ring()
1025
outE2 = self.__E2
1026
outP = self.__E2.point(outP,check=False)
1027
1028
R = output_base_ring
1029
1030
return outE2.point([R(outP[0]), R(outP[1]), R(1)], check=False)
1031
1032
1033
def __getitem__(self, i):
1034
self.__initialize_rational_maps()
1035
if (i < 0) or (i > 2):
1036
raise IndexError
1037
1038
if i == 0:
1039
return self.__X_coord_rational_map
1040
else:
1041
return self.__Y_coord_rational_map
1042
1043
def __iter__(self):
1044
self.__initialize_rational_maps()
1045
return iter((self.__X_coord_rational_map, self.__Y_coord_rational_map))
1046
1047
def __hash__(self):
1048
r"""
1049
Function that implements the hash ability of Isogeny objects.
1050
1051
This hashes the underlying kernel polynomial so that equal
1052
isogeny objects have the same hash value. Also, this hashes
1053
the base field, and domain and codomain curves as well, so
1054
that isogenies with the same kernel polynomial (over different
1055
base fields / curves) hash to different values.
1056
1057
EXAMPLES::
1058
1059
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
1060
sage: phi_v = EllipticCurveIsogeny(E, E((0,0)))
1061
sage: phi_k = EllipticCurveIsogeny(E, [0,1])
1062
sage: phi_k.__hash__() == phi_v.__hash__()
1063
True
1064
sage: E_F17 = EllipticCurve(GF(17), [0,0,0,1,1])
1065
sage: phi_p = EllipticCurveIsogeny(E_F17, E_F17([0,1]))
1066
sage: phi_p.__hash__() == phi_v.__hash__()
1067
False
1068
1069
sage: E = EllipticCurve('49a3')
1070
sage: R.<X> = QQ[]
1071
sage: EllipticCurveIsogeny(E,X^3-13*X^2-58*X+503,check=False)
1072
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
1073
1074
"""
1075
1076
if (self.__this_hash is not None):
1077
return self.__this_hash
1078
1079
ker_poly_list = self.__kernel_polynomial_list
1080
1081
if (ker_poly_list is None):
1082
ker_poly_list = self.__init_kernel_polynomial()
1083
1084
this_hash = 0
1085
1086
for a in ker_poly_list:
1087
this_hash = this_hash.__xor__(hash(a))
1088
1089
this_hash = this_hash.__xor__(hash(self.__E1))
1090
this_hash = this_hash.__xor__(hash(self.__E2))
1091
this_hash = this_hash.__xor__(hash(self.__base_field))
1092
1093
self.__this_hash = this_hash
1094
1095
return self.__this_hash
1096
1097
1098
def __cmp__(self, other):
1099
r"""
1100
Function that implements comparisons between isogeny objects.
1101
1102
This function works by comparing the underlying kernel
1103
objects.
1104
1105
EXAMPLES::
1106
1107
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
1108
sage: phi_v = EllipticCurveIsogeny(E, E((0,0)))
1109
sage: phi_k = EllipticCurveIsogeny(E, [0,1])
1110
sage: phi_k == phi_v
1111
True
1112
sage: E_F17 = EllipticCurve(GF(17), [0,0,0,1,0])
1113
sage: phi_p = EllipticCurveIsogeny(E_F17, [0,1])
1114
sage: phi_p == phi_v
1115
False
1116
sage: E = EllipticCurve('11a1')
1117
sage: phi = E.isogeny(E(5,5))
1118
sage: psi = E.isogeny(phi.kernel_polynomial())
1119
sage: phi == psi
1120
True
1121
sage: phi.dual() == psi.dual()
1122
True
1123
1124
1125
"""
1126
if (not isinstance(other, EllipticCurveIsogeny)):
1127
return -1
1128
1129
if (self.__kernel_polynomial is None):
1130
self.__init_kernel_polynomial()
1131
1132
return cmp(self.__kernel_polynomial, other.kernel_polynomial())
1133
1134
1135
def __neg__(self):
1136
r"""
1137
Function to implement unary negation (-) operator on
1138
isogenies. Returns a copy of this isogeny that has been
1139
negated.
1140
1141
EXAMPLES:
1142
1143
The following examples inherently exercise this function::
1144
1145
sage: E = EllipticCurve(j=GF(17)(0))
1146
sage: phi = EllipticCurveIsogeny(E, E((-1,0)) )
1147
sage: negphi = -phi
1148
sage: phi(E((0,1))) + negphi(E((0,1)))
1149
(0 : 1 : 0)
1150
1151
sage: E = EllipticCurve(j=GF(19)(1728))
1152
sage: R.<x> = GF(19)[]
1153
sage: phi = EllipticCurveIsogeny(E, x)
1154
sage: negphi = -phi
1155
sage: phi(E((3,7))) + negphi(E((3,12))) == phi(2*E((3,7)))
1156
True
1157
sage: negphi(E((18,6)))
1158
(17 : 0 : 1)
1159
1160
sage: R.<x> = QQ[]
1161
sage: E = EllipticCurve('17a1')
1162
sage: R.<x> = QQ[]
1163
sage: f = x - 11/4
1164
sage: phi = EllipticCurveIsogeny(E, f)
1165
sage: negphi = -phi
1166
sage: phi.rational_maps()[0] == negphi.rational_maps()[0]
1167
True
1168
sage: P = E((7,13))
1169
sage: phi(P) + negphi(P)
1170
(0 : 1 : 0)
1171
1172
"""
1173
# save off the kernel lists
1174
kernel_list = self.__kernel_list
1175
self.__kernel_list = None
1176
1177
output = deepcopy(self)
1178
1179
# reset the kernel lists
1180
output.__kernel_list = copy(kernel_list)
1181
self.__kernel_list = kernel_list
1182
1183
output.switch_sign()
1184
return output
1185
1186
1187
1188
#
1189
# Sage Special Functions
1190
#
1191
1192
def _repr_(self):
1193
r"""
1194
Special sage specific function that implement the
1195
functionality to display the isogeny self as a string.
1196
1197
EXAMPLES::
1198
1199
sage: E = EllipticCurve(GF(31), [1,0,1,1,0])
1200
sage: phi = EllipticCurveIsogeny(E, E((0,0)) )
1201
sage: phi._repr_()
1202
'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'
1203
1204
sage: E = EllipticCurve(QQ, [1,0,0,1,9])
1205
sage: phi = EllipticCurveIsogeny(E, [2,1])
1206
sage: phi._repr_()
1207
'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'
1208
1209
"""
1210
return 'Isogeny of degree ' + self.__degree.__repr__() + ' from ' + \
1211
self.__E1.__repr__() + ' to ' + self.__E2.__repr__()
1212
1213
1214
def _latex_(self):
1215
r"""
1216
Special sage specific function that implements functionality
1217
to display an isogeny object as a latex string.
1218
1219
This function returns a latex string representing the isogeny
1220
self as the `x` and `y` coordinate rational functions.
1221
1222
EXAMPLES::
1223
1224
sage: E = EllipticCurve(QQ, [0,0,0,1,-1])
1225
sage: phi = EllipticCurveIsogeny(E, E(0))
1226
sage: phi._latex_()
1227
'\\left( x , y \\right)'
1228
1229
sage: E = EllipticCurve(GF(17), [0,0,0,1,-1])
1230
sage: R.<X> = GF(17)[]
1231
sage: phi = EllipticCurveIsogeny(E, X+11)
1232
sage: phi._latex_()
1233
'\\left( \\frac{x^{2} + 11 x + 7}{x + 11} , \\frac{x^{2} y + 5 x y + 12 y}{x^{2} + 5 x + 2} \\right)'
1234
1235
1236
"""
1237
ratl_maps = self.rational_maps()
1238
return '\\left( %s , %s \\right)' % (ratl_maps[0]._latex_(), ratl_maps[1]._latex_())
1239
1240
1241
###########################
1242
# Private Common Functions
1243
###########################
1244
1245
# delete the hash value
1246
def __clear_cached_values(self):
1247
r"""
1248
A private function to clear the hash if the codomain has been
1249
modified by a pre or post isomorphism.
1250
1251
EXAMPLES::
1252
1253
sage: F = GF(7);
1254
sage: E = EllipticCurve(j=F(0))
1255
sage: phi = EllipticCurveIsogeny(E, [E((0,-1)), E((0,1))])
1256
sage: old_hash = hash(phi)
1257
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
1258
sage: phi.set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(), (-1,2,-3,4)))
1259
sage: hash(phi) == old_hash
1260
False
1261
1262
sage: R.<x> = QQ[]
1263
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
1264
sage: phi = EllipticCurveIsogeny(E, x)
1265
sage: old_ratl_maps = phi.rational_maps()
1266
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
1267
sage: phi.set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(), (-1,0,0,0)))
1268
sage: old_ratl_maps == phi.rational_maps()
1269
False
1270
sage: old_ratl_maps[1] == -phi.rational_maps()[1]
1271
True
1272
1273
sage: F = GF(127); R.<x> = F[]
1274
sage: E = EllipticCurve(j=F(1728))
1275
sage: f = x^5 + 43*x^4 + 97*x^3 + 81*x^2 + 42*x + 82
1276
sage: phi = EllipticCurveIsogeny(E, f)
1277
sage: old_hash = hash(phi)
1278
sage: old_ratl_maps = phi.rational_maps()
1279
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
1280
sage: phi.set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(), (-13,13,-13,13)))
1281
sage: old_hash == hash(phi)
1282
False
1283
sage: old_ratl_maps == phi.rational_maps()
1284
False
1285
sage: phi._EllipticCurveIsogeny__clear_cached_values()
1286
1287
"""
1288
self.__this_hash = None
1289
self.__rational_maps_initialized = False
1290
self.__X_coord_rational_map = None
1291
self.__Y_coord_rational_map = None
1292
self.__dual
1293
1294
1295
# performs the inheritance house keeping
1296
def __perform_inheritance_housekeeping(self):
1297
r"""
1298
Internal helper function, sets values on the super classes of
1299
this class.
1300
1301
EXAMPLES:
1302
1303
The following examples will implicitly exercise this
1304
function::
1305
1306
sage: E = EllipticCurve(GF(43), [2,3,5,7,11])
1307
sage: R.<x> = GF(43)[]; f = x + 42
1308
sage: phi = EllipticCurveIsogeny(E, f)
1309
sage: phi._EllipticCurveIsogeny__perform_inheritance_housekeeping()
1310
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
1311
sage: E2 = phi.codomain()
1312
sage: post_isom = WeierstrassIsomorphism(E2, (41, 37, 31, 29))
1313
sage: phi.set_post_isomorphism(post_isom)
1314
sage: E1pr = WeierstrassIsomorphism(E, (-1, 2, -3, 4)).codomain().codomain()
1315
sage: pre_isom = E1pr.isomorphism_to(E)
1316
sage: phi.set_pre_isomorphism(pre_isom)
1317
1318
"""
1319
1320
# one of the superclasses uses these fields
1321
self._domain = self.__E1
1322
self._codomain = self.__E2
1323
1324
# sets up the parent
1325
parent = homset.Hom(self.__E1(0).parent(), self.__E2(0).parent())
1326
Morphism.__init__(self, parent)
1327
1328
return
1329
1330
1331
# initializes the base field
1332
def __init_algebraic_structs(self, E):
1333
r"""
1334
An internal function for EllipticCurveIsogeny objects that
1335
sets up the member variables necessary for algebra.
1336
1337
EXAMPLES:
1338
1339
The following tests inherently exercise this function::
1340
1341
sage: E = EllipticCurve(j=GF(17)(0))
1342
sage: phi = EllipticCurveIsogeny(E, E((-1,0)))
1343
sage: phi._EllipticCurveIsogeny__init_algebraic_structs(E)
1344
1345
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
1346
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
1347
sage: phi._EllipticCurveIsogeny__init_algebraic_structs(E)
1348
1349
sage: F = GF(19); R.<x> = F[]
1350
sage: E = EllipticCurve(j=GF(19)(0))
1351
sage: phi = EllipticCurveIsogeny(E, x)
1352
sage: phi._EllipticCurveIsogeny__init_algebraic_structs(E)
1353
1354
"""
1355
self.__E1 = E
1356
self.__base_field = E.base_ring()
1357
1358
poly_ring = self.__poly_ring = PolynomialRing(self.__base_field, ['x','y'])
1359
1360
self.__x_var = poly_ring('x')
1361
self.__y_var = poly_ring('y')
1362
1363
self.__intermediate_domain = E
1364
1365
return
1366
1367
1368
def __compute_E2(self):
1369
r"""
1370
Private function that computes and sets the isogeny codomain.
1371
1372
EXAMPLES:
1373
1374
These examples inherently exercise this function::
1375
1376
sage: E = EllipticCurve(j=GF(7)(1728))
1377
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
1378
sage: phi.codomain()
1379
Elliptic Curve defined by y^2 = x^3 + 3*x over Finite Field of size 7
1380
sage: phi._EllipticCurveIsogeny__compute_E2()
1381
1382
sage: R.<x> = GF(7)[]
1383
sage: phi = EllipticCurveIsogeny(E, x)
1384
sage: phi.codomain()
1385
Elliptic Curve defined by y^2 = x^3 + 3*x over Finite Field of size 7
1386
sage: phi._EllipticCurveIsogeny__compute_E2()
1387
1388
"""
1389
1390
if ("velu" == self.__algorithm):
1391
E2 = self.__compute_E2_via_velu()
1392
elif ("kohel" == self.__algorithm):
1393
E2 = self.__compute_E2_via_kohel()
1394
1395
self.__E2 = E2
1396
self.__intermediate_codomain = E2
1397
1398
return
1399
1400
1401
# initializes the rational maps fields
1402
def __initialize_rational_maps(self, precomputed_maps=None):
1403
r"""
1404
Private function that computes and initializes the rational
1405
maps.
1406
1407
INPUT:
1408
1409
- ``
1410
1411
EXAMPLES:
1412
1413
The following examples inherently exercise this function::
1414
1415
sage: E = EllipticCurve(j=GF(7)(1728))
1416
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
1417
sage: phi._EllipticCurveIsogeny__initialize_rational_maps()
1418
sage: phi.rational_maps()
1419
((x^2 + 1)/x, (x^2*y - y)/x^2)
1420
1421
sage: R.<x> = GF(7)[]
1422
sage: phi = EllipticCurveIsogeny(E, x)
1423
sage: phi = EllipticCurveIsogeny(E, x)
1424
sage: phi.rational_maps()
1425
((x^2 + 1)/x, (x^2*y - y)/x^2)
1426
sage: phi._EllipticCurveIsogeny__initialize_rational_maps()
1427
1428
sage: E = EllipticCurve([1,2,3,4,5])
1429
sage: Eshort = E.short_weierstrass_model()
1430
sage: phi = E.isogeny(E(0), Eshort)
1431
sage: phiX, phiY = phi.rational_maps()
1432
sage: phiX(1,2), phiY(1,2)
1433
(63, 864)
1434
"""
1435
if self.__rational_maps_initialized:
1436
return
1437
1438
if precomputed_maps is None:
1439
if ("velu"==self.__algorithm):
1440
(X_map, Y_map) = self.__initialize_rational_maps_via_velu()
1441
if ("kohel"==self.__algorithm):
1442
(X_map, Y_map) = self.__initialize_rational_maps_via_kohel()
1443
else:
1444
X_map, Y_map = precomputed_maps
1445
1446
if self.__prei_x_coord_ratl_map is not None:
1447
prei_X_map = self.__prei_x_coord_ratl_map
1448
prei_Y_map = self.__prei_y_coord_ratl_map
1449
X_map, Y_map = X_map.subs(x=prei_X_map, y=prei_Y_map), \
1450
Y_map.subs(x=prei_X_map, y=prei_Y_map)
1451
1452
if self.__posti_x_coord_ratl_map is not None:
1453
X_map, Y_map = \
1454
self.__posti_x_coord_ratl_map.subs(x=X_map, y=Y_map), \
1455
self.__posti_y_coord_ratl_map.subs(x=X_map, y=Y_map)
1456
1457
self.__X_coord_rational_map = X_map
1458
self.__Y_coord_rational_map = Y_map
1459
self.__rational_maps_initialized = True
1460
1461
1462
def __init_kernel_polynomial(self):
1463
r"""
1464
Private function that initializes the kernel polynomial (if
1465
the algorithm does not take it as a parameter.)
1466
1467
EXAMPLES:
1468
1469
The following examples inherently exercise this function::
1470
1471
sage: E = EllipticCurve(j=GF(7)(1728))
1472
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
1473
sage: phi.kernel_polynomial()
1474
x
1475
sage: phi._EllipticCurveIsogeny__init_kernel_polynomial()
1476
[0, 1]
1477
1478
"""
1479
1480
if (self.__kernel_polynomial_list is not None):
1481
return self.__kernel_polynomial_list
1482
1483
if ("velu" == self.__algorithm):
1484
ker_poly_list = self.__init_kernel_polynomial_velu()
1485
else:
1486
raise InputError, "The kernel polynomial should already be defined!"
1487
1488
return ker_poly_list
1489
1490
1491
def __set_pre_isomorphism(self, domain, isomorphism):
1492
r"""
1493
Private function to set the pre isomorphism and domain (and
1494
keep track of the domain of the isogeny.)
1495
1496
EXAMPLES::
1497
1498
sage: E = EllipticCurve(GF(43), [2,3,5,7,11])
1499
sage: R.<x> = GF(43)[]; f = x + 42
1500
sage: phi = EllipticCurveIsogeny(E, f)
1501
sage: phi._EllipticCurveIsogeny__perform_inheritance_housekeeping()
1502
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
1503
sage: E1pr = WeierstrassIsomorphism(E, (-1, 2, -3, 4)).codomain().codomain()
1504
sage: pre_isom = E1pr.isomorphism_to(E)
1505
sage: phi.set_pre_isomorphism(pre_isom)
1506
sage: phi._EllipticCurveIsogeny__set_pre_isomorphism(E, WeierstrassIsomorphism(E, (-1, 3, -3, 4)))
1507
sage: E == phi.domain()
1508
True
1509
1510
"""
1511
1512
self.__E1 = domain
1513
1514
# set the isomorphism
1515
self.__pre_isomorphism = isomorphism
1516
1517
# calculate the isomorphism as a rational map.
1518
1519
(u, r, s, t) = isomorphism.tuple()
1520
1521
x = self.__x_var;
1522
y = self.__y_var;
1523
1524
self.__prei_x_coord_ratl_map = (x - r)/u**2
1525
self.__prei_y_coord_ratl_map = (y - s*(x-r) - t)/u**3
1526
1527
if (self.__kernel_polynomial is not None):
1528
ker_poly = self.__kernel_polynomial
1529
ker_poly = ker_poly.subs(x=self.__prei_x_coord_ratl_map)
1530
kp_lc = ker_poly.univariate_polynomial().leading_coefficient()
1531
ker_poly = (1/kp_lc)*ker_poly
1532
self.__kernel_polynomial = ker_poly
1533
1534
self.__perform_inheritance_housekeeping()
1535
1536
return;
1537
1538
1539
def __set_post_isomorphism(self, codomain, isomorphism):
1540
r"""
1541
Private function to set the post isomorphism and codomain (and
1542
keep track of the codomain of the isogeny.)
1543
1544
EXAMPLES:
1545
1546
The following examples inherently exercise this function::
1547
1548
sage: E = EllipticCurve(j=GF(7)(1728))
1549
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
1550
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
1551
sage: E2 = phi.codomain()
1552
sage: isom = WeierstrassIsomorphism(E2, (-1,2,-3,4))
1553
sage: phi.set_post_isomorphism(isom)
1554
sage: phi._EllipticCurveIsogeny__set_post_isomorphism(E2, WeierstrassIsomorphism(phi.codomain(), (1,-2,3,-4)))
1555
sage: E2 == phi.codomain()
1556
True
1557
1558
"""
1559
1560
# set the codomains
1561
self.__E2 = codomain
1562
1563
# set the isomorphism
1564
self.__post_isomorphism = isomorphism
1565
1566
# calculate the isomorphism as a rational map.
1567
1568
(u, r, s, t) = isomorphism.tuple()
1569
1570
x = self.__x_var;
1571
y = self.__y_var;
1572
1573
self.__posti_x_coord_ratl_map = (x - r)/u**2
1574
self.__posti_y_coord_ratl_map = (y - s*(x-r) - t)/u**3
1575
1576
self.__perform_inheritance_housekeeping()
1577
1578
return;
1579
1580
1581
def __setup_post_isomorphism(self, codomain, model):
1582
r"""
1583
Private function to set up the post isomorphism given the
1584
codomain.
1585
1586
EXAMPLES:
1587
1588
The following examples inherently exercise this function::
1589
1590
sage: E = EllipticCurve(j=GF(7)(1728))
1591
sage: E2 = EllipticCurve(GF(7), [0,0,0,5,0])
1592
sage: phi = EllipticCurveIsogeny(E, E((0,0)), E2); phi
1593
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
1594
sage: E3 = EllipticCurve(GF(7), [0,0,0,6,0])
1595
sage: phi._EllipticCurveIsogeny__setup_post_isomorphism(E3, None)
1596
sage: phi
1597
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
1598
1599
sage: R.<x> = QQ[]
1600
sage: E = EllipticCurve(j=1728)
1601
sage: f = x^3 - x
1602
sage: phi = EllipticCurveIsogeny(E, f, model='minimal'); phi
1603
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
1604
1605
sage: phi = EllipticCurveIsogeny(E, f, model=None)
1606
sage: phi._EllipticCurveIsogeny__setup_post_isomorphism(None, 'minimal')
1607
sage: 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
"""
1611
1612
# TODO: add checks to make sure that
1613
# codomain and model parameters are consistent with the
1614
# algorithm used.
1615
1616
post_isom = None
1617
newE2 = None
1618
1619
oldE2 = self.__E2
1620
1621
if (model is not None):
1622
1623
if (codomain is not None):
1624
raise ValueError, "Cannot specify a codomain and model flag simultaneously."
1625
1626
if ('minimal' == model):
1627
1628
if (not is_RationalField(oldE2.base_field())):
1629
raise ValueError, "specifying minimal for model flag only valid with curves over the rational numbers."
1630
1631
newE2 = oldE2.minimal_model()
1632
post_isom = oldE2.isomorphism_to(newE2)
1633
1634
else:
1635
raise ValueError, "Unknown value of model flag."
1636
1637
elif (codomain is not None):
1638
if (not is_EllipticCurve(codomain)):
1639
raise ValueError, "Codomain parameter must be an elliptic curve."
1640
1641
if (not oldE2.is_isomorphic(codomain)):
1642
raise ValueError, "Codomain parameter must be isomorphic to computed codomain isogeny"
1643
1644
newE2 = codomain
1645
post_isom = oldE2.isomorphism_to(newE2)
1646
1647
if (post_isom is not None):
1648
self.__set_post_isomorphism(newE2, post_isom)
1649
1650
return
1651
1652
1653
###########################
1654
# Velu's Formula Functions
1655
###########################
1656
1657
#
1658
# Setup function for Velu's formula
1659
#
1660
1661
def __init_from_kernel_list(self, kernel_gens):
1662
r"""
1663
Private function that initializes the isogeny from a list of
1664
points which generate the kernel (For Velu's formulas.)
1665
1666
EXAMPLES:
1667
1668
The following example inherently exercises this function::
1669
1670
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
1671
sage: phi = EllipticCurveIsogeny(E, E((0,0))); phi
1672
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
1673
sage: phi._EllipticCurveIsogeny__init_from_kernel_list([E(0), E((0,0))])
1674
1675
"""
1676
if self.__check :
1677
for P in kernel_gens:
1678
if not P.has_finite_order():
1679
raise ValueError, "The points in the kernel must be of finite order."
1680
# work around the current implementation of torsion points. When they are done better this could be
1681
# reduced but it won't speed things up too much.
1682
kernel_list = Set([self.__E1(0)])
1683
for P in kernel_gens:
1684
points_to_add = []
1685
for j in range(P.order()):
1686
for Q in kernel_list:
1687
points_to_add.append(j*P+Q)
1688
kernel_list += Set(points_to_add)
1689
1690
self.__kernel_list = kernel_list.list()
1691
self.__kernel_2tor = {}
1692
self.__kernel_non2tor = {}
1693
1694
self.__degree = len(kernel_list)
1695
1696
self.__sort_kernel_list()
1697
1698
return
1699
1700
1701
#
1702
# Precompute the values in Velu's Formula.
1703
#
1704
def __sort_kernel_list(self):
1705
r"""
1706
Private function that sorts the list of points in the kernel
1707
(For Velu's formulas). Sorts out the 2 torsion points, and
1708
puts them in a dictionary.
1709
1710
EXAMPLES:
1711
1712
The following example inherently exercises this function::
1713
1714
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
1715
sage: P = E((4,2))
1716
sage: phi = EllipticCurveIsogeny(E, P); phi
1717
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
1718
sage: phi._EllipticCurveIsogeny__kernel_2tor = {}
1719
sage: phi._EllipticCurveIsogeny__kernel_non2tor = {}
1720
sage: phi._EllipticCurveIsogeny__sort_kernel_list()
1721
1722
"""
1723
1724
a1,a2,a3,a4,a6 = self.__E1.ainvs()
1725
1726
v = 0
1727
w = 0
1728
1729
for Q in self.__kernel_list:
1730
1731
if Q.is_zero():
1732
continue
1733
1734
(xQ,yQ) = Q.xy()
1735
1736
gxQ = 3*xQ**2 + 2*a2*xQ + a4 - a1*yQ
1737
gyQ = -2*yQ - a1*xQ - a3
1738
1739
uQ = gyQ**2
1740
1741
# sort torsion points:
1742
if (2*yQ == -a1*xQ - a3): # Q is 2-torsion
1743
vQ = gxQ
1744
self.__kernel_2tor[xQ] = (xQ,yQ,gxQ,gyQ,vQ,uQ)
1745
v = v + vQ
1746
w = w + (uQ + xQ*vQ)
1747
elif (not self.__kernel_non2tor.has_key(xQ)): # Q is not a 2-torsion
1748
vQ = 2*gxQ - a1*gyQ
1749
self.__kernel_non2tor[xQ] = (xQ,yQ,gxQ,gyQ,vQ,uQ)
1750
v = v + vQ
1751
w = w + (uQ + xQ*vQ)
1752
1753
self.__v = v
1754
self.__w = w
1755
1756
return
1757
1758
1759
#
1760
# Velu's formula computing the codomain curve
1761
#
1762
def __compute_E2_via_velu(self):
1763
r"""
1764
Private function that computes the codomain via Velu's
1765
formulas.
1766
1767
EXAMPLES:
1768
1769
The following example inherently exercises this function::
1770
1771
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
1772
sage: P = E((4,2))
1773
sage: phi = EllipticCurveIsogeny(E, P);
1774
sage: phi.codomain()
1775
Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7
1776
sage: phi._EllipticCurveIsogeny__compute_E2_via_velu()
1777
Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7
1778
1779
"""
1780
v = self.__v
1781
w = self.__w
1782
1783
return compute_codomain_formula(self.__E1, v,w)
1784
1785
1786
def __velu_sum_helper(self, Qvalues, a1, a3, x, y):
1787
r"""
1788
Private function for Velu's formulas, helper function to help
1789
perform the summation.
1790
1791
EXAMPLES:
1792
1793
The following example inherently exercises this function::
1794
1795
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
1796
sage: P = E((4,2))
1797
sage: phi = EllipticCurveIsogeny(E, P);
1798
sage: Q = E((0,0)); phi(Q)
1799
(0 : 0 : 1)
1800
sage: phi.rational_maps()
1801
((x^4 - 2*x^3 + x^2 - 3*x)/(x^3 - 2*x^2 + 3*x - 2),
1802
(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))
1803
1804
sage: E = EllipticCurve(GF(7), [0,0,0,1,0])
1805
sage: phi = EllipticCurveIsogeny(E, E((0,0)) )
1806
sage: Qvals = phi._EllipticCurveIsogeny__kernel_2tor[0]
1807
sage: phi._EllipticCurveIsogeny__velu_sum_helper(Qvals, 0, 0, 5, 5)
1808
(3, 3)
1809
sage: R.<x,y> = GF(7)[]
1810
sage: phi._EllipticCurveIsogeny__velu_sum_helper(Qvals, 0, 0, x, y)
1811
(1/x, y/x^2)
1812
1813
"""
1814
xQ = Qvalues[0]
1815
yQ = Qvalues[1]
1816
gxQ = Qvalues[2]
1817
gyQ = Qvalues[3]
1818
vQ = Qvalues[4]
1819
uQ = Qvalues[5]
1820
1821
t1 = x - xQ
1822
inv_t1 = t1**-1
1823
inv_t1_2 = inv_t1**2
1824
inv_t1_3 = inv_t1_2*inv_t1
1825
1826
tX = vQ*inv_t1 + uQ*(inv_t1_2)
1827
1828
tY0 = uQ*(2*y + a1*x + a3)
1829
tY1 = vQ*(a1*t1 + y - yQ)
1830
tY2 = a1*uQ - gxQ*gyQ
1831
1832
tY = ( tY0*inv_t1_3 + (tY1 + tY2)*inv_t1_2 )
1833
1834
return (tX, tY)
1835
1836
1837
def __compute_via_velu_numeric(self, xP, yP):
1838
r"""
1839
Private function that sorts the list of points in the kernel
1840
(for Velu's formulas). Sorts out the 2 torsion points, and
1841
puts them in a diction
1842
1843
EXAMPLES:
1844
1845
The following example inherently exercises this function::
1846
1847
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
1848
sage: P = E((4,2))
1849
sage: phi = EllipticCurveIsogeny(E, P);
1850
sage: Q = E((0,0)); phi(Q)
1851
(0 : 0 : 1)
1852
sage: Q = E((-1,0)); phi(Q)
1853
(0 : 0 : 1)
1854
sage: phi._EllipticCurveIsogeny__compute_via_velu_numeric(0, 0)
1855
(0, 0)
1856
sage: phi._EllipticCurveIsogeny__compute_via_velu_numeric(-1, 0)
1857
(0, 0)
1858
1859
"""
1860
# first check if the point is in the kernel
1861
if ( self.__kernel_2tor.has_key(xP) or self.__kernel_non2tor.has_key(xP) ) :
1862
return self.__intermediate_codomain(0)
1863
1864
outP = self.__compute_via_velu(xP,yP)
1865
1866
return outP
1867
1868
1869
def __compute_via_velu(self, xP, yP):
1870
r"""
1871
Private function for Velu's formulas, to perform the
1872
summation.
1873
1874
EXAMPLES:
1875
1876
The following example inherently exercises this function::
1877
1878
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
1879
sage: P = E((4,2))
1880
sage: phi = EllipticCurveIsogeny(E, P);
1881
sage: Q = E((0,0)); phi(Q)
1882
(0 : 0 : 1)
1883
sage: phi.rational_maps()
1884
((x^4 - 2*x^3 + x^2 - 3*x)/(x^3 - 2*x^2 + 3*x - 2),
1885
(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))
1886
sage: phi._EllipticCurveIsogeny__compute_via_velu(0, 0)
1887
(0, 0)
1888
sage: R.<x,y> = GF(7)[]
1889
sage: phi._EllipticCurveIsogeny__compute_via_velu(x, y)
1890
((x^4 - 2*x^3 + x^2 - 3*x)/(x^3 - 2*x^2 + 3*x - 2),
1891
(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))
1892
1893
"""
1894
1895
ker_2tor = self.__kernel_2tor
1896
ker_non2tor = self.__kernel_non2tor
1897
1898
X = 0
1899
Y = 0
1900
1901
a1 = self.__E1.a1()
1902
a3 = self.__E1.a3()
1903
1904
# next iterate over the 2torsion points of the kernel
1905
for Qvalues in ker_2tor.itervalues():
1906
(tX, tY) = self.__velu_sum_helper(Qvalues, a1, a3, xP, yP)
1907
X = X + tX
1908
Y = Y + tY
1909
1910
for Qvalues in ker_non2tor.itervalues():
1911
(tX, tY) = self.__velu_sum_helper(Qvalues, a1, a3, xP, yP)
1912
X = X + tX
1913
Y = Y + tY
1914
1915
X = xP + X
1916
Y = yP - Y
1917
1918
return (X,Y)
1919
1920
1921
def __initialize_rational_maps_via_velu(self):
1922
r"""
1923
Private function for Velu's formulas, helper function to initialize the rational maps.
1924
1925
EXAMPLES:
1926
1927
The following example inherently exercises this function::
1928
1929
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
1930
sage: P = E((4,2))
1931
sage: phi = EllipticCurveIsogeny(E, P);
1932
sage: phi.rational_maps()
1933
((x^4 - 2*x^3 + x^2 - 3*x)/(x^3 - 2*x^2 + 3*x - 2),
1934
(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))
1935
sage: phi._EllipticCurveIsogeny__initialize_rational_maps_via_velu()
1936
((x^4 - 2*x^3 + x^2 - 3*x)/(x^3 - 2*x^2 + 3*x - 2),
1937
(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))
1938
1939
"""
1940
1941
x = self.__x_var
1942
y = self.__y_var
1943
1944
return self.__compute_via_velu(x,y)
1945
1946
1947
def __init_kernel_polynomial_velu(self):
1948
r"""
1949
Private function for Velu's formulas, helper function to
1950
initialize the rational maps.
1951
1952
EXAMPLES:
1953
1954
The following example inherently exercises this function::
1955
1956
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
1957
sage: P = E((4,2))
1958
sage: phi = EllipticCurveIsogeny(E, P);
1959
sage: phi.kernel_polynomial()
1960
x^2 + 2*x + 4
1961
sage: phi._EllipticCurveIsogeny__init_kernel_polynomial_velu()
1962
[4, 2, 1]
1963
1964
"""
1965
1966
poly_ring = self.__poly_ring
1967
x = self.__x_var
1968
1969
invX = 0
1970
1971
if (self.__pre_isomorphism is not None):
1972
pre_isom = self.__pre_isomorphism
1973
u = pre_isom.u
1974
r = pre_isom.r
1975
invX = (u**2)*x + r
1976
else:
1977
invX = x
1978
1979
psi = poly_ring(1)
1980
1981
for Qvalues in self.__kernel_2tor.itervalues():
1982
xQ = invX(x=Qvalues[0])
1983
psi = psi*(x - xQ)
1984
1985
for Qvalues in self.__kernel_non2tor.itervalues():
1986
xQ = invX(x=Qvalues[0])
1987
psi = psi*(x - xQ)
1988
1989
ker_poly_list = psi.univariate_polynomial().list()
1990
1991
self.__kernel_polynomial_list = ker_poly_list
1992
self.__kernel_polynomial = psi
1993
1994
return ker_poly_list
1995
1996
1997
1998
###################################
1999
# Kohel's Variant of Velu's Formula
2000
###################################
2001
2002
def __init_from_kernel_polynomial(self, kernel_polynomial, degree=None):
2003
r"""
2004
Private function that initializes the isogeny from a kernel
2005
polynomial.
2006
2007
EXAMPLES:
2008
2009
These examples inherently exercise this private function::
2010
2011
sage: R.<x> = GF(7)[]
2012
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
2013
sage: phi = EllipticCurveIsogeny(E, x);phi
2014
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
2015
2016
sage: phi._EllipticCurveIsogeny__init_from_kernel_polynomial(x)
2017
2018
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2019
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3); phi
2020
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
2021
2022
sage: phi._EllipticCurveIsogeny__init_from_kernel_polynomial(x+6, degree=3)
2023
2024
"""
2025
2026
poly_ring = self.__poly_ring
2027
x = self.__x_var
2028
2029
E = self.__E1
2030
2031
if(is_Polynomial(kernel_polynomial)):
2032
kernel_polynomial = kernel_polynomial.list()
2033
2034
n = len(kernel_polynomial)-1
2035
2036
if kernel_polynomial[-1] != 1:
2037
raise ValueError, "The kernel polynomial must be monic."
2038
2039
self.__kernel_polynomial_list = kernel_polynomial
2040
2041
psi = 0
2042
for j in xrange(len(kernel_polynomial)):
2043
psi = psi*x + kernel_polynomial[n-j]
2044
2045
2046
#
2047
# Determine if kernel polynomial is entirely a two torsion
2048
#
2049
psi_G = two_torsion_part(E, poly_ring, psi, degree);
2050
2051
# force this polynomial to be monic:
2052
psi_G = psi_G/psi_G.univariate_polynomial().leading_coefficient()
2053
2054
if (0 != psi_G.degree()): # even degree case
2055
2056
psi_quo = poly_ring(psi/psi_G)
2057
2058
if (0 != psi_quo.degree()):
2059
raise NotImplementedError, "For basic Kohel's algorithm, if the kernel degree is even then the kernel must be contained in the two torsion."
2060
2061
(phi, omega, v, w, n, d) = self.__init_even_kernel_polynomial(E, psi_G)
2062
2063
else: # odd degree case
2064
2065
(phi, omega, v, w, n, d) = self.__init_odd_kernel_polynomial(E, psi)
2066
2067
2068
#
2069
# Set up the necessary instance variables
2070
#
2071
2072
self.__kernel_polynomial = psi
2073
self.__inner_kernel_polynomial = psi
2074
2075
self.__n = n
2076
self.__degree = d
2077
2078
self.__psi = psi
2079
self.__phi = phi
2080
self.__omega = omega
2081
2082
self.__v = v
2083
self.__w = w
2084
2085
return
2086
2087
2088
def __init_even_kernel_polynomial(self, E, psi_G):
2089
r"""
2090
Private function that initializes the isogeny from a kernel
2091
polynomial, for Kohel's algorithm in the even degree case.
2092
2093
EXAMPLES:
2094
2095
These examples inherently exercise this private function::
2096
2097
sage: R.<x> = GF(7)[]
2098
sage: E = EllipticCurve(GF(7), [-1,0])
2099
sage: phi = EllipticCurveIsogeny(E, x);phi
2100
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
2101
2102
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import two_torsion_part
2103
sage: psig = two_torsion_part(E,R,x,None)(phi._EllipticCurveIsogeny__x_var)
2104
sage: phi._EllipticCurveIsogeny__init_even_kernel_polynomial(E,psig)
2105
(x^3 - x, x^3*y + x*y, 6, 0, 1, 2)
2106
2107
sage: F = GF(2^4, 'alpha'); R.<x> = F[]
2108
sage: E = EllipticCurve(F, [1,1,0,1,0])
2109
sage: phi = EllipticCurveIsogeny(E, x); phi
2110
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
2111
2112
sage: psig = two_torsion_part(E,R,x,None)(phi._EllipticCurveIsogeny__x_var)
2113
sage: phi._EllipticCurveIsogeny__init_even_kernel_polynomial(E,psig)
2114
(x^3 + x, x^3*y + x^2 + x*y, 1, 0, 1, 2)
2115
2116
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2117
sage: R.<x> = GF(7)[]
2118
sage: f = x^3 + 6*x^2 + 1
2119
sage: phi = EllipticCurveIsogeny(E, f); phi
2120
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
2121
sage: psig = two_torsion_part(E,R,f,None)
2122
sage: psig = two_torsion_part(E,R,f,None)(phi._EllipticCurveIsogeny__x_var)
2123
sage: phi._EllipticCurveIsogeny__init_even_kernel_polynomial(E,psig)
2124
(x^7 - 2*x^6 + 2*x^5 - x^4 + 3*x^3 - 2*x^2 - x + 3,
2125
x^9*y - 3*x^8*y + 2*x^7*y - 3*x^3*y + 2*x^2*y + x*y - y,
2126
1,
2127
6,
2128
3,
2129
4)
2130
2131
2132
"""
2133
2134
2135
#check if the polynomial really divides the two_torsion_polynomial
2136
if self.__check and E.division_polynomial(2, x=self.__x_var) % psi_G != 0 :
2137
raise ValueError, "The polynomial does not define a finite subgroup of the elliptic curve."
2138
2139
n = psi_G.degree()
2140
d = n+1
2141
2142
base_field = self.__base_field
2143
char = base_field.characteristic()
2144
2145
x = self.__x_var
2146
y = self.__y_var
2147
2148
a1,a2,a3,a4,a6 = E.ainvs()
2149
2150
b2 = E.b2()
2151
b4 = E.b4()
2152
2153
if (1 == n):
2154
x0 = -psi_G.constant_coefficient()
2155
2156
# determine y0
2157
if (2 == char):
2158
y0 = (x0**3 + a2*x0**2 + a4*x0 + a6).sqrt()
2159
else:
2160
y0 = -(a1*x0 + a3)/2
2161
2162
(v,w) = compute_vw_kohel_even_deg1(x0,y0,a1,a2,a4)
2163
2164
phi = (x*psi_G + v)*psi_G
2165
omega = (y*psi_G**2 - v*(a1*psi_G + (y - y0)))*psi_G
2166
2167
elif (3 == n):
2168
s = psi_G.univariate_polynomial().list()
2169
s1 = -s[n-1]
2170
s2 = s[n-2]
2171
s3 = -s[n-3]
2172
2173
psi_G_pr = psi_G.derivative(x)
2174
psi_G_prpr = psi_G_pr.derivative(x)
2175
2176
phi = (psi_G_pr**2) + (-2*psi_G_prpr + (4*x - s1))*psi_G
2177
2178
phi_pr = phi.derivative(x)
2179
2180
psi_2 = 2*y + a1*x + a3
2181
2182
omega = (psi_2*(phi_pr*psi_G - phi*psi_G_pr) - (a1*phi + a3*psi_G)*psi_G)/2
2183
2184
phi = phi*psi_G
2185
omega = omega*psi_G
2186
2187
(v,w) = compute_vw_kohel_even_deg3(b2,b4,s1,s2,s3)
2188
2189
else:
2190
raise ValueError, "input polynomial must be of degree 1 or 3, not %d" % n
2191
2192
return (phi, omega, v, w, n, d)
2193
2194
2195
def __init_odd_kernel_polynomial(self, E, psi):
2196
r"""
2197
Private function that initializes the isogeny from a kernel
2198
polynomial.
2199
2200
EXAMPLES:
2201
2202
These examples inherently exercise this private function::
2203
2204
sage: R.<x> = GF(7)[]
2205
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2206
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3); phi
2207
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
2208
2209
sage: R.<x,y> = GF(7)[]
2210
sage: phi._EllipticCurveIsogeny__init_odd_kernel_polynomial(E, x+6)
2211
(x^3 - 2*x^2 + 3*x + 2, x^3*y - 3*x^2*y + x*y, 2, 6, 1, 3)
2212
2213
sage: F = GF(2^4, 'alpha'); R.<x> = F[]
2214
sage: alpha = F.gen()
2215
sage: E = EllipticCurve(F, [1,1,F.gen(),F.gen()^2+1,1])
2216
sage: f = x + alpha^2 + 1
2217
sage: phi = EllipticCurveIsogeny(E, f); phi
2218
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
2219
2220
sage: R.<x,y> = F[]
2221
sage: f = x + alpha^2 + 1
2222
sage: phi._EllipticCurveIsogeny__init_odd_kernel_polynomial(E, f)
2223
(x^3 + (alpha^2 + 1)*x + (alpha^3 + alpha^2 + alpha),
2224
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),
2225
alpha^2 + alpha + 1,
2226
alpha^3 + alpha^2 + alpha,
2227
1,
2228
3)
2229
2230
sage: E = EllipticCurve(j=-262537412640768000)
2231
sage: f = (E.isogenies_prime_degree()[0]).kernel_polynomial()
2232
sage: f.degree()
2233
81
2234
sage: E.isogeny(kernel=f) # long time (25s on sage.math, 2012)
2235
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
2236
2237
"""
2238
n = psi.degree()
2239
d = 2*n + 1
2240
2241
# check if the polynomial really divides the torsion polynomial :
2242
if self.__check:
2243
alpha = psi.parent().quotient(psi).gen()
2244
if not E.division_polynomial(d, x=alpha).is_zero():
2245
raise ValueError, "The polynomial does not define a finite subgroup of the elliptic curve."
2246
2247
x = self.__x_var
2248
2249
b2 = E.b2()
2250
b4 = E.b4()
2251
b6 = E.b6()
2252
2253
psi_coeffs = psi.univariate_polynomial().list()
2254
2255
s1 = 0; s2 = 0; s3 = 0
2256
2257
if (1 <= n):
2258
s1 = -psi_coeffs[n-1]
2259
2260
if (2 <= n):
2261
s2 = psi_coeffs[n-2]
2262
2263
if (3 <= n):
2264
s3 = -psi_coeffs[n-3]
2265
2266
# initializing these allows us to calculate E2.
2267
(v,w) = compute_vw_kohel_odd(b2,b4,b6,s1,s2,s3,n);
2268
2269
# initialize the polynomial temporary variables
2270
2271
psi_pr = psi.derivative(x)
2272
psi_prpr = psi_pr.derivative(x)
2273
2274
phi = (4*x**3 + b2*x**2 + 2*b4*x + b6)*(psi_pr**2 - psi_prpr*psi) - \
2275
(6*x**2 + b2*x + b4)*psi_pr*psi + (d*x - 2*s1)*psi**2
2276
2277
phi_pr = phi.derivative(x)
2278
2279
omega = 0
2280
if (2 != self.__base_field.characteristic()):
2281
omega = self.__compute_omega_fast(E, psi, psi_pr, phi, phi_pr)
2282
else:
2283
omega = self.__compute_omega_general(E, psi, psi_pr, phi, phi_pr)
2284
2285
return (phi, omega, v, w, n, d)
2286
2287
2288
#
2289
# This is the fast omega computation that works when characteristic is not 2
2290
#
2291
def __compute_omega_fast(self, E, psi, psi_pr, phi, phi_pr):
2292
r"""
2293
Private function that initializes the omega polynomial (from
2294
Kohel's formulas) in the case that the characteristic of the
2295
underlying field is not 2.
2296
2297
EXAMPLES:
2298
2299
These examples inherently exercise this private function::
2300
2301
sage: R.<x> = GF(7)[]
2302
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2303
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3); phi
2304
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
2305
2306
sage: R.<x,y> = GF(7)[]
2307
sage: psi = phi._EllipticCurveIsogeny__psi
2308
sage: psi_pr = psi.derivative(x)
2309
sage: fi = phi._EllipticCurveIsogeny__phi
2310
sage: fi_pr = fi.derivative(x)
2311
sage: phi._EllipticCurveIsogeny__compute_omega_fast(E, psi, psi_pr, fi, fi_pr)
2312
x^3*y - 3*x^2*y + x*y
2313
2314
"""
2315
2316
a1 = E.a1()
2317
a3 = E.a3()
2318
2319
x = self.__x_var; # 'x'
2320
y = self.__y_var; # 'y'
2321
2322
psi_2 = 2*y + a1*x + a3
2323
2324
# note, the formula below is correct
2325
# the formula in Kohel's thesis has some typos
2326
# notably the first plus sign should be a minus
2327
# as it is here below.
2328
2329
omega = phi_pr*psi*psi_2/2 - phi*psi_pr*psi_2 - \
2330
(a1*phi + a3*psi**2)*psi/2
2331
2332
return omega
2333
2334
2335
def __compute_omega_general(self, E, psi, psi_pr, phi, phi_pr):
2336
r"""
2337
Private function that initializes the omega polynomial (from
2338
Kohel's formulas) in the case of general characteristic of the
2339
underlying field.
2340
2341
EXAMPLES:
2342
2343
These examples inherently exercise this private function::
2344
2345
sage: F = GF(2^4, 'alpha'); R.<x> = F[]
2346
sage: alpha = F.gen()
2347
sage: E = EllipticCurve(F, [1,1,F.gen(),F.gen()^2+1,1])
2348
sage: f = x + alpha^2 + 1
2349
sage: phi = EllipticCurveIsogeny(E, f); phi
2350
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
2351
2352
sage: R.<x,y> = F[]
2353
sage: psi = phi._EllipticCurveIsogeny__psi
2354
sage: psi_pr = psi.derivative(x)
2355
sage: fi = phi._EllipticCurveIsogeny__phi
2356
sage: fi_pr = fi.derivative(x)
2357
sage: phi._EllipticCurveIsogeny__compute_omega_general(E, psi, psi_pr, fi, fi_pr)
2358
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)
2359
2360
A bug fixed in ticket #7907::
2361
2362
sage: F = GF(128,'a')
2363
sage: a = F.gen()
2364
sage: E = EllipticCurve([1,0,0,0,(a**6+a**4+a**2+a)])
2365
sage: x = polygen(F)
2366
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)
2367
sage: E.isogeny(ker)
2368
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
2369
2370
2371
"""
2372
2373
a1,a2,a3,a4,a6 = E.ainvs()
2374
2375
b2 = E.b2()
2376
b4 = E.b4()
2377
2378
n = psi.degree()
2379
d = 2*n+1
2380
2381
x = self.__x_var
2382
y = self.__y_var
2383
2384
psi_2 = 2*y + a1*x + a3
2385
2386
psi_coeffs = psi.univariate_polynomial().list()
2387
2388
if (0 < n):
2389
s1 = -psi_coeffs[n-1]
2390
else:
2391
s1 = 0
2392
2393
psi_prpr = 0
2394
cur_x_pow = 1
2395
2396
#
2397
# Note: we now get the "derivatives" of psi
2398
# these are not actually the derivatives
2399
# furthermore, the formulas in Kohel's
2400
# thesis are wrong, the correct formulas
2401
# are coded below
2402
#
2403
from sage.rings.arith import binomial
2404
2405
for j in xrange(0,n-1):
2406
psi_prpr = psi_prpr + \
2407
binomial(j+2,2)*psi_coeffs[(j+2)]*cur_x_pow
2408
cur_x_pow = x*cur_x_pow
2409
2410
psi_prprpr = 0
2411
cur_x_pow = 1
2412
2413
for j in xrange(0,n-2):
2414
psi_prprpr = psi_prprpr + \
2415
(3*binomial(j+3,3))*psi_coeffs[(j+3)]*cur_x_pow
2416
cur_x_pow = x*cur_x_pow
2417
2418
2419
omega = phi_pr*psi*y - phi*psi_pr*psi_2 + \
2420
((a1*x + a3)*(psi_2**2)*(psi_prpr*psi_pr-psi_prprpr*psi) + \
2421
(a1*psi_2**2 - 3*(a1*x + a3)*(6*x**2 + b2*x + b4))*psi_prpr*psi + \
2422
(a1*x**3 + 3*a3*x**2 + (2*a2*a3 - a1*a4)*x + (a3*a4 - 2*a1*a6))*psi_pr**2 + \
2423
(-(3*a1*x**2 + 6*a3*x + (-a1*a4 + 2*a2*a3)) + \
2424
(a1*x + a3)*(d*x - 2*s1) )*psi_pr*psi + (a1*s1 + a3*n)*psi**2)*psi
2425
2426
return omega
2427
2428
2429
def __compute_via_kohel_numeric(self, xP, yP):
2430
r"""
2431
Private function that computes a numeric result of this
2432
isogeny (via Kohel's formulas.)
2433
2434
EXAMPLES:
2435
2436
These examples inherently exercise this private function::
2437
2438
sage: R.<x> = GF(7)[]
2439
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2440
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3)
2441
sage: P = E((0,1)); phi(P)
2442
(2 : 0 : 1)
2443
sage: P = E((1,1)); phi(P)
2444
(0 : 1 : 0)
2445
sage: phi._EllipticCurveIsogeny__compute_via_kohel_numeric(0, 1)
2446
(2, 0)
2447
sage: phi._EllipticCurveIsogeny__compute_via_kohel_numeric(1, 1)
2448
(0 : 1 : 0)
2449
2450
"""
2451
2452
# first check if this is a kernel point
2453
# to avoid a divide by 0 error later
2454
if(0 == self.__inner_kernel_polynomial(x=xP)):
2455
return self.__intermediate_codomain(0)
2456
2457
(xP_out, yP_out) = self.__compute_via_kohel(xP,yP)
2458
2459
# for some dang reason in some cases
2460
# when the base_field is a number field
2461
# xP_out and yP_out do not get evaluated to field elements
2462
# but rather constant polynomials.
2463
# So in this case, we do some explicit casting to make sure
2464
# everything comes out right
2465
2466
if is_NumberField(self.__base_field) and (1 < self.__base_field.degree()) :
2467
xP_out = self.__poly_ring(xP_out).constant_coefficient()
2468
yP_out = self.__poly_ring(yP_out).constant_coefficient()
2469
2470
return (xP_out,yP_out)
2471
2472
2473
def __compute_via_kohel(self, xP, yP):
2474
r"""
2475
Private function that applies Kohel's formulas.
2476
2477
EXAMPLES:
2478
2479
These examples inherently exercise this private function::
2480
2481
sage: R.<x> = GF(7)[]
2482
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2483
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3)
2484
sage: P = E((0,1)); phi(P)
2485
(2 : 0 : 1)
2486
sage: phi.rational_maps()
2487
((x^3 - 2*x^2 + 3*x + 2)/(x^2 - 2*x + 1),
2488
(x^3*y - 3*x^2*y + x*y)/(x^3 - 3*x^2 + 3*x - 1))
2489
sage: phi._EllipticCurveIsogeny__compute_via_kohel(0,1)
2490
(2, 0)
2491
sage: R.<x,y> = GF(7)[]
2492
sage: phi._EllipticCurveIsogeny__compute_via_kohel(x,y)
2493
((x^3 - 2*x^2 + 3*x + 2)/(x^2 - 2*x + 1),
2494
(x^3*y - 3*x^2*y + x*y)/(x^3 - 3*x^2 + 3*x - 1))
2495
2496
"""
2497
2498
x = self.__x_var
2499
y = self.__y_var
2500
2501
psi_out = self.__psi(xP,yP)
2502
phi_out = self.__phi(xP,yP)
2503
omega_out =self.__omega(xP, yP)
2504
2505
psi_inv_out = 1/psi_out
2506
2507
psi_inv_sq_out = psi_inv_out**2
2508
2509
X_out = phi_out*(psi_inv_sq_out)
2510
Y_out = omega_out*(psi_inv_sq_out*psi_inv_out)
2511
2512
return (X_out, Y_out)
2513
2514
2515
def __initialize_rational_maps_via_kohel(self):
2516
r"""
2517
Private function that computes and initializes the rational
2518
maps of this isogeny.
2519
2520
EXAMPLES:
2521
2522
These examples inherently exercise this private function::
2523
2524
sage: R.<x> = GF(7)[]
2525
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2526
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3)
2527
sage: phi.rational_maps()
2528
((x^3 - 2*x^2 + 3*x + 2)/(x^2 - 2*x + 1),
2529
(x^3*y - 3*x^2*y + x*y)/(x^3 - 3*x^2 + 3*x - 1))
2530
sage: phi._EllipticCurveIsogeny__initialize_rational_maps_via_kohel()
2531
((x^3 - 2*x^2 + 3*x + 2)/(x^2 - 2*x + 1),
2532
(x^3*y - 3*x^2*y + x*y)/(x^3 - 3*x^2 + 3*x - 1))
2533
2534
2535
"""
2536
x = self.__x_var
2537
y = self.__y_var
2538
2539
(X,Y) = self.__compute_via_kohel(x,y)
2540
2541
return (X,Y)
2542
2543
2544
#
2545
# Kohel's formula computing the codomain curve
2546
#
2547
def __compute_E2_via_kohel(self):
2548
r"""
2549
Private function that computes and initializes the codomain of
2550
the isogeny (via Kohel's.)
2551
2552
EXAMPLES:
2553
2554
These examples inherently exercise this private function::
2555
2556
sage: R.<x> = GF(7)[]
2557
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2558
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3)
2559
sage: phi.codomain()
2560
Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 4*x + 2 over Finite Field of size 7
2561
sage: phi._EllipticCurveIsogeny__compute_E2_via_kohel()
2562
Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 4*x + 2 over Finite Field of size 7
2563
2564
"""
2565
2566
v = self.__v
2567
w = self.__w
2568
2569
return compute_codomain_formula(self.__E1, v,w)
2570
2571
2572
2573
#
2574
# public isogeny methods
2575
#
2576
2577
def domain(self):
2578
r"""
2579
Returns the domain curve of this isogeny.
2580
2581
EXAMPLES::
2582
2583
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
2584
sage: phi = EllipticCurveIsogeny(E, E(0,0))
2585
sage: phi.domain() == E
2586
True
2587
2588
sage: E = EllipticCurve(GF(31), [1,0,0,1,2])
2589
sage: phi = EllipticCurveIsogeny(E, [17, 1])
2590
sage: phi.domain()
2591
Elliptic Curve defined by y^2 + x*y = x^3 + x + 2 over Finite Field of size 31
2592
2593
"""
2594
return self.__E1
2595
2596
2597
def codomain(self):
2598
r"""
2599
Returns the codomain (range) curve of this isogeny.
2600
2601
EXAMPLES::
2602
2603
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
2604
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
2605
sage: phi.codomain()
2606
Elliptic Curve defined by y^2 = x^3 - 4*x over Rational Field
2607
2608
sage: E = EllipticCurve(GF(31), [1,0,0,1,2])
2609
sage: phi = EllipticCurveIsogeny(E, [17, 1])
2610
sage: phi.codomain()
2611
Elliptic Curve defined by y^2 + x*y = x^3 + 24*x + 6 over Finite Field of size 31
2612
2613
"""
2614
return self.__E2
2615
2616
2617
def degree(self):
2618
r"""
2619
Returns the degree of this isogeny.
2620
2621
EXAMPLES::
2622
2623
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
2624
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
2625
sage: phi.degree()
2626
2
2627
sage: phi = EllipticCurveIsogeny(E, [0,1,0,1])
2628
sage: phi.degree()
2629
4
2630
2631
sage: E = EllipticCurve(GF(31), [1,0,0,1,2])
2632
sage: phi = EllipticCurveIsogeny(E, [17, 1])
2633
sage: phi.degree()
2634
3
2635
2636
"""
2637
return self.__degree
2638
2639
2640
def rational_maps(self):
2641
r"""
2642
This function returns this isogeny as a pair of rational maps.
2643
2644
EXAMPLES::
2645
2646
sage: E = EllipticCurve(QQ, [0,2,0,1,-1])
2647
sage: phi = EllipticCurveIsogeny(E, [1])
2648
sage: phi.rational_maps()
2649
(x, y)
2650
2651
sage: E = EllipticCurve(GF(17), [0,0,0,3,0])
2652
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
2653
sage: phi.rational_maps()
2654
((x^2 + 3)/x, (x^2*y - 3*y)/x^2)
2655
2656
2657
"""
2658
if (not self.__rational_maps_initialized):
2659
self.__initialize_rational_maps()
2660
return (self.__X_coord_rational_map, self.__Y_coord_rational_map)
2661
2662
2663
def is_separable(self):
2664
r"""
2665
This function returns a bool indicating whether or not this
2666
isogeny is separable.
2667
2668
This function always returns ``True`` as currently this class
2669
only implements separable isogenies.
2670
2671
EXAMPLES::
2672
2673
sage: E = EllipticCurve(GF(17), [0,0,0,3,0])
2674
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
2675
sage: phi.is_separable()
2676
True
2677
2678
sage: E = EllipticCurve('11a1')
2679
sage: phi = EllipticCurveIsogeny(E, E.torsion_points())
2680
sage: phi.is_separable()
2681
True
2682
2683
2684
"""
2685
return self.__separable
2686
2687
2688
def kernel_polynomial(self):
2689
r"""
2690
Returns the kernel polynomial of this isogeny.
2691
2692
EXAMPLES::
2693
2694
sage: E = EllipticCurve(QQ, [0,0,0,2,0])
2695
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
2696
sage: phi.kernel_polynomial()
2697
x
2698
2699
sage: E = EllipticCurve('11a1')
2700
sage: phi = EllipticCurveIsogeny(E, E.torsion_points())
2701
sage: phi.kernel_polynomial()
2702
x^2 - 21*x + 80
2703
2704
sage: E = EllipticCurve(GF(17), [1,-1,1,-1,1])
2705
sage: phi = EllipticCurveIsogeny(E, [1])
2706
sage: phi.kernel_polynomial()
2707
1
2708
2709
sage: E = EllipticCurve(GF(31), [0,0,0,3,0])
2710
sage: phi = EllipticCurveIsogeny(E, [0,3,0,1])
2711
sage: phi.kernel_polynomial()
2712
x^3 + 3*x
2713
2714
2715
"""
2716
if (self.__kernel_polynomial is None):
2717
self.__init_kernel_polynomial()
2718
2719
return self.__kernel_polynomial.univariate_polynomial()
2720
2721
2722
def set_pre_isomorphism(self, preWI):
2723
r"""
2724
Modifies this isogeny object to pre compose with the given
2725
Weierstrass isomorphism.
2726
2727
EXAMPLES::
2728
2729
sage: E = EllipticCurve(GF(31), [1,1,0,1,-1])
2730
sage: R.<x> = GF(31)[]
2731
sage: f = x^3 + 9*x^2 + x + 30
2732
sage: phi = EllipticCurveIsogeny(E, f)
2733
sage: Epr = E.short_weierstrass_model()
2734
sage: isom = Epr.isomorphism_to(E)
2735
sage: phi.set_pre_isomorphism(isom)
2736
sage: phi.rational_maps()
2737
((-6*x^4 - 3*x^3 + 12*x^2 + 10*x - 1)/(x^3 + x - 12),
2738
(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))
2739
sage: phi(Epr((0,22)))
2740
(13 : 21 : 1)
2741
sage: phi(Epr((3,7)))
2742
(14 : 17 : 1)
2743
2744
sage: E = EllipticCurve(GF(29), [0,0,0,1,0])
2745
sage: R.<x> = GF(29)[]
2746
sage: f = x^2 + 5
2747
sage: phi = EllipticCurveIsogeny(E, f)
2748
sage: phi
2749
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
2750
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
2751
sage: inv_isom = WeierstrassIsomorphism(E, (1,-2,5,10))
2752
sage: Epr = inv_isom.codomain().codomain()
2753
sage: isom = Epr.isomorphism_to(E)
2754
sage: phi.set_pre_isomorphism(isom); phi
2755
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
2756
sage: phi(Epr((12,1)))
2757
(26 : 0 : 1)
2758
sage: phi(Epr((2,9)))
2759
(0 : 0 : 1)
2760
sage: phi(Epr((21,12)))
2761
(3 : 0 : 1)
2762
sage: phi.rational_maps()[0]
2763
(x^5 - 10*x^4 - 6*x^3 - 7*x^2 - x + 3)/(x^4 - 8*x^3 + 5*x^2 - 14*x - 6)
2764
2765
sage: E = EllipticCurve('11a1')
2766
sage: R.<x> = QQ[]
2767
sage: f = x^2 - 21*x + 80
2768
sage: phi = EllipticCurveIsogeny(E, f); phi
2769
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
2770
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
2771
sage: Epr = E.short_weierstrass_model()
2772
sage: isom = Epr.isomorphism_to(E)
2773
sage: phi.set_pre_isomorphism(isom)
2774
sage: phi
2775
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
2776
sage: phi(Epr((168,1188)))
2777
(0 : 1 : 0)
2778
2779
"""
2780
2781
WIdom = preWI.domain().codomain()
2782
WIcod = preWI.codomain().codomain()
2783
2784
if (type(preWI) != WeierstrassIsomorphism):
2785
raise ValueError, "Invalid parameter: isomorphism must be of type Weierstrass isomorphism."
2786
2787
if (self.__E1 != WIcod):
2788
raise ValueError, "Invalid parameter: isomorphism must have codomain curve equal to this isogenies' domain."
2789
2790
if (self.__pre_isomorphism is None):
2791
isom = preWI
2792
domain = WIdom
2793
else:
2794
isom = self.__pre_isomorphism*preWI
2795
domain = WIdom
2796
2797
self.__clear_cached_values()
2798
2799
self.__set_pre_isomorphism(domain, isom)
2800
2801
return
2802
2803
2804
def set_post_isomorphism(self, postWI):
2805
r"""
2806
Modifies this isogeny object to post compose with the given
2807
Weierstrass isomorphism.
2808
2809
EXAMPLES::
2810
2811
sage: E = EllipticCurve(j=GF(31)(0))
2812
sage: R.<x> = GF(31)[]
2813
sage: phi = EllipticCurveIsogeny(E, x+18)
2814
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
2815
sage: phi.set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(), (6,8,10,12)))
2816
sage: phi
2817
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
2818
2819
sage: E = EllipticCurve(j=GF(47)(0))
2820
sage: f = E.torsion_polynomial(3)/3
2821
sage: phi = EllipticCurveIsogeny(E, f)
2822
sage: E2 = phi.codomain()
2823
sage: post_isom = E2.isomorphism_to(E)
2824
sage: phi.set_post_isomorphism(post_isom)
2825
sage: phi.rational_maps() == E.multiplication_by_m(3)
2826
False
2827
sage: phi.switch_sign()
2828
sage: phi.rational_maps() == E.multiplication_by_m(3)
2829
True
2830
2831
Example over a number field::
2832
2833
sage: R.<x> = QQ[]
2834
sage: K.<a> = NumberField(x^2 + 2)
2835
sage: E = EllipticCurve(j=K(1728))
2836
sage: ker_list = E.torsion_points()
2837
sage: phi = EllipticCurveIsogeny(E, ker_list)
2838
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
2839
sage: post_isom = WeierstrassIsomorphism(phi.codomain(), (a,2,3,5))
2840
sage: phi
2841
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
2842
2843
"""
2844
2845
WIdom = postWI.domain().codomain()
2846
WIcod = postWI.codomain().codomain()
2847
2848
if (type(postWI) != WeierstrassIsomorphism):
2849
raise ValueError, "Invalid parameter: isomorphism must be of type Weierstrass isomorphism."
2850
2851
if (self.__E2 != WIdom):
2852
raise ValueError, "Invalid parameter: isomorphism must have domain curve equal to this isogenies' codomain."
2853
2854
if (self.__post_isomorphism is None):
2855
isom = postWI
2856
codomain = WIcod
2857
else:
2858
isom = postWI*self.__post_isomorphism
2859
codomain = WIcod
2860
2861
self.__clear_cached_values()
2862
2863
self.__set_post_isomorphism(codomain, isom)
2864
2865
return
2866
2867
2868
def get_pre_isomorphism(self):
2869
r"""
2870
Returns the pre-isomorphism of this isogeny. If there has
2871
been no pre-isomorphism set, this returns ``None``.
2872
2873
EXAMPLES::
2874
2875
sage: E = EllipticCurve(GF(31), [1,1,0,1,-1])
2876
sage: R.<x> = GF(31)[]
2877
sage: f = x^3 + 9*x^2 + x + 30
2878
sage: phi = EllipticCurveIsogeny(E, f)
2879
sage: phi.get_post_isomorphism()
2880
sage: Epr = E.short_weierstrass_model()
2881
sage: isom = Epr.isomorphism_to(E)
2882
sage: phi.set_pre_isomorphism(isom)
2883
sage: isom == phi.get_pre_isomorphism()
2884
True
2885
2886
sage: E = EllipticCurve(GF(83), [1,0,1,1,0])
2887
sage: R.<x> = GF(83)[]; f = x+24
2888
sage: phi = EllipticCurveIsogeny(E, f)
2889
sage: E2 = phi.codomain()
2890
sage: phi2 = EllipticCurveIsogeny(E, None, E2, 2)
2891
sage: phi2.get_pre_isomorphism()
2892
Generic morphism:
2893
From: Abelian group of points on Elliptic Curve defined by y^2 + x*y + y = x^3 + x over Finite Field of size 83
2894
To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 62*x + 74 over Finite Field of size 83
2895
Via: (u,r,s,t) = (1, 76, 41, 3)
2896
2897
2898
2899
"""
2900
return self.__pre_isomorphism
2901
2902
2903
def get_post_isomorphism(self):
2904
r"""
2905
Returns the post-isomorphism of this isogeny. If there has
2906
been no post-isomorphism set, this returns ``None``.
2907
2908
EXAMPLES::
2909
2910
sage: E = EllipticCurve(j=GF(31)(0))
2911
sage: R.<x> = GF(31)[]
2912
sage: phi = EllipticCurveIsogeny(E, x+18)
2913
sage: phi.get_post_isomorphism()
2914
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
2915
sage: isom = WeierstrassIsomorphism(phi.codomain(), (6,8,10,12))
2916
sage: phi.set_post_isomorphism(isom)
2917
sage: isom == phi.get_post_isomorphism()
2918
True
2919
2920
sage: E = EllipticCurve(GF(83), [1,0,1,1,0])
2921
sage: R.<x> = GF(83)[]; f = x+24
2922
sage: phi = EllipticCurveIsogeny(E, f)
2923
sage: E2 = phi.codomain()
2924
sage: phi2 = EllipticCurveIsogeny(E, None, E2, 2)
2925
sage: phi2.get_post_isomorphism()
2926
Generic morphism:
2927
From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 65*x + 69 over Finite Field of size 83
2928
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
2929
Via: (u,r,s,t) = (1, 7, 42, 80)
2930
2931
"""
2932
return self.__post_isomorphism
2933
2934
2935
def switch_sign(self):
2936
r"""
2937
This function composes the isogeny with `[-1]` (flipping the
2938
coefficient between +/-1 on the `y` coordinate rational map).
2939
2940
EXAMPLES::
2941
2942
sage: E = EllipticCurve(GF(23), [0,0,0,1,0])
2943
sage: f = E.torsion_polynomial(3)/3
2944
sage: phi = EllipticCurveIsogeny(E, f, E)
2945
sage: phi.rational_maps() == E.multiplication_by_m(3)
2946
False
2947
sage: phi.switch_sign()
2948
sage: phi.rational_maps() == E.multiplication_by_m(3)
2949
True
2950
2951
sage: E = EllipticCurve(GF(17), [-2, 3, -5, 7, -11])
2952
sage: R.<x> = GF(17)[]
2953
sage: f = x+6
2954
sage: phi = EllipticCurveIsogeny(E, f)
2955
sage: phi
2956
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
2957
sage: phi.rational_maps()
2958
((x^2 + 6*x + 4)/(x + 6), (x^2*y - 5*x*y + 8*x - 2*y)/(x^2 - 5*x + 2))
2959
sage: phi.switch_sign()
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),
2964
(2*x^3 - x^2*y - 5*x^2 + 5*x*y - 4*x + 2*y + 7)/(x^2 - 5*x + 2))
2965
2966
sage: E = EllipticCurve('11a1')
2967
sage: R.<x> = QQ[]
2968
sage: f = x^2 - 21*x + 80
2969
sage: phi = EllipticCurveIsogeny(E, f)
2970
sage: (xmap1, ymap1) = phi.rational_maps()
2971
sage: phi.switch_sign()
2972
sage: (xmap2, ymap2) = phi.rational_maps()
2973
sage: xmap1 == xmap2
2974
True
2975
sage: ymap1 == -ymap2 - E.a1()*xmap2 - E.a3()
2976
True
2977
2978
sage: K.<a> = NumberField(x^2 + 1)
2979
sage: E = EllipticCurve(K, [0,0,0,1,0])
2980
sage: R.<x> = K[]
2981
sage: phi = EllipticCurveIsogeny(E, x-a)
2982
sage: phi.rational_maps()
2983
((x^2 + (-a)*x - 2)/(x + (-a)), (x^2*y + (-2*a)*x*y + y)/(x^2 + (-2*a)*x - 1))
2984
sage: phi.switch_sign()
2985
sage: phi.rational_maps()
2986
((x^2 + (-a)*x - 2)/(x + (-a)), (-x^2*y + (2*a)*x*y - y)/(x^2 + (-2*a)*x - 1))
2987
2988
"""
2989
self.set_post_isomorphism(WeierstrassIsomorphism(self.__E2, (-1,0,-self.__E2.a1(),-self.__E2.a3())))
2990
2991
def is_normalized(self, via_formal=True, check_by_pullback=True):
2992
r"""
2993
Returns ``True`` if this isogeny is normalized. An isogeny
2994
`\varphi\colon E\to E_2` between two given Weierstrass
2995
equations is said to be normalized if the constant `c` is `1`
2996
in `\varphi*(\omega_2) = c\cdot\omega`, where `\omega` and
2997
`omega_2` are the invariant differentials on `E` and `E_2`
2998
corresponding to the given equation.
2999
3000
INPUT:
3001
3002
- ``via_formal`` - (default: ``True``) If ``True`` it simply checks if
3003
the leading term of the formal series is 1. Otherwise
3004
it uses a deprecated algorithm involving the second
3005
optional argument.
3006
3007
- ``check_by_pullback`` - (default:``True``) Deprecated.
3008
3009
EXAMPLES::
3010
3011
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
3012
sage: E = EllipticCurve(GF(7), [0,0,0,1,0])
3013
sage: R.<x> = GF(7)[]
3014
sage: phi = EllipticCurveIsogeny(E, x)
3015
sage: phi.is_normalized()
3016
True
3017
sage: isom = WeierstrassIsomorphism(phi.codomain(), (3, 0, 0, 0))
3018
sage: phi.set_post_isomorphism(isom)
3019
sage: phi.is_normalized()
3020
False
3021
sage: isom = WeierstrassIsomorphism(phi.codomain(), (5, 0, 0, 0))
3022
sage: phi.set_post_isomorphism(isom)
3023
sage: phi.is_normalized()
3024
True
3025
sage: isom = WeierstrassIsomorphism(phi.codomain(), (1, 1, 1, 1))
3026
sage: phi.set_post_isomorphism(isom)
3027
sage: phi.is_normalized()
3028
True
3029
3030
sage: F = GF(2^5, 'alpha'); alpha = F.gen()
3031
sage: E = EllipticCurve(F, [1,0,1,1,1])
3032
sage: R.<x> = F[]
3033
sage: phi = EllipticCurveIsogeny(E, x+1)
3034
sage: isom = WeierstrassIsomorphism(phi.codomain(), (alpha, 0, 0, 0))
3035
sage: phi.is_normalized()
3036
True
3037
sage: phi.set_post_isomorphism(isom)
3038
sage: phi.is_normalized()
3039
False
3040
sage: isom = WeierstrassIsomorphism(phi.codomain(), (1/alpha, 0, 0, 0))
3041
sage: phi.set_post_isomorphism(isom)
3042
sage: phi.is_normalized()
3043
True
3044
sage: isom = WeierstrassIsomorphism(phi.codomain(), (1, 1, 1, 1))
3045
sage: phi.set_post_isomorphism(isom)
3046
sage: phi.is_normalized()
3047
True
3048
3049
sage: E = EllipticCurve('11a1')
3050
sage: R.<x> = QQ[]
3051
sage: f = x^3 - x^2 - 10*x - 79/4
3052
sage: phi = EllipticCurveIsogeny(E, f)
3053
sage: isom = WeierstrassIsomorphism(phi.codomain(), (2, 0, 0, 0))
3054
sage: phi.is_normalized()
3055
True
3056
sage: phi.set_post_isomorphism(isom)
3057
sage: phi.is_normalized()
3058
False
3059
sage: isom = WeierstrassIsomorphism(phi.codomain(), (1/2, 0, 0, 0))
3060
sage: phi.set_post_isomorphism(isom)
3061
sage: phi.is_normalized()
3062
True
3063
sage: isom = WeierstrassIsomorphism(phi.codomain(), (1, 1, 1, 1))
3064
sage: phi.set_post_isomorphism(isom)
3065
sage: phi.is_normalized()
3066
True
3067
3068
"""
3069
# easy algorithm using the formal expansion.
3070
if via_formal:
3071
phi_formal = self.formal(prec=5)
3072
return phi_formal[1] == 1
3073
3074
# this is the old algorithm. it should be deprecated.
3075
check_prepost_isomorphism = False
3076
3077
f_normalized = True
3078
3079
if (check_by_pullback):
3080
3081
(Xmap, Ymap) = self.rational_maps()
3082
3083
E1 = self.__E1
3084
E2 = self.__E2
3085
3086
a1 = E1.a1()
3087
a3 = E1.a3()
3088
3089
a1pr = E2.a1()
3090
a3pr = E2.a3()
3091
3092
x = self.__x_var
3093
y = self.__y_var
3094
3095
Xmap_pr = Xmap.derivative(x)
3096
3097
domain_inv_diff = 1/(2*y + a1*x + a3)
3098
codomain_inv_diff = Xmap_pr/(2*Ymap + a1pr*Xmap + a3pr)
3099
3100
inv_diff_quo = domain_inv_diff/codomain_inv_diff
3101
3102
if (1 == inv_diff_quo):
3103
f_normalized = True
3104
else:
3105
# For some reason, in certain cases, when the isogeny
3106
# is pre or post composed with a translation the
3107
# resulting rational functions are too complicated for
3108
# sage to simplify down to a constant in this case, we
3109
# do some cheating by checking if the post-composition
3110
# by isogeny has a non 1 scaling factor
3111
if ( inv_diff_quo.numerator().is_constant() and (inv_diff_quo.denominator().is_constant) ):
3112
f_normalized = False
3113
else:
3114
check_prepost_isomorphism = True
3115
else:
3116
check_prepost_isomorphism = True
3117
3118
# If we skip checking by the pullback of the invariant
3119
# differential OR if that was inconclusive We explicitly check
3120
# if there is a post isomorphism and if it has a non 1 scaling
3121
# factor or if it is a just a translation. NOTE: This only
3122
# works because we are using algorithms for calculating the
3123
# isogenies that calculate a separable normalized isogeny, if
3124
# this changes, this check will no longer be correct.
3125
#
3126
if (check_prepost_isomorphism):
3127
post_isom = self.__post_isomorphism
3128
if (post_isom is not None):
3129
if (1 == self.__base_field(post_isom.u)):
3130
f_post_normalized = True
3131
else:
3132
f_post_normalized = False
3133
else:
3134
f_post_normalized = True
3135
3136
pre_isom = self.__pre_isomorphism
3137
if (pre_isom is not None):
3138
if (1 == self.__base_field(pre_isom.u)):
3139
f_pre_normalized = True
3140
else:
3141
f_pre_normalized = False
3142
else:
3143
f_pre_normalized = True
3144
3145
f_normalized = f_pre_normalized and f_post_normalized
3146
3147
return f_normalized
3148
3149
def dual(self):
3150
r"""
3151
Computes and returns the dual isogeny of this isogeny. If
3152
`\varphi\colon E \to E_2` is the given isogeny, then the dual
3153
is by definition the unique isogeny `\hat\varphi\colon E_2\to
3154
E` such that the compositions `\hat\varphi\circ\varphi` and
3155
`\varphi\circ\hat\varphi` are the multiplication `[n]` by the
3156
degree of `\varphi` on `E` and `E_2` respectively.
3157
3158
EXAMPLES::
3159
3160
sage: E = EllipticCurve('11a1')
3161
sage: R.<x> = QQ[]
3162
sage: f = x^2 - 21*x + 80
3163
sage: phi = EllipticCurveIsogeny(E, f)
3164
sage: phi_hat = phi.dual()
3165
sage: phi_hat.domain() == phi.codomain()
3166
True
3167
sage: phi_hat.codomain() == phi.domain()
3168
True
3169
sage: (X, Y) = phi.rational_maps()
3170
sage: (Xhat, Yhat) = phi_hat.rational_maps()
3171
sage: Xm = Xhat.subs(x=X, y=Y)
3172
sage: Ym = Yhat.subs(x=X, y=Y)
3173
sage: (Xm, Ym) == E.multiplication_by_m(5)
3174
True
3175
3176
sage: E = EllipticCurve(GF(37), [0,0,0,1,8])
3177
sage: R.<x> = GF(37)[]
3178
sage: f = x^3 + x^2 + 28*x + 33
3179
sage: phi = EllipticCurveIsogeny(E, f)
3180
sage: phi_hat = phi.dual()
3181
sage: phi_hat.codomain() == phi.domain()
3182
True
3183
sage: phi_hat.domain() == phi.codomain()
3184
True
3185
sage: (X, Y) = phi.rational_maps()
3186
sage: (Xhat, Yhat) = phi_hat.rational_maps()
3187
sage: Xm = Xhat.subs(x=X, y=Y)
3188
sage: Ym = Yhat.subs(x=X, y=Y)
3189
sage: (Xm, Ym) == E.multiplication_by_m(7)
3190
True
3191
3192
sage: E = EllipticCurve(GF(31), [0,0,0,1,8])
3193
sage: R.<x> = GF(31)[]
3194
sage: f = x^2 + 17*x + 29
3195
sage: phi = EllipticCurveIsogeny(E, f)
3196
sage: phi_hat = phi.dual()
3197
sage: phi_hat.codomain() == phi.domain()
3198
True
3199
sage: phi_hat.domain() == phi.codomain()
3200
True
3201
sage: (X, Y) = phi.rational_maps()
3202
sage: (Xhat, Yhat) = phi_hat.rational_maps()
3203
sage: Xm = Xhat.subs(x=X, y=Y)
3204
sage: Ym = Yhat.subs(x=X, y=Y)
3205
sage: (Xm, Ym) == E.multiplication_by_m(5)
3206
True
3207
3208
Test (for trac ticket 7096)::
3209
3210
sage: E = EllipticCurve('11a1')
3211
sage: phi = E.isogeny(E(5,5))
3212
sage: phi.dual().dual() == phi
3213
True
3214
3215
sage: k = GF(103)
3216
sage: E = EllipticCurve(k,[11,11])
3217
sage: phi = E.isogeny(E(4,4))
3218
sage: phi
3219
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
3220
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
3221
sage: phi.set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(),(5,0,1,2)))
3222
sage: phi.dual().dual() == phi
3223
True
3224
3225
sage: E = EllipticCurve(GF(103),[1,0,0,1,-1])
3226
sage: phi = E.isogeny(E(60,85))
3227
sage: phi.dual()
3228
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
3229
3230
"""
3231
3232
if (self.__base_field.characteristic() in [2,3]):
3233
raise NotImplemented
3234
3235
if (self.__dual is not None):
3236
return self.__dual
3237
3238
# trac 7096
3239
(E1, E2pr, pre_isom, post_isom) = compute_intermediate_curves(self.codomain(), self.domain())
3240
3241
F = self.__base_field
3242
3243
d = self.__degree
3244
3245
# trac 7096
3246
if F(d) == 0:
3247
raise NotImplementedError, "The dual isogeny is not separable, but only separable isogenies are implemented so far"
3248
3249
# trac 7096
3250
# this should take care of the case when the isogeny is not normalized.
3251
u = self.formal(prec=5)[1]
3252
isom = WeierstrassIsomorphism(E2pr, (u/F(d), 0, 0, 0))
3253
3254
E2 = isom.codomain().codomain()
3255
3256
pre_isom = self.__E2.isomorphism_to(E1)
3257
post_isom = E2.isomorphism_to(self.__E1)
3258
3259
phi_hat = EllipticCurveIsogeny(E1, None, E2, d)
3260
3261
phi_hat.set_pre_isomorphism(pre_isom)
3262
phi_hat.set_post_isomorphism(post_isom)
3263
phi_hat.__perform_inheritance_housekeeping()
3264
3265
assert phi_hat.codomain() == self.domain()
3266
3267
# trac 7096 : this adjust a posteriori the automorphism
3268
# on the codomain of the dual isogeny.
3269
# we used _a_ Weierstrass isomorphism to get to the original
3270
# curve, but we may have to change it my an automorphism.
3271
# we impose that the composition has the degree
3272
# as a leading coefficient in the formal expansion.
3273
3274
phi_sc = self.formal(prec=5)[1]
3275
phihat_sc = phi_hat.formal(prec=5)[1]
3276
3277
sc = phi_sc * phihat_sc/F(d)
3278
3279
if sc != 1:
3280
auts = phi_hat.codomain().automorphsims()
3281
aut = [a for a in auts if a.u == sc]
3282
if len(aut) != 1:
3283
raise ValueError, "There is a bug in dual()."
3284
phi_hat.set_post_isomorphism(a[0])
3285
3286
self.__dual = phi_hat
3287
3288
return phi_hat
3289
3290
def formal(self,prec=20):
3291
r"""
3292
Computes the formal isogeny as a power series in the variable
3293
`t=-x/y` on the domain curve.
3294
3295
INPUT:
3296
3297
- ``prec`` - (default = 20), the precision with which the computations
3298
in the formal group are carried out.
3299
3300
EXAMPLES::
3301
3302
sage: E = EllipticCurve(GF(13),[1,7])
3303
sage: phi = E.isogeny(E(10,4))
3304
sage: phi.formal()
3305
t + 12*t^13 + 2*t^17 + 8*t^19 + 2*t^21 + O(t^23)
3306
3307
sage: E = EllipticCurve([0,1])
3308
sage: phi = E.isogeny(E(2,3))
3309
sage: phi.formal(prec=10)
3310
t + 54*t^5 + 255*t^7 + 2430*t^9 + 19278*t^11 + O(t^13)
3311
3312
sage: E = EllipticCurve('11a2')
3313
sage: R.<x> = QQ[]
3314
sage: phi = E.isogeny(x^2 + 101*x + 12751/5)
3315
sage: phi.formal(prec=7)
3316
t - 2724/5*t^5 + 209046/5*t^7 - 4767/5*t^8 + 29200946/5*t^9 + O(t^10)
3317
3318
3319
"""
3320
Eh = self.__E1.formal()
3321
f, g = self.rational_maps()
3322
xh = Eh.x(prec=prec)
3323
yh = Eh.y(prec=prec)
3324
fh = f(xh,yh)
3325
gh = g(xh,yh)
3326
return -fh/gh
3327
3328
#
3329
# Overload Morphism methods that we want to
3330
#
3331
3332
def _composition_(self, right, homset):
3333
r"""
3334
Composition operator function inherited from morphism class.
3335
3336
EXAMPLES::
3337
3338
sage: E = EllipticCurve(j=GF(7)(0))
3339
sage: phi = EllipticCurveIsogeny(E, [E(0), E((0,1)), E((0,-1))])
3340
sage: phi._composition_(phi, phi.parent())
3341
Traceback (most recent call last):
3342
...
3343
NotImplementedError
3344
3345
The following should test that :meth:`_composition_` is called
3346
upon a product. However phi is currently improperly
3347
constructed (see :trac:`12880`), which triggers an assertion
3348
failure before the actual call ::
3349
3350
sage: phi*phi
3351
Traceback (most recent call last):
3352
...
3353
TypeError: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 is not in Category of hom sets in Category of Schemes
3354
3355
Here would be the desired output::
3356
3357
sage: phi*phi # not tested
3358
Traceback (most recent call last):
3359
...
3360
NotImplementedError
3361
"""
3362
raise NotImplementedError
3363
3364
3365
def is_injective(self):
3366
r"""
3367
Method inherited from the morphism class. Returns ``True`` if
3368
and only if this isogeny has trivial kernel.
3369
3370
EXAMPLES::
3371
3372
sage: E = EllipticCurve('11a1')
3373
sage: R.<x> = QQ[]
3374
sage: f = x^2 + x - 29/5
3375
sage: phi = EllipticCurveIsogeny(E, f)
3376
sage: phi.is_injective()
3377
False
3378
sage: phi = EllipticCurveIsogeny(E, R(1))
3379
sage: phi.is_injective()
3380
True
3381
3382
sage: F = GF(7)
3383
sage: E = EllipticCurve(j=F(0))
3384
sage: phi = EllipticCurveIsogeny(E, [ E((0,-1)), E((0,1))])
3385
sage: phi.is_injective()
3386
False
3387
sage: phi = EllipticCurveIsogeny(E, E(0))
3388
sage: phi.is_injective()
3389
True
3390
3391
"""
3392
3393
if (1 < self.__degree): return False
3394
return True
3395
3396
3397
def is_surjective(self):
3398
r"""
3399
For elliptic curve isogenies, always returns ``True`` (as a
3400
non-constant map of algebraic curves must be surjective).
3401
3402
EXAMPLES::
3403
3404
sage: E = EllipticCurve('11a1')
3405
sage: R.<x> = QQ[]
3406
sage: f = x^2 + x - 29/5
3407
sage: phi = EllipticCurveIsogeny(E, f)
3408
sage: phi.is_surjective()
3409
True
3410
3411
sage: E = EllipticCurve(GF(7), [0,0,0,1,0])
3412
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
3413
sage: phi.is_surjective()
3414
True
3415
3416
sage: F = GF(2^5, 'omega')
3417
sage: E = EllipticCurve(j=F(0))
3418
sage: R.<x> = F[]
3419
sage: phi = EllipticCurveIsogeny(E, x)
3420
sage: phi.is_surjective()
3421
True
3422
3423
"""
3424
return True
3425
3426
def is_zero(self):
3427
r"""
3428
Member function inherited from morphism class.
3429
3430
EXAMPLES::
3431
3432
sage: E = EllipticCurve(j=GF(7)(0))
3433
sage: phi = EllipticCurveIsogeny(E, [ E((0,1)), E((0,-1))])
3434
sage: phi.is_zero()
3435
Traceback (most recent call last):
3436
...
3437
NotImplementedError
3438
"""
3439
raise NotImplementedError
3440
3441
def post_compose(self, left):
3442
r"""
3443
Member function inherited from morphism class.
3444
3445
EXAMPLES::
3446
3447
sage: E = EllipticCurve(j=GF(7)(0))
3448
sage: phi = EllipticCurveIsogeny(E, [ E((0,1)), E((0,-1))])
3449
sage: phi.post_compose(phi)
3450
Traceback (most recent call last):
3451
...
3452
NotImplementedError
3453
3454
"""
3455
raise NotImplementedError
3456
3457
3458
def pre_compose(self, right):
3459
r"""
3460
Member function inherited from morphism class.
3461
3462
EXAMPLES::
3463
3464
sage: E = EllipticCurve(j=GF(7)(0))
3465
sage: phi = EllipticCurveIsogeny(E, [ E((0,1)), E((0,-1))])
3466
sage: phi.pre_compose(phi)
3467
Traceback (most recent call last):
3468
...
3469
NotImplementedError
3470
3471
"""
3472
raise NotImplementedError
3473
3474
3475
def n(self):
3476
r"""
3477
Numerical Approximation inherited from Map (through morphism),
3478
nonsensical for isogenies.
3479
3480
EXAMPLES::
3481
3482
sage: E = EllipticCurve(j=GF(7)(0))
3483
sage: phi = EllipticCurveIsogeny(E, [ E((0,1)), E((0,-1))])
3484
sage: phi.n()
3485
Traceback (most recent call last):
3486
...
3487
NotImplementedError: Numerical approximations do not make sense for Elliptic Curve Isogenies
3488
3489
"""
3490
raise NotImplementedError, "Numerical approximations do not make sense for Elliptic Curve Isogenies"
3491
3492
# no longer needed (trac 7096)
3493
# def starks_find_r_and_t(T, Z):
3494
3495
def compute_isogeny_starks(E1, E2, ell):
3496
r"""
3497
Computes the degree ``ell`` isogeny between ``E1`` and ``E2`` via
3498
Stark's algorithm. There must be a degree ``ell``, separable,
3499
normalized cyclic isogeny from ``E1`` to ``E2``.
3500
3501
INPUT:
3502
3503
- ``E1`` - an elliptic curve in short Weierstrass form.
3504
- ``E2`` - an elliptic curve in short Weierstrass form.
3505
- ``ell`` - the degree of the isogeny from E1 to E2.
3506
3507
OUTPUT:
3508
3509
polynomial -- over the field of definition of ``E1``, ``E2``, that is the
3510
kernel polynomial of the isogeny from ``E1`` to ``E2``.
3511
3512
ALGORITHM:
3513
3514
This function uses Starks Algorithm as presented in section 6.2 of
3515
[BMSS].
3516
3517
.. note::
3518
3519
As published there, the algorithm is incorrect, and a correct
3520
version (with slightly different notation) can be found in
3521
[M09]. The algorithm originates in [S72]
3522
3523
REFERENCES:
3524
3525
- [BMSS] Boston, Morain, Salvy, Schost, "Fast Algorithms for Isogenies."
3526
- [M09] Moody, "The Diffie-Hellman Problem and Generalization of Verheul's Theorem"
3527
- [S72] Stark, "Class-numbers of complex quadratic fields."
3528
3529
EXAMPLES::
3530
3531
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_starks, compute_sequence_of_maps
3532
3533
sage: E = EllipticCurve(GF(97), [1,0,1,1,0])
3534
sage: R.<x> = GF(97)[]; f = x^5 + 27*x^4 + 61*x^3 + 58*x^2 + 28*x + 21
3535
sage: phi = EllipticCurveIsogeny(E, f)
3536
sage: E2 = phi.codomain()
3537
sage: (isom1, isom2, E1pr, E2pr, ker_poly) = compute_sequence_of_maps(E, E2, 11)
3538
sage: compute_isogeny_starks(E1pr, E2pr, 11)
3539
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
3540
3541
sage: E = EllipticCurve(GF(37), [0,0,0,1,8])
3542
sage: R.<x> = GF(37)[]
3543
sage: f = (x + 14) * (x + 30)
3544
sage: phi = EllipticCurveIsogeny(E, f)
3545
sage: E2 = phi.codomain()
3546
sage: compute_isogeny_starks(E, E2, 5)
3547
x^4 + 14*x^3 + x^2 + 34*x + 21
3548
sage: f**2
3549
x^4 + 14*x^3 + x^2 + 34*x + 21
3550
3551
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
3552
sage: R.<x> = QQ[]
3553
sage: f = x
3554
sage: phi = EllipticCurveIsogeny(E, f)
3555
sage: E2 = phi.codomain()
3556
sage: compute_isogeny_starks(E, E2, 2)
3557
x
3558
3559
"""
3560
3561
K = E1.base_field()
3562
R = PolynomialRing(K, 'x')
3563
x = R.gen()
3564
3565
wp1 = E1.weierstrass_p(prec=4*ell+4) #BMSS claim 2*ell is enough, but it is not M09
3566
wp2 = E2.weierstrass_p(prec=4*ell+4)
3567
3568
# viewed them as power series in Z = z^2
3569
S = LaurentSeriesRing(K, 'Z')
3570
Z = S.gen()
3571
pe1 = 1/Z
3572
pe2 = 1/Z
3573
for i in xrange(2*ell+1):
3574
pe1 += wp1[2*i] * Z**i
3575
pe2 += wp2[2*i] * Z**i
3576
pe1 = pe1.add_bigoh(2*ell+2)
3577
pe2 = pe2.add_bigoh(2*ell+2)
3578
3579
#print 'wps = ',pe1
3580
#print 'wps2 = ',pe2
3581
3582
n = 1
3583
q = [R(1), R(0)]
3584
#p = [R(0), R(1)]
3585
T = pe2
3586
3587
while ( q[n].degree() < (ell-1) ):
3588
#print 'n=', n
3589
3590
n += 1
3591
a_n = 0
3592
r = -T.valuation()
3593
while (0 <= r):
3594
t_r = T[-r]
3595
#print ' r=',r
3596
#print ' t_r=',t_r
3597
#print ' T=',T
3598
a_n = a_n + t_r * x**r
3599
T = T - t_r*pe1**r
3600
r = -T.valuation()
3601
3602
3603
q_n = a_n*q[n-1] + q[n-2]
3604
q.append(q_n)
3605
#p_n = a_n*p[n-1] + q[n-2]
3606
#p.append(p_n)
3607
3608
if (n == ell+1 or T == 0):
3609
if (T == 0 or T.valuation()<2):
3610
raise ValueError("The two curves are not linked by a cyclic normalized isogeny of degree %s" % ell)
3611
#print 'breaks here'
3612
break
3613
3614
T = 1/T
3615
#print ' a_n=', a_n
3616
#print ' q_n=', q_n
3617
#print ' p_n=', p_n
3618
#print ' T = ', T
3619
3620
qn = q[n]
3621
#pn= p[n]
3622
#print 'final T = ', T
3623
#print ' f =', pn/qn
3624
3625
qn = (1/qn.leading_coefficient())*qn
3626
#pn = (1/qn.leading_coefficient())*pn
3627
3628
return qn
3629
3630
def split_kernel_polynomial(E1, ker_poly, ell):
3631
r"""
3632
Internal helper function for ``compute_isogeny_kernel_polynomial``.
3633
3634
Given a full kernel polynomial (where two torsion `x`-coordinates
3635
are roots of multiplicity 1, and all other roots have multiplicity
3636
2.) of degree `\ell-1`, returns the maximum separable divisor.
3637
(i.e. the kernel polynomial with roots of multiplicity at most 1).
3638
3639
EXAMPLES:
3640
3641
The following example implicitly exercises this function::
3642
3643
sage: E = EllipticCurve(GF(37), [0,0,0,1,8])
3644
sage: R.<x> = GF(37)[]
3645
sage: f = (x + 10) * (x + 12) * (x + 16)
3646
sage: phi = EllipticCurveIsogeny(E, f)
3647
sage: E2 = phi.codomain()
3648
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_starks
3649
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import split_kernel_polynomial
3650
sage: ker_poly = compute_isogeny_starks(E, E2, 7); ker_poly
3651
x^6 + 2*x^5 + 20*x^4 + 11*x^3 + 36*x^2 + 35*x + 16
3652
sage: split_kernel_polynomial(E, ker_poly, 7)
3653
x^3 + x^2 + 28*x + 33
3654
3655
"""
3656
3657
poly_ring = ker_poly.parent()
3658
3659
z = poly_ring.gen(0)
3660
3661
ker_poly_2tor = two_torsion_part(E1, poly_ring, ker_poly, ell)
3662
ker_poly_quo = poly_ring(ker_poly/ker_poly_2tor)
3663
ker_poly_quo_sqrt = ker_poly_quo.gcd(ker_poly_quo.derivative(z))
3664
ker_poly = ker_poly_2tor*ker_poly_quo_sqrt
3665
ker_poly = (1/ker_poly.leading_coefficient())*ker_poly
3666
3667
return ker_poly
3668
3669
3670
def compute_isogeny_kernel_polynomial(E1, E2, ell, algorithm="starks"):
3671
r"""
3672
Computes the kernel polynomial of the degree ``ell`` isogeny
3673
between ``E1`` and ``E2``. There must be a degree ``ell``,
3674
cyclic, separable, normalized isogeny from ``E1`` to ``E2``.
3675
3676
INPUT:
3677
3678
- ``E1`` - an elliptic curve in short Weierstrass form.
3679
3680
- ``E2`` - an elliptic curve in short Weierstrass form.
3681
3682
- ``ell`` - the degree of the isogeny from ``E1`` to ``E2``.
3683
3684
- ``algorithm`` - currently only ``starks`` (default) is implemented.
3685
3686
OUTPUT:
3687
3688
polynomial -- over the field of definition of ``E1``, ``E2``, that is the
3689
kernel polynomial of the isogeny from ``E1`` to ``E2``.
3690
3691
EXAMPLES::
3692
3693
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_kernel_polynomial
3694
3695
sage: E = EllipticCurve(GF(37), [0,0,0,1,8])
3696
sage: R.<x> = GF(37)[]
3697
sage: f = (x + 14) * (x + 30)
3698
sage: phi = EllipticCurveIsogeny(E, f)
3699
sage: E2 = phi.codomain()
3700
sage: compute_isogeny_kernel_polynomial(E, E2, 5)
3701
x^2 + 7*x + 13
3702
sage: f
3703
x^2 + 7*x + 13
3704
3705
sage: R.<x> = QQ[]
3706
sage: K.<i> = NumberField(x^2 + 1)
3707
sage: E = EllipticCurve(K, [0,0,0,1,0])
3708
sage: E2 = EllipticCurve(K, [0,0,0,16,0])
3709
sage: compute_isogeny_kernel_polynomial(E, E2, 4)
3710
x^3 + x
3711
3712
"""
3713
3714
ker_poly = compute_isogeny_starks(E1, E2, ell)
3715
ker_poly = split_kernel_polynomial(E1, ker_poly, ell)
3716
3717
return ker_poly
3718
3719
3720
def compute_intermediate_curves(E1, E2):
3721
r"""
3722
Computes isomorphism from ``E1`` to an intermediate domain and an
3723
isomorphism from an intermediate codomain to ``E2``.
3724
3725
Intermediate domain and intermediate codomain, are in short
3726
Weierstrass form.
3727
3728
This is used so we can compute `\wp` functions from the short
3729
Weierstrass model more easily.
3730
3731
The underlying field must be of characteristic not equal to 2,3.
3732
3733
INPUT:
3734
3735
- ``E1`` - an elliptic curve
3736
- ``E2`` - an elliptic curve
3737
3738
OUTPUT:
3739
3740
tuple -- (``pre_isomorphism``, ``post_isomorphism``, ``intermediate_domain``,
3741
``intermediate_codomain``):
3742
3743
- ``intermediate_domain``: a short Weierstrass model isomorphic to ``E1``
3744
- ``intermediate_codomain``: a short Weierstrass model isomorphic to ``E2``
3745
- ``pre_isomorphism``: normalized isomorphism from ``E1`` to intermediate_domain
3746
- ``post_isomorphism``: normalized isomorphism from intermediate_codomain to ``E2``
3747
3748
EXAMPLES::
3749
3750
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_intermediate_curves
3751
sage: E = EllipticCurve(GF(83), [1,0,1,1,0])
3752
sage: R.<x> = GF(83)[]; f = x+24
3753
sage: phi = EllipticCurveIsogeny(E, f)
3754
sage: E2 = phi.codomain()
3755
sage: compute_intermediate_curves(E, E2)
3756
(Elliptic Curve defined by y^2 = x^3 + 62*x + 74 over Finite Field of size 83,
3757
Elliptic Curve defined by y^2 = x^3 + 65*x + 69 over Finite Field of size 83,
3758
Generic morphism:
3759
From: Abelian group of points on Elliptic Curve defined by y^2 + x*y + y = x^3 + x over Finite Field of size 83
3760
To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 62*x + 74 over Finite Field of size 83
3761
Via: (u,r,s,t) = (1, 76, 41, 3),
3762
Generic morphism:
3763
From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 65*x + 69 over Finite Field of size 83
3764
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
3765
Via: (u,r,s,t) = (1, 7, 42, 80))
3766
3767
sage: R.<x> = QQ[]
3768
sage: K.<i> = NumberField(x^2 + 1)
3769
sage: E = EllipticCurve(K, [0,0,0,1,0])
3770
sage: E2 = EllipticCurve(K, [0,0,0,16,0])
3771
sage: compute_intermediate_curves(E, E2)
3772
(Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1,
3773
Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1,
3774
Generic morphism:
3775
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
3776
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
3777
Via: (u,r,s,t) = (1, 0, 0, 0),
3778
Generic morphism:
3779
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
3780
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
3781
Via: (u,r,s,t) = (1, 0, 0, 0))
3782
3783
"""
3784
3785
if (E1.base_ring().characteristic() in [2,3]):
3786
raise NotImplemented
3787
3788
# compute the r,s,t values that clear the denominator of E1
3789
a1 = E1.a1()
3790
a2 = E1.a2()
3791
a3 = E1.a3()
3792
3793
s1 = -a1/2
3794
r1 = (s1**2 + s1*a1 - a2)/3
3795
t1 = (-r1*a1 - a3)/2
3796
3797
# compute the isomorphism from E1 to intermediate_domain
3798
pre_isom = WeierstrassIsomorphism(E1, (1, r1, s1, t1))
3799
3800
intermediate_domain = pre_isom.codomain().codomain()
3801
3802
# compute the r,s,t values that clear the denominator of E2
3803
a1pr = E2.a1()
3804
a2pr = E2.a2()
3805
a3pr = E2.a3()
3806
3807
s2 = -a1pr/2
3808
r2 = (s2**2 + s2*a1pr - a2pr)/3
3809
t2 = (-r2*a1pr - a3pr)/2
3810
3811
post_isom_inv = WeierstrassIsomorphism(E2, (1, r2, s2, t2))
3812
intermediate_codomain = post_isom_inv.codomain().codomain()
3813
3814
post_isom = WeierstrassIsomorphism(intermediate_codomain, (1, -r2, -s2, -t2))
3815
3816
return (intermediate_domain, intermediate_codomain, pre_isom, post_isom)
3817
3818
3819
def compute_sequence_of_maps(E1, E2, ell):
3820
r"""
3821
Given domain ``E1`` and codomain ``E2`` such that there is a
3822
degree ``ell`` separable normalized isogeny from ``E1`` to ``E2``,
3823
returns pre/post isomorphism, as well as intermediate domain and
3824
codomain, and kernel polynomial.
3825
3826
EXAMPLES::
3827
3828
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_sequence_of_maps
3829
sage: E = EllipticCurve('11a1')
3830
sage: R.<x> = QQ[]; f = x^2 - 21*x + 80
3831
sage: phi = EllipticCurveIsogeny(E, f)
3832
sage: E2 = phi.codomain()
3833
sage: compute_sequence_of_maps(E, E2, 5)
3834
(Generic morphism:
3835
From: Abelian group of points on Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field
3836
To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 - 31/3*x - 2501/108 over Rational Field
3837
Via: (u,r,s,t) = (1, 1/3, 0, -1/2),
3838
Generic morphism:
3839
From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 - 23461/3*x - 28748141/108 over Rational Field
3840
To: Abelian group of points on Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field
3841
Via: (u,r,s,t) = (1, -1/3, 0, 1/2),
3842
Elliptic Curve defined by y^2 = x^3 - 31/3*x - 2501/108 over Rational Field,
3843
Elliptic Curve defined by y^2 = x^3 - 23461/3*x - 28748141/108 over Rational Field,
3844
x^2 - 61/3*x + 658/9)
3845
3846
sage: K.<i> = NumberField(x^2 + 1)
3847
sage: E = EllipticCurve(K, [0,0,0,1,0])
3848
sage: E2 = EllipticCurve(K, [0,0,0,16,0])
3849
sage: compute_sequence_of_maps(E, E2, 4)
3850
(Generic morphism:
3851
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
3852
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
3853
Via: (u,r,s,t) = (1, 0, 0, 0),
3854
Generic morphism:
3855
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
3856
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
3857
Via: (u,r,s,t) = (1, 0, 0, 0),
3858
Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1,
3859
Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1,
3860
x^3 + x)
3861
3862
sage: E = EllipticCurve(GF(97), [1,0,1,1,0])
3863
sage: R.<x> = GF(97)[]; f = x^5 + 27*x^4 + 61*x^3 + 58*x^2 + 28*x + 21
3864
sage: phi = EllipticCurveIsogeny(E, f)
3865
sage: E2 = phi.codomain()
3866
sage: compute_sequence_of_maps(E, E2, 11)
3867
(Generic morphism:
3868
From: Abelian group of points on Elliptic Curve defined by y^2 + x*y + y = x^3 + x over Finite Field of size 97
3869
To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 52*x + 31 over Finite Field of size 97
3870
Via: (u,r,s,t) = (1, 8, 48, 44),
3871
Generic morphism:
3872
From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 41*x + 66 over Finite Field of size 97
3873
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
3874
Via: (u,r,s,t) = (1, 89, 49, 53),
3875
Elliptic Curve defined by y^2 = x^3 + 52*x + 31 over Finite Field of size 97,
3876
Elliptic Curve defined by y^2 = x^3 + 41*x + 66 over Finite Field of size 97,
3877
x^5 + 67*x^4 + 13*x^3 + 35*x^2 + 77*x + 69)
3878
3879
"""
3880
3881
(E1pr, E2pr, pre_isom, post_isom) = compute_intermediate_curves(E1, E2)
3882
3883
ker_poly = compute_isogeny_kernel_polynomial(E1pr, E2pr, ell)
3884
3885
return (pre_isom, post_isom, E1pr, E2pr, ker_poly)
3886
3887
3888
# Utility function for manipulating isogeny degree matrices
3889
3890
def fill_isogeny_matrix(M):
3891
"""
3892
Returns a filled isogeny matrix giving all degrees from one giving only prime degrees.
3893
3894
INPUT:
3895
3896
- ``M`` -- a square symmetric matrix whose off-diagonal `i`, `j`
3897
entry is either a prime `l` (if the `i`'th and `j`'th curves
3898
have an `l`-isogeny between them), otherwise is 0.
3899
3900
OUTPUT:
3901
3902
(matrix) a square matrix with entries `1` on the diagonal, and in
3903
general the `i`, `j` entry is `d>0` if `d` is the minimal degree
3904
of an isogeny from the `i`'th to the `j`'th curve,
3905
3906
EXAMPLES::
3907
3908
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
3909
[0 2 3 3 0 0]
3910
[2 0 0 0 3 3]
3911
[3 0 0 0 2 0]
3912
[3 0 0 0 0 2]
3913
[0 3 2 0 0 0]
3914
[0 3 0 2 0 0]
3915
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix
3916
sage: fill_isogeny_matrix(M)
3917
[ 1 2 3 3 6 6]
3918
[ 2 1 6 6 3 3]
3919
[ 3 6 1 9 2 18]
3920
[ 3 6 9 1 18 2]
3921
[ 6 3 2 18 1 9]
3922
[ 6 3 18 2 9 1]
3923
"""
3924
from sage.matrix.all import Matrix
3925
from sage.rings.infinity import Infinity
3926
3927
n = M.nrows()
3928
M0 = copy(M)
3929
for i in range(n):
3930
M0[i,i]=1
3931
3932
def fix(d):
3933
if d==0: return Infinity
3934
return d
3935
3936
def fix2(d):
3937
if d==Infinity: return 0
3938
return d
3939
3940
def pr(M1,M2):
3941
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)])
3942
3943
M1 = M0
3944
M2 = pr(M0,M1)
3945
while M1!=M2:
3946
M1 = M2
3947
M2 = pr(M0,M1)
3948
3949
return M1
3950
3951
def unfill_isogeny_matrix(M):
3952
"""
3953
Reverses the action of ``fill_isogeny_matrix``.
3954
3955
INPUT:
3956
3957
- ``M`` -- a square symmetric matrix of integers.
3958
3959
OUTPUT:
3960
3961
(matrix) a square symmetric matrix obtained from ``M`` by
3962
replacing non-prime entries with `0`.
3963
3964
EXAMPLES::
3965
3966
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
3967
[0 2 3 3 0 0]
3968
[2 0 0 0 3 3]
3969
[3 0 0 0 2 0]
3970
[3 0 0 0 0 2]
3971
[0 3 2 0 0 0]
3972
[0 3 0 2 0 0]
3973
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix, unfill_isogeny_matrix
3974
sage: M1 = fill_isogeny_matrix(M); M1
3975
[ 1 2 3 3 6 6]
3976
[ 2 1 6 6 3 3]
3977
[ 3 6 1 9 2 18]
3978
[ 3 6 9 1 18 2]
3979
[ 6 3 2 18 1 9]
3980
[ 6 3 18 2 9 1]
3981
sage: unfill_isogeny_matrix(M1)
3982
[0 2 3 3 0 0]
3983
[2 0 0 0 3 3]
3984
[3 0 0 0 2 0]
3985
[3 0 0 0 0 2]
3986
[0 3 2 0 0 0]
3987
[0 3 0 2 0 0]
3988
sage: unfill_isogeny_matrix(M1) == M
3989
True
3990
"""
3991
from sage.matrix.all import Matrix
3992
from sage.rings.infinity import Infinity
3993
3994
n = M.nrows()
3995
M1 = copy(M)
3996
zero = Integer(0)
3997
for i in range(n):
3998
M1[i,i] = zero
3999
for j in range(i):
4000
if not M1[i,j].is_prime():
4001
M1[i,j] = zero
4002
M1[j,i] = zero
4003
return M1
4004
4005