Exemplo 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)

        # 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!")
Exemplo n.º 2
0
    def reduce_cusp(self, c):
        r"""
        Calculate the unique reduced representative of the equivalence of the
        cusp `c` modulo this group. The reduced representative of an
        equivalence class is the unique cusp in the class of the form `u/v`
        with `u, v \ge 0` coprime, `v` minimal, and `u` minimal for that `v`.

        EXAMPLES::

            sage: Gamma(5).reduce_cusp(1/5)
            Infinity
            sage: Gamma(5).reduce_cusp(7/8)
            3/2
            sage: Gamma(6).reduce_cusp(4/3)
            2/3

        TESTS::

            sage: G = Gamma(50); all([c == G.reduce_cusp(c) for c in G.cusps()])
            True
        """
        N = self.level()
        c = Cusp(c)
        u,v = c.numerator() % N, c.denominator() % N
        if (v > N//2) or (2*v == N and u > N//2): 
            u,v = -u,-v
        u,v = _lift_pair(u,v,N)
        return Cusp(u,v)
Exemplo n.º 3
0
    def _find_cusps(self):
        r"""
        Calculate a list of inequivalent cusps.

        EXAMPLES::

            sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5)._find_cusps()
            Traceback (most recent call last):
            ...
            NotImplementedError

        NOTE: There is a generic algorithm implemented at the top level that
        uses the coset representatives of self. This is *very slow* and for all
        the standard congruence subgroups there is a quicker way of doing it,
        so this should usually be overridden in subclasses; but it doesn't have
        to be.
        """
        i = Cusp([1,0])
        L = [i]
        for a in self.coset_reps():
            ai = i.apply([a.a(), a.b(), a.c(), a.d()])
            new = 1
            for v in L:
                if self.are_equivalent(ai, v):
                    new = 0
                    break
            if new == 1:
                L.append(ai)
        return L
    def _find_cusps(self):
        r"""
        Calculate a list of inequivalent cusps.

        EXAMPLES::

            sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5)._find_cusps()
            Traceback (most recent call last):
            ...
            NotImplementedError

        NOTE: There is a generic algorithm implemented at the top level that
        uses the coset representatives of self. This is *very slow* and for all
        the standard congruence subgroups there is a quicker way of doing it,
        so this should usually be overridden in subclasses; but it doesn't have
        to be.
        """
        i = Cusp([1, 0])
        L = [i]
        for a in self.coset_reps():
            ai = i.apply([a.a(), a.b(), a.c(), a.d()])
            new = 1
            for v in L:
                if self.are_equivalent(ai, v):
                    new = 0
                    break
            if new == 1:
                L.append(ai)
        return L
    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)

        # 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!")
