def Circuit(self, n):
        r"""
        Returns the circuit on `n` vertices

        The circuit is an oriented ``CycleGraph``

        EXAMPLE:

        A circuit is the smallest strongly connected digraph::

            sage: circuit = digraphs.Circuit(15)
            sage: len(circuit.strongly_connected_components()) == 1
            True
        """
        g = DiGraph(n)
        g.name("Circuit")

        if n == 0:
            return g
        elif n == 1:
            g.allow_loops(True)
            g.add_edge(0, 0)
            return g
        else:
            g.add_edges([(i, i + 1) for i in xrange(n - 1)])
            g.add_edge(n - 1, 0)
            return g
示例#2
0
    def basis_of_simple_closed_curves(self):
        from sage.graphs.digraph import DiGraph
        from sage.all import randint

        n = self._origami.nb_squares()
        C = self.chain_space()
        G = DiGraph(multiedges=True, loops=True, implementation='c_graph')

        for i in xrange(2 * n):
            G.add_edge(self._starts[i], self._ends[i], i)

        waiting = [0]
        gens = []
        reps = [None] * G.num_verts()
        reps[0] = C.zero()

        while waiting:
            x = waiting.pop(randint(0, len(waiting) - 1))
            for v0, v1, e in G.outgoing_edges(x):
                if reps[v1] is not None:
                    gens.append(reps[v0] + C.gen(e) - reps[v1])
                else:
                    reps[v1] = reps[v0] + C.gen(e)
                    waiting.append(v1)

        return gens
示例#3
0
    def Tournament(self,n):
        r"""
        Returns a tournament on `n` vertices.

        In this tournament there is an edge from `i` to `j` if `i<j`.

        INPUT:

        - ``n`` (integer) -- number of vertices in the tournament.

        EXAMPLES::

            sage: g = digraphs.Tournament(5)
            sage: g.vertices()
            [0, 1, 2, 3, 4]
            sage: g.size()
            10
            sage: g.automorphism_group().cardinality()
            1
        """
        if n<0:
            raise ValueError("The number of vertices must be a positive integer.")

        g = DiGraph()
        g.name("Tournament on "+str(n)+" vertices")

        for i in range(n-1):
            for j in range(i+1, n):
                g.add_edge(i,j)

        if n:
            from sage.graphs.graph_plot import _circle_embedding
            _circle_embedding(g, range(n))

        return g
示例#4
0
    def RandomDirectedGNP(self, n, p):
        r"""
        Returns a random digraph on `n` nodes. Each edge is
        inserted independently with probability `p`.
        
        REFERENCES:

        - [1] P. Erdos and A. Renyi, On Random Graphs, Publ.  Math. 6,
          290 (1959).

        - [2] E. N. Gilbert, Random Graphs, Ann. Math.  Stat., 30,
          1141 (1959).
        
        PLOTTING: When plotting, this graph will use the default
        spring-layout algorithm, unless a position dictionary is
        specified.
        
        EXAMPLE::
        
            sage: D = digraphs.RandomDirectedGNP(10, .2)
            sage: D.num_verts()
            10
            sage: D.edges(labels=False)
            [(0, 1), (0, 3), (0, 6), (0, 8), (1, 4), (3, 7), (4, 1), (4, 8), (5, 2), (5, 6), (5, 8), (6, 4), (7, 6), (8, 4), (8, 5), (8, 7), (8, 9), (9, 3), (9, 4), (9, 6)]
        """
        from sage.misc.prandom import random
        D = DiGraph(n)
        for i in xrange(n):
            for j in xrange(i):
                if random() < p:
                    D.add_edge(i,j)
            for j in xrange(i+1,n):
                if random() < p:
                    D.add_edge(i,j)
        return D
示例#5
0
    def inclusion_digraph(self):
        r"""
        Returns the class inclusion digraph

        Upon the first call, this loads the database from the local
        XML file. Subsequent calls are cached.

        EXAMPLES::

            sage: g = graph_classes.inclusion_digraph(); g
            Digraph on ... vertices
        """
        classes    = self.classes()
        inclusions = self.inclusions()

        from sage.graphs.digraph import DiGraph
        inclusion_digraph = DiGraph()
        inclusion_digraph.add_vertices(classes.keys())

        for edge in inclusions:
            if edge.get("confidence","") == "unpublished":
                continue
            inclusion_digraph.add_edge(edge['super'], edge['sub'])

        return inclusion_digraph
示例#6
0
    def RandomDirectedGNP(self, n, p):
        r"""
        Returns a random digraph on `n` nodes. Each edge is
        inserted independently with probability `p`.
        
        REFERENCES:

        - [1] P. Erdos and A. Renyi, On Random Graphs, Publ.  Math. 6,
          290 (1959).

        - [2] E. N. Gilbert, Random Graphs, Ann. Math.  Stat., 30,
          1141 (1959).
        
        PLOTTING: When plotting, this graph will use the default
        spring-layout algorithm, unless a position dictionary is
        specified.
        
        EXAMPLE::
        
            sage: D = digraphs.RandomDirectedGNP(10, .2)
            sage: D.num_verts()
            10
            sage: D.edges(labels=False)
            [(0, 1), (0, 3), (0, 6), (0, 8), (1, 4), (3, 7), (4, 1), (4, 8), (5, 2), (5, 6), (5, 8), (6, 4), (7, 6), (8, 4), (8, 5), (8, 7), (8, 9), (9, 3), (9, 4), (9, 6)]
        """
        from sage.misc.prandom import random
        D = DiGraph(n)
        for i in xrange(n):
            for j in xrange(i):
                if random() < p:
                    D.add_edge(i,j)
            for j in xrange(i+1,n):
                if random() < p:
                    D.add_edge(i,j)
        return D
示例#7
0
    def Circuit(self,n):
        r"""
        Returns the circuit on `n` vertices

        The circuit is an oriented ``CycleGraph``

        EXAMPLE:

        A circuit is the smallest strongly connected digraph::

            sage: circuit = digraphs.Circuit(15)
            sage: len(circuit.strongly_connected_components()) == 1
            True
        """
        if n<0:
            raise ValueError("The number of vertices must be a positive integer.")

        g = DiGraph()
        g.name("Circuit on "+str(n)+" vertices")

        if n==0:
            return g
        elif n == 1:
            g.allow_loops(True)
            g.add_edge(0,0)
            return g
        else:
            g.add_edges([(i,i+1) for i in xrange(n-1)])
            g.add_edge(n-1,0)
            return g        
示例#8
0
    def basis_of_simple_closed_curves(self):
        from sage.graphs.digraph import DiGraph
        from sage.all import randint

        n = self._origami.nb_squares()
        C = self.chain_space()
        G = DiGraph(multiedges=True,loops=True,implementation='c_graph')

        for i in xrange(2*n):
            G.add_edge(self._starts[i], self._ends[i], i)

        waiting = [0]
        gens = []
        reps = [None] * G.num_verts()
        reps[0] = C.zero()

        while waiting:
            x = waiting.pop(randint(0,len(waiting)-1))
            for v0,v1,e in G.outgoing_edges(x):
                if reps[v1] is not None:
                    gens.append(reps[v0] + C.gen(e) - reps[v1])
                else:
                    reps[v1] = reps[v0] + C.gen(e)
                    waiting.append(v1)

        return gens
示例#9
0
    def Circuit(self, n):
        r"""
        Returns the circuit on `n` vertices

        The circuit is an oriented ``CycleGraph``

        EXAMPLE:

        A circuit is the smallest strongly connected digraph::

            sage: circuit = digraphs.Circuit(15)
            sage: len(circuit.strongly_connected_components()) == 1
            True
        """
        if n < 0:
            raise ValueError(
                "The number of vertices must be a positive integer.")

        g = DiGraph()
        g.name("Circuit on " + str(n) + " vertices")

        if n == 0:
            return g
        elif n == 1:
            g.allow_loops(True)
            g.add_edge(0, 0)
            return g
        else:
            g.add_edges([(i, i + 1) for i in xrange(n - 1)])
            g.add_edge(n - 1, 0)
            return g
示例#10
0
文件: isgci.py 项目: manguluka/sage
    def inclusion_digraph(self):
        r"""
        Returns the class inclusion digraph

        Upon the first call, this loads the database from the local
        XML file. Subsequent calls are cached.

        EXAMPLES::

            sage: g = graph_classes.inclusion_digraph(); g
            Digraph on ... vertices
        """
        classes    = self.classes()
        inclusions = self.inclusions()

        from sage.graphs.digraph import DiGraph
        inclusion_digraph = DiGraph()
        inclusion_digraph.add_vertices(classes.keys())

        for edge in inclusions:
            if edge.get("confidence","") == "unpublished":
                continue
            inclusion_digraph.add_edge(edge['super'], edge['sub'])

        return inclusion_digraph
示例#11
0
    def Circuit(self,n):
        r"""
        Returns the circuit on `n` vertices

        The circuit is an oriented ``CycleGraph``

        EXAMPLE:

        A circuit is the smallest strongly connected digraph::

            sage: circuit = digraphs.Circuit(15)
            sage: len(circuit.strongly_connected_components()) == 1
            True
        """
        g = DiGraph(n)
        g.name("Circuit")

        if n==0:
            return g
        elif n == 1:
            g.allow_loops(True)
            g.add_edge(0,0)
            return g
        else:
            g.add_edges([(i,i+1) for i in xrange(n-1)])
            g.add_edge(n-1,0)
            return g
示例#12
0
            def digraph(self, index_set=None):
                r"""
                Return the :class:`DiGraph` associated to ``self``.

                EXAMPLES::

                    sage: B = crystals.Letters(['A', [1,3]])
                    sage: G = B.digraph(); G
                    Multi-digraph on 6 vertices
                    sage: Q = crystals.Letters(['Q',3])
                    sage: G = Q.digraph(); G
                    Multi-digraph on 3 vertices
                    sage: G.edges()
                    [(1, 2, -1), (1, 2, 1), (2, 3, -2), (2, 3, 2)]

                The edges of the crystal graph are by default colored using
                blue for edge 1, red for edge 2, green for edge 3, and dashed with
                the corresponding color for barred edges. Edge 0 is dotted black::

                    sage: view(G)  # optional - dot2tex graphviz, not tested (opens external window)
                """
                from sage.graphs.digraph import DiGraph
                from sage.misc.latex import LatexExpr
                from sage.combinat.root_system.cartan_type import CartanType

                if index_set is None:
                    index_set = self.index_set()

                G = DiGraph(multiedges=True)
                G.add_vertices(self)
                for i in index_set:
                    for x in G:
                        y = x.f(i)
                        if y is not None:
                            G.add_edge(x, y, i)

                def edge_options(data):
                    u, v, l = data
                    edge_opts = {'edge_string': '->', 'color': 'black'}
                    if l > 0:
                        edge_opts['color'] = CartanType._colors.get(l, 'black')
                        edge_opts['label'] = LatexExpr(str(l))
                    elif l < 0:
                        edge_opts[
                            'color'] = "dashed," + CartanType._colors.get(
                                -l, 'black')
                        edge_opts['label'] = LatexExpr("\\overline{%s}" %
                                                       str(-l))
                    else:
                        edge_opts[
                            'color'] = "dotted," + CartanType._colors.get(
                                l, 'black')
                        edge_opts['label'] = LatexExpr(str(l))
                    return edge_opts

                G.set_latex_options(format="dot2tex",
                                    edge_labels=True,
                                    edge_options=edge_options)
                return G
示例#13
0
def random_orientation(G):
    r"""
    Return a random orientation of a graph `G`.

    An *orientation* of an undirected graph is a directed graph such that every
    edge is assigned a direction. Hence there are `2^m` oriented digraphs for a
    simple graph with `m` edges.

    INPUT:

    - ``G`` -- a Graph.

    EXAMPLES::

        sage: from sage.graphs.orientations import random_orientation
        sage: G = graphs.PetersenGraph()
        sage: D = random_orientation(G)
        sage: D.order() == G.order(), D.size() == G.size()
        (True, True)

    TESTS:

    Giving anything else than a Graph::

        sage: random_orientation([])
        Traceback (most recent call last):
        ...
        ValueError: the input parameter must be a Graph

    .. SEEALSO::

        - :meth:`~Graph.orientations`
    """
    from sage.graphs.graph import Graph
    if not isinstance(G, Graph):
        raise ValueError("the input parameter must be a Graph")

    D = DiGraph(data=[G.vertices(), []],
                format='vertices_and_edges',
                multiedges=G.allows_multiple_edges(),
                loops=G.allows_loops(),
                weighted=G.weighted(),
                pos=G.get_pos(),
                name="Random orientation of {}".format(G.name()) )
    if hasattr(G, '_embedding'):
        D._embedding = copy(G._embedding)

    from sage.misc.prandom import getrandbits
    rbits = getrandbits(G.size())
    for u,v,l in G.edge_iterator():
        if rbits % 2:
            D.add_edge(u, v, l)
        else:
            D.add_edge(v, u, l)
        rbits >>= 1
    return D
示例#14
0
def random_orientation(G):
    r"""
    Return a random orientation of a graph `G`.

    An *orientation* of an undirected graph is a directed graph such that every
    edge is assigned a direction. Hence there are `2^m` oriented digraphs for a
    simple graph with `m` edges.

    INPUT:

    - ``G`` -- a Graph.

    EXAMPLES::

        sage: from sage.graphs.orientations import random_orientation
        sage: G = graphs.PetersenGraph()
        sage: D = random_orientation(G)
        sage: D.order() == G.order(), D.size() == G.size()
        (True, True)

    TESTS:

    Giving anything else than a Graph::

        sage: random_orientation([])
        Traceback (most recent call last):
        ...
        ValueError: the input parameter must be a Graph

    .. SEEALSO::

        - :meth:`~Graph.orientations`
    """
    from sage.graphs.graph import Graph
    if not isinstance(G, Graph):
        raise ValueError("the input parameter must be a Graph")

    D = DiGraph(data=[G.vertices(), []],
                format='vertices_and_edges',
                multiedges=G.allows_multiple_edges(),
                loops=G.allows_loops(),
                weighted=G.weighted(),
                pos=G.get_pos(),
                name="Random orientation of {}".format(G.name()) )
    if hasattr(G, '_embedding'):
        D._embedding = copy(G._embedding)

    from sage.misc.prandom import getrandbits
    rbits = getrandbits(G.size())
    for u,v,l in G.edge_iterator():
        if rbits % 2:
            D.add_edge(u, v, l)
        else:
            D.add_edge(v, u, l)
        rbits >>= 1
    return D
示例#15
0
    def min_hasse_diagram(self):
        r"""
        Return Hasse diagram of the trace.

        OUTPUT:

        Directed graph of generator indexes.

        .. SEEALSO::

            :meth:`~sage.monoids.trace_monoid.TraceMonoidElement.hasse_digram`,
            :meth:`~sage.monoids.trace_monoid.TraceMonoidElement.naive_hasse_diagram`.

        EXAMPLES::

            sage: from sage.monoids.trace_monoid import TraceMonoid
            sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b'))
            sage: M.<a,b,c,d> = TraceMonoid(I=I)
            sage: x = b * a * d * a * c * b
            sage: x.min_hasse_diagram()
            Digraph on 6 vertices
        """
        elements = self._flat_elements()
        elements.reverse()
        independence = self.parent()._independence
        reachable = dict()
        min = set()
        graph = DiGraph({})

        for i, x in enumerate(elements):
            reachable[i] = set()
            front = min.copy()
            while front:
                used = set()
                for j in list(front):
                    y = elements[j]
                    if (x, y) not in independence:
                        graph.add_edge(i, j)
                        reachable[i].add(j)
                        reachable[i].update(reachable[j])
                        if j in min:
                            min.remove(j)
                        used.add(j)
                forbidden = set(chain.from_iterable(reachable[v]
                                                    for v in used))
                front = set(
                    dest
                    for _, dest in graph.outgoing_edges(front, labels=False))
                front = front - forbidden

            min.add(i)

        length = len(elements)
        graph.relabel(length - 1 - i for i in range(length))
        return graph
    def as_graph(self):
        r"""
        Return the graph associated to self
        """
        from sage.graphs.digraph import DiGraph

        G = DiGraph(multiedges=True, loops=True)
        d = self.degree()
        g = [self.g_tuple(i) for i in range(4)]
        for i in range(d):
            for j in range(4):
                G.add_edge(i, g[j][i], j)
        return G
    def as_graph(self):
        r"""
        Return the graph associated to self
        """
        from sage.graphs.digraph import DiGraph

        G = DiGraph(multiedges=True,loops=True)
        d = self.degree()
        g = [self.g_tuple(i) for i in xrange(4)]
        for i in xrange(d):
            for j in xrange(4):
                G.add_edge(i,g[j][i],j)
        return G
