示例#1
0
 def test_add_edge_attrs(self):
     g = self.graph.to_undirected()
     new_attrs = {"b": FiniteSet({1})}
     add_edge_attrs(g, '1', '2', new_attrs)
     normalize_attrs(new_attrs)
     assert (valid_attributes(new_attrs, g.edge['1']['2']))
     assert (valid_attributes(new_attrs, g.edge['2']['1']))
示例#2
0
    def test_merge_nodes(self):
        g = self.graph.to_undirected()

        old_attrs1 = self.graph.node['8']
        old_attrs2 = self.graph.node['9']
        old_edge_attrs1 = self.graph.edge['10']['8']
        old_edge_attrs2 = self.graph.edge['10']['9']
        new_name = merge_nodes(self.graph, ["8", "9"])
        assert(new_name in self.graph.nodes())
        assert("8" not in self.graph.nodes())
        assert("9" not in self.graph.nodes())
        assert(valid_attributes(old_attrs1, self.graph.node[new_name]))
        assert(valid_attributes(old_attrs2, self.graph.node[new_name]))
        assert((new_name, new_name) in self.graph.edges())
        assert(valid_attributes(old_edge_attrs1, self.graph.edge['10'][new_name]))
        assert(valid_attributes(old_edge_attrs2, self.graph.edge['10'][new_name]))

        # test undirected case
        old_attrs1 = g.node['8']
        old_attrs2 = g.node['9']
        old_edge_attrs1 = g.edge['10']['8']
        old_edge_attrs2 = g.edge['10']['9']
        new_name = merge_nodes(g, ["8", "9"])
        assert(new_name in g.nodes())
        assert("8" not in g.nodes())
        assert("9" not in g.nodes())
        assert(valid_attributes(old_attrs1, g.node[new_name]))
        assert(valid_attributes(old_attrs2, g.node[new_name]))
        assert((new_name, new_name) in g.edges())
        assert(valid_attributes(old_edge_attrs1, g.edge['10'][new_name]))
        assert(valid_attributes(old_edge_attrs1, g.edge[new_name]['10']))
        assert(valid_attributes(old_edge_attrs2, g.edge['10'][new_name]))
        assert(valid_attributes(old_edge_attrs2, g.edge[new_name]['10']))
        assert(g.edge['10'][new_name] == g.edge[new_name]['10'])
        assert(id(g.edge['10'][new_name]) == id(g.edge[new_name]['10']))
示例#3
0
 def test_add_edge_attrs(self):
     g = self.graph.to_undirected()
     new_attrs = {"b": FiniteSet({1})}
     add_edge_attrs(g, '1', '2', new_attrs)
     normalize_attrs(new_attrs)
     assert(valid_attributes(new_attrs, g.edge['1']['2']))
     assert(valid_attributes(new_attrs, g.edge['2']['1']))
示例#4
0
def check_homomorphism(source, target, dictionary, total=True):
    """Check if the homomorphism is valid.

    Valid homomorphism preserves edges,
    and attributes if requires.
    """
    # check if there is mapping for all the nodes of source graph
    if total:
        check_totality(source.nodes(), dictionary)
    if not set(dictionary.values()).issubset(target.nodes()):
        raise InvalidHomomorphism(
            "Some of the image nodes in mapping %s do not "
            "exist in target graph (target graph nodes %s) "
            "namely %s" % (dictionary.values(), target.nodes(),
                           set(dictionary.values()) - set(target.nodes())))

    # check connectivity
    for s, t in source.edges():
        try:
            if (s in dictionary.keys() and t in dictionary.keys()
                    and not (dictionary[s], dictionary[t]) in target.edges()):
                if not target.is_directed():
                    if not (dictionary[t], dictionary[s]) in target.edges():
                        raise InvalidHomomorphism(
                            "Connectivity is not preserved!"
                            " Was expecting an edge '%s' and '%s'" %
                            (dictionary[t], dictionary[s]))
                else:
                    raise InvalidHomomorphism(
                        "Connectivity is not preserved!"
                        " Was expecting an edge between '%s' and '%s'" %
                        (dictionary[s], dictionary[t]))
        except KeyError:
            pass

    for s, t in dictionary.items():
        # check sets of attributes of nodes (here homomorphism = set
        # inclusion)
        if not valid_attributes(source.node[s], target.node[t]):
            raise InvalidHomomorphism("Attributes of nodes source:'%s' %s and "
                                      "target:'%s' %s do not match!" %
                                      (s, source.node[s], t, target.node[t]))

    # check sets of attributes of edges (homomorphism = set inclusion)
    for s1, s2 in source.edges():
        try:
            if (s1 in dictionary.keys() and s2 in dictionary.keys()
                    and not valid_attributes(
                        source.edge[s1][s2],
                        target.edge[dictionary[s1]][dictionary[s2]])):
                raise InvalidHomomorphism(
                    "Attributes of edges (%s)-(%s) (%s) and "
                    "(%s)-(%s) (%s) do not match!" %
                    (s1, s2, source.edge[s1][s2], dictionary[s1],
                     dictionary[s2],
                     target.edge[dictionary[s1]][dictionary[s2]]))
        except KeyError:
            pass
    return True