Exemplo n.º 6
0
    def reduce_cusp(self, c):
        r"""
        Calculate the unique reduced representative of the equivalence of the
        cusp `c` modulo this group. The reduced representative of an
        equivalence class is the unique cusp in the class of the form `u/v`
        with `u, v \ge 0` coprime, `v` minimal, and `u` minimal for that `v`.

        EXAMPLES::

            sage: Gamma(5).reduce_cusp(1/5)
            Infinity
            sage: Gamma(5).reduce_cusp(7/8)
            3/2
            sage: Gamma(6).reduce_cusp(4/3)
            2/3

        TESTS::

            sage: G = Gamma(50); all([c == G.reduce_cusp(c) for c in G.cusps()])
            True
        """
        N = self.level()
        c = Cusp(c)
        u, v = c.numerator() % N, c.denominator() % N
        if (v > N // 2) or (2 * v == N and u > N // 2):
            u, v = -u, -v
        u, v = _lift_pair(u, v, N)
        return Cusp(u, v)
Exemplo n.º 7
0
    def _find_cusps(self):
        r"""
        Calculate the reduced representatives of the equivalence classes of
        cusps for this group. Adapted from code by Ron Evans.

        EXAMPLE::

            sage: Gamma(8).cusps() # indirect doctest
            [0, 1/4, 1/3, 3/8, 1/2, 2/3, 3/4, 1, 4/3, 3/2, 5/3, 2, 7/3, 5/2, 8/3, 3, 7/2, 11/3, 4, 14/3, 5, 6, 7, Infinity]
        """
        n = self.level()
        C = [QQ(x) for x in xrange(n)]

        n0 = n // 2
        n1 = (n + 1) // 2

        for r in xrange(1, n1):
            if r > 1 and gcd(r, n) == 1:
                C.append(ZZ(r) / ZZ(n))
            if n0 == n / 2 and gcd(r, n0) == 1:
                C.append(ZZ(r) / ZZ(n0))

        for s in xrange(2, n1):
            for r in xrange(1, 1 + n):
                if GCD_list([s, r, n]) == 1:
                    # GCD_list is ~40x faster than gcd, since gcd wastes loads
                    # of time initialising a Sequence type.
                    u, v = _lift_pair(r, s, n)
                    C.append(ZZ(u) / ZZ(v))

        return [Cusp(x) for x in sorted(C)] + [Cusp(1, 0)]
Exemplo n.º 8
0
def is_rational_cusp_gamma0(c, N, data):
    """
    Return True if the rational number c is a rational cusp of level N.

    This uses remarks in Glenn Steven's Ph.D. thesis.

    INPUT:


    -  ``c`` - a cusp

    -  ``N`` - a positive integer

    -  ``data`` - the list [n for n in range(2,N) if
       gcd(n,N) == 1], which is passed in as a parameter purely for
       efficiency reasons.


    EXAMPLES::

        sage: from sage.modular.abvar.cuspidal_subgroup import is_rational_cusp_gamma0
        sage: N = 27
        sage: data = [n for n in range(2,N) if gcd(n,N) == 1]
        sage: is_rational_cusp_gamma0(Cusp(1/3), N, data)
        False
        sage: is_rational_cusp_gamma0(Cusp(1), N, data)
        True
        sage: is_rational_cusp_gamma0(Cusp(oo), N, data)
        True
        sage: is_rational_cusp_gamma0(Cusp(2/9), N, data)
        False
    """
    num = c.numerator()
    den = c.denominator()
    return all(c.is_gamma0_equiv(Cusp(num, d * den), N) for d in data)
Exemplo n.º 9
0
def find_correct_matrix(M, N):
    r"""
    Given a matrix M1 that maps infinity to a/c this returns a matrix M2 Matrix([[A,B],[C,D]]) that maps infinity to a Gamma0(N)-equivalent cusp A/C and satisfies, C|N, C>0, (A,N)=1 and N|B.
    It also returns T^n = Matrix([[1,n],[0,1]]) such that M2*T^n*M1**(-1) is in Gamma0(N).
    """
    a, b, c, d = list(map(lambda x: ZZ(x), M.list()))
    if (c > 0 and c.divides(N) and gcd(a, N) == 1 and b % N == 0):
        return M, identity_matrix(2)
    if c == 0:
        return Matrix([[1, 0], [N, 1]]), identity_matrix(2)
    cusps = cusps_of_gamma0(N)
    for cusp in cusps:
        if Cusp(QQ(a) / QQ(c)).is_gamma0_equiv(cusp, N):
            break
    A, C = cusp.numerator(), cusp.denominator()
    _, D, b = xgcd(A, N * C)
    M2 = Matrix([[A, -N * b], [C, D]])
    n = 0
    T = Matrix([[1, 1], [0, 1]])
    tmp = identity_matrix(2)
    while True:
        if N.divides((M2 * tmp * M**(-1))[1][0]):
            return M2, tmp
        elif N.divides((M2 * tmp**(-1) * M**(-1))[1][0]):
            return M2, tmp**(-1)
        tmp = tmp * T
Exemplo n.º 10
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
Exemplo n.º 11
0
    def _find_cusps(self):
        r"""
        Return an ordered list of inequivalent cusps for self, i.e. a
        set of representatives for the orbits of self on
        `\mathbb{P}^1(\QQ)`.  These are returned in a reduced
        form; see self.reduce_cusp for the definition of reduced.

        ALGORITHM:
            Uses explicit formulae specific to `\Gamma_0(N)`: a reduced cusp on
            `\Gamma_0(N)` is always of the form `a/d` where `d | N`, and `a_1/d
            \sim a_2/d` if and only if `a_1 \cong a_2 \bmod {\rm gcd}(d,
            N/d)`.

        EXAMPLES::

            sage: Gamma0(90)._find_cusps()
            [0, 1/45, 1/30, 1/18, 1/15, 1/10, 1/9, 2/15, 1/6, 1/5, 1/3, 11/30, 1/2, 2/3, 5/6, Infinity]
            sage: Gamma0(1).cusps()
            [Infinity]
            sage: Gamma0(180).cusps() == Gamma0(180).cusps(algorithm='modsym')
            True
        """
        N = self.level()
        s = []

        for d in arith.divisors(N):
            w = arith.gcd(d, N // d)
            if w == 1:
                if d == 1:
                    s.append(Cusp(1, 0))
                elif d == N:
                    s.append(Cusp(0, 1))
                else:
                    s.append(Cusp(1, d))
            else:
                for a in xrange(1, w):
                    if arith.gcd(a, w) == 1:
                        while arith.gcd(a, d // w) != 1:
                            a += w
                        s.append(Cusp(a, d))
        return sorted(s)
Exemplo n.º 12
0
    def reduce_cusp(self, c):
        r"""
        Return the unique reduced cusp equivalent to c under the
        action of self. Always returns Infinity, since there is only
        one equivalence class of cusps for $SL_2(Z)$.

        EXAMPLES::

            sage: SL2Z.reduce_cusp(Cusps(-1/4))
            Infinity
        """
        return Cusp(1, 0)
Exemplo n.º 13
0
    def _find_cusps(self):
        r"""
        Return an ordered list of inequivalent cusps for self, i.e. a
        set of representatives for the orbits of self on
        `\mathbf{P}^1(\QQ)`.  These are returned in a reduced
        form; see self.reduce_cusp for the definition of reduced.

        ALGORITHM:
            Lemma 3.2 in Cremona's 1997 book shows that for the action
            of Gamma1(N) on "signed projective space"
            `\Q^2 / (\Q_{\geq 0}^+)`, we have `u_1/v_1 \sim u_2 / v_2`
            if and only if `v_1 = v_2 \bmod N` and `u_1 = u_2 \bmod
            gcd(v_1, N)`. It follows that every orbit has a
            representative `u/v` with `v \le N` and `0 \le u \le
            gcd(v, N)`.  We iterate through all pairs `(u,v)`
            satisfying this.

            Having found a set containing at least one of every
            equivalence class modulo Gamma1(N), we can be sure of
            picking up every class modulo GammaH(N) since this
            contains Gamma1(N); and the reduce_cusp call does the
            checking to make sure we don't get any duplicates.

        EXAMPLES::

            sage: Gamma1(5)._find_cusps()
            [0, 2/5, 1/2, Infinity]
            sage: Gamma1(35)._find_cusps()
            [0, 2/35, 1/17, 1/16, 1/15, 1/14, 1/13, 1/12, 3/35, 1/11, 1/10, 1/9, 4/35, 1/8, 2/15, 1/7, 1/6, 6/35, 1/5, 3/14, 8/35, 1/4, 9/35, 4/15, 2/7, 3/10, 11/35, 1/3, 12/35, 5/14, 13/35, 2/5, 3/7, 16/35, 17/35, 1/2, 8/15, 4/7, 3/5, 9/14, 7/10, 5/7, 11/14, 4/5, 6/7, 9/10, 13/14, Infinity]
            sage: Gamma1(24)._find_cusps() == Gamma1(24).cusps(algorithm='modsym')
            True
            sage: GammaH(24, [13,17])._find_cusps() == GammaH(24,[13,17]).cusps(algorithm='modsym')
            True
        """

        s = []
        hashes = []
        N = self.level()

        for d in xrange(1, 1 + N):
            w = N.gcd(d)
            M = int(w) if w > 1 else 2
            for a in xrange(1, M):
                if gcd(a, w) != 1:
                    continue
                while gcd(a, d) != 1:
                    a += w
                c = self.reduce_cusp(Cusp(a, d))
                h = hash(c)
                if not h in hashes:
                    hashes.append(h)
                    s.append(c)
        return sorted(s)
Exemplo n.º 14
0
    def cusps(self, algorithm='default'):
        r"""
        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: 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()
            True
            sage: GammaH(36, [19,29]).cusps() == Gamma0(36).cusps()
            True
            sage: Gamma0(1).cusps()
            [Infinity]
        """
        try:
            return copy(self._cusp_list[algorithm])
        except (AttributeError, KeyError):
            self._cusp_list = {}

        from .congroup_sl2z import is_SL2Z
        if algorithm == 'default':
            if is_SL2Z(self):
                s = [Cusp(1, 0)]
            else:
                s = self._find_cusps()
        elif algorithm == 'modsym':
            s = sorted(self.reduce_cusp(c)
                       for c in self.modular_symbols().cusps())
        else:
            raise ValueError("unknown algorithm: %s" % algorithm)

        self._cusp_list[algorithm] = s
        return copy(s)
Exemplo n.º 15
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
Exemplo n.º 16
0
    def _compute_lattice(self, rational_only=False, rational_subgroup=False):
        r"""
        Return a list of vectors that define elements of the rational
        homology that generate this finite subgroup.
        
        INPUT:
        
        
        -  ``rational_only`` - bool (default: False); if
           ``True``, only use rational cusps.
        
        
        OUTPUT:
        
        
        -  ``list`` - list of vectors
        
        
        EXAMPLES::
        
            sage: J = J0(37)
            sage: C = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: C._compute_lattice()
            Free module of degree 4 and rank 4 over Integer Ring
            Echelon basis matrix:
            [  1   0   0   0]
            [  0   1   0   0]
            [  0   0   1   0]
            [  0   0   0 1/3]
            sage: J = J0(43)
            sage: C = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: C._compute_lattice()
            Free module of degree 6 and rank 6 over Integer Ring
            Echelon basis matrix:
            [  1   0   0   0   0   0]
            [  0 1/7   0 6/7   0 5/7]
            [  0   0   1   0   0   0]
            [  0   0   0   1   0   0]
            [  0   0   0   0   1   0]
            [  0   0   0   0   0   1]
            sage: J = J0(22)
            sage: C = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: C._compute_lattice()
            Free module of degree 4 and rank 4 over Integer Ring
            Echelon basis matrix:
            [1/5 1/5 4/5   0]
            [  0   1   0   0]
            [  0   0   1   0]
            [  0   0   0 1/5]
            sage: J = J1(13)
            sage: C = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: C._compute_lattice()
            Free module of degree 4 and rank 4 over Integer Ring
            Echelon basis matrix:
            [ 1/19     0     0  9/19]
            [    0  1/19  1/19 18/19]
            [    0     0     1     0]
            [    0     0     0     1]
        
        We compute with and without the optional
        ``rational_only`` option.
        
        ::
        
            sage: J = J0(27); G = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: G._compute_lattice()
            Free module of degree 2 and rank 2 over Integer Ring
            Echelon basis matrix:
            [1/3   0]
            [  0 1/3]
            sage: G._compute_lattice(rational_only=True)
            Free module of degree 2 and rank 2 over Integer Ring
            Echelon basis matrix:
            [1/3   0]
            [  0   1]
        """
        A = self.abelian_variety()
        Cusp = A.modular_symbols()
        Amb  = Cusp.ambient_module()
        Eis  = Amb.eisenstein_submodule()

        C = Amb.cusps()
        N = Amb.level()
        
        if rational_subgroup:
            # QQ-rational subgroup of cuspidal subgroup
            assert A.is_ambient()
            Q = Cusp.abvarquo_rational_cuspidal_subgroup()
            return Q.V()
        
        if rational_only:
            # subgroup generated by differences of rational cusps
            if not is_Gamma0(A.group()):
                raise NotImplementedError, 'computation of rational cusps only implemented in Gamma0 case.'
            if not N.is_squarefree():
                data = [n for n in range(2,N) if gcd(n,N) == 1]
                C = [c for c in C if is_rational_cusp_gamma0(c, N, data)]

        v = [Amb([infinity, alpha]).element() for alpha in C]
        cusp_matrix = matrix(QQ, len(v), Amb.dimension(), v)

        # TODO -- refactor something out here
        # Now we project onto the cuspidal part.
        B = Cusp.free_module().basis_matrix().stack(Eis.free_module().basis_matrix())
        X = B.solve_left(cusp_matrix)
        X = X.matrix_from_columns(range(Cusp.dimension()))
        lattice = X.row_module(ZZ) + A.lattice()
        return lattice
Exemplo n.º 17
0
    def _reduce_cusp(self, c):
        r"""
        Compute a minimal representative for the given cusp c.

        Returns a pair (c', t), where c' is the minimal representative
        for the given cusp, and t is either 1 or -1, as explained
        below. Largely for internal use.

        The minimal representative for a cusp is the element in `P^1(Q)`
        in lowest terms with minimal positive denominator, and minimal
        positive numerator for that denominator.

        Two cusps `u1/v1` and `u2/v2` are equivalent modulo `\Gamma_H(N)`
        if and only if

        - `v1 =  h*v2 (mod N)` and `u1 =  h^(-1)*u2 (mod gcd(v1,N))`

        or

        - `v1 = -h*v2 (mod N)` and `u1 = -h^(-1)*u2 (mod gcd(v1,N))`

        for some `h \in H`. Then t is 1 or -1 as c and c' fall into
        the first or second case, respectively.

        EXAMPLES::

            sage: GammaH(6,[5])._reduce_cusp(Cusp(5,3))
            (1/3, -1)
            sage: GammaH(12,[5])._reduce_cusp(Cusp(8,9))
            (1/3, -1)
            sage: GammaH(12,[5])._reduce_cusp(Cusp(5,12))
            (Infinity, 1)
            sage: GammaH(12,[])._reduce_cusp(Cusp(5,12))
            (5/12, 1)
            sage: GammaH(21,[5])._reduce_cusp(Cusp(-9/14))
            (1/7, 1)
        """
        c = Cusp(c)
        N = int(self.level())
        Cusps = c.parent()
        v = int(c.denominator() % N)
        H = self._list_of_elements_in_H()

        # First, if N | v, take care of this case. If u is in \pm H,
        # then we return Infinity. If not, let u_0 be the minimum
        # of \{ h*u | h \in \pm H \}. Then return u_0/N.
        if not v:
            u = c.numerator() % N
            if u in H:
                return Cusps((1,0)), 1
            if (N-u) in H:
                return Cusps((1,0)), -1
            ls = [ (u*h)%N for h in H ]
            m1 = min(ls)
            m2 = N-max(ls)
            if m1 < m2:
                return Cusps((m1,N)), 1
            else:
                return Cusps((m2,N)), -1

        u = int(c.numerator() % v)
        gcd = get_gcd(N)
        d = gcd(v,N)

        # If (N,v) == 1, let v_0 be the minimal element
        # in \{ v * h | h \in \pm H \}. Then we either return
        # Infinity or 1/v_0, as v is or is not in \pm H,
        # respectively.
        if d == 1:
            if v in H:
                return Cusps((0,1)), 1
            if (N-v) in H:
                return Cusps((0,1)), -1
            ls = [ (v*h)%N for h in H ]
            m1 = min(ls)
            m2 = N-max(ls)
            if m1 < m2:
                return Cusps((1,m1)), 1
            else:
                return Cusps((1,m2)), -1

        val_min = v
        inv_mod = get_inverse_mod(N)

        # Now we're in the case (N,v) > 1. So we have to do several
        # steps: first, compute v_0 as above. While computing this
        # minimum, keep track of *all* pairs of (h,s) which give this
        # value of v_0.
        hs_ls = [(1,1)]
        for h in H:
            tmp = (v*h)%N

            if tmp < val_min:
                val_min = tmp
                hs_ls = [(inv_mod(h,N), 1)]
            elif tmp == val_min:
                hs_ls.append((inv_mod(h,N), 1))

            if (N-tmp) < val_min:
                val_min = N - tmp
                hs_ls = [(inv_mod(h,N), -1)]
            elif (N-tmp) == val_min:
                hs_ls.append((inv_mod(h,N), -1))

        # Finally, we find our minimal numerator. Let u_1 be the
        # minimum of s*h^-1*u mod d as (h,s) ranges over the elements
        # of hs_ls. We must find the smallest integer u_0 which is
        # smaller than v_0, congruent to u_1 mod d, and coprime to
        # v_0. Then u_0/v_0 is our minimal representative.
        u_min = val_min
        sign = None
        for h_inv,s in hs_ls:
            tmp = (h_inv * s * u)%d
            while gcd(tmp, val_min) > 1 and tmp < u_min:
                tmp += d
            if tmp < u_min:
                u_min = tmp
                sign = s

        return Cusps((u_min, val_min)), sign
Exemplo n.º 18
0
    def _reduce_cusp(self, c):
        r"""
        Compute a minimal representative for the given cusp c.
        Returns a pair (c', t), where c' is the minimal representative
        for the given cusp, and t is either 1 or -1, as explained
        below. Largely for internal use.

        The minimal representative for a cusp is the element in `P^1(Q)`
        in lowest terms with minimal positive denominator, and minimal
        positive numerator for that denominator.

        Two cusps `u1/v1` and `u2/v2` are equivalent modulo `\Gamma_H(N)`
        if and only if
            `v1 =  h*v2 (mod N)` and `u1 =  h^(-1)*u2 (mod gcd(v1,N))`
        or
            `v1 = -h*v2 (mod N)` and `u1 = -h^(-1)*u2 (mod gcd(v1,N))`
        for some `h \in H`. Then t is 1 or -1 as c and c' fall into
        the first or second case, respectively.

        EXAMPLES::

            sage: GammaH(6,[5])._reduce_cusp(Cusp(5,3))
            (1/3, -1)
            sage: GammaH(12,[5])._reduce_cusp(Cusp(8,9))
            (1/3, -1)
            sage: GammaH(12,[5])._reduce_cusp(Cusp(5,12))
            (Infinity, 1)
            sage: GammaH(12,[])._reduce_cusp(Cusp(5,12))
            (5/12, 1)
            sage: GammaH(21,[5])._reduce_cusp(Cusp(-9/14))
            (1/7, 1)
        """
        c = Cusp(c)
        N = int(self.level())
        Cusps = c.parent()
        v = int(c.denominator() % N)
        H = self._list_of_elements_in_H()

        # First, if N | v, take care of this case. If u is in \pm H,
        # then we return Infinity. If not, let u_0 be the minimum
        # of \{ h*u | h \in \pm H \}. Then return u_0/N.
        if not v:
            u = c.numerator() % N
            if u in H:
                return Cusps((1, 0)), 1
            if (N - u) in H:
                return Cusps((1, 0)), -1
            ls = [(u * h) % N for h in H]
            m1 = min(ls)
            m2 = N - max(ls)
            if m1 < m2:
                return Cusps((m1, N)), 1
            else:
                return Cusps((m2, N)), -1

        u = int(c.numerator() % v)
        gcd = get_gcd(N)
        d = gcd(v, N)

        # If (N,v) == 1, let v_0 be the minimal element
        # in \{ v * h | h \in \pm H \}. Then we either return
        # Infinity or 1/v_0, as v is or is not in \pm H,
        # respectively.
        if d == 1:
            if v in H:
                return Cusps((0, 1)), 1
            if (N - v) in H:
                return Cusps((0, 1)), -1
            ls = [(v * h) % N for h in H]
            m1 = min(ls)
            m2 = N - max(ls)
            if m1 < m2:
                return Cusps((1, m1)), 1
            else:
                return Cusps((1, m2)), -1

        val_min = v
        inv_mod = get_inverse_mod(N)

        # Now we're in the case (N,v) > 1. So we have to do several
        # steps: first, compute v_0 as above. While computing this
        # minimum, keep track of *all* pairs of (h,s) which give this
        # value of v_0.
        hs_ls = [(1, 1)]
        for h in H:
            tmp = (v * h) % N

            if tmp < val_min:
                val_min = tmp
                hs_ls = [(inv_mod(h, N), 1)]
            elif tmp == val_min:
                hs_ls.append((inv_mod(h, N), 1))

            if (N - tmp) < val_min:
                val_min = N - tmp
                hs_ls = [(inv_mod(h, N), -1)]
            elif (N - tmp) == val_min:
                hs_ls.append((inv_mod(h, N), -1))

        # Finally, we find our minimal numerator. Let u_1 be the
        # minimum of s*h^-1*u mod d as (h,s) ranges over the elements
        # of hs_ls. We must find the smallest integer u_0 which is
        # smaller than v_0, congruent to u_1 mod d, and coprime to
        # v_0. Then u_0/v_0 is our minimal representative.
        u_min = val_min
        sign = None
        for h_inv, s in hs_ls:
            tmp = (h_inv * s * u) % d
            while gcd(tmp, val_min) > 1 and tmp < u_min:
                tmp += d
            if tmp < u_min:
                u_min = tmp
                sign = s

        return Cusps((u_min, val_min)), sign
Exemplo n.º 19
0
    def _compute_lattice(self, rational_only=False, rational_subgroup=False):
        r"""
        Return a list of vectors that define elements of the rational
        homology that generate this finite subgroup.

        INPUT:


        -  ``rational_only`` - bool (default: False); if
           ``True``, only use rational cusps.


        OUTPUT:


        -  ``list`` - list of vectors


        EXAMPLES::

            sage: J = J0(37)
            sage: C = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: C._compute_lattice()
            Free module of degree 4 and rank 4 over Integer Ring
            Echelon basis matrix:
            [  1   0   0   0]
            [  0   1   0   0]
            [  0   0   1   0]
            [  0   0   0 1/3]
            sage: J = J0(43)
            sage: C = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: C._compute_lattice()
            Free module of degree 6 and rank 6 over Integer Ring
            Echelon basis matrix:
            [  1   0   0   0   0   0]
            [  0 1/7   0 6/7   0 5/7]
            [  0   0   1   0   0   0]
            [  0   0   0   1   0   0]
            [  0   0   0   0   1   0]
            [  0   0   0   0   0   1]
            sage: J = J0(22)
            sage: C = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: C._compute_lattice()
            Free module of degree 4 and rank 4 over Integer Ring
            Echelon basis matrix:
            [1/5 1/5 4/5   0]
            [  0   1   0   0]
            [  0   0   1   0]
            [  0   0   0 1/5]
            sage: J = J1(13)
            sage: C = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: C._compute_lattice()
            Free module of degree 4 and rank 4 over Integer Ring
            Echelon basis matrix:
            [1/19    0 9/19 9/19]
            [   0 1/19    0 9/19]
            [   0    0    1    0]
            [   0    0    0    1]

        We compute with and without the optional
        ``rational_only`` option.

        ::

            sage: J = J0(27); G = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: G._compute_lattice()
            Free module of degree 2 and rank 2 over Integer Ring
            Echelon basis matrix:
            [1/3   0]
            [  0 1/3]
            sage: G._compute_lattice(rational_only=True)
            Free module of degree 2 and rank 2 over Integer Ring
            Echelon basis matrix:
            [1/3   0]
            [  0   1]
        """
        A = self.abelian_variety()
        Cusp = A.modular_symbols()
        Amb = Cusp.ambient_module()
        Eis = Amb.eisenstein_submodule()

        C = Amb.cusps()
        N = Amb.level()

        if rational_subgroup:
            # QQ-rational subgroup of cuspidal subgroup
            assert A.is_ambient()
            Q = Cusp.abvarquo_rational_cuspidal_subgroup()
            return Q.V()

        if rational_only:
            # subgroup generated by differences of rational cusps
            if not is_Gamma0(A.group()):
                raise NotImplementedError(
                    'computation of rational cusps only implemented in Gamma0 case.'
                )
            if not N.is_squarefree():
                data = [n for n in N.coprime_integers(N) if n >= 2]
                C = [c for c in C if is_rational_cusp_gamma0(c, N, data)]

        v = [Amb([infinity, alpha]).element() for alpha in C]
        cusp_matrix = matrix(QQ, len(v), Amb.dimension(), v)

        # TODO -- refactor something out here
        # Now we project onto the cuspidal part.
        B = Cusp.free_module().basis_matrix().stack(
            Eis.free_module().basis_matrix())
        X = B.solve_left(cusp_matrix)
        X = X.matrix_from_columns(range(Cusp.dimension()))
        lattice = X.row_module(ZZ) + A.lattice()
        return lattice