Esempio n. 1
0
    def perpendicular_bisector(self): #UHP
        r"""
        Return the perpendicular bisector of the hyperbolic geodesic ``self``
        if that geodesic has finite length.

        EXAMPLES::

            sage: UHP = HyperbolicPlane().UHP()
            sage: g = UHP.random_geodesic()
            sage: h = g.perpendicular_bisector()
            sage: c = lambda x: x.coordinates()
            sage: bool(c(g.intersection(h)[0]) - c(g.midpoint()) < 10**-9)
            True

        Infinite geodesics cannot be bisected::

            sage: UHP.get_geodesic(0, 1).perpendicular_bisector()
            Traceback (most recent call last):
            ...
            ValueError: the length must be finite
        """
        if self.length() == infinity:
            raise ValueError("the length must be finite")
        start = self._start.coordinates()
        d = self._model._dist_points(start, self._end.coordinates()) / 2
        S = self.complete()._to_std_geod(start)
        T1 = matrix([[exp(d/2), 0], [0, exp(-d/2)]])
        s2 = sqrt(2) * 0.5
        T2 = matrix([[s2, -s2], [s2, s2]])
        isom_mtrx = S.inverse() * (T1 * T2) * S # We need to clean this matrix up.
        if (isom_mtrx - isom_mtrx.conjugate()).norm() < 5*EPSILON: # Imaginary part is small.
            isom_mtrx = (isom_mtrx + isom_mtrx.conjugate()) / 2 # Set it to its real part.
        H = self._model.get_isometry(isom_mtrx)
        return self._model.get_geodesic(H(self._start), H(self._end))
Esempio n. 2
0
def walsh_matrix(m0):
    """
    This is the generator matrix of a Walsh code. The matrix of
    codewords correspond to a Hadamard matrix.

    EXAMPLES::

        sage: walsh_matrix(2)
        [0 0 1 1]
        [0 1 0 1]
        sage: walsh_matrix(3)
        [0 0 0 0 1 1 1 1]
        [0 0 1 1 0 0 1 1]
        [0 1 0 1 0 1 0 1]
        sage: C = LinearCode(walsh_matrix(4)); C
        [16, 4] linear code over GF(2)
        sage: C.spectrum()
        [1, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0]

    This last code has minimum distance 8.

    REFERENCES:

    - http://en.wikipedia.org/wiki/Hadamard_matrix
    """
    m = int(m0)
    if m == 1:
        return matrix(GF(2), 1, 2, [ 0, 1])
    if m > 1:
        row2 = [x.list() for x in walsh_matrix(m-1).augment(walsh_matrix(m-1)).rows()]
        return matrix(GF(2), m, 2**m, [[0]*2**(m-1) + [1]*2**(m-1)] + row2)
    raise ValueError("%s must be an integer > 0."%m0)
def matrix_multiplicative_order(m):
    r"""
    Return the order of the 2x2 matrix ``m``.
    """
    if m.is_one():
        return Integer(1)
    elif m.det() != 1 and m.det() != -1:
        return Infinity

    # now we compute the potentially preserved quadratic form
    # i.e. looking for A such that m^t A m = A
    m00 = m[0,0]
    m01 = m[0,1]
    m10 = m[1,0]
    m11 = m[1,1]
    M = matrix(m.base_ring(),
        [[m00**2, m00*m10, m10**2],
         [m00*m01, m00*m11, m10*m11],
         [m01**2, m01*m11, m11**2]])

    # might there be several solutions ? (other than scaling)... should not
    try:
        v = (M-identity_matrix(3)).solve_right()
    except ValueError: # no solution
        return False

    raise NotImplementedError("your matrix is conjugate to an orthogonal matrix but the angle might not be rational.. to be terminated.")

    # then we conjugate and check if the angles are rational
    # we need to take a square root of a symmetric matrix... this is not implemented!
    A = matrix(m.base_ring(), [[v[0],v[1]],[v[1],v[2]]])
Esempio n. 4
0
    def parity_check_matrix(self):
        r"""
        Returns a parity check matrix of ``self``.

        This matrix is computed directly from :func:`original_code`.

        EXAMPLES::

            sage: set_random_seed(42)
            sage: C = codes.RandomLinearCode(9, 5, GF(7))
            sage: Ce = codes.ExtendedCode(C)
            sage: Ce.parity_check_matrix()
            [1 1 1 1 1 1 1 1 1 1]
            [1 0 0 0 2 1 6 6 4 0]
            [0 1 0 0 6 1 6 1 0 0]
            [0 0 1 0 3 2 6 2 1 0]
            [0 0 0 1 4 5 4 3 5 0]
        """
        F = self.base_ring()
        zero = F.zero()
        one = F.one()
        H = self.original_code().parity_check_matrix()
        nr, nc = H.nrows(), H.ncols()
        Hlist = H.list()
        v = matrix(F, nr + 1, 1, [one] + [zero] * nr)
        return matrix(F, nr + 1, nc, [one] * nc + Hlist).augment(v)
Esempio n. 5
0
    def orthonormal_1(dim_n=5):
        """
        A matrix of rational approximations to orthonormal vectors to
        ``(1,...,1)``.

        INPUT:

        - ``dim_n`` - the dimension of the vectors

        OUTPUT:

        A matrix over ``QQ`` whose rows are close to an orthonormal
        basis to the subspace normal to ``(1,...,1)``.

        EXAMPLES::

            sage: from sage.geometry.polyhedron.library import Polytopes
            sage: m = Polytopes.orthonormal_1(5)
            sage: m
            [ 70711/100000   -7071/10000             0             0             0]
            [    1633/4000     1633/4000 -81649/100000             0             0]
            [   7217/25000    7217/25000    7217/25000  -43301/50000             0]
            [ 22361/100000  22361/100000  22361/100000  22361/100000  -44721/50000]
        """
        pb = []
        for i in range(0,dim_n-1):
            pb.append([1.0/(i+1)]*(i+1) + [-1] + [0]*(dim_n-i-2))
        m = matrix(RDF,pb)
        new_m = []
        for i in range(0,dim_n-1):
            new_m.append([RDF(100000*q/norm(m[i])).ceil()/100000 for q in m[i]])
        return matrix(QQ,new_m)
Esempio n. 6
0
    def image_mod_n(self):
        r"""
        Return the image of this group in `SL(2, \ZZ / N\ZZ)`.

        EXAMPLE::

            sage: Gamma0(3).image_mod_n()
            Matrix group over Ring of integers modulo 3 with 2 generators (
            [2 0]  [1 1]
            [0 2], [0 1]
            )

        TEST::

            sage: for n in [2..20]:
            ...     for g in Gamma0(n).gamma_h_subgroups():
            ...       G = g.image_mod_n()
            ...       assert G.order() == Gamma(n).index() / g.index()
        """
        N = self.level()
        if N == 1:
            raise NotImplementedError("Matrix groups over ring of integers modulo 1 not implemented")
        gens = [matrix(Zmod(N), 2, 2, [x, 0, 0, Zmod(N)(1)/x]) for x in self._generators_for_H()]
        gens += [matrix(Zmod(N),2,[1,1,0,1])]
        return MatrixGroup(gens)
Esempio n. 7
0
    def reflection_involution(self):
        r"""
        Return the isometry of the involution fixing the geodesic ``self``.

        EXAMPLES::

            sage: UHP = HyperbolicPlane().UHP()
            sage: g1 = UHP.get_geodesic(0, 1)
            sage: g1.reflection_involution()
            Isometry in UHP
            [ 1  0]
            [ 2 -1]
            sage: UHP.get_geodesic(I, 2*I).reflection_involution()
            Isometry in UHP
            [ 1  0]
            [ 0 -1]
        """
        x, y = [real(k.coordinates()) for k in self.ideal_endpoints()]
        if x == infinity:
            M = matrix([[1, -2*y], [0, -1]])
        elif y == infinity:
            M = matrix([[1, -2*x], [0, -1]])
        else:
            M = matrix([[(x+y)/(y-x), -2*x*y/(y-x)], [2/(y-x), -(x+y)/(y-x)]])
        return self._model.get_isometry(M)
Esempio n. 8
0
    def parity_check_matrix(self):
        r"""
        Returns a parity check matrix of ``self``.

        This matrix is computed directly from :func:`original_code`.

        EXAMPLES::

            sage: C = LinearCode(matrix(GF(2),[[1,0,0,1,1],\
                                               [0,1,0,1,0],\
                                               [0,0,1,1,1]]))
            sage: C.parity_check_matrix()
            [1 0 1 0 1]
            [0 1 0 1 1]
            sage: Ce = codes.ExtendedCode(C)
            sage: Ce.parity_check_matrix()
            [1 1 1 1 1 1]
            [1 0 1 0 1 0]
            [0 1 0 1 1 0]
        """
        F = self.base_ring()
        zero = F.zero()
        one = F.one()
        H = self.original_code().parity_check_matrix()
        nr, nc = H.nrows(), H.ncols()
        Hlist = H.list()
        v = matrix(F, nr + 1, 1, [one] + [zero] * nr)
        M = matrix(F, nr + 1, nc, [one] * nc + Hlist).augment(v)
        M.set_immutable()
        return M
Esempio n. 9
0
    def kernel_vector(self, way='LLL', verbose=False):
        r"""
        todo: clean this

        EXAMPLES::

            sage: from slabbe import ChristoffelGraph
            sage: C = ChristoffelGraph((2,5,7))
            sage: C.kernel_vector()
            [(-1, -1, 1), (3, -4, 0)]

        """
        from sage.arith.misc import gcd
        if way == 'vect_gcd':
            a,b,c = self._v
            gcd_ac = gcd(a,c)
            gcd_bc = gcd(b,c)
            U = ua,ub,uc = vector((c,0,-a)) / gcd(a,c)
            V = va,vb,vc = vector((0,c,-b)) / gcd(b,c)
            rows = U,V
        elif way == 'echelon':
            a,b,c = self._v
            m = matrix(ZZ, 4, [1,1,1,c,0,-a,0,c,-b,b,-a,0])
            me = m.echelon_form()
            if verbose:
                print(me)
            rows = me[1],me[2]
        elif way == 'LLL':
            dim = self.dimension()
            if dim == 3:
                a,b,c = self._v
                M = matrix(ZZ, 4, [1,1,1,c,0,-a,0,c,-b,b,-a,0])
            elif dim == 4:
                a,b,c,d = self._v
                M = matrix(ZZ, 7, (1,1,1,1,b,-a,0,0,c,0,-a,0,0,c,-b,0,d,0,0,-a,0,d,0,-b,0,0,d,-c))
            else:
                raise ValueError("dimension (=%s) must be 3 or 4" % dim)
            rows = M.LLL().rows()
            VS = rows[0].parent()
            zero = VS(0)
            un = VS((1,)*dim)
            assert zero in rows, "(0,0,0) not in LLL result"
            assert un in rows, "(1,1,1) not in LLL result"
            while zero in rows: rows.remove(zero)
            while un in rows: rows.remove(un)
        elif way == 'vect':
            a,b,c = self._v
            U = ua,ub,uc = vector((c,0,-a))
            V = va,vb,vc = vector((0,c,-b))
            rows = U,V
        else:
            raise ValueError("unknown way")
        R = matrix(rows)
        if sum(map(abs, R.minors(dim-1))) != sum(map(abs,self._v)):
            print(R)
            print(R.minors(dim-1))
            print(sum(map(abs, R.minors(dim))))
            print(sum(map(abs,self._v)))
            raise Exception("The result (=%s) is false " % rows)
        return rows
Esempio n. 10
0
    def __repr__(self):
        r"""
        Return string representation.

        OUTPUT:

        String.

        EXAMPLES::

            sage: from sage.geometry.polyhedron.double_description import \
            ....:     DoubleDescriptionPair, StandardAlgorithm
            sage: A = matrix(QQ, [(1,0,1), (0,1,1), (-1,-1,1)])
            sage: DD = StandardAlgorithm(A).run()
            sage: DD.__repr__()
            'Double description pair (A, R) defined by\n    [ 1  0  1]
             [ 2/3 -1/3 -1/3]\nA = [ 0  1  1],   R = [-1/3  2/3 -1/3]\n
             [-1 -1  1]        [ 1/3  1/3  1/3]'
        """
        from sage.typeset.ascii_art import ascii_art
        from sage.matrix.constructor import matrix
        s = ascii_art('Double description pair (A, R) defined by')
        A = ascii_art(matrix(self.A))
        A._baseline = (len(self.A) / 2)
        A = ascii_art('A = ') + A
        R = ascii_art(matrix(self.R).transpose())
        if len(self.R) > 0:
            R._baseline = (len(self.R[0]) / 2)
        else:
            R._baseline = 0
        R = ascii_art('R = ') + R
        return str(s * (A + ascii_art(',   ') + R))
Esempio n. 11
0
    def bigraphical(self, G, A=None, K=QQ, names=None):
        r"""
        Return a bigraphical hyperplane arrangement.

        INPUT:

        - ``G`` -- graph

        - ``A`` -- list, matrix, dictionary (default: ``None``
          gives semiorder), or the string 'generic'

        - ``K`` -- field (default: `\QQ`)

        - ``names`` -- tuple of strings or ``None`` (default); the
          variable names for the ambient space

        OUTPUT:

        The hyperplane arrangement with hyperplanes `x_i - x_j =
        A[i,j]` and `x_j - x_i = A[j,i]` for each edge `v_i, v_j` of
        ``G``.  The indices `i,j` are the indices of elements of
        ``G.vertices()``.

        EXAMPLES::

            sage: G = graphs.CycleGraph(4)
            sage: G.edges()
            [(0, 1, None), (0, 3, None), (1, 2, None), (2, 3, None)]
            sage: G.edges(labels=False)
            [(0, 1), (0, 3), (1, 2), (2, 3)]
            sage: A = {0:{1:1, 3:2}, 1:{0:3, 2:0}, 2:{1:2, 3:1}, 3:{2:0, 0:2}}
            sage: HA = hyperplane_arrangements.bigraphical(G, A)
            sage: HA.n_regions()
            63
            sage: hyperplane_arrangements.bigraphical(G, 'generic').n_regions()
            65
            sage: hyperplane_arrangements.bigraphical(G).n_regions()
            59

        REFERENCES:

        ..  [BigraphicalArrangements] S. Hopkins, D. Perkinson.
            "Bigraphical Arrangements".
            :arxiv:`1212.4398`
        """
        n = G.num_verts()
        if A is None:  # default to G-semiorder arrangement
            A = matrix(K, n, lambda i, j: 1)
        elif A == 'generic':
            A = random_matrix(ZZ, n, x=10000)
            A = matrix(K, A)
        H = make_parent(K, n, names)
        x = H.gens()
        hyperplanes = []
        for e in G.edges():
            i = G.vertices().index(e[0])
            j = G.vertices().index(e[1])
            hyperplanes.append( x[i] - x[j] - A[i][j])
            hyperplanes.append(-x[i] + x[j] - A[j][i])
        return H(*hyperplanes)
Esempio n. 12
0
    def __init__(self, R, elements):
        """
        Initialize ``self``.

        EXAMPLES::

            sage: R.<x,y,z> = QQ[]
            sage: K = KoszulComplex(R, [x,y])
            sage: TestSuite(K).run()
        """
        # Generate the differentials
        self._elements = elements
        n = len(elements)
        I = range(n)
        diff = {}
        zero = R.zero()
        for i in I:
            M = matrix(R, binomial(n,i), binomial(n,i+1), zero)
            j = 0
            for comb in itertools.combinations(I, i+1):
                for k,val in enumerate(comb):
                    r = rank(comb[:k] + comb[k+1:], n, False)
                    M[r,j] = (-1)**k * elements[val]
                j += 1
            M.set_immutable()
            diff[i+1] = M
        diff[0] = matrix(R, 0, 1, zero)
        diff[0].set_immutable()
        diff[n+1] = matrix(R, 1, 0, zero)
        diff[n+1].set_immutable()
        ChainComplex_class.__init__(self, ZZ, ZZ(-1), R, diff)
Esempio n. 13
0
def find_kadziela_matrices(M,T):
    '''
    The matrix M describes the relation between periods (A,B,D)^t
    and the periods (A0,B0)^t, where (A,B,D) are the periods of
    the Teitelbaum periods, and (A0,B0) are the Darmon ones.
           (A,B,D)^t = M * (A0,B0)^t
    The matrix T describes the action of Hecke on homology.
    That is, the first column of T describes the image of T
    on the first basis vector.

    The output are matrices X and Y such that
         X * matrix(2,2,[A,B,B,D]) = matrix(2,2,[A0,B0,C0,D0]) * Y

    '''
    a, b, c, d, e, f = M.list()
    x, y, z, t = T.list()
    #     1,  2,  3,  4,  5,  6,  7,  8
    r1 = [a,  c,  0,  0, -1,  0,  0,  0]
    r2 = [b,  d,  0,  0,  0,  0, -1,  0]
    r3 = [c,  e,  0,  0,  0, -1,  0,  0]
    r4 = [d,  f,  0,  0,  0,  0,  0, -1]
    r5 = [0,  0,  a,  c,  0,  0, -1,  0]
    r6 = [0,  0,y*b,y*d, -z,  0,x-t,  0]
    r7 = [0,  0,  c,  e,  0,  0,  0, -1]
    r8 = [0,  0,y*d,y*f,  0, -z,  0,x-t]
    AA = matrix(ZZ,8,8,[r1,r2,r3,r4,r5,r6,r7,r8])
    if AA.rank() == 8:
        raise ValueError('Not isogenous')
    r = AA.right_kernel().matrix().rows()[0].list()
    X = matrix(ZZ,2,2,r[:4])
    Y = matrix(ZZ,2,2,r[4:])
    return X, Y
Esempio n. 14
0
    def symmetric_matrix(self):
        r"""
        The symmetric matrix `M` such that `(x y z) M (x y z)^t`
        is the defining equation of ``self``.

        EXAMPLES ::

            sage: R.<x, y, z> = QQ[]
            sage: C = Conic(x^2 + x*y/2 + y^2 + z^2)
            sage: C.symmetric_matrix()
            [  1 1/4   0]
            [1/4   1   0]
            [  0   0   1]

            sage: C = Conic(x^2 + 2*x*y + y^2 + 3*x*z + z^2)
            sage: v = vector([x, y, z])
            sage: v * C.symmetric_matrix() * v
            x^2 + 2*x*y + y^2 + 3*x*z + z^2
        """
        [a,b,c,d,e,f] = self.coefficients()
        if self.base_ring().characteristic() == 2:
            if b == 0 and c == 0 and e == 0:
                return matrix([[a,0,0],[0,d,0],[0,0,f]])
            raise ValueError, "The conic self (= %s) has no symmetric matrix " \
                              "because the base field has characteristic 2" % \
                              self
        from sage.matrix.constructor import matrix
        return matrix([[  a , b/2, c/2 ],
                       [ b/2,  d , e/2 ],
                       [ c/2, e/2,  f  ]])
    def __call__(self, n, modulus=0):

        """
        Give the nth term of a binary recurrence sequence, possibly mod some modulus.

        INPUT:

        - ``n`` -- an integer (the index of the term in the binary recurrence sequence)

        - ``modulus`` -- a natural number (optional --  default value is 0)

        OUTPUT:

        - An integer (the nth term of the binary recurrence sequence modulo ``modulus``)

        EXAMPLES::

            sage: R = BinaryRecurrenceSequence(3,3,2,1)
            sage: R(2)
            9
            sage: R(101)
            16158686318788579168659644539538474790082623100896663971001
            sage: R(101,12)
            9
            sage: R(101)%12
            9

        """
        R = Integers(modulus)
        F = matrix(R, [[0,1],[self.c,self.b]])            # F*[u_{n}, u_{n+1}]^T = [u_{n+1}, u_{n+2}]^T (T indicates transpose).
        v = matrix(R, [[self.u0],[self.u1]])
        return list(F**n*v)[0][0]