示例#18
0
文件: suffix_trees.py 项目: yarv/sage
    def uncompactify(self):
        r"""
        Returns the tree obtained from self by splitting edges so that they
        are labelled by exactly one letter. The resulting tree is
        isomorphic to the suffix trie.

        EXAMPLES::

            sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree, SuffixTrie
            sage: abbab = Words("ab")("abbab")
            sage: s = SuffixTrie(abbab)
            sage: t = ImplicitSuffixTree(abbab)
            sage: t.uncompactify().is_isomorphic(s.to_digraph())
            True
        """
        tree = self.to_digraph(word_labels=True)
        newtree = DiGraph()
        newtree.add_vertices(range(tree.order()))
        new_node = tree.order() + 1
        for (u, v, label) in tree.edge_iterator():
            if len(label) == 1:
                newtree.add_edge(u, v)
            else:
                newtree.add_edge(u, new_node, label[0])
                for w in label[1:-1]:
                    newtree.add_edge(new_node, new_node + 1, w)
                    new_node += 1
                newtree.add_edge(new_node, v, label[-1])
                new_node += 1
        return newtree
示例#19
0
        def digraph(self):
            r"""
            Return the :class:`DiGraph` associated to ``self``.

            EXAMPLES::

                sage: B = crystals.Letters(['A', [1,3]])
                sage: G = B.digraph(); G
                Multi-digraph on 6 vertices
                sage: Q = crystals.Letters(['Q',3])
                sage: G = Q.digraph(); G
                Multi-digraph on 3 vertices
                sage: G.edges()
                [(1, 2, -1), (1, 2, 1), (2, 3, -2), (2, 3, 2)]

            The edges of the crystal graph are by default colored using
            blue for edge 1, red for edge 2, green for edge 3, and dashed with
            the corresponding color for barred edges. Edge 0 is dotted black::

                sage: view(G)  # optional - dot2tex graphviz, not tested (opens external window)
            """
            from sage.graphs.digraph import DiGraph
            from sage.misc.latex import LatexExpr
            from sage.combinat.root_system.cartan_type import CartanType

            G = DiGraph(multiedges=True)
            G.add_vertices(self)
            for i in self.index_set():
                for x in G:
                    y = x.f(i)
                    if y is not None:
                        G.add_edge(x, y, i)

            def edge_options(data):
                u, v, l = data
                edge_opts = { 'edge_string': '->', 'color': 'black' }
                if l > 0:
                    edge_opts['color'] = CartanType._colors.get(l, 'black')
                    edge_opts['label'] = LatexExpr(str(l))
                elif l < 0:
                    edge_opts['color'] = "dashed," + CartanType._colors.get(-l, 'black')
                    edge_opts['label'] = LatexExpr("\\overline{%s}" % str(-l))
                else:
                    edge_opts['color'] = "dotted," + CartanType._colors.get(l, 'black')
                    edge_opts['label'] = LatexExpr(str(l))
                return edge_opts

            G.set_latex_options(format="dot2tex", edge_labels=True, edge_options=edge_options)

            return G
示例#20
0
    def uncompactify(self):
        r"""
        Returns the tree obtained from self by splitting edges so that they
        are labelled by exactly one letter. The resulting tree is
        isomorphic to the suffix trie.

        EXAMPLES::

            sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree, SuffixTrie
            sage: abbab = Words("ab")("abbab")
            sage: s = SuffixTrie(abbab)
            sage: t = ImplicitSuffixTree(abbab)
            sage: t.uncompactify().is_isomorphic(s.to_digraph())
            True
        """
        tree = self.to_digraph(word_labels=True)
        newtree = DiGraph()
        newtree.add_vertices(range(tree.order()))
        new_node = tree.order() + 1
        for (u,v,label) in tree.edge_iterator():
            if len(label) == 1:
                newtree.add_edge(u,v)
            else:
                newtree.add_edge(u,new_node,label[0]);
                for w in label[1:-1]:
                    newtree.add_edge(new_node,new_node+1,w)
                    new_node += 1
                newtree.add_edge(new_node,v,label[-1])
                new_node += 1
        return newtree
示例#21
0
    def add_edge(self, i, j, label=1):
        """
        EXAMPLES::

            sage: from sage.combinat.root_system.dynkin_diagram import DynkinDiagram_class
            sage: d = DynkinDiagram_class(CartanType(['A',3]))
            sage: list(sorted(d.edges()))
            []
            sage: d.add_edge(2, 3)
            sage: list(sorted(d.edges()))
            [(2, 3, 1), (3, 2, 1)]
        """
        DiGraph.add_edge(self, i, j, label)
        if not self.has_edge(j,i):
            self.add_edge(j,i,1)
    def add_edge(self, i, j, label=1):
        """
        EXAMPLES::

            sage: from sage.combinat.root_system.dynkin_diagram import DynkinDiagram_class
            sage: d = DynkinDiagram_class(CartanType(['A',3]))
            sage: list(sorted(d.edges()))
            []
            sage: d.add_edge(2, 3)
            sage: list(sorted(d.edges()))
            [(2, 3, 1), (3, 2, 1)]
        """
        DiGraph.add_edge(self, i, j, label)
        if not self.has_edge(j, i):
            self.add_edge(j, i, 1)
    def plot(self, **kwargs):
        """
        Return a graphics object representing the Kontsevich graph.

        INPUT:

        - ``edge_labels`` (boolean, default True) -- if True, show edge labels.
        - ``indices`` (boolean, default False) -- if True, show indices as
          edge labels instead of L and R; see :meth:`._latex_`.
        - ``upright`` (boolean, default False) -- if True, try to plot the
          graph with the ground vertices on the bottom and the rest on top.
        """
        if not 'edge_labels' in kwargs:
            kwargs['edge_labels'] = True  # show edge labels by default
        if 'indices' in kwargs:
            del kwargs['indices']
            KG = DiGraph(self)
            for (k, e) in enumerate(self.edges()):
                KG.delete_edge(e)
                KG.add_edge((e[0], e[1], chr(97 + k)))
            return KG.plot(**kwargs)
        if len(self.ground_vertices()) == 2 and 'upright' in kwargs:
            del kwargs['upright']
            kwargs['save_pos'] = True
            DiGraph.plot(self, **kwargs)
            positions = self.get_pos()
            # translate F to origin:
            F_pos = vector(positions[self.ground_vertices()[0]])
            for p in positions:
                positions[p] = list(vector(positions[p]) - F_pos)
            # scale F - G distance to 1:
            G_len = abs(vector(positions[self.ground_vertices()[1]]))
            for p in positions:
                positions[p] = list(vector(positions[p]) / G_len)
            # rotate the vector F - G to (1,0)
            from math import atan2
            theta = -atan2(positions[self.ground_vertices()[1]][1],
                           positions[self.ground_vertices()[1]][0])
            for p in positions:
                positions[p] = list(
                    matrix([[cos(theta), -(sin(theta))],
                            [sin(theta), cos(theta)]]) * vector(positions[p]))
            # flip if most things are below the x-axis:
            if len([(x, y) for (x, y) in positions.values() if y < 0]) / len(
                    self.internal_vertices()) > 0.5:
                for p in positions:
                    positions[p] = [positions[p][0], -positions[p][1]]
        return DiGraph.plot(self, **kwargs)
 def plot(self, **kwds):
     """
     EXAMPLES::
 
         sage: X = gauge_theories.threeSU(3)
         sage: print X.plot().description()
     """
     g = DiGraph(loops=True, sparse=True, multiedges=True)
     for G in self._gauge_groups:
         g.add_vertex(G)
     for field in self._fields:
         if isinstance(field, FieldBifundamental):
             g.add_edge(field.src, field.dst, field)
         if isinstance(field, FieldAdjoint):
             g.add_edge(field.node, field.node, field)
     return g.plot(vertex_labels=True, edge_labels=True, graph_border=True)
 def quiver_v2(self):
     #if hasattr(self, "_quiver_cache"):
     #    return self._quiver_cache
     from sage.combinat.subset import Subsets
     from sage.graphs.digraph import DiGraph
     Q = DiGraph(multiedges=True)
     Q.add_vertices(self.j_transversal())
     g = self.associated_graph()
     for U in Subsets(g.vertices()):
         for W in Subsets(U):
             h = g.subgraph(U.difference(W))
             n = h.connected_components_number() - 1
             if n > 0:
                 u = self.j_class_representative(self.j_class_index(self(''.join(U))))
                 w = self.j_class_representative(self.j_class_index(self(''.join(W))))
                 for i in range(n):
                     Q.add_edge(w, u, i)
     return Q
示例#26
0
 def quiver_v2(self):
     #if hasattr(self, "_quiver_cache"):
     #    return self._quiver_cache
     from sage.combinat.subset import Subsets
     from sage.graphs.digraph import DiGraph
     Q = DiGraph(multiedges=True)
     Q.add_vertices(self.j_transversal())
     g = self.associated_graph()
     for U in Subsets(g.vertices()):
         for W in Subsets(U):
             h = g.subgraph(U.difference(W))
             n = h.connected_components_number() - 1
             if n > 0:
                 u = self.j_class_representative(self.j_class_index(self(''.join(U))))
                 w = self.j_class_representative(self.j_class_index(self(''.join(W))))
                 for i in range(n):
                     Q.add_edge(w, u, i)
     return Q
    def plot(self, **kwargs):
        """
        Return a graphics object representing the Kontsevich graph.

        INPUT:

        - ``edge_labels`` (boolean, default True) -- if True, show edge labels.
        - ``indices`` (boolean, default False) -- if True, show indices as
          edge labels instead of L and R; see :meth:`._latex_`.
        - ``upright`` (boolean, default False) -- if True, try to plot the
          graph with the ground vertices on the bottom and the rest on top.
        """
        if not 'edge_labels' in kwargs:
            kwargs['edge_labels'] = True        # show edge labels by default
        if 'indices' in kwargs:
            del kwargs['indices']
            KG = DiGraph(self)
            for (k,e) in enumerate(self.edges()):
                KG.delete_edge(e)
                KG.add_edge((e[0], e[1], chr(97 + k)))
            return KG.plot(**kwargs)
        if len(self.ground_vertices()) == 2 and 'upright' in kwargs:
            del kwargs['upright']
            kwargs['save_pos'] = True
            DiGraph.plot(self, **kwargs)
            positions = self.get_pos()
            # translate F to origin:
            F_pos = vector(positions[self.ground_vertices()[0]])
            for p in positions:
                positions[p] = list(vector(positions[p]) - F_pos)
            # scale F - G distance to 1:
            G_len = abs(vector(positions[self.ground_vertices()[1]]))
            for p in positions:
                positions[p] = list(vector(positions[p])/G_len)
            # rotate the vector F - G to (1,0)
            from math import atan2
            theta = -atan2(positions[self.ground_vertices()[1]][1], positions[self.ground_vertices()[1]][0])
            for p in positions:
                positions[p] = list(matrix([[cos(theta),-(sin(theta))],[sin(theta),cos(theta)]]) * vector(positions[p]))
            # flip if most things are below the x-axis:
            if len([(x,y) for (x,y) in positions.values() if y < 0])/len(self.internal_vertices()) > 0.5:
                for p in positions:
                    positions[p] = [positions[p][0], -positions[p][1]]
        return DiGraph.plot(self, **kwargs)
示例#28
0
    def RandomTournament(self, n):
        r"""
        Returns a random tournament on `n` vertices.

        For every pair of vertices, the tournament has an edge from
        `i` to `j` with probability `1/2`, otherwise it has an edge
        from `j` to `i`.

        See :wikipedia:`Tournament_(graph_theory)`

        INPUT:

        - ``n`` (integer) -- number of vertices.

        EXAMPLES::

            sage: T = digraphs.RandomTournament(10); T
            Random Tournament: Digraph on 10 vertices
            sage: T.size() == binomial(10, 2)
            True
            sage: digraphs.RandomTournament(-1)
            Traceback (most recent call last):
            ...
            ValueError: The number of vertices cannot be strictly negative!
        """
        from sage.misc.prandom import random

        g = DiGraph(n)
        g.name("Random Tournament")

        for i in range(n - 1):
            for j in range(i + 1, n):
                if random() <= 0.5:
                    g.add_edge(i, j)
                else:
                    g.add_edge(j, i)

        if n:
            from sage.graphs.graph_plot import _circle_embedding

            _circle_embedding(g, range(n))

        return g
示例#29
0
    def TransitiveTournament(self, n):
        r"""
        Returns a transitive tournament on `n` vertices.

        In this tournament there is an edge from `i` to `j` if `i<j`.

        See :wikipedia:`Tournament_(graph_theory)`

        INPUT:

        - ``n`` (integer) -- number of vertices in the tournament.

        EXAMPLES::

            sage: g = digraphs.TransitiveTournament(5)
            sage: g.vertices()
            [0, 1, 2, 3, 4]
            sage: g.size()
            10
            sage: g.automorphism_group().cardinality()
            1

        TESTS::

            sage: digraphs.TransitiveTournament(-1)
            Traceback (most recent call last):
            ...
            ValueError: The number of vertices cannot be strictly negative!
        """
        g = DiGraph(n)
        g.name("Transitive Tournament")

        for i in range(n - 1):
            for j in range(i + 1, n):
                g.add_edge(i, j)

        if n:
            from sage.graphs.graph_plot import _circle_embedding

            _circle_embedding(g, range(n))

        return g
示例#30
0
    def RandomTournament(self, n):
        r"""
        Returns a random tournament on `n` vertices.

        For every pair of vertices, the tournament has an edge from
        `i` to `j` with probability `1/2`, otherwise it has an edge
        from `j` to `i`.

        See :wikipedia:`Tournament_(graph_theory)`

        INPUT:

        - ``n`` (integer) -- number of vertices.

        EXAMPLES::

            sage: T = digraphs.RandomTournament(10); T
            Random Tournament: Digraph on 10 vertices
            sage: T.size() == binomial(10, 2)
            True
            sage: digraphs.RandomTournament(-1)
            Traceback (most recent call last):
            ...
            ValueError: The number of vertices cannot be strictly negative!
        """
        from sage.misc.prandom import random
        g = DiGraph(n)
        g.name("Random Tournament")

        for i in range(n - 1):
            for j in range(i + 1, n):
                if random() <= .5:
                    g.add_edge(i, j)
                else:
                    g.add_edge(j, i)

        if n:
            from sage.graphs.graph_plot import _circle_embedding
            _circle_embedding(g, range(n))

        return g
示例#31
0
    def TransitiveTournament(self, n):
        r"""
        Returns a transitive tournament on `n` vertices.

        In this tournament there is an edge from `i` to `j` if `i<j`.

        See :wikipedia:`Tournament_(graph_theory)`

        INPUT:

        - ``n`` (integer) -- number of vertices in the tournament.

        EXAMPLES::

            sage: g = digraphs.TransitiveTournament(5)
            sage: g.vertices()
            [0, 1, 2, 3, 4]
            sage: g.size()
            10
            sage: g.automorphism_group().cardinality()
            1

        TESTS::

            sage: digraphs.TransitiveTournament(-1)
            Traceback (most recent call last):
            ...
            ValueError: The number of vertices cannot be strictly negative!
        """
        g = DiGraph(n)
        g.name("Transitive Tournament")

        for i in range(n - 1):
            for j in range(i + 1, n):
                g.add_edge(i, j)

        if n:
            from sage.graphs.graph_plot import _circle_embedding
            _circle_embedding(g, range(n))

        return g
