blob: 6e0cc3154c68efa2c59b2b0cf4b80abfe4466e0a [file] [log] [blame]
Narumol Prangnawarat0b51d5a2021-01-20 15:58:29 +00001//
2// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#pragma once
7
8#include <armnn_delegate.hpp>
9
10#include "ConvolutionTestHelper.hpp"
11#include "TestUtils.hpp"
12
13#include <flatbuffers/flatbuffers.h>
14#include <tensorflow/lite/interpreter.h>
15#include <tensorflow/lite/kernels/register.h>
16#include <tensorflow/lite/model.h>
17#include <tensorflow/lite/schema/schema_generated.h>
18#include <tensorflow/lite/version.h>
19
20#include <doctest/doctest.h>
21
22namespace
23{
24
25struct StreamRedirector
26{
27public:
28 StreamRedirector(std::ostream &stream, std::streambuf *newStreamBuffer)
29 : m_Stream(stream), m_BackupBuffer(m_Stream.rdbuf(newStreamBuffer)) {}
30
31 ~StreamRedirector() { m_Stream.rdbuf(m_BackupBuffer); }
32
33private:
34 std::ostream &m_Stream;
35 std::streambuf *m_BackupBuffer;
36};
37
38std::vector<char> CreateAddDivTfLiteModel(tflite::TensorType tensorType,
39 const std::vector<int32_t>& tensorShape,
40 float quantScale = 1.0f,
41 int quantOffset = 0)
42{
43 using namespace tflite;
44 flatbuffers::FlatBufferBuilder flatBufferBuilder;
45
46 std::vector<flatbuffers::Offset<tflite::Buffer>> buffers;
47 buffers.push_back(CreateBuffer(flatBufferBuilder, flatBufferBuilder.CreateVector({})));
48
49 auto quantizationParameters =
50 CreateQuantizationParameters(flatBufferBuilder,
51 0,
52 0,
53 flatBufferBuilder.CreateVector<float>({ quantScale }),
54 flatBufferBuilder.CreateVector<int64_t>({ quantOffset }));
55
56
57 std::array<flatbuffers::Offset<Tensor>, 5> tensors;
58 tensors[0] = CreateTensor(flatBufferBuilder,
59 flatBufferBuilder.CreateVector<int32_t>(tensorShape.data(),
60 tensorShape.size()),
61 tensorType,
62 0,
63 flatBufferBuilder.CreateString("input_0"),
64 quantizationParameters);
65 tensors[1] = CreateTensor(flatBufferBuilder,
66 flatBufferBuilder.CreateVector<int32_t>(tensorShape.data(),
67 tensorShape.size()),
68 tensorType,
69 0,
70 flatBufferBuilder.CreateString("input_1"),
71 quantizationParameters);
72 tensors[2] = CreateTensor(flatBufferBuilder,
73 flatBufferBuilder.CreateVector<int32_t>(tensorShape.data(),
74 tensorShape.size()),
75 tensorType,
76 0,
77 flatBufferBuilder.CreateString("input_2"),
78 quantizationParameters);
79 tensors[3] = CreateTensor(flatBufferBuilder,
80 flatBufferBuilder.CreateVector<int32_t>(tensorShape.data(),
81 tensorShape.size()),
82 tensorType,
83 0,
84 flatBufferBuilder.CreateString("add"),
85 quantizationParameters);
86 tensors[4] = CreateTensor(flatBufferBuilder,
87 flatBufferBuilder.CreateVector<int32_t>(tensorShape.data(),
88 tensorShape.size()),
89 tensorType,
90 0,
91 flatBufferBuilder.CreateString("output"),
92 quantizationParameters);
93
94 // create operator
95 tflite::BuiltinOptions addBuiltinOptionsType = tflite::BuiltinOptions_AddOptions;
96 flatbuffers::Offset<void> addBuiltinOptions =
97 CreateAddOptions(flatBufferBuilder, ActivationFunctionType_NONE).Union();
98
99 tflite::BuiltinOptions divBuiltinOptionsType = tflite::BuiltinOptions_DivOptions;
100 flatbuffers::Offset<void> divBuiltinOptions =
101 CreateAddOptions(flatBufferBuilder, ActivationFunctionType_NONE).Union();
102
103 std::array<flatbuffers::Offset<Operator>, 2> operators;
104 const std::vector<int32_t> addInputs{0, 1};
105 const std::vector<int32_t> addOutputs{3};
106 operators[0] = CreateOperator(flatBufferBuilder,
107 0,
108 flatBufferBuilder.CreateVector<int32_t>(addInputs.data(), addInputs.size()),
109 flatBufferBuilder.CreateVector<int32_t>(addOutputs.data(), addOutputs.size()),
110 addBuiltinOptionsType,
111 addBuiltinOptions);
112 const std::vector<int32_t> divInputs{3, 2};
113 const std::vector<int32_t> divOutputs{4};
114 operators[1] = CreateOperator(flatBufferBuilder,
115 1,
116 flatBufferBuilder.CreateVector<int32_t>(divInputs.data(), divInputs.size()),
117 flatBufferBuilder.CreateVector<int32_t>(divOutputs.data(), divOutputs.size()),
118 divBuiltinOptionsType,
119 divBuiltinOptions);
120
121 const std::vector<int> subgraphInputs{0, 1, 2};
122 const std::vector<int> subgraphOutputs{4};
123 flatbuffers::Offset<SubGraph> subgraph =
124 CreateSubGraph(flatBufferBuilder,
125 flatBufferBuilder.CreateVector(tensors.data(), tensors.size()),
126 flatBufferBuilder.CreateVector<int32_t>(subgraphInputs.data(), subgraphInputs.size()),
127 flatBufferBuilder.CreateVector<int32_t>(subgraphOutputs.data(), subgraphOutputs.size()),
128 flatBufferBuilder.CreateVector(operators.data(), operators.size()));
129
130 flatbuffers::Offset<flatbuffers::String> modelDescription =
131 flatBufferBuilder.CreateString("ArmnnDelegate: Add and Div Operator Model");
132
133 std::array<flatbuffers::Offset<OperatorCode>, 2> codes;
134 codes[0] = CreateOperatorCode(flatBufferBuilder, tflite::BuiltinOperator_ADD);
135 codes[1] = CreateOperatorCode(flatBufferBuilder, tflite::BuiltinOperator_DIV);
136
137 flatbuffers::Offset<Model> flatbufferModel =
138 CreateModel(flatBufferBuilder,
139 TFLITE_SCHEMA_VERSION,
140 flatBufferBuilder.CreateVector(codes.data(), codes.size()),
141 flatBufferBuilder.CreateVector(&subgraph, 1),
142 modelDescription,
143 flatBufferBuilder.CreateVector(buffers.data(), buffers.size()));
144
145 flatBufferBuilder.Finish(flatbufferModel);
146
147 return std::vector<char>(flatBufferBuilder.GetBufferPointer(),
148 flatBufferBuilder.GetBufferPointer() + flatBufferBuilder.GetSize());
149}
150
151void ReduceFp32ToBf16TestImpl()
152{
153 using namespace tflite;
154 // Set input data
155 std::vector<int32_t> inputShape{ 1, 5, 5, 1 };
156 std::vector<int32_t> filterShape{ 1, 3, 3, 1 };
157 std::vector<int32_t> biasShape{ 1 };
158 std::vector<int32_t> outputShape{ 1, 3, 3, 1 };
159
160 std::vector<float> inputValues =
161 {
162 1, 5, 2, 3, 5,
163 8, 7, 3, 6, 3,
164 3, 3, 9, 1, 9,
165 4, 1, 8, 1, 3,
166 6, 8, 1, 9, 2
167 };
168
169 std::vector<float> filterValues =
170 {
171 4, 5, 6,
172 0, 0, 0,
173 3, 2, 1
174 };
175
176 std::vector<float> biasValues = { 5 };
177
178 std::vector<float> expectedResult =
179 {
180 28, 38, 29,
181 96, 104, 53,
182 31, 55, 24
183 };
184
185 tflite::Padding padding = Padding_SAME;
186
187 std::vector<char> modelBuffer;
188 modelBuffer = CreateConv2dTfLiteModel<float>(BuiltinOperator_CONV_2D,
189 ::tflite::TensorType_FLOAT32,
190 2,
191 2,
192 1,
193 1,
194 padding,
195 ActivationFunctionType_NONE,
196 inputShape,
197 filterShape,
198 biasShape,
199 outputShape,
200 filterValues,
201 biasValues);
202
203
204 const Model* tfLiteModel = GetModel(modelBuffer.data());
205 // Create TfLite Interpreters
206 std::unique_ptr<Interpreter> armnnDelegateInterpreter;
207 CHECK(InterpreterBuilder(tfLiteModel, ::tflite::ops::builtin::BuiltinOpResolver())
208 (&armnnDelegateInterpreter) == kTfLiteOk);
209 CHECK(armnnDelegateInterpreter != nullptr);
210 CHECK(armnnDelegateInterpreter->AllocateTensors() == kTfLiteOk);
211
212 // Create the Armnn Delegate
213 std::vector<armnn::BackendId> backends = {armnn::Compute::CpuRef};
214 std::vector<armnn::BackendOptions> backendOptions;
215
216 // Enable debug with BF16 enabled
217 armnn::OptimizerOptions optimizerOptions(false, true, true, false);
218
219 armnnDelegate::DelegateOptions delegateOptions(backends, optimizerOptions);
220 std::unique_ptr<TfLiteDelegate, decltype(&armnnDelegate::TfLiteArmnnDelegateDelete)>
221 theArmnnDelegate(armnnDelegate::TfLiteArmnnDelegateCreate(delegateOptions),
222 armnnDelegate::TfLiteArmnnDelegateDelete);
223 CHECK(theArmnnDelegate != nullptr);
224 // Modify armnnDelegateInterpreter to use armnnDelegate
225 CHECK(armnnDelegateInterpreter->ModifyGraphWithDelegate(theArmnnDelegate.get()) == kTfLiteOk);
226
227 // Set input data
228 armnnDelegate::FillInput(armnnDelegateInterpreter, 0, inputValues);
229
230 // Run EnqueueWorkload
231 CHECK(armnnDelegateInterpreter->Invoke() == kTfLiteOk);
232
233 // Compare output data
234 auto armnnDelegateOutputId = armnnDelegateInterpreter->outputs()[0];
235 auto armnnDelegateOutputData = armnnDelegateInterpreter->typed_tensor<float>(armnnDelegateOutputId);
236 armnnDelegate::CompareData(expectedResult.data(), armnnDelegateOutputData, expectedResult.size());
237 armnnDelegateInterpreter.reset(nullptr);
238}
239
240template <typename T>
241void DelegateOptionTest(tflite::TensorType tensorType,
242 const std::vector<armnn::BackendId>& backends,
243 std::vector<int32_t>& tensorShape,
244 std::vector<T>& input0Values,
245 std::vector<T>& input1Values,
246 std::vector<T>& input2Values,
247 std::vector<T>& expectedOutputValues,
248 const armnnDelegate::DelegateOptions& delegateOptions,
249 float quantScale = 1.0f,
250 int quantOffset = 0)
251{
252 using namespace tflite;
253 std::vector<char> modelBuffer = CreateAddDivTfLiteModel(tensorType,
254 tensorShape,
255 quantScale,
256 quantOffset);
257
258 const Model* tfLiteModel = GetModel(modelBuffer.data());
259 // Create TfLite Interpreters
260 std::unique_ptr<Interpreter> armnnDelegateInterpreter;
261 CHECK(InterpreterBuilder(tfLiteModel, ::tflite::ops::builtin::BuiltinOpResolver())
262 (&armnnDelegateInterpreter) == kTfLiteOk);
263 CHECK(armnnDelegateInterpreter != nullptr);
264 CHECK(armnnDelegateInterpreter->AllocateTensors() == kTfLiteOk);
265
266 std::unique_ptr<Interpreter> tfLiteInterpreter;
267 CHECK(InterpreterBuilder(tfLiteModel, ::tflite::ops::builtin::BuiltinOpResolver())
268 (&tfLiteInterpreter) == kTfLiteOk);
269 CHECK(tfLiteInterpreter != nullptr);
270 CHECK(tfLiteInterpreter->AllocateTensors() == kTfLiteOk);
271
272 // Create the ArmNN Delegate
273 std::unique_ptr<TfLiteDelegate, decltype(&armnnDelegate::TfLiteArmnnDelegateDelete)>
274 theArmnnDelegate(armnnDelegate::TfLiteArmnnDelegateCreate(delegateOptions),
275 armnnDelegate::TfLiteArmnnDelegateDelete);
276 CHECK(theArmnnDelegate != nullptr);
277 // Modify armnnDelegateInterpreter to use armnnDelegate
278 CHECK(armnnDelegateInterpreter->ModifyGraphWithDelegate(theArmnnDelegate.get()) == kTfLiteOk);
279
280 // Set input data
281 armnnDelegate::FillInput(tfLiteInterpreter, 0, input0Values);
282 armnnDelegate::FillInput(tfLiteInterpreter, 1, input1Values);
283 armnnDelegate::FillInput(tfLiteInterpreter, 2, input2Values);
284
285 armnnDelegate::FillInput(armnnDelegateInterpreter, 0, input0Values);
286 armnnDelegate::FillInput(armnnDelegateInterpreter, 1, input1Values);
287 armnnDelegate::FillInput(armnnDelegateInterpreter, 2, input2Values);
288
289 // Run EnqueueWorkload
290 CHECK(tfLiteInterpreter->Invoke() == kTfLiteOk);
291 CHECK(armnnDelegateInterpreter->Invoke() == kTfLiteOk);
292
293 armnnDelegate::CompareOutputData<T>(tfLiteInterpreter, armnnDelegateInterpreter, tensorShape, expectedOutputValues);
294
295 armnnDelegateInterpreter.reset(nullptr);
296}
297
298} // anonymous namespace