示例#5
0
 def _compare_dicts(d1, d2):
     types1 = d1[typing_key]
     types2 = d2[typing_key]
     d1_without_types = copy.copy(d1)
     d2_without_types = copy.copy(d1)
     del d1_without_types[typing_key]
     del d2_without_types[typing_key]
     return (valid_attributes(types2, types1)
             and valid_attributes(d1_without_types, d2_without_types))
示例#6
0
 def _compare_dicts(d1, d2):
     types1 = d1[typing_key]
     types2 = d2[typing_key]
     d1_without_types = copy.copy(d1)
     d2_without_types = copy.copy(d1)
     del d1_without_types[typing_key]
     del d2_without_types[typing_key]
     return (valid_attributes(types2, types1) and
             valid_attributes(d1_without_types, d2_without_types))
示例#7
0
def check_homomorphism(source, target, dictionary, total=True):
    """Check if the homomorphism is valid.

    Valid homomorphism preserves edges,
    and attributes if requires.
    """
    # check if there is mapping for all the nodes of source graph
    if total:
        check_totality(source.nodes(), dictionary)
    if not set(dictionary.values()).issubset(target.nodes()):
        raise InvalidHomomorphism("The image nodes {} do not exist ".format(
            set(dictionary.values()) - set(target.nodes())) +
                                  "in the target graph (existing nodes '{}') ".
                                  format(target.nodes()) +
                                  "in dictionary '{}'".format(dictionary))

    # check connectivity
    for s, t in source.edges():
        try:
            if (s in dictionary.keys() and t in dictionary.keys()
                    and not (dictionary[s], dictionary[t]) in target.edges()):
                raise InvalidHomomorphism(
                    "Connectivity is not preserved!"
                    " Was expecting an edge between '{}' and '{}'".format(
                        dictionary[s], dictionary[t]))
        except KeyError:
            pass

    for s, t in dictionary.items():
        # check sets of attributes of nodes (here homomorphism = set
        # inclusion)
        if not valid_attributes(source.get_node(s), target.get_node(t)):
            raise InvalidHomomorphism(
                "Attributes of nodes source: '{}' {} and ".format(
                    s, source.get_node(s)) +
                "target: '{}' {} do not match!".format(t, target.get_node(t)))

    # check sets of attributes of edges (homomorphism = set inclusion)
    for s1, s2 in source.edges():
        try:
            if (s1 in dictionary.keys() and s2 in dictionary.keys()
                    and not valid_attributes(
                        source.get_edge(s1, s2),
                        target.get_edge(dictionary[s1], dictionary[s2]))):
                raise InvalidHomomorphism(
                    "Attributes of edges ({})-({}) ({}) and ".format(
                        s1, s2, source.get_edge(s1, s2)) +
                    "({})-({}) ({}) do not match!".format(
                        dictionary[s1], dictionary[s2],
                        target.get_edge(dictionary[s1], dictionary[s2])))
        except KeyError:
            pass
    return True
