예제 #1
0
 def __init__(self, ID, order, adj_matrix, line_number):
     self.ID = ID
     #TODO Fix me for string input
     self.order = order
     self.internal_graph = CompleteGraph(self.order)
     self.adj_matrix = adj_matrix
     self.line_number = line_number
예제 #2
0
 def __init__(self, ID, order, adj_matrix, line_number):
     self.ID = ID
     # TODO Fix me for string input
     self.order = order
     self.internal_graph = CompleteGraph(self.order)
     self.adj_matrix = adj_matrix
     self.line_number = line_number
예제 #3
0
def is_transversal_design(B, k, n, verbose=False):
    r"""
    Check that a given set of blocks ``B`` is a transversal design.

    See :func:`~sage.combinat.designs.orthogonal_arrays.transversal_design`
    for a definition.

    INPUT:

    - ``B`` -- the list of blocks

    - ``k, n`` -- integers

    - ``verbose`` (boolean) -- whether to display information about what is
      going wrong.

    .. NOTE::

        The tranversal design must have `\{0, \ldots, kn-1\}` as a ground set,
        partitioned as `k` sets of size `n`: `\{0, \ldots, k-1\} \sqcup
        \{k, \ldots, 2k-1\} \sqcup \cdots \sqcup \{k(n-1), \ldots, kn-1\}`.

    EXAMPLES::

        sage: TD = designs.transversal_design(5, 5, check=True) # indirect doctest
        sage: from sage.combinat.designs.orthogonal_arrays import is_transversal_design
        sage: is_transversal_design(TD, 5, 5)
        True
        sage: is_transversal_design(TD, 4, 4)
        False
    """
    from sage.graphs.generators.basic import CompleteGraph
    from itertools import combinations
    g = k * CompleteGraph(n)
    m = g.size()
    for X in B:
        if len(X) != k:
            if verbose:
                print "A set has wrong size"
            return False
        g.add_edges(list(combinations(X, 2)))
        if g.size() != m + (len(X) * (len(X) - 1)) / 2:
            if verbose:
                print "A pair appears twice"
            return False
        m = g.size()

    if not g.is_clique():
        if verbose:
            print "A pair did not appear"
        return False

    return True
예제 #4
0
class BaseGraph:
    def __init__(self, ID, order, adj_matrix, line_number):
        self.ID = ID
        # TODO Fix me for string input
        self.order = order
        self.internal_graph = CompleteGraph(self.order)
        self.adj_matrix = adj_matrix
        self.line_number = line_number

    def instantiate(self, solver, options):
        prev_vars = solver.nvars()
        solver.add_vars(self, self.internal_graph.vertices())
        solver.add_vars(self, self.internal_graph.edges(labels=False))
        solver.add_instantiate_graph_constraint(edge_to_vertex_axiom(self, solver))
        curr_vars = solver.nvars()
        solver.graph_vars.append((self, prev_vars + 1, curr_vars))

    def create_graph_from_model(self, solver, model):
        """
        Given a model from the SAT solver, construct the graph.
        """
        g = Graph()
        on_vertices = solver.get_objects_in_model(model, self, self.internal_graph.vertices())
        on_edges = solver.get_objects_in_model(model, self, self.internal_graph.edges(labels=False))
        g.add_vertices(on_vertices)
        g.add_edges(on_edges)
        return g

    def vertices(self):
        return self.internal_graph.vertices()

    def edges(self):
        return self.internal_graph.edges()

    def __str__(self):
        return (
            "graph "
            + self.ID.ID
            + "("
            + str(self.order)
            + (":" + str(self.adj_matrix) if self.adj_matrix else "")
            + ")"
        )

    def __repr__(self):
        return self.__str__()

    def toStr(self, indent):
        return common.INDENT * indent + self.__str__()
