Imported Upstream version 1.12.0
[platform/core/ml/nnfw.git] / runtime / libs / tflite / src / RandomTestRunner.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *    http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "tflite/RandomTestRunner.h"
18 #include "tflite/Diff.h"
19 #include "tflite/TensorLogger.h"
20 #include "tflite/ext/nnapi_delegate.h"
21
22 #include <misc/tensor/IndexIterator.h>
23 #include <misc/tensor/Object.h>
24 #include <misc/EnvVar.h>
25 #include <misc/fp32.h>
26
27 #include <cassert>
28 #include <map>
29 #include <functional>
30 #include <iostream>
31
32 namespace nnfw
33 {
34 namespace tflite
35 {
36
37 using namespace std::placeholders;
38
39 void RandomTestRunner::compile(const nnfw::tflite::Builder &builder)
40 {
41   _tfl_interp = builder.build();
42   _nnapi = builder.build();
43
44   _tfl_interp->UseNNAPI(false);
45
46   // Allocate Tensors
47   _tfl_interp->AllocateTensors();
48   _nnapi->AllocateTensors();
49
50   assert(_tfl_interp->inputs() == _nnapi->inputs());
51
52   using ::tflite::Interpreter;
53   using Initializer = std::function<void(int id, Interpreter *, Interpreter *)>;
54
55   std::map<TfLiteType, Initializer> initializers;
56   std::map<TfLiteType, Initializer> reseters;
57
58   // Generate singed 32-bit integer (s32) input
59   initializers[kTfLiteInt32] = [&](int id, Interpreter *tfl_interp, Interpreter *nnapi) {
60     assert(_tfl_interp->tensor(id)->type == kTfLiteInt32);
61     assert(_nnapi->tensor(id)->type == kTfLiteInt32);
62
63     auto tfl_interp_view = nnfw::tflite::TensorView<int32_t>::make(*tfl_interp, id);
64     auto nnapi_view = nnfw::tflite::TensorView<int32_t>::make(*nnapi, id);
65
66     assert(tfl_interp_view.shape() == nnapi_view.shape());
67
68     int32_t value = 0;
69
70     nnfw::misc::tensor::iterate(tfl_interp_view.shape())
71       << [&](const nnfw::misc::tensor::Index &ind) {
72            // TODO Generate random values
73            tfl_interp_view.at(ind) = value;
74            nnapi_view.at(ind) = value;
75            ++value;
76          };
77   };
78
79   // Generate singed 32-bit integer (s32) input
80   reseters[kTfLiteInt32] = [&](int id, Interpreter *tfl_interp, Interpreter *nnapi) {
81     assert(_tfl_interp->tensor(id)->type == kTfLiteInt32);
82     assert(_nnapi->tensor(id)->type == kTfLiteInt32);
83
84     auto tfl_interp_view = nnfw::tflite::TensorView<int32_t>::make(*tfl_interp, id);
85     auto nnapi_view = nnfw::tflite::TensorView<int32_t>::make(*nnapi, id);
86
87     assert(tfl_interp_view.shape() == nnapi_view.shape());
88
89     int32_t value = 0;
90
91     nnfw::misc::tensor::iterate(tfl_interp_view.shape())
92       << [&](const nnfw::misc::tensor::Index &ind) {
93            // TODO Generate random values
94            tfl_interp_view.at(ind) = value;
95            nnapi_view.at(ind) = value;
96          };
97   };
98
99   initializers[kTfLiteUInt8] = [&](int id, Interpreter *tfl_interp, Interpreter *nnapi) {
100     assert(_tfl_interp->tensor(id)->type == kTfLiteUInt8);
101     assert(_nnapi->tensor(id)->type == kTfLiteUInt8);
102
103     auto tfl_interp_view = nnfw::tflite::TensorView<uint8_t>::make(*tfl_interp, id);
104     auto nnapi_view = nnfw::tflite::TensorView<uint8_t>::make(*nnapi, id);
105
106     assert(tfl_interp_view.shape() == nnapi_view.shape());
107
108     auto fp = static_cast<uint8_t (nnfw::misc::RandomGenerator::*)(
109       const ::nnfw::misc::tensor::Shape &, const ::nnfw::misc::tensor::Index &)>(
110       &nnfw::misc::RandomGenerator::generate<uint8_t>);
111     const nnfw::misc::tensor::Object<uint8_t> data(tfl_interp_view.shape(),
112                                                    std::bind(fp, _randgen, _1, _2));
113     assert(tfl_interp_view.shape() == data.shape());
114
115     nnfw::misc::tensor::iterate(tfl_interp_view.shape())
116       << [&](const nnfw::misc::tensor::Index &ind) {
117            const auto value = data.at(ind);
118
119            tfl_interp_view.at(ind) = value;
120            nnapi_view.at(ind) = value;
121          };
122   };
123
124   reseters[kTfLiteUInt8] = [&](int id, Interpreter *tfl_interp, Interpreter *nnapi) {
125     assert(_tfl_interp->tensor(id)->type == kTfLiteUInt8);
126     assert(_nnapi->tensor(id)->type == kTfLiteUInt8);
127
128     auto tfl_interp_view = nnfw::tflite::TensorView<uint8_t>::make(*tfl_interp, id);
129     auto nnapi_view = nnfw::tflite::TensorView<uint8_t>::make(*nnapi, id);
130
131     assert(tfl_interp_view.shape() == nnapi_view.shape());
132
133     auto fp = static_cast<uint8_t (nnfw::misc::RandomGenerator::*)(
134       const ::nnfw::misc::tensor::Shape &, const ::nnfw::misc::tensor::Index &)>(
135       &nnfw::misc::RandomGenerator::generate<uint8_t>);
136     const nnfw::misc::tensor::Object<uint8_t> data(tfl_interp_view.shape(),
137                                                    std::bind(fp, _randgen, _1, _2));
138     assert(tfl_interp_view.shape() == data.shape());
139
140     uint8_t value = 0;
141
142     nnfw::misc::tensor::iterate(tfl_interp_view.shape())
143       << [&](const nnfw::misc::tensor::Index &ind) {
144            tfl_interp_view.at(ind) = value;
145            nnapi_view.at(ind) = value;
146          };
147   };
148
149   initializers[kTfLiteFloat32] = [&](int id, Interpreter *tfl_interp, Interpreter *nnapi) {
150     assert(_tfl_interp->tensor(id)->type == kTfLiteFloat32);
151     assert(_nnapi->tensor(id)->type == kTfLiteFloat32);
152
153     auto tfl_interp_view = nnfw::tflite::TensorView<float>::make(*tfl_interp, id);
154     auto nnapi_view = nnfw::tflite::TensorView<float>::make(*nnapi, id);
155
156     assert(tfl_interp_view.shape() == nnapi_view.shape());
157
158     auto fp = static_cast<float (nnfw::misc::RandomGenerator::*)(
159       const ::nnfw::misc::tensor::Shape &, const ::nnfw::misc::tensor::Index &)>(
160       &nnfw::misc::RandomGenerator::generate<float>);
161     const nnfw::misc::tensor::Object<float> data(tfl_interp_view.shape(),
162                                                  std::bind(fp, _randgen, _1, _2));
163
164     assert(tfl_interp_view.shape() == data.shape());
165
166     nnfw::misc::tensor::iterate(tfl_interp_view.shape())
167       << [&](const nnfw::misc::tensor::Index &ind) {
168            const auto value = data.at(ind);
169
170            tfl_interp_view.at(ind) = value;
171            nnapi_view.at(ind) = value;
172          };
173   };
174
175   reseters[kTfLiteFloat32] = [&](int id, Interpreter *tfl_interp, Interpreter *nnapi) {
176     assert(_tfl_interp->tensor(id)->type == kTfLiteFloat32);
177     assert(_nnapi->tensor(id)->type == kTfLiteFloat32);
178
179     auto tfl_interp_view = nnfw::tflite::TensorView<float>::make(*tfl_interp, id);
180     auto nnapi_view = nnfw::tflite::TensorView<float>::make(*nnapi, id);
181
182     assert(tfl_interp_view.shape() == nnapi_view.shape());
183
184     auto fp = static_cast<float (nnfw::misc::RandomGenerator::*)(
185       const ::nnfw::misc::tensor::Shape &, const ::nnfw::misc::tensor::Index &)>(
186       &nnfw::misc::RandomGenerator::generate<float>);
187     const nnfw::misc::tensor::Object<float> data(tfl_interp_view.shape(),
188                                                  std::bind(fp, _randgen, _1, _2));
189
190     assert(tfl_interp_view.shape() == data.shape());
191
192     float value = 0;
193
194     nnfw::misc::tensor::iterate(tfl_interp_view.shape())
195       << [&](const nnfw::misc::tensor::Index &ind) {
196            tfl_interp_view.at(ind) = value;
197            nnapi_view.at(ind) = value;
198          };
199   };
200
201   initializers[kTfLiteBool] = [&](int id, Interpreter *tfl_interp, Interpreter *nnapi) {
202     assert(_tfl_interp->tensor(id)->type == kTfLiteBool);
203     assert(_nnapi->tensor(id)->type == kTfLiteBool);
204
205     auto tfl_interp_view = nnfw::tflite::TensorView<bool>::make(*tfl_interp, id);
206     auto nnapi_view = nnfw::tflite::TensorView<bool>::make(*nnapi, id);
207
208     assert(tfl_interp_view.shape() == nnapi_view.shape());
209
210     auto fp = static_cast<bool (nnfw::misc::RandomGenerator::*)(
211       const ::nnfw::misc::tensor::Shape &, const ::nnfw::misc::tensor::Index &)>(
212       &nnfw::misc::RandomGenerator::generate<bool>);
213     const nnfw::misc::tensor::Object<bool> data(tfl_interp_view.shape(),
214                                                 std::bind(fp, _randgen, _1, _2));
215
216     assert(tfl_interp_view.shape() == data.shape());
217
218     nnfw::misc::tensor::iterate(tfl_interp_view.shape())
219       << [&](const nnfw::misc::tensor::Index &ind) {
220            const auto value = data.at(ind);
221
222            tfl_interp_view.at(ind) = value;
223            nnapi_view.at(ind) = value;
224          };
225   };
226
227   reseters[kTfLiteBool] = [&](int id, Interpreter *tfl_interp, Interpreter *nnapi) {
228     assert(_tfl_interp->tensor(id)->type == kTfLiteBool);
229     assert(_nnapi->tensor(id)->type == kTfLiteBool);
230
231     auto tfl_interp_view = nnfw::tflite::TensorView<bool>::make(*tfl_interp, id);
232     auto nnapi_view = nnfw::tflite::TensorView<bool>::make(*nnapi, id);
233
234     assert(tfl_interp_view.shape() == nnapi_view.shape());
235
236     auto fp = static_cast<bool (nnfw::misc::RandomGenerator::*)(
237       const ::nnfw::misc::tensor::Shape &, const ::nnfw::misc::tensor::Index &)>(
238       &nnfw::misc::RandomGenerator::generate<bool>);
239     const nnfw::misc::tensor::Object<bool> data(tfl_interp_view.shape(),
240                                                 std::bind(fp, _randgen, _1, _2));
241
242     assert(tfl_interp_view.shape() == data.shape());
243
244     bool value = false;
245
246     nnfw::misc::tensor::iterate(tfl_interp_view.shape())
247       << [&](const nnfw::misc::tensor::Index &ind) {
248            tfl_interp_view.at(ind) = value;
249            nnapi_view.at(ind) = value;
250          };
251   };
252
253   // Fill IFM with random numbers
254   for (const auto id : _tfl_interp->inputs())
255   {
256     assert(_tfl_interp->tensor(id)->type == _nnapi->tensor(id)->type);
257
258     auto it = initializers.find(_tfl_interp->tensor(id)->type);
259
260     if (it == initializers.end())
261     {
262       throw std::runtime_error{"Not supported input type"};
263     }
264
265     it->second(id, _tfl_interp.get(), _nnapi.get());
266   }
267
268   // Fill OFM with 0
269   for (const auto id : _tfl_interp->outputs())
270   {
271     assert(_tfl_interp->tensor(id)->type == _nnapi->tensor(id)->type);
272
273     auto it = reseters.find(_tfl_interp->tensor(id)->type);
274
275     if (it == reseters.end())
276     {
277       throw std::runtime_error{"Not supported input type"};
278     }
279
280     it->second(id, _tfl_interp.get(), _nnapi.get());
281   }
282 }
283
284 int RandomTestRunner::run(size_t running_count)
285 {
286   std::cout << "[NNAPI TEST] Run T/F Lite Interpreter without NNAPI" << std::endl;
287   _tfl_interp->Invoke();
288
289   nnfw::tflite::NNAPIDelegate d;
290
291   for (size_t i = 1; i <= running_count; ++i)
292   {
293     std::cout << "[NNAPI TEST #" << i << "] Run T/F Lite Interpreter with NNAPI" << std::endl;
294
295     char *env = getenv("UPSTREAM_DELEGATE");
296
297     if (env && !std::string(env).compare("1"))
298     {
299       _nnapi->UseNNAPI(true);
300       _nnapi->Invoke();
301     }
302     else
303     {
304       // WARNING
305       // primary_subgraph: Experimental interface. Return 1st sugbraph
306       // Invoke() will call BuildGraph() internally
307       if (d.Invoke(&_nnapi.get()->primary_subgraph()))
308       {
309         throw std::runtime_error{"Failed to BuildGraph"};
310       }
311     }
312
313     // Compare OFM
314     std::cout << "[NNAPI TEST #" << i << "] Compare the result" << std::endl;
315
316     const auto tolerance = _param.tolerance;
317
318     auto equals = [tolerance](float lhs, float rhs) {
319       // NOTE Hybrid approach
320       // TODO Allow users to set tolerance for absolute_epsilon_equal
321       if (nnfw::misc::fp32::absolute_epsilon_equal(lhs, rhs))
322       {
323         return true;
324       }
325
326       return nnfw::misc::fp32::epsilon_equal(lhs, rhs, tolerance);
327     };
328
329     nnfw::misc::tensor::Comparator comparator(equals);
330     TfLiteInterpMatchApp app(comparator);
331
332     app.verbose() = _param.verbose;
333
334     bool res = app.run(*_tfl_interp, *_nnapi);
335
336     if (!res)
337     {
338       return 255;
339     }
340
341     std::cout << "[NNAPI TEST #" << i << "] PASSED" << std::endl << std::endl;
342
343     if (_param.tensor_logging)
344       nnfw::tflite::TensorLogger::get().save(_param.log_path, *_tfl_interp);
345   }
346
347   return 0;
348 }
349
350 RandomTestRunner RandomTestRunner::make(uint32_t seed)
351 {
352   RandomTestParam param;
353
354   param.verbose = nnfw::misc::EnvVar("VERBOSE").asInt(0);
355   param.tolerance = nnfw::misc::EnvVar("TOLERANCE").asInt(1);
356   param.tensor_logging = nnfw::misc::EnvVar("TENSOR_LOGGING").asBool(false);
357   param.log_path = nnfw::misc::EnvVar("TENSOR_LOGGING").asString("tensor_log.txt");
358
359   return RandomTestRunner{seed, param};
360 }
361
362 } // namespace tflite
363 } // namespace nnfw