// // Copyright © 2017 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // #pragma once #include #include #include #include #include #include #include #include #include #include constexpr float g_FloatCloseToZeroTolerance = 1.0e-6f; template struct SelectiveComparer { static bool Compare(T a, T b) { return (std::max(a, b) - std::min(a, b)) <= 1; } }; template struct SelectiveComparer { static bool Compare(T a, T b) { // If a or b is zero, percent_tolerance does an exact match, so compare to a small, constant tolerance instead. if (a == 0.0f || b == 0.0f) { return std::abs(a - b) <= g_FloatCloseToZeroTolerance; } if (std::isinf(a) && a == b) { return true; } if (std::isnan(a) && std::isnan(b)) { return true; } // For unquantized floats we use a tolerance of 1%. return armnnUtils::within_percentage_tolerance(a, b); } }; template bool SelectiveCompare(T a, T b) { return SelectiveComparer()>::Compare(a, b); }; template bool SelectiveCompareBoolean(T a, T b) { return (((a == 0) && (b == 0)) || ((a != 0) && (b != 0))); }; template armnn::PredicateResult CompareTensors(const std::vector& actualData, const std::vector& expectedData, const armnn::TensorShape& actualShape, const armnn::TensorShape& expectedShape, bool compareBoolean = false, bool isDynamic = false) { if (actualData.size() != expectedData.size()) { armnn::PredicateResult res(false); res.Message() << "Different data size [" << actualData.size() << "!=" << expectedData.size() << "]"; return res; } if (actualShape.GetNumDimensions() != expectedShape.GetNumDimensions()) { armnn::PredicateResult res(false); res.Message() << "Different number of dimensions [" << actualShape.GetNumDimensions() << "!=" << expectedShape.GetNumDimensions() << "]"; return res; } if (actualShape.GetNumElements() != expectedShape.GetNumElements()) { armnn::PredicateResult res(false); res.Message() << "Different number of elements [" << actualShape.GetNumElements() << "!=" << expectedShape.GetNumElements() << "]"; return res; } unsigned int numberOfDimensions = actualShape.GetNumDimensions(); if (!isDynamic) { // Checks they are same shape. for (unsigned int i = 0; i < numberOfDimensions; ++i) { if (actualShape[i] != expectedShape[i]) { armnn::PredicateResult res(false); res.Message() << "Different shapes [" << actualShape[i] << "!=" << expectedShape[i] << "]"; return res; } } } // Fun iteration over n dimensions. std::vector indices; for (unsigned int i = 0; i < numberOfDimensions; i++) { indices.emplace_back(0); } std::stringstream errorString; int numFailedElements = 0; constexpr int maxReportedDifferences = 3; unsigned int index = 0; // Compare data element by element. while (true) { bool comparison; // As true for uint8_t is non-zero (1-255) we must have a dedicated compare for Booleans. if(compareBoolean) { comparison = SelectiveCompareBoolean(actualData[index], expectedData[index]); } else { comparison = SelectiveCompare(actualData[index], expectedData[index]); } if (!comparison) { ++numFailedElements; if (numFailedElements <= maxReportedDifferences) { if (numFailedElements >= 2) { errorString << ", "; } errorString << "["; for (unsigned int i = 0; i < numberOfDimensions; ++i) { errorString << indices[i]; if (i != numberOfDimensions - 1) { errorString << ","; } } errorString << "]"; errorString << " (" << +actualData[index] << " != " << +expectedData[index] << ")"; } } ++indices[numberOfDimensions - 1]; for (unsigned int i=numberOfDimensions-1; i>0; i--) { if (indices[i] == actualShape[i]) { indices[i] = 0; ++indices[i - 1]; } } if (indices[0] == actualShape[0]) { break; } index++; } armnn::PredicateResult comparisonResult(true); if (numFailedElements > 0) { comparisonResult.SetResult(false); comparisonResult.Message() << numFailedElements << " different values at: "; if (numFailedElements > maxReportedDifferences) { errorString << ", ... (and " << (numFailedElements - maxReportedDifferences) << " other differences)"; } comparisonResult.Message() << errorString.str(); } return comparisonResult; } template std::vector MakeRandomTensor(const armnn::TensorInfo& tensorInfo, unsigned int seed, float min = -10.0f, float max = 10.0f) { std::mt19937 gen(seed); std::uniform_real_distribution dist(min, max); std::vector init(tensorInfo.GetNumElements()); for (unsigned int i = 0; i < init.size(); i++) { init[i] = dist(gen); } const float qScale = tensorInfo.GetQuantizationScale(); const int32_t qOffset = tensorInfo.GetQuantizationOffset(); return armnnUtils::QuantizedVector(init, qScale, qOffset); }