示例#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()
        """
        try:
            A = A.matrix()
        except AttributeError:
            pass
        if is_Matrix(A) and A.nrows() == A.ncols() == parent.degree()+1:
            g = A
            d = parent.degree()
            A = g.submatrix(0, 0, d, d)
            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
示例#2
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()
        """
        try:
            A = A.matrix()
        except AttributeError:
            pass
        if is_Matrix(A) and A.nrows() == A.ncols() == parent.degree()+1:
            g = A
            d = parent.degree()
            A = g.submatrix(0, 0, d, d)
            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
示例#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)
示例#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)
示例#5
0
def sudoku(m):
    r"""
    Solves Sudoku puzzles described by matrices.

    INPUT:

    - ``m`` - a square Sage matrix over `\ZZ`, where zeros are blank entries

    OUTPUT:

    A Sage matrix over `\ZZ` containing the first solution found,
    otherwise ``None``.

    This function matches the behavior of the prior Sudoku solver
    and is included only to replicate that behavior.  It could be
    safely deprecated, since all of its functionality is included in the :class:`~sage.games.sudoku.Sudoku` class.

    EXAMPLES:

    An example that was used in previous doctests. ::

        sage: A = matrix(ZZ,9,[5,0,0, 0,8,0, 0,4,9, 0,0,0, 5,0,0, 0,3,0, 0,6,7, 3,0,0, 0,0,1, 1,5,0, 0,0,0, 0,0,0,  0,0,0, 2,0,8, 0,0,0, 0,0,0, 0,0,0, 0,1,8, 7,0,0, 0,0,4, 1,5,0, 0,3,0, 0,0,2, 0,0,0, 4,9,0, 0,5,0, 0,0,3])
        sage: A
        [5 0 0 0 8 0 0 4 9]
        [0 0 0 5 0 0 0 3 0]
        [0 6 7 3 0 0 0 0 1]
        [1 5 0 0 0 0 0 0 0]
        [0 0 0 2 0 8 0 0 0]
        [0 0 0 0 0 0 0 1 8]
        [7 0 0 0 0 4 1 5 0]
        [0 3 0 0 0 2 0 0 0]
        [4 9 0 0 5 0 0 0 3]
        sage: sudoku(A)
        [5 1 3 6 8 7 2 4 9]
        [8 4 9 5 2 1 6 3 7]
        [2 6 7 3 4 9 5 8 1]
        [1 5 8 4 6 3 9 7 2]
        [9 7 4 2 1 8 3 6 5]
        [3 2 6 7 9 5 4 1 8]
        [7 8 2 9 3 4 1 5 6]
        [6 3 5 1 7 2 8 9 4]
        [4 9 1 8 5 6 7 2 3]

    Using inputs that are possible with the
    :class:`~sage.games.sudoku.Sudoku` class,
    other than a matrix, will cause an error. ::

        sage: sudoku('.4..32....14..3.')
        Traceback (most recent call last):
        ...
        ValueError: sudoku function expects puzzle to be a matrix, perhaps use the Sudoku class
    """
    from sage.structure.element import is_Matrix

    if not is_Matrix(m):
        raise ValueError(
            'sudoku function expects puzzle to be a matrix, perhaps use the Sudoku class'
        )
    solution = next(Sudoku(m).solve(algorithm='dlx'))
    return (solution.to_matrix() if solution else None)
示例#6
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 (default: ``False``);
      whether to consider the graph as having loops, multiple edges, or weights

    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.structure.element 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 __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 __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)
示例#9
0
    def __rmul__(self,matrix):
        r"""
        EXAMPLES::

            sage: from flatsurf import *
            sage: s=translation_surfaces.infinite_staircase()
            sage: s.underlying_surface()
            The infinite staircase
            sage: m=Matrix([[1,2],[0,1]])
            sage: s2=m*s
            sage: TestSuite(s2).run(skip='_test_pickling')
            sage: s2.polygon(0)
            Polygon: (0, 0), (1, 0), (3, 1), (2, 1)

        Testing multiplication by a matrix with negative determinant::

            sage: from flatsurf import *
            sage: ds1 = dilation_surfaces.genus_two_square(1/2, 1/3, 1/4, 1/5)
            sage: ds1.polygon(0)
            Polygon: (0, 0), (1/2, 0), (1, 1/3), (1, 1), (3/4, 1), (0, 4/5)
            sage: m = matrix(QQ, [[0, 1], [1, 0]]) # maps (x,y) to (y, x)
            sage: ds2 = m*ds1
            sage: ds2.polygon(0)
            Polygon: (0, 0), (4/5, 0), (1, 3/4), (1, 1), (1/3, 1), (0, 1/2)
        """
        if not is_Matrix(matrix):
            raise NotImplementedError("Only implemented for matrices.")
        if not matrix.dimensions!=(2,2):
            raise NotImplementedError("Only implemented for 2x2 matrices.")
        return self.__class__(GL2RImageSurface(self,matrix)).copy()
示例#10
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))
示例#11
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))
示例#12
0
    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).list() for b in A.table()])
            self._basis_matrix = B.echelon_form().image().basis_matrix()
        Ideal_generic.__init__(self, A, gens)
    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).list() for b in A.table()])
            self._basis_matrix = B.echelon_form().image().basis_matrix()
        Ideal_generic.__init__(self, A, gens)
示例#14
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
示例#15
0
文件: sudoku.py 项目: sagemath/sage
def sudoku(m):
    r"""
    Solves Sudoku puzzles described by matrices.

    INPUT:

    - ``m`` - a square Sage matrix over `\ZZ`, where zeros are blank entries

    OUTPUT:

    A Sage matrix over `\ZZ` containing the first solution found,
    otherwise ``None``.

    This function matches the behavior of the prior Sudoku solver
    and is included only to replicate that behavior.  It could be
    safely deprecated, since all of its functionality is included in the :class:`~sage.games.sudoku.Sudoku` class.

    EXAMPLES:

    An example that was used in previous doctests. ::

        sage: A = matrix(ZZ,9,[5,0,0, 0,8,0, 0,4,9, 0,0,0, 5,0,0, 0,3,0, 0,6,7, 3,0,0, 0,0,1, 1,5,0, 0,0,0, 0,0,0,  0,0,0, 2,0,8, 0,0,0, 0,0,0, 0,0,0, 0,1,8, 7,0,0, 0,0,4, 1,5,0, 0,3,0, 0,0,2, 0,0,0, 4,9,0, 0,5,0, 0,0,3])
        sage: A
        [5 0 0 0 8 0 0 4 9]
        [0 0 0 5 0 0 0 3 0]
        [0 6 7 3 0 0 0 0 1]
        [1 5 0 0 0 0 0 0 0]
        [0 0 0 2 0 8 0 0 0]
        [0 0 0 0 0 0 0 1 8]
        [7 0 0 0 0 4 1 5 0]
        [0 3 0 0 0 2 0 0 0]
        [4 9 0 0 5 0 0 0 3]
        sage: sudoku(A)
        [5 1 3 6 8 7 2 4 9]
        [8 4 9 5 2 1 6 3 7]
        [2 6 7 3 4 9 5 8 1]
        [1 5 8 4 6 3 9 7 2]
        [9 7 4 2 1 8 3 6 5]
        [3 2 6 7 9 5 4 1 8]
        [7 8 2 9 3 4 1 5 6]
        [6 3 5 1 7 2 8 9 4]
        [4 9 1 8 5 6 7 2 3]

    Using inputs that are possible with the
    :class:`~sage.games.sudoku.Sudoku` class,
    other than a matrix, will cause an error. ::

        sage: sudoku('.4..32....14..3.')
        Traceback (most recent call last):
        ...
        ValueError: sudoku function expects puzzle to be a matrix, perhaps use the Sudoku class
    """
    from sage.structure.element import is_Matrix

    if not is_Matrix(m):
        raise ValueError('sudoku function expects puzzle to be a matrix, perhaps use the Sudoku class')
    solution = next(Sudoku(m).solve(algorithm='dlx'))
    return (solution.to_matrix() if solution else None)
示例#16
0
    def __init__(self,
                 parent,
                 matrix,
                 inhomogeneous,
                 is_zero=lambda p: False,
                 relations=[]):
        ## Checking the input of matrix and vector
        if (not SAGE_element.is_Matrix(matrix)):
            matrix = Matrix(matrix)

        if (not SAGE_element.is_Vector(inhomogeneous)):
            inhomogeneous = vector(inhomogeneous)

        if (isinstance(parent, Wrap_w_Sequence_Ring)):
            parent = parent.base()

        ## Building the parent of the matrix and the vector
        pmatrix = matrix.parent().base()
        pinhom = inhomogeneous.parent().base()

        ## Setting the variables for the matrix, the parent and the vector
        self.__parent = pushout(pmatrix, pinhom)
        self.__matrix = matrix.change_ring(self.__parent)
        self.__inhomogeneous = inhomogeneous.change_ring(self.__parent)

        ## Setting the parent for the solutions
        if (not pushout(parent, self.__parent) == parent):
            try:
                self.__matrix = self.__matrix.change_ring(parent)
                self.__inhomogeneous = self.__inhomogeneous.change_ring(parent)
                self.__parent = parent
            except:
                raise TypeError(
                    "The parent for the solutions must be an extension of the parent for the input"
                )
        self.__solution_parent = parent

        ## Setting other variables
        if (relations is None):
            relations = []
        self.__relations = [self.__parent(el) for el in relations]
        try:
            self.__gb = ideal(self.parent(), self.__relations).groebner_basis()
        except AttributeError:
            try:
                self.__gb = [ideal(self.parent(), self.__relations).gen()]
            except:
                self.__gb = [0]

        self.__is_zero = is_zero

        ## Creating the variables for the echelon form
        self.__echelon = None
        self.__transformation = None

        ## Creating the variables for the solutions
        self.__solution = None
        self.__syzygy = None
示例#17
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
示例#18
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 (default: ``False``);
      whether to consider the graph as having loops, multiple edges, or weights

    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.structure.element 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)
示例#19
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 (default: ``False``);
      whether to consider the graph as having loops, multiple edges, or weights

    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.structure.element 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 from_matrix_representation(w, base_field=None, basis=None):
    r"""
    Return a vector representation of a matrix ``w`` over ``base_field`` in terms
    of ``basis``.

    Given an `m \times n` matrix over `F_q` and some ``basis`` of `F_{q^m}`
    over `F_q`, we can represent each of its columns as an element of `F_{q^m}`,
    yielding a vector of length `n` over `F_q`.

    In case ``base_field`` is not given, we take `F_{q^m}`, the field extension of
    `F_q` of degree `m`, the number of rows of ``w``.

    INPUT:

    - ``w`` -- a matrix over some field `F_q`

    - ``base_field`` -- (default: ``None``) an extension field of `F_q`. If not
      specified, it is the field `F_{q^m}`, where `m` is the number of rows of
      ``w``.

    - ``basis`` -- (default: ``None``) a basis of `F_{q^m}` as a vector space over
      ``F_q``. If not specified, given that `q = p^s`, let
      `1,\beta,\ldots,\beta^{sm}` be the power basis that SageMath uses to
      represent `F_{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}`.

    EXAMPLES::

        sage: from sage.coding.linear_rank_metric import from_matrix_representation
        sage: m = Matrix(GF(4), [[1, 1, 1], [1, 1, 0], [0, 0, 0]])
        sage: from_matrix_representation(m)
        (z6 + 1, z6 + 1, 1)

        sage: v = vector(GF(4), (1, 0, 0))
        sage: from_matrix_representation(v)
        Traceback (most recent call last):
        ...
        TypeError: Input must be a matrix
    """
    if not is_Matrix(w):
        raise TypeError("Input must be a matrix")
    sub_field = w.base_ring()
    if not base_field:
        base_field = sub_field.extension(w.nrows())
    v = []
    extension, to_big_field, from_big_field = base_field.vector_space(
        sub_field, basis, map=True)
    for i in range(w.ncols()):
        v.append(to_big_field(w.column(i)))
    return vector(v)
示例#21
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.structure.element 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)
示例#22
0
    def __contains__(self, x):
        r"""
        Tests if ``x`` is an element of ``self``.

        INPUT:

        - ``x`` -- matrix

        EXAMPLES::

            sage: from sage.combinat.integer_matrices import IntegerMatrices
            sage: IM = IntegerMatrices([4], [1,2,1])
            sage: matrix([[1, 2, 1]]) in IM
            True
            sage: matrix(QQ, [[1, 2, 1]]) in IM
            True
            sage: matrix([[2, 1, 1]]) in IM
            False

        TESTS::

            sage: from sage.combinat.integer_matrices import IntegerMatrices
            sage: IM = IntegerMatrices([4], [1,2,1])
            sage: [1, 2, 1] in IM
            False
            sage: matrix([[-1, 3, 1]]) in IM
            False
        """
        from sage.structure.element import is_Matrix
        if not is_Matrix(x):
            return False
        row_sums = [ZZ.zero()] * x.nrows()
        col_sums = [ZZ.zero()] * x.ncols()
        for i in range(x.nrows()):
            for j in range(x.ncols()):
                x_ij = x[i, j]
                if x_ij not in ZZ or x_ij < 0:
                    return False
                row_sums[i] += x_ij
                col_sums[j] += x_ij
            if row_sums[i] != self._row_sums[i]:
                return False
        if col_sums != self._col_sums:
            return False
        return True
示例#23
0
    def __contains__(self, x):
        r"""
        Tests if ``x`` is an element of ``self``.

        INPUT:

        - ``x`` -- matrix

        EXAMPLES::

            sage: from sage.combinat.integer_matrices import IntegerMatrices
            sage: IM = IntegerMatrices([4], [1,2,1])
            sage: matrix([[1, 2, 1]]) in IM
            True
            sage: matrix(QQ, [[1, 2, 1]]) in IM
            True
            sage: matrix([[2, 1, 1]]) in IM
            False

        TESTS::

            sage: from sage.combinat.integer_matrices import IntegerMatrices
            sage: IM = IntegerMatrices([4], [1,2,1])
            sage: [1, 2, 1] in IM
            False
            sage: matrix([[-1, 3, 1]]) in IM
            False
        """
        from sage.structure.element import is_Matrix
        if not is_Matrix(x):
            return False
        row_sums = [ZZ.zero()] * x.nrows()
        col_sums = [ZZ.zero()] * x.ncols()
        for i in range(x.nrows()):
            for j in range(x.ncols()):
                x_ij = x[i, j]
                if x_ij not in ZZ or x_ij < 0:
                    return False
                row_sums[i] += x_ij
                col_sums[j] += x_ij
            if row_sums[i] != self._row_sums[i]:
                return False
        if col_sums != self._col_sums:
            return False
        return True
示例#24
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.structure.element 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("the adjacency matrix of a Seidel graph 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("the adjacency matrix of a Seidel graph must" +
                         " have only 0,1,-1 integer entries")
    if any(i == j for i, j in M.nonzero_positions()):
        raise ValueError("the adjacency matrix of a Seidel graph must" +
                         " have 0s on the main diagonal")
    if not M.is_symmetric():
        raise ValueError("the adjacency matrix of a Seidel graph must be symmetric")

    G.add_vertices(range(M.nrows()))
    G.add_edges((i, j) for i, j in M.nonzero_positions() if i <= j and M[i,j] < 0)
