1 /* Copyright 2019 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
16 #ifndef TENSORFLOW_LITE_MICRO_TEST_HELPERS_H_
17 #define TENSORFLOW_LITE_MICRO_TEST_HELPERS_H_
18
19 #include <cstdint>
20 #include <limits>
21
22 #include "flatbuffers/flatbuffers.h" // from @flatbuffers
23 #include "tensorflow/lite/c/common.h"
24 #include "tensorflow/lite/kernels/internal/compatibility.h"
25 #include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
26 #include "tensorflow/lite/micro/all_ops_resolver.h"
27 #include "tensorflow/lite/micro/micro_utils.h"
28 #include "tensorflow/lite/portable_type_to_tflitetype.h"
29 #include "tensorflow/lite/schema/schema_generated.h"
30
31 namespace tflite {
32 namespace testing {
33
34 constexpr int kOfflinePlannerHeaderSize = 3;
35
36 struct NodeConnection_ {
37 std::initializer_list<int32_t> input;
38 std::initializer_list<int32_t> output;
39 };
40 typedef struct NodeConnection_ NodeConnection;
41
42 // A simple operator that returns the median of the input with the number of
43 // times the kernel was invoked. The implementation below is deliberately
44 // complicated, just to demonstrate how kernel memory planning works.
45 class SimpleStatefulOp {
46 static constexpr int kBufferNotAllocated = 0;
47 // Inputs:
48 static constexpr int kInputTensor = 0;
49 // Outputs:
50 static constexpr int kMedianTensor = 0;
51 static constexpr int kInvokeCount = 1;
52 struct OpData {
53 int* invoke_count = nullptr;
54 int sorting_buffer = kBufferNotAllocated;
55 };
56
57 public:
58 static const TfLiteRegistration* getRegistration();
59 static TfLiteRegistration* GetMutableRegistration();
60 static void* Init(TfLiteContext* context, const char* buffer, size_t length);
61 static TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node);
62 static TfLiteStatus Invoke(TfLiteContext* context, TfLiteNode* node);
63 };
64
65 class MockCustom {
66 public:
67 static const TfLiteRegistration* getRegistration();
68 static TfLiteRegistration* GetMutableRegistration();
69 static void* Init(TfLiteContext* context, const char* buffer, size_t length);
70 static void Free(TfLiteContext* context, void* buffer);
71 static TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node);
72 static TfLiteStatus Invoke(TfLiteContext* context, TfLiteNode* node);
73
74 static bool freed_;
75 };
76
77 // A simple operator with the purpose of testing multiple inputs. It returns
78 // the sum of the inputs.
79 class MultipleInputs {
80 public:
81 static const TfLiteRegistration* getRegistration();
82 static TfLiteRegistration* GetMutableRegistration();
83 static void* Init(TfLiteContext* context, const char* buffer, size_t length);
84 static void Free(TfLiteContext* context, void* buffer);
85 static TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node);
86 static TfLiteStatus Invoke(TfLiteContext* context, TfLiteNode* node);
87
88 static bool freed_;
89 };
90
91 // Returns an Op Resolver that can be used in the testing code.
92 AllOpsResolver GetOpResolver();
93
94 // Returns a simple example flatbuffer TensorFlow Lite model. Contains 1 input,
95 // 1 layer of weights, 1 output Tensor, and 1 operator.
96 const Model* GetSimpleMockModel();
97
98 // Returns a flatbuffer TensorFlow Lite model with more inputs, variable
99 // tensors, and operators.
100 const Model* GetComplexMockModel();
101
102 // Returns a simple flatbuffer model with two branches.
103 const Model* GetSimpleModelWithBranch();
104
105 // Returns a simple example flatbuffer TensorFlow Lite model. Contains 3 inputs,
106 // 1 output Tensor, and 1 operator.
107 const Model* GetSimpleMultipleInputsModel();
108
109 // Returns a simple flatbuffer model with offline planned tensors
110 // @param[in] num_tensors Number of tensors in the model.
111 // @param[in] metadata_buffer Metadata for offline planner.
112 // @param[in] node_con List of connections, i.e. operators
113 // in the model.
114 // @param[in] num_conns Number of connections.
115 // @param[in] num_subgraph_inputs How many of the input tensors are in
116 // the subgraph inputs. The default value
117 // of 0 means all of the input tensors
118 // are in the subgraph input list. There
119 // must be at least 1 input tensor in the
120 // subgraph input list.
121 const Model* GetModelWithOfflinePlanning(int num_tensors,
122 const int32_t* metadata_buffer,
123 NodeConnection* node_conn,
124 int num_conns,
125 int num_subgraph_inputs = 0);
126
127 // Returns a flatbuffer with a single operator, two inputs (one unused) and one
128 // output.
129 const Model* GetModelWithUnusedInputs();
130
131 // Returns a flatbuffer model with `simple_stateful_op`
132 const Model* GetSimpleStatefulModel();
133
134 // Returns a flatbuffer model with "if" and two subgraphs.
135 const Model* GetSimpleModelWithSubgraphsAndIf();
136
137 // Builds a one-dimensional flatbuffer tensor of the given size.
138 const Tensor* Create1dFlatbufferTensor(int size, bool is_variable = false);
139
140 // Builds a one-dimensional flatbuffer tensor of the given size with
141 // quantization metadata.
142 const Tensor* CreateQuantizedFlatbufferTensor(int size);
143
144 // Creates a one-dimensional tensor with no quantization metadata.
145 const Tensor* CreateMissingQuantizationFlatbufferTensor(int size);
146
147 // Creates a vector of flatbuffer buffers.
148 const flatbuffers::Vector<flatbuffers::Offset<Buffer>>*
149 CreateFlatbufferBuffers();
150
151 // Performs a simple string comparison without requiring standard C library.
152 int TestStrcmp(const char* a, const char* b);
153
154 // Wrapper to forward kernel errors to the interpreter's error reporter.
155 void ReportOpError(struct TfLiteContext* context, const char* format, ...);
156
157 void PopulateContext(TfLiteTensor* tensors, int tensors_size,
158 TfLiteContext* context);
159
160 // Create a TfLiteIntArray from an array of ints. The first element in the
161 // supplied array must be the size of the array expressed as an int.
162 TfLiteIntArray* IntArrayFromInts(int* int_array);
163
164 // Create a TfLiteFloatArray from an array of floats. The first element in the
165 // supplied array must be the size of the array expressed as a float.
166 TfLiteFloatArray* FloatArrayFromFloats(const float* floats);
167
168 template <typename T>
169 TfLiteTensor CreateTensor(const T* data, TfLiteIntArray* dims,
170 const bool is_variable = false) {
171 TfLiteTensor result;
172 result.dims = dims;
173 result.params = {};
174 result.quantization = {kTfLiteNoQuantization, nullptr};
175 result.is_variable = is_variable;
176 result.allocation_type = kTfLiteMemNone;
177 result.type = typeToTfLiteType<T>();
178 // Const cast is used to allow passing in const and non-const arrays within a
179 // single CreateTensor method. A Const array should be used for immutable
180 // input tensors and non-const array should be used for mutable and output
181 // tensors.
182 result.data.data = const_cast<T*>(data);
183 result.quantization = {kTfLiteAffineQuantization, nullptr};
184 result.bytes = ElementCount(*dims) * sizeof(T);
185 return result;
186 }
187
188 template <typename T>
189 TfLiteTensor CreateQuantizedTensor(const T* data, TfLiteIntArray* dims,
190 const float scale, const int zero_point = 0,
191 const bool is_variable = false) {
192 TfLiteTensor result = CreateTensor(data, dims, is_variable);
193 result.params = {scale, zero_point};
194 result.quantization = {kTfLiteAffineQuantization, nullptr};
195 return result;
196 }
197
198 template <typename T>
199 TfLiteTensor CreateQuantizedTensor(const float* input, T* quantized,
200 TfLiteIntArray* dims, float scale,
201 int zero_point, bool is_variable = false) {
202 int input_size = ElementCount(*dims);
203 tflite::Quantize(input, quantized, input_size, scale, zero_point);
204 return CreateQuantizedTensor(quantized, dims, scale, zero_point, is_variable);
205 }
206
207 TfLiteTensor CreateQuantizedBiasTensor(const float* data, int16_t* quantized,
208 TfLiteIntArray* dims, float input_scale,
209 float weights_scale,
210 bool is_variable = false);
211
212 TfLiteTensor CreateQuantizedBiasTensor(const float* data, int32_t* quantized,
213 TfLiteIntArray* dims, float input_scale,
214 float weights_scale,
215 bool is_variable = false);
216
217 TfLiteTensor CreateQuantizedBiasTensor(const float* data,
218 std::int64_t* quantized,
219 TfLiteIntArray* dims, float input_scale,
220 float weights_scale,
221 bool is_variable = false);
222
223 // Quantizes int32_t bias tensor with per-channel weights determined by input
224 // scale multiplied by weight scale for each channel.
225 TfLiteTensor CreatePerChannelQuantizedBiasTensor(
226 const float* input, int32_t* quantized, TfLiteIntArray* dims,
227 float input_scale, float* weight_scales, float* scales, int* zero_points,
228 TfLiteAffineQuantization* affine_quant, int quantized_dimension,
229 bool is_variable = false);
230
231 // Quantizes int64_t bias tensor with per-channel weights determined by input
232 // scale multiplied by weight scale for each channel.
233 TfLiteTensor CreatePerChannelQuantizedBiasTensor(
234 const float* input, std::int64_t* quantized, TfLiteIntArray* dims,
235 float input_scale, float* weight_scales, float* scales, int* zero_points,
236 TfLiteAffineQuantization* affine_quant, int quantized_dimension,
237 bool is_variable = false);
238
239 TfLiteTensor CreateSymmetricPerChannelQuantizedTensor(
240 const float* input, int8_t* quantized, TfLiteIntArray* dims, float* scales,
241 int* zero_points, TfLiteAffineQuantization* affine_quant,
242 int quantized_dimension, bool is_variable = false);
243
244 // Returns the number of tensors in the default subgraph for a tflite::Model.
245 size_t GetModelTensorCount(const Model* model);
246
247 // Derives the quantization scaling factor from a min and max range.
248 template <typename T>
ScaleFromMinMax(const float min,const float max)249 inline float ScaleFromMinMax(const float min, const float max) {
250 return (max - min) /
251 static_cast<float>((std::numeric_limits<T>::max() * 1.0) -
252 std::numeric_limits<T>::min());
253 }
254
255 // Derives the quantization zero point from a min and max range.
256 template <typename T>
ZeroPointFromMinMax(const float min,const float max)257 inline int ZeroPointFromMinMax(const float min, const float max) {
258 return static_cast<int>(std::numeric_limits<T>::min()) +
259 static_cast<int>(-min / ScaleFromMinMax<T>(min, max) + 0.5f);
260 }
261
262 } // namespace testing
263 } // namespace tflite
264
265 #endif // TENSORFLOW_LITE_MICRO_TEST_HELPERS_H_
266