From e70e7e1e9da654c316f7efa65da1c3893b3995e0 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Thu, 29 Jul 2021 18:19:54 +0300 Subject: [PATCH] Shell implementation for RandomUniform. (#6782) * Added shell implementation for RandomUniform. * Small correction. * Small correction. * Corrected wrong type. * Corrected error message, corrected setters. --- .../core/include/ngraph/op/random_uniform.hpp | 69 +++++ ngraph/core/include/ngraph/ops.hpp | 1 + .../core/include/ngraph/opsets/opset8_tbl.hpp | 3 +- ngraph/core/src/op/random_uniform.cpp | 144 ++++++++++ ngraph/test/CMakeLists.txt | 2 + ngraph/test/type_prop/random_uniform.cpp | 254 ++++++++++++++++++ ngraph/test/visitors/op/random_uniform.cpp | 36 +++ 7 files changed, 508 insertions(+), 1 deletion(-) create mode 100644 ngraph/core/include/ngraph/op/random_uniform.hpp create mode 100644 ngraph/core/src/op/random_uniform.cpp create mode 100644 ngraph/test/type_prop/random_uniform.cpp create mode 100644 ngraph/test/visitors/op/random_uniform.cpp diff --git a/ngraph/core/include/ngraph/op/random_uniform.hpp b/ngraph/core/include/ngraph/op/random_uniform.hpp new file mode 100644 index 00000000000..a9da867dae6 --- /dev/null +++ b/ngraph/core/include/ngraph/op/random_uniform.hpp @@ -0,0 +1,69 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "ngraph/node.hpp" +#include "ngraph/op/op.hpp" + +namespace ngraph +{ + namespace op + { + namespace v8 + { + /// \brief Tensor RandomUniform operation. + class NGRAPH_API RandomUniform : public Op + { + public: + NGRAPH_RTTI_DECLARATION; + + RandomUniform() = default; + + /// + /// \brief Constructs a RandomUniform operation. + /// + /// \param out_shape Node producing the tensor with output shape. + /// \param min_val Node producing the tensor with minimum value. + /// \param max_val Node producing the tensor with maximum value. + /// \param out_type Output type of the tensor. + /// \param global_seed Global seed value. + /// \param op_seed Operational seed value. + RandomUniform(const Output& out_shape, + const Output& min_val, + const Output& max_val, + const ngraph::element::Type& out_type, + uint64_t global_seed, + uint64_t op_seed); + + void validate_and_infer_types() override; + + bool visit_attributes(AttributeVisitor& visitor) override; + + std::shared_ptr + clone_with_new_inputs(const OutputVector& new_args) const override; + + /// \return The output tensor type. + const ngraph::element::Type& get_out_type() const { return m_output_type; } + void set_out_type(const ngraph::element::Type& output_type) + { + m_output_type = output_type; + } + + /// \return The global seed value. + uint64_t get_global_seed() const { return m_global_seed; } + void set_global_seed(uint64_t seed) { m_global_seed = seed; } + + /// \return The operational seed value. + uint64_t get_op_seed() const { return m_op_seed; } + void set_op_seed(uint64_t seed2) { m_op_seed = seed2; } + + protected: + ngraph::element::Type m_output_type; + uint64_t m_global_seed; + uint64_t m_op_seed; + }; + } // namespace v8 + } // namespace op +} // namespace ngraph diff --git a/ngraph/core/include/ngraph/ops.hpp b/ngraph/core/include/ngraph/ops.hpp index 4701a2f733f..2f07e3ef67a 100644 --- a/ngraph/core/include/ngraph/ops.hpp +++ b/ngraph/core/include/ngraph/ops.hpp @@ -112,6 +112,7 @@ #include "ngraph/op/prior_box_clustered.hpp" #include "ngraph/op/proposal.hpp" #include "ngraph/op/psroi_pooling.hpp" +#include "ngraph/op/random_uniform.hpp" #include "ngraph/op/range.hpp" #include "ngraph/op/read_value.hpp" #include "ngraph/op/reduce_l1.hpp" diff --git a/ngraph/core/include/ngraph/opsets/opset8_tbl.hpp b/ngraph/core/include/ngraph/opsets/opset8_tbl.hpp index 0004161dc48..0f216b471d5 100644 --- a/ngraph/core/include/ngraph/opsets/opset8_tbl.hpp +++ b/ngraph/core/include/ngraph/opsets/opset8_tbl.hpp @@ -180,4 +180,5 @@ NGRAPH_OP(AdaptiveAvgPool, ngraph::op::v8) NGRAPH_OP(AdaptiveMaxPool, ngraph::op::v8) NGRAPH_OP(DeformableConvolution, ngraph::op::v8) NGRAPH_OP(MatrixNms, ngraph::op::v8) -NGRAPH_OP(MulticlassNms, ngraph::op::v8) \ No newline at end of file +NGRAPH_OP(MulticlassNms, ngraph::op::v8) +NGRAPH_OP(RandomUniform, ngraph::op::v8) diff --git a/ngraph/core/src/op/random_uniform.cpp b/ngraph/core/src/op/random_uniform.cpp new file mode 100644 index 00000000000..6c037071d43 --- /dev/null +++ b/ngraph/core/src/op/random_uniform.cpp @@ -0,0 +1,144 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ngraph/op/random_uniform.hpp" +#include +#include "itt.hpp" + +using namespace std; +using namespace ngraph; + +NGRAPH_RTTI_DEFINITION(op::v8::RandomUniform, "RandomUniform", 8); + +op::v8::RandomUniform::RandomUniform(const Output& out_shape, + const Output& min_val, + const Output& max_val, + const ngraph::element::Type& out_type, + uint64_t global_seed, + uint64_t op_seed) + : Op({out_shape, min_val, max_val}) + , m_output_type(out_type) + , m_global_seed(global_seed) + , m_op_seed(op_seed) +{ + constructor_validate_and_infer_types(); +} + +void op::v8::RandomUniform::validate_and_infer_types() +{ + NGRAPH_OP_SCOPE(v8_RandomUniform_validate_and_infer_types); + + const auto& shape_et = get_input_element_type(0); + NODE_VALIDATION_CHECK(this, + shape_et.is_dynamic() || shape_et == element::i32 || + shape_et == element::i64, + "Type of the input should be int32 or int64."); + + PartialShape output_shape = PartialShape::dynamic(); + const auto& input_shape = get_input_partial_shape(0); + if (input_shape.rank().is_static()) + { + NODE_VALIDATION_CHECK(this, + input_shape.rank() == 1, + "The rank of the tensor defining output shape must be equal to 1."); + if (const auto& const_shape = get_constant_from_source(input_value(0))) + { + output_shape = PartialShape(const_shape->cast_vector()); + } + } + + const auto& min_pshape = get_input_partial_shape(1); + const auto& max_pshape = get_input_partial_shape(2); + if (min_pshape.is_static()) + { + const auto& min_rank = min_pshape.rank().get_length(); + NODE_VALIDATION_CHECK(this, min_rank <= 1, "Min value must be a scalar or 1D tensor."); + + if (min_rank == 1) + { + NODE_VALIDATION_CHECK( + this, min_pshape.compatible(Shape{1}), "'min_val' should have 1 element."); + } + } + + if (max_pshape.is_static()) + { + const auto& max_rank = max_pshape.rank().get_length(); + NODE_VALIDATION_CHECK(this, max_rank <= 1, "Max value must be a scalar or 1D tensor."); + + if (max_rank == 1) + { + NODE_VALIDATION_CHECK( + this, max_pshape.compatible(Shape{1}), "'max_val' should have 1 element."); + } + } + + const element::Type& min_element_type = get_input_element_type(1); + element::Type max_element_type = get_input_element_type(2); + NODE_VALIDATION_CHECK(this, + min_element_type == max_element_type, + "'min_val' should have the same type as 'max_val'."); + NODE_VALIDATION_CHECK( + this, + min_element_type == get_out_type(), + "'min_val' and 'max_val' should have the same type as 'out_type' attribute."); + + if (const auto& const_min = get_constant_from_source(input_value(1))) + { + if (const auto& const_max = get_constant_from_source(input_value(2))) + { + if (get_out_type() == ngraph::element::Type_t::i64 || + get_out_type() == ngraph::element::Type_t::i32) + { + int64_t min_val = const_min->cast_vector()[0]; + int64_t max_val = const_max->cast_vector()[0]; + + NODE_VALIDATION_CHECK(this, + min_val < max_val, + "Min value must be less than max value. Got " + "min value: ", + min_val, + ", max value: ", + max_val); + } + else if (get_out_type().is_real()) + { + double min_val = const_min->cast_vector()[0]; + double max_val = const_max->cast_vector()[0]; + + NODE_VALIDATION_CHECK(this, + min_val < max_val, + "Min value must be less than max value. Got " + "min value: ", + min_val, + ", max value: ", + max_val); + } + else + { + throw ngraph_error("Unsupported output type of RandomUniform: " + + get_out_type().get_type_name()); + } + } + } + + set_output_type(0, get_out_type(), output_shape); +} + +bool op::v8::RandomUniform::visit_attributes(AttributeVisitor& visitor) +{ + NGRAPH_OP_SCOPE(v8_RandomUniform_visit_attributes); + visitor.on_attribute("output_type", m_output_type); + visitor.on_attribute("op_seed", m_op_seed); + visitor.on_attribute("global_seed", m_global_seed); + return true; +} + +shared_ptr op::v8::RandomUniform::clone_with_new_inputs(const OutputVector& new_args) const +{ + NGRAPH_OP_SCOPE(v8_Roll_clone_with_new_inputs); + check_new_args_count(this, new_args); + return make_shared( + new_args[0], new_args[1], new_args[2], m_output_type, m_global_seed, m_op_seed); +} diff --git a/ngraph/test/CMakeLists.txt b/ngraph/test/CMakeLists.txt index b14aa986d1b..7f802bbc53d 100644 --- a/ngraph/test/CMakeLists.txt +++ b/ngraph/test/CMakeLists.txt @@ -178,6 +178,7 @@ set(SRC type_prop/proposal.cpp type_prop/psroi_pooling.cpp type_prop/prior_box_clustered.cpp + type_prop/random_uniform.cpp type_prop/range.cpp type_prop/read_value.cpp type_prop/reduce_l1.cpp @@ -298,6 +299,7 @@ set(SRC visitors/op/prior_box_clustered.cpp visitors/op/proposal.cpp visitors/op/psroi_pooling.cpp + visitors/op/random_uniform.cpp visitors/op/reduce_l1.cpp visitors/op/reduce_l2.cpp visitors/op/reduce_logical_and.cpp diff --git a/ngraph/test/type_prop/random_uniform.cpp b/ngraph/test/type_prop/random_uniform.cpp new file mode 100644 index 00000000000..1b82e137e0d --- /dev/null +++ b/ngraph/test/type_prop/random_uniform.cpp @@ -0,0 +1,254 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "gtest/gtest.h" +#include "ngraph/ngraph.hpp" +#include "ngraph/opsets/opset8.hpp" +#include "util/type_prop.hpp" + +using namespace std; +using namespace ngraph; + +TEST(type_prop, random_uniform_type_shape) +{ + auto out_shape = opset8::Constant::create(element::i64, Shape{4}, {2, 3, 4, 5}); + auto min_val = make_shared(element::f32, Shape{}, 0.f); + auto max_val = make_shared(element::f32, Shape{}, 1.f); + + auto r = + make_shared(out_shape, min_val, max_val, element::f32, 120, 100); + + EXPECT_EQ(r->get_output_element_type(0), element::f32); + EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape{2, 3, 4, 5})); +} + +TEST(type_prop, random_uniform_dynamic_shape) +{ + auto out_shape = + make_shared(element::i32, PartialShape{Dimension::dynamic()}); + auto min_val = make_shared(element::i64, Shape{}, 5); + auto max_val = make_shared(element::i64, Shape{}, 10); + + auto r = + make_shared(out_shape, min_val, max_val, element::i64, 100, 200); + + EXPECT_EQ(r->get_output_element_type(0), element::i64); + EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape::dynamic())); +} + +TEST(type_prop, random_uniform_dynamic_rank) +{ + auto out_shape = make_shared(element::i32, PartialShape::dynamic()); + auto min_val = make_shared(element::f64, Shape{}, 5); + auto max_val = make_shared(element::f64, Shape{}, 10); + + auto r = + make_shared(out_shape, min_val, max_val, element::f64, 100, 200); + + EXPECT_EQ(r->get_output_element_type(0), element::f64); + EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape::dynamic())); +} + +TEST(type_prop, random_uniform_invalid_out_shape_type) +{ + auto out_shape = opset8::Constant::create(element::f64, Shape{4}, {2, 3, 4, 5}); + auto min_val = make_shared(element::f32, Shape{}, 0.f); + auto max_val = make_shared(element::f32, Shape{}, 1.f); + + try + { + auto r = + make_shared(out_shape, min_val, max_val, element::f32, 120, 100); + // Should have thrown, so fail if it didn't + FAIL() << "Unexpected pass with invalid output shape."; + } + catch (const NodeValidationFailure& error) + { + EXPECT_HAS_SUBSTRING(error.what(), + std::string("Type of the input should be int32 or int64.")); + } + catch (...) + { + FAIL() << "Check failed for unexpected reason."; + } +} + +TEST(type_prop, random_uniform_invalid_out_shape_rank) +{ + auto out_shape = make_shared(element::i32, Shape{3, 2}); + auto min_val = make_shared(element::f32, Shape{}, 0.f); + auto max_val = make_shared(element::f32, Shape{}, 1.f); + try + { + auto r = + make_shared(out_shape, min_val, max_val, element::f32, 120, 100); + // Should have thrown, so fail if it didn't + FAIL() << "Unexpected pass with invalid output shape."; + } + catch (const NodeValidationFailure& error) + { + EXPECT_HAS_SUBSTRING( + error.what(), + std::string("The rank of the tensor defining output shape must be equal to 1.")); + } + catch (...) + { + FAIL() << "Check failed for unexpected reason."; + } +} + +TEST(type_prop, random_uniform_invalid_min_val) +{ + auto out_shape = opset8::Constant::create(element::i32, Shape{4}, {2, 3, 4, 5}); + auto min_val = opset8::Constant::create(element::f32, Shape{2}, {2, 3}); + auto max_val = make_shared(element::f32, Shape{}, 1.f); + + try + { + auto r = + make_shared(out_shape, min_val, max_val, element::f32, 120, 100); + // Should have thrown, so fail if it didn't + FAIL() << "Unexpected pass with invalid min value."; + } + catch (const NodeValidationFailure& error) + { + EXPECT_HAS_SUBSTRING(error.what(), std::string("'min_val' should have 1 element.")); + } + catch (...) + { + FAIL() << "Check failed for unexpected reason."; + } +} + +TEST(type_prop, random_uniform_invalid_max_val) +{ + auto out_shape = opset8::Constant::create(element::i32, Shape{4}, {2, 3, 4, 5}); + auto min_val = make_shared(element::f32, Shape{}, 0.f); + auto max_val = opset8::Constant::create(element::f32, Shape{3}, {2, 3, 5}); + + try + { + auto r = + make_shared(out_shape, min_val, max_val, element::f32, 120, 100); + // Should have thrown, so fail if it didn't + FAIL() << "Unexpected pass with invalid max value."; + } + catch (const NodeValidationFailure& error) + { + EXPECT_HAS_SUBSTRING(error.what(), std::string("'max_val' should have 1 element.")); + } + catch (...) + { + FAIL() << "Check failed for unexpected reason."; + } +} + +TEST(type_prop, random_uniform_invalid_min_max_val_type_case1) +{ + auto out_shape = opset8::Constant::create(element::i64, Shape{4}, {2, 3, 4, 5}); + auto min_val = make_shared(element::f32, Shape{}, 0.f); + auto max_val = make_shared(element::i32, Shape{}, 100); + + try + { + auto r = + make_shared(out_shape, min_val, max_val, element::f32, 120, 100); + // Should have thrown, so fail if it didn't + FAIL() << "Unexpected pass with invalid min value type."; + } + catch (const NodeValidationFailure& error) + { + EXPECT_HAS_SUBSTRING(error.what(), + std::string("'min_val' should have the same type as 'max_val'.")); + } + catch (...) + { + FAIL() << "Check failed for unexpected reason"; + } +} + +TEST(type_prop, random_uniform_invalid_min_max_val_type_case2) +{ + auto out_shape = opset8::Constant::create(element::i64, Shape{4}, {2, 3, 4, 5}); + auto min_val = make_shared(element::f32, Shape{}, 0.f); + auto max_val = make_shared(element::f32, Shape{}, 1.f); + + try + { + auto r = + make_shared(out_shape, min_val, max_val, element::i32, 120, 100); + // Should have thrown, so fail if it didn't + FAIL() << "Unexpected pass with invalid min and max value type."; + } + catch (const NodeValidationFailure& error) + { + EXPECT_HAS_SUBSTRING( + error.what(), + std::string( + "'min_val' and 'max_val' should have the same type as 'out_type' attribute.")); + } + catch (...) + { + FAIL() << "Check failed for unexpected reason"; + } +} + +TEST(type_prop, random_uniform_invalid_min_max_values_case1) +{ + auto out_shape = opset8::Constant::create(element::i64, Shape{4}, {2, 3, 4, 5}); + auto min_val = make_shared(element::f32, Shape{}, 1.f); + auto max_val = make_shared(element::f32, Shape{}, 0.f); + + try + { + auto r = + make_shared(out_shape, min_val, max_val, element::f32, 120, 100); + // Should have thrown, so fail if it didn't + FAIL() << "Unexpected pass with invalid min and max values."; + } + catch (const NodeValidationFailure& error) + { + EXPECT_HAS_SUBSTRING(error.what(), std::string("Min value must be less than max value.")); + } + catch (...) + { + FAIL() << "Check failed for unexpected reason"; + } +} + +TEST(type_prop, random_uniform_invalid_min_max_values_case2) +{ + auto out_shape = opset8::Constant::create(element::i64, Shape{4}, {2, 3, 4, 5}); + auto min_val = make_shared(element::i32, Shape{}, 100); + auto max_val = make_shared(element::i32, Shape{}, 100); + + try + { + auto r = + make_shared(out_shape, min_val, max_val, element::i32, 120, 100); + // Should have thrown, so fail if it didn't + FAIL() << "Unexpected pass with invalid min and max values."; + } + catch (const NodeValidationFailure& error) + { + EXPECT_HAS_SUBSTRING(error.what(), std::string("Min value must be less than max value.")); + } + catch (...) + { + FAIL() << "Check failed for unexpected reason"; + } +} + +TEST(type_prop, random_uniform_min_max_1d_tensors) +{ + auto out_shape = opset8::Constant::create(element::i64, Shape{4}, {2, 3, 4, 5}); + auto min_val = opset8::Constant::create(element::f32, Shape{1}, {-1.0}); + auto max_val = opset8::Constant::create(element::f32, Shape{1}, {2.0}); + + auto r = + make_shared(out_shape, min_val, max_val, element::f32, 120, 100); + + EXPECT_EQ(r->get_output_element_type(0), element::f32); + EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape{2, 3, 4, 5})); +} diff --git a/ngraph/test/visitors/op/random_uniform.cpp b/ngraph/test/visitors/op/random_uniform.cpp new file mode 100644 index 00000000000..6f1b3a1853f --- /dev/null +++ b/ngraph/test/visitors/op/random_uniform.cpp @@ -0,0 +1,36 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "gtest/gtest.h" + +#include "ngraph/ngraph.hpp" +#include "ngraph/op/util/attr_types.hpp" +#include "ngraph/opsets/opset8.hpp" + +#include "util/visitor.hpp" + +using namespace std; +using namespace ngraph; +using ngraph::test::NodeBuilder; +using ngraph::test::ValueMap; + +TEST(attributes, random_uniform_op) +{ + NodeBuilder::get_ops().register_factory(); + auto out_shape = + make_shared(element::i64, Shape{3}, vector{3, 2, 4}); + auto min_val = make_shared(element::f32, Shape{}, 0); + auto max_val = make_shared(element::f32, Shape{}, 1); + + const auto random_uniform = make_shared( + out_shape, min_val, max_val, element::Type_t::f32, 150, 10); + NodeBuilder builder(random_uniform); + auto g_random_uniform = as_type_ptr(builder.create()); + + const auto expected_attr_count = 3; + EXPECT_EQ(builder.get_value_map_size(), expected_attr_count); + EXPECT_EQ(g_random_uniform->get_global_seed(), random_uniform->get_global_seed()); + EXPECT_EQ(g_random_uniform->get_op_seed(), random_uniform->get_op_seed()); + EXPECT_EQ(g_random_uniform->get_out_type(), random_uniform->get_out_type()); +}