Example #1
0
    def __init__(self, parent, A, b=0, convert=True, check=True):
        r"""
        Create element of an affine group.

        TESTS::

            sage: G = AffineGroup(4, GF(5))
            sage: g = G.random_element()
            sage: TestSuite(g).run()
        """
        if is_Matrix(A) and A.nrows() == A.ncols() == parent.degree() + 1:
            g = A
            A = g.submatrix(0, 0, 2, 2)
            d = parent.degree()
            b = [g[i, d] for i in range(d)]
            convert = True
        if convert:
            A = parent.matrix_space()(A)
            b = parent.vector_space()(b)
        if check:
            # Note: the coercion framework expects that we raise TypeError for invalid input
            if not is_Matrix(A):
                raise TypeError('A must be a matrix')
            if not (A.parent() is parent.matrix_space()):
                raise TypeError('A must be an element of ' +
                                str(parent.matrix_space()))
            if not (b.parent() is parent.vector_space()):
                raise TypeError('b must be an element of ' +
                                str(parent.vector_space()))
            parent._element_constructor_check(A, b)
        super(AffineGroupElement, self).__init__(parent)
        self._A = A
        self._b = b
Example #2
0
    def __init__(self, A, b=0, parent=None, convert=True, check=True):
        r"""
        Create element of an affine group.

        TESTS::

            sage: G = AffineGroup(4, GF(5))
            sage: g = G.random_element()
            sage: TestSuite(g).run()
        """
        if is_Matrix(A) and A.nrows() == A.ncols() == parent.degree()+1:
            g = A
            A = g.submatrix(0,0,2,2)
            d = parent.degree()
            b = [ g[i,d] for i in range(d) ]
            convert = True
        if convert:
            A = parent.matrix_space()(A)
            b = parent.vector_space()(b)
        if check:
            # Note: the coercion framework expects that we raise TypeError for invalid input
            if not is_Matrix(A):
                raise TypeError('A must be a matrix')
            if not (A.parent() is parent.matrix_space()):
                raise TypeError('A must be an element of '+str(parent.matrix_space()))
            if not (b.parent() is parent.vector_space()):
                raise TypeError('b must be an element of '+str(parent.vector_space()))
            parent._element_constructor_check(A, b)
        super(AffineGroupElement, self).__init__(parent)
        self._A = A
        self._b = b
Example #3
0
    def __classcall_private__(self, arg0, arg1=None, names=None):
        """
        Choose the correct parent based upon input.

        TESTS:

        We check arguments with passing in an associative algebra::

            sage: cat = Algebras(QQ).WithBasis().FiniteDimensional()
            sage: C = CombinatorialFreeModule(QQ, ['x','y','z'], category=cat)
            sage: J1 = JordanAlgebra(C, names=['a','b','c'])
            sage: J2.<a,b,c> = JordanAlgebra(C)
            sage: J1 is J2
            True

        We check with passing in a symmetric bilinear form::

            sage: m = matrix([[0,1],[1,1]])
            sage: J1 = JordanAlgebra(m)
            sage: J2 = JordanAlgebra(QQ, m)
            sage: J3 = JordanAlgebra(m, QQ)
            sage: J1 is J2
            False
            sage: J2 is J3
            True
            sage: J4 = JordanAlgebra(ZZ, m)
            sage: J1 is J4
            True
            sage: m = matrix(QQ, [[0,1],[1,1]])
            sage: J1 = JordanAlgebra(m)
            sage: J1 is J2
            True
        """
        if names is not None:
            if isinstance(names, str):
                names = names.split(',')
            names = tuple(names)

        if arg1 is None:
            if not is_Matrix(arg0):
                if arg0.base_ring().characteristic() == 2:
                    raise ValueError(
                        "the base ring cannot have characteristic 2")
                return SpecialJordanAlgebra(arg0, names)
            arg0, arg1 = arg0.base_ring(), arg0
        elif is_Matrix(arg0):
            arg0, arg1 = arg1, arg0

        # arg0 is the base ring and arg1 is a matrix
        if not arg1.is_symmetric():
            raise ValueError("the bilinear form is not symmetric")

        arg1 = arg1.change_ring(arg0)  # This makes a copy
        arg1.set_immutable()
        return JordanAlgebraSymmetricBilinear(arg0, arg1, names=names)
Example #4
0
    def __classcall_private__(self, arg0, arg1=None, names=None):
        """
        Choose the correct parent based upon input.

        TESTS:

        We check arguments with passing in an associative algebra::

            sage: cat = Algebras(QQ).WithBasis().FiniteDimensional()
            sage: C = CombinatorialFreeModule(QQ, ['x','y','z'], category=cat)
            sage: J1 = JordanAlgebra(C, names=['a','b','c'])
            sage: J2.<a,b,c> = JordanAlgebra(C)
            sage: J1 is J2
            True

        We check with passing in a symmetric bilinear form::

            sage: m = matrix([[0,1],[1,1]])
            sage: J1 = JordanAlgebra(m)
            sage: J2 = JordanAlgebra(QQ, m)
            sage: J3 = JordanAlgebra(m, QQ)
            sage: J1 is J2
            False
            sage: J2 is J3
            True
            sage: J4 = JordanAlgebra(ZZ, m)
            sage: J1 is J4
            True
            sage: m = matrix(QQ, [[0,1],[1,1]])
            sage: J1 = JordanAlgebra(m)
            sage: J1 is J2
            True
        """
        if names is not None:
            if isinstance(names, str):
                names = names.split(',')
            names = tuple(names)

        if arg1 is None:
            if not is_Matrix(arg0):
                if arg0.base_ring().characteristic() == 2:
                    raise ValueError("the base ring cannot have characteristic 2")
                return SpecialJordanAlgebra(arg0, names)
            arg0, arg1 = arg0.base_ring(), arg0
        elif is_Matrix(arg0):
            arg0, arg1 = arg1, arg0

        # arg0 is the base ring and arg1 is a matrix
        if not arg1.is_symmetric():
            raise ValueError("the bilinear form is not symmetric")

        arg1 = arg1.change_ring(arg0) # This makes a copy
        arg1.set_immutable()
        return JordanAlgebraSymmetricBilinear(arg0, arg1, names=names)
Example #5
0
def from_incidence_matrix(G, M, loops=False, multiedges=False, weighted=False):
    r"""
    Fill ``G`` with the data of an incidence matrix.

    INPUT:

    - ``G`` -- a graph

    - ``M`` -- an incidence matrix

    - ``loops``, ``multiedges``, ``weighted`` (booleans) -- whether to consider
      the graph as having loops, multiple edges, or weights. Set to ``False`` by default.

    EXAMPLES::

        sage: from sage.graphs.graph_input import from_incidence_matrix
        sage: g = Graph()
        sage: from_incidence_matrix(g, graphs.PetersenGraph().incidence_matrix())
        sage: g.is_isomorphic(graphs.PetersenGraph())
        True
    """
    from sage.matrix.matrix import is_Matrix
    assert is_Matrix(M)

    oriented = any(M[pos] < 0 for pos in M.nonzero_positions(copy=False))

    positions = []
    for i in range(M.ncols()):
        NZ = M.nonzero_positions_in_column(i)
        if len(NZ) == 1:
            if oriented:
                raise ValueError(
                    "Column {} of the (oriented) incidence "
                    "matrix contains only one nonzero value".format(i))
            elif M[NZ[0], i] != 2:
                raise ValueError(
                    "Each column of a non-oriented incidence "
                    "matrix must sum to 2, but column {} does not".format(i))
            if loops is None:
                loops = True
            positions.append((NZ[0], NZ[0]))
        elif len(NZ) != 2 or \
             (oriented and not ((M[NZ[0],i] == +1 and M[NZ[1],i] == -1) or \
                                (M[NZ[0],i] == -1 and M[NZ[1],i] == +1))) or \
             (not oriented and (M[NZ[0],i] != 1 or M[NZ[1],i] != 1)):
            msg = "There must be one or two nonzero entries per column in an incidence matrix. "
            msg += "Got entries {} in column {}".format([M[j, i] for j in NZ],
                                                        i)
            raise ValueError(msg)
        else:
            positions.append(tuple(NZ))

    if weighted is None: G._weighted = False
    if multiedges is None:
        total = len(positions)
        multiedges = (len(set(positions)) < total)
    G.allow_loops(False if loops is None else loops, check=False)
    G.allow_multiple_edges(multiedges, check=False)
    G.add_vertices(range(M.nrows()))
    G.add_edges(positions)
Example #6
0
    def __rmul__(self, other):
        r"""
        Implement the action of matrices on points of hyperbolic space.

        EXAMPLES::

            sage: A = matrix(2, [0, 1, 1, 0])
            sage: A = HyperbolicPlane().UHP().get_isometry(A)
            sage: A * HyperbolicPlane().UHP().get_point(2 + I)
            Point in UHP 1/5*I + 2/5

        We also lift matrices into isometries::

            sage: B = diagonal_matrix([-1, -1, 1])
            sage: B = HyperbolicPlane().HM().get_isometry(B)
            sage: B * HyperbolicPlane().HM().get_point((0, 1, sqrt(2)))
            Point in HM (0, -1, sqrt(2))
        """
        if isinstance(other, HyperbolicIsometry):
            return other(self)
        elif is_Matrix(other):
            # TODO: Currently the __mul__ from the matrices gets called first
            #    and returns an error instead of calling this method
            A = self.parent().get_isometry(other)
            return A(self)
        else:
            raise TypeError("unsupported operand type(s) for *:"
                            "{0} and {1}".format(self, other))
Example #7
0
    def _acted_upon_(self, g, self_on_left):
        r"""
        Implements the left action of `SL_2(\ZZ)` on self.

        EXAMPLES::

            sage: g = matrix(ZZ, 2, [1,1,0,1]); g
            [1 1]
            [0 1]
            sage: g * Cusp(2,5)
            7/5
            sage: Cusp(2,5) * g
            Traceback (most recent call last):
            ...
            TypeError: unsupported operand parent(s) for '*': 'Set P^1(QQ) of all cusps' and 'Full MatrixSpace of 2 by 2 dense matrices over Integer Ring'
            sage: h = matrix(ZZ, 2, [12,3,-100,7])
            sage: h * Cusp(2,5)
            -13/55
            sage: Cusp(2,5)._acted_upon_(h, False)
            -13/55
            sage: (h*g) * Cusp(3,7) == h * (g * Cusp(3,7))
            True

            sage: cm = sage.structure.element.get_coercion_model()
            sage: cm.explain(MatrixSpace(ZZ, 2), Cusps)
            Action discovered.
                Left action by Full MatrixSpace of 2 by 2 dense matrices over Integer Ring on Set P^1(QQ) of all cusps
            Result lives in Set P^1(QQ) of all cusps
            Set P^1(QQ) of all cusps
        """
        if not self_on_left:
            if (is_Matrix(g) and g.base_ring() is ZZ
                    and g.ncols() == 2 and g.nrows() == 2):
                a, b, c, d = g.list()
                return Cusp(a*self.__a + b*self.__b, c*self.__a + d*self.__b)
Example #8
0
    def __rmul__(self, other):
        r"""
        Implement the action of matrices on points of hyperbolic space.

        EXAMPLES::

            sage: A = matrix(2, [0, 1, 1, 0])
            sage: A = HyperbolicPlane().UHP().get_isometry(A)
            sage: A * HyperbolicPlane().UHP().get_point(2 + I)
            Point in UHP 1/5*I + 2/5

        We also lift matrices into isometries::

            sage: B = diagonal_matrix([-1, -1, 1])
            sage: B = HyperbolicPlane().HM().get_isometry(B)
            sage: B * HyperbolicPlane().HM().get_point((0, 1, sqrt(2)))
            Point in HM (0, -1, sqrt(2))
        """
        if isinstance(other, HyperbolicIsometry):
            return other(self)
        elif is_Matrix(other):
            # TODO: Currently the __mul__ from the matrices gets called first
            #    and returns an error instead of calling this method
            A = self.parent().get_isometry(other)
            return A(self)
        else:
            raise TypeError("unsupported operand type(s) for *:"
                            "{0} and {1}".format(self, other))
Example #9
0
    def __call__(self, x):
        """
        EXAMPLES::
        
            sage: W = WeylGroup(['A',2])
            sage: W(1)
            [1 0 0]
            [0 1 0]
            [0 0 1]
        
        ::
        
            sage: W(2)
            Traceback (most recent call last):
            ...
            TypeError: no way to coerce element into self.

            sage: W2 = WeylGroup(['A',3])
            sage: W(1) in W2  # indirect doctest
            False

        """
        if isinstance(x, self.element_class) and x.parent() is self:
            return x
        from sage.matrix.matrix import is_Matrix
        if not (x in ZZ or is_Matrix(x)): # this should be handled by self.matrix_space()(x)
            raise TypeError, "no way to coerce element into self"
        M = self.matrix_space()(x)
        # This is really bad, especially for infinite groups!
        # TODO: compute the image of rho, compose by s_i until back in
        # the fundamental chamber. Return True iff the matrix is the identity
        g = self._element_constructor_(M)
        if not gap(g) in gap(self):
            raise TypeError, "no way to coerce element into self."
        return g
Example #10
0
def print_obj(out_stream, obj):
    """
    Print an object. This function is used internally by the displayhook.

    EXAMPLES::

        sage: import sage.misc.displayhook, sys

    For most objects, printing is done simply using their repr::

        sage: sage.misc.displayhook.print_obj(sys.stdout, 'Hello, world!')
        'Hello, world!'
        sage: sage.misc.displayhook.print_obj(sys.stdout, (1, 2, 3, 4))
        (1, 2, 3, 4)

    We demonstrate the special format for lists of matrices::

        sage: sage.misc.displayhook.print_obj(sys.stdout, \
                [matrix([[1], [2]]), matrix([[3], [4]])])
        [
        [1]  [3]
        [2], [4]
        ]
    """
    # We only apply the special formatting to lists (or tuples) where the first
    # element is a matrix. This should cover most cases.
    if isinstance(obj, (tuple, list)):
        if len(obj) > 0 and is_Matrix(obj[0]):
            if _check_tall_list_and_print(out_stream, obj):
                return
    print >>out_stream, `obj`
    def __call__(self, f, check=True, unitary=True):
        """
        Construct a homomorphism.

        .. TODO::

            Implement taking generator images and converting them to a matrix.

        EXAMPLES::

            sage: A = FiniteDimensionalAlgebra(QQ, [Matrix([1])])
            sage: B = FiniteDimensionalAlgebra(QQ, [Matrix([[1, 0], [0, 1]]), Matrix([[0, 1], [0, 0]])])
            sage: H = Hom(A, B)
            sage: H(Matrix([[1, 0]]))
            Morphism from Finite-dimensional algebra of degree 1 over Rational Field to
             Finite-dimensional algebra of degree 2 over Rational Field given by matrix
            [1 0]
        """
        if isinstance(f, FiniteDimensionalAlgebraMorphism):
            if f.parent() is self:
                return f
            if f.parent() == self:
                return FiniteDimensionalAlgebraMorphism(self, f._matrix, check, unitary)
        elif is_Matrix(f):
            return FiniteDimensionalAlgebraMorphism(self, f, check, unitary)
        try:
            from sage.matrix.constructor import Matrix
            return FiniteDimensionalAlgebraMorphism(self, Matrix(f), check, unitary)
        except Exception:
            return RingHomset_generic.__call__(self, f, check)
Example #12
0
File: cusps.py Project: drupel/sage
    def _acted_upon_(self, g, self_on_left):
        r"""
        Implements the left action of `SL_2(\ZZ)` on self.

        EXAMPLES::

            sage: g = matrix(ZZ, 2, [1,1,0,1]); g
            [1 1]
            [0 1]
            sage: g * Cusp(2,5)
            7/5
            sage: Cusp(2,5) * g
            Traceback (most recent call last):
            ...
            TypeError: unsupported operand parent(s) for '*': 'Set P^1(QQ) of all cusps' and 'Full MatrixSpace of 2 by 2 dense matrices over Integer Ring'
            sage: h = matrix(ZZ, 2, [12,3,-100,7])
            sage: h * Cusp(2,5)
            -13/55
            sage: Cusp(2,5)._acted_upon_(h, False)
            -13/55
            sage: (h*g) * Cusp(3,7) == h * (g * Cusp(3,7))
            True

            sage: cm = sage.structure.element.get_coercion_model()
            sage: cm.explain(MatrixSpace(ZZ, 2), Cusps)
            Action discovered.
                Left action by Full MatrixSpace of 2 by 2 dense matrices over Integer Ring on Set P^1(QQ) of all cusps
            Result lives in Set P^1(QQ) of all cusps
            Set P^1(QQ) of all cusps
        """
        if not self_on_left:
            if (is_Matrix(g) and g.base_ring() is ZZ
                    and g.ncols() == 2 and g.nrows() == 2):
                a, b, c, d = g.list()
                return Cusp(a*self.__a + b*self.__b, c*self.__a + d*self.__b)
    def __init__(self, A, gens=None, given_by_matrix=False):
        """
        EXAMPLES::

            sage: A = FiniteDimensionalAlgebra(GF(3), [Matrix([[1, 0], [0, 1]]), Matrix([[0, 1], [0, 0]])])
            sage: I = A.ideal(A([0,1]))
            sage: TestSuite(I).run(skip="_test_category") # Currently ideals are not using the category framework
        """
        k = A.base_ring()
        n = A.degree()
        if given_by_matrix:
            self._basis_matrix = gens
            gens = gens.rows()
        elif gens is None:
            self._basis_matrix = Matrix(k, 0, n)
        elif isinstance(gens, (list, tuple)):
            B = [
                FiniteDimensionalAlgebraIdeal(A, x).basis_matrix()
                for x in gens
            ]
            B = reduce(lambda x, y: x.stack(y), B, Matrix(k, 0, n))
            self._basis_matrix = B.echelon_form().image().basis_matrix()
        elif is_Matrix(gens):
            gens = FiniteDimensionalAlgebraElement(A, gens)
        elif isinstance(gens, FiniteDimensionalAlgebraElement):
            gens = gens.vector()
            B = Matrix([gens * b for b in A.table()])
            self._basis_matrix = B.echelon_form().image().basis_matrix()
        Ideal_generic.__init__(self, A, gens)
