Example #1
0
def underlying_graph(G):
    r"""
    Given a graph `G` with multi-edges, returns a graph where all the
    multi-edges are replaced with a single edge.

    EXAMPLES::

        sage: from sage.graphs.tutte_polynomial import underlying_graph
        sage: G = Graph(multiedges=True)
        sage: G.add_edges([(0,1,'a'),(0,1,'b')])
        sage: G.edges()
        [(0, 1, 'a'), (0, 1, 'b')]
        sage: underlying_graph(G).edges()
        [(0, 1, None)]
    """
    g = Graph()
    g.allow_loops(True)
    for edge in set(G.edges(labels=False)):
        g.add_edge(edge)
    return g
Example #2
0
def graph6_to_plot(graph6):
    """
    Return a ``Graphics`` object from a ``graph6`` string.

    This method constructs a graph from a ``graph6`` string and returns a
    :class:`sage.plot.graphics.Graphics` object with arguments preset for the
    :meth:`sage.plot.graphics.Graphics.show` method.

    INPUT:

    - ``graph6`` -- a ``graph6`` string

    EXAMPLES::

        sage: from sage.graphs.graph_database import graph6_to_plot
        sage: type(graph6_to_plot('D??'))
        <class 'sage.plot.graphics.Graphics'>
    """
    g = Graph(str(graph6))
    return g.plot(layout='circular', vertex_size=30, vertex_labels=False, graph_border=False)
Example #3
0
    def dependence_graph(self):
        r"""
        Return graph of dependence relation.

        OUTPUT: dependence graph with generators as vertices

        TESTS::

            sage: from sage.monoids.trace_monoid import TraceMonoid
            sage: F.<a,b,c> = FreeMonoid()
            sage: M.<ai,bi,ci> = TraceMonoid(F, I=((a,c), (c,a)))
            sage: M.dependence_graph() == Graph({a:[a,b], b:[b], c:[c,b]})
            True
        """
        return Graph(set(
            frozenset((e1, e2)) if e1 != e2 else (e1, e2)
            for e1, e2 in self.dependence()),
                     loops=True,
                     format="list_of_edges",
                     immutable=True)
Example #4
0
    def get_graphs_list(self):
        """
        Returns a list of Sage Graph objects that satisfy the query.

        EXAMPLES::

            sage: Q = GraphQuery(display_cols=['graph6','num_vertices','degree_sequence'],num_edges=['<=',5],min_degree=1)
            sage: L = Q.get_graphs_list()
            sage: L[0]
            Graph on 2 vertices
            sage: len(L)
            35
        """
        from sage.graphs.graph_list import from_graph6

        s = self.__query_string__
        re.sub('SELECT.*FROM ', 'SELECT graph6 FROM ', s)
        q = GenericGraphQuery(s, self.__database__, self.__param_tuple__)
        graph6_list = q.query_results()
        return [Graph(str(g[0])) for g in graph6_list]
Example #5
0
def _check_pbd(B, v, S):
    r"""
    Checks that ``B`` is a PBD on `v` points with given block sizes.

    INPUT:

    - ``bibd`` -- a list of blocks

    - ``v`` (integer) -- number of points

    - ``S`` -- list of integers

    EXAMPLE::

        sage: designs.BalancedIncompleteBlockDesign(40,4).blocks() # indirect doctest
        [[0, 1, 2, 12], [0, 3, 6, 9], [0, 4, 8, 11], [0, 5, 7, 10],
         [0, 13, 26, 39], [0, 14, 28, 38], [0, 15, 25, 27],
         [0, 16, 32, 35], [0, 17, 34, 37], [0, 18, 33, 36],
        ...
    """
    from itertools import combinations
    from sage.graphs.graph import Graph

    if not all(len(X) in S for X in B):
        raise RuntimeError(
            "This is not a nice honest PBD from the good old days !")

    g = Graph()
    m = 0
    for X in B:
        g.add_edges(list(combinations(X, 2)))
        if g.size() != m + binomial(len(X), 2):
            raise RuntimeError(
                "This is not a nice honest PBD from the good old days !")
        m = g.size()

    if not (g.is_clique() and g.vertices() == range(v)):
        raise RuntimeError(
            "This is not a nice honest PBD from the good old days !")

    return B
