Unroll transformation for TensorIterator (#1259)
authorIvan Tikhonov <ivan.tikhonov@intel.com>
Tue, 11 Aug 2020 15:46:57 +0000 (18:46 +0300)
committerGitHub <noreply@github.com>
Tue, 11 Aug 2020 15:46:57 +0000 (18:46 +0300)
* unroll ti transformation, lstm sequence ie, rnn sequence ie

* Update unroll ti transformation, added GRUSequenceIE op, fixed several ti e2e tests

* apply ngraph codestyle

* fix naming after unroll transformation

* Added default constructor for RNNCellBase, fix conversions

* copy runtime info

* added UnrollTI unit tests

* clean up, move sequence ops in a separate PR

* clean up, ngraph code style

* temporary disable ngraph reader unit tests for ti

* fix unit tests on windows

* naming: use name of tensor after unroll tensor iteration transformation

* apply transformations to tensor iterator body, separate pass for ti transformations, fix naming issue

* fix build

* remove TensorIterationTransformations pass

* fix includes

* resolve conflicts

* fix build: incorrect includes

* remove split/concat for single iteration of TI, update to opset4, unit tests

* use matcher pass instead of graph rewrite

* try to enable UnrollTI transformation for all plugins

* disable unrollTI transformation for cpu plugin

* resolve review comments, enable unit tests

* update transformation description

* fix unit tests

* update transformation pipeline

* clean up

* clean up

* resolve review comments

17 files changed:
inference-engine/src/cldnn_engine/cldnn_engine.cpp
inference-engine/src/inference_engine/cnn_network_ngraph_impl.cpp
inference-engine/src/legacy_api/src/cnn_network_impl.cpp
inference-engine/src/legacy_api/src/convert_function_to_cnn_network.cpp
inference-engine/src/mkldnn_plugin/mkldnn_plugin.cpp
inference-engine/src/transformations/include/transformations/tensor_iterator_transformations/apply_transformations_to_ti_body.hpp [moved from inference-engine/src/transformations/include/transformations/apply_transformations_to_ti_body.hpp with 100% similarity]
inference-engine/src/transformations/include/transformations/tensor_iterator_transformations/unroll_tensor_iterator.hpp [new file with mode: 0644]
inference-engine/src/transformations/include/transformations/utils/utils.hpp
inference-engine/src/transformations/src/transformations/tensor_iterator_transformations/apply_transformations_to_ti_body.cpp [moved from inference-engine/src/transformations/src/transformations/apply_transformations_to_ti_body.cpp with 90% similarity]
inference-engine/src/transformations/src/transformations/tensor_iterator_transformations/unroll_tensor_iterator.cpp [new file with mode: 0644]
inference-engine/src/vpu/graph_transformer/src/frontend/frontend.cpp
inference-engine/src/vpu/myriad_plugin/myriad_plugin.cpp
inference-engine/tests/functional/inference_engine/transformations/unroll_tensor_iterator_test.cpp [new file with mode: 0644]
ngraph/core/include/ngraph/descriptor/tensor.hpp
ngraph/core/src/descriptor/tensor.cpp
ngraph/core/src/graph_util.cpp
ngraph/core/src/op/util/fused_op.cpp

index 786a487..81b5ccf 100644 (file)
@@ -27,7 +27,8 @@
 #include <ngraph/op/fused/gelu.hpp>
 #include <ngraph/pass/manager.hpp>
 #include <generic_ie.hpp>
-#include <transformations/apply_transformations_to_ti_body.hpp>
+#include <transformations/tensor_iterator_transformations/apply_transformations_to_ti_body.hpp>
+#include <transformations/tensor_iterator_transformations/unroll_tensor_iterator.hpp>
 #include <transformations/common_optimizations/common_optimizations.hpp>
 #include <transformations/convert_opset1_to_legacy/convert_opset1_to_legacy.hpp>
 #include <transformations/convert_opset2_to_opset1/convert_opset2_to_opset1.hpp>
@@ -106,9 +107,11 @@ InferenceEngine::ICNNNetwork::Ptr clDNNEngine::CloneAndTransformNetwork(const In
         manager.set_callback(transformations_callback);
         manager.run_passes(nGraphFunc);
 
-        // Apply all transformations to TensorIterator body
         ngraph::pass::Manager ti_manager;
+        // Apply all transformations to TensorIterator body
         ti_manager.register_pass<ngraph::pass::ApplyTransformationsToTIBody>(manager);
+        // Unroll will be called after all conversions
+        ti_manager.register_pass<ngraph::pass::UnrollTensorIterator>();
         ti_manager.run_passes(nGraphFunc);
 
         clonedNetwork = InferenceEngine::details::convertFunctionToICNNNetwork(nGraphFunc, *clonedNetwork);
index ec04e39..fbd4060 100644 (file)
@@ -20,6 +20,7 @@
 #include <set>
 #include <string>
 
+#include <transformations/utils/utils.hpp>
 #include <transformations/convert_opset1_to_legacy/convert_one_hot_to_one_hot_ie.hpp>
 
 #include "ngraph_ops/eltwise.hpp"
@@ -237,12 +238,7 @@ StatusCode CNNNetworkNGraphImpl::addOutput(const std::string& layerName, size_t
 }
 
 void CNNNetworkNGraphImpl::addOutput(const ::ngraph::Output<::ngraph::Node> & output) {
-    auto outputNode = output.get_node_shared_ptr();
-    auto dataName = outputNode->get_friendly_name();
-    if (outputNode->get_output_size() != 1) {
-        dataName += "." + std::to_string(output.get_index());
-    }
-
+    auto dataName = ngraph::op::util::create_ie_output_name(output);
     DataPtr data;
     createDataForResult(output, dataName, data);
     _data[dataName] = data;
@@ -310,7 +306,8 @@ CNNNetworkNGraphImpl::reshape(const std::map<std::string, std::vector<size_t>>&
             {
                 OV_ITT_SCOPED_TASK(itt::domains::IE, "CNNNetworkNGraphImpl::ConvertOneHot");
                 ::ngraph::pass::Manager manager;
-                manager.register_pass<::ngraph::pass::ConvertOneHotToOneHotIEMatcher>()->detect_output_type(specialized_ngraph_function);
+                manager.register_pass<::ngraph::pass::ConvertOneHotToOneHotIEMatcher>()->detect_output_type(
+                        specialized_ngraph_function);
                 manager.run_passes(specialized_ngraph_function);
             }
             specialized_ngraph_function->validate_nodes_and_infer_types();
@@ -354,12 +351,12 @@ CNNNetworkNGraphImpl::reshape(const std::map<std::string, std::vector<size_t>>&
             }
 #endif
             std::unordered_set<std::string> opName;
-            for (const auto & result : specialized_ngraph_function->get_results()) {
+            for (const auto &result : specialized_ngraph_function->get_results()) {
                 addOutput(result->input_value(0));
             }
 
-            for (const auto & parameter : specialized_ngraph_function->get_parameters()) {
-                const auto & outName = parameter->get_friendly_name();
+            for (const auto &parameter : specialized_ngraph_function->get_parameters()) {
+                const auto &outName = parameter->get_friendly_name();
                 if (opName.find(outName) != opName.end()) {
                     THROW_IE_EXCEPTION << "All operations in nGraph function should have unique friendly names!";
                 }
index 708917a..7358cde 100644 (file)
@@ -22,7 +22,7 @@
 #include <transformations/convert_opset1_to_legacy/convert_opset1_to_legacy.hpp>
 #include <transformations/convert_opset2_to_opset1/convert_opset2_to_opset1.hpp>
 #include <transformations/convert_opset3_to_opset2/convert_opset3_to_opset2.hpp>
-#include <transformations/apply_transformations_to_ti_body.hpp>
+#include <transformations/tensor_iterator_transformations/apply_transformations_to_ti_body.hpp>
 
 #include "legacy/convert_function_to_cnn_network.hpp"
 #include "legacy/graph_tools.hpp"
index 44f74cd..e85d862 100644 (file)
@@ -812,8 +812,11 @@ void convertFunctionToICNNNetwork(const std::shared_ptr<const ::ngraph::Function
                 cnnLayer->outData.clear();
                 continue;
             }
-            std::string outName = layer->get_friendly_name();
-            if (layer->get_output_size() != 1) outName += "." + std::to_string(i);
+            auto outName = layer->output(i).get_tensor().get_name();
+            if (outName.empty()) {
+                outName = ngraph::op::util::create_ie_output_name(layer->output(i));
+            }
+
             DataPtr &ptr = cnnNetworkImpl->getData(outName.c_str());
             SizeVector dims = layer->get_output_shape(i);
             for (const auto &dim : dims) {
@@ -854,12 +857,13 @@ void convertFunctionToICNNNetwork(const std::shared_ptr<const ::ngraph::Function
         if (std::dynamic_pointer_cast<::ngraph::op::ReadValue>(layer))
             continue;
         if (std::dynamic_pointer_cast<::ngraph::op::Result>(layer)) {
-            IE_ASSERT(layer->inputs().size() == 1);
+            IE_ASSERT(layer->get_input_size() == 1);
             const auto &input = layer->input_value(0);
-            std::string outName = input.get_node_shared_ptr()->get_friendly_name();
-            if (input.get_node_shared_ptr()->get_output_size() != 1)
-                outName += "." + std::to_string(input.get_index());
-            cnnNetworkImpl->addOutput(outName);
+            auto name = input.get_tensor().get_name();
+            if (!name.empty())
+                cnnNetworkImpl->addOutput(name);
+            else
+                cnnNetworkImpl->addOutput(ngraph::op::util::create_ie_output_name(input));
             continue;
         }
 
index ba0b006..4396883 100644 (file)
 #include <legacy/ie_ngraph_utils.hpp>
 
 #include <legacy/convert_function_to_cnn_network.hpp>
-#include <transformations/apply_transformations_to_ti_body.hpp>
 #include <transformations/common_optimizations/common_optimizations.hpp>
 #include <transformations/convert_opset1_to_legacy/convert_opset1_to_legacy.hpp>
 #include <transformations/convert_opset2_to_opset1/convert_opset2_to_opset1.hpp>
 #include <transformations/convert_opset3_to_opset2/convert_opset3_to_opset2.hpp>
 #include <transformations/convert_precision.hpp>
 #include <transformations/rt_info/fused_names_attribute.hpp>
+#include <transformations/tensor_iterator_transformations/apply_transformations_to_ti_body.hpp>
 #include <ngraph/opsets/opset1.hpp>
 #include <ngraph/opsets/opset2.hpp>
 #include <ngraph/opsets/opset3.hpp>
diff --git a/inference-engine/src/transformations/include/transformations/tensor_iterator_transformations/unroll_tensor_iterator.hpp b/inference-engine/src/transformations/include/transformations/tensor_iterator_transformations/unroll_tensor_iterator.hpp
new file mode 100644 (file)
index 0000000..367e7b7
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright (C) 2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#pragma once
+
+#include <vector>
+#include <memory>
+
+#include <transformations_visibility.hpp>
+
+#include <ngraph/pass/graph_rewrite.hpp>
+
+namespace ngraph {
+namespace pass {
+
+class TRANSFORMATIONS_API UnrollTensorIterator;
+
+}  // namespace pass
+}  // namespace ngraph
+
+/**
+ * @ingroup ie_transformation_common_api
+ * @brief Unrolls the body of the TensorIterator layer. Multiple body copies, the number of which is determined by
+ * the number of iterations of the TensorIterator layer, are created and connected to each other and to the external
+ * network. If the number of TensorIterator iterations is greater than 1, then additional Concat and Split layers
+ * are added to the network.
+ */
+
+class ngraph::pass::UnrollTensorIterator: public ngraph::pass::MatcherPass {
+public:
+    UnrollTensorIterator();
+};
index 24d44e5..618630f 100644 (file)
@@ -13,6 +13,7 @@
 #include <transformations_visibility.hpp>
 #include <ngraph/op/util/op_annotations.hpp>
 #include <ngraph/op/constant.hpp>
+#include <ngraph/opsets/opset3.hpp>
 
 namespace ngraph {
 namespace op {
@@ -44,6 +45,14 @@ bool has_op_with_type(const std::shared_ptr<const ngraph::Function> &function) {
     return false;
 }
 
+inline std::string create_ie_output_name(const ngraph::Output<ngraph::Node>& output) {
+    const auto& prev_layer = output.get_node_shared_ptr();
+    std::string out_name = prev_layer->get_friendly_name();
+    if (prev_layer->get_output_size() != 1)
+        out_name += "." + std::to_string(output.get_index());
+    return out_name;
+}
+
 TRANSFORMATIONS_API bool get_single_value(const std::shared_ptr<op::Constant> & const_node, float & value);
 
 TRANSFORMATIONS_API std::shared_ptr<ngraph::Node> normalize_constant(const std::shared_ptr<op::Constant> & constant,
@@ -2,11 +2,10 @@
 // SPDX-License-Identifier: Apache-2.0
 //
 
-#include "transformations/apply_transformations_to_ti_body.hpp"
+#include "transformations/tensor_iterator_transformations/apply_transformations_to_ti_body.hpp"
 #include "transformations/utils/utils.hpp"
 
 #include <memory>
-#include <vector>
 
 #include <ngraph/opsets/opset4.hpp>
 #include <ngraph/pattern/op/wrap_type.hpp>
diff --git a/inference-engine/src/transformations/src/transformations/tensor_iterator_transformations/unroll_tensor_iterator.cpp b/inference-engine/src/transformations/src/transformations/tensor_iterator_transformations/unroll_tensor_iterator.cpp
new file mode 100644 (file)
index 0000000..eb758da
--- /dev/null
@@ -0,0 +1,189 @@
+// Copyright (C) 2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include "transformations/tensor_iterator_transformations/unroll_tensor_iterator.hpp"
+#include "transformations/utils/utils.hpp"
+
+#include <memory>
+#include <vector>
+
+#include <ngraph/graph_util.hpp>
+#include <ngraph/opsets/opset4.hpp>
+#include <ngraph/pattern/op/wrap_type.hpp>
+#include <ngraph/rt_info.hpp>
+
+ngraph::pass::UnrollTensorIterator::UnrollTensorIterator() : MatcherPass() {
+    auto tensor_iterator = ngraph::pattern::wrap_type<ngraph::opset4::TensorIterator>();
+    ngraph::matcher_pass_callback callback = [this](pattern::Matcher& m) {
+        auto ti = std::dynamic_pointer_cast<ngraph::opset4::TensorIterator>(m.get_match_root());
+        if (!ti) {
+            return false;
+        }
+
+        const auto function = ti->get_body()->to_function();
+        auto num_iter = ti->get_num_iterations();
+
+        // negative value means inconsistent TI
+        if (num_iter <= -1) {
+            return false;
+        }
+
+        // Create copies of the TensorIterator body, the number of copies is equal to the number of iterations.
+        // Assign names to the created layers.
+        std::vector<std::shared_ptr<ngraph::Function>> body_functions(num_iter);
+        for (uint64_t idx = 0; idx < num_iter; ++idx) {
+            body_functions[idx] = clone_function(*function);
+            for (auto &node : body_functions[idx]->get_ops()) {
+                node->set_friendly_name(ti->get_friendly_name() + "/" + std::to_string(idx + 1) + "/" + node->get_friendly_name());
+                copy_runtime_info(ti, node);
+            }
+        }
+
+        // Port map : inputs and back edges
+        for (const auto& desc : ti->get_input_descriptions()) {
+            const std::string& type_name = desc->get_type_info().name;
+
+            if (type_name == "SliceInputDescription") {
+                auto input_desc = std::dynamic_pointer_cast<ngraph::opset4::TensorIterator::SliceInputDescription>(desc);
+                if (!input_desc) {
+                    return false;
+                }
+
+                // Connect the sliced input (layer before the input) to the Split layer and connect
+                // the corresponding Split output to the corresponding copy of the body.
+                // If the number of iterations is 1, then the Split is not needed.
+
+                auto in_data = ti->input_values()[input_desc->m_input_index];
+                const auto const_axis = opset4::Constant::create(element::i64, Shape{}, {input_desc->m_axis});
+
+                if (num_iter > 1) {
+                    auto split = std::make_shared<ngraph::opset4::Split>(in_data, const_axis, num_iter);
+                    copy_runtime_info(ti, split);
+                    auto stride = input_desc->m_stride;
+                    // connect to the body
+                    for (uint64_t j = 0; j < num_iter; j++) {
+                        auto idx = stride > 0 ? j : num_iter - j - 1;
+                        auto param = body_functions[j]->get_parameters()[input_desc->m_body_parameter_index];
+                        for (auto &output : param->outputs()) {
+                            output.replace(split->output(idx));
+                        }
+                    }
+                } else {
+                    // connect to the body
+                    auto param = body_functions[0]->get_parameters()[input_desc->m_body_parameter_index];
+                    for (auto &output : param->outputs()) {
+                        output.replace(in_data);
+                    }
+                }
+            } else if (type_name == "MergedInputDescription") {
+                auto input_desc = std::dynamic_pointer_cast<ngraph::opset4::TensorIterator::MergedInputDescription>(desc);
+                if (!input_desc) {
+                    return false;
+                }
+
+                // Connect the input to the corresponding copy of the body.
+                auto in_data = ti->input_values()[input_desc->m_input_index].get_node_shared_ptr();
+                auto param = body_functions[0]->get_parameters()[input_desc->m_body_parameter_index];
+                for (auto &output : param->outputs()) {
+                    output.replace(in_data);
+                }
+
+                // Back-edge processing. Connect the copies of the body to each other.
+                for (uint64_t j = 1; j < num_iter; j++) {
+                    auto cur_param = body_functions[j]->get_parameters()[input_desc->m_body_parameter_index];
+                    auto prev_val = body_functions[j - 1]->get_results()[input_desc->m_body_value_index];
+                    for (auto &output : cur_param->outputs()) {
+                        output.replace(prev_val->get_input_source_output(0));
+                    }
+                }
+            } else if (type_name == "InvariantInputDescription") {
+                auto input_desc = std::dynamic_pointer_cast<ngraph::opset4::TensorIterator::InvariantInputDescription>(
+                        desc);
+                if (!input_desc) {
+                    return false;
+                }
+
+                // Connect the input to the corresponding copy of the body.
+                auto in_data = ti->input_values()[input_desc->m_input_index].get_node_shared_ptr();
+                for (uint64_t j = 0; j < num_iter; j++) {
+                    auto param = body_functions[j]->get_parameters()[input_desc->m_body_parameter_index];
+                    for (auto &output : param->outputs()) {
+                        output.replace(in_data);
+                    }
+                }
+            } else {
+                // "Incorrect type of the input description.";
+                return false;
+            }
+        }
+
+        // Port map: outputs
+        for (const auto& desc : ti->get_output_descriptions()) {
+            std::string type_name = desc->get_type_info().name;
+            if (type_name == "ConcatOutputDescription") {
+                auto output_desc = std::dynamic_pointer_cast<ngraph::opset4::TensorIterator::ConcatOutputDescription>(desc);
+                if (!output_desc) {
+                    return false;
+                }
+
+                // Connect corresponding outputs (layers before Result op) of each copy of the body to Concat layer.
+                // Connect the Concat to corresponding output of TensorIterator.
+                // If the number of iterations is 1, then the Concat is not needed.
+
+                if (num_iter > 1) {
+                    ngraph::OutputVector to_concat(num_iter);
+                    auto stride = output_desc->m_stride;
+
+                    // Connect outputs of the bodies to the Concat layer
+                    for (uint64_t j = 0; j < num_iter; j++) {
+                        auto idx = stride > 0 ? j : num_iter - j - 1;
+                        std::shared_ptr<opset4::Result> result = body_functions[idx]->get_results()[output_desc->m_body_value_index];
+                        auto input_to_res = result->get_input_source_output(0);
+                        to_concat[j] = input_to_res;
+                    }
+                    auto concat = std::make_shared<ngraph::opset4::Concat>(to_concat, output_desc->m_axis);
+                    copy_runtime_info(ti, concat);
+
+                    // connect the Concat layer to the corresponding TI outputs
+                    concat->output(0).get_tensor().set_name(
+                            op::util::create_ie_output_name(ti->output(output_desc->m_output_index)));
+                    for (auto &input : ti->output(output_desc->m_output_index).get_target_inputs()) {
+                        input.replace_source_output(concat);
+                    }
+                } else {
+                    // Connect outputs of the bodies to the corresponding TI outputs
+                    std::shared_ptr<opset4::Result> result = body_functions[0]->get_results()[output_desc->m_body_value_index];
+                    auto input_to_res = result->get_input_source_output(0);
+                    for (auto &input : ti->output(output_desc->m_output_index).get_target_inputs()) {
+                        input.replace_source_output(input_to_res);
+                    }
+                }
+            } else if (type_name == "BodyOutputDescription") {
+                auto output_desc = std::dynamic_pointer_cast<ngraph::opset4::TensorIterator::BodyOutputDescription>(desc);
+                if (!output_desc) {
+                    return false;
+                }
+
+                // Connect outputs of the bodies to the corresponding TI outputs
+                auto iter = output_desc->m_iteration;
+                iter = iter >= 0? iter: num_iter - 1;
+                std::shared_ptr<opset4::Result> result = body_functions[iter]->get_results()[output_desc->m_body_value_index];
+                const auto& in_value = result->input_value(0);
+
+                in_value.get_tensor().set_name(op::util::create_ie_output_name(ti->output(output_desc->m_output_index)));
+                for (const auto &input : ti->output(output_desc->m_output_index).get_target_inputs()) {
+                    input.replace_source_output(result->get_input_source_output(0));
+                }
+            } else {
+                // "Incorrect type of the output description."
+                return false;
+            }
+        }
+
+        return true;
+    };
+
+    auto m = std::make_shared<ngraph::pattern::Matcher>(tensor_iterator, "UnrollTensorIterator");
+    register_matcher(m, callback);
+}
\ No newline at end of file
index 368cef9..4966b14 100644 (file)
@@ -22,7 +22,8 @@
 #include <legacy/convert_function_to_cnn_network.hpp>
 #include <generic_ie.hpp>
 #include <ngraph/opsets/opset3.hpp>
-#include <transformations/apply_transformations_to_ti_body.hpp>
+#include <transformations/tensor_iterator_transformations/apply_transformations_to_ti_body.hpp>
+#include <transformations/tensor_iterator_transformations/unroll_tensor_iterator.hpp>
 #include <transformations/convert_opset3_to_opset2/convert_opset3_to_opset2.hpp>
 #include <transformations/convert_opset2_to_opset1/convert_opset2_to_opset1.hpp>
 #include <transformations/convert_opset1_to_legacy/convert_opset1_to_legacy.hpp>
@@ -402,7 +403,10 @@ ModelPtr FrontEnd::runCommonPasses(ie::ICNNNetwork& network, const UnsupportedLa
             manager.run_passes(nGraphFunc);
 
             ngraph::pass::Manager ti_manager;
+            // Apply all transformations to TensorIterator body
             ti_manager.register_pass<ngraph::pass::ApplyTransformationsToTIBody>(manager);
+            // Unroll should be called after all conversions
+            ti_manager.register_pass<ngraph::pass::UnrollTensorIterator>();
             ti_manager.run_passes(nGraphFunc);
 
             vpu::MergeSubsequentDSROperations().run_on_function(nGraphFunc);
index eaf9e5c..a150647 100644 (file)
@@ -17,7 +17,8 @@
 #include <vpu/parsed_config.hpp>
 #include <vpu/utils/profiling.hpp>
 #include <vpu/utils/error.hpp>
-#include <transformations/apply_transformations_to_ti_body.hpp>
+#include <transformations/tensor_iterator_transformations/apply_transformations_to_ti_body.hpp>
+#include <transformations/tensor_iterator_transformations/unroll_tensor_iterator.hpp>
 #include <transformations/common_optimizations/common_optimizations.hpp>
 #include <vpu/ngraph/transformations/convert_nms_4_to_nms_dynamic.hpp>
 
@@ -52,7 +53,10 @@ ExecutableNetworkInternal::Ptr Engine::LoadExeNetworkImpl(
         manager.run_passes(function);
 
         ngraph::pass::Manager ti_manager;
+        // Apply all transformations to TensorIterator body
         ti_manager.register_pass<ngraph::pass::ApplyTransformationsToTIBody>(manager);
+        // Unroll should be called after all conversions
+        ti_manager.register_pass<ngraph::pass::UnrollTensorIterator>();
         ti_manager.run_passes(function);
     }
 
diff --git a/inference-engine/tests/functional/inference_engine/transformations/unroll_tensor_iterator_test.cpp b/inference-engine/tests/functional/inference_engine/transformations/unroll_tensor_iterator_test.cpp
new file mode 100644 (file)
index 0000000..7d9d0d4
--- /dev/null
@@ -0,0 +1,503 @@
+// Copyright (C) 2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include <gtest/gtest.h>
+
+#include "common_test_utils/test_common.hpp"
+#include <string>
+#include <memory>
+#include <queue>
+
+#include <ngraph/function.hpp>
+#include <ngraph/opsets/opset4.hpp>
+#include <ngraph/pass/manager.hpp>
+#include <ngraph_ops/fully_connected.hpp>
+#include <transformations/tensor_iterator_transformations/unroll_tensor_iterator.hpp>
+#include <transformations/utils/utils.hpp>
+#include <transformations/init_node_info.hpp>
+
+#include "common_test_utils/ngraph_test_utils.hpp"
+
+using namespace testing;
+using namespace ngraph;
+
+TEST(TransformationTests, UnrollTensorIteratorGRUCell) {
+    std::shared_ptr<ngraph::Function> f(nullptr), f_ref(nullptr);
+    {
+        auto X = std::make_shared<opset4::Parameter>(element::f32, Shape{2, 1, 16});
+        auto Y = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+
+        auto Xi = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1, 16});
+        auto Yi = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+
+        // Body
+        auto axis = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{}, {0});
+        auto squeeze = std::make_shared<opset4::Squeeze>(Xi, axis);
+
+        auto w_val = std::vector<float>(384*16, 0);
+        auto r_val = std::vector<float>(384*128, 0);
+        auto b_val = std::vector<float>(384, 0);
+        auto W = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{384, 16}, w_val);
+        auto R = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{384, 128}, r_val);
+        auto B = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{384}, b_val);
+
+        auto gru_cell = std::make_shared<opset4::GRUCell>(squeeze, Yi, W, R, B, 128);
+        auto res_1 = std::make_shared<opset4::Result>(gru_cell);
+        auto unsqueeze = std::make_shared<opset4::Unsqueeze>(gru_cell, axis);
+        auto res_2 = std::make_shared<opset4::Result>(unsqueeze);
+        auto body = std::make_shared<opset4::TensorIterator::BodyLambda>(OutputVector{res_1, res_2},
+                                                                         ParameterVector{Xi, Yi});
+
+        auto tensor_iterator = std::make_shared<opset4::TensorIterator>();
+        tensor_iterator->set_body(body);
+
+        tensor_iterator->set_sliced_input(Xi, X, 0, 1, 1, -1, 0);
+        tensor_iterator->set_merged_input(Yi, Y, res_1);
+
+        auto out0 = tensor_iterator->get_iter_value(res_1, -1);
+        auto out1 = tensor_iterator->get_concatenated_slices(res_2, 0, 1, 1, -1, 0);
+
+        auto res_ti_1 = std::make_shared<opset4::Result>(tensor_iterator->output(1));
+        //auto res_ti_2 = std::make_shared<opset4::Result>(tensor_iterator->output(0));
+        f = std::make_shared<ngraph::Function>(ngraph::NodeVector{res_ti_1},
+                ngraph::ParameterVector{X, Y});
+
+        ngraph::pass::Manager manager;
+        manager.register_pass<ngraph::pass::InitNodeInfo>();
+        manager.register_pass<ngraph::pass::UnrollTensorIterator>();
+        manager.run_passes(f);
+
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto X = std::make_shared<opset4::Parameter>(element::f32, Shape{2, 1, 16});
+        auto Y = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+
+        auto axis_split = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{}, {0});
+        auto split = std::make_shared<opset4::Split>(X, axis_split, 2);
+        auto axis = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{}, {0});
+        auto squeeze_1 = std::make_shared<opset4::Squeeze>(split->output(0), axis);
+        auto squeeze_2 = std::make_shared<opset4::Squeeze>(split->output(1), axis);
+
+        auto w_val = std::vector<float>(384*16, 0);
+        auto r_val = std::vector<float>(384*128, 0);
+        auto b_val = std::vector<float>(384, 0);
+        auto W = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{384, 16}, w_val);
+        auto R = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{384, 128}, r_val);
+        auto B = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{384}, b_val);
+
+        auto gru_cell_1 = std::make_shared<opset4::GRUCell>(squeeze_1, Y, W, R, B, 128);
+        auto gru_cell_2 = std::make_shared<opset4::GRUCell>(squeeze_2, gru_cell_1, W, R, B, 128);
+
+        auto unsqueeze_1 = std::make_shared<opset4::Unsqueeze>(gru_cell_1, axis);
+        auto unsqueeze_2 = std::make_shared<opset4::Unsqueeze>(gru_cell_2, axis);
+        auto concat = std::make_shared<opset4::Concat>(OutputVector{unsqueeze_1, unsqueeze_2}, 0);
+
+        auto res_ti_1 = std::make_shared<opset4::Result>(concat);
+        //auto res_ti_2 = std::make_shared<opset4::Result>(unsqueeze_2);
+        f_ref = std::make_shared<ngraph::Function>(ngraph::NodeVector{res_ti_1}, ngraph::ParameterVector{X, Y});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, UnrollTensorIteratorRNNCell) {
+    std::shared_ptr<ngraph::Function> f(nullptr), f_ref(nullptr);
+    {
+        auto X = std::make_shared<opset4::Parameter>(element::f32, Shape{2, 1, 16});
+        auto Y = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+
+        auto Xi = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1, 16});
+        auto Yi = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+
+        // Body
+        auto axis = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{}, {0});
+        auto squeeze = std::make_shared<opset4::Squeeze>(Xi, axis);
+
+        auto w_val = std::vector<float>(128*16, 0);
+        auto r_val = std::vector<float>(128*128, 0);
+        auto b_val = std::vector<float>(128, 0);
+        auto W = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{128, 16}, w_val);
+        auto R = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{128, 128}, r_val);
+        auto B = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{128}, b_val);
+
+        auto rnn_cell = std::make_shared<opset4::RNNCell>(squeeze, Yi, W, R, B, 128);
+        auto res_1 = std::make_shared<opset4::Result>(rnn_cell);
+        auto unsqueeze = std::make_shared<opset4::Unsqueeze>(rnn_cell, axis);
+        auto res_2 = std::make_shared<opset4::Result>(unsqueeze);
+        auto body = std::make_shared<opset4::TensorIterator::BodyLambda>(OutputVector{res_1, res_2},
+                                                                         ParameterVector{Xi, Yi});
+
+        auto tensor_iterator = std::make_shared<opset4::TensorIterator>();
+        tensor_iterator->set_body(body);
+
+        tensor_iterator->set_sliced_input(Xi, X, 0, 1, 1, -1, 0);
+        tensor_iterator->set_merged_input(Yi, Y, res_1);
+
+        auto out0 = tensor_iterator->get_iter_value(res_1, -1);
+        auto out1 = tensor_iterator->get_concatenated_slices(res_2, 0, 1, 1, -1, 0);
+
+        auto res_ti_1 = std::make_shared<opset4::Result>(tensor_iterator->output(1));
+        //auto res_ti_2 = std::make_shared<opset4::Result>(tensor_iterator->output(0));
+        f = std::make_shared<ngraph::Function>(ngraph::NodeVector{res_ti_1},
+                                               ngraph::ParameterVector{X, Y});
+
+        ngraph::pass::Manager manager;
+        manager.register_pass<ngraph::pass::InitNodeInfo>();
+        manager.register_pass<ngraph::pass::UnrollTensorIterator>();
+        manager.run_passes(f);
+
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto X = std::make_shared<opset4::Parameter>(element::f32, Shape{2, 1, 16});
+        auto Y = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+
+        auto axis_split = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{}, {0});
+        auto split = std::make_shared<opset4::Split>(X, axis_split, 2);
+        auto axis = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{}, {0});
+        auto squeeze_1 = std::make_shared<opset4::Squeeze>(split->output(0), axis);
+        auto squeeze_2 = std::make_shared<opset4::Squeeze>(split->output(1), axis);
+
+        auto w_val = std::vector<float>(128*16, 0);
+        auto r_val = std::vector<float>(128*128, 0);
+        auto b_val = std::vector<float>(128, 0);
+        auto W = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{128, 16}, w_val);
+        auto R = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{128, 128}, r_val);
+        auto B = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{128}, b_val);
+
+        auto rnn_cell_1 = std::make_shared<opset4::RNNCell>(squeeze_1, Y, W, R, B, 128);
+        auto rnn_cell_2 = std::make_shared<opset4::RNNCell>(squeeze_2, rnn_cell_1, W, R, B, 128);
+
+        auto unsqueeze_1 = std::make_shared<opset4::Unsqueeze>(rnn_cell_1, axis);
+        auto unsqueeze_2 = std::make_shared<opset4::Unsqueeze>(rnn_cell_2, axis);
+        auto concat = std::make_shared<opset4::Concat>(OutputVector{unsqueeze_1, unsqueeze_2}, 0);
+
+        auto res_ti_1 = std::make_shared<opset4::Result>(concat);
+        //auto res_ti_2 = std::make_shared<opset4::Result>(unsqueeze_2);
+        f_ref = std::make_shared<ngraph::Function>(ngraph::NodeVector{res_ti_1}, ngraph::ParameterVector{X, Y});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, UnrollTensorIteratorLSTMCell) {
+    std::shared_ptr<ngraph::Function> f(nullptr), f_ref(nullptr);
+    {
+        auto X = std::make_shared<opset4::Parameter>(element::f32, Shape{2, 1, 16});
+        auto Y = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+        auto Z = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+
+        auto Xi = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1, 16});
+        auto Yi = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+        auto Zi = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+
+        // Body
+        auto axis = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{}, {0});
+        auto squeeze = std::make_shared<opset4::Squeeze>(Xi, axis);
+
+        auto w_val = std::vector<float>(512*16, 0);
+        auto r_val = std::vector<float>(512*128, 0);
+        auto b_val = std::vector<float>(512, 0);
+        auto W = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{512, 16}, w_val);
+        auto R = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{512, 128}, r_val);
+        auto B = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{512}, b_val);
+
+        auto lstm_cell = std::make_shared<opset4::LSTMCell>(squeeze, Yi, Zi, W, R, B, 128);
+        auto res_1 = std::make_shared<opset4::Result>(lstm_cell);
+        auto unsqueeze = std::make_shared<opset4::Unsqueeze>(lstm_cell, axis);
+        auto res_2 = std::make_shared<opset4::Result>(unsqueeze);
+        auto body = std::make_shared<opset4::TensorIterator::BodyLambda>(OutputVector{res_1, res_2},
+                                                                         ParameterVector{Xi, Yi, Zi});
+
+        auto tensor_iterator = std::make_shared<opset4::TensorIterator>();
+        tensor_iterator->set_body(body);
+
+        tensor_iterator->set_invariant_input(Zi, Z);
+        tensor_iterator->set_sliced_input(Xi, X, 0, 1, 1, -1, 0);
+        tensor_iterator->set_merged_input(Yi, Y, res_1);
+
+        auto out0 = tensor_iterator->get_iter_value(res_1, -1);
+        auto out1 = tensor_iterator->get_concatenated_slices(res_2, 0, 1, 1, -1, 0);
+
+        auto res_ti_1 = std::make_shared<opset4::Result>(tensor_iterator->output(1));
+        //auto res_ti_2 = std::make_shared<opset4::Result>(tensor_iterator->output(0));
+        f = std::make_shared<ngraph::Function>(ngraph::NodeVector{res_ti_1},
+                                               ngraph::ParameterVector{X, Y, Z});
+
+        ngraph::pass::Manager manager;
+        manager.register_pass<ngraph::pass::InitNodeInfo>();
+        manager.register_pass<ngraph::pass::UnrollTensorIterator>();
+        manager.run_passes(f);
+
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto X = std::make_shared<opset4::Parameter>(element::f32, Shape{2, 1, 16});
+        auto Y = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+        auto Z = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+
+        auto axis_split = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{}, {0});
+        auto split = std::make_shared<opset4::Split>(X, axis_split, 2);
+        auto axis = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{}, {0});
+        auto squeeze_1 = std::make_shared<opset4::Squeeze>(split->output(0), axis);
+        auto squeeze_2 = std::make_shared<opset4::Squeeze>(split->output(1), axis);
+
+        auto w_val = std::vector<float>(512*16, 0);
+        auto r_val = std::vector<float>(512*128, 0);
+        auto b_val = std::vector<float>(512, 0);
+        auto W = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{512, 16}, w_val);
+        auto R = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{512, 128}, r_val);
+        auto B = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{512}, b_val);
+
+        auto lstm_cell_1 = std::make_shared<opset4::LSTMCell>(squeeze_1, Y, Z, W, R, B, 128);
+        auto lstm_cell_2 = std::make_shared<opset4::LSTMCell>(squeeze_2, lstm_cell_1, Z, W, R, B, 128);
+
+        auto unsqueeze_1 = std::make_shared<opset4::Unsqueeze>(lstm_cell_1, axis);
+        auto unsqueeze_2 = std::make_shared<opset4::Unsqueeze>(lstm_cell_2, axis);
+        auto concat = std::make_shared<opset4::Concat>(OutputVector{unsqueeze_1, unsqueeze_2}, 0);
+
+        auto res_ti_1 = std::make_shared<opset4::Result>(concat);
+        //auto res_ti_2 = std::make_shared<opset4::Result>(unsqueeze_2);
+        f_ref = std::make_shared<ngraph::Function>(ngraph::NodeVector{res_ti_1}, ngraph::ParameterVector{X, Y, Z});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, UnrollTensorIteratorGRUCellSingleIteration) {
+    std::shared_ptr<ngraph::Function> f(nullptr), f_ref(nullptr);
+    {
+        auto X = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1, 16});
+        auto Y = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+
+        auto Xi = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1, 16});
+        auto Yi = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+
+        // Body
+        auto axis = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{}, {0});
+        auto squeeze = std::make_shared<opset4::Squeeze>(Xi, axis);
+
+        auto w_val = std::vector<float>(384*16, 0);
+        auto r_val = std::vector<float>(384*128, 0);
+        auto b_val = std::vector<float>(384, 0);
+        auto W = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{384, 16}, w_val);
+        auto R = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{384, 128}, r_val);
+        auto B = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{384}, b_val);
+
+        auto gru_cell = std::make_shared<opset4::GRUCell>(squeeze, Yi, W, R, B, 128);
+        auto res_1 = std::make_shared<opset4::Result>(gru_cell);
+        auto unsqueeze = std::make_shared<opset4::Unsqueeze>(gru_cell, axis);
+        auto res_2 = std::make_shared<opset4::Result>(unsqueeze);
+        auto body = std::make_shared<opset4::TensorIterator::BodyLambda>(OutputVector{res_1, res_2},
+                                                                         ParameterVector{Xi, Yi});
+
+        auto tensor_iterator = std::make_shared<opset4::TensorIterator>();
+        tensor_iterator->set_body(body);
+
+        tensor_iterator->set_sliced_input(Xi, X, 0, 1, 1, -1, 0);
+        tensor_iterator->set_merged_input(Yi, Y, res_1);
+
+        auto out0 = tensor_iterator->get_iter_value(res_1, -1);
+        auto out1 = tensor_iterator->get_concatenated_slices(res_2, 0, 1, 1, -1, 0);
+
+        auto res_ti_1 = std::make_shared<opset4::Result>(tensor_iterator->output(1));
+        //auto res_ti_2 = std::make_shared<opset4::Result>(tensor_iterator->output(0));
+        f = std::make_shared<ngraph::Function>(ngraph::NodeVector{res_ti_1},
+                                               ngraph::ParameterVector{X, Y});
+
+        ngraph::pass::Manager manager;
+        manager.register_pass<ngraph::pass::InitNodeInfo>();
+        manager.register_pass<ngraph::pass::UnrollTensorIterator>();
+        manager.run_passes(f);
+
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto X = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1, 16});
+        auto Y = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+
+        auto axis = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{}, {0});
+        auto squeeze_1 = std::make_shared<opset4::Squeeze>(X, axis);
+
+        auto w_val = std::vector<float>(384*16, 0);
+        auto r_val = std::vector<float>(384*128, 0);
+        auto b_val = std::vector<float>(384, 0);
+        auto W = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{384, 16}, w_val);
+        auto R = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{384, 128}, r_val);
+        auto B = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{384}, b_val);
+
+        auto gru_cell_1 = std::make_shared<opset4::GRUCell>(squeeze_1, Y, W, R, B, 128);
+
+        auto unsqueeze_1 = std::make_shared<opset4::Unsqueeze>(gru_cell_1, axis);
+
+        auto res_ti_1 = std::make_shared<opset4::Result>(unsqueeze_1);
+        //auto res_ti_2 = std::make_shared<opset4::Result>(unsqueeze_2);
+        f_ref = std::make_shared<ngraph::Function>(ngraph::NodeVector{res_ti_1}, ngraph::ParameterVector{X, Y});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, UnrollTensorIteratorRNNCellSingleIteration) {
+    std::shared_ptr<ngraph::Function> f(nullptr), f_ref(nullptr);
+    {
+        auto X = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1, 16});
+        auto Y = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+
+        auto Xi = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1, 16});
+        auto Yi = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+
+        // Body
+        auto axis = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{}, {0});
+        auto squeeze = std::make_shared<opset4::Squeeze>(Xi, axis);
+
+        auto w_val = std::vector<float>(128*16, 0);
+        auto r_val = std::vector<float>(128*128, 0);
+        auto b_val = std::vector<float>(128, 0);
+        auto W = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{128, 16}, w_val);
+        auto R = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{128, 128}, r_val);
+        auto B = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{128}, b_val);
+
+        auto rnn_cell = std::make_shared<opset4::RNNCell>(squeeze, Yi, W, R, B, 128);
+        auto res_1 = std::make_shared<opset4::Result>(rnn_cell);
+        auto unsqueeze = std::make_shared<opset4::Unsqueeze>(rnn_cell, axis);
+        auto res_2 = std::make_shared<opset4::Result>(unsqueeze);
+        auto body = std::make_shared<opset4::TensorIterator::BodyLambda>(OutputVector{res_1, res_2},
+                                                                         ParameterVector{Xi, Yi});
+
+        auto tensor_iterator = std::make_shared<opset4::TensorIterator>();
+        tensor_iterator->set_body(body);
+
+        tensor_iterator->set_sliced_input(Xi, X, 0, 1, 1, -1, 0);
+        tensor_iterator->set_merged_input(Yi, Y, res_1);
+
+        auto out0 = tensor_iterator->get_iter_value(res_1, -1);
+        auto out1 = tensor_iterator->get_concatenated_slices(res_2, 0, 1, 1, -1, 0);
+
+        auto res_ti_1 = std::make_shared<opset4::Result>(tensor_iterator->output(1));
+        //auto res_ti_2 = std::make_shared<opset4::Result>(tensor_iterator->output(0));
+        f = std::make_shared<ngraph::Function>(ngraph::NodeVector{res_ti_1},
+                                               ngraph::ParameterVector{X, Y});
+
+        ngraph::pass::Manager manager;
+        manager.register_pass<ngraph::pass::InitNodeInfo>();
+        manager.register_pass<ngraph::pass::UnrollTensorIterator>();
+        manager.run_passes(f);
+
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto X = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1, 16});
+        auto Y = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+
+        auto axis = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{}, {0});
+        auto squeeze_1 = std::make_shared<opset4::Squeeze>(X, axis);
+
+        auto w_val = std::vector<float>(128*16, 0);
+        auto r_val = std::vector<float>(128*128, 0);
+        auto b_val = std::vector<float>(128, 0);
+        auto W = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{128, 16}, w_val);
+        auto R = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{128, 128}, r_val);
+        auto B = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{128}, b_val);
+
+        auto rnn_cell_1 = std::make_shared<opset4::RNNCell>(squeeze_1, Y, W, R, B, 128);
+
+        auto unsqueeze_1 = std::make_shared<opset4::Unsqueeze>(rnn_cell_1, axis);
+        auto res_ti_1 = std::make_shared<opset4::Result>(unsqueeze_1);
+
+        f_ref = std::make_shared<ngraph::Function>(ngraph::NodeVector{res_ti_1}, ngraph::ParameterVector{X, Y});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, UnrollTensorIteratorLSTMCellSingleIterationSingleIteration) {
+    std::shared_ptr<ngraph::Function> f(nullptr), f_ref(nullptr);
+    {
+        auto X = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1, 16});
+        auto Y = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+        auto Z = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+
+        auto Xi = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1, 16});
+        auto Yi = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+        auto Zi = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+
+        // Body
+        auto axis = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{}, {0});
+        auto squeeze = std::make_shared<opset4::Squeeze>(Xi, axis);
+
+        auto w_val = std::vector<float>(512*16, 0);
+        auto r_val = std::vector<float>(512*128, 0);
+        auto b_val = std::vector<float>(512, 0);
+        auto W = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{512, 16}, w_val);
+        auto R = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{512, 128}, r_val);
+        auto B = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{512}, b_val);
+
+        auto lstm_cell = std::make_shared<opset4::LSTMCell>(squeeze, Yi, Zi, W, R, B, 128);
+        auto res_1 = std::make_shared<opset4::Result>(lstm_cell);
+        auto unsqueeze = std::make_shared<opset4::Unsqueeze>(lstm_cell, axis);
+        auto res_2 = std::make_shared<opset4::Result>(unsqueeze);
+        auto body = std::make_shared<opset4::TensorIterator::BodyLambda>(OutputVector{res_1, res_2},
+                                                                         ParameterVector{Xi, Yi, Zi});
+
+        auto tensor_iterator = std::make_shared<opset4::TensorIterator>();
+        tensor_iterator->set_body(body);
+
+        tensor_iterator->set_invariant_input(Zi, Z);
+        tensor_iterator->set_sliced_input(Xi, X, 0, 1, 1, -1, 0);
+        tensor_iterator->set_merged_input(Yi, Y, res_1);
+
+        auto out0 = tensor_iterator->get_iter_value(res_1, -1);
+        auto out1 = tensor_iterator->get_concatenated_slices(res_2, 0, 1, 1, -1, 0);
+
+        auto res_ti_1 = std::make_shared<opset4::Result>(tensor_iterator->output(1));
+        //auto res_ti_2 = std::make_shared<opset4::Result>(tensor_iterator->output(0));
+        f = std::make_shared<ngraph::Function>(ngraph::NodeVector{res_ti_1},
+                                               ngraph::ParameterVector{X, Y, Z});
+
+        ngraph::pass::Manager manager;
+        manager.register_pass<ngraph::pass::InitNodeInfo>();
+        manager.register_pass<ngraph::pass::UnrollTensorIterator>();
+        manager.run_passes(f);
+
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto X = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1, 16});
+        auto Y = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+        auto Z = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 128});
+
+        auto axis = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{}, {0});
+        auto squeeze_1 = std::make_shared<opset4::Squeeze>(X, axis);
+
+        auto w_val = std::vector<float>(512*16, 0);
+        auto r_val = std::vector<float>(512*128, 0);
+        auto b_val = std::vector<float>(512, 0);
+        auto W = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{512, 16}, w_val);
+        auto R = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{512, 128}, r_val);
+        auto B = ngraph::opset4::Constant::create(ngraph::element::f32, ngraph::Shape{512}, b_val);
+
+        auto lstm_cell_1 = std::make_shared<opset4::LSTMCell>(squeeze_1, Y, Z, W, R, B, 128);
+
+        auto unsqueeze_1 = std::make_shared<opset4::Unsqueeze>(lstm_cell_1, axis);
+        auto res_ti_1 = std::make_shared<opset4::Result>(unsqueeze_1);
+        //auto res_ti_2 = std::make_shared<opset4::Result>(unsqueeze_2);
+        f_ref = std::make_shared<ngraph::Function>(ngraph::NodeVector{res_ti_1}, ngraph::ParameterVector{X, Y, Z});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
index 2a9d1e3..1a48712 100644 (file)
@@ -50,6 +50,7 @@ namespace ngraph
                    size_t node_output_number);
 
             const std::string& get_name() const;