Esempio n. 16
0
    def read_matrix(self, filename):
        r"""
        Read a matrix in 4ti2 format from the file ``filename`` in
        directory ``directory()``.

        INPUT:

        - ``filename`` - The name of the file to read from.

        OUTPUT:
        The data from the file as a matrix over `\ZZ`.

        EXAMPLES::

            sage: from sage.interfaces.four_ti_2 import four_ti_2
            sage: four_ti_2.write_matrix([[1,2,3],[3,4,6]], "test_file")
            sage: four_ti_2.read_matrix("test_file")
            [1 2 3]
            [3 4 6]
        """
        from sage.matrix.constructor import matrix
        try:
            f = open(os.path.join(self.directory(), filename))
            lines = f.readlines()
            f.close()
        except IOError:
            return matrix(ZZ, 0, 0)

        nrows, ncols = map(ZZ, lines.pop(0).strip().split())
        return matrix(ZZ, nrows, ncols,
                      [map(ZZ, line.strip().split()) for line in lines
                       if line.strip() != ""])
Esempio n. 17
0
    def __init__(self, n):
        r"""
        Hecke triangle group (2, n, infinity).
        Namely the von Dyck group corresponding to the triangle group
        with angles (pi/2, pi/n, 0).

        INPUT:

        - ``n``   - ``infinity`` or an integer greater or equal to ``3``.

        OUTPUT:

        The Hecke triangle group for the given parameter ``n``.

        EXAMPLES::

            sage: G = HeckeTriangleGroup(12)
            sage: G
            Hecke triangle group for n = 12
            sage: G.category()
            Category of groups
        """

        self._n = n
        self._T = matrix(AA, [[1,self.lam()],[0,1]])
        self._S = matrix(AA, [[0,-1],[1,0]])

        FinitelyGeneratedMatrixGroup_generic.__init__(self, ZZ(2), AA, [self._S, self._T])
Esempio n. 18
0
 def Reverse(self):
     A1 = matrix(3, [1,1,1, 0,1,0, 0,0,1])
     A2 = matrix(3, [1,0,0, 1,1,1, 0,0,1])
     A3 = matrix(3, [1,0,0, 0,1,0, 1,1,1])
     R = matrix(3, [0,1,1, 1,0,1, 1,1,0])
     gens = {1:A1, 2:A2, 3:A3, 4:R}
     return MatrixCocycle(gens)
Esempio n. 19
0
    def Cassaigne_accelerated(self, order=3):
        r"""
        EXAMPLES::

            sage: from slabbe.matrix_cocycle import cocycles
            sage: c = cocycles.Cassaigne_accelerated(order=3)
            sage: c
            Cocycle with 6 gens over Language of finite words over
            alphabet ['11', '121', '12^{2}1', '212', '21^{2}2', '22']

        """
        C1 = matrix(3, [1,1,0, 0,0,1, 0,1,0])
        C2 = matrix(3, [0,1,0, 1,0,0, 0,1,1])
        C = {1:C1, 2:C2}
        gens = {}
        for i in range(order):
            for (a,b) in [(1,2), (2,1)]:
                if i == 0:
                    code = '{}{}'.format(a,a)
                elif i == 1:
                    code = '{}{}{}'.format(a,b,a)
                else:
                    code = '{}{}^{{{}}}{}'.format(a,b,i,a)
                gens[code] = C[a]*C[b]**i*C[a]
        return MatrixCocycle(gens)
Esempio n. 20
0
    def random_isometry(self, preserve_orientation=True, **kwargs):
        r"""
        Return a random isometry in the Upper Half Plane model.

        INPUT:

        - ``preserve_orientation`` -- if ``True`` return an
          orientation-preserving isometry

        OUTPUT:

        - a hyperbolic isometry

        EXAMPLES::

            sage: A = HyperbolicPlane().UHP().random_isometry()
            sage: B = HyperbolicPlane().UHP().random_isometry(preserve_orientation=False)
            sage: B.preserves_orientation()
            False
        """
        [a,b,c,d] = [RR.random_element() for k in range(4)]
        while abs(a*d - b*c) < EPSILON:
            [a,b,c,d] = [RR.random_element() for k in range(4)]
        M = matrix(RDF, 2,[a,b,c,d])
        M = M / (M.det()).abs().sqrt()
        if M.det() > 0:
            if not preserve_orientation:
                M = M * matrix(2,[0,1,1,0])
        elif preserve_orientation:
            M = M * matrix(2,[0,1,1,0])
        return self._Isometry(self, M, check=False)
Esempio n. 21
0
 def FullySubtractive(self):
     F1 = matrix(3, [1,0,0, 1,1,0, 1,0,1])
     F2 = matrix(3, [1,1,0, 0,1,0, 0,1,1])
     F3 = matrix(3, [1,0,1, 0,1,1, 0,0,1])
     gens = (F1, F2, F3)
     alphabet = [1, 2, 3]
     gens = dict(zip(alphabet, gens))
     return MatrixCocycle(gens)
Esempio n. 22
0
 def hom( self, im_gens, codomain=None, check=True):
     # return self.module().hom( im_gens, codomain = codomain, check = check)
     if not codomain:
         raise NotImplementedError()
     A = matrix( im_gens)
     if codomain and True == check:
         assert self.gram_matrix() == A*codomain.gram_matrix()*A.transpose()
     return Embedding( self, matrix( im_gens), codomain) 
Esempio n. 23
0
 def ArnouxRauzy(self):
     A1 = matrix(3, [1,1,1, 0,1,0, 0,0,1])
     A2 = matrix(3, [1,0,0, 1,1,1, 0,0,1])
     A3 = matrix(3, [1,0,0, 0,1,0, 1,1,1])
     gens = (A1, A2, A3)
     alphabet = [1, 2, 3]
     gens = dict(zip(alphabet, gens))
     return MatrixCocycle(gens)
Esempio n. 24
0
 def Sorted_Brun(self):
     B1 = matrix(3, [1,0,0, 0,1,0, 0,1,1])
     B2 = matrix(3, [1,0,0, 0,0,1, 0,1,1])
     B3 = matrix(3, [0,1,0, 0,0,1, 1,0,1])
     gens = (B1, B2, B3)
     alphabet = [1, 2, 3]
     gens = dict(zip(alphabet, gens))
     cone = matrix(3, [1,1,1,0,1,1,0,0,1])
     return MatrixCocycle(gens, cone)
        def cartan_matrix(self, q = None, idempotents = None):
            """
            EXAMPLES::

                sage: import sage_semigroups.monoids.catalog as semigroups
                sage: M = semigroups.NDPFMonoidPoset(Posets(3)[3])
                sage: M.cartan_matrix(var('q'))
                [1 0 0 0]
                [0 1 q 0]
                [0 0 1 0]
                [0 0 0 1]

            The algorithm used for computing the q-Cartan matrix is
            experimental. It has been tested for the 0-Hecke monoid in
            types A1-A4, B2-3,G2, and for NDPFMonoidB 1-5.

                sage: M =  PiMonoid(["A",3])               # long time
                sage: m1 = M.cartan_matrix(var('q'))       # long time
                sage: m2 = M.cartan_matrix_mupad(var('q')) # long time
                sage: isomorphic_cartan_matrices(m1,m2)    # long time
                True

                sage: M =  PiMonoid(["A",4])               # long time
                sage: m1 = M.cartan_matrix(var('q'))       # long time
                sage: m2 = M.cartan_matrix_mupad(var('q')) # long time
                sage: isomorphic_cartan_matrices(m1,m2)    # long time
                True

            The current version *does* fail for the 0-Hecke monoid of
            type D_4!!!

                sage: ZZ.<q> = ZZ[]
                sage: list(sum(sum(PiMonoid(["D",4]).cartan_matrix(q)))) # long time
                [16, 38, 62, 35, 20, 15, 6]

            Compare with:

                sage: list(sum(sum(PiMonoid(["D",4]).cartan_matrix_mupad(q)))) # long time
                [16, 38, 62, 38, 20, 12, 6]

            Which could mean that the factorization constraints are too weak.

            """
            #left_symbols,  right_modules = cartan_data_of_j_trivial_monoid(self, side="left" )
            #right_symbols, left_modules  = cartan_data_of_j_trivial_monoid(self, side="right")
            if idempotents is None:
                idempotents = self.idempotents()
            #idempotents = [self.retract(self.ambient.e(w)) for w in self.ambient.W]
            rank = rank_from_list(idempotents)
            from sage.matrix.constructor import matrix
            if q is None:
                cartan_matrix = matrix(len(idempotents), len(idempotents), sparse=True)
            else:
                cartan_matrix = matrix(q.parent(), len(idempotents), len(idempotents), sparse=True)
            for (e,f),coeff in self.cartan_matrix_as_table(q).iteritems():
                cartan_matrix[rank(e), rank(f)] = coeff
            return cartan_matrix
Esempio n. 26
0
def qlogs_from_Lp_and_ords(a,b,Tmatrix,q1ord, q2ord, q3ord):
    K = a.parent()
    ordA = q2ord + q3ord
    ordB = -q3ord
    ordD = q1ord + q3ord
    bord = matrix(K,2,2,[ordA,ordB,ordB,ordD]) * Tmatrix
    M = Matrix(K,3,2,[ordA, bord[0,0], ordB, bord[0,1],ordD,bord[1,1]])
    logA, logB, logD = (M * matrix(K,2,1,[a,b])).list()
    return logB + logD, logA + logB, -logB
Esempio n. 27
0
def Pigs():
    r"""
    Return a Pigs game.

    Consider two pigs.
    One dominant pig and one subservient pig.
    These pigs share a pen.
    There is a lever in the pen that delivers 6 units of food but if either pig
    pushes the lever it will take them a little while to get to the food as well
    as cost them 1 unit of food.
    If the dominant pig pushes the lever, the subservient pig has some time
    to eat two thirds of the food before being pushed out of the way.
    If the subservient pig pushes the lever,
    the dominant pig will eat all the food.
    Finally if both pigs go to push the lever the subservient pig will be able
    to eat a third of the food (and they will also both lose 1 unit of food).

    This can be modeled as a normal form game using the following two matrices
    [McMillan]_ (we assume that the dominant pig's utilities are given by
    `A`):

    .. MATH::

        A = \begin{pmatrix}
            3&1\\
            6&0\\
            \end{pmatrix}


        B = \begin{pmatrix}
            1&4\\
            -1&0\\
            \end{pmatrix}

    There is a single Nash equilibrium at which the dominant pig pushes the
    lever and the subservient pig does not.

    This can be implemented in Sage using the following::

        sage: g = game_theory.normal_form_games.Pigs()
        sage: g
        Pigs - Normal Form Game with the following utilities: ...
        sage: d ={(0, 1): [1, 4], (1, 0): [6, -1],
        ....:     (0, 0): [3, 1], (1, 1): [0, 0]}
        sage: g == d
        True
        sage: g.obtain_nash()
        [[(1, 0), (0, 1)]]

    """
    from sage.matrix.constructor import matrix
    A = matrix([[3, 1], [6, 0]])
    B = matrix([[1, 4], [-1, 0]])
    g = NormalFormGame([A, B])
    g.rename('Pigs - ' + repr(g))
    return g
    def _rank(self, K) :
        
        if K is QQ or K in NumberFields() :
            return len(_jacobi_forms_by_taylor_expansion_coords(self.__index, self.__weight, 0))

            ## This is the formula used by Poor and Yuen in Paramodular cusp forms
            if self.__weight == 2 :
                delta = len(self.__index.divisors()) // 2 - 1
            else :
                delta = 0
                
            return sum( ModularForms(1, self.__weight + 2 * j).dimension() + j**2 // (4 * self.__index)
                        for j in xrange(self.__index + 1) ) \
                   + delta
            

            ## This is the formula given by Skoruppa in 
            ## Jacobi forms of critical weight and Weil representations
            ##FIXME: There is some mistake here
            if self.__weight % 2 != 0 :
                ## Otherwise the space X(i**(n - 2 k)) is different
                ## See: Skoruppa, Jacobi forms of critical weight and Weil representations
                raise NotImplementedError
            
            m = self.__index
            K = CyclotomicField(24 * m, 'zeta')
            zeta = K.gen(0)
            
            quadform = lambda x : 6 * x**2
            bilinform = lambda x,y : quadform(x + y) - quadform(x) - quadform(y)
            
            T = diagonal_matrix([zeta**quadform(i) for i in xrange(2*m)])
            S =   sum(zeta**(-quadform(x)) for x in xrange(2 * m)) / (2 * m) \
                * matrix([[zeta**(-bilinform(j,i)) for j in xrange(2*m)] for i in xrange(2*m)])
            subspace_matrix_1 = matrix( [ [1 if j == i or j == 2*m - i else 0 for j in xrange(m + 1) ]
                                        for i in xrange(2*m)] )
            subspace_matrix_2 = zero_matrix(ZZ, m + 1, 2*m)
            subspace_matrix_2.set_block(0,0,identity_matrix(m+1))
            
            T = subspace_matrix_2 * T * subspace_matrix_1
            S = subspace_matrix_2 * S * subspace_matrix_1
            
            sqrt3 = (zeta**(4*m) - zeta**(-4*m)) * zeta**(-6*m) 
            rank =   (self.__weight - 1/2 - 1) / 2 * (m + 1) \
                   + 1/8 * (   zeta**(3*m * (2*self.__weight - 1)) * S.trace()
                             + zeta**(3*m * (1 - 2*self.__weight)) * S.trace().conjugate() ) \
                   + 2/(3*sqrt3) * (   zeta**(4 * m * self.__weight) * (S*T).trace()
                                     + zeta**(-4 * m * self.__weight) * (S*T).trace().conjugate() ) \
                   - sum((j**2 % (m+1))/(m+1) -1/2 for j in range(0,m+1))
            
            if self.__weight > 5 / 2 :
                return rank
            else :
                raise NotImplementedError
            
        raise NotImplementedError
Esempio n. 29
0
def relation_space(v):
    r"""
    Relation space of the given vector ``v``

    This is the sub vector space of `\QQ^d` given as the kernel of the map
    `n \mapsto n \cdot \lambda`. The dimension is `d - rank`.

    EXAMPLES::

        sage: from surface_dynamics.misc.linalg import relation_space
        sage: K.<sqrt2> = QuadraticField(2)
        sage: v3 = vector([sqrt2, 1, 1+sqrt2])
        sage: relation_space(v3)
        Vector space of degree 3 and dimension 1 over Rational Field
        Basis matrix:
        [ 1  1 -1]
        sage: v4 = vector([sqrt2, 1, 1+sqrt2, 1-sqrt2])
        sage: relation_space(v4)
        Vector space of degree 4 and dimension 2 over Rational Field
        Basis matrix:
        [   1    0 -1/2  1/2]
        [   0    1 -1/2 -1/2]

        sage: v = vector([1,2,5,3])
        sage: relation_space(v)
        Vector space of degree 4 and dimension 3 over Rational Field
        Basis matrix:
        [   1    0    0 -1/3]
        [   0    1    0 -2/3]
        [   0    0    1 -5/3]

    The relation space has some covariance relation with respect to matrix
    actions::

        sage: m3 = matrix(3, [1,-1,0,2,-3,4,5,-2,2])
        sage: relation_space(v3 * m3) == relation_space(v3) * ~m3.transpose()
        True
        sage: relation_space(m3 * v3) == relation_space(v3) * ~m3
        True

        sage: m4 = matrix(4, [1,-1,0,1,2,-3,0,4,5,3,-2,2,1,1,1,1])
        sage: relation_space(v4 * m4) == relation_space(v4) * ~m4.transpose()
        True
        sage: relation_space(m4 * v4) == relation_space(v4) * ~m4
        True
    """
    from sage.matrix.constructor import matrix
    try:
        m_lengths = matrix([u.vector() for u in v])
    except AttributeError:
        from sage.rings.rational_field import QQ
        v = [QQ.coerce(i) for i in v]
        m_lengths = matrix([[i] for i in v])
    return m_lengths.left_kernel()
Esempio n. 30
0
def deformation_space(lengths):
    r"""
    Deformation space of the given ``lengths``

    This is the smallest vector space defined over `\QQ` that contains
    the vector ``lengths``. Its dimension is `rank`.

    EXAMPLES::

        sage: from surface_dynamics.misc.linalg import deformation_space
        sage: K.<sqrt2> = QuadraticField(2)
        sage: v3 = vector([sqrt2, 1, 1+sqrt2])
        sage: deformation_space(v3)
        Vector space of degree 3 and dimension 2 over Rational Field
        Basis matrix:
        [1 0 1]
        [0 1 1]
        sage: v4 = vector([sqrt2, 1, 1+sqrt2, 1-sqrt2])
        sage: deformation_space(v4)
        Vector space of degree 4 and dimension 2 over Rational Field
        Basis matrix:
        [ 1  0  1 -1]
        [ 0  1  1  1]

        sage: v = vector([1, 5, 2, 9])
        sage: deformation_space(v)
        Vector space of degree 4 and dimension 1 over Rational Field
        Basis matrix:
        [1 5 2 9]

    The deformation space has some covariance relation with respect to matrix
    actions::

        sage: m3 = matrix(3, [1,-1,0,2,-3,4,5,-2,2])
        sage: deformation_space(v3 * m3) == deformation_space(v3) * m3
        True
        sage: deformation_space(m3 * v3) == deformation_space(v3) * m3.transpose()
        True

        sage: m4 = matrix(4, [1,-1,0,1,2,-3,0,4,5,3,-2,2,1,1,1,1])
        sage: deformation_space(v4 * m4) == deformation_space(v4) * m4
        True
        sage: deformation_space(m4 * v4) == deformation_space(v4) * m4.transpose()
        True
    """
    from sage.matrix.constructor import matrix
    try:
        m_lengths = matrix([u.vector() for u in lengths])
    except AttributeError:
        from sage.rings.rational_field import QQ
        lengths = [QQ.coerce(i) for i in lengths]
        m_lengths = matrix([[i] for i in lengths])

    return m_lengths.column_space()
