If. nGraph implementation + reference (#6777)
* Add If implementation with reference * fix test * fix comments * Fix validate_and_INFER_TYPES * rewrite tests for dynamic cases * Fix ci failed * add comentaries for validate_and_infer_types * fix api * Added ngraph checks and delete copied op from opset8 * code style fix * fix code style * add checkers to reference * add has_evaluate * fix eval * Fix code style * fix code style
This commit is contained in:
committed by
GitHub
parent
c32ecd957b
commit
e13ac4d9ac
94
ngraph/core/include/ngraph/op/if.hpp
Normal file
94
ngraph/core/include/ngraph/op/if.hpp
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright (C) 2018-2021 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ngraph/function.hpp"
|
||||
#include "ngraph/op/parameter.hpp"
|
||||
#include "ngraph/op/util/multi_subgraph_base.hpp"
|
||||
|
||||
namespace ngraph {
|
||||
namespace op {
|
||||
namespace v8 {
|
||||
/// \brief If operation.
|
||||
class NGRAPH_API If : public util::MultiSubGraphOp {
|
||||
public:
|
||||
enum BodyIndexes { THEN_BODY_INDEX = 0, ELSE_BODY_INDEX = 1 };
|
||||
|
||||
NGRAPH_RTTI_DECLARATION;
|
||||
bool visit_attributes(AttributeVisitor& visitor) override;
|
||||
|
||||
/// \brief Constructs If with condition
|
||||
///
|
||||
/// \param execution_condition condition node.
|
||||
If(const Output<Node>& execution_condition);
|
||||
If();
|
||||
|
||||
std::shared_ptr<Node> clone_with_new_inputs(const OutputVector& new_args) const override;
|
||||
|
||||
/// \brief gets then_body as ngraph::Function.
|
||||
///
|
||||
/// \return then_body as ngraph::Function.
|
||||
const std::shared_ptr<Function>& get_then_body() const {
|
||||
return m_bodies[THEN_BODY_INDEX];
|
||||
}
|
||||
|
||||
/// \brief gets else_body as ngraph::Function.
|
||||
///
|
||||
/// \return else_body as ngraph::Function.
|
||||
const std::shared_ptr<Function>& get_else_body() const {
|
||||
return m_bodies[ELSE_BODY_INDEX];
|
||||
}
|
||||
|
||||
/// \brief sets new ngraph::Function as new then_body.
|
||||
///
|
||||
/// \param body new body for 'then' branch.
|
||||
void set_then_body(const std::shared_ptr<Function>& body) {
|
||||
m_bodies[THEN_BODY_INDEX] = body;
|
||||
}
|
||||
|
||||
/// \brief sets new ngraph::Function as new else_body.
|
||||
///
|
||||
/// \param body new body for 'else' branch.
|
||||
void set_else_body(const std::shared_ptr<Function>& body) {
|
||||
m_bodies[ELSE_BODY_INDEX] = body;
|
||||
}
|
||||
|
||||
/// \brief sets new input to the operation associated with parameters
|
||||
/// of each sub-graphs
|
||||
///
|
||||
/// \param value input to operation
|
||||
/// \param then_parameter parameter for then_body or nullptr
|
||||
/// \param else_parameter parameter for else_body or nullpt
|
||||
void set_input(const Output<Node>& value,
|
||||
const std::shared_ptr<Parameter>& then_parameter,
|
||||
const std::shared_ptr<Parameter>& else_parameter);
|
||||
|
||||
/// \brief sets new output from the operation associated with results
|
||||
/// of each sub-graphs
|
||||
///
|
||||
/// \param then_result result from then_body
|
||||
/// \param else_parameter result from else_body
|
||||
/// \return output from operation
|
||||
Output<Node> set_output(const std::shared_ptr<Result>& then_result, const std::shared_ptr<Result>& else_result);
|
||||
|
||||
void validate_and_infer_types() override;
|
||||
bool evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const override;
|
||||
|
||||
bool has_evaluate() const override;
|
||||
|
||||
private:
|
||||
using OutputMap = std::map<int64_t, std::shared_ptr<MultiSubGraphOp::OutputDescription>>;
|
||||
|
||||
void validate_and_infer_type_body(const std::shared_ptr<Function>& body,
|
||||
const ngraph::op::util::MultiSubgraphInputDescriptionVector& input_descriptors);
|
||||
|
||||
OutputMap get_mapping_outputs_on_body_description(
|
||||
const ngraph::op::util::MultiSubgraphOutputDescriptionVector& output_descriptors);
|
||||
};
|
||||
} // namespace v8
|
||||
} // namespace op
|
||||
} // namespace ngraph
|
||||
@@ -75,6 +75,7 @@
|
||||
#include "ngraph/op/hsigmoid.hpp"
|
||||
#include "ngraph/op/hswish.hpp"
|
||||
#include "ngraph/op/idft.hpp"
|
||||
#include "ngraph/op/if.hpp"
|
||||
#include "ngraph/op/interpolate.hpp"
|
||||
#include "ngraph/op/less.hpp"
|
||||
#include "ngraph/op/less_eq.hpp"
|
||||
|
||||
@@ -182,3 +182,4 @@ NGRAPH_OP(MatrixNms, ngraph::op::v8)
|
||||
NGRAPH_OP(MaxPool, ngraph::op::v8)
|
||||
NGRAPH_OP(MulticlassNms, ngraph::op::v8)
|
||||
NGRAPH_OP(RandomUniform, ngraph::op::v8)
|
||||
NGRAPH_OP(If, ngraph::op::v8)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (C) 2018-2021 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "ngraph/op/util/multi_subgraph_base.hpp"
|
||||
|
||||
namespace ngraph {
|
||||
namespace runtime {
|
||||
namespace reference {
|
||||
void if_reference(const std::vector<std::shared_ptr<Function>>& body,
|
||||
const std::vector<op::util::MultiSubgraphOutputDescriptionVector>& out_descs,
|
||||
const std::vector<op::util::MultiSubgraphInputDescriptionVector>& input_descs,
|
||||
const HostTensorVector& out,
|
||||
const HostTensorVector& args);
|
||||
}
|
||||
} // namespace runtime
|
||||
} // namespace ngraph
|
||||
45
ngraph/core/reference/src/runtime/reference/if.cpp
Normal file
45
ngraph/core/reference/src/runtime/reference/if.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright (C) 2018-2021 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "ngraph/runtime/reference/if.hpp"
|
||||
|
||||
#include "ngraph/op/if.hpp"
|
||||
#include "ngraph/runtime/reference/function.hpp"
|
||||
|
||||
namespace ngraph {
|
||||
namespace runtime {
|
||||
namespace reference {
|
||||
void if_reference(const std::vector<std::shared_ptr<Function>>& bodies,
|
||||
const std::vector<op::util::MultiSubgraphOutputDescriptionVector>& out_descs,
|
||||
const std::vector<op::util::MultiSubgraphInputDescriptionVector>& input_descs,
|
||||
const HostTensorVector& out,
|
||||
const HostTensorVector& args) {
|
||||
NGRAPH_CHECK(args.size() > 0, "If operation must have input condition value");
|
||||
|
||||
auto condition_value = args[0]->get_data_ptr<bool>()[0];
|
||||
auto branch_index = (condition_value) ? op::v8::If::THEN_BODY_INDEX : op::v8::If::ELSE_BODY_INDEX;
|
||||
HostTensorVector inputs_to_body;
|
||||
HostTensorVector outs_from_body;
|
||||
inputs_to_body.resize(input_descs[branch_index].size());
|
||||
auto inputs_size = args.size();
|
||||
auto output_size = out.size();
|
||||
for (const auto& input_desc : input_descs[branch_index]) {
|
||||
NGRAPH_CHECK(inputs_size > input_desc->m_input_index,
|
||||
"Incorrect associating! If has not input with id ",
|
||||
input_desc->m_input_index);
|
||||
inputs_to_body[input_desc->m_body_parameter_index] = args[input_desc->m_input_index];
|
||||
}
|
||||
reference::function(bodies[branch_index], inputs_to_body, outs_from_body);
|
||||
for (const auto& out_descr : out_descs[branch_index]) {
|
||||
NGRAPH_CHECK(output_size > out_descr->m_output_index,
|
||||
"Incorrect associating! If has not output with id ",
|
||||
out_descr->m_output_index);
|
||||
auto res = outs_from_body[out_descr->m_body_value_index];
|
||||
out[out_descr->m_output_index]->set_shape(res->get_shape());
|
||||
out[out_descr->m_output_index]->write(res->get_data_ptr(), res->get_size_in_bytes());
|
||||
}
|
||||
}
|
||||
} // namespace reference
|
||||
} // namespace runtime
|
||||
} // namespace ngraph
|
||||
267
ngraph/core/src/op/if.cpp
Normal file
267
ngraph/core/src/op/if.cpp
Normal file
@@ -0,0 +1,267 @@
|
||||
// Copyright (C) 2018-2021 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "ngraph/op/if.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <ngraph/validation_util.hpp>
|
||||
|
||||
#include "itt.hpp"
|
||||
#include "ngraph/factory.hpp"
|
||||
#include "ngraph/graph_util.hpp"
|
||||
#include "ngraph/op/util/multi_subgraph_base.hpp"
|
||||
#include "ngraph/runtime/reference/if.hpp"
|
||||
#include "ngraph/specialize_function.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ngraph;
|
||||
|
||||
NGRAPH_RTTI_DEFINITION(ngraph::op::v8::If, "If", 8, MultiSubGraphOp);
|
||||
|
||||
op::v8::If::If() : MultiSubGraphOp(2) {}
|
||||
|
||||
op::v8::If::If(const Output<Node>& execution_condition) : If() {
|
||||
set_argument(0, execution_condition);
|
||||
}
|
||||
|
||||
// This function tries to calculate the output shape of the if operation by two outputs from two
|
||||
// subgraphs.
|
||||
static ngraph::PartialShape resolve_shape(const ngraph::PartialShape& then_pshape,
|
||||
const ngraph::PartialShape& else_pshape) {
|
||||
// then_pshape - shape of output from then_body
|
||||
// else_pshape - shape of output from else_body
|
||||
auto then_rank = then_pshape.rank();
|
||||
auto else_rank = else_pshape.rank();
|
||||
|
||||
// if rangs of shapes are not equal or rang of one of them is dynamic function
|
||||
// return shape with dynamic rank
|
||||
if (then_rank.is_dynamic() || else_rank.is_dynamic() || then_rank.get_length() != else_rank.get_length()) {
|
||||
return ngraph::PartialShape::dynamic(ngraph::Rank::dynamic());
|
||||
}
|
||||
std::vector<Dimension> new_dims;
|
||||
|
||||
// If rangs are equal each dimesion of then_body output is union with each dimension of
|
||||
// else_body
|
||||
for (auto then_it = then_pshape.cbegin(), else_it = else_pshape.cbegin(); then_it != then_pshape.cend();
|
||||
then_it++, else_it++) {
|
||||
if ((*then_it).is_dynamic() || (*else_it).is_dynamic()) {
|
||||
new_dims.push_back(Dimension::dynamic());
|
||||
} else if (*then_it == *else_it) {
|
||||
new_dims.push_back(Dimension(*then_it));
|
||||
} else {
|
||||
auto dim_min = std::min((*then_it).get_min_length(), (*else_it).get_min_length());
|
||||
auto dim_max = std::max((*then_it).get_min_length(), (*else_it).get_min_length());
|
||||
new_dims.push_back(Dimension(dim_min, dim_max));
|
||||
}
|
||||
}
|
||||
|
||||
return PartialShape(new_dims);
|
||||
}
|
||||
|
||||
bool op::v8::If::visit_attributes(AttributeVisitor& visitor) {
|
||||
NGRAPH_OP_SCOPE(v8_If_visit_attributes);
|
||||
m_bodies[THEN_BODY_INDEX] = std::make_shared<ngraph::Function>(OutputVector{}, ParameterVector{}, "then_branch");
|
||||
m_bodies[ELSE_BODY_INDEX] = std::make_shared<ngraph::Function>(OutputVector{}, ParameterVector{}, "else_branch");
|
||||
visitor.on_attribute("then_body", m_bodies[THEN_BODY_INDEX]);
|
||||
visitor.on_attribute("else_body", m_bodies[ELSE_BODY_INDEX]);
|
||||
visitor.on_attribute("then_inputs", m_input_descriptions[THEN_BODY_INDEX]);
|
||||
visitor.on_attribute("else_inputs", m_input_descriptions[ELSE_BODY_INDEX]);
|
||||
visitor.on_attribute("then_outputs", m_output_descriptions[THEN_BODY_INDEX]);
|
||||
visitor.on_attribute("else_outputs", m_output_descriptions[ELSE_BODY_INDEX]);
|
||||
return true;
|
||||
}
|
||||
|
||||
void op::v8::If::validate_and_infer_type_body(
|
||||
const std::shared_ptr<Function>& body,
|
||||
const ngraph::op::util::MultiSubgraphInputDescriptionVector& input_descriptors) {
|
||||
for (const auto& input_description : input_descriptors) {
|
||||
auto index = input_description->m_input_index;
|
||||
|
||||
auto body_parameter = body->get_parameters().at(input_description->m_body_parameter_index);
|
||||
auto input_partial_shape = input_value(index).get_partial_shape();
|
||||
body_parameter->set_partial_shape(input_partial_shape);
|
||||
}
|
||||
body->validate_nodes_and_infer_types();
|
||||
}
|
||||
|
||||
void op::v8::If::validate_and_infer_types() {
|
||||
NGRAPH_OP_SCOPE(v8_If_validate_and_infer_types);
|
||||
|
||||
NODE_VALIDATION_CHECK(this, m_bodies.size() == 2, "If contains incorrect number of bodies:", m_bodies.size());
|
||||
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
m_input_descriptions.size() == 2,
|
||||
"If contains incorrect number of body input descriptions:",
|
||||
m_input_descriptions.size());
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
m_output_descriptions.size() == 2,
|
||||
"If contains incorrect number of body output descriptions:",
|
||||
m_output_descriptions.size());
|
||||
|
||||
const auto& if_condition = input_value(0);
|
||||
const auto& if_condition_rank = if_condition.get_partial_shape().rank();
|
||||
if (if_condition_rank.is_static()) {
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
if_condition_rank.compatible(1) || if_condition_rank.compatible(0),
|
||||
"Rank of If condition input must be equal to 0 or 1");
|
||||
}
|
||||
|
||||
// Trying to get cond as const value
|
||||
if (const auto& cond_value = get_constant_from_source(if_condition)) {
|
||||
// If cond is const shape and inference is run for one of bodies another body is skipped
|
||||
auto val = cond_value->cast_vector<bool>();
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
val.size() == 1,
|
||||
"The number of values in the If condition constant is greater than 1");
|
||||
|
||||
auto cond_index = val[0] ? THEN_BODY_INDEX : ELSE_BODY_INDEX;
|
||||
auto body = m_bodies[cond_index];
|
||||
auto input_descriptors = m_input_descriptions[cond_index];
|
||||
validate_and_infer_type_body(body, input_descriptors);
|
||||
auto output_nodes = outputs();
|
||||
|
||||
// shape and type inference for outputs from If operations
|
||||
for (const auto& output_descr : m_output_descriptions[cond_index]) {
|
||||
auto body_value = body->get_results().at(output_descr->m_body_value_index)->input_value(0);
|
||||
auto body_value_partial_shape = body_value.get_partial_shape();
|
||||
set_output_type(output_descr->m_output_index, body_value.get_element_type(), body_value_partial_shape);
|
||||
}
|
||||
} else // condition is non constant
|
||||
{
|
||||
// If cond is non const, shape and type inference is run for both bodies
|
||||
validate_and_infer_type_body(get_then_body(), m_input_descriptions[THEN_BODY_INDEX]);
|
||||
validate_and_infer_type_body(get_else_body(), m_input_descriptions[ELSE_BODY_INDEX]);
|
||||
auto output_nodes = outputs();
|
||||
|
||||
// Getting map<output_index_from_if, output_description>. This map guarantees that each
|
||||
// output from the body will be met in it once.
|
||||
auto then_outputs_map = get_mapping_outputs_on_body_description(m_output_descriptions[THEN_BODY_INDEX]);
|
||||
auto else_outputs_map = get_mapping_outputs_on_body_description(m_output_descriptions[ELSE_BODY_INDEX]);
|
||||
|
||||
// Checking each output from If. Each output must be associated with one output from each
|
||||
// body
|
||||
for (size_t output_index = 0; output_index < output_nodes.size(); ++output_index) {
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
then_outputs_map.count(output_index) != 0,
|
||||
"Incorrect associating in then_body! Output ",
|
||||
output_index,
|
||||
" is not associated with results in then_body!");
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
else_outputs_map.count(output_index) != 0,
|
||||
"Incorrect associating in else_body! Output ",
|
||||
output_index,
|
||||
" is not associated with results in else_body!");
|
||||
|
||||
auto then_desc = then_outputs_map.at(output_index);
|
||||
auto else_desc = else_outputs_map.at(output_index);
|
||||
|
||||
auto then_node_result =
|
||||
m_bodies[THEN_BODY_INDEX]->get_results().at(then_desc->m_body_value_index)->input_value(0);
|
||||
|
||||
auto else_node_result =
|
||||
m_bodies[ELSE_BODY_INDEX]->get_results().at(else_desc->m_body_value_index)->input_value(0);
|
||||
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
then_node_result.get_element_type() == else_node_result.get_element_type(),
|
||||
"type of then_body output is not equal type of else_body output");
|
||||
|
||||
// shape inference for output and associated with it body outputs
|
||||
auto partial_shape =
|
||||
resolve_shape(then_node_result.get_partial_shape(), else_node_result.get_partial_shape());
|
||||
set_output_type(output_index, then_node_result.get_element_type(), partial_shape);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> op::v8::If::clone_with_new_inputs(const OutputVector& new_args) const {
|
||||
NGRAPH_OP_SCOPE(v8_If_clone_with_new_inputs);
|
||||
|
||||
check_new_args_count(this, new_args);
|
||||
auto op = make_shared<op::v8::If>();
|
||||
NGRAPH_CHECK(op.get(), op != nullptr, "Cannot clone ", description(), " operation with name ", get_friendly_name());
|
||||
|
||||
op->set_arguments(new_args);
|
||||
op->set_output_size(m_output_descriptions[0].size());
|
||||
op->set_then_body(clone_function(*get_then_body()));
|
||||
op->set_else_body(clone_function(*get_else_body()));
|
||||
|
||||
for (auto body_index = 0; body_index < 2; ++body_index) {
|
||||
for (const auto& m_input_descr : m_input_descriptions[body_index]) {
|
||||
op->m_input_descriptions[body_index].push_back(m_input_descr->copy());
|
||||
}
|
||||
for (const auto& m_output_descr : m_output_descriptions[body_index]) {
|
||||
op->m_output_descriptions[body_index].push_back(m_output_descr->copy());
|
||||
}
|
||||
}
|
||||
op->validate_and_infer_types();
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
op::v8::If::OutputMap op::v8::If::get_mapping_outputs_on_body_description(
|
||||
const ngraph::op::util::MultiSubgraphOutputDescriptionVector& output_descriptors) {
|
||||
OutputMap outputs_map = OutputMap();
|
||||
std::unordered_set<int64_t> checked_results_in_body;
|
||||
|
||||
for (const auto& output_description : output_descriptors) {
|
||||
auto out_index = output_description->m_output_index;
|
||||
auto internal_result_index = output_description->m_body_value_index;
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
checked_results_in_body.count(internal_result_index) == 0,
|
||||
"Incorrect associating in then_body! Result ",
|
||||
internal_result_index,
|
||||
" is already associated with another output!");
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
outputs_map.count(out_index) == 0,
|
||||
"Incorrect associating in then_body! Several results try to "
|
||||
"associate with the same output!");
|
||||
checked_results_in_body.insert(internal_result_index);
|
||||
outputs_map.insert({out_index, output_description});
|
||||
}
|
||||
|
||||
return outputs_map;
|
||||
}
|
||||
|
||||
bool op::v8::If::evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const {
|
||||
NGRAPH_OP_SCOPE(v8_If_evaluate);
|
||||
runtime::reference::if_reference(m_bodies, m_output_descriptions, m_input_descriptions, outputs, inputs);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool op::v8::If::has_evaluate() const {
|
||||
NGRAPH_OP_SCOPE(v8_If_has_evaluate);
|
||||
return true;
|
||||
}
|
||||
|
||||
void op::v8::If::set_input(const Output<Node>& value,
|
||||
const std::shared_ptr<Parameter>& then_parameter,
|
||||
const std::shared_ptr<Parameter>& else_parameter) {
|
||||
NGRAPH_CHECK(then_parameter != nullptr || else_parameter != nullptr,
|
||||
"Missing parameters! Both parameters are nullptr!");
|
||||
auto then_param_index = m_bodies[THEN_BODY_INDEX]->get_parameter_index(then_parameter);
|
||||
auto else_param_index = m_bodies[ELSE_BODY_INDEX]->get_parameter_index(else_parameter);
|
||||
NGRAPH_CHECK(then_parameter == nullptr || then_param_index != -1,
|
||||
"Missing parameter ",
|
||||
then_parameter->get_friendly_name(),
|
||||
" for \'then_body\'!");
|
||||
NGRAPH_CHECK(else_parameter == nullptr || else_param_index != -1,
|
||||
"Missing parameter ",
|
||||
else_parameter->get_friendly_name(),
|
||||
" for \'else_body\'!");
|
||||
set_invariant_inputs(value, {then_parameter, else_parameter});
|
||||
}
|
||||
|
||||
Output<Node> op::v8::If::set_output(const std::shared_ptr<Result>& then_result,
|
||||
const std::shared_ptr<Result>& else_result) {
|
||||
NGRAPH_CHECK(then_result != nullptr, "Incorrect result in \"then_body\"! Result cant be \'nullptr\'");
|
||||
NGRAPH_CHECK(else_result != nullptr, "Incorrect result in \"else_body\"! Result cant be \'nullptr\'");
|
||||
auto then_result_id = m_bodies[THEN_BODY_INDEX]->get_result_index(then_result);
|
||||
auto else_result_id = m_bodies[ELSE_BODY_INDEX]->get_result_index(else_result);
|
||||
|
||||
NGRAPH_CHECK(then_result_id != -1, "Missing result ", then_result->get_friendly_name(), "in \'then_body\'!");
|
||||
NGRAPH_CHECK(else_result_id != -1, "Missing result ", else_result->get_friendly_name(), "in \'then_body\'!");
|
||||
|
||||
return set_body_outputs({then_result, else_result});
|
||||
}
|
||||
368
ngraph/test/op_eval/if_eval.cpp
Normal file
368
ngraph/test/op_eval/if_eval.cpp
Normal file
@@ -0,0 +1,368 @@
|
||||
// Copyright (C) 2018-2021 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include <ngraph/pass/constant_folding.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "ngraph/opsets/opset1.hpp"
|
||||
#include "ngraph/opsets/opset5.hpp"
|
||||
#include "ngraph/opsets/opset8.hpp"
|
||||
#include "ngraph/runtime/host_tensor.hpp"
|
||||
#include "ngraph/validation_util.hpp"
|
||||
#include "runtime/backend.hpp"
|
||||
#include "util/test_tools.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ngraph;
|
||||
|
||||
TEST(op_eval, if_condition_const) {
|
||||
auto X = make_shared<op::Parameter>(element::f32, Shape{1, 2, 2});
|
||||
auto Y = make_shared<op::Parameter>(element::f32, Shape{1, 2, 2});
|
||||
auto cond = std::make_shared<ngraph::opset5::Constant>(element::boolean, Shape{1}, true);
|
||||
auto cond2 = std::make_shared<ngraph::opset5::Constant>(element::boolean, Shape{1}, false);
|
||||
auto Xt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Yt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Xe = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Ye = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto then_op = std::make_shared<op::v1::Multiply>(Xt, Yt);
|
||||
auto res0 = make_shared<op::Result>(then_op);
|
||||
auto res1 = make_shared<op::Result>(Xe);
|
||||
auto then_body = make_shared<ngraph::Function>(OutputVector{res0}, ParameterVector{Xt, Yt});
|
||||
auto else_body = make_shared<ngraph::Function>(OutputVector{res1}, ParameterVector{Xe});
|
||||
auto if_op = make_shared<op::v8::If>(cond);
|
||||
if_op->set_then_body(then_body);
|
||||
if_op->set_else_body(else_body);
|
||||
if_op->set_input(X, Xt, Xe);
|
||||
if_op->set_input(Y, Yt, nullptr);
|
||||
if_op->set_output(res0, res1);
|
||||
if_op->validate_and_infer_types();
|
||||
auto if_op2 = if_op->clone_with_new_inputs(OutputVector{cond2, X, Y});
|
||||
std::vector<float> X_v{1.0, 1.0, 1.0, 1.0};
|
||||
std::vector<float> Y_v{2.0, 2.0, 2.0, 2.0};
|
||||
auto fun = make_shared<Function>(OutputVector{if_op}, ParameterVector{X, Y});
|
||||
auto fun2 = make_shared<Function>(OutputVector{if_op2}, ParameterVector{X, Y});
|
||||
auto result = make_shared<HostTensor>();
|
||||
ASSERT_TRUE(fun->evaluate({result},
|
||||
{make_host_tensor<element::Type_t::f32>(Shape{1, 2, 2}, X_v),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{1, 2, 2}, Y_v)}));
|
||||
EXPECT_EQ(result->get_element_type(), element::f32);
|
||||
EXPECT_EQ(result->get_shape(), Shape{std::vector<size_t>({1, 2, 2})});
|
||||
auto result_data = read_vector<float>(result);
|
||||
std::vector<float> expected_results{2.0, 2.0, 2.0, 2.0};
|
||||
for (auto i = 0; i < expected_results.size(); i++)
|
||||
EXPECT_NEAR(result_data[i], expected_results[i], 0.000001);
|
||||
|
||||
auto result1 = make_shared<HostTensor>();
|
||||
ASSERT_TRUE(fun2->evaluate({result1},
|
||||
{make_host_tensor<element::Type_t::f32>(Shape{1, 2, 2}, X_v),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{1, 2, 2}, Y_v)}));
|
||||
EXPECT_EQ(result1->get_element_type(), element::f32);
|
||||
EXPECT_EQ(result1->get_shape(), Shape{std::vector<size_t>({1, 2, 2})});
|
||||
auto result_data1 = read_vector<float>(result1);
|
||||
for (auto i = 0; i < expected_results.size(); i++)
|
||||
EXPECT_NEAR(result_data1[i], X_v[i], 0.000001);
|
||||
}
|
||||
|
||||
TEST(op_eval, if_condition_non_const) {
|
||||
auto X = make_shared<op::Parameter>(element::f32, Shape{1, 2, 2});
|
||||
auto Y = make_shared<op::Parameter>(element::f32, Shape{1, 2, 2});
|
||||
auto cond = make_shared<op::Parameter>(element::boolean, Shape{1});
|
||||
// Set up the cell body, a function from (Xi, Yi) -> (Zo)
|
||||
// Body parameters
|
||||
auto Xt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Yt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Xe = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Ye = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
// Body
|
||||
auto then_op = std::make_shared<op::v1::Multiply>(Xt, Yt);
|
||||
auto else_op = std::make_shared<op::v1::Add>(Xe, Ye);
|
||||
auto then_op_result = make_shared<op::Result>(then_op);
|
||||
auto else_op_result = make_shared<op::Result>(else_op);
|
||||
auto then_body = make_shared<ngraph::Function>(OutputVector{then_op_result}, ParameterVector{Xt, Yt});
|
||||
auto else_body = make_shared<ngraph::Function>(OutputVector{else_op_result}, ParameterVector{Xe, Ye});
|
||||
auto if_op = make_shared<op::v8::If>(cond);
|
||||
if_op->set_then_body(then_body);
|
||||
if_op->set_else_body(else_body);
|
||||
if_op->set_input(X, Xt, Xe);
|
||||
if_op->set_input(Y, Yt, Ye);
|
||||
if_op->set_output(then_op_result, else_op_result);
|
||||
if_op->validate_and_infer_types();
|
||||
std::vector<float> X_v{1.0, 2.0, 3.0, 4.0};
|
||||
std::vector<float> Y_v{2.0, 1.0, 2.0, 3.0};
|
||||
auto fun = make_shared<Function>(OutputVector{if_op}, ParameterVector{cond, X, Y});
|
||||
auto result = make_shared<HostTensor>();
|
||||
ASSERT_TRUE(fun->evaluate({result},
|
||||
{make_host_tensor<element::Type_t::boolean>(Shape{1}, {true}),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{1, 2, 2}, X_v),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{1, 2, 2}, Y_v)}));
|
||||
EXPECT_EQ(result->get_element_type(), element::f32);
|
||||
EXPECT_EQ(result->get_shape(), Shape{std::vector<size_t>({1, 2, 2})});
|
||||
auto result_data = read_vector<float>(result);
|
||||
std::vector<float> expected_results{2.0, 2.0, 6.0, 12.0};
|
||||
for (auto i = 0; i < expected_results.size(); i++)
|
||||
EXPECT_NEAR(result_data[i], expected_results[i], 0.000001);
|
||||
ASSERT_TRUE(fun->evaluate({result},
|
||||
{make_host_tensor<element::Type_t::boolean>(Shape{1}, {false}),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{1, 2, 2}, X_v),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{1, 2, 2}, Y_v)}));
|
||||
EXPECT_EQ(result->get_element_type(), element::f32);
|
||||
EXPECT_EQ(result->get_shape(), Shape{std::vector<size_t>({1, 2, 2})});
|
||||
result_data = read_vector<float>(result);
|
||||
expected_results = {3.0, 3.0, 5.0, 7.0};
|
||||
|
||||
for (auto i = 0; i < expected_results.size(); i++)
|
||||
EXPECT_NEAR(result_data[i], expected_results[i], 0.000001);
|
||||
}
|
||||
|
||||
TEST(op_eval, if_free_sample) {
|
||||
auto cond = make_shared<op::Parameter>(element::boolean, Shape{1});
|
||||
auto A = std::make_shared<ngraph::opset5::Constant>(element::f32, Shape{1}, 8.0);
|
||||
auto B = std::make_shared<ngraph::opset5::Constant>(element::f32, Shape{1}, 2.0);
|
||||
auto A_res = std::make_shared<op::Result>(A);
|
||||
auto B_res = std::make_shared<op::Result>(B);
|
||||
auto then_body = make_shared<ngraph::Function>(OutputVector{A_res}, ParameterVector{});
|
||||
auto else_body = make_shared<ngraph::Function>(OutputVector{B_res}, ParameterVector{});
|
||||
auto if_op = make_shared<op::v8::If>(cond);
|
||||
if_op->set_then_body(then_body);
|
||||
if_op->set_else_body(else_body);
|
||||
auto res = if_op->set_output(A_res, B_res);
|
||||
auto fun = make_shared<Function>(OutputVector{res}, ParameterVector{cond});
|
||||
fun->validate_nodes_and_infer_types();
|
||||
auto result1 = make_shared<HostTensor>(), result2 = make_shared<HostTensor>();
|
||||
ASSERT_TRUE(fun->evaluate({result1}, {make_host_tensor<element::Type_t::boolean>(Shape{1}, {true})}));
|
||||
ASSERT_TRUE(fun->evaluate({result2}, {make_host_tensor<element::Type_t::boolean>(Shape{1}, {false})}));
|
||||
auto result_data1 = read_vector<float>(result1);
|
||||
auto result_data2 = read_vector<float>(result2);
|
||||
EXPECT_EQ(result1->get_element_type(), element::f32);
|
||||
EXPECT_EQ(result1->get_shape(), Shape{std::vector<size_t>({1})});
|
||||
EXPECT_EQ(result2->get_element_type(), element::f32);
|
||||
EXPECT_EQ(result2->get_shape(), Shape{std::vector<size_t>({1})});
|
||||
EXPECT_NEAR(result_data1[0], 8.0, 0.000001);
|
||||
EXPECT_NEAR(result_data2[0], 2.0, 0.000001);
|
||||
}
|
||||
|
||||
TEST(op_eval, if_constant_folding) {
|
||||
auto cond = std::make_shared<ngraph::opset5::Constant>(element::boolean, Shape{1}, false);
|
||||
auto A1 = std::make_shared<ngraph::opset5::Constant>(element::f32, Shape{1}, 37.0);
|
||||
auto A2 = std::make_shared<ngraph::opset5::Constant>(element::f32, Shape{1}, 45.0);
|
||||
auto B1 = std::make_shared<ngraph::opset5::Constant>(element::f32, Shape{1}, 10.0);
|
||||
auto B2 = std::make_shared<ngraph::opset5::Constant>(element::f32, Shape{1}, 3.0);
|
||||
auto Xt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Yt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Xe = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Ye = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto a_add = std::make_shared<op::v1::Add>(Xt, Yt);
|
||||
auto b_pow = std::make_shared<op::v1::Power>(Xe, Ye);
|
||||
auto then_res = std::make_shared<op::Result>(a_add);
|
||||
auto then_body = make_shared<ngraph::Function>(OutputVector{then_res}, ParameterVector{Xt, Yt});
|
||||
auto else_res = std::make_shared<op::Result>(b_pow);
|
||||
auto else_body = make_shared<ngraph::Function>(OutputVector{else_res}, ParameterVector{Xe, Ye});
|
||||
auto if_op = make_shared<op::v8::If>(cond);
|
||||
if_op->set_then_body(then_body);
|
||||
if_op->set_else_body(else_body);
|
||||
if_op->set_input(A1, Xt, nullptr);
|
||||
if_op->set_input(A2, Yt, nullptr);
|
||||
if_op->set_input(B1, nullptr, Xe);
|
||||
if_op->set_input(B2, nullptr, Ye);
|
||||
if_op->set_output(then_res, else_res);
|
||||
|
||||
auto fun = make_shared<Function>(OutputVector{if_op}, ParameterVector{});
|
||||
fun->validate_nodes_and_infer_types();
|
||||
ngraph::pass::ConstantFolding().run_on_function(fun);
|
||||
auto results = fun->get_results();
|
||||
EXPECT_EQ(results.size(), 1);
|
||||
auto result = results[0];
|
||||
EXPECT_EQ(result->get_element_type(), element::f32);
|
||||
EXPECT_EQ(result->get_shape(), Shape{1});
|
||||
const auto& cond_value = get_constant_from_source(result);
|
||||
auto val = cond_value->cast_vector<float>();
|
||||
EXPECT_NEAR(val[0], 1000.0, 0.000001);
|
||||
}
|
||||
|
||||
TEST(op_eval, if_dynamism) {
|
||||
auto X = make_shared<op::Parameter>(element::f32, Shape{1, 2, 2});
|
||||
auto Y = make_shared<op::Parameter>(element::f32, Shape{4, 2, 2});
|
||||
auto Z = make_shared<op::Parameter>(element::f32, Shape{8, 8, 8});
|
||||
auto cond = make_shared<op::Parameter>(element::boolean, Shape{1});
|
||||
// Set up the cell body, a function from (Xi, Yi) -> (Zo)
|
||||
// Body parameters
|
||||
auto Xt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Yt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Xe = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Ze = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
// Body
|
||||
auto then_op = std::make_shared<op::v1::Multiply>(Xt, Xt);
|
||||
auto else_op = std::make_shared<op::v1::Add>(Xe, Xe);
|
||||
auto then_op_result1 = make_shared<op::Result>(then_op);
|
||||
auto then_op_result2 = make_shared<op::Result>(Yt);
|
||||
auto else_op_result1 = make_shared<op::Result>(else_op);
|
||||
auto else_op_result2 = make_shared<op::Result>(Ze);
|
||||
auto then_body =
|
||||
make_shared<ngraph::Function>(OutputVector{then_op_result1, then_op_result2}, ParameterVector{Xt, Yt});
|
||||
auto else_body =
|
||||
make_shared<ngraph::Function>(OutputVector{else_op_result1, else_op_result2}, ParameterVector{Xe, Ze});
|
||||
auto if_op = make_shared<op::v8::If>(cond);
|
||||
if_op->set_then_body(then_body);
|
||||
if_op->set_else_body(else_body);
|
||||
if_op->set_input(X, Xt, Xe);
|
||||
if_op->set_input(Y, Yt, nullptr);
|
||||
if_op->set_input(Z, nullptr, Ze);
|
||||
auto res1 = if_op->set_output(then_op_result1, else_op_result1);
|
||||
auto res2 = if_op->set_output(then_op_result2, else_op_result2);
|
||||
auto result_if1 = make_shared<op::Result>(res1);
|
||||
auto result_if2 = make_shared<op::Result>(res2);
|
||||
if_op->validate_and_infer_types();
|
||||
std::vector<float> X_v{1.0, 2.0, 3.0, 4.0};
|
||||
std::vector<float> Y_v, Z_v;
|
||||
for (auto c_ind = 0; c_ind < 4; ++c_ind) {
|
||||
for (auto d_ind = 0; d_ind < 4; ++d_ind) {
|
||||
Y_v.push_back(static_cast<float>(c_ind * d_ind));
|
||||
}
|
||||
}
|
||||
for (auto c_ind = 0; c_ind < 8; ++c_ind) {
|
||||
for (auto d_ind = 0; d_ind < 64; ++d_ind) {
|
||||
Z_v.push_back(static_cast<float>(c_ind * d_ind));
|
||||
}
|
||||
}
|
||||
auto fun = make_shared<Function>(OutputVector{result_if1, result_if2}, ParameterVector{cond, X, Y, Z});
|
||||
auto result1 = make_shared<HostTensor>();
|
||||
auto result2 = make_shared<HostTensor>();
|
||||
ASSERT_TRUE(fun->evaluate({result1, result2},
|
||||
{make_host_tensor<element::Type_t::boolean>(Shape{1}, {true}),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{1, 2, 2}, X_v),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{4, 2, 2}, Y_v),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{8, 8, 8}, Z_v)}));
|
||||
EXPECT_EQ(result1->get_element_type(), element::f32);
|
||||
EXPECT_EQ(result1->get_shape(), Shape{std::vector<size_t>({1, 2, 2})});
|
||||
auto result1_data = read_vector<float>(result1);
|
||||
std::vector<float> expected_results1{1.0, 4.0, 9.0, 16.0};
|
||||
for (auto i = 0; i < expected_results1.size(); i++)
|
||||
EXPECT_NEAR(result1_data[i], expected_results1[i], 0.000001);
|
||||
EXPECT_EQ(result2->get_element_type(), element::f32);
|
||||
EXPECT_EQ(result2->get_shape(), Shape{std::vector<size_t>({4, 2, 2})});
|
||||
auto result2_data = read_vector<float>(result2);
|
||||
for (auto i = 0; i < Y_v.size(); i++)
|
||||
EXPECT_NEAR(result2_data[i], Y_v[i], 0.000001);
|
||||
auto result3 = make_shared<HostTensor>();
|
||||
auto result4 = make_shared<HostTensor>();
|
||||
ASSERT_TRUE(fun->evaluate({result3, result4},
|
||||
{make_host_tensor<element::Type_t::boolean>(Shape{1}, {false}),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{1, 2, 2}, X_v),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{4, 2, 2}, Y_v),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{8, 8, 8}, Z_v)}));
|
||||
EXPECT_EQ(result3->get_element_type(), element::f32);
|
||||
EXPECT_EQ(result3->get_shape(), Shape{std::vector<size_t>({1, 2, 2})});
|
||||
auto result3_data = read_vector<float>(result3);
|
||||
std::vector<float> expected_results2{2.0, 4.0, 6.0, 8.0};
|
||||
for (auto i = 0; i < expected_results2.size(); i++)
|
||||
EXPECT_NEAR(result3_data[i], expected_results2[i], 0.000001);
|
||||
EXPECT_EQ(result4->get_element_type(), element::f32);
|
||||
EXPECT_EQ(result4->get_shape(), Shape{std::vector<size_t>({8, 8, 8})});
|
||||
auto result4_data = read_vector<float>(result4);
|
||||
for (auto i = 0; i < Z_v.size(); i++)
|
||||
EXPECT_NEAR(result4_data[i], Z_v[i], 0.000001);
|
||||
}
|
||||
|
||||
TEST(op_eval, if_condition_non_const_scalar) {
|
||||
auto X = make_shared<op::Parameter>(element::f32, Shape{1, 2, 2});
|
||||
auto Y = make_shared<op::Parameter>(element::f32, Shape{1, 2, 2});
|
||||
auto cond = make_shared<op::Parameter>(element::boolean, Shape{});
|
||||
// Set up the cell body, a function from (Xi, Yi) -> (Zo)
|
||||
// Body parameters
|
||||
auto Xt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Yt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Xe = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Ye = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
// Body
|
||||
auto then_op = std::make_shared<op::v1::Multiply>(Xt, Yt);
|
||||
auto else_op = std::make_shared<op::v1::Add>(Xe, Ye);
|
||||
auto then_op_result = make_shared<op::Result>(then_op);
|
||||
auto else_op_result = make_shared<op::Result>(else_op);
|
||||
auto then_body = make_shared<ngraph::Function>(OutputVector{then_op_result}, ParameterVector{Xt, Yt});
|
||||
auto else_body = make_shared<ngraph::Function>(OutputVector{else_op_result}, ParameterVector{Xe, Ye});
|
||||
auto if_op = make_shared<op::v8::If>(cond);
|
||||
if_op->set_then_body(then_body);
|
||||
if_op->set_else_body(else_body);
|
||||
if_op->set_input(X, Xt, Xe);
|
||||
if_op->set_input(Y, Yt, Ye);
|
||||
if_op->set_output(then_op_result, else_op_result);
|
||||
if_op->validate_and_infer_types();
|
||||
std::vector<float> X_v{1.0, 2.0, 3.0, 4.0};
|
||||
std::vector<float> Y_v{2.0, 1.0, 2.0, 3.0};
|
||||
auto fun = make_shared<Function>(OutputVector{if_op}, ParameterVector{cond, X, Y});
|
||||
auto result = make_shared<HostTensor>();
|
||||
ASSERT_TRUE(fun->evaluate({result},
|
||||
{make_host_tensor<element::Type_t::boolean>(Shape{1}, {true}),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{1, 2, 2}, X_v),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{1, 2, 2}, Y_v)}));
|
||||
EXPECT_EQ(result->get_element_type(), element::f32);
|
||||
EXPECT_EQ(result->get_shape(), Shape{std::vector<size_t>({1, 2, 2})});
|
||||
auto result_data = read_vector<float>(result);
|
||||
std::vector<float> expected_results{2.0, 2.0, 6.0, 12.0};
|
||||
for (auto i = 0; i < expected_results.size(); i++)
|
||||
EXPECT_NEAR(result_data[i], expected_results[i], 0.000001);
|
||||
ASSERT_TRUE(fun->evaluate({result},
|
||||
{make_host_tensor<element::Type_t::boolean>(Shape{1}, {false}),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{1, 2, 2}, X_v),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{1, 2, 2}, Y_v)}));
|
||||
EXPECT_EQ(result->get_element_type(), element::f32);
|
||||
EXPECT_EQ(result->get_shape(), Shape{std::vector<size_t>({1, 2, 2})});
|
||||
result_data = read_vector<float>(result);
|
||||
expected_results = {3.0, 3.0, 5.0, 7.0};
|
||||
|
||||
for (auto i = 0; i < expected_results.size(); i++)
|
||||
EXPECT_NEAR(result_data[i], expected_results[i], 0.000001);
|
||||
}
|
||||
TEST(op_eval, if_condition_is_dynamic) {
|
||||
auto X = make_shared<op::Parameter>(element::f32, Shape{1, 2, 2});
|
||||
auto Y = make_shared<op::Parameter>(element::f32, Shape{1, 2, 2});
|
||||
auto cond = make_shared<op::Parameter>(element::boolean, PartialShape{Dimension::dynamic()});
|
||||
// Set up the cell body, a function from (Xi, Yi) -> (Zo)
|
||||
// Body parameters
|
||||
auto Xt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Yt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Xe = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Ye = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
// Body
|
||||
auto then_op = std::make_shared<op::v1::Multiply>(Xt, Yt);
|
||||
auto else_op = std::make_shared<op::v1::Add>(Xe, Ye);
|
||||
auto then_op_result = make_shared<op::Result>(then_op);
|
||||
auto else_op_result = make_shared<op::Result>(else_op);
|
||||
auto then_body = make_shared<ngraph::Function>(OutputVector{then_op_result}, ParameterVector{Xt, Yt});
|
||||
auto else_body = make_shared<ngraph::Function>(OutputVector{else_op_result}, ParameterVector{Xe, Ye});
|
||||
auto if_op = make_shared<op::v8::If>(cond);
|
||||
if_op->set_then_body(then_body);
|
||||
if_op->set_else_body(else_body);
|
||||
if_op->set_input(X, Xt, Xe);
|
||||
if_op->set_input(Y, Yt, Ye);
|
||||
if_op->set_output(then_op_result, else_op_result);
|
||||
if_op->validate_and_infer_types();
|
||||
std::vector<float> X_v{1.0, 2.0, 3.0, 4.0};
|
||||
std::vector<float> Y_v{2.0, 1.0, 2.0, 3.0};
|
||||
auto fun = make_shared<Function>(OutputVector{if_op}, ParameterVector{cond, X, Y});
|
||||
auto result = make_shared<HostTensor>();
|
||||
ASSERT_TRUE(fun->evaluate({result},
|
||||
{make_host_tensor<element::Type_t::boolean>(Shape{1}, {true}),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{1, 2, 2}, X_v),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{1, 2, 2}, Y_v)}));
|
||||
EXPECT_EQ(result->get_element_type(), element::f32);
|
||||
EXPECT_EQ(result->get_shape(), Shape{std::vector<size_t>({1, 2, 2})});
|
||||
auto result_data = read_vector<float>(result);
|
||||
std::vector<float> expected_results{2.0, 2.0, 6.0, 12.0};
|
||||
for (auto i = 0; i < expected_results.size(); i++)
|
||||
EXPECT_NEAR(result_data[i], expected_results[i], 0.000001);
|
||||
ASSERT_TRUE(fun->evaluate({result},
|
||||
{make_host_tensor<element::Type_t::boolean>(Shape{1}, {false}),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{1, 2, 2}, X_v),
|
||||
make_host_tensor<element::Type_t::f32>(Shape{1, 2, 2}, Y_v)}));
|
||||
EXPECT_EQ(result->get_element_type(), element::f32);
|
||||
EXPECT_EQ(result->get_shape(), Shape{std::vector<size_t>({1, 2, 2})});
|
||||
result_data = read_vector<float>(result);
|
||||
expected_results = {3.0, 3.0, 5.0, 7.0};
|
||||
|
||||
for (auto i = 0; i < expected_results.size(); i++)
|
||||
EXPECT_NEAR(result_data[i], expected_results[i], 0.000001);
|
||||
}
|
||||
278
ngraph/test/type_prop/if.cpp
Normal file
278
ngraph/test/type_prop/if.cpp
Normal file
@@ -0,0 +1,278 @@
|
||||
// Copyright (C) 2018-2021 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "ngraph/builder/reshape.hpp"
|
||||
#include "ngraph/ngraph.hpp"
|
||||
#include "ngraph/opsets/opset5.hpp"
|
||||
#include "util/type_prop.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ngraph;
|
||||
|
||||
TEST(type_prop, if_simple_test) {
|
||||
// That which we iterate over
|
||||
auto X = make_shared<op::Parameter>(element::f32, Shape{32, 40, 10});
|
||||
auto Y = make_shared<op::Parameter>(element::f32, Shape{32, 40, 10});
|
||||
auto cond = std::make_shared<ngraph::opset5::Constant>(ngraph::element::boolean, ngraph::Shape{1}, true);
|
||||
|
||||
// Set up the cell body, a function from (Xi, Yi) -> (Zo)
|
||||
// Body parameters
|
||||
auto Xt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Yt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Xe = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Ye = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
// Body
|
||||
auto then_op = std::make_shared<op::v1::Add>(Xt, Yt);
|
||||
auto then_op_res = std::make_shared<op::Result>(then_op);
|
||||
|
||||
auto then_body = make_shared<ngraph::Function>(OutputVector{then_op_res}, ParameterVector{Xt, Yt});
|
||||
|
||||
auto else_op = std::make_shared<op::v1::Maximum>(Xe, Ye);
|
||||
auto else_op_res = std::make_shared<op::Result>(else_op);
|
||||
auto else_body = make_shared<ngraph::Function>(OutputVector{else_op_res}, ParameterVector{Xe, Ye});
|
||||
auto if_op = make_shared<op::v8::If>(cond);
|
||||
if_op->set_then_body(then_body);
|
||||
if_op->set_else_body(else_body);
|
||||
if_op->set_input(X, Xt, Xe);
|
||||
if_op->set_input(Y, Yt, Ye);
|
||||
auto res = if_op->set_output(then_op_res, else_op_res);
|
||||
if_op->validate_and_infer_types();
|
||||
|
||||
auto result0 = make_shared<op::Result>(res);
|
||||
Shape out0_shape{32, 40, 10};
|
||||
auto sh = result0->get_output_shape(0);
|
||||
EXPECT_EQ(sh, out0_shape);
|
||||
}
|
||||
|
||||
TEST(type_prop, if_non_const_condition_test) {
|
||||
// That which we iterate over
|
||||
auto X = make_shared<op::Parameter>(element::f32, Shape{32, 40, 10});
|
||||
auto Y = make_shared<op::Parameter>(element::f32, Shape{32, 40, 10});
|
||||
auto cond = make_shared<op::Parameter>(element::boolean, Shape{1});
|
||||
|
||||
// Set up the cell body, a function from (Xi, Yi) -> (Zo)
|
||||
// Body parameters
|
||||
auto Xt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Yt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Xe = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Ye = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
// Body
|
||||
auto then_op = std::make_shared<op::v1::Add>(Xt, Yt);
|
||||
auto then_body_res = make_shared<op::Result>(then_op);
|
||||
auto then_body = make_shared<ngraph::Function>(OutputVector{then_body_res}, ParameterVector{Xt, Yt});
|
||||
|
||||
auto else_op = std::make_shared<op::v1::Maximum>(Xe, Ye);
|
||||
auto else_body_res = make_shared<op::Result>(else_op);
|
||||
auto else_body = make_shared<ngraph::Function>(OutputVector{else_body_res}, ParameterVector{Xe, Ye});
|
||||
|
||||
auto if_op = make_shared<op::v8::If>(cond);
|
||||
if_op->set_then_body(then_body);
|
||||
if_op->set_else_body(else_body);
|
||||
if_op->set_input(X, Xt, Xe);
|
||||
if_op->set_input(Y, Yt, Ye);
|
||||
auto res = if_op->set_output(then_body_res, else_body_res);
|
||||
if_op->validate_and_infer_types();
|
||||
auto result0 = make_shared<op::Result>(res);
|
||||
Shape out0_shape{32, 40, 10};
|
||||
auto sh = result0->get_output_shape(0);
|
||||
EXPECT_EQ(sh, out0_shape);
|
||||
}
|
||||
|
||||
TEST(type_prop, if_clone_test) {
|
||||
auto X = make_shared<op::Parameter>(element::f32, Shape{32, 40, 10});
|
||||
auto Y = make_shared<op::Parameter>(element::f32, Shape{32, 40, 10});
|
||||
auto cond = make_shared<op::Parameter>(element::boolean, Shape{1});
|
||||
|
||||
// Set up the cell body, a function from (Xi, Yi) -> (Zo)
|
||||
// Body parameters
|
||||
auto Xt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Yt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Xe = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Ye = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Xnew = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Ynew = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
// Body
|
||||
auto then_op = std::make_shared<op::v1::Add>(Xt, Yt);
|
||||
auto then_body_res = make_shared<op::Result>(then_op);
|
||||
auto then_body = make_shared<ngraph::Function>(OutputVector{then_body_res}, ParameterVector{Xt, Yt});
|
||||
auto else_op = std::make_shared<op::v1::Maximum>(Xe, Ye);
|
||||
auto else_body_res = make_shared<op::Result>(else_op);
|
||||
auto else_body = make_shared<ngraph::Function>(OutputVector{else_body_res}, ParameterVector{Xe, Ye});
|
||||
|
||||
auto if_op = make_shared<op::v8::If>(cond);
|
||||
if_op->set_then_body(then_body);
|
||||
if_op->set_else_body(else_body);
|
||||
if_op->set_input(X, Xt, Xe);
|
||||
if_op->set_input(Y, Yt, Ye);
|
||||
auto res = if_op->set_output(then_body_res, else_body_res);
|
||||
|
||||
auto new_if = std::dynamic_pointer_cast<op::v8::If>(if_op->clone_with_new_inputs(OutputVector{cond, Xnew, Ynew}));
|
||||
EXPECT_EQ(true, true);
|
||||
}
|
||||
|
||||
TEST(type_prop, if_multiple_outputs) {
|
||||
auto X = make_shared<op::Parameter>(element::f32, Shape{32, 40, 10});
|
||||
auto Y = make_shared<op::Parameter>(element::f32, Shape{32, 40, 10});
|
||||
auto cond = make_shared<op::Parameter>(element::boolean, Shape{1});
|
||||
|
||||
// Set up the cell body, a function from (Xi, Yi) -> (Zo)
|
||||
// Body parameters
|
||||
auto Xt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Yt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Xe = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Ye = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Xnew = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Ynew = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
// Body
|
||||
auto then_op = std::make_shared<op::v1::Add>(Xt, Yt);
|
||||
auto then_body_res_1 = make_shared<op::Result>(then_op);
|
||||
auto then_body_res_2 = make_shared<op::Result>(Xt);
|
||||
auto then_body =
|
||||
make_shared<ngraph::Function>(OutputVector{then_body_res_1, then_body_res_2}, ParameterVector{Xt, Yt});
|
||||
auto else_op = std::make_shared<op::v1::Maximum>(Xe, Ye);
|
||||
auto else_const = std::make_shared<ngraph::opset5::Constant>(ngraph::element::f32,
|
||||
ngraph::Shape{1, 1, 1},
|
||||
std::vector<float>{0.5f});
|
||||
auto else_body_res_1 = make_shared<op::Result>(else_op);
|
||||
auto else_body_res_2 = make_shared<op::Result>(else_const);
|
||||
auto else_body =
|
||||
make_shared<ngraph::Function>(OutputVector{else_body_res_1, else_body_res_2}, ParameterVector{Xe, Ye});
|
||||
|
||||
auto if_op = make_shared<op::v8::If>(cond);
|
||||
if_op->set_then_body(then_body);
|
||||
if_op->set_else_body(else_body);
|
||||
if_op->set_input(X, Xt, Xe);
|
||||
if_op->set_input(Y, Yt, Ye);
|
||||
auto res1 = if_op->set_output(then_body_res_1, else_body_res_1);
|
||||
auto res2 = if_op->set_output(then_body_res_2, else_body_res_2);
|
||||
if_op->validate_and_infer_types();
|
||||
auto result1 = make_shared<op::Result>(res1);
|
||||
auto result2 = make_shared<op::Result>(res2);
|
||||
Shape out0_shape{32, 40, 10};
|
||||
auto sh = result1->get_output_shape(0);
|
||||
auto is_dynamic = result2->is_dynamic();
|
||||
EXPECT_EQ(out0_shape, sh);
|
||||
EXPECT_EQ(is_dynamic, true);
|
||||
}
|
||||
|
||||
TEST(type_prop, if_scalar_condition) {
|
||||
// That which we iterate over
|
||||
auto X = make_shared<op::Parameter>(element::f32, Shape{32, 40, 10});
|
||||
auto Y = make_shared<op::Parameter>(element::f32, Shape{32, 40, 10});
|
||||
auto cond = make_shared<op::Parameter>(element::boolean, Shape{});
|
||||
|
||||
// Set up the cell body, a function from (Xi, Yi) -> (Zo)
|
||||
// Body parameters
|
||||
auto Xt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Yt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Xe = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Ye = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
// Body
|
||||
auto then_op = std::make_shared<op::v1::Add>(Xt, Yt);
|
||||
auto then_body_res = make_shared<op::Result>(then_op);
|
||||
auto then_body = make_shared<ngraph::Function>(OutputVector{then_body_res}, ParameterVector{Xt, Yt});
|
||||
|
||||
auto else_op = std::make_shared<op::v1::Maximum>(Xe, Ye);
|
||||
auto else_body_res = make_shared<op::Result>(else_op);
|
||||
auto else_body = make_shared<ngraph::Function>(OutputVector{else_body_res}, ParameterVector{Xe, Ye});
|
||||
|
||||
auto if_op = make_shared<op::v8::If>(cond);
|
||||
if_op->set_then_body(then_body);
|
||||
if_op->set_else_body(else_body);
|
||||
if_op->set_input(X, Xt, Xe);
|
||||
if_op->set_input(Y, Yt, Ye);
|
||||
auto res = if_op->set_output(then_body_res, else_body_res);
|
||||
if_op->validate_and_infer_types();
|
||||
auto result0 = make_shared<op::Result>(res);
|
||||
Shape out0_shape{32, 40, 10};
|
||||
auto sh = result0->get_output_shape(0);
|
||||
EXPECT_EQ(sh, out0_shape);
|
||||
}
|
||||
|
||||
TEST(type_prop, if_dynamic_output) {
|
||||
// That which we iterate over
|
||||
auto X_shape = Shape{1, 20, 5, 30};
|
||||
auto Y_shape = Shape{18, 16, 14, 12};
|
||||
auto X = make_shared<op::Parameter>(element::f32, X_shape);
|
||||
auto Y = make_shared<op::Parameter>(element::f32, Y_shape);
|
||||
auto cond = make_shared<op::Parameter>(element::boolean, Shape{1});
|
||||
|
||||
// Set up the cell body, a function from (Xi, Yi) -> (Zo)
|
||||
// Body parameters
|
||||
auto Xt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Ye = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
// Body
|
||||
auto then_op = std::make_shared<op::v1::Add>(Xt, Xt);
|
||||
auto then_body_res = make_shared<op::Result>(then_op);
|
||||
auto then_body = make_shared<ngraph::Function>(OutputVector{then_body_res}, ParameterVector{Xt});
|
||||
|
||||
auto else_op = std::make_shared<op::v1::Maximum>(Ye, Ye);
|
||||
auto else_body_res = make_shared<op::Result>(else_op);
|
||||
auto else_body = make_shared<ngraph::Function>(OutputVector{else_body_res}, ParameterVector{Ye});
|
||||
|
||||
auto if_op = make_shared<op::v8::If>(cond);
|
||||
if_op->set_then_body(then_body);
|
||||
if_op->set_else_body(else_body);
|
||||
if_op->set_input(X, Xt, nullptr);
|
||||
if_op->set_input(Y, nullptr, Ye);
|
||||
auto res = if_op->set_output(then_body_res, else_body_res);
|
||||
if_op->validate_and_infer_types();
|
||||
auto result0 = make_shared<op::Result>(res);
|
||||
auto dynamic_shape = result0->get_output_partial_shape(0);
|
||||
|
||||
EXPECT_EQ(X_shape.size(), dynamic_shape.rank().get_length());
|
||||
for (auto shape_index = 0; shape_index < X_shape.size(); shape_index++) {
|
||||
auto x_shape_it = X_shape.begin();
|
||||
auto y_shape_it = Y_shape.begin();
|
||||
auto res_it = dynamic_shape.begin();
|
||||
EXPECT_EQ(std::max(*x_shape_it, *y_shape_it), (*res_it).get_max_length());
|
||||
EXPECT_EQ(std::min(*x_shape_it, *y_shape_it), (*res_it).get_min_length());
|
||||
x_shape_it++;
|
||||
y_shape_it++;
|
||||
res_it++;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(type_prop, if_dynamic_inputs) {
|
||||
// That which we iterate over
|
||||
auto X_shape = PartialShape{Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic()};
|
||||
auto Y_shape = PartialShape{Dimension::dynamic(), 20, 30};
|
||||
;
|
||||
auto X = make_shared<op::Parameter>(element::f32, X_shape);
|
||||
auto Y = make_shared<op::Parameter>(element::f32, Y_shape);
|
||||
auto cond = make_shared<op::Parameter>(element::boolean, Shape{1});
|
||||
|
||||
// Set up the cell body, a function from (Xi, Yi) -> (Zo)
|
||||
// Body parameters
|
||||
auto Xt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Yt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Xe = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
auto Ye = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
// Body
|
||||
auto then_op = std::make_shared<op::v1::Add>(Xt, Yt);
|
||||
auto then_body_res = make_shared<op::Result>(then_op);
|
||||
auto then_body = make_shared<ngraph::Function>(OutputVector{then_body_res}, ParameterVector{Xt, Yt});
|
||||
|
||||
auto else_op = std::make_shared<op::v1::Multiply>(Xe, Ye);
|
||||
auto else_body_res = make_shared<op::Result>(else_op);
|
||||
auto else_body = make_shared<ngraph::Function>(OutputVector{else_body_res}, ParameterVector{Xe, Ye});
|
||||
|
||||
auto if_op = make_shared<op::v8::If>(cond);
|
||||
if_op->set_then_body(then_body);
|
||||
if_op->set_else_body(else_body);
|
||||
if_op->set_input(X, Xt, Xe);
|
||||
if_op->set_input(Y, Yt, Ye);
|
||||
auto res = if_op->set_output(then_body_res, else_body_res);
|
||||
if_op->validate_and_infer_types();
|
||||
auto result0 = make_shared<op::Result>(res);
|
||||
auto dynamic_shape = result0->get_output_partial_shape(0);
|
||||
auto expected_result = PartialShape{Dimension::dynamic(), 20, 30};
|
||||
EXPECT_EQ(3, dynamic_shape.rank().get_length());
|
||||
for (auto dim_index = 0; dim_index < 3; dim_index++) {
|
||||
auto exp_res_it = expected_result.begin();
|
||||
auto res_it = dynamic_shape.begin();
|
||||
EXPECT_EQ(*exp_res_it, *res_it);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user