summaryrefslogtreecommitdiff
path: root/nn/runtime/test
diff options
context:
space:
mode:
authorXusong Wang <xusongw@google.com>2020-03-11 17:53:08 -0700
committerXusong Wang <xusongw@google.com>2020-04-07 09:53:20 -0700
commitcd321da9fd9c50ff3bea39857945bcf30809f262 (patch)
tree8674c92a82c60b99b21d69f688f638f1be6b8155 /nn/runtime/test
parent10f4d020579c0e0e4d945ffe2f9cc62d392c344e (diff)
downloadml-cd321da9fd9c50ff3bea39857945bcf30809f262.tar.gz
Prevent RGG from generating NaN values.
This CL introduces "ValueProperty" to RGG, which defines the required properties of input values as well as the guaranteed properties of the output values. During the graph generation process, an operation input may only be wired to an operation output with compatible properties. E.g., the input of SQRT will not come from CONV_2D because CONV_2D cannot guarantee non-negative output values; while it could come from RELU because RELU guarantees that the output values are always non-negative. Additionally, fixes the issue that REDUCE_PROD may generate NaN values because 0 * inf evaluates to NaN in C++. Bug: 145188288 Bug: 150805665 Test: NNT_static_fuzzing Change-Id: I5464233839a91afbe2022b8fe603e4296825a7a4
Diffstat (limited to 'nn/runtime/test')
-rw-r--r--nn/runtime/test/fuzzing/RandomGraphGenerator.cpp7
-rw-r--r--nn/runtime/test/fuzzing/RandomGraphGenerator.h10
-rw-r--r--nn/runtime/test/fuzzing/RandomGraphGeneratorUtils.h9
-rw-r--r--nn/runtime/test/fuzzing/TestRandomGraph.cpp9
-rw-r--r--nn/runtime/test/fuzzing/operation_signatures/Broadcast.cpp10
-rw-r--r--nn/runtime/test/fuzzing/operation_signatures/Elementwise.cpp66
-rw-r--r--nn/runtime/test/fuzzing/operation_signatures/OperationSignatureUtils.h55
7 files changed, 104 insertions, 62 deletions
diff --git a/nn/runtime/test/fuzzing/RandomGraphGenerator.cpp b/nn/runtime/test/fuzzing/RandomGraphGenerator.cpp
index e6f9eea9e..7307f68ca 100644
--- a/nn/runtime/test/fuzzing/RandomGraphGenerator.cpp
+++ b/nn/runtime/test/fuzzing/RandomGraphGenerator.cpp
@@ -54,11 +54,16 @@ std::vector<uint32_t> RandomOperand::getDimensions() const {
return result;
}
+static bool areValuePropertiesCompatible(int guaranteed, int required) {
+ return !(~guaranteed & required);
+}
+
// Check if an edge between [this, other] is valid. If yes, add the edge.
bool RandomOperand::createEdgeIfValid(const RandomOperand& other) const {
if (other.type != RandomOperandType::INPUT) return false;
if (dataType != other.dataType || dimensions.size() != other.dimensions.size() ||
- scale != other.scale || zeroPoint != other.zeroPoint || doNotConnect || other.doNotConnect)
+ scale != other.scale || zeroPoint != other.zeroPoint || doNotConnect ||
+ other.doNotConnect || !areValuePropertiesCompatible(valueProperties, other.valueProperties))
return false;
return RandomVariableNetwork::get()->setEqualIfCompatible(dimensions, other.dimensions);
}
diff --git a/nn/runtime/test/fuzzing/RandomGraphGenerator.h b/nn/runtime/test/fuzzing/RandomGraphGenerator.h
index c152ee62c..2a4d7f427 100644
--- a/nn/runtime/test/fuzzing/RandomGraphGenerator.h
+++ b/nn/runtime/test/fuzzing/RandomGraphGenerator.h
@@ -38,7 +38,17 @@ class OperationManager;
enum class RandomOperandType { INPUT = 0, OUTPUT = 1, INTERNAL = 2, CONST = 3, NO_VALUE = 4 };
struct RandomOperand {
+ // Describes the properties of the values of an operand. For operation inputs, this specifies
+ // what is required; for outputs, this specifies what is guaranteed.
+ // The graph generation algorithm will use this information to decide whether to wire an output
+ // to an input or not.
+ enum ValueProperty : int {
+ NON_ZERO = 1 << 0,
+ NON_NEGATIVE = 1 << 1,
+ };
+
RandomOperandType type;
+ int valueProperties = 0;
test_helper::TestOperandType dataType;
float scale = 0.0f;
int32_t zeroPoint = 0;
diff --git a/nn/runtime/test/fuzzing/RandomGraphGeneratorUtils.h b/nn/runtime/test/fuzzing/RandomGraphGeneratorUtils.h
index fff632019..163c46f7e 100644
--- a/nn/runtime/test/fuzzing/RandomGraphGeneratorUtils.h
+++ b/nn/runtime/test/fuzzing/RandomGraphGeneratorUtils.h
@@ -275,6 +275,15 @@ inline std::enable_if_t<nnIsFloat<T>, T> getUniform(T lower, T upper) {
std::uniform_real_distribution<float> dis(nextLower, upper);
return dis(RandomNumberGenerator::generator);
}
+template <typename T>
+inline std::enable_if_t<nnIsFloat<T>, T> getUniformNonZero(T lower, T upper, T zeroPoint) {
+ if (upper >= zeroPoint) {
+ upper = std::nextafter(static_cast<float>(upper), std::numeric_limits<float>::min());
+ }
+ std::uniform_real_distribution<float> dis(lower, upper);
+ const float value = dis(RandomNumberGenerator::generator);
+ return value >= zeroPoint ? std::nextafter(value, std::numeric_limits<float>::max()) : value;
+}
// getUniform for integers operates on a closed interval [lower, upper].
// This is important that 255 should be included as a valid candidate for QUANT8_ASYMM values.
diff --git a/nn/runtime/test/fuzzing/TestRandomGraph.cpp b/nn/runtime/test/fuzzing/TestRandomGraph.cpp
index 7facc363a..7aece223b 100644
--- a/nn/runtime/test/fuzzing/TestRandomGraph.cpp
+++ b/nn/runtime/test/fuzzing/TestRandomGraph.cpp
@@ -210,8 +210,13 @@ class RandomGraphTest : public ::testing::TestWithParam<uint32_t> {
featureLevel <= __ANDROID_API_Q__) {
return true;
}
- // TODO(xusongw): Remove after b/151328024 is resolved.
- if (op.type == TestOperationType::ROI_ALIGN) {
+ // TODO(xusongw): Remove after b/151328024, b/152446228, b/152445711, and b/152446298
+ // are resolved.
+ if ((op.type == TestOperationType::ROI_ALIGN || op.type == TestOperationType::ADD ||
+ op.type == TestOperationType::SUB || op.type == TestOperationType::MAXIMUM ||
+ op.type == TestOperationType::MINIMUM) &&
+ mTestModel.main.operands[op.inputs[0]].type ==
+ TestOperandType::TENSOR_QUANT8_ASYMM) {
return true;
}
}
diff --git a/nn/runtime/test/fuzzing/operation_signatures/Broadcast.cpp b/nn/runtime/test/fuzzing/operation_signatures/Broadcast.cpp
index 4dbad7b81..5c0f64920 100644
--- a/nn/runtime/test/fuzzing/operation_signatures/Broadcast.cpp
+++ b/nn/runtime/test/fuzzing/operation_signatures/Broadcast.cpp
@@ -71,10 +71,12 @@ static void broadcastOpConstructor(TestOperandType dataType, uint32_t rank, Rand
op->inputs[2]->setScalarValue(0);
}
- // TODO(b/151151830): TENSOR_INT32 DIV will crash with 0 as divisor.
- if (op->opType == TestOperationType::DIV && dataType == TestOperandType::TENSOR_INT32) {
- op->inputs[1]->doNotConnect = true;
- op->inputs[1]->finalizer = nonZeroUniformFinalizer;
+ if (op->opType == TestOperationType::DIV) {
+ op->inputs[1]->valueProperties = RandomOperand::NON_ZERO;
+ }
+
+ if (op->opType == TestOperationType::POW) {
+ op->inputs[0]->valueProperties = RandomOperand::NON_NEGATIVE;
}
}
diff --git a/nn/runtime/test/fuzzing/operation_signatures/Elementwise.cpp b/nn/runtime/test/fuzzing/operation_signatures/Elementwise.cpp
index 1c07497b0..567ff0581 100644
--- a/nn/runtime/test/fuzzing/operation_signatures/Elementwise.cpp
+++ b/nn/runtime/test/fuzzing/operation_signatures/Elementwise.cpp
@@ -20,6 +20,39 @@ namespace android {
namespace nn {
namespace fuzzing_test {
+static void elementwiseOpConstructor(TestOperandType dataType, uint32_t rank, RandomOperation* op) {
+ sameShapeOpConstructor(dataType, rank, op);
+
+ switch (op->opType) {
+ case TestOperationType::RELU:
+ case TestOperationType::RELU6:
+ op->outputs[0]->valueProperties = RandomOperand::NON_NEGATIVE;
+ break;
+ case TestOperationType::LOGISTIC:
+ op->outputs[0]->valueProperties = RandomOperand::NON_ZERO | RandomOperand::NON_NEGATIVE;
+ break;
+ case TestOperationType::ABS:
+ op->outputs[0]->valueProperties = RandomOperand::NON_NEGATIVE;
+ break;
+ case TestOperationType::EXP:
+ op->outputs[0]->valueProperties = RandomOperand::NON_ZERO | RandomOperand::NON_NEGATIVE;
+ break;
+ case TestOperationType::LOG:
+ op->inputs[0]->valueProperties = RandomOperand::NON_ZERO | RandomOperand::NON_NEGATIVE;
+ break;
+ case TestOperationType::RSQRT:
+ op->inputs[0]->valueProperties = RandomOperand::NON_ZERO | RandomOperand::NON_NEGATIVE;
+ op->outputs[0]->valueProperties = RandomOperand::NON_ZERO | RandomOperand::NON_NEGATIVE;
+ break;
+ case TestOperationType::SQRT:
+ op->inputs[0]->valueProperties = RandomOperand::NON_NEGATIVE;
+ op->outputs[0]->valueProperties = RandomOperand::NON_NEGATIVE;
+ break;
+ default:
+ break;
+ }
+}
+
#define DEFINE_ELEMENTWISE_SIGNATURE(op, ver, ...) \
DEFINE_OPERATION_SIGNATURE(op##_##ver){.opType = TestOperationType::op, \
.supportedDataTypes = {__VA_ARGS__}, \
@@ -27,7 +60,7 @@ namespace fuzzing_test {
.version = TestHalVersion::ver, \
.inputs = {INPUT_DEFAULT}, \
.outputs = {OUTPUT_DEFAULT}, \
- .constructor = sameShapeOpConstructor};
+ .constructor = elementwiseOpConstructor};
DEFINE_ELEMENTWISE_SIGNATURE(FLOOR, V1_0, TestOperandType::TENSOR_FLOAT32);
DEFINE_ELEMENTWISE_SIGNATURE(RELU, V1_0, TestOperandType::TENSOR_FLOAT32,
@@ -55,7 +88,7 @@ DEFINE_ELEMENTWISE_SIGNATURE(HARD_SWISH, V1_3, TestOperandType::TENSOR_FLOAT32,
.version = TestHalVersion::ver, \
.inputs = {INPUT_DEFAULT}, \
.outputs = {OUTPUT_DEFAULT}, \
- .constructor = sameShapeOpConstructor};
+ .constructor = elementwiseOpConstructor};
DEFINE_ELEMENTWISE_SIGNATURE_WITH_RANK5(ABS, V1_2, TestOperandType::TENSOR_FLOAT32,
TestOperandType::TENSOR_FLOAT16);
@@ -69,29 +102,12 @@ DEFINE_ELEMENTWISE_SIGNATURE_WITH_RANK5(SIN, V1_2, TestOperandType::TENSOR_FLOAT
DEFINE_ELEMENTWISE_SIGNATURE_WITH_RANK5(LOGICAL_NOT, V1_2, TestOperandType::TENSOR_BOOL8);
DEFINE_ELEMENTWISE_SIGNATURE_WITH_RANK5(ABS, V1_3, TestOperandType::TENSOR_INT32);
-// LOG, SQRT, and RSQRT may produce NaN output values. We should not connect the output tensor to
-// the input of another operation.
-static void elementwiseOpWithDisconnectedOutput(TestOperandType type, uint32_t rank,
- RandomOperation* op) {
- sameShapeOpConstructor(type, rank, op);
- op->outputs[0]->doNotConnect = true;
-}
-
-#define DEFINE_ELEMENTWISE_SIGNATURE_WITH_DISCONNECTED_OUTPUT(op, ver, ...) \
- DEFINE_OPERATION_SIGNATURE(op##_##ver){.opType = TestOperationType::op, \
- .supportedDataTypes = {__VA_ARGS__}, \
- .supportedRanks = {1, 2, 3, 4, 5}, \
- .version = TestHalVersion::ver, \
- .inputs = {INPUT_DEFAULT}, \
- .outputs = {OUTPUT_DEFAULT}, \
- .constructor = elementwiseOpWithDisconnectedOutput};
-
-DEFINE_ELEMENTWISE_SIGNATURE_WITH_DISCONNECTED_OUTPUT(LOG, V1_2, TestOperandType::TENSOR_FLOAT32,
- TestOperandType::TENSOR_FLOAT16);
-DEFINE_ELEMENTWISE_SIGNATURE_WITH_DISCONNECTED_OUTPUT(RSQRT, V1_2, TestOperandType::TENSOR_FLOAT32,
- TestOperandType::TENSOR_FLOAT16);
-DEFINE_ELEMENTWISE_SIGNATURE_WITH_DISCONNECTED_OUTPUT(SQRT, V1_2, TestOperandType::TENSOR_FLOAT32,
- TestOperandType::TENSOR_FLOAT16);
+DEFINE_ELEMENTWISE_SIGNATURE_WITH_RANK5(LOG, V1_2, TestOperandType::TENSOR_FLOAT32,
+ TestOperandType::TENSOR_FLOAT16);
+DEFINE_ELEMENTWISE_SIGNATURE_WITH_RANK5(RSQRT, V1_2, TestOperandType::TENSOR_FLOAT32,
+ TestOperandType::TENSOR_FLOAT16);
+DEFINE_ELEMENTWISE_SIGNATURE_WITH_RANK5(SQRT, V1_2, TestOperandType::TENSOR_FLOAT32,
+ TestOperandType::TENSOR_FLOAT16);
// Quantized operations with special output quantization parameters.
#define DEFINE_ELEMENTWISE_WITH_QUANT_OUTPUT_SIGNATURE(op, ver, s, z, ...) \
diff --git a/nn/runtime/test/fuzzing/operation_signatures/OperationSignatureUtils.h b/nn/runtime/test/fuzzing/operation_signatures/OperationSignatureUtils.h
index ebb3b9f0d..74a5ae4e3 100644
--- a/nn/runtime/test/fuzzing/operation_signatures/OperationSignatureUtils.h
+++ b/nn/runtime/test/fuzzing/operation_signatures/OperationSignatureUtils.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TEST_FUZZING_OPERATION_SIGNATURES_OPERATION_SIGNATURE_UTILS_H
#define ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TEST_FUZZING_OPERATION_SIGNATURES_OPERATION_SIGNATURE_UTILS_H
+#include <algorithm>
#include <functional>
#include <memory>
#include <string>
@@ -100,23 +101,28 @@ constexpr float kMaxFloat32 = 2.0f;
constexpr float kMinFloat32 = -kMaxFloat32;
template <typename T>
-inline void uniform(T low, T up, RandomOperand* op) {
- T* data = reinterpret_cast<T*>(op->buffer.data());
- uint32_t len = op->getNumberOfElements();
- for (uint32_t i = 0; i < len; i++) data[i] = getUniform(low, up);
+inline T getUniformValue(int valueProperties, T low, T up, T zeroPoint) {
+ if (valueProperties & RandomOperand::NON_NEGATIVE) {
+ NN_FUZZER_CHECK(up >= zeroPoint);
+ low = std::max(low, zeroPoint);
+ }
+ if (valueProperties & RandomOperand::NON_ZERO) {
+ return getUniformNonZero(low, up, zeroPoint);
+ } else {
+ return getUniform(low, up);
+ }
}
template <>
-inline void uniform<bool8>(bool8, bool8, RandomOperand* op) {
- bool8* data = reinterpret_cast<bool8*>(op->buffer.data());
- uint32_t len = op->getNumberOfElements();
- for (uint32_t i = 0; i < len; i++) data[i] = getBernoulli(0.5f);
+inline bool8 getUniformValue(int, bool8, bool8, bool8) {
+ return getBernoulli(0.5f);
}
+
template <typename T>
-inline void uniformNonZero(T low, T up, T zeroPoint, RandomOperand* op) {
+inline void uniform(T low, T up, T zeroPoint, RandomOperand* op) {
T* data = reinterpret_cast<T*>(op->buffer.data());
uint32_t len = op->getNumberOfElements();
for (uint32_t i = 0; i < len; i++) {
- data[i] = getUniformNonZero(low, up, zeroPoint);
+ data[i] = getUniformValue<T>(op->valueProperties, low, up, zeroPoint);
}
}
@@ -126,44 +132,33 @@ inline void uniformFinalizer(RandomOperand* op) {
switch (op->dataType) {
case TestOperandType::TENSOR_FLOAT32:
case TestOperandType::FLOAT32:
- uniform<float>(kMinFloat32, kMaxFloat32, op);
+ uniform<float>(kMinFloat32, kMaxFloat32, 0.0f, op);
break;
case TestOperandType::TENSOR_INT32:
case TestOperandType::INT32:
- uniform<int32_t>(0, 255, op);
+ uniform<int32_t>(0, 255, op->zeroPoint, op);
break;
case TestOperandType::TENSOR_QUANT8_ASYMM:
- uniform<uint8_t>(0, 255, op);
+ uniform<uint8_t>(0, 255, op->zeroPoint, op);
break;
case TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED:
- uniform<int8_t>(-128, 127, op);
+ uniform<int8_t>(-128, 127, op->zeroPoint, op);
break;
case TestOperandType::TENSOR_QUANT8_SYMM:
- uniform<int8_t>(-128, 127, op);
+ uniform<int8_t>(-128, 127, op->zeroPoint, op);
break;
case TestOperandType::TENSOR_QUANT16_ASYMM:
- uniform<uint16_t>(0, 65535, op);
+ uniform<uint16_t>(0, 65535, op->zeroPoint, op);
break;
case TestOperandType::TENSOR_QUANT16_SYMM:
- uniform<int16_t>(-32768, 32767, op);
+ uniform<int16_t>(-32768, 32767, op->zeroPoint, op);
break;
case TestOperandType::TENSOR_BOOL8:
- uniform<bool8>(true, false, op);
+ uniform<bool8>(true, false, false, op);
break;
case TestOperandType::TENSOR_FLOAT16:
case TestOperandType::FLOAT16:
- uniform<_Float16>(kMinFloat32, kMaxFloat32, op);
- break;
- default:
- NN_FUZZER_CHECK(false) << "Unsupported data type.";
- }
-}
-
-// Generate non-zero random buffer values with uniform distribution.
-inline void nonZeroUniformFinalizer(RandomOperand* op) {
- switch (op->dataType) {
- case TestOperandType::TENSOR_INT32:
- uniformNonZero<int32_t>(0, 255, op->zeroPoint, op);
+ uniform<_Float16>(kMinFloat32, kMaxFloat32, 0.0f, op);
break;
default:
NN_FUZZER_CHECK(false) << "Unsupported data type.";