blob: e9cbb2460497f6a1343b8acaaa2cbccb846be829 [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
8#include <boost/filesystem.hpp>
9#include <boost/assert.hpp>
10#include <boost/format.hpp>
11#include <experimental/filesystem>
12#include <armnn/IRuntime.hpp>
13#include <armnn/TypesUtils.hpp>
14#include "test/TensorHelpers.hpp"
15
16#include "armnnTfLiteParser/ITfLiteParser.hpp"
17
18#include "flatbuffers/idl.h"
19#include "flatbuffers/util.h"
20
21#include <schema_generated.h>
22#include <iostream>
23
24using armnnTfLiteParser::ITfLiteParser;
25using TensorRawPtr = const tflite::TensorT *;
26
27struct ParserFlatbuffersFixture
28{
29 ParserFlatbuffersFixture()
30 : m_Parser(ITfLiteParser::Create()), m_NetworkIdentifier(-1)
31 {
32 armnn::IRuntime::CreationOptions options;
33 m_Runtimes.push_back(std::make_pair(armnn::IRuntime::Create(options), armnn::Compute::CpuRef));
34
35#if ARMCOMPUTENEON_ENABLED
36 m_Runtimes.push_back(std::make_pair(armnn::IRuntime::Create(options), armnn::Compute::CpuAcc));
37#endif
38
39#if ARMCOMPUTECL_ENABLED
40 m_Runtimes.push_back(std::make_pair(armnn::IRuntime::Create(options), armnn::Compute::GpuAcc));
41#endif
42 }
43
44 std::vector<uint8_t> m_GraphBinary;
45 std::string m_JsonString;
46 std::unique_ptr<ITfLiteParser, void (*)(ITfLiteParser *parser)> m_Parser;
David Beckf0b48452018-10-19 15:20:56 +010047 std::vector<std::pair<armnn::IRuntimePtr, armnn::BackendId>> m_Runtimes;
telsoa01c577f2c2018-08-31 09:22:23 +010048 armnn::NetworkId m_NetworkIdentifier;
49
50 /// If the single-input-single-output overload of Setup() is called, these will store the input and output name
51 /// so they don't need to be passed to the single-input-single-output overload of RunTest().
52 std::string m_SingleInputName;
53 std::string m_SingleOutputName;
54
55 void Setup()
56 {
57 bool ok = ReadStringToBinary();
58 if (!ok) {
59 throw armnn::Exception("LoadNetwork failed while reading binary input");
60 }
61
62 for (auto&& runtime : m_Runtimes)
63 {
64 armnn::INetworkPtr network =
65 m_Parser->CreateNetworkFromBinary(m_GraphBinary);
66
67 if (!network) {
68 throw armnn::Exception("The parser failed to create an ArmNN network");
69 }
70
71 auto optimized = Optimize(*network,
72 { runtime.second, armnn::Compute::CpuRef },
73 runtime.first->GetDeviceSpec());
74 std::string errorMessage;
75
76 armnn::Status ret = runtime.first->LoadNetwork(m_NetworkIdentifier,
77 move(optimized),
78 errorMessage);
79
80 if (ret != armnn::Status::Success)
81 {
82 throw armnn::Exception(
83 boost::str(
84 boost::format("The runtime failed to load the network. "
85 "Error was: %1%. in %2% [%3%:%4%]") %
86 errorMessage %
87 __func__ %
88 __FILE__ %
89 __LINE__));
90 }
91 }
92 }
93
94 void SetupSingleInputSingleOutput(const std::string& inputName, const std::string& outputName)
95 {
96 // Store the input and output name so they don't need to be passed to the single-input-single-output RunTest().
97 m_SingleInputName = inputName;
98 m_SingleOutputName = outputName;
99 Setup();
100 }
101
102 bool ReadStringToBinary()
103 {
104 const char* schemafileName = getenv("ARMNN_TF_LITE_SCHEMA_PATH");
105 if (schemafileName == nullptr)
106 {
107 schemafileName = ARMNN_TF_LITE_SCHEMA_PATH;
108 }
109 std::string schemafile;
110
111 bool ok = flatbuffers::LoadFile(schemafileName, false, &schemafile);
112 BOOST_ASSERT_MSG(ok, "Couldn't load schema file " ARMNN_TF_LITE_SCHEMA_PATH);
113 if (!ok)
114 {
115 return false;
116 }
117
118 // parse schema first, so we can use it to parse the data after
119 flatbuffers::Parser parser;
120
121 ok &= parser.Parse(schemafile.c_str());
122 BOOST_ASSERT_MSG(ok, "Failed to parse schema file");
123
124 ok &= parser.Parse(m_JsonString.c_str());
125 BOOST_ASSERT_MSG(ok, "Failed to parse json input");
126
127 if (!ok)
128 {
129 return false;
130 }
131
132 {
133 const uint8_t * bufferPtr = parser.builder_.GetBufferPointer();
134 size_t size = static_cast<size_t>(parser.builder_.GetSize());
135 m_GraphBinary.assign(bufferPtr, bufferPtr+size);
136 }
137 return ok;
138 }
139
140 /// Executes the network with the given input tensor and checks the result against the given output tensor.
141 /// This overload assumes the network has a single input and a single output.
142 template <std::size_t NumOutputDimensions, typename DataType>
143 void RunTest(size_t subgraphId,
144 const std::vector<DataType>& inputData,
145 const std::vector<DataType>& expectedOutputData);
146
147 /// Executes the network with the given input tensors and checks the results against the given output tensors.
148 /// This overload supports multiple inputs and multiple outputs, identified by name.
149 template <std::size_t NumOutputDimensions, typename DataType>
150 void RunTest(size_t subgraphId,
151 const std::map<std::string, std::vector<DataType>>& inputData,
152 const std::map<std::string, std::vector<DataType>>& expectedOutputData);
153
154 void CheckTensors(const TensorRawPtr& tensors, size_t shapeSize, const std::vector<int32_t>& shape,
155 tflite::TensorType tensorType, uint32_t buffer, const std::string& name,
156 const std::vector<float>& min, const std::vector<float>& max,
157 const std::vector<float>& scale, const std::vector<int64_t>& zeroPoint)
158 {
159 BOOST_CHECK(tensors);
160 BOOST_CHECK_EQUAL(shapeSize, tensors->shape.size());
161 BOOST_CHECK_EQUAL_COLLECTIONS(shape.begin(), shape.end(), tensors->shape.begin(), tensors->shape.end());
162 BOOST_CHECK_EQUAL(tensorType, tensors->type);
163 BOOST_CHECK_EQUAL(buffer, tensors->buffer);
164 BOOST_CHECK_EQUAL(name, tensors->name);
165 BOOST_CHECK(tensors->quantization);
166 BOOST_CHECK_EQUAL_COLLECTIONS(min.begin(), min.end(), tensors->quantization.get()->min.begin(),
167 tensors->quantization.get()->min.end());
168 BOOST_CHECK_EQUAL_COLLECTIONS(max.begin(), max.end(), tensors->quantization.get()->max.begin(),
169 tensors->quantization.get()->max.end());
170 BOOST_CHECK_EQUAL_COLLECTIONS(scale.begin(), scale.end(), tensors->quantization.get()->scale.begin(),
171 tensors->quantization.get()->scale.end());
172 BOOST_CHECK_EQUAL_COLLECTIONS(zeroPoint.begin(), zeroPoint.end(),
173 tensors->quantization.get()->zero_point.begin(),
174 tensors->quantization.get()->zero_point.end());
175 }
176};
177
178template <std::size_t NumOutputDimensions, typename DataType>
179void ParserFlatbuffersFixture::RunTest(size_t subgraphId,
180 const std::vector<DataType>& inputData,
181 const std::vector<DataType>& expectedOutputData)
182{
183 RunTest<NumOutputDimensions, DataType>(subgraphId,
184 { { m_SingleInputName, inputData } },
185 { { m_SingleOutputName, expectedOutputData } });
186}
187
188template <std::size_t NumOutputDimensions, typename DataType>
189void
190ParserFlatbuffersFixture::RunTest(size_t subgraphId,
191 const std::map<std::string, std::vector<DataType>>& inputData,
192 const std::map<std::string, std::vector<DataType>>& expectedOutputData)
193{
194 for (auto&& runtime : m_Runtimes)
195 {
196 using BindingPointInfo = std::pair<armnn::LayerBindingId, armnn::TensorInfo>;
197
198 // Setup the armnn input tensors from the given vectors.
199 armnn::InputTensors inputTensors;
200 for (auto&& it : inputData)
201 {
202 BindingPointInfo bindingInfo = m_Parser->GetNetworkInputBindingInfo(subgraphId, it.first);
203 armnn::VerifyTensorInfoDataType<DataType>(bindingInfo.second);
204 inputTensors.push_back({ bindingInfo.first, armnn::ConstTensor(bindingInfo.second, it.second.data()) });
205 }
206
207 // Allocate storage for the output tensors to be written to and setup the armnn output tensors.
208 std::map<std::string, boost::multi_array<DataType, NumOutputDimensions>> outputStorage;
209 armnn::OutputTensors outputTensors;
210 for (auto&& it : expectedOutputData)
211 {
212 BindingPointInfo bindingInfo = m_Parser->GetNetworkOutputBindingInfo(subgraphId, it.first);
213 armnn::VerifyTensorInfoDataType<DataType>(bindingInfo.second);
214 outputStorage.emplace(it.first, MakeTensor<DataType, NumOutputDimensions>(bindingInfo.second));
215 outputTensors.push_back(
216 { bindingInfo.first, armnn::Tensor(bindingInfo.second, outputStorage.at(it.first).data()) });
217 }
218
219 runtime.first->EnqueueWorkload(m_NetworkIdentifier, inputTensors, outputTensors);
220
221 // Compare each output tensor to the expected values
222 for (auto&& it : expectedOutputData)
223 {
224 BindingPointInfo bindingInfo = m_Parser->GetNetworkOutputBindingInfo(subgraphId, it.first);
225 auto outputExpected = MakeTensor<DataType, NumOutputDimensions>(bindingInfo.second, it.second);
226 BOOST_TEST(CompareTensors(outputExpected, outputStorage[it.first]));
227 }
228 }
229}