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