示例#1
0
 def test_is_connected_component_edges_direction_is_ignored(self):
     """
     Check that edges direction is ignored when checking for the connectivity.
     """
     graph = Graph()
     node_names = list(range(1, 5))
     graph.add_nodes_from(node_names)
     graph.add_edges_from([(2, 1), (2, 3), (4, 3)])
     self.assertTrue(is_connected_component(graph, node_names))
     self.assertTrue(is_connected_component(graph, [2, 1]))
     self.assertTrue(is_connected_component(graph, [4, 2, 3]))
示例#2
0
 def test_is_connected_component_two_separate_sub_graphs(self):
     """
     Check that if there are two separate sub-graphs the function returns False.
     """
     graph = Graph()
     graph.add_nodes_from(list(range(1, 7)))
     graph.add_edges_from([(1, 2), (2, 3), (4, 5), (5, 6)])
     self.assertFalse(is_connected_component(graph, list(range(1, 7))))
     self.assertFalse(is_connected_component(graph, [1, 3]))
     self.assertFalse(is_connected_component(graph, [6, 4]))
     self.assertFalse(is_connected_component(graph, [2, 5]))
示例#3
0
 def test_is_connected_component_edges_direction_is_ignored_not_connected(self):
     """
     Check that edges direction is ignored when checking for the connectivity. In this case the graph is not
     connected.
     """
     graph = Graph()
     graph.add_nodes_from(list(range(1, 5)))
     graph.add_edges_from([(2, 1), (2, 3), (4, 3)])
     self.assertFalse(is_connected_component(graph, [1, 2, 4]))
     self.assertFalse(is_connected_component(graph, [1, 4]))
     self.assertFalse(is_connected_component(graph, [2, 4]))
     self.assertFalse(is_connected_component(graph, [3, 4, 1]))
 def find_and_replace_pattern(self, graph: Graph):
     replacement_descriptions = CustomReplacementRegistry(
     ).get_custom_replacement_description(self.replacement_id)
     if replacement_descriptions is None:
         log.info(
             "Failed to find custom replacement description with id '{}'".
             format(self.replacement_id))
         return
     # there are a list of custom replacements descriptions that have the same replacement id
     for replacement_description in replacement_descriptions:
         sub_graph_matcher = SubgraphMatcher(replacement_description)
         matched_instances = list(
             sub_graph_matcher.matched_sub_graph_instances(graph))
         if not len(matched_instances):
             log.error(
                 "Failed to match nodes from custom replacement description with id '{}':\nIt means model and "
                 "custom replacement description are incompatible.\nTry to correct custom replacement "
                 "description according to documentation with respect to model node names"
                 "".format(self.replacement_id))
         for match in matched_instances:
             if not is_connected_component(graph,
                                           match.matched_nodes_names()):
                 log.warning(
                     "The following nodes don't form connected sub-graph: {}"
                     .format(match.matched_nodes_names()))
                 # graph.dump_graph_for_graphviz(match.matched_nodes_names())
             self.replace_sub_graph(graph, match)
示例#5
0
 def test_is_connected_component_connected(self):
     """
     Check that if the sub-graph is connected.
     """
     graph = Graph()
     node_names = list(range(1, 8))
     graph.add_nodes_from(node_names)
     graph.add_edges_from([(1, 2), (2, 3), (4, 5), (5, 6), (1, 7), (7, 4)])
     self.assertTrue(is_connected_component(graph, list(range(1, 8))))
示例#6
0
 def test_is_connected_component_two_separate_sub_graphs_divided_by_ignored_node(self):
     """
     Check that if there are two separate sub-graphs the function connected by an edge going through the ignored node
     then the function returns False.
     """
     graph = Graph()
     node_names = list(range(1, 8))
     graph.add_nodes_from(node_names)
     graph.add_edges_from([(1, 2), (2, 3), (4, 5), (5, 6), (1, 7), (7, 4)])
     self.assertFalse(is_connected_component(graph, list(range(1, 7))))
示例#7
0
 def find_and_replace_pattern(self, graph: Graph):
     replacement_descriptions = CustomReplacementRegistry(
     ).get_custom_replacement_description(self.replacement_id)
     if replacement_descriptions is None:
         log.info(
             "Failed to find custom replacement description with id '{}'".
             format(self.replacement_id))
         return
     # there are a list of custom replacements descriptions that have the same replacement id
     for replacement_description in replacement_descriptions:
         sub_graph_matcher = SubgraphMatcher(replacement_description)
         for match in sub_graph_matcher.matched_sub_graph_instances(graph):
             if not is_connected_component(graph,
                                           match.matched_nodes_names()):
                 log.warning(
                     "The following nodes don't form connected sub-graph: {}"
                     .format(match.matched_nodes_names()))
                 graph.dump_graph_for_graphviz(match.matched_nodes_names())
             self.replace_sub_graph(graph, match)