Example #14
0
    def __call__(self, f, check=True, unitary=True):
        """
        Construct a homomorphism.

        .. TODO::

            Implement taking generator images and converting them to a matrix.

        EXAMPLES::

            sage: A = FiniteDimensionalAlgebra(QQ, [Matrix([1])])
            sage: B = FiniteDimensionalAlgebra(QQ, [Matrix([[1, 0], [0, 1]]), Matrix([[0, 1], [0, 0]])])
            sage: H = Hom(A, B)
            sage: H(Matrix([[1, 0]]))
            Morphism from Finite-dimensional algebra of degree 1 over Rational Field to
             Finite-dimensional algebra of degree 2 over Rational Field given by matrix
            [1 0]
        """
        if isinstance(f, FiniteDimensionalAlgebraMorphism):
            if f.parent() is self:
                return f
            if f.parent() == self:
                return FiniteDimensionalAlgebraMorphism(
                    self, f._matrix, check, unitary)
        elif is_Matrix(f):
            return FiniteDimensionalAlgebraMorphism(self, f, check, unitary)
        try:
            from sage.matrix.constructor import Matrix
            return FiniteDimensionalAlgebraMorphism(self, Matrix(f), check,
                                                    unitary)
        except Exception:
            return RingHomset_generic.__call__(self, f, check)
    def _is_even_symmetric_matrix_(self, A, R=None):
        """
        Tests if a matrix is symmetric, defined over R, and has even diagonal in R.

        INPUT:
            A -- matrix

            R -- ring

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 2, [2,3,5])
            sage: A = Q.matrix()
            sage: A
            [ 4  3]
            [ 3 10]
            sage: Q._is_even_symmetric_matrix_(A)
            True
            sage: A[0,0] = 1
            sage: Q._is_even_symmetric_matrix_(A)
            False

        """
        if not is_Matrix(A):
            raise TypeError("A is not a matrix.")

        ring_coerce_test = True
        if R == None:            ## This allows us to omit the ring from the variables, and take it from the matrix
            R = A.base_ring()
            ring_coerce_test = False

        if not isinstance(R, Ring):
            raise TypeError("R is not a ring.")

        if not A.is_square():
            return False

        ## Test that the matrix is symmetric
        n = A.nrows()
        for i in range(n):
            for j in range(i+1, n):
                if A[i,j] != A[j,i]:
                    return False

        ## Test that all entries coerce to R
        if not ((A.base_ring() == R) or (ring_coerce_test == True)):
            try:
                for i in range(n):
                    for j in range(i, n):
                        x = R(A[i,j])
            except Exception:
                return False

        ## Test that the diagonal is even (if 1/2 isn't in R)
        if not R(2).is_unit():
            for i in range(n):
                if not is_even(R(A[i,i])):
                    return False

        return True
Example #16
0
    def __init__(self, A, elt=None, check=True):
        """
        TESTS::

            sage: A = FiniteDimensionalAlgebra(GF(3), [Matrix([[1,0], [0,1]]), Matrix([[0,1], [0,0]])])
            sage: A(QQ(4))
            Traceback (most recent call last):
            ...
            TypeError: elt should be a vector, a matrix, or an element of the base field

            sage: B = FiniteDimensionalAlgebra(QQ, [Matrix([[1,0], [0,1]]), Matrix([[0,1], [-1,0]])])
            sage: elt = B(Matrix([[1,1], [-1,1]])); elt
            e0 + e1
            sage: TestSuite(elt).run()
            sage: B(Matrix([[0,1], [1,0]]))
            Traceback (most recent call last):
            ...
            ValueError: matrix does not define an element of the algebra
        """
        AlgebraElement.__init__(self, A)
        k = A.base_ring()
        n = A.degree()
        if elt is None:
            self._vector = vector(k, n)
            self._matrix = Matrix(k, n)
        else:
            if isinstance(elt, int):
                elt = Integer(elt)
            elif isinstance(elt, list):
                elt = vector(elt)
            if A == elt.parent():
                self._vector = elt._vector.base_extend(k)
                self._matrix = elt._matrix.base_extend(k)
            elif k.has_coerce_map_from(elt.parent()):
                e = k(elt)
                if e == 0:
                    self._vector = vector(k, n)
                    self._matrix = Matrix(k, n)
                elif A.is_unitary():
                    self._vector = A._one * e
                    self._matrix = Matrix.identity(k, n) * e
                else:
                    raise TypeError("algebra is not unitary")
            elif is_Vector(elt):
                self._vector = elt.base_extend(k)
                self._matrix = Matrix(
                    k, sum([elt[i] * A.table()[i] for i in range(n)]))
            elif is_Matrix(elt):
                if not A.is_unitary():
                    raise TypeError("algebra is not unitary")
                self._vector = A._one * elt
                if not check or sum(
                    [self._vector[i] * A.table()[i] for i in range(n)]) == elt:
                    self._matrix = elt
                else:
                    raise ValueError(
                        "matrix does not define an element of the algebra")
            else:
                raise TypeError("elt should be a vector, a matrix, " +
                                "or an element of the base field")
Example #17
0
def print_obj(out_stream, obj):
    """
    Print an object. This function is used internally by the displayhook.

    EXAMPLES::

        sage: import sage.misc.displayhook, sys

    For most objects, printing is done simply using their repr::

        sage: sage.misc.displayhook.print_obj(sys.stdout, 'Hello, world!')
        'Hello, world!'
        sage: sage.misc.displayhook.print_obj(sys.stdout, (1, 2, 3, 4))
        (1, 2, 3, 4)

    We demonstrate the special format for lists of matrices::

        sage: sage.misc.displayhook.print_obj(sys.stdout, \
                [matrix([[1], [2]]), matrix([[3], [4]])])
        [
        [1]  [3]
        [2], [4]
        ]
    """
    # We only apply the special formatting to lists (or tuples) where the first
    # element is a matrix. This should cover most cases.
    if isinstance(obj, (tuple, list)):
        if len(obj) > 0 and is_Matrix(obj[0]):
            if _check_tall_list_and_print(out_stream, obj):
                return
    print >> out_stream, ` obj `
Example #18
0
    def __call__(self, x):
        """
        EXAMPLES::
        
            sage: W = WeylGroup(['A',2])
            sage: W(1)
            [1 0 0]
            [0 1 0]
            [0 0 1]
        
        ::
        
            sage: W(2)
            Traceback (most recent call last):
            ...
            TypeError: no way to coerce element into self.

            sage: W2 = WeylGroup(['A',3])
            sage: W(1) in W2  # indirect doctest
            False

        """
        if isinstance(x, self.element_class) and x.parent() is self:
            return x
        from sage.matrix.matrix import is_Matrix
        if not (x in ZZ or is_Matrix(x)): # this should be handled by self.matrix_space()(x)
            raise TypeError, "no way to coerce element into self"
        M = self.matrix_space()(x)
        # This is really bad, especially for infinite groups!
        # TODO: compute the image of rho, compose by s_i until back in
        # the fundamental chamber. Return True iff the matrix is the identity
        g = self._element_constructor_(M)
        if not gap(g) in gap(self):
            raise TypeError, "no way to coerce element into self."
        return g
    def __init__(self, A, gens=None, given_by_matrix=False):
        """
        EXAMPLES::

            sage: A = FiniteDimensionalAlgebra(GF(3), [Matrix([[1, 0], [0, 1]]), Matrix([[0, 1], [0, 0]])])
            sage: I = A.ideal(A([0,1]))
            sage: TestSuite(I).run(skip="_test_category") # Currently ideals are not using the category framework
        """
        k = A.base_ring()
        n = A.degree()
        if given_by_matrix:
            self._basis_matrix = gens
            gens = gens.rows()
        elif gens is None:
            self._basis_matrix = Matrix(k, 0, n)
        elif isinstance(gens, (list, tuple)):
            B = [FiniteDimensionalAlgebraIdeal(A, x).basis_matrix() for x in gens]
            B = reduce(lambda x, y: x.stack(y), B, Matrix(k, 0, n))
            self._basis_matrix = B.echelon_form().image().basis_matrix()
        elif is_Matrix(gens):
            gens = FiniteDimensionalAlgebraElement(A, gens)
        elif isinstance(gens, FiniteDimensionalAlgebraElement):
            gens = gens.vector()
            B = Matrix([gens * b for b in A.table()])
            self._basis_matrix = B.echelon_form().image().basis_matrix()
        Ideal_generic.__init__(self, A, gens)
Example #20
0
def normalize_square_matrices(matrices):
    """
    Find a common space for all matrices.

    OUTPUT:

    A list of matrices, all elements of the same matrix space.

    EXAMPLES::

        sage: from sage.groups.matrix_gps.finitely_generated import normalize_square_matrices
        sage: m1 = [[1,2],[3,4]]
        sage: m2 = [2, 3, 4, 5]
        sage: m3 = matrix(QQ, [[1/2,1/3],[1/4,1/5]])
        sage: m4 = MatrixGroup(m3).gen(0)
        sage: normalize_square_matrices([m1, m2, m3, m4])
        [
        [1 2]  [2 3]  [1/2 1/3]  [1/2 1/3]
        [3 4], [4 5], [1/4 1/5], [1/4 1/5]
        ]
    """
    deg = []
    gens = []
    for m in matrices:
        if is_MatrixGroupElement(m):
            deg.append(m.parent().degree())
            gens.append(m.matrix())
            continue
        if is_Matrix(m):
            if not m.is_square():
                raise TypeError('matrix must be square')
            deg.append(m.ncols())
            gens.append(m)
            continue
        try:
            m = list(m)
        except TypeError:
            gens.append(m)
            continue
        if isinstance(m[0], (list, tuple)):
            m = [list(_) for _ in m]
            degree = ZZ(len(m))
        else:
            degree, rem = ZZ(len(m)).sqrtrem()
            if rem != 0:
                raise ValueError(
                    'list of plain numbers must have square integer length')
        deg.append(degree)
        gens.append(matrix(degree, degree, m))
    deg = set(deg)
    if len(set(deg)) != 1:
        raise ValueError('not all matrices have the same size')
    gens = Sequence(gens, immutable=True)
    MS = gens.universe()
    if not is_MatrixSpace(MS):
        raise TypeError('all generators must be matrices')
    if MS.nrows() != MS.ncols():
        raise ValueError('matrices must be square')
    return gens
Example #21
0
def normalize_square_matrices(matrices):
    """
    Find a common space for all matrices.

    OUTPUT:

    A list of matrices, all elements of the same matrix space.

    EXAMPLES::

        sage: from sage.groups.matrix_gps.finitely_generated import normalize_square_matrices
        sage: m1 = [[1,2],[3,4]]
        sage: m2 = [2, 3, 4, 5]
        sage: m3 = matrix(QQ, [[1/2,1/3],[1/4,1/5]])
        sage: m4 = MatrixGroup(m3).gen(0)
        sage: normalize_square_matrices([m1, m2, m3, m4])
        [
        [1 2]  [2 3]  [1/2 1/3]  [1/2 1/3]
        [3 4], [4 5], [1/4 1/5], [1/4 1/5]
        ]
    """
    deg = []
    gens = []
    for m in matrices:
        if is_MatrixGroupElement(m):
            deg.append(m.parent().degree())
            gens.append(m.matrix())
            continue
        if is_Matrix(m):
            if not m.is_square():
                raise TypeError('matrix must be square')
            deg.append(m.ncols())
            gens.append(m)
            continue
        try:
            m = list(m)
        except TypeError:
            gens.append(m)
            continue
        if isinstance(m[0], (list, tuple)):
            m = [list(_) for _ in m]
            degree = ZZ(len(m))
        else:
            degree, rem = ZZ(len(m)).sqrtrem()
            if rem!=0:
                raise ValueError('list of plain numbers must have square integer length')
        deg.append(degree)
        gens.append(matrix(degree, degree, m))
    deg = set(deg)
    if len(set(deg)) != 1:
        raise ValueError('not all matrices have the same size')
    gens = Sequence(gens, immutable=True)
    MS = gens.universe()
    if not is_MatrixSpace(MS):
        raise TypeError('all generators must be matrices')
    if MS.nrows() != MS.ncols():
        raise ValueError('matrices must be square')
    return gens
Example #22
0
def from_incidence_matrix(G, M, loops=False, multiedges=False, weighted=False):
    r"""
    Fill ``G`` with the data of an incidence matrix.

    INPUT:

    - ``G`` -- a graph

    - ``M`` -- an incidence matrix

    - ``loops``, ``multiedges``, ``weighted`` (booleans) -- whether to consider
      the graph as having loops, multiple edges, or weights. Set to ``False`` by default.

    EXAMPLE::

        sage: from sage.graphs.graph_input import from_incidence_matrix
        sage: g = Graph()
        sage: from_incidence_matrix(g, graphs.PetersenGraph().incidence_matrix())
        sage: g.is_isomorphic(graphs.PetersenGraph())
        True
    """
    from sage.matrix.matrix import is_Matrix
    assert is_Matrix(M)

    oriented = any(M[pos] < 0 for pos in M.nonzero_positions(copy=False))

    positions = []
    for i in range(M.ncols()):
        NZ = M.nonzero_positions_in_column(i)
        if len(NZ) == 1:
            if oriented:
                raise ValueError("Column {} of the (oriented) incidence "
                                 "matrix contains only one nonzero value".format(i))
            elif M[NZ[0],i] != 2:
                raise ValueError("Each column of a non-oriented incidence "
                                 "matrix must sum to 2, but column {} does not".format(i))
            if loops is None:
                loops = True
            positions.append((NZ[0],NZ[0]))
        elif len(NZ) != 2 or \
             (oriented and not ((M[NZ[0],i] == +1 and M[NZ[1],i] == -1) or \
                                (M[NZ[0],i] == -1 and M[NZ[1],i] == +1))) or \
             (not oriented and (M[NZ[0],i] != 1 or M[NZ[1],i] != 1)):
            msg  = "There must be one or two nonzero entries per column in an incidence matrix. "
            msg += "Got entries {} in column {}".format([M[j,i] for j in NZ], i)
            raise ValueError(msg)
        else:
            positions.append(tuple(NZ))

    if weighted   is None: G._weighted  = False
    if multiedges is None:
        total = len(positions)
        multiedges = (len(set(positions)) < total  )
    G.allow_loops(False if loops is None else loops, check=False)
    G.allow_multiple_edges(multiedges, check=False)
    G.add_vertices(range(M.nrows()))
    G.add_edges(positions)
    def __init__(self, A, elt=None, check=True):
        """
        TESTS::

            sage: A = FiniteDimensionalAlgebra(GF(3), [Matrix([[1,0], [0,1]]), Matrix([[0,1], [0,0]])])
            sage: A(QQ(4))
            Traceback (most recent call last):
            ...
            TypeError: elt should be a vector, a matrix, or an element of the base field

            sage: B = FiniteDimensionalAlgebra(QQ, [Matrix([[1,0], [0,1]]), Matrix([[0,1], [-1,0]])])
            sage: elt = B(Matrix([[1,1], [-1,1]])); elt
            e0 + e1
            sage: TestSuite(elt).run()
            sage: B(Matrix([[0,1], [1,0]]))
            Traceback (most recent call last):
            ...
            ValueError: matrix does not define an element of the algebra
        """
        AlgebraElement.__init__(self, A)
        k = A.base_ring()
        n = A.degree()
        if elt is None:
            self._vector = vector(k, n)
            self._matrix = Matrix(k, n)
        else:
            if isinstance(elt, int):
                elt = Integer(elt)
            elif isinstance(elt, list):
                elt = vector(elt)
            if A == elt.parent():
                self._vector = elt._vector.base_extend(k)
                self._matrix = elt._matrix.base_extend(k)
            elif k.has_coerce_map_from(elt.parent()):
                e = k(elt)
                if e == 0:
                    self._vector = vector(k, n)
                    self._matrix = Matrix(k, n)
                elif A.is_unitary():
                    self._vector = A._one * e
                    self._matrix = Matrix.identity(k, n) * e
                else:
                    raise TypeError("algebra is not unitary")
            elif is_Vector(elt):
                self._vector = elt.base_extend(k)
                self._matrix = Matrix(k, sum([elt[i] * A.table()[i] for i in xrange(n)]))
            elif is_Matrix(elt):
                if not A.is_unitary():
                    raise TypeError("algebra is not unitary")
                self._vector = A._one * elt
                if not check or sum([self._vector[i]*A.table()[i] for i in xrange(n)]) == elt:
                    self._matrix = elt
                else:
                    raise ValueError("matrix does not define an element of the algebra")
            else:
                raise TypeError("elt should be a vector, a matrix, " +
                                "or an element of the base field")
