def _gnx_vec(self, gnx: nx.Graph, node_order): final_vec = [] if self._deg: degrees = gnx.degree(gnx.nodes) final_vec.append( np.matrix([np.log(degrees[d] + 1e-3) for d in node_order]).T) if self._in_deg and gnx.is_directed(): degrees = gnx.in_degree(gnx.nodes) final_vec.append( np.matrix([np.log(degrees[d] + 1e-3) for d in node_order]).T) if self._out_deg and gnx.is_directed(): degrees = gnx.out_degree(gnx.nodes) final_vec.append( np.matrix([np.log(degrees[d] + 1e-3) for d in node_order]).T) # TODO ========================== TODO handle external features # if self._is_external_data and self._external_data.is_value: # final_vec.append(np.matrix([self._external_data.value_feature(gnx_id, d) for d in node_order])) # TODO ========================== TODO ======================== if self._is_ftr: gnx_dir_path = os.path.join(self._gnx_features_pkl_dir) if not os.path.exists(gnx_dir_path): os.mkdir(gnx_dir_path) raw_ftr = GraphFeatures(gnx, self._ftr_meta, dir_path=gnx_dir_path, is_max_connected=False, logger=PrintLogger("logger")) raw_ftr.build(should_dump=True) # build features final_vec.append( FeaturesProcessor(raw_ftr).as_matrix(norm_func=log_norm)) return np.hstack(final_vec)
def erdos_renyi_like(G: nx.Graph): number_of_nodes = G.number_of_nodes() possible_connections = number_of_nodes * (number_of_nodes - 1) if not G.is_directed(): possible_connections /= 2 probability_of_connection = G.number_of_edges() / possible_connections G_erdos_renyi = nx.gnp_random_graph(number_of_nodes, probability_of_connection, directed=G.is_directed()) return (G_erdos_renyi)
def _get_shortest_path_between_subgraphs_helper( graph: nx.Graph, a: nx.Graph, b: nx.Graph, ) -> List[List[Any]]: """Calculate the shortest path(s) between disconnected sub-graphs ``a`` and ``b`` through ``graph``. :param graph: A graph :param a: A sub-graph of :code:`graph`, disjoint from :code:`b` :param b: A sub-graph of :code:`graph`, disjoint from :code:`a` :return: A list of the shortest paths between the two sub-graphs """ if graph.is_directed(): shortest_paths = [ shortest_path for na, nb in itt.product(a, b) for shortest_path in ( # do it going both ways because it's directed nx.shortest_path(graph, na, nb), nx.shortest_path(graph, nb, na), ) ] else: shortest_paths = [ nx.shortest_path(graph, na, nb) for na, nb in itt.product(a, b) ] min_len = min(map(len, shortest_paths)) return [ shortest_path for shortest_path in shortest_paths if len(shortest_path) == min_len ]
def convert_graph_to_db_dict(graph: nx.Graph, with_weights=False, cast_to_directed=False): """ Encapsulates the convert_graph_to_db_format function by wrapping the returned lists with a dictionary. The dictionary has the keys 'indices','neighbors' , ['weights']. :param graph: the nx graph to convert :param with_weights: whether to return a weights list :param cast_to_directed: whether to process the graph as a directed one. :return: a dictionary with the specified keys and the lists as values. """ ret_dict = {} if with_weights: i, n, w = convert_graph_to_db_format(graph, with_weights, cast_to_directed) ret_dict['indices'] = i ret_dict['neighbors'] = n ret_dict['weights'] = w ret_dict['with_weights'] = True else: i, n = convert_graph_to_db_format(graph, with_weights, cast_to_directed) ret_dict['indices'] = i ret_dict['neighbors'] = n ret_dict['with_weights'] = False ret_dict['directed'] = graph.is_directed() return ret_dict
def remove_edges_randomly(graph: nx.Graph, *, frac=None, number=None, keep_connected=False): """ Removes edges randomly from the given graph. You must either give `frac` or `number` to specify how many edges should be removed. The parameter `keep_connected` can be used to ensure that all nodes remain connected. Some embedding algorithms (DAOR) lead to subsequent problems in later tasks otherwise. A set of all edges that were removed is returned. The graph is altered in place. """ number = number_random_edges(graph, frac=frac, number=number) connected = nx.is_weakly_connected if graph.is_directed() else nx.is_connected if keep_connected and not connected(graph): raise RuntimeError("The graph is already not connected before removing any edges.") removed_edges = set() infeasible_edges = set() while len(removed_edges) < number: to_remove = sample(graph.edges - infeasible_edges, number - len(removed_edges)) for edge in to_remove: edge_data = graph[edge[0]][edge[1]] graph.remove_edge(*edge) if keep_connected and not connected(graph): # The removed edge made the graph disconnected, add the edge # again and re-sample a new edge as replacement later. graph.add_edge(*edge, **edge_data) infeasible_edges.add(edge) else: removed_edges.add(edge) return removed_edges
def _convert_gnx_to_int_based(self, gnx: nx.Graph): nodes = {node: i for i, node in enumerate(gnx.nodes)} new_gnx = nx.DiGraph() if gnx.is_directed() else nx.Graph() for u, v in gnx.edges: new_gnx.add_edge(nodes[u], nodes[v]) return new_gnx, [ n for n, i in sorted(nodes.items(), key=lambda x: x[1]) ]
def assert_is_undirected(graph: nx.Graph): """ Asserts that an object is an undirected graph :param graph: Graph to check :raises ValueError: If a graph is not an undirected graph """ if graph.is_directed(): raise ValueError("graph must be an undirected graph")
def graph_info(g: nx.Graph, display_using: Callable = print) -> None: """Display basic information about a graph. Note: the connected component structure is calculated using the undirected equivalent of the graph. :param g: networkx Graph :param display_using: function to use to display """ graph_type = repr(type(g)) info = f'{graph_type}\n' info += '-' * len(graph_type) + '\n' info += f'Nodes, Edges\t\t{g.number_of_nodes()}, {g.number_of_edges()}\n' is_directed = g.is_directed() info += f'Directed\t\t{is_directed}\n' connected_components = sorted(nx.connected_components(g.to_undirected()), key=len) n_connected_components = len(connected_components) is_connected = n_connected_components == 1 info += f'Connected\t\t{is_connected}\n' if not is_connected: info += f'Number of components\t{n_connected_components}\n' info += f'Max component size\t{len(max(connected_components, key=len))}\n' adj_mx = nx.to_scipy_sparse_matrix(g) if is_directed: out_degree = adj_mx.sum(axis=1) info += f'Degree range (out)\t{out_degree.min()} to {out_degree.max()}\n' in_degree = adj_mx.sum(axis=0) info += f'Degree range (in)\t{in_degree.min()} to {in_degree.max()}\n' else: degree = adj_mx.sum(axis=0) info += f'Degree range\t\t{degree.min()} to {degree.max()}\n' node_label_types = repr(list(set(map(type, g.nodes)))) info += f'Node label types\t{node_label_types}\n' for u in g.nodes: node_attributes = repr(list(g.nodes[u])) info += f'Node attributes\t\t{node_attributes}\n' break for u, v in g.edges: edge_attributes = repr(list(g[u][v])) info += f'Edge attributes\t\t{edge_attributes}' break display_using(info)
def draw_graph(gnx: nx.Graph): pos = nx.layout.spring_layout(gnx) nx.draw_networkx_nodes(gnx, pos) if gnx.is_directed(): nx.draw_networkx_edges(gnx, pos, arrowstyle='->', arrowsize=30) else: nx.draw_networkx_edges(gnx, pos) nx.draw_networkx_labels(gnx, pos, font_size=10, font_family='sans-serif') plt.axis('off') plt.show()
def largest_connected_component(G: nx.Graph, connection='weak'): if G.is_directed(): if connection == 'weak': largest_connected_component_nodes =\ max(nx.weakly_connected_components(G), key=len) elif connection == 'strong': largest_connected_component_nodes = \ max(nx.strongly_connected_components(G), key=len) else: raise Exception('Invalid connection: "' + connection + '".') else: largest_connected_component_nodes = \ max(nx.connected_components(G), key=len) return G.subgraph(largest_connected_component_nodes).copy()
def get_distinct_cliques(G: nx.Graph, n=None): if G.is_directed(): G = G.to_undirected() cliques = sorted(list(nx.find_cliques(G)), key=lambda c: len(c), reverse=True) nodes = set() distinct_cliques = [] for c in cliques: if n is not None and len(distinct_cliques) >= n: break if len(nodes.intersection(set(c))) == 0: nodes = nodes.union(set(c)) distinct_cliques.append(c) return distinct_cliques
def reeb_process(gr: nx.Graph) -> Sequence[Tuple[int, int, int, int]]: # """ # Convert the Reeb graph from NetworkX object to the preferred representation # for the Chinese postman algorithm. # Format: # [(crit pt on left, crit pt on right, cell #, pixels in cell), ...] # """ assert gr.is_directed() and gr.is_multigraph(), "Wrong kind of graph" out = [] for tail, head, data in gr.edges(data=True): out.append((tail, head, data["cell"], data["weight"])) return out
def _nx_to_edge_list( graph: nx.Graph, identifier: _IdentityMapper, is_weighted: Optional[bool], weight_attribute: str, weight_default: float, ) -> Tuple[int, List[Tuple[str, str, float]]]: check_argument( isinstance(graph, nx.Graph) and not (graph.is_directed() or graph.is_multigraph()), "Only undirected non-multi-graph networkx graphs are supported", ) native_safe: List[Tuple[str, str, float]] = [] edge_iter = (graph.edges(data=weight_attribute) if is_weighted is True else graph.edges(data=weight_attribute, default=weight_default)) for source, target, weight in edge_iter: source_str = identifier(source) target_str = identifier(target) native_safe.append((source_str, target_str, float(weight))) return graph.number_of_nodes(), native_safe
def _assertions( graph: nx.Graph, partitions: Dict[Any, int], weight_attribute: str, resolution: float, ) -> None: if not isinstance(graph, nx.Graph): raise TypeError("graph must be a networkx undirected graph") if graph.is_directed(): raise ValueError("The graph must be an undirected graph") if graph.is_multigraph(): raise ValueError( "Multigraphs must be provided in the form of a non multigraph.") if not nx.is_weighted(graph, weight=weight_attribute): raise ValueError( f"weight_attribute {weight_attribute} not found on every edge in the provided graph" ) if not isinstance(partitions, dict): raise TypeError("partitions must be a dictionary") if not isinstance(resolution, float): raise TypeError("resolution must be a float")
def get_dead_ends(graph: nx.Graph, node_filter=no_filter, only_strict=False) -> list: """Return the list of dead end in the given graph. A dead end is defined as a node having only one neighbor. For directed graphs, a strict dead end is a node having a unique predecessor and no successors. A weak dead end is a node having a unique predecessor that is also its unique successor. Parameters ---------- graph : nx.Graph Graph to parse. node_filter : Evaluates to true if a node can be considered as dead end, false otherwise. (Default value = no_filter) only_strict : If true, remove only strict dead ends. Used only for directed graphs. (Default value = False) Returns ------- list List of node name that are dead ends. """ if graph.is_directed(): dead_ends = [] for n in graph.nodes(): if not node_filter(n): continue nb_predecessors = len(graph.pred[n]) if nb_predecessors != 1: continue nb_successors = len(graph.succ[n]) if nb_successors == 0: dead_ends.append(n) elif not only_strict and nb_successors == 1: pred = next(iter(graph.pred[n])) if pred in graph.successors(n): dead_ends.append(n) return dead_ends else: return [n for n in graph.nodes() if node_filter(n) and len(graph.neighbors(n)) == 1]
def sample_negative_edges(graph: nx.Graph, *, frac=None, number=None, exclude=None): number = number_random_edges(graph, frac=frac, number=number) if exclude is None: exclude = set() negative_edges = set() while len(negative_edges) < number: us = choices(list(graph.nodes), k=number - len(negative_edges)) vs = choices(list(graph.nodes), k=number - len(negative_edges)) for u, v in zip(us, vs): if u == v: # we don't want to generate self-loops continue if v < u and not graph.is_directed(): # for undirected graphs don't consider edges in both directions continue if graph.has_edge(u, v): continue if (u, v) in exclude: continue negative_edges.add((u, v)) return negative_edges
def clustering_coefficient(g: nx.Graph): if g.is_directed(): print("clustering coefficient not implemented for directed graphs") else: print(f"clustering coefficient: {str(nx.average_clustering(g))}")
class NetworkXGraphBackend(GenericGraphBackend): """ A wrapper for NetworkX as the backend of a graph. TESTS:: sage: import sage.graphs.base.graph_backends """ _nxg = None def __init__(self, N=None): """ Initialize the backend with NetworkX graph N. TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.iterator_edges([],True) <generator object iterator_edges at ...> """ if N is None: import networkx N = networkx.MultiGraph() self._nxg = N try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() def add_edge(self, u, v, l, directed): """ Add an edge (u,v) to self, with label l. If directed is True, this is interpreted as an arc from u to v. INPUT: - ``u,v`` -- vertices - ``l`` -- edge label - ``directed`` -- boolean TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.add_edge(1,2,'a',True) """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() if u is None: u = self.add_vertex(None) if v is None: v = self.add_vertex(None) if l: self._nxg.add_edge(u, v, weight=l) else: self._nxg.add_edge(u, v) def add_edges(self, edges, directed): """ Add a sequence of edges to self. If directed is True, these are interpreted as arcs. INPUT: - ``edges`` -- list/iterator of edges to be added. - ``directed`` -- boolean TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.add_edges([],True) """ for e in edges: try: u, v, l = e except ValueError: u, v = e l = None self.add_edge(u, v, l, directed) def add_vertex(self, name): """ Add a labelled vertex to self. INPUT: - ``name``: vertex label OUTPUT: If ``name=None``, the new vertex name is returned. ``None`` otherwise. TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.add_vertex(0) """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() retval = None if name is None: # then find an integer to use as a key i = 0 while self.has_vertex(i): i = i + 1 name = i retval = name self._nxg.add_node(name) return retval def add_vertices(self, vertices): """ Add labelled vertices to self. INPUT: - ``vertices``: iterator of vertex labels. A new label is created, used and returned in the output list for all ``None`` values in ``vertices``. OUTPUT: Generated names of new vertices if there is at least one ``None`` value present in ``vertices``. ``None`` otherwise. EXAMPLES:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.add_vertices([1,2,3]) sage: G.add_vertices([4,None,None,5]) [0, 6] """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() vertices = list(vertices) nones = vertices.count(None) vertices = [v for v in vertices if v is not None] self._nxg.add_nodes_from(vertices) new_names = [] i = 0 while nones > 0: while self.has_vertex(i): i += 1 self._nxg.add_node(i) new_names.append(i) nones -= 1 i += 1 return new_names if new_names != [] else None def degree(self, v, directed): """ Returns the total number of vertices incident to v. INPUT: - ``v`` -- a vertex label - ``directed`` -- boolean OUTPUT: degree of v TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.add_vertices(range(3)) sage: G.degree(1, False) 0 """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() return self._nxg.degree(v) def del_edge(self, u, v, l, directed): """ Deletes the edge (u,v) with label l. INPUT: - ``u,v`` -- vertices - ``l`` -- edge label - ``directed`` -- boolean TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.del_edge(1,2,'a',True) """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() import networkx try: if self._nxg.is_multigraph(): for k, d in self._nxg.edge[u][v].iteritems(): if d.get('weight', None) == l: self._nxg.remove_edge(u, v, k) break else: if l is None or self._nxg.edge[u][v].get('weight', None) == l: self._nxg.remove_edge(u, v) except (KeyError, networkx.NetworkXError): pass def del_vertex(self, v): """ Delete a labelled vertex in self. INPUT: - ``v`` -- vertex label TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.del_vertex(0) Traceback (most recent call last): ... NetworkXError: The node 0 is not in the graph. """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() self._nxg.remove_node(v) def del_vertices(self, vertices): """ Delete labelled vertices in self. INPUT: - ``vertices`` -- iterator of vertex labels TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.del_vertices([1,2,3]) Traceback (most recent call last): ... NetworkXError: The node 1 is not in the graph. """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() for v in vertices: self._nxg.remove_node(v) def get_edge_label(self, u, v): """ Returns the edge label of (u,v). INPUT: - ``u,v`` -- vertex labels OUTPUT: label of (u,v) TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.get_edge_label(1,2) Traceback (most recent call last): ... NetworkXError: Edge (1,2) requested via get_edge_label does not exist. """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() try: E = self._nxg.edge[u][v] except KeyError: from networkx import NetworkXError raise NetworkXError( "Edge (%s,%s) requested via get_edge_label does not exist." % (u, v)) if self._nxg.is_multigraph(): return [e.get('weight', None) for e in E.itervalues()] else: return E.get('weight', None) def has_edge(self, u, v, l): """ True if self has an edge (u,v) with label l. INPUT: - ``u,v`` -- vertex labels - ``l`` -- label OUTPUT: boolean TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.has_edge(1,2,'a') False """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() if not self._nxg.has_edge(u, v): return False if l is None: return True if self._nxg.is_multigraph(): return any( e.get('weight', None) == l for e in self._nxg.adj[u][v].itervalues()) else: return any(e == l for e in self._nxg.adj[u][v].itervalues()) def has_vertex(self, v): """ True if self has a vertex with label v. INPUT: - ``v`` -- vertex label OUTPUT: boolean TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.has_vertex(0) False """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() return self._nxg.has_node(v) def iterator_edges(self, vertices, labels): """ Iterate over the edges incident to a sequence of vertices. Edges are assumed to be undirected. INPUT: - ``vertices`` -- a list of vertex labels - ``labels`` -- boolean OUTPUT: a generator which yields edges, with or without labels depending on the labels parameter. TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.iterator_edges([],True) <generator object iterator_edges at ...> """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() if labels: for u, v, d in self._nxg.edges_iter(data=True): if u in vertices or v in vertices: yield (u, v, d.get('weight', None)) else: for u, v in self._nxg.edges_iter(): if u in vertices or v in vertices: yield (u, v) def _iterator_in_edges_with_labels(self, vertices): """ Iterate over the incoming edges incident to a sequence of vertices. Special case, only for internal use. EXAMPLE:: sage: g = DiGraph(graphs.PetersenGraph(), implementation="networkx")._backend sage: sorted(list(g.iterator_in_edges([0,1], True))) [(0, 1, None), (1, 0, None), (2, 1, None), (4, 0, None), (5, 0, None), (6, 1, None)] """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() for u, v, d in self._nxg.in_edges_iter(vertices, data=True): yield (u, v, d.get('weight', None)) def iterator_in_edges(self, vertices, labels): """ Iterate over the incoming edges incident to a sequence of vertices. INPUT: - ``vertices`` -- a list of vertex labels - ``labels`` -- boolean OUTPUT: a generator which yields edges, with or without labels depending on the labels parameter. TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: i = G.iterator_in_edges([],True) """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() if self._nxg.is_directed(): if labels: return self._iterator_in_edges_with_labels(vertices) else: return self._nxg.in_edges_iter(vertices) else: return self.iterator_edges(vertices, labels) def _iterator_out_edges_with_labels(self, vertices): """ Iterate over the outbound edges incident to a sequence of vertices. Special case, only for internal use. EXAMPLE:: sage: g = DiGraph(graphs.PetersenGraph(), implementation="networkx")._backend sage: sorted(list(g.iterator_out_edges([0,1], True))) [(0, 1, None), (0, 4, None), (0, 5, None), (1, 0, None), (1, 2, None), (1, 6, None)] """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() for u, v, d in self._nxg.out_edges_iter(vertices, data=True): yield (u, v, d.get('weight', None)) def iterator_out_edges(self, vertices, labels): """ Iterate over the outbound edges incident to a sequence of vertices. INPUT: - ``vertices`` -- a list of vertex labels - ``labels`` -- boolean OUTPUT: a generator which yields edges, with or without labels depending on the labels parameter. TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: i = G.iterator_out_edges([],True) """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() if self._nxg.is_directed(): if labels: return self._iterator_out_edges_with_labels(vertices) else: return self._nxg.out_edges_iter(vertices) else: return self.iterator_edges(vertices, labels) def iterator_nbrs(self, v): """ Iterate over the vertices adjacent to v. INPUT: - ``v`` -- vertex label OUTPUT: a generator which yields vertex labels TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.add_vertex(0) sage: G.iterator_nbrs(0) <dictionary-keyiterator object at ...> """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() return self._nxg.neighbors_iter(v) def iterator_in_nbrs(self, v): """ Iterate over the vertices u such that the edge (u,v) is in self (that is, predecessors of v). INPUT: - ``v`` -- vertex label OUTPUT: a generator which yields vertex labels TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.iterator_in_nbrs(0) Traceback (most recent call last): ... AttributeError: 'MultiGraph' object has no attribute 'predecessors_iter' """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() return self._nxg.predecessors_iter(v) def iterator_out_nbrs(self, v): """ Iterate over the vertices u such that the edge (v,u) is in self (that is, successors of v). INPUT: - ``v`` -- vertex label OUTPUT: a generator which yields vertex labels TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.iterator_out_nbrs(0) Traceback (most recent call last): ... AttributeError: 'MultiGraph' object has no attribute 'successors_iter' """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() return self._nxg.successors_iter(v) def iterator_verts(self, verts): """ Iterate over the vertices v with labels in verts. INPUT: - ``vertex`` -- vertex labels OUTPUT: a generator which yields vertices TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.iterator_verts(0) <generator object bunch_iter at ...> """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() return self._nxg.nbunch_iter(verts) def loops(self, new=None): """ Get/set whether or not self allows loops. INPUT: - ``new`` -- can be a boolean (in which case it sets the value) or ``None``, in which case the current value is returned. It is set to ``None`` by default. TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.loops(True) sage: G.loops(None) True """ if new is None: return self._loops if new: self._loops = True else: self._loops = False def multiple_edges(self, new=None): """ Get/set whether or not self allows multiple edges. INPUT: - ``new`` -- can be a boolean (in which case it sets the value) or ``None``, in which case the current value is returned. It is set to ``None`` by default. TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.multiple_edges(True) sage: G.multiple_edges(None) True """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() from networkx import Graph, MultiGraph, DiGraph, MultiDiGraph if new is None: return self._nxg.is_multigraph() if new == self._nxg.is_multigraph(): return if new: if self._nxg.is_directed(): self._nxg = MultiDiGraph(self._nxg) else: self._nxg = MultiGraph(self._nxg) else: if self._nxg.is_directed(): self._nxg = DiGraph(self._nxg) else: self._nxg = Graph(self._nxg) def name(self, new=None): """ Get/set name of self. INPUT: - ``new`` -- can be a string (in which case it sets the value) or ``None``, in which case the current value is returned. It is set to ``None`` by default. TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.name("A NetworkX Graph") sage: G.name(None) 'A NetworkX Graph' """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() if new is None: return self._nxg.name self._nxg.name = new def num_edges(self, directed): """ The number of edges in self INPUT: - ``directed`` -- boolean TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.num_edges(True) 0 sage: G.num_edges(False) 0 """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() return self._nxg.size() def num_verts(self): """ The number of vertices in self TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.num_verts() 0 """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() return self._nxg.order() def relabel(self, perm, directed): """ Relabel the vertices of self by a permutation. INPUT: - ``perm`` -- permutation - ``directed`` -- boolean TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.relabel([],False) """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() from networkx import relabel_nodes name = self._nxg.name self._nxg = relabel_nodes(self._nxg, perm) self._nxg.name = name def set_edge_label(self, u, v, l, directed): """ Label the edge (u,v) by l. INPUT: - ``u,v`` -- vertices - ``l`` -- edge label - ``directed`` -- boolean TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.set_edge_label(1,2,'a',True) """ try: assert (not isinstance( self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated))) except AssertionError: self._nxg = self._nxg.mutate() if not self.has_edge(u, v, None): return if self.multiple_edges(None): self._nxg[u][v].clear() self._nxg[u][v][0] = dict(weight=l) if directed is False: self._nxg[v][u].clear() self._nxg[v][u][0] = dict(weight=l) else: self._nxg[u][v]['weight'] = l if directed is False: self._nxg[v][u]['weight'] = l
class NetworkXGraphBackend(GenericGraphBackend): """ A wrapper for NetworkX as the backend of a graph. TESTS:: sage: import sage.graphs.base.graph_backends """ _nxg = None def __init__(self, N=None): """ Initialize the backend with NetworkX graph N. TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.iterator_edges([],True) <generator object iterator_edges at ...> """ if N is None: import networkx N = networkx.MultiGraph() self._nxg = N if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() def add_edge(self, u, v, l, directed): """ Add an edge (u,v) to self, with label l. If directed is True, this is interpreted as an arc from u to v. INPUT: - ``u,v`` -- vertices - ``l`` -- edge label - ``directed`` -- boolean TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.add_edge(1,2,'a',True) """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() if u is None: u = self.add_vertex(None) if v is None: v = self.add_vertex(None) if l: self._nxg.add_edge(u, v, weight=l) else: self._nxg.add_edge(u, v) def add_edges(self, edges, directed): """ Add a sequence of edges to self. If directed is True, these are interpreted as arcs. INPUT: - ``edges`` -- list/iterator of edges to be added. - ``directed`` -- boolean TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.add_edges([],True) """ for e in edges: try: u,v,l = e except ValueError: u,v = e l = None self.add_edge(u,v,l,directed) def add_vertex(self, name): """ Add a labelled vertex to self. INPUT: - ``name``: vertex label OUTPUT: If ``name=None``, the new vertex name is returned. ``None`` otherwise. TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.add_vertex(0) """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() retval = None if name is None: # then find an integer to use as a key i = 0 while self.has_vertex(i): i=i+1 name = i retval = name self._nxg.add_node(name) return retval def add_vertices(self, vertices): """ Add labelled vertices to self. INPUT: - ``vertices``: iterator of vertex labels. A new label is created, used and returned in the output list for all ``None`` values in ``vertices``. OUTPUT: Generated names of new vertices if there is at least one ``None`` value present in ``vertices``. ``None`` otherwise. EXAMPLES:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.add_vertices([1,2,3]) sage: G.add_vertices([4,None,None,5]) [0, 6] """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() vertices = list(vertices) nones = vertices.count(None) vertices = [v for v in vertices if v is not None] self._nxg.add_nodes_from(vertices) new_names = [] i = 0 while nones > 0: while self.has_vertex(i): i += 1 self._nxg.add_node(i) new_names.append(i) nones -= 1 i += 1 return new_names if new_names != [] else None def degree(self, v, directed): """ Returns the total number of vertices incident to v. INPUT: - ``v`` -- a vertex label - ``directed`` -- boolean OUTPUT: degree of v TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.add_vertices(range(3)) sage: G.degree(1, False) 0 """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() return self._nxg.degree(v) def in_degree(self, v): """ Returns the in-degree of v. INPUT: - ``v`` -- a vertex label OUTPUT: degree of v TESTS:: sage: G = DiGraph(digraphs.Path(5),implementation="networkx") sage: G = G._backend sage: G.in_degree(0) 0 sage: G.in_degree(4) 1 """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() return self._nxg.in_degree(v) def out_degree(self, v): """ Returns the out-degree of v. INPUT: - ``v`` -- a vertex label OUTPUT: degree of v TESTS:: sage: G = DiGraph(digraphs.Path(5),implementation="networkx") sage: G = G._backend sage: G.out_degree(0) 1 sage: G.out_degree(4) 0 """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() return self._nxg.out_degree(v) def del_edge(self, u, v, l, directed): """ Deletes the edge (u,v) with label l. INPUT: - ``u,v`` -- vertices - ``l`` -- edge label - ``directed`` -- boolean TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.del_edge(1,2,'a',True) """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() import networkx try: if self._nxg.is_multigraph(): for k,d in self._nxg.edge[u][v].iteritems(): if d.get('weight',None) == l: self._nxg.remove_edge(u,v,k) break else: if l is None or self._nxg.edge[u][v].get('weight',None) == l: self._nxg.remove_edge(u,v) except (KeyError, networkx.NetworkXError): pass def del_vertex(self, v): """ Delete a labelled vertex in self. INPUT: - ``v`` -- vertex label TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.del_vertex(0) Traceback (most recent call last): ... NetworkXError: The node 0 is not in the graph. """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() self._nxg.remove_node(v) def del_vertices(self, vertices): """ Delete labelled vertices in self. INPUT: - ``vertices`` -- iterator of vertex labels TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.del_vertices([1,2,3]) Traceback (most recent call last): ... NetworkXError: The node 1 is not in the graph. """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() for v in vertices: self._nxg.remove_node(v) def get_edge_label(self, u, v): """ Returns the edge label of (u,v). INPUT: - ``u,v`` -- vertex labels OUTPUT: label of (u,v) TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.get_edge_label(1,2) Traceback (most recent call last): ... NetworkXError: Edge (1,2) requested via get_edge_label does not exist. """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() try: E = self._nxg.edge[u][v] except KeyError: from networkx import NetworkXError raise NetworkXError("Edge (%s,%s) requested via get_edge_label does not exist."%(u,v)) if self._nxg.is_multigraph(): return [ e.get('weight',None) for e in E.itervalues() ] else: return E.get('weight',None) def has_edge(self, u, v, l): """ True if self has an edge (u,v) with label l. INPUT: - ``u,v`` -- vertex labels - ``l`` -- label OUTPUT: boolean TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.has_edge(1,2,'a') False """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() if not self._nxg.has_edge(u, v): return False if l is None: return True if self._nxg.is_multigraph(): return any( e.get('weight',None) == l for e in self._nxg.adj[u][v].itervalues() ) else: return any( e == l for e in self._nxg.adj[u][v].itervalues() ) def has_vertex(self, v): """ True if self has a vertex with label v. INPUT: - ``v`` -- vertex label OUTPUT: boolean TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.has_vertex(0) False """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() return self._nxg.has_node(v) def iterator_edges(self, vertices, labels): """ Iterate over the edges incident to a sequence of vertices. Edges are assumed to be undirected. INPUT: - ``vertices`` -- a list of vertex labels - ``labels`` -- boolean OUTPUT: a generator which yields edges, with or without labels depending on the labels parameter. TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.iterator_edges([],True) <generator object iterator_edges at ...> """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() if labels: for u,v,d in self._nxg.edges_iter(data=True): if u in vertices or v in vertices: yield (u,v,d.get('weight',None)) else: for u,v in self._nxg.edges_iter(): if u in vertices or v in vertices: yield (u,v) def _iterator_in_edges_with_labels(self, vertices): """ Iterate over the incoming edges incident to a sequence of vertices. Special case, only for internal use. EXAMPLE:: sage: g = DiGraph(graphs.PetersenGraph(), implementation="networkx")._backend sage: sorted(list(g.iterator_in_edges([0,1], True))) [(0, 1, None), (1, 0, None), (2, 1, None), (4, 0, None), (5, 0, None), (6, 1, None)] """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() for u,v,d in self._nxg.in_edges_iter(vertices,data=True): yield (u,v,d.get('weight',None)) def iterator_in_edges(self, vertices, labels): """ Iterate over the incoming edges incident to a sequence of vertices. INPUT: - ``vertices`` -- a list of vertex labels - ``labels`` -- boolean OUTPUT: a generator which yields edges, with or without labels depending on the labels parameter. TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: i = G.iterator_in_edges([],True) """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() if self._nxg.is_directed(): if labels: return self._iterator_in_edges_with_labels(vertices) else: return self._nxg.in_edges_iter(vertices) else: return self.iterator_edges(vertices,labels) def _iterator_out_edges_with_labels(self, vertices): """ Iterate over the outbound edges incident to a sequence of vertices. Special case, only for internal use. EXAMPLE:: sage: g = DiGraph(graphs.PetersenGraph(), implementation="networkx")._backend sage: sorted(list(g.iterator_out_edges([0,1], True))) [(0, 1, None), (0, 4, None), (0, 5, None), (1, 0, None), (1, 2, None), (1, 6, None)] """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() for u,v,d in self._nxg.out_edges_iter(vertices,data=True): yield (u,v,d.get('weight',None)) def iterator_out_edges(self, vertices, labels): """ Iterate over the outbound edges incident to a sequence of vertices. INPUT: - ``vertices`` -- a list of vertex labels - ``labels`` -- boolean OUTPUT: a generator which yields edges, with or without labels depending on the labels parameter. TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: i = G.iterator_out_edges([],True) """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() if self._nxg.is_directed(): if labels: return self._iterator_out_edges_with_labels(vertices) else: return self._nxg.out_edges_iter(vertices) else: return self.iterator_edges(vertices,labels) def iterator_nbrs(self, v): """ Iterate over the vertices adjacent to v. INPUT: - ``v`` -- vertex label OUTPUT: a generator which yields vertex labels TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.add_vertex(0) sage: G.iterator_nbrs(0) <dictionary-keyiterator object at ...> """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() return self._nxg.neighbors_iter(v) def iterator_in_nbrs(self, v): """ Iterate over the vertices u such that the edge (u,v) is in self (that is, predecessors of v). INPUT: - ``v`` -- vertex label OUTPUT: a generator which yields vertex labels TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.iterator_in_nbrs(0) Traceback (most recent call last): ... AttributeError: 'MultiGraph' object has no attribute 'predecessors_iter' """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() return self._nxg.predecessors_iter(v) def iterator_out_nbrs(self, v): """ Iterate over the vertices u such that the edge (v,u) is in self (that is, successors of v). INPUT: - ``v`` -- vertex label OUTPUT: a generator which yields vertex labels TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.iterator_out_nbrs(0) Traceback (most recent call last): ... AttributeError: 'MultiGraph' object has no attribute 'successors_iter' """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() return self._nxg.successors_iter(v) def iterator_verts(self, verts): """ Iterate over the vertices v with labels in verts. INPUT: - ``vertex`` -- vertex labels OUTPUT: a generator which yields vertices TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.iterator_verts(0) <generator object bunch_iter at ...> """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() return self._nxg.nbunch_iter(verts) def loops(self, new=None): """ Get/set whether or not self allows loops. INPUT: - ``new`` -- can be a boolean (in which case it sets the value) or ``None``, in which case the current value is returned. It is set to ``None`` by default. TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.loops(True) sage: G.loops(None) True """ if new is None: return self._loops if new: self._loops = True else: self._loops = False def multiple_edges(self, new=None): """ Get/set whether or not self allows multiple edges. INPUT: - ``new`` -- can be a boolean (in which case it sets the value) or ``None``, in which case the current value is returned. It is set to ``None`` by default. TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.multiple_edges(True) sage: G.multiple_edges(None) True """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() from networkx import Graph,MultiGraph,DiGraph,MultiDiGraph if new is None: return self._nxg.is_multigraph() if new == self._nxg.is_multigraph(): return if new: if self._nxg.is_directed(): self._nxg = MultiDiGraph(self._nxg) else: self._nxg = MultiGraph(self._nxg) else: if self._nxg.is_directed(): self._nxg = DiGraph(self._nxg) else: self._nxg = Graph(self._nxg) def name(self, new=None): """ Get/set name of self. INPUT: - ``new`` -- can be a string (in which case it sets the value) or ``None``, in which case the current value is returned. It is set to ``None`` by default. TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.name("A NetworkX Graph") sage: G.name(None) 'A NetworkX Graph' """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() if new is None: return self._nxg.name self._nxg.name = new def num_edges(self, directed): """ The number of edges in self INPUT: - ``directed`` -- boolean TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.num_edges(True) 0 sage: G.num_edges(False) 0 """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() return self._nxg.size() def num_verts(self): """ The number of vertices in self TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.num_verts() 0 """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() return self._nxg.order() def relabel(self, perm, directed): """ Relabel the vertices of self by a permutation. INPUT: - ``perm`` -- permutation - ``directed`` -- boolean TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.relabel([],False) """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() from networkx import relabel_nodes name = self._nxg.name self._nxg = relabel_nodes(self._nxg,perm) self._nxg.name = name def set_edge_label(self, u, v, l, directed): """ Label the edge (u,v) by l. INPUT: - ``u,v`` -- vertices - ``l`` -- edge label - ``directed`` -- boolean TESTS:: sage: G = sage.graphs.base.graph_backends.NetworkXGraphBackend() sage: G.set_edge_label(1,2,'a',True) """ if isinstance(self._nxg, (NetworkXGraphDeprecated, NetworkXDiGraphDeprecated)): self._nxg = self._nxg.mutate() if not self.has_edge(u, v, None): return if self.multiple_edges(None): self._nxg[u][v].clear() self._nxg[u][v][0] = dict(weight=l) if directed is False: self._nxg[v][u].clear() self._nxg[v][u][0] = dict(weight=l) else: self._nxg[u][v]['weight'] = l if directed is False: self._nxg[v][u]['weight'] = l
def __init__(self, G: nx.Graph) -> None: """ :param G: Networkx Graph """ self.G = G self.directed = G.is_directed()
def rsc_evaluate_graph(graph: nx.Graph, n_clusters=2, method="vanilla", percentile=None): """ Reconsutrction of [1]Understanding Regularized Spectral Clustering via Graph Conductance, Yilin Zhang, Karl Rohe :param graph: Graph to be evaluated :param n_clusters: How many clusters to look at :param method: one among "vanilla", "regularized", "regularized_with_kmeans", "sklearn_spectral_embedding", "sklearn_kmeans", "percentile". :param percentile: value in [0, 100]. Mandatory if method="percentile", otherwise None :return: """ # Experiment only on undirected graphs if graph.is_directed(): graph = graph.to_undirected() # Before computing anything, largest connected component identified and used graph = graph.subgraph(max(nx.connected_components(graph), key=len)).copy() adj_matrix = nx.to_scipy_sparse_matrix(graph, format="csr") if method == "sklearn_spectral_embedding": ( labels, num_iterations, smallest_cluster_size, ) = __sklearn_spectral_clustering(adj_matrix, n_clusters) elif method == "sklearn_kmeans": labels, num_iterations, smallest_cluster_size = __sklearn_kmeans( adj_matrix, n_clusters) elif method == "vanilla": ( labels, num_iterations, smallest_cluster_size, ) = __regularized_spectral_clustering(adj_matrix, 0, n_clusters) elif method == "regularized_with_kmeans": graph_degree = graph.degree() graph_average_degree = (np.sum(val for (node, val) in graph_degree) / graph.number_of_nodes()) ( labels, num_iterations, smallest_cluster_size, ) = __regularized_spectral_clustering(adj_matrix, graph_average_degree, n_clusters, "kmeans++") else: graph_degree = graph.degree() np.percentile(graph_degree, percentile) ( labels, num_iterations, smallest_cluster_size, ) = __regularized_spectral_clustering(adj_matrix, percentile, n_clusters, "scan") return labels
def __init__(self, gnx: nx.Graph, direct=False): self._gnx = gnx self._direct = gnx.is_directed() and direct
def graph_to_construction_sequence(graph: nx.Graph, root_node=None, node_offset: int = 0, traversal='bfs'): num_components = nx.algorithms.components.number_connected_components( graph) if num_components > 1: subgraphs = [ graph.subgraph(c) for c in sorted(nx.connected_components(graph)) ] current_offset = node_offset sequences = [] for subgraph in subgraphs: sequences.append( graph_to_construction_sequence(subgraph, node_offset=current_offset, traversal=traversal)) current_offset += len(subgraph.nodes) return list(itertools.chain(*sequences)) if root_node is None: root_node = random.choice(list(graph.nodes)) construction_sequence = [] last_node_offset = node_offset node_map = {root_node: last_node_offset} visits = set() #""" traverser = nx.dfs_edges if traversal != 'bfs' else nx.bfs_edges for source, target in traverser(graph, source=root_node): if target not in node_map: construction_sequence.append(ConstructionAction.add_node) last_node_offset += 1 node_map[target] = last_node_offset visits.add(target) for visit in [n for n in graph.neighbors(target) if n not in visits]: if visit not in node_map: construction_sequence.append(ConstructionAction.add_node) last_node_offset += 1 node_map[visit] = last_node_offset construction_sequence.extend([ ConstructionAction.add_edge, node_map[target], node_map[visit] ]) if not graph.is_directed(): construction_sequence.extend([ ConstructionAction.add_edge, node_map[visit], node_map[target] ]) #""" """ for source_node, to_visit in nx.bfs_successors(graph, source=root_node): for visit in to_visit: visits.add(visit) if visit not in node_map: construction_sequence.append(ConstructionAction.add_node) last_node_offset += 1 node_map[visit] = last_node_offset for target in [n for n in graph.neighbors(visit) if n not in visits]: if target not in node_map: construction_sequence.append(ConstructionAction.add_node) last_node_offset += 1 node_map[target] = last_node_offset construction_sequence.extend([ConstructionAction.add_edge, node_map[visit], node_map[target]]) if not graph.is_directed(): construction_sequence.extend([ConstructionAction.add_edge, node_map[target], node_map[visit]]) """ return construction_sequence
def translate(G: nx.Graph, l2n: Dict[str, str]) -> nx.Graph: G_c = nx.Graph() if not G.is_directed() else nx.DiGraph() for u, v in G.edges: G_c.add_edge(l2n.get(u, u), l2n.get(v, v), **G.get_edge_data(u, v), default=dict()) return G_c
def diameter(g: nx.Graph): if not g.is_directed() and nx.is_connected(g): print(f"Diameter: {nx.diameter(g)}") else: print("Diameter not computable, graph not connected or is directed")
def plot_force_atlas_2(G: nx.Graph, alpha=None, edge_color=(0, 0, 0, 0.1), node_color=np.array([(0, 0, 1, 0.6)]), node_size=30, edge_width=0.15, iterations=100, outboundAttractionDistribution=False, edgeWeightInfluence=0.5, jitterTolerance=0.05, barnesHutOptimize=True, barnesHutTheta=0.6, scalingRatio=5, strongGravityMode=False, gravity=1, verbose=True): if G.is_directed(): G = G.to_undirected() # Calculate the positions of the nodes forceatlas2 = ForceAtlas2( # Behavior alternatives outboundAttractionDistribution=outboundAttractionDistribution, # Dissuade hubs linLogMode=False, # NOT IMPLEMENTED adjustSizes=False, # Prevent overlap (NOT IMPLEMENTED) edgeWeightInfluence=edgeWeightInfluence, # Performance jitterTolerance=jitterTolerance, # Tolerance barnesHutOptimize=barnesHutOptimize, barnesHutTheta=barnesHutTheta, multiThreaded=False, # NOT IMPLEMENTED # Tuning scalingRatio=scalingRatio, strongGravityMode=strongGravityMode, gravity=gravity, # Log verbose=verbose) positions = forceatlas2.forceatlas2_networkx_layout(G, pos=None, iterations=iterations) # If required by user, compute node sizes. def compute_node_sizes(quantity_array): size_biggest_node = 300 return (quantity_array / np.max(quantity_array) * size_biggest_node) if node_size == 'by degree': node_size = compute_node_sizes(w.graph.degrees(G)) # Create the plot figure = plt.figure(figsize=(12, 8)) axes = figure.gca() nx.draw( G, positions, edge_color=edge_color, node_size=node_size, node_color=node_color, width=edge_width, # alpha=alpha, ax=axes)
def evaluate_graph(graph: nx.Graph, n_clusters, graph_name): """ Reconsutrction of [1]Understanding Regularized Spectral Clustering via Graph Conductance, Yilin Zhang, Karl Rohe :param graph: Graph to be evaluated :param n_clusters: How many clusters to look at :param graph_name: the graph name used to create checkpoints and figures :return: """ # Experiment only on undirected graphs if graph.is_directed(): logging.debug('Graph is directed graph, mirroring the edges to undirected') graph = graph.to_undirected() graph_size_before_trimming = graph.number_of_nodes() # Before computing anything, largest connected component identified and used graph = graph.subgraph(max(nx.connected_components(graph), key=len)).copy() if is_logging_enabled: # avoid needless mathematical operations logging.debug('Removing all components not connected to largest subgraph') graph_size_after_trimming = graph.number_of_nodes() logging.debug( 'Total nodes removed: {}'.format(graph_size_after_trimming - graph_size_before_trimming)) training_graph, testing_graph = split_graph_edges(graph) training_graph_size_before_removing_dangling_set = training_graph.number_of_nodes() training_graph: nx.Graph = training_graph.subgraph(max(nx.connected_components(training_graph), key=len)).copy() if is_logging_enabled: logging.debug('Total nodes removed from training set: {}'.format( training_graph_size_before_removing_dangling_set - training_graph.number_of_nodes())) results = { 'graph_size': graph.number_of_nodes(), 'vanilla': {}, 'regularized': {}, 'regularized_with_kmeans': {}, 'sklearn_spectral_embedding': {}, 'sklearn_kmeans': {}, 'tau_p30': {}, 'tau_p40': {}, 'tau_p50': {}, 'tau_p90': {}, 'tau_p99': {}, 'tau_max': {}, } graph_degree = graph.degree() graph_average_degree = np.sum(val for (node, val) in graph_degree) / graph.number_of_nodes() logging.debug('Average degree regularisation: %f', graph_average_degree) adj_matrix = nx.to_scipy_sparse_matrix(training_graph, format='csr') tau_values = { 'vanilla': graph_average_degree, 'regularized': graph_average_degree, 'regularized_with_kmeans': graph_average_degree, 'sklearn_spectral_embedding': graph_average_degree, 'sklearn_kmeans': graph_average_degree, 'tau_p30': np.percentile(graph_degree, 30), 'tau_p40': np.percentile(graph_degree, 40), 'tau_p50': np.percentile(graph_degree, 50), 'tau_p90': np.percentile(graph_degree, 90), 'tau_p99': np.percentile(graph_degree, 99), 'tau_max': np.percentile(graph_degree, 100) } for method, tau in tau_values.items(): if method not in EXPERIMENTS: continue # tau = np.sum(adj_matrix) / adj_matrix.shape[0] Unclear from the paper if they recalculate tau after or before logging.info('Spectral clustering with tau = %f', tau) if method == 'sklearn_spectral_embedding': labels, num_iterations, smallest_cluster_size, execution_time = sklearn_spectral_clustering(adj_matrix, n_clusters) elif method == 'sklearn_kmeans': labels, num_iterations, smallest_cluster_size, execution_time = sklearn_kmeans(adj_matrix, n_clusters) elif method == 'vanilla': labels, num_iterations, smallest_cluster_size, execution_time = regularized_spectral_clustering(adj_matrix, 0, n_clusters) elif method == 'regularized_with_kmeans': labels, num_iterations, smallest_cluster_size, execution_time = regularized_spectral_clustering(adj_matrix, tau, n_clusters, 'kmeans++') else: labels, num_iterations, smallest_cluster_size, execution_time = regularized_spectral_clustering(adj_matrix, tau, n_clusters, 'scan') # Create subgraphs based on clusters identified using spectral clustering training_nodes = list(training_graph.nodes()) subgraphs = {i: [] for i in range(n_clusters)} nodes_color = dict() for idx, label in enumerate(labels): node_id = training_nodes[idx] subgraphs[label].append(node_id) if SAVE_PLOTS: nodes_color[node_id] = COLORS[label] logging.debug('Evaluating Training Edges') training_core_cut, training_vanilla_conductance = evaluate_conductance(training_graph, subgraphs, tau) logging.debug('Evaluating Testing Edges') testing_core_cut, testing_vanilla_conductance = evaluate_conductance(testing_graph, subgraphs, tau) results[method]['training_core_cut'] = training_core_cut results[method]['training_vanilla_conductance'] = training_vanilla_conductance results[method]['testing_core_cut'] = testing_core_cut results[method]['testing_vanilla_conductance'] = testing_vanilla_conductance results[method]['num_iterations'] = num_iterations results[method]['smallest_cluster_size'] = smallest_cluster_size results[method]['execution_time'] = execution_time logging.info(method, results[method]) print(method, results[method]) if SAVE_PLOTS: nodes_color_list = [] # because networkx is weird... nodes_size = [] for node_id in set(graph.nodes()): nodes_color_list.append(nodes_color.get(node_id, TRAINING_NODE_COLOR)) nodes_size.append(5 if node_id in nodes_color else 1) plot_name = '{}_graph_{}_clusters_{}'.format(graph_name, method, n_clusters) plot_graph(graph, nodes_color_list, nodes_size, plot_name) return results