예제 #5
0
class BaseGraph():
    def __init__(self, ID, order, adj_matrix, line_number):
        self.ID = ID
        #TODO Fix me for string input
        self.order = order
        self.internal_graph = CompleteGraph(self.order)
        self.adj_matrix = adj_matrix
        self.line_number = line_number

    def instantiate(self, solver, options):
        prev_vars = solver.nvars()
        solver.add_vars(self, self.internal_graph.vertices())
        solver.add_vars(self, self.internal_graph.edges(labels=False))
        solver.add_instantiate_graph_constraint(
            edge_to_vertex_axiom(self, solver))
        curr_vars = solver.nvars()
        solver.graph_vars.append((self, prev_vars + 1, curr_vars))

    def create_graph_from_model(self, solver, model):
        '''
        Given a model from the SAT solver, construct the graph.
        '''
        g = Graph()
        on_vertices = solver.get_objects_in_model(
            model, self, self.internal_graph.vertices())
        on_edges = solver.get_objects_in_model(
            model, self, self.internal_graph.edges(labels=False))
        g.add_vertices(on_vertices)
        g.add_edges(on_edges)
        return g

    def vertices(self):
        return self.internal_graph.vertices()

    def edges(self):
        return self.internal_graph.edges()

    def __str__(self):
        return "graph " + self.ID.ID + "(" + str(self.order) + (
            ":" + str(self.adj_matrix) if self.adj_matrix else "") + ")"

    def __repr__(self):
        return self.__str__()

    def toStr(self, indent):
        return common.INDENT * indent + self.__str__()
예제 #6
0
def RandomGNP(n, p, seed=None, fast=True, method='Sage'):
    r"""
    Returns a random graph on `n` nodes. Each edge is inserted independently
    with probability `p`.

    INPUTS:

    - ``n`` -- number of nodes of the digraph

    - ``p`` -- probability of an edge

    - ``seed`` -- integer seed for random number generator (default=None).

    - ``fast`` -- boolean set to True (default) to use the algorithm with
      time complexity in `O(n+m)` proposed in [3]_. It is designed for
      generating large sparse graphs. It is faster than other methods for
      *LARGE* instances (try it to know whether it is useful for you).

    - ``method`` -- By default (```method='Sage'``), this function uses the
      method implemented in ```sage.graphs.graph_generators_pyx.pyx``. When
      ``method='networkx'``, this function calls the NetworkX function
      ``fast_gnp_random_graph``, unless ``fast=False``, then
      ``gnp_random_graph``. Try them to know which method is the best for
      you. The ``fast`` parameter is not taken into account by the 'Sage'
      method so far.

    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).

    .. [3] V. Batagelj and U. Brandes. Efficient generation of large
           random networks. Phys. Rev. E, 71, 036113, 2005.

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

    EXAMPLES: We show the edge list of a random graph on 6 nodes with
    probability `p = .4`::

        sage: set_random_seed(0)
        sage: graphs.RandomGNP(6, .4).edges(labels=False)
        [(0, 1), (0, 5), (1, 2), (2, 4), (3, 4), (3, 5), (4, 5)]

    We plot a random graph on 12 nodes with probability
    `p = .71`::

        sage: gnp = graphs.RandomGNP(12,.71)
        sage: gnp.show() # long time

    We view many random graphs using a graphics array::

        sage: g = []
        sage: j = []
        sage: for i in range(9):
        ...    k = graphs.RandomGNP(i+3,.43)
        ...    g.append(k)
        ...
        sage: for i in range(3):
        ...    n = []
        ...    for m in range(3):
        ...        n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
        ...    j.append(n)
        ...
        sage: G = sage.plot.graphics.GraphicsArray(j)
        sage: G.show() # long time
        sage: graphs.RandomGNP(4,1)
        Complete graph: Graph on 4 vertices

    TESTS::

        sage: graphs.RandomGNP(50,.2,method=50)
        Traceback (most recent call last):
        ...
        ValueError: 'method' must be equal to 'networkx' or to 'Sage'.
        sage: set_random_seed(0)
        sage: graphs.RandomGNP(50,.2, method="Sage").size()
        243
        sage: graphs.RandomGNP(50,.2, method="networkx").size()
        258
    """
    if n < 0:
        raise ValueError("The number of nodes must be positive or null.")
    if 0.0 > p or 1.0 < p:
        raise ValueError("The probability p must be in [0..1].")

    if seed is None:
        seed = current_randstate().long_seed()
    if p == 1:
        from sage.graphs.generators.basic import CompleteGraph
        return CompleteGraph(n)

    if method == 'networkx':
        import networkx
        if fast:
            G = networkx.fast_gnp_random_graph(n, p, seed=seed)
        else:
            G = networkx.gnp_random_graph(n, p, seed=seed)
        return graph.Graph(G)
    elif method in ['Sage', 'sage']:
        # We use the Sage generator
        from sage.graphs.graph_generators_pyx import RandomGNP as sageGNP
        return sageGNP(n, p)
    else:
        raise ValueError("'method' must be equal to 'networkx' or to 'Sage'.")