Example #6
0
def DegreeSequence(deg_sequence):
    """
    Returns a graph with the given degree sequence. Raises a NetworkX
    error if the proposed degree sequence cannot be that of a graph.

    Graph returned is the one returned by the Havel-Hakimi algorithm,
    which constructs a simple graph by connecting vertices of highest
    degree to other vertices of highest degree, resorting the remaining
    vertices by degree and repeating the process. See Theorem 1.4 in
    [CL1996]_.

    INPUT:

    -  ``deg_sequence`` - a list of integers with each
       entry corresponding to the degree of a different vertex.


    EXAMPLES::

        sage: G = graphs.DegreeSequence([3,3,3,3])
        sage: G.edges(labels=False)
        [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
        sage: G.show()  # long time

    ::

        sage: G = graphs.DegreeSequence([3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3])
        sage: G.show()  # long time

    ::

        sage: G = graphs.DegreeSequence([4,4,4,4,4,4,4,4])
        sage: G.show()  # long time

    ::

        sage: G = graphs.DegreeSequence([1,2,3,4,3,4,3,2,3,2,1])
        sage: G.show()  # long time
    """
    import networkx
    return Graph(networkx.havel_hakimi_graph([int(i) for i in deg_sequence]))
Example #7
0
 def copy(self,
          weighted=None,
          data_structure=None,
          sparse=None,
          immutable=None):
     r"""
     This method has been overridden by DiscreteZOO to ensure that a mutable
     copy will have type ``Graph``.
     """
     if immutable is False or (data_structure is not None
                               and data_structure is not 'static_sparse'):
         return Graph(self).copy(weighted=weighted,
                                 data_structure=data_structure,
                                 sparse=sparse,
                                 immutable=immutable)
     else:
         return Graph.copy(self,
                           weighted=weighted,
                           data_structure=data_structure,
                           sparse=sparse,
                           immutable=immutable)
Example #8
0
def DiamondGraph():
    """
    Return a diamond graph with 4 nodes.

    A diamond graph is a square with one pair of diagonal nodes connected.

    PLOTTING: Upon construction, the position dictionary is filled to override
    the spring-layout algorithm. By convention, the diamond graph is drawn as a
    diamond, with the first node on top, second on the left, third on the right,
    and fourth on the bottom; with the second and third node connected.

    EXAMPLES:

    Construct and show a diamond graph::

        sage: g = graphs.DiamondGraph()
        sage: g.show()  # long time
    """
    pos_dict = {0:(0,1),1:(-1,0),2:(1,0),3:(0,-1)}
    edges = [(0, 1), (0, 2), (1, 2), (1, 3), (2, 3)]
    return Graph(edges, pos=pos_dict, name="Diamond Graph")
    def coxeter_diagram(self):
        """
        Return the Coxeter diagram associated to ``self``.

        EXAMPLES::

            sage: G = ReflectionGroup(['B',3])                          # optional - gap3
            sage: sorted(G.coxeter_diagram().edges(labels=True))        # optional - gap3
            [(1, 2, 4), (2, 3, 3)]
        """
        from sage.graphs.graph import Graph
        from itertools import combinations

        V = self.index_set()
        S = self.simple_reflections()
        E = []
        for i,j in combinations(V, 2):
            o = (S[i]*S[j]).order()
            if o >= 3:
                E.append((i,j,o))
        return Graph([V,E], format='vertices_and_edges', immutable=True)
Example #10
0
def DegreeSequenceTree(deg_sequence):
    """
    Returns a tree with the given degree sequence. Raises a NetworkX
    error if the proposed degree sequence cannot be that of a tree.

    Since every tree has one more vertex than edge, the degree sequence
    must satisfy len(deg_sequence) - sum(deg_sequence)/2 == 1.

    INPUT:

    -  ``deg_sequence`` - a list of integers with each
       entry corresponding to the expected degree of a different vertex.


    EXAMPLES::

        sage: G = graphs.DegreeSequenceTree([3,1,3,3,1,1,1,2,1])
        sage: G.show()  # long time
    """
    import networkx
    return Graph(networkx.degree_sequence_tree([int(i) for i in deg_sequence]))
Example #11
0
    def coxeter_graph(self):
        """
        Return the Coxeter graph of ``self``.

        EXAMPLES::

            sage: C = CoxeterMatrix(['A',3])
            sage: C.coxeter_graph()
            Graph on 3 vertices

            sage: C = CoxeterMatrix([['A',3],['A',1]])
            sage: C.coxeter_graph()
            Graph on 4 vertices
        """
        n = self.rank()
        I = self.index_set()
        val = lambda x: infinity if x == -1 else x
        G = Graph([(I[i], I[j], val((self._matrix)[i, j])) for i in range(n)
                   for j in range(i) if self._matrix[i, j] not in [1, 2]])
        G.add_vertices(I)
        return G.copy(immutable=True)
Example #12
0
File: raag.py Project: yarv/sage
    def __init__(self, G):
        """
        Initialize ``self``.

        INPUT:

        - ``G`` -- a graph

        TESTS::

            sage: G = RightAngledArtinGroup(graphs.CycleGraph(5))
            sage: TestSuite(G).run()
        """
        self._graph = G
        F = FreeGroup(names=['v{}'.format(v) for v in self._graph.vertices()])
        CG = Graph(G).complement()  # Make sure it's mutable
        CG.relabel()  # Standardize the labels
        rels = tuple(
            F([i + 1, j + 1, -i - 1, -j - 1])
            for i, j in CG.edges(False))  # +/- 1 for indexing
        FinitelyPresentedGroup.__init__(self, F, rels)
