Esempio n. 1
0
def basic_graph_no_v12() -> Tuple[MutableEdge, Vertex, Vertex, Vertex,
                                  Vertex]:
    complex_operation = MutableEdge((PointConv2D((1, 4)), MaxPool2D()))
    vertex1 = Vertex()
    vertex2 = Vertex()
    vertex3 = Vertex()
    vertex4 = Vertex()
    edge1 = IdentityOperation()
    edge2 = IdentityOperation()
    edge3 = IdentityOperation()
    edge4 = IdentityOperation()
    edge5 = IdentityOperation()
    edge6 = IdentityOperation()
    complex_operation.input_vertex.out_bound_edges.clear()
    complex_operation.input_vertex.out_bound_edges.extend([edge1, edge2, edge3])
    edge1.end_vertex = vertex1
    edge2.end_vertex = vertex2
    edge3.end_vertex = vertex4
    vertex1.out_bound_edges.append(edge6)
    edge6.end_vertex = complex_operation.output_vertex
    vertex2.out_bound_edges.append(edge4)
    edge4.end_vertex = complex_operation.output_vertex
    vertex3.out_bound_edges.append(edge5)
    edge5.end_vertex = complex_operation.output_vertex

    return complex_operation, vertex1, vertex2, vertex3, vertex4
Esempio n. 2
0
    def __init__(self, name: str) -> None:
        super().__init__()
        self.input_vertex = Vertex(name='input')
        self.output_vertex = Vertex(name='output')

        self.vertices_topo_order: List[Vertex] = [
            self.output_vertex, self.input_vertex
        ]
        self.name = name
        self._layers_below = -1
Esempio n. 3
0
    def build_graph(self) -> None:
        vertex1 = Vertex()
        vertex2 = Vertex()

        edge1 = IdentityOperation()
        edge2 = IdentityOperation()
        edge3 = IdentityOperation()

        self.input_vertex.out_bound_edges.extend([edge1, edge2])
        edge1.end_vertex = vertex1
        edge2.end_vertex = vertex2
        vertex2.out_bound_edges.append(edge3)
        edge3.end_vertex = vertex1
Esempio n. 4
0
    def mutation_add_vertex(self) -> bool:
        if 2 <= self.max_vertices <= len(self.vertices_topo_order):
            return False
        vertex1, vertex2 = np.random.choice(self.vertices_topo_order,
                                            size=2,
                                            replace=False)
        # Never have backward edge, to prevent cycle
        from_vertex: Vertex = max(vertex1, vertex2, key=lambda v: v.order)
        to_vertex: Vertex = min(vertex1, vertex2, key=lambda v: v.order)

        edges: Tuple[Edge, Edge] = np.random.choice(self.available_operations,
                                                    size=2,
                                                    replace=True)
        first, second = edges
        vertex = Vertex()
        first = first.deep_copy()
        second = second.deep_copy()
        first.end_vertex = vertex
        from_vertex.out_bound_edges.append(first)
        second.end_vertex = to_vertex
        vertex.out_bound_edges.append(second)

        # We changed graph structure
        self.sort_vertices()
        return True
Esempio n. 5
0
    def deep_copy_graph(self, copy: 'ComplexEdge') -> None:
        """
        Deep copy the graph to another complex edge
        Assuming the invariants holds

        Args:
            copy: To which the graph should be copied to
        Returns:
            None
        """
        for _ in range(len(self.vertices_topo_order) - 2):
            copy.vertices_topo_order.append(Vertex())

        # Clear existing edges
        copy.input_vertex.out_bound_edges.clear()
        # Copy edges
        for i, vertex in enumerate(self.vertices_topo_order):
            copy_vertex = copy.vertices_topo_order[i]
            for edge in vertex.out_bound_edges:
                copy_edge = edge.deep_copy()
                copy_vertex.out_bound_edges.append(copy_edge)
                # Check here to make mypy happy
                if edge.end_vertex:
                    copy_edge.end_vertex = copy.vertices_topo_order[
                        edge.end_vertex.order]

        copy.sort_vertices()
