def test_pushout_symmetry_directed(self): A = NXGraph() A.add_nodes_from(["a", "b"]) A.add_edges_from([("a", "b")]) B = NXGraph() B.add_nodes_from([1, 2, 3]) B.add_edges_from([(2, 3), (3, 2), (1, 3)]) C = NXGraph() C.add_nodes_from(["x", "y"]) C.add_edges_from([("x", "x"), ("x", "y")]) homAB = {"a": 2, "b": 3} homAC = {"a": "x", "b": "x"} D, homBD, homCD = pushout( A, B, C, homAB, homAC ) D_inv, homCD_inv, homBD_inv = pushout( A, C, B, homAC, homAB ) assert_equals(len(D.nodes()), len(D_inv.nodes())) assert_equals(len(D.edges()), len(D_inv.edges()))
def test_pushout(self): D, homBD, homCD = pushout(self.A, self.B, self.C, self.homAB, self.homAC) assert_equals(type(D), nx.DiGraph) assert_equals(len(D.nodes()), len(self.D.nodes())) assert_equals(len(D.edges()), len(self.D.edges())) assert (id(self.B) != id(D))
def test_pushout_inplace(self): B_copy = copy.deepcopy(self.B) D, homBD, homCD = pushout( self.A, B_copy, self.C, self.homAB, self.homAC, inplace=True ) assert_equals(type(D), NXGraph) assert_equals(len(D.nodes()), len(self.D.nodes())) assert_equals(len(D.edges()), len(self.D.edges())) assert(id(B_copy) == id(D))
def apply_to(self, graph, instance, inplace=False): """Perform graph rewriting with the rule. Parameters ---------- graph : nx.(Di)Graph Graph to rewrite with the rule. instance : dict Instance of the `lhs` pattern in the graph defined by a dictionary where keys are nodes of `lhs` and values are nodes of the graph. inplace : bool, optional If `True`, the rewriting will be performed in-place by applying primitve transformations to the graph object, otherwise the result of the rewriting is a new graph object. Default value is `False`. Returns ------- g_prime : nx.(Di)Graph Result of the rewriting. If parameter `inplace` was `True`, `g_prime` is exactly the (transformed) input graph object `graph`. rhs_g_prime : dict Matching of the `rhs` in `g_prime`, a dictionary, where keys are nodes of `rhs` and values are nodes of `g_prime`. """ g_m, p_g_m, g_m_g = pullback_complement( self.p, self.lhs, graph, self.p_lhs, instance, inplace ) g_prime, g_m_g_prime, rhs_g_prime = pushout( self.p, g_m, self.rhs, p_g_m, self.p_rhs, inplace) return (g_prime, rhs_g_prime)
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 get_rule_projections(tx, hierarchy, graph_id, rule, instance, rhs_typing=None): """Execute the query finding rule liftings.""" if rhs_typing is None: rhs_typing = {} projections = {} if rule.is_relaxing(): if len(rule.lhs.nodes()) > 0: lhs_instance = {n: instance[n] for n in rule.lhs.nodes()} lhs_vars = {n: n for n in rule.lhs.nodes()} match_instance_vars = { v: lhs_instance[k] for k, v in lhs_vars.items() } # Match nodes query = "// Match nodes the instance of the rewritten graph \n" query += "MATCH {}".format(", ".join([ "({}:{} {{id: '{}'}})".format(k, graph_id, v) for k, v in match_instance_vars.items() ])) query += "\n\n" carry_vars = list(lhs_vars.values()) for k, v in lhs_vars.items(): query += ("OPTIONAL MATCH (n)<-[:typing*1..]-({})\n".format( v ) + "WITH {} \n".format(", ".join(carry_vars + [ "collect(DISTINCT {{type:'node', origin: {}.id, id: n.id, graph:labels(n)[0], attrs: properties(n)}}) as {}_dict\n" .format(v, v) ]))) carry_vars.append("{}_dict".format(v)) # Match edges for (u, v) in rule.p.edges(): edge_var = "{}_{}".format(lhs_vars[u], lhs_vars[v]) query += "OPTIONAL MATCH ({}_instance)-[{}:edge]->({}_instance)\n".format( lhs_vars[u], edge_var, lhs_vars[v]) query += "WHERE ({})<-[:typing*1..]-({}) AND ({})<-[:typing*1..]-({})\n".format( "{}_instance".format(lhs_vars[u]), lhs_vars[u], "{}_instance".format(lhs_vars[v]), lhs_vars[v]) query += ("WITH {} \n".format(", ".join(carry_vars + [ "collect({{type: 'edge', source: {}.id, target: {}.id, graph:labels({})[0], attrs: properties({})}}) as {}\n" .format("{}_instance".format(lhs_vars[u]), "{}_instance". format(lhs_vars[v]), "{}_instance".format( lhs_vars[u]), edge_var, edge_var) ]))) carry_vars.append(edge_var) query += "RETURN {}".format(", ".join( ["{}_dict as {}".format(v, v) for v in lhs_vars.values()] + [ "{}_{}".format(lhs_vars[u], lhs_vars[v]) for u, v in rule.p.edges() ])) result = tx.run(query) record = result.single() l_l_ts = {} l_nodes = {} l_edges = {} for k, v in record.items(): if len(v) > 0: if v[0]["type"] == "node": for el in v: l_node = keys_by_value(instance, el["origin"])[0] if el["graph"] not in l_nodes: l_nodes[el["graph"]] = {} l_l_ts[el["graph"]] = {} if el["id"] not in l_nodes[el["graph"]]: l_nodes[el["graph"]][el["id"]] = {} l_nodes[el["graph"]][el["id"]] = attrs_union( l_nodes[el["graph"]][el["id"]], attrs_intersection( generic.convert_props_to_attrs( el["attrs"]), get_node(rule.lhs, l_node))) l_l_ts[el["graph"]][l_node] = el["id"] else: for el in v: l_sources = keys_by_value(l_l_ts[el["graph"]], el["source"]) l_targets = keys_by_value(l_l_ts[el["graph"]], el["target"]) for l_source in l_sources: for l_target in l_targets: if exists_edge(rule.l, l_source, l_target): if el["graph"] not in l_edges: l_edges[el["graph"]] = {} if (el["source"], el["target"] ) not in l_edges[el["graph"]]: l_edges[el["graph"]][( el["source"], el["target"])] = {} l_edges[el["graph"]][(el["source"], el["target"])] =\ attrs_union( l_edges[el["graph"]][(el["source"], el["target"])], attrs_intersection( generic.convert_props_to_attrs(el["attrs"]), get_edge(rule.lhs, l_source, l_target))) for graph, typing in hierarchy.get_descendants(graph_id).items(): if graph in l_nodes: nodes = l_nodes[graph] else: nodes = {} if graph in l_edges: edges = l_edges[graph] else: edges = {} l = nx.DiGraph() add_nodes_from(l, [(k, v) for k, v in nodes.items()]) if graph in l_edges: add_edges_from(l, [(s, t, v) for (s, t), v in edges.items()]) rhs, p_rhs, r_r_t = pushout(rule.p, l, rule.rhs, compose(rule.p_lhs, l_l_ts[graph]), rule.p_rhs) l_t_t = {n: n for n in nodes} # Modify P_T and R_T according to the controlling # relation rhs_typing if graph in rhs_typing.keys(): r_t_factorization = { r_r_t[k]: v for k, v in rhs_typing[graph].items() } added_t_nodes = set() for n in rhs.nodes(): if n in r_t_factorization.keys(): # If corresponding R_T node is specified in # the controlling relation add nodes of T # that type it to P t_nodes = r_t_factorization[n] for t_node in t_nodes: if t_node not in l_t_t.values() and\ t_node not in added_t_nodes: new_p_node = generate_new_id(l.nodes(), t_node) l.add_node(new_p_node) added_t_nodes.add(t_node) p_rhs[new_p_node] = n l_t_t[new_p_node] = t_node else: p_rhs[keys_by_value(l_t_t, t_node)[0]] = n projections[graph] = { "rule": Rule(p=l, rhs=rhs, p_rhs=p_rhs), "instance": l_t_t, "l_l_t": l_l_ts[graph], "p_p_t": {k: l_l_ts[graph][v] for k, v in rule.p_lhs.items()}, "r_r_t": r_r_t } return projections