Review roll class for shape inference aspects (#15295)

* Review Roll label and interval shape propagation

* Review Roll shape_infer template implementation

* Fix compilation issues
This commit is contained in:
Pawel Raasz 2023-01-31 11:05:23 +01:00 committed by GitHub
parent 758a0dea56
commit 3a8646215f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 198 additions and 126 deletions

View File

@ -12,12 +12,11 @@ namespace ov {
namespace op {
namespace v7 {
template <class T>
void shape_infer(const ov::op::v7::Roll* op,
const std::vector<T>& input_shapes,
std::vector<T>& output_shapes,
const std::map<size_t, std::shared_ptr<ngraph::runtime::HostTensor>>& constant_data = {}) {
NODE_VALIDATION_CHECK(op, input_shapes.size() == 3 && output_shapes.size() == 1);
template <class TShape>
std::vector<TShape> shape_infer(const Roll* op,
const std::vector<TShape>& input_shapes,
const std::map<size_t, HostTensorPtr>& constant_data = {}) {
NODE_VALIDATION_CHECK(op, input_shapes.size() == 3);
const auto& data_pshape = input_shapes[0];
const auto& shift_pshape = input_shapes[1];
@ -35,37 +34,25 @@ void shape_infer(const ov::op::v7::Roll* op,
}
}
if (axes_pshape.rank().is_static()) {
const auto& axes_rank = axes_pshape.size();
NODE_VALIDATION_CHECK(op, axes_rank <= 1, "Axes must be a scalar or 1D tensor.");
}
NODE_VALIDATION_CHECK(op,
axes_pshape.rank().is_dynamic() || axes_pshape.size() <= 1,
"Axes must be a scalar or 1D tensor.");
std::vector<int64_t> axes{};
if (get_data_as_int64<T>(2, op, axes, constant_data)) {
if (data_pshape.rank().is_static()) {
const auto& data_rank = data_pshape.size();
for (int64_t& axis : axes) {
NODE_VALIDATION_CHECK(op,
axis < static_cast<int64_t>(data_rank),
"Axes must be less than data tensor rank. Got "
"data tensor rank: ",
data_rank,
", axis: ",
axis);
if (axis < 0) {
axis += static_cast<int64_t>(data_rank);
}
NODE_VALIDATION_CHECK(op,
axis >= 0,
"Axes must be positive or equal to zero. Got "
"axis: ",
axis);
}
if (data_pshape.rank().is_static()) {
if (const auto& axes = get_input_const_data_as<TShape, int64_t>(op, 2, constant_data)) {
ov::normalize_axes(op, data_pshape.size(), *axes);
}
}
output_shapes[0] = input_shapes[0];
return {data_pshape};
}
template <class TShape>
void shape_infer(const Roll* op,
const std::vector<TShape>& input_shapes,
std::vector<TShape>& output_shapes,
const std::map<size_t, HostTensorPtr>& constant_data = {}) {
output_shapes = shape_infer<TShape>(op, input_shapes, constant_data);
}
} // namespace v7

View File

@ -2,22 +2,21 @@
// SPDX-License-Identifier: Apache-2.0
//
#include "ngraph/op/roll.hpp"
#include <ngraph/validation_util.hpp>
#include <roll_shape_inference.hpp>
#include "openvino/op/roll.hpp"
#include "itt.hpp"
#include "openvino/core/validation_util.hpp"
#include "roll_shape_inference.hpp"
using namespace std;
using namespace ngraph;
namespace ov {
namespace op {
namespace v7 {
op::v7::Roll::Roll(const Output<Node>& data, const Output<Node>& shift, const Output<Node>& axes)
: Op({data, shift, axes}) {
Roll::Roll(const Output<Node>& data, const Output<Node>& shift, const Output<Node>& axes) : Op({data, shift, axes}) {
constructor_validate_and_infer_types();
}
void op::v7::Roll::validate_and_infer_types() {
void Roll::validate_and_infer_types() {
OV_OP_SCOPE(v7_Roll_validate_and_infer_types);
const auto& shift_et = get_input_element_type(1);
@ -30,22 +29,21 @@ void op::v7::Roll::validate_and_infer_types() {
axes_et.is_dynamic() || axes_et == element::i32 || axes_et == element::i64,
"Axes must have int32 or int64 element type.");
std::vector<ov::PartialShape> output_shapes = {ov::PartialShape{}};
const std::vector<ov::PartialShape> input_shapes = {get_input_partial_shape(0),
get_input_partial_shape(1),
get_input_partial_shape(2)};
shape_infer(this, input_shapes, output_shapes);
set_output_type(0, get_input_element_type(0), output_shapes[0]);
const auto output_shape = shape_infer(this, get_node_input_partial_shapes(*this)).front();
set_output_type(0, get_input_element_type(0), output_shape);
}
bool op::v7::Roll::visit_attributes(AttributeVisitor& visitor) {
bool Roll::visit_attributes(AttributeVisitor& visitor) {
OV_OP_SCOPE(v7_Roll_visit_attributes);
return true;
}
shared_ptr<Node> op::v7::Roll::clone_with_new_inputs(const OutputVector& new_args) const {
std::shared_ptr<Node> Roll::clone_with_new_inputs(const OutputVector& new_args) const {
OV_OP_SCOPE(v7_Roll_clone_with_new_inputs);
check_new_args_count(this, new_args);
return make_shared<v7::Roll>(new_args[0], new_args[1], new_args[2]);
return std::make_shared<Roll>(new_args[0], new_args[1], new_args[2]);
}
} // namespace v7
} // namespace op
} // namespace ov

View File

@ -2,23 +2,29 @@
// SPDX-License-Identifier: Apache-2.0
//
#include "gtest/gtest.h"
#include "ngraph/ngraph.hpp"
#include "ngraph/opsets/opset7.hpp"
#include "gmock/gmock.h"
#include "openvino/opsets/opset7.hpp"
#include "util/type_prop.hpp"
using namespace std;
using namespace ngraph;
using namespace ov;
using namespace ov::opset7;
using namespace testing;
class TypePropRollV7Test : public TypePropOpTest<op::v7::Roll> {};
TEST(type_prop, roll_output_shape_type_test) {
auto arg = make_shared<opset7::Parameter>(element::f32, Shape{3, 3, 4, 1, 5});
auto arg_shape = PartialShape{3, 3, 4, 1, 5};
set_shape_labels(arg_shape, 10);
auto arg = make_shared<opset7::Parameter>(element::f32, arg_shape);
auto shift = make_shared<opset7::Parameter>(element::i32, Shape{2});
auto axes = make_shared<opset7::Parameter>(element::i64, Shape{2});
auto r = make_shared<opset7::Roll>(arg, shift, axes);
EXPECT_EQ(r->get_output_element_type(0), element::f32);
EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape{3, 3, 4, 1, 5}));
EXPECT_EQ(r->get_output_partial_shape(0), PartialShape({3, 3, 4, 1, 5}));
EXPECT_THAT(get_shape_labels(r->get_output_partial_shape(0)), ElementsAre(10, 11, 12, 13, 14));
}
TEST(type_prop, roll_axis_const_test) {
@ -29,7 +35,7 @@ TEST(type_prop, roll_axis_const_test) {
auto r = make_shared<opset7::Roll>(arg, shift, axes);
EXPECT_EQ(r->get_output_element_type(0), element::f32);
EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape{3, 3, 3}));
EXPECT_EQ(r->get_output_partial_shape(0), PartialShape({3, 3, 3}));
}
TEST(type_prop, roll_incorrect_axis_test) {
@ -42,7 +48,7 @@ TEST(type_prop, roll_incorrect_axis_test) {
// Should have thrown, so fail if it didn't
FAIL() << "Unexpected pass with invalid axes and shift.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Axes must be less than data tensor rank."));
EXPECT_HAS_SUBSTRING(error.what(), std::string("Parameter axis 2 out of the tensor rank range"));
} catch (...) {
FAIL() << "Check failed for unexpected reason";
}
@ -58,7 +64,7 @@ TEST(type_prop, roll_incorrect_negative_axis_test) {
// Should have thrown, so fail if it didn't
FAIL() << "Unexpected pass with invalid axes and shift.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Axes must be positive or equal to zero."));
EXPECT_HAS_SUBSTRING(error.what(), std::string("Parameter axis -5 out of the tensor rank range"));
} catch (...) {
FAIL() << "Check failed for unexpected reason";
}
@ -72,7 +78,7 @@ TEST(type_prop, roll_axis_scalar_test) {
auto r = make_shared<opset7::Roll>(arg, shift, axes);
EXPECT_EQ(r->get_output_element_type(0), element::i32);
EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape{3, 3, 4}));
EXPECT_EQ(r->get_output_partial_shape(0), PartialShape({3, 3, 4}));
}
TEST(type_prop, roll_invalid_axes_check) {
@ -100,7 +106,7 @@ TEST(type_prop, roll_dynamic_shape) {
auto r = make_shared<opset7::Roll>(arg, shift, axes);
EXPECT_EQ(r->get_output_element_type(0), element::f32);
EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape::dynamic(2)));
EXPECT_EQ(r->get_output_partial_shape(0), PartialShape::dynamic(2));
}
TEST(type_prop, roll_dynamic_ranks) {
@ -111,7 +117,7 @@ TEST(type_prop, roll_dynamic_ranks) {
auto r = make_shared<opset7::Roll>(arg, shift, axes);
EXPECT_EQ(r->get_output_element_type(0), element::f32);
EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape::dynamic()));
EXPECT_EQ(r->get_output_partial_shape(0), PartialShape::dynamic());
}
TEST(type_prop, roll_dynamic_axes_static_shift) {
@ -122,10 +128,10 @@ TEST(type_prop, roll_dynamic_axes_static_shift) {
auto r = make_shared<opset7::Roll>(arg, shift, axes);
EXPECT_EQ(r->get_output_element_type(0), element::i32);
EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(Shape{3, 3, 4, 2}));
EXPECT_EQ(r->get_output_partial_shape(0), PartialShape({3, 3, 4, 2}));
}
TEST(type_prop, roll_scatic_axes_dynamic_shift) {
TEST(type_prop, roll_static_axes_dynamic_shift) {
auto arg = make_shared<opset7::Parameter>(element::i32, Shape{1, 2, 4});
auto shift = make_shared<opset7::Parameter>(element::i64, PartialShape{Dimension::dynamic()});
auto axes = make_shared<opset7::Parameter>(element::i32, Shape{3});
@ -133,16 +139,49 @@ TEST(type_prop, roll_scatic_axes_dynamic_shift) {
auto r = make_shared<opset7::Roll>(arg, shift, axes);
EXPECT_EQ(r->get_output_element_type(0), element::i32);
EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(Shape{1, 2, 4}));
EXPECT_EQ(r->get_output_shape(0), Shape({1, 2, 4}));
}
TEST(type_prop, roll_scatic_axes_dynamic_data) {
auto arg = make_shared<opset7::Parameter>(element::f32, PartialShape{Dimension::dynamic(), Dimension::dynamic()});
auto shift = opset7::Constant::create(element::i64, Shape{}, {5});
auto axes = make_shared<opset7::Parameter>(element::i32, PartialShape{Dimension::dynamic()});
TEST_F(TypePropRollV7Test, static_axes_dynamic_data) {
auto arg_shape = PartialShape{-1, -1};
set_shape_labels(arg_shape, 10);
const auto arg = make_shared<Parameter>(element::f32, arg_shape);
const auto shift = Constant::create(element::i64, Shape{}, {5});
const auto axes = make_shared<Parameter>(element::i32, PartialShape{Dimension::dynamic()});
auto r = make_shared<opset7::Roll>(arg, shift, axes);
const auto op = make_op(arg, shift, axes);
EXPECT_EQ(r->get_output_element_type(0), element::f32);
EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape::dynamic(2)));
EXPECT_EQ(op->get_output_element_type(0), element::f32);
EXPECT_EQ(op->get_output_partial_shape(0), PartialShape::dynamic(2));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), ElementsAre(10, 11));
}
TEST_F(TypePropRollV7Test, const_shift_axes_and_interval_dim_on_arg_shape) {
auto arg_shape = PartialShape{{2, 5}, {-1, 10}, {4, -1}, -1};
set_shape_labels(arg_shape, 10);
const auto arg = make_shared<Parameter>(element::f32, arg_shape);
const auto shift = Constant::create(element::i64, Shape{}, {5});
const auto axes = Constant::create(element::i64, Shape{2}, {0, 1});
const auto op = make_op(arg, shift, axes);
EXPECT_EQ(op->get_output_element_type(0), element::f32);
EXPECT_EQ(op->get_output_partial_shape(0), arg_shape);
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), ElementsAre(10, 11, 12, 13));
}
TEST_F(TypePropRollV7Test, default_ctor) {
const auto arg_shape = PartialShape{{3, 5}, -1, 10};
const auto arg = make_shared<Parameter>(element::f32, arg_shape);
const auto shift = Constant::create(element::i64, Shape{}, {5});
const auto axes = Constant::create(element::i64, Shape{2}, {0, 1});
const auto op = make_op();
op->set_arguments(OutputVector{arg, shift, axes});
op->validate_and_infer_types();
EXPECT_EQ(op->get_input_size(), 3);
EXPECT_EQ(op->get_output_size(), 1);
EXPECT_EQ(op->get_output_element_type(0), element::f32);
EXPECT_EQ(op->get_output_partial_shape(0), arg_shape);
}