示例#32
0
    def to_dag(self):
        """
        Returns a directed acyclic graph corresponding to the skew
        partition.

        EXAMPLES::

            sage: dag = SkewPartition([[3, 2, 1], [1, 1]]).to_dag()
            sage: dag.edges()
            [('0,1', '0,2', None), ('0,1', '1,1', None)]
            sage: dag.vertices()
            ['0,1', '0,2', '1,1', '2,0']
        """
        i = 0

        #Make the skew tableau from the shape
        skew = [[1]*row_length for row_length in self.outer()]
        inner = self.inner()
        for i in range(len(inner)):
            for j in range(inner[i]):
                skew[i][j] = None

        G = DiGraph()
        for row in range(len(skew)):
            for column in range(len(skew[row])):
                if skew[row][column] is not None:
                    string = "%d,%d" % (row, column)
                    G.add_vertex(string)
                    #Check to see if there is a node to the right
                    if column != len(skew[row]) - 1:
                        newstring = "%d,%d" % (row, column+1)
                        G.add_edge(string, newstring)

                    #Check to see if there is anything below
                    if row != len(skew) - 1:
                        if len(skew[row+1]) > column:
                            if skew[row+1][column] is not None:
                                newstring = "%d,%d" % (row+1, column)
                                G.add_edge(string, newstring)
        return G
示例#33
0
    def to_dag(self):
        """
        Returns a directed acyclic graph corresponding to the skew
        partition.

        EXAMPLES::

            sage: dag = SkewPartition([[3, 2, 1], [1, 1]]).to_dag()
            sage: dag.edges()
            [('0,1', '0,2', None), ('0,1', '1,1', None)]
            sage: dag.vertices()
            ['0,1', '0,2', '1,1', '2,0']
        """
        i = 0

        #Make the skew tableau from the shape
        skew = [[1]*row_length for row_length in self.outer()]
        inner = self.inner()
        for i in range(len(inner)):
            for j in range(inner[i]):
                skew[i][j] = None

        G = DiGraph()
        for row in range(len(skew)):
            for column in range(len(skew[row])):
                if skew[row][column] is not None:
                    string = "%d,%d" % (row, column)
                    G.add_vertex(string)
                    #Check to see if there is a node to the right
                    if column != len(skew[row]) - 1:
                        newstring = "%d,%d" % (row, column+1)
                        G.add_edge(string, newstring)

                    #Check to see if there is anything below
                    if row != len(skew) - 1:
                        if len(skew[row+1]) > column:
                            if skew[row+1][column] is not None:
                                newstring = "%d,%d" % (row+1, column)
                                G.add_edge(string, newstring)
        return G
示例#34
0
    def _digraph(self):
        r"""
        Constructs the underlying digraph and stores the result as an
        attribute.

        EXAMPLES::
        
            sage: from sage.combinat.yang_baxter_graph import SwapIncreasingOperator
            sage: ops = [SwapIncreasingOperator(i) for i in range(2)]
            sage: Y = YangBaxterGraph(root=(1,2,3), operators=ops)
            sage: Y._digraph
            Digraph on 6 vertices
        """
        digraph = DiGraph()
        digraph.add_vertex(self._root)
        queue = [self._root]
        while queue:
            u = queue.pop()
            for (v, l) in self._succesors(u):
                if v not in digraph:
                    queue.append(v)
                digraph.add_edge(u, v, l)
        return digraph
示例#35
0
    def _digraph(self):
        r"""
        Constructs the underlying digraph and stores the result as an
        attribute.

        EXAMPLES::

            sage: from sage.combinat.yang_baxter_graph import SwapIncreasingOperator
            sage: ops = [SwapIncreasingOperator(i) for i in range(2)]
            sage: Y = YangBaxterGraph(root=(1,2,3), operators=ops)
            sage: Y._digraph
            Digraph on 6 vertices
        """
        digraph = DiGraph()
        digraph.add_vertex(self._root)
        queue = [self._root]
        while queue:
            u = queue.pop()
            for (v, l) in self._successors(u):
                if v not in digraph:
                    queue.append(v)
                digraph.add_edge(u, v, l)
        return digraph
    def induced_orientation(self, w):
        r"""
        The induced subgraph of the complement of the underlying graph with an
        orientation determined by `w`: an edge `(x,y)` is directed from `x` to
        `y` if `x` comes before `y` in `w`.

        EXAMPLES::

            sage: from sage_semigroups.monoids.free_partially_commutative_left_regular_band import FreePartiallyCommutativeLeftRegularBand
            sage: G = Graph({'a':['b'],'b':['d'],'c':[],'d':[]})
            sage: S = FreePartiallyCommutativeLeftRegularBand(G); S
            Free partially commutative left regular band on Graph on 4 vertices
            sage: w = S('cdab')
            sage: H = S.induced_orientation(w)
            sage: H.vertices()
            ['a', 'b', 'c', 'd']
            sage: H.edges()
            [('c', 'a', None), ('c', 'b', None), ('c', 'd', None), ('d', 'a', None)]
            sage: w = S('dab')
            sage: H = S.induced_orientation(w)
            sage: H.vertices()
            ['a', 'b', 'd']
            sage: H.edges()
            [('d', 'a', None)]

        """
        pos = dict((wi,i) for (i,wi) in enumerate(w.value))
        D = DiGraph()
        D.add_vertices(pos)
        for (u,v,l) in self.associated_graph().complement().edges():
            if u in pos and v in pos:
                if pos[u] < pos[v]:
                    D.add_edge(u,v)
                else:
                    D.add_edge(v,u)
        return D
示例#37
0
    def Tournament(self, n):
        r"""
        Returns a tournament on `n` vertices.

        In this tournament there is an edge from `i` to `j` if `i<j`.

        INPUT:

        - ``n`` (integer) -- number of vertices in the tournament.

        EXAMPLES::

            sage: g = digraphs.Tournament(5)
            sage: g.vertices()
            [0, 1, 2, 3, 4]
            sage: g.size()
            10
            sage: g.automorphism_group().cardinality()
            1
        """
        if n < 0:
            raise ValueError(
                "The number of vertices must be a positive integer.")

        g = DiGraph()
        g.name("Tournament on " + str(n) + " vertices")

        for i in range(n - 1):
            for j in range(i + 1, n):
                g.add_edge(i, j)

        if n:
            from sage.graphs.graph_plot import _circle_embedding
            _circle_embedding(g, range(n))

        return g
示例#38
0
    def induced_orientation(self, w):
        r"""
        The induced subgraph of the complement of the underlying graph with an
        orientation determined by `w`: an edge `(x,y)` is directed from `x` to
        `y` if `x` comes before `y` in `w`.

        EXAMPLES::

            sage: from sage_semigroups.monoids.free_partially_commutative_left_regular_band import FreePartiallyCommutativeLeftRegularBand
            sage: G = Graph({'a':['b'],'b':['d'],'c':[],'d':[]})
            sage: S = FreePartiallyCommutativeLeftRegularBand(G); S
            Free partially commutative left regular band on Graph on 4 vertices
            sage: w = S('cdab')
            sage: H = S.induced_orientation(w)
            sage: H.vertices()
            ['a', 'b', 'c', 'd']
            sage: H.edges()
            [('c', 'a', None), ('c', 'b', None), ('c', 'd', None), ('d', 'a', None)]
            sage: w = S('dab')
            sage: H = S.induced_orientation(w)
            sage: H.vertices()
            ['a', 'b', 'd']
            sage: H.edges()
            [('d', 'a', None)]

        """
        pos = dict((wi,i) for (i,wi) in enumerate(w.value))
        D = DiGraph()
        D.add_vertices(pos)
        for (u,v,l) in self.associated_graph().complement().edges():
            if u in pos and v in pos:
                if pos[u] < pos[v]:
                    D.add_edge(u,v)
                else:
                    D.add_edge(v,u)
        return D
示例#39
0
    def Kautz(self, k, D, vertices='strings'):
        r"""
        Returns the Kautz digraph of degree `d` and diameter `D`.

        The Kautz digraph has been defined in [Kautz68]_. The Kautz digraph of
        degree `d` and diameter `D` has `d^{D-1}(d+1)` vertices. This digraph is
        build upon a set of vertices equal to the set of words of length `D`
        from an alphabet of `d+1` letters such that consecutive letters are
        differents. There is an arc from vertex `u` to vertex `v` if `v` can be
        obtained from `u` by removing the leftmost letter and adding a new
        letter, distinct from the rightmost letter of `u`, at the right end.

        The Kautz digraph of degree `d` and diameter `D` is isomorphic to the
        digraph of Imase and Itoh [II83]_ of degree `d` and order
        `d^{D-1}(d+1)`.

        See also the
        :wikipedia:`Wikipedia article on Kautz Graphs <Kautz_graph>`.

        INPUTS:

        - ``k`` -- Two possibilities for this parameter :
            - An integer equal to the degree of the digraph to be produced, that
              is the cardinality minus one of the alphabet to use.
            - An iterable object to be used as the set of letters. The degree of
              the resulting digraph is the cardinality of the set of letters
              minus one.

        - ``D`` -- An integer equal to the diameter of the digraph, and also to
              the length of a vertex label when ``vertices == 'strings'``.

        - ``vertices`` -- 'strings' (default) or 'integers', specifying whether
                      the vertices are words build upon an alphabet or integers.


        EXAMPLES::

            sage: K = digraphs.Kautz(2, 3)
            sage: K.is_isomorphic(digraphs.ImaseItoh(12, 2), certify = True)
            (True,
             {'010': 0,
              '012': 1,
              '020': 3,
              '021': 2,
              '101': 11,
              '102': 10,
              '120': 9,
              '121': 8,
              '201': 5,
              '202': 4,
              '210': 6,
              '212': 7})

            sage: K = digraphs.Kautz([1,'a','B'], 2)
            sage: K.edges()
            [('1B', 'B1', '1'), ('1B', 'Ba', 'a'), ('1a', 'a1', '1'), ('1a', 'aB', 'B'), ('B1', '1B', 'B'), ('B1', '1a', 'a'), ('Ba', 'a1', '1'), ('Ba', 'aB', 'B'), ('a1', '1B', 'B'), ('a1', '1a', 'a'), ('aB', 'B1', '1'), ('aB', 'Ba', 'a')]

            sage: K = digraphs.Kautz([1,'aA','BB'], 2)
            sage: K.edges()
            [('1,BB', 'BB,1', '1'), ('1,BB', 'BB,aA', 'aA'), ('1,aA', 'aA,1', '1'), ('1,aA', 'aA,BB', 'BB'), ('BB,1', '1,BB', 'BB'), ('BB,1', '1,aA', 'aA'), ('BB,aA', 'aA,1', '1'), ('BB,aA', 'aA,BB', 'BB'), ('aA,1', '1,BB', 'BB'), ('aA,1', '1,aA', 'aA'), ('aA,BB', 'BB,1', '1'), ('aA,BB', 'BB,aA', 'aA')]


        TESTS:

        An exception is raised when the degree is less than one::

            sage: G = digraphs.Kautz(0, 2)
            Traceback (most recent call last):
            ...
            ValueError: Kautz digraphs are defined for degree at least one.

            sage: G = digraphs.Kautz(['a'], 2)
            Traceback (most recent call last):
            ...
            ValueError: Kautz digraphs are defined for degree at least one.

        An exception is raised when the diameter of the graph is less than one::

            sage: G = digraphs.Kautz(2, 0)
            Traceback (most recent call last):
            ...
            ValueError: Kautz digraphs are defined for diameter at least one.


        REFERENCE:

        .. [Kautz68] W. H. Kautz. Bounds on directed (d, k) graphs. Theory of
          cellular logic networks and machines, AFCRL-68-0668, SRI Project 7258,
          Final Rep., pp. 20-28, 1968.
        """
        if D < 1:
            raise ValueError(
                "Kautz digraphs are defined for diameter at least one.")

        from sage.combinat.words.words import Words
        from sage.rings.integer import Integer

        my_alphabet = Words(
            [str(i) for i in range(k + 1)] if isinstance(k, Integer) else k, 1)
        if my_alphabet.size_of_alphabet() < 2:
            raise ValueError(
                "Kautz digraphs are defined for degree at least one.")

        if vertices == 'strings':

            # We start building the set of vertices
            V = [i for i in my_alphabet]
            for i in range(D - 1):
                VV = []
                for w in V:
                    VV += [w * a for a in my_alphabet if not w.has_suffix(a)]
                V = VV

            # We now build the set of arcs
            G = DiGraph()
            for u in V:
                for a in my_alphabet:
                    if not u.has_suffix(a):
                        G.add_edge(u.string_rep(), (u[1:] * a).string_rep(),
                                   a.string_rep())

        else:
            d = my_alphabet.size_of_alphabet() - 1
            G = digraphs.ImaseItoh((d + 1) * (d**(D - 1)), d)

        G.name("Kautz digraph (k=%s, D=%s)" % (k, D))
        return G
示例#40
0
    def RandomPoset(n, p):
        r"""
        Generate a random poset on ``n`` elements according to a
        probability ``p``.

        INPUT:

        - ``n`` - number of elements, a non-negative integer

        - ``p`` - a probability, a real number between 0 and 1 (inclusive)

        OUTPUT:

        A poset on `n` elements. The probability `p` roughly measures
        width/height of the output: `p=0` always generates an antichain,
        `p=1` will return a chain. To create interesting examples,
        keep the probability small, perhaps on the order of `1/n`.

        EXAMPLES::

            sage: set_random_seed(0)  # Results are reproducible
            sage: P = Posets.RandomPoset(5, 0.3)
            sage: P.cover_relations()
            [[5, 4], [4, 2], [1, 2]]

        TESTS::

            sage: Posets.RandomPoset('junk', 0.5)
            Traceback (most recent call last):
            ...
            TypeError: number of elements must be an integer, not junk

            sage: Posets.RandomPoset(-6, 0.5)
            Traceback (most recent call last):
            ...
            ValueError: number of elements must be non-negative, not -6

            sage: Posets.RandomPoset(6, 'garbage')
            Traceback (most recent call last):
            ...
            TypeError: probability must be a real number, not garbage

            sage: Posets.RandomPoset(6, -0.5)
            Traceback (most recent call last):
            ...
            ValueError: probability must be between 0 and 1, not -0.5

            sage: Posets.RandomPoset(0, 0.5)
            Finite poset containing 0 elements
        """
        from sage.misc.prandom import random

        try:
            n = Integer(n)
        except TypeError:
            raise TypeError("number of elements must be an integer, not {0}".format(n))
        if n < 0:
            raise ValueError("number of elements must be non-negative, not {0}".format(n))
        try:
            p = float(p)
        except Exception:
            raise TypeError("probability must be a real number, not {0}".format(p))
        if p < 0 or p> 1:
            raise ValueError("probability must be between 0 and 1, not {0}".format(p))

        D = DiGraph(loops=False, multiedges=False)
        D.add_vertices(range(n))
        for i in range(n):
            for j in range(i+1, n):
                if random() < p:
                    D.add_edge(i, j)
        D.relabel(list(Permutations(n).random_element()))
        return Poset(D, cover_relations=False)
