Exemplo n.º 1
0
        def coxeter_knuth_graph(self):
            r"""
            Return the Coxeter-Knuth graph of type `A`.

            The Coxeter-Knuth graph of type `A` is generated by the Coxeter-Knuth relations which are
            given by `a a+1 a \sim a+1 a a+1`, `abc \sim acb` if `b<a<c` and `abc \sim bac` if `a<c<b`.

            EXAMPLES::

                sage: W = WeylGroup(['A',4], prefix='s')
                sage: w = W.from_reduced_word([1,2,1,3,2])
                sage: D = w.coxeter_knuth_graph()
                sage: D.vertices()
                [(1, 2, 1, 3, 2),
                (1, 2, 3, 1, 2),
                (2, 1, 2, 3, 2),
                (2, 1, 3, 2, 3),
                (2, 3, 1, 2, 3)]
                sage: D.edges()
                [((1, 2, 1, 3, 2), (1, 2, 3, 1, 2), None),
                ((1, 2, 1, 3, 2), (2, 1, 2, 3, 2), None),
                ((2, 1, 2, 3, 2), (2, 1, 3, 2, 3), None),
                ((2, 1, 3, 2, 3), (2, 3, 1, 2, 3), None)]

                sage: w = W.from_reduced_word([1,3])
                sage: D = w.coxeter_knuth_graph()
                sage: D.vertices()
                [(1, 3), (3, 1)]
                sage: D.edges()
                []

            TESTS::

                sage: W = WeylGroup(['B',4], prefix='s')
                sage: w = W.from_reduced_word([1,2])
                sage: w.coxeter_knuth_graph()
                Traceback (most recent call last):
                ...
                NotImplementedError: This has only been implemented in finite type A so far!
            """
            from sage.graphs.all import Graph

            R = [tuple(v) for v in self.reduced_words()]
            G = Graph()
            G.add_vertices(R)
            G.add_edges([v, vp] for v in R for vp in self.coxeter_knuth_neighbor(v))
            return G
Exemplo n.º 2
0
    def __init__(self, E, l, Velu=False, upper_bit_limit=100, special=True):

        self._l = l
        self._E = E
        global VELU
        VELU = Velu

        global UPPER_BITS
        UPPER_BITS = upper_bit_limit

        self.field = E.base_field()

        try:
            self._neighbours, self._vertices, self._special = BFS(
                E.j_invariant(), l)

        except large_extension_error as e:
            print(
                "Upper limit for bitlength of size of extension field exceeded"
            )
            print(e.msg)
            return

        if self._special and special:
            print("Curve with j_invariant 0 or 1728 found, may malfunction.")
        self._depths = {}
        self._levels = []
        self._depth = 0
        self._graph = Graph(multiedges=True, loops=True)
        for s in self._neighbours.values():
            self._graph.add_vertex(s[0])

        for s in self._neighbours.values():
            for i in range(1, len(s)):
                if not self._graph.has_edge(s[0], s[i][0]):
                    for j in range(s[i][1]):
                        self._graph.add_edge((s[0], s[i][0], j))
        if len(self._vertices) > 1 and E.is_ordinary():
            self.compute_depths()
        else:
            self._depths = {str(E.j_invariant()): 0}
            self._levels = [self._vertices]
Exemplo n.º 3
0
def spanning_forest(M):
    r"""
    Return a list of edges of a spanning forest of the bipartite
    graph defined by `M`

    INPUT:

    - ``M`` -- a matrix defining a bipartite graph G. The vertices are the
      rows and columns, if `M[i,j]` is non-zero, then there is an edge
      between row `i` and column `j`.

    OUTPUT:

    A list of tuples `(r_i,c_i)` representing edges between row `r_i` and column `c_i`.

    EXAMPLES::

        sage: len(sage.matroids.utilities.spanning_forest(matrix([[1,1,1],[1,1,1],[1,1,1]])))
        5
        sage: len(sage.matroids.utilities.spanning_forest(matrix([[0,0,1],[0,1,0],[0,1,0]])))
        3
    """
    # Given a matrix, produce a spanning tree
    G = Graph()
    m = M.ncols()
    for (x,y) in M.dict():
        G.add_edge(x+m,y)
    T = []
    # find spanning tree in each component
    for component in G.connected_components():
        spanning_tree = kruskal(G.subgraph(component))
        for (x,y,z) in spanning_tree:
            if x < m:
                t = x
                x = y
                y = t
            T.append((x-m,y))
    return T
Exemplo n.º 4
0
    def __init__(self, E, ls, special=False):

        self._ls = ls
        j = E.j_invariant()

        self.field = E.base_field()
        self._graph = Graph(multiedges=True, loops=True)

        self._special = False
        self._graph.add_vertex(j)
        queue = [j]
        R = rainbow(len(ls) + 1)
        self._rainbow = R
        self._edge_colors = {R[i]: [] for i in range(len(ls))}

        while queue:
            color = 0
            s = queue.pop(0)

            if s == 0 or s == 1728:
                self._special = True

            for l in ls:
                neighb = isogenous_curves(s, l)

                for i in neighb:
                    if not self._graph.has_vertex(i[0]):
                        queue.append(i[0])
                        self._graph.add_vertex(i[0])
                    if not ((s, i[0], l) in self._edge_colors[R[color]] or
                            (i[0], s, l) in self._edge_colors[R[color]]):
                        for _ in range(i[1]):
                            self._graph.add_edge((s, i[0], l))
                        self._edge_colors[R[color]].append((s, i[0], l))
                color += 1

        if self._special and special:
            print("Curve with j_invariant 0 or 1728 found, may malfunction.")
Exemplo n.º 5
0
        def coxeter_knuth_graph(self):
            r"""
            Return the Coxeter-Knuth graph of type `A`.

            The Coxeter-Knuth graph of type `A` is generated by the Coxeter-Knuth relations which are
            given by `a a+1 a \sim a+1 a a+1`, `abc \sim acb` if `b<a<c` and `abc \sim bac` if `a<c<b`.

            EXAMPLES::

                sage: W = WeylGroup(['A',4], prefix='s')
                sage: w = W.from_reduced_word([1,2,1,3,2])
                sage: D = w.coxeter_knuth_graph()
                sage: D.vertices()
                [(1, 2, 1, 3, 2),
                (1, 2, 3, 1, 2),
                (2, 1, 2, 3, 2),
                (2, 1, 3, 2, 3),
                (2, 3, 1, 2, 3)]
                sage: D.edges()
                [((1, 2, 1, 3, 2), (1, 2, 3, 1, 2), None),
                ((1, 2, 1, 3, 2), (2, 1, 2, 3, 2), None),
                ((2, 1, 2, 3, 2), (2, 1, 3, 2, 3), None),
                ((2, 1, 3, 2, 3), (2, 3, 1, 2, 3), None)]

                sage: w = W.from_reduced_word([1,3])
                sage: D = w.coxeter_knuth_graph()
                sage: D.vertices()
                [(1, 3), (3, 1)]
                sage: D.edges()
                []

            TESTS::

                sage: W = WeylGroup(['B',4], prefix='s')
                sage: w = W.from_reduced_word([1,2])
                sage: w.coxeter_knuth_graph()
                Traceback (most recent call last):
                ...
                NotImplementedError: This has only been implemented in finite type A so far!
            """
            from sage.graphs.all import Graph
            R = [tuple(v) for v in self.reduced_words()]
            G = Graph()
            G.add_vertices(R)
            G.add_edges([v, vp] for v in R
                        for vp in self.coxeter_knuth_neighbor(v))
            return G