Example #13
0
    def descendant(self, v):
        """
        The descendant :class:`graph <sage.graphs.graph.Graph>` at ``v``

        The :mod:`switching class of graphs <sage.combinat.designs.twographs>`
        corresponding to ``self`` contains a graph ``D`` with ``v`` its own connected
        component; removing ``v`` from ``D``, one obtains the descendant graph of
        ``self`` at ``v``, which is constructed by this method.

        INPUT:

        - ``v`` -- an element of :meth:`ground_set`

        EXAMPLES::

            sage: p=graphs.PetersenGraph().twograph().descendant(0)
            sage: p.is_strongly_regular(parameters=True)
            (9, 4, 1, 2)
        """
        from sage.graphs.graph import Graph
        return Graph(map(lambda y: filter(lambda z: z != v, y),
                            filter(lambda x: v in x, self.blocks())))
Example #14
0
    def to_graph(self):
        r"""
        Returns the graph corresponding to the perfect matching.

        OUTPUT:

            The realization of ``self`` as a graph.

        EXAMPLES::

            sage: PerfectMatching([[1,3], [4,2]]).to_graph().edges(labels=False)
            [(1, 3), (2, 4)]
            sage: PerfectMatching([[1,4], [3,2]]).to_graph().edges(labels=False)
            [(1, 4), (2, 3)]
            sage: PerfectMatching([]).to_graph().edges(labels=False)
            []
        """
        from sage.graphs.graph import Graph
        G = Graph()
        for a, b in self.value:
            G.add_edge((a, b))
        return G
Example #15
0
    def __classcall_private__(cls, G, names=None):
        """
        Normalize input to ensure a unique representation.

        TESTS::

            sage: G1 = RightAngledArtinGroup(graphs.CycleGraph(5))
            sage: Gamma = Graph([(0,1),(1,2),(2,3),(3,4),(4,0)])
            sage: G2 = RightAngledArtinGroup(Gamma)
            sage: G3 = RightAngledArtinGroup([(0,1),(1,2),(2,3),(3,4),(4,0)])
            sage: G4 = RightAngledArtinGroup(Gamma, 'v')
            sage: G1 is G2 and G2 is G3 and G3 is G4
            True

        Handle the empty graph::

            sage: RightAngledArtinGroup(Graph())
            Traceback (most recent call last):
            ...
            ValueError: the graph must not be empty
        """
        if not isinstance(G, Graph):
            G = Graph(G, immutable=True)
        else:
            G = G.copy(immutable=True)
        if G.num_verts() == 0:
            raise ValueError("the graph must not be empty")
        if names is None:
            names = 'v'
        if isinstance(names, six.string_types):
            if ',' in names:
                names = [x.strip() for x in names.split(',')]
            else:
                names = [names + str(v) for v in G.vertices()]
        names = tuple(names)
        if len(names) != G.num_verts():
            raise ValueError("the number of generators must match the"
                             " number of vertices of the defining graph")
        return super(RightAngledArtinGroup, cls).__classcall__(cls, G, names)
def royle_x_graph():
    r"""
    Return a strongly regular graph, as described by Royle [Roy2008]_.

    INPUT:

    None.

    OUTPUT:

    An object of class ``Graph``, representing Royle's X graph [Roy2008]_.

    EXAMPLES:

    ::

        sage: from boolean_cayley_graphs.royle_x_graph import royle_x_graph
        sage: g = royle_x_graph()
        sage: g.is_strongly_regular()
        True
        sage: g.is_strongly_regular(parameters=True)
        (64, 35, 18, 20)

    REFERENCES:

    Royle [Roy2008]_.

    """
    n = 8
    order = 64

    vecs = [vector([1] * n)]
    for a in Combinations(xsrange(1, n), 4):
        vecs.append(vector([-1 if x in a else 1 for x in xsrange(n)]))
    for b in Combinations(xsrange(n), 2):
        vecs.append(vector([-1 if x in b else 1 for x in xsrange(n)]))

    return Graph([(i, j) for i in xsrange(order)
                  for j in xsrange(i + 1, order) if vecs[i] * vecs[j] == 0])
