Add support for ONNX RandomNormal and RandomNormalLike operators (#8024)
Co-authored-by: Tomasz Dołbniak <tomasz.dolbniak@intel.com> Co-authored-by: Tomasz Jankowski <tomasz1.jankowski@intel.com>
This commit is contained in:
36
ngraph/frontend/onnx/frontend/src/op/random_normal.cpp
Normal file
36
ngraph/frontend/onnx/frontend/src/op/random_normal.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (C) 2018-2021 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "utils/random_normal.hpp"
|
||||
|
||||
#include "exceptions.hpp"
|
||||
#include "ngraph/shape.hpp"
|
||||
#include "utils/common.hpp"
|
||||
|
||||
namespace ngraph {
|
||||
namespace onnx_import {
|
||||
namespace op {
|
||||
namespace set_1 {
|
||||
|
||||
OutputVector random_normal(const Node& node) {
|
||||
CHECK_VALID_NODE(node, node.has_attribute("shape"), "RandomNormal operator must specify a 'shape' attribute.");
|
||||
|
||||
const auto dtype =
|
||||
node.get_attribute_value<int64_t>("dtype", static_cast<int64_t>(ONNX_NAMESPACE::TensorProto_DataType_FLOAT));
|
||||
const auto target_type = common::get_ngraph_element_type(dtype);
|
||||
|
||||
const auto mean = node.get_attribute_value<float>("mean", 0.0f);
|
||||
const auto scale = node.get_attribute_value<float>("scale", 1.0f);
|
||||
const auto seed = node.get_attribute_value<float>("seed", 0);
|
||||
|
||||
const auto shape_dims = node.get_attribute_value<std::vector<int64_t>>("shape");
|
||||
const auto shape = default_opset::Constant::create(element::i64, {shape_dims.size()}, shape_dims);
|
||||
|
||||
return detail::make_random_normal(shape, target_type, mean, scale, seed);
|
||||
}
|
||||
|
||||
} // namespace set_1
|
||||
} // namespace op
|
||||
} // namespace onnx_import
|
||||
} // namespace ngraph
|
||||
20
ngraph/frontend/onnx/frontend/src/op/random_normal.hpp
Normal file
20
ngraph/frontend/onnx/frontend/src/op/random_normal.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (C) 2018-2021 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ngraph/node.hpp"
|
||||
#include "onnx_import/core/node.hpp"
|
||||
|
||||
namespace ngraph {
|
||||
namespace onnx_import {
|
||||
namespace op {
|
||||
namespace set_1 {
|
||||
|
||||
OutputVector random_normal(const Node& node);
|
||||
|
||||
} // namespace set_1
|
||||
} // namespace op
|
||||
} // namespace onnx_import
|
||||
} // namespace ngraph
|
||||
37
ngraph/frontend/onnx/frontend/src/op/random_normal_like.cpp
Normal file
37
ngraph/frontend/onnx/frontend/src/op/random_normal_like.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (C) 2018-2021 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "ngraph/shape.hpp"
|
||||
#include "op/random_uniform_like.hpp"
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/random_normal.hpp"
|
||||
|
||||
namespace ngraph {
|
||||
namespace onnx_import {
|
||||
namespace op {
|
||||
namespace set_1 {
|
||||
|
||||
OutputVector random_normal_like(const Node& node) {
|
||||
const auto input = node.get_ng_inputs().at(0);
|
||||
|
||||
ngraph::element::Type target_type;
|
||||
if (node.has_attribute("dtype")) {
|
||||
const auto dtype = node.get_attribute_value<int64_t>("dtype");
|
||||
target_type = common::get_ngraph_element_type(dtype);
|
||||
} else {
|
||||
target_type = input.get_element_type();
|
||||
}
|
||||
|
||||
const auto shape = std::make_shared<default_opset::ShapeOf>(input);
|
||||
const auto mean = node.get_attribute_value<float>("mean", 0.0f);
|
||||
const auto scale = node.get_attribute_value<float>("scale", 1.0f);
|
||||
const auto seed = node.get_attribute_value<float>("seed", 0.0f);
|
||||
|
||||
return detail::make_random_normal(shape, target_type, mean, scale, seed);
|
||||
}
|
||||
|
||||
} // namespace set_1
|
||||
} // namespace op
|
||||
} // namespace onnx_import
|
||||
} // namespace ngraph
|
||||
20
ngraph/frontend/onnx/frontend/src/op/random_normal_like.hpp
Normal file
20
ngraph/frontend/onnx/frontend/src/op/random_normal_like.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (C) 2018-2021 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ngraph/node.hpp"
|
||||
#include "onnx_import/core/node.hpp"
|
||||
|
||||
namespace ngraph {
|
||||
namespace onnx_import {
|
||||
namespace op {
|
||||
namespace set_1 {
|
||||
|
||||
OutputVector random_normal_like(const Node& node);
|
||||
|
||||
} // namespace set_1
|
||||
} // namespace op
|
||||
} // namespace onnx_import
|
||||
} // namespace ngraph
|
||||
@@ -23,7 +23,7 @@ OutputVector random_uniform(const Node& node) {
|
||||
node.get_attribute_value<int64_t>("dtype", static_cast<int64_t>(ONNX_NAMESPACE::TensorProto_DataType_FLOAT));
|
||||
const auto high = node.get_attribute_value<float>("high", 1.0f);
|
||||
const auto low = node.get_attribute_value<float>("low", 0.0f);
|
||||
const auto seed = node.get_attribute_value<int64_t>("seed", 0);
|
||||
const auto seed = node.get_attribute_value<float>("seed", 0.0f);
|
||||
const auto shape = node.get_attribute_value<std::vector<int64_t>>("shape");
|
||||
|
||||
const auto target_shape_const = default_opset::Constant::create(ngraph::element::i64, Shape{shape.size()}, shape);
|
||||
@@ -32,19 +32,17 @@ OutputVector random_uniform(const Node& node) {
|
||||
|
||||
const auto target_type = common::get_ngraph_element_type(dtype);
|
||||
const uint64_t global_seed = 0;
|
||||
const auto seed_uint64 = static_cast<uint64_t>(seed * 1000);
|
||||
|
||||
return {std::make_shared<ngraph::opset8::RandomUniform>(target_shape_const,
|
||||
low_const,
|
||||
high_const,
|
||||
target_type,
|
||||
global_seed,
|
||||
seed)};
|
||||
seed_uint64)};
|
||||
}
|
||||
|
||||
} // namespace set_1
|
||||
|
||||
} // namespace op
|
||||
|
||||
} // namespace onnx_import
|
||||
|
||||
} // namespace ngraph
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace set_1 {
|
||||
|
||||
OutputVector random_uniform_like(const Node& node) {
|
||||
OutputVector inputs{node.get_ng_inputs()};
|
||||
auto input = inputs.at(0);
|
||||
const auto input = inputs.at(0);
|
||||
|
||||
ngraph::element::Type target_type;
|
||||
if (node.has_attribute("dtype")) {
|
||||
@@ -32,25 +32,23 @@ OutputVector random_uniform_like(const Node& node) {
|
||||
|
||||
const auto high = node.get_attribute_value<float>("high", 1.0f);
|
||||
const auto low = node.get_attribute_value<float>("low", 0.0f);
|
||||
const auto seed = node.get_attribute_value<int64_t>("seed", 0);
|
||||
const auto seed = node.get_attribute_value<float>("seed", 0.f);
|
||||
|
||||
const auto high_const = default_opset::Constant::create(ngraph::element::f32, Shape{1}, {high});
|
||||
const auto low_const = default_opset::Constant::create(ngraph::element::f32, Shape{1}, {low});
|
||||
|
||||
const uint64_t global_seed = 0;
|
||||
const auto seed_uint64 = static_cast<uint64_t>(seed * 1000);
|
||||
|
||||
return {std::make_shared<ngraph::opset8::RandomUniform>(target_shape,
|
||||
low_const,
|
||||
high_const,
|
||||
target_type,
|
||||
global_seed,
|
||||
seed)};
|
||||
seed_uint64)};
|
||||
}
|
||||
|
||||
} // namespace set_1
|
||||
|
||||
} // namespace op
|
||||
|
||||
} // namespace onnx_import
|
||||
|
||||
} // namespace ngraph
|
||||
|
||||
@@ -116,6 +116,8 @@
|
||||
#include "op/qlinear_conv.hpp"
|
||||
#include "op/qlinear_matmul.hpp"
|
||||
#include "op/quantize_linear.hpp"
|
||||
#include "op/random_normal.hpp"
|
||||
#include "op/random_normal_like.hpp"
|
||||
#include "op/random_uniform.hpp"
|
||||
#include "op/random_uniform_like.hpp"
|
||||
#include "op/range.hpp"
|
||||
@@ -384,6 +386,8 @@ OperatorsBridge::OperatorsBridge() {
|
||||
REGISTER_OPERATOR("QuantizeLinear", 1, quantize_linear);
|
||||
REGISTER_OPERATOR("QuantizeLinear", 13, quantize_linear);
|
||||
REGISTER_OPERATOR("Range", 1, range);
|
||||
REGISTER_OPERATOR("RandomNormal", 1, random_normal);
|
||||
REGISTER_OPERATOR("RandomNormalLike", 1, random_normal_like);
|
||||
REGISTER_OPERATOR("RandomUniform", 1, random_uniform);
|
||||
REGISTER_OPERATOR("RandomUniformLike", 1, random_uniform_like);
|
||||
REGISTER_OPERATOR("Reciprocal", 1, reciprocal);
|
||||
|
||||
62
ngraph/frontend/onnx/frontend/src/utils/random_normal.cpp
Normal file
62
ngraph/frontend/onnx/frontend/src/utils/random_normal.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright (C) 2018-2021 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "random_normal.hpp"
|
||||
|
||||
#include "default_opset.hpp"
|
||||
#include "ngraph/opsets/opset8.hpp"
|
||||
|
||||
namespace ngraph {
|
||||
namespace onnx_import {
|
||||
namespace detail {
|
||||
|
||||
OutputVector make_random_normal(const Output<ngraph::Node>& shape,
|
||||
element::Type target_type,
|
||||
float mean,
|
||||
float scale,
|
||||
float seed) {
|
||||
// We start by generating two random series from a uniform distribution
|
||||
const uint64_t global_seed = 0;
|
||||
|
||||
// ONNX specifies the seed as a float, but OpenVINO uses uint64_t
|
||||
const auto op_seed = static_cast<uint64_t>(seed * 1000);
|
||||
|
||||
// We need to use two op_seeds to make sure we get different results for two RandomUniform series
|
||||
const uint64_t seed_1 = (op_seed == 0 ? rand() % 10000 : op_seed);
|
||||
const uint64_t seed_2 = (op_seed == 0 ? rand() % 10000 : op_seed + 10000);
|
||||
|
||||
const auto min_val = default_opset::Constant::create(target_type, Shape{1}, {0});
|
||||
const auto max_val = default_opset::Constant::create(target_type, Shape{1}, {1});
|
||||
|
||||
const auto uniform_1 =
|
||||
std::make_shared<ngraph::opset8::RandomUniform>(shape, min_val, max_val, target_type, global_seed, seed_1);
|
||||
const auto uniform_2 =
|
||||
std::make_shared<ngraph::opset8::RandomUniform>(shape, min_val, max_val, target_type, global_seed, seed_2);
|
||||
|
||||
// Compute Box–Muller transform
|
||||
// random_normal = scale * ng.sqrt(-2.0 * ng.log(uniform_1)) * ng.cos(2.0 * np.pi * uniform_2) + mean
|
||||
const auto pi = default_opset::Constant::create(target_type, Shape{1}, {3.141592653589793});
|
||||
const auto minus_two = default_opset::Constant::create(target_type, Shape{1}, {-2.0});
|
||||
const auto two = default_opset::Constant::create(target_type, Shape{1}, {2.0});
|
||||
|
||||
const auto log = std::make_shared<default_opset::Log>(uniform_1);
|
||||
const auto multiply_minus_two_log = std::make_shared<default_opset::Multiply>(log, minus_two);
|
||||
const auto sqrt = std::make_shared<default_opset::Sqrt>(multiply_minus_two_log);
|
||||
|
||||
const auto multiply_two_pi = std::make_shared<default_opset::Multiply>(uniform_2, pi);
|
||||
const auto multiply_two_pi_uniform_2 = std::make_shared<default_opset::Multiply>(multiply_two_pi, uniform_2);
|
||||
auto const cos = std::make_shared<default_opset::Cos>(multiply_two_pi_uniform_2);
|
||||
|
||||
auto const scale_const = default_opset::Constant::create(target_type, Shape{1}, {scale});
|
||||
auto const mean_const = default_opset::Constant::create(target_type, Shape{1}, {mean});
|
||||
auto const product =
|
||||
std::make_shared<default_opset::Multiply>(scale_const, std::make_shared<default_opset::Multiply>(sqrt, cos));
|
||||
auto const sum = std::make_shared<default_opset::Add>(product, mean_const);
|
||||
|
||||
return {sum};
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace onnx_import
|
||||
} // namespace ngraph
|
||||
29
ngraph/frontend/onnx/frontend/src/utils/random_normal.hpp
Normal file
29
ngraph/frontend/onnx/frontend/src/utils/random_normal.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (C) 2018-2021 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ngraph/op/reshape.hpp"
|
||||
#include "ngraph/output_vector.hpp"
|
||||
|
||||
namespace ngraph {
|
||||
namespace onnx_import {
|
||||
namespace detail {
|
||||
|
||||
/// \brief Creates a random normal tensor with the given shape and type.
|
||||
/// \details Uses Box-Mueller algorithm to generate random numbers from a Gauassian distribution
|
||||
/// \param shape Shape of the output tensor
|
||||
/// \param type Type of the output tensor
|
||||
/// \param mean Mean of the distribution
|
||||
/// \param scale Standard deviation of the distribution
|
||||
/// \param seed Seed for the random number generator
|
||||
OutputVector make_random_normal(const Output<ngraph::Node>& shape,
|
||||
element::Type type,
|
||||
float mean,
|
||||
float scale,
|
||||
float seed);
|
||||
|
||||
} // namespace detail
|
||||
} // namespace onnx_import
|
||||
} // namespace ngraph
|
||||
49
ngraph/test/models/onnx/random_normal.prototxt
Normal file
49
ngraph/test/models/onnx/random_normal.prototxt
Normal file
@@ -0,0 +1,49 @@
|
||||
ir_version: 3
|
||||
producer_name: "nGraph ONNX Importer"
|
||||
graph {
|
||||
node {
|
||||
output: "y"
|
||||
op_type: "RandomNormal"
|
||||
attribute {
|
||||
name: "shape"
|
||||
ints: 2
|
||||
ints: 2
|
||||
type: INTS
|
||||
}
|
||||
attribute {
|
||||
name: "mean"
|
||||
f: 50
|
||||
type: FLOAT
|
||||
}
|
||||
attribute {
|
||||
name: "scale"
|
||||
f: 40
|
||||
type: FLOAT
|
||||
}
|
||||
attribute {
|
||||
name: "seed"
|
||||
i: 100
|
||||
type: INT
|
||||
}
|
||||
}
|
||||
name: "test_model"
|
||||
output {
|
||||
name: "y"
|
||||
type {
|
||||
tensor_type {
|
||||
elem_type: 1
|
||||
shape {
|
||||
dim {
|
||||
dim_value: 2
|
||||
}
|
||||
dim {
|
||||
dim_value: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
opset_import {
|
||||
version: 1
|
||||
}
|
||||
60
ngraph/test/models/onnx/random_normal_like.prototxt
Normal file
60
ngraph/test/models/onnx/random_normal_like.prototxt
Normal file
@@ -0,0 +1,60 @@
|
||||
ir_version: 3
|
||||
producer_name: "nGraph ONNX Importer"
|
||||
graph {
|
||||
node {
|
||||
input: "x"
|
||||
output: "y"
|
||||
op_type: "RandomNormalLike"
|
||||
attribute {
|
||||
name: "mean"
|
||||
f: 50
|
||||
type: FLOAT
|
||||
}
|
||||
attribute {
|
||||
name: "scale"
|
||||
f: 40
|
||||
type: FLOAT
|
||||
}
|
||||
attribute {
|
||||
name: "seed"
|
||||
i: 100
|
||||
type: INT
|
||||
}
|
||||
}
|
||||
name: "test_model"
|
||||
input {
|
||||
name: "x"
|
||||
type {
|
||||
tensor_type {
|
||||
elem_type: 1
|
||||
shape {
|
||||
dim {
|
||||
dim_value: 2
|
||||
}
|
||||
dim {
|
||||
dim_value: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
output {
|
||||
name: "y"
|
||||
type {
|
||||
tensor_type {
|
||||
elem_type: 1
|
||||
shape {
|
||||
dim {
|
||||
dim_value: 2
|
||||
}
|
||||
dim {
|
||||
dim_value: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
opset_import {
|
||||
version: 1
|
||||
}
|
||||
@@ -22,8 +22,8 @@ graph {
|
||||
}
|
||||
attribute {
|
||||
name: "seed"
|
||||
i: 100
|
||||
type: INT
|
||||
f: 100
|
||||
type: FLOAT
|
||||
}
|
||||
}
|
||||
name: "test_model"
|
||||
|
||||
@@ -17,8 +17,8 @@ graph {
|
||||
}
|
||||
attribute {
|
||||
name: "seed"
|
||||
i: 100
|
||||
type: INT
|
||||
f: 100
|
||||
type: FLOAT
|
||||
}
|
||||
}
|
||||
name: "test_model"
|
||||
|
||||
@@ -4170,9 +4170,7 @@ NGRAPH_TEST(${BACKEND_NAME}, onnx_model_random_uniform) {
|
||||
onnx_import::import_onnx_model(file_util::path_join(SERIALIZED_ZOO, "onnx/random_uniform.onnx"));
|
||||
|
||||
auto test_case = test::TestCase<TestEngine>(function);
|
||||
// These output values are unknown at this time as we don't have a reference implementation of random number
|
||||
// generator
|
||||
test_case.add_expected_output<ngraph::float16>(Shape{2, 2}, {41, 42, 43, 44});
|
||||
test_case.add_expected_output<float>(Shape{2, 2}, {43.45518, 48.67585, 42.227386, 40.86294});
|
||||
test_case.run();
|
||||
}
|
||||
|
||||
@@ -4181,10 +4179,26 @@ NGRAPH_TEST(${BACKEND_NAME}, onnx_model_random_uniform_like) {
|
||||
onnx_import::import_onnx_model(file_util::path_join(SERIALIZED_ZOO, "onnx/random_uniform_like.onnx"));
|
||||
|
||||
auto test_case = test::TestCase<TestEngine>(function);
|
||||
test_case.add_expected_output<ngraph::float16>(Shape{2, 2}, {0, 0, 0, 0});
|
||||
|
||||
// These output values are unknown at this time as we don't have a reference implementation of random number
|
||||
// generator
|
||||
test_case.add_input<ngraph::float16>(Shape{2, 2}, {41, 42, 43, 44});
|
||||
test_case.add_input<float>(Shape{2, 2}, {41, 42, 43, 44});
|
||||
test_case.add_expected_output<float>(Shape{2, 2}, {43.45518, 48.67585, 42.227386, 40.86294});
|
||||
test_case.run();
|
||||
}
|
||||
|
||||
NGRAPH_TEST(${BACKEND_NAME}, onnx_model_random_normal) {
|
||||
const auto function =
|
||||
onnx_import::import_onnx_model(file_util::path_join(SERIALIZED_ZOO, "onnx/random_normal.onnx"));
|
||||
|
||||
auto test_case = test::TestCase<TestEngine>(function);
|
||||
test_case.add_expected_output<float>(Shape{2, 2}, {13.459274, 41.75028, -19.311913, 131.79282});
|
||||
test_case.run();
|
||||
}
|
||||
|
||||
NGRAPH_TEST(${BACKEND_NAME}, onnx_model_random_normal_like) {
|
||||
const auto function =
|
||||
onnx_import::import_onnx_model(file_util::path_join(SERIALIZED_ZOO, "onnx/random_normal_like.onnx"));
|
||||
|
||||
auto test_case = test::TestCase<TestEngine>(function);
|
||||
test_case.add_input<float>(Shape{2, 2}, {0, 0, 0, 0});
|
||||
test_case.add_expected_output<float>(Shape{2, 2}, {13.459274, 41.75028, -19.311913, 131.79282});
|
||||
test_case.run();
|
||||
}
|
||||
|
||||
@@ -24,9 +24,6 @@ IE_CPU.onnx_model_dequantize_linear_1d_zero_scale_int8
|
||||
# C++ exception with description "Input data precision not supported. Expected float.
|
||||
IE_CPU.onnx_model_dequantize_linear_1d_zero_scale_int8_4d
|
||||
|
||||
# No support yet for RandomUniform
|
||||
onnx_model_random_uniform
|
||||
onnx_model_random_uniform_like
|
||||
|
||||
# Result mismatch
|
||||
onnx_model_shape
|
||||
|
||||
@@ -125,7 +125,3 @@ onnx_model_deformable_conv_2d
|
||||
|
||||
# No support for unsigned types
|
||||
INTERPRETER.zero_sized_negative
|
||||
|
||||
# No support yet for RandomUniform
|
||||
INTERPRETER.onnx_model_random_uniform
|
||||
INTERPRETER.onnx_model_random_uniform_like
|
||||
|
||||
53
runtime/bindings/python/tests/test_onnx/test_ops_random.py
Normal file
53
runtime/bindings/python/tests/test_onnx/test_ops_random.py
Normal file
@@ -0,0 +1,53 @@
|
||||
# Copyright (C) 2018-2021 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import numpy as np
|
||||
import onnx
|
||||
import onnx.mapping
|
||||
|
||||
from tests.test_onnx.utils import run_node
|
||||
|
||||
|
||||
def test_random_uniform():
|
||||
low = 90.0
|
||||
high = 100.0
|
||||
|
||||
node = onnx.helper.make_node(
|
||||
"RandomUniform",
|
||||
inputs=[],
|
||||
outputs=["y"],
|
||||
high=high,
|
||||
low=low,
|
||||
seed=10.0,
|
||||
shape=(30, 30),
|
||||
)
|
||||
|
||||
result = run_node(node, [])[0]
|
||||
|
||||
assert result.shape == (30, 30)
|
||||
assert len(np.unique(result)) == 900
|
||||
assert np.max(result) < high
|
||||
assert np.min(result) > low
|
||||
assert np.isclose(np.mean(result), np.mean(np.array([low, high])), rtol=0.001)
|
||||
|
||||
|
||||
def test_random_normal():
|
||||
mean = 100.0
|
||||
scale = 10.0
|
||||
|
||||
node = onnx.helper.make_node(
|
||||
"RandomNormal",
|
||||
inputs=[],
|
||||
outputs=["y"],
|
||||
mean=mean,
|
||||
scale=scale,
|
||||
seed=10.0,
|
||||
shape=(30, 30),
|
||||
)
|
||||
|
||||
result = run_node(node, [])[0]
|
||||
|
||||
assert result.shape == (30, 30)
|
||||
assert len(np.unique(result)) == 900
|
||||
assert np.allclose(np.mean(result), mean, rtol=0.05)
|
||||
assert np.allclose(np.std(result), scale, rtol=0.05)
|
||||
Reference in New Issue
Block a user