Commit bcaf32c4 authored by Adam Procter's avatar Adam Procter Committed by Scott Cyphers

ConstantFolding for Equal, Greater, GreaterEq, Less, LessEq, NotEqual (#3322)

* CF for And and Or

* CF support for comparison ops

* Fix predicate for binary elementwise; add unit tests for non-arithmetic binops

* Update CPU CF builders
parent c693cb7e
This diff is collapsed.
......@@ -470,6 +470,68 @@ namespace ngraph
BUILD_UNARY_ELEMWISE_CF_FUNCTOR(runtime::cpu::kernel::sqrt);
}
template <>
NodeExecutorTy Builder::BUILDER_CF_DECL(ngraph::op::Equal)
{
BUILD_BINARY_ELEMWISE_CF_FUNCTOR(runtime::cpu::kernel::equal);
}
template <>
NodeExecutorTy Builder::BUILDER_CF_DECL(ngraph::op::NotEqual)
{
BUILD_BINARY_ELEMWISE_CF_FUNCTOR(runtime::cpu::kernel::not_equal);
}
template <>
NodeExecutorTy Builder::BUILDER_CF_DECL(ngraph::op::Greater)
{
BUILD_BINARY_ELEMWISE_CF_FUNCTOR(runtime::cpu::kernel::greater);
}
template <>
NodeExecutorTy Builder::BUILDER_CF_DECL(ngraph::op::GreaterEq)
{
BUILD_BINARY_ELEMWISE_CF_FUNCTOR(runtime::cpu::kernel::greater_eq);
}
template <>
NodeExecutorTy Builder::BUILDER_CF_DECL(ngraph::op::Less)
{
BUILD_BINARY_ELEMWISE_CF_FUNCTOR(runtime::cpu::kernel::less);
}
template <>
NodeExecutorTy Builder::BUILDER_CF_DECL(ngraph::op::LessEq)
{
BUILD_BINARY_ELEMWISE_CF_FUNCTOR(runtime::cpu::kernel::less_eq);
}
template <>
NodeExecutorTy Builder::BUILDER_CF_DECL(ngraph::op::And)
{
auto element_count = shape_size(node->get_shape());
auto functor = [&, element_count](const std::vector<void*>& inputs,
std::vector<void*>& outputs) {
runtime::cpu::kernel::logical_and(
inputs[0], inputs[1], outputs[0], element_count, 0);
};
return functor;
}
template <>
NodeExecutorTy Builder::BUILDER_CF_DECL(ngraph::op::Or)
{
auto element_count = shape_size(node->get_shape());
auto functor = [&, element_count](const std::vector<void*>& inputs,
std::vector<void*>& outputs) {
runtime::cpu::kernel::logical_or(
inputs[0], inputs[1], outputs[0], element_count, 0);
};
return functor;
}
template <>
NodeExecutorTy Builder::BUILDER_CF_DECL(ngraph::op::Sign)
{
......@@ -542,6 +604,14 @@ namespace ngraph
REGISTER_CF_BUILDER(Negative);
REGISTER_CF_BUILDER(Relu);
REGISTER_CF_BUILDER(Sqrt);
REGISTER_CF_BUILDER(Equal);
REGISTER_CF_BUILDER(NotEqual);
REGISTER_CF_BUILDER(Greater);
REGISTER_CF_BUILDER(GreaterEq);
REGISTER_CF_BUILDER(Less);
REGISTER_CF_BUILDER(LessEq);
REGISTER_CF_BUILDER(And);
REGISTER_CF_BUILDER(Or);
REGISTER_CF_BUILDER(Sign);
}
}
......
......@@ -67,9 +67,12 @@ namespace ngraph
}
}
// In English: return type is void and T must be a floating point type.
// In English: return type is void and T must be a standard floating point type, or
// bfloat16, or float16.
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value>::type
typename std::enable_if<std::is_floating_point<T>::value ||
std::is_same<T, bfloat16>::value ||
std::is_same<T, float16>::value>::type
divide(const T* arg0, const T* arg1, T* out, size_t count, bool pythondiv)
{
(void)pythondiv;
......
......@@ -459,6 +459,214 @@ TEST(constant_folding, const_concat)
ASSERT_EQ(values_expected, values_out);
}
TEST(constant_folding, const_equal)
{
auto constant0 =
op::Constant::create(element::i32, Shape{2, 3}, vector<int32_t>{1, 2, 3, 4, 5, 6});
auto constant1 =
op::Constant::create(element::i32, Shape{2, 3}, vector<int32_t>{1, 2, 2, 3, 5, 6});
auto eq = make_shared<op::Equal>(constant0, constant1);
auto f = make_shared<Function>(eq, ParameterVector{});
pass::Manager pass_manager;
pass_manager.register_pass<pass::ConstantFolding>();
pass_manager.run_passes(f);
ASSERT_EQ(count_ops_of_type<op::Equal>(f), 0);
ASSERT_EQ(count_ops_of_type<op::Constant>(f), 1);
auto new_const =
std::dynamic_pointer_cast<op::Constant>(f->get_results().at(0)->get_argument(0));
ASSERT_TRUE(new_const);
auto values_out = new_const->get_vector<char>();
vector<char> values_expected{1, 1, 0, 0, 1, 1};
ASSERT_EQ(values_expected, values_out);
}
TEST(constant_folding, const_not_equal)
{
auto constant0 =
op::Constant::create(element::i32, Shape{2, 3}, vector<int32_t>{1, 2, 3, 4, 5, 6});
auto constant1 =
op::Constant::create(element::i32, Shape{2, 3}, vector<int32_t>{1, 2, 2, 3, 5, 6});
auto eq = make_shared<op::NotEqual>(constant0, constant1);
auto f = make_shared<Function>(eq, ParameterVector{});
pass::Manager pass_manager;
pass_manager.register_pass<pass::ConstantFolding>();
pass_manager.run_passes(f);
ASSERT_EQ(count_ops_of_type<op::NotEqual>(f), 0);
ASSERT_EQ(count_ops_of_type<op::Constant>(f), 1);
auto new_const =
std::dynamic_pointer_cast<op::Constant>(f->get_results().at(0)->get_argument(0));
ASSERT_TRUE(new_const);
auto values_out = new_const->get_vector<char>();
vector<char> values_expected{0, 0, 1, 1, 0, 0};
ASSERT_EQ(values_expected, values_out);
}
TEST(constant_folding, const_greater)
{
auto constant0 =
op::Constant::create(element::i32, Shape{2, 3}, vector<int32_t>{1, 2, 3, 4, 5, 6});
auto constant1 =
op::Constant::create(element::i32, Shape{2, 3}, vector<int32_t>{2, 2, 2, 5, 5, 5});
auto eq = make_shared<op::Greater>(constant0, constant1);
auto f = make_shared<Function>(eq, ParameterVector{});
pass::Manager pass_manager;
pass_manager.register_pass<pass::ConstantFolding>();
pass_manager.run_passes(f);
ASSERT_EQ(count_ops_of_type<op::Greater>(f), 0);
ASSERT_EQ(count_ops_of_type<op::Constant>(f), 1);
auto new_const =
std::dynamic_pointer_cast<op::Constant>(f->get_results().at(0)->get_argument(0));
ASSERT_TRUE(new_const);
auto values_out = new_const->get_vector<char>();
vector<char> values_expected{0, 0, 1, 0, 0, 1};
ASSERT_EQ(values_expected, values_out);
}
TEST(constant_folding, const_greater_eq)
{
auto constant0 =
op::Constant::create(element::i32, Shape{2, 3}, vector<int32_t>{1, 2, 3, 4, 5, 6});
auto constant1 =
op::Constant::create(element::i32, Shape{2, 3}, vector<int32_t>{2, 2, 2, 5, 5, 5});
auto eq = make_shared<op::GreaterEq>(constant0, constant1);
auto f = make_shared<Function>(eq, ParameterVector{});
pass::Manager pass_manager;
pass_manager.register_pass<pass::ConstantFolding>();
pass_manager.run_passes(f);
ASSERT_EQ(count_ops_of_type<op::GreaterEq>(f), 0);
ASSERT_EQ(count_ops_of_type<op::Constant>(f), 1);
auto new_const =
std::dynamic_pointer_cast<op::Constant>(f->get_results().at(0)->get_argument(0));
ASSERT_TRUE(new_const);
auto values_out = new_const->get_vector<char>();
vector<char> values_expected{0, 1, 1, 0, 1, 1};
ASSERT_EQ(values_expected, values_out);
}
TEST(constant_folding, const_less)
{
auto constant0 =
op::Constant::create(element::i32, Shape{2, 3}, vector<int32_t>{1, 2, 3, 4, 5, 6});
auto constant1 =
op::Constant::create(element::i32, Shape{2, 3}, vector<int32_t>{2, 2, 2, 5, 5, 5});
auto eq = make_shared<op::Less>(constant0, constant1);
auto f = make_shared<Function>(eq, ParameterVector{});
pass::Manager pass_manager;
pass_manager.register_pass<pass::ConstantFolding>();
pass_manager.run_passes(f);
ASSERT_EQ(count_ops_of_type<op::Less>(f), 0);
ASSERT_EQ(count_ops_of_type<op::Constant>(f), 1);
auto new_const =
std::dynamic_pointer_cast<op::Constant>(f->get_results().at(0)->get_argument(0));
ASSERT_TRUE(new_const);
auto values_out = new_const->get_vector<char>();
vector<char> values_expected{1, 0, 0, 1, 0, 0};
ASSERT_EQ(values_expected, values_out);
}
TEST(constant_folding, const_less_eq)
{
auto constant0 =
op::Constant::create(element::i32, Shape{2, 3}, vector<int32_t>{1, 2, 3, 4, 5, 6});
auto constant1 =
op::Constant::create(element::i32, Shape{2, 3}, vector<int32_t>{2, 2, 2, 5, 5, 5});
auto eq = make_shared<op::LessEq>(constant0, constant1);
auto f = make_shared<Function>(eq, ParameterVector{});
pass::Manager pass_manager;
pass_manager.register_pass<pass::ConstantFolding>();
pass_manager.run_passes(f);
ASSERT_EQ(count_ops_of_type<op::LessEq>(f), 0);
ASSERT_EQ(count_ops_of_type<op::Constant>(f), 1);
auto new_const =
std::dynamic_pointer_cast<op::Constant>(f->get_results().at(0)->get_argument(0));
ASSERT_TRUE(new_const);
auto values_out = new_const->get_vector<char>();
vector<char> values_expected{1, 1, 0, 1, 1, 0};
ASSERT_EQ(values_expected, values_out);
}
TEST(constant_folding, const_and)
{
auto constant0 =
op::Constant::create(element::boolean, Shape{2, 3}, vector<int32_t>{0, 0, 1, 0, 1, 1});
auto constant1 =
op::Constant::create(element::boolean, Shape{2, 3}, vector<int32_t>{0, 1, 1, 1, 0, 1});
auto eq = make_shared<op::And>(constant0, constant1);
auto f = make_shared<Function>(eq, ParameterVector{});
pass::Manager pass_manager;
pass_manager.register_pass<pass::ConstantFolding>();
pass_manager.run_passes(f);
ASSERT_EQ(count_ops_of_type<op::And>(f), 0);
ASSERT_EQ(count_ops_of_type<op::Constant>(f), 1);
auto new_const =
std::dynamic_pointer_cast<op::Constant>(f->get_results().at(0)->get_argument(0));
ASSERT_TRUE(new_const);
auto values_out = new_const->get_vector<char>();
vector<char> values_expected{0, 0, 1, 0, 0, 1};
ASSERT_EQ(values_expected, values_out);
}
TEST(constant_folding, const_or)
{
auto constant0 =
op::Constant::create(element::boolean, Shape{2, 3}, vector<int32_t>{0, 0, 1, 0, 1, 1});
auto constant1 =
op::Constant::create(element::boolean, Shape{2, 3}, vector<int32_t>{0, 1, 1, 1, 0, 1});
auto eq = make_shared<op::Or>(constant0, constant1);
auto f = make_shared<Function>(eq, ParameterVector{});
pass::Manager pass_manager;
pass_manager.register_pass<pass::ConstantFolding>();
pass_manager.run_passes(f);
ASSERT_EQ(count_ops_of_type<op::Or>(f), 0);
ASSERT_EQ(count_ops_of_type<op::Constant>(f), 1);
auto new_const =
std::dynamic_pointer_cast<op::Constant>(f->get_results().at(0)->get_argument(0));
ASSERT_TRUE(new_const);
auto values_out = new_const->get_vector<char>();
vector<char> values_expected{0, 1, 1, 1, 1, 1};
ASSERT_EQ(values_expected, values_out);
}
TEST(constant_folding, pass_property)
{
auto pass = std::make_shared<ngraph::pass::ConstantFolding>();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment