Skip to content

Commit 4c3f2eb

Browse files
Merge branch 'master' into dev
2 parents 89ade2e + b08fa94 commit 4c3f2eb

File tree

13 files changed

+430
-50
lines changed

13 files changed

+430
-50
lines changed

.ci/azure/mac.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ jobs:
184184
- script: . $(SETUPVARS) && $(INSTALL_TEST_DIR)/ov_ir_frontend_tests --gtest_print_time=1 --gtest_output=xml:$(INSTALL_TEST_DIR)/TEST-IRFrontend.xml
185185
displayName: 'IR Frontend Tests'
186186
continueOnError: false
187+
enabled: false
187188

188189
- script: . $(SETUPVARS) && $(INSTALL_TEST_DIR)/ov_onnx_frontend_tests --gtest_print_time=1 --gtest_filter=-*IE_GPU*--gtest_output=xml:$(INSTALL_TEST_DIR)/TEST-ONNXFrontend.xml
189190
displayName: 'ONNX Frontend Tests'

src/frontends/tensorflow/src/frontend.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "helper_transforms/block_lstm_replacer.hpp"
99
#include "helper_transforms/embedding_segments_feature_fusing.hpp"
1010
#include "helper_transforms/gru_block_cell_replacer.hpp"
11+
#include "helper_transforms/unique_replacer.hpp"
1112
#include "input_model.hpp"
1213
#include "op_table.hpp"
1314
#include "openvino/frontend/tensorflow/extension/conversion.hpp"
@@ -429,9 +430,10 @@ void FrontEnd::normalize(const std::shared_ptr<ov::Model>& function) const {
429430

430431
// Runs middle transformations to convert sub-graphs with intermediate (frontend internal) operations
431432
// into sub-graphs with only OpenVINO operations
432-
manager.register_pass<ov::frontend::tensorflow::pass::EmbeddingSegmentSingleFeatureFusion>();
433-
manager.register_pass<ov::frontend::tensorflow::pass::BlockLSTMReplacer>();
434-
manager.register_pass<ov::frontend::tensorflow::pass::GRUBlockCellReplacer>();
433+
manager.register_pass<pass::EmbeddingSegmentSingleFeatureFusion>();
434+
manager.register_pass<pass::BlockLSTMReplacer>();
435+
manager.register_pass<pass::GRUBlockCellReplacer>();
436+
manager.register_pass<pass::UniqueReplacer>();
435437

436438
// TODO: reimplement TransposeSinking that does not corrupt filters for Convolution
437439
// and preserve tensor names in case of sinking

src/frontends/tensorflow/src/helper_ops/unique.hpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class Unique : public ov::frontend::tensorflow::InternalOperation {
2121
ov::element::Type output_indices_type,
2222
const std::shared_ptr<DecoderBase>& decoder = nullptr)
2323
: ov::frontend::tensorflow::InternalOperation(decoder, OutputVector{input_values}, 2),
24-
out_idx(output_indices_type) {
24+
m_output_indices_type(output_indices_type) {
2525
validate_and_infer_types();
2626
}
2727

@@ -33,11 +33,15 @@ class Unique : public ov::frontend::tensorflow::InternalOperation {
3333
// 0) 1D tensor of unique elements
3434
// 1) 1D tensor of indices of the unique elements in the input
3535
set_output_type(0, get_input_element_type(0), ov::PartialShape({ov::Dimension::dynamic()}));
36-
set_output_type(1, out_idx, ov::PartialShape({ov::Dimension::dynamic()}));
36+
set_output_type(1, m_output_indices_type, ov::PartialShape({ov::Dimension::dynamic()}));
37+
}
38+
39+
ov::element::Type get_output_indices_type() const {
40+
return m_output_indices_type;
3741
}
3842

3943
private:
40-
ov::element::Type out_idx;
44+
ov::element::Type m_output_indices_type;
4145
};
4246
} // namespace tensorflow
4347
} // namespace frontend
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// Copyright (C) 2018-2022 Intel Corporation
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
5+
#include "helper_transforms/unique_replacer.hpp"
6+
7+
#include <memory>
8+
#include <vector>
9+
10+
#include "helper_ops/unique.hpp"
11+
#include "openvino/core/rt_info.hpp"
12+
#include "openvino/opsets/opset9.hpp"
13+
#include "openvino/pass/graph_rewrite.hpp"
14+
#include "openvino/pass/pattern/matcher.hpp"
15+
#include "openvino/pass/pattern/op/wrap_type.hpp"
16+
#include "utils.hpp"
17+
18+
using namespace std;
19+
using namespace ov;
20+
using namespace ov::pass;
21+
using namespace ov::opset9;
22+
using namespace ov::frontend::tensorflow;
23+
24+
ov::frontend::tensorflow::pass::UniqueReplacer::UniqueReplacer() {
25+
auto unique = pattern::wrap_type<Unique>();
26+
27+
matcher_pass_callback callback = [=](pattern::Matcher& matcher) {
28+
NodeRegistry rg;
29+
30+
auto unique_node = std::dynamic_pointer_cast<Unique>(matcher.get_match_root());
31+
if (!unique_node) {
32+
return false;
33+
}
34+
35+
auto x = unique_node->input_value(0);
36+
auto output_indices_type = unique_node->get_output_indices_type();
37+
auto x_type = x.get_element_type();
38+
if (!x_type.is_real() && !x_type.is_integral_number()) {
39+
return false;
40+
}
41+
42+
// denote a number of elements in x as n
43+
auto n = get_elements_number_1d(x, element::i32, rg);
44+
45+
// create auxiliry constants to be re-used by different operations
46+
auto zero_const = rg.make<Constant>(element::i32, Shape{1}, 0);
47+
auto one_const = rg.make<Constant>(element::i32, Shape{1}, 1);
48+
auto one_const_scalar = rg.make<Constant>(element::i32, Shape{}, 1);
49+
auto minus_one_const = rg.make<Constant>(element::i32, Shape{1}, -1);
50+
auto true_const = rg.make<Constant>(element::boolean, Shape{1}, true);
51+
auto one_const_out_idx = rg.make<Constant>(output_indices_type, Shape{1}, 1);
52+
auto zero_const_out_idx = rg.make<Constant>(output_indices_type, Shape{1}, 0);
53+
54+
// compute unique elements but not in the original order
55+
// 1. sort elements in x in order to compute unique elements
56+
auto x_sorted = rg.make<TopK>(x, n, 0, TopK::Mode::MIN, TopK::SortType::SORT_VALUES, element::i32);
57+
// 2. generate two vectors from x_sorted vector by padding in the beginning and in the end:
58+
// x1 = [0, x0, x1, ..., xn]
59+
// x2 = [x0, x1, ..., xn, 0]
60+
auto pad = rg.make<Constant>(x_type, Shape{1}, 0);
61+
auto x1 = rg.make<Concat>(OutputVector{pad, x_sorted->output(0)}, 0);
62+
auto x2 = rg.make<Concat>(OutputVector{x_sorted->output(0), pad}, 0);
63+
// 3. compare two vectors to see where unique elements are placed
64+
// and correct a mask because the first element is always unique
65+
// because the latest boolean element must be removed from the mask since
66+
// the vectors are padded
67+
auto mask1 = rg.make<NotEqual>(x1, x2);
68+
auto mask1_part = rg.make<Slice>(mask1, one_const, minus_one_const, one_const, zero_const);
69+
auto is_unique = rg.make<Concat>(OutputVector{true_const, mask1_part}, 0);
70+
// 5. compute positions where unique elements are placed in the sorted x
71+
auto is_unique_01 = rg.make<Select>(is_unique, one_const, zero_const);
72+
auto indices = rg.make<NonZero>(is_unique_01, element::i64);
73+
auto unique_element_indices = rg.make<Squeeze>(indices, zero_const);
74+
// 6. collect unique elements but currently they are not in the original order
75+
auto unique_elements = rg.make<Gather>(x_sorted->output(0), unique_element_indices, zero_const);
76+
77+
// compute unique elements in the original order
78+
auto unsqueeze_x = rg.make<Unsqueeze>(x, zero_const);
79+
auto unsqueeze_unique_elements = rg.make<Unsqueeze>(unique_elements, one_const);
80+
// 1. compute a mask of pair comparison where each unique element is placed in the original
81+
auto nplus1 = rg.make<Add>(n, one_const_scalar);
82+
auto unique_vs_x = rg.make<Equal>(unsqueeze_unique_elements, unsqueeze_x);
83+
auto unique_vs_x_01 = rg.make<Select>(unique_vs_x, one_const_scalar, nplus1);
84+
auto range_1nplus1 = rg.make<Range>(one_const_scalar, nplus1, one_const_scalar, element::i32);
85+
auto unsqueeze_range_1nplus1 = rg.make<Unsqueeze>(range_1nplus1, zero_const);
86+
// 2. compute a mask with indices counting from one
87+
auto unique_vs_x_ind = rg.make<Multiply>(unique_vs_x_01, unsqueeze_range_1nplus1);
88+
// 3. compute positions of the first occurence for each unique element
89+
// or these are positions of unique elements in the original order
90+
auto minimum_indices_plus1 = rg.make<ReduceMin>(unique_vs_x_ind, one_const);
91+
auto minimum_indices = rg.make<Subtract>(minimum_indices_plus1, one_const);
92+
// denote a number of unique elements as m
93+
auto m = get_elements_number_1d(minimum_indices, element::i32, rg);
94+
auto sorted_minumum_indices =
95+
rg.make<TopK>(minimum_indices, m, 0, TopK::Mode::MIN, TopK::SortType::SORT_VALUES, element::i32);
96+
auto output_unique_elements = rg.make<Gather>(x, sorted_minumum_indices->output(0), zero_const);
97+
98+
if (!unique_node->get_output_target_inputs(0).empty()) {
99+
output_unique_elements->set_friendly_name(unique_node->get_friendly_name() + ":0");
100+
unique_node->output(0).replace(output_unique_elements->output(0));
101+
}
102+
103+
if (!unique_node->get_output_target_inputs(1).empty()) {
104+
// compute the second output
105+
// indices of elements of x in the vector of unique elements
106+
// 1. compute a mask for unique elements in the original order
107+
auto unsqueeze_output_unique_elements = rg.make<Unsqueeze>(output_unique_elements, one_const);
108+
auto unique_vs_x_orig = rg.make<Equal>(unsqueeze_output_unique_elements, unsqueeze_x);
109+
auto mplus1 = rg.make<Add>(m, one_const_scalar);
110+
auto unique_vs_x_orig_01 = rg.make<Select>(unique_vs_x_orig, one_const_out_idx, zero_const_out_idx);
111+
// 2. compute positions where each element from x is located in unique elements vector
112+
// the position counts from 1
113+
auto range_1mplus1 = rg.make<Range>(one_const_scalar, mplus1, one_const_scalar, output_indices_type);
114+
auto unsqueeze_range_1mplus1 = rg.make<Unsqueeze>(range_1mplus1, one_const);
115+
auto unique_vs_x_ind_orig = rg.make<Multiply>(unique_vs_x_orig_01, unsqueeze_range_1mplus1);
116+
auto output_idx_plus1 = rg.make<ReduceMax>(unique_vs_x_ind_orig, zero_const);
117+
auto output_idx = rg.make<Subtract>(output_idx_plus1, one_const_out_idx);
118+
119+
output_idx->set_friendly_name(unique_node->get_friendly_name() + ":1");
120+
unique_node->output(1).replace(output_idx->output(0));
121+
}
122+
123+
copy_runtime_info(unique_node, rg.get());
124+
125+
return true;
126+
};
127+
128+
auto m = make_shared<pattern::Matcher>(unique, "ov::frontend::tensorflow::pass::UniqueReplacer");
129+
register_matcher(m, callback);
130+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (C) 2018-2022 Intel Corporation
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
5+
#pragma once
6+
7+
#include <memory>
8+
#include <utility>
9+
10+
#include "openvino/frontend/tensorflow/visibility.hpp"
11+
#include "openvino/pass/graph_rewrite.hpp"
12+
#include "openvino/pass/pass.hpp"
13+
14+
namespace ov {
15+
namespace frontend {
16+
namespace tensorflow {
17+
namespace pass {
18+
19+
// This transformation expresses Unique with a sub-graph of OpenVINO operations
20+
class TENSORFLOW_API UniqueReplacer : public ov::pass::MatcherPass {
21+
public:
22+
OPENVINO_RTTI("ov::frontend::tensorflow::pass::UniqueReplacer");
23+
UniqueReplacer();
24+
};
25+
26+
} // namespace pass
27+
} // namespace tensorflow
28+
} // namespace frontend
29+
} // namespace ov

src/frontends/tensorflow/src/op/sparse_reshape.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -115,18 +115,6 @@ OutputVector translate_sparse_segment_sum_op(const NodeContext& node) {
115115
return sparse_segment_sum->outputs();
116116
}
117117

118-
OutputVector translate_unique_op(const NodeContext& node) {
119-
default_op_checks(node, 1, {"Unique"});
120-
auto input_values = node.get_input(0);
121-
122-
// retrieve attribute
123-
auto output_indices_type = node.get_attribute<ov::element::Type>("out_idx", ov::element::i32);
124-
125-
auto unique = make_shared<ov::frontend::tensorflow::Unique>(input_values, output_indices_type, node.get_decoder());
126-
set_node_name(node.get_name(), unique);
127-
return unique->outputs();
128-
}
129-
130118
} // namespace op
131119
} // namespace tensorflow
132120
} // namespace frontend
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (C) 2018-2022 Intel Corporation
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
5+
#include "helper_ops/unique.hpp"
6+
7+
#include "op_table.hpp"
8+
9+
using namespace std;
10+
11+
namespace ov {
12+
namespace frontend {
13+
namespace tensorflow {
14+
namespace op {
15+
OutputVector translate_unique_op(const NodeContext& node) {
16+
default_op_checks(node, 1, {"Unique"});
17+
auto input_values = node.get_input(0);
18+
19+
// retrieve attribute
20+
auto output_indices_type = node.get_attribute<ov::element::Type>("out_idx", ov::element::i32);
21+
22+
auto unique = make_shared<ov::frontend::tensorflow::Unique>(input_values, output_indices_type, node.get_decoder());
23+
set_node_name(node.get_name(), unique);
24+
return unique->outputs();
25+
}
26+
27+
} // namespace op
28+
} // namespace tensorflow
29+
} // namespace frontend
30+
} // namespace ov

