From 20b7f24c921bd47d16c89127802b5e9203b3f68a Mon Sep 17 00:00:00 2001 From: Xusong Wang Date: Thu, 2 Apr 2020 11:26:27 -0700 Subject: Increase the fp precision in dumped spec. By default, C++ will output 6 fractional digits of floating point values. This is insufficient for some of the RGG tests that requires precision < 1e-6. This CL uses hex representation for fp values in the generated spec file, while human-readable values are also dumped as a comment. Fixes: 144788409 Test: NNT_static_fuzzing and inspected the dumped spec Change-Id: I43ed11e9f73cb387ed8894713fe79361322315ba --- .../test_generator/test_harness/TestHarness.cpp | 98 ++++++++++++++++++---- .../test_harness/include/TestHarness.h | 5 +- 2 files changed, 84 insertions(+), 19 deletions(-) (limited to 'nn') diff --git a/nn/tools/test_generator/test_harness/TestHarness.cpp b/nn/tools/test_generator/test_harness/TestHarness.cpp index f37332789..56e1414fe 100644 --- a/nn/tools/test_generator/test_harness/TestHarness.cpp +++ b/nn/tools/test_generator/test_harness/TestHarness.cpp @@ -327,6 +327,21 @@ bool isQuantizedType(TestOperandType type) { return kQuantizedTypes.count(type) > 0; } +bool isFloatType(TestOperandType type) { + static const std::set kFloatTypes = { + TestOperandType::TENSOR_FLOAT32, + TestOperandType::TENSOR_FLOAT16, + TestOperandType::FLOAT32, + TestOperandType::FLOAT16, + }; + return kFloatTypes.count(type) > 0; +} + +bool isConstant(TestOperandLifeTime lifetime) { + return lifetime == TestOperandLifeTime::CONSTANT_COPY || + lifetime == TestOperandLifeTime::CONSTANT_REFERENCE; +} + namespace { const char* kOperationTypeNames[] = { @@ -492,11 +507,27 @@ std::string getOperandClassInSpecFile(TestOperandLifeTime lifetime) { } template -const auto defaultToStringFunc = [](const T& value) { return std::to_string(value); }; +std::string defaultToStringFunc(const T& value) { + return std::to_string(value); +}; +template <> +std::string defaultToStringFunc<_Float16>(const _Float16& value) { + return defaultToStringFunc(static_cast(value)); +}; +// Dump floating point values in hex representation. +template +std::string toHexFloatString(const T& value); +template <> +std::string toHexFloatString(const float& value) { + std::stringstream ss; + ss << "\"" << std::hexfloat << value << "\""; + return ss.str(); +}; template <> -const auto defaultToStringFunc<_Float16> = - [](const _Float16& value) { return std::to_string(static_cast(value)); }; +std::string toHexFloatString<_Float16>(const _Float16& value) { + return toHexFloatString(static_cast(value)); +}; template std::string join(const std::string& joint, Iterator begin, Iterator end, ToStringFunc func) { @@ -513,9 +544,15 @@ std::string join(const std::string& joint, const std::vector& range, ToString } template -void dumpTestBufferToSpecFileHelper(const TestBuffer& buffer, std::ostream& os) { +void dumpTestBufferToSpecFileHelper(const TestBuffer& buffer, bool useHexFloat, std::ostream& os) { const T* data = buffer.get(); const uint32_t length = buffer.size() / sizeof(T); + if constexpr (nnIsFloat) { + if (useHexFloat) { + os << "from_hex([" << join(", ", data, data + length, toHexFloatString) << "])"; + return; + } + } os << "[" << join(", ", data, data + length, defaultToStringFunc) << "]"; } @@ -530,36 +567,36 @@ const char* toString(TestOperationType type) { } // Dump a test buffer. -void SpecDumper::dumpTestBuffer(TestOperandType type, const TestBuffer& buffer) { +void SpecDumper::dumpTestBuffer(TestOperandType type, const TestBuffer& buffer, bool useHexFloat) { switch (type) { case TestOperandType::FLOAT32: case TestOperandType::TENSOR_FLOAT32: - dumpTestBufferToSpecFileHelper(buffer, mOs); + dumpTestBufferToSpecFileHelper(buffer, useHexFloat, mOs); break; case TestOperandType::INT32: case TestOperandType::TENSOR_INT32: - dumpTestBufferToSpecFileHelper(buffer, mOs); + dumpTestBufferToSpecFileHelper(buffer, useHexFloat, mOs); break; case TestOperandType::TENSOR_QUANT8_ASYMM: - dumpTestBufferToSpecFileHelper(buffer, mOs); + dumpTestBufferToSpecFileHelper(buffer, useHexFloat, mOs); break; case TestOperandType::TENSOR_QUANT8_SYMM: case TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED: - dumpTestBufferToSpecFileHelper(buffer, mOs); + dumpTestBufferToSpecFileHelper(buffer, useHexFloat, mOs); break; case TestOperandType::TENSOR_QUANT16_ASYMM: - dumpTestBufferToSpecFileHelper(buffer, mOs); + dumpTestBufferToSpecFileHelper(buffer, useHexFloat, mOs); break; case TestOperandType::TENSOR_QUANT16_SYMM: - dumpTestBufferToSpecFileHelper(buffer, mOs); + dumpTestBufferToSpecFileHelper(buffer, useHexFloat, mOs); break; case TestOperandType::BOOL: case TestOperandType::TENSOR_BOOL8: - dumpTestBufferToSpecFileHelper(buffer, mOs); + dumpTestBufferToSpecFileHelper(buffer, useHexFloat, mOs); break; case TestOperandType::FLOAT16: case TestOperandType::TENSOR_FLOAT16: - dumpTestBufferToSpecFileHelper<_Float16>(buffer, mOs); + dumpTestBufferToSpecFileHelper<_Float16>(buffer, useHexFloat, mOs); break; default: CHECK(false) << "Unknown type when dumping the buffer"; @@ -571,17 +608,29 @@ void SpecDumper::dumpTestOperand(const TestOperand& operand, uint32_t index) { << "\", [\"" << toString(operand.type) << "\", [" << join(", ", operand.dimensions, defaultToStringFunc) << "]"; if (operand.scale != 0.0f || operand.zeroPoint != 0) { - mOs << ", " << operand.scale << ", " << operand.zeroPoint; + mOs << ", float.fromhex(" << toHexFloatString(operand.scale) << "), " << operand.zeroPoint; } mOs << "]"; if (operand.lifetime == TestOperandLifeTime::CONSTANT_COPY || operand.lifetime == TestOperandLifeTime::CONSTANT_REFERENCE) { mOs << ", "; - dumpTestBuffer(operand.type, operand.data); + dumpTestBuffer(operand.type, operand.data, /*useHexFloat=*/true); } else if (operand.lifetime == TestOperandLifeTime::NO_VALUE) { mOs << ", value=None"; } - mOs << ")\n"; + mOs << ")"; + // For quantized data types, append a human-readable scale at the end. + if (operand.scale != 0.0f) { + mOs << " # scale = " << operand.scale; + } + // For float buffers, append human-readable values at the end. + if (isFloatType(operand.type) && + (operand.lifetime == TestOperandLifeTime::CONSTANT_COPY || + operand.lifetime == TestOperandLifeTime::CONSTANT_REFERENCE)) { + mOs << " # "; + dumpTestBuffer(operand.type, operand.data, /*useHexFloat=*/false); + } + mOs << "\n"; } void SpecDumper::dumpTestOperation(const TestOperation& operation) { @@ -593,6 +642,7 @@ void SpecDumper::dumpTestOperation(const TestOperation& operation) { void SpecDumper::dumpTestModel() { CHECK_EQ(kTestModel.referenced.size(), 0u) << "Subgraphs not supported"; + mOs << "from_hex = lambda l: [float.fromhex(i) for i in l]\n\n"; // Dump model operands. mOs << "# Model operands\n"; @@ -614,8 +664,14 @@ void SpecDumper::dumpTestModel() { operand.lifetime != TestOperandLifeTime::SUBGRAPH_OUTPUT) { continue; } + // For float buffers, dump human-readable values as a comment. + if (isFloatType(operand.type)) { + mOs << " # op" << i << ": "; + dumpTestBuffer(operand.type, operand.data, /*useHexFloat=*/false); + mOs << "\n"; + } mOs << " op" << i << ": "; - dumpTestBuffer(operand.type, operand.data); + dumpTestBuffer(operand.type, operand.data, /*useHexFloat=*/true); mOs << ",\n"; } mOs << "}).DisableLifeTimeVariation()\n"; @@ -627,8 +683,14 @@ void SpecDumper::dumpResults(const std::string& name, const std::vector Date: Tue, 21 Apr 2020 14:49:06 -0700 Subject: Add STRIDED_SLICE with shrinkMask in RGG tests. Fixes: 132322549 Test: NNT_static_fuzzing Change-Id: I85577481125e131741992e9d896d12320af67e1a --- nn/runtime/test/fuzzing/operation_signatures/Selection.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'nn') diff --git a/nn/runtime/test/fuzzing/operation_signatures/Selection.cpp b/nn/runtime/test/fuzzing/operation_signatures/Selection.cpp index 185644092..6d45d0433 100644 --- a/nn/runtime/test/fuzzing/operation_signatures/Selection.cpp +++ b/nn/runtime/test/fuzzing/operation_signatures/Selection.cpp @@ -280,8 +280,7 @@ static void stridedSliceConstructor(TestOperandType, uint32_t rank, RandomOperat setFreeDimensions(op->inputs[0], rank); std::vector shrinkMask(rank, false); for (uint32_t i = 0; i < rank; i++) { - // TODO: Currently shrinkMask is always set to false. - shrinkMask[i] = false; + shrinkMask[i] = getBernoulli(0.2f); int32_t stride = getUniform(1, 3); op->inputs[3]->value(i) = stride; if (!shrinkMask[i]) { @@ -303,7 +302,8 @@ static void stridedSliceFinalizer(RandomOperation* op) { for (uint32_t i = 0, o = 0; i < rank; i++) { int32_t inputSize = op->inputs[0]->dimensions[i].getValue(); int32_t stride = op->inputs[3]->value(i); - if ((shrinkMask & (1 << i)) == 0) { + bool shrink = shrinkMask & (1 << i); + if (!shrink) { int32_t outputSize = op->outputs[0]->dimensions[o++].getValue(); int32_t maxStart = inputSize - (outputSize - 1) * stride - 1; begin[i] = getUniform(0, maxStart); @@ -328,7 +328,8 @@ static void stridedSliceFinalizer(RandomOperation* op) { } // Switch to negative stride. - if (getBernoulli(0.2f)) { + // TODO(b/154639297): shrinkMask with negative stride will produce uninitialized output. + if (!shrink && getBernoulli(0.2f)) { op->inputs[3]->value(i) = -stride; std::swap(begin[i], end[i]); std::swap(beginMask[i], endMask[i]); -- cgit v1.2.3