Esempio n. 1
0
    def cusp_data(self, c):
        r"""
        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: Gamma1(4).cusp_data(Cusps(1/2))
            (
            [ 1 -1]
            [ 4 -3], 1, -1
            )
        """
        c = Cusp(c)
        from all import SL2Z  # can't import at top as that would cause a circular import

        # first find an element of SL2Z sending infinity to the given cusp
        w = lift_to_sl2z(c.denominator(), c.numerator(), 0)
        g = SL2Z([w[3], w[1], w[2], w[0]])

        for d in xrange(1, 1 + self.index()):
            if g * SL2Z([1, d, 0, 1]) * (~g) in self:
                return (g * SL2Z([1, d, 0, 1]) * (~g), d, 1)
            elif g * SL2Z([-1, -d, 0, -1]) * (~g) in self:
                return (g * SL2Z([-1, -d, 0, -1]) * (~g), d, -1)
        raise ArithmeticError, "Can't get here!"
Esempio n. 2
0
    def coset_reps(self):
        r"""
        Return representatives for the right cosets of this congruence
        subgroup in `{\rm SL}_2(\ZZ)` as a generator object.

        Use ``list(self.coset_reps())`` to obtain coset reps as a
        list.

        EXAMPLES::

            sage: list(Gamma0(5).coset_reps())
            [
            [1 0]  [ 0 -1]  [1 0]  [ 0 -1]  [ 0 -1]  [ 0 -1]
            [0 1], [ 1  0], [1 1], [ 1  2], [ 1  3], [ 1  4]
            ]
            sage: list(Gamma0(4).coset_reps())
            [
            [1 0]  [ 0 -1]  [1 0]  [ 0 -1]  [ 0 -1]  [1 0]
            [0 1], [ 1  0], [1 1], [ 1  2], [ 1  3], [2 1]
            ]
            sage: list(Gamma0(1).coset_reps())
            [
            [1 0]
            [0 1]
            ]
        """
        from all import SL2Z
        N = self.level()
        if N == 1:  # P1List isn't very happy working modulo 1
            yield SL2Z([1, 0, 0, 1])
        else:
            for z in sage.modular.modsym.p1list.P1List(N):
                yield SL2Z(lift_to_sl2z(z[0], z[1], N))
Esempio n. 3
0
    def test_todd_coxeter(self):
        r"""
        Test representatives of Todd-Coxeter algorithm

        EXAMPLES::

            sage: from sage.modular.arithgroup.tests import Test
            sage: Test().test_todd_coxeter() #random
        """
        from all import SL2Z
        from arithgroup_perm import S2m, S3m, Lm, Rm

        G = random_even_arithgroup(self.index)

        reps, gens, l, s2 = G.todd_coxeter_l_s2()
        assert reps[0] == SL2Z([1, 0, 0, 1])
        assert len(reps) == G.index()
        for i in xrange(1, len(reps)):
            assert reps[i] not in G
            assert reps[i] * S2m * ~reps[s2[i]] in G
            assert reps[i] * Lm * ~reps[l[i]] in G
            for j in xrange(i + 1, len(reps)):
                assert reps[i] * ~reps[j] not in G
                assert reps[j] * ~reps[i] not in G

        reps, gens, s2, s3 = G.todd_coxeter_s2_s3()
        assert reps[0] == SL2Z([1, 0, 0, 1])
        assert len(reps) == G.index()
        for i in xrange(1, len(reps)):
            assert reps[i] not in G
            assert reps[i] * S2m * ~reps[s2[i]] in G
            assert reps[i] * S3m * ~reps[s3[i]] in G
            for j in xrange(i + 1, len(reps)):
                assert reps[i] * ~reps[j] not in G
                assert reps[j] * ~reps[i] not in G
Esempio n. 4
0
    def __call__(self, x, check=True):
        r"""
        Create an element of this congruence subgroup from x.

        If the optional flag check is True (default), check whether
        x actually gives an element of self.

        EXAMPLES::

            sage: G = Gamma1(5)
            sage: G([1, 0, -10, 1])
            [ 1   0]
            [-10  1]
            sage: G(matrix(ZZ, 2, [6, 1, 5, 1]))
            [6  1]
            [5  1]
            sage: G([1, 1, 6, 7])
            Traceback (most recent call last):
            ...
            TypeError: matrix must have diagonal entries (=1, 7) congruent to 1 modulo 5, and lower left entry (=6) divisible by 5
        """
        from all import SL2Z
        x = SL2Z(x, check)
        if not check:
            return x
        
        a = x.a()
        c = x.c()
        d = x.d()
        N = self.level()
        if (a%N == 1) and (c%N == 0):
            return x
            # don't need to check d == 1 mod N as this is automatic from det
        else:
            raise TypeError, "matrix must have diagonal entries (=%s, %s) congruent to 1 modulo %s, and lower left entry (=%s) divisible by %s" %(a, d, N, c, N)