Example #24
0
def from_oriented_incidence_matrix(G, M, loops=False, multiedges=False, weighted=False):
    r"""
    Fill ``G`` with the data of an *oriented* incidence matrix.

    An oriented incidence matrix is the incidence matrix of a directed graph, in
    which each non-loop edge corresponds to a `+1` and a `-1`, indicating its
    source and destination.

    INPUT:

    - ``G`` -- a :class:`DiGraph`

    - ``M`` -- an incidence matrix

    - ``loops``, ``multiedges``, ``weighted`` (booleans) -- whether to consider
      the graph as having loops, multiple edges, or weights. Set to ``False`` by default.

    EXAMPLES::

        sage: from sage.graphs.graph_input import from_oriented_incidence_matrix
        sage: g = DiGraph()
        sage: from_oriented_incidence_matrix(g, digraphs.Circuit(10).incidence_matrix())
        sage: g.is_isomorphic(digraphs.Circuit(10))
        True

    TESTS:

    Fix bug reported in :trac:`22985`::

        sage: DiGraph(matrix ([[1,0,0,1],[0,0,1,1],[0,0,1,1]]).transpose())
        Traceback (most recent call last):
        ...
        ValueError: each column represents an edge: -1 goes to 1
    """
    from sage.matrix.matrix import is_Matrix
    assert is_Matrix(M)

    positions = []
    for c in M.columns():
        NZ = c.nonzero_positions()
        if len(NZ) != 2:
            raise ValueError("There must be two nonzero entries (-1 & 1) per column.")
        L = sorted(set(c.list()))
        if L != [-1,0,1]:
            raise ValueError("each column represents an edge: -1 goes to 1")
        if c[NZ[0]] == -1:
            positions.append(tuple(NZ))
        else:
            positions.append((NZ[1],NZ[0]))
    if weighted   is None: weighted  = False
    if multiedges is None:
        total = len(positions)
        multiedges = (  len(set(positions)) < total  )
    G.allow_loops(True if loops else False,check=False)
    G.allow_multiple_edges(multiedges,check=False)
    G.add_vertices(range(M.nrows()))
    G.add_edges(positions)
Example #25
0
def from_seidel_adjacency_matrix(G, M):
    r"""
    Fill ``G`` with the data of a Seidel adjacency matrix.

    INPUT:

    - ``G`` -- a graph

    - ``M`` -- a Seidel adjacency matrix

    EXAMPLE::

        sage: from sage.graphs.graph_input import from_seidel_adjacency_matrix
        sage: g = Graph()
        sage: from_seidel_adjacency_matrix(g, graphs.PetersenGraph().seidel_adjacency_matrix())
        sage: g.is_isomorphic(graphs.PetersenGraph())
        True
    """
    from sage.matrix.matrix import is_Matrix
    from sage.rings.integer_ring import ZZ
    assert is_Matrix(M)

    if M.base_ring() != ZZ:
        try:
            M = M.change_ring(ZZ)
        except TypeError:
            raise ValueError("Graph's Seidel adjacency matrix must"+
                             " have only 0,1,-1 integer entries")

    if M.is_sparse():
        entries = set(M[i,j] for i,j in M.nonzero_positions())
    else:
        entries = set(M.list())

    if any(e <  -1 or e > 1 for e in entries):
        raise ValueError("Graph's Seidel adjacency matrix must"+
                         " have only 0,1,-1 integer entries")
    if any(i==j for i,j in M.nonzero_positions()):
        raise ValueError("Graph's Seidel adjacency matrix must"+
                         " have 0s on the main diagonal")
    if not M.is_symmetric():
        raise ValueError("Graph's Seidel adjacency matrix must"+
                         " be symmetric")
    G.add_vertices(range(M.nrows()))
    e = []
    for i,j in M.nonzero_positions():
       if i <= j and M[i,j] < 0:
                e.append((i,j))
    G.add_edges(e)
Example #26
0
def from_seidel_adjacency_matrix(G, M):
    r"""
    Fill ``G`` with the data of a Seidel adjacency matrix.

    INPUT:

    - ``G`` -- a graph

    - ``M`` -- a Seidel adjacency matrix

    EXAMPLES::

        sage: from sage.graphs.graph_input import from_seidel_adjacency_matrix
        sage: g = Graph()
        sage: from_seidel_adjacency_matrix(g, graphs.PetersenGraph().seidel_adjacency_matrix())
        sage: g.is_isomorphic(graphs.PetersenGraph())
        True
    """
    from sage.matrix.matrix import is_Matrix
    from sage.rings.integer_ring import ZZ
    assert is_Matrix(M)

    if M.base_ring() != ZZ:
        try:
            M = M.change_ring(ZZ)
        except TypeError:
            raise ValueError("Graph's Seidel adjacency matrix must" +
                             " have only 0,1,-1 integer entries")

    if M.is_sparse():
        entries = set(M[i, j] for i, j in M.nonzero_positions())
    else:
        entries = set(M.list())

    if any(e < -1 or e > 1 for e in entries):
        raise ValueError("Graph's Seidel adjacency matrix must" +
                         " have only 0,1,-1 integer entries")
    if any(i == j for i, j in M.nonzero_positions()):
        raise ValueError("Graph's Seidel adjacency matrix must" +
                         " have 0s on the main diagonal")
    if not M.is_symmetric():
        raise ValueError("Graph's Seidel adjacency matrix must" +
                         " be symmetric")
    G.add_vertices(range(M.nrows()))
    e = []
    for i, j in M.nonzero_positions():
        if i <= j and M[i, j] < 0:
            e.append((i, j))
    G.add_edges(e)
    def __init__(self,
                 k,
                 table,
                 names='e',
                 assume_associative=False,
                 category=None):
        """
        TESTS::

            sage: A = FiniteDimensionalAlgebra(QQ, [])
            sage: A
            Finite-dimensional algebra of degree 0 over Rational Field
            sage: type(A)
            <class 'sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra.FiniteDimensionalAlgebra_with_category'>
            sage: TestSuite(A).run()

            sage: B = FiniteDimensionalAlgebra(GF(7), [Matrix([1])])
            sage: B
            Finite-dimensional algebra of degree 1 over Finite Field of size 7
            sage: TestSuite(B).run()

            sage: C = FiniteDimensionalAlgebra(CC, [Matrix([[1, 0], [0, 1]]), Matrix([[0, 1], [0, 0]])])
            sage: C
            Finite-dimensional algebra of degree 2 over Complex Field with 53 bits of precision
            sage: TestSuite(C).run()

            sage: FiniteDimensionalAlgebra(GF(3), [Matrix([[1, 0], [0, 1]])])
            Traceback (most recent call last):
            ...
            ValueError: input is not a multiplication table

            sage: D.<a,b> = FiniteDimensionalAlgebra(RR, [Matrix([[1, 0], [0, 1]]), Matrix([[0, 1], [-1, 0]])])
            sage: D.gens()
            (a, b)

            sage: E = FiniteDimensionalAlgebra(QQ, [Matrix([0])])
            sage: E.gens()
            (e,)
        """
        n = len(table)
        self._table = [b.base_extend(k) for b in table]
        if not all([is_Matrix(b) and b.dimensions() == (n, n) for b in table]):
            raise ValueError("input is not a multiplication table")
        self._assume_associative = assume_associative
        # No further validity checks necessary!
        if category is None:
            category = FiniteDimensionalAlgebrasWithBasis(k)
        Algebra.__init__(self, base_ring=k, names=names, category=category)
Example #28
0
def jacobian(functions, variables):
    """
    Return the Jacobian matrix, which is the matrix of partial
    derivatives in which the i,j entry of the Jacobian matrix is the
    partial derivative diff(functions[i], variables[j]).

    EXAMPLES::

        sage: x,y = var('x,y')
        sage: g=x^2-2*x*y
        sage: jacobian(g, (x,y))
        [2*x - 2*y      -2*x]

    The Jacobian of the Jacobian should give us the "second derivative", which is the Hessian matrix::

        sage: jacobian(jacobian(g, (x,y)), (x,y))
        [ 2 -2]
        [-2  0]
        sage: g.hessian()
        [ 2 -2]
        [-2  0]

        sage: f=(x^3*sin(y), cos(x)*sin(y), exp(x))
        sage: jacobian(f, (x,y))
        [  3*x^2*sin(y)     x^3*cos(y)]
        [-sin(x)*sin(y)  cos(x)*cos(y)]
        [           e^x              0]
        sage: jacobian(f, (y,x))
        [    x^3*cos(y)   3*x^2*sin(y)]
        [ cos(x)*cos(y) -sin(x)*sin(y)]
        [             0            e^x]

    """
    if is_Matrix(functions) and (functions.nrows() == 1
                                 or functions.ncols() == 1):
        functions = functions.list()
    elif not (isinstance(functions, (tuple, list)) or is_Vector(functions)):
        functions = [functions]

    if not isinstance(variables, (tuple, list)) and not is_Vector(variables):
        variables = [variables]

    return matrix([[diff(f, v) for v in variables] for f in functions])
Example #29
0
    def __init__(self, parent, M, check=True, convert=True):
        r"""
        Element of a matrix group over a generic ring.

        The group elements are implemented as Sage matrices.

        INPUT:

        - ``M`` -- a matrix.

        - ``parent`` -- the parent.

        - ``check`` -- bool (default: ``True``). If true does some
          type checking.

        - ``convert`` -- bool (default: ``True``). If true convert
          ``M`` to the right matrix space.

        TESTS::

            sage: MS = MatrixSpace(GF(3),2,2)
            sage: G = MatrixGroup(MS([[1,0],[0,1]]), MS([[1,1],[0,1]]))
            sage: G.gen(0)
            [1 0]
            [0 1]
            sage: g = G.random_element()
            sage: TestSuite(g).run()
        """
        if isinstance(M, GapElement):
            ElementLibGAP.__init__(self, parent, M)
            return
        if convert:
            M = parent.matrix_space()(M)
        from sage.libs.gap.libgap import libgap

        M_gap = libgap(M)
        if check:
            if not is_Matrix(M):
                raise TypeError("M must be a matrix")
            if M.parent() is not parent.matrix_space():
                raise TypeError("M must be a in the matrix space of the group")
            parent._check_matrix(M, M_gap)
        ElementLibGAP.__init__(self, parent, M_gap)
    def __init__(self, k, table, names='e', assume_associative=False, category=None):
        """
        TESTS::

            sage: A = FiniteDimensionalAlgebra(QQ, [])
            sage: A
            Finite-dimensional algebra of degree 0 over Rational Field
            sage: type(A)
            <class 'sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra.FiniteDimensionalAlgebra_with_category'>
            sage: TestSuite(A).run()

            sage: B = FiniteDimensionalAlgebra(GF(7), [Matrix([1])])
            sage: B
            Finite-dimensional algebra of degree 1 over Finite Field of size 7
            sage: TestSuite(B).run()

            sage: C = FiniteDimensionalAlgebra(CC, [Matrix([[1, 0], [0, 1]]), Matrix([[0, 1], [0, 0]])])
            sage: C
            Finite-dimensional algebra of degree 2 over Complex Field with 53 bits of precision
            sage: TestSuite(C).run()

            sage: FiniteDimensionalAlgebra(GF(3), [Matrix([[1, 0], [0, 1]])])
            Traceback (most recent call last):
            ...
            ValueError: input is not a multiplication table

            sage: D.<a,b> = FiniteDimensionalAlgebra(RR, [Matrix([[1, 0], [0, 1]]), Matrix([[0, 1], [-1, 0]])])
            sage: D.gens()
            (a, b)

            sage: E = FiniteDimensionalAlgebra(QQ, [Matrix([0])])
            sage: E.gens()
            (e,)
        """
        n = len(table)
        self._table = [b.base_extend(k) for b in table]
        if not all([is_Matrix(b) and b.dimensions() == (n, n) for b in table]):
            raise ValueError("input is not a multiplication table")
        self._assume_associative = assume_associative
        # No further validity checks necessary!
        if category is None:
            category = FiniteDimensionalAlgebrasWithBasis(k)
        Algebra.__init__(self, base_ring=k, names=names, category=category)
Example #31
0
    def __init__(self, parent, A):
        r"""
        Initialise an element from a matrix. This *must* be over the base ring
        of self and have the right size.

        This is a bit overkill as similar checks will be performed by the call
        and coerce methods of the parent of self, but it can't hurt to be
        paranoid. Any fancy coercion / base_extension / etc happens there, not
        here.

        TESTS::

            sage: T = ModularForms(Gamma0(7), 4).hecke_algebra()
            sage: M = sage.modular.hecke.hecke_operator.HeckeAlgebraElement_matrix(T, matrix(QQ,3,[2,3,0,1,2,3,7,8,9])); M
            Hecke operator on Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(7) of weight 4 over Rational Field defined by:
            [2 3 0]
            [1 2 3]
            [7 8 9]
            sage: loads(dumps(M)) == M
            True
            sage: sage.modular.hecke.hecke_operator.HeckeAlgebraElement_matrix(T, matrix(Integers(2),3,[2,3,0,1,2,3,7,8,9]))
            Traceback (most recent call last):
            ...
            TypeError: base ring of matrix (Ring of integers modulo 2) does not match base ring of space (Rational Field)
            sage: sage.modular.hecke.hecke_operator.HeckeAlgebraElement_matrix(T, matrix(QQ,2,[2,3,0,1]))
            Traceback (most recent call last):
            ...
            TypeError: A must be a square matrix of rank 3
        """
        HeckeAlgebraElement.__init__(self, parent)
        from sage.matrix.matrix import is_Matrix

        if not is_Matrix(A):
            raise TypeError("A must be a matrix")
        if not A.base_ring() == self.parent().base_ring():
            raise TypeError, "base ring of matrix (%s) does not match base ring of space (%s)" % (
                A.base_ring(),
                self.parent().base_ring(),
            )
        if not A.nrows() == A.ncols() == self.parent().module().rank():
            raise TypeError, "A must be a square matrix of rank %s" % self.parent().module().rank()
        self.__matrix = A
Example #32
0
def jacobian(functions, variables):
    """
    Return the Jacobian matrix, which is the matrix of partial
    derivatives in which the i,j entry of the Jacobian matrix is the
    partial derivative diff(functions[i], variables[j]).

    EXAMPLES::

        sage: x,y = var('x,y')
        sage: g=x^2-2*x*y
        sage: jacobian(g, (x,y))
        [2*x - 2*y      -2*x]

    The Jacobian of the Jacobian should give us the "second derivative", which is the Hessian matrix::

        sage: jacobian(jacobian(g, (x,y)), (x,y))
        [ 2 -2]
        [-2  0]
        sage: g.hessian()
        [ 2 -2]
        [-2  0]

        sage: f=(x^3*sin(y), cos(x)*sin(y), exp(x))
        sage: jacobian(f, (x,y))
        [  3*x^2*sin(y)     x^3*cos(y)]
        [-sin(x)*sin(y)  cos(x)*cos(y)]
        [           e^x              0]
        sage: jacobian(f, (y,x))
        [    x^3*cos(y)   3*x^2*sin(y)]
        [ cos(x)*cos(y) -sin(x)*sin(y)]
        [             0            e^x]

    """
    if is_Matrix(functions) and (functions.nrows()==1 or functions.ncols()==1):
        functions = functions.list()
    elif not (isinstance(functions, (tuple, list)) or is_Vector(functions)):
        functions = [functions]

    if not isinstance(variables, (tuple, list)) and not is_Vector(variables):
        variables = [variables]

    return matrix([[diff(f, v) for v in variables] for f in functions])
Example #33
0
    def __init__(self, M, parent, check=True, convert=True):
        r"""
        Element of a matrix group over a generic ring.

        The group elements are implemented as Sage matrices.

        INPUT:

        - ``M`` -- a matrix.

        - ``parent`` -- the parent.

        - ``check`` -- bool (default: ``True``). If true does some
          type checking.

        - ``convert`` -- bool (default: ``True``). If true convert
          ``M`` to the right matrix space.

        TESTS::

            sage: MS = MatrixSpace(GF(3),2,2)
            sage: G = MatrixGroup(MS([[1,0],[0,1]]), MS([[1,1],[0,1]]))
            sage: G.gen(0)
            [1 0]
            [0 1]
            sage: g = G.random_element()
            sage: TestSuite(g).run()
        """
        if isinstance(M, GapElement):
            ElementLibGAP.__init__(self, M, parent)
            return
        if convert:
            M = parent.matrix_space()(M)
        from sage.libs.gap.libgap import libgap
        M_gap = libgap(M)
        if check:
            if not is_Matrix(M):
                raise TypeError('M must be a matrix')
            if M.parent() is not parent.matrix_space():
                raise TypeError('M must be a in the matrix space of the group')
            parent._check_matrix(M, M_gap)
        ElementLibGAP.__init__(self, M_gap, parent)
