blob: f333ac0d40886891cb0a66ae72c8693929f17498 [file] [log] [blame]
telsoa01c577f2c2018-08-31 09:22:23 +01001//
2// Copyright © 2017 Arm Ltd. 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>
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +010014#include <armnn/utility/Assert.hpp>
keidav01222c7532019-03-14 17:12:10 +000015
Matteo Martincighc601aa62019-10-29 15:03:22 +000016#include <armnnTfLiteParser/ITfLiteParser.hpp>
17
18#include <ResolveType.hpp>
19
20#include <test/TensorHelpers.hpp>
21
James Ward58dec6b2020-09-11 17:32:44 +010022#include <fmt/format.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#include <iostream>
31
32using armnnTfLiteParser::ITfLiteParser;
Aron Virginas-Tarc975f922019-10-23 17:38:17 +010033using armnnTfLiteParser::ITfLiteParserPtr;
telsoa01c577f2c2018-08-31 09:22:23 +010034
Aron Virginas-Tarc975f922019-10-23 17:38:17 +010035using TensorRawPtr = const tflite::TensorT *;
telsoa01c577f2c2018-08-31 09:22:23 +010036struct ParserFlatbuffersFixture
37{
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +000038 ParserFlatbuffersFixture() :
Aron Virginas-Tarc975f922019-10-23 17:38:17 +010039 m_Parser(nullptr, &ITfLiteParser::Destroy),
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +000040 m_Runtime(armnn::IRuntime::Create(armnn::IRuntime::CreationOptions())),
41 m_NetworkIdentifier(-1)
telsoa01c577f2c2018-08-31 09:22:23 +010042 {
Aron Virginas-Tarc975f922019-10-23 17:38:17 +010043 ITfLiteParser::TfLiteParserOptions options;
44 options.m_StandInLayerForUnsupported = true;
Sadik Armagand109a4d2020-07-28 10:42:13 +010045 options.m_InferAndValidate = true;
Aron Virginas-Tarc975f922019-10-23 17:38:17 +010046
47 m_Parser.reset(ITfLiteParser::CreateRaw(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;
52 ITfLiteParserPtr m_Parser;
53 armnn::IRuntimePtr m_Runtime;
54 armnn::NetworkId m_NetworkIdentifier;
telsoa01c577f2c2018-08-31 09:22:23 +010055
56 /// If the single-input-single-output overload of Setup() is called, these will store the input and output name
57 /// so they don't need to be passed to the single-input-single-output overload of RunTest().
58 std::string m_SingleInputName;
59 std::string m_SingleOutputName;
60
61 void Setup()
62 {
63 bool ok = ReadStringToBinary();
64 if (!ok) {
65 throw armnn::Exception("LoadNetwork failed while reading binary input");
66 }
67
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +000068 armnn::INetworkPtr network =
69 m_Parser->CreateNetworkFromBinary(m_GraphBinary);
70
71 if (!network) {
72 throw armnn::Exception("The parser failed to create an ArmNN network");
73 }
74
75 auto optimized = Optimize(*network, { armnn::Compute::CpuRef },
76 m_Runtime->GetDeviceSpec());
77 std::string errorMessage;
78
79 armnn::Status ret = m_Runtime->LoadNetwork(m_NetworkIdentifier, move(optimized), errorMessage);
80
81 if (ret != armnn::Status::Success)
telsoa01c577f2c2018-08-31 09:22:23 +010082 {
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +000083 throw armnn::Exception(
James Ward58dec6b2020-09-11 17:32:44 +010084 fmt::format("The runtime failed to load the network. "
85 "Error was: {}. in {} [{}:{}]",
86 errorMessage,
87 __func__,
88 __FILE__,
89 __LINE__));
telsoa01c577f2c2018-08-31 09:22:23 +010090 }
91 }
92
93 void SetupSingleInputSingleOutput(const std::string& inputName, const std::string& outputName)
94 {
95 // Store the input and output name so they don't need to be passed to the single-input-single-output RunTest().
96 m_SingleInputName = inputName;
97 m_SingleOutputName = outputName;
98 Setup();
99 }
100
101 bool ReadStringToBinary()
102 {
Rob Hughesff3c4262019-12-20 17:43:16 +0000103 std::string schemafile(g_TfLiteSchemaText, g_TfLiteSchemaText + g_TfLiteSchemaText_len);
telsoa01c577f2c2018-08-31 09:22:23 +0100104
105 // parse schema first, so we can use it to parse the data after
106 flatbuffers::Parser parser;
107
Matthew Bentham6c8e8e72019-01-15 17:57:00 +0000108 bool ok = parser.Parse(schemafile.c_str());
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100109 ARMNN_ASSERT_MSG(ok, "Failed to parse schema file");
telsoa01c577f2c2018-08-31 09:22:23 +0100110
111 ok &= parser.Parse(m_JsonString.c_str());
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100112 ARMNN_ASSERT_MSG(ok, "Failed to parse json input");
telsoa01c577f2c2018-08-31 09:22:23 +0100113
114 if (!ok)
115 {
116 return false;
117 }
118
119 {
120 const uint8_t * bufferPtr = parser.builder_.GetBufferPointer();
121 size_t size = static_cast<size_t>(parser.builder_.GetSize());
122 m_GraphBinary.assign(bufferPtr, bufferPtr+size);
123 }
124 return ok;
125 }
126
127 /// Executes the network with the given input tensor and checks the result against the given output tensor.
keidav011b3e2ea2019-02-21 10:07:37 +0000128 /// This assumes the network has a single input and a single output.
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000129 template <std::size_t NumOutputDimensions,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000130 armnn::DataType ArmnnType>
telsoa01c577f2c2018-08-31 09:22:23 +0100131 void RunTest(size_t subgraphId,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000132 const std::vector<armnn::ResolveType<ArmnnType>>& inputData,
133 const std::vector<armnn::ResolveType<ArmnnType>>& expectedOutputData);
telsoa01c577f2c2018-08-31 09:22:23 +0100134
135 /// Executes the network with the given input tensors and checks the results against the given output tensors.
136 /// This overload supports multiple inputs and multiple outputs, identified by name.
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000137 template <std::size_t NumOutputDimensions,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000138 armnn::DataType ArmnnType>
telsoa01c577f2c2018-08-31 09:22:23 +0100139 void RunTest(size_t subgraphId,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000140 const std::map<std::string, std::vector<armnn::ResolveType<ArmnnType>>>& inputData,
141 const std::map<std::string, std::vector<armnn::ResolveType<ArmnnType>>>& expectedOutputData);
telsoa01c577f2c2018-08-31 09:22:23 +0100142
keidav011b3e2ea2019-02-21 10:07:37 +0000143 /// Multiple Inputs, Multiple Outputs w/ Variable Datatypes and different dimension sizes.
144 /// Executes the network with the given input tensors and checks the results against the given output tensors.
145 /// This overload supports multiple inputs and multiple outputs, identified by name along with the allowance for
146 /// the input datatype to be different to the output
147 template <std::size_t NumOutputDimensions,
148 armnn::DataType ArmnnType1,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000149 armnn::DataType ArmnnType2>
keidav011b3e2ea2019-02-21 10:07:37 +0000150 void RunTest(size_t subgraphId,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000151 const std::map<std::string, std::vector<armnn::ResolveType<ArmnnType1>>>& inputData,
Sadik Armagand109a4d2020-07-28 10:42:13 +0100152 const std::map<std::string, std::vector<armnn::ResolveType<ArmnnType2>>>& expectedOutputData,
153 bool isDynamic = false);
keidav011b3e2ea2019-02-21 10:07:37 +0000154
Sadik Armagan26868492021-01-22 14:25:31 +0000155 /// Multiple Inputs with different DataTypes, Multiple Outputs w/ Variable DataTypes
156 /// Executes the network with the given input tensors and checks the results against the given output tensors.
157 /// This overload supports multiple inputs and multiple outputs, identified by name along with the allowance for
158 /// the input datatype to be different to the output
159 template <std::size_t NumOutputDimensions,
160 armnn::DataType inputType1,
161 armnn::DataType inputType2,
162 armnn::DataType outputType>
163 void RunTest(size_t subgraphId,
164 const std::map<std::string, std::vector<armnn::ResolveType<inputType1>>>& input1Data,
165 const std::map<std::string, std::vector<armnn::ResolveType<inputType2>>>& input2Data,
166 const std::map<std::string, std::vector<armnn::ResolveType<outputType>>>& expectedOutputData);
keidav011b3e2ea2019-02-21 10:07:37 +0000167
168 /// Multiple Inputs, Multiple Outputs w/ Variable Datatypes and different dimension sizes.
169 /// Executes the network with the given input tensors and checks the results against the given output tensors.
170 /// This overload supports multiple inputs and multiple outputs, identified by name along with the allowance for
171 /// the input datatype to be different to the output
172 template<armnn::DataType ArmnnType1,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000173 armnn::DataType ArmnnType2>
keidav011b3e2ea2019-02-21 10:07:37 +0000174 void RunTest(std::size_t subgraphId,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000175 const std::map<std::string, std::vector<armnn::ResolveType<ArmnnType1>>>& inputData,
176 const std::map<std::string, std::vector<armnn::ResolveType<ArmnnType2>>>& expectedOutputData);
keidav011b3e2ea2019-02-21 10:07:37 +0000177
keidav01222c7532019-03-14 17:12:10 +0000178 static inline std::string GenerateDetectionPostProcessJsonString(
179 const armnn::DetectionPostProcessDescriptor& descriptor)
180 {
181 flexbuffers::Builder detectPostProcess;
182 detectPostProcess.Map([&]() {
183 detectPostProcess.Bool("use_regular_nms", descriptor.m_UseRegularNms);
184 detectPostProcess.Int("max_detections", descriptor.m_MaxDetections);
185 detectPostProcess.Int("max_classes_per_detection", descriptor.m_MaxClassesPerDetection);
186 detectPostProcess.Int("detections_per_class", descriptor.m_DetectionsPerClass);
187 detectPostProcess.Int("num_classes", descriptor.m_NumClasses);
188 detectPostProcess.Float("nms_score_threshold", descriptor.m_NmsScoreThreshold);
189 detectPostProcess.Float("nms_iou_threshold", descriptor.m_NmsIouThreshold);
190 detectPostProcess.Float("h_scale", descriptor.m_ScaleH);
191 detectPostProcess.Float("w_scale", descriptor.m_ScaleW);
192 detectPostProcess.Float("x_scale", descriptor.m_ScaleX);
193 detectPostProcess.Float("y_scale", descriptor.m_ScaleY);
194 });
195 detectPostProcess.Finish();
196
197 // Create JSON string
198 std::stringstream strStream;
199 std::vector<uint8_t> buffer = detectPostProcess.GetBuffer();
200 std::copy(buffer.begin(), buffer.end(),std::ostream_iterator<int>(strStream,","));
201
202 return strStream.str();
203 }
204
telsoa01c577f2c2018-08-31 09:22:23 +0100205 void CheckTensors(const TensorRawPtr& tensors, size_t shapeSize, const std::vector<int32_t>& shape,
206 tflite::TensorType tensorType, uint32_t buffer, const std::string& name,
207 const std::vector<float>& min, const std::vector<float>& max,
208 const std::vector<float>& scale, const std::vector<int64_t>& zeroPoint)
209 {
210 BOOST_CHECK(tensors);
211 BOOST_CHECK_EQUAL(shapeSize, tensors->shape.size());
212 BOOST_CHECK_EQUAL_COLLECTIONS(shape.begin(), shape.end(), tensors->shape.begin(), tensors->shape.end());
213 BOOST_CHECK_EQUAL(tensorType, tensors->type);
214 BOOST_CHECK_EQUAL(buffer, tensors->buffer);
215 BOOST_CHECK_EQUAL(name, tensors->name);
216 BOOST_CHECK(tensors->quantization);
217 BOOST_CHECK_EQUAL_COLLECTIONS(min.begin(), min.end(), tensors->quantization.get()->min.begin(),
218 tensors->quantization.get()->min.end());
219 BOOST_CHECK_EQUAL_COLLECTIONS(max.begin(), max.end(), tensors->quantization.get()->max.begin(),
220 tensors->quantization.get()->max.end());
221 BOOST_CHECK_EQUAL_COLLECTIONS(scale.begin(), scale.end(), tensors->quantization.get()->scale.begin(),
222 tensors->quantization.get()->scale.end());
223 BOOST_CHECK_EQUAL_COLLECTIONS(zeroPoint.begin(), zeroPoint.end(),
224 tensors->quantization.get()->zero_point.begin(),
225 tensors->quantization.get()->zero_point.end());
226 }
Sadik Armagan26868492021-01-22 14:25:31 +0000227
228private:
229 /// Fills the InputTensors with given input data
230 template <armnn::DataType dataType>
231 void FillInputTensors(armnn::InputTensors& inputTensors,
232 const std::map<std::string, std::vector<armnn::ResolveType<dataType>>>& inputData,
233 size_t subgraphId);
telsoa01c577f2c2018-08-31 09:22:23 +0100234};
235
Sadik Armagan26868492021-01-22 14:25:31 +0000236/// Fills the InputTensors with given input data
237template <armnn::DataType dataType>
238void ParserFlatbuffersFixture::FillInputTensors(
239 armnn::InputTensors& inputTensors,
240 const std::map<std::string, std::vector<armnn::ResolveType<dataType>>>& inputData,
241 size_t subgraphId)
242{
243 for (auto&& it : inputData)
244 {
245 armnn::BindingPointInfo bindingInfo = m_Parser->GetNetworkInputBindingInfo(subgraphId, it.first);
246 armnn::VerifyTensorInfoDataType(bindingInfo.second, dataType);
247 inputTensors.push_back({ bindingInfo.first, armnn::ConstTensor(bindingInfo.second, it.second.data()) });
248 }
249}
250
keidav011b3e2ea2019-02-21 10:07:37 +0000251/// Single Input, Single Output
252/// Executes the network with the given input tensor and checks the result against the given output tensor.
253/// This overload assumes the network has a single input and a single output.
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000254template <std::size_t NumOutputDimensions,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000255 armnn::DataType armnnType>
telsoa01c577f2c2018-08-31 09:22:23 +0100256void ParserFlatbuffersFixture::RunTest(size_t subgraphId,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000257 const std::vector<armnn::ResolveType<armnnType>>& inputData,
258 const std::vector<armnn::ResolveType<armnnType>>& expectedOutputData)
telsoa01c577f2c2018-08-31 09:22:23 +0100259{
keidav011b3e2ea2019-02-21 10:07:37 +0000260 RunTest<NumOutputDimensions, armnnType>(subgraphId,
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000261 { { m_SingleInputName, inputData } },
262 { { m_SingleOutputName, expectedOutputData } });
telsoa01c577f2c2018-08-31 09:22:23 +0100263}
264
keidav011b3e2ea2019-02-21 10:07:37 +0000265/// Multiple Inputs, Multiple Outputs
266/// Executes the network with the given input tensors and checks the results against the given output tensors.
267/// This overload supports multiple inputs and multiple outputs, identified by name.
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000268template <std::size_t NumOutputDimensions,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000269 armnn::DataType armnnType>
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000270void ParserFlatbuffersFixture::RunTest(size_t subgraphId,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000271 const std::map<std::string, std::vector<armnn::ResolveType<armnnType>>>& inputData,
272 const std::map<std::string, std::vector<armnn::ResolveType<armnnType>>>& expectedOutputData)
telsoa01c577f2c2018-08-31 09:22:23 +0100273{
keidav011b3e2ea2019-02-21 10:07:37 +0000274 RunTest<NumOutputDimensions, armnnType, armnnType>(subgraphId, inputData, expectedOutputData);
275}
276
277/// Multiple Inputs, Multiple Outputs w/ Variable Datatypes
278/// Executes the network with the given input tensors and checks the results against the given output tensors.
279/// This overload supports multiple inputs and multiple outputs, identified by name along with the allowance for
280/// the input datatype to be different to the output
281template <std::size_t NumOutputDimensions,
282 armnn::DataType armnnType1,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000283 armnn::DataType armnnType2>
keidav011b3e2ea2019-02-21 10:07:37 +0000284void ParserFlatbuffersFixture::RunTest(size_t subgraphId,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000285 const std::map<std::string, std::vector<armnn::ResolveType<armnnType1>>>& inputData,
Sadik Armagand109a4d2020-07-28 10:42:13 +0100286 const std::map<std::string, std::vector<armnn::ResolveType<armnnType2>>>& expectedOutputData,
287 bool isDynamic)
keidav011b3e2ea2019-02-21 10:07:37 +0000288{
Rob Hughesfc6bf052019-12-16 17:10:51 +0000289 using DataType2 = armnn::ResolveType<armnnType2>;
290
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +0000291 // Setup the armnn input tensors from the given vectors.
292 armnn::InputTensors inputTensors;
Sadik Armagan26868492021-01-22 14:25:31 +0000293 FillInputTensors<armnnType1>(inputTensors, inputData, subgraphId);
telsoa01c577f2c2018-08-31 09:22:23 +0100294
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +0000295 // Allocate storage for the output tensors to be written to and setup the armnn output tensors.
keidav011b3e2ea2019-02-21 10:07:37 +0000296 std::map<std::string, boost::multi_array<DataType2, NumOutputDimensions>> outputStorage;
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +0000297 armnn::OutputTensors outputTensors;
298 for (auto&& it : expectedOutputData)
299 {
Narumol Prangnawarat386681a2019-04-29 16:40:55 +0100300 armnn::LayerBindingId outputBindingId = m_Parser->GetNetworkOutputBindingInfo(subgraphId, it.first).first;
301 armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkIdentifier, outputBindingId);
302
303 // Check that output tensors have correct number of dimensions (NumOutputDimensions specified in test)
304 auto outputNumDimensions = outputTensorInfo.GetNumDimensions();
305 BOOST_CHECK_MESSAGE((outputNumDimensions == NumOutputDimensions),
James Ward58dec6b2020-09-11 17:32:44 +0100306 fmt::format("Number of dimensions expected {}, but got {} for output layer {}",
307 NumOutputDimensions,
308 outputNumDimensions,
309 it.first));
Narumol Prangnawarat386681a2019-04-29 16:40:55 +0100310
311 armnn::VerifyTensorInfoDataType(outputTensorInfo, armnnType2);
312 outputStorage.emplace(it.first, MakeTensor<DataType2, NumOutputDimensions>(outputTensorInfo));
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +0000313 outputTensors.push_back(
Narumol Prangnawarat386681a2019-04-29 16:40:55 +0100314 { outputBindingId, armnn::Tensor(outputTensorInfo, outputStorage.at(it.first).data()) });
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +0000315 }
telsoa01c577f2c2018-08-31 09:22:23 +0100316
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +0000317 m_Runtime->EnqueueWorkload(m_NetworkIdentifier, inputTensors, outputTensors);
telsoa01c577f2c2018-08-31 09:22:23 +0100318
Aron Virginas-Tar1d67a6902018-11-19 10:58:30 +0000319 // Compare each output tensor to the expected values
320 for (auto&& it : expectedOutputData)
321 {
Jim Flynnb4d7eae2019-05-01 14:44:27 +0100322 armnn::BindingPointInfo bindingInfo = m_Parser->GetNetworkOutputBindingInfo(subgraphId, it.first);
Sadik Armagand109a4d2020-07-28 10:42:13 +0100323 auto outputExpected = MakeTensor<DataType2, NumOutputDimensions>(bindingInfo.second, it.second, isDynamic);
Colm Donelan25ab3a82021-05-17 13:01:52 +0100324 auto result = CompareTensors(outputExpected, outputStorage[it.first], false, isDynamic);
325 BOOST_TEST(result.m_Result, result.m_Message.str());
telsoa01c577f2c2018-08-31 09:22:23 +0100326 }
327}
keidav011b3e2ea2019-02-21 10:07:37 +0000328
329/// Multiple Inputs, Multiple Outputs w/ Variable Datatypes and different dimension sizes.
330/// Executes the network with the given input tensors and checks the results against the given output tensors.
331/// This overload supports multiple inputs and multiple outputs, identified by name along with the allowance for
332/// the input datatype to be different to the output.
333template <armnn::DataType armnnType1,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000334 armnn::DataType armnnType2>
keidav011b3e2ea2019-02-21 10:07:37 +0000335void ParserFlatbuffersFixture::RunTest(std::size_t subgraphId,
Rob Hughesfc6bf052019-12-16 17:10:51 +0000336 const std::map<std::string, std::vector<armnn::ResolveType<armnnType1>>>& inputData,
337 const std::map<std::string, std::vector<armnn::ResolveType<armnnType2>>>& expectedOutputData)
keidav011b3e2ea2019-02-21 10:07:37 +0000338{
Rob Hughesfc6bf052019-12-16 17:10:51 +0000339 using DataType2 = armnn::ResolveType<armnnType2>;
340
keidav011b3e2ea2019-02-21 10:07:37 +0000341 // Setup the armnn input tensors from the given vectors.
342 armnn::InputTensors inputTensors;
Sadik Armagan26868492021-01-22 14:25:31 +0000343 FillInputTensors<armnnType1>(inputTensors, inputData, subgraphId);
keidav011b3e2ea2019-02-21 10:07:37 +0000344
345 armnn::OutputTensors outputTensors;
346 outputTensors.reserve(expectedOutputData.size());
347 std::map<std::string, std::vector<DataType2>> outputStorage;
348 for (auto&& it : expectedOutputData)
349 {
Jim Flynnb4d7eae2019-05-01 14:44:27 +0100350 armnn::BindingPointInfo bindingInfo = m_Parser->GetNetworkOutputBindingInfo(subgraphId, it.first);
keidav011b3e2ea2019-02-21 10:07:37 +0000351 armnn::VerifyTensorInfoDataType(bindingInfo.second, armnnType2);
352
353 std::vector<DataType2> out(it.second.size());
354 outputStorage.emplace(it.first, out);
355 outputTensors.push_back({ bindingInfo.first,
356 armnn::Tensor(bindingInfo.second,
357 outputStorage.at(it.first).data()) });
358 }
359
360 m_Runtime->EnqueueWorkload(m_NetworkIdentifier, inputTensors, outputTensors);
361
362 // Checks the results.
363 for (auto&& it : expectedOutputData)
364 {
Rob Hughesfc6bf052019-12-16 17:10:51 +0000365 std::vector<armnn::ResolveType<armnnType2>> out = outputStorage.at(it.first);
keidav011b3e2ea2019-02-21 10:07:37 +0000366 {
367 for (unsigned int i = 0; i < out.size(); ++i)
368 {
369 BOOST_TEST(it.second[i] == out[i], boost::test_tools::tolerance(0.000001f));
370 }
371 }
372 }
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100373}
Sadik Armagan26868492021-01-22 14:25:31 +0000374
375/// Multiple Inputs with different DataTypes, Multiple Outputs w/ Variable DataTypes
376/// Executes the network with the given input tensors and checks the results against the given output tensors.
377/// This overload supports multiple inputs and multiple outputs, identified by name along with the allowance for
378/// the input datatype to be different to the output
379template <std::size_t NumOutputDimensions,
380 armnn::DataType inputType1,
381 armnn::DataType inputType2,
382 armnn::DataType outputType>
383void ParserFlatbuffersFixture::RunTest(size_t subgraphId,
384 const std::map<std::string, std::vector<armnn::ResolveType<inputType1>>>& input1Data,
385 const std::map<std::string, std::vector<armnn::ResolveType<inputType2>>>& input2Data,
386 const std::map<std::string, std::vector<armnn::ResolveType<outputType>>>& expectedOutputData)
387{
388 using DataType2 = armnn::ResolveType<outputType>;
389
390 // Setup the armnn input tensors from the given vectors.
391 armnn::InputTensors inputTensors;
392 FillInputTensors<inputType1>(inputTensors, input1Data, subgraphId);
393 FillInputTensors<inputType2>(inputTensors, input2Data, subgraphId);
394
395 // Allocate storage for the output tensors to be written to and setup the armnn output tensors.
396 std::map<std::string, boost::multi_array<DataType2, NumOutputDimensions>> outputStorage;
397 armnn::OutputTensors outputTensors;
398 for (auto&& it : expectedOutputData)
399 {
400 armnn::LayerBindingId outputBindingId = m_Parser->GetNetworkOutputBindingInfo(subgraphId, it.first).first;
401 armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkIdentifier, outputBindingId);
402
403 // Check that output tensors have correct number of dimensions (NumOutputDimensions specified in test)
404 auto outputNumDimensions = outputTensorInfo.GetNumDimensions();
405 BOOST_CHECK_MESSAGE((outputNumDimensions == NumOutputDimensions),
406 fmt::format("Number of dimensions expected {}, but got {} for output layer {}",
407 NumOutputDimensions,
408 outputNumDimensions,
409 it.first));
410
411 armnn::VerifyTensorInfoDataType(outputTensorInfo, outputType);
412 outputStorage.emplace(it.first, MakeTensor<DataType2, NumOutputDimensions>(outputTensorInfo));
413 outputTensors.push_back(
414 { outputBindingId, armnn::Tensor(outputTensorInfo, outputStorage.at(it.first).data()) });
415 }
416
417 m_Runtime->EnqueueWorkload(m_NetworkIdentifier, inputTensors, outputTensors);
418
419 // Compare each output tensor to the expected values
420 for (auto&& it : expectedOutputData)
421 {
422 armnn::BindingPointInfo bindingInfo = m_Parser->GetNetworkOutputBindingInfo(subgraphId, it.first);
423 auto outputExpected = MakeTensor<DataType2, NumOutputDimensions>(bindingInfo.second, it.second);
Colm Donelan25ab3a82021-05-17 13:01:52 +0100424 auto result = CompareTensors(outputExpected, outputStorage[it.first], false);
425 BOOST_TEST(result.m_Result, result.m_Message.str());
Sadik Armagan26868492021-01-22 14:25:31 +0000426 }
427}