示例#8
0
    def test_merge_nodes(self):
        g = self.graph

        old_attrs1 = self.graph.get_node('8')
        old_attrs2 = self.graph.get_node('9')
        old_edge_attrs1 = self.graph.get_edge('10', '8')
        old_edge_attrs2 = self.graph.get_edge('10', '9')
        new_name = merge_nodes(self.graph, ["8", "9"])
        assert (new_name in self.graph.nodes())
        assert ("8" not in self.graph.nodes())
        assert ("9" not in self.graph.nodes())
        assert (valid_attributes(old_attrs1, self.graph.get_node(new_name)))
        assert (valid_attributes(old_attrs2, self.graph.get_node(new_name)))
        assert ((new_name, new_name) in self.graph.edges())
        assert (valid_attributes(old_edge_attrs1,
                                 self.graph.get_edge('10', new_name)))
        assert (valid_attributes(old_edge_attrs2,
                                 self.graph.get_edge('10', new_name)))
示例#9
0
    def test_merge_nodes(self):
        g = self.graph.to_undirected()

        old_attrs1 = self.graph.node['8']
        old_attrs2 = self.graph.node['9']
        old_edge_attrs1 = self.graph.edge['10']['8']
        old_edge_attrs2 = self.graph.edge['10']['9']
        new_name = merge_nodes(self.graph, ["8", "9"])
        assert (new_name in self.graph.nodes())
        assert ("8" not in self.graph.nodes())
        assert ("9" not in self.graph.nodes())
        assert (valid_attributes(old_attrs1, self.graph.node[new_name]))
        assert (valid_attributes(old_attrs2, self.graph.node[new_name]))
        assert ((new_name, new_name) in self.graph.edges())
        assert (valid_attributes(old_edge_attrs1,
                                 self.graph.edge['10'][new_name]))
        assert (valid_attributes(old_edge_attrs2,
                                 self.graph.edge['10'][new_name]))

        # test undirected case
        old_attrs1 = g.node['8']
        old_attrs2 = g.node['9']
        old_edge_attrs1 = g.edge['10']['8']
        old_edge_attrs2 = g.edge['10']['9']
        new_name = merge_nodes(g, ["8", "9"])
        assert (new_name in g.nodes())
        assert ("8" not in g.nodes())
        assert ("9" not in g.nodes())
        assert (valid_attributes(old_attrs1, g.node[new_name]))
        assert (valid_attributes(old_attrs2, g.node[new_name]))
        assert ((new_name, new_name) in g.edges())
        assert (valid_attributes(old_edge_attrs1, g.edge['10'][new_name]))
        assert (valid_attributes(old_edge_attrs1, g.edge[new_name]['10']))
        assert (valid_attributes(old_edge_attrs2, g.edge['10'][new_name]))
        assert (valid_attributes(old_edge_attrs2, g.edge[new_name]['10']))
        assert (g.edge['10'][new_name] == g.edge[new_name]['10'])
        assert (id(g.edge['10'][new_name]) == id(g.edge[new_name]['10']))
示例#10
0
文件: tree.py 项目: y1ngyang/ReGraph
def _same_graphs(hie1, hie2, rel):
    for id1, id2s in rel.items():
        for id2 in id2s:
            new_rel = copy.deepcopy(rel)
            for ch2 in all_children(hie2, id2):
                for ch1 in all_children(hie1, id1):
                    if hie1.node[ch1].attrs["name"] == hie2.node[ch2].attrs["name"]\
                            and hie1.node[ch1] == hie2.node[ch2]:
                        if ch1 in new_rel:
                            new_rel[ch1].add(ch2)
                        else:
                            new_rel[ch1] = {ch2}
    if valid_attributes(new_rel, rel):
        return rel
    else:
        return _same_graphs(hie1, hie2, new_rel)
