Add PushConstantToSubgraph transformation (#15250)
* Add PushConstantToSubgraph transformation Transformation detects constfoldable inputs to MultiSubGraphOp, constantfold them and then pushes them to inner subgraphs. Ticket: 98155 * cast to int * comments, split to functions * remove op::util
This commit is contained in:
parent
2c64c3a8a9
commit
8512fc1655
@ -0,0 +1,25 @@
|
||||
// Copyright (C) 2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvino/pass/pass.hpp>
|
||||
#include <transformations_visibility.hpp>
|
||||
|
||||
namespace ov {
|
||||
namespace pass {
|
||||
|
||||
/**
|
||||
* @ingroup ie_transformation_common_api
|
||||
* @brief PushConstantToSubgraph transformation detects MultiSubGraphOp inputs
|
||||
* that can be constfoldable pushes that inputs to subgraphs.
|
||||
*/
|
||||
class TRANSFORMATIONS_API PushConstantToSubgraph : public ov::pass::ModelPass {
|
||||
public:
|
||||
OPENVINO_RTTI("PushConstantToSubgraph", "0");
|
||||
bool run_on_model(const std::shared_ptr<Model>& model) override;
|
||||
};
|
||||
|
||||
} // namespace pass
|
||||
} // namespace ov
|
@ -45,6 +45,7 @@
|
||||
#include <transformations/common_optimizations/prelu_fusion.hpp>
|
||||
#include <transformations/common_optimizations/pull_through_reduce.hpp>
|
||||
#include <transformations/common_optimizations/pull_transpose_through_fq.hpp>
|
||||
#include <transformations/common_optimizations/push_constant_to_subgraph.hpp>
|
||||
#include <transformations/common_optimizations/random_uniform_fusion.hpp>
|
||||
#include <transformations/common_optimizations/reduce_reshape_fusion.hpp>
|
||||
#include <transformations/common_optimizations/relu_fake_quantize_fusion.hpp>
|
||||
@ -121,6 +122,7 @@ bool ov::pass::MOCTransformations::run_on_model(const std::shared_ptr<ngraph::Fu
|
||||
REGISTER_PASS(manager, RemoveMultiSubGraphOpDanglingParams)
|
||||
REGISTER_PASS(manager, FoldSubgraphEmptyInputs)
|
||||
REGISTER_PASS(manager, DisableRandomUniformConstantFolding)
|
||||
REGISTER_PASS(manager, PushConstantToSubgraph)
|
||||
REGISTER_PASS(manager, ConstantFolding)
|
||||
REGISTER_PASS(manager, Validate)
|
||||
|
||||
|
@ -0,0 +1,122 @@
|
||||
// Copyright (C) 2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "transformations/common_optimizations/push_constant_to_subgraph.hpp"
|
||||
|
||||
#include <openvino/core/validation_util.hpp>
|
||||
#include <openvino/op/util/multi_subgraph_base.hpp>
|
||||
|
||||
#include "itt.hpp"
|
||||
|
||||
using MultiSubGraphOp = ov::op::util::MultiSubGraphOp;
|
||||
|
||||
static std::shared_ptr<ov::op::v0::Constant> try_constantfold_input(
|
||||
const std::shared_ptr<MultiSubGraphOp>& op,
|
||||
const MultiSubGraphOp::InputDescription::Ptr& input_desc,
|
||||
std::unordered_map<size_t, std::shared_ptr<ov::op::v0::Constant>>& cache) {
|
||||
if (!std::dynamic_pointer_cast<MultiSubGraphOp::InvariantInputDescription>(input_desc)) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto input_index = input_desc->m_input_index;
|
||||
auto it = cache.find(input_index);
|
||||
if (it == cache.end()) {
|
||||
auto constant = constantfold_subgraph(op->input_value(input_index));
|
||||
if (constant) {
|
||||
cache.insert({input_index, constant});
|
||||
}
|
||||
return constant;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
static void replace_body_parameter(const std::shared_ptr<ov::Model>& body,
|
||||
const std::shared_ptr<ov::op::v0::Parameter>& body_param,
|
||||
size_t body_parameter_index,
|
||||
const std::shared_ptr<ov::op::v0::Constant>& constant,
|
||||
MultiSubGraphOp::MultiSubgraphInputDescriptionVector& descriptions) {
|
||||
body_param->output(0).replace(constant);
|
||||
body->remove_parameter(body_param);
|
||||
// update all input descriptions to reflect that body parameter was removed
|
||||
for (auto& desc : descriptions) {
|
||||
if (desc->m_body_parameter_index > body_parameter_index) {
|
||||
desc->m_body_parameter_index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void update_multi_sub_graph_op_inputs(const std::shared_ptr<MultiSubGraphOp>& multi_sub_graph_op,
|
||||
int remove_inputs_mask) {
|
||||
int num_subgraphs = static_cast<int>(multi_sub_graph_op->get_internal_subgraphs_size());
|
||||
auto inputs = multi_sub_graph_op->input_values();
|
||||
for (size_t i = multi_sub_graph_op->get_input_size(); i > 0; i--) {
|
||||
const auto input_index = i - 1;
|
||||
if ((remove_inputs_mask & (1 << input_index)) != 0) {
|
||||
// remove MultiSubGraphOp's input if it was marked to be removed
|
||||
// (meaning it was constfolded and pushed to inner subgraph)
|
||||
inputs.erase(inputs.begin() + input_index);
|
||||
|
||||
// update input descriptions to reflect that the input was removed
|
||||
for (int body_idx = 0; body_idx < num_subgraphs; body_idx++) {
|
||||
auto& descriptions = multi_sub_graph_op->get_input_descriptions(body_idx);
|
||||
for (auto& desc : descriptions) {
|
||||
if (desc->m_input_index > input_index) {
|
||||
desc->m_input_index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
multi_sub_graph_op->set_arguments(inputs);
|
||||
}
|
||||
|
||||
bool ov::pass::PushConstantToSubgraph::run_on_model(const std::shared_ptr<Model>& model) {
|
||||
RUN_ON_FUNCTION_SCOPE(PushConstantToSubgraph);
|
||||
|
||||
bool result = false;
|
||||
for (const auto& op : model->get_ordered_ops()) {
|
||||
const auto multi_sub_graph_op = as_type_ptr<op::util::MultiSubGraphOp>(op);
|
||||
if (!multi_sub_graph_op) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// cache for already constant folded inputs
|
||||
std::unordered_map<size_t, std::shared_ptr<op::v0::Constant>> cache;
|
||||
// bitmask describing which MultiSubGraphOp's input to remove
|
||||
int remove_inputs_mask = 0;
|
||||
int num_subgraphs = static_cast<int>(multi_sub_graph_op->get_internal_subgraphs_size());
|
||||
|
||||
for (int body_idx = 0; body_idx < num_subgraphs; body_idx++) {
|
||||
const auto& body = multi_sub_graph_op->get_function(body_idx);
|
||||
auto& body_params = body->get_parameters();
|
||||
auto& descriptions = multi_sub_graph_op->get_input_descriptions(body_idx);
|
||||
for (auto desc_it = descriptions.begin(); desc_it < descriptions.end();) {
|
||||
const auto& desc = *desc_it;
|
||||
const auto input_index = desc->m_input_index;
|
||||
const auto constant = try_constantfold_input(multi_sub_graph_op, desc, cache);
|
||||
if (!constant) {
|
||||
remove_inputs_mask &= ~(1 << input_index);
|
||||
desc_it++;
|
||||
continue;
|
||||
}
|
||||
const auto body_parameter_index = desc->m_body_parameter_index;
|
||||
desc_it = descriptions.erase(desc_it);
|
||||
auto& body_param = body_params[body_parameter_index];
|
||||
replace_body_parameter(body, body_param, body_parameter_index, constant, descriptions);
|
||||
remove_inputs_mask |= 1 << input_index;
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (remove_inputs_mask > 0) {
|
||||
update_multi_sub_graph_op_inputs(multi_sub_graph_op, remove_inputs_mask);
|
||||
}
|
||||
|
||||
for (int body_idx = 0; body_idx < num_subgraphs; body_idx++) {
|
||||
bool model_changed = run_on_model(multi_sub_graph_op->get_function(body_idx));
|
||||
result = result || model_changed;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
// Copyright (C) 2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include <openvino/core/model.hpp>
|
||||
#include <openvino/opsets/opset10.hpp>
|
||||
#include <transformations/common_optimizations/push_constant_to_subgraph.hpp>
|
||||
|
||||
#include "common_test_utils/ngraph_test_utils.hpp"
|
||||
|
||||
using namespace testing;
|
||||
using namespace ov;
|
||||
|
||||
TEST_F(TransformationTestsF, PushConstantToSubgraphLoop) {
|
||||
{
|
||||
auto trip_count = opset10::Constant::create(element::i32, Shape{}, {2});
|
||||
auto term_cond = opset10::Constant::create(element::boolean, Shape{}, {true});
|
||||
std::shared_ptr<Model> loop_body;
|
||||
{
|
||||
auto X = std::make_shared<opset10::Parameter>(element::f32, Shape{1, 2});
|
||||
auto Y = std::make_shared<opset10::Parameter>(element::f32, Shape{1, 2});
|
||||
auto Z = std::make_shared<opset10::Parameter>(element::f32, Shape{1, 2});
|
||||
auto mul = std::make_shared<opset10::Multiply>(X, Y);
|
||||
auto add = std::make_shared<opset10::Add>(mul, Z);
|
||||
auto cond = opset10::Constant::create(element::boolean, Shape{}, {true});
|
||||
loop_body = std::make_shared<Model>(NodeVector{add, cond}, ParameterVector{X, Y, Z});
|
||||
}
|
||||
auto loop = std::make_shared<opset10::Loop>(trip_count, term_cond);
|
||||
loop->set_function(loop_body);
|
||||
|
||||
auto X = std::make_shared<opset10::Parameter>(element::f32, Shape{2, 2});
|
||||
auto constant_1 = opset10::Constant::create(element::i32, Shape{2, 2}, {11});
|
||||
auto convert_1 = std::make_shared<opset10::Convert>(constant_1, element::f32);
|
||||
auto constant_2 = opset10::Constant::create(element::i32, Shape{1, 2}, {22});
|
||||
auto convert_2 = std::make_shared<opset10::Convert>(constant_2, element::f32);
|
||||
const auto& loop_params = loop_body->get_parameters();
|
||||
loop->set_special_body_ports({-1, 1});
|
||||
loop->set_sliced_input(loop_params[0], X, 0, 1, 1, -1, 0);
|
||||
loop->set_sliced_input(loop_params[1], convert_1, 0, 1, 1, -1, 0);
|
||||
loop->set_invariant_input(loop_params[2], convert_2);
|
||||
auto out = loop->get_concatenated_slices(loop_body->get_results()[0], 0, 1, 1, -1, 0);
|
||||
function = std::make_shared<Model>(OutputVector{out}, ParameterVector{X});
|
||||
|
||||
manager.register_pass<pass::PushConstantToSubgraph>();
|
||||
}
|
||||
|
||||
{
|
||||
auto trip_count = opset10::Constant::create(element::i32, Shape{}, {2});
|
||||
auto term_cond = opset10::Constant::create(element::boolean, Shape{}, {true});
|
||||
std::shared_ptr<Model> loop_body;
|
||||
{
|
||||
auto constant = opset10::Constant::create(element::f32, Shape{1, 2}, {22});
|
||||
auto X = std::make_shared<opset10::Parameter>(element::f32, Shape{1, 2});
|
||||
auto Y = std::make_shared<opset10::Parameter>(element::f32, Shape{1, 2});
|
||||
auto mul = std::make_shared<opset10::Multiply>(X, Y);
|
||||
auto add = std::make_shared<opset10::Add>(mul, constant);
|
||||
auto cond = opset10::Constant::create(element::boolean, Shape{}, {true});
|
||||
loop_body = std::make_shared<Model>(NodeVector{add, cond}, ParameterVector{X, Y});
|
||||
}
|
||||
auto loop = std::make_shared<opset10::Loop>(trip_count, term_cond);
|
||||
loop->set_function(loop_body);
|
||||
|
||||
auto X = std::make_shared<opset10::Parameter>(element::f32, Shape{2, 2});
|
||||
auto constant_1 = opset10::Constant::create(element::i32, Shape{2, 2}, {11});
|
||||
auto convert_1 = std::make_shared<opset10::Convert>(constant_1, element::f32);
|
||||
const auto& loop_params = loop_body->get_parameters();
|
||||
loop->set_special_body_ports({-1, 1});
|
||||
loop->set_sliced_input(loop_params[0], X, 0, 1, 1, -1, 0);
|
||||
loop->set_sliced_input(loop_params[1], convert_1, 0, 1, 1, -1, 0);
|
||||
auto out = loop->get_concatenated_slices(loop_body->get_results()[0], 0, 1, 1, -1, 0);
|
||||
function_ref = std::make_shared<Model>(OutputVector{out}, ParameterVector{X});
|
||||
}
|
||||
comparator.enable(FunctionsComparator::CmpValues::ATTRIBUTES);
|
||||
comparator.enable(FunctionsComparator::CmpValues::CONST_VALUES);
|
||||
comparator.enable(FunctionsComparator::CmpValues::ACCURACY);
|
||||
}
|
||||
|
||||
TEST_F(TransformationTestsF, PushConstantToSubgraphIf) {
|
||||
{
|
||||
auto cond = opset10::Constant::create(element::boolean, Shape{}, {false});
|
||||
auto if_op = std::make_shared<ov::opset10::If>(cond);
|
||||
std::shared_ptr<ov::Model> then_body;
|
||||
{
|
||||
auto A = std::make_shared<ov::opset10::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto B = std::make_shared<ov::opset10::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto C = std::make_shared<ov::opset10::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto D = std::make_shared<ov::opset10::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto add = std::make_shared<ov::opset10::Add>(A, B);
|
||||
auto mul = std::make_shared<ov::opset10::Multiply>(add, C);
|
||||
auto sub = std::make_shared<ov::opset10::Subtract>(mul, D);
|
||||
then_body = std::make_shared<ov::Model>(add, ov::ParameterVector{A, B, C, D});
|
||||
}
|
||||
std::shared_ptr<ov::Model> else_body;
|
||||
{
|
||||
auto A = std::make_shared<ov::opset10::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto B = std::make_shared<ov::opset10::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto C = std::make_shared<ov::opset10::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto D = std::make_shared<ov::opset10::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto mul = std::make_shared<ov::opset10::Multiply>(A, B);
|
||||
auto add = std::make_shared<ov::opset10::Add>(mul, C);
|
||||
auto div = std::make_shared<ov::opset10::Divide>(add, D);
|
||||
else_body = std::make_shared<ov::Model>(div, ov::ParameterVector{A, B, C, D});
|
||||
}
|
||||
|
||||
if_op->set_then_body(then_body);
|
||||
if_op->set_else_body(else_body);
|
||||
|
||||
const auto& then_params = then_body->get_parameters();
|
||||
const auto& else_params = else_body->get_parameters();
|
||||
|
||||
auto A = std::make_shared<ov::opset10::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto B = std::make_shared<ov::opset10::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto C = std::make_shared<ov::opset10::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto const_1 = ov::opset10::Constant::create(ov::element::i32, ov::Shape{3}, {1});
|
||||
auto convert_1 = std::make_shared<ov::opset10::Convert>(const_1, ov::element::f32);
|
||||
auto const_2 = ov::opset10::Constant::create(ov::element::i32, ov::Shape{3}, {2});
|
||||
auto convert_2 = std::make_shared<ov::opset10::Convert>(const_2, ov::element::f32);
|
||||
auto const_3 = ov::opset10::Constant::create(ov::element::i32, ov::Shape{3}, {3});
|
||||
auto convert_3 = std::make_shared<ov::opset10::Convert>(const_3, ov::element::f32);
|
||||
|
||||
if_op->set_input(A, then_params[0], nullptr);
|
||||
if_op->set_input(convert_1, then_params[1], nullptr);
|
||||
if_op->set_input(B, then_params[2], else_params[0]);
|
||||
if_op->set_input(convert_2, then_params[3], else_params[1]);
|
||||
|
||||
if_op->set_input(C, nullptr, else_params[2]);
|
||||
if_op->set_input(convert_3, nullptr, else_params[3]);
|
||||
if_op->set_output(then_body->get_results()[0], else_body->get_results()[0]);
|
||||
|
||||
function = std::make_shared<ov::Model>(if_op, ov::ParameterVector{A, B, C});
|
||||
|
||||
manager.register_pass<pass::PushConstantToSubgraph>();
|
||||
}
|
||||
|
||||
{
|
||||
auto cond = opset10::Constant::create(element::boolean, Shape{}, {false});
|
||||
auto const_1 = ov::opset10::Constant::create(ov::element::f32, ov::Shape{3}, {1});
|
||||
auto const_2 = ov::opset10::Constant::create(ov::element::f32, ov::Shape{3}, {2});
|
||||
auto const_3 = ov::opset10::Constant::create(ov::element::f32, ov::Shape{3}, {3});
|
||||
auto if_op = std::make_shared<ov::opset10::If>(cond);
|
||||
std::shared_ptr<ov::Model> then_body;
|
||||
{
|
||||
auto A = std::make_shared<ov::opset10::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto B = std::make_shared<ov::opset10::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto add = std::make_shared<ov::opset10::Add>(A, const_1);
|
||||
auto mul = std::make_shared<ov::opset10::Multiply>(add, B);
|
||||
auto sub = std::make_shared<ov::opset10::Subtract>(mul, const_2);
|
||||
then_body = std::make_shared<ov::Model>(add, ov::ParameterVector{A, B});
|
||||
}
|
||||
std::shared_ptr<ov::Model> else_body;
|
||||
{
|
||||
auto A = std::make_shared<ov::opset10::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto B = std::make_shared<ov::opset10::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto mul = std::make_shared<ov::opset10::Multiply>(A, const_2);
|
||||
auto add = std::make_shared<ov::opset10::Add>(mul, B);
|
||||
auto div = std::make_shared<ov::opset10::Divide>(add, const_3);
|
||||
else_body = std::make_shared<ov::Model>(div, ov::ParameterVector{A, B});
|
||||
}
|
||||
|
||||
if_op->set_then_body(then_body);
|
||||
if_op->set_else_body(else_body);
|
||||
|
||||
const auto& then_params = then_body->get_parameters();
|
||||
const auto& else_params = else_body->get_parameters();
|
||||
|
||||
auto A = std::make_shared<ov::opset10::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto B = std::make_shared<ov::opset10::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto C = std::make_shared<ov::opset10::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
|
||||
if_op->set_input(A, then_params[0], nullptr);
|
||||
if_op->set_input(B, then_params[1], else_params[0]);
|
||||
if_op->set_input(C, nullptr, else_params[1]);
|
||||
if_op->set_output(then_body->get_results()[0], else_body->get_results()[0]);
|
||||
|
||||
function_ref = std::make_shared<ov::Model>(if_op, ov::ParameterVector{A, B, C});
|
||||
}
|
||||
|
||||
comparator.enable(FunctionsComparator::CmpValues::ATTRIBUTES);
|
||||
comparator.enable(FunctionsComparator::CmpValues::CONST_VALUES);
|
||||
comparator.enable(FunctionsComparator::CmpValues::ACCURACY);
|
||||
}
|
@ -6,7 +6,9 @@
|
||||
|
||||
#include <memory>
|
||||
#include <openvino/opsets/opset9.hpp>
|
||||
#include <openvino/pass/constant_folding.hpp>
|
||||
#include <openvino/pass/manager.hpp>
|
||||
#include <transformations/common_optimizations/push_constant_to_subgraph.hpp>
|
||||
#include <transformations/control_flow/unroll_if.hpp>
|
||||
#include <transformations/init_node_info.hpp>
|
||||
|
||||
@ -221,3 +223,85 @@ TEST(TransformationTests, UnrollIfCondIsTrueMultiOutput) {
|
||||
auto res = compare_functions(f, f_ref);
|
||||
ASSERT_TRUE(res.first) << res.second;
|
||||
}
|
||||
|
||||
TEST(TransformationTests, UnrollIfInsideIf) {
|
||||
std::shared_ptr<ov::Model> f(nullptr), f_ref(nullptr);
|
||||
{
|
||||
auto cond = std::make_shared<ov::opset9::Constant>(ov::element::boolean, ov::Shape{1}, true);
|
||||
auto not_cond = std::make_shared<ov::opset9::LogicalNot>(cond);
|
||||
auto if_op = std::make_shared<ov::opset8::If>(cond);
|
||||
|
||||
std::shared_ptr<ov::Model> then_body;
|
||||
{
|
||||
auto cond_inside = std::make_shared<ov::opset9::Parameter>(ov::element::boolean, ov::Shape{1});
|
||||
auto if_inside = std::make_shared<ov::opset8::If>(cond_inside);
|
||||
|
||||
std::shared_ptr<ov::Model> then_body_inside;
|
||||
{
|
||||
auto X = std::make_shared<ov::opset9::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto Y = std::make_shared<ov::opset9::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto add = std::make_shared<ov::opset9::Add>(X, Y);
|
||||
then_body_inside = std::make_shared<ov::Model>(add, ov::ParameterVector{X, Y});
|
||||
}
|
||||
std::shared_ptr<ov::Model> else_body_inside;
|
||||
{
|
||||
auto X = std::make_shared<ov::opset9::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto Y = std::make_shared<ov::opset9::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto mul = std::make_shared<ov::opset9::Multiply>(X, Y);
|
||||
else_body_inside = std::make_shared<ov::Model>(mul, ov::ParameterVector{X, Y});
|
||||
}
|
||||
|
||||
auto X = std::make_shared<ov::opset9::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto Y = std::make_shared<ov::opset9::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
if_inside->set_then_body(then_body_inside);
|
||||
if_inside->set_else_body(else_body_inside);
|
||||
auto then_p = then_body_inside->get_parameters();
|
||||
auto else_p = else_body_inside->get_parameters();
|
||||
if_inside->set_input(X, then_p[0], else_p[0]);
|
||||
if_inside->set_input(Y, then_p[1], else_p[1]);
|
||||
if_inside->set_output(then_body_inside->get_results()[0], else_body_inside->get_results()[0]);
|
||||
auto if_result = std::make_shared<ov::opset9::Result>(if_inside);
|
||||
|
||||
then_body = std::make_shared<ov::Model>(if_result, ov::ParameterVector{cond_inside, X, Y});
|
||||
}
|
||||
|
||||
std::shared_ptr<ov::Model> else_body;
|
||||
{
|
||||
auto X = std::make_shared<ov::opset9::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto Y = std::make_shared<ov::opset9::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto sub = std::make_shared<ov::opset9::Subtract>(X, Y);
|
||||
else_body = std::make_shared<ov::Model>(sub, ov::ParameterVector{X, Y});
|
||||
}
|
||||
|
||||
auto X = std::make_shared<ov::opset9::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto Y = std::make_shared<ov::opset9::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
if_op->set_then_body(then_body);
|
||||
if_op->set_else_body(else_body);
|
||||
auto then_p = then_body->get_parameters();
|
||||
auto else_p = else_body->get_parameters();
|
||||
if_op->set_input(not_cond, then_p[0], nullptr);
|
||||
if_op->set_input(X, then_p[1], else_p[0]);
|
||||
if_op->set_input(Y, then_p[2], else_p[1]);
|
||||
if_op->set_output(then_body->get_results()[0], else_body->get_results()[0]);
|
||||
auto if_result = std::make_shared<ov::opset9::Result>(if_op);
|
||||
|
||||
f = std::make_shared<ov::Model>(ov::NodeVector{if_result}, ov::ParameterVector{X, Y});
|
||||
ov::pass::Manager manager;
|
||||
manager.register_pass<ngraph::pass::InitNodeInfo>();
|
||||
manager.register_pass<ov::pass::PushConstantToSubgraph>();
|
||||
manager.register_pass<ov::pass::ConstantFolding>();
|
||||
manager.register_pass<ngraph::pass::UnrollIf>();
|
||||
manager.run_passes(f);
|
||||
ASSERT_NO_THROW(check_rt_info(f));
|
||||
}
|
||||
|
||||
{
|
||||
auto X = std::make_shared<ov::opset9::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto Y = std::make_shared<ov::opset9::Parameter>(ov::element::f32, ov::Shape{3});
|
||||
auto mul = std::make_shared<ov::opset9::Multiply>(X, Y);
|
||||
f_ref = std::make_shared<ov::Model>(mul, ov::ParameterVector{X, Y});
|
||||
}
|
||||
|
||||
auto res = compare_functions(f, f_ref);
|
||||
ASSERT_TRUE(res.first) << res.second;
|
||||
}
|
||||
|
@ -198,4 +198,11 @@ OPENVINO_API bool are_unique(const std::vector<int64_t>& data);
|
||||
/// \return Value if between min, max otherwise min or max.
|
||||
OPENVINO_API
|
||||
int64_t clip(const int64_t& value, const int64_t& min, const int64_t& max);
|
||||
|
||||
/// \brief Constant folds a subgraph to a constant node
|
||||
///
|
||||
/// \param subgraph sink
|
||||
///
|
||||
/// \return Constant node or nullptr if unable to constantfold the subgraph
|
||||
OPENVINO_API std::shared_ptr<op::v0::Constant> constantfold_subgraph(const Output<Node>& subgraph_sink);
|
||||
} // namespace ov
|
||||
|
@ -1690,3 +1690,27 @@ bool ov::are_unique(const std::vector<int64_t>& data) {
|
||||
int64_t ov::clip(const int64_t& value, const int64_t& min, const int64_t& max) {
|
||||
return std::min(std::max(value, min), max);
|
||||
};
|
||||
|
||||
std::shared_ptr<op::v0::Constant> ov::constantfold_subgraph(const Output<Node>& subgraph_sink) {
|
||||
if (const auto& c = ov::as_type_ptr<op::v0::Constant>(subgraph_sink.get_node_shared_ptr()))
|
||||
return c;
|
||||
|
||||
const auto node = subgraph_sink.get_node();
|
||||
const auto num_inputs = node->get_input_size();
|
||||
if (num_inputs == 0)
|
||||
return nullptr;
|
||||
|
||||
OutputVector inputs;
|
||||
inputs.reserve(num_inputs);
|
||||
for (size_t i = 0; i < num_inputs; i++) {
|
||||
auto constant = constantfold_subgraph(node->input_value(i));
|
||||
if (constant == nullptr)
|
||||
return nullptr;
|
||||
inputs.push_back(constant);
|
||||
}
|
||||
|
||||
OutputVector outputs(node->get_output_size());
|
||||
if (!node->constant_fold(outputs, inputs))
|
||||
return nullptr;
|
||||
return ov::as_type_ptr<op::v0::Constant>(outputs[subgraph_sink.get_index()].get_node_shared_ptr());
|
||||
}
|
||||
|
@ -47,3 +47,22 @@ TEST(get_constant_from_source, extract_static_dim_from_dynamic_shape_check) {
|
||||
ASSERT_TRUE(extract_static_dimension->get_output_tensor(0).get_lower_value());
|
||||
ASSERT_TRUE(extract_static_dimension->get_output_tensor(0).get_upper_value());
|
||||
}
|
||||
|
||||
TEST(constantfold_subgraph, split) {
|
||||
std::vector<float> input{0, 1, 2, 3, 4, 5, 6, 7, 8};
|
||||
auto constant = ov::opset8::Constant::create(ov::element::f32, ov::Shape{input.size()}, input);
|
||||
auto mul = std::make_shared<ov::opset8::Multiply>(constant,
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {1}));
|
||||
auto shape = std::make_shared<ov::opset8::ShapeOf>(mul);
|
||||
auto len_0 =
|
||||
std::make_shared<ov::opset8::Divide>(shape, ov::opset8::Constant::create(ov::element::i64, ov::Shape{}, {2}));
|
||||
auto len_1 = std::make_shared<ov::opset8::Subtract>(shape, len_0);
|
||||
auto lenghts = std::make_shared<ov::opset8::Concat>(ov::OutputVector{len_0, len_1}, 0);
|
||||
auto axis = ov::opset8::Constant::create(ov::element::i64, ov::Shape{}, {0});
|
||||
auto split = std::make_shared<ov::opset8::VariadicSplit>(mul, axis, lenghts);
|
||||
std::vector<float> expected(std::next(input.begin(), input.size() / 2), input.end());
|
||||
auto ret = ov::constantfold_subgraph(split->output(1));
|
||||
ASSERT_NE(ret, nullptr);
|
||||
auto actual = ret->cast_vector<float>();
|
||||
ASSERT_EQ(expected, actual);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user