示例#25
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])
示例#26
0
    def __rmul__(self, matrix):
        r"""
        EXAMPLES::

            sage: from flatsurf import *
            sage: s=translation_surfaces.infinite_staircase()
            sage: s.underlying_surface()
            The infinite staircase
            sage: m=Matrix([[1,2],[0,1]])
            sage: s2=m*s
            sage: TestSuite(s2).run(skip='_test_pickling')
            sage: s2.polygon(0)
            Polygon: (0, 0), (1, 0), (3, 1), (2, 1)
        """
        if not is_Matrix(matrix):
            raise NotImplementedError("Only implemented for matrices.")
        if not matrix.dimensions != (2, 2):
            raise NotImplementedError("Only implemented for 2x2 matrices.")
        return self.__class__(GL2RImageSurface(self, matrix)).copy()
示例#27
0
    def __init__(self, A, B, Title, C=[]):
        if not A.parent().is_exact():
            raise MustBeExact("RungeKutta: parent of A is not exact")
        if not B.parent().is_exact():
            raise MustBeExact("RungeKutta: parent of B is not exact")
        if not is_Matrix(A):
            raise NotA("RungeKutta: A is not a matrix")
        if not is_Vector(B):
            raise NotA("RungeKutta: B is not a vector")
        if  A.dimensions()[0] != A.dimensions()[1]\
            or A.dimensions()[0] != len(B):
            raise DimensionsAreIncompatible(A, B, C)
        if C != [] and len(C) != A.dimensions()[0]:
            raise DimensionsAreIncompatible(A, B, C)

        self.A = A
        self.B = B
        self.Title = Title
        self.C = C
    def __rmul__(self,matrix):
        r"""
        EXAMPLES::

            sage: from flatsurf import *
            sage: s=translation_surfaces.infinite_staircase()
            sage: s.underlying_surface()
            The infinite staircase
            sage: m=Matrix([[1,2],[0,1]])
            sage: s2=m*s
            sage: TestSuite(s2).run(skip='_test_pickling')
            sage: s2.polygon(0)
            Polygon: (0, 0), (1, 0), (3, 1), (2, 1)
        """
        if not is_Matrix(matrix):
            raise NotImplementedError("Only implemented for matrices.")
        if not matrix.dimensions!=(2,2):
            raise NotImplementedError("Only implemented for 2x2 matrices.")
        return self.__class__(GL2RImageSurface(self,matrix)).copy()
