def add_nugget_to_action_graph(hie, nug_id, ag_id, partial_typing, move=True): nug_gr = hie.node[nug_id].graph ag_gr = hie.node[ag_id].graph shared_typings = [ typing for typing in hie.successors(ag_id) if typing in hie.successors(nug_id) ] # necessary_typings = [typing for typing in hie.successors(ag_id) # if hie.edge[ag_id][typing].total] necessary_typings = [typing for typing in hie.successors(ag_id)] for typing in necessary_typings: for node in nug_gr: if node not in partial_typing: mapping = hie.get_typing(nug_id, typing) if mapping is None or node not in mapping: raise ValueError("Node {} is not typed by {}".format( node, typing)) for node in nug_gr: if node not in partial_typing: new_node = prim.add_node_new_id(ag_gr, node, copy.deepcopy(nug_gr.node[node])) partial_typing[node] = new_node for typing in necessary_typings: mapping = hie.get_typing(nug_id, typing) hie.edge[ag_id][typing].mapping[new_node] = mapping[node] for (source, target) in nug_gr.edges(): prim.add_edge(ag_gr, partial_typing[source], partial_typing[target]) if move: for typing in hie.successors(nug_id): hie.remove_edge(nug_id, typing) hie.add_typing(nug_id, ag_id, partial_typing, total=True) hie.node[nug_id].attrs["type"] = "nugget"
def pullback(b, c, d, b_d, c_d, inplace=False): """Find the pullback from b -> d <- c. Given h1 : B -> D; h2 : C -> D returns A, rh1, rh2 with rh1 : A -> B; rh2 : A -> C and A the pullback. """ if inplace is True: a = b else: a = type(b)() # Check homomorphisms check_homomorphism(b, d, b_d) check_homomorphism(c, d, c_d) hom1 = {} hom2 = {} f = b_d g = c_d for n1 in b.nodes(): for n2 in c.nodes(): if f[n1] == g[n2]: new_attrs = merge_attributes(b.node[n1], c.node[n2], 'intersection') if n1 not in a.nodes(): add_node(a, n1, new_attrs) hom1[n1] = n1 hom2[n1] = n2 else: i = 1 new_name = str(n1) + str(i) while new_name in a.nodes(): i += 1 new_name = str(n1) + str(i) # if n2 not in a.nodes(): add_node(a, new_name, new_attrs) hom1[new_name] = n1 hom2[new_name] = n2 for n1 in a.nodes(): for n2 in a.nodes(): if (hom1[n1], hom1[n2]) in b.edges() or \ ((not a.is_directed()) and (hom1[n2], hom1[n1]) in b.edges()): if (hom2[n1], hom2[n2]) in c.edges() or \ ((not a.is_directed) and (hom2[n2], hom2[n1]) in c.edges()): add_edge(a, n1, n2) set_edge( a, n1, n2, merge_attributes( get_edge(b, hom1[n1], hom1[n2]), get_edge(c, hom2[n1], hom2[n2]), 'intersection')) check_homomorphism(a, b, hom1) check_homomorphism(a, c, hom2) return (a, hom1, hom2)
def inject_add_edge(self, n1, n2, attrs=None): """Inject addition of a new edge to the rule. This method adds an edge between two nodes of the `rhs`. Parameters ---------- n1 : hashable Id of an edge's source node in `rhs`. n2 : hashable Id of an edge's target node in `rhs`. Raises ------ RuleError If some of the nodes (`n1` or `n2`) do not exist or if there is already an edge between them in `rhs`. """ if n1 not in self.rhs.nodes(): raise RuleError( "Node with the id '%s' does not exist in the " "right-hand side of the rule" % n1) if n2 not in self.rhs.nodes(): raise RuleError( "Node with the id '%s' does not exist in the " "right-hand side of the rule" % n2) if (n1, n2) in self.rhs.edges(): raise RuleError( "Edge '%s->%s' already exists in the right-" "hand side of the rule" % (n1, n2) ) primitives.add_edge(self.rhs, n1, n2, attrs) return
def paste_nodes(hie, top, graph_id, parent_path, nodes, mouse_x, mouse_y): """paste the selected nodes from graph at parent_path to graph_id""" path_list = [s for s in parent_path.split("/") if s and not s.isspace()] other_id = child_from_path(hie, top, path_list) gr = hie.node[graph_id].graph other_gr = hie.node[other_id].graph old_to_new = {} # check that all copied nodes exist in the graph for node in nodes: if node not in other_gr: raise ValueError( "copied node {} does not exist anymore".format(node)) if hie.has_edge(graph_id, other_id): mapping = hie.edge[graph_id][other_id].mapping for node in nodes: n_id = prim.unique_node_id(gr, node) prim.add_node(gr, n_id, other_gr.node[node]) old_to_new[node] = n_id mapping[n_id] = node for (source, target) in other_gr.subgraph(nodes).edges(): prim.add_edge(gr, old_to_new[source], old_to_new[target], other_gr.edge[source][target]) else: # check that all necessary typings are there necessary_typings = [typing for typing in hie.successors(graph_id)] # if hie.edge[graph_id][typing].total] # until UI can handle partial typings typings = [typing for typing in hie.successors(graph_id) if typing in hie.successors(other_id)] for typing in necessary_typings: if typing not in typings: raise ValueError("copied nodes not typed by {}".format(typing)) for node in nodes: if node not in hie.edge[other_id][typing].mapping: raise ValueError("copied node {} is not typed by {}" .format(node, typing)) for node in nodes: node_id = prim.unique_node_id(gr, node) old_to_new[node] = node_id prim.add_node(gr, node_id, other_gr.node[node]) for typing in typings: other_mapping = hie.edge[other_id][typing].mapping if node in other_mapping: hie.edge[graph_id][typing].mapping[old_to_new[node]] =\ other_mapping[node] for (source, target) in other_gr.subgraph(nodes).edges(): prim.add_edge(gr, old_to_new[source], old_to_new[target], other_gr.edge[source][target]) if "positions" in hie.node[other_id].attrs: if "positions" not in hie.node[graph_id].attrs: hie.node[graph_id].attrs["positions"] = {} positions_old = hie.node[other_id].attrs["positions"] positions_new = hie.node[graph_id].attrs["positions"] add_positions(mouse_x, mouse_y, positions_old, positions_new, old_to_new)
def pullback(b, c, d, b_d, c_d, inplace=False): """Find the pullback from b -> d <- c. Given h1 : B -> D; h2 : C -> D returns A, rh1, rh2 with rh1 : A -> B; rh2 : A -> C and A the pullback. """ if inplace is True: a = b else: a = type(b)() # Check homomorphisms check_homomorphism(b, d, b_d) check_homomorphism(c, d, c_d) hom1 = {} hom2 = {} f = b_d g = c_d for n1 in b.nodes(): for n2 in c.nodes(): if f[n1] == g[n2]: new_attrs = merge_attributes(b.node[n1], c.node[n2], 'intersection') if n1 not in a.nodes(): add_node(a, n1, new_attrs) hom1[n1] = n1 hom2[n1] = n2 else: i = 1 new_name = str(n1) + str(i) while new_name in a.nodes(): i += 1 new_name = str(n1) + str(i) # if n2 not in a.nodes(): add_node(a, new_name, new_attrs) hom1[new_name] = n1 hom2[new_name] = n2 for n1 in a.nodes(): for n2 in a.nodes(): if (hom1[n1], hom1[n2]) in b.edges() or \ ((not a.is_directed()) and (hom1[n2], hom1[n1]) in b.edges()): if (hom2[n1], hom2[n2]) in c.edges() or \ ((not a.is_directed) and (hom2[n2], hom2[n1]) in c.edges()): add_edge(a, n1, n2) set_edge( a, n1, n2, merge_attributes(get_edge(b, hom1[n1], hom1[n2]), get_edge(c, hom2[n1], hom2[n2]), 'intersection')) check_homomorphism(a, b, hom1) check_homomorphism(a, c, hom2) return (a, hom1, hom2)
def link_components(hie, g_id, comp1, comp2, kami_id): """ link two componenst together with brk, bnd""" typing = hie.edge[g_id][kami_id].mapping graph = hie.node[g_id].graph print(graph) bnd_name = unique_node_id(graph, "bnd %s-%s" % (comp1, comp2)) typing[bnd_name] = "bnd" add_node(graph, bnd_name) #brk_name = unique_node_id(graph, "brk") #add_node(graph, brk_name) #typing[brk_name] = "brk" #loc1 = unique_node_id(graph, "loc") #add_node(graph, loc1) #typing[loc1] = "locus" #loc2 = unique_node_id(graph, "loc") #add_node(graph, loc2) #typing[loc2] = "locus" add_edge(graph, comp1, bnd_name) add_edge(graph, comp2, bnd_name) #add_edge(graph, loc1, comp1) #add_edge(graph, loc1, bnd_name) #add_edge(graph, loc1, brk_name) #add_edge(graph, loc2, comp2) #add_edge(graph, loc2, bnd_name) #add_edge(graph, loc2, brk_name) if "positions" in hie.node[g_id].attrs: positions = hie.node[g_id].attrs["positions"] if comp1 in positions.keys(): xpos1 = positions[comp1].get("x", 0) ypos1 = positions[comp1].get("y", 0) else: (xpos1, ypos1) = (0, 0) if comp2 in positions.keys(): xpos2 = positions[comp2].get("x", 0) ypos2 = positions[comp2].get("y", 0) else: (xpos2, ypos2) = (0, 0) difx = xpos2 - xpos1 dify = ypos2 - ypos1 if (difx, dify) != (0, 0): distance = sqrt(difx * difx + dify * dify) vect = (difx / distance, dify / distance) #positions[loc1] = {"x": xpos1+vect[0]*distance/3, # "y": ypos1+vect[1]*distance/3} #positions[loc2] = {"x": xpos1+vect[0]*distance/3*2, # "y": ypos1+vect[1]*distance/3*2} positions[bnd_name] = { "x": (xpos1 + vect[0] * distance / 2), # + #vect[1]*60), "y": (ypos1 + vect[1] * distance / 2) } # -
def add_edge(hie, g_id, parent, node1, node2): """add an edge to a node of the hierarchy""" if isinstance(hie.node[g_id], GraphNode): _valid_edge(hie, g_id, node1, node2) prim.add_edge(hie.node[g_id].graph, node1, node2) elif isinstance(hie.node[g_id], RuleNode): tmp_rule = copy.deepcopy(hie.node[g_id].rule) tmp_rule.add_edge_rhs(node1, node2) typings = [(hie.node[typing].graph, hie.edge[g_id][typing]) for _, typing in hie.out_edges(g_id)] check_rule_typings(typings, tmp_rule) hie.node[g_id].rule = tmp_rule else: raise ValueError("node is neither a rule nor a graph")
def pushout_from_relation(g1, g2, relation, inplace=False): """Find the pushout from a relation.""" left_dict = left_relation_dict(relation) right_dict = right_relation_dict(relation) if inplace is True: g12 = g1 else: g12 = copy.deepcopy(g1) g1_g12 = id_of(g12.nodes()) g2_g12 = dict() for node in g1.nodes(): if node in left_dict.keys(): for g2_node in left_dict[node]: g2_g12[g2_node] = node for node in g2.nodes(): if node not in right_dict.keys(): add_node(g12, node, g2.node[node]) g2_g12[node] = node elif len(right_dict[node]) == 1: node_attrs_diff = dict_sub( g2.node[node], g1.node[list(right_dict[node])[0]]) add_node_attrs( g12, list(right_dict[node])[0], node_attrs_diff) elif len(right_dict[node]) > 1: new_name = merge_nodes(g12, right_dict[node]) for g1_node in right_dict[node]: g1_g12[g1_node] = new_name g2_g12[node] = new_name node_attrs_diff = dict_sub( g2.node[node], g12.node[new_name]) add_node_attrs(g12, new_name, node_attrs_diff) for u, v in g2.edges(): if (g2_g12[u], g2_g12[v]) not in g12.edges(): add_edge(g12, g2_g12[u], g2_g12[v], get_edge(g2, u, v)) else: edge_attrs_diff = dict_sub( g2.edge[u][v], g12.edge[g2_g12[u]][g2_g12[v]]) add_edge_attrs(g12, g2_g12[u], g2_g12[v], edge_attrs_diff) return (g12, g1_g12, g2_g12)
def pushout_from_relation(g1, g2, relation, inplace=False): """Find the pushout from a relation.""" left_dict = left_relation_dict(relation) right_dict = right_relation_dict(relation) if inplace is True: g12 = g1 else: g12 = copy.deepcopy(g1) g1_g12 = id_of(g12.nodes()) g2_g12 = dict() for node in g1.nodes(): if node in left_dict.keys(): for g2_node in left_dict[node]: g2_g12[g2_node] = node for node in g2.nodes(): if node not in right_dict.keys(): add_node(g12, node, g2.node[node]) g2_g12[node] = node elif len(right_dict[node]) == 1: node_attrs_diff = dict_sub(g2.node[node], g1.node[list(right_dict[node])[0]]) add_node_attrs(g12, list(right_dict[node])[0], node_attrs_diff) elif len(right_dict[node]) > 1: new_name = merge_nodes(g12, right_dict[node]) for g1_node in right_dict[node]: g1_g12[g1_node] = new_name g2_g12[node] = new_name node_attrs_diff = dict_sub(g2.node[node], g12.node[new_name]) add_node_attrs(g12, new_name, node_attrs_diff) for u, v in g2.edges(): if (g2_g12[u], g2_g12[v]) not in g12.edges(): add_edge(g12, g2_g12[u], g2_g12[v], get_edge(g2, u, v)) else: edge_attrs_diff = dict_sub(g2.edge[u][v], g12.edge[g2_g12[u]][g2_g12[v]]) add_edge_attrs(g12, g2_g12[u], g2_g12[v], edge_attrs_diff) return (g12, g1_g12, g2_g12)
def test_lifting(self): pattern = nx.DiGraph() primitives.add_nodes_from(pattern, [ ("student", {"sex": {"male", "female"}}), "prof" ]) primitives.add_edge(pattern, "prof", "student") p = nx.DiGraph() primitives.add_nodes_from(p, [ ("girl", {"sex": "female"}), ("boy", {"sex": "male"}), ("generic") ]) p_lhs = { "girl": "student", "boy": "student", "generic": "student" } rule = Rule(p, pattern, p_lhs=p_lhs) # Test non-canonical rule lifting rule_hierarchy1, lhs_instances1 = self.hierarchy.get_rule_propagations( "b", rule, p_typing={"c": {"Alice": {"girl", "generic"}, "Bob": "boy"}}) new_hierarchy, rhs_instances1 = self.hierarchy.apply_rule_hierarchy( rule_hierarchy1, lhs_instances1, inplace=False) pattern = nx.DiGraph() primitives.add_nodes_from(pattern, [ "school", "institute" ]) rule = Rule.from_transform(pattern) rule.inject_add_node("phd") rule.inject_add_edge("phd", "institute", {"type": "internship"}) rule_hierarchy2, lhs_instances2 = self.hierarchy.get_rule_propagations( "b", rule, rhs_typing={"a": {"phd": "red"}}) new_hierarchy, rhs_instances2 = self.hierarchy.apply_rule_hierarchy( rule_hierarchy2, lhs_instances2, inplace=False)
def add_edge(self, n1, n2, attrs=None): """Add an edge in the graph.""" # Find nodes in p mapping to n1 & n2 p_keys_1 = keys_by_value(self.p_lhs, n1) p_keys_2 = keys_by_value(self.p_lhs, n2) for k1 in p_keys_1: if k1 not in self.p.nodes(): raise RuleError( "Node with the id '%s' does not exist in the " "preserved part of the rule" % k1 ) for k2 in p_keys_2: if k2 not in self.p.nodes(): raise RuleError( "Node with the id '%s' does not exist in the " "preserved part of the rule" % k2 ) rhs_key_1 = self.p_rhs[k1] rhs_key_2 = self.p_rhs[k2] if self.rhs.is_directed(): if (rhs_key_1, rhs_key_2) in self.rhs.edges(): raise RuleError( "Edge '%s->%s' already exists in the right " "hand side of the rule" % (rhs_key_1, rhs_key_2) ) primitives.add_edge(self.rhs, rhs_key_1, rhs_key_2, attrs) else: if (rhs_key_1, rhs_key_2) in self.rhs.edges() or\ (rhs_key_2, rhs_key_1) in self.rhs.edges(): raise RuleError( "Edge '%s->%s' already exists in the right " "hand side of the rule" % (rhs_key_1, rhs_key_2) ) primitives.add_edge(self.rhs, rhs_key_1, rhs_key_2, attrs) return
def __init__(self): """Initialize test.""" # Define the left hand side of the rule self.pattern = nx.DiGraph() self.pattern.add_node(1) self.pattern.add_node(2) self.pattern.add_node(3) prim.add_node(self.pattern, 4, {'a': 1}) self.pattern.add_edges_from([ (1, 2), (3, 2), (4, 1) ]) prim.add_edge(self.pattern, 2, 3, {'a': {1}}) # Define preserved part of the rule self.p = nx.DiGraph() self.p.add_node('a') self.p.add_node('b') self.p.add_node('c') prim.add_node(self.p, 'd', {'a': 1}) self.p.add_edges_from([ ('a', 'b'), ('d', 'a') ]) prim.add_edge(self.p, 'b', 'c', {'a': {1}}) # Define the right hand side of the rule self.rhs = nx.DiGraph() self.rhs.add_node('x') self.rhs.add_node('y') self.rhs.add_node('z') # self.rhs.add_node('s', {'a': 1}) prim.add_node(self.rhs, 's', {'a': 1}) self.rhs.add_node('t') self.rhs.add_edges_from([ ('x', 'y'), # ('y', 'z', {'a': {1}}), ('s', 'x'), ('t', 'y') ]) prim.add_edge(self.rhs, 'y', 'z', {'a': {1}}) # Define mappings self.p_lhs = {'a': 1, 'b': 2, 'c': 3, 'd': 4} self.p_rhs = {'a': 'x', 'b': 'y', 'c': 'z', 'd': 's'} return
def _add_edge_lhs(self, source, target, attrs=None): if (source, target) not in self.lhs.edges(): if source in self.lhs.nodes() and target in self.rhs.nodes(): primitives.add_edge(self.lhs, source, target, attrs) p_sources = keys_by_value(self.p_lhs, source) p_targets = keys_by_value(self.p_lhs, target) if len(p_sources) > 0 and len(p_targets) > 0: for p_s in p_sources: for p_t in p_targets: primitives.add_edge(self.p, p_s, p_t, attrs) primitives.add_edge( self.rhs, self.p_rhs[p_s], self.p_rhs[p_t], attrs) else: raise RuleError( "Cannot add an edge between nodes '{}' and '{}': " "one of the nodes does not exist".format(source, target)) else: raise RuleError( "Edge '{}'->'{}' already exists in the left-hand side " "of the rule".format(source, target))
def paste_nodes(hie, top, graph_id, parent_path, nodes, mouse_x, mouse_y): """paste the selected nodes from graph at parent_path to graph_id""" path_list = [s for s in parent_path.split("/") if s and not s.isspace()] other_id = child_from_path(hie, top, path_list) gr = hie.node[graph_id].graph other_gr = hie.node[other_id].graph old_to_new = {} # check that all copied nodes exist in the graph for node in nodes: if node not in other_gr: raise ValueError( "copied node {} does not exist anymore".format(node)) if hie.has_edge(graph_id, other_id): mapping = hie.edge[graph_id][other_id].mapping for node in nodes: n_id = prim.unique_node_id(gr, node) prim.add_node(gr, n_id, other_gr.node[node]) old_to_new[node] = n_id mapping[n_id] = node for (source, target) in other_gr.subgraph(nodes).edges(): prim.add_edge(gr, old_to_new[source], old_to_new[target], other_gr.edge[source][target]) else: # check that all necessary typings are there necessary_typings = [typing for typing in hie.successors(graph_id)] # if hie.edge[graph_id][typing].total] # until UI can handle partial typings typings = [ typing for typing in hie.successors(graph_id) if typing in hie.successors(other_id) ] for typing in necessary_typings: if typing not in typings: raise ValueError("copied nodes not typed by {}".format(typing)) for node in nodes: if node not in hie.edge[other_id][typing].mapping: raise ValueError( "copied node {} is not typed by {}".format( node, typing)) for node in nodes: node_id = prim.unique_node_id(gr, node) old_to_new[node] = node_id prim.add_node(gr, node_id, other_gr.node[node]) for typing in typings: other_mapping = hie.edge[other_id][typing].mapping if node in other_mapping: hie.edge[graph_id][typing].mapping[old_to_new[node]] =\ other_mapping[node] for (source, target) in other_gr.subgraph(nodes).edges(): prim.add_edge(gr, old_to_new[source], old_to_new[target], other_gr.edge[source][target]) if "positions" in hie.node[other_id].attrs: if "positions" not in hie.node[graph_id].attrs: hie.node[graph_id].attrs["positions"] = {} positions_old = hie.node[other_id].attrs["positions"] positions_new = hie.node[graph_id].attrs["positions"] add_positions(mouse_x, mouse_y, positions_old, positions_new, old_to_new)
def add_edge_rhs(self, n1, n2, attrs=None): """Add an edge in the rhs.""" primitives.add_edge(self.rhs, n1, n2, attrs)
def inject_clone_node(self, n, new_node_id=None): """Inject cloning of a node by the rule. This procedure clones `n` in the preserved part and the right-hand side. Parameters ---------- n : hashable Node from `lhs` to clone new_node_id : hashable Id for the clone Returns ------- p_new_node_id : hashable Id of the new clone node in the preserved part rhs_new_node_id : hashable Id of the new clone node in the right-hand side Raises ------ RuleError If the node to clone is already being removed by the rule or if node with the specified clone id already exists in p. """ p_nodes = keys_by_value(self.p_lhs, n) if len(p_nodes) == 0: raise RuleError( "Cannot inject cloning: node '%s' is already " "being removed by the rule, revert its removal " "first" % n) else: if new_node_id is not None and new_node_id in self.p.nodes(): raise RuleError( "Node with id '%s' already exists in the " "preserved part!") some_p_node = p_nodes[0] p_new_node_id = primitives.clone_node( self.p, some_p_node, new_node_id) self.p_lhs[p_new_node_id] = n # add it to the rhs # generate a new id for rhs rhs_new_node_id = p_new_node_id if rhs_new_node_id in self.rhs.nodes(): rhs_new_node_id = primitives.unique_node_id( self.rhs, rhs_new_node_id) primitives.add_node( self.rhs, rhs_new_node_id, self.p.node[p_new_node_id]) self.p_rhs[p_new_node_id] = rhs_new_node_id # reconnect the new rhs node with necessary edges for pred in self.p.predecessors(p_new_node_id): if (self.p_rhs[pred], rhs_new_node_id) not in self.rhs.edges(): primitives.add_edge( self.rhs, self.p_rhs[pred], rhs_new_node_id, self.p.edge[pred][p_new_node_id]) for suc in self.p.successors(p_new_node_id): if (rhs_new_node_id, self.p_rhs[suc]) not in self.rhs.edges(): primitives.add_edge( self.rhs, rhs_new_node_id, self.p_rhs[suc], self.p.edge[p_new_node_id][suc]) return (p_new_node_id, rhs_new_node_id)
def pushout(a, b, c, a_b, a_c, inplace=False): """Find the pushour of the span b <- a -> c.""" check_homomorphism(a, b, a_b) check_homomorphism(a, c, a_c) if inplace is True: d = b else: d = copy.deepcopy(b) b_d = id_of(b.nodes()) c_d = dict() # Add/merge nodes for c_n in c.nodes(): a_keys = keys_by_value(a_c, c_n) # Add nodes if len(a_keys) == 0: add_node(d, c_n, c.node[c_n]) c_d[c_n] = c_n # Keep nodes elif len(a_keys) == 1: c_d[a_c[a_keys[0]]] = a_b[a_keys[0]] # Merge nodes else: nodes_to_merge = [] for k in a_keys: nodes_to_merge.append(a_b[k]) new_name = merge_nodes(d, nodes_to_merge) c_d[c_n] = new_name for node in nodes_to_merge: b_d[node] = new_name # Add edges for (n1, n2) in c.edges(): if b.is_directed(): if (c_d[n1], c_d[n2]) not in d.edges(): add_edge(d, c_d[n1], c_d[n2], get_edge(c, n1, n2)) else: if (c_d[n1], c_d[n2]) not in d.edges() and\ (c_d[n2], c_d[n1]) not in d.edges(): add_edge(d, c_d[n1], c_d[n2], get_edge(c, n1, n2)) # Add node attrs for c_n in c.nodes(): a_keys = keys_by_value(a_c, c_n) # Add attributes to the nodes which stayed invariant if len(a_keys) == 1: attrs_to_add = dict_sub(c.node[c_n], a.node[a_keys[0]]) add_node_attrs(d, c_d[c_n], attrs_to_add) # Add attributes to the nodes which were merged elif len(a_keys) > 1: merged_attrs = {} for k in a_keys: merged_attrs = merge_attributes(merged_attrs, a.node[k]) attrs_to_add = dict_sub(c.node[c_n], merged_attrs) add_node_attrs(d, c_d[c_n], attrs_to_add) # Add edge attrs for (n1, n2) in c.edges(): d_n1 = c_d[n1] d_n2 = c_d[n2] if d.is_directed(): attrs_to_add = dict_sub(get_edge(c, n1, n2), get_edge(d, d_n1, d_n2)) add_edge_attrs(d, c_d[n1], c_d[n2], attrs_to_add) else: attrs_to_add = dict_sub(get_edge(c, n1, n2), get_edge(d, d_n1, d_n2)) add_edge_attrs(d, c_d[n1], c_d[n2], attrs_to_add) return (d, b_d, c_d)
def unfold_nugget(hie, nug_id, ag_id, mm_id, test=False): """unfold a nugget with conflicts to create multiple nuggets""" nug_gr = copy.deepcopy(hie.node[nug_id].graph) mm_typing = copy.deepcopy(hie.get_typing(nug_id, mm_id)) ag_typing = copy.deepcopy(hie.get_typing(nug_id, ag_id)) # create one new locus for each linked agent, region or residue linked to # a locus new_ports = {} # new_port remember the loci/state it is created from old_ports = [] non_comp_neighbors = {} for node in nug_gr.nodes(): # move the state test to explicit "is_equal" nodes if mm_typing[node] == "state" and "val" in nug_gr.node[node]: for val in nug_gr.node[node]["val"]: id_prefix = "{}_{}".format(val, node) test_id = unique_node_id(nug_gr, id_prefix) add_node(nug_gr, test_id, {"val": val}) mm_typing[test_id] = "is_equal" add_edge(nug_gr, test_id, node) # for testing if test: ag = hie.node[ag_id].graph ag_test_id = unique_node_id(ag, id_prefix) add_node(ag, ag_test_id, {"val": val}) add_edge(ag, ag_test_id, ag_typing[node]) hie.edge[ag_id][mm_id].mapping[ag_test_id] = "is_equal" real_nugget = hie.node[nug_id].graph old_test_id = unique_node_id(real_nugget, id_prefix) add_node(real_nugget, old_test_id, {"val": val}) add_edge(real_nugget, old_test_id, node) hie.edge[nug_id][ag_id].mapping[old_test_id] = ag_test_id if mm_typing[node] in ["locus", "state"]: comp_neighbors = [ comp for comp in nug_gr.successors(node) if mm_typing[comp] in ["agent", "region", "residue"] ] other_neighbors = [ other for other in (nug_gr.successors(node) + nug_gr.predecessors(node)) if other not in comp_neighbors ] old_ports.append(node) for comp in comp_neighbors: id_prefix = "{}_{}".format(node, comp) port_id = unique_node_id(nug_gr, id_prefix) add_node(nug_gr, port_id) mm_typing[port_id] = mm_typing[node] ag_typing[port_id] = ag_typing[node] new_ports[port_id] = node add_edge(nug_gr, port_id, comp) for other in other_neighbors: if mm_typing[other] in ["mod", "is_equal"]: add_edge(nug_gr, other, port_id) else: add_edge(nug_gr, port_id, other) non_comp_neighbors[port_id] = set(other_neighbors) # remove the old potentially shared between agents/region/residues loci for port in old_ports: remove_node(nug_gr, port) del mm_typing[port] del ag_typing[port] # associate the components nodes (agent,region, residue) to the ports components = {} for port in new_ports: components[port] = _agents_of_components(nug_gr, mm_typing, port) def _nonconflicting(port1, action_node1, port2, action_node2): typ1 = mm_typing[action_node1] typ2 = mm_typing[action_node2] if port1 == port2: if typ1 == typ2: return False if mm_typing[port1] == "state": return True if {typ1, typ2} & {"is_free", "is_bnd"}: return False different_loci = set(nug_gr.predecessors(action_node1)) !=\ set(nug_gr.predecessors(action_node2)) return different_loci elif action_node1 != action_node2: return True elif typ1 in ["mod", "is_equal", "is_free"]: return False else: return new_ports[port1] != new_ports[port2] def replace(node): """identify is_equal and mod nodes with same values""" if mm_typing[node] == "is_equal": return ("is_equal", str(nug_gr.node[node]["val"])) if mm_typing[node] == "mod": return ("mod", str(nug_gr.node[node]["val"])) return node def reduce_subsets(set_list): return set_list def subset_up_to_equivalence(set1, set2): set1 = {frozenset(map(replace, s)) for s in set1} set2 = {frozenset(map(replace, s)) for s in set2} return set1.issubset(set2) def replace2(node): """identify is_equal and mod nodes with same values""" if mm_typing[node] == "is_equal": return ("is_equal", str(nug_gr.node[node]["val"]), frozenset(nug_gr.successors(node))) if mm_typing[node] == "mod": return ("mod", str(nug_gr.node[node]["val"]), frozenset(nug_gr.successors(node))) return node def _equivalent_actions(act1, act2, edge_list): l1 = [(port, replace(node)) for (port, node) in edge_list if node == act1] l2 = [(port, replace(node)) for (port, node) in edge_list if node == act2] return l1 == l2 def _equivalent_edge(p1, a1, p2, a2): return p1 == p2 and replace2(a1) == replace2(a2) def _valid_subsets(memo_dict, set_list): """build non conflicting sets of sets of nodes""" if set_list == []: return [[]] memo_key = frozenset(set_list) if memo_key in memo_dict: return memo_dict[memo_key] (port, a_node) = set_list[0] conflicting_edges = [ (port2, a_node2) for (port2, a_node2) in set_list[1:] if not _nonconflicting(port, a_node, port2, a_node2) ] nonconflicting_sets =\ [(port2, a_node2) for (port2, a_node2) in set_list[1:] if _nonconflicting(port, a_node, port2, a_node2)] equivalent_edges = [ (p2, n2) for (p2, n2) in set_list if p2 == port and _equivalent_actions(a_node, n2, set_list) ] new_set_list = [ (p2, n2) for (p2, n2) in set_list[1:] if p2 != port or not _equivalent_actions(a_node, n2, set_list) ] cond1 = (len([node for (_, node) in set_list[1:] if node == a_node]) == 0 and all( replace(n2) == replace(a_node) for (p2, n2) in set_list[1:] if p2 == port)) if nonconflicting_sets == new_set_list or cond1: memo_dict[memo_key] =\ [sub + [(port, a_node)] for sub in _valid_subsets(memo_dict, nonconflicting_sets)] return memo_dict[memo_key] else: without_current_edge = _valid_subsets(memo_dict, new_set_list) def conflict_with_removed_edges(edge_list): return all( any(not _nonconflicting(p1, a_node1, p2, a_node2) for (p2, a_node2) in edge_list) for (p1, a_node1) in equivalent_edges) # with_conflict = list(filter(conflict_with_current_edge, without_current_edge)) with_conflict = list( filter(conflict_with_removed_edges, without_current_edge)) memo_dict[memo_key] =\ with_conflict +\ [sub + [(port, a_node)] for sub in _valid_subsets(memo_dict, nonconflicting_sets)] return memo_dict[memo_key] def _complete_subsets(set_list): print(set_list) return [components[port] | {a_node} for (port, a_node) in set_list] def _remove_uncomplete_actions(set_list): """remove actions and test which are not connected to enough components""" labels = {node: 0 for node in nug_gr.nodes()} for nodes in set_list: for node in nodes: labels[node] += 1 to_remove = set() for node in nug_gr.nodes(): if (mm_typing[node] in ["bnd", "brk", "is_bnd"] and labels[node] < 2): to_remove.add(node) if (mm_typing[node] in ["is_free", "mod", "is_equal"] and labels[node] < 1): to_remove.add(node) return [nodes for nodes in set_list if not nodes & to_remove] port_action_list = [(port, a_node) for (port, a_nodes) in non_comp_neighbors.items() for a_node in a_nodes] # build globally non conflicting subsets and remove the uncomplete actions memo_dict = {} valid_ncss = { frozenset( map(frozenset, _remove_uncomplete_actions(_complete_subsets(set_list)))) for set_list in _valid_subsets(memo_dict, port_action_list) } maximal_valid_ncss = valid_ncss # add the nodes that where not considered at all # because they are not connected to a locus or state nodes_with_ports = set.union( set.union(*(list(non_comp_neighbors.values()) + [set()])), set.union(*(list(components.values()) + [set()]))) nodes_without_ports = set(nug_gr.nodes()) - nodes_with_ports # build the nuggets and add them to the hierarchy # as children of the old one for testing def _graph_of_ncs(ncs): sub_graphs = [(subgraph(nug_gr, nodes), {node: node for node in nodes}) for nodes in ncs] sub_graphs.append((subgraph(nug_gr, nodes_without_ports), {node: node for node in nodes_without_ports})) return multi_pullback_pushout(nug_gr, sub_graphs) valid_graphs = map(_graph_of_ncs, maximal_valid_ncss) new_nuggets = [] for (new_nugget, new_typing) in valid_graphs: if test: typing_by_old_nugget = {} for node in new_nugget.nodes(): if new_typing[node] in hie.node[nug_id].graph.nodes(): typing_by_old_nugget[node] = new_typing[node] else: typing_by_old_nugget[node] = new_ports[new_typing[node]] new_nuggets.append((new_nugget, typing_by_old_nugget)) else: new_ag_typing = compose_homomorphisms(ag_typing, new_typing) new_mm_typing = compose_homomorphisms(mm_typing, new_typing) new_nuggets.append((new_nugget, new_ag_typing, new_mm_typing)) return new_nuggets
def test_propagation_node_adds(self): """Test propagation down of additions.""" p = NXGraph() primitives.add_nodes_from(p, ["B"]) l = NXGraph() primitives.add_nodes_from(l, ["B"]) r = NXGraph() primitives.add_nodes_from(r, ["B", "B_res_1", "X", "Y"]) primitives.add_edge(r, "B_res_1", "B") rule = Rule(p, l, r) instance = {"B": "B"} rhs_typing = { "mm": { "B_res_1": "residue" }, "mmm": { "X": "component" }, "colors": { "Y": "red" } } try: self.hierarchy.rewrite("n1", rule, instance, rhs_typing=rhs_typing, strict=True) raise ValueError("Error was not caught!") except RewritingError: pass new_hierarchy = NXHierarchy.copy(self.hierarchy) new_hierarchy.rewrite("n1", rule, instance, rhs_typing=rhs_typing) # test propagation of node adds assert ("B_res_1" in new_hierarchy.get_graph("n1").nodes()) assert ("B_res_1" in new_hierarchy.get_graph("ag").nodes()) assert (new_hierarchy.get_typing("n1", "ag")["B_res_1"] == "B_res_1") assert (new_hierarchy.get_typing("ag", "mm")["B_res_1"] == "residue") assert (("B_res_1", "B") in new_hierarchy.get_graph("n1").edges()) assert (("B_res_1", "B") in new_hierarchy.get_graph("ag").edges()) assert ("X" in new_hierarchy.get_graph("n1").nodes()) assert ("X" in new_hierarchy.get_graph("ag").nodes()) assert ("X" in new_hierarchy.get_graph("mm").nodes()) assert ("X" in new_hierarchy.get_graph("colors").nodes()) assert (new_hierarchy.get_typing("n1", "ag")["X"] == "X") assert (new_hierarchy.get_typing("ag", "mm")["X"] == "X") assert (new_hierarchy.get_typing("mm", "mmm")["X"] == "component") assert (new_hierarchy.get_typing("mm", "colors")["X"] == "X") assert ("Y" in new_hierarchy.get_graph("n1").nodes()) assert ("Y" in new_hierarchy.get_graph("ag").nodes()) assert ("Y" in new_hierarchy.get_graph("mm").nodes()) assert ("Y" in new_hierarchy.get_graph("mm").nodes()) assert (new_hierarchy.get_typing("n1", "ag")["Y"] == "Y") assert (new_hierarchy.get_typing("ag", "mm")["Y"] == "Y") assert (new_hierarchy.get_typing("mm", "mmm")["Y"] == "Y") assert (new_hierarchy.get_typing("mm", "colors")["Y"] == "red")
def test_propagation_node_adds(self): """Test propagation down of additions.""" p = nx.DiGraph() primitives.add_nodes_from( p, ["B"] ) l = nx.DiGraph() primitives.add_nodes_from( l, ["B"] ) r = nx.DiGraph() primitives.add_nodes_from( r, ["B", "B_res_1", "X", "Y"] ) primitives.add_edge(r, "B_res_1", "B") rule = Rule(p, l, r) instance = {"B": "B"} rhs_typing = { "mm": {"B_res_1": "residue"}, "mmm": {"X": "component"}, "colors": {"Y": "red"} } try: self.hierarchy.rewrite( "n1", rule, instance, lhs_typing=None, rhs_typing=rhs_typing) raise ValueError("Error was not caught!") except RewritingError: pass new_hierarchy, _ = self.hierarchy.rewrite( "n1", rule, instance, lhs_typing=None, rhs_typing=rhs_typing, strict=False, inplace=False) # test propagation of node adds assert("B_res_1" in new_hierarchy.graph["n1"].nodes()) assert("B_res_1" in new_hierarchy.graph["ag"].nodes()) assert(new_hierarchy.typing["n1"]["ag"]["B_res_1"] == "B_res_1") assert(new_hierarchy.typing["ag"]["mm"]["B_res_1"] == "residue") assert(("B_res_1", "B") in new_hierarchy.graph["n1"].edges()) assert(("B_res_1", "B") in new_hierarchy.graph["ag"].edges()) assert("X" in new_hierarchy.graph["n1"].nodes()) assert("X" in new_hierarchy.graph["ag"].nodes()) assert("X" in new_hierarchy.graph["mm"].nodes()) assert("X" in new_hierarchy.graph["colors"].nodes()) assert(new_hierarchy.typing["n1"]["ag"]["X"] == "X") assert(new_hierarchy.typing["ag"]["mm"]["X"] == "X") assert(new_hierarchy.typing["mm"]["mmm"]["X"] == "component") assert(new_hierarchy.typing["mm"]["colors"]["X"] == "X") assert("Y" in new_hierarchy.graph["n1"].nodes()) assert("Y" in new_hierarchy.graph["ag"].nodes()) assert("Y" in new_hierarchy.graph["mm"].nodes()) assert("Y" in new_hierarchy.graph["mm"].nodes()) assert(new_hierarchy.typing["n1"]["ag"]["Y"] == "Y") assert(new_hierarchy.typing["ag"]["mm"]["Y"] == "Y") assert(new_hierarchy.typing["mm"]["mmm"]["Y"] == "Y") assert(new_hierarchy.typing["mm"]["colors"]["Y"] == "red")
def pushout(a, b, c, a_b, a_c, inplace=False): """Find the pushour of the span b <- a -> c.""" check_homomorphism(a, b, a_b) check_homomorphism(a, c, a_c) if inplace is True: d = b else: d = copy.deepcopy(b) b_d = id_of(b.nodes()) c_d = dict() # Add/merge nodes for c_n in c.nodes(): a_keys = keys_by_value(a_c, c_n) # Add nodes if len(a_keys) == 0: add_node(d, c_n, c.node[c_n]) c_d[c_n] = c_n # Keep nodes elif len(a_keys) == 1: c_d[a_c[a_keys[0]]] = a_b[a_keys[0]] # Merge nodes else: nodes_to_merge = [] for k in a_keys: nodes_to_merge.append(a_b[k]) new_name = merge_nodes(d, nodes_to_merge) c_d[c_n] = new_name for node in nodes_to_merge: b_d[node] = new_name # Add edges for (n1, n2) in c.edges(): if b.is_directed(): if (c_d[n1], c_d[n2]) not in d.edges(): add_edge( d, c_d[n1], c_d[n2], get_edge(c, n1, n2)) else: if (c_d[n1], c_d[n2]) not in d.edges() and\ (c_d[n2], c_d[n1]) not in d.edges(): add_edge( d, c_d[n1], c_d[n2], get_edge(c, n1, n2) ) # Add node attrs for c_n in c.nodes(): a_keys = keys_by_value(a_c, c_n) # Add attributes to the nodes which stayed invariant if len(a_keys) == 1: attrs_to_add = dict_sub( c.node[c_n], a.node[a_keys[0]] ) add_node_attrs(d, c_d[c_n], attrs_to_add) # Add attributes to the nodes which were merged elif len(a_keys) > 1: merged_attrs = {} for k in a_keys: merged_attrs = merge_attributes( merged_attrs, a.node[k] ) attrs_to_add = dict_sub(c.node[c_n], merged_attrs) add_node_attrs(d, c_d[c_n], attrs_to_add) # Add edge attrs for (n1, n2) in c.edges(): d_n1 = c_d[n1] d_n2 = c_d[n2] if d.is_directed(): attrs_to_add = dict_sub( get_edge(c, n1, n2), get_edge(d, d_n1, d_n2) ) add_edge_attrs( d, c_d[n1], c_d[n2], attrs_to_add ) else: attrs_to_add = dict_sub( get_edge(c, n1, n2), get_edge(d, d_n1, d_n2) ) add_edge_attrs( d, c_d[n1], c_d[n2], attrs_to_add ) return (d, b_d, c_d)