blob: 9e98774ada3940674dbfe85d0ec667143553547e [file] [log] [blame]
telsoa01c577f2c2018-08-31 09:22:23 +01001//
Finn Williamsb49ed182021-06-29 15:50:08 +01002// Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa01c577f2c2018-08-31 09:22:23 +01004//
5
6#pragma once
7
Matteo Martincighc601aa62019-10-29 15:03:22 +00008#include "Schema.hpp"
9
keidav01222c7532019-03-14 17:12:10 +000010#include <armnn/Descriptors.hpp>
11#include <armnn/IRuntime.hpp>
12#include <armnn/TypesUtils.hpp>
Matteo Martincighc601aa62019-10-29 15:03:22 +000013#include <armnn/BackendRegistry.hpp>
keidav01222c7532019-03-14 17:12:10 +000014
Finn Williamsb49ed182021-06-29 15:50:08 +010015#include "../TfLiteParser.hpp"
Matteo Martincighc601aa62019-10-29 15:03:22 +000016
17#include <ResolveType.hpp>
18
Colm Donelanc42a9872022-02-02 16:35:09 +000019#include <armnnTestUtils/TensorHelpers.hpp>
Matteo Martincighc601aa62019-10-29 15:03:22 +000020
James Ward58dec6b2020-09-11 17:32:44 +010021#include <fmt/format.h>
Sadik Armagan1625efc2021-06-10 18:24:34 +010022#include <doctest/doctest.h>
keidav01222c7532019-03-14 17:12:10 +000023
telsoa01c577f2c2018-08-31 09:22:23 +010024#include "flatbuffers/idl.h"
25#include "flatbuffers/util.h"
keidav01222c7532019-03-14 17:12:10 +000026#include "flatbuffers/flexbuffers.h"
telsoa01c577f2c2018-08-31 09:22:23 +010027
28#include <schema_generated.h>
Matteo Martincighc601aa62019-10-29 15:03:22 +000029
telsoa01c577f2c2018-08-31 09:22:23 +010030
31using armnnTfLiteParser::ITfLiteParser;
Aron Virginas-Tarc975f922019-10-23 17:38:17 +010032using armnnTfLiteParser::ITfLiteParserPtr;
telsoa01c577f2c2018-08-31 09:22:23 +010033
Aron Virginas-Tarc975f922019-10-23 17:38:17 +010034using TensorRawPtr = const tflite::TensorT *;
telsoa01c577f2c2018-08-31 09:22:23 +010035struct ParserFlatbuffersFixture
36{
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +000037 ParserFlatbuffersFixture() :
Finn Williamsb49ed182021-06-29 15:50:08 +010038 m_Runtime(armnn::IRuntime::Create(armnn::IRuntime::CreationOptions())),
39 m_NetworkIdentifier(0),
40 m_DynamicNetworkIdentifier(1)
telsoa01c577f2c2018-08-31 09:22:23 +010041 {
Aron Virginas-Tarc975f922019-10-23 17:38:17 +010042 ITfLiteParser::TfLiteParserOptions options;
43 options.m_StandInLayerForUnsupported = true;
Sadik Armagand109a4d2020-07-28 10:42:13 +010044 options.m_InferAndValidate = true;
Aron Virginas-Tarc975f922019-10-23 17:38:17 +010045
Finn Williamsb49ed182021-06-29 15:50:08 +010046 m_Parser = std::make_unique<armnnTfLiteParser::TfLiteParserImpl>(
47 armnn::Optional<ITfLiteParser::TfLiteParserOptions>(options));
telsoa01c577f2c2018-08-31 09:22:23 +010048 }
49
50 std::vector<uint8_t> m_GraphBinary;
Aron Virginas-Tarc975f922019-10-23 17:38:17 +010051 std::string m_JsonString;
Aron Virginas-Tarc975f922019-10-23 17:38:17 +010052 armnn::IRuntimePtr m_Runtime;
53 armnn::NetworkId m_NetworkIdentifier;
Finn Williamsb49ed182021-06-29 15:50:08 +010054 armnn::NetworkId m_DynamicNetworkIdentifier;
55 bool m_TestDynamic;
56 std::unique_ptr<armnnTfLiteParser::TfLiteParserImpl> m_Parser;
telsoa01c577f2c2018-08-31 09:22:23 +010057
58 /// If the single-input-single-output overload of Setup() is called, these will store the input and output name
59 /// so they don't need to be passed to the single-input-single-output overload of RunTest().
60 std::string m_SingleInputName;
61 std::string m_SingleOutputName;
62
Finn Williamsb49ed182021-06-29 15:50:08 +010063 void Setup(bool testDynamic = true)
64 {
65 m_TestDynamic = testDynamic;
66 loadNetwork(m_NetworkIdentifier, false);
67
68 if (m_TestDynamic)
69 {
70 loadNetwork(m_DynamicNetworkIdentifier, true);
71 }
72 }
73
74 std::unique_ptr<tflite::ModelT> MakeModelDynamic(std::vector<uint8_t> graphBinary)
75 {
76 const uint8_t* binaryContent = graphBinary.data();
77 const size_t len = graphBinary.size();
78 if (binaryContent == nullptr)
79 {
80 throw armnn::InvalidArgumentException(fmt::format("Invalid (null) binary content {}",
81 CHECK_LOCATION().AsString()));
82 }
83 flatbuffers::Verifier verifier(binaryContent, len);
84 if (verifier.VerifyBuffer<tflite::Model>() == false)
85 {
86 throw armnn::ParseException(fmt::format("Buffer doesn't conform to the expected Tensorflow Lite "
87 "flatbuffers format. size:{} {}",
88 len,
89 CHECK_LOCATION().AsString()));
90 }
91 auto model = tflite::UnPackModel(binaryContent);
92
93 for (auto const& subgraph : model->subgraphs)
94 {
95 std::vector<int32_t> inputIds = subgraph->inputs;
96 for (unsigned int tensorIndex = 0; tensorIndex < subgraph->tensors.size(); ++tensorIndex)
97 {
98 if (std::find(inputIds.begin(), inputIds.end(), tensorIndex) != inputIds.end())
99 {
100 continue;
101 }
102 for (auto const& tensor : subgraph->tensors)
103 {
104 if (tensor->shape_signature.size() != 0)
105 {
106 continue;
107 }
108
109 for (unsigned int i = 0; i < tensor->shape.size(); ++i)
110 {
111 tensor->shape_signature.push_back(-1);
112 }
113 }
114 }
115 }
116
117 return model;
118 }
119
120 void loadNetwork(armnn::NetworkId networkId, bool loadDynamic)
telsoa01c577f2c2018-08-31 09:22:23 +0100121 {
Colm Donelan940932d2022-01-26 15:11:19 +0000122 if (!ReadStringToBinary())
123 {
telsoa01c577f2c2018-08-31 09:22:23 +0100124 throw armnn::Exception("LoadNetwork failed while reading binary input");
125 }
126
Finn Williamsb49ed182021-06-29 15:50:08 +0100127 armnn::INetworkPtr network = loadDynamic ? m_Parser->LoadModel(MakeModelDynamic(m_GraphBinary))
128 : m_Parser->CreateNetworkFromBinary(m_GraphBinary);
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +0000129
130 if (!network) {
131 throw armnn::Exception("The parser failed to create an ArmNN network");
132 }
133
134 auto optimized = Optimize(*network, { armnn::Compute::CpuRef },
135 m_Runtime->GetDeviceSpec());
136 std::string errorMessage;
137
Finn Williamsb49ed182021-06-29 15:50:08 +0100138 armnn::Status ret = m_Runtime->LoadNetwork(networkId, move(optimized), errorMessage);
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +0000139
140 if (ret != armnn::Status::Success)
telsoa01c577f2c2018-08-31 09:22:23 +0100141 {
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +0000142 throw armnn::Exception(
James Ward58dec6b2020-09-11 17:32:44 +0100143 fmt::format("The runtime failed to load the network. "
144 "Error was: {}. in {} [{}:{}]",
145 errorMessage,
146 __func__,
147 __FILE__,
148 __LINE__));
telsoa01c577f2c2018-08-31 09:22:23 +0100149 }
150 }
151
152 void SetupSingleInputSingleOutput(const std::string& inputName, const std::string& outputName)
153 {
154 // Store the input and output name so they don't need to be passed to the single-input-single-output RunTest().
155 m_SingleInputName = inputName;
156 m_SingleOutputName = outputName;
157 Setup();
158 }
159
160 bool ReadStringToBinary()
161 {
Rob Hughesff3c4262019-12-20 17:43:16 +0000162 std::string schemafile(g_TfLiteSchemaText, g_TfLiteSchemaText + g_TfLiteSchemaText_len);
telsoa01c577f2c2018-08-31 09:22:23 +0100163
164 // parse schema first, so we can use it to parse the data after
165 flatbuffers::Parser parser;
166
Matthew Bentham6c8e8e72019-01-15 17:57:00 +0000167 bool ok = parser.Parse(schemafile.c_str());
Colm Doneland9636032022-02-04 10:56:12 +0000168 CHECK_MESSAGE(ok, std::string("Failed to parse schema file. Error was: " + parser.error_).c_str());
telsoa01c577f2c2018-08-31 09:22:23 +0100169
Colm Donelan940932d2022-01-26 15:11:19 +0000170 ok = parser.Parse(m_JsonString.c_str());
Colm Doneland9636032022-02-04 10:56:12 +0000171 CHECK_MESSAGE(ok, std::string("Failed to parse json input. Error was: " + parser.error_).c_str());
telsoa01c577f2c2018-08-31 09:22:23 +0100172
173 {
174 const uint8_t * bufferPtr = parser.builder_.GetBufferPointer();
175 size_t size = static_cast<size_t>(parser.builder_.GetSize());
176 m_GraphBinary.assign(bufferPtr, bufferPtr+size);
177 }
178 return ok;
179 }
180
181 /// Executes the network with the given input tensor and checks the result against the given output tensor.
keidav011b3e2ea2019-02-21 10:07:37 +0000182 /// This assumes the network has a single input and a single output.
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000183 template <std::size_t NumOutputDimensions,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000184 armnn::DataType ArmnnType>
telsoa01c577f2c2018-08-31 09:22:23 +0100185 void RunTest(size_t subgraphId,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000186 const std::vector<armnn::ResolveType<ArmnnType>>& inputData,
187 const std::vector<armnn::ResolveType<ArmnnType>>& expectedOutputData);
telsoa01c577f2c2018-08-31 09:22:23 +0100188
189 /// Executes the network with the given input tensors and checks the results against the given output tensors.
190 /// This overload supports multiple inputs and multiple outputs, identified by name.
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000191 template <std::size_t NumOutputDimensions,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000192 armnn::DataType ArmnnType>
telsoa01c577f2c2018-08-31 09:22:23 +0100193 void RunTest(size_t subgraphId,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000194 const std::map<std::string, std::vector<armnn::ResolveType<ArmnnType>>>& inputData,
195 const std::map<std::string, std::vector<armnn::ResolveType<ArmnnType>>>& expectedOutputData);
telsoa01c577f2c2018-08-31 09:22:23 +0100196
keidav011b3e2ea2019-02-21 10:07:37 +0000197 /// Multiple Inputs, Multiple Outputs w/ Variable Datatypes and different dimension sizes.
198 /// Executes the network with the given input tensors and checks the results against the given output tensors.
199 /// This overload supports multiple inputs and multiple outputs, identified by name along with the allowance for
200 /// the input datatype to be different to the output
201 template <std::size_t NumOutputDimensions,
202 armnn::DataType ArmnnType1,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000203 armnn::DataType ArmnnType2>
keidav011b3e2ea2019-02-21 10:07:37 +0000204 void RunTest(size_t subgraphId,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000205 const std::map<std::string, std::vector<armnn::ResolveType<ArmnnType1>>>& inputData,
Sadik Armagand109a4d2020-07-28 10:42:13 +0100206 const std::map<std::string, std::vector<armnn::ResolveType<ArmnnType2>>>& expectedOutputData,
207 bool isDynamic = false);
keidav011b3e2ea2019-02-21 10:07:37 +0000208
Sadik Armagan26868492021-01-22 14:25:31 +0000209 /// Multiple Inputs with different DataTypes, Multiple Outputs w/ Variable DataTypes
210 /// Executes the network with the given input tensors and checks the results against the given output tensors.
211 /// This overload supports multiple inputs and multiple outputs, identified by name along with the allowance for
212 /// the input datatype to be different to the output
213 template <std::size_t NumOutputDimensions,
214 armnn::DataType inputType1,
215 armnn::DataType inputType2,
216 armnn::DataType outputType>
217 void RunTest(size_t subgraphId,
218 const std::map<std::string, std::vector<armnn::ResolveType<inputType1>>>& input1Data,
219 const std::map<std::string, std::vector<armnn::ResolveType<inputType2>>>& input2Data,
220 const std::map<std::string, std::vector<armnn::ResolveType<outputType>>>& expectedOutputData);
keidav011b3e2ea2019-02-21 10:07:37 +0000221
222 /// Multiple Inputs, Multiple Outputs w/ Variable Datatypes and different dimension sizes.
223 /// Executes the network with the given input tensors and checks the results against the given output tensors.
224 /// This overload supports multiple inputs and multiple outputs, identified by name along with the allowance for
225 /// the input datatype to be different to the output
226 template<armnn::DataType ArmnnType1,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000227 armnn::DataType ArmnnType2>
keidav011b3e2ea2019-02-21 10:07:37 +0000228 void RunTest(std::size_t subgraphId,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000229 const std::map<std::string, std::vector<armnn::ResolveType<ArmnnType1>>>& inputData,
230 const std::map<std::string, std::vector<armnn::ResolveType<ArmnnType2>>>& expectedOutputData);
keidav011b3e2ea2019-02-21 10:07:37 +0000231
keidav01222c7532019-03-14 17:12:10 +0000232 static inline std::string GenerateDetectionPostProcessJsonString(
233 const armnn::DetectionPostProcessDescriptor& descriptor)
234 {
235 flexbuffers::Builder detectPostProcess;
236 detectPostProcess.Map([&]() {
237 detectPostProcess.Bool("use_regular_nms", descriptor.m_UseRegularNms);
238 detectPostProcess.Int("max_detections", descriptor.m_MaxDetections);
239 detectPostProcess.Int("max_classes_per_detection", descriptor.m_MaxClassesPerDetection);
240 detectPostProcess.Int("detections_per_class", descriptor.m_DetectionsPerClass);
241 detectPostProcess.Int("num_classes", descriptor.m_NumClasses);
242 detectPostProcess.Float("nms_score_threshold", descriptor.m_NmsScoreThreshold);
243 detectPostProcess.Float("nms_iou_threshold", descriptor.m_NmsIouThreshold);
244 detectPostProcess.Float("h_scale", descriptor.m_ScaleH);
245 detectPostProcess.Float("w_scale", descriptor.m_ScaleW);
246 detectPostProcess.Float("x_scale", descriptor.m_ScaleX);
247 detectPostProcess.Float("y_scale", descriptor.m_ScaleY);
248 });
249 detectPostProcess.Finish();
250
251 // Create JSON string
252 std::stringstream strStream;
253 std::vector<uint8_t> buffer = detectPostProcess.GetBuffer();
254 std::copy(buffer.begin(), buffer.end(),std::ostream_iterator<int>(strStream,","));
255
256 return strStream.str();
257 }
258
telsoa01c577f2c2018-08-31 09:22:23 +0100259 void CheckTensors(const TensorRawPtr& tensors, size_t shapeSize, const std::vector<int32_t>& shape,
260 tflite::TensorType tensorType, uint32_t buffer, const std::string& name,
261 const std::vector<float>& min, const std::vector<float>& max,
262 const std::vector<float>& scale, const std::vector<int64_t>& zeroPoint)
263 {
Sadik Armagan1625efc2021-06-10 18:24:34 +0100264 CHECK(tensors);
265 CHECK_EQ(shapeSize, tensors->shape.size());
266 CHECK(std::equal(shape.begin(), shape.end(), tensors->shape.begin(), tensors->shape.end()));
267 CHECK_EQ(tensorType, tensors->type);
268 CHECK_EQ(buffer, tensors->buffer);
269 CHECK_EQ(name, tensors->name);
270 CHECK(tensors->quantization);
271 CHECK(std::equal(min.begin(), min.end(), tensors->quantization.get()->min.begin(),
272 tensors->quantization.get()->min.end()));
273 CHECK(std::equal(max.begin(), max.end(), tensors->quantization.get()->max.begin(),
274 tensors->quantization.get()->max.end()));
275 CHECK(std::equal(scale.begin(), scale.end(), tensors->quantization.get()->scale.begin(),
276 tensors->quantization.get()->scale.end()));
277 CHECK(std::equal(zeroPoint.begin(), zeroPoint.end(),
telsoa01c577f2c2018-08-31 09:22:23 +0100278 tensors->quantization.get()->zero_point.begin(),
Sadik Armagan1625efc2021-06-10 18:24:34 +0100279 tensors->quantization.get()->zero_point.end()));
telsoa01c577f2c2018-08-31 09:22:23 +0100280 }
Sadik Armagan26868492021-01-22 14:25:31 +0000281
282private:
283 /// Fills the InputTensors with given input data
284 template <armnn::DataType dataType>
285 void FillInputTensors(armnn::InputTensors& inputTensors,
286 const std::map<std::string, std::vector<armnn::ResolveType<dataType>>>& inputData,
287 size_t subgraphId);
telsoa01c577f2c2018-08-31 09:22:23 +0100288};
289
Sadik Armagan26868492021-01-22 14:25:31 +0000290/// Fills the InputTensors with given input data
291template <armnn::DataType dataType>
292void ParserFlatbuffersFixture::FillInputTensors(
293 armnn::InputTensors& inputTensors,
294 const std::map<std::string, std::vector<armnn::ResolveType<dataType>>>& inputData,
295 size_t subgraphId)
296{
297 for (auto&& it : inputData)
298 {
299 armnn::BindingPointInfo bindingInfo = m_Parser->GetNetworkInputBindingInfo(subgraphId, it.first);
Cathal Corbett5b8093c2021-10-22 11:12:07 +0100300 bindingInfo.second.SetConstant(true);
Sadik Armagan26868492021-01-22 14:25:31 +0000301 armnn::VerifyTensorInfoDataType(bindingInfo.second, dataType);
302 inputTensors.push_back({ bindingInfo.first, armnn::ConstTensor(bindingInfo.second, it.second.data()) });
303 }
304}
305
keidav011b3e2ea2019-02-21 10:07:37 +0000306/// Single Input, Single Output
307/// Executes the network with the given input tensor and checks the result against the given output tensor.
308/// This overload assumes the network has a single input and a single output.
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000309template <std::size_t NumOutputDimensions,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000310 armnn::DataType armnnType>
telsoa01c577f2c2018-08-31 09:22:23 +0100311void ParserFlatbuffersFixture::RunTest(size_t subgraphId,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000312 const std::vector<armnn::ResolveType<armnnType>>& inputData,
313 const std::vector<armnn::ResolveType<armnnType>>& expectedOutputData)
telsoa01c577f2c2018-08-31 09:22:23 +0100314{
keidav011b3e2ea2019-02-21 10:07:37 +0000315 RunTest<NumOutputDimensions, armnnType>(subgraphId,
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000316 { { m_SingleInputName, inputData } },
317 { { m_SingleOutputName, expectedOutputData } });
telsoa01c577f2c2018-08-31 09:22:23 +0100318}
319
keidav011b3e2ea2019-02-21 10:07:37 +0000320/// Multiple Inputs, Multiple Outputs
321/// Executes the network with the given input tensors and checks the results against the given output tensors.
322/// This overload supports multiple inputs and multiple outputs, identified by name.
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000323template <std::size_t NumOutputDimensions,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000324 armnn::DataType armnnType>
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000325void ParserFlatbuffersFixture::RunTest(size_t subgraphId,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000326 const std::map<std::string, std::vector<armnn::ResolveType<armnnType>>>& inputData,
327 const std::map<std::string, std::vector<armnn::ResolveType<armnnType>>>& expectedOutputData)
telsoa01c577f2c2018-08-31 09:22:23 +0100328{
keidav011b3e2ea2019-02-21 10:07:37 +0000329 RunTest<NumOutputDimensions, armnnType, armnnType>(subgraphId, inputData, expectedOutputData);
330}
331
332/// Multiple Inputs, Multiple Outputs w/ Variable Datatypes
333/// Executes the network with the given input tensors and checks the results against the given output tensors.
334/// This overload supports multiple inputs and multiple outputs, identified by name along with the allowance for
335/// the input datatype to be different to the output
336template <std::size_t NumOutputDimensions,
337 armnn::DataType armnnType1,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000338 armnn::DataType armnnType2>
keidav011b3e2ea2019-02-21 10:07:37 +0000339void ParserFlatbuffersFixture::RunTest(size_t subgraphId,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000340 const std::map<std::string, std::vector<armnn::ResolveType<armnnType1>>>& inputData,
Sadik Armagand109a4d2020-07-28 10:42:13 +0100341 const std::map<std::string, std::vector<armnn::ResolveType<armnnType2>>>& expectedOutputData,
342 bool isDynamic)
keidav011b3e2ea2019-02-21 10:07:37 +0000343{
Rob Hughesfc6bf052019-12-16 17:10:51 +0000344 using DataType2 = armnn::ResolveType<armnnType2>;
345
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +0000346 // Setup the armnn input tensors from the given vectors.
347 armnn::InputTensors inputTensors;
Sadik Armagan26868492021-01-22 14:25:31 +0000348 FillInputTensors<armnnType1>(inputTensors, inputData, subgraphId);
telsoa01c577f2c2018-08-31 09:22:23 +0100349
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +0000350 // Allocate storage for the output tensors to be written to and setup the armnn output tensors.
Sadik Armagan483c8112021-06-01 09:24:52 +0100351 std::map<std::string, std::vector<DataType2>> outputStorage;
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +0000352 armnn::OutputTensors outputTensors;
353 for (auto&& it : expectedOutputData)
354 {
Narumol Prangnawarat386681a2019-04-29 16:40:55 +0100355 armnn::LayerBindingId outputBindingId = m_Parser->GetNetworkOutputBindingInfo(subgraphId, it.first).first;
356 armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkIdentifier, outputBindingId);
357
358 // Check that output tensors have correct number of dimensions (NumOutputDimensions specified in test)
359 auto outputNumDimensions = outputTensorInfo.GetNumDimensions();
Sadik Armagan1625efc2021-06-10 18:24:34 +0100360 CHECK_MESSAGE((outputNumDimensions == NumOutputDimensions),
James Ward58dec6b2020-09-11 17:32:44 +0100361 fmt::format("Number of dimensions expected {}, but got {} for output layer {}",
362 NumOutputDimensions,
363 outputNumDimensions,
364 it.first));
Narumol Prangnawarat386681a2019-04-29 16:40:55 +0100365
366 armnn::VerifyTensorInfoDataType(outputTensorInfo, armnnType2);
Sadik Armagan483c8112021-06-01 09:24:52 +0100367 outputStorage.emplace(it.first, std::vector<DataType2>(outputTensorInfo.GetNumElements()));
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +0000368 outputTensors.push_back(
Narumol Prangnawarat386681a2019-04-29 16:40:55 +0100369 { outputBindingId, armnn::Tensor(outputTensorInfo, outputStorage.at(it.first).data()) });
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +0000370 }
telsoa01c577f2c2018-08-31 09:22:23 +0100371
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +0000372 m_Runtime->EnqueueWorkload(m_NetworkIdentifier, inputTensors, outputTensors);
telsoa01c577f2c2018-08-31 09:22:23 +0100373
Matthew Sloyaneb5f8102021-10-05 17:31:42 +0100374 // Set flag so that the correct comparison function is called if the output is boolean.
375 bool isBoolean = armnnType2 == armnn::DataType::Boolean ? true : false;
376
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +0000377 // Compare each output tensor to the expected values
378 for (auto&& it : expectedOutputData)
379 {
Jim Flynnb4d7eae2019-05-01 14:44:27 +0100380 armnn::BindingPointInfo bindingInfo = m_Parser->GetNetworkOutputBindingInfo(subgraphId, it.first);
Sadik Armagan483c8112021-06-01 09:24:52 +0100381 auto outputExpected = it.second;
Matthew Sloyaneb5f8102021-10-05 17:31:42 +0100382 auto result = CompareTensors(outputExpected, outputStorage[it.first],
383 bindingInfo.second.GetShape(), bindingInfo.second.GetShape(),
384 isBoolean, isDynamic);
385 CHECK_MESSAGE(result.m_Result, result.m_Message.str());
telsoa01c577f2c2018-08-31 09:22:23 +0100386 }
Finn Williamsb49ed182021-06-29 15:50:08 +0100387
388 if (isDynamic)
389 {
390 m_Runtime->EnqueueWorkload(m_DynamicNetworkIdentifier, inputTensors, outputTensors);
391
392 // Compare each output tensor to the expected values
393 for (auto&& it : expectedOutputData)
394 {
395 armnn::BindingPointInfo bindingInfo = m_Parser->GetNetworkOutputBindingInfo(subgraphId, it.first);
396 auto outputExpected = it.second;
397 auto result = CompareTensors(outputExpected, outputStorage[it.first],
398 bindingInfo.second.GetShape(), bindingInfo.second.GetShape(),
399 false, isDynamic);
400 CHECK_MESSAGE(result.m_Result, result.m_Message.str());
401 }
402 }
telsoa01c577f2c2018-08-31 09:22:23 +0100403}
keidav011b3e2ea2019-02-21 10:07:37 +0000404
405/// Multiple Inputs, Multiple Outputs w/ Variable Datatypes and different dimension sizes.
406/// Executes the network with the given input tensors and checks the results against the given output tensors.
407/// This overload supports multiple inputs and multiple outputs, identified by name along with the allowance for
408/// the input datatype to be different to the output.
409template <armnn::DataType armnnType1,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000410 armnn::DataType armnnType2>
keidav011b3e2ea2019-02-21 10:07:37 +0000411void ParserFlatbuffersFixture::RunTest(std::size_t subgraphId,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000412 const std::map<std::string, std::vector<armnn::ResolveType<armnnType1>>>& inputData,
413 const std::map<std::string, std::vector<armnn::ResolveType<armnnType2>>>& expectedOutputData)
keidav011b3e2ea2019-02-21 10:07:37 +0000414{
Rob Hughesfc6bf052019-12-16 17:10:51 +0000415 using DataType2 = armnn::ResolveType<armnnType2>;
416
keidav011b3e2ea2019-02-21 10:07:37 +0000417 // Setup the armnn input tensors from the given vectors.
418 armnn::InputTensors inputTensors;
Sadik Armagan26868492021-01-22 14:25:31 +0000419 FillInputTensors<armnnType1>(inputTensors, inputData, subgraphId);
keidav011b3e2ea2019-02-21 10:07:37 +0000420
421 armnn::OutputTensors outputTensors;
422 outputTensors.reserve(expectedOutputData.size());
423 std::map<std::string, std::vector<DataType2>> outputStorage;
424 for (auto&& it : expectedOutputData)
425 {
Jim Flynnb4d7eae2019-05-01 14:44:27 +0100426 armnn::BindingPointInfo bindingInfo = m_Parser->GetNetworkOutputBindingInfo(subgraphId, it.first);
keidav011b3e2ea2019-02-21 10:07:37 +0000427 armnn::VerifyTensorInfoDataType(bindingInfo.second, armnnType2);
428
429 std::vector<DataType2> out(it.second.size());
430 outputStorage.emplace(it.first, out);
431 outputTensors.push_back({ bindingInfo.first,
432 armnn::Tensor(bindingInfo.second,
433 outputStorage.at(it.first).data()) });
434 }
435
436 m_Runtime->EnqueueWorkload(m_NetworkIdentifier, inputTensors, outputTensors);
437
438 // Checks the results.
439 for (auto&& it : expectedOutputData)
440 {
Rob Hughesfc6bf052019-12-16 17:10:51 +0000441 std::vector<armnn::ResolveType<armnnType2>> out = outputStorage.at(it.first);
keidav011b3e2ea2019-02-21 10:07:37 +0000442 {
443 for (unsigned int i = 0; i < out.size(); ++i)
444 {
Sadik Armagan1625efc2021-06-10 18:24:34 +0100445 CHECK(doctest::Approx(it.second[i]).epsilon(0.000001f) == out[i]);
keidav011b3e2ea2019-02-21 10:07:37 +0000446 }
447 }
448 }
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100449}
Sadik Armagan26868492021-01-22 14:25:31 +0000450
451/// Multiple Inputs with different DataTypes, Multiple Outputs w/ Variable DataTypes
452/// Executes the network with the given input tensors and checks the results against the given output tensors.
453/// This overload supports multiple inputs and multiple outputs, identified by name along with the allowance for
454/// the input datatype to be different to the output
455template <std::size_t NumOutputDimensions,
456 armnn::DataType inputType1,
457 armnn::DataType inputType2,
458 armnn::DataType outputType>
459void ParserFlatbuffersFixture::RunTest(size_t subgraphId,
460 const std::map<std::string, std::vector<armnn::ResolveType<inputType1>>>& input1Data,
461 const std::map<std::string, std::vector<armnn::ResolveType<inputType2>>>& input2Data,
462 const std::map<std::string, std::vector<armnn::ResolveType<outputType>>>& expectedOutputData)
463{
464 using DataType2 = armnn::ResolveType<outputType>;
465
466 // Setup the armnn input tensors from the given vectors.
467 armnn::InputTensors inputTensors;
468 FillInputTensors<inputType1>(inputTensors, input1Data, subgraphId);
469 FillInputTensors<inputType2>(inputTensors, input2Data, subgraphId);
470
471 // Allocate storage for the output tensors to be written to and setup the armnn output tensors.
Sadik Armagan483c8112021-06-01 09:24:52 +0100472 std::map<std::string, std::vector<DataType2>> outputStorage;
Sadik Armagan26868492021-01-22 14:25:31 +0000473 armnn::OutputTensors outputTensors;
474 for (auto&& it : expectedOutputData)
475 {
476 armnn::LayerBindingId outputBindingId = m_Parser->GetNetworkOutputBindingInfo(subgraphId, it.first).first;
477 armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkIdentifier, outputBindingId);
478
479 // Check that output tensors have correct number of dimensions (NumOutputDimensions specified in test)
480 auto outputNumDimensions = outputTensorInfo.GetNumDimensions();
Sadik Armagan1625efc2021-06-10 18:24:34 +0100481 CHECK_MESSAGE((outputNumDimensions == NumOutputDimensions),
Sadik Armagan26868492021-01-22 14:25:31 +0000482 fmt::format("Number of dimensions expected {}, but got {} for output layer {}",
483 NumOutputDimensions,
484 outputNumDimensions,
485 it.first));
486
487 armnn::VerifyTensorInfoDataType(outputTensorInfo, outputType);
Sadik Armagan483c8112021-06-01 09:24:52 +0100488 outputStorage.emplace(it.first, std::vector<DataType2>(outputTensorInfo.GetNumElements()));
Sadik Armagan26868492021-01-22 14:25:31 +0000489 outputTensors.push_back(
490 { outputBindingId, armnn::Tensor(outputTensorInfo, outputStorage.at(it.first).data()) });
491 }
492
493 m_Runtime->EnqueueWorkload(m_NetworkIdentifier, inputTensors, outputTensors);
494
Matthew Sloyaneb5f8102021-10-05 17:31:42 +0100495 // Set flag so that the correct comparison function is called if the output is boolean.
496 bool isBoolean = outputType == armnn::DataType::Boolean ? true : false;
497
Sadik Armagan26868492021-01-22 14:25:31 +0000498 // Compare each output tensor to the expected values
499 for (auto&& it : expectedOutputData)
500 {
501 armnn::BindingPointInfo bindingInfo = m_Parser->GetNetworkOutputBindingInfo(subgraphId, it.first);
Sadik Armagan483c8112021-06-01 09:24:52 +0100502 auto outputExpected = it.second;
Matthew Sloyaneb5f8102021-10-05 17:31:42 +0100503 auto result = CompareTensors(outputExpected, outputStorage[it.first],
504 bindingInfo.second.GetShape(), bindingInfo.second.GetShape(),
505 isBoolean);
506 CHECK_MESSAGE(result.m_Result, result.m_Message.str());
Sadik Armagan26868492021-01-22 14:25:31 +0000507 }
508}