From 2c43725e88aa5746504906d35e0471201c943846 Mon Sep 17 00:00:00 2001 From: Svetlana Dolinina Date: Tue, 29 Dec 2020 10:29:01 +0300 Subject: [PATCH 01/12] added methods for parameters --- ngraph/core/include/ngraph/function.hpp | 10 ++++++++++ ngraph/core/src/function.cpp | 14 ++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/ngraph/core/include/ngraph/function.hpp b/ngraph/core/include/ngraph/function.hpp index 06fad214f1d8e7..4c2ed2f1e0d252 100644 --- a/ngraph/core/include/ngraph/function.hpp +++ b/ngraph/core/include/ngraph/function.hpp @@ -170,6 +170,16 @@ namespace ngraph /// \param result Result node to delete void remove_result(const std::shared_ptr& result); + /// \brief Add new Parameter nodes to the list. Method doesn't validate graph, it should be + /// done manually after all changes. + /// \param params new Parameter nodes + void add_parameters(const ParameterVector& params); + + /// \brief Delete Parameter node from the list of parameters. Method will not delete node from + /// graph. + /// \param param Parameter node to delete + void remove_parameter(const std::shared_ptr& param); + private: Function(const Function&) = delete; Function(const Function&&) = delete; diff --git a/ngraph/core/src/function.cpp b/ngraph/core/src/function.cpp index aa267222b2165a..23d36f64ce039b 100644 --- a/ngraph/core/src/function.cpp +++ b/ngraph/core/src/function.cpp @@ -405,4 +405,18 @@ void Function::remove_result(const std::shared_ptr& result) m_results.end()); } +void Function::add_parameters(const ParameterVector& params) +{ + m_parameters.insert(m_parameters.end(), params.begin(), params.end()); +} + +void Function::remove_parameter(const std::shared_ptr& param) +{ + m_parameters.erase( + std::remove_if(m_parameters.begin(), + m_parameters.end(), + [¶m](std::shared_ptr& r) { return r == param; }), + m_parameters.end()); +} + constexpr DiscreteTypeInfo AttributeAdapter>::type_info; From 64fea173b0aef55a9d10d147544970502d5c51a4 Mon Sep 17 00:00:00 2001 From: Svetlana Dolinina Date: Wed, 13 Jan 2021 00:57:25 +0300 Subject: [PATCH 02/12] added tests --- ngraph/test/build_graph.cpp | 55 +++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/ngraph/test/build_graph.cpp b/ngraph/test/build_graph.cpp index f43fa79bd12423..e3401d3125c48a 100644 --- a/ngraph/test/build_graph.cpp +++ b/ngraph/test/build_graph.cpp @@ -363,3 +363,58 @@ TEST(build_graph, build_graph_with_remove_result) nodes = f->get_ops(); EXPECT_EQ(nodes.size(), 5); } + +TEST(build_graph, build_graph_with_add_parameter) +{ + auto arg = make_shared(element::f32, Shape{2, 4}); + auto arg2 = make_shared(element::f32, Shape{2, 8}); + auto init_const = op::Constant::create(element::f32, Shape{2, 2}, {0, 0, 0, 0}); + auto read = make_shared(init_const, "v0"); + std::vector> args = {arg, read}; + auto pattern = make_shared(args, 1); + auto res = make_shared(pattern); + const auto axis = op::Constant::create(element::i64, Shape{}, {1}); + auto crop = make_shared(pattern, axis, 3); + auto res2 = make_shared(crop, "v0"); + + auto f = make_shared(ResultVector({res, res2}), ParameterVector{arg}); + + NodeVector nodes = f->get_ops(); + EXPECT_EQ(nodes.size(), 8); + ParameterVector params = f->get_parameters(); + EXPECT_EQ(params.size(), 1); + + f->add_parameters(ParameterVector({arg2})); + params = f->get_parameters(); + EXPECT_EQ(params.size(), 2); + EXPECT_EQ(params[1], arg2); + nodes = f->get_ops(); + EXPECT_EQ(nodes.size(), 9); +} + +TEST(build_graph, build_graph_with_remove_parameter) +{ + auto arg = make_shared(element::f32, Shape{2, 4}); + auto arg2 = make_shared(element::f32, Shape{2, 8}); + auto init_const = op::Constant::create(element::f32, Shape{2, 2}, {0, 0, 0, 0}); + auto read = make_shared(init_const, "v0"); + std::vector> args = {arg, read}; + auto pattern = make_shared(args, 1); + auto res = make_shared(pattern); + const auto axis = op::Constant::create(element::i64, Shape{}, {1}); + auto crop = make_shared(pattern, axis, 3); + auto res2 = make_shared(crop, "v0"); + + auto f = make_shared(ResultVector({res, res2}), ParameterVector{arg, arg2}); + + NodeVector nodes = f->get_ops(); + EXPECT_EQ(nodes.size(), 9); + ParameterVector params = f->get_parameters(); + EXPECT_EQ(params.size(), 2); + + f->remove_parameter(arg2); + params = f->get_parameters(); + EXPECT_EQ(params.size(), 1); + nodes = f->get_ops(); + EXPECT_EQ(nodes.size(), 8); +} From 86a91ddcb0c2fb61ba7b0816027fc7ee6ef94dbb Mon Sep 17 00:00:00 2001 From: Svetlana Dolinina Date: Thu, 14 Jan 2021 15:42:24 +0300 Subject: [PATCH 03/12] remove parameter from low latency --- .../transformations/low_latency_test.cpp | 74 ++++++++++++++++--- ngraph/core/src/pass/low_latency.cpp | 40 +++++++++- 2 files changed, 101 insertions(+), 13 deletions(-) diff --git a/inference-engine/tests/functional/inference_engine/transformations/low_latency_test.cpp b/inference-engine/tests/functional/inference_engine/transformations/low_latency_test.cpp index 3d15d463367b84..c5e8b94049b4ed 100644 --- a/inference-engine/tests/functional/inference_engine/transformations/low_latency_test.cpp +++ b/inference-engine/tests/functional/inference_engine/transformations/low_latency_test.cpp @@ -74,8 +74,8 @@ TEST(TransformationTests, LowLatencyLSTM) { } { auto Xi = std::make_shared(element::f32, Shape{1, 1, 16}); - auto H_t = std::make_shared(element::f32, Shape{1, 128}); - auto C_t = std::make_shared(element::f32, Shape{1, 128}); + auto H_t = std::make_shared(element::f32, Shape{1, 128}, std::vector(128, 0)); + auto C_t = std::make_shared(element::f32, Shape{1, 128}, std::vector(128, 0)); const std::string variable_name_H("LSTMTensorIterator/variable0"); const std::string variable_name_C("LSTMTensorIterator/variable1"); @@ -98,7 +98,7 @@ TEST(TransformationTests, LowLatencyLSTM) { auto unsqueeze = std::make_shared(lstm_cell->output(0), axis); auto res_2 = std::make_shared(unsqueeze); auto res_1 = std::make_shared(lstm_cell->output(0)); - f_ref = std::make_shared(OutputVector{res_1, res_2}, ParameterVector{Xi, H_t, C_t}); + f_ref = std::make_shared(OutputVector{res_1, res_2}, ParameterVector{Xi}); f_ref->add_sinks({assign_C, assign_H}); assign_H->add_control_dependency(read_value_H); assign_C->add_control_dependency(read_value_C); @@ -155,7 +155,7 @@ TEST(TransformationTests, LowLatencyGRU) { } { auto Xi = std::make_shared(element::f32, Shape{1, 1, 16}); - auto H_t = std::make_shared(element::f32, Shape{1, 128}); + auto H_t = std::make_shared(element::f32, Shape{1, 128}, std::vector(128, 0)); const std::string variable_name_H("GRUTensorIterator/variable0"); auto read_value_H = std::make_shared(H_t, variable_name_H); @@ -175,7 +175,7 @@ TEST(TransformationTests, LowLatencyGRU) { auto res_1 = std::make_shared(assign_H); auto unsqueeze = std::make_shared(rnn_cell->output(0), axis); auto res_2 = std::make_shared(unsqueeze); - f_ref = std::make_shared(OutputVector{unsqueeze}, ParameterVector{Xi, H_t}); + f_ref = std::make_shared(OutputVector{unsqueeze}, ParameterVector{Xi}); f_ref->add_sinks({assign_H}); assign_H->add_control_dependency(read_value_H); } @@ -232,7 +232,7 @@ TEST(TransformationTests, LowLatencyRNN) { } { auto Xi = std::make_shared(element::f32, Shape{1, 1, 16}); - auto H_t = std::make_shared(element::f32, Shape{1, 128}); + auto H_t = std::make_shared(element::f32, Shape{1, 128}, std::vector(128, 0)); const std::string variable_name_H("RNNTensorIterator/variable0"); auto read_value_H = std::make_shared(H_t, variable_name_H); @@ -252,7 +252,7 @@ TEST(TransformationTests, LowLatencyRNN) { auto res_1 = std::make_shared(assign_H); auto unsqueeze = std::make_shared(rnn_cell->output(0), axis); auto res_2 = std::make_shared(unsqueeze); - f_ref = std::make_shared(OutputVector{unsqueeze}, ParameterVector{Xi, H_t}); + f_ref = std::make_shared(OutputVector{unsqueeze}, ParameterVector{Xi}); f_ref->add_sinks({assign_H}); assign_H->add_control_dependency(read_value_H); } @@ -319,8 +319,8 @@ TEST(TransformationTests, LowLatencyLSTMReshape) { } { auto Xi = std::make_shared(element::f32, Shape{1, 1, 16}); - auto H_t = std::make_shared(element::f32, Shape{1, 128}); - auto C_t = std::make_shared(element::f32, Shape{1, 128}); + auto H_t = std::make_shared(element::f32, Shape{1, 128}, std::vector(128, 0)); + auto C_t = std::make_shared(element::f32, Shape{1, 128}, std::vector(128, 0)); const std::string variable_name_H("LSTMTensorIterator/variable0"); const std::string variable_name_C("LSTMTensorIterator/variable1"); @@ -343,7 +343,7 @@ TEST(TransformationTests, LowLatencyLSTMReshape) { auto unsqueeze = std::make_shared(lstm_cell->output(0), axis); auto res_2 = std::make_shared(unsqueeze); auto res_1 = std::make_shared(lstm_cell->output(0)); - f_ref = std::make_shared(OutputVector{res_1, res_2}, ParameterVector{Xi, H_t, C_t}); + f_ref = std::make_shared(OutputVector{res_1, res_2}, ParameterVector{Xi}); f_ref->add_sinks({assign_C, assign_H}); assign_H->add_control_dependency(read_value_H); assign_C->add_control_dependency(read_value_C); @@ -351,3 +351,57 @@ TEST(TransformationTests, LowLatencyLSTMReshape) { auto res = compare_functions(f, f_ref); ASSERT_TRUE(res.first) << res.second; } + +TEST(TransformationTests, LowLatencyLSTM_3dinput) { + std::shared_ptr f(nullptr), f_ref(nullptr); + + auto X = std::make_shared(element::f32, Shape{1, 1, 16}); + auto H_init = std::make_shared(element::f32, Shape{1, 128}); + auto C_init = std::make_shared(element::f32, Shape{1, 128}); + + auto Xi = std::make_shared(element::f32, Shape{1, 1, 16}); + auto H_t = std::make_shared(element::f32, Shape{1, 128}); + auto C_t = std::make_shared(element::f32, Shape{1, 128}); + + // Body + auto axis = ngraph::opset5::Constant::create(ngraph::element::i64, ngraph::Shape{}, {0}); + auto squeeze = std::make_shared(Xi, axis); + + auto w_val = std::vector(512 * 16, 0); + auto r_val = std::vector(512 * 128, 0); + auto b_val = std::vector(512, 0); + auto W = ngraph::opset5::Constant::create(ngraph::element::f32, ngraph::Shape{512, 16}, w_val); + auto R = ngraph::opset5::Constant::create(ngraph::element::f32, ngraph::Shape{512, 128}, r_val); + auto B = ngraph::opset5::Constant::create(ngraph::element::f32, ngraph::Shape{512}, b_val); + + auto lstm_cell = std::make_shared(squeeze, H_t, C_t, W, R, B, 128); + auto res_1 = std::make_shared(lstm_cell->output(0)); + auto unsqueeze = std::make_shared(lstm_cell->output(0), axis); + auto res_2 = std::make_shared(unsqueeze); + auto res_3 = std::make_shared(lstm_cell->output(1)); + auto body = std::make_shared(OutputVector{res_1, res_2, res_3}, ParameterVector{H_t, Xi, C_t}); + + auto tensor_iterator = std::make_shared(); + tensor_iterator->set_body(body); + tensor_iterator->set_friendly_name("LSTMTensorIterator"); + + tensor_iterator->set_merged_input(C_t, C_init, res_3); + tensor_iterator->set_sliced_input(Xi, X, 0, 1, 1, -1, 0); + tensor_iterator->set_merged_input(H_t, H_init, 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(tensor_iterator->output(1)); + auto res_ti_2 = std::make_shared(tensor_iterator->output(0)); + f = std::make_shared(ngraph::NodeVector{res_ti_1, res_ti_2}, + ngraph::ParameterVector{X, H_init, C_init}); + + ngraph::pass::Manager manager; + manager.register_pass(); + manager.register_pass(); + manager.run_passes(f); + + ASSERT_EQ(body->get_parameters().size(), 1); + ASSERT_EQ(tensor_iterator->get_input_descriptions()[0]->m_body_parameter_index, 0); +} diff --git a/ngraph/core/src/pass/low_latency.cpp b/ngraph/core/src/pass/low_latency.cpp index 89a7a73c4996b2..772135bb9db790 100644 --- a/ngraph/core/src/pass/low_latency.cpp +++ b/ngraph/core/src/pass/low_latency.cpp @@ -9,6 +9,7 @@ #include #include #include +#include NGRAPH_RTTI_DEFINITION(ngraph::pass::LowLatency, "LowLatency", 0); @@ -29,7 +30,9 @@ ngraph::pass::LowLatency::LowLatency() int64_t variable_id = 0; std::vector> assigns; const auto& func = ti->get_function(); - for (const auto& in : ti->get_input_descriptions()) + auto in_descs = ti->get_input_descriptions(); + std::vector inputs_ind_to_delete; + for (const auto& in : in_descs) { // Process all back edges if (const auto& merged_in = std::dynamic_pointer_cast< @@ -44,8 +47,14 @@ ngraph::pass::LowLatency::LowLatency() .at(merged_in->m_body_parameter_index) ->get_friendly_name() + "/variable_" + std::to_string(variable_id)); - auto read_value = std::make_shared( - func->get_parameters().at(merged_in->m_body_parameter_index), variable_name); + auto init_shape = func->get_parameters().at(merged_in->m_body_parameter_index)->get_partial_shape().get_shape(); + int zeros_length = 1.0; + for (auto i : init_shape){ + zeros_length *= i; + } + std::vector zeros(zeros_length, 0); + auto init_const = op::Constant::create(element::f32, init_shape, zeros); + auto read_value = std::make_shared(init_const, variable_name); read_value->set_friendly_name(variable_name); for (const auto& input_to : inputs_to) { @@ -58,9 +67,34 @@ ngraph::pass::LowLatency::LowLatency() // control dependency so that ReadValue is processed before Assign assign->add_control_dependency(read_value); assigns.emplace_back(assign); + // save index of input to delete + inputs_ind_to_delete.push_back(merged_in->m_body_parameter_index); } variable_id++; + } + + std::sort(inputs_ind_to_delete.begin(), inputs_ind_to_delete.end()); + auto params = func->get_parameters(); + for (int i=inputs_ind_to_delete.size()-1; i>=0; i--){ + func->remove_parameter(params[inputs_ind_to_delete[i]]); + } + + // remove replaced inputs from descriptions + ti->get_input_descriptions().erase(remove_if(ti->get_input_descriptions().begin(), ti->get_input_descriptions().end(), + [](ngraph::op::util::InputDescriptionPtr it){ + return std::dynamic_pointer_cast(it); + }), ti->get_input_descriptions().end()); + + // fix indexes for other inputs + for (const auto& in : in_descs) + { + int already_deleted = 0; + while(in->m_body_parameter_index > inputs_ind_to_delete[already_deleted]){ + already_deleted++; + } + in->m_body_parameter_index -= already_deleted; } + // save Assign in the func so that it gets into graph traversals and isn't deleted. func->add_sinks(assigns); return false; From 909ed960066ce5e83744f47aa510504a05d18132 Mon Sep 17 00:00:00 2001 From: Svetlana Dolinina Date: Thu, 14 Jan 2021 16:50:30 +0300 Subject: [PATCH 04/12] Revert "remove parameter from low latency" This reverts commit 86a91ddcb0c2fb61ba7b0816027fc7ee6ef94dbb. --- .../transformations/low_latency_test.cpp | 74 +++---------------- ngraph/core/src/pass/low_latency.cpp | 40 +--------- 2 files changed, 13 insertions(+), 101 deletions(-) diff --git a/inference-engine/tests/functional/inference_engine/transformations/low_latency_test.cpp b/inference-engine/tests/functional/inference_engine/transformations/low_latency_test.cpp index c5e8b94049b4ed..3d15d463367b84 100644 --- a/inference-engine/tests/functional/inference_engine/transformations/low_latency_test.cpp +++ b/inference-engine/tests/functional/inference_engine/transformations/low_latency_test.cpp @@ -74,8 +74,8 @@ TEST(TransformationTests, LowLatencyLSTM) { } { auto Xi = std::make_shared(element::f32, Shape{1, 1, 16}); - auto H_t = std::make_shared(element::f32, Shape{1, 128}, std::vector(128, 0)); - auto C_t = std::make_shared(element::f32, Shape{1, 128}, std::vector(128, 0)); + auto H_t = std::make_shared(element::f32, Shape{1, 128}); + auto C_t = std::make_shared(element::f32, Shape{1, 128}); const std::string variable_name_H("LSTMTensorIterator/variable0"); const std::string variable_name_C("LSTMTensorIterator/variable1"); @@ -98,7 +98,7 @@ TEST(TransformationTests, LowLatencyLSTM) { auto unsqueeze = std::make_shared(lstm_cell->output(0), axis); auto res_2 = std::make_shared(unsqueeze); auto res_1 = std::make_shared(lstm_cell->output(0)); - f_ref = std::make_shared(OutputVector{res_1, res_2}, ParameterVector{Xi}); + f_ref = std::make_shared(OutputVector{res_1, res_2}, ParameterVector{Xi, H_t, C_t}); f_ref->add_sinks({assign_C, assign_H}); assign_H->add_control_dependency(read_value_H); assign_C->add_control_dependency(read_value_C); @@ -155,7 +155,7 @@ TEST(TransformationTests, LowLatencyGRU) { } { auto Xi = std::make_shared(element::f32, Shape{1, 1, 16}); - auto H_t = std::make_shared(element::f32, Shape{1, 128}, std::vector(128, 0)); + auto H_t = std::make_shared(element::f32, Shape{1, 128}); const std::string variable_name_H("GRUTensorIterator/variable0"); auto read_value_H = std::make_shared(H_t, variable_name_H); @@ -175,7 +175,7 @@ TEST(TransformationTests, LowLatencyGRU) { auto res_1 = std::make_shared(assign_H); auto unsqueeze = std::make_shared(rnn_cell->output(0), axis); auto res_2 = std::make_shared(unsqueeze); - f_ref = std::make_shared(OutputVector{unsqueeze}, ParameterVector{Xi}); + f_ref = std::make_shared(OutputVector{unsqueeze}, ParameterVector{Xi, H_t}); f_ref->add_sinks({assign_H}); assign_H->add_control_dependency(read_value_H); } @@ -232,7 +232,7 @@ TEST(TransformationTests, LowLatencyRNN) { } { auto Xi = std::make_shared(element::f32, Shape{1, 1, 16}); - auto H_t = std::make_shared(element::f32, Shape{1, 128}, std::vector(128, 0)); + auto H_t = std::make_shared(element::f32, Shape{1, 128}); const std::string variable_name_H("RNNTensorIterator/variable0"); auto read_value_H = std::make_shared(H_t, variable_name_H); @@ -252,7 +252,7 @@ TEST(TransformationTests, LowLatencyRNN) { auto res_1 = std::make_shared(assign_H); auto unsqueeze = std::make_shared(rnn_cell->output(0), axis); auto res_2 = std::make_shared(unsqueeze); - f_ref = std::make_shared(OutputVector{unsqueeze}, ParameterVector{Xi}); + f_ref = std::make_shared(OutputVector{unsqueeze}, ParameterVector{Xi, H_t}); f_ref->add_sinks({assign_H}); assign_H->add_control_dependency(read_value_H); } @@ -319,8 +319,8 @@ TEST(TransformationTests, LowLatencyLSTMReshape) { } { auto Xi = std::make_shared(element::f32, Shape{1, 1, 16}); - auto H_t = std::make_shared(element::f32, Shape{1, 128}, std::vector(128, 0)); - auto C_t = std::make_shared(element::f32, Shape{1, 128}, std::vector(128, 0)); + auto H_t = std::make_shared(element::f32, Shape{1, 128}); + auto C_t = std::make_shared(element::f32, Shape{1, 128}); const std::string variable_name_H("LSTMTensorIterator/variable0"); const std::string variable_name_C("LSTMTensorIterator/variable1"); @@ -343,7 +343,7 @@ TEST(TransformationTests, LowLatencyLSTMReshape) { auto unsqueeze = std::make_shared(lstm_cell->output(0), axis); auto res_2 = std::make_shared(unsqueeze); auto res_1 = std::make_shared(lstm_cell->output(0)); - f_ref = std::make_shared(OutputVector{res_1, res_2}, ParameterVector{Xi}); + f_ref = std::make_shared(OutputVector{res_1, res_2}, ParameterVector{Xi, H_t, C_t}); f_ref->add_sinks({assign_C, assign_H}); assign_H->add_control_dependency(read_value_H); assign_C->add_control_dependency(read_value_C); @@ -351,57 +351,3 @@ TEST(TransformationTests, LowLatencyLSTMReshape) { auto res = compare_functions(f, f_ref); ASSERT_TRUE(res.first) << res.second; } - -TEST(TransformationTests, LowLatencyLSTM_3dinput) { - std::shared_ptr f(nullptr), f_ref(nullptr); - - auto X = std::make_shared(element::f32, Shape{1, 1, 16}); - auto H_init = std::make_shared(element::f32, Shape{1, 128}); - auto C_init = std::make_shared(element::f32, Shape{1, 128}); - - auto Xi = std::make_shared(element::f32, Shape{1, 1, 16}); - auto H_t = std::make_shared(element::f32, Shape{1, 128}); - auto C_t = std::make_shared(element::f32, Shape{1, 128}); - - // Body - auto axis = ngraph::opset5::Constant::create(ngraph::element::i64, ngraph::Shape{}, {0}); - auto squeeze = std::make_shared(Xi, axis); - - auto w_val = std::vector(512 * 16, 0); - auto r_val = std::vector(512 * 128, 0); - auto b_val = std::vector(512, 0); - auto W = ngraph::opset5::Constant::create(ngraph::element::f32, ngraph::Shape{512, 16}, w_val); - auto R = ngraph::opset5::Constant::create(ngraph::element::f32, ngraph::Shape{512, 128}, r_val); - auto B = ngraph::opset5::Constant::create(ngraph::element::f32, ngraph::Shape{512}, b_val); - - auto lstm_cell = std::make_shared(squeeze, H_t, C_t, W, R, B, 128); - auto res_1 = std::make_shared(lstm_cell->output(0)); - auto unsqueeze = std::make_shared(lstm_cell->output(0), axis); - auto res_2 = std::make_shared(unsqueeze); - auto res_3 = std::make_shared(lstm_cell->output(1)); - auto body = std::make_shared(OutputVector{res_1, res_2, res_3}, ParameterVector{H_t, Xi, C_t}); - - auto tensor_iterator = std::make_shared(); - tensor_iterator->set_body(body); - tensor_iterator->set_friendly_name("LSTMTensorIterator"); - - tensor_iterator->set_merged_input(C_t, C_init, res_3); - tensor_iterator->set_sliced_input(Xi, X, 0, 1, 1, -1, 0); - tensor_iterator->set_merged_input(H_t, H_init, 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(tensor_iterator->output(1)); - auto res_ti_2 = std::make_shared(tensor_iterator->output(0)); - f = std::make_shared(ngraph::NodeVector{res_ti_1, res_ti_2}, - ngraph::ParameterVector{X, H_init, C_init}); - - ngraph::pass::Manager manager; - manager.register_pass(); - manager.register_pass(); - manager.run_passes(f); - - ASSERT_EQ(body->get_parameters().size(), 1); - ASSERT_EQ(tensor_iterator->get_input_descriptions()[0]->m_body_parameter_index, 0); -} diff --git a/ngraph/core/src/pass/low_latency.cpp b/ngraph/core/src/pass/low_latency.cpp index 772135bb9db790..89a7a73c4996b2 100644 --- a/ngraph/core/src/pass/low_latency.cpp +++ b/ngraph/core/src/pass/low_latency.cpp @@ -9,7 +9,6 @@ #include #include #include -#include NGRAPH_RTTI_DEFINITION(ngraph::pass::LowLatency, "LowLatency", 0); @@ -30,9 +29,7 @@ ngraph::pass::LowLatency::LowLatency() int64_t variable_id = 0; std::vector> assigns; const auto& func = ti->get_function(); - auto in_descs = ti->get_input_descriptions(); - std::vector inputs_ind_to_delete; - for (const auto& in : in_descs) + for (const auto& in : ti->get_input_descriptions()) { // Process all back edges if (const auto& merged_in = std::dynamic_pointer_cast< @@ -47,14 +44,8 @@ ngraph::pass::LowLatency::LowLatency() .at(merged_in->m_body_parameter_index) ->get_friendly_name() + "/variable_" + std::to_string(variable_id)); - auto init_shape = func->get_parameters().at(merged_in->m_body_parameter_index)->get_partial_shape().get_shape(); - int zeros_length = 1.0; - for (auto i : init_shape){ - zeros_length *= i; - } - std::vector zeros(zeros_length, 0); - auto init_const = op::Constant::create(element::f32, init_shape, zeros); - auto read_value = std::make_shared(init_const, variable_name); + auto read_value = std::make_shared( + func->get_parameters().at(merged_in->m_body_parameter_index), variable_name); read_value->set_friendly_name(variable_name); for (const auto& input_to : inputs_to) { @@ -67,34 +58,9 @@ ngraph::pass::LowLatency::LowLatency() // control dependency so that ReadValue is processed before Assign assign->add_control_dependency(read_value); assigns.emplace_back(assign); - // save index of input to delete - inputs_ind_to_delete.push_back(merged_in->m_body_parameter_index); } variable_id++; - } - - std::sort(inputs_ind_to_delete.begin(), inputs_ind_to_delete.end()); - auto params = func->get_parameters(); - for (int i=inputs_ind_to_delete.size()-1; i>=0; i--){ - func->remove_parameter(params[inputs_ind_to_delete[i]]); - } - - // remove replaced inputs from descriptions - ti->get_input_descriptions().erase(remove_if(ti->get_input_descriptions().begin(), ti->get_input_descriptions().end(), - [](ngraph::op::util::InputDescriptionPtr it){ - return std::dynamic_pointer_cast(it); - }), ti->get_input_descriptions().end()); - - // fix indexes for other inputs - for (const auto& in : in_descs) - { - int already_deleted = 0; - while(in->m_body_parameter_index > inputs_ind_to_delete[already_deleted]){ - already_deleted++; - } - in->m_body_parameter_index -= already_deleted; } - // save Assign in the func so that it gets into graph traversals and isn't deleted. func->add_sinks(assigns); return false; From ca99c5e319a66f5f9ddbc261b8f764b20c05aade Mon Sep 17 00:00:00 2001 From: Svetlana Dolinina Date: Thu, 14 Jan 2021 16:52:31 +0300 Subject: [PATCH 05/12] comment added --- ngraph/core/include/ngraph/function.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ngraph/core/include/ngraph/function.hpp b/ngraph/core/include/ngraph/function.hpp index 4c2ed2f1e0d252..92d09b78cfee93 100644 --- a/ngraph/core/include/ngraph/function.hpp +++ b/ngraph/core/include/ngraph/function.hpp @@ -176,7 +176,7 @@ namespace ngraph void add_parameters(const ParameterVector& params); /// \brief Delete Parameter node from the list of parameters. Method will not delete node from - /// graph. + /// graph. Attention: Indexing of parameters will be changed. /// \param param Parameter node to delete void remove_parameter(const std::shared_ptr& param); From 49b0674ba2639ec4216bfe5a6cbbcae0d7d65f4a Mon Sep 17 00:00:00 2001 From: Svetlana Dolinina Date: Mon, 18 Jan 2021 10:28:19 +0300 Subject: [PATCH 06/12] fix style --- ngraph/core/include/ngraph/function.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ngraph/core/include/ngraph/function.hpp b/ngraph/core/include/ngraph/function.hpp index 92d09b78cfee93..ff26e752e4d7e8 100644 --- a/ngraph/core/include/ngraph/function.hpp +++ b/ngraph/core/include/ngraph/function.hpp @@ -175,9 +175,9 @@ namespace ngraph /// \param params new Parameter nodes void add_parameters(const ParameterVector& params); - /// \brief Delete Parameter node from the list of parameters. Method will not delete node from - /// graph. Attention: Indexing of parameters will be changed. - /// \param param Parameter node to delete + /// \brief Delete Parameter node from the list of parameters. Method will not delete node + /// from graph. Attention: Indexing of parameters will be changed. \param param Parameter + /// node to delete void remove_parameter(const std::shared_ptr& param); private: @@ -213,4 +213,4 @@ namespace ngraph 0}; const DiscreteTypeInfo& get_type_info() const override { return type_info; } }; -} +} // namespace ngraph From c415f31f914ea1ba6d07cd28812995a5684b5b5b Mon Sep 17 00:00:00 2001 From: sadolini Date: Wed, 20 Jan 2021 12:47:44 +0300 Subject: [PATCH 07/12] review fix --- ngraph/core/src/function.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ngraph/core/src/function.cpp b/ngraph/core/src/function.cpp index 23d36f64ce039b..1957d90b843ff7 100644 --- a/ngraph/core/src/function.cpp +++ b/ngraph/core/src/function.cpp @@ -407,6 +407,17 @@ void Function::remove_result(const std::shared_ptr& result) void Function::add_parameters(const ParameterVector& params) { + for (int i = 0; i < params.size(); i++) + { + for (int j = 0; j < m_parameters.size(); j++) + { + NGRAPH_CHECK(params[i] != m_parameters[j], + "add_parameters(): Tried to add parameter (index in array ", + i, + ") but function already have the same parameter with index ", + j); + } + } m_parameters.insert(m_parameters.end(), params.begin(), params.end()); } From 60c3f3a60dea1889a4ae7fcf75ecf3875208420f Mon Sep 17 00:00:00 2001 From: sadolini Date: Thu, 21 Jan 2021 10:23:58 +0300 Subject: [PATCH 08/12] extended comments --- ngraph/core/include/ngraph/function.hpp | 23 ++++++++++++++++++----- ngraph/core/src/function.cpp | 2 +- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/ngraph/core/include/ngraph/function.hpp b/ngraph/core/include/ngraph/function.hpp index ff26e752e4d7e8..4290196a367e80 100644 --- a/ngraph/core/include/ngraph/function.hpp +++ b/ngraph/core/include/ngraph/function.hpp @@ -1,5 +1,5 @@ //***************************************************************************** -// Copyright 2017-2020 Intel Corporation +// Copyright 2017-2021 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -170,14 +170,27 @@ namespace ngraph /// \param result Result node to delete void remove_result(const std::shared_ptr& result); - /// \brief Add new Parameter nodes to the list. Method doesn't validate graph, it should be - /// done manually after all changes. + /// \brief Add new Parameter nodes to the list. + /// + /// Method doesn't change or validate graph, it should be done manually. + /// For example, if you want to replace `ReadValue` node by `Parameter`, you should do the following steps: + /// * replace node `ReadValue` by `Parameter` in graph + /// * call add_parameter() to add new input to the list + /// * call graph validation to check correctness of changes + /// /// \param params new Parameter nodes void add_parameters(const ParameterVector& params); /// \brief Delete Parameter node from the list of parameters. Method will not delete node - /// from graph. Attention: Indexing of parameters will be changed. \param param Parameter - /// node to delete + /// from graph. Attention: Indexing of parameters can be changed. + /// + /// Possible use of method is to replace input by variable. For it the following steps should be done: + /// * `Parameter` node should be replaced by `ReadValue` + /// * call remove_parameter(param) to remove input from the list + /// * check if any parameter indexes are saved/used somewhere, update it for all inputs because indexes can be changed + /// * call graph validation to check all changes + /// + /// \param param Parameter node to delete void remove_parameter(const std::shared_ptr& param); private: diff --git a/ngraph/core/src/function.cpp b/ngraph/core/src/function.cpp index 1957d90b843ff7..19595e3ecb809c 100644 --- a/ngraph/core/src/function.cpp +++ b/ngraph/core/src/function.cpp @@ -1,5 +1,5 @@ //***************************************************************************** -// Copyright 2017-2020 Intel Corporation +// Copyright 2017-2021 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From ae27ffd4eb0ad36118d1ac51b1c72aa5b7ba7b3f Mon Sep 17 00:00:00 2001 From: sadolini Date: Tue, 26 Jan 2021 16:50:43 +0300 Subject: [PATCH 09/12] style fixes --- ngraph/core/include/ngraph/function.hpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/ngraph/core/include/ngraph/function.hpp b/ngraph/core/include/ngraph/function.hpp index 4290196a367e80..715a1af57b639d 100644 --- a/ngraph/core/include/ngraph/function.hpp +++ b/ngraph/core/include/ngraph/function.hpp @@ -170,13 +170,14 @@ namespace ngraph /// \param result Result node to delete void remove_result(const std::shared_ptr& result); - /// \brief Add new Parameter nodes to the list. + /// \brief Add new Parameter nodes to the list. /// /// Method doesn't change or validate graph, it should be done manually. - /// For example, if you want to replace `ReadValue` node by `Parameter`, you should do the following steps: + /// For example, if you want to replace `ReadValue` node by `Parameter`, you should do the + /// following steps: /// * replace node `ReadValue` by `Parameter` in graph /// * call add_parameter() to add new input to the list - /// * call graph validation to check correctness of changes + /// * call graph validation to check correctness of changes /// /// \param params new Parameter nodes void add_parameters(const ParameterVector& params); @@ -184,10 +185,12 @@ namespace ngraph /// \brief Delete Parameter node from the list of parameters. Method will not delete node /// from graph. Attention: Indexing of parameters can be changed. /// - /// Possible use of method is to replace input by variable. For it the following steps should be done: + /// Possible use of method is to replace input by variable. For it the following steps + /// should be done: /// * `Parameter` node should be replaced by `ReadValue` /// * call remove_parameter(param) to remove input from the list - /// * check if any parameter indexes are saved/used somewhere, update it for all inputs because indexes can be changed + /// * check if any parameter indexes are saved/used somewhere, update it for all inputs + /// because indexes can be changed /// * call graph validation to check all changes /// /// \param param Parameter node to delete From a94c8eca71619d8bbefaa6aa8a31e5867c8e312c Mon Sep 17 00:00:00 2001 From: sadolini Date: Thu, 28 Jan 2021 10:05:25 +0300 Subject: [PATCH 10/12] review fixes --- ngraph/core/include/ngraph/function.hpp | 3 +- ngraph/test/build_graph.cpp | 45 +++++++++++++++++++++---- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/ngraph/core/include/ngraph/function.hpp b/ngraph/core/include/ngraph/function.hpp index 715a1af57b639d..937cafc3913ad8 100644 --- a/ngraph/core/include/ngraph/function.hpp +++ b/ngraph/core/include/ngraph/function.hpp @@ -183,7 +183,8 @@ namespace ngraph void add_parameters(const ParameterVector& params); /// \brief Delete Parameter node from the list of parameters. Method will not delete node - /// from graph. Attention: Indexing of parameters can be changed. + /// from graph. You need to replace Parameter with other operation manually. + /// Attention: Indexing of parameters can be changed. /// /// Possible use of method is to replace input by variable. For it the following steps /// should be done: diff --git a/ngraph/test/build_graph.cpp b/ngraph/test/build_graph.cpp index c3a5b0471e5443..bb7b4d2f8d2a64 100644 --- a/ngraph/test/build_graph.cpp +++ b/ngraph/test/build_graph.cpp @@ -367,7 +367,7 @@ TEST(build_graph, build_graph_with_remove_result) TEST(build_graph, build_graph_with_add_parameter) { auto arg = make_shared(element::f32, Shape{2, 4}); - auto arg2 = make_shared(element::f32, Shape{2, 8}); + auto arg2 = make_shared(element::f32, Shape{2, 2}); auto init_const = op::Constant::create(element::f32, Shape{2, 2}, {0, 0, 0, 0}); auto read = make_shared(init_const, "v0"); std::vector> args = {arg, read}; @@ -384,21 +384,23 @@ TEST(build_graph, build_graph_with_add_parameter) ParameterVector params = f->get_parameters(); EXPECT_EQ(params.size(), 1); + pattern->input(1).replace_source_output(arg2->output(0)); + f->add_parameters(ParameterVector({arg2})); params = f->get_parameters(); EXPECT_EQ(params.size(), 2); EXPECT_EQ(params[1], arg2); nodes = f->get_ops(); - EXPECT_EQ(nodes.size(), 9); + EXPECT_EQ(nodes.size(), 7); } TEST(build_graph, build_graph_with_remove_parameter) { auto arg = make_shared(element::f32, Shape{2, 4}); - auto arg2 = make_shared(element::f32, Shape{2, 8}); + auto arg2 = make_shared(element::f32, Shape{2, 2}); auto init_const = op::Constant::create(element::f32, Shape{2, 2}, {0, 0, 0, 0}); auto read = make_shared(init_const, "v0"); - std::vector> args = {arg, read}; + std::vector> args = {arg, arg2}; auto pattern = make_shared(args, 1); auto res = make_shared(pattern); const auto axis = op::Constant::create(element::i64, Shape{}, {1}); @@ -408,13 +410,44 @@ TEST(build_graph, build_graph_with_remove_parameter) auto f = make_shared(ResultVector({res, res2}), ParameterVector{arg, arg2}); NodeVector nodes = f->get_ops(); - EXPECT_EQ(nodes.size(), 9); + EXPECT_EQ(nodes.size(), 7); ParameterVector params = f->get_parameters(); EXPECT_EQ(params.size(), 2); + pattern->input(1).replace_source_output(read->output(0)); f->remove_parameter(arg2); params = f->get_parameters(); EXPECT_EQ(params.size(), 1); nodes = f->get_ops(); - EXPECT_EQ(nodes.size(), 8); + EXPECT_EQ(nodes.size(), 9); } + +TEST(build_graph, build_graph_with_remove_parameter_indexing) +{ + auto arg = make_shared(element::f32, Shape{2, 4}); + auto arg2 = make_shared(element::f32, Shape{2, 2}); + auto init_const = op::Constant::create(element::f32, Shape{2, 2}, {0, 0, 0, 0}); + auto read = make_shared(init_const, "v0"); + std::vector> args = {arg2, arg}; + auto pattern = make_shared(args, 1); + auto res = make_shared(pattern); + const auto axis = op::Constant::create(element::i64, Shape{}, {1}); + auto crop = make_shared(pattern, axis, 3); + auto res2 = make_shared(crop, "v0"); + + auto f = make_shared(ResultVector({res, res2}), ParameterVector{arg2, arg}); + + NodeVector nodes = f->get_ops(); + EXPECT_EQ(nodes.size(), 7); + ParameterVector params = f->get_parameters(); + EXPECT_EQ(params.size(), 2); + + pattern->input(0).replace_source_output(read->output(0)); + f->remove_parameter(arg2); + params = f->get_parameters(); + EXPECT_EQ(params.size(), 1); + nodes = f->get_ops(); + EXPECT_EQ(nodes.size(), 9); + + f->validate_nodes_and_infer_types(); +} \ No newline at end of file From 8971bbbab3e037dbd7d50191a6ee3d716c676435 Mon Sep 17 00:00:00 2001 From: sadolini Date: Fri, 29 Jan 2021 09:45:07 +0300 Subject: [PATCH 11/12] test fix --- ngraph/test/build_graph.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ngraph/test/build_graph.cpp b/ngraph/test/build_graph.cpp index bb7b4d2f8d2a64..a33230d4b2c14e 100644 --- a/ngraph/test/build_graph.cpp +++ b/ngraph/test/build_graph.cpp @@ -419,7 +419,7 @@ TEST(build_graph, build_graph_with_remove_parameter) params = f->get_parameters(); EXPECT_EQ(params.size(), 1); nodes = f->get_ops(); - EXPECT_EQ(nodes.size(), 9); + EXPECT_EQ(nodes.size(), 8); } TEST(build_graph, build_graph_with_remove_parameter_indexing) @@ -447,7 +447,7 @@ TEST(build_graph, build_graph_with_remove_parameter_indexing) params = f->get_parameters(); EXPECT_EQ(params.size(), 1); nodes = f->get_ops(); - EXPECT_EQ(nodes.size(), 9); + EXPECT_EQ(nodes.size(), 8); f->validate_nodes_and_infer_types(); } \ No newline at end of file From 5283975289dfe140b90d1469d915f1694c11279e Mon Sep 17 00:00:00 2001 From: sadolini Date: Mon, 1 Feb 2021 23:31:14 +0300 Subject: [PATCH 12/12] added full pipeline test --- .../remove_parameter.cpp | 16 +++ .../remove_parameter.hpp | 15 +++ .../remove_parameter.cpp | 110 ++++++++++++++++++ 3 files changed, 141 insertions(+) create mode 100644 inference-engine/tests/functional/plugin/cpu/shared_tests_instances/execution_graph_tests/remove_parameter.cpp create mode 100644 inference-engine/tests/functional/plugin/shared/include/execution_graph_tests/remove_parameter.hpp create mode 100644 inference-engine/tests/functional/plugin/shared/src/execution_graph_tests/remove_parameter.cpp diff --git a/inference-engine/tests/functional/plugin/cpu/shared_tests_instances/execution_graph_tests/remove_parameter.cpp b/inference-engine/tests/functional/plugin/cpu/shared_tests_instances/execution_graph_tests/remove_parameter.cpp new file mode 100644 index 00000000000000..c11d876ee38e01 --- /dev/null +++ b/inference-engine/tests/functional/plugin/cpu/shared_tests_instances/execution_graph_tests/remove_parameter.cpp @@ -0,0 +1,16 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "execution_graph_tests/remove_parameter.hpp" +#include "common_test_utils/test_constants.hpp" + +using namespace ExecutionGraphTests; + +namespace { + +INSTANTIATE_TEST_CASE_P(smoke_removeParameter, ExecGraphRemoveParameterNode, + ::testing::Values(CommonTestUtils::DEVICE_CPU), + ExecGraphRemoveParameterNode::getTestCaseName); + +} // namespace diff --git a/inference-engine/tests/functional/plugin/shared/include/execution_graph_tests/remove_parameter.hpp b/inference-engine/tests/functional/plugin/shared/include/execution_graph_tests/remove_parameter.hpp new file mode 100644 index 00000000000000..e2b19c5ef07611 --- /dev/null +++ b/inference-engine/tests/functional/plugin/shared/include/execution_graph_tests/remove_parameter.hpp @@ -0,0 +1,15 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "gtest/gtest.h" + +namespace ExecutionGraphTests { + +class ExecGraphRemoveParameterNode + : public testing::TestWithParam { +public: + static std::string getTestCaseName(testing::TestParamInfo obj); +}; + +} // namespace ExecutionGraphTests diff --git a/inference-engine/tests/functional/plugin/shared/src/execution_graph_tests/remove_parameter.cpp b/inference-engine/tests/functional/plugin/shared/src/execution_graph_tests/remove_parameter.cpp new file mode 100644 index 00000000000000..3f23df27a1833e --- /dev/null +++ b/inference-engine/tests/functional/plugin/shared/src/execution_graph_tests/remove_parameter.cpp @@ -0,0 +1,110 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "execution_graph_tests/remove_parameter.hpp" +#include "functional_test_utils/skip_tests_config.hpp" + +#include +#include + +namespace ExecutionGraphTests { + +std::string ExecGraphRemoveParameterNode::getTestCaseName( + testing::TestParamInfo obj) { + std::string targetDevice = obj.param; + return "Dev=" + targetDevice; +} + +/** + * Replacing parameter by another node change indexing for other parameters, + * check that we still can correctly process changed network. + */ +TEST_P(ExecGraphRemoveParameterNode, RemoveParameterNode) { + SKIP_IF_CURRENT_TEST_IS_DISABLED() + + auto device_name = this->GetParam(); + ngraph::Shape shape = {3, 2}; + float in_data_2[6] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; + float in_data[6] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; + ngraph::element::Type type = ngraph::element::f32; + + using std::make_shared; + using namespace ngraph::op; + + // Some simple graph with 2 Parameters + // in2 in1 // + // \ / | // + // mul | // + // \ | // + // sum // + // | // + // out // + auto input = make_shared(type, shape); + auto input2 = make_shared(type, shape); + auto mul = make_shared(input2, input); + auto sum = make_shared(mul, input); + + auto function = std::make_shared( + ngraph::NodeVector{sum}, ngraph::ParameterVector{input2, input}, + "SimpleNet"); + + // Load into plugin and get exec graph + auto ie = InferenceEngine::Core(); + auto net = InferenceEngine::CNNNetwork(function); + auto exec_net = ie.LoadNetwork(net, device_name); + auto exec_graph = exec_net.GetExecGraphInfo(); + auto infer_req = exec_net.CreateInferRequest(); + InferenceEngine::TensorDesc tDesc(InferenceEngine::Precision::FP32, shape, + InferenceEngine::Layout::NC); + InferenceEngine::Blob::Ptr inBlob2 = + InferenceEngine::make_shared_blob(tDesc, in_data_2); + infer_req.SetBlob(input2->get_name(), inBlob2); + + InferenceEngine::Blob::Ptr inBlob = + InferenceEngine::make_shared_blob(tDesc, in_data); + infer_req.SetBlob(input->get_name(), inBlob); + + infer_req.Infer(); + + auto outBlob = infer_req.GetBlob(sum->get_name()); + InferenceEngine::MemoryBlob::CPtr output = + InferenceEngine::as(outBlob); + auto outputHolder = output->rmap(); + const auto ref_result = outputHolder.as(); + + ASSERT_EQ(function->get_parameter_index(input2), 0); + ASSERT_EQ(function->get_parameter_index(input), 1); + + // Replace input2 by constant + auto const_in = + make_shared(type, shape, std::vector(6, 1.0)); + mul->input(0).replace_source_output(const_in->output(0)); + function->remove_parameter(input2); + + ASSERT_EQ(function->get_parameters().size(), 1); + ASSERT_EQ(function->get_parameter_index(input), 0); + + // Load new function into plugin and get exec graph + auto new_net = InferenceEngine::CNNNetwork(function); + auto new_exec_net = ie.LoadNetwork(new_net, device_name); + auto new_exec_graph = new_exec_net.GetExecGraphInfo(); + + // infer new graph + auto new_infer_req = new_exec_net.CreateInferRequest(); + new_infer_req.SetBlob(input->get_name(), inBlob); + + new_infer_req.Infer(); + + auto new_outBlob = new_infer_req.GetBlob(sum->get_name()); + InferenceEngine::MemoryBlob::CPtr new_output = + InferenceEngine::as(new_outBlob); + auto new_outputHolder = new_output->rmap(); + const auto result = new_outputHolder.as(); + + for (int i = 0; i < 6; i++) { + ASSERT_NEAR(result[i], ref_result[i], 1e-5); + } +} + +} // namespace ExecutionGraphTests