Review ReverseSequence class for shape inference aspects (#15128)

* Review reverse sequence for:
- partial shapes and labels propagation
- template implementation of shape infer
- refactor shape_infer to use it when op created with default ctor

* Remove friend shape_infer from reverse sequence op
This commit is contained in:
Pawel Raasz authored and GitHub committed 2023-01-27 07:44:03 +01:00
1 parent ea48786bc6
commit c1b208b2a5
6 files changed
+296 -236

No files matched your search

@@ -34,13 +34,12 @@ public:
std::shared_ptr<Node> clone_with_new_inputs(const OutputVector& new_args) const override;
size_t get_batch_axis() const {
return m_normalized_batch_axis;
}
size_t get_batch_axis() const;
int64_t get_origin_batch_axis() const {
return m_batch_axis;
}
void set_batch_axis(int64_t batch_axis);
size_t get_sequence_axis() const {
return m_normalized_seq_axis;
}
@@ -50,16 +49,9 @@ public:
void set_sequence_axis(int64_t sequence_axis);
private:
int64_t m_batch_axis;
int64_t m_seq_axis = 1;
size_t m_normalized_batch_axis;
size_t m_normalized_seq_axis;
template <class T>
friend void shape_infer(const ReverseSequence* op,
const std::vector<T>& input_shapes,
std::vector<T>& output_shapes);
void normalize_axes(ReverseSequence* op);
int64_t m_batch_axis{};
int64_t m_seq_axis{1};
size_t m_normalized_seq_axis{};
};
} // namespace v0
} // namespace op
@@ -9,14 +9,16 @@
namespace ov {
namespace op {
namespace v0 {
template <class T>
void shape_infer(const ReverseSequence* op, const std::vector<T>& input_shapes, std::vector<T>& output_shapes) {
NODE_VALIDATION_CHECK(op, input_shapes.size() == 2 && output_shapes.size() == 1);
template <class TShape>
std::vector<TShape> shape_infer(const ReverseSequence* op, const std::vector<TShape>& input_shapes) {
NODE_VALIDATION_CHECK(op, input_shapes.size() == 2);
using DimType = typename TShape::value_type;
const auto& data_pshape = input_shapes[0];
const auto& data_rank = data_pshape.rank();
using DimType = typename std::iterator_traits<typename T::iterator>::value_type;
const auto& seq_lengths_pshape = input_shapes[1];
const auto& seq_lengths_rank = seq_lengths_pshape.rank();
NODE_VALIDATION_CHECK(op,
data_rank.is_dynamic() || data_rank.get_length() >= 2,
"Data input rank should be equal or greater than 2. Got: ",
@@ -26,25 +28,33 @@ void shape_infer(const ReverseSequence* op, const std::vector<T>& input_shapes,
seq_lengths_rank.compatible(1),
"Sequence lengths rank must be equal to 1. Got: ",
seq_lengths_pshape);
auto& output_pshape = output_shapes[0];
output_pshape = data_pshape;
auto output_pshape = data_pshape;
if (data_rank.is_static() && seq_lengths_rank.is_static()) {
const auto normalized_batch_axis = ov::normalize_axis(op, op->get_origin_batch_axis(), data_rank);
DimType merged_sequence_length;
NODE_VALIDATION_CHECK(
op,
DimType::merge(merged_sequence_length, data_pshape[op->m_normalized_batch_axis], seq_lengths_pshape[0]),
DimType::merge(merged_sequence_length, data_pshape[normalized_batch_axis], seq_lengths_pshape[0]),
"Sequence lengths input size (",
seq_lengths_pshape[0],
") is not equal to batch axis dimension of data input (",
data_pshape[op->m_normalized_batch_axis],
data_pshape[normalized_batch_axis],
") (argument shape: ",
data_pshape,
", sequence indices shape: ",
seq_lengths_pshape,
").");
output_pshape[op->m_normalized_batch_axis] = merged_sequence_length;
output_pshape[normalized_batch_axis] = merged_sequence_length;
}
return {output_pshape};
}
template <class TShape>
void shape_infer(const ReverseSequence* op,
const std::vector<TShape>& input_shapes,
std::vector<TShape>& output_shapes) {
output_shapes = shape_infer(op, input_shapes);
}
} // namespace v0
} // namespace op
+12 -27
View File
@@ -22,9 +22,7 @@ op::ReverseSequence::ReverseSequence(const Output<Node>& arg,
int64_t seq_axis)
: Op({arg, seq_indices}),
m_batch_axis(batch_axis),
m_seq_axis(seq_axis),
m_normalized_batch_axis{0},
m_normalized_seq_axis{0} {
m_seq_axis(seq_axis) {
constructor_validate_and_infer_types();
}
@@ -37,18 +35,16 @@ bool ngraph::op::v0::ReverseSequence::visit_attributes(AttributeVisitor& visitor
void op::ReverseSequence::validate_and_infer_types() {
OV_OP_SCOPE(v0_ReverseSequence_validate_and_infer_types);
const auto& data_pshape = get_input_partial_shape(0);
const auto& seq_lengths_et = get_input_element_type(1);
const auto& seq_lengths_pshape = get_input_partial_shape(1);
normalize_axes(this);
std::vector<ov::PartialShape> output_shapes = {ov::PartialShape{}};
std::vector<ov::PartialShape> input_shapes = {data_pshape, seq_lengths_pshape};
shape_infer(this, input_shapes, output_shapes);
NODE_VALIDATION_CHECK(this,
seq_lengths_et.is_real() || seq_lengths_et.is_integral_number(),
"Sequence lengths element type must be numeric type. Got: ",
seq_lengths_et);
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);
m_normalized_seq_axis = ov::normalize_axis(this, m_seq_axis, get_input_partial_shape(0).rank());
}
shared_ptr<Node> op::ReverseSequence::clone_with_new_inputs(const OutputVector& new_args) const {
@@ -59,25 +55,14 @@ shared_ptr<Node> op::ReverseSequence::clone_with_new_inputs(const OutputVector&
void op::ReverseSequence::set_batch_axis(int64_t batch_axis) {
m_batch_axis = batch_axis;
const auto& data_pshape = get_input_partial_shape(0);
const auto& data_rank = data_pshape.rank();
m_normalized_batch_axis = ov::normalize_axis(this, m_batch_axis, data_rank);
}
size_t op::ReverseSequence::get_batch_axis() const {
const auto& data_rank = get_input_partial_shape(0).rank();
return static_cast<size_t>(ov::normalize_axis(this, m_batch_axis, data_rank));
}
void op::ReverseSequence::set_sequence_axis(int64_t sequence_axis) {
m_seq_axis = sequence_axis;
const auto& data_pshape = get_input_partial_shape(0);
const auto& data_rank = data_pshape.rank();
m_normalized_seq_axis = ov::normalize_axis(this, m_seq_axis, data_rank);
}
void op::ReverseSequence::normalize_axes(ReverseSequence* op) {
const auto& data_pshape = op->get_input_partial_shape(0);
const auto& data_rank = data_pshape.rank();
NODE_VALIDATION_CHECK(op,
data_rank.is_dynamic() || data_rank.get_length() >= 2,
"Data input rank should be equal or greater than 2. Got: ",
data_pshape);
op->m_normalized_batch_axis = ov::normalize_axis(op, op->m_batch_axis, data_rank);
op->m_normalized_seq_axis = ov::normalize_axis(op, op->m_seq_axis, data_rank);
m_normalized_seq_axis = ov::normalize_axis(this, m_seq_axis, get_input_partial_shape(0).rank());
}
+183 -162
View File
@@ -2,17 +2,19 @@
// SPDX-License-Identifier: Apache-2.0
//
#include "gtest/gtest.h"
#include "ngraph/ngraph.hpp"
#include "common_test_utils/test_assertions.hpp"
#include "openvino/opsets/opset10.hpp"
#include "reverse_sequence_shape_inference.hpp"
#include "util/type_prop.hpp"
using namespace std;
using namespace ngraph;
using namespace ov;
using namespace ov::opset10;
using namespace testing;
TEST(type_prop, reverse_sequence_default_attributes) {
auto data = make_shared<op::Parameter>(element::f32, PartialShape{4, 3, 2});
auto seq_lengths = make_shared<op::Parameter>(element::i32, PartialShape{4});
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths);
auto data = std::make_shared<Parameter>(element::f32, PartialShape{4, 3, 2});
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape{4});
auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths);
EXPECT_EQ(reverse_seq->get_batch_axis(), 0);
EXPECT_EQ(reverse_seq->get_sequence_axis(), 1);
@@ -25,9 +27,9 @@ TEST(type_prop, reverse_sequence_default_attributes) {
TEST(type_prop, reverse_sequence_negative_attribute_axes) {
int64_t batch_axis = -3;
int64_t seq_axis = -2;
auto data = make_shared<op::Parameter>(element::f32, PartialShape{1, 2, 3, 4, 5});
auto seq_lengths = make_shared<op::Parameter>(element::i32, PartialShape{3});
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
auto data = std::make_shared<Parameter>(element::f32, PartialShape{1, 2, 3, 4, 5});
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape{3});
auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
EXPECT_EQ(reverse_seq->get_batch_axis(), 2);
EXPECT_EQ(reverse_seq->get_sequence_axis(), 3);
@@ -51,10 +53,10 @@ TEST(type_prop, reverse_sequence_data_et) {
element::boolean};
for (auto& et : element_types) {
try {
auto data = std::make_shared<op::Parameter>(et, PartialShape{4, 4});
auto seq_lengths = std::make_shared<op::Parameter>(element::i32, PartialShape{4});
auto data = std::make_shared<Parameter>(et, PartialShape{4, 4});
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape{4});
EXPECT_NO_THROW(const auto unused = std::make_shared<op::ReverseSequence>(data, seq_lengths));
EXPECT_NO_THROW(const auto unused = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths));
} catch (...) {
FAIL() << "Data input element type validation check failed for unexpected reason";
}
@@ -65,255 +67,274 @@ TEST(type_prop, reverse_sequence_invalid_seq_lengths_et) {
int64_t batch_axis = 0;
int64_t seq_axis = 1;
std::vector<element::Type> invalid_et{element::bf16, element::f16, element::f32, element::boolean};
auto data = make_shared<op::Parameter>(element::f32, PartialShape{4, 3, 2});
auto seq_lengths = make_shared<op::Parameter>(element::boolean, PartialShape{4});
try {
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
FAIL() << "Invalid element type of seq_lengths input not detected";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Sequence lengths element type must be numeric type."));
} catch (...) {
FAIL() << "Element type validation check of seq_lengths input failed for unexpected reason";
}
auto data = std::make_shared<Parameter>(element::f32, PartialShape{4, 3, 2});
auto seq_lengths = std::make_shared<Parameter>(element::boolean, PartialShape{4});
OV_EXPECT_THROW(
auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis),
NodeValidationFailure,
HasSubstr("Sequence lengths element type must be numeric type."));
}
TEST(type_prop, reverse_sequence_invalid_data_rank) {
std::vector<PartialShape> invalid_pshapes = {{}, {4}};
for (auto& pshape : invalid_pshapes) {
try {
auto data = make_shared<op::Parameter>(element::f32, pshape);
auto seq_lengths = make_shared<op::Parameter>(element::i32, PartialShape{1});
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths);
FAIL() << "Invalid rank of data input not detected";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Data input rank should be equal or greater than 2."));
} catch (...) {
FAIL() << "Rank check of data input failed for unexpected reason";
}
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape{1});
for (auto& invalid_shape : PartialShapes{{}, {4}}) {
auto data = std::make_shared<Parameter>(element::f32, invalid_shape);
OV_EXPECT_THROW(auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths),
NodeValidationFailure,
HasSubstr("Data input rank should be equal or greater than 2."));
}
}
TEST(type_prop, reverse_sequence_invalid_seq_lengths_rank) {
std::vector<PartialShape> invalid_pshapes = {{}, {4, 1}, {1, 1, 4}};
int64_t batch_axis = 0;
int64_t seq_axis = 1;
auto data = make_shared<op::Parameter>(element::f32, PartialShape{4, 3, 2});
for (auto& pshape : invalid_pshapes) {
try {
auto seq_lengths = make_shared<op::Parameter>(element::i32, pshape);
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
FAIL() << "Invalid rank of seq_lengths input not detected";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Sequence lengths rank must be equal to 1."));
} catch (...) {
FAIL() << "Rank check of seq_lengths input failed for unexpected reason";
}
constexpr int64_t batch_axis = 0, seq_axis = 1;
auto data = std::make_shared<Parameter>(element::f32, PartialShape{4, 3, 2});
for (auto& invalid_shape : PartialShapes{{}, {4, 1}, {1, 1, 4}}) {
auto seq_lengths = std::make_shared<Parameter>(element::i32, invalid_shape);
OV_EXPECT_THROW(
auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis),
NodeValidationFailure,
HasSubstr("Sequence lengths rank must be equal to 1."));
}
}
TEST(type_prop, reverse_sequence_invalid_batch_axis_value) {
int64_t batch_axis = 3;
int64_t seq_axis = 1;
auto data = make_shared<op::Parameter>(element::f32, PartialShape{4, 3, 2});
auto seq_lengths = make_shared<op::Parameter>(element::i32, PartialShape{3});
try {
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
FAIL() << "Invalid value of batch_axis attribute not detected";
} catch (const ngraph_error& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Parameter axis 3 out of the tensor rank"));
} catch (...) {
FAIL() << "Out of bounds check of batch_axis attribute failed for unexpected reason";
}
auto data = std::make_shared<Parameter>(element::f32, PartialShape{4, 3, 2});
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape{3});
OV_EXPECT_THROW(
auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis),
AssertFailure,
HasSubstr("Parameter axis 3 out of the tensor rank"));
}
TEST(type_prop, reverse_sequence_invalid_seq_axis_value) {
int64_t batch_axis = 1;
int64_t seq_axis = 3;
auto data = make_shared<op::Parameter>(element::f32, PartialShape{4, 3, 2});
auto seq_lengths = make_shared<op::Parameter>(element::i32, PartialShape{3});
try {
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
FAIL() << "Invalid value of seq_axis attribute not detected";
} catch (const ngraph_error& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Parameter axis 3 out of the tensor rank"));
} catch (...) {
FAIL() << "Out of bounds check of seq_axis attribute failed for unexpected reason";
}
auto data = std::make_shared<Parameter>(element::f32, PartialShape{4, 3, 2});
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape{3});
OV_EXPECT_THROW(
auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis),
AssertFailure,
HasSubstr("Parameter axis 3 out of the tensor rank"));
}
TEST(type_prop, reverse_sequence_incompatible_seq_len_size_with_batch_dim) {
auto data = make_shared<op::Parameter>(element::f32, PartialShape{4, 3, 2});
auto seq_lengths = make_shared<op::Parameter>(element::i32, PartialShape{3});
try {
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths);
FAIL() << "Incompatible number of elements between seq_lengths and batch dimension of data input not detected";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(
error.what(),
std::string("Sequence lengths input size (3) is not equal to batch axis dimension of data input (4)"));
} catch (...) {
FAIL() << "Number of elements of input seq_lengths check with respect batch dimension of input failed for "
"unexpected reason";
}
auto data = std::make_shared<Parameter>(element::f32, PartialShape{4, 3, 2});
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape{3});
OV_EXPECT_THROW(
auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths),
NodeValidationFailure,
HasSubstr("Sequence lengths input size (3) is not equal to batch axis dimension of data input (4)"));
}
TEST(type_prop, reverse_sequence_dynamic_inputs_with_dynamic_rank) {
auto data = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
auto seq_lengths = make_shared<op::Parameter>(element::i32, PartialShape::dynamic());
auto data = std::make_shared<Parameter>(element::f32, PartialShape::dynamic());
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape::dynamic());
// Unrealistic values, but they don't matter here.
int64_t batch_axis = 202;
int64_t seq_axis = 909;
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
EXPECT_EQ(reverse_seq->get_output_partial_shape(0), (PartialShape::dynamic()));
EXPECT_EQ(reverse_seq->get_output_element_type(0), element::f32);
}
TEST(type_prop, reverse_sequence_dynamic_data_input_dynamic_rank) {
auto data = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
auto seq_lengths = make_shared<op::Parameter>(element::i32, PartialShape{3});
auto data = std::make_shared<Parameter>(element::f32, PartialShape::dynamic());
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape{3});
// Unrealistic values, but they don't matter here.
int64_t batch_axis = 202;
int64_t seq_axis = 909;
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
EXPECT_EQ(reverse_seq->get_output_partial_shape(0), (PartialShape::dynamic()));
EXPECT_EQ(reverse_seq->get_output_element_type(0), element::f32);
}
TEST(type_prop, reverse_sequence_dynamic_seq_lenghts_input_dynamic_rank) {
auto data = make_shared<op::Parameter>(element::f32, PartialShape{2, 4, 6, 8});
auto seq_lengths = make_shared<op::Parameter>(element::i32, PartialShape::dynamic());
auto data = std::make_shared<Parameter>(element::f32, PartialShape{2, 4, 6, 8});
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape::dynamic());
int64_t batch_axis = 0;
int64_t seq_axis = 1;
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
EXPECT_TRUE(reverse_seq->get_output_partial_shape(0).same_scheme(PartialShape{2, 4, 6, 8}));
EXPECT_EQ(reverse_seq->get_output_partial_shape(0), PartialShape({2, 4, 6, 8}));
EXPECT_EQ(reverse_seq->get_output_element_type(0), element::f32);
}
TEST(type_prop, reverse_sequence_dynamic_data_input_static_rank_and_seq_lengths_dynamic_rank) {
auto data = make_shared<op::Parameter>(
auto data = std::make_shared<Parameter>(
element::f32,
PartialShape{Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic()});
auto seq_lengths = make_shared<op::Parameter>(element::i32, PartialShape::dynamic());
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape::dynamic());
int64_t batch_axis = 0;
int64_t seq_axis = 1;
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
EXPECT_TRUE(reverse_seq->get_output_partial_shape(0).same_scheme(
PartialShape{Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic()}));
EXPECT_EQ(reverse_seq->get_output_partial_shape(0), PartialShape::dynamic(4));
EXPECT_EQ(reverse_seq->get_output_element_type(0), element::f32);
}
TEST(type_prop, reverse_sequence_dynamic_invalid_batch_axis) {
auto data = make_shared<op::Parameter>(
auto data = std::make_shared<Parameter>(
element::f32,
PartialShape{Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic()});
auto seq_lengths = make_shared<op::Parameter>(element::i32, PartialShape{Dimension::dynamic()});
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape{Dimension::dynamic()});
int64_t batch_axis = 4;
int64_t seq_axis = 1;
try {
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
FAIL() << "Invalid batch_axis attribute value not detected (rank-static dynamic shape)";
} catch (const ngraph_error& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Parameter axis 4 out of the tensor rank"));
} catch (...) {
FAIL() << "Out of bounds check of batch_axis attribute failed for unexpected reason";
}
OV_EXPECT_THROW(
auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis),
AssertFailure,
HasSubstr("Parameter axis 4 out of the tensor rank"));
}
TEST(type_prop, reverse_sequence_dynamic_invalid_seq_axis) {
auto data = make_shared<op::Parameter>(
auto data = std::make_shared<Parameter>(
element::f32,
PartialShape{Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic()});
auto seq_lengths = make_shared<op::Parameter>(element::i32, PartialShape{Dimension::dynamic()});
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape{Dimension::dynamic()});
int64_t batch_axis = 1;
int64_t seq_axis = 4;
try {
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
FAIL() << "Invalid seq_axis attribute value not detected (rank-static dynamic shape)";
} catch (const ngraph_error& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Parameter axis 4 out of the tensor rank"));
} catch (...) {
FAIL() << "Out of bounds check of seq_axis attribute failed for unexpected reason";
}
OV_EXPECT_THROW(
auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis),
AssertFailure,
HasSubstr("Parameter axis 4 out of the tensor rank"));
}
TEST(type_prop, reverse_sequence_dynamic_data_input_static_rank) {
auto data = make_shared<op::Parameter>(
element::f32,
PartialShape{Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic()});
auto seq_lengths = make_shared<op::Parameter>(element::i32, PartialShape{3});
auto data_shape = PartialShape::dynamic(4);
set_shape_labels(data_shape, 10);
auto data = std::make_shared<Parameter>(element::f32, data_shape);
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape{3});
int64_t batch_axis = 2;
int64_t seq_axis = 1;
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
EXPECT_TRUE(reverse_seq->get_output_partial_shape(0).same_scheme(
PartialShape{Dimension::dynamic(), Dimension::dynamic(), 3, Dimension::dynamic()}));
EXPECT_EQ(reverse_seq->get_output_partial_shape(0), PartialShape({-1, -1, 3, -1}));
EXPECT_EQ(reverse_seq->get_output_element_type(0), element::f32);
EXPECT_THAT(get_shape_labels(reverse_seq->get_output_partial_shape(0)), ElementsAre(10, 11, 12, 13));
}
TEST(type_prop, reverse_sequence_dynamic_data_input_static_rank_seq_lengths_input_dynamic_rank) {
auto data =
make_shared<op::Parameter>(element::f32,
PartialShape{Dimension::dynamic(), Dimension::dynamic(), 3, Dimension::dynamic()});
auto seq_lengths = make_shared<op::Parameter>(element::i32, PartialShape{Dimension::dynamic()});
std::make_shared<Parameter>(element::f32,
PartialShape{Dimension::dynamic(), Dimension::dynamic(), 3, Dimension::dynamic()});
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape{Dimension::dynamic()});
int64_t batch_axis = 2;
int64_t seq_axis = 1;
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
EXPECT_TRUE(reverse_seq->get_output_partial_shape(0).same_scheme(
PartialShape{Dimension::dynamic(), Dimension::dynamic(), 3, Dimension::dynamic()}));
EXPECT_EQ(reverse_seq->get_output_partial_shape(0), PartialShape({-1, -1, 3, -1}));
EXPECT_EQ(reverse_seq->get_output_element_type(0), element::f32);
}
TEST(type_prop, reverse_sequence_dynamic_data_input_static_rank_with_static_batch_dim) {
auto data =
make_shared<op::Parameter>(element::f32,
PartialShape{Dimension::dynamic(), Dimension::dynamic(), 3, Dimension::dynamic()});
auto seq_lengths = make_shared<op::Parameter>(element::i32, PartialShape{3});
std::make_shared<Parameter>(element::f32,
PartialShape{Dimension::dynamic(), Dimension::dynamic(), 3, Dimension::dynamic()});
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape{3});
int64_t batch_axis = 2;
int64_t seq_axis = 1;
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
EXPECT_TRUE(reverse_seq->get_output_partial_shape(0).same_scheme(
PartialShape{Dimension::dynamic(), Dimension::dynamic(), 3, Dimension::dynamic()}));
EXPECT_EQ(reverse_seq->get_output_partial_shape(0), PartialShape({-1, -1, 3, -1}));
EXPECT_EQ(reverse_seq->get_output_element_type(0), element::f32);
}
TEST(type_prop, reverse_sequence_dynamic_incompatible_data_input_static_rank_with_static_batch_dim) {
auto data =
make_shared<op::Parameter>(element::f32,
PartialShape{Dimension::dynamic(), Dimension::dynamic(), 3, Dimension::dynamic()});
auto seq_lengths = make_shared<op::Parameter>(element::i32, PartialShape{4});
std::make_shared<Parameter>(element::f32,
PartialShape{Dimension::dynamic(), Dimension::dynamic(), 3, Dimension::dynamic()});
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape{4});
int64_t batch_axis = 2;
int64_t seq_axis = 1;
try {
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
FAIL() << "Incompatible number of elements between seq_lengths and batch dimension of data input not detected "
"(rank-static dynamic shape)";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(
error.what(),
std::string("Sequence lengths input size (4) is not equal to batch axis dimension of data input (3)"));
} catch (...) {
FAIL() << "Number of elements of input seq_lengths check with respect batch dimension of input failed for "
"unexpected reason";
}
OV_EXPECT_THROW(
auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis),
NodeValidationFailure,
HasSubstr("Sequence lengths input size (4) is not equal to batch axis dimension of data input (3)"));
}
TEST(type_prop, reverse_sequence_dynamic_invalid_negative_axis_and_data_input_dynamic_rank) {
auto data = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
auto seq_lengths = make_shared<op::Parameter>(element::i32, PartialShape{1});
class TypePropReverseSequenceV0Test : public TypePropOpTest<op::v0::ReverseSequence> {};
TEST_F(TypePropReverseSequenceV0Test, dynamic_invalid_negative_axis_and_data_input_dynamic_rank) {
auto data = std::make_shared<Parameter>(element::f32, PartialShape::dynamic());
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape{1});
int64_t batch_axis = 1;
int64_t seq_axis = -2;
try {
auto reverse_seq = make_shared<op::ReverseSequence>(data, seq_lengths, batch_axis, seq_axis);
FAIL() << "Dynamic rank of data input for negative axis not detected";
} catch (const CheckFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Rank must be static in order to normalize negative axis=-2"));
} catch (...) {
FAIL() << "Static rank of data input for negative axis validation check failed for unexpected reason";
}
OV_EXPECT_THROW(auto reverse_seq = make_op(data, seq_lengths, batch_axis, seq_axis),
AssertFailure,
HasSubstr("Rank must be static in order to normalize negative axis=-2"));
}
TEST_F(TypePropReverseSequenceV0Test, default_ctor) {
auto data = std::make_shared<Parameter>(element::f32, PartialShape{4, 3, 2});
auto seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape{3});
auto op = make_op();
op->set_arguments(OutputVector{data, seq_lengths});
op->set_batch_axis(1);
op->set_sequence_axis(0);
op->validate_and_infer_types();
EXPECT_EQ(op->get_batch_axis(), 1);
EXPECT_EQ(op->get_sequence_axis(), 0);
EXPECT_EQ(op->get_input_size(), 2);
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), (PartialShape{4, 3, 2}));
}
TEST_F(TypePropReverseSequenceV0Test, default_ctor_no_arguments) {
auto op = make_op();
op->set_batch_axis(1);
const auto output_shapes = shape_infer(op.get(), PartialShapes{{{4, 5}, 3, 2}, {3}});
EXPECT_EQ(output_shapes.size(), 1);
EXPECT_EQ(output_shapes.front(), PartialShape({{4, 5}, 3, 2}));
EXPECT_EQ(op->get_input_size(), 0);
EXPECT_EQ(op->get_output_size(), 0);
}
TEST_F(TypePropReverseSequenceV0Test, data_shape_interval_and_sequence_static_dim_with_labels) {
auto data_shape = PartialShape{{2, 5}, 4, {1, 3}};
auto seq_shape = PartialShape{3};
set_shape_labels(data_shape, 10);
set_shape_labels(seq_shape, 20);
auto data = std::make_shared<Parameter>(element::f32, data_shape);
auto seq_lengths = std::make_shared<Parameter>(element::i32, seq_shape);
auto op = make_op(data, seq_lengths, 0, 1);
EXPECT_EQ(op->get_output_element_type(0), element::f32);
EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({3, 4, {1, 3}}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), ElementsAre(20, 11, 12));
}
TEST_F(TypePropReverseSequenceV0Test, data_shape_and_sequence_interval_dim_with_labels) {
auto data_shape = PartialShape{{2, 5}, 4, {1, 3}};
auto seq_shape = PartialShape{{3, 6}};
set_shape_labels(data_shape, 10);
set_shape_labels(seq_shape, 20);
auto data = std::make_shared<Parameter>(element::f32, data_shape);
auto seq_lengths = std::make_shared<Parameter>(element::i32, seq_shape);
auto op = make_op(data, seq_lengths, -3, -1);
EXPECT_EQ(op->get_output_element_type(0), element::f32);
EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({{3, 5}, 4, {1, 3}}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), ElementsAre(20, 11, 12));
}
@@ -1,24 +0,0 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <gtest/gtest.h>
#include <openvino/op/parameter.hpp>
#include <reverse_sequence_shape_inference.hpp>
#include <utils/shape_inference/shape_inference.hpp>
#include <utils/shape_inference/static_shape.hpp>
using namespace ov;
using namespace ov::intel_cpu;
TEST(StaticShapeInferenceTest, ReverseSequenceTest) {
auto data = std::make_shared<op::v0::Parameter>(element::f32, PartialShape{-1, -1, -1});
auto seq_lengths = std::make_shared<op::v0::Parameter>(element::i32, PartialShape{4});
auto reverse_seq = std::make_shared<op::v0::ReverseSequence>(data, seq_lengths);
// Test StaticShape
std::vector<StaticShape> static_input_shapes = {StaticShape{4, 3, 2}, StaticShape{4}},
static_output_shapes = {StaticShape{}};
shape_inference(reverse_seq.get(), static_input_shapes, static_output_shapes);
ASSERT_EQ(static_output_shapes[0], (StaticShape{4, 3, 2}));
}
@@ -0,0 +1,76 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "common_test_utils/test_assertions.hpp"
#include "openvino/opsets/opset10.hpp"
#include "utils.hpp"
using namespace ov;
using namespace ov::intel_cpu;
using namespace ov::opset10;
using namespace testing;
class ReverseSequenceV0StaticShapeInferenceTest : public OpStaticShapeInferenceTest<op::v0::ReverseSequence> {
protected:
void SetUp() override {
output_shapes.resize(1);
data = std::make_shared<Parameter>(element::f32, PartialShape::dynamic());
seq_lengths = std::make_shared<Parameter>(element::i32, PartialShape::dynamic());
}
std::shared_ptr<Parameter> data, seq_lengths;
};
TEST_F(ReverseSequenceV0StaticShapeInferenceTest, default_batch_seq_axes) {
auto op = make_op(data, seq_lengths);
input_shapes = ShapeVector{{4, 3, 2}, {4}};
shape_inference(op.get(), input_shapes, output_shapes);
EXPECT_EQ(output_shapes[0], StaticShape({4, 3, 2}));
}
TEST_F(ReverseSequenceV0StaticShapeInferenceTest, set_batch_seq_axes) {
auto op = make_op(data, seq_lengths, -1, 1);
input_shapes = ShapeVector{{4, 3, 2}, {2}};
shape_inference(op.get(), input_shapes, output_shapes);
EXPECT_EQ(output_shapes[0], StaticShape({4, 3, 2}));
}
TEST_F(ReverseSequenceV0StaticShapeInferenceTest, invalid_input_shapes_count) {
auto op = make_op(data, seq_lengths);
input_shapes = ShapeVector{{1, 2, 4}};
EXPECT_THROW(shape_inference(op.get(), input_shapes, output_shapes), NodeValidationFailure);
}
TEST_F(ReverseSequenceV0StaticShapeInferenceTest, invalid_data_shape_rank) {
auto op = make_op(data, seq_lengths);
input_shapes = ShapeVector{{4}, {4}};
OV_EXPECT_THROW(shape_inference(op.get(), input_shapes, output_shapes),
NodeValidationFailure,
HasSubstr("Data input rank should be equal or greater than 2. Got: "));
}
TEST_F(ReverseSequenceV0StaticShapeInferenceTest, invalid_sequence_shape_rank) {
auto op = make_op(data, seq_lengths);
input_shapes = ShapeVector{{4, 5, 6}, {2, 2}};
OV_EXPECT_THROW(shape_inference(op.get(), input_shapes, output_shapes),
NodeValidationFailure,
HasSubstr("Sequence lengths rank must be equal to 1. Got: "));
}
TEST_F(ReverseSequenceV0StaticShapeInferenceTest, default_ctor) {
auto op = make_op();
input_shapes = ShapeVector{{11, 2, 3}, {11}};
shape_inference(op.get(), input_shapes, output_shapes);
EXPECT_EQ(output_shapes[0], StaticShape({11, 2, 3}));
}