Slice-8 Reference implementation (#7749)

* Init template reference tests

* Move output shape calculation to separate function

* Add to evaluate_map (reuse old ref)

* Add negative step test

* Make slice evaluate template type independent

* Enable typed tests for reference

* Add more reference tests

* Revert old slice ref changes

* New Slice reference implementation POC

* N-dim generalization

* Code refactor

* Style alignment

* Add more checks

* Add more tests

* Add zero dims/empty output tests

* Simplify typed tests generation

* Slice op leftovers

* Add unsigned types reference tests
This commit is contained in:
Katarzyna Mitrus 2021-10-15 11:41:39 +02:00 committed by GitHub
parent a091201b67
commit 96df1a14ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 692 additions and 57 deletions

View File

@ -0,0 +1,447 @@
// Copyright (C) 2018-2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <gtest/gtest.h>
#include <limits>
#include "base_reference_test.hpp"
#include "openvino/opsets/opset8.hpp"
using namespace ov;
namespace reference_tests {
namespace {
struct SliceParams {
SliceParams(const Tensor& data,
const Tensor& start,
const Tensor& stop,
const Tensor& step,
const Tensor& axes,
const Tensor& output,
const std::string& test_name = "")
: m_data(data),
m_start(start),
m_stop(stop),
m_step(step),
m_axes(axes),
m_output(output),
m_test_name(test_name),
m_default_axes(false) {}
// Default `axes` input
SliceParams(const Tensor& data,
const Tensor& start,
const Tensor& stop,
const Tensor& step,
const Tensor& output,
const std::string& test_name = "")
: m_data(data),
m_start(start),
m_stop(stop),
m_step(step),
m_output(output),
m_test_name(test_name),
m_default_axes(true) {}
Tensor m_data;
Tensor m_start;
Tensor m_stop;
Tensor m_step;
Tensor m_axes;
Tensor m_output;
std::string m_test_name;
bool m_default_axes = false;
};
class ReferenceSliceLayerTest : public testing::TestWithParam<SliceParams>, public CommonReferenceTest {
public:
void SetUp() override {
auto params = GetParam();
if (params.m_default_axes) {
function = CreateFunction(params.m_data, params.m_start, params.m_stop, params.m_step);
inputData = {params.m_data.data, params.m_start.data, params.m_stop.data, params.m_step.data};
} else {
function = CreateFunction(params.m_data, params.m_start, params.m_stop, params.m_step, params.m_axes);
inputData = {params.m_data.data,
params.m_start.data,
params.m_stop.data,
params.m_step.data,
params.m_axes.data};
}
refOutData = {params.m_output.data};
}
static std::string getTestCaseName(const testing::TestParamInfo<SliceParams>& obj) {
auto param = obj.param;
std::ostringstream result;
result << "test_name=" << param.m_test_name << "__";
result << "data_shape=" << param.m_data.shape << "_";
result << "data_type=" << param.m_data.type << "_";
result << "ind_type=" << param.m_start.type << "_";
if (param.m_default_axes) {
result << "axes_shape="
<< "default"
<< "_";
result << "axes_type="
<< "default"
<< "_";
} else {
result << "axes_shape=" << param.m_axes.shape << "_";
result << "axes_type=" << param.m_axes.type << "_";
}
return result.str();
}
private:
static std::shared_ptr<Function> CreateFunction(const Tensor& data,
const Tensor& start,
const Tensor& stop,
const Tensor& step,
const Tensor& axes) {
const auto data_param = std::make_shared<opset8::Parameter>(data.type, data.shape);
const auto start_param = std::make_shared<opset8::Parameter>(start.type, start.shape);
const auto stop_param = std::make_shared<opset8::Parameter>(stop.type, stop.shape);
const auto step_param = std::make_shared<opset8::Parameter>(step.type, step.shape);
const auto axes_param = std::make_shared<opset8::Parameter>(axes.type, axes.shape);
const auto slice = std::make_shared<opset8::Slice>(data_param, start_param, stop_param, step_param, axes_param);
return std::make_shared<Function>(NodeVector{slice},
ParameterVector{data_param, start_param, stop_param, step_param, axes_param});
}
// Default `axes` input
static std::shared_ptr<Function> CreateFunction(const Tensor& data,
const Tensor& start,
const Tensor& stop,
const Tensor& step) {
const auto data_param = std::make_shared<opset8::Parameter>(data.type, data.shape);
const auto start_param = std::make_shared<opset8::Parameter>(start.type, start.shape);
const auto stop_param = std::make_shared<opset8::Parameter>(stop.type, stop.shape);
const auto step_param = std::make_shared<opset8::Parameter>(step.type, step.shape);
const auto slice = std::make_shared<opset8::Slice>(data_param, start_param, stop_param, step_param);
return std::make_shared<Function>(NodeVector{slice},
ParameterVector{data_param, start_param, stop_param, step_param});
}
};
TEST_P(ReferenceSliceLayerTest, CompareWithHardcodedRefs) {
Exec();
}
} // namespace
template <element::Type_t DATA_ET, element::Type_t IND_ET, element::Type_t AXIS_ET>
std::vector<SliceParams> generateSliceParamsUnsigned() {
using DATA_T = typename element_type_traits<DATA_ET>::value_type;
using IND_T = typename element_type_traits<IND_ET>::value_type;
using AXIS_T = typename element_type_traits<AXIS_ET>::value_type;
std::vector<SliceParams> test_params{
SliceParams(Tensor{{4}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4}},
Tensor{{1}, IND_ET, std::vector<IND_T>{0}},
Tensor{{1}, IND_ET, std::vector<IND_T>{5}},
Tensor{{1}, IND_ET, std::vector<IND_T>{1}},
Tensor{{1}, AXIS_ET, std::vector<AXIS_T>{0}},
Tensor{{4}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4}},
"1D_full_axes"),
SliceParams(Tensor{{4}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4}},
Tensor{{1}, IND_ET, std::vector<IND_T>{0}},
Tensor{{1}, IND_ET, std::vector<IND_T>{5}},
Tensor{{1}, IND_ET, std::vector<IND_T>{1}},
Tensor{{4}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4}},
"1D_default_axes"),
SliceParams(Tensor{{4}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4}},
Tensor{{1}, IND_ET, std::vector<IND_T>{6}},
Tensor{{1}, IND_ET, std::vector<IND_T>{5}},
Tensor{{1}, IND_ET, std::vector<IND_T>{1}},
Tensor{{1}, AXIS_ET, std::vector<AXIS_T>{0}},
Tensor{{0}, DATA_ET, std::vector<DATA_T>{}},
"1D_empty_output"),
SliceParams(Tensor{{2, 2}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4}},
Tensor{{2}, IND_ET, std::vector<IND_T>{0, 0}},
Tensor{{2}, IND_ET, std::vector<IND_T>{2, 2}},
Tensor{{2}, IND_ET, std::vector<IND_T>{1, 1}},
Tensor{{2}, AXIS_ET, std::vector<AXIS_T>{0, 1}},
Tensor{{2, 2}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4}},
"2D_full_axes"),
SliceParams(Tensor{{2, 2}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4}},
Tensor{{2}, IND_ET, std::vector<IND_T>{0, 0}},
Tensor{{2}, IND_ET, std::vector<IND_T>{2, 2}},
Tensor{{2}, IND_ET, std::vector<IND_T>{1, 1}},
Tensor{{2, 2}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4}},
"2D_default_axes"),
SliceParams(Tensor{{2, 4}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4, 5, 6, 7, 8}},
Tensor{{2}, IND_ET, std::vector<IND_T>{1, 0}},
Tensor{{2}, IND_ET, std::vector<IND_T>{2, 3}},
Tensor{{2}, IND_ET, std::vector<IND_T>{1, 2}},
Tensor{{2}, AXIS_ET, std::vector<AXIS_T>{0, 1}},
Tensor{{1, 2}, DATA_ET, std::vector<DATA_T>{5, 7}},
"2D_step_2"),
SliceParams(Tensor{{3, 16}, DATA_ET, std::vector<DATA_T>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}},
Tensor{{2}, IND_ET, std::vector<IND_T>{0, 0}},
Tensor{{2}, IND_ET, std::vector<IND_T>{3, 16}},
Tensor{{2}, IND_ET, std::vector<IND_T>{1, 15}},
Tensor{{2}, AXIS_ET, std::vector<AXIS_T>{0, 1}},
Tensor{{3, 2}, DATA_ET, std::vector<DATA_T>{0, 15, 16, 31, 32, 47}},
"2D_big_step"),
SliceParams(Tensor{{2, 2}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4}},
Tensor{{2}, IND_ET, std::vector<IND_T>{10, 0}},
Tensor{{2}, IND_ET, std::vector<IND_T>{20, 2}},
Tensor{{2}, IND_ET, std::vector<IND_T>{1, 1}},
Tensor{{2}, AXIS_ET, std::vector<AXIS_T>{0, 1}},
Tensor{{0, 2}, DATA_ET, std::vector<DATA_T>{}},
"2D_empty_output"),
SliceParams(Tensor{{2, 2, 2}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4, 5, 6, 7, 8}},
Tensor{{3}, IND_ET, std::vector<IND_T>{0, 0, 0}},
Tensor{{3}, IND_ET, std::vector<IND_T>{2, 2, 2}},
Tensor{{3}, IND_ET, std::vector<IND_T>{1, 1, 1}},
Tensor{{3}, AXIS_ET, std::vector<AXIS_T>{0, 1, 2}},
Tensor{{2, 2, 2}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4, 5, 6, 7, 8}},
"3D_full_axes"),
SliceParams(Tensor{{2, 2, 2}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4, 5, 6, 7, 8}},
Tensor{{3}, IND_ET, std::vector<IND_T>{0, 0, 0}},
Tensor{{3}, IND_ET, std::vector<IND_T>{2, 2, 2}},
Tensor{{3}, IND_ET, std::vector<IND_T>{1, 1, 1}},
Tensor{{2, 2, 2}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4, 5, 6, 7, 8}},
"3D_default_axes"),
SliceParams(Tensor{{2, 2, 2}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4, 5, 6, 7, 8}},
Tensor{{2}, IND_ET, std::vector<IND_T>{0, 0}},
Tensor{{2}, IND_ET, std::vector<IND_T>{2, 2}},
Tensor{{2}, IND_ET, std::vector<IND_T>{1, 1}},
Tensor{{2}, AXIS_ET, std::vector<AXIS_T>{0, 1}},
Tensor{{2, 2, 2}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4, 5, 6, 7, 8}},
"3D_less_axes"),
SliceParams(Tensor{{2, 2, 2}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4, 5, 6, 7, 8}},
Tensor{{3}, IND_ET, std::vector<IND_T>{0, 2, 0}},
Tensor{{3}, IND_ET, std::vector<IND_T>{2, 2, 2}},
Tensor{{3}, IND_ET, std::vector<IND_T>{1, 1, 1}},
Tensor{{3}, AXIS_ET, std::vector<AXIS_T>{0, 1, 2}},
Tensor{{2, 0, 2}, DATA_ET, std::vector<DATA_T>{}},
"3D_empty_output"),
SliceParams(Tensor{{4, 2, 3, 2}, DATA_ET, std::vector<DATA_T>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}},
Tensor{{4}, IND_ET, std::vector<IND_T>{0, 0, 0, 0}},
Tensor{{4}, IND_ET, std::vector<IND_T>{4, 2, 3, 2}},
Tensor{{4}, IND_ET, std::vector<IND_T>{1, 1, 1, 1}},
Tensor{{4}, AXIS_ET, std::vector<AXIS_T>{0, 1, 2, 3}},
Tensor{{4, 2, 3, 2}, DATA_ET, std::vector<DATA_T>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}},
"4D_full_axes"),
SliceParams(Tensor{{4, 2, 3, 2}, DATA_ET, std::vector<DATA_T>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}},
Tensor{{4}, IND_ET, std::vector<IND_T>{0, 0, 0, 0}},
Tensor{{4}, IND_ET, std::vector<IND_T>{4, 2, 3, 2}},
Tensor{{4}, IND_ET, std::vector<IND_T>{1, 1, 1, 1}},
Tensor{{4, 2, 3, 2}, DATA_ET, std::vector<DATA_T>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}},
"4D_default_axes"),
SliceParams(Tensor{{4, 2, 3, 2}, DATA_ET, std::vector<DATA_T>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}},
Tensor{{1}, IND_ET, std::vector<IND_T>{0}},
Tensor{{1}, IND_ET, std::vector<IND_T>{2}},
Tensor{{1}, IND_ET, std::vector<IND_T>{1}},
Tensor{{1}, AXIS_ET, std::vector<AXIS_T>{0}},
Tensor{{2, 2, 3, 2}, DATA_ET, std::vector<DATA_T>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}},
"4D_half_dim"),
SliceParams(Tensor{{4, 2, 3, 2}, DATA_ET, std::vector<DATA_T>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}},
Tensor{{1}, IND_ET, std::vector<IND_T>{0}},
Tensor{{1}, IND_ET, std::vector<IND_T>{4}},
Tensor{{1}, IND_ET, std::vector<IND_T>{2}},
Tensor{{1}, AXIS_ET, std::vector<AXIS_T>{0}},
Tensor{{2, 2, 3, 2}, DATA_ET, std::vector<DATA_T>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35}},
"4D_half_dim_step"),
SliceParams(
Tensor{{2, 4, 2, 2, 3},
DATA_ET,
std::vector<DATA_T>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}},
Tensor{{5}, IND_ET, std::vector<IND_T>{0, 0, 0, 0, 0}},
Tensor{{5}, IND_ET, std::vector<IND_T>{2, 4, 2, 2, 3}},
Tensor{{5}, IND_ET, std::vector<IND_T>{1, 1, 1, 1, 1}},
Tensor{{5}, AXIS_ET, std::vector<AXIS_T>{0, 1, 2, 3, 4}},
Tensor{{2, 2, 2, 1, 2},
DATA_ET,
std::vector<DATA_T>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}},
"5D_full_axes"),
};
return test_params;
}
template <element::Type_t DATA_ET, element::Type_t IND_ET, element::Type_t AXIS_ET>
std::vector<SliceParams> generateSliceParams() {
using DATA_T = typename element_type_traits<DATA_ET>::value_type;
using IND_T = typename element_type_traits<IND_ET>::value_type;
using AXIS_T = typename element_type_traits<AXIS_ET>::value_type;
std::vector<SliceParams> test_params{
SliceParams(Tensor{{4}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4}},
Tensor{{1}, IND_ET, std::vector<IND_T>{5}},
Tensor{{1}, IND_ET, std::vector<IND_T>{-6}},
Tensor{{1}, IND_ET, std::vector<IND_T>{-1}},
Tensor{{4}, DATA_ET, std::vector<DATA_T>{4, 3, 2, 1}},
"1D_negative_step"),
SliceParams(Tensor{{4}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4}},
Tensor{{1}, IND_ET, std::vector<IND_T>{0}},
Tensor{{1}, IND_ET, std::vector<IND_T>{5}},
Tensor{{1}, IND_ET, std::vector<IND_T>{-1}},
Tensor{{1}, AXIS_ET, std::vector<AXIS_T>{0}},
Tensor{{0}, DATA_ET, std::vector<DATA_T>{}},
"1D_empty_output_negative_step"),
SliceParams(Tensor{{2, 2}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4}},
Tensor{{2}, IND_ET, std::vector<IND_T>{0, 3}},
Tensor{{2}, IND_ET, std::vector<IND_T>{2, -4}},
Tensor{{2}, IND_ET, std::vector<IND_T>{1, -1}},
Tensor{{2}, AXIS_ET, std::vector<AXIS_T>{0, 1}},
Tensor{{2, 2}, DATA_ET, std::vector<DATA_T>{2, 1, 4, 3}},
"2D_negative_step_mix"),
SliceParams(Tensor{{2, 2}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4}},
Tensor{{2}, IND_ET, std::vector<IND_T>{-1, 3}},
Tensor{{2}, IND_ET, std::vector<IND_T>{-4, -4}},
Tensor{{2}, IND_ET, std::vector<IND_T>{-1, -1}},
Tensor{{2}, AXIS_ET, std::vector<AXIS_T>{0, 1}},
Tensor{{2, 2}, DATA_ET, std::vector<DATA_T>{4, 3, 2, 1}},
"2D_negative_step_only"),
SliceParams(Tensor{{3, 16}, DATA_ET, std::vector<DATA_T>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}},
Tensor{{2}, IND_ET, std::vector<IND_T>{2, 15}},
Tensor{{2}, IND_ET, std::vector<IND_T>{-4, -17}},
Tensor{{2}, IND_ET, std::vector<IND_T>{-1, -15}},
Tensor{{2}, AXIS_ET, std::vector<AXIS_T>{0, 1}},
Tensor{{3, 2}, DATA_ET, std::vector<DATA_T>{47, 32, 31, 16, 15, 0}},
"2D_negative_big_step"),
SliceParams(Tensor{{2, 2, 2}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4, 5, 6, 7, 8}},
Tensor{{3}, IND_ET, std::vector<IND_T>{0, 0, 0}},
Tensor{{3}, IND_ET, std::vector<IND_T>{2, 2, 2}},
Tensor{{3}, IND_ET, std::vector<IND_T>{1, 1, 1}},
Tensor{{3}, AXIS_ET, std::vector<AXIS_T>{-3, -2, -1}},
Tensor{{2, 2, 2}, DATA_ET, std::vector<DATA_T>{1, 2, 3, 4, 5, 6, 7, 8}},
"3D_negative_axes"),
SliceParams(Tensor{{2, 4, 3}, DATA_ET, std::vector<DATA_T>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}},
Tensor{{3}, IND_ET, std::vector<IND_T>{0, 0, 4}},
Tensor{{3}, IND_ET, std::vector<IND_T>{2, 4, -5}},
Tensor{{3}, IND_ET, std::vector<IND_T>{3, 2, -2}},
Tensor{{3}, AXIS_ET, std::vector<AXIS_T>{0, 1, 2}},
Tensor{{1, 2, 2}, DATA_ET, std::vector<DATA_T>{2, 0, 8, 6}},
"3D_mixed_step"),
SliceParams(Tensor{{4, 2, 3, 2}, DATA_ET, std::vector<DATA_T>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}},
Tensor{{4},
IND_ET,
std::vector<IND_T>{std::numeric_limits<IND_T>::min(),
std::numeric_limits<IND_T>::max(),
std::numeric_limits<IND_T>::max(),
std::numeric_limits<IND_T>::min()}},
Tensor{{4},
IND_ET,
std::vector<IND_T>{std::numeric_limits<IND_T>::max(),
std::numeric_limits<IND_T>::min(),
std::numeric_limits<IND_T>::min(),
std::numeric_limits<IND_T>::max()}},
Tensor{{4}, IND_ET, std::vector<IND_T>{1, -1, -1, 1}},
Tensor{{4}, AXIS_ET, std::vector<AXIS_T>{0, 1, 2, 3}},
Tensor{{4, 2, 3, 2}, DATA_ET, std::vector<DATA_T>{10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1,
22, 23, 20, 21, 18, 19, 16, 17, 14, 15, 12, 13,
34, 35, 32, 33, 30, 31, 28, 29, 26, 27, 24, 25,
46, 47, 44, 45, 42, 43, 40, 41, 38, 39, 36, 37}},
"4D_INT_MIN_MAX_index"),
SliceParams(Tensor{{4, 2, 3, 2}, DATA_ET, std::vector<DATA_T>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}},
Tensor{{2}, IND_ET, std::vector<IND_T>{100, -100}},
Tensor{{2}, IND_ET, std::vector<IND_T>{-100, 100}},
Tensor{{2}, IND_ET, std::vector<IND_T>{-1, 2}},
Tensor{{2}, AXIS_ET, std::vector<AXIS_T>{2, 0}},
Tensor{{2, 2, 3, 2}, DATA_ET, std::vector<DATA_T>{4, 5, 2, 3, 0, 1, 10, 11, 8, 9, 6, 7,
28, 29, 26, 27, 24, 25, 34, 35, 32, 33, 30, 31}},
"4D_mixed"),
SliceParams(
Tensor{{2, 4, 2, 2, 3},
DATA_ET,
std::vector<DATA_T>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}},
Tensor{{5}, IND_ET, std::vector<IND_T>{0, 1, -5, 100, 1}},
Tensor{{5}, IND_ET, std::vector<IND_T>{2, 6, 3, -100, 2}},
Tensor{{5}, IND_ET, std::vector<IND_T>{1, 2, 2, -1, 1}},
Tensor{{5}, AXIS_ET, std::vector<AXIS_T>{-5, 1, 4, 2, 3}},
Tensor{{2, 2, 2, 1, 2},
DATA_ET,
std::vector<DATA_T>{21, 23, 15, 17, 45, 47, 39, 41, 69, 71, 63, 65, 93, 95, 87, 89}},
"5D_mixed"),
};
const auto& unsigned_test_params = generateSliceParamsUnsigned<DATA_ET, IND_ET, AXIS_ET>();
test_params.insert(test_params.end(), unsigned_test_params.begin(), unsigned_test_params.end());
return test_params;
}
std::vector<SliceParams> generateSliceCombinedParams() {
const std::vector<std::vector<SliceParams>> opTypeParams{
// Mixed types
generateSliceParams<element::Type_t::f16, element::Type_t::i32, element::Type_t::i8>(),
generateSliceParams<element::Type_t::bf16, element::Type_t::i32, element::Type_t::i16>(),
generateSliceParams<element::Type_t::f32, element::Type_t::i64, element::Type_t::i32>(),
generateSliceParams<element::Type_t::i8, element::Type_t::i16, element::Type_t::i8>(),
generateSliceParams<element::Type_t::i16, element::Type_t::i8, element::Type_t::i16>(),
generateSliceParams<element::Type_t::i32, element::Type_t::i64, element::Type_t::i32>(),
generateSliceParams<element::Type_t::i64, element::Type_t::i32, element::Type_t::i64>(),
generateSliceParams<element::Type_t::u32, element::Type_t::i32, element::Type_t::i16>(),
// Unsigned types
generateSliceParamsUnsigned<element::Type_t::u8, element::Type_t::u8, element::Type_t::u8>(),
generateSliceParamsUnsigned<element::Type_t::u16, element::Type_t::u16, element::Type_t::u16>(),
generateSliceParamsUnsigned<element::Type_t::u32, element::Type_t::u32, element::Type_t::u32>(),
generateSliceParamsUnsigned<element::Type_t::u64, element::Type_t::u64, element::Type_t::u64>(),
generateSliceParamsUnsigned<element::Type_t::u16, element::Type_t::u8, element::Type_t::u32>(),
generateSliceParamsUnsigned<element::Type_t::u32, element::Type_t::u16, element::Type_t::u8>(),
};
std::vector<SliceParams> combinedParams;
std::for_each(opTypeParams.begin(), opTypeParams.end(), [&](std::vector<SliceParams> params) {
combinedParams.insert(combinedParams.end(), params.begin(), params.end());
});
return combinedParams;
}
INSTANTIATE_TEST_SUITE_P(smoke_Slice_With_Hardcoded_Refs,
ReferenceSliceLayerTest,
::testing::ValuesIn(generateSliceCombinedParams()),
ReferenceSliceLayerTest::getTestCaseName);
} // namespace reference_tests