Esempio n. 6
0
    def build_graph(self) -> None:
        edge1 = TestEdge1()
        edge2 = edge1.deep_copy()
        edge3 = Flatten()
        edge4 = Dense(5)
        vertex1 = Vertex(name='V1')
        vertex2 = Vertex(name='V2')
        vertex3 = Vertex(name='V3')

        self.input_vertex.out_bound_edges.append(edge1)
        vertex1.out_bound_edges.append(edge2)
        edge1.end_vertex = vertex1
        edge2.end_vertex = vertex2
        vertex2.out_bound_edges.append(edge3)
        edge3.end_vertex = vertex3
        vertex3.out_bound_edges.append(edge4)
        edge4.end_vertex = self.output_vertex
Esempio n. 7
0
    def build_graph(self) -> None:
        edge1 = PointConv2D((0, 3))
        edge2 = ReLU()
        edge3 = IdentityOperation()
        vertex1 = Vertex(name='V1')

        self.input_vertex.out_bound_edges.append(edge1)
        self.input_vertex.out_bound_edges.append(edge3)
        vertex1.out_bound_edges.append(edge2)
        edge1.end_vertex = vertex1
        edge2.end_vertex = self.output_vertex
        edge3.end_vertex = self.output_vertex
Esempio n. 8
0
def test_remove_node_fail():
    complex_operation = MutableEdge((PointConv2D((1, 4)), ))
    assert not complex_operation.mutation_remove_vertex()

    complex_operation.input_vertex.out_bound_edges.clear()

    vertex1 = Vertex()
    vertex2 = Vertex()
    edge1 = IdentityOperation()
    edge2 = IdentityOperation()
    edge3 = IdentityOperation()

    complex_operation.input_vertex.out_bound_edges.append(edge1)
    edge1.end_vertex = vertex1
    vertex1.out_bound_edges.append(edge2)
    edge2.end_vertex = vertex2
    vertex2.out_bound_edges.append(edge3)
    edge3.end_vertex = complex_operation.output_vertex

    complex_operation.sort_vertices()
    assert len(complex_operation.vertices_topo_order) == 4
    assert not complex_operation.mutation_remove_vertex()
Esempio n. 9
0
def test_remove_edge_fail2():
    complex_operation = MutableEdge((PointConv2D((1, 4)), MaxPool2D()))
    edge1 = IdentityOperation()
    edge2 = IdentityOperation()
    complex_operation.input_vertex.out_bound_edges.clear()
    complex_operation.input_vertex.out_bound_edges.append(edge1)
    middle_vertex = Vertex()
    complex_operation.vertices_topo_order.append(middle_vertex)
    edge1.end_vertex = middle_vertex
    middle_vertex.out_bound_edges.append(edge2)
    edge2.end_vertex = complex_operation.output_vertex

    assert not complex_operation.mutation_remove_edge()
Esempio n. 10
0
def test_remove_edge_success():
    complex_operation = MutableEdge((PointConv2D((1, 4)), MaxPool2D()))
    edge1 = IdentityOperation()
    edge2 = IdentityOperation()
    complex_operation.input_vertex.out_bound_edges.clear()
    complex_operation.input_vertex.out_bound_edges.append(edge1)
    middle_vertex = Vertex()
    complex_operation.vertices_topo_order.append(middle_vertex)
    edge1.end_vertex = middle_vertex
    middle_vertex.out_bound_edges.append(edge2)
    edge2.end_vertex = complex_operation.output_vertex

    # Edge from input to output. So now we can remove one edge
    edge3 = IdentityOperation()
    complex_operation.input_vertex.out_bound_edges.append(edge3)
    edge3.end_vertex = complex_operation.output_vertex

    assert complex_operation.mutation_remove_edge()
    assert len(complex_operation.input_vertex.out_bound_edges) == 1