src/frontends/tensorflow/src/utils.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,3 +198,16 @@ void ov::frontend::tensorflow::default_op_checks(const ov::frontend::tensorflow:
198198
bool ov::frontend::tensorflow::is_conditional_edge(const std::string& input_tensor_name) {
199199
return input_tensor_name.length() > 0 && input_tensor_name[0] == '^';
200200
}
201+
202+
ov::Output<ov::Node> ov::frontend::tensorflow::get_elements_number_1d(const ov::Output<ov::Node>& output,
203+
ov::element::Type output_type,
204+
ov::pass::NodeRegistry& rg) {
205+
auto output_rank = output.get_partial_shape().rank();
206+
if (output_rank.is_static() && output_rank.get_length() != 1) {
207+
FRONT_END_OP_CONVERSION_CHECK(false,
208+
"Internal error: get_elements_number_1d method supports only 1D input tensor.");
209+
}
210+
auto shape = rg.make<ShapeOf>(output, output_type);
211+
auto num_elements = rg.make<Squeeze>(shape);
212+
return num_elements;
213+
}

src/frontends/tensorflow/src/utils.hpp

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "openvino/core/validation_util.hpp"
88
#include "openvino/frontend/tensorflow/node_context.hpp"
99
#include "openvino/opsets/opset8.hpp"
10+
#include "openvino/pass/graph_rewrite.hpp"
1011

1112
namespace ov {
1213
namespace frontend {
@@ -24,34 +25,6 @@ void set_node_name(const std::string& node_name, const std::shared_ptr<Node>& no
2425

2526
bool is_conditional_edge(const std::string& input_tensor_name);
2627

27-
static bool vec_str_cmp(const std::vector<std::string>& a, const std::vector<std::string>& b) {
28-
return a == b;
29-
}
30-
31-
template <typename T>
32-
void make_padding(const std::string& tf_padding_type,
33-
const ov::Shape& ng_image_shape,
34-
const ov::Shape& ng_kernel_shape,
35-
const ov::Strides& ng_strides,
36-
const ov::Shape& ng_dilations,
37-
T& ng_padding_below,
38-
T& ng_padding_above) {
39-
if (tf_padding_type == "SAME") {
40-
ov::Shape img_shape = {0, 0};
41-
img_shape.insert(img_shape.end(), ng_image_shape.begin(), ng_image_shape.end());
42-
ov::infer_auto_padding(img_shape,
43-
ng_kernel_shape,
44-
ng_strides,
45-
ng_dilations,
46-
ov::op::PadType::SAME_UPPER,
47-
ng_padding_above,
48-
ng_padding_below);
49-
} else if (tf_padding_type == "VALID") {
50-
ng_padding_below.assign(ng_image_shape.size(), 0);
51-
ng_padding_above.assign(ng_image_shape.size(), 0);
52-
}
53-
}
54-
5528
template <typename T>
5629
void get_const_input(const NodeContext& node, int64_t input_index, std::vector<T>* vector) {
5730
auto ng_input = node.get_input(static_cast<int>(input_index));
@@ -75,6 +48,10 @@ void fill_explicit_pads_vectors(const NodeContext& node,
7548

7649
void default_op_checks(const NodeContext& node, int min_input_size, const std::vector<std::string>& supported_ops);
7750

51+
ov::Output<Node> get_elements_number_1d(const Output<Node>& output,
52+
ov::element::Type output_type,
53+
ov::pass::NodeRegistry& rg);
54+
7855
} // namespace tensorflow
7956
} // namespace frontend
8057
} // namespace ov

0 commit comments

Comments
 (0)