diff --git a/src/frontends/tensorflow/src/input_model.cpp b/src/frontends/tensorflow/src/input_model.cpp index 65672ae13a6..459ce69b546 100644 --- a/src/frontends/tensorflow/src/input_model.cpp +++ b/src/frontends/tensorflow/src/input_model.cpp @@ -149,7 +149,44 @@ void InputModel::InputModelTFImpl::load_places() { all_op_names.insert(op_name); m_op_places.push_back(op_place); m_op_places_map[op_name] = op_place; + + // compute non-terminating nodes in the graph + // and put such nodes into op_names_with_consumers + for (size_t input_port_idx = 0; input_port_idx < node_decoder->get_input_size(); ++input_port_idx) { + std::string producer_op_name; + std::string producer_output_port_name; + size_t producer_output_port_idx; + try { + node_decoder->get_input_node(input_port_idx, + producer_op_name, + producer_output_port_name, + producer_output_port_idx); + if (is_conditional_edge(producer_op_name)) { + // exclude "^" mark indicating (execution) conditional dependency + // for example, "^sub_op" means dependency on a producer node with a name "sub_op" + // if a node has dependent operation nodes and has no data consumers, + // this node is not terminating and will not output to the Result node + producer_op_name = producer_op_name.substr(1); + } + + op_names_with_consumers.insert(producer_op_name); + } catch (const std::exception&) { + FRONT_END_THROW("[ ERROR ] Exception happened when preparing input " + std::to_string(input_port_idx) + + " for op '" + node_decoder->get_op_name() + "', expected input name: '" + + producer_op_name + + "', expected input port index: " + std::to_string(producer_output_port_idx)); + } + } + + // put places for all inputs of a model into m_inputs if (op_type == "Placeholder" || op_type == "PlaceholderWithDefault") { + if (m_input_names.size() > 0 && + std::find(m_input_names.begin(), m_input_names.end(), op_name) == m_input_names.end()) { + // this is a body graph since it contains non-empty m_input_names + // such node not included into m_input_names should be skipped + continue; + } + // in case Placeholder we put created TensorPlace to both m_tensor_places container and m_inputs // since they can be used if user does not override them // in case PlaceholderWithDefault we put created TensorPlace only to m_tensor_places container @@ -199,6 +236,13 @@ void InputModel::InputModelTFImpl::load_places() { m_inputs.push_back(tensor_place); } } else if (op_type == "input_arg") { + if (m_input_names.size() > 0 && + std::find(m_input_names.begin(), m_input_names.end(), op_name) == m_input_names.end()) { + // this is a body graph since it contains non-empty m_input_names + // such node not included into m_input_names should be skipped + continue; + } + // create a tensor place for the body graph parameter node and save it in the m_inputs // it allows to set shapes for the body graph InputModel for its more optimal conversion auto param_type = node_decoder->get_attribute("type"); @@ -212,31 +256,6 @@ void InputModel::InputModelTFImpl::load_places() { std::vector{op_name}); m_inputs.push_back(tensor_place); } - for (size_t input_port_idx = 0; input_port_idx < node_decoder->get_input_size(); ++input_port_idx) { - std::string producer_op_name; - std::string producer_output_port_name; - size_t producer_output_port_idx; - try { - node_decoder->get_input_node(input_port_idx, - producer_op_name, - producer_output_port_name, - producer_output_port_idx); - if (is_conditional_edge(producer_op_name)) { - // exclude "^" mark indicating (execution) conditional dependency - // for example, "^sub_op" means dependency on a producer node with a name "sub_op" - // if a node has dependent operation nodes and has no data consumers, - // this node is not terminating and will not output to the Result node - producer_op_name = producer_op_name.substr(1); - } - - op_names_with_consumers.insert(producer_op_name); - } catch (const std::exception&) { - FRONT_END_THROW("[ ERROR ] Exception happened when preparing input " + std::to_string(input_port_idx) + - " for op '" + node_decoder->get_op_name() + "', expected input name: '" + - producer_op_name + - "', expected input port index: " + std::to_string(producer_output_port_idx)); - } - } } if (m_telemetry) { diff --git a/src/frontends/tensorflow/src/op/sparse_reshape.cpp b/src/frontends/tensorflow/src/op/sparse_reshape.cpp index 10f830cf4b2..1def5f4192f 100644 --- a/src/frontends/tensorflow/src/op/sparse_reshape.cpp +++ b/src/frontends/tensorflow/src/op/sparse_reshape.cpp @@ -70,20 +70,29 @@ OutputVector translate_sparse_reshape_op(const ov::frontend::tensorflow::NodeCon return {input_indices, input_shape}; } -OutputVector translate_sparse_fill_empty_rows_op(const ov::frontend::tensorflow::NodeContext& node) { +NamedOutputVector translate_sparse_fill_empty_rows_op(const ov::frontend::tensorflow::NodeContext& node) { default_op_checks(node, 3, {"SparseFillEmptyRows"}); auto input_indices = node.get_input(0); auto input_values = node.get_input(1); auto dense_shape = node.get_input(2); auto default_value = node.get_input(3); + auto node_name = node.get_name(); auto sparse_fill_empty_rows = make_shared(input_indices, input_values, dense_shape, default_value, node.get_decoder()); - set_node_name(node.get_name(), sparse_fill_empty_rows); - return sparse_fill_empty_rows->outputs(); + sparse_fill_empty_rows->set_friendly_name(node_name); + set_out_name(node_name + ":0", sparse_fill_empty_rows->output(0)); + set_out_name(node_name + ":1", sparse_fill_empty_rows->output(1)); + set_out_name(node_name + ":2", sparse_fill_empty_rows->output(2)); + set_out_name(node_name + ":3", sparse_fill_empty_rows->output(3)); + + return {{"output_indices", sparse_fill_empty_rows->output(0)}, + {"output_values", sparse_fill_empty_rows->output(1)}, + {"empty_row_indicator", sparse_fill_empty_rows->output(2)}, + {"reverse_index_map", sparse_fill_empty_rows->output(3)}}; } OutputVector translate_sparse_segment_sum_op(const ov::frontend::tensorflow::NodeContext& node) { diff --git a/src/frontends/tensorflow/src/op_table.cpp b/src/frontends/tensorflow/src/op_table.cpp index 3a4c570c657..3251ad81b0f 100644 --- a/src/frontends/tensorflow/src/op_table.cpp +++ b/src/frontends/tensorflow/src/op_table.cpp @@ -18,7 +18,8 @@ namespace frontend { namespace tensorflow { namespace op { -#define TF_OP_CONVERTER(op) OutputVector op(const ov::frontend::tensorflow::NodeContext& node) +#define TF_OP_CONVERTER(op) OutputVector op(const ov::frontend::tensorflow::NodeContext& node) +#define TF_OP_CONVERTER_NAMED(op) NamedOutputVector op(const ov::frontend::tensorflow::NodeContext& node) TF_OP_CONVERTER(translate_assignvariable_op); TF_OP_CONVERTER(translate_block_lstm_op); @@ -40,7 +41,7 @@ TF_OP_CONVERTER(translate_queue_dequeue_op); TF_OP_CONVERTER(translate_queue_dequeue_many_op); TF_OP_CONVERTER(translate_readvariable_op); TF_OP_CONVERTER(translate_restorev2_op); -TF_OP_CONVERTER(translate_sparse_fill_empty_rows_op); +TF_OP_CONVERTER_NAMED(translate_sparse_fill_empty_rows_op); TF_OP_CONVERTER(translate_sparse_reshape_op); TF_OP_CONVERTER(translate_sparse_segment_sum_op); TF_OP_CONVERTER(translate_staticregexfullmatch_op); diff --git a/src/frontends/tensorflow/src/translate_session.cpp b/src/frontends/tensorflow/src/translate_session.cpp index 51f0addb7c3..4038995c6cb 100644 --- a/src/frontends/tensorflow/src/translate_session.cpp +++ b/src/frontends/tensorflow/src/translate_session.cpp @@ -337,6 +337,7 @@ void TranslateSession::translate_graph(const ov::frontend::InputModel::Ptr& inpu const auto& model_frozen_inputs = model_tf->get_tensor_values(); const auto& saved_model_inputs = model_tf->get_saved_model_input_names(); const auto& saved_model_outputs = model_tf->get_saved_model_output_names(); + bool is_body_graph = (model_tf->get_input_names().size() > 0); // fill ng_op_map with Constant outputs for frozen inputs for (const auto& frozen_input : model_frozen_inputs) { @@ -532,7 +533,7 @@ void TranslateSession::translate_graph(const ov::frontend::InputModel::Ptr& inpu } else { auto param = as_type_ptr(output.port.get_node_shared_ptr()); // avoid duplicating Parameter nodes if they are already in the Parameters vector - if (param && std::find(params.begin(), params.end(), param) == params.end()) { + if (param && std::find(params.begin(), params.end(), param) == params.end() && !is_body_graph) { params.push_back(param); } ng_op_map[operation_name].push_back(output); diff --git a/src/frontends/tensorflow_common/src/op/cumsum.cpp b/src/frontends/tensorflow_common/src/op/cumsum.cpp index cfffbb0c0b8..0fcf0f2da04 100644 --- a/src/frontends/tensorflow_common/src/op/cumsum.cpp +++ b/src/frontends/tensorflow_common/src/op/cumsum.cpp @@ -3,10 +3,10 @@ // #include "common_op_table.hpp" -#include "openvino/opsets/opset8.hpp" +#include "openvino/op/cum_sum.hpp" using namespace std; -using namespace ov::opset8; +using namespace ov::op; namespace ov { namespace frontend { @@ -14,14 +14,15 @@ namespace tensorflow { namespace op { OutputVector translate_cumsum_op(const NodeContext& node) { - auto ng_x = node.get_input(0); - auto ng_axis = node.get_input(1); - auto exclusive = node.get_attribute("exclusive"); - auto reverse = node.get_attribute("reverse"); + default_op_checks(node, 2, {"Cumsum"}); + auto x = node.get_input(0); + auto axis = node.get_input(1); + auto exclusive = node.get_attribute("exclusive", false); + auto reverse = node.get_attribute("reverse", false); - auto res = make_shared(ng_x, ng_axis, exclusive, reverse); - set_node_name(node.get_name(), res); - return res->outputs(); + auto cum_sum = make_shared(x, axis, exclusive, reverse); + set_node_name(node.get_name(), cum_sum); + return cum_sum->outputs(); } } // namespace op } // namespace tensorflow diff --git a/tests/layer_tests/tensorflow_tests/test_tf_Cumsum.py b/tests/layer_tests/tensorflow_tests/test_tf_Cumsum.py index 7379d08a600..79bb756d4a0 100644 --- a/tests/layer_tests/tensorflow_tests/test_tf_Cumsum.py +++ b/tests/layer_tests/tensorflow_tests/test_tf_Cumsum.py @@ -6,24 +6,16 @@ import pytest from common.tf_layer_test_class import CommonTFLayerTest + # Testing Cumsum operation # Documentation: https://www.tensorflow.org/api_docs/python/tf/raw_ops/Cumsum -class TestCumsumOps(CommonTFLayerTest): +class TestCumsum(CommonTFLayerTest): # input_shape - should be an array # axis - array which points on axis for the operation # exclusive - enables exclusive Cumsum # reverse - enables reverse order of Cumsum - # ir_version - common parameter - # use_new_frontend - common parameter - def create_cumsum_ops_placeholder_const_net(self, input_shape, axis, exclusive, reverse, ir_version, use_new_frontend): - """ - Tensorflow net IR net - - Placeholder->Cumsum => Placeholder->Cumsum - - """ - + def create_cumsum_net(self, input_shape, axis, exclusive, reverse): import tensorflow as tf tf.compat.v1.reset_default_graph() @@ -31,9 +23,9 @@ class TestCumsumOps(CommonTFLayerTest): # Create the graph and model with tf.compat.v1.Session() as sess: tf_input = tf.compat.v1.placeholder(tf.float32, input_shape, 'Input') - tf_axis = tf.constant(axis) - tf.raw_ops.Cumsum(x = tf_input, axis = tf_axis, exclusive = exclusive, reverse = reverse) + tf_axis = tf.constant(axis, dtype=tf.int32) + tf.raw_ops.Cumsum(x=tf_input, axis=tf_axis, exclusive=exclusive, reverse=reverse) tf.compat.v1.global_variables_initializer() tf_net = sess.graph_def @@ -43,20 +35,22 @@ class TestCumsumOps(CommonTFLayerTest): return tf_net, ref_net test_data = [ - pytest.param( - dict(input_shape=[2, 3], axis=1), - marks=pytest.mark.precommit_tf_fe), + dict(input_shape=[2], axis=-1), + dict(input_shape=[2, 3], axis=0), + dict(input_shape=[2, 3], axis=1), + dict(input_shape=[2, 3], axis=-2), dict(input_shape=[2, 3, 3, 4], axis=2), + dict(input_shape=[2, 3, 3, 4], axis=-3), ] @pytest.mark.parametrize("params", test_data) - @pytest.mark.parametrize("exclusive", [False, True]) - @pytest.mark.parametrize("reverse", [False, True]) + @pytest.mark.parametrize("exclusive", [False, True, None]) + @pytest.mark.parametrize("reverse", [False, True, None]) @pytest.mark.precommit + @pytest.mark.precommit_tf_fe @pytest.mark.nightly - def test_cumsum_ops_placeholder_const(self, params, exclusive, reverse, ie_device, precision, ir_version, temp_dir, - use_new_frontend, use_old_api): - self._test(*self.create_cumsum_ops_placeholder_const_net(**params, exclusive=exclusive, ir_version=ir_version, - use_new_frontend=use_new_frontend, reverse=reverse), + def test_cumsum_basic(self, params, exclusive, reverse, ie_device, precision, ir_version, temp_dir, + use_new_frontend, use_old_api): + self._test(*self.create_cumsum_net(**params, exclusive=exclusive, reverse=reverse), ie_device, precision, ir_version, temp_dir=temp_dir, use_new_frontend=use_new_frontend, use_old_api=use_old_api)