NetworkX

Source code for networkx.algorithms.components.strongly_connected

# -*- coding: utf-8 -*-
"""
Strongly connected components.
"""
#    Copyright (C) 2004-2011 by
#    Aric Hagberg <hagberg@lanl.gov>
#    Dan Schult <dschult@colgate.edu>
#    Pieter Swart <swart@lanl.gov>
#    All rights reserved.
#    BSD license.
import networkx as nx
__authors__ = "\n".join(['Eben Kenah',
                         'Aric Hagberg (hagberg@lanl.gov)'
                         'Christopher Ellison',
                         'Ben Edwards (bedwards@cs.unm.edu)'])

__all__ = ['number_strongly_connected_components',
           'strongly_connected_components',
           'strongly_connected_component_subgraphs',
           'is_strongly_connected',
           'strongly_connected_components_recursive',
           'kosaraju_strongly_connected_components',
           'condensation']

[docs]def strongly_connected_components(G): """Return nodes in strongly connected components of graph. Parameters ---------- G : NetworkX Graph An directed graph. Returns ------- comp : list of lists A list of nodes for each component of G. The list is ordered from largest connected component to smallest. Raises ------ NetworkXError: If G is undirected. See Also -------- connected_components, weakly_connected_components Notes ----- Uses Tarjan's algorithm with Nuutila's modifications. Nonrecursive version of algorithm. References ---------- .. [1] Depth-first search and linear graph algorithms, R. Tarjan SIAM Journal of Computing 1(2):146-160, (1972). .. [2] On finding the strongly connected components in a directed graph. E. Nuutila and E. Soisalon-Soinen Information Processing Letters 49(1): 9-14, (1994).. """ if not G.is_directed(): raise nx.NetworkXError("""Not allowed for undirected graph G. Use connected_components() """) preorder={} lowlink={} scc_found={} scc_queue = [] scc_list=[] i=0 # Preorder counter for source in G: if source not in scc_found: queue=[source] while queue: v=queue[-1] if v not in preorder: i=i+1 preorder[v]=i done=1 v_nbrs=G[v] for w in v_nbrs: if w not in preorder: queue.append(w) done=0 break if done==1: lowlink[v]=preorder[v] for w in v_nbrs: if w not in scc_found: if preorder[w]>preorder[v]: lowlink[v]=min([lowlink[v],lowlink[w]]) else: lowlink[v]=min([lowlink[v],preorder[w]]) queue.pop() if lowlink[v]==preorder[v]: scc_found[v]=True scc=[v] while scc_queue and preorder[scc_queue[-1]]>preorder[v]: k=scc_queue.pop() scc_found[k]=True scc.append(k) scc_list.append(scc) else: scc_queue.append(v) scc_list.sort(key=len,reverse=True) return scc_list
[docs]def kosaraju_strongly_connected_components(G,source=None): """Return nodes in strongly connected components of graph. Parameters ---------- G : NetworkX Graph An directed graph. Returns ------- comp : list of lists A list of nodes for each component of G. The list is ordered from largest connected component to smallest. Raises ------ NetworkXError: If G is undirected See Also -------- connected_components Notes ----- Uses Kosaraju's algorithm. """ if not G.is_directed(): raise nx.NetworkXError("""Not allowed for undirected graph G. Use connected_components() """) components=[] G=G.reverse(copy=False) post=list(nx.dfs_postorder_nodes(G,source=source)) G=G.reverse(copy=False) seen={} while post: r=post.pop() if r in seen: continue c=nx.dfs_preorder_nodes(G,r) new=[v for v in c if v not in seen] seen.update([(u,True) for u in new]) components.append(new) components.sort(key=len,reverse=True) return components
[docs]def strongly_connected_components_recursive(G): """Return nodes in strongly connected components of graph. Recursive version of algorithm. Parameters ---------- G : NetworkX Graph An directed graph. Returns ------- comp : list of lists A list of nodes for each component of G. The list is ordered from largest connected component to smallest. Raises ------ NetworkXError : If G is undirected See Also -------- connected_components Notes ----- Uses Tarjan's algorithm with Nuutila's modifications. References ---------- .. [1] Depth-first search and linear graph algorithms, R. Tarjan SIAM Journal of Computing 1(2):146-160, (1972). .. [2] On finding the strongly connected components in a directed graph. E. Nuutila and E. Soisalon-Soinen Information Processing Letters 49(1): 9-14, (1994).. """ def visit(v,cnt): root[v]=cnt visited[v]=cnt cnt+=1 stack.append(v) for w in G[v]: if w not in visited: visit(w,cnt) if w not in component: root[v]=min(root[v],root[w]) if root[v]==visited[v]: component[v]=root[v] tmpc=[v] # hold nodes in this component while stack[-1]!=v: w=stack.pop() component[w]=root[v] tmpc.append(w) stack.remove(v) scc.append(tmpc) # add to scc list if not G.is_directed(): raise nx.NetworkXError("""Not allowed for undirected graph G. Use connected_components() """) scc=[] visited={} component={} root={} cnt=0 stack=[] for source in G: if source not in visited: visit(source,cnt) scc.sort(key=len,reverse=True) return scc
[docs]def strongly_connected_component_subgraphs(G): """Return strongly connected components as subgraphs. Parameters ---------- G : NetworkX Graph A graph. Returns ------- glist : list A list of graphs, one for each strongly connected component of G. See Also -------- connected_component_subgraphs Notes ----- The list is ordered from largest strongly connected component to smallest. Graph, node, and edge attributes are copied to the subgraphs. """ cc=strongly_connected_components(G) graph_list=[] for c in cc: graph_list.append(G.subgraph(c).copy()) return graph_list
[docs]def number_strongly_connected_components(G): """Return number of strongly connected components in graph. Parameters ---------- G : NetworkX graph A directed graph. Returns ------- n : integer Number of strongly connected components See Also -------- connected_components Notes ----- For directed graphs only. """ return len(strongly_connected_components(G))
[docs]def is_strongly_connected(G): """Test directed graph for strong connectivity. Parameters ---------- G : NetworkX Graph A directed graph. Returns ------- connected : bool True if the graph is strongly connected, False otherwise. See Also -------- strongly_connected_components Notes ----- For directed graphs only. """ if not G.is_directed(): raise nx.NetworkXError("""Not allowed for undirected graph G. See is_connected() for connectivity test.""") if len(G)==0: raise nx.NetworkXPointlessConcept( """Connectivity is undefined for the null graph.""") return len(strongly_connected_components(G)[0])==len(G)
[docs]def condensation(G, scc=None): """Returns the condensation of G. The condensation of G is the graph with each of the strongly connected components contracted into a single node. Parameters ---------- G : NetworkX DiGraph A directed graph. scc: list (optional, default=None) A list of strongly connected components. If provided, the elements in `scc` must partition the nodes in `G`. If not provided, it will be calculated as scc=nx.strongly_connected_components(G). Returns ------- C : NetworkX DiGraph The condensation of G. The node labels are integers corresponding to the index of the component in the list of strongly connected components. Raises ------ NetworkXError: If G is not directed Notes ----- After contracting all strongly connected components to a single node, the resulting graph is a directed acyclic graph. """ if not G.is_directed(): raise nx.NetworkXError("""Not allowed for undirected graph G. See is_connected() for connectivity test.""") if scc is None: scc = nx.strongly_connected_components(G) mapping = {} C = nx.DiGraph() for i,component in enumerate(scc): for n in component: mapping[n] = i C.add_nodes_from(range(len(scc))) for u,v in G.edges(): if mapping[u] != mapping[v]: C.add_edge(mapping[u],mapping[v]) return C