Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/schemes/elliptic_curves/heegner.py
4128 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: This is a *ring* class field, not a ray class field. In
207
general, the ring class field of given conductor is a subfield of
208
the ray class field of the same conductor.
209
210
EXAMPLES::
211
212
sage: heegner_point(37,-7).ring_class_field()
213
Hilbert class field of QQ[sqrt(-7)]
214
sage: heegner_point(37,-7,5).ring_class_field()
215
Ring class field extension of QQ[sqrt(-7)] of conductor 5
216
sage: heegner_point(37,-7,55).ring_class_field()
217
Ring class field extension of QQ[sqrt(-7)] of conductor 55
218
219
TESTS::
220
221
sage: K_c = heegner_point(37,-7).ring_class_field()
222
sage: type(K_c)
223
<class 'sage.schemes.elliptic_curves.heegner.RingClassField'>
224
sage: loads(dumps(K_c)) == K_c
225
True
226
"""
227
def __init__(self, D, c, check=True):
228
"""
229
INPUTS:
230
231
- `D` -- discriminant of quadratic imaginary field
232
233
- `c` -- conductor (positive integer coprime to `D`)
234
235
- ``check`` -- bool (default: True); whether to check
236
validity of input
237
238
EXAMPLES::
239
240
sage: sage.schemes.elliptic_curves.heegner.RingClassField(-7,5, False)
241
Ring class field extension of QQ[sqrt(-7)] of conductor 5
242
243
"""
244
if check:
245
D = ZZ(D); c = ZZ(c)
246
self.__D = D
247
self.__c = c
248
249
def __eq__(self, other):
250
"""
251
Used for equality testing.
252
253
EXAMPLES::
254
255
sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()
256
sage: K11 = E.heegner_point(-7,11).ring_class_field()
257
sage: K5 == K11
258
False
259
sage: K5 == K5
260
True
261
sage: K11 == 11
262
False
263
"""
264
return isinstance(other, RingClassField) and self.__D == other.__D and self.__c == other.__c
265
266
def __hash__(self):
267
"""
268
Used for computing hash of self.
269
270
EXAMPLES::
271
272
sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()
273
sage: hash(K5)
274
-3713088127102618519 # 64-bit
275
1817441385 # 32-bit
276
sage: hash((-7,5))
277
-3713088127102618519 # 64-bit
278
1817441385 # 32-bit
279
"""
280
return hash((self.__D, self.__c))
281
282
def conductor(self):
283
"""
284
Return the conductor of this ring class field.
285
286
EXAMPLES::
287
288
sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()
289
sage: K5.conductor()
290
5
291
"""
292
return self.__c
293
294
def discriminant_of_K(self):
295
"""
296
Return the discriminant of the quadratic imaginary field `K` contained in self.
297
298
EXAMPLES::
299
300
sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()
301
sage: K5.discriminant_of_K()
302
-7
303
"""
304
return self.__D
305
306
@cached_method
307
def ramified_primes(self):
308
r"""
309
Return the primes of `\ZZ` that ramify in this ring class field.
310
311
EXAMPLES::
312
313
sage: E = EllipticCurve('389a'); K55 = E.heegner_point(-7,55).ring_class_field()
314
sage: K55.ramified_primes()
315
[5, 7, 11]
316
sage: E.heegner_point(-7).ring_class_field().ramified_primes()
317
[7]
318
"""
319
return arith.prime_divisors(self.__D * self.__c)
320
321
def _repr_(self):
322
"""
323
EXAMPLES::
324
325
sage: heegner_point(37,-7,55).ring_class_field()._repr_()
326
'Ring class field extension of QQ[sqrt(-7)] of conductor 55'
327
sage: heegner_point(37,-7).ring_class_field()._repr_()
328
'Hilbert class field of QQ[sqrt(-7)]'
329
"""
330
c = self.__c
331
if c == 1:
332
return "Hilbert class field of QQ[sqrt(%s)]"%self.__D
333
else:
334
return "Ring class field extension of QQ[sqrt(%s)] of conductor %s"%(self.__D, self.__c)
335
336
@cached_method
337
def degree_over_K(self):
338
"""
339
Return the relative degree of this ring class field over the
340
quadratic imaginary field `K`.
341
342
EXAMPLES::
343
344
sage: E = EllipticCurve('389a'); P = E.heegner_point(-7,5)
345
sage: K5 = P.ring_class_field(); K5
346
Ring class field extension of QQ[sqrt(-7)] of conductor 5
347
sage: K5.degree_over_K()
348
6
349
sage: type(K5.degree_over_K())
350
<type 'sage.rings.integer.Integer'>
351
352
sage: E = EllipticCurve('389a'); E.heegner_point(-20).ring_class_field().degree_over_K()
353
2
354
sage: E.heegner_point(-20,3).ring_class_field().degree_over_K()
355
4
356
sage: kronecker(-20,11)
357
-1
358
sage: E.heegner_point(-20,11).ring_class_field().degree_over_K()
359
24
360
"""
361
D, c = self.__D, self.__c
362
363
K = self.quadratic_field()
364
365
# Multiply class number by relative degree of the Hilbert class field H over K.
366
return K.class_number() * self.degree_over_H()
367
368
@cached_method
369
def degree_over_H(self):
370
"""
371
Return the degree of this field over the Hilbert class field `H` of `K`.
372
373
EXAMPLES::
374
375
sage: E = EllipticCurve('389a')
376
sage: E.heegner_point(-59).ring_class_field().degree_over_H()
377
1
378
sage: E.heegner_point(-59).ring_class_field().degree_over_K()
379
3
380
sage: QuadraticField(-59,'a').class_number()
381
3
382
383
Some examples in which prime dividing c is inert::
384
385
sage: heegner_point(37,-7,3).ring_class_field().degree_over_H()
386
4
387
sage: heegner_point(37,-7,3^2).ring_class_field().degree_over_H()
388
12
389
sage: heegner_point(37,-7,3^3).ring_class_field().degree_over_H()
390
36
391
392
The prime dividing c is split. For example, in the first case
393
`O_K/cO_K` is isomorphic to a direct sum of two copies of
394
``GF(2)``, so the units are trivial::
395
396
sage: heegner_point(37,-7,2).ring_class_field().degree_over_H()
397
1
398
sage: heegner_point(37,-7,4).ring_class_field().degree_over_H()
399
2
400
sage: heegner_point(37,-7,8).ring_class_field().degree_over_H()
401
4
402
403
Now c is ramified::
404
405
sage: heegner_point(37,-7,7).ring_class_field().degree_over_H()
406
7
407
sage: heegner_point(37,-7,7^2).ring_class_field().degree_over_H()
408
49
409
"""
410
c = self.__c
411
if c == 1:
412
return ZZ(1)
413
414
# Let K_c be the ring class field. We have by class field theory that
415
# Gal(K_c / H) = (O_K/c*O_K)^* / (Z/cZ)^*.
416
#
417
# To compute the cardinality of the above Galois group, we
418
# first reduce to the case that c = p^e is a prime power
419
# (since the expression is multiplicative in c).
420
# Of course, note also that #(Z/cZ)^* = phi(c)
421
#
422
# Case 1: p splits in O_K. Then
423
# #(O_K/p^e*O_K)^* = (#(Z/p^eZ)^*)^2 = phi(p^e)^2, so
424
# #(O_K/p^e*O_K)^*/(Z/p^eZ)^* = phi(p^e) = p^e - p^(e-1)
425
#
426
# Case 2: p is inert in O_K. Then
427
# #(O_K/p^e O_K)^* = p^(2*e)-p^(2*(e-1))
428
# so #(O_K/p^e*O_K)^*/(Z/p^eZ)^*
429
# = (p^(2*e)-p^(2*(e-1)))/(p^e-p^(e-1)) = p^e + p^(e-1).
430
#
431
# Case 3: p ramified in O_K. Then
432
# #(O_K/p^e O_K)^* = p^(2*e) - p^(2*e-1),
433
# so #(O_K/p^e O_K)^*/#(Z/p^eZ)^* = p^e.
434
#
435
# Section 4.2 of Cohen's "Advanced Computational Algebraic
436
# Number Theory" GTM is also relevant, though Cohen is working
437
# with *ray* class fields and here we want the cardinality
438
# of the *ring* class field, which is a subfield.
439
440
K = self.quadratic_field()
441
442
n = ZZ(1)
443
for p, e in c.factor():
444
F = K.factor(p)
445
if len(F) == 2:
446
# split case
447
n *= p**e - p**(e-1)
448
else:
449
if F[0][1] > 1:
450
# ramified case
451
n *= p**e
452
else:
453
# inert case
454
n *= p**e + p**(e-1)
455
return n
456
457
@cached_method
458
def absolute_degree(self):
459
r"""
460
Return the absolute degree of this field over `\QQ`.
461
462
EXAMPLES::
463
464
sage: E = EllipticCurve('389a'); K = E.heegner_point(-7,5).ring_class_field()
465
sage: K.absolute_degree()
466
12
467
sage: K.degree_over_K()
468
6
469
"""
470
return 2*self.degree_over_K()
471
472
degree_over_Q = absolute_degree
473
474
@cached_method
475
def quadratic_field(self):
476
r"""
477
Return the quadratic imaginary field `K = \QQ(\sqrt{D})`.
478
479
EXAMPLES::
480
481
sage: E = EllipticCurve('389a'); K = E.heegner_point(-7,5).ring_class_field()
482
sage: K.quadratic_field()
483
Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
484
"""
485
D = self.__D
486
var = 'sqrt_minus_%s'%(-D)
487
return number_field.QuadraticField(D,var)
488
489
@cached_method
490
def galois_group(self, base=QQ):
491
"""
492
Return the Galois group of self over base.
493
494
INPUT:
495
496
- ``base`` -- (default: `\QQ`) a subfield of self or `\QQ`
497
498
EXAMPLES::
499
500
sage: E = EllipticCurve('389a')
501
sage: A = E.heegner_point(-7,5).ring_class_field()
502
sage: A.galois_group()
503
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5
504
sage: B = E.heegner_point(-7).ring_class_field()
505
sage: C = E.heegner_point(-7,15).ring_class_field()
506
sage: A.galois_group()
507
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5
508
sage: A.galois_group(B)
509
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Hilbert class field of QQ[sqrt(-7)]
510
sage: A.galois_group().cardinality()
511
12
512
sage: A.galois_group(B).cardinality()
513
6
514
sage: C.galois_group(A)
515
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
516
sage: C.galois_group(A).cardinality()
517
4
518
519
"""
520
return GaloisGroup(self, base)
521
522
def is_subfield(self, M):
523
"""
524
Return True if this ring class field is a subfield of the ring class field `M`.
525
If `M` is not a ring class field, then a TypeError is raised.
526
527
EXAMPLES::
528
529
sage: E = EllipticCurve('389a')
530
sage: A = E.heegner_point(-7,5).ring_class_field()
531
sage: B = E.heegner_point(-7).ring_class_field()
532
sage: C = E.heegner_point(-20).ring_class_field()
533
sage: D = E.heegner_point(-7,15).ring_class_field()
534
sage: B.is_subfield(A)
535
True
536
sage: B.is_subfield(B)
537
True
538
sage: B.is_subfield(D)
539
True
540
sage: B.is_subfield(C)
541
False
542
sage: A.is_subfield(B)
543
False
544
sage: A.is_subfield(D)
545
True
546
"""
547
if not isinstance(M, RingClassField):
548
raise TypeError, "M must be a ring class field"
549
return self.quadratic_field() == M.quadratic_field() and \
550
M.conductor() % self.conductor() == 0
551
552
##################################################################################
553
#
554
# Galois groups of ring class fields
555
#
556
##################################################################################
557
558
class GaloisGroup(SageObject):
559
"""
560
A Galois group of a ring class field.
561
562
EXAMPLES::
563
564
sage: E = EllipticCurve('389a')
565
sage: G = E.heegner_point(-7,5).ring_class_field().galois_group(); G
566
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5
567
sage: G.field()
568
Ring class field extension of QQ[sqrt(-7)] of conductor 5
569
sage: G.cardinality()
570
12
571
sage: G.complex_conjugation()
572
Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 5
573
574
TESTS::
575
576
sage: G = heegner_point(37,-7).ring_class_field().galois_group()
577
sage: loads(dumps(G)) == G
578
True
579
sage: type(G)
580
<class 'sage.schemes.elliptic_curves.heegner.GaloisGroup'>
581
"""
582
def __init__(self, field, base=QQ):
583
r"""
584
INPUT:
585
586
- ``field`` -- a ring class field
587
588
- ``base`` -- subfield of field (default: `\QQ`)
589
590
EXAMPLES::
591
592
sage: K5 = heegner_points(389,-7,5).ring_class_field()
593
sage: K1 = heegner_points(389,-7,1).ring_class_field()
594
sage: sage.schemes.elliptic_curves.heegner.GaloisGroup(K5,K1)
595
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Hilbert class field of QQ[sqrt(-7)]
596
sage: K5.galois_group(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
"""
599
if not isinstance(field, RingClassField):
600
raise TypeError, "field must be of type RingClassField"
601
if base != QQ and base != field.quadratic_field():
602
if not isinstance(base, RingClassField):
603
raise TypeError, "base must be of type RingClassField or QQ or quadratic field"
604
if not base.is_subfield(field):
605
raise TypeError, "base must be a subfield of field"
606
self.__field = field
607
self.__base = base
608
609
def __eq__(self, G):
610
"""
611
EXAMPLES::
612
613
sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()
614
sage: G == G
615
True
616
sage: G == 0
617
False
618
sage: H = EllipticCurve('389a').heegner_point(-7,11).ring_class_field().galois_group()
619
sage: G == H
620
False
621
"""
622
return isinstance(G, GaloisGroup) and (G.__field,G.__base) == (self.__field,self.__base)
623
624
def __hash__(self):
625
"""
626
Return hash of this Galois group, which is the same as the
627
hash of the pair, the field and its base.
628
629
EXAMPLES::
630
631
sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()
632
sage: hash(G)
633
-6198252699510613726 # 64-bit
634
1905285410 # 32-bit
635
sage: hash((G.field(), G.base_field()))
636
-6198252699510613726 # 64-bit
637
1905285410 # 32-bit
638
"""
639
return hash((self.__field, self.__base))
640
641
def __call__(self, x):
642
"""
643
Coerce `x` into self, where `x` is a Galois group element, or
644
in case self has base field the Hilbert class field, `x` can
645
also be an element of the ring of integers.
646
647
INPUT:
648
649
- `x` -- automorphism or quadratic field element
650
651
OUTPUT:
652
653
- automorphism (or TypeError)
654
655
EXAMPLES::
656
657
sage: K5 = heegner_points(389,-52,5).ring_class_field()
658
sage: K1 = heegner_points(389,-52,1).ring_class_field()
659
sage: G = K5.galois_group(K1)
660
sage: G(1)
661
Class field automorphism defined by x^2 + 325*y^2
662
sage: G(G[0])
663
Class field automorphism defined by x^2 + 325*y^2
664
sage: alpha = 2 + K1.quadratic_field().gen(); alpha
665
sqrt_minus_52 + 2
666
sage: G(alpha)
667
Class field automorphism defined by 14*x^2 - 10*x*y + 25*y^2
668
669
A TypeError is raised when the coercion isn't possible::
670
671
sage: G(0)
672
Traceback (most recent call last):
673
...
674
TypeError: x does not define element of (O_K/c*O_K)^*
675
676
"""
677
if isinstance(x, GaloisAutomorphism) and x.parent() == self:
678
return x
679
try:
680
return self._alpha_to_automorphism(x)
681
except Exception, msg:
682
raise TypeError, "x does not define element of (O_K/c*O_K)^*"
683
684
def _repr_(self):
685
"""
686
Return string representation of this Galois group.
687
688
EXAMPLES::
689
690
sage: E = EllipticCurve('389a')
691
sage: G = E.heegner_point(-7,5).ring_class_field().galois_group()
692
sage: G._repr_()
693
'Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5'
694
"""
695
if self.base_field() != QQ:
696
s = " over %s"%self.base_field()
697
else:
698
s = ''
699
return "Galois group of %s%s"%(self.field(), s)
700
701
def field(self):
702
"""
703
Return the ring class field that this Galois group acts on.
704
705
EXAMPLES::
706
707
sage: G = heegner_point(389,-7,5).ring_class_field().galois_group()
708
sage: G.field()
709
Ring class field extension of QQ[sqrt(-7)] of conductor 5
710
"""
711
return self.__field
712
713
def base_field(self):
714
"""
715
Return the base field, which the field fixed by all the
716
automorphisms in this Galois group.
717
718
EXAMPLES::
719
720
sage: x = heegner_point(37,-7,5)
721
sage: Kc = x.ring_class_field(); Kc
722
Ring class field extension of QQ[sqrt(-7)] of conductor 5
723
sage: K = x.quadratic_field()
724
sage: G = Kc.galois_group(); G
725
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5
726
sage: G.base_field()
727
Rational Field
728
sage: G.cardinality()
729
12
730
sage: Kc.absolute_degree()
731
12
732
sage: G = Kc.galois_group(K); G
733
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
734
sage: G.cardinality()
735
6
736
sage: G.base_field()
737
Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
738
sage: G = Kc.galois_group(Kc); G
739
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
740
sage: G.cardinality()
741
1
742
sage: G.base_field()
743
Ring class field extension of QQ[sqrt(-7)] of conductor 5
744
"""
745
return self.__base
746
747
@cached_method
748
def kolyvagin_generators(self):
749
r"""
750
Assuming this Galois group `G` is of the form
751
`G=\textrm{Gal}(K_c/K_1)`, with `c=p_1\dots p_n` satisfying the
752
Kolyvagin hypothesis, this function returns noncanonical
753
choices of lifts of generators for each of the cyclic factors
754
of `G` corresponding to the primes dividing `c`. Thus the
755
`i`-th returned valued is an element of `G` that maps to the
756
identity element of `\textrm{Gal}(K_p/K_1)` for all `p \neq p_i` and
757
to a choice of generator of `\textrm{Gal}(K_{p_i}/K_1)`.
758
759
OUTPUT:
760
761
- list of elements of self
762
763
EXAMPLES::
764
765
sage: K3 = heegner_points(389,-52,3).ring_class_field()
766
sage: K1 = heegner_points(389,-52,1).ring_class_field()
767
sage: G = K3.galois_group(K1)
768
sage: G.kolyvagin_generators()
769
(Class field automorphism defined by 9*x^2 - 6*x*y + 14*y^2,)
770
771
sage: K5 = heegner_points(389,-52,5).ring_class_field()
772
sage: K1 = heegner_points(389,-52,1).ring_class_field()
773
sage: G = K5.galois_group(K1)
774
sage: G.kolyvagin_generators()
775
(Class field automorphism defined by 17*x^2 - 14*x*y + 22*y^2,)
776
"""
777
M = self.field()
778
c = M.conductor()
779
if not (self._base_is_hilbert_class_field() and self.is_kolyvagin()):
780
raise ValueError, "field must be of the form Gal(K_c/K_1)"
781
if not c.is_prime():
782
raise NotImplementedError, "only implemented when c is prime"
783
784
# Since c satisfies Kolyvagin and is prime, the group is cyclic,
785
# so we just find a generator.
786
for sigma in self:
787
if sigma.order() == self.cardinality():
788
return tuple([sigma])
789
790
raise NotImplementedError
791
792
@cached_method
793
def lift_of_hilbert_class_field_galois_group(self):
794
r"""
795
Assuming this Galois group `G` is of the form `G=\textrm{Gal}(K_c/K)`,
796
this function returns noncanonical choices of lifts of the
797
elements of the quotient group `\textrm{Gal}(K_1/K)`.
798
799
OUTPUT:
800
801
- tuple of elements of self
802
803
EXAMPLES::
804
805
sage: K5 = heegner_points(389,-52,5).ring_class_field()
806
sage: G = K5.galois_group(K5.quadratic_field())
807
sage: G.lift_of_hilbert_class_field_galois_group()
808
(Class field automorphism defined by x^2 + 325*y^2, Class field automorphism defined by 2*x^2 + 2*x*y + 163*y^2)
809
sage: G.cardinality()
810
12
811
sage: K5.quadratic_field().class_number()
812
2
813
"""
814
if not self._base_is_quad_imag_field():
815
raise ValueError, "Galois group must be of the form Gal(K_c/K)"
816
K = self.base_field()
817
C = K.class_group()
818
v = []
819
lifts = []
820
for sigma in self:
821
I = sigma.ideal()
822
g = C(I)
823
if g not in v:
824
v.append(g)
825
lifts.append(sigma)
826
return tuple(lifts)
827
828
@cached_method
829
def _list(self):
830
r"""
831
Enumerate the elements of self.
832
833
EXAMPLES::
834
835
Example with order 1 (a special case)::
836
837
sage: E = EllipticCurve('389a'); F= E.heegner_point(-7,1).ring_class_field()
838
sage: G = F.galois_group(F.quadratic_field())
839
sage: G._list()
840
(Class field automorphism defined by x^2 + x*y + 2*y^2,)
841
842
Example over quadratic imaginary field::
843
844
sage: E = EllipticCurve('389a'); F= E.heegner_point(-7,5).ring_class_field()
845
sage: G = F.galois_group(F.quadratic_field())
846
sage: G._list()
847
(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)
848
849
Example over `\QQ` (it is not implemented yet)::
850
851
sage: K3 = heegner_points(389,-52,3).ring_class_field()
852
sage: K3.galois_group()._list()
853
Traceback (most recent call last):
854
...
855
NotImplementedError: Galois group over QQ not yet implemented
856
857
Example over Hilbert class field::
858
859
sage: K3 = heegner_points(389,-52,3).ring_class_field(); K1 = heegner_points(389,-52,1).ring_class_field()
860
sage: G = K3.galois_group(K1)
861
sage: G._list()
862
(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)
863
"""
864
if self._base_is_QQ():
865
raise NotImplementedError, "Galois group over QQ not yet implemented"
866
elif self._base_is_quad_imag_field():
867
# Over the quadratic imaginary field, so straightforward
868
# enumeration of all reduced primitive binary quadratic
869
# forms of discriminant D*c^2.
870
D = self.base_field().discriminant()
871
c = self.field().conductor()
872
Q = [f for f in BinaryQF_reduced_representatives(D*c*c) if f.is_primitive()]
873
v = [GaloisAutomorphismQuadraticForm(self, f) for f in Q]
874
875
elif self._base_is_hilbert_class_field() and self.is_kolyvagin():
876
# Take only the automorphisms in the quad imag case that map to
877
# a principal ideal.
878
M = self.field()
879
K = M.quadratic_field()
880
A = K.maximal_order().free_module()
881
v = []
882
self.__p1_to_automorphism = {}
883
for sigma in M.galois_group(K)._list():
884
I = sigma.ideal()
885
if I.is_principal():
886
# sigma does define an element of our Galois subgroup.
887
alpha = sigma.ideal().gens_reduced()[0]
888
t = GaloisAutomorphismQuadraticForm(self, sigma.quadratic_form(), alpha=alpha)
889
self.__p1_to_automorphism[t.p1_element()] = t
890
v.append(t)
891
else:
892
raise NotImplementedError, "general Galois group not yet implemented"
893
894
v.sort()
895
assert len(v) == self.cardinality(), "bug enumerating Galois group elements"
896
return tuple(v)
897
898
def _quadratic_form_to_alpha(self, f):
899
"""
900
INPUT:
901
902
- `f` -- a binary quadratic form with discriminant `c^2 D`
903
904
OUTPUT:
905
906
- an element of the ring of integers of the quadratic
907
imaginary field
908
909
EXAMPLES::
910
911
sage: K3 = heegner_points(389,-52,3).ring_class_field(); K1 = heegner_points(389,-52,1).ring_class_field()
912
sage: G = K3.galois_group(K1)
913
sage: [G._quadratic_form_to_alpha(s.quadratic_form()) for s in G]
914
[3/2*sqrt_minus_52, 1/6*sqrt_minus_52 + 1/3, 1/6*sqrt_minus_52, 1/6*sqrt_minus_52 - 1/3]
915
916
What happens when we input a quadratic form that has nothing
917
to do with `G`::
918
919
sage: G._quadratic_form_to_alpha(BinaryQF([1,2,3]))
920
Traceback (most recent call last):
921
...
922
ValueError: quadratic form has the wrong discriminant
923
"""
924
A,B,C = f
925
K = self.field().quadratic_field()
926
if f.discriminant() != self.field().conductor()**2 * K.discriminant():
927
raise ValueError, "quadratic form has the wrong discriminant"
928
929
R = K['X']
930
S = K.maximal_order()
931
M = S.free_module()
932
v = R([C,B,A]).roots()[0][0]
933
return v
934
935
def _alpha_to_automorphism(self, alpha):
936
r"""
937
Assuming self has base field the Hilbert class field, make an
938
automorphism from the element `\alpha` of the ring of integers
939
into self.
940
941
INPUT:
942
943
- `\alpha` -- element of quadratic imaginary field coprime to conductor
944
945
EXAMPLES::
946
947
sage: K3 = heegner_points(389,-52,3).ring_class_field()
948
sage: K1 = heegner_points(389,-52,1).ring_class_field()
949
sage: G = K3.galois_group(K1)
950
sage: G._alpha_to_automorphism(1)
951
Class field automorphism defined by x^2 + 117*y^2
952
sage: [G._alpha_to_automorphism(s.alpha()) for s in G] == list(G)
953
True
954
"""
955
if not self._base_is_hilbert_class_field() and self.is_kolyvagin():
956
raise TypeError, "base must be Hilbert class field with Kolyvagin condition on conductor"
957
R = self.field().quadratic_field().maximal_order()
958
uv = self._alpha_to_p1_element(R(alpha))
959
try:
960
d = self.__p1_to_automorphism
961
except AttributeError:
962
self._list() # computes attribute as side-effect
963
d = self.__p1_to_automorphism
964
return d[uv]
965
966
967
def _alpha_to_p1_element(self, alpha):
968
r"""
969
Given an element of the ring of integers that is nonzero
970
modulo c, return canonical (after our fixed choice of basis)
971
element of the project line corresponding to it.
972
973
INPUT:
974
975
- `\alpha` -- element of the ring of integers of the
976
quadratic imaginary field
977
978
OUTPUT:
979
980
- 2-tuple of integers
981
982
EXAMPLES::
983
984
sage: K3 = heegner_points(389,-52,3).ring_class_field()
985
sage: K1 = heegner_points(389,-52,1).ring_class_field()
986
sage: G = K3.galois_group(K1)
987
sage: G._alpha_to_p1_element(1)
988
(1, 0)
989
sage: sorted([G._alpha_to_p1_element(s.alpha()) for s in G])
990
[(0, 1), (1, 0), (1, 1), (1, 2)]
991
"""
992
try:
993
A, P1 = self.__alpha_to_p1_element
994
except AttributeError:
995
# todo (optimize) -- this whole function can be massively optimized:
996
M = self.field()
997
A = M.quadratic_field().maximal_order().free_module()
998
P1 = P1List(M.conductor())
999
self.__alpha_to_p1_element = A, P1
1000
alpha = self.field().quadratic_field()(alpha)
1001
w = A.coordinate_vector(alpha.vector())
1002
w *= w.denominator()
1003
w = w.change_ring(ZZ)
1004
n = arith.gcd(w)
1005
w /= n
1006
c = P1.N()
1007
w = P1.normalize(ZZ(w[0])%c, ZZ(w[1])%c)
1008
if w == (0,0):
1009
w = (1,0)
1010
return w
1011
1012
def _p1_element_to_alpha(self, uv):
1013
"""
1014
Convert a normalized pair ``uv=(u,v)`` of integers to the
1015
corresponding element of the ring of integers got by taking `u
1016
b_0 + v b_1` where `b_0, b_1` are the basis for the ring of
1017
integers.
1018
1019
INPUT:
1020
1021
- ``uv`` -- pair of integers
1022
1023
OUTPUT:
1024
1025
- element of maximal order of quadratic field
1026
1027
EXAMPLES::
1028
1029
sage: K5 = heegner_points(389,-52,5).ring_class_field()
1030
sage: K1 = heegner_points(389,-52,1).ring_class_field()
1031
sage: G = K5.galois_group(K1)
1032
sage: v = [G._alpha_to_p1_element(s.alpha()) for s in G]
1033
sage: [G._p1_element_to_alpha(z) for z in v]
1034
[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]
1035
sage: [G(G._p1_element_to_alpha(z)) for z in v] == list(G)
1036
True
1037
"""
1038
B = self.field().quadratic_field().maximal_order().basis()
1039
return uv[0]*B[0] + uv[1]*B[1]
1040
1041
1042
def _base_is_QQ(self):
1043
r"""
1044
Return True if the base field of this ring class field is `\QQ`.
1045
1046
EXAMPLES::
1047
1048
sage: H = heegner_points(389,-20,3); M = H.ring_class_field()
1049
sage: M.galois_group(H.quadratic_field())._base_is_QQ()
1050
False
1051
sage: M.galois_group(QQ)._base_is_QQ()
1052
True
1053
sage: M.galois_group(heegner_points(389,-20,1).ring_class_field())._base_is_QQ()
1054
False
1055
"""
1056
return self.__base == QQ
1057
1058
def _base_is_quad_imag_field(self):
1059
"""
1060
Return True if the base field of this ring class field is the
1061
quadratic imaginary field `K`.
1062
1063
EXAMPLES::
1064
1065
sage: H = heegner_points(389,-20,3); M = H.ring_class_field()
1066
sage: M.galois_group(H.quadratic_field())._base_is_quad_imag_field()
1067
True
1068
sage: M.galois_group(QQ)._base_is_quad_imag_field()
1069
False
1070
sage: M.galois_group(heegner_points(389,-20,1).ring_class_field())._base_is_quad_imag_field()
1071
False
1072
"""
1073
return number_field.is_QuadraticField(self.__base)
1074
1075
def is_kolyvagin(self):
1076
"""
1077
Return True if conductor `c` is prime to the discriminant of the
1078
quadratic field, `c` is squarefree and each prime dividing `c`
1079
is inert.
1080
1081
EXAMPLES::
1082
1083
sage: K5 = heegner_points(389,-52,5).ring_class_field()
1084
sage: K1 = heegner_points(389,-52,1).ring_class_field()
1085
sage: K5.galois_group(K1).is_kolyvagin()
1086
True
1087
sage: K7 = heegner_points(389,-52,7).ring_class_field()
1088
sage: K7.galois_group(K1).is_kolyvagin()
1089
False
1090
sage: K25 = heegner_points(389,-52,25).ring_class_field()
1091
sage: K25.galois_group(K1).is_kolyvagin()
1092
False
1093
"""
1094
M = self.field()
1095
c = M.conductor()
1096
D = M.quadratic_field().discriminant()
1097
if c.gcd(D) != 1: return False
1098
if not c.is_squarefree(): return False
1099
for p in c.prime_divisors():
1100
if not is_inert(D,p):
1101
return False
1102
return True
1103
1104
def _base_is_hilbert_class_field(self):
1105
"""
1106
Return True if the base field of this ring class field is the
1107
Hilbert class field of `K` viewed as a ring class field (so
1108
not of data type QuadraticField).
1109
1110
EXAMPLES::
1111
1112
sage: H = heegner_points(389,-20,3); M = H.ring_class_field()
1113
sage: M.galois_group(H.quadratic_field())._base_is_hilbert_class_field()
1114
False
1115
sage: M.galois_group(QQ)._base_is_hilbert_class_field()
1116
False
1117
sage: M.galois_group(heegner_points(389,-20,1).ring_class_field())._base_is_hilbert_class_field()
1118
True
1119
"""
1120
M = self.__base
1121
return isinstance(M, RingClassField) and M.conductor() == 1
1122
1123
1124
def __getitem__(self, i):
1125
"""
1126
EXAMPLES::
1127
1128
sage: E = EllipticCurve('389a'); F= E.heegner_point(-7,5).ring_class_field()
1129
sage: G = F.galois_group(F.quadratic_field())
1130
sage: G[0]
1131
Class field automorphism defined by x^2 + x*y + 44*y^2
1132
"""
1133
return self._list()[i]
1134
1135
1136
def __len__(self):
1137
"""
1138
EXAMPLES::
1139
1140
sage: K5 = heegner_points(389,-52,5).ring_class_field()
1141
sage: K1 = heegner_points(389,-52,1).ring_class_field()
1142
sage: G = K5.galois_group(K1)
1143
sage: G.cardinality()
1144
6
1145
sage: len(G)
1146
6
1147
"""
1148
return self.cardinality()
1149
1150
@cached_method
1151
def cardinality(self):
1152
"""
1153
Return the cardinality of this Galois group.
1154
1155
EXAMPLES::
1156
1157
sage: E = EllipticCurve('389a')
1158
sage: G = E.heegner_point(-7,5).ring_class_field().galois_group(); G
1159
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5
1160
sage: G.cardinality()
1161
12
1162
sage: G = E.heegner_point(-7).ring_class_field().galois_group()
1163
sage: G.cardinality()
1164
2
1165
sage: G = E.heegner_point(-7,55).ring_class_field().galois_group()
1166
sage: G.cardinality()
1167
120
1168
"""
1169
return self.__field.absolute_degree() // self.__base.absolute_degree()
1170
1171
@cached_method
1172
def complex_conjugation(self):
1173
"""
1174
Return the automorphism of self determined by complex
1175
conjugation. The base field must be the rational numbers.
1176
1177
EXAMPLES::
1178
1179
sage: E = EllipticCurve('389a')
1180
sage: G = E.heegner_point(-7,5).ring_class_field().galois_group()
1181
sage: G.complex_conjugation()
1182
Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 5
1183
"""
1184
if self.base_field() != QQ:
1185
raise ValueError, "the base field must be fixed by complex conjugation"
1186
return GaloisAutomorphismComplexConjugation(self)
1187
1188
1189
##################################################################################
1190
#
1191
# Elements of Galois groups
1192
#
1193
##################################################################################
1194
1195
1196
# TODO -- make GaloisAutomorphism derive from GroupElement so get powers for free, etc.
1197
1198
class GaloisAutomorphism(SageObject):
1199
"""
1200
An abstract automorphism of a ring class field.
1201
"""
1202
def __init__(self, parent):
1203
"""
1204
INPUT:
1205
1206
- ``parent`` -- a group of automorphisms of a ring class field
1207
1208
EXAMPLES::
1209
1210
sage: G = heegner_points(389,-7,5).ring_class_field().galois_group(); G
1211
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5
1212
sage: sage.schemes.elliptic_curves.heegner.GaloisAutomorphism(G)
1213
<class 'sage.schemes.elliptic_curves.heegner.GaloisAutomorphism'>
1214
"""
1215
self.__parent = parent
1216
1217
def parent(self):
1218
"""
1219
Return the parent of this automorphism, which is a Galois
1220
group of a ring class field.
1221
1222
EXAMPLES::
1223
1224
sage: E = EllipticCurve('389a')
1225
sage: s = E.heegner_point(-7,5).ring_class_field().galois_group().complex_conjugation()
1226
sage: s.parent()
1227
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5
1228
"""
1229
return self.__parent
1230
1231
def domain(self):
1232
"""
1233
Return the domain of this automorphism.
1234
1235
EXAMPLES::
1236
1237
sage: E = EllipticCurve('389a')
1238
sage: s = E.heegner_point(-7,5).ring_class_field().galois_group().complex_conjugation()
1239
sage: s.domain()
1240
Ring class field extension of QQ[sqrt(-7)] of conductor 5
1241
"""
1242
return self.parent().field()
1243
1244
class GaloisAutomorphismComplexConjugation(GaloisAutomorphism):
1245
"""
1246
The complex conjugation automorphism of a ring class field.
1247
1248
EXAMPLES::
1249
1250
sage: conj = heegner_point(37,-7,5).ring_class_field().galois_group().complex_conjugation()
1251
sage: conj
1252
Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 5
1253
sage: conj.domain()
1254
Ring class field extension of QQ[sqrt(-7)] of conductor 5
1255
1256
TESTS::
1257
1258
sage: type(conj)
1259
<class 'sage.schemes.elliptic_curves.heegner.GaloisAutomorphismComplexConjugation'>
1260
sage: loads(dumps(conj)) == conj
1261
True
1262
"""
1263
def __init__(self, parent):
1264
"""
1265
INPUT:
1266
1267
- ``parent`` -- a group of automorphisms of a ring class field
1268
1269
EXAMPLES::
1270
1271
sage: G = heegner_point(37,-7,5).ring_class_field().galois_group()
1272
sage: sage.schemes.elliptic_curves.heegner.GaloisAutomorphismComplexConjugation(G)
1273
Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 5
1274
"""
1275
GaloisAutomorphism.__init__(self, parent)
1276
1277
def __hash__(self):
1278
"""
1279
EXAMPLES::
1280
1281
sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()
1282
sage: conj = G.complex_conjugation(); hash(conj)
1283
1347197483068745902 # 64-bit
1284
480045230 # 32-bit
1285
"""
1286
return hash((self.parent(), 1))
1287
1288
def __eq__(self, right):
1289
"""
1290
EXAMPLES::
1291
1292
sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()
1293
sage: conj = G.complex_conjugation()
1294
sage: conj2 = sage.schemes.elliptic_curves.heegner.GaloisAutomorphismComplexConjugation(G)
1295
sage: conj is conj2
1296
False
1297
sage: conj == conj2
1298
True
1299
"""
1300
return isinstance(right, GaloisAutomorphismComplexConjugation) and \
1301
self.parent() == right.parent()
1302
1303
def _repr_(self):
1304
"""
1305
Return print representation of the complex conjugation automorphism.
1306
1307
EXAMPLES::
1308
1309
sage: conj = heegner_point(37,-7,5).ring_class_field().galois_group().complex_conjugation()
1310
sage: conj._repr_()
1311
'Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 5'
1312
"""
1313
return "Complex conjugation automorphism of %s"%self.domain()
1314
1315
## def __mul__(self, right):
1316
## """
1317
## Return the composition of two automorphisms.
1318
1319
## EXAMPLES::
1320
1321
## sage: ?
1322
## """
1323
## if self.parent() != right.__parent():
1324
## raise TypeError, "automorphisms must be of the same class field"
1325
## raise NotImplementedError
1326
1327
def __invert__(self):
1328
"""
1329
Return the inverse of self, which is just self again.
1330
1331
EXAMPLES::
1332
1333
sage: conj = heegner_point(37,-7,5).ring_class_field().galois_group().complex_conjugation()
1334
sage: ~conj
1335
Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 5
1336
"""
1337
return self
1338
1339
def order(self):
1340
"""
1341
EXAMPLES::
1342
1343
sage: conj = heegner_point(37,-7,5).ring_class_field().galois_group().complex_conjugation()
1344
sage: conj.order()
1345
2
1346
"""
1347
return ZZ(2)
1348
1349
class GaloisAutomorphismQuadraticForm(GaloisAutomorphism):
1350
"""
1351
An automorphism of a ring class field defined by a quadratic form.
1352
1353
EXAMPLES::
1354
1355
sage: H = heegner_points(389,-20,3)
1356
sage: sigma = H.ring_class_field().galois_group(H.quadratic_field())[0]; sigma
1357
Class field automorphism defined by x^2 + 45*y^2
1358
sage: type(sigma)
1359
<class 'sage.schemes.elliptic_curves.heegner.GaloisAutomorphismQuadraticForm'>
1360
sage: loads(dumps(sigma)) == sigma
1361
True
1362
"""
1363
def __init__(self, parent, quadratic_form, alpha=None):
1364
r"""
1365
INPUT:
1366
1367
- ``parent`` -- a group of automorphisms of a ring class field
1368
1369
- ``quadratic_form`` -- a binary quadratic form that
1370
defines an element of the Galois group of `K_c` over `K`.
1371
1372
- ``\alpha`` -- (default: None) optional data that specified
1373
element corresponding element of `(\mathcal{O}_K /
1374
c\mathcal{O}_K)^* / (\ZZ/c\ZZ)^*`, via class field
1375
theory.
1376
1377
EXAMPLES::
1378
1379
sage: H = heegner_points(389,-20,3); G = H.ring_class_field().galois_group(H.quadratic_field())
1380
sage: f = BinaryQF_reduced_representatives(-20*9)[0]
1381
sage: sage.schemes.elliptic_curves.heegner.GaloisAutomorphismQuadraticForm(G, f)
1382
Class field automorphism defined by x^2 + 45*y^2
1383
"""
1384
self.__quadratic_form = quadratic_form.reduced_form()
1385
self.__alpha = alpha
1386
GaloisAutomorphism.__init__(self, parent)
1387
1388
@cached_method
1389
def order(self):
1390
"""
1391
Return the multiplicative order of this Galois group automorphism.
1392
1393
EXAMPLES::
1394
1395
sage: K3 = heegner_points(389,-52,3).ring_class_field()
1396
sage: K1 = heegner_points(389,-52,1).ring_class_field()
1397
sage: G = K3.galois_group(K1)
1398
sage: sorted([g.order() for g in G])
1399
[1, 2, 4, 4]
1400
sage: K5 = heegner_points(389,-52,5).ring_class_field()
1401
sage: K1 = heegner_points(389,-52,1).ring_class_field()
1402
sage: G = K5.galois_group(K1)
1403
sage: sorted([g.order() for g in G])
1404
[1, 2, 3, 3, 6, 6]
1405
"""
1406
alpha = self.__alpha
1407
if alpha is None:
1408
raise NotImplementedError, "order only currently implemented when alpha given in construction"
1409
G = self.parent()
1410
one = G(1).p1_element()
1411
ans = ZZ(1)
1412
z = alpha
1413
for i in range(G.cardinality()):
1414
if G._alpha_to_p1_element(z) == one:
1415
return ans
1416
ans += 1
1417
z *= alpha
1418
assert False, "bug in order"
1419
1420
def alpha(self):
1421
r"""
1422
Optional data that specified element corresponding element of
1423
`(\mathcal{O}_K / c\mathcal{O}_K)^* / (\ZZ/c\ZZ)^*`, via class
1424
field theory.
1425
1426
This is a generator of the ideal corresponding to this
1427
automorphism.
1428
1429
EXAMPLES::
1430
1431
sage: K3 = heegner_points(389,-52,3).ring_class_field()
1432
sage: K1 = heegner_points(389,-52,1).ring_class_field()
1433
sage: G = K3.galois_group(K1)
1434
sage: orb = sorted([g.alpha() for g in G]); orb # random (the sign depends on the database being installed or not)
1435
[1, 1/2*sqrt_minus_52 + 1, -1/2*sqrt_minus_52, 1/2*sqrt_minus_52 - 1]
1436
sage: [x^2 for x in orb] # this is just for testing
1437
[1, sqrt_minus_52 - 12, -13, -sqrt_minus_52 - 12]
1438
1439
sage: K5 = heegner_points(389,-52,5).ring_class_field()
1440
sage: K1 = heegner_points(389,-52,1).ring_class_field()
1441
sage: G = K5.galois_group(K1)
1442
sage: orb = sorted([g.alpha() for g in G]); orb # random (the sign depends on the database being installed or not)
1443
[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]
1444
sage: [x^2 for x in orb] # just for testing
1445
[1, -13, sqrt_minus_52 - 12, -sqrt_minus_52 - 12, -2*sqrt_minus_52 - 9, 2*sqrt_minus_52 - 9]
1446
1447
"""
1448
if self.__alpha is None:
1449
raise ValueError, "alpha data not defined"
1450
return self.__alpha
1451
1452
@cached_method
1453
def p1_element(self):
1454
r"""
1455
Return element of the projective line corresponding to this
1456
automorphism.
1457
1458
This only makes sense if this automorphism is in the Galois
1459
group `\textrm{Gal}(K_c/K_1)`.
1460
1461
EXAMPLES::
1462
1463
sage: K3 = heegner_points(389,-52,3).ring_class_field()
1464
sage: K1 = heegner_points(389,-52,1).ring_class_field()
1465
sage: G = K3.galois_group(K1)
1466
sage: sorted([g.p1_element() for g in G])
1467
[(0, 1), (1, 0), (1, 1), (1, 2)]
1468
1469
sage: K5 = heegner_points(389,-52,5).ring_class_field()
1470
sage: K1 = heegner_points(389,-52,1).ring_class_field()
1471
sage: G = K5.galois_group(K1)
1472
sage: sorted([g.p1_element() for g in G])
1473
[(0, 1), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4)]
1474
"""
1475
return self.parent()._alpha_to_p1_element(self.__alpha)
1476
1477
def __hash__(self):
1478
"""
1479
EXAMPLES::
1480
1481
sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]
1482
sage: hash(s)
1483
4262582128197601113 # 64-bit
1484
-1994029223 # 32-bit
1485
"""
1486
return hash((self.parent(), tuple(self.__quadratic_form)))
1487
1488
def __eq__(self, right):
1489
"""
1490
EXAMPLES::
1491
1492
sage: H = heegner_points(389,-7,5); s = H.ring_class_field().galois_group(H.quadratic_field())[1]
1493
sage: s == s
1494
True
1495
sage: s == s*s
1496
False
1497
sage: s == s*s*s*s*s
1498
False
1499
sage: s == s*s*s*s*s*s*s
1500
True
1501
"""
1502
return isinstance(right, GaloisAutomorphismQuadraticForm) and \
1503
self.parent() == right.parent() and \
1504
self.quadratic_form().is_equivalent(right.quadratic_form())
1505
1506
def __cmp__(self, right):
1507
"""
1508
Compare self and right. Used mainly so that lists of
1509
automorphisms are sorted consistently between runs.
1510
1511
EXAMPLES::
1512
1513
sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]
1514
sage: s.__cmp__(s)
1515
0
1516
sage: s.__cmp__(0) != 0
1517
True
1518
"""
1519
if not isinstance(right, GaloisAutomorphismQuadraticForm):
1520
return cmp(type(self), type(right))
1521
c = cmp(self.parent(), right.parent())
1522
if c: return c
1523
if self.quadratic_form().is_equivalent(right.quadratic_form()):
1524
return 0
1525
return cmp(self.quadratic_form(), right.quadratic_form())
1526
1527
def _repr_(self):
1528
"""
1529
Return string representation of this automorphism.
1530
1531
EXAMPLES::
1532
1533
sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]
1534
sage: s._repr_()
1535
'Class field automorphism defined by x^2 + 45*y^2'
1536
1537
"""
1538
return "Class field automorphism defined by %s"%self.__quadratic_form
1539
1540
def __mul__(self, right):
1541
"""
1542
Return the composition of two automorphisms.
1543
1544
EXAMPLES::
1545
1546
sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]
1547
sage: s * s
1548
Class field automorphism defined by x^2 + 45*y^2
1549
sage: G = s.parent(); list(G)
1550
[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]
1551
sage: G[0]*G[0]
1552
Class field automorphism defined by x^2 + 45*y^2
1553
sage: G[1]*G[2] == G[3]
1554
True
1555
"""
1556
if self.parent() != right.parent():
1557
raise TypeError, "automorphisms must be of the same class field"
1558
if not isinstance(right, GaloisAutomorphismQuadraticForm):
1559
# TODO: special case when right is complex conjugation
1560
raise NotImplementedError
1561
Q = (self.__quadratic_form * right.__quadratic_form).reduced_form()
1562
if self.__alpha and right.__alpha:
1563
alpha = self.__alpha * right.__alpha
1564
else:
1565
alpha = None
1566
return GaloisAutomorphismQuadraticForm(self.parent(), Q, alpha=alpha)
1567
1568
def quadratic_form(self):
1569
"""
1570
Return reduced quadratic form corresponding to this Galois
1571
automorphism.
1572
1573
1574
EXAMPLES::
1575
1576
sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]
1577
sage: s.quadratic_form()
1578
x^2 + 45*y^2
1579
"""
1580
return self.__quadratic_form
1581
1582
@cached_method
1583
def ideal(self):
1584
r"""
1585
Return ideal of ring of integers of quadratic imaginary field
1586
corresponding to this quadratic form. This is the ideal
1587
1588
`I = \left(A, \frac{-B+ c\sqrt{D}}{2}\right) \mathcal{O}_K`.
1589
1590
EXAMPLES::
1591
1592
sage: E = EllipticCurve('389a'); F= E.heegner_point(-20,3).ring_class_field()
1593
sage: G = F.galois_group(F.quadratic_field())
1594
sage: G[1].ideal()
1595
Fractional ideal (2, 1/2*sqrt_minus_20 + 1)
1596
sage: [s.ideal().gens() for s in G]
1597
[(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)]
1598
"""
1599
M = self.parent().field()
1600
K = M.quadratic_field()
1601
f = self.quadratic_form()
1602
c = M.conductor()
1603
sqrtD = K.gen()
1604
(A,B,C) = f
1605
if A%c == 0:
1606
A, C = C, A
1607
return K.maximal_order().ideal([A, (-B+c*sqrtD)/2])
1608
1609
## def __call__(self, z):
1610
## """
1611
## Return image of the Heegner point `z` under this automorphism.
1612
##
1613
## INPUT:
1614
##
1615
## - `z` -- a Heegner point on `X_0(N)` or an elliptic curve
1616
##
1617
## OUTPUT:
1618
##
1619
## - a Heegner point
1620
##
1621
## EXAMPLES::
1622
##
1623
## sage: x = heegner_point(389,-20,3); F = x.ring_class_field()
1624
## sage: sigma = F.galois_group(F.quadratic_field())[1]; sigma
1625
## Class field automorphism defined by 2*x^2 + 2*x*y + 23*y^2
1626
## sage: sigma(x)
1627
## Heegner point 3/1556*sqrt(-20) - 495/778 of discriminant -20 and conductor 3 on X_0(389)
1628
## """
1629
## if isinstance(z, HeegnerPointOnX0N):
1630
## if z.ring_class_field() != self.domain():
1631
## raise NotImplementedError, "class fields must be the same"
1632
## # TODO -- check more compatibilities?
1633
## # TODO -- this is surely backwards -- something must be inverted?
1634
## f = z.quadratic_form() * self.quadratic_form()
1635
## # TODO -- put f into the correct form with A divisible by N, etc.?
1636
## # That could be done by looking up reduced form of f in a canonical
1637
## # list of best reps.
1638
## N,D,c = z.level(),z.discriminant(),z.conductor()
1639
## return HeegnerPointOnX0N(N,D,c, f = f)
1640
## else:
1641
## raise NotImplementedError
1642
1643
##################################################################################
1644
#
1645
# Specific Heegner points
1646
#
1647
##################################################################################
1648
1649
1650
class HeegnerPoint(SageObject):
1651
r"""
1652
A Heegner point of level `N`, discriminant `D` and conductor `c`
1653
is any point on a modular curve or elliptic curve that is
1654
concocted in some way from a quadratic imaginary `\tau` in the upper
1655
half plane with `\Delta(\tau) = D c = \Delta(N \tau)`.
1656
1657
EXAMPLES::
1658
1659
sage: x = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,13); x
1660
Heegner point of level 389, discriminant -7, and conductor 13
1661
sage: type(x)
1662
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>
1663
sage: loads(dumps(x)) == x
1664
True
1665
"""
1666
def __init__(self, N, D, c):
1667
"""
1668
INPUT:
1669
1670
- `N` -- (positive integer) the level
1671
1672
- `D` -- (negative integer) fundamental discriminant
1673
1674
- `c` -- (positive integer) conductor
1675
1676
Since this is an abstract base class, no type or compatibility
1677
checks are done, as those are all assumed to be done in the
1678
derived class.
1679
1680
EXAMPLES::
1681
1682
sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5)
1683
sage: type(H)
1684
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>
1685
"""
1686
self.__N = N
1687
self.__D = D
1688
self.__c = c
1689
1690
def __cmp__(self, x):
1691
"""
1692
Compare two Heegner points.
1693
1694
EXAMPLES::
1695
1696
sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5)
1697
sage: H.__cmp__(H)
1698
0
1699
"""
1700
if not isinstance(x, HeegnerPoint):
1701
raise NotImplementedError
1702
return cmp((self.__N, self.__D, self.__c), (x.__N, x.__D, x.__c))
1703
1704
def _repr_(self):
1705
"""
1706
EXAMPLES::
1707
1708
sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5)
1709
sage: type(H)
1710
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>
1711
sage: H._repr_()
1712
'Heegner point of level 389, discriminant -7, and conductor 5'
1713
"""
1714
return "Heegner point of level %s, discriminant %s, and conductor %s"%(
1715
self.__N, self.__D, self.__c)
1716
1717
def __hash__(self):
1718
"""
1719
EXAMPLES::
1720
1721
sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5); type(H)
1722
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>
1723
sage: hash(H)
1724
6187687223143458874 # 64-bit
1725
-458201030 # 32-bit
1726
"""
1727
return hash((self.__N, self.__D, self.__c))
1728
1729
def __eq__(self, right):
1730
"""
1731
EXAMPLES::
1732
1733
sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5); type(H)
1734
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>
1735
sage: J = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,11)
1736
sage: H == H
1737
True
1738
sage: H == J
1739
False
1740
sage: J == H
1741
False
1742
sage: H == 0
1743
False
1744
"""
1745
if not isinstance(right, HeegnerPoint): return False
1746
return (self.__N, self.__D, self.__c) == (right.__N, right.__D, right.__c)
1747
1748
def level(self):
1749
"""
1750
Return the level of this Heegner point, which is the level of the
1751
modular curve `X_0(N)` on which this is a Heegner point.
1752
1753
EXAMPLES::
1754
1755
sage: heegner_point(389,-7,5).level()
1756
389
1757
"""
1758
return self.__N
1759
1760
def conductor(self):
1761
"""
1762
Return the conductor of this Heegner point.
1763
1764
EXAMPLES::
1765
1766
sage: heegner_point(389,-7,5).conductor()
1767
5
1768
sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67,7); P
1769
Kolyvagin point of discriminant -67 and conductor 7 on elliptic curve of conductor 37
1770
sage: P.conductor()
1771
7
1772
sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5); P.conductor()
1773
5
1774
"""
1775
return self.__c
1776
1777
def discriminant(self):
1778
"""
1779
Return the discriminant of the quadratic imaginary field
1780
associated to this Heegner point.
1781
1782
EXAMPLES::
1783
1784
sage: heegner_point(389,-7,5).discriminant()
1785
-7
1786
sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67,7); P
1787
Kolyvagin point of discriminant -67 and conductor 7 on elliptic curve of conductor 37
1788
sage: P.discriminant()
1789
-67
1790
sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5); P.discriminant()
1791
-7
1792
"""
1793
return self.__D
1794
1795
@cached_method
1796
def quadratic_field(self):
1797
"""
1798
Return the quadratic number field of discriminant `D`.
1799
1800
EXAMPLES::
1801
1802
sage: x = heegner_point(37,-7,5)
1803
sage: x.quadratic_field()
1804
Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
1805
1806
1807
sage: E = EllipticCurve('37a'); P = E.heegner_point(-40)
1808
sage: P.quadratic_field()
1809
Number Field in sqrt_minus_40 with defining polynomial x^2 + 40
1810
sage: P.quadratic_field() is P.quadratic_field()
1811
True
1812
sage: type(P.quadratic_field())
1813
<class 'sage.rings.number_field.number_field.NumberField_quadratic_with_category'>
1814
"""
1815
return self.ring_class_field().quadratic_field()
1816
1817
@cached_method
1818
def quadratic_order(self):
1819
"""
1820
Return the order in the quadratic imaginary field of conductor
1821
`c`, where `c` is the conductor of this Heegner point.
1822
1823
EXAMPLES::
1824
1825
sage: heegner_point(389,-7,5).quadratic_order()
1826
Order in Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
1827
sage: heegner_point(389,-7,5).quadratic_order().basis()
1828
[1, 5*sqrt_minus_7]
1829
1830
sage: E = EllipticCurve('37a'); P = E.heegner_point(-40,11)
1831
sage: P.quadratic_order()
1832
Order in Number Field in sqrt_minus_40 with defining polynomial x^2 + 40
1833
sage: P.quadratic_order().basis()
1834
[1, 11*sqrt_minus_40]
1835
1836
"""
1837
K = self.quadratic_field()
1838
return K.order([1,self.conductor()*K.gen()])
1839
1840
@cached_method
1841
def ring_class_field(self):
1842
"""
1843
Return the ring class field associated to this Heegner point.
1844
This is an extension `K_c` over `K`, where `K` is the
1845
quadratic imaginary field and `c` is the conductor associated
1846
to this Heegner point. This Heegner point is defined over
1847
`K_c` and the Galois group `Gal(K_c/K)` acts transitively on
1848
the Galois conjugates of this Heegner point.
1849
1850
EXAMPLES::
1851
1852
sage: E = EllipticCurve('389a'); K.<a> = QuadraticField(-5)
1853
sage: len(K.factor(5))
1854
1
1855
sage: len(K.factor(23))
1856
2
1857
sage: E.heegner_point(-7, 5).ring_class_field().degree_over_K()
1858
6
1859
sage: E.heegner_point(-7, 23).ring_class_field().degree_over_K()
1860
22
1861
sage: E.heegner_point(-7, 5*23).ring_class_field().degree_over_K()
1862
132
1863
sage: E.heegner_point(-7, 5^2).ring_class_field().degree_over_K()
1864
30
1865
sage: E.heegner_point(-7, 7).ring_class_field().degree_over_K()
1866
7
1867
"""
1868
return RingClassField(self.discriminant(), self.conductor())
1869
1870
1871
##################################################################################
1872
#
1873
# Sets of Heegner points
1874
#
1875
##################################################################################
1876
1877
class HeegnerPoints(SageObject):
1878
"""
1879
The set of Heegner points with given parameters.
1880
1881
EXAMPLES::
1882
1883
sage: H = heegner_points(389); H
1884
Set of all Heegner points on X_0(389)
1885
sage: type(H)
1886
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoints_level'>
1887
sage: isinstance(H, sage.schemes.elliptic_curves.heegner.HeegnerPoints)
1888
True
1889
"""
1890
def __init__(self, N):
1891
"""
1892
INPUT:
1893
1894
- `N` -- level, a positive integer
1895
1896
EXAMPLES::
1897
1898
sage: heegner_points(37)
1899
Set of all Heegner points on X_0(37)
1900
sage: heegner_points(0)
1901
Traceback (most recent call last):
1902
...
1903
ValueError: N must a positive integer
1904
"""
1905
self.__N = ZZ(N)
1906
if self.__N <= 0:
1907
raise ValueError, "N must a positive integer"
1908
1909
def level(self):
1910
"""
1911
Return the level `N` of the modular curve `X_0(N)`.
1912
1913
EXAMPLES::
1914
1915
sage: heegner_points(389).level()
1916
389
1917
"""
1918
return self.__N
1919
1920
1921
class HeegnerPoints_level(HeegnerPoints):
1922
"""
1923
Return the infinite set of all Heegner points on `X_0(N)` for all
1924
quadratic imaginary fields.
1925
1926
EXAMPLES::
1927
1928
sage: H = heegner_points(11); H
1929
Set of all Heegner points on X_0(11)
1930
sage: type(H)
1931
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoints_level'>
1932
sage: loads(dumps(H)) == H
1933
True
1934
"""
1935
def __eq__(self, other):
1936
"""
1937
EXAMPLES::
1938
1939
sage: H = heegner_points(11)
1940
sage: H == heegner_points(13)
1941
False
1942
sage: H == heegner_points(11)
1943
True
1944
sage: H == 0
1945
False
1946
"""
1947
return isinstance(other, HeegnerPoints_level) and self.level() == other.level()
1948
1949
def _repr_(self):
1950
"""
1951
Return string representation of the set of Heegner points.
1952
1953
EXAMPLES::
1954
1955
sage: heegner_points(389)._repr_()
1956
'Set of all Heegner points on X_0(389)'
1957
"""
1958
return "Set of all Heegner points on X_0(%s)"%self.level()
1959
1960
def reduce_mod(self, ell):
1961
r"""
1962
eturn object that allows for computation with Heegner points
1963
of level `N` modulo the prime `\ell`, represented using
1964
quaternion algebras.
1965
1966
INPUT:
1967
1968
- `\ell` -- prime
1969
1970
EXAMPLES::
1971
1972
sage: heegner_points(389).reduce_mod(7).quaternion_algebra()
1973
Quaternion Algebra (-1, -7) with base ring Rational Field
1974
"""
1975
return HeegnerQuatAlg(self.level(), ell)
1976
1977
def discriminants(self, n=10, weak=False):
1978
r"""
1979
Return the first `n` quadratic imaginary discriminants that
1980
satisfy the Heegner hypothesis for `N`.
1981
1982
INPUTS:
1983
1984
- `n` -- nonnegative integer
1985
1986
- ``weak`` -- bool (default: False); if True only require
1987
weak Heegner hypothesis, which is the same as usual but
1988
without the condition that `\gcd(D,N)=1`.
1989
1990
EXAMPLES::
1991
1992
sage: X = heegner_points(37)
1993
sage: X.discriminants(5)
1994
[-7, -11, -40, -47, -67]
1995
1996
The default is 10::
1997
1998
sage: X.discriminants()
1999
[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104]
2000
sage: X.discriminants(15)
2001
[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104, -107, -115, -120, -123, -127]
2002
2003
The discriminant -111 satisfies only the weak Heegner hypothesis, since it
2004
is divisible by 37::
2005
2006
sage: X.discriminants(15,weak=True)
2007
[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104, -107, -111, -115, -120, -123]
2008
"""
2009
N = self.level()
2010
n = ZZ(n)
2011
v = []
2012
D = ZZ(-4)
2013
while len(v) < n:
2014
D -= 1
2015
if satisfies_weak_heegner_hypothesis(N,D):
2016
# if not weak, then also require gcd(D,N)=1
2017
if not weak and D.gcd(N) != 1:
2018
continue
2019
v.append(D)
2020
return v
2021
2022
class HeegnerPoints_level_disc(HeegnerPoints):
2023
"""
2024
Set of Heegner points of given level and all conductors associated
2025
to a quadratic imaginary field.
2026
2027
EXAMPLES::
2028
2029
sage: H = heegner_points(389,-7); H
2030
Set of all Heegner points on X_0(389) associated to QQ[sqrt(-7)]
2031
sage: type(H)
2032
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoints_level_disc'>
2033
sage: H._repr_()
2034
'Set of all Heegner points on X_0(389) associated to QQ[sqrt(-7)]'
2035
sage: H.discriminant()
2036
-7
2037
sage: H.quadratic_field()
2038
Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
2039
sage: H.kolyvagin_conductors()
2040
[1, 3, 5, 13, 15, 17, 19, 31, 39, 41]
2041
2042
sage: loads(dumps(H)) == H
2043
True
2044
"""
2045
def __init__(self, N, D):
2046
"""
2047
INPUT:
2048
2049
- `N` -- positive integer
2050
2051
- `D` -- negative fundamental discriminant
2052
2053
EXAMPLES::
2054
2055
sage: sage.schemes.elliptic_curves.heegner.HeegnerPoints_level_disc(37,-7)
2056
Set of all Heegner points on X_0(37) associated to QQ[sqrt(-7)]
2057
"""
2058
HeegnerPoints.__init__(self, N)
2059
D = ZZ(D)
2060
if not satisfies_weak_heegner_hypothesis(N,D):
2061
raise ValueError, "D (=%s) must satisfy the weak Heegner hypothesis for N (=%s)"%(D,N)
2062
self.__D = D
2063
2064
def __eq__(self, other):
2065
"""
2066
EXAMPLES::
2067
2068
sage: H = heegner_points(389,-7)
2069
sage: H == heegner_points(389,-7)
2070
True
2071
sage: H == 0
2072
False
2073
sage: H == heegner_points(389,-11)
2074
False
2075
"""
2076
return isinstance(other, HeegnerPoints_level_disc) and \
2077
self.level() == other.level() and self.__D == other.__D
2078
2079
def _repr_(self):
2080
"""
2081
Return string representation of the set of Heegner points for a given
2082
quadratic field.
2083
2084
EXAMPLES::
2085
2086
sage: heegner_points(389,-7)._repr_()
2087
'Set of all Heegner points on X_0(389) associated to QQ[sqrt(-7)]'
2088
"""
2089
return "Set of all Heegner points on X_0(%s) associated to QQ[sqrt(%s)]"%(
2090
self.level(), self.discriminant())
2091
2092
2093
def discriminant(self):
2094
r"""
2095
Return the discriminant of the quadratic imaginary extension `K`.
2096
2097
EXAMPLES::
2098
2099
sage: heegner_points(389,-7).discriminant()
2100
-7
2101
"""
2102
return self.__D
2103
2104
@cached_method
2105
def quadratic_field(self):
2106
r"""
2107
Return the quadratic imaginary field `K = \QQ(\sqrt{D})`.
2108
2109
EXAMPLES::
2110
2111
sage: E = EllipticCurve('389a'); K = E.heegner_point(-7,5).ring_class_field()
2112
sage: K.quadratic_field()
2113
Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
2114
"""
2115
D = self.__D
2116
var = 'sqrt_minus_%s'%(-D)
2117
return number_field.QuadraticField(D,var)
2118
2119
def kolyvagin_conductors(self, r=None, n=10, E=None, m=None):
2120
r"""
2121
Return the first `n` conductors that are squarefree products
2122
of distinct primes inert in the quadratic imaginary field
2123
`K = \QQ(\sqrt{D})`. If `r` is specified, return only
2124
conductors that are a product of `r` distinct primes all inert
2125
in `K`. If `r = 0`, always return the list ``[1]``,
2126
no matter what.
2127
2128
If the optional elliptic curve `E` and integer `m` are given,
2129
then only include conductors `c` such that for each prime
2130
divisor `p` of `c` we have `m \mid \gcd(a_p(E), p+1)`.
2131
2132
INPUT:
2133
2134
- `r` -- (default: None) nonnegative integer or None
2135
2136
- `n` -- positive integer
2137
2138
- `E` -- an elliptic curve
2139
2140
- `m` -- a positive integer
2141
2142
EXAMPLES::
2143
2144
sage: H = heegner_points(389,-7)
2145
sage: H.kolyvagin_conductors(0)
2146
[1]
2147
sage: H.kolyvagin_conductors(1)
2148
[3, 5, 13, 17, 19, 31, 41, 47, 59, 61]
2149
sage: H.kolyvagin_conductors(1,15)
2150
[3, 5, 13, 17, 19, 31, 41, 47, 59, 61, 73, 83, 89, 97, 101]
2151
sage: H.kolyvagin_conductors(1,5)
2152
[3, 5, 13, 17, 19]
2153
sage: H.kolyvagin_conductors(1,5,EllipticCurve('389a'),3)
2154
[5, 17, 41, 59, 83]
2155
sage: H.kolyvagin_conductors(2,5,EllipticCurve('389a'),3)
2156
[85, 205, 295, 415, 697]
2157
"""
2158
D = self.__D
2159
if not satisfies_weak_heegner_hypothesis(self.level(),D):
2160
raise ValueError, "D must satisfy the weak Heegner hypothesis"
2161
n = ZZ(n)
2162
if n <= 0:
2163
raise ValueError, "n must be a positive integer"
2164
if r is not None:
2165
r = ZZ(r)
2166
if r < 0:
2167
raise ValueError, "n must be a nonnegative integer"
2168
if r == 0:
2169
return [ZZ(1)]
2170
2171
c = ZZ(1)
2172
v = []
2173
N = self.level()
2174
ND = N * D
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 isn't::
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's good/important that this return a copy, since
2726
# BinaryQF's stupidly are mutable and can't 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` isn't 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's 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 shouldn't 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
E = self.curve()
3482
v = []
3483
for z in self.conjugates_over_K():
3484
m = z.numerical_approx(prec)
3485
v.append(m)
3486
v.append(m.curve().point([w.conjugate() for w in m], check=False))
3487
v.sort()
3488
return v
3489
3490
def _numerical_approx_xy_poly(self, prec=53):
3491
r"""
3492
Return polynomials with real floating point coefficients got
3493
by taking the real part of the product of `X - \alpha` over
3494
the numerical approximations `\alpha` to the conjugates of
3495
this Heegner point. The first polynomial runs through the
3496
`x`-coordinates and the second through the `y`-coordinates.
3497
3498
INPUT:
3499
3500
- ``prec`` -- positive integer (default: 53)
3501
3502
OUTPUT:
3503
3504
- 2-tuple of polynomials with floating point coefficients
3505
3506
EXAMPLES::
3507
sage: E = EllipticCurve('37a')
3508
sage: y = E.heegner_point(-7,3); y
3509
Heegner point of discriminant -7 and conductor 3 on elliptic curve of conductor 37
3510
sage: y._numerical_approx_xy_poly()
3511
(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)
3512
"""
3513
v = self._numerical_approx_conjugates_over_QQ(prec)
3514
R = ComplexField(prec)['X']
3515
S = RealField(prec)['X']
3516
X = R.gen()
3517
fx = prod(X-a[0] for a in v)
3518
fx = S([a.real() for a in fx])
3519
fy = prod(X-a[1] for a in v)
3520
fy = S([a.real() for a in fy])
3521
return fx, fy
3522
3523
def _xy_poly_nearby(self, prec=53, max_error=10**(-10)):
3524
"""
3525
Return polynomials with rational coefficients that for sufficiently
3526
tight bounds are the characteristic polynomial of the x and y
3527
coordinate of this Heegner point.
3528
3529
INPUT:
3530
3531
- ``prec`` -- positive integer (default: 53)
3532
3533
- ``max_error`` -- very small floating point number
3534
3535
OUTPUT:
3536
3537
- 2-tuple of polynomials with rational coefficients
3538
3539
EXAMPLES::
3540
3541
sage: E = EllipticCurve('37a')
3542
sage: y = E.heegner_point(-7,3); y
3543
Heegner point of discriminant -7 and conductor 3 on elliptic curve of conductor 37
3544
sage: y._xy_poly_nearby()
3545
[X^8 + 6*X^7 + 9*X^6 - 12*X^5 - 42*X^4 - 18*X^3 + 36*X^2 + 36*X + 9,
3546
X^8 + 12*X^7 + 72*X^6 + 270*X^5 + 678*X^4 + 1152*X^3 + 1269*X^2 + 810*X + 225]
3547
3548
3549
"""
3550
v = self._numerical_approx_xy_poly(prec)
3551
return [nearby_rational_poly(g, max_error=max_error) for g in v]
3552
3553
def _xy_poly_simplest(self, prec=53, prec2=None):
3554
"""
3555
Return polynomials with rational coefficients that for
3556
sufficiently tight bounds are the characteristic polynomial of
3557
the x and y coordinate of this Heegner point.
3558
3559
INPUT:
3560
3561
- ``prec`` -- positive integer (default: 53)
3562
3563
- ``prec2`` -- passed into simplest_rational_poly function
3564
3565
EXAMPLES::
3566
3567
sage: E = EllipticCurve('37a'); y = E.heegner_point(-7,3)
3568
sage: y._xy_poly_simplest()
3569
[X^8 + 6*X^7 + 9*X^6 - 12*X^5 - 42*X^4 - 18*X^3 + 36*X^2 + 36*X + 9,
3570
X^8 + 12*X^7 + 72*X^6 + 270*X^5 + 678*X^4 + 1152*X^3 + 1269*X^2 + 810*X + 225]
3571
"""
3572
v = self._numerical_approx_xy_poly(prec)
3573
if prec2 is None: prec2 = max(2, prec - 20)
3574
return [simplest_rational_poly(g,prec2) for g in v]
3575
3576
@cached_method
3577
def _square_roots_mod_2N_of_D_mod_4N(self):
3578
"""
3579
Return the square roots of `D` modulo `4N` all reduced mod `2N`,
3580
without multiplicity.
3581
3582
EXAMPLES::
3583
3584
sage: E = EllipticCurve('37a'); P = E.heegner_point(-40); P
3585
Heegner point of discriminant -40 on elliptic curve of conductor 37
3586
sage: P._square_roots_mod_2N_of_D_mod_4N()
3587
[16, 58]
3588
sage: parent(P._square_roots_mod_2N_of_D_mod_4N()[0])
3589
Ring of integers modulo 74
3590
"""
3591
D = self.discriminant()
3592
N = self.__E.conductor()
3593
R = Integers(4*N)
3594
m = 2*N
3595
return sorted( set([a%m for a in R(self.discriminant()).sqrt(all=True)]) )
3596
3597
def _trace_numerical_conductor_1(self, prec=53):
3598
"""
3599
Return numerical approximation using ``prec`` terms of working
3600
precision to the trace down to the quadratic imaginary field
3601
`K` of this Heegner point.
3602
3603
INPUT:
3604
3605
- `prec` -- bits precision (default: 53)
3606
3607
EXAMPLES::
3608
3609
sage: E = EllipticCurve('57a1')
3610
sage: P = E.heegner_point(-8); P
3611
Heegner point of discriminant -8 on elliptic curve of conductor 57
3612
sage: P._trace_numerical_conductor_1() # approx. (1 : 0 : 1)
3613
(1.00000000000000 + ...e-16*I : ...e-16 - ...e-16*I : 1.00000000000000)
3614
sage: P = E(2,1) # a generator
3615
sage: E([1,0]).height()
3616
0.150298370947295
3617
sage: P.height()
3618
0.0375745927368238
3619
sage: E.heegner_index(-8)
3620
2.0000?
3621
sage: E.torsion_order()
3622
1
3623
sage: 2*P
3624
(1 : 0 : 1)
3625
"""
3626
if self.conductor() != 1:
3627
raise ValueError, "conductor must be 1"
3628
R, U = self._good_tau_representatives()
3629
E = self.__E
3630
phi = E.modular_parametrization()
3631
C = rings.ComplexField(prec)
3632
F = E.change_ring(C)
3633
s = 0
3634
for u, weight in U:
3635
P = phi(C(self._qf_to_tau(u)))
3636
z = F.point(list(P),check=False)
3637
if abs(weight) == 2:
3638
t = F.point(z,check=False) + F.point(tuple([x.conjugate() for x in z]), check=False)
3639
if weight < 0:
3640
s -= t
3641
else:
3642
s += t
3643
else:
3644
if weight < 0:
3645
s -= z
3646
else:
3647
s += z
3648
return s
3649
3650
@cached_method
3651
def _good_tau_representatives(self):
3652
"""
3653
Return good upper half plane representatives for Heegner points.
3654
3655
ALGORITHM: This is Algorithm 3.5 in Watkins's paper.
3656
3657
EXAMPLES::
3658
3659
sage: P = EllipticCurve('389a1').heegner_point(-7)
3660
sage: P._good_tau_representatives()
3661
([(1, 1, 2)], [((389, 185, 22), 1)])
3662
"""
3663
if self.conductor() != 1: raise NotImplementedError
3664
E = self.__E
3665
SDN = self._square_roots_mod_2N_of_D_mod_4N()
3666
beta = SDN[0]
3667
U = []
3668
R = []
3669
N = self.__E.conductor()
3670
D = self.discriminant()
3671
h = self.ring_class_field().degree_over_K()
3672
divs = D.gcd(N).divisors()
3673
a = 1
3674
while True:
3675
for b in SDN:
3676
b = b.lift()
3677
# todo (optimize) -- replace for over all s with for over solution
3678
# set that can be found quickly.
3679
y = ZZ((b*b - D)/(4*N))
3680
for s in Integers(a):
3681
if N*s*s + b*s + y == 0:
3682
s = s.lift()
3683
f = (a*N, b+2*N*s, ZZ( ((b + 2*N*s)**2 - D)/(4*a*N)) )
3684
for d in divs:
3685
Q = d * prod(p**k for p,k in N.factor() if (b-beta)%(p**k)!=0)
3686
g = self._qf_atkin_lehner_act(Q, f)
3687
gbar = (ZZ(g[0]/N), -g[1], g[2]*N)
3688
g = self._qf_reduce(g)
3689
gbar = self._qf_reduce(gbar)
3690
if g in R or gbar in R:
3691
continue
3692
R.append(g)
3693
if g != gbar:
3694
R.append(gbar)
3695
epsilon_Q = prod([E.root_number(q) for q in Q.prime_divisors()])
3696
if g == gbar:
3697
# weight is epsilon_Q
3698
weight = epsilon_Q
3699
else:
3700
# weight is 2*epsilon_Q
3701
weight = 2*epsilon_Q
3702
U.append((f,weight))
3703
if len(R) == h:
3704
return R, U
3705
assert len(R) < h, "bug -- too many quadratic forms"
3706
a += 1
3707
3708
def _qf_to_tau(self, f):
3709
r"""
3710
Function used internally that given a quadratic form
3711
`f=(A,B,C)`, return `\tau` in the upper half plane with
3712
`A\tau^2 + B \tau + C = 0`. Here `A>0` and `\gcd(A,B,C)=1`.
3713
Also, `\tau` has discriminant `D=B^2-4AC`. In fact, `\tau =
3714
(-B + \sqrt{D})/(2A)`.
3715
3716
INPUT:
3717
3718
- `f` -- binary quadratic form
3719
3720
EXAMPLES::
3721
3722
sage: P = EllipticCurve('57a1').heegner_point(-8)
3723
sage: R, U = P._good_tau_representatives()
3724
sage: f = U[0][0]; f
3725
(57, 26, 3)
3726
sage: P._qf_to_tau(f)
3727
1/114*sqrt_minus_8 - 13/57
3728
"""
3729
c = self.conductor()
3730
A,B,_ = f
3731
alpha = c * self.quadratic_field().gen() # this is sqrt(D) = sqrt(c^2*disc(K))
3732
return (-B + alpha)/(2*A)
3733
3734
def _qf_from_tau(self, tau):
3735
r"""
3736
Return quadratic form associated to a given `\tau` in the upper
3737
half plane.
3738
3739
INPUT:
3740
3741
- `\tau` -- quadratic element of the upper half plane
3742
3743
EXAMPLES::
3744
3745
sage: P = EllipticCurve('57a1').heegner_point(-8)
3746
sage: R, U = P._good_tau_representatives()
3747
sage: f = U[0][0]; f
3748
(57, 26, 3)
3749
sage: tau = P._qf_to_tau(f); tau
3750
1/114*sqrt_minus_8 - 13/57
3751
sage: P._qf_from_tau(tau)
3752
(57, 26, 3)
3753
"""
3754
g = tau.minpoly()
3755
g *= g.denominator()
3756
return (ZZ(g[2]), ZZ(g[1]), ZZ(g[0]))
3757
3758
3759
def _qf_atkin_lehner_act(self, Q, f):
3760
r"""
3761
Given a positive integer `Q` with `Q | N` and `\gcd(Q, N/Q) =
3762
1`, we compute the quadratic form corresponding to the image
3763
of the `tau` corresponding to `f` under the Atkin-Lehner
3764
operator `W_Q`.
3765
3766
We do this by letting `u,v` be integers such that
3767
`u Q^2 - v N = Q`, and using that `W_Q` sends `\tau`
3768
to `( (u Q \tau + v) / (N \tau + Q) ) / Q`.
3769
3770
INPUT:
3771
3772
- `Q` -- integer that divides the level `N`
3773
3774
- `f` -- quadratic form
3775
3776
OUTPUT:
3777
3778
- quadratic form
3779
3780
EXAMPLES::
3781
3782
sage: P = EllipticCurve('57a1').heegner_point(-8)
3783
sage: R, U = P._good_tau_representatives()
3784
sage: f = U[0][0]; f
3785
(57, 26, 3)
3786
sage: P._qf_atkin_lehner_act(3, f)
3787
(1938, 1204, 187)
3788
sage: g = P._qf_atkin_lehner_act(19, f); g
3789
(114, -64, 9)
3790
sage: h = P._qf_atkin_lehner_act(19, g); h
3791
(7353, -4762, 771)
3792
sage: BinaryQF(f).reduced_form() == BinaryQF(h).reduced_form()
3793
True
3794
"""
3795
N = self.__E.conductor()
3796
g, u, v = arith.xgcd(Q*Q, -N)
3797
assert g == Q
3798
tau = self._qf_to_tau(f)
3799
tau2 = ((u*Q*tau + v) / (N*tau + Q))
3800
return self._qf_from_tau(tau2)
3801
3802
3803
def _qf_reduce(self, f):
3804
"""
3805
Given a binary quadratic form `f` represented as a 3-tuple
3806
(A,B,C), return the reduced binary quadratic form equivalent
3807
to `f`, represented in the same way.
3808
3809
EXAMPLES::
3810
3811
sage: P = EllipticCurve('57a1').heegner_point(-8)
3812
sage: R, U = P._good_tau_representatives()
3813
sage: f = U[0][0]; f
3814
(57, 26, 3)
3815
sage: P._qf_reduce(f)
3816
(1, 0, 2)
3817
"""
3818
return tuple(BinaryQF(f).reduced_form())
3819
3820
def kolyvagin_cohomology_class(self, n=None):
3821
"""
3822
Return the Kolyvagin class associated to this Heegner point.
3823
3824
INPUT:
3825
3826
- `n` -- positive integer that divides the gcd of `a_p`
3827
and `p+1` for all `p` dividing the conductor. If `n` is
3828
None, choose the largest valid `n`.
3829
3830
EXAMPLES::
3831
3832
sage: y = EllipticCurve('389a').heegner_point(-7,5)
3833
sage: y.kolyvagin_cohomology_class(3)
3834
Kolyvagin cohomology class c(5) in H^1(K,E[3])
3835
"""
3836
return KolyvaginCohomologyClassEn(self.kolyvagin_point(), n)
3837
3838
#########################################################################################
3839
# Kolyvagin Points P_c
3840
#########################################################################################
3841
class KolyvaginPoint(HeegnerPoint):
3842
"""
3843
A Kolyvagin point.
3844
3845
EXAMPLES:
3846
3847
We create a few Kolyvagin points::
3848
3849
sage: EllipticCurve('11a1').kolyvagin_point(-7)
3850
Kolyvagin point of discriminant -7 on elliptic curve of conductor 11
3851
sage: EllipticCurve('37a1').kolyvagin_point(-7)
3852
Kolyvagin point of discriminant -7 on elliptic curve of conductor 37
3853
sage: EllipticCurve('37a1').kolyvagin_point(-67)
3854
Kolyvagin point of discriminant -67 on elliptic curve of conductor 37
3855
sage: EllipticCurve('389a1').kolyvagin_point(-7, 5)
3856
Kolyvagin point of discriminant -7 and conductor 5 on elliptic curve of conductor 389
3857
3858
One can also associated a Kolyvagin point to a Heegner point::
3859
3860
sage: y = EllipticCurve('37a1').heegner_point(-7); y
3861
Heegner point of discriminant -7 on elliptic curve of conductor 37
3862
sage: y.kolyvagin_point()
3863
Kolyvagin point of discriminant -7 on elliptic curve of conductor 37
3864
3865
TESTS::
3866
3867
sage: y = EllipticCurve('37a1').heegner_point(-7)
3868
sage: type(y)
3869
<class 'sage.schemes.elliptic_curves.heegner.HeegnerPointOnEllipticCurve'>
3870
sage: loads(dumps(y)) == y
3871
True
3872
"""
3873
def __init__(self, heegner_point):
3874
"""
3875
Create a Kolyvagin point.
3876
3877
INPUT:
3878
3879
- ``heegner_point`` -- a Heegner point on some elliptic curve
3880
3881
EXAMPLES:
3882
3883
We directly construct a Kolyvagin point from the KolyvaginPoint class::
3884
3885
sage: y = EllipticCurve('37a1').heegner_point(-7)
3886
sage: sage.schemes.elliptic_curves.heegner.KolyvaginPoint(y)
3887
Kolyvagin point of discriminant -7 on elliptic curve of conductor 37
3888
"""
3889
if not heegner_point.satisfies_kolyvagin_hypothesis():
3890
raise ValueError, "Heegner point doesn't satisfy Kolyvagin hypothesis"
3891
self.__heegner_point = heegner_point
3892
HeegnerPoint.__init__(self, heegner_point.level(), heegner_point.discriminant(),
3893
heegner_point.conductor())
3894
3895
def satisfies_kolyvagin_hypothesis(self, n=None):
3896
r"""
3897
Return True if this Kolyvagin point satisfies the Heegner
3898
hypothesis for `n`, so that it defines a Galois equivariant
3899
element of `E(K_c)/n E(K_c)`.
3900
3901
EXAMPLES::
3902
3903
sage: y = EllipticCurve('389a').heegner_point(-7,5); P = y.kolyvagin_point()
3904
sage: P.kolyvagin_cohomology_class(3)
3905
Kolyvagin cohomology class c(5) in H^1(K,E[3])
3906
sage: P.satisfies_kolyvagin_hypothesis(3)
3907
True
3908
sage: P.satisfies_kolyvagin_hypothesis(5)
3909
False
3910
sage: P.satisfies_kolyvagin_hypothesis(7)
3911
False
3912
sage: P.satisfies_kolyvagin_hypothesis(11)
3913
False
3914
"""
3915
return self.__heegner_point.satisfies_kolyvagin_hypothesis(n)
3916
3917
def curve(self):
3918
r"""
3919
Return the elliptic curve over `\QQ` on which this Kolyvagin
3920
point sits.
3921
3922
EXAMPLES::
3923
3924
sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67, 3)
3925
sage: P.curve()
3926
Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
3927
"""
3928
return self.__heegner_point.curve()
3929
3930
def heegner_point(self):
3931
"""
3932
This Kolyvagin point `P_c` is associated to some Heegner point
3933
`y_c` via Kolyvagin's construction. This function returns that
3934
point `y_c`.
3935
3936
EXAMPLES::
3937
3938
sage: E = EllipticCurve('37a1')
3939
sage: P = E.kolyvagin_point(-67); P
3940
Kolyvagin point of discriminant -67 on elliptic curve of conductor 37
3941
sage: y = P.heegner_point(); y
3942
Heegner point of discriminant -67 on elliptic curve of conductor 37
3943
sage: y.kolyvagin_point() is P
3944
True
3945
"""
3946
return self.__heegner_point
3947
3948
def _repr_(self):
3949
"""
3950
Return string representation of this Kolyvagin point.
3951
3952
EXAMPLES::
3953
3954
sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67,7); P._repr_()
3955
'Kolyvagin point of discriminant -67 and conductor 7 on elliptic curve of conductor 37'
3956
"""
3957
s = repr(self.__heegner_point)
3958
return s.replace('Heegner','Kolyvagin')
3959
3960
def index(self, *args, **kwds):
3961
"""
3962
Return index of this Kolyvagin point in the full group of
3963
$K_c$ rational points on $E$.
3964
3965
When the conductor is 1, this is computed numerically using
3966
the Gross-Zagier formula and explicit point search, and it may
3967
be off by $2$. See the documentation for ``E.heegner_index``,
3968
where `E` is the curve attached to self.
3969
3970
EXAMPLES::
3971
3972
sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67); P.index()
3973
6
3974
"""
3975
if self.conductor() == 1:
3976
return self.__heegner_point._trace_index(*args, **kwds)
3977
raise NotImplementedError
3978
3979
def numerical_approx(self, prec=53):
3980
"""
3981
Return a numerical approximation to this Kolyvagin point using
3982
prec bits of working precision.
3983
3984
INPUT:
3985
3986
- ``prec`` -- precision in bits (default: 53)
3987
3988
EXAMPLES::
3989
3990
sage: P = EllipticCurve('37a1').kolyvagin_point(-7); P
3991
Kolyvagin point of discriminant -7 on elliptic curve of conductor 37
3992
sage: P.numerical_approx() # approx. (0 : 0 : 1)
3993
(...e-16 - ...e-16*I : ...e-16 + ...e-16*I : 1.00000000000000)
3994
sage: P.numerical_approx(100)[0].abs() < 2.0^-99
3995
True
3996
3997
sage: P = EllipticCurve('389a1').kolyvagin_point(-7, 5); P
3998
Kolyvagin point of discriminant -7 and conductor 5 on elliptic curve of conductor 389
3999
4000
Numerical approximation is only implemented for points of conductor 1::
4001
4002
sage: P.numerical_approx()
4003
Traceback (most recent call last):
4004
...
4005
NotImplementedError
4006
"""
4007
if self.conductor() == 1:
4008
return self.__heegner_point._trace_numerical_conductor_1(prec)
4009
raise NotImplementedError
4010
4011
def point_exact(self, prec=53):
4012
"""
4013
INPUT:
4014
4015
- ``prec`` -- precision in bits (default: 53)
4016
4017
EXAMPLES:
4018
4019
A rank 1 curve::
4020
4021
sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67)
4022
sage: P.point_exact()
4023
(6 : -15 : 1)
4024
sage: P.point_exact(40)
4025
(6 : -15 : 1)
4026
sage: P.point_exact(20)
4027
Traceback (most recent call last):
4028
...
4029
RuntimeError: insufficient precision to find exact point
4030
4031
A rank 0 curve::
4032
4033
sage: E = EllipticCurve('11a1'); P = E.kolyvagin_point(-7)
4034
sage: P.point_exact()
4035
(-1/2*sqrt_minus_7 + 1/2 : -2*sqrt_minus_7 - 2 : 1)
4036
4037
A rank 2 curve::
4038
4039
sage: E = EllipticCurve('389a1'); P = E.kolyvagin_point(-7)
4040
sage: P.point_exact()
4041
(0 : 1 : 0)
4042
4043
"""
4044
if self.conductor() == 1:
4045
# the result is a point defined over K in the conductor 1 case, which is easier.
4046
P = self.numerical_approx(prec)
4047
4048
E = self.curve()
4049
if P[2] == 0:
4050
return E(0)
4051
4052
if E.root_number() == -1:
4053
return self._recognize_point_over_QQ(P, 2*self.index())
4054
else:
4055
# root number +1. We use algdep to recognize the x
4056
# coordinate, stick it in the appropriate quadratic
4057
# field, then make sure that we got the right
4058
# embedding, and if not fix things so we do.
4059
x = P[0]
4060
C = x.parent()
4061
f = x.algdep(2)
4062
K = self.quadratic_field()
4063
roots = [r[0] for r in f.roots(K)]
4064
if len(roots) == 0:
4065
raise RuntimeError, "insufficient precision to find exact point"
4066
if len(roots) == 1:
4067
X = roots[0]
4068
else:
4069
d = [abs(C(r) - x) for r in roots]
4070
if d[0] == d[1]:
4071
raise RuntimeError, "insufficient precision to distinguish roots"
4072
if d[0] < d[1]:
4073
X = roots[0]
4074
else:
4075
X = roots[1]
4076
F = E.change_ring(K)
4077
Q = F.lift_x(X, all=True)
4078
if len(Q) == 1:
4079
return Q[0]
4080
if len(Q) == 0:
4081
raise RuntimeError, "insufficient precision"
4082
y = P[1]
4083
d = [abs(C(r[1])-y) for r in Q]
4084
if d[0] == d[1]:
4085
raise RuntimeError, "insufficient precision to distinguish roots"
4086
if d[0] < d[1]:
4087
return Q[0]
4088
else:
4089
return Q[1]
4090
4091
else:
4092
raise NotImplementedError
4093
4094
def plot(self, prec=53, *args, **kwds):
4095
r"""
4096
Plot a Kolyvagin point `P_1` if it is defined over the
4097
rational numbers.
4098
4099
EXAMPLES::
4100
4101
sage: E = EllipticCurve('37a'); P = E.heegner_point(-11).kolyvagin_point()
4102
sage: P.plot(prec=30, pointsize=50, rgbcolor='red') + E.plot()
4103
"""
4104
if self.conductor() != 1:
4105
raise NotImplementedError
4106
4107
E = self.curve()
4108
if E.root_number() == -1:
4109
P = self.numerical_approx(prec=prec)
4110
from sage.plot.all import point, Graphics
4111
if not P:
4112
# point at infinity
4113
return Graphics()
4114
return point((P[0].real(), P[1].real()),*args, **kwds)
4115
else:
4116
raise NotImplementedError
4117
4118
4119
@cached_method
4120
def trace_to_real_numerical(self, prec=53):
4121
"""
4122
Return the trace of this Kolyvagin point down to the real
4123
numbers, computed numerically using prec bits of working
4124
precision.
4125
4126
EXAMPLES::
4127
4128
sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67)
4129
sage: PP = P.numerical_approx(); PP
4130
(6.00000000000000 ... : -15.0000000000000 ... : 1.00000000000000)
4131
sage: [c.real() for c in PP]
4132
[6.00000000000000, -15.0000000000000, 1.00000000000000]
4133
sage: all([c.imag().abs() < 1e-14 for c in PP])
4134
True
4135
sage: P.trace_to_real_numerical()
4136
(1.61355529131986 : -2.18446840788880 : 1.00000000000000)
4137
sage: P.trace_to_real_numerical(prec=80)
4138
(1.6135552913198573127230 : -2.1844684078888023289187 : 1.0000000000000000000000)
4139
4140
"""
4141
# Compute numerical approximation of P in E(K).
4142
P = self.numerical_approx(prec=prec)
4143
# Trace this numerical approximation down to E(Q) (numerically).
4144
E = P.curve()
4145
if self.curve().root_number() == -1:
4146
R = 2*P
4147
else:
4148
R = P + E.point([x.conjugate() for x in P],check=False)
4149
F = self.curve().change_ring(rings.RealField(prec))
4150
return F.point([x.real() for x in R], check=False)
4151
4152
@cached_method
4153
def _trace_exact_conductor_1(self, prec=53):
4154
r"""
4155
Return the trace from `K` to `\QQ` of this Kolyvagin point in
4156
the case of conductor 1, computed using prec bits of
4157
precision, then approximated using some algorithm (e.g.,
4158
continued fractions). If the precision is not enough to
4159
determine a point on the curve, then a RuntimeError is raised.
4160
Even if the precision determines a point, there is no guarantee
4161
that it is correct.
4162
4163
EXAMPLES:
4164
4165
A Kolyvagin point on a rank 1 curve::
4166
4167
sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67)
4168
sage: P.trace_to_real_numerical()
4169
(1.61355529131986 : -2.18446840788880 : 1.00000000000000)
4170
sage: P._trace_exact_conductor_1() # the actual point we're reducing
4171
(1357/841 : -53277/24389 : 1)
4172
sage: (P._trace_exact_conductor_1().height() / E.regulator()).sqrt()
4173
12.0000000000000
4174
"""
4175
if not self.conductor() == 1:
4176
raise ValueError, "the conductor must be 1"
4177
4178
P = self.trace_to_real_numerical(prec)
4179
return self._recognize_point_over_QQ(P, 2*self.index())
4180
4181
def _recognize_point_over_QQ(self, P, n):
4182
"""
4183
Used internally when computing an exact point on an elliptic curve.
4184
4185
INPUT:
4186
4187
- `P` -- numerical approximation for a point on `E`
4188
4189
- `n` -- upper bound on divisibility index of `P` in group `E(\QQ)`
4190
4191
EXAMPLES::
4192
4193
sage: E = EllipticCurve('43a'); P = E.heegner_point(-20).kolyvagin_point()
4194
sage: PP = P.numerical_approx(); PP
4195
(...e-16 : -1.00000000000000 : 1.00000000000000)
4196
sage: P._recognize_point_over_QQ(PP, 4)
4197
(0 : -1 : 1)
4198
"""
4199
# Here is where we *should* implement the "method of Cremona
4200
# etc" mentioned in Watkins' article... which involves local
4201
# heights.
4202
E = self.curve() # over Q
4203
v = sum([list(n*w) for w in E.gens()] + [list(w) for w in E.torsion_points()], [])
4204
# note -- we do not claim to prove anything, so making up a factor of 100 is fine.
4205
max_denominator = 100*max([z.denominator() for z in v])
4206
try:
4207
# the coercion below also checks if point is on elliptic curve
4208
return E([x.real().nearby_rational(max_denominator=max_denominator) for x in P])
4209
except TypeError:
4210
raise RuntimeError, "insufficient precision to find exact point"
4211
4212
def mod(self, p, prec=53):
4213
r"""
4214
Return the trace of the reduction `Q` modulo a prime over `p` of this
4215
Kolyvagin point as an element of `E(\GF{p})`, where
4216
`p` is any prime that is inert in `K` that is coprime to `NDc`.
4217
The point `Q` is only well defined up to an element of
4218
`(p+1) E(\GF{p})`, i.e., it gives a well defined element
4219
of the abelian group `E(\GF{p}) / (p+1) E(\GF{p})`.
4220
4221
See Stein, "Toward a Generalization of the Gross-Zagier Conjecture",
4222
Proposition 5.4 for a proof of the above well-definedness assertion.
4223
4224
EXAMPLES:
4225
4226
A Kolyvagin point on a rank 1 curve::
4227
4228
sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67)
4229
sage: P.mod(2)
4230
(1 : 1 : 1)
4231
sage: P.mod(3)
4232
(1 : 0 : 1)
4233
sage: P.mod(5)
4234
(2 : 2 : 1)
4235
sage: P.mod(7)
4236
(6 : 0 : 1)
4237
sage: P.trace_to_real_numerical()
4238
(1.61355529131986 : -2.18446840788880 : 1.00000000000000)
4239
sage: P._trace_exact_conductor_1() # the actual point we're reducing
4240
(1357/841 : -53277/24389 : 1)
4241
sage: (P._trace_exact_conductor_1().height() / E.regulator()).sqrt()
4242
12.0000000000000
4243
4244
Here the Kolyvagin point is a torsion point (since `E` has
4245
rank 1), and we reduce it modulo several primes.::
4246
4247
sage: E = EllipticCurve('11a1'); P = E.kolyvagin_point(-7)
4248
sage: P.mod(3,70)
4249
(1 : 2 : 1)
4250
sage: P.mod(5,70)
4251
(1 : 4 : 1)
4252
sage: P.mod(7,70)
4253
Traceback (most recent call last):
4254
...
4255
ValueError: p must be coprime to conductors and discriminant
4256
sage: P.mod(11,70)
4257
Traceback (most recent call last):
4258
...
4259
ValueError: p must be coprime to conductors and discriminant
4260
sage: P.mod(13,70)
4261
(3 : 4 : 1)
4262
"""
4263
# check preconditions
4264
p = ZZ(p)
4265
if not p.is_prime():
4266
raise ValueError, "p must be prime"
4267
E = self.curve()
4268
D = self.discriminant()
4269
if (E.conductor() * D * self.conductor()) % p == 0:
4270
raise ValueError, "p must be coprime to conductors and discriminant"
4271
K = self.heegner_point().quadratic_field()
4272
if len(K.factor(p)) != 1:
4273
raise ValueError, "p must be inert"
4274
4275
# do actual calculation
4276
if self.conductor() == 1:
4277
4278
P = self._trace_exact_conductor_1(prec = prec)
4279
return E.change_ring(GF(p))(P)
4280
4281
else:
4282
4283
raise NotImplementedError
4284
4285
## def congruent_rational_point(self, n, prec=53):
4286
## r"""
4287
## Let `P` be this Kolyvagin point. Determine whether there is a
4288
## point `z` in `E(\QQ)` such that `z - P \in n E(K_c)`, where `K_c`
4289
## is the ring class field over which this Kolyvagin point is defined.
4290
## If `z` exists return `z`. Otherwise return None.
4291
##
4292
## INPUT:
4293
##
4294
## - `n` -- positive integer
4295
##
4296
## - ``prec`` -- positive integer (default: 53)
4297
##
4298
##
4299
## EXAMPLES::
4300
##
4301
## """
4302
## raise NotImplementedError
4303
4304
4305
def kolyvagin_cohomology_class(self, n=None):
4306
"""
4307
INPUT:
4308
4309
- `n` -- positive integer that divides the gcd of `a_p`
4310
and `p+1` for all `p` dividing the conductor. If `n` is
4311
None, choose the largest valid `n`.
4312
4313
EXAMPLES::
4314
4315
sage: y = EllipticCurve('389a').heegner_point(-7,5)
4316
sage: P = y.kolyvagin_point()
4317
sage: P.kolyvagin_cohomology_class(3)
4318
Kolyvagin cohomology class c(5) in H^1(K,E[3])
4319
4320
sage: y = EllipticCurve('37a').heegner_point(-7,5).kolyvagin_point()
4321
sage: y.kolyvagin_cohomology_class()
4322
Kolyvagin cohomology class c(5) in H^1(K,E[2])
4323
"""
4324
D = self.discriminant()
4325
c = self.conductor()
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 doesn't 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 isn't 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([z for z in reps if arith.gcd(z) == 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 don't 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 isn't 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
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()]
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 doesn't 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's 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
from sage.matrix.all import matrix
5768
return matrix(QQ,2,4,[[1,0,0,0], self.__beta.coefficient_tuple()])
5769
5770
@cached_method
5771
def domain(self):
5772
"""
5773
Return the domain of this embedding.
5774
5775
EXAMPLES::
5776
5777
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5778
sage: H.optimal_embeddings(-7, 2, R)[0].domain()
5779
Order in Number Field in a with defining polynomial x^2 + 7
5780
"""
5781
R, a = quadratic_order(self.__D, self.__c)
5782
5783
# The following assumption is used, e.g., in the __call__
5784
# method. I know that it is satisfied by the current
5785
# implementation. But somebody might someday annoying change
5786
# the implementation, and we want to catch that if it were to
5787
# ever happen.
5788
5789
assert R.basis() == [1, a], "an assumption about construction of orders is violated"
5790
self.__domain_gen = a
5791
return R
5792
5793
def domain_gen(self):
5794
r"""
5795
Return the specific generator `c \sqrt{D}` for the domain
5796
order.
5797
5798
EXAMPLES::
5799
5800
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5801
sage: f = H.optimal_embeddings(-7, 2, R)[0]
5802
sage: f.domain_gen()
5803
2*a
5804
sage: f.domain_gen()^2
5805
-28
5806
"""
5807
self.domain()
5808
return self.__domain_gen
5809
5810
def domain_conductor(self):
5811
"""
5812
Return the conductor of the domain.
5813
5814
EXAMPLES::
5815
5816
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5817
sage: H.optimal_embeddings(-7, 2, R)[0].domain_conductor()
5818
2
5819
"""
5820
return self.__c
5821
5822
def beta(self):
5823
r"""
5824
Return the element `\beta` in the quaternion algebra order
5825
that `c\sqrt{D}` maps to.
5826
5827
EXAMPLES::
5828
5829
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5830
sage: H.optimal_embeddings(-7, 2, R)[0].beta()
5831
-5*i + k
5832
"""
5833
return self.__beta
5834
5835
def codomain(self):
5836
"""
5837
Return the codomain of this embedding.
5838
5839
EXAMPLES::
5840
5841
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5842
sage: H.optimal_embeddings(-7, 2, R)[0].codomain()
5843
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)
5844
"""
5845
return self.__R
5846
5847
@cached_method
5848
def _repr_(self):
5849
"""
5850
Return string representation of this embedding.
5851
5852
EXAMPLES::
5853
5854
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5855
sage: f = H.optimal_embeddings(-7, 2, R)[0]; f._repr_()
5856
'Embedding sending 2*sqrt(-7) to -5*i + k'
5857
"""
5858
R = self.domain()
5859
a = '%ssqrt(%s)'%('%s*'%self.__c if self.__c > 1 else '', self.__D)
5860
return "Embedding sending %s to %s"%(a, self.__beta)
5861
5862
def conjugate(self):
5863
"""
5864
Return the conjugate of this embedding, which is also an
5865
embedding.
5866
5867
EXAMPLES::
5868
5869
sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]
5870
sage: f = H.optimal_embeddings(-7, 2, R)[0]
5871
sage: f.conjugate()
5872
Embedding sending 2*sqrt(-7) to 5*i - k
5873
sage: f
5874
Embedding sending 2*sqrt(-7) to -5*i + k
5875
"""
5876
return HeegnerQuatAlgEmbedding(self.__D, self.__c,
5877
self.__R, self.__beta.conjugate())
5878
5879
5880
#############################################################################
5881
# Utility Functions
5882
#############################################################################
5883
5884
def quadratic_order(D, c, names='a'):
5885
"""
5886
Return order of conductor `c` in quadratic field with fundamental
5887
discriminant `D`.
5888
5889
INPUT:
5890
5891
- `D` -- fundamental discriminant
5892
5893
- `c` -- conductor
5894
5895
- ``names`` -- string (default: 'a')
5896
5897
OUTPUT:
5898
5899
- order `R` of conductor `c` in an imaginary quadratic field
5900
5901
- the element `c\sqrt{D}` as an element of `R`
5902
5903
The generator for the field is named 'a' by default.
5904
5905
EXAMPLES::
5906
5907
sage: sage.schemes.elliptic_curves.heegner.quadratic_order(-7,3)
5908
(Order in Number Field in a with defining polynomial x^2 + 7, 3*a)
5909
sage: sage.schemes.elliptic_curves.heegner.quadratic_order(-7,3,'alpha')
5910
(Order in Number Field in alpha with defining polynomial x^2 + 7, 3*alpha)
5911
"""
5912
K = QuadraticField(D, names)
5913
sqrtD = K.gen(0)
5914
t = sqrtD * c
5915
R = K.order([t])
5916
return R, R(t)
5917
5918
def class_number(D):
5919
"""
5920
Return the class number of the quadratic field with fundamental
5921
discriminant `D`.
5922
5923
INPUT:
5924
5925
- `D` -- integer
5926
5927
EXAMPLES::
5928
5929
sage: sage.schemes.elliptic_curves.heegner.class_number(-20)
5930
2
5931
sage: sage.schemes.elliptic_curves.heegner.class_number(-23)
5932
3
5933
sage: sage.schemes.elliptic_curves.heegner.class_number(-163)
5934
1
5935
5936
A ValueError is raised when `D` is not a fundamental
5937
discriminant::
5938
5939
sage: sage.schemes.elliptic_curves.heegner.class_number(-5)
5940
Traceback (most recent call last):
5941
...
5942
ValueError: D (=-5) must be a fundamental discriminant
5943
"""
5944
if not number_field.is_fundamental_discriminant(D):
5945
raise ValueError, "D (=%s) must be a fundamental discriminant"%D
5946
return QuadraticField(D,'a').class_number()
5947
5948
def is_inert(D, p):
5949
r"""
5950
Return True if p is an inert prime in the field `\QQ(\sqrt{D})`.
5951
5952
INPUT:
5953
5954
- `D` -- fundamental discriminant
5955
5956
- `p` -- prime integer
5957
5958
EXAMPLES::
5959
5960
sage: sage.schemes.elliptic_curves.heegner.is_inert(-7,3)
5961
True
5962
sage: sage.schemes.elliptic_curves.heegner.is_inert(-7,7)
5963
False
5964
sage: sage.schemes.elliptic_curves.heegner.is_inert(-7,11)
5965
False
5966
"""
5967
K = QuadraticField(D,'a')
5968
F = K.factor(p)
5969
return len(F) == 1 and F[0][1] == 1
5970
5971
def is_split(D, p):
5972
r"""
5973
Return True if p is a split prime in the field `\QQ(\sqrt{D})`.
5974
5975
INPUT:
5976
5977
- `D` -- fundamental discriminant
5978
5979
- `p` -- prime integer
5980
5981
EXAMPLES::
5982
5983
sage: sage.schemes.elliptic_curves.heegner.is_split(-7,3)
5984
False
5985
sage: sage.schemes.elliptic_curves.heegner.is_split(-7,7)
5986
False
5987
sage: sage.schemes.elliptic_curves.heegner.is_split(-7,11)
5988
True
5989
"""
5990
K = QuadraticField(D,'a')
5991
F = K.factor(p)
5992
return len(F) == 2
5993
5994
def is_ramified(D, p):
5995
r"""
5996
Return True if p is a ramified prime in the field `\QQ(\sqrt{D})`.
5997
5998
INPUT:
5999
6000
- `D` -- fundamental discriminant
6001
6002
- `p` -- prime integer
6003
6004
EXAMPLES::
6005
6006
sage: sage.schemes.elliptic_curves.heegner.is_ramified(-7,2)
6007
False
6008
sage: sage.schemes.elliptic_curves.heegner.is_ramified(-7,7)
6009
True
6010
sage: sage.schemes.elliptic_curves.heegner.is_ramified(-1,2)
6011
True
6012
"""
6013
return QuadraticField(D,'a').discriminant() % p == 0
6014
6015
def nearby_rational_poly(f, **kwds):
6016
r"""
6017
Return a polynomial whose coefficients are rational numbers close
6018
to the coefficients of `f`.
6019
6020
INPUT:
6021
6022
- `f` -- polynomial with real floating point entries
6023
6024
- ``**kwds`` -- passed on to ``nearby_rational`` method
6025
6026
EXAMPLES::
6027
6028
sage: R.<x> = RR[]
6029
sage: sage.schemes.elliptic_curves.heegner.nearby_rational_poly(2.1*x^2 + 3.5*x - 1.2, max_error=10e-16)
6030
21/10*X^2 + 7/2*X - 6/5
6031
sage: sage.schemes.elliptic_curves.heegner.nearby_rational_poly(2.1*x^2 + 3.5*x - 1.2, max_error=10e-17)
6032
4728779608739021/2251799813685248*X^2 + 7/2*X - 5404319552844595/4503599627370496
6033
sage: RR(4728779608739021/2251799813685248 - 21/10)
6034
8.88178419700125e-17
6035
"""
6036
R = QQ['X']
6037
return R([a.nearby_rational(**kwds) for a in f])
6038
6039
def simplest_rational_poly(f, prec):
6040
"""
6041
Return a polynomial whose coefficients are as simple as possible
6042
rationals that are also close to the coefficients of f.
6043
6044
INPUT:
6045
6046
- `f` -- polynomial with real floating point entries
6047
6048
- ``prec`` -- positive integer
6049
6050
EXAMPLES::
6051
6052
sage: R.<x> = RR[]
6053
sage: sage.schemes.elliptic_curves.heegner.simplest_rational_poly(2.1*x^2 + 3.5*x - 1.2, 53)
6054
21/10*X^2 + 7/2*X - 6/5
6055
"""
6056
R = QQ['X']
6057
Z = RealField(prec)
6058
return R([Z(a).simplest_rational() for a in f])
6059
6060
def satisfies_weak_heegner_hypothesis(N, D):
6061
r"""
6062
Check that `D` satisfies the weak Heegner hypothesis relative to `N`.
6063
This is all that is needed to define Heegner points.
6064
6065
The condition is that `D<0` is a fundamental discriminant and that
6066
each unramified prime dividing `N` splits in `K=\QQ(\sqrt{D})` and
6067
each ramified prime exactly divides `N`. We also do not require
6068
that `D<-4`.
6069
6070
INPUT:
6071
6072
- `N` -- positive integer
6073
6074
- `D` -- negative integer
6075
6076
EXAMPLES::
6077
6078
sage: s = sage.schemes.elliptic_curves.heegner.satisfies_weak_heegner_hypothesis
6079
sage: s(37,-7)
6080
True
6081
sage: s(37,-37)
6082
False
6083
sage: s(37,-37*4)
6084
True
6085
sage: s(100,-4)
6086
False
6087
sage: [D for D in [-1,-2,..,-40] if s(37,D)]
6088
[-3, -4, -7, -11, -40]
6089
sage: [D for D in [-1,-2,..,-100] if s(37,D)]
6090
[-3, -4, -7, -11, -40, -47, -67, -71, -83, -84, -95]
6091
sage: EllipticCurve('37a').heegner_discriminants_list(10)
6092
[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104]
6093
"""
6094
if not number_field.is_fundamental_discriminant(D):
6095
return False
6096
if D >= 0: return False
6097
K = QuadraticField(D,'a')
6098
for p, e in N.factor():
6099
if D % p == 0:
6100
if e > 1:
6101
return False
6102
elif D.kronecker(p) != 1:
6103
return False
6104
return True
6105
6106
def make_monic(f):
6107
r"""
6108
``make_monic`` returns a monic integral polynomial `g` and an
6109
integer `d` such that if `\alpha` is a root of `g` then a root of
6110
`f` is `\alpha/d`.
6111
6112
INPUT:
6113
6114
- f -- polynomial over the rational numbers
6115
6116
EXAMPLES::
6117
6118
sage: R.<x> = QQ[]
6119
sage: sage.schemes.elliptic_curves.heegner.make_monic(3*x^3 + 14*x^2 - 7*x + 5)
6120
(x^3 + 14*x^2 - 21*x + 45, 3)
6121
6122
In this example we verify that make_monic does what we claim it does::
6123
6124
sage: K.<a> = NumberField(x^3 + 17*x - 3)
6125
sage: f = (a/7+2/3).minpoly(); f
6126
x^3 - 2*x^2 + 247/147*x - 4967/9261
6127
sage: g, d = sage.schemes.elliptic_curves.heegner.make_monic(f)
6128
sage: g
6129
x^3 - 18522*x^2 + 144110421*x - 426000323007
6130
sage: d
6131
9261
6132
sage: K.<b> = NumberField(g)
6133
sage: (b/d).minpoly()
6134
x^3 - 2*x^2 + 247/147*x - 4967/9261
6135
"""
6136
# make f monic
6137
n = f.degree()
6138
f = f / f.leading_coefficient()
6139
# find lcm of denominators
6140
d = arith.lcm([b.denominator() for b in f.list() if b])
6141
x = f.variables()[0]
6142
g = (d**n) * f(x/d)
6143
return g, d
6144
6145
6146
#####################################################################
6147
# Elliptic curve methods
6148
# Everywhere self below is an elliptic curve over QQ.
6149
#####################################################################
6150
6151
def ell_heegner_point(self, D, c=ZZ(1), f=None, check=True):
6152
"""
6153
Returns the Heegner point on this curve associated to the
6154
quadratic imaginary field `K=\QQ(\sqrt{D})`.
6155
6156
If the optional parameter `c` is given, returns the higher Heegner
6157
point associated to the order of conductor `c`.
6158
6159
INPUT::
6160
6161
- `D` -- a Heegner discriminant
6162
6163
- `c` -- (default: 1) conductor, must be coprime to `DN`
6164
6165
- `f` -- binary quadratic form or 3-tuple `(A,B,C)` of coefficients
6166
of `AX^2 + BXY + CY^2`
6167
6168
- ``check`` -- bool (default: True)
6169
6170
6171
OUTPUT::
6172
6173
The Heegner point `y_c`.
6174
6175
6176
EXAMPLES::
6177
6178
sage: E = EllipticCurve('37a')
6179
sage: E.heegner_discriminants_list(10)
6180
[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104]
6181
sage: P = E.heegner_point(-7); P # indirect doctest
6182
Heegner point of discriminant -7 on elliptic curve of conductor 37
6183
sage: P.point_exact()
6184
(0 : 0 : 1)
6185
sage: P.curve()
6186
Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
6187
sage: P = E.heegner_point(-40).point_exact(); P
6188
(a : a - 2 : 1)
6189
sage: P = E.heegner_point(-47).point_exact(); P
6190
(a : a^4 + a - 1 : 1)
6191
sage: P[0].parent()
6192
Number Field in a with defining polynomial x^5 - x^4 + x^3 + x^2 - 2*x + 1
6193
6194
Working out the details manually::
6195
6196
sage: P = E.heegner_point(-47).numerical_approx(prec=200)
6197
sage: f = algdep(P[0], 5); f
6198
x^5 - x^4 + x^3 + x^2 - 2*x + 1
6199
sage: f.discriminant().factor()
6200
47^2
6201
6202
The Heegner hypothesis is checked::
6203
6204
sage: E = EllipticCurve('389a'); P = E.heegner_point(-5,7);
6205
Traceback (most recent call last):
6206
...
6207
ValueError: N (=389) and D (=-5) must satisfy the Heegner hypothesis
6208
6209
We can specify the quadratic form::
6210
6211
sage: P = EllipticCurve('389a').heegner_point(-7, 5, (778,925,275)); P
6212
Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 389
6213
sage: P.quadratic_form()
6214
778*x^2 + 925*x*y + 275*y^2
6215
"""
6216
y = HeegnerPointOnX0N(self.conductor(), D, c, f, check=check)
6217
return y.map_to_curve(self)
6218
6219
def kolyvagin_point(self, D, c=ZZ(1), check=True):
6220
"""
6221
Returns the Kolyvagin point on this curve associated to the
6222
quadratic imaginary field `K=\QQ(\sqrt{D})` and conductor `c`.
6223
6224
INPUT:
6225
6226
- `D` -- a Heegner discriminant
6227
6228
- `c` -- (default: 1) conductor, must be coprime to `DN`
6229
6230
- ``check`` -- bool (default: True)
6231
6232
6233
OUTPUT:
6234
6235
The Kolyvagin point `P` of conductor `c`.
6236
6237
EXAMPLES::
6238
6239
sage: E = EllipticCurve('37a1')
6240
sage: P = E.kolyvagin_point(-67); P
6241
Kolyvagin point of discriminant -67 on elliptic curve of conductor 37
6242
sage: P.numerical_approx() # imaginary parts approx. 0
6243
(6.00000000000000 ... : -15.0000000000000 ... : 1.00000000000000)
6244
sage: P.index()
6245
6
6246
sage: g = E((0,-1,1)) # a generator
6247
sage: E.regulator() == E.regulator_of_points([g])
6248
True
6249
sage: 6*g
6250
(6 : -15 : 1)
6251
6252
"""
6253
return self.heegner_point(D,c,check=check).kolyvagin_point()
6254
6255
def ell_heegner_discriminants(self, bound):
6256
"""
6257
Return the list of self's Heegner discriminants between -1 and
6258
-bound.
6259
6260
INPUT:
6261
6262
6263
- ``bound (int)`` - upper bound for -discriminant
6264
6265
6266
OUTPUT: The list of Heegner discriminants between -1 and -bound for
6267
the given elliptic curve.
6268
6269
EXAMPLES::
6270
6271
sage: E=EllipticCurve('11a')
6272
sage: E.heegner_discriminants(30) # indirect doctest
6273
[-7, -8, -19, -24]
6274
"""
6275
return [-D for D in xrange(1,bound) if self.satisfies_heegner_hypothesis(-D)]
6276
6277
def ell_heegner_discriminants_list(self, n):
6278
"""
6279
Return the list of self's first `n` Heegner discriminants smaller
6280
than -5.
6281
6282
INPUT:
6283
6284
- ``n (int)`` - the number of discriminants to
6285
compute
6286
6287
6288
OUTPUT: The list of the first n Heegner discriminants smaller than
6289
-5 for the given elliptic curve.
6290
6291
EXAMPLE::
6292
6293
sage: E=EllipticCurve('11a')
6294
sage: E.heegner_discriminants_list(4) # indirect doctest
6295
[-7, -8, -19, -24]
6296
"""
6297
v = []
6298
D = -5
6299
while len(v) < n:
6300
while not self.satisfies_heegner_hypothesis(D):
6301
D -= 1
6302
v.append(D)
6303
D -= 1
6304
return v
6305
6306
def heegner_point_height(self, D, prec=2):
6307
r"""
6308
Use the Gross-Zagier formula to compute the Neron-Tate canonical
6309
height over `K` of the Heegner point corresponding to `D`, as an
6310
interval (it's computed to some precision using `L`-functions).
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 conductor.
6319
6320
6321
OUTPUT: Interval that contains the height of the Heegner point.
6322
6323
EXAMPLE::
6324
6325
sage: E = EllipticCurve('11a')
6326
sage: E.heegner_point_height(-7)
6327
0.22227?
6328
"""
6329
6330
if not self.satisfies_heegner_hypothesis(D):
6331
raise ArithmeticError, "Discriminant (=%s) must be a fundamental discriminant that satisfies the Heegner hypothesis."%D
6332
if D == -3 or D == -4:
6333
raise ArithmeticError, "Discriminant (=%s) must not be -3 or -4."%D
6334
eps = self.root_number()
6335
L1_vanishes = self.lseries().L1_vanishes()
6336
6337
IR = rings.RealIntervalField(20) # TODO: why 20 bits here?
6338
6339
if eps == 1 and L1_vanishes:
6340
return IR(0) # rank even hence >= 2, so Heegner point is torsion.
6341
6342
RR = rings.RealField()
6343
from math import sqrt
6344
6345
alpha = RR(sqrt(abs(D)))/(2*self.period_lattice().complex_area())
6346
F = self.quadratic_twist(D)
6347
E = self
6348
k_E = prec*sqrt(E.conductor()) + 20
6349
k_F = prec*sqrt(F.conductor()) + 20
6350
6351
MIN_ERR = RR('1e-6') # we assume that regulator and
6352
# discriminant, etc., computed to this accuracy (which is easily the case).
6353
# this should be made more intelligent / rigorous relative
6354
# to the rest of the system.
6355
6356
if eps == 1: # E has even rank
6357
LF1, err_F = F.lseries().deriv_at1(k_F)
6358
LE1, err_E = E.lseries().at1(k_E)
6359
err_F = max(err_F, MIN_ERR)
6360
err_E = max(err_E, MIN_ERR)
6361
return IR(alpha-MIN_ERR,alpha+MIN_ERR) * IR(LE1-err_E,LE1+err_E) * IR(LF1-err_F,LF1+err_F)
6362
6363
else: # E has odd rank
6364
LE1, err_E = E.lseries().deriv_at1(k_E)
6365
LF1, err_F = F.lseries().at1(k_F)
6366
err_F = max(err_F, MIN_ERR)
6367
err_E = max(err_E, MIN_ERR)
6368
return IR(alpha-MIN_ERR,alpha+MIN_ERR) * IR(LE1-err_E,LE1+err_E) * IR(LF1-err_F,LF1+err_F)
6369
6370
6371
def heegner_index(self, D, min_p=2, prec=5, descent_second_limit=12, verbose_mwrank=False):
6372
r"""
6373
Return an interval that contains the index of the Heegner
6374
point `y_K` in the group of `K`-rational points modulo torsion
6375
on this elliptic curve, computed using the Gross-Zagier
6376
formula and/or a point search, or possibly half the index
6377
if the rank is greater than one.
6378
6379
.. note::
6380
6381
If ``min_p`` is bigger than 2 then the index can be off by
6382
any prime less than ``min_p``. This function returns the
6383
index divided by `2` exactly when the rank of `E(K)` is
6384
greater than 1 and `E(\QQ)_{/tor} \oplus E^D(\QQ)_{/tor}`
6385
has index `2` in `E(K)_{/tor}`, where the second factor
6386
undergoes a twist.
6387
6388
INPUT:
6389
6390
6391
- ``D (int)`` - Heegner discriminant
6392
6393
- ``min_p (int)`` - (default: 2) only rule out primes
6394
= min_p dividing the index.
6395
6396
- ``verbose_mwrank (bool)`` - (default: False); print lots of
6397
mwrank search status information when computing regulator
6398
6399
- ``prec (int)`` - (default: 5), use prec\*sqrt(N) +
6400
20 terms of L-series in computations, where N is the conductor.
6401
6402
- ``descent_second_limit`` - (default: 12)- used in 2-descent
6403
when computing regulator of the twist
6404
6405
OUTPUT: an interval that contains the index, or half the index
6406
6407
EXAMPLES::
6408
6409
sage: E = EllipticCurve('11a')
6410
sage: E.heegner_discriminants(50)
6411
[-7, -8, -19, -24, -35, -39, -40, -43]
6412
sage: E.heegner_index(-7)
6413
1.00000?
6414
6415
::
6416
6417
sage: E = EllipticCurve('37b')
6418
sage: E.heegner_discriminants(100)
6419
[-3, -4, -7, -11, -40, -47, -67, -71, -83, -84, -95]
6420
sage: E.heegner_index(-95) # long time (1 second)
6421
2.00000?
6422
6423
This tests doing direct computation of the Mordell-Weil group.
6424
6425
::
6426
6427
sage: EllipticCurve('675b').heegner_index(-11)
6428
3.0000?
6429
6430
Currently discriminants -3 and -4 are not supported::
6431
6432
sage: E.heegner_index(-3)
6433
Traceback (most recent call last):
6434
...
6435
ArithmeticError: Discriminant (=-3) must not be -3 or -4.
6436
6437
The curve 681b returns the true index, which is `3`::
6438
6439
sage: E = EllipticCurve('681b')
6440
sage: I = E.heegner_index(-8); I
6441
3.0000?
6442
6443
In fact, whenever the returned index has a denominator of
6444
`2`, the true index is got by multiplying the returned
6445
index by `2`. Unfortunately, this is not an if and only if
6446
condition, i.e., sometimes the index must be multiplied by
6447
`2` even though the denominator is not `2`.
6448
6449
This example demonstrates the `descent_second_limit` option,
6450
which can be used to fine tune the 2-descent used to compute
6451
the regulator of the twist::
6452
6453
sage: E = EllipticCurve([0, 0, 1, -34874, -2506691])
6454
sage: E.heegner_index(-8)
6455
Traceback (most recent call last):
6456
...
6457
RuntimeError: ...
6458
6459
However when we search higher, we find the points we need::
6460
6461
sage: E.heegner_index(-8, descent_second_limit=16)
6462
1.00000?
6463
6464
"""
6465
# First compute upper bound on height of Heegner point.
6466
tm = verbose("computing heegner point height...")
6467
h0 = self.heegner_point_height(D, prec=prec)
6468
6469
# We divide by 2 to get the height **over Q** of the
6470
# Heegner point on the twist.
6471
6472
ht = h0/2
6473
verbose('Height of heegner point = %s'%ht, tm)
6474
6475
if self.root_number() == 1:
6476
F = self.quadratic_twist(D)
6477
else:
6478
F = self
6479
# Now rank(F) > 0
6480
h = ht.upper()
6481
verbose("Heegner height bound = %s"%h)
6482
B = F.CPS_height_bound()
6483
verbose("CPS bound = %s"%B)
6484
c = h/(min_p**2) + B
6485
verbose("Search would have to be up to height = %s"%c)
6486
6487
from ell_rational_field import _MAX_HEIGHT
6488
6489
IR = rings.RealIntervalField(20) # todo: 20?
6490
6491
a = 1
6492
if c > _MAX_HEIGHT or F is self:
6493
verbose("Doing direct computation of MW group.")
6494
reg = F.regulator(descent_second_limit=descent_second_limit, verbose=verbose_mwrank)
6495
if F.rank(use_database=True) == 1:
6496
z = F.gens()[0]
6497
FK = F.base_extend(QuadraticField(D,'a'))
6498
z = FK(z)
6499
if z.is_divisible_by(2):
6500
a = 2
6501
else:
6502
FK_even_tor_pts = [T for T in FK.torsion_subgroup().gens() if T.order()%2==0]
6503
if len(FK_even_tor_pts) == 2:
6504
FK_even_tor_pts.append(sum(FK_even_tor_pts))
6505
for T in FK_even_tor_pts:
6506
if (z + T).is_divisible_by(2):
6507
a = 2; break
6508
return a*self._adjust_heegner_index(ht/IR(reg))
6509
6510
# Do naive search to eliminate possibility that Heegner point
6511
# is divisible by p<min_p, without finding Heegner point.
6512
verbose("doing point search")
6513
P = F.point_search(c)
6514
verbose("done with point search")
6515
P = [x for x in P if x.order() == rings.infinity]
6516
a = 1
6517
if len(P) == 0:
6518
return IR(1)
6519
elif len(P) == 1:
6520
z = P[0]
6521
FK = F.base_extend(QuadraticField(D,'a'))
6522
z = FK(z)
6523
if z.is_divisible_by(2):
6524
a = 2
6525
else:
6526
FK_even_tor_pts = [T for T in FK.torsion_subgroup().gens() if T.order()%2==0]
6527
if len(FK_even_tor_pts) == 2:
6528
FK_even_tor_pts.append(sum(FK_even_tor_pts))
6529
for T in FK_even_tor_pts:
6530
if (z + T).is_divisible_by(2):
6531
a = 2; break
6532
6533
verbose("saturating")
6534
S, I, reg = F.saturation(P)
6535
verbose("done saturating")
6536
return a*self._adjust_heegner_index(ht/IR(reg))
6537
6538
def _adjust_heegner_index(self, a):
6539
r"""
6540
Take the square root of the interval that contains the Heegner
6541
index.
6542
6543
EXAMPLES::
6544
6545
sage: E = EllipticCurve('11a1')
6546
sage: a = RIF(sqrt(2))-1.4142135623730951
6547
sage: E._adjust_heegner_index(a)
6548
1.?e-8
6549
"""
6550
if a.lower() < 0:
6551
IR = rings.RealIntervalField(20) # todo: 20?
6552
a = IR((0, a.upper()))
6553
return a.sqrt()
6554
6555
6556
def heegner_index_bound(self, D=0, prec=5, max_height=None):
6557
r"""
6558
Assume self has rank 0.
6559
6560
Return a list `v` of primes such that if an odd prime `p` divides
6561
the index of the Heegner point in the group of rational points
6562
modulo torsion, then `p` is in `v`.
6563
6564
If 0 is in the interval of the height of the Heegner point
6565
computed to the given prec, then this function returns `v =
6566
0`. This does not mean that the Heegner point is torsion, just
6567
that it is very likely torsion.
6568
6569
If we obtain no information from a search up to ``max_height``,
6570
e.g., if the Siksek et al. bound is bigger than ``max_height``,
6571
then we return `v = -1`.
6572
6573
INPUT:
6574
6575
6576
- ``D (int)`` - (default: 0) Heegner discriminant; if
6577
0, use the first discriminant -4 that satisfies the Heegner
6578
hypothesis
6579
6580
- ``verbose (bool)`` - (default: True)
6581
6582
- ``prec (int)`` - (default: 5), use `prec \cdot \sqrt(N) + 20`
6583
terms of `L`-series in computations, where `N` is the conductor.
6584
6585
- ``max_height (float)`` - should be = 21; bound on
6586
logarithmic naive height used in point searches. Make smaller to
6587
make this function faster, at the expense of possibly obtaining a
6588
worse answer. A good range is between 13 and 21.
6589
6590
6591
OUTPUT:
6592
6593
6594
- ``v`` - list or int (bad primes or 0 or -1)
6595
6596
- ``D`` - the discriminant that was used (this is
6597
useful if `D` was automatically selected).
6598
6599
- ``exact`` - either False, or the exact Heegner index
6600
(up to factors of 2)
6601
6602
EXAMPLES::
6603
6604
sage: E = EllipticCurve('11a1')
6605
sage: E.heegner_index_bound()
6606
([2], -7, 2)
6607
"""
6608
from ell_rational_field import _MAX_HEIGHT
6609
if max_height is None:
6610
max_height = _MAX_HEIGHT
6611
else:
6612
max_height = min(float(max_height), _MAX_HEIGHT)
6613
if self.root_number() != 1:
6614
raise RuntimeError, "The rank must be 0."
6615
6616
if D == 0:
6617
D = -5
6618
while not self.satisfies_heegner_hypothesis(D):
6619
D -= 1
6620
6621
# First compute upper bound on Height of Heegner point.
6622
ht = self.heegner_point_height(D, prec=prec)
6623
if 0 in ht:
6624
return 0, D, False
6625
F = self.quadratic_twist(D)
6626
h = ht.upper()
6627
verbose("Heegner height bound = %s"%h)
6628
B = F.CPS_height_bound()
6629
verbose("CPS bound = %s"%B)
6630
if self.two_torsion_rank() == 0:
6631
H = h
6632
else:
6633
H = 4*h
6634
p = 3
6635
from sage.all import next_prime
6636
while True:
6637
c = H/(2*p**2) + B
6638
if c < max_height:
6639
break
6640
if p > 100:
6641
break
6642
p = next_prime(p)
6643
verbose("Using p = %s"%p)
6644
6645
if c > max_height:
6646
verbose("No information by searching only up to max_height (=%s)."%c)
6647
return -1, D, False
6648
6649
verbose("Searching up to height = %s"%c)
6650
eps = 10e-5
6651
6652
def _bound(P):
6653
"""
6654
We will use this function below in two places. It bounds the index
6655
using a nontrivial point.
6656
"""
6657
assert len(P) == 1
6658
6659
S, I, reg = F.saturation(P)
6660
6661
IR = rings.RealIntervalField(20) # todo: 20?
6662
h = IR(reg-eps,reg+eps)
6663
ind2 = ht/(h/2)
6664
verbose("index squared = %s"%ind2)
6665
ind = ind2.sqrt()
6666
verbose("index = %s"%ind)
6667
# Compute upper bound on square root of index.
6668
if ind.absolute_diameter() < 1:
6669
t, i = ind.is_int()
6670
if t: # unique integer in interval, so we've found exact index squared.
6671
return arith.prime_divisors(i), D, i
6672
raise RuntimeError, "Unable to compute bound for e=%s, D=%s (try increasing precision)"%(self,D)
6673
6674
# First try a quick search, in case we get lucky and find
6675
# a generator.
6676
P = F.point_search(13, rank_bound=1)
6677
P = [x for x in P if x.order() == rings.infinity]
6678
if len(P) > 0:
6679
return _bound(P)
6680
6681
# Do search to eliminate possibility that Heegner point is
6682
# divisible by primes up to p, without finding Heegner point.
6683
P = F.point_search(c, rank_bound=1)
6684
P = [x for x in P if x.order() == rings.infinity]
6685
if len(P) == 0:
6686
# We've eliminated the possibility of a divisor up to p.
6687
return rings.prime_range(3,p), D, False
6688
else:
6689
return _bound(P)
6690
6691
6692
#################################################################################
6693
def _heegner_index_in_EK(self, D):
6694
"""
6695
Return the index of the sum of `E(\QQ)/tor + E^D(\QQ)/tor` in `E(K)/tor`.
6696
6697
INPUT:
6698
- `D` -- negative integer; the Heegner discriminant
6699
6700
OUTPUT:
6701
a power of 2 -- the given index
6702
6703
EXAMPLES:
6704
We compute the index for a rank 2 curve and found that it is 2::
6705
6706
sage: E = EllipticCurve('389a')
6707
sage: E._heegner_index_in_EK(-7)
6708
2
6709
6710
We explicitly verify in the above example that indeed that
6711
index is divisibly by 2 by writing down a generator of
6712
`E(\QQ)/tor + E^D(\QQ)/tor` that is divisible by 2 in `E(K)`::
6713
6714
sage: F = E.quadratic_twist(-7)
6715
sage: K = QuadraticField(-7,'a')
6716
sage: G = E.change_ring(K)
6717
sage: phi = F.change_ring(K).isomorphism_to(G)
6718
sage: P = G(E(-1,1)) + G((0,-1)) + G(phi(F(14,25))); P
6719
(-867/3872*a - 3615/3872 : -18003/170368*a - 374575/170368 : 1)
6720
sage: P.division_points(2)
6721
[(1/8*a + 5/8 : -5/16*a - 9/16 : 1)]
6722
6723
"""
6724
# check conditions, then use cache if possible.
6725
if not self.satisfies_heegner_hypothesis(D):
6726
raise ValueError, "D (=%s) must satisfy the Heegner hypothesis"%D
6727
try:
6728
return self.__heegner_index_in_EK[D]
6729
except AttributeError:
6730
self.__heegner_index_in_EK = {}
6731
except KeyError:
6732
pass
6733
6734
#####################################################################
6735
# THE ALGORITHM:
6736
#
6737
# For an element P of an abelian group A, let [P] denote the
6738
# equivalence class of P in the quotient A/A_tor of A by
6739
# its torsion subgroup. Then for P in E(Q) + E^D(QQ), we
6740
# have that [P] is divisible by 2 in E(K)/tor if and only
6741
# there is R in E(K) such that 2*[R] = [P], and this is
6742
# only if there is R in E(K) and t in E(K)_tor such that
6743
# 2*R = P + t.
6744
#
6745
# Using complex conjugation, one sees that the quotient
6746
# group E(K)/tor / ( E(Q)/tor + E^D(Q)/tor ) is killed by 2.
6747
# So to compute the order of this group we run through
6748
# representatives P for A/(2A) where A = E(Q)/tor + E^D(Q)/tor,
6749
# and for each we see whether there is a torsion point t in E(K)
6750
# such that P + t is divisible by 2. Also, we have
6751
# 2 | P+t <==> 2 | P+n*t for any odd integer n,
6752
# so we may assume t is of 2-power order.
6753
#####################################################################
6754
6755
E = self # nice shortcut
6756
F = E.quadratic_twist(D).minimal_model()
6757
K = rings.QuadraticField(D,'a')
6758
6759
# Define a map phi that we'll use to put the points of E^D(QQ)
6760
# into E(K):
6761
G = E.change_ring(K)
6762
G2 = F.change_ring(K)
6763
phi = G2.isomorphism_to(G)
6764
6765
# Basis for E(Q)/tor oplus E^D(QQ)/tor in E(K):
6766
basis = [G(z) for z in E.gens()] + [G(phi(z)) for z in F.gens()]
6767
# Make a list of the 2-power order torsion points in E(K), including 0.
6768
T = [G(z) for z in G.torsion_subgroup().list() if z.order() == 1 or
6769
((z.order() % 2 == 0 and len(z.order().factor()) == 1))]
6770
6771
r = len(basis) # rank
6772
V = rings.QQ**r
6773
B = []
6774
6775
# Iterate through reps for A/(2*A) creating vectors in (1/2)*ZZ^r
6776
for v in rings.GF(2)**r:
6777
if not v: continue
6778
P = sum([basis[i] for i in range(r) if v[i]])
6779
for t in T:
6780
if (P+t).is_divisible_by(2):
6781
B.append(V(v)/2)
6782
6783
A = rings.ZZ**r
6784
# Take span of our vectors in (1/2)*ZZ^r, along with ZZ^r. This is E(K)/tor.
6785
W = V.span(B,rings.ZZ) + A
6786
6787
# Compute the index in E(K)/tor of A = E(Q)/tor + E^D(Q)/tor, cache, and return.
6788
index = A.index_in(W)
6789
self.__heegner_index_in_EK[D] = index
6790
return index
6791
6792
def heegner_sha_an(self, D, prec=53):
6793
r"""
6794
Return the conjectural (analytic) order of Sha for E over the field `K=\QQ(\sqrt{D})`.
6795
6796
INPUT:
6797
6798
- `D` -- negative integer; the Heegner discriminant
6799
6800
- prec -- integer (default: 53); bits of precision to
6801
compute analytic order of Sha
6802
6803
OUTPUT:
6804
6805
(floating point number) an approximation to the conjectural order of Sha.
6806
6807
.. note::
6808
6809
Often you'll want to do ``proof.elliptic_curve(False)`` when
6810
using this function, since often the twisted elliptic
6811
curves that come up have enormous conductor, and Sha is
6812
nontrivial, which makes provably finding the Mordell-Weil
6813
group using 2-descent difficult.
6814
6815
6816
EXAMPLES:
6817
6818
An example where E has conductor 11::
6819
6820
sage: E = EllipticCurve('11a')
6821
sage: E.heegner_sha_an(-7) # long time
6822
1.00000000000000
6823
6824
The cache works::
6825
6826
sage: E.heegner_sha_an(-7) is E.heegner_sha_an(-7) # long time
6827
True
6828
6829
Lower precision::
6830
6831
sage: E.heegner_sha_an(-7,10) # long time
6832
1.0
6833
6834
Checking that the cache works for any precision::
6835
6836
sage: E.heegner_sha_an(-7,10) is E.heegner_sha_an(-7,10) # long time
6837
True
6838
6839
Next we consider a rank 1 curve with nontrivial Sha over the
6840
quadratic imaginary field `K`; however, there is no Sha for `E`
6841
over `\QQ` or for the quadratic twist of `E`::
6842
6843
sage: E = EllipticCurve('37a')
6844
sage: E.heegner_sha_an(-40) # long time
6845
4.00000000000000
6846
sage: E.quadratic_twist(-40).sha().an() # long time
6847
1
6848
sage: E.sha().an() # long time
6849
1
6850
6851
A rank 2 curve::
6852
6853
sage: E = EllipticCurve('389a') # long time
6854
sage: E.heegner_sha_an(-7) # long time
6855
1.00000000000000
6856
6857
If we remove the hypothesis that `E(K)` has rank 1 in Conjecture
6858
2.3 in [Gross-Zagier, 1986, page 311], then that conjecture is
6859
false, as the following example shows::
6860
6861
sage: E = EllipticCurve('65a') # long time
6862
sage: E.heegner_sha_an(-56) # long time
6863
1.00000000000000
6864
sage: E.torsion_order() # long time
6865
2
6866
sage: E.tamagawa_product() # long time
6867
1
6868
sage: E.quadratic_twist(-56).rank() # long time
6869
2
6870
"""
6871
# check conditions, then return from cache if possible.
6872
if not self.satisfies_heegner_hypothesis(D):
6873
raise ValueError, "D (=%s) must satisfy the Heegner hypothesis"%D
6874
try:
6875
return self.__heegner_sha_an[(D,prec)]
6876
except AttributeError:
6877
self.__heegner_sha_an = {}
6878
except KeyError:
6879
pass
6880
6881
# Use the BSD conjecture over the quadratic imaginary K --
6882
# see page 311 of [Gross-Zagier, 1986] for the formula.
6883
E = self # notational convenience
6884
F = E.quadratic_twist(D).minimal_model()
6885
K = rings.QuadraticField(D,'a')
6886
6887
# Compute each of the quantities in BSD
6888
# - The torsion subgroup over K.
6889
T = E.change_ring(K).torsion_order()
6890
6891
# - The product of the Tamagawa numbers, which because D is
6892
# coprime to N is just the square of the product of the
6893
# Tamagawa numbers over QQ for E. (we square below in the
6894
# BSD formula)
6895
cqprod = E.tamagawa_product()
6896
6897
# - The leading term of the L-series, as a product of two
6898
# other L-series.
6899
rE = E.rank()
6900
rF = F.rank()
6901
L_E = E.lseries().dokchitser(prec).derivative(1, rE)
6902
L_F = F.lseries().dokchitser(prec).derivative(1, rF)
6903
# NOTE: The binomial coefficient in the following formula
6904
# for the leading term in terms of the other two leading
6905
# terms comes from the product rule for the derivative.
6906
# You can think this through or just type something like
6907
# f = function('f',x); g = function('g',x); diff(f*g,6)
6908
# into Sage to be convinced.
6909
L = rings.binomial(rE + rF, rE) * (L_E * L_F / (rings.factorial(rE+rF)) )
6910
6911
# - ||omega||^2 -- the period. It's twice the volume of the
6912
# period lattice. See the following paper for a derivation:
6913
# "Verification of the Birch and Swinnerton-Dyer Conjecture
6914
# for Specific Elliptic Curves", G. Grigorov, A. Jorza, S. Patrikis,
6915
# C. Patrascu, W. Stein
6916
omega = 2 * abs(E.period_lattice().basis_matrix().det())
6917
6918
# - The regulator.
6919
# First we compute the regualtor of the subgroup E(QQ) + E^D(QQ)
6920
# of E(K). The factor of 2 in the regulator
6921
# accounts for the fact that the height over K is twice the
6922
# height over QQ, i.e., for P in E(QQ) we have h_K(P,P) =
6923
# 2*h_Q(P,P). See, e.g., equation (6.4) on page 230 of
6924
# [Gross-Zagier, 1986].
6925
Reg_prod = 2**(rE + rF) * E.regulator(precision=prec) * F.regulator(precision=prec)
6926
# Next we call off to the _heegner_index_in_EK function, which
6927
# saturates the group E(QQ) + E^D(QQ) in E(K), given us the index,
6928
# which must be a power of 2, since E(QQ) is the +1 eigenspace for
6929
# complex conjugation, and E^D(QQ) is the -1 eigenspace.
6930
ind = self._heegner_index_in_EK(D)
6931
# Finally, we know the regulator of E(K).
6932
Reg = Reg_prod / ind**2
6933
6934
# - Square root of the absolute value of the discriminant. This is
6935
# easy; we just make sure the D passed in is an integer, so we
6936
# can call sqrt with the chosen precision.
6937
sqrtD = ZZ(abs(D)).sqrt(prec=prec)
6938
6939
# - Done: Finally, we plug everything into the BSD formula to get the
6940
# analytic order of Sha.
6941
sha_an = (L * T**2 * sqrtD) / (omega * Reg * cqprod**2)
6942
6943
# - We cache and return the answer.
6944
self.__heegner_sha_an[(D,prec)] = sha_an
6945
return sha_an
6946
6947
def _heegner_forms_list(self, D, beta=None, expected_count=None):
6948
"""
6949
Returns a list of quadratic forms corresponding to Heegner points
6950
with discriminant `D` and a choice of `\beta` a square root of
6951
`D` mod `4N`. Specifically, given a quadratic form
6952
`f = Ax^2 + Bxy + Cy^2` we let `\tau_f` be a root of `Ax^2 + Bx + C`
6953
and the discriminant `\Delta(\tau_f) = \Delta(f) = D` must be
6954
invariant under multiplication by `N`, the conductor of self.
6955
6956
`\Delta(N\tau_f) = \Delta(\tau_f) = \Delta(f) = D`
6957
6958
EXAMPLES::
6959
6960
sage: E = EllipticCurve('37a')
6961
sage: E._heegner_forms_list(-7)
6962
[37*x^2 + 17*x*y + 2*y^2]
6963
sage: E._heegner_forms_list(-195)
6964
[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]
6965
sage: E._heegner_forms_list(-195)[-1].discriminant()
6966
-195
6967
sage: len(E._heegner_forms_list(-195))
6968
4
6969
sage: QQ[sqrt(-195)].class_number()
6970
4
6971
6972
sage: E = EllipticCurve('389a')
6973
sage: E._heegner_forms_list(-7)
6974
[389*x^2 + 185*x*y + 22*y^2]
6975
sage: E._heegner_forms_list(-59)
6976
[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]
6977
"""
6978
if expected_count is None:
6979
expected_count = number_field.QuadraticField(D, 'a').class_number()
6980
N = self.conductor()
6981
if beta is None:
6982
beta = Integers(4*N)(D).sqrt(extend=False)
6983
else:
6984
assert beta**2 == Integers(4*N)(D)
6985
from sage.quadratic_forms.all import BinaryQF
6986
b = ZZ(beta) % (2*N)
6987
all = []
6988
seen = []
6989
# Note: This may give a sub-optimal list of forms.
6990
while True:
6991
R = (b**2-D)//(4*N)
6992
for d in R.divisors():
6993
f = BinaryQF([d*N, b, R//d])
6994
fr = f.reduced_form()
6995
if fr not in seen:
6996
seen.append(fr)
6997
all.append(f)
6998
if len(all) == expected_count:
6999
return all
7000
b += 2*N
7001
7002
def _heegner_best_tau(self, D, prec=None):
7003
r"""
7004
Given a discrimanent `D`, find the Heegner point `\tau` in the
7005
upper half plane with largest imaginary part (which is optimal
7006
for evaluating the modular parametrization). If the optional
7007
parameter ``prec`` is given, return `\tau` to ``prec`` bits of
7008
precision, otherwise return it exactly as a symbolic object.
7009
7010
EXAMPLES::
7011
7012
sage: E = EllipticCurve('37a')
7013
sage: E._heegner_best_tau(-7)
7014
1/74*sqrt(-7) - 17/74
7015
sage: EllipticCurve('389a')._heegner_best_tau(-11)
7016
1/778*sqrt(-11) - 355/778
7017
sage: EllipticCurve('389a')._heegner_best_tau(-11, prec=100)
7018
-0.45629820051413881748071979434 + 0.0042630138693514136878083968338*I
7019
"""
7020
# We know that N|A, so A = N is optimal.
7021
N = self.conductor()
7022
b = ZZ(Integers(4*N)(D).sqrt(extend=False) % (2*N))
7023
# TODO: make sure a different choice of b isn't better?
7024
return (-b + ZZ(D).sqrt(prec=prec)) / (2*N)
7025
7026
def satisfies_heegner_hypothesis(self, D):
7027
"""
7028
Returns True precisely when `D` is a fundamental discriminant that
7029
satisfies the Heegner hypothesis for this elliptic curve.
7030
7031
EXAMPLES::
7032
7033
sage: E = EllipticCurve('11a1')
7034
sage: E.satisfies_heegner_hypothesis(-7)
7035
True
7036
sage: E.satisfies_heegner_hypothesis(-11)
7037
False
7038
"""
7039
if not number_field.is_fundamental_discriminant(D):
7040
return False
7041
D = ZZ(D)
7042
if D >= 0: return False
7043
if D.gcd(self.conductor()) != 1:
7044
return False
7045
for p, _ in self.conductor().factor():
7046
if D.kronecker(p) != 1:
7047
return False
7048
return True
7049
7050
7051
#####################################################################
7052
# End of elliptic curve methods.
7053
#####################################################################
7054
7055