diff --git a/src/core/include/openvino/op/non_zero.hpp b/src/core/include/openvino/op/non_zero.hpp index e14d757e5ff341..2a39e85b9ff4c3 100644 --- a/src/core/include/openvino/op/non_zero.hpp +++ b/src/core/include/openvino/op/non_zero.hpp @@ -57,9 +57,7 @@ class OPENVINO_API NonZero : public Op { // Overload collision with method on Node using Node::set_output_type; - 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; protected: diff --git a/src/core/reference/include/openvino/reference/non_zero.hpp b/src/core/reference/include/openvino/reference/non_zero.hpp index 69276e37594d9c..a693bfbe94990f 100644 --- a/src/core/reference/include/openvino/reference/non_zero.hpp +++ b/src/core/reference/include/openvino/reference/non_zero.hpp @@ -4,9 +4,12 @@ #pragma once +#include #include #include "openvino/core/shape.hpp" +#include "openvino/reference/utils/coordinate_index.hpp" +#include "openvino/reference/utils/coordinate_transform.hpp" namespace ov { namespace reference { @@ -17,26 +20,9 @@ namespace reference { /// Output number of non-zero entries in arg template size_t non_zero_get_count(const T* arg, const Shape& arg_shape) { - T zero = 0; - size_t arg_rank = arg_shape.size(); - size_t arg_count = shape_size(arg_shape); - size_t non_zero_count = 0; - - // Input arg is scalar - if (arg_rank == 0) { - if (*arg != zero) { - non_zero_count = 1; - } - } else // Input is non scalar case - { - for (size_t i = 0; i < arg_count; i++) { - if (arg[i] != zero) { - non_zero_count++; - } - } - } - - return non_zero_count; + const auto zero = T{0}; + const auto arg_count = shape_size(arg_shape); + return arg_count - std::count(arg, arg + arg_count, zero); } /// \brief Return indices of non-zero entries in input argument. @@ -46,41 +32,22 @@ size_t non_zero_get_count(const T* arg, const Shape& arg_shape) { /// \param out Output containing indices of non-zero entries in arg template void non_zero(const T* arg, U* out, const Shape& arg_shape) { - T zero = 0; - size_t arg_rank = arg_shape.size(); - size_t arg_count = shape_size(arg_shape); - - size_t non_zero_count = non_zero_get_count(arg, arg_shape); - - // Input arg only contains 0s - if (non_zero_count == 0) { + const auto non_zero_count = non_zero_get_count(arg, arg_shape); + if (non_zero_count == 0) return; - } - // Input arg is non-zero scalar - if (arg_rank == 0) { - out[0] = static_cast(0); + const auto arg_count = shape_size(arg_shape); + if (arg_count == 1) { + out[0] = U{0}; return; } // Dimensional size for the arg_shape. This is used to map one-dimentional // arg array indices to corresponding arg_rank-dimentional shape indices. - // i.e., arg_shape {2, 3, 2} => elem_per_axis {6, 2, 1}. + // i.e., arg_shape {2, 3, 2} // Array index 4 in arg (arg[4]) correspond to 3-D index of [0][2][0] - std::vector elem_per_axis; - elem_per_axis.reserve(arg_rank); - - size_t temp = arg_count; - for (size_t i = 0; i < arg_rank; i++) { - temp = temp / arg_shape[i]; - elem_per_axis.push_back(temp); - } - - // Column index in out to record a non-zero entry - size_t col_index = 0; - - // Array index in out to write non-zero index value - size_t out_index = 0; + const auto arg_strides = row_major_strides(arg_shape); + const auto arg_transform = CoordinateTransformBasic{arg_shape}; // Find non-zero entries in arg and write the indices info in out. // For a non-zero entry, map its array index to corresponding indices @@ -98,18 +65,15 @@ void non_zero(const T* arg, U* out, const Shape& arg_shape) { // // input[0][2][0] = 3 is arg[4] // output for this entry out[1] = 0, out[8] = 2, out[15] = 0 - for (size_t i = 0; i < arg_count; i++) { - if (arg[i] != zero) { - temp = i; - - for (size_t j = 0; j < arg_rank; j++) { - out_index = j * non_zero_count + col_index; - out[out_index] = static_cast(temp / elem_per_axis[j]); - - temp = temp % elem_per_axis[j]; + const auto arg_rank = arg_shape.size(); + size_t col_index = 0; + for (const auto& arg_coord : arg_transform) { + const auto arg_index = coordinate_offset(arg_coord, arg_strides); + if (arg[arg_index] != T{0}) { + for (size_t j = 0, out_index = col_index; j < arg_rank; ++j, out_index += non_zero_count) { + out[out_index] = static_cast(arg_coord[j]); } - - col_index++; + ++col_index; } } } diff --git a/src/core/src/op/non_zero.cpp b/src/core/src/op/non_zero.cpp index a281cdf2645268..8257c05924fae4 100644 --- a/src/core/src/op/non_zero.cpp +++ b/src/core/src/op/non_zero.cpp @@ -2,179 +2,147 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "ngraph/op/non_zero.hpp" +#include "openvino/op/non_zero.hpp" -#include #include +#include "element_visitor.hpp" #include "itt.hpp" -#include "ngraph/op/op.hpp" -#include "ngraph/runtime/host_tensor.hpp" -#include "ngraph/type/element_type_traits.hpp" +#include "openvino/core/type/element_type_traits.hpp" #include "openvino/reference/non_zero.hpp" +#include "validation_util.hpp" + +namespace ov { +namespace op { +namespace non_zero { +struct Evaluate : public element::NoAction { + using element::NoAction::visit; + + template > + static result_type visit(const Tensor& in, const Shape& in_shape, const size_t in_rank, Tensor& out) { + const auto in_data = in.data(); + const size_t non_zero_count = reference::non_zero_get_count(in_data, in_shape); + const auto out_shape = Shape{in_rank == 0 && non_zero_count > 0 ? 1 : in_rank, non_zero_count}; + out.set_shape(out_shape); + + using namespace ov::element; + return IfTypeOf::apply(out.get_element_type(), in_data, out, in_shape); + } -using namespace ngraph; -using namespace std; - -op::v3::NonZero::NonZero(const Output& arg) : Op({arg}) { +private: + struct EvalByOutType : public element::NoAction { + using element::NoAction::visit; + + template > + static result_type visit(const T* in, Tensor& out, const Shape& in_shape) { + reference::non_zero(in, out.data(), in_shape); + return true; + } + }; +}; +} // namespace non_zero + +namespace v3 { +NonZero::NonZero(const Output& arg) : Op({arg}) { constructor_validate_and_infer_types(); } -op::v3::NonZero::NonZero(const Output& arg, const std::string& output_type) +NonZero::NonZero(const Output& arg, const std::string& output_type) : Op({arg}), m_output_type(EnumNames::as_enum(output_type)) { constructor_validate_and_infer_types(); } -op::v3::NonZero::NonZero(const Output& arg, const element::Type& output_type) - : Op({arg}), - m_output_type(output_type) { +NonZero::NonZero(const Output& arg, const element::Type& output_type) : Op({arg}), m_output_type(output_type) { constructor_validate_and_infer_types(); } -bool ngraph::op::v3::NonZero::visit_attributes(AttributeVisitor& visitor) { +bool NonZero::visit_attributes(AttributeVisitor& visitor) { OV_OP_SCOPE(v3_NonZero_visit_attributes); visitor.on_attribute("output_type", m_output_type); return true; } -void op::v3::NonZero::validate_and_infer_types() { +void NonZero::validate_and_infer_types() { OV_OP_SCOPE(v3_NonZero_validate_and_infer_types); NODE_VALIDATION_CHECK(this, m_output_type == element::i64 || m_output_type == element::i32, "Output type must be i32 or i64"); // For scalar non-zero value case, onnx test case expects output shape {1, 1} - const ov::PartialShape& input_shape = get_input_partial_shape(0); + const auto& input_shape = get_input_partial_shape(0); if (input_shape.rank().compatible(0)) { - set_output_type(0, m_output_type, ov::PartialShape{Dimension::dynamic(), Dimension::dynamic()}); + set_output_type(0, m_output_type, PartialShape::dynamic(2)); } else { - const Dimension dim = - std::accumulate(begin(input_shape), end(input_shape), Dimension(0, 1), std::multiplies()); - set_output_type(0, m_output_type, ov::PartialShape{input_shape.rank(), dim}); + auto dim = Dimension{0, 1}; + for (auto&& d : input_shape) + dim *= d; + set_output_type(0, m_output_type, PartialShape{input_shape.rank(), dim}); } set_input_is_relevant_to_shape(0); - OPENVINO_SUPPRESS_DEPRECATED_START - if (const auto& input_constant = get_constant_from_source(input_value(0))) { + if (const auto input_constant = ov::util::get_constant_from_source(input_value(0))) { // input_value is available to calculate output shape - const auto& input_data = std::make_shared(input_constant); - auto output = std::make_shared(m_output_type, get_output_partial_shape(0)); - if (!evaluate({output}, {input_data})) - return; - set_output_type(0, m_output_type, output->get_partial_shape()); - auto t = Tensor(output->get_element_type(), output->get_shape()); - memcpy(t.data(), output->get_data_ptr(), t.get_byte_size()); + // const_cast of Constant data is needed to avoid obsolete copy of this data into the Tensor. + // It's safe here as evaluate() method doesn't modify input Tensors. + const auto inputs = TensorVector{{input_constant->get_element_type(), + input_constant->get_shape(), + const_cast(input_constant->get_data_ptr())}}; + auto outputs = TensorVector{{m_output_type, {}}}; + if (!evaluate(outputs, inputs)) + return; + const auto& output = outputs[0]; + set_output_type(0, m_output_type, output.get_shape()); - get_output_tensor(0).set_lower_value(t); - get_output_tensor(0).set_upper_value(t); + get_output_tensor(0).set_lower_value(output); + get_output_tensor(0).set_upper_value(output); } - OPENVINO_SUPPRESS_DEPRECATED_END } -shared_ptr op::v3::NonZero::clone_with_new_inputs(const OutputVector& new_args) const { +std::shared_ptr NonZero::clone_with_new_inputs(const OutputVector& new_args) const { OV_OP_SCOPE(v3_NonZero_clone_with_new_inputs); check_new_args_count(this, new_args); - return make_shared(new_args.at(0), m_output_type); -} - -OPENVINO_SUPPRESS_DEPRECATED_START -namespace nonzero { -namespace { -template -bool evaluate_nonzero_execute(const HostTensorPtr& input, const HostTensorPtr& output) { - using IN_T = typename element_type_traits::value_type; - using OUT_T = typename element_type_traits::value_type; - - ov::Shape input_shape = input->get_shape(); - size_t input_rank = input_shape.size(); - - size_t non_zero_count = ov::reference::non_zero_get_count(input->get_data_ptr(), input_shape); - - ov::Shape out_shape; - if (input_rank == 0 && non_zero_count > 0) { - out_shape = ov::Shape{1, 1}; - } else { - out_shape = ov::Shape{input_rank, non_zero_count}; - } - - output->set_shape(out_shape); - ov::reference::non_zero(input->get_data_ptr(), output->get_data_ptr(), input_shape); - - return true; -} - -#define TYPE_OUT_CASE(a, ...) \ - case element::Type_t::a: { \ - OV_OP_SCOPE(OV_PP_CAT3(evaluate_nonzero_out, _, a)); \ - rc = evaluate_nonzero_execute(__VA_ARGS__); \ - } break - -template -bool evaluate(const HostTensorPtr& input, const HostTensorPtr& output) { - bool rc = true; - switch (output->get_element_type()) { - TYPE_OUT_CASE(i64, input, output); - TYPE_OUT_CASE(i32, input, output); - default: - rc = false; - break; - } - - return rc; -} -#undef TYPE_OUT_CASE -bool evaluate_nonzero(const HostTensorPtr& input, const HostTensorPtr& output) { - bool rc = true; - - switch (input->get_element_type()) { - OPENVINO_TYPE_CASE(evaluate_nonzero, boolean, input, output); - OPENVINO_TYPE_CASE(evaluate_nonzero, i8, input, output); - OPENVINO_TYPE_CASE(evaluate_nonzero, i16, input, output); - OPENVINO_TYPE_CASE(evaluate_nonzero, i32, input, output); - OPENVINO_TYPE_CASE(evaluate_nonzero, i64, input, output); - OPENVINO_TYPE_CASE(evaluate_nonzero, u8, input, output); - OPENVINO_TYPE_CASE(evaluate_nonzero, u16, input, output); - OPENVINO_TYPE_CASE(evaluate_nonzero, u32, input, output); - OPENVINO_TYPE_CASE(evaluate_nonzero, u64, input, output); - OPENVINO_TYPE_CASE(evaluate_nonzero, bf16, input, output); - OPENVINO_TYPE_CASE(evaluate_nonzero, f16, input, output); - OPENVINO_TYPE_CASE(evaluate_nonzero, f32, input, output); - OPENVINO_TYPE_CASE(evaluate_nonzero, f64, input, output); - default: - rc = false; - break; - } - return rc; + return std::make_shared(new_args.at(0), m_output_type); } -} // namespace -} // namespace nonzero -bool op::v3::NonZero::evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const { +bool NonZero::evaluate(TensorVector& outputs, const TensorVector& inputs) const { OV_OP_SCOPE(v3_NonZero_evaluate); - return nonzero::evaluate_nonzero(inputs[0], outputs[0]); + + const auto& input = inputs[0]; + auto& output = outputs[0]; + using namespace ov::element; + const auto& input_shape = input.get_shape(); + return IfTypeOf::apply( + input.get_element_type(), + input, + input_shape, + input_shape.size(), + output); } -bool op::v3::NonZero::has_evaluate() const { +bool NonZero::has_evaluate() const { OV_OP_SCOPE(v3_NonZero_has_evaluate); switch (get_input_element_type(0)) { - 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: - case ngraph::element::bf16: - case ngraph::element::f16: - case ngraph::element::f32: - case ngraph::element::f64: + case element::boolean: + case element::bf16: + case element::f16: + case element::f32: + case element::f64: + 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: - break; + return false; } - return false; } +} // namespace v3 +} // namespace op +} // namespace ov