From 72566cde0dad51f60d168cb4780932f8382bd528 Mon Sep 17 00:00:00 2001 From: Pawel Raasz Date: Mon, 13 Mar 2023 13:48:44 +0100 Subject: [PATCH] Review pooling classes for shape inference aspects (#16114) * Review adaptive avg pool shape inference * Review adaptive max pool shape inference * Review AvgPool and MaxPool * Minor improvement for StaticShape * Update ShapeInferBaseWithPadding's infer to be compatible with interface after rebase * Fix build issues * Set default pads before checks * Fix include openvino headers --- .../include/openvino/op/adaptive_max_pool.hpp | 1 + .../openvino/op/util/max_pool_base.hpp | 10 +- .../adaptive_avg_pool_shape_inference.hpp | 31 ++ .../adaptive_max_pool_shape_inference.hpp | 31 ++ .../include/avg_pool_shape_inference.hpp | 59 ++++ .../include/dimension_util.hpp | 83 ++++++ .../include/max_pool_shape_inference.hpp | 56 ++++ .../include/pooling_shape_inference_util.hpp | 264 ++++++++++++++++++ src/core/src/op/adaptive_avg_pool.cpp | 39 +-- src/core/src/op/adaptive_max_pool.cpp | 46 +-- src/core/src/op/avg_pool.cpp | 87 +----- src/core/src/op/max_pool.cpp | 51 +--- src/core/src/op/util/max_pool_base.cpp | 111 +------- src/core/src/pass/serialize.cpp | 2 +- .../tests/type_prop/adaptive_avg_pool.cpp | 165 +++++++---- .../tests/type_prop/adaptive_max_pool.cpp | 225 ++++++++++----- src/core/tests/type_prop/avg_pool.cpp | 131 ++++++--- src/core/tests/type_prop/max_pool.cpp | 165 ++++++++--- .../utils/shape_inference/shape_inference.cpp | 55 +++- .../shape_inference/static_dimension.hpp | 8 +- ...adaptive_avg_pool_shape_inference_test.cpp | 79 ++++++ ...adaptive_max_pool_shape_inference_test.cpp | 79 ++++++ .../avg_pool_shape_inference_test.cpp | 127 +++++++++ .../max_pool_shape_inference_test.cpp | 152 ++++++++++ 24 files changed, 1545 insertions(+), 512 deletions(-) create mode 100644 src/core/shape_inference/include/adaptive_avg_pool_shape_inference.hpp create mode 100644 src/core/shape_inference/include/adaptive_max_pool_shape_inference.hpp create mode 100644 src/core/shape_inference/include/avg_pool_shape_inference.hpp create mode 100644 src/core/shape_inference/include/dimension_util.hpp create mode 100644 src/core/shape_inference/include/max_pool_shape_inference.hpp create mode 100644 src/core/shape_inference/include/pooling_shape_inference_util.hpp create mode 100644 src/plugins/intel_cpu/tests/unit/shape_inference_test/adaptive_avg_pool_shape_inference_test.cpp create mode 100644 src/plugins/intel_cpu/tests/unit/shape_inference_test/adaptive_max_pool_shape_inference_test.cpp create mode 100644 src/plugins/intel_cpu/tests/unit/shape_inference_test/avg_pool_shape_inference_test.cpp create mode 100644 src/plugins/intel_cpu/tests/unit/shape_inference_test/max_pool_shape_inference_test.cpp diff --git a/src/core/include/openvino/op/adaptive_max_pool.hpp b/src/core/include/openvino/op/adaptive_max_pool.hpp index d730e882b2f..e61d1ed053c 100644 --- a/src/core/include/openvino/op/adaptive_max_pool.hpp +++ b/src/core/include/openvino/op/adaptive_max_pool.hpp @@ -42,6 +42,7 @@ public: element::Type get_index_element_type() const { return m_index_element_type; } + void set_index_element_type(const element::Type& type); protected: ov::element::Type m_index_element_type = ov::element::i64; diff --git a/src/core/include/openvino/op/util/max_pool_base.hpp b/src/core/include/openvino/op/util/max_pool_base.hpp index 55625a28b59..4e8d9937ea5 100644 --- a/src/core/include/openvino/op/util/max_pool_base.hpp +++ b/src/core/include/openvino/op/util/max_pool_base.hpp @@ -58,9 +58,12 @@ public: const Shape& get_pads_end() const { return m_pads_end; } + OPENVINO_DEPRECATED("This method is deprecated and will be removed soon. Please use set_pads_end instead.") void set_adding_above(const Shape& pads_end) { m_pads_end = pads_end; } + void set_pads_end(Shape pads_end); + /// \return The pad type for pooling. PadType get_auto_pad() const { return m_auto_pad; @@ -77,13 +80,6 @@ public: } protected: - bool update_auto_padding(const PartialShape& in_shape, - const Strides& filter_dilations, - Shape& new_pads_end, - Shape& new_pads_begin) const; - - PartialShape infer_output_shape(const Strides& dilations); - Shape m_kernel; Strides m_strides; Shape m_pads_begin; diff --git a/src/core/shape_inference/include/adaptive_avg_pool_shape_inference.hpp b/src/core/shape_inference/include/adaptive_avg_pool_shape_inference.hpp new file mode 100644 index 00000000000..50ebf376144 --- /dev/null +++ b/src/core/shape_inference/include/adaptive_avg_pool_shape_inference.hpp @@ -0,0 +1,31 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "openvino/op/adaptive_avg_pool.hpp" +#include "pooling_shape_inference_util.hpp" +#include "utils.hpp" + +namespace ov { +namespace op { +namespace v8 { + +template +std::vector shape_infer(const AdaptiveAvgPool* op, + const std::vector& input_shapes, + const std::map& constant_data = {}) { + return {pooling::out_shape_infer(op, input_shapes, constant_data)}; +} + +template +void shape_infer(const AdaptiveAvgPool* op, + const std::vector& input_shapes, + std::vector& output_shapes, + const std::map& constant_data = {}) { + output_shapes = shape_infer(op, input_shapes, constant_data); +} +} // namespace v8 +} // namespace op +} // namespace ov diff --git a/src/core/shape_inference/include/adaptive_max_pool_shape_inference.hpp b/src/core/shape_inference/include/adaptive_max_pool_shape_inference.hpp new file mode 100644 index 00000000000..4e111d897a2 --- /dev/null +++ b/src/core/shape_inference/include/adaptive_max_pool_shape_inference.hpp @@ -0,0 +1,31 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "openvino/op/adaptive_max_pool.hpp" +#include "pooling_shape_inference_util.hpp" +#include "utils.hpp" + +namespace ov { +namespace op { +namespace v8 { + +template +std::vector shape_infer(const AdaptiveMaxPool* op, + const std::vector& input_shapes, + const std::map& constant_data = {}) { + return {2, pooling::out_shape_infer(op, input_shapes, constant_data)}; +} + +template +void shape_infer(const AdaptiveMaxPool* op, + const std::vector& input_shapes, + std::vector& output_shapes, + const std::map& constant_data = {}) { + output_shapes = shape_infer(op, input_shapes, constant_data); +} +} // namespace v8 +} // namespace op +} // namespace ov diff --git a/src/core/shape_inference/include/avg_pool_shape_inference.hpp b/src/core/shape_inference/include/avg_pool_shape_inference.hpp new file mode 100644 index 00000000000..577d014a6c9 --- /dev/null +++ b/src/core/shape_inference/include/avg_pool_shape_inference.hpp @@ -0,0 +1,59 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "dimension_util.hpp" +#include "max_pool_shape_inference.hpp" +#include "openvino/op/max_pool.hpp" +#include "utils.hpp" + +namespace ov { +namespace op { + +namespace pooling { + +template <> +inline void valid_dilated_kernel_with_padding(const v1::AvgPool* op, + const size_t kernel, + const size_t pad_begin, + const size_t pad_end, + const size_t axis) { + NODE_VALIDATION_CHECK(op, + !op->get_exclude_pad() || ((kernel > pad_begin) && (kernel > pad_end)), + "Kernel after dilation is sometimes entirely in the padding area for axis ", + axis, + " (dilated kernel dimension: ", + kernel, + ", padding below dimension: ", + pad_begin, + ", padding above dimension: ", + pad_end, + ") and this is not ", + "allowed."); +} +} // namespace pooling + +namespace v1 { + +template +std::vector shape_infer(const AvgPool* op, const std::vector& input_shapes) { + NODE_VALIDATION_CHECK(op, input_shapes.size() == 1); + const auto& data_shape = input_shapes[0]; + + const auto dilations = Strides(op->get_kernel().size(), 1); + + pooling::update_and_validate_attributes(const_cast(op), data_shape, dilations); + + auto output_shape = pooling::out_shape_infer(op, data_shape, dilations); + return {output_shape}; +} + +template +void shape_infer(const AvgPool* op, const std::vector& input_shapes, std::vector& output_shapes) { + output_shapes = shape_infer(op, input_shapes); +} +} // namespace v1 +} // namespace op +} // namespace ov diff --git a/src/core/shape_inference/include/dimension_util.hpp b/src/core/shape_inference/include/dimension_util.hpp new file mode 100644 index 00000000000..b3ffb07b7ec --- /dev/null +++ b/src/core/shape_inference/include/dimension_util.hpp @@ -0,0 +1,83 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// +#pragma once + +#include + +#include "openvino/util/common_util.hpp" + +namespace ov { +namespace util { +namespace dim { + +constexpr auto inf_bound = -1; //!< Infinite bound value for dimension. + +/** + * @brief Calculate dilated dimension value. + * + * @param dim Dimension size value. + * @param dilation Dilation value + * @return Dilated dimension value. + */ +template +constexpr auto dilated(const T dim, const T dilation) -> T { + return (dim < 1) ? inf_bound : dilation * (dim - 1) + 1; +} + +/** + * @brief Calculate padded dimension size as dim size + padding size + * + * @param dim Dimension size value. + * @param pad_num Number of padded dimension. + * @return Padded dimension value or infinite bound. + */ +constexpr auto padded(const int64_t dim, const int64_t pad_num) -> int64_t { + return ((dim == inf_bound) || (dim + pad_num < 0)) ? inf_bound : dim + pad_num; +} + +/** + * @brief Divide dimension using ceil rounding. + * + * @tparam TDim Dimension type. + * @tparam T Dimension length value type. + * + * @param dim Input dimension. + * @param divisor Dimension division. + * @return Divided dimension with bounds round up. + */ +template +auto ceil_div(const TDim& dim, const T divisor) -> TDim { + if (dim.is_static()) { + return {util::ceil_div(dim.get_length(), divisor)}; + } else if (dim.get_max_length() == static_cast(dim::inf_bound)) { + return {dim}; + } else { + return {util::ceil_div(dim.get_min_length(), divisor), util::ceil_div(dim.get_max_length(), divisor)}; + } +} + +/** + * @brief Divide dimension using floor rounding. + * + * @tparam TDim Dimension type. + * @tparam T Dimension length value type. + * + * @param dim Input dimension. + * @param divisor Dimension division. + * @return Divided dimension with bound round down. + */ +template +auto floor_div(const TDim& dim, const T divisor) -> TDim { + if (dim.is_static()) { + return {dim.get_length() / divisor}; + } else if (dim.get_max_length() == static_cast(dim::inf_bound)) { + return {dim}; + } else { + return {dim.get_min_length() / divisor, dim.get_max_length() / divisor}; + } +} + +} // namespace dim +} // namespace util +} // namespace ov diff --git a/src/core/shape_inference/include/max_pool_shape_inference.hpp b/src/core/shape_inference/include/max_pool_shape_inference.hpp new file mode 100644 index 00000000000..74dbd4962ae --- /dev/null +++ b/src/core/shape_inference/include/max_pool_shape_inference.hpp @@ -0,0 +1,56 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "dimension_util.hpp" +#include "pooling_shape_inference_util.hpp" +#include "utils.hpp" + +namespace ov { +namespace op { + +namespace v1 { +template +std::vector shape_infer(const MaxPool* op, const std::vector& input_shapes) { + NODE_VALIDATION_CHECK(op, input_shapes.size() == 1); + const auto& data_shape = input_shapes[0]; + + const auto dilations = Strides(op->get_kernel().size(), 1); + + pooling::update_and_validate_attributes(const_cast(op), data_shape, dilations); + + return {pooling::out_shape_infer(op, data_shape, dilations)}; +} + +template +void shape_infer(const MaxPool* op, const std::vector& input_shapes, std::vector& output_shapes) { + output_shapes = shape_infer(op, input_shapes); +} +} // namespace v1 + +namespace v8 { +template +std::vector shape_infer(const MaxPool* op, const std::vector& input_shapes) { + NODE_VALIDATION_CHECK(op, input_shapes.size() == 1); + const auto& data_shape = input_shapes[0]; + + auto dilations = op->get_dilations(); + if (dilations.empty()) { + dilations.resize(op->get_kernel().size(), 1); + } + + pooling::update_and_validate_attributes(const_cast(op), data_shape, dilations); + + auto output_shape = pooling::out_shape_infer(op, data_shape, dilations); + return {2, output_shape}; +} + +template +void shape_infer(const MaxPool* op, const std::vector& input_shapes, std::vector& output_shapes) { + output_shapes = shape_infer(op, input_shapes); +} +} // namespace v8 +} // namespace op +} // namespace ov diff --git a/src/core/shape_inference/include/pooling_shape_inference_util.hpp b/src/core/shape_inference/include/pooling_shape_inference_util.hpp new file mode 100644 index 00000000000..8832f546f47 --- /dev/null +++ b/src/core/shape_inference/include/pooling_shape_inference_util.hpp @@ -0,0 +1,264 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "dimension_util.hpp" +#include "utils.hpp" + +namespace ov { +namespace op { +namespace pooling { +constexpr size_t spatial_dim_offset = 2; + +/** + * @brief Calculate dimension padding required by filter/kernel properties. + * + * Provides pair of padding values as left padding is total value of required padding divided by 2 and right as + * total required padding minus left padding. + * + * @param dim input dimension to calculate its padding. + * @param filter_size Kernel size for input dimension. + * @param dilation Kernel dilation. + * @param stride Kernel stride. + * @return Pair of left, right padding values for input dimension. + */ +template +inline std::pair dim_padding(const TDim& dim, const int64_t kernel_size, const int64_t dilation, int64_t stride) { + if (dim.is_static()) { + const auto dim_size = static_cast(dim.get_length()); + const auto dilated_kernel = ov::util::dim::dilated(kernel_size, dilation); + const int64_t tmp = (dim_size + stride - 1) / stride; + + const auto padding = std::max(0, (tmp - 1) * stride + dilated_kernel - dim_size); + const auto left_padding = padding / 2; + return {left_padding, padding - left_padding}; + } else { + // If input dimension is infinite or interval the padding will be set to 0 + // as operator cannot store paddings for both bounds. + return {0, 0}; + } +} + +template +void update_and_validate_attributes(TOp* op, const TShape& data_shape, const Strides& dilations) { + const auto& data_rank = data_shape.rank(); + + NODE_VALIDATION_CHECK(op, + is_rank_compatible_any_of(data_rank, {3, 4, 5}), + "Expected a 3D, 4D or 5D tensor for the input. Got: ", + data_shape); + + const auto& kernel = op->get_kernel(); + const auto& auto_pad = op->get_auto_pad(); + const auto num_spatial = kernel.size(); + const auto& strides = op->get_strides(); + + if (auto_pad == PadType::VALID || op->get_pads_begin().empty()) { + op->set_pads_begin(Shape(num_spatial, 0)); + } + if (auto_pad == PadType::VALID || op->get_pads_end().empty()) { + op->set_pads_end(Shape(num_spatial, 0)); + } + + NODE_VALIDATION_CHECK(op, + op->get_pads_begin().size() == num_spatial, + "Expected pads_begin size to be equal to input size - 2. Got: ", + op->get_pads_begin().size()); + NODE_VALIDATION_CHECK(op, + op->get_pads_end().size() == num_spatial, + "Expected pads_end size to be equal to input size - 2. Got: ", + op->get_pads_end().size()); + NODE_VALIDATION_CHECK(op, + strides.size() == num_spatial, + "Expected strides size to be equal to input size - 2. Got: ", + strides.size()); + NODE_VALIDATION_CHECK(op, + dilations.size() == num_spatial, + "Expected dilations size to be equal to kernel size. Got: ", + dilations.size()); + + if (data_rank.is_static()) { + NODE_VALIDATION_CHECK(op, + num_spatial == (data_shape.size() - spatial_dim_offset), + "Expected kernel size to be equal to input size - 2. Got: ", + num_spatial); + + if (auto_pad == PadType::SAME_UPPER || auto_pad == PadType::SAME_LOWER) { + Shape pads_begin, pads_end; + pads_begin.reserve(num_spatial); + pads_end.reserve(num_spatial); + + auto data_dim = data_shape.cbegin() + spatial_dim_offset; + auto pad_begin_ins = std::back_inserter(pads_begin); + auto pad_end_ins = std::back_inserter(pads_end); + auto& pad_left = auto_pad == PadType::SAME_UPPER ? pad_begin_ins : pad_end_ins; + auto& pad_right = auto_pad == PadType::SAME_UPPER ? pad_end_ins : pad_begin_ins; + + for (size_t i = 0; i < num_spatial; ++i, ++pad_left, ++pad_right, ++data_dim) { + std::tie(*pad_left, *pad_right) = dim_padding(*data_dim, kernel[i], dilations[i], strides[i]); + } + + op->set_pads_begin(pads_begin); + op->set_pads_end(std::move(pads_end)); + } + } + + constexpr auto is_zero = cmp::Equal(0); + NODE_VALIDATION_CHECK(op, + std::none_of(strides.cbegin(), strides.cend(), is_zero), + "Strides has zero dimension(s). ", + strides); + NODE_VALIDATION_CHECK(op, + std::none_of(dilations.cbegin(), dilations.cend(), is_zero), + "Kernel dilations has zero dimension(s). ", + dilations); +} + +template +void valid_dilated_kernel_with_dim(const TOp* op, const size_t kernel, const TDim& dim, const size_t axis) { + NODE_VALIDATION_CHECK(op, + kernel > 0, + "Kernel after dilation has dimension less than 1 (dim: ", + kernel, + ") at axis ", + axis, + "."); + + NODE_VALIDATION_CHECK(op, + cmp::le(kernel, dim.get_length()), + "Kernel after dilation has size (dim: ", + kernel, + ") larger than the data shape after padding (dim: ", + dim, + ") at axis ", + axis, + "."); +} + +template +void valid_dilated_kernel_with_padding(const TOp* op, + const size_t kernel, + const size_t pad_begin, + const size_t pad_end, + const size_t axis) {} + +template +TShape spatial_shape_infer(const TOp* op, const TShape& data_shape, const Strides& dilations) { + using namespace ov::util; + const auto spatial_num = data_shape.size() - spatial_dim_offset; + const auto is_ceil_mode = op->get_rounding_type() == RoundingType::CEIL; + const auto is_auto_pad = (op->get_auto_pad() == PadType::SAME_UPPER) || (op->get_auto_pad() == PadType::SAME_LOWER); + + using TDim = typename TShape::value_type; + const auto& dim_divide = is_ceil_mode ? dim::ceil_div : dim::floor_div; + + TShape out_shape; + out_shape.reserve(spatial_num); + + auto data_dim = data_shape.cbegin() + spatial_dim_offset; + const auto& pads_begin = op->get_pads_begin(); + const auto& pads_end = op->get_pads_end(); + const auto& kernel = op->get_kernel(); + const auto& stride = op->get_strides(); + + for (size_t i = 0; i < spatial_num; ++i, ++data_dim) { + if (data_dim->is_static() || !is_auto_pad) { + auto dim = *data_dim + (pads_begin[i] + pads_end[i]); + const auto kernel_dilated = dim::dilated(kernel[i], dilations[i]); + + if (data_dim->is_static()) { + valid_dilated_kernel_with_dim(op, kernel_dilated, dim, i); + valid_dilated_kernel_with_padding(op, kernel_dilated, pads_begin[i], pads_end[i], i); + } + + dim = dim - kernel_dilated; + dim = dim_divide(dim, stride[i]); + dim += 1; + out_shape.push_back(std::move(dim)); + } else { + // If dimension is interval and is auto pad then result is dynamic shape as padding values are not correct. + // Operator cannot keep separate auto padding values for upper, lower bounds. + out_shape.emplace_back(dim::inf_bound); + } + } + + return out_shape; +} + +/** + * @brief Shape inference helper used for pooling operators such Max Pool, Avg Pool. + */ +template +TShape out_shape_infer(const TOp* op, const TShape& data_shape, const Strides& dilations) { + TShape out_shape; + if (data_shape.rank().is_static()) { + const auto& batch_size = data_shape[0]; + const auto& channel_count = data_shape[1]; + + NODE_VALIDATION_CHECK(op, batch_size.is_dynamic() || batch_size.get_length() > 0, "Batch size is zero."); + NODE_VALIDATION_CHECK(op, + channel_count.is_dynamic() || channel_count.get_length() > 0, + "Channel count is zero."); + + out_shape = spatial_shape_infer(op, data_shape, dilations); + out_shape.insert(out_shape.begin(), data_shape.begin(), data_shape.begin() + spatial_dim_offset); + } else { + out_shape.insert(out_shape.begin(), spatial_dim_offset + op->get_kernel().size(), Dimension::dynamic()); + } + + return out_shape; +} + +/** + * @brief Shape inference helper used for adaptive pooling operators. + */ +template ::value || + std::is_same::value>::type* = nullptr> +TShape out_shape_infer(const TOp* op, + const std::vector& input_shapes, + const std::map& constant_data = {}) { + NODE_VALIDATION_CHECK(op, input_shapes.size() == 2); + + const auto& data_shape = input_shapes[0]; + const auto& out_spatial_shape = input_shapes[1]; + + const auto& data_rank = data_shape.rank(); + + NODE_VALIDATION_CHECK(op, + is_rank_compatible_any_of(data_rank, {3, 4, 5}), + "Expected a 3D, 4D or 5D tensor for the input. Got: ", + data_shape); + + TShape output_shape; + if (data_rank.is_static()) { + auto num_of_spatial_dims = data_shape.size() - spatial_dim_offset; + + NODE_VALIDATION_CHECK( + op, + out_spatial_shape.rank().is_dynamic() || out_spatial_shape[0].compatible(num_of_spatial_dims), + "Output shape for spatial dimension not compatible with data shape."); + + output_shape.reserve(data_shape.size()); + std::copy_n(data_shape.begin(), spatial_dim_offset, std::back_inserter(output_shape)); + + if (const auto spatial_dims = get_input_const_data_as_shape(op, 1, constant_data)) { + NODE_VALIDATION_CHECK(op, + num_of_spatial_dims == spatial_dims->size(), + "Number of spatial dimensions is not compatible with input data rank"); + + output_shape.insert(output_shape.end(), spatial_dims->begin(), spatial_dims->end()); + } else { + output_shape.insert(output_shape.end(), num_of_spatial_dims, ov::util::dim::inf_bound); + } + } else { + output_shape = PartialShape::dynamic(); + } + return output_shape; +} +} // namespace pooling +} // namespace op +} // namespace ov diff --git a/src/core/src/op/adaptive_avg_pool.cpp b/src/core/src/op/adaptive_avg_pool.cpp index 70422422b02..775af472729 100644 --- a/src/core/src/op/adaptive_avg_pool.cpp +++ b/src/core/src/op/adaptive_avg_pool.cpp @@ -2,15 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "ngraph/op/adaptive_avg_pool.hpp" +#include "openvino/op/adaptive_avg_pool.hpp" +#include "adaptive_avg_pool_shape_inference.hpp" #include "itt.hpp" -#include "ngraph/attribute_visitor.hpp" -#include "ngraph/graph_util.hpp" -#include "ngraph/validation_util.hpp" using namespace std; -using namespace ngraph; + +namespace ov { op::v8::AdaptiveAvgPool::AdaptiveAvgPool(const Output& data, const Output& output_shape) : Op({data, output_shape}) { @@ -20,33 +19,7 @@ op::v8::AdaptiveAvgPool::AdaptiveAvgPool(const Output& data, const Output< void op::v8::AdaptiveAvgPool::validate_and_infer_types() { OV_OP_SCOPE(v8_AdaptiveAvgPool_validate_and_infer_types); - const ov::PartialShape& data_shape = get_input_partial_shape(0); - - NODE_VALIDATION_CHECK( - this, - data_shape.rank().compatible(3) || data_shape.rank().compatible(4) || data_shape.rank().compatible(5), - "Expected a 3D, 4D or 5D tensor for the input. Got: ", - data_shape); - - auto output_shape = ov::PartialShape::dynamic(data_shape.rank()); - if (data_shape.rank().is_static()) { - if (data_shape[0].is_static()) { - output_shape[0] = data_shape[0]; // batch size - } - if (data_shape[1].is_static()) { - output_shape[1] = data_shape[1]; // channel size - } - if (const auto& const_output_shape = get_constant_from_source(input_value(1))) { - auto output_spatial_shape = const_output_shape->cast_vector(); - NODE_VALIDATION_CHECK(this, - (size_t)data_shape.rank().get_length() == 2 + output_spatial_shape.size(), - "Output shape is not compatible with input data rank"); - int i = 2; - for (auto& dim : output_spatial_shape) { - output_shape[i++] = dim; - } - } - } + const auto output_shape = shape_infer(this, get_node_input_partial_shapes(*this)).front(); set_output_type(0, get_input_element_type(0), output_shape); } @@ -55,3 +28,5 @@ shared_ptr op::v8::AdaptiveAvgPool::clone_with_new_inputs(const OutputVect check_new_args_count(this, new_args); return make_shared(new_args.at(0), new_args.at(1)); } + +} // namespace ov diff --git a/src/core/src/op/adaptive_max_pool.cpp b/src/core/src/op/adaptive_max_pool.cpp index 6a90202ff6e..e3bced2b9a6 100644 --- a/src/core/src/op/adaptive_max_pool.cpp +++ b/src/core/src/op/adaptive_max_pool.cpp @@ -2,19 +2,17 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "ngraph/op/adaptive_max_pool.hpp" +#include "openvino/op/adaptive_max_pool.hpp" +#include "adaptive_max_pool_shape_inference.hpp" #include "itt.hpp" -#include "ngraph/attribute_visitor.hpp" -#include "ngraph/graph_util.hpp" -#include "ngraph/validation_util.hpp" using namespace std; -using namespace ngraph; +namespace ov { op::v8::AdaptiveMaxPool::AdaptiveMaxPool(const Output& data, const Output& output_shape, - const ngraph::element::Type& index_element_type) + const element::Type& index_element_type) : Op({data, output_shape}), m_index_element_type{index_element_type} { constructor_validate_and_infer_types(); @@ -33,35 +31,10 @@ void op::v8::AdaptiveMaxPool::validate_and_infer_types() { m_index_element_type == element::i64 || m_index_element_type == element::i32, "Index element type must be i32 or i64"); - const ov::PartialShape& data_shape = get_input_partial_shape(0); + const auto output_shapes = shape_infer(this, get_node_input_partial_shapes(*this)); - NODE_VALIDATION_CHECK( - this, - data_shape.rank().compatible(3) || data_shape.rank().compatible(4) || data_shape.rank().compatible(5), - "Expected a 3D, 4D or 5D tensor for the input. Got: ", - data_shape); - - auto output_shape = ov::PartialShape::dynamic(data_shape.rank()); - if (data_shape.rank().is_static()) { - if (data_shape[0].is_static()) { - output_shape[0] = data_shape[0]; // batch size - } - if (data_shape[1].is_static()) { - output_shape[1] = data_shape[1]; // channel size - } - if (const auto& const_output_shape = get_constant_from_source(input_value(1))) { - auto output_spatial_shape = const_output_shape->cast_vector(); - NODE_VALIDATION_CHECK(this, - (size_t)data_shape.rank().get_length() == 2 + output_spatial_shape.size(), - "Output shape is not compatible with input data rank"); - int i = 2; - for (auto& dim : output_spatial_shape) { - output_shape[i++] = dim; - } - } - } - set_output_type(0, get_input_element_type(0), output_shape); - set_output_type(1, m_index_element_type, output_shape); + set_output_type(0, get_input_element_type(0), output_shapes[0]); + set_output_type(1, m_index_element_type, output_shapes[1]); } shared_ptr op::v8::AdaptiveMaxPool::clone_with_new_inputs(const OutputVector& new_args) const { @@ -69,3 +42,8 @@ shared_ptr op::v8::AdaptiveMaxPool::clone_with_new_inputs(const OutputVect check_new_args_count(this, new_args); return make_shared(new_args.at(0), new_args.at(1), m_index_element_type); } + +void op::v8::AdaptiveMaxPool::set_index_element_type(const element::Type& type) { + m_index_element_type = type; +} +} // namespace ov diff --git a/src/core/src/op/avg_pool.cpp b/src/core/src/op/avg_pool.cpp index 8d89849ad31..8e63d173144 100644 --- a/src/core/src/op/avg_pool.cpp +++ b/src/core/src/op/avg_pool.cpp @@ -4,6 +4,7 @@ #include "ngraph/op/avg_pool.hpp" +#include "avg_pool_shape_inference.hpp" #include "itt.hpp" #include "ngraph/attribute_visitor.hpp" #include "ngraph/graph_util.hpp" @@ -45,91 +46,9 @@ bool ov::op::v1::AvgPool::visit_attributes(AttributeVisitor& visitor) { void ov::op::v1::AvgPool::validate_and_infer_types() { OV_OP_SCOPE(v1_AvgPool_validate_and_infer_types); - if (0 == m_strides.size()) { - m_strides = Strides(m_kernel.size(), 1); - } - if (0 == m_pads_begin.size()) { - m_pads_begin = Shape(m_kernel.size(), 0); - } - - if (0 == m_pads_end.size()) { - m_pads_end = Shape(m_kernel.size(), 0); - } - - const ov::PartialShape& arg_shape = get_input_partial_shape(0); - - NODE_VALIDATION_CHECK( - this, - arg_shape.rank().compatible(3) || arg_shape.rank().compatible(4) || arg_shape.rank().compatible(5), - "Expected a 3D, 4D or 5D tensor for the input. Got: ", - arg_shape); - - if (arg_shape.rank().is_static()) { - NODE_VALIDATION_CHECK(this, - static_cast(m_pads_end.size()) == arg_shape.rank().get_max_length() - 2, - "Expected pads_end size to be equal to input size - 2. Got: ", - m_pads_end.size()); - - NODE_VALIDATION_CHECK(this, - static_cast(m_pads_begin.size()) == arg_shape.rank().get_max_length() - 2, - "Expected pads_begin size to be equal to input size - 2. Got: ", - m_pads_begin.size()); - NODE_VALIDATION_CHECK(this, - static_cast(m_kernel.size()) == arg_shape.rank().get_max_length() - 2, - "Expected kernel size to be equal to input size - 2. Got: ", - m_kernel.size()); - NODE_VALIDATION_CHECK(this, - static_cast(m_strides.size()) == arg_shape.rank().get_max_length() - 2, - "Expected strides size to be equal to input size - 2. Got: ", - m_kernel.size()); - } - - auto output_shape = ov::PartialShape::dynamic(); - if (arg_shape.rank().is_static() && arg_shape.rank().get_max_length() > 0) { - output_shape = std::vector(arg_shape.rank().get_max_length(), Dimension::dynamic()); - if (arg_shape[0].is_static()) { - output_shape[0] = arg_shape[0]; // batch size - } - if (arg_shape[1].is_static()) { - output_shape[1] = arg_shape[1]; // channel size - } - } - bool update_auto_padding_succeed = true; - if (m_auto_pad == PadType::SAME_UPPER || m_auto_pad == PadType::SAME_LOWER) { - CoordinateDiff pads_end; - CoordinateDiff pads_begin; - update_auto_padding_succeed = ngraph::try_apply_auto_padding(arg_shape, - m_kernel, - m_strides, - Strides(m_kernel.size(), 1), // No dilation - m_auto_pad, - pads_end, - pads_begin); - m_pads_end = Shape(pads_end.begin(), pads_end.end()); - m_pads_begin = Shape(pads_begin.begin(), pads_begin.end()); - } - if (m_auto_pad == PadType::VALID) { - m_pads_end = Shape(m_pads_end.size(), 0); - m_pads_begin = Shape(m_pads_begin.size(), 0); - } - // infer_batched_forward_pooling wants CoordinateDiffs for these, while the pooling ops for - // now still take Shape (no negative padding). - CoordinateDiff pads_begin(m_pads_begin.begin(), m_pads_begin.end()); - CoordinateDiff pads_end(m_pads_end.begin(), m_pads_end.end()); - set_output_type(0, - get_input_element_type(0), - update_auto_padding_succeed - ? ngraph::infer_batched_pooling_forward(this, - arg_shape, - pads_begin, - pads_end, - m_kernel, - m_strides, - !m_exclude_pad, - m_rounding_type == op::RoundingType::CEIL, - Strides{}) // no dilation of the window - : output_shape); + const auto output_shapes = shape_infer(this, get_node_input_partial_shapes(*this)); + set_output_type(0, get_input_element_type(0), output_shapes.front()); } const ov::Shape& ov::op::v1::AvgPool::get_kernel() const { diff --git a/src/core/src/op/max_pool.cpp b/src/core/src/op/max_pool.cpp index 940a8809b5a..13d98688c4d 100644 --- a/src/core/src/op/max_pool.cpp +++ b/src/core/src/op/max_pool.cpp @@ -5,6 +5,7 @@ #include "ngraph/op/max_pool.hpp" #include "itt.hpp" +#include "max_pool_shape_inference.hpp" #include "ngraph/attribute_visitor.hpp" #include "ngraph/op/add.hpp" #include "ngraph/op/constant.hpp" @@ -40,11 +41,8 @@ bool ngraph::op::v1::MaxPool::visit_attributes(AttributeVisitor& visitor) { void op::v1::MaxPool::validate_and_infer_types() { OV_OP_SCOPE(v1_MaxPool_validate_and_infer_types); - MaxPoolBase::validate_and_infer_types(); - - const ov::PartialShape output_shape = infer_output_shape(Strides{}); // no dilations of the filter window - - set_output_type(0, get_input_element_type(0), output_shape); + const auto output_shapes = shape_infer(this, get_node_input_partial_shapes(*this)); + set_output_type(0, get_input_element_type(0), output_shapes.front()); } shared_ptr op::v1::MaxPool::clone_with_new_inputs(const OutputVector& new_args) const { @@ -109,21 +107,8 @@ bool evaluate_maxpool(const HostTensorPtr& arg, } // namespace maxpool bool op::v1::MaxPool::evaluate_maxpool(const HostTensorVector& outputs, const HostTensorVector& inputs) const { - auto arg_shape = inputs[0]->get_partial_shape(); - auto pads_begin_s = get_pads_begin(); - auto pads_end_s = get_pads_end(); - update_auto_padding(arg_shape, Strides(m_kernel.size(), 1), pads_end_s, pads_begin_s); - CoordinateDiff pads_begin(pads_begin_s.begin(), pads_begin_s.end()); - CoordinateDiff pads_end(pads_end_s.begin(), pads_end_s.end()); - auto out_shape = infer_batched_pooling_forward(this, - arg_shape, - pads_begin, - pads_end, - get_kernel(), - get_strides(), - true, - get_rounding_type() == op::RoundingType::CEIL, - Strides{}); // no dilation of the window + const auto input_shapes = std::vector{inputs[0]->get_partial_shape()}; + auto out_shape = shape_infer(this, input_shapes).front(); return maxpool::evaluate_maxpool(inputs[0], outputs[0], @@ -286,17 +271,14 @@ bool ngraph::op::v8::MaxPool::visit_attributes(AttributeVisitor& visitor) { void op::v8::MaxPool::validate_and_infer_types() { OV_OP_SCOPE(v8_MaxPool_validate_and_infer_types); - MaxPoolBase::validate_and_infer_types(); - const auto input_shape = get_input_partial_shape(0); if (input_shape.rank().is_static()) { m_axis = ngraph::normalize_axis(this, m_axis, input_shape.rank()); } - const ov::PartialShape output_shape = infer_output_shape(m_dilations); - - set_output_type(0, get_input_element_type(0), output_shape); - set_output_type(1, m_index_element_type, output_shape); + const auto output_shapes = shape_infer(this, get_node_input_partial_shapes(*this)); + set_output_type(0, get_input_element_type(0), output_shapes[0]); + set_output_type(1, m_index_element_type, output_shapes[1]); } shared_ptr op::v8::MaxPool::clone_with_new_inputs(const OutputVector& new_args) const { @@ -335,21 +317,8 @@ bool op::v8::MaxPool::has_evaluate() const { bool op::v8::MaxPool::evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const { OV_OP_SCOPE(v8_MaxPool_evaluate); - const auto arg_shape = inputs[0]->get_partial_shape(); - auto pads_begin_s = get_pads_begin(); - auto pads_end_s = get_pads_end(); - update_auto_padding(arg_shape, get_dilations(), pads_end_s, pads_begin_s); - CoordinateDiff pads_begin(pads_begin_s.begin(), pads_begin_s.end()); - CoordinateDiff pads_end(pads_end_s.begin(), pads_end_s.end()); - auto out_shape = infer_batched_pooling_forward(this, - arg_shape, - pads_begin, - pads_end, - get_kernel(), - get_strides(), - true, - get_rounding_type() == op::RoundingType::CEIL, - get_dilations()); + const auto input_shapes = std::vector{inputs[0]->get_partial_shape()}; + auto out_shape = shape_infer(this, input_shapes).front(); return maxpool_v8::evaluate_maxpool(inputs[0], outputs[0], diff --git a/src/core/src/op/util/max_pool_base.cpp b/src/core/src/op/util/max_pool_base.cpp index 1975436b2db..e9f889d38df 100644 --- a/src/core/src/op/util/max_pool_base.cpp +++ b/src/core/src/op/util/max_pool_base.cpp @@ -2,14 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "ngraph/op/util/max_pool_base.hpp" - -#include +#include "openvino/op/util/max_pool_base.hpp" #include "itt.hpp" -#include "ngraph/shape.hpp" - -using namespace std; ov::op::util::MaxPoolBase::MaxPoolBase(const Output& arg, const Strides& strides, @@ -31,107 +26,19 @@ ov::op::util::MaxPoolBase::MaxPoolBase(const Output& arg, void ov::op::util::MaxPoolBase::validate_and_infer_types() { OV_OP_SCOPE(util_MaxPoolBase_validate_and_infer_types); - if (0 == m_strides.size()) { - m_strides = Strides(m_kernel.size(), 1); + if (m_strides.empty()) { + m_strides.resize(m_kernel.size(), 1); } - if (0 == m_pads_begin.size()) { - m_pads_begin = ov::Shape(m_kernel.size(), 0); + if (m_pads_begin.empty()) { + m_pads_begin.resize(m_kernel.size(), 0); } - if (0 == m_pads_end.size()) { - m_pads_end = ov::Shape(m_kernel.size(), 0); - } - - const PartialShape& arg_shape = get_input_partial_shape(0); - - NODE_VALIDATION_CHECK( - this, - arg_shape.rank().compatible(3) || arg_shape.rank().compatible(4) || arg_shape.rank().compatible(5), - "Expected a 3D, 4D or 5D tensor for the input. Got: ", - arg_shape); - - if (arg_shape.rank().is_static()) { - NODE_VALIDATION_CHECK(this, - static_cast(m_pads_end.size()) == arg_shape.rank().get_max_length() - 2, - "Expected pads_end size to be equal to input size - 2. Got: ", - m_pads_end.size()); - - NODE_VALIDATION_CHECK(this, - static_cast(m_pads_begin.size()) == arg_shape.rank().get_max_length() - 2, - "Expected pads_begin size to be equal to input size - 2. Got: ", - m_pads_begin.size()); - NODE_VALIDATION_CHECK(this, - static_cast(m_kernel.size()) == arg_shape.rank().get_max_length() - 2, - "Expected kernel size to be equal to input size - 2. Got: ", - m_kernel.size()); - NODE_VALIDATION_CHECK(this, - static_cast(m_strides.size()) == arg_shape.rank().get_max_length() - 2, - "Expected strides size to be equal to input size - 2. Got: ", - m_strides.size()); + if (m_pads_end.empty()) { + m_pads_end.resize(m_kernel.size(), 0); } } -ov::PartialShape ov::op::util::MaxPoolBase::infer_output_shape(const Strides& dilations) { - OV_OP_SCOPE(util_MaxPoolBase_infer_output_shape); - - const auto& arg_shape = get_input_partial_shape(0); - - bool update_auto_padding_succeed = true; - - if (m_auto_pad == PadType::SAME_UPPER || m_auto_pad == PadType::SAME_LOWER) { - const auto filter_dilations = dilations.empty() ? Strides(m_kernel.size(), 1) : dilations; - update_auto_padding_succeed = update_auto_padding(arg_shape, filter_dilations, m_pads_end, m_pads_begin); - } - if (m_auto_pad == PadType::VALID) { - m_pads_end = ov::Shape(m_pads_end.size(), 0); - m_pads_begin = ov::Shape(m_pads_begin.size(), 0); - } - - auto output_shape = PartialShape::dynamic(); - if (update_auto_padding_succeed) { - CoordinateDiff pads_begin(m_pads_begin.begin(), m_pads_begin.end()); - CoordinateDiff pads_end(m_pads_end.begin(), m_pads_end.end()); - output_shape = ngraph::infer_batched_pooling_forward(this, - get_input_partial_shape(0), - pads_begin, - pads_end, - m_kernel, - m_strides, - true, - m_rounding_type == op::RoundingType::CEIL, - dilations); - } else { - if (arg_shape.rank().is_static() && arg_shape.rank().get_max_length() > 0) { - output_shape = std::vector(arg_shape.rank().get_max_length(), Dimension::dynamic()); - if (arg_shape[0].is_static()) { - output_shape[0] = arg_shape[0]; // batch size - } - if (arg_shape[1].is_static()) { - output_shape[1] = arg_shape[1]; // channel size - } - } - } - - return output_shape; -} - -bool ov::op::util::MaxPoolBase::update_auto_padding(const PartialShape& in_shape, - const Strides& filter_dilations, - ov::Shape& new_pads_end, - ov::Shape& new_pads_begin) const { - bool update_auto_padding_succeed = true; - if (m_auto_pad == PadType::SAME_UPPER || m_auto_pad == PadType::SAME_LOWER) { - CoordinateDiff pads_end, pads_begin; - update_auto_padding_succeed = ngraph::try_apply_auto_padding(in_shape, - m_kernel, - m_strides, - filter_dilations, - m_auto_pad, - pads_end, - pads_begin); - new_pads_end = ov::Shape(pads_end.begin(), pads_end.end()); - new_pads_begin = ov::Shape(pads_begin.begin(), pads_begin.end()); - } - return update_auto_padding_succeed; +void ov::op::util::MaxPoolBase::set_pads_end(Shape pads_end) { + m_pads_end = std::move(pads_end); } diff --git a/src/core/src/pass/serialize.cpp b/src/core/src/pass/serialize.cpp index 81e0d09cfb9..87e3a168c9f 100644 --- a/src/core/src/pass/serialize.cpp +++ b/src/core/src/pass/serialize.cpp @@ -779,7 +779,7 @@ void auto_pad_resolving(ov::Node* node) { } else if (auto op = as_type(node)) { if (pad_agnostic_types.count(op->get_auto_pad())) { op->set_pads_begin(Shape(op->get_pads_begin().size(), 0)); - op->set_adding_above(Shape(op->get_pads_end().size(), 0)); + op->set_pads_end(Shape(op->get_pads_end().size(), 0)); } } } diff --git a/src/core/tests/type_prop/adaptive_avg_pool.cpp b/src/core/tests/type_prop/adaptive_avg_pool.cpp index 17e6cfa4427..c249d771c22 100644 --- a/src/core/tests/type_prop/adaptive_avg_pool.cpp +++ b/src/core/tests/type_prop/adaptive_avg_pool.cpp @@ -2,94 +2,143 @@ // SPDX-License-Identifier: Apache-2.0 // +#include "common_test_utils/test_assertions.hpp" #include "gtest/gtest.h" -#include "ngraph/ngraph.hpp" +#include "openvino/opsets/opset10.hpp" #include "util/type_prop.hpp" using namespace std; -using namespace ngraph; +using namespace ov; +using namespace ov::opset10; +using namespace testing; -TEST(type_prop, adaptive_avg_pool) { - const PartialShape arg_shape{1, 6, 8, 9}; - const vector output_shape{5, 7}; +class AdaptiveAvgPoolV8Test : public TypePropOpTest {}; - auto data = make_shared(element::f32, arg_shape); - auto out_shape = op::Constant::create(element::i64, Shape{2}, output_shape); - auto adaptive_pool = make_shared(data, out_shape); +TEST_F(AdaptiveAvgPoolV8Test, default_ctor) { + const auto data = make_shared(element::f32, PartialShape{2, 6, 3, 2}); + const auto out_shape = Constant::create(element::i64, Shape{2}, {5, 7}); - ASSERT_TRUE(adaptive_pool->get_output_partial_shape(0).same_scheme({1, 6, 5, 7})); + const auto op = make_op(); + op->set_arguments(OutputVector{data, out_shape}); + op->validate_and_infer_types(); + + EXPECT_EQ(op->get_input_size(), 2); + EXPECT_EQ(op->get_output_size(), 1); + EXPECT_EQ(op->get_output_element_type(0), element::f32); + EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({2, 6, 5, 7})); } -TEST(type_prop, adaptive_avg_pool_dyn_batch) { - const PartialShape arg_shape{Dimension::dynamic(), 6, 8, 9}; - const vector output_shape{5, 7}; +TEST_F(AdaptiveAvgPoolV8Test, static_dim_shape_prop) { + auto data_shape = PartialShape{1, 6, 8, 9}; + set_shape_labels(data_shape, 10); - auto data = make_shared(element::f32, arg_shape); - auto out_shape = op::Constant::create(element::i64, Shape{2}, output_shape); - auto adaptive_pool = make_shared(data, out_shape); + const auto data = make_shared(element::f32, data_shape); + const auto out_shape = Constant::create(element::i64, Shape{2}, {5, 7}); + const auto op = make_op(data, out_shape); - ASSERT_TRUE(adaptive_pool->get_output_partial_shape(0).same_scheme({Dimension::dynamic(), 6, 5, 7})); + EXPECT_EQ(op->get_output_element_type(0), element::f32); + EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({1, 6, 5, 7})); + EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), ElementsAre(10, 11, ov::no_label, ov::no_label)); } -TEST(type_prop, adaptive_avg_pool_dyn_channels) { - const PartialShape arg_shape{1, Dimension::dynamic(), 8, 9}; - const vector output_shape{5, 7}; +TEST_F(AdaptiveAvgPoolV8Test, dynamic_batch) { + PartialShape data_shape{Dimension::dynamic(), 6, 8, 9}; + set_shape_labels(data_shape, 10); - auto data = make_shared(element::f32, arg_shape); - auto out_shape = op::Constant::create(element::i64, Shape{2}, output_shape); - auto adaptive_pool = make_shared(data, out_shape); + const auto data = make_shared(element::f32, data_shape); + const auto out_shape = Constant::create(element::i64, Shape{2}, {5, 7}); + const auto op = make_op(data, out_shape); - ASSERT_TRUE(adaptive_pool->get_output_partial_shape(0).same_scheme({1, Dimension::dynamic(), 5, 7})); + EXPECT_EQ(op->get_output_element_type(0), element::f32); + EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({-1, 6, 5, 7})); + EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), ElementsAre(10, 11, ov::no_label, ov::no_label)); } -TEST(type_prop, adaptive_avg_pool_dyn_spatial) { - const PartialShape arg_shape{1, 6, Dimension::dynamic(), Dimension::dynamic()}; - const vector output_shape{5, 7}; +TEST_F(AdaptiveAvgPoolV8Test, dynamic_channel) { + PartialShape data_shape{1, Dimension::dynamic(), {10, 20}, 9}; + set_shape_labels(data_shape, 20); - auto data = make_shared(element::f32, arg_shape); - auto out_shape = op::Constant::create(element::i64, Shape{2}, output_shape); - auto adaptive_pool = make_shared(data, out_shape); + const auto data = make_shared(element::f32, data_shape); + const auto out_shape = Constant::create(element::i64, Shape{2}, {5, 7}); + const auto op = make_op(data, out_shape); - ASSERT_TRUE(adaptive_pool->get_output_partial_shape(0).same_scheme({1, 6, 5, 7})); + EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({1, -1, 5, 7})); + EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), ElementsAre(20, 21, ov::no_label, ov::no_label)); } -TEST(type_prop, adaptive_avg_pool_dyn_output_shape) { - const PartialShape arg_shape{1, 6, 8, 9}; +TEST_F(AdaptiveAvgPoolV8Test, dynamic_spatial) { + PartialShape data_shape{1, 6, -1, -1}; + set_shape_labels(data_shape, 20); - auto data = make_shared(element::f32, arg_shape); - auto out_shape = make_shared(element::i64, Shape{2}); - auto adaptive_pool = make_shared(data, out_shape); + const auto data = make_shared(element::f32, data_shape); + const auto out_shape = Constant::create(element::i64, Shape{2}, {5, 7}); + const auto op = make_op(data, out_shape); - ASSERT_TRUE( - adaptive_pool->get_output_partial_shape(0).same_scheme({1, 6, Dimension::dynamic(), Dimension::dynamic()})); + EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({1, 6, 5, 7})); + EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), ElementsAre(20, 21, ov::no_label, ov::no_label)); } -TEST(type_prop, adaptive_avg_pool_dyn_rank) { - const PartialShape arg_shape = PartialShape::dynamic(); +TEST_F(AdaptiveAvgPoolV8Test, dynamic_output_shape) { + auto data = make_shared(element::f32, PartialShape{1, 6, 8, 9, 2}); + auto out_shape = make_shared(element::i64, PartialShape::dynamic()); + const auto op = make_op(data, out_shape); - auto data = make_shared(element::f32, arg_shape); - auto out_shape = make_shared(element::i64, Shape{2}); - auto adaptive_pool = make_shared(data, out_shape); - - ASSERT_TRUE(adaptive_pool->get_output_partial_shape(0).same_scheme(PartialShape::dynamic())); + EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({1, 6, -1, -1, -1})); } -TEST(type_prop, adaptive_avg_pool_unsupported_input_shape) { - const PartialShape arg_shape{1, 6}; - const vector output_shape{1}; +TEST_F(AdaptiveAvgPoolV8Test, output_shape_as_parameter) { + auto data = make_shared(element::f32, PartialShape{1, 6, 8, 9, 2}); + auto out_shape = make_shared(element::i64, PartialShape{3}); + const auto op = make_op(data, out_shape); - auto data = make_shared(element::f32, arg_shape); - auto out_shape = op::Constant::create(element::i64, Shape{}, output_shape); - - EXPECT_THROW(const auto unused = make_shared(data, out_shape), NodeValidationFailure); + EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({1, 6, -1, -1, -1})); } -TEST(type_prop, adaptive_avg_pool_wrong_out_shape) { - const PartialShape arg_shape{1, 6, 8, 9}; - const vector output_shape{5, 7, 8}; +TEST_F(AdaptiveAvgPoolV8Test, data_dynamic_rank) { + auto data = make_shared(element::f32, PartialShape::dynamic()); + auto out_shape = make_shared(element::i32, Shape{3}); + const auto op = make_op(data, out_shape); - auto data = make_shared(element::f32, arg_shape); - auto out_shape = op::Constant::create(element::i64, Shape{3}, output_shape); - - EXPECT_THROW(const auto unused = make_shared(data, out_shape), NodeValidationFailure); + EXPECT_EQ(op->get_output_partial_shape(0), PartialShape::dynamic()); +} + +TEST_F(AdaptiveAvgPoolV8Test, preserve_partial_values_and_labels_on_output_shape_input) { + auto data_shape = PartialShape{{1, 2}, {2, 4}, 5, {10, 20}, -1}; + set_shape_labels(data_shape, 10); + auto out_shape = PartialShape{{2, 6}, 3, {12, 13}}; + set_shape_labels(out_shape, 20); + + const auto data = make_shared(element::f32, data_shape); + const auto spatial_dim_shape = make_shared(make_shared(element::i64, out_shape)); + const auto op = make_op(data, spatial_dim_shape); + + EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({{1, 2}, {2, 4}, {2, 6}, 3, {12, 13}})); + EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), ElementsAre(10, 11, 20, 21, 22)); +} + +TEST_F(AdaptiveAvgPoolV8Test, out_spatial_shape_size_not_match_data_spatial_dimensions) { + auto data = make_shared(element::f32, PartialShape{2, 3, 5, 6}); + auto out_shape = make_shared(element::i32, Shape{3}); + + OV_EXPECT_THROW(const auto op = make_op(data, out_shape), + NodeValidationFailure, + HasSubstr("Output shape for spatial dimension not compatible with data shape.")); +} + +TEST_F(AdaptiveAvgPoolV8Test, unsupported_input_shape) { + auto data = make_shared(element::f32, PartialShape{1, 6}); + auto out_shape = Constant::create(element::i64, Shape{}, {1}); + + OV_EXPECT_THROW(const auto op = make_op(data, out_shape), + NodeValidationFailure, + HasSubstr("Expected a 3D, 4D or 5D tensor for the input. Got:")); +} + +TEST_F(AdaptiveAvgPoolV8Test, wrong_out_shape) { + auto data = make_shared(element::f32, PartialShape{1, 6, 8, 9}); + auto out_shape = Constant::create(element::i64, Shape{3}, {5, 7, 8}); + + OV_EXPECT_THROW(const auto op = make_op(data, out_shape), + NodeValidationFailure, + HasSubstr("Output shape for spatial dimension not compatible with data shape.")); } diff --git a/src/core/tests/type_prop/adaptive_max_pool.cpp b/src/core/tests/type_prop/adaptive_max_pool.cpp index 2d1e2d59c85..a9be5ebbaf3 100644 --- a/src/core/tests/type_prop/adaptive_max_pool.cpp +++ b/src/core/tests/type_prop/adaptive_max_pool.cpp @@ -2,114 +2,191 @@ // SPDX-License-Identifier: Apache-2.0 // +#include "common_test_utils/test_assertions.hpp" #include "gtest/gtest.h" -#include "ngraph/ngraph.hpp" +#include "openvino/opsets/opset10.hpp" #include "util/type_prop.hpp" using namespace std; -using namespace ngraph; +using namespace ov; +using namespace ov::opset10; +using namespace testing; -TEST(type_prop, adaptive_max_pool) { - const PartialShape arg_shape{1, 6, 8, 9}; - const vector output_shape{5, 7}; +class AdaptiveMaxPoolV8Test : public TypePropOpTest {}; - auto data = make_shared(element::f32, arg_shape); - auto out_shape = op::Constant::create(element::i64, Shape{2}, output_shape); - auto adaptive_pool = make_shared(data, out_shape); +TEST_F(AdaptiveMaxPoolV8Test, default_ctor) { + const auto data = make_shared(element::f32, PartialShape{2, 6, 3, 2}); + const auto out_shape = Constant::create(element::i64, Shape{2}, {5, 7}); - ASSERT_TRUE(adaptive_pool->get_output_partial_shape(0).same_scheme({1, 6, 5, 7})); - ASSERT_TRUE(adaptive_pool->get_output_partial_shape(1).same_scheme({1, 6, 5, 7})); + const auto op = make_op(); + op->set_arguments(OutputVector{data, out_shape}); + op->set_index_element_type(element::i64); + op->validate_and_infer_types(); + + EXPECT_EQ(op->get_index_element_type(), element::i64); + EXPECT_EQ(op->get_input_size(), 2); + EXPECT_EQ(op->get_output_size(), 2); + EXPECT_THAT(op->outputs(), + ElementsAre(Property("Output type", &Output::get_element_type, element::f32), + Property("Indices type", &Output::get_element_type, element::i64))); + EXPECT_THAT(op->outputs(), + Each(Property("PartialShape", &Output::get_partial_shape, PartialShape({2, 6, 5, 7})))); } -TEST(type_prop, adaptive_max_pool_i32_indices) { - const PartialShape arg_shape{1, 6, 8, 9}; - const vector output_shape{5, 7}; +TEST_F(AdaptiveMaxPoolV8Test, shape_infer) { + const auto data = make_shared(element::f64, Shape{2, 6, 3, 2, 10}); + const auto out_shape = Constant::create(element::i64, Shape{3}, {5, 7, 1}); - auto data = make_shared(element::f32, arg_shape); - auto out_shape = op::Constant::create(element::i64, Shape{2}, output_shape); - auto adaptive_pool = make_shared(data, out_shape, element::i32); + const auto op = make_op(data, out_shape); - ASSERT_TRUE(adaptive_pool->get_output_partial_shape(0).same_scheme({1, 6, 5, 7})); - ASSERT_EQ(adaptive_pool->output(1).get_element_type(), element::i32); - ASSERT_TRUE(adaptive_pool->get_output_partial_shape(1).same_scheme({1, 6, 5, 7})); + EXPECT_THAT(op->outputs(), + ElementsAre(Property("Output type", &Output::get_element_type, element::f64), + Property("Indices type", &Output::get_element_type, element::i64))); + EXPECT_THAT(op->outputs(), Each(Property("Shape", &Output::get_shape, Shape({2, 6, 5, 7, 1})))); } -TEST(type_prop, adaptive_max_pool_dyn_batch) { - const PartialShape arg_shape{Dimension::dynamic(), 6, 8, 9}; - const vector output_shape{5, 7}; +TEST_F(AdaptiveMaxPoolV8Test, i32_indices) { + auto data_shape = PartialShape{2, 6, 2, 10}; + set_shape_labels(data_shape, 10); - auto data = make_shared(element::f32, arg_shape); - auto out_shape = op::Constant::create(element::i64, Shape{2}, output_shape); - auto adaptive_pool = make_shared(data, out_shape); + const auto data = make_shared(element::f64, data_shape); + const auto out_shape = Constant::create(element::i32, Shape{2}, {7, 1}); - ASSERT_TRUE(adaptive_pool->get_output_partial_shape(0).same_scheme({Dimension::dynamic(), 6, 5, 7})); - ASSERT_TRUE(adaptive_pool->get_output_partial_shape(1).same_scheme({Dimension::dynamic(), 6, 5, 7})); + const auto op = make_op(data, out_shape, element::i32); + + EXPECT_THAT(op->outputs(), + ElementsAre(Property("Output type", &Output::get_element_type, element::f64), + Property("Indices type", &Output::get_element_type, element::i32))); + EXPECT_THAT(op->outputs(), + Each(Property("PartialShape", &Output::get_partial_shape, PartialShape({2, 6, 7, 1})))); + EXPECT_THAT(op->outputs(), + Each(Property(&Output::get_partial_shape, + ResultOf(get_shape_labels, ElementsAre(10, 11, ov::no_label, ov::no_label))))); } -TEST(type_prop, adaptive_max_pool_dyn_channels) { - const PartialShape arg_shape{1, Dimension::dynamic(), 8, 9}; - const vector output_shape{5, 7}; +TEST_F(AdaptiveMaxPoolV8Test, dynamic_batch) { + PartialShape data_shape{Dimension::dynamic(), 6, 8, 9}; + set_shape_labels(data_shape, 10); - auto data = make_shared(element::f32, arg_shape); - auto out_shape = op::Constant::create(element::i64, Shape{2}, output_shape); - auto adaptive_pool = make_shared(data, out_shape); + const auto data = make_shared(element::f32, data_shape); + const auto out_shape = Constant::create(element::i64, Shape{2}, {9, 9}); + const auto op = make_op(data, out_shape); - ASSERT_TRUE(adaptive_pool->get_output_partial_shape(0).same_scheme({1, Dimension::dynamic(), 5, 7})); - ASSERT_TRUE(adaptive_pool->get_output_partial_shape(1).same_scheme({1, Dimension::dynamic(), 5, 7})); + EXPECT_THAT(op->outputs(), + Each(Property("PartialShape", &Output::get_partial_shape, PartialShape({-1, 6, 9, 9})))); + EXPECT_THAT(op->outputs(), + Each(Property(&Output::get_partial_shape, + ResultOf(get_shape_labels, ElementsAre(10, 11, ov::no_label, ov::no_label))))); } -TEST(type_prop, adaptive_max_pool_dyn_spatial) { - const PartialShape arg_shape{1, 6, Dimension::dynamic(), Dimension::dynamic()}; - const vector output_shape{5, 7}; +TEST_F(AdaptiveMaxPoolV8Test, dynamic_channel) { + PartialShape data_shape{2, Dimension::dynamic(), {10, 20}, 9}; + set_shape_labels(data_shape, 10); - auto data = make_shared(element::f32, arg_shape); - auto out_shape = op::Constant::create(element::i64, Shape{2}, output_shape); - auto adaptive_pool = make_shared(data, out_shape); + const auto data = make_shared(element::f32, data_shape); + const auto out_shape = Constant::create(element::i64, Shape{2}, {5, 7}); + const auto op = make_op(data, out_shape); - ASSERT_TRUE(adaptive_pool->get_output_partial_shape(0).same_scheme({1, 6, 5, 7})); - ASSERT_TRUE(adaptive_pool->get_output_partial_shape(1).same_scheme({1, 6, 5, 7})); + EXPECT_THAT(op->outputs(), + Each(Property("PartialShape", &Output::get_partial_shape, PartialShape({2, -1, 5, 7})))); + EXPECT_THAT(op->outputs(), + Each(Property(&Output::get_partial_shape, + ResultOf(get_shape_labels, ElementsAre(10, 11, ov::no_label, ov::no_label))))); } -TEST(type_prop, adaptive_max_pool_dyn_output_shape) { - const PartialShape arg_shape{1, 6, 8, 9}; +TEST_F(AdaptiveMaxPoolV8Test, dynamic_spatial) { + PartialShape data_shape{2, 6, -1, -1}; + set_shape_labels(data_shape, 10); - auto data = make_shared(element::f32, arg_shape); - auto out_shape = make_shared(element::i64, Shape{2}); - auto adaptive_pool = make_shared(data, out_shape); + const auto data = make_shared(element::f32, data_shape); + const auto out_shape = Constant::create(element::i64, Shape{2}, {5, 7}); + const auto op = make_op(data, out_shape); - ASSERT_TRUE( - adaptive_pool->get_output_partial_shape(0).same_scheme({1, 6, Dimension::dynamic(), Dimension::dynamic()})); - ASSERT_TRUE( - adaptive_pool->get_output_partial_shape(1).same_scheme({1, 6, Dimension::dynamic(), Dimension::dynamic()})); + EXPECT_THAT(op->outputs(), + Each(Property("PartialShape", &Output::get_partial_shape, PartialShape({2, 6, 5, 7})))); + EXPECT_THAT(op->outputs(), + Each(Property(&Output::get_partial_shape, + ResultOf(get_shape_labels, ElementsAre(10, 11, ov::no_label, ov::no_label))))); } -TEST(type_prop, adaptive_max_pool_dyn_rank) { - const PartialShape arg_shape = PartialShape::dynamic(); +TEST_F(AdaptiveMaxPoolV8Test, dynamic_output_shape) { + auto data = make_shared(element::f32, PartialShape{1, 6, 8, 9, 2}); + auto out_shape = make_shared(element::i64, PartialShape::dynamic()); + const auto op = make_op(data, out_shape); - auto data = make_shared(element::f32, arg_shape); - auto out_shape = make_shared(element::i64, Shape{2}); - auto adaptive_pool = make_shared(data, out_shape); - - ASSERT_TRUE(adaptive_pool->get_output_partial_shape(0).same_scheme(PartialShape::dynamic())); - ASSERT_TRUE(adaptive_pool->get_output_partial_shape(1).same_scheme(PartialShape::dynamic())); + EXPECT_THAT(op->outputs(), + Each(Property("PartialShape", &Output::get_partial_shape, PartialShape({1, 6, -1, -1, -1})))); } -TEST(type_prop, adaptive_max_pool_unsupported_input_shape) { - const PartialShape arg_shape{1, 6}; - const vector output_shape{1}; +TEST_F(AdaptiveMaxPoolV8Test, output_shape_as_parameter) { + auto data = make_shared(element::f32, PartialShape{1, 6, 8, 9, 2}); + auto out_shape = make_shared(element::i64, PartialShape{3}); + const auto op = make_op(data, out_shape); - auto data = make_shared(element::f32, arg_shape); - auto out_shape = op::Constant::create(element::i64, Shape{}, output_shape); - - EXPECT_THROW(const auto unused = make_shared(data, out_shape), NodeValidationFailure); + EXPECT_THAT(op->outputs(), + Each(Property("PartialShape", &Output::get_partial_shape, PartialShape({1, 6, -1, -1, -1})))); } -TEST(type_prop, adaptive_max_pool_wrong_out_shape) { - const PartialShape arg_shape{1, 6, 8, 9}; - const vector output_shape{5, 7, 8}; +TEST_F(AdaptiveMaxPoolV8Test, data_dynamic_rank) { + auto data = make_shared(element::f32, PartialShape::dynamic()); + auto out_shape = make_shared(element::i32, Shape{3}); + const auto op = make_op(data, out_shape); - auto data = make_shared(element::f32, arg_shape); - auto out_shape = op::Constant::create(element::i64, Shape{3}, output_shape); - - EXPECT_THROW(const auto unused = make_shared(data, out_shape), NodeValidationFailure); + EXPECT_THAT(op->outputs(), + Each(Property("PartialShape", &Output::get_partial_shape, PartialShape::dynamic()))); +} + +TEST_F(AdaptiveMaxPoolV8Test, out_spatial_shape_size_not_match_data_spatial_dimensions) { + auto data = make_shared(element::f32, PartialShape{2, 3, 5, 6}); + auto out_shape = make_shared(element::i32, Shape{3}); + + OV_EXPECT_THROW(const auto op = make_op(data, out_shape), + NodeValidationFailure, + HasSubstr("Output shape for spatial dimension not compatible with data shape.")); +} + +TEST_F(AdaptiveMaxPoolV8Test, preserve_partial_values_and_labels_on_output_shape_input) { + auto data_shape = PartialShape{{1, 2}, {2, 4}, 5, {10, 20}, -1}; + set_shape_labels(data_shape, 10); + auto out_shape = PartialShape{{2, 6}, -1, {12, 13}}; + set_shape_labels(out_shape, 20); + + const auto data = make_shared(element::f32, data_shape); + const auto spatial_dim_shape = make_shared(make_shared(element::i64, out_shape)); + const auto op = make_op(data, spatial_dim_shape); + + EXPECT_THAT(op->outputs(), + Each(Property("PartialShape", + &Output::get_partial_shape, + PartialShape({{1, 2}, {2, 4}, {2, 6}, -1, {12, 13}})))); + EXPECT_THAT( + op->outputs(), + Each(Property(&Output::get_partial_shape, ResultOf(get_shape_labels, ElementsAre(10, 11, 20, 21, 22))))); +} + +TEST_F(AdaptiveMaxPoolV8Test, unsupported_input_shape) { + auto data = make_shared(element::f32, PartialShape{1, 6}); + auto out_shape = Constant::create(element::i64, Shape{}, {1}); + + OV_EXPECT_THROW(const auto op = make_op(data, out_shape), + NodeValidationFailure, + HasSubstr("Expected a 3D, 4D or 5D tensor for the input. Got:")); +} + +TEST_F(AdaptiveMaxPoolV8Test, wrong_out_shape) { + auto data = make_shared(element::f32, PartialShape{1, 6, 8, 9}); + auto out_shape = Constant::create(element::i64, Shape{3}, {5, 7, 8}); + + OV_EXPECT_THROW(const auto op = make_op(data, out_shape), + NodeValidationFailure, + HasSubstr("Output shape for spatial dimension not compatible with data shape.")); +} + +TEST_F(AdaptiveMaxPoolV8Test, wrong_index_element_type) { + auto data = make_shared(element::f32, PartialShape{1, 6, 8, 9}); + auto out_shape = Constant::create(element::i16, Shape{2}, {5, 7}); + + OV_EXPECT_THROW(const auto op = make_op(data, out_shape, element::i16), + NodeValidationFailure, + HasSubstr("Index element type must be i32 or i64")); } diff --git a/src/core/tests/type_prop/avg_pool.cpp b/src/core/tests/type_prop/avg_pool.cpp index 365eecbf7fd..47a01018934 100644 --- a/src/core/tests/type_prop/avg_pool.cpp +++ b/src/core/tests/type_prop/avg_pool.cpp @@ -2,12 +2,39 @@ // SPDX-License-Identifier: Apache-2.0 // +#include "common_test_utils/test_assertions.hpp" #include "gtest/gtest.h" #include "ngraph/ngraph.hpp" #include "util/type_prop.hpp" using namespace std; using namespace ngraph; +using namespace testing; + +TEST(type_prop, avg_pool_default_ctor) { + PartialShape arg_shape{1, 3, 32}; + set_shape_labels(arg_shape, 10); + auto arg = make_shared(element::f32, arg_shape); + + auto mp = make_shared(); + mp->set_argument(0, arg); + mp->set_pads_begin({2}); + mp->set_pads_end({2}); + mp->set_kernel({2}); + mp->set_strides({1}); + mp->set_rounding_type(op::RoundingType::CEIL); + mp->set_auto_pad(op::PadType::SAME_LOWER); + mp->validate_and_infer_types(); + + EXPECT_TRUE(mp->get_exclude_pad()); + EXPECT_EQ(mp->get_input_size(), 1); + EXPECT_EQ(mp->get_output_size(), 1); + EXPECT_EQ(mp->get_output_element_type(0), element::f32); + EXPECT_EQ(mp->get_output_partial_shape(0), PartialShape({1, 3, 32})); + EXPECT_THAT(get_shape_labels(mp->get_output_partial_shape(0)), ElementsAre(10, 11, ov::no_label)); + EXPECT_EQ(mp->get_pads_begin(), (Shape{1})); + EXPECT_EQ(mp->get_pads_end(), (Shape{0})); +} TEST(type_prop, avg_pool_auto_padding) { const PartialShape arg_shape{1, 3, 32}; @@ -29,20 +56,20 @@ TEST(type_prop, avg_pool_auto_padding) { rounding_mode, auto_pad); - ASSERT_TRUE(mp->get_output_partial_shape(0).same_scheme({1, 3, 32})); - ASSERT_EQ(mp->get_pads_begin(), (Shape{1})); - ASSERT_EQ(mp->get_pads_end(), (Shape{0})); + EXPECT_EQ(mp->get_output_partial_shape(0), PartialShape({1, 3, 32})); + EXPECT_EQ(mp->get_pads_begin(), (Shape{1})); + EXPECT_EQ(mp->get_pads_end(), (Shape{0})); } -TEST(type_prop, avg_pool_auto_padding_3D_nc_dims_dynamic_same_lower) { - const PartialShape arg_shape{Dimension::dynamic(), 32, 32}; - const Strides strides{1}; - const Shape pads_begin{0}; - const Shape pads_end{0}; - const Shape kernel_shape{2}; +TEST(type_prop, avg_pool_explicit_padding_round_ceil_dynamic_dimensions) { + const PartialShape arg_shape{-1, -1, -1}; + const Strides strides{4}; + const Shape pads_begin{2}; + const Shape pads_end{2}; + const Shape kernel_shape{4}; const bool exclude_pad = true; - const auto rounding_mode = op::RoundingType::FLOOR; - const auto auto_pad = op::PadType::SAME_LOWER; + const auto rounding_mode = op::RoundingType::CEIL; + const auto auto_pad = op::PadType::EXPLICIT; auto arg = make_shared(element::f32, arg_shape); auto mp = make_shared(arg, @@ -54,9 +81,9 @@ TEST(type_prop, avg_pool_auto_padding_3D_nc_dims_dynamic_same_lower) { rounding_mode, auto_pad); - ASSERT_TRUE(mp->get_output_partial_shape(0).same_scheme({Dimension::dynamic(), 32, 32})); - ASSERT_EQ(mp->get_pads_begin(), (Shape{1})); - ASSERT_EQ(mp->get_pads_end(), (Shape{0})); + EXPECT_EQ(mp->get_output_partial_shape(0), PartialShape({-1, -1, {1, -1}})); + EXPECT_EQ(mp->get_pads_begin(), (Shape{2})); + EXPECT_EQ(mp->get_pads_end(), (Shape{2})); } TEST(type_prop, avg_pool_auto_padding_4D_nc_dims_dynamic_same_lower) { @@ -79,9 +106,9 @@ TEST(type_prop, avg_pool_auto_padding_4D_nc_dims_dynamic_same_lower) { rounding_mode, auto_pad); - ASSERT_TRUE(mp->get_output_partial_shape(0).same_scheme({Dimension::dynamic(), Dimension::dynamic(), 32, 32})); - ASSERT_EQ(mp->get_pads_begin(), (Shape{1, 1})); - ASSERT_EQ(mp->get_pads_end(), (Shape{0, 0})); + EXPECT_EQ(mp->get_output_partial_shape(0), PartialShape({Dimension::dynamic(), Dimension::dynamic(), 32, 32})); + EXPECT_EQ(mp->get_pads_begin(), (Shape{1, 1})); + EXPECT_EQ(mp->get_pads_end(), (Shape{0, 0})); } TEST(type_prop, avg_pool_auto_padding_nc_dims_dynamic_same_upper) { @@ -104,9 +131,9 @@ TEST(type_prop, avg_pool_auto_padding_nc_dims_dynamic_same_upper) { rounding_mode, auto_pad); - ASSERT_TRUE(mp->get_output_partial_shape(0).same_scheme({Dimension::dynamic(), Dimension::dynamic(), 32, 32})); - ASSERT_EQ(mp->get_pads_begin(), (Shape{0, 0})); - ASSERT_EQ(mp->get_pads_end(), (Shape{1, 1})); + EXPECT_EQ(mp->get_output_partial_shape(0), PartialShape({Dimension::dynamic(), Dimension::dynamic(), 32, 32})); + EXPECT_EQ(mp->get_pads_begin(), (Shape{0, 0})); + EXPECT_EQ(mp->get_pads_end(), (Shape{1, 1})); } TEST(type_prop, avg_pool_auto_padding_spatial_dims_dynamic) { @@ -129,9 +156,9 @@ TEST(type_prop, avg_pool_auto_padding_spatial_dims_dynamic) { rounding_mode, auto_pad); - ASSERT_TRUE(mp->get_output_partial_shape(0).same_scheme({1, 3, 32, Dimension::dynamic()})); - ASSERT_EQ(mp->get_pads_begin(), (Shape{1, 0})); - ASSERT_EQ(mp->get_pads_end(), (Shape{0, 0})); + EXPECT_EQ(mp->get_output_partial_shape(0), PartialShape({1, 3, 32, Dimension::dynamic()})); + EXPECT_EQ(mp->get_pads_begin(), (Shape{1, 0})); + EXPECT_EQ(mp->get_pads_end(), (Shape{0, 0})); } TEST(type_prop, avg_pool_1d_deduce) { @@ -429,8 +456,8 @@ TEST(type_prop, avg_pool_partial_rank_dynamic_ok) { false, op::RoundingType::FLOOR); - ASSERT_EQ(ap->get_output_element_type(0), element::f32); - ASSERT_TRUE(ap->get_output_partial_shape(0).same_scheme(PartialShape::dynamic(6))); + EXPECT_EQ(ap->get_output_element_type(0), element::f32); + EXPECT_EQ(ap->get_output_partial_shape(0), PartialShape(PartialShape::dynamic(6))); } TEST(type_prop, avg_pool_partial_rank_dynamic_attrib_rank_mismatch) { @@ -468,8 +495,8 @@ TEST(type_prop, avg_pool_partial_rank_static_dynamic_ok) { false, op::RoundingType::FLOOR); - ASSERT_EQ(ap->get_output_element_type(0), element::f32); - ASSERT_TRUE(ap->get_output_partial_shape(0).same_scheme(PartialShape::dynamic(5))); + EXPECT_EQ(ap->get_output_element_type(0), element::f32); + EXPECT_EQ(ap->get_output_partial_shape(0), PartialShape({-1, -1, {1, -1}, {1, -1}, {1, -1}})); } TEST(type_prop, avg_pool_partial_rank_static_dynamic_some_dims_known_ok) { @@ -488,9 +515,8 @@ TEST(type_prop, avg_pool_partial_rank_static_dynamic_some_dims_known_ok) { false, op::RoundingType::FLOOR); - ASSERT_EQ(ap->get_output_element_type(0), element::f32); - ASSERT_TRUE( - ap->get_output_partial_shape(0).same_scheme(PartialShape{5, Dimension::dynamic(), 7, Dimension::dynamic(), 1})); + EXPECT_EQ(ap->get_output_element_type(0), element::f32); + EXPECT_EQ(ap->get_output_partial_shape(0), PartialShape(PartialShape{5, -1, 7, {1, -1}, 1})); } TEST(type_prop, avg_pool_partial_rank_static_dynamic_attrib_rank_mismatch) { @@ -547,9 +573,8 @@ TEST(type_prop, avg_pool_partial_rank_static_dynamic_padded_window_not_too_big) true, op::RoundingType::FLOOR); - ASSERT_EQ(ap->get_output_element_type(0), element::f32); - ASSERT_TRUE( - ap->get_output_partial_shape(0).same_scheme(PartialShape{5, Dimension::dynamic(), 1, Dimension::dynamic(), 1})); + EXPECT_EQ(ap->get_output_element_type(0), element::f32); + EXPECT_EQ(ap->get_output_partial_shape(0), PartialShape(PartialShape{5, Dimension::dynamic(), 1, {1, -1}, 1})); } TEST(type_prop, avg_pool_partial_rank_static_dynamic_window_in_padding) { @@ -570,3 +595,43 @@ TEST(type_prop, avg_pool_partial_rank_static_dynamic_window_in_padding) { op::RoundingType::FLOOR), NodeValidationFailure); } + +TEST(type_prop, avg_pool_kernel_dilation_not_compatible_with_padding_begin) { + const PartialShape arg_shape{5, -1, 8}; + const Shape kernel{9}; + const Strides window_movement_strides{1}; + const Shape pads_begin{10}; + const Shape pads_end{0}; + + const auto param = make_shared(element::f32, arg_shape); + + OV_EXPECT_THROW(const auto unused = make_shared(param, + window_movement_strides, + pads_begin, + pads_end, + kernel, + true, + op::RoundingType::FLOOR), + NodeValidationFailure, + HasSubstr("Kernel after dilation is sometimes entirely in the padding area for axis 0")); +} + +TEST(type_prop, avg_pool_kernel_dilation_not_compatible_with_padding_end) { + const PartialShape arg_shape{5, -1, 8}; + const Shape kernel{9}; + const Strides window_movement_strides{1}; + const Shape pads_begin{0}; + const Shape pads_end{10}; + + const auto param = make_shared(element::f32, arg_shape); + + OV_EXPECT_THROW(const auto unused = make_shared(param, + window_movement_strides, + pads_begin, + pads_end, + kernel, + true, + op::RoundingType::FLOOR), + NodeValidationFailure, + HasSubstr("Kernel after dilation is sometimes entirely in the padding area for axis 0")); +} diff --git a/src/core/tests/type_prop/max_pool.cpp b/src/core/tests/type_prop/max_pool.cpp index 0f2c88a14d1..5880cc426d9 100644 --- a/src/core/tests/type_prop/max_pool.cpp +++ b/src/core/tests/type_prop/max_pool.cpp @@ -8,9 +8,37 @@ using namespace std; using namespace ngraph; +using namespace testing; + +TEST(type_prop, max_pool_default_ctor) { + PartialShape arg_shape{1, 3, 32}; + set_shape_labels(arg_shape, 10); + const Strides strides{1}; + const Shape pads_begin{2}; + const Shape pads_end{2}; + const Shape kernel_shape{2}; + + auto arg = make_shared(element::f32, arg_shape); + + auto mp = make_shared(); + mp->set_argument(0, arg); + mp->set_pads_begin(pads_begin); + mp->set_pads_end(pads_end); + mp->set_kernel(kernel_shape); + mp->set_strides(strides); + mp->set_rounding_type(op::RoundingType::CEIL); + mp->set_auto_pad(op::PadType::VALID); + mp->validate_and_infer_types(); + + EXPECT_EQ(mp->get_output_partial_shape(0), PartialShape({1, 3, 31})); + EXPECT_THAT(get_shape_labels(mp->get_output_partial_shape(0)), ElementsAre(10, 11, ov::no_label)); + EXPECT_EQ(mp->get_pads_begin(), (Shape{0})); + EXPECT_EQ(mp->get_pads_end(), (Shape{0})); +} TEST(type_prop, max_pool_valid_auto_padding) { - const PartialShape arg_shape{1, 3, 32}; + PartialShape arg_shape{1, 3, {10, 32}}; + set_shape_labels(arg_shape, 10); const Strides strides{1}; const Shape pads_begin{2}; const Shape pads_end{2}; @@ -20,9 +48,10 @@ TEST(type_prop, max_pool_valid_auto_padding) { auto arg = make_shared(element::f32, arg_shape); auto mp = make_shared(arg, strides, pads_begin, pads_end, kernel_shape, rounding_mode, auto_pad); - ASSERT_TRUE(mp->get_output_partial_shape(0).same_scheme({1, 3, 31})); - ASSERT_EQ(mp->get_pads_begin(), (Shape{0})); - ASSERT_EQ(mp->get_pads_end(), (Shape{0})); + EXPECT_EQ(mp->get_output_partial_shape(0), PartialShape({1, 3, {9, 31}})); + EXPECT_THAT(get_shape_labels(mp->get_output_partial_shape(0)), ElementsAre(10, 11, ov::no_label)); + EXPECT_EQ(mp->get_pads_begin(), (Shape{0})); + EXPECT_EQ(mp->get_pads_end(), (Shape{0})); } TEST(type_prop, max_pool_1D_auto_padding) { @@ -37,9 +66,9 @@ TEST(type_prop, max_pool_1D_auto_padding) { auto arg = make_shared(element::f32, arg_shape); auto mp = make_shared(arg, strides, pads_begin, pads_end, kernel_shape, rounding_mode, auto_pad); - ASSERT_TRUE(mp->get_output_partial_shape(0).same_scheme({1, 3, 32})); - ASSERT_EQ(mp->get_pads_begin(), (Shape{1})); - ASSERT_EQ(mp->get_pads_end(), (Shape{0})); + EXPECT_EQ(mp->get_output_partial_shape(0), PartialShape({1, 3, 32})); + EXPECT_EQ(mp->get_pads_begin(), (Shape{1})); + EXPECT_EQ(mp->get_pads_end(), (Shape{0})); } TEST(type_prop, max_pool_2D_auto_padding) { @@ -54,9 +83,9 @@ TEST(type_prop, max_pool_2D_auto_padding) { auto arg = make_shared(element::f32, arg_shape); auto mp = make_shared(arg, strides, pads_begin, pads_end, kernel_shape, rounding_mode, auto_pad); - ASSERT_TRUE(mp->get_output_partial_shape(0).same_scheme({1, 3, 32, 32})); - ASSERT_EQ(mp->get_pads_begin(), (Shape{1, 1})); - ASSERT_EQ(mp->get_pads_end(), (Shape{0, 0})); + EXPECT_EQ(mp->get_output_partial_shape(0), PartialShape({1, 3, 32, 32})); + EXPECT_EQ(mp->get_pads_begin(), (Shape{1, 1})); + EXPECT_EQ(mp->get_pads_end(), (Shape{0, 0})); } TEST(type_prop, max_pool_auto_padding_1D_nc_dims_dynamic_same_lower) { @@ -71,9 +100,9 @@ TEST(type_prop, max_pool_auto_padding_1D_nc_dims_dynamic_same_lower) { auto arg = make_shared(element::f32, arg_shape); auto mp = make_shared(arg, strides, pads_begin, pads_end, kernel_shape, rounding_mode, auto_pad); - ASSERT_TRUE(mp->get_output_partial_shape(0).same_scheme({Dimension::dynamic(), 32, 32})); - ASSERT_EQ(mp->get_pads_begin(), (Shape{1})); - ASSERT_EQ(mp->get_pads_end(), (Shape{0})); + EXPECT_EQ(mp->get_output_partial_shape(0), PartialShape({Dimension::dynamic(), 32, 32})); + EXPECT_EQ(mp->get_pads_begin(), (Shape{1})); + EXPECT_EQ(mp->get_pads_end(), (Shape{0})); } TEST(type_prop, max_pool_auto_padding_2D_nc_dims_dynamic_same_lower) { @@ -88,13 +117,14 @@ TEST(type_prop, max_pool_auto_padding_2D_nc_dims_dynamic_same_lower) { auto arg = make_shared(element::f32, arg_shape); auto mp = make_shared(arg, strides, pads_begin, pads_end, kernel_shape, rounding_mode, auto_pad); - ASSERT_TRUE(mp->get_output_partial_shape(0).same_scheme({Dimension::dynamic(), Dimension::dynamic(), 32, 32})); - ASSERT_EQ(mp->get_pads_begin(), (Shape{1, 1})); - ASSERT_EQ(mp->get_pads_end(), (Shape{0, 0})); + EXPECT_EQ(mp->get_output_partial_shape(0), PartialShape({Dimension::dynamic(), Dimension::dynamic(), 32, 32})); + EXPECT_EQ(mp->get_pads_begin(), (Shape{1, 1})); + EXPECT_EQ(mp->get_pads_end(), (Shape{0, 0})); } TEST(type_prop, max_pool_auto_padding_nc_dims_dynamic_same_upper) { - const PartialShape arg_shape{Dimension::dynamic(), Dimension::dynamic(), 32, 32}; + PartialShape arg_shape{Dimension::dynamic(), Dimension::dynamic(), 32, 32}; + set_shape_labels(arg_shape, 10); const Strides strides{1, 1}; const Shape pads_begin{0, 0}; const Shape pads_end{0, 0}; @@ -105,9 +135,29 @@ TEST(type_prop, max_pool_auto_padding_nc_dims_dynamic_same_upper) { auto arg = make_shared(element::f32, arg_shape); auto mp = make_shared(arg, strides, pads_begin, pads_end, kernel_shape, rounding_mode, auto_pad); - ASSERT_TRUE(mp->get_output_partial_shape(0).same_scheme({Dimension::dynamic(), Dimension::dynamic(), 32, 32})); - ASSERT_EQ(mp->get_pads_begin(), (Shape{0, 0})); - ASSERT_EQ(mp->get_pads_end(), (Shape{1, 1})); + EXPECT_EQ(mp->get_output_partial_shape(0), PartialShape({Dimension::dynamic(), Dimension::dynamic(), 32, 32})); + EXPECT_THAT(get_shape_labels(mp->get_output_partial_shape(0)), ElementsAre(10, 11, ov::no_label, ov::no_label)); + EXPECT_EQ(mp->get_pads_begin(), (Shape{0, 0})); + EXPECT_EQ(mp->get_pads_end(), (Shape{1, 1})); +} + +TEST(type_prop, max_pool_auto_padding_interval_dims_same_upper) { + PartialShape arg_shape{{1, 2}, {2, 3}, {16, 32}, {11, 32}}; + set_shape_labels(arg_shape, 10); + const Strides strides{1, 1}; + const Shape pads_begin{0, 0}; + const Shape pads_end{0, 0}; + const Shape kernel_shape{2, 2}; + const auto rounding_mode = op::RoundingType::FLOOR; + const auto auto_pad = op::PadType::SAME_UPPER; + + auto arg = make_shared(element::f32, arg_shape); + auto mp = make_shared(arg, strides, pads_begin, pads_end, kernel_shape, rounding_mode, auto_pad); + + EXPECT_EQ(mp->get_output_partial_shape(0), PartialShape({{1, 2}, {2, 3}, -1, -1})); + EXPECT_THAT(get_shape_labels(mp->get_output_partial_shape(0)), ElementsAre(10, 11, ov::no_label, ov::no_label)); + EXPECT_EQ(mp->get_pads_begin(), (Shape{0, 0})); + EXPECT_EQ(mp->get_pads_end(), (Shape{0, 0})); } TEST(type_prop, max_pool_auto_padding_spatial_dims_dynamic) { @@ -122,9 +172,9 @@ TEST(type_prop, max_pool_auto_padding_spatial_dims_dynamic) { auto arg = make_shared(element::f32, arg_shape); auto mp = make_shared(arg, strides, pads_begin, pads_end, kernel_shape, rounding_mode, auto_pad); - ASSERT_TRUE(mp->get_output_partial_shape(0).same_scheme({1, 3, 32, Dimension::dynamic()})); - ASSERT_EQ(mp->get_pads_begin(), (Shape{1, 0})); - ASSERT_EQ(mp->get_pads_end(), (Shape{0, 0})); + EXPECT_EQ(mp->get_output_partial_shape(0), PartialShape({1, 3, 32, Dimension::dynamic()})); + EXPECT_EQ(mp->get_pads_begin(), (Shape{1, 0})); + EXPECT_EQ(mp->get_pads_end(), (Shape{0, 0})); } TEST(type_prop, max_pool_default_values) { @@ -137,8 +187,8 @@ TEST(type_prop, max_pool_default_values) { auto arg = make_shared(element::f32, arg_shape); auto mp = make_shared(arg, strides, pads_begin, pads_end, kernel_shape); - ASSERT_EQ(mp->get_rounding_type(), op::RoundingType::FLOOR); - ASSERT_EQ(mp->get_auto_pad(), op::PadType::EXPLICIT); + EXPECT_EQ(mp->get_rounding_type(), op::RoundingType::FLOOR); + EXPECT_EQ(mp->get_auto_pad(), op::PadType::EXPLICIT); } TEST(type_prop, max_pool_v8_3D_no_dilations) { @@ -153,8 +203,8 @@ TEST(type_prop, max_pool_v8_3D_no_dilations) { const auto mp = make_shared(arg, strides, dilations, pads_begin, pads_end, kernel_shape); const auto expected_output_shape = PartialShape({1, 7, 11}); - ASSERT_TRUE(mp->get_output_partial_shape(0).same_scheme(expected_output_shape)); - ASSERT_TRUE(mp->get_output_partial_shape(1).same_scheme(expected_output_shape)); + EXPECT_EQ(mp->get_output_partial_shape(0), expected_output_shape); + EXPECT_EQ(mp->get_output_partial_shape(1), expected_output_shape); } TEST(type_prop, max_pool_v8_3D_with_dilations) { @@ -169,8 +219,8 @@ TEST(type_prop, max_pool_v8_3D_with_dilations) { const auto mp = make_shared(arg, strides, dilations, pads_begin, pads_end, kernel_shape); const auto expected_output_shape = PartialShape({1, 7, 9}); - ASSERT_TRUE(mp->get_output_partial_shape(0).same_scheme(expected_output_shape)); - ASSERT_TRUE(mp->get_output_partial_shape(1).same_scheme(expected_output_shape)); + EXPECT_EQ(mp->get_output_partial_shape(0), expected_output_shape); + EXPECT_EQ(mp->get_output_partial_shape(1), expected_output_shape); } TEST(type_prop, max_pool_v8_3D_with_dilations_and_padding) { @@ -185,8 +235,8 @@ TEST(type_prop, max_pool_v8_3D_with_dilations_and_padding) { const auto mp = make_shared(arg, strides, dilations, pads_begin, pads_end, kernel_shape); const auto expected_output_shape = PartialShape({1, 7, 12}); - ASSERT_TRUE(mp->get_output_partial_shape(0).same_scheme(expected_output_shape)); - ASSERT_TRUE(mp->get_output_partial_shape(1).same_scheme(expected_output_shape)); + EXPECT_EQ(mp->get_output_partial_shape(0), expected_output_shape); + EXPECT_EQ(mp->get_output_partial_shape(1), expected_output_shape); } TEST(type_prop, max_pool_v8_4D_no_dilations) { @@ -201,8 +251,8 @@ TEST(type_prop, max_pool_v8_4D_no_dilations) { const auto mp = make_shared(arg, strides, dilations, pads_begin, pads_end, kernel_shape); const auto expected_output_shape = PartialShape({1, 3, 12, 12}); - ASSERT_TRUE(mp->get_output_partial_shape(0).same_scheme(expected_output_shape)); - ASSERT_TRUE(mp->get_output_partial_shape(1).same_scheme(expected_output_shape)); + EXPECT_EQ(mp->get_output_partial_shape(0), expected_output_shape); + EXPECT_EQ(mp->get_output_partial_shape(1), expected_output_shape); } TEST(type_prop, max_pool_v8_4D_with_dilations) { @@ -217,6 +267,51 @@ TEST(type_prop, max_pool_v8_4D_with_dilations) { const auto mp = make_shared(arg, strides, dilations, pads_begin, pads_end, kernel_shape); const auto expected_output_shape = PartialShape({1, 3, 11, 10}); - ASSERT_TRUE(mp->get_output_partial_shape(0).same_scheme(expected_output_shape)); - ASSERT_TRUE(mp->get_output_partial_shape(1).same_scheme(expected_output_shape)); + EXPECT_EQ(mp->get_output_partial_shape(0), expected_output_shape); + EXPECT_EQ(mp->get_output_partial_shape(1), expected_output_shape); +} + +TEST(type_prop, max_pool_v8_4D_interval_dims_with_dilations) { + PartialShape arg_shape{{2, 3}, {1, 3}, {2, 13}, {6, 13}}; + set_shape_labels(arg_shape, 10); + const Strides strides{1, 1}; + const Strides dilations{2, 3}; + const Shape pads_begin{0, 0}; + const Shape pads_end{0, 0}; + const Shape kernel_shape{2, 2}; + + const auto arg = make_shared(element::f32, arg_shape); + const auto mp = make_shared(arg, strides, dilations, pads_begin, pads_end, kernel_shape); + + const auto expected_output_shape = PartialShape({{2, 3}, {1, 3}, {1, 11}, {3, 10}}); + EXPECT_EQ(mp->get_output_partial_shape(0), expected_output_shape); + EXPECT_EQ(mp->get_output_partial_shape(1), expected_output_shape); + EXPECT_THAT(get_shape_labels(mp->get_output_partial_shape(0)), ElementsAre(10, 11, ov::no_label, ov::no_label)); +} + +TEST(type_prop, max_pool_v8_4D_with_dilations_and_auto_pad_same_upper) { + const PartialShape arg_shape{1, 3, 13, 13}; + const Strides strides{1, 1}; + const Strides dilations{2, 3}; + const Shape pads_begin{0, 0}; + const Shape pads_end{0, 0}; + const Shape kernel_shape{3, 3}; + const auto rounding_mode = op::RoundingType::FLOOR; + const auto auto_pad = op::PadType::SAME_UPPER; + + const auto arg = make_shared(element::f32, arg_shape); + const auto mp = make_shared(arg, + strides, + dilations, + pads_begin, + pads_end, + kernel_shape, + rounding_mode, + auto_pad); + + const auto expected_output_shape = PartialShape({1, 3, 13, 13}); + EXPECT_EQ(mp->get_output_partial_shape(0), expected_output_shape); + EXPECT_EQ(mp->get_output_partial_shape(1), expected_output_shape); + EXPECT_EQ(mp->get_pads_begin(), (Shape{2, 3})); + EXPECT_EQ(mp->get_pads_end(), (Shape{2, 3})); } diff --git a/src/plugins/intel_cpu/src/utils/shape_inference/shape_inference.cpp b/src/plugins/intel_cpu/src/utils/shape_inference/shape_inference.cpp index f75f02a1cfd..1c788b09052 100644 --- a/src/plugins/intel_cpu/src/utils/shape_inference/shape_inference.cpp +++ b/src/plugins/intel_cpu/src/utils/shape_inference/shape_inference.cpp @@ -8,9 +8,12 @@ #include #include +#include "adaptive_avg_pool_shape_inference.hpp" +#include "adaptive_max_pool_shape_inference.hpp" #include "assign_shape_inference.hpp" #include "augru_cell_shape_inference.hpp" #include "augru_sequence_shape_inference.hpp" +#include "avg_pool_shape_inference.hpp" #include "batch_to_space_shape_inference.hpp" #include "broadcast_shape_inference.hpp" #include "bucketize_shape_inference.hpp" @@ -45,6 +48,7 @@ #include "irdft_shape_inference.hpp" #include "lstm_cell_shape_inference.hpp" #include "matmul_shape_inference.hpp" +#include "max_pool_shape_inference.hpp" #include "one_hot_shape_inference.hpp" #include "pad_shape_inference.hpp" #include "proposal_shape_inference.hpp" @@ -255,11 +259,7 @@ static inline ov::CoordinateDiff convertPadding(const ov::CoordinateDiff& newPad } static inline ov::CoordinateDiff convertPadding(const ov::Shape& newPads) { - std::vector pads(newPads.size()); - for (int i = 0; i < newPads.size(); i++) { - pads[i] = static_cast(newPads[i]); - } - return pads; + return {newPads.begin(), newPads.end()}; } template @@ -361,13 +361,48 @@ protected: ov::CoordinateDiff pads_begin, pads_end; }; +template +class ShapeInferBaseWithPadding : public entryBase { +public: + ShapeInferBaseWithPadding(std::shared_ptr node) : entryBase{std::move(node)}, m_pads_begin{}, m_pads_end{} {} + + IShapeInferCommon::Result infer(const std::vector& input_shapes, + const std::map& constant_data) override { + auto out_shapes = shape_infer(static_cast(node.get()), input_shapes); + on_infer_exit(); + return {std::move(out_shapes), ShapeInferStatus::success}; + } + + const ov::CoordinateDiff& get_pads_begin() override { + return m_pads_begin; + } + + const ov::CoordinateDiff& get_pads_end() override { + return m_pads_end; + } + +protected: + void on_infer_exit() { + auto op = static_cast(node.get()); + m_pads_begin = convertPadding(op->get_pads_begin()); + m_pads_end = convertPadding(op->get_pads_end()); + } + + ov::CoordinateDiff m_pads_begin, m_pads_end; +}; + +/** + * @brief Base shape inference object implementing the IStaticShapeInfer without padding support + * + * @tparam TOp Type of operator. + */ template class ShapeInferBase : public IStaticShapeInfer { public: using iface_type = IStaticShapeInfer; virtual ~ShapeInferBase() = default; - ShapeInferBase(std::shared_ptr node) : m_node{node} { + ShapeInferBase(std::shared_ptr node) : m_input_ranks{}, m_node{node} { static_assert(std::is_same::value, "Rank type not match to input_ranks type."); for (size_t i = 0; i < node->get_input_size(); ++i) { const auto& shape = node->get_input_partial_shape(i); @@ -508,8 +543,10 @@ const IShapeInferCommonFactory::TRegistry IShapeInferCommonFactory::registry{ _OV_OP_NON_TEMPLATE_SHAPE_INFER_REG(Selu, entryFirstPassthrough), _OV_OP_NON_TEMPLATE_SHAPE_INFER_REG(Softmax, entryCopy), _OV_OP_NON_TEMPLATE_SHAPE_INFER_REG(Swish, entryFirstPassthrough), + _OV_OP_SHAPE_INFER_REG(AdaptiveAvgPool, entryIOC), + _OV_OP_SHAPE_INFER_REG(AdaptiveMaxPool, entryIOC), _OV_OP_SHAPE_INFER_REG(Assign, entryIO), - _OV_OP_SHAPE_INFER_REG(AvgPool, entryFallbackWithPadding), + _OV_OP_SHAPE_INFER_REG(AvgPool, ShapeInferBaseWithPadding), _OV_OP_SHAPE_INFER_REG(BatchToSpace, entryIOC), _OV_OP_SHAPE_INFER_REG(Broadcast, entryIOC), _OV_OP_SHAPE_INFER_REG(Bucketize, entryIO), @@ -544,7 +581,7 @@ const IShapeInferCommonFactory::TRegistry IShapeInferCommonFactory::registry{ _OV_OP_SHAPE_INFER_REG(IRDFT, entryIOC), _OV_OP_SHAPE_INFER_REG(LSTMCell, entryIO), _OV_OP_SHAPE_INFER_REG(MatMul, entryIO), - _OV_OP_SHAPE_INFER_REG(MaxPool, entryFallbackWithPadding), + _OV_OP_SHAPE_INFER_REG(MaxPool, ShapeInferBaseWithPadding), _OV_OP_SHAPE_INFER_REG(OneHot, entryIOC), _OV_OP_SHAPE_INFER_REG(ov::op::internal::AUGRUCell, entryIO), _OV_OP_SHAPE_INFER_REG(ov::op::internal::AUGRUSequence, entryIO), @@ -607,7 +644,7 @@ const IShapeInferCommonFactory::TRegistry IShapeInferCommonFactory::registry{ _OV_OP_SHAPE_INFER_REG(opset1::DetectionOutput, entryIO), _OV_OP_SHAPE_INFER_REG(opset1::Interpolate, entryIOC), _OV_OP_SHAPE_INFER_REG(opset1::LSTMCell, entryIO), - _OV_OP_SHAPE_INFER_REG(opset1::MaxPool, entryFallbackWithPadding), + _OV_OP_SHAPE_INFER_REG(opset1::MaxPool, ShapeInferBaseWithPadding), _OV_OP_SHAPE_INFER_REG(opset1::Proposal, entryIO), _OV_OP_SHAPE_INFER_REG(opset1::Range, entryIOC), _OV_OP_SHAPE_INFER_REG(opset1::ShapeOf, entryIO), diff --git a/src/plugins/intel_cpu/src/utils/shape_inference/static_dimension.hpp b/src/plugins/intel_cpu/src/utils/shape_inference/static_dimension.hpp index 8db3c3566f8..1736a8c938c 100644 --- a/src/plugins/intel_cpu/src/utils/shape_inference/static_dimension.hpp +++ b/src/plugins/intel_cpu/src/utils/shape_inference/static_dimension.hpp @@ -42,8 +42,12 @@ public: bool operator==(const StaticDimension& dimension) const; bool operator!=(const StaticDimension& dimension) const; - static bool is_static() { return true; } - static bool is_dynamic() { return false; } + static constexpr bool is_static() { + return true; + } + static constexpr bool is_dynamic() { + return false; + } value_type get_length() const; value_type get_min_length() const; diff --git a/src/plugins/intel_cpu/tests/unit/shape_inference_test/adaptive_avg_pool_shape_inference_test.cpp b/src/plugins/intel_cpu/tests/unit/shape_inference_test/adaptive_avg_pool_shape_inference_test.cpp new file mode 100644 index 00000000000..0059829fe39 --- /dev/null +++ b/src/plugins/intel_cpu/tests/unit/shape_inference_test/adaptive_avg_pool_shape_inference_test.cpp @@ -0,0 +1,79 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include "common_test_utils/test_assertions.hpp" +#include "openvino/opsets/opset10.hpp" +#include "utils.hpp" + +using namespace ov; +using namespace ov::intel_cpu; +using namespace testing; + +class AdaptiveAvgPoolV8StaticShapeInferenceTest : public OpStaticShapeInferenceTest { +protected: + void SetUp() override { + output_shapes.resize(1); + } +}; + +TEST_F(AdaptiveAvgPoolV8StaticShapeInferenceTest, default_ctor) { + int32_t spatial_dims[] = {10, 20}; + const std::map const_data{ + {1, std::make_shared(element::i32, ov::Shape{2}, spatial_dims)}}; + + op = make_op(); + input_shapes = ShapeVector{{1, 3, 1, 2}, {2}}; + shape_inference(op.get(), input_shapes, output_shapes, const_data); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({1, 3, 10, 20})); +} + +TEST_F(AdaptiveAvgPoolV8StaticShapeInferenceTest, out_spatial_dims_as_constant) { + const auto data = std::make_shared(element::f64, PartialShape{-1, -1, -2}); + const auto out_shape = op::v0::Constant::create(element::i64, ov::Shape{1}, {17}); + + op = make_op(data, out_shape); + + input_shapes = ShapeVector{{1, 3, 10}, {1}}; + shape_inference(op.get(), input_shapes, output_shapes); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({1, 3, 17})); +} + +TEST_F(AdaptiveAvgPoolV8StaticShapeInferenceTest, out_spatial_dims_in_const_map) { + const auto data = std::make_shared(element::f64, PartialShape::dynamic()); + const auto out_shape = std::make_shared(element::i32, PartialShape::dynamic()); + + op = make_op(data, out_shape); + + int32_t spatial_dims[] = {9, 8, 7}; + const std::map const_data{ + {1, std::make_shared(element::i32, ov::Shape{3}, spatial_dims)}}; + + input_shapes = ShapeVector{{1, 3, 10, 2, 4}, {3}}; + shape_inference(op.get(), input_shapes, output_shapes, const_data); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({1, 3, 9, 8, 7})); +} + +TEST_F(AdaptiveAvgPoolV8StaticShapeInferenceTest, out_spatial_dims_in_const_map_has_wrong_length) { + const auto data = std::make_shared(element::f64, PartialShape::dynamic()); + const auto out_shape = std::make_shared(element::i32, PartialShape::dynamic()); + + op = make_op(data, out_shape); + + int32_t spatial_dims[] = {9, 8}; + const std::map const_data{ + {1, std::make_shared(element::i32, ov::Shape{2}, spatial_dims)}}; + + input_shapes = ShapeVector{{1, 3, 10, 2, 4}, {3}}; + OV_EXPECT_THROW(shape_inference(op.get(), input_shapes, output_shapes, const_data), + ov::NodeValidationFailure, + HasSubstr("Number of spatial dimensions is not compatible with input data rank")); +} diff --git a/src/plugins/intel_cpu/tests/unit/shape_inference_test/adaptive_max_pool_shape_inference_test.cpp b/src/plugins/intel_cpu/tests/unit/shape_inference_test/adaptive_max_pool_shape_inference_test.cpp new file mode 100644 index 00000000000..a9619b747b0 --- /dev/null +++ b/src/plugins/intel_cpu/tests/unit/shape_inference_test/adaptive_max_pool_shape_inference_test.cpp @@ -0,0 +1,79 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include "common_test_utils/test_assertions.hpp" +#include "openvino/opsets/opset10.hpp" +#include "utils.hpp" + +using namespace ov; +using namespace ov::intel_cpu; +using namespace testing; + +class AdaptiveMaxPoolV8StaticShapeInferenceTest : public OpStaticShapeInferenceTest { +protected: + void SetUp() override { + output_shapes.resize(2); + } +}; + +TEST_F(AdaptiveMaxPoolV8StaticShapeInferenceTest, default_ctor) { + int32_t spatial_dims[] = {10, 20}; + const std::map const_data{ + {1, std::make_shared(element::i32, ov::Shape{2}, spatial_dims)}}; + + op = make_op(); + input_shapes = ShapeVector{{1, 3, 1, 2}, {2}}; + shape_inference(op.get(), input_shapes, output_shapes, const_data); + + EXPECT_EQ(output_shapes.size(), 2); + EXPECT_THAT(output_shapes, Each(StaticShape({1, 3, 10, 20}))); +} + +TEST_F(AdaptiveMaxPoolV8StaticShapeInferenceTest, out_spatial_dims_as_constant) { + const auto data = std::make_shared(element::f64, PartialShape{-1, -1, -2}); + const auto out_shape = op::v0::Constant::create(element::i64, ov::Shape{1}, {17}); + + op = make_op(data, out_shape); + + input_shapes = ShapeVector{{1, 3, 10}, {1}}; + shape_inference(op.get(), input_shapes, output_shapes); + + EXPECT_EQ(output_shapes.size(), 2); + EXPECT_THAT(output_shapes, Each(StaticShape({1, 3, 17}))); +} + +TEST_F(AdaptiveMaxPoolV8StaticShapeInferenceTest, out_spatial_dims_in_const_map) { + const auto data = std::make_shared(element::f64, PartialShape::dynamic()); + const auto out_shape = std::make_shared(element::i32, PartialShape::dynamic()); + + op = make_op(data, out_shape); + + int32_t spatial_dims[] = {9, 8, 7}; + const std::map const_data{ + {1, std::make_shared(element::i32, ov::Shape{3}, spatial_dims)}}; + + input_shapes = ShapeVector{{1, 3, 10, 2, 4}, {3}}; + shape_inference(op.get(), input_shapes, output_shapes, const_data); + + EXPECT_EQ(output_shapes.size(), 2); + EXPECT_THAT(output_shapes, Each(StaticShape({1, 3, 9, 8, 7}))); +} + +TEST_F(AdaptiveMaxPoolV8StaticShapeInferenceTest, out_spatial_dims_in_const_map_has_wrong_length) { + const auto data = std::make_shared(element::f64, PartialShape::dynamic()); + const auto out_shape = std::make_shared(element::i32, PartialShape::dynamic()); + + op = make_op(data, out_shape); + + int32_t spatial_dims[] = {9, 8}; + const std::map const_data{ + {1, std::make_shared(element::i32, ov::Shape{2}, spatial_dims)}}; + + input_shapes = ShapeVector{{1, 3, 10, 2, 4}, {3}}; + OV_EXPECT_THROW(shape_inference(op.get(), input_shapes, output_shapes, const_data), + ov::NodeValidationFailure, + HasSubstr("Number of spatial dimensions is not compatible with input data rank")); +} diff --git a/src/plugins/intel_cpu/tests/unit/shape_inference_test/avg_pool_shape_inference_test.cpp b/src/plugins/intel_cpu/tests/unit/shape_inference_test/avg_pool_shape_inference_test.cpp new file mode 100644 index 00000000000..abb8dd8f6f3 --- /dev/null +++ b/src/plugins/intel_cpu/tests/unit/shape_inference_test/avg_pool_shape_inference_test.cpp @@ -0,0 +1,127 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include "common_test_utils/test_assertions.hpp" +#include "openvino/opsets/opset10.hpp" +#include "utils.hpp" + +using namespace ov; +using namespace ov::intel_cpu; +using namespace testing; + +class AvgPoolV1StaticShapeInferenceTest : public OpStaticShapeInferenceTest { +protected: + void SetUp() override { + output_shapes.resize(1); + } +}; + +TEST_F(AvgPoolV1StaticShapeInferenceTest, default_ctor) { + op = make_op(); + op->set_strides({1, 1}); + op->set_pads_begin({2, 2}); + op->set_pads_end({2, 1}); + op->set_kernel({3, 2}); + op->set_rounding_type(op::RoundingType::FLOOR); + op->set_auto_pad(op::PadType::VALID); + + input_shapes = ShapeVector{{1, 3, 10, 12}}; + auto shape_infer = make_shape_inference(op); + output_shapes = shape_infer->infer(input_shapes, {}).shapes; + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({1, 3, 8, 11})); + EXPECT_EQ(shape_infer->get_pads_begin(), CoordinateDiff({0, 0})); + EXPECT_EQ(shape_infer->get_pads_end(), CoordinateDiff({0, 0})); +} + +TEST_F(AvgPoolV1StaticShapeInferenceTest, no_auto_pad_round_floor) { + const auto data = std::make_shared(element::f64, PartialShape{-1, -1, -1, -1}); + + const Strides strides{1, 1}; + const Shape pads_begin{2, 2}; + const Shape pads_end{2, 1}; + const Shape kernel_shape{3, 2}; + const auto rounding_mode = op::RoundingType::FLOOR; + const auto pad_type = op::PadType::EXPLICIT; + + op = make_op(data, strides, pads_begin, pads_end, kernel_shape, false, rounding_mode, pad_type); + + input_shapes = ShapeVector{{1, 3, 10, 12}}; + auto shape_infer = make_shape_inference(op); + output_shapes = shape_infer->infer(input_shapes, {}).shapes; + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({1, 3, 12, 14})); + EXPECT_EQ(shape_infer->get_pads_begin(), CoordinateDiff({2, 2})); + EXPECT_EQ(shape_infer->get_pads_end(), CoordinateDiff({2, 1})); +} + +TEST_F(AvgPoolV1StaticShapeInferenceTest, auto_padding_same_lower_round_ceil) { + const auto data = std::make_shared(element::f64, PartialShape::dynamic()); + + const Strides strides{1, 3, 2}; + const Shape pads_begin{2, 2, 1}; + const Shape pads_end{2, 1, 10}; + const Shape kernel_shape{5, 5, 5}; + const auto rounding_mode = op::RoundingType::CEIL; + const auto pad_type = op::PadType::SAME_LOWER; + + op = make_op(data, strides, pads_begin, pads_end, kernel_shape, false, rounding_mode, pad_type); + + input_shapes = ShapeVector{{1, 3, 10, 12, 20}}; + auto shape_infer = make_shape_inference(op); + output_shapes = shape_infer->infer(input_shapes, {}).shapes; + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({1, 3, 10, 4, 10})); + EXPECT_EQ(shape_infer->get_pads_begin(), CoordinateDiff({2, 1, 2})); + EXPECT_EQ(shape_infer->get_pads_end(), CoordinateDiff({2, 1, 1})); +} + +TEST_F(AvgPoolV1StaticShapeInferenceTest, auto_padding_same_upper_round_floor_exclude_pad) { + const auto data = std::make_shared(element::f64, PartialShape::dynamic()); + + const Strides strides{1, 3, 2}; + const Shape pads_begin{2, 2, 1}; + const Shape pads_end{2, 1, 10}; + const Shape kernel_shape{5, 5, 5}; + const auto rounding_mode = op::RoundingType::FLOOR; + const auto pad_type = op::PadType::SAME_UPPER; + + op = make_op(data, strides, pads_begin, pads_end, kernel_shape, true, rounding_mode, pad_type); + + input_shapes = ShapeVector{{1, 3, 10, 12, 20}}; + auto shape_infer = make_shape_inference(op); + output_shapes = shape_infer->infer(input_shapes, {}).shapes; + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({1, 3, 10, 4, 10})); + EXPECT_EQ(shape_infer->get_pads_begin(), CoordinateDiff({2, 1, 1})); + EXPECT_EQ(shape_infer->get_pads_end(), CoordinateDiff({2, 1, 2})); +} + +TEST_F(AvgPoolV1StaticShapeInferenceTest, 5d_auto_padding_same_upper_round_floor) { + const auto data = std::make_shared(element::f64, PartialShape::dynamic()); + + const Strides strides{1, 1, 1}; + const Shape pads_begin{0, 0, 0}; + const Shape pads_end{0, 0, 0}; + const Shape kernel_shape{2, 2, 2}; + const auto rounding_mode = op::RoundingType::FLOOR; + const auto pad_type = op::PadType::SAME_UPPER; + + op = make_op(data, strides, pads_begin, pads_end, kernel_shape, true, rounding_mode, pad_type); + + input_shapes = ShapeVector{{32, 32, 2, 2, 4}}; + auto shape_infer = make_shape_inference(op); + output_shapes = shape_infer->infer(input_shapes, {}).shapes; + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({32, 32, 2, 2, 4})); + EXPECT_EQ(shape_infer->get_pads_begin(), CoordinateDiff({0, 0, 0})); + EXPECT_EQ(shape_infer->get_pads_end(), CoordinateDiff({1, 1, 1})); +} diff --git a/src/plugins/intel_cpu/tests/unit/shape_inference_test/max_pool_shape_inference_test.cpp b/src/plugins/intel_cpu/tests/unit/shape_inference_test/max_pool_shape_inference_test.cpp new file mode 100644 index 00000000000..bd3e6c8e7c9 --- /dev/null +++ b/src/plugins/intel_cpu/tests/unit/shape_inference_test/max_pool_shape_inference_test.cpp @@ -0,0 +1,152 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include "common_test_utils/test_assertions.hpp" +#include "openvino/opsets/opset10.hpp" +#include "utils.hpp" + +using namespace ov; +using namespace ov::intel_cpu; +using namespace testing; + +class MaxPoolV1StaticShapeInferenceTest : public OpStaticShapeInferenceTest { +protected: + void SetUp() override { + output_shapes.resize(1); + } +}; + +TEST_F(MaxPoolV1StaticShapeInferenceTest, default_ctor) { + op = make_op(); + op->set_strides({1, 1}); + op->set_pads_begin({2, 2}); + op->set_pads_end({2, 1}); + op->set_kernel({3, 2}); + op->set_rounding_type(op::RoundingType::FLOOR); + op->set_auto_pad(op::PadType::VALID); + + input_shapes = ShapeVector{{1, 3, 10, 12}}; + auto shape_infer = make_shape_inference(op); + output_shapes = shape_infer->infer(input_shapes, {}).shapes; + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({1, 3, 8, 11})); + EXPECT_EQ(shape_infer->get_pads_begin(), CoordinateDiff({0, 0})); + EXPECT_EQ(shape_infer->get_pads_end(), CoordinateDiff({0, 0})); +} + +TEST_F(MaxPoolV1StaticShapeInferenceTest, no_auto_pad_round_floor) { + const auto data = std::make_shared(element::f64, PartialShape{-1, -1, -1, -1}); + + const Strides strides{1, 1}; + const Shape pads_begin{2, 2}; + const Shape pads_end{2, 1}; + const Shape kernel_shape{3, 2}; + const auto rounding_mode = op::RoundingType::FLOOR; + const auto pad_type = op::PadType::EXPLICIT; + + op = make_op(data, strides, pads_begin, pads_end, kernel_shape, rounding_mode, pad_type); + + input_shapes = ShapeVector{{1, 3, 10, 12}}; + auto shape_infer = make_shape_inference(op); + output_shapes = shape_infer->infer(input_shapes, {}).shapes; + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({1, 3, 12, 14})); + EXPECT_EQ(shape_infer->get_pads_begin(), CoordinateDiff({2, 2})); + EXPECT_EQ(shape_infer->get_pads_end(), CoordinateDiff({2, 1})); +} + +TEST_F(MaxPoolV1StaticShapeInferenceTest, auto_padding_same_lower_round_ceil) { + const auto data = std::make_shared(element::f64, PartialShape::dynamic()); + + const Strides strides{1, 3, 2}; + const Shape pads_begin{2, 2, 1}; + const Shape pads_end{2, 1, 10}; + const Shape kernel_shape{5, 5, 5}; + const auto rounding_mode = op::RoundingType::CEIL; + const auto pad_type = op::PadType::SAME_LOWER; + + op = make_op(data, strides, pads_begin, pads_end, kernel_shape, rounding_mode, pad_type); + + input_shapes = ShapeVector{{1, 3, 10, 12, 20}}; + auto shape_infer = make_shape_inference(op); + output_shapes = shape_infer->infer(input_shapes, {}).shapes; + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({1, 3, 10, 4, 10})); + EXPECT_EQ(shape_infer->get_pads_begin(), CoordinateDiff({2, 1, 2})); + EXPECT_EQ(shape_infer->get_pads_end(), CoordinateDiff({2, 1, 1})); +} + +class MaxPoolV8StaticShapeInferenceTest : public OpStaticShapeInferenceTest { +protected: + void SetUp() override { + output_shapes.resize(2); + } +}; + +TEST_F(MaxPoolV8StaticShapeInferenceTest, default_ctor) { + op = make_op(); + op->set_strides({1, 1}); + op->set_pads_begin({2, 2}); + op->set_pads_end({2, 1}); + op->set_kernel({3, 2}); + op->set_dilations({2, 1}); + op->set_rounding_type(op::RoundingType::FLOOR); + op->set_auto_pad(op::PadType::VALID); + + input_shapes = ShapeVector{{1, 3, 10, 12}}; + auto shape_infer = make_shape_inference(op); + output_shapes = shape_infer->infer(input_shapes, {}).shapes; + + EXPECT_EQ(output_shapes.size(), 2); + EXPECT_THAT(output_shapes, Each(StaticShape({1, 3, 6, 11}))); + EXPECT_EQ(shape_infer->get_pads_begin(), CoordinateDiff({0, 0})); + EXPECT_EQ(shape_infer->get_pads_end(), CoordinateDiff({0, 0})); +} + +TEST_F(MaxPoolV8StaticShapeInferenceTest, no_dilation) { + const auto data = std::make_shared(element::f64, PartialShape{-1, -1, -1, -1}); + + const Strides strides{1, 1}; + const Strides dilations{1, 1}; + const Shape pads_begin{1, 1}; + const Shape pads_end{0, 0}; + const Shape kernel_shape{2, 2}; + + op = make_op(data, strides, dilations, pads_begin, pads_end, kernel_shape); + + input_shapes = ShapeVector{{2, 3, 13, 13}}; + auto shape_infer = make_shape_inference(op); + output_shapes = shape_infer->infer(input_shapes, {}).shapes; + + EXPECT_EQ(output_shapes.size(), 2); + EXPECT_THAT(output_shapes, Each(StaticShape({2, 3, 13, 13}))); + EXPECT_EQ(shape_infer->get_pads_begin(), CoordinateDiff({1, 1})); + EXPECT_EQ(shape_infer->get_pads_end(), CoordinateDiff({0, 0})); +} + +TEST_F(MaxPoolV8StaticShapeInferenceTest, with_dilations) { + const auto data = std::make_shared(element::f64, PartialShape::dynamic()); + + const Strides strides{1, 1}; + const Strides dilations{2, 3}; + const Shape pads_begin{0, 0}; + const Shape pads_end{1, 1}; + const Shape kernel_shape{2, 2}; + + op = make_op(data, strides, dilations, pads_begin, pads_end, kernel_shape); + + input_shapes = ShapeVector{{2, 4, 13, 13}}; + auto shape_infer = make_shape_inference(op); + output_shapes = shape_infer->infer(input_shapes, {}).shapes; + + EXPECT_EQ(output_shapes.size(), 2); + EXPECT_THAT(output_shapes, Each(StaticShape({2, 4, 12, 11}))); + EXPECT_EQ(shape_infer->get_pads_begin(), CoordinateDiff({0, 0})); + EXPECT_EQ(shape_infer->get_pads_end(), CoordinateDiff({1, 1})); +}