def _check_rule_typing(hierarchy, rule_id, graph_id, lhs_mapping, rhs_mapping): all_paths = nx.all_pairs_shortest_path(hierarchy) paths_from_target = {} for s in hierarchy.nodes(): if s == graph_id: for key in all_paths[graph_id].keys(): paths_from_target[key] = all_paths[graph_id][key] for t in paths_from_target.keys(): if t != graph_id: new_lhs_h = compose( lhs_mapping, hierarchy.compose_path_typing(paths_from_target[t])) new_rhs_h = compose( rhs_mapping, hierarchy.compose_path_typing(paths_from_target[t])) try: # find homomorphisms from s to t via other paths s_t_paths = nx.all_shortest_paths(hierarchy, rule_id, t) for path in s_t_paths: lhs_h, rhs_h = hierarchy.compose_path_typing(path) if lhs_h != new_lhs_h: raise HierarchyError( "Invalid lhs typing: homomorphism does not " "commute with an existing " "path from '%s' to '%s'!" % (s, t)) if rhs_h != new_rhs_h: raise HierarchyError( "Invalid rhs typing: homomorphism does not " "commute with an existing " + "path from '%s' to '%s'!" % (s, t)) except (nx.NetworkXNoPath): pass return
def __rmul__(self, other): """Right multiplication.""" if isinstance(other, Typing): return RuleTyping(compose(self.lhs_mapping, other.mapping), compose(self.rhs_mapping, other.mapping), self.lhs_total and other.total, self.rhs_total and other.total, self.attrs) else: return NotImplemented
def __mul__(self, other): """Multiplication operation.""" if isinstance(other, Typing): return Typing(compose(other.mapping, self.mapping)) elif isinstance(other, RuleTyping): return RuleTyping(compose(other.lhs_mapping, self.mapping), compose(other.rhs_mapping, self.mapping), other.lhs_total, other.rhs_total) else: return NotImplemented
def _check_consistency(hierarchy, source, target, mapping=None): all_paths = nx.all_pairs_shortest_path(hierarchy) paths_to_source = {} paths_from_target = {} for s in hierarchy.nodes(): if source in all_paths[s].keys(): paths_to_source[s] = all_paths[s][source] if s == target: for key in all_paths[target].keys(): paths_from_target[key] = all_paths[target][key] for s in paths_to_source.keys(): if hierarchy._path_from_rule(paths_to_source[s]): for t in paths_from_target.keys(): # find homomorphism from s to t via new path if s == source: raise HierarchyError( "Found a rule typing some node in the hierarchy!") new_lhs_h, new_rhs_h = hierarchy.compose_path_typing( paths_to_source[s]) new_lhs_h = compose(new_lhs_h, mapping) new_rhs_h = compose(new_rhs_h, mapping) if t != target: new_lhs_h = compose( new_lhs_h, hierarchy.compose_path_typing(paths_from_target[t])) new_rhs_h = compose( new_rhs_h, hierarchy.compose_path_typing(paths_from_target[t]), ) try: # find homomorphisms from s to t via other paths s_t_paths = nx.all_shortest_paths(hierarchy, s, t) for path in s_t_paths: lhs_h, rhs_h = hierarchy.compose_path_typing(path) if lhs_h != new_lhs_h: raise HierarchyError( "Invalid lhs typing: homomorphism does " "not commute with an existing " + "path from '%s' to '%s'!" % (s, t)) if rhs_h != new_rhs_h: raise HierarchyError( "Invalid rhs typing: homomorphism does " "not commute with an existing " + "path from '%s' to '%s'!" % (s, t)) except (nx.NetworkXNoPath): pass else: for t in paths_from_target.keys(): # find homomorphism from s to t via new path if s != source: new_homomorphism = hierarchy.compose_path_typing( paths_to_source[s]) else: new_homomorphism = dict([(key, key) for key, _ in mapping.items()]) new_homomorphism = compose(new_homomorphism, mapping) if t != target: new_homomorphism = compose( new_homomorphism, hierarchy.compose_path_typing(paths_from_target[t])) # find homomorphisms from s to t via other paths s_t_paths = nx.all_shortest_paths(hierarchy, s, t) try: # check only the first path for path in s_t_paths: path_homomorphism = hierarchy.compose_path_typing(path) if path_homomorphism != new_homomorphism: raise HierarchyError( "Homomorphism does not commute with an " + "existing path from '%s' to '%s'!" % (s, t)) except (nx.NetworkXNoPath): pass
def _propagate_down(hierarchy, origin_id, origin_construct, rule, instance, rhs_typing_rels, inplace=False): """Propagate changes down the hierarchy.""" updated_graphs = dict() updated_homomorphisms = dict() updated_relations = [] (origin_m, origin_m_origin, origin_prime, origin_m_origin_prime, rhs_origin_prime) = origin_construct if rule.is_relaxing(): for graph in nx.bfs_tree(hierarchy, origin_id): if graph != origin_id: relation_g_rhs = set() for key, values in rhs_typing_rels[graph].items(): for v in values: relation_g_rhs.add((v, key)) (g_prime, g_g_prime, rhs_g_prime) =\ pushout_from_relation( hierarchy.node[graph].graph, rule.rhs, relation_g_rhs, inplace) updated_graphs[graph] = (g_prime, g_g_prime, rhs_g_prime) graph_predecessors = hierarchy.predecessors(graph) if origin_id in graph_predecessors: updated_homomorphisms[(origin_id, graph)] =\ get_unique_map_from_pushout( origin_prime.nodes(), origin_m_origin_prime, rhs_origin_prime, compose_chain( [origin_m_origin, hierarchy.edge[origin_id][graph].mapping, g_g_prime]), rhs_g_prime) if len(rule.added_nodes()) > 0 or\ len(rule.merged_nodes()) > 0: for pred in hierarchy.predecessors(graph): if pred in updated_graphs.keys(): if pred != origin_id: updated_homomorphisms[(pred, graph)] =\ get_unique_map_from_pushout( updated_graphs[pred][0].nodes(), updated_graphs[pred][1], updated_graphs[pred][2], compose( hierarchy.edge[pred][graph].mapping, g_g_prime), rhs_g_prime) for suc in hierarchy.successors(graph): if suc in updated_graphs.keys(): updated_homomorphisms[(graph, suc)] =\ get_unique_map_from_pushout( g_prime.nodes(), g_g_prime, rhs_g_prime, compose( hierarchy.edge[graph][suc].mapping, updated_graphs[suc][1]), updated_graphs[suc][2]) if len(rule.merged_nodes()) > 0: # propagate changes to adjacent relations for related_g in hierarchy.adjacent_relations(graph): updated_relations.append((graph, related_g)) else: for suc in hierarchy.successors(origin_id): updated_homomorphisms[(origin_id, suc)] =\ compose( origin_m_origin, hierarchy.edge[origin_id][suc].mapping) return { "graphs": updated_graphs, "homomorphisms": updated_homomorphisms, "relations": updated_relations }
def _propagate_up(hierarchy, graph_id, rule, instance, p_origin_m, origin_m_origin_prime, inplace=False): updated_graphs = dict() updated_homomorphisms = dict() updated_relations = set() updated_rules = dict() updated_rule_h = dict() if rule.is_restrictive(): for graph in nx.bfs_tree(hierarchy, graph_id, reverse=True): if graph != graph_id: if isinstance(hierarchy.node[graph], hierarchy.graph_node_cls): origin_typing = hierarchy.get_typing(graph, graph_id) (graph_prime, graph_prime_graph, graph_prime_origin) =\ _propagate_rule_to( hierarchy.node[graph].graph, origin_typing, rule, instance, p_origin_m, inplace) updated_graphs[graph] =\ (graph_prime, graph_prime_graph, None, graph_prime_origin) graph_successors = hierarchy.successors(graph) if graph_id in graph_successors: updated_homomorphisms[(graph, graph_id)] =\ compose( graph_prime_origin, origin_m_origin_prime) if len(rule.removed_nodes()) > 0 or\ len(rule.cloned_nodes()) > 0: for suc in graph_successors: if suc != graph_id: if suc in updated_graphs.keys(): graph_prime_suc_prime =\ get_unique_map_to_pullback( updated_graphs[suc][0].nodes(), updated_graphs[suc][1], updated_graphs[suc][3], compose( graph_prime_graph, hierarchy.edge[graph][suc].mapping), graph_prime_origin) else: graph_prime_suc_prime = compose( graph_prime_graph, hierarchy.edge[graph][suc].mapping) updated_homomorphisms[( graph, suc)] = graph_prime_suc_prime for pred in hierarchy.predecessors(graph): if pred in updated_graphs.keys(): pred_m_graph_m = get_unique_map_to_pullback( graph_prime.nodes(), graph_prime_graph, graph_prime_origin, updated_graphs[pred][1], updated_graphs[pred][3]) updated_homomorphisms[(pred, graph)] = pred_m_graph_m # propagate changes to adjacent relations for related_g in hierarchy.adjacent_relations(graph): updated_relations.add((graph, related_g)) else: rule_to_rewrite = hierarchy.node[graph].rule (lhs_origin_typing, p_origin_typing, rhs_origin_typing) =\ hierarchy.get_rule_typing(graph, graph_id) (lhs_prime, lhs_prime_lhs, lhs_prime_origin) =\ _propagate_rule_to( rule_to_rewrite.lhs, lhs_origin_typing, rule, instance, p_origin_m, inplace=False) (pr_prime, pr_prime_pr, pr_prime_origin) =\ _propagate_rule_to( rule_to_rewrite.p, p_origin_typing, rule, instance, p_origin_m, inplace=False) (rhs_prime, rhs_prime_rhs, rhs_prime_origin) =\ _propagate_rule_to( rule_to_rewrite.rhs, rhs_origin_typing, rule, instance, p_origin_m, inplace=False) # find p_m -> lhs_m new_p_lhs = get_unique_map_to_pullback( lhs_prime.nodes(), lhs_prime_lhs, lhs_prime_origin, compose(pr_prime_pr, rule_to_rewrite.p_lhs), pr_prime_origin) # find p_m -> rhs_m new_p_rhs = get_unique_map_to_pullback( rhs_prime.nodes(), rhs_prime_rhs, rhs_prime_origin, compose(pr_prime_pr, rule_to_rewrite.p_rhs), pr_prime_origin) new_rule =\ Rule(pr_prime, lhs_prime, rhs_prime, new_p_lhs, new_p_rhs) updated_rules[graph] = new_rule for suc in hierarchy.successors(graph): if suc == graph_id: lhs_prime_suc_prime =\ compose(lhs_prime_origin, origin_m_origin_prime) rhs_prime_suc_prime =\ compose(rhs_prime_origin, origin_m_origin_prime) if suc in updated_graphs.keys(): lhs_prime_suc_prime = get_unique_map_to_pullback( updated_graphs[suc][0].nodes(), updated_graphs[suc][1], updated_graphs[suc][3], compose( lhs_prime_lhs, hierarchy.edge[graph][suc].lhs_mapping), lhs_prime_origin) rhs_prime_suc_prime = get_unique_map_to_pullback( updated_graphs[suc][0].nodes(), updated_graphs[suc][1], updated_graphs[suc][3], compose( rhs_prime_rhs, hierarchy.edge[graph][suc].rhs_mapping), rhs_prime_origin) else: lhs_prime_suc_prime =\ compose( lhs_prime_lhs, hierarchy.edge[graph][suc].lhs_mapping) rhs_prime_suc_prime =\ compose( rhs_prime_rhs, hierarchy.edge[graph][suc].rhs_mapping) updated_rule_h[(graph, suc)] =\ (lhs_prime_suc_prime, rhs_prime_suc_prime) else: for pred in hierarchy.predecessors(graph_id): if isinstance(hierarchy.node[pred], hierarchy.graph_node_cls): updated_homomorphisms[(pred, graph_id)] =\ compose( hierarchy.edge[pred][graph_id].mapping, origin_m_origin_prime) else: updated_rule_h[(pred, graph_id)] = ( compose(hierarchy.edge[pred][graph_id].lhs_mapping, origin_m_origin_prime), compose(hierarchy.edge[pred][graph_id].rhs_mapping, origin_m_origin_prime)) return { "graphs": updated_graphs, "homomorphisms": updated_homomorphisms, "rules": updated_rules, "rule_homomorphisms": updated_rule_h, "relations": updated_relations }
def __rmul__(self, other): """Right multiplication operation.""" if isinstance(other, Typing): return Typing(compose(other.mapping), self.attrs) else: return NotImplemented
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