Review opset1 unsqueeze for shape inference aspects (#13538)
* Use non-blocking assertions in unsqueeze tests for independent properties * Review unsqueeze interval shape propagation: - extend partial shape propagation tests - add invalid axis value tests - fix issues when repeated axes on input * Shape inference test, unsqueeze using default ctor - private fields set by setters * Review unsqeeze bounds propagation: - preserve and propagate labels - bounds propagation lowe/upper * Add template shape inference for unsqueeze - extract current implementation and unify it for shape types - add unit test for static shape inference - add unsqueeze in/out indexes * Unify axes normalization * Use common fixture for static shape inference * Fix build issue in GPU plugin - include unsqueeze shape inference * Remove enum with in/out indexes Due to build issue on Windows * Remove make move iterator minor changes validation util * Add test for label propagation - expand static shape inference tests * Add common validation for axes input * Fix build issues Co-authored-by: Evgenya Stepyreva <evgenya.stepyreva@intel.com>
This commit is contained in:
@@ -108,9 +108,9 @@ int64_t normalize_axis(const std::string& node_description,
|
||||
/// If any negative axis in vector, it counts from the last to the first axis,
|
||||
/// by adding tensor_rank to axis. Changes axes vector inplace.
|
||||
///
|
||||
/// \param[in] node The node with requested axes.
|
||||
/// \param[in] tensor_rank The corresponding tensor rank.
|
||||
/// \param[in] axes The requested vector of axes.
|
||||
/// \param[in] node The node with requested axes.
|
||||
/// \param[in] tensor_rank The corresponding tensor rank.
|
||||
/// \param[in,out] axes The requested vector of axes.
|
||||
///
|
||||
OPENVINO_API
|
||||
void normalize_axes(const Node* node, const int64_t& tensor_rank, std::vector<int64_t>& axes);
|
||||
@@ -157,4 +157,19 @@ OPENVINO_API bool is_valid_axes_order(const std::vector<int64_t>& axes_order, co
|
||||
/// \param labels Label tensor for check.
|
||||
/// \return True if there is no labels, otherwise false.
|
||||
OPENVINO_API bool has_no_labels(const TensorLabel& labels);
|
||||
|
||||
/// \brief Get the node input partial shapes.
|
||||
///
|
||||
/// \param node Node to extract input shapes.
|
||||
///
|
||||
/// \return Vector of PartialShapes of each input.
|
||||
OPENVINO_API std::vector<PartialShape> get_node_input_partial_shapes(const ov::Node& node);
|
||||
|
||||
/// \brief Check if rank is compatible to any of rank from container.
|
||||
///
|
||||
/// \param rank Rank to check.
|
||||
/// \param ranks VEctor of ranks used to check input rank compatibility.
|
||||
///
|
||||
/// \return True if rank compatible to any from ranks, otherwise false.
|
||||
OPENVINO_API bool is_rank_compatible_any_of(const ov::Rank& rank, const std::vector<ov::Rank>& ranks);
|
||||
} // namespace ov
|
||||
|
||||
@@ -23,29 +23,37 @@ enum Bound : uint8_t { NONE, LOWER, UPPER, BOTH };
|
||||
*/
|
||||
template <class T, Bound BMode = Bound::NONE>
|
||||
class Between {
|
||||
T _lower_bound, _upper_bound;
|
||||
const T m_lower_bound, m_upper_bound;
|
||||
|
||||
public:
|
||||
constexpr Between(const T& lower, const T& upper) : _lower_bound{lower}, _upper_bound{upper} {}
|
||||
constexpr Between(const T& lower, const T& upper) : m_lower_bound{lower}, m_upper_bound{upper} {}
|
||||
|
||||
template <Bound B = BMode, typename std::enable_if<B == Bound::NONE>::type* = nullptr>
|
||||
constexpr bool operator()(const T& value) const {
|
||||
return (_lower_bound < value) && (value < _upper_bound);
|
||||
return (lower() < value) && (value < upper());
|
||||
}
|
||||
|
||||
template <Bound B = BMode, typename std::enable_if<B == Bound::LOWER>::type* = nullptr>
|
||||
constexpr bool operator()(const T& value) const {
|
||||
return (_lower_bound <= value) && (value < _upper_bound);
|
||||
return (lower() <= value) && (value < upper());
|
||||
}
|
||||
|
||||
template <Bound B = BMode, typename std::enable_if<B == Bound::UPPER>::type* = nullptr>
|
||||
constexpr bool operator()(const T& value) const {
|
||||
return (_lower_bound < value) && (value <= _upper_bound);
|
||||
return (lower() < value) && (value <= upper());
|
||||
}
|
||||
|
||||
template <Bound B = BMode, typename std::enable_if<B == Bound::BOTH>::type* = nullptr>
|
||||
constexpr bool operator()(const T& value) const {
|
||||
return (_lower_bound <= value) && (value <= _upper_bound);
|
||||
return (lower() <= value) && (value <= upper());
|
||||
}
|
||||
|
||||
const T& upper() const {
|
||||
return m_upper_bound;
|
||||
}
|
||||
|
||||
const T& lower() const {
|
||||
return m_lower_bound;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -124,33 +124,6 @@ void shape_infer(const ov::opset1::Squeeze* op,
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void shape_infer(const ov::opset1::Unsqueeze* 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() == 2 && output_shapes.size() == 1);
|
||||
std::vector<int64_t> axes;
|
||||
bool axes_is_constant = get_data_as_int64<T>(1, op, axes, constant_data);
|
||||
NODE_VALIDATION_CHECK(op, axes_is_constant, "Shape inference lacks input data");
|
||||
|
||||
auto& input_shape = input_shapes[0];
|
||||
OPENVINO_ASSERT(input_shape.is_static());
|
||||
auto& output_shape = output_shapes[0];
|
||||
output_shape = input_shape;
|
||||
|
||||
NODE_VALIDATION_CHECK(op, !axes.empty(), "'axes' input is mandatory");
|
||||
|
||||
int64_t expanded_rank = input_shape.size() + axes.size();
|
||||
ov::normalize_axes(op, static_cast<int64_t>(expanded_rank), axes);
|
||||
|
||||
std::set<int64_t> unique_sorted_axes(axes.begin(), axes.end());
|
||||
for (const auto& axis : unique_sorted_axes) {
|
||||
NODE_VALIDATION_CHECK(op, axis <= expanded_rank, "provided 'axes' value ", axis, " is not valid.");
|
||||
output_shape.insert(next(output_shape.begin(), axis), 1);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void dynamic_shape(T& output_shape) {
|
||||
OPENVINO_UNREACHABLE("This code should be executed only for PartialShape class");
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright (C) 2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "openvino/op/unsqueeze.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace op {
|
||||
namespace v0 {
|
||||
|
||||
template <class TOp>
|
||||
void check_unsqueeze_axes_rank(const TOp* op, const Rank& rank) {
|
||||
NODE_VALIDATION_CHECK(op,
|
||||
is_rank_compatible_any_of(rank, {0, 1}),
|
||||
"Second input (axes) should not be of rank higher than 1. Got: ",
|
||||
rank);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void shape_infer(const Unsqueeze* 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() == 2 && output_shapes.size() == 1);
|
||||
check_unsqueeze_axes_rank(op, input_shapes[1].rank());
|
||||
const auto& arg_shape = input_shapes[0];
|
||||
auto& out_shape = output_shapes[0];
|
||||
|
||||
std::vector<int64_t> axes_val;
|
||||
const auto has_axes = get_data_as_int64<T>(1, op, axes_val, constant_data);
|
||||
|
||||
if (has_axes && arg_shape.rank().is_static()) {
|
||||
NODE_VALIDATION_CHECK(op, !axes_val.empty(), "'axes' input is mandatory");
|
||||
// Remove repeated axes on input
|
||||
std::unordered_set<int64_t> tmp(axes_val.begin(), axes_val.end());
|
||||
std::vector<int64_t> unique_axes(tmp.begin(), tmp.end());
|
||||
|
||||
const auto expanded_rank = arg_shape.rank().get_length() + unique_axes.size();
|
||||
|
||||
// Normalize then remove repeated axes after normalization.
|
||||
normalize_axes(op, expanded_rank, unique_axes);
|
||||
const std::set<int64_t> axes(unique_axes.begin(), unique_axes.end());
|
||||
|
||||
out_shape = arg_shape;
|
||||
for (const auto& axis : axes) {
|
||||
NODE_VALIDATION_CHECK(op,
|
||||
static_cast<size_t>(axis) <= out_shape.size() + 1,
|
||||
"provided 'axes' value ",
|
||||
axis,
|
||||
" is not valid.");
|
||||
// As shape not throw exception on repeated axis it has to be check if insert or append dimension.
|
||||
// This will be not required if this op has same behaviour as numpy expand_dims.
|
||||
if (static_cast<size_t>(axis) <= out_shape.size()) {
|
||||
out_shape.insert(std::next(std::begin(out_shape), axis), 1);
|
||||
} else {
|
||||
// Append dimension at end when there is difference in size of input axes and after normalization
|
||||
// e.g. input shape {2,3,4} axes_value(4,-1) then output rank is determined as 5,
|
||||
// but after final normalization and removing duplicates it points sam location in shape.
|
||||
// The numpy throws exception "repeated axis" in that case.
|
||||
out_shape.push_back(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out_shape = ov::PartialShape::dynamic();
|
||||
}
|
||||
}
|
||||
} // namespace v0
|
||||
} // namespace op
|
||||
} // namespace ov
|
||||
@@ -9,12 +9,9 @@
|
||||
#include <set>
|
||||
|
||||
#include "itt.hpp"
|
||||
#include "ngraph/builder/reshape.hpp"
|
||||
#include "ngraph/op/constant.hpp"
|
||||
#include "ngraph/op/reshape.hpp"
|
||||
#include "ngraph/op/util/op_types.hpp"
|
||||
#include "ngraph/runtime/reference/copy.hpp"
|
||||
#include "ngraph/validation_util.hpp"
|
||||
#include "unsqueeze_shape_inference.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ngraph;
|
||||
@@ -27,38 +24,13 @@ op::v0::Unsqueeze::Unsqueeze(const Output<Node>& data, const Output<Node>& axes)
|
||||
|
||||
void op::v0::Unsqueeze::validate_and_infer_types() {
|
||||
OV_OP_SCOPE(v0_Unsqueeze_validate_and_infer_types);
|
||||
const auto data = input_value(0);
|
||||
auto data_partial_shape = data.get_partial_shape();
|
||||
const auto data_rank = data_partial_shape.rank();
|
||||
|
||||
const auto axes_constant = get_constant_from_source(input_value(1));
|
||||
auto axes_pshape = get_input_partial_shape(1);
|
||||
const auto input_shapes = get_node_input_partial_shapes(*this);
|
||||
auto output_shapes = std::vector<ov::PartialShape>(1);
|
||||
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
axes_pshape.rank().compatible(0) || axes_pshape.rank().compatible(1),
|
||||
"Second input (axes) should not be of rank higher than 1. Got: ",
|
||||
axes_pshape.rank().get_length());
|
||||
shape_infer(this, input_shapes, output_shapes);
|
||||
|
||||
if (data_rank.is_dynamic() || !axes_constant) {
|
||||
set_output_type(0, get_input_element_type(0), ov::PartialShape::dynamic());
|
||||
return;
|
||||
}
|
||||
|
||||
const auto axes_values = axes_constant->cast_vector<int64_t>();
|
||||
uint64_t data_rank_value = data_partial_shape.rank().get_length();
|
||||
const int64_t expanded_rank = data_rank_value + axes_values.size();
|
||||
|
||||
NODE_VALIDATION_CHECK(this, !axes_values.empty(), "'axes' input is mandatory");
|
||||
|
||||
auto normalized_axes = normalize_axes(this->description(), axes_values, expanded_rank);
|
||||
set<int64_t> axes(begin(normalized_axes), end(normalized_axes));
|
||||
vector<Dimension> output_shape{data_partial_shape};
|
||||
for (auto axis : axes) {
|
||||
NODE_VALIDATION_CHECK(this, axis <= expanded_rank, "provided 'axes' value ", axis, " is not valid.");
|
||||
|
||||
output_shape.insert(next(begin(output_shape), axis), 1);
|
||||
}
|
||||
set_output_type(0, get_input_element_type(0), ov::PartialShape{output_shape});
|
||||
set_output_type(0, get_input_element_type(0), output_shapes[0]);
|
||||
}
|
||||
|
||||
bool op::v0::Unsqueeze::visit_attributes(AttributeVisitor& visitor) {
|
||||
@@ -82,29 +54,32 @@ bool evaluate(const HostTensorPtr& arg0, const HostTensorPtr& out) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool evaluate_unsqueeze(const HostTensorPtr& arg0, const HostTensorPtr& arg1, const HostTensorPtr& out) {
|
||||
// The evaluate cannot use shape_infer for output shape calculation as shape inference accepts
|
||||
// repeated axis and evaluate not. When shape inference will changed to be compatible with `numpy` then
|
||||
// evaluate and inference can use same function to calculate output shape. TODO for next version for this operator.
|
||||
bool evaluate_unsqueeze(const Node* node,
|
||||
const HostTensorPtr& arg0,
|
||||
const HostTensorPtr& arg1,
|
||||
const HostTensorPtr& out) {
|
||||
auto element_type = arg0->get_element_type();
|
||||
out->set_element_type(element_type);
|
||||
|
||||
auto data_shape = arg0->get_shape();
|
||||
int64_t data_rank = static_cast<int64_t>(data_shape.size());
|
||||
auto axes_shape = arg1->get_shape();
|
||||
NGRAPH_CHECK(axes_shape.size() == 1 || axes_shape.empty(),
|
||||
"Axes to add must be a scalar or 1D tensor with 1 element");
|
||||
const auto& axes_shape = arg1->get_shape();
|
||||
ov::op::v0::check_unsqueeze_axes_rank(node, Rank(axes_shape.size()));
|
||||
|
||||
const auto& data_shape = arg0->get_shape();
|
||||
const auto out_rank = static_cast<int64_t>(data_shape.size() + shape_size(axes_shape));
|
||||
|
||||
// Get axes and normalize
|
||||
auto axes = read_index_vector(arg1);
|
||||
normalize_axes(node, out_rank, axes);
|
||||
|
||||
// Sort in increasing order
|
||||
std::set<int64_t> axes_set(axes.begin(), axes.end());
|
||||
NGRAPH_CHECK(axes.size() == axes_set.size(), "Axes has duplicate axis.");
|
||||
|
||||
auto out_shape = data_shape;
|
||||
int64_t out_rank = data_rank + static_cast<int64_t>(shape_size(axes_shape));
|
||||
// Get axes
|
||||
vector<int64_t> axes = read_index_vector(arg1);
|
||||
// Normalize axes
|
||||
std::transform(axes.begin(), axes.end(), axes.begin(), [out_rank](int64_t i) -> int64_t {
|
||||
return i < 0 ? out_rank + i : i;
|
||||
});
|
||||
// Sort in increasing order
|
||||
std::set<int64_t, less<int64_t>> axes_set(axes.begin(), axes.end());
|
||||
NGRAPH_CHECK(axes.size() == axes_set.size(), "Axes has duplicate axis.");
|
||||
for (int64_t axis : axes_set) {
|
||||
NGRAPH_CHECK(axis >= 0 && axis < out_rank, "Axis is out of bounds: ", axis);
|
||||
out_shape.insert(out_shape.begin() + axis, 1);
|
||||
}
|
||||
out->set_shape(out_shape);
|
||||
@@ -130,7 +105,7 @@ bool op::v0::Unsqueeze::evaluate(const HostTensorVector& outputs, const HostTens
|
||||
OV_OP_SCOPE(v0_Unsqueeze_evaluate);
|
||||
NGRAPH_CHECK(validate_host_tensor_vector(inputs, 2));
|
||||
NGRAPH_CHECK(validate_host_tensor_vector(outputs, 1));
|
||||
return unsqueeze::evaluate_unsqueeze(inputs[0], inputs[1], outputs[0]);
|
||||
return unsqueeze::evaluate_unsqueeze(this, inputs[0], inputs[1], outputs[0]);
|
||||
}
|
||||
|
||||
bool op::v0::Unsqueeze::has_evaluate() const {
|
||||
|
||||
@@ -850,28 +850,40 @@ PartialShape ngraph::infer_slice_shape(const Node* node,
|
||||
return dim;
|
||||
}
|
||||
|
||||
namespace {
|
||||
const auto normalize_axis_to = [](const int64_t& tensor_rank) {
|
||||
return [&tensor_rank](int64_t& axis) {
|
||||
if (axis < 0) {
|
||||
axis += tensor_rank;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
std::string normalize_axis_error_msg(const int64_t& axis, const int64_t& lower, const int64_t& upper) {
|
||||
return std::string(" Parameter axis ")
|
||||
.append(to_string(axis))
|
||||
.append(" out of the tensor rank range [")
|
||||
.append(to_string(lower))
|
||||
.append(", ")
|
||||
.append(to_string(upper))
|
||||
.append("].");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ov::normalize_axes(const Node* node, const int64_t& tensor_rank, std::vector<int64_t>& axes) {
|
||||
const auto& min_value = -tensor_rank;
|
||||
const auto& max_value = tensor_rank ? (tensor_rank - 1) : 0;
|
||||
transform(axes.begin(), axes.end(), axes.begin(), [=](int64_t& axis) {
|
||||
NODE_VALIDATION_CHECK(node,
|
||||
((axis >= min_value) && (axis <= max_value)),
|
||||
" Parameter axis ",
|
||||
axis,
|
||||
" out of the tensor rank range [",
|
||||
min_value,
|
||||
", ",
|
||||
max_value,
|
||||
"].");
|
||||
return axis < 0 ? axis + tensor_rank : axis;
|
||||
});
|
||||
const auto axis_checker = cmp::Between<int64_t, cmp::BOTH>(-tensor_rank, tensor_rank ? (tensor_rank - 1) : 0);
|
||||
const auto invalid_axis = std::find_if_not(axes.cbegin(), axes.cend(), axis_checker);
|
||||
NODE_VALIDATION_CHECK(node,
|
||||
invalid_axis == axes.cend(),
|
||||
normalize_axis_error_msg(*invalid_axis, axis_checker.lower(), axis_checker.upper()));
|
||||
std::for_each(axes.begin(), axes.end(), normalize_axis_to(tensor_rank));
|
||||
}
|
||||
|
||||
std::vector<size_t> ov::normalize_axes(const std::string& node_description,
|
||||
const std::vector<int64_t>& axes,
|
||||
const Rank& tensor_rank) {
|
||||
std::vector<size_t> new_axes;
|
||||
|
||||
new_axes.reserve(axes.size());
|
||||
for (const auto& axis : axes) {
|
||||
new_axes.push_back(normalize_axis(node_description, axis, tensor_rank));
|
||||
}
|
||||
@@ -908,7 +920,7 @@ int64_t ov::normalize_axis(const Node* node,
|
||||
std::uint64_t tensor_rank,
|
||||
std::int64_t axis_range_min,
|
||||
std::int64_t axis_range_max) {
|
||||
return ngraph::normalize_axis(node->description(), axis, tensor_rank, axis_range_min, axis_range_max);
|
||||
return normalize_axis(node->description(), axis, tensor_rank, axis_range_min, axis_range_max);
|
||||
}
|
||||
|
||||
int64_t ov::normalize_axis(const std::string& node_description,
|
||||
@@ -917,21 +929,11 @@ int64_t ov::normalize_axis(const std::string& node_description,
|
||||
std::int64_t axis_range_min,
|
||||
std::int64_t axis_range_max) {
|
||||
// Accepted range of value for axis is [axis_range_min, axis_range_max].
|
||||
NGRAPH_CHECK(((axis >= axis_range_min) && (axis <= axis_range_max)),
|
||||
node_description,
|
||||
" Parameter axis ",
|
||||
axis,
|
||||
" out of the tensor rank range [",
|
||||
axis_range_min,
|
||||
", ",
|
||||
axis_range_max,
|
||||
"].");
|
||||
|
||||
if (axis < 0) {
|
||||
axis = axis + tensor_rank;
|
||||
}
|
||||
|
||||
return int64_t(axis);
|
||||
OPENVINO_ASSERT((axis_range_min <= axis) && (axis <= axis_range_max),
|
||||
node_description,
|
||||
normalize_axis_error_msg(axis, axis_range_min, axis_range_max));
|
||||
normalize_axis_to(tensor_rank)(axis);
|
||||
return axis;
|
||||
}
|
||||
|
||||
void ngraph::opset1::infer_conv_backprop_auto_padding(const Shape& input_data_shape,
|
||||
@@ -1652,3 +1654,18 @@ bool ov::is_valid_axes_order(const std::vector<int64_t>& axes_order, const size_
|
||||
return (std::unordered_set<size_t>(axes_order.cbegin(), axes_order.cend()).size() == size) &&
|
||||
std::all_of(axes_order.cbegin(), axes_order.cend(), ov::cmp::Between<int64_t, ov::cmp::LOWER>(0, size));
|
||||
}
|
||||
|
||||
std::vector<PartialShape> ov::get_node_input_partial_shapes(const ov::Node& node) {
|
||||
std::vector<PartialShape> out;
|
||||
out.reserve(node.get_input_size());
|
||||
for (size_t i = 0; i < node.get_input_size(); ++i) {
|
||||
out.push_back(node.get_input_partial_shape(i));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool ov::is_rank_compatible_any_of(const ov::Rank& rank, const std::vector<Rank>& ranks) {
|
||||
return std::any_of(ranks.cbegin(), ranks.cend(), [&rank](const Rank& r) {
|
||||
return rank.compatible(r);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,42 +2,28 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include <dimension_tracker.hpp>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "dimension_tracker.hpp"
|
||||
#include "gmock/gmock.h"
|
||||
#include "ngraph/ngraph.hpp"
|
||||
#include "sequnce_generator.hpp"
|
||||
#include "util/type_prop.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ngraph;
|
||||
using namespace testing;
|
||||
|
||||
TEST(type_prop, unsqueeze) {
|
||||
auto param = make_shared<op::Parameter>(element::f32, Shape{4, 1, 4, 1, 8});
|
||||
auto axes_node = make_shared<ngraph::op::Constant>(element::u64, Shape{2}, vector<int64_t>{1, 2});
|
||||
auto axes_node = make_shared<op::Constant>(element::u64, Shape{2}, vector<int64_t>{1, 2});
|
||||
auto unsqueeze = make_shared<op::v0::Unsqueeze>(param, axes_node);
|
||||
|
||||
ASSERT_EQ(unsqueeze->get_element_type(), element::f32);
|
||||
ASSERT_EQ(unsqueeze->get_shape(), (Shape{4, 1, 1, 1, 4, 1, 8}));
|
||||
}
|
||||
|
||||
TEST(type_prop, unsqueeze_dynamic) {
|
||||
auto param = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(5));
|
||||
auto axes_node = make_shared<ngraph::op::Constant>(element::u64, Shape{2}, vector<int64_t>{1, 2});
|
||||
auto unsqueeze = make_shared<op::v0::Unsqueeze>(param, axes_node);
|
||||
|
||||
ASSERT_EQ(unsqueeze->get_element_type(), element::f32);
|
||||
EXPECT_TRUE(unsqueeze->get_output_partial_shape(0).same_scheme(PartialShape{Dimension::dynamic(),
|
||||
1,
|
||||
1,
|
||||
Dimension::dynamic(),
|
||||
Dimension::dynamic(),
|
||||
Dimension::dynamic(),
|
||||
Dimension::dynamic()}));
|
||||
EXPECT_EQ(unsqueeze->get_element_type(), element::f32);
|
||||
EXPECT_EQ(unsqueeze->get_shape(), (Shape{4, 1, 1, 1, 4, 1, 8}));
|
||||
}
|
||||
|
||||
TEST(type_prop, unsqueeze_incorrect_axes_shape) {
|
||||
auto param = make_shared<op::Parameter>(element::f32, Shape{4, 1, 4, 1, 8});
|
||||
auto axes_node = make_shared<ngraph::op::Constant>(element::u64, Shape{1, 1, 1}, vector<int64_t>{1});
|
||||
auto axes_node = make_shared<op::Constant>(element::u64, Shape{1, 1, 1}, 1);
|
||||
|
||||
try {
|
||||
auto unsqueeze = make_shared<op::v0::Unsqueeze>(param, axes_node);
|
||||
@@ -49,9 +35,41 @@ TEST(type_prop, unsqueeze_incorrect_axes_shape) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(type_prop, unsqueeze_positive_axis_gt_ouput_rank) {
|
||||
constexpr int64_t bad_axis = 6;
|
||||
auto param = make_shared<op::Parameter>(element::f32, Shape{4, 1, 4, 1, 8});
|
||||
auto axes_node = make_shared<op::Constant>(element::u64, Shape{1}, bad_axis);
|
||||
|
||||
try {
|
||||
auto unsqueeze = make_shared<op::v0::Unsqueeze>(param, axes_node);
|
||||
FAIL() << "Unsqueeze axes invalid rank not detected";
|
||||
} catch (const NodeValidationFailure& error) {
|
||||
const auto exp_msg = "Parameter axis " + std::to_string(bad_axis) + " out of the tensor rank range";
|
||||
EXPECT_HAS_SUBSTRING(error.what(), exp_msg);
|
||||
} catch (...) {
|
||||
FAIL() << "Deduced type check failed for unexpected reason";
|
||||
}
|
||||
}
|
||||
|
||||
TEST(type_prop, unsqueeze_negative_axis_gt_ouput_rank) {
|
||||
constexpr int64_t bad_axis = -7;
|
||||
auto param = make_shared<op::Parameter>(element::f32, Shape{4, 1, 4, 1, 8});
|
||||
auto axes_node = make_shared<op::Constant>(element::u64, Shape{1}, bad_axis);
|
||||
|
||||
try {
|
||||
auto unsqueeze = make_shared<op::v0::Unsqueeze>(param, axes_node);
|
||||
FAIL() << "Unsqueeze axes invalid rank not detected";
|
||||
} catch (const NodeValidationFailure& error) {
|
||||
const auto exp_msg = "Parameter axis " + std::to_string(bad_axis) + " out of the tensor rank range";
|
||||
EXPECT_HAS_SUBSTRING(error.what(), exp_msg);
|
||||
} catch (...) {
|
||||
FAIL() << "Deduced type check failed for unexpected reason";
|
||||
}
|
||||
}
|
||||
|
||||
TEST(type_prop, unsqueeze_empty_axes) {
|
||||
auto param = make_shared<op::Parameter>(element::f32, Shape{4, 1, 4, 1, 8});
|
||||
auto axes_node = make_shared<ngraph::op::Constant>(element::u64, Shape{0}, vector<int64_t>{});
|
||||
auto axes_node = make_shared<op::Constant>(element::u64, Shape{0}, vector<int64_t>{});
|
||||
try {
|
||||
auto unsqueeze = make_shared<op::v0::Unsqueeze>(param, axes_node);
|
||||
FAIL() << "Unsqueeze axes empty not detected";
|
||||
@@ -62,35 +80,232 @@ TEST(type_prop, unsqueeze_empty_axes) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(type_prop, unsqueeze_dynamic_axes) {
|
||||
auto param = make_shared<op::Parameter>(element::f32, Shape{4, 1, 4, 1, 8});
|
||||
auto axes_node = make_shared<ngraph::op::Parameter>(element::u64, PartialShape::dynamic());
|
||||
class UnsqueezeTestCommon : public Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
param = std::make_shared<op::Parameter>(element::f32, p_shape);
|
||||
}
|
||||
|
||||
auto unsqueeze = make_shared<op::v0::Unsqueeze>(param, axes_node);
|
||||
ASSERT_EQ(unsqueeze->get_element_type(), element::f32);
|
||||
ASSERT_EQ(unsqueeze->get_output_partial_shape(0), PartialShape::dynamic());
|
||||
PartialShape p_shape, exp_shape;
|
||||
std::shared_ptr<op::Parameter> param;
|
||||
};
|
||||
|
||||
using TypePropTestParam = std::tuple<PartialShape, std::vector<int64_t>, PartialShape>;
|
||||
|
||||
class UnsqueezeTest : public WithParamInterface<TypePropTestParam>, public UnsqueezeTestCommon {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
std::tie(p_shape, axes, exp_shape) = GetParam();
|
||||
UnsqueezeTestCommon::SetUp();
|
||||
}
|
||||
|
||||
std::pair<std::vector<size_t>, std::vector<size_t>> make_in_exp_labels() const {
|
||||
std::vector<size_t> in_labels;
|
||||
std::generate_n(std::back_inserter(in_labels), p_shape.size(), ov::SeqGen<size_t>(1));
|
||||
|
||||
auto unique_axes = std::set<int64_t>(axes.begin(), axes.end());
|
||||
auto out_rank = unique_axes.size() + p_shape.size();
|
||||
|
||||
std::set<int64_t> no_label_axes;
|
||||
for (const auto& axis : axes) {
|
||||
no_label_axes.insert(axis < 0 ? axis + out_rank : axis);
|
||||
}
|
||||
|
||||
auto exp_labels = in_labels;
|
||||
for (const auto& axis : no_label_axes) {
|
||||
if (axis < exp_labels.size()) {
|
||||
exp_labels.insert(exp_labels.begin() + axis, ov::no_label);
|
||||
} else {
|
||||
exp_labels.push_back(ov::no_label);
|
||||
}
|
||||
}
|
||||
return {in_labels, exp_labels};
|
||||
}
|
||||
|
||||
std::vector<int64_t> axes;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
type_prop_expand_dynamic_shape,
|
||||
UnsqueezeTest,
|
||||
Values(std::make_tuple(PartialShape::dynamic(), std::vector<int64_t>{-1, -5}, PartialShape::dynamic()),
|
||||
std::make_tuple(PartialShape::dynamic(3),
|
||||
std::vector<int64_t>{-1, -5},
|
||||
PartialShape{1, Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic(), 1}),
|
||||
std::make_tuple(PartialShape::dynamic(5),
|
||||
std::vector<int64_t>{1, 2},
|
||||
PartialShape{Dimension::dynamic(),
|
||||
1,
|
||||
1,
|
||||
Dimension::dynamic(),
|
||||
Dimension::dynamic(),
|
||||
Dimension::dynamic(),
|
||||
Dimension::dynamic()}),
|
||||
std::make_tuple(PartialShape{Dimension(3, 5), Dimension(-1, 2)},
|
||||
std::vector<int64_t>{0},
|
||||
PartialShape{1, Dimension(3, 5), Dimension(-1, 2)}),
|
||||
std::make_tuple(PartialShape{2, Dimension::dynamic(), 4, Dimension(2, 7), 6},
|
||||
std::vector<int64_t>{3, 1, 5},
|
||||
PartialShape{2, 1, Dimension::dynamic(), 1, 4, 1, Dimension(2, 7), 6})),
|
||||
PrintToStringParamName());
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
type_prop_expand_shape_at_edges,
|
||||
UnsqueezeTest,
|
||||
Values(std::make_tuple(PartialShape{}, std::vector<int64_t>{0}, PartialShape{1}),
|
||||
std::make_tuple(PartialShape{-1}, std::vector<int64_t>{-1}, PartialShape{-1, 1}),
|
||||
std::make_tuple(PartialShape{0}, std::vector<int64_t>{-1}, PartialShape{0, 1}),
|
||||
std::make_tuple(PartialShape{5}, std::vector<int64_t>{0}, PartialShape{1, 5}),
|
||||
std::make_tuple(PartialShape{5}, std::vector<int64_t>{-2}, PartialShape{1, 5}),
|
||||
std::make_tuple(PartialShape{5}, std::vector<int64_t>{1}, PartialShape{5, 1}),
|
||||
std::make_tuple(PartialShape{5}, std::vector<int64_t>{-1}, PartialShape{5, 1}),
|
||||
std::make_tuple(PartialShape{2, 3}, std::vector<int64_t>{0}, PartialShape{1, 2, 3}),
|
||||
std::make_tuple(PartialShape{2, 3, 6}, std::vector<int64_t>{0, -1}, PartialShape{1, 2, 3, 6, 1}),
|
||||
std::make_tuple(PartialShape{2, 3, 6}, std::vector<int64_t>{-1, -5}, PartialShape{1, 2, 3, 6, 1})),
|
||||
PrintToStringParamName());
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
type_prop_expand_shape_inside,
|
||||
UnsqueezeTest,
|
||||
Values(
|
||||
std::make_tuple(PartialShape{5}, std::vector<int64_t>{1, -1}, PartialShape{5, 1, 1}),
|
||||
std::make_tuple(PartialShape{5, 4}, std::vector<int64_t>{1}, PartialShape{5, 1, 4}),
|
||||
std::make_tuple(PartialShape{8, 2}, std::vector<int64_t>{-2}, PartialShape{8, 1, 2}),
|
||||
std::make_tuple(PartialShape{3, 4}, std::vector<int64_t>{1, -2}, PartialShape{3, 1, 1, 4}),
|
||||
std::make_tuple(PartialShape{2, 3, 4}, std::vector<int64_t>{1, 3}, PartialShape{2, 1, 3, 1, 4}),
|
||||
std::make_tuple(PartialShape{3, 6, 7, 3}, std::vector<int64_t>{-2, 3, 1}, PartialShape{3, 1, 6, 1, 7, 1, 3})),
|
||||
PrintToStringParamName());
|
||||
|
||||
// These are cases with repeated axis which should throw exception as numpy.
|
||||
// Current implementation allow this in shape inference by removing duplicates.
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
type_prop_repeated_axis,
|
||||
UnsqueezeTest,
|
||||
Values(std::make_tuple(PartialShape{5}, std::vector<int64_t>{1, 1}, PartialShape{5, 1}),
|
||||
std::make_tuple(PartialShape{5}, std::vector<int64_t>{-1, -1}, PartialShape{5, 1}),
|
||||
std::make_tuple(PartialShape{5}, std::vector<int64_t>{1, -1, 1}, PartialShape{5, 1, 1}),
|
||||
std::make_tuple(PartialShape{2, 3, 4}, std::vector<int64_t>{1, 1}, PartialShape{2, 1, 3, 4}),
|
||||
std::make_tuple(PartialShape{2, 3, 4}, std::vector<int64_t>{1, 1, 3, 3}, PartialShape{2, 1, 3, 1, 4}),
|
||||
std::make_tuple(PartialShape{2, 3, 4}, std::vector<int64_t>{1, -1, 1}, PartialShape{2, 1, 3, 4, 1}),
|
||||
std::make_tuple(PartialShape{2, 3, 4}, std::vector<int64_t>{1, -1}, PartialShape{2, 1, 3, 4, 1}),
|
||||
std::make_tuple(PartialShape{2, 3, 4}, std::vector<int64_t>{-1, -1}, PartialShape{2, 3, 4, 1}),
|
||||
std::make_tuple(PartialShape{2, 3, 4}, std::vector<int64_t>{-1, -1, 1}, PartialShape{2, 1, 3, 4, 1}),
|
||||
std::make_tuple(PartialShape{2, 3, 4}, std::vector<int64_t>{-1, 4, -1}, PartialShape{2, 3, 4, 1}),
|
||||
std::make_tuple(PartialShape{2, 3, 4}, std::vector<int64_t>{-1, 4}, PartialShape{2, 3, 4, 1}),
|
||||
std::make_tuple(PartialShape{2, 3, 4}, std::vector<int64_t>{4, -1}, PartialShape{2, 3, 4, 1}),
|
||||
std::make_tuple(PartialShape{2, 3, 4},
|
||||
std::vector<int64_t>{4, 4, -2, -2, -1, -1},
|
||||
PartialShape{2, 3, 4, 1, 1})),
|
||||
PrintToStringParamName());
|
||||
|
||||
TEST_P(UnsqueezeTest, dimension_propagation_const_axis_i8) {
|
||||
const auto axes_node = std::make_shared<op::Constant>(element::i8, Shape{axes.size()}, axes);
|
||||
const auto unsqueeze = std::make_shared<op::v0::Unsqueeze>(param, axes_node);
|
||||
|
||||
EXPECT_EQ(unsqueeze->get_element_type(), element::f32);
|
||||
EXPECT_EQ(unsqueeze->get_output_partial_shape(0), exp_shape);
|
||||
}
|
||||
|
||||
TEST(type_prop, unsqueeze_dynamic_value_and_label_propagation) {
|
||||
Dimension marked_0 = Dimension(3);
|
||||
ov::DimensionTracker::set_label(marked_0, 10);
|
||||
PartialShape target_0 = PartialShape{marked_0, 4};
|
||||
TEST_P(UnsqueezeTest, dimension_propagation_const_axis_i32) {
|
||||
const auto axes_node = std::make_shared<op::Constant>(element::i32, Shape{axes.size()}, axes);
|
||||
const auto unsqueeze = std::make_shared<op::v0::Unsqueeze>(param, axes_node);
|
||||
|
||||
auto param = std::make_shared<op::Parameter>(element::f32, Shape{1});
|
||||
auto param_0 = std::make_shared<op::Parameter>(element::f32, target_0);
|
||||
auto shape_0 = std::make_shared<op::ShapeOf>(param_0);
|
||||
EXPECT_EQ(unsqueeze->get_element_type(), element::f32);
|
||||
EXPECT_EQ(unsqueeze->get_output_partial_shape(0), exp_shape);
|
||||
}
|
||||
|
||||
const auto& et = element::i64;
|
||||
std::vector<int64_t> zero{0};
|
||||
const auto indices = std::make_shared<op::v0::Constant>(et, Shape{}, zero);
|
||||
TEST_P(UnsqueezeTest, dimension_propagation_dynamic_axis_shape) {
|
||||
const auto axes_node = std::make_shared<op::Parameter>(element::i64, PartialShape::dynamic());
|
||||
const auto unsqueeze = std::make_shared<op::v0::Unsqueeze>(param, axes_node);
|
||||
|
||||
EXPECT_EQ(unsqueeze->get_element_type(), element::f32);
|
||||
EXPECT_EQ(unsqueeze->get_output_partial_shape(0), PartialShape::dynamic());
|
||||
}
|
||||
|
||||
TEST_P(UnsqueezeTest, dimension_propagation_static_rank_dynamic_dim_axis_shape) {
|
||||
const auto axes_node = std::make_shared<op::Parameter>(element::u32, PartialShape{Dimension(2, 6)});
|
||||
const auto unsqueeze = std::make_shared<op::v0::Unsqueeze>(param, axes_node);
|
||||
|
||||
EXPECT_EQ(unsqueeze->get_element_type(), element::f32);
|
||||
EXPECT_EQ(unsqueeze->get_output_partial_shape(0), PartialShape::dynamic());
|
||||
}
|
||||
|
||||
TEST_P(UnsqueezeTest, use_default_ctor) {
|
||||
const auto axes_node = std::make_shared<op::Constant>(element::i32, Shape{axes.size()}, axes);
|
||||
|
||||
const auto unsqueeze = make_shared<op::Unsqueeze>();
|
||||
unsqueeze->set_arguments(NodeVector{param, axes_node});
|
||||
unsqueeze->validate_and_infer_types();
|
||||
|
||||
EXPECT_EQ(unsqueeze->get_output_element_type(0), element::f32);
|
||||
EXPECT_EQ(unsqueeze->get_output_partial_shape(0), exp_shape);
|
||||
}
|
||||
|
||||
TEST_P(UnsqueezeTest, labels_propagation) {
|
||||
if (p_shape.rank().is_dynamic()) {
|
||||
GTEST_SKIP() << "No dimension to set label";
|
||||
}
|
||||
std::vector<size_t> in_labels, exp_labels;
|
||||
std::tie(in_labels, exp_labels) = make_in_exp_labels();
|
||||
|
||||
set_shape_labels(p_shape, in_labels);
|
||||
param = make_shared<op::Parameter>(element::f32, p_shape);
|
||||
|
||||
const auto axes_node = std::make_shared<op::Constant>(element::i32, Shape{axes.size()}, axes);
|
||||
const auto unsqueeze = std::make_shared<op::v0::Unsqueeze>(param, axes_node);
|
||||
|
||||
EXPECT_EQ(get_shape_labels(unsqueeze->get_output_partial_shape(0)), exp_labels);
|
||||
}
|
||||
|
||||
using BoundTestParam = std::tuple<PartialShape, PartialShape>;
|
||||
|
||||
class UnsqueezeBoundTest : public WithParamInterface<BoundTestParam>, public UnsqueezeTestCommon {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
std::tie(p_shape, exp_shape) = GetParam();
|
||||
UnsqueezeTestCommon::SetUp();
|
||||
}
|
||||
|
||||
std::vector<size_t> in_labels;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
type_prop_bounds_propagate,
|
||||
UnsqueezeBoundTest,
|
||||
Values(std::make_tuple(PartialShape::dynamic(2), PartialShape::dynamic(1)),
|
||||
std::make_tuple(PartialShape{Dimension(-1)}, PartialShape{Dimension(-1)}),
|
||||
std::make_tuple(PartialShape{Dimension::dynamic(), 8}, PartialShape{Dimension::dynamic()}),
|
||||
std::make_tuple(PartialShape{Dimension(2, 5), Dimension::dynamic()}, PartialShape{Dimension(2, 5)}),
|
||||
std::make_tuple(PartialShape{Dimension(2, -1), Dimension::dynamic()}, PartialShape::dynamic(1)),
|
||||
std::make_tuple(PartialShape{Dimension(-1, 3), Dimension::dynamic()}, PartialShape{Dimension(-1, 3)}),
|
||||
std::make_tuple(PartialShape{5}, PartialShape{5}),
|
||||
std::make_tuple(PartialShape{2, 6}, PartialShape{2})),
|
||||
PrintToStringParamName());
|
||||
|
||||
/**
|
||||
* \brief Check label and dynamic value propagation.
|
||||
*
|
||||
* Test use evaluate label, lower/upper.
|
||||
*/
|
||||
TEST_P(UnsqueezeBoundTest, propagate_label_and_dynamic_value) {
|
||||
PartialShape labeled_shape = PartialShape{p_shape};
|
||||
|
||||
std::generate_n(std::back_inserter(in_labels), labeled_shape.size(), ov::SeqGen<size_t>(1));
|
||||
set_shape_labels(labeled_shape, in_labels);
|
||||
|
||||
constexpr auto et = element::i64;
|
||||
const auto labeled_param = std::make_shared<op::Parameter>(et, labeled_shape);
|
||||
const auto labeled_shape_of = std::make_shared<op::ShapeOf>(labeled_param);
|
||||
|
||||
const auto zero = std::vector<int64_t>{0};
|
||||
const auto axis = std::make_shared<op::v0::Constant>(et, Shape{}, zero);
|
||||
const auto gather = std::make_shared<op::v7::Gather>(shape_0, indices, axis);
|
||||
|
||||
const auto indices = std::make_shared<op::v0::Constant>(et, Shape{}, zero);
|
||||
const auto gather = std::make_shared<op::v7::Gather>(labeled_shape_of, indices, axis);
|
||||
const auto unsqueeze = std::make_shared<op::v0::Unsqueeze>(gather, axis);
|
||||
|
||||
auto bc = std::make_shared<op::v1::Broadcast>(param, unsqueeze);
|
||||
ASSERT_EQ(bc->get_shape(), (Shape{3}));
|
||||
const auto bc = std::make_shared<op::v1::Broadcast>(param, unsqueeze);
|
||||
|
||||
const auto& output_shape = bc->get_output_partial_shape(0);
|
||||
ASSERT_EQ(ov::DimensionTracker::get_label(output_shape[0]), 10);
|
||||
EXPECT_EQ(bc->get_output_partial_shape(0), exp_shape);
|
||||
const auto labels = get_shape_labels(bc->get_output_partial_shape(0));
|
||||
EXPECT_THAT(labels, ElementsAre(in_labels.front()));
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
#include "tile_shape_inference.hpp"
|
||||
#include "topk_shape_inference.hpp"
|
||||
#include "transpose_shape_inference.hpp"
|
||||
#include "unsqueeze_shape_inference.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "variadic_split_shape_inference.hpp"
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "openvino/op/parameter.hpp"
|
||||
#include "openvino/pass/graph_rewrite.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "utils/shape_inference/static_shape.hpp"
|
||||
|
||||
using namespace ov;
|
||||
@@ -18,7 +19,8 @@ using TestParams = std::tuple<int64_t, // concatenation axis
|
||||
StaticShape // Expected shape
|
||||
>;
|
||||
|
||||
class ConcatStaticShapeInferenceTest : public TestWithParam<TestParams> {
|
||||
class ConcatStaticShapeInferenceTest : public OpStaticShapeInferenceTest<op::v0::Concat>,
|
||||
public WithParamInterface<TestParams> {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
std::tie(concat_axis, input_shapes, exp_shape) = GetParam();
|
||||
@@ -26,15 +28,14 @@ protected:
|
||||
for (const auto& in : input_shapes) {
|
||||
params.make<op::v0::Parameter>(element::f32, in.get_shape());
|
||||
}
|
||||
concat = std::make_shared<op::v0::Concat>(params.get(), concat_axis);
|
||||
op = std::make_shared<op::v0::Concat>(params.get(), concat_axis);
|
||||
|
||||
output_shapes = ShapeVector(1);
|
||||
}
|
||||
|
||||
int64_t concat_axis;
|
||||
StaticShape exp_shape;
|
||||
ShapeVector input_shapes;
|
||||
|
||||
pass::NodeRegistry params{};
|
||||
std::shared_ptr<op::v0::Concat> concat;
|
||||
};
|
||||
|
||||
/** \brief Concatenate simple 1d shapes. */
|
||||
@@ -66,9 +67,7 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
|
||||
/** \brief Check shape_infer for concat op on static shapes. */
|
||||
TEST_P(ConcatStaticShapeInferenceTest, concat_static) {
|
||||
auto output_shapes = ShapeVector(1);
|
||||
|
||||
shape_infer(concat.get(), input_shapes, output_shapes);
|
||||
shape_infer(op.get(), input_shapes, output_shapes);
|
||||
|
||||
ASSERT_EQ(output_shapes.front(), exp_shape);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
// Copyright (C) 2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "openvino/op/constant.hpp"
|
||||
#include "openvino/op/parameter.hpp"
|
||||
#include "openvino/op/unsqueeze.hpp"
|
||||
#include "unsqueeze_shape_inference.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
using namespace ov;
|
||||
using namespace ov::intel_cpu;
|
||||
using namespace testing;
|
||||
|
||||
class UnsqueezeStaticShapeInferenceAssertTest : public OpStaticShapeInferenceTest<op::v0::Unsqueeze> {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
output_shapes = ShapeVector(1);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(UnsqueezeStaticShapeInferenceAssertTest, no_axes) {
|
||||
const auto arg = std::make_shared<op::v0::Parameter>(element::f64, PartialShape{-1, -1});
|
||||
const auto axes = std::make_shared<op::v0::Parameter>(element::i64, PartialShape{1});
|
||||
op = std::make_shared<op::v0::Unsqueeze>(arg, axes);
|
||||
|
||||
input_shapes = ShapeVector{{5, 6}, axes->get_shape()};
|
||||
|
||||
try {
|
||||
shape_infer(op.get(), input_shapes, output_shapes);
|
||||
FAIL() << "Axes nullptr not detected";
|
||||
} catch (const NodeValidationFailure& error) {
|
||||
EXPECT_THAT(error.what(), HasSubstr("Check 'constant != nullptr'"));
|
||||
} catch (...) {
|
||||
FAIL() << "Deduced type check failed for unexpected reason";
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(UnsqueezeStaticShapeInferenceAssertTest, empty_axes) {
|
||||
const auto arg = std::make_shared<op::v0::Parameter>(element::f64, Shape{5, 6});
|
||||
const auto axes = std::make_shared<op::v0::Constant>(element::i64, Shape{0}, std::vector<int64_t>{});
|
||||
|
||||
try {
|
||||
op = std::make_shared<op::v0::Unsqueeze>(arg, axes);
|
||||
FAIL() << "Empty axes not detected";
|
||||
} catch (const NodeValidationFailure& error) {
|
||||
EXPECT_THAT(error.what(), HasSubstr("'axes' input is mandatory"));
|
||||
} catch (...) {
|
||||
FAIL() << "Deduced type check failed for unexpected reason";
|
||||
}
|
||||
}
|
||||
|
||||
using TestParams = std::tuple<ShapeVector, // Input shapes
|
||||
std::vector<int64_t>, // Unsqueeze axes
|
||||
StaticShape // Expected shape
|
||||
>;
|
||||
|
||||
class UnsqueezeStaticShapeInferenceTest : public UnsqueezeStaticShapeInferenceAssertTest,
|
||||
public WithParamInterface<TestParams> {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
UnsqueezeStaticShapeInferenceAssertTest::SetUp();
|
||||
std::tie(input_shapes, axes, exp_shape) = GetParam();
|
||||
|
||||
output_shapes = ShapeVector(1);
|
||||
arg = std::make_shared<op::v0::Parameter>(element::f32, input_shapes.front().get_shape());
|
||||
}
|
||||
|
||||
std::vector<int64_t> axes;
|
||||
std::shared_ptr<op::v0::Parameter> arg;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(1d_shapes,
|
||||
UnsqueezeStaticShapeInferenceTest,
|
||||
Values(make_tuple(ShapeVector{{0}, {1}}, std::vector<int64_t>{-1}, StaticShape({0, 1})),
|
||||
make_tuple(ShapeVector{{0}, {1}}, std::vector<int64_t>{0}, StaticShape({1, 0})),
|
||||
make_tuple(ShapeVector{{1}, {1}}, std::vector<int64_t>{1}, StaticShape({1, 1})),
|
||||
make_tuple(ShapeVector{{2}, {1}}, std::vector<int64_t>{0}, StaticShape({1, 2})),
|
||||
make_tuple(ShapeVector{{2}, {1}}, std::vector<int64_t>{1}, StaticShape({2, 1})),
|
||||
make_tuple(ShapeVector{{2}, {1}}, std::vector<int64_t>{-1}, StaticShape({2, 1})),
|
||||
make_tuple(ShapeVector{{2}, {1}}, std::vector<int64_t>{-2}, StaticShape({1, 2}))),
|
||||
PrintToStringParamName());
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
multi_dim_shapes,
|
||||
UnsqueezeStaticShapeInferenceTest,
|
||||
Values(make_tuple(ShapeVector{{2, 3}, {2}}, std::vector<int64_t>{0, 3}, StaticShape({1, 2, 3, 1})),
|
||||
make_tuple(ShapeVector{{2, 4}, {2}}, std::vector<int64_t>{2, 1}, StaticShape({2, 1, 1, 4})),
|
||||
make_tuple(ShapeVector{{3, 2}, {3}}, std::vector<int64_t>{0, 2, 4}, StaticShape({1, 3, 1, 2, 1})),
|
||||
make_tuple(ShapeVector{{3, 2}, {3}}, std::vector<int64_t>{4, 2, 0}, StaticShape({1, 3, 1, 2, 1})),
|
||||
make_tuple(ShapeVector{{3, 2}, {3}}, std::vector<int64_t>{2, 0, 4}, StaticShape({1, 3, 1, 2, 1})),
|
||||
make_tuple(ShapeVector{{10, 0, 3}, {4}},
|
||||
std::vector<int64_t>{1, -1, 3, -2},
|
||||
StaticShape({10, 1, 0, 1, 3, 1, 1})),
|
||||
make_tuple(ShapeVector{{2, 6, 7, 8, 3}, {1}}, std::vector<int64_t>{0}, StaticShape({1, 2, 6, 7, 8, 3}))),
|
||||
PrintToStringParamName());
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
multi_dim_shapes_repeated_axis,
|
||||
UnsqueezeStaticShapeInferenceTest,
|
||||
Values(
|
||||
make_tuple(ShapeVector{{2, 3}, {2}}, std::vector<int64_t>{1, 1}, StaticShape({2, 1, 3})),
|
||||
make_tuple(ShapeVector{{3, 2}, {3}}, std::vector<int64_t>{1, -1, 1}, StaticShape({3, 1, 2, 1})),
|
||||
make_tuple(ShapeVector{{3, 2}, {3}}, std::vector<int64_t>{1, -1, 1, -1}, StaticShape({3, 1, 2, 1})),
|
||||
make_tuple(ShapeVector{{3, 2}, {3}}, std::vector<int64_t>{2, -1, 2, -1, 0}, StaticShape({1, 3, 1, 2, 1})),
|
||||
make_tuple(ShapeVector{{2, 6, 7, 8, 3}, {2}}, std::vector<int64_t>{-1, -1}, StaticShape({2, 6, 7, 8, 3, 1}))),
|
||||
PrintToStringParamName());
|
||||
|
||||
TEST_P(UnsqueezeStaticShapeInferenceTest, shape_inference_empty_const_map) {
|
||||
const auto axes_node = std::make_shared<op::v0::Constant>(element::i64, Shape{axes.size()}, axes);
|
||||
op = std::make_shared<op::v0::Unsqueeze>(arg, axes_node);
|
||||
|
||||
shape_infer(op.get(), input_shapes, output_shapes);
|
||||
|
||||
ASSERT_EQ(output_shapes.front(), exp_shape);
|
||||
}
|
||||
|
||||
TEST_P(UnsqueezeStaticShapeInferenceTest, shape_inference_with_const_map) {
|
||||
const auto axes_node = std::make_shared<op::v0::Parameter>(element::i64, Shape{1});
|
||||
op = std::make_shared<op::v0::Unsqueeze>(arg, axes_node);
|
||||
|
||||
const auto axes_const = std::make_shared<op::v0::Constant>(element::i64, ov::Shape{axes.size()}, axes);
|
||||
const auto axes_tensor = std::make_shared<ngraph::runtime::HostTensor>(axes_const);
|
||||
const std::map<size_t, std::shared_ptr<ngraph::runtime::HostTensor>>& constant_data = {{1, axes_tensor}};
|
||||
|
||||
shape_infer(op.get(), input_shapes, output_shapes, constant_data);
|
||||
|
||||
ASSERT_EQ(output_shapes.front(), exp_shape);
|
||||
}
|
||||
@@ -9,6 +9,8 @@
|
||||
#include <utils/shape_inference/shape_inference.hpp>
|
||||
#include <utils/shape_inference/static_shape.hpp>
|
||||
|
||||
#include "utils/shape_inference/static_shape.hpp"
|
||||
|
||||
#pragma once
|
||||
|
||||
struct TestTensor {
|
||||
@@ -90,6 +92,7 @@ class OpStaticShapeInferenceTest : public testing::Test {
|
||||
protected:
|
||||
ShapeVector input_shapes, output_shapes;
|
||||
ov::intel_cpu::StaticShape exp_shape;
|
||||
std::shared_ptr<TOp> op;
|
||||
|
||||
template <class... Args>
|
||||
std::shared_ptr<TOp> make_op(Args&&... args) {
|
||||
|
||||
@@ -3,14 +3,15 @@
|
||||
//
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "reshape_inst.h"
|
||||
#include "primitive_type_base.h"
|
||||
#include "intel_gpu/runtime/memory.hpp"
|
||||
#include "intel_gpu/runtime/error_handler.hpp"
|
||||
#include "json_object.h"
|
||||
#include <string>
|
||||
|
||||
#include "intel_gpu/runtime/error_handler.hpp"
|
||||
#include "intel_gpu/runtime/memory.hpp"
|
||||
#include "json_object.h"
|
||||
#include "primitive_type_base.h"
|
||||
#include "reshape_inst.h"
|
||||
#include "shape_nodes.hpp"
|
||||
#include "unsqueeze_shape_inference.hpp"
|
||||
|
||||
namespace cldnn {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user