View File

@ -1,52 +0,0 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <gtest/gtest.h>
#include <openvino/op/constant.hpp>
#include <openvino/op/parameter.hpp>
#include <openvino/op/roll.hpp>
#include <utils/shape_inference/shape_inference.hpp>
#include <utils/shape_inference/static_shape.hpp>
using namespace ov::intel_cpu;
TEST(StaticShapeInferenceTest, RollTest) {
auto arg =
std::make_shared<ov::op::v0::Parameter>(ov::element::f32,
ov::PartialShape{ov::Dimension::dynamic(), ov::Dimension::dynamic()});
auto shift = std::make_shared<ov::op::v0::Parameter>(ov::element::i64, ov::PartialShape{ov::Dimension::dynamic()});
auto axes = std::make_shared<ov::op::v0::Parameter>(ov::element::i32, ov::PartialShape{ov::Dimension::dynamic()});
auto roll = std::make_shared<ov::op::v7::Roll>(arg, shift, axes);
int32_t axes_val[] = {0, 1, -1};
auto axes_tensor = std::make_shared<ngraph::runtime::HostTensor>(ov::element::i32, ov::Shape{3}, axes_val);
std::map<size_t, std::shared_ptr<ngraph::runtime::HostTensor>> constant_data;
constant_data[2] = axes_tensor;
const std::vector<StaticShape> input_shapes = {StaticShape{3, 3, 3},
StaticShape{3},
StaticShape{3}};
std::vector<StaticShape> output_shapes = {StaticShape{}};
shape_inference(roll.get(), input_shapes, output_shapes, constant_data);
ASSERT_EQ(output_shapes[0], input_shapes[0]);
}
TEST(StaticShapeInferenceTest, RollTestWithConstAxis) {
auto arg =
std::make_shared<ov::op::v0::Parameter>(ov::element::f32,
ov::PartialShape{ov::Dimension::dynamic(), ov::Dimension::dynamic()});
auto shift = std::make_shared<ov::op::v0::Parameter>(ov::element::i64, ov::PartialShape{ov::Dimension::dynamic()});
auto axes = std::make_shared<ov::op::v0::Constant>(ov::element::i32, ov::Shape{3}, std::vector<int32_t>{0, 1, -1});
auto roll = std::make_shared<ov::op::v7::Roll>(arg, shift, axes);
const std::vector<StaticShape> input_shapes = {StaticShape{3, 3, 3},
StaticShape{3},
StaticShape{3}};
std::vector<StaticShape> output_shapes = {StaticShape{}};
shape_inference(roll.get(), input_shapes, output_shapes);
ASSERT_EQ(output_shapes[0], input_shapes[0]);
}

