def choose_who_to_vaccinate(graph: networkx.Graph) -> List: people_to_vaccinate = [] # T = networkx.algorithms.maximum_spanning_tree(graph) node2degree = dict(graph.degree) sorted_nodes = sorted(node2degree.items(), key=lambda item: item[1], reverse=True)[:100] l1 = [node[0] for node in sorted_nodes] g2 = graph.subgraph(l1) l2 = networkx.closeness_centrality(g2) sorted_by_closness = sorted(l2.items(), key=lambda item: item[1], reverse=True)[:70] l3 = [i[0] for i in sorted_by_closness] g3 = graph.subgraph(l3) contenders = networkx.betweenness_centrality_subset(g3, l3, l3, normalized=True) sorted_nodes = sorted(contenders.items(), key=lambda item: item[1], reverse=True)[:50] people_to_vaccinate = [i[0] for i in sorted_nodes] patientsRand = [] graph.remove_nodes_from( [n for n in graph if n in set(people_to_vaccinate)]) for i in range(50): patientsRand.append(np.random.choice(graph.nodes())) # ICM(graph, patientsRand, 6) return people_to_vaccinate
def canonize_neighborhood(self, graph: nx.Graph, core: Any, shell: int, color_key='atom_type') -> str: """Calculate a canonical key for a neighborhood of an atom. Given a molecule graph and an atom in that molecule, this \ function finds the neighborhood of the given atom of the given \ depth, and returns a string that uniquely identifies that \ neighborhood. A neighborhood comprises the given atom, any atoms at most \ shell covalent bonds away from it, and any covalent bonds \ between those atoms. Neighborhoods that consist of atoms with the same colors, \ connected in the same way, will return the same key. Args: graph: A molecule's atomic graph. core: A node in graph, the core of the neighborhood. shell: Shell size to use when creating the neighborhood. color_key: Attribute key to use to determine atom color. Returns: A string unique to the neighborhood. """ if shell > 0: fragment = graph.subgraph(bfs_nodes(graph, core, max_depth=shell)) else: fragment = graph.subgraph([core]) result = self.canonize(fragment, color_key=color_key, core=core) return result
def _propagate(self, graph: Graph, embedded_sub_graph: Graph, embeddings: np.array, node2id, id2node, max_itr: int = 20): reachable_nodes = self._get_reachable_subgraph(graph, embedded_sub_graph) Z1 = embeddings sparse_adj = nx.to_scipy_sparse_matrix(graph) while reachable_nodes.order() > 0: print("Propagating : current embedding size : {}, reachable nodes : {}, total graph size : {}".format( len(embedded_sub_graph), len(reachable_nodes), len(graph))) Z1 = embeddings reachable_indexes = [i for i, node in enumerate(graph) if node in reachable_nodes] embedded_indexes = [i for i, node in enumerate(graph) if node in embedded_sub_graph] A1, A2 = sparse_adj[embedded_indexes, :][:, reachable_indexes], sparse_adj[reachable_indexes, :][:, reachable_indexes] norm = sparse.hstack([A1.T, A2]).sum(axis=1) A1_norm, A2_norm = sparse.csc_matrix(A1/norm.T), sparse.csc_matrix(A2/norm.T) Z2 = sparse.csr_matrix(np.random.uniform(-1, 1, size=(reachable_nodes.order(), self.out_dim_))) for itr in range(max_itr): Z2 = A1_norm.T@Z1 + A2_norm.T@Z2 n1 = embedded_sub_graph.order() for node in reachable_nodes: node2id[node] = n1 id2node.append(node) n1 = n1 + 1 embeddings = np.concatenate([embeddings, Z2], axis=0) embedded_sub_graph = graph.subgraph(list(node2id.keys())) reachable_nodes = self._get_reachable_subgraph(graph, embedded_sub_graph) unreachable_nodes = graph.subgraph([node for node in graph if node not in embedded_sub_graph]) if unreachable_nodes: unreachable_nodes_embeddings = sparse.csr_matrix(np.zeros((unreachable_nodes.order(), self.out_dim_))) embeddings = np.concatenate([embeddings, unreachable_nodes_embeddings.todense()], axis=0) n1 = embedded_sub_graph.order() for node in unreachable_nodes: node2id[node] = n1 id2node.append(node) n1 = n1 + 1 return { "embeddings": np.array(embeddings), "node2id": node2id, "id2node": id2node, "unreachable_nodes": unreachable_nodes }
def swap(clique: list, graph: nx.Graph, node_select: str = "uniform") -> list: """If possible, generates a new clique by swapping a node in the input clique with a node outside the clique. Proceeds by calculating the set :math:`C_1` of nodes in the rest of the graph that are connected to all but one of the nodes in the clique. If this set is not empty, this function randomly picks a node and swaps it with the corresponding node in the clique that is not connected to it. The set :math:`C_1` and corresponding nodes in the clique are provided by the :func:`c_1` function. Whenever there are multiple nodes within :math:`C_1`, one must choose which node to add to the growing clique. This function allows a method of choosing nodes to be set with the ``node_select`` argument, with node selection based on uniform randomness and node degree supported. Degree-based node selection involves picking the node with the greatest degree, with ties settled by uniform random choice. **Example usage:** >>> graph = nx.wheel_graph(5) >>> graph.remove_edge(0, 4) >>> clique = [0, 1, 2] >>> swap(clique, graph) [0, 2, 3] Args: clique (list[int]): a subgraph specified by a list of nodes; the subgraph must be a clique graph (nx.Graph): the input graph node_select (str): method of selecting nodes from :math:`C_1` during growth. Can be either ``"uniform"`` for uniform random selection or ``"degree"`` for degree-based selection. Defaults to ``"uniform"``. Returns: list[int]: a new clique subgraph of equal size as the input """ if not set(clique).issubset(graph.nodes): raise ValueError("Input is not a valid subgraph") if not is_clique(graph.subgraph(clique)): raise ValueError("Input subgraph is not a clique") clique = set(clique) _c_1 = c_1(clique, graph) if _c_1: if node_select == "uniform": swap_index = np.random.choice(len(_c_1)) swap_nodes = _c_1[swap_index] elif node_select == "degree": degrees = np.array([graph.degree(n[1]) for n in _c_1]) to_swap_index = np.random.choice( np.where(degrees == degrees.max())[0]) swap_nodes = _c_1[to_swap_index] else: raise ValueError("Node selection method not recognized") clique.remove(swap_nodes[0]) clique.add(swap_nodes[1]) return sorted(clique)
def evaluate_conductance(graph: nx.Graph, subgraphs, tau): """ :param graph: the graph being evaluated :param subgraphs: K cluster of Subsets of the main graph :param tau: tuning parameter, tau = 0 = vanilla conductance :return: core_cut, vanilla_conductance """ vanilla_conductances = [] core_cuts = [] for _, nodes in subgraphs.items(): subgraph = graph.subgraph(nodes).copy() subgraph_complement = set(graph) - set(subgraph) cut = nx.cut_size(graph, subgraph, subgraph_complement) volume_subgraph = nx.volume(graph, subgraph) volume_subgraph_complement = nx.volume(graph, subgraph_complement) volume_div = min(volume_subgraph, volume_subgraph_complement) vanilla_conductances.append((cut / volume_div)) core_cuts.append((cut + ((tau / len(graph)) * len(subgraph) * len(subgraph_complement))) / ( volume_div + (tau * len(subgraph)))) vanilla_conductance = min(vanilla_conductances) core_cut = min(core_cuts) logging.debug('Vanilla graph conductance: %f', vanilla_conductance) logging.debug('CoreCut graph conductance: %f', core_cut) return core_cut, vanilla_conductance
def getReplaceable(nodes: np.ndarray, graph: nx.Graph) -> list: """ Function that returns the nodes from the nodes argument, that can be removed from the network without generating two disconnected sub-networks. Parameters ---------- nodes: np.ndarray Nodes composing the sub-graph whose nodes are to be examined. graph: nx.Graph Graph defining the structure of the network to be explored. Returns ------- :list Nodes that can be replaced without generating two disconnected sub-networks. """ replaceable_nodes = [] for node in nodes: selected_nodes = [n for n in nodes if n != node] subgraph = graph.subgraph(selected_nodes) if nx.is_connected(subgraph): replaceable_nodes.append(node) return replaceable_nodes
def create_personas( G: nx.Graph, n: Hashable, clustering: Callable[[nx.Graph], Iterable[Sequence[Hashable]]], ) -> Tuple[List[PersonaNode], Dict[Hashable, PersonaNode]]: """ Given a graph, a node in the graph, and a clustering algorithm, generate the personas for the given node. :param G: input graph :param n: node in the graph :param clustering: algorithm to cluster node on a graph, a callable taking a graph to an iterable of hashable sequences :return: 2-tuple which holds: personalities, so PersonaNode objects, for the given node; and a remap dictionary which maps every node in the ego-net of n in G to its corresponding PersonaNode of n """ # TODO this can be optimised clusters = clustering(G.subgraph(G.neighbors(n))) persona_remap = {} personalities = [] for index, cluster in enumerate(clusters): persona = PersonaNode(node=n, index=index) personalities.append(persona) for other_node in cluster: persona_remap[other_node] = persona return personalities, persona_remap
def is_near_cleaner_wasteful(X, graph : nx.Graph, pool : Pool): for Y in subsets_upto(vertex_set(graph), 3): hole = find_odd_hole(graph.subgraph(vertex_set(graph) - (set(X) - set(Y))), pool) if hole: return True return False
def get_community(G: nx.Graph, n, hops: int, max, maxCore: bool, visited={}): e = nx.ego_graph(G, n['id'], hops) # filter here before k-core # # here we filter out the nodes that doesn't # meet the domination requirements # currently we select nodes that have dom > 0 # but this can change to a pruning function community = [ x for x, y in e.nodes(data=True) if (y['dom'] > 0 and y['id'] not in visited) ] community = G.subgraph(community) k_core = nx.k_core(community) max_k_core = min(k_core.degree(), key=lambda x: x[1])[1] if maxCore: res = k_core else: res = community # mark nodes as visited # res = [x for x,y in res.nodes(data=True) if y['id'] not in visited ] # mod = nx_comm.modularity(G, community) data1 = json_graph.node_link_data(res, {"link": "edges"}) data1['stats'] = get_graph_stats(res, max['dom'], max_k_core, fast=True) data1['stats']['init'] = n['id'] return (data1, res)
def community_keynodes(graph: nx.Graph, community: list, social_position: list, alpha: int = 10) -> list: """ Key nodes detection Algorithm :param graph: the graph object to be used :param community: a list of nodes in the community :param social_position: social position score of each node in the community :param alpha: therehold score :return: a list of key nodes """ if len(set(social_position)) == 1: # SP(V1) == SP(V2) == ... == SP(Vn) return community key, SP = {}, {} for node, sp in zip(community, social_position): key[node], SP[node] = 0, sp subgraph = graph.subgraph(community) for u, v in subgraph.edges(): if SP[u] < SP[v]: key[u] -= abs(SP[u]-SP[v]) key[v] += abs(SP[v]-SP[u]) else: key[u] += abs(SP[u]-SP[v]) key[v] -= abs(SP[v]-SP[u]) max_score = max(list(key.values())) keynodes =[node for node, score in key.items() if score > 0 and max_score / score < alpha] return keynodes
def CIS_oracles(U: Set, G: nx.Graph, max_size: int): if len(U) == 0: f_cis = None adj_cis = lambda k: {k} elif len(U) == 1: v = U.__iter__().__next__() f_cis = set() def adj_cis(k: int): if k == v: return set() else: if k in G[v]: return {v, k} else: return None elif len(U) > 1: subgraph = G.subgraph(U) articulations = set(articulation_points(subgraph)) f_cis = U.difference( {min(set(subgraph.nodes).difference(articulations))}) def adj_cis(k: int): if k in U: if k in articulations: return None else: return U.difference({k}) else: if len(U) < max_size and len(set(G[k]).intersection(U)) > 0: return U.union({k}) else: return None return f_cis, adj_cis
def vertex_branching(graph: nx.Graph, budget: int): #print("in branching start:", graph.number_of_nodes(), graph.number_of_edges(), "budget", budget, file=sys.stderr) flag, graph, vc, folded_verts = reduction.rrhandler(graph, budget) #print("in branching vc:", vc, "fold_verts:", folded_verts, "new graph", graph.number_of_nodes(), file=sys.stderr) if not flag: # NO instance return False, set() budget -= len(vc) + len(folded_verts) num_edges = graph.number_of_edges() if budget < 0: return False, set() if budget <= 0 and num_edges > 0: return False, set() elif num_edges == 0: return True, utils.unfold(vc, folded_verts) else: # budget > 0 and num_edges > 0 cur_node, max_degree = max(graph.degree_iter(), key=itemgetter(1)) # print("branching on vertex ", cur_node, "of degree", max_degree, file=sys.stderr) nbrs = set(graph.neighbors_iter(cur_node)) flip = random.random() < 0.5 # flip coin to decide branch order if flip: branches = [(nbrs, len(nbrs)), (set(), 1)] else: branches = [(set(), 1), (nbrs, len(nbrs))] nodes = set(graph.nodes_iter()) for branch, budget_change in branches: if budget_change > budget: continue res, vc_new = vertex_branching( graph.subgraph(nodes - {cur_node} - branch), budget - budget_change) if res: final_vc = vc | vc_new | (branch or {cur_node}) return True, utils.unfold(final_vc, folded_verts) return False, set()
def draw(gr: nx.Graph, check_overload=True, circular=False): node_color = [] subgraphs = [gr.subgraph(c) for c in nx.connected_components(gr)] for node in gr.nodes(): if check_overload: added = False for adjacent in gr.adj[node]: if gr[node][adjacent]['a'] > gr[node][adjacent]['c']: node_color.append('purple') added = True break if added: continue if node in subgraphs[0]: node_color.append('blue') else: node_color.append('red') if circular: pos = nx.circular_layout(gr) else: pos = nx.nx_pydot.graphviz_layout(gr, prog='neato') nx.draw(gr, pos=pos, node_size=500, with_labels=True, node_color=node_color) plt.show()
def shortest_path_sets(graph : nx.Graph, A : set, B : set): # Find a shortest path between two connected sets of verts for a in A: for b in B: if graph.has_edge(a, b): return [a, b] F = nx.Graph(graph.subgraph(vertex_set(graph) - (A | B))) F.add_nodes_from(("a", "b")) for (u, v) in edge_set(graph): if u in A: F.add_edge("a", v) elif v in A: F.add_edge(u, "a") if u in B: F.add_edge("b", v) elif v in B: F.add_edge(u, "b") P = shortest_path(F, "a", "b") if P is None: return None new_a = (A & neighbourhood(graph, P[ 1])).pop() new_b = (B & neighbourhood(graph, P[-2])).pop() return [new_a] + P[1:-1] + [new_b]
def path_through(graph : nx.Graph, S, u, v): # Returns a path from u to v in G with all its interior vertices in S if # one exists. S = set(S) | {u, v} return shortest_path(graph.subgraph(S), u, v)
def subtree(graph: nx.Graph, nodes: Iterable[Node], predecessors: Mapping[Node, Node] = None) -> nx.Graph: """Extract a subtree with the specified nodes. Parameters ---------- graph : nx.Graph Full tree graph. nodes : Iterable[Node] Nodes to include. predecessors : Mapping[Node, Node], optional Mapping of node to parent node, by default None Returns ------- nx.Graph A proper tree containing at least the specified nodes. """ if predecessors is None: (root, ), nodes = _peek(iter(nodes)) predecessors = dict(nx.bfs_predecessors(graph, root)) base = set(nodes) parent_nodes = (parents(node, predecessors) for node in base) result = set(itertools.chain.from_iterable(parent_nodes)) result.update(base) return graph.subgraph(result)
def get_path_graph( self, graph: nx.Graph, source: str, target: str, enforce_directionality: bool = True, try_reverse: bool = True, ): # pylint: disable=too-many-arguments """Make a new graph with the shortest paths between two nodes""" new_graph = graph if enforce_directionality else self._make_undirected( graph) try: nodes = set( sum( map(list, nx.all_shortest_paths(new_graph, source, target)), [])) return graph.__class__(graph.subgraph(nodes)) except (nx.NetworkXError, nx.NetworkXException): warn(traceback.format_exc()) if try_reverse: return self.get_path_graph( graph=graph, source=target, target=source, enforce_directionality=enforce_directionality, try_reverse=False, ) return graph.__class__()
def largest_connected_component(G): """ Input G: an n x n matrix or a networkx Graph Output The largest connected component of g """ if type(G) == np.ndarray: if G.ndim == 2: if G.shape[0] == G.shape[1]: # n x n matrix G = Graph(G) else: raise TypeError("Networkx graphs or n x n numpy arrays only") subgraphs = [ G.subgraph(i).copy() for i in networkx.connected_components(G) ] G_connected = [] for i in subgraphs: if len(i) > len(G_connected): G_connected = i return G_connected
def is_berge_naive(graph : nx.Graph, pool : Pool): subsets = it.chain.from_iterable( ( it.combinations(graph.nodes, k) for k in range(5, len(graph.nodes), 2) ) ) graphs = it.chain.from_iterable( ( (graph.subgraph(S), nx.complement(graph.subgraph(S))) for S in subsets ) ) for hole_found in pool.imap_unordered(is_cycle, graphs, chunksize=4096): if hole_found: return False return True
def readjust_tree(root: TreeNode, input_graph: nx.Graph) -> TreeNode: new_root = deepcopy(root) second_lowest_tnodes = set(tnode.parent for tnode in new_root.leaves) # TODO: untested!!! buggy # find out the offending tnodes not_ok_tnodes: Set[TreeNode] = set() for tnode in second_lowest_tnodes: leaves = get_leaves(tnode) subg = input_graph.subgraph(leaves) is_ok = subg.order() > 0 and nx.is_connected(subg) if not is_ok: not_ok_tnodes.add(tnode) # combine all the leaves of each offending tnode siblings together for offennding_tnode in not_ok_tnodes: parent = offennding_tnode.parent if parent is None: # we have processed this tnode already continue parent.children = tuple() combined_leaves = get_leaves(offennding_tnode) for sib in offennding_tnode.siblings: combined_leaves |= get_leaves(sib) for new_leaf in combined_leaves: TreeNode(name=new_leaf, score=None, parent=parent) return new_root
def c_0(clique: list, graph: nx.Graph): """Generates the set :math:`C_0` of nodes that are connected to all nodes in the input clique subgraph. The set :math:`C_0` is defined in :cite:`pullan2006phased` and is used to determine nodes that can be added to the current clique to grow it into a larger one. **Example usage:** >>> graph = nx.complete_graph(10) >>> clique = [0, 1, 2, 3, 4] >>> c_0(clique, graph) [5, 6, 7, 8, 9] Args: clique (list[int]): a subgraph specified by a list of nodes; the subgraph must be a clique graph (nx.Graph): the input graph Returns: list[int]: a list containing the :math:`C_0` nodes for the clique """ if not is_clique(graph.subgraph(clique)): raise ValueError("Input subgraph is not a clique") clique = set(clique) c_0_nodes = [] non_clique_nodes = set(graph.nodes) - clique for i in non_clique_nodes: if clique.issubset(graph.neighbors(i)): c_0_nodes.append(i) return c_0_nodes
def degreeone(graph: nx.Graph, k: int = -1): """ Degree 1 vertices (RR2) :param graph: graph to reduce :param k: parameter k :return: 4-tuple (flag, changed, new_graph, vc) where flag=false denotes that this is a NO-instance and vice-versa, changed indicates if the reduction rule caused any change, new_graph is the reduced subgraph, vc is the partial vc constructed while applying reduction rules """ deg_one = set() for v, deg in graph.degree_iter(): if deg == 1: deg_one.add(v) if len(deg_one) == 0: # no degree 1 vertices found, so no change return True, False, graph, set() vc = set() to_delete = set() while deg_one: v = deg_one.pop() if v in vc: continue neigh = utils.first(graph.neighbors_iter(v)) to_delete.add(v) vc.add(neigh) if len(vc) > k: # NO-instance return False, False, graph, set() nodes = set(graph.nodes_iter()) new_graph = graph.subgraph(nodes - vc - to_delete) return True, True, new_graph, vc
def clique_shrink(subgraph: list, graph: nx.Graph) -> list: """Shrinks an input subgraph until it forms a clique. Proceeds by removing nodes in the input subgraph one at a time until the result is a clique that satisfies :func:`~strawberryfields.apps.graph.utils.is_clique`. Upon each iteration, this function selects the node with the lowest degree relative to the subgraph and removes it. Args: subgraph (list[int]): a subgraph specified by a list of nodes graph (nx.Graph): the input graph Returns: list[int]: a clique of size smaller than or equal to the input subgraph """ if not utils.is_subgraph(subgraph, graph): raise ValueError("Input is not a valid subgraph") subgraph = graph.subgraph( subgraph).copy() # A copy is required to be able to modify the # structure of the subgraph (https://networkx.github.io/documentation/stable/reference/classes/generated/networkx.Graph.subgraph.html) while not utils.is_clique(subgraph): degrees = list(subgraph.degree()) np.random.shuffle( degrees ) # used to make sure selection of node with lowest degree is not # deterministic in case of a tie (https://docs.python.org/3/library/functions.html#min) to_remove = min(degrees, key=lambda x: x[1]) subgraph.remove_node(to_remove[0]) return sorted(subgraph.nodes())
def hcsw(graph: nx.Graph, multiplier_threshold: float = 2) -> nx.Graph: """Clusters a connected undirected weighted graph. Returns a graph with the same nodes but not necessarily connected """ number_of_nodes = graph.number_of_nodes() logger.debug(f'Clustering graph with {number_of_nodes} nodes') # singular graphs are already clustered if number_of_nodes < 2: logger.debug('Graph too small, exiting') return graph cut_weight, partitions = nx.algorithms.connectivity.stoer_wagner(graph) if not highly_connected(graph, cut_weight, multiplier_threshold): logger.debug('Graph not dense, performing cut') sub_graphs = [graph.subgraph(v).copy() for v in partitions] component_1 = hcsw(sub_graphs[0]) component_2 = hcsw(sub_graphs[1]) graph = nx.compose(component_1, component_2) else: logger.debug('Graph is dense, skipping cut') return graph
def graph_stats(G: nx.Graph) -> Dict[str, Any]: stats = dict() n, m = G.number_of_nodes(), G.number_of_edges() stats['number_of_vertices'] = n stats['number_of_edges'] = m stats['complexity'] = n * m stats['density'] = 2 * m / (n * (n - 1)) stats['connected_components'] = [] for G_hat in (G.subgraph(c) for c in nx.connected_components(G)): component_stats = dict() component_stats['number_of_vertices'] = G_hat.number_of_nodes() component_stats['number_of_edges'] = G_hat.number_of_edges() component_stats['diameter'] = nx.diameter(G_hat, usebounds=True) component_stats['radius'] = nx.radius(G_hat, usebounds=True) component_stats['center_size'] = len(nx.center(G_hat, usebounds=True)) component_stats['periphery_size'] = len( nx.periphery(G_hat, usebounds=True)) stats['connected_components'] += [component_stats] stats['number_of_connected_components'] = len( stats['connected_components']) stats['average_clustering_coefficient'] = nx.average_clustering(G) return stats
def prune_graph_by_distance(graph: nx.Graph) -> nx.Graph: """Get a subgraph with far flung nodes pruned. Many graphs end up with a few far flung nodes that distort the whole map. This function computes the mean distances from the center of the graph and removes any nodes that are more than 2.5x the average distance. """ if len(graph.nodes) == 0: return graph positions = nx.get_node_attributes(graph, 'position') center_x = sum([positions[n][0] for n in graph.nodes()]) / len(graph.nodes()) center_y = sum([positions[n][1] for n in graph.nodes()]) / len(graph.nodes()) distance_map = {} for node in graph.nodes(): node_x = positions[node][0] node_y = positions[node][1] distance = math.sqrt((node_x - center_x) ** 2 + (node_y - center_y) ** 2) distance_map[node] = distance mean_distance = sorted(distance_map.values())[int(len(distance_map.values()) / 2)] max_distance = mean_distance * 2 include_nodes = [] for node in graph.nodes(): if distance_map[node] <= max_distance: include_nodes.append(node) return graph.subgraph(include_nodes)
def optimize_subgraph_connectivity(graph: nx.Graph, nodes: set, utility_function: Callable[[nx.Graph], float], n_iter: int = 50, **kwargs) -> Tuple[nx.Graph, float]: """ Optimize graph with the rewire operations restricted to the nodes given as arguments. Arguments --- graph: NetworkX graph nodes: Set Nodes contained on `graph` utility_function: Function It takes a graph and returns a real number. Higher values are more optimal. """ best_score = utility_function(graph) best_subgraph = graph.subgraph(nodes) rewiring_rule: callable = lambda x: subgraph_rewire(x, nodes) try: # (best_score, best_subgraph) = simulated_annealing_optimize(graph, # utility_function, # rewiring_rule, # n_iter=3) (best_score, best_subgraph) = hill_climb_optimize( graph, utility_function, rewiring_rule, n_iter=n_iter, **kwargs ) finally: return (best_subgraph, best_score)
def compress(graph: nx.Graph, vc, smart_subsets=True): """ Given a vc tries to find a vc of strictly smaller size If unable to find, then provided vc is optimal. :param graph: Graph to find vc for :param vc: Supplied vc :param smart_subsets: generate subsets smartly by branching :return: 2-tuple (status, new_vc) where status is True if it found a smaller vc in which case new_vc contains this vc. Else status is false and new_vc is empty_set() """ vc = set(vc) ctr = 0 subset_func = gen_ind_subsets if smart_subsets else brute_ind_subsets for ss in subset_func(graph.subgraph(vc)): if ctr % 1000 == 0: print(ctr, "done", file=sys.stderr) ctr += 1 if len(ss) < 1: # ignore empty subsets continue nbrs = set() for u in ss: # accumulate neighborhood of subset nbrs.update(graph.neighbors_iter(u)) if len(nbrs) < len(ss): # found smaller neighborhood return True, vc - ss | nbrs return False, set()
def subgraph_rewire(G: nx.Graph, nodes: set, seed=None) -> Tuple[nx.Graph, nx.Graph]: """ Rewire a graph with rewiring restricted to a subgraph of it. Arguments --- G: NetworkX graph nodes: Set of nodes that makes the subgraph seed: random seed """ r = random.Random(seed) subgraph = G.subgraph(nodes) contributors_set = { n for n, attrs in subgraph.nodes(data=True) if attrs["type"] == "contributor" } grants_set = { n for n, attrs in subgraph.nodes(data=True) if attrs["type"] == "grant" } if len(subgraph.nodes) > 2 and len(subgraph.edges) > 1: edge = r.choice(tuple(subgraph.edges)) edge_data = G.edges[edge] node_1 = r.choice(tuple(contributors_set)) node_2 = r.choice(tuple(grants_set)) G_new = G.copy() G_new.remove_edge(*edge) G_new.add_edge(node_1, node_2, **edge_data) return G_new else: raise ValueError("Subgraph must have more than two nodes & one edge")
def double_star_decomposition(graph : nx.Graph, pool : Pool): next_ = {graph} while not empty(next_): F = next_.pop() flag = False for u, v in vertex_combinations(F, 2): path = shortest_path(graph, u, v) if path and len(path) > 4: flag = True break if not flag: continue S = find_double_star_cutset(F, pool) if S is None: yield F continue for H in nx.connected_components(F.subgraph(vertex_set(F)-S)): next_.add(graph.subgraph(set(H | S)))
def subnetwork(self, nbunch, pos=None): """ Returns Network instance with nbunch nodes, see subgraph. """ if not pos: pos = self.pos H = Graph.subgraph(self, nbunch) for node in H: H.pos.update({node: pos[node][:2]}) if len(pos[node]) > 2: H.ori.update({node: pos[node][2]}) else: H.ori.update({node: self.ori[node]}) H.labels.update({node: self.labels[node]}) H._environment = deepcopy(self._environment) assert(isinstance(H, Network)) return H
def _get_fbvs(self, graph: Graph): # get the list of cycles cycles = cycle_basis(graph) # if the graph is already acyclic, there's nothing to remove, so return an empty set if len(cycles) == 0: return set() # get the set of nodes that is in at least one cycle nodes_in_cycles = set([item for sublist in cycles for item in sublist]) min_num_to_remove = sys.maxsize min_nodes_to_remove = set() for node in nodes_in_cycles: # make an induced subgraph with the current node removed nodes = graph.nodes() nodes.remove(node) nodes_set = frozenset(nodes) if nodes_set in self.cacheDict: # if we have previously calculated the min fbvs for this induced subgraph, get that # fbvs from the cache dict nodes_to_remove = self.cacheDict[nodes_set] else: # otherwise we have to calculate it new_graph = graph.subgraph(nodes) nodes_to_remove = self._get_fbvs(new_graph) # add the newly calculated fbvs to the cache dict self.cacheDict[nodes_set] = nodes_to_remove if len(nodes_to_remove) < min_num_to_remove: min_num_to_remove = len(nodes_to_remove) nodes_to_remove.add(node) min_nodes_to_remove = nodes_to_remove return min_nodes_to_remove
class Network(HasTraits): """ The implementation of the Connectome Networks """ implements(INetwork) # Network ID, from parsed GraphML the graphid networkid = '' # Network name networkname = Str # network name as seen in the TreeView name = Str # Is it an hierarchical network? hierarchical = CBool(False) # TODO: later, also Hypergraph?! # see: http://www.ploscompbiol.org/article/info%3Adoi%2F10.1371%2Fjournal.pcbi.1000385 hypergraph = CBool(False) # Directionality of the Network, {True: 'directed', False: 'undirected'} directed = CBool(False) # metadata for the network metadata = Dict # NodeKeys from the parsed GraphML # These are Dict of Dict, all having strings nodekeys = {} # Edgekeys, from parsed GraphML edgekeys = {} # A NetworkX AttrGraph containing all the information graph = Any # Surface containers surfaces = List(ISurfaceContainer) # Surface containers loaded surfaces_loaded = List(ISurfaceContainer) # Volume data volumes = List(IVolume) # Track data tracks = List(ITrackfile) # is this network active, and thus a render manager displayed? active = Bool # the render manager of this network rendermanager = Instance(RenderManager) # DatasourceManager Instance of this network datasourcemanager = Instance(DatasourceManager) # private traits ########### # parent cfile this networks belongs to _parentcfile = Any # filezip of cfile _filezip = DelegatesTo('_parentcfile') # edge parameters for visualization _edge_para = Instance(EdgeParameters) # View traits_view = View( Item('networkname', style = 'readonly'), Item('hierarchical', style = 'simple'), Item('hypergraph', style = 'simple'), Item('directed', style = 'simple'), Item('active', style = 'simple'), title = 'A network', ) def __init__(self, name, src = None, directed = '0', pickled_graph = None, \ hierarchical ='0', hypergraph = '0', graph = None): """ Initializes the network and sets the traits. Parameters ---------- name : string the name of the network src : file handle or StringIO object the source text of the network to parse pickled_graph : NetworkX graph reference to a graph object, src should be None directed : bool Is the network directed? hierarchical : bool Is the network hierarchical? (default: '0') Not implemented yet. hypergraph : bool Is the network a hypergraph? (default: '0') Not implemented yet. """ # initialize the traits self.networkname = name self.directed = int(directed) self.hierarchical = int(hierarchical) self.hypergraph = int(hypergraph) if src is None and not pickled_graph is None: self.load_pickled_graphml(pickled_graph) else: if not src is None: # generates NetworkX Graph self.graph = self.parse_network_graphml(src) elif not graph is None: self.graph = graph else: if self.directed: from networkx import DiGraph self.graph = DiGraph() logger.info("Initialize with empty directed Graph") else: from networkx import Graph self.graph = Graph() logger.info("Initialize with empty undirected Graph") # initializes the weight key of the graph # with the first edgekey if len(self.edgekeys) > 0: edgk = self.edgekeys.keys() if not 'weight' in edgk: self.set_weight_key(edgk[0]) else: # try grabbing first edge from the graph if self.graph.number_of_edges() > 0: it = self.graph.edges_iter(data=True) edg = it.next() if len(edg[2]) > 0: # if it has a weigth key, just leave it edgk = edg[2].keys() if not 'weight' in edgk: self.set_weight_key(edgk[0]) else: pass # logger.error('Cannot set weight key for network : ' + self.networkname) def _name_default(self): return self.networkname def _active_default(self): return False def _active_changed(self , value): if value: n = self.name if ' [Active]' not in n: self.name = "%s [Active]" % n # XXX: do refactor with threaded loading of surfaces # and default spring force layout for graph rendering! # see also TraitsUI Demos: Multi thread demo # load the surface containers data # make a deep copy of the already loaded surface containers import copy self.surfaces = copy.deepcopy(self.surfaces_loaded) for surfcont in self.surfaces: surfcont.load_surface_container() if self.rendermanager is None: self._create_datasourcemanager() self._create_renderer() # if there are no surfaces, initialize # network rendering, but only if dn_positions are given if len(self.surfaces) == 0: logger.debug('No surfaces found. Try to render graph view with dn_position information.') self.rendermanager.datasourcemanager._compute_3DLayout(-1, -1) self.rendermanager.visualize_graph() else: logger.debug('SurfaceContainer found. Try to render 3D View using %s.' % self.surfaces[0].name) if len(self.surfaces[0].surfaces) == 0: logger.debug('Rendering not possible because SurfaceContainer contains no surfaces.') else: logger.debug('Using first surface for rendering.') self.surfaces[0].surfaces[0]._layout_3DView() if not self._parentcfile._workbenchwin is None: #from enthought.pyface.timer.api import do_later from enthought.pyface.api import GUI GUI.invoke_later(self._parentcfile._workbenchwin.status_bar_manager.set, message = '') else: self.name = self.name.replace(' [Active]', '') logger.debug('Close RenderManager scenes') self.rendermanager.close_scenes() logger.debug('All scenes closed.') # FIXME: what is happening in the following? # e.g. for instances. e.g. reset traits? # XXX: this is somehow not correct. do i need to use del # or remove/reset traits? self.rendermanager = None self.datasourcemanager = None self.surfaces = [] def _de_activate(self): """ Toggles the internal state of the activation """ if self.active: self.active = False else: self._parentcfile._workbenchwin.status_bar_manager.message = 'Activating network ...' self.active = True def _edge_parameters(self): """ Dialog to change edge attribute and thresholding """ if self._edge_para is None: self._edge_para = EdgeParameters(self, self.rendermanager.attract.point_scalars_name) self._edge_para.configure_traits() def _create_renderer(self): """ Creates the renderer instance if not yet available and opens the scenes in mayavi """ if self.active: if self.rendermanager is None: logger.debug('Create a RenderManager instance') self.rendermanager = RenderManager(network=self) else: logger.debug('RenderManager instance already running. This is an error.') def _create_datasourcemanager(self): """ Creates the datasource manager instance if not yet available """ if self.active: if self.datasourcemanager is None: logger.debug('Create a DatasourceManager instance') self.datasourcemanager = DatasourceManager(network=self) else: logger.debug('DatasourceManager instance already running. This is an error.') def _render_matrix(self): """ Invokes the connectivity matrix viewer """ # assume the network is activated (i.e. data source generated) # we need the edge parameter instance initialized if self._edge_para is None: self._edge_para = EdgeParameters(self, self.rendermanager.attract.point_scalars_name) logger.debug('Invoke Matrix Viewer...') self.rendermanager.invoke_matrix_viewer() def _trackvis_launch(self): """ Generates scene file and launch Trackvis on the selected nodes """ import tempfile logger.debug('Starting TrackVis ...') # extract selected subgraph selectionlist = self.get_selectiongraph_list() if len(selectionlist) == 0: # message from enthought.traits.ui.message import message message(message = 'No nodes selected for ROI creation!', title = 'Infomessage', buttons = [ 'OK' ], parent = None) tmpgraph = self.graph.subgraph(selectionlist) # extract trackfile temporarily if len(self.tracks) == 0: logger.info('No trackfile found to invoke Trackvis.') return else: # load the first trackfile trackfname = self.tracks[0].load_trackfile_to_file() # find the first valid segmentation volume in the self.volumes list for vol in self.volumes: if vol.segmentation: logger.debug('Found a segmentation volume file. Assume labels are corresponding.') volumefname = vol.load_volume_to_file() break # generate the scene file in the temporary folder tmpscenefile=tempfile.mkstemp(prefix='tmp', suffix='.scene') # generate trackfile generate_scene_file(scenefname=tmpscenefile[1], \ trackfname = trackfname, \ volumefname = volumefname, \ selectiongraph = tmpgraph) # execute trackvis in a thread pref = preference_manager.preferences action = ThreadedTrackvis(tvpath = pref.get('cviewer.plugins.ui.trackvispath'), \ fname = tmpscenefile[1], \ trkfname = trackfname,\ volfname = volumefname) action.start() def add_surface_container(self, surfacecontainer): """ Add a surface container to the loaded list Parameters ---------- surfacecontainer : `ISurfaceContainer` instance a surface container object """ surfacecontainer._networkref = self self.surfaces_loaded.append(surfacecontainer) def add_volume(self, volume): """ Adds a volume to the volumes list Parameters ---------- volume : `IVolume` instance a volume object """ self.volumes.append(volume) def add_trackfile(self, trackfile): """ Adds a trackfile to the tracks list Parameters ---------- trackfile : `ITrackfile` instance a trackfile of type ITrackfile """ self.tracks.append(trackfile) def unselect_all(self): """ Unselects every node in the current network """ if self.datasourcemanager is None: raise Exception('No DatasourceManager. You have to first activate the network and render it.') from numpy import array # get all the nodes graphnodes = self.datasourcemanager._srcobj.relabled_graph.nodes() # and unselect all nodes self.rendermanager._select_nodes(selection_node_array = array(graphnodes)) def select_all(self): """ Selects all nodes in the current network """ if self.datasourcemanager is None: raise Exception('No DatasourceManager. You have to first activate the network and render it.') from numpy import array # get all the nodes graphnodes = self.datasourcemanager._srcobj.relabled_graph.nodes() # and select all nodes self.rendermanager._select_nodes(selection_node_array = array(graphnodes), activate = True) def set_selectiongraph(self, sellist, activate = False): """ Sets the selected nodes in the network to active. Parameters ---------- sellist : array_like a list of nodeids conforming to the NetworkX node id activate : boolean set the selectionlist nodes to activated? """ from numpy import array, int16 graphnodes = self.graph.nodes(data=False) if self.rendermanager is None: raise Exception('No RenderManager. You have to first activate the network and render it.') if len(sellist) == 0: self.unselect_all() return from numpy import array, append tmparr = array([]) for node in sellist: # check if it is a valid graph node id if node in graphnodes: # get the node id as integer j = int(node.lstrip('n'))-1 # extend empty array with node id tmparr = append(tmparr, j) self.rendermanager._select_nodes(selection_node_array = array(tmparr, dtype = int16), activate = activate) def get_selectiongraph_list(self): """ Returns a list of the node ids that were selected in the rendered scene. """ if self.datasourcemanager is None: raise Exception('No DatasourceManager. You have to first activate the network and render it.') import numpy as np sel_list = [] if not self.active: return sel_list selnodesarray = self.datasourcemanager._srcobj.selected_nodes # array with indices where the nodes are selected (==1) idx = np.where(selnodesarray == 1)[0] for i in idx: sel_list.append('n' + str(i + 1)) return sel_list def set_weight_key(self, weight_key = None): """ Sets the weight key in the graph representation of the network. Parameters ---------- weight_key : Str Must be a possible existing edge key """ if not weight_key is None: for u,v,d in self.graph.edges(data=True): self.graph[u][v]['weight']=d[weight_key] return True else: return False def get_matrix(self, weight_key = None): """ Returns the connectivity matrix of the network with the nodes ordered according to their id in the GraphML file. Parameters ---------- weight_key : Str Possible key value of the edges Returns ------- matrix : `Numpy.array` instance The connectivity matrix """ nr_nodes = len(self.graph.nodes()) if not weight_key is None: #FIXME: sanity check if weight_key exists # thanks to Aric Hagberg for u,v,d in self.graph.edges(data=True): self.graph[u][v]['weight']=d[weight_key] nodes = [(lambda nmod:'n'+str(nmod))(node) for node in range(1,nr_nodes + 1)] from networkx import to_numpy_matrix return to_numpy_matrix(self.graph, nodelist = nodes) def toggle_surface(self): """ Toggle the surface for the selected network nodes """ if self.rendermanager is None: raise Exception('No RenderManager. You have to first activate the network and render it.') self.rendermanager._toggle_surface() def show_surface(self): """ Shows the surface for the selected network nodes """ if self.rendermanager is None: raise Exception('No RenderManager. You have to first activate the network and render it.') self.rendermanager._show_surface() def load_pickled_graphml(self, graph): """ Loads a pickled GraphML file Parameters ---------- graph : NetworkX Graph instance A graph instance """ # setting the graph self.graph = graph if self.graph.has_node('n0'): if self.graph.node['n0'].has_key('nodekeys'): # extracting the node keys from the first node self.nodekeys = self.graph.node['n0']['nodekeys'] # extracting the edge keys from the first edge (without explanation) if self.graph.node['n0'].has_key('edgekeys'): self.edgekeys = self.graph.node['n0']['edgekeys'] if self.graph.node['n0'].has_key('graphid'): self.networkid = self.graph.node['n0']['graphid'] # remove node self.graph.remove_node('n0') def _return_default_edgevalue(self, edgekeys, key): """ Looks up if there is a default value defined, otherwise return zero """ if edgekeys[key].has_key('default'): return float(edgekeys[key]['default']) else: return 0.0 def parse_network_graphml(self, path): """ Read network in GraphML format from a path. Parameters ---------- path : string path the the GraphML file Returns ------- graph : NetworkX `Graph` """ import networkx as nx from networkx.utils import _get_fh from lxml import etree # Return a file handle for given path. # Path can be a string or a file handle. # Attempt to uncompress/compress files ending in .gz and .bz2. fh=_get_fh(path,mode='r') tree = etree.parse(fh) # get the root node from parsed lxml root = tree.getroot() # Schema Validation # http://codespeak.net/lxml/validation.html#xmlschema # define the namespace prefixes nsprefix = "{%s}" % root.nsmap[None] nsxlink = "{%s}" % root.nsmap['xlink'] nodekeys = {} edgekeys = {} defaultDirected = [True] # Parse the KEYs for child in root.iterchildren(): if child.tag == (nsprefix+'key'): attribs = child.attrib ddkeys = {} for mchildren in child: if mchildren.tag == (nsprefix+'default'): ddkeys['default'] = mchildren.text elif mchildren.tag == (nsprefix+'desc'): ddkeys['desc'] = mchildren.text if child.attrib['for'] == 'node': # Parse all the node keys # Read in the description and the default (if existing) # dict of dicts for nodes: key1: the id; key2: rest: attr.name, attr.type, desc, default nodekeys[attribs['id']] = {'attr.name' : attribs['attr.name'], \ 'attr.type' : attribs['attr.type']} # add default/desc keys if existing nodekeys[attribs['id']] = ddkeys elif child.attrib['for'] == 'edge': # Parse all the edge keys # Read in the description and the default (if existing) # dict of dicts for edges: key1: the id; key2: rest: attr.name, attr.type, desc, default edgekeys[attribs['id']] = {'attr.name' : attribs['attr.name'], \ 'attr.type' : attribs['attr.type']} # add default/desc keys if existing edgekeys[attribs['id']] = ddkeys else: logger.error("The 'for' attribute of key-tag not known, must be either node or edge") elif child.tag == (nsprefix+'graph'): # start parsing the graph into networkx data structure # create graph depending on (either AttrGraph or AttrDiGraph) # directionality: undirected/directed # version of networkx: # contains self-loops # edges have dicts # data per graph/node/edge for attr, value in child.items(): if attr == 'edgedefault' and value == 'undirected': defaultDirected[0] = False elif attr == 'id': graphid = value if defaultDirected[0]: G = nx.DiGraph() else: G = nx.Graph() # add id, nodekeys and edkeys as traits self.networkid = graphid self.nodekeys = nodekeys self.edgekeys = edgekeys # iterate over all nodes and edges for children in child.iterchildren(): if children.tag == (nsprefix+'node'): # parse the node for attr, value in children.items(): if attr == 'id': # add the node with corresponding id G.add_node(value) # keep node id to store attributes nodeid = value elif attr == (nsxlink+'href'): # add xlink to node dictionary G.node[nodeid]['xlink'] = value else: # node attribute not known logger.warning('The following node attribute is not known and thus discarded:'+ attr + ':' + value) # parse node data, add to node dict for data in children.iterchildren(): # read the keylabel, i.e. the data attribute name keylabel = data.attrib['key'] # is the keylabel in the list of allowed keys if nodekeys.has_key(keylabel): if not data.text == '': # add data to the node's dict G.node[nodeid][keylabel] = data.text else: # no data available, check if default value exists if nodekeys[keylabel].has_key('default'): # add default data to the node's dict G.node[nodeid][keylabel] = nodekeys[keylabel]['default'] logger.debug('Added default value '+ keylabel + ':' + nodekeys[keylabel]['default']) else: logger.warning('Nor data nor default value defined for ' + keylabel) # TODO: Work with exceptions! else: logger.warning("Data entry with key " + keylabel + " not defined.") elif children.tag == (nsprefix+'edge'): # parse the edge # parse its attributes for attr, value in children.items(): if attr == 'id': # no usage of edge id # add the edge with corresponding id src = children.attrib['source'] tar = children.attrib['target'] G.add_edge(src, tar) # keep dest and tar id to store attributes srcid = src tarid = tar elif attr == (nsxlink+'href'): # add xlink to edge dictionary G.edge[srcid][tarid]['xlink'] = value # parse data, and add to the edge dict for data in children.iterchildren(): # read the keylabel, i.e. the data attribute name keylabel = data.attrib['key'] # is the keylabel in the list of allowed keys if self.edgekeys.has_key(keylabel): if not data.text == '': # add data to the edge's dict, assume float!! G.edge[srcid][tarid][keylabel] = float(data.text) else: # no data available, check if default value exists G.edge[srcid][tarid][keylabel] = self._return_default_edgevalue(self.edgekeys, keylabel) data_keys = G.edge[srcid][tarid].keys() # check if we missed some edge keys that are available in the header for k, v in self.edgekeys.items(): if not k in data_keys: G.edge[srcid][tarid][k] = self._return_default_edgevalue(self.edgekeys, k) # return the generated network graph return G