Esempio n. 5
0
    def gamma0_coset_reps(self):
        r"""
        Return a set of coset representatives for self \\ Gamma0(N), where N is
        the level of self.

        EXAMPLE::

            sage: GammaH(108, [1,-1]).gamma0_coset_reps()
            [
            [1 0]  [-43 -45]  [ 31  33]  [-49 -54]  [ 25  28]  [-19 -22]
            [0 1], [108 113], [108 115], [108 119], [108 121], [108 125],
            <BLANKLINE>
            [-17 -20]  [ 47  57]  [ 13  16]  [ 41  52]  [  7   9]  [-37 -49]
            [108 127], [108 131], [108 133], [108 137], [108 139], [108 143],
            <BLANKLINE>
            [-35 -47]  [ 29  40]  [ -5  -7]  [ 23  33]  [-11 -16]  [ 53  79]
            [108 145], [108 149], [108 151], [108 155], [108 157], [108 161]
            ]
        """
        from all import SL2Z
        N = self.level()
        return [
            SL2Z(lift_to_sl2z(0, d.lift(), N))
            for d in _GammaH_coset_helper(N, self._list_of_elements_in_H())
        ]
Esempio n. 6
0
    def __call__(self, x, check=True):
        r"""
        Create an element of this congruence subgroup from x.

        If the optional flag check is True (default), check whether
        x actually gives an element of self.

        EXAMPLES::

            sage: G = Gamma0(12)
            sage: G([1, 0, 24, 1])
            [ 1  0]
            [24  1]
            sage: G(matrix(ZZ, 2, [1, 1, -12, -11]))
            [  1   1]
            [-12 -11]
            sage: G([1, 0, 23, 1])
            Traceback (most recent call last):
            ...
            TypeError: matrix must have lower left entry (=23) divisible by 12
        """
        from all import SL2Z
        x = SL2Z(x, check)
        if not check:
            return x
        
        c = x.c()
        N = self.level()
        if c%N == 0:
            return x
        else:
            raise TypeError, "matrix must have lower left entry (=%s) divisible by %s" %(c, N)
Esempio n. 7
0
    def coset_reps(self):
        r"""
        Return a set of coset representatives for self \\ SL2Z.

        EXAMPLES::

            sage: list(Gamma1(3).coset_reps())
            [[1 0]
            [0 1], [-1 -2]
            [ 3  5], [ 0 -1]
            [ 1  0], [-2  1]
            [ 5 -3], [1 0]
            [1 1], [-3 -2]
            [ 8  5], [ 0 -1]
            [ 1  2], [-2 -3]
            [ 5  7]]
            sage: len(list(Gamma1(31).coset_reps())) == 31**2 - 1
            True
        """
        from all import Gamma0, SL2Z
        reps1 = Gamma0(self.level()).coset_reps()
        for r in reps1:
            reps2 = self.gamma0_coset_reps()
            for t in reps2:
                yield SL2Z(t) * r
Esempio n. 8
0
    def __call__(self, x, check=True):
        r"""
        Create an element of this congruence subgroup from x.

        If the optional flag check is True (default), check whether
        x actually gives an element of self.

        EXAMPLES::

            sage: G = GammaH(10, [3])
            sage: G([1, 0, -10, 1])
            [ 1   0]
            [-10  1]
            sage: G(matrix(ZZ, 2, [7, 1, 20, 3]))
            [ 7  1]
            [20  3]
            sage: GammaH(10, [9])([7, 1, 20, 3])
            Traceback (most recent call last):
            ...
            TypeError: matrix must have lower right entry (=3) congruent modulo 10 to some element of H
        """
        from all import SL2Z
        x = SL2Z(x, check)
        if not check:
            return x
        
        c = x.c()
        d = x.d()
        N = self.level()
        if c%N != 0:
            raise TypeError, "matrix must have lower left entry (=%s) divisible by %s" %(c, N)
        elif d%N in self._list_of_elements_in_H():
            return x
        else:
            raise TypeError, "matrix must have lower right entry (=%s) congruent modulo %s to some element of H" %(d, N)