示例#41
0
def line_graph(self, labels=True):
    """
    Returns the line graph of the (di)graph.

    INPUT:

    - ``labels`` (boolean) -- whether edge labels should be taken in
      consideration. If ``labels=True``, the vertices of the line graph will be
      triples ``(u,v,label)``, and pairs of vertices otherwise.

      This is set to ``True`` by default.

    The line graph of an undirected graph G is an undirected graph H such that
    the vertices of H are the edges of G and two vertices e and f of H are
    adjacent if e and f share a common vertex in G. In other words, an edge in H
    represents a path of length 2 in G.

    The line graph of a directed graph G is a directed graph H such that the
    vertices of H are the edges of G and two vertices e and f of H are adjacent
    if e and f share a common vertex in G and the terminal vertex of e is the
    initial vertex of f. In other words, an edge in H represents a (directed)
    path of length 2 in G.

    .. NOTE::

        As a :class:`Graph` object only accepts hashable objects as vertices
        (and as the vertices of the line graph are the edges of the graph), this
        code will fail if edge labels are not hashable. You can also set the
        argument ``labels=False`` to ignore labels.

    .. SEEALSO::

        - The :mod:`line_graph <sage.graphs.line_graph>` module.

        - :meth:`~sage.graphs.graph_generators.GraphGenerators.line_graph_forbidden_subgraphs`
          -- the forbidden subgraphs of a line graph.

        - :meth:`~Graph.is_line_graph` -- tests whether a graph is a line graph.

    EXAMPLES::

        sage: g = graphs.CompleteGraph(4)
        sage: h = g.line_graph()
        sage: h.vertices()
        [(0, 1, None),
        (0, 2, None),
        (0, 3, None),
        (1, 2, None),
        (1, 3, None),
        (2, 3, None)]
        sage: h.am()
        [0 1 1 1 1 0]
        [1 0 1 1 0 1]
        [1 1 0 0 1 1]
        [1 1 0 0 1 1]
        [1 0 1 1 0 1]
        [0 1 1 1 1 0]
        sage: h2 = g.line_graph(labels=False)
        sage: h2.vertices()
        [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
        sage: h2.am() == h.am()
        True
        sage: g = DiGraph([[1..4],lambda i,j: i<j])
        sage: h = g.line_graph()
        sage: h.vertices()
        [(1, 2, None),
        (1, 3, None),
        (1, 4, None),
        (2, 3, None),
        (2, 4, None),
        (3, 4, None)]
        sage: h.edges()
        [((1, 2, None), (2, 3, None), None),
         ((1, 2, None), (2, 4, None), None),
         ((1, 3, None), (3, 4, None), None),
         ((2, 3, None), (3, 4, None), None)]

    Tests:

    :trac:`13787`::

        sage: g = graphs.KneserGraph(7,1)
        sage: C = graphs.CompleteGraph(7)
        sage: C.is_isomorphic(g)
        True
        sage: C.line_graph().is_isomorphic(g.line_graph())
        True
    """
    self._scream_if_not_simple()
    if self._directed:
        from sage.graphs.digraph import DiGraph
        G = DiGraph()
        G.add_vertices(self.edges(labels=labels))
        for v in self:
            # Connect appropriate incident edges of the vertex v
            G.add_edges([(e,f) for e in self.incoming_edge_iterator(v, labels=labels) \
                         for f in self.outgoing_edge_iterator(v, labels=labels)])
        return G
    else:
        from sage.graphs.all import Graph
        G = Graph()

        # We must sort the edges' endpoints so that (1,2,None) is seen as
        # the same edge as (2,1,None).
        #
        # We do so by comparing hashes, just in case all the natural order
        # (<) on vertices would not be a total order (for instance when
        # vertices are sets). If two adjacent vertices have the same hash,
        # then we store the pair in the dictionary of conflicts

        conflicts = {}

        # 1) List of vertices in the line graph
        elist = []
        for e in self.edge_iterator(labels=labels):
            if hash(e[0]) < hash(e[1]):
                elist.append(e)
            elif hash(e[0]) > hash(e[1]):
                elist.append((e[1], e[0]) + e[2:])
            else:
                # Settle the conflict arbitrarily
                conflicts[e] = e
                conflicts[(e[1], e[0]) + e[2:]] = e
                elist.append(e)

        G.add_vertices(elist)

        # 2) adjacencies in the line graph
        for v in self:
            elist = []

            # Add the edge to the list, according to hashes, as previously
            for e in self.edge_iterator(v, labels=labels):
                if hash(e[0]) < hash(e[1]):
                    elist.append(e)
                elif hash(e[0]) > hash(e[1]):
                    elist.append((e[1], e[0]) + e[2:])
                else:
                    elist.append(conflicts[e])

            # Alls pairs of elements in elist are edges of the
            # line graph
            while elist:
                x = elist.pop()
                for y in elist:
                    G.add_edge(x, y)

        return G
示例#42
0
    def tournaments_nauty(self,
                          n,
                          min_out_degree=None,
                          max_out_degree=None,
                          strongly_connected=False,
                          debug=False,
                          options=""):
        r"""
        Returns all tournaments on `n` vertices using Nauty.

        INPUT:

        - ``n`` (integer) -- number of vertices.

        - ``min_out_degree``, ``max_out_degree`` (integers) -- if set to
          ``None`` (default), then the min/max out-degree is not constrained.

        - ``debug`` (boolean) -- if ``True`` the first line of genbg's output to
          standard error is captured and the first call to the generator's
          ``next()`` function will return this line as a string.  A line leading
          with ">A" indicates a successful initiation of the program with some
          information on the arguments, while a line beginning with ">E"
          indicates an error with the input.

        - ``options`` (string) -- anything else that should be forwarded as
          input to Nauty's genbg. See its documentation for more information :
          `<http://cs.anu.edu.au/~bdm/nauty/>`_.


        .. NOTE::

            To use this method you must first install the Nauty spkg.

        EXAMPLES::

            sage: for g in digraphs.tournaments_nauty(4): # optional - nauty
            ....:    print g.edges(labels = False)        # optional - nauty
            [(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2)]
            [(1, 0), (1, 3), (2, 0), (2, 1), (3, 0), (3, 2)]
            [(0, 2), (1, 0), (2, 1), (3, 0), (3, 1), (3, 2)]
            [(0, 2), (0, 3), (1, 0), (2, 1), (3, 1), (3, 2)]
            sage: tournaments = digraphs.tournaments_nauty
            sage: [len(list(tournaments(x))) for x in range(1,8)] # optional - nauty
            [1, 1, 2, 4, 12, 56, 456]
            sage: [len(list(tournaments(x, strongly_connected = True))) for x in range(1,9)] # optional - nauty
            [1, 0, 1, 1, 6, 35, 353, 6008]
        """
        import subprocess
        from sage.misc.package import is_package_installed
        if not is_package_installed("nauty"):
            raise TypeError(
                "The optional nauty spkg does not seem to be installed")

        nauty_input = options

        if min_out_degree is None:
            min_out_degree = 0
        if max_out_degree is None:
            max_out_degree = n - 1

        nauty_input += " -d" + str(min_out_degree)
        nauty_input += " -D" + str(max_out_degree)

        if strongly_connected:
            nauty_input += " -c"

        nauty_input += " " + str(n) + " "

        sp = subprocess.Popen("nauty-gentourng {0}".format(nauty_input),
                              shell=True,
                              stdin=subprocess.PIPE,
                              stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE,
                              close_fds=True)

        if debug:
            yield sp.stderr.readline()

        gen = sp.stdout
        while True:
            try:
                s = gen.next()
            except StopIteration:
                raise StopIteration("Exhausted list of graphs from nauty geng")

            G = DiGraph(n)
            i = 0
            j = 1
            for b in s[:-1]:
                if b == '0':
                    G.add_edge(i, j)
                else:
                    G.add_edge(j, i)

                if j == n - 1:
                    i += 1
                    j = i + 1
                else:
                    j += 1

            yield G
示例#43
0
    def RandomPoset(n,p):
        r"""
        Generate a random poset on ``n`` vertices according to a
        probability ``p``.

        INPUT:

        - ``n`` - number of vertices, a non-negative integer

        - ``p`` - a probability, a real number between 0 and 1 (inclusive)

        OUTPUT:

        A poset on ``n`` vertices.  The construction decides to make an
        ordered pair of vertices comparable in the poset with probability
        ``p``, however a pair is not made comparable if it would violate
        the defining properties of a poset, such as transitivity.

        So in practice, once the probability exceeds a small number the
        generated posets may be very similar to a chain.  So to create
        interesting examples, keep the probability small, perhaps on the
        order of `1/n`.

        EXAMPLES::

            sage: Posets.RandomPoset(17,.15)
            Finite poset containing 17 elements

        TESTS::

            sage: Posets.RandomPoset('junk', 0.5)
            Traceback (most recent call last):
            ...
            TypeError: number of elements must be an integer, not junk

            sage: Posets.RandomPoset(-6, 0.5)
            Traceback (most recent call last):
            ...
            ValueError: number of elements must be non-negative, not -6

            sage: Posets.RandomPoset(6, 'garbage')
            Traceback (most recent call last):
            ...
            TypeError: probability must be a real number, not garbage

            sage: Posets.RandomPoset(6, -0.5)
            Traceback (most recent call last):
            ...
            ValueError: probability must be between 0 and 1, not -0.5
        """
        from sage.misc.prandom import random
        try:
            n = Integer(n)
        except TypeError:
            raise TypeError("number of elements must be an integer, not {0}".format(n))
        if n < 0:
            raise ValueError("number of elements must be non-negative, not {0}".format(n))
        try:
            p = float(p)
        except Exception:
            raise TypeError("probability must be a real number, not {0}".format(p))
        if p < 0 or p> 1:
            raise ValueError("probability must be between 0 and 1, not {0}".format(p))

        D = DiGraph(loops=False,multiedges=False)
        D.add_vertices(range(n))
        for i in range(n):
            for j in range(n):
                if random() < p:
                    D.add_edge(i,j)
                    if not D.is_directed_acyclic():
                        D.delete_edge(i,j)
        return Poset(D,cover_relations=False)
示例#44
0
    def ImaseItoh(self, n, d):
        r"""
        Returns the digraph of Imase and Itoh of order `n` and degree `d`.

        The digraph of Imase and Itoh has been defined in [II83]_. It has vertex
        set `V=\{0, 1,..., n-1\}` and there is an arc from vertex `u \in V` to
        all vertices `v \in V` such that `v \equiv (-u*d-a-1) \mod{n}` with
        `0 \leq a < d`.

        When `n = d^{D}`, the digraph of Imase and Itoh is isomorphic to the de
        Bruijn digraph of degree `d` and diameter `D`. When `n = d^{D-1}(d+1)`,
        the digraph of Imase and Itoh is isomorphic to the Kautz digraph
        [Kautz68]_ of degree `d` and diameter `D`.

        INPUTS:

        - ``n`` -- is the number of vertices of the digraph

        - ``d`` -- is the degree of the digraph

        EXAMPLES::

            sage: II = digraphs.ImaseItoh(8, 2)
            sage: II.is_isomorphic(digraphs.DeBruijn(2, 3), certify = True)
            (True, {0: '010', 1: '011', 2: '000', 3: '001', 4: '110', 5: '111', 6: '100', 7: '101'})

            sage: II = digraphs.ImaseItoh(12, 2)
            sage: II.is_isomorphic(digraphs.Kautz(2, 3), certify = True)
            (True, {0: '010', 1: '012', 2: '021', 3: '020', 4: '202', 5: '201', 6: '210', 7: '212', 8: '121', 9: '120', 10: '102', 11: '101'})


        TESTS:

        An exception is raised when the degree is less than one::

            sage: G = digraphs.ImaseItoh(2, 0)
            Traceback (most recent call last):
            ...
            ValueError: The digraph of Imase and Itoh is defined for degree at least one.

        An exception is raised when the order of the graph is less than two::

            sage: G = digraphs.ImaseItoh(1, 2)
            Traceback (most recent call last):
            ...
            ValueError: The digraph of Imase and Itoh is defined for at least two vertices.


        REFERENCE:

        .. [II83] M. Imase and M. Itoh. A design for directed graphs with
          minimum diameter, *IEEE Trans. Comput.*, vol. C-32, pp. 782-784, 1983.
        """
        if n < 2:
            raise ValueError(
                "The digraph of Imase and Itoh is defined for at least two vertices."
            )
        if d < 1:
            raise ValueError(
                "The digraph of Imase and Itoh is defined for degree at least one."
            )

        II = DiGraph(loops=True)
        II.allow_multiple_edges(True)
        for u in xrange(n):
            for a in xrange(-u * d - d, -u * d):
                II.add_edge(u, a % n)

        II.name("Imase and Itoh digraph (n=%s, d=%s)" % (n, d))
        return II
示例#45
0
    def spanning_tree(self):
        r"""
        Return a spanning tree

        OUTPUT:

        - spanning tree as a DiGraph

        - remaining edges as 2-tuples ``(i,e[i])``

        EXAMPLES::

            sage: from surface_dynamics import *

            sage: R = RibbonGraph('(1,2,3)','(1,2)(3,4)')
            sage: R
            Ribbon graph with 2 vertices, 2 edges and 2 faces
            sage: T,o = R.spanning_tree()
            sage: T
            Digraph on 2 vertices
            sage: T.edges()
            [(0, 1, (3, 4))]
            sage: o
            [(1, 2)]

            sage: R = RibbonGraph('(1,2,3)(4,5,6)','(1,2)(3,4)(5,6)')
            sage: R
            Ribbon graph with 2 vertices, 3 edges and 3 faces
            sage: T,o = R.spanning_tree()
            sage: T
            Digraph on 2 vertices
            sage: T.edges()
            [(0, 1, (3, 4))]
            sage: o
            [(1, 2), (5, 6)]

            sage: e = '(1,3)(5,7)(2,4)(6,8)'
            sage: f = '(1,2,3,4,5,6,7,8)'
            sage: R = RibbonGraph(edges=e, faces=f)
            sage: T,o = R.spanning_tree()
            sage: T
            Digraph on 1 vertex
            sage: o
            [[1, 3], [2, 4], [5, 7], [6, 8]]
        """
        from sage.graphs.digraph import DiGraph

        d = self.darts()
        v = self.vertices()
        e = self.edge_perm()

        if self.num_darts() == 0:
            return DiGraph(),[]
        if self.num_vertices() == 1:
            return DiGraph({0:[]}),self.edges()

        T = DiGraph()

        v0 = 0
        for root in v[0]:
            v1 = self.dart_to_vertex(e[root])
            if v1 != 0:
                break

        T.add_edge(v0,v1,(root,e[root]))
        o = []
        seen = set([v0,v1])       # seen vertices
        waiting = [e[root],root]  # waiting darts
        cc = []

        while waiting:
            ii = waiting.pop() # this is a dart
            v0 = self.dart_to_vertex(ii)
            seen.add(v0)
            for j in self.vertex_orbit(ii)[1:]:
                v1 = self.dart_to_vertex(e[j])
                if v1 in seen:
                    if j < e[j]:
                        o.append((j,e[j]))
                else:
                    T.add_edge(v0,v1,(j,e[j]))
                    waiting.append(e[j])
                    seen.add(v1)

        return T, sorted(o)