Esempio n. 31
0
    def p_minimal_polynomials(self, p, s_max=None):
        r"""
        Compute `(p^s)`-minimal polynomials `\nu_s` of `B`.

        Compute a finite subset `\mathcal{S}` of the positive
        integers and `(p^s)`-minimal polynomials
        `\nu_s` for `s \in \mathcal{S}`.

        For `0 < t \le \max \mathcal{S}`, a `(p^t)`-minimal polynomial is
        given by `\nu_s` where
        `s = \min\{ r \in \mathcal{S} \mid r\ge t \}`.
        For `t > \max \mathcal{S}`, the minimal polynomial of `B` is
        also a `(p^t)`-minimal polynomial.

        INPUT:

        - ``p`` -- a prime in `D`

        - ``s_max`` -- a positive integer (default: ``None``); if set, only
          `(p^s)`-minimal polynomials for ``s <= s_max`` are computed
          (see below for details)

        OUTPUT:

        A dictionary. Keys are the finite set `\mathcal{S}`, the values
        are the associated `(p^s)`-minimal polynomials `\nu_s`,
        `s \in \mathcal{S}`.

        Setting ``s_max`` only affects the output if ``s_max`` is at
        most `\max\mathcal{S}` where `\mathcal{S}` denotes the full
        set. In that case, only those `\nu_s` with ``s <= s_max`` are
        returned where ``s_max`` is always included even if it is not
        included in the full set `\mathcal{S}`.

        EXAMPLES::

            sage: from sage.matrix.compute_J_ideal import ComputeMinimalPolynomials
            sage: B = matrix(ZZ, [[1, 0, 1], [1, -2, -1], [10, 0, 0]])
            sage: C = ComputeMinimalPolynomials(B)
            sage: C.p_minimal_polynomials(2)
            {2: x^2 + 3*x + 2}
            sage: set_verbose(1)
            sage: C = ComputeMinimalPolynomials(B)
            sage: C.p_minimal_polynomials(2)
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            ------------------------------------------
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            p = 2, t = 1:
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            Result of lifting:
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            F =
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            [x^2 + x]
            [      x]
            [      0]
            [      1]
            [      1]
            [  x + 1]
            [      1]
            [      0]
            [      0]
            [  x + 1]
            verbose 1 (...: compute_J_ideal.py, current_nu)
            ------------------------------------------
            verbose 1 (...: compute_J_ideal.py, current_nu)
            (x^2 + x)
            verbose 1 (...: compute_J_ideal.py, current_nu)
            Generators with (p^t)-generating property:
            verbose 1 (...: compute_J_ideal.py, current_nu)
            [x^2 + x]
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            nu = x^2 + x
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            corresponding columns for G
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            [x^2 + x]
            [  x + 2]
            [      0]
            [      1]
            [      1]
            [  x - 1]
            [     -1]
            [     10]
            [      0]
            [  x + 1]
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            ------------------------------------------
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            p = 2, t = 2:
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            Result of lifting:
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            F =
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            [  2*x^2 + 2*x x^2 + 3*x + 2]
            [          2*x         x + 4]
            [            0             0]
            [            2             1]
            [            2             1]
            [      2*x + 2         x + 1]
            [            2            -1]
            [            0            10]
            [            0             0]
            [      2*x + 2         x + 3]
            verbose 1 (...: compute_J_ideal.py, current_nu)
            ------------------------------------------
            verbose 1 (...: compute_J_ideal.py, current_nu)
            (2*x^2 + 2*x, x^2 + 3*x + 2)
            verbose 1 (...: compute_J_ideal.py, current_nu)
            Generators with (p^t)-generating property:
            verbose 1 (...: compute_J_ideal.py, current_nu)
            [x^2 + 3*x + 2]
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            nu = x^2 + 3*x + 2
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            corresponding columns for G
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            [x^2 + 3*x + 2]
            [        x + 4]
            [            0]
            [            1]
            [            1]
            [        x + 1]
            [           -1]
            [           10]
            [            0]
            [        x + 3]
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            ------------------------------------------
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            p = 2, t = 3:
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            Result of lifting:
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            F =
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            [x^3 + 7*x^2 + 6*x x^3 + 3*x^2 + 2*x]
            [        x^2 + 8*x         x^2 + 4*x]
            [                0                 0]
            [                x             x + 4]
            [            x + 4                 x]
            [    x^2 + 5*x + 4           x^2 + x]
            [           -x + 4                -x]
            [             10*x              10*x]
            [                0                 0]
            [        x^2 + 7*x     x^2 + 3*x + 4]
            verbose 1 (...: compute_J_ideal.py, current_nu)
            ------------------------------------------
            verbose 1 (...: compute_J_ideal.py, current_nu)
            (x^3 + 7*x^2 + 6*x, x^3 + 3*x^2 + 2*x)
            verbose 1 (...: compute_J_ideal.py, current_nu)
            Generators with (p^t)-generating property:
            verbose 1 (...: compute_J_ideal.py, current_nu)
            ...
            verbose 1 (...: compute_J_ideal.py, current_nu)
            [x^3 + 3*x^2 + 2*x]
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            nu = x^3 + 3*x^2 + 2*x
            {2: x^2 + 3*x + 2}
            sage: set_verbose(0)
            sage: C.p_minimal_polynomials(2, s_max=1)
            {1: x^2 + x}
            sage: C.p_minimal_polynomials(2, s_max=2)
            {2: x^2 + 3*x + 2}
            sage: C.p_minimal_polynomials(2, s_max=3)
            {2: x^2 + 3*x + 2}

        ALGORITHM:

        [HR2016]_, Algorithm 5.
        """
        from sage.misc.misc import verbose
        from sage.rings.infinity import Infinity

        deg_mu = self.mu_B.degree()
        if s_max is None:
            s_max = Infinity

        if p in self._cache:
            (t, G, p_min_polys) = self._cache[p]
            if t < Infinity:
                nu = G[0][0]
        else:
            t = 0
            p_min_polys = {}
            nu = self._DX(1)
            d = self._A.ncols()
            G = matrix(self._DX, d, 0)


        while t < s_max:
            deg_prev_nu = nu.degree()
            t += 1
            verbose("------------------------------------------")
            verbose("p = %s, t = %s:" % (p, t))

            verbose("Result of lifting:")
            verbose("F =")
            F = lifting(p, t, self._A, G)
            verbose(F)

            nu = self.current_nu(p, t, F[0], nu)

            verbose("nu = %s" % nu)
            if nu.degree() >= deg_mu:
                t = Infinity
                break

            if nu.degree() == deg_prev_nu:
                G = G.delete_columns([G.ncols() - 1])
                del p_min_polys[t-1]

            column = self.mccoy_column(p, t, nu)
            verbose("corresponding columns for G")
            verbose(column)

            G = matrix.block([[p * G, column]])
            p_min_polys[t] = nu

        self._cache[p] = (t, G, p_min_polys)

        if s_max < t:
            result = {r: polynomial
                      for r, polynomial in p_min_polys.items() if r < s_max}
            next_t_candidates = list(r for r in p_min_polys if r >= s_max)
            if next_t_candidates:
                next_t = min(next_t_candidates)
                result.update({s_max: p_min_polys[next_t] % p**s_max})

            return result

        return p_min_polys
Esempio n. 32
0
    def generator_matrix(self):
        r"""
        Return a generator matrix of ``self``

        Generator matrices of all Golay codes are known, and are thus returned
        by this method without performing any computation

        EXAMPLES::

            sage: C = codes.GolayCode(GF(2), extended=True)
            sage: C.generator_matrix()
            [1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 1 1 0 0 0 1 1]
            [0 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 1 0 0 1 0]
            [0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 1 0 1 0 1 1]
            [0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 0 0 1 1 1 0 1 1 0]
            [0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 1 1 0 1 1 0 0 1]
            [0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 1 1 0 1 1 0 1]
            [0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 1 1 0 1 1 1]
            [0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 1 0 1 1 1 1 0 0 0]
            [0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 1 0 1 1 1 1 0 0]
            [0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 1 0 1 1 1 1 0]
            [0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 1 1 0 0 0 1 1 0 1]
            [0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 1 1 0 0 0 1 1 1]
        """
        n = self.length()
        if n == 23:
            G = matrix(GF(2), [[
                1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                0, 0
            ],
                               [
                                   0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0,
                                   0, 0, 0, 0, 0, 0, 0, 0
                               ],
                               [
                                   0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0,
                                   0, 0, 0, 0, 0, 0, 0, 0
                               ],
                               [
                                   0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1,
                                   0, 0, 0, 0, 0, 0, 0, 0
                               ],
                               [
                                   0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1,
                                   1, 0, 0, 0, 0, 0, 0, 0
                               ],
                               [
                                   0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0,
                                   1, 1, 0, 0, 0, 0, 0, 0
                               ],
                               [
                                   0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0,
                                   0, 1, 1, 0, 0, 0, 0, 0
                               ],
                               [
                                   0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0,
                                   0, 0, 1, 1, 0, 0, 0, 0
                               ],
                               [
                                   0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1,
                                   0, 0, 0, 1, 1, 0, 0, 0
                               ],
                               [
                                   0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1,
                                   1, 0, 0, 0, 1, 1, 0, 0
                               ],
                               [
                                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1,
                                   1, 1, 0, 0, 0, 1, 1, 0
                               ],
                               [
                                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,
                                   1, 1, 1, 0, 0, 0, 1, 1
                               ]])
        elif n == 24:
            G = matrix(GF(2), [[
                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0,
                0, 1, 1
            ],
                               [
                                   0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
                                   1, 1, 0, 0, 1, 0, 0, 1, 0
                               ],
                               [
                                   0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
                                   1, 0, 0, 1, 0, 1, 0, 1, 1
                               ],
                               [
                                   0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
                                   0, 0, 1, 1, 1, 0, 1, 1, 0
                               ],
                               [
                                   0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
                                   0, 1, 1, 0, 1, 1, 0, 0, 1
                               ],
                               [
                                   0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
                                   0, 0, 1, 1, 0, 1, 1, 0, 1
                               ],
                               [
                                   0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1,
                                   1, 0, 0, 1, 1, 0, 1, 1, 1
                               ],
                               [
                                   0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1,
                                   1, 0, 1, 1, 1, 1, 0, 0, 0
                               ],
                               [
                                   0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0,
                                   1, 1, 0, 1, 1, 1, 1, 0, 0
                               ],
                               [
                                   0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
                                   0, 1, 1, 0, 1, 1, 1, 1, 0
                               ],
                               [
                                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1,
                                   1, 1, 0, 0, 0, 1, 1, 0, 1
                               ],
                               [
                                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,
                                   1, 1, 1, 0, 0, 0, 1, 1, 1
                               ]])
        elif n == 11:
            G = matrix(GF(3), [[2, 0, 1, 2, 1, 1, 0, 0, 0, 0, 0],
                               [0, 2, 0, 1, 2, 1, 1, 0, 0, 0, 0],
                               [0, 0, 2, 0, 1, 2, 1, 1, 0, 0, 0],
                               [0, 0, 0, 2, 0, 1, 2, 1, 1, 0, 0],
                               [0, 0, 0, 0, 2, 0, 1, 2, 1, 1, 0],
                               [0, 0, 0, 0, 0, 2, 0, 1, 2, 1, 1]])
        else:
            G = matrix(GF(3), [[1, 0, 0, 0, 0, 0, 2, 0, 1, 2, 1, 2],
                               [0, 1, 0, 0, 0, 0, 1, 2, 2, 2, 1, 0],
                               [0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1],
                               [0, 0, 0, 1, 0, 0, 1, 1, 0, 2, 2, 2],
                               [0, 0, 0, 0, 1, 0, 2, 1, 2, 2, 0, 1],
                               [0, 0, 0, 0, 0, 1, 0, 2, 1, 2, 2, 1]])
        return G
Esempio n. 33
0
def insert_row(M, k, row):
    return matrix(M.rows()[:k] + [row] + M.rows()[k:])
    def _find_isomorphism_degenerate(self, polytope):
        """
        Helper to pick an isomorphism of degenerate polygons

        INPUT:

        - ``polytope`` -- a :class:`LatticePolytope_PPL_class`. The
          polytope to compare with.

        EXAMPLES::

            sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL, C_Polyhedron
            sage: L1 = LatticePolytope_PPL(C_Polyhedron(2, 'empty'))
            sage: L2 = LatticePolytope_PPL(C_Polyhedron(3, 'empty'))
            sage: iso = L1.find_isomorphism(L2)   # indirect doctest
            sage: iso(L1) == L2
            True
            sage: iso = L1._find_isomorphism_degenerate(L2)
            sage: iso(L1) == L2
            True

            sage: L1 = LatticePolytope_PPL((-1,4))
            sage: L2 = LatticePolytope_PPL((2,1,5))
            sage: iso = L1.find_isomorphism(L2)
            sage: iso(L1) == L2
            True

            sage: L1 = LatticePolytope_PPL((-1,), (3,))
            sage: L2 = LatticePolytope_PPL((2,1,5), (2,-3,5))
            sage: iso = L1.find_isomorphism(L2)
            sage: iso(L1) == L2
            True

            sage: L1 = LatticePolytope_PPL((-1,-1), (3,-1))
            sage: L2 = LatticePolytope_PPL((2,1,5), (2,-3,5))
            sage: iso = L1.find_isomorphism(L2)
            sage: iso(L1) == L2
            True

            sage: L1 = LatticePolytope_PPL((-1,2), (3,1))
            sage: L2 = LatticePolytope_PPL((1,2,3),(1,2,4))
            sage: iso = L1.find_isomorphism(L2)
            sage: iso(L1) == L2
            True

            sage: L1 = LatticePolytope_PPL((-1,2), (3,2))
            sage: L2 = LatticePolytope_PPL((1,2,3),(1,2,4))
            sage: L1.find_isomorphism(L2)
            Traceback (most recent call last):
            ...
            LatticePolytopesNotIsomorphicError: different number of integral points

            sage: L1 = LatticePolytope_PPL((-1,2), (3,1))
            sage: L2 = LatticePolytope_PPL((1,2,3),(1,2,5))
            sage: L1.find_isomorphism(L2)
            Traceback (most recent call last):
            ...
            LatticePolytopesNotIsomorphicError: different number of integral points
        """
        from sage.geometry.polyhedron.lattice_euclidean_group_element import \
            LatticePolytopesNotIsomorphicError
        polytope_vertices = polytope.vertices()
        self_vertices = self.ordered_vertices()
        # handle degenerate cases
        if self.n_vertices() == 0:
            A = zero_matrix(ZZ, polytope.space_dimension(),
                            self.space_dimension())
            b = zero_vector(ZZ, polytope.space_dimension())
            return LatticeEuclideanGroupElement(A, b)
        if self.n_vertices() == 1:
            A = zero_matrix(ZZ, polytope.space_dimension(),
                            self.space_dimension())
            b = polytope_vertices[0]
            return LatticeEuclideanGroupElement(A, b)
        if self.n_vertices() == 2:
            self_origin = self_vertices[0]
            self_ray = self_vertices[1] - self_origin
            polytope_origin = polytope_vertices[0]
            polytope_ray = polytope_vertices[1] - polytope_origin
            Ds, Us, Vs = self_ray.column().smith_form()
            Dp, Up, Vp = polytope_ray.column().smith_form()
            assert Vs.nrows() == Vs.ncols() == Vp.nrows() == Vp.ncols() == 1
            assert abs(Vs[0, 0]) == abs(Vp[0, 0]) == 1
            A = zero_matrix(ZZ, Dp.nrows(), Ds.nrows())
            A[0, 0] = 1
            A = Up.inverse() * A * Us * (Vs[0, 0] * Vp[0, 0])
            b = polytope_origin - A * self_origin
            try:
                A = matrix(ZZ, A)
                b = vector(ZZ, b)
            except TypeError:
                raise LatticePolytopesNotIsomorphicError('different lattice')
            hom = LatticeEuclideanGroupElement(A, b)
            if hom(self) == polytope:
                return hom
            raise LatticePolytopesNotIsomorphicError('different polygons')
Esempio n. 35
0
def dodecahedron(center=(0, 0, 0), size=1, **kwds):
    r"""
    A dodecahedron.

    INPUT:


    -  ``center`` - (default: (0,0,0))

    -  ``size`` - (default: 1)

    -  ``color`` - a string that describes a color; this
       can also be a list of 3-tuples or strings length 6 or 3, in which
       case the faces (and oppositive faces) are colored.

    -  ``opacity`` - (default: 1) if less than 1 then is
       transparent


    EXAMPLES: A plain Dodecahedron::

        sage: dodecahedron()

    A translucent dodecahedron that contains a black sphere::

        sage: dodecahedron(color='orange', opacity=0.8) + \
              sphere(size=0.5, color='black')

    CONSTRUCTION: This is how we construct a dodecahedron. We let one
    point be `Q = (0,1,0)`.

    Now there are three points spaced equally on a circle around the
    north pole. The other requirement is that the angle between them be
    the angle of a pentagon, namely `3\pi/5`. This is enough to
    determine them. Placing one on the `xz`-plane we have.

    `P_1 = \left(t, 0, \sqrt{1-t^2}\right)`

    `P_2 = \left(-\frac{1}{2}t, \frac{\sqrt{3}}{2}t, \sqrt{1-t^2}\right)`

    `P_3 = \left(-\frac{1}{2}t, \frac{\sqrt{3}}{2}t, \sqrt{1-t^2}\right)`

    Solving
    `\frac{(P_1-Q) \cdot (P_2-Q)}{|P_1-Q||P_2-Q|} = \cos(3\pi/5)`
    we get `t = 2/3`.

    Now we have 6 points `R_1, ..., R_6` to close the three
    top pentagons. These can be found by mirroring `P_2` and
    `P_3` by the `yz`-plane and rotating around the
    `y`-axis by the angle `\theta` from `Q` to
    `P_1`. Note that `\cos(\theta) = t = 2/3` and so
    `\sin(\theta) = \sqrt{5}/3`. Rotation gives us the other
    four.

    Now we reflect through the origin for the bottom half.

    AUTHORS:

    - Robert Bradshaw, William Stein
    """
    RR = RDF
    one = RR(1)
    sqrt3 = RR(3).sqrt()
    sqrt5 = RR(5).sqrt()
    R3 = RR**3
    rot = matrix(
        RR, [[-one / 2, -sqrt3 / 2, 0], [sqrt3 / 2, -one / 2, 0], [0, 0, 1]])
    rot2 = rot * rot

    # The top
    Q = R3([0, 0, 1])
    # The first ring
    P1 = R3([2 * one / 3, 0, sqrt5 / 3])
    # The second ring
    R1 = R3([sqrt5 / 3, 1 / sqrt3, one / 3])
    R2 = R3([sqrt5 / 3, -1 / sqrt3, one / 3])

    top = [
        Q, P1, rot * P1, rot2 * P1, R1, rot * R2, rot * R1, rot2 * R2,
        rot2 * R1, R2
    ]
    point_list = top + [-p for p in reversed(top)]

    top_faces = [[0, 1, 4, 5, 2], [0, 2, 6, 7, 3], [0, 3, 8, 9, 1],
                 [1, 9, 13, 12, 4], [2, 5, 11, 10, 6], [3, 7, 15, 14, 8]]
    face_list = top_faces + [[19 - p for p in reversed(f)] for f in top_faces]

    if 'aspect_ratio' not in kwds:
        kwds['aspect_ratio'] = [1, 1, 1]
    return index_face_set(face_list,
                          point_list,
                          enclosed=True,
                          center=center,
                          size=size,
                          **kwds)