View File

@ -1,4 +1,3 @@
// Copyright (C) 2018-2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
@ -32,6 +31,12 @@ public:
bool visit_attributes(AttributeVisitor& visitor) override;
std::shared_ptr<Node> clone_with_new_inputs(const OutputVector& new_args) const override;
PartialShape calculate_output_shape(const std::vector<int64_t>& starts,
const std::vector<int64_t>& stops,
const std::vector<int64_t>& steps,
const std::vector<int64_t>& axes,
const PartialShape& data_shape) const;
};
} // namespace v8
} // namespace op

View File

@ -10,6 +10,17 @@
namespace ngraph {
namespace runtime {
namespace reference {
// Slice-8 reference implementation
void slice(const char* data,
const Shape& data_shape,
char* out,
const Shape& out_shape,
size_t elem_size,
const std::vector<int64_t>& starts,
const std::vector<int64_t>& steps,
const std::vector<int64_t>& axes);
// Part of StridedSlice implementation
void slice(const char* arg,
char* out,
const Shape& arg_shape,
@ -18,6 +29,6 @@ void slice(const char* arg,
const Strides& strides,
const Shape& out_shape,
size_t elem_size);
}
} // namespace reference
} // namespace runtime
} // namespace ngraph

View File

@ -8,10 +8,54 @@
#include "ngraph/check.hpp"
#include "ngraph/coordinate_range.hpp"
#include "openvino/core/except.hpp"
namespace ngraph {
namespace runtime {
namespace reference {
void slice(const char* data,
const Shape& data_shape,
char* out,
const Shape& out_shape,
size_t elem_size,
const std::vector<int64_t>& starts,
const std::vector<int64_t>& steps,
const std::vector<int64_t>& axes) {
const auto ind_size = starts.size();
OPENVINO_ASSERT(steps.size() == ind_size && axes.size() == ind_size,
"Slice starts, steps, axes args need to have the same size.");
OPENVINO_ASSERT(data_shape.size() == out_shape.size(),
"Slice output data rank need to be equal to input data rank.");
// Align inputs rank with data shape and normalize
const auto data_rank = data_shape.size();
std::vector<int64_t> aligned_starts(data_rank, 0);
std::vector<int64_t> aligned_steps(data_rank, 1);
for (size_t i = 0; i < axes.size(); ++i) {
const auto axis = axes[i] >= 0 ? axes[i] : axes[i] + data_rank;
OPENVINO_ASSERT(axis >= 0 && axis < data_rank, "Slice `axes` arg has out of range value.");
const auto& dim = data_shape[axis];
aligned_starts[axis] = starts[i] >= 0 ? std::min<int64_t>(starts[i], steps[i] < 0 ? dim - 1 : dim)
: std::min<int64_t>(std::max<int64_t>(0, starts[i] + dim), dim - 1);
aligned_steps[axis] = steps[i];
}
// Slice elements
const auto in_data_strides = row_major_strides(data_shape);
const auto out_data_strides = row_major_strides(out_shape);
std::vector<int64_t> in_data_coord(aligned_starts);
for (size_t out_idx = 0; out_idx < shape_size(out_shape); ++out_idx) {
for (size_t i = 0; i < in_data_coord.size(); ++i) {
in_data_coord[i] = aligned_starts[i] + (out_idx / out_data_strides[i] % out_shape[i]) * aligned_steps[i];
}
const auto in_idx = std::inner_product(in_data_coord.begin(), in_data_coord.end(), in_data_strides.begin(), 0);
const auto in_mem = data + in_idx * elem_size;
std::memcpy(out, in_mem, elem_size);
out += elem_size;
}
}
void slice(const char* arg,
char* out,
const Shape& arg_shape,

View File

@ -133,13 +133,13 @@ void op::v8::Slice::validate_and_infer_types() {
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,
start_rank.is_dynamic() || start_shape[0].get_min_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,
stop_rank.is_dynamic() || stop_shape[0].get_min_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,
step_rank.is_dynamic() || step_shape[0].get_min_length() <= data_rank_length,
"Slice `step` input dim size can't be bigger than `data` rank.");
}
@ -186,7 +186,6 @@ void op::v8::Slice::validate_and_infer_types() {
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<int64_t>();
@ -194,55 +193,9 @@ void op::v8::Slice::validate_and_infer_types() {
const auto& steps = step_const->cast_vector<int64_t>();
const auto& axes = axes_const->cast_vector<int64_t>();
std::unordered_set<int64_t> 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);
}
output_shape = calculate_output_shape(starts, stops, steps, axes, data_shape);
} else {
const auto data_static_rank = data_shape.rank().get_length();
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.
@ -256,12 +209,12 @@ void op::v8::Slice::validate_and_infer_types() {
data_static_rank - 1,
"]. Got: ",
axis);
output_shape[axis] = Dimension(0, data_shape[axis].get_max_length());
output_shape[norm_axis] = Dimension(0, data_shape[norm_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) {
for (size_t i = 0; i < data_static_rank; ++i) {
output_shape[i] = Dimension(0, data_shape[i].get_max_length());
}
}
@ -269,7 +222,7 @@ void op::v8::Slice::validate_and_infer_types() {
set_output_type(0, get_input_element_type(0), output_shape);
}
shared_ptr<Node> op::v8::Slice::clone_with_new_inputs(const OutputVector& new_args) const {
std::shared_ptr<Node> 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) {
@ -282,3 +235,72 @@ shared_ptr<Node> op::v8::Slice::clone_with_new_inputs(const OutputVector& new_ar
new_args.at(4));
}
}
PartialShape op::v8::Slice::calculate_output_shape(const std::vector<int64_t>& starts,
const std::vector<int64_t>& stops,
const std::vector<int64_t>& steps,
const std::vector<int64_t>& axes,
const PartialShape& data_shape) const {
NGRAPH_OP_SCOPE(v8_Slice_calculate_output_shape);
const auto ind_size = starts.size();
NODE_VALIDATION_CHECK(this,
stops.size() == ind_size && steps.size() == ind_size && axes.size() == ind_size,
"Slice `start`, `stop`, `step`, `axes` inputs need to have the same size.");
std::unordered_set<int64_t> axes_set(axes.begin(), axes.end());
NODE_VALIDATION_CHECK(this, axes_set.size() == axes.size(), "Slice values in `axes` input must be unique.");
PartialShape output_shape(data_shape);
if (data_shape.rank().is_dynamic()) {
return output_shape;
}
const auto data_static_rank = data_shape.rank().get_length();
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;
continue;
}
// 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);
}
return output_shape;
}

View File

@ -67,6 +67,7 @@
#include <ngraph/runtime/reference/selu.hpp>
#include <ngraph/runtime/reference/sequences.hpp>
#include <ngraph/runtime/reference/sign.hpp>
#include <ngraph/runtime/reference/slice.hpp>
#include <ngraph/runtime/reference/squared_difference.hpp>
#include <ngraph/runtime/reference/tensor_iterator.hpp>
#include <ngraph/runtime/reference/utils/nms_common.hpp>
@ -1480,6 +1481,43 @@ bool evaluate(const shared_ptr<op::v1::AvgPool>& op, const HostTensorVector& out
return true;
}
template <element::Type_t ET>
bool evaluate(const std::shared_ptr<op::v8::Slice>& op,
const HostTensorVector& outputs,
const HostTensorVector& inputs) {
OPENVINO_ASSERT(inputs.size() >= 4, "Slice evaluate needs at least 4 inputs.");
std::vector<int64_t> starts = host_tensor_2_vector<int64_t>(inputs[1]);
std::vector<int64_t> stops = host_tensor_2_vector<int64_t>(inputs[2]);
std::vector<int64_t> steps = host_tensor_2_vector<int64_t>(inputs[3]);
std::vector<int64_t> axes(starts.size());
if (inputs.size() < 5) {
std::iota(axes.begin(), axes.end(), 0);
} else {
axes = host_tensor_2_vector<int64_t>(inputs[4]);
}
// Static HostTensor data shape is needed to clamp and normalize `start` values
const auto data_shape = inputs[0]->get_partial_shape();
OPENVINO_ASSERT(data_shape.is_static(), "Can't evaluate Slice elements without static HostTensor data shape.");
// We need calculate static output shape based on HostTensor inputs
PartialShape output_shape = op->calculate_output_shape(starts, stops, steps, axes, data_shape);
OPENVINO_ASSERT(output_shape.is_static(), "Can't calculate static output shape for Slice evaluation.");
outputs[0]->set_shape(output_shape.to_shape());
outputs[0]->set_element_type(inputs[0]->get_element_type());
runtime::reference::slice(inputs[0]->get_data_ptr<char>(),
data_shape.to_shape(),
outputs[0]->get_data_ptr<char>(),
output_shape.to_shape(),
inputs[0]->get_element_type().size(),
starts,
steps,
axes);
return true;
}
template <element::Type_t ET>
bool evaluate(const shared_ptr<op::v0::HardSigmoid>& op,
const HostTensorVector& outputs,

View File

@ -103,3 +103,4 @@ NGRAPH_OP(AdaptiveMaxPool, ngraph::op::v8)
NGRAPH_OP(Gather, op::v8)
NGRAPH_OP(MatrixNms, op::v8)
NGRAPH_OP(MulticlassNms, op::v8)
NGRAPH_OP(Slice, op::v8)

View File

@ -103,6 +103,23 @@ TEST(type_prop, slice_v8_basic_const_inputs_default_axes) {
EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape);
}
TEST(type_prop, slice_v8_const_inputs_output_zero_dims) {
PartialShape data_shape{5, 5, 5, 5, 9, 9};
PartialShape expected_out_shape{3, 0, 5, 0, 5, 0};
std::vector<int32_t> start_val{0, 0, 0, 5, 0, 10};
std::vector<int32_t> stop_val{5, 5, 5, 5, 9, 9};
std::vector<int32_t> step_val{2, -3, 1, 5, 2, 1};
element::Type_t et = element::i32;
std::vector<std::vector<int32_t>> 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};
@ -219,6 +236,56 @@ TEST(type_prop, slice_v8_basic_param_inputs_default_axes) {
EXPECT_EQ(op->get_output_partial_shape(0), expected_out_shape);
}
TEST(type_prop, slice_v8_sss_param_inputs_mixed_neg_const_axes) {
PartialShape data_shape{Dimension(5, 10), 2, 7, Dimension(0, 4)};
PartialShape expected_out_shape{Dimension(0, 10), Dimension(0, 2), 7, Dimension(0, 4)};
PartialShape start_shape{3};
PartialShape stop_shape{3};
PartialShape step_shape{3};
element::Type_t et = element::i32;
Shape axes_shape{3};
std::vector<int32_t> axes_val{0, 3, 1};
const auto data = std::make_shared<op::v0::Parameter>(et, data_shape);
const auto start = std::make_shared<op::v0::Parameter>(et, start_shape);
const auto stop = std::make_shared<op::v0::Parameter>(et, stop_shape);
const auto step = std::make_shared<op::v0::Parameter>(et, step_shape);
const auto axes = std::make_shared<op::v0::Constant>(et, axes_shape, axes_val);
const auto op = std::make_shared<op::v8::Slice>(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_sss_param_inputs_mixed_const_axes) {
PartialShape data_shape{Dimension(5, 10), 2, 7, Dimension(0, 4)};
PartialShape expected_out_shape{Dimension(0, 10), Dimension(0, 2), 7, Dimension(0, 4)};
PartialShape start_shape{3};
PartialShape stop_shape{3};
PartialShape step_shape{3};
element::Type_t et = element::i32;
Shape axes_shape{3};
std::vector<int32_t> axes_val{-4, -1, -3};
const auto data = std::make_shared<op::v0::Parameter>(et, data_shape);
const auto start = std::make_shared<op::v0::Parameter>(et, start_shape);
const auto stop = std::make_shared<op::v0::Parameter>(et, stop_shape);
const auto step = std::make_shared<op::v0::Parameter>(et, step_shape);
const auto axes = std::make_shared<op::v0::Constant>(et, axes_shape, axes_val);
const auto op = std::make_shared<op::v8::Slice>(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_const_axes) {
PartialShape data_shape{Dimension(0, 10),
Dimension(1, 10),