示例#11
0
def _same_graphs(hie1, hie2, rel):
    for id1, id2s in rel.items():
        for id2 in id2s:
            new_rel = copy.deepcopy(rel)
            for ch2 in all_children(hie2, id2):
                for ch1 in all_children(hie1, id1):
                    if hie1.node[ch1].attrs["name"] == hie2.node[ch2].attrs["name"]\
                            and hie1.node[ch1] == hie2.node[ch2]:
                        if ch1 in new_rel:
                            new_rel[ch1].add(ch2)
                        else:
                            new_rel[ch1] = {ch2}
    if valid_attributes(new_rel, rel):
        return rel
    else:
        return _same_graphs(hie1, hie2, new_rel)
示例#12
0
def add_attributes(hie, g_id, parent, node, json_attrs):
    """add the attributes from the json_attrs dict to a node"""
    attrs = prim.json_dict_to_attrs(json_attrs)
    if isinstance(hie.node[g_id], GraphNode):
        for typing in hie.successors(g_id):
            mapping = hie.edge[g_id][typing].mapping
            if node in mapping:
                parent_attrs = hie.node[typing].graph.node[mapping[node]]
                if not valid_attributes(attrs, parent_attrs):
                    raise ValueError("Attributes not in node {} of {}"
                                     .format(mapping[node], typing))
        prim.add_node_attrs(hie.node[g_id].graph, node, attrs)

    elif isinstance(hie.node[g_id], RuleNode):
        hie.node[g_id].rule.add_node_attrs_rhs(node, attrs)
    else:
        raise ValueError("node is neither a rule nor a graph")
示例#13
0
文件: tree.py 项目: y1ngyang/ReGraph
def add_attributes(hie, g_id, parent, node, json_attrs):
    """add the attributes from the json_attrs dict to a node"""
    attrs = prim.json_dict_to_attrs(json_attrs)
    if isinstance(hie.node[g_id], GraphNode):
        for typing in hie.successors(g_id):
            mapping = hie.edge[g_id][typing].mapping
            if node in mapping:
                parent_attrs = hie.node[typing].graph.node[mapping[node]]
                if not valid_attributes(attrs, parent_attrs):
                    raise ValueError("Attributes not in node {} of {}".format(
                        mapping[node], typing))
        prim.add_node_attrs(hie.node[g_id].graph, node, attrs)

    elif isinstance(hie.node[g_id], RuleNode):
        hie.node[g_id].rule.add_node_attrs_rhs(node, attrs)
    else:
        raise ValueError("node is neither a rule nor a graph")