Example #17
0
 def apply(solver, model, structures):
 
     g = structures[0]
     vertices = solver.get_objects_in_model(model, g, g.internal_graph.vertices())
     dims = int(math.log(g.order, 2))
     #print(vertices)
     #print(dims)
     edges = solver.get_objects_in_model(model, g, g.internal_graph.edges(labels=False))
     #print(edges)
     #create temp graph
     t = Graph()
     t.add_vertices(vertices)
     t.add_edges(edges)
     for i in range(g.order/2):
         antipod = g.order - 1 - i
         #print(i, antipod)
         if i in vertices and antipod in vertices:
             path = t.shortest_path(i, antipod)
             if path:
                 #print(path)
                 return (True, [g, path])
     print("COUNTER")
     return (False, [])
    def connection_graph(self):
        """
        Return the graph which has the variables of this system as
        vertices and edges between two variables if they appear in the
        same polynomial.

        EXAMPLE::

            sage: B.<x,y,z> = BooleanPolynomialRing()
            sage: F = Sequence([x*y + y + 1, z + 1])
            sage: F.connection_graph()
            Graph on 3 vertices
        """
        V = sorted(self.variables())
        from sage.graphs.graph import Graph
        g = Graph()
        g.add_vertices(sorted(V))
        for f in self:
            v = f.variables()
            a,tail = v[0],v[1:]
            for b in tail:
                g.add_edge((a,b))
        return g
Example #19
0
def EmptyGraph():
    """
    Return an empty graph (0 nodes and 0 edges).

    This is useful for constructing graphs by adding edges and vertices
    individually or in a loop.

    PLOTTING: When plotting, this graph will use the default
    spring-layout algorithm, unless a position dictionary is
    specified.

    EXAMPLES:

    Add one vertex to an empty graph and then show::

        sage: empty1 = graphs.EmptyGraph()
        sage: empty1.add_vertex()
        0
        sage: empty1.show()  # long time

    Use for loops to build a graph from an empty graph::

        sage: empty2 = graphs.EmptyGraph()
        sage: for i in range(5):
        ....:     empty2.add_vertex()  # add 5 nodes, labeled 0-4
        0
        1
        2
        3
        4
        sage: for i in range(3):
        ....:     empty2.add_edge(i,i+1)  # add edges {[0:1],[1:2],[2:3]}
        sage: for i in range(1, 4):
        ....:     empty2.add_edge(4,i)  # add edges {[1:4],[2:4],[3:4]}
        sage: empty2.show()  # long time
    """
    return Graph(sparse=True)
Example #20
0
    def _construct_object(self, cl, d):
        r"""
        Prepare all necessary data and construct the graph.

        INPUT:

        - ``cl`` - the class to construct the graph for.

        - ``d`` - the dictionary of parameters.
        """
        ZooObject.__init__(self, **d)
        if d["data"] is None:
            try:
                d["data"] = self._db_read(cl, kargs=d)["data"]
            except KeyError as ex:
                if not d["store"]:
                    raise ex
        propname = lookup(self._graphprops, "name", default=None)
        if d["name"]:
            self._graphprops["name"] = d["name"]
        elif propname:
            d["name"] = propname
        if propname == '':
            del self._graphprops["name"]
        if d["vertex_labels"] is not None:
            d["data"] = Graph(d["data"]).relabel(d["vertex_labels"],
                                                 inplace=False)
        if d["loops"] is None:
            d["loops"] = self._graphprops["number_of_loops"] > 0
        elif not d["loops"] and self._graphprops["number_of_loops"] > 0:
            raise ValueError("the requested graph has loops")
        if d["multiedges"] is None:
            d["multiedges"] = self._graphprops["has_multiple_edges"]
        elif not d["multiedges"] and self._graphprops["has_multiple_edges"]:
            raise ValueError("the requested graph has multiple edges")
        construct(Graph, self, d)
        self._initialized = True
Example #21
0
def DegreeSequenceExpected(deg_sequence, seed=None):
    """
    Returns a random graph with expected given degree sequence. Raises
    a NetworkX error if the proposed degree sequence cannot be that of
    a graph.

    One requirement is that the sum of the degrees must be even, since
    every edge must be incident with two vertices.

    INPUT:

    -  ``deg_sequence`` - a list of integers with each
       entry corresponding to the expected degree of a different vertex.

    -  ``seed`` - for the random number generator.


    EXAMPLES::

        sage: G = graphs.DegreeSequenceExpected([1,2,3,2,3])
        sage: G.edges(labels=False)
        [(0, 2), (0, 3), (1, 1), (1, 4), (2, 3), (2, 4), (3, 4), (4, 4)]
        sage: G.show()  # long time

    REFERENCE:

    .. [ChungLu2002] Chung, Fan and Lu, L. Connected components in random
      graphs with given expected degree sequences.
      Ann. Combinatorics (6), 2002 pp. 125-145.
    """
    if seed is None:
        seed = current_randstate().long_seed()
    import networkx
    return Graph(networkx.expected_degree_graph([int(i) for i in deg_sequence],
                                                seed=seed),
                 loops=True)