Example #34
0
    def __init__(self, parent, A):
        r"""
        Initialise an element from a matrix. This *must* be over the base ring
        of self and have the right size.

        This is a bit overkill as similar checks will be performed by the call
        and coerce methods of the parent of self, but it can't hurt to be
        paranoid. Any fancy coercion / base_extension / etc happens there, not
        here.

        TESTS::

            sage: T = ModularForms(Gamma0(7), 4).hecke_algebra()
            sage: M = sage.modular.hecke.hecke_operator.HeckeAlgebraElement_matrix(T, matrix(QQ,3,[2,3,0,1,2,3,7,8,9])); M
            Hecke operator on Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(7) of weight 4 over Rational Field defined by:
            [2 3 0]
            [1 2 3]
            [7 8 9]
            sage: loads(dumps(M)) == M
            True
            sage: sage.modular.hecke.hecke_operator.HeckeAlgebraElement_matrix(T, matrix(Integers(2),3,[2,3,0,1,2,3,7,8,9]))
            Traceback (most recent call last):
            ...
            TypeError: base ring of matrix (Ring of integers modulo 2) does not match base ring of space (Rational Field)
            sage: sage.modular.hecke.hecke_operator.HeckeAlgebraElement_matrix(T, matrix(QQ,2,[2,3,0,1]))
            Traceback (most recent call last):
            ...
            TypeError: A must be a square matrix of rank 3
        """
        HeckeAlgebraElement.__init__(self, parent)
        from sage.matrix.matrix import is_Matrix
        if not is_Matrix(A):
            raise TypeError("A must be a matrix")
        if not A.base_ring() == self.parent().base_ring():
            raise TypeError(
                "base ring of matrix (%s) does not match base ring of space (%s)"
                % (A.base_ring(), self.parent().base_ring()))
        if not A.nrows() == A.ncols() == self.parent().module().rank():
            raise TypeError("A must be a square matrix of rank %s" %
                            self.parent().module().rank())
        self.__matrix = A
Example #35
0
def is_generalized_cartan_matrix(M):
    """
    Return ``True`` if ``M`` is a generalized Cartan matrix. For a definition
    of a generalized Cartan matrix, see :class:`CartanMatrix`.

    EXAMPLES::

        sage: from sage.combinat.root_system.cartan_matrix import is_generalized_cartan_matrix
        sage: M = matrix([[2,-1,-2], [-1,2,-1], [-2,-1,2]])
        sage: is_generalized_cartan_matrix(M)
        True
        sage: M = matrix([[2,-1,-2], [-1,2,-1], [0,-1,2]])
        sage: is_generalized_cartan_matrix(M)
        False
        sage: M = matrix([[1,-1,-2], [-1,2,-1], [-2,-1,2]])
        sage: is_generalized_cartan_matrix(M)
        False

    A non-symmetrizable example::

        sage: M = matrix([[2,-1,-2], [-1,2,-1], [-1,-1,2]])
        sage: is_generalized_cartan_matrix(M)
        True
    """
    if not is_Matrix(M):
        return False
    if not M.is_square():
        return False
    n = M.ncols()
    for i in xrange(n):
        if M[i,i] != 2:
            return False
        for j in xrange(i+1, n):
            if M[i,j] > 0 or M[j,i] > 0:
                return False
            elif M[i,j] == 0 and M[j,i] != 0:
                return False
            elif M[j,i] == 0 and M[i,j] != 0:
                return False
    return True
Example #36
0
def is_generalized_cartan_matrix(M):
    """
    Return ``True`` if ``M`` is a generalized Cartan matrix. For a definition
    of a generalized Cartan matrix, see :class:`CartanMatrix`.

    EXAMPLES::

        sage: from sage.combinat.root_system.cartan_matrix import is_generalized_cartan_matrix
        sage: M = matrix([[2,-1,-2], [-1,2,-1], [-2,-1,2]])
        sage: is_generalized_cartan_matrix(M)
        True
        sage: M = matrix([[2,-1,-2], [-1,2,-1], [0,-1,2]])
        sage: is_generalized_cartan_matrix(M)
        False
        sage: M = matrix([[1,-1,-2], [-1,2,-1], [-2,-1,2]])
        sage: is_generalized_cartan_matrix(M)
        False

    A non-symmetrizable example::

        sage: M = matrix([[2,-1,-2], [-1,2,-1], [-1,-1,2]])
        sage: is_generalized_cartan_matrix(M)
        True
    """
    if not is_Matrix(M):
        return False
    if not M.is_square():
        return False
    n = M.ncols()
    for i in xrange(n):
        if M[i,i] != 2:
            return False
        for j in xrange(i+1, n):
            if M[i,j] > 0 or M[j,i] > 0:
                return False
            elif M[i,j] == 0 and M[j,i] != 0:
                return False
            elif M[j,i] == 0 and M[i,j] != 0:
                return False
    return True
Example #37
0
    def __init__(self, parent, M, check=True, convert=True):
        r"""
        Element of a matrix group over a generic ring.

        The group elements are implemented as Sage matrices.

        INPUT:

        - ``M`` -- a matrix.

        - ``parent`` -- the parent.

        - ``check`` -- bool (default: ``True``). If true does some
           type checking.

        - ``convert`` -- bool (default: ``True``). If true convert
          ``M`` to the right matrix space.

        TESTS::

            sage: F = GF(3); MS = MatrixSpace(F,2,2)
            sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])]
            sage: G = MatrixGroup(gens)
            sage: g = G.random_element()
            sage: TestSuite(g).run()
        """
        if convert:
            M = parent.matrix_space()(M)
        if check:
            if not is_Matrix(M):
                raise TypeError("M must be a matrix")
            if M.parent() is not parent.matrix_space():
                raise TypeError("M must be a in the matrix space of the group")
            parent._check_matrix(M)
        super(MatrixGroupElement_generic, self).__init__(parent)
        if M.is_immutable():
            self._matrix = M
        else:
            self._matrix = M.__copy__()
            self._matrix.set_immutable()
Example #38
0
    def __init__(self, M, parent, check=True, convert=True):
        r"""
        Element of a matrix group over a generic ring.

        The group elements are implemented as Sage matrices.

        INPUT:

        - ``M`` -- a matrix.

        - ``parent`` -- the parent.

        - ``check`` -- bool (default: ``True``). If true does some
           type checking.

        - ``convert`` -- bool (default: ``True``). If true convert
          ``M`` to the right matrix space.

        TESTS::

            sage: F = GF(3); MS = MatrixSpace(F,2,2)
            sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])]
            sage: G = MatrixGroup(gens)
            sage: g = G.random_element()
            sage: TestSuite(g).run()
        """
        if convert:
            M = parent.matrix_space()(M)
        if check:
            if not is_Matrix(M):
                raise TypeError('M must be a matrix')
            if M.parent() is not parent.matrix_space():
                raise TypeError('M must be a in the matrix space of the group')
            parent._check_matrix(M)
        super(MatrixGroupElement_generic, self).__init__(parent)
        if M.is_immutable():
            self._matrix = M
        else:
            self._matrix = M.__copy__()
            self._matrix.set_immutable()
Example #39
0
    def write_matrix(self, mat, filename):
        r"""
        Write the matrix ``mat`` to the file ``filename`` in 4ti2 format.

        INPUT:

        - ``mat`` -- A matrix of integers or something that can be
          converted to that.
        - ``filename`` -- A file name not including a path.

        EXAMPLES::

            sage: from sage.interfaces.four_ti_2 import four_ti_2
            sage: four_ti_2.write_matrix([[1,2],[3,4]], "test_file")
        """
        from sage.matrix.constructor import matrix
        from sage.matrix.matrix import is_Matrix
        if not is_Matrix(mat):
            mat = matrix(ZZ, mat)
        if mat.base_ring() != ZZ:
            mat = mat.change_ring(ZZ)

        self.write_array(mat, mat.nrows(), mat.ncols(), filename)
Example #40
0
    def write_matrix(self, mat, filename):
        r"""
        Write the matrix ``mat`` to the file ``filename`` in 4ti2 format.

        INPUT:

        - ``mat`` - A matrix of integers or something that can be
           converted to that.
        - ``filename`` - A file name not including a path.

        EXAMPLES::

            sage: from sage.interfaces.four_ti_2 import four_ti_2
            sage: four_ti_2.write_matrix([[1,2],[3,4]], "test_file")
        """
        from sage.matrix.constructor import matrix
        from sage.matrix.matrix import is_Matrix
        if not is_Matrix(mat):
            mat = matrix(ZZ, mat)
        if mat.base_ring() != ZZ:
            mat = mat.change_ring(ZZ)

        self.write_array(mat, mat.nrows(), mat.ncols(), filename)
def DynkinDiagram(*args, **kwds):
    r"""
    Return the Dynkin diagram corresponding to the input.

    INPUT:

    The input can be one of the following:

    - empty to obtain an empty Dynkin diagram
    - a Cartan type
    - a Cartan matrix
    - a Cartan matrix and an indexing set

    Also one can input an index_set by

    The edge multiplicities are encoded as edge labels. This uses the
    convention in Hong and Kang, Kac, Fulton Harris, and crystals. This is the
    **opposite** convention in Bourbaki and Wikipedia's Dynkin diagram
    (:wikipedia:`Dynkin_diagram`). That is for `i \neq j`::

       i <--k-- j <==> a_ij = -k
                  <==> -scalar(coroot[i], root[j]) = k
                  <==> multiple arrows point from the longer root
                       to the shorter one

    For example, in type `C_2`, we have::

        sage: C2 = DynkinDiagram(['C',2]); C2
        O=<=O
        1   2
        C2
        sage: C2.cartan_matrix()
        [ 2 -2]
        [-1  2]

    However Bourbaki would have the Cartan matrix as:

    .. MATH::

        \begin{bmatrix}
        2 & -1 \\
        -2 & 2
        \end{bmatrix}.

    EXAMPLES::

        sage: DynkinDiagram(['A', 4])
        O---O---O---O
        1   2   3   4
        A4

        sage: DynkinDiagram(['A',1],['A',1])
        O
        1
        O
        2
        A1xA1

        sage: R = RootSystem("A2xB2xF4")
        sage: DynkinDiagram(R)
        O---O
        1   2
        O=>=O
        3   4
        O---O=>=O---O
        5   6   7   8
        A2xB2xF4

        sage: R = RootSystem("A2xB2xF4")
        sage: CM = R.cartan_matrix(); CM
        [ 2 -1| 0  0| 0  0  0  0]
        [-1  2| 0  0| 0  0  0  0]
        [-----+-----+-----------]
        [ 0  0| 2 -1| 0  0  0  0]
        [ 0  0|-2  2| 0  0  0  0]
        [-----+-----+-----------]
        [ 0  0| 0  0| 2 -1  0  0]
        [ 0  0| 0  0|-1  2 -1  0]
        [ 0  0| 0  0| 0 -2  2 -1]
        [ 0  0| 0  0| 0  0 -1  2]
        sage: DD = DynkinDiagram(CM); DD
        O---O
        1   2
        O=>=O
        3   4
        O---O=>=O---O
        5   6   7   8
        A2xB2xF4
        sage: DD.cartan_matrix()
        [ 2 -1  0  0  0  0  0  0]
        [-1  2  0  0  0  0  0  0]
        [ 0  0  2 -1  0  0  0  0]
        [ 0  0 -2  2  0  0  0  0]
        [ 0  0  0  0  2 -1  0  0]
        [ 0  0  0  0 -1  2 -1  0]
        [ 0  0  0  0  0 -2  2 -1]
        [ 0  0  0  0  0  0 -1  2]

    We can also create Dynkin diagrams from arbitrary Cartan matrices::

        sage: C = CartanMatrix([[2, -3], [-4, 2]])
        sage: DynkinDiagram(C)
        Dynkin diagram of rank 2
        sage: C.index_set()
        (0, 1)
        sage: CI = CartanMatrix([[2, -3], [-4, 2]], [3, 5])
        sage: DI = DynkinDiagram(CI)
        sage: DI.index_set()
        (3, 5)
        sage: CII = CartanMatrix([[2, -3], [-4, 2]])
        sage: DII = DynkinDiagram(CII, ('y', 'x'))
        sage: DII.index_set()
        ('x', 'y')

    .. SEEALSO::

        :func:`CartanType` for a general discussion on Cartan
        types and in particular node labeling conventions.

    TESTS:

    Check that :trac:`15277` is fixed by not having edges from 0's::

        sage: CM = CartanMatrix([[2,-1,0,0],[-3,2,-2,-2],[0,-1,2,-1],[0,-1,-1,2]])
        sage: CM
        [ 2 -1  0  0]
        [-3  2 -2 -2]
        [ 0 -1  2 -1]
        [ 0 -1 -1  2]
        sage: CM.dynkin_diagram().edges()
        [(0, 1, 3),
         (1, 0, 1),
         (1, 2, 1),
         (1, 3, 1),
         (2, 1, 2),
         (2, 3, 1),
         (3, 1, 2),
         (3, 2, 1)]
    """
    if len(args) == 0:
        return DynkinDiagram_class()
    mat = args[0]
    if is_Matrix(mat):
        mat = CartanMatrix(*args)
    if isinstance(mat, CartanMatrix):
        if mat.cartan_type() is not mat:
            try:
                return mat.cartan_type().dynkin_diagram()
            except AttributeError:
                ct = CartanType(*args)
                raise ValueError(
                    "Dynkin diagram data not yet hardcoded for type %s" % ct)
        if len(args) > 1:
            index_set = tuple(args[1])
        elif "index_set" in kwds:
            index_set = tuple(kwds["index_set"])
        else:
            index_set = mat.index_set()
        D = DynkinDiagram_class(index_set=index_set)
        for (i, j) in mat.nonzero_positions():
            if i != j:
                D.add_edge(index_set[i], index_set[j], -mat[j, i])
        return D
    ct = CartanType(*args)
    try:
        return ct.dynkin_diagram()
    except AttributeError:
        raise ValueError("Dynkin diagram data not yet hardcoded for type %s" %
                         ct)
