diff --git a/docs/ops/image/Interpolate_1.md b/docs/ops/image/Interpolate_1.md index 090c5ef7e86..5acecd1648e 100644 --- a/docs/ops/image/Interpolate_1.md +++ b/docs/ops/image/Interpolate_1.md @@ -68,7 +68,7 @@ This is a scalar that specifies padding for each spatial dimension. **Outputs** -* **1**: Resulting interpolated tensor with elements of the same type as input ``data`` tensor. The shape of the output matches input ``data`` shape except spatial dimensions mentioned in ``axes`` attribute. For other dimensions shape matches sizes from ``target_spaticl_shape`` in order specified in ``axes``. +* **1**: Resulting interpolated tensor with elements of the same type as input ``data`` tensor. The shape of the output matches input ``data`` shape except spatial dimensions mentioned in ``axes`` attribute. For other dimensions shape matches sizes from ``target_spatial_shape`` in order specified in ``axes``. **Example** @@ -98,4 +98,3 @@ This is a scalar that specifies padding for each spatial dimension. @endsphinxdirective - diff --git a/src/core/include/openvino/op/interpolate.hpp b/src/core/include/openvino/op/interpolate.hpp index d6e32cc28cb..ac6876e1286 100644 --- a/src/core/include/openvino/op/interpolate.hpp +++ b/src/core/include/openvino/op/interpolate.hpp @@ -72,6 +72,8 @@ public: return m_attrs; } + void set_attrs(Attributes attrs); + private: Attributes m_attrs; }; @@ -113,74 +115,11 @@ public: void validate_and_infer_types() override; std::shared_ptr clone_with_new_inputs(const OutputVector& new_args) const override; - OPENVINO_SUPPRESS_DEPRECATED_START - bool evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const override; - OPENVINO_SUPPRESS_DEPRECATED_END + bool evaluate(TensorVector& outputs, const TensorVector& inputs) const override; bool has_evaluate() const override; - const InterpolateAttrs& get_attrs() const { - return m_attrs; - } - void set_attrs(const InterpolateAttrs& attrs) { - this->m_attrs = attrs; - } - -protected: - /// \return The interpolation axes. - std::vector get_axes() const; - private: - bool evaluate_interpolate(const HostTensorVector& outputs, const HostTensorVector& inputs) const; - - /// \brief Corrects pads_begin and pads_end attributes. - /// - /// \details When Interpolate-4 is a result of some transformation, it is possible - /// that pads_begin.size() != pads_end.size() or - /// pads_begin.size() != input_rank. In such case, we should correct - /// pads_begin and pads_end, using padding of pads_begin and pads_end by - /// zeros or using pads_begin[0 : input_rank], pads_end[0 : input_rank]. - /// - /// Padding of pads_begin is performed when pads_begin.size() < input_rank, - /// and pads_begin[0 : input_rank] is used when - /// pads_begin.size() < input_rank. - /// - /// Similarly for pads_end. - void correct_pads(); - - /// \brief Calculates input shape after padding. - /// - /// \param input_shape PartialShape of input data. - /// - /// \return Padded input shape, i.e. input_shape + pads_begin + pads_end - PartialShape get_padded_input_shape(const PartialShape& input_shape) const; - - /// \brief Infers output shape using scales. - /// - /// \param output_shape[in,out] output shape - /// \param axes Interpolation axes - /// \param scales Scales for interpolated axes - /// \param padded_input_shape input shape after padding - void infer_using_scales(PartialShape& output_shape, - const std::vector& axes, - const std::vector& scales, - const PartialShape& padded_input_shape) const; - - /// \brief Infers output shape using sizes. - /// - /// \param output_shape[in,out] output shape - /// \param axes Interpolation axes - /// \param sizes sizes for interpolated axes - void infer_using_shapes(PartialShape& output_shape, - const std::vector& axes, - const std::vector& sizes) const; - - template - friend void shape_infer(const Interpolate* op, - std::vector& pads_begin, - std::vector& pads_end, - const std::vector& input_shapes, - std::vector& output_shapes, - const std::map>& constant_data); + bool evaluate_interpolate(TensorVector& outputs, const TensorVector& inputs) const; }; } // namespace v4 diff --git a/src/core/include/openvino/op/util/interpolate_base.hpp b/src/core/include/openvino/op/util/interpolate_base.hpp index ac4a897a1a7..84654c79ed7 100644 --- a/src/core/include/openvino/op/util/interpolate_base.hpp +++ b/src/core/include/openvino/op/util/interpolate_base.hpp @@ -145,12 +145,6 @@ protected: void validate_scales_element_type(const element::Type& et) const; void validate_sizes_element_type(const element::Type& et) const; void validate_axes_element_type(const element::Type& et) const; - - template - friend void correct_pads_attr(const InterpolateBase* op, - std::vector& pads_begin, - std::vector& pads_end, - const std::vector& input_shapes); }; } // namespace util } // namespace op diff --git a/src/core/shape_inference/include/dimension_util.hpp b/src/core/shape_inference/include/dimension_util.hpp index 5d96708d050..cc000495a45 100644 --- a/src/core/shape_inference/include/dimension_util.hpp +++ b/src/core/shape_inference/include/dimension_util.hpp @@ -108,11 +108,15 @@ constexpr typename std::enable_if::value, TDim>::type p */ template typename std::enable_if::value, TDim>::type padded(const TDim& dim, const int64_t pad_num) { - auto ub = padded(dim.get_max_length(), pad_num); - if (dim.is_static()) { - return {ub}; + if (pad_num != 0) { + auto ub = padded(dim.get_max_length(), pad_num); + if (dim.is_static()) { + return {ub}; + } else { + return {padded(dim.get_min_length(), pad_num), ub}; + } } else { - return {padded(dim.get_min_length(), pad_num), ub}; + return dim; } } @@ -207,6 +211,31 @@ inline bool is_divisible(const Dimension& quotient, const typename Di return !(quotient / dividend).get_interval().empty(); } +/** + * @brief Scale dimension size by floating point value. + * + * @tparam TDim Dimension type. + * @param d Dimension to scale. + * @param scale Scale value for dimension. + */ +template +void scale(TDim& d, float scale) { + using T = typename TDim::value_type; + static constexpr float epsilon = 1.0e-6f; + if (scale != 1.0f) { + scale += epsilon; + + auto ub = d.get_max_length(); + ub = is_inf_bound(ub) ? static_cast(inf_bound) : static_cast(static_cast(ub) * scale); + + if (d.is_static()) { + d = TDim(ub); + } else { + d = TDim(static_cast(static_cast(d.get_min_length()) * scale), ub); + } + } +} + } // namespace dim } // namespace util } // namespace ov diff --git a/src/core/shape_inference/include/interpolate_shape_inference.hpp b/src/core/shape_inference/include/interpolate_shape_inference.hpp index 52743ceb314..aeb7e43c6f6 100644 --- a/src/core/shape_inference/include/interpolate_shape_inference.hpp +++ b/src/core/shape_inference/include/interpolate_shape_inference.hpp @@ -4,248 +4,352 @@ #pragma once +#include #include +#include "dimension_util.hpp" +#include "pooling_shape_inference_util.hpp" #include "utils.hpp" namespace ov { namespace op { +namespace interpolate { +namespace validate { +/** + * @brief Validates that input at port number from is 1-D rank. + * + * @tparam TShape + * @param op Pointer to operator. + * @param shapes Vector of op's input shapes. + * @param port Port number. + */ +template +void input_rank_1d(const Node* const op, const std::vector& shapes, size_t port) { + constexpr auto exp_rank = 1; + const auto r = shapes[port].rank(); + NODE_VALIDATION_CHECK(op, r.compatible(exp_rank), "Input [", port, "] is not rank ", exp_rank); +} -namespace util { - -template -void correct_pads_attr(const util::InterpolateBase* op, - std::vector& pads_begin, - std::vector& pads_end, - const std::vector& input_shapes) { - auto input_shape = input_shapes[0]; - if (input_shape.rank().is_dynamic()) { - return; - } - const auto input_rank = input_shape.size(); - - pads_begin = op->m_attrs.pads_begin; - pads_end = op->m_attrs.pads_end; - if (pads_begin.size() != input_rank) { - pads_begin.resize(input_rank); - } - if (pads_end.size() != input_rank) { - pads_end.resize(input_rank); +/** + * @brief Validates that inputs from 2nd to last are compatible 1-D rank. + * + * @tparam TShape + * @param op Pointer to operator. + * @param shapes Vector of op's input shapes. + */ +template +void are_inputs_except_first_1d(const Node* const op, const std::vector& shapes) { + for (size_t i = 1; i < shapes.size(); ++i) { + input_rank_1d(op, shapes, i); } } -inline int64_t multiply_bound_and_scale(int64_t bound, float scale) { - if (bound == -1) { - return bound; - } - return static_cast(static_cast(bound) * scale); +/** + * @brief Check if axes values in range [0,rank]. + * + * @tparam TContainer Type of axes container. + * @param op Pointer to operator. + * @param axes Container with axes to check. + * @param rank Maximum value for axes values. + */ +template +void axes_values(const Node* const op, const TContainer& axes, size_t rank) { + NODE_VALIDATION_CHECK(op, + std::all_of(axes.cbegin(), axes.cend(), ov::cmp::Less(rank)), + "All axes values should less than input rank: ", + rank); } -template -void infer_using_scales(T& output_shape, const std::vector& axes, const std::vector& scales) { - size_t i = 0; - static constexpr float epsilon = 1.0e-6f; - for (const auto& axis : axes) { - if (scales[i] == 1.) { - ++i; - continue; +/** + * @brief Check if number of elements in input is same as expected. + * + * @param op Pointer to operator. + * @param input_name Input name. + * @param element_count Element count in tested input. + * @param exp_count Expected element count on tested input. + */ +inline void input_elements_num(const Node* const op, + const std::string& input_name, + size_t element_count, + size_t exp_count) { + NODE_VALIDATION_CHECK(op, + element_count == exp_count, + "The number of elements in the '", + input_name, + "' input does not match the number of axes ", + exp_count); +} +} // namespace validate + +template ::value>::type* = nullptr> +constexpr bool is_same_instance(const T& lhs, const U& rhs) { + return std::addressof(lhs) == std::addressof(rhs); +} + +template ::value>::type* = nullptr> +constexpr bool is_same_instance(const T& lhs, const U& rhs) { + return false; +} + +/** + * @brief Resize padding to input rank. + * + * @tparam TContainer Pads container type. + * @param op Pointer to base of interpolate. + * @param input_rank Expected padding size. + * @param pads_begin Begin padding container. + * @param pads_end End padding container. + */ +template +void resize_padding(const ov::op::util::InterpolateBase* op, + size_t input_rank, + TContainer& pads_begin, + TContainer& pads_end) { + const auto& op_pads_begin = op->get_attrs().pads_begin; + const auto& op_pads_end = op->get_attrs().pads_end; + + if (!is_same_instance(op_pads_begin, pads_begin)) { + pads_begin = TContainer(op_pads_begin.begin(), op_pads_begin.end()); + } + + if (!is_same_instance(op_pads_end, pads_end)) { + pads_end = TContainer(op_pads_end.begin(), op_pads_end.end()); + } + + pads_begin.resize(input_rank); + pads_end.resize(input_rank); +} + +/** + * @brief Makes padded shape from input shapes and padding values + * + * @note The input shape must be static rank and padding count must match input shape rank. + * + * @param input Input shape used as source for output result. + * @param pads_begin Dimensions begin padding values. + * @param pads_end Dimensions end padding values. + * @return TShape Shape with dimensions of input plus paddings. + */ +template +TShape make_padded_shape(const TShape& input, TInputIter pads_begin, TInputIter pads_end) { + using TDim = typename TShape::value_type; + TShape out; + out.reserve(input.size()); + std::transform(input.cbegin(), input.cend(), std::back_inserter(out), [&pads_begin, &pads_end](const TDim& d) { + return ov::util::dim::padded(d, (*pads_begin++ + *pads_end++)); + }); + + return out; +} + +/** + * @brief Get the axes for interpolate from constant input or default value if op has no axes input. + * + * @param op Pointer to operator. + * @param port Axes input port number. + * @param has_axes Flag if op has input with axes. + * @param rank input shape used for axes values validation. + * @param ta Tensor accessor for input data. + * @return Not null pointer with axes values or null pointer if can't get axes from input. + */ +template > +std::unique_ptr get_axes(const Node* const op, + size_t port, + bool has_axes, + size_t rank, + const ITensorAccessor& ta) { + std::unique_ptr axes; + if (has_axes) { + using TAxis = typename TRes::value_type; + axes = std::move(get_input_const_data_as(op, port, ta)); + if (axes) { + validate::axes_values(op, *axes, rank); } - const auto& current_dim = output_shape[axis]; - float multiplier = scales[i] + epsilon; - if (current_dim.is_static()) { - output_shape[axis] = multiply_bound_and_scale(current_dim.get_length(), multiplier); - } else { - int64_t new_lower_bound = multiply_bound_and_scale(current_dim.get_min_length(), multiplier); - int64_t new_upper_bound = multiply_bound_and_scale(current_dim.get_max_length(), multiplier); - output_shape[axis] = ov::Dimension(new_lower_bound, new_upper_bound); - } - ++i; + } else { + axes.reset(new TRes(rank)); + std::iota(axes->begin(), axes->end(), 0); + } + return axes; +} + +/** + * @brief Set the undefined dimensions on specified axes. + * + * @param out Output shape to update. + * @param axes List of axes for update. + */ +template +void set_undefined_dim_on_axes(TShape& out, TContainer& axes) { + static const auto undefined_dim = Dimension::dynamic(); + for (const auto axis : axes) { + out[axis] = undefined_dim; } } -} // namespace util + +/** + * @brief Update output shape with dimension size from input on specified axes. + * + * @param out_shape Output shape to be updated. + * @param axes List of axes for dimension update. + * @param op Pointer to operator. + * @param port Sizes/output target shape input with values + * @param ta Tensor accessor. + */ +template +void update_dims_with_sizes_on_axes(TShape& out_shape, + const TContainer& axes, + const Node* const op, + const size_t port, + const ITensorAccessor& ta) { + if (const auto sizes = get_input_const_data_as_shape(op, port, ta)) { + validate::input_elements_num(op, "sizes", sizes->size(), axes.size()); + auto sizes_iter = sizes->begin(); + for (const auto axis : axes) { + out_shape[axis] = *sizes_iter++; + } + } else { + set_undefined_dim_on_axes(out_shape, axes); + } +} + +/** + * @brief Update output shape by scaling dimensions on axes. + * + * @param out_shape Output shape to update. + * @param axes List of axes to scale dimension. + * @param op Pointer to operator. + * @param port Scales input port number with values. + * @param ta Tensor accessor. + */ +template +void update_dims_with_scales_on_axes(TShape& out_shape, + const std::vector& axes, + const Node* const op, + const size_t port, + const ITensorAccessor& ta) { + if (const auto scales = get_input_const_data_as(op, port, ta)) { + validate::input_elements_num(op, "scales", scales->size(), axes.size()); + auto scale_iter = scales->begin(); + for (const auto axis : axes) { + ov::util::dim::scale(out_shape[axis], *scale_iter++); + } + } else { + set_undefined_dim_on_axes(out_shape, axes); + } +} +} // namespace interpolate namespace v0 { -template -void shape_infer(const Interpolate* op, - const std::vector& input_shapes, - std::vector& output_shapes, - const std::map>& constant_data = {}) { - NODE_VALIDATION_CHECK(op, input_shapes.size() == 2 && output_shapes.size() == 1); +template +std::vector shape_infer(const Interpolate* op, + const std::vector& input_shapes, + const ITensorAccessor& tensor_accessor) { + NODE_VALIDATION_CHECK(op, input_shapes.size() == 2); + const auto& img_shape = input_shapes[0]; - const auto& input_shape = input_shapes[0]; - auto& output_shape = output_shapes[0]; - output_shape = input_shape; - const auto& attr = op->get_attrs(); + auto output_shapes = std::vector(1, img_shape); + auto& out_shape = output_shapes.front(); - if (input_shape.rank().is_static()) { - auto input_rank = input_shape.size(); - NODE_VALIDATION_CHECK(op, - std::all_of(attr.axes.begin(), - attr.axes.end(), - [input_rank](size_t axis) { - return axis < input_rank; - }), - "Axis value should less than input rank. ", - "Got: input rank ", - input_rank, - ", axes ", - attr.axes); + if (img_shape.rank().is_static()) { + const auto& axes = op->get_attrs().axes; + const auto img_rank = img_shape.size(); - T target_spatial_shape; - if (get_data_as_shape(1, op, target_spatial_shape, constant_data)) { - size_t i = 0; - for (auto axis : attr.axes) { - output_shape[axis] = target_spatial_shape[i++]; + interpolate::validate::axes_values(op, axes, img_rank); + + if (const auto target_spatial_shape = get_input_const_data_as_shape(op, 1, tensor_accessor)) { + auto target_spatial_shape_iter = target_spatial_shape->begin(); + for (const auto axis : axes) { + out_shape[axis] = *target_spatial_shape_iter++; } } else { - for (auto axis : attr.axes) { - output_shape[axis] = ov::Dimension::dynamic(); - } + interpolate::set_undefined_dim_on_axes(out_shape, axes); } } + + return output_shapes; } } // namespace v0 namespace v4 { -template -void shape_infer(const Interpolate* op, - std::vector& pads_begin, - std::vector& pads_end, - const std::vector& input_shapes, - std::vector& output_shapes, - const std::map>& constant_data) { - NODE_VALIDATION_CHECK(op, (input_shapes.size() == 3 || input_shapes.size() == 4) && output_shapes.size() == 1); - using DimType = typename std::iterator_traits::value_type; +template +std::vector shape_infer(const Interpolate* op, + const std::vector& input_shapes, + TContainer& pads_begin, + TContainer& pads_end, + const ITensorAccessor& tensor_accessor) { + const auto has_axes_input = (input_shapes.size() == 4); + NODE_VALIDATION_CHECK(op, (input_shapes.size() == 3 || has_axes_input)); - const auto& input_shape = input_shapes[0]; - auto& output_shape = output_shapes[0]; - output_shape = input_shape; + const auto is_using_scales = (op->get_attrs().shape_calculation_mode == Interpolate::ShapeCalcMode::SCALES); - if (input_shape.rank().is_static()) { - const auto input_rank = input_shape.size(); + interpolate::validate::input_rank_1d(op, input_shapes, is_using_scales ? 2 : 1); - // Get axes - std::vector axes; - if (input_shapes.size() == 4 && !(get_data_as_int64(3, op, axes, constant_data))) { - for (size_t i = 0; i < input_rank; i++) - output_shape[i] = ov::Dimension::dynamic(); - return; - } else if (input_shapes.size() == 3) { - axes.resize(input_rank); - std::iota(axes.begin(), axes.end(), 0); - } - NODE_VALIDATION_CHECK(op, - std::all_of(axes.begin(), - axes.end(), - [input_rank](size_t axis) { - return axis < input_rank; - }), - "Axis value should less than input rank."); + if (has_axes_input) { + interpolate::validate::input_rank_1d(op, input_shapes, 3); + } - // Get padded input shape - for (size_t i = 0; i < input_rank; ++i) { - output_shape[i] = DimType(pads_begin[i]) + DimType(pads_end[i]) + input_shape[i]; - } + const auto& img_shape = input_shapes[0]; + auto output_shapes = std::vector(); - if (op->m_attrs.shape_calculation_mode == Interpolate::ShapeCalcMode::SCALES) { - std::vector scales; - if (get_data_as_float(2, op, scales, constant_data)) { - util::infer_using_scales(output_shape, axes, scales); + if (img_shape.rank().is_static()) { + const auto img_rank = img_shape.size(); + interpolate::resize_padding(op, img_rank, pads_begin, pads_end); + + const auto axes = interpolate::get_axes(op, 3, has_axes_input, img_rank, tensor_accessor); + if (axes) { + output_shapes.push_back(interpolate::make_padded_shape(img_shape, pads_begin.cbegin(), pads_end.cbegin())); + + if (is_using_scales) { + interpolate::update_dims_with_scales_on_axes(output_shapes.front(), *axes, op, 2, tensor_accessor); } else { - for (const auto& axis : axes) { - output_shape[axis] = ov::Dimension::dynamic(); - } + interpolate::update_dims_with_sizes_on_axes(output_shapes.front(), *axes, op, 1, tensor_accessor); } } else { - T target_spatial_shape; - if (get_data_as_shape(1, op, target_spatial_shape, constant_data)) { - size_t i = 0; - for (const auto& axis : axes) { - output_shape[axis] = target_spatial_shape[i++]; - } - } else { - for (const auto& axis : axes) { - output_shape[axis] = ov::Dimension::dynamic(); - } - } + output_shapes.push_back(PartialShape::dynamic(img_rank)); } + } else { + output_shapes.push_back(PartialShape::dynamic()); } + return output_shapes; } - } // namespace v4 namespace v11 { -template -void shape_infer(const Interpolate* op, - std::vector& pads_begin, - std::vector& pads_end, - const std::vector& input_shapes, - std::vector& output_shapes, - const std::map>& constant_data) { - NODE_VALIDATION_CHECK(op, (input_shapes.size() == 2 || input_shapes.size() == 3) && output_shapes.size() == 1); - using DimType = typename std::iterator_traits::value_type; +template +std::vector shape_infer(const Interpolate* op, + const std::vector& input_shapes, + TContainer& pads_begin, + TContainer& pads_end, + const ITensorAccessor& tensor_accessor) { + NODE_VALIDATION_CHECK(op, (input_shapes.size() == 2 || input_shapes.size() == 3)); - const auto& input_shape = input_shapes[0]; - auto& output_shape = output_shapes[0]; - output_shape = input_shape; + interpolate::validate::are_inputs_except_first_1d(op, input_shapes); - if (input_shape.rank().is_static()) { - const auto input_rank = input_shape.size(); + const auto& img_shape = input_shapes[0]; + auto output_shapes = std::vector(); - // Get axes - std::vector axes; - if (input_shapes.size() == 3 && !(get_data_as_int64(2, op, axes, constant_data))) { - for (size_t i = 0; i < input_rank; i++) - output_shape[i] = ov::Dimension::dynamic(); - return; - } else if (input_shapes.size() == 2) { - axes.resize(input_rank); - std::iota(axes.begin(), axes.end(), 0); - } - NODE_VALIDATION_CHECK(op, - std::all_of(axes.begin(), - axes.end(), - [input_rank](size_t axis) { - return axis < input_rank; - }), - "Axis value should be smaller than the input's rank."); + if (img_shape.rank().is_static()) { + const auto img_rank = img_shape.size(); + const auto has_axes_input = (input_shapes.size() == 3); - // Get padded input shape - for (size_t i = 0; i < input_rank; ++i) { - output_shape[i] = DimType(pads_begin[i]) + DimType(pads_end[i]) + input_shape[i]; - } + interpolate::resize_padding(op, img_rank, pads_begin, pads_end); - if (op->get_attrs().shape_calculation_mode == Interpolate::ShapeCalcMode::SCALES) { - std::vector scales; - if (get_data_as_float(1, op, scales, constant_data)) { - NODE_VALIDATION_CHECK( - op, - axes.size() == scales.size(), - "The number of elements in the 'scales_or_sizes' input does not match the number of axes"); - util::infer_using_scales(output_shape, axes, scales); + const auto axes = interpolate::get_axes(op, 2, has_axes_input, img_rank, tensor_accessor); + if (axes) { + output_shapes.push_back(interpolate::make_padded_shape(img_shape, pads_begin.cbegin(), pads_end.cbegin())); + + if (op->get_attrs().shape_calculation_mode == Interpolate::ShapeCalcMode::SCALES) { + interpolate::update_dims_with_scales_on_axes(output_shapes.front(), *axes, op, 1, tensor_accessor); } else { - for (const auto& axis : axes) { - output_shape[axis] = ov::Dimension::dynamic(); - } + interpolate::update_dims_with_sizes_on_axes(output_shapes.front(), *axes, op, 1, tensor_accessor); } } else { - T target_spatial_shape; - if (get_data_as_shape(1, op, target_spatial_shape, constant_data)) { - NODE_VALIDATION_CHECK( - op, - axes.size() == target_spatial_shape.size(), - "The number of elements in the 'scales_or_sizes' input does not match the number of axes"); - size_t i = 0; - for (const auto& axis : axes) { - output_shape[axis] = target_spatial_shape[i++]; - } - } else { - for (const auto& axis : axes) { - output_shape[axis] = ov::Dimension::dynamic(); - } - } + output_shapes.push_back(PartialShape::dynamic(img_rank)); } + } else { + output_shapes.push_back(PartialShape::dynamic()); } + return output_shapes; } } // namespace v11 } // namespace op diff --git a/src/core/shape_inference/include/tensor_data_accessor.hpp b/src/core/shape_inference/include/tensor_data_accessor.hpp index d6fd3f82c0c..41c48641ad9 100644 --- a/src/core/shape_inference/include/tensor_data_accessor.hpp +++ b/src/core/shape_inference/include/tensor_data_accessor.hpp @@ -61,6 +61,9 @@ Tensor TensorAccessor::operator()(size_t port) const; template <> Tensor TensorAccessor::operator()(size_t port) const; +template <> +Tensor TensorAccessor>::operator()(size_t port) const; + template <> Tensor TensorAccessor>::operator()(size_t port) const; diff --git a/src/core/shape_inference/src/tensor_data_accessor.cpp b/src/core/shape_inference/src/tensor_data_accessor.cpp index 30aadaa8bd6..6d90dee024a 100644 --- a/src/core/shape_inference/src/tensor_data_accessor.cpp +++ b/src/core/shape_inference/src/tensor_data_accessor.cpp @@ -24,6 +24,16 @@ Tensor TensorAccessor::operator()(size_t port) const { } } +template <> +Tensor TensorAccessor>::operator()(size_t port) const { + const auto t_iter = m_tensors->find(port); + if (t_iter != m_tensors->cend()) { + return t_iter->second; + } else { + return make_tensor_accessor()(port); + } +} + template <> Tensor TensorAccessor>::operator()(size_t port) const { const auto t_iter = m_tensors->find(port); diff --git a/src/core/src/op/interpolate.cpp b/src/core/src/op/interpolate.cpp index a8946b78242..92e04a2f876 100644 --- a/src/core/src/op/interpolate.cpp +++ b/src/core/src/op/interpolate.cpp @@ -2,18 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "openvino/op/interpolate.hpp" +#include "ngraph/runtime/reference/interpolate.hpp" #include #include #include -#include -#include #include +#include "interpolate_shape_inference.hpp" #include "itt.hpp" -#include "ngraph/op/constant.hpp" -#include "ngraph/runtime/reference/interpolate.hpp" +#include "openvino/op/interpolate.hpp" #include "openvino/op/util/precision_sensitive_attribute.hpp" using namespace std; @@ -45,12 +43,11 @@ void ov::op::v0::Interpolate::validate_and_infer_types() { "output shape must be an integral number."); set_input_is_relevant_to_shape(1); - const auto& input_shape = get_input_partial_shape(0); - const auto& target_spatial_shape = get_input_partial_shape(1); - std::vector input_shapes = {input_shape, target_spatial_shape}; - std::vector output_shapes = {ov::PartialShape{}}; + OPENVINO_SUPPRESS_DEPRECATED_START + const auto input_shapes = ov::get_node_input_partial_shapes(*this); + OPENVINO_SUPPRESS_DEPRECATED_END - shape_infer(this, input_shapes, output_shapes); + const auto output_shapes = shape_infer(this, input_shapes, make_tensor_accessor()); set_output_type(0, get_input_element_type(0), output_shapes[0]); } @@ -75,6 +72,11 @@ EnumNames::get() { {"area", ov::op::v0::Interpolate::InterpolateMode::AREA}}); return enum_names; } + +void op::v0::Interpolate::set_attrs(Attributes attrs) { + m_attrs = std::move(attrs); +} + } // namespace ov // Interpolate v4 @@ -96,98 +98,18 @@ ov::op::v4::Interpolate::Interpolate(const Output& image, constructor_validate_and_infer_types(); } -std::vector ov::op::v4::Interpolate::get_axes() const { - auto inputs = input_values(); - if (inputs.size() <= 3) { - ov::PartialShape input_shape = ov::PartialShape(get_input_partial_shape(0)); - NODE_VALIDATION_CHECK(this, - input_shape.rank().is_static(), - "Could not define axes of interpolation because there are " - "only three inputs and input data has a dynamic rank."); - - const auto input_rank = input_shape.rank().get_length(); - std::vector default_value(input_rank); - std::iota(default_value.begin(), default_value.end(), 0); - - return default_value; - } - - OPENVINO_SUPPRESS_DEPRECATED_START - auto axes_node = get_constant_from_source(input_value(3)); - OPENVINO_SUPPRESS_DEPRECATED_END - NODE_VALIDATION_CHECK(this, axes_node, "Input 'axes' should be Constant or foldable."); - - return axes_node->cast_vector(); -} - -static constexpr float epsilon = 1.0e-6f; - -void ov::op::v4::Interpolate::infer_using_scales(ov::PartialShape& output_shape, - const std::vector& axes, - const std::vector& scales, - const ov::PartialShape& padded_input_shape) const { - size_t i = 0; - for (auto axis : axes) { - const auto& current_dim = padded_input_shape[axis]; - float multiplier = scales[i] + epsilon; - - int64_t new_lower_bound = util::multiply_bound_and_scale(current_dim.get_min_length(), multiplier); - int64_t new_upper_bound = util::multiply_bound_and_scale(current_dim.get_max_length(), multiplier); - - output_shape[axis] = Dimension(new_lower_bound, new_upper_bound); - ++i; - } -} - -void ov::op::v4::Interpolate::infer_using_shapes(ov::PartialShape& output_shape, - const std::vector& axes, - const std::vector& sizes) const { - size_t i = 0; - for (auto axis : axes) { - output_shape[axis] = Dimension(sizes[i++]); - } -} - -ov::PartialShape ov::op::v4::Interpolate::get_padded_input_shape(const ov::PartialShape& input_shape) const { - const auto input_rank = input_shape.rank().get_length(); - - ov::PartialShape padded_input_shape = input_shape; - - for (int64_t i = 0; i < input_rank; ++i) { - if (input_shape[i].is_static()) { - auto new_length = m_attrs.pads_begin[i] + m_attrs.pads_end[i] + input_shape[i].get_length(); - padded_input_shape[i] = Dimension(new_length); - } - } - - return padded_input_shape; -} - void ov::op::v4::Interpolate::validate_and_infer_types() { OV_OP_SCOPE(v4_Interpolate_validate_and_infer_types); InterpolateBase::validate_and_infer_types(); validate_sizes_element_type(get_input_element_type(1)); - validate_scales_element_type(get_input_element_type(2)); if (input_values().size() == 4) { validate_axes_element_type(get_input_element_type(3)); } - std::vector output_shapes = {ov::PartialShape()}; - std::vector input_shapes; - const auto& input_shape = get_input_partial_shape(0); - const auto& target_spatial_shape = get_input_partial_shape(1); - const auto& scales = get_input_partial_shape(2); - if (input_values().size() == 3) { - input_shapes = {input_shape, target_spatial_shape, scales}; - } else { - const auto& axes = get_input_partial_shape(3); - input_shapes = {input_shape, target_spatial_shape, scales, axes}; - } - const auto interpolation_mode_check = [](const op::util::InterpolateBase::InterpolateMode mode) { constexpr std::array allowed_modes = { op::util::InterpolateBase::InterpolateMode::NEAREST, @@ -203,8 +125,12 @@ void ov::op::v4::Interpolate::validate_and_infer_types() { "Unsupported interpolation mode used with version 4 of the Interpolate op: ", as_string(m_attrs.mode)); - util::correct_pads_attr(this, m_attrs.pads_begin, m_attrs.pads_end, input_shapes); - shape_infer(this, m_attrs.pads_begin, m_attrs.pads_end, input_shapes, output_shapes, {}); + OPENVINO_SUPPRESS_DEPRECATED_START + const auto input_shapes = get_node_input_partial_shapes(*this); + OPENVINO_SUPPRESS_DEPRECATED_END + + const auto output_shapes = + shape_infer(this, input_shapes, m_attrs.pads_begin, m_attrs.pads_end, make_tensor_accessor()); set_output_type(0, get_input_element_type(0), output_shapes[0]); } @@ -228,107 +154,27 @@ static constexpr size_t scales_port = 2; static constexpr size_t axes_port = 3; static constexpr size_t max_num_of_ports = 4; -std::vector get_axes_vector(const ngraph::HostTensorVector& args) { - ov::Shape input_shape{args[data_port]->get_shape()}; - size_t input_rank = input_shape.size(); - size_t num_of_inputs = args.size(); - - std::vector axes; - - if (num_of_inputs == max_num_of_ports) { - auto axes_arg = args[axes_port]; - size_t num_of_axes = args[axes_port]->get_shape()[0]; - axes.reserve(num_of_axes); - - if (axes_arg->get_element_type() == ov::element::i64) { - int64_t* axes_ptr = axes_arg->get_data_ptr(); - axes.insert(axes.end(), axes_ptr, axes_ptr + num_of_axes); - } else if (axes_arg->get_element_type() == ov::element::i32) { - int32_t* axes_ptr = axes_arg->get_data_ptr(); - for (size_t i = 0; i < num_of_axes; ++i) - axes.push_back(axes_ptr[i]); - } else { - OPENVINO_ASSERT(false, "Failed to process ", axes_arg->get_element_type()); - } - } else { - for (size_t i = 0; i < input_rank; ++i) { - axes.push_back(i); - } - } - - return axes; -} - -std::vector get_target_shape_vector(const ngraph::HostTensorVector& args, size_t num_of_axes) { - std::vector target_shape; - target_shape.reserve(num_of_axes); - - auto target_shape_arg = args[target_shape_port]; - if (target_shape_arg->get_element_type() == ov::element::i64) { - int64_t* target_shape_ptr = target_shape_arg->get_data_ptr(); - target_shape.insert(target_shape.end(), target_shape_ptr, target_shape_ptr + num_of_axes); - } else if (target_shape_arg->get_element_type() == ov::element::i32) { - int32_t* target_shape_ptr = target_shape_arg->get_data_ptr(); - for (size_t i = 0; i < num_of_axes; ++i) - target_shape.push_back(target_shape_ptr[i]); - } else { - OPENVINO_ASSERT(false, "Failed to process ", target_shape_arg->get_element_type()); - } - - return target_shape; -} - -std::vector get_scales_vector(const ngraph::HostTensorVector& args, +std::vector get_scales_vector(const ov::TensorVector& args, const ov::Shape& input_shape, const ov::op::v4::Interpolate::InterpolateAttrs& attrs, std::vector axes) { - std::vector scales; - size_t num_of_axes = axes.size(); + using scales_t = float; + constexpr auto f32_cast = ov::util::Cast(); + if (attrs.shape_calculation_mode == ov::op::util::InterpolateBase::ShapeCalcMode::SCALES) { - float* scales_ptr = args[scales_port]->get_data_ptr(); - scales.insert(scales.end(), scales_ptr, scales_ptr + num_of_axes); + return ov::get_tensor_data_as(args[scales_port], f32_cast); } else { - auto target_shape = get_target_shape_vector(args, num_of_axes); - for (size_t i = 0; i < num_of_axes; ++i) { - size_t axis = axes[i]; - float scale = static_cast(target_shape[i]) / static_cast(input_shape[axis]); - scales.push_back(scale); + auto scales = ov::get_tensor_data_as(args[target_shape_port], f32_cast); + auto scales_iter = scales.begin(); + for (const auto axis : axes) { + *scales_iter /= input_shape[axis]; + ++scales_iter; } + return scales; } - return scales; -} - -template -std::vector correct_pad(const std::vector& p, size_t rank) { - size_t pad_len = p.size(); - if (pad_len == rank) { - return p; - } - - std::vector result; - - if (pad_len > rank) { - result.insert(result.end(), p.begin(), p.begin() + rank); - } else { - result = p; - result.insert(result.end(), rank - pad_len, T{}); - } - - return result; } } // namespace -void ov::op::v4::Interpolate::correct_pads() { - ov::PartialShape input_shape = ov::PartialShape(get_input_partial_shape(0)); - if (input_shape.rank().is_dynamic()) { - return; - } - const auto input_rank = input_shape.rank().get_length(); - - m_attrs.pads_begin = correct_pad(m_attrs.pads_begin, input_rank); - m_attrs.pads_end = correct_pad(m_attrs.pads_end, input_rank); -} - static void pad_input_data(const uint8_t* data_ptr, uint8_t* padded_data_ptr, size_t type_size, @@ -353,49 +199,46 @@ static void pad_input_data(const uint8_t* data_ptr, NGRAPH_SUPPRESS_DEPRECATED_END } -bool ov::op::v4::Interpolate::evaluate_interpolate(const HostTensorVector& outputs, - const HostTensorVector& inputs) const { - element::Type input_et = get_input_element_type(0); - size_t type_size = input_et.size(); +bool ov::op::v4::Interpolate::evaluate_interpolate(TensorVector& outputs, const TensorVector& inputs) const { + auto input_shapes = std::vector(); + const auto inputs_num = inputs.size(); - ov::Shape input_shape{inputs[data_port]->get_shape()}; - ov::Shape padded_input_shape = get_padded_input_shape(input_shape).to_shape(); - - auto axes = get_axes_vector(inputs); - size_t num_of_axes = axes.size(); - - auto scales = get_scales_vector(inputs, padded_input_shape, m_attrs, axes); - - ov::PartialShape output_shape{padded_input_shape}; - - if (m_attrs.shape_calculation_mode == ShapeCalcMode::SCALES) { - infer_using_scales(output_shape, axes, scales, padded_input_shape); - } else { - auto sizes = get_target_shape_vector(inputs, num_of_axes); - infer_using_shapes(output_shape, axes, sizes); + input_shapes.reserve(inputs_num); + for (const auto& in : inputs) { + input_shapes.push_back(in.get_shape()); } - ov::Shape out_shape = output_shape.to_shape(); + auto pads_begin = m_attrs.pads_begin; + auto pads_end = m_attrs.pads_end; - outputs[0]->set_element_type(inputs[0]->get_element_type()); - outputs[0]->set_shape(out_shape); + const auto ta = make_tensor_accessor(inputs); + const auto out_shape = shape_infer(this, input_shapes, pads_begin, pads_end, ta).front().to_shape(); + outputs[0].set_shape(out_shape); - size_t bytes_in_padded_input = shape_size(padded_input_shape) * type_size; + auto padded_input_shape = + interpolate::make_padded_shape(input_shapes.front(), pads_begin.begin(), pads_end.begin()).to_shape(); - std::vector padded_input_data(bytes_in_padded_input, 0); + const auto has_axes_input = (inputs_num == max_num_of_ports); + const auto axes = interpolate::get_axes(this, axes_port, has_axes_input, out_shape.size(), ta); + const auto scales = get_scales_vector(inputs, padded_input_shape, m_attrs, *axes); - const uint8_t* data_ptr = inputs[0]->get_data_ptr(); - uint8_t* padded_data_ptr = padded_input_data.data(); + const auto input_et = get_input_element_type(0); + const auto type_size = input_et.size(); + const auto bytes_in_padded_input = shape_size(padded_input_shape) * type_size; + auto padded_input_data = std::vector(bytes_in_padded_input, 0); - pad_input_data(data_ptr, padded_data_ptr, type_size, input_shape, padded_input_shape, m_attrs.pads_begin); + auto* data_ptr = static_cast(inputs[data_port].data()); + auto* padded_data_ptr = padded_input_data.data(); + + pad_input_data(data_ptr, padded_data_ptr, type_size, inputs[data_port].get_shape(), padded_input_shape, pads_begin); switch (input_et) { case element::Type_t::f32: ngraph::runtime::reference::interpolate(reinterpret_cast(padded_data_ptr), padded_input_shape, scales, - axes, - outputs[0]->get_data_ptr(), + *axes, + outputs[0].data(), out_shape, m_attrs); break; @@ -403,8 +246,8 @@ bool ov::op::v4::Interpolate::evaluate_interpolate(const HostTensorVector& outpu ngraph::runtime::reference::interpolate(reinterpret_cast(padded_data_ptr), padded_input_shape, scales, - axes, - outputs[0]->get_data_ptr(), + *axes, + outputs[0].data(), out_shape, m_attrs); break; @@ -412,8 +255,8 @@ bool ov::op::v4::Interpolate::evaluate_interpolate(const HostTensorVector& outpu ngraph::runtime::reference::interpolate(reinterpret_cast(padded_data_ptr), padded_input_shape, scales, - axes, - outputs[0]->get_data_ptr(), + *axes, + outputs[0].data(), out_shape, m_attrs); break; @@ -421,8 +264,8 @@ bool ov::op::v4::Interpolate::evaluate_interpolate(const HostTensorVector& outpu ngraph::runtime::reference::interpolate(reinterpret_cast(padded_data_ptr), padded_input_shape, scales, - axes, - outputs[0]->get_data_ptr(), + *axes, + outputs[0].data(), out_shape, m_attrs); break; @@ -430,8 +273,8 @@ bool ov::op::v4::Interpolate::evaluate_interpolate(const HostTensorVector& outpu ngraph::runtime::reference::interpolate(reinterpret_cast(padded_data_ptr), padded_input_shape, scales, - axes, - outputs[0]->get_data_ptr(), + *axes, + outputs[0].data(), out_shape, m_attrs); break; @@ -441,7 +284,7 @@ bool ov::op::v4::Interpolate::evaluate_interpolate(const HostTensorVector& outpu return true; } -bool ov::op::v4::Interpolate::evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const { +bool ov::op::v4::Interpolate::evaluate(TensorVector& outputs, const TensorVector& inputs) const { OV_OP_SCOPE(v4_Interpolate_evaluate); return evaluate_interpolate(outputs, inputs); } @@ -502,19 +345,12 @@ void op::v11::Interpolate::validate_and_infer_types() { validate_axes_element_type(get_input_element_type(2)); } - std::vector output_shapes = {ov::PartialShape()}; - std::vector input_shapes; - const auto& input_shape = get_input_partial_shape(0); - const auto& scales_or_sizes = get_input_partial_shape(1); - if (input_values().size() == 2) { - input_shapes = {input_shape, scales_or_sizes}; - } else { - const auto& axes = get_input_partial_shape(2); - input_shapes = {input_shape, scales_or_sizes, axes}; - } + OPENVINO_SUPPRESS_DEPRECATED_START + const auto input_shapes = get_node_input_partial_shapes(*this); + OPENVINO_SUPPRESS_DEPRECATED_END - util::correct_pads_attr(this, m_attrs.pads_begin, m_attrs.pads_end, input_shapes); - shape_infer(this, m_attrs.pads_begin, m_attrs.pads_end, input_shapes, output_shapes, {}); + const auto output_shapes = + shape_infer(this, input_shapes, m_attrs.pads_begin, m_attrs.pads_end, make_tensor_accessor()); set_output_type(0, get_input_element_type(0), output_shapes[0]); } } // namespace ov diff --git a/src/core/tests/type_prop/interpolate.cpp b/src/core/tests/type_prop/interpolate.cpp index d4c876c1a91..610838120e2 100644 --- a/src/core/tests/type_prop/interpolate.cpp +++ b/src/core/tests/type_prop/interpolate.cpp @@ -18,6 +18,117 @@ using Nearest_mode = op::v4::Interpolate::NearestMode; using InterpolateAttrs = op::v4::Interpolate::InterpolateAttrs; using ShapeCalcMode = op::v4::Interpolate::ShapeCalcMode; +TEST(type_prop, interpolate_v0_default_ctor) { + auto image = std::make_shared(element::f32, Shape{2, 2, 30, 60}); + auto target_shape = op::Constant::create(element::i32, Shape{2}, {15, 30}); + + op::v0::Interpolate::Attributes attrs; + attrs.axes = AxisSet{2, 3}; + attrs.pads_begin = {0, 0, 0, 0}; + attrs.pads_end = {0, 0, 0, 0}; + + auto interp = std::make_shared(); + interp->set_arguments(OutputVector{image, target_shape}); + interp->set_attrs(attrs); + interp->validate_and_infer_types(); + + EXPECT_EQ(interp->get_element_type(), element::f32); + EXPECT_EQ(interp->get_shape(), (Shape{2, 2, 15, 30})); + EXPECT_THAT(get_shape_labels(interp->get_output_partial_shape(0)), Each(ov::no_label)); +} + +TEST(type_prop, interpolate_v0_all_inputs_dynamic_rank) { + const auto image = std::make_shared(element::f16, PartialShape::dynamic()); + const auto target_shape = std::make_shared(element::i32, PartialShape::dynamic()); + + op::v0::Interpolate::Attributes attrs; + attrs.axes = AxisSet{2, 3}; + attrs.pads_begin = {0, 0, 0, 0}; + attrs.pads_end = {0, 0, 0, 0}; + + auto interp = std::make_shared(image, target_shape, attrs); + + EXPECT_EQ(interp->get_element_type(), element::f16); + EXPECT_EQ(interp->get_output_partial_shape(0), PartialShape::dynamic()); +} + +TEST(type_prop, interpolate_v0_all_inputs_static_rank) { + const auto image = std::make_shared(element::f16, PartialShape::dynamic(6)); + const auto target_shape = std::make_shared(element::i32, PartialShape::dynamic(1)); + + op::v0::Interpolate::Attributes attrs; + attrs.axes = AxisSet{2, 3}; + attrs.pads_begin = {0, 0, 0, 0}; + attrs.pads_end = {0, 0, 0, 0}; + + auto interp = std::make_shared(image, target_shape, attrs); + + EXPECT_EQ(interp->get_element_type(), element::f16); + EXPECT_EQ(interp->get_output_partial_shape(0), PartialShape::dynamic(6)); +} + +TEST(type_prop, interpolate_v0_target_shape_not_constant) { + const auto image = std::make_shared(element::bf16, PartialShape{2, 4, 12, 12}); + const auto target_shape = std::make_shared(element::i64, PartialShape{1}); + + op::v0::Interpolate::Attributes attrs; + attrs.axes = AxisSet{3, 1}; + attrs.pads_begin = {0, 0, 0, 0}; + attrs.pads_end = {0, 0, 0, 0}; + + auto interp = std::make_shared(image, target_shape, attrs); + + EXPECT_EQ(interp->get_element_type(), element::bf16); + EXPECT_EQ(interp->get_output_partial_shape(0), PartialShape({2, -1, 12, -1})); +} + +TEST(type_prop, interpolate_v0_target_shape_as_shape_of) { + auto img_shape = PartialShape{{1, 2}, 10, 10, {5, 30}}; + auto out_shape = PartialShape{{2, 4}, -1}; + set_shape_labels(img_shape, 10); + set_shape_labels(out_shape, 20); + + auto image = std::make_shared(element::f64, img_shape); + auto target_shape = std::make_shared(std::make_shared(element::i32, out_shape)); + + op::v0::Interpolate::Attributes attrs; + attrs.axes = AxisSet{3, 1}; + attrs.pads_begin = {0, 0, 1, 0}; + attrs.pads_end = {0, 2, 0, 0}; + auto interp = std::make_shared(image, target_shape, attrs); + + EXPECT_EQ(interp->get_element_type(), element::f64); + EXPECT_EQ(interp->get_output_partial_shape(0), PartialShape({{1, 2}, {2, 4}, 10, -1})); + EXPECT_THAT(get_shape_labels(interp->get_output_partial_shape(0)), ElementsAre(10, 20, 12, 21)); +} + +// --- v4 --- +TEST(type_prop, interpolate_v4_default_ctor) { + auto image = std::make_shared(element::f32, Shape{2, 2, 30, 60}); + auto target_shape = std::make_shared(element::i32, Shape{}); + auto scales = op::Constant::create(element::f32, Shape{2}, {0.5f, 0.5f}); + auto axes = op::Constant::create(element::i64, Shape{2}, {2, 3}); + + InterpolateAttrs attrs; + attrs.mode = InterpolateMode::NEAREST; + attrs.shape_calculation_mode = ShapeCalcMode::SCALES; + attrs.coordinate_transformation_mode = CoordinateTransformMode::HALF_PIXEL; + attrs.nearest_mode = Nearest_mode::ROUND_PREFER_FLOOR; + attrs.antialias = false; + attrs.pads_begin = {0, 0, 0, 0}; + attrs.pads_end = {0, 0, 0, 0}; + attrs.cube_coeff = -0.75; + + auto interp = std::make_shared(); + interp->set_arguments(OutputVector{image, target_shape, scales, axes}); + interp->set_attrs(attrs); + interp->validate_and_infer_types(); + + EXPECT_EQ(interp->get_element_type(), element::f32); + EXPECT_EQ(interp->get_shape(), (Shape{2, 2, 15, 30})); + EXPECT_THAT(get_shape_labels(interp->get_output_partial_shape(0)), Each(ov::no_label)); +} + TEST(type_prop, interpolate_v4) { auto image = std::make_shared(element::f32, Shape{2, 2, 30, 60}); auto target_shape = std::make_shared(element::i32, Shape{15, 30}); @@ -37,13 +148,16 @@ TEST(type_prop, interpolate_v4) { EXPECT_EQ(interp->get_element_type(), element::f32); EXPECT_EQ(interp->get_shape(), (Shape{2, 2, 15, 30})); + EXPECT_THAT(get_shape_labels(interp->get_output_partial_shape(0)), Each(ov::no_label)); } TEST(type_prop, interpolate_v4_non_constant_axes_scales) { - auto image = std::make_shared(element::f32, Shape{2, 2, 30, 60}); - auto target_shape = std::make_shared(element::i64, Shape{15, 30}); - auto scales = op::Constant::create(element::f32, Shape{2}, {0.5f, 0.5f}); + auto img_shape = PartialShape{2, 2, 30, 60}; + set_shape_labels(img_shape, 10); + auto image = std::make_shared(element::f16, img_shape); + auto target_shape = std::make_shared(element::i64, Shape{}); + auto scales = op::Constant::create(element::f32, Shape{2}, {0.5f, 0.5f}); auto axes = std::make_shared(element::i32, PartialShape{2}); InterpolateAttrs attrs; @@ -57,16 +171,18 @@ TEST(type_prop, interpolate_v4_non_constant_axes_scales) { attrs.cube_coeff = -0.75; auto interp = std::make_shared(image, target_shape, scales, axes, attrs); - EXPECT_EQ(interp->get_element_type(), element::f32); - auto dyn_dim = Dimension::dynamic(); - auto expected_shape = PartialShape{dyn_dim, dyn_dim, dyn_dim, dyn_dim}; - ASSERT_TRUE(interp->get_output_partial_shape(0).same_scheme(expected_shape)); + EXPECT_EQ(interp->get_element_type(), element::f16); + EXPECT_EQ(interp->get_output_partial_shape(0), PartialShape::dynamic(4)); + EXPECT_THAT(get_shape_labels(interp->get_output_partial_shape(0)), Each(ov::no_label)); } TEST(type_prop, interpolate_v4_non_constant_axes_sizes) { - auto image = std::make_shared(element::f32, Shape{2, 2, 30, 60}); - auto target_shape = std::make_shared(element::i64, Shape{15, 30}); - auto scales = op::Constant::create(element::f32, Shape{2}, {0.5f, 0.5f}); + auto img_shape = PartialShape{2, 2, 30, 60}; + set_shape_labels(img_shape, 10); + + auto image = std::make_shared(element::bf16, img_shape); + auto target_shape = std::make_shared(element::i64, Shape{2}); + auto scales = op::Constant::create(element::f32, Shape{2, 1}, {0.5f, 0.5f}); auto axes = std::make_shared(element::i32, PartialShape{2}); @@ -81,17 +197,14 @@ TEST(type_prop, interpolate_v4_non_constant_axes_sizes) { attrs.cube_coeff = -0.75; auto interp = std::make_shared(image, target_shape, scales, axes, attrs); - EXPECT_EQ(interp->get_element_type(), element::f32); - auto dyn_dim = Dimension::dynamic(); - auto expected_shape = PartialShape{dyn_dim, dyn_dim, dyn_dim, dyn_dim}; - ASSERT_TRUE(interp->get_output_partial_shape(0).same_scheme(expected_shape)); + EXPECT_EQ(interp->get_element_type(), element::bf16); + EXPECT_EQ(interp->get_output_partial_shape(0), PartialShape::dynamic(4)); + EXPECT_THAT(get_shape_labels(interp->get_output_partial_shape(0)), Each(ov::no_label)); } -TEST(type_prop, interpolate_v4_partial) { - auto partial_shape = PartialShape{2, 2, Dimension::dynamic(), Dimension::dynamic()}; - - auto image = std::make_shared(element::f32, partial_shape); - auto target_shape = std::make_shared(element::i32, Shape{15, 30}); +TEST(type_prop, interpolate_v4_img_dynamic_rank) { + auto image = std::make_shared(element::bf16, PartialShape::dynamic()); + auto target_shape = std::make_shared(element::i32, Shape{2}); auto scales = op::Constant::create(element::f32, Shape{2}, {0.5f, 0.5f}); auto axes = op::Constant::create(element::i64, Shape{2}, {2, 3}); @@ -106,20 +219,16 @@ TEST(type_prop, interpolate_v4_partial) { attrs.cube_coeff = -0.75; auto interp = std::make_shared(image, target_shape, scales, axes, attrs); - EXPECT_EQ(interp->get_element_type(), element::f32); - ASSERT_TRUE(interp->get_output_partial_shape(0).same_scheme(partial_shape)); - - // rank unknown - auto partial_param = std::make_shared(element::f32, PartialShape::dynamic()); - auto interp_part = std::make_shared(partial_param, target_shape, scales, axes, attrs); - ASSERT_TRUE(interp_part->get_output_partial_shape(0).same_scheme(PartialShape::dynamic())); + EXPECT_EQ(interp->get_element_type(), element::bf16); + EXPECT_EQ(interp->get_output_partial_shape(0), PartialShape::dynamic()); } TEST(type_prop, interpolate_v4_partial_static_rank) { - auto partial_shape = PartialShape{2, 2, Dimension::dynamic(), Dimension::dynamic()}; + auto img_shape = PartialShape{2, 2, -1, {5, 30}}; + set_shape_labels(img_shape, 10); - auto image = std::make_shared(element::f32, partial_shape); - auto target_shape = std::make_shared(element::i32, Shape{15, 30}); + auto image = std::make_shared(element::f32, img_shape); + auto target_shape = std::make_shared(element::i32, Shape{2}); auto scales = op::Constant::create(element::f32, Shape{2}, {0.5f, 0.5f}); auto axes = op::Constant::create(element::i64, Shape{2}, {2, 3}); @@ -129,22 +238,23 @@ TEST(type_prop, interpolate_v4_partial_static_rank) { attrs.coordinate_transformation_mode = CoordinateTransformMode::HALF_PIXEL; attrs.nearest_mode = Nearest_mode::ROUND_PREFER_FLOOR; attrs.antialias = false; - attrs.pads_begin = {0, 0, 0, 0}; - attrs.pads_end = {0, 0, 0, 0}; + attrs.pads_begin = {0, 1, 0, 0}; + attrs.pads_end = {0, 1, 0, 0}; attrs.cube_coeff = -0.75; auto interp = std::make_shared(image, target_shape, scales, axes, attrs); EXPECT_EQ(interp->get_element_type(), element::f32); - ASSERT_TRUE(interp->get_output_partial_shape(0).same_scheme(partial_shape)); - ASSERT_TRUE(interp->get_output_partial_shape(0).rank().is_static()); + EXPECT_EQ(interp->get_output_partial_shape(0), PartialShape({2, 4, -1, {2, 15}})); + EXPECT_THAT(get_shape_labels(interp->get_output_partial_shape(0)), + ElementsAre(10, ov::no_label, ov::no_label, ov::no_label)); } -TEST(type_prop, interpolate_v4_partial_static_rank2) { - auto partial_shape = PartialShape{Dimension::dynamic(), Dimension::dynamic(), 10, 20}; - auto out_shape = PartialShape{Dimension::dynamic(), Dimension::dynamic(), 5, 10}; +TEST(type_prop, interpolate_v4_img_intervals_use_scales) { + auto img_shape = PartialShape{{1, 2}, -1, 10, {5, 30}}; + set_shape_labels(img_shape, 10); - auto image = std::make_shared(element::f32, partial_shape); - auto target_shape = std::make_shared(element::i32, Shape{15, 30}); + auto image = std::make_shared(element::f32, img_shape); + auto target_shape = std::make_shared(element::i32, Shape{2}); auto scales = op::Constant::create(element::f32, Shape{2}, {0.5f, 0.5f}); auto axes = op::Constant::create(element::i64, Shape{2}, {2, 3}); @@ -154,28 +264,30 @@ TEST(type_prop, interpolate_v4_partial_static_rank2) { attrs.coordinate_transformation_mode = CoordinateTransformMode::HALF_PIXEL; attrs.nearest_mode = Nearest_mode::ROUND_PREFER_FLOOR; attrs.antialias = false; - attrs.pads_begin = {0, 0, 0, 0}; - attrs.pads_end = {0, 0, 0, 0}; + attrs.pads_begin = {0, 0, 0, 1}; + attrs.pads_end = {1, 1, 0, 1}; attrs.cube_coeff = -0.75; auto interp = std::make_shared(image, target_shape, scales, axes, attrs); EXPECT_EQ(interp->get_element_type(), element::f32); - ASSERT_TRUE(interp->get_output_partial_shape(0).same_scheme(out_shape)); - ASSERT_TRUE(interp->get_output_partial_shape(0).rank().is_static()); + EXPECT_EQ(interp->get_output_partial_shape(0), PartialShape({{2, 3}, {1, -1}, 5, {3, 16}})); + EXPECT_THAT(get_shape_labels(interp->get_output_partial_shape(0)), Each(ov::no_label)); } -TEST(type_prop, interpolate_v4_partial_static_rank3) { - auto partial_shape = PartialShape{Dimension::dynamic(), Dimension::dynamic(), 3, 3}; - auto out_shape = PartialShape{Dimension::dynamic(), Dimension::dynamic(), 1, 1}; +TEST(type_prop, interpolate_v4_use_sizes_as_shape_of) { + auto img_shape = PartialShape{{1, 2}, 10, 10, {5, 30}}; + auto out_shape = PartialShape{{2, 4}, -1}; + set_shape_labels(img_shape, 10); + set_shape_labels(out_shape, 20); - auto image = std::make_shared(element::f32, partial_shape); - auto target_shape = std::make_shared(element::i32, Shape{1, 1}); + auto image = std::make_shared(element::f32, img_shape); + auto target_shape = std::make_shared(std::make_shared(element::i32, out_shape)); auto scales = op::Constant::create(element::f32, Shape{2}, {1.0f / 3.0f, 1.0f / 3.0f}); - auto axes = op::Constant::create(element::i64, Shape{2}, {2, 3}); + auto axes = op::Constant::create(element::i64, Shape{2}, {3, 1}); InterpolateAttrs attrs; attrs.mode = InterpolateMode::NEAREST; - attrs.shape_calculation_mode = ShapeCalcMode::SCALES; + attrs.shape_calculation_mode = ShapeCalcMode::SIZES; attrs.coordinate_transformation_mode = CoordinateTransformMode::HALF_PIXEL; attrs.nearest_mode = Nearest_mode::ROUND_PREFER_FLOOR; attrs.antialias = false; @@ -185,20 +297,19 @@ TEST(type_prop, interpolate_v4_partial_static_rank3) { auto interp = std::make_shared(image, target_shape, scales, axes, attrs); EXPECT_EQ(interp->get_element_type(), element::f32); - ASSERT_TRUE(interp->get_output_partial_shape(0).same_scheme(out_shape)); - ASSERT_TRUE(interp->get_output_partial_shape(0).rank().is_static()); + EXPECT_EQ(interp->get_output_partial_shape(0), PartialShape({{1, 2}, -1, 10, {2, 4}})); + EXPECT_THAT(get_shape_labels(interp->get_output_partial_shape(0)), ElementsAre(10, 21, 12, 20)); } -TEST(type_prop, interpolate_v4_interval_logic) { - auto image = - std::make_shared(element::f32, - PartialShape{2, 2, Dimension(12, 800), Dimension(0, -1), Dimension(24, -1)}); +TEST(type_prop, interpolate_v4_use_scales_interval_shapes) { + auto img_shape = PartialShape{2, 2, {12, 800}, {0, -1}, {24, -1}}; + set_shape_labels(img_shape, 10); + + auto image = std::make_shared(element::f32, img_shape); auto target_shape = std::make_shared(element::i32, Shape{3}); auto scales = op::Constant::create(element::f32, Shape{3}, {0.5f, 0.25f, 0.125f}); auto axes = op::Constant::create(element::i64, Shape{3}, {2, 3, 4}); - const auto out_shape = PartialShape{2, 2, Dimension(6, 400), Dimension(0, -1), Dimension(3, -1)}; - InterpolateAttrs attrs; attrs.mode = InterpolateMode::NEAREST; attrs.shape_calculation_mode = ShapeCalcMode::SCALES; @@ -211,7 +322,9 @@ TEST(type_prop, interpolate_v4_interval_logic) { auto interp = std::make_shared(image, target_shape, scales, axes, attrs); EXPECT_EQ(interp->get_element_type(), element::f32); - ASSERT_TRUE(interp->get_output_partial_shape(0).same_scheme(out_shape)); + EXPECT_EQ(interp->get_output_partial_shape(0), PartialShape({2, 2, {6, 400}, -1, {3, -1}})); + EXPECT_THAT(get_shape_labels(interp->get_output_partial_shape(0)), + ElementsAre(10, 11, ov::no_label, ov::no_label, ov::no_label)); } TEST(type_prop, interpolate_v4_incorrect_mode) { @@ -236,6 +349,119 @@ TEST(type_prop, interpolate_v4_incorrect_mode) { HasSubstr("Unsupported interpolation mode used with version 4 of the Interpolate op")); } +TEST(type_prop, interpolate_v4_target_shape_not_1d) { + const auto image = std::make_shared(element::f32, Shape{1, 3, 30, 60}); + const auto scales = op::Constant::create(element::f32, Shape{2}, {6.f, 12.f}); + const auto axes = op::Constant::create(element::i64, Shape{2}, {2, 3}); + + ov::op::util::InterpolateBase::InterpolateAttrs attrs; + attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SIZES; + attrs.mode = ov::op::util::InterpolateBase::InterpolateMode::NEAREST; + attrs.pads_begin = {0, 0, 0, 0}; + attrs.pads_end = {0, 0, 0, 0}; + + OV_EXPECT_THROW(std::ignore = std::make_shared( + image, + std::make_shared(element::i32, Shape{1, 2}), + scales, + axes, + attrs), + ov::NodeValidationFailure, + HasSubstr("Input [1] is not rank 1")); + + OV_EXPECT_THROW( + std::ignore = std::make_shared(image, + std::make_shared(element::i32, Shape{}), + scales, + axes, + attrs), + ov::NodeValidationFailure, + HasSubstr("Input [1] is not rank 1")); +} + +TEST(type_prop, interpolate_v4_scales_not_1d) { + const auto image = std::make_shared(element::f32, Shape{1, 3, 30, 60}); + const auto target_shape = op::Constant::create(element::i32, Shape{2}, {10, 20}); + const auto scales = op::Constant::create(element::f32, Shape{2}, {6.f, 12.f}); + const auto axes = op::Constant::create(element::i64, Shape{2}, {2, 3}); + + ov::op::util::InterpolateBase::InterpolateAttrs attrs; + attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SCALES; + attrs.mode = ov::op::util::InterpolateBase::InterpolateMode::NEAREST; + attrs.pads_begin = {0, 0, 0, 0}; + attrs.pads_end = {0, 0, 0, 0}; + + OV_EXPECT_THROW(std::ignore = std::make_shared( + image, + target_shape, + std::make_shared(element::f32, Shape{1, 2}), + axes, + attrs), + ov::NodeValidationFailure, + HasSubstr("Input [2] is not rank 1")); + + OV_EXPECT_THROW( + std::ignore = std::make_shared(image, + target_shape, + std::make_shared(element::f32, Shape{}), + axes, + attrs), + ov::NodeValidationFailure, + HasSubstr("Input [2] is not rank 1")); +} + +TEST(type_prop, interpolate_v4_axes_not_1d) { + const auto image = std::make_shared(element::f32, Shape{1, 3, 30, 60}); + const auto target_shape = op::Constant::create(element::i32, Shape{2}, {10, 20}); + const auto scales = op::Constant::create(element::f32, Shape{2}, {6.f, 12.f}); + const auto axes = op::Constant::create(element::i64, Shape{2}, {2, 3}); + + ov::op::util::InterpolateBase::InterpolateAttrs attrs; + attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SCALES; + attrs.mode = ov::op::util::InterpolateBase::InterpolateMode::NEAREST; + attrs.pads_begin = {0, 0, 0, 0}; + attrs.pads_end = {0, 0, 0, 0}; + + OV_EXPECT_THROW(std::ignore = std::make_shared( + image, + target_shape, + scales, + std::make_shared(element::i32, Shape{1, 2}), + attrs), + ov::NodeValidationFailure, + HasSubstr("Input [3] is not rank 1")); + + OV_EXPECT_THROW(std::ignore = std::make_shared( + image, + target_shape, + scales, + std::make_shared(element::i32, Shape{1, 2}), + attrs), + ov::NodeValidationFailure, + HasSubstr("Input [3] is not rank 1")); +} + +// --- v11 --- +TEST(type_prop, interpolate_v11_default_ctor) { + auto image = std::make_shared(element::f32, Shape{1, 3, 30, 60}); + auto scales = op::Constant::create(element::f32, Shape{2}, {0.2f, 0.2f}); + auto axes = op::Constant::create(element::i64, Shape{2}, {2, 3}); + + ov::op::util::InterpolateBase::InterpolateAttrs attrs; + attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SCALES; + attrs.pads_begin = {1, 0, 0, 0}; + attrs.pads_end = {0, 1, 0, 0}; + + auto interp = std::make_shared(); + interp->set_arguments(OutputVector{image, scales, axes}); + interp->set_attrs(attrs); + interp->validate_and_infer_types(); + + EXPECT_EQ(interp->get_element_type(), element::f32); + EXPECT_EQ(interp->get_shape(), (Shape{2, 4, 6, 12})); + EXPECT_THAT(get_shape_labels(interp->get_output_partial_shape(0)), Each(ov::no_label)); +} + TEST(type_prop, interpolate_v11_scales) { const auto image = std::make_shared(element::f32, Shape{1, 3, 30, 60}); const auto scales = op::Constant::create(element::f32, Shape{2}, {0.2f, 0.2f}); @@ -251,6 +477,21 @@ TEST(type_prop, interpolate_v11_scales) { EXPECT_EQ(interp->get_shape(), (Shape{1, 3, 6, 12})); } +TEST(type_prop, interpolate_v11_scales_all_inputs_static_rank) { + const auto image = std::make_shared(element::f16, PartialShape::dynamic(8)); + const auto scales = std::make_shared(element::f32, PartialShape::dynamic(1)); + const auto axes = std::make_shared(element::i64, PartialShape::dynamic(1)); + + ov::op::util::InterpolateBase::InterpolateAttrs attrs; + attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SCALES; + attrs.pads_begin = {0, 0, 0, 0}; + attrs.pads_end = {0, 0, 0, 0}; + auto interp = std::make_shared(image, scales, axes, attrs); + + EXPECT_EQ(interp->get_element_type(), element::f16); + EXPECT_EQ(interp->get_output_partial_shape(0), PartialShape::dynamic(8)); +} + TEST(type_prop, interpolate_v11_sizes) { const auto image = std::make_shared(element::f32, Shape{1, 3, 30, 60}); const auto sizes = op::Constant::create(element::i32, Shape{2}, {6, 12}); @@ -266,6 +507,98 @@ TEST(type_prop, interpolate_v11_sizes) { EXPECT_EQ(interp->get_shape(), (Shape{1, 3, 6, 12})); } +TEST(type_prop, interpolate_v11_sizes_all_inputs_dynamic_rank) { + const auto image = std::make_shared(element::f32, PartialShape::dynamic()); + const auto sizes = std::make_shared(element::i32, PartialShape::dynamic()); + const auto axes = std::make_shared(element::i64, PartialShape::dynamic()); + + ov::op::util::InterpolateBase::InterpolateAttrs attrs; + attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SIZES; + attrs.pads_begin = {0, 0, 0, 0}; + attrs.pads_end = {0, 0, 0, 0}; + auto interp = std::make_shared(image, sizes, axes, attrs); + + EXPECT_EQ(interp->get_element_type(), element::f32); + EXPECT_EQ(interp->get_output_partial_shape(0), PartialShape::dynamic()); +} + +TEST(type_prop, interpolate_v11_intervals_with_scales_mode) { + auto img_shape = PartialShape{{1, 3}, 3, {1, 10}, {10, -1}, {10, 20}}; + set_shape_labels(img_shape, 10); + + const auto image = std::make_shared(element::f32, img_shape); + const auto scales = op::Constant::create(element::f32, Shape{3}, {2.0f, 3.0f, 1.0f}); + const auto axes = op::Constant::create(element::i64, Shape{3}, {2, 3, 4}); + + ov::op::util::InterpolateBase::InterpolateAttrs attrs; + attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SCALES; + attrs.pads_begin = {0, 2, 1, 0, 0}; + attrs.pads_end = {1, 1, 0, 1, 0}; + auto interp = std::make_shared(image, scales, axes, attrs); + + EXPECT_EQ(interp->get_output_partial_shape(0), PartialShape({{2, 4}, 6, {4, 22}, {33, -1}, {10, 20}})); + EXPECT_THAT(get_shape_labels(interp->get_output_partial_shape(0)), + ElementsAre(ov::no_label, ov::no_label, ov::no_label, ov::no_label, 14)); +} + +TEST(type_prop, interpolate_v11_intervals_with_sizes_mode) { + auto img_shape = PartialShape{{1, 3}, 3, {1, 10}, {10, -1}}; + set_shape_labels(img_shape, 10); + + const auto image = std::make_shared(element::f32, img_shape); + const auto sizes = op::Constant::create(element::i32, Shape{2}, {200, 300}); + const auto axes = op::Constant::create(element::i64, Shape{2}, {2, 3}); + + ov::op::util::InterpolateBase::InterpolateAttrs attrs; + attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SIZES; + attrs.pads_begin = {0, 0, 0, 0}; + attrs.pads_end = {0, 0, 0, 0}; + auto interp = std::make_shared(image, sizes, axes, attrs); + + EXPECT_EQ(interp->get_output_partial_shape(0), PartialShape({{1, 3}, 3, 200, 300})); + EXPECT_THAT(get_shape_labels(interp->get_output_partial_shape(0)), ElementsAre(10, 11, ov::no_label, ov::no_label)); +} + +TEST(type_prop, interpolate_v11_sizes_with_shapeof) { + auto img_shape = PartialShape{{1, 3}, 3, {1, 10}, {10, -1}}; + auto sizes_shape = PartialShape{{12, 37}, {0, 21}}; + set_shape_labels(img_shape, 10); + set_shape_labels(sizes_shape, 20); + + const auto image = std::make_shared(element::f32, img_shape); + const auto param = std::make_shared(element::f32, sizes_shape); + const auto sizes = std::make_shared(param); + const auto axes = op::Constant::create(element::i64, Shape{2}, {2, 1}); + + ov::op::util::InterpolateBase::InterpolateAttrs attrs; + attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SIZES; + attrs.pads_begin = {0, 0, 0, 0}; + attrs.pads_end = {0, 0, 0, 0}; + auto interp = std::make_shared(image, sizes, axes, attrs); + + EXPECT_EQ(interp->get_output_partial_shape(0), (PartialShape{{1, 3}, {0, 21}, {12, 37}, {10, -1}})); + EXPECT_THAT(get_shape_labels(interp->get_output_partial_shape(0)), ElementsAre(10, 21, 20, 13)); +} + +TEST(type_prop, interpolate_v11_non_constant_axes_scales) { + auto img_shape = PartialShape{2, 2, 30, 60}; + set_shape_labels(img_shape, 10); + + auto image = std::make_shared(element::f16, img_shape); + auto scales = op::Constant::create(element::f32, Shape{2}, {0.5f, 0.5f}); + auto axes = std::make_shared(element::i32, PartialShape{2}); + + ov::op::util::InterpolateBase::InterpolateAttrs attrs; + attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SCALES; + attrs.pads_begin = {0, 0, 0, 0}; + attrs.pads_end = {0, 0, 0, 0}; + auto interp = std::make_shared(image, scales, axes, attrs); + + EXPECT_EQ(interp->get_element_type(), element::f16); + EXPECT_EQ(interp->get_output_partial_shape(0), PartialShape::dynamic(4)); + EXPECT_THAT(get_shape_labels(interp->get_output_partial_shape(0)), Each(ov::no_label)); +} + TEST(type_prop, interpolate_v11_scales_incorrect_et) { const auto image = std::make_shared(element::f32, Shape{1, 3, 30, 60}); const auto scales = op::Constant::create(element::i64, Shape{2}, {2, 2}); @@ -296,49 +629,6 @@ TEST(type_prop, interpolate_v11_sizes_incorrect_et) { HasSubstr("Sizes element type must be i32, i64, u32 or u64")); } -TEST(type_prop, interpolate_v11_intervals_with_scales_mode) { - const auto image = std::make_shared(element::f32, PartialShape{1, 3, {1, 10}, {1, 10}}); - const auto scales = op::Constant::create(element::f32, Shape{2}, {2.0f, 3.0f}); - const auto axes = op::Constant::create(element::i64, Shape{2}, {2, 3}); - - ov::op::util::InterpolateBase::InterpolateAttrs attrs; - attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SCALES; - attrs.pads_begin = {0, 0, 0, 0}; - attrs.pads_end = {0, 0, 0, 0}; - auto interp = std::make_shared(image, scales, axes, attrs); - - EXPECT_EQ(interp->get_output_partial_shape(0), (PartialShape{1, 3, {2, 20}, {3, 30}})); -} - -TEST(type_prop, interpolate_v11_intervals_with_sizes_mode) { - const auto image = std::make_shared(element::f32, PartialShape{1, 3, {1, 10}, {1, 10}}); - const auto sizes = op::Constant::create(element::i32, Shape{2}, {200, 300}); - const auto axes = op::Constant::create(element::i64, Shape{2}, {2, 3}); - - ov::op::util::InterpolateBase::InterpolateAttrs attrs; - attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SIZES; - attrs.pads_begin = {0, 0, 0, 0}; - attrs.pads_end = {0, 0, 0, 0}; - auto interp = std::make_shared(image, sizes, axes, attrs); - - EXPECT_EQ(interp->get_output_partial_shape(0), (PartialShape{1, 3, 200, 300})); -} - -TEST(type_prop, interpolate_v11_sizes_with_shapeof) { - const auto image = std::make_shared(element::f32, PartialShape{1, 200, 100, 3}); - const auto param = std::make_shared(element::f32, PartialShape{37, 21}); - const auto sizes = std::make_shared(param); - const auto axes = op::Constant::create(element::i64, Shape{2}, {2, 1}); - - ov::op::util::InterpolateBase::InterpolateAttrs attrs; - attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SIZES; - attrs.pads_begin = {0, 0, 0, 0}; - attrs.pads_end = {0, 0, 0, 0}; - auto interp = std::make_shared(image, sizes, axes, attrs); - - EXPECT_EQ(interp->get_output_partial_shape(0), (PartialShape{1, 21, 37, 3})); -} - TEST(type_prop, interpolate_v11_scales_incorrect_number) { const auto image = std::make_shared(element::f32, Shape{1, 3, 30, 60}); const auto scales = op::Constant::create(element::f32, Shape{2}, {0.2f, 0.2f}); @@ -347,10 +637,9 @@ TEST(type_prop, interpolate_v11_scales_incorrect_number) { attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SCALES; attrs.pads_begin = {0, 0, 0, 0}; attrs.pads_end = {0, 0, 0, 0}; - OV_EXPECT_THROW( - auto interp = std::make_shared(image, scales, attrs), - ov::NodeValidationFailure, - HasSubstr("The number of elements in the 'scales_or_sizes' input does not match the number of axes")); + OV_EXPECT_THROW(auto interp = std::make_shared(image, scales, attrs), + ov::NodeValidationFailure, + HasSubstr("The number of elements in the 'scales' input does not match the number of axes")); } TEST(type_prop, interpolate_v11_sizes_incorrect_number) { @@ -361,8 +650,60 @@ TEST(type_prop, interpolate_v11_sizes_incorrect_number) { attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SIZES; attrs.pads_begin = {0, 0, 0, 0}; attrs.pads_end = {0, 0, 0, 0}; - OV_EXPECT_THROW( - auto interp = std::make_shared(image, sizes, attrs), - ov::NodeValidationFailure, - HasSubstr("The number of elements in the 'scales_or_sizes' input does not match the number of axes")); + OV_EXPECT_THROW(auto interp = std::make_shared(image, sizes, attrs), + ov::NodeValidationFailure, + HasSubstr("The number of elements in the 'sizes' input does not match the number of axes")); +} + +TEST(type_prop, interpolate_v11_scales_not_1d) { + const auto image = std::make_shared(element::f32, Shape{1, 3, 30, 60}); + const auto axes = op::Constant::create(element::i32, Shape{2}, {2, 3}); + + ov::op::util::InterpolateBase::InterpolateAttrs attrs; + attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SCALES; + attrs.pads_begin = {0, 0, 0, 0}; + attrs.pads_end = {0, 0, 0, 0}; + + OV_EXPECT_THROW(std::ignore = std::make_shared( + image, + std::make_shared(element::f32, Shape{1, 2}), + axes, + attrs), + ov::NodeValidationFailure, + HasSubstr("Input [1] is not rank 1")); + + OV_EXPECT_THROW( + std::ignore = std::make_shared(image, + std::make_shared(element::f32, Shape{}), + axes, + attrs), + ov::NodeValidationFailure, + HasSubstr("Input [1] is not rank 1")); +} + +TEST(type_prop, interpolate_v11_axes_not_1d) { + const auto image = std::make_shared(element::f32, Shape{1, 3, 30, 60}); + const auto scales = op::Constant::create(element::f32, Shape{2}, {6.f, 12.f}); + + ov::op::util::InterpolateBase::InterpolateAttrs attrs; + attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SCALES; + attrs.mode = ov::op::util::InterpolateBase::InterpolateMode::NEAREST; + attrs.pads_begin = {0, 0, 0, 0}; + attrs.pads_end = {0, 0, 0, 0}; + + OV_EXPECT_THROW(std::ignore = std::make_shared( + image, + scales, + std::make_shared(element::i32, Shape{1, 2}), + attrs), + ov::NodeValidationFailure, + HasSubstr("Input [2] is not rank 1")); + + OV_EXPECT_THROW( + std::ignore = std::make_shared(image, + scales, + std::make_shared(element::i32, Shape{}), + attrs), + ov::NodeValidationFailure, + HasSubstr("Input [2] is not rank 1")); } 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 77a6155084c..0ec92dfb2cb 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 @@ -263,22 +263,6 @@ public: } }; -template -class entryInterpolate : public entryBase { -public: - using entryBase::entryBase; - - IShapeInferCommon::Result - infer(const std::vector& input_shapes, const std::map& constant_data) override { - std::vector pads_begin, pads_end; - auto op = static_cast(node.get()); - std::vector output_shapes(op->get_output_size()); - correct_pads_attr(op, pads_begin, pads_end, input_shapes); - shape_infer(op, pads_begin, pads_end, input_shapes, output_shapes, constant_data); - return {std::move(output_shapes), ShapeInferStatus::success}; - } -}; - template class ShapeInferWithPadding : public entryBase { public: @@ -304,7 +288,7 @@ protected: }; /** - * @brief Base shape inference object implementing the IStaticShapeInfer without padding support + * @brief Base shape inference object implementing the IStaticShapeInfer without padding support. */ class ShapeInferBase : public IStaticShapeInfer { public: @@ -389,6 +373,50 @@ public: } }; +/** @brief Base shape inference object implementing the IStaticShapeInfer with padding support. */ +class ShapeInferPaddingBase : public ShapeInferBase { +public: + ShapeInferPaddingBase(std::shared_ptr node) : ShapeInferBase(std::move(node)), m_pads_begin{}, m_pads_end{} {} + + IShapeInferCommon::Result infer(const std::vector& input_shapes, + const ITensorAccessor& tensor_accessor) override { + OPENVINO_THROW("Not implemented by base class"); + } + + const ov::CoordinateDiff& get_pads_begin() override { + return m_pads_begin; + } + + const ov::CoordinateDiff& get_pads_end() override { + return m_pads_end; + } + +protected: + ov::CoordinateDiff m_pads_begin, m_pads_end; +}; + +/** + * @brief Shape inference using tensor accessor to get constant data and padding + * + * @tparam TOp Type of operator. + * @tparam MASK The bit mask where each bit corresponds to an input port number. + */ +template +class ShapeInferPaddingTA : public ShapeInferPaddingBase { +public: + using ShapeInferPaddingBase::ShapeInferPaddingBase; + + IShapeInferCommon::Result infer(const std::vector& input_shapes, + const ov::ITensorAccessor& tensor_accessor) override { + return {shape_infer(static_cast(m_node.get()), input_shapes, m_pads_begin, m_pads_end, tensor_accessor), + ShapeInferStatus::success}; + } + + port_mask_t get_port_mask() const override { + return MASK; + } +}; + /** * \brief Shape infer factory * @@ -531,7 +559,6 @@ const IShapeInferCommonFactory::TRegistry IShapeInferCommonFactory::registry{ _OV_OP_SHAPE_INFER_REG(GRUCell, entryIO), _OV_OP_SHAPE_INFER_REG(GRUSequence, entryIO), _OV_OP_SHAPE_INFER_REG(IDFT, entryIOC), - _OV_OP_SHAPE_INFER_REG(Interpolate, entryInterpolate), _OV_OP_SHAPE_INFER_REG(IRDFT, entryIOC), _OV_OP_SHAPE_INFER_REG(LSTMCell, entryIO), _OV_OP_SHAPE_INFER_REG(MatMul, entryIO), @@ -585,7 +612,6 @@ const IShapeInferCommonFactory::TRegistry IShapeInferCommonFactory::registry{ _OV_OP_SHAPE_INFER_REG(opset1::Broadcast, entryIOC), _OV_OP_SHAPE_INFER_REG(opset1::DeformableConvolution, ShapeInferWithPadding), _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, ShapeInferWithPadding), _OV_OP_SHAPE_INFER_REG(opset1::Range, entryIOC), @@ -605,6 +631,7 @@ template <> const IStaticShapeInferFactory::TRegistry IStaticShapeInferFactory::registry{ // Default opset _OV_OP_SHAPE_INFER_MASK_REG(ExperimentalDetectronROIFeatureExtractor, ShapeInferTA, util::bit::mask()), + _OV_OP_SHAPE_INFER_MASK_REG(Interpolate, ShapeInferPaddingTA, util::bit::mask(1, 2, 3)), _OV_OP_SHAPE_INFER_MASK_REG(Proposal, ShapeInferTA, util::bit::mask()), _OV_OP_SHAPE_INFER_VA_REG(ReduceL1, ShapeInferTA, op::util::ArithmeticReductionKeepDims, util::bit::mask(1)), _OV_OP_SHAPE_INFER_VA_REG(ReduceL2, ShapeInferTA, op::util::ArithmeticReductionKeepDims, util::bit::mask(1)), @@ -618,6 +645,7 @@ const IStaticShapeInferFactory::TRegistry IStaticShapeInferFactory::registry{ _OV_OP_SHAPE_INFER_MASK_REG(Tile, ShapeInferTA, util::bit::mask(1)), // Operators shape inferences for specific opset version should be specified below // opset1 + _OV_OP_SHAPE_INFER_MASK_REG(opset1::Interpolate, ShapeInferTA, util::bit::mask(1)), _OV_OP_SHAPE_INFER_MASK_REG(opset1::Proposal, ShapeInferTA, util::bit::mask()), _OV_OP_SHAPE_INFER_MASK_REG(opset1::Reverse, ShapeInferTA, util::bit::mask(1)), }; diff --git a/src/plugins/intel_cpu/tests/unit/shape_inference_test/interpolate_shape_inference.cpp b/src/plugins/intel_cpu/tests/unit/shape_inference_test/interpolate_shape_inference.cpp deleted file mode 100644 index e5016f58511..00000000000 --- a/src/plugins/intel_cpu/tests/unit/shape_inference_test/interpolate_shape_inference.cpp +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (C) 2018-2023 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include - -#include "utils.hpp" - -using namespace ov; -using namespace ov::intel_cpu; - -using InterpolateMode = op::v4::Interpolate::InterpolateMode; -using CoordinateTransformMode = op::v4::Interpolate::CoordinateTransformMode; -using Nearest_mode = op::v4::Interpolate::NearestMode; -using ShapeCalcMode = op::v4::Interpolate::ShapeCalcMode; - -static std::shared_ptr build_InterpolateV4() { - op::v4::Interpolate::InterpolateAttrs attrs; - attrs.mode = InterpolateMode::NEAREST; - attrs.shape_calculation_mode = ShapeCalcMode::SCALES; - attrs.coordinate_transformation_mode = CoordinateTransformMode::HALF_PIXEL; - attrs.nearest_mode = Nearest_mode::ROUND_PREFER_FLOOR; - attrs.antialias = false; - attrs.pads_begin = {0, 0, 0, 0}; - attrs.pads_end = {0, 0, 0, 0}; - attrs.cube_coeff = -0.75; - - auto input_data = std::make_shared(element::f32, PartialShape{-1, -1, -1, -1}); - auto sizes = std::make_shared(element::i32, PartialShape::dynamic()); - auto scales = std::make_shared(element::f32, PartialShape::dynamic()); - auto axes = std::make_shared(element::i32, PartialShape::dynamic()); - - auto interpolate = std::make_shared(input_data, sizes, scales, axes, attrs); - return interpolate; -} - -static std::shared_ptr build_InterpolateV4ConstantInput() { - op::v4::Interpolate::InterpolateAttrs attrs; - attrs.mode = InterpolateMode::NEAREST; - attrs.shape_calculation_mode = ShapeCalcMode::SCALES; - attrs.coordinate_transformation_mode = CoordinateTransformMode::HALF_PIXEL; - attrs.nearest_mode = Nearest_mode::ROUND_PREFER_FLOOR; - attrs.antialias = false; - attrs.pads_begin = {0, 0, 0, 0}; - attrs.pads_end = {0, 0, 0, 0}; - attrs.cube_coeff = -0.75; - - auto input_data = std::make_shared(element::f32, PartialShape{-1, -1, -1, -1}); - auto sizes = std::make_shared(element::i32, Shape{2}, std::vector{24, 160}); - auto scales = std::make_shared(element::f32, Shape{2}, std::vector{2.0, 0.5}); - auto axes = std::make_shared(element::i32, Shape{2}, std::vector{2, 3}); - - auto interpolate = std::make_shared(input_data, sizes, scales, axes, attrs); - return interpolate; -} - -static std::shared_ptr build_InterpolateV0() { - ov::op::v0::Interpolate::Attributes attrs; - attrs.axes = {2, 3}; - attrs.mode = "nearest"; - attrs.align_corners = true; - attrs.antialias = false; - attrs.pads_begin = {0, 0, 0, 0}; - attrs.pads_end = {0, 0, 0, 0}; - - auto input_data = std::make_shared(element::f32, PartialShape{-1, -1, -1, -1}); - auto sizes = std::make_shared(element::i32, PartialShape::dynamic()); - - auto interpolate_v0 = std::make_shared(input_data, sizes, attrs); - return interpolate_v0; -} - -TEST(StaticShapeInferenceTest, InterpolateV4Test) { - auto interpolate = build_InterpolateV4(); - - int32_t sizes_val[] = {24, 160}; - float scales_val[] = {2.0, 0.5}; - int32_t axes_val[] = {2, 3}; - std::map> constant_data; - constant_data[1] = std::make_shared(ngraph::element::Type_t::i32, Shape{2}, sizes_val); - constant_data[2] = std::make_shared(element::f32, Shape{2}, scales_val); - constant_data[3] = std::make_shared(element::i32, Shape{2}, axes_val); - - std::vector static_input_shapes = {StaticShape{1, 2, 48, 80}, - StaticShape{2}, - StaticShape{2}, - StaticShape{2}}, - static_output_shapes = {StaticShape{}}; - - shape_inference(interpolate.get(), static_input_shapes, static_output_shapes, constant_data); - ASSERT_EQ(static_output_shapes[0], StaticShape({1, 2, 96, 40})); -} - -TEST(StaticShapeInferenceTest, InterpolateV4ConstantInputTest) { - auto interpolate = build_InterpolateV4ConstantInput(); - - std::vector static_input_shapes = {StaticShape{1, 2, 50, 80}, - StaticShape{2}, - StaticShape{2}, - StaticShape{2}}, - static_output_shapes = {StaticShape{}}; - - shape_inference(interpolate.get(), static_input_shapes, static_output_shapes); - ASSERT_EQ(static_output_shapes[0], StaticShape({1, 2, 100, 40})); -} - -TEST(StaticShapeInferenceTest, InterpolateV4MissingConstantTest) { - auto interpolate = build_InterpolateV4(); - - int32_t sizes_val[] = {24, 160}; - float scales_val[] = {2.0, 0.5}; - std::map> constant_data; - constant_data[1] = std::make_shared(ngraph::element::Type_t::i32, Shape{2}, sizes_val); - constant_data[2] = std::make_shared(element::f32, Shape{2}, scales_val); - - std::vector static_input_shapes = {StaticShape{1, 2, 48, 80}, - StaticShape{2}, - StaticShape{2}, - StaticShape{2}}, - static_output_shapes = {StaticShape{}}; - - EXPECT_THROW(shape_inference(interpolate.get(), static_input_shapes, static_output_shapes, constant_data), - NodeValidationFailure); -} - -TEST(StaticShapeInferenceTest, InterpolateV0Test) { - auto interpolate = build_InterpolateV0(); - - int32_t sizes_val[] = {15, 30}; - std::map> constant_data; - constant_data[1] = std::make_shared(ngraph::element::Type_t::i32, Shape{2}, sizes_val); - - std::vector static_input_shapes = {StaticShape{2, 2, 33, 65}, StaticShape{2}}, - static_output_shapes = {StaticShape{}}; - shape_inference(interpolate.get(), static_input_shapes, static_output_shapes, constant_data); - ASSERT_EQ(static_output_shapes[0], StaticShape({2, 2, 15, 30})); -} - -TEST(StaticShapeInferenceTest, InterpolateV0MissingConstantTest) { - auto interpolate = build_InterpolateV0(); - - std::vector static_input_shapes = {StaticShape{2, 2, 33, 65}, StaticShape{2}}, - static_output_shapes = {StaticShape{}}; - EXPECT_THROW(shape_inference(interpolate.get(), static_input_shapes, static_output_shapes), NodeValidationFailure); -} diff --git a/src/plugins/intel_cpu/tests/unit/shape_inference_test/interpolate_shape_inference_test.cpp b/src/plugins/intel_cpu/tests/unit/shape_inference_test/interpolate_shape_inference_test.cpp new file mode 100644 index 00000000000..4e484107bbf --- /dev/null +++ b/src/plugins/intel_cpu/tests/unit/shape_inference_test/interpolate_shape_inference_test.cpp @@ -0,0 +1,301 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include + +#include "utils.hpp" + +using namespace ov; +using namespace ov::intel_cpu; + +// --- v0 --- +class InterpolateV0StaticShapeInferenceTest : public OpStaticShapeInferenceTest { +protected: + using Attrs = typename op_type::Attributes; + + void SetUp() override { + output_shapes.resize(1); + } + + Attrs attrs; +}; + +TEST_F(InterpolateV0StaticShapeInferenceTest, default_ctor_no_attributes) { + attrs.axes = AxisSet{2, 0, 5}; + + op = make_op(); + op->set_attrs(attrs); + + int32_t out_shape_v[] = {10, 20, 30}; + const auto const_data = + std::map{{1, std::make_shared(element::i32, Shape{3}, out_shape_v)}}; + + input_shapes = ShapeVector{{5, 2, 128, 128, 128, 64}, {3}}; + shape_inference(op.get(), input_shapes, output_shapes, const_data); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({10, 2, 20, 128, 128, 30})); +} + +TEST_F(InterpolateV0StaticShapeInferenceTest, out_shape_as_constant) { + attrs.axes = AxisSet{1, 3}; + + const auto img = std::make_shared(element::f32, PartialShape::dynamic()); + const auto out_shape = op::v0::Constant::create(element::i64, Shape{2}, {100, 100}); + op = make_op(img, out_shape, attrs); + + input_shapes = ShapeVector{{5, 2, 128, 128, 128}, {2}}; + shape_inference(op.get(), input_shapes, output_shapes); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({5, 100, 128, 100, 128})); +} + +TEST_F(InterpolateV0StaticShapeInferenceTest, all_inputs_dynamic_rank_use_scales) { + attrs.axes = AxisSet{2, 4, 5}; + + const auto img = std::make_shared(element::f32, PartialShape::dynamic()); + const auto out_shape = std::make_shared(element::i32, PartialShape::dynamic()); + op = make_op(img, out_shape, attrs); + + int32_t out_shape_v[] = {10, 20, 30}; + const auto const_data = + std::map{{1, std::make_shared(element::i32, Shape{3}, out_shape_v)}}; + + input_shapes = ShapeVector{{5, 2, 128, 128, 128, 64}, {3}}; + shape_inference(op.get(), input_shapes, output_shapes, const_data); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({5, 2, 10, 128, 20, 30})); +} + +TEST_F(InterpolateV0StaticShapeInferenceTest, all_inputs_static_rank_use_sizes) { + attrs.axes = AxisSet{0, 1, 2}; + + const auto img = std::make_shared(element::f32, PartialShape::dynamic(6)); + const auto out_shape = std::make_shared(element::i32, PartialShape::dynamic(1)); + op = make_op(img, out_shape, attrs); + + int32_t out_shape_v[] = {10, 20, 30}; + const auto const_data = + std::map{{1, std::make_shared(element::i32, Shape{3}, out_shape_v)}}; + + input_shapes = ShapeVector{{5, 2, 128, 128, 128, 64}, {3}}; + shape_inference(op.get(), input_shapes, output_shapes, const_data); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({10, 20, 30, 128, 128, 64})); +} + +// --- v4 --- +class InterpolateV4StaticShapeInferenceTest : public OpStaticShapeInferenceTest { +protected: + using Attrs = typename op_type::InterpolateAttrs; + using ShapeCalcMode = typename op_type::ShapeCalcMode; + + void SetUp() override { + output_shapes.resize(1); + } + + Attrs attrs; +}; + +TEST_F(InterpolateV4StaticShapeInferenceTest, default_ctor_no_attributes) { + attrs.shape_calculation_mode = ShapeCalcMode::SCALES; + + op = make_op(); + op->set_attrs(attrs); + + float scales_v[] = {1.5f, 3.0f, 0.2f}; + int32_t axes_v[] = {2, 0, 5}; + const auto const_data = + std::map{{2, std::make_shared(element::f32, Shape{3}, scales_v)}, + {3, std::make_shared(element::i32, Shape{3}, axes_v)}}; + + input_shapes = ShapeVector{{5, 2, 128, 128, 128, 64}, {3}, {3}, {3}}; + shape_inference(op.get(), input_shapes, output_shapes, const_data); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({15, 2, 192, 128, 128, 12})); +} + +TEST_F(InterpolateV4StaticShapeInferenceTest, scales_as_constant) { + attrs.shape_calculation_mode = ShapeCalcMode::SCALES; + + const auto img = std::make_shared(element::f32, PartialShape::dynamic()); + const auto sizes = std::make_shared(element::i32, PartialShape{1}); + const auto scales = op::v0::Constant::create(element::f32, Shape{2}, {2.0f, 0.7f}); + const auto axes = op::v0::Constant::create(element::i64, Shape{2}, {1, 3}); + op = make_op(img, sizes, scales, axes, attrs); + + input_shapes = ShapeVector{{5, 2, 128, 128, 128}, {1}, {2}, {2}}; + shape_inference(op.get(), input_shapes, output_shapes); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({5, 4, 128, 89, 128})); +} + +TEST_F(InterpolateV4StaticShapeInferenceTest, sizes_as_constant) { + const auto img = std::make_shared(element::f32, PartialShape::dynamic()); + const auto sizes = op::v0::Constant::create(element::i32, Shape{2}, {10, 5}); + const auto scales = std::make_shared(element::f32, PartialShape{1}); + const auto axes = op::v0::Constant::create(element::i64, Shape{2}, {3, 1}); + op = make_op(img, sizes, scales, axes, attrs); + + input_shapes = ShapeVector{{5, 2, 128, 128, 128}, {2}, {1}, {2}}; + shape_inference(op.get(), input_shapes, output_shapes); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({5, 5, 128, 10, 128})); +} + +TEST_F(InterpolateV4StaticShapeInferenceTest, all_inputs_dynamic_rank_use_scales) { + attrs.shape_calculation_mode = ShapeCalcMode::SCALES; + attrs.pads_end = std::vector(5, 1); + + const auto img = std::make_shared(element::f32, PartialShape::dynamic()); + const auto sizes = std::make_shared(element::i32, PartialShape::dynamic()); + const auto scales = std::make_shared(element::f32, PartialShape::dynamic()); + const auto axes = std::make_shared(element::i32, PartialShape::dynamic()); + op = make_op(img, sizes, scales, axes, attrs); + + float scales_v[] = {1.5f, 3.0f, 0.2f}; + int32_t axes_v[] = {2, 0, 5}; + const auto const_data = + std::map{{2, std::make_shared(element::f32, Shape{3}, scales_v)}, + {3, std::make_shared(element::i32, Shape{3}, axes_v)}}; + + input_shapes = ShapeVector{{5, 2, 128, 128, 128, 64}, {3}, {3}, {3}}; + shape_inference(op.get(), input_shapes, output_shapes, const_data); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({18, 3, 193, 129, 129, 12})); +} + +TEST_F(InterpolateV4StaticShapeInferenceTest, all_inputs_static_rank_use_sizes) { + attrs.shape_calculation_mode = ShapeCalcMode::SIZES; + + const auto img = std::make_shared(element::f32, PartialShape::dynamic(5)); + const auto sizes = std::make_shared(element::i32, PartialShape::dynamic(1)); + const auto scales = std::make_shared(element::f32, PartialShape::dynamic(1)); + const auto axes = std::make_shared(element::i32, PartialShape::dynamic(1)); + op = make_op(img, sizes, scales, axes, attrs); + + int32_t sizes_v[] = {10, 50, 60}; + int32_t axes_v[] = {1, 0, 3}; + const auto const_data = + std::map{{1, std::make_shared(element::i32, Shape{3}, sizes_v)}, + {3, std::make_shared(element::i32, Shape{3}, axes_v)}}; + + input_shapes = ShapeVector{{5, 2, 128, 128, 128, 64}, {3}, {3}, {3}}; + shape_inference(op.get(), input_shapes, output_shapes, const_data); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({50, 10, 128, 60, 128, 64})); +} + +// --- v11 --- +class InterpolateV11StaticShapeInferenceTest : public OpStaticShapeInferenceTest { +protected: + using Attrs = typename op_type::InterpolateAttrs; + using ShapeCalcMode = typename op_type::ShapeCalcMode; + + void SetUp() override { + output_shapes.resize(1); + } + + Attrs attrs; +}; + +TEST_F(InterpolateV11StaticShapeInferenceTest, default_ctor_no_attributes) { + GTEST_SKIP() << "Enable test when v11 opset will be added to shape inference factory."; + attrs.shape_calculation_mode = ShapeCalcMode::SCALES; + + op = make_op(); + op->set_attrs(attrs); + + float scales_v[] = {1.5f, 3.0f, 0.2f}; + int32_t axes_v[] = {2, 0, 5}; + const auto const_data = + std::map{{1, std::make_shared(element::f32, Shape{3}, scales_v)}, + {2, std::make_shared(element::i32, Shape{3}, axes_v)}}; + + input_shapes = ShapeVector{{5, 2, 128, 128, 128, 64}, {3}, {3}}; + shape_inference(op.get(), input_shapes, output_shapes, const_data); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({15, 2, 192, 128, 128, 12})); +} + +TEST_F(InterpolateV11StaticShapeInferenceTest, scales_as_constant) { + attrs.shape_calculation_mode = ShapeCalcMode::SCALES; + + const auto img = std::make_shared(element::f32, PartialShape::dynamic()); + const auto scales = op::v0::Constant::create(element::f32, Shape{2}, {2.0f, 0.7f}); + const auto axes = op::v0::Constant::create(element::i64, Shape{2}, {1, 3}); + op = make_op(img, scales, axes, attrs); + + input_shapes = ShapeVector{{5, 2, 128, 128, 128}, {2}, {2}}; + shape_inference(op.get(), input_shapes, output_shapes); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({5, 4, 128, 89, 128})); +} + +TEST_F(InterpolateV11StaticShapeInferenceTest, sizes_as_constant) { + const auto img = std::make_shared(element::f32, PartialShape::dynamic()); + const auto sizes = op::v0::Constant::create(element::i32, Shape{2}, {10, 5}); + const auto axes = op::v0::Constant::create(element::i64, Shape{2}, {3, 1}); + op = make_op(img, sizes, axes, attrs); + + input_shapes = ShapeVector{{5, 2, 128, 128, 128}, {2}, {2}}; + shape_inference(op.get(), input_shapes, output_shapes); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({5, 5, 128, 10, 128})); +} + +TEST_F(InterpolateV11StaticShapeInferenceTest, all_inputs_dynamic_rank_use_scales) { + attrs.shape_calculation_mode = ShapeCalcMode::SCALES; + + const auto img = std::make_shared(element::f32, PartialShape::dynamic()); + const auto scales = std::make_shared(element::f32, PartialShape::dynamic()); + const auto axes = std::make_shared(element::i32, PartialShape::dynamic()); + op = make_op(img, scales, axes, attrs); + + float scales_v[] = {1.5f, 3.0f, 0.2f}; + int32_t axes_v[] = {2, 0, 5}; + const auto const_data = + std::map{{1, std::make_shared(element::f32, Shape{3}, scales_v)}, + {2, std::make_shared(element::i32, Shape{3}, axes_v)}}; + + input_shapes = ShapeVector{{5, 2, 128, 128, 128, 64}, {3}, {3}}; + shape_inference(op.get(), input_shapes, output_shapes, const_data); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({15, 2, 192, 128, 128, 12})); +} + +TEST_F(InterpolateV11StaticShapeInferenceTest, all_inputs_static_rank_use_sizes) { + attrs.shape_calculation_mode = ShapeCalcMode::SIZES; + + const auto img = std::make_shared(element::f32, PartialShape::dynamic(5)); + const auto sizes = std::make_shared(element::i32, PartialShape::dynamic(1)); + const auto axes = std::make_shared(element::i32, PartialShape::dynamic(1)); + op = make_op(img, sizes, axes, attrs); + + int32_t sizes_v[] = {10, 50, 60}; + int32_t axes_v[] = {1, 0, 3}; + const auto const_data = + std::map{{1, std::make_shared(element::i32, Shape{3}, sizes_v)}, + {2, std::make_shared(element::i32, Shape{3}, axes_v)}}; + + input_shapes = ShapeVector{{5, 2, 128, 128, 128, 64}, {3}, {3}}; + shape_inference(op.get(), input_shapes, output_shapes, const_data); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({50, 10, 128, 60, 128, 64})); +} diff --git a/src/plugins/intel_gpu/src/graph/include/memory_accessor.hpp b/src/plugins/intel_gpu/src/graph/include/memory_accessor.hpp index a5edea15fe8..d18f0f82983 100644 --- a/src/plugins/intel_gpu/src/graph/include/memory_accessor.hpp +++ b/src/plugins/intel_gpu/src/graph/include/memory_accessor.hpp @@ -34,7 +34,7 @@ struct MemoryAccessor : public ov::ITensorAccessor { * @param stream CLDNN stream used for memory locks. * @param clbk Function object for custom callback when accessing data and not found in CLDNN memories. */ - MemoryAccessor(const container_type* ptrs, const stream& stream, std::function clbk) + MemoryAccessor(const container_type* ptrs, const stream& stream, std::function clbk) : m_ptrs{ptrs}, m_stream{stream}, m_clbk{std::move(clbk)}, diff --git a/src/plugins/intel_gpu/src/graph/resample.cpp b/src/plugins/intel_gpu/src/graph/resample.cpp index 3a37d7cbbf9..190e2fdf5a1 100644 --- a/src/plugins/intel_gpu/src/graph/resample.cpp +++ b/src/plugins/intel_gpu/src/graph/resample.cpp @@ -1,12 +1,13 @@ // Copyright (C) 2018-2023 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // -#include "resample_inst.h" -#include "primitive_type_base.h" #include -#include "json_object.h" #include "interpolate_shape_inference.hpp" +#include "json_object.h" +#include "memory_accessor.hpp" +#include "primitive_type_base.h" +#include "resample_inst.h" namespace cldnn { GPU_DEFINE_PRIMITIVE_TYPE_ID(resample) @@ -41,67 +42,34 @@ std::vector resample_inst::calc_output_layouts(resample_node const& /*no ShapeType sizes_shape = desc->sizes.empty() ? ov::Shape{ input_rank } : ov::Shape{ desc->sizes.size() }; - ShapeType scales_shape = desc->scales.empty() ? ov::Shape{ input_rank } - : ov::Shape{ desc->scales.size() }; - std::vector output_shapes = {ShapeType()}; - std::vector input_shapes = { - input_shape, - sizes_shape, - scales_shape, - ov::Shape{ desc->axes.size() } - }; + ShapeType scales_shape = desc->scales.empty() ? ov::Shape{input_rank} : ov::Shape{desc->scales.size()}; + std::vector input_shapes = {input_shape, sizes_shape, scales_shape}; + + std::unordered_map tensors; + + auto sizes = desc->sizes; + if (!sizes.empty()) { + tensors.emplace(1, ov::Tensor(ov::element::i64, ov::Shape{sizes.size()}, sizes.data())); + } + + auto scales = desc->scales; + if (!scales.empty()) { + tensors.emplace(2, ov::Tensor(ov::element::f32, ov::Shape{scales.size()}, scales.data())); + } + + auto axes = desc->axes; + if (!axes.empty()) { + auto axes_shape = ov::Shape{axes.size()}; + input_shapes.push_back(axes_shape); + tensors.emplace(3, ov::Tensor(ov::element::i64, axes_shape, axes.data())); + } auto& memory_deps = impl_param.memory_deps; - std::map const_data; - - auto sizes_data = desc->sizes; - auto scales_data = desc->scales; - - bool sizes_calc_mod = desc->get_attrs().shape_calculation_mode == ov::op::v4::Interpolate::ShapeCalcMode::SIZES; - - if (((sizes_data.empty() && !memory_deps.count(1)) || !sizes_calc_mod) && - ((scales_data.empty() && !memory_deps.count(2)) || sizes_calc_mod)) { - return { layout{ShapeType::dynamic(input_rank), input_layout.data_type, input_layout.format} }; - } - - auto axes_data = desc->axes; - if (axes_data.empty()) { - axes_data.resize(input_layout.get_rank()); - std::iota(axes_data.begin(), axes_data.end(), 0); - } - auto axes_tensor = make_host_tensor({ ov::PartialShape{ ov::Shape{axes_data.size()} }, data_types::i64, format::bfyx }, - static_cast(axes_data.data())); - const_data.emplace(3, axes_tensor); + const auto ta = MemoryAccessor(&memory_deps, impl_param.get_stream(), ov::make_tensor_accessor(tensors)); auto pads_begin = desc->pads_begin; auto pads_end = desc->pads_end; - ov::op::util::correct_pads_attr(&op, pads_begin, pads_end, input_shapes); - - if (sizes_calc_mod) { - if (!sizes_data.empty()) { - auto sizes_tensor = make_host_tensor({ sizes_shape, data_types::i64, format::bfyx }, static_cast(sizes_data.data())); - const_data.emplace(1, sizes_tensor); - ov::op::v4::shape_infer(&op, pads_begin, pads_end, input_shapes, output_shapes, {const_data}); - } else { - auto sizes_mem = memory_deps.at(1); - cldnn::mem_lock lock(sizes_mem, impl_param.get_stream()); - auto sizes_tensor = make_host_tensor(sizes_mem->get_layout(), lock.data()); - const_data.emplace(1, sizes_tensor); - ov::op::v4::shape_infer(&op, pads_begin, pads_end, input_shapes, output_shapes, {const_data}); - } - } else { - if (!scales_data.empty()) { - auto scales_tensor = make_host_tensor({ scales_shape, data_types::f32, format::bfyx }, static_cast(scales_data.data())); - const_data.emplace(2, scales_tensor); - ov::op::v4::shape_infer(&op, pads_begin, pads_end, input_shapes, output_shapes, {const_data}); - } else { - auto scales_mem = memory_deps.at(2); - cldnn::mem_lock lock(scales_mem, impl_param.get_stream()); - auto scales_tensor = make_host_tensor(scales_mem->get_layout(), lock.data()); - const_data.emplace(2, scales_tensor); - ov::op::v4::shape_infer(&op, pads_begin, pads_end, input_shapes, output_shapes, {const_data}); - } - } + const auto output_shapes = ov::op::v4::shape_infer(&op, input_shapes, pads_begin, pads_end, ta); return { layout{output_shapes[0], input_layout.data_type, format::adjust_to_rank(input_layout.format, output_shapes[0].size())} }; } diff --git a/src/plugins/template/backend/ops/interpolate.cpp b/src/plugins/template/backend/ops/interpolate.cpp index 31fb71aa042..2c0f1c38dd0 100644 --- a/src/plugins/template/backend/ops/interpolate.cpp +++ b/src/plugins/template/backend/ops/interpolate.cpp @@ -242,7 +242,14 @@ bool evaluate_interpolate(const std::shared_ptr& op, PartialShape input_shape{inputs[data_port]->get_shape()}; auto m_attrs = op->get_attrs(); - op::util::correct_pads_attr(op.get(), m_attrs.pads_begin, m_attrs.pads_end, std::vector{input_shape}); + + const auto ta = make_tensor_accessor(inputs); + auto input_shapes = std::vector(); + std::transform(inputs.cbegin(), inputs.cend(), std::back_inserter(input_shapes), [](const HostTensorPtr& ht) { + return ht->get_shape(); + }); + const auto output_shape = + ov::op::v11::shape_infer(op.get(), input_shapes, m_attrs.pads_begin, m_attrs.pads_end, ta).front(); Shape padded_input_shape; for (size_t i = 0; i < input_shape.size(); ++i) { @@ -252,16 +259,6 @@ bool evaluate_interpolate(const std::shared_ptr& op, auto axes = get_axes_vector(inputs, inputs[1]->get_shape()[0], axes_port, max_num_of_ports); auto scales = get_scales_vector(inputs, padded_input_shape, m_attrs, axes, scales_sizes_port); - PartialShape output_shape{padded_input_shape}; - if (m_attrs.shape_calculation_mode == op::util::InterpolateBase::ShapeCalcMode::SCALES) { - op::util::infer_using_scales(output_shape, axes, scales); - } else { - auto sizes = get_target_shape_vector(inputs, axes.size(), scales_sizes_port); - for (size_t i = 0; i < sizes.size(); ++i) { - output_shape[axes[i]] = Dimension(sizes[i]); - } - } - Shape out_shape = output_shape.to_shape(); outputs[0]->set_shape(out_shape); outputs[0]->set_element_type(input_et);