Exemplo n.º 6
0
def spanning_forest(M):
    r"""
    Return a list of edges of a spanning forest of the bipartite
    graph defined by `M`

    INPUT:

    - ``M`` -- a matrix defining a bipartite graph G. The vertices are the 
      rows and columns, if `M[i,j]` is non-zero, then there is an edge
      between row `i` and column `j`.

    OUTPUT:

    A list of tuples `(r_i,c_i)` representing edges between row `r_i` and column `c_i`.

    EXAMPLES::

        sage: len(sage.matroids.utilities.spanning_forest(matrix([[1,1,1],[1,1,1],[1,1,1]])))
        5
        sage: len(sage.matroids.utilities.spanning_forest(matrix([[0,0,1],[0,1,0],[0,1,0]])))
        3
    """
    # Given a matrix, produce a spanning tree
    G = Graph()
    m = M.ncols()
    for (x, y) in M.dict():
        G.add_edge(x + m, y)
    T = []
    # find spanning tree in each component
    for component in G.connected_components():
        spanning_tree = kruskal(G.subgraph(component))
        for (x, y, z) in spanning_tree:
            if x < m:
                t = x
                x = y
                y = t
            T.append((x - m, y))
    return T
Exemplo n.º 7
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
Exemplo n.º 8
0
def lift_cross_ratios(A, lift_map=None):
    r"""
    Return a matrix which arises from the given matrix by lifting cross ratios.

    INPUT:

    - ``A`` -- a matrix over a ring ``source_ring``.
    - ``lift_map`` -- a python dictionary, mapping each cross ratio of ``A`` to some element
      of a target ring, and such that ``lift_map[source_ring(1)] = target_ring(1)``.

    OUTPUT:

    - ``Z`` -- a matrix over the ring ``target_ring``.

    The intended use of this method is to create a (reduced) matrix representation of a
    matroid ``M`` over a ring ``target_ring``, given a (reduced) matrix representation of
    ``A`` of ``M`` over a ring ``source_ring`` and a map ``lift_map`` from ``source_ring``
    to ``target_ring``.

    This method will create a unique candidate representation ``Z``, but will not verify
    if ``Z`` is indeed a representation of ``M``. However, this is guaranteed if the
    conditions of the lift theorem (see [PvZ2010]_) hold for the lift map in combination with
    the matrix ``A``.

    For a lift map `f` and a matrix `A` these conditions are as follows. First of all
    `f: S \rightarrow T`, where `S` is a set of invertible elements of the source ring and
    `T` is a set of invertible elements of the target ring. The matrix `A` has entries
    from the source ring, and each cross ratio of `A` is contained in `S`. Moreover:

    - `1 \in S`, `1 \in T`;
    - for all `x \in S`: `f(x) = 1` if and only if `x = 1`;
    - for all `x, y \in S`: if `x + y = 0` then `f(x) + f(y) = 0`;
    - for all `x, y \in S`: if `x + y = 1` then `f(x) + f(y) = 1`;
    - for all `x, y, z \in S`: if  `xy = z` then `f(x)f(y) = f(z)`.

    Any ring homomorphism `h: P \rightarrow R` induces a lift map from the set of units `S` of
    `P` to the set of units `T` of `R`. There exist lift maps which do not arise in
    this manner. Several such maps can be created by the function
    :meth:`lift_map() <sage.matroids.utilities.lift_map>`.

    .. SEEALSO::

        :meth:`lift_map() <sage.matroids.utilities.lift_map>`

    EXAMPLES::

        sage: from sage.matroids.advanced import lift_cross_ratios, lift_map, LinearMatroid
        sage: R = GF(7)
        sage: to_sixth_root_of_unity = lift_map('sru')
        sage: A = Matrix(R, [[1, 0, 6, 1, 2],[6, 1, 0, 0, 1],[0, 6, 3, 6, 0]])
        sage: A
        [1 0 6 1 2]
        [6 1 0 0 1]
        [0 6 3 6 0]
        sage: Z = lift_cross_ratios(A, to_sixth_root_of_unity)
        sage: Z
        [ 1  0  1  1  1]
        [ 1  1  0  0  z]
        [ 0  z - 1  1  -z + 1  0]
        sage: M = LinearMatroid(reduced_matrix = A)
        sage: sorted(M.cross_ratios())
        [3, 5]
        sage: N = LinearMatroid(reduced_matrix = Z)
        sage: sorted(N.cross_ratios())
        [-z + 1, z]
        sage: M.is_isomorphism(N, {e:e for e in M.groundset()})
        True

    """
    for s, t in iteritems(lift_map):
        source_ring = s.parent()
        target_ring = t.parent()
        break
    plus_one1 = source_ring(1)
    minus_one1 = source_ring(-1)
    plus_one2 = target_ring(1)
    minus_one2 = target_ring(-1)

    G = Graph([((r, 0), (c, 1), (r, c)) for r, c in A.nonzero_positions()])
    # write the entries of (a scaled version of) A as products of cross ratios of A
    T = set()
    for C in G.connected_components():
        T.update(G.subgraph(C).min_spanning_tree())
    # - fix a tree of the support graph G to units (= empty dict, product of 0 terms)
    F = {entry[2]: dict() for entry in T}
    W = set(G.edges()) - set(T)
    H = G.subgraph(edges=T)
    while W:
        # - find an edge in W to process, closing a circuit in H which is induced in G
        edge = W.pop()
        path = H.shortest_path(edge[0], edge[1])
        retry = True
        while retry:
            retry = False
            for edge2 in W:
                if edge2[0] in path and edge2[1] in path:
                    W.add(edge)
                    edge = edge2
                    W.remove(edge)
                    path = H.shortest_path(edge[0], edge[1])
                    retry = True
                    break
        entry = edge[2]
        entries = []
        for i in range(len(path) - 1):
            v = path[i]
            w = path[i + 1]
            if v[1] == 0:
                entries.append((v[0], w[0]))
            else:
                entries.append((w[0], v[0]))
        # - compute the cross ratio `cr` of this whirl
        cr = source_ring(A[entry])
        div = True
        for entry2 in entries:
            if div:
                cr = cr / A[entry2]
            else:
                cr = cr * A[entry2]
            div = not div

        monomial = dict()
        if len(path) % 4 == 0:
            if not cr == plus_one1:
                monomial[cr] = 1
        else:
            cr = -cr
            if not cr == plus_one1:
                monomial[cr] = 1
            if minus_one1 in monomial:
                monomial[minus_one1] = monomial[minus_one1] + 1
            else:
                monomial[minus_one1] = 1

        if cr != plus_one1 and not cr in lift_map:
            raise ValueError("Input matrix has a cross ratio " + str(cr) +
                             ", which is not in the lift_map")
        # - write the entry as a product of cross ratios of A
        div = True
        for entry2 in entries:
            if div:
                for cr, degree in iteritems(F[entry2]):
                    if cr in monomial:
                        monomial[cr] = monomial[cr] + degree
                    else:
                        monomial[cr] = degree
            else:
                for cr, degree in iteritems(F[entry2]):
                    if cr in monomial:
                        monomial[cr] = monomial[cr] - degree
                    else:
                        monomial[cr] = -degree
            div = not div
        F[entry] = monomial
        # - current edge is done, can be used in next iteration
        H.add_edge(edge)

    # compute each entry of Z as the product of lifted cross ratios
    Z = Matrix(target_ring, A.nrows(), A.ncols())
    for entry, monomial in iteritems(F):
        Z[entry] = plus_one2
        for cr, degree in iteritems(monomial):
            if cr == minus_one1:
                Z[entry] = Z[entry] * (minus_one2**degree)
            else:
                Z[entry] = Z[entry] * (lift_map[cr]**degree)

    return Z