示例#14
0
    def find_matching(self,
                      pattern,
                      nodes=None,
                      graph_typing=None,
                      pattern_typing=None):
        """Find matching of a pattern in a graph.

        This function takes as an input a graph and a pattern, optionally,
        it also takes a collection of nodes specifying the subgraph of the
        original graph, where the matching should be searched in, then it
        searches for a matching of the pattern inside of the graph (or induced
        subragh), which corresponds to solving subgraph matching problem.
        The matching is defined by a map from the nodes of the pattern
        to the nodes of the graph such that:

        * edges are preserved, i.e. if there is an edge between nodes `n1`
          and `n2` in the pattern, there is an edge between the nodes of
          the graph that correspond to the image of `n1` and `n2`,
          moreover, the attribute dictionary of the edge between `n1`
          and `n2` is the subdictiotary of the edge it corresponds to
          in the graph;
        * the attribute dictionary of a pattern node is a subdictionary of
          its image in the graph;

        Uses `networkx.isomorphism.(Di)GraphMatcher` class, which implements
        subgraph matching algorithm.

        In addition, two parameters `graph_typing` and `pattern_typing`
        can be specified. They restrict the space of admisible solutions
        by checking if an isomorphic subgraph found in the input graph respects
        the provided pattern typings according to the specified graph typings.

        Parameters
        ----------
        graph : nx.(Di)Graph
        pattern : nx.(Di)Graph
            Pattern graph to search for
        nodes : iterable, optional
            Subset of nodes to search for matching
        graph_typing : dict of dict, optional
            Dictionary defining typing of graph nodes
        pattern_typing : dict of dict, optional
            Dictionary definiting typing of pattern nodes

        Returns
        -------
        instances : list of dict's
            List of instances of matching found in the graph, every instance
            is represented with a dictionary where keys are nodes of the
            pattern, and values are corresponding nodes of the graph.

        """
        new_pattern_typing = dict()
        if pattern_typing:
            for graph, pattern_mapping in pattern_typing.items():
                new_pattern_typing[graph] = normalize_relation(pattern_mapping)

        if graph_typing is None:
            graph_typing = {}

        # check graph/pattern typing is consistent
        for g, mapping in new_pattern_typing.items():
            if g not in graph_typing:
                raise ReGraphError(
                    "Graph is not typed by '{}' from the specified ".format(
                        g) + "pattern typing")

        if nodes is not None:
            g = self._graph.subgraph(nodes)
        else:
            g = self._graph

        labels_mapping = dict([(n, i + 1) for i, n in enumerate(g.nodes())])
        g = self.get_relabeled_graph(labels_mapping, raw=True)
        inverse_mapping = dict([(value, key)
                                for key, value in labels_mapping.items()])

        matching_nodes = set()

        # find all the nodes matching the nodes in pattern
        for pattern_node in pattern.nodes():
            for node in g.nodes():
                if new_pattern_typing:
                    # check types match
                    match = False
                    for graph, pattern_mapping in new_pattern_typing.items():
                        if node in graph_typing[graph].keys() and\
                           pattern_node in pattern_mapping.keys():
                            if graph_typing[graph][node] in pattern_mapping[
                                    pattern_node]:
                                if valid_attributes(
                                        pattern.get_node(pattern_node),
                                        g.nodes[node]):
                                    match = True
                        else:
                            if valid_attributes(pattern.get_node(pattern_node),
                                                g.nodes[node]):
                                match = True
                    if match:
                        matching_nodes.add(node)
                else:
                    if valid_attributes(pattern.get_node(pattern_node),
                                        g.nodes[node]):
                        matching_nodes.add(node)

        # find all the isomorphic subgraphs
        reduced_graph = g.subgraph(matching_nodes)
        instances = []
        isomorphic_subgraphs = []
        for sub_nodes in itertools.combinations(reduced_graph.nodes(),
                                                len(pattern.nodes())):
            subg = reduced_graph.subgraph(sub_nodes)
            for edgeset in itertools.combinations(subg.edges(),
                                                  len(pattern.edges())):
                edge_induced_graph = nx.DiGraph(list(edgeset))
                edge_induced_graph.add_nodes_from([
                    n for n in subg.nodes()
                    if n not in edge_induced_graph.nodes()
                ])
                if isinstance(pattern, Graph):
                    matching_obj = isomorphism.DiGraphMatcher(
                        pattern._graph, edge_induced_graph)
                else:
                    matching_obj = isomorphism.DiGraphMatcher(
                        pattern, edge_induced_graph)
                for isom in matching_obj.isomorphisms_iter():
                    isomorphic_subgraphs.append((subg, isom))

        for subgraph, mapping in isomorphic_subgraphs:
            # print(subgraph.nodes(), mapping)
            # check node matches
            # exclude subgraphs which nodes information does not
            # correspond to pattern
            for (pattern_node, node) in mapping.items():
                if new_pattern_typing:
                    for g, pattern_mapping in new_pattern_typing.items():
                        if inverse_mapping[node] in graph_typing[g].keys() and\
                           pattern_node in pattern_mapping.keys():
                            if graph_typing[g][inverse_mapping[
                                    node]] not in pattern_mapping[
                                        pattern_node]:
                                break
                        if not valid_attributes(pattern.get_node(pattern_node),
                                                subgraph.nodes[node]):
                            break
                    else:
                        continue
                    break
                else:
                    if not valid_attributes(pattern.get_node(pattern_node),
                                            subgraph.nodes[node]):
                        break
            else:
                # check edge attribute matched
                for edge in pattern.edges():
                    pattern_attrs = pattern.get_edge(edge[0], edge[1])
                    target_attrs = subgraph.adj[mapping[edge[0]]][mapping[
                        edge[1]]]
                    if not valid_attributes(pattern_attrs, target_attrs):
                        break
                else:
                    instances.append(mapping)

        # bring back original labeling
        for instance in instances:
            for key, value in instance.items():
                instance[key] = inverse_mapping[value]

        return instances