+            void set_name(const std::string& name);
             void set_tensor_type(const element::Type& element_type, const PartialShape& pshape);
             void set_element_type(const element::Type& elemenet_type);
             void set_partial_shape(const PartialShape& partial_shape);
index 2f5e8b8..944d304 100644 (file)
@@ -43,6 +43,11 @@ descriptor::Tensor::Tensor(const element::Type& element_type,
 {
 }
 
+void descriptor::Tensor::set_name(const string& name)
+{
+    m_name = name;
+}
+
 void descriptor::Tensor::set_tensor_type(const element::Type& element_type,
                                          const PartialShape& pshape)
 {
@@ -121,11 +126,6 @@ void descriptor::Tensor::set_tensor_layout(
 
 const std::string& descriptor::Tensor::get_name() const
 {
-    if (m_name.empty() && m_node != nullptr)
-    {
-        const_cast<Tensor*>(this)->m_name =
-            m_node->get_name() + "_" + to_string(m_node_output_number);
-    }
     return m_name;
 }
 
index c15c391..3cbb43a 100644 (file)
@@ -78,7 +78,7 @@ void ngraph::traverse_nodes(const NodeVector& subgraph_results,
         stack.push(node_ptr.get());
     }
 
-    while (stack.size() > 0)
+    while (!stack.empty())
     {
         Node* n = stack.top();
         stack.pop();
@@ -117,7 +117,7 @@ NodeVector ngraph::find_common_args(std::shared_ptr<Node> node1, std::shared_ptr
     traverse_nodes({node2}, compute_node2_args, NodeVector{});
 
     NodeVector common_args;
-    for (auto e : node1_args)
+    for (const auto& e : node1_args)
     {
         if (node2_args.count(e) > 0)
         {
index e45adc2..e3bdb2a 100644 (file)
@@ -47,7 +47,7 @@ void op::util::FusedOp::validate_and_infer_types()
     validate_nodes_and_infer_types(subgraph);
 
     size_t i = 0;
-    for (auto output : subgraph_outputs)
+    for (const auto& output : subgraph_outputs)
     {
         if (i >= get_output_size())
         {