From 6d2740a335f8b608f188ad230ee8d6399cf08108 Mon Sep 17 00:00:00 2001 From: Pavel Esir Date: Mon, 12 Apr 2021 17:01:39 +0300 Subject: [PATCH] [nGraph] Gather 7 evaluate (#5088) * initial working solution * constant_folding for Gather-7 successfully enabled * successfuuly added remaining unittests * removed redundant evaluate from private * fixed evaluate for dynamic axis * fix pre-commit for KMB: added default batch_dims=0 to runtime/reference * clang_format_fix * added include ngraph/shape.hpp to fix compilation for onednn-plugin * temporary removed out of bound check for gather * removed optional argument for batch_dims * added const * finally successfully run on ARM: removed redundant declaration * style fix * changed argument types from size_t -> int64_t * changed back to size_t --- ngraph/core/include/ngraph/op/gather.hpp | 10 + .../ngraph/runtime/reference/gather.hpp | 125 +-- ngraph/core/src/op/gather.cpp | 94 ++- ngraph/test/backend/gather.in.cpp | 735 +++++++++++++++++- ngraph/test/constant_folding.cpp | 270 +++++++ ngraph/test/eval.cpp | 47 +- ngraph/test/runtime/ie/unit_test.manifest | 30 +- .../runtime/interpreter/unit_test.manifest | 5 + ngraph/test/type_prop/gather.cpp | 4 +- 9 files changed, 1204 insertions(+), 116 deletions(-) diff --git a/ngraph/core/include/ngraph/op/gather.hpp b/ngraph/core/include/ngraph/op/gather.hpp index be935559721..6a1c096f04f 100644 --- a/ngraph/core/include/ngraph/op/gather.hpp +++ b/ngraph/core/include/ngraph/op/gather.hpp @@ -81,6 +81,16 @@ namespace ngraph int64_t get_axis() const; bool is_axis_set() const; + bool evaluate_gather(const HostTensorVector& outputs, + const HostTensorVector& inputs) const; + bool evaluate(const HostTensorVector& outputs, + const HostTensorVector& inputs) const override; + bool evaluate_lower(const HostTensorVector& outputs) const override; + bool evaluate_upper(const HostTensorVector& outputs) const override; + + bool constant_fold(OutputVector& output_values, + const OutputVector& inputs_values) override; + private: int64_t m_batch_dims = 0; }; diff --git a/ngraph/core/reference/include/ngraph/runtime/reference/gather.hpp b/ngraph/core/reference/include/ngraph/runtime/reference/gather.hpp index 365bdf9ac25..fcdf1c2a122 100644 --- a/ngraph/core/reference/include/ngraph/runtime/reference/gather.hpp +++ b/ngraph/core/reference/include/ngraph/runtime/reference/gather.hpp @@ -6,9 +6,7 @@ #include -#include "ngraph/coordinate_range.hpp" -#include "ngraph/coordinate_transform.hpp" -#include "ngraph/runtime/reference/gather_nd.hpp" +#include "ngraph/shape.hpp" #include "utils/span.hpp" namespace ngraph @@ -17,107 +15,54 @@ namespace ngraph { namespace reference { - namespace - { - template - Shape to_shape(const Container& c) - { - return Shape(begin(c), end(c)); - } - - template - std::vector - join(const Container& c1, const Container& c2, const Container& c3) - { - using container_value_type = - typename std::remove_cv::type; - static_assert(std::is_same::value, - "Expect same type in container"); - std::vector ret; - ret.reserve(c1.size() + c2.size() + c3.size()); - std::copy(begin(c1), end(c1), std::back_inserter(ret)); - std::copy(begin(c2), end(c2), std::back_inserter(ret)); - std::copy(begin(c3), end(c3), std::back_inserter(ret)); - return ret; - } - - const auto only_one = [] { return coordinates::index(Shape{1}); }; - } // namespace template - void gather(const T* const params, + void gather(const T* const data, const U* const indices, - T* const out, - const Shape& params_shape, + T* out, + const Shape& data_shape, const Shape& indices_shape, const Shape& out_shape, - size_t axis) + size_t axis, + size_t batch_dims = 0) { - using std::next; - assert(std::memset(out, 0, shape_size(out_shape) * sizeof(T))); + // flattened shapes + int64_t batch_size = shape_size(span(data_shape).subspan(0, batch_dims)); + int64_t outer_size = + shape_size(span(data_shape).subspan(batch_dims, axis - batch_dims)); + int64_t indices_size = shape_size(span(indices_shape).subspan(batch_dims)); + int64_t inner_size = shape_size(span(data_shape).subspan(axis + 1)); - const auto params_axes_part = span(params_shape).subspan(0, axis); + int64_t batch_data_mul = shape_size(span(data_shape).subspan(batch_dims)); + int64_t batch_out_mul = shape_size(span(out_shape).subspan(batch_dims)); + int64_t batch_indices_mul = shape_size(span(indices_shape).subspan(batch_dims)); - NGRAPH_CHECK(params_shape.size() >= axis, "Not enough axes in param_shape."); + int64_t axis_size = data_shape[axis]; + int64_t data_offset, out_offset, idx; - const auto remainder_part_shape = span(params_shape).subspan(axis + 1); - - const auto found_out_shape = - join(params_axes_part, span(indices_shape), remainder_part_shape); - - NGRAPH_CHECK(found_out_shape == out_shape, - "Output shape mismatch with calculations"); - - const auto batch_shape = span(params_shape).subspan(axis); - - const auto batch_size = shape_size(batch_shape); - - const auto copy_size = shape_size(remainder_part_shape); - - const size_t copy_round_in_batch = - indices_shape.size() > 1 - ? shape_size(span(indices_shape.data(), indices_shape.size() - 1)) - : 1; - const size_t round_batch_offset = indices_shape.empty() ? 1 : indices_shape.back(); - - auto dst = out; - - auto gather_range = params_axes_part.empty() - ? only_one() - : coordinates::index(to_shape(params_axes_part)); - for (auto i : gather_range) - { - auto batch_index = i.begin_index; - for (size_t batch = 0; batch != i.element_number; - batch_index += i.step, ++batch) + for (int64_t batch = 0; batch < batch_size; batch++) + for (int64_t outer_idx = 0; outer_idx < outer_size; outer_idx++) { - const auto batch_offset = batch_index * batch_size; - assert(batch_offset < shape_size(params_shape)); - for (size_t round = 0; round != copy_round_in_batch; ++round) + data_offset = batch_data_mul * batch + inner_size * axis_size * outer_idx; + out_offset = batch_out_mul * batch + indices_size * inner_size * outer_idx; + for (int64_t i = 0; i < indices_size; i++) { - const U* input_indices = indices + round * round_batch_offset; - const auto indices_no = - indices_shape.empty() ? 1 : indices_shape.back(); + idx = indices[i + batch_indices_mul * batch]; + // clang-format off + // todo: check if bound check is needed + // if (idx >= axis_size || (idx < 0 && -idx >= axis_size)) + // throw std::domain_error{"indices values of Gather exceed size along axis"}; + // clang-format on + if (idx < 0) + idx += axis_size; - assert(!batch_shape.empty()); - for (size_t ii = 0; ii != indices_no; ++ii) - { - const auto positive_input_index = - input_indices[ii] < 0 ? batch_shape.front() + input_indices[ii] - : input_indices[ii]; - - const auto src_offset = - batch_offset + copy_size * positive_input_index; - - const auto src_begin = next(params, src_offset); - const auto src_end = next(src_begin, copy_size); - - std::copy(src_begin, src_end, dst); - dst += copy_size; - } + const auto src_begin = std::next(data, data_offset + inner_size * idx); + const auto src_end = std::next(src_begin, inner_size); + const auto out_ptr = std::next(out, out_offset + inner_size * i); + std::copy(src_begin, src_end, out_ptr); } } - } } + } // namespace reference } // namespace runtime } // namespace ngraph diff --git a/ngraph/core/src/op/gather.cpp b/ngraph/core/src/op/gather.cpp index f64f5e0a9f9..df1be923b2e 100644 --- a/ngraph/core/src/op/gather.cpp +++ b/ngraph/core/src/op/gather.cpp @@ -202,8 +202,8 @@ void op::v7::Gather::validate_and_infer_types() { NODE_VALIDATION_CHECK( this, - batch_dims < indices_rank.get_length(), - "The batch_dims must be < indices_rank. But instead got: batch_dims = ", + batch_dims <= indices_rank.get_length(), + "The batch_dims must be <= indices_rank. But instead got: batch_dims = ", batch_dims, ", indices_rank = ", indices_rank.get_length()); @@ -315,18 +315,19 @@ namespace gather bool evaluate(const HostTensorPtr& arg0, const HostTensorPtr& arg1, const HostTensorPtr& out, - size_t axis) + size_t axis, + size_t batch_dims) { using T = typename element_type_traits::value_type; Shape params_shape = arg0->get_shape(); Shape indices_shape = arg1->get_shape(); - Shape out_shape(params_shape.size() + indices_shape.size() - 1); + Shape out_shape(params_shape.size() + indices_shape.size() - 1 - batch_dims); uint64_t i = 0; for (; i < axis; i++) { out_shape[i] = params_shape[i]; } - for (uint64_t j = 0; j < indices_shape.size(); i++, j++) + for (uint64_t j = batch_dims; j < indices_shape.size(); i++, j++) { out_shape[i] = indices_shape[j]; } @@ -345,7 +346,8 @@ namespace gather arg0->get_shape(), arg1->get_shape(), out->get_shape(), - axis); + axis, + batch_dims); } else if (arg1->get_element_type() == element::i32) { @@ -355,7 +357,8 @@ namespace gather arg0->get_shape(), arg1->get_shape(), out->get_shape(), - axis); + axis, + batch_dims); } else { @@ -368,19 +371,20 @@ namespace gather bool evaluate_gather(const HostTensorPtr& arg0, const HostTensorPtr& arg1, const HostTensorPtr& out, - size_t axis) + size_t axis, + size_t batch_dims = 0) { bool rc = true; switch (out->get_element_type()) { - NGRAPH_TYPE_CASE(evaluate_gather, i32, arg0, arg1, out, axis); - NGRAPH_TYPE_CASE(evaluate_gather, i64, arg0, arg1, out, axis); - NGRAPH_TYPE_CASE(evaluate_gather, u32, arg0, arg1, out, axis); - NGRAPH_TYPE_CASE(evaluate_gather, u64, arg0, arg1, out, axis); - NGRAPH_TYPE_CASE(evaluate_gather, f16, arg0, arg1, out, axis); - NGRAPH_TYPE_CASE(evaluate_gather, f32, arg0, arg1, out, axis); - NGRAPH_TYPE_CASE(evaluate_gather, boolean, arg0, arg1, out, axis); + NGRAPH_TYPE_CASE(evaluate_gather, i32, arg0, arg1, out, axis, batch_dims); + NGRAPH_TYPE_CASE(evaluate_gather, i64, arg0, arg1, out, axis, batch_dims); + NGRAPH_TYPE_CASE(evaluate_gather, u32, arg0, arg1, out, axis, batch_dims); + NGRAPH_TYPE_CASE(evaluate_gather, u64, arg0, arg1, out, axis, batch_dims); + NGRAPH_TYPE_CASE(evaluate_gather, f16, arg0, arg1, out, axis, batch_dims); + NGRAPH_TYPE_CASE(evaluate_gather, f32, arg0, arg1, out, axis, batch_dims); + NGRAPH_TYPE_CASE(evaluate_gather, boolean, arg0, arg1, out, axis, batch_dims); default: rc = false; break; } return rc; @@ -518,3 +522,63 @@ bool op::v1::Gather::constant_fold(OutputVector& output_values, const OutputVect output_values, input_values, get_output_partial_shape(0)); } } + +bool op::v7::Gather::evaluate_gather(const HostTensorVector& outputs, + const HostTensorVector& inputs) const +{ + int64_t axis = 0; + switch (inputs[2]->get_element_type()) + { + case element::Type_t::i32: axis = inputs[2]->get_data_ptr()[0]; break; + case element::Type_t::i64: axis = inputs[2]->get_data_ptr()[0]; break; + default: throw ngraph_error("axis must be of int32 or int64 type."); + } + + if (axis < 0) + { + const auto& input_rank = get_input_partial_shape(0).rank(); + if (input_rank.is_static()) + { + axis += input_rank.get_length(); + } + } + return gather::evaluate_gather(inputs[0], inputs[1], outputs[0], axis, get_batch_dims()); +} + +bool op::v7::Gather::evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const +{ + NGRAPH_OP_SCOPE(v7_Gather_evaluate); + NGRAPH_CHECK(this, validate_host_tensor_vector(inputs, 3)); + NGRAPH_CHECK(this, validate_host_tensor_vector(outputs, 1)); + return evaluate_gather(outputs, inputs); +} + +bool op::v7::Gather::evaluate_lower(const HostTensorVector& output_values) const +{ + if (!input_value(1).get_tensor().has_and_set_bound() || + !input_value(2).get_tensor().has_and_set_bound()) + return false; + return default_lower_bound_evaluator(this, output_values); +} + +bool op::v7::Gather::evaluate_upper(const HostTensorVector& output_values) const +{ + if (!input_value(1).get_tensor().has_and_set_bound() || + !input_value(2).get_tensor().has_and_set_bound()) + return false; + return default_upper_bound_evaluator(this, output_values); +} + +bool op::v7::Gather::constant_fold(OutputVector& output_values, const OutputVector& input_values) +{ + // try the regular constant folding just for the Gather node + if (Node::constant_fold(output_values, input_values)) + { + return true; + } + else + { + return gather::cf_gather_with_subgraph( + output_values, input_values, get_output_partial_shape(0)); + } +} diff --git a/ngraph/test/backend/gather.in.cpp b/ngraph/test/backend/gather.in.cpp index dc0f07ff07a..31a77d7d316 100644 --- a/ngraph/test/backend/gather.in.cpp +++ b/ngraph/test/backend/gather.in.cpp @@ -450,11 +450,19 @@ NGRAPH_TEST(${BACKEND_NAME}, gather_axis_0_int32) auto A = op::Constant::create(element::i64, Shape{}, {0}); auto G = make_shared(P, I, A); auto f = make_shared(G, ParameterVector{P, I}); - + // clang-format off auto test_case = test::TestCase(f); - test_case.add_input({10, 11, 20, 21, 30, 31}); - test_case.add_input({0, 1, 1, 2}); - test_case.add_expected_output(out_shape, {10, 11, 20, 21, 20, 21, 30, 31}); + test_case.add_input({10, 11, + 20, 21, + 30, 31}); + test_case.add_input({0, 1, + 1, 2}); + test_case.add_expected_output(out_shape, {10, 11, + 20, 21, + + 20, 21, + 30, 31}); + // clang-format on test_case.run(MIN_FLOAT_TOLERANCE_BITS); } @@ -548,7 +556,560 @@ NGRAPH_TEST(${BACKEND_NAME}, gather_axis_0_uint64) test_case.run(MIN_FLOAT_TOLERANCE_BITS); } -NGRAPH_TEST(${BACKEND_NAME}, gather_axis_0_bool) +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_4d_indices_axis_0_uint8) +{ + Shape data_shape{3, 2}; + Shape indices_shape{2, 2, 3, 4}; + Shape out_shape{2, 2, 3, 4, 2}; + auto P = make_shared(element::u8, data_shape); + auto I = make_shared(element::i32, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {0}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + test_case.add_input({10, 11, 20, 21, 30, 31}); + test_case.add_input({0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, + 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, + 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2}); + test_case.add_expected_output( + out_shape, {10, 11, 20, 21, 20, 21, 30, 31, 10, 11, 20, 21, 20, 21, 30, 31, 10, 11, 20, 21, + 20, 21, 30, 31, 10, 11, 20, 21, 20, 21, 30, 31, 10, 11, 20, 21, 20, 21, 30, 31, + 10, 11, 20, 21, 20, 21, 30, 31, 10, 11, 20, 21, 20, 21, 30, 31, 10, 11, 20, 21, + 20, 21, 30, 31, 10, 11, 20, 21, 20, 21, 30, 31, 10, 11, 20, 21, 20, 21, 30, 31, + 10, 11, 20, 21, 20, 21, 30, 31, 10, 11, 20, 21, 20, 21, 30, 31}); + test_case.run(); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_4d_indices_axis_0_2d_input) +{ + Shape data_shape{3, 2}; + Shape indices_shape{2, 2, 3, 4}; + Shape out_shape{2, 2, 3, 4, 2}; + auto P = make_shared(element::f32, data_shape); + auto I = make_shared(element::i32, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {0}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + + // clang-format off + test_case.add_input({1.0f, 1.1f, + 2.0f, 2.1f, + 3.0f, 3.1f}); + + test_case.add_input({0, 1, 1, 2, + 0, 1, 1, 2, + 0, 1, 1, 2, + + 0, 1, 1, 2, + 0, 1, 1, 2, + 0, 1, 1, 2, + + + 0, 1, 1, 2, + 0, 1, 1, 2, + 0, 1, 1, 2, + + 0, 1, 1, 2, + 0, 1, 1, 2, + 0, 1, 1, 2}); + test_case.add_expected_output( + out_shape, + { 1.0f, 1.1f, + 2.0f, 2.1f, + 2.0f, 2.1f, + 3.0f, 3.1f, + + 1.0f, 1.1f, + 2.0f, 2.1f, + 2.0f, 2.1f, + 3.0f, 3.1f, + + 1.0f, 1.1f, + 2.0f, 2.1f, + 2.0f, 2.1f, + 3.0f, 3.1f, + + + 1.0f, 1.1f, + 2.0f, 2.1f, + 2.0f, 2.1f, + 3.0f, 3.1f, + + 1.0f, 1.1f, + 2.0f, 2.1f, + 2.0f, 2.1f, + 3.0f, 3.1f, + + 1.0f, 1.1f, + 2.0f, 2.1f, + 2.0f, 2.1f, + 3.0f, 3.1f, + + + + 1.0f, 1.1f, + 2.0f, 2.1f, + 2.0f, 2.1f, + 3.0f, 3.1f, + + + 1.0f, 1.1f, + 2.0f, 2.1f, + 2.0f, 2.1f, + 3.0f, 3.1f, + + 1.0f, 1.1f, + 2.0f, 2.1f, + 2.0f, 2.1f, + 3.0f, 3.1f, + + + 1.0f, 1.1f, + 2.0f, 2.1f, + 2.0f, 2.1f, + 3.0f, 3.1f, + + 1.0f, 1.1f, + 2.0f, 2.1f, + 2.0f, 2.1f, + 3.0f, 3.1f, + + 1.0f, 1.1f, + 2.0f, 2.1f, + 2.0f, 2.1f, + 3.0f, 3.1f}); + // clang-format on + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_3d_indices_axis_0_2d_input) +{ + Shape data_shape{3, 2}; + Shape indices_shape{2, 3, 4}; + Shape out_shape{2, 3, 4, 2}; + auto P = make_shared(element::f32, data_shape); + auto I = make_shared(element::i32, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {0}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + // clang-format off + test_case.add_input({1.0f, 1.1f, + 2.0f, 2.1f, + 3.0f, 3.1f}); + test_case.add_input( + {0, 1, 1, 2, + 0, 1, 1, 2, + 0, 1, 1, 2, + + 0, 1, 1, 2, + 0, 1, 1, 2, + 0, 1, 1, 2}); + test_case.add_expected_output( + out_shape, {1.0f, 1.1f, + 2.0f, 2.1f, + 2.0f, 2.1f, + 3.0f, 3.1f, + + 1.0f, 1.1f, + 2.0f, 2.1f, + 2.0f, 2.1f, + 3.0f, 3.1f, + + 1.0f, 1.1f, + 2.0f, 2.1f, + 2.0f, 2.1f, + 3.0f, 3.1f, + + + 1.0f, 1.1f, + 2.0f, 2.1f, + 2.0f, 2.1f, + 3.0f, 3.1f, + + 1.0f, 1.1f, + 2.0f, 2.1f, + 2.0f, 2.1f, + 3.0f, 3.1f, + + 1.0f, 1.1f, + 2.0f, 2.1f, + 2.0f, 2.1f, + 3.0f, 3.1f}); + // clang-format on + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_1d_int32) +{ + Shape data_shape{3}; + Shape indices_shape{2}; + Shape out_shape{2}; + auto P = make_shared(element::i32, data_shape); + auto I = make_shared(element::i32, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {0}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + // clang-format off + test_case.add_input({1, 2, 3}); + test_case.add_input({2, 0}); + test_case.add_expected_output(out_shape, {3, 1}); + // clang-format on + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_2d_indices_axis_0_2d_input) +{ + Shape data_shape{3, 2}; + Shape indices_shape{2, 2}; + Shape out_shape{2, 2, 2}; + auto P = make_shared(element::f32, data_shape); + auto I = make_shared(element::i32, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {0}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + // clang-format off + test_case.add_input({1.0f, 1.1f, + 2.0f, 2.1f, + 3.0f, 3.1f}); + // clang-format on + test_case.add_input({0, 1, 1, 2}); + // clang-format off + test_case.add_expected_output(out_shape, + {1.0f, 1.1f, + 2.0f, 2.1f, + + 2.0f, 2.1f, + 3.0f, 3.1f}); + // clang-format on + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_2d_negative_and_positive_indices_axis_0_2d_input) +{ + Shape data_shape{3, 2}; + Shape indices_shape{2, 2}; + Shape out_shape{2, 2, 2}; + auto P = make_shared(element::f32, data_shape); + auto I = make_shared(element::i32, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {0}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + + // clang-format off + test_case.add_input({1.0f, 1.1f, + 2.0f, 2.1f, + 3.0f, 3.1f}); + // clang-format on + + test_case.add_input({0, -2, 1, 2}); + + // clang-format off + test_case.add_expected_output(out_shape, + {1.0f, 1.1f, + 2.0f, 2.1f, + + 2.0f, 2.1f, + 3.0f, 3.1f}); + // clang-format on + + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_1d_indices_axis_0_1d_input) +{ + Shape data_shape{3}; + Shape indices_shape{2}; + Shape out_shape{2}; + auto P = make_shared(element::f32, data_shape); + auto I = make_shared(element::i32, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {0}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + test_case.add_input({1.0f, 2.0f, 3.0f}); + test_case.add_input({1, 0}); + test_case.add_expected_output(out_shape, {2.0f, 1.0f}); + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_scalar_indices_axis_0_2d_input) +{ + Shape data_shape{3, 2}; + Shape indices_shape{}; + Shape out_shape{2}; + auto P = make_shared(element::f32, data_shape); + auto I = make_shared(element::i32, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {0}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + test_case.add_input({1.0f, 1.1f, 2.0f, 2.1f, 3.0f, 3.1f}); + test_case.add_input({1}); + test_case.add_expected_output(out_shape, {2.0f, 2.1f}); + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_2d_indices_axis_1_2d_input) +{ + Shape data_shape{3, 3}; + Shape indices_shape{1, 2}; + Shape out_shape{3, 1, 2}; + auto P = make_shared(element::f32, data_shape); + auto I = make_shared(element::i32, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {1}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + + // clang-format off + test_case.add_input({1.0f, 1.1f, 1.2f, + 2.0f, 2.1f, 2.2f, + 3.0f, 3.1f, 3.2f}); + // clang-format on + test_case.add_input({0, 2}); + + // clang-format off + test_case.add_expected_output(out_shape, {1.0f, 1.2f, + 2.0f, 2.2f, + 3.0f, 3.2f}); + // clang-format on + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_1d_indices_axis_2_4d_input) +{ + Shape data_shape{2, 2, 3, 3}; + Shape indices_shape{2}; + Shape out_shape{2, 2, 2, 3}; + auto P = make_shared(element::f32, data_shape); + auto I = make_shared(element::i32, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {2}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + // clang-format off + test_case.add_input({ 1.0f, 1.1f, 1.2f, + 2.0f, 2.1f, 2.2f, + 3.0f, 3.1f, 3.2f, + + 11.0f, 11.1f, 11.2f, + 12.0f, 12.1f, 12.2f, + 13.0f, 13.1f, 13.2f, + + + 101.0f, 101.1f, 101.2f, + 102.0f, 102.1f, 102.2f, + 103.0f, 103.1f, 103.2f, + + 111.0f, 111.1f, 111.2f, + 112.0f, 112.1f, 112.2f, + 113.0f, 113.1f, 113.2f}); + // clang-format on + test_case.add_input({0, 2}); + // clang-format off + test_case.add_expected_output( + out_shape, { 1.0f, 1.1f, 1.2f, + 3.0f, 3.1f, 3.2f, + + 11.0f, 11.1f, 11.2f, + 13.0f, 13.1f, 13.2f, + + + 101.0f, 101.1f, 101.2f, + 103.0f, 103.1f, 103.2f, + + 111.0f, 111.1f, 111.2f, + 113.0f, 113.1f, 113.2f}); + // clang-format on + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_scalar_indices_axis_1_2d_input) +{ + Shape data_shape{3, 3}; + Shape indices_shape{}; + Shape out_shape{3}; + auto P = make_shared(element::f32, data_shape); + auto I = make_shared(element::i32, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {1}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + test_case.add_input({1.0f, 1.1f, 1.2f, 2.0f, 2.1f, 2.2f, 3.0f, 3.1f, 3.2f}); + test_case.add_input({0}); + test_case.add_expected_output(out_shape, {1.0f, 2.0f, 3.0f}); + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_axis_0_int8) +{ + Shape data_shape{3, 2}; + Shape indices_shape{2, 2}; + Shape out_shape{2, 2, 2}; + auto P = make_shared(element::i8, data_shape); + auto I = make_shared(element::i32, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {0}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + test_case.add_input({10, 11, 20, 21, 30, 31}); + test_case.add_input({0, 1, 1, 2}); + test_case.add_expected_output(out_shape, {10, 11, 20, 21, 20, 21, 30, 31}); + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_axis_0_int16) +{ + Shape data_shape{3, 2}; + Shape indices_shape{2, 2}; + Shape out_shape{2, 2, 2}; + auto P = make_shared(element::i16, data_shape); + auto I = make_shared(element::i64, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {0}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + test_case.add_input({10, 11, 20, 21, 30, 31}); + test_case.add_input({0, 1, 1, 2}); + test_case.add_expected_output(out_shape, {10, 11, 20, 21, 20, 21, 30, 31}); + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_axis_0_int32) +{ + Shape data_shape{3, 2}; + Shape indices_shape{2, 2}; + Shape out_shape{2, 2, 2}; + auto P = make_shared(element::i32, data_shape); + auto I = make_shared(element::i32, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {0}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + // clang-format off + auto test_case = test::TestCase(f); + test_case.add_input({10, 11, + 20, 21, + 30, 31}); + test_case.add_input({0, 1, + 1, 2}); + test_case.add_expected_output(out_shape, {10, 11, + 20, 21, + + 20, 21, + 30, 31}); + // clang-format on + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_axis_0_int64) +{ + Shape data_shape{3, 2}; + Shape indices_shape{2, 2}; + Shape out_shape{2, 2, 2}; + auto P = make_shared(element::i64, data_shape); + auto I = make_shared(element::i64, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {0}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + test_case.add_input({10, 11, 20, 21, 30, 31}); + test_case.add_input({0, 1, 1, 2}); + test_case.add_expected_output(out_shape, {10, 11, 20, 21, 20, 21, 30, 31}); + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_axis_0_uint8) +{ + Shape data_shape{3, 2}; + Shape indices_shape{2, 2}; + Shape out_shape{2, 2, 2}; + auto P = make_shared(element::u8, data_shape); + auto I = make_shared(element::i32, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {0}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + test_case.add_input({10, 11, 20, 21, 30, 31}); + test_case.add_input({0, 1, 1, 2}); + test_case.add_expected_output(out_shape, {10, 11, 20, 21, 20, 21, 30, 31}); + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_axis_0_uint16) +{ + Shape data_shape{3, 2}; + Shape indices_shape{2, 2}; + Shape out_shape{2, 2, 2}; + auto P = make_shared(element::u16, data_shape); + auto I = make_shared(element::i64, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {0}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + test_case.add_input({10, 11, 20, 21, 30, 31}); + test_case.add_input({0, 1, 1, 2}); + test_case.add_expected_output(out_shape, {10, 11, 20, 21, 20, 21, 30, 31}); + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_axis_0_uint32) +{ + Shape data_shape{3, 2}; + Shape indices_shape{2, 2}; + Shape out_shape{2, 2, 2}; + auto P = make_shared(element::u32, data_shape); + auto I = make_shared(element::i32, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {0}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + test_case.add_input({10, 11, 20, 21, 30, 31}); + test_case.add_input({0, 1, 1, 2}); + test_case.add_expected_output(out_shape, {10, 11, 20, 21, 20, 21, 30, 31}); + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_axis_0_uint64) +{ + Shape data_shape{3, 2}; + Shape indices_shape{2, 2}; + Shape out_shape{2, 2, 2}; + auto P = make_shared(element::u64, data_shape); + auto I = make_shared(element::i64, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {0}); + auto G = make_shared(P, I, A); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + test_case.add_input({10, 11, 20, 21, 30, 31}); + test_case.add_input({0, 1, 1, 2}); + test_case.add_expected_output(out_shape, {10, 11, 20, 21, 20, 21, 30, 31}); + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_axis_0_bool) { Shape data_shape{3, 2}; Shape indices_shape{2, 2}; @@ -556,7 +1117,7 @@ NGRAPH_TEST(${BACKEND_NAME}, gather_axis_0_bool) auto P = make_shared(element::boolean, data_shape); auto I = make_shared(element::i64, indices_shape); auto A = op::Constant::create(element::i64, Shape{}, {0}); - auto G = make_shared(P, I, A); + auto G = make_shared(P, I, A); auto f = make_shared(G, ParameterVector{P, I}); auto test_case = test::TestCase(f); @@ -565,3 +1126,165 @@ NGRAPH_TEST(${BACKEND_NAME}, gather_axis_0_bool) test_case.add_expected_output(out_shape, {1, 1, 1, 0, 1, 0, 0, 1}); test_case.run(MIN_FLOAT_TOLERANCE_BITS); } + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_data_int32_3d_indices_axis_1_batch_dims_1) +{ + Shape data_shape{2, 3}; + Shape indices_shape{2, 2, 2}; + Shape out_shape{2, 2, 2}; + int64_t batch_dims = 1; + int64_t axis = 1; + + auto P = make_shared(element::i32, data_shape); + auto I = make_shared(element::i64, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {axis}); + auto G = make_shared(P, I, A, batch_dims); + auto f = make_shared(G, ParameterVector{P, I}); + + // clang-format off + auto test_case = test::TestCase(f); + test_case.add_input({1, 2, 3, // batch 0 + 4, 5, 6}); // batch 1 + + test_case.add_input({0, 1, // batch 0 + 1, 2, + + 2, 0, // batch 1 + 1, 2}); + test_case.add_expected_output(out_shape, {1, 2, // batch 1 + 2, 3, + + 6, 4, // batch 1 + 5, 6}); + test_case.run(); + // clang-format on +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_data_int32_2d_indices_axis_1_batch_dims_1) +{ + Shape data_shape{2, 5}; + Shape indices_shape{2, 3}; + Shape out_shape{2, 3}; + int64_t batch_dims = 1; + int64_t axis = 1; + + auto P = make_shared(element::i32, data_shape); + auto I = make_shared(element::i64, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {axis}); + auto G = make_shared(P, I, A, batch_dims); + auto f = make_shared(G, ParameterVector{P, I}); + + // clang-format off + auto test_case = test::TestCase(f); + test_case.add_input({1, 2, 3, 4, 5, // batch 0 + 6, 7, 8, 9, 10}); // batch 1 + + test_case.add_input({0, 0, 4, // batch 0 + 4, 0, 0}); // batch 1 + test_case.add_expected_output(out_shape, {1, 1, 5, // batch 0 + 10, 6, 6}); // batch 1 + test_case.run(); + // clang-format on +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_4d_data_axis_2_batch_dims_1_int32) +{ + Shape data_shape{2, 1, 5, 4}; + Shape indices_shape{2, 3}; + Shape out_shape{2, 1, 3, 4}; + int64_t batch_dims = 1; + int64_t axis = 2; + + auto P = make_shared(element::i32, data_shape); + auto I = make_shared(element::i64, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {axis}); + auto G = make_shared(P, I, A, batch_dims); + auto f = make_shared(G, ParameterVector{P, I}); + + // clang-format off + auto test_case = test::TestCase(f); + test_case.add_input({ + 1, 2, 3, 4, // first batch + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16, + 17, 18, 19, 20, + + 21, 22, 23, 24, // second batch + 25, 26, 27, 28, + 29, 30, 31, 32, + 33, 34, 35, 36, + 37, 38, 39, 40 + }); + + test_case.add_input({ + 1, 2, 4, // first batch + 4, 3, 2 // second batch + }); + test_case.add_expected_output(out_shape, { + 5, 6, 7, 8, + 9, 10, 11, 12, + 17, 18, 19, 20, + + 37, 38, 39, 40, + 33, 34, 35, 36, + 29, 30, 31, 32 + }); + test_case.run(); + // clang-format on +} + +NGRAPH_TEST(${BACKEND_NAME}, gather_v7_3d_indices_axis_1_batch_dims_1) +{ + Shape data_shape{2, 5, 2}; + Shape indices_shape{2, 2, 3}; + Shape out_shape{2, 2, 3, 2}; + int64_t batch_dims = 1; + int64_t axis = 1; + + auto P = make_shared(element::f32, data_shape); + auto I = make_shared(element::i32, indices_shape); + auto A = op::Constant::create(element::i64, Shape{}, {axis}); + + auto G = make_shared(P, I, A, batch_dims); + auto f = make_shared(G, ParameterVector{P, I}); + + auto test_case = test::TestCase(f); + + // clang-format off + test_case.add_input({1.0f, 2.0f, + 3.0f, 4.0f, + 5.0f, 6.0f, + 7.0f, 8.0f, + 9.0f, 10.0f, + + 11.0f, 12.0f, + 13.0f, 14.0f, + 15.0f, 16.0f, + 17.0f, 18.0f, + 19.0f, 20.0f}); + + test_case.add_input({0, 0, 4, + 4, 0, 0, + + 1, 2, 4, + 4, 3, 2}); + test_case.add_expected_output({1.0f, 2.0f, + 1.0f, 2.0f, + 9.0f, 10.0f, + + 9.0f, 10.0f, + 1.0f, 2.0f, + 1.0f, 2.0f, + + + 13.0f, 14.0f, + 15.0f, 16.0f, + 19.0f, 20.0f, + + 19.0f, 20.0f, + 17.0f, 18.0f, + 15.0f, 16.0f}); + // clang-format on + test_case.run(MIN_FLOAT_TOLERANCE_BITS); +} diff --git a/ngraph/test/constant_folding.cpp b/ngraph/test/constant_folding.cpp index b416938ef9e..c34dcb12c6e 100644 --- a/ngraph/test/constant_folding.cpp +++ b/ngraph/test/constant_folding.cpp @@ -1917,6 +1917,276 @@ TEST(constant_folding, const_gather_v1_subgraph_skip_if_not_single_input) ASSERT_EQ(count_ops_of_type(f), 1); } +TEST(constant_folding, const_gather_v7) +{ + auto constant_data = op::Constant::create( + element::f32, + Shape{2, 5}, + vector{1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f}); + auto constant_indices = + op::Constant::create(element::i64, Shape{4}, vector{0, 3, 2, 2}); + auto constant_axis = op::Constant::create(element::i64, Shape{1}, vector{1}); + auto gather = make_shared(constant_data, constant_indices, constant_axis); + gather->set_friendly_name("test"); + auto f = make_shared(gather, ParameterVector{}); + + pass::Manager pass_manager; + pass_manager.register_pass(); + pass_manager.run_passes(f); + + ASSERT_EQ(count_ops_of_type(f), 0); + ASSERT_EQ(count_ops_of_type(f), 1); + + auto new_const = + as_type_ptr(f->get_results().at(0)->input_value(0).get_node_shared_ptr()); + ASSERT_TRUE(new_const); + ASSERT_EQ(new_const->get_friendly_name(), "test"); + auto values_out = new_const->get_vector(); + + vector values_expected{1.0f, 4.0f, 3.0f, 3.0f, 6.0f, 9.0f, 8.0f, 8.0f}; + + ASSERT_TRUE(test::all_close_f(values_out, values_expected, MIN_FLOAT_TOLERANCE_BITS)); +} + +TEST(constant_folding, const_gather_v7_scalar) +{ + auto constant_data = op::Constant::create( + element::f32, + Shape{2, 5}, + vector{1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f}); + auto constant_indices = + op::Constant::create(element::i64, Shape{4}, vector{0, 3, 2, 2}); + auto constant_axis = op::Constant::create(element::i64, Shape{}, vector{1}); + auto gather = make_shared(constant_data, constant_indices, constant_axis); + gather->set_friendly_name("test"); + auto f = make_shared(gather, ParameterVector{}); + + pass::Manager pass_manager; + pass_manager.register_pass(); + pass_manager.run_passes(f); + + ASSERT_EQ(count_ops_of_type(f), 0); + ASSERT_EQ(count_ops_of_type(f), 1); + + auto new_const = + as_type_ptr(f->get_results().at(0)->input_value(0).get_node_shared_ptr()); + ASSERT_TRUE(new_const); + ASSERT_EQ(new_const->get_friendly_name(), "test"); + auto values_out = new_const->get_vector(); + + vector values_expected{1.0f, 4.0f, 3.0f, 3.0f, 6.0f, 9.0f, 8.0f, 8.0f}; + + ASSERT_TRUE(test::all_close_f(values_out, values_expected, MIN_FLOAT_TOLERANCE_BITS)); +} + +TEST(constant_folding, const_gather_v7_subgraph) +{ + const auto A = make_shared(element::f32, Shape{1}); + const float b_value = 3.21f; + const auto B_const = op::Constant::create(element::f32, {1}, {b_value}); + const auto C = make_shared(element::f32, Shape{1}); + const int64_t axis = 0; + const auto axis_const = op::Constant::create(element::i64, {}, {axis}); + + const auto concat = make_shared(NodeVector{A, B_const, C}, axis); + + const vector indices{1}; + const auto indices_const = op::Constant::create(element::i64, {indices.size()}, indices); + const auto gather = make_shared(concat, indices_const, axis_const); + gather->set_friendly_name("test"); + auto f = make_shared(gather, ParameterVector{A, C}); + + pass::Manager pass_manager; + pass_manager.register_pass(); + pass_manager.run_passes(f); + + ASSERT_EQ(count_ops_of_type(f), 0); + ASSERT_EQ(count_ops_of_type(f), 0); + ASSERT_EQ(count_ops_of_type(f), 1); + + const auto new_const = + as_type_ptr(f->get_results().at(0)->input_value(0).get_node_shared_ptr()); + ASSERT_TRUE(new_const); + ASSERT_EQ(new_const->get_friendly_name(), "test"); + + const auto values_out = new_const->get_vector(); + ASSERT_TRUE(test::all_close_f(values_out, {b_value}, MIN_FLOAT_TOLERANCE_BITS)); +} + +TEST(constant_folding, const_gather_v7_subgraph_neg_axis) +{ + const auto A = make_shared(element::f32, Shape{1}); + const float b_value = 1.23f; + const auto B = make_shared(element::f32, Shape{1}); + const auto C_const = op::Constant::create(element::f32, {1}, {b_value}); + const int64_t axis = 0; + const auto axis_const = op::Constant::create(element::i64, {}, {axis}); + + const auto concat = make_shared(NodeVector{A, B, C_const}, axis); + + const vector indices{-1}; + const auto indices_const = op::Constant::create(element::i64, {indices.size()}, indices); + const auto gather = make_shared(concat, indices_const, axis_const); + gather->set_friendly_name("test"); + auto f = make_shared(gather, ParameterVector{A, B}); + + pass::Manager pass_manager; + pass_manager.register_pass(); + pass_manager.run_passes(f); + + ASSERT_EQ(count_ops_of_type(f), 0); + ASSERT_EQ(count_ops_of_type(f), 0); + ASSERT_EQ(count_ops_of_type(f), 1); + + const auto new_const = + as_type_ptr(f->get_results().at(0)->input_value(0).get_node_shared_ptr()); + ASSERT_TRUE(new_const); + ASSERT_EQ(new_const->get_friendly_name(), "test"); + + const auto values_out = new_const->get_vector(); + ASSERT_TRUE(test::all_close_f(values_out, {b_value}, MIN_FLOAT_TOLERANCE_BITS)); +} + +TEST(constant_folding, const_gather_v7_subgraph_no_constant_input) +{ + const auto A = make_shared(element::f32, Shape{1}); + const auto B = make_shared(element::f32, Shape{1}); + const auto C = make_shared(element::f32, Shape{1}); + const int64_t axis = 0; + const auto axis_const = op::Constant::create(element::i64, {}, {axis}); + + const auto concat = make_shared(NodeVector{A, B, C}, axis); + + const vector indices{1}; + const auto indices_const = op::Constant::create(element::i64, {indices.size()}, indices); + const auto gather = make_shared(concat, indices_const, axis_const); + gather->set_friendly_name("test"); + auto f = make_shared(gather, ParameterVector{A, B, C}); + + pass::Manager pass_manager; + pass_manager.register_pass(); + pass_manager.run_passes(f); + + ASSERT_EQ(count_ops_of_type(f), 0); + ASSERT_EQ(count_ops_of_type(f), 0); +} + +TEST(constant_folding, const_gather_v7_subgraph_no_constant_input_scalar) +{ + const auto A = make_shared(element::f32, Shape{1}); + const auto B = make_shared(element::f32, Shape{1}); + const auto C = make_shared(element::f32, Shape{1}); + const int64_t axis = 0; + const auto axis_const = op::Constant::create(element::i64, {}, {axis}); + + const auto concat = make_shared(NodeVector{A, B, C}, axis); + + const vector indices{1}; + const auto indices_const = op::Constant::create(element::i64, {}, indices); + const auto gather = make_shared(concat, indices_const, axis_const); + auto f = make_shared(gather, ParameterVector{A, B, C}); + + pass::Manager pass_manager; + pass_manager.register_pass(); + pass_manager.run_passes(f); + + ASSERT_EQ(count_ops_of_type(f), 0); + ASSERT_EQ(count_ops_of_type(f), 0); + ASSERT_EQ(count_ops_of_type(f), 1); +} + +TEST(constant_folding, const_gather_v7_subgraph_skip_if_non_zero_axis) +{ + const auto A = make_shared(element::f32, Shape{2, 2}); + const auto B = make_shared(element::f32, Shape{2, 2}); + const auto C = make_shared(element::f32, Shape{2, 2}); + const int64_t axis = 1; + const auto axis_const = op::Constant::create(element::i64, {}, {axis}); + + const auto concat = make_shared(NodeVector{A, B, C}, axis); + + const vector indices{1}; + const auto indices_const = op::Constant::create(element::i64, {indices.size()}, indices); + const auto gather = make_shared(concat, indices_const, axis_const); + auto f = make_shared(gather, ParameterVector{A, B, C}); + + pass::Manager pass_manager; + pass_manager.register_pass(); + pass_manager.run_passes(f); + + ASSERT_EQ(count_ops_of_type(f), 1); + ASSERT_EQ(count_ops_of_type(f), 1); +} + +TEST(constant_folding, const_gather_v7_subgraph_skip_if_non_single_indices) +{ + const auto A = make_shared(element::f32, Shape{1}); + const auto B = make_shared(element::f32, Shape{1}); + const auto C = make_shared(element::f32, Shape{1}); + const int64_t axis = 0; + const auto axis_const = op::Constant::create(element::i64, {}, {axis}); + + const auto concat = make_shared(NodeVector{A, B, C}, axis); + + const vector indices{0, 1}; + const auto indices_const = op::Constant::create(element::i64, {indices.size()}, indices); + const auto gather = make_shared(concat, indices_const, axis_const); + auto f = make_shared(gather, ParameterVector{A, B, C}); + + pass::Manager pass_manager; + pass_manager.register_pass(); + pass_manager.run_passes(f); + + ASSERT_EQ(count_ops_of_type(f), 1); + ASSERT_EQ(count_ops_of_type(f), 1); +} + +TEST(constant_folding, const_gather_v7_subgraph_skip_if_concat_output_shape_dynamic) +{ + const auto A = make_shared(element::f32, PartialShape::dynamic()); + const auto B = make_shared(element::f32, Shape{1}); + const auto C = make_shared(element::f32, Shape{1}); + const int64_t axis = 0; + const auto axis_const = op::Constant::create(element::i64, {}, {axis}); + + const auto concat = make_shared(NodeVector{A, B, C}, axis); + + const vector indices{1}; + const auto indices_const = op::Constant::create(element::i64, {indices.size()}, indices); + const auto gather = make_shared(concat, indices_const, axis_const); + auto f = make_shared(gather, ParameterVector{A, B, C}); + + pass::Manager pass_manager; + pass_manager.register_pass(); + pass_manager.run_passes(f); + + ASSERT_EQ(count_ops_of_type(f), 1); + ASSERT_EQ(count_ops_of_type(f), 1); +} + +TEST(constant_folding, const_gather_v7_subgraph_skip_if_not_single_input) +{ + const auto A = make_shared(element::f32, Shape{2}); + const auto B = make_shared(element::f32, Shape{1}); + const auto C = make_shared(element::f32, Shape{1}); + const int64_t axis = 0; + const auto axis_const = op::Constant::create(element::i64, {}, {axis}); + + const auto concat = make_shared(NodeVector{A, B, C}, axis); + + const vector indices{1}; + const auto indices_const = op::Constant::create(element::i64, {indices.size()}, indices); + const auto gather = make_shared(concat, indices_const, axis_const); + auto f = make_shared(gather, ParameterVector{A, B, C}); + + pass::Manager pass_manager; + pass_manager.register_pass(); + pass_manager.run_passes(f); + + ASSERT_EQ(count_ops_of_type(f), 1); + ASSERT_EQ(count_ops_of_type(f), 1); +} + TEST(constant_folding, const_strided_slice) { Shape shape_in{16}; diff --git a/ngraph/test/eval.cpp b/ngraph/test/eval.cpp index 0a06de330b8..e37a2cad95b 100644 --- a/ngraph/test/eval.cpp +++ b/ngraph/test/eval.cpp @@ -1197,7 +1197,7 @@ TEST(eval, evaluate_logical_not) ASSERT_EQ(result_val, expec); } -TEST(eval, evaluate_dynamic_gather) +TEST(eval, evaluate_dynamic_gather_v1) { auto arg1 = make_shared(element::f32, PartialShape::dynamic()); auto arg2 = make_shared(element::i32, PartialShape::dynamic()); @@ -1216,7 +1216,7 @@ TEST(eval, evaluate_dynamic_gather) ASSERT_EQ(cval, out); } -TEST(eval, evaluate_dynamic_axis_gather) +TEST(eval, evaluate_dynamic_gather_v1_scalar_axis) { auto arg1 = make_shared(element::f32, PartialShape::dynamic()); auto arg2 = make_shared(element::i32, PartialShape::dynamic()); @@ -1236,6 +1236,49 @@ TEST(eval, evaluate_dynamic_axis_gather) ASSERT_EQ(cval, out); } +TEST(eval, evaluate_dynamic_gather_v7) +{ + auto arg1 = make_shared(element::f32, PartialShape::dynamic()); + auto arg2 = make_shared(element::i32, PartialShape::dynamic()); + auto arg3 = make_shared(element::i32, PartialShape::dynamic()); + int64_t batch_dims = 1; + int32_t axis = 1; + auto gather = make_shared(arg1, arg2, arg3, batch_dims); + auto fun = make_shared(OutputVector{gather}, ParameterVector{arg1, arg2, arg3}); + auto result_tensor = make_shared(); + ASSERT_TRUE(fun->evaluate({result_tensor}, + {make_host_tensor({2, 3}, {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}), + make_host_tensor({2, 2}, {1, 0, 1, 0}), + make_host_tensor({1}, {axis})})); + EXPECT_EQ(result_tensor->get_element_type(), element::f32); + EXPECT_EQ(result_tensor->get_partial_shape(), (PartialShape{2, 2})); + auto cval = read_vector(result_tensor); + vector out{2.0f, 1.0f, 5.0f, 4.0f}; + ASSERT_EQ(cval, out); +} + +TEST(eval, evaluate_dynamic_gather_v7_axis_scalar) +{ + auto arg1 = make_shared(element::f32, PartialShape::dynamic()); + auto arg2 = make_shared(element::i32, PartialShape::dynamic()); + auto arg3 = make_shared(element::i64, PartialShape::dynamic()); + int64_t batch_dims = 0; + int64_t axis = 1; + auto gather = make_shared(arg1, arg2, arg3, batch_dims); + auto fun = make_shared(OutputVector{gather}, ParameterVector{arg1, arg2, arg3}); + auto result_tensor = make_shared(); + ASSERT_TRUE(fun->evaluate({result_tensor}, + {make_host_tensor( + {3, 3}, {1.0f, 1.1f, 1.2f, 2.0f, 2.1f, 2.2f, 3.0f, 3.1f, 3.2f}), + make_host_tensor({1, 2}, {0, 2}), + make_host_tensor({}, {axis})})); + EXPECT_EQ(result_tensor->get_element_type(), element::f32); + EXPECT_EQ(result_tensor->get_partial_shape(), (PartialShape{3, 1, 2})); + auto cval = read_vector(result_tensor); + vector out{1.0f, 1.2f, 2.0f, 2.2f, 3.0f, 3.2f}; + ASSERT_EQ(cval, out); +} + TEST(eval, evaluate_dynamic_concat) { auto arg1 = make_shared(element::f32, PartialShape::dynamic()); diff --git a/ngraph/test/runtime/ie/unit_test.manifest b/ngraph/test/runtime/ie/unit_test.manifest index 2458b9af26a..3b13dc14161 100644 --- a/ngraph/test/runtime/ie/unit_test.manifest +++ b/ngraph/test/runtime/ie/unit_test.manifest @@ -883,8 +883,9 @@ dyn_group_convolution_backprop_data dynamic_transpose transpose -# Failing from new reason after unblocking more Blob types +# todo: check negative indices implementation gather_2d_negative_and_positive_indices_axis_0_2d_input +# Failing from new reason after unblocking more Blob types gather_axis_0_int8 gather_axis_0_uint8 gather_axis_0_uint32 @@ -1582,6 +1583,33 @@ evaluate_mvn_6_across_chanells evaluate_mvn_6_across_batch IE_CPU.onnx_mvn_v6 +# not yet implemented on CPU/GPU Gather 7 +gather_v7_1d_int32 +gather_v7_data_int32_3d_indices_axis_1_batch_dims_1 +gather_v7_data_int32_2d_indices_axis_1_batch_dims_1 +gather_v7_3d_indices_axis_1_batch_dims_1 +gather_v7_4d_indices_axis_0_uint8 +gather_v7_4d_indices_axis_0_2d_input +gather_v7_3d_indices_axis_0_2d_input +gather_v7_2d_indices_axis_0_2d_input +gather_v7_2d_negative_and_positive_indices_axis_0_2d_input +gather_v7_1d_indices_axis_0_1d_input +gather_v7_scalar_indices_axis_0_2d_input +gather_v7_2d_indices_axis_1_2d_input +gather_v7_1d_indices_axis_2_4d_input +gather_v7_scalar_indices_axis_1_2d_input +gather_v7_axis_0_int8 +gather_v7_axis_0_int16 +gather_v7_axis_0_int32 +gather_v7_axis_0_int64 +gather_v7_axis_0_uint8 +gather_v7_axis_0_uint16 +gather_v7_axis_0_uint32 +gather_v7_axis_0_uint64 +gather_v7_axis_0_bool +gather_v7_3d_indices_axis_1_batch_dims_1_int32 +gather_v7_4d_data_axis_2_batch_dims_1_int32 + # Issue 49621: Incorrect blob sizes for node BinaryConvolution_X bin_convolution_2D_1batch_1channel bin_convolution_2D_1batch_1channel_padding_pad_val_0 diff --git a/ngraph/test/runtime/interpreter/unit_test.manifest b/ngraph/test/runtime/interpreter/unit_test.manifest index 9856353f7b8..c04ba98df6b 100644 --- a/ngraph/test/runtime/interpreter/unit_test.manifest +++ b/ngraph/test/runtime/interpreter/unit_test.manifest @@ -65,6 +65,11 @@ INTERPRETER.gather_axis_0_int8 INTERPRETER.gather_axis_0_int16 INTERPRETER.gather_axis_0_uint8 INTERPRETER.gather_axis_0_uint16 +INTERPRETER.gather_v7_4d_indices_axis_0_uint8 +INTERPRETER.gather_v7_axis_0_int8 +INTERPRETER.gather_v7_axis_0_int16 +INTERPRETER.gather_v7_axis_0_uint8 +INTERPRETER.gather_v7_axis_0_uint16 INTERPRETER.auto_bcast_binary_elementwise INTERPRETER.auto_bcast_binary_elementwise_pdpd diff --git a/ngraph/test/type_prop/gather.cpp b/ngraph/test/type_prop/gather.cpp index 81954930af9..e74b809f416 100644 --- a/ngraph/test/type_prop/gather.cpp +++ b/ngraph/test/type_prop/gather.cpp @@ -395,7 +395,7 @@ TEST(type_prop, gather_7_batch_dims_less_check) TEST(type_prop, gather_7_batch_dims_less_indices_rank_check) { PartialShape data_shape{1, 20, 20, 22, 22}; - PartialShape indices_shape{1, 3, 8}; + PartialShape indices_shape{1, 3}; auto D = make_shared(element::f32, data_shape); auto I = make_shared(element::i64, indices_shape); @@ -413,7 +413,7 @@ TEST(type_prop, gather_7_batch_dims_less_indices_rank_check) { EXPECT_HAS_SUBSTRING( error.what(), - std::string("batch_dims must be < indices_rank")); + std::string("batch_dims must be <= indices_rank")); } catch (...) {