Example #22
0
def DegreeSequenceExpected(deg_sequence, seed=None):
    """
    Returns a random graph with expected given degree sequence. Raises
    a NetworkX error if the proposed degree sequence cannot be that of
    a graph.

    One requirement is that the sum of the degrees must be even, since
    every edge must be incident with two vertices.

    INPUT:

    - ``deg_sequence`` - a list of integers with each entry corresponding to the
      expected degree of a different vertex.

    - ``seed`` - a ``random.Random`` seed or a Python ``int`` for the random
      number generator (default: ``None``).


    EXAMPLES::

        sage: G = graphs.DegreeSequenceExpected([1,2,3,2,3])
        sage: G.edges(labels=False)
        [(0, 3), (1, 3), (1, 4), (4, 4)]                    # 32-bit 
        [(0, 3), (1, 4), (2, 2), (2, 3), (2, 4), (4, 4)]    # 64-bit
        sage: G.show()  # long time

    REFERENCE:

    [CL2002]_
    """
    if seed is None:
        seed = int(current_randstate().long_seed() % sys.maxsize)
    import networkx
    return Graph(networkx.expected_degree_graph([int(i) for i in deg_sequence],
                                                seed=seed),
                 loops=True)
Example #23
0
def ClawGraph():
    """
    Return a claw graph.

    A claw graph is named for its shape. It is actually a complete
    bipartite graph with ``(n1, n2) = (1, 3)``.

    PLOTTING: See :meth:`CompleteBipartiteGraph`.

    EXAMPLES:

    Show a Claw graph::

        sage: (graphs.ClawGraph()).show()  # long time

    Inspect a Claw graph::

        sage: G = graphs.ClawGraph()
        sage: G
        Claw graph: Graph on 4 vertices
    """
    edge_list = [(0, 1), (0, 2), (0, 3)]
    pos_dict = {0: (0, 1), 1: (-1, 0), 2: (0, 0), 3: (1, 0)}
    return Graph(edge_list, pos=pos_dict, name="Claw graph")
Example #24
0
 def relabel(self,
             perm=None,
             inplace=True,
             return_map=False,
             check_input=True,
             complete_partial_function=True,
             immutable=True):
     r"""
     This method has been overridden by DiscreteZOO to ensure that a mutable
     copy will have type ``Graph``.
     """
     if inplace:
         raise ValueError("To relabel an immutable graph use inplace=False")
     G = Graph(self, immutable=False)
     perm = G.relabel(perm,
                      return_map=True,
                      check_input=check_input,
                      complete_partial_function=complete_partial_function)
     if immutable is not False:
         G = self.__class__(self, vertex_labels=perm)
     if return_map:
         return G, perm
     else:
         return G
Example #25
0
def RandomLobster(n, p, q, seed=None):
    """
    Returns a random lobster.

    A lobster is a tree that reduces to a caterpillar when pruning all
    leaf vertices. A caterpillar is a tree that reduces to a path when
    pruning all leaf vertices (q=0).

    INPUT:

    -  ``n`` - expected number of vertices in the backbone

    -  ``p`` - probability of adding an edge to the
       backbone

    -  ``q`` - probability of adding an edge (claw) to the
       arms

    -  ``seed`` - for the random number generator


    EXAMPLE: We show the edge list of a random graph with 3 backbone
    nodes and probabilities `p = 0.7` and `q = 0.3`::

        sage: graphs.RandomLobster(3, 0.7, 0.3).edges(labels=False)
        [(0, 1), (1, 2)]

    ::

        sage: G = graphs.RandomLobster(9, .6, .3)
        sage: G.show()  # long time
    """
    if seed is None:
        seed = current_randstate().long_seed()
    import networkx
    return Graph(networkx.random_lobster(n, p, q, seed=seed))
Example #26
0
def decode_fgraph_string(s):
    data = s.split("_")
    n = decode_6bits(data[0])
    f = [decode_6bits(data[1][i]) for i in range(n)]

    G = Graph()
    G.add_vertices(range(n))

    # read in the adjacency matrix
    cur = 0
    val = decode_6bits(data[2][cur])
    mask = 1 << 5  # start with the high bit
    for j in range(
            n):  # adj matrix is bit packed in colex order, as in graph6 format
        for i in range(j):
            if val & mask != 0:  # test whether that bit is nonzero
                G.add_edge(i, j)
            mask >>= 1
            if mask == 0:  # mask has become 0
                cur += 1
                val = decode_6bits(data[2][cur])
                mask = 1 << 5

    return G, f
