-
-
Notifications
You must be signed in to change notification settings - Fork 405
/
heegner.py
7164 lines (5755 loc) · 245 KB
/
heegner.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# -*- coding: utf-8 -*-
r"""
Heegner points on elliptic curves over the rational numbers
AUTHORS:
- William Stein (August 2009)-- most of the initial version
- Robert Bradshaw (July 2009) -- an early version of some specific code
EXAMPLES::
sage: E = EllipticCurve('433a')
sage: P = E.heegner_point(-8,3)
sage: z = P.point_exact(201); z
(-4/3 : 1/27*a - 4/27 : 1)
sage: parent(z)
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
sage: parent(z[0]).discriminant()
-3
sage: E.quadratic_twist(-3).rank()
1
sage: K.<a> = QuadraticField(-8)
sage: K.factor(3)
(Fractional ideal (1/2*a + 1)) * (Fractional ideal (-1/2*a + 1))
Next try an inert prime::
sage: K.factor(5)
Fractional ideal (5)
sage: P = E.heegner_point(-8,5)
sage: z = P.point_exact(300)
sage: z[0].charpoly().factor()
(x^6 + x^5 - 1/4*x^4 + 19/10*x^3 + 31/20*x^2 - 7/10*x + 49/100)^2
sage: z[1].charpoly().factor()
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
sage: f = P.x_poly_exact(300); f
x^6 + x^5 - 1/4*x^4 + 19/10*x^3 + 31/20*x^2 - 7/10*x + 49/100
sage: f.discriminant().factor()
-1 * 2^-9 * 5^-9 * 7^2 * 281^2 * 1021^2
We find some Mordell-Weil generators in the rank 1 case using Heegner points::
sage: E = EllipticCurve('43a'); P = E.heegner_point(-7)
sage: P.x_poly_exact()
x
sage: P.point_exact()
(0 : 0 : 1)
sage: E = EllipticCurve('997a')
sage: E.rank()
1
sage: E.heegner_discriminants_list(10)
[-19, -23, -31, -35, -39, -40, -52, -55, -56, -59]
sage: P = E.heegner_point(-19)
sage: P.x_poly_exact()
x - 141/49
sage: P.point_exact()
(141/49 : -162/343 : 1)
Here we find that the Heegner point generates a subgroup of index 3::
sage: E = EllipticCurve('92b1')
sage: E.heegner_discriminants_list(1)
[-7]
sage: P = E.heegner_point(-7); z = P.point_exact(); z
(0 : 1 : 1)
sage: E.regulator()
0.0498083972980648
sage: z.height()
0.448275575682583
sage: P = E(1,1); P # a generator
(1 : 1 : 1)
sage: -3*P
(0 : 1 : 1)
sage: E.tamagawa_product()
3
The above is consistent with the following analytic computation::
sage: E.heegner_index(-7)
3.0000?
"""
#*****************************************************************************
# Copyright (C) 2005-2009 William Stein <wstein@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# http://www.gnu.org/licenses/
#*****************************************************************************
from __future__ import print_function, absolute_import
from six.moves import range
from sage.misc.all import verbose, prod
from sage.misc.cachefunc import cached_method
from sage.structure.sage_object import SageObject
from sage.structure.richcmp import (richcmp_method, richcmp,
richcmp_not_equal, rich_to_bool)
import sage.rings.number_field.number_field_element
import sage.rings.number_field.number_field as number_field
import sage.rings.all as rings
from sage.rings.all import (ZZ, GF, QQ, CDF,
Integers, RealField, ComplexField, QuadraticField,
is_fundamental_discriminant)
from sage.arith.all import (gcd, xgcd, lcm, prime_divisors, factorial,
binomial)
from sage.quadratic_forms.all import (BinaryQF,
BinaryQF_reduced_representatives)
from sage.matrix.all import MatrixSpace, matrix
from sage.modular.modsym.p1list import P1List
##################################################################################
#
# The exported functions, which are in most cases enough to get the
# user going working with Heegner points:
#
# heegner_points -- all of them with given level, discriminant, conducto
# heegner_point -- a specific one
#
##################################################################################
def heegner_points(N, D=None, c=None):
"""
Return all Heegner points of given level `N`. Can also restrict
to Heegner points with specified discriminant `D` and optionally
conductor `c`.
INPUT:
- `N` -- level (positive integer)
- `D` -- discriminant (negative integer)
- `c` -- conductor (positive integer)
EXAMPLES::
sage: heegner_points(389,-7)
Set of all Heegner points on X_0(389) associated to QQ[sqrt(-7)]
sage: heegner_points(389,-7,1)
All Heegner points of conductor 1 on X_0(389) associated to QQ[sqrt(-7)]
sage: heegner_points(389,-7,5)
All Heegner points of conductor 5 on X_0(389) associated to QQ[sqrt(-7)]
"""
if D is None and c is None:
return HeegnerPoints_level(N)
if D is not None and c is None:
return HeegnerPoints_level_disc(N, D)
if D is not None and c is not None:
return HeegnerPoints_level_disc_cond(N,D,c)
raise TypeError
def heegner_point(N, D=None, c=1):
"""
Return a specific Heegner point of level `N` with given
discriminant and conductor. If `D` is not specified, then the
first valid Heegner discriminant is used. If `c` is not given,
then `c=1` is used.
INPUT:
- `N` -- level (positive integer)
- `D` -- discriminant (optional: default first valid `D`)
- `c` -- conductor (positive integer, optional, default: 1)
EXAMPLES::
sage: heegner_point(389)
Heegner point 1/778*sqrt(-7) - 185/778 of discriminant -7 on X_0(389)
sage: heegner_point(389,-7)
Heegner point 1/778*sqrt(-7) - 185/778 of discriminant -7 on X_0(389)
sage: heegner_point(389,-7,5)
Heegner point 5/778*sqrt(-7) - 147/778 of discriminant -7 and conductor 5 on X_0(389)
sage: heegner_point(389,-20)
Heegner point 1/778*sqrt(-20) - 165/389 of discriminant -20 on X_0(389)
"""
if D is not None:
return heegner_points(N,D,c)[0]
H = heegner_points(N)
D = H.discriminants(1)[0]
return heegner_points(N,D,c)[0]
##################################################################################
#
# Ring class fields, represented as abstract objects. These do not
# derive from number fields, since we do not need to work with their
# elements, and explicitly representing them as number fields would be
# far too difficult.
#
##################################################################################
class RingClassField(SageObject):
"""
A Ring class field of a quadratic imaginary field of given conductor.
.. NOTE::
This is a *ring* class field, not a ray class field. In
general, the ring class field of given conductor is a subfield
of the ray class field of the same conductor.
EXAMPLES::
sage: heegner_point(37,-7).ring_class_field()
Hilbert class field of QQ[sqrt(-7)]
sage: heegner_point(37,-7,5).ring_class_field()
Ring class field extension of QQ[sqrt(-7)] of conductor 5
sage: heegner_point(37,-7,55).ring_class_field()
Ring class field extension of QQ[sqrt(-7)] of conductor 55
TESTS::
sage: K_c = heegner_point(37,-7).ring_class_field()
sage: type(K_c)
<class 'sage.schemes.elliptic_curves.heegner.RingClassField'>
sage: loads(dumps(K_c)) == K_c
True
"""
def __init__(self, D, c, check=True):
"""
INPUT:
- `D` -- discriminant of quadratic imaginary field
- `c` -- conductor (positive integer coprime to `D`)
- ``check`` -- bool (default: ``True``); whether to check
validity of input
EXAMPLES::
sage: sage.schemes.elliptic_curves.heegner.RingClassField(-7,5, False)
Ring class field extension of QQ[sqrt(-7)] of conductor 5
"""
if check:
D = ZZ(D); c = ZZ(c)
self.__D = D
self.__c = c
def __eq__(self, other):
"""
Used for equality testing.
EXAMPLES::
sage: E = EllipticCurve('389a')
sage: K5 = E.heegner_point(-7,5).ring_class_field()
sage: K11 = E.heegner_point(-7,11).ring_class_field()
sage: K5 == K11
False
sage: K5 == K5
True
sage: K11 == 11
False
"""
return isinstance(other, RingClassField) and self.__D == other.__D and self.__c == other.__c
def __ne__(self, other):
"""
Check whether ``self`` is not equal to ``other``.
EXAMPLES::
sage: E = EllipticCurve('389a')
sage: K5 = E.heegner_point(-7,5).ring_class_field()
sage: K11 = E.heegner_point(-7,11).ring_class_field()
sage: K5 != K11
True
sage: K5 != K5
False
sage: K11 != 11
True
"""
return not (self == other)
def __hash__(self):
"""
Used for computing hash of ``self``.
.. NOTE::
The hash is equal to the hash of the pair
``(discriminant, conductor)``.
EXAMPLES::
sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()
sage: hash(K5) == hash((-7,5))
True
"""
return hash((self.__D, self.__c))
def conductor(self):
"""
Return the conductor of this ring class field.
EXAMPLES::
sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()
sage: K5.conductor()
5
"""
return self.__c
def discriminant_of_K(self):
"""
Return the discriminant of the quadratic imaginary field `K` contained in ``self``.
EXAMPLES::
sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()
sage: K5.discriminant_of_K()
-7
"""
return self.__D
@cached_method
def ramified_primes(self):
r"""
Return the primes of `\ZZ` that ramify in this ring class field.
EXAMPLES::
sage: E = EllipticCurve('389a'); K55 = E.heegner_point(-7,55).ring_class_field()
sage: K55.ramified_primes()
[5, 7, 11]
sage: E.heegner_point(-7).ring_class_field().ramified_primes()
[7]
"""
return prime_divisors(self.__D * self.__c)
def _repr_(self):
"""
EXAMPLES::
sage: heegner_point(37,-7,55).ring_class_field()._repr_()
'Ring class field extension of QQ[sqrt(-7)] of conductor 55'
sage: heegner_point(37,-7).ring_class_field()._repr_()
'Hilbert class field of QQ[sqrt(-7)]'
"""
c = self.__c
if c == 1:
return "Hilbert class field of QQ[sqrt(%s)]"%self.__D
else:
return "Ring class field extension of QQ[sqrt(%s)] of conductor %s"%(self.__D, self.__c)
@cached_method
def degree_over_K(self):
"""
Return the relative degree of this ring class field over the
quadratic imaginary field `K`.
EXAMPLES::
sage: E = EllipticCurve('389a'); P = E.heegner_point(-7,5)
sage: K5 = P.ring_class_field(); K5
Ring class field extension of QQ[sqrt(-7)] of conductor 5
sage: K5.degree_over_K()
6
sage: type(K5.degree_over_K())
<type 'sage.rings.integer.Integer'>
sage: E = EllipticCurve('389a'); E.heegner_point(-20).ring_class_field().degree_over_K()
2
sage: E.heegner_point(-20,3).ring_class_field().degree_over_K()
4
sage: kronecker(-20,11)
-1
sage: E.heegner_point(-20,11).ring_class_field().degree_over_K()
24
"""
K = self.quadratic_field()
# Multiply class number by relative degree of the Hilbert class field H over K.
return K.class_number() * self.degree_over_H()
@cached_method
def degree_over_H(self):
"""
Return the degree of this field over the Hilbert class field `H` of `K`.
EXAMPLES::
sage: E = EllipticCurve('389a')
sage: E.heegner_point(-59).ring_class_field().degree_over_H()
1
sage: E.heegner_point(-59).ring_class_field().degree_over_K()
3
sage: QuadraticField(-59,'a').class_number()
3
Some examples in which prime dividing c is inert::
sage: heegner_point(37,-7,3).ring_class_field().degree_over_H()
4
sage: heegner_point(37,-7,3^2).ring_class_field().degree_over_H()
12
sage: heegner_point(37,-7,3^3).ring_class_field().degree_over_H()
36
The prime dividing c is split. For example, in the first case
`O_K/cO_K` is isomorphic to a direct sum of two copies of
``GF(2)``, so the units are trivial::
sage: heegner_point(37,-7,2).ring_class_field().degree_over_H()
1
sage: heegner_point(37,-7,4).ring_class_field().degree_over_H()
2
sage: heegner_point(37,-7,8).ring_class_field().degree_over_H()
4
Now c is ramified::
sage: heegner_point(37,-7,7).ring_class_field().degree_over_H()
7
sage: heegner_point(37,-7,7^2).ring_class_field().degree_over_H()
49
Check that :trac:`15218` is solved::
sage: E = EllipticCurve("19a");
sage: s = E.heegner_point(-3,2).ring_class_field().galois_group().complex_conjugation()
sage: H = s.domain(); H.absolute_degree()
2
"""
c = self.__c
if c == 1:
return ZZ(1)
# Let K_c be the ring class field. We have by class field theory that
# Gal(K_c / H) = (O_K / c O_K)^* / ((Z/cZ)^* M),
# where M is the image of the roots of unity of K in (O_K / c O_K)^*.
#
# To compute the cardinality of the above Galois group, we
# first reduce to the case that c = p^e is a prime power
# (since the expression is multiplicative in c).
# Of course, note also that #(Z/cZ)^* = phi(c)
#
# Case 1: p splits in O_K. Then
# #(O_K/p^e*O_K)^* = (#(Z/p^eZ)^*)^2 = phi(p^e)^2, so
# #(O_K/p^e*O_K)^*/(Z/p^eZ)^* = phi(p^e) = p^e - p^(e-1)
#
# Case 2: p is inert in O_K. Then
# #(O_K/p^e O_K)^* = p^(2*e)-p^(2*(e-1))
# so #(O_K/p^e*O_K)^*/(Z/p^eZ)^*
# = (p^(2*e)-p^(2*(e-1)))/(p^e-p^(e-1)) = p^e + p^(e-1).
#
# Case 3: p ramified in O_K. Then
# #(O_K/p^e O_K)^* = p^(2*e) - p^(2*e-1),
# so #(O_K/p^e O_K)^*/#(Z/p^eZ)^* = p^e.
#
# Section 4.2 of Cohen's "Advanced Computational Algebraic
# Number Theory" GTM is also relevant, though Cohen is working
# with *ray* class fields and here we want the cardinality
# of the *ring* class field, which is a subfield.
K = self.quadratic_field()
n = ZZ(1)
for p, e in c.factor():
F = K.factor(p)
if len(F) == 2:
# split case
n *= p**e - p**(e-1)
else:
if F[0][1] > 1:
# ramified case
n *= p**e
else:
# inert case
n *= p**e + p**(e-1)
return (n * ZZ(2)) // K.number_of_roots_of_unity()
@cached_method
def absolute_degree(self):
r"""
Return the absolute degree of this field over `\QQ`.
EXAMPLES::
sage: E = EllipticCurve('389a'); K = E.heegner_point(-7,5).ring_class_field()
sage: K.absolute_degree()
12
sage: K.degree_over_K()
6
"""
return 2*self.degree_over_K()
degree_over_Q = absolute_degree
@cached_method
def quadratic_field(self):
r"""
Return the quadratic imaginary field `K = \QQ(\sqrt{D})`.
EXAMPLES::
sage: E = EllipticCurve('389a'); K = E.heegner_point(-7,5).ring_class_field()
sage: K.quadratic_field()
Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
"""
D = self.__D
var = 'sqrt_minus_%s'%(-D)
return number_field.QuadraticField(D,var)
@cached_method
def galois_group(self, base=QQ):
"""
Return the Galois group of ``self`` over base.
INPUT:
- ``base`` -- (default: `\QQ`) a subfield of ``self`` or `\QQ`
EXAMPLES::
sage: E = EllipticCurve('389a')
sage: A = E.heegner_point(-7,5).ring_class_field()
sage: A.galois_group()
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5
sage: B = E.heegner_point(-7).ring_class_field()
sage: C = E.heegner_point(-7,15).ring_class_field()
sage: A.galois_group()
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5
sage: A.galois_group(B)
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Hilbert class field of QQ[sqrt(-7)]
sage: A.galois_group().cardinality()
12
sage: A.galois_group(B).cardinality()
6
sage: C.galois_group(A)
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
sage: C.galois_group(A).cardinality()
4
"""
return GaloisGroup(self, base)
def is_subfield(self, M):
"""
Return ``True`` if this ring class field is a subfield of the ring class field `M`.
If `M` is not a ring class field, then a TypeError is raised.
EXAMPLES::
sage: E = EllipticCurve('389a')
sage: A = E.heegner_point(-7,5).ring_class_field()
sage: B = E.heegner_point(-7).ring_class_field()
sage: C = E.heegner_point(-20).ring_class_field()
sage: D = E.heegner_point(-7,15).ring_class_field()
sage: B.is_subfield(A)
True
sage: B.is_subfield(B)
True
sage: B.is_subfield(D)
True
sage: B.is_subfield(C)
False
sage: A.is_subfield(B)
False
sage: A.is_subfield(D)
True
"""
if not isinstance(M, RingClassField):
raise TypeError("M must be a ring class field")
return self.quadratic_field() == M.quadratic_field() and \
M.conductor() % self.conductor() == 0
##################################################################################
#
# Galois groups of ring class fields
#
##################################################################################
class GaloisGroup(SageObject):
"""
A Galois group of a ring class field.
EXAMPLES::
sage: E = EllipticCurve('389a')
sage: G = E.heegner_point(-7,5).ring_class_field().galois_group(); G
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5
sage: G.field()
Ring class field extension of QQ[sqrt(-7)] of conductor 5
sage: G.cardinality()
12
sage: G.complex_conjugation()
Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 5
TESTS::
sage: G = heegner_point(37,-7).ring_class_field().galois_group()
sage: loads(dumps(G)) == G
True
sage: type(G)
<class 'sage.schemes.elliptic_curves.heegner.GaloisGroup'>
"""
def __init__(self, field, base=QQ):
r"""
INPUT:
- ``field`` -- a ring class field
- ``base`` -- subfield of field (default: `\QQ`)
EXAMPLES::
sage: K5 = heegner_points(389,-7,5).ring_class_field()
sage: K1 = heegner_points(389,-7,1).ring_class_field()
sage: sage.schemes.elliptic_curves.heegner.GaloisGroup(K5,K1)
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Hilbert class field of QQ[sqrt(-7)]
sage: K5.galois_group(K1)
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Hilbert class field of QQ[sqrt(-7)]
"""
if not isinstance(field, RingClassField):
raise TypeError("field must be of type RingClassField")
if base != QQ and base != field.quadratic_field():
if not isinstance(base, RingClassField):
raise TypeError("base must be of type RingClassField or QQ or quadratic field")
if not base.is_subfield(field):
raise TypeError("base must be a subfield of field")
self.__field = field
self.__base = base
def __eq__(self, G):
"""
EXAMPLES::
sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()
sage: G == G
True
sage: G == 0
False
sage: H = EllipticCurve('389a').heegner_point(-7,11).ring_class_field().galois_group()
sage: G == H
False
"""
return isinstance(G, GaloisGroup) and (G.__field,G.__base) == (self.__field,self.__base)
def __ne__(self, other):
"""
EXAMPLES::
sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()
sage: G != G
False
sage: G != 0
True
sage: H = EllipticCurve('389a').heegner_point(-7,11).ring_class_field().galois_group()
sage: G != H
True
"""
return not (self == other)
def __hash__(self):
"""
Return hash of this Galois group, which is the same as the
hash of the pair, the field and its base.
EXAMPLES::
sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()
sage: hash(G) == hash((G.field(), G.base_field()))
True
"""
return hash((self.__field, self.__base))
def __call__(self, x):
"""
Coerce `x` into ``self``, where `x` is a Galois group element, or
in case ``self`` has base field the Hilbert class field, `x` can
also be an element of the ring of integers.
INPUT:
- `x` -- automorphism or quadratic field element
OUTPUT:
- automorphism (or TypeError)
EXAMPLES::
sage: K5 = heegner_points(389,-52,5).ring_class_field()
sage: K1 = heegner_points(389,-52,1).ring_class_field()
sage: G = K5.galois_group(K1)
sage: G(1)
Class field automorphism defined by x^2 + 325*y^2
sage: G(G[0])
Class field automorphism defined by x^2 + 325*y^2
sage: alpha = 2 + K1.quadratic_field().gen(); alpha
sqrt_minus_52 + 2
sage: G(alpha)
Class field automorphism defined by 14*x^2 - 10*x*y + 25*y^2
A TypeError is raised when the coercion is not possible::
sage: G(0)
Traceback (most recent call last):
...
TypeError: x does not define element of (O_K/c*O_K)^*
"""
if isinstance(x, GaloisAutomorphism) and x.parent() == self:
return x
try:
return self._alpha_to_automorphism(x)
except (ZeroDivisionError, TypeError):
raise TypeError("x does not define element of (O_K/c*O_K)^*")
def _repr_(self):
"""
Return string representation of this Galois group.
EXAMPLES::
sage: E = EllipticCurve('389a')
sage: G = E.heegner_point(-7,5).ring_class_field().galois_group()
sage: G._repr_()
'Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5'
"""
if self.base_field() != QQ:
s = " over %s"%self.base_field()
else:
s = ''
return "Galois group of %s%s"%(self.field(), s)
def field(self):
"""
Return the ring class field that this Galois group acts on.
EXAMPLES::
sage: G = heegner_point(389,-7,5).ring_class_field().galois_group()
sage: G.field()
Ring class field extension of QQ[sqrt(-7)] of conductor 5
"""
return self.__field
def base_field(self):
"""
Return the base field, which the field fixed by all the
automorphisms in this Galois group.
EXAMPLES::
sage: x = heegner_point(37,-7,5)
sage: Kc = x.ring_class_field(); Kc
Ring class field extension of QQ[sqrt(-7)] of conductor 5
sage: K = x.quadratic_field()
sage: G = Kc.galois_group(); G
Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5
sage: G.base_field()
Rational Field
sage: G.cardinality()
12
sage: Kc.absolute_degree()
12
sage: G = Kc.galois_group(K); G
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
sage: G.cardinality()
6
sage: G.base_field()
Number Field in sqrt_minus_7 with defining polynomial x^2 + 7
sage: G = Kc.galois_group(Kc); G
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
sage: G.cardinality()
1
sage: G.base_field()
Ring class field extension of QQ[sqrt(-7)] of conductor 5
"""
return self.__base
@cached_method
def kolyvagin_generators(self):
r"""
Assuming this Galois group `G` is of the form
`G=\textrm{Gal}(K_c/K_1)`, with `c=p_1\dots p_n` satisfying the
Kolyvagin hypothesis, this function returns noncanonical
choices of lifts of generators for each of the cyclic factors
of `G` corresponding to the primes dividing `c`. Thus the
`i`-th returned valued is an element of `G` that maps to the
identity element of `\textrm{Gal}(K_p/K_1)` for all `p \neq p_i` and
to a choice of generator of `\textrm{Gal}(K_{p_i}/K_1)`.
OUTPUT:
- list of elements of ``self``
EXAMPLES::
sage: K3 = heegner_points(389,-52,3).ring_class_field()
sage: K1 = heegner_points(389,-52,1).ring_class_field()
sage: G = K3.galois_group(K1)
sage: G.kolyvagin_generators()
(Class field automorphism defined by 9*x^2 - 6*x*y + 14*y^2,)
sage: K5 = heegner_points(389,-52,5).ring_class_field()
sage: K1 = heegner_points(389,-52,1).ring_class_field()
sage: G = K5.galois_group(K1)
sage: G.kolyvagin_generators()
(Class field automorphism defined by 17*x^2 - 14*x*y + 22*y^2,)
"""
M = self.field()
c = M.conductor()
if not (self._base_is_hilbert_class_field() and self.is_kolyvagin()):
raise ValueError("field must be of the form Gal(K_c/K_1)")
if not c.is_prime():
raise NotImplementedError("only implemented when c is prime")
# Since c satisfies Kolyvagin and is prime, the group is cyclic,
# so we just find a generator.
for sigma in self:
if sigma.order() == self.cardinality():
return tuple([sigma])
raise NotImplementedError
@cached_method
def lift_of_hilbert_class_field_galois_group(self):
r"""
Assuming this Galois group `G` is of the form `G=\textrm{Gal}(K_c/K)`,
this function returns noncanonical choices of lifts of the
elements of the quotient group `\textrm{Gal}(K_1/K)`.
OUTPUT:
- tuple of elements of self
EXAMPLES::
sage: K5 = heegner_points(389,-52,5).ring_class_field()
sage: G = K5.galois_group(K5.quadratic_field())
sage: G.lift_of_hilbert_class_field_galois_group()
(Class field automorphism defined by x^2 + 325*y^2, Class field automorphism defined by 2*x^2 + 2*x*y + 163*y^2)
sage: G.cardinality()
12
sage: K5.quadratic_field().class_number()
2
"""
if not self._base_is_quad_imag_field():
raise ValueError("Galois group must be of the form Gal(K_c/K)")
K = self.base_field()
C = K.class_group()
v = []
lifts = []
for sigma in self:
I = sigma.ideal()
g = C(I)
if g not in v:
v.append(g)
lifts.append(sigma)
return tuple(lifts)
@cached_method
def _list(self):
r"""
Enumerate the elements of ``self``.
EXAMPLES::
Example with order 1 (a special case)::
sage: E = EllipticCurve('389a'); F= E.heegner_point(-7,1).ring_class_field()
sage: G = F.galois_group(F.quadratic_field())
sage: G._list()
(Class field automorphism defined by x^2 + x*y + 2*y^2,)
Example over quadratic imaginary field::
sage: E = EllipticCurve('389a'); F= E.heegner_point(-7,5).ring_class_field()
sage: G = F.galois_group(F.quadratic_field())
sage: G._list()
(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)
Example over `\QQ` (it is not implemented yet)::
sage: K3 = heegner_points(389,-52,3).ring_class_field()
sage: K3.galois_group()._list()
Traceback (most recent call last):
...
NotImplementedError: Galois group over QQ not yet implemented
Example over Hilbert class field::
sage: K3 = heegner_points(389,-52,3).ring_class_field(); K1 = heegner_points(389,-52,1).ring_class_field()
sage: G = K3.galois_group(K1)
sage: G._list()
(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)
"""
if self._base_is_QQ():
raise NotImplementedError("Galois group over QQ not yet implemented")
elif self._base_is_quad_imag_field():
# Over the quadratic imaginary field, so straightforward
# enumeration of all reduced primitive binary quadratic
# forms of discriminant D*c^2.
D = self.base_field().discriminant()
c = self.field().conductor()
Q = [f for f in BinaryQF_reduced_representatives(D*c*c) if f.is_primitive()]
v = [GaloisAutomorphismQuadraticForm(self, f) for f in Q]
elif self._base_is_hilbert_class_field() and self.is_kolyvagin():
# Take only the automorphisms in the quad imag case that map to
# a principal ideal.
M = self.field()
K = M.quadratic_field()
v = []
self.__p1_to_automorphism = {}
for sigma in M.galois_group(K)._list():
I = sigma.ideal()
if I.is_principal():
# sigma does define an element of our Galois subgroup.
alpha = sigma.ideal().gens_reduced()[0]
t = GaloisAutomorphismQuadraticForm(self, sigma.quadratic_form(), alpha=alpha)
self.__p1_to_automorphism[t.p1_element()] = t
v.append(t)
else:
raise NotImplementedError("general Galois group not yet implemented")
v.sort()
assert len(v) == self.cardinality(), "bug enumerating Galois group elements"
return tuple(v)
def _quadratic_form_to_alpha(self, f):
"""
INPUT:
- `f` -- a binary quadratic form with discriminant `c^2 D`
OUTPUT:
- an element of the ring of integers of the quadratic
imaginary field
EXAMPLES::
sage: K3 = heegner_points(389,-52,3).ring_class_field(); K1 = heegner_points(389,-52,1).ring_class_field()
sage: G = K3.galois_group(K1)
sage: [G._quadratic_form_to_alpha(s.quadratic_form()) for s in G]
[3/2*sqrt_minus_52, 1/6*sqrt_minus_52 + 1/3, 1/6*sqrt_minus_52, 1/6*sqrt_minus_52 - 1/3]
What happens when we input a quadratic form that has nothing
to do with `G`::
sage: G._quadratic_form_to_alpha(BinaryQF([1,2,3]))
Traceback (most recent call last):
...
ValueError: quadratic form has the wrong discriminant
"""
A,B,C = f
K = self.field().quadratic_field()
if f.discriminant() != self.field().conductor()**2 * K.discriminant():
raise ValueError("quadratic form has the wrong discriminant")
R = K['X']
v = R([C,B,A]).roots()[0][0]
return v
def _alpha_to_automorphism(self, alpha):
r"""
Assuming ``self`` has base field the Hilbert class field, make an
automorphism from the element `\alpha` of the ring of integers
into ``self``.
INPUT:
- `\alpha` -- element of quadratic imaginary field coprime to conductor
EXAMPLES::
sage: K3 = heegner_points(389,-52,3).ring_class_field()
sage: K1 = heegner_points(389,-52,1).ring_class_field()
sage: G = K3.galois_group(K1)
sage: G._alpha_to_automorphism(1)
Class field automorphism defined by x^2 + 117*y^2
sage: [G._alpha_to_automorphism(s.alpha()) for s in G] == list(G)
True
"""
if not self._base_is_hilbert_class_field() and self.is_kolyvagin():
raise TypeError("base must be Hilbert class field with Kolyvagin condition on conductor")
R = self.field().quadratic_field().maximal_order()
uv = self._alpha_to_p1_element(R(alpha))
try:
d = self.__p1_to_automorphism
except AttributeError:
self._list() # computes attribute as side-effect
d = self.__p1_to_automorphism