Path: blob/master/src/sage/modular/arithgroup/farey_symbol.pyx
8821 views
r"""1Farey Symbol for arithmetic subgroups of `{\rm PSL}_2(\ZZ)`23AUTHORS:45- Hartmut Monien (08 - 2011)67based on the *KFarey* package by Chris Kurth. Implemented as C++ module8for speed.9"""10#*****************************************************************************11# Copyright (C) 2011 Hartmut Monien <[email protected]>12#13# Distributed under the terms of the GNU General Public License (GPL)14#15# This code is distributed in the hope that it will be useful,16# but WITHOUT ANY WARRANTY; without even the implied warranty of17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU18# General Public License for more details.19#20# The full text of the GPL is available at:21#22# http://www.gnu.org/licenses/23#*****************************************************************************2425include 'sage/ext/interrupt.pxi'26include 'sage/ext/stdsage.pxi'27include 'sage/ext/cdefs.pxi'2829include "farey.pxd"3031import sage.rings.arith32from sage.rings.all import CC, RR33from sage.rings.integer cimport Integer34from sage.rings.infinity import infinity35from congroup_gammaH import is_GammaH36from congroup_gamma1 import is_Gamma137from congroup_gamma0 import is_Gamma038from congroup_gamma import is_Gamma39from congroup_sl2z import SL2Z40from sage.modular.cusps import Cusp4142from sage.plot.all import Graphics43from sage.plot.colors import to_mpl_color44from sage.plot.misc import options, rename_keyword45from sage.plot.all import hyperbolic_arc, hyperbolic_triangle, text4647from sage.misc.latex import latex4849cdef class Farey:50r"""51A class for calculating Farey symbols of arithmetics subgroups of52`{\rm PSL}_2(\ZZ)`. The arithmetic subgroup can be either any of53the congruence subgroups implemented in Sage, i.e. Gamma, Gamma0,54Gamma1 and GammaH or a subgroup of `{\rm PSL}_2(\ZZ)` which is55given by a user written helper class defining membership in that56group.5758REFERENCES:5960- Ravi S. Kulkarni, ''An arithmetic-geometric method in the study of the61subgroups of the modular group'', `Amer. J. Math., 113(6):1053--1133,621991. <http://www.jstor.org/stable/2374900>`_6364INPUTS:6566- `G` - an arithmetic subgroup of `{\rm PSL}_2(\ZZ)`6768EXAMPLES:6970Create a Farey symbol for the group `\Gamma_0(11)`::7172sage: f = FareySymbol(Gamma0(11)); f73FareySymbol(Congruence Subgroup Gamma0(11))7475Calculate the generators::7677sage: f.generators()78[79[1 1] [ 7 -2] [ 8 -3] [-1 0]80[0 1], [11 -3], [11 -4], [ 0 -1]81]8283Pickling the FareySymbol and recovering it::8485sage: f == loads(dumps(f))86True8788Calculate the index of `\Gamma_H(33, [2, 5])` in89`{\rm PSL}_2(\ZZ)` via FareySymbol::9091sage: FareySymbol(GammaH(33, [2, 5])).index()92489394Calculate the generators of `\Gamma_1(4)`::9596sage: FareySymbol(Gamma1(4)).generators()97[98[1 1] [-3 1]99[0 1], [-4 1]100]101102Calculate the generators of the :meth:`example103<sage.modular.arithgroup.arithgroup_perm.HsuExample10>` of an104index 10 arithmetic subgroup given by Tim Hsu::105106sage: from sage.modular.arithgroup.arithgroup_perm import HsuExample10107sage: FareySymbol(HsuExample10()).generators()108[109[1 2] [-2 1] [ 4 -3]110[0 1], [-7 3], [ 3 -2]111]112113Calculate the generators of the group `\Gamma' =114\Gamma_0(8)\cap\Gamma_1(4)` using a helper class to define group membership::115116sage: class GPrime:117... def __contains__(self, M):118... return M in Gamma0(8) and M in Gamma1(4)119...120121sage: FareySymbol(GPrime()).generators()122[123[1 1] [ 5 -1] [ 5 -2]124[0 1], [16 -3], [ 8 -3]125]126127Calculate cusps of arithmetic subgroup defined via permutation group::128129sage: L = SymmetricGroup(4)('(1, 2, 3)')130131sage: R = SymmetricGroup(4)('(1, 2, 4)')132133sage: FareySymbol(ArithmeticSubgroup_Permutation(L, R)).cusps()134[-1, Infinity]135136Calculate the left coset representation of `\Gamma_H(8, [3])`::137138sage: FareySymbol(GammaH(8, [3])).coset_reps()139[140[1 0] [ 4 -1] [ 3 -1] [ 2 -1] [ 1 -1] [ 3 -1] [ 2 -1] [-1 0]141[0 1], [ 1 0], [ 1 0], [ 1 0], [ 1 0], [ 4 -1], [ 3 -1], [ 3 -1],142[ 1 -1] [-1 0] [ 0 -1] [-1 0]143[ 2 -1], [ 2 -1], [ 1 -1], [ 1 -1]144]145"""146cdef cpp_farey *this_ptr147cdef object group148149def __cinit__(self, group, data=None):150r"""151Initialize FareySymbol::152153sage: FareySymbol(Gamma0(23))154FareySymbol(Congruence Subgroup Gamma0(23))155"""156self.group = group157# if data is present we want to restore158if data is not None:159sig_on()160self.this_ptr = new cpp_farey(data)161sig_off()162return163## to accelerate the calculation of the FareySymbol164## we implement the tests for the standard congruence groups165## in the c++ module. For a general group the test if an element166## of SL2Z is in the group the python __contains__ attribute167## of the group is called168cdef int p169if hasattr(group, "level"): p=group.level()170if group == SL2Z:171sig_on()172self.this_ptr = new cpp_farey()173sig_off()174elif is_Gamma0(group):175sig_on()176self.this_ptr = new cpp_farey(group, new is_element_Gamma0(p))177sig_off()178elif is_Gamma1(group):179sig_on()180self.this_ptr = new cpp_farey(group, new is_element_Gamma1(p))181sig_off()182elif is_Gamma(group):183sig_on()184self.this_ptr = new cpp_farey(group, new is_element_Gamma(p))185sig_off()186elif is_GammaH(group):187sig_on()188l = group._GammaH_class__H189self.this_ptr = new cpp_farey(group, new is_element_GammaH(p, l))190sig_off()191else:192sig_on()193self.this_ptr = new cpp_farey(group)194sig_off()195196def __deallocpp__(self):197r"""198Remove reference to FareySymbol::199200sage: F = FareySymbol(Gamma0(23))201202sage: del F203204"""205del self.this_ptr206207def __contains__(self, M):208r"""209Tests if element is in the arithmetic group of the Farey symbol210via LLT algorithm.211212EXAMPLES::213214sage: SL2Z([0, -1, 1, 0]) in FareySymbol(Gamma0(6))215False216217sage: SL2Z([1, 1, 0, 1]) in FareySymbol(Gamma0(6))218True219"""220cdef Integer a = M.a()221cdef Integer b = M.b()222cdef Integer c = M.c()223cdef Integer d = M.d()224sig_on()225result = self.this_ptr.is_element(a.value, b.value, c.value, d.value)226sig_off()227return result228229def __cmp__(self, other):230r"""231Compare self to others.232233EXAMPLES::234235sage: FareySymbol(Gamma(2)) == FareySymbol(Gamma0(7))236False237238sage: FareySymbol(Gamma0(23)) == loads(dumps(FareySymbol(Gamma0(23))))239True240"""241cmp_fcts = [lambda fs: fs.coset_reps(),242lambda fs: fs.cusps(),243lambda fs: fs.fractions()]244245for cf in cmp_fcts:246c = cmp(cf(self), cf(other))247if c != 0: return c248249return c250251def __reduce__(self):252r"""253Serialization for pickling::254255sage: FareySymbol(Gamma0(4)).__reduce__()256(<type 'sage.modular.arithgroup.farey_symbol.Farey'>, ...))257258"""259return Farey, (self.group, self.this_ptr.dumps())260261def __repr__(self):262r"""263Return the string representation of self.264265EXAMPLES::266267sage: FareySymbol(Gamma0(23)).__repr__()268'FareySymbol(Congruence Subgroup Gamma0(23))'269"""270if hasattr(self.group, "_repr_"):271return "FareySymbol(%s)" % self.group._repr_()272elif hasattr(self.group, "__repr__"):273return "FareySymbol(%s)" % self.group.__repr__()274else:275return "FareySymbol(?)"276277def _latex_(self, forced_format = None):278r"""279Return the LaTeX representation of self.280281INPUT:282283- ``forced_format`` -- A format string ('plain' or 'xymatrix')284or ``None``.285286EXAMPLES::287288sage: FareySymbol(Gamma0(11))._latex_(forced_format = 'plain')289'\\left( -\\infty\\underbrace{\\quad}_{1} 0\\underbrace{\\quad}_{2} \\frac{1}{3}\\underbrace{\\quad}_{3} \\frac{1}{2}\\underbrace{\\quad}_{2} \\frac{2}{3}\\underbrace{\\quad}_{3} 1\\underbrace{\\quad}_{1} \\infty\\right)'290sage: FareySymbol(Gamma0(11))._latex_(forced_format = 'xymatrix')291'\\begin{xy}\\xymatrix{& -\\infty \\ar@{-}@/_1pc/[r]_{1}& 0 \\ar@{-}@/_1pc/[r]_{2}& \\frac{1}{3} \\ar@{-}@/_1pc/[r]_{3}& \\frac{1}{2} \\ar@{-}@/_1pc/[r]_{2}& \\frac{2}{3} \\ar@{-}@/_1pc/[r]_{3}& 1 \\ar@{-}@/_1pc/[r]_{1}& \\infty }\\end{xy}'292293sage: if '\\xymatrix' in sage.misc.latex.latex.mathjax_avoid_list():294... 'xymatrix' not in FareySymbol(Gamma0(11))._latex_()295... else:296... 'xymatrix' in FareySymbol(Gamma0(11))._latex_()297True298"""299if forced_format == 'plain' or \300(forced_format is None and '\\xymatrix' in latex.mathjax_avoid_list()):301# output not using xymatrix302s = r'\left( -\infty'303a = [x._latex_() for x in self.fractions()] + ['\infty']304b = self.pairings()305for i in xrange(len(a)):306u = b[i]307if u == -3: u = r'\bullet'308elif u == -2: u = r'\circ'309s += r'\underbrace{\quad}_{%s} %s' % (u,a[i])310return s + r'\right)'311else:312# output using xymatrix313s = r'\begin{xy}\xymatrix{& -\infty '314f = [x._latex_() for x in self.fractions()]+[r'\infty']315f.reverse()316for p in self.pairings():317if p >= 0:318s += r'\ar@{-}@/_1pc/[r]_{%s}' % p319elif p == -2:320s += r'\ar@{-}@/_1pc/[r]_{\circ}'321elif p == -3:322s += r'\ar@{-}@/_1pc/[r]_{\bullet}'323s += r'& %s ' % f.pop()324s += r'}\end{xy}'325return s326327def index(self):328r"""329Return the index of the arithmetic group of the FareySymbol330in `{\rm PSL}_2(\ZZ)`.331332EXAMPLES::333334sage: [FareySymbol(Gamma0(n)).index() for n in range(1, 16)]335[1, 3, 4, 6, 6, 12, 8, 12, 12, 18, 12, 24, 14, 24, 24]336"""337return self.this_ptr.index()338339def genus(self):340r"""341Return the genus of the arithmetic group of the FareySymbol.342343EXAMPLES::344345sage: [FareySymbol(Gamma0(n)).genus() for n in range(16, 32)]346[0, 1, 0, 1, 1, 1, 2, 2, 1, 0, 2, 1, 2, 2, 3, 2]347"""348return self.this_ptr.genus()349350def level(self):351r"""352Return the level of the arithmetic group of the FareySymbol.353354EXAMPLES::355356sage: [FareySymbol(Gamma0(n)).level() for n in range(1, 16)]357[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]358"""359return self.this_ptr.level();360361def nu2(self):362r"""363Return the number of elliptic points of order two.364365EXAMPLES::366367sage: [FareySymbol(Gamma0(n)).nu2() for n in range(1, 16)]368[1, 1, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0]369"""370return self.this_ptr.nu2()371372def nu3(self):373r"""374Return the number of elliptic points of order three.375376EXAMPLES::377378sage: [FareySymbol(Gamma0(n)).nu3() for n in range(1, 16)]379[1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0]380"""381return self.this_ptr.nu3()382383def coset_reps(self):384r"""385Left coset of the arithmetic group of the FareySymbol.386387EXAMPLES:388389Calculate the left coset of `\Gamma_0(6)`::390391sage: FareySymbol(Gamma0(6)).coset_reps()392[393[1 0] [ 3 -1] [ 2 -1] [ 1 -1] [ 2 -1] [ 3 -2] [ 1 -1] [-1 0]394[0 1], [ 1 0], [ 1 0], [ 1 0], [ 3 -1], [ 2 -1], [ 2 -1], [ 2 -1],395[ 1 -1] [ 0 -1] [-1 0] [-2 1]396[ 3 -2], [ 1 -1], [ 1 -1], [ 1 -1]397]398"""399return self.this_ptr.get_coset()400401def generators(self):402r"""403Minmal set of generators of the group of the FareySymbol.404405EXAMPLES:406407Calculate the generators of `\Gamma_0(6)`::408409sage: FareySymbol(Gamma0(6)).generators()410[411[1 1] [ 5 -1] [ 7 -3] [-1 0]412[0 1], [ 6 -1], [12 -5], [ 0 -1]413]414415Calculate the generators of `{\rm SL}_2(\ZZ)`::416417sage: FareySymbol(SL2Z).generators()418[419[ 0 -1] [ 0 -1]420[ 1 0], [ 1 -1]421]422423The unique index 2 even subgroup and index 4 odd subgroup each get handled correctly::424425sage: FareySymbol(ArithmeticSubgroup_Permutation(S2="(1,2)", S3="()")).generators()426[427[ 0 1] [-1 1]428[-1 -1], [-1 0]429]430sage: FareySymbol(ArithmeticSubgroup_Permutation(S2="(1,2, 3, 4)", S3="(1,3)(2,4)")).generators()431[432[ 0 1] [-1 1]433[-1 -1], [-1 0]434]435"""436return self.this_ptr.get_generators()437438def fractions(self):439r"""440Fractions of the FareySymbol.441442EXAMPLES::443444sage: FareySymbol(Gamma(4)).fractions()445[0, 1/2, 1, 3/2, 2, 5/2, 3, 7/2, 4]446"""447return self.this_ptr.get_fractions()448449450def pairings(self):451r"""452Pairings of the sides of the fundamental domain of the Farey symbol453of the arithmetic group. The sides of the hyperbolic polygon are454numbered 0, 1, ... from left to right. Conventions: even pairings are455denoted by -2, odd pairings by -3 while free pairings are denoted by456an integer number greater than zero.457458EXAMPLES:459460Odd pairings::461462sage: FareySymbol(Gamma0(7)).pairings()463[1, -3, -3, 1]464465Even and odd pairings::466467FareySymbol(Gamma0(13)).pairings()468[1, -3, -2, -2, -3, 1]469470Only free pairings::471472sage: FareySymbol(Gamma0(23)).pairings()473[1, 2, 3, 5, 3, 4, 2, 4, 5, 1]474"""475return self.this_ptr.get_pairings()476477def paired_sides(self):478r"""479Pairs of index of the sides of the fundamental domain of the480Farey symbol of the arithmetic group. The sides of the481hyperbolic polygon are numbered 0, 1, ... from left to right.482483.. image:: ../../../media/pairing.png484485EXAMPLES::486487sage: FareySymbol(Gamma0(11)).paired_sides()488[(0, 5), (1, 3), (2, 4)]489490indicating that the side 0 is paired with 5, 1 with 3 and 2 with 4.491"""492return self.this_ptr.get_paired_sides()493494def pairing_matrices(self):495r"""496Pairing matrices of the sides of the fundamental domain. The sides497of the hyperbolic polygon are numbered 0, 1, ... from left to right.498499EXAMPLES::500501sage: FareySymbol(Gamma0(6)).pairing_matrices()502[503[1 1] [ 5 -1] [ 7 -3] [ 5 -3] [ 1 -1] [-1 1]504[0 1], [ 6 -1], [12 -5], [12 -7], [ 6 -5], [ 0 -1]505]506"""507508return self.this_ptr.get_pairing_matrices()509510def cusps(self):511r"""512Cusps of the FareySymbol.513514EXAMPLES::515516sage: FareySymbol(Gamma0(6)).cusps()517[0, 1/3, 1/2, Infinity]518"""519return self.this_ptr.get_cusps()+[Cusp(infinity)]520521def cusp_widths(self):522r"""523Cusps widths of the FareySymbol.524525EXAMPLES::526527sage: FareySymbol(Gamma0(6)).cusp_widths()528[6, 2, 3, 1]529"""530return self.this_ptr.get_cusp_widths()531532def cusp_class(self, c):533r"""534Cusp class of a cusp in the FareySymbol.535536EXAMPLES::537538sage: FareySymbol(Gamma0(12)).cusp_class(Cusp(1, 12))5395540"""541cusp = Cusp(c)542cdef Integer p = cusp.numerator()543cdef Integer q = cusp.denominator()544sig_on()545result = self.this_ptr.get_cusp_class(p.value, q.value)546sig_off()547return result548549def reduce_to_cusp(self, r):550r"""551Transformation of a rational number to cusp representative.552553EXAMPLES::554555sage: FareySymbol(Gamma0(12)).reduce_to_cusp(5/8)556[ 5 -3]557[12 -7]558559Reduce 11/17 to a cusp of for HsuExample10()::560561sage: from sage.modular.arithgroup.arithgroup_perm import HsuExample10562sage: f = FareySymbol(HsuExample10())563sage: f.reduce_to_cusp(11/17)564[14 -9]565[-3 2]566sage: _.acton(11/17)5671568sage: f.cusps()[f.cusp_class(11/17)]5691570"""571cdef Integer p = r.numerator()572cdef Integer q = r.denominator()573sig_on()574result = self.this_ptr.get_transformation_to_cusp(p.value, q.value)575sig_off()576return result577578@rename_keyword(rgbcolor='color')579@options(alpha=1, fill=True, thickness=1, color='lightgray', \580color_even='white', \581zorder=2, linestyle='solid', show_pairing=True, \582tesselation='Dedekind', ymax=1583)584def fundamental_domain(self, **options):585r"""586Plot a fundamental domain of an arithmetic subgroup of587`{\rm PSL}_2(\ZZ)` corresponding to the Farey symbol.588589OPTIONS:590591- ``fill`` -- boolean (default ``True``) fill the fundamental domain592593- ``linestyle`` -- string (default: 'solid') The style of the line,594which is one of 'dashed', 'dotted', 'solid', 'dashdot', or '--',595':', '-', '-.', respectively596597- ``color`` -- (default: 'lightgray') fill color; fill598color for odd part of Dedekind tesselation.599600- ``show_pairing`` -- boolean (default: ``True``) flag for pairing601602- ``tesselation`` -- (default: 'Dedekind') The type of603hyperbolic tesselation which is one of604'coset', 'Dedekind' or None respectively605606- ``color_even`` -- fill color for even parts of Dedekind607tesselation (default 'white'); ignored for608other tesselations609610- ``thickness`` -- float (default: `1`) the thickness of the line611612- ``ymax`` -- float (default: `1`) maximal height613614EXAMPLES:615616For example, to plot the fundamental domain of `\Gamma_0(11)`617with pairings use the following command::618619sage: FareySymbol(Gamma0(11)).fundamental_domain()620621indicating that side 1 is paired with side 3 and side 2 is622paired with side 4, see also :meth:`.paired_sides`.623624To plot the fundamental domain of `\Gamma(3)` without pairings625use the following command::626627sage: FareySymbol(Gamma(3)).fundamental_domain(show_pairing=False)628629Plot the fundamental domain of `\Gamma_0(23)` showing the left630coset representatives::631632sage: FareySymbol(Gamma0(23)).fundamental_domain(tesselation='coset')633634The same as above but with a custom linestyle::635636sage: FareySymbol(Gamma0(23)).fundamental_domain(tesselation='coset', linestyle=':', thickness='2')637638"""639from sage.plot.colors import rainbow640I = CC(0, 1)641w = RR(3).sqrt()642L = 1000643g = Graphics()644## show coset645for x in self.coset_reps():646a, b, c, d = x[1, 1], -x[0, 1], -x[1, 0], x[0, 0]647A, B = CC(0, L), CC(0, L)648if d!=0: A = b/d649if c!=0: B = a/c650C = (a*c+b*d+(a*d+b*c)/2+I*w/2)/(c*c+c*d+d*d)651D = (a*c+b*d + CC(0, 1))/(c*c+d*d)652if options['tesselation'] == 'Dedekind':653g += hyperbolic_triangle(A, D, C,654alpha=options['alpha'],655color=options['color'],656fill=options['fill'],657linestyle=options['linestyle'],658thickness=options['thickness'])659g += hyperbolic_triangle(D, C, B,660alpha=options['alpha'],661color=options['color_even'],662fill=options['fill'],663linestyle=options['linestyle'],664thickness=options['thickness'])665g += hyperbolic_triangle(A, D, C, color='gray')666g += hyperbolic_triangle(D, C, B, color='gray')667elif options['tesselation'] == 'coset':668g += hyperbolic_triangle(A, B, C,669alpha=options['alpha'],670color=options['color'],671fill=options['fill'],672linestyle=options['linestyle'],673thickness=options['thickness'])674g += hyperbolic_triangle(A, B, C, color='gray',675linestyle=options['linestyle'],676thickness=options['thickness'])677else:678g += hyperbolic_triangle(A, B, C,679alpha=options['alpha'],680color=options['color'],681fill=options['fill'],682linestyle=options['linestyle'],683thickness=options['thickness'])684## show pairings685p = self.pairings()686x = self.fractions()687if options['show_pairing']:688rc = rainbow(max(p)-min(p)+1)689if p[0] > 0:690g += hyperbolic_arc(CC(0, L), x[0], color=rc[p[0]-min(p)],691linestyle=options['linestyle'],692thickness=options['thickness'])693if p[-1] > 0:694g += hyperbolic_arc(CC(0, L), x[-1], color=rc[p[-1]-min(p)],695linestyle=options['linestyle'],696thickness=options['thickness'])697for i in range(len(x)-1):698if p[i+1] > 0:699g += hyperbolic_arc(x[i], x[i+1], color=rc[p[i+1]-min(p)],700linestyle=options['linestyle'],701thickness=options['thickness'])702d = g.get_minmax_data()703g.set_axes_range(d['xmin'], d['xmax'], 0, options['ymax'])704return g705706707#--- conversions ------------------------------------------------------------708709cdef public long convert_to_long(n):710cdef long m = n711return m712713cdef public object convert_to_Integer(mpz_class a):714A = Integer()715A.set_from_mpz(a.get_mpz_t())716return A717718cdef public object convert_to_rational(mpq_class r):719a = Integer(); a.set_from_mpz(r.get_num_mpz_t())720b = Integer(); b.set_from_mpz(r.get_den_mpz_t())721return a/b722723cdef public object convert_to_cusp(mpq_class r):724a = Integer(); a.set_from_mpz(r.get_num_mpz_t())725b = Integer(); b.set_from_mpz(r.get_den_mpz_t())726return Cusp(a/b)727728cdef public object convert_to_SL2Z(cpp_SL2Z M):729a = convert_to_Integer(M.a())730b = convert_to_Integer(M.b())731c = convert_to_Integer(M.c())732d = convert_to_Integer(M.d())733return SL2Z([a, b, c, d])734735736