Example #27
0
def RandomBipartite(n1, n2, p):
    r"""
    Returns a bipartite graph with `n1+n2` vertices
    such that any edge from `[n1]` to `[n2]` exists
    with probability `p`.

    INPUT:

        - ``n1,n2`` : Cardinalities of the two sets
        - ``p``   : Probability for an edge to exist


    EXAMPLE::

        sage: g=graphs.RandomBipartite(5,2,0.5)
        sage: g.vertices()
        [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (1, 1)]

    TESTS::

        sage: g=graphs.RandomBipartite(5,-3,0.5)
        Traceback (most recent call last):
        ...
        ValueError: n1 and n2 should be integers strictly greater than 0
        sage: g=graphs.RandomBipartite(5,3,1.5)
        Traceback (most recent call last):
        ...
        ValueError: Parameter p is a probability, and so should be a real value between 0 and 1

    Trac ticket #12155::

        sage: graphs.RandomBipartite(5,6,.2).complement()
        complement(Random bipartite graph of size 5+6 with edge probability 0.200000000000000): Graph on 11 vertices
    """
    if not (p >= 0 and p <= 1):
        raise ValueError, "Parameter p is a probability, and so should be a real value between 0 and 1"
    if not (n1 > 0 and n2 > 0):
        raise ValueError, "n1 and n2 should be integers strictly greater than 0"

    from numpy.random import uniform
    from sage.graphs.all import Graph

    g = Graph(name="Random bipartite graph of size " + str(n1) + "+" +
              str(n2) + " with edge probability " + str(p))

    S1 = [(0, i) for i in range(n1)]
    S2 = [(1, i) for i in range(n2)]
    g.add_vertices(S1)
    g.add_vertices(S2)

    for w in range(n2):
        for v in range(n1):
            if uniform() <= p:
                g.add_edge((0, v), (1, w))

    pos = {}
    for i in range(n1):
        pos[(0, i)] = (0, i / (n1 - 1.0))
    for i in range(n2):
        pos[(1, i)] = (1, i / (n2 - 1.0))

    g.set_pos(pos)

    return g