Example #42
0
def matrix_plot(mat, **options):
    r"""
    A plot of a given matrix or 2D array.

    If the matrix is dense, each matrix element is given a different
    color value depending on its relative size compared to the other
    elements in the matrix.  If the matrix is sparse, colors only
    indicate whether an element is nonzero or zero, so the plot
    represents the sparsity pattern of the matrix.

    The tick marks drawn on the frame axes denote the row numbers
    (vertical ticks) and the column numbers (horizontal ticks) of the
    matrix.

    INPUT:

    - ``mat`` - a 2D matrix or array

    The following input must all be passed in as named parameters, if
    default not used:

    - ``cmap`` - a colormap (default: 'gray'), the name of
      a predefined colormap, a list of colors,
      or an instance of a matplotlib Colormap.
      Type: ``import matplotlib.cm; matplotlib.cm.datad.keys()``
      for available colormap names.

    - ``colorbar`` -- boolean (default: False) Show a colorbar or not (dense matrices only).

      The following options are used to adjust the style and placement
      of colorbars.  They have no effect if a colorbar is not shown.

      - ``colorbar_orientation`` -- string (default: 'vertical'),
        controls placement of the colorbar, can be either 'vertical'
        or 'horizontal'

      - ``colorbar_format`` -- a format string, this is used to format
        the colorbar labels.

      - ``colorbar_options`` -- a dictionary of options for the matplotlib
        colorbar API.  Documentation for the :mod:`matplotlib.colorbar` module
        has details.

    - ``norm`` - If None (default), the value range is scaled to the interval
      [0,1].  If 'value', then the actual value is used with no
      scaling.  A :class:`matplotlib.colors.Normalize` instance may
      also passed.

    - ``vmin`` - The minimum value (values below this are set to this value)

    - ``vmax`` - The maximum value (values above this are set to this value)

    - ``origin`` - If 'upper' (default), the first row of the matrix
      is on the top of the graph.  If 'lower', the first row is on the
      bottom of the graph.

    - ``subdivisions`` - If True, plot the subdivisions of the matrix as lines.

    - ``subdivision_boundaries`` - a list of lists in the form
      ``[row_subdivisions, column_subdivisions]``, which specifies
      the row and column subdivisions to use.  If not specified,
      defaults to the matrix subdivisions

    - ``subdivision_style`` - a dictionary of properties passed
      on to the :func:`~sage.plot.line.line2d` command for plotting
      subdivisions.  If this is a two-element list or tuple, then it
      specifies the styles of row and column divisions, respectively.

    EXAMPLES:

    A matrix over `\ZZ` colored with different grey levels::

        sage: matrix_plot(matrix([[1,3,5,1],[2,4,5,6],[1,3,5,7]]))

    Here we make a random matrix over `\RR` and use ``cmap='hsv'``
    to color the matrix elements different RGB colors::

        sage: matrix_plot(random_matrix(RDF, 50), cmap='hsv')

    By default, entries are scaled to the interval [0,1] before
    determining colors from the color map.  That means the two plots
    below are the same::

        sage: P = matrix_plot(matrix(2,[1,1,3,3]))
        sage: Q = matrix_plot(matrix(2,[2,2,3,3]))
        sage: P; Q

    However, we can specify which values scale to 0 or 1 with the
    ``vmin`` and ``vmax`` parameters (values outside the range are
    clipped).  The two plots below are now distinguished::

        sage: P = matrix_plot(matrix(2,[1,1,3,3]), vmin=0, vmax=3, colorbar=True)
        sage: Q = matrix_plot(matrix(2,[2,2,3,3]), vmin=0, vmax=3, colorbar=True)
        sage: P; Q

    We can also specify a norm function of 'value', which means that
    there is no scaling performed::

        sage: matrix_plot(random_matrix(ZZ,10)*.05, norm='value', colorbar=True)

    Matrix subdivisions can be plotted as well::

        sage: m=random_matrix(RR,10)
        sage: m.subdivide([2,4],[6,8])
        sage: matrix_plot(m, subdivisions=True, subdivision_style=dict(color='red',thickness=3))

    You can also specify your own subdivisions and separate styles
    for row or column subdivisions::

        sage: m=random_matrix(RR,10)
        sage: matrix_plot(m, subdivisions=True, subdivision_boundaries=[[2,4],[6,8]], subdivision_style=[dict(color='red',thickness=3),dict(linestyle='--',thickness=6)])

    Generally matrices are plotted with the (0,0) entry in the upper
    left.  However, sometimes if we are plotting an image, we'd like
    the (0,0) entry to be in the lower left.  We can do that with the
    ``origin`` argument::

        sage: matrix_plot(identity_matrix(100), origin='lower')

    Another random plot, but over `\GF{389}`::

        sage: m = random_matrix(GF(389), 10)
        sage: matrix_plot(m, cmap='Oranges')

    It also works if you lift it to the polynomial ring::

        sage: matrix_plot(m.change_ring(GF(389)['x']), cmap='Oranges')

    We have several options for colorbars::

        sage: matrix_plot(random_matrix(RDF, 50), colorbar=True, colorbar_orientation='horizontal')

    ::

        sage: matrix_plot(random_matrix(RDF, 50), colorbar=True, colorbar_format='%.3f')

    The length of a color bar and the length of the adjacent
    matrix plot dimension may be quite different.  This example
    shows how to adjust the length of the colorbar by passing a
    dictionary of options to the matplotlib colorbar routines.  ::

        sage: m = random_matrix(ZZ, 40, 80, x=-10, y=10)
        sage: m.plot(colorbar=True, colorbar_orientation='vertical',
        ...          colorbar_options={'shrink':0.50})

    Here we plot a random sparse matrix::

        sage: sparse = matrix(dict([((randint(0, 10), randint(0, 10)), 1) for i in xrange(100)]))
        sage: matrix_plot(sparse)

    ::

        sage: A=random_matrix(ZZ,100000,density=.00001,sparse=True)
        sage: matrix_plot(A,marker=',')

    As with dense matrices, sparse matrix entries are automatically
    converted to floating point numbers before plotting.  Thus the
    following works::

        sage: b=random_matrix(GF(2),200,sparse=True,density=0.01)
        sage: matrix_plot(b)

    While this returns an error::

        sage: b=random_matrix(CDF,200,sparse=True,density=0.01)
        sage: matrix_plot(b)
        Traceback (most recent call last):
        ...
        ValueError: can not convert entries to floating point numbers

    To plot the absolute value of a complex matrix, use the
    ``apply_map`` method::

        sage: b=random_matrix(CDF,200,sparse=True,density=0.01)
        sage: matrix_plot(b.apply_map(abs))

    Plotting lists of lists also works::

        sage: matrix_plot([[1,3,5,1],[2,4,5,6],[1,3,5,7]])

    As does plotting of NumPy arrays::

        sage: import numpy
        sage: matrix_plot(numpy.random.rand(10, 10))

    A plot title can be added to the matrix plot.::

        sage: matrix_plot(identity_matrix(50), origin='lower', title='not identity')

    The title position is adjusted upwards if the ``origin`` keyword is set
    to ``"upper"`` (this is the default).::

        sage: matrix_plot(identity_matrix(50), title='identity')

    TESTS::

        sage: P.<t> = RR[]
        sage: matrix_plot(random_matrix(P, 3, 3))
        Traceback (most recent call last):
        ...
        TypeError: cannot coerce nonconstant polynomial to float

    ::

        sage: matrix_plot([1,2,3])
        Traceback (most recent call last):
        ...
        TypeError: mat must be a Matrix or a two dimensional array

    ::

        sage: matrix_plot([[sin(x), cos(x)], [1, 0]])
        Traceback (most recent call last):
        ...
        TypeError: mat must be a Matrix or a two dimensional array

    Test that sparse matrices also work with subdivisions::

        sage: matrix_plot(sparse, subdivisions=True, subdivision_boundaries=[[2,4],[6,8]])
    """
    import numpy as np
    import scipy.sparse as scipysparse
    from sage.plot.all import Graphics
    from sage.matrix.matrix import is_Matrix
    from sage.rings.all import RDF
    orig_mat = mat
    if is_Matrix(mat):
        sparse = mat.is_sparse()
        if sparse:
            entries = list(mat._dict().items())
            try:
                data = np.asarray([d for _, d in entries], dtype=float)
            except StandardError:
                raise ValueError, "can not convert entries to floating point numbers"
            positions = np.asarray([[row for (row, col), _ in entries],
                                    [col for (row, col), _ in entries]],
                                   dtype=int)
            mat = scipysparse.coo_matrix((data, positions),
                                         shape=(mat.nrows(), mat.ncols()))
        else:
            mat = mat.change_ring(RDF).numpy()
    elif hasattr(mat, 'tocoo'):
        sparse = True
    else:
        sparse = False

    try:
        if sparse:
            xy_data_array = mat
        else:
            xy_data_array = np.asarray(mat, dtype=float)
    except TypeError:
        raise TypeError, "mat must be a Matrix or a two dimensional array"
    except ValueError:
        raise ValueError, "can not convert entries to floating point numbers"

    if len(xy_data_array.shape) < 2:
        raise TypeError, "mat must be a Matrix or a two dimensional array"

    xrange = (0, xy_data_array.shape[1])
    yrange = (0, xy_data_array.shape[0])

    if options['subdivisions'] and options['subdivision_options'][
            'boundaries'] is None:
        options['subdivision_options'][
            'boundaries'] = orig_mat.get_subdivisions()

    # Custom position the title. Otherwise it overlaps with tick labels
    if options['origin'] == 'upper' and 'title_pos' not in options:
        options['title_pos'] = (0.5, 1.05)

    g = Graphics()
    g._set_extra_kwds(Graphics._extract_kwds_for_show(options))
    g.add_primitive(MatrixPlot(xy_data_array, xrange, yrange, options))
    return g
Example #43
0
    def _element_constructor_(self, x, check=True):
        """
        Construct a scheme morphism.

        INPUT:

        - `x` -- anything that defines a morphism of toric
          varieties. A matrix, fan morphism, or a list or tuple of
          homogeneous polynomials that define a morphism.

        - ``check`` -- boolean (default: ``True``) passed onto
          functions called by this to be more careful about input
          argument type checking

        OUTPUT:

        The morphism of toric varieties determined by ``x``.
        
        EXAMPLES:

        First, construct from fan morphism::

            sage: dP8.<t,x0,x1,x2> = toric_varieties.dP8()
            sage: P2.<y0,y1,y2> = toric_varieties.P2()
            sage: hom_set = dP8.Hom(P2)

            sage: fm = FanMorphism(identity_matrix(2), dP8.fan(), P2.fan())
            sage: hom_set(fm)     # calls hom_set._element_constructor_()
            Scheme morphism:
              From: 2-d CPR-Fano toric variety covered by 4 affine patches
              To:   2-d CPR-Fano toric variety covered by 3 affine patches
              Defn: Defined by sending Rational polyhedral fan in 2-d lattice N
                    to Rational polyhedral fan in 2-d lattice N.

        A matrix will automatically be converted to a fan morphism::

            sage: hom_set(identity_matrix(2))
            Scheme morphism:
              From: 2-d CPR-Fano toric variety covered by 4 affine patches
              To:   2-d CPR-Fano toric variety covered by 3 affine patches
              Defn: Defined by sending Rational polyhedral fan in 2-d lattice N
                    to Rational polyhedral fan in 2-d lattice N.

        Alternatively, one can use homogeneous polynomials to define morphisms::

            sage: P2.inject_variables()
            Defining y0, y1, y2
            sage: dP8.inject_variables()
            Defining t, x0, x1, x2
            sage: hom_set([x0,x1,x2])
            Scheme morphism:
              From: 2-d CPR-Fano toric variety covered by 4 affine patches
              To:   2-d CPR-Fano toric variety covered by 3 affine patches
              Defn: Defined on coordinates by sending [t : x0 : x1 : x2] to
                    [x0 : x1 : x2]

        A morphism of the coordinate ring will also work::

            sage: ring_hom = P2.coordinate_ring().hom([x0,x1,x2], dP8.coordinate_ring())
            sage: ring_hom
            Ring morphism:
              From: Multivariate Polynomial Ring in y0, y1, y2 over Rational Field
              To:   Multivariate Polynomial Ring in t, x0, x1, x2 over Rational Field
              Defn: y0 |--> x0
                    y1 |--> x1
                    y2 |--> x2
            sage: hom_set(ring_hom)
            Scheme morphism:
              From: 2-d CPR-Fano toric variety covered by 4 affine patches
              To:   2-d CPR-Fano toric variety covered by 3 affine patches
              Defn: Defined on coordinates by sending [t : x0 : x1 : x2] to
                    [x0 : x1 : x2]
        """
        from sage.schemes.toric.morphism import SchemeMorphism_polynomial_toric_variety
        if isinstance(x, (list, tuple)):
            return SchemeMorphism_polynomial_toric_variety(self, x, check=check)
        
        if is_RingHomomorphism(x):
            assert x.domain() is self.codomain().coordinate_ring()
            assert x.codomain() is self.domain().coordinate_ring()
            return SchemeMorphism_polynomial_toric_variety(self, x.im_gens(), check=check)

        from sage.schemes.toric.morphism import SchemeMorphism_fan_toric_variety
        if isinstance(x, FanMorphism):
            return SchemeMorphism_fan_toric_variety(self, x, check=check)

        if is_Matrix(x):
            fm = FanMorphism(x, self.domain().fan(), self.codomain().fan())
            return SchemeMorphism_fan_toric_variety(self, fm, check=check)
        
        raise TypeError, "x must be a fan morphism or a list/tuple of polynomials"
Example #44
0
def extend_to_primitive(A_input):
    """
    Given a matrix (resp. list of vectors), extend it to a square
    matrix (resp. list of vectors), such that its determinant is the
    gcd of its minors (i.e. extend the basis of a lattice to a
    "maximal" one in Z^n).

    Author(s): Gonzalo Tornaria and Jonathan Hanke.

    INPUT:
        a matrix, or a list of length n vectors (in the same space)

    OUTPUT:
        a square matrix, or a list of n vectors (resp.)

    EXAMPLES:
        sage: A = Matrix(ZZ, 3, 2, range(6))
        sage: extend_to_primitive(A)
        [ 0  1  0]
        [ 2  3  0]
        [ 4  5 -1]

        sage: extend_to_primitive([vector([1,2,3])])
        [(1, 2, 3), (0, 1, 0), (0, 0, 1)]

    """
    ## Deal with a list of vectors
    if not is_Matrix(A_input):
        A = matrix(A_input)      ## Make a matrix A with the given rows.
        vec_output_flag = True
    else:
        A = A_input
        vec_output_flag = False


    ## Arrange for A  to have more columns than rows.
    if A.is_square():
        return A
    if A.nrows() > A.ncols():
        return extend_to_primitive(A.transpose()).transpose()

    ## Setup
    k = A.nrows()
    n = A.ncols()
    R = A.base_ring()

    # Smith normal form transformation, assuming more columns than rows
    V = A.smith_form()[2]

    ## Extend the matrix in new coordinates, then switch back.
    B = A * V
    B_new = matrix(R, n-k, n)
    for i in range(n-k):
        B_new[i, n-i-1] = 1
    C = B.stack(B_new)
    D = C * V**(-1)

    ## DIAGNOSTIC
    #print "A = ", A, "\n"
    #print "B = ", B, "\n"
    #print "C = ", C, "\n"
    #print "D = ", D, "\n"

    # Normalize for a positive determinant
    if D.det() < 0:
        D.rescale_row(n-1, -1)

    ## Return the current information
    if  vec_output_flag:
        return D.rows()
    else:
        return D
