Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/modular/arithgroup/arithgroup_perm.py
4057 views
1
r"""
2
Arithmetic subgroups defined by permutations of cosets
3
4
A subgroup of finite index `H` of a finitely generated group `G` is completely
5
described by the action of a set of generators of `G` on the right cosets `H
6
\backslash G = \{Hg\}_{g \in G}`. After some arbitrary choice of numbering one
7
can identify the action of generators as elements of a symmetric group acting
8
transitively (and satisfying the relations of the relators in G). As `{\rm
9
SL}_2(\ZZ)` has a very simple presentation as a central extension of a free
10
product of cyclic groups, one can easily design algorithms from this point of
11
view.
12
13
The generators of `{\rm SL}_2(\ZZ)` used in this module are named as follows `s_2`,
14
`s_3`, `l`, `r` which are defined by
15
16
.. MATH::
17
18
s_2 = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix},\quad
19
s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad
20
l = \begin{pmatrix} 1 & 1 \\ 0 & 1 \end{pmatrix},\quad
21
r = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}.
22
23
Those generators satisfy the following relations
24
25
.. MATH::
26
27
s_2^2 = s_3^3 = -1, \quad r = s_2^{-1}\ l^{-1}\ s_2.
28
29
In particular not all four are needed to generate the whole group `{\rm
30
SL}_2(\ZZ)`. Three couples which generate `{\rm SL}_2(\ZZ)` are of particular
31
interest:
32
33
- `(l,r)` as they are also semigroup generators for the semigroup of matrices
34
in `{\rm SL}_2(\ZZ)` with non-negative entries,
35
- `(l,s_2)` as they are closely related to the continued fraction algorithm,
36
- `(s_2,s_3)` as the group `{\rm PSL}_2(\ZZ)` is the free product of the finite
37
cyclic groups generated by these two elements.
38
39
Part of these functions are based on Chris Kurth's *KFarey* package [Kur08]_.
40
For tests see the file ``sage.modular.arithgroup.tests``.
41
42
REFERENCES:
43
44
.. [AtSD71] A. O. L. Atkin and H. P. F. Swinnerton-Dyer, "Modular forms on
45
noncongruence subgroups", Proc. Symp. Pure Math., Combinatorics (T. S. Motzkin,
46
ed.), vol. 19, AMS, Providence 1971
47
48
.. [Hsu96] Tim Hsu, "Identifying congruence subgroups of the modular group",
49
Proc. AMS 124, no. 5, 1351-1359 (1996)
50
51
.. [Hsu97] Tim Hsu, "Permutation techniques for coset representations of modular
52
subgroups", in L. Schneps (ed.), Geometric Galois Actions II: Dessins
53
d'Enfants, Mapping Class Groups and Moduli, volume 243 of LMS Lect. Notes,
54
67-77, Cambridge Univ. Press (1997)
55
56
.. [Go09] Alexey G. Gorinov, "Combinatorics of double cosets and fundamental
57
domains for the subgroups of the modular group", preprint
58
http://arxiv.org/abs/0901.1340
59
60
.. [KSV11] Ian Kiming, Matthias Schuett and Helena Verrill, "Lifts of
61
projective congruence groups", J. London Math. Soc. (2011) 83 (1): 96-120,
62
http://dx.doi.org/10.1112/jlms/jdq062. Arxiv version:
63
http://arxiv.org/abs/0905.4798.
64
65
.. [Kul91] Ravi Kulkarni "An arithmetic geometric method in the study of the
66
subgroups of the modular group", American Journal of Mathematics 113 (1991),
67
no 6, 1053-1133
68
69
.. [Kur08] Chris Kurth, "K Farey package for Sage",
70
http://www.public.iastate.edu/~kurthc/research/index.html
71
72
.. [KuLo] Chris Kurth and Ling Long, "Computations with finite index subgroups
73
of `{\rm PSL}_2(\ZZ)` using Farey symbols"
74
75
.. [Ve] Helena Verrill, "Fundamental domain drawer", Java program,
76
http://www.math.lsu.edu/~verrill/
77
78
TODO:
79
80
- modular Farey symbols
81
82
- computation of generators of a modular subgroup with a standard surface
83
group presentation. In other words, compute a presentation of the form
84
85
.. MATH::
86
87
\langle x_i,y_i,c_j |\ \prod_i [x_i,y_i] \prod_j c_j^{\nu_j} = 1\rangle
88
89
where the elements `x_i` and `y_i` are hyperbolic and `c_j` are parabolic
90
(`\nu_j=\infty`) or elliptic elements (`\nu_j < \infty`).
91
92
- computation of centralizer.
93
94
- generation of modular (even) subgroups of fixed index.
95
96
AUTHORS:
97
98
- Chris Kurth (2008): created KFarey package
99
100
- David Loeffler (2009): adapted functions from KFarey for inclusion into Sage
101
102
- Vincent Delecroix (2010): implementation for odd groups, new design,
103
improvements, documentation
104
105
- David Loeffler (2011): congruence testing for odd subgroups, enumeration of
106
liftings of projective subgroups
107
"""
108
109
################################################################################
110
#
111
# Copyright (C) 2009, The Sage Group -- http://www.sagemath.org/
112
#
113
# Distributed under the terms of the GNU General Public License (GPL)
114
#
115
# The full text of the GPL is available at:
116
#
117
# http://www.gnu.org/licenses/
118
#
119
################################################################################
120
121
from all import SL2Z
122
from arithgroup_generic import ArithmeticSubgroup
123
from arithgroup_element import ArithmeticSubgroupElement
124
from sage.rings.all import Zmod, ZZ
125
from sage.rings.infinity import Infinity
126
from sage.misc.cachefunc import cached_method
127
import sage.rings.arith as arith
128
129
from sage.groups.perm_gps.permgroup_element import PermutationGroupElement
130
131
132
Idm = SL2Z([1,0,0,1]) # identity
133
134
Lm = SL2Z([1,1,0,1]) # parabolic that fixes infinity
135
Rm = SL2Z([1,0,1,1]) # parabolic that fixes 0
136
S2m = SL2Z([0,-1,1,0]) # elliptic of order 2 (fix i)
137
S3m = SL2Z([0,1,-1,1]) # elliptic of order 3 (fix j)
138
139
S2mi = SL2Z([0,1,-1,0]) # the inverse of S2m in SL(2,Z)
140
S3mi = SL2Z([1,-1,1,0]) # the inverse of S3m in SL(2,Z)
141
Lmi = SL2Z([1,-1,0,1]) # the inverse of Lm in SL(2,Z)
142
Rmi = SL2Z([1,0,-1,1]) # the inverse of Rm in SL(2,Z)
143
144
def sl2z_word_problem(A):
145
r"""
146
Given an element of `{\rm SL}_2(\ZZ)`, express it as a word in the generators L =
147
[1,1,0,1] and R = [1,0,1,1].
148
149
The return format is a list of pairs ``(a,b)``, where ``a = 0`` or ``1``
150
denoting ``L`` or ``R`` respectively, and ``b`` is an integer exponent.
151
152
See also the function :func:`eval_sl2z_word`.
153
154
EXAMPLES::
155
156
sage: from sage.modular.arithgroup.arithgroup_perm import eval_sl2z_word, sl2z_word_problem
157
sage: m = SL2Z([1,0,0,1])
158
sage: eval_sl2z_word(sl2z_word_problem(m)) == m
159
True
160
sage: m = SL2Z([0,-1,1,0])
161
sage: eval_sl2z_word(sl2z_word_problem(m)) == m
162
True
163
sage: m = SL2Z([7,8,-50,-57])
164
sage: eval_sl2z_word(sl2z_word_problem(m)) == m
165
True
166
"""
167
A = SL2Z(A)
168
output=[]
169
170
## If A00 is zero
171
if A[0,0]==0:
172
c=A[1,1]
173
if c != 1:
174
A=A*Lm**(c-1)*Rm*Lmi
175
output.extend([(0,1-c),(1,-1),(0,1)])
176
else:
177
A=A*Rm*Lmi
178
output.extend([(1,-1),(0,1)])
179
180
if A[0,0]<0: # Make sure A00 is positive
181
A=SL2Z(-1)*A
182
output.extend([(1,-1), (0,1), (1,-1), (0,1), (1,-1), (0,1)])
183
184
if A[0,1]<0: # if A01 is negative make it positive
185
n=(-A[0,1]/A[0,0]).ceil() #n s.t. 0 <= A[0,1]+n*A[0,0] < A[0,0]
186
A=A*Lm**n
187
output.append((0, -n))
188
## At this point A00>0 and A01>=0
189
while not (A[0,0]==0 or A[0,1]==0):
190
if A[0,0]>A[0,1]:
191
n=(A[0,0]/A[0,1]).floor()
192
A=A*SL2Z([1,0,-n,1])
193
output.append((1, n))
194
195
else: #A[0,0]<=A[0,1]
196
n=(A[0,1]/A[0,0]).floor()
197
A=A*SL2Z([1,-n,0,1])
198
output.append((0, n))
199
200
if A==SL2Z(1):
201
pass #done, so don't add R^0
202
elif A[0,0]==0:
203
c=A[1,1]
204
if c != 1:
205
A=A*Lm**(c-1)*Rm*Lmi
206
output.extend([(0,1-c),(1,-1),(0, 1)])
207
else:
208
A=A*Rm*Lmi
209
output.extend([(1,-1),(0,1)])
210
else:
211
c=A[1,0]
212
if c:
213
A=A*Rm**(-c)
214
output.append((1,c))
215
216
output.reverse()
217
return output
218
219
def eval_sl2z_word(w):
220
r"""
221
Given a word in the format output by :func:`sl2z_word_problem`, convert it back
222
into an element of `{\rm SL}_2(\ZZ)`.
223
224
EXAMPLES::
225
226
sage: from sage.modular.arithgroup.arithgroup_perm import eval_sl2z_word
227
sage: eval_sl2z_word([(0, 1), (1, -1), (0, 0), (1, 3), (0, 2), (1, 9), (0, -1)])
228
[ 66 -59]
229
[ 47 -42]
230
"""
231
from sage.all import prod
232
mat = [Lm,Rm]
233
w0 = Idm
234
w1 = w
235
return w0 * prod((mat[a[0]]**a[1] for a in w1), Idm)
236
237
def word_of_perms(w, p1, p2):
238
r"""
239
Given a word `w` as a list of 2-tuples ``(index,power)`` and permutations
240
``p1`` and ``p2`` return the product of ``p1`` and ``p2`` that corresponds
241
to ``w``.
242
243
EXAMPLES::
244
245
sage: import sage.modular.arithgroup.arithgroup_perm as ap
246
sage: S2 = SymmetricGroup(4)
247
sage: p1 = S2('(1,2)(3,4)')
248
sage: p2 = S2('(1,2,3,4)')
249
sage: ap.word_of_perms([(1,1),(0,1)], p1, p2) == p2 * p1
250
True
251
sage: ap.word_of_perms([(0,1),(1,1)], p1, p2) == p1 * p2
252
True
253
"""
254
if not isinstance(p1,PermutationGroupElement):
255
p1 = PermutationGroupElement(p1)
256
if not isinstance(p2,PermutationGroupElement):
257
p2 = PermutationGroupElement(p2)
258
259
G = p1.parent()
260
if G != p2.parent(): # find a minimal parent
261
G2 = p2.parent()
262
if G.has_coerce_map_from(G2):
263
p2 = G(p2)
264
elif G2.has_coerce_map_from(G):
265
G = G2
266
p1 = G(p1)
267
else:
268
from sage.groups.perm_gps.all import PermutationGroup
269
G = PermutationGroup([p1,p2])
270
p1 = G(p1)
271
p2 = G(p2)
272
273
M = G.identity()
274
p = [p1, p2]
275
m = [p1.order(),p2.order()]
276
277
for i,j in w:
278
M *= p[i]**(j%m[i])
279
280
return M
281
282
def _equalize_perms(l):
283
r"""
284
Transform a list of lists into a list of lists with identical length. Each
285
list ``p`` in the argument is completed with ``range(len(p),n)`` where
286
``n`` is the maximal length of the lists in ``l``. Note that the lists are
287
modified in-place (rather than returning new lists).
288
289
EXAMPLES::
290
291
sage: from sage.modular.arithgroup.arithgroup_perm import _equalize_perms
292
sage: l = [[],[1,0],[3,0,1,2]]
293
sage: _equalize_perms(l)
294
sage: l
295
[[0, 1, 2, 3], [1, 0, 2, 3], [3, 0, 1, 2]]
296
"""
297
n=max(map(len,l))
298
if n ==0:
299
n = 1
300
for p in l:
301
p.extend(xrange(len(p),n))
302
303
# Tedious point: in order to unpickle pickled objects from prior to patch
304
# #11422, this function needs to accept two non-keyword arguments, to be
305
# interpreted as L and R. Hence the order of the arguments is slightly
306
# different from the class __init__ methods.
307
308
def ArithmeticSubgroup_Permutation(
309
L=None, R=None, S2=None, S3=None,
310
relabel=False,
311
check=True):
312
r"""
313
Construct a subgroup of `{\rm SL}_2(\ZZ)` from the action of generators on its
314
right cosets.
315
316
Return an arithmetic subgroup knowing the action, given by permutations, of
317
at least two standard generators on the its cosets. The generators
318
considered are the following matrices:
319
320
.. math::
321
322
s_2 = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix},\quad
323
s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad
324
l = \begin{pmatrix} 1 & 1 \\ 0 & 1\end{pmatrix},\quad
325
r = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}.
326
327
An error will be raised if only one permutation is given. If no arguments
328
are given at all, the full modular group `{\rm SL}(2, \ZZ)` is returned.
329
330
INPUT:
331
332
- ``S2``, ``S3``, ``L``, ``R`` - permutations - action of matrices on the
333
right cosets (each coset is identified to an element of `\{1,\dots,n\}`
334
where `1` is reserved for the identity coset).
335
336
- ``relabel`` - boolean (default: False) - if True, renumber the cosets in a
337
canonical way.
338
339
- ``check`` - boolean (default: True) - check that the input is valid (it
340
may be time efficient but less safe to set it to False)
341
342
EXAMPLES::
343
344
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,3)")
345
sage: G
346
Arithmetic subgroup with permutations of right cosets
347
S2=(1,2)(3,4)
348
S3=(1,2,3)
349
L=(1,4,3)
350
R=(2,4,3)
351
sage: G.index()
352
4
353
354
sage: G = ArithmeticSubgroup_Permutation(); G
355
Arithmetic subgroup with permutations of right cosets
356
S2=()
357
S3=()
358
L=()
359
R=()
360
sage: G == SL2Z
361
True
362
363
Some invalid inputs::
364
365
sage: ArithmeticSubgroup_Permutation(S2="(1,2)")
366
Traceback (most recent call last):
367
...
368
ValueError: Need at least two generators
369
sage: ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(3,4,5)")
370
Traceback (most recent call last):
371
...
372
ValueError: Permutations do not generate a transitive group
373
sage: ArithmeticSubgroup_Permutation(L="(1,2)",R="(1,2,3)")
374
Traceback (most recent call last):
375
...
376
ValueError: Wrong relations between generators
377
sage: ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="()")
378
Traceback (most recent call last):
379
...
380
ValueError: S2^2 does not equal to S3^3
381
sage: ArithmeticSubgroup_Permutation(S2="(1,4,2,5,3)", S3="(1,3,5,2,4)")
382
Traceback (most recent call last):
383
...
384
ValueError: S2^2 = S3^3 must have order 1 or 2
385
386
The input checks can be disabled for speed::
387
388
sage: ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(3,4,5)", check=False) # don't do this!
389
Arithmetic subgroup with permutations of right cosets
390
S2=(1,2)
391
S3=(3,4,5)
392
L=(1,2)(3,5,4)
393
R=(1,2)(3,4,5)
394
"""
395
gens = filter(lambda x: x is not None, [S2,S3,L,R])
396
if len(gens) == 0:
397
S2 = S3 = L = R = ''
398
elif len(gens) < 2:
399
raise ValueError, "Need at least two generators"
400
401
if S2 is not None:
402
S2 = PermutationGroupElement(S2,check=check)
403
if S3 is not None:
404
S3 = PermutationGroupElement(S3,check=check)
405
if L is not None:
406
L = PermutationGroupElement(L,check=check)
407
if R is not None:
408
R = PermutationGroupElement(R,check=check)
409
410
if L is not None:
411
if R is not None: # initialize from L,R
412
if S2 is None:
413
S2 = R * ~L * R
414
if S3 is None:
415
S3 = L * ~R
416
elif S2 is not None: # initialize from L,S2
417
if S3 is None:
418
S3 = ~S2 * ~L
419
if R is None:
420
R = ~S2 * ~L * S2
421
elif S3 is not None: # initialize from L,S3
422
if S2 is None:
423
S2 = ~L * ~S3
424
if R is None:
425
R = S3 * ~L * ~S3
426
elif R is not None:
427
if S2 is not None: # initialize from R, S2
428
if L is None:
429
L = ~S2 * ~R * S2
430
if S3 is None:
431
S3 = R * ~S2
432
elif S3 is not None: # initialize from R, S3
433
if L is None:
434
L = ~S3 * ~R * S3
435
if S2 is None:
436
S2 = ~S3 * R
437
else: # intialize from S2, S3
438
if L is None:
439
L = ~S3 * ~S2
440
if R is None:
441
R = S3 * S2
442
443
if check and (L != ~S3 * ~S2 or R != S3 * S2):
444
raise ValueError, "Wrong relations between generators"
445
446
inv = S2*S2
447
448
if check:
449
if inv != S3*S3*S3:
450
raise ValueError, "S2^2 does not equal to S3^3"
451
elif not (inv*inv).is_one():
452
raise ValueError, "S2^2 = S3^3 must have order 1 or 2"
453
454
# Check transitivity. This is the most expensive check, so we do it
455
# last.
456
from sage.groups.perm_gps.all import PermutationGroup
457
458
G = PermutationGroup(gens)
459
if not G.is_transitive():
460
raise ValueError, "Permutations do not generate a transitive group"
461
462
s2 = [i-1 for i in S2.list()]
463
s3 = [i-1 for i in S3.list()]
464
l = [i-1 for i in L.list()]
465
r = [i-1 for i in R.list()]
466
_equalize_perms((s2,s3,l,r))
467
468
if inv.is_one(): # the group is even
469
G = EvenArithmeticSubgroup_Permutation(s2,s3,l,r)
470
else: # the group is odd
471
G = OddArithmeticSubgroup_Permutation(s2,s3,l,r)
472
473
if relabel:
474
G.relabel()
475
476
return G
477
478
class ArithmeticSubgroup_Permutation_class(ArithmeticSubgroup):
479
r"""
480
A subgroup of `{\rm SL}_2(\ZZ)` defined by the action of generators on its
481
coset graph.
482
483
The class stores the action of generators `s_2, s_3, l, r` on right cosets
484
`Hg` of a finite index subgroup `H < {\rm SL}_2(\ZZ)`. In particular the action of
485
`{\rm SL}_2(\ZZ)` on the cosets is on right.
486
487
.. MATH::
488
489
s_2 = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix},\quad
490
s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad
491
l = \begin{pmatrix} 1 & 1 \\ 0 & 1\end{pmatrix},\quad
492
r = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}.
493
494
TEST::
495
496
sage: s2 = PermutationGroupElement('(1,2)(3,4)(5,6)')
497
sage: s3 = PermutationGroupElement('(1,3,5)(2,4,6)')
498
sage: G = ArithmeticSubgroup_Permutation(S2=s2, S3=s3)
499
sage: G.S2() == s2
500
True
501
sage: G.S3() == s3
502
True
503
sage: G == ArithmeticSubgroup_Permutation(L=G.L(), R=G.R())
504
True
505
sage: G == ArithmeticSubgroup_Permutation(L=G.L(), S2=G.S2())
506
True
507
sage: G == ArithmeticSubgroup_Permutation(L=G.L(), S3=G.S3())
508
True
509
sage: G == ArithmeticSubgroup_Permutation(R=G.R(), S2=G.S2())
510
True
511
sage: G == ArithmeticSubgroup_Permutation(R=G.R(), S3=G.S3())
512
True
513
sage: G == ArithmeticSubgroup_Permutation(S2=G.S2(), S3=G.S3())
514
True
515
516
sage: G = ArithmeticSubgroup_Permutation(S2='',S3='')
517
sage: G == loads(dumps(G))
518
True
519
520
sage: S2 = '(1,2)(3,4)(5,6)'
521
sage: S3 = '(1,2,3)(4,5,6)'
522
sage: G = ArithmeticSubgroup_Permutation(S2=S2, S3=S3)
523
sage: loads(dumps(G)) == G
524
True
525
"""
526
def __eq__(self, other):
527
r"""
528
Equality test.
529
530
TESTS::
531
532
sage: G2 = Gamma(2)
533
sage: G3 = Gamma(3)
534
sage: H = ArithmeticSubgroup_Permutation(S2="(1,4)(2,6)(3,5)",S3="(1,2,3)(4,5,6)")
535
sage: (G2 == H) and (H == G2)
536
True
537
sage: (G3 == H) or (H == G3)
538
False
539
540
sage: G2 = Gamma1(2)
541
sage: G3 = Gamma1(3)
542
sage: H = ArithmeticSubgroup_Permutation(S2="(1,6,4,3)(2,7,5,8)",S3="(1,2,3,4,5,6)(7,8)")
543
sage: (G2 == H) or (H == G2)
544
False
545
sage: (G3 == H) and (H == G3)
546
True
547
"""
548
if isinstance(other, ArithmeticSubgroup_Permutation_class):
549
550
return (self.is_odd() == other.is_odd() and
551
self.index() == other.index() and
552
self.relabel(inplace=False)._S2 == other.relabel(inplace=False)._S2 and
553
self.relabel(inplace=False)._S3 == other.relabel(inplace=False)._S3)
554
555
elif isinstance(other, ArithmeticSubgroup):
556
return self == other.as_permutation_group()
557
558
raise NotImplemented
559
560
def __hash__(self):
561
r"""
562
Return a hash value.
563
564
TESTS::
565
566
sage: G1 = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)(5,6)',S3='(1,2,3)(4,5,6)')
567
sage: G2 = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)(5,6)',S3='(1,5,6)(4,2,3)')
568
sage: G1.__hash__() == G2.__hash__()
569
False
570
"""
571
return hash((tuple(self.relabel(inplace=False)._S2),tuple(self.relabel(inplace=False)._S3)))
572
573
def _repr_(self):
574
r"""
575
String representation of self.
576
577
EXAMPLES::
578
579
sage: G = Gamma(2).as_permutation_group()
580
sage: repr(G) #indirect doctest
581
'Arithmetic subgroup with permutations of right cosets\n S2=(1,4)(2,6)(3,5)\n S3=(1,2,3)(4,5,6)\n L=(1,5)(2,4)(3,6)\n R=(1,6)(2,5)(3,4)'
582
sage: G = Gamma(3).as_permutation_group()
583
sage: repr(G) #indirect doctest
584
'Arithmetic subgroup of index 24'
585
"""
586
if self.index() < 20:
587
return "Arithmetic subgroup with permutations of right cosets\n S2=%s\n S3=%s\n L=%s\n R=%s" %(
588
self.S2(), self.S3(), self.L(), self.R())
589
590
else:
591
return "Arithmetic subgroup of index %d" %self.index()
592
593
#
594
# Attribute access
595
#
596
597
def S2(self):
598
r"""
599
Return the action of the matrix `s_2` as a permutation of cosets.
600
601
.. MATH::
602
603
s_2 = \begin{pmatrix}0&-1\\1&0\end{pmatrix}
604
605
EXAMPLES::
606
607
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")
608
sage: G.S2()
609
(1,2)
610
"""
611
return PermutationGroupElement([i+1 for i in self._S2], check=False)
612
613
def S3(self):
614
r"""
615
Return the action of the matrix `s_3` as a permutation of cosets.
616
617
.. MATH::
618
619
s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad
620
621
EXAMPLES::
622
623
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")
624
sage: G.S3()
625
(1,2,3)
626
"""
627
628
return PermutationGroupElement([i+1 for i in self._S3], check=False)
629
630
def L(self):
631
r"""
632
Return the action of the matrix `l` as a permutation of cosets.
633
634
.. MATH::
635
636
l = \begin{pmatrix}1&1\\0&1\end{pmatrix}
637
638
EXAMPLES::
639
640
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")
641
sage: G.L()
642
(1,3)
643
"""
644
return PermutationGroupElement([i+1 for i in self._L], check=False)
645
646
def R(self):
647
r"""
648
Return the action of the matrix `r` as a permutation of cosets.
649
650
.. MATH::
651
652
r = \begin{pmatrix}1&0\\1&1\end{pmatrix}
653
654
EXAMPLES::
655
656
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")
657
sage: G.R()
658
(2,3)
659
"""
660
return PermutationGroupElement([i+1 for i in self._R], check=False)
661
662
def perm_group(self):
663
r"""
664
Return the underlying permutation group.
665
666
The permutation group returned is isomorphic to the action of the
667
generators `s_2` (element of order two), `s_3` (element of order 3), `l`
668
(parabolic element) and `r` (parabolic element) on right cosets (the
669
action is on the right).
670
671
EXAMPLE::
672
673
sage: import sage.modular.arithgroup.arithgroup_perm as ap
674
sage: ap.HsuExample10().perm_group()
675
Permutation Group with generators [(1,2)(3,4)(5,6)(7,8)(9,10), (1,4)(2,5,9,10,8)(3,7,6), (1,7,9,10,6)(2,3)(4,5,8), (1,8,3)(2,4,6)(5,7,10)]
676
"""
677
from sage.groups.perm_gps.all import PermutationGroup
678
return PermutationGroup([self.S2(), self.S3(), self.L(), self.R()])
679
680
def index(self):
681
r"""
682
Returns the index of this modular subgroup in the full modular group.
683
684
EXAMPLES::
685
686
sage: G = Gamma(2)
687
sage: P = G.as_permutation_group()
688
sage: P.index()
689
6
690
sage: G.index() == P.index()
691
True
692
693
sage: G = Gamma0(8)
694
sage: P = G.as_permutation_group()
695
sage: P.index()
696
12
697
sage: G.index() == P.index()
698
True
699
700
sage: G = Gamma1(6)
701
sage: P = G.as_permutation_group()
702
sage: P.index()
703
24
704
sage: G.index() == P.index()
705
True
706
"""
707
return len(self._S2)
708
709
#
710
# Canonical renumbering
711
#
712
713
def relabel(self, inplace=True):
714
r"""
715
Relabel the cosets of this modular subgroup in a canonical way.
716
717
The implementation of modular subgroup by action of generators on cosets
718
depends on the choice of a numbering. This function provides
719
canonical labels in the sense that two equal subgroups whith different
720
labels are relabeled the same way. The default implementation relabels
721
the group itself. If you want to get a relabeled copy of your modular
722
subgroup, put to ``False`` the option ``inplace``.
723
724
ALGORITHM:
725
726
We give an overview of how the canonical labels for the modular subgroup
727
are built. The procedure only uses the permutations `S3` and `S2` that
728
define the modular subgroup and can be used to renumber any
729
transitive action of the symmetric group. In other words, the algorithm
730
construct a canonical representative of a transitive subgroup in its
731
conjugacy class in the full symmetric group.
732
733
1. The identity is still numbered `0` and set the curent vertex to be
734
the identity.
735
736
2. Number the cycle of `S3` the current vertex belongs to: if the
737
current vertex is labeled `n` then the numbering in such way that the
738
cycle becomes `(n, n+1, \ldots, n+k)`).
739
740
3. Find a new current vertex using the permutation `S2`.
741
If all vertices are relabeled then it's done, otherwise go to step 2.
742
743
EXAMPLES::
744
745
sage: S2 = "(1,2)(3,4)(5,6)"; S3 = "(1,2,3)(4,5,6)"
746
sage: G1 = ArithmeticSubgroup_Permutation(S2=S2,S3=S3); G1
747
Arithmetic subgroup with permutations of right cosets
748
S2=(1,2)(3,4)(5,6)
749
S3=(1,2,3)(4,5,6)
750
L=(1,4,5,3)
751
R=(2,4,6,3)
752
sage: G1.relabel(); G1
753
Arithmetic subgroup with permutations of right cosets
754
S2=(1,2)(3,4)(5,6)
755
S3=(1,2,3)(4,5,6)
756
L=(1,4,5,3)
757
R=(2,4,6,3)
758
759
sage: S2 = "(1,2)(3,5)(4,6)"; S3 = "(1,2,3)(4,5,6)"
760
sage: G2 = ArithmeticSubgroup_Permutation(S2=S2,S3=S3); G2
761
Arithmetic subgroup with permutations of right cosets
762
S2=(1,2)(3,5)(4,6)
763
S3=(1,2,3)(4,5,6)
764
L=(1,5,6,3)
765
R=(2,5,4,3)
766
sage: G2.relabel(); G2
767
Arithmetic subgroup with permutations of right cosets
768
S2=(1,2)(3,4)(5,6)
769
S3=(1,2,3)(4,5,6)
770
L=(1,4,5,3)
771
R=(2,4,6,3)
772
773
sage: S2 = "(1,2)(3,6)(4,5)"; S3 = "(1,2,3)(4,5,6)"
774
sage: G3 = ArithmeticSubgroup_Permutation(S2=S2,S3=S3); G3
775
Arithmetic subgroup with permutations of right cosets
776
S2=(1,2)(3,6)(4,5)
777
S3=(1,2,3)(4,5,6)
778
L=(1,6,4,3)
779
R=(2,6,5,3)
780
sage: G4 = G3.relabel(inplace=False)
781
sage: G4
782
Arithmetic subgroup with permutations of right cosets
783
S2=(1,2)(3,4)(5,6)
784
S3=(1,2,3)(4,5,6)
785
L=(1,4,5,3)
786
R=(2,4,6,3)
787
sage: G3 is G4
788
False
789
790
TESTS::
791
792
sage: S2 = "(1,2)(3,6)(4,5)"
793
sage: S3 = "(1,2,3)(4,5,6)"
794
sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
795
sage: H = G.relabel(inplace=False)
796
sage: G is H
797
False
798
sage: G._S2 is H._S2 or G._S3 is H._S3 or G._L is H._L or G._R is H._R
799
False
800
sage: G.relabel(inplace=False) is H
801
True
802
sage: H.relabel(inplace=False) is H
803
True
804
sage: G.relabel(); G
805
Arithmetic subgroup with permutations of right cosets
806
S2=(1,2)(3,4)(5,6)
807
S3=(1,2,3)(4,5,6)
808
L=(1,4,5,3)
809
R=(2,4,6,3)
810
sage: G.relabel(inplace=False) is G
811
True
812
"""
813
if hasattr(self,'_canonical_label_group'):
814
if inplace:
815
if not (self is self._canonical_label_group):
816
self.__dict__ = self._canonical_label_group.__dict__
817
self._canonical_label_group = self
818
else:
819
return self._canonical_label_group
820
821
if inplace:
822
G = self
823
else:
824
from copy import deepcopy
825
G = deepcopy(self)
826
827
n = G.index()
828
mapping = G._canonical_rooted_labels()
829
S2 = G._S2
830
S3 = G._S3
831
L = G._L
832
R = G._R
833
G._S2 = [None]*n
834
G._S3 = [None]*n
835
G._L = [None]*n
836
G._R = [None]*n
837
838
for i in xrange(n):
839
G._S2[mapping[i]] = mapping[S2[i]]
840
G._S3[mapping[i]] = mapping[S3[i]]
841
G._L[mapping[i]] = mapping[L[i]]
842
G._R[mapping[i]] = mapping[R[i]]
843
844
self._canonical_label_group = G
845
G._canonical_label_group = G
846
847
if not inplace:
848
return G
849
850
def _canonical_unrooted_labels(self):
851
r"""
852
Returns the smallest label among rooted label
853
854
OUTPUT:
855
856
A 3-tuple of lists corresponding to permutations. The first list is the
857
mapping that gives the canonical labels and the second and third one
858
correspond to the permutations obtained by the conjugation of ``S2`` and
859
``S3``.
860
861
EXAMPLES::
862
863
sage: S2 = "(1,2)(4,5)"
864
sage: S3 = "(1,3,4)(2,5,6)"
865
sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
866
sage: s2,s3 = G._S2,G._S3
867
sage: m,ss2,ss3 = G._canonical_unrooted_labels()
868
sage: all(ss2[m[i]] == m[s2[i]] for i in xrange(6))
869
True
870
sage: all(ss3[m[i]] == m[s3[i]] for i in xrange(6))
871
True
872
873
sage: S2 = "(1,2)(3,4)(5,6)"
874
sage: S3 = "(1,3,4)(2,5,6)"
875
sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
876
sage: s2,s3 = G._S2,G._S3
877
sage: m,ss2,ss3 = G._canonical_unrooted_labels()
878
sage: all(ss2[m[i]] == m[s2[i]] for i in xrange(6))
879
True
880
sage: all(ss3[m[i]] == m[s3[i]] for i in xrange(6))
881
True
882
883
sage: S2 = "(1,2)(3,4)(5,6)"
884
sage: S3 = "(1,3,5)(2,4,6)"
885
sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
886
sage: s2,s3 = G._S2,G._S3
887
sage: m,ss2,ss3 = G._canonical_unrooted_labels()
888
sage: all(ss2[m[i]] == m[s2[i]] for i in xrange(6))
889
True
890
sage: all(ss3[m[i]] == m[s3[i]] for i in xrange(6))
891
True
892
"""
893
n = self.index()
894
S2_win = [None]*n; S3_win = [None]*n
895
S2_test = [None]*n; S3_test = [None]*n
896
897
m_win = self._canonical_rooted_labels(0)
898
for i in xrange(n): # conjugation
899
S2_win[m_win[i]] = m_win[self._S2[i]]
900
S3_win[m_win[i]] = m_win[self._S3[i]]
901
902
for j0 in xrange(1,self.index()):
903
m_test = self._canonical_rooted_labels(j0)
904
for i in xrange(n):
905
S2_test[m_test[i]] = m_test[self._S2[i]]
906
S3_test[m_test[i]] = m_test[self._S3[i]]
907
908
for i in xrange(n-1):
909
if (S2_test[i] < S2_win[i] or
910
(S2_test[i] == S2_win[i] and S3_test[i] < S3_win[i])):
911
S2_win,S2_test = S2_test,S2_win
912
S3_win,S3_test = S3_test,S3_win
913
m_win = m_test
914
break
915
916
return m_win, S2_win, S3_win
917
918
def _canonical_rooted_labels(self, j0=0):
919
r"""
920
Return the permutation which correspond to the renumbering of self in
921
order to get canonical labels.
922
923
If ``j0`` is 0 then the renumbering corresponds to the same group. If
924
not, the renumbering corresponds to the conjugated subgroup such that
925
``j0`` becomes the identity coset.
926
927
EXAMPLES::
928
929
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")
930
sage: G._canonical_rooted_labels(0)
931
[0, 1, 2]
932
sage: G._canonical_rooted_labels(1)
933
[2, 0, 1]
934
sage: G._canonical_rooted_labels(2)
935
[1, 2, 0]
936
"""
937
x = self._S3
938
y = self._S2
939
n = len(x)
940
941
k = 0
942
seen = [True] * n
943
mapping = [None] * n
944
waiting = []
945
946
while True:
947
# intialize at j0
948
mapping[j0] = k
949
waiting.append(j0)
950
k += 1
951
# complete x cycle from j0
952
j = x[j0]
953
while j != j0:
954
mapping[j] = k
955
waiting.append(j)
956
k += 1
957
j = x[j]
958
959
# if everybody is labelled do not go further
960
if k == n: break
961
962
# find another guy with y
963
j0 = y[waiting.pop(0)]
964
while mapping[j0] is not None:
965
j0 = y[waiting.pop(0)]
966
967
return mapping
968
969
#
970
# Contains and random element
971
#
972
973
@cached_method
974
def _index_to_lr_cusp_width(self):
975
r"""
976
Precomputation of cusps data of self for this modular subgroup.
977
978
This is a central precomputation for the ``.__contains__()`` method and
979
consists in two lists of positive integers ``lc`` and ``rc`` of length
980
the index of the subgroup. They are defined as follows: the number
981
``lc[i]`` (resp ``rc[i]``) is the lenth of the cycle of ``L`` (resp.
982
``R``) which contains ``i``.
983
984
EXAMPLES::
985
986
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")
987
sage: G.L()
988
(1,3)
989
sage: G.R()
990
(2,3)
991
sage: G._index_to_lr_cusp_width()
992
([2, 1, 2], [1, 2, 2])
993
"""
994
G = self.relabel(inplace=False)
995
996
l = G.L()
997
l_cycle_length = [None]*self.index()
998
for c in l.cycle_tuples(singletons=True):
999
for i in c:
1000
l_cycle_length[i-1]=len(c)
1001
1002
r = G.R()
1003
r_cycle_length = [None]*self.index()
1004
for c in r.cycle_tuples(singletons=True):
1005
for i in c:
1006
r_cycle_length[i-1]=len(c)
1007
1008
return (l_cycle_length, r_cycle_length)
1009
1010
def __contains__(self, x):
1011
r"""
1012
Test whether ``x`` is in the group or not.
1013
1014
ALGORITHM:
1015
1016
An element of `{\rm SL}_2(\ZZ)` is in a given modular subgroup if it does not
1017
permute the identity coset!
1018
1019
TEST::
1020
1021
sage: G = Gamma(4)
1022
sage: m1 = G([1,4,0,1])
1023
sage: m2 = G([17,4,4,1])
1024
sage: m3 = G([1,-4,-4,17])
1025
sage: m4 = SL2Z([1,2,0,1])
1026
sage: P = G.as_permutation_group()
1027
sage: m1 in P
1028
True
1029
sage: m2 in P
1030
True
1031
sage: m3 in P
1032
True
1033
sage: m4 in P
1034
False
1035
"""
1036
#if x.parent() is self or x.parent() == self: return True
1037
if x not in SL2Z: return False
1038
1039
w = sl2z_word_problem(x)
1040
1041
perms = [self.relabel(inplace=False)._L,self.relabel(inplace=False)._R]
1042
widths = self._index_to_lr_cusp_width()
1043
1044
k = 0
1045
for (i,j) in w:
1046
for _ in xrange(j % widths[i][k]):
1047
k = perms[i][k]
1048
1049
return not k
1050
1051
def random_element(self, initial_steps=30):
1052
r"""
1053
Returns a random element in this subgroup.
1054
1055
The algorithm uses a random walk on the Cayley graph of `{\rm SL}_2(\ZZ)` stopped
1056
at the first time it reaches the subgroup after at least
1057
``initial_steps`` steps.
1058
1059
INPUT:
1060
1061
- ``initial_steps`` - positive integer (default: 30)
1062
1063
EXAMPLES::
1064
1065
sage: G = ArithmeticSubgroup_Permutation(S2='(1,3)(4,5)',S3='(1,2,5)(3,4,6)')
1066
sage: all(G.random_element() in G for _ in xrange(10))
1067
True
1068
"""
1069
from sage.misc.prandom import randint
1070
1071
i = 0
1072
m = SL2Z(1)
1073
for _ in xrange(initial_steps):
1074
j = randint(0,1)
1075
if j == 0:
1076
i = self._S2[i]
1077
m = m*S2m
1078
else:
1079
i = self._S3[i]
1080
m = m*S3m
1081
1082
while i != 0:
1083
j = randint(0,1)
1084
if j == 0:
1085
i = self._S2[i]
1086
m = m*S2m
1087
else:
1088
i = self._S3[i]
1089
m = m*S3m
1090
1091
return m
1092
1093
def permutation_action(self, x):
1094
r"""
1095
Given an element ``x`` of `{\rm SL}_2(\ZZ)`, compute the permutation of the
1096
cosets of self given by right multiplication by ``x``.
1097
1098
EXAMPLE::
1099
1100
sage: import sage.modular.arithgroup.arithgroup_perm as ap
1101
sage: ap.HsuExample10().permutation_action(SL2Z([32, -21, -67, 44]))
1102
(1,4,6,2,10,5,3,7,8,9)
1103
"""
1104
return word_of_perms(sl2z_word_problem(x), self.L(), self.R())
1105
1106
def __call__(self, g, check=True):
1107
r"""
1108
Create an element of this group from ``g``. If check=True (the default),
1109
perform the (possibly rather computationally-intensive) check to make
1110
sure ``g`` is in this group.
1111
1112
EXAMPLE::
1113
1114
sage: import sage.modular.arithgroup.arithgroup_perm as ap
1115
sage: m = SL2Z([1,1,0,1])
1116
sage: m in ap.HsuExample10()
1117
False
1118
sage: ap.HsuExample10()(m)
1119
Traceback (most recent call last):
1120
...
1121
TypeError: The element is not in group
1122
sage: ap.HsuExample10()(m, check=False)
1123
[1 1]
1124
[0 1]
1125
"""
1126
g = SL2Z(g, check=check)
1127
if not check or g in self:
1128
return g
1129
raise TypeError, "The element is not in group"
1130
1131
#
1132
# Group stuff
1133
#
1134
1135
def is_normal(self):
1136
r"""
1137
Test whether the group is normal
1138
1139
EXAMPLES::
1140
1141
sage: G = Gamma(2).as_permutation_group()
1142
sage: G.is_normal()
1143
True
1144
1145
sage: G = Gamma1(2).as_permutation_group()
1146
sage: G.is_normal()
1147
False
1148
"""
1149
N = self.index()
1150
G = self.relabel(inplace=False)
1151
s2 = G._S2
1152
s3 = G._S3
1153
ss2 = [None]*N
1154
ss3 = [None]*N
1155
for j in [s2[0],s3[0]]:
1156
m = G._canonical_rooted_labels(j)
1157
for i in xrange(N):
1158
ss2[m[i]] = m[s2[i]]
1159
ss3[m[i]] = m[s3[i]]
1160
if s2 != ss2 or s3 != ss3:
1161
return False
1162
return True
1163
1164
def _conjugate(self,j0):
1165
r"""
1166
Return the conjugate of self rooted at j0.
1167
1168
EXAMPLES::
1169
1170
sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)(4,5,6)')
1171
sage: G
1172
Arithmetic subgroup with permutations of right cosets
1173
S2=(1,2)(3,4)
1174
S3=(1,2,3)(4,5,6)
1175
L=(1,4,6,5,3)
1176
R=(2,4,5,6,3)
1177
sage: G._conjugate(0) == G
1178
True
1179
sage: G._conjugate(4)
1180
Arithmetic subgroup with permutations of right cosets
1181
S2=(3,4)(5,6)
1182
S3=(1,2,3)(4,5,6)
1183
L=(1,4,5,3,2)
1184
R=(1,2,4,6,3)
1185
"""
1186
N = self.index()
1187
s2 = self._S2
1188
s3 = self._S3
1189
l = self._L
1190
r = self._R
1191
ss2 = [None]*N
1192
ss3 = [None]*N
1193
ll = [None]*N
1194
rr = [None]*N
1195
1196
m = self._canonical_rooted_labels(j0)
1197
for i in xrange(N):
1198
ss2[m[i]] = m[s2[i]]
1199
ss3[m[i]] = m[s3[i]]
1200
ll[m[i]] = m[l[i]]
1201
rr[m[i]] = m[r[i]]
1202
return self.__class__(ss2,ss3,ll,rr,True)
1203
1204
def coset_graph(self,
1205
right_cosets=False,
1206
s2_edges=True, s3_edges=True, l_edges=False, r_edges=False,
1207
s2_label='s2', s3_label='s3', l_label='l', r_label='r'):
1208
r"""
1209
Return the right (or left) coset graph.
1210
1211
INPUT:
1212
1213
- ``right_cosets`` - bool (default: False) - right or left coset graph
1214
1215
- ``s2_edges`` - bool (default: True) - put edges associated to s2
1216
1217
- ``s3_edges`` - bool (default: True) - put edges associated to s3
1218
1219
- ``l_edges`` - bool (default: False) - put edges associated to l
1220
1221
- ``r_edges`` - bool (default: False) - put edges associated to r
1222
1223
- ``s2_label``, ``s3_label``, ``l_label``, ``r_label`` - the labels to
1224
put on the edges corresponding to the generators action. Use ``None``
1225
for no label.
1226
1227
EXAMPLES::
1228
1229
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="()")
1230
sage: G
1231
Arithmetic subgroup with permutations of right cosets
1232
S2=(1,2)
1233
S3=()
1234
L=(1,2)
1235
R=(1,2)
1236
sage: G.index()
1237
2
1238
sage: G.coset_graph()
1239
Looped multi-digraph on 2 vertices
1240
"""
1241
from sage.graphs.digraph import DiGraph
1242
res = DiGraph(multiedges=True,loops=True)
1243
res.add_vertices(range(self.index()))
1244
1245
1246
if right_cosets: # invert the permutations
1247
S2 = [None]*self.index()
1248
S3 = [None]*self.index()
1249
L = [None]*self.index()
1250
R = [None]*self.index()
1251
for i in xrange(self.index()):
1252
S2[self._S2[i]] = i
1253
S3[self._S3[i]] = i
1254
L[self._L[i]] = i
1255
R[self._R[i]] = i
1256
1257
else:
1258
S2 = self._S2
1259
S3 = self._S3
1260
L = self._L
1261
R = self._R
1262
1263
if s2_edges:
1264
if s2_label is not None:
1265
res.add_edges((i,S2[i],s2_label) for i in xrange(self.index()))
1266
else:
1267
res.add_edges((i,S2[i]) for i in xrange(self.index()))
1268
1269
if s3_edges:
1270
if s3_label is not None:
1271
res.add_edges((i,S3[i],s3_label) for i in xrange(self.index()))
1272
else:
1273
res.add_edges((i,S3) for i in xrange(self.index()))
1274
1275
if l_edges:
1276
if l_label is not None:
1277
res.add_edges((i,L[i],l_label) for i in xrange(self.index()))
1278
else:
1279
res.add_edges((i,L[i]) for i in xrange(self.index()))
1280
1281
if r_edges:
1282
if r_label is not None:
1283
res.add_edges((i,R[i],r_label) for i in xrange(self.index()))
1284
else:
1285
res.add_edges((i,R[i]) for i in xrange(self.index()))
1286
1287
res.plot.options['color_by_label'] = True
1288
1289
if s2_label or s3_label or l_label or r_label:
1290
res.plot.options['edge_labels'] = True
1291
1292
return res
1293
1294
def generalised_level(self):
1295
r"""
1296
Return the generalised level of this subgroup.
1297
1298
The *generalised level* of a subgroup of the modular group is the least
1299
common multiple of the widths of the cusps. It was proven by Wohlfart
1300
that for even congruence subgroups, the (conventional) level coincides
1301
with the generalised level. For odd congruence subgroups the level is
1302
either the generalised level, or twice the generalised level [KSV11]_.
1303
1304
EXAMPLES::
1305
1306
sage: G = Gamma(2).as_permutation_group()
1307
sage: G.generalised_level()
1308
2
1309
sage: G = Gamma0(3).as_permutation_group()
1310
sage: G.generalised_level()
1311
3
1312
"""
1313
return arith.lcm(self.cusp_widths())
1314
1315
def congruence_closure(self):
1316
r"""
1317
Returns the smallest congruence subgroup containing self. If self is
1318
congruence, this is just self, but represented as a congruence subgroup
1319
data type. If self is not congruence, then it may be larger.
1320
1321
In practice, we use the following criterion: let `m` be the generalised
1322
level of self. If this subgroup is even, let `n = m`, else let `n =
1323
2m`. Then any congruence subgroup containing self contains `\Gamma(n)`
1324
(a generalisation of Wohlfahrt's theorem due to Kiming, Verrill and
1325
Schuett). So we compute the image of self modulo `n` and return the
1326
preimage of that.
1327
1328
EXAMPLE::
1329
1330
sage: Gamma1(3).as_permutation_group().congruence_closure()
1331
Congruence subgroup of SL(2,Z) of level 3, preimage of:
1332
Matrix group over Ring of integers modulo 3 with 2 generators:
1333
[[[1, 2], [0, 1]], [[1, 1], [0, 1]]]
1334
sage: sage.modular.arithgroup.arithgroup_perm.HsuExample10().congruence_closure() # long time (40 sec)
1335
Modular Group SL(2,Z)
1336
"""
1337
if self.is_even():
1338
N = self.generalised_level()
1339
else:
1340
N = 2*self.generalised_level()
1341
1342
from congroup_generic import CongruenceSubgroup_constructor as CS
1343
return CS(N, [x.matrix() for x in self.gens()])
1344
1345
class OddArithmeticSubgroup_Permutation(ArithmeticSubgroup_Permutation_class):
1346
def __init__(self, S2, S3, L, R, canonical_labels=False):
1347
r"""
1348
TESTS::
1349
1350
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")
1351
sage: G
1352
Arithmetic subgroup with permutations of right cosets
1353
S2=(1,2,3,4)
1354
S3=(1,3)(2,4)
1355
L=(1,2,3,4)
1356
R=(1,4,3,2)
1357
sage: loads(dumps(G)) == G
1358
True
1359
"""
1360
self._S2 = S2
1361
self._S3 = S3
1362
self._L = L
1363
self._R = R
1364
if canonical_labels:
1365
self._canonical_label_group = self
1366
1367
def __reduce__(self):
1368
r"""
1369
Return the data used to construct self. Used in pickling.
1370
1371
TESTS::
1372
1373
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")
1374
sage: G == loads(dumps(G)) #indirect doctest
1375
True
1376
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)",relabel=True)
1377
sage: GG = loads(dumps(G))
1378
sage: GG == G #indirect doctest
1379
True
1380
sage: GG.relabel(inplace=False) is GG
1381
True
1382
"""
1383
if hasattr(self,'_canonical_label_group'):
1384
canonical_labels = (self is self._canonical_label_group)
1385
else:
1386
canonical_labels = False
1387
return (OddArithmeticSubgroup_Permutation,
1388
(self._S2,self._S3,self._L,self._R,canonical_labels))
1389
1390
def is_odd(self):
1391
r"""
1392
Test whether the group is odd.
1393
1394
EXAMPLES::
1395
1396
sage: G = ArithmeticSubgroup_Permutation(S2="(1,6,4,3)(2,7,5,8)",S3="(1,2,3,4,5,6)(7,8)")
1397
sage: G.is_odd()
1398
True
1399
"""
1400
return True
1401
1402
def is_even(self):
1403
r"""
1404
Test whether the group is even.
1405
1406
EXAMPLES::
1407
1408
sage: G = ArithmeticSubgroup_Permutation(S2="(1,6,4,3)(2,7,5,8)",S3="(1,2,3,4,5,6)(7,8)")
1409
sage: G.is_even()
1410
False
1411
"""
1412
return False
1413
1414
def to_even_subgroup(self,relabel=True):
1415
r"""
1416
Returns the group with `-Id` added in it.
1417
1418
EXAMPLES::
1419
1420
sage: G = Gamma1(3).as_permutation_group()
1421
sage: G.to_even_subgroup()
1422
Arithmetic subgroup with permutations of right cosets
1423
S2=(1,3)(2,4)
1424
S3=(1,2,3)
1425
L=(2,3,4)
1426
R=(1,4,2)
1427
1428
sage: H = ArithmeticSubgroup_Permutation(S2 = '(1,4,11,14)(2,7,12,17)(3,5,13,15)(6,9,16,19)(8,10,18,20)', S3 = '(1,2,3,11,12,13)(4,5,6,14,15,16)(7,8,9,17,18,19)(10,20)')
1429
sage: G = H.to_even_subgroup(relabel=False); G
1430
Arithmetic subgroup with permutations of right cosets
1431
S2=(1,4)(2,7)(3,5)(6,9)(8,10)
1432
S3=(1,2,3)(4,5,6)(7,8,9)
1433
L=(1,5)(2,4,9,10,8)(3,7,6)
1434
R=(1,7,10,8,6)(2,5,9)(3,4)
1435
sage: H.is_subgroup(G)
1436
True
1437
"""
1438
N = self.index()
1439
1440
# build equivalence classes in e
1441
s2 = self._S2
1442
e = []
1443
e2i = [None]*N
1444
for i in xrange(N):
1445
j = s2[s2[i]]
1446
if i < j:
1447
e2i[i] = e2i[j] = len(e)
1448
e.append((i,j))
1449
1450
# build the quotient permutations
1451
ss2 = [None]*(N/2)
1452
ss3 = [None]*(N/2)
1453
ll = [None]*(N/2)
1454
rr = [None]*(N/2)
1455
1456
s3 = self._S3
1457
l = self._L
1458
r = self._R
1459
for (j0,j1) in e:
1460
ss2[e2i[j0]] = e2i[s2[j0]]
1461
ss3[e2i[j0]] = e2i[s3[j0]]
1462
ll[e2i[j0]] = e2i[l[j0]]
1463
rr[e2i[j0]] = e2i[r[j0]]
1464
1465
G = EvenArithmeticSubgroup_Permutation(ss2,ss3,ll,rr)
1466
if relabel:
1467
G.relabel()
1468
return G
1469
1470
def nu2(self):
1471
r"""
1472
Return the number of elliptic points of order 2.
1473
1474
EXAMPLES::
1475
1476
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")
1477
sage: G.nu2()
1478
0
1479
1480
sage: G = Gamma1(2).as_permutation_group()
1481
sage: G.nu2()
1482
1
1483
"""
1484
return sum(1 for c in self.S2().cycle_tuples() if len(c) == 2)
1485
1486
def nu3(self):
1487
r"""
1488
Return the number of elliptic points of order 3.
1489
1490
EXAMPLES::
1491
1492
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")
1493
sage: G.nu3()
1494
2
1495
1496
sage: G = Gamma1(3).as_permutation_group()
1497
sage: G.nu3()
1498
1
1499
"""
1500
return sum(1 for c in self.S3().cycle_tuples() if len(c) == 2)
1501
1502
def nirregcusps(self):
1503
r"""
1504
Return the number of irregular cusps.
1505
1506
The cusps are associated to cycles of the permutations `L` or `R`.
1507
The irregular cusps are the one which are stabilised by `-Id`.
1508
1509
EXAMPLES::
1510
1511
sage: S2 = "(1,3,2,4)(5,7,6,8)(9,11,10,12)"
1512
sage: S3 = "(1,3,5,2,4,6)(7,9,11,8,10,12)"
1513
sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
1514
sage: G.nirregcusps()
1515
3
1516
"""
1517
inv = self.S2()**2
1518
n = 0
1519
for c in self.L().cycle_tuples(singletons=True):
1520
if inv(c[0]) in c:
1521
n += 1
1522
return n
1523
1524
def nregcusps(self):
1525
r"""
1526
Return the number of regular cusps of the group.
1527
1528
The cusps are associated to cycles of `L` or `R`. The irregular cusps
1529
correspond to the ones which are not stabilised by `-Id`.
1530
1531
EXAMPLES::
1532
1533
sage: G = Gamma1(3).as_permutation_group()
1534
sage: G.nregcusps()
1535
2
1536
"""
1537
inv = self.S2()**2
1538
n = 0
1539
for c in self.L().cycle_tuples(singletons=True):
1540
if inv(c[0]) not in c:
1541
n += 1
1542
return n//2
1543
1544
def cusp_widths(self,exp=False):
1545
r"""
1546
Return the list of cusp widths.
1547
1548
INPUT:
1549
1550
``exp`` - boolean (default: False) - if True, return a dictionnary with
1551
keys the possible widths and with values the number of cusp with that
1552
width.
1553
1554
EXAMPLES::
1555
1556
sage: G = Gamma1(5).as_permutation_group()
1557
sage: G.cusp_widths()
1558
[1, 1, 5, 5]
1559
sage: G.cusp_widths(exp=True)
1560
{1: 2, 5: 2}
1561
"""
1562
inv = self.S2()**2
1563
L = self.L()
1564
cusps = set(c[0] for c in L.cycle_tuples(singletons=True))
1565
if exp:
1566
widths = {}
1567
else:
1568
widths = []
1569
1570
while cusps:
1571
c0 = cusps.pop()
1572
c = L.orbit(c0)
1573
if inv(c0) not in c:
1574
c1 = min(L.orbit(inv(c0)))
1575
cusps.remove(c1)
1576
if exp:
1577
if not len(c) in widths:
1578
widths[len(c)] = 0
1579
widths[len(c)] += 1
1580
else:
1581
widths.append(len(c))
1582
else:
1583
if exp:
1584
if not len(c)/2 in widths:
1585
widths[len(c)/2] = 0
1586
widths[len(c)/2] += 1
1587
else:
1588
widths.append(len(c)/2)
1589
1590
if exp:
1591
return widths
1592
return sorted(widths)
1593
1594
def ncusps(self):
1595
r"""
1596
Returns the number of cusps.
1597
1598
EXAMPLES::
1599
1600
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")
1601
sage: G.ncusps()
1602
1
1603
1604
sage: G = Gamma1(3).as_permutation_group()
1605
sage: G.ncusps()
1606
2
1607
"""
1608
inv = self.S2()**2
1609
n = 0
1610
m = 0
1611
for c in self.L().cycle_tuples(singletons=True):
1612
if inv(c[0]) in c:
1613
n += 1
1614
else:
1615
m += 1
1616
return n + m//2
1617
1618
def is_congruence(self):
1619
r"""
1620
Test whether self is a congruence group, i.e.~whether or not it
1621
contains the subgroup `\Gamma(n)` for some `n`.
1622
1623
For odd groups, we first test whether the group generated by `G` and
1624
`-1` is congruence, and then use a theorem of Kiming, Schuett and
1625
Verrill [KSV11]_, which shows that an odd subgroup is congruence if and
1626
only if it contains `\Gamma(N)` where `N` is twice its generalised
1627
level (the least common multiple of its cusp widths). We can therefore
1628
proceed by calculating the index of the subgroup of `{\rm SL}(2, \ZZ /
1629
N\ZZ)` generated by the gens of self, and checking whether or not it
1630
has the same index as self.
1631
1632
EXAMPLES::
1633
1634
sage: GammaH(11,[4]).as_permutation_group().is_congruence()
1635
True
1636
1637
The following example (taken from [KSV11]_) shows that it may be the
1638
case that G is not congruence, even if its image in `{\rm PSL}(2,\ZZ)`
1639
is congruence::
1640
1641
sage: S2 = "(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23)"
1642
sage: S3 = "(1,14,15,13,2,3)(4,5,6,16,17,18)(7,8,9,19,20,21)(10,11,12,22,23,24)"
1643
sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
1644
sage: G.is_congruence()
1645
False
1646
sage: G.to_even_subgroup().is_congruence()
1647
True
1648
1649
In fact G is a lifting to `{\rm SL}(2,\ZZ)` of the group
1650
`\bar{\Gamma}_0(6)`::
1651
1652
sage: G.to_even_subgroup() == Gamma0(6)
1653
True
1654
"""
1655
Gev = self.to_even_subgroup()
1656
if not Gev.is_congruence():
1657
return False
1658
else:
1659
N = 2*self.generalised_level()
1660
from sage.groups.matrix_gps.matrix_group import MatrixGroup
1661
H = MatrixGroup([x.matrix().change_ring(Zmod(N)) for x in self.gens()])
1662
from congroup_gamma import Gamma_constructor as Gamma
1663
return Gamma(N).index() == self.index() * H.order()
1664
1665
class EvenArithmeticSubgroup_Permutation(ArithmeticSubgroup_Permutation_class):
1666
r"""
1667
An arithmetic subgroup `\Gamma` defined by two permutations, giving the
1668
action of four standard generators
1669
1670
.. MATH::
1671
1672
s_2 = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix},\quad
1673
s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad
1674
l = \begin{pmatrix} 1 & 1 \\ 0 & 1\end{pmatrix},\quad
1675
r = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}.
1676
1677
by right multiplication on the coset representatives `\Gamma \backslash {\rm SL}_2(\ZZ)`.
1678
1679
1680
EXAMPLES:
1681
1682
Construct a noncongruence subgroup of index 7 (the smallest possible)::
1683
1684
sage: a2 = SymmetricGroup(7)([(1,2),(3,4),(6,7)]); a3 = SymmetricGroup(7)([(1,2,3),(4,5,6)])
1685
sage: G = ArithmeticSubgroup_Permutation(S2=a2, S3=a3); G
1686
Arithmetic subgroup with permutations of right cosets
1687
S2=(1,2)(3,4)(6,7)
1688
S3=(1,2,3)(4,5,6)
1689
L=(1,4,7,6,5,3)
1690
R=(2,4,5,7,6,3)
1691
sage: G.index()
1692
7
1693
sage: G.dimension_cusp_forms(4)
1694
1
1695
sage: G.is_congruence()
1696
False
1697
1698
Convert some standard congruence subgroups into permutation form::
1699
1700
sage: G = Gamma0(8).as_permutation_group()
1701
sage: G.index()
1702
12
1703
sage: G.is_congruence()
1704
True
1705
1706
sage: G = Gamma0(12).as_permutation_group()
1707
sage: print G
1708
Arithmetic subgroup of index 24
1709
sage: G.is_congruence()
1710
True
1711
1712
The following is the unique index 2 even subgroup of `{\rm SL}_2(\ZZ)`::
1713
1714
sage: w = SymmetricGroup(2)([2,1])
1715
sage: G = ArithmeticSubgroup_Permutation(L=w, R=w)
1716
sage: G.dimension_cusp_forms(6)
1717
1
1718
sage: G.genus()
1719
0
1720
"""
1721
def __init__(self, S2, S3, L, R, canonical_labels=False):
1722
r"""
1723
TESTS::
1724
1725
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)(5,6)",S3="(1,2,3)(4,5,6)")
1726
sage: G == loads(dumps(G))
1727
True
1728
sage: G is loads(dumps(G))
1729
False
1730
"""
1731
self._S2 = S2
1732
self._S3 = S3
1733
self._L = L
1734
self._R = R
1735
if canonical_labels:
1736
self._canonical_label_group = self
1737
1738
def __reduce__(self):
1739
r"""
1740
Data for pickling.
1741
1742
TESTS::
1743
1744
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,4)")
1745
sage: G == loads(dumps(G)) #indirect doctest
1746
True
1747
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,4)",relabel=True)
1748
sage: GG = loads(dumps(G))
1749
sage: G == GG #indirect doctest
1750
True
1751
sage: GG.relabel(inplace=False) is GG
1752
True
1753
"""
1754
if hasattr(self, '_canonical_label_group'):
1755
canonical_labels = (self is self._canonical_label_group)
1756
else:
1757
canonical_labels = False
1758
return (EvenArithmeticSubgroup_Permutation,
1759
(self._S2, self._S3, self._L, self._R, canonical_labels))
1760
1761
def is_odd(self):
1762
r"""
1763
Returns True if this subgroup does not contain the matrix `-Id`.
1764
1765
EXAMPLES::
1766
1767
sage: G = Gamma(2).as_permutation_group()
1768
sage: G.is_odd()
1769
False
1770
"""
1771
return False
1772
1773
def is_even(self):
1774
r"""
1775
Returns True if this subgroup contains the matrix `-Id`.
1776
1777
EXAMPLES::
1778
1779
sage: G = Gamma(2).as_permutation_group()
1780
sage: G.is_even()
1781
True
1782
"""
1783
return True
1784
1785
def nu2(self):
1786
r"""
1787
Returns the number of orbits of elliptic points of order 2 for this
1788
arithmetic subgroup.
1789
1790
EXAMPLES::
1791
1792
sage: G = ArithmeticSubgroup_Permutation(S2="(1,4)(2)(3)",S3="(1,2,3)(4)")
1793
sage: G.nu2()
1794
2
1795
"""
1796
return sum(1 for i in xrange(self.index()) if self._S2[i] == i)
1797
1798
def nu3(self):
1799
r"""
1800
Returns the number of orbits of elliptic points of order 3 for this
1801
arithmetic subgroup.
1802
1803
EXAMPLES::
1804
1805
sage: G = ArithmeticSubgroup_Permutation(S2="(1,4)(2)(3)",S3="(1,2,3)(4)")
1806
sage: G.nu3()
1807
1
1808
"""
1809
return sum(1 for i in xrange(self.index()) if self._S3[i] == i)
1810
1811
def ncusps(self):
1812
r"""
1813
Return the number of cusps of this arithmetic subgroup.
1814
1815
EXAMPLES::
1816
1817
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)(5,6)",S3="(1,2,3)(4,5,6)")
1818
sage: G.ncusps()
1819
3
1820
"""
1821
return len(self.L().cycle_tuples(True))
1822
1823
def _spanning_tree_kulkarni(self, root=0, on_right=True):
1824
r"""
1825
Returns a spanning tree for the coset graph of the group for the
1826
generators `S2` and `S3`.
1827
1828
Warning: the output is randomized in order to be able to obtain any
1829
spanning tree of the coset graph. The algorithm mainly follows
1830
Kulkarni's paper.
1831
1832
INPUT:
1833
1834
- ``on_right`` - boolean (default: True) - if False, return spanning
1835
tree for the left cosets.
1836
1837
OUTPUT:
1838
1839
- ``tree`` - a spanning tree of the graph associated to the action of
1840
``L`` and ``S2`` on the cosets
1841
1842
- ``reps`` - list of matrices in `{\rm SL}_2(\ZZ)`` - representatives of the
1843
cosets with respect to the spanning tree
1844
1845
- ``word_reps`` - list of lists with ``s2`` and ``s3`` - word
1846
representatives of the cosets with respect to the spanning tree.
1847
1848
- ``gens`` - list of 3-tuples ``(in,out,label)`` - the list of edges in
1849
the graph which are not in the spanning tree.
1850
1851
EXAMPLES::
1852
1853
sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')
1854
sage: tree,reps,wreps,gens = G._spanning_tree_kulkarni()
1855
sage: tree
1856
Digraph on 4 vertices
1857
sage: for m in reps: print m,"\n****"
1858
[1 0]
1859
[0 1]
1860
****
1861
[ 0 1]
1862
[-1 1]
1863
****
1864
[-1 1]
1865
[-1 0]
1866
****
1867
[1 1]
1868
[0 1]
1869
****
1870
sage: for w in wreps: print ','.join(w)
1871
<BLANKLINE>
1872
s3
1873
s3,s3
1874
s3,s3,s2
1875
sage: gens
1876
[(0, 1, 's2'), (3, 3, 's3')]
1877
"""
1878
from sage.graphs.digraph import DiGraph
1879
from sage.misc.prandom import randint
1880
1881
N = len(self._S2)
1882
1883
if on_right:
1884
s2 = self._S2
1885
s3 = self._S3
1886
1887
else:
1888
s2 = [None] * N
1889
s3 = [None] * N
1890
for i in xrange(N):
1891
s2[self._S2[i]] = i
1892
s3[self._S3[i]] = i
1893
1894
# the tree and the lift
1895
tree = DiGraph(multiedges=False,loops=False)
1896
gens = []
1897
1898
reps = [None]*self.index()
1899
word_reps = [None]*self.index()
1900
reps[root] = SL2Z(1)
1901
word_reps[root] = []
1902
1903
x0 = root
1904
tree.add_vertex(x0)
1905
l = [x0]
1906
1907
while True:
1908
# complete the current 3-loop in the tree
1909
if s3[x0] != x0: # loop of length 3
1910
x1 = s3[x0]
1911
x2 = s3[x1]
1912
tree.add_edge(x0,x1,'s3')
1913
tree.add_edge(x1,x2,'s3')
1914
if on_right:
1915
reps[x1] = reps[x0] * S3m
1916
reps[x2] = reps[x1] * S3m
1917
word_reps[x1] = word_reps[x0] + ['s3']
1918
word_reps[x2] = word_reps[x1] + ['s3']
1919
else:
1920
reps[x1] = S3m * reps[x0]
1921
reps[x2] = S3m * reps[x1]
1922
word_reps[x1] = ['s3'] + word_reps[x0]
1923
word_reps[x2] = ['s3'] + word_reps[x1]
1924
l.append(x1)
1925
l.append(x2)
1926
else: # elliptic generator
1927
gens.append((x0,x0,'s3'))
1928
1929
# now perform links with s while we find another guy
1930
while l:
1931
x1 = l.pop(randint(0,len(l)-1))
1932
x0 = s2[x1]
1933
1934
if x1 != x0: # loop of length 2
1935
if x0 in tree:
1936
gens.append((x1,x0,'s2'))
1937
del l[l.index(x0)] # x0 must be in l
1938
else:
1939
tree.add_edge(x1,x0,'s2')
1940
if on_right:
1941
reps[x0] = reps[x1] * S2m
1942
word_reps[x0] = word_reps[x1] + ['s2']
1943
else:
1944
reps[x0] = S2m * reps[x1]
1945
word_reps[x0] = ['s2'] + word_reps[x1]
1946
break
1947
else: # elliptic generator
1948
gens.append((x1,x1,'s2'))
1949
1950
else:
1951
break
1952
1953
return tree,reps,word_reps,gens
1954
1955
def _spanning_tree_verrill(self, root=0, on_right=True):
1956
r"""
1957
Returns a spanning tree with generators `S2` and `L`.
1958
1959
The algorithm follows the one of Helena Verrill.
1960
1961
OUTPUT:
1962
1963
- ``tree`` - a spanning tree of the graph associated to the action of
1964
``L`` and ``S2`` on the cosets
1965
1966
- ``reps`` - list of matrices in `{\rm SL}_2(\ZZ)` - representatives of the
1967
cosets with respect to the spanning tree
1968
1969
- ``word_reps`` - list of string with ``s`` and ``l`` - word
1970
representatives of the cosets with respect to the spanning tree.
1971
1972
- ``gens`` - list of 3-tuples ``(in,out,label)`` - the list of edges in
1973
the graph which are not in the spanning tree.
1974
1975
EXAMPLES::
1976
1977
sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')
1978
sage: tree,reps,wreps,gens=G._spanning_tree_verrill()
1979
sage: tree
1980
Digraph on 4 vertices
1981
sage: for m in reps: print m, "\n****"
1982
[1 0]
1983
[0 1]
1984
****
1985
[ 0 -1]
1986
[ 1 0]
1987
****
1988
[1 2]
1989
[0 1]
1990
****
1991
[1 1]
1992
[0 1]
1993
****
1994
sage: wreps
1995
['', 's', 'll', 'l']
1996
sage: gens
1997
[(2, 0, 'l'), (1, 1, 'l'), (2, 3, 's')]
1998
1999
TODO:
2000
2001
Take care of the shape of the spanning tree as in Helena Verrill's program.
2002
"""
2003
from sage.misc.prandom import randint
2004
from copy import copy
2005
2006
if on_right:
2007
s = self._S2
2008
l = self._L
2009
else:
2010
s = [None]*self.index()
2011
l = [None]*self.index()
2012
for i in xrange(self.index()):
2013
s[self._S2[i]] = i
2014
l[self._L[i]] = i
2015
2016
from sage.graphs.digraph import DiGraph
2017
tree = DiGraph(multiedges=False,loops=False)
2018
gens = []
2019
2020
reps = [None]*self.index()
2021
word_reps = [None]*self.index()
2022
reps[root] = SL2Z(1)
2023
word_reps[root] = ''
2024
2025
x0 = root
2026
tree.add_vertex(x0)
2027
waiting = [x0]
2028
2029
while True:
2030
# complete the current l-loop in the tree from x0
2031
x = x0
2032
xx = l[x]
2033
while xx != x0:
2034
tree.add_edge(x,xx,'l')
2035
if on_right:
2036
reps[xx] = reps[x] * Lm
2037
word_reps[xx] = word_reps[x] + 'l'
2038
else:
2039
reps[xx] = Lm * reps[x]
2040
word_reps[xx] = 'l' + word_reps[x]
2041
waiting.append(xx)
2042
x = xx
2043
xx = l[x]
2044
2045
gens.append((x,x0,'l'))
2046
2047
# now perform links with s while we find another guy which will
2048
# become the new x0
2049
while waiting:
2050
x0 = None
2051
while waiting and x0 is None:
2052
x1 = waiting.pop(randint(0,len(waiting)-1))
2053
x0 = s[x1]
2054
2055
if x0 is not None:
2056
if x1 != x0: # loop of length 2
2057
if x0 in tree:
2058
gens.append((x1,x0,'s'))
2059
if x0 in waiting:
2060
del waiting[waiting.index(x0)] # x0 must be in l
2061
else:
2062
tree.add_edge(x1,x0,'s')
2063
if on_right:
2064
reps[x0] = reps[x1] * S2m
2065
word_reps[x0] = word_reps[x1] + 's'
2066
else:
2067
reps[x0] = S2m * reps[x1]
2068
word_reps[x0] = 's' + word_reps[x1]
2069
break
2070
else: # elliptic generator
2071
gens.append((x1,x1,'s'))
2072
2073
else:
2074
break
2075
2076
return tree,reps,word_reps,gens
2077
2078
def todd_coxeter_s2_s3(self):
2079
r"""
2080
Returns a 4-tuple ``(coset_reps, gens, s2, s3)`` where ``coset_reps``
2081
are coset representatives of the subgroup, ``gens`` is a list of
2082
generators, ``s2`` and ``s3`` are the action of the matrices `S2` and
2083
`S3` on the list of cosets.
2084
2085
The so called *Todd-Coxeter algorithm* is a general method for coset
2086
enumeration for a subgroup of a group given by generators and relations.
2087
2088
EXAMPLES::
2089
2090
sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')
2091
sage: G.genus()
2092
0
2093
sage: reps,gens,s2,s3=G.todd_coxeter_s2_s3()
2094
sage: g1,g2 = gens
2095
sage: g1 in G and g2 in G
2096
True
2097
sage: g1
2098
[-1 0]
2099
[ 1 -1]
2100
sage: g2
2101
[-1 3]
2102
[-1 2]
2103
sage: S2 = SL2Z([0,-1,1,0])
2104
sage: S3 = SL2Z([0,1,-1,1])
2105
sage: reps[0] == SL2Z([1,0,0,1])
2106
True
2107
sage: all(reps[i]*S2*~reps[s2[i]] in G for i in xrange(4))
2108
True
2109
sage: all(reps[i]*S3*~reps[s3[i]] in G for i in xrange(4))
2110
True
2111
"""
2112
tree,reps,wreps,edges = self._spanning_tree_kulkarni()
2113
2114
gens = []
2115
for e in edges:
2116
if e[2] == 's2':
2117
gens.append(self(reps[e[0]] * S2m * ~reps[e[1]]))
2118
elif e[2] == 's3':
2119
gens.append(self(reps[e[0]] * S3m * ~reps[e[1]]))
2120
else:
2121
raise ValueError, "this should not happen"
2122
2123
return reps, gens, self._S2[:], self._S3[:]
2124
2125
def todd_coxeter_l_s2(self):
2126
r"""
2127
Returns a 4-tuple ``(coset_reps, gens, l, s2)`` where ``coset_reps`` is
2128
a list of coset representatives of the subgroup, ``gens`` a list of
2129
generators, ``l`` and ``s2`` are list that corresponds to the action of
2130
the matrix `S2` and `L` on the cosets.
2131
2132
EXAMPLES::
2133
2134
sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')
2135
sage: reps,gens,l,s=G.todd_coxeter_l_s2()
2136
sage: reps
2137
[
2138
[1 0] [ 0 -1] [1 2] [1 1]
2139
[0 1], [ 1 0], [0 1], [0 1]
2140
]
2141
sage: gens
2142
[
2143
[1 3] [ 1 0] [ 2 -3]
2144
[0 1], [-1 1], [ 1 -1]
2145
]
2146
sage: l
2147
[3, 1, 0, 2]
2148
sage: s
2149
[1, 0, 3, 2]
2150
sage: S2 = SL2Z([0,-1,1,0])
2151
sage: L = SL2Z([1,1,0,1])
2152
sage: reps[0] == SL2Z([1,0,0,1])
2153
True
2154
sage: all(reps[i]*S2*~reps[s[i]] in G for i in xrange(4))
2155
True
2156
sage: all(reps[i]*L*~reps[l[i]] in G for i in xrange(4))
2157
True
2158
"""
2159
tree,reps,wreps,edges = self._spanning_tree_verrill()
2160
2161
gens = []
2162
for e in edges:
2163
if e[2] == 'l':
2164
gens.append(self(reps[e[0]] * Lm * ~reps[e[1]]))
2165
elif e[2] == 's':
2166
gens.append(self(reps[e[0]] * S2m * ~reps[e[1]]))
2167
else:
2168
raise ValueError, "this should not happen"
2169
2170
return reps, gens, self._L[:], self._S2[:]
2171
2172
todd_coxeter = todd_coxeter_l_s2
2173
2174
def coset_reps(self):
2175
r"""
2176
Return coset representatives.
2177
2178
EXAMPLES::
2179
2180
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,3)")
2181
sage: c = G.coset_reps()
2182
sage: len(c)
2183
4
2184
sage: [g in G for g in c]
2185
[True, False, False, False]
2186
"""
2187
return self.todd_coxeter()[0]
2188
2189
def cusp_widths(self,exp=False):
2190
r"""
2191
Return the list of cusp widths of the group.
2192
2193
EXAMPLES::
2194
2195
sage: G = Gamma(2).as_permutation_group()
2196
sage: G.cusp_widths()
2197
[2, 2, 2]
2198
sage: G.cusp_widths(exp=True)
2199
{2: 3}
2200
2201
sage: S2 = "(1,2)(3,4)(5,6)"
2202
sage: S3 = "(1,2,3)(4,5,6)"
2203
sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
2204
sage: G.cusp_widths()
2205
[1, 1, 4]
2206
sage: G.cusp_widths(exp=True)
2207
{1: 2, 4: 1}
2208
2209
sage: S2 = "(1,2)(3,4)(5,6)"
2210
sage: S3 = "(1,3,5)(2,4,6)"
2211
sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
2212
sage: G.cusp_widths()
2213
[6]
2214
sage: G.cusp_widths(exp=True)
2215
{6: 1}
2216
"""
2217
seen = [True]*self.index()
2218
2219
if exp:
2220
widths = {}
2221
else:
2222
widths = []
2223
for i in xrange(self.index()):
2224
if seen[i]:
2225
seen[i] = False
2226
j = self._L[i]
2227
n = 1
2228
while j != i:
2229
seen[j] = False
2230
n += 1
2231
j = self._L[j]
2232
if exp:
2233
if n not in widths:
2234
widths[n] = 0
2235
widths[n] += 1
2236
else:
2237
widths.append(n)
2238
2239
if exp:
2240
return widths
2241
return sorted(widths)
2242
2243
def to_even_subgroup(self, relabel=True):
2244
r"""
2245
Return the subgroup generated by self and ``-Id``. Since self is even,
2246
this is just self. Provided for compatibility.
2247
2248
EXAMPLE::
2249
2250
sage: G = Gamma0(4).as_permutation_group()
2251
sage: H = G.to_even_subgroup()
2252
sage: H == G
2253
True
2254
"""
2255
if relabel:
2256
return self.relabel(inplace=False)
2257
else:
2258
return self
2259
2260
def is_congruence(self):
2261
r"""
2262
Return True if this is a congruence subgroup.
2263
2264
ALGORITHM:
2265
2266
Uses Hsu's algorithm [Hsu96]_. Adapted from Chris Kurth's
2267
implementation in KFarey [Kur08]_.
2268
2269
EXAMPLES:
2270
2271
Test if `{\rm SL}_2(\ZZ)` is congruence::
2272
2273
sage: a = ArithmeticSubgroup_Permutation(L='',R='')
2274
sage: a.index()
2275
1
2276
sage: a.is_congruence()
2277
True
2278
2279
This example is congruence -- it's Gamma0(3) in disguise: ::
2280
2281
sage: S2 = SymmetricGroup(4)
2282
sage: l = S2((2,3,4))
2283
sage: r = S2((1,3,4))
2284
sage: G = ArithmeticSubgroup_Permutation(L=l,R=r)
2285
sage: print G
2286
Arithmetic subgroup with permutations of right cosets
2287
S2=(1,2)(3,4)
2288
S3=(1,4,2)
2289
L=(2,3,4)
2290
R=(1,3,4)
2291
sage: G.is_congruence()
2292
True
2293
2294
This one is noncongruence: ::
2295
2296
sage: import sage.modular.arithgroup.arithgroup_perm as ap
2297
sage: ap.HsuExample10().is_congruence()
2298
False
2299
"""
2300
if self.index() == 1: # the group is SL2Z (trivial case)
2301
return True
2302
2303
L = self.L() # action of L
2304
R = self.R() # action of R
2305
2306
N = L.order() # generalised level of the group
2307
2308
# write N as N = em where e = 2^k and m odd
2309
e = 1
2310
m = N
2311
while m%2 == 0:
2312
m //= 2
2313
e *= 2
2314
2315
if e==1: # N is odd
2316
onehalf = int(~Zmod(N)(2)) #i.e. 2^(-1) mod N
2317
rel = (R*R*L**(-onehalf))**3
2318
return rel.is_one()
2319
2320
elif m==1: # N is a power of 2
2321
onefifth=int(~Zmod(N)(5)) #i.e. 5^(-1) mod N
2322
S=L**20*R**onefifth*L**(-4)*~R
2323
2324
# congruence if the three below permutations are trivial
2325
rel=(~L*R*~L) * S * (L*~R*L) * S
2326
if not rel.is_one():
2327
return False
2328
2329
rel=~S*R*S*R**(-25)
2330
if not rel.is_one():
2331
return False
2332
2333
rel=(S*R**5*L*~R*L)**3
2334
if not rel.is_one():
2335
return False
2336
2337
return True
2338
2339
else: # e>1, m>1
2340
onehalf=int(~Zmod(m)(2)) #i.e. 2^(-1) mod m
2341
onefifth=int(~Zmod(e)(5)) #i.e. 5^(-1) mod e
2342
c=int(~Zmod(m)(e))*e #i.e. e^(-1)e mod m
2343
d=int(~Zmod(e)(m))*m #i.e. m^(-1)m mod e
2344
a=L**c
2345
b=R**c
2346
l=L**d
2347
r=R**d
2348
s=l**20*r**onefifth*l**(-4)*~r
2349
2350
#Congruence if the seven permutations below are trivial:
2351
rel=~a*~r*a*r
2352
if not rel.is_one():
2353
return False
2354
2355
rel=(a*~b*a)**4
2356
if not rel.is_one():
2357
return False
2358
2359
rel=(a*~b*a)**2*(~a*b)**3
2360
if not rel.is_one():
2361
return False
2362
2363
rel=(a*~b*a)**2*(b*b*a**(-onehalf))**(-3)
2364
if not rel.is_one():
2365
return False
2366
2367
rel=(~l*r*~l)*s*(l*~r*l)*s
2368
if not rel.is_one():
2369
return False
2370
2371
rel=~s*r*s*r**(-25)
2372
if not rel.is_one():
2373
return False
2374
2375
rel=(l*~r*l)**2*(s*r**5*l*~r*l)**(-3)
2376
if not rel.is_one():
2377
return False
2378
2379
return True
2380
2381
def one_odd_subgroup(self,random=False):
2382
r"""
2383
Return an odd subgroup of index 2 in `\Gamma`, where `\Gamma` is this
2384
subgroup. If the optional argument ``random`` is False (the default),
2385
this returns an arbitrary but consistent choice from the set of index 2
2386
odd subgroups. If ``random`` is True, then it will choose one of these
2387
at random.
2388
2389
For details of the algorithm used, see the docstring for the related
2390
function :meth:`odd_subgroups`, which returns a list of all the
2391
index 2 odd subgroups.
2392
2393
EXAMPLES:
2394
2395
Starting from `\Gamma(4)` we get back `\Gamma(4)`::
2396
2397
sage: G = Gamma(4).as_permutation_group()
2398
sage: print G.is_odd(), G.index()
2399
True 48
2400
sage: Ge = G.to_even_subgroup()
2401
sage: Go = Ge.one_odd_subgroup()
2402
sage: print Go.is_odd(), Go.index()
2403
True 48
2404
sage: Go == G
2405
True
2406
2407
Strating from `\Gamma(6)` we get a different group::
2408
2409
sage: G = Gamma(6).as_permutation_group()
2410
sage: print G.is_odd(), G.index()
2411
True 144
2412
sage: Ge = G.to_even_subgroup()
2413
sage: Go = Ge.one_odd_subgroup()
2414
sage: print Go.is_odd(), Go.index()
2415
True 144
2416
sage: Go == G
2417
False
2418
2419
An error will be raised if there are no such subgroups, which occurs if
2420
and only if the group contains an element of order 4::
2421
2422
sage: Gamma0(10).as_permutation_group().one_odd_subgroup()
2423
Traceback (most recent call last):
2424
...
2425
ValueError: Group contains an element of order 4, hence no index 2 odd subgroups
2426
2427
Testing randomness::
2428
2429
sage: G = Gamma(6).as_permutation_group().to_even_subgroup()
2430
sage: G1 = G.one_odd_subgroup(random=True) # random
2431
sage: G1.is_subgroup(G)
2432
True
2433
"""
2434
if self.nu2() != 0:
2435
raise ValueError, "Group contains an element of order 4, hence no index 2 odd subgroups"
2436
n = self.index()
2437
s2old, s3old = self.S2(), self.S3()
2438
s2cycs = s2old.cycle_tuples() # no singletons can exist
2439
s3cycs = s3old.cycle_tuples(singletons=True)
2440
s2 = PermutationGroupElement([x + tuple(y + n for y in x) for x in s2cycs])
2441
s3 = PermutationGroupElement([x + tuple(y + n for y in x) for x in s3cycs])
2442
2443
if random is False:
2444
return ArithmeticSubgroup_Permutation(S2=s2,S3=s3,check=False)
2445
2446
from sage.misc.prandom import randint
2447
2448
t = []
2449
for i in xrange(1,n+1):
2450
if randint(0,1):
2451
t.append((i,n+i))
2452
t = PermutationGroupElement(t)
2453
return ArithmeticSubgroup_Permutation(S2=s2,S3=t*s3*t,check=False)
2454
2455
def odd_subgroups(self):
2456
r"""
2457
Return a list of the odd subgroups of index 2 in `\Gamma`, where
2458
`\Gamma` is this subgroup. (Equivalently, return the liftings of
2459
`\bar{\Gamma} \le {\rm PSL}(2, \ZZ)` to `{\rm SL}(2, \ZZ)`.)
2460
2461
.. seealso:: :meth:`one_odd_subgroup`, which returns just one of the
2462
odd subgroups (which is much quicker than enumerating them all).
2463
2464
ALGORITHM:
2465
2466
- If `\Gamma` has an element of order 4, then there are no index 2 odd
2467
subgroups, so return the empty set.
2468
2469
- If `\Gamma` has no elements of order 4, then the permutation `S_2` is
2470
a combination of 2-cycles with no fixed points on `\{1, \dots, N\}`.
2471
We construct the permutation `\tilde{S}_2` of `\{1, \dots, 2N\}`
2472
which has a 4-cycle `(a, b, a+N, b+N)` for each 2-cycle `(a,b)` in
2473
``S2``. Similarly, we construct a permutation `\tilde{S}_3` which has
2474
a 6-cycle `(a,b,c,a+N,b+N,c+N)` for each 3-cycle `(a,b,c)` in `S_3`,
2475
and a 2-cycle `(a,a+N)` for each fixed point `a` of `S_3`.
2476
2477
Then the permutations `\tilde{S}_2` and `\tilde{S}_3` satisfy
2478
`\tilde{S}_2^2 = \tilde{S}_3^3 = \iota` where `\iota` is the order 2
2479
permutation interchanging `a` and `a+N` for each `a`. So the subgroup
2480
corresponding to these permutations is an index 2 odd subgroup of
2481
`\Gamma`.
2482
2483
- The other index 2 odd subgroups of `\Gamma` are obtained from the
2484
pairs `\tilde{S}_2, \tilde{S}_3^\sigma` where `\sigma` is an element
2485
of the group generated by the 2-cycles `(a, a+N)`.
2486
2487
Studying the permutations in the first example below gives a good
2488
illustration of the algorithm.
2489
2490
EXAMPLES::
2491
2492
sage: G = sage.modular.arithgroup.arithgroup_perm.HsuExample10()
2493
sage: [G.S2(), G.S3()]
2494
[(1,2)(3,4)(5,6)(7,8)(9,10), (1,8,3)(2,4,6)(5,7,10)]
2495
sage: X = G.odd_subgroups()
2496
sage: for u in X: print [u.S2(), u.S3()]
2497
[(1,2,11,12)(3,4,13,14)(5,6,15,16)(7,8,17,18)(9,10,19,20), (1,8,3,11,18,13)(2,4,6,12,14,16)(5,7,10,15,17,20)(9,19)]
2498
[(1,2,11,12)(3,4,13,14)(5,6,15,16)(7,8,17,18)(9,10,19,20), (1,18,13,11,8,3)(2,4,6,12,14,16)(5,7,10,15,17,20)(9,19)]
2499
[(1,2,11,12)(3,4,13,14)(5,6,15,16)(7,8,17,18)(9,10,19,20), (1,8,13,11,18,3)(2,4,6,12,14,16)(5,7,10,15,17,20)(9,19)]
2500
[(1,2,11,12)(3,4,13,14)(5,6,15,16)(7,8,17,18)(9,10,19,20), (1,18,3,11,8,13)(2,4,6,12,14,16)(5,7,10,15,17,20)(9,19)]
2501
2502
A projective congruence subgroup may have noncongruence liftings, as the example of `\bar{\Gamma}_0(6)` illustrates (see [KSV11]_)::
2503
2504
sage: X = Gamma0(6).as_permutation_group().odd_subgroups(); Sequence([[u.S2(), u.S3()] for u in X],cr=True)
2505
[
2506
[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23), (1,2,3,13,14,15)(4,5,6,16,17,18)(7,8,9,19,20,21)(10,11,12,22,23,24)],
2507
[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23), (1,14,15,13,2,3)(4,5,6,16,17,18)(7,8,9,19,20,21)(10,11,12,22,23,24)],
2508
[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23), (1,2,3,13,14,15)(4,17,6,16,5,18)(7,8,9,19,20,21)(10,11,12,22,23,24)],
2509
[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23), (1,14,15,13,2,3)(4,17,6,16,5,18)(7,8,9,19,20,21)(10,11,12,22,23,24)],
2510
[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23), (1,2,3,13,14,15)(4,5,6,16,17,18)(7,20,9,19,8,21)(10,11,12,22,23,24)],
2511
[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23), (1,14,15,13,2,3)(4,5,6,16,17,18)(7,20,9,19,8,21)(10,11,12,22,23,24)],
2512
[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23), (1,2,3,13,14,15)(4,17,6,16,5,18)(7,20,9,19,8,21)(10,11,12,22,23,24)],
2513
[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23), (1,14,15,13,2,3)(4,17,6,16,5,18)(7,20,9,19,8,21)(10,11,12,22,23,24)]
2514
]
2515
sage: [u.is_congruence() for u in X]
2516
[True, False, False, True, True, False, False, True]
2517
2518
Subgroups of large index may take a very long time::
2519
2520
sage: len(GammaH(11,[-1]).as_permutation_group().odd_subgroups()) # long time (15s)
2521
2048
2522
"""
2523
if self.nu2() != 0:
2524
return []
2525
n = self.index()
2526
s2old, s3old = self.S2(), self.S3()
2527
s2cycs = s2old.cycle_tuples() # no singletons can exist
2528
s3cycs = s3old.cycle_tuples(singletons=True)
2529
s2 = PermutationGroupElement([x + tuple(y + n for y in x) for x in s2cycs])
2530
s3 = PermutationGroupElement([x + tuple(y + n for y in x) for x in s3cycs])
2531
H = ArithmeticSubgroup_Permutation(S2=s2,S3=s3)
2532
2533
bucket = set([H])
2534
res = [H]
2535
# We use a set *and* a list since checking whether an element is in a
2536
# set is very fast, but on the other hand we want the order the results
2537
# are returned to be at least somewhat canonical.
2538
ts = [PermutationGroupElement(range(1,1+2*n))]
2539
2540
for i in xrange(1,n+1):
2541
2542
t = PermutationGroupElement([(i, n+i)],check=False)
2543
2544
s3c = t*s3*t
2545
2546
if s3c == s3:
2547
# t commutes with s3; nothing to see here.
2548
continue
2549
2550
HH = ArithmeticSubgroup_Permutation(S2=s2,S3=s3c,check=False)
2551
2552
if HH not in bucket:
2553
# Because the liftings are indexed by Hom(self, +-1) which is a
2554
# vector space over F2, either HH is already familiar, or all
2555
# the subgroups one gets by acting by t are new.
2556
2557
bucket.add(HH)
2558
res.append(HH)
2559
ts.append(t)
2560
for tt in ts[1:-1]:
2561
ts.append(tt*t)
2562
res.append(ArithmeticSubgroup_Permutation(S2=s2,S3=tt*s3c*tt,check=False))
2563
bucket.add(res[-1])
2564
2565
return res
2566
2567
def HsuExample10():
2568
r"""
2569
An example of an index 10 arithmetic subgroup studied by Tim Hsu.
2570
2571
EXAMPLE::
2572
2573
sage: import sage.modular.arithgroup.arithgroup_perm as ap
2574
sage: ap.HsuExample10()
2575
Arithmetic subgroup with permutations of right cosets
2576
S2=(1,2)(3,4)(5,6)(7,8)(9,10)
2577
S3=(1,8,3)(2,4,6)(5,7,10)
2578
L=(1,4)(2,5,9,10,8)(3,7,6)
2579
R=(1,7,9,10,6)(2,3)(4,5,8)
2580
"""
2581
return ArithmeticSubgroup_Permutation(
2582
L="(1,4)(2,5,9,10,8)(3,7,6)",
2583
R="(1,7,9,10,6)(2,3)(4,5,8)",
2584
relabel=False)
2585
2586
def HsuExample18():
2587
r"""
2588
An example of an index 18 arithmetic subgroup studied by Tim Hsu.
2589
2590
EXAMPLE::
2591
2592
sage: import sage.modular.arithgroup.arithgroup_perm as ap
2593
sage: ap.HsuExample18()
2594
Arithmetic subgroup with permutations of right cosets
2595
S2=(1,5)(2,11)(3,10)(4,15)(6,18)(7,12)(8,14)(9,16)(13,17)
2596
S3=(1,7,11)(2,18,5)(3,9,15)(4,14,10)(6,17,12)(8,13,16)
2597
L=(1,2)(3,4)(5,6,7)(8,9,10)(11,12,13,14,15,16,17,18)
2598
R=(1,12,18)(2,6,13,9,4,8,17,7)(3,16,14)(5,11)(10,15)
2599
"""
2600
return ArithmeticSubgroup_Permutation(
2601
L="(1,2)(3,4)(5,6,7)(8,9,10)(11,12,13,14,15,16,17,18)",
2602
R="(1,12,18)(2,6,13,9,4,8,17,7)(3,16,14)(5,11)(10,15)",
2603
relabel=False)
2604
2605