2 Copyright (c) 2018 Intel Corporation
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
20 from mo.front.common.partial_infer.transpose import transpose_infer
21 from mo.front.extractor import attr_getter
22 from mo.ops.op import Op
26 ''' Pad operation that explicitly extends an input tensor at edges.
28 This operation frequently appears in TF and rarely in ONNX models
29 followed by some windowed operation like convolution or pooling.
30 The operation extends each (not only spatial) dimensions of input
31 tensors by new elements increasing output shape. The filling values
32 is defined by 'mode' and 'fill_value' attributes, but usually it is zero
35 The operation has two forms: with one or two input arguments.
36 The first aruments is an input tensor to be padded. The second
37 argument is an optional padding values of shape Nx2, where N is
38 a number of dimensions in an input tensor:
40 [[pad_begin_dim1, pad_end_dim1],
41 [pad_begin_dim2, pad_end_dim2],
43 [pad_begin_dimN, pad_end_dimN]]
45 where pad_begin_dim1 etc. are padding margins in elements. If the second
46 input argument is omitted, then it is in 'pads' attribute in the same
53 def __init__(self, graph: nx.MultiDiGraph, attrs: dict):
54 super().__init__(graph, {
55 # no 'type' as this operation is not directly supported by IE
57 'infer': __class__.infer,
59 'fill_value': float(0),
63 def supported_attrs(self):
64 return ['mode', 'fill_value', 'pads']
66 def backend_attrs(self):
67 # it shouldn't be translated to IE layer
72 if node.has_valid('pads'):
73 assert len(node.in_nodes()) == 1, "Pad operation has pads attribute and unexpected additional input argument for node {}.".format(node.name)
76 assert len(node.in_nodes()) == 2, "Missing required second input argument for node {} and pads attribute is missing.".format(node.name)
77 padding = node.in_node(1).value
79 input_shape = node.in_node(0).shape
80 if padding is None or input_shape is None:
83 # paddings can be defined, partially defined or undefined
84 # TODO for now we only handle fully defined paddings
85 # That means that intermediate tensor that delivers padding
86 # should have defined value and size Nx2
87 # TODO possible broadcasts are not supported
88 assert (padding.ndim == 2 and padding.shape[1] == 2)
90 # make sure that input has the same number of dimensions as the number of padding dimensions
91 assert (padding.shape[0] == len(input_shape)), \
92 "Input tensor shape {} and pads values {} do not match for Pad node {}".format(
93 input_shape, padding.shape, node.name
96 # sum low and high padding values to calculate the shape modification vector
97 shape_change = np.add.reduce(padding, 1)
98 assert (shape_change.shape == input_shape.shape)
100 # preserve non-positive values in the input shape, because it has a special meaning
102 [shape_change[i] + input_shape[i] if input_shape[i] > 0 else input_shape[i] for i in range(len(input_shape))])
104 assert len(node.out_nodes()) == 1
106 node.out_node().shape = shape