Broadcast-3 shape_infer review (#13849)
* v3::Broadcast tests * Shape infer improvements * Use Dimension broadcast_merge in Bidirectional mode * More tests * Style apply * Fix Unqueeze with Broadcast tests * Align Squeeze/Unsqueeze tests * Fix warning * Updates test shapes names * loops refactor * Remove redundant set/get labels from expected shapes * Padding refactor * Tests refactor * Revert auto for i in loop
This commit is contained in:
@@ -19,9 +19,9 @@ template <typename T>
|
||||
void validate_target_shape_none(const ov::Node* op,
|
||||
const T& arg_shape,
|
||||
const AxisVector& axes_mapping_val,
|
||||
const T& target_shape) {
|
||||
if (arg_shape.rank().is_static() && target_shape.rank().is_static()) {
|
||||
const auto target_rank_length = target_shape.size();
|
||||
const T& target_input_shape) {
|
||||
if (arg_shape.rank().is_static() && target_input_shape.rank().is_static()) {
|
||||
const auto target_rank_length = target_input_shape.size();
|
||||
// axes_mapping needs to be in sorted order
|
||||
NODE_VALIDATION_CHECK(op,
|
||||
std::is_sorted(axes_mapping_val.begin(), axes_mapping_val.end()),
|
||||
@@ -31,12 +31,12 @@ void validate_target_shape_none(const ov::Node* op,
|
||||
|
||||
if (arg_shape.size() == 0 && axes_mapping_val.size() > 0) {
|
||||
NODE_VALIDATION_CHECK(op,
|
||||
target_shape[axes_mapping_val[0]].compatible(1),
|
||||
target_input_shape[axes_mapping_val[0]].compatible(1),
|
||||
"Broadcast target[axes_mapping[0]]. Expected 1. Got ",
|
||||
target_shape[axes_mapping_val[0]]);
|
||||
target_input_shape[axes_mapping_val[0]]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < axes_mapping_val.size(); i++) {
|
||||
for (size_t i = 0; i < axes_mapping_val.size(); ++i) {
|
||||
NODE_VALIDATION_CHECK(op,
|
||||
axes_mapping_val[i] < target_rank_length,
|
||||
"Broadcast axes_mapping[",
|
||||
@@ -49,118 +49,111 @@ void validate_target_shape_none(const ov::Node* op,
|
||||
if (arg_shape.size() > 0) {
|
||||
NODE_VALIDATION_CHECK(
|
||||
op,
|
||||
target_shape[axes_mapping_val[i]].compatible(arg_shape[i]) || arg_shape[i].compatible(1),
|
||||
target_input_shape[axes_mapping_val[i]].compatible(arg_shape[i]) || arg_shape[i].compatible(1),
|
||||
"Broadcast target[axes_mapping[",
|
||||
i,
|
||||
"]]",
|
||||
" Expected ",
|
||||
arg_shape[i],
|
||||
". Got ",
|
||||
target_shape[axes_mapping_val[i]]);
|
||||
target_input_shape[axes_mapping_val[i]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void validate_target_shape_numpy(const ov::Node* op, const T& arg_shape, const T& target_shape) {
|
||||
if (arg_shape.rank().is_dynamic() || target_shape.rank().is_dynamic()) {
|
||||
void validate_target_shape_numpy(const ov::Node* op, const T& arg_shape, const T& target_input_shape) {
|
||||
if (arg_shape.rank().is_dynamic() || target_input_shape.rank().is_dynamic()) {
|
||||
return;
|
||||
}
|
||||
const auto arg_rank_length = arg_shape.size();
|
||||
const auto target_rank_length = target_shape.size();
|
||||
const auto start_axis = target_rank_length - arg_rank_length;
|
||||
const auto target_rank_length = target_input_shape.size();
|
||||
NODE_VALIDATION_CHECK(op,
|
||||
start_axis >= 0,
|
||||
!(target_rank_length < arg_rank_length),
|
||||
"Broadcast target_shape has smaller rank ",
|
||||
target_rank_length,
|
||||
" than arg shape ",
|
||||
arg_rank_length);
|
||||
for (size_t i = static_cast<size_t>(start_axis); i < target_rank_length; i++) {
|
||||
const size_t start_axis = target_rank_length - arg_rank_length;
|
||||
for (auto i = start_axis; i < target_rank_length; ++i) {
|
||||
NODE_VALIDATION_CHECK(op,
|
||||
arg_shape[i - start_axis].is_dynamic() || target_shape[i].is_dynamic() ||
|
||||
arg_shape[i - start_axis].is_dynamic() || target_input_shape[i].is_dynamic() ||
|
||||
arg_shape[i - start_axis].compatible(1) ||
|
||||
arg_shape[i - start_axis].compatible(target_shape[i]),
|
||||
arg_shape[i - start_axis].compatible(target_input_shape[i]),
|
||||
"Input shape dimension equal ",
|
||||
arg_shape[i - start_axis],
|
||||
" cannot be broadcasted (numpy mode) to ",
|
||||
target_shape[i],
|
||||
target_input_shape[i],
|
||||
". Allowed input dimension value would be 1",
|
||||
target_shape[i] != 1 ? " or " : "",
|
||||
target_shape[i] != 1 ? std::to_string(target_shape[i].get_length()) : "");
|
||||
target_input_shape[i] != 1 ? " or " : "",
|
||||
target_input_shape[i] != 1 ? std::to_string(target_input_shape[i].get_length()) : "");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void set_result_shape_pdpd(const ov::Node* op,
|
||||
const T& arg0_shape,
|
||||
const T& target_shape,
|
||||
const T& target_input_shape,
|
||||
T& result_shape,
|
||||
const ov::op::BroadcastModeSpec& broadcast_spec) {
|
||||
using DimType = typename std::iterator_traits<typename T::iterator>::value_type;
|
||||
if (arg0_shape.rank().is_dynamic() || target_shape.rank().is_dynamic()) {
|
||||
result_shape = PartialShape::dynamic(target_shape.rank());
|
||||
if (arg0_shape.rank().is_dynamic() || target_input_shape.rank().is_dynamic()) {
|
||||
result_shape = PartialShape::dynamic(target_input_shape.rank());
|
||||
return;
|
||||
}
|
||||
result_shape = target_shape;
|
||||
result_shape = target_input_shape;
|
||||
auto& start_axis = broadcast_spec.m_axis;
|
||||
|
||||
NODE_VALIDATION_CHECK(op, start_axis >= 0, "Broadcast start_axis must be greater than 0");
|
||||
|
||||
for (size_t i = start_axis; i < target_shape.size(); i++) {
|
||||
for (size_t i = start_axis; i < target_input_shape.size(); ++i) {
|
||||
const auto& arg_dim = arg0_shape[i - start_axis];
|
||||
if (arg_dim == 1) {
|
||||
result_shape[i] = target_shape[i];
|
||||
} else if (target_shape[i] == 1) {
|
||||
result_shape[i] = target_input_shape[i];
|
||||
} else if (target_input_shape[i] == 1) {
|
||||
result_shape[i] = arg_dim;
|
||||
} else {
|
||||
NODE_VALIDATION_CHECK(op,
|
||||
DimType::merge(result_shape[i], arg_dim, target_shape[i]),
|
||||
DimType::merge(result_shape[i], arg_dim, target_input_shape[i]),
|
||||
"Broadcast incorrect target shape. Expecting either 1 or ",
|
||||
arg_dim,
|
||||
" . Got ",
|
||||
target_shape[i]);
|
||||
target_input_shape[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void set_result_shape_bidirectional(const ov::Node* op, const T& arg_shape, T& target_shape, T& result_shape) {
|
||||
void set_result_shape_bidirectional(const ov::Node* op, const T& arg_shape, T& target_input_shape, T& result_shape) {
|
||||
using DimType = typename std::iterator_traits<typename T::iterator>::value_type;
|
||||
if (arg_shape.rank().is_dynamic() || target_shape.rank().is_dynamic()) {
|
||||
if (arg_shape.rank().is_dynamic() || target_input_shape.rank().is_dynamic()) {
|
||||
result_shape = PartialShape::dynamic();
|
||||
return;
|
||||
}
|
||||
auto arg_shape_vec = arg_shape;
|
||||
|
||||
// Add left padding to shorter target or argument shape
|
||||
const auto target_padded_rank = std::max(arg_shape_vec.size(), target_shape.size());
|
||||
while (arg_shape_vec.size() < target_padded_rank) {
|
||||
arg_shape_vec.insert(arg_shape_vec.begin(), 1);
|
||||
}
|
||||
while (target_shape.size() < target_padded_rank) {
|
||||
target_shape.insert(target_shape.begin(), 1);
|
||||
// Add left padding to the shape with smaller rank, if the ranks are not equal
|
||||
if (arg_shape_vec.size() < target_input_shape.size()) {
|
||||
arg_shape_vec.insert(arg_shape_vec.begin(), target_input_shape.size() - arg_shape_vec.size(), 1);
|
||||
} else {
|
||||
target_input_shape.insert(target_input_shape.begin(), arg_shape_vec.size() - target_input_shape.size(), 1);
|
||||
}
|
||||
|
||||
result_shape.resize(target_padded_rank);
|
||||
for (size_t i = 0; i < target_shape.size(); ++i) {
|
||||
if (arg_shape_vec[i] == 1) {
|
||||
result_shape[i] = target_shape[i];
|
||||
} else if (target_shape[i] == 1) {
|
||||
result_shape[i] = arg_shape_vec[i];
|
||||
} else {
|
||||
NODE_VALIDATION_CHECK(op,
|
||||
DimType::merge(result_shape[i], arg_shape_vec[i], target_shape[i]),
|
||||
"Broadcast incorrect target shape. Expecting either 1 or ",
|
||||
arg_shape_vec[i],
|
||||
". Got ",
|
||||
target_shape[i]);
|
||||
}
|
||||
result_shape.resize(target_input_shape.size());
|
||||
|
||||
for (size_t i = 0; i < target_input_shape.size(); ++i) {
|
||||
NODE_VALIDATION_CHECK(op,
|
||||
DimType::broadcast_merge(result_shape[i], arg_shape_vec[i], target_input_shape[i]),
|
||||
"Broadcast incorrect target shape. Expecting either 1 or ",
|
||||
arg_shape_vec[i],
|
||||
". Got ",
|
||||
target_input_shape[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void broadcase_base_shape_infer(
|
||||
void broadcast_base_shape_infer(
|
||||
const ov::op::util::BroadcastBase* op,
|
||||
const std::vector<T>& input_shapes,
|
||||
std::vector<T>& output_shapes,
|
||||
@@ -183,14 +176,14 @@ void broadcase_base_shape_infer(
|
||||
}
|
||||
|
||||
auto& result_shape = output_shapes[0];
|
||||
const auto& input_shape = input_shapes[0];
|
||||
const auto& target_shape = input_shapes[1];
|
||||
const bool is_target_shape_known = target_shape.is_static();
|
||||
const auto& data_input_shape = input_shapes[0];
|
||||
const auto& target_input_shape = input_shapes[1];
|
||||
const bool is_target_input_shape_static = target_input_shape.is_static();
|
||||
|
||||
T output_shape;
|
||||
bool output_shape_defined = get_data_as_shape<T>(1, op, output_shape, constant_data);
|
||||
T target_as_shape;
|
||||
bool is_target_shape_defined = get_data_as_shape<T>(1, op, target_as_shape, constant_data);
|
||||
|
||||
if (!output_shape_defined) {
|
||||
if (!is_target_shape_defined) {
|
||||
if (auto concat = ov::as_type_ptr<ov::opset1::Concat>(op->get_input_node_shared_ptr(1))) {
|
||||
const auto concat_inputs = concat->input_values();
|
||||
if (concat->get_output_partial_shape(0).is_static() && concat->get_shape().size() == 1 &&
|
||||
@@ -198,28 +191,29 @@ void broadcase_base_shape_infer(
|
||||
for (const auto& concat_input : concat_inputs) {
|
||||
auto source_node_ptr = concat_input.get_node_shared_ptr();
|
||||
if (auto source_const_ptr = ov::as_type_ptr<ov::opset1::Constant>(source_node_ptr)) {
|
||||
output_shape.push_back(source_const_ptr->get_axis_vector_val()[0]);
|
||||
target_as_shape.push_back(source_const_ptr->get_axis_vector_val()[0]);
|
||||
} else {
|
||||
output_shape.push_back(Dimension::dynamic());
|
||||
target_as_shape.push_back(Dimension::dynamic());
|
||||
}
|
||||
}
|
||||
output_shape_defined = true;
|
||||
is_target_shape_defined = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mode.m_type == BroadcastType::NONE) {
|
||||
if (output_shape_defined) {
|
||||
result_shape = output_shape;
|
||||
} else if (is_target_shape_known) {
|
||||
result_shape = PartialShape::dynamic(target_shape[0].get_length());
|
||||
if (is_target_shape_defined) {
|
||||
result_shape = target_as_shape;
|
||||
} else if (is_target_input_shape_static) {
|
||||
result_shape = PartialShape::dynamic(target_input_shape[0].get_length());
|
||||
} else {
|
||||
result_shape = PartialShape::dynamic();
|
||||
}
|
||||
// Validate axes_mapping
|
||||
const auto& axes_shape = input_shapes[2];
|
||||
if (input_shape.rank().is_static() && target_shape.rank().is_static() && axes_shape.is_static()) {
|
||||
int64_t input_rank = (input_shape.size() == 0 && axes_shape[0].get_length() > 0) ? 1 : input_shape.size();
|
||||
if (data_input_shape.rank().is_static() && target_input_shape.rank().is_static() && axes_shape.is_static()) {
|
||||
int64_t input_rank =
|
||||
(data_input_shape.size() == 0 && axes_shape[0].get_length() > 0) ? 1 : data_input_shape.size();
|
||||
NODE_VALIDATION_CHECK(op,
|
||||
axes_shape[0].get_length() == input_rank,
|
||||
"Broadcast axes_mapping shape ",
|
||||
@@ -227,34 +221,36 @@ void broadcase_base_shape_infer(
|
||||
" doesn't match rank of input tensor ",
|
||||
input_rank);
|
||||
std::vector<int64_t> axes_mapping_val;
|
||||
if (output_shape_defined && get_data_as_int64<T>(2, op, axes_mapping_val, constant_data)) {
|
||||
if (is_target_shape_defined && get_data_as_int64<T>(2, op, axes_mapping_val, constant_data)) {
|
||||
AxisVector axes_mapping =
|
||||
AxisVector(std::vector<size_t>(axes_mapping_val.begin(), axes_mapping_val.end()));
|
||||
validate_target_shape_none(op, input_shape, axes_mapping, output_shape);
|
||||
validate_target_shape_none(op, data_input_shape, axes_mapping, target_as_shape);
|
||||
}
|
||||
}
|
||||
} else if (mode.m_type == BroadcastType::NUMPY) {
|
||||
if (output_shape_defined) {
|
||||
result_shape = output_shape;
|
||||
validate_target_shape_numpy(op, input_shape, output_shape);
|
||||
} else if (is_target_shape_known) {
|
||||
result_shape = PartialShape::dynamic(target_shape[0].get_length());
|
||||
if (is_target_shape_defined) {
|
||||
result_shape = target_as_shape;
|
||||
validate_target_shape_numpy(op, data_input_shape, target_as_shape);
|
||||
} else if (is_target_input_shape_static) {
|
||||
result_shape = PartialShape::dynamic(target_input_shape[0].get_length());
|
||||
} else {
|
||||
result_shape = PartialShape::dynamic();
|
||||
}
|
||||
} else if (mode.m_type == BroadcastType::PDPD) {
|
||||
if (output_shape_defined) {
|
||||
set_result_shape_pdpd(op, input_shape, output_shape, result_shape, mode);
|
||||
} else if (is_target_shape_known) {
|
||||
result_shape = PartialShape::dynamic(target_shape[0].get_length());
|
||||
if (is_target_shape_defined) {
|
||||
set_result_shape_pdpd(op, data_input_shape, target_as_shape, result_shape, mode);
|
||||
} else if (is_target_input_shape_static) {
|
||||
result_shape = PartialShape::dynamic(target_input_shape[0].get_length());
|
||||
} else {
|
||||
result_shape = PartialShape::dynamic();
|
||||
}
|
||||
} else if (mode.m_type == BroadcastType::BIDIRECTIONAL) {
|
||||
if (output_shape_defined) {
|
||||
set_result_shape_bidirectional(op, input_shape, output_shape, result_shape);
|
||||
} else if (input_shape.rank().is_static() && is_target_shape_known) {
|
||||
auto output_rank = std::max(input_shape.size(), static_cast<size_t>(target_shape[0].get_length()));
|
||||
if (is_target_shape_defined) {
|
||||
set_result_shape_bidirectional(op, data_input_shape, target_as_shape, result_shape);
|
||||
} else if (data_input_shape.rank().is_static() && is_target_input_shape_static) {
|
||||
// TODO: Preserve dim != 1 info from the input_0
|
||||
auto output_rank =
|
||||
std::max(data_input_shape.size(), static_cast<size_t>(target_input_shape[0].get_length()));
|
||||
result_shape = PartialShape::dynamic(output_rank);
|
||||
} else {
|
||||
result_shape = PartialShape::dynamic();
|
||||
@@ -280,7 +276,7 @@ void shape_infer(const ov::op::v3::Broadcast* op,
|
||||
input_shapes.size() == 2,
|
||||
"axes_mapping input should not be provided for mode other than explicit");
|
||||
}
|
||||
broadcase_base_shape_infer(op, input_shapes, output_shapes, constant_data);
|
||||
broadcast_base_shape_infer(op, input_shapes, output_shapes, constant_data);
|
||||
}
|
||||
} // namespace v3
|
||||
|
||||
@@ -292,7 +288,7 @@ void shape_infer(const ov::op::v1::Broadcast* op,
|
||||
const std::map<size_t, std::shared_ptr<ngraph::runtime::HostTensor>>& constant_data = {}) {
|
||||
NODE_VALIDATION_CHECK(op, output_shapes.size() == 1 && (input_shapes.size() == 2 || input_shapes.size() == 3));
|
||||
|
||||
broadcase_base_shape_infer(op, input_shapes, output_shapes, constant_data);
|
||||
broadcast_base_shape_infer(op, input_shapes, output_shapes, constant_data);
|
||||
}
|
||||
} // namespace v1
|
||||
|
||||
|
||||
@@ -4,15 +4,18 @@
|
||||
|
||||
#include <dimension_tracker.hpp>
|
||||
|
||||
#include "common_test_utils/test_assertions.hpp"
|
||||
#include "gtest/gtest.h"
|
||||
#include "ngraph/ngraph.hpp"
|
||||
#include "ngraph/opsets/opset6.hpp"
|
||||
#include "openvino/op/util/attr_types.hpp"
|
||||
#include "util/type_prop.hpp"
|
||||
|
||||
NGRAPH_SUPPRESS_DEPRECATED_START
|
||||
|
||||
using namespace std;
|
||||
using namespace ngraph;
|
||||
using namespace testing;
|
||||
|
||||
// Because v3::Broadcast is backward compatible to v1::Broadcast all v1::Broadcast tests should pass
|
||||
template <typename T>
|
||||
@@ -915,4 +918,291 @@ TEST(type_prop, broadcast_i32_shape_value) {
|
||||
broadcast_v3->revalidate_and_infer_types();
|
||||
|
||||
ASSERT_EQ(broadcast_v3->get_output_partial_shape(0), PartialShape({5, -1}));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(type_prop, broadcast_v3_default_constructor) {
|
||||
auto param = make_shared<op::Parameter>(element::f32, Shape{5, 2, 3, 1});
|
||||
auto target_shape = op::Constant::create<int64_t>(element::i64, Shape{3}, {1, 3, 6});
|
||||
|
||||
auto op = make_shared<op::v3::Broadcast>();
|
||||
|
||||
EXPECT_EQ(op->get_broadcast_spec().m_type, op::BroadcastType::NUMPY);
|
||||
|
||||
op->set_broadcast_spec(op::BroadcastType::BIDIRECTIONAL);
|
||||
EXPECT_EQ(op->get_broadcast_spec().m_type, op::BroadcastType::BIDIRECTIONAL);
|
||||
|
||||
op->set_argument(0, param);
|
||||
op->set_argument(1, target_shape);
|
||||
op->validate_and_infer_types();
|
||||
|
||||
EXPECT_EQ(op->get_element_type(), element::f32);
|
||||
EXPECT_EQ(op->get_shape(), (Shape{5, 2, 3, 6}));
|
||||
}
|
||||
|
||||
TEST(type_prop, broadcast_v3_bidirectional_data_bigger_rank_numpy) {
|
||||
auto param = make_shared<op::Parameter>(element::f32, Shape{5, 2, 3, 1});
|
||||
auto target_shape = op::Constant::create<int64_t>(element::i64, Shape{3}, {4, 3, 6});
|
||||
|
||||
OV_EXPECT_THROW(auto b = make_shared<op::v3::Broadcast>(param, target_shape),
|
||||
NodeValidationFailure,
|
||||
HasSubstr("Broadcast target_shape has smaller rank"));
|
||||
}
|
||||
|
||||
TEST(type_prop, broadcast_v3_labels_in0_dynamic_mixed_dims_bidirectional) {
|
||||
// All dimensions of A have labels, B without labels
|
||||
PartialShape pshape_a{-1, 2, 1, {4, 8}, -1, {4, 8}, -1, {1, 8}, {1, 10}, {4, 18}};
|
||||
PartialShape pshape_b{-1, 2, {3, 9}, 1, {3, 9}, -1, {1, 9}, -1, {3, 19}, {1, 10}};
|
||||
|
||||
PartialShape expected_shape = {-1, 2, {3, 9}, {4, 8}, {3, 9}, {4, 8}, -1, -1, {3, 19}, {4, 18}};
|
||||
std::vector<size_t> expected_labels{10, 11, ov::no_label, 13, ov::no_label, 15, 16, 17, ov::no_label, 19};
|
||||
|
||||
set_shape_labels(pshape_a, {10, 11, 12, 13, 14, 15, 16, 17, 18, 19});
|
||||
|
||||
auto data = std::make_shared<op::Parameter>(element::f32, pshape_a);
|
||||
auto target_shape = std::make_shared<op::Parameter>(element::f32, pshape_b);
|
||||
auto shape_of = make_shared<op::v3::ShapeOf>(target_shape);
|
||||
|
||||
auto op = make_shared<op::v3::Broadcast>(data, shape_of, "BIDIRECTIONAL");
|
||||
|
||||
const auto out_shape = op->get_output_partial_shape(0);
|
||||
|
||||
EXPECT_EQ(out_shape, expected_shape);
|
||||
EXPECT_EQ(get_shape_labels(out_shape), expected_labels);
|
||||
}
|
||||
|
||||
TEST(type_prop, broadcast_v3_labels_in1_dynamic_mixed_dims_bidirectional) {
|
||||
// All dimensions of B have labels, A without labels
|
||||
PartialShape pshape_a{-1, 2, 1, {4, 8}, -1, {4, 8}, -1, {1, 8}, {1, 10}, {4, 18}};
|
||||
PartialShape pshape_b{-1, 2, {3, 9}, 1, {3, 9}, -1, {1, 9}, -1, {3, 19}, {1, 10}};
|
||||
|
||||
PartialShape expected_shape = {-1, 2, {3, 9}, {4, 8}, {3, 9}, {4, 8}, -1, -1, {3, 19}, {4, 18}};
|
||||
std::vector<size_t> expected_labels{10, 11, 12, ov::no_label, 14, ov::no_label, 16, 17, 18, ov::no_label};
|
||||
|
||||
set_shape_labels(pshape_b, {10, 11, 12, 13, 14, 15, 16, 17, 18, 19});
|
||||
|
||||
auto data = std::make_shared<op::Parameter>(element::f32, pshape_a);
|
||||
auto target_shape = std::make_shared<op::Parameter>(element::f32, pshape_b);
|
||||
auto shape_of = make_shared<op::v3::ShapeOf>(target_shape);
|
||||
|
||||
auto op = make_shared<op::v3::Broadcast>(data, shape_of, "BIDIRECTIONAL");
|
||||
|
||||
const auto out_shape = op->get_output_partial_shape(0);
|
||||
|
||||
EXPECT_EQ(out_shape, expected_shape);
|
||||
EXPECT_EQ(get_shape_labels(out_shape), expected_labels);
|
||||
}
|
||||
|
||||
TEST(type_prop, broadcast_v3_labels_different_dynamic_mixed_dims_broadcast_bidirectional) {
|
||||
// Both params have dimensions with different labels
|
||||
PartialShape pshape_a{-1, 2, 1, {4, 8}, -1, {4, 8}, -1, {1, 8}, {1, 10}, {4, 18}};
|
||||
PartialShape pshape_b{-1, 2, {3, 9}, 1, {3, 9}, -1, {1, 9}, -1, {3, 19}, {1, 10}};
|
||||
|
||||
PartialShape expected_shape = {-1, 2, {3, 9}, {4, 8}, {3, 9}, {4, 8}, -1, -1, {3, 19}, {4, 18}};
|
||||
std::vector<size_t> expected_labels{ov::no_label, 21, 22, 13, 24, 15, ov::no_label, ov::no_label, 28, 19};
|
||||
|
||||
set_shape_labels(pshape_a, {10, 11, 12, 13, 14, 15, 16, 17, 18, 19});
|
||||
set_shape_labels(pshape_b, {20, 21, 22, 23, 24, 25, 26, 27, 28, 29});
|
||||
|
||||
auto data = std::make_shared<op::Parameter>(element::f32, pshape_a);
|
||||
auto target_shape = std::make_shared<op::Parameter>(element::f32, pshape_b);
|
||||
auto shape_of = make_shared<op::v3::ShapeOf>(target_shape);
|
||||
|
||||
auto op = make_shared<op::v3::Broadcast>(data, shape_of, "BIDIRECTIONAL");
|
||||
|
||||
const auto out_shape = op->get_output_partial_shape(0);
|
||||
|
||||
EXPECT_EQ(out_shape, expected_shape);
|
||||
EXPECT_EQ(get_shape_labels(out_shape), expected_labels);
|
||||
}
|
||||
|
||||
TEST(type_prop, broadcast_v3_labels_same_dynamic_mixed_dims_broadcast_bidirectional) {
|
||||
// Both params have dimensions with the same labels
|
||||
PartialShape pshape_a{-1, 2, 1, {4, 8}, -1, {4, 8}, -1, {1, 8}, {1, 10}, {4, 18}};
|
||||
PartialShape pshape_b{-1, 2, {3, 9}, 1, {3, 9}, -1, {1, 9}, -1, {3, 19}, {1, 10}};
|
||||
|
||||
PartialShape expected_shape = {-1, 2, {3, 9}, {4, 8}, {3, 9}, {4, 8}, -1, -1, {3, 19}, {4, 18}};
|
||||
std::vector<size_t> expected_labels{10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
|
||||
|
||||
set_shape_labels(pshape_a, expected_labels);
|
||||
set_shape_labels(pshape_b, expected_labels);
|
||||
|
||||
auto data = std::make_shared<op::Parameter>(element::f32, pshape_a);
|
||||
auto target_shape = std::make_shared<op::Parameter>(element::f32, pshape_b);
|
||||
auto shape_of = make_shared<op::v3::ShapeOf>(target_shape);
|
||||
|
||||
auto op = make_shared<op::v3::Broadcast>(data, shape_of, "BIDIRECTIONAL");
|
||||
|
||||
const auto out_shape = op->get_output_partial_shape(0);
|
||||
|
||||
EXPECT_EQ(out_shape, expected_shape);
|
||||
EXPECT_EQ(get_shape_labels(out_shape), expected_labels);
|
||||
}
|
||||
|
||||
TEST(type_prop, broadcast_v3_non_broadcastable_dims_numpy) {
|
||||
// Numpy mode for v3::Broadcast mode is one directional
|
||||
PartialShape pshape_a{{4, 8}, {2, 4}};
|
||||
PartialShape pshape_b{{1}, {5, 6}};
|
||||
|
||||
// No validation for non-broadcastable dimensions pair
|
||||
PartialShape expected_shape = {1, {5, 6}};
|
||||
|
||||
auto data = std::make_shared<op::Parameter>(element::f32, pshape_a);
|
||||
auto target_shape = std::make_shared<op::Parameter>(element::f32, pshape_b);
|
||||
auto shape_of = make_shared<op::v3::ShapeOf>(target_shape);
|
||||
|
||||
auto op = make_shared<op::v3::Broadcast>(data, shape_of, "NUMPY");
|
||||
|
||||
const auto out_shape = op->get_output_partial_shape(0);
|
||||
EXPECT_EQ(out_shape, expected_shape);
|
||||
}
|
||||
|
||||
TEST(type_prop, broadcast_v3_labels_in0_dynamic_mixed_dims_numpy) {
|
||||
// Numpy mode for v3::Broadcast mode is one directional
|
||||
// All dimensions of A have labels, B without labels
|
||||
PartialShape pshape_a{-1, 2, 1, -1, {4, 8}, -1, {1, 8}, {1, 10}, {4, 18}};
|
||||
PartialShape pshape_b{-1, 2, {3, 9}, {4, 10}, -1, {5, 11}, -1, {6, 20}, {1, 10}};
|
||||
|
||||
PartialShape expected_shape = {-1, 2, {3, 9}, {4, 10}, -1, {5, 11}, -1, {6, 20}, {1, 10}};
|
||||
|
||||
set_shape_labels(pshape_a, {10, 11, 12, 13, 14, 15, 16, 17, 18});
|
||||
|
||||
auto data = std::make_shared<op::Parameter>(element::f32, pshape_a);
|
||||
auto target_shape = std::make_shared<op::Parameter>(element::f32, pshape_b);
|
||||
auto shape_of = make_shared<op::v3::ShapeOf>(target_shape);
|
||||
|
||||
auto op = make_shared<op::v3::Broadcast>(data, shape_of, "NUMPY");
|
||||
|
||||
const auto out_shape = op->get_output_partial_shape(0);
|
||||
EXPECT_EQ(out_shape, expected_shape);
|
||||
// Output shape is a copy of the target shape value, the `A` labels are not propagated
|
||||
EXPECT_THAT(get_shape_labels(out_shape), Each(ov::no_label));
|
||||
}
|
||||
|
||||
TEST(type_prop, broadcast_v3_labels_in1_dynamic_mixed_dims_numpy) {
|
||||
// Numpy mode for v3::Broadcast mode is one directional
|
||||
// All dimensions of B have labels, A without labels
|
||||
PartialShape pshape_a{-1, 2, 1, -1, {4, 8}, -1, {1, 8}, {1, 10}, {4, 18}};
|
||||
PartialShape pshape_b{-1, 2, {3, 9}, {4, 10}, -1, {5, 11}, -1, {6, 20}, {1, 10}};
|
||||
|
||||
PartialShape expected_shape = {-1, 2, {3, 9}, {4, 10}, -1, {5, 11}, -1, {6, 20}, {1, 10}};
|
||||
// Output shape is a copy of the target shape, `B` labels are propagated
|
||||
std::vector<size_t> expected_labels{10, 11, 12, 13, 14, 15, 16, 17, 18};
|
||||
|
||||
set_shape_labels(pshape_b, expected_labels);
|
||||
|
||||
auto data = std::make_shared<op::Parameter>(element::f32, pshape_a);
|
||||
auto target_shape = std::make_shared<op::Parameter>(element::f32, pshape_b);
|
||||
auto shape_of = make_shared<op::v3::ShapeOf>(target_shape);
|
||||
|
||||
auto op = make_shared<op::v3::Broadcast>(data, shape_of, "NUMPY");
|
||||
|
||||
const auto out_shape = op->get_output_partial_shape(0);
|
||||
|
||||
EXPECT_EQ(out_shape, expected_shape);
|
||||
EXPECT_EQ(get_shape_labels(out_shape), expected_labels);
|
||||
}
|
||||
|
||||
TEST(type_prop, broadcast_v3_labels_both_inputs_dynamic_mixed_dims_numpy) {
|
||||
// Numpy mode for v3::Broadcast mode is one directional
|
||||
// All dimensions of A and B have labels
|
||||
PartialShape pshape_a{-1, 2, 1, -1, {4, 8}, -1, {1, 8}, {1, 10}, {4, 18}};
|
||||
PartialShape pshape_b{-1, 2, {3, 9}, {4, 10}, -1, {5, 11}, -1, {6, 20}, {1, 10}};
|
||||
|
||||
PartialShape expected_shape = {-1, 2, {3, 9}, {4, 10}, -1, {5, 11}, -1, {6, 20}, {1, 10}};
|
||||
// Output shape is a copy of the target shape, `B` labels are propagated
|
||||
std::vector<size_t> expected_labels{20, 21, 22, 23, 24, 25, 26, 27, 28};
|
||||
|
||||
set_shape_labels(pshape_a, {10, 11, 12, 13, 14, 15, 16, 17, 18});
|
||||
set_shape_labels(pshape_b, {20, 21, 22, 23, 24, 25, 26, 27, 28});
|
||||
|
||||
auto data = std::make_shared<op::Parameter>(element::f32, pshape_a);
|
||||
auto target_shape = std::make_shared<op::Parameter>(element::f32, pshape_b);
|
||||
auto shape_of = make_shared<op::v3::ShapeOf>(target_shape);
|
||||
|
||||
auto op = make_shared<op::v3::Broadcast>(data, shape_of, "NUMPY");
|
||||
|
||||
const auto out_shape = op->get_output_partial_shape(0);
|
||||
|
||||
EXPECT_EQ(out_shape, expected_shape);
|
||||
EXPECT_EQ(get_shape_labels(out_shape), expected_labels);
|
||||
}
|
||||
|
||||
TEST(type_prop, broadcast_v3_labels_dynamic_mixed_dims_explicit) {
|
||||
PartialShape pshape_a{2, {6, 8}, -1};
|
||||
PartialShape pshape_b{2, -1, {6, 8}, -1, 5};
|
||||
|
||||
PartialShape expected_shape = {2, -1, {6, 8}, -1, 5};
|
||||
std::vector<size_t> expected_labels{21, 22, 23, 24, 25};
|
||||
|
||||
set_shape_labels(pshape_b, {21, 22, 23, 24, 25});
|
||||
auto axis_map = std::make_shared<op::Constant>(element::i32, Shape{3}, std::vector<int32_t>{0, 2, 3});
|
||||
|
||||
auto data = std::make_shared<op::Parameter>(element::f32, pshape_a);
|
||||
auto target_shape = std::make_shared<op::Parameter>(element::f32, pshape_b);
|
||||
auto shape_of = make_shared<op::v3::ShapeOf>(target_shape);
|
||||
|
||||
auto op = make_shared<op::v3::Broadcast>(data, shape_of, axis_map, "EXPLICIT");
|
||||
|
||||
const auto out_shape = op->get_output_partial_shape(0);
|
||||
|
||||
EXPECT_EQ(out_shape, expected_shape);
|
||||
EXPECT_EQ(get_shape_labels(out_shape), expected_labels);
|
||||
}
|
||||
|
||||
TEST(type_prop, broadcast_v3_eval_labels_static_dims_numpy) {
|
||||
// Numpy mode for v3::Broadcast mode is one directional
|
||||
// All dimensions of A have labels, B without labels
|
||||
PartialShape pshape_a{1, 1};
|
||||
PartialShape pshape_b{2, 3};
|
||||
PartialShape pshape_c{1, 3};
|
||||
|
||||
PartialShape expected_shape = {2, 3};
|
||||
std::vector<size_t> expected_labels{22, 23};
|
||||
|
||||
set_shape_labels(pshape_b, {22, 23});
|
||||
|
||||
auto a = std::make_shared<op::Parameter>(element::f32, pshape_a);
|
||||
auto b = std::make_shared<op::Parameter>(element::f32, pshape_b);
|
||||
auto shape_of_a = make_shared<op::v3::ShapeOf>(a);
|
||||
auto shape_of_b = make_shared<op::v3::ShapeOf>(b);
|
||||
|
||||
auto broadcast_a = make_shared<op::v3::Broadcast>(a, shape_of_b, "NUMPY");
|
||||
auto shape_of_broadcast_a = make_shared<op::v3::ShapeOf>(broadcast_a);
|
||||
|
||||
auto c = std::make_shared<op::Parameter>(element::f32, pshape_c);
|
||||
auto broadcast_c = make_shared<op::v3::Broadcast>(c, shape_of_broadcast_a, "NUMPY");
|
||||
|
||||
const auto out_shape = broadcast_c->get_output_partial_shape(0);
|
||||
|
||||
EXPECT_EQ(out_shape, expected_shape);
|
||||
EXPECT_EQ(get_shape_labels(out_shape), expected_labels);
|
||||
}
|
||||
|
||||
TEST(type_prop, broadcast_v3_eval_labels_static_dims_bidirectional) {
|
||||
PartialShape pshape_a{1, 3};
|
||||
PartialShape pshape_b{2, 1};
|
||||
PartialShape pshape_c{1, 1};
|
||||
|
||||
PartialShape expected_shape = {2, 3};
|
||||
std::vector<size_t> expected_labels{22, 13};
|
||||
|
||||
set_shape_labels(pshape_a, {12, 13});
|
||||
set_shape_labels(pshape_b, {22, 23});
|
||||
set_shape_labels(pshape_c, {33, 33});
|
||||
|
||||
auto a = std::make_shared<op::Parameter>(element::f32, pshape_a);
|
||||
auto b = std::make_shared<op::Parameter>(element::f32, pshape_b);
|
||||
auto shape_of_a = make_shared<op::v3::ShapeOf>(a);
|
||||
auto shape_of_b = make_shared<op::v3::ShapeOf>(b);
|
||||
|
||||
auto broadcast_a = make_shared<op::v3::Broadcast>(a, shape_of_b, "BIDIRECTIONAL");
|
||||
auto shape_of_broadcast_a = make_shared<op::v3::ShapeOf>(broadcast_a);
|
||||
|
||||
auto c = std::make_shared<op::Parameter>(element::f32, pshape_c);
|
||||
auto broadcast_c = make_shared<op::v3::Broadcast>(c, shape_of_broadcast_a, "BIDIRECTIONAL");
|
||||
|
||||
const auto out_shape = broadcast_c->get_output_partial_shape(0);
|
||||
|
||||
EXPECT_EQ(out_shape, expected_shape);
|
||||
EXPECT_EQ(get_shape_labels(out_shape), expected_labels);
|
||||
}
|
||||
|
||||
@@ -19,15 +19,15 @@ TEST(StaticShapeInferenceTest, BroadcastBidirectionalTest) {
|
||||
auto target_shape = std::make_shared<ov::op::v0::Parameter>(element::i32, PartialShape{-1});
|
||||
auto broadcast_v3 = std::make_shared<op::v3::Broadcast>(input, target_shape, op::BroadcastType::BIDIRECTIONAL);
|
||||
|
||||
int32_t target_shape_val[] = {1, 16, 50, 50};
|
||||
int32_t target_shape_val[] = {1, 16, 50, 1};
|
||||
std::map<size_t, std::shared_ptr<ngraph::runtime::HostTensor>> constant_data;
|
||||
constant_data[1] =
|
||||
std::make_shared<ngraph::runtime::HostTensor>(ngraph::element::Type_t::i32, ov::Shape{4}, target_shape_val);
|
||||
|
||||
std::vector<StaticShape> static_input_shapes = {StaticShape{16, 1, 1}, StaticShape{4}},
|
||||
std::vector<StaticShape> static_input_shapes = {StaticShape{16, 1, 8}, StaticShape{4}},
|
||||
static_output_shapes = {StaticShape{}};
|
||||
shape_inference(broadcast_v3.get(), static_input_shapes, static_output_shapes, constant_data);
|
||||
ASSERT_EQ(static_output_shapes[0], StaticShape({1, 16, 50, 50}));
|
||||
ASSERT_EQ(static_output_shapes[0], StaticShape({1, 16, 50, 8}));
|
||||
|
||||
static_input_shapes = {StaticShape{16, 1, 1}, StaticShape{4}};
|
||||
static_output_shapes = {StaticShape{}};
|
||||
@@ -215,4 +215,4 @@ TEST(StaticShapeInferenceTest, BroadcastV1ExplicitTest) {
|
||||
static_input_shapes = {StaticShape{3, 1}, StaticShape{3}, StaticShape{2}};
|
||||
static_output_shapes = {StaticShape{}};
|
||||
EXPECT_THROW(shape_inference(broadcast_v1.get(), static_input_shapes, static_output_shapes, {}), NodeValidationFailure);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user