summaryrefslogtreecommitdiff
path: root/runtimes/neurun/core/src/util/ShapeInference.cc
diff options
context:
space:
mode:
Diffstat (limited to 'runtimes/neurun/core/src/util/ShapeInference.cc')
-rw-r--r--runtimes/neurun/core/src/util/ShapeInference.cc202
1 files changed, 202 insertions, 0 deletions
diff --git a/runtimes/neurun/core/src/util/ShapeInference.cc b/runtimes/neurun/core/src/util/ShapeInference.cc
new file mode 100644
index 000000000..5a7bfde41
--- /dev/null
+++ b/runtimes/neurun/core/src/util/ShapeInference.cc
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "util/Utils.h"
+#include "model/InternalType.h"
+#include "model/Shape.h"
+#include "model/operation/AvgPool2DNode.h"
+#include "model/operation/MaxPool2DNode.h"
+#include "util/ShapeInference.h"
+
+namespace neurun
+{
+namespace shape_inference
+{
+
+//
+// Helper functions
+//
+
+namespace
+{
+
+template <typename T, typename U>
+typename std::enable_if<std::is_integral<T>::value && std::is_integral<U>::value,
+ typename std::common_type<T, U>::type>::type
+ceil_div(T dividend, U divisor)
+{
+ assert(dividend > 0 && divisor > 0 && "this implementations is for positive numbers only");
+ return (dividend + divisor - 1) / divisor;
+}
+
+// Calculate the result of broadcast of two shapes
+model::Shape broadcastShapes(const model::Shape &lhs_shape, const model::Shape &rhs_shape)
+{
+ model::Shape out_shape;
+ auto max_rank = std::max(lhs_shape.rank(), rhs_shape.rank());
+
+ for (int idx = 0; idx < max_rank; ++idx)
+ {
+ // Go over operands dimensions from right to left
+ int lhs_idx = lhs_shape.rank() - idx - 1;
+ int rhs_idx = rhs_shape.rank() - idx - 1;
+
+ int32_t lhs_dim = lhs_idx >= 0 ? lhs_shape.dim(lhs_idx) : 1;
+ int32_t rhs_dim = rhs_idx >= 0 ? rhs_shape.dim(rhs_idx) : 1;
+
+ if (lhs_dim != 1 && rhs_dim != 1 && lhs_dim != rhs_dim)
+ throw std::runtime_error("Incompatible shapes for broadcast");
+
+ out_shape.prepend(std::max(lhs_dim, rhs_dim));
+ }
+
+ return out_shape;
+}
+
+// Calculate output height and width of convolution-like operation
+std::pair<int, int> calcConvLikeHeightAndWidth(const int in_h, const int in_w, const int ker_h,
+ const int ker_w, const model::Padding pad,
+ const model::Stride stride)
+{
+ int32_t out_h = 0, out_w = 0;
+
+ switch (pad.type)
+ {
+ case model::PaddingType::SAME:
+ out_h = ceil_div(in_h, stride.vertical);
+ out_w = ceil_div(in_w, stride.horizontal);
+ break;
+ case model::PaddingType::VALID:
+ out_h = ceil_div(in_h - ker_h + 1, stride.vertical);
+ out_w = ceil_div(in_w - ker_w + 1, stride.horizontal);
+ break;
+ case model::PaddingType::EXPLICIT:
+ out_h = (in_h + pad.param.top + pad.param.bottom - ker_h) / stride.vertical + 1;
+ out_w = (in_w + pad.param.left + pad.param.right - ker_w) / stride.horizontal + 1;
+ break;
+ default:
+ assert(false);
+ }
+
+ return {out_h, out_w};
+}
+
+} // namespace
+
+//
+// Shape inference
+//
+
+Shapes inferEltwiseShape(const model::Shape &lhs_shape, const model::Shape &rhs_shape)
+{
+ return {broadcastShapes(lhs_shape, rhs_shape)};
+}
+
+Shapes inferAvgPoolShape(const model::Shape &in_shape,
+ const model::operation::AvgPool2DNode::Param &param,
+ const model::Layout layout)
+{
+ assert(layout == model::Layout::NHWC);
+ auto ifm_shape = in_shape.asFeature(layout);
+ const auto out_h_w = calcConvLikeHeightAndWidth(ifm_shape.H, ifm_shape.W, param.kh, param.kw,
+ param.padding, param.stride);
+ // Pooling don't change number of channels and batch size
+ return {model::Shape{ifm_shape.N, out_h_w.first, out_h_w.second, ifm_shape.C}};
+}
+
+Shapes inferConcatShape(const Shapes &in_shapes, const model::operation::ConcatNode::Param &param)
+{
+ const int32_t concat_axis = param.axis;
+ const auto &first_in_shape = in_shapes[0];
+
+ // Check that all shapes are equal except for concat axis dimension
+ for (const auto &in_shape : in_shapes)
+ {
+ assert(in_shape.rank() == first_in_shape.rank());
+ for (int64_t dim_idx = 0; dim_idx < in_shape.rank(); ++dim_idx)
+ assert(dim_idx == concat_axis || in_shape.dim(dim_idx) == first_in_shape.dim(dim_idx));
+ }
+
+ // Calculate output shape
+ model::Shape out_shape(first_in_shape);
+ out_shape.dim(concat_axis) = 0;
+ for (const auto &in_shape : in_shapes)
+ out_shape.dim(concat_axis) += in_shape.dim(concat_axis);
+ return {out_shape};
+}
+
+Shapes inferMaxPoolShape(const model::Shape &in_shape,
+ const model::operation::MaxPool2DNode::Param &param,
+ const model::Layout layout)
+{
+ assert(layout == model::Layout::NHWC);
+ auto ifm_shape = in_shape.asFeature(layout);
+ const auto out_h_w = calcConvLikeHeightAndWidth(ifm_shape.H, ifm_shape.W, param.kh, param.kw,
+ param.padding, param.stride);
+ // Pooling don't change number of channels and batch size
+ return {model::Shape{ifm_shape.N, out_h_w.first, out_h_w.second, ifm_shape.C}};
+}
+
+Shapes inferConv2DShape(const model::Shape &in_shape, const model::Shape &ker_shape,
+ const model::operation::Conv2DNode::Param &param, model::Layout layout)
+{
+ assert(layout == model::Layout::NHWC);
+ auto ifm_shape = in_shape.asFeature(layout);
+
+ // Kernel format is [depth_out, kernel_height, kernel_width, depth_in]
+ auto kf_shape = ker_shape.asFeature(layout);
+ assert(ifm_shape.C == kf_shape.C);
+
+ const auto out_h_w = calcConvLikeHeightAndWidth(ifm_shape.H, ifm_shape.W, kf_shape.H, kf_shape.W,
+ param.padding, param.stride);
+
+ return {model::Shape{ifm_shape.N, out_h_w.first, out_h_w.second, kf_shape.N}};
+}
+
+Shapes inferDepthwiseConv2DShape(const model::Shape &in_shape, const model::Shape &ker_shape,
+ const model::operation::DepthwiseConv2DNode::Param &param,
+ model::Layout layout)
+{
+ assert(layout == model::Layout::NHWC);
+ auto ifm_shape = in_shape.asFeature(layout);
+
+ // Kernel format is [1, kernel_height, kernel_width, depth_out]
+ auto kf_shape = ker_shape.asFeature(layout);
+ assert(kf_shape.C == static_cast<int32_t>(ifm_shape.C * param.multiplier));
+ assert(kf_shape.N == 1);
+
+ const auto out_h_w = calcConvLikeHeightAndWidth(ifm_shape.H, ifm_shape.W, kf_shape.H, kf_shape.W,
+ param.padding, param.stride);
+
+ return {model::Shape{ifm_shape.N, out_h_w.first, out_h_w.second, kf_shape.C}};
+}
+
+Shapes inferFullyConnectedShape(const model::Shape &in_shape, const model::Shape &ker_shape)
+{
+ assert(in_shape.rank() >= 2);
+ assert(ker_shape.rank() == 2);
+
+ const auto input_size_with_batch = in_shape.num_elements();
+ const auto num_units = ker_shape.dim(0);
+ const auto input_size = ker_shape.dim(1);
+ const auto batch_size = input_size_with_batch / input_size;
+ assert(input_size_with_batch % input_size == 0);
+
+ return {{model::Shape({static_cast<int32_t>(batch_size), num_units})}};
+}
+
+} // namespace shape_inference
+} // namespace neurun