Esempio n. 11
0
def test_remove_node_success(basic_graph_no_v12, mocker):
    complex_operation, vertex1, vertex2, vertex3, vertex4 = basic_graph_no_v12

    vertex = Vertex()
    edge1 = IdentityOperation()
    edge2 = IdentityOperation()

    vertex1.out_bound_edges.append(edge1)
    edge1.end_vertex = vertex
    vertex.out_bound_edges.append(edge2)
    edge2.end_vertex = vertex2

    complex_operation.sort_vertices()

    mocker.patch('numpy.random.permutation', return_value=[vertex, vertex2])

    assert vertex in complex_operation.vertices_topo_order

    assert complex_operation.mutation_remove_vertex()

    assert vertex2 in complex_operation.vertices_topo_order
    assert vertex not in complex_operation.vertices_topo_order

    assert len(vertex1.out_bound_edges) == 1
Esempio n. 12
0
    def build_graph(self) -> None:
        conv_edge1 = MutableEdge((BatchNorm(), PointConv2D(
            (20, 40)), DepthwiseConv2D(), IdentityOperation(),
                                  SeparableConv2D(
                                      (20, 40)), Dropout(0.25), ReLU()),
                                 max_vertices=10,
                                 initialize_with_identity=False)
        conv_edge2 = conv_edge1.deep_copy()
        vertex1 = Vertex(name='V1')
        vertex2 = Vertex(name='V2')
        self.input_vertex.add_edge(conv_edge1, vertex1)
        vertex7 = Vertex(name='V7')
        vertex1.add_edge(MaxPool2D(), vertex7)
        vertex7.add_edge(conv_edge2, vertex2)

        vertex3 = Vertex(name='V3')
        vertex2.add_edge(Flatten(), vertex3)
        vertex4 = Vertex(name='V4')
        vertex3.add_edge(Dense(512), vertex4)
        vertex5 = Vertex(name='V5')
        vertex4.add_edge(ReLU(), vertex5)
        vertex6 = Vertex(name='V6')

        vertex5.add_edge(Dropout(0.5), vertex6)
        vertex6.add_edge(Dense(num_classes), self.output_vertex)