Example #28
0
def OrthogonalArrayBlockGraph(k, n, OA=None):
    r"""
    Return the graph of an `OA(k,n)`.

    The intersection graph of the blocks of a transversal design with parameters
    `(k,n)`, or `TD(k,n)` for short, is a strongly regular graph (unless it is a
    complete graph). Its parameters `(v,k',\lambda,\mu)` are determined by the
    parameters `k,n` via:

    .. MATH::

        v=n^2, k'=k(n-1), \lambda=(k-1)(k-2)+n-2, \mu=k(k-1)

    As transversal designs and orthogonal arrays (OA for short) are equivalent
    objects, this graph can also be built from the blocks of an `OA(k,n)`, two
    of them being adjacent if one of their coordinates match.

    For more information on these graphs, see `Andries Brouwer's page
    on Orthogonal Array graphs <https://www.win.tue.nl/~aeb/graphs/OA.html>`_.

    .. WARNING::

        - Brouwer's website uses the notation `OA(n,k)` instead of `OA(k,n)`

        - For given parameters `k` and `n` there can be many `OA(k,n)` : the
          graphs returned are not uniquely defined by their parameters (see the
          examples below).

        - If the function is called only with the parameter ``k`` and ``n`` the
          results might be different with two versions of Sage, or even worse :
          some could not be available anymore.

    .. SEEALSO::

        :mod:`sage.combinat.designs.orthogonal_arrays`

    INPUT:

    - ``k,n`` (integers)

    - ``OA`` -- An orthogonal array. If set to ``None`` (default) then
      :func:`~sage.combinat.designs.orthogonal_arrays.orthogonal_array` is
      called to compute an `OA(k,n)`.

    EXAMPLES::

        sage: G = graphs.OrthogonalArrayBlockGraph(5,5); G
        OA(5,5): Graph on 25 vertices
        sage: G.is_strongly_regular(parameters=True)
        (25, 20, 15, 20)
        sage: G = graphs.OrthogonalArrayBlockGraph(4,10); G
        OA(4,10): Graph on 100 vertices
        sage: G.is_strongly_regular(parameters=True)
        (100, 36, 14, 12)

    Two graphs built from different orthogonal arrays are also different::

        sage: k=4;n=10
        sage: OAa = designs.orthogonal_arrays.build(k,n)
        sage: OAb = [[(x+1)%n for x in R] for R in OAa]
        sage: set(map(tuple,OAa)) == set(map(tuple,OAb))
        False
        sage: Ga = graphs.OrthogonalArrayBlockGraph(k,n,OAa)
        sage: Gb = graphs.OrthogonalArrayBlockGraph(k,n,OAb)
        sage: Ga == Gb
        False

    As ``OAb`` was obtained from ``OAa`` by a relabelling the two graphs are
    isomorphic::

        sage: Ga.is_isomorphic(Gb)
        True

    But there are examples of `OA(k,n)` for which the resulting graphs are not
    isomorphic::

        sage: oa0 = [[0, 0, 1], [0, 1, 3], [0, 2, 0], [0, 3, 2],
        ....:        [1, 0, 3], [1, 1, 1], [1, 2, 2], [1, 3, 0],
        ....:        [2, 0, 0], [2, 1, 2], [2, 2, 1], [2, 3, 3],
        ....:        [3, 0, 2], [3, 1, 0], [3, 2, 3], [3, 3, 1]]
        sage: oa1 = [[0, 0, 1], [0, 1, 0], [0, 2, 3], [0, 3, 2],
        ....:        [1, 0, 3], [1, 1, 2], [1, 2, 0], [1, 3, 1],
        ....:        [2, 0, 0], [2, 1, 1], [2, 2, 2], [2, 3, 3],
        ....:        [3, 0, 2], [3, 1, 3], [3, 2, 1], [3, 3, 0]]
        sage: g0 = graphs.OrthogonalArrayBlockGraph(3,4,oa0)
        sage: g1 = graphs.OrthogonalArrayBlockGraph(3,4,oa1)
        sage: g0.is_isomorphic(g1)
        False

    But nevertheless isospectral::

        sage: g0.spectrum()
        [9, 1, 1, 1, 1, 1, 1, 1, 1, 1, -3, -3, -3, -3, -3, -3]
        sage: g1.spectrum()
        [9, 1, 1, 1, 1, 1, 1, 1, 1, 1, -3, -3, -3, -3, -3, -3]

    Note that the graph ``g0`` is actually isomorphic to the affine polar graph
    `VO^+(4,2)`::

        sage: graphs.AffineOrthogonalPolarGraph(4,2,'+').is_isomorphic(g0)
        True

    TESTS::

        sage: G = graphs.OrthogonalArrayBlockGraph(4,6)
        Traceback (most recent call last):
        ...
        NotImplementedError: I don't know how to build an OA(4,6)!
        sage: G = graphs.OrthogonalArrayBlockGraph(8,2)
        Traceback (most recent call last):
        ...
        ValueError: There is no OA(8,2). Beware, Brouwer's website uses OA(n,k) instead of OA(k,n) !
    """
    if n > 1 and k >= n + 2:
        raise ValueError(
            "There is no OA({},{}). Beware, Brouwer's website uses OA(n,k) instead of OA(k,n) !"
            .format(k, n))

    from itertools import combinations

    if OA is None:
        from sage.combinat.designs.orthogonal_arrays import orthogonal_array
        OA = orthogonal_array(k, n)
    else:
        assert len(OA) == n**2
        assert n == 0 or k == len(OA[0])

    OA = map(tuple, OA)

    d = [[[] for j in range(n)] for i in range(k)]
    for R in OA:
        for i, x in enumerate(R):
            d[i][x].append(R)

    g = Graph()
    for l in d:
        for ll in l:
            g.add_edges(combinations(ll, 2))

    g.name("OA({},{})".format(k, n))

    return g
