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