xref: /aosp_15_r20/external/tensorflow/tensorflow/lite/tools/optimize/modify_model_interface_test.cc (revision b6fb3261f9314811a0f4371741dbb8839866f948)
1 /* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 #include "tensorflow/lite/tools/optimize/modify_model_interface.h"
16 
17 #include <memory>
18 #include <utility>
19 
20 #include <gmock/gmock.h>
21 #include <gtest/gtest.h>
22 #include "absl/memory/memory.h"
23 #include "tensorflow/lite/model.h"
24 #include "tensorflow/lite/schema/schema_generated.h"
25 #include "tensorflow/lite/schema/schema_utils.h"
26 
27 namespace tflite {
28 namespace optimize {
29 namespace {
30 
31 // Create a model with 1 quant, 1 FC, 1 dequant
CreateQuantizedModelSingleInputOutput(const TensorType & quantization_type)32 std::unique_ptr<ModelT> CreateQuantizedModelSingleInputOutput(
33     const TensorType& quantization_type) {
34   auto model = std::make_unique<ModelT>();
35   auto subgraph = std::make_unique<tflite::SubGraphT>();
36   auto buffer = std::make_unique<tflite::BufferT>();
37   auto quant_op_code = std::make_unique<OperatorCodeT>();
38   auto quant_op = std::make_unique<OperatorT>();
39   auto fc_op_code = std::make_unique<OperatorCodeT>();
40   auto fc_op = std::make_unique<OperatorT>();
41   auto dequant_op_code = std::make_unique<OperatorCodeT>();
42   auto dequant_op = std::make_unique<OperatorT>();
43 
44   model->subgraphs.push_back(std::move(subgraph));
45 
46   // Op code
47   quant_op_code->builtin_code = BuiltinOperator_QUANTIZE;
48   quant_op_code->deprecated_builtin_code =
49       static_cast<int8_t>(BuiltinOperator_QUANTIZE);
50   quant_op_code->version = 2;
51 
52   fc_op_code->builtin_code = BuiltinOperator_FULLY_CONNECTED;
53   fc_op_code->deprecated_builtin_code =
54       static_cast<int8_t>(BuiltinOperator_FULLY_CONNECTED);
55   fc_op_code->version = 2;
56 
57   dequant_op_code->builtin_code = BuiltinOperator_DEQUANTIZE;
58   dequant_op_code->deprecated_builtin_code =
59       static_cast<int8_t>(BuiltinOperator_DEQUANTIZE);
60   dequant_op_code->version = 2;
61 
62   // Op.
63   quant_op->opcode_index = 0;
64   quant_op->inputs = {0};
65   quant_op->outputs = {1};
66 
67   fc_op->opcode_index = 1;
68   fc_op->inputs = {1};
69   fc_op->outputs = {2};
70 
71   dequant_op->opcode_index = 2;
72   dequant_op->inputs = {2};
73   dequant_op->outputs = {3};
74 
75   model->subgraphs[0]->operators.push_back(std::move(quant_op));
76   model->subgraphs[0]->operators.push_back(std::move(fc_op));
77   model->subgraphs[0]->operators.push_back(std::move(dequant_op));
78 
79   model->operator_codes.push_back(std::move(quant_op_code));
80   model->operator_codes.push_back(std::move(fc_op_code));
81   model->operator_codes.push_back(std::move(dequant_op_code));
82 
83   // Model input/output.
84   model->subgraphs[0]->inputs = {0};
85   model->subgraphs[0]->outputs = {3};
86 
87   // Tensors
88   auto tensor_0 = std::make_unique<TensorT>();
89   tensor_0->name = "tensor_0";
90   tensor_0->shape = {};
91   tensor_0->type = TensorType_FLOAT32;
92 
93   auto tensor_1 = std::make_unique<TensorT>();
94   tensor_1->quantization = std::make_unique<QuantizationParametersT>();
95   tensor_1->quantization->scale.push_back(0.35);
96   tensor_1->quantization->zero_point.push_back(28);
97   tensor_1->name = "tensor_1";
98   tensor_1->shape = {};
99   tensor_1->type = quantization_type;
100 
101   auto tensor_2 = std::make_unique<TensorT>();
102   tensor_2->quantization = std::make_unique<QuantizationParametersT>();
103   tensor_2->quantization->scale.push_back(0.12);
104   tensor_2->quantization->zero_point.push_back(50);
105   tensor_2->name = "tensor_2";
106   tensor_2->shape = {};
107   tensor_2->type = quantization_type;
108 
109   auto tensor_3 = std::make_unique<TensorT>();
110   tensor_3->name = "tensor_3";
111   tensor_3->shape = {};
112   tensor_3->type = TensorType_FLOAT32;
113 
114   model->subgraphs[0]->tensors.push_back(std::move(tensor_0));
115   model->subgraphs[0]->tensors.push_back(std::move(tensor_1));
116   model->subgraphs[0]->tensors.push_back(std::move(tensor_2));
117   model->subgraphs[0]->tensors.push_back(std::move(tensor_3));
118 
119   // Buffer
120   model->buffers.push_back(std::move(buffer));
121 
122   return model;
123 }
124 
125 // Create a model with 2 quant, 1 FC, 2 dequant
126 // The model mimics the behavior of the quantize_model.cc.
CreateQuantizedModelMultipleInputOutput(const TensorType & quantization_type)127 std::unique_ptr<ModelT> CreateQuantizedModelMultipleInputOutput(
128     const TensorType& quantization_type) {
129   auto model = std::make_unique<ModelT>();
130   auto subgraph = std::make_unique<tflite::SubGraphT>();
131   auto buffer = std::make_unique<tflite::BufferT>();
132   auto quant_op_code = std::make_unique<OperatorCodeT>();
133   auto quant_op_1 = std::make_unique<OperatorT>();
134   auto quant_op_2 = std::make_unique<OperatorT>();
135   auto fc_op_code = std::make_unique<OperatorCodeT>();
136   auto fc_op = std::make_unique<OperatorT>();
137   auto dequant_op_code = std::make_unique<OperatorCodeT>();
138   auto dequant_op_1 = std::make_unique<OperatorT>();
139   auto dequant_op_2 = std::make_unique<OperatorT>();
140 
141   model->subgraphs.push_back(std::move(subgraph));
142 
143   // Op code
144   quant_op_code->builtin_code = BuiltinOperator_QUANTIZE;
145   quant_op_code->deprecated_builtin_code =
146       static_cast<int8_t>(BuiltinOperator_QUANTIZE);
147   quant_op_code->version = 2;
148 
149   fc_op_code->builtin_code = BuiltinOperator_FULLY_CONNECTED;
150   fc_op_code->deprecated_builtin_code =
151       static_cast<int8_t>(BuiltinOperator_FULLY_CONNECTED);
152   fc_op_code->version = 2;
153 
154   dequant_op_code->builtin_code = BuiltinOperator_DEQUANTIZE;
155   dequant_op_code->deprecated_builtin_code =
156       static_cast<int8_t>(BuiltinOperator_DEQUANTIZE);
157   dequant_op_code->version = 2;
158 
159   // Op.
160   quant_op_1->opcode_index = 0;
161   quant_op_1->inputs = {0};
162   quant_op_1->outputs = {2};
163   quant_op_2->opcode_index = 0;
164   quant_op_2->inputs = {1};
165   quant_op_2->outputs = {3};
166 
167   fc_op->opcode_index = 1;
168   fc_op->inputs = {2, 3};
169   fc_op->outputs = {4, 5};
170 
171   dequant_op_1->opcode_index = 2;
172   dequant_op_1->inputs = {4};
173   dequant_op_1->outputs = {6};
174   dequant_op_2->opcode_index = 2;
175   dequant_op_2->inputs = {5};
176   dequant_op_2->outputs = {7};
177 
178   model->subgraphs[0]->operators.push_back(std::move(quant_op_1));
179   model->subgraphs[0]->operators.push_back(std::move(quant_op_2));
180   model->subgraphs[0]->operators.push_back(std::move(fc_op));
181   model->subgraphs[0]->operators.push_back(std::move(dequant_op_1));
182   model->subgraphs[0]->operators.push_back(std::move(dequant_op_2));
183 
184   model->operator_codes.push_back(std::move(quant_op_code));
185   model->operator_codes.push_back(std::move(fc_op_code));
186   model->operator_codes.push_back(std::move(dequant_op_code));
187 
188   // Model input/output.
189   model->subgraphs[0]->inputs = {0, 1};
190   model->subgraphs[0]->outputs = {6, 7};
191 
192   // Tensors
193   auto tensor_0 = std::make_unique<TensorT>();
194   tensor_0->name = "tensor_0";
195   tensor_0->shape = {};
196   tensor_0->type = TensorType_FLOAT32;
197 
198   auto tensor_1 = std::make_unique<TensorT>();
199   tensor_1->name = "tensor_1";
200   tensor_1->shape = {};
201   tensor_1->type = TensorType_FLOAT32;
202 
203   auto tensor_2 = std::make_unique<TensorT>();
204   tensor_2->quantization = std::make_unique<QuantizationParametersT>();
205   tensor_2->quantization->scale.push_back(0.35);
206   tensor_2->quantization->zero_point.push_back(28);
207   tensor_2->name = "tensor_2";
208   tensor_2->shape = {};
209   tensor_2->type = quantization_type;
210 
211   auto tensor_3 = std::make_unique<TensorT>();
212   tensor_3->quantization = std::make_unique<QuantizationParametersT>();
213   tensor_3->quantization->scale.push_back(0.12);
214   tensor_3->quantization->zero_point.push_back(50);
215   tensor_3->name = "tensor_3";
216   tensor_3->shape = {};
217   tensor_3->type = quantization_type;
218 
219   auto tensor_4 = std::make_unique<TensorT>();
220   tensor_4->quantization = std::make_unique<QuantizationParametersT>();
221   tensor_4->quantization->scale.push_back(0.45);
222   tensor_4->quantization->zero_point.push_back(28);
223   tensor_4->name = "tensor_4";
224   tensor_4->shape = {};
225   tensor_4->type = quantization_type;
226 
227   auto tensor_5 = std::make_unique<TensorT>();
228   tensor_5->quantization = std::make_unique<QuantizationParametersT>();
229   tensor_5->quantization->scale.push_back(0.22);
230   tensor_5->quantization->zero_point.push_back(50);
231   tensor_5->name = "tensor_5";
232   tensor_5->shape = {};
233   tensor_5->type = quantization_type;
234 
235   auto tensor_6 = std::make_unique<TensorT>();
236   tensor_6->name = "tensor_6";
237   tensor_6->shape = {};
238   tensor_6->type = TensorType_FLOAT32;
239 
240   auto tensor_7 = std::make_unique<TensorT>();
241   tensor_7->name = "tensor_7";
242   tensor_7->shape = {};
243   tensor_7->type = TensorType_FLOAT32;
244 
245   model->subgraphs[0]->tensors.push_back(std::move(tensor_0));
246   model->subgraphs[0]->tensors.push_back(std::move(tensor_1));
247   model->subgraphs[0]->tensors.push_back(std::move(tensor_2));
248   model->subgraphs[0]->tensors.push_back(std::move(tensor_3));
249   model->subgraphs[0]->tensors.push_back(std::move(tensor_4));
250   model->subgraphs[0]->tensors.push_back(std::move(tensor_5));
251   model->subgraphs[0]->tensors.push_back(std::move(tensor_6));
252   model->subgraphs[0]->tensors.push_back(std::move(tensor_7));
253 
254   // Buffer
255   model->buffers.push_back(std::move(buffer));
256 
257   return model;
258 }
259 
260 // Create a model with 1 FC.
CreateFloatModel()261 std::unique_ptr<ModelT> CreateFloatModel() {
262   auto model = std::make_unique<ModelT>();
263   auto subgraph = std::make_unique<tflite::SubGraphT>();
264   auto buffer = std::make_unique<tflite::BufferT>();
265   auto fc_op_code = std::make_unique<OperatorCodeT>();
266   auto fc_op = std::make_unique<OperatorT>();
267 
268   model->subgraphs.push_back(std::move(subgraph));
269 
270   // Op code
271   fc_op_code->builtin_code = BuiltinOperator_FULLY_CONNECTED;
272   fc_op_code->deprecated_builtin_code =
273       static_cast<int8_t>(BuiltinOperator_FULLY_CONNECTED);
274   fc_op_code->version = 2;
275 
276   // Op.
277   fc_op->opcode_index = 0;
278   fc_op->inputs = {0};
279   fc_op->outputs = {1};
280 
281   model->subgraphs[0]->operators.push_back(std::move(fc_op));
282   model->operator_codes.push_back(std::move(fc_op_code));
283 
284   // Model input/output.
285   model->subgraphs[0]->inputs = {0};
286   model->subgraphs[0]->outputs = {1};
287 
288   // Tensors
289   auto tensor_0 = std::make_unique<TensorT>();
290   tensor_0->name = "tensor_0";
291   tensor_0->shape = {};
292   tensor_0->type = TensorType_FLOAT32;
293 
294   auto tensor_1 = std::make_unique<TensorT>();
295   tensor_1->name = "tensor_1";
296   tensor_1->shape = {};
297   tensor_1->type = TensorType_FLOAT32;
298 
299   model->subgraphs[0]->tensors.push_back(std::move(tensor_0));
300   model->subgraphs[0]->tensors.push_back(std::move(tensor_1));
301 
302   // Buffer
303   model->buffers.push_back(std::move(buffer));
304 
305   return model;
306 }
307 
308 struct ModelInterface : ::testing::TestWithParam<tflite::TensorType> {};
309 
TEST_P(ModelInterface,SingleInputOutput)310 TEST_P(ModelInterface, SingleInputOutput) {
311   TensorType quantization_type = GetParam();
312 
313   auto model = CreateQuantizedModelSingleInputOutput(quantization_type);
314 
315   // Change model type.
316   flatbuffers::FlatBufferBuilder builder;
317   EXPECT_EQ(ModifyModelInterface(&builder, model.get(), quantization_type,
318                                  quantization_type),
319             kTfLiteOk);
320 
321   // Verify results.
322   EXPECT_EQ(model->subgraphs.size(), 1);
323   EXPECT_EQ(model->subgraphs[0]->tensors.size(), 3);
324   EXPECT_EQ(model->subgraphs[0]->inputs.size(), 1);
325   EXPECT_EQ(model->subgraphs[0]->inputs[0], 1);
326   EXPECT_EQ(model->subgraphs[0]->outputs.size(), 1);
327   EXPECT_EQ(model->subgraphs[0]->outputs[0], 2);
328   EXPECT_EQ(model->operator_codes.size(), 3);
329   EXPECT_EQ(model->subgraphs[0]->operators.size(), 1);
330   EXPECT_EQ(model->subgraphs[0]->operators[0]->opcode_index, 1);
331 
332   auto fc_op = model->subgraphs[0]->operators[0].get();
333 
334   auto input = model->subgraphs[0]->tensors[fc_op->inputs[0]].get();
335   EXPECT_EQ(input->name, "tensor_1");
336   EXPECT_EQ(input->type, quantization_type);
337   EXPECT_FLOAT_EQ(input->quantization->scale[0], 0.35);
338   EXPECT_EQ(input->quantization->zero_point[0], 28);
339 
340   auto output = model->subgraphs[0]->tensors[fc_op->outputs[0]].get();
341   EXPECT_EQ(output->name, "tensor_2");
342   EXPECT_EQ(output->type, quantization_type);
343   EXPECT_FLOAT_EQ(output->quantization->scale[0], 0.12);
344   EXPECT_EQ(output->quantization->zero_point[0], 50);
345 }
346 
TEST_P(ModelInterface,MutipleInputOutput)347 TEST_P(ModelInterface, MutipleInputOutput) {
348   TensorType quantization_type = GetParam();
349 
350   auto model = CreateQuantizedModelMultipleInputOutput(quantization_type);
351 
352   // Change model type.
353   flatbuffers::FlatBufferBuilder builder;
354   EXPECT_EQ(ModifyModelInterface(&builder, model.get(), quantization_type,
355                                  quantization_type),
356             kTfLiteOk);
357 
358   // Verify results.
359   EXPECT_EQ(model->subgraphs.size(), 1);
360   EXPECT_EQ(model->subgraphs[0]->tensors.size(), 6);
361   EXPECT_EQ(model->subgraphs[0]->inputs.size(), 2);
362   EXPECT_EQ(model->subgraphs[0]->inputs[0], 2);
363   EXPECT_EQ(model->subgraphs[0]->inputs[1], 3);
364   EXPECT_EQ(model->subgraphs[0]->outputs.size(), 2);
365   EXPECT_EQ(model->subgraphs[0]->outputs[0], 4);
366   EXPECT_EQ(model->subgraphs[0]->outputs[1], 5);
367   EXPECT_EQ(model->operator_codes.size(), 3);
368   EXPECT_EQ(model->subgraphs[0]->operators.size(), 1);
369   EXPECT_EQ(model->subgraphs[0]->operators[0]->opcode_index, 1);
370 
371   auto fc_op = model->subgraphs[0]->operators[0].get();
372 
373   auto input_1 = model->subgraphs[0]->tensors[fc_op->inputs[0]].get();
374   EXPECT_EQ(input_1->name, "tensor_2");
375   EXPECT_EQ(input_1->type, quantization_type);
376   EXPECT_FLOAT_EQ(input_1->quantization->scale[0], 0.35);
377   EXPECT_EQ(input_1->quantization->zero_point[0], 28);
378 
379   auto input_2 = model->subgraphs[0]->tensors[fc_op->inputs[1]].get();
380   EXPECT_EQ(input_2->name, "tensor_3");
381   EXPECT_EQ(input_2->type, quantization_type);
382   EXPECT_FLOAT_EQ(input_2->quantization->scale[0], 0.12);
383   EXPECT_EQ(input_2->quantization->zero_point[0], 50);
384 
385   auto output_1 = model->subgraphs[0]->tensors[fc_op->outputs[0]].get();
386   EXPECT_EQ(output_1->name, "tensor_4");
387   EXPECT_EQ(output_1->type, quantization_type);
388   EXPECT_FLOAT_EQ(output_1->quantization->scale[0], 0.45);
389   EXPECT_EQ(output_1->quantization->zero_point[0], 28);
390 
391   auto output_2 = model->subgraphs[0]->tensors[fc_op->outputs[1]].get();
392   EXPECT_EQ(output_2->name, "tensor_5");
393   EXPECT_EQ(output_2->type, quantization_type);
394   EXPECT_FLOAT_EQ(output_2->quantization->scale[0], 0.22);
395   EXPECT_EQ(output_2->quantization->zero_point[0], 50);
396 }
397 
398 INSTANTIATE_TEST_SUITE_P(MultipleInputOutputTests, ModelInterface,
399                          ::testing::Values(TensorType_INT8, TensorType_INT16));
400 
TEST(ModelInterface,MixedTypeSingleInputOutput)401 TEST(ModelInterface, MixedTypeSingleInputOutput) {
402   auto model = CreateQuantizedModelSingleInputOutput(TensorType_INT8);
403 
404   // Change model type.
405   flatbuffers::FlatBufferBuilder builder;
406   EXPECT_EQ(ModifyModelInterface(&builder, model.get(), TensorType_UINT8,
407                                  TensorType_INT8),
408             kTfLiteOk);
409 
410   // Verify results.
411   EXPECT_EQ(model->subgraphs.size(), 1);
412   EXPECT_EQ(model->subgraphs[0]->tensors.size(), 3);
413   EXPECT_EQ(model->subgraphs[0]->inputs.size(), 1);
414   EXPECT_EQ(model->subgraphs[0]->inputs[0], 0);
415   EXPECT_EQ(model->subgraphs[0]->outputs.size(), 1);
416   EXPECT_EQ(model->subgraphs[0]->outputs[0], 2);
417   EXPECT_EQ(model->operator_codes.size(), 3);
418   EXPECT_EQ(model->subgraphs[0]->operators.size(), 2);
419   EXPECT_EQ(model->subgraphs[0]->operators[0]->opcode_index, 0);
420   EXPECT_EQ(model->subgraphs[0]->operators[1]->opcode_index, 1);
421 
422   auto quant_op = model->subgraphs[0]->operators[0].get();
423   auto input = model->subgraphs[0]->tensors[quant_op->inputs[0]].get();
424   EXPECT_EQ(input->name, "tensor_0");
425   EXPECT_EQ(input->type, TensorType_UINT8);
426   EXPECT_FLOAT_EQ(input->quantization->scale[0], 0.35);
427   EXPECT_EQ(input->quantization->zero_point[0], 156);
428 
429   auto fc_op = model->subgraphs[0]->operators[1].get();
430   auto output = model->subgraphs[0]->tensors[fc_op->outputs[0]].get();
431   EXPECT_EQ(output->name, "tensor_2");
432   EXPECT_EQ(output->type, TensorType_INT8);
433   EXPECT_FLOAT_EQ(output->quantization->scale[0], 0.12);
434   EXPECT_EQ(output->quantization->zero_point[0], 50);
435 }
436 
TEST(ModelInterface,Uint8SingleInputOutput)437 TEST(ModelInterface, Uint8SingleInputOutput) {
438   auto model = CreateQuantizedModelSingleInputOutput(TensorType_INT8);
439 
440   // Change model type.
441   flatbuffers::FlatBufferBuilder builder;
442   EXPECT_EQ(ModifyModelInterface(&builder, model.get(), TensorType_UINT8,
443                                  TensorType_UINT8),
444             kTfLiteOk);
445 
446   // Verify results.
447   EXPECT_EQ(model->subgraphs.size(), 1);
448   EXPECT_EQ(model->subgraphs[0]->tensors.size(), 4);
449   EXPECT_EQ(model->subgraphs[0]->inputs.size(), 1);
450   EXPECT_EQ(model->subgraphs[0]->inputs[0], 0);
451   EXPECT_EQ(model->subgraphs[0]->outputs.size(), 1);
452   EXPECT_EQ(model->subgraphs[0]->outputs[0], 3);
453   EXPECT_EQ(model->operator_codes.size(), 3);
454   EXPECT_EQ(model->subgraphs[0]->operators.size(), 3);
455   EXPECT_EQ(model->subgraphs[0]->operators[0]->opcode_index, 0);
456   EXPECT_EQ(model->subgraphs[0]->operators[1]->opcode_index, 1);
457   EXPECT_EQ(model->subgraphs[0]->operators[2]->opcode_index, 0);
458 
459   auto input_quant_op = model->subgraphs[0]->operators[0].get();
460   auto input = model->subgraphs[0]->tensors[input_quant_op->inputs[0]].get();
461   EXPECT_EQ(input->name, "tensor_0");
462   EXPECT_EQ(input->type, TensorType_UINT8);
463   EXPECT_FLOAT_EQ(input->quantization->scale[0], 0.35);
464   EXPECT_EQ(input->quantization->zero_point[0], 156);
465 
466   auto output_quant_op = model->subgraphs[0]->operators[2].get();
467   auto output = model->subgraphs[0]->tensors[output_quant_op->outputs[0]].get();
468   EXPECT_EQ(output->name, "tensor_3");
469   EXPECT_EQ(output->type, TensorType_UINT8);
470   EXPECT_FLOAT_EQ(output->quantization->scale[0], 0.12);
471   EXPECT_EQ(output->quantization->zero_point[0], 178);
472 }
473 
TEST(ModelInterface,Uint8MutipleInputOutput)474 TEST(ModelInterface, Uint8MutipleInputOutput) {
475   auto model = CreateQuantizedModelMultipleInputOutput(TensorType_INT8);
476 
477   // Change model type.
478   flatbuffers::FlatBufferBuilder builder;
479   EXPECT_EQ(ModifyModelInterface(&builder, model.get(), TensorType_UINT8,
480                                  TensorType_UINT8),
481             kTfLiteOk);
482 
483   // Verify results.
484   EXPECT_EQ(model->subgraphs.size(), 1);
485   EXPECT_EQ(model->subgraphs[0]->tensors.size(), 8);
486   EXPECT_EQ(model->subgraphs[0]->inputs.size(), 2);
487   EXPECT_EQ(model->subgraphs[0]->inputs[0], 0);
488   EXPECT_EQ(model->subgraphs[0]->inputs[1], 1);
489   EXPECT_EQ(model->subgraphs[0]->outputs.size(), 2);
490   EXPECT_EQ(model->subgraphs[0]->outputs[0], 6);
491   EXPECT_EQ(model->subgraphs[0]->outputs[1], 7);
492   EXPECT_EQ(model->operator_codes.size(), 3);
493   EXPECT_EQ(model->subgraphs[0]->operators.size(), 5);
494   EXPECT_EQ(model->subgraphs[0]->operators[0]->opcode_index, 0);
495   EXPECT_EQ(model->subgraphs[0]->operators[1]->opcode_index, 0);
496   EXPECT_EQ(model->subgraphs[0]->operators[2]->opcode_index, 1);
497   EXPECT_EQ(model->subgraphs[0]->operators[3]->opcode_index, 0);
498   EXPECT_EQ(model->subgraphs[0]->operators[4]->opcode_index, 0);
499 
500   auto input_quant_1 = model->subgraphs[0]->operators[0].get();
501   auto input_1 = model->subgraphs[0]->tensors[input_quant_1->inputs[0]].get();
502   EXPECT_EQ(input_1->name, "tensor_0");
503   EXPECT_EQ(input_1->type, TensorType_UINT8);
504   EXPECT_FLOAT_EQ(input_1->quantization->scale[0], 0.35);
505   EXPECT_EQ(input_1->quantization->zero_point[0], 156);
506 
507   auto input_quant_2 = model->subgraphs[0]->operators[1].get();
508   auto input_2 = model->subgraphs[0]->tensors[input_quant_2->inputs[0]].get();
509   EXPECT_EQ(input_2->name, "tensor_1");
510   EXPECT_EQ(input_2->type, TensorType_UINT8);
511   EXPECT_FLOAT_EQ(input_2->quantization->scale[0], 0.12);
512   EXPECT_EQ(input_2->quantization->zero_point[0], 178);
513 
514   auto output_quant_1 = model->subgraphs[0]->operators[3].get();
515   auto output_1 =
516       model->subgraphs[0]->tensors[output_quant_1->outputs[0]].get();
517   EXPECT_EQ(output_1->name, "tensor_6");
518   EXPECT_EQ(output_1->type, TensorType_UINT8);
519   EXPECT_FLOAT_EQ(output_1->quantization->scale[0], 0.45);
520   EXPECT_EQ(output_1->quantization->zero_point[0], 156);
521 
522   auto output_quant_2 = model->subgraphs[0]->operators[4].get();
523   auto output_2 =
524       model->subgraphs[0]->tensors[output_quant_2->outputs[0]].get();
525   EXPECT_EQ(output_2->name, "tensor_7");
526   EXPECT_EQ(output_2->type, TensorType_UINT8);
527   EXPECT_FLOAT_EQ(output_2->quantization->scale[0], 0.22);
528   EXPECT_EQ(output_2->quantization->zero_point[0], 178);
529 }
530 
TEST(ModelInterface,Int8MutipleInputOutput)531 TEST(ModelInterface, Int8MutipleInputOutput) {
532   auto model = CreateQuantizedModelMultipleInputOutput(TensorType_INT8);
533 
534   // Change model type.
535   flatbuffers::FlatBufferBuilder builder;
536   EXPECT_EQ(ModifyModelInterface(&builder, model.get(), TensorType_INT8,
537                                  TensorType_INT8),
538             kTfLiteOk);
539 
540   // Verify results.
541   EXPECT_EQ(model->subgraphs.size(), 1);
542   EXPECT_EQ(model->subgraphs[0]->tensors.size(), 6);
543   EXPECT_EQ(model->subgraphs[0]->inputs.size(), 2);
544   EXPECT_EQ(model->subgraphs[0]->inputs[0], 2);
545   EXPECT_EQ(model->subgraphs[0]->inputs[1], 3);
546   EXPECT_EQ(model->subgraphs[0]->outputs.size(), 2);
547   EXPECT_EQ(model->subgraphs[0]->outputs[0], 4);
548   EXPECT_EQ(model->subgraphs[0]->outputs[1], 5);
549   EXPECT_EQ(model->operator_codes.size(), 3);
550   EXPECT_EQ(model->subgraphs[0]->operators.size(), 1);
551   EXPECT_EQ(model->subgraphs[0]->operators[0]->opcode_index, 1);
552 
553   auto fc_op = model->subgraphs[0]->operators[0].get();
554 
555   auto input_1 = model->subgraphs[0]->tensors[fc_op->inputs[0]].get();
556   EXPECT_EQ(input_1->name, "tensor_2");
557   EXPECT_EQ(input_1->type, TensorType_INT8);
558   EXPECT_FLOAT_EQ(input_1->quantization->scale[0], 0.35);
559   EXPECT_EQ(input_1->quantization->zero_point[0], 28);
560 
561   auto input_2 = model->subgraphs[0]->tensors[fc_op->inputs[1]].get();
562   EXPECT_EQ(input_2->name, "tensor_3");
563   EXPECT_EQ(input_2->type, TensorType_INT8);
564   EXPECT_FLOAT_EQ(input_2->quantization->scale[0], 0.12);
565   EXPECT_EQ(input_2->quantization->zero_point[0], 50);
566 
567   auto output_1 = model->subgraphs[0]->tensors[fc_op->outputs[0]].get();
568   EXPECT_EQ(output_1->name, "tensor_4");
569   EXPECT_EQ(output_1->type, TensorType_INT8);
570   EXPECT_FLOAT_EQ(output_1->quantization->scale[0], 0.45);
571   EXPECT_EQ(output_1->quantization->zero_point[0], 28);
572 
573   auto output_2 = model->subgraphs[0]->tensors[fc_op->outputs[1]].get();
574   EXPECT_EQ(output_2->name, "tensor_5");
575   EXPECT_EQ(output_2->type, TensorType_INT8);
576   EXPECT_FLOAT_EQ(output_2->quantization->scale[0], 0.22);
577   EXPECT_EQ(output_2->quantization->zero_point[0], 50);
578 }
579 
TEST(ModelInterface,Float)580 TEST(ModelInterface, Float) {
581   // Create the model.
582   std::unique_ptr<ModelT> input_model_t = CreateFloatModel();
583   flatbuffers::FlatBufferBuilder builder_temp;
584   flatbuffers::Offset<Model> output_model_location =
585       Model::Pack(builder_temp, input_model_t.get());
586   FinishModelBuffer(builder_temp, output_model_location);
587   const uint8_t* buffer_temp = builder_temp.GetBufferPointer();
588   const Model* input_model = GetModel(buffer_temp);
589 
590   // Change model type.
591   flatbuffers::FlatBufferBuilder builder;
592   EXPECT_EQ(Uint8QuantizeModelInputsOutputs(&builder, input_model,
593                                             {{"tensor_0", {0.4, 2}}},
594                                             {{"tensor_1", {0.5, -5}}}),
595             kTfLiteOk);
596 
597   const uint8_t* buffer = builder.GetBufferPointer();
598   const Model* output_model = GetModel(buffer);
599   std::unique_ptr<ModelT> model;
600   model.reset(output_model->UnPack());
601 
602   // Verify results.
603   EXPECT_EQ(model->subgraphs.size(), 1);
604   EXPECT_EQ(model->subgraphs[0]->tensors.size(), 4);
605   EXPECT_EQ(model->subgraphs[0]->inputs.size(), 1);
606   EXPECT_EQ(model->subgraphs[0]->inputs[0], 0);
607   EXPECT_EQ(model->subgraphs[0]->outputs.size(), 1);
608   EXPECT_EQ(model->subgraphs[0]->outputs[0], 1);
609   EXPECT_EQ(model->operator_codes.size(), 3);
610   EXPECT_EQ(GetBuiltinCode(model->operator_codes[0].get()),
611             BuiltinOperator_FULLY_CONNECTED);
612   EXPECT_EQ(GetBuiltinCode(model->operator_codes[1].get()),
613             BuiltinOperator_DEQUANTIZE);
614   EXPECT_EQ(GetBuiltinCode(model->operator_codes[2].get()),
615             BuiltinOperator_QUANTIZE);
616   EXPECT_EQ(model->subgraphs[0]->operators.size(), 3);
617 
618   auto dequantize_op = model->subgraphs[0]->operators[0].get();
619   auto input = model->subgraphs[0]->tensors[dequantize_op->inputs[0]].get();
620   EXPECT_EQ(input->name, "tensor_0_uint8");
621   EXPECT_EQ(input->type, TensorType_UINT8);
622   EXPECT_FLOAT_EQ(input->quantization->scale[0], 0.4);
623   EXPECT_EQ(input->quantization->zero_point[0], 2);
624 
625   auto quantize_op = model->subgraphs[0]->operators[2].get();
626   auto output = model->subgraphs[0]->tensors[quantize_op->outputs[0]].get();
627   EXPECT_EQ(output->name, "tensor_1_uint8");
628   EXPECT_EQ(output->type, TensorType_UINT8);
629   EXPECT_FLOAT_EQ(output->quantization->scale[0], 0.5);
630   EXPECT_EQ(output->quantization->zero_point[0], -5);
631 }
632 
633 }  // namespace
634 }  // namespace optimize
635 }  // namespace tflite
636