Exemplo n.º 9
0
def spanning_stars(M):
    r"""
    Returns the edges of a connected subgraph that is a union of 
    all edges incident some subset of vertices.

    INPUT:

    - ``M`` -- a matrix defining a bipartite graph G. The vertices are the 
      rows and columns, if `M[i,j]` is non-zero, then there is an edge
      between row i and column 0.

    OUTPUT:

    A list of tuples `(row,column)` in a spanning forest of the bipartite graph defined by ``M``
    
    EXAMPLES::

        sage: edges = sage.matroids.utilities.spanning_stars(matrix([[1,1,1],[1,1,1],[1,1,1]]))
        sage: Graph([(x+3, y) for x,y in edges]).is_connected()
        True
    """

    G = Graph()
    m = M.ncols()
    for (x, y) in M.dict():
        G.add_edge(x + m, y)

    delta = (M.nrows() + m)**0.5
    # remove low degree vertices
    H = []
    # candidate vertices
    V_0 = set([])
    d = 0
    while G.order() > 0:
        (x, d) = min(G.degree_iterator(labels=True), key=itemgetter(1))
        if d < delta:
            V_0.add(x)
            H.extend(G.edges_incident(x, False))
            G.delete_vertex(x)
        else:
            break

    # min degree is at least sqrt(n)
    # greedily remove vertices
    G2 = G.copy()
    # set of picked vertices
    V_1 = set([])
    while G2.order() > 0:
        # choose vertex with maximum degree in G2
        (x, d) = max(G2.degree_iterator(labels=True), key=itemgetter(1))
        V_1.add(x)
        G2.delete_vertices(G2.neighbors(x))
        G2.delete_vertex(x)

    # G2 is a graph of all edges incident to V_1
    G2 = Graph()
    for v in V_1:
        for u in G.neighbors(v):
            G2.add_edge(u, v)

    V = V_0 | V_1
    # compute a spanning tree
    T = spanning_forest(M)
    for (x, y) in T:
        if not x in V and not y in V:
            V.add(v)

    for v in V:
        if G.has_vertex(v):  # some vertices are not in G
            H.extend(G.edges_incident(v, False))

    # T contain all edges in some spanning tree
    T = []
    for (x, y) in H:
        if x < m:
            t = x
            x = y
            y = t
        T.append((x - m, y))
    return T
