def main(): powerg = ig.Graph.Read_GML('data/power.gml') powerx = nx.Graph() powerx.add_nodes_from(powerg.vs.indices) powerx.add_edges_from([(x.source, x.target) for x in powerg.es]) work_graph = nx.Graph(powerx) deg_dict = nx.degree(work_graph) iteration = 0 k = 2 while min(deg_dict.values()) < k: deg2_nodes = [n for n, d in deg_dict.items() if d > k - 1] work_graph = nx.Graph(work_graph.subgraph(deg2_nodes)) deg_dict = nx.degree(work_graph) iteration += 1 print("Interation {0}. length deg_dict = {1}".format( iteration, len(deg_dict))) nodes_to_remove = set() iteration = 0 for nodes in nx.all_node_cuts(work_graph, 1): sys.stdout.write("{0}\r".format(iteration)) iteration += 1 nodes_to_remove = nodes_to_remove.union(nodes) sys.stdout.write("\n") nodes_to_keep = set(work_graph.nodes()) - nodes_to_remove work_graph = nx.Graph(work_graph.subgraph(list(nodes_to_keep))) components = list(nx.connected_components(work_graph)) largest_component = max(components, key=lambda x: len(x)) work_graph = nx.Graph(work_graph.subgraph(list(largest_component))) save_nx_as_gml(work_graph, "data/large_power_2_connected.gml") k = 3 iteration = 0 while min(deg_dict.values()) < k: deg3_nodes = [n for n, d in deg_dict.items() if d > k - 1] work_graph = nx.Graph(work_graph.subgraph(deg3_nodes)) deg_dict = nx.degree(work_graph) iteration += 1 print("Interation {0}. length deg_dict = {1}".format( iteration, len(deg_dict))) nodes_to_keep = set(work_graph.nodes()) - nodes_to_remove work_graph = nx.Graph(work_graph.subgraph(list(nodes_to_keep))) components = list(nx.connected_components(work_graph)) largest_component = max(components, key=lambda x: len(x)) work_graph = nx.Graph(work_graph.subgraph(list(largest_component))) iteration = 0 nodes_to_remove = set() for nodes in nx.all_node_cuts(work_graph, 2): sys.stdout.write("{0}\r".format(iteration)) iteration += 1 nodes_to_remove = nodes_to_remove.union(nodes) if not nodes_to_remove: save_nx_as_gml(work_graph, "data/power_subset.gml") return
def test_articulation_points(): Ggen = _generate_no_biconnected() for i in range(2): G = next(Ggen) articulation_points = list({a} for a in nx.articulation_points(G)) for cut in nx.all_node_cuts(G): assert_true(cut in articulation_points)
def test_articulation_points(): Ggen = _generate_no_biconnected() for i in range(1): # change 1 to 3 or more for more realizations. G = next(Ggen) articulation_points = list({a} for a in nx.articulation_points(G)) for cut in nx.all_node_cuts(G): assert_true(cut in articulation_points)
def test_cycle_graph(): G = nx.cycle_graph(5) solution = [{0, 2}, {0, 3}, {1, 3}, {1, 4}, {2, 4}] cuts = list(nx.all_node_cuts(G)) assert_true(len(solution) == len(cuts)) for cut in cuts: assert_true(cut in solution)
def test_complete_graph(): G = nx.complete_graph(5) solution = [{0, 1, 2, 3}, {0, 1, 2, 4}, {0, 1, 3, 4}, {0, 2, 3, 4}, {1, 2, 3, 4}] cuts = list(nx.all_node_cuts(G)) assert len(solution) == len(cuts) for cut in cuts: assert cut in solution
def P(A, E, V, G): G_A = G.subgraph(A) cutsets = list(nx.all_node_cuts(G_A)) cutvertices = [] for i in cutsets: if (len(i) == 1): cutvertices.append(i.pop()) return cutvertices
def _check_separating_sets(G): for Gc in nx.connected_component_subgraphs(G): if len(Gc) < 3: continue for cut in nx.all_node_cuts(Gc): assert_equal(nx.node_connectivity(Gc), len(cut)) H = Gc.copy() H.remove_nodes_from(cut) assert_false(nx.is_connected(H))
def test_grid_2d_graph(): # All minimum node cuts of a 2d grid # are the four pairs of nodes that are # neighbors of the four corner nodes. G = nx.grid_2d_graph(5, 5) solution = [{(0, 1), (1, 0)}, {(3, 0), (4, 1)}, {(3, 4), (4, 3)}, {(0, 3), (1, 4)}] for cut in nx.all_node_cuts(G): assert cut in solution
def test_alternative_flow_functions(): graph_funcs = [graph_example_1, nx.davis_southern_women_graph] for graph_func in graph_funcs: G = graph_func() for flow_func in flow_funcs: for cut in nx.all_node_cuts(G, flow_func=flow_func): assert_equal(nx.node_connectivity(G), len(cut)) H = G.copy() H.remove_nodes_from(cut) assert_false(nx.is_connected(H))
def test_alternative_flow_functions(): graphs = [nx.grid_2d_graph(4, 4), nx.cycle_graph(5)] for G in graphs: node_conn = nx.node_connectivity(G) for flow_func in flow_funcs: all_cuts = nx.all_node_cuts(G, flow_func=flow_func) # Only test a limited number of cut sets to reduce test time. for cut in itertools.islice(all_cuts, MAX_CUTSETS_TO_TEST): assert_equal(node_conn, len(cut)) assert_false(nx.is_connected(nx.restricted_view(G, cut, [])))
def test_alternative_flow_functions(): graphs = [nx.grid_2d_graph(4, 4), nx.cycle_graph(5)] for G in graphs: node_conn = nx.node_connectivity(G) for flow_func in flow_funcs: all_cuts = nx.all_node_cuts(G, flow_func=flow_func) for cut in all_cuts: assert_equal(node_conn, len(cut)) H = G.copy() H.remove_nodes_from(cut) assert_false(nx.is_connected(H))
def _check_separating_sets(G): for cc in nx.connected_components(G): if len(cc) < 3: continue Gc = G.subgraph(cc) node_conn = nx.node_connectivity(Gc) all_cuts = nx.all_node_cuts(Gc) # Only test a limited number of cut sets to reduce test time. for cut in itertools.islice(all_cuts, MAX_CUTSETS_TO_TEST): assert_equal(node_conn, len(cut)) assert_false(nx.is_connected(nx.restricted_view(G, cut, [])))
def cut_sets(self): """Return list of cut sets. Each cut set is a set of nodes describing a sub-graph G'. Removing all the edges of G' from the graph disconnects it. This will fail if there are unconnected components.""" # It may be better to return a list of sets of edges. if hasattr(self, '_cutsets'): return self._cutsets self._cutsets = list(nx.all_node_cuts(self.G)) return self._cutsets
def find_case_1_two_connected_subgraphs(g, q, verbose=False): """Find subgraphs of g that has a single separating pair (ie, that is suitable for the first 2-connected case in section 3.2 of the DBCP paper. Each subgraph Gi must have |Vi| < |V|(q-1)/q. Make a generating function that returns valid subgraphs and nodes. """ g = remove_degree_x_vertices(g, 1, verbose) all_nodes = g.nodes() size = len(all_nodes) q = float(q) max_subgraph_size = (q - 1.0) / q * size if any(nx.all_node_cuts(g, 1)): raise GraphNot2ConnectedException() min_size = float('inf') found_one = False for nodes in nx.all_node_cuts(g, 2): sub_graph_nodes = [n for n in all_nodes if n not in nodes] disconnected_graph = nx.Graph(g.subgraph(sub_graph_nodes)) try: for component in nx.connected_components(disconnected_graph): if verbose: print("Subcomponent has size {0}".format(len(component))) if len(component) >= max_subgraph_size: print("{} is too big!".format(len(component))) min_size = min(min_size, len(component)) raise SubgraphTooBigException() except SubgraphTooBigException: print("For nodes {0} found a subgraph > (q-1)/q*size = {1}".format( nodes, max_subgraph_size)) continue found_one = True yield (nodes, disconnected_graph) if not found_one: print("Found no suitable components. Smallest one was {}".format( min_size)) return
def _check_separating_sets(G): for Gc in nx.connected_component_subgraphs(G): if len(Gc) < 3: continue node_conn = nx.node_connectivity(Gc) all_cuts = nx.all_node_cuts(Gc) # Only test a limited number of cut sets to reduce # test time. for cut in itertools.islice(all_cuts, MAX_CUTSETS_TO_TEST): assert_equal(node_conn, len(cut)) H = Gc.copy() H.remove_nodes_from(cut) assert_false(nx.is_connected(H))
def test_complete_graph(): G = nx.complete_graph(5) solution = [ set([0, 1, 2, 3]), set([0, 1, 2, 4]), set([0, 1, 3, 4]), set([0, 2, 3, 4]), set([1, 2, 3, 4]), ] cuts = list(nx.all_node_cuts(G)) assert_true(len(solution) == len(cuts)) for cut in cuts: assert_true(cut in solution)
def test_cycle_graph(): G = nx.cycle_graph(5) solution = [ set([0, 2]), set([0, 3]), set([1, 3]), set([1, 4]), set([2, 4]) ] cuts = list(nx.all_node_cuts(G)) assert_true(len(solution) == len(cuts)) for cut in cuts: assert_true(cut in solution)
def test_grid_2d_graph(): # All minimum node cuts of a 2d grid # are the four pairs of nodes that are # neighbors of the four corner nodes. G = nx.grid_2d_graph(5, 5) solution = [ set([(0, 1), (1, 0)]), set([(3, 0), (4, 1)]), set([(3, 4), (4, 3)]), set([(0, 3), (1, 4)]), ] for cut in nx.all_node_cuts(G): assert_true(cut in solution)
def test_complete_graph(): G = nx.complete_graph(5) solution = [ {0, 1, 2, 3}, {0, 1, 2, 4}, {0, 1, 3, 4}, {0, 2, 3, 4}, {1, 2, 3, 4}, ] cuts = list(nx.all_node_cuts(G)) assert_true(len(solution) == len(cuts)) for cut in cuts: assert_true(cut in solution)
def test_non_repeated_cuts(): # The algorithm was repeating the cut {0, 1} for the giant biconnected # component of the Karate club graph. K = nx.karate_club_graph() G = max(list(nx.biconnected_component_subgraphs(K)), key=len) solution = [{32, 33}, {2, 33}, {0, 3}, {0, 1}, {29, 33}] cuts = list(nx.all_node_cuts(G)) if len(solution) != len(cuts): print(nx.info(G)) print("Solution: {}".format(solution)) print("Result: {}".format(cuts)) assert_true(len(solution) == len(cuts)) for cut in cuts: assert_true(cut in solution)
def test_non_repeated_cuts(): # The algorithm was repeating the cut {0, 1} for the giant biconnected # component of the Karate club graph. K = nx.karate_club_graph() bcc = max(list(nx.biconnected_components(K)), key=len) G = K.subgraph(bcc) solution = [{32, 33}, {2, 33}, {0, 3}, {0, 1}, {29, 33}] cuts = list(nx.all_node_cuts(G)) if len(solution) != len(cuts): print(nx.info(G)) print(f"Solution: {solution}") print(f"Result: {cuts}") assert len(solution) == len(cuts) for cut in cuts: assert cut in solution
def find_two_connected_subgraphs(g, verbose=False): """Find the largest 2-connected subgraph of the graph passed in.""" work_graph = remove_degree_x_vertices(g, 1, verbose) nodes_to_remove = set() iteration = 0 for nodes in nx.all_node_cuts(work_graph, 1): if verbose: sys.stdout.write("{0}\r".format(iteration)) iteration += 1 nodes_to_remove = nodes_to_remove.union(nodes) if verbose: sys.stdout.write("\n") if nodes_to_remove: nodes_to_keep = set(work_graph.nodes()) - nodes_to_remove work_graph = nx.Graph(work_graph.subgraph(list(nodes_to_keep))) components = list(nx.connected_components(work_graph)) for component in components: yield (nx.Graph(work_graph.subgraph(list(component)))) return
def big_cut_reduction(Graph, MIS, big_cut_dict): cuts = nx.all_node_cuts(Graph, 1) for i in cuts: node = list(i)[0] break G = copy.deepcopy(Graph) G.remove_node(node) G_list = get_subgraph(G) neighbors = nx.all_neighbors(Graph, node) for G_sub in G_list: G_sub.add_node(node, weight=Graph.nodes[node]['weight']) for i in neighbors: for G_sub in G_list: if i in G_sub.nodes: G_sub.add_edge(node, i) flag = 0 new_weight = Graph.nodes[node]['weight'] for i in range(len(G_list)): G_sub = G_list[i] G_sub.nodes[node]['weight'] = new_weight S1, s1 = Cut(G_sub) if node in S1: S1.remove(node) G_sub.remove_node(node) S0, s0 = Cut(G_sub) if s0 >= s1: MIS += S0 if node in big_cut_dict: MIS += big_cut_dict[node][0] _ = big_cut_dict.pop(node) if i + 1 < len(G_list): flag = 1 break else: new_weight = s1 - s0 for u in S0: if node not in big_cut_dict: big_cut_dict[node] = {0: [], 1: []} big_cut_dict[node][0] += [u] if u in big_cut_dict: if u in S1: continue else: big_cut_dict[node][0] += big_cut_dict[u][1] big_cut_dict[node][1] += big_cut_dict[u][0] for w in S1: if node not in big_cut_dict: big_cut_dict[node] = {0: [], 1: []} big_cut_dict[node][1] += [w] if w in big_cut_dict: if w in S0: continue else: big_cut_dict[node][1] += big_cut_dict[w][1] big_cut_dict[node][0] += big_cut_dict[w][0] for u in S0: if u in big_cut_dict: _ = big_cut_dict.pop(u) for w in S1: if w in big_cut_dict: _ = big_cut_dict.pop(w) if flag == 1: for j in range(i + 1, len(G_list)): G_new = G_list[j] G_new.remove_node(node) S, _ = Cut(G_new) MIS += S return 0
def test_disconnected_graph(): G = nx.fast_gnp_random_graph(100, 0.01) cuts = nx.all_node_cuts(G) assert_raises(nx.NetworkXError, next, cuts)
def k_components(G, flow_func=None): r"""Returns the k-component structure of a graph G. A `k`-component is a maximal subgraph of a graph G that has, at least, node connectivity `k`: we need to remove at least `k` nodes to break it into more components. `k`-components have an inherent hierarchical structure because they are nested in terms of connectivity: a connected graph can contain several 2-components, each of which can contain one or more 3-components, and so forth. Parameters ---------- G : NetworkX graph flow_func : function Function to perform the underlying flow computations. Default value :meth:`edmonds_karp`. This function performs better in sparse graphs with right tailed degree distributions. :meth:`shortest_augmenting_path` will perform better in denser graphs. Returns ------- k_components : dict Dictionary with all connectivity levels `k` in the input Graph as keys and a list of sets of nodes that form a k-component of level `k` as values. Raises ------ NetworkXNotImplemented: If the input graph is directed. Examples -------- >>> # Petersen graph has 10 nodes and it is triconnected, thus all >>> # nodes are in a single component on all three connectivity levels >>> G = nx.petersen_graph() >>> k_components = nx.k_components(G) Notes ----- Moody and White [1]_ (appendix A) provide an algorithm for identifying k-components in a graph, which is based on Kanevsky's algorithm [2]_ for finding all minimum-size node cut-sets of a graph (implemented in :meth:`all_node_cuts` function): 1. Compute node connectivity, k, of the input graph G. 2. Identify all k-cutsets at the current level of connectivity using Kanevsky's algorithm. 3. Generate new graph components based on the removal of these cutsets. Nodes in a cutset belong to both sides of the induced cut. 4. If the graph is neither complete nor trivial, return to 1; else end. This implementation also uses some heuristics (see [3]_ for details) to speed up the computation. See also -------- node_connectivity all_node_cuts biconnected_components : special case of this function when k=2 k_edge_components : similar to this function, but uses edge-connectivity instead of node-connectivity References ---------- .. [1] Moody, J. and D. White (2003). Social cohesion and embeddedness: A hierarchical conception of social groups. American Sociological Review 68(1), 103--28. http://www2.asanet.org/journals/ASRFeb03MoodyWhite.pdf .. [2] Kanevsky, A. (1993). Finding all minimum-size separating vertex sets in a graph. Networks 23(6), 533--541. http://onlinelibrary.wiley.com/doi/10.1002/net.3230230604/abstract .. [3] Torrents, J. and F. Ferraro (2015). Structural Cohesion: Visualization and Heuristics for Fast Computation. http://arxiv.org/pdf/1503.04476v1 """ # Dictionary with connectivity level (k) as keys and a list of # sets of nodes that form a k-component as values. Note that # k-compoents can overlap (but only k - 1 nodes). k_components = defaultdict(list) # Define default flow function if flow_func is None: flow_func = default_flow_func # Bicomponents as a base to check for higher order k-components for component in nx.connected_components(G): # isolated nodes have connectivity 0 comp = set(component) if len(comp) > 1: k_components[1].append(comp) bicomponents = list(nx.biconnected_component_subgraphs(G)) for bicomponent in bicomponents: bicomp = set(bicomponent) # avoid considering dyads as bicomponents if len(bicomp) > 2: k_components[2].append(bicomp) for B in bicomponents: if len(B) <= 2: continue k = nx.node_connectivity(B, flow_func=flow_func) if k > 2: k_components[k].append(set(B.nodes())) # Perform cuts in a DFS like order. cuts = list(nx.all_node_cuts(B, k=k, flow_func=flow_func)) stack = [(k, _generate_partition(B, cuts, k))] while stack: (parent_k, partition) = stack[-1] try: nodes = next(partition) C = B.subgraph(nodes) this_k = nx.node_connectivity(C, flow_func=flow_func) if this_k > parent_k and this_k > 2: k_components[this_k].append(set(C.nodes())) cuts = list(nx.all_node_cuts(C, k=this_k, flow_func=flow_func)) if cuts: stack.append((this_k, _generate_partition(C, cuts, this_k))) except StopIteration: stack.pop() # This is necessary because k-components may only be reported at their # maximum k level. But we want to return a dictionary in which keys are # connectivity levels and values list of sets of components, without # skipping any connectivity level. Also, it's possible that subsets of # an already detected k-component appear at a level k. Checking for this # in the while loop above penalizes the common case. Thus we also have to # _consolidate all connectivity levels in _reconstruct_k_components. return _reconstruct_k_components(k_components)
def compute_3_connected_k_dominating_set(G, D): graphCDS = G.subgraph(D) if nx.node_connectivity(graphCDS) < 2: print "Input is not 2-connected.Exiting" return D separators = list(nx.all_node_cuts(graphCDS)) # print "separators" # print separators while separators and nx.node_connectivity(graphCDS) < 3: broken = False discD = list(set(D) - separators[0]) # print "discD" # print discD temp_graph_D = G.subgraph(discD) genD = nx.connected_components(temp_graph_D) components = list(genD) # print "Components" # print components for v in components[0]: tempD = list(D) tempD.remove(v) for u in components[1]: tempD.remove(u) newG = G.copy() newG.remove_nodes_from(tempD) if nx.has_path(newG, v, u): Hpath = nx.shortest_path(newG, v, u) # print "Hpath is", # print Hpath broken = True break if broken: break for node in Hpath: if node not in D: D.append(node) graphCDS = G.subgraph(D) separators = list(nx.all_node_cuts(graphCDS)) # print "separators" # print separators return D
# In[4]: qzo = [[0 if i == 0. else 1 for i in q[j]] for j in range(len(q))] # In[20]: A = nx.adjacency_matrix(Gvc) # In[6]: A # In[27]: t = list(nx.all_node_cuts(Gvc)) # In[22]: t # In[28]: Gvc.remove_node(16) Gvc.remove_node(25) Gvc.remove_node(26) Gvc.remove_node(27) # In[29]: nx.draw_networkx(Gvc)
#ap = nv.BasePlot(G, node_grouping='cluster', node_color = 'connectivity', node_size = 'cluster', node_label='connectivity') color = nx.betweenness_centrality(G, weight='count').values() nx.draw(G, cmap=plt.get_cmap('jet'), alpha=0.4, node_order='count', node_color=color, node_size=65, with_labels=True, font_size=8, radius=20) #ap.draw() #plt.show() plt.savefig('network.png', dpi=300) cutsets = list(nx.all_node_cuts(G)) len(cutsets) # In[10]: all(1 == len(cutset) for cutset in cutsets) # In[11]: color # In[12]: color.items()
def test_disconnected_graph(): G = nx.fast_gnp_random_graph(100, 0.01, seed=42) cuts = nx.all_node_cuts(G) pytest.raises(nx.NetworkXError, next, cuts)
def k_components(G, flow_func=None): r"""Returns the k-component structure of a graph G. A `k`-component is a maximal subgraph of a graph G that has, at least, node connectivity `k`: we need to remove at least `k` nodes to break it into more components. `k`-components have an inherent hierarchical structure because they are nested in terms of connectivity: a connected graph can contain several 2-components, each of which can contain one or more 3-components, and so forth. Parameters ---------- G : NetworkX graph flow_func : function Function to perform the underlying flow computations. Default value :meth:`edmonds_karp`. This function performs better in sparse graphs with right tailed degree distributions. :meth:`shortest_augmenting_path` will perform better in denser graphs. Returns ------- k_components : dict Dictionary with all connectivity levels `k` in the input Graph as keys and a list of sets of nodes that form a k-component of level `k` as values. Raises ------ NetworkXNotImplemented: If the input graph is directed. Examples -------- >>> # Petersen graph has 10 nodes and it is triconnected, thus all >>> # nodes are in a single component on all three connectivity levels >>> G = nx.petersen_graph() >>> k_components = nx.k_components(G) Notes ----- Moody and White [1]_ (appendix A) provide an algorithm for identifying k-components in a graph, which is based on Kanevsky's algorithm [2]_ for finding all minimum-size node cut-sets of a graph (implemented in :meth:`all_node_cuts` function): 1. Compute node connectivity, k, of the input graph G. 2. Identify all k-cutsets at the current level of connectivity using Kanevsky's algorithm. 3. Generate new graph components based on the removal of these cutsets. Nodes in a cutset belong to both sides of the induced cut. 4. If the graph is neither complete nor trivial, return to 1; else end. This implementation also uses some heuristics (see [3]_ for details) to speed up the computation. See also -------- node_connectivity all_node_cuts biconnected_components : special case of this function when k=2 k_edge_components : similar to this function, but uses edge-connectivity instead of node-connectivity References ---------- .. [1] Moody, J. and D. White (2003). Social cohesion and embeddedness: A hierarchical conception of social groups. American Sociological Review 68(1), 103--28. http://www2.asanet.org/journals/ASRFeb03MoodyWhite.pdf .. [2] Kanevsky, A. (1993). Finding all minimum-size separating vertex sets in a graph. Networks 23(6), 533--541. http://onlinelibrary.wiley.com/doi/10.1002/net.3230230604/abstract .. [3] Torrents, J. and F. Ferraro (2015). Structural Cohesion: Visualization and Heuristics for Fast Computation. https://arxiv.org/pdf/1503.04476v1 """ # Dictionary with connectivity level (k) as keys and a list of # sets of nodes that form a k-component as values. Note that # k-compoents can overlap (but only k - 1 nodes). k_components = defaultdict(list) # Define default flow function if flow_func is None: flow_func = default_flow_func # Bicomponents as a base to check for higher order k-components for component in nx.connected_components(G): # isolated nodes have connectivity 0 comp = set(component) if len(comp) > 1: k_components[1].append(comp) bicomponents = list(nx.biconnected_component_subgraphs(G)) for bicomponent in bicomponents: bicomp = set(bicomponent) # avoid considering dyads as bicomponents if len(bicomp) > 2: k_components[2].append(bicomp) for B in bicomponents: if len(B) <= 2: continue k = nx.node_connectivity(B, flow_func=flow_func) if k > 2: k_components[k].append(set(B.nodes())) # Perform cuts in a DFS like order. cuts = list(nx.all_node_cuts(B, k=k, flow_func=flow_func)) stack = [(k, _generate_partition(B, cuts, k))] while stack: (parent_k, partition) = stack[-1] try: nodes = next(partition) C = B.subgraph(nodes) this_k = nx.node_connectivity(C, flow_func=flow_func) if this_k > parent_k and this_k > 2: k_components[this_k].append(set(C.nodes())) cuts = list(nx.all_node_cuts(C, k=this_k, flow_func=flow_func)) if cuts: stack.append((this_k, _generate_partition(C, cuts, this_k))) except StopIteration: stack.pop() # This is necessary because k-components may only be reported at their # maximum k level. But we want to return a dictionary in which keys are # connectivity levels and values list of sets of components, without # skipping any connectivity level. Also, it's possible that subsets of # an already detected k-component appear at a level k. Checking for this # in the while loop above penalizes the common case. Thus we also have to # _consolidate all connectivity levels in _reconstruct_k_components. return _reconstruct_k_components(k_components)
def reduction_rule_6(graph, oct_set): """ node cuts of size 2 can be handled. """ # print("Entered RR6") # find cut of size 2: {u, v} # find vertex sets C1, ..., Cn of connected component in G-{u,v} # If Ci is bipartite: # If Ci + {u, v} is bipartite, handle # If Ci + {u} or Ci + {v} is bipartite, handle # Loop until no cuts of size 2 remain changed = False updated = True while (updated): updated = False # If the graph isn't 2-connected or if it's a clique on three vertices, # return the graph if graph.order() == 0 or \ nx.node_connectivity(graph) != 2 or \ graph.order() == 3: return changed, graph, oct_set # Else find a cut vertex and proceed node_u, node_v = list(nx.all_node_cuts(graph, k=2))[0] node_cuts = sort_collection_of_sets(nx.all_node_cuts(graph, k=2)) while node_cuts and not updated: node_u, node_v = node_cuts.pop() remaining_nodes = set(graph.nodes()) remaining_nodes.remove(node_u) remaining_nodes.remove(node_v) component_views = sort_collection_of_sets( nx.connected_components(graph.subgraph(remaining_nodes))) while component_views and not updated: # Find the nodes in a component of G - {u, v} component_nodes = set(component_views.pop()) # The original component needs to be bipartite if nx.is_bipartite(graph.subgraph(component_nodes)): # Case 1: Component + u + v is bipartite component_nodes.add(node_u) component_nodes.add(node_v) if nx.is_bipartite(graph.subgraph(component_nodes)): if len(component_nodes) > 3: changed = True updated = True # Replace the component with one of equal parity. # Check this by checking a 2-coloring of the # component: If the neighborhood of {u, v} is one # color then replace with a single new vertex. # Otherwise replace with an edge. # # print("component nodes before coloring:", component_nodes) subgraph = graph.subgraph(component_nodes) # # print("Subgraph: Nodes {} Edges {}".format(subgraph.nodes(), subgraph.edges())) # # print("Connected?", nx.is_connected(graph.subgraph(component_nodes))) coloring = nx.algorithms.bipartite.color( graph.subgraph(component_nodes)) component_nodes.remove(node_u) component_nodes.remove(node_v) # # print("Coloring:",coloring) # # print("v: {} u: {}".format(node_v, node_u)) if coloring[node_u] == coloring[node_v]: new_midpoint = component_nodes.pop() og_name_lookup = nx.get_node_attributes( graph, 'og_name') og_name = og_name_lookup[new_midpoint] graph.remove_node(new_midpoint) graph.add_node(new_midpoint, og_name=og_name) graph.add_edge(node_u, new_midpoint) graph.add_edge(node_v, new_midpoint) else: graph.add_edge(node_u, node_v) # In both cases, remove the component # Update the preprocessing statistics graph.graph['vertices_removed'] +=\ len(component_nodes) graph.remove_nodes_from(component_nodes) continue # Case 2: Component + u is bipartite component_nodes.remove(node_v) if nx.is_bipartite(graph.subgraph(component_nodes)): changed = True updated = True # Add v to OCT set og_name_lookup = nx.get_node_attributes( graph, 'og_name') oct_set.add(og_name_lookup[node_v]) graph.remove_node(node_v) # Remove this component component_nodes.remove(node_u) graph.remove_nodes_from(component_nodes) # Update preprocessing statistics graph.graph['oct'] += 1 graph.graph['vertices_removed'] += len(component_nodes) continue # Case 3: Component + v is bipartite component_nodes.remove(node_u) component_nodes.add(node_v) if nx.is_bipartite(graph.subgraph(component_nodes)): changed = True updated = True # Add u to OCT set og_name_lookup = nx.get_node_attributes( graph, 'og_name') oct_set.add(og_name_lookup[node_u]) graph.remove_node(node_u) # Remove component component_nodes.remove(node_v) graph.remove_nodes_from(component_nodes) # Update preprocessing statistics graph.graph['oct'] += 1 graph.graph['vertices_removed'] += len(component_nodes) continue # print("Exited RR6") return changed, graph, oct_set
def reduction_rule_4(graph, oct_set): """ Cut-vertices and their resulting components are examined. Note: OCT set may be updated. """ # print("Entered RR4") # May need to remove multiple cut vertices changed = False updated = True while (updated): updated = False # Return if no cut vertex if graph.order() == 0 or nx.node_connectivity(graph) != 1: return changed, graph, oct_set # Else find a cut vertex and proceed # nx.all_node_cuts returns a "list" of "sets" # We know the sets are of size 1 # So pull out the first set and store its only item in node_v node_cuts = sort_collection_of_sets(nx.all_node_cuts(graph, k=1)) while node_cuts and not updated: # Pull off the next cut vertex node_v = node_cuts.pop().pop() # Compute remaining nodes remaining_nodes = set(graph.nodes()) remaining_nodes.remove(node_v) # Range over the resulting components components = sort_collection_of_sets( nx.connected_components(graph.subgraph(remaining_nodes))) while components and not updated: component_nodes = set(components.pop()) # Original component needs to be bipartite if nx.is_bipartite(graph.subgraph(component_nodes)): changed = True updated = True # Case 1: Component + v is bipartite component_nodes.add(node_v) if nx.is_bipartite(graph.subgraph(component_nodes)): component_nodes.remove(node_v) # Update the preprocessing statistics graph.graph['vertices_removed'] += len(component_nodes) graph.remove_nodes_from(component_nodes) # Case 2: Only component is bipartite else: # Update the preprocessing statistics graph.graph['oct'] += 1 og_name_lookup = nx.get_node_attributes( graph, 'og_name') oct_set.add(og_name_lookup[node_v]) graph.remove_node(node_v) # print("Exited RR4") return changed, graph, oct_set