示例#46
0
    def tournaments_nauty(self, n,
                          min_out_degree = None, max_out_degree = None,
                          strongly_connected = False, debug=False, options=""):
        r"""
        Returns all tournaments on `n` vertices using Nauty.

        INPUT:

        - ``n`` (integer) -- number of vertices.

        - ``min_out_degree``, ``max_out_degree`` (integers) -- if set to
          ``None`` (default), then the min/max out-degree is not constrained.

        - ``debug`` (boolean) -- if ``True`` the first line of genbg's output to
          standard error is captured and the first call to the generator's
          ``next()`` function will return this line as a string.  A line leading
          with ">A" indicates a successful initiation of the program with some
          information on the arguments, while a line beginning with ">E"
          indicates an error with the input.

        - ``options`` (string) -- anything else that should be forwarded as
          input to Nauty's genbg. See its documentation for more information :
          `<http://cs.anu.edu.au/~bdm/nauty/>`_.


        .. NOTE::

            To use this method you must first install the Nauty spkg.

        EXAMPLES::

            sage: for g in digraphs.tournaments_nauty(4): # optional - nauty
            ....:    print g.edges(labels = False)        # optional - nauty
            [(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2)]
            [(1, 0), (1, 3), (2, 0), (2, 1), (3, 0), (3, 2)]
            [(0, 2), (1, 0), (2, 1), (3, 0), (3, 1), (3, 2)]
            [(0, 2), (0, 3), (1, 0), (2, 1), (3, 1), (3, 2)]
            sage: tournaments = digraphs.tournaments_nauty
            sage: [len(list(tournaments(x))) for x in range(1,8)] # optional - nauty
            [1, 1, 2, 4, 12, 56, 456]
            sage: [len(list(tournaments(x, strongly_connected = True))) for x in range(1,9)] # optional - nauty
            [1, 0, 1, 1, 6, 35, 353, 6008]
        """
        import subprocess
        from sage.misc.package import is_package_installed
        if not is_package_installed("nauty"):
            raise TypeError("The optional nauty spkg does not seem to be installed")

        nauty_input = options

        if min_out_degree is None:
            min_out_degree = 0
        if max_out_degree is None:
            max_out_degree = n-1

        nauty_input += " -d"+str(min_out_degree)
        nauty_input += " -D"+str(max_out_degree)

        if strongly_connected:
            nauty_input += " -c"

        nauty_input +=  " "+str(n) +" "

        sp = subprocess.Popen("nauty-gentourng {0}".format(nauty_input), shell=True,
                              stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE, close_fds=True)

        if debug:
            yield sp.stderr.readline()

        gen = sp.stdout
        while True:
            try:
                s = gen.next()
            except StopIteration:
                raise StopIteration("Exhausted list of graphs from nauty geng")

            G = DiGraph(n)
            i = 0
            j = 1
            for b in s[:-1]:
                if b == '0':
                    G.add_edge(i,j)
                else:
                    G.add_edge(j,i)

                if j == n-1:
                    i += 1
                    j = i+1
                else:
                    j += 1

            yield G
