Source code for openmdao.util.graph

import networkx as nx
from collections import OrderedDict


[docs]class OrderedDigraph(nx.DiGraph): node_dict_factory = OrderedDict adjlist_dict_factory = OrderedDict edge_attr_dict_factory = OrderedDict
[docs]def collapse_nodes(graph, node_map, copy=False): """ Args ---- graph : nx.DiGraph Graph with nodes we want to collapse. node_map : dict A map of existing node names to collapsed names. copy : bool(False) If True, copy the graph before collapsing the nodes. Returns ------- nx.DiGraph The graph with the nodes collapsed. """ graph = nx.relabel_nodes(graph, node_map, copy=copy) # remove any self edges created by the relabeling graph.remove_edges_from([(u, v) for u, v in graph.edges_iter() if u == v]) return graph # plain_bfs is taken from networkx, but it isn't present in all versions, # so putting it here to make sure it's available. # # Copyright (C) 2004-2015 by # Aric Hagberg <hagberg@lanl.gov> # Dan Schult <dschult@colgate.edu> # Pieter Swart <swart@lanl.gov> # All rights reserved. # BSD license.
[docs]def plain_bfs(G, source): """A fast BFS node generator The direction of the edge between nodes is ignored. For directed graphs only. """ Gsucc = G.succ Gpred = G.pred seen = set() nextlevel = {source} while nextlevel: thislevel = nextlevel nextlevel = set() for v in thislevel: if v not in seen: yield v seen.add(v) nextlevel.update(Gsucc[v]) nextlevel.update(Gpred[v])
[docs]def break_strongly_connected(parent, broken_edges, scc): """ Breaks strongly connected components. Called recursively until all such cycles are broken. Args ---- parent : nx.DiGraph Directed graph from which the SCCs are drawn. broken_edges : list List to which broken edges are appended. scc : list List of nodes that make up a single SCC. """ sgraph = parent.subgraph(scc) max_node = None max_score = -1 in_smaller = False # Greedy Heuristic: look for the most asymmetrical (in terms of inputs vs # outputs) node and break the smallest set of connections for that node. for node in scc: din = sgraph.in_degree(node, weight='weight') dout = sgraph.out_degree(node, weight='weight') score = abs(din - dout) # Break ties lexicographically if max_node is None or score > max_score or \ (score == max_score and node < max_node): max_node = node max_score = score in_smaller = din <= dout if in_smaller: for p in sgraph.predecessors(max_node): sgraph.remove_edge(p, max_node) parent.remove_edge(p, max_node) broken_edges.append((p, max_node)) else: for s in sgraph.successors(max_node): sgraph.remove_edge(max_node, s) parent.remove_edge(max_node, s) broken_edges.append((max_node, s)) # This subgraph is no longer strongly connected, but there may be such # components remaining. remaining_sccs = (s for s in nx.strongly_connected_components(sgraph) if len(s) > 1) for sccs in remaining_sccs: break_strongly_connected(parent, broken_edges, sccs)