1 /*
2 * Copyright (c) 2023-2024 Tomeu Vizoso <[email protected]>
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include <dlfcn.h>
7 #include <stdio.h>
8 #include <vector>
9 #include <gtest/gtest.h>
10 #include <xtensor/xrandom.hpp>
11
12 #include "util/macros.h"
13
14 #include "tensorflow/lite/c/c_api.h"
15 #include "tensorflow/lite/c/common.h"
16
17 #include <fcntl.h>
18 #include "test_executor.h"
19 #include "tflite-schema-v2.15.0_generated.h"
20
21 static float
randf(float min,float max)22 randf(float min, float max)
23 {
24 return ((max - min) * ((float)rand() / (float)RAND_MAX)) + min;
25 }
26
27 static void
read_model(const char * file_name,tflite::ModelT & model)28 read_model(const char *file_name, tflite::ModelT &model)
29 {
30 std::ostringstream file_path;
31 assert(getenv("TEFLON_TEST_DATA"));
32 file_path << getenv("TEFLON_TEST_DATA") << "/" << file_name;
33
34 FILE *f = fopen(file_path.str().c_str(), "rb");
35 assert(f);
36 fseek(f, 0, SEEK_END);
37 long fsize = ftell(f);
38 fseek(f, 0, SEEK_SET);
39 void *buf = malloc(fsize);
40 fread(buf, fsize, 1, f);
41 fclose(f);
42
43 tflite::GetModel(buf)->UnPackTo(&model);
44 }
45
46 static void
patch_conv2d(unsigned operation_index,tflite::ModelT * model,int input_size,int weight_size,int input_channels,int output_channels,int stride,bool padding_same,bool is_signed,bool depthwise)47 patch_conv2d(unsigned operation_index,
48 tflite::ModelT *model,
49 int input_size,
50 int weight_size,
51 int input_channels,
52 int output_channels,
53 int stride,
54 bool padding_same,
55 bool is_signed,
56 bool depthwise)
57 {
58 unsigned output_size = 0;
59 unsigned input_index;
60 unsigned weights_index;
61 unsigned bias_index;
62 unsigned output_index;
63 unsigned weights_buffer_index;
64 unsigned bias_buffer_index;
65
66 auto subgraph = model->subgraphs[0];
67
68 /* Operation */
69 if (depthwise) {
70 auto value = new tflite::DepthwiseConv2DOptionsT();
71 value->depth_multiplier = 1;
72 value->padding = padding_same ? tflite::Padding_SAME : tflite::Padding_VALID;
73 value->stride_w = stride;
74 value->stride_h = stride;
75 value->dilation_w_factor = 1;
76 value->dilation_h_factor = 1;
77 subgraph->operators[operation_index]->builtin_options.value = value;
78 subgraph->operators[operation_index]->builtin_options.type = tflite::BuiltinOptions_DepthwiseConv2DOptions;
79
80 model->operator_codes[0]->deprecated_builtin_code = 4;
81 model->operator_codes[0]->builtin_code = tflite::BuiltinOperator_DEPTHWISE_CONV_2D;
82 } else {
83 auto value = new tflite::Conv2DOptionsT();
84 value->padding = padding_same ? tflite::Padding_SAME : tflite::Padding_VALID;
85 value->stride_w = stride;
86 value->stride_h = stride;
87 subgraph->operators[operation_index]->builtin_options.value = value;
88 }
89
90 input_index = subgraph->operators[operation_index]->inputs.data()[0];
91 weights_index = subgraph->operators[operation_index]->inputs.data()[1];
92 bias_index = subgraph->operators[operation_index]->inputs.data()[2];
93 output_index = subgraph->operators[operation_index]->outputs.data()[0];
94
95 /* Input */
96 auto input_tensor = subgraph->tensors[input_index];
97 input_tensor->shape.data()[0] = 1;
98 input_tensor->shape.data()[1] = input_size;
99 input_tensor->shape.data()[2] = input_size;
100 input_tensor->shape.data()[3] = input_channels;
101 input_tensor->type = is_signed ? tflite::TensorType_INT8 : tflite::TensorType_UINT8;
102
103 /* Bias */
104 auto bias_tensor = subgraph->tensors[bias_index];
105 bias_buffer_index = bias_tensor->buffer;
106 bias_tensor->shape.data()[0] = output_channels;
107
108 auto bias_data = &model->buffers[bias_buffer_index]->data;
109 xt::xarray<int32_t> bias_array = xt::random::randint<int32_t>({output_channels}, -20000, 20000);
110 bias_data->resize(bias_array.size() * sizeof(int32_t));
111 memcpy(bias_data->data(), bias_array.data(), bias_array.size() * sizeof(int32_t));
112
113 /* Weight */
114 auto weight_tensor = subgraph->tensors[weights_index];
115 weights_buffer_index = weight_tensor->buffer;
116 if (depthwise) {
117 weight_tensor->shape.data()[0] = 1;
118 weight_tensor->shape.data()[1] = weight_size;
119 weight_tensor->shape.data()[2] = weight_size;
120 weight_tensor->shape.data()[3] = output_channels;
121 } else {
122 weight_tensor->shape.data()[0] = output_channels;
123 weight_tensor->shape.data()[1] = weight_size;
124 weight_tensor->shape.data()[2] = weight_size;
125 weight_tensor->shape.data()[3] = input_channels;
126 }
127 weight_tensor->type = is_signed ? tflite::TensorType_INT8 : tflite::TensorType_UINT8;
128
129 auto weights_data = &model->buffers[weights_buffer_index]->data;
130 std::vector<int> weight_shape;
131 if (depthwise)
132 weight_shape = {1, weight_size, weight_size, output_channels};
133 else
134 weight_shape = {output_channels, weight_size, weight_size, input_channels};
135
136 xt::xarray<uint8_t> weights_array = xt::random::randint<uint8_t>(weight_shape, 0, 255);
137 weights_data->resize(weights_array.size());
138 memcpy(weights_data->data(), weights_array.data(), weights_array.size());
139
140 /* Output */
141 if (padding_same)
142 output_size = (input_size + stride - 1) / stride;
143 else
144 output_size = (input_size + stride - weight_size) / stride;
145
146 auto output_tensor = subgraph->tensors[output_index];
147 output_tensor->shape.data()[0] = 1;
148 output_tensor->shape.data()[1] = output_size;
149 output_tensor->shape.data()[2] = output_size;
150 output_tensor->shape.data()[3] = output_channels;
151 output_tensor->type = is_signed ? tflite::TensorType_INT8 : tflite::TensorType_UINT8;
152 }
153
154 std::vector<uint8_t>
conv2d_generate_model(int input_size,int weight_size,int input_channels,int output_channels,int stride,bool padding_same,bool is_signed,bool depthwise)155 conv2d_generate_model(int input_size,
156 int weight_size,
157 int input_channels,
158 int output_channels,
159 int stride,
160 bool padding_same,
161 bool is_signed,
162 bool depthwise)
163 {
164 tflite::ModelT model;
165 read_model("conv2d.tflite", model);
166
167 patch_conv2d(0, &model, input_size, weight_size, input_channels, output_channels, stride, padding_same, is_signed, depthwise);
168
169 flatbuffers::FlatBufferBuilder builder;
170 builder.Finish(tflite::Model::Pack(builder, &model), "TFL3");
171
172 return {builder.GetBufferPointer(), builder.GetBufferPointer() + builder.GetSize()};
173 }
174
175 static void
patch_quant_for_add(tflite::ModelT * model)176 patch_quant_for_add(tflite::ModelT *model)
177 {
178 auto subgraph = model->subgraphs[0];
179 auto add_op = subgraph->operators[2];
180
181 auto input_index = add_op->inputs.data()[0];
182 auto input_tensor = subgraph->tensors[input_index];
183 input_tensor->quantization->scale[0] = randf(0.0078125, 0.4386410117149353);
184 input_tensor->quantization->zero_point[0] = rand() % 255;
185
186 input_index = add_op->inputs.data()[1];
187 input_tensor = subgraph->tensors[input_index];
188 input_tensor->quantization->scale[0] = randf(0.0078125, 0.4386410117149353);
189 input_tensor->quantization->zero_point[0] = rand() % 255;
190 }
191
192 std::vector<uint8_t>
add_generate_model(int input_size,int weight_size,int input_channels,int output_channels,int stride,bool padding_same,bool is_signed,bool depthwise)193 add_generate_model(int input_size,
194 int weight_size,
195 int input_channels,
196 int output_channels,
197 int stride,
198 bool padding_same,
199 bool is_signed,
200 bool depthwise)
201 {
202 tflite::ModelT model;
203 read_model("add.tflite", model);
204
205 patch_conv2d(0, &model, input_size, weight_size, input_channels, output_channels, stride, padding_same, is_signed, depthwise);
206 patch_conv2d(1, &model, input_size, weight_size, input_channels, output_channels, stride, padding_same, is_signed, depthwise);
207 patch_quant_for_add(&model);
208
209 /* Output */
210 auto subgraph = model.subgraphs[0];
211 unsigned input_index = subgraph->operators[2]->inputs.data()[0];
212 unsigned output_index = subgraph->operators[2]->outputs.data()[0];
213
214 auto input_tensor = subgraph->tensors[input_index];
215 auto output_tensor = subgraph->tensors[output_index];
216 output_tensor->shape.data()[0] = input_tensor->shape.data()[0];
217 output_tensor->shape.data()[1] = input_tensor->shape.data()[1];
218 output_tensor->shape.data()[2] = input_tensor->shape.data()[2];
219 output_tensor->shape.data()[3] = input_tensor->shape.data()[3];
220 output_tensor->type = is_signed ? tflite::TensorType_INT8 : tflite::TensorType_UINT8;
221
222 flatbuffers::FlatBufferBuilder builder;
223 builder.Finish(tflite::Model::Pack(builder, &model), "TFL3");
224
225 return {builder.GetBufferPointer(), builder.GetBufferPointer() + builder.GetSize()};
226 }
227
228 static void
tflite_error_cb(void * user_data,const char * format,va_list args)229 tflite_error_cb(void *user_data, const char *format, va_list args)
230 {
231 vfprintf(stderr, format, args);
232 }
233
234 TfLiteDelegate *(*tflite_plugin_create_delegate)(char **options_keys,
235 char **options_values,
236 size_t num_options,
237 void (*report_error)(const char *));
238
239 void (*tflite_plugin_destroy_delegate)(TfLiteDelegate *delegate);
240
241 static void
load_delegate()242 load_delegate()
243 {
244 const char *delegate_path = getenv("TEFLON_TEST_DELEGATE");
245 assert(delegate_path);
246
247 void *delegate_lib = dlopen(delegate_path, RTLD_LAZY | RTLD_LOCAL);
248 assert(delegate_lib);
249
250 tflite_plugin_create_delegate = reinterpret_cast<TfLiteDelegate *(*)(char **options_keys,
251 char **options_values,
252 size_t num_options,
253 void (*report_error)(const char *))>(
254 dlsym(delegate_lib, "tflite_plugin_create_delegate"));
255 assert(tflite_plugin_create_delegate);
256
257 tflite_plugin_destroy_delegate = reinterpret_cast<void (*)(TfLiteDelegate *delegate)>(
258 dlsym(delegate_lib, "tflite_plugin_destroy_delegate"));
259 assert(tflite_plugin_destroy_delegate);
260 }
261
262 std::vector<std::vector<uint8_t>>
run_model(TfLiteModel * model,enum executor executor,std::vector<std::vector<uint8_t>> & input)263 run_model(TfLiteModel *model, enum executor executor, std::vector<std::vector<uint8_t>> &input)
264 {
265 TfLiteDelegate *delegate = NULL;
266 TfLiteInterpreterOptions *options = TfLiteInterpreterOptionsCreate();
267 bool generate_random_input = input.empty();
268 std::vector<std::vector<uint8_t>> output;
269
270 if (executor == EXECUTOR_NPU) {
271 load_delegate();
272 delegate = tflite_plugin_create_delegate(NULL, NULL, 0, NULL);
273 TfLiteInterpreterOptionsAddDelegate(options, delegate);
274 }
275
276 TfLiteInterpreterOptionsSetErrorReporter(options, tflite_error_cb, NULL);
277
278 TfLiteInterpreter *interpreter = TfLiteInterpreterCreate(model, options);
279 assert(interpreter);
280
281 TfLiteInterpreterAllocateTensors(interpreter);
282
283 unsigned input_tensors = TfLiteInterpreterGetInputTensorCount(interpreter);
284 for (unsigned i = 0; i < input_tensors; i++) {
285 TfLiteTensor *input_tensor = TfLiteInterpreterGetInputTensor(interpreter, i);
286
287 if (generate_random_input) {
288 int shape[4] = {input_tensor->dims->data[0],
289 input_tensor->dims->data[1],
290 input_tensor->dims->data[2],
291 input_tensor->dims->data[3]};
292 xt::xarray<uint8_t> a = xt::random::randint<uint8_t>(shape, 0, 255);
293 input.push_back({a.begin(), a.end()});
294 }
295
296 TfLiteTensorCopyFromBuffer(input_tensor, input[i].data(), input_tensor->bytes);
297 }
298
299 EXPECT_EQ(TfLiteInterpreterInvoke(interpreter), kTfLiteOk);
300
301 unsigned output_tensors = TfLiteInterpreterGetOutputTensorCount(interpreter);
302 for (unsigned i = 0; i < output_tensors; i++) {
303 const TfLiteTensor *output_tensor = TfLiteInterpreterGetOutputTensor(interpreter, i);
304
305 std::vector<uint8_t> out;
306 out.resize(output_tensor->bytes);
307 EXPECT_EQ(TfLiteTensorCopyToBuffer(output_tensor, out.data(), output_tensor->bytes), kTfLiteOk);
308
309 output.push_back(out);
310 }
311
312 TfLiteInterpreterDelete(interpreter);
313 if (executor == EXECUTOR_NPU)
314 tflite_plugin_destroy_delegate(delegate);
315 TfLiteInterpreterOptionsDelete(options);
316
317 return output;
318 }
319