View File

@ -0,0 +1,100 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <array>
#include "common_test_utils/test_assertions.hpp"
#include "gmock/gmock.h"
#include "openvino/opsets/opset10.hpp"
#include "utils.hpp"
using namespace ov;
using namespace ov::intel_cpu;
using namespace ov::opset10;
using namespace testing;
class RollV7StaticShapeInferenceTest : public OpStaticShapeInferenceTest<op::v7::Roll> {
protected:
void SetUp() override {
output_shapes.resize(1);
}
};
TEST_F(RollV7StaticShapeInferenceTest, axes_as_constant) {
const auto arg = std::make_shared<Parameter>(ov::element::f32, ov::PartialShape::dynamic());
const auto shift = std::make_shared<Parameter>(ov::element::i64, ov::PartialShape::dynamic());
const auto axes = Constant::create(element::i64, Shape{2}, {-2, 1});
const auto op = make_op(arg, shift, axes);
input_shapes = {StaticShape{3, 5}, StaticShape{2}, StaticShape{2}};
shape_inference(op.get(), input_shapes, output_shapes);
EXPECT_EQ(output_shapes[0], input_shapes[0]);
}
TEST_F(RollV7StaticShapeInferenceTest, axes_in_const_map) {
const auto arg = std::make_shared<Parameter>(ov::element::f32, ov::PartialShape::dynamic());
const auto shift = std::make_shared<Parameter>(ov::element::i64, ov::PartialShape::dynamic());
const auto axes = std::make_shared<Parameter>(ov::element::i32, ov::PartialShape::dynamic());
const auto op = make_op(arg, shift, axes);
auto axes_val = std::array<int32_t, 3>{0, 1, -1};
const auto constant_data = std::map<size_t, HostTensorPtr>{
{2, std::make_shared<HostTensor>(element::i32, Shape{axes_val.size()}, axes_val.data())}};
input_shapes = {StaticShape{3, 3, 3}, StaticShape{3}, StaticShape{3}};
shape_inference(op.get(), input_shapes, output_shapes, constant_data);
EXPECT_EQ(output_shapes[0], input_shapes[0]);
}
TEST_F(RollV7StaticShapeInferenceTest, axes_over_arg_rank) {
const auto arg = std::make_shared<Parameter>(ov::element::f32, ov::PartialShape::dynamic());
const auto shift = std::make_shared<Parameter>(ov::element::i64, ov::PartialShape::dynamic());
const auto axes = std::make_shared<Parameter>(ov::element::i32, ov::PartialShape::dynamic());
const auto op = make_op(arg, shift, axes);
auto axes_val = std::array<int32_t, 3>{0, 3, -1};
const auto constant_data = std::map<size_t, HostTensorPtr>{
{2, std::make_shared<HostTensor>(element::i32, Shape{axes_val.size()}, axes_val.data())}};
input_shapes = {StaticShape{3, 3, 3}, StaticShape{3}, StaticShape{3}};
OV_EXPECT_THROW(shape_inference(op.get(), input_shapes, output_shapes, constant_data),
NodeValidationFailure,
HasSubstr("Parameter axis 3 out of the tensor rank range"));
}
TEST_F(RollV7StaticShapeInferenceTest, axes_has_negative_after_normalization) {
const auto arg = std::make_shared<Parameter>(ov::element::f32, ov::PartialShape::dynamic());
const auto shift = std::make_shared<Parameter>(ov::element::i64, ov::PartialShape::dynamic());
const auto axes = std::make_shared<Parameter>(ov::element::i64, ov::PartialShape::dynamic());
const auto op = make_op(arg, shift, axes);
auto axes_val = std::array<int64_t, 3>{-4, 2, -1};
const auto constant_data = std::map<size_t, HostTensorPtr>{
{2, std::make_shared<HostTensor>(element::i64, Shape{axes_val.size()}, axes_val.data())}};
input_shapes = {StaticShape{3, 3, 3}, StaticShape{3}, StaticShape{3}};
OV_EXPECT_THROW(shape_inference(op.get(), input_shapes, output_shapes, constant_data),
NodeValidationFailure,
HasSubstr(" Parameter axis -4 out of the tensor rank range"));
}
TEST_F(RollV7StaticShapeInferenceTest, default_ctor) {
const auto op = make_op();
auto axes_val = std::array<int64_t, 4>{-4, 2, -1, 1};
const auto constant_data = std::map<size_t, HostTensorPtr>{
{2, std::make_shared<HostTensor>(element::i64, Shape{axes_val.size()}, axes_val.data())}};
input_shapes = {StaticShape{3, 2, 5, 1}, StaticShape{}, StaticShape{4}};
shape_inference(op.get(), input_shapes, output_shapes, constant_data);
EXPECT_EQ(output_shapes[0], input_shapes[0]);
}