def inject_remove_node(self, lhs_node_id): """Inject a new node removal to the rule. This method removes from `p` all the nodes that map to the node with the id `lhs_node_id`. In addition, all the nodes from `rhs` that are mapped by the nodes removed in `p` are also removed. Parameters ---------- lhs_node_id Id of the node in `lhs` that should be removed by the rule. """ # remove corresponding nodes from p and rhs p_keys = keys_by_value(self.p_lhs, lhs_node_id) for k in p_keys: if k in self.p.nodes(): primitives.remove_node(self.p, k) if self.p_rhs[k] in self.rhs.nodes(): primitives.remove_node(self.rhs, self.p_rhs[k]) affected_nodes = keys_by_value(self.p_rhs, self.p_rhs[k]) for node in affected_nodes: del self.p_rhs[node] del self.p_lhs[k] return
def added_edge_attrs(self): """Get edge attributes added by the rule. Returns ------- attrs : dict Dictionary where keys are edges from `rhs` and values are attribute dictionaries to add. """ attrs = dict() for s, t in self.rhs.edges(): s_p_nodes = keys_by_value(self.p_rhs, s) t_p_nodes = keys_by_value(self.p_rhs, t) new_attrs = {} for s_p_node in s_p_nodes: for t_p_node in t_p_nodes: if (s_p_node, t_p_node) in self.p.edges(): new_attrs = attrs_union( new_attrs, dict_sub( self.rhs.edge[s][t], self.p.edge[s_p_node][t_p_node] ) ) return attrs
def _remove_edge_rhs(self, node1, node2): """Remove edge from the rhs of the graph.""" primitives.remove_edge(self.rhs, node1, node2) for pn1 in keys_by_value(self.p_rhs, node1): for pn2 in keys_by_value(self.p_rhs, node2): if (pn1, pn2) in self.p.edges(): primitives.remove_edge(self.p, pn1, pn2)
def merge_nodes(self, n1, n2, node_id=None): """Merge two nodes of the graph.""" # Update graphs new_name = None p_keys_1 = keys_by_value(self.p_lhs, n1) p_keys_2 = keys_by_value(self.p_lhs, n2) nodes_to_merge = set() 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 ) nodes_to_merge.add(self.p_rhs[k1]) nodes_to_merge.add(self.p_rhs[k2]) new_name = primitives.merge_nodes( self.rhs, list(nodes_to_merge), node_id=node_id ) # Update mappings keys = p_keys_1 + p_keys_2 for k in keys: self.p_rhs[k] = new_name return new_name
def removed_edge_attrs(self): """Get edge attributes removed by the rule. Returns ------- attrs : dict Dictionary where keys are edges from `lhs` and values are attribute dictionaries to remove. """ attrs = dict() for s, t in self.lhs.edges(): s_p_nodes = keys_by_value(self.p_lhs, s) t_p_nodes = keys_by_value(self.p_lhs, t) new_attrs = {} for s_p_node in s_p_nodes: for t_p_node in t_p_nodes: if (s_p_node, t_p_node) in self.p.edges(): new_attrs = attrs_union( new_attrs, dict_sub( self.lhs.edge[s][t], self.p.edge[s_p_node][t_p_node] ) ) if len(new_attrs) > 0: attrs[(s, t)] = new_attrs return attrs
def get_unique_map_to_pullback_complement_full(a_p, p_c, a_prime_a, a_prime_z, z_c): """Find morphism z->p using the UP of PBC.""" # Preliminary checks if not is_monic(a_p): raise ReGraphError("Morphism 'a_p' is required to be a mono " "to use the UP of the pullback complement") z_p = {} for z_element, c_element in z_c.items(): a_prime_elements = keys_by_value(a_prime_z, z_element) p_elements1 = set() # candidate p elements for a_prime_element in a_prime_elements: p_elements1.add(a_p[a_prime_a[a_prime_element]]) # resolve ambiguity going the other way p_elements2 = keys_by_value(p_c, c_element) if len(p_elements1) == 0: if len(p_elements2) == 1: z_p[z_element] = list(p_elements2)[0] else: raise ValueError("Something is wrong") else: intersection = p_elements1.intersection(p_elements2) if len(intersection) == 1: z_p[z_element] = list(intersection)[0] else: raise ValueError("Something is wrong") return z_p
def remove_edge_rhs(self, node1, node2): """Remove edge from the rhs of the graph.""" primitives.remove_edge(self.rhs, node1, node2) for pn1 in keys_by_value(self.p_rhs, node1): for pn2 in keys_by_value(self.p_rhs, node2): print(pn1, pn2) try: primitives.remove_edge(self.p, pn1, pn2) except GraphError: continue
def removed_edges(self): """.""" edges = set() for s, t in self.lhs.edges(): s_p_nodes = keys_by_value(self.p_lhs, s) t_p_nodes = keys_by_value(self.p_lhs, t) if len(s_p_nodes) != 0 and len(t_p_nodes) != 0: for s_p_node in s_p_nodes: for t_p_node in t_p_nodes: if (s_p_node, t_p_node) not in self.p.edges(): edges.add((s_p_node, t_p_node)) return edges
def subtract(a, b, ba_mapping): """Subtract graphs provided node mapping. Subtract graph B from A having mapping of nodes from B to nodes from A specified. Parameters ---------- a : networkx.(Di)Graph b : networkx.(Di)Graph ba_mapping : dict Returns ------- Graph representing the difference a - b. Examples -------- >>> a = nx.DiGraph() >>> add_nodes_from(a, [1, 2, 3]) >>> add_edges_from(a, [(1, 2), (2, 2), (2, 3)]) >>> b = nx.DiGraph() >>> add_nodes_from(b, ['x', 'y']) >>> ba_mapping = {'x': 1, 'y': 3} >>> diff = subtract(a, b, ba_mapping) >>> diff.nodes() [2] >>> diff.edges() [(2, 2)] """ res = type(a)() f = ba_mapping for n in a.nodes(): if n not in f.values(): add_node(res, n, a.node[n]) for (n1, n2) in a.edges(): if n1 in res.nodes() and n2 in res.nodes(): b_keys_1 = keys_by_value(f, n1) b_keys_2 = keys_by_value(f, n2) if len(b_keys_1) == 0 or len(b_keys_2) == 0: add_edge(res, n1, n2, get_edge(a, n1, n2)) else: for k1 in b_keys_1: for k2 in b_keys_2: if (k1, k2) not in b.edges(): add_edge(res, n1, n2, get_edge(a, n1, n2)) return res
def remove_node(self, n): """Remove a node in the graph.""" # remove corresponding nodes from p and rhs p_keys = keys_by_value(self.p_lhs, n) for k in p_keys: if k in self.p.nodes(): primitives.remove_node(self.p, k) if self.p_rhs[k] in self.rhs.nodes(): primitives.remove_node(self.rhs, self.p_rhs[k]) affected_nodes = keys_by_value(self.p_rhs, self.p_rhs[k]) for node in affected_nodes: del self.p_rhs[node] del self.p_lhs[k] return
def inject_remove_edge(self, n1, n2): """Inject removal of an edge by the rule. Parameters ---------- n1 : hashable Id of an edge's source node in `lhs`. n2 : hashable Id of an edge's target node in `lhs`. Raises ------ RuleError If some of the nodes are not found in neither `lhs` nor `p`, or if a corresponding edge in `p` does not exist. """ # 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) # n1 is actually a node from `p` if len(p_keys_1) == 0: if n1 in self.p.nodes(): p_keys_1 = [n1] else: raise RuleError( "Node '%s' is not found in neither left-hand " "side nor preserved part" % n2) if len(p_keys_2) == 0: if n2 in self.p.nodes(): p_keys_2 = [n2] else: raise RuleError( "Node '%s' is not found in neither left-hand " "side nor preserved part" % str(n2)) for k1 in p_keys_1: for k2 in p_keys_2: if (k1, k2) in self.p.edges(): primitives.remove_edge(self.p, k1, k2) primitives.remove_edge(self.rhs, self.p_rhs[k1], self.p_rhs[k2]) else: raise RuleError( "Edge '%s->%s' does not exist in the preserved part" % (k1, k2)) return
def remove_edge(self, n1, n2): """Remove edge from 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) # Remove edge from the preserved part & rhs of the rule 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" % 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" % k2 ) rhs_key_1 = self.p_rhs[k1] rhs_key_2 = self.p_rhs[k2] if self.p.is_directed(): if (k1, k2) not in self.p.edges(): raise RuleError( "Edge '%s->%s' does not exist in the preserved " "part of the rule " % (k1, k2) ) if (rhs_key_1, rhs_key_2) not in self.rhs.edges(): raise RuleError( "Edge '%s->%s' does not exist in the right hand " "side of the rule " % (rhs_key_1, rhs_key_2) ) primitives.remove_edge(self.p, k1, k2) primitives.remove_edge(self.rhs, rhs_key_1, rhs_key_2) else: if (k1, k2) not in self.p.edges() and\ (k2, k1) not in self.p.edges(): raise RuleError( "Edge '%s->%s' does not exist in the " "preserved part of the rule " % (k1, k2) ) if (rhs_key_1, rhs_key_2) not in self.rhs.edges() and\ (rhs_key_2, rhs_key_1) not in self.rhs.edges(): raise RuleError( "Edge '%s->%s' does not exist in the right " "hand side of the rule " % (rhs_key_1, rhs_key_2) ) primitives.remove_edge(self.p, k1, k2) return
def get_unique_map_to_pullback_complement(a_p, p_c, a_prime_a, a_prime_z, z_c): """Find morphism z->p using the UP of PBC.""" # Preliminary checks if not is_monic(a_p): raise ReGraphError("Morphism 'a_p' is required to be a mono " "to use the UP of the pullback complement") z_p = {} for z_element, c_element in z_c.items(): a_prime_elements = keys_by_value(a_prime_z, z_element) p_elements1 = set() # candidate p elements for a_prime_element in a_prime_elements: p_elements1.add(a_p[a_prime_a[a_prime_element]]) # resolve ambiguity going the other way p_elements2 = keys_by_value(p_c, c_element) if len(p_elements1) == 0: if len(p_elements2) == 1: z_p[z_element] = list(p_elements2)[0] else: raise ValueError( "Cannot apply the universal property, " + "check if the conditions to apply it are satisfied, " + "problem: element '{}' from Z ".format(z_element) + "corresponds to more than one element " + "from P (i.e. corresponds to {}) and A'->A->P doesn't ". format(p_elements2) + "resolve the conflict") else: intersection = p_elements1.intersection(p_elements2) if len(intersection) == 1: z_p[z_element] = list(intersection)[0] elif len(intersection) == 0: raise ValueError( "Cannot apply the universal property, " + "check if the conditions to apply it are satisfied, " + "problem: element '{}' from Z ".format(z_element) + "corresponds to '{}' ".format(p_elements1) + "from P following A'->A->P ".format(intersection) + "to '{}' following (P->C)^{{-1}} composed with (Z -> C)". format(p_elements2)) else: raise ValueError( "Cannot apply the universal property, " + "check if the conditions to apply it are satisfied, " + "problem: element '{}' from Z ".format(z_element) + "doesn't corresponds to exactly one element " + "from P (i.e. corresponds to {}) in both A'->A->P ".format( intersection) + "and (P->C)^{{-1}} composed with (Z -> C)") return z_p
def inject_update_node_attrs(self, n, attrs): """Inject an update of node attrs by the rule. Parameters ---------- n : hashable Id of a node from the left-hand side whose attrs should be updated attrs : dict Dictionary of new attrs that will replace the old ones Raises ------ RuleError If node `n` does not exist in the left-hand side or is being removed by the rule. """ if n not in self.lhs.nodes(): raise RuleError( "Node '%s' does not exist in the left hand " "side of the rule" % n) p_keys = keys_by_value(self.p_lhs, n) if len(p_keys) == 0: raise RuleError( "Node '%s' is being removed by the rule, " "cannot update attributes" % n) for k in p_keys: self.p.node[k] = None primitives.update_node_attrs(self.rhs, self.p_rhs[k], attrs) return
def _refine_delta(self, delta): lhs = delta["rule"].refine(self.graph, delta["lhs_instance"]) delta["lhs_instance"] = lhs for n in delta["rule"].rhs.nodes(): if n not in delta["rhs_instance"].keys(): delta["rhs_instance"][n] = lhs[ delta["rule"].p_lhs[keys_by_value(delta["rule"].p_rhs, n)[0]]]
def _check_totality(hierarchy, graph_id, rule, instance, lhs_typing, rhs_typing): """"Check that everything is typed at the end of the rewriting.""" for node in rule.rhs.nodes(): p_nodes = keys_by_value(rule.p_rhs, node) for typing_graph in hierarchy.successors(graph_id): typing = hierarchy.edge[graph_id][typing_graph].mapping # Totality can be broken in two cases if len(p_nodes) > 1: # node will be merged all_untyped = True for p_node in p_nodes: if instance[rule.p_lhs[p_node]] in typing.keys(): all_untyped = False break if all_untyped: continue if typing_graph in rhs_typing.keys() and\ node in rhs_typing[typing_graph].keys(): continue else: raise RewritingError( "Rewriting is strict (no propagation of types is " "allowed), typing of the node `%s` " "in rhs is required (typing by the following " "graph stays unresolved: '%s')!" % (node, typing_graph))
def test_inject_clone_node(self): pattern = nx.DiGraph() prim.add_nodes_from(pattern, [1, 2, 3]) prim.add_edges_from(pattern, [(1, 2), (3, 2)]) rule = Rule.from_transform(pattern) new_p_node, new_rhs_node = rule.inject_clone_node(2) check_homomorphism(rule.p, rule.lhs, rule.p_lhs) check_homomorphism(rule.p, rule.rhs, rule.p_rhs) assert (new_p_node in rule.p.nodes()) assert (new_rhs_node in rule.rhs.nodes()) assert (rule.p_rhs[new_p_node] == new_rhs_node) assert ((1, new_p_node) in rule.p.edges()) assert ((3, new_p_node) in rule.p.edges()) assert ((1, new_rhs_node) in rule.rhs.edges()) assert ((3, new_rhs_node) in rule.rhs.edges()) new_p_node, new_rhs_node = rule.inject_clone_node(2) assert (len(keys_by_value(rule.p_lhs, 2)) == 3) check_homomorphism(rule.p, rule.lhs, rule.p_lhs) check_homomorphism(rule.p, rule.rhs, rule.p_rhs) rule.inject_remove_node(3) try: rule.inject_clone_node(3) raise ValueError("Cloning of removed node was not caught") except: pass
def add_node(self, node_id, attrs=None): """Add node to the graph. Parameters ---------- node_id : hashable Id of a node to add attrs : dict, optional Attributes of the node. Raises ------ RuleError If node with this id already exists in the `rhs`. """ if node_id not in self.rhs.nodes(): p_keys = keys_by_value(self.p_rhs, node_id) # here we check for the nodes with the same name in the lhs for k in p_keys: lhs_key = self.p_lhs[k] if lhs_key == node_id: raise RuleError( "Node with the id '%s' already exists in the " "left hand side of the rule" % node_id ) primitives.add_node(self.rhs, node_id, attrs) else: raise RuleError( "Node with the id '%s' already exists in the " "right hand side of the rule" % node_id )
def get_unique_map(a, b, c, d, a_b, b_d, c_d): """Get a map a->c that makes a square commute.""" a_c = dict() for node in b.nodes(): a_keys = keys_by_value(a_b, node) if len(a_keys) > 0: # node stayed in the rule if node in b_d.keys(): d_node = b_d[node] c_keys = keys_by_value(c_d, d_node) if len(a_keys) != len(c_keys): raise ReGraphError("Map is not unique!") else: for i, a_key in enumerate(a_keys): a_c[a_key] = c_keys[i] return a_c
def _autocomplete_typing(hierarchy, graph_id, instance, lhs_typing, p_typing, rhs_typing_rel, p_lhs, p_rhs): if lhs_typing is None: new_lhs_typing = dict() else: new_lhs_typing = format_typing(lhs_typing) if p_typing is None: new_p_typing = dict() else: new_p_typing = normalize_typing_relation(p_typing) if rhs_typing_rel is None: new_rhs_typing_rel = dict() else: new_rhs_typing_rel = normalize_typing_relation(rhs_typing_rel) successors = list(hierarchy.successors(graph_id)) if len(successors) > 0: ancestors = hierarchy.get_descendants(graph_id) for anc, anc_typing in ancestors.items(): if anc not in new_rhs_typing_rel.keys(): new_rhs_typing_rel[anc] = dict() merged_nodes = set() for r_node in p_rhs.values(): p_nodes = keys_by_value(p_rhs, r_node) if len(p_nodes) > 1: merged_nodes.add(r_node) for typing_graph in hierarchy.successors(graph_id): typing = hierarchy.typing[graph_id][typing_graph] # Autocomplete lhs and rhs typings # by immediate successors induced by an instance if typing_graph not in new_lhs_typing.keys(): new_lhs_typing[typing_graph] = dict() for (source, target) in instance.items(): if source not in new_lhs_typing[typing_graph].keys(): if target in typing.keys(): new_lhs_typing[typing_graph][source] = typing[target] for (p_node, l_node) in p_lhs.items(): if l_node in new_lhs_typing[typing_graph].keys(): if p_rhs[p_node] not in new_rhs_typing_rel[ typing_graph].keys(): new_rhs_typing_rel[typing_graph][p_rhs[p_node]] = set() new_rhs_typing_rel[typing_graph][p_rhs[p_node]].add( new_lhs_typing[typing_graph][l_node]) # Second step of autocompletion of rhs typing for graph, typing in new_rhs_typing_rel.items(): ancestors = hierarchy.get_descendants(graph) for ancestor, ancestor_typing in ancestors.items(): dif = set(typing.keys()) -\ set(new_rhs_typing_rel[ancestor].keys()) for node in dif: type_set = set() for el in new_rhs_typing_rel[graph][node]: type_set.add(ancestor_typing[el]) new_rhs_typing_rel[ancestor][node] = type_set return (new_lhs_typing, new_p_typing, new_rhs_typing_rel)
def added_edges(self): """.""" edges = set() for s, t in self.rhs.edges(): s_p_nodes = keys_by_value(self.p_rhs, s) t_p_nodes = keys_by_value(self.p_rhs, t) if len(s_p_nodes) == 0 or len(t_p_nodes) == 0: edges.add((s, t)) else: found_edge = False for s_p_node in s_p_nodes: for t_p_node in t_p_nodes: if (s_p_node, t_p_node) in self.p.edges(): found_edge = True if not found_edge: edges.add((s, t)) return edges
def remove_node_rhs(self, n): """Remove a node from a rhs.""" p_keys = keys_by_value(self.p_rhs, n) for p_node in p_keys: primitives.remove_node(self.p, p_node) del self.p_rhs[p_node] del self.p_lhs[p_node] primitives.remove_node(self.rhs, n)
def added_nodes(self): """A set of nodes from rhs which are added by a rule.""" nodes = set() for r_node in self.rhs.nodes(): p_nodes = keys_by_value(self.p_rhs, r_node) if len(p_nodes) == 0: nodes.add(r_node) return nodes
def removed_nodes(self): """.""" nodes = set() for node in self.lhs.nodes(): p_nodes = keys_by_value(self.p_lhs, node) if len(p_nodes) == 0: nodes.add(node) return nodes
def cloned_nodes(self): """.""" nodes = dict() for node in self.lhs.nodes(): p_nodes = keys_by_value(self.p_lhs, node) if len(p_nodes) > 1: nodes[node] = set(p_nodes) return nodes
def _autocomplete_typing(hierarchy, graph_id, instance, lhs_typing, rhs_typing_rel, p_lhs, p_rhs): successors = list(hierarchy.successors(graph_id)) if len(successors) > 0: if lhs_typing is None: new_lhs_typing = dict() else: new_lhs_typing = format_typing(lhs_typing) if rhs_typing_rel is None: new_rhs_typing_rel = dict() else: new_rhs_typing_rel = normalize_typing_relation(rhs_typing_rel) ancestors = hierarchy.get_ancestors(graph_id) for anc, anc_typing in ancestors.items(): if anc not in new_rhs_typing_rel.keys(): new_rhs_typing_rel[anc] = dict() merged_nodes = set() for r_node in p_rhs.values(): p_nodes = keys_by_value(p_rhs, r_node) if len(p_nodes) > 1: merged_nodes.add(r_node) for typing_graph in hierarchy.successors(graph_id): typing = hierarchy.typing[graph_id][typing_graph] # Autocomplete lhs and rhs typings # by immediate successors induced by an instance if typing_graph not in new_lhs_typing.keys(): new_lhs_typing[typing_graph] = dict() for (source, target) in instance.items(): if source not in new_lhs_typing[typing_graph].keys(): if target in typing.keys(): new_lhs_typing[typing_graph][source] = typing[target] for (p_node, l_node) in p_lhs.items(): if l_node in new_lhs_typing[typing_graph].keys(): if p_rhs[p_node] not in new_rhs_typing_rel[typing_graph].keys(): new_rhs_typing_rel[typing_graph][p_rhs[p_node]] = set() new_rhs_typing_rel[typing_graph][p_rhs[p_node]].add( new_lhs_typing[typing_graph][l_node]) # Second step of autocompletion of rhs typing for graph, typing in new_rhs_typing_rel.items(): ancestors = hierarchy.get_ancestors(graph) for ancestor, ancestor_typing in ancestors.items(): dif = set(typing.keys()) -\ set(new_rhs_typing_rel[ancestor].keys()) for node in dif: type_set = set() for el in new_rhs_typing_rel[graph][node]: type_set.add(ancestor_typing[el]) new_rhs_typing_rel[ancestor][node] = type_set return (new_lhs_typing, new_rhs_typing_rel) else: return (None, None)
def added_edge_attrs(self): """.""" attrs = dict() for s, t in self.rhs.edges(): s_p_nodes = keys_by_value(self.p_rhs, s) t_p_nodes = keys_by_value(self.p_rhs, t) new_attrs = {} for s_p_node in s_p_nodes: for t_p_node in t_p_nodes: if (s_p_node, t_p_node) in self.p.edges(): new_attrs = attrs_union( new_attrs, dict_sub( self.rhs.edge[s][t], self.p.edge[s_p_node][t_p_node] ) ) return attrs
def removed_edges(self): """Get edges removed by the rule. Returns ------- edges : set Set of edges from `lhs` removed by the rule. """ edges = set() for s, t in self.lhs.edges(): s_p_nodes = keys_by_value(self.p_lhs, s) t_p_nodes = keys_by_value(self.p_lhs, t) if len(s_p_nodes) != 0 and len(t_p_nodes) != 0: for s_p_node in s_p_nodes: for t_p_node in t_p_nodes: if (s_p_node, t_p_node) not in self.p.edges(): edges.add((s_p_node, t_p_node)) return edges
def _add_node_attrs_lhs(self, n, attrs): if n not in self.lhs.nodes(): raise RuleError( "Node '%s' does not exist in the lhs " "of the rule" % n) primitives.add_node_attrs(self.lhs, n, attrs) p_nodes = keys_by_value(self.p_rhs, n) for p_node in p_nodes: primitives.add_node_attrs(self.p, p_node, attrs)
def get_unique_map(a, b, c, d, a_b, b_d, c_d): """Get a map a->c that makes a square commute.""" a_c = dict() for node in b.nodes(): a_keys = keys_by_value(a_b, node) if len(a_keys) > 0: # node stayed in the rule if node in b_d.keys(): d_node = b_d[node] c_keys = keys_by_value( c_d, d_node ) if len(a_keys) != len(c_keys): raise ReGraphError("Map is not unique!") else: for i, a_key in enumerate(a_keys): a_c[a_key] = c_keys[i] return a_c
def get_unique_map_to_pullback(p, p_a, p_b, z_a, z_b): """Find a unique map to pullback.""" z_p = dict() for value in p: z_keys_from_a = set() if value in p_a.keys(): a_value = p_a[value] z_keys_from_a = set(keys_by_value(z_a, a_value)) z_keys_from_b = set() if value in p_b.keys(): b_value = p_b[value] z_keys_from_b.update(keys_by_value(z_b, b_value)) z_keys = z_keys_from_a.intersection(z_keys_from_b) for z_key in z_keys: z_p[z_key] = value return z_p
def removed_edge_attrs(self): """.""" attrs = dict() for s, t in self.lhs.edges(): s_p_nodes = keys_by_value(self.p_lhs, s) t_p_nodes = keys_by_value(self.p_lhs, t) new_attrs = {} for s_p_node in s_p_nodes: for t_p_node in t_p_nodes: if (s_p_node, t_p_node) in self.p.edges(): new_attrs = attrs_union( new_attrs, dict_sub( self.lhs.edge[s][t], self.p.edge[s_p_node][t_p_node] ) ) if len(new_attrs) > 0: attrs[(s, t)] = new_attrs return attrs
def remove_node_attrs_rhs(self, n, attrs): """Remove attrs of a node in the rhs.""" if n not in self.rhs.nodes(): raise RuleError( "Node '%s' does not exist in the right hand " "side of the rule" % n) p_keys = keys_by_value(self.p_rhs, n) for p_node in p_keys: primitives.remove_node_attrs(self.p, p_node, attrs) primitives.remove_node_attrs(self.rhs, n, attrs)
def removed_node_attrs(self): """.""" attrs = dict() for node in self.lhs.nodes(): p_nodes = keys_by_value(self.p_lhs, node) new_attrs = {} for p_node in p_nodes: new_attrs = attrs_union(new_attrs, dict_sub( self.lhs.node[node], self.p.node[p_node])) if len(new_attrs) > 0: attrs[node] = new_attrs return attrs
def get_unique_map_from_pushout(p, a_p, b_p, a_z, b_z): """Find a unique map to pushout.""" p_z = dict() for value in p: z_values = set() a_values = set(keys_by_value(a_p, value)) for a_value in a_values: if a_value in a_z.keys(): z_values.add(a_z[a_value]) b_values = set(keys_by_value(b_p, value)) for b_value in b_values: if b_value in b_z.keys(): z_values.add(b_z[b_value]) if len(z_values) > 0: if len(z_values) > 1: raise ReGraphError("Cannot construct a unique map!") p_z[value] = z_values.pop() return p_z
def clone_node(self, n, node_name=None): """Clone a node of the graph.""" p_new_nodes = [] rhs_new_nodes = [] p_keys = keys_by_value(self.p_lhs, n) for k in p_keys: p_new_node = primitives.clone_node(self.p, k) p_new_nodes.append(p_new_node) rhs_new_node = primitives.clone_node(self.rhs, self.p_rhs[k]) rhs_new_nodes.append(rhs_new_node) # self.p_lhs[k] = n self.p_lhs[p_new_node] = n self.p_rhs[p_new_node] = rhs_new_node return (p_new_nodes, rhs_new_nodes)
def add_node_attrs(self, n, attrs): """Add node attributes to a node in the graph.""" if n not in self.lhs.nodes(): raise RuleError( "Node '%s' does not exist in the left " "hand side of the rule" % n) p_keys = keys_by_value(self.p_lhs, n) if len(p_keys) == 0: raise RuleError( "Node '%s' is being removed by the rule, " "cannot add attributes" % n) for k in p_keys: primitives.add_node_attrs(self.rhs, self.p_rhs[k], attrs) return
def added_node_attrs(self): """.""" attrs = dict() for node in self.rhs.nodes(): p_nodes = keys_by_value(self.p_rhs, node) # if len(p_nodes) == 0: # attrs[node] = self.rhs.node[node] new_attrs = {} for p_node in p_nodes: new_attrs = attrs_union(new_attrs, dict_sub( self.rhs.node[node], self.p.node[p_node])) if len(new_attrs) > 0: attrs[node] = new_attrs return attrs
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 update_node_attrs(self, n, attrs): """Update attributes of a node.""" if n not in self.lhs.nodes(): raise RuleError( "Node '%s' does not exist in the left hand " "side of the rule" % n) p_keys = keys_by_value(self.p_lhs, n) if len(p_keys) == 0: raise RuleError( "Node '%s' is being removed by the rule, " "cannot update attributes" % n) for k in p_keys: self.p.node[k] = None primitives.update_node_attrs(self.rhs, self.p_rhs[k], attrs) return
def clone_rhs_node(self, node, new_name=None): """Clone an rhs node.""" if node not in self.rhs.nodes(): raise RuleError( "Node '%s' is not a node of right hand side" % node ) p_keys = keys_by_value(self.p_rhs, node) if len(p_keys) == 0: primitives.clone_node(self.rhs, node, new_name) elif len(p_keys) == 1: primitives.clone_node(self.rhs, node, new_name) new_p_node = primitives.clone_node(self.p, p_keys[0]) self.p_rhs[new_p_node] = new_name self.p_lhs[new_p_node] = self.p_lhs[p_keys[0]] else: raise RuleError("Cannot clone node that is result of merge!")
def update_edge_attrs(self, n1, n2, attrs): """Update the attributes of an edge with a new set `attrs`.""" if n1 not in self.lhs.nodes(): raise RuleError( "Node '%s' does not exist in the left hand side of the rule" % n1 ) if n2 not in self.lhs.nodes(): raise RuleError( "Node '%s' does not exist in the left hand side of the rule" % n2 ) normalize_attrs(attrs) if self.lhs.is_directed(): if (n1, n2) not in self.lhs.edges(): raise RuleError( "Edge '%s->%s' does not exist in the left hand " "side of the rule" % (n1, n2) ) p_keys_1 = keys_by_value(self.p_lhs, n1) p_keys_2 = keys_by_value(self.p_lhs, n2) if len(p_keys_1) == 0: raise RuleError( "Node '%s' is being removed by the rule, cannot update " "attributes from the incident edge" % n2 ) if len(p_keys_2) == 0: raise RuleError( "Node '%s' is being removed by the rule, cannot update " "attributes from the incident edge" % n1 ) for k1 in p_keys_1: for k2 in p_keys_2: self.p.edge[k1][k2] = None primitives.update_edge_attrs( self.rhs, self.p_rhs[k1], self.p_rhs[k2], attrs ) else: if (n1, n2) not in self.lhs.edges() and\ (n2, n1) not in self.lhs.edges(): raise RuleError( "Edge '%s->%s' does not exist in the " "left hand side of the rule" % (n1, n2) ) p_keys_1 = keys_by_value(self.p_lhs, n1) p_keys_2 = keys_by_value(self.p_lhs, n2) if len(p_keys_1) == 0: raise RuleError( "Node '%s' is being removed by the rule, cannot update " "attributes from the incident edge" % n1 ) if len(p_keys_2) == 0: raise RuleError( "Node '%s' is being removed by the rule, cannot update " "attributes from the incident edge" % n2 ) for k1 in p_keys_1: for k2 in p_keys_2: self.p.edge[k1][k2] = None primitives.update_edge_attrs( self.rhs, self.p_rhs[k1], self.p_rhs[k2], attrs ) return
def _rewrite_base(hierarchy, graph_id, rule, instance, lhs_typing, rhs_typing, inplace=False): g_m, p_g_m, g_m_g =\ pullback_complement(rule.p, rule.lhs, hierarchy.node[graph_id].graph, rule.p_lhs, instance, inplace) g_prime, g_m_g_prime, r_g_prime = pushout(rule.p, g_m, rule.rhs, p_g_m, rule.p_rhs, inplace) relation_updates = [] for related_g in hierarchy.adjacent_relations(graph_id): relation_updates.append((graph_id, related_g)) updated_homomorphisms = dict() for typing_graph in hierarchy.successors(graph_id): new_hom = copy.deepcopy(hierarchy.edge[graph_id][typing_graph].mapping) removed_nodes = set() new_nodes = dict() for node in rule.lhs.nodes(): p_keys = keys_by_value(rule.p_lhs, node) # nodes that were removed if len(p_keys) == 0: removed_nodes.add(instance[node]) elif len(p_keys) == 1: if typing_graph not in rhs_typing.keys() or\ rule.p_rhs[p_keys[0]] not in rhs_typing[typing_graph].keys(): if r_g_prime[rule.p_rhs[p_keys[0]]] in new_hom.keys(): removed_nodes.add(r_g_prime[rule.p_rhs[p_keys[0]]]) # nodes were clonned elif len(p_keys) > 1: for k in p_keys: if typing_graph in rhs_typing.keys() and\ rule.p_rhs[k] in rhs_typing[typing_graph].keys(): new_nodes[r_g_prime[rule.p_rhs[k]]] =\ list(rhs_typing[typing_graph][rule.p_rhs[k]])[0] else: removed_nodes.add(r_g_prime[rule.p_rhs[k]]) for node in rule.rhs.nodes(): p_keys = keys_by_value(rule.p_rhs, node) # nodes that were added if len(p_keys) == 0: if typing_graph in rhs_typing.keys(): if node in rhs_typing[typing_graph].keys(): new_nodes[node] = list(rhs_typing[ typing_graph][node])[0] # nodes that were merged elif len(p_keys) > 1: for k in p_keys: removed_nodes.add(p_g_m[k]) # assign new type of node if typing_graph in rhs_typing.keys(): if node in rhs_typing[typing_graph].keys(): new_type = list(rhs_typing[typing_graph][node]) new_nodes[r_g_prime[node]] = new_type # update homomorphisms for n in removed_nodes: if n in new_hom.keys(): del new_hom[n] new_hom.update(new_nodes) updated_homomorphisms.update({ (graph_id, typing_graph): new_hom }) return { "graph": (g_m, p_g_m, g_m_g, g_prime, g_m_g_prime, r_g_prime), "homomorphisms": updated_homomorphisms, "relations": relation_updates }
def _propagate_rule_to(graph, origin_typing, rule, instance, p_origin, inplace=False): if inplace is True: graph_prime = graph else: graph_prime = copy.deepcopy(graph) lhs_removed_nodes = rule.removed_nodes() lhs_removed_node_attrs = rule.removed_node_attrs() p_removed_edges = rule.removed_edges() p_removed_edge_attrs = rule.removed_edge_attrs() lhs_cloned_nodes = rule.cloned_nodes() graph_prime_graph = id_of(graph.nodes()) graph_prime_origin = dict() for lhs_node in rule.lhs.nodes(): origin_node = instance[lhs_node] g_nodes = keys_by_value( origin_typing, origin_node) for node in g_nodes: if lhs_node in lhs_removed_nodes: primitives.remove_node( graph_prime, node) del graph_prime_graph[node] else: graph_prime_origin[node] = origin_node for lhs_node, p_nodes in lhs_cloned_nodes.items(): nodes_to_clone = keys_by_value(origin_typing, instance[lhs_node]) for node in nodes_to_clone: for i, p_node in enumerate(p_nodes): if i == 0: graph_prime_origin[node] = p_origin[p_node] graph_prime_graph[node] = node else: new_name = primitives.clone_node( graph_prime, node) graph_prime_origin[new_name] = p_origin[p_node] graph_prime_graph[new_name] = node for lhs_node, attrs in lhs_removed_node_attrs.items(): nodes_to_remove_attrs = keys_by_value( origin_typing, instance[lhs_node]) for node in nodes_to_remove_attrs: primitives.remove_node_attrs( graph_prime, node, attrs) for p_u, p_v in p_removed_edges: us = keys_by_value(graph_prime_origin, p_origin[p_u]) vs = keys_by_value(graph_prime_origin, p_origin[p_v]) for u in us: for v in vs: if (u, v) in graph_prime.edges(): primitives.remove_edge( graph_prime, u, v) for (p_u, p_v), attrs in p_removed_edge_attrs.items(): us = keys_by_value(origin_typing, p_origin[p_u]) vs = keys_by_value(origin_typing, p_origin[p_v]) for u in us: for v in vs: primitives.removed_edge_attrs( graph_prime, u, v, attrs) return (graph_prime, graph_prime_graph, graph_prime_origin)