Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/modular/cusps.py
8817 views
1
# -*- coding: utf-8 -*-
2
r"""
3
The set `\mathbb{P}^1(\QQ)` of cusps
4
5
EXAMPLES::
6
7
sage: Cusps
8
Set P^1(QQ) of all cusps
9
10
::
11
12
sage: Cusp(oo)
13
Infinity
14
"""
15
16
#*****************************************************************************
17
# Copyright (C) 2005 William Stein <[email protected]>
18
#
19
# Distributed under the terms of the GNU General Public License (GPL)
20
#
21
# This code is distributed in the hope that it will be useful,
22
# but WITHOUT ANY WARRANTY; without even the implied warranty of
23
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24
# General Public License for more details.
25
#
26
# The full text of the GPL is available at:
27
#
28
# http://www.gnu.org/licenses/
29
#*****************************************************************************
30
31
from sage.rings.all import Rational, Integer, ZZ, QQ
32
33
from sage.rings.infinity import is_Infinite
34
from sage.structure.parent_base import ParentWithBase
35
from sage.structure.element import Element, is_InfinityElement
36
from sage.modular.modsym.p1list import lift_to_sl2z_llong
37
from sage.matrix.matrix import is_Matrix
38
from sage.misc.cachefunc import cached_method
39
40
class Cusps_class(ParentWithBase):
41
"""
42
The set of cusps.
43
44
EXAMPLES::
45
46
sage: C = Cusps; C
47
Set P^1(QQ) of all cusps
48
sage: loads(C.dumps()) == C
49
True
50
"""
51
def __init__(self):
52
r"""
53
The set of cusps, i.e. `\mathbb{P}^1(\QQ)`.
54
55
EXAMPLES::
56
57
sage: C = sage.modular.cusps.Cusps_class() ; C
58
Set P^1(QQ) of all cusps
59
sage: Cusps == C
60
True
61
"""
62
ParentWithBase.__init__(self, self)
63
64
def __cmp__(self, right):
65
"""
66
Return equality only if right is the set of cusps.
67
68
EXAMPLES::
69
70
sage: Cusps == Cusps
71
True
72
sage: Cusps == QQ
73
False
74
"""
75
return cmp(type(self), type(right))
76
77
def _repr_(self):
78
"""
79
String representation of the set of cusps.
80
81
EXAMPLES::
82
83
sage: Cusps
84
Set P^1(QQ) of all cusps
85
sage: Cusps._repr_()
86
'Set P^1(QQ) of all cusps'
87
sage: Cusps.rename('CUSPS'); Cusps
88
CUSPS
89
sage: Cusps.rename(); Cusps
90
Set P^1(QQ) of all cusps
91
sage: Cusps
92
Set P^1(QQ) of all cusps
93
"""
94
return "Set P^1(QQ) of all cusps"
95
96
def _latex_(self):
97
"""
98
Return latex representation of self.
99
100
EXAMPLES::
101
102
sage: latex(Cusps)
103
\mathbf{P}^1(\QQ)
104
sage: latex(Cusps) == Cusps._latex_()
105
True
106
"""
107
return "\\mathbf{P}^1(\\QQ)"
108
109
def __call__(self, x):
110
"""
111
Coerce x into the set of cusps.
112
113
EXAMPLES::
114
115
sage: a = Cusps(-4/5); a
116
-4/5
117
sage: Cusps(a) is a
118
False
119
sage: Cusps(1.5)
120
3/2
121
sage: Cusps(oo)
122
Infinity
123
sage: Cusps(I)
124
Traceback (most recent call last):
125
...
126
TypeError: Unable to convert I to a Cusp
127
"""
128
return Cusp(x, parent=self)
129
130
def _coerce_impl(self, x):
131
"""
132
Canonical coercion of x into the set of cusps.
133
134
EXAMPLES::
135
136
sage: Cusps._coerce_(7/13)
137
7/13
138
sage: Cusps._coerce_(GF(7)(3))
139
Traceback (most recent call last):
140
...
141
TypeError: no canonical coercion of element into self
142
sage: Cusps(GF(7)(3))
143
3
144
sage: Cusps._coerce_impl(GF(7)(3))
145
Traceback (most recent call last):
146
...
147
TypeError: no canonical coercion of element into self
148
"""
149
if is_Infinite(x):
150
return Cusp(x, parent=self)
151
else:
152
return self._coerce_try(x, QQ)
153
154
@cached_method
155
def zero_element(self):
156
"""
157
Return the zero cusp.
158
159
NOTE:
160
161
The existence of this method is assumed by some
162
parts of Sage's coercion model.
163
164
EXAMPLE::
165
166
sage: Cusps.zero_element()
167
0
168
169
"""
170
return Cusp(0, parent=self)
171
172
Cusps = Cusps_class()
173
174
175
class Cusp(Element):
176
"""
177
A cusp.
178
179
A cusp is either a rational number or infinity, i.e., an element of
180
the projective line over Q. A Cusp is stored as a pair (a,b), where
181
gcd(a,b)=1 and a,b are of type Integer.
182
183
EXAMPLES::
184
185
sage: a = Cusp(2/3); b = Cusp(oo)
186
sage: a.parent()
187
Set P^1(QQ) of all cusps
188
sage: a.parent() is b.parent()
189
True
190
"""
191
192
def __init__(self, a, b=None, parent=None, check=True):
193
r"""
194
Create the cusp a/b in `\mathbb{P}^1(\QQ)`, where if b=0
195
this is the cusp at infinity.
196
197
When present, b must either be Infinity or coercible to an
198
Integer.
199
200
EXAMPLES::
201
202
sage: Cusp(2,3)
203
2/3
204
sage: Cusp(3,6)
205
1/2
206
sage: Cusp(1,0)
207
Infinity
208
sage: Cusp(infinity)
209
Infinity
210
sage: Cusp(5)
211
5
212
sage: Cusp(1/2)
213
1/2
214
sage: Cusp(1.5)
215
3/2
216
sage: Cusp(int(7))
217
7
218
sage: Cusp(1, 2, check=False)
219
1/2
220
sage: Cusp('sage', 2.5, check=False) # don't do this!
221
sage/2.50000000000000
222
223
::
224
225
sage: I**2
226
-1
227
sage: Cusp(I)
228
Traceback (most recent call last):
229
...
230
TypeError: Unable to convert I to a Cusp
231
232
::
233
234
sage: a = Cusp(2,3)
235
sage: loads(a.dumps()) == a
236
True
237
238
::
239
240
sage: Cusp(1/3,0)
241
Infinity
242
sage: Cusp((1,0))
243
Infinity
244
245
TESTS::
246
247
sage: Cusp("1/3", 5)
248
1/15
249
sage: Cusp(Cusp(3/5), 7)
250
3/35
251
sage: Cusp(5/3, 0)
252
Infinity
253
sage: Cusp(3,oo)
254
0
255
sage: Cusp((7,3), 5)
256
7/15
257
sage: Cusp(int(5), 7)
258
5/7
259
260
::
261
262
sage: Cusp(0,0)
263
Traceback (most recent call last):
264
...
265
TypeError: Unable to convert (0, 0) to a Cusp
266
267
::
268
269
sage: Cusp(oo,oo)
270
Traceback (most recent call last):
271
...
272
TypeError: Unable to convert (+Infinity, +Infinity) to a Cusp
273
274
::
275
276
sage: Cusp(Cusp(oo),oo)
277
Traceback (most recent call last):
278
...
279
TypeError: Unable to convert (Infinity, +Infinity) to a Cusp
280
"""
281
if parent is None:
282
parent = Cusps
283
Element.__init__(self, parent)
284
285
if not check:
286
self.__a = a; self.__b = b
287
return
288
289
if b is None:
290
if isinstance(a, Integer):
291
self.__a = a
292
self.__b = ZZ(1)
293
elif isinstance(a, Rational):
294
self.__a = a.numer()
295
self.__b = a.denom()
296
elif is_InfinityElement(a):
297
self.__a = ZZ(1)
298
self.__b = ZZ(0)
299
elif isinstance(a, Cusp):
300
self.__a = a.__a
301
self.__b = a.__b
302
elif isinstance(a, (int, long)):
303
self.__a = ZZ(a)
304
self.__b = ZZ(1)
305
elif isinstance(a, (tuple, list)):
306
if len(a) != 2:
307
raise TypeError, "Unable to convert %s to a Cusp"%a
308
if ZZ(a[1]) == 0:
309
self.__a = ZZ(1)
310
self.__b = ZZ(0)
311
return
312
try:
313
r = QQ((a[0], a[1]))
314
self.__a = r.numer()
315
self.__b = r.denom()
316
except (ValueError, TypeError):
317
raise TypeError, "Unable to convert %s to a Cusp"%a
318
else:
319
try:
320
r = QQ(a)
321
self.__a = r.numer()
322
self.__b = r.denom()
323
except (ValueError, TypeError):
324
raise TypeError, "Unable to convert %s to a Cusp"%a
325
return
326
327
if is_InfinityElement(b):
328
if is_InfinityElement(a) or (isinstance(a, Cusp) and a.is_infinity()):
329
raise TypeError, "Unable to convert (%s, %s) to a Cusp"%(a, b)
330
self.__a = ZZ(0)
331
self.__b = ZZ(1)
332
return
333
elif not b:
334
if not a:
335
raise TypeError, "Unable to convert (%s, %s) to a Cusp"%(a, b)
336
self.__a = ZZ(1)
337
self.__b = ZZ(0)
338
return
339
340
if isinstance(a, Integer) or isinstance(a, Rational):
341
r = a / ZZ(b)
342
elif is_InfinityElement(a):
343
self.__a = ZZ(1)
344
self.__b = ZZ(0)
345
return
346
elif isinstance(a, Cusp):
347
if a.__b:
348
r = a.__a / (a.__b * b)
349
else:
350
self.__a = ZZ(1)
351
self.__b = ZZ(0)
352
return
353
elif isinstance(a, (int, long)):
354
r = ZZ(a) / b
355
elif isinstance(a, (tuple, list)):
356
if len(a) != 2:
357
raise TypeError, "Unable to convert (%s, %s) to a Cusp"%(a, b)
358
r = ZZ(a[0]) / (ZZ(a[1]) * b)
359
else:
360
try:
361
r = QQ(a) / b
362
except (ValueError, TypeError):
363
raise TypeError, "Unable to convert (%s, %s) to a Cusp"%(a, b)
364
365
self.__a = r.numer()
366
self.__b = r.denom()
367
368
369
def __hash__(self):
370
"""
371
EXAMPLES:
372
sage: hash(Cusp(1/3))
373
1298787075 # 32-bit
374
3713081631933328131 # 64-bit
375
sage: hash(Cusp(oo))
376
1302034650 # 32-bit
377
3713081631936575706 # 64-bit
378
"""
379
return hash((self.__a, self.__b))
380
381
def __cmp__(self, right):
382
"""
383
Compare the cusps self and right. Comparison is as for rational
384
numbers, except with the cusp oo greater than everything but
385
itself.
386
387
The ordering in comparison is only really meaningful for infinity
388
or elements that coerce to the rationals.
389
390
EXAMPLES::
391
392
sage: Cusp(2/3) == Cusp(oo)
393
False
394
395
::
396
397
sage: Cusp(2/3) < Cusp(oo)
398
True
399
400
::
401
402
sage: Cusp(2/3)> Cusp(oo)
403
False
404
405
::
406
407
sage: Cusp(2/3) > Cusp(5/2)
408
False
409
410
::
411
412
sage: Cusp(2/3) < Cusp(5/2)
413
True
414
415
::
416
417
sage: Cusp(2/3) == Cusp(5/2)
418
False
419
420
::
421
422
sage: Cusp(oo) == Cusp(oo)
423
True
424
425
::
426
427
sage: 19/3 < Cusp(oo)
428
True
429
430
::
431
432
sage: Cusp(oo) < 19/3
433
False
434
435
::
436
437
sage: Cusp(2/3) < Cusp(11/7)
438
True
439
440
::
441
442
sage: Cusp(11/7) < Cusp(2/3)
443
False
444
445
::
446
447
sage: 2 < Cusp(3)
448
True
449
"""
450
if not self.__b:
451
# self is oo, which is bigger than everything but oo.
452
if not right.__b:
453
return 0
454
else:
455
return 1
456
elif not right.__b:
457
if not self.__b:
458
return 0
459
else:
460
return -1
461
return cmp(self._rational_(), right._rational_())
462
463
def is_infinity(self):
464
"""
465
Returns True if this is the cusp infinity.
466
467
EXAMPLES::
468
469
sage: Cusp(3/5).is_infinity()
470
False
471
sage: Cusp(1,0).is_infinity()
472
True
473
sage: Cusp(0,1).is_infinity()
474
False
475
"""
476
return not self.__b
477
478
def numerator(self):
479
"""
480
Return the numerator of the cusp a/b.
481
482
EXAMPLES::
483
484
sage: x=Cusp(6,9); x
485
2/3
486
sage: x.numerator()
487
2
488
sage: Cusp(oo).numerator()
489
1
490
sage: Cusp(-5/10).numerator()
491
-1
492
"""
493
return self.__a
494
495
def denominator(self):
496
"""
497
Return the denominator of the cusp a/b.
498
499
EXAMPLES::
500
501
sage: x=Cusp(6,9); x
502
2/3
503
sage: x.denominator()
504
3
505
sage: Cusp(oo).denominator()
506
0
507
sage: Cusp(-5/10).denominator()
508
2
509
"""
510
return self.__b
511
512
def _rational_(self):
513
"""
514
Coerce to a rational number.
515
516
EXAMPLES::
517
518
sage: QQ(Cusp(oo))
519
Traceback (most recent call last):
520
...
521
TypeError: cusp Infinity is not a rational number
522
sage: QQ(Cusp(-3,7))
523
-3/7
524
sage: Cusp(11,2)._rational_()
525
11/2
526
"""
527
try:
528
return self.__rational
529
except AttributeError:
530
pass
531
532
if not self.__b:
533
raise TypeError, "cusp %s is not a rational number"%self
534
self.__rational = self.__a / self.__b
535
return self.__rational
536
537
def _integer_(self, ZZ=None):
538
"""
539
Coerce to an integer.
540
541
EXAMPLES::
542
543
sage: ZZ(Cusp(-19))
544
-19
545
sage: Cusp(4,2)._integer_()
546
2
547
548
::
549
550
sage: ZZ(Cusp(oo))
551
Traceback (most recent call last):
552
...
553
TypeError: cusp Infinity is not an integer
554
sage: ZZ(Cusp(-3,7))
555
Traceback (most recent call last):
556
...
557
TypeError: cusp -3/7 is not an integer
558
"""
559
if self.__b != 1:
560
raise TypeError, "cusp %s is not an integer"%self
561
return self.__a
562
563
def _repr_(self):
564
"""
565
String representation of this cusp.
566
567
EXAMPLES::
568
569
sage: a = Cusp(2/3); a
570
2/3
571
sage: a._repr_()
572
'2/3'
573
sage: a.rename('2/3(cusp)'); a
574
2/3(cusp)
575
"""
576
if self.__b.is_zero():
577
return "Infinity"
578
if self.__b != 1:
579
return "%s/%s"%(self.__a,self.__b)
580
else:
581
return str(self.__a)
582
583
def _latex_(self):
584
r"""
585
Latex representation of this cusp.
586
587
EXAMPLES::
588
589
sage: latex(Cusp(-2/7))
590
\frac{-2}{7}
591
sage: latex(Cusp(oo))
592
\infty
593
sage: latex(Cusp(oo)) == Cusp(oo)._latex_()
594
True
595
"""
596
if self.__b.is_zero():
597
return "\\infty"
598
if self.__b != 1:
599
return "\\frac{%s}{%s}"%(self.__a,self.__b)
600
else:
601
return str(self.__a)
602
603
def __neg__(self):
604
"""
605
The negative of this cusp.
606
607
EXAMPLES::
608
609
sage: -Cusp(2/7)
610
-2/7
611
sage: -Cusp(oo)
612
Infinity
613
"""
614
return Cusp(-self.__a, self.__b)
615
616
def is_gamma0_equiv(self, other, N, transformation = None):
617
r"""
618
Return whether self and other are equivalent modulo the action of
619
`\Gamma_0(N)` via linear fractional transformations.
620
621
INPUT:
622
623
624
- ``other`` - Cusp
625
626
- ``N`` - an integer (specifies the group
627
Gamma_0(N))
628
629
- ``transformation`` - None (default) or either the string 'matrix' or 'corner'. If 'matrix',
630
it also returns a matrix in Gamma_0(N) that sends self to other. The matrix is chosen such that the lower left entry is as small as possible in absolute value. If 'corner' (or True for backwards compatibility), it returns only the upper left entry of such a matrix.
631
632
633
OUTPUT:
634
635
636
- a boolean - True if self and other are equivalent
637
638
- a matrix or an integer- returned only if transformation is 'matrix' or 'corner', respectively.
639
640
641
EXAMPLES::
642
643
sage: x = Cusp(2,3)
644
sage: y = Cusp(4,5)
645
sage: x.is_gamma0_equiv(y, 2)
646
True
647
sage: _, ga = x.is_gamma0_equiv(y, 2, 'matrix'); ga
648
[-1 2]
649
[-2 3]
650
sage: x.is_gamma0_equiv(y, 3)
651
False
652
sage: x.is_gamma0_equiv(y, 3, 'matrix')
653
(False, None)
654
sage: Cusp(1/2).is_gamma0_equiv(1/3,11,'corner')
655
(True, 19)
656
657
sage: Cusp(1,0)
658
Infinity
659
sage: z = Cusp(1,0)
660
sage: x.is_gamma0_equiv(z, 3, 'matrix')
661
(
662
[-1 1]
663
True, [-3 2]
664
)
665
666
667
ALGORITHM: See Proposition 2.2.3 of Cremona's book 'Algorithms for
668
Modular Elliptic Curves', or Prop 2.27 of Stein's Ph.D. thesis.
669
"""
670
if transformation not in [False,True,"matrix",None,"corner"]:
671
raise ValueError, "Value %s of the optional argument transformation is not valid."
672
673
if not isinstance(other, Cusp):
674
other = Cusp(other)
675
N = ZZ(N)
676
u1 = self.__a
677
v1 = self.__b
678
u2 = other.__a
679
v2 = other.__b
680
681
zero = ZZ.zero_element()
682
one = ZZ.one_element()
683
684
if transformation == "matrix":
685
from sage.matrix.constructor import matrix
686
687
#if transformation :
688
# transformation = "corner"
689
690
if v1 == v2 and u1 == u2:
691
if not transformation:
692
return True
693
elif transformation == "matrix":
694
return True, matrix(ZZ,[[1,0],[0,1]])
695
else:
696
return True, one
697
698
# a necessary, but not sufficient condition unless N is square-free
699
if v1.gcd(N) != v2.gcd(N):
700
if not transformation:
701
return False
702
else:
703
return False, None
704
705
if (u1,v1) != (zero,one):
706
if v1 in [zero, one]:
707
s1 = one
708
else:
709
s1 = u1.inverse_mod(v1)
710
else:
711
s1 = 0
712
if (u2,v2) != (zero, one):
713
if v2 in [zero,one]:
714
s2 = one
715
else:
716
s2 = u2.inverse_mod(v2)
717
else:
718
s2 = zero
719
g = (v1*v2).gcd(N)
720
a = s1*v2 - s2*v1
721
if a%g != 0:
722
if not transformation:
723
return False
724
else:
725
return False, None
726
727
if not transformation:
728
return True
729
730
# Now we know the cusps are equivalent. Use the proof of Prop 2.2.3
731
# of Cremona to find a matrix in Gamma_0(N) relating them.
732
if v1 == 0: # the first is oo
733
if v2 == 0: # both are oo
734
if transformation == "matrix":
735
return (True, matrix(ZZ,[[1,0],[0,1]]))
736
else:
737
return (True, one)
738
else:
739
dum, s2, r2 = u2.xgcd(-v2)
740
assert dum.is_one()
741
if transformation == "matrix":
742
return (True, matrix(ZZ, [[u2,r2],[v2,s2]]) )
743
else:
744
return (True, u2)
745
746
elif v2 == 0: # the second is oo
747
dum, s1, r1 = u1.xgcd(-v1)
748
assert dum.is_one()
749
if transformation == "matrix":
750
return (True, matrix(ZZ, [[s1,-r1],[-v1,u1]]) )
751
else:
752
return (True, s1)
753
754
dum, s2, r2 = u2.xgcd(-v2)
755
assert dum.is_one()
756
dum, s1, r1 = u1.xgcd(-v1)
757
assert dum.is_one()
758
a = s1*v2 - s2*v1
759
assert (a%g).is_zero()
760
# solve x*v1*v2 + a = 0 (mod N).
761
d,x0,y0 = (v1*v2).xgcd(N) # x0*v1*v2 + y0*N = d = g.
762
# so x0*v1*v2 - g = 0 (mod N)
763
x = -x0 * ZZ(a/g)
764
# now x*v1*v2 + a = 0 (mod N)
765
766
# the rest is all added in trac 10926
767
s1p = s1+x*v1
768
M = N//g
769
770
if transformation == "matrix":
771
C = s1p*v2 - s2*v1
772
if C % (M*v1*v2) == 0 :
773
k = - C//(M*v1*v2)
774
else:
775
k = - (C/(M*v1*v2)).round()
776
777
s1pp = s1p + k *M* v1
778
# C += k*M*v1*v2 # is now the smallest in absolute value
779
C = s1pp*v2 - s2*v1
780
A = u2*s1pp - r2*v1
781
782
r1pp = r1 + (x+k*M)*u1
783
B = r2 * u1 - r1pp * u2
784
D = s2 * u1 - r1pp * v2
785
786
ga = matrix(ZZ, [[A,B],[C,D]])
787
assert ga.det() == 1
788
assert C % N == 0
789
assert (A*u1 + B*v1)/(C*u1+D*v1) == u2/v2
790
return (True, ga)
791
792
else:
793
# mainly for backwards compatibility and
794
# for how it is used in modular symbols
795
A = (u2*s1p - r2*v1)
796
if u2 != 0 and v1 != 0:
797
A = A % (u2*v1*M)
798
return (True, A)
799
800
def is_gamma1_equiv(self, other, N):
801
"""
802
Return whether self and other are equivalent modulo the action of
803
Gamma_1(N) via linear fractional transformations.
804
805
INPUT:
806
807
808
- ``other`` - Cusp
809
810
- ``N`` - an integer (specifies the group
811
Gamma_1(N))
812
813
814
OUTPUT:
815
816
817
- ``bool`` - True if self and other are equivalent
818
819
- ``int`` - 0, 1 or -1, gives further information
820
about the equivalence: If the two cusps are u1/v1 and u2/v2, then
821
they are equivalent if and only if v1 = v2 (mod N) and u1 = u2 (mod
822
gcd(v1,N)) or v1 = -v2 (mod N) and u1 = -u2 (mod gcd(v1,N)) The
823
sign is +1 for the first and -1 for the second. If the two cusps
824
are not equivalent then 0 is returned.
825
826
827
EXAMPLES::
828
829
sage: x = Cusp(2,3)
830
sage: y = Cusp(4,5)
831
sage: x.is_gamma1_equiv(y,2)
832
(True, 1)
833
sage: x.is_gamma1_equiv(y,3)
834
(False, 0)
835
sage: z = Cusp(QQ(x) + 10)
836
sage: x.is_gamma1_equiv(z,10)
837
(True, 1)
838
sage: z = Cusp(1,0)
839
sage: x.is_gamma1_equiv(z, 3)
840
(True, -1)
841
sage: Cusp(0).is_gamma1_equiv(oo, 1)
842
(True, 1)
843
sage: Cusp(0).is_gamma1_equiv(oo, 3)
844
(False, 0)
845
"""
846
if not isinstance(other, Cusp):
847
other = Cusp(other)
848
N = ZZ(N)
849
u1 = self.__a
850
v1 = self.__b
851
u2 = other.__a
852
v2 = other.__b
853
g = v1.gcd(N)
854
if ((v2 - v1) % N == 0 and (u2 - u1)%g== 0):
855
return True, 1
856
elif ((v2 + v1) % N == 0 and (u2 + u1)%g== 0):
857
return True, -1
858
return False, 0
859
860
def is_gamma_h_equiv(self, other, G):
861
"""
862
Return a pair (b, t), where b is True or False as self and other
863
are equivalent under the action of G, and t is 1 or -1, as
864
described below.
865
866
Two cusps `u1/v1` and `u2/v2` are equivalent modulo
867
Gamma_H(N) if and only if `v1 = h*v2 (\mathrm{mod} N)` and
868
`u1 = h^{(-1)}*u2 (\mathrm{mod} gcd(v1,N))` or
869
`v1 = -h*v2 (mod N)` and
870
`u1 = -h^{(-1)}*u2 (\mathrm{mod} gcd(v1,N))` for some
871
`h \in H`. Then t is 1 or -1 as c and c' fall into the
872
first or second case, respectively.
873
874
INPUT:
875
876
877
- ``other`` - Cusp
878
879
- ``G`` - a congruence subgroup Gamma_H(N)
880
881
882
OUTPUT:
883
884
885
- ``bool`` - True if self and other are equivalent
886
887
- ``int`` - -1, 0, 1; extra info
888
889
890
EXAMPLES::
891
892
sage: x = Cusp(2,3)
893
sage: y = Cusp(4,5)
894
sage: x.is_gamma_h_equiv(y,GammaH(13,[2]))
895
(True, 1)
896
sage: x.is_gamma_h_equiv(y,GammaH(13,[5]))
897
(False, 0)
898
sage: x.is_gamma_h_equiv(y,GammaH(5,[]))
899
(False, 0)
900
sage: x.is_gamma_h_equiv(y,GammaH(23,[4]))
901
(True, -1)
902
903
Enumerating the cusps for a space of modular symbols uses this
904
function.
905
906
::
907
908
sage: G = GammaH(25,[6]) ; M = G.modular_symbols() ; M
909
Modular Symbols space of dimension 11 for Congruence Subgroup Gamma_H(25) with H generated by [6] of weight 2 with sign 0 and over Rational Field
910
sage: M.cusps()
911
[37/75, 1/2, 31/125, 1/4, -2/5, 2/5, -1/5, 1/10, -3/10, 1/15, 7/15, 9/20]
912
sage: len(M.cusps())
913
12
914
915
This is always one more than the associated space of weight 2 Eisenstein
916
series.
917
918
::
919
920
sage: G.dimension_eis(2)
921
11
922
sage: M.cuspidal_subspace()
923
Modular Symbols subspace of dimension 0 of Modular Symbols space of dimension 11 for Congruence Subgroup Gamma_H(25) with H generated by [6] of weight 2 with sign 0 and over Rational Field
924
sage: G.dimension_cusp_forms(2)
925
0
926
"""
927
from sage.modular.arithgroup.all import is_GammaH
928
if not isinstance(other, Cusp):
929
other = Cusp(other)
930
if not is_GammaH(G):
931
raise TypeError, "G must be a group GammaH(N)."
932
933
H = G._list_of_elements_in_H()
934
N = ZZ(G.level())
935
u1 = self.__a
936
v1 = self.__b
937
u2 = other.__a
938
v2 = other.__b
939
g = v1.gcd(N)
940
941
for h in H:
942
v_tmp = (h*v1)%N
943
u_tmp = (h*u2)%N
944
if (v_tmp - v2)%N == 0 and (u_tmp - u1)%g == 0:
945
return True, 1
946
if (v_tmp + v2)%N == 0 and (u_tmp + u1)%g == 0:
947
return True, -1
948
return False, 0
949
950
def _acted_upon_(self, g, self_on_left):
951
r"""
952
Implements the left action of `SL_2(\ZZ)` on self.
953
954
EXAMPLES::
955
956
sage: g = matrix(ZZ, 2, [1,1,0,1]); g
957
[1 1]
958
[0 1]
959
sage: g * Cusp(2,5)
960
7/5
961
sage: Cusp(2,5) * g
962
Traceback (most recent call last):
963
...
964
TypeError: unsupported operand parent(s) for '*': 'Set P^1(QQ) of all cusps' and 'Full MatrixSpace of 2 by 2 dense matrices over Integer Ring'
965
sage: h = matrix(ZZ, 2, [12,3,-100,7])
966
sage: h * Cusp(2,5)
967
-13/55
968
sage: Cusp(2,5)._acted_upon_(h, False)
969
-13/55
970
sage: (h*g) * Cusp(3,7) == h * (g * Cusp(3,7))
971
True
972
973
sage: cm = sage.structure.element.get_coercion_model()
974
sage: cm.explain(MatrixSpace(ZZ, 2), Cusps)
975
Action discovered.
976
Left action by Full MatrixSpace of 2 by 2 dense matrices over Integer Ring on Set P^1(QQ) of all cusps
977
Result lives in Set P^1(QQ) of all cusps
978
Set P^1(QQ) of all cusps
979
"""
980
if not self_on_left:
981
if (is_Matrix(g) and g.base_ring() is ZZ
982
and g.ncols() == 2 and g.nrows() == 2):
983
a, b, c, d = g.list()
984
return Cusp(a*self.__a + b*self.__b, c*self.__a + d*self.__b)
985
986
987
def apply(self, g):
988
"""
989
Return g(self), where g=[a,b,c,d] is a list of length 4, which we
990
view as a linear fractional transformation.
991
992
EXAMPLES: Apply the identity matrix::
993
994
sage: Cusp(0).apply([1,0,0,1])
995
0
996
sage: Cusp(0).apply([0,-1,1,0])
997
Infinity
998
sage: Cusp(0).apply([1,-3,0,1])
999
-3
1000
"""
1001
return Cusp(g[0]*self.__a + g[1]*self.__b, g[2]*self.__a + g[3]*self.__b)
1002
1003
def galois_action(self, t, N):
1004
r"""
1005
Suppose this cusp is `\alpha`, `G` a congruence subgroup of level `N`
1006
and `\sigma` is the automorphism in the Galois group of
1007
`\QQ(\zeta_N)/\QQ` that sends `\zeta_N` to `\zeta_N^t`. Then this
1008
function computes a cusp `\beta` such that `\sigma([\alpha]) = [\beta]`,
1009
where `[\alpha]` is the equivalence class of `\alpha` modulo `G`.
1010
1011
This code only needs as input the level and not the group since the
1012
action of galois for a congruence group `G` of level `N` is compatible
1013
with the action of the full congruence group `\Gamma(N)`.
1014
1015
1016
INPUT:
1017
1018
- `t` -- integer that is coprime to N
1019
1020
- `N` -- positive integer (level)
1021
1022
OUTPUT:
1023
1024
- a cusp
1025
1026
1027
.. WARNING::
1028
1029
In some cases `N` must fit in a long long, i.e., there
1030
are cases where this algorithm isn't fully implemented.
1031
1032
.. NOTE::
1033
1034
Modular curves can have multiple non-isomorphic models over `\QQ`.
1035
The action of galois depends on such a model. The model over `\QQ`
1036
of `X(G)` used here is the model where the function field
1037
`\QQ(X(G))` is given by the functions whose fourier expansion at
1038
`\infty` have their coefficients in `\QQ`. For `X(N):=X(\Gamma(N))`
1039
the corresponding moduli interpretation over `\ZZ[1/N]` is that
1040
`X(N)` parametrizes pairs `(E,a)` where `E` is a (generalized)
1041
elliptic curve and `a: \ZZ / N\ZZ \times \mu_N \to E` is a closed
1042
immersion such that the weil pairing of `a(1,1)` and `a(0,\zeta_N)`
1043
is `\zeta_N`. In this parameterisation the point `z \in H`
1044
corresponds to the pair `(E_z,a_z)` with `E_z=\CC/(z \ZZ+\ZZ)` and
1045
`a_z: \ZZ / N\ZZ \times \mu_N \to E` given by `a_z(1,1) = z/N` and
1046
`a_z(0,\zeta_N) = 1/N`.
1047
Similarly `X_1(N):=X(\Gamma_1(N))` parametrizes pairs `(E,a)` where
1048
`a: \mu_N \to E` is a closed immersion.
1049
1050
EXAMPLES::
1051
1052
sage: Cusp(1/10).galois_action(3, 50)
1053
1/170
1054
sage: Cusp(oo).galois_action(3, 50)
1055
Infinity
1056
sage: c=Cusp(0).galois_action(3, 50); c
1057
50/67
1058
sage: Gamma0(50).reduce_cusp(c)
1059
0
1060
1061
Here we compute the permutations of the action for t=3 on cusps for
1062
Gamma0(50). ::
1063
1064
sage: N = 50; t=3; G = Gamma0(N); C = G.cusps()
1065
sage: cl = lambda z: exists(C, lambda y:y.is_gamma0_equiv(z, N))[1]
1066
sage: for i in range(5): print i, t^i, [cl(alpha.galois_action(t^i,N)) for alpha in C]
1067
0 1 [0, 1/25, 1/10, 1/5, 3/10, 2/5, 1/2, 3/5, 7/10, 4/5, 9/10, Infinity]
1068
1 3 [0, 1/25, 7/10, 2/5, 1/10, 4/5, 1/2, 1/5, 9/10, 3/5, 3/10, Infinity]
1069
2 9 [0, 1/25, 9/10, 4/5, 7/10, 3/5, 1/2, 2/5, 3/10, 1/5, 1/10, Infinity]
1070
3 27 [0, 1/25, 3/10, 3/5, 9/10, 1/5, 1/2, 4/5, 1/10, 2/5, 7/10, Infinity]
1071
4 81 [0, 1/25, 1/10, 1/5, 3/10, 2/5, 1/2, 3/5, 7/10, 4/5, 9/10, Infinity]
1072
1073
TESTS:
1074
1075
Here we check that the galois action is indeed a permutation on the
1076
cusps of Gamma1(48) and check that :trac:`13253` is fixed. ::
1077
1078
sage: G=Gamma1(48)
1079
sage: C=G.cusps()
1080
sage: for i in Integers(48).unit_gens():
1081
... C_permuted = [G.reduce_cusp(c.galois_action(i,48)) for c in C]
1082
... assert len(set(C_permuted))==len(C)
1083
1084
We test that Gamma1(19) has 9 rational cusps and check that :trac:`8998`
1085
is fixed. ::
1086
1087
sage: G = Gamma1(19)
1088
sage: [c for c in G.cusps() if c.galois_action(2,19).is_gamma1_equiv(c,19)[0]]
1089
[2/19, 3/19, 4/19, 5/19, 6/19, 7/19, 8/19, 9/19, Infinity]
1090
1091
1092
REFERENCES:
1093
1094
- Section 1.3 of Glenn Stevens, "Arithmetic on Modular Curves"
1095
1096
- There is a long comment about our algorithm in the source code for this function.
1097
1098
AUTHORS:
1099
1100
- William Stein, 2009-04-18
1101
1102
"""
1103
if self.is_infinity(): return self
1104
if not isinstance(t, Integer): t = Integer(t)
1105
1106
# Our algorithm for computing the Galois action works as
1107
# follows (see Section 1.3 of Glenn Stevens "Arithmetic on
1108
# Modular Curves" for a proof that the action given below is
1109
# correct). We alternatively view the set of cusps as the
1110
# Gamma-equivalence classes of column vectors [a;b] with
1111
# gcd(a,b,N)=1, and the left action of Gamma by matrix
1112
# multiplication. The action of t is induced by [a;b] |-->
1113
# [a;t'*b], where t' is an inverse mod N of t. For [a;t'*b]
1114
# with gcd(a,t'*b)==1, the cusp corresponding to [a;t'*b] is
1115
# just the rational number a/(t'*b). Thus in this case, to
1116
# compute the action of t we just do a/b <--> [a;b] |--->
1117
# [a;t'*b] <--> a/(t'*b). IN the other case when we get
1118
# [a;t'*b] with gcd(a,t'*b) != 1, which can and does happen,
1119
# we have to work a bit harder. We need to find [c;d] such
1120
# that [c;d] is congruent to [a;t'*b] modulo N, and
1121
# gcd(c,d)=1. There is a standard lifting algorithm that is
1122
# implemented for working with P^1(Z/NZ) [it is needed for
1123
# modular symbols algorithms], so we just apply it to lift
1124
# [a,t'*b] to a matrix [A,B;c,d] in SL_2(Z) with lower two
1125
# entries congruent to [a,t'*b] modulo N. This exactly solves
1126
# our problem, since gcd(c,d)=1.
1127
1128
a = self.__a
1129
b = self.__b * t.inverse_mod(N)
1130
if b.gcd(a) != 1:
1131
_,_,a,b = lift_to_sl2z_llong(a,b,N)
1132
a = Integer(a); b = Integer(b)
1133
1134
# Now that we've computed the Galois action, we efficiently
1135
# construct the corresponding cusp as a Cusp object.
1136
return Cusp(a,b,check=False)
1137
1138