[FrontEnd] add control flow, basic tensor array support for paddle faster_rcnn model (#10684)
* support control flow, tensor array
* fix clang error
* support ppdet2.3 model
* 1. code clean; 2. more comments inlined.
* fix after clone ov::Model no more in need.
* support dynamic shape; more comments
* only process same rank
* remove unused function
* simplify the loop logic
* fix review comments
* support shape{1} {}
* disable FoldSubgraphEmptyInputs because loop in loop
* fix review comments
* remove scalar{}->shape{1} testcase
* allow re-infer shape when backedge changes input shape
* fix condition shape to {1}
* support output rank is not same or dynamic
* fix refactor error
* apply review comments
* fix win warnings
* remove TransformEliminateConvert
Co-authored-by: jialipen <cecilia.peng@intel.com>
Co-authored-by: Evgenya Stepyreva <evgenya.stepyreva@intel.com>
This commit is contained in:
@@ -13,6 +13,13 @@ namespace ov {
|
||||
namespace pass {
|
||||
|
||||
class TRANSFORMATIONS_API FoldSubgraphEmptyInputs;
|
||||
class TRANSFORMATIONS_API DisableFoldSubgraphEmptyInputs;
|
||||
|
||||
TRANSFORMATIONS_API void disable_fold_subgraph_empty_inputs(const std::shared_ptr<Node>& node);
|
||||
|
||||
TRANSFORMATIONS_API void enable_fold_subgraph_empty_inputs(const std::shared_ptr<Node>& node);
|
||||
|
||||
TRANSFORMATIONS_API bool fold_subgraph_empty_inputs_is_disabled(const std::shared_ptr<Node>& node);
|
||||
|
||||
} // namespace pass
|
||||
} // namespace ov
|
||||
@@ -29,3 +36,12 @@ public:
|
||||
OPENVINO_RTTI("FoldSubgraphEmptyInputs", "0");
|
||||
FoldSubgraphEmptyInputs();
|
||||
};
|
||||
|
||||
class ov::pass::DisableFoldSubgraphEmptyInputs : public ov::RuntimeAttribute {
|
||||
public:
|
||||
OPENVINO_RTTI("DisableFoldSubgraphEmptyInputs");
|
||||
DisableFoldSubgraphEmptyInputs() = default;
|
||||
bool is_copyable() const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -12,10 +12,8 @@
|
||||
namespace ov {
|
||||
namespace pass {
|
||||
|
||||
class TRANSFORMATIONS_API RemoveConcatZeroDimInput;
|
||||
|
||||
} // namespace pass
|
||||
} // namespace ov
|
||||
class OPENVINO_API RemoveConcatZeroDimInput;
|
||||
class OPENVINO_API DisableRemoveConcatZeroDimInput;
|
||||
|
||||
/**
|
||||
* @ingroup ie_transformation_common_api
|
||||
@@ -23,8 +21,26 @@ class TRANSFORMATIONS_API RemoveConcatZeroDimInput;
|
||||
* removes input of Concat if the tensor size is equal to 0
|
||||
*/
|
||||
|
||||
class ov::pass::RemoveConcatZeroDimInput : public ov::pass::MatcherPass {
|
||||
class RemoveConcatZeroDimInput : public ov::pass::MatcherPass {
|
||||
public:
|
||||
OPENVINO_RTTI("RemoveConcatZeroDimInput", "0");
|
||||
RemoveConcatZeroDimInput();
|
||||
};
|
||||
|
||||
OPENVINO_API void disable_remove_concat_zerodim_input(const std::shared_ptr<Node>& node);
|
||||
|
||||
OPENVINO_API void enable_remove_concat_zerodim_input(const std::shared_ptr<Node>& node);
|
||||
|
||||
OPENVINO_API bool remove_concat_zerodim_input_is_disabled(const std::shared_ptr<Node>& node);
|
||||
|
||||
class DisableRemoveConcatZeroDimInput : public ov::RuntimeAttribute {
|
||||
public:
|
||||
OPENVINO_RTTI("DisableRemoveConcatZeroDimInput");
|
||||
DisableRemoveConcatZeroDimInput() = default;
|
||||
bool is_copyable() const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace pass
|
||||
} // namespace ov
|
||||
@@ -22,6 +22,10 @@ ov::pass::FoldSubgraphEmptyInputs::FoldSubgraphEmptyInputs() {
|
||||
if (multi_subgraph_op == nullptr) {
|
||||
return false;
|
||||
}
|
||||
const auto& rt_info = multi_subgraph_op->get_rt_info();
|
||||
if (rt_info.count(DisableFoldSubgraphEmptyInputs::get_type_info_static())) {
|
||||
return false;
|
||||
}
|
||||
auto multi_subgraph_op_inputs = multi_subgraph_op->input_values();
|
||||
|
||||
std::vector<ov::Output<ov::Node>> empty_inputs;
|
||||
@@ -60,3 +64,16 @@ ov::pass::FoldSubgraphEmptyInputs::FoldSubgraphEmptyInputs() {
|
||||
auto m = std::make_shared<ngraph::pattern::Matcher>(multi_subgraph_op_pattern, matcher_name);
|
||||
this->register_matcher(m, callback);
|
||||
}
|
||||
|
||||
void ov::pass::disable_fold_subgraph_empty_inputs(const std::shared_ptr<ov::Node>& node) {
|
||||
node->get_rt_info().emplace(DisableFoldSubgraphEmptyInputs::get_type_info_static(),
|
||||
DisableFoldSubgraphEmptyInputs{});
|
||||
}
|
||||
|
||||
void ov::pass::enable_fold_subgraph_empty_inputs(const std::shared_ptr<ov::Node>& node) {
|
||||
node->get_rt_info().erase(DisableFoldSubgraphEmptyInputs::get_type_info_static());
|
||||
}
|
||||
|
||||
bool ov::pass::fold_subgraph_empty_inputs_is_disabled(const std::shared_ptr<ov::Node>& node) {
|
||||
return node->get_rt_info().count(DisableFoldSubgraphEmptyInputs::get_type_info_static());
|
||||
}
|
||||
|
||||
@@ -19,6 +19,11 @@ ov::pass::RemoveConcatZeroDimInput::RemoveConcatZeroDimInput() {
|
||||
auto concat_pattern = pattern::wrap_type<opset8::Concat>();
|
||||
ngraph::matcher_pass_callback callback = [=](pattern::Matcher& m) {
|
||||
auto concat = m.get_match_root();
|
||||
const auto& rt_info = concat->get_rt_info();
|
||||
if (rt_info.count(DisableRemoveConcatZeroDimInput::get_type_info_static())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto concat_inputs = concat->input_values();
|
||||
concat_inputs.erase(
|
||||
std::remove_if(
|
||||
@@ -47,3 +52,16 @@ ov::pass::RemoveConcatZeroDimInput::RemoveConcatZeroDimInput() {
|
||||
auto m = std::make_shared<ngraph::pattern::Matcher>(concat_pattern, matcher_name);
|
||||
this->register_matcher(m, callback);
|
||||
}
|
||||
|
||||
void ov::pass::disable_remove_concat_zerodim_input(const std::shared_ptr<Node>& node) {
|
||||
node->get_rt_info().emplace(DisableRemoveConcatZeroDimInput::get_type_info_static(),
|
||||
DisableRemoveConcatZeroDimInput{});
|
||||
}
|
||||
|
||||
void ov::pass::enable_remove_concat_zerodim_input(const std::shared_ptr<Node>& node) {
|
||||
node->get_rt_info().erase(DisableRemoveConcatZeroDimInput::get_type_info_static());
|
||||
}
|
||||
|
||||
bool ov::pass::remove_concat_zerodim_input_is_disabled(const std::shared_ptr<Node>& node) {
|
||||
return node->get_rt_info().count(DisableRemoveConcatZeroDimInput::get_type_info_static());
|
||||
}
|
||||
|
||||
@@ -37,7 +37,9 @@ bool ngraph::pass::UnrollIf::run_on_model(const std::shared_ptr<ngraph::Function
|
||||
for (const auto& input_descr : input_descriptions) {
|
||||
auto in_data = if_node->input_value(input_descr->m_input_index);
|
||||
auto& param = body->get_parameters()[input_descr->m_body_parameter_index];
|
||||
ngraph::replace_node(param, {in_data});
|
||||
for (const auto& input : param->output(0).get_target_inputs()) {
|
||||
input.replace_source_output(in_data);
|
||||
}
|
||||
}
|
||||
for (const auto& output_desc : output_descriptions) {
|
||||
std::shared_ptr<opset8::Result> result = body->get_results()[output_desc->m_body_value_index];
|
||||
|
||||
@@ -164,9 +164,25 @@ void loop(const std::shared_ptr<Function>& func,
|
||||
}
|
||||
|
||||
// Back-edge processing
|
||||
bool need_validate = false;
|
||||
for (auto& back_edge : back_edges) {
|
||||
const auto& input_shape = inputs_to_body[back_edge.param_idx]->get_shape();
|
||||
const auto& result_shape = body_outputs[back_edge.result_idx]->get_shape();
|
||||
// when output shape does not equal to input shape in a back-edge, such as
|
||||
// Parameter(out:1)->|
|
||||
// |->Concat(out:2)->Result(out:2)
|
||||
// Const(out:1)->|
|
||||
// after iteration completed, should update (out:2) to input, then use new input
|
||||
// shape to propagate others.
|
||||
if (input_shape != result_shape) {
|
||||
const auto& param = func->get_parameters().at(back_edge.param_idx);
|
||||
param->set_partial_shape(result_shape);
|
||||
need_validate = true;
|
||||
}
|
||||
inputs_to_body[back_edge.param_idx] = body_outputs[back_edge.result_idx];
|
||||
}
|
||||
if (need_validate)
|
||||
func->validate_nodes_and_infer_types();
|
||||
}
|
||||
|
||||
for (const auto& desc : out_descs) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "ngraph/op/loop.hpp"
|
||||
|
||||
#include <climits>
|
||||
#include <ngraph/validation_util.hpp>
|
||||
|
||||
#include "itt.hpp"
|
||||
@@ -152,6 +153,10 @@ void op::v5::Loop::validate_and_infer_types() {
|
||||
get_input_size() == m_input_descriptions[0].size() + input_offset,
|
||||
"Number of inputs must be the same as number of input descriptions");
|
||||
|
||||
// if output shape is not same with input in a back-edge, here will update the input shape
|
||||
// Port map processing: output -> input
|
||||
std::map<uint64_t, uint64_t> back_edges;
|
||||
|
||||
// Input
|
||||
for (const auto& input_description : m_input_descriptions[0]) {
|
||||
auto index = input_description->m_input_index;
|
||||
@@ -177,6 +182,7 @@ void op::v5::Loop::validate_and_infer_types() {
|
||||
auto input_partial_shape = input(index).get_partial_shape();
|
||||
|
||||
body_parameter->set_partial_shape(input_partial_shape);
|
||||
back_edges[merged_input_description->m_body_value_index] = merged_input_description->m_body_parameter_index;
|
||||
} else if (auto invariant_input_description =
|
||||
ov::as_type_ptr<v0::TensorIterator::InvariantInputDescription>(input_description)) {
|
||||
auto body_parameter = m_bodies[0]->get_parameters().at(invariant_input_description->m_body_parameter_index);
|
||||
@@ -191,6 +197,72 @@ void op::v5::Loop::validate_and_infer_types() {
|
||||
// Body
|
||||
m_bodies[0]->validate_nodes_and_infer_types();
|
||||
|
||||
if (!back_edges.empty()) {
|
||||
// if an exact value is available, limit the number of iterations.
|
||||
size_t i, max_num_of_iterations = m_num_iterations == -1 ? INT_MAX : m_num_iterations;
|
||||
bool need_reinvalidate = false;
|
||||
for (i = 0; i < max_num_of_iterations; i++) {
|
||||
need_reinvalidate = false;
|
||||
for (const auto& output_description : m_output_descriptions[0]) {
|
||||
auto body_value = m_bodies[0]->get_results().at(output_description->m_body_value_index)->input_value(0);
|
||||
|
||||
if (auto body_output_description =
|
||||
ov::as_type_ptr<v0::TensorIterator::BodyOutputDescription>(output_description)) {
|
||||
if (!back_edges.count(output_description->m_body_value_index))
|
||||
continue;
|
||||
const ov::PartialShape& body_value_shape = body_value.get_partial_shape();
|
||||
auto input_param =
|
||||
m_bodies[0]->get_parameters().at(back_edges[output_description->m_body_value_index]);
|
||||
const auto& input_param_ps = input_param->get_partial_shape();
|
||||
if (body_value_shape.rank().is_static()) {
|
||||
// handle the case: when sub-model's output shape does not compatible input shape in a
|
||||
// back-edge, such as
|
||||
// Parameter(out:-1, 1)->|
|
||||
// |->Concat(out:-1, 2)->Result(out:-1, 2)
|
||||
// Parameter(out:-1, 1)->| (axis==1)
|
||||
// when iteration number is unknown or sub-model output shape may be vary, the Result shape
|
||||
// should infer as (-1, -1), then set changed one to input and propagate to others.
|
||||
if (input_param_ps.rank().is_static()) {
|
||||
const auto body_rank_len = body_value_shape.rank().get_length();
|
||||
const auto input_rank_len = input_param_ps.rank().get_length();
|
||||
ov::PartialShape new_ps;
|
||||
bool shape_changed = false;
|
||||
if (body_rank_len == input_rank_len) {
|
||||
new_ps = body_value_shape;
|
||||
for (auto j = 0; j < body_rank_len; j++) {
|
||||
if (!body_value_shape[j].compatible(input_param_ps[j])) {
|
||||
new_ps[j] = Dimension::dynamic();
|
||||
shape_changed = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
new_ps = ov::PartialShape::dynamic();
|
||||
shape_changed = true;
|
||||
}
|
||||
// reset sub model input shape
|
||||
if (shape_changed) {
|
||||
need_reinvalidate = true;
|
||||
input_param->set_partial_shape(new_ps);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (input_param_ps.rank().is_static()) {
|
||||
// output shape is dynamic, let the input known now we are dynamic shape
|
||||
input_param->set_partial_shape(body_value_shape);
|
||||
need_reinvalidate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// only input shape changed we will re-compute output shape
|
||||
if (need_reinvalidate) {
|
||||
m_bodies[0]->validate_nodes_and_infer_types();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output
|
||||
for (const auto& output_description : m_output_descriptions[0]) {
|
||||
auto index = output_description->m_output_index;
|
||||
@@ -221,11 +293,11 @@ void op::v5::Loop::validate_and_infer_types() {
|
||||
|
||||
else if (auto body_output_description =
|
||||
ov::as_type_ptr<v0::TensorIterator::BodyOutputDescription>(output_description)) {
|
||||
const ov::PartialShape& ps = body_value.get_partial_shape();
|
||||
if (ps.is_dynamic()) {
|
||||
set_output_type(index, body_value.get_element_type(), ps);
|
||||
const ov::PartialShape& body_value_shape = body_value.get_partial_shape();
|
||||
if (body_value_shape.is_dynamic()) {
|
||||
set_output_type(index, body_value.get_element_type(), body_value_shape);
|
||||
} else {
|
||||
auto shape = ps.get_shape();
|
||||
auto shape = body_value_shape.get_shape();
|
||||
if (zero_number_of_iter) {
|
||||
shape.at(0) = 0;
|
||||
}
|
||||
|
||||
@@ -62,6 +62,40 @@ static const std::vector<std::string> models{
|
||||
std::string("bmm"),
|
||||
std::string("ceil"),
|
||||
std::string("clip"),
|
||||
// could not support dynamic rank
|
||||
// std::string("conditional_block_const/conditional_block_const.pdmodel"),
|
||||
// std::string("conditional_block_const_2outputs/conditional_block_const_2outputs.pdmodel"),
|
||||
std::string("conditional_block_2inputs/conditional_block_2inputs.pdmodel"),
|
||||
std::string("conditional_block_2inputs_2outputs/conditional_block_2inputs_2outputs.pdmodel"),
|
||||
std::string("conditional_block_2inputs_dyn/conditional_block_2inputs_dyn.pdmodel"),
|
||||
std::string("conditional_block_2inputs_dyn_2outputs/conditional_block_2inputs_dyn_2outputs.pdmodel"),
|
||||
std::string("conditional_block_dyn_multiple_consumers/conditional_block_dyn_multiple_consumers.pdmodel"),
|
||||
std::string("conditional_block_dyn_multiple_blocks/conditional_block_dyn_multiple_blocks.pdmodel"),
|
||||
std::string("conditional_block_dyn_multiple_blocks2/conditional_block_dyn_multiple_blocks2.pdmodel"),
|
||||
// TensorArray case
|
||||
std::string("conditional_block_concat/conditional_block_concat.pdmodel"),
|
||||
std::string("conditional_block_concat_dyn/conditional_block_concat_dyn.pdmodel"),
|
||||
std::string("conditional_block_concat_dyn_2axes/conditional_block_concat_dyn_2axes.pdmodel"),
|
||||
std::string("conditional_block_conditional_block_concat/conditional_block_conditional_block_concat.pdmodel"),
|
||||
std::string(
|
||||
"conditional_block_conditional_block_concat_dyn/conditional_block_conditional_block_concat_dyn.pdmodel"),
|
||||
std::string("conditional_block_slice0/conditional_block_slice0.pdmodel"),
|
||||
std::string("conditional_block_slice0_dyn/conditional_block_slice0_dyn.pdmodel"),
|
||||
std::string("conditional_block_slice0_else/conditional_block_slice0_else.pdmodel"),
|
||||
std::string("conditional_block_slice0_scaler/conditional_block_slice0_scaler.pdmodel"),
|
||||
std::string("conditional_block_slice0_scaler_dyn/conditional_block_slice0_scaler_dyn.pdmodel"),
|
||||
// std::string("conditional_block_slice0_axis2/conditional_block_slice0_axis2.pdmodel"), // No such case in model,
|
||||
// as paddle.concat always concat along axis 0.
|
||||
// std::string("conditional_block_slice0_axis2_dyn/conditional_block_slice0_axis2_dyn.pdmodel"),
|
||||
// std::string("conditional_block_slice0_axis1_axis2/conditional_block_slice0_axis1_axis2.pdmodel"),
|
||||
// std::string("conditional_block_slice0_axis1_axis2_dyn/conditional_block_slice0_axis1_axis2_dyn.pdmodel"),
|
||||
std::string("conditional_block_concat_false/conditional_block_concat_false.pdmodel"),
|
||||
std::string("conditional_block_concat_false_dyn/conditional_block_concat_false_dyn.pdmodel"),
|
||||
std::string("conditional_block_slice0_2tensorarrays/conditional_block_slice0_2tensorarrays.pdmodel"),
|
||||
std::string("conditional_block_slice0_2tensorarrays_dyn/conditional_block_slice0_2tensorarrays_dyn.pdmodel"),
|
||||
std::string("conditional_block_slice0_2tensorarrays_extra/conditional_block_slice0_2tensorarrays_extra.pdmodel"),
|
||||
std::string(
|
||||
"conditional_block_slice0_2tensorarrays_extra_dyn/conditional_block_slice0_2tensorarrays_extra_dyn.pdmodel"),
|
||||
std::string("conv2d_dilation_assymetric_pads_strides"),
|
||||
std::string("conv2d_SAME_padding"),
|
||||
std::string("conv2d_strides_assymetric_padding"),
|
||||
@@ -223,6 +257,18 @@ static const std::vector<std::string> models{
|
||||
std::string("logical_not"),
|
||||
std::string("logical_or"),
|
||||
std::string("logical_xor"),
|
||||
std::string("loop/loop.pdmodel"),
|
||||
std::string("loop_dyn/loop_dyn.pdmodel"),
|
||||
std::string("loop_dyn_x/loop_dyn_x.pdmodel"),
|
||||
std::string("loop_if/loop_if.pdmodel"),
|
||||
std::string("loop_if_loop/loop_if_loop.pdmodel"),
|
||||
std::string("loop_if_loop_if/loop_if_loop_if.pdmodel"),
|
||||
std::string("loop_if_loop_complex/loop_if_loop_complex.pdmodel"),
|
||||
// disabed due to slice could not produce full dynamic shape
|
||||
// std::string("loop_if_tensor_array/loop_if_tensor_array.pdmodel"),
|
||||
std::string("loop_t/loop_t.pdmodel"),
|
||||
std::string("loop_tensor_array/loop_tensor_array.pdmodel"),
|
||||
std::string("loop_x/loop_x.pdmodel"),
|
||||
std::string("matmul_xt"),
|
||||
std::string("matmul_xt_yt"),
|
||||
std::string("matmul_yt"),
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
import paddle
|
||||
|
||||
from save_model import exportModel
|
||||
|
||||
'''
|
||||
test: simple conditiona_block pair + select_input without input to conditional_block.
|
||||
'''
|
||||
x = np.full(shape=[1], dtype='float32', fill_value=0.1)
|
||||
y = np.full(shape=[1], dtype='float32', fill_value=0.23)
|
||||
data = np.less(y,x)
|
||||
|
||||
@paddle.jit.to_static
|
||||
def test_model(pred):
|
||||
# pred: A boolean tensor whose numel should be 1.
|
||||
def true_func():
|
||||
return paddle.full(shape=[3, 4], dtype='float32', # TODO: FAILED with different dtype
|
||||
fill_value=1)
|
||||
|
||||
def false_func():
|
||||
return paddle.full(shape=[1, 2], dtype='float32',
|
||||
fill_value=3)
|
||||
|
||||
return paddle.static.nn.cond(pred, true_func, false_func)
|
||||
|
||||
exportModel('conditional_block_const', test_model, [data], target_dir=sys.argv[1])
|
||||
|
||||
|
||||
'''
|
||||
more than one select_input with constant inputs.
|
||||
'''
|
||||
@paddle.jit.to_static
|
||||
def test_model_2outputs(pred):
|
||||
# pred: A boolean tensor whose numel should be 1.
|
||||
def true_func():
|
||||
return paddle.full(shape=[1, 2], dtype='float32',
|
||||
fill_value=1), paddle.full(shape=[1, 3], dtype='float32', # TODO: FAILED with different dtype
|
||||
fill_value=3)
|
||||
|
||||
def false_func():
|
||||
return paddle.full(shape=[3, 4], dtype='float32',
|
||||
fill_value=3), paddle.full(shape=[1, 4], dtype='float32',
|
||||
fill_value=4)
|
||||
|
||||
return paddle.static.nn.cond(pred, true_func, false_func)
|
||||
|
||||
exportModel('conditional_block_const_2outputs', test_model_2outputs, [data], target_dir=sys.argv[1])
|
||||
|
||||
|
||||
'''
|
||||
more than one select_input with 2 inputs and 2 select_input nodes.
|
||||
'''
|
||||
@paddle.jit.to_static
|
||||
def test_model_2inputs_2outputs(a, b):
|
||||
return paddle.static.nn.cond(a < b, lambda: (a, a * b), lambda: (b, a * b) )
|
||||
|
||||
a = np.full(shape=[1], dtype='float32', fill_value=0.1)
|
||||
b = np.full(shape=[1], dtype='float32', fill_value=0.23)
|
||||
exportModel('conditional_block_2inputs_2outputs', test_model_2inputs_2outputs, [a, b], target_dir=sys.argv[1])
|
||||
|
||||
'''
|
||||
simple test case with 2 inputs to conditional_block node.
|
||||
'''
|
||||
@paddle.jit.to_static
|
||||
def test_model2(a, b):
|
||||
c = a * b
|
||||
return paddle.static.nn.cond(a < b, lambda: a + c, lambda: b * b)
|
||||
|
||||
a = np.full(shape=[1], dtype='float32', fill_value=0.1)
|
||||
b = np.full(shape=[1], dtype='float32', fill_value=0.23)
|
||||
exportModel('conditional_block_2inputs', test_model2, [a, b], target_dir=sys.argv[1])
|
||||
|
||||
|
||||
'''
|
||||
'''
|
||||
@paddle.jit.to_static
|
||||
def test_model_dyn(a, b):
|
||||
c = a * b
|
||||
return a + c if a < b else b * b
|
||||
|
||||
a = np.full(shape=[1], dtype='float32', fill_value=0.1)
|
||||
b = np.full(shape=[1], dtype='float32', fill_value=0.23)
|
||||
exportModel('conditional_block_2inputs_dyn', test_model_dyn, [a, b], target_dir=sys.argv[1])
|
||||
|
||||
'''
|
||||
more than one select_input
|
||||
# looks there are bugs in paddle dyngraph to static... failed to generate 2 select_inputs.
|
||||
'''
|
||||
@paddle.jit.to_static
|
||||
def test_model_dyn_2outputs(a, b):
|
||||
c = a * b
|
||||
return a, c if a < b else b, c
|
||||
|
||||
a = np.full(shape=[1], dtype='float32', fill_value=0.1)
|
||||
b = np.full(shape=[1], dtype='float32', fill_value=0.23)
|
||||
exportModel('conditional_block_2inputs_dyn_2outputs', test_model_dyn_2outputs, [a, b], target_dir=sys.argv[1])
|
||||
|
||||
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
"""question: how to make test case for only one
|
||||
conditional_block ?? """
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
@paddle.jit.to_static
|
||||
def test_model_dyn_conditionalblock_only(a):
|
||||
rpn_rois_list = []
|
||||
|
||||
if a.shape[0] >= 1:
|
||||
rpn_rois_list.append(a)
|
||||
|
||||
return rpn_rois_list[0]
|
||||
|
||||
a = paddle.to_tensor([[1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0]])
|
||||
exportModel('conditional_block_dyn_conditionalblock_only', test_model_dyn_conditionalblock_only, [a], target_dir=sys.argv[1])
|
||||
|
||||
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
"""return type: tensor, tuple(tensor), list(tensor)
|
||||
question: how to work with LoDTensorArray ?? """
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
# '''
|
||||
# test: only conditiona_block node in the pattern.
|
||||
# '''
|
||||
# @paddle.jit.to_static
|
||||
# def test_model_return_tuple(a, b):
|
||||
# return paddle.static.nn.cond(a < b, lambda: (tuple((a, a * b))), lambda: (b, a * b) )
|
||||
|
||||
# a = np.full(shape=[1], dtype='float32', fill_value=0.1)
|
||||
# b = np.full(shape=[1], dtype='float32', fill_value=0.23)
|
||||
# exportModel('conditional_block_return_tuple', test_model_return_tuple, [a, b], target_dir=sys.argv[1])
|
||||
|
||||
# x = np.full(shape=[1], dtype='float32', fill_value=0.1)
|
||||
# y = np.full(shape=[1], dtype='float32', fill_value=0.23)
|
||||
# data = np.less(y,x)
|
||||
# def test_model(pred):
|
||||
# # pred: A boolean tensor whose numel should be 1.
|
||||
# def true_func():
|
||||
# return (paddle.full(shape=[3, 4], dtype='float32', # TODO: FAILED with different dtype
|
||||
# fill_value=1), paddle.full(shape=[3, 4], dtype='float32', # TODO: FAILED with different dtype
|
||||
# fill_value=1))
|
||||
|
||||
# def false_func():
|
||||
# return (paddle.full(shape=[1, 2], dtype='float32',
|
||||
# fill_value=3),paddle.full(shape=[1, 2], dtype='float32',
|
||||
# fill_value=3))
|
||||
|
||||
# return paddle.static.nn.cond(pred, true_func, false_func)
|
||||
|
||||
# exportModel('conditional_block_return_tuple2', test_model, [data], target_dir=sys.argv[1])
|
||||
|
||||
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
"""stack blocks"""
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
'''
|
||||
select_output with multiple consumers.
|
||||
'''
|
||||
@paddle.jit.to_static
|
||||
def test_model_dyn_multiple_consumers(a, b):
|
||||
c = a * b
|
||||
cond0 = a + c if a < b else b * b
|
||||
o1 = cond0 + a
|
||||
o2 = cond0 + b
|
||||
return o1, o2
|
||||
|
||||
a = np.full(shape=[1], dtype='float32', fill_value=0.1)
|
||||
b = np.full(shape=[1], dtype='float32', fill_value=0.23)
|
||||
exportModel('conditional_block_dyn_multiple_consumers', test_model_dyn_multiple_consumers, [a, b], target_dir=sys.argv[1])
|
||||
|
||||
'''
|
||||
stack if-else blocks
|
||||
'''
|
||||
@paddle.jit.to_static
|
||||
def test_model_dyn_multiple_blocks(a, b, c):
|
||||
cond0 = a + c if a < b else b * b
|
||||
cond1 = a - c if b < c else b * b
|
||||
|
||||
return cond0, cond1
|
||||
|
||||
a = np.full(shape=[1], dtype='float32', fill_value=0.1)
|
||||
b = np.full(shape=[1], dtype='float32', fill_value=0.23)
|
||||
c = np.full(shape=[1], dtype='float32', fill_value=0.9)
|
||||
exportModel('conditional_block_dyn_multiple_blocks', test_model_dyn_multiple_blocks, [a, b, c], target_dir=sys.argv[1])
|
||||
|
||||
@paddle.jit.to_static
|
||||
def test_model_dyn_multiple_blocks2(a, b, c):
|
||||
cond0 = a + c if a < b else b * b
|
||||
cond1 = a - c if cond0 < c else b * b
|
||||
|
||||
return cond0, cond1
|
||||
|
||||
a = np.full(shape=[1], dtype='float32', fill_value=0.1)
|
||||
b = np.full(shape=[1], dtype='float32', fill_value=0.23)
|
||||
c = np.full(shape=[1], dtype='float32', fill_value=0.9)
|
||||
exportModel('conditional_block_dyn_multiple_blocks2', test_model_dyn_multiple_blocks2, [a, b, c], target_dir=sys.argv[1])
|
||||
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
# ref: https://www.paddlepaddle.org.cn/tutorials/projectdetail/1998893#anchor-2
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
import paddle
|
||||
|
||||
from save_model import exportModel, saveModel
|
||||
|
||||
|
||||
def loop():
|
||||
paddle.enable_static()
|
||||
x = np.full(shape=[1], fill_value=0, dtype='int64')
|
||||
|
||||
def cond(i, ten):
|
||||
return ten >= i
|
||||
|
||||
def body(i, dummy):
|
||||
i = i + 1
|
||||
return i, dummy
|
||||
|
||||
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
|
||||
node_x = paddle.static.data(name='x', shape=x.shape, dtype=x.dtype)
|
||||
node_i = paddle.full(shape=[1], fill_value=0, dtype='int64', name='i')
|
||||
node_i = paddle.fluid.layers.nn.elementwise_add(node_i, node_x)
|
||||
node_ten = paddle.full(shape=[1], fill_value=10, dtype='int64', name='ten')
|
||||
|
||||
out, dummy = paddle.static.nn.while_loop(cond, body, [node_i, node_ten], name='while_loop')
|
||||
|
||||
exe = paddle.static.Executor(place=paddle.CPUPlace())
|
||||
res = exe.run(paddle.static.default_main_program(), feed={'x':x}, fetch_list=out)
|
||||
|
||||
saveModel('loop', exe, feedkeys=['x'], fetchlist=[out], inputs=[x], outputs=[res[0]], target_dir=sys.argv[1])
|
||||
|
||||
return res
|
||||
|
||||
def loop_x():
|
||||
paddle.enable_static()
|
||||
x = np.full(shape=[1], fill_value=1, dtype='int64')
|
||||
|
||||
def cond(i, ten):
|
||||
return ten >= i
|
||||
|
||||
def body(i, t):
|
||||
i = i + x
|
||||
return i, t
|
||||
|
||||
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
|
||||
node_x = paddle.static.data(name='x', shape=x.shape, dtype=x.dtype)
|
||||
node_i = paddle.full(shape=[1], fill_value=0, dtype='int64', name='i')
|
||||
node_i = paddle.fluid.layers.nn.elementwise_add(node_i, node_x)
|
||||
node_ten = paddle.full(shape=[1], fill_value=10, dtype='int64', name='ten')
|
||||
|
||||
out, dummy = paddle.static.nn.while_loop(cond, body, [node_i, node_ten], name='while_loop')
|
||||
|
||||
exe = paddle.static.Executor(place=paddle.CPUPlace())
|
||||
res = exe.run(paddle.static.default_main_program(), feed={'x':x}, fetch_list=out)
|
||||
|
||||
saveModel('loop_x', exe, feedkeys=['x'], fetchlist=[out], inputs=[x], outputs=[res[0]], target_dir=sys.argv[1])
|
||||
|
||||
return res
|
||||
|
||||
def loop_t():
|
||||
paddle.enable_static()
|
||||
x = np.full(shape=[1], fill_value=0, dtype='int64')
|
||||
|
||||
def cond(i, ten):
|
||||
return ten >= i
|
||||
|
||||
def body(i, t):
|
||||
i = i + 1
|
||||
t = t - 1
|
||||
return i, t
|
||||
|
||||
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
|
||||
node_x = paddle.static.data(name='x', shape=x.shape, dtype=x.dtype)
|
||||
node_i = paddle.full(shape=[1], fill_value=0, dtype='int64', name='i')
|
||||
node_i = paddle.fluid.layers.nn.elementwise_add(node_i, node_x)
|
||||
node_ten = paddle.full(shape=[1], fill_value=10, dtype='int64', name='ten')
|
||||
|
||||
out_i,out_t = paddle.static.nn.while_loop(cond, body, [node_i, node_ten], name='while_loop')
|
||||
|
||||
exe = paddle.static.Executor(place=paddle.CPUPlace())
|
||||
res = exe.run(paddle.static.default_main_program(), feed={'x':x}, fetch_list=[out_i,out_t])
|
||||
|
||||
saveModel('loop_t', exe, feedkeys=['x'], fetchlist=[out_i,out_t], inputs=[x], outputs=res, target_dir=sys.argv[1])
|
||||
|
||||
return res
|
||||
|
||||
def loop_dyn():
|
||||
paddle.disable_static()
|
||||
|
||||
@paddle.jit.to_static
|
||||
def test_model(x):
|
||||
i = paddle.full(shape=[1], fill_value=0, dtype='int64')
|
||||
|
||||
i = i + x
|
||||
t = paddle.full(shape=[1], fill_value=10, dtype='int64')
|
||||
j = i + 1
|
||||
|
||||
while t >= i:
|
||||
i = i + 1
|
||||
|
||||
return i, j
|
||||
|
||||
x = np.full(shape=[1], fill_value=0, dtype='int64')
|
||||
return exportModel('loop_dyn', test_model, [x], target_dir=sys.argv[1])
|
||||
|
||||
|
||||
def loop_dyn_x():
|
||||
paddle.disable_static()
|
||||
|
||||
@paddle.jit.to_static
|
||||
def test_model(x):
|
||||
i = paddle.full(shape=[1], fill_value=0, dtype='int64')
|
||||
|
||||
i = i + x
|
||||
t = paddle.full(shape=[1], fill_value=10, dtype='int64')
|
||||
|
||||
while t >= i:
|
||||
i = i + x
|
||||
|
||||
return i
|
||||
|
||||
x = np.full(shape=[1], fill_value=1, dtype='int64')
|
||||
return exportModel('loop_dyn_x', test_model, [x], target_dir=sys.argv[1])
|
||||
|
||||
def loop_if():
|
||||
paddle.disable_static()
|
||||
|
||||
@paddle.jit.to_static
|
||||
def test_model(x):
|
||||
i = paddle.full(shape=[1], fill_value=0, dtype='int64')
|
||||
|
||||
i = i + x
|
||||
t = paddle.full(shape=[1], fill_value=10, dtype='int64')
|
||||
|
||||
while t >= i:
|
||||
if i < 5:
|
||||
i = i + x
|
||||
else:
|
||||
i = i + 2 * x
|
||||
|
||||
return i
|
||||
|
||||
x = np.full(shape=[1], fill_value=1, dtype='int64')
|
||||
return exportModel('loop_if', test_model, [x], target_dir=sys.argv[1])
|
||||
|
||||
def loop_if_loop():
|
||||
paddle.disable_static()
|
||||
|
||||
@paddle.jit.to_static
|
||||
def test_model(x):
|
||||
i = paddle.full(shape=[1], fill_value=0, dtype='int64')
|
||||
|
||||
i = i + x
|
||||
t = paddle.full(shape=[1], fill_value=10, dtype='int64')
|
||||
|
||||
if x < 5:
|
||||
while t >= i:
|
||||
i = i + x * 2
|
||||
else:
|
||||
while t >= i:
|
||||
i = i + x
|
||||
|
||||
return i
|
||||
|
||||
x = np.full(shape=[1], fill_value=1, dtype='int64')
|
||||
return exportModel('loop_if_loop', test_model, [x], target_dir=sys.argv[1])
|
||||
|
||||
def loop_if_loop_if():
|
||||
paddle.disable_static()
|
||||
|
||||
@paddle.jit.to_static
|
||||
def test_model(x):
|
||||
i = paddle.full(shape=[1], fill_value=0, dtype='int64')
|
||||
|
||||
i = i + x
|
||||
t = paddle.full(shape=[1], fill_value=10, dtype='int64')
|
||||
|
||||
if x < 5:
|
||||
while t >= i:
|
||||
if x == 0:
|
||||
i = i + x * 2
|
||||
else:
|
||||
i = i + x * 3
|
||||
else:
|
||||
while t >= i:
|
||||
i = i + x
|
||||
|
||||
return i
|
||||
|
||||
x = np.full(shape=[1], fill_value=1, dtype='int64')
|
||||
return exportModel('loop_if_loop_if', test_model, [x], target_dir=sys.argv[1])
|
||||
|
||||
def loop_if_loop_complex():
|
||||
paddle.disable_static()
|
||||
|
||||
@paddle.jit.to_static
|
||||
def test_model(x, y):
|
||||
i = paddle.full(shape=[1], fill_value=0, dtype='int64')
|
||||
j = paddle.full(shape=[1], fill_value=0, dtype='int64')
|
||||
|
||||
i = i + x
|
||||
j = j + y
|
||||
t = paddle.full(shape=[1], fill_value=10, dtype='int64')
|
||||
|
||||
if x < 5:
|
||||
if y < 44:
|
||||
while t >= i:
|
||||
while t >= i:
|
||||
if x == 0:
|
||||
i = i + x * 2
|
||||
j = j + y * 2
|
||||
else:
|
||||
i = i + x * 3
|
||||
j = j + y * 3
|
||||
else:
|
||||
while t >= i:
|
||||
i = i + x
|
||||
|
||||
return i, j
|
||||
|
||||
x = np.full(shape=[1], fill_value=1, dtype='int64')
|
||||
y = np.full(shape=[1], fill_value=1, dtype='int64')
|
||||
return exportModel('loop_if_loop_complex', test_model, [x, y], target_dir=sys.argv[1])
|
||||
|
||||
def loop_tensor_array():
|
||||
paddle.disable_static()
|
||||
|
||||
@paddle.jit.to_static
|
||||
def test_model(x):
|
||||
i = paddle.full(shape=[1], fill_value=0, dtype='int32')
|
||||
|
||||
t = paddle.full(shape=[1], fill_value=10, dtype='int32')
|
||||
y = paddle.full(shape=[30,3], fill_value=2, dtype='float32')
|
||||
|
||||
result = []
|
||||
while t >= i:
|
||||
i = i + 1
|
||||
result.append(x[0:2,:])
|
||||
|
||||
return paddle.concat(result)
|
||||
|
||||
x = np.full(shape=[30,3], fill_value=1, dtype='float32')
|
||||
return exportModel('loop_tensor_array', test_model, [x], target_dir=sys.argv[1])
|
||||
|
||||
def loop_if_tensor_array():
|
||||
paddle.disable_static()
|
||||
|
||||
@paddle.jit.to_static
|
||||
def test_model(x):
|
||||
i = paddle.full(shape=[1], fill_value=0, dtype='int32')
|
||||
|
||||
t = paddle.full(shape=[1], fill_value=10, dtype='int32')
|
||||
y = paddle.full(shape=[30,3], fill_value=2, dtype='float32')
|
||||
|
||||
result1 = []
|
||||
result2 = []
|
||||
while t >= i:
|
||||
i = i + 1
|
||||
if i >= 0:
|
||||
v = i + 1
|
||||
result1.append(x[0:i,:])
|
||||
result2.append(x[0:v,:] + 2)
|
||||
else:
|
||||
result1.append(x[0:i,:])
|
||||
#result2.append(x[0:i,:])
|
||||
return paddle.concat(result1), paddle.concat(result2)
|
||||
|
||||
x = np.full(shape=[30,3], fill_value=1, dtype='float32')
|
||||
return exportModel('loop_if_tensor_array', test_model, [x], target_dir=sys.argv[1])
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(loop())
|
||||
print(loop_dyn())
|
||||
|
||||
print(loop_t())
|
||||
print(loop_x())
|
||||
|
||||
print(loop_dyn_x().numpy())
|
||||
print(loop_if().numpy())
|
||||
print(loop_if_loop().numpy())
|
||||
print(loop_if_loop_if().numpy())
|
||||
print(loop_if_loop_complex())
|
||||
print(loop_tensor_array().numpy())
|
||||
x, y = loop_if_tensor_array()
|
||||
@@ -0,0 +1,357 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
import paddle
|
||||
|
||||
from save_model import exportModel
|
||||
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
# tensorarray case: conditional_block + slice[0]
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
def test_conditional_block_slice0():
|
||||
def test(model_name, inputs:list, input_shapes=[]):
|
||||
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
|
||||
@paddle.jit.to_static
|
||||
def test_model(a):
|
||||
rpn_rois_list = []
|
||||
|
||||
if a.shape[0] >= 1:
|
||||
rpn_rois_list.append(a)
|
||||
|
||||
return rpn_rois_list[0]
|
||||
exportModel(model_name, test_model, inputs, target_dir=sys.argv[1], dyn_shapes=input_shapes)
|
||||
|
||||
a = paddle.to_tensor([[1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0]])
|
||||
test('conditional_block_slice0', [a])
|
||||
a_shape = a.shape
|
||||
a_shape[0] = -1
|
||||
test('conditional_block_slice0_dyn', [a], [a_shape])
|
||||
|
||||
def test_conditional_block_slice0_else():
|
||||
def test(model_name, inputs:list, input_shapes=[]):
|
||||
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
|
||||
@paddle.jit.to_static
|
||||
def test_model(a):
|
||||
rpn_rois_list = []
|
||||
|
||||
if a.shape[0] >= 1:
|
||||
rpn_rois_list.append(a)
|
||||
else:
|
||||
rpn_rois_list.append(a)
|
||||
|
||||
return rpn_rois_list[0]
|
||||
exportModel(model_name, test_model, inputs, target_dir=sys.argv[1], dyn_shapes=input_shapes)
|
||||
|
||||
a = paddle.to_tensor([[1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0]])
|
||||
test('conditional_block_slice0_else', [a])
|
||||
|
||||
# wrong test case. paddle throw error " IndexError: list index out of range" in paddle.jit.save()
|
||||
def test_conditional_block_slice0_empty():
|
||||
def test(model_name, inputs:list, input_shapes=[]):
|
||||
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
|
||||
@paddle.jit.to_static
|
||||
def test_model(a):
|
||||
rpn_rois_list = []
|
||||
|
||||
return rpn_rois_list[0]
|
||||
exportModel(model_name, test_model, inputs, target_dir=sys.argv[1], dyn_shapes=input_shapes)
|
||||
|
||||
a = paddle.to_tensor([[1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0]])
|
||||
test('conditional_block_slice0_empty', [a])
|
||||
|
||||
# wrong test case. paddle throw error " IndexError: list index out of range" in paddle.jit.save()
|
||||
def test_conditional_block_concat_empty():
|
||||
def test(model_name, inputs:list, input_shapes=[]):
|
||||
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
|
||||
@paddle.jit.to_static
|
||||
def test_model(a):
|
||||
rpn_rois_list = []
|
||||
|
||||
return paddle.concat(rpn_rois_list)
|
||||
exportModel(model_name, test_model, inputs, target_dir=sys.argv[1], dyn_shapes=input_shapes)
|
||||
|
||||
a = paddle.to_tensor([[1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0]])
|
||||
test('conditional_block_concat_empty', [a])
|
||||
|
||||
# could generate paddle model, but paddle throw exception during inferencing.
|
||||
def test_conditional_block_slice0_empty_false():
|
||||
def test(model_name, inputs:list, input_shapes=[]):
|
||||
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
|
||||
@paddle.jit.to_static
|
||||
def test_model(a):
|
||||
rpn_rois_list = []
|
||||
|
||||
if a.shape[0] >= 1000: # false condition
|
||||
rpn_rois_list.append(a)
|
||||
|
||||
return rpn_rois_list[0]
|
||||
exportModel(model_name, test_model, inputs, target_dir=sys.argv[1], dyn_shapes=input_shapes)
|
||||
|
||||
a = paddle.to_tensor([[1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0]])
|
||||
test('conditional_block_slice0_empty_false', [a])
|
||||
|
||||
def test_conditional_block_slice0_scaler():
|
||||
def test(model_name, inputs:list, input_shapes=[]):
|
||||
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
|
||||
@paddle.jit.to_static
|
||||
def test_model(a):
|
||||
rpn_rois_list = []
|
||||
|
||||
if a.shape[0] >= 1:
|
||||
rpn_rois_list.append(a)
|
||||
|
||||
return rpn_rois_list[0]
|
||||
return exportModel(model_name, test_model, inputs, target_dir=sys.argv[1], dyn_shapes=input_shapes)
|
||||
|
||||
a = paddle.to_tensor(10.0)
|
||||
print(test('conditional_block_slice0_scaler', [a]))
|
||||
a_shape = a.shape
|
||||
a_shape[0] = -1
|
||||
print(test('conditional_block_slice0_scaler_dyn', [a], [a_shape]))
|
||||
|
||||
# No such case in faster/mask rcnn... as paddle.concat always concat along axis 0.
|
||||
def test_conditional_block_slice0_axis2():
|
||||
def test(model_name, inputs:list, input_shapes=[]):
|
||||
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
|
||||
@paddle.jit.to_static
|
||||
def test_model(a):
|
||||
rpn_rois_list = []
|
||||
|
||||
if a.shape[0] >= 1:
|
||||
rpn_rois_list.append(a)
|
||||
|
||||
return rpn_rois_list[0]
|
||||
return exportModel(model_name, test_model, inputs, target_dir=sys.argv[1], dyn_shapes=input_shapes)
|
||||
|
||||
a = paddle.arange(2*3*4*5)
|
||||
a = paddle.reshape(a, (2, 3, 4, 5)).astype('float32')
|
||||
print(test('conditional_block_slice0_axis2', [a]))
|
||||
a_shape = a.shape
|
||||
a_shape[2] = -1 # instead of 0
|
||||
print(test('conditional_block_slice0_axis2_dyn', [a], [a_shape]))
|
||||
|
||||
# No such case in faster/mask rcnn... as paddle.concat always concat along axis 0.
|
||||
def test_conditional_block_slice0_aixs1_axis2():
|
||||
def test(model_name, inputs:list, input_shapes=[]):
|
||||
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
|
||||
@paddle.jit.to_static
|
||||
def test_model(a):
|
||||
rpn_rois_list = []
|
||||
|
||||
if a.shape[0] >= 1:
|
||||
rpn_rois_list.append(a)
|
||||
|
||||
return rpn_rois_list[0]
|
||||
return exportModel(model_name, test_model, inputs, target_dir=sys.argv[1], dyn_shapes=input_shapes)
|
||||
|
||||
a = paddle.arange(2,2,3)
|
||||
a = paddle.reshape(a, (2,2,3)).astype('float32')
|
||||
print(test('conditional_block_slice0_axis1_axis2', [a]))
|
||||
a_shape = a.shape
|
||||
a_shape[1] = -1 # instead of 0
|
||||
a_shape[2] = -1 # instead of 0
|
||||
print(test('conditional_block_slice0_axis1_axis2_dyn', [a], [a_shape]))
|
||||
|
||||
def test_conditional_block_slice1():
|
||||
def test(model_name, inputs:list, input_shapes=[]):
|
||||
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
|
||||
@paddle.jit.to_static
|
||||
def test_model(a, b):
|
||||
rpn_rois_list = []
|
||||
|
||||
if a.shape[0] >= 1:
|
||||
rpn_rois_list.append(a)
|
||||
rpn_rois_list.append(b)
|
||||
|
||||
return rpn_rois_list[1]
|
||||
exportModel(model_name, test_model, inputs, target_dir=sys.argv[1], dyn_shapes=input_shapes)
|
||||
|
||||
a = paddle.to_tensor([[1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0]])
|
||||
b = paddle.to_tensor( [[7.0, 8.0, 9.0]])
|
||||
test('conditional_block_slice1', [a, b])
|
||||
a_shape = a.shape
|
||||
a_shape[0] = -1
|
||||
test('conditional_block_slice1_dyn', [a, b], [a_shape, b.shape])
|
||||
|
||||
def test_conditional_block_slice0_slice1():
|
||||
def test(model_name, inputs:list, input_shapes=[]):
|
||||
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
|
||||
@paddle.jit.to_static
|
||||
def test_model(a, b):
|
||||
rpn_rois_list = []
|
||||
|
||||
if a.shape[0] >= 1:
|
||||
rpn_rois_list.append(a)
|
||||
rpn_rois_list.append(b)
|
||||
|
||||
return rpn_rois_list[0], rpn_rois_list[1]
|
||||
exportModel(model_name, test_model, inputs, target_dir=sys.argv[1], dyn_shapes=input_shapes)
|
||||
|
||||
a = paddle.to_tensor([[1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0]])
|
||||
b = paddle.to_tensor( [[7.0, 8.0, 9.0]])
|
||||
test('conditional_block_slice0_slice1', [a, b])
|
||||
a_shape = a.shape
|
||||
a_shape[0] = -1
|
||||
test('conditional_block_slice0_slice1_dyn', [a, b], [a_shape, b.shape])
|
||||
|
||||
def test_conditional_block_slice0_2tensorarrays():
|
||||
def test(model_name, inputs:list, input_shapes=[]):
|
||||
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
|
||||
@paddle.jit.to_static
|
||||
def test_model(a, b):
|
||||
rpn_rois_list = []
|
||||
rpn_rois_list1 = []
|
||||
|
||||
if a.shape[0] >= 1:
|
||||
rpn_rois_list.append(a)
|
||||
rpn_rois_list1.append(b)
|
||||
|
||||
return rpn_rois_list[0], rpn_rois_list1[0]
|
||||
exportModel(model_name, test_model, inputs, target_dir=sys.argv[1], dyn_shapes=input_shapes)
|
||||
|
||||
a = paddle.to_tensor([[1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0]])
|
||||
b = paddle.to_tensor( [[7.0, 8.0, 9.0]])
|
||||
test('conditional_block_slice0_2tensorarrays', [a, b])
|
||||
a_shape = a.shape
|
||||
a_shape[0] = -1
|
||||
b_shape = b.shape
|
||||
b_shape[0] = -1
|
||||
test('conditional_block_slice0_2tensorarrays_dyn', [a, b], [a_shape, b_shape])
|
||||
|
||||
def test_conditional_block_slice0_2tensorarrays_extra():
|
||||
def test(model_name, inputs:list, input_shapes=[]):
|
||||
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
|
||||
@paddle.jit.to_static
|
||||
def test_model(a, b):
|
||||
rpn_rois_list = []
|
||||
rpn_rois_list1 = []
|
||||
|
||||
if a.shape[0] >= 1:
|
||||
c = a + b
|
||||
rpn_rois_list.append(a)
|
||||
rpn_rois_list.append(c)
|
||||
rpn_rois_list1.append(b)
|
||||
|
||||
return paddle.concat(rpn_rois_list), rpn_rois_list1[0]
|
||||
exportModel(model_name, test_model, inputs, target_dir=sys.argv[1], dyn_shapes=input_shapes)
|
||||
|
||||
a = paddle.to_tensor([[1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0]])
|
||||
b = paddle.to_tensor( [[7.0, 8.0, 9.0]])
|
||||
test('conditional_block_slice0_2tensorarrays_extra', [a, b])
|
||||
a_shape = a.shape
|
||||
a_shape[0] = -1
|
||||
b_shape = b.shape
|
||||
b_shape[0] = -1
|
||||
test('conditional_block_slice0_2tensorarrays_extra_dyn', [a, b], [a_shape, b_shape])
|
||||
|
||||
def test_conditional_block_concat():
|
||||
def test(model_name, inputs:list, input_shapes=[]):
|
||||
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
|
||||
@paddle.jit.to_static
|
||||
def test_model(a, b):
|
||||
rpn_rois_list = []
|
||||
|
||||
if a.shape[0] >= 1:
|
||||
rpn_rois_list.append(a)
|
||||
rpn_rois_list.append(b)
|
||||
|
||||
return paddle.concat(rpn_rois_list)
|
||||
|
||||
exportModel(model_name, test_model, inputs, target_dir=sys.argv[1], dyn_shapes=input_shapes)
|
||||
|
||||
a = paddle.to_tensor([[[1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0]]])
|
||||
b = paddle.to_tensor([[[7.0, 8.0, 9.0],
|
||||
[10.0, 11.0, 12.0]]])
|
||||
test('conditional_block_concat', [a, b])
|
||||
|
||||
a_shape = a.shape
|
||||
a_shape[1] = -1
|
||||
test('conditional_block_concat_dyn', [a, b], [a_shape, b.shape])
|
||||
|
||||
# the case of mask_rcnn_r50_1x_coco block13.
|
||||
a_shape = a.shape
|
||||
a_shape[1] = -1
|
||||
a_shape[2] = -1
|
||||
test('conditional_block_concat_dyn_2axes', [a, b], [a_shape, b.shape])
|
||||
|
||||
# the condition is false
|
||||
def test_conditional_block_concat_false():
|
||||
def test(model_name, inputs:list, input_shapes=[]):
|
||||
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
|
||||
@paddle.jit.to_static
|
||||
def test_model(a, b):
|
||||
rpn_rois_list = []
|
||||
|
||||
if a.shape[0] >= 1:
|
||||
rpn_rois_list.append(a)
|
||||
|
||||
if a.shape[0] >= 100: # False condition
|
||||
rpn_rois_list.append(b)
|
||||
|
||||
return paddle.concat(rpn_rois_list)
|
||||
|
||||
exportModel(model_name, test_model, inputs, target_dir=sys.argv[1], dyn_shapes=input_shapes)
|
||||
|
||||
a = paddle.to_tensor([[1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0]])
|
||||
b = paddle.to_tensor([[7.0, 8.0, 9.0],
|
||||
[10.0, 11.0, 12.0]])
|
||||
test('conditional_block_concat_false', [a, b])
|
||||
|
||||
a_shape = a.shape
|
||||
a_shape[0] = -1
|
||||
test('conditional_block_concat_false_dyn', [a, b], [a_shape, b.shape])
|
||||
|
||||
"""
|
||||
conditional_block connects to another conditional_block.
|
||||
This is the case of faster/mask rcnn with fpn.
|
||||
"""
|
||||
def test_conditional_block_conditional_block_concat():
|
||||
def test(model_name, inputs:list, input_shapes=[]):
|
||||
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
|
||||
@paddle.jit.to_static
|
||||
def test_model(a):
|
||||
rpn_rois_list = []
|
||||
|
||||
for i in range(5):
|
||||
if a.shape[0] >= 1:
|
||||
rpn_rois_list.append(a)
|
||||
|
||||
return paddle.concat(rpn_rois_list)
|
||||
|
||||
exportModel(model_name, test_model, inputs, target_dir=sys.argv[1], dyn_shapes=input_shapes)
|
||||
|
||||
a = paddle.to_tensor([[1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0]])
|
||||
test('conditional_block_conditional_block_concat', [a])
|
||||
a_shape = a.shape
|
||||
a_shape[0] = -1
|
||||
test('conditional_block_conditional_block_concat_dyn', [a], [a_shape])
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_conditional_block_slice0()
|
||||
#test_conditional_block_slice1()
|
||||
#test_conditional_block_slice0_slice1()
|
||||
test_conditional_block_slice0_scaler()
|
||||
test_conditional_block_concat()
|
||||
test_conditional_block_concat_false()
|
||||
test_conditional_block_conditional_block_concat()
|
||||
test_conditional_block_slice0_axis2()
|
||||
#test_conditional_block_slice0_aixs1_axis2()
|
||||
test_conditional_block_slice0_2tensorarrays()
|
||||
test_conditional_block_slice0_2tensorarrays_extra()
|
||||
test_conditional_block_slice0_else()
|
||||
# test_conditional_block_slice0_empty()
|
||||
# test_conditional_block_concat_empty()
|
||||
# test_conditional_block_slice0_empty_false()
|
||||
@@ -346,12 +346,12 @@ TEST(type_prop, loop_operation_for_and_condition_mode_dynamic_iter_dynamic_shape
|
||||
// trip_count = 10
|
||||
// execution_condition = true
|
||||
// body_condition is not a Constant
|
||||
// inputs have partially known shape
|
||||
// inputs have dyanamic shape
|
||||
// concat output has dynamic dimension on axis position, another outputs are static
|
||||
TEST(type_prop, loop_operation_for_and_condition_mode_dynamic_iter_partially_dynamic_shapes) {
|
||||
// That which we iterate over
|
||||
auto X = make_shared<opset5::Parameter>(element::f32, PartialShape{1, 2, 3, Dimension::dynamic()});
|
||||
auto Y = make_shared<opset5::Parameter>(element::f32, PartialShape{1, 2, 3, Dimension::dynamic()});
|
||||
auto X = make_shared<opset5::Parameter>(element::f32, PartialShape{Dimension::dynamic()});
|
||||
auto Y = make_shared<opset5::Parameter>(element::f32, PartialShape{Dimension::dynamic()});
|
||||
auto M = make_shared<opset5::Parameter>(element::f32, Shape{1});
|
||||
|
||||
// Set up the cell body, a function from (Xi, Yi) -> (Zo)
|
||||
@@ -397,8 +397,8 @@ TEST(type_prop, loop_operation_for_and_condition_mode_dynamic_iter_partially_dyn
|
||||
// Output 0 is last Zo
|
||||
auto out0 = loop->get_iter_value(body_condition, -1);
|
||||
auto out1 = loop->get_iter_value(Zo, -1);
|
||||
// axis=1 so sliced output on this dimension will be dynamic
|
||||
auto out2 = loop->get_concatenated_slices(Zo, 0, 1, 1, -1, 1);
|
||||
// axis=0 so sliced output on this dimension will be dynamic
|
||||
auto out2 = loop->get_concatenated_slices(Zo, 0, 1, 1, -1, 0);
|
||||
|
||||
// check output descriptors
|
||||
for (auto& desc : loop->get_output_descriptions()) {
|
||||
@@ -415,8 +415,8 @@ TEST(type_prop, loop_operation_for_and_condition_mode_dynamic_iter_partially_dyn
|
||||
auto result1 = make_shared<opset5::Result>(out1);
|
||||
auto result2 = make_shared<opset5::Result>(out2);
|
||||
Shape out0_shape{1};
|
||||
PartialShape out1_shape{1, 2, 3, Dimension::dynamic()};
|
||||
PartialShape out2_shape{1, Dimension::dynamic(), 3, Dimension::dynamic()};
|
||||
PartialShape out1_shape{Dimension::dynamic()};
|
||||
PartialShape out2_shape{Dimension::dynamic()};
|
||||
|
||||
auto results = ResultVector{result0, result1, result2};
|
||||
auto f = make_shared<Function>(results, ParameterVector{X, Y, M});
|
||||
@@ -1016,3 +1016,405 @@ TEST(type_prop, loop_operation_dynamic_iter_dynamic_shapes_sliced_inputs_concate
|
||||
EXPECT_EQ(loop->get_output_partial_shape(1), out1_shape);
|
||||
EXPECT_EQ(loop->get_output_partial_shape(2), out2_shape);
|
||||
}
|
||||
|
||||
// dynamic output
|
||||
// trip_count = dynamic
|
||||
// execution_condition = true
|
||||
// body_condition = true
|
||||
// input is static shape, sub-model output shapes has one dynamic dimension and this output is a backedge to a
|
||||
// parameter, other shapes are static
|
||||
TEST(type_prop, loop_operation_dynamic_iter_static_shapes_inputs_dynamic_shape_outputs) {
|
||||
// That which we iterate over
|
||||
auto X = make_shared<opset5::Parameter>(element::f32, PartialShape{1, 1, 10});
|
||||
auto T = make_shared<opset5::Parameter>(element::i64, Shape{});
|
||||
|
||||
// Set up the cell body, a function from (Xi) -> Concat(Xi, C) -> (Zo)
|
||||
// Body parameters
|
||||
auto Xi = make_shared<opset5::Parameter>(element::f32, PartialShape{1, Dimension::dynamic(), 10});
|
||||
|
||||
auto body_condition = make_shared<opset5::Constant>(element::boolean, Shape{}, true);
|
||||
auto trip_count = std::make_shared<ngraph::opset5::Constant>(ngraph::element::i64, ngraph::Shape{1}, 10);
|
||||
auto exec_condition = make_shared<opset5::Constant>(element::boolean, Shape{}, true);
|
||||
|
||||
// Body
|
||||
auto C = opset5::Constant::create(element::f32, {1, 1, 10}, {0});
|
||||
auto Zo = make_shared<opset5::Concat>(NodeVector{Xi, C}, 1);
|
||||
auto Z = make_shared<opset5::Result>(Zo);
|
||||
auto body = make_shared<Function>(OutputVector{Z, body_condition}, ParameterVector{Xi});
|
||||
|
||||
auto loop = make_shared<opset5::Loop>(T, exec_condition);
|
||||
loop->set_function(body);
|
||||
loop->set_special_body_ports(opset5::Loop::SpecialBodyPorts{-1, 1});
|
||||
loop->set_merged_input(Xi, X, Z);
|
||||
|
||||
// check input descriptors
|
||||
for (auto& desc : loop->get_input_descriptions()) {
|
||||
auto type_info = desc->get_type_info();
|
||||
if (std::strcmp(type_info.name, "InvariantInputDescription") == 0) {
|
||||
auto input_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::InvariantInputDescription>(desc);
|
||||
EXPECT_NE(input_desc, nullptr);
|
||||
} else if (std::strcmp(type_info.name, "SliceInputDescription") == 0) {
|
||||
auto input_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::SliceInputDescription>(desc);
|
||||
EXPECT_NE(input_desc, nullptr);
|
||||
} else if (std::strcmp(type_info.name, "MergedInputDescription") == 0) {
|
||||
auto input_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::MergedInputDescription>(desc);
|
||||
EXPECT_NE(input_desc, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Output 1 is last Z
|
||||
auto out0 = loop->get_iter_value(body_condition, -1);
|
||||
auto out1 = loop->get_iter_value(Z, -1);
|
||||
|
||||
// check output descriptors
|
||||
for (auto& desc : loop->get_output_descriptions()) {
|
||||
auto type_info = desc->get_type_info();
|
||||
if (std::strcmp(type_info.name, "ConcatOutputDescription") == 0) {
|
||||
auto output_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::ConcatOutputDescription>(desc);
|
||||
EXPECT_NE(output_desc, nullptr);
|
||||
} else if (std::strcmp(type_info.name, "BodyOutputDescription") == 0) {
|
||||
auto output_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::BodyOutputDescription>(desc);
|
||||
EXPECT_NE(output_desc, nullptr);
|
||||
}
|
||||
}
|
||||
auto result0 = make_shared<opset5::Result>(out0);
|
||||
auto result1 = make_shared<opset5::Result>(out1);
|
||||
Shape out0_shape{};
|
||||
PartialShape out1_shape{1, Dimension::dynamic(), 10};
|
||||
|
||||
auto results = ResultVector{result0, result1};
|
||||
auto f = make_shared<Function>(results, ParameterVector{X, T});
|
||||
EXPECT_EQ(f->get_output_size(), 2);
|
||||
EXPECT_EQ(result0->get_output_shape(0), out0_shape);
|
||||
// should be dynamic
|
||||
EXPECT_TRUE(result1->get_output_partial_shape(0).compatible(out1_shape));
|
||||
|
||||
const auto inp0_shape = PartialShape{1, Dimension::dynamic(), 10};
|
||||
const auto inp1_shape = Shape{};
|
||||
EXPECT_EQ(body->get_parameters().size(), 1);
|
||||
// backedge, should be also dynamic
|
||||
EXPECT_EQ(body->get_parameters().at(0)->get_partial_shape(), inp0_shape);
|
||||
|
||||
EXPECT_EQ(loop->get_output_size(), 2);
|
||||
EXPECT_EQ(loop->get_output_shape(0), out0_shape);
|
||||
// map from the submodel, should be dynamic
|
||||
EXPECT_TRUE(loop->get_output_partial_shape(1).compatible(out1_shape));
|
||||
}
|
||||
|
||||
// dynamic output
|
||||
// trip_count = dynamic
|
||||
// execution_condition = true
|
||||
// body_condition = true
|
||||
// input is dynamic shape, sub-model output shapes has one dynamic dimension and this output is a backedge to a
|
||||
// parameter, one dynamic shape and one static shape
|
||||
TEST(type_prop, loop_operation_dynamic_iter_dynamic_shapes_inputs_dynamic_shape_outputs) {
|
||||
// That which we iterate over
|
||||
auto X = make_shared<opset5::Parameter>(element::f32, PartialShape{-1, 1, 10});
|
||||
auto T = make_shared<opset5::Parameter>(element::i64, Shape{});
|
||||
|
||||
// Set up the cell body, a function from (Xi) -> Concat(Xi, Xi, 1) -> (Zo)
|
||||
// Body parameters
|
||||
auto Xi = make_shared<opset5::Parameter>(element::f32, PartialShape{-1, -1, 10});
|
||||
|
||||
auto body_condition = make_shared<opset5::Constant>(element::boolean, Shape{}, true);
|
||||
auto trip_count = std::make_shared<ngraph::opset5::Constant>(ngraph::element::i64, ngraph::Shape{1}, 10);
|
||||
auto exec_condition = make_shared<opset5::Constant>(element::boolean, Shape{}, true);
|
||||
|
||||
// Body
|
||||
auto Zo = make_shared<opset5::Concat>(NodeVector{Xi, Xi}, 1);
|
||||
auto Z = make_shared<opset5::Result>(Zo);
|
||||
auto body = make_shared<Function>(OutputVector{Z, body_condition}, ParameterVector{Xi});
|
||||
|
||||
auto loop = make_shared<opset5::Loop>(T, exec_condition);
|
||||
loop->set_function(body);
|
||||
loop->set_special_body_ports(opset5::Loop::SpecialBodyPorts{-1, 1});
|
||||
loop->set_merged_input(Xi, X, Z);
|
||||
|
||||
// check input descriptors
|
||||
for (auto& desc : loop->get_input_descriptions()) {
|
||||
auto type_info = desc->get_type_info();
|
||||
if (std::strcmp(type_info.name, "InvariantInputDescription") == 0) {
|
||||
auto input_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::InvariantInputDescription>(desc);
|
||||
EXPECT_NE(input_desc, nullptr);
|
||||
} else if (std::strcmp(type_info.name, "SliceInputDescription") == 0) {
|
||||
auto input_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::SliceInputDescription>(desc);
|
||||
EXPECT_NE(input_desc, nullptr);
|
||||
} else if (std::strcmp(type_info.name, "MergedInputDescription") == 0) {
|
||||
auto input_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::MergedInputDescription>(desc);
|
||||
EXPECT_NE(input_desc, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Output 1 is last Z
|
||||
auto out0 = loop->get_iter_value(body_condition, -1);
|
||||
auto out1 = loop->get_iter_value(Z, -1);
|
||||
|
||||
// check output descriptors
|
||||
for (auto& desc : loop->get_output_descriptions()) {
|
||||
auto type_info = desc->get_type_info();
|
||||
if (std::strcmp(type_info.name, "ConcatOutputDescription") == 0) {
|
||||
auto output_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::ConcatOutputDescription>(desc);
|
||||
EXPECT_NE(output_desc, nullptr);
|
||||
} else if (std::strcmp(type_info.name, "BodyOutputDescription") == 0) {
|
||||
auto output_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::BodyOutputDescription>(desc);
|
||||
EXPECT_NE(output_desc, nullptr);
|
||||
}
|
||||
}
|
||||
auto result0 = make_shared<opset5::Result>(out0);
|
||||
auto result1 = make_shared<opset5::Result>(out1);
|
||||
Shape out0_shape{};
|
||||
PartialShape out1_shape{-1, -1, 10};
|
||||
|
||||
auto results = ResultVector{result0, result1};
|
||||
auto f = make_shared<Function>(results, ParameterVector{X, T});
|
||||
EXPECT_EQ(f->get_output_size(), 2);
|
||||
EXPECT_EQ(result0->get_output_shape(0), out0_shape);
|
||||
// should be dynamic
|
||||
EXPECT_EQ(result1->get_output_partial_shape(0), out1_shape);
|
||||
|
||||
const auto inp0_shape = PartialShape{-1, -1, 10};
|
||||
const auto inp1_shape = Shape{};
|
||||
EXPECT_EQ(body->get_parameters().size(), 1);
|
||||
// backedge, should be also dynamic
|
||||
EXPECT_EQ(body->get_parameters().at(0)->get_partial_shape(), inp0_shape);
|
||||
|
||||
EXPECT_EQ(loop->get_output_size(), 2);
|
||||
EXPECT_EQ(loop->get_output_shape(0), out0_shape);
|
||||
// map from the submodel, should be dynamic
|
||||
EXPECT_EQ(loop->get_output_partial_shape(1), out1_shape);
|
||||
}
|
||||
|
||||
// dynamic output
|
||||
// trip_count = dynamic
|
||||
// execution_condition = true
|
||||
// body_condition = true
|
||||
// 2 inputs is dynamic shape, sub-model's 2 output shapes has one dynamic dimension and this output is a backedge to a
|
||||
// parameter, other shapes are static
|
||||
// main model:
|
||||
// Parameter(-1,1,10) Parameter(-1,1,10) Const/Condition()...
|
||||
// | | |
|
||||
// |_________Loop________|________________|
|
||||
// |
|
||||
// ________________________________
|
||||
// | | | |
|
||||
// r0() r1(-1,-1,10) r2(-1,-1,10) r3(-1,-1,10)
|
||||
//
|
||||
// sub model:
|
||||
// Parameter1 (-1,-1,10) Parameter2 (-1,-1,10) Const/Condition
|
||||
// | | | | |
|
||||
// |_Concat(-1,-1,10)_| |_Concat(-1,-1,10)_| |
|
||||
// | | | |
|
||||
// Result(-1,-1,10) Result(-1,-1,10) Result(-1,-1,10) Result()
|
||||
// | |
|
||||
// backedge to Parameter1 backedge to Parameter2
|
||||
TEST(type_prop, loop_operation_dynamic_iter_dynamic_shapes2_inputs_dynamic_shape_outputs3) {
|
||||
// That which we iterate over
|
||||
auto X0 = make_shared<opset5::Parameter>(element::f32, PartialShape{-1, 1, 10});
|
||||
auto X1 = make_shared<opset5::Parameter>(element::f32, PartialShape{-1, 1, 10});
|
||||
auto T = make_shared<opset5::Parameter>(element::i64, Shape{});
|
||||
|
||||
// Set up the cell body, a function from (Xi0) -> Concat(Xi0, Xi0, 1) -> (Zo0)
|
||||
// (Xi1) -> Concat(Xi1, Xi1, 1) -> (Zo1)
|
||||
// Body parameters
|
||||
auto Xi0 = make_shared<opset5::Parameter>(element::f32, PartialShape{-1, 1, 10});
|
||||
auto Xi1 = make_shared<opset5::Parameter>(element::f32, PartialShape{-1, 1, 10});
|
||||
|
||||
auto body_condition = make_shared<opset5::Constant>(element::boolean, Shape{}, true);
|
||||
auto trip_count = std::make_shared<ngraph::opset5::Constant>(ngraph::element::i64, ngraph::Shape{1}, 10);
|
||||
auto exec_condition = make_shared<opset5::Constant>(element::boolean, Shape{}, true);
|
||||
|
||||
// Body
|
||||
auto Zo0 = make_shared<opset5::Concat>(NodeVector{Xi0, Xi0}, 1);
|
||||
auto Zo1 = make_shared<opset5::Concat>(NodeVector{Xi1, Xi1}, 1);
|
||||
auto Y = make_shared<opset5::Result>(Zo0);
|
||||
auto Z0 = make_shared<opset5::Result>(Zo0);
|
||||
auto Z1 = make_shared<opset5::Result>(Zo1);
|
||||
auto body = make_shared<Function>(OutputVector{Y, Z0, Z1, body_condition}, ParameterVector{Xi0, Xi1});
|
||||
|
||||
auto loop = make_shared<opset5::Loop>(T, exec_condition);
|
||||
loop->set_function(body);
|
||||
loop->set_special_body_ports(opset5::Loop::SpecialBodyPorts{-1, 3});
|
||||
loop->set_merged_input(Xi0, X0, Z0);
|
||||
loop->set_merged_input(Xi1, X1, Z1);
|
||||
|
||||
// check input descriptors
|
||||
for (auto& desc : loop->get_input_descriptions()) {
|
||||
auto type_info = desc->get_type_info();
|
||||
if (std::strcmp(type_info.name, "InvariantInputDescription") == 0) {
|
||||
auto input_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::InvariantInputDescription>(desc);
|
||||
EXPECT_NE(input_desc, nullptr);
|
||||
} else if (std::strcmp(type_info.name, "SliceInputDescription") == 0) {
|
||||
auto input_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::SliceInputDescription>(desc);
|
||||
EXPECT_NE(input_desc, nullptr);
|
||||
} else if (std::strcmp(type_info.name, "MergedInputDescription") == 0) {
|
||||
auto input_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::MergedInputDescription>(desc);
|
||||
EXPECT_NE(input_desc, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Output 1 is last Z
|
||||
auto out0 = loop->get_iter_value(body_condition, -1);
|
||||
auto out1 = loop->get_iter_value(Y, -1);
|
||||
auto out2 = loop->get_iter_value(Z0, -1);
|
||||
auto out3 = loop->get_iter_value(Z1, -1);
|
||||
|
||||
// check output descriptors
|
||||
for (auto& desc : loop->get_output_descriptions()) {
|
||||
auto type_info = desc->get_type_info();
|
||||
if (std::strcmp(type_info.name, "ConcatOutputDescription") == 0) {
|
||||
auto output_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::ConcatOutputDescription>(desc);
|
||||
EXPECT_NE(output_desc, nullptr);
|
||||
} else if (std::strcmp(type_info.name, "BodyOutputDescription") == 0) {
|
||||
auto output_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::BodyOutputDescription>(desc);
|
||||
EXPECT_NE(output_desc, nullptr);
|
||||
}
|
||||
}
|
||||
auto result0 = make_shared<opset5::Result>(out0);
|
||||
auto result1 = make_shared<opset5::Result>(out1);
|
||||
auto result2 = make_shared<opset5::Result>(out2);
|
||||
auto result3 = make_shared<opset5::Result>(out3);
|
||||
Shape out0_shape{};
|
||||
PartialShape out1_shape{-1, -1, 10};
|
||||
|
||||
auto results = ResultVector{result0, result1, result2, result3};
|
||||
auto f = make_shared<Function>(results, ParameterVector{X0, X1, T});
|
||||
EXPECT_EQ(f->get_output_size(), 4);
|
||||
EXPECT_EQ(result0->get_output_shape(0), out0_shape);
|
||||
// should be dynamic
|
||||
EXPECT_EQ(result1->get_output_partial_shape(0), out1_shape);
|
||||
EXPECT_EQ(result2->get_output_partial_shape(0), out1_shape);
|
||||
EXPECT_EQ(result3->get_output_partial_shape(0), out1_shape);
|
||||
|
||||
const auto inp0_shape = PartialShape{-1, -1, 10};
|
||||
const auto inp1_shape = Shape{};
|
||||
EXPECT_EQ(body->get_parameters().size(), 2);
|
||||
// backedge, should be also dynamic
|
||||
EXPECT_EQ(body->get_parameters().at(0)->get_partial_shape(), inp0_shape);
|
||||
EXPECT_EQ(body->get_parameters().at(1)->get_partial_shape(), inp0_shape);
|
||||
|
||||
EXPECT_EQ(loop->get_output_size(), 4);
|
||||
EXPECT_EQ(loop->get_output_shape(0), out0_shape);
|
||||
// map from the submodel, should be dynamic
|
||||
EXPECT_EQ(loop->get_output_partial_shape(1), out1_shape);
|
||||
EXPECT_EQ(loop->get_output_partial_shape(2), out1_shape);
|
||||
EXPECT_EQ(loop->get_output_partial_shape(3), out1_shape);
|
||||
}
|
||||
|
||||
// dynamic output
|
||||
// trip_count = dynamic
|
||||
// execution_condition = true
|
||||
// body_condition = true
|
||||
// input is 1D shape, sub-model output shapes has one dynamic dimension and this output is a backedge to a
|
||||
// parameter, one dynamic shape and one static shape
|
||||
TEST(type_prop, loop_operation_dynamic_iter_1d_shapes_inputs_dynamic_shape_outputs) {
|
||||
// That which we iterate over
|
||||
auto X = make_shared<opset5::Parameter>(element::f32, PartialShape{1});
|
||||
auto T = make_shared<opset5::Parameter>(element::i64, Shape{});
|
||||
|
||||
// Set up the cell body, a function from (Xi) -> Concat(Xi, Xi, 1) -> (Zo)
|
||||
// Body parameters
|
||||
auto Xi = make_shared<opset5::Parameter>(element::f32, PartialShape{1});
|
||||
|
||||
auto body_condition = make_shared<opset5::Constant>(element::boolean, Shape{}, true);
|
||||
auto trip_count = std::make_shared<ngraph::opset5::Constant>(ngraph::element::i64, ngraph::Shape{1}, 10);
|
||||
auto exec_condition = make_shared<opset5::Constant>(element::boolean, Shape{}, true);
|
||||
|
||||
// Body
|
||||
auto X0 = make_shared<opset5::Reshape>(Xi, opset5::Constant::create(ov::element::i32, {1}, {-1}), false);
|
||||
auto Zo = make_shared<opset5::Concat>(NodeVector{X0, X0}, 0);
|
||||
auto Z = make_shared<opset5::Result>(Zo);
|
||||
auto body = make_shared<Function>(OutputVector{Z, body_condition}, ParameterVector{Xi});
|
||||
|
||||
auto loop = make_shared<opset5::Loop>(T, exec_condition);
|
||||
loop->set_function(body);
|
||||
loop->set_special_body_ports(opset5::Loop::SpecialBodyPorts{-1, 1});
|
||||
loop->set_merged_input(Xi, X, Z);
|
||||
|
||||
// check input descriptors
|
||||
for (auto& desc : loop->get_input_descriptions()) {
|
||||
auto type_info = desc->get_type_info();
|
||||
if (std::strcmp(type_info.name, "InvariantInputDescription") == 0) {
|
||||
auto input_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::InvariantInputDescription>(desc);
|
||||
EXPECT_NE(input_desc, nullptr);
|
||||
} else if (std::strcmp(type_info.name, "SliceInputDescription") == 0) {
|
||||
auto input_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::SliceInputDescription>(desc);
|
||||
EXPECT_NE(input_desc, nullptr);
|
||||
} else if (std::strcmp(type_info.name, "MergedInputDescription") == 0) {
|
||||
auto input_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::MergedInputDescription>(desc);
|
||||
EXPECT_NE(input_desc, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Output 1 is last Z
|
||||
auto out0 = loop->get_iter_value(body_condition, -1);
|
||||
auto out1 = loop->get_iter_value(Z, -1);
|
||||
|
||||
// check output descriptors
|
||||
for (auto& desc : loop->get_output_descriptions()) {
|
||||
auto type_info = desc->get_type_info();
|
||||
if (std::strcmp(type_info.name, "ConcatOutputDescription") == 0) {
|
||||
auto output_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::ConcatOutputDescription>(desc);
|
||||
EXPECT_NE(output_desc, nullptr);
|
||||
} else if (std::strcmp(type_info.name, "BodyOutputDescription") == 0) {
|
||||
auto output_desc = ov::as_type_ptr<ngraph::opset5::TensorIterator::BodyOutputDescription>(desc);
|
||||
EXPECT_NE(output_desc, nullptr);
|
||||
}
|
||||
}
|
||||
auto result0 = make_shared<opset5::Result>(out0);
|
||||
auto result1 = make_shared<opset5::Result>(out1);
|
||||
Shape out0_shape{};
|
||||
PartialShape out1_shape{-1};
|
||||
|
||||
auto results = ResultVector{result0, result1};
|
||||
auto f = make_shared<Function>(results, ParameterVector{X, T});
|
||||
EXPECT_EQ(f->get_output_size(), 2);
|
||||
EXPECT_EQ(result0->get_output_shape(0), out0_shape);
|
||||
// should be dynamic
|
||||
EXPECT_EQ(result1->get_output_partial_shape(0), out1_shape);
|
||||
|
||||
const auto inp0_shape = PartialShape{-1};
|
||||
const auto inp1_shape = Shape{};
|
||||
EXPECT_EQ(body->get_parameters().size(), 1);
|
||||
// backedge, should be also dynamic
|
||||
EXPECT_EQ(body->get_parameters().at(0)->get_partial_shape(), inp0_shape);
|
||||
|
||||
EXPECT_EQ(loop->get_output_size(), 2);
|
||||
EXPECT_EQ(loop->get_output_shape(0), out0_shape);
|
||||
// map from the submodel, should be dynamic
|
||||
EXPECT_EQ(loop->get_output_partial_shape(1), out1_shape);
|
||||
}
|
||||
|
||||
// dynamic output
|
||||
// trip_count = -1
|
||||
// execution_condition = true
|
||||
// body_condition = true
|
||||
// model could be described like so:
|
||||
// Parameter([-1, -1])
|
||||
// while (true) {
|
||||
// input = unsqueeze(input, 0);
|
||||
// }
|
||||
TEST(type_prop, loop_operation_dynamic_iter_dynamic_shapes_unsqueeze) {
|
||||
// Inner model
|
||||
const auto inner_parameter = std::make_shared<opset5::Parameter>(element::dynamic, ov::PartialShape::dynamic());
|
||||
const auto unsqueeze =
|
||||
std::make_shared<opset5::Unsqueeze>(inner_parameter, opset5::Constant::create(element::i64, {1}, {0}));
|
||||
const auto true_const = opset5::Constant::create(element::boolean, {1}, {1});
|
||||
auto body = std::make_shared<Function>(OutputVector{unsqueeze, true_const}, ParameterVector{inner_parameter});
|
||||
|
||||
// Outer model
|
||||
const auto outer_parameter = std::make_shared<opset5::Parameter>(element::dynamic, ov::PartialShape::dynamic(2));
|
||||
|
||||
const auto trip_count = opset5::Constant::create(element::i64, {1}, {-1});
|
||||
const auto execution_condition = opset5::Constant::create(element::boolean, {1}, {1});
|
||||
const auto loop = std::make_shared<opset5::Loop>(trip_count, execution_condition);
|
||||
loop->set_function(body);
|
||||
loop->set_merged_input(inner_parameter, outer_parameter, unsqueeze);
|
||||
loop->set_special_body_ports(ngraph::opset5::Loop::SpecialBodyPorts{-1, 1});
|
||||
|
||||
auto outer_result = make_shared<opset5::Result>(loop->get_iter_value(unsqueeze, -1));
|
||||
|
||||
auto outer_model = std::make_shared<Function>(ResultVector{outer_result}, ParameterVector{outer_parameter});
|
||||
PartialShape outer_shape = PartialShape::dynamic();
|
||||
EXPECT_EQ(outer_model->get_output_size(), 1);
|
||||
EXPECT_EQ(outer_result->get_output_partial_shape(0), outer_shape);
|
||||
}
|
||||
@@ -33,9 +33,12 @@ public:
|
||||
|
||||
/// \brief Get the output names
|
||||
virtual std::vector<OutPortName> get_output_names() const = 0;
|
||||
virtual std::vector<TensorName> get_output_var_names(const std::string& var_name) const = 0;
|
||||
virtual std::vector<TensorName> get_input_var_names(const std::string& var_name) const = 0;
|
||||
|
||||
/// \brief Get the output size
|
||||
virtual size_t get_output_size() const = 0;
|
||||
virtual size_t get_output_size(const std::string& port_name) const = 0;
|
||||
|
||||
/// \brief Get output port type
|
||||
///
|
||||
@@ -47,6 +50,8 @@ public:
|
||||
///
|
||||
/// \return Type of specified output port
|
||||
virtual ov::element::Type get_out_port_type(const std::string& port_name) const = 0;
|
||||
virtual std::vector<std::pair<ov::element::Type, ov::PartialShape>> get_output_port_infos(
|
||||
const std::string& port_name) const = 0;
|
||||
|
||||
/// \brief Get the type of the operation
|
||||
virtual std::string get_op_type() const = 0;
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace frontend {
|
||||
namespace paddle {
|
||||
|
||||
class OpPlace;
|
||||
class TensorPlace;
|
||||
|
||||
class PADDLE_API FrontEnd : public ov::frontend::FrontEnd {
|
||||
public:
|
||||
@@ -72,10 +73,19 @@ protected:
|
||||
InputModel::Ptr load_impl(const std::vector<ov::Any>& params) const override;
|
||||
|
||||
protected:
|
||||
static std::shared_ptr<Model> convert_each_node(
|
||||
void try_remove_internal_ops(const std::vector<std::shared_ptr<Model>>& models) const;
|
||||
|
||||
static std::vector<std::shared_ptr<Model>> convert_each_node(
|
||||
const std::shared_ptr<InputModel>& frontend_model,
|
||||
std::function<std::map<std::string, OutputVector>(const std::map<std::string, Output<Node>>&,
|
||||
const std::shared_ptr<OpPlace>&)> func);
|
||||
static std::map<int32_t, std::shared_ptr<Model>> convert_each_node_recursive(
|
||||
const std::shared_ptr<InputModel>& frontend_model,
|
||||
const int32_t block_idx,
|
||||
const std::vector<std::shared_ptr<TensorPlace>>& input_tensors,
|
||||
const std::vector<std::shared_ptr<TensorPlace>>& output_tensors,
|
||||
std::function<std::map<std::string, OutputVector>(const std::map<std::string, Output<Node>>&,
|
||||
const std::shared_ptr<OpPlace>&)> func);
|
||||
|
||||
TelemetryExtension::Ptr m_telemetry;
|
||||
std::vector<DecoderTransformationExtension::Ptr> m_transformation_extensions;
|
||||
|
||||
@@ -48,6 +48,16 @@ public:
|
||||
return name_map.at(name);
|
||||
}
|
||||
|
||||
/// Returns all inputs in order they appear in map. This is used for FrameworkNode
|
||||
/// creation
|
||||
OutputVector get_all_ng_inputs() const {
|
||||
OutputVector res;
|
||||
for (const auto& entry : name_map) {
|
||||
res.insert(res.end(), entry.second.begin(), entry.second.end());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Output<Node> get_input(const std::string& name, int idx) const override {
|
||||
return name_map.at(name).at(idx);
|
||||
}
|
||||
@@ -60,6 +70,14 @@ public:
|
||||
return decoder.get_output_names();
|
||||
}
|
||||
|
||||
std::vector<TensorName> get_output_var_names(const std::string& var_name) const {
|
||||
return decoder.get_output_var_names(var_name);
|
||||
}
|
||||
|
||||
std::vector<TensorName> get_input_var_names(const std::string& var_name) const {
|
||||
return decoder.get_input_var_names(var_name);
|
||||
}
|
||||
|
||||
ov::element::Type get_out_port_type(const std::string& port_name) const {
|
||||
return decoder.get_out_port_type(port_name);
|
||||
}
|
||||
@@ -72,6 +90,15 @@ public:
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t get_output_size(const std::string& port_name) const {
|
||||
return decoder.get_output_size(port_name);
|
||||
}
|
||||
|
||||
std::vector<std::pair<ov::element::Type, ov::PartialShape>> get_output_port_infos(
|
||||
const std::string& port_name) const {
|
||||
return decoder.get_output_port_infos(port_name);
|
||||
}
|
||||
|
||||
private:
|
||||
ov::Any apply_additional_conversion_rules(const ov::Any& any, const std::type_info& type_info) const override {
|
||||
auto res = decoder.convert_attribute(any, type_info);
|
||||
|
||||
@@ -92,6 +92,35 @@ std::vector<paddle::OutPortName> DecoderProto::get_output_names() const {
|
||||
return output_names;
|
||||
}
|
||||
|
||||
std::vector<paddle::TensorName> DecoderProto::get_output_var_names(const std::string& var_name) const {
|
||||
std::vector<std::string> output_names;
|
||||
for (const auto& output : op_place->get_desc().outputs()) {
|
||||
if (output.parameter() == var_name) {
|
||||
for (int idx = 0; idx < output.arguments_size(); ++idx) {
|
||||
output_names.push_back(output.arguments()[idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return output_names;
|
||||
}
|
||||
|
||||
std::vector<paddle::TensorName> DecoderProto::get_input_var_names(const std::string& var_name) const {
|
||||
std::vector<std::string> input_names;
|
||||
for (const auto& input : op_place->get_desc().inputs()) {
|
||||
if (input.parameter() == var_name) {
|
||||
for (int idx = 0; idx < input.arguments_size(); ++idx) {
|
||||
input_names.push_back(input.arguments()[idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return input_names;
|
||||
}
|
||||
|
||||
size_t DecoderProto::get_output_size(const std::string& port_name) const {
|
||||
const auto out_port = op_place->get_output_ports().at(port_name);
|
||||
return out_port.size();
|
||||
}
|
||||
|
||||
size_t DecoderProto::get_output_size() const {
|
||||
size_t res = 0;
|
||||
for (const auto& output : op_place->get_desc().outputs()) {
|
||||
@@ -110,6 +139,16 @@ std::map<std::string, std::vector<ov::element::Type>> DecoderProto::get_output_t
|
||||
return output_types;
|
||||
}
|
||||
|
||||
std::vector<std::pair<ov::element::Type, ov::PartialShape>> DecoderProto::get_output_port_infos(
|
||||
const std::string& port_name) const {
|
||||
std::vector<std::pair<ov::element::Type, ov::PartialShape>> output_types;
|
||||
for (const auto& out_port : op_place->get_output_ports().at(port_name)) {
|
||||
output_types.push_back({out_port->get_target_tensor_paddle()->get_element_type(),
|
||||
out_port->get_target_tensor_paddle()->get_partial_shape()});
|
||||
}
|
||||
return output_types;
|
||||
}
|
||||
|
||||
ov::element::Type DecoderProto::get_out_port_type(const std::string& port_name) const {
|
||||
std::vector<ov::element::Type> output_types;
|
||||
for (const auto& out_port : op_place->get_output_ports().at(port_name)) {
|
||||
|
||||
@@ -30,17 +30,23 @@ public:
|
||||
|
||||
ov::Any get_attribute(const std::string& name) const override;
|
||||
|
||||
std::vector<TensorName> get_output_var_names(const std::string& var_name) const override;
|
||||
std::vector<TensorName> get_input_var_names(const std::string& var_name) const override;
|
||||
|
||||
ov::Any convert_attribute(const ov::Any& data, const std::type_info& type_info) const override;
|
||||
|
||||
std::vector<paddle::OutPortName> get_output_names() const override;
|
||||
|
||||
size_t get_output_size() const override;
|
||||
size_t get_output_size(const std::string& port_name) const override;
|
||||
|
||||
ov::element::Type get_out_port_type(const std::string& port_name) const override;
|
||||
|
||||
std::string get_op_type() const override;
|
||||
|
||||
std::map<std::string, std::vector<ov::element::Type>> get_output_type_map() const;
|
||||
std::vector<std::pair<ov::element::Type, ov::PartialShape>> get_output_port_infos(
|
||||
const std::string& port_name) const override;
|
||||
|
||||
std::map<std::string, OutputVector> map_for_each_input(
|
||||
const std::function<Output<Node>(const std::string&, size_t)>& func) const;
|
||||
|
||||
@@ -10,19 +10,22 @@
|
||||
#include <vector>
|
||||
|
||||
#include "decoder_proto.hpp"
|
||||
#include "default_opset.hpp"
|
||||
#include "framework.pb.h"
|
||||
#include "input_model.hpp"
|
||||
#include "internal/pass/transform_if.hpp"
|
||||
#include "internal/pass/transform_tensorarray.hpp"
|
||||
#include "internal/pass/transform_while.hpp"
|
||||
#include "op_table.hpp"
|
||||
#include "openvino/frontend/extension/conversion.hpp"
|
||||
#include "openvino/frontend/paddle/node_context.hpp"
|
||||
#include "openvino/opsets/opset7.hpp"
|
||||
#include "openvino/util/common_util.hpp"
|
||||
#include "paddle_fw_node.hpp"
|
||||
#include "paddle_utils.hpp"
|
||||
#include "place.hpp"
|
||||
#include "so_extension.hpp"
|
||||
|
||||
using namespace ov::opset7;
|
||||
using namespace ov::frontend::paddle::op::default_opset;
|
||||
using namespace ov;
|
||||
using namespace ov::frontend;
|
||||
|
||||
@@ -55,7 +58,7 @@ NamedOutputs make_ng_node(const std::map<paddle::TensorName, Output<Node>>& node
|
||||
NamedOutputs outputs;
|
||||
// In case the conversion function throws exception
|
||||
try {
|
||||
outputs = creator_it->second(NodeContext(DecoderProto(op_place), named_inputs));
|
||||
outputs = creator_it->second(paddle::NodeContext(DecoderProto(op_place), named_inputs));
|
||||
} catch (std::exception& ex) {
|
||||
FRONT_END_OP_CONVERSION_CHECK(false, "Fail to convert " + op_desc.type() + " Exception " + ex.what());
|
||||
}
|
||||
@@ -96,7 +99,7 @@ bool normalize_framework_node(const std::shared_ptr<FrameworkNode>& node,
|
||||
auto creator_it = CREATORS_MAP.find(type);
|
||||
FRONT_END_OP_CONVERSION_CHECK(creator_it != CREATORS_MAP.end(), "No creator found for ", type, " node.");
|
||||
|
||||
auto new_node_outputs = creator_it->second(NodeContext(node->get_decoder(), node->get_named_inputs()));
|
||||
auto new_node_outputs = creator_it->second(paddle::NodeContext(node->get_decoder(), node->get_named_inputs()));
|
||||
auto new_node = new_node_outputs.begin()->second[0].get_node_shared_ptr();
|
||||
new_node->set_friendly_name(node->get_friendly_name());
|
||||
auto node_outputs = node->return_named_outputs();
|
||||
@@ -137,17 +140,102 @@ std::istream* variant_to_stream_ptr(const ov::Any& variant, std::ifstream& ext_s
|
||||
|
||||
FrontEnd::FrontEnd() : m_op_translators(paddle::get_supported_ops()) {}
|
||||
|
||||
std::shared_ptr<ov::Model> FrontEnd::convert_each_node(
|
||||
std::vector<std::shared_ptr<ov::Model>> FrontEnd::convert_each_node(
|
||||
const std::shared_ptr<ov::frontend::InputModel>& frontend_model,
|
||||
std::function<std::map<std::string, OutputVector>(const std::map<std::string, Output<Node>>&,
|
||||
const std::shared_ptr<OpPlace>&)> func) {
|
||||
auto model = std::dynamic_pointer_cast<InputModel>(frontend_model);
|
||||
FRONT_END_GENERAL_CHECK(model, "Invalid input model");
|
||||
std::vector<std::shared_ptr<TensorPlace>> input_tensors;
|
||||
std::vector<std::shared_ptr<TensorPlace>> output_tensors;
|
||||
for (const auto& _inp_place : model->get_inputs()) {
|
||||
const auto& inp_place = std::dynamic_pointer_cast<TensorPlace>(_inp_place);
|
||||
input_tensors.emplace_back(inp_place);
|
||||
}
|
||||
for (const auto& _outp_place : model->get_outputs()) {
|
||||
const auto& outp_place = std::dynamic_pointer_cast<TensorPlace>(_outp_place);
|
||||
output_tensors.emplace_back(outp_place);
|
||||
}
|
||||
auto funcs = convert_each_node_recursive(model, 0, input_tensors, output_tensors, func);
|
||||
std::vector<std::shared_ptr<Model>> funcs_vec;
|
||||
for (auto&& item : funcs) {
|
||||
funcs_vec.emplace_back(item.second);
|
||||
}
|
||||
|
||||
return funcs_vec;
|
||||
}
|
||||
|
||||
// Paddle's subblock does not has 'feed' and 'fetch' and the sub-model's parameters and results
|
||||
// could not be generated just like the main model. We extract the information from 'conditional_block'
|
||||
// and 'while' ops
|
||||
using SubblockInfo = std::map<
|
||||
int32_t,
|
||||
std::tuple<std::string, std::vector<std::shared_ptr<TensorPlace>>, std::vector<std::shared_ptr<TensorPlace>>>>;
|
||||
void try_update_sublock_info(const std::shared_ptr<OpPlace>& op_place, SubblockInfo& subblock_info) {
|
||||
const auto& op_desc = op_place->get_desc();
|
||||
if (op_desc.type() == "conditional_block") {
|
||||
std::vector<std::shared_ptr<TensorPlace>> outp_tensors;
|
||||
std::vector<std::shared_ptr<TensorPlace>> inp_tensors;
|
||||
|
||||
auto outp_ports = op_place->get_output_ports();
|
||||
for (auto outp_port : outp_ports["Out"]) {
|
||||
auto outp_tensor = outp_port->get_target_tensor_paddle();
|
||||
outp_tensors.push_back(outp_tensor);
|
||||
}
|
||||
FRONT_END_GENERAL_CHECK(outp_tensors.size() > 0, "Port has no tensors connected.");
|
||||
|
||||
auto inp_ports = op_place->get_input_ports();
|
||||
for (auto inp_port : inp_ports["Input"]) {
|
||||
auto inp_tensor = inp_port->get_source_tensor_paddle();
|
||||
inp_tensors.push_back(inp_tensor);
|
||||
}
|
||||
|
||||
auto tmp_node = paddle::NodeContext(DecoderProto(op_place), paddle::NamedInputs());
|
||||
auto block_idx = tmp_node.get_attribute<int32_t>("sub_block");
|
||||
|
||||
subblock_info[block_idx] = std::make_tuple(op_desc.type(), inp_tensors, outp_tensors);
|
||||
} else if (op_desc.type() == "while") {
|
||||
std::vector<std::shared_ptr<TensorPlace>> outp_tensors;
|
||||
std::vector<std::shared_ptr<TensorPlace>> inp_tensors;
|
||||
|
||||
auto outp_ports = op_place->get_output_ports();
|
||||
for (auto outp_port : outp_ports["Out"]) {
|
||||
auto outp_tensor = outp_port->get_target_tensor_paddle();
|
||||
outp_tensors.push_back(outp_tensor);
|
||||
}
|
||||
FRONT_END_GENERAL_CHECK(outp_tensors.size() > 0, "Port has no tensors connected.");
|
||||
|
||||
auto inp_ports = op_place->get_input_ports();
|
||||
for (auto inp_port : inp_ports["X"]) {
|
||||
auto inp_tensor = inp_port->get_source_tensor_paddle();
|
||||
inp_tensors.push_back(inp_tensor);
|
||||
}
|
||||
FRONT_END_GENERAL_CHECK(inp_tensors.size() > 0, "Port has no tensors connected.");
|
||||
|
||||
auto tmp_node = paddle::NodeContext(DecoderProto(op_place), paddle::NamedInputs());
|
||||
auto block_idx = tmp_node.get_attribute<int32_t>("sub_block");
|
||||
|
||||
subblock_info[block_idx] = std::make_tuple(op_desc.type(), inp_tensors, outp_tensors);
|
||||
}
|
||||
}
|
||||
|
||||
std::map<int32_t, std::shared_ptr<ov::Model>> FrontEnd::convert_each_node_recursive(
|
||||
const std::shared_ptr<ov::frontend::InputModel>& frontend_model,
|
||||
const int32_t block_idx,
|
||||
const std::vector<std::shared_ptr<TensorPlace>>& input_tensors,
|
||||
const std::vector<std::shared_ptr<TensorPlace>>& output_tensors,
|
||||
std::function<std::map<std::string, OutputVector>(const std::map<std::string, Output<Node>>&,
|
||||
const std::shared_ptr<OpPlace>&)> func) {
|
||||
auto model = std::dynamic_pointer_cast<InputModel>(frontend_model);
|
||||
FRONT_END_GENERAL_CHECK(model, "Invalid input model");
|
||||
auto nodes_dict(model->get_tensor_values());
|
||||
ParameterVector parameter_nodes;
|
||||
ResultVector result_nodes;
|
||||
OutputVector output_nodes;
|
||||
|
||||
for (const auto& _inp_place : model->get_inputs()) {
|
||||
SubblockInfo subblock_inputs_outputs; // keep info of controlflow ops
|
||||
|
||||
for (const auto& _inp_place : input_tensors) {
|
||||
const auto& inp_place = std::dynamic_pointer_cast<TensorPlace>(_inp_place);
|
||||
const auto& var = inp_place->get_desc();
|
||||
const auto& shape = inp_place->get_partial_shape();
|
||||
@@ -159,13 +247,15 @@ std::shared_ptr<ov::Model> FrontEnd::convert_each_node(
|
||||
parameter_nodes.push_back(param);
|
||||
}
|
||||
|
||||
const auto& op_places = model->get_op_places();
|
||||
const auto& op_places = model->get_op_places(block_idx);
|
||||
for (const auto& op_place : op_places) {
|
||||
const auto& op_desc = op_place->get_desc();
|
||||
if (op_desc.type() == "feed" || op_desc.type() == "fetch") {
|
||||
// inputs and outputs are stored in the model already
|
||||
continue;
|
||||
} else {
|
||||
try_update_sublock_info(op_place, subblock_inputs_outputs);
|
||||
|
||||
paddle::NamedOutputs named_outputs = func(nodes_dict, op_place);
|
||||
|
||||
if (!named_outputs.empty()) {
|
||||
@@ -188,8 +278,7 @@ std::shared_ptr<ov::Model> FrontEnd::convert_each_node(
|
||||
ng_outputs[idx].get_tensor().set_names({var_name});
|
||||
// if nodes_dict already has node mapped to this tensor name it
|
||||
// usually means that it was overwritten using setTensorValue
|
||||
if (!nodes_dict.count(var_name))
|
||||
nodes_dict[var_name] = ng_outputs[idx];
|
||||
nodes_dict[var_name] = ng_outputs[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,16 +286,49 @@ std::shared_ptr<ov::Model> FrontEnd::convert_each_node(
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& _outp_place : model->get_outputs()) {
|
||||
for (const auto& _outp_place : output_tensors) {
|
||||
const auto& outp_place = std::dynamic_pointer_cast<TensorPlace>(_outp_place);
|
||||
auto var = outp_place->get_desc();
|
||||
auto input_var_name = var.name();
|
||||
auto result = std::make_shared<Result>(nodes_dict.at(input_var_name));
|
||||
result->set_friendly_name(input_var_name + "/Result");
|
||||
result_nodes.push_back(result);
|
||||
output_nodes.push_back(nodes_dict.at(input_var_name));
|
||||
}
|
||||
|
||||
return std::make_shared<ov::Model>(result_nodes, parameter_nodes);
|
||||
std::shared_ptr<ov::Model> main_block_func;
|
||||
if (parameter_nodes.size() > 0) {
|
||||
main_block_func = std::make_shared<ov::Model>(result_nodes, parameter_nodes);
|
||||
} else {
|
||||
main_block_func = std::make_shared<ov::Model>(output_nodes);
|
||||
}
|
||||
|
||||
// convert each sub block
|
||||
std::map<int32_t, std::shared_ptr<ov::Model>> block_funcs;
|
||||
block_funcs.insert({block_idx, main_block_func});
|
||||
|
||||
for (auto& item : subblock_inputs_outputs) {
|
||||
auto ctl_op_info = item.second;
|
||||
auto sub_block_func =
|
||||
convert_each_node_recursive(model, item.first, std::get<1>(ctl_op_info), std::get<2>(ctl_op_info), func);
|
||||
block_funcs.insert(sub_block_func.begin(), sub_block_func.end());
|
||||
}
|
||||
|
||||
return block_funcs;
|
||||
}
|
||||
|
||||
void FrontEnd::try_remove_internal_ops(const std::vector<std::shared_ptr<Model>>& models) const {
|
||||
for (auto& model : models) {
|
||||
ov::pass::Manager manager;
|
||||
manager.register_pass<ov::frontend::paddle::pass::TransformTensorArray>(models);
|
||||
manager.register_pass<ov::frontend::paddle::pass::TransformIf>(models);
|
||||
manager.register_pass<ov::frontend::paddle::pass::TransformWhile>(models);
|
||||
manager.run_passes(model);
|
||||
}
|
||||
if (models.size() > 0) {
|
||||
// revalidate as child models are transformed after parent models.
|
||||
models[0]->validate_nodes_and_infer_types();
|
||||
}
|
||||
}
|
||||
|
||||
bool FrontEnd::supported_impl(const std::vector<ov::Any>& variants) const {
|
||||
@@ -288,7 +410,7 @@ std::shared_ptr<ov::Model> FrontEnd::convert(const InputModel::Ptr& model) const
|
||||
if (!m_transformation_extensions.empty()) {
|
||||
auto function = decode(model);
|
||||
|
||||
pass::Manager manager;
|
||||
ov::pass::Manager manager;
|
||||
for (const auto& transformation : m_transformation_extensions) {
|
||||
transformation->register_pass(manager);
|
||||
}
|
||||
@@ -302,7 +424,9 @@ std::shared_ptr<ov::Model> FrontEnd::convert(const InputModel::Ptr& model) const
|
||||
[&](const std::map<std::string, Output<Node>>& nodes_dict, const std::shared_ptr<OpPlace>& op_place) {
|
||||
return paddle::make_ng_node(nodes_dict, op_place, m_op_translators);
|
||||
});
|
||||
return f;
|
||||
|
||||
try_remove_internal_ops(f);
|
||||
return f[0];
|
||||
}
|
||||
|
||||
void FrontEnd::convert(const std::shared_ptr<ov::Model>& partiallyConverted) const {
|
||||
@@ -314,6 +438,8 @@ void FrontEnd::convert(const std::shared_ptr<ov::Model>& partiallyConverted) con
|
||||
for (const auto& result : partiallyConverted->get_results()) {
|
||||
result->validate_and_infer_types();
|
||||
}
|
||||
|
||||
try_remove_internal_ops({partiallyConverted});
|
||||
}
|
||||
|
||||
std::shared_ptr<ov::Model> FrontEnd::convert_partially(const InputModel::Ptr& model) const {
|
||||
@@ -323,7 +449,7 @@ std::shared_ptr<ov::Model> FrontEnd::convert_partially(const InputModel::Ptr& mo
|
||||
if (!m_transformation_extensions.empty()) {
|
||||
auto function = decode(model);
|
||||
|
||||
pass::Manager manager;
|
||||
ov::pass::Manager manager;
|
||||
for (const auto& transformation : m_transformation_extensions) {
|
||||
transformation->register_pass(manager);
|
||||
}
|
||||
@@ -343,7 +469,10 @@ std::shared_ptr<ov::Model> FrontEnd::convert_partially(const InputModel::Ptr& mo
|
||||
}
|
||||
return named_outputs;
|
||||
});
|
||||
return f;
|
||||
|
||||
try_remove_internal_ops(f);
|
||||
|
||||
return f[0];
|
||||
}
|
||||
|
||||
std::shared_ptr<ov::Model> FrontEnd::decode(const InputModel::Ptr& model) const {
|
||||
@@ -351,7 +480,8 @@ std::shared_ptr<ov::Model> FrontEnd::decode(const InputModel::Ptr& model) const
|
||||
FRONT_END_GENERAL_CHECK(paddle_model != nullptr, "Invalid input model");
|
||||
|
||||
auto f = convert_each_node(paddle_model, paddle::make_framework_node);
|
||||
return f;
|
||||
FRONT_END_GENERAL_CHECK(f.size() == 1, "Input model has subblocks, currently 'decode' could not support it");
|
||||
return f[0];
|
||||
}
|
||||
|
||||
std::string FrontEnd::get_name() const {
|
||||
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
void setElementType(Place::Ptr place, const ov::element::Type&);
|
||||
void setTensorValue(Place::Ptr place, const void* value);
|
||||
|
||||
std::vector<std::shared_ptr<OpPlace>> get_op_places() const;
|
||||
std::vector<std::shared_ptr<OpPlace>> get_op_places(const int32_t blck_idx) const;
|
||||
std::map<std::string, std::shared_ptr<TensorPlace>> get_var_places() const {
|
||||
return m_var_places;
|
||||
}
|
||||
@@ -60,9 +60,10 @@ private:
|
||||
void loadPlaces();
|
||||
template <typename T>
|
||||
void loadConsts(const std::basic_string<T>& folder_with_weights, std::istream* weight_stream);
|
||||
void createTempConsts();
|
||||
std::vector<std::shared_ptr<OpPlace>> determine_cut_nodes() const;
|
||||
|
||||
std::vector<std::shared_ptr<OpPlace>> m_op_places;
|
||||
std::vector<std::vector<std::shared_ptr<OpPlace>>> m_op_places;
|
||||
std::map<std::string, std::shared_ptr<TensorPlace>> m_var_places;
|
||||
std::shared_ptr<ProgramDesc> m_fw_ptr;
|
||||
const InputModel& m_input_model;
|
||||
@@ -81,6 +82,8 @@ void InputModel::InputModelImpl::loadPlaces() {
|
||||
const auto& blocks = m_fw_ptr->blocks();
|
||||
std::map<std::string, uint64_t> op_statistics;
|
||||
|
||||
m_op_places.resize(cnt_of_blocks);
|
||||
|
||||
for (int block_idx = 0; block_idx < cnt_of_blocks; block_idx++) {
|
||||
const auto& block = blocks[block_idx];
|
||||
|
||||
@@ -95,7 +98,7 @@ void InputModel::InputModelImpl::loadPlaces() {
|
||||
op_statistics[op.type()]++;
|
||||
}
|
||||
|
||||
m_op_places.push_back(op_place);
|
||||
m_op_places[block_idx].push_back(op_place);
|
||||
|
||||
for (const auto& output : op.outputs()) {
|
||||
for (const auto& var_name : output.arguments()) {
|
||||
@@ -215,18 +218,20 @@ std::basic_string<wchar_t> get_model_path(const std::basic_string<wchar_t>& path
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
std::vector<std::shared_ptr<OpPlace>> InputModel::InputModelImpl::get_op_places() const {
|
||||
std::vector<std::shared_ptr<OpPlace>> InputModel::InputModelImpl::get_op_places(const int32_t blck_idx) const {
|
||||
if (m_graph_changed) {
|
||||
return determine_cut_nodes();
|
||||
}
|
||||
return m_op_places;
|
||||
if (static_cast<size_t>(blck_idx) < m_op_places.size())
|
||||
return m_op_places[blck_idx];
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<OpPlace>> InputModel::InputModelImpl::determine_cut_nodes() const {
|
||||
std::queue<OpPlace*> q;
|
||||
std::unordered_set<OpPlace*> visited;
|
||||
std::vector<std::shared_ptr<OpPlace>> new_op_places;
|
||||
new_op_places.reserve(m_op_places.size());
|
||||
new_op_places.reserve(m_op_places[0].size());
|
||||
// Marking nodes from outputs to inputs/constants
|
||||
for (const auto& output : getOutputs()) {
|
||||
if (!output->is_input()) {
|
||||
@@ -327,6 +332,53 @@ InputModel::InputModelImpl::InputModelImpl(const std::basic_string<T>& path,
|
||||
} else {
|
||||
loadConsts(path, nullptr);
|
||||
}
|
||||
createTempConsts();
|
||||
}
|
||||
|
||||
void InputModel::InputModelImpl::createTempConsts() {
|
||||
for (const auto& item : m_var_places) {
|
||||
const auto& var_place = item.second;
|
||||
const auto& var_desc = var_place->get_desc();
|
||||
const auto& name = item.first;
|
||||
if (var_desc.persistable())
|
||||
continue;
|
||||
|
||||
// The node with tensorarray as its input may be created before the node with this tensorarray
|
||||
// as its output. e.g. the tensorarray is both the input and output of the same node.
|
||||
// So we have to create a fake empty node here.
|
||||
// Problem is, we have no idea which axis should be 0.
|
||||
// Since the models (faster/mask rcnn) are either concating tensors in tensorarray along the dynamic
|
||||
// dimension, or concating static shape tensors. So we make the dynamic dimension to be 0. In case of static
|
||||
// shape, we simply the the first dimension be 0.
|
||||
if (var_desc.type().has_tensor_array()) {
|
||||
const auto& tensor = var_desc.type().tensor_array().tensor();
|
||||
const auto& type = TYPE_MAP[tensor.data_type()];
|
||||
|
||||
PartialShape tensor_ps(std::vector<Dimension>(tensor.dims().cbegin(), tensor.dims().cend()));
|
||||
tensor_ps.insert(tensor_ps.begin(), 1); // unsqueeze
|
||||
// also update the place for following initialize the graph connection
|
||||
var_place->set_element_type(type);
|
||||
var_place->set_partial_shape(tensor_ps);
|
||||
|
||||
Shape shape(tensor_ps.size());
|
||||
for (auto i = 0; i < tensor_ps.size(); i++) {
|
||||
const auto& dim = tensor_ps[i];
|
||||
if (dim.is_static()) {
|
||||
shape[i] = dim.get_length();
|
||||
}
|
||||
}
|
||||
|
||||
if (tensor_ps.is_static()) {
|
||||
shape[1] = 0;
|
||||
}
|
||||
|
||||
auto node = opset7::Constant::create(type, shape, {0});
|
||||
node->set_friendly_name(name);
|
||||
node->output(0).get_tensor().add_names({name});
|
||||
|
||||
m_tensor_values[name] = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InputModel::InputModelImpl::InputModelImpl(const std::vector<std::istream*>& streams,
|
||||
@@ -347,6 +399,7 @@ InputModel::InputModelImpl::InputModelImpl(const std::vector<std::istream*>& str
|
||||
loadPlaces();
|
||||
if (streams.size() > 1)
|
||||
loadConsts(std::string(), streams[1]);
|
||||
createTempConsts();
|
||||
}
|
||||
|
||||
std::vector<Place::Ptr> InputModel::InputModelImpl::getInputs() const {
|
||||
@@ -438,8 +491,8 @@ InputModel::InputModel(const std::wstring& path, const std::shared_ptr<Telemetry
|
||||
InputModel::InputModel(const std::vector<std::istream*>& streams, const std::shared_ptr<TelemetryExtension>& telemetry)
|
||||
: _impl{std::make_shared<InputModelImpl>(streams, *this, telemetry)} {}
|
||||
|
||||
std::vector<std::shared_ptr<OpPlace>> InputModel::get_op_places() const {
|
||||
return _impl->get_op_places();
|
||||
std::vector<std::shared_ptr<OpPlace>> InputModel::get_op_places(const int32_t blck_idx) const {
|
||||
return _impl->get_op_places(blck_idx);
|
||||
}
|
||||
|
||||
std::map<std::string, std::shared_ptr<TensorPlace>> InputModel::get_var_places() const {
|
||||
|
||||
@@ -38,7 +38,7 @@ private:
|
||||
class InputModelImpl;
|
||||
std::shared_ptr<InputModelImpl> _impl;
|
||||
|
||||
std::vector<std::shared_ptr<OpPlace>> get_op_places() const;
|
||||
std::vector<std::shared_ptr<OpPlace>> get_op_places(const int32_t block_idx) const;
|
||||
std::map<std::string, std::shared_ptr<TensorPlace>> get_var_places() const;
|
||||
std::map<std::string, Output<Node>> get_tensor_values() const;
|
||||
};
|
||||
|
||||
83
src/frontends/paddle/src/internal/op/conditional_block.cpp
Normal file
83
src/frontends/paddle/src/internal/op/conditional_block.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "internal/op/conditional_block.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ngraph/validation_util.hpp>
|
||||
|
||||
#include "ngraph/op/constant.hpp"
|
||||
#include "openvino/op/util/precision_sensitive_attribute.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ov;
|
||||
|
||||
BWDCMP_RTTI_DEFINITION(op::internal::ConditionalBlock);
|
||||
|
||||
op::internal::ConditionalBlock::ConditionalBlock(
|
||||
const Output<Node>& cond,
|
||||
bool is_scalar_condition,
|
||||
int32_t sub_block_index,
|
||||
const std::vector<std::pair<ov::element::Type, ov::PartialShape>>& output_infos)
|
||||
: Op({cond}),
|
||||
m_is_scalar_condition(is_scalar_condition),
|
||||
m_sub_block_index(sub_block_index),
|
||||
m_output_infos(output_infos) {
|
||||
constructor_validate_and_infer_types();
|
||||
}
|
||||
|
||||
op::internal::ConditionalBlock::ConditionalBlock(
|
||||
const OutputVector& inputs,
|
||||
const Output<Node>& cond,
|
||||
bool is_scalar_condition,
|
||||
int32_t sub_block_index,
|
||||
const std::vector<std::pair<ov::element::Type, ov::PartialShape>>& output_infos)
|
||||
: m_is_scalar_condition(is_scalar_condition),
|
||||
m_sub_block_index(sub_block_index),
|
||||
m_output_infos(output_infos) {
|
||||
OutputVector new_args;
|
||||
std::move(inputs.begin(), inputs.end(), std::back_inserter(new_args));
|
||||
new_args.emplace_back(cond);
|
||||
set_arguments(new_args);
|
||||
constructor_validate_and_infer_types();
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> op::internal::ConditionalBlock::clone_with_new_inputs(const OutputVector& new_args) const {
|
||||
check_new_args_count(this, new_args);
|
||||
|
||||
if (new_args.size() == 1) { // w/o inputs
|
||||
return make_shared<ConditionalBlock>(new_args.at(0), m_is_scalar_condition, m_sub_block_index, m_output_infos);
|
||||
} else {
|
||||
OutputVector inputs_args;
|
||||
for (auto i = 0; i < new_args.size() - 1; i++) {
|
||||
inputs_args.push_back(new_args[i]);
|
||||
}
|
||||
return make_shared<ConditionalBlock>(inputs_args,
|
||||
new_args.back(),
|
||||
m_is_scalar_condition,
|
||||
m_sub_block_index,
|
||||
m_output_infos);
|
||||
}
|
||||
}
|
||||
|
||||
bool op::internal::ConditionalBlock::visit_attributes(AttributeVisitor& visitor) {
|
||||
visitor.on_attribute("is_scalar_condition", m_is_scalar_condition);
|
||||
visitor.on_attribute("sub_block_index", m_sub_block_index);
|
||||
return true;
|
||||
}
|
||||
|
||||
void op::internal::ConditionalBlock::validate_and_infer_types() {
|
||||
for (auto i = 0; i < m_output_infos.size(); i++) {
|
||||
set_output_type(i, m_output_infos[i].first, m_output_infos[i].second);
|
||||
}
|
||||
}
|
||||
|
||||
const OutputVector op::internal::ConditionalBlock::get_inputs_from_parent() const {
|
||||
OutputVector result;
|
||||
const auto& inputs = this->input_values();
|
||||
for (size_t i = 0; i < inputs.size() - 1; i++) { // execpt the one at last, which is "cond".
|
||||
result.push_back(inputs[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
50
src/frontends/paddle/src/internal/op/conditional_block.hpp
Normal file
50
src/frontends/paddle/src/internal/op/conditional_block.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openvino/op/op.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace op {
|
||||
namespace internal {
|
||||
class ConditionalBlock : public Op {
|
||||
public:
|
||||
OPENVINO_OP("ConditionalBlock", "internal");
|
||||
BWDCMP_RTTI_DECLARATION;
|
||||
|
||||
ConditionalBlock() = default;
|
||||
|
||||
ConditionalBlock(const OutputVector& inputs,
|
||||
const Output<Node>& cond,
|
||||
bool is_scalar_condition,
|
||||
int32_t sub_block_index,
|
||||
const std::vector<std::pair<ov::element::Type, ov::PartialShape>>& output_infos);
|
||||
ConditionalBlock(const Output<Node>& cond,
|
||||
bool is_scalar_condition,
|
||||
int32_t sub_block_index,
|
||||
const std::vector<std::pair<ov::element::Type, ov::PartialShape>>& output_infos);
|
||||
|
||||
void validate_and_infer_types() override;
|
||||
|
||||
bool visit_attributes(AttributeVisitor& visitor) override;
|
||||
|
||||
std::shared_ptr<Node> clone_with_new_inputs(const OutputVector& new_args) const override;
|
||||
|
||||
/// \return A vector containing the values for each input except "cond".
|
||||
const OutputVector get_inputs_from_parent() const;
|
||||
|
||||
const int32_t get_subblock_index() const {
|
||||
return m_sub_block_index;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_is_scalar_condition;
|
||||
int32_t m_sub_block_index;
|
||||
std::vector<std::pair<ov::element::Type, ov::PartialShape>> m_output_infos;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace op
|
||||
} // namespace ov
|
||||
46
src/frontends/paddle/src/internal/op/tensorarray_write.cpp
Normal file
46
src/frontends/paddle/src/internal/op/tensorarray_write.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "internal/op/tensorarray_write.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ngraph/validation_util.hpp>
|
||||
|
||||
#include "ngraph/op/constant.hpp"
|
||||
#include "openvino/op/util/precision_sensitive_attribute.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ov;
|
||||
|
||||
BWDCMP_RTTI_DEFINITION(op::internal::TensorArrayWrite);
|
||||
|
||||
op::internal::TensorArrayWrite::TensorArrayWrite(const Output<Node>& input, const Output<Node>& index)
|
||||
: Op({input, index}) {
|
||||
constructor_validate_and_infer_types();
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> op::internal::TensorArrayWrite::clone_with_new_inputs(const OutputVector& new_args) const {
|
||||
return make_shared<TensorArrayWrite>(new_args[0], new_args[1]);
|
||||
}
|
||||
|
||||
bool op::internal::TensorArrayWrite::visit_attributes(AttributeVisitor& visitor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// tensorarray_write will be transformed and replaced.
|
||||
// Here we simply make it to be an internal dynamic node, to make sure
|
||||
// all its offsprings will validate and infer shapes from an dynamic input.
|
||||
void op::internal::TensorArrayWrite::validate_and_infer_types() {
|
||||
auto ps = get_input_partial_shape(0);
|
||||
if (ps.rank().is_static() && ps.rank().get_length() >= 1) {
|
||||
ps.insert(ps.begin(), 1); // unsqueeze in order to handyfully slice a tensorarray
|
||||
|
||||
// will use concat to implement tensor_write and a different dimension is enough for
|
||||
// a zero-dimension const input
|
||||
if (ps[1].is_static()) {
|
||||
ps[1] += 1;
|
||||
}
|
||||
}
|
||||
set_output_type(0, get_input_element_type(0), ps);
|
||||
}
|
||||
32
src/frontends/paddle/src/internal/op/tensorarray_write.hpp
Normal file
32
src/frontends/paddle/src/internal/op/tensorarray_write.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openvino/op/op.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace op {
|
||||
namespace internal {
|
||||
class TensorArrayWrite : public Op {
|
||||
public:
|
||||
OPENVINO_OP("TensorArrayWrite", "internal");
|
||||
BWDCMP_RTTI_DECLARATION;
|
||||
|
||||
TensorArrayWrite() = default;
|
||||
|
||||
TensorArrayWrite(const Output<Node>& input, const Output<Node>& index);
|
||||
|
||||
void validate_and_infer_types() override;
|
||||
|
||||
bool visit_attributes(AttributeVisitor& visitor) override;
|
||||
|
||||
std::shared_ptr<Node> clone_with_new_inputs(const OutputVector& new_args) const override;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace op
|
||||
} // namespace ov
|
||||
40
src/frontends/paddle/src/internal/op/while.cpp
Normal file
40
src/frontends/paddle/src/internal/op/while.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "internal/op/while.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ngraph/validation_util.hpp>
|
||||
|
||||
#include "ngraph/op/constant.hpp"
|
||||
#include "openvino/op/util/precision_sensitive_attribute.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ov;
|
||||
|
||||
BWDCMP_RTTI_DEFINITION(op::internal::While);
|
||||
|
||||
op::internal::While::While(const OutputVector& inputs,
|
||||
int32_t sub_block,
|
||||
const std::vector<std::pair<ov::element::Type, ov::PartialShape>>& output_infos)
|
||||
: Op(inputs),
|
||||
m_sub_block(sub_block),
|
||||
m_output_infos(output_infos) {
|
||||
constructor_validate_and_infer_types();
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> op::internal::While::clone_with_new_inputs(const OutputVector& new_args) const {
|
||||
return make_shared<While>(new_args, m_sub_block, m_output_infos);
|
||||
}
|
||||
|
||||
bool op::internal::While::visit_attributes(AttributeVisitor& visitor) {
|
||||
visitor.on_attribute("sub_block", m_sub_block);
|
||||
return true;
|
||||
}
|
||||
|
||||
void op::internal::While::validate_and_infer_types() {
|
||||
for (auto i = 0; i < m_output_infos.size(); i++) {
|
||||
set_output_type(i, m_output_infos[i].first, m_output_infos[i].second);
|
||||
}
|
||||
}
|
||||
41
src/frontends/paddle/src/internal/op/while.hpp
Normal file
41
src/frontends/paddle/src/internal/op/while.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openvino/op/op.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace op {
|
||||
namespace internal {
|
||||
class While : public Op {
|
||||
public:
|
||||
OPENVINO_OP("While", "internal");
|
||||
BWDCMP_RTTI_DECLARATION;
|
||||
|
||||
While() = default;
|
||||
|
||||
While(const OutputVector& inputs,
|
||||
int32_t sub_block,
|
||||
const std::vector<std::pair<ov::element::Type, ov::PartialShape>>& output_infos);
|
||||
|
||||
void validate_and_infer_types() override;
|
||||
|
||||
bool visit_attributes(AttributeVisitor& visitor) override;
|
||||
|
||||
std::shared_ptr<Node> clone_with_new_inputs(const OutputVector& new_args) const override;
|
||||
|
||||
const int32_t get_subblock_index() const {
|
||||
return m_sub_block;
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t m_sub_block = 0;
|
||||
|
||||
std::vector<std::pair<ov::element::Type, ov::PartialShape>> m_output_infos;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace op
|
||||
} // namespace ov
|
||||
110
src/frontends/paddle/src/internal/pass/transform_if.cpp
Normal file
110
src/frontends/paddle/src/internal/pass/transform_if.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "internal/pass/transform_if.hpp"
|
||||
|
||||
#include <ngraph/ngraph.hpp>
|
||||
#include <ngraph/pattern/matcher.hpp>
|
||||
#include <ngraph/pattern/op/or.hpp>
|
||||
#include <ngraph/pattern/op/wrap_type.hpp>
|
||||
#include <ngraph/rt_info.hpp>
|
||||
#include <ngraph/variant.hpp>
|
||||
#include <transformations/common_optimizations/fold_subgraph_empty_inputs.hpp>
|
||||
|
||||
#include "default_opset.hpp"
|
||||
#include "internal/op/conditional_block.hpp"
|
||||
#include "internal/op/tensorarray_write.hpp"
|
||||
#include "ngraph/op/util/op_types.hpp"
|
||||
#include "openvino/frontend/paddle/exception.hpp"
|
||||
#include "openvino/op/util/op_types.hpp"
|
||||
#include "openvino/pass/pattern/op/label.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ov;
|
||||
using namespace ov::pass;
|
||||
using namespace ov::frontend::paddle::op::default_opset;
|
||||
|
||||
// Transform Paddle "conditonal_block" to OpenVINO If op.
|
||||
// The contional_block only has "then" branch, while If op requires both "then" and "else" branch the same time.
|
||||
// Thus a "pass-through" model is built on purpose for "else" branch with the same outputs as "then" branch.
|
||||
ov::frontend::paddle::pass::TransformIf::TransformIf(std::vector<std::shared_ptr<Model>> funcs) {
|
||||
const auto cond_label = ngraph::pattern::wrap_type<ov::op::internal::ConditionalBlock>();
|
||||
|
||||
matcher_pass_callback callback = [funcs](pattern::Matcher& m) -> bool {
|
||||
const auto conditional_block =
|
||||
std::dynamic_pointer_cast<ov::op::internal::ConditionalBlock>(m.get_match_root());
|
||||
const auto mask_idx = conditional_block->get_input_size() - 1;
|
||||
const auto cond = conditional_block->get_input_node_shared_ptr(mask_idx);
|
||||
|
||||
if (!conditional_block || !cond) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// build_if_node
|
||||
const auto then_idx = conditional_block->get_subblock_index();
|
||||
const auto& then_branch = funcs[then_idx];
|
||||
const auto& then_params = then_branch->get_parameters();
|
||||
|
||||
// make a pass-through else branch, as
|
||||
// openvino If requires both then and else branch at the same time.
|
||||
ParameterVector params;
|
||||
ResultVector results;
|
||||
for (auto i = 0; i < then_branch->get_output_size(); i++) {
|
||||
const auto param = std::make_shared<Parameter>(then_branch->get_output_element_type(i),
|
||||
then_branch->get_output_partial_shape(i));
|
||||
param->set_friendly_name(then_branch->get_output_op(i)->get_output_tensor(0).get_any_name());
|
||||
params.push_back(param);
|
||||
const auto result = std::make_shared<Result>(param);
|
||||
results.push_back(result);
|
||||
}
|
||||
const auto else_branch = std::make_shared<Model>(results, params);
|
||||
const auto& else_params = else_branch->get_parameters();
|
||||
|
||||
auto if_node = std::make_shared<If>(cond);
|
||||
ov::pass::disable_fold_subgraph_empty_inputs(if_node);
|
||||
if_node->set_then_body(then_branch);
|
||||
if_node->set_else_body(else_branch);
|
||||
|
||||
const auto then_branch_inputs_from_parent = conditional_block->get_inputs_from_parent();
|
||||
NGRAPH_CHECK(then_branch_inputs_from_parent.size() == then_params.size(),
|
||||
"Number of inputs to 'then_branch' is invalid. Expected " +
|
||||
std::to_string(then_branch_inputs_from_parent.size()) + ", actual " +
|
||||
std::to_string(then_params.size()));
|
||||
auto then_param = then_params.cbegin();
|
||||
for (const auto& from_parent : then_branch_inputs_from_parent) {
|
||||
if_node->set_input(from_parent, *then_param, nullptr);
|
||||
then_param++;
|
||||
}
|
||||
|
||||
for (const auto& else_param : else_params) {
|
||||
bool found = false;
|
||||
for (const auto& from_parent : then_branch_inputs_from_parent) {
|
||||
if (from_parent.get_any_name() == else_param->get_friendly_name()) {
|
||||
if_node->set_input(from_parent, nullptr, else_param);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// the output generate from the body, make a default value
|
||||
if (!found) {
|
||||
auto ps = else_param->get_partial_shape();
|
||||
const auto placeholder = Constant::create(else_param->get_element_type(), ps.get_min_shape(), {0});
|
||||
if_node->set_input(placeholder, nullptr, else_param);
|
||||
}
|
||||
}
|
||||
|
||||
auto else_results = else_branch->get_results();
|
||||
auto then_results = then_branch->get_results();
|
||||
for (auto i = 0; i < else_results.size(); i++) {
|
||||
if_node->set_output(then_results[i], else_results[i]);
|
||||
}
|
||||
replace_node(conditional_block, if_node);
|
||||
if_node->set_friendly_name(conditional_block->get_friendly_name());
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
auto m = std::make_shared<ngraph::pattern::Matcher>(cond_label, "condtionalblock_if");
|
||||
this->register_matcher(m, callback);
|
||||
}
|
||||
27
src/frontends/paddle/src/internal/pass/transform_if.hpp
Normal file
27
src/frontends/paddle/src/internal/pass/transform_if.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openvino/pass/graph_rewrite.hpp"
|
||||
#include "openvino/pass/pass.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace frontend {
|
||||
namespace paddle {
|
||||
namespace pass {
|
||||
|
||||
class TransformIf : public ov::pass::MatcherPass {
|
||||
public:
|
||||
OPENVINO_RTTI("ov::frontend::paddle::pass::TransformIf");
|
||||
TransformIf(std::vector<std::shared_ptr<Model>> functions);
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Model>> m_functions;
|
||||
};
|
||||
|
||||
} // namespace pass
|
||||
} // namespace paddle
|
||||
} // namespace frontend
|
||||
} // namespace ov
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "internal/pass/transform_tensorarray.hpp"
|
||||
|
||||
#include <ngraph/log.hpp>
|
||||
#include <ngraph/ngraph.hpp>
|
||||
#include <ngraph/pattern/matcher.hpp>
|
||||
#include <ngraph/pattern/op/or.hpp>
|
||||
#include <ngraph/pattern/op/wrap_type.hpp>
|
||||
#include <ngraph/rt_info.hpp>
|
||||
#include <ngraph/variant.hpp>
|
||||
#include <transformations/common_optimizations/remove_concat_zero_dim_input.hpp>
|
||||
|
||||
#include "default_opset.hpp"
|
||||
#include "internal/op/conditional_block.hpp"
|
||||
#include "internal/op/tensorarray_write.hpp"
|
||||
#include "internal/op/while.hpp"
|
||||
#include "openvino/frontend/paddle/exception.hpp"
|
||||
#include "openvino/op/util/op_types.hpp"
|
||||
#include "openvino/pass/constant_folding.hpp"
|
||||
#include "openvino/pass/pattern/op/label.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ov;
|
||||
using namespace ov::pass;
|
||||
using namespace frontend::paddle::op::default_opset;
|
||||
|
||||
// Transform pattern "TensorArrayLength->TensorArrayWrite" to OV concat, which
|
||||
// will append to the end of array after unsqueeze along axis 0.
|
||||
ov::frontend::paddle::pass::TransformTensorArray::TransformTensorArray(std::vector<std::shared_ptr<Model>> functions) {
|
||||
const auto shape_label = ngraph::pattern::wrap_type<ShapeOf>();
|
||||
const auto length_label = ngraph::pattern::wrap_type<StridedSlice>(
|
||||
{shape_label, ngraph::pattern::any_input(), ngraph::pattern::any_input(), ngraph::pattern::any_input()});
|
||||
auto write_label =
|
||||
ngraph::pattern::wrap_type<ov::op::internal::TensorArrayWrite>({ngraph::pattern::any_input(), length_label});
|
||||
|
||||
matcher_pass_callback callback = [=](pattern::Matcher& m) -> bool {
|
||||
const auto& opsMap = m.get_pattern_value_map();
|
||||
const auto& write_node = opsMap.at(write_label).get_node_shared_ptr();
|
||||
const auto& shape_node = opsMap.at(shape_label).get_node_shared_ptr();
|
||||
if (!write_node || !shape_node)
|
||||
return false;
|
||||
const auto& new_item = write_node->get_input_node_shared_ptr(0);
|
||||
const auto& list = shape_node->get_input_node_shared_ptr(0);
|
||||
const auto& new_item_unsqueeze = std::make_shared<Unsqueeze>(
|
||||
new_item->output(0),
|
||||
Constant::create(element::i32, {1}, {0})); // unsqueeze in order to handyfully slice a tensorarray
|
||||
// remove TensorArrayLength->TensorArrayWrite
|
||||
const auto concat = std::make_shared<Concat>(OutputVector{list->output(0), new_item_unsqueeze->output(0)}, 1);
|
||||
// prevent to remove concating zero-tensor
|
||||
ov::pass::disable_remove_concat_zerodim_input(concat);
|
||||
|
||||
replace_node(write_node, concat);
|
||||
concat->set_friendly_name(write_node->get_friendly_name());
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
auto m = std::make_shared<ngraph::pattern::Matcher>(write_label, "tensorarray");
|
||||
this->register_matcher(m, callback);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openvino/pass/graph_rewrite.hpp"
|
||||
#include "openvino/pass/pass.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace frontend {
|
||||
namespace paddle {
|
||||
namespace pass {
|
||||
|
||||
class TransformTensorArray : public ov::pass::MatcherPass {
|
||||
public:
|
||||
OPENVINO_RTTI("ov::frontend::paddle::pass::TransformTensorArray");
|
||||
TransformTensorArray(std::vector<std::shared_ptr<Model>> functions);
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Model>> m_functions;
|
||||
};
|
||||
|
||||
} // namespace pass
|
||||
} // namespace paddle
|
||||
} // namespace frontend
|
||||
} // namespace ov
|
||||
104
src/frontends/paddle/src/internal/pass/transform_while.cpp
Normal file
104
src/frontends/paddle/src/internal/pass/transform_while.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "internal/pass/transform_while.hpp"
|
||||
|
||||
#include <ngraph/ngraph.hpp>
|
||||
#include <ngraph/pattern/matcher.hpp>
|
||||
#include <ngraph/pattern/op/or.hpp>
|
||||
#include <ngraph/pattern/op/wrap_type.hpp>
|
||||
#include <ngraph/rt_info.hpp>
|
||||
#include <ngraph/variant.hpp>
|
||||
#include <transformations/common_optimizations/fold_subgraph_empty_inputs.hpp>
|
||||
|
||||
#include "default_opset.hpp"
|
||||
#include "internal/op/conditional_block.hpp"
|
||||
#include "internal/op/tensorarray_write.hpp"
|
||||
#include "internal/op/while.hpp"
|
||||
#include "openvino/frontend/paddle/exception.hpp"
|
||||
#include "openvino/op/util/op_types.hpp"
|
||||
#include "openvino/pass/constant_folding.hpp"
|
||||
#include "openvino/pass/pattern/op/label.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ov;
|
||||
using namespace ov::pass;
|
||||
using namespace ov::frontend::paddle::op::default_opset;
|
||||
|
||||
// Transform Paddle "while" to OpenVINO Loop op.
|
||||
// "set_merged_input" is used for possible cases like TensorArray
|
||||
// when it is both the input and the output to the loop.
|
||||
// The reason why not using concat the output (i.e. "get_concatenated_slices") here is that,
|
||||
// it would complicate processing TensorArray.
|
||||
// TensorArray could be in loop body, but it could not always append something;
|
||||
// TensorArray could be a non-empty input of the loop body, which needs extra concat.
|
||||
// What's more, we have to tell which output is of TensorArray type to concate.
|
||||
ov::frontend::paddle::pass::TransformWhile::TransformWhile(std::vector<std::shared_ptr<Model>> functions) {
|
||||
const auto while_label = ngraph::pattern::wrap_type<ov::op::internal::While>();
|
||||
|
||||
matcher_pass_callback callback = [functions](pattern::Matcher& m) -> bool {
|
||||
const auto& while_node = std::dynamic_pointer_cast<ov::op::internal::While>(m.get_match_root());
|
||||
if (!while_node)
|
||||
return false;
|
||||
const auto& inputs = while_node->input_values();
|
||||
const auto trip_count = Constant::create(element::i64, {1}, {-1});
|
||||
const auto& cond = inputs.back();
|
||||
const auto cond_name = cond.get_node_shared_ptr()->get_friendly_name();
|
||||
auto loop = std::make_shared<Loop>(trip_count, cond);
|
||||
ov::pass::disable_fold_subgraph_empty_inputs(loop);
|
||||
const auto block_idx = while_node->get_subblock_index();
|
||||
const auto sub_model = functions[block_idx];
|
||||
loop->set_function(sub_model);
|
||||
|
||||
const auto& parameters = sub_model->get_parameters();
|
||||
const auto submodel_outputs = sub_model->outputs();
|
||||
const auto is_exist = [&submodel_outputs](const std::string& name) {
|
||||
for (const auto& out : submodel_outputs) {
|
||||
if (out.get_any_name() == name)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
for (size_t i = 0; i < parameters.size(); i++) {
|
||||
const auto names = inputs[i].get_names();
|
||||
std::string param_name;
|
||||
if (!names.empty()) {
|
||||
param_name = *names.begin();
|
||||
}
|
||||
if (!param_name.empty() && is_exist(param_name)) {
|
||||
auto out_node = sub_model->output(param_name);
|
||||
loop->set_merged_input(parameters[i], inputs[i], out_node);
|
||||
} else {
|
||||
loop->set_invariant_input(parameters[i], inputs[i]);
|
||||
}
|
||||
}
|
||||
int64_t idx = -1;
|
||||
for (size_t i = 0; i < sub_model->get_results().size(); i++) {
|
||||
if (sub_model->output(i).get_tensor().get_any_name() == cond_name)
|
||||
idx = static_cast<int64_t>(i);
|
||||
}
|
||||
FRONT_END_GENERAL_CHECK(idx != -1, "could not find condition node in outputs.");
|
||||
|
||||
loop->set_special_body_ports(Loop::SpecialBodyPorts{-1, idx});
|
||||
|
||||
// replace output
|
||||
const auto& results = sub_model->get_results();
|
||||
for (size_t i = 0; i < results.size(); i++) {
|
||||
auto out = loop->get_iter_value(results[i], -1);
|
||||
while_node->output(i).replace(out);
|
||||
}
|
||||
|
||||
loop->add_node_control_dependents(while_node);
|
||||
loop->add_node_control_dependencies(while_node);
|
||||
while_node->clear_control_dependents();
|
||||
|
||||
loop->set_friendly_name(while_node->get_friendly_name());
|
||||
copy_runtime_info(while_node, loop);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
auto m = std::make_shared<ngraph::pattern::Matcher>(while_label, "while_loop");
|
||||
this->register_matcher(m, callback);
|
||||
}
|
||||
27
src/frontends/paddle/src/internal/pass/transform_while.hpp
Normal file
27
src/frontends/paddle/src/internal/pass/transform_while.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openvino/pass/graph_rewrite.hpp"
|
||||
#include "openvino/pass/pass.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace frontend {
|
||||
namespace paddle {
|
||||
namespace pass {
|
||||
|
||||
class TransformWhile : public ov::pass::MatcherPass {
|
||||
public:
|
||||
OPENVINO_RTTI("ov::frontend::paddle::pass::TransformWhile");
|
||||
TransformWhile(std::vector<std::shared_ptr<Model>> functions);
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Model>> m_functions;
|
||||
};
|
||||
|
||||
} // namespace pass
|
||||
} // namespace paddle
|
||||
} // namespace frontend
|
||||
} // namespace ov
|
||||
50
src/frontends/paddle/src/op/conditional_block.cpp
Normal file
50
src/frontends/paddle/src/op/conditional_block.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "internal/op/conditional_block.hpp"
|
||||
|
||||
#include "default_opset.hpp"
|
||||
#include "internal/op/while.hpp"
|
||||
#include "openvino/frontend/paddle/node_context.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace frontend {
|
||||
namespace paddle {
|
||||
namespace op {
|
||||
NamedOutputs conditional_block(const NodeContext& node) {
|
||||
const auto cond = node.get_input("Cond");
|
||||
const auto sub_block = node.get_attribute<int32_t>("sub_block");
|
||||
const auto is_scalar_condition = node.get_attribute<bool>("is_scalar_condition", true);
|
||||
|
||||
const auto outputs_info = node.get_output_port_infos("Out");
|
||||
|
||||
std::shared_ptr<Node> placehodler;
|
||||
if (node.has_input("Input")) {
|
||||
const auto inputs = node.get_ng_inputs("Input");
|
||||
placehodler = std::make_shared<ov::op::internal::ConditionalBlock>(inputs,
|
||||
cond,
|
||||
is_scalar_condition,
|
||||
sub_block,
|
||||
outputs_info);
|
||||
} else {
|
||||
placehodler =
|
||||
std::make_shared<ov::op::internal::ConditionalBlock>(cond, is_scalar_condition, sub_block, outputs_info);
|
||||
}
|
||||
const auto outputs = placehodler->outputs();
|
||||
|
||||
auto out_names = node.get_output_names();
|
||||
auto it = std::find(out_names.begin(), out_names.end(), "Out");
|
||||
PADDLE_OP_CHECK(node, it != out_names.end(), "Expected output not found");
|
||||
|
||||
NamedOutputs named_outputs;
|
||||
for (const auto& output : outputs) {
|
||||
named_outputs[*it].push_back(output);
|
||||
}
|
||||
return named_outputs;
|
||||
}
|
||||
|
||||
} // namespace op
|
||||
} // namespace paddle
|
||||
} // namespace frontend
|
||||
} // namespace ov
|
||||
32
src/frontends/paddle/src/op/lod_array_length.cpp
Normal file
32
src/frontends/paddle/src/op/lod_array_length.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "default_opset.hpp"
|
||||
#include "openvino/frontend/paddle/node_context.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace frontend {
|
||||
namespace paddle {
|
||||
namespace op {
|
||||
NamedOutputs lod_array_length(const NodeContext& node) {
|
||||
using namespace default_opset;
|
||||
const auto x = node.get_input("X");
|
||||
const auto shape = std::make_shared<default_opset::ShapeOf>(x);
|
||||
// here simply get the length along the concated axis.
|
||||
// we've lost the original tensor array length actually.
|
||||
// luckily it's equalent since all elements are concated.
|
||||
const auto const_1_node = Constant::create(element::i64, {1}, {1});
|
||||
const auto const_2_node = Constant::create(element::i64, {1}, {2});
|
||||
const auto len = std::make_shared<StridedSlice>(shape,
|
||||
const_1_node,
|
||||
const_2_node,
|
||||
std::vector<int64_t>{0},
|
||||
std::vector<int64_t>{0});
|
||||
|
||||
return node.default_single_output_mapping({len}, {"Out"});
|
||||
}
|
||||
} // namespace op
|
||||
} // namespace paddle
|
||||
} // namespace frontend
|
||||
} // namespace ov
|
||||
68
src/frontends/paddle/src/op/select_input.cpp
Normal file
68
src/frontends/paddle/src/op/select_input.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "default_opset.hpp"
|
||||
#include "openvino/frontend/paddle/node_context.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace frontend {
|
||||
namespace paddle {
|
||||
namespace op {
|
||||
NamedOutputs select_input(const NodeContext& node) {
|
||||
const auto x = node.get_ng_inputs("X");
|
||||
const auto mask = node.get_input("Mask");
|
||||
|
||||
PADDLE_OP_CHECK(node, x.size() == 2, "select_input needs 2 input nodes.");
|
||||
|
||||
const auto cond = std::make_shared<default_opset::Convert>(mask, element::boolean);
|
||||
const auto ps0 = x[0].get_partial_shape();
|
||||
const auto ps1 = x[1].get_partial_shape();
|
||||
int idx0 = -1;
|
||||
int idx1 = -1;
|
||||
|
||||
if (ps0.compatible(ps1)) {
|
||||
idx0 = 0;
|
||||
idx1 = 1;
|
||||
} else {
|
||||
// paddle detection model code is wrong and will result a dynamic rank model:
|
||||
// https://github.com/PaddlePaddle/PaddleDetection/blob/16e3d7408161713c765886cfb952f98d9f68713c/ppdet/modeling/layers.py#L407
|
||||
// workaround: check the rank and remove the wrong condition
|
||||
if (ps0.rank() != ps1.rank()) {
|
||||
const auto fix_idx = [&](int idx) {
|
||||
const auto ps = x[idx].get_partial_shape();
|
||||
if (ps.is_static()) {
|
||||
const Shape shape(ps.get_shape());
|
||||
const auto size = std::accumulate(shape.begin(), shape.end(), size_t{1}, std::multiplies<size_t>());
|
||||
if (size == 0)
|
||||
return 1 - idx;
|
||||
}
|
||||
return idx;
|
||||
};
|
||||
idx0 = fix_idx(0);
|
||||
idx1 = fix_idx(1);
|
||||
}
|
||||
PADDLE_OP_CHECK(node, idx0 >= 0, "input shapes should be compatible.");
|
||||
}
|
||||
// paddle two branch may produce dynamic shape, use 'if' to satisfy it
|
||||
const auto ps0_new = x[idx0].get_partial_shape();
|
||||
const auto ps1_new = x[idx1].get_partial_shape();
|
||||
const auto if_node = std::make_shared<default_opset::If>(cond);
|
||||
const auto then_param = std::make_shared<default_opset::Parameter>(x[idx1].get_element_type(), ps1_new);
|
||||
const auto then_result = std::make_shared<default_opset::Result>(then_param);
|
||||
const auto then_branch = std::make_shared<Model>(ResultVector{then_result}, ParameterVector{then_param});
|
||||
const auto else_param = std::make_shared<default_opset::Parameter>(x[idx0].get_element_type(), ps0_new);
|
||||
const auto else_result = std::make_shared<default_opset::Result>(else_param);
|
||||
const auto else_branch = std::make_shared<Model>(ResultVector{else_result}, ParameterVector{else_param});
|
||||
if_node->set_then_body(then_branch);
|
||||
if_node->set_else_body(else_branch);
|
||||
if_node->set_input(x[idx1], then_param, nullptr);
|
||||
if_node->set_input(x[idx0], nullptr, else_param);
|
||||
if_node->set_output(then_result, else_result);
|
||||
return node.default_single_output_mapping({if_node}, {"Out"});
|
||||
}
|
||||
|
||||
} // namespace op
|
||||
} // namespace paddle
|
||||
} // namespace frontend
|
||||
} // namespace ov
|
||||
49
src/frontends/paddle/src/op/tensor_array_to_tensor.cpp
Normal file
49
src/frontends/paddle/src/op/tensor_array_to_tensor.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "default_opset.hpp"
|
||||
#include "openvino/frontend/paddle/node_context.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace frontend {
|
||||
namespace paddle {
|
||||
namespace op {
|
||||
// Paddle TensorArray is not natively supported by OpenVINO.
|
||||
// Here paddle frontend only partially support following circumstances:
|
||||
// 1. TensorArray could be indexed with paddle slice op.
|
||||
// We only support slice along axis 0 with only 1 element in TensorArray for now,
|
||||
// with unsqueezing the element along axis 0 manually.
|
||||
// 2. TensoArray could be tranfered to tensor with paddle tensor_array_to_tensor op.
|
||||
// The elements in it should be concated along an axis.
|
||||
// We only support concat along axis 0 for now. paddle.concat always along axis 0.
|
||||
// what's more, we only support the pattern of "TensorArrayLength<->TensorArrayWrite" for now, which
|
||||
// is tranformed togther. That means, tensorarray are always appended at the end.
|
||||
NamedOutputs tensor_array_to_tensor(const NodeContext& node) {
|
||||
using namespace default_opset;
|
||||
const auto x = node.get_input("X");
|
||||
auto axis = node.get_attribute<int32_t>("axis", 0);
|
||||
PADDLE_OP_CHECK(node, axis == 0, "axis should be 0, got: ", axis);
|
||||
|
||||
// All elements in TensorArray have already been unsqueezed and concated. So here
|
||||
// just squeeze. We use reshape instead because the tensorarray could be empty, and
|
||||
// it looks squeeze would fail with empty tensor.
|
||||
const auto shape = std::make_shared<ShapeOf>(x, element::Type_t::i32);
|
||||
const auto const_1_node = Constant::create(element::i32, {1}, {1});
|
||||
const auto const_max_node = Constant::create(element::i32, {1}, {INT_MAX});
|
||||
const auto new_shape = std::make_shared<StridedSlice>(shape,
|
||||
const_1_node,
|
||||
const_max_node,
|
||||
std::vector<int64_t>{0},
|
||||
std::vector<int64_t>{0});
|
||||
|
||||
auto placeholder = std::make_shared<Reshape>(x, new_shape, false);
|
||||
|
||||
return node.default_single_output_mapping({placeholder}, {"Out"});
|
||||
}
|
||||
} // namespace op
|
||||
} // namespace paddle
|
||||
} // namespace frontend
|
||||
} // namespace ov
|
||||
32
src/frontends/paddle/src/op/while.cpp
Normal file
32
src/frontends/paddle/src/op/while.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "internal/op/while.hpp"
|
||||
|
||||
#include "default_opset.hpp"
|
||||
#include "openvino/frontend/paddle/node_context.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace frontend {
|
||||
namespace paddle {
|
||||
namespace op {
|
||||
|
||||
using namespace default_opset;
|
||||
|
||||
NamedOutputs while_(const NodeContext& node) {
|
||||
const auto data = node.get_ng_inputs("X");
|
||||
const auto cond = node.get_input("Condition");
|
||||
const auto sub_block = node.get_attribute<int32_t>("sub_block");
|
||||
auto outputs_info = node.get_output_port_infos("Out");
|
||||
|
||||
ov::OutputVector inputs = data;
|
||||
inputs.push_back(cond);
|
||||
NamedOutputs named_outputs;
|
||||
named_outputs["Out"] = std::make_shared<ov::op::internal::While>(inputs, sub_block, outputs_info)->outputs();
|
||||
return named_outputs;
|
||||
}
|
||||
} // namespace op
|
||||
} // namespace paddle
|
||||
} // namespace frontend
|
||||
} // namespace ov
|
||||
24
src/frontends/paddle/src/op/write_to_array.cpp
Normal file
24
src/frontends/paddle/src/op/write_to_array.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "default_opset.hpp"
|
||||
#include "internal/op/tensorarray_write.hpp"
|
||||
#include "openvino/frontend/paddle/node_context.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace frontend {
|
||||
namespace paddle {
|
||||
namespace op {
|
||||
NamedOutputs write_to_array(const NodeContext& node) {
|
||||
const auto x = node.get_input("X");
|
||||
const auto index = node.get_input("I");
|
||||
|
||||
auto placehodler = std::make_shared<ov::op::internal::TensorArrayWrite>(x, index);
|
||||
|
||||
return node.default_single_output_mapping({placehodler}, {"Out"});
|
||||
}
|
||||
} // namespace op
|
||||
} // namespace paddle
|
||||
} // namespace frontend
|
||||
} // namespace ov
|
||||
@@ -18,6 +18,7 @@ OP_CONVERTER(cast);
|
||||
OP_CONVERTER(ceil);
|
||||
OP_CONVERTER(clip);
|
||||
OP_CONVERTER(concat);
|
||||
OP_CONVERTER(conditional_block);
|
||||
OP_CONVERTER(conv2d);
|
||||
OP_CONVERTER(conv2d_transpose);
|
||||
OP_CONVERTER(cumsum);
|
||||
@@ -53,6 +54,7 @@ OP_CONVERTER(layer_norm);
|
||||
OP_CONVERTER(leaky_relu);
|
||||
OP_CONVERTER(less_than);
|
||||
OP_CONVERTER(linear_interp_v2);
|
||||
OP_CONVERTER(lod_array_length);
|
||||
OP_CONVERTER(log);
|
||||
OP_CONVERTER(logical_and);
|
||||
OP_CONVERTER(logical_not);
|
||||
@@ -81,6 +83,7 @@ OP_CONVERTER(reshape2);
|
||||
OP_CONVERTER(rnn);
|
||||
OP_CONVERTER(roi_align);
|
||||
OP_CONVERTER(scale);
|
||||
OP_CONVERTER(select_input);
|
||||
OP_CONVERTER(shape);
|
||||
OP_CONVERTER(slice);
|
||||
OP_CONVERTER(softmax);
|
||||
@@ -94,12 +97,15 @@ OP_CONVERTER(strided_slice);
|
||||
OP_CONVERTER(sum);
|
||||
OP_CONVERTER(swish);
|
||||
OP_CONVERTER(tanh);
|
||||
OP_CONVERTER(tensor_array_to_tensor);
|
||||
OP_CONVERTER(tile);
|
||||
OP_CONVERTER(top_k_v2);
|
||||
OP_CONVERTER(transpose2);
|
||||
OP_CONVERTER(trilinear_interp_v2);
|
||||
OP_CONVERTER(unsqueeze);
|
||||
OP_CONVERTER(where);
|
||||
OP_CONVERTER(while_);
|
||||
OP_CONVERTER(write_to_array);
|
||||
OP_CONVERTER(where_index);
|
||||
OP_CONVERTER(yolo_box);
|
||||
OP_CONVERTER(generate_proposals_v2);
|
||||
@@ -117,6 +123,7 @@ std::map<std::string, CreatorFunction> get_supported_ops() {
|
||||
{"ceil", op::ceil},
|
||||
{"clip", op::clip},
|
||||
{"concat", op::concat},
|
||||
{"conditional_block", op::conditional_block},
|
||||
{"conv2d", op::conv2d},
|
||||
{"conv2d_transpose", op::conv2d_transpose},
|
||||
{"cumsum", op::cumsum},
|
||||
@@ -145,6 +152,7 @@ std::map<std::string, CreatorFunction> get_supported_ops() {
|
||||
{"gather", op::gather},
|
||||
{"gather_nd", op::gather_nd},
|
||||
{"gelu", op::gelu},
|
||||
{"generate_proposals_v2", op::generate_proposals_v2},
|
||||
{"greater_equal", op::elementwise_greater_equal},
|
||||
{"greater_than", op::greater_than},
|
||||
{"group_norm", op::group_norm},
|
||||
@@ -154,6 +162,7 @@ std::map<std::string, CreatorFunction> get_supported_ops() {
|
||||
{"leaky_relu", op::leaky_relu},
|
||||
{"less_than", op::less_than},
|
||||
{"linear_interp_v2", op::linear_interp_v2},
|
||||
{"lod_array_length", op::lod_array_length},
|
||||
{"log", op::log},
|
||||
{"logical_and", op::logical_and},
|
||||
{"logical_not", op::logical_not},
|
||||
@@ -185,6 +194,7 @@ std::map<std::string, CreatorFunction> get_supported_ops() {
|
||||
{"rnn", op::rnn},
|
||||
{"roi_align", op::roi_align},
|
||||
{"scale", op::scale},
|
||||
{"select_input", op::select_input},
|
||||
{"shape", op::shape},
|
||||
{"slice", op::slice},
|
||||
{"softmax", op::softmax},
|
||||
@@ -199,12 +209,15 @@ std::map<std::string, CreatorFunction> get_supported_ops() {
|
||||
{"swish", op::swish},
|
||||
{"sync_batch_norm", op::batch_norm},
|
||||
{"tanh", op::tanh},
|
||||
{"tensor_array_to_tensor", op::tensor_array_to_tensor},
|
||||
{"tile", op::tile},
|
||||
{"top_k_v2", op::top_k_v2},
|
||||
{"transpose2", op::transpose2},
|
||||
{"trilinear_interp_v2", op::trilinear_interp_v2},
|
||||
{"unsqueeze2", op::unsqueeze},
|
||||
{"where", op::where},
|
||||
{"while", op::while_},
|
||||
{"write_to_array", op::write_to_array},
|
||||
{"where_index", op::where_index},
|
||||
{"yolo_box", op::yolo_box},
|
||||
{"generate_proposals_v2", op::generate_proposals_v2}};
|
||||
|
||||
@@ -81,6 +81,8 @@ void FrontEndFuzzyOpTest::runConvertedModel(const std::shared_ptr<ngraph::Functi
|
||||
addInputOutput<int32_t>(input, testCase, true);
|
||||
} else if (input_dtype == element::i64) {
|
||||
addInputOutput<int64_t>(input, testCase, true);
|
||||
} else if (input_dtype == element::boolean) {
|
||||
addInputOutput<bool>(input, testCase, true);
|
||||
} else {
|
||||
throw std::runtime_error("not supported dtype in" + input_dtype.get_type_name());
|
||||
}
|
||||
|
||||
@@ -37,6 +37,46 @@ TEST_F(TransformationTestsF, RemoveConcatZeroDimInputStaticShape) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TransformationTestsF, DisableRemoveConcatZeroDimInputStaticShape) {
|
||||
auto input1 = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape{1, 2, 3});
|
||||
auto input2 = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape{1, 0, 3});
|
||||
auto input3 = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape{1, 2, 3});
|
||||
int64_t axis = 1;
|
||||
{
|
||||
auto concat = std::make_shared<ov::opset8::Concat>(ov::OutputVector{input1, input2, input3}, axis);
|
||||
ov::pass::disable_remove_concat_zerodim_input(concat);
|
||||
|
||||
function = std::make_shared<ov::Model>(ov::NodeVector{concat}, ov::ParameterVector{input1, input2, input3});
|
||||
|
||||
manager.register_pass<ov::pass::RemoveConcatZeroDimInput>();
|
||||
}
|
||||
|
||||
{
|
||||
auto concat = std::make_shared<ov::opset8::Concat>(ov::OutputVector{input1, input2, input3}, axis);
|
||||
function_ref = std::make_shared<ov::Model>(ov::NodeVector{concat}, ov::ParameterVector{input1, input2, input3});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TransformationTestsF, DisableRemoveConcatZeroDimInputPartiallyKnowShape) {
|
||||
auto input1 = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape{-1, -1, -1});
|
||||
auto input2 = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape{-1, 0, -1});
|
||||
auto input3 = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape{-1, -1, -1});
|
||||
int64_t axis = 1;
|
||||
{
|
||||
auto concat = std::make_shared<ov::opset8::Concat>(ov::OutputVector{input1, input2, input3}, axis);
|
||||
ov::pass::disable_remove_concat_zerodim_input(concat);
|
||||
|
||||
function = std::make_shared<ov::Model>(ov::NodeVector{concat}, ov::ParameterVector{input1, input2, input3});
|
||||
|
||||
manager.register_pass<ov::pass::RemoveConcatZeroDimInput>();
|
||||
}
|
||||
|
||||
{
|
||||
auto concat = std::make_shared<ov::opset8::Concat>(ov::OutputVector{input1, input2, input3}, axis);
|
||||
function_ref = std::make_shared<ov::Model>(ov::NodeVector{concat}, ov::ParameterVector{input1, input2, input3});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TransformationTestsF, RemoveConcatZeroDimInputSubgraph) {
|
||||
auto input1 = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape{1, 2, 3});
|
||||
auto input3 = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape{1, 2, 3});
|
||||
|
||||
@@ -164,3 +164,47 @@ TEST(TransformationTests, UnrollIfWithSplitInput) {
|
||||
auto res = compare_functions(f, f_ref);
|
||||
ASSERT_TRUE(res.first) << res.second;
|
||||
}
|
||||
|
||||
TEST(TransformationTests, UnrollIfCondIsTrueMultiOutput) {
|
||||
std::shared_ptr<ngraph::Function> f(nullptr), f_ref(nullptr);
|
||||
{
|
||||
auto data = std::make_shared<ngraph::opset6::Parameter>(ngraph::element::f32, ngraph::Shape{ 3 });
|
||||
auto X = std::make_shared<ngraph::opset6::VariadicSplit>(data, ngraph::opset6::Constant::create(ngraph::element::i32, {1}, {0}),
|
||||
ngraph::opset6::Constant::create(ngraph::element::i32, {2}, {1, 2}));
|
||||
auto cond = std::make_shared<ngraph::opset1::Constant>(ngraph::element::boolean, ngraph::Shape{ 1 }, true);
|
||||
auto if_op = std::make_shared<ngraph::opset8::If>(cond);
|
||||
auto Xt = std::make_shared<ngraph::opset6::Parameter>(ngraph::element::f32, ngraph::Shape{ 2 });
|
||||
auto then_op_result = std::make_shared<ngraph::opset1::Result>(Xt);
|
||||
auto then_body = std::make_shared<ngraph::Function>(ngraph::OutputVector{ then_op_result }, ngraph::ParameterVector{ Xt });
|
||||
|
||||
auto Xe = std::make_shared<ngraph::opset6::Parameter>(ngraph::element::f32, ngraph::Shape{ 2 });
|
||||
auto else_op_result = std::make_shared<ngraph::opset1::Result>(Xe);
|
||||
auto else_body = std::make_shared<ngraph::Function>(ngraph::OutputVector{ else_op_result }, ngraph::ParameterVector{ Xe });
|
||||
|
||||
if_op->set_then_body(then_body);
|
||||
if_op->set_else_body(else_body);
|
||||
if_op->set_input(X->output(1), Xt, Xe);
|
||||
if_op->set_output(then_op_result, else_op_result);
|
||||
auto if_result = std::make_shared<ngraph::opset1::Result>(if_op);
|
||||
|
||||
f = std::make_shared<ngraph::Function>(ngraph::NodeVector{ if_result }, ngraph::ParameterVector{ data });
|
||||
|
||||
ngraph::pass::Manager manager;
|
||||
manager.register_pass<ngraph::pass::InitNodeInfo>();
|
||||
manager.register_pass<ngraph::pass::UnrollIf>();
|
||||
manager.run_passes(f);
|
||||
|
||||
ASSERT_NO_THROW(check_rt_info(f));
|
||||
}
|
||||
|
||||
{
|
||||
auto data = std::make_shared<ngraph::opset6::Parameter>(ngraph::element::f32, ngraph::Shape{ 3 });
|
||||
auto X = std::make_shared<ngraph::opset6::VariadicSplit>(data, ngraph::opset6::Constant::create(ngraph::element::i32, {1}, {0}),
|
||||
ngraph::opset6::Constant::create(ngraph::element::i32, {2}, {1, 2}));
|
||||
auto if_result = std::make_shared<ngraph::opset1::Result>(X->output(1));
|
||||
f_ref = std::make_shared<ngraph::Function>(ngraph::NodeVector{ if_result }, ngraph::ParameterVector{ data });
|
||||
}
|
||||
|
||||
auto res = compare_functions(f, f_ref);
|
||||
ASSERT_TRUE(res.first) << res.second;
|
||||
}
|
||||
|
||||
@@ -118,6 +118,11 @@ bool equal_type_and_partial_shape(const InOut1& lhs, const InOut2& rhs) {
|
||||
return lhs.get_element_type() == rhs.get_element_type() && lhs.get_partial_shape() == rhs.get_partial_shape();
|
||||
}
|
||||
|
||||
template <typename InOut1, typename InOut2>
|
||||
bool equal_type_and_partial_shape_compatible(const InOut1& lhs, const InOut2& rhs) {
|
||||
return lhs.get_element_type() == rhs.get_element_type() && lhs.get_partial_shape().compatible(rhs.get_partial_shape());
|
||||
}
|
||||
|
||||
class NodeAndInputDescription {
|
||||
public:
|
||||
using SubGraphOp = ov::op::util::SubGraphOp;
|
||||
@@ -187,7 +192,8 @@ public:
|
||||
return true;
|
||||
} else if (m_description->get_type_info() == SubGraphOp::MergedInputDescription::get_type_info_static() ||
|
||||
m_description->get_type_info() == SubGraphOp::InvariantInputDescription::get_type_info_static()) {
|
||||
return equal_type_and_partial_shape(*m_parameter, m_input);
|
||||
// If loop op has back edge it may change the parameter to dynamic. The shape will be different with the initial op
|
||||
return equal_type_and_partial_shape_compatible(*m_parameter, m_input);
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
Reference in New Issue
Block a user