示例#29
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])
示例#30
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.structure.element 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
示例#31
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 range(n):
        if M[i,i] != 2:
            return False
        for j in range(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
示例#32
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 range(n):
        if M[i, i] != 2:
            return False
        for j in range(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
示例#33
0
def is_borcherds_cartan_matrix(M):
    """
    Return ``True`` if ``M`` is an even, integral Borcherds-Cartan matrix.
    For a definition of such a matrix, see :class:`CartanMatrix`.

    EXAMPLES::

        sage: from sage.combinat.root_system.cartan_matrix import is_borcherds_cartan_matrix
        sage: M = Matrix([[2,-1],[-1,2]])
        sage: is_borcherds_cartan_matrix(M)
        True
        sage: N = Matrix([[2,-1],[-1,0]])
        sage: is_borcherds_cartan_matrix(N)
        False
        sage: O = Matrix([[2,-1],[-1,-2]])
        sage: is_borcherds_cartan_matrix(O)
        True
        sage: O = Matrix([[2,-1],[-1,-3]])
        sage: is_borcherds_cartan_matrix(O)
        False
    """
    if not is_Matrix(M):
        return False
    if not M.is_square():
        return False
    n = M.ncols()
    for i in range(n):
        if M[i,i] == 0:
            return False
        if M[i,i] % 2 == 1:
            return False
        for j in range(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
示例#34
0
def is_borcherds_cartan_matrix(M):
    """
    Return ``True`` if ``M`` is an even, integral Borcherds-Cartan matrix.
    For a definition of such a matrix, see :class:`CartanMatrix`.

    EXAMPLES::

        sage: from sage.combinat.root_system.cartan_matrix import is_borcherds_cartan_matrix
        sage: M = Matrix([[2,-1],[-1,2]])
        sage: is_borcherds_cartan_matrix(M)
        True
        sage: N = Matrix([[2,-1],[-1,0]])
        sage: is_borcherds_cartan_matrix(N)
        False
        sage: O = Matrix([[2,-1],[-1,-2]])
        sage: is_borcherds_cartan_matrix(O)
        True
        sage: O = Matrix([[2,-1],[-1,-3]])
        sage: is_borcherds_cartan_matrix(O)
        False
    """
    if not is_Matrix(M):
        return False
    if not M.is_square():
        return False
    n = M.ncols()
    for i in range(n):
        if M[i, i] == 0:
            return False
        if M[i, i] % 2 == 1:
            return False
        for j in range(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
示例#35
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.structure.element 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
示例#36
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.structure.element 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)
示例#37
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.structure.element 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)
示例#38
0
    def simplify(self, obj):
        r'''
            Method to simplify an object using the relations found.

            This method simplifies an object (either an element, vector, list, tuple or matrix)
            using the relations that we found during the computation of the solutions of this linear
            system.

            WARNING: repeated executions of this method may return different outputs since we may have
            found more relations.
        '''
        if (SAGE_element.is_Matrix(obj)):
            return Matrix(obj.parent().base(),
                          [[self.simplify(el) for el in row] for row in obj])
        elif (SAGE_element.is_Vector(obj)):
            return vector(obj.parent().base(),
                          [self.simplify(el) for el in obj])
        elif (isinstance(obj, list)):
            return [self.simplify(el) for el in obj]
        elif (isinstance(obj, tuple)):
            return tuple([self.simplify(el) for el in obj])
        elif (self.have_ideal()):
            return obj.reduce(self.__gb)
        return obj
示例#39
0
文件: cusps.py 项目: yjjcc/sage
    def _acted_upon_(self, g, self_on_left):
        r"""
        Implement 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 == g.nrows()):
                a, b, c, d = g.list()
                return Cusp(a * self.__a + b * self.__b,
                            c * self.__a + d * self.__b)
    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)
示例#41
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)

        from sage.categories.map import Map
        from sage.categories.all import Rings
        if isinstance(x, Map) and x.category_for().is_subcategory(Rings()):
            # x is a morphism of Rings
            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)

        if is_Matrix(x):
            x = FanMorphism(x, self.domain().fan(), self.codomain().fan())
        if isinstance(x, FanMorphism):
            if x.is_dominant():
                from sage.schemes.toric.morphism import SchemeMorphism_fan_toric_variety_dominant
                return SchemeMorphism_fan_toric_variety_dominant(self,
                                                                 x,
                                                                 check=check)
            else:
                from sage.schemes.toric.morphism import SchemeMorphism_fan_toric_variety
                return SchemeMorphism_fan_toric_variety(self, x, check=check)

        raise TypeError(
            "x must be a fan morphism or a list/tuple of polynomials")
示例#42
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)
        
        The identity map between two representations of the same conic:
        
        ::
        
            sage: C = Conic([1,2,3,4,5,6])
            sage: D = Conic([2,4,6,8,10,12])
            sage: C.hom(identity_matrix(3), D)
            Scheme morphism:
              From: Projective Conic Curve over Rational Field defined by x^2 + 2*x*y + 4*y^2 + 3*x*z + 5*y*z + 6*z^2
              To:   Projective Conic Curve over Rational Field defined by 2*x^2 + 4*x*y + 8*y^2 + 6*x*z + 10*y*z + 12*z^2
              Defn: Defined on coordinates by sending (x : y : z) to
                    (x : y : z)

        An example not over the rational numbers:
        
        ::
        
            sage: P.<t> = QQ[]
            sage: C = Conic([1,0,0,t,0,1/t])
            sage: D = Conic([1/t^2, 0, -2/t^2, t, 0, (t + 1)/t^2])
            sage: T = Matrix([[t,0,1],[0,1,0],[0,0,1]])
            sage: C.hom(T, D)
            Scheme morphism:
              From: Projective Conic Curve over Fraction Field of Univariate Polynomial Ring in t over Rational Field defined by x^2 + t*y^2 + 1/t*z^2
              To:   Projective Conic Curve over Fraction Field of Univariate Polynomial Ring in t over Rational Field defined by 1/t^2*x^2 + t*y^2 + (-2/t^2)*x*z + ((t + 1)/t^2)*z^2
              Defn: Defined on coordinates by sending (x : y : z) to
                    (t*x + z : y : z)

        """
        if is_Matrix(x):
            from .constructor import Conic
            y = x.inverse()
            A = y.transpose()*self.matrix()*y
            im = Conic(A)
            if Y is None:
                Y = im
            elif not Y == im:
                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 ProjectivePlaneCurve.hom(self, x, Y)
示例#43
0
    def __call__(self, M):
        r"""
        Create a homomorphism in this space from M. M can be any of the
        following:

        - a Morphism of abelian varieties

        - a matrix of the appropriate size
          (i.e. 2\*self.domain().dimension() x
          2\*self.codomain().dimension()) whose entries are coercible
          into self.base_ring()

        - anything that can be coerced into self.matrix_space()

        EXAMPLES::

            sage: H = Hom(J0(11), J0(22))
            sage: phi = H(matrix(ZZ,2,4,[5..12])) ; phi
            Abelian variety morphism:
              From: Abelian variety J0(11) of dimension 1
              To:   Abelian variety J0(22) of dimension 2
            sage: phi.matrix()
            [ 5  6  7  8]
            [ 9 10 11 12]
            sage: phi.matrix().parent()
            Full MatrixSpace of 2 by 4 dense matrices over Integer Ring

        ::

            sage: H = J0(22).Hom(J0(11)*J0(11))
            sage: m1 = J0(22).degeneracy_map(11,1).matrix() ; m1
            [ 0  1]
            [-1  1]
            [-1  0]
            [ 0 -1]
            sage: m2 = J0(22).degeneracy_map(11,2).matrix() ; m2
            [ 1 -2]
            [ 0 -2]
            [ 1 -1]
            [ 0 -1]
            sage: m = m1.transpose().stack(m2.transpose()).transpose() ; m
            [ 0  1  1 -2]
            [-1  1  0 -2]
            [-1  0  1 -1]
            [ 0 -1  0 -1]
            sage: phi = H(m) ; phi
            Abelian variety morphism:
              From: Abelian variety J0(22) of dimension 2
              To:   Abelian variety J0(11) x J0(11) of dimension 2
            sage: phi.matrix()
            [ 0  1  1 -2]
            [-1  1  0 -2]
            [-1  0  1 -1]
            [ 0 -1  0 -1]
        """
        if isinstance(M, morphism.Morphism):
            if M.parent() is self:
                return M
            elif M.domain() == self.domain() and M.codomain() == self.codomain():
                M = M.matrix()
            else:
                raise ValueError("cannot convert %s into %s" % (M, self))
        elif is_Matrix(M):
            if M.base_ring() != ZZ:
                M = M.change_ring(ZZ)
            if M.nrows() != 2*self.domain().dimension() or M.ncols() != 2*self.codomain().dimension():
                raise TypeError("matrix has wrong dimension")
        elif self.matrix_space().has_coerce_map_from(parent(M)):
            M = self.matrix_space()(M)
        else:
            raise TypeError("can only coerce in matrices or morphisms")
        return self.element_class(self, M)
示例#44
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 __classcall_private__(cls, k, table, names='e', assume_associative=False,
                              category=None):
        """
        Normalize input.

        TESTS::

            sage: table = [Matrix([[1, 0], [0, 1]]), Matrix([[0, 1], [0, 0]])]
            sage: A1 = FiniteDimensionalAlgebra(GF(3), table)
            sage: A2 = FiniteDimensionalAlgebra(GF(3), table, names='e')
            sage: A3 = FiniteDimensionalAlgebra(GF(3), table, names=['e0', 'e1'])
            sage: A1 is A2 and A2 is A3
            True

        The ``assume_associative`` keyword is built into the category::

            sage: from sage.categories.magmatic_algebras import MagmaticAlgebras
            sage: cat = MagmaticAlgebras(GF(3)).FiniteDimensional().WithBasis()
            sage: A1 = FiniteDimensionalAlgebra(GF(3), table, category=cat.Associative())
            sage: A2 = FiniteDimensionalAlgebra(GF(3), table, assume_associative=True)
            sage: A1 is A2
            True

        Uniqueness depends on the category::

            sage: cat = Algebras(GF(3)).FiniteDimensional().WithBasis()
            sage: A1 = FiniteDimensionalAlgebra(GF(3), table)
            sage: A2 = FiniteDimensionalAlgebra(GF(3), table, category=cat)
            sage: A1 == A2
            False
            sage: A1 is A2
            False

        Checking that equality is still as expected::

            sage: A = FiniteDimensionalAlgebra(GF(3), table)
            sage: B = FiniteDimensionalAlgebra(GF(5), [Matrix([0])])
            sage: A == A
            True
            sage: B == B
            True
            sage: A == B
            False
            sage: A != A
            False
            sage: B != B
            False
            sage: A != B
            True
        """
        n = len(table)
        table = [b.base_extend(k) for b in table]
        for b in table:
            b.set_immutable()
            if not (is_Matrix(b) and b.dimensions() == (n, n)):
                raise ValueError("input is not a multiplication table")
        table = tuple(table)

        cat = MagmaticAlgebras(k).FiniteDimensional().WithBasis()
        cat = cat.or_subcategory(category)
        if assume_associative:
            cat = cat.Associative()

        names = normalize_names(n, names)

        return super(FiniteDimensionalAlgebra, cls).__classcall__(cls, k, table,
                             names, category=cat)
示例#46
0
def gens_to_basis_matrix(syms, relation_matrix, mod, field, sparse):
    """
    Compute echelon form of 3-term relation matrix, and read off each
    generator in terms of basis.

    INPUT:

    - ``syms`` -- :class:`ManinSymbolList`

    -  ``relation_matrix`` - as output by
       ``__compute_T_relation_matrix(self, mod)``

    -  ``mod`` - quotient of modular symbols modulo the
       2-term S (and possibly I) relations

    -  ``field`` - base field

    -  ``sparse`` - (bool): whether or not matrix should be
       sparse

    OUTPUT:

    -  ``matrix`` - a matrix whose ith row expresses the
       Manin symbol generators in terms of a basis of Manin symbols
       (modulo the S, (possibly I,) and T rels) Note that the entries of
       the matrix need not be integers.

    -  ``list`` - integers i, such that the Manin symbols `x_i` are a basis.

    EXAMPLES::

        sage: from sage.modular.modsym.relation_matrix import sparse_2term_quotient, T_relation_matrix_wtk_g0, gens_to_basis_matrix, modS_relations
        sage: L = sage.modular.modsym.manin_symbol_list.ManinSymbolList_gamma1(4, 3)
        sage: modS = sparse_2term_quotient(modS_relations(L), 24, GF(3))
        sage: gens_to_basis_matrix(L, T_relation_matrix_wtk_g0(L, modS, GF(3), 24), modS, GF(3), True)
        (24 x 2 sparse matrix over Finite Field of size 3, [13, 23])
    """
    from sage.structure.element import is_Matrix
    if not is_Matrix(relation_matrix):
        raise TypeError("relation_matrix must be a matrix")
    if not isinstance(mod, list):
        raise TypeError("mod must be a list")

    misc.verbose(str(relation_matrix.parent()))

    try:
        h = relation_matrix.height()
    except AttributeError:
        h = 9999999
    tm = misc.verbose("putting relation matrix in echelon form (height = %s)"%h)
    if h < 10:
        A = relation_matrix.echelon_form(algorithm='multimodular', height_guess=1)
    else:
        A = relation_matrix.echelon_form()
    A.set_immutable()
    tm = misc.verbose('finished echelon', tm)

    tm = misc.verbose("Now creating gens --> basis mapping")

    basis_set = set(A.nonpivots())
    pivots = A.pivots()

    basis_mod2 = set([j for j,c in mod if c != 0])

    basis_set = basis_set.intersection(basis_mod2)
    basis = sorted(basis_set)

    ONE = field(1)

    misc.verbose("done doing setup",tm)


    tm = misc.verbose("now forming quotient matrix")
    M = matrix_space.MatrixSpace(field, len(syms), len(basis), sparse=sparse)

    B = M(0)
    cols_index = dict([(basis[i], i) for i in range(len(basis))])

    for i in basis_mod2:
        t, l = search(basis, i)
        if t:
            B[i,l] = ONE
        else:
            _, r = search(pivots, i)    # so pivots[r] = i
            # Set row i to -(row r of A), but where we only take
            # the non-pivot columns of A:
            B._set_row_to_negative_of_row_of_A_using_subset_of_columns(i, A, r, basis, cols_index)

    misc.verbose("done making quotient matrix",tm)

    # The following is very fast (over Q at least).
    tm = misc.verbose('now filling in the rest of the matrix')
    k = 0
    for i in range(len(mod)):
        j, s = mod[i]
        if j != i and s != 0:   # ignored in the above matrix
            k += 1
            B.set_row_to_multiple_of_row(i, j, s)
    misc.verbose("set %s rows"%k)
    tm = misc.verbose("time to fill in rest of matrix", tm)

    return B, basis
示例#47
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 (default: ``False``);
      whether to consider the graph as having loops, multiple edges, or weights

    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
    
    Handle incidence matrix containing a column with only zeros (:trac:`29275`)::

        sage: m = Matrix([[0,1],[0,-1],[0,0]])
        sage: m
        [ 0  1]
        [ 0 -1]
        [ 0  0]
        sage: G = DiGraph(m,format='incidence_matrix')
        sage: list(G.edges(labels=False))
        [(1, 0)]

    Handle incidence matrix [[1],[-1]] (:trac:`29275`)::

        sage: m = Matrix([[1],[-1]])
        sage: m
        [ 1]
        [-1]
        sage: G = DiGraph(m,format='incidence_matrix')
        sage: list(G.edges(labels=False))
        [(1, 0)]
    """
    from sage.structure.element import is_Matrix
    assert is_Matrix(M)

    positions = []
    for c in M.columns():
        NZ = c.nonzero_positions()
        if not NZ:
            continue
        if len(NZ) != 2:
            raise ValueError(
                "there must be two nonzero entries (-1 & 1) per column")
        L = sorted([c[i] for i in NZ])
        if L != [-1, 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)
示例#48
0
def from_adjacency_matrix(G, M, loops=False, multiedges=False, weighted=False):
    r"""
    Fill ``G`` with the data of an adjacency matrix.

    INPUT:

    - ``G`` -- a :class:`Graph` or :class:`DiGraph`

    - ``M`` -- an adjacency matrix

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

    EXAMPLES::

        sage: from sage.graphs.graph_input import from_adjacency_matrix
        sage: g = Graph()
        sage: from_adjacency_matrix(g, graphs.PetersenGraph().adjacency_matrix())
        sage: g.is_isomorphic(graphs.PetersenGraph())
        True
    """
    from sage.structure.element import is_Matrix
    from sage.rings.integer_ring import ZZ
    assert is_Matrix(M)
    # note: the adjacency matrix might be weighted and hence not
    # necessarily consists of integers
    if not weighted and M.base_ring() != ZZ:
        try:
            M = M.change_ring(ZZ)
        except TypeError:
            if weighted is False:
                raise ValueError("the adjacency matrix of a non-weighted graph" +
                                 " must have only nonnegative integer entries")
            weighted = True

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

    if not weighted and any(e < 0 for e in entries):
        if weighted is False:
            raise ValueError("the adjacency matrix of a non-weighted graph" +
                             " must have only nonnegative integer entries")
        weighted = True
        if multiedges is None:
            multiedges = False
    if weighted is None:
        weighted = False

    if multiedges is None:
        multiedges = ((not weighted) and any(e != 0 and e != 1 for e in entries))

    if not loops and any(M[i,i] for i in range(M.nrows())):
        if loops is False:
            raise ValueError("the adjacency matrix of a non-weighted graph" +
                             " must have zeroes on the diagonal")
        loops = True
    if loops is None:
        loops = False
    G.allow_loops(loops, check=False)
    G.allow_multiple_edges(multiedges, check=False)
    G.add_vertices(range(M.nrows()))
    if G.is_directed():
        pairs = M.nonzero_positions()
    else:
        pairs = ((i, j) for i, j in M.nonzero_positions() if i <= j)
    if weighted:
        G.add_edges((i, j, M[i][j]) for i, j in pairs)
    elif multiedges:
        G.add_edges((i, j) for i, j in pairs for _ in range(int(M[i][j])))
    else:
        G.add_edges((i, j) for i, j in pairs)
    G._weighted = weighted
示例#49
0
    def __init__(self, data=None, partition=None, check=True, *args, **kwds):
        """
        Create a bipartite graph. See documentation ``BipartiteGraph?`` for
        detailed information.

        EXAMPLE::

            sage: P = graphs.PetersenGraph()
            sage: partition = [range(5), range(5,10)]
            sage: B = BipartiteGraph(P, partition, check=False)
        """
        if data is None:
            if partition != None and check:
                if partition[0] or partition[1]:
                    raise ValueError("Invalid partition.")
            Graph.__init__(self, **kwds)
            self.left = set()
            self.right = set()
            return

        # need to turn off partition checking for Graph.__init__() adding
        # vertices and edges; methods are restored ad the end of big "if"
        # statement below
        import types
        self.add_vertex = types.MethodType(Graph.add_vertex,
                                           self,
                                           BipartiteGraph)
        self.add_vertices = types.MethodType(Graph.add_vertices,
                                             self,
                                             BipartiteGraph)
        self.add_edge = types.MethodType(Graph.add_edge, self, BipartiteGraph)

        from sage.structure.element import is_Matrix
        if isinstance(data, BipartiteGraph):
            Graph.__init__(self, data, *args, **kwds)
            self.left = set(data.left)
            self.right = set(data.right)
        elif isinstance(data, str):
            Graph.__init__(self, *args, **kwds)
            # will call self.load_afile after restoring add_vertex() instance
            # methods; initialize left and right attributes
            self.left = set()
            self.right = set()
        elif is_Matrix(data):
            # sanity check for mutually exclusive keywords
            if kwds.get("multiedges", False) and kwds.get("weighted", False):
                raise TypeError(
                    "Weighted multi-edge bipartite graphs from reduced " +
                    "adjacency matrix not supported.")
            Graph.__init__(self, *args, **kwds)
            ncols = data.ncols()
            nrows = data.nrows()
            self.left = set(xrange(ncols))
            self.right = set(xrange(ncols, nrows + ncols))

            # ensure that the vertices exist even if there
            # are no associated edges (trac #10356)
            self.add_vertices(self.left)
            self.add_vertices(self.right)

            if kwds.get("multiedges", False):
                for ii in range(ncols):
                    for jj in range(nrows):
                        if data[jj][ii] != 0:
                            self.add_edges([(ii, jj + ncols)] * data[jj][ii])
            elif kwds.get("weighted", False):
                for ii in range(ncols):
                    for jj in range(nrows):
                        if data[jj][ii] != 0:
                            self.add_edge((ii, jj + ncols, data[jj][ii]))
            else:
                for ii in range(ncols):
                    for jj in range(nrows):
                        if data[jj][ii] != 0:
                            self.add_edge((ii, jj + ncols))
        elif (isinstance(data, Graph) and partition != None):
            from copy import copy
            left, right = partition
            left = copy(left)
            right = copy(right)
            verts = set(left) | set(right)
            if set(data.vertices()) != verts:
                data = data.subgraph(list(verts))
            Graph.__init__(self, data, *args, **kwds)
            if check:
                while len(left) > 0:
                    a = left.pop(0)
                    if len(set(data.neighbors(a)) & set(left)) != 0:
                        raise TypeError(
                            "Input graph is not bipartite with " +
                            "respect to the given partition!")
                while len(right) > 0:
                    a = right.pop(0)
                    if len(set(data.neighbors(a)) & set(right)) != 0:
                        raise TypeError(
                            "Input graph is not bipartite with " +
                            "respect to the given partition!")
            else:
                while len(left) > 0:
                    a = left.pop(0)
                    a_nbrs = set(data.neighbors(a)) & set(left)
                    if len(a_nbrs) != 0:
                        self.delete_edges([(a, b) for b in a_nbrs])
                while len(right) > 0:
                    a = right.pop(0)
                    a_nbrs = set(data.neighbors(a)) & set(right)
                    if len(a_nbrs) != 0:
                        self.delete_edges([(a, b) for b in a_nbrs])
            self.left, self.right = set(partition[0]), set(partition[1])
        elif isinstance(data, Graph):
            Graph.__init__(self, data, *args, **kwds)
            try:
                self.left, self.right = self.bipartite_sets()
            except StandardError:
                raise TypeError("Input graph is not bipartite!")
        else:
            import networkx
            Graph.__init__(self, data, *args, **kwds)
            if isinstance(data, (networkx.MultiGraph, networkx.Graph)):
                if hasattr(data, "node_type"):
                    # Assume the graph is bipartite
                    self.left = set()
                    self.right = set()
                    for v in data.nodes_iter():
                        if data.node_type[v] == "Bottom":
                            self.left.add(v)
                        elif data.node_type[v] == "Top":
                            self.right.add(v)
                        else:
                            raise TypeError(
                                "NetworkX node_type defies bipartite " +
                                "assumption (is not 'Top' or 'Bottom')")
            # make sure we found a bipartition
            if not (hasattr(self, "left") and hasattr(self, "right")):
                try:
                    self.left, self.right = self.bipartite_sets()
                except StandardError:
                    raise TypeError("Input graph is not bipartite!")

        # restore vertex partition checking
        self.add_vertex = types.MethodType(BipartiteGraph.add_vertex,
                                           self,
                                           BipartiteGraph)
        self.add_vertices = types.MethodType(BipartiteGraph.add_vertices,
                                             self,
                                             BipartiteGraph)
        self.add_edge = types.MethodType(BipartiteGraph.add_edge,
                                         self,
                                         BipartiteGraph)

        # post-processing
        if isinstance(data, str):
            self.load_afile(data)

        return
示例#50
0
def Conic(base_field, F=None, names=None, unique=True):
    r"""
    Return the plane projective conic curve defined by ``F``
    over ``base_field``.
    
    The input form ``Conic(F, names=None)`` is also accepted,
    in which case the fraction field of the base ring of ``F``
    is used as base field.

    INPUT:
    
    - ``base_field`` -- The base field of the conic.
    
    - ``names`` -- a list, tuple, or comma separated string
      of three variable names specifying the names
      of the coordinate functions of the ambient
      space `\Bold{P}^3`. If not specified or read
      off from ``F``, then this defaults to ``'x,y,z'``.

    - ``F`` -- a polynomial, list, matrix, ternary quadratic form,
      or list or tuple of 5 points in the plane.
                   
                   If ``F`` is a polynomial or quadratic form,
                   then the output is the curve in the projective plane
                   defined by ``F = 0``.

                   If ``F`` is a polynomial, then it must be a polynomial
                   of degree at most 2 in 2 variables, or a homogeneous
                   polynomial in of degree 2 in 3 variables.
                   
                   If ``F`` is a matrix, then the output is the zero locus
                   of `(x,y,z) F (x,y,z)^t`.
    
                   If ``F`` is a list of coefficients, then it has
                   length 3 or 6 and gives the coefficients of
                   the monomials `x^2, y^2, z^2` or all 6 monomials
                   `x^2, xy, xz, y^2, yz, z^2` in lexicographic order.

                   If ``F`` is a list of 5 points in the plane, then the output
                   is a conic through those points.
      
    - ``unique`` -- Used only if ``F`` is a list of points in the plane.
      If the conic through the points is not unique, then
      raise ``ValueError`` if and only if ``unique`` is True
                    
    OUTPUT:
    
    A plane projective conic curve defined by ``F`` over a field.
    
    EXAMPLES:
    
    Conic curves given by polynomials ::

        sage: X,Y,Z = QQ['X,Y,Z'].gens()
        sage: Conic(X^2 - X*Y + Y^2 - Z^2)
        Projective Conic Curve over Rational Field defined by X^2 - X*Y + Y^2 - Z^2
        sage: x,y = GF(7)['x,y'].gens()
        sage: Conic(x^2 - x + 2*y^2 - 3, 'U,V,W')
        Projective Conic Curve over Finite Field of size 7 defined by U^2 + 2*V^2 - U*W - 3*W^2

    Conic curves given by matrices ::

        sage: Conic(matrix(QQ, [[1, 2, 0], [4, 0, 0], [7, 0, 9]]), 'x,y,z')
        Projective Conic Curve over Rational Field defined by x^2 + 6*x*y + 7*x*z + 9*z^2

        sage: x,y,z = GF(11)['x,y,z'].gens()
        sage: C = Conic(x^2+y^2-2*z^2); C
        Projective Conic Curve over Finite Field of size 11 defined by x^2 + y^2 - 2*z^2
        sage: Conic(C.symmetric_matrix(), 'x,y,z')
        Projective Conic Curve over Finite Field of size 11 defined by x^2 + y^2 - 2*z^2

    Conics given by coefficients ::
    
        sage: Conic(QQ, [1,2,3])
        Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + 3*z^2
        sage: Conic(GF(7), [1,2,3,4,5,6], 'X')
        Projective Conic Curve over Finite Field of size 7 defined by X0^2 + 2*X0*X1 - 3*X1^2 + 3*X0*X2 - 2*X1*X2 - X2^2
    
    The conic through a set of points ::

        sage: C = Conic(QQ, [[10,2],[3,4],[-7,6],[7,8],[9,10]]); C
        Projective Conic Curve over Rational Field defined by x^2 + 13/4*x*y - 17/4*y^2 - 35/2*x*z + 91/4*y*z - 37/2*z^2
        sage: C.rational_point()
        (10 : 2 : 1)
        sage: C.point([3,4])
        (3 : 4 : 1)

        sage: a=AffineSpace(GF(13),2)
        sage: Conic([a([x,x^2]) for x in range(5)])
        Projective Conic Curve over Finite Field of size 13 defined by x^2 - y*z
    """
    if not (is_IntegralDomain(base_field) or base_field == None):
        if names is None:
            names = F
        F = base_field
        base_field = None
    if isinstance(F, (list,tuple)):
        if len(F) == 1:
            return Conic(base_field, F[0], names)
        if names == None:
            names = 'x,y,z'
        if len(F) == 5:
            L=[]
            for f in F:
                if isinstance(f, SchemeMorphism_point_affine):
                    C = Sequence(f, universe = base_field)
                    if len(C) != 2:
                        raise TypeError, "points in F (=%s) must be planar"%F
                    C.append(1)
                elif isinstance(f, SchemeMorphism_point_projective_field):
                    C = Sequence(f, universe = base_field)
                elif isinstance(f, (list, tuple)):
                    C = Sequence(f, universe = base_field)
                    if len(C) == 2:
                        C.append(1)
                else:
                    raise TypeError, "F (=%s) must be a sequence of planar " \
                                      "points" % F
                if len(C) != 3:
                    raise TypeError, "points in F (=%s) must be planar" % F
                P = C.universe()
                if not is_IntegralDomain(P):
                    raise TypeError, "coordinates of points in F (=%s) must " \
                                     "be in an integral domain" % F
                L.append(Sequence([C[0]**2, C[0]*C[1], C[0]*C[2], C[1]**2,
                                   C[1]*C[2], C[2]**2], P.fraction_field()))
            M=Matrix(L)
            if unique and M.rank() != 5:
                raise ValueError, "points in F (=%s) do not define a unique " \
                                   "conic" % F
            con = Conic(base_field, Sequence(M.right_kernel().gen()), names)
            con.point(F[0])
            return con
        F = Sequence(F, universe = base_field)
        base_field = F.universe().fraction_field()
        temp_ring = PolynomialRing(base_field, 3, names)
        (x,y,z) = temp_ring.gens()
        if len(F) == 3:
            return Conic(F[0]*x**2 + F[1]*y**2 + F[2]*z**2)
        if len(F) == 6:
            return Conic(F[0]*x**2 + F[1]*x*y + F[2]*x*z + F[3]*y**2 + \
                         F[4]*y*z + F[5]*z**2)
        raise TypeError, "F (=%s) must be a sequence of 3 or 6" \
                         "coefficients" % F
    if is_QuadraticForm(F):
        F = F.matrix()
    if is_Matrix(F) and F.is_square() and F.ncols() == 3:
        if names == None:
            names = 'x,y,z'
        temp_ring = PolynomialRing(F.base_ring(), 3, names)
        F = vector(temp_ring.gens()) * F * vector(temp_ring.gens())

    if not is_MPolynomial(F):
        raise TypeError, "F (=%s) must be a three-variable polynomial or " \
                         "a sequence of points or coefficients" % F

    if F.total_degree() != 2:
        raise TypeError, "F (=%s) must have degree 2" % F

    if base_field == None:
        base_field = F.base_ring()
    if not is_IntegralDomain(base_field):
        raise ValueError, "Base field (=%s) must be a field" % base_field
    base_field = base_field.fraction_field()
    if names == None:
        names = F.parent().variable_names()
    pol_ring = PolynomialRing(base_field, 3, names)

    if F.parent().ngens() == 2:
        (x,y,z) = pol_ring.gens()
        F = pol_ring(F(x/z,y/z)*z**2)    

    if F == 0:
        raise ValueError, "F must be nonzero over base field %s" % base_field

    if F.total_degree() != 2:
        raise TypeError, "F (=%s) must have degree 2 over base field %s" % \
                          (F, base_field)

    if F.parent().ngens() == 3:
        P2 = ProjectiveSpace(2, base_field, names)
        if is_PrimeFiniteField(base_field):
            return ProjectiveConic_prime_finite_field(P2, F)
        if is_FiniteField(base_field):
            return ProjectiveConic_finite_field(P2, F)
        if is_RationalField(base_field):
            return ProjectiveConic_rational_field(P2, F)
        if is_NumberField(base_field):
            return ProjectiveConic_number_field(P2, F)
        return ProjectiveConic_field(P2, F)

    raise TypeError, "Number of variables of F (=%s) must be 2 or 3" % F
示例#51
0
def Matroid(groundset=None, data=None, **kwds):
    r"""
    Construct a matroid.

    Matroids are combinatorial structures that capture the abstract properties
    of (linear/algebraic/...) dependence. Formally, a matroid is a pair
    `M = (E, I)` of a finite set `E`, the *groundset*, and a collection of
    subsets `I`, the independent sets, subject to the following axioms:

    * `I` contains the empty set
    * If `X` is a set in `I`, then each subset of `X` is in `I`
    * If two subsets `X`, `Y` are in `I`, and `|X| > |Y|`, then there exists
      `x \in X - Y` such that `Y + \{x\}` is in `I`.

    See the :wikipedia:`Wikipedia article on matroids <Matroid>` for more
    theory and examples. Matroids can be obtained from many types of
    mathematical structures, and Sage supports a number of them.

    There are two main entry points to Sage's matroid functionality. For
    built-in matroids, do the following:

    * Within a Sage session, type "matroids." (Do not press "Enter", and do
      not forget the final period ".")
    * Hit "tab".

    You will see a list of methods which will construct matroids. For
    example::

        sage: F7 = matroids.named_matroids.Fano()
        sage: len(F7.nonspanning_circuits())
        7

    or::

        sage: U36 = matroids.Uniform(3, 6)
        sage: U36.equals(U36.dual())
        True

    To define your own matroid, use the function ``Matroid()``.
    This function attempts to interpret its arguments to create an appropriate
    matroid. The following named arguments are supported:

    INPUT:

    - ``groundset`` -- (optional) If provided, the groundset of the
      matroid. Otherwise, the function attempts to determine a groundset
      from the data.

    Exactly one of the following inputs must be given (where ``data``
    must be a positional argument and anything else must be a keyword
    argument):

    - ``data`` -- a graph or a matrix or a RevLex-Index string or a list
      of independent sets containing all bases or a matroid.
    - ``bases`` -- The list of bases (maximal independent sets) of the
      matroid.
    - ``independent_sets`` -- The list of independent sets of the matroid.
    - ``circuits`` -- The list of circuits of the matroid.
    - ``graph`` -- A graph, whose edges form the elements of the matroid.
    - ``matrix`` -- A matrix representation of the matroid.
    - ``reduced_matrix`` -- A reduced representation of the matroid: if
      ``reduced_matrix = A``
      then the matroid is represented by `[I\ \ A]` where `I` is an
      appropriately sized identity matrix.
    - ``rank_function`` -- A function that computes the rank of each subset.
      Can only be provided together with a groundset.
    - ``circuit_closures`` -- Either a list of tuples ``(k, C)`` with ``C``
      the closure of a circuit, and ``k`` the rank of ``C``, or a dictionary
      ``D`` with ``D[k]`` the set of closures of rank-``k`` circuits.
    - ``revlex`` -- the encoding as a string of ``0`` and ``*`` symbols.
      Used by [MatroidDatabase]_ and explained in [MMIB2012]_.
    - ``matroid`` -- An object that is already a matroid. Useful only with the
      ``regular`` option.

    Further options:

    - ``regular`` -- (default: ``False``) boolean. If ``True``,
      output a
      :class:`RegularMatroid <sage.matroids.linear_matroid.RegularMatroid>`
      instance such that, *if* the input defines a valid regular matroid, then
      the output represents this matroid. Note that this option can be
      combined with any type of input.
    - ``ring`` -- any ring. If provided, and the input is a ``matrix`` or
      ``reduced_matrix``, output will be a linear matroid over the ring or
      field ``ring``.
    - ``field`` -- any field. Same as ``ring``, but only fields are allowed.
    - ``check`` -- (default: ``True``) boolean. If ``True`` and
      ``regular`` is true, the output is checked to make sure it is a valid
      regular matroid.

    .. WARNING::

        Except for regular matroids, the input is not checked for validity. If
        your data does not correspond to an actual matroid, the behavior of
        the methods is undefined and may cause strange errors. To ensure you
        have a matroid, run
        :meth:`M.is_valid() <sage.matroids.matroid.Matroid.is_valid>`.

    .. NOTE::

        The ``Matroid()`` method will return instances of type
        :class:`BasisMatroid <sage.matroids.basis_matroid.BasisMatroid>`,
        :class:`CircuitClosuresMatroid <sage.matroids.circuit_closures_matroid.CircuitClosuresMatroid>`,
        :class:`LinearMatroid <sage.matroids.linear_matroid.LinearMatroid>`,
        :class:`BinaryMatroid <sage.matroids.linear_matroid.LinearMatroid>`,
        :class:`TernaryMatroid <sage.matroids.linear_matroid.LinearMatroid>`,
        :class:`QuaternaryMatroid <sage.matroids.linear_matroid.LinearMatroid>`,
        :class:`RegularMatroid <sage.matroids.linear_matroid.LinearMatroid>`, or
        :class:`RankMatroid <sage.matroids.rank_matroid.RankMatroid>`. To
        import these classes (and other useful functions) directly into Sage's
        main namespace, type::

            sage: from sage.matroids.advanced import *

        See :mod:`sage.matroids.advanced <sage.matroids.advanced>`.

    EXAMPLES:

    Note that in these examples we will often use the fact that strings are
    iterable in these examples. So we type ``'abcd'`` to denote the list
    ``['a', 'b', 'c', 'd']``.

    #.  List of bases:

        All of the following inputs are allowed, and equivalent::

            sage: M1 = Matroid(groundset='abcd', bases=['ab', 'ac', 'ad',
            ....:                                       'bc', 'bd', 'cd'])
            sage: M2 = Matroid(bases=['ab', 'ac', 'ad', 'bc', 'bd', 'cd'])
            sage: M3 = Matroid(['ab', 'ac', 'ad', 'bc', 'bd', 'cd'])
            sage: M4 = Matroid('abcd', ['ab', 'ac', 'ad', 'bc', 'bd', 'cd'])
            sage: M5 = Matroid('abcd', bases=[['a', 'b'], ['a', 'c'],
            ....:                             ['a', 'd'], ['b', 'c'],
            ....:                             ['b', 'd'], ['c', 'd']])
            sage: M1 == M2
            True
            sage: M1 == M3
            True
            sage: M1 == M4
            True
            sage: M1 == M5
            True

        We do not check if the provided input forms an actual matroid::

            sage: M1 = Matroid(groundset='abcd', bases=['ab', 'cd'])
            sage: M1.full_rank()
            2
            sage: M1.is_valid()
            False

        Bases may be repeated::

            sage: M1 = Matroid(['ab', 'ac'])
            sage: M2 = Matroid(['ab', 'ac', 'ab'])
            sage: M1 == M2
            True

    #.  List of independent sets:

        ::

            sage: M1 = Matroid(groundset='abcd',
            ....:              independent_sets=['', 'a', 'b', 'c', 'd', 'ab',
            ....:                               'ac', 'ad', 'bc', 'bd', 'cd'])

        We only require that the list of independent sets contains each basis
        of the matroid; omissions of smaller independent sets and
        repetitions are allowed::

            sage: M1 = Matroid(bases=['ab', 'ac'])
            sage: M2 = Matroid(independent_sets=['a', 'ab', 'b', 'ab', 'a',
            ....:                                'b', 'ac'])
            sage: M1 == M2
            True

    #.  List of circuits:

        ::

            sage: M1 = Matroid(groundset='abc', circuits=['bc'])
            sage: M2 = Matroid(bases=['ab', 'ac'])
            sage: M1 == M2
            True

        A matroid specified by a list of circuits gets converted to a
        :class:`BasisMatroid <sage.matroids.basis_matroid.BasisMatroid>`
        internally::

            sage: M = Matroid(groundset='abcd', circuits=['abc', 'abd', 'acd',
            ....:                                         'bcd'])
            sage: type(M)
            <... 'sage.matroids.basis_matroid.BasisMatroid'>

        Strange things can happen if the input does not satisfy the circuit
        axioms, and these are not always caught by the
        :meth:`is_valid() <sage.matroids.matroid.Matroid.is_valid>` method. So
        always check whether your input makes sense!

        ::

            sage: M = Matroid('abcd', circuits=['ab', 'acd'])
            sage: M.is_valid()
            True
            sage: [sorted(C) for C in M.circuits()] # py2
            [['a']]
            sage: [sorted(C) for C in M.circuits()] # py3 random
            [['a']]



    #.  Graph:

        Sage has great support for graphs, see :mod:`sage.graphs.graph`.

        ::

            sage: G = graphs.PetersenGraph()
            sage: Matroid(G)
            Graphic matroid of rank 9 on 15 elements

        If each edge has a unique label, then those are used as the ground set
        labels::

            sage: G = Graph([(0, 1, 'a'), (0, 2, 'b'), (1, 2, 'c')])
            sage: M = Matroid(G)
            sage: sorted(M.groundset())
            ['a', 'b', 'c']

        If there are parallel edges, then integers are used for the ground set.
        If there are no edges in parallel, and is not a complete list of labels,
        or the labels are not unique, then vertex tuples are used::

            sage: G = Graph([(0, 1, 'a'), (0, 2, 'b'), (1, 2, 'b')])
            sage: M = Matroid(G)
            sage: sorted(M.groundset())
            [(0, 1), (0, 2), (1, 2)]
            sage: H = Graph([(0, 1, 'a'), (0, 2, 'b'), (1, 2, 'b'), (1, 2, 'c')], multiedges=True)
            sage: N = Matroid(H)
            sage: sorted(N.groundset())
            [0, 1, 2, 3]

        The GraphicMatroid object forces its graph to be connected. If a
        disconnected graph is used as input, it will connect the components.

            sage: G1 = graphs.CycleGraph(3); G2 = graphs.DiamondGraph()
            sage: G = G1.disjoint_union(G2)
            sage: M = Matroid(G)
            sage: M
            Graphic matroid of rank 5 on 8 elements
            sage: M.graph()
            Looped multi-graph on 6 vertices
            sage: M.graph().is_connected()
            True
            sage: M.is_connected()
            False


        If the keyword ``regular`` is set to ``True``, the output will instead
        be an instance of ``RegularMatroid``.

        ::

            sage: G = Graph([(0, 1), (0, 2), (1, 2)])
            sage: M = Matroid(G, regular=True); M
            Regular matroid of rank 2 on 3 elements with 3 bases

        Note: if a groundset is specified, we assume it is in the same order
        as
        :meth:`G.edge_iterator() <sage.graphs.generic_graph.GenericGraph.edge_iterator>`
        provides::

            sage: G = Graph([(0, 1), (0, 2), (0, 2), (1, 2)], multiedges=True)
            sage: M = Matroid('abcd', G)
            sage: M.rank(['b', 'c'])
            1

        As before,
        if no edge labels are present and the graph is simple, we use the
        tuples ``(i, j)`` of endpoints. If that fails, we simply use a list
        ``[0..m-1]`` ::

            sage: G = Graph([(0, 1), (0, 2), (1, 2)])
            sage: M = Matroid(G, regular=True)
            sage: sorted(M.groundset())
            [(0, 1), (0, 2), (1, 2)]

            sage: G = Graph([(0, 1), (0, 2), (0, 2), (1, 2)], multiedges=True)
            sage: M = Matroid(G, regular=True)
            sage: sorted(M.groundset())
            [0, 1, 2, 3]

        When the ``graph`` keyword is used, a variety of inputs can be
        converted to a graph automatically. The following uses a graph6 string
        (see the :class:`Graph <sage.graphs.graph.Graph>` method's
        documentation)::

            sage: Matroid(graph=':I`AKGsaOs`cI]Gb~')
            Graphic matroid of rank 9 on 17 elements

        However, this method is no more clever than ``Graph()``::

            sage: Matroid(graph=41/2)
            Traceback (most recent call last):
            ...
            ValueError: This input cannot be turned into a graph

    #.  Matrix:

        The basic input is a
        :mod:`Sage matrix <sage.matrix.constructor>`::

            sage: A = Matrix(GF(2), [[1, 0, 0, 1, 1, 0],
            ....:                    [0, 1, 0, 1, 0, 1],
            ....:                    [0, 0, 1, 0, 1, 1]])
            sage: M = Matroid(matrix=A)
            sage: M.is_isomorphic(matroids.CompleteGraphic(4))
            True

        Various shortcuts are possible::

            sage: M1 = Matroid(matrix=[[1, 0, 0, 1, 1, 0],
            ....:                      [0, 1, 0, 1, 0, 1],
            ....:                      [0, 0, 1, 0, 1, 1]], ring=GF(2))
            sage: M2 = Matroid(reduced_matrix=[[1, 1, 0],
            ....:                              [1, 0, 1],
            ....:                              [0, 1, 1]], ring=GF(2))
            sage: M3 = Matroid(groundset=[0, 1, 2, 3, 4, 5],
            ....:              matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
            ....:              ring=GF(2))
            sage: A = Matrix(GF(2), [[1, 1, 0], [1, 0, 1], [0, 1, 1]])
            sage: M4 = Matroid([0, 1, 2, 3, 4, 5], A)
            sage: M1 == M2
            True
            sage: M1 == M3
            True
            sage: M1 == M4
            True

        However, with unnamed arguments the input has to be a ``Matrix``
        instance, or the function will try to interpret it as a set of bases::

            sage: Matroid([0, 1, 2], [[1, 0, 1], [0, 1, 1]])
            Traceback (most recent call last):
            ...
            ValueError: basis has wrong cardinality.

        If the groundset size equals number of rows plus number of columns, an
        identity matrix is prepended. Otherwise the groundset size must equal
        the number of columns::

            sage: A = Matrix(GF(2), [[1, 1, 0], [1, 0, 1], [0, 1, 1]])
            sage: M = Matroid([0, 1, 2], A)
            sage: N = Matroid([0, 1, 2, 3, 4, 5], A)
            sage: M.rank()
            2
            sage: N.rank()
            3

        We automatically create an optimized subclass, if available::

            sage: Matroid([0, 1, 2, 3, 4, 5],
            ....:         matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
            ....:         field=GF(2))
            Binary matroid of rank 3 on 6 elements, type (2, 7)
            sage: Matroid([0, 1, 2, 3, 4, 5],
            ....:         matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
            ....:         field=GF(3))
            Ternary matroid of rank 3 on 6 elements, type 0-
            sage: Matroid([0, 1, 2, 3, 4, 5],
            ....:         matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
            ....:         field=GF(4, 'x'))
            Quaternary matroid of rank 3 on 6 elements
            sage: Matroid([0, 1, 2, 3, 4, 5],
            ....:         matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
            ....:         field=GF(2), regular=True)
            Regular matroid of rank 3 on 6 elements with 16 bases

        Otherwise the generic LinearMatroid class is used::

            sage: Matroid([0, 1, 2, 3, 4, 5],
            ....:         matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
            ....:         field=GF(83))
            Linear matroid of rank 3 on 6 elements represented over the Finite
            Field of size 83

        An integer matrix is automatically converted to a matrix over `\QQ`.
        If you really want integers, you can specify the ring explicitly::

            sage: A = Matrix([[1, 1, 0], [1, 0, 1], [0, 1, -1]])
            sage: A.base_ring()
            Integer Ring
            sage: M = Matroid([0, 1, 2, 3, 4, 5], A)
            sage: M.base_ring()
            Rational Field
            sage: M = Matroid([0, 1, 2, 3, 4, 5], A, ring=ZZ)
            sage: M.base_ring()
            Integer Ring

    #.  Rank function:

        Any function mapping subsets to integers can be used as input::

            sage: def f(X):
            ....:     return min(len(X), 2)
            sage: M = Matroid('abcd', rank_function=f)
            sage: M
            Matroid of rank 2 on 4 elements
            sage: M.is_isomorphic(matroids.Uniform(2, 4))
            True

    #.  Circuit closures:

        This is often a really concise way to specify a matroid. The usual way
        is a dictionary of lists::

            sage: M = Matroid(circuit_closures={3: ['edfg', 'acdg', 'bcfg',
            ....:     'cefh', 'afgh', 'abce', 'abdf', 'begh', 'bcdh', 'adeh'],
            ....:     4: ['abcdefgh']})
            sage: M.equals(matroids.named_matroids.P8())
            True

        You can also input tuples `(k, X)` where `X` is the closure of a
        circuit, and `k` the rank of `X`::

            sage: M = Matroid(circuit_closures=[(2, 'abd'), (3, 'abcdef'),
            ....:                               (2, 'bce')])
            sage: M.equals(matroids.named_matroids.Q6())
            True

    #.  RevLex-Index:

        This requires the ``groundset`` to be given and also needs a
        additional keyword argument ``rank`` to specify the rank of the
        matroid::

            sage: M = Matroid("abcdef", "000000******0**", rank=4); M
            Matroid of rank 4 on 6 elements with 8 bases
            sage: list(M.bases())
            [frozenset({'a', 'b', 'd', 'f'}),
             frozenset({'a', 'c', 'd', 'f'}),
             frozenset({'b', 'c', 'd', 'f'}),
             frozenset({'a', 'b', 'e', 'f'}),
             frozenset({'a', 'c', 'e', 'f'}),
             frozenset({'b', 'c', 'e', 'f'}),
             frozenset({'b', 'd', 'e', 'f'}),
             frozenset({'c', 'd', 'e', 'f'})]

        Only the ``0`` symbols really matter, any symbol can be used
        instead of ``*``:

            sage: Matroid("abcdefg", revlex="0++++++++0++++0+++++0+--++----+--++", rank=4)
            Matroid of rank 4 on 7 elements with 31 bases

        It is checked that the input makes sense (but not that it
        defines a matroid)::

            sage: Matroid("abcdef", "000000******0**")
            Traceback (most recent call last):
            ...
            TypeError: for RevLex-Index, the rank needs to be specified
            sage: Matroid("abcdef", "000000******0**", rank=3)
            Traceback (most recent call last):
            ...
            ValueError: expected string of length 20 (6 choose 3), got 15
            sage: M = Matroid("abcdef", "*0000000000000*", rank=4); M
            Matroid of rank 4 on 6 elements with 2 bases
            sage: M.is_valid()
            False

    #.  Matroid:

        Most of the time, the matroid itself is returned::

            sage: M = matroids.named_matroids.Fano()
            sage: N = Matroid(M)
            sage: N is M
            True

        But it can be useful with the ``regular`` option::

            sage: M = Matroid(circuit_closures={2:['adb', 'bec', 'cfa',
            ....:                                  'def'], 3:['abcdef']})
            sage: N = Matroid(M, regular=True)
            sage: N
            Regular matroid of rank 3 on 6 elements with 16 bases
            sage: M == N
            False
            sage: M.is_isomorphic(N)
            True
            sage: Matrix(N) # py2
            [1 0 0 1 1 0]
            [0 1 0 1 1 1]
            [0 0 1 0 1 1]
            sage: Matrix(N) # py3 random
            [1 0 0 1 1 0]
            [0 1 0 1 1 1]
            [0 0 1 0 1 1]

    The ``regular`` option::

        sage: M = Matroid(reduced_matrix=[[1, 1, 0],
        ....:                             [1, 0, 1],
        ....:                             [0, 1, 1]], regular=True)
        sage: M
        Regular matroid of rank 3 on 6 elements with 16 bases

        sage: M.is_isomorphic(matroids.CompleteGraphic(4))
        True

    By default we check if the resulting matroid is actually regular. To
    increase speed, this check can be skipped::

        sage: M = matroids.named_matroids.Fano()
        sage: N = Matroid(M, regular=True)
        Traceback (most recent call last):
        ...
        ValueError: input is not a valid regular matroid
        sage: N = Matroid(M, regular=True, check=False)
        sage: N
        Regular matroid of rank 3 on 7 elements with 32 bases

        sage: N.is_valid()
        False

    Sometimes the output is regular, but represents a different matroid
    from the one you intended::

        sage: M = Matroid(Matrix(GF(3), [[1, 0, 1, 1], [0, 1, 1, 2]]))
        sage: N = Matroid(Matrix(GF(3), [[1, 0, 1, 1], [0, 1, 1, 2]]),
        ....:             regular=True)
        sage: N.is_valid()
        True
        sage: N.is_isomorphic(M)
        False

    TESTS::

        sage: Matroid()
        Traceback (most recent call last):
        ...
        TypeError: no input data given for Matroid()
        sage: Matroid("abc", bases=["abc"], foo="bar")
        Traceback (most recent call last):
        ...
        TypeError: Matroid() got an unexpected keyword argument 'foo'
        sage: Matroid(data=["x"], matrix=Matrix(1,1))
        Traceback (most recent call last):
        ...
        TypeError: Matroid() got an unexpected keyword argument 'matrix'
        sage: Matroid(bases=["x"], matrix=Matrix(1,1))
        Traceback (most recent call last):
        ...
        TypeError: Matroid() got an unexpected keyword argument 'matrix'
        sage: Matroid(Matrix(1,1), ring=ZZ, field=QQ)
        Traceback (most recent call last):
        ...
        TypeError: Matroid() got an unexpected keyword argument 'ring'
        sage: Matroid(rank_function=lambda X: len(X))
        Traceback (most recent call last):
        ...
        TypeError: for rank functions, the groundset needs to be specified
        sage: Matroid(matroid="rubbish")
        Traceback (most recent call last):
        ...
        TypeError: input 'rubbish' is not a matroid
    """
    # process options
    want_regular = kwds.pop('regular', False)
    check = kwds.pop('check', True)

    base_ring = None
    if 'field' in kwds:
        base_ring = kwds.pop('field')
        if check and not base_ring.is_field():
            raise TypeError("{} is not a field".format(base_ring))
    elif 'ring' in kwds:
        base_ring = kwds.pop('ring')
        if check and not base_ring.is_ring():
            raise TypeError("{} is not a ring".format(base_ring))

    # "key" is the kind of data we got
    key = None
    if data is None:
        for k in [
                'bases', 'independent_sets', 'circuits', 'graph', 'matrix',
                'reduced_matrix', 'rank_function', 'revlex',
                'circuit_closures', 'matroid'
        ]:
            if k in kwds:
                data = kwds.pop(k)
                key = k
                break
        else:
            # Assume that the single positional argument was actually
            # the data (instead of the groundset)
            data = groundset
            groundset = None

    if key is None:
        if isinstance(data, sage.graphs.graph.Graph):
            key = 'graph'
        elif is_Matrix(data):
            key = 'matrix'
        elif isinstance(data, sage.matroids.matroid.Matroid):
            key = 'matroid'
        elif isinstance(data, str):
            key = 'revlex'
        elif data is None:
            raise TypeError("no input data given for Matroid()")
        else:
            key = 'independent_sets'

    # Bases:
    if key == 'bases':
        if groundset is None:
            groundset = set()
            for B in data:
                groundset.update(B)
        M = BasisMatroid(groundset=groundset, bases=data)

    # Independent sets:
    elif key == 'independent_sets':
        # Convert to list of bases first
        rk = -1
        bases = []
        for I in data:
            if len(I) == rk:
                bases.append(I)
            elif len(I) > rk:
                bases = [I]
                rk = len(I)
        if groundset is None:
            groundset = set()
            for B in bases:
                groundset.update(B)
        M = BasisMatroid(groundset=groundset, bases=bases)

    # Circuits:
    elif key == 'circuits':
        # Convert to list of bases first
        # Determine groundset (note that this cannot detect coloops)
        if groundset is None:
            groundset = set()
            for C in data:
                groundset.update(C)
        # determine the rank by computing a basis element
        b = set(groundset)
        for C in data:
            I = b.intersection(C)
            if len(I) >= len(C):
                b.discard(I.pop())
        rk = len(b)
        # Construct the basis matroid of appropriate rank. Note: slow!
        BB = [
            frozenset(B) for B in combinations(groundset, rk)
            if not any(frozenset(C).issubset(B) for C in data)
        ]
        M = BasisMatroid(groundset=groundset, bases=BB)

    # Graphs:

    elif key == 'graph':
        if isinstance(data, sage.graphs.generic_graph.GenericGraph):
            G = data
        else:
            G = Graph(data)
        # Decide on the groundset
        m = G.num_edges()
        if groundset is None:
            # 1. Attempt to use edge labels.
            sl = G.edge_labels()
            if len(sl) == len(set(sl)):
                groundset = sl
                # 2. If simple, use vertex tuples
            elif not G.has_multiple_edges():
                groundset = [(i, j) for i, j, k in G.edge_iterator()]
            else:
                # 3. Use numbers
                groundset = list(range(m))
        if want_regular:
            # Construct the incidence matrix
            # NOTE: we are not using Sage's built-in method because
            # 1) we would need to fix the loops anyway
            # 2) Sage will sort the columns, making it impossible to keep labels!
            V = G.vertices()
            n = G.num_verts()
            A = Matrix(ZZ, n, m, 0)
            mm = 0
            for i, j, k in G.edge_iterator():
                A[V.index(i), mm] = -1
                A[V.index(j), mm] += 1  # So loops get 0
                mm += 1
            M = RegularMatroid(matrix=A, groundset=groundset)
            want_regular = False  # Save some time, since result is already regular
        else:
            M = GraphicMatroid(G, groundset=groundset)

    # Matrices:
    elif key in ['matrix', 'reduced_matrix']:
        A = data
        is_reduced = (key == 'reduced_matrix')

        # Fix the representation
        if not is_Matrix(A):
            if base_ring is not None:
                A = Matrix(base_ring, A)
            else:
                A = Matrix(A)

        # Fix the ring
        if base_ring is not None:
            if A.base_ring() is not base_ring:
                A = A.change_ring(base_ring)
        elif A.base_ring(
        ) is ZZ and not want_regular:  # Usually a rational matrix is intended, we presume.
            A = A.change_ring(QQ)
            base_ring = QQ
        else:
            base_ring = A.base_ring()

        # Check groundset
        if groundset is not None:
            if not is_reduced:
                if len(groundset) == A.ncols():
                    pass
                elif len(groundset) == A.nrows() + A.ncols():
                    is_reduced = True
                else:
                    raise ValueError(
                        "groundset size does not correspond to matrix size")
            elif is_reduced:
                if len(groundset) == A.nrows() + A.ncols():
                    pass
                else:
                    raise ValueError(
                        "groundset size does not correspond to matrix size")

        if is_reduced:
            kw = dict(groundset=groundset, reduced_matrix=A)
        else:
            kw = dict(groundset=groundset, matrix=A)

        if isinstance(base_ring, FiniteField):
            q = base_ring.order()
        else:
            q = 0

        if q == 2:
            M = BinaryMatroid(**kw)
        elif q == 3:
            M = TernaryMatroid(**kw)
        elif q == 4:
            M = QuaternaryMatroid(**kw)
        else:
            M = LinearMatroid(ring=base_ring, **kw)

    # Rank functions:
    elif key == 'rank_function':
        if groundset is None:
            raise TypeError(
                'for rank functions, the groundset needs to be specified')
        M = RankMatroid(groundset=groundset, rank_function=data)

    # RevLex-Index:
    elif key == "revlex":
        if groundset is None:
            raise TypeError(
                'for RevLex-Index, the groundset needs to be specified')
        try:
            rk = kwds.pop("rank")
        except KeyError:
            raise TypeError('for RevLex-Index, the rank needs to be specified')

        groundset = tuple(groundset)
        data = tuple(data)
        rk = int(rk)
        N = len(groundset)

        def revlex_sort_key(s):
            return tuple(reversed(s))

        subsets = sorted(combinations(range(N), rk), key=revlex_sort_key)
        if len(data) != len(subsets):
            raise ValueError(
                "expected string of length %s (%s choose %s), got %s" %
                (len(subsets), N, rk, len(data)))
        bases = []
        for i, x in enumerate(data):
            if x != '0':
                bases.append([groundset[c] for c in subsets[i]])
        M = BasisMatroid(groundset=groundset, bases=bases)

    # Circuit closures:
    elif key == 'circuit_closures':
        if isinstance(data, dict):
            CC = data
        else:
            # Convert to dictionary
            CC = {}
            for X in data:
                if X[0] not in CC:
                    CC[X[0]] = []
                CC[X[0]].append(X[1])

        if groundset is None:
            groundset = set()
            for X in itervalues(CC):
                for Y in X:
                    groundset.update(Y)

        M = CircuitClosuresMatroid(groundset=groundset, circuit_closures=CC)

    # Matroids:
    elif key == 'matroid':
        if not isinstance(data, sage.matroids.matroid.Matroid):
            raise TypeError("input {!r} is not a matroid".format(data))
        M = data

    else:
        raise AssertionError("unknown key %r" % key)

    # All keywords should be used
    for k in kwds:
        raise TypeError(
            "Matroid() got an unexpected keyword argument '{}'".format(k))

    if want_regular:
        M = sage.matroids.utilities.make_regular_matroid_from_matroid(M)
        if check and not M.is_valid():
            raise ValueError('input is not a valid regular matroid')

    return M
示例#52
0
def is_Matrix(x):
    try:
        from sage.structure.element import is_Matrix
    except ImportError:
        return False
    return is_Matrix(x)
示例#53
0
def from_adjacency_matrix(G, M, loops=False, multiedges=False, weighted=False):
    r"""
    Fill ``G`` with the data of an adjacency matrix.

    INPUT:

    - ``G`` -- a :class:`Graph` or :class:`DiGraph`

    - ``M`` -- an adjacency matrix

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

    EXAMPLES::

        sage: from sage.graphs.graph_input import from_adjacency_matrix
        sage: g = Graph()
        sage: from_adjacency_matrix(g, graphs.PetersenGraph().adjacency_matrix())
        sage: g.is_isomorphic(graphs.PetersenGraph())
        True
    """
    from sage.structure.element import is_Matrix
    from sage.rings.integer_ring import ZZ
    assert is_Matrix(M)
    # note: the adjacency matrix might be weighted and hence not
    # necessarily consists of integers
    if not weighted and M.base_ring() != ZZ:
        try:
            M = M.change_ring(ZZ)
        except TypeError:
            if weighted is False:
                raise ValueError(
                    "the adjacency matrix of a non-weighted graph" +
                    " must have only nonnegative integer entries")
            weighted = True

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

    if not weighted and any(e < 0 for e in entries):
        if weighted is False:
            raise ValueError("the adjacency matrix of a non-weighted graph" +
                             " must have only nonnegative integer entries")
        weighted = True
        if multiedges is None:
            multiedges = False
    if weighted is None:
        weighted = False

    if multiedges is None:
        multiedges = ((not weighted)
                      and any(e != 0 and e != 1 for e in entries))

    if not loops and any(M[i, i] for i in range(M.nrows())):
        if loops is False:
            raise ValueError("the adjacency matrix of a non-weighted graph" +
                             " must have zeroes on the diagonal")
        loops = True
    if loops is None:
        loops = False
    G.allow_loops(loops, check=False)
    G.allow_multiple_edges(multiedges, check=False)
    G.add_vertices(range(M.nrows()))
    if G.is_directed():
        pairs = M.nonzero_positions()
    else:
        pairs = ((i, j) for i, j in M.nonzero_positions() if i <= j)
    if weighted:
        G.add_edges((i, j, M[i][j]) for i, j in pairs)
    elif multiedges:
        G.add_edges((i, j) for i, j in pairs for _ in range(int(M[i][j])))
    else:
        G.add_edges((i, j) for i, j in pairs)
    G._weighted = weighted
示例#54
0
def Conic(base_field, F=None, names=None, unique=True):
    r"""
    Return the plane projective conic curve defined by ``F``
    over ``base_field``.

    The input form ``Conic(F, names=None)`` is also accepted,
    in which case the fraction field of the base ring of ``F``
    is used as base field.

    INPUT:

    - ``base_field`` -- The base field of the conic.

    - ``names`` -- a list, tuple, or comma separated string
      of three variable names specifying the names
      of the coordinate functions of the ambient
      space `\Bold{P}^3`. If not specified or read
      off from ``F``, then this defaults to ``'x,y,z'``.

    - ``F`` -- a polynomial, list, matrix, ternary quadratic form,
      or list or tuple of 5 points in the plane.

                   If ``F`` is a polynomial or quadratic form,
                   then the output is the curve in the projective plane
                   defined by ``F = 0``.

                   If ``F`` is a polynomial, then it must be a polynomial
                   of degree at most 2 in 2 variables, or a homogeneous
                   polynomial in of degree 2 in 3 variables.

                   If ``F`` is a matrix, then the output is the zero locus
                   of `(x,y,z) F (x,y,z)^t`.

                   If ``F`` is a list of coefficients, then it has
                   length 3 or 6 and gives the coefficients of
                   the monomials `x^2, y^2, z^2` or all 6 monomials
                   `x^2, xy, xz, y^2, yz, z^2` in lexicographic order.

                   If ``F`` is a list of 5 points in the plane, then the output
                   is a conic through those points.

    - ``unique`` -- Used only if ``F`` is a list of points in the plane.
      If the conic through the points is not unique, then
      raise ``ValueError`` if and only if ``unique`` is True

    OUTPUT:

    A plane projective conic curve defined by ``F`` over a field.

    EXAMPLES:

    Conic curves given by polynomials ::

        sage: X,Y,Z = QQ['X,Y,Z'].gens()
        sage: Conic(X^2 - X*Y + Y^2 - Z^2)
        Projective Conic Curve over Rational Field defined by X^2 - X*Y + Y^2 - Z^2
        sage: x,y = GF(7)['x,y'].gens()
        sage: Conic(x^2 - x + 2*y^2 - 3, 'U,V,W')
        Projective Conic Curve over Finite Field of size 7 defined by U^2 + 2*V^2 - U*W - 3*W^2

    Conic curves given by matrices ::

        sage: Conic(matrix(QQ, [[1, 2, 0], [4, 0, 0], [7, 0, 9]]), 'x,y,z')
        Projective Conic Curve over Rational Field defined by x^2 + 6*x*y + 7*x*z + 9*z^2

        sage: x,y,z = GF(11)['x,y,z'].gens()
        sage: C = Conic(x^2+y^2-2*z^2); C
        Projective Conic Curve over Finite Field of size 11 defined by x^2 + y^2 - 2*z^2
        sage: Conic(C.symmetric_matrix(), 'x,y,z')
        Projective Conic Curve over Finite Field of size 11 defined by x^2 + y^2 - 2*z^2

    Conics given by coefficients ::

        sage: Conic(QQ, [1,2,3])
        Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + 3*z^2
        sage: Conic(GF(7), [1,2,3,4,5,6], 'X')
        Projective Conic Curve over Finite Field of size 7 defined by X0^2 + 2*X0*X1 - 3*X1^2 + 3*X0*X2 - 2*X1*X2 - X2^2

    The conic through a set of points ::

        sage: C = Conic(QQ, [[10,2],[3,4],[-7,6],[7,8],[9,10]]); C
        Projective Conic Curve over Rational Field defined by x^2 + 13/4*x*y - 17/4*y^2 - 35/2*x*z + 91/4*y*z - 37/2*z^2
        sage: C.rational_point()
        (10 : 2 : 1)
        sage: C.point([3,4])
        (3 : 4 : 1)

        sage: a=AffineSpace(GF(13),2)
        sage: Conic([a([x,x^2]) for x in range(5)])
        Projective Conic Curve over Finite Field of size 13 defined by x^2 - y*z
    """
    if not (is_IntegralDomain(base_field) or base_field == None):
        if names is None:
            names = F
        F = base_field
        base_field = None
    if isinstance(F, (list,tuple)):
        if len(F) == 1:
            return Conic(base_field, F[0], names)
        if names == None:
            names = 'x,y,z'
        if len(F) == 5:
            L=[]
            for f in F:
                if isinstance(f, SchemeMorphism_point_affine):
                    C = Sequence(f, universe = base_field)
                    if len(C) != 2:
                        raise TypeError, "points in F (=%s) must be planar"%F
                    C.append(1)
                elif isinstance(f, SchemeMorphism_point_projective_field):
                    C = Sequence(f, universe = base_field)
                elif isinstance(f, (list, tuple)):
                    C = Sequence(f, universe = base_field)
                    if len(C) == 2:
                        C.append(1)
                else:
                    raise TypeError, "F (=%s) must be a sequence of planar " \
                                      "points" % F
                if len(C) != 3:
                    raise TypeError, "points in F (=%s) must be planar" % F
                P = C.universe()
                if not is_IntegralDomain(P):
                    raise TypeError, "coordinates of points in F (=%s) must " \
                                     "be in an integral domain" % F
                L.append(Sequence([C[0]**2, C[0]*C[1], C[0]*C[2], C[1]**2,
                                   C[1]*C[2], C[2]**2], P.fraction_field()))
            M=Matrix(L)
            if unique and M.rank() != 5:
                raise ValueError, "points in F (=%s) do not define a unique " \
                                   "conic" % F
            con = Conic(base_field, Sequence(M.right_kernel().gen()), names)
            con.point(F[0])
            return con
        F = Sequence(F, universe = base_field)
        base_field = F.universe().fraction_field()
        temp_ring = PolynomialRing(base_field, 3, names)
        (x,y,z) = temp_ring.gens()
        if len(F) == 3:
            return Conic(F[0]*x**2 + F[1]*y**2 + F[2]*z**2)
        if len(F) == 6:
            return Conic(F[0]*x**2 + F[1]*x*y + F[2]*x*z + F[3]*y**2 + \
                         F[4]*y*z + F[5]*z**2)
        raise TypeError, "F (=%s) must be a sequence of 3 or 6" \
                         "coefficients" % F
    if is_QuadraticForm(F):
        F = F.matrix()
    if is_Matrix(F) and F.is_square() and F.ncols() == 3:
        if names == None:
            names = 'x,y,z'
        temp_ring = PolynomialRing(F.base_ring(), 3, names)
        F = vector(temp_ring.gens()) * F * vector(temp_ring.gens())

    if not is_MPolynomial(F):
        raise TypeError, "F (=%s) must be a three-variable polynomial or " \
                         "a sequence of points or coefficients" % F

    if F.total_degree() != 2:
        raise TypeError, "F (=%s) must have degree 2" % F

    if base_field == None:
        base_field = F.base_ring()
    if not is_IntegralDomain(base_field):
        raise ValueError, "Base field (=%s) must be a field" % base_field
    base_field = base_field.fraction_field()
    if names == None:
        names = F.parent().variable_names()
    pol_ring = PolynomialRing(base_field, 3, names)

    if F.parent().ngens() == 2:
        (x,y,z) = pol_ring.gens()
        F = pol_ring(F(x/z,y/z)*z**2)

    if F == 0:
        raise ValueError, "F must be nonzero over base field %s" % base_field

    if F.total_degree() != 2:
        raise TypeError, "F (=%s) must have degree 2 over base field %s" % \
                          (F, base_field)

    if F.parent().ngens() == 3:
        P2 = ProjectiveSpace(2, base_field, names)
        if is_PrimeFiniteField(base_field):
            return ProjectiveConic_prime_finite_field(P2, F)
        if is_FiniteField(base_field):
            return ProjectiveConic_finite_field(P2, F)
        if is_RationalField(base_field):
            return ProjectiveConic_rational_field(P2, F)
        if is_NumberField(base_field):
            return ProjectiveConic_number_field(P2, F)
        return ProjectiveConic_field(P2, F)

    raise TypeError, "Number of variables of F (=%s) must be 2 or 3" % F
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 transformation 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
        vector 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 SR
    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(SR)
        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.coefficient(a) for e in exprs] for a in args]
        try:
            arg2 = matrix(D.base_ring(), m, n, arg2)
        except TypeError as 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) as e:
            msg = 'some image of the function is not in the codomain, because\n' + e.args[0]
            raise ArithmeticError(msg)
    else:
        msg = 'third argument must be a matrix, function, or list of images, not {0}'
        raise TypeError(msg.format(arg2))

    # arg2 now compatible with homspace H call method
    # __init__ will check matrix sizes versus domain/codomain dimensions
    return H(arg2)
示例#56
0
    def _element_constructor_(self, *args, **kwds):
        r"""
        TESTS::

            sage: from flatsurf.geometry.similarity import SimilarityGroup
            sage: S = SimilarityGroup(QQ)
            sage: S((1,1))  # translation
            (x, y) |-> (x + 1, y + 1)

            sage: V = QQ^2
            sage: S(V((1,-1)))
            (x, y) |-> (x + 1, y - 1)

            sage: S(vector((1,1)))
            (x, y) |-> (x + 1, y + 1)
        """
        if len(args) == 1:
            x = args[0]
        else:
            x = args

        a = self._field.one()
        b = s = t = self._field.zero()
        sign = ZZ_1

        # TODO: 2x2 and 3x3 matrix input

        if isinstance(x, (tuple,list)):
            if len(x) == 2:
                s,t = map(self._field, x)
            elif len(x) == 4:
                a,b,s,t = map(self._field, x)
            elif len(x) == 5:
                a,b,s,t = map(self._field, x[:4])
                sign = ZZ(x[4])
            else:
                raise ValueError("can not construct a similarity from a list of length {}".format(len(x)))
        elif is_Matrix(x):
            #   a -sb
            #   b sa
            if x.nrows() == x.ncols() == 2:
                a,c,b,d = x.list()
                if a == d and b == -c:
                    sign = ZZ_1
                elif a == -d and b == c:
                    sign = ZZ_m1
                else:
                    raise ValueError("not a similarity matrix")
            elif x.nrows() == x.ncols() == 3:
                raise NotImplementedError
            else:
                raise ValueError("invalid dimension for matrix input")
        elif isinstance(x, FreeModuleElement):
            if len(x) == 2:
                if x.base_ring() is self._field:
                    s,t = x
                else:
                    s,t = map(self._field, x)
            else:
                raise ValueError("invalid dimension for vector input")
        else:
            p = parent(x)
            if self._field.has_coerce_map_from(p):
                a = self._field(x)
            else:
                raise ValueError

        if (a*a + b*b).is_zero():
            raise ValueError("not invertible")

        return self.element_class(self, a, b, s, t, sign)
示例#57
0
def Matroid(groundset=None, data=None, **kwds):
    r"""
    Construct a matroid.

    Matroids are combinatorial structures that capture the abstract properties
    of (linear/algebraic/...) dependence. Formally, a matroid is a pair
    `M = (E, I)` of a finite set `E`, the *groundset*, and a collection of
    subsets `I`, the independent sets, subject to the following axioms:

    * `I` contains the empty set
    * If `X` is a set in `I`, then each subset of `X` is in `I`
    * If two subsets `X`, `Y` are in `I`, and `|X| > |Y|`, then there exists
      `x \in X - Y` such that `Y + \{x\}` is in `I`.

    See the :wikipedia:`Wikipedia article on matroids <Matroid>` for more
    theory and examples. Matroids can be obtained from many types of
    mathematical structures, and Sage supports a number of them.

    There are two main entry points to Sage's matroid functionality. For
    built-in matroids, do the following:

    * Within a Sage session, type "matroids." (Do not press "Enter", and do
      not forget the final period ".")
    * Hit "tab".

    You will see a list of methods which will construct matroids. For
    example::

        sage: F7 = matroids.named_matroids.Fano()
        sage: len(F7.nonspanning_circuits())
        7

    or::

        sage: U36 = matroids.Uniform(3, 6)
        sage: U36.equals(U36.dual())
        True

    To define your own matroid, use the function ``Matroid()``.
    This function attempts to interpret its arguments to create an appropriate
    matroid. The following named arguments are supported:

    INPUT:

    - ``groundset`` -- (optional) If provided, the groundset of the
      matroid. Otherwise, the function attempts to determine a groundset
      from the data.

    Exactly one of the following inputs must be given (where ``data``
    must be a positional argument and anything else must be a keyword
    argument):

    - ``data`` -- a graph or a matrix or a RevLex-Index string or a list
      of independent sets containing all bases or a matroid.
    - ``bases`` -- The list of bases (maximal independent sets) of the
      matroid.
    - ``independent_sets`` -- The list of independent sets of the matroid.
    - ``circuits`` -- The list of circuits of the matroid.
    - ``graph`` -- A graph, whose edges form the elements of the matroid.
    - ``matrix`` -- A matrix representation of the matroid.
    - ``reduced_matrix`` -- A reduced representation of the matroid: if
      ``reduced_matrix = A``
      then the matroid is represented by `[I\ \ A]` where `I` is an
      appropriately sized identity matrix.
    - ``rank_function`` -- A function that computes the rank of each subset.
      Can only be provided together with a groundset.
    - ``circuit_closures`` -- Either a list of tuples ``(k, C)`` with ``C``
      the closure of a circuit, and ``k`` the rank of ``C``, or a dictionary
      ``D`` with ``D[k]`` the set of closures of rank-``k`` circuits.
    - ``revlex`` -- the encoding as a string of ``0`` and ``*`` symbols.
      Used by [MatroidDatabase]_ and explained in [MMIB2012]_.
    - ``matroid`` -- An object that is already a matroid. Useful only with the
      ``regular`` option.

    Further options:

    - ``regular`` -- (default: ``False``) boolean. If ``True``,
      output a
      :class:`RegularMatroid <sage.matroids.linear_matroid.RegularMatroid>`
      instance such that, *if* the input defines a valid regular matroid, then
      the output represents this matroid. Note that this option can be
      combined with any type of input.
    - ``ring`` -- any ring. If provided, and the input is a ``matrix`` or
      ``reduced_matrix``, output will be a linear matroid over the ring or
      field ``ring``.
    - ``field`` -- any field. Same as ``ring``, but only fields are allowed.
    - ``check`` -- (default: ``True``) boolean. If ``True`` and
      ``regular`` is true, the output is checked to make sure it is a valid
      regular matroid.

    .. WARNING::

        Except for regular matroids, the input is not checked for validity. If
        your data does not correspond to an actual matroid, the behavior of
        the methods is undefined and may cause strange errors. To ensure you
        have a matroid, run
        :meth:`M.is_valid() <sage.matroids.matroid.Matroid.is_valid>`.

    .. NOTE::

        The ``Matroid()`` method will return instances of type
        :class:`BasisMatroid <sage.matroids.basis_matroid.BasisMatroid>`,
        :class:`CircuitClosuresMatroid <sage.matroids.circuit_closures_matroid.CircuitClosuresMatroid>`,
        :class:`LinearMatroid <sage.matroids.linear_matroid.LinearMatroid>`,
        :class:`BinaryMatroid <sage.matroids.linear_matroid.LinearMatroid>`,
        :class:`TernaryMatroid <sage.matroids.linear_matroid.LinearMatroid>`,
        :class:`QuaternaryMatroid <sage.matroids.linear_matroid.LinearMatroid>`,
        :class:`RegularMatroid <sage.matroids.linear_matroid.LinearMatroid>`, or
        :class:`RankMatroid <sage.matroids.rank_matroid.RankMatroid>`. To
        import these classes (and other useful functions) directly into Sage's
        main namespace, type::

            sage: from sage.matroids.advanced import *

        See :mod:`sage.matroids.advanced <sage.matroids.advanced>`.

    EXAMPLES:

    Note that in these examples we will often use the fact that strings are
    iterable in these examples. So we type ``'abcd'`` to denote the list
    ``['a', 'b', 'c', 'd']``.

    #.  List of bases:

        All of the following inputs are allowed, and equivalent::

            sage: M1 = Matroid(groundset='abcd', bases=['ab', 'ac', 'ad',
            ....:                                       'bc', 'bd', 'cd'])
            sage: M2 = Matroid(bases=['ab', 'ac', 'ad', 'bc', 'bd', 'cd'])
            sage: M3 = Matroid(['ab', 'ac', 'ad', 'bc', 'bd', 'cd'])
            sage: M4 = Matroid('abcd', ['ab', 'ac', 'ad', 'bc', 'bd', 'cd'])
            sage: M5 = Matroid('abcd', bases=[['a', 'b'], ['a', 'c'],
            ....:                             ['a', 'd'], ['b', 'c'],
            ....:                             ['b', 'd'], ['c', 'd']])
            sage: M1 == M2
            True
            sage: M1 == M3
            True
            sage: M1 == M4
            True
            sage: M1 == M5
            True

        We do not check if the provided input forms an actual matroid::

            sage: M1 = Matroid(groundset='abcd', bases=['ab', 'cd'])
            sage: M1.full_rank()
            2
            sage: M1.is_valid()
            False

        Bases may be repeated::

            sage: M1 = Matroid(['ab', 'ac'])
            sage: M2 = Matroid(['ab', 'ac', 'ab'])
            sage: M1 == M2
            True

    #.  List of independent sets:

        ::

            sage: M1 = Matroid(groundset='abcd',
            ....:              independent_sets=['', 'a', 'b', 'c', 'd', 'ab',
            ....:                               'ac', 'ad', 'bc', 'bd', 'cd'])

        We only require that the list of independent sets contains each basis
        of the matroid; omissions of smaller independent sets and
        repetitions are allowed::

            sage: M1 = Matroid(bases=['ab', 'ac'])
            sage: M2 = Matroid(independent_sets=['a', 'ab', 'b', 'ab', 'a',
            ....:                                'b', 'ac'])
            sage: M1 == M2
            True

    #.  List of circuits:

        ::

            sage: M1 = Matroid(groundset='abc', circuits=['bc'])
            sage: M2 = Matroid(bases=['ab', 'ac'])
            sage: M1 == M2
            True

        A matroid specified by a list of circuits gets converted to a
        :class:`BasisMatroid <sage.matroids.basis_matroid.BasisMatroid>`
        internally::

            sage: M = Matroid(groundset='abcd', circuits=['abc', 'abd', 'acd',
            ....:                                         'bcd'])
            sage: type(M)
            <... 'sage.matroids.basis_matroid.BasisMatroid'>

        Strange things can happen if the input does not satisfy the circuit
        axioms, and these are not always caught by the
        :meth:`is_valid() <sage.matroids.matroid.Matroid.is_valid>` method. So
        always check whether your input makes sense!

        ::

            sage: M = Matroid('abcd', circuits=['ab', 'acd'])
            sage: M.is_valid()
            True
            sage: [sorted(C) for C in M.circuits()]
            [['a']]



    #.  Graph:

        Sage has great support for graphs, see :mod:`sage.graphs.graph`.

        ::

            sage: G = graphs.PetersenGraph()
            sage: Matroid(G)
            Graphic matroid of rank 9 on 15 elements

        If each edge has a unique label, then those are used as the ground set
        labels::

            sage: G = Graph([(0, 1, 'a'), (0, 2, 'b'), (1, 2, 'c')])
            sage: M = Matroid(G)
            sage: sorted(M.groundset())
            ['a', 'b', 'c']

        If there are parallel edges, then integers are used for the ground set.
        If there are no edges in parallel, and is not a complete list of labels,
        or the labels are not unique, then vertex tuples are used::

            sage: G = Graph([(0, 1, 'a'), (0, 2, 'b'), (1, 2, 'b')])
            sage: M = Matroid(G)
            sage: sorted(M.groundset())
            [(0, 1), (0, 2), (1, 2)]
            sage: H = Graph([(0, 1, 'a'), (0, 2, 'b'), (1, 2, 'b'), (1, 2, 'c')], multiedges=True)
            sage: N = Matroid(H)
            sage: sorted(N.groundset())
            [0, 1, 2, 3]

        The GraphicMatroid object forces its graph to be connected. If a
        disconnected graph is used as input, it will connect the components.

            sage: G1 = graphs.CycleGraph(3); G2 = graphs.DiamondGraph()
            sage: G = G1.disjoint_union(G2)
            sage: M = Matroid(G)
            sage: M
            Graphic matroid of rank 5 on 8 elements
            sage: M.graph()
            Looped multi-graph on 6 vertices
            sage: M.graph().is_connected()
            True
            sage: M.is_connected()
            False


        If the keyword ``regular`` is set to ``True``, the output will instead
        be an instance of ``RegularMatroid``.

        ::

            sage: G = Graph([(0, 1), (0, 2), (1, 2)])
            sage: M = Matroid(G, regular=True); M
            Regular matroid of rank 2 on 3 elements with 3 bases

        Note: if a groundset is specified, we assume it is in the same order
        as
        :meth:`G.edge_iterator() <sage.graphs.generic_graph.GenericGraph.edge_iterator>`
        provides::

            sage: G = Graph([(0, 1), (0, 2), (0, 2), (1, 2)], multiedges=True)
            sage: M = Matroid('abcd', G)
            sage: M.rank(['b', 'c'])
            1

        As before,
        if no edge labels are present and the graph is simple, we use the
        tuples ``(i, j)`` of endpoints. If that fails, we simply use a list
        ``[0..m-1]`` ::

            sage: G = Graph([(0, 1), (0, 2), (1, 2)])
            sage: M = Matroid(G, regular=True)
            sage: sorted(M.groundset())
            [(0, 1), (0, 2), (1, 2)]

            sage: G = Graph([(0, 1), (0, 2), (0, 2), (1, 2)], multiedges=True)
            sage: M = Matroid(G, regular=True)
            sage: sorted(M.groundset())
            [0, 1, 2, 3]

        When the ``graph`` keyword is used, a variety of inputs can be
        converted to a graph automatically. The following uses a graph6 string
        (see the :class:`Graph <sage.graphs.graph.Graph>` method's
        documentation)::

            sage: Matroid(graph=':I`AKGsaOs`cI]Gb~')
            Graphic matroid of rank 9 on 17 elements

        However, this method is no more clever than ``Graph()``::

            sage: Matroid(graph=41/2)
            Traceback (most recent call last):
            ...
            ValueError: This input cannot be turned into a graph

    #.  Matrix:

        The basic input is a
        :mod:`Sage matrix <sage.matrix.constructor>`::

            sage: A = Matrix(GF(2), [[1, 0, 0, 1, 1, 0],
            ....:                    [0, 1, 0, 1, 0, 1],
            ....:                    [0, 0, 1, 0, 1, 1]])
            sage: M = Matroid(matrix=A)
            sage: M.is_isomorphic(matroids.CompleteGraphic(4))
            True

        Various shortcuts are possible::

            sage: M1 = Matroid(matrix=[[1, 0, 0, 1, 1, 0],
            ....:                      [0, 1, 0, 1, 0, 1],
            ....:                      [0, 0, 1, 0, 1, 1]], ring=GF(2))
            sage: M2 = Matroid(reduced_matrix=[[1, 1, 0],
            ....:                              [1, 0, 1],
            ....:                              [0, 1, 1]], ring=GF(2))
            sage: M3 = Matroid(groundset=[0, 1, 2, 3, 4, 5],
            ....:              matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
            ....:              ring=GF(2))
            sage: A = Matrix(GF(2), [[1, 1, 0], [1, 0, 1], [0, 1, 1]])
            sage: M4 = Matroid([0, 1, 2, 3, 4, 5], A)
            sage: M1 == M2
            True
            sage: M1 == M3
            True
            sage: M1 == M4
            True

        However, with unnamed arguments the input has to be a ``Matrix``
        instance, or the function will try to interpret it as a set of bases::

            sage: Matroid([0, 1, 2], [[1, 0, 1], [0, 1, 1]])
            Traceback (most recent call last):
            ...
            ValueError: basis has wrong cardinality.

        If the groundset size equals number of rows plus number of columns, an
        identity matrix is prepended. Otherwise the groundset size must equal
        the number of columns::

            sage: A = Matrix(GF(2), [[1, 1, 0], [1, 0, 1], [0, 1, 1]])
            sage: M = Matroid([0, 1, 2], A)
            sage: N = Matroid([0, 1, 2, 3, 4, 5], A)
            sage: M.rank()
            2
            sage: N.rank()
            3

        We automatically create an optimized subclass, if available::

            sage: Matroid([0, 1, 2, 3, 4, 5],
            ....:         matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
            ....:         field=GF(2))
            Binary matroid of rank 3 on 6 elements, type (2, 7)
            sage: Matroid([0, 1, 2, 3, 4, 5],
            ....:         matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
            ....:         field=GF(3))
            Ternary matroid of rank 3 on 6 elements, type 0-
            sage: Matroid([0, 1, 2, 3, 4, 5],
            ....:         matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
            ....:         field=GF(4, 'x'))
            Quaternary matroid of rank 3 on 6 elements
            sage: Matroid([0, 1, 2, 3, 4, 5],
            ....:         matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
            ....:         field=GF(2), regular=True)
            Regular matroid of rank 3 on 6 elements with 16 bases

        Otherwise the generic LinearMatroid class is used::

            sage: Matroid([0, 1, 2, 3, 4, 5],
            ....:         matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
            ....:         field=GF(83))
            Linear matroid of rank 3 on 6 elements represented over the Finite
            Field of size 83

        An integer matrix is automatically converted to a matrix over `\QQ`.
        If you really want integers, you can specify the ring explicitly::

            sage: A = Matrix([[1, 1, 0], [1, 0, 1], [0, 1, -1]])
            sage: A.base_ring()
            Integer Ring
            sage: M = Matroid([0, 1, 2, 3, 4, 5], A)
            sage: M.base_ring()
            Rational Field
            sage: M = Matroid([0, 1, 2, 3, 4, 5], A, ring=ZZ)
            sage: M.base_ring()
            Integer Ring

    #.  Rank function:

        Any function mapping subsets to integers can be used as input::

            sage: def f(X):
            ....:     return min(len(X), 2)
            sage: M = Matroid('abcd', rank_function=f)
            sage: M
            Matroid of rank 2 on 4 elements
            sage: M.is_isomorphic(matroids.Uniform(2, 4))
            True

    #.  Circuit closures:

        This is often a really concise way to specify a matroid. The usual way
        is a dictionary of lists::

            sage: M = Matroid(circuit_closures={3: ['edfg', 'acdg', 'bcfg',
            ....:     'cefh', 'afgh', 'abce', 'abdf', 'begh', 'bcdh', 'adeh'],
            ....:     4: ['abcdefgh']})
            sage: M.equals(matroids.named_matroids.P8())
            True

        You can also input tuples `(k, X)` where `X` is the closure of a
        circuit, and `k` the rank of `X`::

            sage: M = Matroid(circuit_closures=[(2, 'abd'), (3, 'abcdef'),
            ....:                               (2, 'bce')])
            sage: M.equals(matroids.named_matroids.Q6())
            True

    #.  RevLex-Index:

        This requires the ``groundset`` to be given and also needs a
        additional keyword argument ``rank`` to specify the rank of the
        matroid::

            sage: M = Matroid("abcdef", "000000******0**", rank=4); M
            Matroid of rank 4 on 6 elements with 8 bases
            sage: list(M.bases())
            [frozenset({'a', 'b', 'd', 'f'}),
             frozenset({'a', 'c', 'd', 'f'}),
             frozenset({'b', 'c', 'd', 'f'}),
             frozenset({'a', 'b', 'e', 'f'}),
             frozenset({'a', 'c', 'e', 'f'}),
             frozenset({'b', 'c', 'e', 'f'}),
             frozenset({'b', 'd', 'e', 'f'}),
             frozenset({'c', 'd', 'e', 'f'})]

        Only the ``0`` symbols really matter, any symbol can be used
        instead of ``*``:

            sage: Matroid("abcdefg", revlex="0++++++++0++++0+++++0+--++----+--++", rank=4)
            Matroid of rank 4 on 7 elements with 31 bases

        It is checked that the input makes sense (but not that it
        defines a matroid)::

            sage: Matroid("abcdef", "000000******0**")
            Traceback (most recent call last):
            ...
            TypeError: for RevLex-Index, the rank needs to be specified
            sage: Matroid("abcdef", "000000******0**", rank=3)
            Traceback (most recent call last):
            ...
            ValueError: expected string of length 20 (6 choose 3), got 15
            sage: M = Matroid("abcdef", "*0000000000000*", rank=4); M
            Matroid of rank 4 on 6 elements with 2 bases
            sage: M.is_valid()
            False

    #.  Matroid:

        Most of the time, the matroid itself is returned::

            sage: M = matroids.named_matroids.Fano()
            sage: N = Matroid(M)
            sage: N is M
            True

        But it can be useful with the ``regular`` option::

            sage: M = Matroid(circuit_closures={2:['adb', 'bec', 'cfa',
            ....:                                  'def'], 3:['abcdef']})
            sage: N = Matroid(M, regular=True)
            sage: N
            Regular matroid of rank 3 on 6 elements with 16 bases
            sage: Matrix(N)
            [1 0 0 1 1 0]
            [0 1 0 1 1 1]
            [0 0 1 0 1 1]

    The ``regular`` option::

        sage: M = Matroid(reduced_matrix=[[1, 1, 0],
        ....:                             [1, 0, 1],
        ....:                             [0, 1, 1]], regular=True)
        sage: M
        Regular matroid of rank 3 on 6 elements with 16 bases

        sage: M.is_isomorphic(matroids.CompleteGraphic(4))
        True

    By default we check if the resulting matroid is actually regular. To
    increase speed, this check can be skipped::

        sage: M = matroids.named_matroids.Fano()
        sage: N = Matroid(M, regular=True)
        Traceback (most recent call last):
        ...
        ValueError: input is not a valid regular matroid
        sage: N = Matroid(M, regular=True, check=False)
        sage: N
        Regular matroid of rank 3 on 7 elements with 32 bases

        sage: N.is_valid()
        False

    Sometimes the output is regular, but represents a different matroid
    from the one you intended::

        sage: M = Matroid(Matrix(GF(3), [[1, 0, 1, 1], [0, 1, 1, 2]]))
        sage: N = Matroid(Matrix(GF(3), [[1, 0, 1, 1], [0, 1, 1, 2]]),
        ....:             regular=True)
        sage: N.is_valid()
        True
        sage: N.is_isomorphic(M)
        False

    TESTS::

        sage: Matroid()
        Traceback (most recent call last):
        ...
        TypeError: no input data given for Matroid()
        sage: Matroid("abc", bases=["abc"], foo="bar")
        Traceback (most recent call last):
        ...
        TypeError: Matroid() got an unexpected keyword argument 'foo'
        sage: Matroid(data=["x"], matrix=Matrix(1,1))
        Traceback (most recent call last):
        ...
        TypeError: Matroid() got an unexpected keyword argument 'matrix'
        sage: Matroid(bases=["x"], matrix=Matrix(1,1))
        Traceback (most recent call last):
        ...
        TypeError: Matroid() got an unexpected keyword argument 'matrix'
        sage: Matroid(Matrix(1,1), ring=ZZ, field=QQ)
        Traceback (most recent call last):
        ...
        TypeError: Matroid() got an unexpected keyword argument 'ring'
        sage: Matroid(rank_function=lambda X: len(X))
        Traceback (most recent call last):
        ...
        TypeError: for rank functions, the groundset needs to be specified
        sage: Matroid(matroid="rubbish")
        Traceback (most recent call last):
        ...
        TypeError: input 'rubbish' is not a matroid
    """
    # process options
    want_regular = kwds.pop('regular', False)
    check = kwds.pop('check', True)

    base_ring = None
    if 'field' in kwds:
        base_ring = kwds.pop('field')
        if check and not base_ring.is_field():
            raise TypeError("{} is not a field".format(base_ring))
    elif 'ring' in kwds:
        base_ring = kwds.pop('ring')
        if check and not base_ring.is_ring():
            raise TypeError("{} is not a ring".format(base_ring))

    # "key" is the kind of data we got
    key = None
    if data is None:
        for k in ['bases', 'independent_sets', 'circuits', 'graph',
                'matrix', 'reduced_matrix', 'rank_function', 'revlex',
                'circuit_closures', 'matroid']:
            if k in kwds:
                data = kwds.pop(k)
                key = k
                break
        else:
            # Assume that the single positional argument was actually
            # the data (instead of the groundset)
            data = groundset
            groundset = None

    if key is None:
        if isinstance(data, sage.graphs.graph.Graph):
            key = 'graph'
        elif is_Matrix(data):
            key = 'matrix'
        elif isinstance(data, sage.matroids.matroid.Matroid):
            key = 'matroid'
        elif isinstance(data, str):
            key = 'revlex'
        elif data is None:
            raise TypeError("no input data given for Matroid()")
        else:
            key = 'independent_sets'

    # Bases:
    if key == 'bases':
        if groundset is None:
            groundset = set()
            for B in data:
                groundset.update(B)
        M = BasisMatroid(groundset=groundset, bases=data)

    # Independent sets:
    elif key == 'independent_sets':
        # Convert to list of bases first
        rk = -1
        bases = []
        for I in data:
            if len(I) == rk:
                bases.append(I)
            elif len(I) > rk:
                bases = [I]
                rk = len(I)
        if groundset is None:
            groundset = set()
            for B in bases:
                groundset.update(B)
        M = BasisMatroid(groundset=groundset, bases=bases)

    # Circuits:
    elif key == 'circuits':
        # Convert to list of bases first
        # Determine groundset (note that this cannot detect coloops)
        if groundset is None:
            groundset = set()
            for C in data:
                groundset.update(C)
        # determine the rank by computing a basis element
        b = set(groundset)
        for C in data:
            I = b.intersection(C)
            if len(I) >= len(C):
                b.discard(I.pop())
        rk = len(b)
        # Construct the basis matroid of appropriate rank. Note: slow!
        BB = [frozenset(B) for B in combinations(groundset, rk) if not any([frozenset(C).issubset(B) for C in data])]
        M = BasisMatroid(groundset=groundset, bases=BB)

    # Graphs:

    elif key == 'graph':
        if isinstance(data, sage.graphs.generic_graph.GenericGraph):
            G = data
        else:
            G = Graph(data)
        # Decide on the groundset
        m = G.num_edges()
        if groundset is None:
            # 1. Attempt to use edge labels.
            sl = G.edge_labels()
            if len(sl) == len(set(sl)):
                groundset = sl
                # 2. If simple, use vertex tuples
            elif not G.has_multiple_edges():
                groundset = [(i, j) for i, j, k in G.edge_iterator()]
            else:
                # 3. Use numbers
                groundset = list(range(m))
        if want_regular:
        # Construct the incidence matrix
        # NOTE: we are not using Sage's built-in method because
        # 1) we would need to fix the loops anyway
        # 2) Sage will sort the columns, making it impossible to keep labels!
            V = G.vertices()
            n = G.num_verts()
            A = Matrix(ZZ, n, m, 0)
            mm = 0
            for i, j, k in G.edge_iterator():
                A[V.index(i), mm] = -1
                A[V.index(j), mm] += 1  # So loops get 0
                mm += 1
            M = RegularMatroid(matrix=A, groundset=groundset)
            want_regular = False  # Save some time, since result is already regular
        else:
            M = GraphicMatroid(G, groundset=groundset)

    # Matrices:
    elif key in ['matrix', 'reduced_matrix']:
        A = data
        is_reduced = (key == 'reduced_matrix')

        # Fix the representation
        if not is_Matrix(A):
            if base_ring is not None:
                A = Matrix(base_ring, A)
            else:
                A = Matrix(A)

        # Fix the ring
        if base_ring is not None:
            if A.base_ring() is not base_ring:
                A = A.change_ring(base_ring)
        elif A.base_ring() is ZZ and not want_regular:  # Usually a rational matrix is intended, we presume.
            A = A.change_ring(QQ)
            base_ring = QQ
        else:
            base_ring = A.base_ring()

        # Check groundset
        if groundset is not None:
            if not is_reduced:
                if len(groundset) == A.ncols():
                    pass
                elif len(groundset) == A.nrows() + A.ncols():
                    is_reduced = True
                else:
                    raise ValueError("groundset size does not correspond to matrix size")
            elif is_reduced:
                if len(groundset) == A.nrows() + A.ncols():
                    pass
                else:
                    raise ValueError("groundset size does not correspond to matrix size")

        if is_reduced:
            kw = dict(groundset=groundset, reduced_matrix=A)
        else:
            kw = dict(groundset=groundset, matrix=A)

        if isinstance(base_ring, FiniteField):
            q = base_ring.order()
        else:
            q = 0

        if q == 2:
            M = BinaryMatroid(**kw)
        elif q == 3:
            M = TernaryMatroid(**kw)
        elif q == 4:
            M = QuaternaryMatroid(**kw)
        else:
            M = LinearMatroid(ring=base_ring, **kw)

    # Rank functions:
    elif key == 'rank_function':
        if groundset is None:
            raise TypeError('for rank functions, the groundset needs to be specified')
        M = RankMatroid(groundset=groundset, rank_function=data)

    # RevLex-Index:
    elif key == "revlex":
        if groundset is None:
            raise TypeError('for RevLex-Index, the groundset needs to be specified')
        try:
            rk = kwds.pop("rank")
        except KeyError:
            raise TypeError('for RevLex-Index, the rank needs to be specified')

        groundset = tuple(groundset)
        data = tuple(data)
        rk = int(rk)
        N = len(groundset)

        def revlex_sort_key(s):
            return tuple(reversed(s))
        subsets = sorted(combinations(range(N), rk), key=revlex_sort_key)
        if len(data) != len(subsets):
            raise ValueError("expected string of length %s (%s choose %s), got %s" %
                (len(subsets), N, rk, len(data)))
        bases = []
        for i, x in enumerate(data):
            if x != '0':
                bases.append([groundset[c] for c in subsets[i]])
        M = BasisMatroid(groundset=groundset, bases=bases)

    # Circuit closures:
    elif key == 'circuit_closures':
        if isinstance(data, dict):
            CC = data
        else:
            # Convert to dictionary
            CC = {}
            for X in data:
                if X[0] not in CC:
                    CC[X[0]] = []
                CC[X[0]].append(X[1])

        if groundset is None:
            groundset = set()
            for X in itervalues(CC):
                for Y in X:
                    groundset.update(Y)

        M = CircuitClosuresMatroid(groundset=groundset, circuit_closures=CC)

    # Matroids:
    elif key == 'matroid':
        if not isinstance(data, sage.matroids.matroid.Matroid):
            raise TypeError("input {!r} is not a matroid".format(data))
        M = data

    else:
        raise AssertionError("unknown key %r" % key)

    # All keywords should be used
    for k in kwds:
        raise TypeError("Matroid() got an unexpected keyword argument '{}'".format(k))

    if want_regular:
        M = sage.matroids.utilities.make_regular_matroid_from_matroid(M)
        if check and not M.is_valid():
            raise ValueError('input is not a valid regular matroid')

    return M
示例#58
0
def is_Matrix(x):
    try:
        from sage.structure.element import is_Matrix
    except ImportError:
        return False
    return is_Matrix(x)