def condensation( G, components ): """Return the condensation of G with respect to the given components. Given components C_1 .. C_k which partition the nodes of G, the condensation graph cG has nodes <C_1> .. <C_k> and has edge (<C_i>,<C_j>) iff there was an edge in G from a node in C_i to a node in C_j. Note: the given components can be arbitrary, but if they are the strongly connected components (in the graph theory sense, not the dynamics sense) of G, then cG corresponds to the directed acyclic graph between strongly connected components. Parameters ---------- G : DiGraph components : list of lists of component nodes. Returns ------- cG : DiGraph The condensed graph. """ mapping = dict([(n,c) for c in range(len(components)) for n in components[c]]) cG = DiGraph() for u in mapping: cG.add_node(mapping[u]) for _,v in G.graph.edges_iter(u, data=False): if v not in components[mapping[u]]: cG.add_edge(mapping[u], mapping[v]) return cG
def condensation( G, components, loops=False ): """Return the condensation of G with respect to the given components. Given components C_1 .. C_k which partition the nodes of G, the condensation graph cG has nodes <C_1> .. <C_k> and has edge (<C_i>,<C_j>) iff there was an edge in G from a node in C_i to a node in C_j. Note: the given components can be arbitrary, but if they are the strongly connected components (in the graph theory sense, not the dynamics sense) of G, then cG corresponds to the directed acyclic graph between strongly connected components. Parameters ---------- G : DiGraph components : list of lists of component nodes, or dictionary of component label -> component nodes loops : whether to allow condensed nodes to have self loops (default: False) Returns ------- cG : DiGraph The condensed graph. """ # convert list to dict if isinstance(components,list): components = {c:components[c] for c in range(len(components))} mapping = {n:c for c in components for n in components[c]} cG = DiGraph() for u in mapping.keys(): cG.add_node(mapping[u]) for v in G.successors(u): # if v~u and u,v are in the same component, don't add the loop # (unless we allow loops) if loops or v not in components[mapping[u]]: cG.add_edge(mapping[u], mapping[v]) return cG