diff --git a/ngraph/core/include/ngraph/op/slice.hpp b/ngraph/core/include/ngraph/op/slice.hpp new file mode 100644 index 00000000000..44305383502 --- /dev/null +++ b/ngraph/core/include/ngraph/op/slice.hpp @@ -0,0 +1,16 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "ngraph/op/op.hpp" +#include "openvino/op/slice.hpp" + +namespace ngraph { +namespace op { +namespace v8 { +using ov::op::v8::Slice; +} // namespace v8 +} // namespace op +} // namespace ngraph diff --git a/ngraph/core/include/ngraph/ops.hpp b/ngraph/core/include/ngraph/ops.hpp index a6222ae58ad..bd21031b1a3 100644 --- a/ngraph/core/include/ngraph/ops.hpp +++ b/ngraph/core/include/ngraph/ops.hpp @@ -147,6 +147,7 @@ #include "ngraph/op/sign.hpp" #include "ngraph/op/sin.hpp" #include "ngraph/op/sinh.hpp" +#include "ngraph/op/slice.hpp" #include "ngraph/op/softmax.hpp" #include "ngraph/op/softplus.hpp" #include "ngraph/op/space_to_batch.hpp" diff --git a/ngraph/core/include/openvino/op/ops.hpp b/ngraph/core/include/openvino/op/ops.hpp index 194dd60b697..d1f2d22ab93 100644 --- a/ngraph/core/include/openvino/op/ops.hpp +++ b/ngraph/core/include/openvino/op/ops.hpp @@ -148,6 +148,7 @@ #include "openvino/op/sign.hpp" #include "openvino/op/sin.hpp" #include "openvino/op/sinh.hpp" +#include "openvino/op/slice.hpp" #include "openvino/op/softmax.hpp" #include "openvino/op/softplus.hpp" #include "openvino/op/space_to_batch.hpp" diff --git a/ngraph/core/include/openvino/op/slice.hpp b/ngraph/core/include/openvino/op/slice.hpp new file mode 100644 index 00000000000..b9ff0da3887 --- /dev/null +++ b/ngraph/core/include/openvino/op/slice.hpp @@ -0,0 +1,38 @@ + +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "openvino/op/op.hpp" + +namespace ov { +namespace op { +namespace v8 { +/// \brief Slice operation. +/// +class OPENVINO_API Slice : public Op { +public: + OPENVINO_RTTI_DECLARATION; + + Slice() = default; + + /// + /// \brief Constructs Slice operation. + /// + Slice(const Output& data, const Output& start, const Output& stop, const Output& step); + Slice(const Output& data, + const Output& start, + const Output& stop, + const Output& step, + const Output& axes); + + void validate_and_infer_types() override; + bool visit_attributes(AttributeVisitor& visitor) override; + + std::shared_ptr clone_with_new_inputs(const OutputVector& new_args) const override; +}; +} // namespace v8 +} // namespace op +} // namespace ov diff --git a/ngraph/core/include/openvino/opsets/opset8_tbl.hpp b/ngraph/core/include/openvino/opsets/opset8_tbl.hpp index ae3eac8380b..a94b747a3b5 100644 --- a/ngraph/core/include/openvino/opsets/opset8_tbl.hpp +++ b/ngraph/core/include/openvino/opsets/opset8_tbl.hpp @@ -182,4 +182,5 @@ OPENVINO_OP(MatrixNms, ov::op::v8) OPENVINO_OP(MaxPool, ov::op::v8) OPENVINO_OP(MulticlassNms, ov::op::v8) OPENVINO_OP(RandomUniform, ov::op::v8) +OPENVINO_OP(Slice, ov::op::v8) OPENVINO_OP(If, ov::op::v8) diff --git a/ngraph/core/src/op/slice.cpp b/ngraph/core/src/op/slice.cpp new file mode 100644 index 00000000000..295a077eaf9 --- /dev/null +++ b/ngraph/core/src/op/slice.cpp @@ -0,0 +1,286 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ngraph/op/slice.hpp" + +#include + +#include "itt.hpp" +#include "ngraph/attribute_visitor.hpp" +#include "ngraph/graph_util.hpp" +#include "ngraph/op/constant.hpp" +#include "ngraph/validation_util.hpp" + +using namespace std; +using namespace ngraph; + +OPENVINO_RTTI_DEFINITION(op::v8::Slice, "Slice", 8); + +op::v8::Slice::Slice(const Output& data, + const Output& start, + const Output& stop, + const Output& step) + : Op({data, start, stop, step}) { + constructor_validate_and_infer_types(); +} + +op::v8::Slice::Slice(const Output& data, + const Output& start, + const Output& stop, + const Output& step, + const Output& axes) + : Op({data, start, stop, step, axes}) { + constructor_validate_and_infer_types(); +} + +namespace { + +std::shared_ptr get_default_const_axes(const Output& start) { + const auto start_pshape = start.get_partial_shape(); + // Static case + if (start_pshape.rank().is_static() && start_pshape.rank().get_length() == 1 && start_pshape[0].is_static()) { + size_t axes_length = start_pshape[0].get_length(); + std::vector axes(axes_length); + std::iota(axes.begin(), axes.end(), 0); + return op::v0::Constant::create(element::i64, Shape{axes_length}, axes); + } + // Dynamic case + return nullptr; +} + +int64_t get_sliced_dim_size(int64_t start, int64_t stop, int64_t step, int64_t dim_size) { + // Normalize index + start = start < 0 ? dim_size + start : start; + stop = stop < 0 ? dim_size + stop : stop; + + // Clip normalized bounds according to the dim size + start = std::max(int64_t(0), std::min(start, dim_size)); // inclusive + stop = std::max(int64_t(-1), std::min(stop, dim_size)); // exclusive + + int64_t elements_in_range = 0; + if (step < 0) { + // Clip max start index (last element inclusively) + elements_in_range = std::max(int64_t(0), std::min(dim_size - 1, start) - stop); + } else { + // Clip max stop index (last element exclusively) + elements_in_range = std::max(int64_t(0), std::min(dim_size, stop) - start); + } + const int64_t sliced_dim_size = std::ceil(elements_in_range / std::fabs(step)); + return sliced_dim_size; +} + +} // namespace + +bool op::v8::Slice::visit_attributes(AttributeVisitor& visitor) { + NGRAPH_OP_SCOPE(v8_Slice_visit_attributes); + return true; +} + +void op::v8::Slice::validate_and_infer_types() { + NGRAPH_OP_SCOPE(v8_Slice_validate_and_infer_types); + + const auto inputs_size = get_input_size(); + NODE_VALIDATION_CHECK(this, + inputs_size == 4 || inputs_size == 5, + "Slice has to have 4 or 5 inputs. Got: ", + inputs_size); + + const PartialShape& data_shape = get_input_partial_shape(0); + const auto& data_rank = data_shape.rank(); + + NODE_VALIDATION_CHECK(this, + data_rank.is_dynamic() || data_rank.get_length() > 0, + "Slice `data` input can't be a scalar."); + + const auto start_const = get_constant_from_source(input_value(1)); + const auto stop_const = get_constant_from_source(input_value(2)); + const auto step_const = get_constant_from_source(input_value(3)); + + const auto& start_input = start_const ? start_const : input_value(1); + const auto& stop_input = stop_const ? stop_const : input_value(2); + const auto& step_input = step_const ? step_const : input_value(3); + + NODE_VALIDATION_CHECK(this, + start_input.get_element_type().is_integral_number(), + "Slice `start` input type must be integer."); + NODE_VALIDATION_CHECK(this, + stop_input.get_element_type().is_integral_number(), + "Slice `stop` input type must be integer."); + NODE_VALIDATION_CHECK(this, + step_input.get_element_type().is_integral_number(), + "Slice `step` input type must be integer."); + + const auto& start_shape = start_input.get_partial_shape(); + const auto& stop_shape = stop_input.get_partial_shape(); + const auto& step_shape = step_input.get_partial_shape(); + + const auto& start_rank = start_shape.rank(); + const auto& stop_rank = stop_shape.rank(); + const auto& step_rank = step_shape.rank(); + + NODE_VALIDATION_CHECK(this, + start_rank.compatible(1), + "Slice `start` input must be a 1D tensor. Got rank: ", + start_rank); + NODE_VALIDATION_CHECK(this, + stop_rank.compatible(1), + "Slice `stop` input must be a 1D tensor. Got rank: ", + stop_rank); + NODE_VALIDATION_CHECK(this, + step_rank.compatible(1), + "Slice `step` input must be a 1D tensor. Got rank: ", + step_rank); + + if (data_rank.is_static()) { + const auto data_rank_length = data_rank.get_length(); + NODE_VALIDATION_CHECK(this, + start_rank.is_dynamic() || start_shape[0].get_max_length() <= data_rank_length, + "Slice `start` input dim size can't be bigger than `data` rank."); + NODE_VALIDATION_CHECK(this, + stop_rank.is_dynamic() || stop_shape[0].get_max_length() <= data_rank_length, + "Slice `stop` input dim size can't be bigger than `data` rank."); + NODE_VALIDATION_CHECK(this, + step_rank.is_dynamic() || step_shape[0].get_max_length() <= data_rank_length, + "Slice `step` input dim size can't be bigger than `data` rank."); + } + + NODE_VALIDATION_CHECK( + this, + start_shape.compatible(stop_shape) && start_shape.compatible(step_shape) && stop_shape.compatible(step_shape), + "Slice `start`, `stop`, `step` inputs must have compatible shapes."); + + set_input_is_relevant_to_shape(0); + set_input_is_relevant_to_shape(1); + set_input_is_relevant_to_shape(2); + set_input_is_relevant_to_shape(3); + + std::shared_ptr axes_const; + if (get_input_size() > 4) { + set_input_is_relevant_to_shape(4); + axes_const = get_constant_from_source(input_value(4)); + const auto& axes_input = axes_const ? axes_const : input_value(4); + const auto& axes_rank = axes_input.get_partial_shape().rank(); + NODE_VALIDATION_CHECK(this, + axes_rank.compatible(1), + "Slice `axes` input must be a 1D tensor. Got rank: ", + axes_rank); + NODE_VALIDATION_CHECK(this, + axes_rank.is_dynamic() || axes_input.get_partial_shape()[0].get_max_length() <= + data_rank.get_interval().get_max_val(), + "Slice `axes` input dim size can't be bigger than `data` rank."); + NODE_VALIDATION_CHECK(this, + axes_input.get_partial_shape().compatible(start_shape), + "Slice `axes` input must have compatible shape with `start`, `stop`, `step` inputs."); + NODE_VALIDATION_CHECK(this, + axes_input.get_element_type().is_integral_number(), + "Slice `axes` input type must be integer."); + } else { + axes_const = get_default_const_axes(start_input); + } + + PartialShape output_shape(data_shape); + + // If data_shape rank is dynamic we can't calulate output shape. + // Even with const start/stop/step/axes, we don't know how many axes should be copied + // as "unspefified" in the final output shape, so the output shape rank is also dynamic. + if (data_rank.is_dynamic()) { + set_output_type(0, get_input_element_type(0), output_shape); + return; + } + const auto data_static_rank = data_shape.rank().get_length(); + + if (start_const && stop_const && step_const && axes_const) { + const auto& starts = start_const->cast_vector(); + const auto& stops = stop_const->cast_vector(); + const auto& steps = step_const->cast_vector(); + const auto& axes = axes_const->cast_vector(); + + std::unordered_set axes_set(axes.begin(), axes.end()); + NODE_VALIDATION_CHECK(this, axes_set.size() == axes.size(), "Slice values in `axes` input must be unique."); + + for (size_t i = 0; i < axes.size(); ++i) { + const auto norm_axis = axes[i] < 0 ? data_static_rank + axes[i] : axes[i]; + NODE_VALIDATION_CHECK(this, + norm_axis >= 0 && norm_axis < data_static_rank, + "Values in the `axes` input must be in range of the `data` input rank: [-", + data_static_rank, + ", ", + data_static_rank - 1, + "]. Got: ", + axes[i]); + + auto start = starts[i]; + auto stop = stops[i]; + auto step = steps[i]; + + NODE_VALIDATION_CHECK(this, step != 0, "Slice 'step' value can't be zero."); + + const auto& axis_dim = data_shape[norm_axis]; + const auto axis_min_dim_length = axis_dim.get_min_length(); + const auto min_dim_size = get_sliced_dim_size(start, stop, step, axis_min_dim_length); + if (axis_dim.is_static()) { + output_shape[norm_axis] = min_dim_size; + } + + // Avoid negative index normalization without upper bounds + if (!axis_dim.get_interval().has_upper_bound()) { + if ((step < 0 && start < 0 && stop > 0) || (step > 0 && stop < 0 && start > 0)) { + output_shape[norm_axis] = Dimension(-1); + continue; + } else if (step < 0 && start > 0 && stop < 0) { + int64_t max_out_dim = start >= INT32_MAX ? INT64_MAX : start + 1; + output_shape[norm_axis] = Dimension(0, max_out_dim); + continue; + } else if (step > 0 && stop > 0 && start < 0) { + int64_t max_out_dim = stop >= INT32_MAX ? INT64_MAX : stop; + output_shape[norm_axis] = Dimension(0, max_out_dim); + continue; + } + } + + // Calculate max dim length (upper bound) + auto axis_max_dim_length = axis_dim.get_interval().get_max_val(); + const auto max_dim_size = get_sliced_dim_size(start, stop, step, axis_max_dim_length); + output_shape[norm_axis] = Dimension(min_dim_size, max_dim_size); + } + } else { + if (axes_const) { + // If we know only `axes` values, we should update lower_bound to 0 value, + // for the specified dims by the axes. For unspecified dims, bounds as in data_shape. + for (const auto& axis : axes_const->cast_vector()) { + const auto norm_axis = axis < 0 ? data_static_rank + axis : axis; + NODE_VALIDATION_CHECK(this, + norm_axis >= 0 && norm_axis < data_static_rank, + "Values in the `axes` input must be in range of the `data` input rank: [-", + data_static_rank, + ", ", + data_static_rank - 1, + "]. Got: ", + axis); + output_shape[axis] = Dimension(0, data_shape[axis].get_max_length()); + } + } else { + // Otherwise `axes` values are also unknown, + // then all of the output dims can be 0, so have lower bound = 0. + for (size_t i = 0; i < data_shape.rank().get_length(); ++i) { + output_shape[i] = Dimension(0, data_shape[i].get_max_length()); + } + } + } + set_output_type(0, get_input_element_type(0), output_shape); +} + +shared_ptr op::v8::Slice::clone_with_new_inputs(const OutputVector& new_args) const { + NGRAPH_OP_SCOPE(v8_Slice_clone_with_new_inputs); + check_new_args_count(this, new_args); + if (new_args.size() == 4) { + return std::make_shared(new_args.at(0), new_args.at(1), new_args.at(2), new_args.at(3)); + } else { + return std::make_shared(new_args.at(0), + new_args.at(1), + new_args.at(2), + new_args.at(3), + new_args.at(4)); + } +} diff --git a/ngraph/test/CMakeLists.txt b/ngraph/test/CMakeLists.txt index 0455c05b7db..ca2d3c32e28 100644 --- a/ngraph/test/CMakeLists.txt +++ b/ngraph/test/CMakeLists.txt @@ -218,6 +218,7 @@ set(SRC type_prop/sign.cpp type_prop/sin.cpp type_prop/sinh.cpp + type_prop/slice.cpp type_prop/softmax.cpp type_prop/softplus.cpp type_prop/space_to_batch.cpp @@ -337,6 +338,7 @@ set(SRC visitors/op/shuffle_channels.cpp visitors/op/sign.cpp visitors/op/sinh.cpp + visitors/op/slice.cpp visitors/op/softmax.cpp visitors/op/softplus.cpp visitors/op/space_to_batch.cpp diff --git a/ngraph/test/opset.cpp b/ngraph/test/opset.cpp index 15392615b3d..24a6e4d2857 100644 --- a/ngraph/test/opset.cpp +++ b/ngraph/test/opset.cpp @@ -140,5 +140,5 @@ TEST(opset, opset8_dump) { std::cout << t.name << " "; } std::cout << std::endl; - ASSERT_EQ(162, opset.get_types_info().size()); + ASSERT_EQ(163, opset.get_types_info().size()); } diff --git a/ngraph/test/type_prop/slice.cpp b/ngraph/test/type_prop/slice.cpp new file mode 100644 index 00000000000..e70d7f4b514 --- /dev/null +++ b/ngraph/test/type_prop/slice.cpp @@ -0,0 +1,1013 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include "gtest/gtest.h" +#include "ngraph/ngraph.hpp" +#include "util/type_prop.hpp" + +using namespace ngraph; + +namespace { +template +std::shared_ptr make_slice_op_const_inputs(const std::vector>& args, + PartialShape& data_shape, + element::Type_t et) { + const auto& start_val = args[0]; + const auto& stop_val = args[1]; + const auto& step_val = args[2]; + + const auto data = std::make_shared(et, data_shape); + const auto start = std::make_shared(et, Shape{start_val.size()}, start_val); + const auto stop = std::make_shared(et, Shape{stop_val.size()}, stop_val); + const auto step = std::make_shared(et, Shape{step_val.size()}, step_val); + + if (args.size() > 3) { + const auto& axes_val = args[3]; + const auto axes = std::make_shared(et, Shape{axes_val.size()}, axes_val); + return std::make_shared(data, start, stop, step, axes); + } + return std::make_shared(data, start, stop, step); +} +} // namespace + +TEST(type_prop, slice_v8_basic_const_inputs) { + PartialShape data_shape{10, 10, 10, 10, 10, 10, 10, 10}; + PartialShape expected_out_shape{7, 4, 10, 10, 9, 9, 5, 10}; + + std::vector start_val{1, 1, -20, 9, 9, 9, 9, 20}; + std::vector stop_val{8, 8, 20, -11, 0, -10, -11, -20}; + std::vector step_val{1, 2, 1, -1, -1, -1, -2, -1}; + + std::vector axes_val(start_val.size()); + std::iota(axes_val.begin(), axes_val.end(), 0); + + element::Type_t et = element::i32; + + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_const_inputs_dif_steps) { + PartialShape data_shape{5, 5, 5, 5, 9, 9}; + PartialShape expected_out_shape{3, 2, 2, 1, 5, 3}; + + std::vector start_val{0, 0, 0, 0, 0, 0}; + std::vector stop_val{5, 5, 5, 5, 9, 9}; + std::vector step_val{2, 3, 4, 5, 2, 3}; + + element::Type_t et = element::i32; + + std::vector> input_vals{start_val, stop_val, step_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_const_inputs_dif_neg_steps) { + PartialShape data_shape{5, 5, 5, 5, 9, 9}; + PartialShape expected_out_shape{2, 2, 1, 1, 4, 3}; + + std::vector start_val{5, 5, 5, 5, 9, 9}; + std::vector stop_val{0, 0, 0, 0, 0, 0}; + std::vector step_val{-2, -3, -4, -5, -2, -3}; + + element::Type_t et = element::i32; + + std::vector> input_vals{start_val, stop_val, step_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_const_inputs_default_axes) { + PartialShape data_shape{10, 10, 10, 10, 10, 10, 10, 10}; + PartialShape expected_out_shape{7, 4, 10, 10, 9, 9, 5, 10}; + + std::vector start_val{1, 1, -20, 9, 9, 9, 9, 20}; + std::vector stop_val{8, 8, 20, -11, 0, -10, -11, -20}; + std::vector step_val{1, 2, 1, -1, -1, -1, -2, -1}; + + element::Type_t et = element::i32; + std::vector> input_vals{start_val, stop_val, step_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_const_inputs_unordered_axes) { + PartialShape data_shape{10, 10, 10, 10, 10, 10, 10, 10}; + PartialShape expected_out_shape{4, 9, 7, 10, 10, 9, 5, 10}; + + std::vector start_val{1, 1, -20, 9, 9, 9, 9, 20}; + std::vector stop_val{8, 8, 20, -11, 0, -10, -11, -20}; + std::vector step_val{1, 2, 1, -1, -1, -1, -2, -1}; + + std::vector axes_val{2, 0, 3, 7, 1, 5, 6, 4}; + + element::Type_t et = element::i32; + + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_const_inputs_not_all_axes_unordered) { + PartialShape data_shape{10, 10, 10, 10, 10, 20, Dimension(20, 30), 30, Dimension(2, 5), Dimension(-1)}; + PartialShape expected_out_shape{4, 7, 10, 10, 9, 20, Dimension(10, 15), 30, Dimension(2, 5), Dimension(-1)}; + + std::vector start_val{1, 1, -20, 9, 10, 9}; + std::vector stop_val{8, 8, 20, -11, 25, 0}; + std::vector step_val{1, 2, 1, -1, 1, -1}; + + std::vector axes_val{1, 0, 2, 3, 6, 4}; + + element::Type_t et = element::i32; + + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_const_inputs_data_dynamic_bounds_dimensions) { + PartialShape data_shape{Dimension(10, 20), Dimension(20, 30), Dimension(30, 40)}; + PartialShape expected_out_shape{6, Dimension(10, 15), Dimension(0, 5)}; + + std::vector start_val{2, 10, 35}; + std::vector stop_val{8, 25, 40}; + std::vector step_val{1, 1, 1}; + + std::vector axes_val(start_val.size()); + std::iota(axes_val.begin(), axes_val.end(), 0); + + element::Type_t et = element::i32; + + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_const_inputs_data_dynamic_rank) { + PartialShape data_shape = PartialShape::dynamic(); + PartialShape expected_out_shape = PartialShape::dynamic(); + + std::vector start_val{1, 1, -20, 9, 9, 9, 9, 20}; + std::vector stop_val{8, 8, 20, -11, 0, -10, -11, -20}; + std::vector step_val{1, 2, 1, -1, -1, -1, -2, -1}; + + std::vector axes_val(start_val.size()); + std::iota(axes_val.begin(), axes_val.end(), 0); + + element::Type_t et = element::i32; + + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); + EXPECT_TRUE(op->get_output_partial_shape(0).rank().is_dynamic()); +} + +TEST(type_prop, slice_v8_basic_param_inputs_default_axes) { + PartialShape data_shape{Dimension(0, 10), + Dimension(1, 10), + 10, + Dimension(3, 5), + Dimension(-1, -1), + Dimension(100, -1), + Dimension(0, 8), + Dimension(4, 8), + 16}; + PartialShape expected_out_shape{Dimension(0, 10), + Dimension(0, 10), + Dimension(0, 10), + Dimension(0, 5), + Dimension(0, -1), + Dimension(0, -1), + Dimension(0, 8), + Dimension(4, 8), + 16}; + + PartialShape start_shape{7}; + PartialShape stop_shape{7}; + PartialShape step_shape{7}; + + element::Type_t et = element::i32; + + const auto data = std::make_shared(et, data_shape); + const auto start = std::make_shared(et, start_shape); + const auto stop = std::make_shared(et, stop_shape); + const auto step = std::make_shared(et, step_shape); + + const auto op = std::make_shared(data, start, stop, step); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_sss_param_inputs_const_axes) { + PartialShape data_shape{Dimension(0, 10), + Dimension(1, 10), + 10, + Dimension(3, 5), + Dimension(-1, -1), + Dimension(100, -1), + Dimension(0, 8), + Dimension(4, 8), + 16}; + + PartialShape expected_out_shape{Dimension(0, 10), + Dimension(0, 10), + Dimension(0, 10), + Dimension(0, 5), + Dimension(0, -1), + Dimension(0, -1), + Dimension(0, 8), + Dimension(4, 8), + 16}; + + PartialShape start_shape{7}; + PartialShape stop_shape{7}; + PartialShape step_shape{7}; + + element::Type_t et = element::i32; + + Shape axes_shape{7}; + std::vector axes_val(7); + std::iota(axes_val.begin(), axes_val.end(), 0); + + const auto data = std::make_shared(et, data_shape); + const auto start = std::make_shared(et, start_shape); + const auto stop = std::make_shared(et, stop_shape); + const auto step = std::make_shared(et, step_shape); + const auto axes = std::make_shared(et, axes_shape, axes_val); + + const auto op = std::make_shared(data, start, stop, step, axes); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_sss_param_inputs_param_axes) { + PartialShape data_shape{Dimension(0, 10), + Dimension(1, 10), + 10, + Dimension(3, 5), + Dimension(-1, -1), + Dimension(100, -1), + Dimension(0, 8), + Dimension(4, 8), + 16}; + + PartialShape expected_out_shape{Dimension(0, 10), + Dimension(0, 10), + Dimension(0, 10), + Dimension(0, 5), + Dimension(0, -1), + Dimension(0, -1), + Dimension(0, 8), + Dimension(4, 8), + 16}; + PartialShape start_shape{7}; + PartialShape stop_shape{7}; + PartialShape step_shape{7}; + PartialShape axes_shape{7}; + + element::Type_t et = element::i32; + + const auto data = std::make_shared(et, data_shape); + const auto start = std::make_shared(et, start_shape); + const auto stop = std::make_shared(et, stop_shape); + const auto step = std::make_shared(et, step_shape); + const auto axes = std::make_shared(et, axes_shape); + + const auto op = std::make_shared(data, start, stop, step); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_const_inputs_MAX_MIN_INT_dynamic_dimensions) { + PartialShape data_shape{10, + Dimension(10, 20), + Dimension(10, 20), + Dimension(10, 20), + Dimension(10, 20), + Dimension(10, 20), + Dimension(30, 40), + Dimension(0, 50), + Dimension(0, -1)}; + PartialShape expected_out_shape{8, + Dimension(8, 18), + 5, + 10, + Dimension(10, 15), + Dimension(10, 20), + Dimension(30, 40), + Dimension(0, 50), + Dimension(0, INT32_MAX)}; + + std::vector start_val{2, 2, INT32_MIN, INT32_MIN, INT32_MIN, INT32_MIN, INT32_MIN, 0, 0}; + std::vector stop_val{10, INT32_MAX, 5, 10, 15, 25, INT32_MAX, 50, INT32_MAX}; + std::vector step_val{1, 1, 1, 1, 1, 1, 1, 1, 1}; + + std::vector axes_val(start_val.size()); + std::iota(axes_val.begin(), axes_val.end(), 0); + + element::Type_t et = element::i32; + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_const_inputs_MAX_MIN_INT_dynamic_dimensions_neg_step) { + PartialShape data_shape{10, + Dimension(10, 20), + Dimension(10, 20), + Dimension(10, 20), + Dimension(11, 20), + Dimension(10, 20), + Dimension(10, 20), + Dimension(30, 40), + Dimension(20), + Dimension(20), + Dimension(0, 20), + Dimension(-1), + Dimension(-1)}; + PartialShape expected_out_shape{8, + Dimension(8, 18), + Dimension(5, 15), + Dimension(0, 9), + Dimension(0, 9), + Dimension(0, 4), + Dimension(0), + Dimension(30, 40), + Dimension(20), + Dimension(20), + Dimension(0, 20), + Dimension(0, 21), + Dimension(-1)}; + + std::vector start_val{9, + INT32_MAX, + INT32_MAX, + INT32_MAX, + INT32_MAX, + INT32_MAX, + INT32_MAX, + INT32_MAX, + 20, + 20, + 20, + 20, + INT32_MAX}; + std::vector stop_val{1, 1, 4, 10, 10, 15, 25, INT32_MIN, -21, INT32_MIN, INT32_MIN, INT32_MIN, INT32_MIN}; + std::vector step_val{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + + std::vector axes_val(start_val.size()); + std::iota(axes_val.begin(), axes_val.end(), 0); + + element::Type_t et = element::i32; + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_const_inputs_data_full_dynamic_dims) { + PartialShape data_shape{Dimension(-1), Dimension(-1), Dimension(-1)}; + PartialShape expected_out_shape{Dimension(0, 6), Dimension(0, 15), Dimension(0, 5)}; + + std::vector start_val{2, 10, 35}; + std::vector stop_val{8, 25, 40}; + std::vector step_val{1, 1, 1}; + + std::vector axes_val(start_val.size()); + std::iota(axes_val.begin(), axes_val.end(), 0); + + element::Type_t et = element::i32; + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_const_inputs_data_full_dynamic_dims_neg_ind) { + PartialShape data_shape{Dimension(-1), Dimension(-1), Dimension(-1)}; + PartialShape expected_out_shape{Dimension(0, 6), Dimension(0, 15), Dimension(0, 5)}; + + std::vector start_val{-8, -25, -40}; + std::vector stop_val{-2, -10, -35}; + std::vector step_val{1, 1, 1}; + + std::vector axes_val(start_val.size()); + std::iota(axes_val.begin(), axes_val.end(), 0); + + element::Type_t et = element::i32; + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_const_inputs_data_full_dynamic_dims_neg_step) { + PartialShape data_shape{Dimension(-1), Dimension(-1), Dimension(-1)}; + PartialShape expected_out_shape{Dimension(0, 6), Dimension(0, 15), Dimension(0, 5)}; + + std::vector start_val{8, 25, 40}; + std::vector stop_val{2, 10, 35}; + std::vector step_val{-1, -1, -1}; + + std::vector axes_val(start_val.size()); + std::iota(axes_val.begin(), axes_val.end(), 0); + + element::Type_t et = element::i32; + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_const_inputs_data_full_dynamic_dims_neg_step_neg_ind) { + PartialShape data_shape{Dimension(-1), Dimension(-1), Dimension(-1)}; + PartialShape expected_out_shape{Dimension(0, 6), Dimension(0, 15), Dimension(0, 5)}; + + std::vector start_val{-2, -10, -35}; + std::vector stop_val{-8, -25, -40}; + std::vector step_val{-1, -1, -1}; + + std::vector axes_val(start_val.size()); + std::iota(axes_val.begin(), axes_val.end(), 0); + + element::Type_t et = element::i32; + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_const_inputs_data_full_dynamic_dims_neg_step_mix_ind) { + PartialShape data_shape{Dimension(-1), Dimension(-1), Dimension(-1), Dimension(-1), Dimension(-1)}; + PartialShape expected_out_shape{Dimension(0, 6), + Dimension(0, 6), + Dimension(-1), + Dimension(0, INT32_MAX - 5), + Dimension(-1)}; + + std::vector start_val{5, 5, -10, INT32_MAX, INT32_MAX}; + std::vector stop_val{-10, INT32_MIN, 5, 5, INT32_MIN}; + std::vector step_val{-1, -1, -1, -1, -1}; + + std::vector axes_val(start_val.size()); + std::iota(axes_val.begin(), axes_val.end(), 0); + + element::Type_t et = element::i32; + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_const_inputs_MAX_MIN_INT_64_dynamic_dimensions_neg_step) { + PartialShape data_shape{10, + Dimension(10, 20), + Dimension(10, 20), + Dimension(10, 20), + Dimension(11, 20), + Dimension(10, 20), + Dimension(10, 20), + Dimension(30, 40), + Dimension(20), + Dimension(20), + Dimension(0, 20), + Dimension(-1), + Dimension(-1)}; + PartialShape expected_out_shape{8, + Dimension(8, 18), + Dimension(5, 15), + Dimension(0, 9), + Dimension(0, 9), + Dimension(0, 4), + Dimension(0), + Dimension(30, 40), + Dimension(20), + Dimension(20), + Dimension(0, 20), + Dimension(0, 21), + Dimension(-1)}; + + std::vector start_val{9, + INT64_MAX, + INT64_MAX, + INT64_MAX, + INT64_MAX, + INT64_MAX, + INT64_MAX, + INT64_MAX, + 20, + 20, + 20, + 20, + INT64_MAX}; + std::vector stop_val{1, 1, 4, 10, 10, 15, 25, INT64_MIN, -21, INT64_MIN, INT64_MIN, INT64_MIN, INT64_MIN}; + std::vector step_val{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + + std::vector axes_val(start_val.size()); + std::iota(axes_val.begin(), axes_val.end(), 0); + + element::Type_t et = element::i64; + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_const_inputs_MAX_MIN_64_no_upper_bounds_neg_step) { + PartialShape data_shape{Dimension(-1), Dimension(0, INT64_MAX), Dimension(0, INT64_MAX)}; + PartialShape expected_out_shape{Dimension(-1), Dimension(0, INT64_MAX), Dimension(0, INT64_MAX)}; + + std::vector start_val{INT64_MAX, INT64_MAX, INT64_MAX}; + std::vector stop_val{INT64_MIN, INT64_MIN, INT64_MIN}; + std::vector step_val{-1, -1, -1}; + + std::vector axes_val(start_val.size()); + std::iota(axes_val.begin(), axes_val.end(), 0); + + element::Type_t et = element::i64; + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_const_inputs_MAX_MIN_64_no_upper_bounds) { + PartialShape data_shape{Dimension(-1), Dimension(0, INT64_MAX), Dimension(0, INT64_MAX)}; + PartialShape expected_out_shape{Dimension(-1), Dimension(0, INT64_MAX), Dimension(0, INT64_MAX)}; + + std::vector start_val{INT64_MIN, INT64_MIN, INT64_MIN}; + std::vector stop_val{INT64_MAX, INT64_MAX, INT64_MAX}; + std::vector step_val{1, 1, 1}; + + std::vector axes_val(start_val.size()); + std::iota(axes_val.begin(), axes_val.end(), 0); + + element::Type_t et = element::i64; + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_const_inputs_MAX_MIN_32_no_upper_bounds) { + PartialShape data_shape{Dimension(-1), Dimension(0, INT64_MAX), Dimension(0, INT32_MAX), Dimension(0, INT32_MAX)}; + PartialShape expected_out_shape{Dimension(-1), + Dimension(0, INT64_MAX), + Dimension(0, INT32_MAX), + Dimension(0, INT32_MAX)}; + + std::vector start_val{INT32_MIN, INT32_MIN, INT32_MIN, INT32_MIN}; + std::vector stop_val{INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX}; + std::vector step_val{1, 1, 1, 1}; + + std::vector axes_val(start_val.size()); + std::iota(axes_val.begin(), axes_val.end(), 0); + + element::Type_t et = element::i32; + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_basic_const_inputs_MAX_MIN_32_no_upper_bounds_neg_step) { + PartialShape data_shape{Dimension(-1), Dimension(0, INT64_MAX), Dimension(0, INT32_MAX), Dimension(0, INT32_MAX)}; + PartialShape expected_out_shape{Dimension(-1), Dimension(-1), Dimension(0, INT32_MAX), Dimension(0, INT32_MAX)}; + + std::vector start_val{INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX}; + std::vector stop_val{INT32_MIN, INT32_MIN, INT32_MIN, INT32_MIN}; + std::vector step_val{-1, -1, -1, -1}; + + std::vector axes_val(start_val.size()); + std::iota(axes_val.begin(), axes_val.end(), 0); + + element::Type_t et = element::i32; + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + + EXPECT_EQ(op->get_element_type(), et); + EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape); +} + +TEST(type_prop, slice_v8_duplicated_axes) { + PartialShape data_shape{100, 100, 100, 100}; + PartialShape expected_out_shape{100, 100, 100, 100}; + + std::vector start_val{2, 10, 35, 10}; + std::vector stop_val{8, 25, 40, 20}; + std::vector step_val{1, 1, 1, 100}; + + std::vector axes_val{2, 1, 2, 3}; + + element::Type_t et = element::i32; + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + + EXPECT_THROW(make_slice_op_const_inputs(input_vals, data_shape, et), NodeValidationFailure); +} + +TEST(type_prop, slice_v8_zero_step) { + PartialShape data_shape{100, 100, 100}; + PartialShape expected_out_shape{100, 100, 100}; + + std::vector start_val{2, 10, 35}; + std::vector stop_val{8, 25, 40}; + std::vector step_val{1, 0, 1}; + + std::vector axes_val{1, 2, 3}; + + element::Type_t et = element::i32; + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + + EXPECT_THROW(make_slice_op_const_inputs(input_vals, data_shape, et), NodeValidationFailure); +} + +TEST(type_prop, slice_v8_ind_bad_type) { + PartialShape data_shape{100, 100, 100, 100}; + PartialShape expected_out_shape{100, 100, 100, 100}; + + std::vector start_val{2, 10, 35, 10}; + std::vector stop_val{8, 25, 40, 20}; + std::vector step_val{1, 1, 1, 100}; + + std::vector axes_val{0, 1, 2, 3}; + + element::Type_t et = element::f32; + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + + EXPECT_THROW(make_slice_op_const_inputs(input_vals, data_shape, et), NodeValidationFailure); +} + +TEST(type_prop, slice_v8_input_wrong_shape_catch) { + PartialShape data_shape{100, 100, 100, 100}; + + PartialShape correct_shape{3}; + PartialShape wrong_shape{}; + + element::Type_t et = element::i32; + + const auto data = std::make_shared(et, data_shape); + const auto start = std::make_shared(et, correct_shape); + const auto stop = std::make_shared(et, correct_shape); + const auto step = std::make_shared(et, correct_shape); + const auto axes = std::make_shared(et, correct_shape); + { + try { + const auto start = std::make_shared(et, wrong_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "`start` input must be a 1D tensor"); + } + } + { + try { + const auto stop = std::make_shared(et, wrong_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "`stop` input must be a 1D tensor"); + } + } + { + try { + const auto step = std::make_shared(et, wrong_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "`step` input must be a 1D tensor"); + } + } + { + try { + const auto axes = std::make_shared(et, wrong_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "`axes` input must be a 1D tensor"); + } + } + { + try { + const auto data = std::make_shared(et, wrong_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "`data` input can't be a scalar"); + } + } +} + +TEST(type_prop, slice_v8_input_start_stop_step_dif_length_catch) { + PartialShape data_shape{100, 100, 100, 100}; + + PartialShape correct_shape{3}; + PartialShape wrong_shape{2}; + + element::Type_t et = element::i32; + + const auto data = std::make_shared(et, data_shape); + const auto start = std::make_shared(et, correct_shape); + const auto stop = std::make_shared(et, correct_shape); + const auto step = std::make_shared(et, correct_shape); + const auto axes = std::make_shared(et, correct_shape); + { + try { + const auto start = std::make_shared(et, wrong_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "start`, `stop`, `step` inputs must have compatible shapes"); + } + } + { + try { + const auto stop = std::make_shared(et, wrong_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "start`, `stop`, `step` inputs must have compatible shapes"); + } + } + { + try { + const auto step = std::make_shared(et, wrong_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "start`, `stop`, `step` inputs must have compatible shapes"); + } + } + { + try { + const auto axes = std::make_shared(et, wrong_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), + "`axes` input must have compatible shape with `start`, `stop`, `step` inputs"); + } + } +} + +TEST(type_prop, slice_v8_input_start_stop_step_out_of_data_rank_length_catch) { + PartialShape data_shape{100, 100, 100, 100}; + + PartialShape correct_shape{3}; + PartialShape wrong_shape{5}; + + element::Type_t et = element::i32; + + const auto data = std::make_shared(et, data_shape); + const auto start = std::make_shared(et, correct_shape); + const auto stop = std::make_shared(et, correct_shape); + const auto step = std::make_shared(et, correct_shape); + const auto axes = std::make_shared(et, correct_shape); + { + try { + const auto start = std::make_shared(et, wrong_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "`start` input dim size can't be bigger than `data` rank"); + } + } + { + try { + const auto stop = std::make_shared(et, wrong_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "`stop` input dim size can't be bigger than `data` rank"); + } + } + { + try { + const auto step = std::make_shared(et, wrong_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "`step` input dim size can't be bigger than `data` rank"); + } + } + { + try { + const auto axes = std::make_shared(et, wrong_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "`axes` input dim size can't be bigger than `data` rank"); + } + } +} + +TEST(type_prop, slice_v8_input_wrong_types_float_catch) { + PartialShape data_shape{100, 100, 100, 100}; + PartialShape correct_shape{3}; + + element::Type_t et = element::i32; + element::Type_t wrong_et = element::f32; + + const auto data = std::make_shared(et, data_shape); + const auto start = std::make_shared(et, correct_shape); + const auto stop = std::make_shared(et, correct_shape); + const auto step = std::make_shared(et, correct_shape); + const auto axes = std::make_shared(et, correct_shape); + { + try { + const auto start = std::make_shared(wrong_et, correct_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "`start` input type must be integer."); + } + } + { + try { + const auto stop = std::make_shared(wrong_et, correct_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "`stop` input type must be integer."); + } + } + { + try { + const auto step = std::make_shared(wrong_et, correct_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "`step` input type must be integer."); + } + } + { + try { + const auto axes = std::make_shared(wrong_et, correct_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "`axes` input type must be integer."); + } + } +} + +TEST(type_prop, slice_v8_input_wrong_types_bool_catch) { + PartialShape data_shape{100, 100, 100, 100}; + PartialShape correct_shape{3}; + + element::Type_t et = element::u64; + element::Type_t wrong_et = element::boolean; + + const auto data = std::make_shared(et, data_shape); + const auto start = std::make_shared(et, correct_shape); + const auto stop = std::make_shared(et, correct_shape); + const auto step = std::make_shared(et, correct_shape); + const auto axes = std::make_shared(et, correct_shape); + { + try { + const auto start = std::make_shared(wrong_et, correct_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "`start` input type must be integer."); + } + } + { + try { + const auto stop = std::make_shared(wrong_et, correct_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "`stop` input type must be integer."); + } + } + { + try { + const auto step = std::make_shared(wrong_et, correct_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "`step` input type must be integer."); + } + } + { + try { + const auto axes = std::make_shared(wrong_et, correct_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "`axes` input type must be integer."); + } + } +} + +TEST(type_prop, slice_v8_basic_const_inputs_out_axes_val) { + PartialShape data_shape{10, 10, 10, 10, 10, 10, 10, 10}; + + std::vector start_val{1, 1, -20, 9, 9, 9, 9, 20}; + std::vector stop_val{8, 8, 20, -11, 0, -10, -11, -20}; + std::vector step_val{1, 2, 1, -1, -1, -1, -2, -1}; + + element::Type_t et = element::i32; + { + try { + std::vector axes_val{2, 0, -20, 7, 1, 20, 6, 4}; + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + FAIL() << "Slice validation did not work!"; + } catch (const ov::AssertFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "must be in range of the `data` input rank: [-8, 7]. Got: -20"); + } + } + { + try { + std::vector axes_val{2, 0, 9, 7, 1, 20, 6, 4}; + std::vector> input_vals{start_val, stop_val, step_val, axes_val}; + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "must be in range of the `data` input rank: [-8, 7]. Got: 9"); + } + } + { + try { + const auto data = std::make_shared(et, data_shape); + const auto start = std::make_shared(et, PartialShape{2}); + const auto stop = std::make_shared(et, PartialShape{2}); + const auto step = std::make_shared(et, PartialShape{2}); + const auto axes = std::make_shared(et, Shape{2}, std::vector{-15, 7}); + const auto op = std::make_shared(data, start, stop, step, axes); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "must be in range of the `data` input rank: [-8, 7]. Got: -15"); + } + } +} + +TEST(type_prop, slice_v8_basic_const_inputs_step_zero) { + PartialShape data_shape{10, 10, 10, 10, 10, 10, 10, 10}; + PartialShape expected_out_shape{4, 9, 7, 10, 10, 9, 5, 10}; + + std::vector start_val{1, 1, -20, 9, 9, 9, 9, 20}; + std::vector stop_val{8, 8, 20, -11, 0, -10, -11, -20}; + + element::Type_t et = element::i32; + { + std::vector step_val{1, 2, 0, -1, -1, -1, -2, -1}; + std::vector> input_vals{start_val, stop_val, step_val}; + try { + const auto op = make_slice_op_const_inputs(input_vals, data_shape, et); + FAIL() << "Slice validation did not work!"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), "'step' value can't be zero"); + } + } +} + +TEST(type_prop, slice_v8_dynamic_rank_inputs) { + PartialShape dyn_rank_shape = PartialShape::dynamic(); + element::Type_t et = element::i32; + + const auto data = std::make_shared(et, dyn_rank_shape); + const auto start = std::make_shared(et, dyn_rank_shape); + const auto stop = std::make_shared(et, dyn_rank_shape); + const auto step = std::make_shared(et, dyn_rank_shape); + const auto axes = std::make_shared(et, dyn_rank_shape); + const auto op = std::make_shared(data, start, stop, step, axes); + + EXPECT_EQ(op->get_output_partial_shape(0), dyn_rank_shape); +} diff --git a/ngraph/test/visitors/op/slice.cpp b/ngraph/test/visitors/op/slice.cpp new file mode 100644 index 00000000000..e29ef9906fb --- /dev/null +++ b/ngraph/test/visitors/op/slice.cpp @@ -0,0 +1,42 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "gtest/gtest.h" +#include "ngraph/ngraph.hpp" +#include "ngraph/op/util/attr_types.hpp" +#include "ngraph/opsets/opset8.hpp" +#include "util/visitor.hpp" + +using namespace std; +using namespace ngraph; +using ngraph::test::NodeBuilder; + +TEST(attributes, slice_op_no_axes) { + NodeBuilder::get_ops().register_factory(); + const auto data = make_shared(element::f32, Shape{1, 3, 5, 4}); + const auto start = make_shared(element::i32, Shape{4}); + const auto stop = make_shared(element::i32, Shape{4}); + const auto step = make_shared(element::i32, Shape{4}); + + const auto op = make_shared(data, start, stop, step); + NodeBuilder builder(op); + + const auto expected_attr_count = 0; + EXPECT_EQ(builder.get_value_map_size(), expected_attr_count); +} + +TEST(attributes, slice_op_with_axes) { + NodeBuilder::get_ops().register_factory(); + const auto data = make_shared(element::f32, Shape{1, 3, 5, 4}); + const auto start = make_shared(element::i32, Shape{4}); + const auto stop = make_shared(element::i32, Shape{4}); + const auto step = make_shared(element::i32, Shape{4}); + const auto axes = make_shared(element::i32, Shape{4}); + + const auto op = make_shared(data, start, stop, step, axes); + NodeBuilder builder(op); + + const auto expected_attr_count = 0; + EXPECT_EQ(builder.get_value_map_size(), expected_attr_count); +}