def linear_transformation(arg0, arg1=None, arg2=None, side='left'):
    r"""
    Create a linear transformation from a variety of possible inputs.

    FORMATS:

    In the following, ``D`` and ``C`` are vector spaces over
    the same field that are the domain and codomain
    (respectively) of the linear transformation.

    ``side`` is a keyword that is either 'left' or 'right'.
    When a matrix is used to specify a linear transformation,
    as in the first two call formats below, you may specify
    if the function is given by matrix multiplication with
    the vector on the left, or the vector on the right.
    The default is 'left'. Internally representations are
    always carried as the 'left' version, and the default
    text representation is this version.  However, the matrix
    representation may be obtained as either version, no matter
    how it is created.

    - ``linear_transformation(A, side='left')``

      Where ``A`` is a matrix.  The domain and codomain are inferred
      from the dimension of the matrix and the base ring of the matrix.
      The base ring must be a field, or have its fraction field implemented
      in Sage.

    - ``linear_transformation(D, C, A, side='left')``

      ``A`` is a matrix that behaves as above.  However, now the domain
      and codomain are given explicitly. The matrix is checked for
      compatibility with the domain and codomain.  Additionally, the
      domain and codomain may be supplied with alternate ("user") bases
      and the matrix is interpreted as being a representation relative
      to those bases.

    - ``linear_transformation(D, C, f)``

      ``f`` is any function that can be applied to the basis elements of the
      domain and that produces elements of the codomain.  The linear
      transformation returned is the unique linear transformation that
      extends this mapping on the basis elements.  ``f`` may come from a
      function defined by a Python ``def`` statement, or may be defined as a
      ``lambda`` function.

      Alternatively, ``f`` may be specified by a callable symbolic function,
      see the examples below for a demonstration.

    - ``linear_transformation(D, C, images)``

      ``images`` is a list, or tuple, of codomain elements, equal in number
      to the size of the basis of the domain.  Each basis element of the domain
      is mapped to the corresponding element of the ``images`` list, and the
      linear transformation returned is the unique linear transfromation that
      extends this mapping.

    OUTPUT:

    A linear transformation described by the input.  This is a
    "vector space morphism", an object of the class
    :class:`sage.modules.vector_space_morphism`.

    EXAMPLES:

    We can define a linear transformation with just a matrix, understood to
    act on a vector placed on one side or the other.  The field for the 
    vector spaces used as domain and codomain is obtained from the base 
    ring of the matrix, possibly promoting to a fraction field.  ::

        sage: A = matrix(ZZ, [[1, -1, 4], [2, 0, 5]])
        sage: phi = linear_transformation(A)
        sage: phi
        Vector space morphism represented by the matrix:
        [ 1 -1  4]
        [ 2  0  5]
        Domain: Vector space of dimension 2 over Rational Field
        Codomain: Vector space of dimension 3 over Rational Field
        sage: phi([1/2, 5])
        (21/2, -1/2, 27)

        sage: B = matrix(Integers(7), [[1, 2, 1], [3, 5, 6]])
        sage: rho = linear_transformation(B, side='right')
        sage: rho
        Vector space morphism represented by the matrix:
        [1 3]
        [2 5]
        [1 6]
        Domain: Vector space of dimension 3 over Ring of integers modulo 7
        Codomain: Vector space of dimension 2 over Ring of integers modulo 7
        sage: rho([2, 4, 6])
        (2, 6)

    We can define a linear transformation with a matrix, while explicitly
    giving the domain and codomain.  Matrix entries will be coerced into the
    common field of scalars for the vector spaces.  ::

        sage: D = QQ^3
        sage: C = QQ^2
        sage: A = matrix([[1, 7], [2, -1], [0, 5]])
        sage: A.parent()
        Full MatrixSpace of 3 by 2 dense matrices over Integer Ring
        sage: zeta = linear_transformation(D, C, A)
        sage: zeta.matrix().parent()
        Full MatrixSpace of 3 by 2 dense matrices over Rational Field
        sage: zeta
        Vector space morphism represented by the matrix:
        [ 1  7]
        [ 2 -1]
        [ 0  5]
        Domain: Vector space of dimension 3 over Rational Field
        Codomain: Vector space of dimension 2 over Rational Field

    Matrix representations are relative to the bases for the domain
    and codomain.  ::

        sage: u = vector(QQ, [1, -1])
        sage: v = vector(QQ, [2, 3])
        sage: D = (QQ^2).subspace_with_basis([u, v])
        sage: x = vector(QQ, [2, 1])
        sage: y = vector(QQ, [-1, 4])
        sage: C = (QQ^2).subspace_with_basis([x, y])
        sage: A = matrix(QQ, [[2, 5], [3, 7]])
        sage: psi = linear_transformation(D, C, A)
        sage: psi
        Vector space morphism represented by the matrix:
        [2 5]
        [3 7]
        Domain: Vector space of degree 2 and dimension 2 over Rational Field
        User basis matrix:
        [ 1 -1]
        [ 2  3]
        Codomain: Vector space of degree 2 and dimension 2 over Rational Field
        User basis matrix:
        [ 2  1]
        [-1  4]
        sage: psi(u) == 2*x + 5*y
        True
        sage: psi(v) == 3*x + 7*y
        True

    Functions that act on the domain may be used to compute images of
    the domain's basis elements, and this mapping can be extended to
    a unique linear transformation.  The function may be a Python
    function (via ``def`` or ``lambda``) or a Sage symbolic function.  ::

        sage: def g(x):
        ...     return vector(QQ, [2*x[0]+x[2], 5*x[1]])
        ...
        sage: phi = linear_transformation(QQ^3, QQ^2, g)
        sage: phi
        Vector space morphism represented by the matrix:
        [2 0]
        [0 5]
        [1 0]
        Domain: Vector space of dimension 3 over Rational Field
        Codomain: Vector space of dimension 2 over Rational Field

        sage: f = lambda x: vector(QQ, [2*x[0]+x[2], 5*x[1]])
        sage: rho = linear_transformation(QQ^3, QQ^2, f)
        sage: rho
        Vector space morphism represented by the matrix:
        [2 0]
        [0 5]
        [1 0]
        Domain: Vector space of dimension 3 over Rational Field
        Codomain: Vector space of dimension 2 over Rational Field

        sage: x, y, z = var('x y z')
        sage: h(x, y, z) = [2*x + z, 5*y]
        sage: zeta = linear_transformation(QQ^3, QQ^2, h)
        sage: zeta
        Vector space morphism represented by the matrix:
        [2 0]
        [0 5]
        [1 0]
        Domain: Vector space of dimension 3 over Rational Field
        Codomain: Vector space of dimension 2 over Rational Field

        sage: phi == rho
        True
        sage: rho == zeta
        True


    We create a linear transformation relative to non-standard bases,
    and capture its representation relative to standard bases.  With this, we
    can build functions that create the same linear transformation relative
    to the nonstandard bases.  ::

        sage: u = vector(QQ, [1, -1])
        sage: v = vector(QQ, [2, 3])
        sage: D = (QQ^2).subspace_with_basis([u, v])
        sage: x = vector(QQ, [2, 1])
        sage: y = vector(QQ, [-1, 4])
        sage: C = (QQ^2).subspace_with_basis([x, y])
        sage: A = matrix(QQ, [[2, 5], [3, 7]])
        sage: psi = linear_transformation(D, C, A)
        sage: rho = psi.restrict_codomain(QQ^2).restrict_domain(QQ^2)
        sage: rho.matrix()
        [ -4/5  97/5]
        [  1/5 -13/5]

        sage: f = lambda x: vector(QQ, [(-4/5)*x[0] + (1/5)*x[1], (97/5)*x[0] + (-13/5)*x[1]])
        sage: psi = linear_transformation(D, C, f)
        sage: psi.matrix()
        [2 5]
        [3 7]

        sage: s, t = var('s t')
        sage: h(s, t) = [(-4/5)*s + (1/5)*t, (97/5)*s + (-13/5)*t]
        sage: zeta = linear_transformation(D, C, h)
        sage: zeta.matrix()
        [2 5]
        [3 7]

    Finally, we can give an explicit list of images for the basis
    elements of the domain.  ::

        sage: x = polygen(QQ)
        sage: F.<a> = NumberField(x^3+x+1)
        sage: u = vector(F, [1, a, a^2])
        sage: v = vector(F, [a, a^2, 2])
        sage: w = u + v
        sage: D = F^3
        sage: C = F^3
        sage: rho = linear_transformation(D, C, [u, v, w])
        sage: rho.matrix()
        [      1       a     a^2]
        [      a     a^2       2]
        [  a + 1 a^2 + a a^2 + 2]
        sage: C = (F^3).subspace_with_basis([u, v])
        sage: D = (F^3).subspace_with_basis([u, v])
        sage: psi = linear_transformation(C, D, [u+v, u-v])
        sage: psi.matrix()
        [ 1  1]
        [ 1 -1]

    TESTS:

    We test some bad inputs.  First, the wrong things in the wrong places.  ::

        sage: linear_transformation('junk')
        Traceback (most recent call last):
        ...
        TypeError: first argument must be a matrix or a vector space, not junk

        sage: linear_transformation(QQ^2, QQ^3, 'stuff')
        Traceback (most recent call last):
        ...
        TypeError: third argument must be a matrix, function, or list of images, not stuff

        sage: linear_transformation(QQ^2, 'garbage')
        Traceback (most recent call last):
        ...
        TypeError: if first argument is a vector space, then second argument must be a vector space, not garbage

        sage: linear_transformation(QQ^2, Integers(7)^2)
        Traceback (most recent call last):
        ...
        TypeError: vector spaces must have the same field of scalars, not Rational Field and Ring of integers modulo 7

    Matrices must be over a field (or a ring that can be promoted to a field),
    and of the right size.  ::

        sage: linear_transformation(matrix(Integers(6), [[2, 3],[4, 5]]))
        Traceback (most recent call last):
        ...
        TypeError: matrix must have entries from a field, or a ring with a fraction field, not Ring of integers modulo 6

        sage: A = matrix(QQ, 3, 4, range(12))
        sage: linear_transformation(QQ^4, QQ^4, A)
        Traceback (most recent call last):
        ...
        TypeError: domain dimension is incompatible with matrix size

        sage: linear_transformation(QQ^3, QQ^3, A, side='right')
        Traceback (most recent call last):
        ...
        TypeError: domain dimension is incompatible with matrix size

        sage: linear_transformation(QQ^3, QQ^3, A)
        Traceback (most recent call last):
        ...
        TypeError: codomain dimension is incompatible with matrix size

        sage: linear_transformation(QQ^4, QQ^4, A, side='right')
        Traceback (most recent call last):
        ...
        TypeError: codomain dimension is incompatible with matrix size

    Lists of images can be of the wrong number, or not really
    elements of the codomain.  ::

        sage: linear_transformation(QQ^3, QQ^2, [vector(QQ, [1,2])])
        Traceback (most recent call last):
        ...
        ValueError: number of images should equal the size of the domain's basis (=3), not 1

        sage: C = (QQ^2).subspace_with_basis([vector(QQ, [1,1])])
        sage: linear_transformation(QQ^1, C, [vector(QQ, [1,2])])
        Traceback (most recent call last):
        ...
        ArithmeticError: some proposed image is not in the codomain, because
        element (= [1, 2]) is not in free module


    Functions may not apply properly to domain elements,
    or return values outside the codomain.  ::

        sage: f = lambda x: vector(QQ, [x[0], x[4]])
        sage: linear_transformation(QQ^3, QQ^2, f)
        Traceback (most recent call last):
        ...
        ValueError: function cannot be applied properly to some basis element because
        index out of range

        sage: f = lambda x: vector(QQ, [x[0], x[1]])
        sage: C = (QQ^2).span([vector(QQ, [1, 1])])
        sage: linear_transformation(QQ^2, C, f)
        Traceback (most recent call last):
        ...
        ArithmeticError: some image of the function is not in the codomain, because
        element (= [1, 0]) is not in free module

    A Sage symbolic function can come in a variety of forms that are
    not representative of a linear transformation. ::

        sage: x, y = var('x, y')
        sage: f(x, y) = [y, x, y]
        sage: linear_transformation(QQ^3, QQ^3, f)
        Traceback (most recent call last):
        ...
        ValueError: symbolic function has the wrong number of inputs for domain

        sage: linear_transformation(QQ^2, QQ^2, f)
        Traceback (most recent call last):
        ...
        ValueError: symbolic function has the wrong number of outputs for codomain

        sage: x, y = var('x y')
        sage: f(x, y) = [y, x*y]
        sage: linear_transformation(QQ^2, QQ^2, f)
        Traceback (most recent call last):
        ...
        ValueError: symbolic function must be linear in all the inputs:
        unable to convert y to a rational

        sage: x, y = var('x y')
        sage: f(x, y) = [x, 2*y]
        sage: C = (QQ^2).span([vector(QQ, [1, 1])])
        sage: linear_transformation(QQ^2, C, f)
        Traceback (most recent call last):
        ...
        ArithmeticError: some image of the function is not in the codomain, because
        element (= [1, 0]) is not in free module
    """
    from sage.matrix.constructor import matrix
    from sage.modules.module import is_VectorSpace
    from sage.modules.free_module import VectorSpace
    from sage.categories.homset import Hom
    from sage.symbolic.ring import SymbolicRing
    from sage.modules.vector_callable_symbolic_dense import Vector_callable_symbolic_dense
    from inspect import isfunction

    if not side in ['left', 'right']:
        raise ValueError(
            "side must be 'left' or 'right', not {0}".format(side))
    if not (is_Matrix(arg0) or is_VectorSpace(arg0)):
        raise TypeError(
            'first argument must be a matrix or a vector space, not {0}'.
            format(arg0))
    if is_Matrix(arg0):
        R = arg0.base_ring()
        if not R.is_field():
            try:
                R = R.fraction_field()
            except (NotImplementedError, TypeError):
                msg = 'matrix must have entries from a field, or a ring with a fraction field, not {0}'
                raise TypeError(msg.format(R))
        if side == 'right':
            arg0 = arg0.transpose()
            side = 'left'
        arg2 = arg0
        arg0 = VectorSpace(R, arg2.nrows())
        arg1 = VectorSpace(R, arg2.ncols())
    elif is_VectorSpace(arg0):
        if not is_VectorSpace(arg1):
            msg = 'if first argument is a vector space, then second argument must be a vector space, not {0}'
            raise TypeError(msg.format(arg1))
        if arg0.base_ring() != arg1.base_ring():
            msg = 'vector spaces must have the same field of scalars, not {0} and {1}'
            raise TypeError(msg.format(arg0.base_ring(), arg1.base_ring()))

    # Now arg0 = domain D, arg1 = codomain C, and
    #   both are vector spaces with common field of scalars
    #   use these to make a VectorSpaceHomSpace
    # arg2 might be a matrix that began in arg0
    D = arg0
    C = arg1
    H = Hom(D, C, category=None)

    # Examine arg2 as the "rule" for the linear transformation
    # Pass on matrices, Python functions and lists to homspace call
    # Convert symbolic function here, to a matrix
    if is_Matrix(arg2):
        if side == 'right':
            arg2 = arg2.transpose()
    elif isinstance(arg2, (list, tuple)):
        pass
    elif isfunction(arg2):
        pass
    elif isinstance(arg2, Vector_callable_symbolic_dense):
        args = arg2.parent().base_ring()._arguments
        exprs = arg2.change_ring(SymbolicRing())
        m = len(args)
        n = len(exprs)
        if m != D.degree():
            raise ValueError(
                'symbolic function has the wrong number of inputs for domain')
        if n != C.degree():
            raise ValueError(
                'symbolic function has the wrong number of outputs for codomain'
            )
        arg2 = [[e.coeff(a) for e in exprs] for a in args]
        try:
            arg2 = matrix(D.base_ring(), m, n, arg2)
        except TypeError, e:
            msg = 'symbolic function must be linear in all the inputs:\n' + e.args[
                0]
            raise ValueError(msg)
        # have matrix with respect to standard bases, now consider user bases
        images = [v * arg2 for v in D.basis()]
        try:
            arg2 = matrix([C.coordinates(C(a)) for a in images])
        except (ArithmeticError, TypeError), e:
            msg = 'some image of the function is not in the codomain, because\n' + e.args[
                0]
            raise ArithmeticError(msg)
Example #46
0
    def hom(self, x, Y=None):
        r"""
        Return the scheme morphism from ``self`` to ``Y`` defined by ``x``.
        Here ``x`` can be a matrix or a sequence of polynomials.
        If ``Y`` is omitted, then a natural image is found if possible.

        EXAMPLES:

        Here are a few Morphisms given by matrices. In the first
        example, ``Y`` is omitted, in the second example, ``Y`` is specified.

        ::

            sage: c = Conic([-1, 1, 1])
            sage: h = c.hom(Matrix([[1,1,0],[0,1,0],[0,0,1]])); h
            Scheme morphism:
              From: Projective Conic Curve over Rational Field defined by -x^2 + y^2 + z^2
              To:   Projective Conic Curve over Rational Field defined by -x^2 + 2*x*y + z^2
              Defn: Defined on coordinates by sending (x : y : z) to
                    (x + y : y : z)
            sage: h([-1, 1, 0])
            (0 : 1 : 0)

            sage: c = Conic([-1, 1, 1])
            sage: d = Conic([4, 1, -1])
            sage: c.hom(Matrix([[0, 0, 1/2], [0, 1, 0], [1, 0, 0]]), d)
            Scheme morphism:
              From: Projective Conic Curve over Rational Field defined by -x^2 + y^2 + z^2
              To:   Projective Conic Curve over Rational Field defined by 4*x^2 + y^2 - z^2
              Defn: Defined on coordinates by sending (x : y : z) to
                    (1/2*z : y : x)

        ``ValueError`` is raised if the wrong codomain ``Y`` is specified:

        ::

            sage: c = Conic([-1, 1, 1])
            sage: c.hom(Matrix([[0, 0, 1/2], [0, 1, 0], [1, 0, 0]]), c)
            Traceback (most recent call last):
            ...
            ValueError: The matrix x (= [  0   0 1/2]
            [  0   1   0]
            [  1   0   0]) does not define a map from self (= Projective Conic Curve over Rational Field defined by -x^2 + y^2 + z^2) to Y (= Projective Conic Curve over Rational Field defined by -x^2 + y^2 + z^2)
        """
        if is_Matrix(x):
            from constructor import Conic
            y = x.inverse()
            A = y.transpose()*self.matrix()*y
            im = Conic(A)
            if Y == None:
                Y = im
            else:
                q = Y.defining_polynomial()/im.defining_polynomial()
                if not (q.numerator().is_constant()
                        and q.denominator().is_constant()):
                    raise ValueError, "The matrix x (= %s) does not define a " \
                                      "map from self (= %s) to Y (= %s)" % \
                                      (x, self, Y)
            x = Sequence(x*vector(self.ambient_space().gens()))
            return self.Hom(Y)(x, check = False)
        return ProjectiveCurve_generic.hom(self, x, Y)
    def __call__(self, v):
        """
        Evaluate this quadratic form Q on a vector or matrix of elements
        coercible to the base ring of the quadratic form.  If a vector
        is given then the output will be the ring element Q(`v`), but if a
        matrix is given then the output will be the quadratic form Q'
        which in matrix notation is given by:

        .. math::
                Q' = v^t * Q * v.


        EXAMPLES::

            ## Evaluate a quadratic form at a vector:
            ## --------------------------------------
            sage: Q = QuadraticForm(QQ, 3, range(6))
            sage: Q
            Quadratic form in 3 variables over Rational Field with coefficients:
            [ 0 1 2 ]
            [ * 3 4 ]
            [ * * 5 ]
            sage: Q([1,2,3])
            89
            sage: Q([1,0,0])
            0
            sage: Q([1,1,1])
            15

    ::

            ## Evaluate a quadratic form using a column matrix:
            ## ------------------------------------------------
            sage: Q = QuadraticForm(QQ, 2, range(1,4))
            sage: A = Matrix(ZZ,2,2,[-1,0,0,1])
            sage: Q(A)
            Quadratic form in 2 variables over Rational Field with coefficients:
            [ 1 -2 ]
            [ * 3 ]
            sage: Q([1,0])
            1
            sage: type(Q([1,0]))
            <type 'sage.rings.rational.Rational'>
            sage: Q = QuadraticForm(QQ, 2, range(1,4))
            sage: Q(matrix(2, [1,0]))
            Quadratic form in 1 variables over Rational Field with coefficients:
            [ 1 ]

        ::

            ## Simple 2x2 change of variables:
            ## -------------------------------
            sage: Q = QuadraticForm(ZZ, 2, [1,0,1])
            sage: Q
            Quadratic form in 2 variables over Integer Ring with coefficients:
            [ 1 0 ]
            [ * 1 ]
            sage: M = Matrix(ZZ, 2, 2, [1,1,0,1])
            sage: M
            [1 1]
            [0 1]
            sage: Q(M)
            Quadratic form in 2 variables over Integer Ring with coefficients:
            [ 1 2 ]
            [ * 2 ]

        ::

            ## Some more tests:
            ## ----------------
            sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1])
            sage: Q([1,2,3])
            14
            sage: v = vector([1,2,3])
            sage: Q(v)
            14
            sage: t = tuple([1,2,3])
            sage: Q(v)
            14
            sage: M = Matrix(ZZ, 3, [1,2,3])
            sage: Q(M)
            Quadratic form in 1 variables over Integer Ring with coefficients:
            [ 14 ]

        """
        ## If we are passed a matrix A, return the quadratic form Q(A(x))
        ## (In matrix notation: A^t * Q * A)
        n = self.dim()

        if is_Matrix(v):
            ## Check that v has the correct number of rows
            if v.nrows() != n:
                raise TypeError("Oops!  The matrix must have " + str(n) + " rows. =(")

            ## Create the new quadratic form
            m = v.ncols()
            Q2 = QuadraticForm(self.base_ring(), m)
            return QFEvaluateMatrix(self, v, Q2)

        elif (is_Vector(v) or isinstance(v, (list, tuple))):
            ## Check the vector/tuple/list has the correct length
            if not (len(v) == n):
                raise TypeError("Oops!  Your vector needs to have length " + str(n) + " .")

            ## TO DO:  Check that the elements can be coerced into the base ring of Q -- on first elt.
            if len(v) > 0:
                try:
                    x = self.base_ring()(v[0])
                except Exception:
                    raise TypeError("Oops!  Your vector is not coercible to the base ring of the quadratic form... =(")

            ## Attempt to evaluate Q[v]
            return QFEvaluateVector(self, v)

        else:
            raise TypeError
