Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/modular/arithgroup/farey_symbol.pyx
4102 views
1
r"""
2
Farey Symbol for arithmetic subgroups of `{\rm PSL}_2(\ZZ)`
3
4
AUTHORS:
5
6
- Hartmut Monien (08 - 2011)
7
8
based on the *KFarey* package by Chris Kurth. Implemented as C++ module
9
for speed.
10
"""
11
#*****************************************************************************
12
# Copyright (C) 2011 Hartmut Monien <[email protected]>
13
#
14
# Distributed under the terms of the GNU General Public License (GPL)
15
#
16
# This code is distributed in the hope that it will be useful,
17
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19
# General Public License for more details.
20
#
21
# The full text of the GPL is available at:
22
#
23
# http://www.gnu.org/licenses/
24
#*****************************************************************************
25
26
include '../../ext/interrupt.pxi'
27
include '../../ext/stdsage.pxi'
28
include '../../ext/cdefs.pxi'
29
30
include "farey.pxd"
31
32
import sage.rings.arith
33
from sage.rings.all import CC, RR
34
from sage.rings.integer cimport Integer
35
from sage.rings.infinity import infinity
36
from congroup_gammaH import is_GammaH
37
from congroup_gamma1 import is_Gamma1
38
from congroup_gamma0 import is_Gamma0
39
from congroup_gamma import is_Gamma
40
from congroup_sl2z import SL2Z
41
from sage.modular.cusps import Cusp
42
43
from sage.plot.all import Graphics
44
from sage.plot.colors import to_mpl_color
45
from sage.plot.misc import options, rename_keyword
46
from sage.plot.all import hyperbolic_arc, hyperbolic_triangle, text
47
48
cdef class Farey:
49
r"""
50
A class for calculating Farey symbols of arithmetics subgroups of
51
`{\rm PSL}_2(\ZZ)`. The arithmetic subgroup can be either any of
52
the congruence subgroups implemented in Sage, i.e. Gamma, Gamma0,
53
Gamma1 and GammaH or a subgroup of `{\rm PSL}_2(\ZZ)` which is
54
given by a user written helper class defining membership in that
55
group.
56
57
REFERENCES:
58
59
- Ravi S. Kulkarni, ''An arithmetic-geometric method in the study of the
60
subgroups of the modular group'', `Amer. J. Math., 113(6):1053--1133,
61
1991. <http://www.jstor.org/stable/2374900>`_
62
63
INPUTS:
64
65
- `G` - an arithmetic subgroup of `{\rm PSL}_2(\ZZ)`
66
67
EXAMPLES:
68
69
Create a Farey symbol for the group `\Gamma_0(11)`::
70
71
sage: F = FareySymbol(Gamma0(11)); F
72
FareySymbol(Congruence Subgroup Gamma0(11))
73
74
Calculate the generators::
75
76
sage: F.generators()
77
[
78
[1 1] [ 7 -2] [ 8 -3] [-1 0]
79
[0 1], [11 -3], [11 -4], [ 0 -1]
80
]
81
82
Pickling the FareySymbol and recovering it::
83
84
sage: F == loads(dumps(F))
85
True
86
87
Calculate the index of `\Gamma_H(33, [2, 5])` in
88
`{\rm PSL}_2(\ZZ)` via FareySymbol::
89
90
sage: FareySymbol(GammaH(33, [2, 5])).index()
91
48
92
93
Calculate the generators of `\Gamma_1(4)`::
94
95
sage: FareySymbol(Gamma1(4)).generators()
96
[
97
[1 1] [-3 1]
98
[0 1], [-4 1]
99
]
100
101
Calculate the generators of the :meth:`example
102
<sage.modular.arithgroup.arithgroup_perm.HsuExample10>` of an
103
index 10 arithmetic subgroup given by Tim Hsu::
104
105
sage: from sage.modular.arithgroup.arithgroup_perm import HsuExample10
106
sage: FareySymbol(HsuExample10()).generators()
107
[
108
[1 2] [-2 1] [ 4 -3]
109
[0 1], [-7 3], [ 3 -2]
110
]
111
112
Calculate the generators of the group `\Gamma' =
113
\Gamma_0(8)\cap\Gamma_1(4)` using a helper class to define group membership::
114
115
sage: class GPrime:
116
... def __contains__(self, M):
117
... return M in Gamma0(8) and M in Gamma1(4)
118
...
119
120
sage: FareySymbol(GPrime()).generators()
121
[
122
[1 1] [ 5 -1] [ 5 -2]
123
[0 1], [16 -3], [ 8 -3]
124
]
125
126
Calculate cusps of arithmetic subgroup defined via permutation group::
127
128
sage: L = SymmetricGroup(4)('(1, 2, 3)')
129
130
sage: R = SymmetricGroup(4)('(1, 2, 4)')
131
132
sage: FareySymbol(ArithmeticSubgroup_Permutation(L, R)).cusps()
133
[2, Infinity]
134
135
Calculate the left coset representation of `\Gamma_H(8, [3])`::
136
137
sage: FareySymbol(GammaH(8, [3])).coset_reps()
138
[
139
[1 0] [ 4 -1] [ 3 -1] [ 2 -1] [ 1 -1] [ 3 -1] [ 2 -1] [-1 0]
140
[0 1], [ 1 0], [ 1 0], [ 1 0], [ 1 0], [ 4 -1], [ 3 -1], [ 3 -1],
141
[ 1 -1] [-1 0] [ 0 -1] [-1 0]
142
[ 2 -1], [ 2 -1], [ 1 -1], [ 1 -1]
143
]
144
"""
145
cdef cpp_farey *this_ptr
146
cdef object group
147
148
def __cinit__(self, group, data=None):
149
r"""
150
Initialize FareySymbol::
151
152
sage: FareySymbol(Gamma0(23))
153
FareySymbol(Congruence Subgroup Gamma0(23))
154
"""
155
self.group = group
156
# if data is present we want to restore
157
if data is not None:
158
self.this_ptr = new cpp_farey(data)
159
return
160
## to accelerate the calculation of the FareySymbol
161
## we implement the tests for the standard congruence groups
162
## in the c++ module. For a general group the test if an element
163
## of SL2Z is in the group the python __contains__ attribute
164
## of the group is called
165
cdef int p
166
if hasattr(group, "level"): p=group.level()
167
if group == SL2Z:
168
sig_on()
169
self.this_ptr = new cpp_farey()
170
sig_off()
171
elif is_Gamma0(group):
172
sig_on()
173
self.this_ptr = new cpp_farey(group, new is_element_Gamma0(p))
174
sig_off()
175
elif is_Gamma1(group):
176
sig_on()
177
self.this_ptr = new cpp_farey(group, new is_element_Gamma1(p))
178
sig_off()
179
elif is_Gamma(group):
180
sig_on()
181
self.this_ptr = new cpp_farey(group, new is_element_Gamma(p))
182
sig_off()
183
elif is_GammaH(group):
184
sig_on()
185
l = group._GammaH_class__H
186
self.this_ptr = new cpp_farey(group, new is_element_GammaH(p, l))
187
sig_off()
188
else:
189
sig_on()
190
self.this_ptr = new cpp_farey(group)
191
sig_off()
192
193
def __deallocpp__(self):
194
r"""
195
Remove reference to FareySymbol::
196
197
sage: F = FareySymbol(Gamma0(23))
198
199
sage: del F
200
201
"""
202
del self.this_ptr
203
204
def __cmp__(self, other):
205
r"""
206
Compare self to others.
207
208
EXAMPLES::
209
210
sage: FareySymbol(Gamma(2)) == FareySymbol(Gamma0(7))
211
False
212
213
sage: FareySymbol(Gamma0(23)) == loads(dumps(FareySymbol(Gamma0(23))))
214
True
215
"""
216
cmp_fcts = [lambda fs: fs.coset_reps(),
217
lambda fs: fs.cusps(),
218
lambda fs: fs.fractions()]
219
220
for cf in cmp_fcts:
221
c = cmp(cf(self), cf(other))
222
if c != 0: return c
223
224
return c
225
226
def __reduce__(self):
227
r"""
228
Serialization for pickling::
229
230
sage: FareySymbol(Gamma0(4)).__reduce__()
231
(<type 'sage.modular.arithgroup.farey_symbol.Farey'>, ...))
232
233
"""
234
return Farey, (self.group, self.this_ptr.dumps())
235
236
def __repr__(self):
237
r"""
238
Return the string representation of self.
239
240
EXAMPLES::
241
242
sage: FareySymbol(Gamma0(23)).__repr__()
243
'FareySymbol(Congruence Subgroup Gamma0(23))'
244
"""
245
if hasattr(self.group, "_repr_"):
246
return "FareySymbol(%s)" % self.group._repr_()
247
elif hasattr(self.group, "__repr__"):
248
return "FareySymbol(%s)" % self.group.__repr__()
249
else:
250
return "FareySymbol(?)"
251
252
def _latex_(self):
253
r"""
254
Return the LaTeX representation of self.
255
256
EXAMPLES::
257
258
sage: FareySymbol(Gamma0(23))._latex_()
259
'\\mathcal{F}(\\Gamma_0(23))'
260
"""
261
if hasattr(self.group, "_latex_"):
262
return "\mathcal{F}(%s)" % self.group._latex_()
263
else:
264
return "\mathcal{F}(%s)" % "unknonwn"
265
266
def index(self):
267
r"""
268
Return the index of the arithmetic group of the FareySymbol
269
in `{\rm PSL}_2(\ZZ)`.
270
271
EXAMPLES::
272
273
sage: [FareySymbol(Gamma0(n)).index() for n in range(1, 16)]
274
[1, 3, 4, 6, 6, 12, 8, 12, 12, 18, 12, 24, 14, 24, 24]
275
"""
276
return self.this_ptr.index()
277
278
def genus(self):
279
r"""
280
Return the genus of the arithmetic group of the FareySymbol.
281
282
EXAMPLES::
283
284
sage: [FareySymbol(Gamma0(n)).genus() for n in range(16, 32)]
285
[0, 1, 0, 1, 1, 1, 2, 2, 1, 0, 2, 1, 2, 2, 3, 2]
286
"""
287
return self.this_ptr.genus()
288
289
def level(self):
290
r"""
291
Return the level of the arithmetic group of the FareySymbol.
292
293
EXAMPLES::
294
295
sage: [FareySymbol(Gamma0(n)).level() for n in range(1, 16)]
296
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
297
"""
298
return self.this_ptr.level();
299
300
def nu2(self):
301
r"""
302
Return the number of elliptic points of order two.
303
304
EXAMPLES::
305
306
sage: [FareySymbol(Gamma0(n)).nu2() for n in range(1, 16)]
307
[1, 1, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0]
308
"""
309
return self.this_ptr.nu2()
310
311
def nu3(self):
312
r"""
313
Return the number of elliptic points of order three.
314
315
EXAMPLES::
316
317
sage: [FareySymbol(Gamma0(n)).nu3() for n in range(1, 16)]
318
[1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0]
319
"""
320
return self.this_ptr.nu3()
321
322
def coset_reps(self):
323
r"""
324
Left coset of the arithmetic group of the FareySymbol.
325
326
EXAMPLES:
327
328
Calculate the left coset of `\Gamma_0(6)`::
329
330
sage: FareySymbol(Gamma0(6)).coset_reps()
331
[
332
[1 0] [ 3 -1] [ 2 -1] [ 1 -1] [ 2 -1] [ 3 -2] [ 1 -1] [-1 0]
333
[0 1], [ 1 0], [ 1 0], [ 1 0], [ 3 -1], [ 2 -1], [ 2 -1], [ 2 -1],
334
[ 1 -1] [ 0 -1] [-1 0] [-2 1]
335
[ 3 -2], [ 1 -1], [ 1 -1], [ 1 -1]
336
]
337
"""
338
return self.this_ptr.get_coset()
339
340
def generators(self):
341
r"""
342
Minmal set of generators of the group of the FareySymbol.
343
344
EXAMPLES:
345
346
Calculate the generators of `\Gamma_0(6)`::
347
348
sage: FareySymbol(Gamma0(6)).generators()
349
[
350
[1 1] [ 5 -1] [ 7 -3] [-1 0]
351
[0 1], [ 6 -1], [12 -5], [ 0 -1]
352
]
353
354
Calculate the generators of `{\rm SL}_2(\ZZ)`::
355
356
sage: FareySymbol(SL2Z).generators()
357
[
358
[ 0 -1] [ 0 -1]
359
[ 1 0], [ 1 -1]
360
]
361
362
The unique index 2 even subgroup and index 4 odd subgroup each get handled correctly::
363
364
sage: FareySymbol(ArithmeticSubgroup_Permutation(S2="(1,2)", S3="()")).generators()
365
[
366
[ 0 -1] [-1 1]
367
[ 1 1], [-1 0]
368
]
369
sage: FareySymbol(ArithmeticSubgroup_Permutation(S2="(1,2, 3, 4)", S3="(1,3)(2,4)")).generators()
370
[
371
[ 0 1] [-1 1]
372
[-1 -1], [-1 0]
373
]
374
"""
375
return self.this_ptr.get_generators()
376
377
def fractions(self):
378
r"""
379
Fractions of the FareySymbol.
380
381
EXAMPLES::
382
383
sage: FareySymbol(Gamma(4)).fractions()
384
[0, 1/2, 1, 3/2, 2, 5/2, 3, 7/2, 4]
385
"""
386
return self.this_ptr.get_fractions()
387
388
389
def pairings(self):
390
r"""
391
Pairings of the sides of the fundamental domain of the Farey symbol
392
of the arithmetic group. The sides of the hyperbolic polygon are
393
numbered 0, 1, ... from left to right. Conventions: even pairings are
394
denoted by -2, odd pairings by -3 while free pairings are denoted by
395
an integer number greater than zero.
396
397
EXAMPLES:
398
399
Odd pairings::
400
401
sage: FareySymbol(Gamma0(7)).pairings()
402
[1, -3, -3, 1]
403
404
Even and odd pairings::
405
406
FareySymbol(Gamma0(13)).pairings()
407
[1, -3, -2, -2, -3, 1]
408
409
Only free pairings::
410
411
sage: FareySymbol(Gamma0(23)).pairings()
412
[1, 2, 3, 5, 3, 4, 2, 4, 5, 1]
413
"""
414
return self.this_ptr.get_pairings()
415
416
def paired_sides(self):
417
r"""
418
Pairs of index of the sides of the fundamental domain of the
419
Farey symbol of the arithmetic group. The sides of the
420
hyperbolic polygon are numbered 0, 1, ... from left to right.
421
422
.. image:: ../../../media/modular/arithgroup/pairing.png
423
424
EXAMPLES::
425
426
sage: FareySymbol(Gamma0(11)).paired_sides()
427
[(0, 5), (1, 3), (2, 4)]
428
429
indicating that the side 0 is paired with 5, 1 with 3 and 2 with 4.
430
"""
431
return self.this_ptr.get_paired_sides()
432
433
def pairing_matrices(self):
434
r"""
435
Pairing matrices of the sides of the fundamental domain. The sides
436
of the hyperbolic polygon are numbered 0, 1, ... from left to right.
437
438
EXAMPLES::
439
440
sage: FareySymbol(Gamma0(6)).pairing_matrices()
441
[
442
[1 1] [ 5 -1] [ 7 -3] [ 5 -3] [ 1 -1] [-1 1]
443
[0 1], [ 6 -1], [12 -5], [12 -7], [ 6 -5], [ 0 -1]
444
]
445
"""
446
447
return self.this_ptr.get_pairing_matrices()
448
449
def cusps(self):
450
r"""
451
Cusps of the FareySymbol.
452
453
EXAMPLES::
454
455
sage: FareySymbol(Gamma0(6)).cusps()
456
[0, 1/3, 1/2, Infinity]
457
"""
458
return self.this_ptr.get_cusps()+[Cusp(infinity)]
459
460
@rename_keyword(color='rgbcolor')
461
@options(alpha=1, fill=True, thickness=1, rgbcolor="lightgray", \
462
zorder=2, linestyle='solid', show_pairing=True, \
463
show_tesselation=False)
464
465
def fundamental_domain(self, **options):
466
r"""
467
Plot a fundamental domain of an arithmetic subgroup of
468
`{\rm PSL}_2(\ZZ)` corresponding to the Farey symbol.
469
470
OPTIONS:
471
472
- ``fill`` - fill the fundamental domain (default True)
473
474
- ``rgbcolor`` - fill color (default 'lightgray')
475
476
- ``show_pairing`` - flag for pairing (default True)
477
478
- ``show_tesselation`` - flag for the hyperbolic tesselation
479
(default False)
480
481
EXAMPLES:
482
483
For example to plot the fundamental domain of `\Gamma_0(11)`
484
with pairings use the following command::
485
486
sage: FareySymbol(Gamma0(11)).fundamental_domain()
487
488
indicating that side 1 is paired with side 3 and side 2 is
489
paired with side 4, see also :meth:`.paired_sides`.
490
491
To plot the fundamental domain of `\Gamma(3)` without pairings
492
use the following command::
493
494
sage: FareySymbol(Gamma(3)).fundamental_domain(show_pairing=False)
495
496
Plot the fundamental domain of `\Gamma_0(23)` showing the left
497
coset representatives.
498
499
::
500
501
sage: FareySymbol(Gamma0(23)).fundamental_domain(show_tesselation=True)
502
"""
503
from sage.plot.colors import rainbow
504
L = 1000
505
g = Graphics()
506
## show coset
507
for x in self.coset_reps():
508
if self.index() != 2:
509
a, b, c, d = x[1, 1], -x[0, 1], -x[1, 0], x[0, 0]
510
A, B = CC(0, L), CC(0, L)
511
if d!=0: A = b/d
512
if c!=0: B = a/c
513
C = (a*c+b*d+(a*d+b*c)/2+CC(0, 1)*RR(3).sqrt()/2)\
514
/(c*c+c*d+d*d)
515
else:
516
A, B, C = [x.acton(z) for z in [CC(0, 0), CC(1, 0), CC(0, L)]]
517
g += hyperbolic_triangle(A, B, C, \
518
color=options['rgbcolor'], \
519
fill=options['fill'], \
520
alpha=options['alpha'])
521
if options['show_tesselation']:
522
g += hyperbolic_triangle(A, B, C, color="gray")
523
## show pairings
524
p = self.pairings()
525
x = self.fractions()
526
if options['show_pairing']:
527
rc = rainbow(max(p)-min(p)+1)
528
if p[0] > 0:
529
g += hyperbolic_arc(CC(0, L), x[0], color=rc[p[0]-min(p)])
530
if p[-1] > 0:
531
g += hyperbolic_arc(CC(0, L), x[-1], color=rc[p[-1]-min(p)])
532
for i in range(len(x)-1):
533
if p[i+1] > 0:
534
g += hyperbolic_arc(x[i], x[i+1], color=rc[p[i+1]-min(p)])
535
d = g.get_minmax_data()
536
g.set_axes_range(d['xmin'], d['xmax'], 0, 1)
537
return g
538
539
540
#--- conversions ------------------------------------------------------------
541
542
cdef public long convert_to_long(n):
543
cdef long m = n
544
return m
545
546
cdef public object convert_to_Integer(mpz_class a):
547
A = Integer()
548
A.set_from_mpz(a.get_mpz_t())
549
return A
550
551
cdef public object convert_to_rational(mpq_class r):
552
a = Integer(); a.set_from_mpz(r.get_num_mpz_t())
553
b = Integer(); b.set_from_mpz(r.get_den_mpz_t())
554
return a/b
555
556
cdef public object convert_to_cusp(mpq_class r):
557
a = Integer(); a.set_from_mpz(r.get_num_mpz_t())
558
b = Integer(); b.set_from_mpz(r.get_den_mpz_t())
559
return Cusp(a/b)
560
561
cdef public object convert_to_SL2Z(cpp_SL2Z M):
562
a = convert_to_Integer(M.a())
563
b = convert_to_Integer(M.b())
564
c = convert_to_Integer(M.c())
565
d = convert_to_Integer(M.d())
566
return SL2Z([a, b, c, d])
567
568