示例#15
0
 def _compare_dicts(d1, d2):
     return valid_attributes(d2, d1)
示例#16
0
def find_matching(graph, pattern):
    """Find matching of a pattern in a graph.

    This function takes as an input a graph and a pattern graph, it
    searches for a matching of the pattern inside of the graph
    (corresponds to subgraph matching problem). The matching is defined by
    a map from the nodes of the pattern to the nodes of the graph such that:

    * edges are preserved, i.e. if there is an edge between nodes `n1` and `n2`
      in the pattern, there is an edge between the nodes of the graph that
      correspond to the image of `n1` and `n2`, moreover, the attribute
      dictionary of the edge between `n1` and `n2` is the subdictiotary of
      the edge it corresponds to in the graph;
    * the attribute dictionary of a pattern node is a subdictionary of
      its image in the graph;

    Uses `networkx.isomorphism.(Di)GraphMatcher` class, which implements
    subgraph matching algorithm.

    Parameters
    ----------
    graph : nx.(Di)Graph
    pattern : nx.(Di)Graph
        Pattern graph to search for

    Returns
    -------
    instances : list of dict's
        List of instances of matching found in the graph, every instance
        is represented with a dictionary where keys are nodes of the
        pattern, and values are corresponding nodes of the graph.

    Examples
    --------
    Suppose you are given the following graph:

    >>> g = networkx.DiGraph()
    >>> add_nodes_from(g, [(1, {"color": {"red"}}), 2, (3, {"color": {"blue"}})])
    >>> add_edges_from(g, [(1, 1), (1, 2), (3, 3), (3, 2)])

    And you would like to match the following pattern:

    >>> pattern = networkx.DiGraph()
    >>> add_nodes_from(pattern, [("x", {"color": "blue"}), "y"])
    >>> add_edges_from(pattern, [("x", "x"), ("x", "y")])

    Matching instances can be found as follows:

    >>> instances = find_matching(g, pattern)
    >>> instances
    [{"x": 3, "y": 2}]

    """
    labels_mapping = dict([(n, i + 1) for i, n in enumerate(graph.nodes())])
    g = get_relabeled_graph(graph, labels_mapping)
    matching_nodes = set()

    # find all the nodes matching the nodes in pattern
    for pattern_node in pattern.nodes():
        for node in g.nodes():
            if valid_attributes(pattern.node[pattern_node], g.node[node]):
                matching_nodes.add(node)
    reduced_graph = g.subgraph(matching_nodes)
    instances = []
    isomorphic_subgraphs = []
    for sub_nodes in itertools.combinations(reduced_graph.nodes(),
                                            len(pattern.nodes())):
        subg = reduced_graph.subgraph(sub_nodes)
        for edgeset in itertools.combinations(subg.edges(),
                                              len(pattern.edges())):
            if g.is_directed():
                edge_induced_graph = nx.DiGraph(list(edgeset))
                edge_induced_graph.add_nodes_from([
                    n for n in subg.nodes()
                    if n not in edge_induced_graph.nodes()
                ])
                matching_obj = isomorphism.DiGraphMatcher(
                    pattern, edge_induced_graph)
                for isom in matching_obj.isomorphisms_iter():
                    isomorphic_subgraphs.append((subg, isom))
            else:
                edge_induced_graph = nx.Graph(edgeset)
                edge_induced_graph.add_nodes_from([
                    n for n in subg.nodes()
                    if n not in edge_induced_graph.nodes()
                ])
                matching_obj = isomorphism.GraphMatcher(
                    pattern, edge_induced_graph)
                for isom in matching_obj.isomorphisms_iter():
                    isomorphic_subgraphs.append((subg, isom))

    for subgraph, mapping in isomorphic_subgraphs:
        # check node matches
        # exclude subgraphs which nodes information does not
        # correspond to pattern
        for (pattern_node, node) in mapping.items():
            if not valid_attributes(pattern.node[pattern_node],
                                    subgraph.node[node]):
                break
        else:
            # check edge attribute matched
            for edge in pattern.edges():
                pattern_attrs = get_edge(pattern, edge[0], edge[1])
                target_attrs = get_edge(subgraph, mapping[edge[0]],
                                        mapping[edge[1]])
                if not valid_attributes(pattern_attrs, target_attrs):
                    break
            else:
                instances.append(mapping)

    # bring back original labeling
    inverse_mapping = dict([(value, key)
                            for key, value in labels_mapping.items()])
    for instance in instances:
        for key, value in instance.items():
            instance[key] = inverse_mapping[value]
    return instances
