def unwind_refs(graph, field="label"): unwind = {} _, levels = get_graph_levels(graph) for l in sorted(levels.keys()): for n_id in levels[l]: new = re.sub(r'@@(\d+)@@', lambda x: f"{unwind[int(x.group(1))]}", graph.nodes[n_id][field]) unwind[n_id] = new return unwind
def reorder(graph: nx.DiGraph): try: _, levels = get_graph_levels(graph) except Exception as ex: _logger.warning("skip - Failed to invoke reorder()") return # order by levels relabel_map = {} next_node_id = 1 for level in sorted(levels.keys()): nodes_by_label = {} for n_id in levels[level]: # for consistency n = graph.nodes[n_id] n["label"] = re.sub( r"@@(\d+)@@", lambda x: f"@@{relabel_map[int(x.group(1))]}@@", n["label"]) if n["label"] not in nodes_by_label: nodes_by_label[n["label"]] = n_id else: # duplicate label # safe merge since previous levels are fixed exists_node_id = nodes_by_label[n["label"]] # successors - should be the same due to label predecessors = graph.predecessors(n_id) for p_id in predecessors: graph.add_edge(p_id, exists_node_id) graph.nodes[p_id]["label"] = re.sub( f"@@{n_id}@@", lambda x: f"@@{exists_node_id}@@", graph.nodes[p_id]["label"]) graph.remove_node(n_id) nodes_order = sorted(nodes_by_label.keys()) for label in nodes_order: n_id = nodes_by_label[label] relabel_map[n_id] = next_node_id next_node_id += 1 # update labels # double mapping since new and old labels are overlap nx.relabel.relabel_nodes(graph, {k: str(v) for k, v in relabel_map.items()}, copy=False) nx.relabel.relabel_nodes(graph, {str(v): v for v in relabel_map.values()}, copy=False)
def draw_decomposition_graph(graph, title=None, pos=None): options = { 'node_color': 'lightblue', 'node_size': 400, 'width': 1, 'arrowstyle': '-|>', 'arrowsize': 14, } if not pos: try: _, levels = get_graph_levels(graph) pos = {} max_num_nodes_per_layer = max([len(levels[l]) for l in levels]) + 1 for l in levels.keys(): num_layer_nodes = len(levels[l]) if num_layer_nodes == 0: continue space_factor = max_num_nodes_per_layer // (num_layer_nodes + 1) for i, n_id in enumerate(sorted(levels[l])): pos[n_id] = ((i + 1) * space_factor, l) except: # cyclic print("a cyclic graph - change layout (no-layers)") pos = nx.spring_layout(graph, k=0.5) nx.draw_networkx(graph, pos=pos, arrows=True, with_labels=True, **options) for node in graph.nodes: plt.text(pos[node][0], pos[node][1] - 0.1, s=re.sub(r'@@(\d+)@@', r'#\g<1>', graph.nodes[node]['label']), bbox=dict(facecolor='red', alpha=0.5), horizontalalignment='center', wrap=True, size=6) if title: plt.title(title) plt.axis("off") plt.show() plt.clf()