1 // 2 // Copyright © 2019 Arm Ltd and Contributors. All rights reserved. 3 // SPDX-License-Identifier: MIT 4 // 5 6 #include "ParserFlatbuffersFixture.hpp" 7 8 #include <armnn/StrategyBase.hpp> 9 #include <armnn/utility/Assert.hpp> 10 #include <armnn/utility/NumericCast.hpp> 11 #include <armnn/utility/PolymorphicDowncast.hpp> 12 13 #include <layers/StandInLayer.hpp> 14 15 #include <sstream> 16 #include <vector> 17 18 TEST_SUITE("TensorflowLiteParser_Unsupported") 19 { 20 using namespace armnn; 21 22 class StandInLayerVerifier : public StrategyBase<NoThrowStrategy> 23 { 24 public: StandInLayerVerifier(const std::vector<TensorInfo> & inputInfos,const std::vector<TensorInfo> & outputInfos)25 StandInLayerVerifier(const std::vector<TensorInfo>& inputInfos, 26 const std::vector<TensorInfo>& outputInfos) 27 : m_InputInfos(inputInfos) 28 , m_OutputInfos(outputInfos) {} 29 ExecuteStrategy(const armnn::IConnectableLayer * layer,const armnn::BaseDescriptor & descriptor,const std::vector<armnn::ConstTensor> & constants,const char * name,const armnn::LayerBindingId id=0)30 void ExecuteStrategy(const armnn::IConnectableLayer* layer, 31 const armnn::BaseDescriptor& descriptor, 32 const std::vector<armnn::ConstTensor>& constants, 33 const char* name, 34 const armnn::LayerBindingId id = 0) override 35 { 36 armnn::IgnoreUnused(descriptor, constants, id); 37 switch (layer->GetType()) 38 { 39 case armnn::LayerType::StandIn: 40 { 41 auto standInDescriptor = static_cast<const armnn::StandInDescriptor&>(descriptor); 42 unsigned int numInputs = armnn::numeric_cast<unsigned int>(m_InputInfos.size()); 43 CHECK(standInDescriptor.m_NumInputs == numInputs); 44 CHECK(layer->GetNumInputSlots() == numInputs); 45 46 unsigned int numOutputs = armnn::numeric_cast<unsigned int>(m_OutputInfos.size()); 47 CHECK(standInDescriptor.m_NumOutputs == numOutputs); 48 CHECK(layer->GetNumOutputSlots() == numOutputs); 49 50 const StandInLayer* standInLayer = PolymorphicDowncast<const StandInLayer*>(layer); 51 for (unsigned int i = 0u; i < numInputs; ++i) 52 { 53 const OutputSlot* connectedSlot = standInLayer->GetInputSlot(i).GetConnectedOutputSlot(); 54 CHECK(connectedSlot != nullptr); 55 56 const TensorInfo& inputInfo = connectedSlot->GetTensorInfo(); 57 CHECK(inputInfo == m_InputInfos[i]); 58 } 59 60 for (unsigned int i = 0u; i < numOutputs; ++i) 61 { 62 const TensorInfo& outputInfo = layer->GetOutputSlot(i).GetTensorInfo(); 63 CHECK(outputInfo == m_OutputInfos[i]); 64 } 65 break; 66 } 67 default: 68 { 69 m_DefaultStrategy.Apply(GetLayerTypeAsCString(layer->GetType())); 70 } 71 } 72 } 73 74 private: 75 std::vector<TensorInfo> m_InputInfos; 76 std::vector<TensorInfo> m_OutputInfos; 77 }; 78 79 class DummyCustomFixture : public ParserFlatbuffersFixture 80 { 81 public: DummyCustomFixture(const std::vector<TensorInfo> & inputInfos,const std::vector<TensorInfo> & outputInfos)82 explicit DummyCustomFixture(const std::vector<TensorInfo>& inputInfos, 83 const std::vector<TensorInfo>& outputInfos) 84 : ParserFlatbuffersFixture() 85 , m_StandInLayerVerifier(inputInfos, outputInfos) 86 { 87 const unsigned int numInputs = armnn::numeric_cast<unsigned int>(inputInfos.size()); 88 ARMNN_ASSERT(numInputs > 0); 89 90 const unsigned int numOutputs = armnn::numeric_cast<unsigned int>(outputInfos.size()); 91 ARMNN_ASSERT(numOutputs > 0); 92 93 m_JsonString = R"( 94 { 95 "version": 3, 96 "operator_codes": [{ 97 "builtin_code": "CUSTOM", 98 "custom_code": "DummyCustomOperator" 99 }], 100 "subgraphs": [ { 101 "tensors": [)"; 102 103 // Add input tensors 104 for (unsigned int i = 0u; i < numInputs; ++i) 105 { 106 const TensorInfo& inputInfo = inputInfos[i]; 107 m_JsonString += R"( 108 { 109 "shape": )" + GetTensorShapeAsString(inputInfo.GetShape()) + R"(, 110 "type": )" + GetDataTypeAsString(inputInfo.GetDataType()) + R"(, 111 "buffer": 0, 112 "name": "inputTensor)" + std::to_string(i) + R"(", 113 "quantization": { 114 "min": [ 0.0 ], 115 "max": [ 255.0 ], 116 "scale": [ )" + std::to_string(inputInfo.GetQuantizationScale()) + R"( ], 117 "zero_point": [ )" + std::to_string(inputInfo.GetQuantizationOffset()) + R"( ], 118 } 119 },)"; 120 } 121 122 // Add output tensors 123 for (unsigned int i = 0u; i < numOutputs; ++i) 124 { 125 const TensorInfo& outputInfo = outputInfos[i]; 126 m_JsonString += R"( 127 { 128 "shape": )" + GetTensorShapeAsString(outputInfo.GetShape()) + R"(, 129 "type": )" + GetDataTypeAsString(outputInfo.GetDataType()) + R"(, 130 "buffer": 0, 131 "name": "outputTensor)" + std::to_string(i) + R"(", 132 "quantization": { 133 "min": [ 0.0 ], 134 "max": [ 255.0 ], 135 "scale": [ )" + std::to_string(outputInfo.GetQuantizationScale()) + R"( ], 136 "zero_point": [ )" + std::to_string(outputInfo.GetQuantizationOffset()) + R"( ], 137 } 138 })"; 139 140 if (i + 1 < numOutputs) 141 { 142 m_JsonString += ","; 143 } 144 } 145 146 const std::string inputIndices = GetIndicesAsString(0u, numInputs - 1u); 147 const std::string outputIndices = GetIndicesAsString(numInputs, numInputs + numOutputs - 1u); 148 149 // Add dummy custom operator 150 m_JsonString += R"(], 151 "inputs": )" + inputIndices + R"(, 152 "outputs": )" + outputIndices + R"(, 153 "operators": [ 154 { 155 "opcode_index": 0, 156 "inputs": )" + inputIndices + R"(, 157 "outputs": )" + outputIndices + R"(, 158 "builtin_options_type": 0, 159 "custom_options": [ ], 160 "custom_options_format": "FLEXBUFFERS" 161 } 162 ], 163 } ], 164 "buffers" : [ 165 { }, 166 { } 167 ] 168 } 169 )"; 170 171 ReadStringToBinary(); 172 } 173 RunTest()174 void RunTest() 175 { 176 INetworkPtr network = m_Parser->CreateNetworkFromBinary(m_GraphBinary); 177 network->ExecuteStrategy(m_StandInLayerVerifier); 178 } 179 180 private: GetTensorShapeAsString(const TensorShape & tensorShape)181 static std::string GetTensorShapeAsString(const TensorShape& tensorShape) 182 { 183 std::stringstream stream; 184 stream << "[ "; 185 for (unsigned int i = 0u; i < tensorShape.GetNumDimensions(); ++i) 186 { 187 stream << tensorShape[i]; 188 if (i + 1 < tensorShape.GetNumDimensions()) 189 { 190 stream << ","; 191 } 192 stream << " "; 193 } 194 stream << "]"; 195 196 return stream.str(); 197 } 198 GetDataTypeAsString(DataType dataType)199 static std::string GetDataTypeAsString(DataType dataType) 200 { 201 switch (dataType) 202 { 203 case DataType::Float32: return "FLOAT32"; 204 case DataType::QAsymmU8: return "UINT8"; 205 default: return "UNKNOWN"; 206 } 207 } 208 GetIndicesAsString(unsigned int first,unsigned int last)209 static std::string GetIndicesAsString(unsigned int first, unsigned int last) 210 { 211 std::stringstream stream; 212 stream << "[ "; 213 for (unsigned int i = first; i <= last ; ++i) 214 { 215 stream << i; 216 if (i + 1 <= last) 217 { 218 stream << ","; 219 } 220 stream << " "; 221 } 222 stream << "]"; 223 224 return stream.str(); 225 } 226 227 StandInLayerVerifier m_StandInLayerVerifier; 228 }; 229 230 class DummyCustom1Input1OutputFixture : public DummyCustomFixture 231 { 232 public: DummyCustom1Input1OutputFixture()233 DummyCustom1Input1OutputFixture() 234 : DummyCustomFixture({ TensorInfo({ 1, 1 }, DataType::Float32) }, 235 { TensorInfo({ 2, 2 }, DataType::Float32) }) {} 236 }; 237 238 class DummyCustom2Inputs1OutputFixture : public DummyCustomFixture 239 { 240 public: DummyCustom2Inputs1OutputFixture()241 DummyCustom2Inputs1OutputFixture() 242 : DummyCustomFixture({ TensorInfo({ 1, 1 }, DataType::Float32), TensorInfo({ 2, 2 }, DataType::Float32) }, 243 { TensorInfo({ 3, 3 }, DataType::Float32) }) {} 244 }; 245 246 TEST_CASE_FIXTURE(DummyCustom1Input1OutputFixture, "UnsupportedCustomOperator1Input1Output") 247 { 248 RunTest(); 249 } 250 251 TEST_CASE_FIXTURE(DummyCustom2Inputs1OutputFixture, "UnsupportedCustomOperator2Inputs1Output") 252 { 253 RunTest(); 254 } 255 256 } 257