예제 #7
0
def root_graph(g, verbose=False):
    r"""
    Computes the root graph corresponding to the given graph

    See the documentation of :mod:`sage.graphs.line_graph` to know how it works.

    INPUT:

    - ``g`` -- a graph

    - ``verbose`` (boolean) -- display some information about what is happening
      inside of the algorithm.

    .. NOTE::

        It is best to use this code through
        :meth:`~sage.graphs.graph.Graph.is_line_graph`, which first checks that
        the graph is indeed a line graph, and deals with the disconnected
        case. But if you are sure of yourself, dig in !

    .. WARNING::

        * This code assumes that the graph is connected.

        * If the graph is *not* a line graph, this implementation will take a
          loooooong time to run. Its first step is to enumerate all maximal
          cliques, and that can take a while for general graphs. As soon as
          there is a way to iterate over maximal cliques without first building
          the (long) list of them this implementation can be updated, and will
          deal reasonably with non-line graphs too !

    TESTS:

    All connected graphs on 6 vertices::

        sage: from sage.graphs.line_graph import root_graph
        sage: def test(g):
        ...      gl = g.line_graph(labels = False)
        ...      d=root_graph(gl)
        sage: for i,g in enumerate(graphs(6)): # long time
        ...     if not g.is_connected():       # long time
        ...       continue                     # long time
        ...     test(g)                        # long time

    Non line-graphs::

        sage: root_graph(graphs.PetersenGraph())
        Traceback (most recent call last):
        ...
        ValueError: This graph is not a line graph !

    Small corner-cases::

        sage: from sage.graphs.line_graph import root_graph
        sage: root_graph(graphs.CompleteGraph(3))
        (Complete bipartite graph: Graph on 4 vertices, {0: (0, 1), 1: (0, 2), 2: (0, 3)})
        sage: root_graph(graphs.OctahedralGraph())
        (Complete graph: Graph on 4 vertices, {0: (0, 1), 1: (0, 2), 2: (0, 3), 3: (1, 2), 4: (1, 3), 5: (2, 3)})
        sage: root_graph(graphs.DiamondGraph())
        (Graph on 4 vertices, {0: (0, 3), 1: (0, 1), 2: (0, 2), 3: (1, 2)})
        sage: root_graph(graphs.WheelGraph(5))
        (Diamond Graph: Graph on 4 vertices, {0: (1, 2), 1: (0, 1), 2: (0, 2), 3: (2, 3), 4: (1, 3)})
    """
    from sage.graphs.digraph import DiGraph

    if isinstance(g, DiGraph):
        raise ValueError("g cannot be a DiGraph !")
    if g.has_multiple_edges():
        raise ValueError("g cannot have multiple edges !")
    if not g.is_connected():
        raise ValueError("g is not connected !")

    # Complete Graph ?
    if g.is_clique():
        from sage.graphs.generators.basic import CompleteBipartiteGraph
        return (CompleteBipartiteGraph(1, g.order()),
                {v: (0, 1 + i)
                 for i, v in enumerate(g)})

    # Diamond Graph ?
    elif g.order() == 4 and g.size() == 5:
        from sage.graphs.graph import Graph
        root = Graph([(0, 1), (1, 2), (2, 0), (0, 3)])
        return (root,
                g.is_isomorphic(root.line_graph(labels=False),
                                certify=True)[1])

    # Wheel on 5 vertices ?
    elif g.order() == 5 and g.size() == 8 and min(g.degree()) == 3:
        from sage.graphs.generators.basic import DiamondGraph
        root = DiamondGraph()
        return (root,
                g.is_isomorphic(root.line_graph(labels=False),
                                certify=True)[1])

    # Octahedron ?
    elif g.order() == 6 and g.size() == 12 and g.is_regular(k=4):
        from sage.graphs.generators.platonic_solids import OctahedralGraph
        if g.is_isomorphic(OctahedralGraph()):
            from sage.graphs.generators.basic import CompleteGraph
            root = CompleteGraph(4)
            return (root,
                    g.is_isomorphic(root.line_graph(labels=False),
                                    certify=True)[1])

    # From now on we can assume (thanks to Beineke) that no edge belongs to two
    # even triangles at once.

    error_message = ("It looks like there is a problem somewhere. You"
                     "found a bug here ! Please report it on sage-devel,"
                     "our google group !")

    # Better to work on integers... Everything takes more time
    # otherwise.
    G = g.relabel(inplace=False)

    # Dictionary of (pairs of) cliques, i.e. the two cliques
    # associated with each vertex.
    v_cliques = {v: [] for v in G}

    # All the even triangles we meet
    even_triangles = []

    # Here is THE "problem" of this implementation. Listing all maximal cliques
    # takes an exponential time on general graphs (while it is obviously
    # polynomial on line graphs). The problem is that this implementation cannot
    # be used to *recognise* line graphs for as long as cliques_maximal returns
    # a list and does not ITERATE on the maximal cliques : if there are too many
    # cliques in the graph, this implementation will notice it and answer that
    # the graph is not a line graph. If, on the other hand, the first thing it
    # does is enumerate ALL maximal cliques, then there is no way to say early
    # that the graph is not a line graph.
    #
    # If this cliques_maximal thing is replaced by an iterator that does not
    # build the list of all cliques before returning them, then this method is a
    # good recognition algorithm.

    for S in G.cliques_maximal():

        # Triangles... even or odd ?
        if len(S) == 3:

            # If a vertex of G has an odd number of neighbors among the vertices
            # of S, then the triangle is odd. We compute the list of such
            # vertices by taking the symmetric difference of the neighborhood of
            # our three vertices.
            #
            # Note that the elements of S do not appear in this set as they are
            # all seen exactly twice.

            odd_neighbors = set(G.neighbors(S[0]))
            odd_neighbors.symmetric_difference_update(G.neighbors(S[1]))
            odd_neighbors.symmetric_difference_update(G.neighbors(S[2]))

            # Even triangles
            if not odd_neighbors:
                even_triangles.append(tuple(S))
                continue

            # We manage odd triangles the same way we manage other cliques ...

        # We now associate the clique to all the vertices it contains.
        for v in S:
            if len(v_cliques[v]) == 2:
                raise ValueError("This graph is not a line graph !")
            v_cliques[v].append(tuple(S))

        if verbose:
            print("Added clique", S)

    # Deal with even triangles
    for u, v, w in even_triangles:

        # According to Beineke, we must go through all even triangles, and for
        # each triangle uvw consider its three pairs of adjacent verties uv, vw,
        # wu. For all pairs xy among those such that xy do not appear together
        # in any clique we have found so far, we add xy to the list of cliques
        # describing our covering.

        for x, y in [(u, v), (v, w), (w, u)]:

            # If edge xy does not appear in any of the cliques associated with y
            if all([not x in C for C in v_cliques[y]]):
                if len(v_cliques[y]) >= 2 or len(v_cliques[x]) >= 2:
                    raise ValueError("This graph is not a line graph !")

                v_cliques[x].append((x, y))
                v_cliques[y].append((x, y))

                if verbose:
                    print("Adding pair", (x, y),
                          "appearing in the even triangle", (u, v, w))

    # Deal with vertices contained in only one clique. All edges must be defined
    # by TWO endpoints, so we add a fake clique.
    for x, clique_list in v_cliques.iteritems():
        if len(clique_list) == 1:
            clique_list.append((x, ))

    # We now have all our cliques. Let's build the root graph to check that it
    # all fits !
    from sage.graphs.graph import Graph
    R = Graph()

    # Associates an integer to each clique
    relabel = {}

    # Associates to each vertex of G its pair of coordinates in R
    vertex_to_map = {}

    for v, L in v_cliques.iteritems():

        # Add cliques to relabel dictionary
        for S in L:
            if not S in relabel:
                relabel[S] = len(relabel)

        # The coordinates of edge v
        vertex_to_map[v] = relabel[L[0]], relabel[L[1]]

    if verbose:
        print("Final associations :")
        for v, L in v_cliques.iteritems():
            print(v, L)

    # We now build R
    R.add_edges(vertex_to_map.values())

    # Even if whatever is written above is complete nonsense, here we
    # make sure that we do not return gibberish. Is the line graph of
    # R isomorphic to the input ? If so, we return R, and the
    # isomorphism. Else, we panic and scream.
    #
    # It's actually "just to make sure twice". This can be removed later if it
    # turns out to be too costly.
    is_isom, isom = g.is_isomorphic(R.line_graph(labels=False), certify=True)

    if not is_isom:
        raise Exception(error_message)

    return R, isom