Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/schemes/elliptic_curves/heegner.py
8821 views
1
# -*- coding: utf-8 -*-
2
r"""
3
Heegner points on elliptic curves over the rational numbers
4
5
AUTHORS:
6
7
- William Stein (August 2009)-- most of the initial version
8
9
- Robert Bradshaw (July 2009) -- an early version of some specific code
10
11
EXAMPLES::
12
13
sage: E = EllipticCurve('433a')
14
sage: P = E.heegner_point(-8,3)
15
sage: z = P.point_exact(201); z
16
(-4/3 : 1/27*a - 4/27 : 1)
17
sage: parent(z)
18
Abelian group of points on Elliptic Curve defined by y^2 + x*y = x^3 + 1 over Number Field in a with defining polynomial x^2 - 44*x + 1159
19
sage: parent(z[0]).discriminant()
20
-3
21
sage: E.quadratic_twist(-3).rank()
22
1
23
sage: K.<a> = QuadraticField(-8)
24
sage: K.factor(3)
25
(Fractional ideal (1/2*a + 1)) * (Fractional ideal (1/2*a - 1))
26
27
Next try an inert prime::
28
29
sage: K.factor(5)
30
Fractional ideal (5)
31
sage: P = E.heegner_point(-8,5)
32
sage: z = P.point_exact(300)
33
sage: z[0].charpoly().factor()
34
(x^6 + x^5 - 1/4*x^4 + 19/10*x^3 + 31/20*x^2 - 7/10*x + 49/100)^2
35
sage: z[1].charpoly().factor()
36
x^12 - x^11 + 6/5*x^10 - 33/40*x^9 - 89/320*x^8 + 3287/800*x^7 - 5273/1600*x^6 + 993/4000*x^5 + 823/320*x^4 - 2424/625*x^3 + 12059/12500*x^2 + 3329/25000*x + 123251/250000
37
sage: f = P.x_poly_exact(300); f
38
x^6 + x^5 - 1/4*x^4 + 19/10*x^3 + 31/20*x^2 - 7/10*x + 49/100
39
sage: f.discriminant().factor()
40
-1 * 2^-9 * 5^-9 * 7^2 * 281^2 * 1021^2
41
42
We find some Mordell-Weil generators in the rank 1 case using Heegner points::
43
44
sage: E = EllipticCurve('43a'); P = E.heegner_point(-7)
45
sage: P.x_poly_exact()
46
x
47
sage: P.point_exact()
48
(0 : 0 : 1)
49
50
sage: E = EllipticCurve('997a')
51
sage: E.rank()
52
1
53
sage: E.heegner_discriminants_list(10)
54
[-19, -23, -31, -35, -39, -40, -52, -55, -56, -59]
55
sage: P = E.heegner_point(-19)
56
sage: P.x_poly_exact()
57
x - 141/49
58
sage: P.point_exact()
59
(141/49 : -162/343 : 1)
60
61
Here we find that the Heegner point generates a subgroup of index 3::
62
63
sage: E = EllipticCurve('92b1')
64
sage: E.heegner_discriminants_list(1)
65
[-7]
66
sage: P = E.heegner_point(-7); z = P.point_exact(); z
67
(0 : 1 : 1)
68
sage: E.regulator()
69
0.0498083972980648
70
sage: z.height()
71
0.448275575682583
72
sage: P = E(1,1); P # a generator
73
(1 : 1 : 1)
74
sage: -3*P
75
(0 : 1 : 1)
76
sage: E.tamagawa_product()
77
3
78
79
The above is consistent with the following analytic computation::
80
81
sage: E.heegner_index(-7)
82
3.0000?
83
"""
84
85
##############################################################################
86
# Copyright (C) 2005-2009 William Stein <[email protected]>
87
#
88
# Distributed under the terms of the GNU General Public License (GPL)
89
#
90
# This code is distributed in the hope that it will be useful,
91
# but WITHOUT ANY WARRANTY; without even the implied warranty of
92
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
93
# General Public License for more details.
94
#
95
# The full text of the GPL is available at:
96
#
97
# http://www.gnu.org/licenses/
98
##############################################################################
99
100
101
from sage.misc.all import verbose, prod
102
from sage.misc.cachefunc import cached_method
103
104
from sage.structure.sage_object import SageObject
105
106
import sage.rings.number_field.number_field as number_field
107
import sage.rings.arith as arith
108
import sage.rings.all as rings
109
from sage.rings.all import (ZZ, GF, QQ, CDF,
110
Integers, RealField, ComplexField, QuadraticField,
111
gcd, lcm, is_fundamental_discriminant)
112
from sage.quadratic_forms.all import (BinaryQF,
113
BinaryQF_reduced_representatives)
114
from sage.matrix.all import MatrixSpace, matrix
115
116
from sage.modular.modsym.p1list import P1List
117
118
119
##################################################################################
120
#
121
# The exported functions, which are in most cases enough to get the
122
# user going working with Heegner points:
123
#
124
# heegner_points -- all of them with given level, discriminant, conducto
125
# heegner_point -- a specific one
126
#
127
##################################################################################
128
129
def heegner_points(N, D=None, c=None):
130
"""
131
Return all Heegner points of given level `N`. Can also restrict
132
to Heegner points with specified discriminant `D` and optionally
133
conductor `c`.
134
135
INPUT:
136
137
- `N` -- level (positive integer)
138
139
- `D` -- discriminant (negative integer)
140
141
- `c` -- conductor (positive integer)
142
143
EXAMPLES::
144
145
sage: heegner_points(389,-7)
146
Set of all Heegner points on X_0(389) associated to QQ[sqrt(-7)]
147
sage: heegner_points(389,-7,1)
148
All Heegner points of conductor 1 on X_0(389) associated to QQ[sqrt(-7)]
149
sage: heegner_points(389,-7,5)
150
All Heegner points of conductor 5 on X_0(389) associated to QQ[sqrt(-7)]
151
"""
152
if D is None and c is None:
153
return HeegnerPoints_level(N)
154
if D is not None and c is None:
155
return HeegnerPoints_level_disc(N, D)
156
if D is not None and c is not None:
157
return HeegnerPoints_level_disc_cond(N,D,c)
158
raise TypeError
159
160
def heegner_point(N, D=None, c=1):
161
"""
162
Return a specific Heegner point of level `N` with given
163
discriminant and conductor. If `D` is not specified, then the
164
first valid Heegner discriminant is used. If `c` is not given,
165
then `c=1` is used.
166
167
INPUT:
168
169
- `N` -- level (positive integer)
170
171
- `D` -- discriminant (optional: default first valid `D`)
172
173
- `c` -- conductor (positive integer, optional, default: 1)
174
175
EXAMPLES::
176
177
sage: heegner_point(389)
178
Heegner point 1/778*sqrt(-7) - 185/778 of discriminant -7 on X_0(389)
179
sage: heegner_point(389,-7)
180
Heegner point 1/778*sqrt(-7) - 185/778 of discriminant -7 on X_0(389)
181
sage: heegner_point(389,-7,5)
182
Heegner point 5/778*sqrt(-7) - 147/778 of discriminant -7 and conductor 5 on X_0(389)
183
sage: heegner_point(389,-20)
184
Heegner point 1/778*sqrt(-20) - 165/389 of discriminant -20 on X_0(389)
185
"""
186
if D is not None:
187
return heegner_points(N,D,c)[0]
188
H = heegner_points(N)
189
D = H.discriminants(1)[0]
190
return heegner_points(N,D,c)[0]
191
192
193
##################################################################################
194
#
195
# Ring class fields, represented as abstract objects. These do not
196
# derive from number fields, since we do not need to work with their
197
# elements, and explicitly representing them as number fields would be
198
# far too difficult.
199
#
200
##################################################################################
201
202
class RingClassField(SageObject):
203
"""
204
A Ring class field of a quadratic imaginary field of given conductor.
205
206
.. NOTE::
207
208
This is a *ring* class field, not a ray class field. In
209
general, the ring class field of given conductor is a subfield
210
of the ray class field of the same conductor.
211
212
EXAMPLES::
213
214
sage: heegner_point(37,-7).ring_class_field()
215
Hilbert class field of QQ[sqrt(-7)]
216
sage: heegner_point(37,-7,5).ring_class_field()
217
Ring class field extension of QQ[sqrt(-7)] of conductor 5
218
sage: heegner_point(37,-7,55).ring_class_field()
219
Ring class field extension of QQ[sqrt(-7)] of conductor 55
220
221
TESTS::
222
223
sage: K_c = heegner_point(37,-7).ring_class_field()
224
sage: type(K_c)
225
<class 'sage.schemes.elliptic_curves.heegner.RingClassField'>
226
sage: loads(dumps(K_c)) == K_c
227
True
228
"""
229
def __init__(self, D, c, check=True):
230
"""
231
INPUTS:
232
233
- `D` -- discriminant of quadratic imaginary field
234
235
- `c` -- conductor (positive integer coprime to `D`)
236
237
- ``check`` -- bool (default: ``True``); whether to check
238
validity of input
239
240
EXAMPLES::
241
242
sage: sage.schemes.elliptic_curves.heegner.RingClassField(-7,5, False)
243
Ring class field extension of QQ[sqrt(-7)] of conductor 5
244
245
"""
246
if check:
247
D = ZZ(D); c = ZZ(c)
248
self.__D = D
249
self.__c = c
250
251
def __eq__(self, other):
252
"""
253
Used for equality testing.
254
255
EXAMPLES::
256
257
sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()
258
sage: K11 = E.heegner_point(-7,11).ring_class_field()
259
sage: K5 == K11
260
False
261
sage: K5 == K5
262
True
263
sage: K11 == 11
264
False
265
"""
266
return isinstance(other, RingClassField) and self.__D == other.__D and self.__c == other.__c
267
268
def __hash__(self):
269
"""
270
Used for computing hash of ``self``.
271
272
EXAMPLES::
273
274
sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()
275
sage: hash(K5)
276
-3713088127102618519 # 64-bit
277
1817441385 # 32-bit
278
sage: hash((-7,5))
279
-3713088127102618519 # 64-bit
280
1817441385 # 32-bit
281
"""
282
return hash((self.__D, self.__c))
283
284
def conductor(self):
285
"""
286
Return the conductor of this ring class field.
287
288
EXAMPLES::
289
290
sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()
291
sage: K5.conductor()
292
5
293
"""
294
return self.__c
295
296
def discriminant_of_K(self):
297
"""
298
Return the discriminant of the quadratic imaginary field `K` contained in ``self``.
299
300
EXAMPLES::
301
302
sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()
303
sage: K5.discriminant_of_K()
304
-7
305
"""
306
return self.__D
307
308
@cached_method
309
def ramified_primes(self):
310
r"""
311
Return the primes of `\ZZ` that ramify in this ring class field.
312
313
EXAMPLES::
314
315
sage: E = EllipticCurve('389a'); K55 = E.heegner_point(-7,55).ring_class_field()
316
sage: K55.ramified_primes()
317
[5, 7, 11]
318
sage: E.heegner_point(-7).ring_class_field().ramified_primes()
319
[7]
320
"""
321
return arith.prime_divisors(self.__D * self.__c)
322
323
def _repr_(self):
324
"""
325
EXAMPLES::
326
327
sage: heegner_point(37,-7,55).ring_class_field()._repr_()
328
'Ring class field extension of QQ[sqrt(-7)] of conductor 55'
329
sage: heegner_point(37,-7).ring_class_field()._repr_()
330
'Hilbert class field of QQ[sqrt(-7)]'
331
"""
332
c = self.__c
333
if c == 1:
334
return "Hilbert class field of QQ[sqrt(%s)]"%self.__D
335
else:
336
return "Ring class field extension of QQ[sqrt(%s)] of conductor %s"%(self.__D, self.__c)
337
338
@cached_method
339
def degree_over_K(self):
340
"""
341
Return the relative degree of this ring class field over the
342
quadratic imaginary field `K`.
343
344
EXAMPLES::
345
346
sage: E = EllipticCurve('389a'); P = E.heegner_point(-7,5)
347
sage: K5 = P.ring_class_field(); K5
348
Ring class field extension of QQ[sqrt(-7)] of conductor 5
349
sage: K5.degree_over_K()
350
6
351
sage: type(K5.degree_over_K())
352
<type 'sage.rings.integer.Integer'>
353
354
sage: E = EllipticCurve('389a'); E.heegner_point(-20).ring_class_field().degree_over_K()
355
2
356
sage: E.heegner_point(-20,3).ring_class_field().degree_over_K()
357
4
358
sage: kronecker(-20,11)
359
-1
360
sage: E.heegner_point(-20,11).ring_class_field().degree_over_K()
361
24
362
"""
363
D, c = self.__D, self.__c
364
365
K = self.quadratic_field()
366
367
# Multiply class number by relative degree of the Hilbert class field H over K.
368
return K.class_number() * self.degree_over_H()
369
370
@cached_method
371
def degree_over_H(self):
372
"""
373
Return the degree of this field over the Hilbert class field `H` of `K`.
374
375
EXAMPLES::
376
377
sage: E = EllipticCurve('389a')
378
sage: E.heegner_point(-59).ring_class_field().degree_over_H()
379
1
380
sage: E.heegner_point(-59).ring_class_field().degree_over_K()
381
3
382
sage: QuadraticField(-59,'a').class_number()
383
3
384
385
Some examples in which prime dividing c is inert::
386
387
sage: heegner_point(37,-7,3).ring_class_field().degree_over_H()
388
4
389
sage: heegner_point(37,-7,3^2).ring_class_field().degree_over_H()
390
12
391
sage: heegner_point(37,-7,3^3).ring_class_field().degree_over_H()
392
36
393
394
The prime dividing c is split. For example, in the first case
395
`O_K/cO_K` is isomorphic to a direct sum of two copies of
396
``GF(2)``, so the units are trivial::
397
398
sage: heegner_point(37,-7,2).ring_class_field().degree_over_H()
399
1
400
sage: heegner_point(37,-7,4).ring_class_field().degree_over_H()
401
2
402
sage: heegner_point(37,-7,8).ring_class_field().degree_over_H()
403
4
404
405
Now c is ramified::
406
407
sage: heegner_point(37,-7,7).ring_class_field().degree_over_H()
408
7
409
sage: heegner_point(37,-7,7^2).ring_class_field().degree_over_H()
410
49
411
"""
412
c = self.__c
413
if c == 1:
414
return ZZ(1)
415
416
# Let K_c be the ring class field. We have by class field theory that
417
# Gal(K_c / H) = (O_K/c*O_K)^* / (Z/cZ)^*.
418
#
419
# To compute the cardinality of the above Galois group, we
420
# first reduce to the case that c = p^e is a prime power
421
# (since the expression is multiplicative in c).
422
# Of course, note also that #(Z/cZ)^* = phi(c)
423
#
424
# Case 1: p splits in O_K. Then
425
# #(O_K/p^e*O_K)^* = (#(Z/p^eZ)^*)^2 = phi(p^e)^2, so
426
# #(O_K/p^e*O_K)^*/(Z/p^eZ)^* = phi(p^e) = p^e - p^(e-1)
427
#
428
# Case 2: p is inert in O_K. Then
429
# #(O_K/p^e O_K)^* = p^(2*e)-p^(2*(e-1))
430
# so #(O_K/p^e*O_K)^*/(Z/p^eZ)^*
431
# = (p^(2*e)-p^(2*(e-1)))/(p^e-p^(e-1)) = p^e + p^(e-1).
432
#
433
# Case 3: p ramified in O_K. Then
434
# #(O_K/p^e O_K)^* = p^(2*e) - p^(2*e-1),
435
# so #(O_K/p^e O_K)^*/#(Z/p^eZ)^* = p^e.
436
#
437
# Section 4.2 of Cohen's "Advanced Computational Algebraic
438
# Number Theory" GTM is also relevant, though Cohen is working
439
# with *ray* class fields and here we want the cardinality
440
# of the *ring* class field, which is a subfield.
441
442
K = self.quadratic_field()
443
444
n = ZZ(1)
445
for p, e in c.factor():
446
F = K.factor(p)
447
if len(F) == 2:
448
# split case
449
n *= p**e - p**(e-1)
450
else:
451
if F[0][1] > 1:
452
# ramified case
453
n *= p**e
454
else:
455
# inert case
456
n *= p**e + p**(e-1)
457
return n
458
459
@cached_method
460
def absolute_degree(self):
461
r"""
462
Return the absolute degree of this field over `\QQ`.
463
464
EXAMPLES::
465
466
sage: E = EllipticCurve('389a'); K = E.heegner_point(-7,5).ring_class_field()
467
sage: K.absolute_degree()
468
12
469
sage: K.degree_over_K()
470
6
471
"""
472
return 2*self.degree_over_K()
473
474
degree_over_Q = absolute_degree
475
476
@cached_method
477
def quadratic_field(self):
478
r"""
479
Return the quadratic imaginary field `K = \QQ(\sqrt{D})`.
480
481
EXAMPLES::
482
483
sage: E = EllipticCurve('389a'); K = E.heegner_point(-7,5).ring_class_field()
484
sage: K.quadratic_field()
485
Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
486
"""
487
D = self.__D
488
var = 'sqrt_minus_%s'%(-D)
489
return number_field.QuadraticField(D,var)
490
491
@cached_method
492
def galois_group(self, base=QQ):
493
"""
494
Return the Galois group of ``self`` over base.
495
496
INPUT:
497
498
- ``base`` -- (default: `\QQ`) a subfield of ``self`` or `\QQ`
499
500
EXAMPLES::
501
502
sage: E = EllipticCurve('389a')
503
sage: A = E.heegner_point(-7,5).ring_class_field()
504
sage: A.galois_group()
505
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5
506
sage: B = E.heegner_point(-7).ring_class_field()
507
sage: C = E.heegner_point(-7,15).ring_class_field()
508
sage: A.galois_group()
509
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5
510
sage: A.galois_group(B)
511
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Hilbert class field of QQ[sqrt(-7)]
512
sage: A.galois_group().cardinality()
513
12
514
sage: A.galois_group(B).cardinality()
515
6
516
sage: C.galois_group(A)
517
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 15 over Ring class field extension of QQ[sqrt(-7)] of conductor 5
518
sage: C.galois_group(A).cardinality()
519
4
520
521
"""
522
return GaloisGroup(self, base)
523
524
def is_subfield(self, M):
525
"""
526
Return ``True`` if this ring class field is a subfield of the ring class field `M`.
527
If `M` is not a ring class field, then a TypeError is raised.
528
529
EXAMPLES::
530
531
sage: E = EllipticCurve('389a')
532
sage: A = E.heegner_point(-7,5).ring_class_field()
533
sage: B = E.heegner_point(-7).ring_class_field()
534
sage: C = E.heegner_point(-20).ring_class_field()
535
sage: D = E.heegner_point(-7,15).ring_class_field()
536
sage: B.is_subfield(A)
537
True
538
sage: B.is_subfield(B)
539
True
540
sage: B.is_subfield(D)
541
True
542
sage: B.is_subfield(C)
543
False
544
sage: A.is_subfield(B)
545
False
546
sage: A.is_subfield(D)
547
True
548
"""
549
if not isinstance(M, RingClassField):
550
raise TypeError("M must be a ring class field")
551
return self.quadratic_field() == M.quadratic_field() and \
552
M.conductor() % self.conductor() == 0
553
554
##################################################################################
555
#
556
# Galois groups of ring class fields
557
#
558
##################################################################################
559
560
class GaloisGroup(SageObject):
561
"""
562
A Galois group of a ring class field.
563
564
EXAMPLES::
565
566
sage: E = EllipticCurve('389a')
567
sage: G = E.heegner_point(-7,5).ring_class_field().galois_group(); G
568
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5
569
sage: G.field()
570
Ring class field extension of QQ[sqrt(-7)] of conductor 5
571
sage: G.cardinality()
572
12
573
sage: G.complex_conjugation()
574
Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 5
575
576
TESTS::
577
578
sage: G = heegner_point(37,-7).ring_class_field().galois_group()
579
sage: loads(dumps(G)) == G
580
True
581
sage: type(G)
582
<class 'sage.schemes.elliptic_curves.heegner.GaloisGroup'>
583
"""
584
def __init__(self, field, base=QQ):
585
r"""
586
INPUT:
587
588
- ``field`` -- a ring class field
589
590
- ``base`` -- subfield of field (default: `\QQ`)
591
592
EXAMPLES::
593
594
sage: K5 = heegner_points(389,-7,5).ring_class_field()
595
sage: K1 = heegner_points(389,-7,1).ring_class_field()
596
sage: sage.schemes.elliptic_curves.heegner.GaloisGroup(K5,K1)
597
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Hilbert class field of QQ[sqrt(-7)]
598
sage: K5.galois_group(K1)
599
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Hilbert class field of QQ[sqrt(-7)]
600
"""
601
if not isinstance(field, RingClassField):
602
raise TypeError("field must be of type RingClassField")
603
if base != QQ and base != field.quadratic_field():
604
if not isinstance(base, RingClassField):
605
raise TypeError("base must be of type RingClassField or QQ or quadratic field")
606
if not base.is_subfield(field):
607
raise TypeError("base must be a subfield of field")
608
self.__field = field
609
self.__base = base
610
611
def __eq__(self, G):
612
"""
613
EXAMPLES::
614
615
sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()
616
sage: G == G
617
True
618
sage: G == 0
619
False
620
sage: H = EllipticCurve('389a').heegner_point(-7,11).ring_class_field().galois_group()
621
sage: G == H
622
False
623
"""
624
return isinstance(G, GaloisGroup) and (G.__field,G.__base) == (self.__field,self.__base)
625
626
def __hash__(self):
627
"""
628
Return hash of this Galois group, which is the same as the
629
hash of the pair, the field and its base.
630
631
EXAMPLES::
632
633
sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()
634
sage: hash(G)
635
-6198252699510613726 # 64-bit
636
1905285410 # 32-bit
637
sage: hash((G.field(), G.base_field()))
638
-6198252699510613726 # 64-bit
639
1905285410 # 32-bit
640
"""
641
return hash((self.__field, self.__base))
642
643
def __call__(self, x):
644
"""
645
Coerce `x` into ``self``, where `x` is a Galois group element, or
646
in case ``self`` has base field the Hilbert class field, `x` can
647
also be an element of the ring of integers.
648
649
INPUT:
650
651
- `x` -- automorphism or quadratic field element
652
653
OUTPUT:
654
655
- automorphism (or TypeError)
656
657
EXAMPLES::
658
659
sage: K5 = heegner_points(389,-52,5).ring_class_field()
660
sage: K1 = heegner_points(389,-52,1).ring_class_field()
661
sage: G = K5.galois_group(K1)
662
sage: G(1)
663
Class field automorphism defined by x^2 + 325*y^2
664
sage: G(G[0])
665
Class field automorphism defined by x^2 + 325*y^2
666
sage: alpha = 2 + K1.quadratic_field().gen(); alpha
667
sqrt_minus_52 + 2
668
sage: G(alpha)
669
Class field automorphism defined by 14*x^2 - 10*x*y + 25*y^2
670
671
A TypeError is raised when the coercion is not possible::
672
673
sage: G(0)
674
Traceback (most recent call last):
675
...
676
TypeError: x does not define element of (O_K/c*O_K)^*
677
678
"""
679
if isinstance(x, GaloisAutomorphism) and x.parent() == self:
680
return x
681
try:
682
return self._alpha_to_automorphism(x)
683
except (ZeroDivisionError, TypeError):
684
raise TypeError("x does not define element of (O_K/c*O_K)^*")
685
686
def _repr_(self):
687
"""
688
Return string representation of this Galois group.
689
690
EXAMPLES::
691
692
sage: E = EllipticCurve('389a')
693
sage: G = E.heegner_point(-7,5).ring_class_field().galois_group()
694
sage: G._repr_()
695
'Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5'
696
"""
697
if self.base_field() != QQ:
698
s = " over %s"%self.base_field()
699
else:
700
s = ''
701
return "Galois group of %s%s"%(self.field(), s)
702
703
def field(self):
704
"""
705
Return the ring class field that this Galois group acts on.
706
707
EXAMPLES::
708
709
sage: G = heegner_point(389,-7,5).ring_class_field().galois_group()
710
sage: G.field()
711
Ring class field extension of QQ[sqrt(-7)] of conductor 5
712
"""
713
return self.__field
714
715
def base_field(self):
716
"""
717
Return the base field, which the field fixed by all the
718
automorphisms in this Galois group.
719
720
EXAMPLES::
721
722
sage: x = heegner_point(37,-7,5)
723
sage: Kc = x.ring_class_field(); Kc
724
Ring class field extension of QQ[sqrt(-7)] of conductor 5
725
sage: K = x.quadratic_field()
726
sage: G = Kc.galois_group(); G
727
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5
728
sage: G.base_field()
729
Rational Field
730
sage: G.cardinality()
731
12
732
sage: Kc.absolute_degree()
733
12
734
sage: G = Kc.galois_group(K); G
735
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
736
sage: G.cardinality()
737
6
738
sage: G.base_field()
739
Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
740
sage: G = Kc.galois_group(Kc); G
741
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Ring class field extension of QQ[sqrt(-7)] of conductor 5
742
sage: G.cardinality()
743
1
744
sage: G.base_field()
745
Ring class field extension of QQ[sqrt(-7)] of conductor 5
746
"""
747
return self.__base
748
749
@cached_method
750
def kolyvagin_generators(self):
751
r"""
752
Assuming this Galois group `G` is of the form
753
`G=\textrm{Gal}(K_c/K_1)`, with `c=p_1\dots p_n` satisfying the
754
Kolyvagin hypothesis, this function returns noncanonical
755
choices of lifts of generators for each of the cyclic factors
756
of `G` corresponding to the primes dividing `c`. Thus the
757
`i`-th returned valued is an element of `G` that maps to the
758
identity element of `\textrm{Gal}(K_p/K_1)` for all `p \neq p_i` and
759
to a choice of generator of `\textrm{Gal}(K_{p_i}/K_1)`.
760
761
OUTPUT:
762
763
- list of elements of ``self``
764
765
EXAMPLES::
766
767
sage: K3 = heegner_points(389,-52,3).ring_class_field()
768
sage: K1 = heegner_points(389,-52,1).ring_class_field()
769
sage: G = K3.galois_group(K1)
770
sage: G.kolyvagin_generators()
771
(Class field automorphism defined by 9*x^2 - 6*x*y + 14*y^2,)
772
773
sage: K5 = heegner_points(389,-52,5).ring_class_field()
774
sage: K1 = heegner_points(389,-52,1).ring_class_field()
775
sage: G = K5.galois_group(K1)
776
sage: G.kolyvagin_generators()
777
(Class field automorphism defined by 17*x^2 - 14*x*y + 22*y^2,)
778
"""
779
M = self.field()
780
c = M.conductor()
781
if not (self._base_is_hilbert_class_field() and self.is_kolyvagin()):
782
raise ValueError("field must be of the form Gal(K_c/K_1)")
783
if not c.is_prime():
784
raise NotImplementedError("only implemented when c is prime")
785
786
# Since c satisfies Kolyvagin and is prime, the group is cyclic,
787
# so we just find a generator.
788
for sigma in self:
789
if sigma.order() == self.cardinality():
790
return tuple([sigma])
791
792
raise NotImplementedError
793
794
@cached_method
795
def lift_of_hilbert_class_field_galois_group(self):
796
r"""
797
Assuming this Galois group `G` is of the form `G=\textrm{Gal}(K_c/K)`,
798
this function returns noncanonical choices of lifts of the
799
elements of the quotient group `\textrm{Gal}(K_1/K)`.
800
801
OUTPUT:
802
803
- tuple of elements of self
804
805
EXAMPLES::
806
807
sage: K5 = heegner_points(389,-52,5).ring_class_field()
808
sage: G = K5.galois_group(K5.quadratic_field())
809
sage: G.lift_of_hilbert_class_field_galois_group()
810
(Class field automorphism defined by x^2 + 325*y^2, Class field automorphism defined by 2*x^2 + 2*x*y + 163*y^2)
811
sage: G.cardinality()
812
12
813
sage: K5.quadratic_field().class_number()
814
2
815
"""
816
if not self._base_is_quad_imag_field():
817
raise ValueError("Galois group must be of the form Gal(K_c/K)")
818
K = self.base_field()
819
C = K.class_group()
820
v = []
821
lifts = []
822
for sigma in self:
823
I = sigma.ideal()
824
g = C(I)
825
if g not in v:
826
v.append(g)
827
lifts.append(sigma)
828
return tuple(lifts)
829
830
@cached_method
831
def _list(self):
832
r"""
833
Enumerate the elements of ``self``.
834
835
EXAMPLES::
836
837
Example with order 1 (a special case)::
838
839
sage: E = EllipticCurve('389a'); F= E.heegner_point(-7,1).ring_class_field()
840
sage: G = F.galois_group(F.quadratic_field())
841
sage: G._list()
842
(Class field automorphism defined by x^2 + x*y + 2*y^2,)
843
844
Example over quadratic imaginary field::
845
846
sage: E = EllipticCurve('389a'); F= E.heegner_point(-7,5).ring_class_field()
847
sage: G = F.galois_group(F.quadratic_field())
848
sage: G._list()
849
(Class field automorphism defined by x^2 + x*y + 44*y^2, Class field automorphism defined by 2*x^2 - x*y + 22*y^2, Class field automorphism defined by 2*x^2 + x*y + 22*y^2, Class field automorphism defined by 4*x^2 - x*y + 11*y^2, Class field automorphism defined by 4*x^2 + x*y + 11*y^2, Class field automorphism defined by 7*x^2 + 7*x*y + 8*y^2)
850
851
Example over `\QQ` (it is not implemented yet)::
852
853
sage: K3 = heegner_points(389,-52,3).ring_class_field()
854
sage: K3.galois_group()._list()
855
Traceback (most recent call last):
856
...
857
NotImplementedError: Galois group over QQ not yet implemented
858
859
Example over Hilbert class field::
860
861
sage: K3 = heegner_points(389,-52,3).ring_class_field(); K1 = heegner_points(389,-52,1).ring_class_field()
862
sage: G = K3.galois_group(K1)
863
sage: G._list()
864
(Class field automorphism defined by x^2 + 117*y^2, Class field automorphism defined by 9*x^2 - 6*x*y + 14*y^2, Class field automorphism defined by 9*x^2 + 13*y^2, Class field automorphism defined by 9*x^2 + 6*x*y + 14*y^2)
865
"""
866
if self._base_is_QQ():
867
raise NotImplementedError("Galois group over QQ not yet implemented")
868
elif self._base_is_quad_imag_field():
869
# Over the quadratic imaginary field, so straightforward
870
# enumeration of all reduced primitive binary quadratic
871
# forms of discriminant D*c^2.
872
D = self.base_field().discriminant()
873
c = self.field().conductor()
874
Q = [f for f in BinaryQF_reduced_representatives(D*c*c) if f.is_primitive()]
875
v = [GaloisAutomorphismQuadraticForm(self, f) for f in Q]
876
877
elif self._base_is_hilbert_class_field() and self.is_kolyvagin():
878
# Take only the automorphisms in the quad imag case that map to
879
# a principal ideal.
880
M = self.field()
881
K = M.quadratic_field()
882
v = []
883
self.__p1_to_automorphism = {}
884
for sigma in M.galois_group(K)._list():
885
I = sigma.ideal()
886
if I.is_principal():
887
# sigma does define an element of our Galois subgroup.
888
alpha = sigma.ideal().gens_reduced()[0]
889
t = GaloisAutomorphismQuadraticForm(self, sigma.quadratic_form(), alpha=alpha)
890
self.__p1_to_automorphism[t.p1_element()] = t
891
v.append(t)
892
else:
893
raise NotImplementedError("general Galois group not yet implemented")
894
895
v.sort()
896
assert len(v) == self.cardinality(), "bug enumerating Galois group elements"
897
return tuple(v)
898
899
def _quadratic_form_to_alpha(self, f):
900
"""
901
INPUT:
902
903
- `f` -- a binary quadratic form with discriminant `c^2 D`
904
905
OUTPUT:
906
907
- an element of the ring of integers of the quadratic
908
imaginary field
909
910
EXAMPLES::
911
912
sage: K3 = heegner_points(389,-52,3).ring_class_field(); K1 = heegner_points(389,-52,1).ring_class_field()
913
sage: G = K3.galois_group(K1)
914
sage: [G._quadratic_form_to_alpha(s.quadratic_form()) for s in G]
915
[3/2*sqrt_minus_52, 1/6*sqrt_minus_52 + 1/3, 1/6*sqrt_minus_52, 1/6*sqrt_minus_52 - 1/3]
916
917
What happens when we input a quadratic form that has nothing
918
to do with `G`::
919
920
sage: G._quadratic_form_to_alpha(BinaryQF([1,2,3]))
921
Traceback (most recent call last):
922
...
923
ValueError: quadratic form has the wrong discriminant
924
"""
925
A,B,C = f
926
K = self.field().quadratic_field()
927
if f.discriminant() != self.field().conductor()**2 * K.discriminant():
928
raise ValueError("quadratic form has the wrong discriminant")
929
930
R = K['X']
931
v = R([C,B,A]).roots()[0][0]
932
return v
933
934
def _alpha_to_automorphism(self, alpha):
935
r"""
936
Assuming ``self`` has base field the Hilbert class field, make an
937
automorphism from the element `\alpha` of the ring of integers
938
into ``self``.
939
940
INPUT:
941
942
- `\alpha` -- element of quadratic imaginary field coprime to conductor
943
944
EXAMPLES::
945
946
sage: K3 = heegner_points(389,-52,3).ring_class_field()
947
sage: K1 = heegner_points(389,-52,1).ring_class_field()
948
sage: G = K3.galois_group(K1)
949
sage: G._alpha_to_automorphism(1)
950
Class field automorphism defined by x^2 + 117*y^2
951
sage: [G._alpha_to_automorphism(s.alpha()) for s in G] == list(G)
952
True
953
"""
954
if not self._base_is_hilbert_class_field() and self.is_kolyvagin():
955
raise TypeError("base must be Hilbert class field with Kolyvagin condition on conductor")
956
R = self.field().quadratic_field().maximal_order()
957
uv = self._alpha_to_p1_element(R(alpha))
958
try:
959
d = self.__p1_to_automorphism
960
except AttributeError:
961
self._list() # computes attribute as side-effect
962
d = self.__p1_to_automorphism
963
return d[uv]
964
965
966
def _alpha_to_p1_element(self, alpha):
967
r"""
968
Given an element of the ring of integers that is nonzero
969
modulo c, return canonical (after our fixed choice of basis)
970
element of the project line corresponding to it.
971
972
INPUT:
973
974
- `\alpha` -- element of the ring of integers of the
975
quadratic imaginary field
976
977
OUTPUT:
978
979
- 2-tuple of integers
980
981
EXAMPLES::
982
983
sage: K3 = heegner_points(389,-52,3).ring_class_field()
984
sage: K1 = heegner_points(389,-52,1).ring_class_field()
985
sage: G = K3.galois_group(K1)
986
sage: G._alpha_to_p1_element(1)
987
(1, 0)
988
sage: sorted([G._alpha_to_p1_element(s.alpha()) for s in G])
989
[(0, 1), (1, 0), (1, 1), (1, 2)]
990
"""
991
try:
992
A, P1 = self.__alpha_to_p1_element
993
except AttributeError:
994
# todo (optimize) -- this whole function can be massively optimized:
995
M = self.field()
996
A = M.quadratic_field().maximal_order().free_module()
997
P1 = P1List(M.conductor())
998
self.__alpha_to_p1_element = A, P1
999
alpha = self.field().quadratic_field()(alpha)
1000
w = A.coordinate_vector(alpha.vector())
1001
w *= w.denominator()
1002
w = w.change_ring(ZZ)
1003
n = arith.gcd(w)
1004
w /= n
1005
c = P1.N()
1006
w = P1.normalize(ZZ(w[0])%c, ZZ(w[1])%c)
1007
if w == (0,0):
1008
w = (1,0)
1009
return w
1010
1011
def _p1_element_to_alpha(self, uv):
1012
"""
1013
Convert a normalized pair ``uv=(u,v)`` of integers to the
1014
corresponding element of the ring of integers got by taking `u
1015
b_0 + v b_1` where `b_0, b_1` are the basis for the ring of
1016
integers.
1017
1018
INPUT:
1019
1020
- ``uv`` -- pair of integers
1021
1022
OUTPUT:
1023
1024
- element of maximal order of quadratic field
1025
1026
EXAMPLES::
1027
1028
sage: K5 = heegner_points(389,-52,5).ring_class_field()
1029
sage: K1 = heegner_points(389,-52,1).ring_class_field()
1030
sage: G = K5.galois_group(K1)
1031
sage: v = [G._alpha_to_p1_element(s.alpha()) for s in G]
1032
sage: [G._p1_element_to_alpha(z) for z in v]
1033
[1, 1/2*sqrt_minus_52, 1/2*sqrt_minus_52 + 1, 2*sqrt_minus_52 + 1, sqrt_minus_52 + 1, 3/2*sqrt_minus_52 + 1]
1034
sage: [G(G._p1_element_to_alpha(z)) for z in v] == list(G)
1035
True
1036
"""
1037
B = self.field().quadratic_field().maximal_order().basis()
1038
return uv[0]*B[0] + uv[1]*B[1]
1039
1040
1041
def _base_is_QQ(self):
1042
r"""
1043
Return ``True`` if the base field of this ring class field is `\QQ`.
1044
1045
EXAMPLES::
1046
1047
sage: H = heegner_points(389,-20,3); M = H.ring_class_field()
1048
sage: M.galois_group(H.quadratic_field())._base_is_QQ()
1049
False
1050
sage: M.galois_group(QQ)._base_is_QQ()
1051
True
1052
sage: M.galois_group(heegner_points(389,-20,1).ring_class_field())._base_is_QQ()
1053
False
1054
"""
1055
return self.__base == QQ
1056
1057
def _base_is_quad_imag_field(self):
1058
"""
1059
Return ``True`` if the base field of this ring class field is the
1060
quadratic imaginary field `K`.
1061
1062
EXAMPLES::
1063
1064
sage: H = heegner_points(389,-20,3); M = H.ring_class_field()
1065
sage: M.galois_group(H.quadratic_field())._base_is_quad_imag_field()
1066
True
1067
sage: M.galois_group(QQ)._base_is_quad_imag_field()
1068
False
1069
sage: M.galois_group(heegner_points(389,-20,1).ring_class_field())._base_is_quad_imag_field()
1070
False
1071
"""
1072
return number_field.is_QuadraticField(self.__base)
1073
1074
def is_kolyvagin(self):
1075
"""
1076
Return ``True`` if conductor `c` is prime to the discriminant of the
1077
quadratic field, `c` is squarefree and each prime dividing `c`
1078
is inert.
1079
1080
EXAMPLES::
1081
1082
sage: K5 = heegner_points(389,-52,5).ring_class_field()
1083
sage: K1 = heegner_points(389,-52,1).ring_class_field()
1084
sage: K5.galois_group(K1).is_kolyvagin()
1085
True
1086
sage: K7 = heegner_points(389,-52,7).ring_class_field()
1087
sage: K7.galois_group(K1).is_kolyvagin()
1088
False
1089
sage: K25 = heegner_points(389,-52,25).ring_class_field()
1090
sage: K25.galois_group(K1).is_kolyvagin()
1091
False
1092
"""
1093
M = self.field()
1094
c = M.conductor()
1095
D = M.quadratic_field().discriminant()
1096
if c.gcd(D) != 1: return False
1097
if not c.is_squarefree(): return False
1098
for p in c.prime_divisors():
1099
if not is_inert(D,p):
1100
return False
1101
return True
1102
1103
def _base_is_hilbert_class_field(self):
1104
"""
1105
Return ``True`` if the base field of this ring class field is the
1106
Hilbert class field of `K` viewed as a ring class field (so
1107
not of data type QuadraticField).
1108
1109
EXAMPLES::
1110
1111
sage: H = heegner_points(389,-20,3); M = H.ring_class_field()
1112
sage: M.galois_group(H.quadratic_field())._base_is_hilbert_class_field()
1113
False
1114
sage: M.galois_group(QQ)._base_is_hilbert_class_field()
1115
False
1116
sage: M.galois_group(heegner_points(389,-20,1).ring_class_field())._base_is_hilbert_class_field()
1117
True
1118
"""
1119
M = self.__base
1120
return isinstance(M, RingClassField) and M.conductor() == 1
1121
1122
1123
def __getitem__(self, i):
1124
"""
1125
EXAMPLES::
1126
1127
sage: E = EllipticCurve('389a'); F= E.heegner_point(-7,5).ring_class_field()
1128
sage: G = F.galois_group(F.quadratic_field())
1129
sage: G[0]
1130
Class field automorphism defined by x^2 + x*y + 44*y^2
1131
"""
1132
return self._list()[i]
1133
1134
1135
def __len__(self):
1136
"""
1137
EXAMPLES::
1138
1139
sage: K5 = heegner_points(389,-52,5).ring_class_field()
1140
sage: K1 = heegner_points(389,-52,1).ring_class_field()
1141
sage: G = K5.galois_group(K1)
1142
sage: G.cardinality()
1143
6
1144
sage: len(G)
1145
6
1146
"""
1147
return self.cardinality()
1148
1149
@cached_method
1150
def cardinality(self):
1151
"""
1152
Return the cardinality of this Galois group.
1153
1154
EXAMPLES::
1155
1156
sage: E = EllipticCurve('389a')
1157
sage: G = E.heegner_point(-7,5).ring_class_field().galois_group(); G
1158
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5
1159
sage: G.cardinality()
1160
12
1161
sage: G = E.heegner_point(-7).ring_class_field().galois_group()
1162
sage: G.cardinality()
1163
2
1164
sage: G = E.heegner_point(-7,55).ring_class_field().galois_group()
1165
sage: G.cardinality()
1166
120
1167
"""
1168
return self.__field.absolute_degree() // self.__base.absolute_degree()
1169
1170
@cached_method
1171
def complex_conjugation(self):
1172
"""
1173
Return the automorphism of ``self`` determined by complex
1174
conjugation. The base field must be the rational numbers.
1175
1176
EXAMPLES::
1177
1178
sage: E = EllipticCurve('389a')
1179
sage: G = E.heegner_point(-7,5).ring_class_field().galois_group()
1180
sage: G.complex_conjugation()
1181
Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 5
1182
"""
1183
if self.base_field() != QQ:
1184
raise ValueError("the base field must be fixed by complex conjugation")
1185
return GaloisAutomorphismComplexConjugation(self)
1186
1187
1188
##################################################################################
1189
#
1190
# Elements of Galois groups
1191
#
1192
##################################################################################
1193
1194
class GaloisAutomorphism(SageObject):
1195
"""
1196
An abstract automorphism of a ring class field.
1197
1198
.. TODO::
1199
1200
make :class:`GaloisAutomorphism` derive from GroupElement, so
1201
that one gets powers for free, etc.
1202
"""
1203
def __init__(self, parent):
1204
"""
1205
INPUT:
1206
1207
- ``parent`` -- a group of automorphisms of a ring class field
1208
1209
EXAMPLES::
1210
1211
sage: G = heegner_points(389,-7,5).ring_class_field().galois_group(); G
1212
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5
1213
sage: sage.schemes.elliptic_curves.heegner.GaloisAutomorphism(G)
1214
<class 'sage.schemes.elliptic_curves.heegner.GaloisAutomorphism'>
1215
"""
1216
self.__parent = parent
1217
1218
def parent(self):
1219
"""
1220
Return the parent of this automorphism, which is a Galois
1221
group of a ring class field.
1222
1223
EXAMPLES::
1224
1225
sage: E = EllipticCurve('389a')
1226
sage: s = E.heegner_point(-7,5).ring_class_field().galois_group().complex_conjugation()
1227
sage: s.parent()
1228
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5
1229
"""
1230
return self.__parent
1231
1232
def domain(self):
1233
"""
1234
Return the domain of this automorphism.
1235
1236
EXAMPLES::
1237
1238
sage: E = EllipticCurve('389a')
1239
sage: s = E.heegner_point(-7,5).ring_class_field().galois_group().complex_conjugation()
1240
sage: s.domain()
1241
Ring class field extension of QQ[sqrt(-7)] of conductor 5
1242
"""
1243
return self.parent().field()
1244
1245
class GaloisAutomorphismComplexConjugation(GaloisAutomorphism):
1246
"""
1247
The complex conjugation automorphism of a ring class field.
1248
1249
EXAMPLES::
1250
1251
sage: conj = heegner_point(37,-7,5).ring_class_field().galois_group().complex_conjugation()
1252
sage: conj
1253
Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 5
1254
sage: conj.domain()
1255
Ring class field extension of QQ[sqrt(-7)] of conductor 5
1256
1257
TESTS::
1258
1259
sage: type(conj)
1260
<class 'sage.schemes.elliptic_curves.heegner.GaloisAutomorphismComplexConjugation'>
1261
sage: loads(dumps(conj)) == conj
1262
True
1263
"""
1264
def __init__(self, parent):
1265
"""
1266
INPUT:
1267
1268
- ``parent`` -- a group of automorphisms of a ring class field
1269
1270
EXAMPLES::
1271
1272
sage: G = heegner_point(37,-7,5).ring_class_field().galois_group()
1273
sage: sage.schemes.elliptic_curves.heegner.GaloisAutomorphismComplexConjugation(G)
1274
Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 5
1275
"""
1276
GaloisAutomorphism.__init__(self, parent)
1277
1278
def __hash__(self):
1279
"""
1280
EXAMPLES::
1281
1282
sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()
1283
sage: conj = G.complex_conjugation(); hash(conj)
1284
1347197483068745902 # 64-bit
1285
480045230 # 32-bit
1286
"""
1287
return hash((self.parent(), 1))
1288
1289
def __eq__(self, right):
1290
"""
1291
EXAMPLES::
1292
1293
sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()
1294
sage: conj = G.complex_conjugation()
1295
sage: conj2 = sage.schemes.elliptic_curves.heegner.GaloisAutomorphismComplexConjugation(G)
1296
sage: conj is conj2
1297
False
1298
sage: conj == conj2
1299
True
1300
"""
1301
return isinstance(right, GaloisAutomorphismComplexConjugation) and \
1302
self.parent() == right.parent()
1303
1304
def _repr_(self):
1305
"""
1306
Return print representation of the complex conjugation automorphism.
1307
1308
EXAMPLES::
1309
1310
sage: conj = heegner_point(37,-7,5).ring_class_field().galois_group().complex_conjugation()
1311
sage: conj._repr_()
1312
'Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 5'
1313
"""
1314
return "Complex conjugation automorphism of %s"%self.domain()
1315
1316
## def __mul__(self, right):
1317
## """
1318
## Return the composition of two automorphisms.
1319
1320
## EXAMPLES::
1321
1322
## sage: ?
1323
## """
1324
## if self.parent() != right.__parent():
1325
## raise TypeError, "automorphisms must be of the same class field"
1326
## raise NotImplementedError
1327
1328
def __invert__(self):
1329
"""
1330
Return the inverse of ``self``, which is just ``self`` again.
1331
1332
EXAMPLES::
1333
1334
sage: conj = heegner_point(37,-7,5).ring_class_field().galois_group().complex_conjugation()
1335
sage: ~conj
1336
Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 5
1337
"""
1338
return self
1339
1340
def order(self):
1341
"""
1342
EXAMPLES::
1343
1344
sage: conj = heegner_point(37,-7,5).ring_class_field().galois_group().complex_conjugation()
1345
sage: conj.order()
1346
2
1347
"""
1348
return ZZ(2)
1349
1350
class GaloisAutomorphismQuadraticForm(GaloisAutomorphism):
1351
"""
1352
An automorphism of a ring class field defined by a quadratic form.
1353
1354
EXAMPLES::
1355
1356
sage: H = heegner_points(389,-20,3)
1357
sage: sigma = H.ring_class_field().galois_group(H.quadratic_field())[0]; sigma
1358
Class field automorphism defined by x^2 + 45*y^2
1359
sage: type(sigma)
1360
<class 'sage.schemes.elliptic_curves.heegner.GaloisAutomorphismQuadraticForm'>
1361
sage: loads(dumps(sigma)) == sigma
1362
True
1363
"""
1364
def __init__(self, parent, quadratic_form, alpha=None):
1365
r"""
1366
INPUT:
1367
1368
- ``parent`` -- a group of automorphisms of a ring class field
1369
1370
- ``quadratic_form`` -- a binary quadratic form that
1371
defines an element of the Galois group of `K_c` over `K`.
1372
1373
- ``\alpha`` -- (default: ``None``) optional data that specified
1374
element corresponding element of `(\mathcal{O}_K /
1375
c\mathcal{O}_K)^* / (\ZZ/c\ZZ)^*`, via class field
1376
theory.
1377
1378
EXAMPLES::
1379
1380
sage: H = heegner_points(389,-20,3); G = H.ring_class_field().galois_group(H.quadratic_field())
1381
sage: f = BinaryQF_reduced_representatives(-20*9)[0]
1382
sage: sage.schemes.elliptic_curves.heegner.GaloisAutomorphismQuadraticForm(G, f)
1383
Class field automorphism defined by x^2 + 45*y^2
1384
"""
1385
self.__quadratic_form = quadratic_form.reduced_form()
1386
self.__alpha = alpha
1387
GaloisAutomorphism.__init__(self, parent)
1388
1389
@cached_method
1390
def order(self):
1391
"""
1392
Return the multiplicative order of this Galois group automorphism.
1393
1394
EXAMPLES::
1395
1396
sage: K3 = heegner_points(389,-52,3).ring_class_field()
1397
sage: K1 = heegner_points(389,-52,1).ring_class_field()
1398
sage: G = K3.galois_group(K1)
1399
sage: sorted([g.order() for g in G])
1400
[1, 2, 4, 4]
1401
sage: K5 = heegner_points(389,-52,5).ring_class_field()
1402
sage: K1 = heegner_points(389,-52,1).ring_class_field()
1403
sage: G = K5.galois_group(K1)
1404
sage: sorted([g.order() for g in G])
1405
[1, 2, 3, 3, 6, 6]
1406
"""
1407
alpha = self.__alpha
1408
if alpha is None:
1409
raise NotImplementedError("order only currently implemented when alpha given in construction")
1410
G = self.parent()
1411
one = G(1).p1_element()
1412
ans = ZZ(1)
1413
z = alpha
1414
for i in range(G.cardinality()):
1415
if G._alpha_to_p1_element(z) == one:
1416
return ans
1417
ans += 1
1418
z *= alpha
1419
assert False, "bug in order"
1420
1421
def alpha(self):
1422
r"""
1423
Optional data that specified element corresponding element of
1424
`(\mathcal{O}_K / c\mathcal{O}_K)^* / (\ZZ/c\ZZ)^*`, via class
1425
field theory.
1426
1427
This is a generator of the ideal corresponding to this
1428
automorphism.
1429
1430
EXAMPLES::
1431
1432
sage: K3 = heegner_points(389,-52,3).ring_class_field()
1433
sage: K1 = heegner_points(389,-52,1).ring_class_field()
1434
sage: G = K3.galois_group(K1)
1435
sage: orb = sorted([g.alpha() for g in G]); orb # random (the sign depends on the database being installed or not)
1436
[1, 1/2*sqrt_minus_52 + 1, -1/2*sqrt_minus_52, 1/2*sqrt_minus_52 - 1]
1437
sage: sorted([x^2 for x in orb]) # this is just for testing
1438
[-13, -sqrt_minus_52 - 12, sqrt_minus_52 - 12, 1]
1439
1440
sage: K5 = heegner_points(389,-52,5).ring_class_field()
1441
sage: K1 = heegner_points(389,-52,1).ring_class_field()
1442
sage: G = K5.galois_group(K1)
1443
sage: orb = sorted([g.alpha() for g in G]); orb # random (the sign depends on the database being installed or not)
1444
[1, -1/2*sqrt_minus_52, 1/2*sqrt_minus_52 + 1, 1/2*sqrt_minus_52 - 1, 1/2*sqrt_minus_52 - 2, -1/2*sqrt_minus_52 - 2]
1445
sage: sorted([x^2 for x in orb]) # just for testing
1446
[-13, -sqrt_minus_52 - 12, sqrt_minus_52 - 12, -2*sqrt_minus_52 - 9, 2*sqrt_minus_52 - 9, 1]
1447
1448
"""
1449
if self.__alpha is None:
1450
raise ValueError("alpha data not defined")
1451
return self.__alpha
1452
1453
@cached_method
1454
def p1_element(self):
1455
r"""
1456
Return element of the projective line corresponding to this
1457
automorphism.
1458
1459
This only makes sense if this automorphism is in the Galois
1460
group `\textrm{Gal}(K_c/K_1)`.
1461
1462
EXAMPLES::
1463
1464
sage: K3 = heegner_points(389,-52,3).ring_class_field()
1465
sage: K1 = heegner_points(389,-52,1).ring_class_field()
1466
sage: G = K3.galois_group(K1)
1467
sage: sorted([g.p1_element() for g in G])
1468
[(0, 1), (1, 0), (1, 1), (1, 2)]
1469
1470
sage: K5 = heegner_points(389,-52,5).ring_class_field()
1471
sage: K1 = heegner_points(389,-52,1).ring_class_field()
1472
sage: G = K5.galois_group(K1)
1473
sage: sorted([g.p1_element() for g in G])
1474
[(0, 1), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4)]
1475
"""
1476
return self.parent()._alpha_to_p1_element(self.__alpha)
1477
1478
def __hash__(self):
1479
"""
1480
EXAMPLES::
1481
1482
sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]
1483
sage: hash(s)
1484
4262582128197601113 # 64-bit
1485
-1994029223 # 32-bit
1486
"""
1487
return hash((self.parent(), tuple(self.__quadratic_form)))
1488
1489
def __eq__(self, right):
1490
"""
1491
EXAMPLES::
1492
1493
sage: H = heegner_points(389,-7,5); s = H.ring_class_field().galois_group(H.quadratic_field())[1]
1494
sage: s == s
1495
True
1496
sage: s == s*s
1497
False
1498
sage: s == s*s*s*s*s
1499
False
1500
sage: s == s*s*s*s*s*s*s
1501
True
1502
"""
1503
return isinstance(right, GaloisAutomorphismQuadraticForm) and \
1504
self.parent() == right.parent() and \
1505
self.quadratic_form().is_equivalent(right.quadratic_form())
1506
1507
def __cmp__(self, right):
1508
"""
1509
Compare ``self`` and right. Used mainly so that lists of
1510
automorphisms are sorted consistently between runs.
1511
1512
EXAMPLES::
1513
1514
sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]
1515
sage: s.__cmp__(s)
1516
0
1517
sage: s.__cmp__(0) != 0
1518
True
1519
"""
1520
if not isinstance(right, GaloisAutomorphismQuadraticForm):
1521
return cmp(type(self), type(right))
1522
c = cmp(self.parent(), right.parent())
1523
if c: return c
1524
if self.quadratic_form().is_equivalent(right.quadratic_form()):
1525
return 0
1526
return cmp(self.quadratic_form(), right.quadratic_form())
1527
1528
def _repr_(self):
1529
"""
1530
Return string representation of this automorphism.
1531
1532
EXAMPLES::
1533
1534
sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]
1535
sage: s._repr_()
1536
'Class field automorphism defined by x^2 + 45*y^2'
1537
1538
"""
1539
return "Class field automorphism defined by %s"%self.__quadratic_form
1540
1541
def __mul__(self, right):
1542
"""
1543
Return the composition of two automorphisms.
1544
1545
EXAMPLES::
1546
1547
sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]
1548
sage: s * s
1549
Class field automorphism defined by x^2 + 45*y^2
1550
sage: G = s.parent(); list(G)
1551
[Class field automorphism defined by x^2 + 45*y^2, Class field automorphism defined by 2*x^2 + 2*x*y + 23*y^2, Class field automorphism defined by 5*x^2 + 9*y^2, Class field automorphism defined by 7*x^2 + 4*x*y + 7*y^2]
1552
sage: G[0]*G[0]
1553
Class field automorphism defined by x^2 + 45*y^2
1554
sage: G[1]*G[2] == G[3]
1555
True
1556
"""
1557
if self.parent() != right.parent():
1558
raise TypeError("automorphisms must be of the same class field")
1559
if not isinstance(right, GaloisAutomorphismQuadraticForm):
1560
# TODO: special case when right is complex conjugation
1561
raise NotImplementedError
1562
Q = (self.__quadratic_form * right.__quadratic_form).reduced_form()
1563
if self.__alpha and right.__alpha:
1564
alpha = self.__alpha * right.__alpha
1565
else:
1566
alpha = None
1567
return GaloisAutomorphismQuadraticForm(self.parent(), Q, alpha=alpha)
1568
1569
def quadratic_form(self):
1570
"""
1571
Return reduced quadratic form corresponding to this Galois
1572
automorphism.
1573
1574
1575
EXAMPLES::
1576
1577
sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]
1578
sage: s.quadratic_form()
1579
x^2 + 45*y^2
1580
"""
1581
return self.__quadratic_form
1582
1583
@cached_method
1584
def ideal(self):
1585
r"""
1586
Return ideal of ring of integers of quadratic imaginary field
1587
corresponding to this quadratic form. This is the ideal
1588
1589
`I = \left(A, \frac{-B+ c\sqrt{D}}{2}\right) \mathcal{O}_K`.
1590
1591
EXAMPLES::
1592
1593
sage: E = EllipticCurve('389a'); F= E.heegner_point(-20,3).ring_class_field()
1594
sage: G = F.galois_group(F.quadratic_field())
1595
sage: G[1].ideal()
1596
Fractional ideal (2, 1/2*sqrt_minus_20 + 1)
1597
sage: [s.ideal().gens() for s in G]
1598
[(1, 3/2*sqrt_minus_20), (2, 3/2*sqrt_minus_20 - 1), (5, 3/2*sqrt_minus_20), (7, 3/2*sqrt_minus_20 - 2)]
1599
"""
1600
M = self.parent().field()
1601
K = M.quadratic_field()
1602
f = self.quadratic_form()
1603
c = M.conductor()
1604
sqrtD = K.gen()
1605
(A,B,C) = f
1606
if A%c == 0:
1607
A, C = C, A
1608
return K.maximal_order().ideal([A, (-B+c*sqrtD)/2])
1609
1610
## def __call__(self, z):
1611
## """
1612
## Return image of the Heegner point `z` under this automorphism.
1613
##
1614
## INPUT:
1615
##
1616
## - `z` -- a Heegner point on `X_0(N)` or an elliptic curve
1617
##
1618
## OUTPUT:
1619
##
1620
## - a Heegner point
1621
##
1622
## EXAMPLES::
1623
##
1624
## sage: x = heegner_point(389,-20,3); F = x.ring_class_field()
1625
## sage: sigma = F.galois_group(F.quadratic_field())[1]; sigma
1626
## Class field automorphism defined by 2*x^2 + 2*x*y + 23*y^2
1627
## sage: sigma(x)
1628
## Heegner point 3/1556*sqrt(-20) - 495/778 of discriminant -20 and conductor 3 on X_0(389)
1629
## """
1630
## if isinstance(z, HeegnerPointOnX0N):
1631
## if z.ring_class_field() != self.domain():
1632
## raise NotImplementedError, "class fields must be the same"
1633
## # TODO -- check more compatibilities?
1634
## # TODO -- this is surely backwards -- something must be inverted?
1635
## f = z.quadratic_form() * self.quadratic_form()
1636
## # TODO -- put f into the correct form with A divisible by N, etc.?
1637
## # That could be done by looking up reduced form of f in a canonical
1638
## # list of best reps.
1639
## N,D,c = z.level(),z.discriminant(),z.conductor()
1640
## return HeegnerPointOnX0N(N,D,c, f = f)
1641
## else:
1642
## raise NotImplementedError
1643
1644
##################################################################################
1645
#
1646
# Specific Heegner points
1647
#
1648
##################################################################################
1649
1650
1651
class HeegnerPoint(SageObject):
1652
r"""
1653
A Heegner point of level `N`, discriminant `D` and conductor `c`
1654
is any point on a modular curve or elliptic curve that is
1655
concocted in some way from a quadratic imaginary `\tau` in the upper
1656
half plane with `\Delta(\tau) = D c = \Delta(N \tau)`.
1657
1658
EXAMPLES::
1659
1660
sage: x = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,13); x
1661
Heegner point of level 389, discriminant -7, and conductor 13
1662
sage: type(x)
1663
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>
1664
sage: loads(dumps(x)) == x
1665
True
1666
"""
1667
def __init__(self, N, D, c):
1668
"""
1669
INPUT:
1670
1671
- `N` -- (positive integer) the level
1672
1673
- `D` -- (negative integer) fundamental discriminant
1674
1675
- `c` -- (positive integer) conductor
1676
1677
Since this is an abstract base class, no type or compatibility
1678
checks are done, as those are all assumed to be done in the
1679
derived class.
1680
1681
EXAMPLES::
1682
1683
sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5)
1684
sage: type(H)
1685
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>
1686
"""
1687
self.__N = N
1688
self.__D = D
1689
self.__c = c
1690
1691
def __cmp__(self, x):
1692
"""
1693
Compare two Heegner points.
1694
1695
EXAMPLES::
1696
1697
sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5)
1698
sage: H.__cmp__(H)
1699
0
1700
"""
1701
if not isinstance(x, HeegnerPoint):
1702
raise NotImplementedError
1703
return cmp((self.__N, self.__D, self.__c), (x.__N, x.__D, x.__c))
1704
1705
def _repr_(self):
1706
"""
1707
EXAMPLES::
1708
1709
sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5)
1710
sage: type(H)
1711
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>
1712
sage: H._repr_()
1713
'Heegner point of level 389, discriminant -7, and conductor 5'
1714
"""
1715
return "Heegner point of level %s, discriminant %s, and conductor %s"%(
1716
self.__N, self.__D, self.__c)
1717
1718
def __hash__(self):
1719
"""
1720
EXAMPLES::
1721
1722
sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5); type(H)
1723
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>
1724
sage: hash(H)
1725
6187687223143458874 # 64-bit
1726
-458201030 # 32-bit
1727
"""
1728
return hash((self.__N, self.__D, self.__c))
1729
1730
def __eq__(self, right):
1731
"""
1732
EXAMPLES::
1733
1734
sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5); type(H)
1735
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>
1736
sage: J = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,11)
1737
sage: H == H
1738
True
1739
sage: H == J
1740
False
1741
sage: J == H
1742
False
1743
sage: H == 0
1744
False
1745
"""
1746
if not isinstance(right, HeegnerPoint): return False
1747
return (self.__N, self.__D, self.__c) == (right.__N, right.__D, right.__c)
1748
1749
def level(self):
1750
"""
1751
Return the level of this Heegner point, which is the level of the
1752
modular curve `X_0(N)` on which this is a Heegner point.
1753
1754
EXAMPLES::
1755
1756
sage: heegner_point(389,-7,5).level()
1757
389
1758
"""
1759
return self.__N
1760
1761
def conductor(self):
1762
"""
1763
Return the conductor of this Heegner point.
1764
1765
EXAMPLES::
1766
1767
sage: heegner_point(389,-7,5).conductor()
1768
5
1769
sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67,7); P
1770
Kolyvagin point of discriminant -67 and conductor 7 on elliptic curve of conductor 37
1771
sage: P.conductor()
1772
7
1773
sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5); P.conductor()
1774
5
1775
"""
1776
return self.__c
1777
1778
def discriminant(self):
1779
"""
1780
Return the discriminant of the quadratic imaginary field
1781
associated to this Heegner point.
1782
1783
EXAMPLES::
1784
1785
sage: heegner_point(389,-7,5).discriminant()
1786
-7
1787
sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67,7); P
1788
Kolyvagin point of discriminant -67 and conductor 7 on elliptic curve of conductor 37
1789
sage: P.discriminant()
1790
-67
1791
sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5); P.discriminant()
1792
-7
1793
"""
1794
return self.__D
1795
1796
@cached_method
1797
def quadratic_field(self):
1798
"""
1799
Return the quadratic number field of discriminant `D`.
1800
1801
EXAMPLES::
1802
1803
sage: x = heegner_point(37,-7,5)
1804
sage: x.quadratic_field()
1805
Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
1806
1807
1808
sage: E = EllipticCurve('37a'); P = E.heegner_point(-40)
1809
sage: P.quadratic_field()
1810
Number Field in sqrt_minus_40 with defining polynomial x^2 + 40
1811
sage: P.quadratic_field() is P.quadratic_field()
1812
True
1813
sage: type(P.quadratic_field())
1814
<class 'sage.rings.number_field.number_field.NumberField_quadratic_with_category'>
1815
"""
1816
return self.ring_class_field().quadratic_field()
1817
1818
@cached_method
1819
def quadratic_order(self):
1820
"""
1821
Return the order in the quadratic imaginary field of conductor
1822
`c`, where `c` is the conductor of this Heegner point.
1823
1824
EXAMPLES::
1825
1826
sage: heegner_point(389,-7,5).quadratic_order()
1827
Order in Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
1828
sage: heegner_point(389,-7,5).quadratic_order().basis()
1829
[1, 5*sqrt_minus_7]
1830
1831
sage: E = EllipticCurve('37a'); P = E.heegner_point(-40,11)
1832
sage: P.quadratic_order()
1833
Order in Number Field in sqrt_minus_40 with defining polynomial x^2 + 40
1834
sage: P.quadratic_order().basis()
1835
[1, 11*sqrt_minus_40]
1836
1837
"""
1838
K = self.quadratic_field()
1839
return K.order([1,self.conductor()*K.gen()])
1840
1841
@cached_method
1842
def ring_class_field(self):
1843
"""
1844
Return the ring class field associated to this Heegner point.
1845
This is an extension `K_c` over `K`, where `K` is the
1846
quadratic imaginary field and `c` is the conductor associated
1847
to this Heegner point. This Heegner point is defined over
1848
`K_c` and the Galois group `Gal(K_c/K)` acts transitively on
1849
the Galois conjugates of this Heegner point.
1850
1851
EXAMPLES::
1852
1853
sage: E = EllipticCurve('389a'); K.<a> = QuadraticField(-5)
1854
sage: len(K.factor(5))
1855
1
1856
sage: len(K.factor(23))
1857
2
1858
sage: E.heegner_point(-7, 5).ring_class_field().degree_over_K()
1859
6
1860
sage: E.heegner_point(-7, 23).ring_class_field().degree_over_K()
1861
22
1862
sage: E.heegner_point(-7, 5*23).ring_class_field().degree_over_K()
1863
132
1864
sage: E.heegner_point(-7, 5^2).ring_class_field().degree_over_K()
1865
30
1866
sage: E.heegner_point(-7, 7).ring_class_field().degree_over_K()
1867
7
1868
"""
1869
return RingClassField(self.discriminant(), self.conductor())
1870
1871
1872
##################################################################################
1873
#
1874
# Sets of Heegner points
1875
#
1876
##################################################################################
1877
1878
class HeegnerPoints(SageObject):
1879
"""
1880
The set of Heegner points with given parameters.
1881
1882
EXAMPLES::
1883
1884
sage: H = heegner_points(389); H
1885
Set of all Heegner points on X_0(389)
1886
sage: type(H)
1887
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoints_level'>
1888
sage: isinstance(H, sage.schemes.elliptic_curves.heegner.HeegnerPoints)
1889
True
1890
"""
1891
def __init__(self, N):
1892
"""
1893
INPUT:
1894
1895
- `N` -- level, a positive integer
1896
1897
EXAMPLES::
1898
1899
sage: heegner_points(37)
1900
Set of all Heegner points on X_0(37)
1901
sage: heegner_points(0)
1902
Traceback (most recent call last):
1903
...
1904
ValueError: N must a positive integer
1905
"""
1906
self.__N = ZZ(N)
1907
if self.__N <= 0:
1908
raise ValueError("N must a positive integer")
1909
1910
def level(self):
1911
"""
1912
Return the level `N` of the modular curve `X_0(N)`.
1913
1914
EXAMPLES::
1915
1916
sage: heegner_points(389).level()
1917
389
1918
"""
1919
return self.__N
1920
1921
1922
class HeegnerPoints_level(HeegnerPoints):
1923
"""
1924
Return the infinite set of all Heegner points on `X_0(N)` for all
1925
quadratic imaginary fields.
1926
1927
EXAMPLES::
1928
1929
sage: H = heegner_points(11); H
1930
Set of all Heegner points on X_0(11)
1931
sage: type(H)
1932
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoints_level'>
1933
sage: loads(dumps(H)) == H
1934
True
1935
"""
1936
def __eq__(self, other):
1937
"""
1938
EXAMPLES::
1939
1940
sage: H = heegner_points(11)
1941
sage: H == heegner_points(13)
1942
False
1943
sage: H == heegner_points(11)
1944
True
1945
sage: H == 0
1946
False
1947
"""
1948
return isinstance(other, HeegnerPoints_level) and self.level() == other.level()
1949
1950
def _repr_(self):
1951
"""
1952
Return string representation of the set of Heegner points.
1953
1954
EXAMPLES::
1955
1956
sage: heegner_points(389)._repr_()
1957
'Set of all Heegner points on X_0(389)'
1958
"""
1959
return "Set of all Heegner points on X_0(%s)"%self.level()
1960
1961
def reduce_mod(self, ell):
1962
r"""
1963
Return object that allows for computation with Heegner points
1964
of level `N` modulo the prime `\ell`, represented using
1965
quaternion algebras.
1966
1967
INPUT:
1968
1969
- `\ell` -- prime
1970
1971
EXAMPLES::
1972
1973
sage: heegner_points(389).reduce_mod(7).quaternion_algebra()
1974
Quaternion Algebra (-1, -7) with base ring Rational Field
1975
"""
1976
return HeegnerQuatAlg(self.level(), ell)
1977
1978
def discriminants(self, n=10, weak=False):
1979
r"""
1980
Return the first `n` quadratic imaginary discriminants that
1981
satisfy the Heegner hypothesis for `N`.
1982
1983
INPUTS:
1984
1985
- `n` -- nonnegative integer
1986
1987
- ``weak`` -- bool (default: ``False``); if ``True`` only require
1988
weak Heegner hypothesis, which is the same as usual but
1989
without the condition that `\gcd(D,N)=1`.
1990
1991
EXAMPLES::
1992
1993
sage: X = heegner_points(37)
1994
sage: X.discriminants(5)
1995
[-7, -11, -40, -47, -67]
1996
1997
The default is 10::
1998
1999
sage: X.discriminants()
2000
[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104]
2001
sage: X.discriminants(15)
2002
[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104, -107, -115, -120, -123, -127]
2003
2004
The discriminant -111 satisfies only the weak Heegner hypothesis, since it
2005
is divisible by 37::
2006
2007
sage: X.discriminants(15,weak=True)
2008
[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104, -107, -111, -115, -120, -123]
2009
"""
2010
N = self.level()
2011
n = ZZ(n)
2012
v = []
2013
D = ZZ(-4)
2014
while len(v) < n:
2015
D -= 1
2016
if satisfies_weak_heegner_hypothesis(N,D):
2017
# if not weak, then also require gcd(D,N)=1
2018
if not weak and D.gcd(N) != 1:
2019
continue
2020
v.append(D)
2021
return v
2022
2023
class HeegnerPoints_level_disc(HeegnerPoints):
2024
"""
2025
Set of Heegner points of given level and all conductors associated
2026
to a quadratic imaginary field.
2027
2028
EXAMPLES::
2029
2030
sage: H = heegner_points(389,-7); H
2031
Set of all Heegner points on X_0(389) associated to QQ[sqrt(-7)]
2032
sage: type(H)
2033
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoints_level_disc'>
2034
sage: H._repr_()
2035
'Set of all Heegner points on X_0(389) associated to QQ[sqrt(-7)]'
2036
sage: H.discriminant()
2037
-7
2038
sage: H.quadratic_field()
2039
Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
2040
sage: H.kolyvagin_conductors()
2041
[1, 3, 5, 13, 15, 17, 19, 31, 39, 41]
2042
2043
sage: loads(dumps(H)) == H
2044
True
2045
"""
2046
def __init__(self, N, D):
2047
"""
2048
INPUT:
2049
2050
- `N` -- positive integer
2051
2052
- `D` -- negative fundamental discriminant
2053
2054
EXAMPLES::
2055
2056
sage: sage.schemes.elliptic_curves.heegner.HeegnerPoints_level_disc(37,-7)
2057
Set of all Heegner points on X_0(37) associated to QQ[sqrt(-7)]
2058
"""
2059
HeegnerPoints.__init__(self, N)
2060
D = ZZ(D)
2061
if not satisfies_weak_heegner_hypothesis(N,D):
2062
raise ValueError("D (=%s) must satisfy the weak Heegner hypothesis for N (=%s)"%(D,N))
2063
self.__D = D
2064
2065
def __eq__(self, other):
2066
"""
2067
EXAMPLES::
2068
2069
sage: H = heegner_points(389,-7)
2070
sage: H == heegner_points(389,-7)
2071
True
2072
sage: H == 0
2073
False
2074
sage: H == heegner_points(389,-11)
2075
False
2076
"""
2077
return isinstance(other, HeegnerPoints_level_disc) and \
2078
self.level() == other.level() and self.__D == other.__D
2079
2080
def _repr_(self):
2081
"""
2082
Return string representation of the set of Heegner points for a given
2083
quadratic field.
2084
2085
EXAMPLES::
2086
2087
sage: heegner_points(389,-7)._repr_()
2088
'Set of all Heegner points on X_0(389) associated to QQ[sqrt(-7)]'
2089
"""
2090
return "Set of all Heegner points on X_0(%s) associated to QQ[sqrt(%s)]"%(
2091
self.level(), self.discriminant())
2092
2093
2094
def discriminant(self):
2095
r"""
2096
Return the discriminant of the quadratic imaginary extension `K`.
2097
2098
EXAMPLES::
2099
2100
sage: heegner_points(389,-7).discriminant()
2101
-7
2102
"""
2103
return self.__D
2104
2105
@cached_method
2106
def quadratic_field(self):
2107
r"""
2108
Return the quadratic imaginary field `K = \QQ(\sqrt{D})`.
2109
2110
EXAMPLES::
2111
2112
sage: E = EllipticCurve('389a'); K = E.heegner_point(-7,5).ring_class_field()
2113
sage: K.quadratic_field()
2114
Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
2115
"""
2116
D = self.__D
2117
var = 'sqrt_minus_%s'%(-D)
2118
return number_field.QuadraticField(D,var)
2119
2120
def kolyvagin_conductors(self, r=None, n=10, E=None, m=None):
2121
r"""
2122
Return the first `n` conductors that are squarefree products
2123
of distinct primes inert in the quadratic imaginary field
2124
`K = \QQ(\sqrt{D})`. If `r` is specified, return only
2125
conductors that are a product of `r` distinct primes all inert
2126
in `K`. If `r = 0`, always return the list ``[1]``,
2127
no matter what.
2128
2129
If the optional elliptic curve `E` and integer `m` are given,
2130
then only include conductors `c` such that for each prime
2131
divisor `p` of `c` we have `m \mid \gcd(a_p(E), p+1)`.
2132
2133
INPUT:
2134
2135
- `r` -- (default: ``None``) nonnegative integer or ``None``
2136
2137
- `n` -- positive integer
2138
2139
- `E` -- an elliptic curve
2140
2141
- `m` -- a positive integer
2142
2143
EXAMPLES::
2144
2145
sage: H = heegner_points(389,-7)
2146
sage: H.kolyvagin_conductors(0)
2147
[1]
2148
sage: H.kolyvagin_conductors(1)
2149
[3, 5, 13, 17, 19, 31, 41, 47, 59, 61]
2150
sage: H.kolyvagin_conductors(1,15)
2151
[3, 5, 13, 17, 19, 31, 41, 47, 59, 61, 73, 83, 89, 97, 101]
2152
sage: H.kolyvagin_conductors(1,5)
2153
[3, 5, 13, 17, 19]
2154
sage: H.kolyvagin_conductors(1,5,EllipticCurve('389a'),3)
2155
[5, 17, 41, 59, 83]
2156
sage: H.kolyvagin_conductors(2,5,EllipticCurve('389a'),3)
2157
[85, 205, 295, 415, 697]
2158
"""
2159
D = self.__D
2160
if not satisfies_weak_heegner_hypothesis(self.level(),D):
2161
raise ValueError("D must satisfy the weak Heegner hypothesis")
2162
n = ZZ(n)
2163
if n <= 0:
2164
raise ValueError("n must be a positive integer")
2165
if r is not None:
2166
r = ZZ(r)
2167
if r < 0:
2168
raise ValueError("n must be a nonnegative integer")
2169
if r == 0:
2170
return [ZZ(1)]
2171
2172
c = ZZ(1)
2173
v = []
2174
N = self.level()
2175
2176
if E is not None:
2177
m = ZZ(m)
2178
2179
while len(v) < n:
2180
if is_kolyvagin_conductor(N, E, D, r, m, c):
2181
v.append(c)
2182
c += 1
2183
2184
return v
2185
2186
2187
def is_kolyvagin_conductor(N, E, D, r, n, c):
2188
r"""
2189
Return ``True`` if `c` is a Kolyvagin conductor for level `N`,
2190
discriminant `D`, mod `n`, etc., i.e., `c` is divisible by exactly
2191
`r` prime factors, is coprime to `ND`, each prime dividing `c` is
2192
inert, and if `E` is not ``None`` then `n | \gcd(p+1, a_p(E))`
2193
for each prime `p` dividing `c`.
2194
2195
INPUT:
2196
2197
- `N` -- level (positive integer)
2198
2199
- `E` -- elliptic curve or ``None``
2200
2201
- `D` -- negative fundamental discriminant
2202
2203
- `r` -- number of prime factors (nonnegative integer) or ``None``
2204
2205
- `n` -- torsion order (i.e., do we get class in `(E(K_c)/n E(K_c))^{Gal(K_c/K)}`?)
2206
2207
- `c` -- conductor (positive integer)
2208
2209
EXAMPLES::
2210
2211
sage: from sage.schemes.elliptic_curves.heegner import is_kolyvagin_conductor
2212
sage: is_kolyvagin_conductor(389,None,-7,1,None,5)
2213
True
2214
sage: is_kolyvagin_conductor(389,None,-7,1,None,7)
2215
False
2216
sage: is_kolyvagin_conductor(389,None,-7,1,None,11)
2217
False
2218
sage: is_kolyvagin_conductor(389,EllipticCurve('389a'),-7,1,3,5)
2219
True
2220
sage: is_kolyvagin_conductor(389,EllipticCurve('389a'),-7,1,11,5)
2221
False
2222
"""
2223
ND = N*D
2224
if ND.gcd(c) != 1: return False
2225
if not c.is_squarefree(): return False
2226
P = c.prime_factors()
2227
if r is not None and len(P) != r:
2228
return False
2229
# check that each prime in P is inert in K
2230
for p in P:
2231
if D.kronecker(p) != -1:
2232
return False
2233
if E is not None and n is not None:
2234
for p in P:
2235
if (p+1).gcd(E.ap(p)) % n != 0:
2236
return False
2237
return True
2238
2239
2240
class HeegnerPoints_level_disc_cond(HeegnerPoints_level, HeegnerPoints_level_disc):
2241
"""
2242
The set of Heegner points of given level, discriminant, and conductor.
2243
2244
EXAMPLES::
2245
2246
sage: H = heegner_points(389,-7,5); H
2247
All Heegner points of conductor 5 on X_0(389) associated to QQ[sqrt(-7)]
2248
sage: type(H)
2249
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoints_level_disc_cond'>
2250
sage: H.discriminant()
2251
-7
2252
sage: H.level()
2253
389
2254
2255
sage: len(H.points())
2256
12
2257
sage: H.points()[0]
2258
Heegner point 5/778*sqrt(-7) - 147/778 of discriminant -7 and conductor 5 on X_0(389)
2259
sage: H.betas()
2260
(147, 631)
2261
2262
sage: H.quadratic_field()
2263
Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
2264
sage: H.ring_class_field()
2265
Ring class field extension of QQ[sqrt(-7)] of conductor 5
2266
2267
sage: H.kolyvagin_conductors()
2268
[1, 3, 5, 13, 15, 17, 19, 31, 39, 41]
2269
sage: H.satisfies_kolyvagin_hypothesis()
2270
True
2271
2272
sage: H = heegner_points(389,-7,5)
2273
sage: loads(dumps(H)) == H
2274
True
2275
"""
2276
def __init__(self, N, D, c=ZZ(1)):
2277
"""
2278
Create set of Heegner points.
2279
2280
INPUT:
2281
2282
- `N` -- positive integer (the level)
2283
2284
- `D` -- negative fundamental discriminant
2285
2286
- `c` -- conductor (default: 1)
2287
2288
EXAMPLES::
2289
2290
sage: H = heegner_points(389,-7,5); H
2291
All Heegner points of conductor 5 on X_0(389) associated to QQ[sqrt(-7)]
2292
sage: type(H)
2293
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoints_level_disc_cond'>
2294
"""
2295
HeegnerPoints_level.__init__(self, N)
2296
HeegnerPoints_level_disc.__init__(self, N, D)
2297
self.__c = ZZ(c)
2298
2299
def __eq__(self, other):
2300
"""
2301
EXAMPLES::
2302
2303
sage: H = heegner_points(389,-7, 3)
2304
sage: H == heegner_points(389,-7, 3)
2305
True
2306
sage: H == heegner_points(389,-7, 1)
2307
False
2308
sage: H == 0
2309
False
2310
"""
2311
return isinstance(other, HeegnerPoints_level_disc_cond) and \
2312
self.level() == other.level() and self.discriminant() == other.discriminant() \
2313
and self.conductor() == other.conductor()
2314
2315
def _repr_(self):
2316
"""
2317
Return string representation of this set of Heegner points.
2318
2319
EXAMPLES::
2320
2321
sage: H = heegner_points(37,-7,5); H._repr_()
2322
'All Heegner points of conductor 5 on X_0(37) associated to QQ[sqrt(-7)]'
2323
"""
2324
return "All Heegner points of conductor %s on X_0(%s) associated to QQ[sqrt(%s)]"%(
2325
self.conductor(), self.level(), self.discriminant())
2326
2327
def conductor(self):
2328
"""
2329
Return the level of the conductor.
2330
2331
EXAMPLES::
2332
2333
sage: heegner_points(389,-7,5).conductor()
2334
5
2335
"""
2336
return self.__c
2337
2338
@cached_method
2339
def satisfies_kolyvagin_hypothesis(self):
2340
"""
2341
Return ``True`` if ``self`` satisfies the Kolyvagin hypothesis, i.e.,
2342
that each prime dividing the conductor `c` of ``self`` is inert in
2343
`K` and coprime to `ND`.
2344
2345
EXAMPLES:
2346
2347
The prime 5 is inert, but the prime 11 is not::
2348
2349
sage: heegner_points(389,-7,5).satisfies_kolyvagin_hypothesis()
2350
True
2351
sage: heegner_points(389,-7,11).satisfies_kolyvagin_hypothesis()
2352
False
2353
"""
2354
return is_kolyvagin_conductor(N=self.level(), E=None, D=self.discriminant(),
2355
r=None, n=None, c=self.conductor())
2356
2357
@cached_method
2358
def ring_class_field(self):
2359
"""
2360
Return the ring class field associated to this set of Heegner
2361
points. This is an extension `K_c` over `K`, where `K` is the
2362
quadratic imaginary field and `c` the conductor associated to
2363
this Heegner point. This Heegner point is defined over `K_c`
2364
and the Galois group `Gal(K_c/K)` acts transitively on the
2365
Galois conjugates of this Heegner point.
2366
2367
EXAMPLES::
2368
2369
sage: heegner_points(389,-7,5).ring_class_field()
2370
Ring class field extension of QQ[sqrt(-7)] of conductor 5
2371
"""
2372
return RingClassField(self.discriminant(), self.conductor())
2373
2374
def __getitem__(self, i):
2375
"""
2376
Return the `i`-th Heegner point.
2377
2378
EXAMPLES::
2379
2380
sage: H = heegner_points(389,-7,5)
2381
sage: len(H)
2382
12
2383
sage: H[0]
2384
Heegner point 5/778*sqrt(-7) - 147/778 of discriminant -7 and conductor 5 on X_0(389)
2385
sage: H[-1]
2386
Heegner point 5/5446*sqrt(-7) - 757/778 of discriminant -7 and conductor 5 on X_0(389)
2387
"""
2388
return self.points()[i]
2389
2390
def __len__(self):
2391
"""
2392
Return the number of Heegner points.
2393
2394
EXAMPLES::
2395
2396
sage: len(heegner_points(389,-7,5))
2397
12
2398
2399
When the conductor is 1 the length is a power of 2 (number of
2400
square roots of `D` mod `4N` reduced mod `2N`) times the class
2401
number::
2402
2403
sage: len(heegner_points(389,-20,1))
2404
4
2405
sage: QQ[sqrt(-20)].class_number()
2406
2
2407
"""
2408
return len(self.points())
2409
2410
@cached_method
2411
def betas(self):
2412
"""
2413
Return the square roots of `D c^2` modulo `4 N` all reduced
2414
mod `2 N`, without multiplicity.
2415
2416
EXAMPLES::
2417
2418
sage: X = heegner_points(45,-11,1); X
2419
All Heegner points of conductor 1 on X_0(45) associated to QQ[sqrt(-11)]
2420
sage: [x.quadratic_form() for x in X]
2421
[45*x^2 + 13*x*y + y^2,
2422
45*x^2 + 23*x*y + 3*y^2,
2423
45*x^2 + 67*x*y + 25*y^2,
2424
45*x^2 + 77*x*y + 33*y^2]
2425
sage: X.betas()
2426
(13, 23, 67, 77)
2427
sage: X.points(13)
2428
(Heegner point 1/90*sqrt(-11) - 13/90 of discriminant -11 on X_0(45),)
2429
sage: [x.quadratic_form() for x in X.points(13)]
2430
[45*x^2 + 13*x*y + y^2]
2431
"""
2432
c = self.__c
2433
D = self.discriminant()*c*c
2434
N = self.level()
2435
R = Integers(4*N)
2436
m = 2*N
2437
return tuple(sorted( set([a%m for a in R(D).sqrt(all=True)]) ))
2438
2439
2440
@cached_method
2441
def points(self, beta=None):
2442
r"""
2443
Return the Heegner points in ``self``. If `\beta` is given,
2444
return only those Heegner points with given `\beta`, i.e.,
2445
whose quadratic form has `B` congruent to `\beta` modulo `2 N`.
2446
2447
Use ``self.beta()`` to get a list of betas.
2448
2449
EXAMPLES::
2450
2451
sage: H = heegner_points(389,-7,5); H
2452
All Heegner points of conductor 5 on X_0(389) associated to QQ[sqrt(-7)]
2453
sage: H.points()
2454
(Heegner point 5/778*sqrt(-7) - 147/778 of discriminant -7 and conductor 5 on X_0(389), ..., Heegner point 5/5446*sqrt(-7) - 757/778 of discriminant -7 and conductor 5 on X_0(389))
2455
sage: H.betas()
2456
(147, 631)
2457
sage: [x.tau() for x in H.points(147)]
2458
[5/778*sqrt_minus_7 - 147/778, 5/1556*sqrt_minus_7 - 147/1556, 5/1556*sqrt_minus_7 - 925/1556, 5/3112*sqrt_minus_7 - 1703/3112, 5/3112*sqrt_minus_7 - 2481/3112, 5/5446*sqrt_minus_7 - 21/778]
2459
2460
sage: [x.tau() for x in H.points(631)]
2461
[5/778*sqrt_minus_7 - 631/778, 5/1556*sqrt_minus_7 - 631/1556, 5/1556*sqrt_minus_7 - 1409/1556, 5/3112*sqrt_minus_7 - 631/3112, 5/3112*sqrt_minus_7 - 1409/3112, 5/5446*sqrt_minus_7 - 757/778]
2462
2463
The result is cached and is a tuple (since it is immutable)::
2464
2465
sage: H.points() is H.points()
2466
True
2467
sage: type(H.points())
2468
<type 'tuple'>
2469
"""
2470
if beta is None:
2471
SDN = self.betas()
2472
return tuple(sorted(sum([list(self.points(b)) for b in SDN], [])))
2473
2474
c = self.conductor()
2475
N = self.level()
2476
D = self.discriminant()
2477
b = ZZ(beta) % (2*N)
2478
2479
disc = D*c*c
2480
2481
U = []
2482
R = []
2483
h = self.ring_class_field().degree_over_K()
2484
a = 1
2485
while len(U) < h:
2486
if c.gcd(a) != 1:
2487
a += 1
2488
continue
2489
# todo (optimize) -- replace for over all s with for over solution set
2490
y = ZZ((b*b - disc)/(4*N))
2491
for s in Integers(a):
2492
if N*s*s + b*s + y == 0:
2493
s = s.lift()
2494
f = (a*N, b+2*N*s, ZZ( ((b + 2*N*s)**2 - disc)/(4*a*N)) )
2495
g = BinaryQF(f).reduced_form()
2496
assert g.discriminant() == disc
2497
if g not in U:
2498
U.append(g)
2499
R.append(HeegnerPointOnX0N(N,D,c,f))
2500
if len(U) >= h: break
2501
a += 1
2502
return tuple(sorted(R))
2503
2504
def plot(self, *args, **kwds):
2505
"""
2506
Returns plot of all the representatives in the upper half
2507
plane of the Heegner points in this set of Heegner points.
2508
2509
The inputs to this function get passed onto the point command.
2510
2511
EXAMPLES::
2512
2513
sage: heegner_points(389,-7,5).plot(pointsize=50, rgbcolor='red')
2514
sage: heegner_points(53,-7,15).plot(pointsize=50, rgbcolor='purple')
2515
"""
2516
return sum(z.plot(*args, **kwds) for z in self)
2517
2518
2519
class HeegnerPointOnX0N(HeegnerPoint):
2520
r"""
2521
A Heegner point as a point on the modular curve `X_0(N)`, which we
2522
view as the upper half plane modulo the action of `\Gamma_0(N)`.
2523
2524
EXAMPLES::
2525
2526
sage: x = heegner_point(37,-7,5); x
2527
Heegner point 5/74*sqrt(-7) - 11/74 of discriminant -7 and conductor 5 on X_0(37)
2528
sage: type(x)
2529
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPointOnX0N'>
2530
sage: x.level()
2531
37
2532
sage: x.conductor()
2533
5
2534
sage: x.discriminant()
2535
-7
2536
sage: x.quadratic_field()
2537
Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
2538
sage: x.quadratic_form()
2539
37*x^2 + 11*x*y + 2*y^2
2540
sage: x.quadratic_order()
2541
Order in Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
2542
sage: x.tau()
2543
5/74*sqrt_minus_7 - 11/74
2544
sage: loads(dumps(x)) == x
2545
True
2546
"""
2547
def __init__(self, N, D, c=ZZ(1), f=None, check=True):
2548
r"""
2549
INPUT:
2550
2551
- `N` -- positive integer
2552
2553
- `D` -- fundamental discriminant, a negative integer
2554
2555
- `c` -- conductor, a positive integer coprime to `N`
2556
2557
- `f` -- binary quadratic form, 3-tuple `(A,B,C)` of coefficients
2558
of `AX^2 + BXY + CY^2`, or element of quadratic imaginary
2559
field `\QQ(\sqrt{D})` in the upper half plan.
2560
2561
- ``check`` -- bool, default: ``True``. should not be used
2562
except internally.
2563
2564
2565
EXAMPLES::
2566
2567
sage: x = heegner_point(389, -7, 5); x
2568
Heegner point 5/778*sqrt(-7) - 147/778 of discriminant -7 and conductor 5 on X_0(389)
2569
sage: type(x)
2570
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPointOnX0N'>
2571
sage: sage.schemes.elliptic_curves.heegner.HeegnerPointOnX0N(389, -7, 5, None, check=False)
2572
Heegner point 5/778*sqrt(-7) - 147/778 of discriminant -7 and conductor 5 on X_0(389)
2573
"""
2574
if check:
2575
N = ZZ(N); D = ZZ(D); c = ZZ(c)
2576
if c.gcd(N) != 1:
2577
raise ValueError("conductor c (=%s) must be coprime to N (=%s)" % (c, N))
2578
if not satisfies_weak_heegner_hypothesis(N, D):
2579
raise ValueError("N (=%s) and D (=%s) must satisfy the Heegner hypothesis"%(N, D))
2580
if f is not None:
2581
if isinstance(f, tuple):
2582
if len(f) != 3:
2583
raise ValueError("if f is a tuple, it must have length 3")
2584
f = tuple(ZZ(a) for a in f)
2585
elif isinstance(f, BinaryQF):
2586
# convert from BinaryQF
2587
f = tuple(f)
2588
elif rings.is_NumberFieldElement(f):
2589
# tau = number field element
2590
g = f.minpoly()
2591
if g.degree() != 2:
2592
raise TypeError("number field element f must have degree 2")
2593
g *= g.denominator() # make integral
2594
f = (ZZ(g[2]), ZZ(g[1]), ZZ(g[0]))
2595
else:
2596
raise TypeError("f must be a 3-tuple, quadratic form, or element of the upper half plane")
2597
A, B, C = f
2598
if B*B - 4*A*C != D*c*c:
2599
raise ValueError("f (=%s) must have discriminant %s"%(f, D*c*c))
2600
HeegnerPoint.__init__(self, N, D, c)
2601
if f is None:
2602
# We know that N|A, so A = N is optimal.
2603
A = N
2604
B = ZZ(Integers(4*N)(D*c*c).sqrt(extend=False) % (2*N))
2605
C = ZZ((B*B - D*c*c)/(4*A))
2606
f = (A,B,C)
2607
self.__f = f
2608
2609
2610
def __hash__(self):
2611
"""
2612
EXAMPLES::
2613
2614
sage: y = EllipticCurve('389a').heegner_point(-7,5)
2615
sage: hash(y)
2616
-756867903203770682 # 64-bit
2617
-274399546 # 32-bit
2618
"""
2619
return hash((HeegnerPoint.__hash__(self), self.reduced_quadratic_form()))
2620
2621
def __eq__(self, right):
2622
"""
2623
EXAMPLES::
2624
2625
sage: x1 = EllipticCurve('389a').heegner_point(-7).heegner_point_on_X0N()
2626
sage: x5 = EllipticCurve('389a').heegner_point(-7,5).heegner_point_on_X0N()
2627
sage: x1 == x1
2628
True
2629
sage: x5 == x5
2630
True
2631
sage: x1 == x5
2632
False
2633
sage: x1 == 10
2634
False
2635
"""
2636
return isinstance(right, HeegnerPointOnX0N) and \
2637
HeegnerPoint.__eq__(self,right) and \
2638
self.reduced_quadratic_form() == right.reduced_quadratic_form()
2639
2640
def __cmp__(self, x):
2641
"""
2642
Compare two Heegner points with character.
2643
2644
EXAMPLES::
2645
2646
sage: x1 = EllipticCurve('389a').heegner_point(-7).heegner_point_on_X0N()
2647
sage: x5 = EllipticCurve('389a').heegner_point(-7,5).heegner_point_on_X0N()
2648
sage: x1.__cmp__(x1)
2649
0
2650
sage: x1.__cmp__(x5)
2651
-1
2652
sage: x5.__cmp__(x1)
2653
1
2654
"""
2655
c = HeegnerPoint.__cmp__(self, x)
2656
if c: return c
2657
return cmp(self.__f, x.__f)
2658
2659
def _repr_(self):
2660
"""
2661
Return string representation of this Heegner point.
2662
2663
EXAMPLES::
2664
2665
sage: x = heegner_point(37,-7,5); x._repr_()
2666
'Heegner point 5/74*sqrt(-7) - 11/74 of discriminant -7 and conductor 5 on X_0(37)'
2667
"""
2668
c = self.conductor()
2669
s = " and conductor %s"%c if c != 1 else ""
2670
N = self.level()
2671
D = self.discriminant()
2672
tau = repr(self.tau()).replace('sqrt_minus_%s'%(-D),'sqrt(%s)'%D)
2673
return "Heegner point %s of discriminant %s%s on X_0(%s)"%(tau, D, s, N)
2674
2675
def atkin_lehner_act(self, Q=None):
2676
r"""
2677
Given an integer Q dividing the level N such that `\gcd(Q, N/Q) = 1`, returns the
2678
image of this Heegner point under the Atkin-Lehner operator `W_Q`.
2679
2680
INPUT:
2681
2682
- `Q` -- positive divisor of `N`; if not given, default to `N`
2683
2684
EXAMPLES::
2685
2686
sage: x = heegner_point(389,-7,5)
2687
sage: x.atkin_lehner_act()
2688
Heegner point 5/199168*sqrt(-7) - 631/199168 of discriminant -7 and conductor 5 on X_0(389)
2689
2690
sage: x = heegner_point(45,D=-11,c=1); x
2691
Heegner point 1/90*sqrt(-11) - 13/90 of discriminant -11 on X_0(45)
2692
sage: x.atkin_lehner_act(5)
2693
Heegner point 1/90*sqrt(-11) + 23/90 of discriminant -11 on X_0(45)
2694
sage: y = x.atkin_lehner_act(9); y
2695
Heegner point 1/90*sqrt(-11) - 23/90 of discriminant -11 on X_0(45)
2696
sage: z = y.atkin_lehner_act(9); z
2697
Heegner point 1/90*sqrt(-11) - 13/90 of discriminant -11 on X_0(45)
2698
sage: z == x
2699
True
2700
"""
2701
N = self.level()
2702
if Q is None:
2703
Q = N
2704
if Q == 1:
2705
return self # trivial special case
2706
g, u, v = arith.xgcd(Q*Q, -N)
2707
if g != Q:
2708
raise ValueError("Q must divide N and be coprime to N/Q")
2709
tau = self.tau()
2710
WQ_tau = ((u*Q*tau + v) / (N*tau + Q))
2711
return HeegnerPointOnX0N(N, self.discriminant(), self.conductor(), f=WQ_tau, check=True)
2712
2713
2714
@cached_method
2715
def quadratic_form(self):
2716
"""
2717
Return the integral primitive positive-definite binary
2718
quadratic form associated to this Heegner point.
2719
2720
EXAMPLES::
2721
2722
sage: heegner_point(389,-7,5).quadratic_form()
2723
389*x^2 + 147*x*y + 14*y^2
2724
"""
2725
# It is good/important that this return a copy, since
2726
# BinaryQF's stupidly are mutable and cannot be made immutable.
2727
# In particular, they have a stupid reduce method that changes
2728
# them in place.
2729
return BinaryQF(self.__f)
2730
2731
def reduced_quadratic_form(self):
2732
"""
2733
Return reduced binary quadratic corresponding to this Heegner point.
2734
2735
EXAMPLES::
2736
2737
sage: x = heegner_point(389,-7,5)
2738
sage: x.quadratic_form()
2739
389*x^2 + 147*x*y + 14*y^2
2740
sage: x.reduced_quadratic_form()
2741
4*x^2 - x*y + 11*y^2
2742
"""
2743
return self.quadratic_form().reduced_form()
2744
2745
@cached_method
2746
def tau(self):
2747
"""
2748
Return an element tau in the upper half plane that corresponds
2749
to this particular Heegner point (actually, tau is in the
2750
quadratic imagqinary field K associated to this Heegner point).
2751
2752
EXAMPLES::
2753
2754
sage: x = heegner_point(37,-7,5); tau = x.tau(); tau
2755
5/74*sqrt_minus_7 - 11/74
2756
sage: 37 * tau.minpoly()
2757
37*x^2 + 11*x + 2
2758
sage: x.quadratic_form()
2759
37*x^2 + 11*x*y + 2*y^2
2760
"""
2761
K = self.quadratic_field()
2762
c = self.conductor()
2763
d = K.gen()*c
2764
A,B,_ = self.__f
2765
return (-B + d)/(2*A)
2766
2767
def map_to_curve(self, E):
2768
"""
2769
Return the image of this Heegner point on the elliptic curve
2770
`E`, which must also have conductor `N`, where `N` is the
2771
level of ``self``.
2772
2773
EXAMPLES::
2774
2775
sage: x = heegner_point(389,-7,5); x
2776
Heegner point 5/778*sqrt(-7) - 147/778 of discriminant -7 and conductor 5 on X_0(389)
2777
sage: y = x.map_to_curve(EllipticCurve('389a')); y
2778
Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 389
2779
sage: y.curve().cremona_label()
2780
'389a1'
2781
sage: y.heegner_point_on_X0N()
2782
Heegner point 5/778*sqrt(-7) - 147/778 of discriminant -7 and conductor 5 on X_0(389)
2783
2784
You can also directly apply the modular parametrization of the elliptic curve::
2785
2786
sage: x = heegner_point(37,-7); x
2787
Heegner point 1/74*sqrt(-7) - 17/74 of discriminant -7 on X_0(37)
2788
sage: E = EllipticCurve('37a'); phi = E.modular_parametrization()
2789
sage: phi(x)
2790
Heegner point of discriminant -7 on elliptic curve of conductor 37
2791
"""
2792
return HeegnerPointOnEllipticCurve(E, self)
2793
2794
@cached_method
2795
def galois_orbit_over_K(self):
2796
r"""
2797
Return the `Gal(K_c/K)`-orbit of this Heegner point.
2798
2799
EXAMPLES::
2800
2801
sage: x = heegner_point(389,-7,3); x
2802
Heegner point 3/778*sqrt(-7) - 223/778 of discriminant -7 and conductor 3 on X_0(389)
2803
sage: x.galois_orbit_over_K()
2804
[Heegner point 3/778*sqrt(-7) - 223/778 of discriminant -7 and conductor 3 on X_0(389), Heegner point 3/1556*sqrt(-7) - 223/1556 of discriminant -7 and conductor 3 on X_0(389), Heegner point 3/1556*sqrt(-7) - 1001/1556 of discriminant -7 and conductor 3 on X_0(389), Heegner point 3/3112*sqrt(-7) - 223/3112 of discriminant -7 and conductor 3 on X_0(389)]
2805
"""
2806
c = self.conductor()
2807
N = self.level()
2808
D = self.discriminant()
2809
b = self.__f[1] % (2*N) # B
2810
2811
disc = D*c*c
2812
2813
U = []
2814
R = []
2815
h = self.ring_class_field().degree_over_K()
2816
a = 1
2817
while len(U) < h:
2818
if c.gcd(a) != 1:
2819
a += 1
2820
continue
2821
# todo (optimize) -- replace for over all s with for over solution set
2822
y = ZZ((b*b - disc)/(4*N))
2823
for s in Integers(a):
2824
if N*s*s + b*s + y == 0:
2825
s = s.lift()
2826
f = (a*N, b+2*N*s, ZZ( ((b + 2*N*s)**2 - disc)/(4*a*N)) )
2827
g = BinaryQF(f).reduced_form()
2828
assert g.discriminant() == disc
2829
if g not in U:
2830
U.append(g)
2831
R.append(HeegnerPointOnX0N(N,D,c,f))
2832
a += 1
2833
return R
2834
2835
def plot(self, **kwds):
2836
r"""
2837
Draw a point at `(x,y)` where this Heegner point is
2838
represented by the point `\tau = x + i y` in the upper half
2839
plane.
2840
2841
The ``kwds`` get passed onto the point plotting command.
2842
2843
EXAMPLES::
2844
2845
sage: heegner_point(389,-7,1).plot(pointsize=50)
2846
"""
2847
from sage.plot.all import point
2848
return point(CDF(self.tau()), **kwds)
2849
2850
class HeegnerPointOnEllipticCurve(HeegnerPoint):
2851
"""
2852
A Heegner point on a curve associated to an order in a quadratic
2853
imaginary field.
2854
2855
EXAMPLES::
2856
2857
sage: E = EllipticCurve('37a'); P = E.heegner_point(-7,5); P
2858
Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 37
2859
sage: type(P)
2860
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPointOnEllipticCurve'>
2861
"""
2862
def __init__(self, E, x, check=True):
2863
r"""
2864
INPUT:
2865
2866
- `E` -- an elliptic curve over the rational numbers
2867
2868
- `x` -- Heegner point on `X_0(N)`
2869
2870
- ``check`` -- bool (default: ``True``); if ``True``, ensure that `D`,
2871
`c` are of type Integer and define a Heegner point
2872
on `E`
2873
2874
EXAMPLES::
2875
2876
sage: x = heegner_point(389,-7,5)
2877
sage: E = EllipticCurve('389a')
2878
sage: sage.schemes.elliptic_curves.heegner.HeegnerPointOnEllipticCurve(E, x)
2879
Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 389
2880
"""
2881
if check:
2882
if E.conductor() != x.level():
2883
raise ValueError("conductor of curve must equal level of Heegner point")
2884
self.__E = E
2885
self.__x = x
2886
HeegnerPoint.__init__(self, x.level(), x.discriminant(), x.conductor())
2887
2888
@cached_method
2889
def satisfies_kolyvagin_hypothesis(self, n=None):
2890
"""
2891
Return ``True`` if this Heegner point and `n` satisfy the
2892
Kolyvagin hypothesis, i.e., that each prime dividing the
2893
conductor `c` of ``self`` is inert in K and coprime to `ND`.
2894
Moreover, if `n` is not ``None``, also check that for each prime
2895
`p` dividing `c` we have that `n | \gcd(a_p(E), p+1)`.
2896
2897
INPUT:
2898
2899
`n` -- positive integer
2900
2901
EXAMPLES::
2902
2903
sage: EllipticCurve('389a').heegner_point(-7).satisfies_kolyvagin_hypothesis()
2904
True
2905
sage: EllipticCurve('389a').heegner_point(-7,5).satisfies_kolyvagin_hypothesis()
2906
True
2907
sage: EllipticCurve('389a').heegner_point(-7,11).satisfies_kolyvagin_hypothesis()
2908
False
2909
"""
2910
if n is not None:
2911
n = ZZ(n)
2912
if n <= 0: raise ValueError("n must be a positive integer")
2913
return is_kolyvagin_conductor(N=self.level(), E=self.__E, D=self.discriminant(),
2914
r=None, n=n, c=self.conductor())
2915
2916
def __hash__(self):
2917
"""
2918
EXAMPLES::
2919
2920
sage: hash(EllipticCurve('389a').heegner_point(-7,5))
2921
-756867903203770682 # 64-bit
2922
-274399546 # 32-bit
2923
"""
2924
return hash((self.__E, self.__x))
2925
2926
def __eq__(self, right):
2927
"""
2928
EXAMPLES::
2929
2930
sage: y1 = EllipticCurve('389a').heegner_point(-7)
2931
sage: y5 = EllipticCurve('389a').heegner_point(-7,5)
2932
sage: y1 == y1
2933
True
2934
sage: y5 == y5
2935
True
2936
sage: y1 == y5
2937
False
2938
sage: y1 == 10
2939
False
2940
"""
2941
return isinstance(right, HeegnerPointOnEllipticCurve) and \
2942
(self.__E, self.__x) == (right.__E, right.__x)
2943
2944
def _repr_(self):
2945
"""
2946
Return string representation of this Heegner point.
2947
2948
EXAMPLES::
2949
2950
sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 97)
2951
sage: P._repr_()
2952
'Heegner point of discriminant -7 and conductor 97 on elliptic curve of conductor 389'
2953
"""
2954
s = " and conductor %s"%self.conductor() if self.conductor() != 1 else ""
2955
N = self.__E.conductor()
2956
return "Heegner point of discriminant %s%s on elliptic curve of conductor %s"%(self.discriminant(), s, N)
2957
2958
def heegner_point_on_X0N(self):
2959
r"""
2960
Return Heegner point on `X_0(N)` that maps to this Heegner point on `E`.
2961
2962
EXAMPLES::
2963
2964
sage: E = EllipticCurve('37a'); P = E.heegner_point(-7,5); P
2965
Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 37
2966
sage: P.heegner_point_on_X0N()
2967
Heegner point 5/74*sqrt(-7) - 11/74 of discriminant -7 and conductor 5 on X_0(37)
2968
"""
2969
return self.__x
2970
2971
def map_to_complex_numbers(self, prec=53):
2972
"""
2973
Return the point in the subfield `M` of the complex numbers
2974
(well defined only modulo the period lattice) corresponding to
2975
this Heegner point.
2976
2977
EXAMPLES:
2978
2979
We compute a nonzero Heegner point over a ring class field on
2980
a curve of rank 2::
2981
2982
sage: E = EllipticCurve('389a'); y = E.heegner_point(-7,5)
2983
sage: y.map_to_complex_numbers()
2984
1.49979679635196 + 0.369156204821526*I
2985
sage: y.map_to_complex_numbers(100)
2986
1.4997967963519640592142411892 + 0.36915620482152626830089145962*I
2987
sage: y.map_to_complex_numbers(10)
2988
1.5 + 0.37*I
2989
2990
Here we see that the Heegner point is 0 since it lies in the
2991
lattice::
2992
2993
sage: E = EllipticCurve('389a'); y = E.heegner_point(-7)
2994
sage: y.map_to_complex_numbers(10)
2995
0.0034 - 3.9*I
2996
sage: y.map_to_complex_numbers()
2997
4.71844785465692e-15 - 3.94347540310330*I
2998
sage: E.period_lattice().basis()
2999
(2.49021256085505, 1.97173770155165*I)
3000
sage: 2*E.period_lattice().basis()[1]
3001
3.94347540310330*I
3002
3003
You can also directly coerce to the complex field::
3004
3005
sage: E = EllipticCurve('389a'); y = E.heegner_point(-7)
3006
sage: z = ComplexField(100)(y); z # real part approx. 0
3007
-... - 3.9434754031032964088448153963*I
3008
sage: E.period_lattice().elliptic_exponential(z)
3009
(0.00000000000000000000000000000 : 1.0000000000000000000000000000 : 0.00000000000000000000000000000)
3010
"""
3011
phi = self.__E.modular_parametrization()
3012
tau = self.heegner_point_on_X0N().tau()
3013
return phi.map_to_complex_numbers(tau, prec)
3014
3015
def _complex_mpfr_field_(self, C):
3016
"""
3017
Used internally for coercing Heegner point to a complex field.
3018
3019
EXAMPLES::
3020
3021
sage: E = EllipticCurve('37a'); y = E.heegner_point(-7)
3022
sage: CC(y) # indirect doctest
3023
0.929592715285395 - 1.22569469099340*I
3024
sage: ComplexField(100)(y)
3025
0.92959271528539567440519934446 - 1.2256946909933950304271124159*I
3026
"""
3027
phi = self.__E.modular_parametrization()
3028
tau = C(self.heegner_point_on_X0N().tau())
3029
return phi.map_to_complex_numbers(tau)
3030
3031
@cached_method
3032
def kolyvagin_point(self):
3033
"""
3034
Return the Kolyvagin point corresponding to this Heegner
3035
point. This is the point obtained by applying the Kolyvagin
3036
operator `J_c I_c` in the group ring of the Galois group to
3037
this Heegner point. It is a point that defines an element
3038
of `H^1(K, E[n])`, under certain hypotheses on `n`.
3039
3040
EXAMPLES::
3041
3042
sage: E = EllipticCurve('37a1'); y = E.heegner_point(-7); y
3043
Heegner point of discriminant -7 on elliptic curve of conductor 37
3044
sage: P = y.kolyvagin_point(); P
3045
Kolyvagin point of discriminant -7 on elliptic curve of conductor 37
3046
sage: PP = P.numerical_approx() # approximately (0 : 0 : 1)
3047
sage: all([c.abs() < 1e-15 for c in PP.xy()])
3048
True
3049
"""
3050
return KolyvaginPoint(self)
3051
3052
@cached_method
3053
def _trace_index(self, *args, **kwds):
3054
"""
3055
Return index of the trace of this Heegner point down to `K` in
3056
the group of `K`-rational points.
3057
3058
IMPORTANT: See the help for ``E=self.curve(); E.index?`` for
3059
the inputs to this function and more details about what is
3060
computed. In particular, the returned index can be off at 2.
3061
3062
OUTPUT:
3063
3064
- ``Integer`` -- returns an integer
3065
3066
EXAMPLES::
3067
3068
sage: E = EllipticCurve('77a1')
3069
sage: P = E.heegner_point(-19); y = P._trace_numerical_conductor_1(); [c.real() for c in y]
3070
[-1.2...e-16, -1.00000000000000, 1.00000000000000]
3071
sage: -2*E.gens()[0]
3072
(0 : -1 : 1)
3073
sage: P._trace_index()
3074
2
3075
3076
sage: P = E.heegner_point(-68); P
3077
Heegner point of discriminant -68 on elliptic curve of conductor 77
3078
sage: N(P)
3079
(0.219223593595584 - 1.87443160153148*I : -1.34232921921325 - 1.52356748877889*I : 1.00000000000000)
3080
sage: P._trace_index()
3081
0
3082
"""
3083
if self.conductor() != 1:
3084
raise ValueError("conductor of Heegner point must be 1")
3085
i = self.__E.heegner_index(self.discriminant(), *args, **kwds)
3086
lower = i.lower().round()
3087
upper = i.upper().round()
3088
if lower == upper:
3089
return lower
3090
# here we would say raise precision somehow.
3091
raise NotImplementedError("unable to compute index")
3092
3093
def curve(self):
3094
"""
3095
Return the elliptic curve on which this is a Heegner point.
3096
3097
EXAMPLES::
3098
3099
sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5)
3100
sage: P.curve()
3101
Elliptic Curve defined by y^2 + y = x^3 + x^2 - 2*x over Rational Field
3102
sage: P.curve() is E
3103
True
3104
"""
3105
return self.__E
3106
3107
@cached_method
3108
def quadratic_form(self):
3109
"""
3110
Return the integral primitive positive definite binary
3111
quadratic form associated to this Heegner point.
3112
3113
3114
EXAMPLES::
3115
3116
sage: EllipticCurve('389a').heegner_point(-7, 5).quadratic_form()
3117
389*x^2 + 147*x*y + 14*y^2
3118
3119
sage: P = EllipticCurve('389a').heegner_point(-7, 5, (778,925,275)); P
3120
Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 389
3121
sage: P.quadratic_form()
3122
778*x^2 + 925*x*y + 275*y^2
3123
"""
3124
return self.__x.quadratic_form()
3125
3126
@cached_method
3127
def numerical_approx(self, prec=53):
3128
"""
3129
Return a numerical approximation to this Heegner point
3130
computed using a working precision of prec bits.
3131
3132
.. WARNING::
3133
3134
The answer is *not* provably correct to prec bits! A
3135
priori, due to rounding and other errors, it is possible that
3136
not a single digit is correct.
3137
3138
INPUT:
3139
3140
- prec -- (default: ``None``) the working precision
3141
3142
EXAMPLES::
3143
3144
sage: E = EllipticCurve('37a'); P = E.heegner_point(-7); P
3145
Heegner point of discriminant -7 on elliptic curve of conductor 37
3146
sage: all([c.abs()< 1e-15 for c in P.numerical_approx().xy()])
3147
True
3148
sage: P.numerical_approx(10) # expect random digits
3149
(0.0030 - 0.0028*I : -0.0030 + 0.0028*I : 1.0)
3150
sage: P.numerical_approx(100)[0] # expect random digits
3151
8.4...e-31 + 6.0...e-31*I
3152
sage: E = EllipticCurve('37a'); P = E.heegner_point(-40); P
3153
Heegner point of discriminant -40 on elliptic curve of conductor 37
3154
sage: P.numerical_approx()
3155
(-6.68...e-16 + 1.41421356237310*I : 1.00000000000000 - 1.41421356237309*I : 1.00000000000000)
3156
3157
A rank 2 curve, where all Heegner points of conductor 1 are 0::
3158
3159
sage: E = EllipticCurve('389a'); E.rank()
3160
2
3161
sage: P = E.heegner_point(-7); P
3162
Heegner point of discriminant -7 on elliptic curve of conductor 389
3163
sage: P.numerical_approx()
3164
(0.000000000000000 : 1.00000000000000 : 0.000000000000000)
3165
3166
However, Heegner points of bigger conductor are often nonzero::
3167
3168
sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5); P
3169
Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 389
3170
sage: numerical_approx(P)
3171
(0.675507556926806 + 0.344749649302635*I : -0.377142931401887 + 0.843366227137146*I : 1.00000000000000)
3172
sage: P.numerical_approx()
3173
(0.6755075569268... + 0.3447496493026...*I : -0.3771429314018... + 0.8433662271371...*I : 1.00000000000000)
3174
sage: E.heegner_point(-7, 11).numerical_approx()
3175
(0.1795583794118... + 0.02035501750912...*I : -0.5573941377055... + 0.2738940831635...*I : 1.00000000000000)
3176
sage: E.heegner_point(-7, 13).numerical_approx()
3177
(1.034302915374... - 3.302744319777...*I : 1.323937875767... + 6.908264226850...*I : 1.00000000000000)
3178
3179
We find (probably) the definining polynomial of the
3180
`x`-coordinate of `P`, which defines a class field. The shape of
3181
the discriminant below is strong confirmation -- but not proof
3182
-- that this polynomial is correct::
3183
3184
sage: f = P.numerical_approx(70)[0].algdep(6); f
3185
1225*x^6 + 1750*x^5 - 21675*x^4 - 380*x^3 + 110180*x^2 - 129720*x + 48771
3186
sage: f.discriminant().factor()
3187
2^6 * 3^2 * 5^11 * 7^4 * 13^2 * 19^6 * 199^2 * 719^2 * 26161^2
3188
"""
3189
tau = ComplexField(prec)(self.tau())
3190
E = self.curve()
3191
return E.modular_parametrization()(tau)
3192
3193
#This line is added to resolve ticket 9032, because both top-level function
3194
#and method call _numerical_approx instead of numerical_approx
3195
_numerical_approx=numerical_approx
3196
3197
def tau(self):
3198
r"""
3199
Return `\tau` in the upper half plane that maps via the
3200
modular parametrization to this Heegner point.
3201
3202
EXAMPLES::
3203
3204
sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5)
3205
sage: P.tau()
3206
5/778*sqrt_minus_7 - 147/778
3207
"""
3208
return self.heegner_point_on_X0N().tau()
3209
3210
@cached_method
3211
def x_poly_exact(self, prec=53, algorithm='lll'):
3212
"""
3213
Return irreducible polynomial over the rational numbers
3214
satisfied by the `x` coordinate of this Heegner point. A
3215
ValueError is raised if the precision is clearly insignificant
3216
to define a point on the curve.
3217
3218
.. WARNING::
3219
3220
It is in theory possible for this function to not raise a
3221
ValueError, find a polynomial, but via some very unlikely
3222
coincidence that point is not actually this Heegner point.
3223
3224
INPUT:
3225
3226
- ``prec`` -- integer (default: 53)
3227
3228
- ``algorithm`` -- 'conjugates' or 'lll' (default); if
3229
'conjugates', compute numerically all the
3230
conjugates ``y[i]`` of the Heegner point and construct
3231
the characteristic polynomial as the product
3232
`f(X)=(X-y[i])`. If 'lll', compute only one of the
3233
conjugates ``y[0]``, then uses the LLL algorithm to
3234
guess `f(X)`.
3235
3236
3237
EXAMPLES:
3238
3239
We compute some `x`-coordinate polynomials of some conductor 1
3240
Heegner points::
3241
3242
sage: E = EllipticCurve('37a')
3243
sage: v = E.heegner_discriminants_list(10)
3244
sage: [E.heegner_point(D).x_poly_exact() for D in v]
3245
[x, x, x^2 + 2, x^5 - x^4 + x^3 + x^2 - 2*x + 1, x - 6, x^7 - 2*x^6 + 9*x^5 - 10*x^4 - x^3 + 8*x^2 - 5*x + 1, x^3 + 5*x^2 + 10*x + 4, x^4 - 10*x^3 + 10*x^2 + 12*x - 12, x^8 - 5*x^7 + 7*x^6 + 13*x^5 - 10*x^4 - 4*x^3 + x^2 - 5*x + 7, x^6 - 2*x^5 + 11*x^4 - 24*x^3 + 30*x^2 - 16*x + 4]
3246
3247
3248
We compute `x`-coordinate polynomials for some Heegner points
3249
of conductor bigger than 1 on a rank 2 curve::
3250
3251
sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5); P
3252
Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 389
3253
sage: P.x_poly_exact()
3254
Traceback (most recent call last):
3255
...
3256
ValueError: insufficient precision to determine Heegner point (fails discriminant test)
3257
sage: P.x_poly_exact(75)
3258
x^6 + 10/7*x^5 - 867/49*x^4 - 76/245*x^3 + 3148/35*x^2 - 25944/245*x + 48771/1225
3259
sage: E.heegner_point(-7,11).x_poly_exact(300)
3260
x^10 + 282527/52441*x^9 + 27049007420/2750058481*x^8 - 22058564794/2750058481*x^7 - 140054237301/2750058481*x^6 + 696429998952/30250643291*x^5 + 2791387923058/30250643291*x^4 - 3148473886134/30250643291*x^3 + 1359454055022/30250643291*x^2 - 250620385365/30250643291*x + 181599685425/332757076201
3261
3262
Here we compute a Heegner point of conductor 5 on a rank 3 curve::
3263
3264
sage: E = EllipticCurve('5077a'); P = E.heegner_point(-7,5); P
3265
Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 5077
3266
sage: P.x_poly_exact(300)
3267
x^6 + 1108754853727159228/72351048803252547*x^5 + 88875505551184048168/1953478317687818769*x^4 - 2216200271166098662132/3255797196146364615*x^3 + 14941627504168839449851/9767391588439093845*x^2 - 3456417460183342963918/3255797196146364615*x + 1306572835857500500459/5426328660243941025
3268
"""
3269
D, c = self.discriminant(), self.conductor()
3270
n = self.ring_class_field().degree_over_K()
3271
3272
if algorithm == 'lll':
3273
P = self.numerical_approx(prec)
3274
g = None
3275
for e in [1,2]: # is there a condition under which we should not bother trying e=1?
3276
f = P[0].algdep(e*n)
3277
3278
# If f is correct, then disc(f) = m^2 * (a product of primes dividing D*c).
3279
# To check this, we divide out the primes dividing D*c, then
3280
# check that the resulting cofactor is a perfect square.
3281
F = f.factor()
3282
if len(F) == 1:
3283
f = F[0][0]
3284
if self._check_poly_discriminant(f):
3285
g = f
3286
break
3287
3288
if g is None:
3289
raise ValueError("insufficient precision to determine Heegner point (fails discriminant test)")
3290
f = g
3291
f = f/f.leading_coefficient()
3292
3293
elif algorithm == 'conjugates':
3294
3295
raise NotImplementedError
3296
3297
return f
3298
3299
def _check_poly_discriminant(self, f):
3300
"""
3301
Return ``True`` if the prime to `Dc` part of the discriminant of
3302
each factor of the polynomial `f` is plus or minus a square.
3303
This is used for verifying that a polynomial is likely to
3304
define a subfield of a specific ring class field.
3305
3306
INPUT:
3307
3308
- `f` -- a polynomial
3309
3310
EXAMPLES::
3311
3312
sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5); P
3313
Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 389
3314
sage: R.<x> = QQ[]
3315
sage: P._check_poly_discriminant(x^2 - 5)
3316
True
3317
sage: P._check_poly_discriminant(x^2 - 19)
3318
False
3319
sage: P._check_poly_discriminant((x^2 - 19)*(x^2-5))
3320
False
3321
"""
3322
if f.is_irreducible():
3323
disc = f.discriminant()
3324
(D, c) = (self.discriminant(), self.conductor())
3325
for p in D.prime_divisors() + c.prime_divisors():
3326
disc = disc // (p**disc.valuation(p))
3327
if disc < 0: disc = -disc
3328
return disc.is_square()
3329
else:
3330
for g,_ in f.factor():
3331
if not self._check_poly_discriminant(g):
3332
return False
3333
return True
3334
3335
3336
def point_exact(self, prec=53, algorithm='lll', var='a', optimize=False):
3337
"""
3338
Return exact point on the elliptic curve over a number field
3339
defined by computing this Heegner point to the given number of
3340
bits of precision. A ValueError is raised if the precision
3341
is clearly insignificant to define a point on the curve.
3342
3343
.. WARNING::
3344
3345
It is in theory possible for this function to not raise a
3346
ValueError, find a point on the curve, but via some very
3347
unlikely coincidence that point is not actually this Heegner
3348
point.
3349
3350
.. WARNING::
3351
3352
Currently we make an arbitrary choice of `y`-coordinate for
3353
the lift of the `x`-coordinate.
3354
3355
INPUT:
3356
3357
- ``prec`` -- integer (default: 53)
3358
3359
- ``algorithm`` -- see the description of the algorithm
3360
parameter for the ``x_poly_exact`` method.
3361
3362
- ``var`` -- string (default: 'a')
3363
3364
- ``optimize`` -- book (default; False) if ``True``, try to
3365
optimize defining polynomial for the number field that
3366
the point is defined over. Off by default, since this
3367
can be very expensive.
3368
3369
EXAMPLES::
3370
3371
sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5); P
3372
Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 389
3373
sage: z = P.point_exact(100, optimize=True)
3374
sage: z[1].charpoly()
3375
x^12 + 6*x^11 + 90089/1715*x^10 + 71224/343*x^9 + 52563964/588245*x^8 - 483814934/588245*x^7 - 156744579/16807*x^6 - 2041518032/84035*x^5 + 1259355443184/14706125*x^4 + 3094420220918/14706125*x^3 + 123060442043827/367653125*x^2 + 82963044474852/367653125*x + 211679465261391/1838265625
3376
sage: f = P.numerical_approx(500)[1].algdep(12); f / f.leading_coefficient()
3377
x^12 + 6*x^11 + 90089/1715*x^10 + 71224/343*x^9 + 52563964/588245*x^8 - 483814934/588245*x^7 - 156744579/16807*x^6 - 2041518032/84035*x^5 + 1259355443184/14706125*x^4 + 3094420220918/14706125*x^3 + 123060442043827/367653125*x^2 + 82963044474852/367653125*x + 211679465261391/1838265625
3378
3379
sage: E = EllipticCurve('5077a')
3380
sage: P = E.heegner_point(-7)
3381
sage: P.point_exact(prec=100)
3382
(0 : 1 : 0)
3383
"""
3384
E = self.__E
3385
if self.numerical_approx(prec)[-1] == 0:
3386
return E(0)
3387
f = self.x_poly_exact(prec, algorithm=algorithm)
3388
if f.degree() == 1:
3389
v = E.lift_x(-f[0], all=True)
3390
if len(v) > 0:
3391
return v[0]
3392
3393
g, d = make_monic(f)
3394
K = rings.NumberField(g, var)
3395
x = K.gen() / d
3396
if optimize:
3397
KO, from_KO, to_KO = K.optimized_representation()
3398
K = KO
3399
x = to_KO(x)
3400
if K.degree() < 2 * self.ring_class_field().degree_over_K():
3401
M = rings.QuadraticField(self.discriminant(),'b')
3402
KD = K.composite_fields(M, names='a')[0]
3403
phi = K.embeddings(KD)[0]
3404
x = phi(x)
3405
K = KD.change_names(names=var)
3406
x = K.structure()[1](x)
3407
a1,a2,a3,a4,a6 = E.a_invariants()
3408
R = K['Y']; Y = R.gen()
3409
g = Y**2 + a1*x*Y + a3*Y - (x**3 + a2*x**2 + a4*x + a6)
3410
F = g.factor() # this takes a long time
3411
if len(F) == 1 and F[0][0] == 2:
3412
# reducible -- 1 factor squared
3413
y = F[0][0]
3414
L = K
3415
elif len(F) == 2:
3416
# reducible -- 2 factors
3417
y0 = -F[0][0][0]
3418
# y1 = -F[1][0][0]
3419
# Figure out which of y0 or y1 is right by
3420
# P = self.numerical_approx(prec)
3421
# TODO: finish this -- have to do some thing numerical
3422
y = y0
3423
L = K
3424
else:
3425
# TODO -- is there an issue with choice of root?
3426
# irreducible
3427
gg, dd = make_monic(g)
3428
M = K.extension(gg, names='b')
3429
y = M.gen()/dd
3430
x = M(x)
3431
L = M.absolute_field(names = var)
3432
phi = L.structure()[1]
3433
x = phi(x)
3434
y = phi(y)
3435
3436
EL = E.change_ring(L)
3437
P = EL.point((x,y,1), check=False)
3438
return P
3439
3440
@cached_method
3441
def conjugates_over_K(self):
3442
r"""
3443
Return the `Gal(K_c/K)` conjugates of this Heegner point.
3444
3445
EXAMPLES::
3446
3447
sage: E = EllipticCurve('77a')
3448
sage: y = E.heegner_point(-52,5); y
3449
Heegner point of discriminant -52 and conductor 5 on elliptic curve of conductor 77
3450
sage: print [z.quadratic_form() for z in y.conjugates_over_K()]
3451
[77*x^2 + 52*x*y + 13*y^2, 154*x^2 + 206*x*y + 71*y^2, 539*x^2 + 822*x*y + 314*y^2, 847*x^2 + 1284*x*y + 487*y^2, 1001*x^2 + 52*x*y + y^2, 1078*x^2 + 822*x*y + 157*y^2, 1309*x^2 + 360*x*y + 25*y^2, 1309*x^2 + 2054*x*y + 806*y^2, 1463*x^2 + 976*x*y + 163*y^2, 2233*x^2 + 2824*x*y + 893*y^2, 2387*x^2 + 2054*x*y + 442*y^2, 3619*x^2 + 3286*x*y + 746*y^2]
3452
sage: y.quadratic_form()
3453
77*x^2 + 52*x*y + 13*y^2
3454
"""
3455
H = heegner_points(self.level(), self.discriminant(), self.conductor())
3456
E = self.curve()
3457
beta = self.quadratic_form()[1]
3458
return tuple([z.map_to_curve(E) for z in H.points(beta)])
3459
3460
def _numerical_approx_conjugates_over_QQ(self, prec=53):
3461
"""
3462
Return a list v of the numerical approximations to precision
3463
prec of the conjugates of this Heegner point, and their
3464
complex conjugates.
3465
3466
INPUT:
3467
3468
- ``prec`` -- positive integer (default: 53)
3469
3470
EXAMPLES::
3471
3472
sage: E = EllipticCurve('37a')
3473
sage: y = E.heegner_point(-7,3); y
3474
Heegner point of discriminant -7 and conductor 3 on elliptic curve of conductor 37
3475
sage: y._numerical_approx_conjugates_over_QQ()
3476
[(-1.89564392373896 - 0.444771808762067*I : -1.50000000000000 + 2.13102976222246*I : 1.00000000000000), ...]
3477
sage: y._numerical_approx_conjugates_over_QQ(prec=10)
3478
[(-1.9 - 0.44*I : -1.5 + 2.1*I : 1.0), ...
3479
(-1.9 - 0.44*I : -1.5 + 2.1*I : 1.0)]
3480
"""
3481
v = []
3482
for z in self.conjugates_over_K():
3483
m = z.numerical_approx(prec)
3484
v.append(m)
3485
v.append(m.curve().point([w.conjugate() for w in m], check=False))
3486
v.sort()
3487
return v
3488
3489
def _numerical_approx_xy_poly(self, prec=53):
3490
r"""
3491
Return polynomials with real floating point coefficients got
3492
by taking the real part of the product of `X - \alpha` over
3493
the numerical approximations `\alpha` to the conjugates of
3494
this Heegner point. The first polynomial runs through the
3495
`x`-coordinates and the second through the `y`-coordinates.
3496
3497
INPUT:
3498
3499
- ``prec`` -- positive integer (default: 53)
3500
3501
OUTPUT:
3502
3503
- 2-tuple of polynomials with floating point coefficients
3504
3505
EXAMPLES::
3506
sage: E = EllipticCurve('37a')
3507
sage: y = E.heegner_point(-7,3); y
3508
Heegner point of discriminant -7 and conductor 3 on elliptic curve of conductor 37
3509
sage: y._numerical_approx_xy_poly()
3510
(X^8 + 6.00000000000000*X^7 + 8.99999999999998*X^6 - 12.0000000000000*X^5 - 42.0000000000000*X^4 - 17.999999999999...*X^3 + 36.0000000000001*X^2 + 35.9999999999999*X + 8.999999999999..., X^8 + 12.0000000000000*X^7 + 72.0000000000000*X^6 + 270.000000000000*X^5 + 678.000000000001*X^4 + 1152.00000000000*X^3 + 1269.00000000000*X^2 + 810.00000000000...*X + 225.000000000001)
3511
"""
3512
v = self._numerical_approx_conjugates_over_QQ(prec)
3513
R = ComplexField(prec)['X']
3514
S = RealField(prec)['X']
3515
X = R.gen()
3516
fx = prod(X-a[0] for a in v)
3517
fx = S([b.real() for b in fx])
3518
fy = prod(X-c[1] for c in v)
3519
fy = S([d.real() for d in fy])
3520
return fx, fy
3521
3522
def _xy_poly_nearby(self, prec=53, max_error=10**(-10)):
3523
"""
3524
Return polynomials with rational coefficients that for sufficiently
3525
tight bounds are the characteristic polynomial of the x and y
3526
coordinate of this Heegner point.
3527
3528
INPUT:
3529
3530
- ``prec`` -- positive integer (default: 53)
3531
3532
- ``max_error`` -- very small floating point number
3533
3534
OUTPUT:
3535
3536
- 2-tuple of polynomials with rational coefficients
3537
3538
EXAMPLES::
3539
3540
sage: E = EllipticCurve('37a')
3541
sage: y = E.heegner_point(-7,3); y
3542
Heegner point of discriminant -7 and conductor 3 on elliptic curve of conductor 37
3543
sage: y._xy_poly_nearby()
3544
[X^8 + 6*X^7 + 9*X^6 - 12*X^5 - 42*X^4 - 18*X^3 + 36*X^2 + 36*X + 9,
3545
X^8 + 12*X^7 + 72*X^6 + 270*X^5 + 678*X^4 + 1152*X^3 + 1269*X^2 + 810*X + 225]
3546
3547
3548
"""
3549
v = self._numerical_approx_xy_poly(prec)
3550
return [nearby_rational_poly(g, max_error=max_error) for g in v]
3551
3552
def _xy_poly_simplest(self, prec=53, prec2=None):
3553
"""
3554
Return polynomials with rational coefficients that for
3555
sufficiently tight bounds are the characteristic polynomial of
3556
the x and y coordinate of this Heegner point.
3557
3558
INPUT:
3559
3560
- ``prec`` -- positive integer (default: 53)
3561
3562
- ``prec2`` -- passed into simplest_rational_poly function
3563
3564
EXAMPLES::
3565
3566
sage: E = EllipticCurve('37a'); y = E.heegner_point(-7,3)
3567
sage: y._xy_poly_simplest()
3568
[X^8 + 6*X^7 + 9*X^6 - 12*X^5 - 42*X^4 - 18*X^3 + 36*X^2 + 36*X + 9,
3569
X^8 + 12*X^7 + 72*X^6 + 270*X^5 + 678*X^4 + 1152*X^3 + 1269*X^2 + 810*X + 225]
3570
"""
3571
v = self._numerical_approx_xy_poly(prec)
3572
if prec2 is None: prec2 = max(2, prec - 20)
3573
return [simplest_rational_poly(g,prec2) for g in v]
3574
3575
@cached_method
3576
def _square_roots_mod_2N_of_D_mod_4N(self):
3577
"""
3578
Return the square roots of `D` modulo `4N` all reduced mod `2N`,
3579
without multiplicity.
3580
3581
EXAMPLES::
3582
3583
sage: E = EllipticCurve('37a'); P = E.heegner_point(-40); P
3584
Heegner point of discriminant -40 on elliptic curve of conductor 37
3585
sage: P._square_roots_mod_2N_of_D_mod_4N()
3586
[16, 58]
3587
sage: parent(P._square_roots_mod_2N_of_D_mod_4N()[0])
3588
Ring of integers modulo 74
3589
"""
3590
N = self.__E.conductor()
3591
R = Integers(4*N)
3592
m = 2*N
3593
return sorted( set([a%m for a in R(self.discriminant()).sqrt(all=True)]) )
3594
3595
def _trace_numerical_conductor_1(self, prec=53):
3596
"""
3597
Return numerical approximation using ``prec`` terms of working
3598
precision to the trace down to the quadratic imaginary field
3599
`K` of this Heegner point.
3600
3601
INPUT:
3602
3603
- `prec` -- bits precision (default: 53)
3604
3605
EXAMPLES::
3606
3607
sage: E = EllipticCurve('57a1')
3608
sage: P = E.heegner_point(-8); P
3609
Heegner point of discriminant -8 on elliptic curve of conductor 57
3610
sage: P._trace_numerical_conductor_1() # approx. (1 : 0 : 1)
3611
(1.00000000000000 + ...e-16*I : ...e-16 - ...e-16*I : 1.00000000000000)
3612
sage: P = E(2,1) # a generator
3613
sage: E([1,0]).height()
3614
0.150298370947295
3615
sage: P.height()
3616
0.0375745927368238
3617
sage: E.heegner_index(-8)
3618
2.0000?
3619
sage: E.torsion_order()
3620
1
3621
sage: 2*P
3622
(1 : 0 : 1)
3623
"""
3624
if self.conductor() != 1:
3625
raise ValueError("conductor must be 1")
3626
R, U = self._good_tau_representatives()
3627
E = self.__E
3628
phi = E.modular_parametrization()
3629
C = rings.ComplexField(prec)
3630
F = E.change_ring(C)
3631
s = 0
3632
for u, weight in U:
3633
P = phi(C(self._qf_to_tau(u)))
3634
z = F.point(list(P),check=False)
3635
if abs(weight) == 2:
3636
t = F.point(z,check=False) + F.point(tuple([x.conjugate() for x in z]), check=False)
3637
if weight < 0:
3638
s -= t
3639
else:
3640
s += t
3641
else:
3642
if weight < 0:
3643
s -= z
3644
else:
3645
s += z
3646
return s
3647
3648
@cached_method
3649
def _good_tau_representatives(self):
3650
"""
3651
Return good upper half plane representatives for Heegner points.
3652
3653
ALGORITHM: This is Algorithm 3.5 in Watkins's paper.
3654
3655
EXAMPLES::
3656
3657
sage: P = EllipticCurve('389a1').heegner_point(-7)
3658
sage: P._good_tau_representatives()
3659
([(1, 1, 2)], [((389, 185, 22), 1)])
3660
"""
3661
if self.conductor() != 1: raise NotImplementedError
3662
E = self.__E
3663
SDN = self._square_roots_mod_2N_of_D_mod_4N()
3664
beta = SDN[0]
3665
U = []
3666
R = []
3667
N = self.__E.conductor()
3668
D = self.discriminant()
3669
h = self.ring_class_field().degree_over_K()
3670
divs = D.gcd(N).divisors()
3671
a = 1
3672
while True:
3673
for b in SDN:
3674
b = b.lift()
3675
# todo (optimize) -- replace for over all s with for over solution
3676
# set that can be found quickly.
3677
y = ZZ((b*b - D)/(4*N))
3678
for s in Integers(a):
3679
if N*s*s + b*s + y == 0:
3680
s = s.lift()
3681
f = (a*N, b+2*N*s, ZZ( ((b + 2*N*s)**2 - D)/(4*a*N)) )
3682
for d in divs:
3683
Q = d * prod(p**k for p,k in N.factor() if (b-beta)%(p**k)!=0)
3684
g = self._qf_atkin_lehner_act(Q, f)
3685
gbar = (ZZ(g[0]/N), -g[1], g[2]*N)
3686
g = self._qf_reduce(g)
3687
gbar = self._qf_reduce(gbar)
3688
if g in R or gbar in R:
3689
continue
3690
R.append(g)
3691
if g != gbar:
3692
R.append(gbar)
3693
epsilon_Q = prod([E.root_number(q) for q in Q.prime_divisors()])
3694
if g == gbar:
3695
# weight is epsilon_Q
3696
weight = epsilon_Q
3697
else:
3698
# weight is 2*epsilon_Q
3699
weight = 2*epsilon_Q
3700
U.append((f,weight))
3701
if len(R) == h:
3702
return R, U
3703
assert len(R) < h, "bug -- too many quadratic forms"
3704
a += 1
3705
3706
def _qf_to_tau(self, f):
3707
r"""
3708
Function used internally that given a quadratic form
3709
`f=(A,B,C)`, return `\tau` in the upper half plane with
3710
`A\tau^2 + B \tau + C = 0`. Here `A>0` and `\gcd(A,B,C)=1`.
3711
Also, `\tau` has discriminant `D=B^2-4AC`. In fact, `\tau =
3712
(-B + \sqrt{D})/(2A)`.
3713
3714
INPUT:
3715
3716
- `f` -- binary quadratic form
3717
3718
EXAMPLES::
3719
3720
sage: P = EllipticCurve('57a1').heegner_point(-8)
3721
sage: R, U = P._good_tau_representatives()
3722
sage: f = U[0][0]; f
3723
(57, 26, 3)
3724
sage: P._qf_to_tau(f)
3725
1/114*sqrt_minus_8 - 13/57
3726
"""
3727
c = self.conductor()
3728
A,B,_ = f
3729
alpha = c * self.quadratic_field().gen() # this is sqrt(D) = sqrt(c^2*disc(K))
3730
return (-B + alpha)/(2*A)
3731
3732
def _qf_from_tau(self, tau):
3733
r"""
3734
Return quadratic form associated to a given `\tau` in the upper
3735
half plane.
3736
3737
INPUT:
3738
3739
- `\tau` -- quadratic element of the upper half plane
3740
3741
EXAMPLES::
3742
3743
sage: P = EllipticCurve('57a1').heegner_point(-8)
3744
sage: R, U = P._good_tau_representatives()
3745
sage: f = U[0][0]; f
3746
(57, 26, 3)
3747
sage: tau = P._qf_to_tau(f); tau
3748
1/114*sqrt_minus_8 - 13/57
3749
sage: P._qf_from_tau(tau)
3750
(57, 26, 3)
3751
"""
3752
g = tau.minpoly()
3753
g *= g.denominator()
3754
return (ZZ(g[2]), ZZ(g[1]), ZZ(g[0]))
3755
3756
3757
def _qf_atkin_lehner_act(self, Q, f):
3758
r"""
3759
Given a positive integer `Q` with `Q | N` and `\gcd(Q, N/Q) =
3760
1`, we compute the quadratic form corresponding to the image
3761
of the `tau` corresponding to `f` under the Atkin-Lehner
3762
operator `W_Q`.
3763
3764
We do this by letting `u,v` be integers such that
3765
`u Q^2 - v N = Q`, and using that `W_Q` sends `\tau`
3766
to `( (u Q \tau + v) / (N \tau + Q) ) / Q`.
3767
3768
INPUT:
3769
3770
- `Q` -- integer that divides the level `N`
3771
3772
- `f` -- quadratic form
3773
3774
OUTPUT:
3775
3776
- quadratic form
3777
3778
EXAMPLES::
3779
3780
sage: P = EllipticCurve('57a1').heegner_point(-8)
3781
sage: R, U = P._good_tau_representatives()
3782
sage: f = U[0][0]; f
3783
(57, 26, 3)
3784
sage: P._qf_atkin_lehner_act(3, f)
3785
(1938, 1204, 187)
3786
sage: g = P._qf_atkin_lehner_act(19, f); g
3787
(114, -64, 9)
3788
sage: h = P._qf_atkin_lehner_act(19, g); h
3789
(7353, -4762, 771)
3790
sage: BinaryQF(f).reduced_form() == BinaryQF(h).reduced_form()
3791
True
3792
"""
3793
N = self.__E.conductor()
3794
g, u, v = arith.xgcd(Q*Q, -N)
3795
assert g == Q
3796
tau = self._qf_to_tau(f)
3797
tau2 = ((u*Q*tau + v) / (N*tau + Q))
3798
return self._qf_from_tau(tau2)
3799
3800
3801
def _qf_reduce(self, f):
3802
"""
3803
Given a binary quadratic form `f` represented as a 3-tuple
3804
(A,B,C), return the reduced binary quadratic form equivalent
3805
to `f`, represented in the same way.
3806
3807
EXAMPLES::
3808
3809
sage: P = EllipticCurve('57a1').heegner_point(-8)
3810
sage: R, U = P._good_tau_representatives()
3811
sage: f = U[0][0]; f
3812
(57, 26, 3)
3813
sage: P._qf_reduce(f)
3814
(1, 0, 2)
3815
"""
3816
return tuple(BinaryQF(f).reduced_form())
3817
3818
def kolyvagin_cohomology_class(self, n=None):
3819
"""
3820
Return the Kolyvagin class associated to this Heegner point.
3821
3822
INPUT:
3823
3824
- `n` -- positive integer that divides the gcd of `a_p`
3825
and `p+1` for all `p` dividing the conductor. If `n` is
3826
``None``, choose the largest valid `n`.
3827
3828
EXAMPLES::
3829
3830
sage: y = EllipticCurve('389a').heegner_point(-7,5)
3831
sage: y.kolyvagin_cohomology_class(3)
3832
Kolyvagin cohomology class c(5) in H^1(K,E[3])
3833
"""
3834
return KolyvaginCohomologyClassEn(self.kolyvagin_point(), n)
3835
3836
#########################################################################################
3837
# Kolyvagin Points P_c
3838
#########################################################################################
3839
class KolyvaginPoint(HeegnerPoint):
3840
"""
3841
A Kolyvagin point.
3842
3843
EXAMPLES:
3844
3845
We create a few Kolyvagin points::
3846
3847
sage: EllipticCurve('11a1').kolyvagin_point(-7)
3848
Kolyvagin point of discriminant -7 on elliptic curve of conductor 11
3849
sage: EllipticCurve('37a1').kolyvagin_point(-7)
3850
Kolyvagin point of discriminant -7 on elliptic curve of conductor 37
3851
sage: EllipticCurve('37a1').kolyvagin_point(-67)
3852
Kolyvagin point of discriminant -67 on elliptic curve of conductor 37
3853
sage: EllipticCurve('389a1').kolyvagin_point(-7, 5)
3854
Kolyvagin point of discriminant -7 and conductor 5 on elliptic curve of conductor 389
3855
3856
One can also associated a Kolyvagin point to a Heegner point::
3857
3858
sage: y = EllipticCurve('37a1').heegner_point(-7); y
3859
Heegner point of discriminant -7 on elliptic curve of conductor 37
3860
sage: y.kolyvagin_point()
3861
Kolyvagin point of discriminant -7 on elliptic curve of conductor 37
3862
3863
TESTS::
3864
3865
sage: y = EllipticCurve('37a1').heegner_point(-7)
3866
sage: type(y)
3867
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPointOnEllipticCurve'>
3868
sage: loads(dumps(y)) == y
3869
True
3870
"""
3871
def __init__(self, heegner_point):
3872
"""
3873
Create a Kolyvagin point.
3874
3875
INPUT:
3876
3877
- ``heegner_point`` -- a Heegner point on some elliptic curve
3878
3879
EXAMPLES:
3880
3881
We directly construct a Kolyvagin point from the KolyvaginPoint class::
3882
3883
sage: y = EllipticCurve('37a1').heegner_point(-7)
3884
sage: sage.schemes.elliptic_curves.heegner.KolyvaginPoint(y)
3885
Kolyvagin point of discriminant -7 on elliptic curve of conductor 37
3886
"""
3887
if not heegner_point.satisfies_kolyvagin_hypothesis():
3888
raise ValueError("Heegner point does not satisfy Kolyvagin hypothesis")
3889
self.__heegner_point = heegner_point
3890
HeegnerPoint.__init__(self, heegner_point.level(), heegner_point.discriminant(),
3891
heegner_point.conductor())
3892
3893
def satisfies_kolyvagin_hypothesis(self, n=None):
3894
r"""
3895
Return ``True`` if this Kolyvagin point satisfies the Heegner
3896
hypothesis for `n`, so that it defines a Galois equivariant
3897
element of `E(K_c)/n E(K_c)`.
3898
3899
EXAMPLES::
3900
3901
sage: y = EllipticCurve('389a').heegner_point(-7,5); P = y.kolyvagin_point()
3902
sage: P.kolyvagin_cohomology_class(3)
3903
Kolyvagin cohomology class c(5) in H^1(K,E[3])
3904
sage: P.satisfies_kolyvagin_hypothesis(3)
3905
True
3906
sage: P.satisfies_kolyvagin_hypothesis(5)
3907
False
3908
sage: P.satisfies_kolyvagin_hypothesis(7)
3909
False
3910
sage: P.satisfies_kolyvagin_hypothesis(11)
3911
False
3912
"""
3913
return self.__heegner_point.satisfies_kolyvagin_hypothesis(n)
3914
3915
def curve(self):
3916
r"""
3917
Return the elliptic curve over `\QQ` on which this Kolyvagin
3918
point sits.
3919
3920
EXAMPLES::
3921
3922
sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67, 3)
3923
sage: P.curve()
3924
Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
3925
"""
3926
return self.__heegner_point.curve()
3927
3928
def heegner_point(self):
3929
"""
3930
This Kolyvagin point `P_c` is associated to some Heegner point
3931
`y_c` via Kolyvagin's construction. This function returns that
3932
point `y_c`.
3933
3934
EXAMPLES::
3935
3936
sage: E = EllipticCurve('37a1')
3937
sage: P = E.kolyvagin_point(-67); P
3938
Kolyvagin point of discriminant -67 on elliptic curve of conductor 37
3939
sage: y = P.heegner_point(); y
3940
Heegner point of discriminant -67 on elliptic curve of conductor 37
3941
sage: y.kolyvagin_point() is P
3942
True
3943
"""
3944
return self.__heegner_point
3945
3946
def _repr_(self):
3947
"""
3948
Return string representation of this Kolyvagin point.
3949
3950
EXAMPLES::
3951
3952
sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67,7); P._repr_()
3953
'Kolyvagin point of discriminant -67 and conductor 7 on elliptic curve of conductor 37'
3954
"""
3955
s = repr(self.__heegner_point)
3956
return s.replace('Heegner','Kolyvagin')
3957
3958
def index(self, *args, **kwds):
3959
"""
3960
Return index of this Kolyvagin point in the full group of
3961
$K_c$ rational points on $E$.
3962
3963
When the conductor is 1, this is computed numerically using
3964
the Gross-Zagier formula and explicit point search, and it may
3965
be off by $2$. See the documentation for ``E.heegner_index``,
3966
where `E` is the curve attached to ``self``.
3967
3968
EXAMPLES::
3969
3970
sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67); P.index()
3971
6
3972
"""
3973
if self.conductor() == 1:
3974
return self.__heegner_point._trace_index(*args, **kwds)
3975
raise NotImplementedError
3976
3977
def numerical_approx(self, prec=53):
3978
"""
3979
Return a numerical approximation to this Kolyvagin point using
3980
prec bits of working precision.
3981
3982
INPUT:
3983
3984
- ``prec`` -- precision in bits (default: 53)
3985
3986
EXAMPLES::
3987
3988
sage: P = EllipticCurve('37a1').kolyvagin_point(-7); P
3989
Kolyvagin point of discriminant -7 on elliptic curve of conductor 37
3990
sage: P.numerical_approx() # approx. (0 : 0 : 1)
3991
(...e-16 - ...e-16*I : ...e-16 + ...e-16*I : 1.00000000000000)
3992
sage: P.numerical_approx(100)[0].abs() < 2.0^-99
3993
True
3994
3995
sage: P = EllipticCurve('389a1').kolyvagin_point(-7, 5); P
3996
Kolyvagin point of discriminant -7 and conductor 5 on elliptic curve of conductor 389
3997
3998
Numerical approximation is only implemented for points of conductor 1::
3999
4000
sage: P.numerical_approx()
4001
Traceback (most recent call last):
4002
...
4003
NotImplementedError
4004
"""
4005
if self.conductor() == 1:
4006
return self.__heegner_point._trace_numerical_conductor_1(prec)
4007
raise NotImplementedError
4008
4009
def point_exact(self, prec=53):
4010
"""
4011
INPUT:
4012
4013
- ``prec`` -- precision in bits (default: 53)
4014
4015
EXAMPLES:
4016
4017
A rank 1 curve::
4018
4019
sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67)
4020
sage: P.point_exact()
4021
(6 : -15 : 1)
4022
sage: P.point_exact(40)
4023
(6 : -15 : 1)
4024
sage: P.point_exact(20)
4025
Traceback (most recent call last):
4026
...
4027
RuntimeError: insufficient precision to find exact point
4028
4029
A rank 0 curve::
4030
4031
sage: E = EllipticCurve('11a1'); P = E.kolyvagin_point(-7)
4032
sage: P.point_exact()
4033
(-1/2*sqrt_minus_7 + 1/2 : -2*sqrt_minus_7 - 2 : 1)
4034
4035
A rank 2 curve::
4036
4037
sage: E = EllipticCurve('389a1'); P = E.kolyvagin_point(-7)
4038
sage: P.point_exact()
4039
(0 : 1 : 0)
4040
4041
"""
4042
if self.conductor() == 1:
4043
# the result is a point defined over K in the conductor 1 case, which is easier.
4044
P = self.numerical_approx(prec)
4045
4046
E = self.curve()
4047
if P[2] == 0:
4048
return E(0)
4049
4050
if E.root_number() == -1:
4051
return self._recognize_point_over_QQ(P, 2*self.index())
4052
else:
4053
# root number +1. We use algdep to recognize the x
4054
# coordinate, stick it in the appropriate quadratic
4055
# field, then make sure that we got the right
4056
# embedding, and if not fix things so we do.
4057
x = P[0]
4058
C = x.parent()
4059
f = x.algdep(2)
4060
K = self.quadratic_field()
4061
roots = [r[0] for r in f.roots(K)]
4062
if len(roots) == 0:
4063
raise RuntimeError("insufficient precision to find exact point")
4064
if len(roots) == 1:
4065
X = roots[0]
4066
else:
4067
d = [abs(C(r) - x) for r in roots]
4068
if d[0] == d[1]:
4069
raise RuntimeError("insufficient precision to distinguish roots")
4070
if d[0] < d[1]:
4071
X = roots[0]
4072
else:
4073
X = roots[1]
4074
F = E.change_ring(K)
4075
Q = F.lift_x(X, all=True)
4076
if len(Q) == 1:
4077
return Q[0]
4078
if len(Q) == 0:
4079
raise RuntimeError("insufficient precision")
4080
y = P[1]
4081
d = [abs(C(r[1])-y) for r in Q]
4082
if d[0] == d[1]:
4083
raise RuntimeError("insufficient precision to distinguish roots")
4084
if d[0] < d[1]:
4085
return Q[0]
4086
else:
4087
return Q[1]
4088
4089
else:
4090
raise NotImplementedError
4091
4092
def plot(self, prec=53, *args, **kwds):
4093
r"""
4094
Plot a Kolyvagin point `P_1` if it is defined over the
4095
rational numbers.
4096
4097
EXAMPLES::
4098
4099
sage: E = EllipticCurve('37a'); P = E.heegner_point(-11).kolyvagin_point()
4100
sage: P.plot(prec=30, pointsize=50, rgbcolor='red') + E.plot()
4101
"""
4102
if self.conductor() != 1:
4103
raise NotImplementedError
4104
4105
E = self.curve()
4106
if E.root_number() == -1:
4107
P = self.numerical_approx(prec=prec)
4108
from sage.plot.all import point, Graphics
4109
if not P:
4110
# point at infinity
4111
return Graphics()
4112
return point((P[0].real(), P[1].real()),*args, **kwds)
4113
else:
4114
raise NotImplementedError
4115
4116
4117
@cached_method
4118
def trace_to_real_numerical(self, prec=53):
4119
"""
4120
Return the trace of this Kolyvagin point down to the real
4121
numbers, computed numerically using prec bits of working
4122
precision.
4123
4124
EXAMPLES::
4125
4126
sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67)
4127
sage: PP = P.numerical_approx(); PP
4128
(6.00000000000000 ... : -15.0000000000000 ... : 1.00000000000000)
4129
sage: [c.real() for c in PP]
4130
[6.00000000000000, -15.0000000000000, 1.00000000000000]
4131
sage: all([c.imag().abs() < 1e-14 for c in PP])
4132
True
4133
sage: P.trace_to_real_numerical()
4134
(1.61355529131986 : -2.18446840788880 : 1.00000000000000)
4135
sage: P.trace_to_real_numerical(prec=80)
4136
(1.6135552913198573127230 : -2.1844684078888023289187 : 1.0000000000000000000000)
4137
4138
"""
4139
# Compute numerical approximation of P in E(K).
4140
P = self.numerical_approx(prec=prec)
4141
# Trace this numerical approximation down to E(Q) (numerically).
4142
E = P.curve()
4143
if self.curve().root_number() == -1:
4144
R = 2*P
4145
else:
4146
R = P + E.point([x.conjugate() for x in P],check=False)
4147
F = self.curve().change_ring(rings.RealField(prec))
4148
return F.point([x.real() for x in R], check=False)
4149
4150
@cached_method
4151
def _trace_exact_conductor_1(self, prec=53):
4152
r"""
4153
Return the trace from `K` to `\QQ` of this Kolyvagin point in
4154
the case of conductor 1, computed using prec bits of
4155
precision, then approximated using some algorithm (e.g.,
4156
continued fractions). If the precision is not enough to
4157
determine a point on the curve, then a RuntimeError is raised.
4158
Even if the precision determines a point, there is no guarantee
4159
that it is correct.
4160
4161
EXAMPLES:
4162
4163
A Kolyvagin point on a rank 1 curve::
4164
4165
sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67)
4166
sage: P.trace_to_real_numerical()
4167
(1.61355529131986 : -2.18446840788880 : 1.00000000000000)
4168
sage: P._trace_exact_conductor_1() # the actual point we're reducing
4169
(1357/841 : -53277/24389 : 1)
4170
sage: (P._trace_exact_conductor_1().height() / E.regulator()).sqrt()
4171
12.0000000000000
4172
"""
4173
if not self.conductor() == 1:
4174
raise ValueError("the conductor must be 1")
4175
4176
P = self.trace_to_real_numerical(prec)
4177
return self._recognize_point_over_QQ(P, 2*self.index())
4178
4179
def _recognize_point_over_QQ(self, P, n):
4180
"""
4181
Used internally when computing an exact point on an elliptic curve.
4182
4183
INPUT:
4184
4185
- `P` -- numerical approximation for a point on `E`
4186
4187
- `n` -- upper bound on divisibility index of `P` in group `E(\QQ)`
4188
4189
EXAMPLES::
4190
4191
sage: E = EllipticCurve('43a'); P = E.heegner_point(-20).kolyvagin_point()
4192
sage: PP = P.numerical_approx(); PP
4193
(...e-16 : -1.00000000000000 : 1.00000000000000)
4194
sage: P._recognize_point_over_QQ(PP, 4)
4195
(0 : -1 : 1)
4196
"""
4197
# Here is where we *should* implement the "method of Cremona
4198
# etc" mentioned in Watkins' article... which involves local
4199
# heights.
4200
E = self.curve() # over Q
4201
v = sum([list(n*w) for w in E.gens()] + [list(w) for w in E.torsion_points()], [])
4202
# note -- we do not claim to prove anything, so making up a factor of 100 is fine.
4203
max_denominator = 100*max([z.denominator() for z in v])
4204
try:
4205
# the coercion below also checks if point is on elliptic curve
4206
return E([x.real().nearby_rational(max_denominator=max_denominator) for x in P])
4207
except TypeError:
4208
raise RuntimeError("insufficient precision to find exact point")
4209
4210
def mod(self, p, prec=53):
4211
r"""
4212
Return the trace of the reduction `Q` modulo a prime over `p` of this
4213
Kolyvagin point as an element of `E(\GF{p})`, where
4214
`p` is any prime that is inert in `K` that is coprime to `NDc`.
4215
4216
The point `Q` is only well defined up to an element of
4217
`(p+1) E(\GF{p})`, i.e., it gives a well defined element
4218
of the abelian group `E(\GF{p}) / (p+1) E(\GF{p})`.
4219
4220
See [SteinToward]_, Proposition 5.4 for a proof of the above
4221
well-definedness assertion.
4222
4223
EXAMPLES:
4224
4225
A Kolyvagin point on a rank 1 curve::
4226
4227
sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67)
4228
sage: P.mod(2)
4229
(1 : 1 : 1)
4230
sage: P.mod(3)
4231
(1 : 0 : 1)
4232
sage: P.mod(5)
4233
(2 : 2 : 1)
4234
sage: P.mod(7)
4235
(6 : 0 : 1)
4236
sage: P.trace_to_real_numerical()
4237
(1.61355529131986 : -2.18446840788880 : 1.00000000000000)
4238
sage: P._trace_exact_conductor_1() # the actual point we're reducing
4239
(1357/841 : -53277/24389 : 1)
4240
sage: (P._trace_exact_conductor_1().height() / E.regulator()).sqrt()
4241
12.0000000000000
4242
4243
Here the Kolyvagin point is a torsion point (since `E` has
4244
rank 1), and we reduce it modulo several primes.::
4245
4246
sage: E = EllipticCurve('11a1'); P = E.kolyvagin_point(-7)
4247
sage: P.mod(3,70) # long time (4s on sage.math, 2013)
4248
(1 : 2 : 1)
4249
sage: P.mod(5,70)
4250
(1 : 4 : 1)
4251
sage: P.mod(7,70)
4252
Traceback (most recent call last):
4253
...
4254
ValueError: p must be coprime to conductors and discriminant
4255
sage: P.mod(11,70)
4256
Traceback (most recent call last):
4257
...
4258
ValueError: p must be coprime to conductors and discriminant
4259
sage: P.mod(13,70)
4260
(3 : 4 : 1)
4261
4262
REFERENCES:
4263
4264
.. [SteinToward] Stein, "Toward a Generalization of the Gross-Zagier Conjecture", Int Math Res Notices (2011), :doi:`10.1093/imrn/rnq075`
4265
"""
4266
# check preconditions
4267
p = ZZ(p)
4268
if not p.is_prime():
4269
raise ValueError("p must be prime")
4270
E = self.curve()
4271
D = self.discriminant()
4272
if (E.conductor() * D * self.conductor()) % p == 0:
4273
raise ValueError("p must be coprime to conductors and discriminant")
4274
K = self.heegner_point().quadratic_field()
4275
if len(K.factor(p)) != 1:
4276
raise ValueError("p must be inert")
4277
4278
# do actual calculation
4279
if self.conductor() == 1:
4280
4281
P = self._trace_exact_conductor_1(prec = prec)
4282
return E.change_ring(GF(p))(P)
4283
4284
else:
4285
4286
raise NotImplementedError
4287
4288
## def congruent_rational_point(self, n, prec=53):
4289
## r"""
4290
## Let `P` be this Kolyvagin point. Determine whether there is a
4291
## point `z` in `E(\QQ)` such that `z - P \in n E(K_c)`, where `K_c`
4292
## is the ring class field over which this Kolyvagin point is defined.
4293
## If `z` exists return `z`. Otherwise return None.
4294
##
4295
## INPUT:
4296
##
4297
## - `n` -- positive integer
4298
##
4299
## - ``prec`` -- positive integer (default: 53)
4300
##
4301
##
4302
## EXAMPLES::
4303
##
4304
## """
4305
## raise NotImplementedError
4306
4307
4308
def kolyvagin_cohomology_class(self, n=None):
4309
"""
4310
INPUT:
4311
4312
- `n` -- positive integer that divides the gcd of `a_p`
4313
and `p+1` for all `p` dividing the conductor. If `n` is
4314
``None``, choose the largest valid `n`.
4315
4316
EXAMPLES::
4317
4318
sage: y = EllipticCurve('389a').heegner_point(-7,5)
4319
sage: P = y.kolyvagin_point()
4320
sage: P.kolyvagin_cohomology_class(3)
4321
Kolyvagin cohomology class c(5) in H^1(K,E[3])
4322
4323
sage: y = EllipticCurve('37a').heegner_point(-7,5).kolyvagin_point()
4324
sage: y.kolyvagin_cohomology_class()
4325
Kolyvagin cohomology class c(5) in H^1(K,E[2])
4326
"""
4327
return KolyvaginCohomologyClassEn(self, n)
4328
4329
4330
class KolyvaginCohomologyClass(SageObject):
4331
"""
4332
A Kolyvagin cohomology class in `H^1(K,E[n])` or `H^1(K,E)[n]`
4333
attached to a Heegner point.
4334
4335
EXAMPLES::
4336
4337
sage: y = EllipticCurve('37a').heegner_point(-7)
4338
sage: c = y.kolyvagin_cohomology_class(3); c
4339
Kolyvagin cohomology class c(1) in H^1(K,E[3])
4340
sage: type(c)
4341
<class 'sage.schemes.elliptic_curves.heegner.KolyvaginCohomologyClassEn'>
4342
sage: loads(dumps(c)) == c
4343
True
4344
sage: y.kolyvagin_cohomology_class(5)
4345
Kolyvagin cohomology class c(1) in H^1(K,E[5])
4346
"""
4347
def __init__(self, kolyvagin_point, n):
4348
"""
4349
4350
EXAMPLES::
4351
4352
sage: y = EllipticCurve('389a').heegner_point(-7,5)
4353
sage: y.kolyvagin_cohomology_class(3)
4354
Kolyvagin cohomology class c(5) in H^1(K,E[3])
4355
"""
4356
if n is None:
4357
c = kolyvagin_point.conductor()
4358
E = kolyvagin_point.curve()
4359
n = arith.GCD([(p+1).gcd(E.ap(p)) for p in c.prime_divisors()])
4360
4361
if not kolyvagin_point.satisfies_kolyvagin_hypothesis(n):
4362
raise ValueError("Kolyvagin point does not satisfy Kolyvagin hypothesis for %s"%n)
4363
self.__kolyvagin_point = kolyvagin_point
4364
self.__n = n
4365
4366
def __eq__(self, other):
4367
"""
4368
EXAMPLES:
4369
sage: y = EllipticCurve('37a').heegner_point(-7)
4370
sage: c = y.kolyvagin_cohomology_class(3)
4371
sage: c == y.kolyvagin_cohomology_class(3)
4372
True
4373
sage: c == y.kolyvagin_cohomology_class(5)
4374
False
4375
4376
This does not mean that c is nonzero (!) -- it just means c is not the number 0::
4377
4378
sage: c == 0
4379
False
4380
"""
4381
return isinstance(other, KolyvaginCohomologyClass) and \
4382
self.__kolyvagin_point == other.__kolyvagin_point and \
4383
self.__n == other.__n
4384
4385
4386
def n(self):
4387
"""
4388
Return the integer `n` so that this is a cohomology class in
4389
`H^1(K,E[n])` or `H^1(K,E)[n]`.
4390
4391
EXAMPLES::
4392
4393
sage: y = EllipticCurve('37a').heegner_point(-7)
4394
sage: t = y.kolyvagin_cohomology_class(3); t
4395
Kolyvagin cohomology class c(1) in H^1(K,E[3])
4396
sage: t.n()
4397
3
4398
"""
4399
return self.__n
4400
4401
def conductor(self):
4402
r"""
4403
Return the integer `c` such that this cohomology class is associated
4404
to the Heegner point `y_c`.
4405
4406
EXAMPLES::
4407
4408
sage: y = EllipticCurve('37a').heegner_point(-7,5)
4409
sage: t = y.kolyvagin_cohomology_class()
4410
sage: t.conductor()
4411
5
4412
"""
4413
return self.__kolyvagin_point.conductor()
4414
4415
def kolyvagin_point(self):
4416
"""
4417
Return the Kolyvagin point `P_c` to which this cohomology
4418
class is associated.
4419
4420
EXAMPLES::
4421
4422
sage: y = EllipticCurve('37a').heegner_point(-7,5)
4423
sage: t = y.kolyvagin_cohomology_class()
4424
sage: t.kolyvagin_point()
4425
Kolyvagin point of discriminant -7 and conductor 5 on elliptic curve of conductor 37
4426
"""
4427
return self.__kolyvagin_point
4428
4429
def heegner_point(self):
4430
"""
4431
Return the Heegner point `y_c` to which this cohomology class
4432
is associated.
4433
4434
EXAMPLES::
4435
4436
sage: y = EllipticCurve('37a').heegner_point(-7,5)
4437
sage: t = y.kolyvagin_cohomology_class()
4438
sage: t.heegner_point()
4439
Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 37
4440
"""
4441
return self.__kolyvagin_point.heegner_point()
4442
4443
class KolyvaginCohomologyClassEn(KolyvaginCohomologyClass):
4444
"""
4445
4446
EXAMPLES:
4447
4448
"""
4449
def _repr_(self):
4450
"""
4451
4452
EXAMPLES::
4453
4454
sage: y = EllipticCurve('37a').heegner_point(-7,5)
4455
sage: t = y.kolyvagin_cohomology_class()
4456
sage: t._repr_()
4457
'Kolyvagin cohomology class c(5) in H^1(K,E[2])'
4458
"""
4459
return "Kolyvagin cohomology class c(%s) in H^1(K,E[%s])"%(
4460
self.conductor(), self.n())
4461
4462
4463
#############################################################################
4464
# Reduction of Heegner points using Quaternion Algebras
4465
#
4466
# This section contains implementations of algorithms for computing
4467
# information about reduction modulo primes of Heegner points using
4468
# quaternion algebras. Some of this code could later be moved to the
4469
# quaternion algebras code, but it is too immature and not general
4470
# enough at present for that.
4471
#############################################################################
4472
4473
class HeegnerQuatAlg(SageObject):
4474
r"""
4475
Heegner points viewed as supersingular points on the modular curve
4476
`X_0(N)/\mathbf{F}_{\ell}`.
4477
4478
EXAMPLES::
4479
4480
sage: H = heegner_points(11).reduce_mod(13); H
4481
Heegner points on X_0(11) over F_13
4482
sage: type(H)
4483
<class 'sage.schemes.elliptic_curves.heegner.HeegnerQuatAlg'>
4484
sage: loads(dumps(H)) == H
4485
True
4486
"""
4487
def __init__(self, level, ell):
4488
r"""
4489
INPUT:
4490
4491
- ``level`` -- the level (a positive integer)
4492
4493
- `\ell` -- the characteristic, a prime coprime to the level
4494
4495
EXAMPLES::
4496
4497
sage: sage.schemes.elliptic_curves.heegner.HeegnerQuatAlg(11, 13)
4498
Heegner points on X_0(11) over F_13
4499
"""
4500
level = ZZ(level); ell = ZZ(ell)
4501
if not ell.is_prime():
4502
raise ValueError("ell must be prime")
4503
if level.gcd(ell) != 1:
4504
raise ValueError("level and ell must be coprime")
4505
self.__level = level
4506
self.__ell = ell
4507
4508
def __eq__(self, other):
4509
"""
4510
EXAMPLES::
4511
4512
sage: H = heegner_points(11).reduce_mod(3)
4513
sage: H == heegner_points(11).reduce_mod(3)
4514
True
4515
sage: H == heegner_points(11).reduce_mod(5)
4516
False
4517
sage: H == 0
4518
False
4519
"""
4520
return isinstance(other, HeegnerQuatAlg) and self.__level == other.__level \
4521
and self.__ell == other.__ell
4522
4523
def _repr_(self):
4524
"""
4525
Return string representation.
4526
4527
EXAMPLES::
4528
4529
sage: heegner_points(11).reduce_mod(13)._repr_()
4530
'Heegner points on X_0(11) over F_13'
4531
"""
4532
return "Heegner points on X_0(%s) over F_%s"%(
4533
self.__level, self.__ell)
4534
4535
def level(self):
4536
"""
4537
Return the level.
4538
4539
EXAMPLES::
4540
4541
sage: heegner_points(11).reduce_mod(3).level()
4542
11
4543
"""
4544
return self.__level
4545
4546
def ell(self):
4547
r"""
4548
Return the prime `\ell` modulo which we are working.
4549
4550
EXAMPLES::
4551
4552
sage: heegner_points(11).reduce_mod(3).ell()
4553
3
4554
"""
4555
return self.__ell
4556
4557
def satisfies_heegner_hypothesis(self, D, c=ZZ(1)):
4558
r"""
4559
The fundamental discriminant `D` must be coprime to `N\ell`,
4560
and must define a quadratic imaginary field `K` in which `\ell`
4561
is inert. Also, all primes dividing `N` must split in `K`,
4562
and `c` must be squarefree and coprime to `ND\ell`.
4563
4564
INPUT:
4565
4566
- `D` -- negative integer
4567
4568
- `c` -- positive integer (default: 1)
4569
4570
OUTPUT:
4571
4572
- bool
4573
4574
EXAMPLES::
4575
4576
sage: H = heegner_points(11).reduce_mod(7)
4577
sage: H.satisfies_heegner_hypothesis(-5)
4578
False
4579
sage: H.satisfies_heegner_hypothesis(-7)
4580
False
4581
sage: H.satisfies_heegner_hypothesis(-8)
4582
True
4583
sage: [D for D in [-1,-2..-100] if H.satisfies_heegner_hypothesis(D)]
4584
[-8, -39, -43, -51, -79, -95]
4585
"""
4586
D = ZZ(D); c = ZZ(c)
4587
if arith.gcd(c*D, self.__level*self.__ell) != 1 or arith.gcd(c,D) != 1:
4588
return False
4589
if not satisfies_weak_heegner_hypothesis(self.__level, D):
4590
return False
4591
if not is_inert(D, self.__ell):
4592
return False
4593
return True
4594
4595
def heegner_discriminants(self, n=5):
4596
r"""
4597
Return the first `n` negative fundamental discriminants
4598
coprime to `N\ell` such that `\ell` is inert in the
4599
corresponding quadratic imaginary field and that field
4600
satisfies the Heegner hypothesis, and `N` is the level.
4601
4602
INPUT:
4603
4604
- `n` -- positive integer (default: 5)
4605
4606
OUTPUT:
4607
4608
- list
4609
4610
EXAMPLES::
4611
4612
sage: H = heegner_points(11).reduce_mod(3)
4613
sage: H.heegner_discriminants()
4614
[-7, -19, -40, -43, -52]
4615
sage: H.heegner_discriminants(10)
4616
[-7, -19, -40, -43, -52, -79, -127, -139, -151, -184]
4617
"""
4618
v = []
4619
D = ZZ(-5)
4620
while len(v) < n:
4621
if self.satisfies_heegner_hypothesis(D):
4622
v.append(D)
4623
D -= 1
4624
return v
4625
4626
def heegner_conductors(self, D, n=5):
4627
r"""
4628
Return the first `n` negative fundamental discriminants
4629
coprime to `N\ell` such that `\ell` is inert in the
4630
corresponding quadratic imaginary field and that field
4631
satisfies the Heegner hypothesis.
4632
4633
INPUT:
4634
4635
- `D` -- negative integer; a fundamental Heegner
4636
discriminant
4637
4638
- `n` -- positive integer (default: 5)
4639
4640
OUTPUT:
4641
4642
- list
4643
4644
EXAMPLES::
4645
4646
sage: H = heegner_points(11).reduce_mod(3)
4647
sage: H.heegner_conductors(-7)
4648
[1, 2, 4, 5, 8]
4649
sage: H.heegner_conductors(-7, 10)
4650
[1, 2, 4, 5, 8, 10, 13, 16, 17, 19]
4651
"""
4652
v = [ZZ(1)]
4653
c = ZZ(2)
4654
while len(v) < n:
4655
if self.satisfies_heegner_hypothesis(D, c):
4656
v.append(c)
4657
c += 1
4658
return v
4659
4660
4661
def optimal_embeddings(self, D, c, R):
4662
"""
4663
INPUT:
4664
4665
- `D` -- negative fundamental disriminant
4666
4667
- `c` -- integer coprime
4668
4669
- `R` -- Eichler order
4670
4671
EXAMPLES::
4672
4673
sage: H = heegner_points(11).reduce_mod(3)
4674
sage: R = H.left_orders()[0]
4675
sage: H.optimal_embeddings(-7, 1, R)
4676
[Embedding sending sqrt(-7) to -i + j + k,
4677
Embedding sending sqrt(-7) to i - j - k]
4678
sage: H.optimal_embeddings(-7, 2, R)
4679
[Embedding sending 2*sqrt(-7) to -5*i + k,
4680
Embedding sending 2*sqrt(-7) to 5*i - k,
4681
Embedding sending 2*sqrt(-7) to -2*i + 2*j + 2*k,
4682
Embedding sending 2*sqrt(-7) to 2*i - 2*j - 2*k]
4683
"""
4684
Q, G = R.ternary_quadratic_form(include_basis=True)
4685
n = -D*c*c
4686
reps = Q.representation_vector_list(n+1)[-1]
4687
4688
# The representatives give elements in terms of the
4689
# subspace's basis such that the embedding is given by
4690
# phi(c*sqrt(D)) = beta
4691
E = []
4692
for r in reps:
4693
beta = sum(G[i]*r[i] for i in range(len(G)))
4694
phi = HeegnerQuatAlgEmbedding(D, c, R, beta)
4695
E.append(phi)
4696
return E
4697
4698
@cached_method
4699
def brandt_module(self):
4700
"""
4701
Return the Brandt module of right ideal classes that we
4702
used to represent the set of supersingular points on
4703
the modular curve.
4704
4705
EXAMPLES::
4706
4707
sage: heegner_points(11).reduce_mod(3).brandt_module()
4708
Brandt module of dimension 2 of level 3*11 of weight 2 over Rational Field
4709
"""
4710
from sage.modular.quatalg.all import BrandtModule
4711
return BrandtModule(self.__ell, self.__level)
4712
4713
@cached_method
4714
def quaternion_algebra(self):
4715
"""
4716
Return the rational quaternion algebra used to implement self.
4717
4718
EXAMPLES::
4719
4720
sage: heegner_points(389).reduce_mod(7).quaternion_algebra()
4721
Quaternion Algebra (-1, -7) with base ring Rational Field
4722
"""
4723
return self.brandt_module().quaternion_algebra()
4724
4725
def right_ideals(self):
4726
"""
4727
Return representative right ideals in the Brandt module.
4728
4729
EXAMPLES::
4730
4731
sage: heegner_points(11).reduce_mod(3).right_ideals()
4732
(Fractional ideal (2 + 2*j + 28*k, 2*i + 26*k, 4*j + 12*k, 44*k),
4733
Fractional ideal (2 + 2*j + 28*k, 2*i + 4*j + 38*k, 8*j + 24*k, 88*k))
4734
"""
4735
return self.brandt_module().right_ideals()
4736
4737
@cached_method
4738
def left_orders(self):
4739
"""
4740
Return the left orders associated to the representative right
4741
ideals in the Brandt module.
4742
4743
EXAMPLES::
4744
4745
sage: heegner_points(11).reduce_mod(3).left_orders()
4746
[Order of Quaternion Algebra (-1, -3) with base ring Rational Field with basis (1/2 + 1/2*j + 7*k, 1/2*i + 13/2*k, j + 3*k, 11*k),
4747
Order of Quaternion Algebra (-1, -3) with base ring Rational Field with basis (1/2 + 1/2*j + 7*k, 1/4*i + 1/2*j + 63/4*k, j + 14*k, 22*k)]
4748
"""
4749
return [I.left_order() for I in self.right_ideals()]
4750
4751
@cached_method
4752
def heegner_divisor(self, D, c=ZZ(1)):
4753
r"""
4754
Return Heegner divisor as an element of the Brandt module
4755
corresponding to the discriminant `D` and conductor `c`, which
4756
both must be coprime to `N\ell`.
4757
4758
More precisely, we compute the sum of the reductions of the
4759
`\textrm{Gal}(K_1/K)`-conjugates of each choice of `y_1`,
4760
where the choice comes from choosing the ideal `\mathcal{N}`.
4761
Then we apply the Hecke operator `T_c` to this sum.
4762
4763
INPUT:
4764
4765
- `D` -- discriminant (negative integer)
4766
4767
- `c` -- conductor (positive integer)
4768
4769
OUTPUT:
4770
4771
- Brandt module element
4772
4773
EXAMPLES::
4774
4775
sage: H = heegner_points(11).reduce_mod(7)
4776
sage: H.heegner_discriminants()
4777
[-8, -39, -43, -51, -79]
4778
sage: H.heegner_divisor(-8)
4779
(1, 0, 0, 1, 0, 0)
4780
sage: H.heegner_divisor(-39)
4781
(1, 2, 2, 1, 2, 0)
4782
sage: H.heegner_divisor(-43)
4783
(1, 0, 0, 1, 0, 0)
4784
sage: H.heegner_divisor(-51)
4785
(1, 0, 0, 1, 0, 2)
4786
sage: H.heegner_divisor(-79)
4787
(3, 2, 2, 3, 0, 0)
4788
4789
sage: sum(H.heegner_divisor(-39).element())
4790
8
4791
sage: QuadraticField(-39,'a').class_number()
4792
4
4793
"""
4794
if not self.satisfies_heegner_hypothesis(D, c):
4795
raise ValueError("D and c must be coprime to N and ell")
4796
4797
B = self.brandt_module()
4798
4799
if c > 1:
4800
# Just apply T_c to divisor for c=1
4801
z = self.heegner_divisor(D)
4802
return B.hecke_operator(c)(z)
4803
4804
n = -D
4805
v = [0]*B.degree()
4806
for i, R in enumerate(self.left_orders()):
4807
Q = R.ternary_quadratic_form()
4808
a = Q.theta_series(n+1)[n]
4809
if a > 0:
4810
reps = Q.representation_vector_list(n+1)[-1]
4811
k = len([r for r in reps if arith.gcd(r) == 1])
4812
assert k%2 == 0
4813
v[i] += k/2
4814
return B(v)
4815
4816
@cached_method
4817
def modp_splitting_data(self, p):
4818
r"""
4819
Return mod `p` splitting data for the quaternion algebra at the
4820
unramified prime `p`. This is a pair of `2\times 2` matrices
4821
`A`, `B` over the finite field `\GF{p}` such that if the
4822
quaternion algebra has generators `i, j, k`, then the
4823
homomorphism sending `i` to `A` and `j` to `B` maps any
4824
maximal order homomorphically onto the ring of `2\times 2` matrices.
4825
4826
Because of how the homomorphism is defined, we must assume that the
4827
prime `p` is odd.
4828
4829
INPUT:
4830
4831
- `p` -- unramified odd prime
4832
4833
OUTPUT:
4834
4835
- 2-tuple of matrices over finite field
4836
4837
EXAMPLES::
4838
4839
sage: H = heegner_points(11).reduce_mod(7)
4840
sage: H.quaternion_algebra()
4841
Quaternion Algebra (-1, -7) with base ring Rational Field
4842
sage: I, J = H.modp_splitting_data(13)
4843
sage: I
4844
[ 0 12]
4845
[ 1 0]
4846
sage: J
4847
[7 3]
4848
[3 6]
4849
sage: I^2
4850
[12 0]
4851
[ 0 12]
4852
sage: J^2
4853
[6 0]
4854
[0 6]
4855
sage: I*J == -J*I
4856
True
4857
4858
The following is a good test because of the asserts in the code::
4859
4860
sage: v = [H.modp_splitting_data(p) for p in primes(13,200)]
4861
4862
Some edge cases::
4863
4864
sage: H.modp_splitting_data(11)
4865
(
4866
[ 0 10] [6 1]
4867
[ 1 0], [1 5]
4868
)
4869
4870
Proper error handling::
4871
4872
sage: H.modp_splitting_data(7)
4873
Traceback (most recent call last):
4874
...
4875
ValueError: p (=7) must be an unramified prime
4876
4877
sage: H.modp_splitting_data(2)
4878
Traceback (most recent call last):
4879
...
4880
ValueError: p must be odd
4881
"""
4882
p = ZZ(p)
4883
if not p.is_prime():
4884
raise ValueError("p (=%s) must be prime"%p)
4885
if p == 2:
4886
raise ValueError("p must be odd")
4887
Q = self.quaternion_algebra()
4888
if Q.discriminant() % p == 0:
4889
raise ValueError("p (=%s) must be an unramified prime"%p)
4890
i, j, k = Q.gens()
4891
F = GF(p)
4892
i2 = F(i*i)
4893
j2 = F(j*j)
4894
M = MatrixSpace(F, 2)
4895
I = M([0,i2,1,0])
4896
i2inv = 1/i2
4897
a = None
4898
#for b in reversed(list(F)):
4899
for b in list(F):
4900
if not b: continue
4901
c = j2 + i2inv * b*b
4902
if c.is_square():
4903
a = -c.sqrt()
4904
break
4905
assert a is not None, "bug in that no splitting solution found"
4906
J = M([a,b,(j2-a*a)/b, -a])
4907
assert I*J == -J*I, "bug in that I,J do not skew commute"
4908
return I, J
4909
4910
def modp_splitting_map(self, p):
4911
r"""
4912
Return (algebra) map from the (`p`-integral) quaternion algebra to
4913
the set of `2\times 2` matrices over `\GF{p}`.
4914
4915
INPUT:
4916
4917
- `p` -- prime number
4918
4919
EXAMPLES::
4920
4921
sage: H = heegner_points(11).reduce_mod(7)
4922
sage: f = H.modp_splitting_map(13)
4923
sage: B = H.quaternion_algebra(); B
4924
Quaternion Algebra (-1, -7) with base ring Rational Field
4925
sage: i,j,k = H.quaternion_algebra().gens()
4926
sage: a = 2+i-j+3*k; b = 7+2*i-4*j+k
4927
sage: f(a*b)
4928
[12 3]
4929
[10 5]
4930
sage: f(a)*f(b)
4931
[12 3]
4932
[10 5]
4933
"""
4934
I, J = self.modp_splitting_data(p)
4935
K = I*J
4936
F = I.base_ring()
4937
def phi(q):
4938
v = [F(a) for a in q.coefficient_tuple()]
4939
return v[0] + I*v[1] + J*v[2] + K*v[3]
4940
return phi
4941
4942
def cyclic_subideal_p1(self, I, c):
4943
r"""
4944
Compute dictionary mapping 2-tuples that defined normalized
4945
elements of `P^1(\ZZ/c\ZZ)`
4946
4947
INPUT:
4948
4949
- `I` -- right ideal of Eichler order or in quaternion algebra
4950
4951
- `c` -- square free integer (currently must be odd prime
4952
and coprime to level, discriminant, characteristic,
4953
etc.
4954
4955
OUTPUT:
4956
4957
- dictionary mapping 2-tuples (u,v) to ideals
4958
4959
EXAMPLES::
4960
4961
sage: H = heegner_points(11).reduce_mod(7)
4962
sage: I = H.brandt_module().right_ideals()[0]
4963
sage: sorted(H.cyclic_subideal_p1(I,3).iteritems())
4964
[((0, 1),
4965
Fractional ideal (2 + 2*j + 32*k, 2*i + 8*j + 82*k, 12*j + 60*k, 132*k)),
4966
((1, 0),
4967
Fractional ideal (2 + 10*j + 28*k, 2*i + 4*j + 62*k, 12*j + 60*k, 132*k)),
4968
((1, 1),
4969
Fractional ideal (2 + 2*j + 76*k, 2*i + 4*j + 106*k, 12*j + 60*k, 132*k)),
4970
((1, 2),
4971
Fractional ideal (2 + 10*j + 116*k, 2*i + 8*j + 38*k, 12*j + 60*k, 132*k))]
4972
sage: len(H.cyclic_subideal_p1(I,17))
4973
18
4974
"""
4975
c = ZZ(c)
4976
if not c.is_prime():
4977
raise NotImplementedError("currently c must be prime")
4978
if c == 2:
4979
raise NotImplementedError("currently c must be odd")
4980
phi = self.modp_splitting_map(c)
4981
B = self.brandt_module()
4982
P1 = P1List(c)
4983
ans = {}
4984
# Actually they are submodules despite the name below.
4985
for J in B.cyclic_submodules(I, c):
4986
B = J.basis()
4987
V = phi(B[0]).kernel()
4988
for i in [1,2,3]:
4989
V = V.intersection(phi(B[i]).kernel())
4990
b = V.basis()
4991
assert len(b) == 1, "common kernel must have dimension 1"
4992
uv = P1.normalize(ZZ(b[0][0])%c, ZZ(b[0][1])%c)
4993
ans[uv] = J
4994
assert len(ans) == c+1
4995
return ans
4996
4997
@cached_method
4998
def galois_group_over_hilbert_class_field(self, D, c):
4999
"""
5000
Return the Galois group of the extension of ring class fields
5001
`K_c` over the Hilbert class field `K_{1}` of the quadratic
5002
imaginary field of discriminant `D`.
5003
5004
INPUT:
5005
5006
- `D` -- fundamental discriminant
5007
5008
- `c` -- conductor (square-free integer)
5009
5010
EXAMPLES::
5011
5012
sage: N = 37; D = -7; ell = 17; c = 41; p = 3
5013
sage: H = heegner_points(N).reduce_mod(ell)
5014
sage: H.galois_group_over_hilbert_class_field(D, c)
5015
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 41 over Hilbert class field of QQ[sqrt(-7)]
5016
"""
5017
Kc = heegner_points(self.level(), D, c).ring_class_field()
5018
K1 = heegner_points(self.level(), D, 1).ring_class_field()
5019
return Kc.galois_group(K1)
5020
5021
@cached_method
5022
def galois_group_over_quadratic_field(self, D, c):
5023
"""
5024
Return the Galois group of the extension of ring class fields
5025
`K_c` over the quadratic imaginary field `K` of discriminant `D`.
5026
5027
INPUT:
5028
5029
- `D` -- fundamental discriminant
5030
5031
- `c` -- conductor (square-free integer)
5032
5033
EXAMPLES::
5034
5035
sage: N = 37; D = -7; ell = 17; c = 41; p = 3
5036
sage: H = heegner_points(N).reduce_mod(ell)
5037
sage: H.galois_group_over_quadratic_field(D, c)
5038
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 41 over Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
5039
5040
"""
5041
Kc = heegner_points(self.level(), D, c).ring_class_field()
5042
return Kc.galois_group(Kc.quadratic_field())
5043
5044
@cached_method
5045
def quadratic_field(self, D):
5046
"""
5047
Return our fixed choice of quadratic imaginary field of
5048
discriminant `D`.
5049
5050
INPUT:
5051
5052
- `D` -- fundamental discriminant
5053
5054
OUTPUT:
5055
5056
- a quadratic number field
5057
5058
EXAMPLES::
5059
5060
sage: H = heegner_points(389).reduce_mod(5)
5061
sage: H.quadratic_field(-7)
5062
Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
5063
"""
5064
Kc = heegner_points(self.level(), D, 1).ring_class_field()
5065
return Kc.quadratic_field()
5066
5067
@cached_method
5068
def kolyvagin_cyclic_subideals(self, I, p, alpha_quaternion):
5069
r"""
5070
Return list of pairs `(J, n)` where `J` runs through the
5071
cyclic subideals of `I` of index `(\ZZ/p\ZZ)^2`, and `J \sim
5072
\alpha^n(J_0)` for some fixed choice of cyclic subideal `J_0`.
5073
5074
INPUT:
5075
5076
- `I` -- right ideal of the quaternion algebra
5077
5078
- `p` -- prime number
5079
5080
- ``alpha_quaternion`` -- image in the quaternion algebra
5081
of generator `\alpha` for
5082
`(\mathcal{O}_K / c\mathcal{O}_K)^* / (\ZZ/c\ZZ)^*`.
5083
5084
OUTPUT:
5085
5086
- list of 2-tuples
5087
5088
EXAMPLES::
5089
5090
sage: N = 37; D = -7; ell = 17; c=5
5091
sage: H = heegner_points(N).reduce_mod(ell)
5092
sage: B = H.brandt_module(); I = B.right_ideals()[32]
5093
sage: f = H.optimal_embeddings(D, 1, I.left_order())[0]
5094
sage: g = H.kolyvagin_generators(f.domain().number_field(), c)
5095
sage: alpha_quaternion = f(g[0]); alpha_quaternion
5096
1 - 5/128*i - 77/192*j + 137/384*k
5097
sage: H.kolyvagin_cyclic_subideals(I, 5, alpha_quaternion)
5098
[(Fractional ideal (2 + 874/3*j + 128356/3*k, 2*i + 932/3*j + 198806/3*k, 2560/3*j + 33280/3*k, 94720*k), 0), (Fractional ideal (2 + 462*j + 82892*k, 2*i + 932/3*j + 141974/3*k, 2560/3*j + 33280/3*k, 94720*k), 1), (Fractional ideal (2 + 2410/3*j + 261988/3*k, 2*i + 652*j + 89650*k, 2560/3*j + 33280/3*k, 94720*k), 2), (Fractional ideal (2 + 2410/3*j + 91492/3*k, 2*i + 1444/3*j + 148630/3*k, 2560/3*j + 33280/3*k, 94720*k), 3), (Fractional ideal (2 + 874/3*j + 71524/3*k, 2*i + 2468/3*j + 275606/3*k, 2560/3*j + 33280/3*k, 94720*k), 4), (Fractional ideal (2 + 462*j + 63948*k, 2*i + 2468/3*j + 218774/3*k, 2560/3*j + 33280/3*k, 94720*k), 5)]
5099
"""
5100
X = I.cyclic_right_subideals(p, alpha_quaternion)
5101
return [(J, i) for i, J in enumerate(X)]
5102
5103
@cached_method
5104
def kolyvagin_generator(self, K, p):
5105
r"""
5106
Return element in `K` that maps to the multiplicative generator
5107
for the quotient group
5108
5109
`(\mathcal{O}_K / p \mathcal{O}_K)^* / (\ZZ/p\ZZ)^*`
5110
5111
of the form `\sqrt{D}+n` with `n\geq 1` minimal.
5112
5113
INPUT:
5114
5115
- `K` -- quadratic imaginary field
5116
5117
- `p` -- inert prime
5118
5119
EXAMPLES::
5120
5121
sage: N = 37; D = -7; ell = 17; p=5
5122
sage: H = heegner_points(N).reduce_mod(ell)
5123
sage: B = H.brandt_module(); I = B.right_ideals()[32]
5124
sage: f = H.optimal_embeddings(D, 1, I.left_order())[0]
5125
sage: H.kolyvagin_generator(f.domain().number_field(), 5)
5126
a + 1
5127
5128
This function requires that p be prime, but kolyvagin_generators works in general::
5129
5130
sage: H.kolyvagin_generator(f.domain().number_field(), 5*17)
5131
Traceback (most recent call last):
5132
...
5133
NotImplementedError: p must be prime
5134
sage: H.kolyvagin_generators(f.domain().number_field(), 5*17)
5135
[-34*a + 1, 35*a + 106]
5136
"""
5137
p = ZZ(p)
5138
if not p.is_prime():
5139
raise NotImplementedError("p must be prime")
5140
if K.discriminant() % p == 0:
5141
raise ValueError("p must be unramified")
5142
if len(K.factor(p)) != 1:
5143
raise ValueError("p must be inert")
5144
5145
F = K.residue_field(p)
5146
a = F.gen()
5147
assert a*a == K.discriminant(), "bug: we assumed generator of finite field must be square root of discriminant, but for some reason this is not true"
5148
for n in range(1,p):
5149
if (a + n).multiplicative_order() % (p*p-1) == 0:
5150
return K.gen() + n
5151
raise RuntimeError("there is a bug in kolyvagin_generator")
5152
5153
@cached_method
5154
def kolyvagin_generators(self, K, c):
5155
r"""
5156
Return elements in `\mathcal{O}_K` that map to multiplicative generators
5157
for the factors of the quotient group
5158
5159
`(\mathcal{O}_K / c \mathcal{O}_K)^* / (\ZZ/c\ZZ)^*`
5160
5161
corresponding to the prime divisors of c. Each generator is
5162
of the form `\sqrt{D}+n` with `n\geq 1` minimal.
5163
5164
INPUT:
5165
5166
- `K` -- quadratic imaginary field
5167
5168
- `c` -- square free product of inert prime
5169
5170
EXAMPLES::
5171
5172
sage: N = 37; D = -7; ell = 17; p=5
5173
sage: H = heegner_points(N).reduce_mod(ell)
5174
sage: B = H.brandt_module(); I = B.right_ideals()[32]
5175
sage: f = H.optimal_embeddings(D, 1, I.left_order())[0]
5176
sage: H.kolyvagin_generators(f.domain().number_field(), 5*17)
5177
[-34*a + 1, 35*a + 106]
5178
"""
5179
v = []
5180
F = ZZ(c).factor()
5181
from sage.rings.integer_ring import crt_basis
5182
B = crt_basis([x[0] for x in F])
5183
for i, (p, e) in enumerate(F):
5184
if e > 1:
5185
raise ValueError("c must be square free")
5186
alpha = self.kolyvagin_generator(K, p)
5187
# Now we use the Chinese Remainder Theorem to make an element
5188
# of O_K that equals alpha modulo p and equals 1 modulo
5189
# all other prime divisors of c.
5190
Z = [1]*len(B)
5191
Z[i] = alpha[0]
5192
a0 = sum([Z[j]*B[j] for j in range(len(B))])
5193
Z = [0]*len(B)
5194
Z[i] = alpha[1]
5195
a1 = sum([Z[j]*B[j] for j in range(len(B))])
5196
v.append(alpha.parent()([a0,a1]))
5197
return v
5198
5199
@cached_method
5200
def kolyvagin_sigma_operator(self, D, c, r, bound=None):
5201
"""
5202
Return the action of the Kolyvagin sigma operator on the `r`-th
5203
basis vector.
5204
5205
INPUT:
5206
5207
- `D` -- fundamental discriminant
5208
5209
- `c` -- conductor (square-free integer, need not be prime)
5210
5211
- `r` -- nonnegative integer
5212
5213
- ``bound`` -- (default: ``None``), if given, controls
5214
precision of computation of theta series, which could
5215
impact performance, but does not impact correctness
5216
5217
EXAMPLES:
5218
5219
We first try to verify Kolyvagin's conjecture for a rank 2
5220
curve by working modulo 5, but we are unlucky with `c=17`::
5221
5222
sage: N = 389; D = -7; ell = 5; c = 17; q = 3
5223
sage: H = heegner_points(N).reduce_mod(ell)
5224
sage: E = EllipticCurve('389a')
5225
sage: V = H.modp_dual_elliptic_curve_factor(E, q, 5) # long time (4s on sage.math, 2012)
5226
sage: k118 = H.kolyvagin_sigma_operator(D, c, 118)
5227
sage: k104 = H.kolyvagin_sigma_operator(D, c, 104)
5228
sage: [b.dot_product(k104.element().change_ring(GF(3))) for b in V.basis()] # long time
5229
[0, 0]
5230
sage: [b.dot_product(k118.element().change_ring(GF(3))) for b in V.basis()] # long time
5231
[0, 0]
5232
5233
Next we try again with `c=41` and this does work, in that we
5234
get something nonzero, when dotting with V::
5235
5236
sage: c = 41
5237
sage: k118 = H.kolyvagin_sigma_operator(D, c, 118)
5238
sage: k104 = H.kolyvagin_sigma_operator(D, c, 104)
5239
sage: [b.dot_product(k118.element().change_ring(GF(3))) for b in V.basis()] # long time
5240
[1, 0]
5241
sage: [b.dot_product(k104.element().change_ring(GF(3))) for b in V.basis()] # long time
5242
[2, 0]
5243
5244
By the way, the above is the first ever provable verification
5245
of Kolyvagin's conjecture for any curve of rank at least 2.
5246
5247
Another example, but where the curve has rank 1::
5248
5249
sage: N = 37; D = -7; ell = 17; c = 41; q = 3
5250
sage: H = heegner_points(N).reduce_mod(ell)
5251
sage: H.heegner_divisor(D,1).element().nonzero_positions()
5252
[32, 51]
5253
sage: k32 = H.kolyvagin_sigma_operator(D, c, 32); k32
5254
(63, 68, 47, 47, 31, 52, 37, 0, 0, 47, 3, 31, 47, 7, 21, 26, 19, 10, 0, 0, 11, 28, 41, 2, 47, 25, 0, 0, 36, 0, 33, 0, 0, 0, 40, 6, 14, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
5255
sage: k51 = H.kolyvagin_sigma_operator(D, c, 51); k51
5256
(5, 13, 0, 0, 14, 0, 21, 0, 0, 0, 29, 0, 0, 45, 0, 6, 0, 40, 0, 61, 0, 0, 40, 32, 0, 9, 0, 0, 0, 0, 17, 0, 0, 0, 77, 40, 2, 10, 18, 0, 0, 61, 19, 45, 26, 80, 61, 35, 35, 19, 1, 0)
5257
sage: V = H.modp_dual_elliptic_curve_factor(EllipticCurve('37a'), q, 5); V
5258
Vector space of degree 52 and dimension 2 over Ring of integers modulo 3
5259
Basis matrix:
5260
2 x 52 dense matrix over Ring of integers modulo 3
5261
sage: [b.dot_product(k32.element().change_ring(GF(q))) for b in V.basis()]
5262
[1, 1]
5263
sage: [b.dot_product(k51.element().change_ring(GF(q))) for b in V.basis()]
5264
[1, 1]
5265
5266
An example with `c` a product of two primes::
5267
5268
sage: N = 389; D = -7; ell = 5; q = 3
5269
sage: H = heegner_points(N).reduce_mod(ell)
5270
sage: V = H.modp_dual_elliptic_curve_factor(EllipticCurve('389a'), q, 5)
5271
sage: k = H.kolyvagin_sigma_operator(D, 17*41, 104) # long time
5272
sage: k # long time
5273
(494, 472, 1923, 1067, ..., 102, 926)
5274
sage: [b.dot_product(k.element().change_ring(GF(3))) for b in V.basis()] # long time (but only because depends on something slow)
5275
[0, 0]
5276
"""
5277
B = self.brandt_module()
5278
RI = B.right_ideals()
5279
5280
f = self.optimal_embeddings(D, 1, RI[r].left_order())[0]
5281
alphas = self.kolyvagin_generators(f.domain().number_field(), c)
5282
alpha_quaternions = [f(x) for x in alphas]
5283
5284
if bound is None:
5285
bound = B.dimension() // 2 + 5
5286
theta_dict = B._theta_dict(bound)
5287
5288
c = ZZ(c)
5289
J_lists = []
5290
F = c.factor()
5291
I = RI[r]
5292
for i, (p, e) in enumerate(F):
5293
if e > 1: raise ValueError("c must be square free")
5294
X = I.cyclic_right_subideals(p, alpha_quaternions[i])
5295
J_lists.append(dict(enumerate(X)))
5296
5297
ans = [0]*B.dimension()
5298
from sage.misc.mrange import cartesian_product_iterator
5299
for v in cartesian_product_iterator([range(1,p+1) for p,_ in F]):
5300
J = J_lists[0][v[0]]
5301
for i in range(1,len(J_lists)):
5302
J = J.intersection(J_lists[i][v[i]])
5303
J_theta = tuple(J.theta_series_vector(bound))
5304
d = theta_dict[J_theta]
5305
j = None
5306
if len(d) == 1:
5307
j = d[0]
5308
else:
5309
for z in d:
5310
if RI[z].is_equivalent(J, 0):
5311
j = z
5312
# we found the right j
5313
break
5314
if j is None:
5315
raise RuntimeError("bug finding equivalent ideal")
5316
ans[j] += prod(v)
5317
return B(ans)
5318
5319
@cached_method
5320
def modp_dual_elliptic_curve_factor(self, E, p, bound=10):
5321
"""
5322
Return the factor of the Brandt module space modulo `p`
5323
corresponding to the elliptic curve `E`, cut out using
5324
Hecke operators up to ``bound``.
5325
5326
INPUT:
5327
5328
- `E` -- elliptic curve of conductor equal to the level of self
5329
5330
- `p` -- prime number
5331
5332
- `bound` -- positive integer (default: 10)
5333
5334
EXAMPLES::
5335
5336
sage: N = 37; D = -7; ell = 17; c = 41; q = 3
5337
sage: H = heegner_points(N).reduce_mod(ell)
5338
sage: V = H.modp_dual_elliptic_curve_factor(EllipticCurve('37a'), q, 5); V
5339
Vector space of degree 52 and dimension 2 over Ring of integers modulo 3
5340
Basis matrix:
5341
2 x 52 dense matrix over Ring of integers modulo 3
5342
"""
5343
if E.conductor() != self.level():
5344
raise ValueError("conductor of E must equal level of self")
5345
p = ZZ(p)
5346
if not p.is_prime():
5347
raise ValueError("p (=%s) must be prime"%p)
5348
bad = self.__level * self.__ell
5349
5350
V = None
5351
q = ZZ(2)
5352
B = self.brandt_module()
5353
F = GF(p)
5354
while q <= bound and (V is None or V.dimension() > 2):
5355
verbose("q = %s"%q)
5356
if bad % q != 0:
5357
T = B._compute_hecke_matrix_directly(q).change_ring(F).transpose()
5358
if V is None:
5359
V = (T - E.ap(q)).kernel()
5360
else:
5361
t = T.restrict(V)
5362
W = (t - E.ap(q)).kernel()
5363
V = (W.basis_matrix() * V.basis_matrix()).row_space()
5364
q = q.next_prime()
5365
return V
5366
5367
@cached_method
5368
def rational_kolyvagin_divisor(self, D, c):
5369
r"""
5370
Return the Kolyvagin divisor as an element of the Brandt module
5371
corresponding to the discriminant `D` and conductor `c`, which
5372
both must be coprime to `N\ell`.
5373
5374
INPUT:
5375
5376
- `D` -- discriminant (negative integer)
5377
5378
- `c` -- conductor (positive integer)
5379
5380
5381
OUTPUT:
5382
5383
- Brandt module element (or tuple of them)
5384
5385
EXAMPLES::
5386
5387
sage: N = 389; D = -7; ell = 5; c = 17; q = 3
5388
sage: H = heegner_points(N).reduce_mod(ell)
5389
sage: k = H.rational_kolyvagin_divisor(D, c); k # long time (5s on sage.math, 2013)
5390
(14, 16, 0, 0, ... 0, 0, 0)
5391
sage: V = H.modp_dual_elliptic_curve_factor(EllipticCurve('389a'), q, 2)
5392
sage: [b.dot_product(k.element().change_ring(GF(q))) for b in V.basis()] # long time
5393
[0, 0]
5394
sage: k = H.rational_kolyvagin_divisor(D, 59)
5395
sage: [b.dot_product(k.element().change_ring(GF(q))) for b in V.basis()]
5396
[1, 0]
5397
"""
5398
if not self.satisfies_heegner_hypothesis(D, c):
5399
raise ValueError("D and c must be coprime to N and ell")
5400
5401
hd = self.heegner_divisor(D)
5402
v = hd.element()
5403
if class_number(D) != 1:
5404
raise NotImplementedError("class number greater than 1 not implemented")
5405
i = min(v.nonzero_positions())
5406
return self.kolyvagin_sigma_operator(D, c, i)
5407
5408
#w = 0
5409
#for i, a in v.dict().iteritems():
5410
# w += a * self.kolyvagin_sigma_operator(D, c, i)
5411
# return w
5412
5413
@cached_method
5414
def kolyvagin_point_on_curve(self, D, c, E, p, bound=10):
5415
r"""
5416
Compute image of the Kolyvagin divisor `P_c` in
5417
`E(\GF{\ell^2}) / p E(\GF{\ell^2})`. Note that
5418
this image is by definition only well defined up to
5419
scalars. However, doing multiple computations
5420
will always yield the same result, and working
5421
modulo different `\ell` is compatible (since we
5422
always chose the same generator for `\textrm{Gal}(K_c/K_1)`).
5423
5424
INPUT:
5425
5426
- `D` -- fundamental negative discriminant
5427
5428
- `c` -- conductor
5429
5430
- `E` -- elliptic curve of conductor the level of self
5431
5432
- `p` -- odd prime number such that we consider image in
5433
`E(\GF{\ell^2}) / p E(\GF{\ell^2})`
5434
5435
- ``bound`` -- integer (default: 10)
5436
5437
EXAMPLES::
5438
5439
sage: N = 37; D = -7; ell = 17; c = 41; p = 3
5440
sage: H = heegner_points(N).reduce_mod(ell)
5441
sage: H.kolyvagin_point_on_curve(D, c, EllipticCurve('37a'), p)
5442
[1, 1]
5443
"""
5444
k = self.rational_kolyvagin_divisor(D, c)
5445
V = self.modp_dual_elliptic_curve_factor(E, p, bound)
5446
return [b.dot_product(k.element().change_ring(GF(p))) for b in V.basis()]
5447
5448
def kolyvagin_reduction_data(E, q, first_only=True):
5449
r"""
5450
Given an elliptic curve of positive rank and a prime `q`, this
5451
function returns data about how to use Kolyvagin's `q`-torsion
5452
Heegner point Euler system to do computations with this curve.
5453
See the precise description of the output below.
5454
5455
INPUT:
5456
5457
- `E` -- elliptic curve over `\QQ` of rank 1 or 2
5458
5459
- `q` -- an odd prime that does not divide the order of the
5460
rational torsion subgroup of `E`
5461
5462
- ``first_only`` -- bool (default: ``True``) whether two only return
5463
the first prime that one can work modulo to get data about
5464
the Euler system
5465
5466
OUTPUT in the rank 1 case or when the default flag ``first_only=True``:
5467
5468
- `\ell` -- first good odd prime satisfying the Kolyvagin
5469
condition that `q` divides \gcd(a_{\ell},\ell+1)` and the
5470
reduction map is surjective to `E(\GF{\ell}) / q
5471
E(\GF{\ell})`
5472
5473
- `D` -- discriminant of the first quadratic imaginary field
5474
`K` that satisfies the Heegner hypothesis for `E` such that
5475
both `\ell` is inert in `K`, and the twist `E^D` has analytic
5476
rank `\leq 1`
5477
5478
- `h_D` -- the class number of `K`
5479
5480
- the dimension of the Brandt module `B(\ell,N)`, where `N` is
5481
the conductor of `E`
5482
5483
OUTPUT in the rank 2 case:
5484
5485
- `\ell_1` -- first prime (as above in the rank 1 case) where
5486
reduction map is surjective
5487
5488
- `\ell_2` -- second prime (as above) where reduction map is
5489
surjective
5490
5491
- `D` -- discriminant of the first quadratic imaginary field
5492
`K` that satisfies the Heegner hypothesis for `E` such that
5493
both `\ell_1` and `\ell_2` are simultaneously inert in `K`,
5494
and the twist `E^D` has analytic rank `\leq 1`
5495
5496
- `h_D` -- the class number of `K`
5497
5498
- the dimension of the Brandt module `B(\ell_1,N)`, where `N` is
5499
the conductor of `E`
5500
5501
- the dimension of the Brandt module `B(\ell_2,N)`
5502
5503
5504
EXAMPLES:
5505
5506
Import this function::
5507
5508
sage: from sage.schemes.elliptic_curves.heegner import kolyvagin_reduction_data
5509
5510
A rank 1 example::
5511
5512
sage: kolyvagin_reduction_data(EllipticCurve('37a1'),3)
5513
(17, -7, 1, 52)
5514
5515
A rank 3 example::
5516
5517
sage: kolyvagin_reduction_data(EllipticCurve('5077a1'),3)
5518
(11, -47, 5, 4234)
5519
sage: H = heegner_points(5077, -47)
5520
sage: [c for c in H.kolyvagin_conductors(2,10,EllipticCurve('5077a1'),3) if c%11]
5521
[667, 943, 1189, 2461]
5522
sage: factor(667)
5523
23 * 29
5524
5525
5526
A rank 4 example (the first Kolyvagin class that we could try to
5527
compute would be `P_{23\cdot 29\cdot 41}`, and would require
5528
working in a space of dimension 293060 (so prohibitive at
5529
present)::
5530
5531
sage: E = elliptic_curves.rank(4)[0]
5532
sage: kolyvagin_reduction_data(E,3) # long time
5533
(11, -71, 7, 293060)
5534
sage: H = heegner_points(293060, -71)
5535
sage: H.kolyvagin_conductors(1,4,E,3)
5536
[11, 17, 23, 41]
5537
5538
The first rank 2 example::
5539
5540
sage: kolyvagin_reduction_data(EllipticCurve('389a'),3)
5541
(5, -7, 1, 130)
5542
sage: kolyvagin_reduction_data(EllipticCurve('389a'),3, first_only=False)
5543
(5, 17, -7, 1, 130, 520)
5544
5545
A large `q = 7`::
5546
5547
sage: kolyvagin_reduction_data(EllipticCurve('1143c1'),7, first_only=False)
5548
(13, 83, -59, 3, 1536, 10496)
5549
5550
Additive reduction::
5551
5552
sage: kolyvagin_reduction_data(EllipticCurve('2350g1'),5, first_only=False)
5553
(19, 239, -311, 19, 6480, 85680)
5554
"""
5555
from ell_generic import is_EllipticCurve
5556
if not is_EllipticCurve(E):
5557
raise TypeError("E must be an elliptic curve")
5558
5559
q = ZZ(q)
5560
if not q.is_prime():
5561
raise ValueError("q must be a prime")
5562
5563
if q.gcd(E.torsion_order()) != 1:
5564
raise NotImplementedError("q must be coprime to torsion")
5565
5566
N = E.conductor()
5567
r = E.rank()
5568
5569
if r == 0:
5570
raise ValueError("E must have positive rank")
5571
5572
if E.rank() == 1:
5573
first_only = True
5574
5575
from sage.modular.quatalg.all import BrandtModule
5576
5577
def twist_is_minimal(D):
5578
# return True if the quadratic twist E^D has analytic rank <= 1
5579
return E.quadratic_twist(D).analytic_rank() <= 1
5580
5581
def red(P, ell):
5582
# reduce the point P on the elliptic curve modulo ell
5583
w = list(P)
5584
d = lcm([a.denominator() for a in w])
5585
return E.change_ring(GF(ell))([d*a for a in w])
5586
5587
5588
def best_heegner_D(ell_1, ell_2):
5589
# return the first Heegner D satisfy all hypothesis such that
5590
# both ell_1 and ell_2 are inert
5591
D = -5
5592
while True:
5593
if is_fundamental_discriminant(D) and \
5594
D%ell_1 and D%ell_2 and \
5595
E.satisfies_heegner_hypothesis(D) and \
5596
is_inert(D, ell_1) and is_inert(D, ell_2) and \
5597
twist_is_minimal(D):
5598
return D
5599
D -= 1
5600
5601
if first_only:
5602
# find first prime ell with various conditions
5603
# such that reduction is surjective to E(F_ell)/q.
5604
ell = ZZ(3)
5605
while True:
5606
while N % ell == 0 or gcd(ell+1,E.ap(ell)) % q != 0:
5607
ell = ell.next_prime()
5608
# determine if mod ell reduction is surjective, using
5609
# partly that it is a lemma that E(F_ell)/q is cyclic.
5610
m = ZZ(E.Np(ell) / q)
5611
for P in E.gens():
5612
if red(P,ell) * m != 0:
5613
# bingo, is surjective
5614
D = best_heegner_D(ell,ell)
5615
return (ell, D, class_number(D), BrandtModule(ell,N).dimension())
5616
# end for
5617
ell = ell.next_prime()
5618
5619
if E.rank() != 2:
5620
raise ValueError("if first_only is not True, then the curve E must have rank 1 or 2")
5621
5622
P, Q = E.gens()
5623
def kernel_of_reduction(ell):
5624
# return list of reps for the kernel as a subgroup of the map
5625
# E(Q) / q E(Q) ----> E(F_ell) / q E(F_ell)
5626
m = ZZ(E.Np(ell) / q)
5627
A = [a*P + b*Q for a in range(q) for b in range(q)]
5628
return [z for z in A if red(z,ell) * m == 0]
5629
5630
# compute first good odd prime
5631
ell_1 = ZZ(3)
5632
while True:
5633
while N % ell_1 == 0 or gcd(ell_1+1,E.ap(ell_1)) % q != 0:
5634
ell_1 = ell_1.next_prime()
5635
# compute kernel of reduction modulo ell_1
5636
G1 = set(kernel_of_reduction(ell_1))
5637
if len(G1) == q: break
5638
ell_1 = ell_1.next_prime()
5639
5640
# compute next good odd prime with distinct kernel of order q
5641
ell_2 = ell_1.next_prime()
5642
while True:
5643
while N % ell_2 == 0 or gcd(ell_2+1,E.ap(ell_2)) % q != 0:
5644
ell_2 = ell_2.next_prime()
5645
G2 = set(kernel_of_reduction(ell_2))
5646
if G1 != G2 and len(G2) == q:
5647
break
5648
ell_2 = ell_2.next_prime()
5649
5650
# Find smallest D where both ell_1 and ell_2 are inert
5651
D = best_heegner_D(ell_1, ell_2)
5652
return (ell_1, ell_2, D, class_number(D),
5653
BrandtModule(ell_1,N).dimension(),
5654
BrandtModule(ell_2,N).dimension())
5655
5656
class HeegnerQuatAlgEmbedding(SageObject):
5657
r"""
5658
The homomorphism `\mathcal{O} \to R`, where `\mathcal{O}` is the
5659
order of conductor `c` in the quadratic field of discriminant `D`,
5660
and `R` is an Eichler order in a quaternion algebra.
5661
5662
EXAMPLES::
5663
5664
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5665
sage: f = H.optimal_embeddings(-7, 2, R)[0]; f
5666
Embedding sending 2*sqrt(-7) to -5*i + k
5667
sage: type(f)
5668
<class 'sage.schemes.elliptic_curves.heegner.HeegnerQuatAlgEmbedding'>
5669
sage: loads(dumps(f)) == f
5670
True
5671
"""
5672
def __init__(self, D, c, R, beta):
5673
r"""
5674
INPUT:
5675
5676
- `D` -- negative fundamental discriminant
5677
5678
- `c` -- positive integer coprime to `D`
5679
5680
- `R` -- Eichler order in a rational quaternion algebra
5681
5682
- `\beta` -- element of `R` such that the homomorphism
5683
sends `c\sqrt{D}` to `\beta`
5684
5685
EXAMPLES::
5686
5687
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5688
sage: i,j,k = H.quaternion_algebra().gens()
5689
sage: import sage.schemes.elliptic_curves.heegner as heegner
5690
sage: heegner.HeegnerQuatAlgEmbedding(-7, 2, R, -5*i+k)
5691
Embedding sending 2*sqrt(-7) to -5*i + k
5692
"""
5693
self.__D = D
5694
self.__c = c
5695
self.__R = R
5696
self.__beta = beta
5697
5698
def __eq__(self, other):
5699
"""
5700
EXAMPLES::
5701
5702
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5703
sage: f = H.optimal_embeddings(-7, 2, R)[0]
5704
sage: f == H.optimal_embeddings(-7, 2, R)[0]
5705
True
5706
sage: f == H.optimal_embeddings(-7, 2, R)[1]
5707
False
5708
sage: f == 0
5709
False
5710
"""
5711
return isinstance(other, HeegnerQuatAlgEmbedding) and \
5712
self.__D == other.__D and \
5713
self.__c == other.__c and \
5714
self.__R == other.__R and \
5715
self.__beta == other.__beta
5716
5717
def __call__(self, x):
5718
"""
5719
Return image of `x` under this embedding.
5720
5721
INPUT:
5722
5723
- `x` -- element of the quadratic order
5724
5725
EXAMPLES::
5726
5727
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5728
sage: f = H.optimal_embeddings(-7, 1, R)[0]; f
5729
Embedding sending sqrt(-7) to -i + j + k
5730
sage: a = f.domain_gen(); a^2
5731
-7
5732
sage: f(2 + 3*a)
5733
2 - 3*i + 3*j + 3*k
5734
sage: 2 + 3*f(a)
5735
2 - 3*i + 3*j + 3*k
5736
sage: f(a)^2
5737
-7
5738
"""
5739
v = self.domain().number_field()(x).vector()
5740
w = v * self.matrix()
5741
z = self.codomain().quaternion_algebra()(w)
5742
# There is no notion of an "element of an order" implemented
5743
# for quaternion algebras right now. All elements are
5744
# elements of the ambient rational quaternion algebra.
5745
return z
5746
5747
@cached_method
5748
def matrix(self):
5749
r"""
5750
Return matrix over `\QQ` of this morphism, with respect to the
5751
basis 1, `c\sqrt{D}` of the domain and the basis `1,i,j,k` of
5752
the ambient rational quaternion algebra (which contains the
5753
domain).
5754
5755
EXAMPLES::
5756
5757
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5758
sage: f = H.optimal_embeddings(-7, 1, R)[0]; f
5759
Embedding sending sqrt(-7) to -i + j + k
5760
sage: f.matrix()
5761
[ 1 0 0 0]
5762
[ 0 -1 1 1]
5763
sage: f.conjugate().matrix()
5764
[ 1 0 0 0]
5765
[ 0 1 -1 -1]
5766
"""
5767
return matrix(QQ,2,4,[[1,0,0,0], self.__beta.coefficient_tuple()])
5768
5769
@cached_method
5770
def domain(self):
5771
"""
5772
Return the domain of this embedding.
5773
5774
EXAMPLES::
5775
5776
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5777
sage: H.optimal_embeddings(-7, 2, R)[0].domain()
5778
Order in Number Field in a with defining polynomial x^2 + 7
5779
"""
5780
R, a = quadratic_order(self.__D, self.__c)
5781
5782
# The following assumption is used, e.g., in the __call__
5783
# method. I know that it is satisfied by the current
5784
# implementation. But somebody might someday annoying change
5785
# the implementation, and we want to catch that if it were to
5786
# ever happen.
5787
5788
assert R.basis() == [1, a], "an assumption about construction of orders is violated"
5789
self.__domain_gen = a
5790
return R
5791
5792
def domain_gen(self):
5793
r"""
5794
Return the specific generator `c \sqrt{D}` for the domain
5795
order.
5796
5797
EXAMPLES::
5798
5799
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5800
sage: f = H.optimal_embeddings(-7, 2, R)[0]
5801
sage: f.domain_gen()
5802
2*a
5803
sage: f.domain_gen()^2
5804
-28
5805
"""
5806
self.domain()
5807
return self.__domain_gen
5808
5809
def domain_conductor(self):
5810
"""
5811
Return the conductor of the domain.
5812
5813
EXAMPLES::
5814
5815
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5816
sage: H.optimal_embeddings(-7, 2, R)[0].domain_conductor()
5817
2
5818
"""
5819
return self.__c
5820
5821
def beta(self):
5822
r"""
5823
Return the element `\beta` in the quaternion algebra order
5824
that `c\sqrt{D}` maps to.
5825
5826
EXAMPLES::
5827
5828
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5829
sage: H.optimal_embeddings(-7, 2, R)[0].beta()
5830
-5*i + k
5831
"""
5832
return self.__beta
5833
5834
def codomain(self):
5835
"""
5836
Return the codomain of this embedding.
5837
5838
EXAMPLES::
5839
5840
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5841
sage: H.optimal_embeddings(-7, 2, R)[0].codomain()
5842
Order of Quaternion Algebra (-1, -3) with base ring Rational Field with basis (1/2 + 1/2*j + 7*k, 1/2*i + 13/2*k, j + 3*k, 11*k)
5843
"""
5844
return self.__R
5845
5846
@cached_method
5847
def _repr_(self):
5848
"""
5849
Return string representation of this embedding.
5850
5851
EXAMPLES::
5852
5853
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5854
sage: f = H.optimal_embeddings(-7, 2, R)[0]; f._repr_()
5855
'Embedding sending 2*sqrt(-7) to -5*i + k'
5856
"""
5857
a = '%ssqrt(%s)'%('%s*'%self.__c if self.__c > 1 else '', self.__D)
5858
return "Embedding sending %s to %s"%(a, self.__beta)
5859
5860
def conjugate(self):
5861
"""
5862
Return the conjugate of this embedding, which is also an
5863
embedding.
5864
5865
EXAMPLES::
5866
5867
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5868
sage: f = H.optimal_embeddings(-7, 2, R)[0]
5869
sage: f.conjugate()
5870
Embedding sending 2*sqrt(-7) to 5*i - k
5871
sage: f
5872
Embedding sending 2*sqrt(-7) to -5*i + k
5873
"""
5874
return HeegnerQuatAlgEmbedding(self.__D, self.__c,
5875
self.__R, self.__beta.conjugate())
5876
5877
5878
#############################################################################
5879
# Utility Functions
5880
#############################################################################
5881
5882
def quadratic_order(D, c, names='a'):
5883
"""
5884
Return order of conductor `c` in quadratic field with fundamental
5885
discriminant `D`.
5886
5887
INPUT:
5888
5889
- `D` -- fundamental discriminant
5890
5891
- `c` -- conductor
5892
5893
- ``names`` -- string (default: 'a')
5894
5895
OUTPUT:
5896
5897
- order `R` of conductor `c` in an imaginary quadratic field
5898
5899
- the element `c\sqrt{D}` as an element of `R`
5900
5901
The generator for the field is named 'a' by default.
5902
5903
EXAMPLES::
5904
5905
sage: sage.schemes.elliptic_curves.heegner.quadratic_order(-7,3)
5906
(Order in Number Field in a with defining polynomial x^2 + 7, 3*a)
5907
sage: sage.schemes.elliptic_curves.heegner.quadratic_order(-7,3,'alpha')
5908
(Order in Number Field in alpha with defining polynomial x^2 + 7, 3*alpha)
5909
"""
5910
K = QuadraticField(D, names)
5911
sqrtD = K.gen(0)
5912
t = sqrtD * c
5913
R = K.order([t])
5914
return R, R(t)
5915
5916
def class_number(D):
5917
"""
5918
Return the class number of the quadratic field with fundamental
5919
discriminant `D`.
5920
5921
INPUT:
5922
5923
- `D` -- integer
5924
5925
EXAMPLES::
5926
5927
sage: sage.schemes.elliptic_curves.heegner.class_number(-20)
5928
2
5929
sage: sage.schemes.elliptic_curves.heegner.class_number(-23)
5930
3
5931
sage: sage.schemes.elliptic_curves.heegner.class_number(-163)
5932
1
5933
5934
A ValueError is raised when `D` is not a fundamental
5935
discriminant::
5936
5937
sage: sage.schemes.elliptic_curves.heegner.class_number(-5)
5938
Traceback (most recent call last):
5939
...
5940
ValueError: D (=-5) must be a fundamental discriminant
5941
"""
5942
if not number_field.is_fundamental_discriminant(D):
5943
raise ValueError("D (=%s) must be a fundamental discriminant"%D)
5944
return QuadraticField(D,'a').class_number()
5945
5946
def is_inert(D, p):
5947
r"""
5948
Return ``True`` if p is an inert prime in the field `\QQ(\sqrt{D})`.
5949
5950
INPUT:
5951
5952
- `D` -- fundamental discriminant
5953
5954
- `p` -- prime integer
5955
5956
EXAMPLES::
5957
5958
sage: sage.schemes.elliptic_curves.heegner.is_inert(-7,3)
5959
True
5960
sage: sage.schemes.elliptic_curves.heegner.is_inert(-7,7)
5961
False
5962
sage: sage.schemes.elliptic_curves.heegner.is_inert(-7,11)
5963
False
5964
"""
5965
K = QuadraticField(D,'a')
5966
F = K.factor(p)
5967
return len(F) == 1 and F[0][1] == 1
5968
5969
def is_split(D, p):
5970
r"""
5971
Return ``True`` if p is a split prime in the field `\QQ(\sqrt{D})`.
5972
5973
INPUT:
5974
5975
- `D` -- fundamental discriminant
5976
5977
- `p` -- prime integer
5978
5979
EXAMPLES::
5980
5981
sage: sage.schemes.elliptic_curves.heegner.is_split(-7,3)
5982
False
5983
sage: sage.schemes.elliptic_curves.heegner.is_split(-7,7)
5984
False
5985
sage: sage.schemes.elliptic_curves.heegner.is_split(-7,11)
5986
True
5987
"""
5988
K = QuadraticField(D,'a')
5989
F = K.factor(p)
5990
return len(F) == 2
5991
5992
def is_ramified(D, p):
5993
r"""
5994
Return ``True`` if p is a ramified prime in the field `\QQ(\sqrt{D})`.
5995
5996
INPUT:
5997
5998
- `D` -- fundamental discriminant
5999
6000
- `p` -- prime integer
6001
6002
EXAMPLES::
6003
6004
sage: sage.schemes.elliptic_curves.heegner.is_ramified(-7,2)
6005
False
6006
sage: sage.schemes.elliptic_curves.heegner.is_ramified(-7,7)
6007
True
6008
sage: sage.schemes.elliptic_curves.heegner.is_ramified(-1,2)
6009
True
6010
"""
6011
return QuadraticField(D,'a').discriminant() % p == 0
6012
6013
def nearby_rational_poly(f, **kwds):
6014
r"""
6015
Return a polynomial whose coefficients are rational numbers close
6016
to the coefficients of `f`.
6017
6018
INPUT:
6019
6020
- `f` -- polynomial with real floating point entries
6021
6022
- ``**kwds`` -- passed on to ``nearby_rational`` method
6023
6024
EXAMPLES::
6025
6026
sage: R.<x> = RR[]
6027
sage: sage.schemes.elliptic_curves.heegner.nearby_rational_poly(2.1*x^2 + 3.5*x - 1.2, max_error=10e-16)
6028
21/10*X^2 + 7/2*X - 6/5
6029
sage: sage.schemes.elliptic_curves.heegner.nearby_rational_poly(2.1*x^2 + 3.5*x - 1.2, max_error=10e-17)
6030
4728779608739021/2251799813685248*X^2 + 7/2*X - 5404319552844595/4503599627370496
6031
sage: RR(4728779608739021/2251799813685248 - 21/10)
6032
8.88178419700125e-17
6033
"""
6034
R = QQ['X']
6035
return R([a.nearby_rational(**kwds) for a in f])
6036
6037
def simplest_rational_poly(f, prec):
6038
"""
6039
Return a polynomial whose coefficients are as simple as possible
6040
rationals that are also close to the coefficients of f.
6041
6042
INPUT:
6043
6044
- `f` -- polynomial with real floating point entries
6045
6046
- ``prec`` -- positive integer
6047
6048
EXAMPLES::
6049
6050
sage: R.<x> = RR[]
6051
sage: sage.schemes.elliptic_curves.heegner.simplest_rational_poly(2.1*x^2 + 3.5*x - 1.2, 53)
6052
21/10*X^2 + 7/2*X - 6/5
6053
"""
6054
R = QQ['X']
6055
Z = RealField(prec)
6056
return R([Z(a).simplest_rational() for a in f])
6057
6058
def satisfies_weak_heegner_hypothesis(N, D):
6059
r"""
6060
Check that `D` satisfies the weak Heegner hypothesis relative to `N`.
6061
This is all that is needed to define Heegner points.
6062
6063
The condition is that `D<0` is a fundamental discriminant and that
6064
each unramified prime dividing `N` splits in `K=\QQ(\sqrt{D})` and
6065
each ramified prime exactly divides `N`. We also do not require
6066
that `D<-4`.
6067
6068
INPUT:
6069
6070
- `N` -- positive integer
6071
6072
- `D` -- negative integer
6073
6074
EXAMPLES::
6075
6076
sage: s = sage.schemes.elliptic_curves.heegner.satisfies_weak_heegner_hypothesis
6077
sage: s(37,-7)
6078
True
6079
sage: s(37,-37)
6080
False
6081
sage: s(37,-37*4)
6082
True
6083
sage: s(100,-4)
6084
False
6085
sage: [D for D in [-1,-2,..,-40] if s(37,D)]
6086
[-3, -4, -7, -11, -40]
6087
sage: [D for D in [-1,-2,..,-100] if s(37,D)]
6088
[-3, -4, -7, -11, -40, -47, -67, -71, -83, -84, -95]
6089
sage: EllipticCurve('37a').heegner_discriminants_list(10)
6090
[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104]
6091
"""
6092
if not number_field.is_fundamental_discriminant(D):
6093
return False
6094
if D >= 0: return False
6095
for p, e in N.factor():
6096
if D % p == 0:
6097
if e > 1:
6098
return False
6099
elif D.kronecker(p) != 1:
6100
return False
6101
return True
6102
6103
def make_monic(f):
6104
r"""
6105
``make_monic`` returns a monic integral polynomial `g` and an
6106
integer `d` such that if `\alpha` is a root of `g` then a root of
6107
`f` is `\alpha/d`.
6108
6109
INPUT:
6110
6111
- f -- polynomial over the rational numbers
6112
6113
EXAMPLES::
6114
6115
sage: R.<x> = QQ[]
6116
sage: sage.schemes.elliptic_curves.heegner.make_monic(3*x^3 + 14*x^2 - 7*x + 5)
6117
(x^3 + 14*x^2 - 21*x + 45, 3)
6118
6119
In this example we verify that make_monic does what we claim it does::
6120
6121
sage: K.<a> = NumberField(x^3 + 17*x - 3)
6122
sage: f = (a/7+2/3).minpoly(); f
6123
x^3 - 2*x^2 + 247/147*x - 4967/9261
6124
sage: g, d = sage.schemes.elliptic_curves.heegner.make_monic(f)
6125
sage: g
6126
x^3 - 18522*x^2 + 144110421*x - 426000323007
6127
sage: d
6128
9261
6129
sage: K.<b> = NumberField(g)
6130
sage: (b/d).minpoly()
6131
x^3 - 2*x^2 + 247/147*x - 4967/9261
6132
"""
6133
# make f monic
6134
n = f.degree()
6135
f = f / f.leading_coefficient()
6136
# find lcm of denominators
6137
d = arith.lcm([b.denominator() for b in f.list() if b])
6138
x = f.variables()[0]
6139
g = (d**n) * f(x/d)
6140
return g, d
6141
6142
6143
#####################################################################
6144
# Elliptic curve methods
6145
# Everywhere self below is an elliptic curve over QQ.
6146
#####################################################################
6147
6148
def ell_heegner_point(self, D, c=ZZ(1), f=None, check=True):
6149
"""
6150
Returns the Heegner point on this curve associated to the
6151
quadratic imaginary field `K=\QQ(\sqrt{D})`.
6152
6153
If the optional parameter `c` is given, returns the higher Heegner
6154
point associated to the order of conductor `c`.
6155
6156
INPUT::
6157
6158
- `D` -- a Heegner discriminant
6159
6160
- `c` -- (default: 1) conductor, must be coprime to `DN`
6161
6162
- `f` -- binary quadratic form or 3-tuple `(A,B,C)` of coefficients
6163
of `AX^2 + BXY + CY^2`
6164
6165
- ``check`` -- bool (default: ``True``)
6166
6167
6168
OUTPUT::
6169
6170
The Heegner point `y_c`.
6171
6172
6173
EXAMPLES::
6174
6175
sage: E = EllipticCurve('37a')
6176
sage: E.heegner_discriminants_list(10)
6177
[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104]
6178
sage: P = E.heegner_point(-7); P # indirect doctest
6179
Heegner point of discriminant -7 on elliptic curve of conductor 37
6180
sage: P.point_exact()
6181
(0 : 0 : 1)
6182
sage: P.curve()
6183
Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
6184
sage: P = E.heegner_point(-40).point_exact(); P
6185
(a : -a + 1 : 1)
6186
sage: P = E.heegner_point(-47).point_exact(); P
6187
(a : a^4 + a - 1 : 1)
6188
sage: P[0].parent()
6189
Number Field in a with defining polynomial x^5 - x^4 + x^3 + x^2 - 2*x + 1
6190
6191
Working out the details manually::
6192
6193
sage: P = E.heegner_point(-47).numerical_approx(prec=200)
6194
sage: f = algdep(P[0], 5); f
6195
x^5 - x^4 + x^3 + x^2 - 2*x + 1
6196
sage: f.discriminant().factor()
6197
47^2
6198
6199
The Heegner hypothesis is checked::
6200
6201
sage: E = EllipticCurve('389a'); P = E.heegner_point(-5,7);
6202
Traceback (most recent call last):
6203
...
6204
ValueError: N (=389) and D (=-5) must satisfy the Heegner hypothesis
6205
6206
We can specify the quadratic form::
6207
6208
sage: P = EllipticCurve('389a').heegner_point(-7, 5, (778,925,275)); P
6209
Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 389
6210
sage: P.quadratic_form()
6211
778*x^2 + 925*x*y + 275*y^2
6212
"""
6213
y = HeegnerPointOnX0N(self.conductor(), D, c, f, check=check)
6214
return y.map_to_curve(self)
6215
6216
def kolyvagin_point(self, D, c=ZZ(1), check=True):
6217
"""
6218
Returns the Kolyvagin point on this curve associated to the
6219
quadratic imaginary field `K=\QQ(\sqrt{D})` and conductor `c`.
6220
6221
INPUT:
6222
6223
- `D` -- a Heegner discriminant
6224
6225
- `c` -- (default: 1) conductor, must be coprime to `DN`
6226
6227
- ``check`` -- bool (default: ``True``)
6228
6229
6230
OUTPUT:
6231
6232
The Kolyvagin point `P` of conductor `c`.
6233
6234
EXAMPLES::
6235
6236
sage: E = EllipticCurve('37a1')
6237
sage: P = E.kolyvagin_point(-67); P
6238
Kolyvagin point of discriminant -67 on elliptic curve of conductor 37
6239
sage: P.numerical_approx() # imaginary parts approx. 0
6240
(6.00000000000000 ... : -15.0000000000000 ... : 1.00000000000000)
6241
sage: P.index()
6242
6
6243
sage: g = E((0,-1,1)) # a generator
6244
sage: E.regulator() == E.regulator_of_points([g])
6245
True
6246
sage: 6*g
6247
(6 : -15 : 1)
6248
6249
"""
6250
return self.heegner_point(D,c,check=check).kolyvagin_point()
6251
6252
def ell_heegner_discriminants(self, bound):
6253
"""
6254
Return the list of self's Heegner discriminants between -1 and
6255
-bound.
6256
6257
INPUT:
6258
6259
6260
- ``bound (int)`` - upper bound for -discriminant
6261
6262
6263
OUTPUT: The list of Heegner discriminants between -1 and -bound for
6264
the given elliptic curve.
6265
6266
EXAMPLES::
6267
6268
sage: E=EllipticCurve('11a')
6269
sage: E.heegner_discriminants(30) # indirect doctest
6270
[-7, -8, -19, -24]
6271
"""
6272
return [-D for D in xrange(1,bound) if self.satisfies_heegner_hypothesis(-D)]
6273
6274
def ell_heegner_discriminants_list(self, n):
6275
"""
6276
Return the list of self's first `n` Heegner discriminants smaller
6277
than -5.
6278
6279
INPUT:
6280
6281
- ``n (int)`` - the number of discriminants to
6282
compute
6283
6284
6285
OUTPUT: The list of the first n Heegner discriminants smaller than
6286
-5 for the given elliptic curve.
6287
6288
EXAMPLE::
6289
6290
sage: E=EllipticCurve('11a')
6291
sage: E.heegner_discriminants_list(4) # indirect doctest
6292
[-7, -8, -19, -24]
6293
"""
6294
v = []
6295
D = -5
6296
while len(v) < n:
6297
while not self.satisfies_heegner_hypothesis(D):
6298
D -= 1
6299
v.append(D)
6300
D -= 1
6301
return v
6302
6303
def heegner_point_height(self, D, prec=2, check_rank=True):
6304
r"""
6305
Use the Gross-Zagier formula to compute the Neron-Tate canonical
6306
height over `K` of the Heegner point corresponding to `D`, as an
6307
interval (it is computed to some precision using `L`-functions).
6308
6309
If the curve has rank at least 2, then the returned height is the
6310
exact Sage integer 0.
6311
6312
INPUT:
6313
6314
6315
- ``D (int)`` - fundamental discriminant (=/= -3, -4)
6316
6317
- ``prec (int)`` - (default: 2), use `prec \cdot \sqrt(N) + 20`
6318
terms of `L`-series in computations, where `N` is the
6319
conductor.
6320
6321
- ``check_rank`` - whether to check if the rank is at least 2 by
6322
computing the Mordell-Weil rank directly.
6323
6324
6325
OUTPUT: Interval that contains the height of the Heegner point.
6326
6327
EXAMPLE::
6328
6329
sage: E = EllipticCurve('11a')
6330
sage: E.heegner_point_height(-7)
6331
0.22227?
6332
6333
Some higher rank examples::
6334
6335
sage: E = EllipticCurve('389a')
6336
sage: E.heegner_point_height(-7)
6337
0
6338
sage: E = EllipticCurve('5077a')
6339
sage: E.heegner_point_height(-7)
6340
0
6341
sage: E.heegner_point_height(-7,check_rank=False)
6342
0.0000?
6343
"""
6344
6345
if not self.satisfies_heegner_hypothesis(D):
6346
raise ArithmeticError("Discriminant (=%s) must be a fundamental discriminant that satisfies the Heegner hypothesis."%D)
6347
6348
if check_rank and self.rank() >= 2:
6349
return ZZ(0)
6350
6351
if D == -3 or D == -4:
6352
raise ArithmeticError("Discriminant (=%s) must not be -3 or -4."%D)
6353
eps = self.root_number()
6354
L1_vanishes = self.lseries().L1_vanishes()
6355
6356
IR = rings.RealIntervalField(20) # TODO: why 20 bits here?
6357
6358
if eps == 1 and L1_vanishes:
6359
return IR(0) # rank even hence >= 2, so Heegner point is torsion.
6360
6361
RR = rings.RealField()
6362
from math import sqrt
6363
6364
alpha = RR(sqrt(abs(D)))/(2*self.period_lattice().complex_area())
6365
F = self.quadratic_twist(D)
6366
E = self
6367
k_E = prec*sqrt(E.conductor()) + 20
6368
k_F = prec*sqrt(F.conductor()) + 20
6369
6370
MIN_ERR = RR('1e-6') # we assume that regulator and
6371
# discriminant, etc., computed to this accuracy (which is easily the case).
6372
# this should be made more intelligent / rigorous relative
6373
# to the rest of the system.
6374
6375
if eps == 1: # E has even rank
6376
LF1, err_F = F.lseries().deriv_at1(k_F)
6377
LE1, err_E = E.lseries().at1(k_E)
6378
err_F = max(err_F, MIN_ERR)
6379
err_E = max(err_E, MIN_ERR)
6380
return IR(alpha-MIN_ERR,alpha+MIN_ERR) * IR(LE1-err_E,LE1+err_E) * IR(LF1-err_F,LF1+err_F)
6381
6382
else: # E has odd rank
6383
LE1, err_E = E.lseries().deriv_at1(k_E)
6384
LF1, err_F = F.lseries().at1(k_F)
6385
err_F = max(err_F, MIN_ERR)
6386
err_E = max(err_E, MIN_ERR)
6387
return IR(alpha-MIN_ERR,alpha+MIN_ERR) * IR(LE1-err_E,LE1+err_E) * IR(LF1-err_F,LF1+err_F)
6388
6389
6390
def heegner_index(self, D, min_p=2, prec=5, descent_second_limit=12, verbose_mwrank=False, check_rank=True):
6391
r"""
6392
Return an interval that contains the index of the Heegner
6393
point `y_K` in the group of `K`-rational points modulo torsion
6394
on this elliptic curve, computed using the Gross-Zagier
6395
formula and/or a point search, or possibly half the index
6396
if the rank is greater than one.
6397
6398
If the curve has rank > 1, then the returned index is infinity.
6399
6400
.. NOTE::
6401
6402
If ``min_p`` is bigger than 2 then the index can be off by
6403
any prime less than ``min_p``. This function returns the
6404
index divided by `2` exactly when the rank of `E(K)` is
6405
greater than 1 and `E(\QQ)_{/tor} \oplus E^D(\QQ)_{/tor}`
6406
has index `2` in `E(K)_{/tor}`, where the second factor
6407
undergoes a twist.
6408
6409
INPUT:
6410
6411
- ``D (int)`` - Heegner discriminant
6412
6413
- ``min_p (int)`` - (default: 2) only rule out primes
6414
= min_p dividing the index.
6415
6416
- ``verbose_mwrank (bool)`` - (default: ``False``); print lots of
6417
mwrank search status information when computing regulator
6418
6419
- ``prec (int)`` - (default: 5), use prec\*sqrt(N) +
6420
20 terms of L-series in computations, where N is the conductor.
6421
6422
- ``descent_second_limit`` - (default: 12)- used in 2-descent
6423
when computing regulator of the twist
6424
6425
- ``check_rank`` - whether to check if the rank is at least 2 by
6426
computing the Mordell-Weil rank directly.
6427
6428
6429
OUTPUT: an interval that contains the index, or half the index
6430
6431
EXAMPLES::
6432
6433
sage: E = EllipticCurve('11a')
6434
sage: E.heegner_discriminants(50)
6435
[-7, -8, -19, -24, -35, -39, -40, -43]
6436
sage: E.heegner_index(-7)
6437
1.00000?
6438
6439
::
6440
6441
sage: E = EllipticCurve('37b')
6442
sage: E.heegner_discriminants(100)
6443
[-3, -4, -7, -11, -40, -47, -67, -71, -83, -84, -95]
6444
sage: E.heegner_index(-95) # long time (1 second)
6445
2.00000?
6446
6447
This tests doing direct computation of the Mordell-Weil group.
6448
6449
::
6450
6451
sage: EllipticCurve('675b').heegner_index(-11)
6452
3.0000?
6453
6454
Currently discriminants -3 and -4 are not supported::
6455
6456
sage: E.heegner_index(-3)
6457
Traceback (most recent call last):
6458
...
6459
ArithmeticError: Discriminant (=-3) must not be -3 or -4.
6460
6461
The curve 681b returns the true index, which is `3`::
6462
6463
sage: E = EllipticCurve('681b')
6464
sage: I = E.heegner_index(-8); I
6465
3.0000?
6466
6467
In fact, whenever the returned index has a denominator of
6468
`2`, the true index is got by multiplying the returned
6469
index by `2`. Unfortunately, this is not an if and only if
6470
condition, i.e., sometimes the index must be multiplied by
6471
`2` even though the denominator is not `2`.
6472
6473
This example demonstrates the ``descent_second_limit`` option,
6474
which can be used to fine tune the 2-descent used to compute
6475
the regulator of the twist::
6476
6477
sage: E = EllipticCurve([0, 0, 1, -34874, -2506691])
6478
sage: E.heegner_index(-8)
6479
Traceback (most recent call last):
6480
...
6481
RuntimeError: ...
6482
6483
However when we search higher, we find the points we need::
6484
6485
sage: E.heegner_index(-8, descent_second_limit=16, check_rank=False)
6486
1.00000?
6487
6488
6489
Two higher rank examples (of ranks 2 and 3)::
6490
6491
sage: E = EllipticCurve('389a')
6492
sage: E.heegner_index(-7)
6493
+Infinity
6494
sage: E = EllipticCurve('5077a')
6495
sage: E.heegner_index(-7)
6496
+Infinity
6497
sage: E.heegner_index(-7, check_rank=False)
6498
0.001?
6499
sage: E.heegner_index(-7, check_rank=False).lower() == 0
6500
True
6501
"""
6502
if not self.satisfies_heegner_hypothesis(D):
6503
raise ArithmeticError("Discriminant (=%s) must be a fundamental discriminant that satisfies the Heegner hypothesis."%D)
6504
6505
if check_rank and self.rank() >= 2:
6506
return rings.infinity
6507
6508
# First compute upper bound on height of Heegner point.
6509
tm = verbose("computing heegner point height...")
6510
h0 = self.heegner_point_height(D, prec=prec, check_rank=check_rank)
6511
if h0 == 0:
6512
return rings.infinity
6513
6514
# We divide by 2 to get the height **over Q** of the
6515
# Heegner point on the twist.
6516
6517
ht = h0/2
6518
verbose('Height of heegner point = %s'%ht, tm)
6519
6520
if self.root_number() == 1:
6521
F = self.quadratic_twist(D)
6522
else:
6523
F = self
6524
# Now rank(F) > 0
6525
h = ht.upper()
6526
verbose("Heegner height bound = %s"%h)
6527
B = F.CPS_height_bound()
6528
verbose("CPS bound = %s"%B)
6529
c = h/(min_p**2) + B
6530
verbose("Search would have to be up to height = %s"%c)
6531
6532
from ell_rational_field import _MAX_HEIGHT
6533
6534
IR = rings.RealIntervalField(20) # todo: 20?
6535
6536
a = 1
6537
if c > _MAX_HEIGHT or F is self:
6538
verbose("Doing direct computation of MW group.")
6539
reg = F.regulator(descent_second_limit=descent_second_limit, verbose=verbose_mwrank)
6540
if F.rank(use_database=True) == 1:
6541
z = F.gens()[0]
6542
FK = F.base_extend(QuadraticField(D,'a'))
6543
z = FK(z)
6544
if z.is_divisible_by(2):
6545
a = 2
6546
else:
6547
FK_even_tor_pts = [T for T in FK.torsion_subgroup().gens() if T.order()%2==0]
6548
if len(FK_even_tor_pts) == 2:
6549
FK_even_tor_pts.append(sum(FK_even_tor_pts))
6550
for T in FK_even_tor_pts:
6551
if (z + T).is_divisible_by(2):
6552
a = 2; break
6553
return a*self._adjust_heegner_index(ht/IR(reg))
6554
6555
# Do naive search to eliminate possibility that Heegner point
6556
# is divisible by p<min_p, without finding Heegner point.
6557
verbose("doing point search")
6558
P = F.point_search(c)
6559
verbose("done with point search")
6560
P = [x for x in P if x.order() == rings.infinity]
6561
a = 1
6562
if len(P) == 0:
6563
return IR(1)
6564
elif len(P) == 1:
6565
z = P[0]
6566
FK = F.base_extend(QuadraticField(D,'a'))
6567
z = FK(z)
6568
if z.is_divisible_by(2):
6569
a = 2
6570
else:
6571
FK_even_tor_pts = [T for T in FK.torsion_subgroup().gens() if T.order()%2==0]
6572
if len(FK_even_tor_pts) == 2:
6573
FK_even_tor_pts.append(sum(FK_even_tor_pts))
6574
for T in FK_even_tor_pts:
6575
if (z + T).is_divisible_by(2):
6576
a = 2; break
6577
6578
verbose("saturating")
6579
S, I, reg = F.saturation(P)
6580
verbose("done saturating")
6581
return a*self._adjust_heegner_index(ht/IR(reg))
6582
6583
def _adjust_heegner_index(self, a):
6584
r"""
6585
Take the square root of the interval that contains the Heegner
6586
index.
6587
6588
EXAMPLES::
6589
6590
sage: E = EllipticCurve('11a1')
6591
sage: a = RIF(sqrt(2))-1.4142135623730951
6592
sage: E._adjust_heegner_index(a)
6593
1.?e-8
6594
"""
6595
if a.lower() < 0:
6596
IR = rings.RealIntervalField(20) # todo: 20?
6597
a = IR((0, a.upper()))
6598
return a.sqrt()
6599
6600
6601
def heegner_index_bound(self, D=0, prec=5, max_height=None):
6602
r"""
6603
Assume ``self`` has rank 0.
6604
6605
Return a list `v` of primes such that if an odd prime `p` divides
6606
the index of the Heegner point in the group of rational points
6607
modulo torsion, then `p` is in `v`.
6608
6609
If 0 is in the interval of the height of the Heegner point
6610
computed to the given prec, then this function returns `v =
6611
0`. This does not mean that the Heegner point is torsion, just
6612
that it is very likely torsion.
6613
6614
If we obtain no information from a search up to ``max_height``,
6615
e.g., if the Siksek et al. bound is bigger than ``max_height``,
6616
then we return `v = -1`.
6617
6618
INPUT:
6619
6620
6621
- ``D (int)`` - (default: 0) Heegner discriminant; if
6622
0, use the first discriminant -4 that satisfies the Heegner
6623
hypothesis
6624
6625
- ``verbose (bool)`` - (default: ``True``)
6626
6627
- ``prec (int)`` - (default: 5), use `prec \cdot \sqrt(N) + 20`
6628
terms of `L`-series in computations, where `N` is the conductor.
6629
6630
- ``max_height (float)`` - should be = 21; bound on
6631
logarithmic naive height used in point searches. Make smaller to
6632
make this function faster, at the expense of possibly obtaining a
6633
worse answer. A good range is between 13 and 21.
6634
6635
6636
OUTPUT:
6637
6638
6639
- ``v`` - list or int (bad primes or 0 or -1)
6640
6641
- ``D`` - the discriminant that was used (this is
6642
useful if `D` was automatically selected).
6643
6644
- ``exact`` - either False, or the exact Heegner index
6645
(up to factors of 2)
6646
6647
EXAMPLES::
6648
6649
sage: E = EllipticCurve('11a1')
6650
sage: E.heegner_index_bound()
6651
([2], -7, 2)
6652
"""
6653
from ell_rational_field import _MAX_HEIGHT
6654
if max_height is None:
6655
max_height = _MAX_HEIGHT
6656
else:
6657
max_height = min(float(max_height), _MAX_HEIGHT)
6658
if self.root_number() != 1:
6659
raise RuntimeError("The rank must be 0.")
6660
6661
if D == 0:
6662
D = -5
6663
while not self.satisfies_heegner_hypothesis(D):
6664
D -= 1
6665
6666
# First compute upper bound on Height of Heegner point.
6667
ht = self.heegner_point_height(D, prec=prec)
6668
if 0 in ht:
6669
return 0, D, False
6670
F = self.quadratic_twist(D)
6671
h = ht.upper()
6672
verbose("Heegner height bound = %s"%h)
6673
B = F.CPS_height_bound()
6674
verbose("CPS bound = %s"%B)
6675
if self.two_torsion_rank() == 0:
6676
H = h
6677
else:
6678
H = 4*h
6679
p = 3
6680
from sage.all import next_prime
6681
while True:
6682
c = H/(2*p**2) + B
6683
if c < max_height:
6684
break
6685
if p > 100:
6686
break
6687
p = next_prime(p)
6688
verbose("Using p = %s"%p)
6689
6690
if c > max_height:
6691
verbose("No information by searching only up to max_height (=%s)."%c)
6692
return -1, D, False
6693
6694
verbose("Searching up to height = %s"%c)
6695
eps = 10e-5
6696
6697
def _bound(P):
6698
"""
6699
We will use this function below in two places. It bounds the index
6700
using a nontrivial point.
6701
"""
6702
assert len(P) == 1
6703
6704
S, I, reg = F.saturation(P)
6705
6706
IR = rings.RealIntervalField(20) # todo: 20?
6707
h = IR(reg-eps,reg+eps)
6708
ind2 = ht/(h/2)
6709
verbose("index squared = %s"%ind2)
6710
ind = ind2.sqrt()
6711
verbose("index = %s"%ind)
6712
# Compute upper bound on square root of index.
6713
if ind.absolute_diameter() < 1:
6714
t, i = ind.is_int()
6715
if t: # unique integer in interval, so we've found exact index squared.
6716
return arith.prime_divisors(i), D, i
6717
raise RuntimeError("Unable to compute bound for e=%s, D=%s (try increasing precision)"%(self, D))
6718
6719
# First try a quick search, in case we get lucky and find
6720
# a generator.
6721
P = F.point_search(13, rank_bound=1)
6722
P = [x for x in P if x.order() == rings.infinity]
6723
if len(P) > 0:
6724
return _bound(P)
6725
6726
# Do search to eliminate possibility that Heegner point is
6727
# divisible by primes up to p, without finding Heegner point.
6728
P = F.point_search(c, rank_bound=1)
6729
P = [x for x in P if x.order() == rings.infinity]
6730
if len(P) == 0:
6731
# We've eliminated the possibility of a divisor up to p.
6732
return rings.prime_range(3, p), D, False
6733
else:
6734
return _bound(P)
6735
6736
6737
#################################################################################
6738
def _heegner_index_in_EK(self, D):
6739
"""
6740
Return the index of the sum of `E(\QQ)/tor + E^D(\QQ)/tor` in `E(K)/tor`.
6741
6742
INPUT:
6743
- `D` -- negative integer; the Heegner discriminant
6744
6745
OUTPUT:
6746
a power of 2 -- the given index
6747
6748
EXAMPLES:
6749
We compute the index for a rank 2 curve and found that it is 2::
6750
6751
sage: E = EllipticCurve('389a')
6752
sage: E._heegner_index_in_EK(-7)
6753
2
6754
6755
We explicitly verify in the above example that indeed that
6756
index is divisibly by 2 by writing down a generator of
6757
`E(\QQ)/tor + E^D(\QQ)/tor` that is divisible by 2 in `E(K)`::
6758
6759
sage: F = E.quadratic_twist(-7)
6760
sage: K = QuadraticField(-7,'a')
6761
sage: G = E.change_ring(K)
6762
sage: phi = F.change_ring(K).isomorphism_to(G)
6763
sage: P = G(E(-1,1)) + G((0,-1)) + G(phi(F(14,25))); P
6764
(-867/3872*a - 3615/3872 : -18003/170368*a - 374575/170368 : 1)
6765
sage: P.division_points(2)
6766
[(1/8*a + 5/8 : -5/16*a - 9/16 : 1)]
6767
6768
"""
6769
# check conditions, then use cache if possible.
6770
if not self.satisfies_heegner_hypothesis(D):
6771
raise ValueError("D (=%s) must satisfy the Heegner hypothesis"%D)
6772
try:
6773
return self.__heegner_index_in_EK[D]
6774
except AttributeError:
6775
self.__heegner_index_in_EK = {}
6776
except KeyError:
6777
pass
6778
6779
#####################################################################
6780
# THE ALGORITHM:
6781
#
6782
# For an element P of an abelian group A, let [P] denote the
6783
# equivalence class of P in the quotient A/A_tor of A by
6784
# its torsion subgroup. Then for P in E(Q) + E^D(QQ), we
6785
# have that [P] is divisible by 2 in E(K)/tor if and only
6786
# there is R in E(K) such that 2*[R] = [P], and this is
6787
# only if there is R in E(K) and t in E(K)_tor such that
6788
# 2*R = P + t.
6789
#
6790
# Using complex conjugation, one sees that the quotient
6791
# group E(K)/tor / ( E(Q)/tor + E^D(Q)/tor ) is killed by 2.
6792
# So to compute the order of this group we run through
6793
# representatives P for A/(2A) where A = E(Q)/tor + E^D(Q)/tor,
6794
# and for each we see whether there is a torsion point t in E(K)
6795
# such that P + t is divisible by 2. Also, we have
6796
# 2 | P+t <==> 2 | P+n*t for any odd integer n,
6797
# so we may assume t is of 2-power order.
6798
#####################################################################
6799
6800
E = self # nice shortcut
6801
F = E.quadratic_twist(D).minimal_model()
6802
K = rings.QuadraticField(D, 'a')
6803
6804
# Define a map phi that we'll use to put the points of E^D(QQ)
6805
# into E(K):
6806
G = E.change_ring(K)
6807
G2 = F.change_ring(K)
6808
phi = G2.isomorphism_to(G)
6809
6810
# Basis for E(Q)/tor oplus E^D(QQ)/tor in E(K):
6811
basis = [G(z) for z in E.gens()] + [G(phi(z)) for z in F.gens()]
6812
# Make a list of the 2-power order torsion points in E(K), including 0.
6813
T = [G(z) for z in G.torsion_subgroup().list() if z.order() == 1 or
6814
((z.order() % 2 == 0 and len(z.order().factor()) == 1))]
6815
6816
r = len(basis) # rank
6817
V = rings.QQ**r
6818
B = []
6819
6820
# Iterate through reps for A/(2*A) creating vectors in (1/2)*ZZ^r
6821
for v in rings.GF(2)**r:
6822
if not v: continue
6823
P = sum([basis[i] for i in range(r) if v[i]])
6824
for t in T:
6825
if (P+t).is_divisible_by(2):
6826
B.append(V(v)/2)
6827
6828
A = rings.ZZ**r
6829
# Take span of our vectors in (1/2)*ZZ^r, along with ZZ^r. This is E(K)/tor.
6830
W = V.span(B, rings.ZZ) + A
6831
6832
# Compute the index in E(K)/tor of A = E(Q)/tor + E^D(Q)/tor, cache, and return.
6833
index = A.index_in(W)
6834
self.__heegner_index_in_EK[D] = index
6835
return index
6836
6837
def heegner_sha_an(self, D, prec=53):
6838
r"""
6839
Return the conjectural (analytic) order of Sha for E over the field `K=\QQ(\sqrt{D})`.
6840
6841
INPUT:
6842
6843
- `D` -- negative integer; the Heegner discriminant
6844
6845
- prec -- integer (default: 53); bits of precision to
6846
compute analytic order of Sha
6847
6848
OUTPUT:
6849
6850
(floating point number) an approximation to the conjectural order of Sha.
6851
6852
.. NOTE::
6853
6854
Often you'll want to do ``proof.elliptic_curve(False)`` when
6855
using this function, since often the twisted elliptic
6856
curves that come up have enormous conductor, and Sha is
6857
nontrivial, which makes provably finding the Mordell-Weil
6858
group using 2-descent difficult.
6859
6860
6861
EXAMPLES:
6862
6863
An example where E has conductor 11::
6864
6865
sage: E = EllipticCurve('11a')
6866
sage: E.heegner_sha_an(-7) # long time
6867
1.00000000000000
6868
6869
The cache works::
6870
6871
sage: E.heegner_sha_an(-7) is E.heegner_sha_an(-7) # long time
6872
True
6873
6874
Lower precision::
6875
6876
sage: E.heegner_sha_an(-7,10) # long time
6877
1.0
6878
6879
Checking that the cache works for any precision::
6880
6881
sage: E.heegner_sha_an(-7,10) is E.heegner_sha_an(-7,10) # long time
6882
True
6883
6884
Next we consider a rank 1 curve with nontrivial Sha over the
6885
quadratic imaginary field `K`; however, there is no Sha for `E`
6886
over `\QQ` or for the quadratic twist of `E`::
6887
6888
sage: E = EllipticCurve('37a')
6889
sage: E.heegner_sha_an(-40) # long time
6890
4.00000000000000
6891
sage: E.quadratic_twist(-40).sha().an() # long time
6892
1
6893
sage: E.sha().an() # long time
6894
1
6895
6896
A rank 2 curve::
6897
6898
sage: E = EllipticCurve('389a') # long time
6899
sage: E.heegner_sha_an(-7) # long time
6900
1.00000000000000
6901
6902
If we remove the hypothesis that `E(K)` has rank 1 in Conjecture
6903
2.3 in [Gross-Zagier, 1986, page 311], then that conjecture is
6904
false, as the following example shows::
6905
6906
sage: E = EllipticCurve('65a') # long time
6907
sage: E.heegner_sha_an(-56) # long time
6908
1.00000000000000
6909
sage: E.torsion_order() # long time
6910
2
6911
sage: E.tamagawa_product() # long time
6912
1
6913
sage: E.quadratic_twist(-56).rank() # long time
6914
2
6915
"""
6916
# check conditions, then return from cache if possible.
6917
if not self.satisfies_heegner_hypothesis(D):
6918
raise ValueError("D (=%s) must satisfy the Heegner hypothesis"%D)
6919
try:
6920
return self.__heegner_sha_an[(D, prec)]
6921
except AttributeError:
6922
self.__heegner_sha_an = {}
6923
except KeyError:
6924
pass
6925
6926
# Use the BSD conjecture over the quadratic imaginary K --
6927
# see page 311 of [Gross-Zagier, 1986] for the formula.
6928
E = self # notational convenience
6929
F = E.quadratic_twist(D).minimal_model()
6930
K = rings.QuadraticField(D, 'a')
6931
6932
# Compute each of the quantities in BSD
6933
# - The torsion subgroup over K.
6934
T = E.change_ring(K).torsion_order()
6935
6936
# - The product of the Tamagawa numbers, which because D is
6937
# coprime to N is just the square of the product of the
6938
# Tamagawa numbers over QQ for E. (we square below in the
6939
# BSD formula)
6940
cqprod = E.tamagawa_product()
6941
6942
# - The leading term of the L-series, as a product of two
6943
# other L-series.
6944
rE = E.rank()
6945
rF = F.rank()
6946
L_E = E.lseries().dokchitser(prec).derivative(1, rE)
6947
L_F = F.lseries().dokchitser(prec).derivative(1, rF)
6948
# NOTE: The binomial coefficient in the following formula
6949
# for the leading term in terms of the other two leading
6950
# terms comes from the product rule for the derivative.
6951
# You can think this through or just type something like
6952
# f = function('f',x); g = function('g',x); diff(f*g,6)
6953
# into Sage to be convinced.
6954
L = rings.binomial(rE + rF, rE) * (L_E * L_F / (rings.factorial(rE+rF)) )
6955
6956
# - ||omega||^2 -- the period. It is twice the volume of the
6957
# period lattice. See the following paper for a derivation:
6958
# "Verification of the Birch and Swinnerton-Dyer Conjecture
6959
# for Specific Elliptic Curves", G. Grigorov, A. Jorza, S. Patrikis,
6960
# C. Patrascu, W. Stein
6961
omega = 2 * abs(E.period_lattice().basis_matrix().det())
6962
6963
# - The regulator.
6964
# First we compute the regualtor of the subgroup E(QQ) + E^D(QQ)
6965
# of E(K). The factor of 2 in the regulator
6966
# accounts for the fact that the height over K is twice the
6967
# height over QQ, i.e., for P in E(QQ) we have h_K(P,P) =
6968
# 2*h_Q(P,P). See, e.g., equation (6.4) on page 230 of
6969
# [Gross-Zagier, 1986].
6970
Reg_prod = 2**(rE + rF) * E.regulator(precision=prec) * F.regulator(precision=prec)
6971
# Next we call off to the _heegner_index_in_EK function, which
6972
# saturates the group E(QQ) + E^D(QQ) in E(K), given us the index,
6973
# which must be a power of 2, since E(QQ) is the +1 eigenspace for
6974
# complex conjugation, and E^D(QQ) is the -1 eigenspace.
6975
ind = self._heegner_index_in_EK(D)
6976
# Finally, we know the regulator of E(K).
6977
Reg = Reg_prod / ind**2
6978
6979
# - Square root of the absolute value of the discriminant. This is
6980
# easy; we just make sure the D passed in is an integer, so we
6981
# can call sqrt with the chosen precision.
6982
sqrtD = ZZ(abs(D)).sqrt(prec=prec)
6983
6984
# - Done: Finally, we plug everything into the BSD formula to get the
6985
# analytic order of Sha.
6986
sha_an = (L * T**2 * sqrtD) / (omega * Reg * cqprod**2)
6987
6988
# - We cache and return the answer.
6989
self.__heegner_sha_an[(D, prec)] = sha_an
6990
return sha_an
6991
6992
def _heegner_forms_list(self, D, beta=None, expected_count=None):
6993
"""
6994
Returns a list of quadratic forms corresponding to Heegner points
6995
with discriminant `D` and a choice of `\beta` a square root of
6996
`D` mod `4N`. Specifically, given a quadratic form
6997
`f = Ax^2 + Bxy + Cy^2` we let `\tau_f` be a root of `Ax^2 + Bx + C`
6998
and the discriminant `\Delta(\tau_f) = \Delta(f) = D` must be
6999
invariant under multiplication by `N`, the conductor of ``self``.
7000
7001
`\Delta(N\tau_f) = \Delta(\tau_f) = \Delta(f) = D`
7002
7003
EXAMPLES::
7004
7005
sage: E = EllipticCurve('37a')
7006
sage: E._heegner_forms_list(-7)
7007
[37*x^2 + 17*x*y + 2*y^2]
7008
sage: E._heegner_forms_list(-195)
7009
[37*x^2 + 29*x*y + 7*y^2, 259*x^2 + 29*x*y + y^2, 111*x^2 + 177*x*y + 71*y^2, 2627*x^2 + 177*x*y + 3*y^2]
7010
sage: E._heegner_forms_list(-195)[-1].discriminant()
7011
-195
7012
sage: len(E._heegner_forms_list(-195))
7013
4
7014
sage: QQ[sqrt(-195)].class_number()
7015
4
7016
7017
sage: E = EllipticCurve('389a')
7018
sage: E._heegner_forms_list(-7)
7019
[389*x^2 + 185*x*y + 22*y^2]
7020
sage: E._heegner_forms_list(-59)
7021
[389*x^2 + 313*x*y + 63*y^2, 1167*x^2 + 313*x*y + 21*y^2, 3501*x^2 + 313*x*y + 7*y^2]
7022
"""
7023
if expected_count is None:
7024
expected_count = number_field.QuadraticField(D, 'a').class_number()
7025
N = self.conductor()
7026
if beta is None:
7027
beta = Integers(4*N)(D).sqrt(extend=False)
7028
else:
7029
assert beta**2 == Integers(4*N)(D)
7030
from sage.quadratic_forms.all import BinaryQF
7031
b = ZZ(beta) % (2*N)
7032
all = []
7033
seen = []
7034
# Note: This may give a sub-optimal list of forms.
7035
while True:
7036
R = (b**2-D)//(4*N)
7037
for d in R.divisors():
7038
f = BinaryQF([d*N, b, R//d])
7039
fr = f.reduced_form()
7040
if fr not in seen:
7041
seen.append(fr)
7042
all.append(f)
7043
if len(all) == expected_count:
7044
return all
7045
b += 2*N
7046
7047
def _heegner_best_tau(self, D, prec=None):
7048
r"""
7049
Given a discrimanent `D`, find the Heegner point `\tau` in the
7050
upper half plane with largest imaginary part (which is optimal
7051
for evaluating the modular parametrization). If the optional
7052
parameter ``prec`` is given, return `\tau` to ``prec`` bits of
7053
precision, otherwise return it exactly as a symbolic object.
7054
7055
EXAMPLES::
7056
7057
sage: E = EllipticCurve('37a')
7058
sage: E._heegner_best_tau(-7)
7059
1/74*sqrt(-7) - 17/74
7060
sage: EllipticCurve('389a')._heegner_best_tau(-11)
7061
1/778*sqrt(-11) - 355/778
7062
sage: EllipticCurve('389a')._heegner_best_tau(-11, prec=100)
7063
-0.45629820051413881748071979434 + 0.0042630138693514136878083968338*I
7064
"""
7065
# We know that N|A, so A = N is optimal.
7066
N = self.conductor()
7067
b = ZZ(Integers(4*N)(D).sqrt(extend=False) % (2*N))
7068
# TODO: make sure a different choice of b is not better?
7069
return (-b + ZZ(D).sqrt(prec=prec)) / (2*N)
7070
7071
def satisfies_heegner_hypothesis(self, D):
7072
"""
7073
Returns ``True`` precisely when `D` is a fundamental discriminant that
7074
satisfies the Heegner hypothesis for this elliptic curve.
7075
7076
EXAMPLES::
7077
7078
sage: E = EllipticCurve('11a1')
7079
sage: E.satisfies_heegner_hypothesis(-7)
7080
True
7081
sage: E.satisfies_heegner_hypothesis(-11)
7082
False
7083
"""
7084
if not number_field.is_fundamental_discriminant(D):
7085
return False
7086
D = ZZ(D)
7087
if D >= 0: return False
7088
if D.gcd(self.conductor()) != 1:
7089
return False
7090
for p, _ in self.conductor().factor():
7091
if D.kronecker(p) != 1:
7092
return False
7093
return True
7094
7095
7096
#####################################################################
7097
# End of elliptic curve methods.
7098
#####################################################################
7099
7100