Handle 0 dims case in ReshapeOptimizations (#24879)

### Details:
The transformation works in 2 cases:
 * 1. all in/out dims are static, or we can match them via the symbols.
   2. only one out dim doesn't have the corresponding input static dim,
and we can't match it using symbols. Besides that the output shape must
not contain zero dims,
because then value -1 in 2nd input to Reshape op can't guarantee an
unambiguous determination of the remaining dim value.

### Tickets:
 - *CVS-143026*
This commit is contained in:
Ivan Tikhonov 2024-06-10 11:24:28 +04:00 committed by GitHub
parent bb75829a88
commit 72500f3e85
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 67 additions and 5 deletions

View File

@ -16,7 +16,39 @@ class TRANSFORMATIONS_API ReshapeOptimizations;
/**
* @ingroup ov_transformation_common_api
* @brief Searches for Flatten-like Reshape operations and simplifies 2nd input of such Reshape using special zero
* feature
* feature.
* The transformation works in 2 cases:
* 1. all in/out dims are static, or we can match them via the symbols.
2. only one out dim doesn't have the corresponding input static dim,
and we can't match it using symbols. Besides that the output shape must not contain zero dims,
because then value -1 in 2nd input to Reshape op can't guarantee an unambiguous determination of the remaining dim
value.
for example:
Before:
+-------------+ +----------+
|data | | Concat |
|shape: (0, 0)| | shape (2)|<- the values might be determined in runtime
+-----+-------+ +-----+----+ the empty data tensor can be reshaped to
| | any other empty shape, e.g. (0, 800)
+-----v---------------+ |
| Reshape | |
| shape (0,-1) <--+
| special zero = False|
+---------------------+
After:
+---------------+
+--------------+ | Constant |
| data | | shape (2) |
| shape: (0, 0)| | values (0, -1)|<- -1 means copy the corresponding input dim
+-----+--------+ +-------+-------+
| |
+----v----------------+ |
| Reshape <----+
| shape (0,0) | <- it might cause inconsistency
| special zero = True |
+---------------------+
*/
class ov::pass::ReshapeOptimizations : public ov::pass::MatcherPass {
public:

View File

@ -34,16 +34,23 @@ ov::pass::ReshapeOptimizations::ReshapeOptimizations() {
const auto& out_shape = reshape->get_output_partial_shape(0);
const auto& out_rank = out_shape.size();
int64_t cnt_static_zeros = 0;
std::vector<int64_t> output_pattern(out_rank, -1);
for (size_t i = 0; i < out_rank; ++i) {
if (out_shape[i].is_static())
if (out_shape[i].is_static()) {
output_pattern[i] = out_shape[i].get_length();
else if (i >= in_rank)
if (output_pattern[i] == 0) {
++cnt_static_zeros;
}
} else if (i >= in_rank) {
break;
else if (dims_are_equal(in_shape[i], out_shape[i]))
} else if (dims_are_equal(in_shape[i], out_shape[i])) {
output_pattern[i] = 0;
}
}
if (std::count(output_pattern.begin(), output_pattern.end(), -1) <= 1) {
int64_t cnt_neg_ones = std::count(output_pattern.begin(), output_pattern.end(), -1);
if (cnt_neg_ones == 0 || (cnt_neg_ones == 1 && cnt_static_zeros == 0)) {
auto new_pattern = ov::op::v0::Constant::create(element::i64, Shape{output_pattern.size()}, output_pattern);
ov::copy_runtime_info(reshape->get_input_node_shared_ptr(1), new_pattern);
reshape->set_special_zero(true);

View File

@ -149,3 +149,26 @@ TEST_F(TransformationTestsF, NegativeTest) {
manager.register_pass<pass::ReshapeOptimizations>();
}
}
TEST_F(TransformationTestsF, ZeroDimsInOutputShape) {
// [A, B]
auto shape = PartialShape{0, 0};
{
auto data = make_shared<v0::Parameter>(element::f32, shape);
auto b = make_shared<v0::Parameter>(element::f32, PartialShape{-1});
auto a = ov::op::v0::Constant::create(element::i64, Shape{1}, {0});
auto shape_of = make_shared<v3::ShapeOf>(b);
auto indices = ov::op::v0::Constant::create(element::i64, {1}, {0});
auto axis = ov::op::v0::Constant::create(element::i64, {}, {0});
auto b_dim = make_shared<v1::Gather>(shape_of, indices, axis);
auto pattern = make_shared<v0::Concat>(OutputVector{a, b_dim}, 0);
auto reshape = make_shared<v1::Reshape>(data, pattern, false);
model = make_shared<Model>(NodeVector{reshape}, ParameterVector{data, b});
manager.register_pass<pass::ReshapeOptimizations>();
}
}