RandomUniform reference implementation. (#7012)

* Added RandomUniform reference implementation.

* Corrected comments.

* Small correction.

* Code style correction.

* Added has_evaluate() method.

* Added comments, added names to consts.

* Small fix.

* Replaced arrays with vectors.

* Apply suggestions from code review

Co-authored-by: Ilya Churaev <ilyachur@gmail.com>

* Code refactoring.

* Corrected tests, code style.

* Added comment.

* Added comments.

* Temporarily added debug output.

* Temporarily added debug output.

* Removed debug output.

* Added comment.

* Added comment.

* Enabled state saving for RandomUniform.

* Code style.

* Used to template to convert types.

* Added comments.

Co-authored-by: Ilya Churaev <ilyachur@gmail.com>
This commit is contained in:
Anastasia PopovaandIlya Churaev authored and GitHub committed 2021-09-06 11:49:27 +03:00
1 parent 4978e8e6f8
commit d82fed9527
8 files changed
+737 -3

No files matched your search

@@ -0,0 +1,205 @@
// Copyright (C) 2018-2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <gtest/gtest.h>
#include <vector>
#include "base_reference_test.hpp"
#include "ngraph/opsets/opset8.hpp"
#include "ngraph/util.hpp"
using namespace ngraph;
namespace reference_tests {
namespace {
struct RandomUniformParams {
RandomUniformParams(const std::vector<int64_t>& paramOutShape,
const Tensor& paramMinValue,
const Tensor& paramMaxValue,
ngraph::element::Type paramOutType,
int64_t paramGlobalSeed,
int64_t paramOpSeed,
const Tensor& paramExpected,
const std::string& test_name)
: out_shape(paramOutShape),
min_val(paramMinValue),
max_val(paramMaxValue),
out_type(paramOutType),
global_seed(paramGlobalSeed),
op_seed(paramOpSeed),
expected(paramExpected),
test_case_name(test_name) {}
std::vector<int64_t> out_shape;
Tensor min_val;
Tensor max_val;
ngraph::element::Type out_type;
int64_t global_seed;
int64_t op_seed;
Tensor expected;
std::string test_case_name;
};
class ReferenceRandomUniformLayerTest : public testing::TestWithParam<RandomUniformParams>, public CommonReferenceTest {
public:
void SetUp() override {
auto params = GetParam();
function = CreateFunction(params.out_shape,
params.min_val,
params.max_val,
params.out_type,
params.global_seed,
params.op_seed);
inputData = {params.min_val.data, params.max_val.data};
refOutData = {params.expected.data};
}
static std::string getTestCaseName(const testing::TestParamInfo<RandomUniformParams>& obj) {
auto param = obj.param;
return param.test_case_name;
}
private:
static std::shared_ptr<Function> CreateFunction(const std::vector<int64_t>& out_shape,
const Tensor& min_val,
const Tensor& max_val,
const ngraph::element::Type& out_type,
int64_t global_seed,
int64_t op_seed) {
const auto min_val_param = std::make_shared<opset8::Parameter>(min_val.type, min_val.shape);
const auto max_val_param = std::make_shared<opset8::Parameter>(max_val.type, max_val.shape);
auto out_shape_ = std::make_shared<opset8::Constant>(element::i64, Shape{out_shape.size()}, out_shape);
return std::make_shared<Function>(NodeVector{std::make_shared<opset8::RandomUniform>(out_shape_,
min_val_param,
max_val_param,
out_type,
global_seed,
op_seed)},
ParameterVector{min_val_param, max_val_param});
}
};
TEST_P(ReferenceRandomUniformLayerTest, RandomUniformWithHardcodedRefs) {
Exec();
}
} // namespace
// Reference values for the following tests are obtained from single layer TensorFlow model with tf.random.uniform().
INSTANTIATE_TEST_SUITE_P(
smoke_RandomUniform_With_Hardcoded_Refs,
ReferenceRandomUniformLayerTest,
::testing::Values(
RandomUniformParams(std::vector<int64_t>{3, 2, 4},
Tensor{{1}, element::f32, std::vector<float>{0}},
Tensor{{1}, element::f32, std::vector<float>{1}},
element::Type_t::f32,
150,
10,
Tensor{{3, 2, 4},
element::f32,
std::vector<float>{0.70112360, 0.30539632, 0.93931055, 0.94560349, 0.11694777,
0.50770056, 0.51971972, 0.22727466, 0.99137402, 0.35519040,
0.82692313, 0.59864855, 0.31364107, 0.57481313, 0.41399086,
0.96308255, 0.37140799, 0.85253167, 0.09358585, 0.08200955,
0.23655081, 0.81056309, 0.74226606, 0.76106691}},
"float32_default_min_max"),
RandomUniformParams(std::vector<int64_t>{3, 2, 4},
Tensor{{1}, element::f16, std::vector<float16>{0}},
Tensor{{1}, element::f16, std::vector<float16>{1}},
element::Type_t::f16,
150,
10,
Tensor{{3, 2, 4},
element::f16,
std::vector<float16>{0.60449219, 0.80664062, 0.83203125, 0.38378906, 0.03613281,
0.08300781, 0.54394531, 0.83398438, 0.33593750, 0.71972656,
0.15429688, 0.12890625, 0.34765625, 0.86914062, 0.41308594,
0.57226562, 0.57421875, 0.93945312, 0.65527344, 0.82226562,
0.82421875, 0.13281250, 0.64355469, 0.66015625}},
"float16_default_min_max"),
RandomUniformParams(std::vector<int64_t>{3, 2, 4},
Tensor{{1}, element::f32, std::vector<float>{-650}},
Tensor{{1}, element::f32, std::vector<float>{450}},
element::Type_t::f32,
150,
10,
Tensor{{3, 2, 4},
element::f32,
std::vector<float>{121.23596191, -314.06405640, 383.24157715, 390.16381836,
-521.35742188, -91.52935791, -78.30828857, -399.99786377,
440.51147461, -259.29055786, 259.61541748, 8.51342773,
-304.99481201, -17.70556641, -194.61004639, 409.39074707,
-241.45120239, 287.78485107, -547.05554199, -559.78948975,
-389.79409790, 241.61938477, 166.49267578, 187.17358398}},
"float32_non_default_min_max"),
RandomUniformParams(std::vector<int64_t>{3, 2, 4},
Tensor{{1}, element::f16, std::vector<float16>{-1.5}},
Tensor{{1}, element::f16, std::vector<float16>{-1.0}},
element::Type_t::f16,
150,
10,
Tensor{{3, 2, 4},
element::f16,
std::vector<float16>{-1.19726562, -1.09667969, -1.08398438, -1.30859375, -1.48242188,
-1.45898438, -1.22851562, -1.08300781, -1.33203125, -1.14062500,
-1.42285156, -1.43554688, -1.32617188, -1.06542969, -1.29296875,
-1.21386719, -1.21289062, -1.03027344, -1.17187500, -1.08886719,
-1.08789062, -1.43359375, -1.17773438, -1.16992188}},
"float16_non_default_min_max"),
RandomUniformParams(std::vector<int64_t>{2, 3, 4},
Tensor{{1}, element::i32, std::vector<int32_t>{-100}},
Tensor{{1}, element::i32, std::vector<int32_t>{50}},
element::Type_t::i32,
100,
350,
Tensor{{2, 3, 4},
element::i32,
std::vector<int32_t>{
22, -56, -33, -89, -98, -33, -3, -48, -82, 5, -66, 21,
29, -42, -73, -37, 3, 36, -35, 20, -11, -8, -78, 47,
}},
"int32"),
RandomUniformParams(std::vector<int64_t>{5, 4, 3},
Tensor{{1}, element::i64, std::vector<int64_t>{-2600}},
Tensor{{1}, element::i64, std::vector<int64_t>{3700}},
element::Type_t::i64,
755,
951,
Tensor{{5, 4, 3},
element::i64,
std::vector<int64_t>{
2116, -1581, 2559, -339, -1660, 519, 90, 2027, -210, 3330, 1831, -1737,
2683, 2661, 3473, 1220, 3534, -2384, 2199, 1935, 499, 2861, 2743, 3223,
-531, -836, -65, 3435, 632, 1765, 2613, 1891, 1698, 3069, 169, -792,
-32, 2976, -1552, -2588, 3327, -1756, 2637, -1084, 3567, -778, -1465, 2967,
1242, 2672, -1585, -2271, 3536, -1502, 400, 2241, 3126, 908, 1073, -2110}},
"int64"),
RandomUniformParams(std::vector<int64_t>{7, 3},
Tensor{{1}, element::bf16, std::vector<bfloat16>{0}},
Tensor{{1}, element::bf16, std::vector<bfloat16>{1}},
element::Type_t::bf16,
4978,
5164,
Tensor{{7, 3},
element::bf16,
std::vector<bfloat16>{0.8984375, 0.84375, 0.1640625, 0.1875, 0.46875, 0.6875,
0.5234375, 0.3046875, 0.9140625, 0.453125, 0.953125, 0.328125,
0.359375, 0.1875, 0.9453125, 0.390625, 0.21875, 0.9921875,
0.8203125, 0.453125, 0.875}},
"bfloat16_default_min_max"),
RandomUniformParams(std::vector<int64_t>{7, 3},
Tensor{{1}, element::bf16, std::vector<bfloat16>{-150}},
Tensor{{1}, element::bf16, std::vector<bfloat16>{200}},
element::Type_t::bf16,
4978,
5164,
Tensor{{7, 3},
element::bf16,
std::vector<bfloat16>{164, 146, -92.5, -84.5, 14, 90, 33, -43.5, 170, 8, 182,
-35, -24, -84.5, 180, -14, -73.5, 198, 138, 8, 156}},
"bfloat16_non_default_min_max")),
ReferenceRandomUniformLayerTest::getTestCaseName);
} // namespace reference_tests
@@ -0,0 +1,27 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include <memory>
#include <ngraph/pass/graph_rewrite.hpp>
namespace ngraph {
namespace pass {
class DisableRandomUniformConstantFolding;
} // namespace pass
} // namespace ngraph
/**
* @ingroup ie_transformation_common_api
* @brief Disables ConstantFolding for RandomUniform operation. It is required as RandomUniform
* should generate new sequence each run.
*/
class ngraph::pass::DisableRandomUniformConstantFolding : public ngraph::pass::MatcherPass {
public:
NGRAPH_RTTI_DECLARATION;
DisableRandomUniformConstantFolding();
};
@@ -27,6 +27,7 @@
#include "transformations/common_optimizations/hswish_fusion.hpp"
#include "transformations/common_optimizations/convert_quantize_dequantize.hpp"
#include "transformations/common_optimizations/relu_fake_quantize_fusion.hpp"
#include "transformations/common_optimizations/disable_random_uniform_constant_folding.hpp"
#include "transformations/common_optimizations/add_fake_quantize_fusion.hpp"
#include "transformations/common_optimizations/mul_fake_quantize_fusion.hpp"
#include "transformations/common_optimizations/clamp_fusion.hpp"
@@ -88,6 +89,7 @@ bool ngraph::pass::CommonOptimizations::run_on_function(std::shared_ptr<ngraph::
// This pass must be called first in pipeline
manager.register_pass<ngraph::pass::InitNodeInfo>();
manager.register_pass<ngraph::pass::DisableRandomUniformConstantFolding>();
manager.register_pass<ngraph::pass::SimplifyShapeOfSubGraph>();
manager.register_pass<ngraph::pass::ConstantFolding>();
manager.register_pass<ngraph::pass::RemoveFilteringBoxesBySize>(); // Resolves dynamism (replaces NonZero), CF needed
@@ -0,0 +1,24 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "transformations/common_optimizations/disable_random_uniform_constant_folding.hpp"
#include <memory>
#include <ngraph/opsets/opset8.hpp>
#include <ngraph/pattern/op/wrap_type.hpp>
#include <transformations/rt_info/disable_constant_folding.hpp>
NGRAPH_RTTI_DEFINITION(ngraph::pass::DisableRandomUniformConstantFolding, "DisableRandomUniformConstantFolding", 0);
ngraph::pass::DisableRandomUniformConstantFolding::DisableRandomUniformConstantFolding() {
auto random_uniform = pattern::wrap_type<opset8::RandomUniform>();
ngraph::matcher_pass_callback callback = [=](pattern::Matcher& m) {
disable_constant_folding(m.get_match_root());
return true;
};
auto m = std::make_shared<ngraph::pattern::Matcher>(random_uniform, "DisableRandomUniformConstantFolding");
this->register_matcher(m, callback);
}
@@ -30,8 +30,8 @@ public:
const Output<Node>& min_val,
const Output<Node>& max_val,
const ngraph::element::Type& out_type,
uint64_t global_seed,
uint64_t op_seed);
uint64_t global_seed = 0,
uint64_t op_seed = 0);
void validate_and_infer_types() override;
@@ -39,6 +39,11 @@ public:
std::shared_ptr<Node> clone_with_new_inputs(const OutputVector& new_args) const override;
/// \return Turns off constant folding for RandomUniform operation.
bool constant_fold(OutputVector& output_values, const OutputVector& inputs_values) override {
return false;
}
/// \return The output tensor type.
const ngraph::element::Type& get_out_type() const {
return m_output_type;
@@ -63,10 +68,17 @@ public:
m_op_seed = seed2;
}
bool evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const override;
bool has_evaluate() const override;
protected:
ngraph::element::Type m_output_type;
uint64_t m_global_seed;
uint64_t m_op_seed;
mutable std::mutex m_state_mutex;
mutable std::pair<uint64_t, uint64_t> m_state;
};
} // namespace v8
} // namespace op
@@ -0,0 +1,39 @@
// Copyright (C) 2018-2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include <ctime>
#include <ngraph/type/element_type.hpp>
#include "ngraph/shape.hpp"
namespace ngraph {
namespace runtime {
namespace reference {
std::pair<uint64_t, uint64_t> random_uniform(const uint64_t* out_shape,
const char* min_val,
const char* max_val,
char* out,
const Shape& out_shape_shape,
const ngraph::element::Type& elem_type,
uint64_t seed,
uint64_t seed2,
std::pair<uint64_t, uint64_t> prev_state);
// Following const values are taken from the original paper:
// https://www.thesalmons.org/john/random123/papers/random123sc11.pdf
const uint32_t crush_resistance_const_lower_value = 0x9E3779B9;
const uint32_t crush_resistance_const_upper_value = 0xBB67AE85;
const uint64_t statistic_maximizing_multiplier_n = 0xD2511F53;
const uint64_t statistic_maximizing_multiplier_counter = 0xCD9E8D57;
const size_t rounds_number = 10;
// Determines how many sequence elements of RNG sequence are skipped between runs.
// Can be any positive value, 256 is chosen for parity with Tensorflow.
const uint64_t skip_const = 256;
} // namespace reference
} // namespace runtime
} // namespace ngraph
@@ -0,0 +1,333 @@
// Copyright (C) 2018-2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "ngraph/runtime/reference/random_uniform.hpp"
#include <ctime>
#include "ngraph/shape.hpp"
namespace ngraph {
namespace runtime {
namespace reference {
// Splits uint64 value into two uint32 values with right and left part of original value.
std::pair<uint32_t, uint32_t> split_high_low(uint64_t value) {
uint32_t low = static_cast<uint32_t>(value);
uint32_t high = static_cast<uint32_t>(value >> 32);
return {low, high};
}
// Concatenates two uint32 values into single uint64 values.
uint64_t unite_high_low(uint32_t high, uint32_t low) {
return (static_cast<uint64_t>(high) << 32) + low;
}
// Runs single "round" of Philox algorithm.
void calculate_round(uint64_t key, uint64_t& counter, uint64_t& n) {
// Split key, counter and n into two uint32 values.
auto counter_lr = split_high_low(counter);
auto key_lr = split_high_low(key);
auto n_lr = split_high_low(n);
// Each round performs following updating for n and counter:
// left uint32 part = mullo(R, M)
// right uint32 part = mulhi(R, M) xor k xor L
// mulhi(a, b) = floor((a * b) / 2^32)
// mullo(a, b) = (a * b) mod 2^32,
// where M - statistic_maximizing_multiplier const
auto prod0 = split_high_low(statistic_maximizing_multiplier_n * n_lr.first);
auto prod1 = split_high_low(statistic_maximizing_multiplier_counter * counter_lr.first);
n_lr.first = prod1.second ^ n_lr.second ^ key_lr.first;
n_lr.second = prod1.first;
counter_lr.first = prod0.second ^ counter_lr.second ^ key_lr.second;
counter_lr.second = prod0.first;
// Unite counter and n into uint64 values.
counter = unite_high_low(counter_lr.second, counter_lr.first);
n = unite_high_low(n_lr.second, n_lr.first);
}
// Increases key value.
void raise_key(uint64_t& key) {
auto key_lr = split_high_low(key);
key_lr.first += crush_resistance_const_lower_value;
key_lr.second += crush_resistance_const_upper_value;
key = unite_high_low(key_lr.second, key_lr.first);
}
// Helper function for converting uint32 values to float32. Sets fractional part of
// floating value with bits from uint32 value. Resulting value is in interval [0,1).
float uint32_to_float(uint32_t x) {
// float32 is formatted as follows: sign(1 bit) exponent(8 bits) mantissa(23 bits). The value is interpreted
// The value is interpreted using following formula:
// (-1)^sign * 1, mantissa * 2 ^ (exponent - 127)
// Here we set the following values:
// sign = 0
// exponent = 127, for obtaining a zero exponent.
// mantissa = 23 right bits from generated uint32 random value.
uint32_t x_uint32 = (static_cast<uint32_t>(127) << 23) | (x & 0x7fffffu);
float x_float;
memcpy(&x_float, &x_uint32, sizeof(x_uint32));
return x_float - 1.0f;
}
// Helper function for converting uint32 values to float16.Sets fractional part of
// floating value with bits from uint32 value. Resulting value is in interval [0,1).
float16 uint32_to_float16(uint32_t x) {
// float16 is formatted as follows: sign(1 bit) exponent(5 bits) mantissa(10 bits). The value is interpreted
// The value is interpreted using following formula:
// (-1)^sign * 1, mantissa * 2 ^ (exponent - 15)
// Here we set the following values:
// sign = 0
// exponent = 15, for obtaining a zero exponent.
// mantissa = 10 right bits from generated uint32 random value.
uint16_t x_uint16 = static_cast<uint16_t>(x);
x_uint16 = (static_cast<uint16_t>(15) << 10) | (x_uint16 & 0x3ffu);
float16 x_float16;
memcpy(&x_float16, &x_uint16, sizeof(x_uint16));
return x_float16 - static_cast<float16>(1);
}
// Helper function for converting uint32 values to double. Sets fractional part of
// floating double with bits from uint32 values. Resulting value is in interval [0,1).
double uint32_to_double(uint32_t x1, uint32_t x2) {
// float64 is formatted as follows: sign(1 bit) exponent(11 bits) mantissa(52 bits). The value is interpreted
// The value is interpreted using following formula:
// (-1)^sign * 1, mantissa * 2 ^ (exponent - 1023)
// Here we set the following values:
// sign = 0
// exponent = 1023, for obtaining a zero exponent.
// mantissa = 52 right bits from two concatenated uint32 values from random integer generator.
uint64_t significant = ((static_cast<uint64_t>(x1) & 0xfffffu) << 32) | static_cast<uint64_t>(x2);
uint64_t x_uint64 = ((static_cast<uint64_t>(1023) << 52) | significant);
double x_double;
memcpy(&x_double, &x_uint64, sizeof(x_uint64));
return x_double - 1.0;
}
// Helper function for converting uint32 values to bfloat16. Sets fractional part of
// floating value with bits from uint32 value. Resulting value is in interval [0,1).
bfloat16 uint32_to_bfloat16(uint32_t x) {
// bfloat16 is formatted as follows: sign(1 bit) exponent(8 bits) mantissa(7 bits). The value is interpreted
// The value is interpreted using following formula:
// (-1)^sign * 1, mantissa * 2 ^ (exponent - 127)
// Here we set the following values:
// sign = 0
// exponent = 127, for obtaining a zero exponent.
// mantissa = 7 right bits from generated uint32 random value.
uint16_t x_uint16 = static_cast<uint16_t>(x);
x_uint16 = (static_cast<uint16_t>(127) << 7) | (x_uint16 & 0x7fu);
bfloat16 x_bfloat16;
memcpy(&x_bfloat16, &x_uint16, sizeof(x_uint16));
return x_bfloat16 - static_cast<bfloat16>(1);
}
// Runs Philox algorithm.
void run_philox(uint64_t key, uint64_t counter, uint64_t n, size_t n_rounds, std::vector<uint32_t>& res) {
for (size_t i = 0; i < n_rounds; i++) {
calculate_round(key, counter, n);
if (i < n_rounds - 1)
raise_key(key);
}
auto res1 = split_high_low(n);
auto res2 = split_high_low(counter);
res[0] = res1.first;
res[1] = res1.second;
res[2] = res2.first;
res[3] = res2.second;
}
// Converts uint32 values to destination type and normalizes to required range
template <typename T>
void convert_to_output_type(const std::vector<uint32_t>& res,
size_t step,
const ngraph::element::Type& elem_type,
const char* min_val,
const char* max_val,
char* out,
size_t k,
size_t elem_count,
T (*convert_single_input)(uint32_t) = nullptr,
T (*convert_two_inputs)(uint32_t, uint32_t, T, T) = nullptr,
T (*mod_func)(uint32_t, T, T) = nullptr) {
// Get min and max values
T mn[1];
T mx[1];
memcpy(mn, min_val, elem_type.size());
memcpy(mx, max_val, elem_type.size());
std::vector<T> res_out_type(step);
if (elem_type.size() > 4) {
// Each element of resulting sequence is formed using two uint32 values
res_out_type[0] = convert_two_inputs(res[0], res[1], mn[0], mx[0]);
res_out_type[1] = convert_two_inputs(res[2], res[3], mn[0], mx[0]);
} else {
// Each element of resulting sequence is formed using single uint32 value
std::transform(res.data(),
res.data() + step,
res_out_type.data(),
[&mn, &mx, &convert_single_input, &mod_func](uint32_t elem) {
if (convert_single_input != nullptr) {
return convert_single_input(elem) * (mx[0] - mn[0]) + mn[0];
} else {
return mod_func(elem, mn[0], mx[0]);
}
});
}
memcpy(out + k * elem_type.size(), res_out_type.data(), std::min(step, elem_count - k) * elem_type.size());
}
// Implementation of RandomUniform that uses Philox algorithm as inner random unsigned integer generator.
std::pair<uint64_t, uint64_t> random_uniform(const uint64_t* out_shape,
const char* min_val,
const char* max_val,
char* out,
const Shape& out_shape_shape,
const ngraph::element::Type& elem_type,
uint64_t seed,
uint64_t seed2,
std::pair<uint64_t, uint64_t> prev_state) {
// When both seed values are equal to zero RandomUniform should generate non-deterministic sequence.
// Implementation in plugins may differ for this case.
if (seed == 0 && seed2 == 0) {
std::srand(std::time(nullptr));
seed = std::rand();
}
// Get previous counter state
uint64_t n_state = prev_state.first;
uint64_t counter_state = prev_state.second;
// Initialize Philox key and counters
uint64_t key = seed;
uint64_t counter = counter_state > 0 ? counter_state : seed2;
uint64_t n = n_state;
// Calculate total element count for generation
size_t shape_count = shape_size(out_shape_shape);
size_t elem_count = 1;
for (size_t i = 0; i < shape_count; i++) {
elem_count *= out_shape[i];
}
// Philox algorithm returns 4 elements of RNG sequence per each invocation
const size_t philox_output_size = 4;
// Each run of Philox algorithm generates 4 uint32 values.
// If output_type is int32, f32, bf16, or f16 each value is converted to
// corresponding type so we have 4 result values. For f64 and i64 we use
// a pair of values for conversion, so we have 2 result values.
// Step indicates how many values we generate in one iteration.
const size_t step = elem_type.size() > 4 ? 2 : 4;
for (size_t k = 0; k < elem_count; k += step) {
// generate 4 random uint32 values using Philox algorithm
std::vector<uint32_t> res(philox_output_size);
run_philox(key, counter, n, rounds_number, res);
// convert values to corresponding output_type
switch (elem_type) {
case ngraph::element::Type_t::f32: {
convert_to_output_type<float>(res, step, elem_type, min_val, max_val, out, k, elem_count, uint32_to_float);
break;
}
case ngraph::element::Type_t::f16: {
convert_to_output_type<float16>(res,
step,
elem_type,
min_val,
max_val,
out,
k,
elem_count,
uint32_to_float16);
break;
}
case ngraph::element::Type_t::bf16: {
convert_to_output_type<bfloat16>(res,
step,
elem_type,
min_val,
max_val,
out,
k,
elem_count,
uint32_to_bfloat16);
break;
}
case ngraph::element::Type_t::f64: {
convert_to_output_type<double>(res,
step,
elem_type,
min_val,
max_val,
out,
k,
elem_count,
nullptr,
[](uint32_t a, uint32_t b, double mn, double mx) {
return uint32_to_double(a, b) * (mx - mn) + mn;
});
break;
}
case ngraph::element::Type_t::i32: {
convert_to_output_type<int>(res,
step,
elem_type,
min_val,
max_val,
out,
k,
elem_count,
nullptr,
nullptr,
[](uint32_t x, int mn, int mx) {
return static_cast<int>(x % (mx - mn) + mn);
});
break;
}
case ngraph::element::Type_t::i64: {
convert_to_output_type<int64_t>(res,
step,
elem_type,
min_val,
max_val,
out,
k,
elem_count,
nullptr,
[](uint32_t a, uint32_t b, int64_t mn, int64_t mx) {
return static_cast<int64_t>(unite_high_low(b, a) % (mx - mn) + mn);
});
break;
}
default:
throw ngraph_error("Unsupported type of RandomUniform: " + elem_type.get_type_name());
}
if (++n == 0)
++counter;
}
// Calculate counter values for next RandomUniform run
uint64_t skip_count = elem_count * skip_const;
n_state += skip_count;
if (n_state < skip_count)
counter_state++;
return {n_state, counter_state};
}
} // namespace reference
} // namespace runtime
} // namespace ngraph
+93 -1
View File
@@ -7,6 +7,7 @@
#include <ngraph/validation_util.hpp>
#include "itt.hpp"
#include "ngraph/runtime/reference/random_uniform.hpp"
using namespace std;
using namespace ngraph;
@@ -116,7 +117,7 @@ bool op::v8::RandomUniform::visit_attributes(AttributeVisitor& visitor) {
}
shared_ptr<Node> op::v8::RandomUniform::clone_with_new_inputs(const OutputVector& new_args) const {
NGRAPH_OP_SCOPE(v8_Roll_clone_with_new_inputs);
NGRAPH_OP_SCOPE(v8_RandomUniform_clone_with_new_inputs);
check_new_args_count(this, new_args);
return make_shared<v8::RandomUniform>(new_args[0],
new_args[1],
@@ -125,3 +126,94 @@ shared_ptr<Node> op::v8::RandomUniform::clone_with_new_inputs(const OutputVector
m_global_seed,
m_op_seed);
}
bool op::v8::RandomUniform::evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const {
NGRAPH_OP_SCOPE(v8_RandomUniform_evaluate);
const uint64_t* out_shape;
std::vector<uint64_t> out_shape_uint64(shape_size(inputs[0]->get_shape()));
if (inputs[0]->get_element_type() == element::Type_t::u64) {
out_shape = inputs[0]->get_data_ptr<const uint64_t>();
} else if (inputs[0]->get_element_type() == element::Type_t::i32) {
auto out_shape_i32 = inputs[0]->get_data_ptr<const int32_t>();
std::transform(out_shape_i32,
out_shape_i32 + shape_size(inputs[0]->get_shape()),
out_shape_uint64.begin(),
[](const int32_t& elem) {
return static_cast<uint64_t>(elem);
});
out_shape = out_shape_uint64.data();
} else if (inputs[0]->get_element_type() == element::Type_t::i64) {
auto out_shape_i64 = inputs[0]->get_data_ptr<const int64_t>();
std::transform(out_shape_i64,
out_shape_i64 + shape_size(inputs[0]->get_shape()),
out_shape_uint64.begin(),
[](const int64_t& elem) {
return static_cast<uint64_t>(elem);
});
out_shape = out_shape_uint64.data();
} else {
throw ngraph_error("Unsupported type of out shape in RandomUniform operation: " +
inputs[0]->get_element_type().get_type_name());
}
element::Type_t t_out = get_out_type();
char* out;
switch (t_out) {
case element::Type_t::i32:
out = (char*)outputs[0]->get_data_ptr<const int32_t>();
break;
case element::Type_t::i64:
out = (char*)outputs[0]->get_data_ptr<const int64_t>();
break;
case element::Type_t::f16:
out = (char*)outputs[0]->get_data_ptr<const float16>();
break;
case element::Type_t::bf16:
out = (char*)outputs[0]->get_data_ptr<const bfloat16>();
break;
case element::Type_t::f32:
out = (char*)outputs[0]->get_data_ptr<const float>();
break;
case element::Type_t::f64:
out = (char*)outputs[0]->get_data_ptr<const double>();
break;
default:
throw ngraph_error("Unsupported type of RandomUniform: " + get_out_type().get_type_name());
}
auto state = runtime::reference::random_uniform(out_shape,
inputs[1]->get_data_ptr<const char>(),
inputs[2]->get_data_ptr<const char>(),
out,
inputs[0]->get_shape(),
get_out_type(),
get_global_seed(),
get_op_seed(),
m_state);
// Update RandomUniform state
std::lock_guard<std::mutex> guard(m_state_mutex);
m_state = state;
return true;
}
bool op::v8::RandomUniform::has_evaluate() const {
NGRAPH_OP_SCOPE(v8_RandomUniform_has_evaluate);
if (get_input_element_type(0) != ngraph::element::i32 && get_input_element_type(0) != ngraph::element::i64) {
return false;
}
switch (get_out_type()) {
case ngraph::element::i32:
case ngraph::element::i64:
case ngraph::element::f16:
case ngraph::element::bf16:
case ngraph::element::f32:
case ngraph::element::f64:
return true;
default:
break;
}
return false;
}