Arithmetic subgroups, finite index subgroups of \(\SL_2(\ZZ)\) - Arithmetic Subgroups of $\SL_2(\ZZ)$

Skip to content
Back to top
View this page
Edit this page

# Arithmetic subgroups, finite index subgroups of \(\SL_2(\ZZ)\)

class sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup

  Bases: `Group`

  Base class for arithmetic subgroups of \(\SL_2(\ZZ)\). Not intended to be used directly, but still includes quite a few general-purpose routines which compute data about an arithmetic subgroup assuming that it has a working element testing routine.

  Element

    alias of `ArithmeticSubgroupElement`

  are_equivalent(x, y, trans=False)

    Test whether or not cusps \(x\) and \(y\) are equivalent modulo `self`.

    If `self` has a `reduce_cusp()` method, use that; otherwise do a slow explicit test.

    If `trans == False`, returns `True` or `False`. If `trans == True`, then return either `False` or an element of `self` mapping \(x\) onto \(y\).

    EXAMPLES:

    Sage

        sage: Gamma0(7).are_equivalent(Cusp(1/3), Cusp(0), trans=True)
        [  3  -1]
        [-14   5]
        sage: Gamma0(7).are_equivalent(Cusp(1/3), Cusp(1/7))
        False

    Python

        >>> from sage.all import *
        >>> Gamma0(Integer(7)).are_equivalent(Cusp(Integer(1)/Integer(3)), Cusp(Integer(0)), trans=True)
        [  3  -1]
        [-14   5]
        >>> Gamma0(Integer(7)).are_equivalent(Cusp(Integer(1)/Integer(3)), Cusp(Integer(1)/Integer(7)))
        False

  as_permutation_group()

    Return `self` as an arithmetic subgroup defined in terms of the permutation action of \(SL(2,\ZZ)\) on its right cosets.

    This method uses Todd-Coxeter enumeration (via the method `todd_coxeter()`) which can be extremely slow for arithmetic subgroups with relatively large index in \(SL(2,\ZZ)\).

    EXAMPLES:

    Sage

        sage: # needs sage.groups
        sage: G = Gamma(3)
        sage: P = G.as_permutation_group(); P
        Arithmetic subgroup of index 24
        sage: G.ncusps() == P.ncusps()
        True
        sage: G.nu2() == P.nu2()
        True
        sage: G.nu3() == P.nu3()
        True
        sage: G.an_element() in P
        True
        sage: P.an_element() in G
        True

    Python

        >>> from sage.all import *
        >>> # needs sage.groups
        >>> G = Gamma(Integer(3))
        >>> P = G.as_permutation_group(); P
        Arithmetic subgroup of index 24
        >>> G.ncusps() == P.ncusps()
        True
        >>> G.nu2() == P.nu2()
        True
        >>> G.nu3() == P.nu3()
        True
        >>> G.an_element() in P
        True
        >>> P.an_element() in G
        True

  coset_reps(G=None)

    Return right coset representatives for `self \\ G`, where \(G\) is another arithmetic subgroup that contains `self`. If `G == None`, default to `G = SL2Z`.

    For generic arithmetic subgroups \(G\) this is carried out by Todd-Coxeter enumeration; here \(G\) is treated as a black box, implementing nothing but membership testing.

    EXAMPLES:

    Sage

        sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().coset_reps()
        Traceback (most recent call last):
        ...
        NotImplementedError: Please implement _contains_sl2 for <class 'sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup_with_category'>
        sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.coset_reps(Gamma0(3))
        [
        [1 0]  [ 0 -1]  [ 0 -1]  [ 0 -1]
        [0 1], [ 1  0], [ 1  1], [ 1  2]
        ]

    Python

        >>> from sage.all import *
        >>> sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().coset_reps()
        Traceback (most recent call last):
        ...
        NotImplementedError: Please implement _contains_sl2 for <class 'sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup_with_category'>
        >>> sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.coset_reps(Gamma0(Integer(3)))
        [
        [1 0]  [ 0 -1]  [ 0 -1]  [ 0 -1]
        [0 1], [ 1  0], [ 1  1], [ 1  2]
        ]

  cusp_data(c)

    Return a triple \((g, w, t)\) where \(g\) is an element of `self` generating the stabiliser of the given cusp, \(w\) is the width of the cusp, and \(t\) is 1 if the cusp is regular and -1 if not.

    EXAMPLES:

    Sage

        sage: Gamma1(4).cusp_data(Cusps(1/2))
        (
        [ 1 -1]
        [ 4 -3], 1, -1
        )

    Python

        >>> from sage.all import *
        >>> Gamma1(Integer(4)).cusp_data(Cusps(Integer(1)/Integer(2)))
        (
        [ 1 -1]
        [ 4 -3], 1, -1
        )

  cusp_width(c)

    Return the width of the orbit of cusps represented by c.

    EXAMPLES:

    Sage

        sage: Gamma0(11).cusp_width(Cusps(oo))
        1
        sage: Gamma0(11).cusp_width(0)
        11
        sage: [Gamma0(100).cusp_width(c) for c in Gamma0(100).cusps()]
        [100, 1, 4, 1, 1, 1, 4, 25, 1, 1, 4, 1, 25, 4, 1, 4, 1, 1]

    Python

        >>> from sage.all import *
        >>> Gamma0(Integer(11)).cusp_width(Cusps(oo))
        1
        >>> Gamma0(Integer(11)).cusp_width(Integer(0))
        11
        >>> [Gamma0(Integer(100)).cusp_width(c) for c in Gamma0(Integer(100)).cusps()]
        [100, 1, 4, 1, 1, 1, 4, 25, 1, 1, 4, 1, 25, 4, 1, 4, 1, 1]

  cusps(algorithm='default')

    Return a sorted list of inequivalent cusps for `self`, i.e. a set of representatives for the orbits of `self` on \(\mathbb{P}^1(\QQ)\).

    These should be returned in a reduced form where this makes sense.

    INPUT:

    - `algorithm` – which algorithm to use to compute the cusps of `self`. `'default'` finds representatives for a known complete set of cusps. `'modsym'` computes the boundary map on the space of weight two modular symbols associated to `self`, which finds the cusps for `self` in the process.

    EXAMPLES:

    Sage

        sage: Gamma0(36).cusps()
        [0, 1/18, 1/12, 1/9, 1/6, 1/4, 1/3, 5/12, 1/2, 2/3, 5/6, Infinity]
        sage: Gamma0(36).cusps(algorithm='modsym') == Gamma0(36).cusps()            # needs sage.libs.flint
        True
        sage: GammaH(36, [19,29]).cusps() == Gamma0(36).cusps()
        True
        sage: Gamma0(1).cusps()
        [Infinity]

    Python

        >>> from sage.all import *
        >>> Gamma0(Integer(36)).cusps()
        [0, 1/18, 1/12, 1/9, 1/6, 1/4, 1/3, 5/12, 1/2, 2/3, 5/6, Infinity]
        >>> Gamma0(Integer(36)).cusps(algorithm='modsym') == Gamma0(Integer(36)).cusps()            # needs sage.libs.flint
        True
        >>> GammaH(Integer(36), [Integer(19),Integer(29)]).cusps() == Gamma0(Integer(36)).cusps()
        True
        >>> Gamma0(Integer(1)).cusps()
        [Infinity]

  dimension_cusp_forms(k=2)

    Return the dimension of the space of weight k cusp forms for this group.

    For \(k \ge 2\), this is given by a standard formula in terms of k and various invariants of the group; see Diamond + Shurman, “A First Course in Modular Forms”, section 3.5 and 3.6. If k is not given, default to k = 2.

    For dimensions of spaces of cusp forms with character for Gamma1, use the dimension_cusp_forms method of the Gamma1 class, or the standalone function dimension_cusp_forms().

    For weight 1 cusp forms this generic implementation only works in cases where one can prove solely via Riemann-Roch theory that there aren’t any cusp forms (i.e. when the number of regular cusps is strictly greater than the degree of the canonical divisor). Otherwise a `NotImplementedError` is raised.

    EXAMPLES:

    Sage

        sage: Gamma1(31).dimension_cusp_forms(2)
        26
        sage: Gamma(5).dimension_cusp_forms(1)
        0
        sage: Gamma1(4).dimension_cusp_forms(1) # irregular cusp
        0
        sage: Gamma(13).dimension_cusp_forms(1)
        Traceback (most recent call last):
        ...
        NotImplementedError: Computation of dimensions of weight 1 cusp forms spaces not implemented in general

    Python

        >>> from sage.all import *
        >>> Gamma1(Integer(31)).dimension_cusp_forms(Integer(2))
        26
        >>> Gamma(Integer(5)).dimension_cusp_forms(Integer(1))
        0
        >>> Gamma1(Integer(4)).dimension_cusp_forms(Integer(1)) # irregular cusp
        0
        >>> Gamma(Integer(13)).dimension_cusp_forms(Integer(1))
        Traceback (most recent call last):
        ...
        NotImplementedError: Computation of dimensions of weight 1 cusp forms spaces not implemented in general

  dimension_eis(k=2)

    Return the dimension of the space of weight k Eisenstein series for this group, which is a subspace of the space of modular forms complementary to the space of cusp forms.

    INPUT:

    - `k` – integer (default: 2)

    EXAMPLES:

    Sage

        sage: GammaH(33,[2]).dimension_eis()
        7
        sage: GammaH(33,[2]).dimension_eis(3)
        0
        sage: GammaH(33, [2,5]).dimension_eis(2)
        3
        sage: GammaH(33, [4]).dimension_eis(1)
        4

    Python

        >>> from sage.all import *
        >>> GammaH(Integer(33),[Integer(2)]).dimension_eis()
        7
        >>> GammaH(Integer(33),[Integer(2)]).dimension_eis(Integer(3))
        0
        >>> GammaH(Integer(33), [Integer(2),Integer(5)]).dimension_eis(Integer(2))
        3
        >>> GammaH(Integer(33), [Integer(4)]).dimension_eis(Integer(1))
        4

  dimension_modular_forms(k=2)

    Return the dimension of the space of weight k modular forms for this group.

    This is given by a standard formula in terms of k and various invariants of the group; see Diamond + Shurman, “A First Course in Modular Forms”, section 3.5 and 3.6. If k is not given, defaults to k = 2.

    For dimensions of spaces of modular forms with character for Gamma1, use the dimension_modular_forms method of the Gamma1 class, or the standalone function dimension_modular_forms().

    For weight 1 modular forms this generic implementation only works in cases where one can prove solely via Riemann-Roch theory that there aren’t any cusp forms (i.e. when the number of regular cusps is strictly greater than the degree of the canonical divisor). Otherwise a `NotImplementedError` is raised.

    EXAMPLES:

    Sage

        sage: Gamma1(31).dimension_modular_forms(2)
        55
        sage: Gamma1(3).dimension_modular_forms(1)
        1
        sage: Gamma1(4).dimension_modular_forms(1) # irregular cusp
        1
        sage: Gamma(13).dimension_modular_forms(1)
        Traceback (most recent call last):
        ...
        NotImplementedError: Computation of dimensions of weight 1 cusp forms spaces not implemented in general

    Python

        >>> from sage.all import *
        >>> Gamma1(Integer(31)).dimension_modular_forms(Integer(2))
        55
        >>> Gamma1(Integer(3)).dimension_modular_forms(Integer(1))
        1
        >>> Gamma1(Integer(4)).dimension_modular_forms(Integer(1)) # irregular cusp
        1
        >>> Gamma(Integer(13)).dimension_modular_forms(Integer(1))
        Traceback (most recent call last):
        ...
        NotImplementedError: Computation of dimensions of weight 1 cusp forms spaces not implemented in general

  farey_symbol()

    Return the Farey symbol associated to this subgroup.

    See the `farey_symbol` module for more information.

    EXAMPLES:

    Sage

        sage: Gamma1(4).farey_symbol()
        FareySymbol(Congruence Subgroup Gamma1(4))

    Python

        >>> from sage.all import *
        >>> Gamma1(Integer(4)).farey_symbol()
        FareySymbol(Congruence Subgroup Gamma1(4))

  gen(i)

    Return the \(i\)-th generator of self, i.e. the \(i\)-th element of the tuple `self.gens()`.

    EXAMPLES:

    Sage

        sage: SL2Z.gen(1)
        [1 1]
        [0 1]

    Python

        >>> from sage.all import *
        >>> SL2Z.gen(Integer(1))
        [1 1]
        [0 1]

  generalised_level()

    Return the generalised level of `self`, i.e., the least common multiple of the widths of all cusps.

    If `self` is even, Wohlfart’s theorem tells us that this is equal to the (conventional) level of `self` when `self` is a congruence subgroup. This can fail if `self` is odd, but the actual level is at most twice the generalised level. See the paper by Kiming, Schuett and Verrill for more examples.

    EXAMPLES:

    Sage

        sage: Gamma0(18).generalised_level()
        18
        sage: from sage.modular.arithgroup.arithgroup_perm import HsuExample18      # needs sage.groups
        sage: HsuExample18().generalised_level()                                    # needs sage.groups
        24

    Python

        >>> from sage.all import *
        >>> Gamma0(Integer(18)).generalised_level()
        18
        >>> from sage.modular.arithgroup.arithgroup_perm import HsuExample18      # needs sage.groups
        >>> HsuExample18().generalised_level()                                    # needs sage.groups
        24

    In the following example, the actual level is twice the generalised level. This is the group \(G_2\) from Example 17 of K-S-V.

    Sage

        sage: G = CongruenceSubgroup(8, [ [1,1,0,1], [3,-1,4,-1] ])
        sage: G.level()
        8
        sage: G.generalised_level()
        4

    [Python]

        >>> from sage.all import *
        >>> G = CongruenceSubgroup(Integer(8), [ [Integer(1),Integer(1),Integer(0),Integer(1)], [Integer(3),-Integer(1),Integer(4),-Integer(1)] ])
        >>> G.level()
        8
        >>> G.generalised_level()
        4

  generators(algorithm='farey')

    Return a list of generators for this congruence subgroup. The result is cached.

    INPUT:

    - `algorithm` – string; either `farey` or `todd-coxeter`

    If `algorithm` is set to `'farey'`, then the generators will be calculated using Farey symbols, which will always return a minimal generating set. See `farey_symbol` for more information.

    If `algorithm` is set to `'todd-coxeter'`, a simpler algorithm based on Todd-Coxeter enumeration will be used. This is exceedingly slow for general subgroups, and the list of generators will be far from minimal (indeed it may contain repetitions).

    EXAMPLES:

    Sage

        sage: Gamma(2).generators()
        [
        [1 2]  [ 3 -2]  [-1  0]
        [0 1], [ 2 -1], [ 0 -1]
        ]
        sage: Gamma(2).generators(algorithm='todd-coxeter')
        [
        [1 2]  [-1  0]  [ 1  0]  [-1  0]  [-1  2]  [-1  0]  [1 0]
        [0 1], [ 0 -1], [-2  1], [ 0 -1], [-2  3], [ 2 -1], [2 1]
        ]

    Python

        >>> from sage.all import *
        >>> Gamma(Integer(2)).generators()
        [
        [1 2]  [ 3 -2]  [-1  0]
        [0 1], [ 2 -1], [ 0 -1]
        ]
        >>> Gamma(Integer(2)).generators(algorithm='todd-coxeter')
        [
        [1 2]  [-1  0]  [ 1  0]  [-1  0]  [-1  2]  [-1  0]  [1 0]
        [0 1], [ 0 -1], [-2  1], [ 0 -1], [-2  3], [ 2 -1], [2 1]
        ]

  gens(*args, **kwds)

    Return a tuple of generators for this congruence subgroup.

    The generators need not be minimal. For arguments, see `generators()`.

    EXAMPLES:

    Sage

        sage: SL2Z.gens()
        (
        [ 0 -1]  [1 1]
        [ 1  0], [0 1]
        )

    Python

        >>> from sage.all import *
        >>> SL2Z.gens()
        (
        [ 0 -1]  [1 1]
        [ 1  0], [0 1]
        )

  genus()

    Return the genus of the modular curve of `self`.

    EXAMPLES:

    Sage

        sage: Gamma1(5).genus()
        0
        sage: Gamma1(31).genus()
        26
        sage: from sage.modular.dims import dimension_cusp_forms
        sage: Gamma1(157).genus() == dimension_cusp_forms(Gamma1(157), 2)
        True
        sage: GammaH(7, [2]).genus()
        0
        sage: [Gamma0(n).genus() for n in [1..23]]
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 2, 2]
        sage: [n for n in [1..200] if Gamma0(n).genus() == 1]
        [11, 14, 15, 17, 19, 20, 21, 24, 27, 32, 36, 49]

    Python

        >>> from sage.all import *
        >>> Gamma1(Integer(5)).genus()
        0
        >>> Gamma1(Integer(31)).genus()
        26
        >>> from sage.modular.dims import dimension_cusp_forms
        >>> Gamma1(Integer(157)).genus() == dimension_cusp_forms(Gamma1(Integer(157)), Integer(2))
        True
        >>> GammaH(Integer(7), [Integer(2)]).genus()
        0
        >>> [Gamma0(n).genus() for n in (ellipsis_range(Integer(1),Ellipsis,Integer(23)))]
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 2, 2]
        >>> [n for n in (ellipsis_range(Integer(1),Ellipsis,Integer(200))) if Gamma0(n).genus() == Integer(1)]
        [11, 14, 15, 17, 19, 20, 21, 24, 27, 32, 36, 49]

  index()

    Return the index of `self` in the full modular group.

    EXAMPLES:

    Sage

        sage: Gamma0(17).index()
        18
        sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5).index()
        Traceback (most recent call last):
        ...
        NotImplementedError

    Python

        >>> from sage.all import *
        >>> Gamma0(Integer(17)).index()
        18
        >>> sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(Integer(5)).index()
        Traceback (most recent call last):
        ...
        NotImplementedError

  is_abelian()

    Return `True` if this arithmetic subgroup is abelian.

    Since arithmetic subgroups are always nonabelian, this always returns `False`.

    EXAMPLES:

    Sage

        sage: SL2Z.is_abelian()
        False
        sage: Gamma0(3).is_abelian()
        False
        sage: Gamma1(12).is_abelian()
        False
        sage: GammaH(4, [3]).is_abelian()
        False

    Python

        >>> from sage.all import *
        >>> SL2Z.is_abelian()
        False
        >>> Gamma0(Integer(3)).is_abelian()
        False
        >>> Gamma1(Integer(12)).is_abelian()
        False
        >>> GammaH(Integer(4), [Integer(3)]).is_abelian()
        False

  is_congruence()

    Return `True` if `self` is a congruence subgroup.

    EXAMPLES:

    Sage

        sage: Gamma0(5).is_congruence()
        True
        sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().is_congruence()
        Traceback (most recent call last):
        ...
        NotImplementedError

    Python

        >>> from sage.all import *
        >>> Gamma0(Integer(5)).is_congruence()
        True
        >>> sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().is_congruence()
        Traceback (most recent call last):
        ...
        NotImplementedError

  is_even()

    Return `True` precisely if this subgroup contains the matrix -1.

    EXAMPLES:

    Sage

        sage: SL2Z.is_even()
        True
        sage: Gamma0(20).is_even()
        True
        sage: Gamma1(5).is_even()
        False
        sage: GammaH(11, [3]).is_even()
        False

    Python

        >>> from sage.all import *
        >>> SL2Z.is_even()
        True
        >>> Gamma0(Integer(20)).is_even()
        True
        >>> Gamma1(Integer(5)).is_even()
        False
        >>> GammaH(Integer(11), [Integer(3)]).is_even()
        False

  is_finite()

    Return `True` if this arithmetic subgroup is finite.

    Since arithmetic subgroups are always infinite, this always returns `False`.

    EXAMPLES:

    Sage

        sage: SL2Z.is_finite()
        False
        sage: Gamma0(3).is_finite()
        False
        sage: Gamma1(12).is_finite()
        False
        sage: GammaH(4, [3]).is_finite()
        False

    Python

        >>> from sage.all import *
        >>> SL2Z.is_finite()
        False
        >>> Gamma0(Integer(3)).is_finite()
        False
        >>> Gamma1(Integer(12)).is_finite()
        False
        >>> GammaH(Integer(4), [Integer(3)]).is_finite()
        False

  is_normal()

    Return `True` precisely if this subgroup is a normal subgroup of `SL2Z`.

    EXAMPLES:

    Sage

        sage: Gamma(3).is_normal()
        True
        sage: Gamma1(3).is_normal()
        False

    Python

        >>> from sage.all import *
        >>> Gamma(Integer(3)).is_normal()
        True
        >>> Gamma1(Integer(3)).is_normal()
        False

  is_odd()

    Return `True` precisely if this subgroup does not contain the matrix -1.

    EXAMPLES:

    Sage

        sage: SL2Z.is_odd()
        False
        sage: Gamma0(20).is_odd()
        False
        sage: Gamma1(5).is_odd()
        True
        sage: GammaH(11, [3]).is_odd()
        True

    Python

        >>> from sage.all import *
        >>> SL2Z.is_odd()
        False
        >>> Gamma0(Integer(20)).is_odd()
        False
        >>> Gamma1(Integer(5)).is_odd()
        True
        >>> GammaH(Integer(11), [Integer(3)]).is_odd()
        True

  is_parent_of(x)

    Check whether this group is a valid parent for the element x.

    Required by Sage’s testing framework.

    EXAMPLES:

    Sage

        sage: Gamma(3).is_parent_of(ZZ(1))
        False
        sage: Gamma(3).is_parent_of([1,0,0,1])
        False
        sage: Gamma(3).is_parent_of(SL2Z([1,1,0,1]))
        False
        sage: Gamma(3).is_parent_of(SL2Z(1))
        True

    Python

        >>> from sage.all import *
        >>> Gamma(Integer(3)).is_parent_of(ZZ(Integer(1)))
        False
        >>> Gamma(Integer(3)).is_parent_of([Integer(1),Integer(0),Integer(0),Integer(1)])
        False
        >>> Gamma(Integer(3)).is_parent_of(SL2Z([Integer(1),Integer(1),Integer(0),Integer(1)]))
        False
        >>> Gamma(Integer(3)).is_parent_of(SL2Z(Integer(1)))
        True

  is_regular_cusp(c)

    Return `True` if the orbit of the given cusp is a regular cusp for `self`, otherwise `False`.

    This is automatically true if -1 is in `self`.

    EXAMPLES:

    Sage

        sage: Gamma1(4).is_regular_cusp(Cusps(1/2))
        False
        sage: Gamma1(4).is_regular_cusp(Cusps(oo))
        True

    Python

        >>> from sage.all import *
        >>> Gamma1(Integer(4)).is_regular_cusp(Cusps(Integer(1)/Integer(2)))
        False
        >>> Gamma1(Integer(4)).is_regular_cusp(Cusps(oo))
        True

  is_subgroup(right)

    Return `True` if `self` is a subgroup of right, and `False` otherwise.

    For generic arithmetic subgroups this is done by the absurdly slow algorithm of checking all of the generators of `self` to see if they are in `right`.

    EXAMPLES:

    Sage

        sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().is_subgroup(SL2Z)
        Traceback (most recent call last):
        ...
        NotImplementedError: Please implement _contains_sl2 for <class 'sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup_with_category'>
        sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.is_subgroup(Gamma1(18), Gamma0(6))
        True

    Python

        >>> from sage.all import *
        >>> sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().is_subgroup(SL2Z)
        Traceback (most recent call last):
        ...
        NotImplementedError: Please implement _contains_sl2 for <class 'sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup_with_category'>
        >>> sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.is_subgroup(Gamma1(Integer(18)), Gamma0(Integer(6)))
        True

  matrix_space()

    Return the parent space of the matrices, which is always `MatrixSpace(ZZ, 2)`.

    EXAMPLES:

    Sage

        sage: Gamma(3).matrix_space()
        Full MatrixSpace of 2 by 2 dense matrices over Integer Ring

    Python

        >>> from sage.all import *
        >>> Gamma(Integer(3)).matrix_space()
        Full MatrixSpace of 2 by 2 dense matrices over Integer Ring

  ncusps()

    Return the number of cusps of this arithmetic subgroup.

    This is provided as a separate function since for dimension formulae in even weight all we need to know is the number of cusps, and this can be calculated very quickly, while enumerating all cusps is much slower.

    EXAMPLES:

    Sage

        sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.ncusps(Gamma0(7))
        2

    Python

        >>> from sage.all import *
        >>> sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.ncusps(Gamma0(Integer(7)))
        2

  ngens()

    Return the size of the minimal generating set of `self` returned by `generators()`.

    EXAMPLES:

    Sage

        sage: Gamma0(22).ngens()
        8
        sage: Gamma1(14).ngens()
        13
        sage: GammaH(11, [3]).ngens()
        3
        sage: SL2Z.ngens()
        2

    Python

        >>> from sage.all import *
        >>> Gamma0(Integer(22)).ngens()
        8
        >>> Gamma1(Integer(14)).ngens()
        13
        >>> GammaH(Integer(11), [Integer(3)]).ngens()
        3
        >>> SL2Z.ngens()
        2

  nirregcusps()

    Return the number of cusps of `self` that are “irregular”, i.e. their stabiliser can only be generated by elements with both eigenvalues -1 rather than +1.

    If the group contains -1, every cusp is clearly regular.

    EXAMPLES:

    Sage

        sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.nirregcusps(Gamma1(4))
        1

    Python

        >>> from sage.all import *
        >>> sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.nirregcusps(Gamma1(Integer(4)))
        1

  nregcusps()

    Return the number of cusps of `self` that are “regular”, i.e. their stabiliser has a generator with both eigenvalues +1 rather than -1.

    If the group contains -1, every cusp is clearly regular.

    EXAMPLES:

    Sage

        sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.nregcusps(Gamma1(4))
        2

    Python

        >>> from sage.all import *
        >>> sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.nregcusps(Gamma1(Integer(4)))
        2

  nu2()

    Return the number of orbits of elliptic points of order 2 for this arithmetic subgroup.

    EXAMPLES:

    Sage

        sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().nu2()
        Traceback (most recent call last):
        ...
        NotImplementedError: Please implement _contains_sl2 for <class 'sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup_with_category'>
        sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.nu2(Gamma0(1105)) == 8
        True

    Python

        >>> from sage.all import *
        >>> sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().nu2()
        Traceback (most recent call last):
        ...
        NotImplementedError: Please implement _contains_sl2 for <class 'sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup_with_category'>
        >>> sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.nu2(Gamma0(Integer(1105))) == Integer(8)
        True

  nu3()

    Return the number of orbits of elliptic points of order 3 for this arithmetic subgroup.

    EXAMPLES:

    Sage

        sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().nu3()
        Traceback (most recent call last):
        ...
        NotImplementedError: Please implement _contains_sl2 for <class 'sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup_with_category'>
        sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.nu3(Gamma0(1729)) == 8
        True

    Python

        >>> from sage.all import *
        >>> sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().nu3()
        Traceback (most recent call last):
        ...
        NotImplementedError: Please implement _contains_sl2 for <class 'sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup_with_category'>
        >>> sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.nu3(Gamma0(Integer(1729))) == Integer(8)
        True

    We test that a bug in handling of subgroups not containing -1 is fixed:

    Sage

        sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.nu3(GammaH(7, [2]))
        2

    [Python]

        >>> from sage.all import *
        >>> sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.nu3(GammaH(Integer(7), [Integer(2)]))
        2

  order()

    Return the number of elements in this arithmetic subgroup.

    Since arithmetic subgroups are always infinite, this always returns infinity.

    EXAMPLES:

    Sage

        sage: SL2Z.order()
        +Infinity
        sage: Gamma0(5).order()
        +Infinity
        sage: Gamma1(2).order()
        +Infinity
        sage: GammaH(12, [5]).order()
        +Infinity

    Python

        >>> from sage.all import *
        >>> SL2Z.order()
        +Infinity
        >>> Gamma0(Integer(5)).order()
        +Infinity
        >>> Gamma1(Integer(2)).order()
        +Infinity
        >>> GammaH(Integer(12), [Integer(5)]).order()
        +Infinity

  projective_index()

    Return the index of the image of `self` in \(\PSL_2(\ZZ)\). This is equal to the index of `self` if `self` contains -1, and half of this otherwise.

    This is equal to the degree of the natural map from the modular curve of `self` to the \(j\)-line.

    EXAMPLES:

    Sage

        sage: Gamma0(5).projective_index()
        6
        sage: Gamma1(5).projective_index()
        12

    Python

        >>> from sage.all import *
        >>> Gamma0(Integer(5)).projective_index()
        6
        >>> Gamma1(Integer(5)).projective_index()
        12

  reduce_cusp(c)

    Given a cusp \(c \in \mathbb{P}^1(\QQ)\), return the unique reduced cusp equivalent to c under the action of self, where a reduced cusp is an element \(\tfrac{r}{s}\) with r,s coprime nonnegative integers, s as small as possible, and r as small as possible for that s.

    Note

    This function should be overridden by all subclasses.

    EXAMPLES:

    Sage

        sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().reduce_cusp(1/4)
        Traceback (most recent call last):
        ...
        NotImplementedError

    Python

        >>> from sage.all import *
        >>> sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().reduce_cusp(Integer(1)/Integer(4))
        Traceback (most recent call last):
        ...
        NotImplementedError

  sturm_bound(weight=2)

    Return the Sturm bound for modular forms of the given weight and level this subgroup.

    INPUT:

    - `weight` – integer \(\geq 2\) (default: 2)

    EXAMPLES:

    Sage

        sage: Gamma0(11).sturm_bound(2)
        2
        sage: Gamma0(389).sturm_bound(2)
        65
        sage: Gamma0(1).sturm_bound(12)
        1
        sage: Gamma0(100).sturm_bound(2)
        30
        sage: Gamma0(1).sturm_bound(36)
        3
        sage: Gamma0(11).sturm_bound()
        2
        sage: Gamma0(13).sturm_bound()
        3
        sage: Gamma0(16).sturm_bound()
        4
        sage: GammaH(16,[13]).sturm_bound()
        8
        sage: GammaH(16,[15]).sturm_bound()
        16
        sage: Gamma1(16).sturm_bound()
        32
        sage: Gamma1(13).sturm_bound()
        28
        sage: Gamma1(13).sturm_bound(5)
        70

    Python

        >>> from sage.all import *
        >>> Gamma0(Integer(11)).sturm_bound(Integer(2))
        2
        >>> Gamma0(Integer(389)).sturm_bound(Integer(2))
        65
        >>> Gamma0(Integer(1)).sturm_bound(Integer(12))
        1
        >>> Gamma0(Integer(100)).sturm_bound(Integer(2))
        30
        >>> Gamma0(Integer(1)).sturm_bound(Integer(36))
        3
        >>> Gamma0(Integer(11)).sturm_bound()
        2
        >>> Gamma0(Integer(13)).sturm_bound()
        3
        >>> Gamma0(Integer(16)).sturm_bound()
        4
        >>> GammaH(Integer(16),[Integer(13)]).sturm_bound()
        8
        >>> GammaH(Integer(16),[Integer(15)]).sturm_bound()
        16
        >>> Gamma1(Integer(16)).sturm_bound()
        32
        >>> Gamma1(Integer(13)).sturm_bound()
        28
        >>> Gamma1(Integer(13)).sturm_bound(Integer(5))
        70

    FURTHER DETAILS: This function returns a positive integer \(n\) such that the Hecke operators \(T_1,\ldots, T_n\) acting on cusp forms generate the Hecke algebra as a \(\ZZ\)-module when the character is trivial or quadratic. Otherwise, \(T_1,\ldots,T_n\) generate the Hecke algebra at least as a \(\ZZ[\varepsilon]\)-module, where \(\ZZ[\varepsilon]\) is the ring generated by the values of the Dirichlet character \(\varepsilon\). Alternatively, this is a bound such that if two cusp forms associated to this space of modular symbols are congruent modulo \((\lambda, q^n)\), then they are congruent modulo \(\lambda\).

    REFERENCES:

    - See the Agashe-Stein appendix to Lario and Schoof, Some computations with Hecke rings and deformation rings, Experimental Math., 11 (2002), no. 2, 303-311.

    - This result originated in the paper Sturm, On the congruence of modular forms, Springer LNM 1240, 275-280, 1987.

    REMARK: Kevin Buzzard pointed out to me (William Stein) in Fall 2002 that the above bound is fine for \(\Gamma_1(N)\) with character, as one sees by taking a power of \(f\). More precisely, if \(f \cong 0 \pmod{p}\) for first \(s\) coefficients, then \(f^r \cong 0 \pmod{p}\) for first \(sr\) coefficients. Since the weight of \(f^r\) is \(r\cdot k(f)\), it follows that if \(s \geq b\), where \(b\) is the Sturm bound for \(\Gamma_0(N)\) at weight \(k(f)\), then \(f^r\) has valuation large enough to be forced to be \(0\) at \(r*k(f)\) by Sturm bound (which is valid if we choose \(r\) correctly). Thus \(f \cong 0 \pmod{p}\). Conclusion: For \(\Gamma_1(N)\) with fixed character, the Sturm bound is exactly the same as for \(\Gamma_0(N)\).

    A key point is that we are finding \(\ZZ[\varepsilon]\) generators for the Hecke algebra here, not \(\ZZ\)-generators. So if one wants generators for the Hecke algebra over \(\ZZ\), this bound must be suitably modified (and I’m not sure what the modification is).

    AUTHORS:

    - William Stein

  to_even_subgroup()

    Return the smallest even subgroup of \(SL(2, \ZZ)\) containing `self`.

    EXAMPLES:

    Sage

        sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().to_even_subgroup()
        Traceback (most recent call last):
        ...
        NotImplementedError: Please implement _contains_sl2 for <class 'sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup_with_category'>

    Python

        >>> from sage.all import *
        >>> sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().to_even_subgroup()
        Traceback (most recent call last):
        ...
        NotImplementedError: Please implement _contains_sl2 for <class 'sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup_with_category'>

  todd_coxeter(G=None, on_right=True)

    Compute coset representatives for `self \\ G` and action of standard generators on them via Todd-Coxeter enumeration.

    If `G` is `None`, default to `SL2Z`. The method also computes generators of the subgroup at same time.

    INPUT:

    - `G` – intermediate subgroup (currently not implemented if different from SL(2,Z))

    - `on_right` – boolean (default: `True`); if `True` return right coset enumeration, if `False` return left one

    This is extremely slow in general.

    OUTPUT: list of coset representatives

    - a list of generators for the group

    - `l` – list of integers that correspond to the action of the standard parabolic element [[1,1],[0,1]] of \(SL(2,\ZZ)\) on the cosets of `self`.

    - `s` – list of integers that correspond to the action of the standard element of order \(2\) [[0,-1],[1,0]] on the cosets of `self`

    EXAMPLES:

    Sage

        sage: L = SL2Z([1,1,0,1])
        sage: S = SL2Z([0,-1,1,0])

        sage: G = Gamma(2)
        sage: reps, gens, l, s = G.todd_coxeter()
        sage: len(reps) == G.index()
        True
        sage: all(reps[i] * L * ~reps[l[i]] in G for i in range(6))
        True
        sage: all(reps[i] * S * ~reps[s[i]] in G for i in range(6))
        True

        sage: G = Gamma0(7)
        sage: reps, gens, l, s = G.todd_coxeter()
        sage: len(reps) == G.index()
        True
        sage: all(reps[i] * L * ~reps[l[i]] in G for i in range(8))
        True
        sage: all(reps[i] * S * ~reps[s[i]] in G for i in range(8))
        True

        sage: G = Gamma1(3)
        sage: reps, gens, l, s = G.todd_coxeter(on_right=False)
        sage: len(reps) == G.index()
        True
        sage: all(~reps[l[i]] * L * reps[i] in G for i in range(8))
        True
        sage: all(~reps[s[i]] * S * reps[i] in G for i in range(8))
        True

        sage: G = Gamma0(5)
        sage: reps, gens, l, s = G.todd_coxeter(on_right=False)
        sage: len(reps) == G.index()
        True
        sage: all(~reps[l[i]] * L * reps[i] in G for i in range(6))
        True
        sage: all(~reps[s[i]] * S * reps[i] in G for i in range(6))
        True

    Python

        >>> from sage.all import *
        >>> L = SL2Z([Integer(1),Integer(1),Integer(0),Integer(1)])
        >>> S = SL2Z([Integer(0),-Integer(1),Integer(1),Integer(0)])

        >>> G = Gamma(Integer(2))
        >>> reps, gens, l, s = G.todd_coxeter()
        >>> len(reps) == G.index()
        True
        >>> all(reps[i] * L * ~reps[l[i]] in G for i in range(Integer(6)))
        True
        >>> all(reps[i] * S * ~reps[s[i]] in G for i in range(Integer(6)))
        True

        >>> G = Gamma0(Integer(7))
        >>> reps, gens, l, s = G.todd_coxeter()
        >>> len(reps) == G.index()
        True
        >>> all(reps[i] * L * ~reps[l[i]] in G for i in range(Integer(8)))
        True
        >>> all(reps[i] * S * ~reps[s[i]] in G for i in range(Integer(8)))
        True

        >>> G = Gamma1(Integer(3))
        >>> reps, gens, l, s = G.todd_coxeter(on_right=False)
        >>> len(reps) == G.index()
        True
        >>> all(~reps[l[i]] * L * reps[i] in G for i in range(Integer(8)))
        True
        >>> all(~reps[s[i]] * S * reps[i] in G for i in range(Integer(8)))
        True

        >>> G = Gamma0(Integer(5))
        >>> reps, gens, l, s = G.todd_coxeter(on_right=False)
        >>> len(reps) == G.index()
        True
        >>> all(~reps[l[i]] * L * reps[i] in G for i in range(Integer(6)))
        True
        >>> all(~reps[s[i]] * S * reps[i] in G for i in range(Integer(6)))
        True