Esempio n. 36
0
    def basis(self, reduce=True):
        r"""
        Produce a basis for the free abelian group of eta-products of level
        N (under multiplication), attempting to find basis vectors of the
        smallest possible degree.

        INPUT:


        -  ``reduce`` - a boolean (default True) indicating
           whether or not to apply LLL-reduction to the calculated basis


        EXAMPLES::

            sage: EtaGroup(5).basis()
            [Eta product of level 5 : (eta_1)^6 (eta_5)^-6]
            sage: EtaGroup(12).basis()
            [Eta product of level 12 : (eta_1)^2 (eta_2)^1 (eta_3)^2 (eta_4)^-1 (eta_6)^-7 (eta_12)^3,
            Eta product of level 12 : (eta_1)^-4 (eta_2)^2 (eta_3)^4 (eta_6)^-2,
            Eta product of level 12 : (eta_1)^-1 (eta_2)^3 (eta_3)^3 (eta_4)^-2 (eta_6)^-9 (eta_12)^6,
            Eta product of level 12 : (eta_1)^1 (eta_2)^-1 (eta_3)^-3 (eta_4)^-2 (eta_6)^7 (eta_12)^-2,
            Eta product of level 12 : (eta_1)^-6 (eta_2)^9 (eta_3)^2 (eta_4)^-3 (eta_6)^-3 (eta_12)^1]
            sage: EtaGroup(12).basis(reduce=False) # much bigger coefficients
            [Eta product of level 12 : (eta_2)^24 (eta_12)^-24,
            Eta product of level 12 : (eta_1)^-336 (eta_2)^576 (eta_3)^696 (eta_4)^-216 (eta_6)^-576 (eta_12)^-144,
            Eta product of level 12 : (eta_1)^-8 (eta_2)^-2 (eta_6)^2 (eta_12)^8,
            Eta product of level 12 : (eta_1)^1 (eta_2)^9 (eta_3)^13 (eta_4)^-4 (eta_6)^-15 (eta_12)^-4,
            Eta product of level 12 : (eta_1)^15 (eta_2)^-24 (eta_3)^-29 (eta_4)^9 (eta_6)^24 (eta_12)^5]

        ALGORITHM: An eta product of level `N` is uniquely
        determined by the integers `r_d` for `d | N` with
        `d < N`, since `\sum_{d | N} r_d = 0`. The valid
        `r_d` are those that satisfy two congruences modulo 24,
        and one congruence modulo 2 for every prime divisor of N. We beef
        up the congruences modulo 2 to congruences modulo 24 by multiplying
        by 12. To calculate the kernel of the ensuing map
        `\ZZ^m \to (\ZZ/24\ZZ)^n`
        we lift it arbitrarily to an integer matrix and calculate its Smith
        normal form. This gives a basis for the lattice.

        This lattice typically contains "large" elements, so by default we
        pass it to the reduce_basis() function which performs
        LLL-reduction to give a more manageable basis.
        """
        N = self.level()
        divs = divisors(N)[:-1]
        s = len(divs)
        primedivs = prime_divisors(N)

        rows = []
        for i in range(s):
            # generate a row of relation matrix
            row = [
                Mod(divs[i], 24) - Mod(N, 24),
                Mod(N / divs[i], 24) - Mod(1, 24)
            ]
            for p in primedivs:
                row.append(Mod(12 * (N / divs[i]).valuation(p), 24))
            rows.append(row)
        M = matrix(IntegerModRing(24), rows)
        Mlift = M.change_ring(ZZ)
        # now we compute elementary factors of Mlift
        S, U, V = Mlift.smith_form()
        good_vects = []
        for i in range(U.nrows()):
            vect = U.row(i)
            nf = (i < S.ncols() and S[i, i]) or 0
            good_vects.append((vect * 24 / gcd(nf, 24)).list())
        for v in good_vects:
            v.append(-sum([r for r in v]))
        dicts = []
        for v in good_vects:
            dicts.append({})
            for i in range(s):
                dicts[-1][divs[i]] = v[i]
            dicts[-1][N] = v[-1]
        if reduce:
            return self.reduce_basis([self(d) for d in dicts])
        else:
            return [self(d) for d in dicts]
Esempio n. 37
0
def affine_minimal(vp, return_transformation=False, D=None, quick=False):
    r"""
    Determine if given map is affine minimal.

    Given vp a scheme morphisms on the projective line over the rationals,
    this procedure determines if `\phi` is minimal. In particular, it determines
    if the map is affine minimal, which is enough to decide if it is minimal
    or not. See Proposition 2.10 in [Bruin-Molnar]_.

    INPUT:

    - ``vp`` -- scheme morphism on the projective line.

    - ``D`` -- a list of primes, in case one only wants to check minimality
               at those specific primes.

    - ``return_transformation`` -- a boolean value, default value True. This
      signals a return of the ``PGL_2`` transformation to conjugate ``vp`` to
      the calculated minimal model. default: False.

    - ``quick`` -- a boolean value. If true the algorithm terminates once
      algorithm determines F/G is not minimal, otherwise algorithm only
      terminates once a minimal model has been found.

    OUTPUT:

    - ``newvp`` -- scheme morphism on the projective line.

    - ``conj`` -- linear fractional transformation which conjugates ``vp`` to ``newvp``.

    EXAMPLES::

        sage: PS.<X,Y> = ProjectiveSpace(QQ, 1)
        sage: H = Hom(PS,PS)
        sage: vp = H([X^2 + 9*Y^2, X*Y])
        sage: from sage.schemes.projective.endPN_minimal_model import affine_minimal
        sage: affine_minimal(vp, True)
        (
        Scheme endomorphism of Projective Space of dimension 1 over Rational
        Field
          Defn: Defined on coordinates by sending (X : Y) to
                (X^2 + Y^2 : X*Y)
        ,
        [3 0]
        [0 1]
        )
    """
    BR = vp.domain().base_ring()
    conj = matrix(BR,2,2,1)
    flag = True
    d = vp.degree()

    vp.normalize_coordinates();
    Affvp = vp.dehomogenize(1)
    R = Affvp.coordinate_ring()
    if R.is_field():
        #want the polynomial ring not the fraction field
        R = R.ring()
    F = R(Affvp[0].numerator())
    G = R(Affvp[0].denominator())
    if R(G.degree()) == 0 or R(F.degree()) == 0:
        raise TypeError("affine minimality is only considered for maps not of the form f or 1/f for a polynomial f")
    z = F.parent().gen(0)
    minF,minG = F,G
    #If the valuation of a prime in the resultant is small enough, we can say the
    #map is affine minimal at that prime without using the local minimality loop. See
    #Theorem 3.2.2 in [Molnar, M.Sc. thesis]
    if d%2 == 0:
        g = d
    else:
        g = 2*d
    Res = vp.resultant();

    #Some quantities needed for the local minimization loop, but we compute now
    #since the value is constant, so we do not wish to compute in every local loop.
    #See Theorem 3.3.3 in [Molnar, M.Sc thesis]
    H = F-z*minG
    d1 = F.degree()
    A = AffineSpace(BR,1,H.parent().variable_name())
    end_ring = End(A)
    ubRes = end_ring([H/minG]).homogenize(1).resultant()
    #Set the primes to check minimality at, if not already prescribed
    if D is None:
        D = ZZ(Res).prime_divisors()

    #Check minimality at all primes in D. If D is all primes dividing
    #Res(minF/minG), this is enough to show whether minF/minG is minimal or not. See
    #Propositions 3.2.1 and 3.3.7 in [Molnar, M.Sc. thesis].
    for p in D:
        while True:
            if Res.valuation(p) < g:
                #The model is minimal at p
                min = True
            else:
                #The model may not be minimal at p.
                newvp,conj = Min(vp,p,ubRes,conj)
                if newvp == vp:
                    min = True
                else:
                    vp = newvp
                    Affvp = vp.dehomogenize(1)
                    min = False
            if min:
                #The model is minimal at p
                break
            elif F == Affvp[0].numerator() and G == Affvp[0].denominator():
                #The model is minimal at p
                break
            else:
                #The model is not minimal at p
                flag = False
                if quick:
                    break
        if quick and not flag:
            break

    if quick: #only return whether the model is minimal
        return flag

    if return_transformation:
        return vp, conj
    return vp
Esempio n. 38
0
def _helper_payley_matrix(n, zero_position=True):
    r"""
    Return the marix constructed in Lemma 1.19 page 291 of [SWW72]_.

    This function return a `n^2` matrix `M` whose rows/columns are indexed by
    the element of a finite field on `n` elements `x_1,...,x_n`. The value
    `M_{i,j}` is equal to `\chi(x_i-x_j)`.

    The elements `x_1,...,x_n` are ordered in such a way that the matrix
    (respectively, its submatrix obtained by removing first row and first column in the case
    ``zero_position=False``) is symmetric with respect to its second diagonal.
    The matrix is symmetric if `n=4k+1`, and skew-symmetric otherwise.

    INPUT:

    - ``n`` -- an odd prime power.

    - ``zero_position`` -- if it is true (default), place 0 of ``F_n`` in the middle,
      otherwise place it first.

    .. SEEALSO::

        :func:`rshcd_from_close_prime_powers`

    EXAMPLE::

        sage: from sage.combinat.matrices.hadamard_matrix import _helper_payley_matrix
        sage: _helper_payley_matrix(5)
        [ 0  1 -1 -1  1]
        [ 1  0  1 -1 -1]
        [-1  1  0  1 -1]
        [-1 -1  1  0  1]
        [ 1 -1 -1  1  0]

    TESTS::

        sage: _helper_payley_matrix(11,zero_position=True)
        [ 0 -1  1 -1 -1 -1  1  1  1 -1  1]
        [ 1  0 -1 -1  1 -1  1 -1  1  1 -1]
        [-1  1  0  1 -1 -1 -1 -1  1  1  1]
        [ 1  1 -1  0  1 -1 -1  1 -1 -1  1]
        [ 1 -1  1 -1  0  1 -1 -1 -1  1  1]
        [ 1  1  1  1 -1  0  1 -1 -1 -1 -1]
        [-1 -1  1  1  1 -1  0  1 -1  1 -1]
        [-1  1  1 -1  1  1 -1  0  1 -1 -1]
        [-1 -1 -1  1  1  1  1 -1  0 -1  1]
        [ 1 -1 -1  1 -1  1 -1  1  1  0 -1]
        [-1  1 -1 -1 -1  1  1  1 -1  1  0]
        sage: _helper_payley_matrix(11,zero_position=False)
        [ 0  1  1  1  1 -1  1 -1 -1 -1 -1]
        [-1  0 -1  1 -1 -1  1  1  1 -1  1]
        [-1  1  0 -1 -1  1  1 -1  1  1 -1]
        [-1 -1  1  0  1 -1 -1 -1  1  1  1]
        [-1  1  1 -1  0  1 -1  1 -1 -1  1]
        [ 1  1 -1  1 -1  0 -1 -1 -1  1  1]
        [-1 -1 -1  1  1  1  0  1 -1  1 -1]
        [ 1 -1  1  1 -1  1 -1  0  1 -1 -1]
        [ 1 -1 -1 -1  1  1  1 -1  0 -1  1]
        [ 1  1 -1 -1  1 -1 -1  1  1  0 -1]
        [ 1 -1  1 -1 -1 -1  1  1 -1  1  0]
    """
    from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF
    K = GF(n, prefix='x')

    # Order the elements of K in K_list
    # so that K_list[i] = -K_list[n-i-1]
    K_pairs = set(frozenset([x, -x]) for x in K)
    K_pairs.discard(frozenset([0]))
    K_list = [None] * n
    if zero_position:
        zero_position = n // 2
        shift = 0
    else:
        shift = 1

    for i, (x, y) in enumerate(K_pairs):
        K_list[i + shift] = x
        K_list[-i - 1] = y
    K_list[zero_position] = K(0)
    M = matrix(n, [[2 * ((x - y).is_square()) - 1 for x in K_list]
                   for y in K_list])
    M = M - I(n)
    assert (M * J(n)).is_zero()
    assert (M * M.transpose()) == n * I(n) - J(n)
    return M
Esempio n. 39
0
def regular_symmetric_hadamard_matrix_with_constant_diagonal(
        n, e, existence=False):
    r"""
    Return a Regular Symmetric Hadamard Matrix with Constant Diagonal.

    A Hadamard matrix is said to be *regular* if its rows all sum to the same
    value.

    For `\epsilon\in\{-1,+1\}`, we say that `M` is a `(n,\epsilon)-RSHCD` if
    `M` is a regular symmetric Hadamard matrix with constant diagonal
    `\delta\in\{-1,+1\}` and row sums all equal to `\delta \epsilon
    \sqrt(n)`. For more information, see [HX10]_ or 10.5.1 in
    [BH12]_. For the case `n=324`, see :func:`RSHCD_324` and [CP16]_.

    INPUT:

    - ``n`` (integer) -- side of the matrix

    - ``e`` -- one of `-1` or `+1`, equal to the value of `\epsilon`

    EXAMPLES::

        sage: from sage.combinat.matrices.hadamard_matrix import regular_symmetric_hadamard_matrix_with_constant_diagonal
        sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(4,1)
        [ 1  1  1 -1]
        [ 1  1 -1  1]
        [ 1 -1  1  1]
        [-1  1  1  1]
        sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(4,-1)
        [ 1 -1 -1 -1]
        [-1  1 -1 -1]
        [-1 -1  1 -1]
        [-1 -1 -1  1]

    Other hardcoded values::

        sage: for n,e in [(36,1),(36,-1),(100,1),(100,-1),(196, 1)]:
        ....:     print regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e)
        36 x 36 dense matrix over Integer Ring
        36 x 36 dense matrix over Integer Ring
        100 x 100 dense matrix over Integer Ring
        100 x 100 dense matrix over Integer Ring
        196 x 196 dense matrix over Integer Ring

        sage: for n,e in [(324,1),(324,-1)]: # not tested - long time, tested in RSHCD_324
        ....:     print regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e) # not tested - long time
        324 x 324 dense matrix over Integer Ring
        324 x 324 dense matrix over Integer Ring

    From two close prime powers::

        sage: print regular_symmetric_hadamard_matrix_with_constant_diagonal(64,-1)
        64 x 64 dense matrix over Integer Ring

    Recursive construction::

        sage: print regular_symmetric_hadamard_matrix_with_constant_diagonal(144,-1)
        144 x 144 dense matrix over Integer Ring

    REFERENCE:

    .. [BH12] A. Brouwer and W. Haemers,
      Spectra of graphs,
      Springer, 2012,
      http://homepages.cwi.nl/~aeb/math/ipm/ipm.pdf

    .. [HX10] W. Haemers and Q. Xiang,
      Strongly regular graphs with parameters `(4m^4,2m^4+m^2,m^4+m^2,m^4+m^2)` exist for all `m>1`,
      European Journal of Combinatorics,
      Volume 31, Issue 6, August 2010, Pages 1553-1559,
      http://dx.doi.org/10.1016/j.ejc.2009.07.009.
    """
    if existence and (n, e) in _rshcd_cache:
        return _rshcd_cache[n, e]

    from sage.graphs.strongly_regular_db import strongly_regular_graph

    def true():
        _rshcd_cache[n, e] = True
        return True

    M = None
    if abs(e) != 1:
        raise ValueError
    if n < 0:
        if existence:
            return False
        raise ValueError
    elif n == 4:
        if existence:
            return true()
        if e == 1:
            M = J(4) - 2 * matrix(4, [[int(i + j == 3) for i in range(4)]
                                      for j in range(4)])
        else:
            M = -J(4) + 2 * I(4)
    elif n == 36:
        if existence:
            return true()
        if e == 1:
            M = strongly_regular_graph(36, 15, 6, 6).adjacency_matrix()
            M = J(36) - 2 * M
        else:
            M = strongly_regular_graph(36, 14, 4, 6).adjacency_matrix()
            M = -J(36) + 2 * M + 2 * I(36)
    elif n == 100:
        if existence:
            return true()
        if e == -1:
            M = strongly_regular_graph(100, 44, 18, 20).adjacency_matrix()
            M = 2 * M - J(100) + 2 * I(100)
        else:
            M = strongly_regular_graph(100, 45, 20, 20).adjacency_matrix()
            M = J(100) - 2 * M
    elif n == 196 and e == 1:
        if existence:
            return true()
        M = strongly_regular_graph(196, 91, 42, 42).adjacency_matrix()
        M = J(196) - 2 * M
    elif n == 324:
        if existence:
            return true()
        M = RSHCD_324(e)
    elif (e == 1 and n % 16 == 0 and is_square(n)
          and is_prime_power(sqrt(n) - 1) and is_prime_power(sqrt(n) + 1)):
        if existence:
            return true()
        M = -rshcd_from_close_prime_powers(int(sqrt(n)))

    # Recursive construction: the kronecker product of two RSHCD is a RSHCD
    else:
        from itertools import product
        for n1, e1 in product(divisors(n)[1:-1], [-1, 1]):
            e2 = e1 * e
            n2 = n // n1
            if (regular_symmetric_hadamard_matrix_with_constant_diagonal(
                    n1, e1, existence=True) and
                    regular_symmetric_hadamard_matrix_with_constant_diagonal(
                        n2, e2, existence=True)):
                if existence:
                    return true()
                M1 = regular_symmetric_hadamard_matrix_with_constant_diagonal(
                    n1, e1)
                M2 = regular_symmetric_hadamard_matrix_with_constant_diagonal(
                    n2, e2)
                M = M1.tensor_product(M2)
                break

    if M is None:
        from sage.misc.unknown import Unknown
        _rshcd_cache[n, e] = Unknown
        if existence:
            return Unknown
        raise ValueError("I do not know how to build a {}-RSHCD".format(
            (n, e)))

    assert M * M.transpose() == n * I(n)
    assert set(map(sum, M)) == {e * sqrt(n)}

    return M