Esempio n. 13
0
class ComplexEdge(Edge):
    """
    Complex operation class. This operation encapsulates a small graph of
    nodes and operations. The graph follows such invariants:
    1. The graph has no circle
    2. Output is always reachable from input (implied from 3)
    3. All the vertices should be reachable from input
    4. All the vertices could reach output

    Class level invariants:
    1. input_vertex is not None
    2. output_vertex is not None
    3. vertices_topo_order always contains vertices sorted in topological order
    4. Each edge's end_vertex should point to the the end vertex of this
    edge, when the edge is in the graph
    """
    def __init__(self, name: str) -> None:
        super().__init__()
        self.input_vertex = Vertex(name='input')
        self.output_vertex = Vertex(name='output')

        self.vertices_topo_order: List[Vertex] = [
            self.output_vertex, self.input_vertex
        ]
        self.name = name
        self._layers_below = -1

    def deep_copy_graph(self, copy: 'ComplexEdge') -> None:
        """
        Deep copy the graph to another complex edge
        Assuming the invariants holds

        Args:
            copy: To which the graph should be copied to
        Returns:
            None
        """
        for _ in range(len(self.vertices_topo_order) - 2):
            copy.vertices_topo_order.append(Vertex())

        # Clear existing edges
        copy.input_vertex.out_bound_edges.clear()
        # Copy edges
        for i, vertex in enumerate(self.vertices_topo_order):
            copy_vertex = copy.vertices_topo_order[i]
            for edge in vertex.out_bound_edges:
                copy_edge = edge.deep_copy()
                copy_vertex.out_bound_edges.append(copy_edge)
                # Check here to make mypy happy
                if edge.end_vertex:
                    copy_edge.end_vertex = copy.vertices_topo_order[
                        edge.end_vertex.order]

        copy.sort_vertices()

    def deep_copy_info(self, copy: 'ComplexEdge') -> None:
        copy.name = self.name
        copy._layers_below = self._layers_below

    @abstractmethod
    def deep_copy(self) -> Edge:
        pass

    def _topo_sort_recursion(self, current: Vertex, vertex_list: List[Vertex],
                             accessing_set: Set[int],
                             finished_status: Dict[int, bool]) -> bool:
        """

        Args:
            current:
            vertex_list:
            accessing_set:
            finished_status:

        Returns:

        """
        current_ref = id(current)
        if current_ref in accessing_set:
            raise RuntimeError('Found cycle in graph')
        if current_ref in finished_status:
            return finished_status[current_ref]
        accessing_set.add(current_ref)
        to_remove: List[Edge] = []
        for out_edge in current.out_bound_edges:
            if out_edge.end_vertex:
                # If can't reach output, the vertex will be removed, as well
                # as the edge to it.
                if not self._topo_sort_recursion(out_edge.end_vertex,
                                                 vertex_list, accessing_set,
                                                 finished_status):
                    to_remove.append(out_edge)
        can_reach_output = (current is self.output_vertex
                            or len(to_remove) != len(current.out_bound_edges))
        finished_status[current_ref] = can_reach_output
        accessing_set.remove(current_ref)

        for edge in to_remove:
            current.out_bound_edges.remove(edge)
            edge.end_vertex = None

        if can_reach_output:
            vertex_list.append(current)
        return can_reach_output

    def sort_vertices(self) -> None:
        """
        Sort the vertices in topological order. Maintains the invariant that
        vertices_topo_order contains vertices sorted in topological order.

        Returns:
            None
        """
        vertex_list: List[Vertex] = []
        accessing_set: Set[int] = set()
        finished_status: Dict[int, bool] = dict()
        self._topo_sort_recursion(self.input_vertex, vertex_list,
                                  accessing_set, finished_status)
        self.vertices_topo_order = vertex_list
        for order, vertex in enumerate(vertex_list):
            vertex.order = order

    def check_output_reachable(self) -> bool:
        """
        Checks for the invariant "All the vertices should be reachable from
        input". Assumes there's no circle in the graph.

        Returns:
            True if output is reachable, False otherwise.
        """
        # Standard BFS
        visited_set: Set[Vertex] = set()
        queue: Queue = Queue()
        visited_set.add(self.input_vertex)
        queue.put(self.input_vertex)
        while not queue.empty():
            current = queue.get()
            for out_edge in current.out_bound_edges:
                if out_edge.end_vertex in visited_set:
                    continue
                new_vertex = out_edge.end_vertex
                queue.put(new_vertex)
                visited_set.add(new_vertex)
                if new_vertex == self.output_vertex:
                    return True
        return False

    @abstractmethod
    def mutate(self) -> bool:
        pass

    def invalidate_layer_count(self) -> None:
        self._layers_below = -1

        # Invalidate everything below
        for vertex in self.vertices_topo_order:
            for edge in vertex.out_bound_edges:
                edge.invalidate_layer_count()

    def build(self, x: tf.Tensor) -> tf.Tensor:
        for vertex in self.vertices_topo_order:
            vertex.reset()
        with tf.name_scope('%s.layer_%d' % (self.name, self.level)):
            self.input_vertex.collect(x)
            for vertex in reversed(self.vertices_topo_order[1:]):
                vertex.submit()
            return self.output_vertex.aggregate()

    @property
    def level(self) -> int:
        if self._layers_below < 1:
            max_layers = 1
            for vertex in self.vertices_topo_order:
                for operation in vertex.out_bound_edges:
                    max_layers = max(max_layers, operation.level)
            self._layers_below = max_layers + 1
        return self._layers_below
Esempio n. 14
0
 def build_graph(self) -> None:
     vertex = Vertex()
     edge1 = MaxPool2D()
     edge2 = IdentityOperation()
     self.input_vertex.add_edge(edge1, vertex)
     vertex.add_edge(edge2, self.output_vertex)