Exemplo n.º 10
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
Exemplo n.º 11
0
def lift_cross_ratios(A, lift_map = None):
    r"""
    Return a matrix which arises from the given matrix by lifting cross ratios.

    INPUT:

    - ``A`` -- a matrix over a ring ``source_ring``.
    - ``lift_map`` -- a python dictionary, mapping each cross ratio of ``A`` to some element
      of a target ring, and such that ``lift_map[source_ring(1)] = target_ring(1)``.

    OUTPUT:

    - ``Z`` -- a matrix over the ring ``target_ring``.

    The intended use of this method is to create a (reduced) matrix representation of a
    matroid ``M`` over a ring ``target_ring``, given a (reduced) matrix representation of
    ``A`` of ``M`` over a ring ``source_ring`` and a map ``lift_map`` from ``source_ring``
    to ``target_ring``.

    This method will create a unique candidate representation ``Z``, but will not verify
    if ``Z`` is indeed a representation of ``M``. However, this is guaranteed if the
    conditions of the lift theorem (see [PvZ2010]_) hold for the lift map in combination with
    the matrix ``A``.

    For a lift map `f` and a matrix `A` these conditions are as follows. First of all
    `f: S \rightarrow T`, where `S` is a set of invertible elements of the source ring and
    `T` is a set of invertible elements of the target ring. The matrix `A` has entries
    from the source ring, and each cross ratio of `A` is contained in `S`. Moreover:

    - `1 \in S`, `1 \in T`;
    - for all `x \in S`: `f(x) = 1` if and only if `x = 1`;
    - for all `x, y \in S`: if `x + y = 0` then `f(x) + f(y) = 0`;
    - for all `x, y \in S`: if `x + y = 1` then `f(x) + f(y) = 1`;
    - for all `x, y, z \in S`: if  `xy = z` then `f(x)f(y) = f(z)`.

    Any ring homomorphism `h: P \rightarrow R` induces a lift map from the set of units `S` of
    `P` to the set of units `T` of `R`. There exist lift maps which do not arise in
    this manner. Several such maps can be created by the function
    :meth:`lift_map() <sage.matroids.utilities.lift_map>`.

    .. SEEALSO::

        :meth:`lift_map() <sage.matroids.utilities.lift_map>`

    EXAMPLES::

        sage: from sage.matroids.advanced import lift_cross_ratios, lift_map, LinearMatroid
        sage: R = GF(7)
        sage: to_sixth_root_of_unity = lift_map('sru')
        sage: A = Matrix(R, [[1, 0, 6, 1, 2],[6, 1, 0, 0, 1],[0, 6, 3, 6, 0]])
        sage: A
        [1 0 6 1 2]
        [6 1 0 0 1]
        [0 6 3 6 0]
        sage: Z = lift_cross_ratios(A, to_sixth_root_of_unity)
        sage: Z
        [ 1  0  1  1  1]
        [ 1  1  0  0  z]
        [ 0  z - 1  1  -z + 1  0]
        sage: M = LinearMatroid(reduced_matrix = A)
        sage: sorted(M.cross_ratios())
        [3, 5]
        sage: N = LinearMatroid(reduced_matrix = Z)
        sage: sorted(N.cross_ratios())
        [-z + 1, z]
        sage: M.is_isomorphism(N, {e:e for e in M.groundset()})
        True

    """
    for s, t in iteritems(lift_map):
        source_ring = s.parent()
        target_ring = t.parent()
        break
    plus_one1 = source_ring(1)
    minus_one1 = source_ring(-1)
    plus_one2 = target_ring(1)
    minus_one2 = target_ring(-1)

    G = Graph([((r,0),(c,1),(r,c)) for r,c in A.nonzero_positions()])
    # write the entries of (a scaled version of) A as products of cross ratios of A
    T = set()
    for C in G.connected_components():
        T.update(G.subgraph(C).min_spanning_tree())
    # - fix a tree of the support graph G to units (= empty dict, product of 0 terms)
    F = {entry[2]: dict() for entry in T}
    W = set(G.edges()) - set(T)
    H = G.subgraph(edges = T)
    while W:
        # - find an edge in W to process, closing a circuit in H which is induced in G
        edge = W.pop()
        path = H.shortest_path(edge[0], edge[1])
        retry = True
        while retry:
            retry = False
            for edge2 in W:
                if edge2[0] in path and edge2[1] in path:
                    W.add(edge)
                    edge = edge2
                    W.remove(edge)
                    path = H.shortest_path(edge[0], edge[1])
                    retry = True
                    break
        entry = edge[2]
        entries = []
        for i in range(len(path) - 1):
            v = path[i]
            w = path[i+1]
            if v[1] == 0:
                entries.append((v[0],w[0]))
            else:
                entries.append((w[0],v[0]))
        # - compute the cross ratio `cr` of this whirl
        cr = source_ring(A[entry])
        div = True
        for entry2 in entries:
            if div:
                cr = cr/A[entry2]
            else:
                cr = cr* A[entry2]
            div = not div

        monomial = dict()
        if len(path) % 4 == 0:
            if not cr == plus_one1:
                monomial[cr] = 1
        else:
            cr = -cr
            if not cr ==plus_one1:
                monomial[cr] = 1
            if  minus_one1 in monomial:
                monomial[minus_one1] = monomial[minus_one1] + 1
            else:
                monomial[minus_one1] = 1

        if cr != plus_one1 and not cr in lift_map:
            raise ValueError("Input matrix has a cross ratio "+str(cr)+", which is not in the lift_map")
        # - write the entry as a product of cross ratios of A
        div = True
        for entry2 in entries:
            if div:
                for cr, degree in iteritems(F[entry2]):
                    if cr in monomial:
                        monomial[cr] = monomial[cr]+ degree
                    else:
                        monomial[cr] = degree
            else:
                for cr, degree in iteritems(F[entry2]):
                    if cr in monomial:
                        monomial[cr] = monomial[cr] - degree
                    else:
                        monomial[cr] = -degree
            div = not div
        F[entry] = monomial
        # - current edge is done, can be used in next iteration
        H.add_edge(edge)

    # compute each entry of Z as the product of lifted cross ratios
    Z = Matrix(target_ring, A.nrows(), A.ncols())
    for entry, monomial in iteritems(F):
        Z[entry] = plus_one2
        for cr,degree in iteritems(monomial):
            if cr == minus_one1:
                Z[entry] = Z[entry] * (minus_one2**degree)
            else:
                Z[entry] = Z[entry] * (lift_map[cr]**degree)

    return Z
Exemplo n.º 12
0
def spanning_stars(M):
    r"""
    Returns the edges of a connected subgraph that is a union of
    all edges incident some subset of vertices.

    INPUT:

    - ``M`` -- a matrix defining a bipartite graph G. The vertices are the
      rows and columns, if `M[i,j]` is non-zero, then there is an edge
      between row i and column 0.

    OUTPUT:

    A list of tuples `(row,column)` in a spanning forest of the bipartite graph defined by ``M``

    EXAMPLES::

        sage: edges = sage.matroids.utilities.spanning_stars(matrix([[1,1,1],[1,1,1],[1,1,1]]))
        sage: Graph([(x+3, y) for x,y in edges]).is_connected()
        True
    """

    G = Graph()
    m = M.ncols()
    for (x,y) in M.dict():
        G.add_edge(x+m,y)

    delta = (M.nrows()+m)**0.5
    # remove low degree vertices
    H = []
    # candidate vertices
    V_0 = set([])
    d = 0
    while G.order()>0:
        (x,d) = min(G.degree_iterator(labels=True),key=itemgetter(1))
        if d < delta:
            V_0.add(x)
            H.extend(G.edges_incident(x,False))
            G.delete_vertex(x)
        else:
            break

    # min degree is at least sqrt(n)
    # greedily remove vertices
    G2 = G.copy()
    # set of picked vertices
    V_1 = set([])
    while G2.order()>0:
        # choose vertex with maximum degree in G2
        (x,d) = max(G2.degree_iterator(labels=True),key=itemgetter(1))
        V_1.add(x)
        G2.delete_vertices(G2.neighbors(x))
        G2.delete_vertex(x)

    # G2 is a graph of all edges incident to V_1
    G2 = Graph()
    for v in V_1:
        for u in G.neighbors(v):
            G2.add_edge(u,v)

    V = V_0 | V_1
    # compute a spanning tree
    T = spanning_forest(M)
    for (x,y) in T:
        if not x in V and not y in V:
            V.add(v)

    for v in V:
        if G.has_vertex(v): # some vertices are not in G
            H.extend(G.edges_incident(v,False))

    # T contain all edges in some spanning tree
    T = []
    for (x,y) in H:
        if x < m:
            t = x
            x = y
            y = t
        T.append((x-m,y))
    return T