Esempio n. 9
0
    def is_normal(self):
        r"""
        Return True precisely if this subgroup is a normal subgroup of SL2Z.

        EXAMPLES::

            sage: Gamma(3).is_normal()
            True
            sage: Gamma1(3).is_normal()
            False
        """
        from all import SL2Z
        for x in self.gens():
            for y in SL2Z.gens():
                if y * SL2Z(x) * (~y) not in self:
                    return False
        return True
Esempio n. 10
0
    def test_spanning_trees(self):
        r"""
        Test coset representatives obtained from spanning trees for even
        subgroup (Kulkarni's method with generators ``S2``, ``S3`` and Verrill's
        method with generators ``L``, ``S2``).

        EXAMPLES::

            sage: from sage.modular.arithgroup.tests import Test
            sage: Test().test_spanning_trees() #random
        """
        from sage.all import prod
        from all import SL2Z
        from arithgroup_perm import S2m, S3m, Lm

        G = random_even_arithgroup(self.index)

        m = {'l': Lm, 's': S2m}
        tree, reps, wreps, gens = G._spanning_tree_verrill()
        assert reps[0] == SL2Z([1, 0, 0, 1])
        assert wreps[0] == ''
        for i in xrange(1, self.index):
            assert prod(m[letter] for letter in wreps[i]) == reps[i]
        tree, reps, wreps, gens = G._spanning_tree_verrill(on_right=False)
        assert reps[0] == SL2Z([1, 0, 0, 1])
        assert wreps[0] == ''
        for i in xrange(1, self.index):
            assert prod(m[letter] for letter in wreps[i]) == reps[i]

        m = {'s2': S2m, 's3': S3m}
        tree, reps, wreps, gens = G._spanning_tree_kulkarni()
        assert reps[0] == SL2Z([1, 0, 0, 1])
        assert wreps[0] == []
        for i in xrange(1, self.index):
            assert prod(m[letter] for letter in wreps[i]) == reps[i]
        tree, reps, wreps, gens = G._spanning_tree_kulkarni(on_right=False)
        assert reps[0] == SL2Z([1, 0, 0, 1])
        assert wreps[0] == []
        for i in xrange(1, self.index):
            assert prod(m[letter] for letter in wreps[i]) == reps[i]
Esempio n. 11
0
    def is_normal(self):
        r"""
        Return True precisely if this subgroup is a normal subgroup of SL2Z.

        EXAMPLES::
            
            sage: Gamma(3).is_normal()
            True
            sage: Gamma1(3).is_normal()
            False
        """
        from all import SL2Z
        for x in self.gens():
            for y in SL2Z.gens():
                if y*SL2Z(x)*(~y) not in self:
                    return False
        return True
Esempio n. 12
0
    def are_equivalent(self, x, y, trans=False):
        r"""
        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.

        EXAMPLE::

            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
        """
        x = Cusp(x)
        y = Cusp(y)
        if not trans:
            try:
                xr = self.reduce_cusp(x)
                yr = self.reduce_cusp(y)
                if xr != yr:
                    return False
                if xr == yr:
                    return True
            except NotImplementedError:
                pass

        from all import SL2Z

        vx = lift_to_sl2z(x.numerator(), x.denominator(), 0)
        dx = SL2Z([vx[2], -vx[0], vx[3], -vx[1]])
        vy = lift_to_sl2z(y.numerator(), y.denominator(), 0)
        dy = SL2Z([vy[2], -vy[0], vy[3], -vy[1]])

        for i in xrange(self.index()):
            # Note that the width of any cusp is bounded above by the index of self.
            # If self is congruence, then the level of self is a much better bound, but
            # this method is written to work with non-congruence subgroups as well,
            if dy * SL2Z([1, i, 0, 1]) * (~dx) in self:
                if trans:
                    return dy * SL2Z([1, i, 0, 1]) * ~dx
                else:
                    return True
            elif (self.is_odd() and dy * SL2Z([-1, -i, 0, -1]) * ~dx in self):
                if trans:
                    return dy * SL2Z([-1, -i, 0, -1]) * ~dx
                else:
                    return True
        return False
Esempio n. 13
0
    def nu2(self):
        r"""
        Return the number of orbits of elliptic points of order 2 for this
        arithmetic subgroup.

        EXAMPLES::

            sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().nu2()
            Traceback (most recent call last):
            ...
            NotImplementedError
            sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.nu2(Gamma0(1105)) == 8
            True
        """

        # Subgroups not containing -1 have no elliptic points of order 2.

        if not self.is_even():
            return 0

        # Cheap trick: if self is a subgroup of something with no elliptic points,
        # then self has no elliptic points either.

        from all import Gamma0, is_CongruenceSubgroup
        if is_CongruenceSubgroup(self):
            if self.is_subgroup(Gamma0(self.level())) and Gamma0(
                    self.level()).nu2() == 0:
                return 0

        # Otherwise, the number of elliptic points is the number of g in self \
        # SL2Z such that the stabiliser of g * i in self is not trivial. (Note
        # that the points g*i for g in the coset reps are not distinct, but it
        # still works, since the failure of these points to be distinct happens
        # precisely when the preimages are not elliptic.)

        from all import SL2Z
        count = 0
        for g in self.coset_reps():
            if g * SL2Z([0, 1, -1, 0]) * (~g) in self:
                count += 1
        return count
