1 // 2 // Copyright © 2017 Arm Ltd. All rights reserved. 3 // SPDX-License-Identifier: MIT 4 // 5 #include <doctest/doctest.h> 6 7 8 #include <armnn/BackendHelper.hpp> 9 #include <armnn/Utils.hpp> 10 #include <armnn/Types.hpp> 11 #include <armnn/TypesUtils.hpp> 12 #include <armnn/Descriptors.hpp> 13 #include <armnnUtils/Permute.hpp> 14 #include <GraphTopologicalSort.hpp> 15 #include <Graph.hpp> 16 #include <ResolveType.hpp> 17 18 TEST_SUITE("Utils") 19 { 20 TEST_CASE("DataTypeSize") 21 { 22 CHECK(armnn::GetDataTypeSize(armnn::DataType::Float32) == 4); 23 CHECK(armnn::GetDataTypeSize(armnn::DataType::QAsymmU8) == 1); 24 CHECK(armnn::GetDataTypeSize(armnn::DataType::Signed32) == 4); 25 CHECK(armnn::GetDataTypeSize(armnn::DataType::Boolean) == 1); 26 } 27 28 TEST_CASE("PermuteDescriptorWithTooManyMappings") 29 { 30 CHECK_THROWS_AS(armnn::PermuteDescriptor({ 0u, 1u, 2u, 3u, 4u, 5u }), armnn::InvalidArgumentException); 31 } 32 33 TEST_CASE("PermuteDescriptorWithInvalidMappings1d") 34 { 35 CHECK_THROWS_AS(armnn::PermuteDescriptor({ 1u }), armnn::InvalidArgumentException); 36 } 37 38 TEST_CASE("PermuteDescriptorWithInvalidMappings2d") 39 { 40 CHECK_THROWS_AS(armnn::PermuteDescriptor({ 2u, 0u }), armnn::InvalidArgumentException); 41 } 42 43 TEST_CASE("PermuteDescriptorWithInvalidMappings3d") 44 { 45 CHECK_THROWS_AS(armnn::PermuteDescriptor({ 0u, 3u, 1u }), armnn::InvalidArgumentException); 46 } 47 48 TEST_CASE("PermuteDescriptorWithInvalidMappings4d") 49 { 50 CHECK_THROWS_AS(armnn::PermuteDescriptor({ 0u, 1u, 2u, 4u }), armnn::InvalidArgumentException); 51 } 52 53 TEST_CASE("PermuteDescriptorWithInvalidMappings5d") 54 { 55 CHECK_THROWS_AS(armnn::PermuteDescriptor({ 0u, 1u, 2u, 3u, 5u }), armnn::InvalidArgumentException); 56 } 57 58 TEST_CASE("PermuteDescriptorWithDuplicatedMappings") 59 { 60 CHECK_THROWS_AS(armnn::PermuteDescriptor({ 1u, 1u, 0u }), armnn::InvalidArgumentException); 61 } 62 63 TEST_CASE("HalfType") 64 { 65 using namespace half_float::literal; 66 armnn::Half a = 1.0_h; 67 68 float b = 1.0f; 69 armnn::Half c(b); 70 71 // Test half type 72 CHECK_EQ(a, b); 73 CHECK_EQ(sizeof(c), 2); 74 75 // Test half type is floating point type 76 CHECK(std::is_floating_point<armnn::Half>::value); 77 78 // Test utility function returns correct type. 79 using ResolvedType = armnn::ResolveType<armnn::DataType::Float16>; 80 constexpr bool isHalfType = std::is_same<armnn::Half, ResolvedType>::value; 81 CHECK(isHalfType); 82 83 //Test utility functions return correct size 84 CHECK(GetDataTypeSize(armnn::DataType::Float16) == 2); 85 86 //Test utility functions return correct name 87 CHECK((GetDataTypeName(armnn::DataType::Float16) == std::string("Float16"))); 88 } 89 90 TEST_CASE("BFloatType") 91 { 92 uint16_t v = 16256; 93 armnn::BFloat16 a(v); 94 armnn::BFloat16 b(1.0f); 95 armnn::BFloat16 zero; 96 97 // Test BFloat16 type 98 CHECK_EQ(sizeof(a), 2); 99 CHECK_EQ(a, b); 100 CHECK_EQ(a.Val(), v); 101 CHECK_EQ(a, 1.0f); 102 CHECK_EQ(zero, 0.0f); 103 104 // Infinity 105 float infFloat = std::numeric_limits<float>::infinity(); 106 armnn::BFloat16 infBF(infFloat); 107 CHECK_EQ(infBF, armnn::BFloat16::Inf()); 108 109 // NaN 110 float nan = std::numeric_limits<float>::quiet_NaN(); 111 armnn::BFloat16 nanBF(nan); 112 CHECK_EQ(nanBF, armnn::BFloat16::Nan()); 113 114 // Test utility function returns correct type. 115 using ResolvedType = armnn::ResolveType<armnn::DataType::BFloat16>; 116 constexpr bool isBFloat16Type = std::is_same<armnn::BFloat16, ResolvedType>::value; 117 CHECK(isBFloat16Type); 118 119 //Test utility functions return correct size 120 CHECK(GetDataTypeSize(armnn::DataType::BFloat16) == 2); 121 122 //Test utility functions return correct name 123 CHECK((GetDataTypeName(armnn::DataType::BFloat16) == std::string("BFloat16"))); 124 } 125 126 TEST_CASE("GraphTopologicalSortSimpleTest") 127 { 128 std::map<int, std::vector<int>> graph; 129 130 graph[0] = {2}; 131 graph[1] = {3}; 132 graph[2] = {4}; 133 graph[3] = {4}; 134 graph[4] = {5}; 135 graph[5] = {}; 136 137 auto getNodeInputs = [graph](int node) -> std::vector<int> __anon445299380102(int node) 138 { 139 return graph.find(node)->second; 140 }; 141 142 std::vector<int> targetNodes = {0, 1}; 143 144 std::vector<int> output; 145 bool sortCompleted = armnnUtils::GraphTopologicalSort<int>(targetNodes, getNodeInputs, output); 146 147 CHECK(sortCompleted); 148 149 std::vector<int> correctResult = {5, 4, 2, 0, 3, 1}; 150 CHECK(std::equal(output.begin(), output.end(), correctResult.begin(), correctResult.end())); 151 } 152 153 TEST_CASE("GraphTopologicalSortVariantTest") 154 { 155 std::map<int, std::vector<int>> graph; 156 157 graph[0] = {2}; 158 graph[1] = {2}; 159 graph[2] = {3, 4}; 160 graph[3] = {5}; 161 graph[4] = {5}; 162 graph[5] = {6}; 163 graph[6] = {}; 164 165 auto getNodeInputs = [graph](int node) -> std::vector<int> __anon445299380202(int node) 166 { 167 return graph.find(node)->second; 168 }; 169 170 std::vector<int> targetNodes = {0, 1}; 171 172 std::vector<int> output; 173 bool sortCompleted = armnnUtils::GraphTopologicalSort<int>(targetNodes, getNodeInputs, output); 174 175 CHECK(sortCompleted); 176 177 std::vector<int> correctResult = {6, 5, 3, 4, 2, 0, 1}; 178 CHECK(std::equal(output.begin(), output.end(), correctResult.begin(), correctResult.end())); 179 } 180 181 TEST_CASE("CyclicalGraphTopologicalSortTest") 182 { 183 std::map<int, std::vector<int>> graph; 184 185 graph[0] = {1}; 186 graph[1] = {2}; 187 graph[2] = {0}; 188 189 auto getNodeInputs = [graph](int node) -> std::vector<int> __anon445299380302(int node) 190 { 191 return graph.find(node)->second; 192 }; 193 194 std::vector<int> targetNodes = {0}; 195 196 std::vector<int> output; 197 bool sortCompleted = armnnUtils::GraphTopologicalSort<int>(targetNodes, getNodeInputs, output); 198 199 CHECK(!sortCompleted); 200 } 201 202 TEST_CASE("PermuteQuantizationDim") 203 { 204 std::vector<float> scales {1.0f, 1.0f}; 205 206 // Set QuantizationDim to be index 1 207 const armnn::TensorInfo perChannelInfo({ 1, 2, 3, 4 }, armnn::DataType::Float32, scales, 1U); 208 CHECK(perChannelInfo.GetQuantizationDim().value() == 1U); 209 210 // Permute so that index 1 moves to final index i.e. index 3 211 armnn::PermutationVector mappings({ 0, 3, 2, 1 }); 212 auto permutedPerChannel = armnnUtils::Permuted(perChannelInfo, mappings); 213 214 // Check that QuantizationDim is in index 3 215 CHECK(permutedPerChannel.GetQuantizationDim().value() == 3U); 216 217 // Even if there is only a single scale the quantization dim still exists and needs to be permuted 218 std::vector<float> scale {1.0f}; 219 const armnn::TensorInfo perChannelInfo1({ 1, 2, 3, 4 }, armnn::DataType::Float32, scale, 1U); 220 auto permuted = armnnUtils::Permuted(perChannelInfo1, mappings); 221 CHECK(permuted.GetQuantizationDim().value() == 3U); 222 } 223 224 TEST_CASE("EmptyPermuteVectorIndexOutOfBounds") 225 { 226 armnn::PermutationVector pv = armnn::PermutationVector({}); 227 CHECK_THROWS_AS(pv[0], armnn::InvalidArgumentException); 228 } 229 230 TEST_CASE("PermuteDescriptorIndexOutOfBounds") 231 { 232 armnn::PermutationVector pv = armnn::PermutationVector({ 1u, 2u, 0u }); 233 armnn::PermuteDescriptor desc = armnn::PermuteDescriptor(pv); 234 CHECK_THROWS_AS(desc.m_DimMappings[3], armnn::InvalidArgumentException); 235 CHECK(desc.m_DimMappings[0] == 1u); 236 } 237 238 TEST_CASE("TransposeDescriptorIndexOutOfBounds") 239 { 240 armnn::PermutationVector pv = armnn::PermutationVector({ 2u, 1u, 0u }); 241 armnn::TransposeDescriptor desc = armnn::TransposeDescriptor(pv); 242 CHECK_THROWS_AS(desc.m_DimMappings[3], armnn::InvalidArgumentException); 243 CHECK(desc.m_DimMappings[2] == 0u); 244 } 245 246 TEST_CASE("PermuteVectorIterator") 247 { 248 // We're slightly breaking the spirit of std::array.end() because we're using it as a 249 // variable length rather than fixed length. This test is to use a couple of iterators and 250 // make sure it still mostly makes sense. 251 252 // Create zero length. 253 armnn::PermutationVector zeroPVector({}); 254 // Begin should be equal to end. 255 CHECK(zeroPVector.begin() == zeroPVector.end()); 256 257 // Create length 4. Summing the 4 values should be 6. 258 armnn::PermutationVector fourPVector({ 0, 3, 2, 1 }); 259 unsigned int sum = 0; 260 for (unsigned int it : fourPVector) 261 { 262 sum += it; 263 } 264 CHECK(sum == 6); 265 // Directly use begin and end, make sure there are 4 iterations. 266 unsigned int iterations = 0; 267 auto itr = fourPVector.begin(); 268 while(itr != fourPVector.end()) 269 { 270 ++iterations; 271 itr++; 272 } 273 CHECK(iterations == 4); 274 275 // Do the same with 2 elements. 276 armnn::PermutationVector twoPVector({ 0, 1 }); 277 iterations = 0; 278 itr = twoPVector.begin(); 279 while(itr != twoPVector.end()) 280 { 281 ++iterations; 282 itr++; 283 } 284 CHECK(iterations == 2); 285 } 286 287 #if defined(ARMNNREF_ENABLED) 288 TEST_CASE("LayerSupportHandle") 289 { 290 auto layerSupportObject = armnn::GetILayerSupportByBackendId("CpuRef"); 291 armnn::TensorInfo input; 292 std::string reasonIfUnsupported; 293 // InputLayer always supported for CpuRef 294 CHECK_EQ(layerSupportObject.IsInputSupported(input, reasonIfUnsupported), true); 295 296 CHECK(layerSupportObject.IsBackendRegistered()); 297 } 298 #endif 299 300 } 301