Exemplo n.º 13
0
def Matroid(*args, **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`` -- If provided, the groundset of the matroid.
      If not provided, the function attempts to determine a groundset from the
      data.
    - ``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.
    - ``matroid`` -- An object that is already a matroid. Useful only with the
      ``regular`` option.

    Up to two unnamed arguments are allowed.

    - One unnamed argument, no named arguments other than ``regular`` -- the
      input should be either a graph, or a matrix, or a list of independent
      sets containing all bases, or a matroid.
    - Two unnamed arguments: the first is the groundset, the second a graph,
      or a matrix, or a list of independent sets containing all bases, or a
      matroid.
    - One unnamed argument, at least one named argument: the unnamed argument
      is the groundset, the named argument is as above (but must be different
      from ``groundset``).

    The examples section details how each of the input types deals with
    explicit or implicit groundset arguments.

    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)
            <type '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)
            Regular matroid of rank 9 on 15 elements with 2000 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)])
            sage: M = Matroid('abcd', G)
            sage: M.rank(['b', 'c'])
            1

        If no groundset is provided, we attempt to use the edge 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 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)
            sage: sorted(M.groundset())
            [(0, 1), (0, 2), (1, 2)]

            sage: G = Graph([(0, 1), (0, 2), (0, 2), (1, 2)])
            sage: M = Matroid(G)
            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~')
            Regular matroid of rank 9 on 17 elements with 4004 bases

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

            sage: Matroid(graph=41/2)
            Traceback (most recent call last):
            ...
            ValueError: input does not seem to represent 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

    #. 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 does not correspond to 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

    """
    # These are the valid arguments:
    inputS = set([
        'groundset', 'bases', 'independent_sets', 'circuits', 'graph',
        'matrix', 'reduced_matrix', 'rank_function', 'circuit_closures',
        'matroid'
    ])

    # process options
    if 'regular' in kwds:
        want_regular = kwds['regular']
        kwds.pop('regular')
    else:
        want_regular = False
    if 'check' in kwds:
        want_check = kwds['check']
        kwds.pop('check')
    else:
        want_check = True

    base_ring = None
    have_field = False
    if 'field' in kwds:
        base_ring = kwds['field']
        kwds.pop('field')
        have_field = True
        if 'ring' in kwds:
            raise ValueError("only one of ring and field can be specified.")
        try:
            if not base_ring.is_field():
                raise TypeError("specified ``field`` is not a field.")
        except AttributeError:
            raise TypeError("specified ``field`` is not a field.")
    if 'ring' in kwds:
        base_ring = kwds['ring']
        kwds.pop('ring')
        try:
            if not base_ring.is_ring():
                raise TypeError("specified ``ring`` is not a ring.")
        except AttributeError:
            raise TypeError("specified ``ring`` is not a ring.")

    # Process unnamed arguments
    if len(args) > 0:
        if 'groundset' in kwds:
            raise ValueError(
                'when using unnamed arguments, groundset must be the first unnamed argument or be implicit.'
            )
        if len(args) > 2:
            raise ValueError('at most two unnamed arguments are allowed.')
        if len(args) == 2 or len(kwds) > 0:
            # First argument should be the groundset
            kwds['groundset'] = args[0]
        # Check for unnamed data
        dataindex = -1
        if len(args) == 2:
            dataindex = 1
        if len(args) == 1 and len(kwds) == 0:
            dataindex = 0
        if dataindex > -1:
            # One unnamed argument, no named arguments
            if isinstance(args[dataindex], sage.graphs.graph.Graph):
                kwds['graph'] = args[dataindex]
            elif isinstance(args[dataindex], sage.matrix.matrix.Matrix):
                kwds['matrix'] = args[dataindex]
            elif isinstance(args[dataindex], sage.matroids.matroid.Matroid):
                kwds['matroid'] = args[dataindex]
            else:
                kwds['independent_sets'] = args[dataindex]

    # Check for multiple types of input
    if len(set(kwds).difference(inputS)) > 0:
        raise ValueError("unknown input argument")
    if ('grondset' in kwds and len(kwds) != 2) or ('groundset' not in kwds
                                                   and len(kwds) > 1):
        raise ValueError("only one type of input may be specified.")

    # Bases:
    if 'bases' in kwds:
        if 'groundset' not in kwds:
            gs = set()
            for B in kwds['bases']:
                gs.update(B)
            kwds['groundset'] = gs
        M = BasisMatroid(groundset=kwds['groundset'], bases=kwds['bases'])

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

    # Circuits:
    if 'circuits' in kwds:
        # Convert to list of bases first
        # Determine groundset (note that this cannot detect coloops)
        if 'groundset' not in kwds:
            gs = set()
            for C in kwds['circuits']:
                gs.update(C)
            kwds['groundset'] = gs
        # determine the rank by computing a basis
        B = set(kwds['groundset'])
        for C in kwds['circuits']:
            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(kwds['groundset'], rk)
            if not any([frozenset(C).issubset(B) for C in kwds['circuits']])
        ]
        M = BasisMatroid(groundset=kwds['groundset'], bases=BB)

    # Graphs:
    if 'graph' in kwds:
        # 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!
        G = kwds['graph']
        if not isinstance(G, sage.graphs.generic_graph.GenericGraph):
            try:
                G = Graph(G)
            except (ValueError, TypeError, NetworkXError):
                raise ValueError("input does not seem to represent a graph.")
        V = G.vertices()
        E = G.edges()
        n = G.num_verts()
        m = G.num_edges()
        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
        # Decide on the groundset
        if 'groundset' not in kwds:
            # 1. Attempt to use edge labels.
            sl = G.edge_labels()
            if len(sl) == len(set(sl)):
                kwds['groundset'] = sl
                # 2. If simple, use vertex tuples
            elif not G.has_multiple_edges():
                kwds['groundset'] = [(i, j) for i, j, k in G.edge_iterator()]
            else:
                # 3. Use numbers
                kwds['groundset'] = range(m)
        M = RegularMatroid(matrix=A, groundset=kwds['groundset'])
        want_regular = False  # Save some time, since result is already regular

    # Matrices:
    if 'matrix' in kwds or 'reduced_matrix' in kwds:
        if 'matrix' in kwds:
            A = kwds['matrix']
        if 'reduced_matrix' in kwds:
            A = kwds['reduced_matrix']

        # Fix the representation
        if not isinstance(A, sage.matrix.matrix.Matrix):
            try:
                if base_ring is not None:
                    A = Matrix(base_ring, A)
                else:
                    A = Matrix(A)
            except ValueError:
                raise ValueError("input does not seem to contain a matrix.")

        # Fix the ring
        if base_ring is not None:
            if A.base_ring() != base_ring:
                A = A.change_ring(base_ring)
        elif A.base_ring(
        ) == 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()

        # Determine groundset:
        if 'matrix' in kwds:
            if 'groundset' in kwds:
                if len(kwds['groundset']) == A.nrows() + A.ncols():
                    kwds['reduced_matrix'] = A
                    kwds.pop('matrix')
                else:
                    if len(kwds['groundset']) != A.ncols():
                        raise ValueError(
                            "groundset size does not correspond to matrix size."
                        )
            else:
                kwds['groundset'] = range(A.ncols())
        if 'reduced_matrix' in kwds:
            if 'groundset' in kwds:
                if len(kwds['groundset']) != A.nrows() + A.ncols():
                    raise ValueError(
                        "groundset size does not correspond to matrix size.")
            else:
                kwds['groundset'] = range(A.nrows() + A.ncols())
        if 'matrix' in kwds:
            if base_ring == GF(2):
                M = BinaryMatroid(groundset=kwds['groundset'], matrix=A)
            elif base_ring == GF(3):
                M = TernaryMatroid(groundset=kwds['groundset'], matrix=A)
            elif base_ring.is_field() and base_ring.order(
            ) == 4:  # GF(4) can have different generators.
                M = QuaternaryMatroid(groundset=kwds['groundset'], matrix=A)
            else:
                M = LinearMatroid(groundset=kwds['groundset'],
                                  matrix=A,
                                  ring=base_ring)

        if 'reduced_matrix' in kwds:
            if A.base_ring() == GF(2):
                M = BinaryMatroid(groundset=kwds['groundset'],
                                  reduced_matrix=A)
            elif A.base_ring() == GF(3):
                M = TernaryMatroid(groundset=kwds['groundset'],
                                   reduced_matrix=A)
            elif A.base_ring().is_field() and A.base_ring().order(
            ) == 4:  # GF(4) can have different generators.
                M = QuaternaryMatroid(groundset=kwds['groundset'],
                                      reduced_matrix=A)
            else:
                M = LinearMatroid(groundset=kwds['groundset'],
                                  reduced_matrix=A,
                                  ring=base_ring)

    # Rank functions:
    if 'rank_function' in kwds:
        if 'groundset' not in kwds:
            raise ValueError(
                'for rank functions, groundset needs to be specified.')
        M = RankMatroid(groundset=kwds['groundset'],
                        rank_function=kwds['rank_function'])

    # Circuit closures:
    if 'circuit_closures' in kwds:
        if 'groundset' not in kwds:
            E = set()
            if isinstance(kwds['circuit_closures'], dict):
                for X in kwds['circuit_closures'].itervalues():
                    for Y in X:
                        E.update(Y)
            else:
                for X in kwds['circuit_closures']:
                    E.update(X[1])
        else:
            E = kwds['groundset']
        if not isinstance(kwds['circuit_closures'], dict):
            # Convert to dictionary
            CC = {}
            for X in kwds['circuit_closures']:
                if X[0] not in CC:
                    CC[X[0]] = []
                CC[X[0]].append(X[1])
        else:
            CC = kwds['circuit_closures']
        M = CircuitClosuresMatroid(groundset=E, circuit_closures=CC)

    # Matroids:
    if 'matroid' in kwds:
        M = kwds['matroid']
        if not isinstance(M, sage.matroids.matroid.Matroid):
            raise ValueError("input does not appear to be of Matroid type.")
    # Regular option:
    if want_regular:
        M = sage.matroids.utilities.make_regular_matroid_from_matroid(M)
        if want_check:
            if not M.is_valid():
                raise ValueError(
                    'input does not correspond to a valid regular matroid.')
    return M
Exemplo n.º 14
0
class Isogeny_graph:

    # Class for construction isogeny graphs with 1 or more degrees
    # ls is a list of primes which define the defining degrees
    def __init__(self, E, ls, special=False):

        self._ls = ls
        j = E.j_invariant()

        self.field = E.base_field()
        self._graph = Graph(multiedges=True, loops=True)

        self._special = False
        self._graph.add_vertex(j)
        queue = [j]
        R = rainbow(len(ls) + 1)
        self._rainbow = R
        self._edge_colors = {R[i]: [] for i in range(len(ls))}

        while queue:
            color = 0
            s = queue.pop(0)

            if s == 0 or s == 1728:
                self._special = True

            for l in ls:
                neighb = isogenous_curves(s, l)

                for i in neighb:
                    if not self._graph.has_vertex(i[0]):
                        queue.append(i[0])
                        self._graph.add_vertex(i[0])
                    if not ((s, i[0], l) in self._edge_colors[R[color]] or
                            (i[0], s, l) in self._edge_colors[R[color]]):
                        for _ in range(i[1]):
                            self._graph.add_edge((s, i[0], l))
                        self._edge_colors[R[color]].append((s, i[0], l))
                color += 1

        if self._special and special:
            print("Curve with j_invariant 0 or 1728 found, may malfunction.")

    def __repr__(self):
        return "Isogeny graph of degrees %r" % (self._ls)

    # Returns degrees of isogenies
    def degrees(self):
        return self._ls

    # Returns list of all edges
    def edges(self):
        return self._graph.edges()

    # Returns list of all vertices
    def vertices(self):
        return self._graph.vertices()

    # Plots the graph
    # Optional arguments figsize, vertex_size, vertex_labels and layout which are the same as in igraph
    def plot(self,
             figsize=None,
             edge_labels=False,
             vertex_size=None,
             layout=None):
        if vertex_size == None:
            return self._graph.plot(edge_colors=self._edge_colors,
                                    figsize=figsize,
                                    edge_labels=edge_labels,
                                    layout=layout)
        else:
            return self._graph.plot(edge_colors=self._edge_colors,
                                    figsize=figsize,
                                    edge_labels=edge_labels,
                                    vertex_size=vertex_size,
                                    layout=layout)
Exemplo n.º 15
0
class Volcano:

    # Class for l-volcano of elliptic curve E over finite field
    # We can construct Volcano either using Velu algorithm (Velu = True) or modular polynomials (Velu = False)

    # If Velu algorithm is chosen, constructor tries to find kernels of isogenies in extension,
    # if the size of the base field of extension is higher than upper_bit_limit, then the algorithm stops

    # In case of the option of modular polynomials, we assume that l<127
    # You can turn off the 0,1728 warning with special = False
    def __init__(self, E, l, Velu=False, upper_bit_limit=100, special=True):

        self._l = l
        self._E = E
        global VELU
        VELU = Velu

        global UPPER_BITS
        UPPER_BITS = upper_bit_limit

        self.field = E.base_field()

        try:
            self._neighbours, self._vertices, self._special = BFS(
                E.j_invariant(), l)

        except large_extension_error as e:
            print(
                "Upper limit for bitlength of size of extension field exceeded"
            )
            print(e.msg)
            return

        if self._special and special:
            print("Curve with j_invariant 0 or 1728 found, may malfunction.")
        self._depths = {}
        self._levels = []
        self._depth = 0
        self._graph = Graph(multiedges=True, loops=True)
        for s in self._neighbours.values():
            self._graph.add_vertex(s[0])

        for s in self._neighbours.values():
            for i in range(1, len(s)):
                if not self._graph.has_edge(s[0], s[i][0]):
                    for j in range(s[i][1]):
                        self._graph.add_edge((s[0], s[i][0], j))
        if len(self._vertices) > 1 and E.is_ordinary():
            self.compute_depths()
        else:
            self._depths = {str(E.j_invariant()): 0}
            self._levels = [self._vertices]

    # Method for computing depths and sorting vertices into levels
    def compute_depths(self):
        heights = {}
        level = []

        if len(list(self._neighbours.values())[0]) == 3:
            self._levels = [self._vertices]
            self._depths = {str(i): 0 for i in self._vertices}
            self._depth = 0
            return

        for s in self._neighbours.keys():
            if len(self._neighbours[s]) == 2:
                heights[s] = 0
                level.append(self._neighbours[s][0])

        self._levels.append(level)
        h = 1
        while len(heights.keys()) != len(self._vertices):

            level = []
            for s in self._neighbours.keys():

                if s in heights.keys():
                    continue
                if len(self._neighbours[s]) > 2:
                    if str(self._neighbours[s][1][0]) in heights.keys(
                    ) and heights[str(self._neighbours[s][1][0])] == h - 1:
                        heights[s] = h
                        level.append(self._neighbours[s][0])

                        continue

                    if str(self._neighbours[s][2][0]) in heights.keys(
                    ) and heights[str(self._neighbours[s][2][0])] == h - 1:
                        heights[s] = h
                        level.append(self._neighbours[s][0])

                        continue

                if len(self._neighbours[s]) > 3:
                    if str(self._neighbours[s][3][0]) in heights.keys(
                    ) and heights[str(self._neighbours[s][3][0])] == h - 1:
                        heights[s] = h
                        level.append(self._neighbours[s][0])

                        continue
            h += 1
            self._levels.append(level)

        self._depth = h - 1
        self._depths = {}
        for k in heights.keys():
            self._depths[k] = h - 1 - heights[k]
        self._levels.reverse()

    # Returns the defining degree of volcano
    def degree(self):
        return self._l

    # Returns the level at depth i
    def level(self, i):
        return self._levels[i]

    # Returns list of all edges
    def edges(self):
        return self._graph.edges()

    # Returns depth of volcano
    def depth(self):
        return self._depth

    # Returns the crater of volcano
    def crater(self):
        return self._levels[0]

    # Plots the volcano
    # Optional arguments figsize, vertex_size, vertex_labels and layout which are the same as in igraph (sage Class)
    def plot(self,
             figsize=None,
             vertex_labels=True,
             vertex_size=None,
             layout=None):
        try:
            self._graph.layout(layout=layout, save_pos=True)
        except:
            pass
        if vertex_size != None:
            return self._graph.plot(figsize=figsize,
                                    vertex_labels=vertex_labels,
                                    vertex_size=vertex_size)
        else:
            return self._graph.plot(figsize=figsize,
                                    vertex_labels=vertex_labels)

    # Returns all vertices of volcano
    def vertices(self):
        return self._vertices

    # Returns all neighbours of vertex j
    def neighbors(self, j):
        return self._neighbours[str(j)][1:]

    # Returns depth of vertex j
    def vertex_depth(self, j):
        return self._depths[str(j)]

    # Returns true if the volcano contains vertex 1728 or 0
    def special(self):
        return self._special

    # Returns parent (upper level neighbour) of j
    def volcano_parent(self, j):
        h = self._depths[str(j)]
        for i in self._neighbours[str(j)][1:]:
            if self._depths[str(i[0])] < h:
                return i[0]
        return None

    # Returns all children (lower level neighbours) of vertex j
    def volcano_children(self, j):
        children = []
        h = self._depths[str(j)]
        for i in self._neighbours[str(j)][1:]:
            if self._depths[str(i[0])] > h:
                children.append(i[0])
        return children

    # Finds an extension of curve over which the volcano has depth h
    def expand_volcano(self, h):
        return Volcano(expand_volcano(self._E, h, self._l), self._l)

    def __repr__(self):
        return "Isogeny %r-volcano of depth %r over %r" % (
            self._l, self.depth(), self.field)
Exemplo n.º 16
0
def Matroid(*args, **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`` -- If provided, the groundset of the matroid.
      If not provided, the function attempts to determine a groundset from the
      data.
    - ``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.
    - ``matroid`` -- An object that is already a matroid. Useful only with the
      ``regular`` option.

    Up to two unnamed arguments are allowed.

    - One unnamed argument, no named arguments other than ``regular`` -- the
      input should be either a graph, or a matrix, or a list of independent
      sets containing all bases, or a matroid.
    - Two unnamed arguments: the first is the groundset, the second a graph,
      or a matrix, or a list of independent sets containing all bases, or a
      matroid.
    - One unnamed argument, at least one named argument: the unnamed argument
      is the groundset, the named argument is as above (but must be different
      from ``groundset``).

    The examples section details how each of the input types deals with
    explicit or implicit groundset arguments.

    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)
            <type '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)
            Regular matroid of rank 9 on 15 elements with 2000 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)])
            sage: M = Matroid('abcd', G)
            sage: M.rank(['b', 'c'])
            1

        If no groundset is provided, we attempt to use the edge 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 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)
            sage: sorted(M.groundset())
            [(0, 1), (0, 2), (1, 2)]

            sage: G = Graph([(0, 1), (0, 2), (0, 2), (1, 2)])
            sage: M = Matroid(G)
            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~')
            Regular matroid of rank 9 on 17 elements with 4004 bases

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

            sage: Matroid(graph=41/2)
            Traceback (most recent call last):
            ...
            ValueError: input does not seem to represent 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

    #. 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 does not correspond to 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

    """
    # These are the valid arguments:
    inputS = set(['groundset', 'bases', 'independent_sets', 'circuits', 'graph', 'matrix', 'reduced_matrix', 'rank_function', 'circuit_closures', 'matroid'])

    # process options
    if 'regular' in kwds:
        want_regular = kwds['regular']
        kwds.pop('regular')
    else:
        want_regular = False
    if 'check' in kwds:
        want_check = kwds['check']
        kwds.pop('check')
    else:
        want_check = True

    base_ring = None
    have_field = False
    if 'field' in kwds:
        base_ring = kwds['field']
        kwds.pop('field')
        have_field = True
        if 'ring' in kwds:
            raise ValueError("only one of ring and field can be specified.")
        try:
            if not base_ring.is_field():
                raise TypeError("specified ``field`` is not a field.")
        except AttributeError:
            raise TypeError("specified ``field`` is not a field.")
    if 'ring' in kwds:
        base_ring = kwds['ring']
        kwds.pop('ring')
        try:
            if not base_ring.is_ring():
                raise TypeError("specified ``ring`` is not a ring.")
        except AttributeError:
            raise TypeError("specified ``ring`` is not a ring.")

    # Process unnamed arguments
    if len(args) > 0:
        if 'groundset' in kwds:
            raise ValueError('when using unnamed arguments, groundset must be the first unnamed argument or be implicit.')
        if len(args) > 2:
            raise ValueError('at most two unnamed arguments are allowed.')
        if len(args) == 2 or len(kwds) > 0:
            # First argument should be the groundset
            kwds['groundset'] = args[0]
        # Check for unnamed data
        dataindex = -1
        if len(args) == 2:
            dataindex = 1
        if len(args) == 1 and len(kwds) == 0:
            dataindex = 0
        if dataindex > -1:
            # One unnamed argument, no named arguments
            if isinstance(args[dataindex], sage.graphs.graph.Graph):
                kwds['graph'] = args[dataindex]
            elif isinstance(args[dataindex], sage.matrix.matrix.Matrix):
                kwds['matrix'] = args[dataindex]
            elif isinstance(args[dataindex], sage.matroids.matroid.Matroid):
                kwds['matroid'] = args[dataindex]
            else:
                kwds['independent_sets'] = args[dataindex]

    # Check for multiple types of input
    if len(set(kwds).difference(inputS)) > 0:
        raise ValueError("unknown input argument")
    if ('grondset' in kwds and len(kwds) != 2) or ('groundset' not in kwds and len(kwds) > 1):
        raise ValueError("only one type of input may be specified.")

    # Bases:
    if 'bases' in kwds:
        if 'groundset' not in kwds:
            gs = set()
            for B in kwds['bases']:
                gs.update(B)
            kwds['groundset'] = gs
        M = BasisMatroid(groundset=kwds['groundset'], bases=kwds['bases'])

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

    # Circuits:
    if 'circuits' in kwds:
        # Convert to list of bases first
        # Determine groundset (note that this cannot detect coloops)
        if 'groundset' not in kwds:
            gs = set()
            for C in kwds['circuits']:
                gs.update(C)
            kwds['groundset'] = gs
        # determine the rank by computing a basis
        B = set(kwds['groundset'])
        for C in kwds['circuits']:
            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(kwds['groundset'], rk) if not any([frozenset(C).issubset(B) for C in kwds['circuits']])]
        M = BasisMatroid(groundset=kwds['groundset'], bases=BB)

    # Graphs:
    if 'graph' in kwds:
        # 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!
        G = kwds['graph']
        if not isinstance(G, sage.graphs.generic_graph.GenericGraph):
            try:
                G = Graph(G)
            except (ValueError, TypeError, NetworkXError):
                raise ValueError("input does not seem to represent a graph.")
        V = G.vertices()
        E = G.edges()
        n = G.num_verts()
        m = G.num_edges()
        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
        # Decide on the groundset
        if 'groundset' not in kwds:
            # 1. Attempt to use edge labels.
            sl = G.edge_labels()
            if len(sl) == len(set(sl)):
                kwds['groundset'] = sl
                # 2. If simple, use vertex tuples
            elif not G.has_multiple_edges():
                kwds['groundset'] = [(i, j) for i, j, k in G.edge_iterator()]
            else:
                # 3. Use numbers
                kwds['groundset'] = range(m)
        M = RegularMatroid(matrix=A, groundset=kwds['groundset'])
        want_regular = False  # Save some time, since result is already regular

    # Matrices:
    if 'matrix' in kwds or 'reduced_matrix' in kwds:
        if 'matrix' in kwds:
            A = kwds['matrix']
        if 'reduced_matrix' in kwds:
            A = kwds['reduced_matrix']

        # Fix the representation
        if not isinstance(A, sage.matrix.matrix.Matrix):
            try:
                if base_ring is not None:
                    A = Matrix(base_ring, A)
                else:
                    A = Matrix(A)
            except ValueError:
                raise ValueError("input does not seem to contain a matrix.")

        # Fix the ring
        if base_ring is not None:
            if A.base_ring() != base_ring:
                A = A.change_ring(base_ring)
        elif A.base_ring() == 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()

        # Determine groundset:
        if 'matrix' in kwds:
            if 'groundset' in kwds:
                if len(kwds['groundset']) == A.nrows() + A.ncols():
                    kwds['reduced_matrix'] = A
                    kwds.pop('matrix')
                else:
                    if len(kwds['groundset']) != A.ncols():
                        raise ValueError("groundset size does not correspond to matrix size.")
            else:
                kwds['groundset'] = range(A.ncols())
        if 'reduced_matrix' in kwds:
            if 'groundset' in kwds:
                if len(kwds['groundset']) != A.nrows() + A.ncols():
                    raise ValueError("groundset size does not correspond to matrix size.")
            else:
                kwds['groundset'] = range(A.nrows() + A.ncols())
        if 'matrix' in kwds:
            if base_ring == GF(2):
                M = BinaryMatroid(groundset=kwds['groundset'], matrix=A)
            elif base_ring == GF(3):
                M = TernaryMatroid(groundset=kwds['groundset'], matrix=A)
            elif base_ring.is_field() and base_ring.order() == 4:  # GF(4) can have different generators.
                M = QuaternaryMatroid(groundset=kwds['groundset'], matrix=A)
            else:
                M = LinearMatroid(groundset=kwds['groundset'], matrix=A, ring=base_ring)

        if 'reduced_matrix' in kwds:
            if A.base_ring() == GF(2):
                M = BinaryMatroid(groundset=kwds['groundset'], reduced_matrix=A)
            elif A.base_ring() == GF(3):
                M = TernaryMatroid(groundset=kwds['groundset'], reduced_matrix=A)
            elif A.base_ring().is_field() and A.base_ring().order() == 4:  # GF(4) can have different generators.
                M = QuaternaryMatroid(groundset=kwds['groundset'], reduced_matrix=A)
            else:
                M = LinearMatroid(groundset=kwds['groundset'], reduced_matrix=A, ring=base_ring)

    # Rank functions:
    if 'rank_function' in kwds:
        if 'groundset' not in kwds:
            raise ValueError('for rank functions, groundset needs to be specified.')
        M = RankMatroid(groundset=kwds['groundset'], rank_function=kwds['rank_function'])

    # Circuit closures:
    if 'circuit_closures' in kwds:
        if 'groundset' not in kwds:
            E = set()
            if isinstance(kwds['circuit_closures'], dict):
                for X in kwds['circuit_closures'].itervalues():
                    for Y in X:
                        E.update(Y)
            else:
                for X in kwds['circuit_closures']:
                    E.update(X[1])
        else:
            E = kwds['groundset']
        if not isinstance(kwds['circuit_closures'], dict):
            # Convert to dictionary
            CC = {}
            for X in kwds['circuit_closures']:
                if X[0] not in CC:
                    CC[X[0]] = []
                CC[X[0]].append(X[1])
        else:
            CC = kwds['circuit_closures']
        M = CircuitClosuresMatroid(groundset=E, circuit_closures=CC)

    # Matroids:
    if 'matroid' in kwds:
        M = kwds['matroid']
        if not isinstance(M, sage.matroids.matroid.Matroid):
            raise ValueError("input does not appear to be of Matroid type.")
    # Regular option:
    if want_regular:
        M = sage.matroids.utilities.make_regular_matroid_from_matroid(M)
        if want_check:
            if not M.is_valid():
                    raise ValueError('input does not correspond to a valid regular matroid.')
    return M