示例#17
0
 def test_remove_edge_attrs(self):
     g = self.graph.to_undirected()
     attrs = {"s": FiniteSet({"p"})}
     remove_edge_attrs(g, '1', '2', attrs)
     assert (not valid_attributes(attrs, g.edge['1']['2']))
     assert (not valid_attributes(attrs, g.edge['2']['1']))
示例#18
0
 def test_remove_edge_attrs(self):
     g = self.graph.to_undirected()
     attrs = {"s": FiniteSet({"p"})}
     remove_edge_attrs(g, '1', '2', attrs)
     assert(not valid_attributes(attrs, g.edge['1']['2']))
     assert(not valid_attributes(attrs, g.edge['2']['1']))
示例#19
0
def check_homomorphism(source, target, dictionary, total=True):
    """Check if the homomorphism is valid.

    Valid homomorphism preserves edges,
    and attributes if requires.
    """
    # check if there is mapping for all the nodes of source graph
    if total:
        check_totality(source.nodes(), dictionary)
    if not set(dictionary.values()).issubset(target.nodes()):
        raise InvalidHomomorphism(
            "Some of the image nodes in mapping %s do not "
            "exist in target graph (target graph nodes %s) "
            "namely %s" %
            (dictionary.values(),
             target.nodes(),
             set(dictionary.values()) - set(target.nodes()))
        )

    # check connectivity
    for s, t in source.edges():
        try:
            if (s in dictionary.keys() and
                    t in dictionary.keys() and
                    not (dictionary[s], dictionary[t])
                    in target.edges()):
                if not target.is_directed():
                    if not (dictionary[t], dictionary[s]) in target.edges():
                        raise InvalidHomomorphism(
                            "Connectivity is not preserved!"
                            " Was expecting an edge '%s' and '%s'" %
                            (dictionary[t], dictionary[s]))
                else:
                    raise InvalidHomomorphism(
                        "Connectivity is not preserved!"
                        " Was expecting an edge between '%s' and '%s'" %
                        (dictionary[s], dictionary[t]))
        except KeyError:
            pass

    for s, t in dictionary.items():
            # check sets of attributes of nodes (here homomorphism = set
            # inclusion)
        if not valid_attributes(source.node[s], target.node[t]):
            raise InvalidHomomorphism(
                "Attributes of nodes source:'%s' %s and "
                "target:'%s' %s do not match!" %
                (s, source.node[s], t, target.node[t])
            )

    # check sets of attributes of edges (homomorphism = set inclusion)
    for s1, s2 in source.edges():
        try:
            if (s1 in dictionary.keys() and s2 in dictionary.keys() and
                    not valid_attributes(
                        source.edge[s1][s2],
                        target.edge[dictionary[s1]][dictionary[s2]])):
                raise InvalidHomomorphism(
                    "Attributes of edges (%s)-(%s) (%s) and "
                    "(%s)-(%s) (%s) do not match!" %
                    (s1, s2, source.edge[s1][s2], dictionary[s1],
                     dictionary[s2],
                     target.edge[dictionary[s1]][dictionary[s2]]))
        except KeyError:
            pass
    return True
示例#20
0
 def _compare_dicts(d1, d2):
     return valid_attributes(d2, d1)