示例#47
0
def is_partial_cube(G, certificate=False):
    r"""
    Test whether the given graph is a partial cube.

    A partial cube is a graph that can be isometrically embedded into a
    hypercube, i.e., its vertices can be labelled with (0,1)-vectors of some
    fixed length such that the distance between any two vertices in the graph
    equals the Hamming distance of their labels.

    Originally written by D. Eppstein for the PADS library
    (http://www.ics.uci.edu/~eppstein/PADS/), see also
    [Epp2008]_.  The algorithm runs in `O(n^2)` time, where `n`
    is the number of vertices. See the documentation of
    :mod:`~sage.graphs.partial_cube` for an overview of the algorithm.

    INPUT:

    - ``certificate`` -- boolean (default: ``False``); this function returns
      ``True`` or ``False`` according to the graph, when ``certificate =
      False``. When ``certificate = True`` and the graph is a partial cube, the
      function returns ``(True, mapping)``, where ``mapping`` is an isometric
      mapping of the vertices of the graph to the vertices of a hypercube
      ((0, 1)-strings of a fixed length). When ``certificate = True`` and the
      graph is not a partial cube, ``(False, None)`` is returned.

    EXAMPLES:

    The Petersen graph is not a partial cube::

        sage: g = graphs.PetersenGraph()
        sage: g.is_partial_cube()
        False

    All prisms are partial cubes::

        sage: g = graphs.CycleGraph(10).cartesian_product(graphs.CompleteGraph(2))
        sage: g.is_partial_cube()
        True

    TESTS:

    The returned mapping is an isometric embedding into a hypercube::

        sage: g = graphs.DesarguesGraph()
        sage: _, m = g.is_partial_cube(certificate=True)
        sage: m # random
        {0: '00000',
         1: '00001',
         2: '00011',
         3: '01011',
         4: '11011',
         5: '11111',
         6: '11110',
         7: '11100',
         8: '10100',
         9: '00100',
         10: '01000',
         11: '10001',
         12: '00111',
         13: '01010',
         14: '11001',
         15: '10111',
         16: '01110',
         17: '11000',
         18: '10101',
         19: '00110'}
        sage: all(all(g.distance(u, v) == len([i for i in range(len(m[u])) if m[u][i] != m[v][i]]) for v in m) for u in m)
        True

    A graph without vertices is trivially a partial cube::

        sage: Graph().is_partial_cube(certificate=True)
        (True, {})

    """
    G._scream_if_not_simple()

    if not G.order():
        if certificate:
            return (True, {})
        else:
            return True

    if certificate:
        fail = (False, None)
    else:
        fail = False

    if not G.is_connected():
        return fail
    n = G.order()

    # Initial sanity check: are there few enough edges?
    # Needed so that we don't try to use union-find on a dense
    # graph and incur superquadratic runtimes.
    if 1 << (2 * G.size() // n) > n:
        return fail

    # Check for bipartiteness.
    # This ensures also that each contraction will be bipartite.
    if not G.is_bipartite():
        return fail

    # Set up data structures for algorithm:
    # - contracted: contracted graph at current stage of algorithm
    # - unionfind: union find data structure representing known edge equivalences
    # - available: limit on number of remaining available labels
    from sage.graphs.digraph import DiGraph
    from sage.graphs.graph import Graph
    from sage.sets.disjoint_set import DisjointSet
    contracted = DiGraph({v: {w: (v, w) for w in G[v]} for v in G})
    unionfind = DisjointSet(contracted.edges(labels=False))
    available = n - 1

    # Main contraction loop in place of the original algorithm's recursion
    while contracted.order() > 1:
        # Find max degree vertex in contracted, and update label limit
        deg, root = max([(contracted.out_degree(v), v) for v in contracted],
                        key=lambda x: x[0])
        if deg > available:
            return fail
        available -= deg

        # Set up bitvectors on vertices
        bitvec = {v: 0 for v in contracted}
        neighbors = {}
        for i, neighbor in enumerate(contracted[root]):
            bitvec[neighbor] = 1 << i
            neighbors[1 << i] = neighbor

        # Breadth first search to propagate bitvectors to the rest of the graph
        for level in breadth_first_level_search(contracted, root):
            for v in level:
                for w in level[v]:
                    bitvec[w] |= bitvec[v]

        # Make graph of labeled edges and union them together
        labeled = Graph([contracted.vertices(), []])
        for v, w in contracted.edge_iterator(labels=False):
            diff = bitvec[v] ^ bitvec[w]
            if not diff or not bitvec[w] & ~bitvec[v]:
                continue  # zero edge or wrong direction
            if diff not in neighbors:
                return fail
            neighbor = neighbors[diff]
            unionfind.union(contracted.edge_label(v, w),
                            contracted.edge_label(root, neighbor))
            unionfind.union(contracted.edge_label(w, v),
                            contracted.edge_label(neighbor, root))
            labeled.add_edge(v, w)

        # Map vertices to components of labeled-edge graph
        component = {}
        for i, SCC in enumerate(labeled.connected_components()):
            for v in SCC:
                component[v] = i

        # generate new compressed subgraph
        newgraph = DiGraph()
        for v, w, t in contracted.edge_iterator():
            if bitvec[v] == bitvec[w]:
                vi = component[v]
                wi = component[w]
                if vi == wi:
                    return fail
                if newgraph.has_edge(vi, wi):
                    unionfind.union(newgraph.edge_label(vi, wi), t)
                else:
                    newgraph.add_edge(vi, wi, t)
        contracted = newgraph

    # Make a digraph with edges labeled by the equivalence classes in unionfind
    g = DiGraph({v: {w: unionfind.find((v, w)) for w in G[v]} for v in G})

    # Associates to a vertex the token that acts on it, an check that
    # no two edges on a single vertex have the same label
    action = {}
    for v in g:
        action[v] = set(t for _, _, t in g.edge_iterator(v))
        if len(action[v]) != g.out_degree(v):
            return fail

    # Associate every token to its reverse
    reverse = {}
    for v, w, t in g.edge_iterator():
        rt = g.edge_label(w, v)
        reverse[t] = rt
        reverse[rt] = t

    current = initialState = next(g.vertex_iterator())

    # A token T is said to be 'active' for a vertex u if it takes u
    # one step closer to the source in terms of distance. The 'source'
    # is initially 'initialState'. See the module's documentation for
    # more explanations.

    # Find list of tokens that lead to the initial state
    activeTokens = set()
    for level in breadth_first_level_search(g, initialState):
        for v in level:
            for w in level[v]:
                activeTokens.add(g.edge_label(w, v))
    for t in activeTokens:
        if reverse[t] in activeTokens:
            return fail
    activeTokens = list(activeTokens)

    # Rest of data structure: point from states to list and list to states
    state_to_active_token = {v: -1 for v in g}
    token_to_states = [[] for i in activeTokens
                       ]  # (i.e. vertices on which each token acts)

    def scan(v):
        """Find the next token that is effective for v."""
        a = next(
            i for i in range(state_to_active_token[v] + 1, len(activeTokens))
            if activeTokens[i] is not None and activeTokens[i] in action[v])
        state_to_active_token[v] = a
        token_to_states[a].append(v)

    # Initialize isometric embedding into a hypercube
    if certificate:
        dim = 0
        tokmap = {}
        for t in reverse:
            if t not in tokmap:
                tokmap[t] = tokmap[reverse[t]] = 1 << dim
                dim += 1
        embed = {initialState: 0}

    # Set initial active states
    for v in g:
        if v != current:
            try:
                scan(v)
            except StopIteration:
                return fail

    # Traverse the graph, maintaining active tokens
    for prev, current, fwd in depth_first_traversal(g, initialState):
        if not fwd:
            prev, current = current, prev
        elif certificate:
            embed[current] = embed[prev] ^ tokmap[g.edge_label(prev, current)]

        # Add token to end of list, point to it from old state
        activeTokens.append(g.edge_label(prev, current))
        state_to_active_token[prev] = len(activeTokens) - 1
        token_to_states.append([prev])

        # Inactivate reverse token, find new token for its states
        #
        # (the 'active' token of 'current' is necessarily the label of
        #  (current, previous))
        activeTokens[state_to_active_token[current]] = None
        for v in token_to_states[state_to_active_token[current]]:
            if v != current:
                try:
                    scan(v)
                except StopIteration:
                    return fail

    # All checks passed, return the result
    if certificate:
        format = "{0:0%db}" % dim
        return (True, {v: format.format(l) for v, l in embed.items()})
    else:
        return True
示例#48
0
def Hasse_diagram_from_incidences(atom_to_coatoms, coatom_to_atoms,
                                  face_constructor=None,
                                  required_atoms=None,
                                  key = None,
                                  **kwds):
    r"""
    Compute the Hasse diagram of an atomic and coatomic lattice.

    INPUT:

    - ``atom_to_coatoms`` -- list, ``atom_to_coatom[i]`` should list all
      coatoms over the ``i``-th atom;

    - ``coatom_to_atoms`` -- list, ``coatom_to_atom[i]`` should list all
      atoms under the ``i``-th coatom;

    - ``face_constructor`` -- function or class taking as the first two
      arguments sorted :class:`tuple` of integers and any keyword arguments.
      It will be called to construct a face over atoms passed as the first
      argument and under coatoms passed as the second argument. Default
      implementation will just return these two tuples as a tuple;

    - ``required_atoms`` -- list of atoms (default:None). Each
      non-empty "face" requires at least on of the specified atoms
      present. Used to ensure that each face has a vertex.

    - ``key`` -- any hashable value (default: None). It is passed down
      to :class:`~sage.combinat.posets.posets.FinitePoset`.

    - all other keyword arguments will be passed to ``face_constructor`` on
      each call.

    OUTPUT:

    - :class:`finite poset <sage.combinat.posets.posets.FinitePoset>` with
      elements constructed by ``face_constructor``.

    .. NOTE::

        In addition to the specified partial order, finite posets in Sage have
        internal total linear order of elements which extends the partial one.
        This function will try to make this internal order to start with the
        bottom and atoms in the order corresponding to ``atom_to_coatoms`` and
        to finish with coatoms in the order corresponding to
        ``coatom_to_atoms`` and the top. This may not be possible if atoms and
        coatoms are the same, in which case the preference is given to the
        first list.

    ALGORITHM:

    The detailed description of the used algorithm is given in [KP2002]_.

    The code of this function follows the pseudo-code description in the
    section 2.5 of the paper, although it is mostly based on frozen sets
    instead of sorted lists - this makes the implementation easier and should
    not cost a big performance penalty. (If one wants to make this function
    faster, it should be probably written in Cython.)

    While the title of the paper mentions only polytopes, the algorithm (and
    the implementation provided here) is applicable to any atomic and coatomic
    lattice if both incidences are given, see Section 3.4.

    In particular, this function can be used for strictly convex cones and
    complete fans.

    REFERENCES: [KP2002]_

    AUTHORS:

    - Andrey Novoseltsev (2010-05-13) with thanks to Marshall Hampton for the
      reference.

    EXAMPLES:

    Let's construct the Hasse diagram of a lattice of subsets of {0, 1, 2}.
    Our atoms are {0}, {1}, and {2}, while our coatoms are {0,1}, {0,2}, and
    {1,2}. Then incidences are ::

        sage: atom_to_coatoms = [(0,1), (0,2), (1,2)]
        sage: coatom_to_atoms = [(0,1), (0,2), (1,2)]

    and we can compute the Hasse diagram as ::

        sage: L = sage.geometry.cone.Hasse_diagram_from_incidences(
        ...                       atom_to_coatoms, coatom_to_atoms)
        sage: L
        Finite poset containing 8 elements with distinguished linear extension
        sage: for level in L.level_sets(): print(level)
        [((), (0, 1, 2))]
        [((0,), (0, 1)), ((1,), (0, 2)), ((2,), (1, 2))]
        [((0, 1), (0,)), ((0, 2), (1,)), ((1, 2), (2,))]
        [((0, 1, 2), ())]

    For more involved examples see the *source code* of
    :meth:`sage.geometry.cone.ConvexRationalPolyhedralCone.face_lattice` and
    :meth:`sage.geometry.fan.RationalPolyhedralFan._compute_cone_lattice`.
    """
    from sage.graphs.digraph import DiGraph
    from sage.combinat.posets.posets import FinitePoset
    def default_face_constructor(atoms, coatoms, **kwds):
        return (atoms, coatoms)
    if face_constructor is None:
        face_constructor = default_face_constructor
    atom_to_coatoms = [frozenset(atc) for atc in atom_to_coatoms]
    A = frozenset(range(len(atom_to_coatoms)))  # All atoms
    coatom_to_atoms = [frozenset(cta) for cta in coatom_to_atoms]
    C = frozenset(range(len(coatom_to_atoms)))  # All coatoms
    # Comments with numbers correspond to steps in Section 2.5 of the article
    L = DiGraph(1)       # 3: initialize L
    faces = dict()
    atoms = frozenset()
    coatoms = C
    faces[atoms, coatoms] = 0
    next_index = 1
    Q = [(atoms, coatoms)]              # 4: initialize Q with the empty face
    while Q:                            # 5
        q_atoms, q_coatoms = Q.pop()    # 6: remove some q from Q
        q = faces[q_atoms, q_coatoms]
        # 7: compute H = {closure(q+atom) : atom not in atoms of q}
        H = dict()
        candidates = set(A.difference(q_atoms))
        for atom in candidates:
            coatoms = q_coatoms.intersection(atom_to_coatoms[atom])
            atoms = A
            for coatom in coatoms:
                atoms = atoms.intersection(coatom_to_atoms[coatom])
            H[atom] = (atoms, coatoms)
        # 8: compute the set G of minimal sets in H
        minimals = set([])
        while candidates:
            candidate = candidates.pop()
            atoms = H[candidate][0]
            if atoms.isdisjoint(candidates) and atoms.isdisjoint(minimals):
                minimals.add(candidate)
        # Now G == {H[atom] : atom in minimals}
        for atom in minimals:   # 9: for g in G:
            g_atoms, g_coatoms = H[atom]
            if not required_atoms is None:
                if g_atoms.isdisjoint(required_atoms):
                    continue
            if (g_atoms, g_coatoms) in faces:
                g = faces[g_atoms, g_coatoms]
            else:               # 11: if g was newly created
                g = next_index
                faces[g_atoms, g_coatoms] = g
                next_index += 1
                Q.append((g_atoms, g_coatoms))  # 12
            L.add_edge(q, g)                    # 14
    # End of algorithm, now construct a FinitePoset.
    # In principle, it is recommended to use Poset or in this case perhaps
    # even LatticePoset, but it seems to take several times more time
    # than the above computation, makes unnecessary copies, and crashes.
    # So for now we will mimic the relevant code from Poset.

    # Enumeration of graph vertices must be a linear extension of the poset
    new_order = L.topological_sort()
    # Make sure that coatoms are in the end in proper order
    tail = [faces[atoms, frozenset([coatom])]
            for coatom, atoms in enumerate(coatom_to_atoms)]
    tail.append(faces[A, frozenset()])
    new_order = [n for n in new_order if n not in tail] + tail
    # Make sure that atoms are in the beginning in proper order
    head = [0] # We know that the empty face has index 0
    head.extend(faces[frozenset([atom]), coatoms]
                for atom, coatoms in enumerate(atom_to_coatoms)
                if required_atoms is None or atom in required_atoms)
    new_order = head + [n for n in new_order if n not in head]
    # "Invert" this list to a dictionary
    labels = dict()
    for new, old in enumerate(new_order):
        labels[old] = new
    L.relabel(labels)
    # Construct the actual poset elements
    elements = [None] * next_index
    for face, index in faces.items():
        atoms, coatoms = face
        elements[labels[index]] = face_constructor(
                        tuple(sorted(atoms)), tuple(sorted(coatoms)), **kwds)
    D = {i:f for i,f in enumerate(elements)}
    L.relabel(D)
    return FinitePoset(L, elements, key = key)
示例#49
0
def line_graph(self, labels=True):
    """
    Returns the line graph of the (di)graph.

    INPUT:

    - ``labels`` (boolean) -- whether edge labels should be taken in
      consideration. If ``labels=True``, the vertices of the line graph will be
      triples ``(u,v,label)``, and pairs of vertices otherwise.

      This is set to ``True`` by default.

    The line graph of an undirected graph G is an undirected graph H such that
    the vertices of H are the edges of G and two vertices e and f of H are
    adjacent if e and f share a common vertex in G. In other words, an edge in H
    represents a path of length 2 in G.

    The line graph of a directed graph G is a directed graph H such that the
    vertices of H are the edges of G and two vertices e and f of H are adjacent
    if e and f share a common vertex in G and the terminal vertex of e is the
    initial vertex of f. In other words, an edge in H represents a (directed)
    path of length 2 in G.

    .. NOTE::

        As a :class:`Graph` object only accepts hashable objects as vertices
        (and as the vertices of the line graph are the edges of the graph), this
        code will fail if edge labels are not hashable. You can also set the
        argument ``labels=False`` to ignore labels.

    .. SEEALSO::

        - The :mod:`line_graph <sage.graphs.line_graph>` module.

        - :meth:`~sage.graphs.graph_generators.GraphGenerators.line_graph_forbidden_subgraphs`
          -- the forbidden subgraphs of a line graph.

        - :meth:`~Graph.is_line_graph` -- tests whether a graph is a line graph.

    EXAMPLES::

        sage: g = graphs.CompleteGraph(4)
        sage: h = g.line_graph()
        sage: h.vertices()
        [(0, 1, None),
        (0, 2, None),
        (0, 3, None),
        (1, 2, None),
        (1, 3, None),
        (2, 3, None)]
        sage: h.am()
        [0 1 1 1 1 0]
        [1 0 1 1 0 1]
        [1 1 0 0 1 1]
        [1 1 0 0 1 1]
        [1 0 1 1 0 1]
        [0 1 1 1 1 0]
        sage: h2 = g.line_graph(labels=False)
        sage: h2.vertices()
        [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
        sage: h2.am() == h.am()
        True
        sage: g = DiGraph([[1..4],lambda i,j: i<j])
        sage: h = g.line_graph()
        sage: h.vertices()
        [(1, 2, None),
        (1, 3, None),
        (1, 4, None),
        (2, 3, None),
        (2, 4, None),
        (3, 4, None)]
        sage: h.edges()
        [((1, 2, None), (2, 3, None), None),
         ((1, 2, None), (2, 4, None), None),
         ((1, 3, None), (3, 4, None), None),
         ((2, 3, None), (3, 4, None), None)]

    Tests:

    :trac:`13787`::

        sage: g = graphs.KneserGraph(7,1)
        sage: C = graphs.CompleteGraph(7)
        sage: C.is_isomorphic(g)
        True
        sage: C.line_graph().is_isomorphic(g.line_graph())
        True
    """
    self._scream_if_not_simple()
    if self._directed:
        from sage.graphs.digraph import DiGraph
        G=DiGraph()
        G.add_vertices(self.edges(labels=labels))
        for v in self:
            # Connect appropriate incident edges of the vertex v
            G.add_edges([(e,f) for e in self.incoming_edge_iterator(v, labels=labels) \
                         for f in self.outgoing_edge_iterator(v, labels=labels)])
        return G
    else:
        from sage.graphs.all import Graph
        G=Graph()

        # We must sort the edges' endpoints so that (1,2,None) is seen as
        # the same edge as (2,1,None).
        #
        # We do so by comparing hashes, just in case all the natural order
        # (<) on vertices would not be a total order (for instance when
        # vertices are sets). If two adjacent vertices have the same hash,
        # then we store the pair in the dictionary of conflicts

        conflicts = {}

        # 1) List of vertices in the line graph
        elist = []
        for e in self.edge_iterator(labels = labels):
            if hash(e[0]) < hash(e[1]):
                elist.append(e)
            elif hash(e[0]) > hash(e[1]):
                elist.append((e[1],e[0])+e[2:])
            else:
                # Settle the conflict arbitrarily
                conflicts[e] = e
                conflicts[(e[1],e[0])+e[2:]] = e
                elist.append(e)

        G.add_vertices(elist)

        # 2) adjacencies in the line graph
        for v in self:
            elist = []

            # Add the edge to the list, according to hashes, as previously
            for e in self.edge_iterator(v, labels=labels):
                if hash(e[0]) < hash(e[1]):
                    elist.append(e)
                elif hash(e[0]) > hash(e[1]):
                    elist.append((e[1],e[0])+e[2:])
                else:
                    elist.append(conflicts[e])

            # Alls pairs of elements in elist are edges of the
            # line graph
            while elist:
                x = elist.pop()
                for y in elist:
                    G.add_edge(x,y)

        return G
示例#50
0
    def ImaseItoh(self, n, d):
        r"""
        Returns the digraph of Imase and Itoh of order `n` and degree `d`.

        The digraph of Imase and Itoh has been defined in [II83]_. It has vertex
        set `V=\{0, 1,..., n-1\}` and there is an arc from vertex `u \in V` to
        all vertices `v \in V` such that `v \equiv (-u*d-a-1) \mod{n}` with
        `0 \leq a < d`.

        When `n = d^{D}`, the digraph of Imase and Itoh is isomorphic to the de
        Bruijn digraph of degree `d` and diameter `D`. When `n = d^{D-1}(d+1)`,
        the digraph of Imase and Itoh is isomorphic to the Kautz digraph
        [Kautz68]_ of degree `d` and diameter `D`.

        INPUTS:

        - ``n`` -- is the number of vertices of the digraph

        - ``d`` -- is the degree of the digraph

        EXAMPLES::

            sage: II = digraphs.ImaseItoh(8, 2)
            sage: II.is_isomorphic(digraphs.DeBruijn(2, 3), certify = True)
            (True, {0: '010', 1: '011', 2: '000', 3: '001', 4: '110', 5: '111', 6: '100', 7: '101'})

            sage: II = digraphs.ImaseItoh(12, 2)
            sage: II.is_isomorphic(digraphs.Kautz(2, 3), certify = True)
            (True, {0: '010', 1: '012', 2: '021', 3: '020', 4: '202', 5: '201', 6: '210', 7: '212', 8: '121', 9: '120', 10: '102', 11: '101'})


        TESTS:

        An exception is raised when the degree is less than one::

            sage: G = digraphs.ImaseItoh(2, 0)
            Traceback (most recent call last):
            ...
            ValueError: The digraph of Imase and Itoh is defined for degree at least one.

        An exception is raised when the order of the graph is less than two::

            sage: G = digraphs.ImaseItoh(1, 2)
            Traceback (most recent call last):
            ...
            ValueError: The digraph of Imase and Itoh is defined for at least two vertices.


        REFERENCE:

        .. [II83] M. Imase and M. Itoh. A design for directed graphs with
          minimum diameter, *IEEE Trans. Comput.*, vol. C-32, pp. 782-784, 1983.
        """
        if n < 2:
            raise ValueError("The digraph of Imase and Itoh is defined for at least two vertices.")
        if d < 1:
            raise ValueError("The digraph of Imase and Itoh is defined for degree at least one.")

        II = DiGraph(loops = True)
        II.allow_multiple_edges(True)
        for u in xrange(n):
            for a in xrange(-u*d-d, -u*d):
                II.add_edge(u, a % n)

        II.name( "Imase and Itoh digraph (n=%s, d=%s)"%(n,d) )
        return II
示例#51
0
    def DeBruijn(self, k, n, vertices = 'strings'):
        r"""
        Returns the De Bruijn digraph with parameters `k,n`.

        The De Bruijn digraph with parameters `k,n` is built upon a set of
        vertices equal to the set of words of length `n` from a dictionary of
        `k` letters.

        In this digraph, there is an arc `w_1w_2` if `w_2` can be obtained from
        `w_1` by removing the leftmost letter and adding a new letter at its
        right end.  For more information, see the
        :wikipedia:`Wikipedia article on De Bruijn graph <De_Bruijn_graph>`.

        INPUT:

        - ``k`` -- Two possibilities for this parameter :
              - An integer equal to the cardinality of the alphabet to use, that
                is the degree of the digraph to be produced.
              - An iterable object to be used as the set of letters. The degree
                of the resulting digraph is the cardinality of the set of
                letters.

        - ``n`` -- An integer equal to the length of words in the De Bruijn
          digraph when ``vertices == 'strings'``, and also to the diameter of
          the digraph.

        - ``vertices`` -- 'strings' (default) or 'integers', specifying whether
          the vertices are words build upon an alphabet or integers.

        EXAMPLES::

            sage: db=digraphs.DeBruijn(2,2); db
            De Bruijn digraph (k=2, n=2): Looped digraph on 4 vertices
            sage: db.order()
            4
            sage: db.size()
            8

        TESTS::

            sage: digraphs.DeBruijn(5,0)
            De Bruijn digraph (k=5, n=0): Looped multi-digraph on 1 vertex
            sage: digraphs.DeBruijn(0,0)
            De Bruijn digraph (k=0, n=0): Looped multi-digraph on 0 vertices
        """
        from sage.combinat.words.words import Words
        from sage.rings.integer import Integer

        W = Words(range(k) if isinstance(k, Integer) else k, n)
        A = Words(range(k) if isinstance(k, Integer) else k, 1)
        g = DiGraph(loops=True)

        if vertices == 'strings':
            if n == 0 :
                g.allow_multiple_edges(True)
                v = W[0]
                for a in A:
                    g.add_edge(v.string_rep(), v.string_rep(), a.string_rep())
            else:
                for w in W:
                    ww = w[1:]
                    for a in A:
                        g.add_edge(w.string_rep(), (ww*a).string_rep(), a.string_rep())
        else:
            d = W.size_of_alphabet()
            g = digraphs.GeneralizedDeBruijn(d**n, d)

        g.name( "De Bruijn digraph (k=%s, n=%s)"%(k,n) )
        return g
示例#52
0
def strong_orientations_iterator(G):
    r"""
    Returns an iterator over all strong orientations of a graph `G`.

    A strong orientation of a graph is an orientation of its edges such that
    the obtained digraph is strongly connected (i.e. there exist a directed path
    between each pair of vertices).

    ALGORITHM:

    It is an adaptation of the algorithm published in [CGMRV16]_.
    It runs in `O(mn)` amortized time, where `m` is the number of edges and
    `n` is the number of vertices. The amortized time can be improved to `O(m)`
    with a more involved method.
    In this function, first the graph is preprocessed and a spanning tree is
    generated. Then every orientation of the non-tree edges of the graph can be
    extended to at least one new strong orientation by orienting properly
    the edges of the spanning tree (this property is proved in [CGMRV16]_).
    Therefore, this function generates all partial orientations of the non-tree
    edges and then launches a helper function corresponding to the generation
    algorithm described in [CGMRV16]_.
    In order to avoid trivial symetries, the orientation of an arbitrary edge
    is fixed before the start of the enumeration process.

    INPUT:

    - ``G`` -- an undirected graph.

    OUTPUT:

    - an iterator which will produce all strong orientations of this graph.

    .. NOTE::

        Works only for simple graphs (no multiple edges).
        In order to avoid symetries an orientation of an arbitrary edge is fixed.


    EXAMPLES:

    A cycle has one possible (non-symmetric) strong orientation::

        sage: g = graphs.CycleGraph(4)
        sage: it = g.strong_orientations_iterator()
        sage: len(list(it))
        1

    A tree cannot be strongly oriented::

        sage: g = graphs.RandomTree(100)
        sage: len(list(g.strong_orientations_iterator()))
        0

    Neither can be a disconnected graph::

        sage: g = graphs.CompleteGraph(6)
        sage: g.add_vertex(7)
        sage: len(list(g.strong_orientations_iterator()))
        0

    TESTS:

        sage: g = graphs.CompleteGraph(2)
        sage: len(list(g.strong_orientations_iterator()))
        0

        sage: g = graphs.CubeGraph(3)
        sage: b = True
        sage: for orientedGraph in g.strong_orientations_iterator():
        ....:     if not orientedGraph.is_strongly_connected():
        ....:         b = False
        sage: b
        True

    The total number of strong orientations of a graph can be counted using
    the Tutte polynomial evaluated at points (0,2)::

        sage: g = graphs.PetersenGraph()
        sage: nr1 = len(list(g.strong_orientations_iterator()))
        sage: nr2 = g.tutte_polynomial()(0,2)
        sage: nr1 == nr2/2 # The Tutte polynomial counts also the symmetrical orientations
        True

    """
    # if the graph has a bridge or is disconnected,
    # then it cannot be strongly oriented
    if G.order() < 3 or not G.is_biconnected():
        return

    V = G.vertices()
    Dg = DiGraph([G.vertices(), G.edges()], pos=G.get_pos())

    # compute an arbitrary spanning tree of the undirected graph
    te = kruskal(G)
    treeEdges = [(u,v) for u,v,_ in te]
    A = [edge for edge in G.edges(labels=False) if edge not in treeEdges]

    # initialization of the first binary word 00...0
    # corresponding to the current orientation of the non-tree edges
    existingAedges = [0]*len(A)

    # Make the edges of the spanning tree doubly oriented
    for e in treeEdges:
        if Dg.has_edge(e):
            Dg.add_edge(e[1], e[0])
        else:
            Dg.add_edge(e)

    # Generate all orientations for non-tree edges (using Gray code)
    # Each of these orientations can be extended to a strong orientation
    # of G by orienting properly the tree-edges
    previousWord = 0
    i = 0

    # the orientation of one edge is fixed so we consider one edge less
    nr = 2**(len(A)-1)
    while i < nr:
        word = (i >> 1) ^ i
        bitChanged = word ^ previousWord
        
        bit = 0
        while bitChanged > 1:
            bitChanged >>= 1
            bit += 1

        previousWord = word
        if existingAedges[bit] == 0:
            Dg.reverse_edge(A[bit])
            existingAedges[bit] = 1
        else:
            Dg.reverse_edge(A[bit][1], A[bit][0])
            existingAedges[bit] = 0
        # launch the algorithm for enumeration of the solutions
        for sol in _strong_orientations_of_a_mixed_graph(Dg, V, treeEdges):
            yield sol
        i = i + 1
示例#53
0
    def Kautz(self, k, D, vertices = 'strings'):
        r"""
        Returns the Kautz digraph of degree `d` and diameter `D`.

        The Kautz digraph has been defined in [Kautz68]_. The Kautz digraph of
        degree `d` and diameter `D` has `d^{D-1}(d+1)` vertices. This digraph is
        build upon a set of vertices equal to the set of words of length `D`
        from an alphabet of `d+1` letters such that consecutive letters are
        differents. There is an arc from vertex `u` to vertex `v` if `v` can be
        obtained from `u` by removing the leftmost letter and adding a new
        letter, distinct from the rightmost letter of `u`, at the right end.

        The Kautz digraph of degree `d` and diameter `D` is isomorphic to the
        digraph of Imase and Itoh [II83]_ of degree `d` and order
        `d^{D-1}(d+1)`.

        See also the
        :wikipedia:`Wikipedia article on Kautz Graphs <Kautz_graph>`.

        INPUTS:

        - ``k`` -- Two possibilities for this parameter :
            - An integer equal to the degree of the digraph to be produced, that
              is the cardinality minus one of the alphabet to use.
            - An iterable object to be used as the set of letters. The degree of
              the resulting digraph is the cardinality of the set of letters
              minus one.

        - ``D`` -- An integer equal to the diameter of the digraph, and also to
              the length of a vertex label when ``vertices == 'strings'``.

        - ``vertices`` -- 'strings' (default) or 'integers', specifying whether
                      the vertices are words build upon an alphabet or integers.


        EXAMPLES::

            sage: K = digraphs.Kautz(2, 3)
            sage: K.is_isomorphic(digraphs.ImaseItoh(12, 2), certify = True)
            (True,
             {'010': 0,
              '012': 1,
              '020': 3,
              '021': 2,
              '101': 11,
              '102': 10,
              '120': 9,
              '121': 8,
              '201': 5,
              '202': 4,
              '210': 6,
              '212': 7})

            sage: K = digraphs.Kautz([1,'a','B'], 2)
            sage: K.edges()
            [('1B', 'B1', '1'), ('1B', 'Ba', 'a'), ('1a', 'a1', '1'), ('1a', 'aB', 'B'), ('B1', '1B', 'B'), ('B1', '1a', 'a'), ('Ba', 'a1', '1'), ('Ba', 'aB', 'B'), ('a1', '1B', 'B'), ('a1', '1a', 'a'), ('aB', 'B1', '1'), ('aB', 'Ba', 'a')]

            sage: K = digraphs.Kautz([1,'aA','BB'], 2)
            sage: K.edges()
            [('1,BB', 'BB,1', '1'), ('1,BB', 'BB,aA', 'aA'), ('1,aA', 'aA,1', '1'), ('1,aA', 'aA,BB', 'BB'), ('BB,1', '1,BB', 'BB'), ('BB,1', '1,aA', 'aA'), ('BB,aA', 'aA,1', '1'), ('BB,aA', 'aA,BB', 'BB'), ('aA,1', '1,BB', 'BB'), ('aA,1', '1,aA', 'aA'), ('aA,BB', 'BB,1', '1'), ('aA,BB', 'BB,aA', 'aA')]


        TESTS:

        An exception is raised when the degree is less than one::

            sage: G = digraphs.Kautz(0, 2)
            Traceback (most recent call last):
            ...
            ValueError: Kautz digraphs are defined for degree at least one.

            sage: G = digraphs.Kautz(['a'], 2)
            Traceback (most recent call last):
            ...
            ValueError: Kautz digraphs are defined for degree at least one.

        An exception is raised when the diameter of the graph is less than one::

            sage: G = digraphs.Kautz(2, 0)
            Traceback (most recent call last):
            ...
            ValueError: Kautz digraphs are defined for diameter at least one.


        REFERENCE:

        .. [Kautz68] W. H. Kautz. Bounds on directed (d, k) graphs. Theory of
          cellular logic networks and machines, AFCRL-68-0668, SRI Project 7258,
          Final Rep., pp. 20-28, 1968.
        """
        if D < 1:
            raise ValueError("Kautz digraphs are defined for diameter at least one.")

        from sage.combinat.words.words import Words
        from sage.rings.integer import Integer

        my_alphabet = Words([str(i) for i in range(k+1)] if isinstance(k, Integer) else k, 1)
        if my_alphabet.size_of_alphabet() < 2:
            raise ValueError("Kautz digraphs are defined for degree at least one.")

        if vertices == 'strings':

            # We start building the set of vertices
            V = [i for i in my_alphabet]
            for i in range(D-1):
                VV = []
                for w in V:
                    VV += [w*a for a in my_alphabet if not w.has_suffix(a) ]
                V = VV

            # We now build the set of arcs
            G = DiGraph()
            for u in V:
                for a in my_alphabet:
                    if not u.has_suffix(a):
                        G.add_edge(u.string_rep(), (u[1:]*a).string_rep(), a.string_rep())

        else:
            d = my_alphabet.size_of_alphabet()-1
            G = digraphs.ImaseItoh( (d+1)*(d**(D-1)), d)

        G.name( "Kautz digraph (k=%s, D=%s)"%(k,D) )
        return G
示例#54
0
class ParentBigOh(Parent,UniqueRepresentation):
    def __init__(self,ambiant):
        try:
            self._uniformizer = ambiant.uniformizer_name()
        except NotImplementedError:
            raise TypeError("Impossible to determine the name of the uniformizer")
        self._ambiant_space = ambiant
        self._models = DiGraph(loops=False)
        self._default_model = None
        Parent.__init__(self,ambiant.base_ring())
        if self.base_ring() is None:
            self._base_precision = None
        else:
            if self.base_ring() == ambiant:
                self._base_precision = self
            else:
                self._base_precision = ParentBigOh(self.base_ring())

    def __hash__(self):
        return id(self)

    def base_precision(self):
        return self._base_precision

    def precision(self):
        return self._precision

    def default_model(self):
        if self._default_mode is None:
            self.set_default_model()
        return self._default_model
    
    def set_default_model(self,model=None):
        if model is None:
            self._default_model = self._models.topological_sort()[-1]
        else:
            if self._models.has_vertex(model):
                self._default_model = model
            else:
                raise ValueError

    def add_model(self,model):
        from bigoh import BigOh
        if not isinstance(model,list):
            model = [model]
        for m in model:
            if not issubclass(m,BigOh):
                raise TypeError("A precision model must derive from BigOh but '%s' is not"%m)
            self._models.add_vertex(m)

    def delete_model(self,model):
        if isinstance(model,list):
            model = [model]
        for m in model:
            if self._models.has_vertex(m):
                self._models.delete_vertex(m)

    def update_model(self,old,new):
        from bigoh import BigOh
        if self._models.has_vertex(old):
            if not issubclass(new,BigOh):
                raise TypeError("A precision model must derive from BigOh but '%s' is not"%new)
            self._models.relabel({old:new})
        else:
            raise ValueError("Model '%m' does not exist"%old)

    def add_modelconversion(self,domain,codomain,constructor=None,safemode=False):
        if not self._models.has_vertex(domain):
            if safemode: return
            raise ValueError("Model '%s' does not exist"%domain)
        if not self._models.has_vertex(codomain):
            if safemode: return
            raise ValueError("Model '%s' does not exist"%codomain)
        path = self._models.shortest_path(codomain,domain)
        if len(path) > 0:
            raise ValueError("Adding this conversion creates a cycle")
        self._models.add_edge(domain,codomain,constructor)

    def delete_modelconversion(self,domain,codomain):
        if not self._models.has_vertex(domain):
            raise ValueError("Model '%s' does not exist"%domain)
        if not self._models.has_vertex(codomain):
            raise ValueError("Model '%s' does not exist"%codomain)
        if not self._models_has_edge(domain,codomain):
            raise ValueError("No conversion from %s to %s"%(domain,codomain))
        self._modelfs.delete_edge(domain,codomain)

    def uniformizer_name(self):
        return self._uniformizer

    def ambiant_space(self):
        return self._ambiant_space

    # ?!?
    def __call__(self,*args,**kwargs):
        return self._element_constructor_(*args,**kwargs)
    
    def _element_constructor_(self, *args, **kwargs):
        if kwargs.has_key('model'):
            del kwargs['model']
            return kwargs['model'](self, *args, **kwargs)
        if len(args) > 0:
            from precision.bigoh import BigOh, ExactBigOh
            arg = args[0]
            if isinstance(arg,BigOh) and arg.is_exact():
                return ExactBigOh(self)
            if self._models.has_vertex(arg.__class__) and arg.parent() is self:
                return arg
        if self._default_model is not None:
            try:
                return self._default_model(self,*args,**kwargs)
            except (AttributeError,ValueError,TypeError):
                pass
        models = self._models.topological_sort()
        models.reverse()
        for m in models:
            try:
                return m(self,*args,**kwargs)
            except (AttributeError,ValueError,TypeError,PrecisionError):
                pass
        raise PrecisionError("unable to create a BigOh object")

    def __repr__(self):
        return "Parent for BigOh in %s" % self._ambiant_space

    def models(self,graph=False):
        if graph:
            return self._models
        else:
            return self._models.vertices()

    def dimension(self):
        return self._ambiant_space.dimension()

    def indices_basis(self):
        return self._ambiant_space.indices_basis()
示例#55
0
    def GeneralizedDeBruijn(self, n, d):
        r"""
        Returns the generalized de Bruijn digraph of order `n` and degree `d`.

        The generalized de Bruijn digraph has been defined in [RPK80]_
        [RPK83]_. It has vertex set `V=\{0, 1,..., n-1\}` and there is an arc
        from vertex `u \in V` to all vertices `v \in V` such that
        `v \equiv (u*d + a) \mod{n}` with `0 \leq a < d`.

        When `n = d^{D}`, the generalized de Bruijn digraph is isomorphic to the
        de Bruijn digraph of degree `d` and diameter `D`.

        INPUTS:

        - ``n`` -- is the number of vertices of the digraph

        - ``d`` -- is the degree of the digraph

        .. SEEALSO::

            * :meth:`sage.graphs.generic_graph.GenericGraph.is_circulant` --
              checks whether a (di)graph is circulant, and/or returns all
              possible sets of parameters.

        EXAMPLE::

            sage: GB = digraphs.GeneralizedDeBruijn(8, 2)
            sage: GB.is_isomorphic(digraphs.DeBruijn(2, 3), certify = True)
            (True, {0: '000', 1: '001', 2: '010', 3: '011', 4: '100', 5: '101', 6: '110', 7: '111'})

        TESTS:

        An exception is raised when the degree is less than one::

            sage: G = digraphs.GeneralizedDeBruijn(2, 0)
            Traceback (most recent call last):
            ...
            ValueError: The generalized de Bruijn digraph is defined for degree at least one.

        An exception is raised when the order of the graph is less than one::

            sage: G = digraphs.GeneralizedDeBruijn(0, 2)
            Traceback (most recent call last):
            ...
            ValueError: The generalized de Bruijn digraph is defined for at least one vertex.


        REFERENCES:

        .. [RPK80] S. M. Reddy, D. K. Pradhan, and J. Kuhl. Directed graphs with
          minimal diameter and maximal connectivity, School Eng., Oakland Univ.,
          Rochester MI, Tech. Rep., July 1980.

        .. [RPK83] S. Reddy, P. Raghavan, and J. Kuhl. A Class of Graphs for
          Processor Interconnection. *IEEE International Conference on Parallel
          Processing*, pages 154-157, Los Alamitos, Ca., USA, August 1983.
        """
        if n < 1:
            raise ValueError(
                "The generalized de Bruijn digraph is defined for at least one vertex."
            )
        if d < 1:
            raise ValueError(
                "The generalized de Bruijn digraph is defined for degree at least one."
            )

        GB = DiGraph(loops=True)
        GB.allow_multiple_edges(True)
        for u in xrange(n):
            for a in xrange(u * d, u * d + d):
                GB.add_edge(u, a % n)

        GB.name("Generalized de Bruijn digraph (n=%s, d=%s)" % (n, d))
        return GB
示例#56
0
def Hasse_diagram_from_incidences(atom_to_coatoms, coatom_to_atoms,
                                  face_constructor=None,
                                  required_atoms=None,
                                  key = None,
                                  **kwds):
    r"""
    Compute the Hasse diagram of an atomic and coatomic lattice.

    INPUT:

    - ``atom_to_coatoms`` -- list, ``atom_to_coatom[i]`` should list all
      coatoms over the ``i``-th atom;

    - ``coatom_to_atoms`` -- list, ``coatom_to_atom[i]`` should list all
      atoms under the ``i``-th coatom;

    - ``face_constructor`` -- function or class taking as the first two
      arguments sorted :class:`tuple` of integers and any keyword arguments.
      It will be called to construct a face over atoms passed as the first
      argument and under coatoms passed as the second argument. Default
      implementation will just return these two tuples as a tuple;

    - ``required_atoms`` -- list of atoms (default:None). Each
      non-empty "face" requires at least on of the specified atoms
      present. Used to ensure that each face has a vertex.

    - ``key`` -- any hashable value (default: None). It is passed down
      to :class:`~sage.combinat.posets.posets.FinitePoset`.

    - all other keyword arguments will be passed to ``face_constructor`` on
      each call.

    OUTPUT:

    - :class:`finite poset <sage.combinat.posets.posets.FinitePoset>` with
      elements constructed by ``face_constructor``.

    .. NOTE::

        In addition to the specified partial order, finite posets in Sage have
        internal total linear order of elements which extends the partial one.
        This function will try to make this internal order to start with the
        bottom and atoms in the order corresponding to ``atom_to_coatoms`` and
        to finish with coatoms in the order corresponding to
        ``coatom_to_atoms`` and the top. This may not be possible if atoms and
        coatoms are the same, in which case the preference is given to the
        first list.

    ALGORITHM:

    The detailed description of the used algorithm is given in [KP2002]_.

    The code of this function follows the pseudo-code description in the
    section 2.5 of the paper, although it is mostly based on frozen sets
    instead of sorted lists - this makes the implementation easier and should
    not cost a big performance penalty. (If one wants to make this function
    faster, it should be probably written in Cython.)

    While the title of the paper mentions only polytopes, the algorithm (and
    the implementation provided here) is applicable to any atomic and coatomic
    lattice if both incidences are given, see Section 3.4.

    In particular, this function can be used for strictly convex cones and
    complete fans.

    REFERENCES: [KP2002]_

    AUTHORS:

    - Andrey Novoseltsev (2010-05-13) with thanks to Marshall Hampton for the
      reference.

    EXAMPLES:

    Let's construct the Hasse diagram of a lattice of subsets of {0, 1, 2}.
    Our atoms are {0}, {1}, and {2}, while our coatoms are {0,1}, {0,2}, and
    {1,2}. Then incidences are ::

        sage: atom_to_coatoms = [(0,1), (0,2), (1,2)]
        sage: coatom_to_atoms = [(0,1), (0,2), (1,2)]

    and we can compute the Hasse diagram as ::

        sage: L = sage.geometry.cone.Hasse_diagram_from_incidences(
        ....:                     atom_to_coatoms, coatom_to_atoms)
        sage: L
        Finite poset containing 8 elements with distinguished linear extension
        sage: for level in L.level_sets(): print(level)
        [((), (0, 1, 2))]
        [((0,), (0, 1)), ((1,), (0, 2)), ((2,), (1, 2))]
        [((0, 1), (0,)), ((0, 2), (1,)), ((1, 2), (2,))]
        [((0, 1, 2), ())]

    For more involved examples see the *source code* of
    :meth:`sage.geometry.cone.ConvexRationalPolyhedralCone.face_lattice` and
    :meth:`sage.geometry.fan.RationalPolyhedralFan._compute_cone_lattice`.
    """
    from sage.graphs.digraph import DiGraph
    from sage.combinat.posets.posets import FinitePoset
    def default_face_constructor(atoms, coatoms, **kwds):
        return (atoms, coatoms)
    if face_constructor is None:
        face_constructor = default_face_constructor
    atom_to_coatoms = [frozenset(atc) for atc in atom_to_coatoms]
    A = frozenset(range(len(atom_to_coatoms)))  # All atoms
    coatom_to_atoms = [frozenset(cta) for cta in coatom_to_atoms]
    C = frozenset(range(len(coatom_to_atoms)))  # All coatoms
    # Comments with numbers correspond to steps in Section 2.5 of the article
    L = DiGraph(1)       # 3: initialize L
    faces = dict()
    atoms = frozenset()
    coatoms = C
    faces[atoms, coatoms] = 0
    next_index = 1
    Q = [(atoms, coatoms)]              # 4: initialize Q with the empty face
    while Q:                            # 5
        q_atoms, q_coatoms = Q.pop()    # 6: remove some q from Q
        q = faces[q_atoms, q_coatoms]
        # 7: compute H = {closure(q+atom) : atom not in atoms of q}
        H = dict()
        candidates = set(A.difference(q_atoms))
        for atom in candidates:
            coatoms = q_coatoms.intersection(atom_to_coatoms[atom])
            atoms = A
            for coatom in coatoms:
                atoms = atoms.intersection(coatom_to_atoms[coatom])
            H[atom] = (atoms, coatoms)
        # 8: compute the set G of minimal sets in H
        minimals = set([])
        while candidates:
            candidate = candidates.pop()
            atoms = H[candidate][0]
            if atoms.isdisjoint(candidates) and atoms.isdisjoint(minimals):
                minimals.add(candidate)
        # Now G == {H[atom] : atom in minimals}
        for atom in minimals:   # 9: for g in G:
            g_atoms, g_coatoms = H[atom]
            if not required_atoms is None:
                if g_atoms.isdisjoint(required_atoms):
                    continue
            if (g_atoms, g_coatoms) in faces:
                g = faces[g_atoms, g_coatoms]
            else:               # 11: if g was newly created
                g = next_index
                faces[g_atoms, g_coatoms] = g
                next_index += 1
                Q.append((g_atoms, g_coatoms))  # 12
            L.add_edge(q, g)                    # 14
    # End of algorithm, now construct a FinitePoset.
    # In principle, it is recommended to use Poset or in this case perhaps
    # even LatticePoset, but it seems to take several times more time
    # than the above computation, makes unnecessary copies, and crashes.
    # So for now we will mimic the relevant code from Poset.

    # Enumeration of graph vertices must be a linear extension of the poset
    new_order = L.topological_sort()
    # Make sure that coatoms are in the end in proper order
    tail = [faces[atoms, frozenset([coatom])]
            for coatom, atoms in enumerate(coatom_to_atoms)]
    tail.append(faces[A, frozenset()])
    new_order = [n for n in new_order if n not in tail] + tail
    # Make sure that atoms are in the beginning in proper order
    head = [0] # We know that the empty face has index 0
    head.extend(faces[frozenset([atom]), coatoms]
                for atom, coatoms in enumerate(atom_to_coatoms)
                if required_atoms is None or atom in required_atoms)
    new_order = head + [n for n in new_order if n not in head]
    # "Invert" this list to a dictionary
    labels = dict()
    for new, old in enumerate(new_order):
        labels[old] = new
    L.relabel(labels)
    # Construct the actual poset elements
    elements = [None] * next_index
    for face, index in faces.items():
        atoms, coatoms = face
        elements[labels[index]] = face_constructor(
                        tuple(sorted(atoms)), tuple(sorted(coatoms)), **kwds)
    D = {i:f for i,f in enumerate(elements)}
    L.relabel(D)
    return FinitePoset(L, elements, key = key)
示例#57
0
def strong_orientations_iterator(G):
    r"""
    Returns an iterator over all strong orientations of a graph `G`.

    A strong orientation of a graph is an orientation of its edges such that
    the obtained digraph is strongly connected (i.e. there exist a directed path
    between each pair of vertices).

    ALGORITHM:

    It is an adaptation of the algorithm published in [CGMRV16]_.
    It runs in `O(mn)` amortized time, where `m` is the number of edges and
    `n` is the number of vertices. The amortized time can be improved to `O(m)`
    with a more involved method.
    In this function, first the graph is preprocessed and a spanning tree is
    generated. Then every orientation of the non-tree edges of the graph can be
    extended to at least one new strong orientation by orienting properly
    the edges of the spanning tree (this property is proved in [CGMRV16]_).
    Therefore, this function generates all partial orientations of the non-tree
    edges and then launches a helper function corresponding to the generation
    algorithm described in [CGMRV16]_.
    In order to avoid trivial symetries, the orientation of an arbitrary edge
    is fixed before the start of the enumeration process.

    INPUT:

    - ``G`` -- an undirected graph.

    OUTPUT:

    - an iterator which will produce all strong orientations of this graph.

    .. NOTE::

        Works only for simple graphs (no multiple edges).
        In order to avoid symetries an orientation of an arbitrary edge is fixed.


    EXAMPLES:

    A cycle has one possible (non-symmetric) strong orientation::

        sage: g = graphs.CycleGraph(4)
        sage: it = g.strong_orientations_iterator()
        sage: len(list(it))
        1

    A tree cannot be strongly oriented::

        sage: g = graphs.RandomTree(100)
        sage: len(list(g.strong_orientations_iterator()))
        0

    Neither can be a disconnected graph::

        sage: g = graphs.CompleteGraph(6)
        sage: g.add_vertex(7)
        sage: len(list(g.strong_orientations_iterator()))
        0

    TESTS:

        sage: g = graphs.CompleteGraph(2)
        sage: len(list(g.strong_orientations_iterator()))
        0

        sage: g = graphs.CubeGraph(3)
        sage: b = True
        sage: for orientedGraph in g.strong_orientations_iterator():
        ....:     if not orientedGraph.is_strongly_connected():
        ....:         b = False
        sage: b
        True

    The total number of strong orientations of a graph can be counted using
    the Tutte polynomial evaluated at points (0,2)::

        sage: g = graphs.PetersenGraph()
        sage: nr1 = len(list(g.strong_orientations_iterator()))
        sage: nr2 = g.tutte_polynomial()(0,2)
        sage: nr1 == nr2/2 # The Tutte polynomial counts also the symmetrical orientations
        True

    """
    # if the graph has a bridge or is disconnected,
    # then it cannot be strongly oriented
    if G.order() < 3 or not G.is_biconnected():
        return

    V = G.vertices()
    Dg = DiGraph([G.vertices(), G.edges()], pos=G.get_pos())

    # compute an arbitrary spanning tree of the undirected graph
    te = kruskal(G)
    treeEdges = [(u,v) for u,v,_ in te]
    A = [edge for edge in G.edges(labels=False) if edge not in treeEdges]

    # initialization of the first binary word 00...0
    # corresponding to the current orientation of the non-tree edges
    existingAedges = [0]*len(A)

    # Make the edges of the spanning tree doubly oriented
    for e in treeEdges:
        if Dg.has_edge(e):
            Dg.add_edge(e[1], e[0])
        else:
            Dg.add_edge(e)

    # Generate all orientations for non-tree edges (using Gray code)
    # Each of these orientations can be extended to a strong orientation
    # of G by orienting properly the tree-edges
    previousWord = 0
    i = 0

    # the orientation of one edge is fixed so we consider one edge less
    nr = 2**(len(A)-1)
    while i < nr:
        word = (i >> 1) ^ i
        bitChanged = word ^ previousWord
        
        bit = 0
        while bitChanged > 1:
            bitChanged >>= 1
            bit += 1

        previousWord = word
        if existingAedges[bit] == 0:
            Dg.reverse_edge(A[bit])
            existingAedges[bit] = 1
        else:
            Dg.reverse_edge(A[bit][1], A[bit][0])
            existingAedges[bit] = 0
        # launch the algorithm for enumeration of the solutions
        for sol in _strong_orientations_of_a_mixed_graph(Dg, V, treeEdges):
            yield sol
        i = i + 1
示例#58
0
    def GeneralizedDeBruijn(self, n, d):
        r"""
        Returns the generalized de Bruijn digraph of order `n` and degree `d`.

        The generalized de Bruijn digraph has been defined in [RPK80]_
        [RPK83]_. It has vertex set `V=\{0, 1,..., n-1\}` and there is an arc
        from vertex `u \in V` to all vertices `v \in V` such that
        `v \equiv (u*d + a) \mod{n}` with `0 \leq a < d`.

        When `n = d^{D}`, the generalized de Bruijn digraph is isomorphic to the
        de Bruijn digraph of degree `d` and diameter `D`.

        INPUTS:

        - ``n`` -- is the number of vertices of the digraph

        - ``d`` -- is the degree of the digraph

        .. SEEALSO::

            * :meth:`sage.graphs.generic_graph.GenericGraph.is_circulant` --
              checks whether a (di)graph is circulant, and/or returns all
              possible sets of parameters.

        EXAMPLE::

            sage: GB = digraphs.GeneralizedDeBruijn(8, 2)
            sage: GB.is_isomorphic(digraphs.DeBruijn(2, 3), certify = True)
            (True, {0: '000', 1: '001', 2: '010', 3: '011', 4: '100', 5: '101', 6: '110', 7: '111'})

        TESTS:

        An exception is raised when the degree is less than one::

            sage: G = digraphs.GeneralizedDeBruijn(2, 0)
            Traceback (most recent call last):
            ...
            ValueError: The generalized de Bruijn digraph is defined for degree at least one.

        An exception is raised when the order of the graph is less than one::

            sage: G = digraphs.GeneralizedDeBruijn(0, 2)
            Traceback (most recent call last):
            ...
            ValueError: The generalized de Bruijn digraph is defined for at least one vertex.


        REFERENCES:

        .. [RPK80] S. M. Reddy, D. K. Pradhan, and J. Kuhl. Directed graphs with
          minimal diameter and maximal connectivity, School Eng., Oakland Univ.,
          Rochester MI, Tech. Rep., July 1980.

        .. [RPK83] S. Reddy, P. Raghavan, and J. Kuhl. A Class of Graphs for
          Processor Interconnection. *IEEE International Conference on Parallel
          Processing*, pages 154-157, Los Alamitos, Ca., USA, August 1983.
        """
        if n < 1:
            raise ValueError("The generalized de Bruijn digraph is defined for at least one vertex.")
        if d < 1:
            raise ValueError("The generalized de Bruijn digraph is defined for degree at least one.")

        GB = DiGraph(loops = True)
        GB.allow_multiple_edges(True)
        for u in xrange(n):
            for a in xrange(u*d, u*d+d):
                GB.add_edge(u, a%n)

        GB.name( "Generalized de Bruijn digraph (n=%s, d=%s)"%(n,d) )
        return GB
示例#59
0
    def RandomPoset(n,p):
        r"""
        Generate a random poset on ``n`` vertices according to a
        probability ``p``.

        INPUT:

        - ``n`` - number of vertices, a non-negative integer

        - ``p`` - a probability, a real number between 0 and 1 (inclusive)

        OUTPUT:

        A poset on ``n`` vertices.  The construction decides to make an
        ordered pair of vertices comparable in the poset with probability
        ``p``, however a pair is not made comparable if it would violate
        the defining properties of a poset, such as transitivity.

        So in practice, once the probability exceeds a small number the
        generated posets may be very similar to a chain.  So to create
        interesting examples, keep the probability small, perhaps on the
        order of `1/n`.

        EXAMPLES::

            sage: Posets.RandomPoset(17,.15)
            Finite poset containing 17 elements

        TESTS::

            sage: Posets.RandomPoset('junk', 0.5)
            Traceback (most recent call last):
            ...
            TypeError: number of elements must be an integer, not junk

            sage: Posets.RandomPoset(-6, 0.5)
            Traceback (most recent call last):
            ...
            ValueError: number of elements must be non-negative, not -6

            sage: Posets.RandomPoset(6, 'garbage')
            Traceback (most recent call last):
            ...
            TypeError: probability must be a real number, not garbage

            sage: Posets.RandomPoset(6, -0.5)
            Traceback (most recent call last):
            ...
            ValueError: probability must be between 0 and 1, not -0.5
        """
        from sage.misc.prandom import random
        try:
            n = Integer(n)
        except TypeError:
            raise TypeError("number of elements must be an integer, not {0}".format(n))
        if n < 0:
            raise ValueError("number of elements must be non-negative, not {0}".format(n))
        try:
            p = float(p)
        except Exception:
            raise TypeError("probability must be a real number, not {0}".format(p))
        if p < 0 or p> 1:
            raise ValueError("probability must be between 0 and 1, not {0}".format(p))

        D = DiGraph(loops=False,multiedges=False)
        D.add_vertices(range(n))
        for i in range(n):
            for j in range(n):
                if random() < p:
                    D.add_edge(i,j)
                    if not D.is_directed_acyclic():
                        D.delete_edge(i,j)
        return Poset(D,cover_relations=False)
示例#60
0
    def DeBruijn(self, k, n, vertices='strings'):
        r"""
        Returns the De Bruijn digraph with parameters `k,n`.

        The De Bruijn digraph with parameters `k,n` is built upon a set of
        vertices equal to the set of words of length `n` from a dictionary of
        `k` letters.

        In this digraph, there is an arc `w_1w_2` if `w_2` can be obtained from
        `w_1` by removing the leftmost letter and adding a new letter at its
        right end.  For more information, see the
        :wikipedia:`Wikipedia article on De Bruijn graph <De_Bruijn_graph>`.

        INPUT:

        - ``k`` -- Two possibilities for this parameter :
              - An integer equal to the cardinality of the alphabet to use, that
                is the degree of the digraph to be produced.
              - An iterable object to be used as the set of letters. The degree
                of the resulting digraph is the cardinality of the set of
                letters.

        - ``n`` -- An integer equal to the length of words in the De Bruijn
          digraph when ``vertices == 'strings'``, and also to the diameter of
          the digraph.

        - ``vertices`` -- 'strings' (default) or 'integers', specifying whether
          the vertices are words build upon an alphabet or integers.

        EXAMPLES::

            sage: db=digraphs.DeBruijn(2,2); db
            De Bruijn digraph (k=2, n=2): Looped digraph on 4 vertices
            sage: db.order()
            4
            sage: db.size()
            8

        TESTS::

            sage: digraphs.DeBruijn(5,0)
            De Bruijn digraph (k=5, n=0): Looped multi-digraph on 1 vertex
            sage: digraphs.DeBruijn(0,0)
            De Bruijn digraph (k=0, n=0): Looped multi-digraph on 0 vertices
        """
        from sage.combinat.words.words import Words
        from sage.rings.integer import Integer

        W = Words(range(k) if isinstance(k, Integer) else k, n)
        A = Words(range(k) if isinstance(k, Integer) else k, 1)
        g = DiGraph(loops=True)

        if vertices == 'strings':
            if n == 0:
                g.allow_multiple_edges(True)
                v = W[0]
                for a in A:
                    g.add_edge(v.string_rep(), v.string_rep(), a.string_rep())
            else:
                for w in W:
                    ww = w[1:]
                    for a in A:
                        g.add_edge(w.string_rep(), (ww * a).string_rep(),
                                   a.string_rep())
        else:
            d = W.size_of_alphabet()
            g = digraphs.GeneralizedDeBruijn(d**n, d)

        g.name("De Bruijn digraph (k=%s, n=%s)" % (k, n))
        return g