7313086382d97f2e3f13a512e3604e11e26ddc4e
[platform/core/ml/nnfw.git] / tests / custom_op / FillFrom / FillFrom_runner.cc
1 /*
2  * Copyright (c) 2019 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 "nnfw.h"
18 #include "nnfw_experimental.h"
19
20 #include <cassert>
21 #include <iostream>
22 #include <vector>
23 #include <chrono>
24 #include <cmath>
25
26 #define NNPR_ENSURE_STATUS(a)        \
27   do                                 \
28   {                                  \
29     if ((a) != NNFW_STATUS_NO_ERROR) \
30     {                                \
31       exit(-1);                      \
32     }                                \
33   } while (0)
34
35 extern "C" void FillFromEval(nnfw_custom_kernel_params *params, char *userdata,
36                              size_t userdata_size);
37
38 const nnfw_custom_eval custom_func_ptr_list[] = {FillFromEval};
39 const char *custom_func_name_list[] = {"FillFrom"};
40 int custom_func_list_size = 1;
41
42 void register_custom_operations(nnfw_session *session)
43 {
44   for (int i = 0; i < custom_func_list_size; ++i)
45   {
46     auto name = custom_func_name_list[i];
47     custom_kernel_registration_info info;
48     info.eval_function = custom_func_ptr_list[i];
49     NNPR_ENSURE_STATUS(nnfw_register_custom_op_info(session, name, &info));
50   }
51 }
52
53 uint64_t NowMicros()
54 {
55   auto time_point = std::chrono::high_resolution_clock::now();
56   auto since_epoch = time_point.time_since_epoch();
57   // default precision of high resolution clock is 10e-9 (nanoseconds)
58   return std::chrono::duration_cast<std::chrono::microseconds>(since_epoch).count();
59 }
60
61 uint64_t num_elems(const nnfw_tensorinfo *ti)
62 {
63   uint64_t n = 1;
64   for (uint32_t i = 0; i < ti->rank; ++i)
65   {
66     assert(ti->dims[i] >= 0);
67     n *= ti->dims[i];
68   }
69   return n;
70 };
71
72 // TODO replace with data import
73 // Valid only for model FillFrom.tflite
74 // FillFrom(idx=3, val=1.1)
75 static const float in_data[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
76 static const float ref_data[10] = {1, 2, 3, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1};
77
78 std::vector<float> genData(uint64_t size)
79 {
80   assert(size == sizeof(in_data) / sizeof(in_data[0]));
81   std::cout << "Warning: runner uses hardcoded data form in_data" << std::endl;
82   std::vector<float> vec(size);
83   for (uint64_t i = 0; i < size; i++)
84     vec[i] = in_data[i];
85   return vec;
86 }
87
88 template <typename InIter1, typename InIter2>
89 static auto findMaxDifference(InIter1 first1, InIter1 last1, InIter2 first2)
90     -> decltype(*first1 - *first2)
91 {
92   auto max_difference = std::abs(*first1 - *first2);
93   for (; first1 != last1; ++first1, ++first2)
94   {
95     auto diff = std::abs(*first1 - *first2);
96     if (diff > max_difference)
97     {
98       max_difference = diff;
99     }
100   }
101   return max_difference;
102 }
103
104 std::string dirFilename(const std::string &str)
105 {
106   std::size_t found = str.find_last_of("/\\");
107   // Finished with '/' or '\' to merge
108   return str.substr(0, found + 1);
109 }
110
111 int main(const int argc, char **argv)
112 {
113   std::string dir = dirFilename(argv[0]);
114   std::string model_path = dir + "nnpkgs/FillFrom";
115
116   if (argc == 1)
117   {
118     std::cout << "[WARNING] Use default package path\n";
119   }
120   else if (argc == 2)
121   {
122     model_path = argv[1];
123   }
124   else
125   {
126     std::cerr << "[ERROR] Invalid argument\n";
127     return 1;
128   }
129   nnfw_session *session = nullptr;
130   NNPR_ENSURE_STATUS(nnfw_create_session(&session));
131
132   register_custom_operations(session);
133
134   NNPR_ENSURE_STATUS(nnfw_load_model_from_file(session, model_path.c_str()));
135
136   uint32_t num_inputs;
137   NNPR_ENSURE_STATUS(nnfw_input_size(session, &num_inputs));
138
139   // verify input and output
140
141   if (num_inputs == 0)
142   {
143     std::cerr << "[ ERROR ] "
144               << "No inputs in model => execution is not possible" << std::endl;
145     exit(1);
146   }
147
148   auto verifyInputTypes = [session]() {
149     uint32_t sz;
150     NNPR_ENSURE_STATUS(nnfw_input_size(session, &sz));
151     for (uint32_t i = 0; i < sz; ++i)
152     {
153       nnfw_tensorinfo ti;
154       NNPR_ENSURE_STATUS(nnfw_input_tensorinfo(session, i, &ti));
155       if (ti.dtype != NNFW_TYPE_TENSOR_FLOAT32)
156       {
157         std::cerr << "Only float 32bit is supported." << std::endl;
158         exit(-1);
159       }
160     }
161   };
162
163   auto verifyOutputTypes = [session]() {
164     uint32_t sz;
165     NNPR_ENSURE_STATUS(nnfw_output_size(session, &sz));
166
167     for (uint32_t i = 0; i < sz; ++i)
168     {
169       nnfw_tensorinfo ti;
170       NNPR_ENSURE_STATUS(nnfw_output_tensorinfo(session, i, &ti));
171       if (ti.dtype != NNFW_TYPE_TENSOR_FLOAT32)
172       {
173         std::cerr << "Only float 32bit is supported." << std::endl;
174         exit(-1);
175       }
176     }
177   };
178
179   verifyInputTypes();
180   verifyOutputTypes();
181
182   // prepare execution
183
184   uint64_t prepare_ms = NowMicros();
185   NNPR_ENSURE_STATUS(nnfw_prepare(session));
186   prepare_ms = NowMicros() - prepare_ms;
187
188   // prepare input
189   std::vector<std::vector<float>> inputs(num_inputs);
190
191   auto generateInputs = [session, num_inputs, &inputs]() {
192     // generate random data
193     const int seed = 1;
194     for (uint32_t i = 0; i < num_inputs; ++i)
195     {
196       nnfw_tensorinfo ti;
197       NNPR_ENSURE_STATUS(nnfw_input_tensorinfo(session, i, &ti));
198       auto input_num_elements = num_elems(&ti);
199       inputs[i] = genData(input_num_elements);
200       NNPR_ENSURE_STATUS(nnfw_set_input(session, i, NNFW_TYPE_TENSOR_FLOAT32, inputs[i].data(),
201                                         sizeof(float) * input_num_elements));
202       NNPR_ENSURE_STATUS(nnfw_set_input_layout(session, i, NNFW_LAYOUT_CHANNELS_LAST));
203     }
204   };
205
206   generateInputs();
207
208   // prepare output
209   uint32_t num_outputs = 0;
210   NNPR_ENSURE_STATUS(nnfw_output_size(session, &num_outputs));
211   std::vector<std::vector<float>> outputs(num_outputs);
212
213   for (uint32_t i = 0; i < num_outputs; i++)
214   {
215     nnfw_tensorinfo ti;
216     NNPR_ENSURE_STATUS(nnfw_output_tensorinfo(session, i, &ti));
217     auto output_num_elements = num_elems(&ti);
218     outputs[i].resize(output_num_elements);
219     NNPR_ENSURE_STATUS(nnfw_set_output(session, i, NNFW_TYPE_TENSOR_FLOAT32, outputs[i].data(),
220                                        sizeof(float) * output_num_elements));
221     NNPR_ENSURE_STATUS(nnfw_set_output_layout(session, i, NNFW_LAYOUT_CHANNELS_LAST));
222   }
223
224   uint64_t run_ms = NowMicros();
225   NNPR_ENSURE_STATUS(nnfw_run(session));
226   run_ms = NowMicros() - run_ms;
227
228   const float tolerance = 0.01f;
229   auto max_difference =
230       findMaxDifference(outputs[0].begin(), outputs[0].end(), std::begin(ref_data));
231
232   int exit_code = 0;
233   if (max_difference > tolerance)
234   {
235     std::cout << "Max difference is more than tolerance" << std::endl;
236     std::cout << "Max difference is " << max_difference << std::endl;
237     exit_code = 1;
238   }
239
240   std::cout << "nnfw_prepare takes " << prepare_ms / 1e3 << " sec" << std::endl;
241   std::cout << "nnfw_run     takes " << run_ms / 1e3 << " sec" << std::endl;
242
243   NNPR_ENSURE_STATUS(nnfw_close_session(session));
244
245   return exit_code;
246 }