From e1a33f10d5b4b8bf1efe05dc3875a1ad63f10b96 Mon Sep 17 00:00:00 2001 From: Pawel Raasz Date: Wed, 25 Oct 2023 10:19:14 +0200 Subject: [PATCH] [core]Migrate Slice to new API (#20417) * Migrate slice to new API * Remove visit_attributes, is same as base class * Move shape checks to shape_infer - minor refactor Slice op * Move `get_tensors_partial_shapes` to dev API * Correct comment Co-authored-by: Tomasz Jankowski --------- Co-authored-by: Tomasz Jankowski --- src/core/include/openvino/op/slice.hpp | 7 +- .../include/slice_shape_inference.hpp | 14 +- src/core/src/op/slice.cpp | 249 +++++++----------- 3 files changed, 101 insertions(+), 169 deletions(-) diff --git a/src/core/include/openvino/op/slice.hpp b/src/core/include/openvino/op/slice.hpp index 934e6896f1629e..bb36ea4cac02da 100644 --- a/src/core/include/openvino/op/slice.hpp +++ b/src/core/include/openvino/op/slice.hpp @@ -4,6 +4,7 @@ #pragma once +#include "openvino/op/constant.hpp" #include "openvino/op/op.hpp" namespace ov { @@ -40,14 +41,10 @@ class OPENVINO_API Slice : public Op { 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; - OPENVINO_SUPPRESS_DEPRECATED_START bool has_evaluate() const override; - // TODO: Update to use new evaluate with TensorVector - bool evaluate(const HostTensorVector&, const HostTensorVector&) const override; - OPENVINO_SUPPRESS_DEPRECATED_END + bool evaluate(TensorVector&, const TensorVector&) const override; bool evaluate_lower(TensorVector& outputs) const override; bool evaluate_upper(TensorVector& outputs) const override; bool evaluate_label(TensorLabelVector& output_labels) const override; diff --git a/src/core/shape_inference/include/slice_shape_inference.hpp b/src/core/shape_inference/include/slice_shape_inference.hpp index cd6c3b018bdf68..04461484a81154 100644 --- a/src/core/shape_inference/include/slice_shape_inference.hpp +++ b/src/core/shape_inference/include/slice_shape_inference.hpp @@ -57,6 +57,14 @@ std::vector shape_infer(const Slice* op, const auto& input_shape = input_shapes[0]; const auto& input_rank = input_shape.rank(); + // it is not possible to define output shape if input data shape rank is undefined + // even if lengths of begin, end, or strides are defined + if (input_rank.is_dynamic()) { + return {PartialShape::dynamic()}; + } else { + NODE_SHAPE_INFER_CHECK(op, input_shapes, input_rank.get_length() > 0, "Slice `data` input can't be a scalar."); + } + for (size_t i = 1; i < input_shapes.size(); ++i) { const auto& shape = input_shapes[i]; const auto& shape_rank = shape.rank(); @@ -87,12 +95,6 @@ std::vector shape_infer(const Slice* op, "Slice `start`, `stop`, `step` inputs must have compatible shapes."); auto output_shapes = std::vector(1); - // it is not possible to define output shape if input data shape rank is undefined - // even the lengths of begin, end, or strides are defined - if (input_rank.is_dynamic()) { - output_shapes[0] = PartialShape::dynamic(); - return output_shapes; - } // compute constant values of begin, end, and strides if possible const auto start = get_input_bounds(op, 1, ta); diff --git a/src/core/src/op/slice.cpp b/src/core/src/op/slice.cpp index f08a885bea280e..0bca5274b15ff2 100644 --- a/src/core/src/op/slice.cpp +++ b/src/core/src/op/slice.cpp @@ -2,223 +2,156 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "ngraph/op/slice.hpp" - -#include +#include "openvino/op/slice.hpp" #include "bound_evaluate.hpp" #include "itt.hpp" -#include "ngraph/attribute_visitor.hpp" -#include "ngraph/graph_util.hpp" -#include "ngraph/op/constant.hpp" #include "openvino/reference/slice.hpp" #include "slice_shape_inference.hpp" -using namespace std; -using namespace ngraph; +namespace ov { +namespace op { +namespace { +std::vector default_axes(const size_t n) { + std::vector axes; + axes.reserve(n); + std::generate_n(std::back_inserter(axes), n, SeqGen(0)); + return axes; +} + +bool slice_bound_check(const ov::Node* const node) { + return ov::have_node_inputs_bounds_set(node, 1, node->get_input_size() - 1); +} + +bool slice_no_axes(const Node* const node) { + return node->get_input_size() < 5; +} +} // namespace + +namespace v8 { +using ov::op::v0::Constant; -op::v8::Slice::Slice(const Output& data, - const Output& start, - const Output& stop, - const Output& step) +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) +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(); } -bool op::v8::Slice::visit_attributes(AttributeVisitor& visitor) { - OV_OP_SCOPE(v8_Slice_visit_attributes); - return true; -} - -std::shared_ptr op::v8::Slice::get_default_const_axes(const Output& start) const { - const auto start_pshape = start.get_partial_shape(); +std::shared_ptr Slice::get_default_const_axes(const Output& start) const { + 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 v0::Constant::create(element::i64, Shape{axes_length}, axes); + if (start_pshape.is_static() && start_pshape.size() == 1) { + const auto axes = default_axes(static_cast(start_pshape[0].get_length())); + return Constant::create(element::i64, start_pshape.get_shape(), axes); } else { // Dynamic case return {}; } -} +} // namespace ov -void op::v8::Slice::validate_and_infer_types() { +void Slice::validate_and_infer_types() { OV_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."); - - if (get_input_size() < 5) { + if (slice_no_axes(this)) { if (auto axes_const = get_default_const_axes(input_value(1))) { set_argument(4, axes_const); } } - for (size_t i = 0; i < get_input_size(); ++i) { - if (i > 0) { - NODE_VALIDATION_CHECK(this, - get_input_element_type(i).is_integral_number(), - "Slice `", - slice::shape_names[i - 1], - "` input type must be integer."); - } - - set_input_is_relevant_to_shape(i); - } - 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); + + set_input_is_relevant_to_shape(0); + for (size_t i = 1; i < get_input_size(); ++i) { + NODE_VALIDATION_CHECK(this, + get_input_element_type(i).is_integral_number(), + "Slice `", + slice::shape_names[i - 1], + "` input type must be integer."); + set_input_is_relevant_to_shape(i); + } + set_output_type(0, get_input_element_type(0), output_shapes.front()); } -std::shared_ptr op::v8::Slice::clone_with_new_inputs(const OutputVector& new_args) const { +std::shared_ptr Slice::clone_with_new_inputs(const OutputVector& new_args) const { OV_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)); + 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)); + return std::make_shared(new_args.at(0), new_args.at(1), new_args.at(2), new_args.at(3), new_args.at(4)); } } -bool op::v8::Slice::has_evaluate() const { +bool Slice::has_evaluate() const { OV_OP_SCOPE(v8_Slice_has_evaluate); - switch (get_input_element_type(1)) { - case ngraph::element::i8: - case ngraph::element::i16: - case ngraph::element::i32: - case ngraph::element::i64: - case ngraph::element::u8: - case ngraph::element::u16: - case ngraph::element::u32: - case ngraph::element::u64: - break; - default: - return false; - } - if (get_input_size() > 4) { - switch (get_input_element_type(4)) { - case ngraph::element::i8: - case ngraph::element::i16: - case ngraph::element::i32: - case ngraph::element::i64: - case ngraph::element::u8: - case ngraph::element::u16: - case ngraph::element::u32: - case ngraph::element::u64: - break; + const auto valid_integral_type = [](const element::Type& et) -> bool { + switch (et) { + case element::i8: + case element::i16: + case element::i32: + case element::i64: + case element::u8: + case element::u16: + case element::u32: + case element::u64: + return true; default: return false; } - } + }; - return true; + return valid_integral_type(get_input_element_type(1)) && + (slice_no_axes(this) || valid_integral_type(get_input_element_type(4))); } -OPENVINO_SUPPRESS_DEPRECATED_START -bool op::v8::Slice::evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const { +bool Slice::evaluate(TensorVector& outputs, const TensorVector& inputs) const { OV_OP_SCOPE(v8_Slice_evaluate); - OPENVINO_ASSERT(inputs.size() >= 4, "Slice evaluate needs at least 4 inputs."); - - // Static HostTensor data shape is needed to clamp and normalize `start` values - OPENVINO_ASSERT(inputs[0]->get_partial_shape().is_static(), - "Can't evaluate Slice elements without static HostTensor data shape."); - - auto input_shapes = std::vector(); - input_shapes.reserve(inputs.size()); - - for (size_t i = 0; i < inputs.size(); ++i) { - auto&& tensor = inputs[i]; - input_shapes.push_back(tensor->get_partial_shape()); - } - - OPENVINO_SUPPRESS_DEPRECATED_START - const auto starts = host_tensor_2_vector(inputs[1]); - const auto stops = host_tensor_2_vector(inputs[2]); - const auto steps = host_tensor_2_vector(inputs[3]); - std::vector axes; - if (inputs.size() < 5) { - axes.reserve(starts.size()); - std::generate_n(std::back_inserter(axes), starts.size(), SeqGen(0)); - } else { - axes = host_tensor_2_vector(inputs[4]); - } - OPENVINO_SUPPRESS_DEPRECATED_END - - const auto output_shapes = shape_infer(this, input_shapes, make_tensor_accessor(inputs)); - OPENVINO_ASSERT(output_shapes.front().is_static(), "Can't calculate static output shape for Slice evaluation."); - - outputs[0]->set_shape(output_shapes.front().to_shape()); - outputs[0]->set_element_type(inputs[0]->get_element_type()); - - ov::reference::slice(inputs[0]->get_data_ptr(), - inputs[0]->get_shape(), - outputs[0]->get_data_ptr(), - outputs[0]->get_shape(), - inputs[0]->get_element_type().size(), - starts, - steps, - axes); - return true; -} -OPENVINO_SUPPRESS_DEPRECATED_END - -namespace { -bool slice_input_check(const ov::Node* node) { - if (!node->get_input_tensor(1).has_and_set_bound()) - return false; - if (!node->get_input_tensor(2).has_and_set_bound()) - return false; - if (!node->get_input_tensor(3).has_and_set_bound()) - return false; - if (node->get_input_size() == 5 && !node->get_input_tensor(4).has_and_set_bound()) - return false; + const auto output_shapes = + shape_infer(this, ov::util::get_tensors_partial_shapes(inputs), make_tensor_accessor(inputs)); + outputs[0].set_shape(output_shapes.front().to_shape()); + + const auto starts = ov::get_tensor_data_as(inputs[1]); + const auto steps = ov::get_tensor_data_as(inputs[3]); + const auto axes = slice_no_axes(this) ? default_axes(starts.size()) : ov::get_tensor_data_as(inputs[4]); + + reference::slice(static_cast(inputs[0].data()), + inputs[0].get_shape(), + static_cast(outputs[0].data()), + outputs[0].get_shape(), + inputs[0].get_element_type().size(), + starts, + steps, + axes); return true; } -} // namespace -bool op::v8::Slice::evaluate_lower(ov::TensorVector& output_values) const { - return slice_input_check(this) && default_lower_bound_evaluator(this, output_values); +bool Slice::evaluate_lower(ov::TensorVector& output_values) const { + return slice_bound_check(this) && default_lower_bound_evaluator(this, output_values); } -bool op::v8::Slice::evaluate_upper(ov::TensorVector& output_values) const { - return slice_input_check(this) && default_upper_bound_evaluator(this, output_values); +bool Slice::evaluate_upper(ov::TensorVector& output_values) const { + return slice_bound_check(this) && default_upper_bound_evaluator(this, output_values); } -bool op::v8::Slice::evaluate_label(TensorLabelVector& output_labels) const { - if (!slice_input_check(this)) - return false; +bool Slice::evaluate_label(TensorLabelVector& output_labels) const { OPENVINO_SUPPRESS_DEPRECATED_START - return default_label_evaluator(this, output_labels); + return slice_bound_check(this) && default_label_evaluator(this, output_labels); OPENVINO_SUPPRESS_DEPRECATED_END } +} // namespace v8 +} // namespace op +} // namespace ov