Example #48
0
def RSK(obj1=None, obj2=None, insertion='RSK', check_standard=False, **options):
    r"""
    Perform the Robinson-Schensted-Knuth (RSK) correspondence.

    The Robinson-Schensted-Knuth (RSK) correspondence (also known
    as the RSK algorithm) is most naturally stated as a bijection
    between generalized permutations (also known as two-line arrays,
    biwords, ...) and pairs of semi-standard Young tableaux `(P, Q)`
    of identical shape. The tableau `P` is known as the insertion
    tableau, and `Q` is known as the recording tableau.

    The basic operation is known as row insertion `P \leftarrow k`
    (where `P` is a given semi-standard Young tableau, and `k` is an
    integer). Row insertion is a recursive algorithm which starts by
    setting `k_0 = k`, and in its `i`-th step inserts the number `k_i`
    into the `i`-th row of `P` (we start counting the rows at `0`) by
    replacing the first integer greater than `k_i` in the row by `k_i`
    and defines `k_{i+1}` as the integer that has been replaced. If no
    integer greater than `k_i` exists in the `i`-th row, then `k_i` is
    simply appended to the row and the algorithm terminates at this
    point.

    Now the RSK algorithm, applied to a generalized permutation
    `p = ((j_0, k_0), (j_1, k_1), \ldots, (j_{\ell-1}, k_{\ell-1}))`
    (encoded as a lexicographically sorted list of pairs) starts by
    initializing two semi-standard tableaux `P_0` and `Q_0` as empty
    tableaux. For each nonnegative integer `t` starting at `0`, take
    the pair `(j_t, k_t)` from `p` and set
    `P_{t+1} = P_t \leftarrow k_t`, and define `Q_{t+1}` by adding a
    new box filled with `j_t` to the tableau `Q_t` at the same
    location the row insertion on `P_t` ended (that is to say, adding
    a new box with entry `j_t` such that `P_{t+1}` and `Q_{t+1}` have
    the same shape). The iterative process stops when `t` reaches the
    size of `p`, and the pair `(P_t, Q_t)` at this point is the image
    of `p` under the Robinson-Schensted-Knuth correspondence.

    This correspondence has been introduced in [Knu1970]_, where it has
    been referred to as "Construction A".

    For more information, see Chapter 7 in [Sta-EC2]_.

    We also note that integer matrices are in bijection with generalized
    permutations. Furthermore, we can convert any word `w` (and, in
    particular, any permutation) to a generalized permutation by
    considering the top line to be `(1, 2, \ldots, n)` where `n` is the
    length of `w`.

    The optional argument ``insertion`` allows to specify an alternative
    insertion procedure to be used instead of the standard
    Robinson-Schensted-Knuth insertion. If the input is a reduced word of
    a permutation (i.e., an element of a type-`A` Coxeter group), one can
    set ``insertion`` to ``'EG'``, which gives Edelman-Greene insertion,
    an algorithm defined in [EG1987]_ Definition 6.20 (where it is
    referred to as Coxeter-Knuth insertion). The Edelman-Greene insertion
    is similar to the standard row insertion except that if `k_i` and
    `k_i + 1` both exist in row `i`, we *only* set `k_{i+1} = k_i + 1` and
    continue.

    One can also perform a "Hecke RSK algorithm", defined using the
    Hecke insertion studied in [BKSTY06]_ (but using rows instead of
    columns). The algorithm proceeds similarly to the classical RSK
    algorithm. However, it is not clear in what generality it works;
    thus, following [BKSTY06]_, we shall assume that our biword `p`
    has top line `(1, 2, \ldots, n)` (or, at least, has its top line
    strictly increasing). The Hecke RSK algorithm returns a pair of
    an increasing tableau and a set-valued standard tableau. If
    `p = ((j_0, k_0), (j_1, k_1), \ldots, (j_{\ell-1}, k_{\ell-1}))`,
    then the algorithm recursively constructs pairs
    `(P_0, Q_0), (P_1, Q_1), \ldots, (P_\ell, Q_\ell)` of tableaux.
    The construction of `P_{t+1}` and `Q_{t+1}` from `P_t`, `Q_t`,
    `j_t` and `k_t` proceeds as follows: Set `i = j_t`, `x = k_t`,
    `P = P_t` and `Q = Q_t`. We are going to insert `x` into the
    increasing tableau `P` and update the set-valued "recording
    tableau" `Q` accordingly. As in the classical RSK algorithm, we
    first insert `x` into row `1` of `P`, then into row `2` of the
    resulting tableau, and so on, until the construction terminates.
    The details are different: Suppose we are inserting `x` into
    row `R` of `P`. If (Case 1) there exists an entry `y` in row `R`
    such that `x < y`, then let `y` be the minimal such entry. We
    replace this entry `y` with `x` if the result is still an
    increasing tableau; in either subcase, we then continue
    recursively, inserting `y` into the next row of `P`.
    If, on the other hand, (Case 2) no such `y` exists, then we
    append `x` to the end of `R` if the result is an increasing
    tableau (Subcase 2.1), and otherwise (Subcase 2.2) do nothing.
    Furthermore, in Subcase 2.1, we add the box that we have just
    filled with `x` in `P` to the shape of `Q`, and fill it with
    the one-element set `\{i\}`. In Subcase 2.2, we find the
    bottommost box of the column containing the rightmost box of
    row `R`, and add `i` to the entry of `Q` in this box (this
    entry is a set, since `Q` is a set-valued). In either
    subcase, we terminate the recursion, and set
    `P_{t+1} = P` and `Q_{t+1} = Q`.

    Notice that set-valued tableaux are encoded as tableaux whose
    entries are tuples of positive integers; each such tuple is strictly
    increasing and encodes a set (namely, the set of its entries).

    INPUT:

    - ``obj1, obj2`` -- Can be one of the following:

      - A word in an ordered alphabet
      - An integer matrix
      - Two lists of equal length representing a generalized permutation
      - Any object which has a method ``_rsk_iter()`` which returns an
        iterator over the object represented as generalized permutation or
        a pair of lists.

    - ``insertion`` -- (Default: ``'RSK'``) The following types of insertion
      are currently supported:

      - ``'RSK'`` -- Robinson-Schensted-Knuth
      - ``'EG'`` -- Edelman-Greene (only for reduced words of
        permutations/elements of a type-`A` Coxeter group)
      - ``'hecke'`` -- Hecke insertion (only guaranteed for
        generalized permutations whose top row is strictly increasing)

    - ``check_standard`` -- (Default: ``False``) Check if either of the
      resulting tableaux is a standard tableau, and if so, typecast it
      as such

    EXAMPLES:

    If we only give one line, we treat the top line as being
    `(1, 2, \ldots, n)`::

        sage: RSK([3,3,2,4,1])
        [[[1, 3, 4], [2], [3]], [[1, 2, 4], [3], [5]]]
        sage: RSK(Word([3,3,2,4,1]))
        [[[1, 3, 4], [2], [3]], [[1, 2, 4], [3], [5]]]
        sage: RSK(Word([2,3,3,2,1,3,2,3]))
        [[[1, 2, 2, 3, 3], [2, 3], [3]], [[1, 2, 3, 6, 8], [4, 7], [5]]]

    With a generalized permutation::

        sage: RSK([1, 2, 2, 2], [2, 1, 1, 2])
        [[[1, 1, 2], [2]], [[1, 2, 2], [2]]]
        sage: RSK(Word([1,1,3,4,4]), [1,4,2,1,3])
        [[[1, 1, 3], [2], [4]], [[1, 1, 4], [3], [4]]]
        sage: RSK([1,3,3,4,4], Word([6,2,2,1,7]))
        [[[1, 2, 7], [2], [6]], [[1, 3, 4], [3], [4]]]

    If we give it a matrix::

        sage: RSK(matrix([[0,1],[2,1]]))
        [[[1, 1, 2], [2]], [[1, 2, 2], [2]]]

    We can also give it something looking like a matrix::

        sage: RSK([[0,1],[2,1]])
        [[[1, 1, 2], [2]], [[1, 2, 2], [2]]]

    There are also variations of the insertion algorithm in RSK.
    Here we consider Edelman-Greene insertion::

        sage: RSK([2,1,2,3,2], insertion='EG')
        [[[1, 2, 3], [2, 3]], [[1, 3, 4], [2, 5]]]

    We reproduce figure 6.4 in [EG1987]_::

        sage: RSK([2,3,2,1,2,3], insertion='EG')
        [[[1, 2, 3], [2, 3], [3]], [[1, 2, 6], [3, 5], [4]]]

    Hecke insertion is also supported. We construct Example 2.1
    in :arxiv:`0801.1319v2`::

        sage: w = [5, 4, 1, 3, 4, 2, 5, 1, 2, 1, 4, 2, 4]
        sage: RSK(w, insertion='hecke')
        [[[1, 2, 4, 5], [2, 4, 5], [3, 5], [4], [5]],
         [[(1,), (4,), (5,), (7,)],
          [(2,), (9,), (11, 13)],
          [(3,), (12,)],
          [(6,)],
          [(8, 10)]]]

    There is also :func:`~sage.combinat.rsk.RSK_inverse` which performs
    the inverse of the bijection on a pair of semistandard tableaux. We
    note that the inverse function takes 2 separate tableaux as inputs, so
    to compose with :func:`~sage.combinat.rsk.RSK`, we need to use the
    python ``*`` on the output::

        sage: RSK_inverse(*RSK([1, 2, 2, 2], [2, 1, 1, 2]))
        [[1, 2, 2, 2], [2, 1, 1, 2]]
        sage: P,Q = RSK([1, 2, 2, 2], [2, 1, 1, 2])
        sage: RSK_inverse(P, Q)
        [[1, 2, 2, 2], [2, 1, 1, 2]]

    TESTS:

    Empty objects::

        sage: RSK(Permutation([]))
        [[], []]
        sage: RSK(Word([]))
        [[], []]
        sage: RSK(matrix([[]]))
        [[], []]
        sage: RSK([], [])
        [[], []]
        sage: RSK([[]])
        [[], []]
        sage: RSK(Word([]), insertion='EG')
        [[], []]
        sage: RSK(Word([]), insertion='hecke')
        [[], []]
    """
    from sage.combinat.tableau import SemistandardTableau, StandardTableau

    if insertion == 'hecke':
        return hecke_insertion(obj1, obj2)

    if obj1 is None and obj2 is None:
        if 'matrix' in options:
            obj1 = matrix(options['matrix'])
        else:
            raise ValueError("invalid input")

    if is_Matrix(obj1):
        obj1 = obj1.rows()
    if len(obj1) == 0:
        return [StandardTableau([]), StandardTableau([])]

    if obj2 is None:
        try:
            itr = obj1._rsk_iter()
        except AttributeError:
            # If this is (something which looks like) a matrix
            #   then build the generalized permutation
            try:
                t = []
                b = []
                for i, row in enumerate(obj1):
                    for j, mult in enumerate(row):
                        if mult > 0:
                            t.extend([i+1]*mult)
                            b.extend([j+1]*mult)
                itr = izip(t, b)
            except TypeError:
                itr = izip(range(1, len(obj1)+1), obj1)
    else:
        if len(obj1) != len(obj2):
            raise ValueError("the two arrays must be the same length")
        # Check it is a generalized permutation
        lt = 0
        lb = 0
        for t,b in izip(obj1, obj2):
            if t < lt or (lt == t and b < lb):
                raise ValueError("invalid generalized permutation")
            lt = t
            lb = b
        itr = izip(obj1, obj2)

    from bisect import bisect_right
    p = []       #the "insertion" tableau
    q = []       #the "recording" tableau

    use_EG = (insertion == 'EG')

    #For each x in self, insert x into the tableau p.
    lt = 0
    lb = 0
    for i, x in itr:
        for r, qr in izip(p,q):
            if r[-1] > x:
                #Figure out where to insert x into the row r.  The
                #bisect command returns the position of the least
                #element of r greater than x.  We will call it y.
                y_pos = bisect_right(r, x)
                if use_EG and r[y_pos] == x + 1 and y_pos > 0 and x == r[y_pos - 1]:
                    #Special bump: Nothing to do except increment x by 1
                    x += 1
                else:
                    #Switch x and y
                    x, r[y_pos] = r[y_pos], x
            else:
                break
        else:
            #We made through all of the rows of p without breaking
            #so we need to add a new row to p and q.
            r = []; p.append(r)
            qr = []; q.append(qr)

        r.append(x)
        qr.append(i) # Values are always inserted to the right

    if check_standard:
        try:
            P = StandardTableau(p)
        except ValueError:
            P = SemistandardTableau(p)
        try:
            Q = StandardTableau(q)
        except ValueError:
            Q = SemistandardTableau(q)
        return [P, Q]
    return [SemistandardTableau(p), SemistandardTableau(q)]
    def __init__(self, homspace, A):
        r"""
        Create a linear transformation, a morphism between vector spaces.

        INPUT:

        -  ``homspace`` - a homspace (of vector spaces) to serve
           as a parent for the linear transformation and a home for
           the domain and codomain of the morphism
        -  ``A`` - a matrix representing the linear transformation,
           which will act on vectors placed to the left of the matrix

        EXAMPLES:

        Nominally, we require a homspace to hold the domain
        and codomain and a matrix representation of the morphism
        (linear transformation).  ::

            sage: from sage.modules.vector_space_homspace import VectorSpaceHomspace
            sage: from sage.modules.vector_space_morphism import VectorSpaceMorphism
            sage: H = VectorSpaceHomspace(QQ^3, QQ^2)
            sage: A = matrix(QQ, 3, 2, range(6))
            sage: zeta = VectorSpaceMorphism(H, A)
            sage: zeta
            Vector space morphism represented by the matrix:
            [0 1]
            [2 3]
            [4 5]
            Domain: Vector space of dimension 3 over Rational Field
            Codomain: Vector space of dimension 2 over Rational Field

        See the constructor,
        :func:`sage.modules.vector_space_morphism.linear_transformation`
        for another way to create linear transformations.

        The ``.hom()`` method of a vector space will create a vector
        space morphism. ::

            sage: V = QQ^3; W = V.subspace_with_basis([[1,2,3], [-1,2,5/3], [0,1,-1]])
            sage: phi = V.hom(matrix(QQ, 3, range(9)), codomain=W) # indirect doctest
            sage: type(phi)
            <class 'sage.modules.vector_space_morphism.VectorSpaceMorphism'>

        A matrix may be coerced into a vector space homspace to
        create a vector space morphism.  ::

            sage: from sage.modules.vector_space_homspace import VectorSpaceHomspace
            sage: H = VectorSpaceHomspace(QQ^3, QQ^2)
            sage: A = matrix(QQ, 3, 2, range(6))
            sage: rho = H(A)  # indirect doctest
            sage: type(rho)
            <class 'sage.modules.vector_space_morphism.VectorSpaceMorphism'>
        """
        if not vector_space_homspace.is_VectorSpaceHomspace(homspace):
            raise TypeError, 'homspace must be a vector space hom space, not {0}'.format(
                homspace)
        if isinstance(A, matrix_morphism.MatrixMorphism):
            A = A.matrix()
        if not is_Matrix(A):
            msg = 'input must be a matrix representation or another matrix morphism, not {0}'
            raise TypeError(msg.format(A))
        # now have a vector space homspace, and a matrix, check compatibility

        if homspace.domain().dimension() != A.nrows():
            raise TypeError(
                'domain dimension is incompatible with matrix size')
        if homspace.codomain().dimension() != A.ncols():
            raise TypeError(
                'codomain dimension is incompatible with matrix size')

        A = homspace._matrix_space()(A)
        free_module_morphism.FreeModuleMorphism.__init__(self, homspace, A)
