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 __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 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
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__()
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__()
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'.")
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