def from_agraph(cls, A: AGraph, lambdas): """ Construct a ProgramAnalysisGraph from an AGraph """ self = cls(nx.DiGraph()) for n in A.nodes(): if n.attr["node_type"] in ("LoopVariableNode", "FuncVariableNode"): self.add_variable_node(n) for n in A.nodes(): if n.attr["node_type"] == "ActionNode": self.add_action_node(A, lambdas, n) for n in self.nodes(data=True): n_preds = len(n[1]["pred_fns"]) if n_preds == 0: del n[1]["pred_fns"] elif n_preds == 1: n[1]["update_fn"], = n[1].pop("pred_fns") else: n[1]["choice_fns"] = n[1].pop("pred_fns") def update_fn(n, **kwargs): cond_fn = n[1]["condition_fn"] sig = signature(cond_fn) ind = 0 if cond_fn(**kwargs) else 1 return n[1]["choice_fns"][ind](**kwargs) n[1]["update_fn"] = partial(update_fn, n) isolated_nodes = [ n for n in self.nodes() if len(list(self.predecessors(n))) == len(list(self.successors(n))) == 0 ] for n in isolated_nodes: self.remove_node(n) # Create lists of all input function and all input variables self.input_variables = list() self.input_functions = list() for n in self.nodes(): if self.nodes[n].get("init_fn") is not None: self.input_functions.append(n) if ( self.nodes[n]["node_type"] in ("LoopVariableNode", "FuncVariableNode") and len(list(self.predecessors(n))) == 0 ): self.input_variables.append(n) return self
def process_nodes(g: pgv.AGraph) -> Node: nodes = {str(gvnode): from_label(gvnode) for gvnode in g.nodes()} root_node = None for gvn, ndata in nodes.items(): if 'this/RootNode' in ndata[1]: root_node = gvn break else: print("Error: cannot find root node\n") sys.exit(-1) def walk(rn: pgv.Node) -> Node: args_dict = {} for n in g.successors(rn): e = pgv.Edge(g, rn, n) m = re.match('^args\\s+\\[(\\d+)\\]$', e.attr['label']) assert m arg_idx = int(m.group(1)) assert arg_idx not in args_dict args_dict[arg_idx] = walk(n) args = [args_dict[idx] for idx in range(len(args_dict))] return Node(*nodes[rn], args=args) return walk(root_node)
def build_graph(ts): g = AGraph(directed=True) g.add_edges_from(ts.ts.todok().keys()) g.graph_attr['overlap'] = 'scalexy' for n, s in zip(g.nodes(), ts._pwa.states): n.attr['label'] = s return g
def gen_op_insts(rf_allocs: List[RFallocation], dfg: AGraph, fu: AGraph, input_map: Dict[str, int]) -> List[ATAI]: assembly = [] instructions: List[Instruction] = [] for instruction in fu.nodes(): instructions.append(parse_instruction(instruction)) for instruction in instructions: n = dfg.get_node(instruction.name) nodes = dfg.predecessors(n) input_type0 = inst_input_type(rf_allocs, fu, nodes[0]) if len(nodes) > 1: input_type1 = inst_input_type(rf_allocs, fu, nodes[1]) else: input_type1 = input_type0 # This should never occur but we check for it anyways if input_type0 == OpInput and input_type1 == OpInput: if nodes[0] != nodes[1]: raise DoubleUnidenticalOPInputException # TODO: find scheduling for fetch ops might need to be swapped to fit? if input_type0 == RFInput: # If the data is in the RF we need to generate fetch instructions assembly.append( generate_fetch(rf_allocs, instruction, nodes[0], ATAFetch.REG.REG0)) input0 = RFInput() elif input_type0 == OpInput: input0 = OpInput() else: n = input_map[nodes[0].get_name()] if n is None: raise FUinputException( 'Cannot find FU from which predecessing node originates in map' ) input0 = FUinput(n) if input_type1 == RFInput: assembly.append( generate_fetch(rf_allocs, instruction, nodes[1], ATAFetch.REG.REG1)) input1 = RFInput() elif input_type1 == OpInput: input1 = OpInput() else: n = input_map[nodes[1].get_name()] if n is None: raise FUinputException( 'Cannot find FU from which predecessing node originates in map' ) input1 = FUinput(n) assembly.append(ATAOp(input0, input1, instruction.cycle)) return assembly
def process_nodes(g: pgv.AGraph) -> Tuple[Node, bool]: nodes = {str(gvnode): from_label(gvnode) for gvnode in g.nodes()} root_node = None for gvn, ndata in nodes.items(): if 'this/RootNode' in ndata[1]: root_node = gvn break else: print("Error: cannot find root node\n") sys.exit(-1) def walk(rn: pgv.Node) -> Tuple[Node, bool]: args_dict = {} correctness_holds = True for n in g.successors(rn): e = pgv.Edge(g, rn, n) m = re.match('^args\\s+\\[(\\d+)\\]$', e.attr['label']) assert m arg_idx = int(m.group(1)) assert arg_idx not in args_dict args_dict[arg_idx], have_correctness = walk(n) if not have_correctness: correctness_holds = False args = [args_dict[idx] for idx in range(len(args_dict))] new_node = Node(*nodes[rn], args=args) if correctness_holds: correctness_holds = 'this/CorrectnessHolds' in new_node.sets return new_node, correctness_holds return walk(root_node)
def gen_add_input2( dfg: AGraph, fu: AGraph, input_map: Dict[str, int]) -> Tuple[List[Input], List[Output]]: outputs: List[Output] = [] inputs: List[Input] = [] nodes: List[Instruction] = [] for node in fu.nodes(): nodes.append(parse_instruction(node)) nodes.sort() prev_o = 0 for node in nodes: preds = dfg.predecessors(node.name) parent0 = parse_instruction(preds[0]) parent1 = parse_instruction(preds[1]) if parent0.name in input_map: label = dfg.get_node(parent0.name).attr['label'] if 'mul' in label: latency = 2 else: latency = 1 i0 = prev_o + 1 inputs.append( Input(i0, input_map[parent0.name], parent0.cycle + latency)) else: for output in outputs: if output.cycle == parent0.cycle + 1: i0 = output.val break if parent1.name in input_map: label = dfg.get_node(parent1.name).attr['label'] if 'mul' in label: latency = 2 else: latency = 1 i1 = prev_o + 1 inputs.append( Input(i1, input_map[parent1.name], parent1.cycle + latency)) else: for output in outputs: if output.cycle == parent1.cycle + 1: i1 = output.val break expected = i0 + i1 prev_o = expected outputs.append(Output(expected, node.cycle + 1)) inputs.sort() return inputs, outputs
def from_agraph(cls, A: AGraph, lambdas): """ Construct a ProgramAnalysisGraph from an AGraph """ G = nx.DiGraph() for n in A.nodes(): if n.attr["node_type"] == "LoopVariableNode": add_variable_node(G, n) for n in A.nodes(): if n.attr["node_type"] == "ActionNode": add_action_node(A, G, lambdas, n) for n in G.nodes(data=True): n_preds = len(n[1]["pred_fns"]) if n_preds == 0: del n[1]["pred_fns"] elif n_preds == 1: n[1]["update_fn"], = n[1].pop("pred_fns") else: n[1]["choice_fns"] = n[1].pop("pred_fns") def update_fn(n, **kwargs): cond_fn = n[1]["condition_fn"] sig = signature(cond_fn) ind = 0 if cond_fn(**kwargs) else 1 return n[1]["choice_fns"][ind](**kwargs) n[1]["update_fn"] = partial(update_fn, n) isolated_nodes = [ n for n in G.nodes() if len(list(G.predecessors(n))) == len(list(G.successors(n))) == 0 ] for n in isolated_nodes: G.remove_node(n) return cls(G)
def _( G: ProgramAnalysisGraph, show_values=True, save=False, filename="program_analysis_graph.pdf", **kwargs, ): """ Visualizes ProgramAnalysisGraph in Jupyter notebook cell. Args: args kwargs Returns: AGraph """ A = AGraph(directed=True) A.graph_attr.update({"dpi": 227, "fontsize": 20, "fontname": "Menlo"}) A.node_attr.update({ "shape": "rectangle", "color": "#650021", "style": "rounded", "fontname": "Gill Sans", }) color_str = "#650021" for n in G.nodes(): A.add_node(n, label=n) for e in G.edges(data=True): A.add_edge(e[0], e[1], color=color_str, arrowsize=0.5) if show_values: for n in A.nodes(): value = str(G.nodes[n]["value"]) n.attr["label"] = n.attr["label"] + f": {value:.4}" if save: A.draw(filename, prog=kwargs.get("layout", "dot")) return Image(A.draw(format="png", prog=kwargs.get("layout", "dot")), retina=True)
def draw_relations(self, relations, fname): def get_node_name(n): return n.__name__ g= AGraph(directed=True) for n in relations: n_name= get_node_name(n) g.add_node(n_name) for relation in chain(*relations.values()): n1_name= get_node_name(relation.object1) n2_name= get_node_name(relation.object2) g.add_edge(n1_name, n2_name) e= g.get_edge(n1_name, n2_name) relation.set_edge_attributes(e) for n in g.nodes(): n.attr['shape']= 'box' g.draw(fname, prog='dot', args='-Grankdir=TB')
def get_graphviz(self, triple_hook=graphviz_triple_hook, node_hook=graphviz_node_hook, theme_options=VIS_THEME_OPTIONS, **hook_options): """ Create a pygraphviz graph from the tree @param triple_hook: a function that returns an attribute dict (or None) given a triple and the kargs @param node_hook: a function that returns a label given a node and the kargs @param theme_options: a dict-of-dicts containing global graph/node/edge attributes @param hook_options: additional arguments to pass to the hook functions """ def _id(node): return node.uri.split("/")[-1] g = AGraph(directed=True, strict=False) triples = list(self.get_triples()) # create nodes nodeset = set(chain.from_iterable((t.subject, t.object) for t in triples)) for n in sorted(nodeset, key=lambda n:n.id): g.add_node(_id(n), **node_hook(n, **hook_options)) connected = set() # create edges for triple in sorted(triples, key=lambda t:t.predicate): kargs = triple_hook(triple, **hook_options) if kargs: if kargs.get('reverse'): g.add_edge(_id(triple.object), _id(triple.subject), **kargs) else: g.add_edge(_id(triple.subject), _id(triple.object), **kargs) connected |= {_id(triple.subject), _id(triple.object)} connected = chain.from_iterable(g.edges()) for isolate in set(g.nodes()) - set(connected): g.remove_node(isolate) # some theme options for obj, attrs in theme_options.iteritems(): for k, v in attrs.iteritems(): getattr(g, "%s_attr" % obj)[k] = v return g
def igraph_from_dot(file): """numbered_graph(file) -> graph Input: file: the name of the dot-file Output: graph: igraph.Graph object. Verticies has the attribute "name". """ print("Step 1/3: Reading from dot.") a=AGraph(file) vertices = a.nodes() edges = a.edges() numbered_edges = [] print("Step 2/3: To numbered graph. Be patient...") for x, y in edges: xx = vertices.index(x) yy = vertices.index(y) numbered_edges.append((xx,yy)) print("Step 3/3: To igraph.") g=igraph.Graph(len(vertices), numbered_edges) g.vs["name"]=vertices return g
def visualize(self, show_values=False): """ Exports AnalysisGraph to pygraphviz AGraph Args: args kwargs Returns: AGraph """ A = AGraph(directed=True) A.graph_attr.update({"dpi": 227, "fontsize": 20, "fontname": "Menlo"}) A.node_attr.update({ "shape": "rectangle", "color": "#650021", "style": "rounded", "fontname": "Gill Sans", }) color_str = "#650021" for n in self.nodes(): A.add_node(n, label=n) for e in self.edges(data=True): A.add_edge(e[0], e[1], color=color_str, arrowsize=0.5) if show_values: for n in A.nodes(): value = str(self.nodes[n]["value"]) n.attr["label"] = n.attr["label"] + f": {value:.4}" # Drawing indicator variables return Image(A.draw(format="png", prog="dot"), retina=True)
def get_graphviz_layout(input_subgraph, ref_chain=[], aim_chain=[], freq_min=1, draw_all=False): print('SD') ps = AGraph(directed=True) ps.graph_attr['rankdir'] = 'LR' ps.graph_attr['mode'] = 'hier' nodes = delete_nodes(ref_chain, input_subgraph[1], freq_min) print('Number of nodes: ', end='') if draw_all == False: print(len(nodes)) elif draw_all == True: print(len(input_subgraph[1])) cluster_main = ps.add_subgraph() cluster_main.graph_attr['rank'] = '0' for i in range(len(ref_chain)): shape = 'circle' if ref_chain[i] in aim_chain: cluster_main.add_node(ref_chain[i], shape=shape, color='red') else: cluster_main.add_node(ref_chain[i], shape=shape, color='pink') for i in input_subgraph[0]: if i in aim_chain or i in ref_chain: continue if i in nodes: ps.add_node(str(i)) continue else: if draw_all == True: ps.add_node(str(i), color='grey') continue for i in input_subgraph[1]: color = 'black' try: if ref_chain.index(i[1]) - ref_chain.index(i[0]) == 1: color = 'red' ps.add_edge(str(i[0]), str(i[1]), color=color, penwidth=str(math.sqrt(i[2]))) continue except ValueError: pass if i[2] < freq_min: if draw_all == True: ps.add_edge(str(i[0]), str(i[1]), color='grey', penwidth=str(math.sqrt(i[2])), constraint='false') continue elif i[0] in nodes and i[1] in nodes: ps.add_edge(str(i[0]), str(i[1]), color=color, penwidth=str(math.sqrt(i[2]))) elif draw_all == True: ps.add_edge(str(i[0]), str(i[1]), color='grey', penwidth=str(math.sqrt(i[2])), constraint='false') ps.layout(prog='dot') positions = {n: n.attr['pos'] for n in ps.nodes()} edge_points = {edge: edge.attr['pos'] for edge in ps.edges()} return positions, edge_points
class UiGraphio(QMainWindow): """ Main window for application """ def __init__(self): """ Constructor """ QMainWindow.__init__(self) self.ui = graphio.Ui_MainWindow() self.ui.setupUi(self) self.scene_graph = QGraphicsScene() self.ui.graphicsView.setScene(self.scene_graph) self.ui.graphicsView.update() self.graph = AGraph(strict=True, directed=True) self.graph.layout(prog='dot') ##################################################### # View update ##################################################### def update_adj_matrix(self): count = self.graph.number_of_nodes() self.ui.tw_adjmatrix.setRowCount(count) self.ui.tw_adjmatrix.setColumnCount(count) self.ui.tw_adjmatrix.setHorizontalHeaderLabels(self.graph.nodes()) self.ui.tw_adjmatrix.setVerticalHeaderLabels(self.graph.nodes()) for (i, u) in enumerate(self.graph.nodes()): for (j, v) in enumerate(self.graph.nodes_iter()): if self.graph.has_edge(u, v): self.ui.tw_adjmatrix.setItem( i, j, QTableWidgetItem( self.graph.get_edge(u, v).attr['label'])) else: self.ui.tw_adjmatrix.setItem(i, j, QTableWidgetItem(str(0))) def update_matrix_incidence(self): nodes = self.graph.nodes() edges = self.graph.edges() self.ui.tw_incmatrix.setRowCount(len(nodes)) self.ui.tw_incmatrix.setColumnCount(len(edges)) self.ui.tw_incmatrix.setHorizontalHeaderLabels( [str(node) for node in self.graph.edges()]) self.ui.tw_incmatrix.setVerticalHeaderLabels(self.graph.nodes()) for (i, u) in enumerate(nodes): for (j, edg) in enumerate(edges): value = 0 if (u == edg[0]): value = 1 elif (u == edg[1]): value = -1 self.ui.tw_incmatrix.setItem(i, j, QTableWidgetItem(str(value))) def update_list_edges(self): edges = self.graph.edges() self.ui.tw_edges.setRowCount(len(edges)) for (i, edge) in enumerate(edges): self.ui.tw_edges.setItem(i, 0, QTableWidgetItem(edge[0])) self.ui.tw_edges.setItem(i, 1, QTableWidgetItem(edge[1])) def update_adj_list(self): nodes = self.graph.nodes() self.ui.tw_adjlist.setRowCount(self.graph.number_of_nodes()) self.ui.tw_adjlist.setVerticalHeaderLabels(nodes) for (i, node) in enumerate(nodes): value = '' for adj in self.graph.out_edges(node): value += adj[1] + ', ' self.ui.tw_adjlist.setItem(i, 0, QTableWidgetItem(value[:-2])) ##################################################### # Reset editors ##################################################### def add_cb_item(self, label): n = self.ui.cb_nodes.count() i = 0 while i < n: itext = self.ui.cb_nodes.itemText(i) if label == itext: return elif label < itext: break i += 1 # insert item to lists self.ui.cb_nodes.insertItem(i, label) self.ui.cb_starting_node.insertItem(i, label) self.ui.cb_ending_node.insertItem(i, label) @pyqtSlot(bool, name='on_bt_add_node_clicked') @pyqtSlot(bool, name='on_bt_del_node_clicked') @pyqtSlot(bool, name='on_bt_add_edge_clicked') @pyqtSlot(bool, name='on_bt_del_edge_clicked') def reset_editors(self): self.ui.cb_nodes.clearEditText() self.ui.cb_starting_node.clearEditText() self.ui.cb_ending_node.clearEditText() self.ui.sb_weight_edge.setMinimum() def redraw(self): self.graph.draw('graph', 'png', 'dot') self.scene_graph.clear() self.scene_graph.addItem(QGraphicsPixmapItem(QPixmap('graph'))) self.ui.graphicsView.update() ##################################################### # Buttons actions ##################################################### @pyqtSlot() def on_bt_add_node_clicked(self): """ Slot when click on Add node button """ label = self.ui.cb_nodes.currentText() if not self.graph.has_node(label): self.add_cb_item(label) self.graph.add_node(label) self.redraw() @pyqtSlot() def on_bt_del_node_clicked(self): """ Slot when click on Delete node button """ index = self.ui.cb_nodes.currentIndex() label = self.ui.cb_nodes.currentText() if index > -1 and self.graph.has_node(label): self.graph.remove_node(label) self.redraw() self.ui.cb_nodes.removeItem(index) self.ui.cb_starting_node.removeItem(index) self.ui.cb_ending_node.removeItem(index) @pyqtSlot() def on_bt_add_edge_clicked(self): """ Slot when click on Add branch button """ start = self.ui.cb_starting_node.currentText() end = self.ui.cb_ending_node.currentText() weight = self.ui.sb_weight_edge.value() if start and end: self.add_cb_item(start) self.add_cb_item(end) self.graph.add_edge(start, end, label=weight) self.redraw() @pyqtSlot() def on_bt_del_edge_clicked(self): """ Slot when click on Delete branch button """ start = self.ui.cb_starting_node.currentText() end = self.ui.cb_ending_node.currentText() weight = self.ui.sb_weight_edge.value() if start and end and self.graph.has_edge(start, end): self.graph.remove_edge(start, end) self.redraw() @pyqtSlot(int) @pyqtSlot(bool, name='on_bt_add_node_clicked') @pyqtSlot(bool, name='on_bt_del_node_clicked') @pyqtSlot(bool, name='on_bt_add_edge_clicked') @pyqtSlot(bool, name='on_bt_del_edge_clicked') def on_toolbox_view_currentChanged(self, index): index = self.ui.toolbox_view.currentIndex() if index == 0: self.update_adj_matrix() elif index == 1: self.update_matrix_incidence() elif index == 2: self.update_list_edges() elif index == 3: self.update_adj_list()
def dot_layout(cy_elements, edge_labels=False, subgraph_boxes=False, node_gt=None): """ Get a CyElements object and augment it (in-place) with positions, widths, heights, and spline data from a dot based layout. edge_labels is true if labels should appear on edges subgraph_boxes is true if boxes should be drawn around subgraphs Returns the object. """ elements = cy_elements.elements # g = AGraph(directed=True, strict=False) g = AGraph(directed=True, strict=False, forcelabels=True) # make transitive relations appear top to bottom elements = list(elements) nodes_by_id = dict((e["data"]["id"], e) for e in elements if e["group"] == "nodes") order = [ (nodes_by_id[e["data"]["source"]], nodes_by_id[e["data"]["target"]]) for e in elements if e["group"] == "edges" and "transitive" in e["data"] and e["data"]["transitive"] ] elements = topological_sort(elements, order, lambda e: e["data"]["id"]) # get the node id's and stable sort them by cluster # the idea here is to convert the graph into a dag by sorting # the nodes, then reversing the back edges. In particular, we try to make # all the edges between two clusters go in the same direction so clustering # doesn't result in horizontal edges, which dot renders badly. sorted_nodes = [e["data"]["id"] for e in elements if e["group"] == "nodes"] sorted_nodes = sorted(enumerate(sorted_nodes), key=lambda x: (nodes_by_id[x[1]]["data"]["cluster"], x[0])) sorted_nodes = [y for idx, y in sorted_nodes] node_key = dict((id, idx) for idx, id in enumerate(sorted_nodes)) if node_gt is None: node_gt = lambda X, y: False else: node_gt = lambda x, y: node_key[x] > node_key[y] # add nodes to the graph for e in elements: if e["group"] == "nodes" and e["classes"] != "non_existing": g.add_node(e["data"]["id"], label=e["data"]["label"].replace("\n", "\\n")) # TODO: remove this, it's specific to leader_demo weight = {"reach": 10, "le": 10, "id": 1} constraint = {"pending": False} # add edges to the graph for e in elements: if e["group"] == "edges": # kwargs = {'weight': weight.get(e["data"]["obj"], 0)}, kwargs = {"label": e["data"]["label"]} if edge_labels else {} if node_gt(e["data"]["source"], e["data"]["target"]): g.add_edge( e["data"]["target"], e["data"]["source"], e["data"]["id"], dir="back", **kwargs # constraint=constraint.get(e["data"]["obj"], True), ) else: g.add_edge( e["data"]["source"], e["data"]["target"], e["data"]["id"], **kwargs # constraint=constraint.get(e["data"]["obj"], True), ) # add clusters clusters = defaultdict(list) for e in elements: if e["group"] == "nodes" and e["data"]["cluster"] is not None and e["classes"] != "non_existing": clusters[e["data"]["cluster"]].append(e["data"]["id"]) for i, k in enumerate(sorted(clusters.keys())): g.add_subgraph(name="cluster_{}".format(i), nbunch=clusters[k], rank="min") # now get positions, heights, widths, and bsplines g.layout(prog="dot") # get the y origin. we want the top left of the graph to be a # fixed coordinate (hopefully (0,0)) so the graph doesn't jump when # its height changes. Unfortunately, pygraphviz has a bug a gives # the wrong bbox, so we compute the max y coord. # bbox = pygraphviz.graphviz.agget(g.handle,'bb') global y_origin y_origin = 0.0 for n in g.nodes(): top = float(n.attr["pos"].split(",")[1]) + float(n.attr["height"]) / 2 if top > y_origin: y_origin = top if subgraph_boxes: for sg in g.subgraphs(): top = float(sg.graph_attr["bb"].split(",")[3]) if top > y_origin: y_origin = top for e in elements: if e["group"] == "nodes" and e["classes"] != "non_existing": attr = g.get_node(e["data"]["id"]).attr e["position"] = _to_position(attr["pos"]) e["data"]["width"] = 72 * float(attr["width"]) e["data"]["height"] = 72 * float(attr["height"]) elif e["group"] == "edges": if node_gt(e["data"]["source"], e["data"]["target"]): attr = g.get_edge(e["data"]["target"], e["data"]["source"], e["data"]["id"]).attr pos = attr["pos"] pe = pos.split() ppe = pe[1:] ppe.reverse() pos = " ".join([pe[0].replace("s", "e")] + ppe) else: attr = g.get_edge(e["data"]["source"], e["data"]["target"], e["data"]["id"]).attr pos = attr["pos"] e["data"].update(_to_edge_position(pos)) if edge_labels and e["data"]["label"] != "": e["data"]["lp"] = _to_position(attr["lp"]) # g.draw('g.png') if subgraph_boxes: for sg in g.subgraphs(): box = cy_elements.add_shape(sg.name, classes="subgraphs") coords = _to_coord_list(sg.graph_attr["bb"]) box["data"]["coords"] = coords return cy_elements
def gen_mul_inputs( dfg: AGraph, fu: AGraph, input_map: Dict[str, int]) -> Tuple[List[Input], List[Output]]: outputs: List[Output] = [] inputs: List[Input] = [] nodes: List[Instruction] = [] for node in fu.nodes(): nodes.append(parse_instruction(node)) nodes.sort() prime_idx = 0 for node in nodes: preds = dfg.predecessors(node.name) parent0 = parse_instruction(preds[0]) parent1 = parse_instruction(preds[1]) label0 = dfg.get_node(parent0.name).attr['label'] label1 = dfg.get_node(parent1.name).attr['label'] i0, edge_case0, prime_idx = gen_mul_input(node, parent0, input_map, inputs, outputs, prime_idx, label0) i1, edge_case1, prime_idx = gen_mul_input(node, parent1, input_map, inputs, outputs, prime_idx, label1) if edge_case0 or edge_case1: expected = None else: expected = i0 * i1 # if parent0.name in input_map: # label = dfg.get_node(parent0.name).attr['label'] # if 'mul' in label: # latency = 2 # else: # latency = 1 # # i0 = PRIMES[prime_idx] # prime_idx += 1 # inputs.append(Input(i0, input_map[parent0.name], parent0.cycle + latency)) # else: # for output in outputs: # if output.cycle == parent0.cycle + 2: # i0 = output.val # break # # if parent1.name in input_map: # label = dfg.get_node(parent1.name).attr['label'] # if 'mul' in label: # latency = 2 # else: # latency = 1 # # i1 = PRIMES[prime_idx] # prime_idx += 1 # inputs.append(Input(i1, input_map[parent1.name], parent1.cycle + latency)) # else: # for output in outputs: # if output.cycle == parent1.cycle + 2: # i1 = output.val # break # expected = i0 * i1 outputs.append(Output(expected, node.cycle + 1)) inputs.sort() return inputs, outputs
def to_agraph(G, *args, **kwargs) -> AGraph: """ Exports AnalysisGraph to pygraphviz AGraph Args: G args kwargs Returns: AGraph """ A = AGraph(directed=True) A.graph_attr.update( { "dpi": 227, "fontsize": 20, "rankdir": kwargs.get("rankdir", "TB"), "fontname": font, "overlap": "scale", "splines": True, } ) A.node_attr.update( { "shape": "rectangle", "color": "#650021", "style": "rounded", "fontname": font, } ) nodes_with_indicators = [ n for n in G.nodes(data=True) if n[1].get("indicators") is not None ] n_max = max( [ sum([len(s.evidence) for s in e[2]["InfluenceStatements"]]) for e in G.edges(data=True) ] ) color_str = "#650021" for n in G.nodes(data=True): if kwargs.get("values"): node_label = n[0].capitalize().replace("_", " ") + " ("+str(np.mean(n[1]["rv"].dataset))+")" else: node_label = n[0].capitalize().replace("_", " ") A.add_node(n[0], label=node_label) for e in G.edges(data=True): reinforcement = np.mean( [ stmt.subj_delta["polarity"] * stmt.obj_delta["polarity"] for stmt in e[2]["InfluenceStatements"] ] ) opacity = ( sum([len(s.evidence) for s in e[2]["InfluenceStatements"]]) / n_max ) h = (opacity * 255).hex() cmap = cm.Greens if reinforcement > 0 else cm.Reds c_str = matplotlib.colors.rgb2hex(cmap(abs(reinforcement)))# + h[4:6] A.add_edge(e[0], e[1], color=c_str, arrowsize=0.5) # Drawing indicator variables if kwargs.get("indicators"): for n in nodes_with_indicators: for indicator_name, ind in n[1]["indicators"].items(): node_label = _insert_line_breaks(ind.name.replace("_", " ")) if kwargs.get("indicator_values"): if ind.unit is not None: units = f" {ind.unit}" else: units = "" if ind.mean is not None: ind_value = "{:.2f}".format(ind.mean) + f"{units}" node_label = f"{node_label}\n[{ind_value}]" A.add_node( node_label, style="rounded, filled", fillcolor="lightblue" ) A.add_edge(n[0], node_label, color="royalblue4") if kwargs.get("nodes_to_highlight") is not None: nodes = kwargs.pop("nodes_to_highlight") if isinstance(nodes, list): for n in nodes: if n in A.nodes(): A.add_node(n, fontcolor="royalblue") elif isinstance(nodes, str): if n in A.nodes(): A.add_node(nodes, fontcolor="royalblue") if kwargs.get("graph_label") is not None: A.graph_attr["label"] = kwargs["graph_label"] return A
def to_agraph(G, *args, **kwargs) -> AGraph: """ Exports AnalysisGraph to pygraphviz AGraph Args: G args kwargs Returns: AGraph """ A = AGraph(directed=True) A.graph_attr.update( { "dpi": 227, "fontsize": 20, "rankdir": kwargs.get("rankdir", "TB"), "fontname": font, } ) A.node_attr.update( { "shape": "rectangle", "color": "#650021", "style": "rounded", "fontname": font, } ) nodes_with_indicators = [ n for n in G.nodes(data=True) if n[1].get("indicators") is not None ] n_max = max( [ sum([len(s.evidence) for s in e[2]["InfluenceStatements"]]) for e in G.edges(data=True) ] ) color_str = "#650021" for n in G.nodes(): A.add_node(n, label=n.capitalize().replace("_", " ")) for e in G.edges(data=True): opacity = ( sum([len(s.evidence) for s in e[2]["InfluenceStatements"]]) / n_max ) h = (opacity * 255).hex() c_str = color_str + h[4:6] A.add_edge(e[0], e[1], color=c_str, arrowsize=0.5) # Drawing indicator variables if kwargs.get("indicators"): for n in nodes_with_indicators: for ind in n[1]["indicators"]: node_label = _insert_line_breaks(ind.name) if kwargs.get("indicator_values"): if ind.unit is not None: units = f" {ind.unit}" else: units = "" ind_value = "{:.2f}".format(ind.mean) + f"{units}" node_label = f"{node_label}\n[{ind_value}]" A.add_node( node_label, style="rounded, filled", fillcolor="lightblue" ) A.add_edge(n[0], node_label, color="royalblue4") if kwargs.get("nodes_to_highlight") is not None: nodes = kwargs.pop("nodes_to_highlight") if isinstance(nodes, list): for n in nodes: if n in A.nodes(): A.add_node(n, fontcolor="royalblue") elif isinstance(nodes, str): if n in A.nodes(): A.add_node(nodes, fontcolor="royalblue") if kwargs.get("graph_label") is not None: A.graph_attr["label"] = kwargs["graph_label"] return A
def from_agraph(cls, A: AGraph, lambdas): G = nx.DiGraph() variable_nodes = [ n for n in A.nodes() if n.attr["node_type"] != "ActionNode" ] for n in variable_nodes: name = n.attr["cag_label"] G.add_node(name, value=None, pred_fns=[], agraph_name=n) if n.attr["is_index"] == "True": G.nodes[name]["init_fn"] = lambda: 1 G.nodes[name]["update_fn"] = (lambda **kwargs: int( kwargs.pop(list(kwargs.keys())[0])) + 1) G.add_edge(name, name) function_nodes = [n for n in A.nodes() if n not in variable_nodes] for f in function_nodes: output, = A.successors(f) oname = output.attr["cag_label"] # Check if it is an initialization function if len(A.predecessors(f)) == 0: G.nodes[oname]["init_fn"] = getattr(lambdas, f.attr["lambda_fn"]) # Otherwise append the predecessor function list elif f.attr["label"] == "__decision__": preds = A.predecessors(f) if_var, = [ n for n in preds if list(A.predecessors(n)) [0].attr["label"] == "__condition__" ] condition_fn, = A.predecessors(if_var) cut = condition_fn.rfind("__") condition_fn = condition_fn[:cut] condition_lambda = condition_fn.replace("condition", "lambda") G.nodes[oname]["condition_fn"] = getattr( lambdas, condition_lambda) else: G.nodes[oname]["pred_fns"].append( getattr(lambdas, f.attr["lambda_fn"])) # If the type of the function is assign, then add an edge in the CAG if f.attr["label"] == "__assign__": for i in A.predecessors(f): iname = i.attr["cag_label"] G.add_edge(iname, oname) for n in G.nodes(data=True): n_preds = len(n[1]["pred_fns"]) if n_preds == 0: del n[1]["pred_fns"] elif n_preds == 1: n[1]["update_fn"], = n[1].pop("pred_fns") else: n[1]["choice_fns"] = n[1].pop("pred_fns") def update_fn(n, **kwargs): cond_fn = n[1]["condition_fn"] sig = signature(cond_fn) if cond_fn(**kwargs): return n[1]["choice_fns"][0](**kwargs) else: return n[1]["choice_fns"][1](**kwargs) n[1]["update_fn"] = partial(update_fn, n) isolated_nodes = [ n for n in G.nodes() if len(list(G.predecessors(n))) == len(list(G.successors(n))) == 0 ] for n in isolated_nodes: G.remove_node(n) return cls(G)
def dot_layout(cy_elements, edge_labels=False, subgraph_boxes=False, node_gt=None): """ Get a CyElements object and augment it (in-place) with positions, widths, heights, and spline data from a dot based layout. edge_labels is true if labels should appear on edges subgraph_boxes is true if boxes should be drawn around subgraphs Returns the object. """ elements = cy_elements.elements # g = AGraph(directed=True, strict=False) g = AGraph(directed=True, strict=False, forcelabels=True) # make transitive relations appear top to bottom elements = list(elements) nodes_by_id = dict( (e["data"]["id"], e) for e in elements if e["group"] == "nodes") order = [(nodes_by_id[e["data"]["source"]], nodes_by_id[e["data"]["target"]]) for e in elements if e["group"] == "edges" and "transitive" in e["data"] and e["data"]["transitive"]] elements = topological_sort(elements, order, lambda e: e["data"]["id"]) # get the node id's and stable sort them by cluster # the idea here is to convert the graph into a dag by sorting # the nodes, then reversing the back edges. In particular, we try to make # all the edges between two clusters go in the same direction so clustering # doesn't result in horizontal edges, which dot renders badly. sorted_nodes = [e["data"]["id"] for e in elements if e["group"] == "nodes"] sorted_nodes = sorted(enumerate(sorted_nodes), key=lambda x: (nodes_by_id[x[1]]["data"]["cluster"], x[0])) sorted_nodes = [y for idx, y in sorted_nodes] node_key = dict((id, idx) for idx, id in enumerate(sorted_nodes)) if node_gt is None: node_gt = lambda X, y: False else: node_gt = lambda x, y: node_key[x] > node_key[y] # add nodes to the graph for e in elements: if e["group"] == "nodes" and e["classes"] != 'non_existing': g.add_node(e["data"]["id"], label=e["data"]["label"].replace('\n', '\\n')) # TODO: remove this, it's specific to leader_demo weight = { 'reach': 10, 'le': 10, 'id': 1, } constraint = { 'pending': False, } # add edges to the graph for e in elements: if e["group"] == "edges": # kwargs = {'weight': weight.get(e["data"]["obj"], 0)}, kwargs = {'label': e["data"]["label"]} if edge_labels else {} if node_gt(e["data"]["source"], e["data"]["target"]): g.add_edge(e["data"]["target"], e["data"]["source"], e["data"]["id"], dir='back', **kwargs #constraint=constraint.get(e["data"]["obj"], True), ) else: g.add_edge(e["data"]["source"], e["data"]["target"], e["data"]["id"], **kwargs #constraint=constraint.get(e["data"]["obj"], True), ) # add clusters clusters = defaultdict(list) for e in elements: if e["group"] == "nodes" and e["data"][ "cluster"] is not None and e["classes"] != 'non_existing': clusters[e["data"]["cluster"]].append(e["data"]["id"]) for i, k in enumerate(sorted(clusters.keys())): g.add_subgraph( name='cluster_{}'.format(i), nbunch=clusters[k], rank='min', ) # now get positions, heights, widths, and bsplines g.layout(prog='dot') # get the y origin. we want the top left of the graph to be a # fixed coordinate (hopefully (0,0)) so the graph doesn't jump when # its height changes. Unfortunately, pygraphviz has a bug a gives # the wrong bbox, so we compute the max y coord. # bbox = pygraphviz.graphviz.agget(g.handle,'bb') global y_origin y_origin = 0.0 for n in g.nodes(): top = float(n.attr['pos'].split(',')[1]) + float(n.attr['height']) / 2 if top > y_origin: y_origin = top if subgraph_boxes: for sg in g.subgraphs(): top = float(sg.graph_attr['bb'].split(',')[3]) if top > y_origin: y_origin = top for e in elements: if e["group"] == "nodes" and e["classes"] != 'non_existing': attr = g.get_node(e["data"]["id"]).attr e["position"] = _to_position(attr['pos']) e["data"]["width"] = 72 * float(attr['width']) e["data"]["height"] = 72 * float(attr['height']) elif e["group"] == "edges": if node_gt(e["data"]["source"], e["data"]["target"]): attr = g.get_edge(e["data"]["target"], e["data"]["source"], e["data"]["id"]).attr pos = attr['pos'] pe = pos.split() ppe = pe[1:] ppe.reverse() pos = ' '.join([pe[0].replace('s', 'e')] + ppe) else: attr = g.get_edge(e["data"]["source"], e["data"]["target"], e["data"]["id"]).attr pos = attr['pos'] e["data"].update(_to_edge_position(pos)) if edge_labels and e["data"]["label"] != '': e["data"]["lp"] = _to_position(attr['lp']) # g.draw('g.png') if subgraph_boxes: for sg in g.subgraphs(): box = cy_elements.add_shape(sg.name, classes='subgraphs') coords = _to_coord_list(sg.graph_attr['bb']) box["data"]["coords"] = coords return cy_elements
def create_graph(filename, layout="dot", use_singularity=False, color_for_disabled_converter='red'): """ :param filename: should end in .png or .svg or .dot If extension is .dot, only the dot file is created. This is useful if you have issues installing graphviz. If so, under Linux you could use our singularity container see github.com/cokelaer/graphviz4all """ from bioconvert.core.registry import Registry rr = Registry() try: if filename.endswith(".dot") or use_singularity is True: raise Exception() from pygraphviz import AGraph dg = AGraph(directed=True) url = "https://bioconvert.readthedocs.io/en/master/formats.html#{}" for a, b, s in rr.get_all_conversions(): if len(a) == 1 and len(b) == 1: dg.add_node(a[0], shape="rectangle", style="filled", url=url.format(a[0].upper())) dg.add_node(b[0], shape="rectangle", style="filled", url=url.format(b[0].upper())) dg.add_edge( a[0], b[0], color='black' if s else color_for_disabled_converter) else: and_node = "_".join(a) + "_and_" + "_".join(b) dg.add_node(and_node, label="", fillcolor="black", width=.1, height=.1, styled="filled", fixedsize=True, shape="circle") for this in a: dg.add_edge( this, and_node, color="black" if s else color_for_disabled_converter) for this in b: dg.add_edge( and_node, this, color="black" if s else color_for_disabled_converter) for name in dg.nodes(): if dg.degree(name) < 5: dg.get_node(name).attr["fillcolor"] = "white" elif dg.degree(name) < 10: # yellow dg.get_node(name).attr["fillcolor"] = "yellow" elif dg.degree(name) < 20: # orange dg.get_node(name).attr["fillcolor"] = "orange" else: # red dg.get_node(name).attr["fillcolor"] = "red" dg.layout(layout) dg.draw(filename) dg.write("conversion.dot") print(list(dg.get_node("FASTQ").attr.values())) except Exception as e: _log.error(e) dot = """ strict digraph{ node [label="\\N"]; """ nodes = set([ item for items in rr.get_all_conversions() for item in items[0:1] ]) for node in nodes: dot += "\"{}\";\n".format(node) for a, b, s in rr.get_all_conversions(): dot += "\"{}\" -> \"{}\";\n".format(a, b) dot += "}\n" from easydev import TempFile from bioconvert import shell dotfile = TempFile(suffix=".dot") with open(dotfile.name, "w") as fout: fout.write(dot) dotpath = "" if use_singularity: from bioconvert.core.downloader import download_singularity_image singfile = download_singularity_image( "graphviz.simg", "shub://cokelaer/graphviz4all:v1", "4288088d91c848e5e3a327282a1ab3d1") dotpath = "singularity run {} ".format(singfile) on_rtd = environ.get('READTHEDOCS', None) == 'True' if on_rtd: dotpath = "" ext = filename.rsplit(".", 1)[1] cmd = "{}dot -T{} {} -o {}".format(dotpath, ext, dotfile.name, filename) print(dotfile.name) try: shell(cmd) except: import os os.system(cmd)