Пример #1
0
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
Пример #2
0
 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
Пример #3
0
    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
Пример #4
0
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
Пример #5
0
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
    }
Пример #6
0
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
    }
Пример #7
0
 def __rmul__(self, other):
     """Right multiplication operation."""
     if isinstance(other, Typing):
         return Typing(compose(other.mapping), self.attrs)
     else:
         return NotImplemented
Пример #8
0
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