def test_transitive_reduction(): # reduce # 1 -> 2 -> 3 # | | # ---->---- # in # 1 -> 2 -> 3 G = nx.DiGraph() G.add_edges_from([(1, 2), (2, 3), (1, 3)]) G1 = transitive_reduction(G) assert json_graph.node_link_data(G1)['links'] == [{'source': 1, 'target': 2}, {'source': 2, 'target': 3}] # reduce # <- 1 -> # | | # 2 --<-- 4 # | | # -> 3 -> # error : loop G = nx.DiGraph() G.add_edges_from([(1, 2), (2, 3), (3, 4), (1, 4), (4, 2)]) with pytest.raises(NetworkXError) as excinfo: G1 = transitive_reduction(G) assert 'Transitive reduction only uniquely defined on directed acyclic graphs' in str(excinfo.value)
def build_dag(fname: str): """ build DAG with Networkx Parameters ---------- fname : file containing dependencies query parents req3.sql req1.sql,req2.sql Returns ------- G : directed graph """ df = pd.read_csv(fname, sep=' ', names=["node", "parents"], header='infer') # build DAG G = nx.DiGraph() for idx, row in df.iterrows(): node = row["node"] for parent in row["parents"].split(','): G.add_edge(parent, node) # reduce graph G = transitive_reduction(G) assert is_directed_acyclic_graph(G), "check it is a DAG" return G
def hasse(pairs_in, thresh): """ Draw a hasse diagram showing which treatments/expcombs have significantly differences from each other. """ from networkx.algorithms.dag import transitive_reduction from networkx.drawing.nx_pylab import draw_networkx from networkx.drawing.nx_agraph import graphviz_layout import matplotlib.pyplot as plt pvalmat, orig_scores, docs = load_pairs_in(pairs_in) digraph = mk_sd_graph(pvalmat, thresh) digraph = transitive_reduction(digraph) layout = graphviz_layout(digraph, prog="dot") draw_networkx(digraph, pos=layout) plt.show()
def statistics(graph, reduce=True, verb=True): stats = dict() # perform transitive reduction if reduce: from networkx.algorithms.dag import transitive_reduction reduced = transitive_reduction(graph) stats['reduced'] = reduced # git the extra edges that we honestly don't need superfluous_edges = graph.edges() - reduced.edges() stats['superfluous_edges'] = superfluous_edges # if verb: # print(superfluous_edges) else: stats['reduced'] = graph # find any cycles from networkx.algorithms.cycles import simple_cycles cycles = list(simple_cycles(stats['reduced'])) if verb and len(cycles) > 0: print("Found {0} cycles".format(len(cycles))) elif verb: print("Graph is cycle-free") stats['cycles'] = cycles # calculate connected components # from networkx.algorithms.components import connected_components components = list(nx.weakly_connected_components(stats['reduced'])) if verb: print("{0} connected component{1}".format( len(components), "" if len(components) == 1 else "s")) stats['components'] = components # return all the stats we calclulated return stats
def dag(nodenum, initial_edges, ranks_wanted): if nodenum == 0: return { 'cocos': [], 'edges': { 'present': [], 'removed': [], 'closure': [], 'closing': [], 'forbidden': [] }, 'r_to_pq': [] } if nodenum == 1: return { 'cocos': [{ 'nodes': [{ 'q': 0, 'r': 0, 'x': 0, 'y': 0, 'rank': { 'min': 0, 'max': 0 } }], 'edges': [], 'svg_size': { 'x': 0, 'y': 0 }, 'longest_path_length': 0 }], 'edges': { 'present': [], 'removed': [], 'closure': [], 'closing': [], 'forbidden': [] }, 'r_to_pq': [[0, 0]] } nodes = range(nodenum) graph = nx.DiGraph() graph.add_nodes_from(nodes) graph.add_edges_from(initial_edges) if not is_directed_acyclic_graph(graph): return 'Error: The graph is not a DAG.' # transitive reduction (remove redundant edges) graph = transitive_reduction(graph) edges = list(graph.edges) removed_edges = list(set(initial_edges).difference(set(edges))) # transitive closure closure_graph = transitive_closure(graph) closure_edges = list(closure_graph.edges) closing_edges = list(set(closure_edges).difference(set(edges))) # forbidden edges (opposite edges of those in the t. c.) forbidden_edges = [] for edge in closure_edges: forbidden_edges.append((edge[1], edge[0])) # create connected components (works only from undirected graph) graph_undir = graph.to_undirected() if nx.is_connected(graph_undir): result = connected_dag(nodes, edges, ranks_wanted) cocos = [result] else: cocos = [] nodes_by_component = nx.connected_components(graph_undir) # generator for comp_nodes in nodes_by_component: comp_edges = [] for graph_edge in edges: if graph_edge[0] in comp_nodes: comp_edges.append(graph_edge) coco = connected_dag(comp_nodes, comp_edges, ranks_wanted) cocos.append(coco) # r --> (p, q) (node ID in DAG to pair of coco ID and node ID in coco) r_to_pq = [0] * nodenum for p, coco in enumerate(cocos): for node in coco['nodes']: r_to_pq[node['r']] = [p, node['q']] return { 'cocos': cocos, 'edges': { 'present': edges, # edges of the graph 'removed': removed_edges, # edges removed in transitive reduction 'closure': closure_edges, # transitive closure (present + closing) 'closing': closing_edges, # t. c. without present edges 'forbidden': forbidden_edges # edges that would create circles }, 'r_to_pq': r_to_pq }