Example #29
0
def ToleranceGraph(tolrep):
    r"""
    Return the graph generated by the tolerance representation ``tolrep``.

    The tolerance representation ``tolrep`` is described by the list
    `((l_0,r_0,t_0), (l_1,r_1,t_1), ..., (l_k,r_k,t_k))` where `I_i = (l_i,r_i)`
    denotes a closed interval on the real line with `l_i < r_i` and `t_i` a
    positive value, called tolerance. This representation generates the
    tolerance graph with the vertex set {0,1, ..., k} and the edge set `{(i,j):
    |I_i \cap I_j| \ge \min{t_i, t_j}}` where `|I_i \cap I_j|` denotes the
    length of the intersection of `I_i` and `I_j`.

    INPUT:

    - ``tolrep`` -- list of triples `(l_i,r_i,t_i)` where `(l_i,r_i)` denotes a
      closed interval on the real line and `t_i` a positive value.

    .. NOTE::

        The vertices are named 0, 1, ..., k. The tolerance representation used
        to create the graph is saved with the graph and can be recovered using
        ``get_vertex()`` or ``get_vertices()``.

    EXAMPLES:

    The following code creates a tolerance representation ``tolrep``, generates
    its tolerance graph ``g``, and applies some checks::

        sage: tolrep = [(1,4,3),(1,2,1),(2,3,1),(0,3,3)]
        sage: g = graphs.ToleranceGraph(tolrep)
        sage: g.get_vertex(3)
        (0, 3, 3)
        sage: neigh = g.neighbors(3)
        sage: for v in neigh: print(g.get_vertex(v))
        (1, 2, 1)
        (2, 3, 1)
        sage: g.is_interval()
        False
        sage: g.is_weakly_chordal()
        True

    The intervals in the list need not be distinct ::

        sage: tolrep2 = [(0,4,5),(1,2,1),(2,3,1),(0,4,5)]
        sage: g2 = graphs.ToleranceGraph(tolrep2)
        sage: g2.get_vertices()
        {0: (0, 4, 5), 1: (1, 2, 1), 2: (2, 3, 1), 3: (0, 4, 5)}
        sage: g2.is_isomorphic(g)
        True

    Real values are also allowed ::

        sage: tolrep = [(0.1,3.3,4.4),(1.1,2.5,1.1),(1.4,4.4,3.3)]
        sage: g = graphs.ToleranceGraph(tolrep)
        sage: g.is_isomorphic(graphs.PathGraph(3))
        True

    TESTS:

    Giving negative third value::

        sage: tolrep = [(0.1,3.3,-4.4),(1.1,2.5,1.1),(1.4,4.4,3.3)]
        sage: g = graphs.ToleranceGraph(tolrep)
        Traceback (most recent call last):
        ...
        ValueError: Invalid tolerance representation at position 0; third value must be positive!
    """
    n = len(tolrep)

    for i in range(n):
        if tolrep[i][2] <= 0:
            raise ValueError("Invalid tolerance representation at position " +
                             str(i) + "; third value must be positive!")

    g = Graph(n)

    for i in range(n - 1):
        li, ri, ti = tolrep[i]
        for j in range(i + 1, n):
            lj, rj, tj = tolrep[j]
            if min(ri, rj) - max(li, lj) >= min(ti, tj):
                g.add_edge(i, j)

    rep = dict(zip(range(n), tolrep))
    g.set_vertices(rep)

    return g
Example #30
0
def IntervalGraph(intervals, points_ordered=False):
    r"""
    Return the graph corresponding to the given intervals.

    An interval graph is built from a list `(a_i,b_i)_{1\leq i \leq n}` of
    intervals : to each interval of the list is associated one vertex, two
    vertices being adjacent if the two corresponding (closed) intervals
    intersect.

    INPUT:

    - ``intervals`` -- the list of pairs `(a_i,b_i)` defining the graph.

    - ``points_ordered`` -- states whether every interval `(a_i,b_i)` of
      `intervals` satisfies `a_i<b_i`. If satisfied then setting
      ``points_ordered`` to ``True`` will speed up the creation of the graph.

    .. NOTE::

        * The vertices are named 0, 1, 2, and so on. The intervals used
          to create the graph are saved with the graph and can be recovered
          using ``get_vertex()`` or ``get_vertices()``.

    EXAMPLES:

    The following line creates the sequence of intervals
    `(i, i+2)` for i in `[0, ..., 8]`::

        sage: intervals = [(i,i+2) for i in range(9)]

    In the corresponding graph ::

        sage: g = graphs.IntervalGraph(intervals)
        sage: g.get_vertex(3)
        (3, 5)
        sage: neigh = g.neighbors(3)
        sage: for v in neigh: print(g.get_vertex(v))
        (1, 3)
        (2, 4)
        (4, 6)
        (5, 7)

    The is_interval() method verifies that this graph is an interval graph. ::

        sage: g.is_interval()
        True

    The intervals in the list need not be distinct. ::

        sage: intervals = [ (1,2), (1,2), (1,2), (2,3), (3,4) ]
        sage: g = graphs.IntervalGraph(intervals,True)
        sage: g.clique_maximum()
        [0, 1, 2, 3]
        sage: g.get_vertices()
        {0: (1, 2), 1: (1, 2), 2: (1, 2), 3: (2, 3), 4: (3, 4)}

    The endpoints of the intervals are not ordered we get the same graph
    (except for the vertex labels). ::

        sage: rev_intervals = [ (2,1), (2,1), (2,1), (3,2), (4,3) ]
        sage: h = graphs.IntervalGraph(rev_intervals,False)
        sage: h.get_vertices()
        {0: (2, 1), 1: (2, 1), 2: (2, 1), 3: (3, 2), 4: (4, 3)}
        sage: g.edges() == h.edges()
        True
    """
    intervals = list(intervals)
    n = len(intervals)
    g = Graph(n)

    if points_ordered:
        for i in range(n - 1):
            li, ri = intervals[i]
            for j in range(i + 1, n):
                lj, rj = intervals[j]
                if ri < lj or rj < li:
                    continue
                g.add_edge(i, j)
    else:
        for i in range(n - 1):
            I = intervals[i]
            for j in range(i + 1, n):
                J = intervals[j]
                if max(I) < min(J) or max(J) < min(I):
                    continue
                g.add_edge(i, j)

    rep = dict(zip(range(n), intervals))
    g.set_vertices(rep)

    return g