def PolynomialSequence(arg1, arg2=None, immutable=False, cr=False, cr_str=None):
    """
    Construct a new polynomial sequence object.

    INPUT:

    - ``arg1`` - a multivariate polynomial ring, an ideal or a matrix

    - ``arg2`` - an iterable object of parts or polynomials
      (default:``None``)

      - ``immutable`` - if ``True`` the sequence is immutable (default: ``False``)

      - ``cr`` - print a line break after each element (default: ``False``)

      - ``cr_str`` - print a line break after each element if 'str' is
        called (default: ``None``)

    EXAMPLES::

        sage: P.<a,b,c,d> = PolynomialRing(GF(127),4)
        sage: I = sage.rings.ideal.Katsura(P)

    If a list of tuples is provided, those form the parts::

        sage: F = Sequence([I.gens(),I.gens()], I.ring()); F # indirect doctest
        [a + 2*b + 2*c + 2*d - 1,
         a^2 + 2*b^2 + 2*c^2 + 2*d^2 - a,
         2*a*b + 2*b*c + 2*c*d - b,
         b^2 + 2*a*c + 2*b*d - c,
         a + 2*b + 2*c + 2*d - 1,
         a^2 + 2*b^2 + 2*c^2 + 2*d^2 - a,
         2*a*b + 2*b*c + 2*c*d - b,
         b^2 + 2*a*c + 2*b*d - c]
        sage: F.nparts()
        2

    If an ideal is provided, the generators are used::

        sage: Sequence(I)
        [a + 2*b + 2*c + 2*d - 1,
         a^2 + 2*b^2 + 2*c^2 + 2*d^2 - a,
         2*a*b + 2*b*c + 2*c*d - b,
         b^2 + 2*a*c + 2*b*d - c]

    If a list of polynomials is provided, the system has only one
    part::

        sage: F = Sequence(I.gens(), I.ring()); F
        [a + 2*b + 2*c + 2*d - 1,
         a^2 + 2*b^2 + 2*c^2 + 2*d^2 - a,
         2*a*b + 2*b*c + 2*c*d - b,
         b^2 + 2*a*c + 2*b*d - c]
         sage: F.nparts()
         1
    """

    from sage.matrix.matrix import is_Matrix

    if is_MPolynomialRing(arg1) or is_QuotientRing(arg1):
        ring = arg1
        gens = arg2

    elif is_MPolynomialRing(arg2) or is_QuotientRing(arg2):
        ring = arg2
        gens = arg1

    elif is_Matrix(arg1) and arg2 is None:
        ring = arg1.base_ring()
        gens = arg1.list()

    elif isinstance(arg1, MPolynomialIdeal) and arg2 is None:
        ring = arg1.ring()
        gens = arg1.gens()

    elif isinstance(arg1, (list,tuple,GeneratorType)) and arg2 is None:
        gens = arg1

        try:
            e = iter(gens).next()
        except StopIteration:
            raise ValueError("Cannot determine ring from provided information.")

        if is_MPolynomial(e) or isinstance(e, QuotientRingElement):
            ring = e.parent()
        else:
            ring = iter(e).next().parent()
    else:
        raise TypeError("Cannot understand input.")

    try:
        e = iter(gens).next()

        if is_MPolynomial(e) or isinstance(e, QuotientRingElement):
            gens = tuple(gens)
            parts = (gens,)
            if not all(f.parent() is ring for f in gens):
                parts = ((ring(f) for f in gens),)
        else:
            parts = []
            _gens = []
            for part in gens:
                _part = []
                for gen in part:
                    if not gen.parent() is ring:
                        ring(gen)
                    _part.append(gen)
                _gens.extend(_part)
                parts.append(tuple(_part))
            gens = _gens
    except StopIteration:
        gens = tuple()
        parts = ((),)

    k = ring.base_ring()

    try: c = k.characteristic()
    except NotImplementedError: c = -1

    if c != 2:
        return PolynomialSequence_generic(parts, ring, immutable=immutable, cr=cr, cr_str=cr_str)
    elif k.degree() == 1:
        return PolynomialSequence_gf2(parts, ring, immutable=immutable, cr=cr, cr_str=cr_str)
    elif k.degree() > 1:
        return PolynomialSequence_gf2e(parts, ring, immutable=immutable, cr=cr, cr_str=cr_str)
Example #51
0
def DynkinDiagram(*args, **kwds):
    r"""
    Return the Dynkin diagram corresponding to the input.

    INPUT:

    The input can be one of the following:

    - empty to obtain an empty Dynkin diagram
    - a Cartan type
    - a Cartan matrix
    - a Cartan matrix and an indexing set

    One can also input an indexing set by passing a tuple using the optional
    argument ``index_set``.

    The edge multiplicities are encoded as edge labels. For the corresponding
    Cartan matrices, this uses the convention in Hong and Kang, Kac,
    Fulton and Harris, and crystals. This is the **opposite** convention
    in Bourbaki and Wikipedia's Dynkin diagram (:wikipedia:`Dynkin_diagram`).
    That is for `i \neq j`::

       i <--k-- j <==> a_ij = -k
                  <==> -scalar(coroot[i], root[j]) = k
                  <==> multiple arrows point from the longer root
                       to the shorter one

    For example, in type `C_2`, we have::

        sage: C2 = DynkinDiagram(['C',2]); C2
        O=<=O
        1   2
        C2
        sage: C2.cartan_matrix()
        [ 2 -2]
        [-1  2]

    However Bourbaki would have the Cartan matrix as:

    .. MATH::

        \begin{bmatrix}
        2 & -1 \\
        -2 & 2
        \end{bmatrix}.

    EXAMPLES::

        sage: DynkinDiagram(['A', 4])
        O---O---O---O
        1   2   3   4
        A4

        sage: DynkinDiagram(['A',1],['A',1])
        O
        1
        O
        2
        A1xA1

        sage: R = RootSystem("A2xB2xF4")
        sage: DynkinDiagram(R)
        O---O
        1   2
        O=>=O
        3   4
        O---O=>=O---O
        5   6   7   8
        A2xB2xF4

        sage: R = RootSystem("A2xB2xF4")
        sage: CM = R.cartan_matrix(); CM
        [ 2 -1| 0  0| 0  0  0  0]
        [-1  2| 0  0| 0  0  0  0]
        [-----+-----+-----------]
        [ 0  0| 2 -1| 0  0  0  0]
        [ 0  0|-2  2| 0  0  0  0]
        [-----+-----+-----------]
        [ 0  0| 0  0| 2 -1  0  0]
        [ 0  0| 0  0|-1  2 -1  0]
        [ 0  0| 0  0| 0 -2  2 -1]
        [ 0  0| 0  0| 0  0 -1  2]
        sage: DD = DynkinDiagram(CM); DD
        O---O
        1   2
        O=>=O
        3   4
        O---O=>=O---O
        5   6   7   8
        A2xB2xF4
        sage: DD.cartan_matrix()
        [ 2 -1  0  0  0  0  0  0]
        [-1  2  0  0  0  0  0  0]
        [ 0  0  2 -1  0  0  0  0]
        [ 0  0 -2  2  0  0  0  0]
        [ 0  0  0  0  2 -1  0  0]
        [ 0  0  0  0 -1  2 -1  0]
        [ 0  0  0  0  0 -2  2 -1]
        [ 0  0  0  0  0  0 -1  2]

    We can also create Dynkin diagrams from arbitrary Cartan matrices::

        sage: C = CartanMatrix([[2, -3], [-4, 2]])
        sage: DynkinDiagram(C)
        Dynkin diagram of rank 2
        sage: C.index_set()
        (0, 1)
        sage: CI = CartanMatrix([[2, -3], [-4, 2]], [3, 5])
        sage: DI = DynkinDiagram(CI)
        sage: DI.index_set()
        (3, 5)
        sage: CII = CartanMatrix([[2, -3], [-4, 2]])
        sage: DII = DynkinDiagram(CII, ('y', 'x'))
        sage: DII.index_set()
        ('x', 'y')

    .. SEEALSO::

        :func:`CartanType` for a general discussion on Cartan
        types and in particular node labeling conventions.

    TESTS:

    Check that :trac:`15277` is fixed by not having edges from 0's::

        sage: CM = CartanMatrix([[2,-1,0,0],[-3,2,-2,-2],[0,-1,2,-1],[0,-1,-1,2]])
        sage: CM
        [ 2 -1  0  0]
        [-3  2 -2 -2]
        [ 0 -1  2 -1]
        [ 0 -1 -1  2]
        sage: CM.dynkin_diagram().edges()
        [(0, 1, 3),
         (1, 0, 1),
         (1, 2, 1),
         (1, 3, 1),
         (2, 1, 2),
         (2, 3, 1),
         (3, 1, 2),
         (3, 2, 1)]
    """
    if len(args) == 0:
        return DynkinDiagram_class()
    mat = args[0]
    if is_Matrix(mat):
        mat = CartanMatrix(*args)
    if isinstance(mat, CartanMatrix):
        if mat.cartan_type() is not mat:
            try:
                return mat.cartan_type().dynkin_diagram()
            except AttributeError:
                ct = CartanType(*args)
                raise ValueError("Dynkin diagram data not yet hardcoded for type %s"%ct)
        if len(args) > 1:
            index_set = tuple(args[1])
        elif "index_set" in kwds:
            index_set = tuple(kwds["index_set"])
        else:
            index_set = mat.index_set()
        D = DynkinDiagram_class(index_set=index_set)
        for (i, j) in mat.nonzero_positions():
            if i != j:
                D.add_edge(index_set[i], index_set[j], -mat[j, i])
        return D
    ct = CartanType(*args)
    try:
        return ct.dynkin_diagram()
    except AttributeError:
        raise ValueError("Dynkin diagram data not yet hardcoded for type %s"%ct)
    def __init__(self, R, n=None, entries=None, unsafe_initialization=False, number_of_automorphisms=None, determinant=None):
        """
        EXAMPLES::

            sage: s = QuadraticForm(ZZ, 4, range(10))
            sage: s == loads(dumps(s))
            True
        """
        ## Deal with:  QuadraticForm(ring, matrix)
        matrix_init_flag = False
        if isinstance(R, Ring):
            if is_Matrix(n):
                ## Test if n is symmetric and has even diagonal
                if not self._is_even_symmetric_matrix_(n, R):
                    raise TypeError("Oops!  The matrix is not a symmetric with even diagonal defined over R.")

                ## Rename the matrix and ring
                M = n
                M_ring = R
                matrix_init_flag = True


        ## Deal with:  QuadraticForm(matrix)
        if is_Matrix(R) and (n == None):

            ## Test if R is symmetric and has even diagonal
            if not self._is_even_symmetric_matrix_(R):
                raise TypeError("Oops!  The matrix is not a symmetric with even diagonal.")

            ## Rename the matrix and ring
            M = R
            M_ring = R.base_ring()
            matrix_init_flag = True

        ## Perform the quadratic form initialization
        if matrix_init_flag == True:
            self.__n = M.nrows()
            self.__base_ring = M_ring
            self.__coeffs = []
            for i in range(M.nrows()):
                for j in range(i, M.nrows()):
                    if (i == j):
                        self.__coeffs += [ M_ring(M[i,j] / 2) ]
                    else:
                        self.__coeffs += [ M_ring(M[i,j]) ]

            return

        ## -----------------------------------------------------------

        ## Verify the size of the matrix is an integer >= 0
        try:
            n = int(n)
        except Exception:
            raise TypeError("Oops! The size " + str(n) + " must be an integer.")
            if (n < 0):
                raise TypeError("Oops! The size " + str(n) + " must be a non-negative integer.")

        ## TODO: Verify that R is a ring...

        ## Store the relevant variables
        N = int(n*(n+1))/2
        self.__n = int(n)
        self.__base_ring = R
        self.__coeffs = [self.__base_ring(0)  for i in range(N)]

        ## Check if entries is a list for the current size, and if so, write the upper-triangular matrix
        if isinstance(entries, list) and (len(entries) == N):
            for i in range(N):
                self.__coeffs[i] = self.__base_ring(entries[i])
        elif (entries != None):
            raise TypeError("Oops! The entries " + str(entries) + "must be a list of size n(n+1)/2.")

        ## -----------------------------------------------------------

        ## Process possible forced initialization of various fields
        self._external_initialization_list = []
        if unsafe_initialization:

            ## Set the number of automorphisms
            if number_of_automorphisms != None:
                self.set_number_of_automorphisms(number_of_automorphisms)
                #self.__number_of_automorphisms = number_of_automorphisms
                #self.__external_initialization_list.append('number_of_automorphisms')

            ## Set the determinant
            if determinant != None:
                self.__det = determinant
                self._external_initialization_list.append('determinant')
Example #53
0
    def __call__(self, A, check=True):
        r"""
        INPUT:

        - ``A`` - one of several possible inputs representing
          a morphism from this vector space homspace.
          - a vector space morphism in this homspace
          - a matrix representation relative to the bases of the vector spaces,
            which acts on a vector placed to the left of the matrix
          - a list or tuple containing images of the domain's basis vectors
          - a function from the domain to the codomain
        - ``check`` (default: True) - ``True`` or ``False``, required for
          compatibility with calls from
          :meth:`sage.structure.parent_gens.ParentWithGens.hom`.

        EXAMPLES::

            sage: V = (QQ^3).span_of_basis([[1,1,0],[1,0,2]])
            sage: H = V.Hom(V)
            sage: H
            Set of Morphisms (Linear Transformations) from
            Vector space of degree 3 and dimension 2 over Rational Field
            User basis matrix:
            [1 1 0]
            [1 0 2]
            to
            Vector space of degree 3 and dimension 2 over Rational Field
            User basis matrix:
            [1 1 0]
            [1 0 2]

        Coercing a matrix::

            sage: A = matrix(QQ, [[0, 1], [1, 0]])
            sage: rho = H(A)          # indirect doctest
            sage: rho
            Vector space morphism represented by the matrix:
            [0 1]
            [1 0]
            Domain: Vector space of degree 3 and dimension 2 over Rational Field
            User basis matrix:
            [1 1 0]
            [1 0 2]
            Codomain: Vector space of degree 3 and dimension 2 over Rational Field
            User basis matrix:
            [1 1 0]
            [1 0 2]

        Coercing a list of images::

            sage: phi = H([V.1, V.0])
            sage: phi(V.1) == V.0
            True
            sage: phi(V.0) == V.1
            True
            sage: phi
            Vector space morphism represented by the matrix:
            [0 1]
            [1 0]
            Domain: Vector space of degree 3 and dimension 2 over Rational Field
            User basis matrix:
            [1 1 0]
            [1 0 2]
            Codomain: Vector space of degree 3 and dimension 2 over Rational Field
            User basis matrix:
            [1 1 0]
            [1 0 2]

        Coercing a lambda function::

            sage: f = lambda x: vector(QQ, [x[0], (1/2)*x[2], 2*x[1]])
            sage: zeta = H(f)
            sage: zeta
            Vector space morphism represented by the matrix:
            [0 1]
            [1 0]
            Domain: Vector space of degree 3 and dimension 2 over Rational Field
            User basis matrix:
            [1 1 0]
            [1 0 2]
            Codomain: Vector space of degree 3 and dimension 2 over Rational Field
            User basis matrix:
            [1 1 0]
            [1 0 2]

        Coercing a vector space morphism into the parent of a second vector
        space morphism will unify their parents::

            sage: U = FreeModule(QQ,3, sparse=True ); V = QQ^4
            sage: W = FreeModule(QQ,3, sparse=False); X = QQ^4
            sage: H = Hom(U, V)
            sage: K = Hom(W, X)
            sage: H is K, H == K
            (False, True)

            sage: A = matrix(QQ, 3, 4, [0]*12)
            sage: f = H(A)
            sage: B = matrix(QQ, 3, 4, range(12))
            sage: g = K(B)
            sage: f.parent() is H and g.parent() is K
            True

            sage: h = H(g)
            sage: f.parent() is h.parent()
            True

        See other examples in the module-level documentation.

        TESTS::

            sage: V = GF(3)^0
            sage: W = GF(3)^1
            sage: H = V.Hom(W)
            sage: H.zero().is_zero()
            True

        Previously the above code resulted in a TypeError because the
        dimensions of the matrix were incorrect.
        """
        from .vector_space_morphism import is_VectorSpaceMorphism, VectorSpaceMorphism
        D = self.domain()
        C = self.codomain()
        from sage.matrix.matrix import is_Matrix
        if is_Matrix(A):
            pass
        elif is_VectorSpaceMorphism(A):
            A = A.matrix()
        elif inspect.isfunction(A):
            try:
                images = [A(g) for g in D.basis()]
            except (ValueError, TypeError, IndexError) as e:
                msg = 'function cannot be applied properly to some basis element because\n' + e.args[0]
                raise ValueError(msg)
            try:
                A = matrix.matrix(D.dimension(), C.dimension(), [C.coordinates(C(a)) for a in images])
            except (ArithmeticError, TypeError) as e:
                msg = 'some image of the function is not in the codomain, because\n' + e.args[0]
                raise ArithmeticError(msg)
        elif isinstance(A, (list, tuple)):
            if len(A) != len(D.basis()):
                msg = "number of images should equal the size of the domain's basis (={0}), not {1}"
                raise ValueError(msg.format(len(D.basis()), len(A)))
            try:
                v = [C(a) for a in A]
                A = matrix.matrix(D.dimension(), C.dimension(), [C.coordinates(a) for a in v])
            except (ArithmeticError, TypeError) as e:
                msg = 'some proposed image is not in the codomain, because\n' + e.args[0]
                raise ArithmeticError(msg)
        else:
            msg = 'vector space homspace can only coerce matrices, vector space morphisms, functions or lists, not {0}'
            raise TypeError(msg.format(A))
        return VectorSpaceMorphism(self, A)