[Ref][Core][Opset13] Add BitwiseNot operation (#19956)

* [Ref][Core][Opset13] Add bitwise_not operation

* Fix CI issues + add missing test

* improve test

* formatting

* Requested changes

* Remove unused include

* Add requested changes

* Try to fix test problems

* Fix CI

* Fix type validation

* Add checks in template eval
This commit is contained in:
Mateusz Mikolajczyk 2023-09-27 08:17:53 +02:00 committed by GitHub
parent 4ea370c172
commit f5fe664fb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 475 additions and 1 deletions

View File

@ -0,0 +1,30 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include "openvino/op/op.hpp"
namespace ov {
namespace op {
namespace v13 {
/// \brief Elementwise bitwise negation operation.
/// \ingroup ov_ops_cpp_api
class OPENVINO_API BitwiseNot : public op::Op {
public:
OPENVINO_OP("BitwiseNot", "opset13", op::Op);
/// \brief Constructs a bitwise negation operation.
BitwiseNot() = default;
/// \brief Constructs a bitwise negation operation.
///
/// \param arg Node that produces the input tensor.
BitwiseNot(const Output<Node>& arg);
void validate_and_infer_types() override;
std::shared_ptr<Node> clone_with_new_inputs(const OutputVector& new_args) const override;
};
} // namespace v13
} // namespace op
} // namespace ov

View File

@ -21,6 +21,7 @@
#include "openvino/op/batch_norm.hpp"
#include "openvino/op/batch_to_space.hpp"
#include "openvino/op/binary_convolution.hpp"
#include "openvino/op/bitwise_not.hpp"
#include "openvino/op/broadcast.hpp"
#include "openvino/op/bucketize.hpp"
#include "openvino/op/ceiling.hpp"

View File

@ -209,3 +209,4 @@ _OPENVINO_OP_REG(Pad, ov::op::v12)
_OPENVINO_OP_REG(ScatterElementsUpdate, ov::op::v12)
// New operations added in opset13
_OPENVINO_OP_REG(BitwiseNot, ov::op::v13)

View File

@ -0,0 +1,36 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include <algorithm>
#include <cstddef>
namespace ov {
namespace reference {
namespace func {
// Check for char datatype used by ov::element::boolean
template <class T, typename std::enable_if<std::is_same<typename std::decay<T>::type, char>::value>::type* = nullptr>
T bitwise_not(const T in) {
return static_cast<T>(!in);
}
template <class T, typename std::enable_if<!std::is_same<typename std::decay<T>::type, char>::value>::type* = nullptr>
T bitwise_not(const T in) {
return static_cast<T>(~in);
}
} // namespace func
/**
* @brief Reference implementation of BitwiseNot operator.
*
* @param in Input pointer to data.
* @param out Output pointer to results.
* @param count Number of elements in input buffer.
*/
template <class T>
void bitwise_not(const T* in, T* out, size_t count) {
std::transform(in, std::next(in, count), out, &func::bitwise_not<T>);
}
} // namespace reference
} // namespace ov

View File

@ -0,0 +1,33 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "openvino/op/bitwise_not.hpp"
#include "itt.hpp"
#include "openvino/core/validation_util.hpp"
#include "openvino/op/op.hpp"
namespace ov {
namespace op {
namespace v13 {
BitwiseNot::BitwiseNot(const Output<Node>& arg) : op::Op({arg}) {
constructor_validate_and_infer_types();
}
void BitwiseNot::validate_and_infer_types() {
OV_OP_SCOPE(v13_BitwiseNot_validate_and_infer_types);
const auto& element_type = get_input_element_type(0);
NODE_VALIDATION_CHECK(this,
element_type.is_dynamic() || element_type.is_integral(),
"The element type of the input tensor must be integer or boolean.");
set_output_type(0, element_type, get_input_partial_shape(0));
}
std::shared_ptr<Node> BitwiseNot::clone_with_new_inputs(const OutputVector& new_args) const {
OV_OP_SCOPE(v13_BitwiseNot_clone_with_new_inputs);
check_new_args_count(this, new_args);
return std::make_shared<BitwiseNot>(new_args.at(0));
}
} // namespace v13
} // namespace op
} // namespace ov

View File

@ -26,6 +26,7 @@ _OPENVINO_OP_REG(AvgPool, ov::op::v1)
_OPENVINO_OP_REG(BatchNormInference, ov::op::v0)
_OPENVINO_OP_REG(BatchToSpace, ov::op::v1)
_OPENVINO_OP_REG(BinaryConvolution, ov::op::v1)
_OPENVINO_OP_REG(BitwiseNot, ov::op::v13)
_OPENVINO_OP_REG(Broadcast, ov::op::v1)
_OPENVINO_OP_REG(Broadcast, ov::op::v3)
_OPENVINO_OP_REG(Bucketize, ov::op::v3)

View File

@ -71,7 +71,7 @@ INSTANTIATE_TEST_SUITE_P(opset,
OpsetTestParams{ov::get_opset10, 177},
OpsetTestParams{ov::get_opset11, 177},
OpsetTestParams{ov::get_opset12, 178},
OpsetTestParams{ov::get_opset13, 178}),
OpsetTestParams{ov::get_opset13, 179}),
OpsetTestNameGenerator{});
class MyOpOld : public ov::op::Op {

View File

@ -0,0 +1,85 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "openvino/op/bitwise_not.hpp"
#include <gtest/gtest.h>
#include "common_test_utils/test_assertions.hpp"
#include "common_test_utils/type_prop.hpp"
using namespace ov;
using namespace testing;
using BitwiseNotTestParam = std::tuple<element::Type, PartialShape>;
namespace {
using namespace ov::element;
constexpr size_t exp_num_of_outputs = 1;
const auto types = Values(boolean, i8, i16, i32, i64, u8, u16, u32, u64);
const auto static_shapes = Values(PartialShape{0}, PartialShape{1}, PartialShape{2, 3, 7, 8});
const auto dynamic_shapes =
Values(PartialShape::dynamic(3), PartialShape{2, {0, 5}, {4, -1}, -1, {3, 8}}, PartialShape::dynamic());
} // namespace
class BitwiseNotTest : public TypePropOpTest<ov::op::v13::BitwiseNot>, public WithParamInterface<BitwiseNotTestParam> {
protected:
void SetUp() override {
std::tie(exp_type, exp_shape) = GetParam();
}
element::Type exp_type;
PartialShape exp_shape;
};
INSTANTIATE_TEST_SUITE_P(type_prop_static_shape,
BitwiseNotTest,
Combine(types, static_shapes),
PrintToStringParamName());
INSTANTIATE_TEST_SUITE_P(type_prop_dynamic_shape,
BitwiseNotTest,
Combine(types, dynamic_shapes),
PrintToStringParamName());
TEST_P(BitwiseNotTest, propagate_dimensions) {
const auto input = std::make_shared<ov::op::v0::Parameter>(exp_type, exp_shape);
const auto op = make_op(input);
EXPECT_EQ(op->get_element_type(), exp_type);
EXPECT_EQ(op->get_output_size(), exp_num_of_outputs);
EXPECT_EQ(op->get_output_partial_shape(0), exp_shape);
}
TEST_P(BitwiseNotTest, propagate_labels) {
if (exp_shape.rank().is_static()) {
set_shape_labels(exp_shape, 10);
}
const auto exp_labels = get_shape_labels(exp_shape);
const auto input = std::make_shared<ov::op::v0::Parameter>(exp_type, exp_shape);
const auto op = make_op(input);
EXPECT_EQ(get_shape_labels(op->get_output_partial_shape(0)), exp_labels);
}
TEST_P(BitwiseNotTest, default_ctor) {
const auto op = make_op();
const auto input = std::make_shared<ov::op::v0::Parameter>(exp_type, exp_shape);
op->set_argument(0, input);
op->validate_and_infer_types();
EXPECT_EQ(op->get_element_type(), exp_type);
EXPECT_EQ(op->get_output_size(), exp_num_of_outputs);
EXPECT_EQ(op->get_output_partial_shape(0), exp_shape);
}
TEST(BitwiseNotTest, invalid_element_type) {
auto data = std::make_shared<ov::op::v0::Parameter>(ov::element::f32, ov::Shape{2, 2});
OV_EXPECT_THROW(std::ignore = std::make_shared<ov::op::v13::BitwiseNot>(data),
ov::NodeValidationFailure,
HasSubstr("The element type of the input tensor must be integer or boolean."));
}

View File

@ -0,0 +1,11 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "openvino/op/bitwise_not.hpp"
#include "unary_ops.hpp"
using Type = ::testing::Types<UnaryOperatorType<ov::op::v13::BitwiseNot, ov::element::i32>>;
INSTANTIATE_TYPED_TEST_SUITE_P(visitor_without_attribute, UnaryOperatorVisitor, Type, UnaryOperatorTypeName);

View File

@ -0,0 +1,52 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "openvino/op/bitwise_not.hpp"
#include "evaluate_node.hpp"
#include "openvino/reference/bitwise_not.hpp"
#include "utils.hpp"
using namespace ov;
template <element::Type_t ET>
bool evaluate(const std::shared_ptr<ov::op::v13::BitwiseNot>& node,
ov::TensorVector& outputs,
const ov::TensorVector& inputs) {
OPENVINO_ASSERT(inputs.size() == 1);
OPENVINO_ASSERT(outputs.size() == 1);
outputs[0].set_shape(inputs[0].get_shape());
using T = typename ov::element_type_traits<ET>::value_type;
ov::reference::bitwise_not(inputs[0].data<T>(), outputs[0].data<T>(), shape_size(inputs[0].get_shape()));
return true;
}
template <>
bool evaluate_node<op::v13::BitwiseNot>(std::shared_ptr<ov::Node> node,
ov::TensorVector& outputs,
const ov::TensorVector& inputs) {
switch (node->get_input_element_type(0)) {
case element::boolean:
return evaluate<element::boolean>(as_type_ptr<op::v13::BitwiseNot>(node), outputs, inputs);
case element::u8:
return evaluate<element::u8>(as_type_ptr<op::v13::BitwiseNot>(node), outputs, inputs);
case element::i8:
return evaluate<element::i8>(as_type_ptr<op::v13::BitwiseNot>(node), outputs, inputs);
case element::u16:
return evaluate<element::u16>(as_type_ptr<op::v13::BitwiseNot>(node), outputs, inputs);
case element::i16:
return evaluate<element::i16>(as_type_ptr<op::v13::BitwiseNot>(node), outputs, inputs);
case element::u32:
return evaluate<element::u32>(as_type_ptr<op::v13::BitwiseNot>(node), outputs, inputs);
case element::i32:
return evaluate<element::i32>(as_type_ptr<op::v13::BitwiseNot>(node), outputs, inputs);
case element::u64:
return evaluate<element::u64>(as_type_ptr<op::v13::BitwiseNot>(node), outputs, inputs);
case element::i64:
return evaluate<element::i64>(as_type_ptr<op::v13::BitwiseNot>(node), outputs, inputs);
default:
OPENVINO_THROW("Unhandled data type ", node->get_element_type().get_type_name(), "in evaluate_node()");
}
}

View File

@ -445,6 +445,10 @@ extern template bool evaluate_node<ov::op::v12::GroupNormalization>(std::shared_
ov::TensorVector& outputs,
const ov::TensorVector& inputs);
extern template bool evaluate_node<ov::op::v13::BitwiseNot>(std::shared_ptr<ov::Node> node,
ov::TensorVector& outputs,
const ov::TensorVector& inputs);
extern template bool evaluate_node<ov::op::internal::AUGRUCell>(std::shared_ptr<ov::Node> node,
ov::TensorVector& outputs,
const ov::TensorVector& inputs);

View File

@ -150,5 +150,7 @@ _OPENVINO_OP_REG(Interpolate, op::v11)
_OPENVINO_OP_REG(GroupNormalization, ov::op::v12)
_OPENVINO_OP_REG(BitwiseNot, ov::op::v13)
_OPENVINO_OP_REG(AUGRUCell, ov::op::internal)
_OPENVINO_OP_REG(AUGRUSequence, ov::op::internal)

View File

@ -194,6 +194,7 @@ ov::SupportedOpsMap ov::template_plugin::Plugin::query_model(const std::shared_p
#include "openvino/opsets/opset10_tbl.hpp"
#include "openvino/opsets/opset11_tbl.hpp"
#include "openvino/opsets/opset12_tbl.hpp"
#include "openvino/opsets/opset13_tbl.hpp"
// clang-format on
#undef _OPENVINO_OP_REG
return op_super_set.contains_type(node->get_type_info());

View File

@ -0,0 +1,17 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "bitwise.hpp"
namespace reference_tests {
namespace BitwiseOpsRefTestDefinitions {
namespace {
TEST_P(ReferenceBitwiseLayerTest, BitwiseWithHardcodedRefs) {
Exec();
}
} // namespace
} // namespace BitwiseOpsRefTestDefinitions
} // namespace reference_tests

View File

@ -0,0 +1,74 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <gtest/gtest.h>
#include "base_reference_test.hpp"
#include "openvino/op/bitwise_not.hpp"
using namespace ov;
namespace reference_tests {
namespace BitwiseOpsRefTestDefinitions {
enum BitwiseTypes { BITWISE_NOT };
struct RefBitwiseParams {
BitwiseTypes opType;
std::vector<reference_tests::Tensor> inputs;
reference_tests::Tensor expected;
};
struct Builder : ParamsBuilder<RefBitwiseParams> {
REFERENCE_TESTS_ADD_SET_PARAM(Builder, opType);
REFERENCE_TESTS_ADD_SET_PARAM(Builder, inputs);
REFERENCE_TESTS_ADD_SET_PARAM(Builder, expected);
};
class ReferenceBitwiseLayerTest : public testing::TestWithParam<RefBitwiseParams>, public CommonReferenceTest {
public:
void SetUp() override {
const auto& params = GetParam();
function = CreateFunction(params.opType, params.inputs);
for (auto& input : params.inputs) {
inputData.push_back(input.data);
}
refOutData = {params.expected.data};
}
static std::string getTestCaseName(const testing::TestParamInfo<RefBitwiseParams>& obj) {
const auto& param = obj.param;
std::ostringstream result;
result << "BitwiseType=" << param.opType << "_";
for (size_t i = 0; i < param.inputs.size(); i++) {
const auto input = param.inputs[i];
result << "inpt_shape" << i << "=" << input.shape << "_";
result << "inpt_type" << i << "=" << input.type << "_";
}
result << "oType=" << param.expected.type;
return result.str();
}
private:
static std::shared_ptr<ov::Model> CreateFunction(BitwiseTypes op_type,
const std::vector<reference_tests::Tensor>& inputs) {
ov::ParameterVector params_vec;
for (auto& input : inputs) {
params_vec.push_back(std::make_shared<op::v0::Parameter>(input.type, input.shape));
}
std::shared_ptr<ov::Node> bitwise_op;
switch (op_type) {
case BitwiseTypes::BITWISE_NOT: {
bitwise_op = std::make_shared<ov::op::v13::BitwiseNot>(params_vec[0]);
break;
}
default: {
throw std::runtime_error("Incorrect type of Bitwise operation");
}
}
return std::make_shared<ov::Model>(ov::NodeVector{bitwise_op}, ov::ParameterVector{params_vec});
}
};
} // namespace BitwiseOpsRefTestDefinitions
} // namespace reference_tests

View File

@ -0,0 +1,119 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "openvino/op/bitwise_not.hpp"
#include <gtest/gtest.h>
#include "bitwise.hpp"
using namespace ov;
namespace reference_tests {
namespace BitwiseOpsRefTestDefinitions {
namespace {
std::vector<RefBitwiseParams> generateBitwiseParams() {
std::vector<RefBitwiseParams> bitwiseParams{
Builder{}
.opType(BitwiseTypes::BITWISE_NOT)
.inputs({{{2, 2}, element::boolean, std::vector<char>{true, false, true, false}}})
.expected({{2, 2}, element::boolean, std::vector<char>{false, true, false, true}}),
Builder{}
.opType(BitwiseTypes::BITWISE_NOT)
.inputs({{{3},
element::i8,
std::vector<int8_t>{std::numeric_limits<int8_t>::max(), std::numeric_limits<int8_t>::min(), -7}}})
.expected({{3},
element::i8,
std::vector<int8_t>{std::numeric_limits<int8_t>::min(), std::numeric_limits<int8_t>::max(), 6}}),
Builder{}
.opType(BitwiseTypes::BITWISE_NOT)
.inputs(
{{{3},
element::u8,
std::vector<uint8_t>{std::numeric_limits<uint8_t>::max(), std::numeric_limits<uint8_t>::min(), 7}}})
.expected({{3},
element::u8,
std::vector<uint8_t>{std::numeric_limits<uint8_t>::min(),
std::numeric_limits<uint8_t>::max(),
std::numeric_limits<uint8_t>::max() - 7}}),
Builder{}
.opType(BitwiseTypes::BITWISE_NOT)
.inputs(
{{{3},
element::i16,
std::vector<int16_t>{std::numeric_limits<int16_t>::max(), std::numeric_limits<int16_t>::min(), -7}}})
.expected(
{{3},
element::i16,
std::vector<int16_t>{std::numeric_limits<int16_t>::min(), std::numeric_limits<int16_t>::max(), 6}}),
Builder{}
.opType(BitwiseTypes::BITWISE_NOT)
.inputs({{{3},
element::u16,
std::vector<uint16_t>{std::numeric_limits<uint16_t>::max(),
std::numeric_limits<uint16_t>::min(),
7}}})
.expected({{3},
element::u16,
std::vector<uint16_t>{std::numeric_limits<uint16_t>::min(),
std::numeric_limits<uint16_t>::max(),
std::numeric_limits<uint16_t>::max() - 7}}),
Builder{}
.opType(BitwiseTypes::BITWISE_NOT)
.inputs(
{{{3},
element::i32,
std::vector<int32_t>{std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::min(), -7}}})
.expected(
{{3},
element::i32,
std::vector<int32_t>{std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::max(), 6}}),
Builder{}
.opType(BitwiseTypes::BITWISE_NOT)
.inputs({{{3},
element::u32,
std::vector<uint32_t>{std::numeric_limits<uint32_t>::max(),
std::numeric_limits<uint32_t>::min(),
7}}})
.expected({{3},
element::u32,
std::vector<uint32_t>{std::numeric_limits<uint32_t>::min(),
std::numeric_limits<uint32_t>::max(),
std::numeric_limits<uint32_t>::max() - 7}}),
Builder{}
.opType(BitwiseTypes::BITWISE_NOT)
.inputs(
{{{3},
element::i64,
std::vector<int64_t>{std::numeric_limits<int64_t>::max(), std::numeric_limits<int64_t>::min(), -7}}})
.expected(
{{3},
element::i64,
std::vector<int64_t>{std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max(), 6}}),
Builder{}
.opType(BitwiseTypes::BITWISE_NOT)
.inputs({{{3},
element::u64,
std::vector<uint64_t>{std::numeric_limits<uint64_t>::max(),
std::numeric_limits<uint64_t>::min(),
7}}})
.expected({{3},
element::u64,
std::vector<uint64_t>{std::numeric_limits<uint64_t>::min(),
std::numeric_limits<uint64_t>::max(),
std::numeric_limits<uint64_t>::max() - 7}}),
};
return bitwiseParams;
}
INSTANTIATE_TEST_SUITE_P(smoke_BitwiseNot_With_Hardcoded_Refs,
ReferenceBitwiseLayerTest,
::testing::ValuesIn(generateBitwiseParams()),
ReferenceBitwiseLayerTest::getTestCaseName);
} // namespace
} // namespace BitwiseOpsRefTestDefinitions
} // namespace reference_tests

View File

@ -1242,6 +1242,13 @@ std::shared_ptr<ov::Model> generate(const std::shared_ptr<ov::op::v10::IsNaN> &n
return std::make_shared<ov::Model>(results, ov::ParameterVector{param}, "is_nan_graph");
}
std::shared_ptr<ov::Model> generate(const std::shared_ptr<ov::op::v13::BitwiseNot> &node) {
const auto param = std::make_shared<ov::op::v0::Parameter>(ov::element::i64, ov::PartialShape{1, 2});
auto bitwise = std::make_shared<ov::op::v13::BitwiseNot>(param);
ov::ResultVector results{std::make_shared<ov::op::v0::Result>(bitwise)};
return std::make_shared<ov::Model>(results, ov::ParameterVector{param}, "BitwiseNotGraph");
}
std::shared_ptr<ov::Model> generateArithmeticReductionKeepDims(const std::shared_ptr<ov::op::Op> &node) {
const auto data = std::make_shared<ov::op::v0::Parameter>(ov::element::f32, ov::PartialShape{3, 3});
const auto axes = ov::op::v0::Constant::create(ov::element::i32, {1}, {1});