Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/modular/arithgroup/congroup_gammaH.py
8820 views
1
r"""
2
Congruence Subgroup `\Gamma_H(N)`
3
4
AUTHORS:
5
6
- Jordi Quer
7
- David Loeffler
8
"""
9
10
################################################################################
11
#
12
# Copyright (C) 2009, The Sage Group -- http://www.sagemath.org/
13
#
14
# Distributed under the terms of the GNU General Public License (GPL)
15
#
16
# The full text of the GPL is available at:
17
#
18
# http://www.gnu.org/licenses/
19
#
20
################################################################################
21
22
from sage.rings.arith import euler_phi, lcm, gcd, divisors, get_inverse_mod, get_gcd, factor
23
from sage.modular.modsym.p1list import lift_to_sl2z
24
from congroup_generic import CongruenceSubgroup
25
from sage.modular.cusps import Cusp
26
from sage.misc.cachefunc import cached_method
27
28
# Just for now until we make an SL_2 group type.
29
from sage.rings.integer_ring import ZZ
30
from sage.rings.finite_rings.integer_mod_ring import Zmod
31
from sage.matrix.matrix_space import MatrixSpace
32
from sage.groups.matrix_gps.finitely_generated import MatrixGroup
33
from sage.matrix.constructor import matrix
34
35
Mat2Z = MatrixSpace(ZZ,2)
36
37
38
_gammaH_cache = {}
39
def GammaH_constructor(level, H):
40
r"""
41
Return the congruence subgroup `\Gamma_H(N)`, which is the subgroup of
42
`SL_2(\ZZ)` consisting of matrices of the form `\begin{pmatrix} a & b \\
43
c & d \end{pmatrix}` with `N | c` and `a, b \in H`, for `H` a specified
44
subgroup of `(\ZZ/N\ZZ)^\times`.
45
46
INPUT:
47
48
- level -- an integer
49
- H -- either 0, 1, or a list
50
* If H is a list, return `\Gamma_H(N)`, where `H`
51
is the subgroup of `(\ZZ/N\ZZ)^*` **generated** by the
52
elements of the list.
53
* If H = 0, returns `\Gamma_0(N)`.
54
* If H = 1, returns `\Gamma_1(N)`.
55
56
EXAMPLES::
57
58
sage: GammaH(11,0) # indirect doctest
59
Congruence Subgroup Gamma0(11)
60
sage: GammaH(11,1)
61
Congruence Subgroup Gamma1(11)
62
sage: GammaH(11,[10])
63
Congruence Subgroup Gamma_H(11) with H generated by [10]
64
sage: GammaH(11,[10,1])
65
Congruence Subgroup Gamma_H(11) with H generated by [10]
66
sage: GammaH(14,[10])
67
Traceback (most recent call last):
68
...
69
ArithmeticError: The generators [10] must be units modulo 14
70
"""
71
from all import Gamma0, Gamma1, SL2Z
72
if level == 1:
73
return SL2Z
74
elif H == 0:
75
return Gamma0(level)
76
elif H == 1:
77
return Gamma1(level)
78
79
H = _normalize_H(H, level)
80
if H == []:
81
return Gamma1(level)
82
83
Hlist = _list_subgroup(level, H)
84
if len(Hlist) == euler_phi(level):
85
return Gamma0(level)
86
87
key = (level, tuple(H))
88
try:
89
return _gammaH_cache[key]
90
except KeyError:
91
_gammaH_cache[key] = GammaH_class(level, H, Hlist)
92
return _gammaH_cache[key]
93
94
def is_GammaH(x):
95
"""
96
Return True if x is a congruence subgroup of type GammaH.
97
98
EXAMPLES::
99
100
sage: from sage.modular.arithgroup.all import is_GammaH
101
sage: is_GammaH(GammaH(13, [2]))
102
True
103
sage: is_GammaH(Gamma0(6))
104
True
105
sage: is_GammaH(Gamma1(6))
106
True
107
sage: is_GammaH(sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5))
108
False
109
"""
110
return isinstance(x, GammaH_class)
111
112
def _normalize_H(H, level):
113
"""
114
Normalize representatives for a given subgroup H of the units
115
modulo level.
116
117
NOTE: This function does *not* make any attempt to find a minimal
118
set of generators for H. It simply normalizes the inputs for use
119
in hashing.
120
121
EXAMPLES::
122
123
sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([23], 10)
124
[3]
125
sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([1,5], 7)
126
[5]
127
sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([4,18], 14)
128
Traceback (most recent call last):
129
...
130
ArithmeticError: The generators [4, 18] must be units modulo 14
131
sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([3,17], 14)
132
[3]
133
sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([-1,7,9], 10)
134
[7, 9]
135
"""
136
H = [ZZ(h) for h in H]
137
for h in H:
138
if gcd(h, level) > 1:
139
raise ArithmeticError, 'The generators %s must be units modulo %s'%(H, level)
140
H = list(set([h%level for h in H]))
141
H.sort()
142
if 1 in H:
143
H.remove(1)
144
return H
145
146
class GammaH_class(CongruenceSubgroup):
147
148
r"""
149
The congruence subgroup `\Gamma_H(N)` for some subgroup `H \trianglelefteq
150
(\ZZ / N\ZZ)^\times`, which is the subgroup of `{\rm
151
SL}_2(\ZZ)` consisting of matrices of the form `\begin{pmatrix} a &
152
b \\ c & d \end{pmatrix}` with `N \mid c` and `a, b \in H`.
153
154
TESTS:
155
156
We test calculation of various invariants of the group: ::
157
158
sage: GammaH(33,[2]).projective_index()
159
96
160
sage: GammaH(33,[2]).genus()
161
5
162
sage: GammaH(7,[2]).genus()
163
0
164
sage: GammaH(23, [1..22]).genus()
165
2
166
sage: Gamma0(23).genus()
167
2
168
sage: GammaH(23, [1]).genus()
169
12
170
sage: Gamma1(23).genus()
171
12
172
173
We calculate the dimensions of some modular forms spaces: ::
174
175
sage: GammaH(33,[2]).dimension_cusp_forms(2)
176
5
177
sage: GammaH(33,[2]).dimension_cusp_forms(3)
178
0
179
sage: GammaH(33,[2,5]).dimension_cusp_forms(2)
180
3
181
sage: GammaH(32079, [21676]).dimension_cusp_forms(20)
182
180266112
183
184
We can sometimes show that there are no weight 1 cusp forms: ::
185
186
sage: GammaH(20, [9]).dimension_cusp_forms(1)
187
0
188
"""
189
190
def __init__(self, level, H, Hlist=None):
191
r"""
192
The congruence subgroup `\Gamma_H(N)`. The subgroup H
193
must be input as a list.
194
195
EXAMPLES::
196
197
sage: GammaH(117, [4])
198
Congruence Subgroup Gamma_H(117) with H generated by [4]
199
sage: G = GammaH(16, [7])
200
sage: TestSuite(G).run()
201
sage: G is loads(dumps(G))
202
True
203
"""
204
CongruenceSubgroup.__init__(self, level)
205
self.__H = H
206
if Hlist is None: Hlist = _list_subgroup(level, H)
207
self.__Hlist = Hlist
208
209
def restrict(self, M):
210
r"""
211
Return the subgroup of `\Gamma_0(M)`, for `M` a divisor of `N`,
212
obtained by taking the image of this group under reduction modulo `N`.
213
214
EXAMPLES::
215
216
sage: G = GammaH(33,[2])
217
sage: G.restrict(11)
218
Congruence Subgroup Gamma0(11)
219
sage: G.restrict(1)
220
Modular Group SL(2,Z)
221
sage: G.restrict(15)
222
Traceback (most recent call last):
223
...
224
ValueError: M (=15) must be a divisor of the level (33) of self
225
"""
226
M = ZZ(M)
227
if self.level() % M:
228
raise ValueError, "M (=%s) must be a divisor of the level (%s) of self" % (M, self.level())
229
return self._new_group_from_level(M)
230
231
def extend(self, M):
232
r"""
233
Return the subgroup of `\Gamma_0(M)`, for `M` a multiple of `N`,
234
obtained by taking the preimage of this group under the reduction map;
235
in other words, the intersection of this group with `\Gamma_0(M)`.
236
237
EXAMPLES::
238
239
sage: G = GammaH(33, [2])
240
sage: G.extend(99)
241
Congruence Subgroup Gamma_H(99) with H generated by [2, 35, 68]
242
sage: G.extend(11)
243
Traceback (most recent call last):
244
...
245
ValueError: M (=11) must be a multiple of the level (33) of self
246
247
"""
248
M = ZZ(M)
249
if M % self.level():
250
raise ValueError, "M (=%s) must be a multiple of the level (%s) of self" % (M, self.level())
251
return self._new_group_from_level(M)
252
253
def __reduce__(self):
254
"""
255
Used for pickling self.
256
257
EXAMPLES::
258
259
sage: GammaH(92,[45,47]).__reduce__()
260
(<function GammaH_constructor at ...>, (92, [45, 47]))
261
"""
262
return GammaH_constructor, (self.level(), self.__H)
263
264
def divisor_subgroups(self):
265
r"""
266
Given this congruence subgroup `\Gamma_H(N)`, return all
267
subgroups `\Gamma_G(M)` for `M` a divisor of `N` and such that
268
`G` is equal to the image of `H` modulo `M`.
269
270
EXAMPLES::
271
272
sage: G = GammaH(33,[2]); G
273
Congruence Subgroup Gamma_H(33) with H generated by [2]
274
sage: G._list_of_elements_in_H()
275
[1, 2, 4, 8, 16, 17, 25, 29, 31, 32]
276
sage: G.divisor_subgroups()
277
[Modular Group SL(2,Z),
278
Congruence Subgroup Gamma0(3),
279
Congruence Subgroup Gamma0(11),
280
Congruence Subgroup Gamma_H(33) with H generated by [2]]
281
"""
282
v = self.__H
283
ans = []
284
for M in self.level().divisors():
285
w = [a % M for a in v if a%M]
286
ans.append(GammaH_constructor(M, w))
287
return ans
288
289
def to_even_subgroup(self):
290
r"""
291
Return the smallest even subgroup of `SL(2, \ZZ)` containing self.
292
293
EXAMPLE::
294
295
sage: GammaH(11, [4]).to_even_subgroup()
296
Congruence Subgroup Gamma0(11)
297
sage: Gamma1(11).to_even_subgroup()
298
Congruence Subgroup Gamma_H(11) with H generated by [10]
299
300
"""
301
if self.is_even(): return self
302
else:
303
return GammaH_constructor(self.level(), self._generators_for_H() + [-1])
304
305
def __cmp__(self, other):
306
"""
307
Compare self to other.
308
309
The ordering on congruence subgroups of the form GammaH(N) for some H
310
is first by level, then by the order of H, then lexicographically by H.
311
In particular, this means that we have Gamma1(N) < GammaH(N) <
312
Gamma0(N) for every nontrivial proper subgroup H.
313
314
EXAMPLES::
315
316
sage: G = GammaH(86, [9])
317
sage: G.__cmp__(G)
318
0
319
sage: G.__cmp__(GammaH(86, [11])) is not 0
320
True
321
sage: Gamma1(11) < Gamma0(11)
322
True
323
sage: Gamma1(11) == GammaH(11, [])
324
True
325
sage: Gamma0(11) == GammaH(11, [2])
326
True
327
sage: G = Gamma0(86)
328
sage: G.__cmp__(G)
329
0
330
sage: G.__cmp__(GammaH(86, [11])) is not 0
331
True
332
sage: Gamma1(17) < Gamma0(17)
333
True
334
sage: Gamma0(1) == SL2Z
335
True
336
sage: Gamma0(2) == Gamma1(2)
337
True
338
339
sage: [x._list_of_elements_in_H() for x in sorted(Gamma0(24).gamma_h_subgroups())]
340
[[1],
341
[1, 5],
342
[1, 7],
343
[1, 11],
344
[1, 13],
345
[1, 17],
346
[1, 19],
347
[1, 23],
348
[1, 5, 7, 11],
349
[1, 5, 13, 17],
350
[1, 5, 19, 23],
351
[1, 7, 13, 19],
352
[1, 7, 17, 23],
353
[1, 11, 13, 23],
354
[1, 11, 17, 19],
355
[1, 5, 7, 11, 13, 17, 19, 23]]
356
"""
357
if is_GammaH(other):
358
return (cmp(self.level(), other.level())
359
or -cmp(self.index(), other.index())
360
or cmp(self._list_of_elements_in_H(), other._list_of_elements_in_H()))
361
else:
362
return CongruenceSubgroup.__cmp__(self, other)
363
364
def _generators_for_H(self):
365
"""
366
Return generators for the subgroup H of the units mod
367
self.level() that defines self.
368
369
EXAMPLES::
370
371
sage: GammaH(17,[4])._generators_for_H()
372
[4]
373
sage: GammaH(12,[-1])._generators_for_H()
374
[11]
375
"""
376
return self.__H
377
378
def _repr_(self):
379
"""
380
Return the string representation of self.
381
382
EXAMPLES::
383
384
sage: GammaH(123, [55])._repr_()
385
'Congruence Subgroup Gamma_H(123) with H generated by [55]'
386
"""
387
return "Congruence Subgroup Gamma_H(%s) with H generated by %s"%(self.level(), self.__H)
388
389
def _latex_(self):
390
r"""
391
Return the \LaTeX representation of self.
392
393
EXAMPLES::
394
395
sage: GammaH(5,[4])._latex_()
396
'\\Gamma_H(5, [4])'
397
"""
398
return '\\Gamma_H(%s, %s)' % (self.level(), self.__H)
399
400
def _list_of_elements_in_H(self):
401
"""
402
Returns a sorted list of Python ints that are representatives
403
between 1 and N-1 of the elements of H.
404
405
WARNING: Do not change this returned list.
406
407
EXAMPLES::
408
409
sage: G = GammaH(11,[3]); G
410
Congruence Subgroup Gamma_H(11) with H generated by [3]
411
sage: G._list_of_elements_in_H()
412
[1, 3, 4, 5, 9]
413
"""
414
return self.__Hlist
415
416
def is_even(self):
417
"""
418
Return True precisely if this subgroup contains the matrix -1.
419
420
EXAMPLES::
421
422
sage: GammaH(10, [3]).is_even()
423
True
424
sage: GammaH(14, [1]).is_even()
425
False
426
"""
427
if self.level() == 1:
428
return True
429
v = self._list_of_elements_in_H()
430
return int(self.level() - 1) in v
431
432
@cached_method
433
def generators(self, algorithm="farey"):
434
r"""
435
Return generators for this congruence subgroup. The result is cached.
436
437
INPUT:
438
439
- ``algorithm`` (string): either ``farey`` (default) or
440
``todd-coxeter``.
441
442
If ``algorithm`` is set to ``"farey"``, then the generators will be
443
calculated using Farey symbols, which will always return a *minimal*
444
generating set. See :mod:`~sage.modular.arithgroup.farey_symbol` for
445
more information.
446
447
If ``algorithm`` is set to ``"todd-coxeter"``, a simpler algorithm
448
based on Todd-Coxeter enumeration will be used. This tends to return
449
far larger sets of generators.
450
451
EXAMPLE::
452
453
sage: GammaH(7, [2]).generators()
454
[
455
[1 1] [ 2 -1] [ 4 -3]
456
[0 1], [ 7 -3], [ 7 -5]
457
]
458
sage: GammaH(7, [2]).generators(algorithm="todd-coxeter")
459
[
460
[1 1] [-90 29] [ 15 4] [-10 -3] [ 1 -1] [1 0] [1 1] [-3 -1]
461
[0 1], [301 -97], [-49 -13], [ 7 2], [ 0 1], [7 1], [0 1], [ 7 2],
462
<BLANKLINE>
463
[-13 4] [-5 -1] [-5 -2] [-10 3] [ 1 0] [ 9 -1] [-20 7]
464
[ 42 -13], [21 4], [28 11], [ 63 -19], [-7 1], [28 -3], [-63 22],
465
<BLANKLINE>
466
[1 0] [-3 -1] [ 15 -4] [ 2 -1] [ 22 -7] [-5 1] [ 8 -3]
467
[7 1], [ 7 2], [ 49 -13], [ 7 -3], [ 63 -20], [14 -3], [-21 8],
468
<BLANKLINE>
469
[11 5] [-13 -4]
470
[35 16], [-42 -13]
471
]
472
"""
473
if algorithm=="farey":
474
return self.farey_symbol().generators()
475
elif algorithm=="todd-coxeter":
476
from sage.modular.modsym.ghlist import GHlist
477
from congroup_pyx import generators_helper
478
level = self.level()
479
gen_list = generators_helper(GHlist(self), level, Mat2Z)
480
return [self(g, check=False) for g in gen_list]
481
else:
482
raise ValueError, "Unknown algorithm '%s' (should be either 'farey' or 'todd-coxeter')" % algorithm
483
484
def _coset_reduction_data_first_coord(G):
485
"""
486
Compute data used for determining the canonical coset
487
representative of an element of SL_2(Z) modulo G. This
488
function specifically returns data needed for the first part
489
of the reduction step (the first coordinate).
490
491
INPUT:
492
G -- a congruence subgroup Gamma_0(N), Gamma_1(N), or Gamma_H(N).
493
494
OUTPUT:
495
A list v such that
496
v[u] = (min(u*h: h in H),
497
gcd(u,N) ,
498
an h such that h*u = min(u*h: h in H)).
499
500
EXAMPLES::
501
502
sage: G = Gamma0(12)
503
sage: sage.modular.arithgroup.congroup_gammaH.GammaH_class._coset_reduction_data_first_coord(G)
504
[(0, 12, 0), (1, 1, 1), (2, 2, 1), (3, 3, 1), (4, 4, 1), (1, 1, 5), (6, 6, 1),
505
(1, 1, 7), (4, 4, 5), (3, 3, 7), (2, 2, 5), (1, 1, 11)]
506
"""
507
H = [ int(x) for x in G._list_of_elements_in_H() ]
508
N = int(G.level())
509
510
# Get some useful fast functions for inverse and gcd
511
inverse_mod = get_inverse_mod(N) # optimal inverse function
512
gcd = get_gcd(N) # optimal gcd function
513
514
# We will be filling this list in below.
515
reduct_data = [0] * N
516
517
# We can fill in 0 and all elements of H immediately
518
reduct_data[0] = (0,N,0)
519
for u in H:
520
reduct_data[u] = (1, 1, inverse_mod(u, N))
521
522
# Make a table of the reduction of H (mod N/d), one for each
523
# divisor d.
524
repr_H_mod_N_over_d = {}
525
for d in divisors(N):
526
# We special-case N == d because in this case,
527
# 1 % N_over_d is 0
528
if N == d:
529
repr_H_mod_N_over_d[d] = [1]
530
break
531
N_over_d = N//d
532
# For each element of H, we look at its image mod
533
# N_over_d. If we haven't yet seen it, add it on to
534
# the end of z.
535
w = [0] * N_over_d
536
z = [1]
537
for x in H:
538
val = x%N_over_d
539
if not w[val]:
540
w[val] = 1
541
z.append(x)
542
repr_H_mod_N_over_d[d] = z
543
544
# Compute the rest of the tuples. The values left to process
545
# are those where reduct_data has a 0. Note that several of
546
# these values are processed on each loop below, so re-index
547
# each time.
548
while True:
549
try:
550
u = reduct_data.index(0)
551
except ValueError:
552
break
553
d = gcd(u, N)
554
for x in repr_H_mod_N_over_d[d]:
555
reduct_data[(u*x)%N] = (u, d, inverse_mod(x,N))
556
557
return reduct_data
558
559
def _coset_reduction_data_second_coord(G):
560
"""
561
Compute data used for determining the canonical coset
562
representative of an element of SL_2(Z) modulo G. This
563
function specifically returns data needed for the second part
564
of the reduction step (the second coordinate).
565
566
INPUT:
567
self
568
569
OUTPUT:
570
a dictionary v with keys the divisors of N such that v[d]
571
is the subgroup {h in H : h = 1 (mod N/d)}.
572
573
EXAMPLES::
574
575
sage: G = GammaH(240,[7,239])
576
sage: G._coset_reduction_data_second_coord()
577
{1: [1], 2: [1], 3: [1], 4: [1], 5: [1, 49], 6: [1], 48: [1, 191], 8: [1], 80: [1, 7, 49, 103], 10: [1, 49], 12: [1], 15: [1, 49], 240: [1, 7, 49, 103, 137, 191, 233, 239], 40: [1, 7, 49, 103], 20: [1, 49], 24: [1, 191], 120: [1, 7, 49, 103, 137, 191, 233, 239], 60: [1, 49, 137, 233], 30: [1, 49, 137, 233], 16: [1]}
578
sage: G = GammaH(1200,[-1,7]); G
579
Congruence Subgroup Gamma_H(1200) with H generated by [7, 1199]
580
sage: K = G._coset_reduction_data_second_coord().keys() ; K.sort()
581
sage: K == divisors(1200)
582
True
583
"""
584
H = G._list_of_elements_in_H()
585
N = G.level()
586
v = { 1: [1] , N: H }
587
for d in [x for x in divisors(N) if x > 1 and x < N ]:
588
N_over_d = N // d
589
v[d] = [x for x in H if x % N_over_d == 1]
590
return v
591
592
@cached_method
593
def _coset_reduction_data(self):
594
"""
595
Compute data used for determining the canonical coset
596
representative of an element of SL_2(Z) modulo G.
597
598
EXAMPLES::
599
600
sage: G = GammaH(13, [-1]); G
601
Congruence Subgroup Gamma_H(13) with H generated by [12]
602
sage: G._coset_reduction_data()
603
([(0, 13, 0), (1, 1, 1), (2, 1, 1), (3, 1, 1), (4, 1, 1), (5, 1, 1), (6, 1, 1), (6, 1, 12), (5, 1, 12), (4, 1, 12), (3, 1, 12), (2, 1, 12), (1, 1, 12)], {1: [1], 13: [1, 12]})
604
"""
605
return (self._coset_reduction_data_first_coord(),
606
self._coset_reduction_data_second_coord())
607
608
609
def _reduce_coset(self, uu, vv):
610
r"""
611
Compute a canonical form for a given Manin symbol.
612
613
INPUT:
614
Two integers (uu,vv) that define an element of `(Z/NZ)^2`.
615
uu -- an integer
616
vv -- an integer
617
618
OUTPUT:
619
pair of integers that are equivalent to (uu,vv).
620
621
NOTE: We do *not* require that gcd(uu,vv,N) = 1. If the gcd is
622
not 1, we return (0,0).
623
624
EXAMPLES:
625
626
An example at level 9.::
627
628
sage: G = GammaH(9,[7]); G
629
Congruence Subgroup Gamma_H(9) with H generated by [7]
630
sage: a = []
631
sage: for i in range(G.level()):
632
... for j in range(G.level()):
633
... a.append(G._reduce_coset(i,j))
634
sage: v = list(set(a))
635
sage: v.sort()
636
sage: v
637
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (3, 1), (3, 2), (6, 1), (6, 2)]
638
639
An example at level 100::
640
641
sage: G = GammaH(100,[3,7]); G
642
Congruence Subgroup Gamma_H(100) with H generated by [3, 7]
643
sage: a = []
644
sage: for i in range(G.level()):
645
... for j in range(G.level()):
646
... a.append(G._reduce_coset(i,j))
647
sage: v = list(set(a))
648
sage: v.sort()
649
sage: len(v)
650
361
651
652
This demonstrates the problem underlying trac \#1220::
653
654
sage: G = GammaH(99, [67])
655
sage: G._reduce_coset(11,-3)
656
(11, 96)
657
sage: G._reduce_coset(77, -3)
658
(11, 96)
659
"""
660
N = int(self.level())
661
u = uu % N
662
v = vv % N
663
first, second = self._coset_reduction_data()
664
665
if gcd(first[u][1], first[v][1]) != 1:
666
return (0,0)
667
if not u:
668
return (0, first[v][0])
669
if not v:
670
return (first[u][0], 0)
671
672
new_u = first[u][0]
673
d = first[u][1]
674
new_v = (first[u][2] * v) % N
675
H_ls = second[d]
676
if len(H_ls) > 1:
677
new_v = min([ (new_v * h)%N for h in H_ls ])
678
679
return (new_u, new_v)
680
681
def reduce_cusp(self, c):
682
r"""
683
Compute a minimal representative for the given cusp c. Returns
684
a cusp c' which is equivalent to the given cusp, and is in
685
lowest terms with minimal positive denominator, and minimal
686
positive numerator for that denominator.
687
688
Two cusps `u_1/v_1` and `u_2/v_2` are equivalent modulo `\Gamma_H(N)`
689
if and only if
690
691
.. math::
692
693
v_1 = h v_2 \bmod N\quad \text{and}\quad u_1 = h^{-1} u_2 \bmod {\rm gcd}(v_1,N)
694
695
or
696
697
.. math::
698
699
v_1 = -h v_2 \bmod N\quad \text{and}\quad u_1 = -h^{-1} u_2 \bmod {\rm gcd}(v_1,N)
700
701
for some `h \in H`.
702
703
EXAMPLES::
704
705
sage: GammaH(6,[5]).reduce_cusp(5/3)
706
1/3
707
sage: GammaH(12,[5]).reduce_cusp(Cusp(8,9))
708
1/3
709
sage: GammaH(12,[5]).reduce_cusp(5/12)
710
Infinity
711
sage: GammaH(12,[]).reduce_cusp(Cusp(5,12))
712
5/12
713
sage: GammaH(21,[5]).reduce_cusp(Cusp(-9/14))
714
1/7
715
sage: Gamma1(5).reduce_cusp(oo)
716
Infinity
717
sage: Gamma1(5).reduce_cusp(0)
718
0
719
"""
720
return self._reduce_cusp(c)[0]
721
722
def _reduce_cusp(self, c):
723
r"""
724
Compute a minimal representative for the given cusp c.
725
Returns a pair (c', t), where c' is the minimal representative
726
for the given cusp, and t is either 1 or -1, as explained
727
below. Largely for internal use.
728
729
The minimal representative for a cusp is the element in `P^1(Q)`
730
in lowest terms with minimal positive denominator, and minimal
731
positive numerator for that denominator.
732
733
Two cusps `u1/v1` and `u2/v2` are equivalent modulo `\Gamma_H(N)`
734
if and only if
735
`v1 = h*v2 (mod N)` and `u1 = h^(-1)*u2 (mod gcd(v1,N))`
736
or
737
`v1 = -h*v2 (mod N)` and `u1 = -h^(-1)*u2 (mod gcd(v1,N))`
738
for some `h \in H`. Then t is 1 or -1 as c and c' fall into
739
the first or second case, respectively.
740
741
EXAMPLES::
742
743
sage: GammaH(6,[5])._reduce_cusp(Cusp(5,3))
744
(1/3, -1)
745
sage: GammaH(12,[5])._reduce_cusp(Cusp(8,9))
746
(1/3, -1)
747
sage: GammaH(12,[5])._reduce_cusp(Cusp(5,12))
748
(Infinity, 1)
749
sage: GammaH(12,[])._reduce_cusp(Cusp(5,12))
750
(5/12, 1)
751
sage: GammaH(21,[5])._reduce_cusp(Cusp(-9/14))
752
(1/7, 1)
753
"""
754
c = Cusp(c)
755
N = int(self.level())
756
Cusps = c.parent()
757
v = int(c.denominator() % N)
758
H = self._list_of_elements_in_H()
759
760
# First, if N | v, take care of this case. If u is in \pm H,
761
# then we return Infinity. If not, let u_0 be the minimum
762
# of \{ h*u | h \in \pm H \}. Then return u_0/N.
763
if not v:
764
u = c.numerator() % N
765
if u in H:
766
return Cusps((1,0)), 1
767
if (N-u) in H:
768
return Cusps((1,0)), -1
769
ls = [ (u*h)%N for h in H ]
770
m1 = min(ls)
771
m2 = N-max(ls)
772
if m1 < m2:
773
return Cusps((m1,N)), 1
774
else:
775
return Cusps((m2,N)), -1
776
777
u = int(c.numerator() % v)
778
gcd = get_gcd(N)
779
d = gcd(v,N)
780
781
# If (N,v) == 1, let v_0 be the minimal element
782
# in \{ v * h | h \in \pm H \}. Then we either return
783
# Infinity or 1/v_0, as v is or is not in \pm H,
784
# respectively.
785
if d == 1:
786
if v in H:
787
return Cusps((0,1)), 1
788
if (N-v) in H:
789
return Cusps((0,1)), -1
790
ls = [ (v*h)%N for h in H ]
791
m1 = min(ls)
792
m2 = N-max(ls)
793
if m1 < m2:
794
return Cusps((1,m1)), 1
795
else:
796
return Cusps((1,m2)), -1
797
798
val_min = v
799
inv_mod = get_inverse_mod(N)
800
801
# Now we're in the case (N,v) > 1. So we have to do several
802
# steps: first, compute v_0 as above. While computing this
803
# minimum, keep track of *all* pairs of (h,s) which give this
804
# value of v_0.
805
hs_ls = [(1,1)]
806
for h in H:
807
tmp = (v*h)%N
808
809
if tmp < val_min:
810
val_min = tmp
811
hs_ls = [(inv_mod(h,N), 1)]
812
elif tmp == val_min:
813
hs_ls.append((inv_mod(h,N), 1))
814
815
if (N-tmp) < val_min:
816
val_min = N - tmp
817
hs_ls = [(inv_mod(h,N), -1)]
818
elif (N-tmp) == val_min:
819
hs_ls.append((inv_mod(h,N), -1))
820
821
# Finally, we find our minimal numerator. Let u_1 be the
822
# minimum of s*h^-1*u mod d as (h,s) ranges over the elements
823
# of hs_ls. We must find the smallest integer u_0 which is
824
# smaller than v_0, congruent to u_1 mod d, and coprime to
825
# v_0. Then u_0/v_0 is our minimal representative.
826
u_min = val_min
827
sign = None
828
for h_inv,s in hs_ls:
829
tmp = (h_inv * s * u)%d
830
while gcd(tmp, val_min) > 1 and tmp < u_min:
831
tmp += d
832
if tmp < u_min:
833
u_min = tmp
834
sign = s
835
836
return Cusps((u_min, val_min)), sign
837
838
def _find_cusps(self):
839
r"""
840
Return an ordered list of inequivalent cusps for self, i.e. a
841
set of representatives for the orbits of self on
842
`\mathbf{P}^1(\QQ)`. These are returned in a reduced
843
form; see self.reduce_cusp for the definition of reduced.
844
845
ALGORITHM:
846
Lemma 3.2 in Cremona's 1997 book shows that for the action
847
of Gamma1(N) on "signed projective space"
848
`\Q^2 / (\Q_{\geq 0}^+)`, we have `u_1/v_1 \sim u_2 / v_2`
849
if and only if `v_1 = v_2 \bmod N` and `u_1 = u_2 \bmod
850
gcd(v_1, N)`. It follows that every orbit has a
851
representative `u/v` with `v \le N` and `0 \le u \le
852
gcd(v, N)`. We iterate through all pairs `(u,v)`
853
satisfying this.
854
855
Having found a set containing at least one of every
856
equivalence class modulo Gamma1(N), we can be sure of
857
picking up every class modulo GammaH(N) since this
858
contains Gamma1(N); and the reduce_cusp call does the
859
checking to make sure we don't get any duplicates.
860
861
EXAMPLES::
862
863
sage: Gamma1(5)._find_cusps()
864
[0, 2/5, 1/2, Infinity]
865
sage: Gamma1(35)._find_cusps()
866
[0, 2/35, 1/17, 1/16, 1/15, 1/14, 1/13, 1/12, 3/35, 1/11, 1/10, 1/9, 4/35, 1/8, 2/15, 1/7, 1/6, 6/35, 1/5, 3/14, 8/35, 1/4, 9/35, 4/15, 2/7, 3/10, 11/35, 1/3, 12/35, 5/14, 13/35, 2/5, 3/7, 16/35, 17/35, 1/2, 8/15, 4/7, 3/5, 9/14, 7/10, 5/7, 11/14, 4/5, 6/7, 9/10, 13/14, Infinity]
867
sage: Gamma1(24)._find_cusps() == Gamma1(24).cusps(algorithm='modsym')
868
True
869
sage: GammaH(24, [13,17])._find_cusps() == GammaH(24,[13,17]).cusps(algorithm='modsym')
870
True
871
"""
872
873
s = []
874
hashes = []
875
N = self.level()
876
877
for d in xrange(1, 1+N):
878
w = N.gcd(d)
879
M = int(w) if w > 1 else 2
880
for a in xrange(1,M):
881
if gcd(a, w) != 1:
882
continue
883
while gcd(a, d) != 1:
884
a += w
885
c = self.reduce_cusp(Cusp(a,d))
886
h = hash(c)
887
if not h in hashes:
888
hashes.append(h)
889
s.append(c)
890
return sorted(s)
891
892
def _contains_sl2(self, a,b,c,d):
893
r"""
894
Test whether [a,b,c,d] is an element of this subgroup.
895
896
EXAMPLES::
897
898
sage: G = GammaH(10, [3])
899
sage: [1, 0, -10, 1] in G
900
True
901
sage: matrix(ZZ, 2, [7, 1, 20, 3]) in G
902
True
903
sage: SL2Z.0 in G
904
False
905
sage: GammaH(10, [9])([7, 1, 20, 3]) # indirect doctest
906
Traceback (most recent call last):
907
...
908
TypeError: matrix [ 7 1]
909
[20 3] is not an element of Congruence Subgroup Gamma_H(10) with H generated by [9]
910
"""
911
N = self.level()
912
return ( (c%N == 0) and (d%N in self._list_of_elements_in_H()))
913
914
def gamma0_coset_reps(self):
915
r"""
916
Return a set of coset representatives for self \\ Gamma0(N), where N is
917
the level of self.
918
919
EXAMPLE::
920
921
sage: GammaH(108, [1,-1]).gamma0_coset_reps()
922
[
923
[1 0] [-43 -45] [ 31 33] [-49 -54] [ 25 28] [-19 -22]
924
[0 1], [108 113], [108 115], [108 119], [108 121], [108 125],
925
<BLANKLINE>
926
[-17 -20] [ 47 57] [ 13 16] [ 41 52] [ 7 9] [-37 -49]
927
[108 127], [108 131], [108 133], [108 137], [108 139], [108 143],
928
<BLANKLINE>
929
[-35 -47] [ 29 40] [ -5 -7] [ 23 33] [-11 -16] [ 53 79]
930
[108 145], [108 149], [108 151], [108 155], [108 157], [108 161]
931
]
932
"""
933
from all import SL2Z
934
N = self.level()
935
return [SL2Z(lift_to_sl2z(0, d.lift(), N)) for d in _GammaH_coset_helper(N, self._list_of_elements_in_H())]
936
937
def coset_reps(self):
938
r"""
939
Return a set of coset representatives for self \\ SL2Z.
940
941
EXAMPLES::
942
943
sage: list(Gamma1(3).coset_reps())
944
[
945
[1 0] [-1 -2] [ 0 -1] [-2 1] [1 0] [-3 -2] [ 0 -1] [-2 -3]
946
[0 1], [ 3 5], [ 1 0], [ 5 -3], [1 1], [ 8 5], [ 1 2], [ 5 7]
947
]
948
sage: len(list(Gamma1(31).coset_reps())) == 31**2 - 1
949
True
950
"""
951
from all import Gamma0, SL2Z
952
reps1 = Gamma0(self.level()).coset_reps()
953
for r in reps1:
954
reps2 = self.gamma0_coset_reps()
955
for t in reps2:
956
yield SL2Z(t)*r
957
958
959
def is_subgroup(self, other):
960
r"""
961
Return True if self is a subgroup of right, and False
962
otherwise.
963
964
EXAMPLES::
965
966
sage: GammaH(24,[7]).is_subgroup(SL2Z)
967
True
968
sage: GammaH(24,[7]).is_subgroup(Gamma0(8))
969
True
970
sage: GammaH(24, []).is_subgroup(GammaH(24, [7]))
971
True
972
sage: GammaH(24, []).is_subgroup(Gamma1(24))
973
True
974
sage: GammaH(24, [17]).is_subgroup(GammaH(24, [7]))
975
False
976
sage: GammaH(1371, [169]).is_subgroup(GammaH(457, [169]))
977
True
978
"""
979
980
from all import is_Gamma0, is_Gamma1
981
if not isinstance(other, GammaH_class):
982
raise NotImplementedError
983
984
# level of self should divide level of other
985
if self.level() % other.level():
986
return False
987
988
# easy cases
989
if is_Gamma0(other):
990
return True # recall self is a GammaH, so it's contained in Gamma0
991
992
if is_Gamma1(other) and len(self._generators_for_H()) > 0:
993
return False
994
995
else:
996
# difficult case
997
t = other._list_of_elements_in_H()
998
for x in self._generators_for_H():
999
if not (x in t):
1000
return False
1001
return True
1002
1003
1004
def index(self):
1005
r"""
1006
Return the index of self in SL2Z.
1007
1008
EXAMPLE::
1009
1010
sage: [G.index() for G in Gamma0(40).gamma_h_subgroups()]
1011
[72, 144, 144, 144, 144, 288, 288, 288, 288, 144, 288, 288, 576, 576, 144, 288, 288, 576, 576, 144, 288, 288, 576, 576, 288, 576, 1152]
1012
"""
1013
from all import Gamma1
1014
return Gamma1(self.level()).index() / len(self._list_of_elements_in_H())
1015
1016
def nu2(self):
1017
r"""
1018
Return the number of orbits of elliptic points of order 2 for this
1019
group.
1020
1021
EXAMPLE::
1022
1023
sage: [H.nu2() for n in [1..10] for H in Gamma0(n).gamma_h_subgroups()]
1024
[1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0]
1025
sage: GammaH(33,[2]).nu2()
1026
0
1027
sage: GammaH(5,[2]).nu2()
1028
2
1029
1030
AUTHORS:
1031
1032
- Jordi Quer
1033
1034
"""
1035
N = self.level()
1036
H = self._list_of_elements_in_H()
1037
if N % 4 == 0: return ZZ(0)
1038
for p, r in N.factor():
1039
if p % 4 == 3: return ZZ(0)
1040
return (euler_phi(N) // len(H))*len([x for x in H if (x**2 + 1) % N == 0])
1041
1042
def nu3(self):
1043
r"""
1044
Return the number of orbits of elliptic points of order 3 for this
1045
group.
1046
1047
EXAMPLE::
1048
1049
sage: [H.nu3() for n in [1..10] for H in Gamma0(n).gamma_h_subgroups()]
1050
[1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
1051
sage: GammaH(33,[2]).nu3()
1052
0
1053
sage: GammaH(7,[2]).nu3()
1054
2
1055
1056
AUTHORS:
1057
1058
- Jordi Quer
1059
1060
"""
1061
N = self.level()
1062
H = self._list_of_elements_in_H()
1063
if N % 9 == 0: return ZZ(0)
1064
for p, r in N.factor():
1065
if p % 3 == 2: return ZZ(0)
1066
lenHpm = len(H)
1067
if N - ZZ(1) not in H: lenHpm*=2
1068
return (euler_phi(N)//lenHpm)*len([x for x in H if (x**2+x+1) % N == 0])
1069
1070
def ncusps(self):
1071
r"""
1072
Return the number of orbits of cusps (regular or otherwise) for this subgroup.
1073
1074
EXAMPLE::
1075
1076
sage: GammaH(33,[2]).ncusps()
1077
8
1078
sage: GammaH(32079, [21676]).ncusps()
1079
28800
1080
1081
AUTHORS:
1082
1083
- Jordi Quer
1084
1085
"""
1086
N = self.level()
1087
H = self._list_of_elements_in_H()
1088
c = ZZ(0)
1089
for d in [d for d in N.divisors() if d**2 <= N]:
1090
Nd = lcm(d,N//d)
1091
Hd = set([x % Nd for x in H])
1092
lenHd = len(Hd)
1093
if Nd-1 not in Hd: lenHd *= 2
1094
summand = euler_phi(d)*euler_phi(N//d)//lenHd
1095
if d**2 == N:
1096
c = c + summand
1097
else:
1098
c = c + 2*summand
1099
return c
1100
1101
def nregcusps(self):
1102
r"""
1103
Return the number of orbits of regular cusps for this subgroup. A cusp is regular
1104
if we may find a parabolic element generating the stabiliser of that
1105
cusp whose eigenvalues are both +1 rather than -1. If G contains -1,
1106
all cusps are regular.
1107
1108
EXAMPLES::
1109
1110
sage: GammaH(20, [17]).nregcusps()
1111
4
1112
sage: GammaH(20, [17]).nirregcusps()
1113
2
1114
sage: GammaH(3212, [2045, 2773]).nregcusps()
1115
1440
1116
sage: GammaH(3212, [2045, 2773]).nirregcusps()
1117
720
1118
1119
AUTHOR:
1120
1121
- Jordi Quer
1122
"""
1123
if self.is_even():
1124
return self.ncusps()
1125
1126
N = self.level()
1127
H = self._list_of_elements_in_H()
1128
1129
c = ZZ(0)
1130
for d in [d for d in divisors(N) if d**2 <= N]:
1131
Nd = lcm(d,N//d)
1132
Hd = set([x%Nd for x in H])
1133
if Nd - 1 not in Hd:
1134
summand = euler_phi(d)*euler_phi(N//d)//(2*len(Hd))
1135
if d**2==N:
1136
c = c + summand
1137
else:
1138
c = c + 2*summand
1139
return c
1140
1141
def nirregcusps(self):
1142
r"""
1143
Return the number of irregular cusps for this subgroup.
1144
1145
EXAMPLES::
1146
1147
sage: GammaH(3212, [2045, 2773]).nirregcusps()
1148
720
1149
"""
1150
1151
return self.ncusps() - self.nregcusps()
1152
1153
def dimension_new_cusp_forms(self, k=2, p=0):
1154
r"""
1155
Return the dimension of the space of new (or `p`-new)
1156
weight `k` cusp forms for this congruence subgroup.
1157
1158
INPUT:
1159
1160
- ``k`` - an integer (default: 2), the weight. Not fully implemented for k = 1.
1161
- ``p`` - integer (default: 0); if nonzero, compute the `p`-new subspace.
1162
1163
OUTPUT: Integer
1164
1165
EXAMPLES::
1166
1167
sage: GammaH(33,[2]).dimension_new_cusp_forms()
1168
3
1169
sage: Gamma1(4*25).dimension_new_cusp_forms(2, p=5)
1170
225
1171
sage: Gamma1(33).dimension_new_cusp_forms(2)
1172
19
1173
sage: Gamma1(33).dimension_new_cusp_forms(2,p=11)
1174
21
1175
1176
"""
1177
N = self.level()
1178
if p==0 or N % p != 0:
1179
return sum([H.dimension_cusp_forms(k) * mumu(N // H.level()) \
1180
for H in self.divisor_subgroups()])
1181
else:
1182
return self.dimension_cusp_forms(k) - \
1183
2*self.restrict(N//p).dimension_new_cusp_forms(k)
1184
1185
def image_mod_n(self):
1186
r"""
1187
Return the image of this group in `SL(2, \ZZ / N\ZZ)`.
1188
1189
EXAMPLE::
1190
1191
sage: Gamma0(3).image_mod_n()
1192
Matrix group over Ring of integers modulo 3 with 2 generators (
1193
[2 0] [1 1]
1194
[0 2], [0 1]
1195
)
1196
1197
TEST::
1198
1199
sage: for n in [2..20]:
1200
... for g in Gamma0(n).gamma_h_subgroups():
1201
... G = g.image_mod_n()
1202
... assert G.order() == Gamma(n).index() / g.index()
1203
"""
1204
N = self.level()
1205
if N == 1:
1206
raise NotImplementedError, "Matrix groups over ring of integers modulo 1 not implemented"
1207
gens = [matrix(Zmod(N), 2, 2, [x, 0, 0, Zmod(N)(1)/x]) for x in self._generators_for_H()]
1208
gens += [matrix(Zmod(N),2,[1,1,0,1])]
1209
return MatrixGroup(gens)
1210
1211
def _list_subgroup(N, gens):
1212
r"""
1213
Given an integer ``N`` and a list of integers ``gens``, return a list of
1214
the elements of the subgroup of `(\ZZ / N\ZZ)^\times` generated by the
1215
elements of ``gens``.
1216
1217
EXAMPLE::
1218
1219
sage: sage.modular.arithgroup.congroup_gammaH._list_subgroup(11, [3])
1220
[1, 3, 4, 5, 9]
1221
"""
1222
H = set([1])
1223
N = int(N)
1224
for g in gens:
1225
if gcd(g, N) != 1:
1226
raise ValueError, "gen (=%s) is not in (Z/%sZ)^*"%(g,N)
1227
gk = int(g) % N
1228
sbgrp = [gk]
1229
while not (gk in H):
1230
gk = (gk * g)%N
1231
sbgrp.append(gk)
1232
H = set([(x*h)%N for x in sbgrp for h in H])
1233
H = list(H)
1234
H.sort()
1235
return H
1236
1237
def _GammaH_coset_helper(N, H):
1238
r"""
1239
Return a list of coset representatives for H in (Z / NZ)^*.
1240
1241
EXAMPLE::
1242
1243
sage: from sage.modular.arithgroup.congroup_gammaH import _GammaH_coset_helper
1244
sage: _GammaH_coset_helper(108, [1, 107])
1245
[1, 5, 7, 11, 13, 17, 19, 23, 25, 29, 31, 35, 37, 41, 43, 47, 49, 53]
1246
"""
1247
t = [Zmod(N)(1)]
1248
W = [Zmod(N)(h) for h in H]
1249
HH = [Zmod(N)(h) for h in H]
1250
k = euler_phi(N)
1251
1252
for i in xrange(1, N):
1253
if gcd(i, N) != 1: continue
1254
if not i in W:
1255
t.append(t[0]*i)
1256
W = W + [i*h for h in HH]
1257
if len(W) == k: break
1258
return t
1259
1260
def mumu(N):
1261
"""
1262
Return 0 if any cube divides `N`. Otherwise return
1263
`(-2)^v` where `v` is the number of primes that
1264
exactly divide `N`.
1265
1266
This is similar to the Moebius function.
1267
1268
INPUT:
1269
1270
1271
- ``N`` - an integer at least 1
1272
1273
1274
OUTPUT: Integer
1275
1276
EXAMPLES::
1277
1278
sage: from sage.modular.arithgroup.congroup_gammaH import mumu
1279
sage: mumu(27)
1280
0
1281
sage: mumu(6*25)
1282
4
1283
sage: mumu(7*9*25)
1284
-2
1285
sage: mumu(9*25)
1286
1
1287
"""
1288
if N < 1:
1289
raise ValueError, "N must be at least 1"
1290
p = 1
1291
for _,r in factor(N):
1292
if r > 2:
1293
return ZZ(0)
1294
elif r == 1:
1295
p *= -2
1296
return ZZ(p)
1297
1298