Esempio n. 14
0
    def nu3(self):
        r"""
        Return the number of orbits of elliptic points of order 3 for this
        arithmetic subgroup.

        EXAMPLES::

            sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().nu3()
            Traceback (most recent call last):
            ...
            NotImplementedError
            sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.nu3(Gamma0(1729)) == 8
            True

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

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

        # Cheap trick: if self is a subgroup of something with no elliptic points,
        # then self has no elliptic points either.

        from all import Gamma0, is_CongruenceSubgroup
        if is_CongruenceSubgroup(self):
            if self.is_subgroup(Gamma0(self.level())) and Gamma0(
                    self.level()).nu3() == 0:
                return 0

        from all import SL2Z
        count = 0
        for g in self.coset_reps():
            if g * SL2Z([0, 1, -1, -1]) * (~g) in self:
                count += 1

        if self.is_even():
            return count
        else:
            return count / 2
Esempio n. 15
0
    def todd_coxeter(self, G=None, on_right=True):
        r"""
        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 diffferent
          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:

        - a 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: 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 xrange(6))
            True
            sage: all(reps[i] * S * ~reps[s[i]] in G for i in xrange(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 xrange(8))
            True
            sage: all(reps[i] * S * ~reps[s[i]] in G for i in xrange(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 xrange(8))
            True
            sage: all(~reps[s[i]] * S * reps[i] in G for i in xrange(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 xrange(6))
            True
            sage: all(~reps[s[i]] * S * reps[i] in G for i in xrange(6))
            True
        """
        from all import SL2Z

        if G is None:
            G = SL2Z
        if G != SL2Z:
            raise NotImplementedError, "Don't know how to compute coset reps for subgroups yet"

        id = SL2Z([1, 0, 0, 1])
        l = SL2Z([1, 1, 0, 1])
        s = SL2Z([0, -1, 1, 0])

        reps = [id]  # coset representatives
        reps_inv = {id: 0}  # coset representatives index

        l_wait_back = [id]  # rep with no incoming s_edge
        s_wait_back = [id]  # rep with no incoming l_edge
        l_wait = [id]  # rep with no outgoing l_edge
        s_wait = [id]  # rep with no outgoing s_edge

        l_edges = [None]  # edges for l
        s_edges = [None]  # edges for s

        gens = []

        while l_wait or s_wait:
            if l_wait:
                x = l_wait.pop(0)
                y = x
                not_end = True
                while not_end:
                    if on_right:
                        y = y * l
                    else:
                        y = l * y
                    for i in xrange(len(l_wait_back)):
                        v = l_wait_back[i]
                        if on_right:
                            yy = y * ~v
                        else:
                            yy = ~v * y
                        if yy in self:
                            l_edges[reps_inv[x]] = reps_inv[v]
                            del l_wait_back[i]
                            if yy != id:
                                gens.append(self(yy))
                            not_end = False
                            break
                    else:
                        reps_inv[y] = len(reps)
                        l_edges[reps_inv[x]] = len(reps)
                        reps.append(y)
                        l_edges.append(None)
                        s_edges.append(None)
                        s_wait_back.append(y)
                        s_wait.append(y)
                    x = y

            if s_wait:
                x = s_wait.pop(0)
                y = x
                not_end = True
                while not_end:
                    if on_right:
                        y = y * s
                    else:
                        y = s * y
                    for i in xrange(len(s_wait_back)):
                        v = s_wait_back[i]
                        if on_right:
                            yy = y * ~v
                        else:
                            yy = ~v * y
                        if yy in self:
                            s_edges[reps_inv[x]] = reps_inv[v]
                            del s_wait_back[i]
                            if yy != id:
                                gens.append(self(yy))
                            not_end = False
                            break
                    else:
                        reps_inv[y] = len(reps)
                        s_edges[reps_inv[x]] = len(reps)
                        reps.append(y)
                        l_edges.append(None)
                        s_edges.append(None)
                        l_wait_back.append(y)
                        l_wait.append(y)
                    x = y

        return reps, gens, l_edges, s_edges