示例#21
0
def find_matching(graph, pattern):
    """Find matching of a pattern in a graph.

    This function takes as an input a graph and a pattern graph, it
    searches for a matching of the pattern inside of the graph
    (corresponds to subgraph matching problem). The matching is defined by
    a map from the nodes of the pattern to the nodes of the graph such that:

    * edges are preserved, i.e. if there is an edge between nodes `n1` and `n2`
      in the pattern, there is an edge between the nodes of the graph that
      correspond to the image of `n1` and `n2`, moreover, the attribute
      dictionary of the edge between `n1` and `n2` is the subdictiotary of
      the edge it corresponds to in the graph;
    * the attribute dictionary of a pattern node is a subdictionary of
      its image in the graph;

    Uses `networkx.isomorphism.(Di)GraphMatcher` class, which implements
    subgraph matching algorithm.

    Parameters
    ----------
    graph : nx.(Di)Graph
    pattern : nx.(Di)Graph
        Pattern graph to search for

    Returns
    -------
    instances : list of dict's
        List of instances of matching found in the graph, every instance
        is represented with a dictionary where keys are nodes of the
        pattern, and values are corresponding nodes of the graph.

    Examples
    --------
    Suppose you are given the following graph:

    >>> g = networkx.DiGraph()
    >>> add_nodes_from(g, [(1, {"color": {"red"}}), 2, (3, {"color": {"blue"}})])
    >>> add_edges_from(g, [(1, 1), (1, 2), (3, 3), (3, 2)])

    And you would like to match the following pattern:

    >>> pattern = networkx.DiGraph()
    >>> add_nodes_from(pattern, [("x", {"color": "blue"}), "y"])
    >>> add_edges_from(pattern, [("x", "x"), ("x", "y")])

    Matching instances can be found as follows:

    >>> instances = find_matching(g, pattern)
    >>> instances
    [{"x": 3, "y": 2}]

    """
    labels_mapping = dict([(n, i + 1) for i, n in enumerate(graph.nodes())])
    g = get_relabeled_graph(graph, labels_mapping)
    matching_nodes = set()

    # find all the nodes matching the nodes in pattern
    for pattern_node in pattern.nodes():
        for node in g.nodes():
            if valid_attributes(pattern.node[pattern_node], g.node[node]):
                matching_nodes.add(node)
    reduced_graph = g.subgraph(matching_nodes)
    instances = []
    isomorphic_subgraphs = []
    for sub_nodes in itertools.combinations(reduced_graph.nodes(),
                                            len(pattern.nodes())):
        subg = reduced_graph.subgraph(sub_nodes)
        for edgeset in itertools.combinations(subg.edges(),
                                              len(pattern.edges())):
            if g.is_directed():
                edge_induced_graph = nx.DiGraph(list(edgeset))
                edge_induced_graph.add_nodes_from(
                    [n for n in subg.nodes() if n not in edge_induced_graph.nodes()])
                matching_obj = isomorphism.DiGraphMatcher(
                    pattern, edge_induced_graph)
                for isom in matching_obj.isomorphisms_iter():
                    isomorphic_subgraphs.append((subg, isom))
            else:
                edge_induced_graph = nx.Graph(edgeset)
                edge_induced_graph.add_nodes_from(
                    [n for n in subg.nodes() if n not in edge_induced_graph.nodes()])
                matching_obj = isomorphism.GraphMatcher(
                    pattern, edge_induced_graph)
                for isom in matching_obj.isomorphisms_iter():
                    isomorphic_subgraphs.append((subg, isom))

    for subgraph, mapping in isomorphic_subgraphs:
        # check node matches
        # exclude subgraphs which nodes information does not
        # correspond to pattern
        for (pattern_node, node) in mapping.items():
            if not valid_attributes(pattern.node[pattern_node], subgraph.node[node]):
                break
        else:
            # check edge attribute matched
            for edge in pattern.edges():
                pattern_attrs = get_edge(pattern, edge[0], edge[1])
                target_attrs = get_edge(
                    subgraph, mapping[edge[0]], mapping[edge[1]])
                if not valid_attributes(pattern_attrs, target_attrs):
                    break
            else:
                instances.append(mapping)

    # bring back original labeling
    inverse_mapping = dict(
        [(value, key) for key, value in labels_mapping.items()]
    )
    for instance in instances:
        for key, value in instance.items():
            instance[key] = inverse_mapping[value]
    return instances