Esempio n. 40
0
def hadamard_matrix(n, existence=False, check=True):
    r"""
    Tries to construct a Hadamard matrix using a combination of Paley
    and Sylvester constructions.

    INPUT:

    - ``n`` (integer) -- dimension of the matrix

    - ``existence`` (boolean) -- whether to build the matrix or merely query if
      a construction is available in Sage. When set to ``True``, the function
      returns:

        - ``True`` -- meaning that Sage knows how to build the matrix

        - ``Unknown`` -- meaning that Sage does not know how to build the
          matrix, although the matrix may exist (see :mod:`sage.misc.unknown`).

        - ``False`` -- meaning that the matrix does not exist.

    - ``check`` (boolean) -- whether to check that output is correct before
      returning it. As this is expected to be useless (but we are cautious
      guys), you may want to disable it whenever you want speed. Set to ``True``
      by default.

    EXAMPLES::

        sage: hadamard_matrix(12).det()
        2985984
        sage: 12^6
        2985984
        sage: hadamard_matrix(1)
        [1]
        sage: hadamard_matrix(2)
        [ 1  1]
        [ 1 -1]
        sage: hadamard_matrix(8) # random
        [ 1  1  1  1  1  1  1  1]
        [ 1 -1  1 -1  1 -1  1 -1]
        [ 1  1 -1 -1  1  1 -1 -1]
        [ 1 -1 -1  1  1 -1 -1  1]
        [ 1  1  1  1 -1 -1 -1 -1]
        [ 1 -1  1 -1 -1  1 -1  1]
        [ 1  1 -1 -1 -1 -1  1  1]
        [ 1 -1 -1  1 -1  1  1 -1]
        sage: hadamard_matrix(8).det() == 8^4
        True

    We note that the method `hadamard_matrix()` returns a normalised Hadamard matrix
    (the entries in the first row and column are all +1) ::

        sage: hadamard_matrix(12) # random
        [ 1  1| 1  1| 1  1| 1  1| 1  1| 1  1]
        [ 1 -1|-1  1|-1  1|-1  1|-1  1|-1  1]
        [-----+-----+-----+-----+-----+-----]
        [ 1 -1| 1 -1| 1  1|-1 -1|-1 -1| 1  1]
        [ 1  1|-1 -1| 1 -1|-1  1|-1  1| 1 -1]
        [-----+-----+-----+-----+-----+-----]
        [ 1 -1| 1  1| 1 -1| 1  1|-1 -1|-1 -1]
        [ 1  1| 1 -1|-1 -1| 1 -1|-1  1|-1  1]
        [-----+-----+-----+-----+-----+-----]
        [ 1 -1|-1 -1| 1  1| 1 -1| 1  1|-1 -1]
        [ 1  1|-1  1| 1 -1|-1 -1| 1 -1|-1  1]
        [-----+-----+-----+-----+-----+-----]
        [ 1 -1|-1 -1|-1 -1| 1  1| 1 -1| 1  1]
        [ 1  1|-1  1|-1  1| 1 -1|-1 -1| 1 -1]
        [-----+-----+-----+-----+-----+-----]
        [ 1 -1| 1  1|-1 -1|-1 -1| 1  1| 1 -1]
        [ 1  1| 1 -1|-1  1|-1  1| 1 -1|-1 -1]

    TESTS::

        sage: matrix.hadamard(10,existence=True)
        False
        sage: matrix.hadamard(12,existence=True)
        True
        sage: matrix.hadamard(92,existence=True)
        Unknown
        sage: matrix.hadamard(10)
        Traceback (most recent call last):
        ...
        ValueError: The Hadamard matrix of order 10 does not exist
    """
    if not (n % 4 == 0) and (n > 2):
        if existence:
            return False
        raise ValueError("The Hadamard matrix of order %s does not exist" % n)
    if n == 2:
        if existence:
            return True
        M = matrix([[1, 1], [1, -1]])
    elif n == 1:
        if existence:
            return True
        M = matrix([1])
    elif is_prime_power(n // 2 - 1) and (n // 2 - 1) % 4 == 1:
        if existence:
            return True
        M = hadamard_matrix_paleyII(n)
    elif n == 4 or n % 8 == 0:
        if existence:
            return hadamard_matrix(n // 2, existence=True)
        had = hadamard_matrix(n // 2, check=False)
        chad1 = matrix([list(r) + list(r) for r in had.rows()])
        mhad = (-1) * had
        R = len(had.rows())
        chad2 = matrix(
            [list(had.rows()[i]) + list(mhad.rows()[i]) for i in range(R)])
        M = chad1.stack(chad2)
    elif is_prime_power(n - 1) and (n - 1) % 4 == 3:
        if existence:
            return True
        M = hadamard_matrix_paleyI(n)
    else:
        if existence:
            return Unknown
        raise ValueError(
            "The Hadamard matrix of order %s is not yet implemented." % n)

    if check:
        assert is_hadamard_matrix(M, normalized=True)

    return M
Esempio n. 41
0
def hadamard_matrix_paleyII(n):
    """
    Implements the Paley type II construction.

    The Paley type II case corresponds to the case `p \cong 1 \mod{4}` for a
    prime `p` (see [Hora]_).

    EXAMPLES::

        sage: sage.combinat.matrices.hadamard_matrix.hadamard_matrix_paleyII(12).det()
        2985984
        sage: 12^6
        2985984

    We note that the method returns a normalised Hadamard matrix ::

        sage: sage.combinat.matrices.hadamard_matrix.hadamard_matrix_paleyII(12)
        [ 1  1| 1  1| 1  1| 1  1| 1  1| 1  1]
        [ 1 -1|-1  1|-1  1|-1  1|-1  1|-1  1]
        [-----+-----+-----+-----+-----+-----]
        [ 1 -1| 1 -1| 1  1|-1 -1|-1 -1| 1  1]
        [ 1  1|-1 -1| 1 -1|-1  1|-1  1| 1 -1]
        [-----+-----+-----+-----+-----+-----]
        [ 1 -1| 1  1| 1 -1| 1  1|-1 -1|-1 -1]
        [ 1  1| 1 -1|-1 -1| 1 -1|-1  1|-1  1]
        [-----+-----+-----+-----+-----+-----]
        [ 1 -1|-1 -1| 1  1| 1 -1| 1  1|-1 -1]
        [ 1  1|-1  1| 1 -1|-1 -1| 1 -1|-1  1]
        [-----+-----+-----+-----+-----+-----]
        [ 1 -1|-1 -1|-1 -1| 1  1| 1 -1| 1  1]
        [ 1  1|-1  1|-1  1| 1 -1|-1 -1| 1 -1]
        [-----+-----+-----+-----+-----+-----]
        [ 1 -1| 1  1|-1 -1|-1 -1| 1  1| 1 -1]
        [ 1  1| 1 -1|-1  1|-1  1| 1 -1|-1 -1]

    TESTS::

        sage: from sage.combinat.matrices.hadamard_matrix import (hadamard_matrix_paleyII, is_hadamard_matrix)
        sage: test_cases = [2*(x+1) for x in range(50) if is_prime_power(x) and x%4==1]
        sage: all(is_hadamard_matrix(hadamard_matrix_paleyII(n),normalized=True,verbose=True)
        ....:     for n in test_cases)
        True
    """
    q = n // 2 - 1
    if not (n % 2 == 0 and is_prime_power(q) and (q % 4 == 1)):
        raise ValueError(
            "The order %s is not covered by the Paley type II construction." %
            n)

    from sage.rings.finite_rings.finite_field_constructor import FiniteField
    K = FiniteField(q, 'x')
    K_list = list(K)
    K_list.insert(0, K.zero())
    H = matrix(ZZ, [[(1 if (x - y).is_square() else -1) for x in K_list]
                    for y in K_list])
    for i in range(q + 1):
        H[0, i] = 1
        H[i, 0] = 1
        H[i, i] = 0

    tr = {
        0: matrix(2, 2, [1, -1, -1, -1]),
        1: matrix(2, 2, [1, 1, 1, -1]),
        -1: matrix(2, 2, [-1, -1, -1, 1])
    }

    H = block_matrix(q + 1, q + 1, [tr[v] for r in H for v in r])

    return normalise_hadamard(H)
Esempio n. 42
0
def skew_hadamard_matrix(n, existence=False, skew_normalize=True, check=True):
    r"""
    Tries to construct a skew Hadamard matrix

    A Hadamard matrix `H` is called skew if `H=S-I`, for `I` the identity matrix
    and `-S=S^\top`. Currently constructions from Section 14.1 of [Ha83]_ and few
    more exotic ones are implemented.

    INPUT:

    - ``n`` (integer) -- dimension of the matrix

    - ``existence`` (boolean) -- whether to build the matrix or merely query if
      a construction is available in Sage. When set to ``True``, the function
      returns:

        - ``True`` -- meaning that Sage knows how to build the matrix

        - ``Unknown`` -- meaning that Sage does not know how to build the
          matrix, but that the design may exist (see :mod:`sage.misc.unknown`).

        - ``False`` -- meaning that the matrix does not exist.

    - ``skew_normalize`` (boolean) -- whether to make the 1st row all-one, and
      adjust the 1st column accordingly. Set to ``True`` by default.

    - ``check`` (boolean) -- whether to check that output is correct before
      returning it. As this is expected to be useless (but we are cautious
      guys), you may want to disable it whenever you want speed. Set to ``True``
      by default.

    EXAMPLES::

        sage: from sage.combinat.matrices.hadamard_matrix import skew_hadamard_matrix
        sage: skew_hadamard_matrix(12).det()
        2985984
        sage: 12^6
        2985984
        sage: skew_hadamard_matrix(1)
        [1]
        sage: skew_hadamard_matrix(2)
        [ 1  1]
        [-1  1]

    TESTS::

        sage: skew_hadamard_matrix(10,existence=True)
        False
        sage: skew_hadamard_matrix(12,existence=True)
        True
        sage: skew_hadamard_matrix(784,existence=True)
        True
        sage: skew_hadamard_matrix(10)
        Traceback (most recent call last):
        ...
        ValueError: A skew Hadamard matrix of order 10 does not exist
        sage: skew_hadamard_matrix(36)
        36 x 36 dense matrix over Integer Ring...
        sage: skew_hadamard_matrix(36)==skew_hadamard_matrix(36,skew_normalize=False)
        False
        sage: skew_hadamard_matrix(52)
        52 x 52 dense matrix over Integer Ring...
        sage: skew_hadamard_matrix(92)
        92 x 92 dense matrix over Integer Ring...
        sage: skew_hadamard_matrix(816)     # long time
        816 x 816 dense matrix over Integer Ring...
        sage: skew_hadamard_matrix(100)
        Traceback (most recent call last):
        ...
        ValueError: A skew Hadamard matrix of order 100 is not yet implemented.
        sage: skew_hadamard_matrix(100,existence=True)
        Unknown

    REFERENCES:

    .. [Ha83] M. Hall,
      Combinatorial Theory,
      2nd edition,
      Wiley, 1983
    """
    def true():
        _skew_had_cache[n] = True
        return True

    M = None
    if existence and n in _skew_had_cache:
        return True
    if not (n % 4 == 0) and (n > 2):
        if existence:
            return False
        raise ValueError("A skew Hadamard matrix of order %s does not exist" %
                         n)
    if n == 2:
        if existence:
            return true()
        M = matrix([[1, 1], [-1, 1]])
    elif n == 1:
        if existence:
            return true()
        M = matrix([1])
    elif is_prime_power(n - 1) and ((n - 1) % 4 == 3):
        if existence:
            return true()
        M = hadamard_matrix_paleyI(n, normalize=False)

    elif n % 8 == 0:
        if skew_hadamard_matrix(n // 2,
                                existence=True):  # (Lemma 14.1.6 in [Ha83]_)
            if existence:
                return true()
            H = skew_hadamard_matrix(n // 2, check=False)
            M = block_matrix([[H, H], [-H.T, H.T]])

        else:  # try Williamson construction (Lemma 14.1.5 in [Ha83]_)
            for d in divisors(n)[2:-2]:  # skip 1, 2, n/2, and n
                n1 = n // d
                if is_prime_power(d - 1) and (d % 4 == 0) and (n1 % 4 == 0)\
                    and skew_hadamard_matrix(n1,existence=True):
                    if existence:
                        return true()
                    H = skew_hadamard_matrix(n1, check=False) - I(n1)
                    U = matrix(ZZ, d, lambda i, j: -1 if i==j==0 else\
                                        1 if i==j==1 or (i>1 and j-1==d-i)\
                                          else 0)
                    A = block_matrix(
                        [[matrix([0]),
                          matrix(ZZ, 1, d - 1, [1] * (d - 1))],
                         [
                             matrix(ZZ, d - 1, 1, [-1] * (d - 1)),
                             _helper_payley_matrix(d - 1, zero_position=0)
                         ]]) + I(d)
                    M = A.tensor_product(I(n1)) + (U * A).tensor_product(H)
                    break
    if M is None:  # try Williamson-Goethals-Seidel construction
        if GS_skew_hadamard_smallcases(n, existence=True):
            if existence:
                return true()
            M = GS_skew_hadamard_smallcases(n)

        else:
            if existence:
                return Unknown
            raise ValueError(
                "A skew Hadamard matrix of order %s is not yet implemented." %
                n)
    if skew_normalize:
        dd = diagonal_matrix(M[0])
        M = dd * M * dd
    if check:
        assert is_hadamard_matrix(M, normalized=False, skew=True)
        if skew_normalize:
            from sage.modules.free_module_element import vector
            assert M[0] == vector([1] * n)
    _skew_had_cache[n] = True
    return M
Esempio n. 43
0
def lifting(p, t, A, G):
    r"""
    Compute generators of `\{f \in D[X]^d \mid Af \equiv 0 \pmod{p^{t}}\}` given
    generators of `\{f\in D[X]^d \mid Af \equiv 0\pmod{p^{t-1}}\}`.

    INPUT:

    - ``p`` -- a prime element of some principal ideal domain `D`

    - ``t`` -- a non-negative integer

    - ``A`` -- a `c\times d` matrix over `D[X]`

    - ``G`` -- a matrix over `D[X]`. The columns of
      `\begin{pmatrix}p^{t-1}I& G\end{pmatrix}` are generators
      of `\{ f\in D[X]^d \mid Af \equiv 0\pmod{p^{t-1}}\}`;
      can be set to ``None`` if ``t`` is zero

    OUTPUT:

    A matrix `F` over `D[X]` such that the columns of
    `\begin{pmatrix}p^tI&F&pG\end{pmatrix}` are generators of
    `\{ f\in D[X]^d \mid Af \equiv 0\pmod{p^t}\}`.

    EXAMPLES::

        sage: from sage.matrix.compute_J_ideal import lifting
        sage: X = polygen(ZZ, 'X')
        sage: A = matrix([[1, X], [2*X, X^2]])
        sage: G0 = lifting(5, 0, A, None)
        sage: G1 = lifting(5, 1, A, G0); G1
        []
        sage: (A*G1 % 5).is_zero()
        True
        sage: A = matrix([[1, X, X^2], [2*X, X^2, 3*X^3]])
        sage: G0 = lifting(5, 0, A, None)
        sage: G1 = lifting(5, 1, A, G0); G1
        [3*X^2]
        [    X]
        [    1]
        sage: (A*G1 % 5).is_zero()
        True
        sage: G2 = lifting(5, 2, A, G1); G2
        [15*X^2 23*X^2]
        [   5*X      X]
        [     5      1]
        sage: (A*G2 % 25).is_zero()
        True
        sage: lifting(5, 10, A, G1)
        Traceback (most recent call last):
        ...
        ValueError: A*G not zero mod 5^9

    ALGORITHM:

    [HR2016]_, Algorithm 1.

    TESTS::

        sage: A = matrix([[1, X], [X, X^2]])
        sage: G0 = lifting(5, 0, A, None)
        sage: G1 = lifting(5, 1, A, G0); G1
        Traceback (most recent call last):
        ...
        ValueError: [  1   X|]
        [  X X^2|] does not have full rank.
    """
    from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing


    DX = A.parent().base()
    (X,) = DX.variable_names()
    D = DX.base_ring()
    d = A.ncols()
    c = A.nrows()

    if t == 0:
        return matrix(DX, d, 0)

    if not (A*G % p**(t-1)).is_zero():
        raise ValueError("A*G not zero mod %s^%s" % (p, t-1))


    R = A*G/p**(t-1)
    R.change_ring(DX)

    AR = matrix.block([[A, R]])
    Fp = D.quotient(p*D)
    FpX = PolynomialRing(Fp, name=X)

    ARb = AR.change_ring(FpX)
    (Db, Sb, Tb) = ARb.smith_form()
    #assert Sb * ARb * Tb == Db
    #assert all(i == j or Db[i, j].is_zero()
    #           for i in range(Db.nrows())
    #           for j in range(Db.ncols()))

    r = Db.rank()
    if r != c:
        raise ValueError("{} does not have full rank.".format(ARb))

    T = Tb.change_ring(DX)

    F1 = matrix.block([[p**(t-1) * matrix.identity(d), G]])*T
    F = F1.matrix_from_columns(range(r, F1.ncols()))
    assert (A*F % (p**t)).is_zero(), "A*F=%s" % (A*F)

    return F
Esempio n. 44
0
        def duality_pairing_matrix(self, basis, degree):
            r"""
            The matrix of scalar products between elements of NSym and
            elements of QSym.

            INPUT:

            - ``basis`` -- A basis of the dual Hopf algebra
            - ``degree`` -- a non-negative integer

            OUTPUT:

            - The matrix of scalar products between the basis ``self`` and the basis
              ``basis`` in the dual Hopf algebra of degree ``degree``.

            EXAMPLES:

            The ribbon basis of NCSF is dual to the fundamental basis of
            QSym::

                sage: R = NonCommutativeSymmetricFunctions(QQ).ribbon()
                sage: F = QuasiSymmetricFunctions(QQ).Fundamental()
                sage: R.duality_pairing_matrix(F, 3)
                [1 0 0 0]
                [0 1 0 0]
                [0 0 1 0]
                [0 0 0 1]
                sage: F.duality_pairing_matrix(R, 3)
                [1 0 0 0]
                [0 1 0 0]
                [0 0 1 0]
                [0 0 0 1]

            The complete basis of NCSF is dual to the monomial basis of
            QSym::

                sage: S = NonCommutativeSymmetricFunctions(QQ).complete()
                sage: M = QuasiSymmetricFunctions(QQ).Monomial()
                sage: S.duality_pairing_matrix(M, 3)
                [1 0 0 0]
                [0 1 0 0]
                [0 0 1 0]
                [0 0 0 1]
                sage: M.duality_pairing_matrix(S, 3)
                [1 0 0 0]
                [0 1 0 0]
                [0 0 1 0]
                [0 0 0 1]

            The matrix between the ribbon basis of NCSF and the monomial
            basis of QSym::

                sage: R = NonCommutativeSymmetricFunctions(QQ).ribbon()
                sage: M = QuasiSymmetricFunctions(QQ).Monomial()
                sage: R.duality_pairing_matrix(M, 3)
                [ 1 -1 -1  1]
                [ 0  1  0 -1]
                [ 0  0  1 -1]
                [ 0  0  0  1]
                sage: M.duality_pairing_matrix(R, 3)
                [ 1  0  0  0]
                [-1  1  0  0]
                [-1  0  1  0]
                [ 1 -1 -1  1]

            The matrix between the complete basis of NCSF and the
            fundamental basis of QSym::

                sage: S = NonCommutativeSymmetricFunctions(QQ).complete()
                sage: F = QuasiSymmetricFunctions(QQ).Fundamental()
                sage: S.duality_pairing_matrix(F, 3)
                [1 1 1 1]
                [0 1 0 1]
                [0 0 1 1]
                [0 0 0 1]

            A base case test::

                sage: R.duality_pairing_matrix(M,0)
                [1]
            """
            from sage.matrix.constructor import matrix
            # TODO: generalize to keys indexing the basis of the graded component
            from sage.combinat.composition import Compositions
            return matrix(self.base_ring(),
                    [[self.duality_pairing(self[I], basis[J]) \
                            for J in Compositions(degree)] \
                            for I in Compositions(degree)])
Esempio n. 45
0
def Min(Fun, p, ubRes, conj):
    r"""
    Local loop for Affine_minimal, where we check minimality at the prime p.

    First we bound the possible k in our transformations A = zp^k + b.
    See Theorems 3.3.2 and 3.3.3 in [Molnar]_.

    INPUT:

    - ``Fun`` -- a projective space morphisms.

    - ``p`` - a prime.

    - ``ubRes`` -- integer, the upper bound needed for Th. 3.3.3 in [Molnar]_.

    - ``conj`` -- a 2x2 matrix keeping track of the conjugation.

    OUTPUT:

    - Boolean -- ``True`` if ``Fun`` is minimal at ``p``, ``False`` otherwise.

    - a projective morphism minimal at ``p``.

    EXAMPLES::

        sage: P.<x,y> = ProjectiveSpace(QQ, 1)
        sage: H = End(P)
        sage: f = H([149*x^2 + 39*x*y + y^2, -8*x^2 + 137*x*y + 33*y^2])
        sage: from sage.schemes.projective.endPN_minimal_model import Min
        sage: Min(f, 3, -27000000, matrix(QQ,[[1, 0],[0, 1]]))
        (
        Scheme endomorphism of Projective Space of dimension 1 over Rational
        Field
          Defn: Defined on coordinates by sending (x : y) to
                (181*x^2 + 313*x*y + 81*y^2 : -24*x^2 + 73*x*y + 151*y^2)
        ,
        [3 4]
        [0 1]
        )
    """
    d = Fun.degree()
    AffFun = Fun.dehomogenize(1)
    R = AffFun.coordinate_ring()
    if R.is_field():
        #want the polynomial ring not the fraction field
        R = R.ring()
    F = R(AffFun[0].numerator())
    G = R(AffFun[0].denominator())
    dG = G.degree()
    if dG > (d+1)/2:
        lowerBound = (-2*(G[dG]).valuation(p)/(2*dG - d + 1) + 1).floor()
    else:
        lowerBound = (-2*(F[d]).valuation(p)/(d-1) + 1).floor()
    upperBound = 2*(ubRes.valuation(p))

    if upperBound < lowerBound:
        #There are no possible transformations to reduce the resultant.
        return Fun,conj
    else:
        #Looping over each possible k, we search for transformations to reduce the
        #resultant of F/G
        k = lowerBound
        Qb = PolynomialRing(QQ,'b')
        b = Qb.gen(0)
        Q = PolynomialRing(Qb,'z')
        z = Q.gen(0)
        while k <= upperBound:
            A = (p**k)*z + b
            Ft = Q(F(A) - b*G(A))
            Gt = Q((p**k)*G(A))
            Fcoeffs = Ft.coefficients(sparse=False)
            Gcoeffs = Gt.coefficients(sparse=False)
            coeffs = Fcoeffs + Gcoeffs
            RHS = (d + 1)*k/2
            #If there is some b such that Res(phi^A) < Res(phi), we must have ord_p(c) >
            #RHS for each c in coeffs.
            #Make sure constant coefficients in coeffs satisfy the inequality.
            if all( QQ(c).valuation(p) > RHS for c in coeffs if c.degree() ==0 ):
                #Constant coefficients in coeffs have large enough valuation, so check
                #the rest. We start by checking if simply picking b=0 works
                if all(c(0).valuation(p) > RHS for c in coeffs):
                    #A = z*p^k satisfies the inequalities, and F/G is not minimal
                    #"Conjugating by", p,"^", k, "*z +", 0
                    newconj = matrix(QQ,2,2,[p**k,0,0,1])
                    minFun = Fun.conjugate(newconj)
                    conj = conj*newconj
                    minFun.normalize_coordinates()
                    return minFun, conj

                #Otherwise we search if any value of b will work. We start by finding a
                #minimum bound on the valuation of b that is necessary. See Theorem 3.3.5
                #in [Molnar, M.Sc. thesis].
                bval = max([bCheck(coeff,RHS,p,b) for coeff in coeffs if coeff.degree() > 0])

                #We scale the coefficients in coeffs, so that we may assume ord_p(b) is
                #at least 0
                scaledCoeffs = [coeff(b*(p**bval)) for coeff in coeffs]

                #We now scale the inequalities, ord_p(coeff) > RHS, so that coeff is in
                #ZZ[b]
                scale = QQ(max([coeff.denominator() for coeff in scaledCoeffs]))
                normalizedCoeffs = [coeff*scale for coeff in scaledCoeffs]
                scaleRHS = RHS + scale.valuation(p)

                #We now search for integers that satisfy the inequality ord_p(coeff) >
                #RHS. See Lemma 3.3.6 in [Molnar, M.Sc. thesis].
                bound = (scaleRHS+1).floor()
                bool,sol = blift(normalizedCoeffs,bound,p)

                #If bool is true after lifting, we have a solution b, and F/G is not
                #minimal.
                if bool:
                    #Rescale, conjugate and return new map
                    bsol = QQ(sol*(p**bval))
                    #"Conjugating by ", p,"^", k, "*z +", bsol
                    newconj = matrix(QQ,2,2,[p**k,bsol,0,1])
                    minFun = Fun.conjugate(newconj)
                    conj = conj*newconj

                    minFun.normalize_coordinates()
                    return minFun, conj
            k = k + 1
        return Fun, conj
Esempio n. 46
0
def rshcd_from_close_prime_powers(n):
    r"""
    Return a `(n^2,1)`-RSHCD when `n-1` and `n+1` are odd prime powers and `n=0\pmod{4}`.

    The construction implemented here appears in Theorem 4.3 from [GS70]_.

    Note that the authors of [SWW72]_ claim in Corollary 5.12 (page 342) to have
    proved the same result without the `n=0\pmod{4}` restriction with a *very*
    similar construction. So far, however, I (Nathann Cohen) have not been able
    to make it work.

    INPUT:

    - ``n`` -- an integer congruent to `0\pmod{4}`

    .. SEEALSO::

        :func:`regular_symmetric_hadamard_matrix_with_constant_diagonal`

    EXAMPLES::

        sage: from sage.combinat.matrices.hadamard_matrix import rshcd_from_close_prime_powers
        sage: rshcd_from_close_prime_powers(4)
        [-1 -1  1 -1  1 -1 -1  1 -1  1 -1 -1  1 -1  1 -1]
        [-1 -1  1  1 -1 -1 -1 -1 -1  1  1 -1 -1  1 -1  1]
        [ 1  1 -1  1  1 -1 -1 -1 -1 -1  1 -1 -1 -1  1 -1]
        [-1  1  1 -1  1  1 -1 -1 -1 -1 -1  1 -1 -1 -1  1]
        [ 1 -1  1  1 -1  1  1 -1 -1 -1 -1 -1  1 -1 -1 -1]
        [-1 -1 -1  1  1 -1  1  1 -1 -1 -1  1 -1  1 -1 -1]
        [-1 -1 -1 -1  1  1 -1 -1  1 -1  1 -1  1  1 -1 -1]
        [ 1 -1 -1 -1 -1  1 -1 -1 -1  1 -1  1 -1  1  1 -1]
        [-1 -1 -1 -1 -1 -1  1 -1 -1 -1  1  1  1 -1  1  1]
        [ 1  1 -1 -1 -1 -1 -1  1 -1 -1 -1 -1  1  1 -1  1]
        [-1  1  1 -1 -1 -1  1 -1  1 -1 -1 -1 -1  1  1 -1]
        [-1 -1 -1  1 -1  1 -1  1  1 -1 -1 -1 -1 -1  1  1]
        [ 1 -1 -1 -1  1 -1  1 -1  1  1 -1 -1 -1 -1 -1  1]
        [-1  1 -1 -1 -1  1  1  1 -1  1  1 -1 -1 -1 -1 -1]
        [ 1 -1  1 -1 -1 -1 -1  1  1 -1  1  1 -1 -1 -1 -1]
        [-1  1 -1  1 -1 -1 -1 -1  1  1 -1  1  1 -1 -1 -1]

    REFERENCE:

    .. [SWW72] A Street, W. Wallis, J. Wallis,
      Combinatorics: Room squares, sum-free sets, Hadamard matrices.
      Lecture notes in Mathematics 292 (1972).
    """
    if n % 4:
        raise ValueError("n(={}) must be congruent to 0 mod 4")

    a, b = sorted([n - 1, n + 1], key=lambda x: -x % 4)
    Sa = _helper_payley_matrix(a)
    Sb = _helper_payley_matrix(b)
    U = matrix(a, [[int(i + j == a - 1) for i in range(a)] for j in range(a)])

    K = (U * Sa).tensor_product(Sb) + U.tensor_product(J(b) - I(b)) - J(
        a).tensor_product(I(b))

    F = lambda x: diagonal_matrix([-(-1)**i for i in range(x)])
    G = block_diagonal_matrix([J(1), I(a).tensor_product(F(b))])
    e = matrix(a * b, [1] * (a * b))
    H = block_matrix(2, [-J(1), e.transpose(), e, K])

    HH = G * H * G
    assert len(set(map(sum, HH))) == 1
    assert HH**2 == n**2 * I(n**2)
    return HH
Esempio n. 47
0
    def _LKB_matrix_(self, braid, variab):
        """
        Compute the Lawrence-Krammer-Bigelow representation matrix.

        The variables of the matrix must be given. This actual
        computation is done in this helper method for caching
        purposes.

        INPUT:

        - ``braid`` -- tuple of integers. The Tietze list of the
          braid.

        - ``variab`` -- string. the names of the variables that will
          appear in the matrix. They must be given as a string,
          separated by a comma

        OUTPUT:

        The LKB matrix of the braid, with respect to the variables.

        TESTS::

            sage: B=BraidGroup(3)
            sage: B._LKB_matrix_((2, 1, 2), 'x, y')
            [             0 -x^4*y + x^3*y         -x^4*y]
            [             0         -x^3*y              0]
            [        -x^2*y  x^3*y - x^2*y              0]
            sage: B._LKB_matrix_((1, 2, 1), 'x, y')
            [             0 -x^4*y + x^3*y         -x^4*y]
            [             0         -x^3*y              0]
            [        -x^2*y  x^3*y - x^2*y              0]
            sage: B._LKB_matrix_((-1, -2, -1, 2, 1, 2), 'x, y')
            [1 0 0]
            [0 1 0]
            [0 0 1]
        """
        n = self.strands()
        if len(braid) > 1:
            A = self._LKB_matrix_(braid[:1], variab)
            for i in braid[1:]:
                A = A * self._LKB_matrix_((i, ), variab)
            return A
        l = list(Set(range(n)).subsets(2))
        R = LaurentPolynomialRing(IntegerRing(), variab)
        q = R.gens()[0]
        t = R.gens()[1]
        if len(braid) == 0:
            return identity_matrix(R, len(l), sparse=True)
        A = matrix(R, len(l), sparse=True)
        if braid[0] > 0:
            i = braid[0] - 1
            for m in range(len(l)):
                j = min(l[m])
                k = max(l[m])
                if i == j - 1:
                    A[l.index(Set([i, k])), m] = q
                    A[l.index(Set([i, j])), m] = q * q - q
                    A[l.index(Set([j, k])), m] = 1 - q
                elif i == j and not j == k - 1:
                    A[l.index(Set([j, k])), m] = 0
                    A[l.index(Set([j + 1, k])), m] = 1
                elif k - 1 == i and not k - 1 == j:
                    A[l.index(Set([j, i])), m] = q
                    A[l.index(Set([j, k])), m] = 1 - q
                    A[l.index(Set([i, k])), m] = (1 - q) * q * t
                elif i == k:
                    A[l.index(Set([j, k])), m] = 0
                    A[l.index(Set([j, k + 1])), m] = 1
                elif i == j and j == k - 1:
                    A[l.index(Set([j, k])), m] = -t * q * q
                else:
                    A[l.index(Set([j, k])), m] = 1
            return A
        else:
            i = -braid[0] - 1
            for m in range(len(l)):
                j = min(l[m])
                k = max(l[m])
                if i == j - 1:
                    A[l.index(Set([j - 1, k])), m] = 1
                elif i == j and not j == k - 1:
                    A[l.index(Set([j + 1, k])), m] = q**(-1)
                    A[l.index(Set([j, k])), m] = 1 - q**(-1)
                    A[l.index(Set([j, j + 1])),
                      m] = t**(-1) * q**(-1) - t**(-1) * q**(-2)
                elif k - 1 == i and not k - 1 == j:
                    A[l.index(Set([j, k - 1])), m] = 1
                elif i == k:
                    A[l.index(Set([j, k + 1])), m] = q**(-1)
                    A[l.index(Set([j, k])), m] = 1 - q**(-1)
                    A[l.index(Set([k, k + 1])), m] = -q**(-1) + q**(-2)
                elif i == j and j == k - 1:
                    A[l.index(Set([j, k])), m] = -t**(-1) * q**(-2)
                else:
                    A[l.index(Set([j, k])), m] = 1
            return A
Esempio n. 48
0
def hadamard_matrix_paleyI(n, normalize=True):
    """
    Implements the Paley type I construction.

    The Paley type I case corresponds to the case `p \cong 3 \mod{4}` for a
    prime `p` (see [Hora]_).

    INPUT:

    - ``n`` -- the matrix size 

    - ``normalize`` (boolean) -- whether to normalize the result.

    EXAMPLES:

    We note that this method by default returns a normalised Hadamard matrix ::

        sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_paleyI
        sage: hadamard_matrix_paleyI(4)
        [ 1  1  1  1]
        [ 1 -1  1 -1]
        [ 1 -1 -1  1]
        [ 1  1 -1 -1]

    Otherwise, it returns a skew Hadamard matrix `H`, i.e. `H=S+I`, with
    `S=-S^\top`  ::

        sage: M=hadamard_matrix_paleyI(4, normalize=False); M
        [ 1  1  1  1]
        [-1  1  1 -1]
        [-1 -1  1  1]
        [-1  1 -1  1]
        sage: S=M-identity_matrix(4); -S==S.T
        True

    TESTS::

        sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix
        sage: test_cases = [x+1 for x in range(100) if is_prime_power(x) and x%4==3]
        sage: all(is_hadamard_matrix(hadamard_matrix_paleyI(n),normalized=True,verbose=True)
        ....:     for n in test_cases)
        True
        sage: all(is_hadamard_matrix(hadamard_matrix_paleyI(n,normalize=False),verbose=True)
        ....:     for n in test_cases)
        True
    """
    p = n - 1
    if not (is_prime_power(p) and (p % 4 == 3)):
        raise ValueError(
            "The order %s is not covered by the Paley type I construction." %
            n)

    from sage.rings.finite_rings.finite_field_constructor import FiniteField
    K = FiniteField(p, 'x')
    K_list = list(K)
    K_list.insert(0, K.zero())
    H = matrix(ZZ, [[(1 if (x - y).is_square() else -1) for x in K_list]
                    for y in K_list])
    for i in range(n):
        H[i, 0] = -1
        H[0, i] = 1
    if normalize:
        for i in range(n):
            H[i, i] = -1
        H = normalise_hadamard(H)
    return H
Esempio n. 49
0
def _eta_relations_helper(eta1, eta2, degree, qexp_terms, labels, verbose):
    r"""
    Helper function used by eta_poly_relations. Finds a basis for the
    space of linear relations between the first qexp_terms of the
    `q`-expansions of the monomials
    `\eta_1^i * \eta_2^j` for `0 \le i,j < degree`,
    and calculates a Groebner basis for the ideal generated by these
    relations.

    Liable to return meaningless results if qexp_terms isn't at least
    `1 + d*(m_1,m_2)` where

    .. MATH::

       m_i = min(0, {\text degree of the pole of $\eta_i$ at $\infty$})

    as then 1 will be in the ideal.

    EXAMPLES::

        sage: from sage.modular.etaproducts import _eta_relations_helper
        sage: r,s = EtaGroup(4).basis()
        sage: _eta_relations_helper(r,s,4,100,['a','b'],False)
        [a*b - a + 16]
        sage: _eta_relations_helper(EtaProduct(26, {2:2,13:2,26:-2,1:-2}),EtaProduct(26, {2:4,13:2,26:-4,1:-2}),3,12,['a','b'],False) # not enough terms, will return rubbish
        [1]
    """
    indices = [(i, j) for j in range(degree) for i in range(degree)]
    inf = CuspFamily(eta1.level(), 1)

    pole_at_infinity = -(min([0, eta1.order_at_cusp(inf)]) +
                         min([0, eta2.order_at_cusp(inf)])) * degree
    if verbose:
        print("Trying all coefficients from q^%s to q^%s inclusive" %
              (-pole_at_infinity, -pole_at_infinity + qexp_terms - 1))

    rows = []
    for j in range(qexp_terms):
        rows.append([])
    for i in indices:
        func = (eta1**i[0] * eta2**i[1]).qexp(qexp_terms)
        for j in range(qexp_terms):
            rows[j].append(func[j - pole_at_infinity])
    M = matrix(rows)
    V = M.right_kernel()
    if V.dimension() == 0:
        if verbose:
            print("No polynomial relation of order %s valid for %s terms" %
                  (degree, qexp_terms))
        return None
    if V.dimension() >= 1:
        R = PolynomialRing(QQ, 2, labels)
        x, y = R.gens()
        relations = []
        for c in V.basis():
            relations.append(
                sum([
                    c[v] * x**indices[v][0] * y**indices[v][1]
                    for v in range(len(indices))
                ]))
        id = R.ideal(relations)
        return id.groebner_basis()
Esempio n. 50
0
    def hilbert_symbol_negative_at_S(self, S, b, check=True):
        r"""
        Returns an integer that has a negative Hilbert symbol with respect
        to a given rational number and a given set of primes (or places).

        The function is algorithm 3.4.1 in [Kir2016]_. It finds an integer `a`
        that has negative Hilbert symbol with respect to a given rational number
        exactly at a given set of primes (or places).

        INPUT:

        - ``S`` -- a list of rational primes, the infinite place as real
          embedding of `\QQ` or as -1
        - ``b`` -- a non-zero rational number which is a non-square locally
          at every prime in ``S``.
        - ``check`` -- ``bool`` (default:``True``) perform additional checks on
          input and confirm the output.

        OUTPUT:

        - An integer `a` that has negative Hilbert symbol `(a,b)_p` for
          every place `p` in `S` and no other place.

        EXAMPLES::

            sage: QQ.hilbert_symbol_negative_at_S([-1,5,3,2,7,11,13,23], -10/7)
            -9867
            sage: QQ.hilbert_symbol_negative_at_S([3, 5, QQ.places()[0], 11], -15)
            -33
            sage: QQ.hilbert_symbol_negative_at_S([3, 5], 2)
            15

        TESTS::

            sage: QQ.hilbert_symbol_negative_at_S(5/2, -2)
            Traceback (most recent call last):
            ...
            TypeError: first argument must be a list or integer

        ::

            sage: QQ.hilbert_symbol_negative_at_S([1, 3], 0)
            Traceback (most recent call last):
            ...
            ValueError: second argument must be nonzero

        ::

            sage: QQ.hilbert_symbol_negative_at_S([-1, 3, 5], 2)
            Traceback (most recent call last):
            ...
            ValueError: list should be of even cardinality

        ::

            sage: QQ.hilbert_symbol_negative_at_S([1, 3], 2)
            Traceback (most recent call last):
            ...
            ValueError: all entries in list must be prime or -1 for
            infinite place

        ::

            sage: QQ.hilbert_symbol_negative_at_S([5, 7], 2)
            Traceback (most recent call last):
            ...
            ValueError: second argument must be a nonsquare with
            respect to every finite prime in the list

        ::

            sage: QQ.hilbert_symbol_negative_at_S([1, 3], sqrt(2))
            Traceback (most recent call last):
            ...
            TypeError: second argument must be a rational number

        ::

            sage: QQ.hilbert_symbol_negative_at_S([-1, 3], 2)
            Traceback (most recent call last):
            ...
            ValueError: if the infinite place is in the list, the second
            argument must be negative

        AUTHORS:

        - Simon Brandhorst, Juanita Duque, Anna Haensch, Manami Roy, Sandi Rudzinski (10-24-2017)

        """
        from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF
        from sage.rings.padics.factory import Qp
        from sage.modules.free_module import VectorSpace
        from sage.matrix.constructor import matrix
        from sage.sets.primes import Primes
        from sage.arith.misc import hilbert_symbol, is_prime

        # input checks
        if not type(S) is list:
            raise TypeError("first argument must be a list or integer")
        # -1 is used for the infinite place
        infty = -1
        for i in range(len(S)):
            if S[i] == self.places()[0]:
                S[i] = -1
        if not b in self:
            raise TypeError("second argument must be a rational number")
        b = self(b)
        if b == 0:
            raise ValueError("second argument must be nonzero")
        if len(S) % 2 != 0:
            raise ValueError("list should be of even cardinality")
        for p in S:
            if p != infty:
                if check and not is_prime(p):
                    raise ValueError("all entries in list must be prime"
                                     " or -1 for infinite place")
                R = Qp(p)
                if R(b).is_square():
                    raise ValueError(
                        "second argument must be a nonsquare with"
                        " respect to every finite prime in the list")
            elif b > 0:
                raise ValueError("if the infinite place is in the list, "
                                 "the second argument must be negative")
        # L is the list of primes that we need to consider, b must have
        # nonzero valuation for each prime in L, this is the set S'
        # in Kirschmer's algorithm
        L = []
        L = [p[0] for p in b.factor() if p[0] not in S]
        # We must also consider 2 to be in L
        if 2 not in L and 2 not in S:
            L.append(2)
        # This adds the infinite place to L
        if b < 0 and infty not in S:
            L.append(infty)

        P = S + L
        # This constructs the vector v in the algorithm. This is the vector
        # that we are searching for. It represents the case when the Hilbert
        # symbol is negative for all primes in S and positive
        # at all primes in S'
        V = VectorSpace(GF(2), len(P))
        v = V([1] * len(S) + [0] * len(L))

        # Compute the map phi of Hilbert symbols at all the primes
        # in S and S'
        # For technical reasons, a Hilbert symbol of -1 is
        # respresented as 1 and a Hilbert symbol of 1
        # is represented as 0
        def phi(x):
            v = [(1 - hilbert_symbol(x, b, p)) // 2 for p in P]
            return V(v)

        M = matrix(GF(2), [phi(p) for p in P + [-1]])
        # We search through all the primes
        for q in Primes():
            # Only look at this prime if it is not in our list
            if q in P:
                continue

            # The algorithm terminates when the vector v is in the
            # subspace of V generated by the image of the phi map
            # on the set of generators
            w = phi(q)
            W = M.stack(matrix(w))
            if v in W.row_space():
                break
        Pq = P + [-1] + [q]
        l = W.solve_left(v)
        a = self.prod([Pq[i]**ZZ(l[i]) for i in range(l.degree())])
        if check:
            assert phi(a) == v, "oops"
        return a
Esempio n. 51
0
 def morphism_matrix(self, f):
     return matrix(self.domain().base_ring(), [f(b).to_vector()
                        for b in self.domain().basis()]).transpose()
Esempio n. 52
0
    def rho(self, g):
        r"""
        Calculate the action of the group element `g` on the type space.

        EXAMPLES::

            sage: from sage.modular.local_comp.type_space import example_type_space
            sage: T = example_type_space(2)
            sage: m = T.rho([2,0,0,1]); m
            [ 1 -2  1  0]
            [ 1 -1  0  1]
            [ 1  0 -1  1]
            [ 0  1 -2  1]
            sage: v = T.eigensymbol_subspace().basis()[0]
            sage: m * v == v
            True

        We test that it is a left action::

            sage: T = example_type_space(0)
            sage: a = [0,5,4,3]; b = [0,2,3,5]; ab = [1,4,2,2]
            sage: T.rho(ab) == T.rho(a) * T.rho(b)
            True

        An odd level example::

            sage: from sage.modular.local_comp.type_space import TypeSpace
            sage: T = TypeSpace(Newform('54a'), 3)
            sage: a = [0,1,3,0]; b = [2,1,0,1]; ab = [0,1,6,3]
            sage: T.rho(ab) == T.rho(a) * T.rho(b)
            True
        """
        if not self.is_minimal():
            raise NotImplementedError(
                "Group action on non-minimal type space not implemented")

        if self.u() == 0:
            # silly special case: rep is principal series or special, so SL2
            # action on type space is trivial
            raise ValueError("Representation is not supercuspidal")

        p = self.prime()
        f = p**self.u()
        g = [ZZ(_) for _ in g]
        d = (g[0] * g[3] - g[2] * g[1])

        # g is in S(K_0) (easy case)
        if d % f == 1:
            return self._rho_s(g)

        # g is in K_0, but not in S(K_0)

        if d % p != 0:
            try:
                a = self._a
            except AttributeError:
                self._discover_torus_action()
                a = self._a
            i = 0
            while (d * a**i) % f != 1:
                i += 1
                if i > f: raise ArithmeticError
            return self._rho_s([a**i * g[0], g[1], a**i * g[2], g[3]
                                ]) * self._amat**(-i)

        # funny business

        if (self.conductor() % 2 == 0):
            if all([x.valuation(p) > 0 for x in g]):
                eps = self.form().character()(crt(1, p, f, self.tame_level()))
                return ~eps * self.rho([x // p for x in g])
            else:
                raise ArithmeticError("g(={0}) not in K".format(g))

        else:
            m = matrix(ZZ, 2, g)
            s = m.det().valuation(p)
            mm = (matrix(QQ, 2, [0, -1, p, 0])**(-s) * m).change_ring(ZZ)
            return self._unif_ramified()**s * self.rho(mm.list())
    def _find_cyclic_isomorphism_matching_edge(self, polytope, polytope_origin,
                                               p_ray_left, p_ray_right):
        """
        Helper to find an isomorphism of polygons

        INPUT:

        - ``polytope`` -- the lattice polytope to compare to.

        - ``polytope_origin`` -- `\ZZ`-vector. a vertex of ``polytope``

        - ``p_ray_left`` - vector. the vector from ``polytope_origin``
          to one of its neighboring vertices.

        - ``p_ray_right`` - vector. the vector from
          ``polytope_origin`` to the other neighboring vertices.

        OUTPUT:

        The element of the lattice Euclidean group that maps ``self``
        to ``polytope`` with given origin and left/right neighboring
        vertex. A
        :class:`~sage.geometry.polyhedron.lattice_euclidean_group_element.LatticePolytopesNotIsomorphicError`
        is raised if no such isomorphism exists.

        EXAMPLES::

            sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
            sage: L1 = LatticePolytope_PPL((1,0),(0,1),(0,0))
            sage: L2 = LatticePolytope_PPL((1,0,3),(0,1,0),(0,0,1))
            sage: v0, v1, v2 = L2.vertices()
            sage: L1._find_cyclic_isomorphism_matching_edge(L2, v0, v1-v0, v2-v0)
            The map A*x+b with A=
            [ 0  1]
            [-1 -1]
            [ 1  3]
            b =
            (0, 1, 0)
        """
        from sage.geometry.polyhedron.lattice_euclidean_group_element import \
            LatticePolytopesNotIsomorphicError
        polytope_matrix = block_matrix(
            1, 2,
            [p_ray_left.column(), p_ray_right.column()])
        self_vertices = self.ordered_vertices()
        for i in range(len(self_vertices)):
            # three consecutive vertices
            v_left = self_vertices[(i + 0) % len(self_vertices)]
            v_origin = self_vertices[(i + 1) % len(self_vertices)]
            v_right = self_vertices[(i + 2) % len(self_vertices)]
            r_left = v_left - v_origin
            r_right = v_right - v_origin
            self_matrix = block_matrix(
                1, 2, [r_left.column(), r_right.column()])
            A = self_matrix.solve_left(polytope_matrix)
            b = polytope_origin - A * v_origin
            try:
                A = matrix(ZZ, A)
                b = vector(ZZ, b)
            except TypeError:
                continue
            if A.elementary_divisors()[0:2] != [1, 1]:
                continue
            hom = LatticeEuclideanGroupElement(A, b)
            if hom(self) == polytope:
                return hom
        raise LatticePolytopesNotIsomorphicError('different polygons')
Esempio n. 54
0
    def __call__(self, x, check=True):
        r"""
        Convert x into an element of this Hecke algebra. Here x is either:

        - an element of a Hecke algebra equal to this one

        - an element of the corresponding anemic Hecke algebra, if x is a full
          Hecke algebra

        - an element of the corresponding full Hecke algebra of the
          form `T_i` where i is coprime to ``self.level()``, if self
          is an anemic Hecke algebra

        - something that can be converted into an element of the
          underlying matrix space.

        In the last case, the parameter ``check'' controls whether or
        not to check that this element really does lie in the
        appropriate algebra. At present, setting ``check=True'' raises
        a NotImplementedError unless x is a scalar (or a diagonal
        matrix).

        EXAMPLES::

            sage: T = ModularSymbols(11).hecke_algebra()
            sage: T.gen(2) in T
            True
            sage: 5 in T
            True
            sage: T.gen(2).matrix() in T
            Traceback (most recent call last):
            ...
            NotImplementedError: Membership testing for '...' not implemented
            sage: T(T.gen(2).matrix(), check=False)
            Hecke operator on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field defined by:
            [ 3  0 -1]
            [ 0 -2  0]
            [ 0  0 -2]
            sage: A = ModularSymbols(11).anemic_hecke_algebra()
            sage: A(T.gen(3))
            Hecke operator T_3 on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field
            sage: A(T.gen(11))
            Traceback (most recent call last):
            ...
            TypeError: Don't know how to construct an element of Anemic Hecke algebra acting on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field from Hecke operator T_11 on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field

        TESTS:

        We test that coercion is OK between the Hecke algebras associated to two submodules which are equal but have different bases::

            sage: M = CuspForms(Gamma0(57))
            sage: f1,f2,f3 = M.newforms()
            sage: N1 = M.submodule(M.free_module().submodule_with_basis([f1.element().element(), f2.element().element()]))
            sage: N2 = M.submodule(M.free_module().submodule_with_basis([f1.element().element(), (f1.element() + f2.element()).element()]))
            sage: N1.hecke_operator(5).matrix_form()
            Hecke operator on Modular Forms subspace of dimension 2 of ... defined by:
            [-3  0]
            [ 0  1]
            sage: N2.hecke_operator(5).matrix_form()
            Hecke operator on Modular Forms subspace of dimension 2 of ... defined by:
            [-3  0]
            [-4  1]
            sage: N1.hecke_algebra()(N2.hecke_operator(5)).matrix_form()
            Hecke operator on Modular Forms subspace of dimension 2 of ... defined by:
            [-3  0]
            [ 0  1]
            sage: N1.hecke_algebra()(N2.hecke_operator(5).matrix_form())
            Hecke operator on Modular Forms subspace of dimension 2 of ... defined by:
            [-3  0]
            [ 0  1]
        """
        try:
            if not isinstance(x, Element):
                x = self.base_ring()(x)
            if x.parent() is self:
                return x
            elif hecke_operator.is_HeckeOperator(x):
                if x.parent() == self \
                        or (self.is_anemic() == False and x.parent() == self.anemic_subalgebra()) \
                        or (self.is_anemic() == True and x.parent().anemic_subalgebra() == self and arith.gcd(x.index(), self.level()) == 1):
                    return hecke_operator.HeckeOperator(self, x.index())
                else:
                    raise TypeError
            elif hecke_operator.is_HeckeAlgebraElement(x):
                if x.parent() == self or (self.is_anemic() == False
                                          and x.parent()
                                          == self.anemic_subalgebra()):
                    if x.parent().module().basis_matrix() == self.module(
                    ).basis_matrix():
                        return hecke_operator.HeckeAlgebraElement_matrix(
                            self, x.matrix())
                    else:
                        A = matrix([self.module().coordinate_vector(x.parent().module().gen(i)) \
                            for i in xrange(x.parent().module().rank())])
                        return hecke_operator.HeckeAlgebraElement_matrix(
                            self, ~A * x.matrix() * A)
                elif x.parent() == self.anemic_subalgebra():
                    pass

                else:
                    raise TypeError
            else:
                A = self.matrix_space()(x)
                if check:
                    if not A.is_scalar():
                        raise NotImplementedError(
                            "Membership testing for '%s' not implemented" %
                            self)
                return hecke_operator.HeckeAlgebraElement_matrix(self, A)

        except TypeError:
            raise TypeError(
                "Don't know how to construct an element of %s from %s" %
                (self, x))
Esempio n. 55
0
    def parity_check_matrix(self):
        r"""
        Return the parity check matrix of ``self``.

        The parity check matrix of a linear code `C` corresponds to the
        generator matrix of the dual code of `C`.

        Parity check matrices of all Golay codes are known, and are thus returned
        by this method without performing any computation.

        EXAMPLES::

            sage: C = codes.GolayCode(GF(3), extended=False)
            sage: C.parity_check_matrix()
            [1 0 0 0 0 1 2 2 2 1 0]
            [0 1 0 0 0 0 1 2 2 2 1]
            [0 0 1 0 0 2 1 2 0 1 2]
            [0 0 0 1 0 1 1 0 1 1 1]
            [0 0 0 0 1 2 2 2 1 0 1]
        """
        n = self.length()
        if n == 23:
            H = matrix(GF(2), [[
                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
                1, 0
            ],
                               [
                                   0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
                                   1, 1, 0, 0, 1, 0, 0, 1
                               ],
                               [
                                   0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
                                   0, 1, 1, 1, 0, 1, 1, 0
                               ],
                               [
                                   0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
                                   0, 0, 1, 1, 1, 0, 1, 1
                               ],
                               [
                                   0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
                                   1, 0, 0, 0, 1, 1, 1, 1
                               ],
                               [
                                   0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1,
                                   1, 1, 0, 1, 0, 1, 0, 1
                               ],
                               [
                                   0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1,
                                   0, 1, 1, 1, 1, 0, 0, 0
                               ],
                               [
                                   0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1,
                                   1, 0, 1, 1, 1, 1, 0, 0
                               ],
                               [
                                   0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0,
                                   1, 1, 0, 1, 1, 1, 1, 0
                               ],
                               [
                                   0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
                                   0, 1, 1, 0, 1, 1, 1, 1
                               ],
                               [
                                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
                                   0, 0, 1, 0, 0, 1, 0, 1
                               ]])
        elif n == 11:
            H = matrix(GF(3), [[1, 0, 0, 0, 0, 1, 2, 2, 2, 1, 0],
                               [0, 1, 0, 0, 0, 0, 1, 2, 2, 2, 1],
                               [0, 0, 1, 0, 0, 2, 1, 2, 0, 1, 2],
                               [0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1],
                               [0, 0, 0, 0, 1, 2, 2, 2, 1, 0, 1]])
        else:
            H = self.generator_matrix()
        return H
    def period(self, m):
        """
        Return the period of the binary recurrence sequence modulo
        an integer ``m``.

        If `n_1` is congruent to `n_2` modulo ``period(m)``, then `u_{n_1}` is
        is congruent to `u_{n_2}` modulo ``m``.

        INPUT:

        - ``m`` -- an integer (modulo which the period of the recurrence relation is calculated).

        OUTPUT:

        - The integer (the period of the sequence modulo m)

        EXAMPLES:

        If `p = \\pm 1 \\mod 5`, then the period of the Fibonacci sequence
        mod `p` is `p-1` (c.f. Lemma 3.3 of [BMS2006]_).

        ::

            sage: R = BinaryRecurrenceSequence(1,1)
            sage: R.period(31)
            30

            sage: [R(i) % 4 for i in range(12)]
            [0, 1, 1, 2, 3, 1, 0, 1, 1, 2, 3, 1]
            sage: R.period(4)
            6

        This function works for degenerate sequences as well.

        ::

            sage: S = BinaryRecurrenceSequence(2,0,1,2)
            sage: S.is_degenerate()
            True
            sage: S.is_geometric()
            True
            sage: [S(i) % 17 for i in range(16)]
            [1, 2, 4, 8, 16, 15, 13, 9, 1, 2, 4, 8, 16, 15, 13, 9]
            sage: S.period(17)
            8

        .. NOTE:: The answer is cached.
        """

        #If we have already computed the period mod m, then we return the stored value.

        if m in self._period_dict:
            return self._period_dict[m]

        else:
            R = Integers(m)
            A = matrix(R, [[0, 1], [self.c, self.b]])
            w = vector(R, [self.u0, self.u1])
            Fac = list(m.factor())
            Periods = {}

            #To compute the period mod m, we compute the least integer n such that A^n*w == w.  This necessarily
            #divides the order of A as a matrix in GL_2(Z/mZ).

            #We compute the period modulo all distinct prime powers dividing m, and combine via the lcm.
            #To compute the period mod p^e, we first compute the order mod p.  Then the period mod p^e
            #must divide p^{4e-4}*period(p), as the subgroup of matrices mod p^e, which reduce to
            #the identity mod p is of order (p^{e-1})^4.  So we compute the period mod p^e by successively
            #multiplying the period mod p by powers of p.

            for i in Fac:
                p = i[0]
                e = i[1]
                #first compute the period mod p
                if p in self._period_dict:
                    perp = self._period_dict[p]
                else:
                    F = A.change_ring(GF(p))
                    v = w.change_ring(GF(p))
                    FF = F**(p-1)
                    p1fac = list((p-1).factor())

                    #The order of any matrix in GL_2(F_p) either divides p(p-1) or (p-1)(p+1).
                    #The order divides p-1 if it is diagonalizable.  In any case, det(F^(p-1))=1,
                    #so if tr(F^(p-1)) = 2, then it must be triangular of the form [[1,a],[0,1]].
                    #The order of the subgroup of matrices of this form is p, so the order must divide
                    #p(p-1) -- in fact it must be a multiple of p.  If this is not the case, then the
                    #order divides (p-1)(p+1).  As the period divides the order of the matrix in GL_2(F_p),
                    #these conditions hold for the period as well.

                    #check if the order divides (p-1)
                    if FF*v == v:
                        M = p-1
                        Mfac = p1fac

                    #check if the trace is 2, then the order is a multiple of p dividing p*(p-1)
                    elif FF.trace() == 2:
                        M = p-1
                        Mfac = p1fac
                        F = F**p        #replace F by F^p as now we only need to determine the factor dividing (p-1)

                    #otherwise it will divide (p+1)(p-1)
                    else:
                        M = (p+1)*(p-1)
                        p2fac = list((p+1).factor())        #factor the (p+1) and (p-1) terms separately and then combine for speed
                        Mfac_dic = {}
                        for i0, i1 in list(p1fac + p2fac):
                            if i0 not in Mfac_dic:
                                Mfac_dic[i0] = i1
                            else:
                                Mfac_dic[i0] += i1
                        Mfac = list(Mfac_dic.items())

                    #Now use a fast order algorithm to compute the period.  We know that the period divides
                    #M = i_1*i_2*...*i_l where the i_j denote not necessarily distinct prime factors.  As
                    #F^M*v == v, for each i_j, if F^(M/i_j)*v == v, then the period divides (M/i_j).  After
                    #all factors have been iterated over, the result is the period mod p.

                    Mfac = list(Mfac)
                    C = []

                    #expand the list of prime factors so every factor is with multiplicity 1

                    for i0, i1 in Mfac:
                        for j in range(i1):
                            C.append(i0)

                    Mfac = C
                    n = M
                    for ii in Mfac:
                        b = n // ii
                        if F**b * v == v:
                            n = b
                    perp = n

                #Now compute the period mod p^e by stepping up by multiples of p
                F = A.change_ring(Integers(p**e))
                v = w.change_ring(Integers(p**e))
                FF = F**perp
                if FF*v == v:
                    perpe = perp
                else:
                    tries = 0
                    while True:
                        tries += 1
                        FF = FF**p
                        if FF*v == v:
                            perpe = perp*p**tries
                            break
                Periods[p] = perpe

            #take the lcm of the periods mod all distinct primes dividing m
            period = 1
            for p in Periods:
                period = lcm(Periods[p], period)

            self._period_dict[m] = period        #cache the period mod m
            return period
Esempio n. 57
0
        def _symmetric_form_matrix(self):
            r"""
            Return the matrix for the symmetric form `( | )` in
            the weight lattice basis.

            Let `A` be a symmetrizable Cartan matrix with symmetrizer `D`,.
            This returns the matrix `M^t DA M`, where `M` is dependent upon
            the type given below.

            In finite types, `M` is the inverse of the Cartan matrix.

            In affine types, `M` takes the basis
            `(\Lambda_0, \Lambda_1, \ldots, \Lambda_r, \delta)` to
            `(\alpha_0, \ldots, \alpha_r, \Lambda_0)` where `r` is the
            rank of ``self``.

            This is used in computing the symmetric form for affine
            root systems.

            EXAMPLES::

                sage: P = RootSystem(['C',2]).weight_lattice()
                sage: P._symmetric_form_matrix
                [1 1]
                [1 2]

                sage: P = RootSystem(['C',2,1]).weight_lattice()
                sage: P._symmetric_form_matrix
                [0 0 0 1]
                [0 1 1 1]
                [0 1 2 1]
                [1 1 1 0]

                sage: P = RootSystem(['A',4,2]).weight_lattice()
                sage: P._symmetric_form_matrix
                [  0   0   0 1/2]
                [  0   2   2   1]
                [  0   2   4   1]
                [1/2   1   1   0]
            """
            from sage.matrix.constructor import matrix
            ct = self.cartan_type()
            cm = ct.cartan_matrix()
            if cm.det() != 0:
                cm_inv = cm.inverse()
                diag = cm.is_symmetrizable(True)
                return cm_inv.transpose() * matrix.diagonal(diag)

            if not ct.is_affine():
                raise ValueError("only implemented for affine types when the"
                                 " Cartan matrix is singular")

            r = ct.rank()
            a = ct.a()
            # Determine the change of basis matrix
            # La[0], ..., La[r], delta -> al[0], ..., al[r], La[0]
            M = cm.stack(matrix([1] + [0] * (r - 1)))
            M = matrix.block([[M, matrix([[1]] + [[0]] * r)]])
            M = M.inverse()

            if a[0] != 1:
                from sage.rings.all import QQ
                S = matrix([~a[0]] + [0] * (r - 1))
                A = cm.symmetrized_matrix().change_ring(QQ).stack(S)
            else:
                A = cm.symmetrized_matrix().stack(matrix([1] + [0] * (r - 1)))
            A = matrix.block([[A, matrix([[~a[0]]] + [[0]] * r)]])
            return M.transpose() * A * M
Esempio n. 58
0
    def matrix(self, basis1=None, basis2=None):
        r"""
        Return the matrix of ``self`` w.r.t to a pair of bases.

        If the matrix is not known already, it is computed from the matrix in
        another pair of bases by means of the change-of-basis formula.

        INPUT:

        - ``basis1`` -- (default: ``None``) basis of the free module on which
          ``self`` is defined; if none is provided, the module's default basis
          is assumed
        - ``basis2`` -- (default: ``None``) basis of the free module on which
          ``self`` is defined; if none is provided, ``basis2`` is set to
          ``basis1``

        OUTPUT:

        - the matrix representing representing the automorphism ``self`` w.r.t
          to bases ``basis1`` and ``basis2``; more precisely, the columns of
          this matrix are formed by the components w.r.t. ``basis2`` of
          the images of the elements of ``basis1``.

        EXAMPLES:

        Matrices of an automorphism of a rank-3 free `\ZZ`-module::

            sage: M = FiniteRankFreeModule(ZZ, 3, name='M', start_index=1)
            sage: e = M.basis('e')
            sage: a = M.automorphism([[-1,0,0],[0,1,2],[0,1,3]], name='a')
            sage: a.matrix(e)
            [-1  0  0]
            [ 0  1  2]
            [ 0  1  3]
            sage: a.matrix()
            [-1  0  0]
            [ 0  1  2]
            [ 0  1  3]
            sage: f = M.basis('f', from_family=(-e[2], 4*e[1]+3*e[3],  7*e[1]+5*e[3])) ; f
            Basis (f_1,f_2,f_3) on the Rank-3 free module M over the Integer Ring
            sage: a.matrix(f)
            [  1  -6 -10]
            [ -7  83 140]
            [  4 -48 -81]

        Check of the above matrix::

            sage: a(f[1]).display(f)
            a(f_1) = f_1 - 7 f_2 + 4 f_3
            sage: a(f[2]).display(f)
            a(f_2) = -6 f_1 + 83 f_2 - 48 f_3
            sage: a(f[3]).display(f)
            a(f_3) = -10 f_1 + 140 f_2 - 81 f_3

        Check of the change-of-basis formula::

            sage: P = M.change_of_basis(e,f).matrix(e)
            sage: a.matrix(f) == P^(-1) * a.matrix(e) * P
            True

        Check that the matrix of the product of two automorphisms is the
        product of their matrices::

            sage: b = M.change_of_basis(e,f) ; b
            Automorphism of the Rank-3 free module M over the Integer Ring
            sage: b.matrix(e)
            [ 0  4  7]
            [-1  0  0]
            [ 0  3  5]
            sage: (a*b).matrix(e) == a.matrix(e) * b.matrix(e)
            True

        Check that the matrix of the inverse automorphism is the inverse of the
        automorphism's matrix::

            sage: (~a).matrix(e)
            [-1  0  0]
            [ 0  3 -2]
            [ 0 -1  1]
            sage: (~a).matrix(e) == ~(a.matrix(e))
            True

        Matrices of the identity map::

            sage: id = M.identity_map()
            sage: id.matrix(e)
            [1 0 0]
            [0 1 0]
            [0 0 1]
            sage: id.matrix(f)
            [1 0 0]
            [0 1 0]
            [0 0 1]

        """
        from sage.matrix.constructor import matrix
        fmodule = self._fmodule
        if basis1 is None:
            basis1 = fmodule.default_basis()
        elif basis1 not in fmodule.bases():
            raise TypeError("{} is not a basis on the {}".format(
                basis1, fmodule))
        if basis2 is None:
            basis2 = basis1
        elif basis2 not in fmodule.bases():
            raise TypeError("{} is not a basis on the {}".format(
                basis2, fmodule))
        if (basis1, basis2) not in self._matrices:
            if basis2 == basis1:
                comp = self.components(basis1)
                mat = [[comp[[i, j]] for j in fmodule.irange()]
                       for i in fmodule.irange()]
                self._matrices[(basis1, basis1)] = matrix(mat)
            else:
                # 1/ determine the matrix w.r.t. basis1:
                self.matrix(basis1)
                # 2/ perform the change (basis1, basis1) --> (basis1, basis2):
                raise NotImplementedError(
                    "basis1 != basis2 not implemented yet")
        return self._matrices[(basis1, basis2)]
Esempio n. 59
0
def maximal_grading(L):
    r"""
    Return a maximal grading of a Lie algebra defined over an
    algebraically closed field.

    A maximal grading of a Lie algebra `\mathfrak{g}` is the finest
    possible grading of `\mathfrak{g}` over torsion free abelian groups.
    If `\mathfrak{g} = \bigoplus_{n\in \mathbb{Z}^k} \mathfrak{g}_n`
    is a maximal grading, then there exists no other grading
    `\mathfrak{g} = \bigoplus_{a\in A} \mathfrak{g}_a` over a torsion
    free abelian group `A` such that every `\mathfrak{g}_a` is contained
    in some `\mathfrak{g}_n`.

    EXAMPLES:

    A maximal grading of an abelian Lie algebra puts each basis element
    into an independent layer::

        sage: import sys, pathlib
        sage: sys.path.append(str(pathlib.Path().absolute()))
        sage: from lie_gradings.gradings.grading import maximal_grading
        sage: L = LieAlgebra(QQbar, 4, abelian=True)
        sage: maximal_grading(L)
        Grading over Additive abelian group isomorphic to Z + Z + Z + Z
        of Abelian Lie algebra on 4 generators (L[0], L[1], L[2], L[3])
        over Algebraic Field with nonzero layers
          (1, 0, 0, 0) : (L[3],)
          (0, 1, 0, 0) : (L[2],)
          (0, 0, 1, 0) : (L[1],)
          (0, 0, 0, 1) : (L[0],)

    A maximal grading of a free nilpotent Lie algebra decomposes the Lie
    algebra based on how many times each generator appears in the
    defining Lie bracket::

        sage: L = LieAlgebra(QQbar, 3, step=3)
        sage: maximal_grading(L)
        Grading over Additive abelian group isomorphic to Z + Z + Z of
        Free Nilpotent Lie algebra on 14 generators (X_1, X_2, X_3,
        X_12, X_13, X_23, X_112, X_113, X_122, X_123, X_132, X_133,
        X_223, X_233) over Algebraic Field with nonzero layers
          (1, 0, 0) : (X_1,)
          (0, 1, 0) : (X_2,)
          (1, 1, 0) : (X_12,)
          (1, 2, 0) : (X_122,)
          (2, 1, 0) : (X_112,)
          (0, 0, 1) : (X_3,)
          (0, 1, 1) : (X_23,)
          (0, 1, 2) : (X_233,)
          (0, 2, 1) : (X_223,)
          (1, 0, 1) : (X_13,)
          (1, 0, 2) : (X_133,)
          (1, 1, 1) : (X_123, X_132)
          (2, 0, 1) : (X_113,)
    """

    # define utilities to convert from matrices to vectors and back
    R = L.base_ring()
    n = L.dimension()
    MS = MatrixSpace(R, n, n)

    def matrix_to_vec(A):
        return vector(R, sum((list(Ar) for Ar in A.rows()), []))

    db = L.derivations_basis()

    def derivation_lincomb(vec):
        return sum((vk * dk for vk, dk in zip(vec, db)), MS.zero())

    # iteratively construct larger and larger tori of derivations
    t = []
    while True:
        # compute the centralizer of the torus in the derivation algebra
        ker = FreeModule(R, len(db))
        for der in t:
            # form the matrix of ad(der) with rows
            # the images of basis derivations
            A = matrix([matrix_to_vec(der * X - X * der) for X in db])
            ker = ker.intersection(A.left_kernel())
        cb = [derivation_lincomb(v) for v in ker.basis()]

        # check the basis of the centralizer for semisimple parts outside of t
        gl = FreeModule(R, n * n)
        t_submodule = gl.submodule([matrix_to_vec(der) for der in t])
        for A in cb:
            As, An = jordan_decomposition(A)
            if matrix_to_vec(As) not in t_submodule:
                # extend the torus by As
                t.append(As)
                break
        else:
            # no new elements found, so the torus is maximal
            break

    # compute the eigenspace intersections to get the concrete grading
    common_eigenspaces = [([], FreeModule(R, n))]
    for A in t:
        new_eigenspaces = []
        eig = A.right_eigenspaces()
        for ev, V in common_eigenspaces:
            for ew, W in eig:
                VW = V.intersection(W)
                if VW.dimension() > 0:
                    new_eigenspaces.append((ev + [ew], VW))
        common_eigenspaces = new_eigenspaces

    if not t:
        # zero dimensional maximal torus
        # the only grading is the trivial grading
        magma = AdditiveAbelianGroup([])
        layers = {magma.zero(): L.basis().list()}
        return grading(L, layers, magma=magma)

    # define a grading with layers indexed by tuples of eigenvalues
    cm = get_coercion_model()
    all_eigenvalues = sum((ev for ev, V in common_eigenspaces), [])
    k = len(common_eigenspaces[0][0])
    evR = cm.common_parent(*all_eigenvalues)
    layers = {
        tuple(ev): [L.from_vector(v) for v in V.basis()]
        for ev, V in common_eigenspaces
    }
    magma = evR.cartesian_product(*[evR] * (k - 1))
    gr = grading(L, layers, magma=magma)

    # convert to a grading over Z^k
    return gr.universal_realization()
Esempio n. 60
0
def stratification(L):
    r"""
    Return a stratification of the Lie algebra if one exists.

    INPUT:

    - ``L`` -- a Lie algebra

    OUTPUT:

    A grading of the Lie algebra `\mathfrak{g}` over the integers such
    that the layer `\mathfrak{g}_1` generates the full Lie algebra.

    EXAMPLES::

    A stratification for a free nilpotent Lie algebra is the
    one based on the length of the defining bracket::

        sage: from lie_gradings.gradings.grading import stratification
        sage: from lie_gradings.gradings.utilities import in_new_basis
        sage: L = LieAlgebra(QQ, 3, step=3)
        sage: strat = stratification(L)
        sage: strat
        Grading over Additive abelian group isomorphic to Z of Free Nilpotent
        Lie algebra on 14 generators (X_1, X_2, X_3, X_12, X_13, X_23, X_112,
        X_113, X_122, X_123, X_132, X_133, X_223, X_233) over Rational Field
        with nonzero layers
          (1) : (X_1, X_2, X_3)
          (2) : (X_12, X_13, X_23)
          (3) : (X_112, X_113, X_122, X_123, X_132, X_133, X_223, X_233)

    The main use case is when the original basis of the stratifiable Lie
    algebra is not adapted to a stratification. Consider the following
    quotient Lie algebra::

        sage: X_1, X_2, X_3 = L.basis().list()[:3]
        sage: Q = L.quotient(L[X_2, X_3])
        sage: Q
        Lie algebra quotient L/I of dimension 10 over Rational Field where
        L: Free Nilpotent Lie algebra on 14 generators (X_1, X_2, X_3, X_12, X_13, X_23, X_112, X_113, X_122, X_123, X_132, X_133, X_223, X_233) over Rational Field
        I: Ideal (X_23)

    We switch to a basis which does not define a stratification::

        sage: Y_1 = Q(X_1)
        sage: Y_2 = Q(X_2) + Q[X_1, X_2]
        sage: Y_3 = Q(X_3)
        sage: basis = [Y_1, Y_2, Y_3] + Q.basis().list()[3:]
        sage: Y_labels = ["Y_%d"%(k+1) for k in range(len(basis))]
        sage: K = in_new_basis(Q, basis,Y_labels)
        sage: K.inject_variables()
        Defining Y_1, Y_2, Y_3, Y_4, Y_5, Y_6, Y_7, Y_8, Y_9, Y_10
        sage: K[Y_2, Y_3]
        Y_9
        sage: K[[Y_1, Y_3], Y_2]
        Y_9

    We may reconstruct a stratification in the new basis without
    any knowledge of the original stratification::

        sage: stratification(K)
        Grading over Additive abelian group isomorphic to Z of Nilpotent Lie
        algebra on 10 generators (Y_1, Y_2, Y_3, Y_4, Y_5, Y_6, Y_7, Y_8, Y_9,
        Y_10) over Rational Field with nonzero layers
          (1) : (Y_1, Y_2 - Y_4, Y_3)
          (2) : (Y_4, Y_5)
          (3) : (Y_10, Y_6, Y_7, Y_8, Y_9)
        sage: K[Y_1, Y_2 - Y_4]
        Y_4
        sage: K[Y_1, Y_3]
        Y_5
        sage: K[Y_2 - Y_4, Y_3]
        0

    A non-stratifiable Lie algebra raises an error::

        sage: L = LieAlgebra(QQ, {('X_1','X_3'): {'X_4': 1},
        ....:                     ('X_1','X_4'): {'X_5': 1},
        ....:                     ('X_2','X_3'): {'X_5': 1}},
        ....:                    names='X_1,X_2,X_3,X_4,X_5')
        sage: stratification(L)
        Traceback (most recent call last):
        ...
        ValueError: Lie algebra on 5 generators (X_1, X_2, X_3, X_4,
        X_5) over Rational Field is not a stratifiable Lie algebra
    """
    lcs = L.lower_central_series(submodule=True)
    quots = [V.quotient(W) for V, W in zip(lcs, lcs[1:])]

    # find a basis adapted to the filtration by the lower central series
    adapted_basis = []
    weights = []
    for k, q in enumerate(quots):
        weights += [k + 1] * q.dimension()

        for v in q.basis():
            b = q.lift(v)
            adapted_basis.append(b)

    # define a submodule to compute structural
    # coefficients in the filtration adapted basis
    try:
        m = L.module()
    except AttributeError:
        m = FreeModule(L.base_ring(), L.dimension())
    sm = m.submodule_with_basis(adapted_basis)

    # form the linear system Ax=b of constraints from the Leibniz rule
    paramspace = [(k, h) for k in range(L.dimension())
                  for h in range(L.dimension()) if weights[h] > weights[k]]
    Arows = []
    bvec = []
    zerovec = m.zero()

    for i in range(L.dimension()):
        Y_i = adapted_basis[i]
        w_i = weights[i]
        for j in range(i + 1, L.dimension()):
            Y_j = adapted_basis[j]
            w_j = weights[j]
            Y_ij = L.bracket(Y_i, Y_j)
            c_ij = sm.coordinate_vector(Y_ij.to_vector())

            bcomp = L.zero()
            for k in range(L.dimension()):
                w_k = weights[k]
                Y_k = adapted_basis[k]
                bcomp += (w_k - w_i - w_j) * c_ij[k] * Y_k
            bv = bcomp.to_vector()

            Acomp = {}
            for k, h in paramspace:
                w_k = weights[k]
                Y_h = adapted_basis[h]
                if k == i:
                    Acomp[(k, h)] = L.bracket(Y_h, Y_j).to_vector()
                elif k == j:
                    Acomp[(k, h)] = L.bracket(Y_i, Y_h).to_vector()
                elif w_k >= w_i + w_j:
                    Acomp[(k, h)] = -c_ij[k] * Y_h

            for r in range(L.dimension()):
                Arows.append(
                    [Acomp.get((k, h), zerovec)[r] for k, h in paramspace])
                bvec.append(bv[r])

    A = matrix(L.base_ring(), Arows)
    b = vector(L.base_ring(), bvec)

    # solve the linear system Ax=b if possible
    try:
        coeffs_flat = A.solve_right(b)
    except ValueError:
        raise ValueError("%s is not a stratifiable Lie algebra" % L)

    coeffs = {(k, h): ckh for (k, h), ckh in zip(paramspace, coeffs_flat)}

    # define the matrix of the derivation determined by the solution
    # in the adapted basis
    cols = []
    for k in range(L.dimension()):
        w_k = weights[k]
        Y_k = adapted_basis[k]
        hspace = [h for (l, h) in paramspace if l == k]
        Yk_im = w_k * Y_k + sum(
            (coeffs[(k, h)] * adapted_basis[h] for h in hspace), L.zero())
        cols.append(sm.coordinate_vector(Yk_im.to_vector()))

    der = matrix(L.base_ring(), cols).transpose()

    # the layers of the stratification are V_k = ker(der - kI)
    layers = {}
    for k in range(len(quots)):
        degree = k + 1
        B = der - degree * matrix.identity(L.dimension())
        adapted_kernel = B.right_kernel()

        # convert back to the original basis
        Vk_basis = [sm.from_vector(X) for X in adapted_kernel.basis()]
        layers[(degree, )] = [
            L.from_vector(v) for v in m.submodule(Vk_basis).basis()
        ]

    return grading(L,
                   layers,
                   magma=AdditiveAbelianGroup([0]),
                   projections=True)