示例#8
0
def merge_nodes(graph: Graph, nodes_to_merge_names: list, inputs_desc: list = None,
                outputs_desc: list = None):
    """
    Merges nodes specified in the set 'nodes_to_merge_names' into one mega-node, creating new edges between mega-node
    and inputs/outputs nodes of the mega-node. The added edges contain name of input/output nodes which will be used for
    generation of placeholders and will be saved to the IR xml so IE plug-in know how to map input/output data for the
    layer. Also the function adds protobufs of the nodes of the sub-graph and 'Const' ops consumed by nodes in the
    sub-graph to the node's attribute 'pbs'.
    :param graph: the graph object to operate on.
    :param nodes_to_merge_names: list of nodes names that should be merged into a single node.
    :param inputs_desc: optional list describing input nodes order.
    :param outputs_desc: optional list describing output nodes order.
    """
    if not is_connected_component(graph, nodes_to_merge_names):
        log.warning("The following nodes do not form connected sub-graph: {}".format(nodes_to_merge_names))
        graph.dump_graph_for_graphviz(nodes_to_dump=nodes_to_merge_names)

    new_node_name = graph.unique_id("TFSubgraphCall_")
    log.info("Create new node with name '{}' for nodes '{}'".format(new_node_name, ', '.join(nodes_to_merge_names)))
    graph.add_node(new_node_name)
    new_node_attrs = graph.node[new_node_name]

    new_node_attrs['name'] = new_node_name
    set_tf_custom_call_node_attrs(new_node_attrs)
    new_node = Node(graph, new_node_name)

    added_input_tensors_names = set()  # set of tensors that are were added as input to the sub-graph
    added_new_node_output_tensors = dict()  # key - tensor name, value - out port

    for node_name in nodes_to_merge_names:
        node = Node(graph, node_name)
        add_node_pb_if_not_yet_added(node, new_node)
        # TODO: any improvements?
        for in_node_name, edge_attrs in Node(graph, node_name).get_inputs():
            in_node = Node(graph, in_node_name)

            # internal edges between nodes of the sub-graph
            if in_node_name in nodes_to_merge_names:
                add_node_pb_if_not_yet_added(in_node, new_node)
                continue

            # edge outside of sub-graph into sub-graph
            if in_node_name not in nodes_to_merge_names:
                # we cannot use the 'in_node_name' as a protobuf operation name here
                # because the 'in_node_name' could be a sub-graph matched before.
                input_tensor_name = node.pb.input[edge_attrs['in']]
                if input_tensor_name not in added_input_tensors_names:
                    graph.add_edge(in_node_name, new_node_name,
                                   **merge_edge_props(
                                       {'in': find_input_port(new_node, inputs_desc, node_name, edge_attrs['in']),
                                        'out': edge_attrs['out'],
                                        'internal_input_node_name': input_tensor_name,
                                        'original_dst_node_name': node_name,
                                        'original_dst_port': edge_attrs['in'],
                                        'in_attrs': ['in', 'internal_input_node_name', 'original_dst_node_name',
                                                     'original_dst_port', 'placeholder_name'],
                                        'out_attrs': ['out']},
                                       edge_attrs)
                                   )
                    log.debug("Creating edge from outside of sub-graph to inside sub-graph: {} -> {}".format(
                        in_node_name, new_node_name))
                    added_input_tensors_names.add(input_tensor_name)

        # edge from inside sub-graph to outside sub-graph
        for out_node_name, edge_attrs in Node(graph, node_name).get_outputs():
            if out_node_name not in nodes_to_merge_names:
                log.debug("Creating edge from inside of sub-graph to outside sub-graph: {} -> {}".format(
                    new_node_name, out_node_name))
                out_name = internal_output_name_for_node(node_name, edge_attrs['out'])
                if out_name not in added_new_node_output_tensors.keys():
                    added_new_node_output_tensors[out_name] = find_output_port(new_node, outputs_desc, node_name,
                                                                               edge_attrs['out'])
                graph.add_edge(new_node_name, out_node_name,
                               **merge_edge_props(
                                   {'in': edge_attrs['in'],
                                    'out': added_new_node_output_tensors[out_name],
                                    'internal_output_node_name': out_name,
                                    'in_attrs': ['in', 'internal_input_node_name'],
                                    'out_attrs': ['out', 'internal_output_node_name']},
                                   edge_attrs)
                               )
        new_node['output_tensors_names'] = [val for val in
                                            {v: k for k, v in added_new_node_output_tensors.items()}.values()]

    # add nodes using the same order as in initial GraphDef so we can dump them to IR in "correct" order
    new_node['nodes_order'] = [node for node in graph.graph['initial_nodes_order'] if node in new_node['pbs'].keys()]

    for n in nodes_to_merge_names:
        if graph.has_node(n):  # check if not deleted by another (similar) pattern
            graph.remove_node(n)
    return Node(graph, new_node_name)