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