1 /* Copyright 2017 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 #include <stdint.h>
17
18 #include <initializer_list>
19
20 #include "tensorflow/lite/c/common.h"
21 #include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
22 #include "tensorflow/lite/micro/kernels/kernel_runner.h"
23 #include "tensorflow/lite/micro/micro_utils.h"
24 #include "tensorflow/lite/micro/test_helpers.h"
25 #include "tensorflow/lite/micro/testing/micro_test.h"
26
27 namespace tflite {
28 namespace testing {
29 namespace {
30
31 // TODO(b/162356196): Cleanup this unit test more.
32
33 template <typename T>
ValidateReshapeGoldens(TfLiteTensor * tensors,int tensors_size,TfLiteIntArray * inputs_array,TfLiteIntArray * outputs_array,const T * expected_output,const size_t expected_output_len,const int * expected_dims,const size_t expected_dims_len,bool expect_failure)34 void ValidateReshapeGoldens(
35 TfLiteTensor* tensors, int tensors_size, TfLiteIntArray* inputs_array,
36 TfLiteIntArray* outputs_array, const T* expected_output,
37 const size_t expected_output_len, const int* expected_dims,
38 const size_t expected_dims_len, bool expect_failure) {
39 const TfLiteRegistration registration =
40 tflite::ops::micro::Register_RESHAPE();
41 micro::KernelRunner runner(registration, tensors, tensors_size, inputs_array,
42 outputs_array,
43 /*builtin_data=*/nullptr);
44
45 if (expect_failure) {
46 TF_LITE_MICRO_EXPECT_NE(kTfLiteOk, runner.InitAndPrepare());
47 return;
48 }
49
50 TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, runner.InitAndPrepare());
51 TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, runner.Invoke());
52
53 TfLiteTensor* output_tensor = &tensors[outputs_array->data[0]];
54 const T* output_data = GetTensorData<T>(output_tensor);
55 for (size_t i = 0; i < expected_output_len; ++i) {
56 TF_LITE_MICRO_EXPECT_NEAR(expected_output[i], output_data[i], 1e-5f);
57 }
58 TF_LITE_MICRO_EXPECT_EQ(expected_dims_len,
59 static_cast<size_t>(output_tensor->dims->size));
60 for (size_t i = 0; i < expected_dims_len; ++i) {
61 TF_LITE_MICRO_EXPECT_EQ(expected_dims[i], output_tensor->dims->data[i]);
62 }
63 }
64 template <typename T>
TestReshapeWithShape(TfLiteTensor * input_tensor,TfLiteTensor * shape_tensor,TfLiteTensor * output_tensor,const T * expected_output,const size_t expected_output_len,const int * expected_dims,const size_t expected_dims_len,bool expect_failure)65 void TestReshapeWithShape(TfLiteTensor* input_tensor,
66 TfLiteTensor* shape_tensor,
67 TfLiteTensor* output_tensor, const T* expected_output,
68 const size_t expected_output_len,
69 const int* expected_dims,
70 const size_t expected_dims_len, bool expect_failure) {
71 constexpr int inputs_size = 2;
72 constexpr int outputs_size = 1;
73 constexpr int tensors_size = inputs_size + outputs_size;
74 TfLiteTensor tensors[tensors_size];
75 tensors[0] = *input_tensor;
76 tensors[1] = *shape_tensor;
77 tensors[2] = *output_tensor;
78
79 int inputs_data[] = {2, 0, 1};
80 TfLiteIntArray* inputs_array = IntArrayFromInts(inputs_data);
81 int outputs_data[] = {1, 2};
82 TfLiteIntArray* outputs_array = IntArrayFromInts(outputs_data);
83
84 ValidateReshapeGoldens(tensors, tensors_size, inputs_array, outputs_array,
85 expected_output, expected_output_len, expected_dims,
86 expected_dims_len, expect_failure);
87 }
88
89 // If expected output is empty, the test is expected to fail.
90 template <typename T>
TestReshapeWithoutShape(TfLiteTensor * input_tensor,TfLiteTensor * output_tensor,const T * expected_output,const size_t expected_output_len,const int * expected_dims,const size_t expected_dims_len,bool expect_failure)91 void TestReshapeWithoutShape(TfLiteTensor* input_tensor,
92 TfLiteTensor* output_tensor,
93 const T* expected_output,
94 const size_t expected_output_len,
95 const int* expected_dims,
96 const size_t expected_dims_len,
97 bool expect_failure) {
98 constexpr int inputs_size = 1;
99 constexpr int outputs_size = 1;
100 constexpr int tensors_size = inputs_size + outputs_size;
101 TfLiteTensor tensors[tensors_size];
102 tensors[0] = *input_tensor;
103 tensors[1] = *output_tensor;
104
105 int inputs_data[] = {1, 0};
106 TfLiteIntArray* inputs_array = IntArrayFromInts(inputs_data);
107 int outputs_data[] = {1, 1};
108 TfLiteIntArray* outputs_array = IntArrayFromInts(outputs_data);
109
110 ValidateReshapeGoldens(tensors, tensors_size, inputs_array, outputs_array,
111 expected_output, expected_output_len, expected_dims,
112 expected_dims_len, expect_failure);
113 }
114
TestReshape(const int * input_dims_data,const float * input_data,const int * shape_dims_data,const int32_t * shape_data,int * output_dims_data,float * output_data,const float * expected_output,const size_t expected_output_len,const int * expected_dims,const size_t expected_dims_len,bool expect_failure=false)115 void TestReshape(const int* input_dims_data, const float* input_data,
116 const int* shape_dims_data, const int32_t* shape_data,
117 int* output_dims_data, float* output_data,
118 const float* expected_output, const size_t expected_output_len,
119 const int* expected_dims, const size_t expected_dims_len,
120 bool expect_failure = false) {
121 TfLiteIntArray* input_dims = IntArrayFromInts(input_dims_data);
122 TfLiteIntArray* shape_dims = IntArrayFromInts(shape_dims_data);
123 TfLiteIntArray* output_dims = IntArrayFromInts(output_dims_data);
124 TfLiteTensor input_tensor = CreateTensor(input_data, input_dims);
125 TfLiteTensor shape_tensor = CreateTensor(shape_data, shape_dims);
126 TfLiteTensor output_tensor = CreateTensor(output_data, output_dims);
127
128 TestReshapeWithShape(&input_tensor, &shape_tensor, &output_tensor,
129 expected_output, expected_output_len, expected_dims,
130 expected_dims_len, expect_failure);
131 }
132
133 template <typename T>
TestReshapeQuantized(const int * input_dims_data,const T * input_data,const int * shape_dims_data,const int32_t * shape_data,int * output_dims_data,T * output_data,const T * expected_output,const size_t expected_output_len,const int * expected_dims,const size_t expected_dims_len,bool expect_failure=false)134 void TestReshapeQuantized(const int* input_dims_data, const T* input_data,
135 const int* shape_dims_data, const int32_t* shape_data,
136 int* output_dims_data, T* output_data,
137 const T* expected_output,
138 const size_t expected_output_len,
139 const int* expected_dims,
140 const size_t expected_dims_len,
141 bool expect_failure = false) {
142 TfLiteIntArray* input_dims = IntArrayFromInts(input_dims_data);
143 TfLiteIntArray* shape_dims = IntArrayFromInts(shape_dims_data);
144 TfLiteIntArray* output_dims = IntArrayFromInts(output_dims_data);
145 TfLiteTensor input_tensor = CreateQuantizedTensor(
146 input_data, input_dims, /*scale=*/1.f, /*zero_point=*/0);
147 TfLiteTensor shape_tensor = CreateTensor(shape_data, shape_dims);
148 TfLiteTensor output_tensor = CreateQuantizedTensor(
149 output_data, output_dims, /*scale=*/1.f, /*zero_point=*/0);
150
151 TestReshapeWithShape(&input_tensor, &shape_tensor, &output_tensor,
152 expected_output, expected_output_len, expected_dims,
153 expected_dims_len, expect_failure);
154 }
155 } // namespace
156 } // namespace testing
157 } // namespace tflite
158
159 TF_LITE_MICRO_TESTS_BEGIN
160
TF_LITE_MICRO_TEST(ReshapeWithMismatchedDimensionsShouldFail)161 TF_LITE_MICRO_TEST(ReshapeWithMismatchedDimensionsShouldFail) {
162 float output_data[32];
163 const int input_dims[] = {4, 1, 2, 4, 1};
164 const float input_data[] = {3};
165 const int shape_dims[] = {1, 2};
166 const int32_t shape_int32[] = {2, 1};
167 int output_dims[] = {2, 2, 1};
168 const int golden_output_len = 0;
169 const float golden_output[] = {};
170 const int golden_dims_len = 0;
171 const int golden_dims[] = {};
172 tflite::testing::TestReshape(
173 input_dims, input_data, shape_dims, shape_int32, output_dims, output_data,
174 golden_output, golden_output_len, golden_dims, golden_dims_len, true);
175 }
176
TF_LITE_MICRO_TEST(ReshapeWithTooManyDimensionsShouldFail)177 TF_LITE_MICRO_TEST(ReshapeWithTooManyDimensionsShouldFail) {
178 float output_data[32];
179 const int input_dims[] = {9, 1, 1, 2, 1, 1, 1, 1, 1, 1};
180 const float input[] = {3, 2};
181 const int shape_dims[] = {1, 9};
182 const int32_t shape_int32[] = {1, 1, 1, 1, 1, 1, 1, 1, 2};
183 int output_dims[] = {9, 1, 1, 1, 1, 1, 1, 1, 1, 2};
184 const int golden_output_len = 2;
185 const float golden_output[] = {3, 2};
186 const int golden_dims_len = 9;
187 const int golden_dims[] = {1, 1, 1, 1, 1, 1, 1, 1, 2};
188 tflite::testing::TestReshape(
189 input_dims, input, shape_dims, shape_int32, output_dims, output_data,
190 golden_output, golden_output_len, golden_dims, golden_dims_len, false);
191 }
192
TF_LITE_MICRO_TEST(ReshapeWithTooManySpecialDimensionsShouldFail)193 TF_LITE_MICRO_TEST(ReshapeWithTooManySpecialDimensionsShouldFail) {
194 float output_data[32];
195 const int input_dims[] = {4, 1, 2, 4, 11};
196 const float input[] = {3};
197 const int shape_dims[] = {1, 4};
198 const int32_t shape_int32[] = {-1, -1, 2, 4};
199 int output_dims[] = {4, -1, -1, 2, 4};
200 const int golden_output_len = 2;
201 const float golden_output[] = {};
202 const int golden_dims_len = 9;
203 const int golden_dims[] = {};
204 tflite::testing::TestReshape(
205 input_dims, input, shape_dims, shape_int32, output_dims, output_data,
206 golden_output, golden_output_len, golden_dims, golden_dims_len, true);
207 }
208
209 // Create the model with a 2x2 shape. Processing still works because the new
210 // shape ends up being hardcoded as a flat vector.
TF_LITE_MICRO_TEST(ReshapeWithInvalidShapeShouldFail)211 TF_LITE_MICRO_TEST(ReshapeWithInvalidShapeShouldFail) {
212 int input_dims_data[] = {3, 1, 2, 2};
213 TfLiteIntArray* input_dims =
214 tflite::testing::IntArrayFromInts(input_dims_data);
215 const float input_data[] = {3.0f};
216 auto input_tensor = tflite::testing::CreateTensor(input_data, input_dims);
217 float output_data[4];
218 int output_dims_data[6] = {2, 2, 1, 2, 2, 1};
219 TfLiteIntArray* output_dims =
220 tflite::testing::IntArrayFromInts(output_dims_data);
221 auto output_tensor = tflite::testing::CreateTensor(output_data, output_dims);
222 const int expected_output[] = {};
223 const int expected_output_len = 0;
224 const int expected_dims[] = {};
225 const int expected_dims_len = 0;
226 tflite::testing::TestReshapeWithoutShape(
227 &input_tensor, &output_tensor, expected_output, expected_output_len,
228 expected_dims, expected_dims_len, true);
229 }
230
TF_LITE_MICRO_TEST(ReshapeWithRegularShapesShouldSucceed)231 TF_LITE_MICRO_TEST(ReshapeWithRegularShapesShouldSucceed) {
232 float output_data_float[32];
233 int8_t output_data_int8[32];
234 uint8_t output_data_uint8[32];
235 const int input_dims[] = {4, 1, 2, 4, 1};
236 const float input_float[] = {1, 2, 3, 4, 5, 6, 7, 8};
237 const int8_t input_int8[] = {1, 2, 3, 4, 5, 6, 7, 8};
238 const uint8_t input_uint8[] = {1, 2, 3, 4, 5, 6, 7, 8};
239 const int shape_dims[] = {1, 3};
240 const int32_t shape_int32[] = {2, 2, 2};
241 int output_dims[] = {3, 2, 2, 2};
242 const int golden_output_len = 8;
243 const float golden_output_float[] = {1, 2, 3, 4, 5, 6, 7, 8};
244 const int8_t golden_output_int8[] = {1, 2, 3, 4, 5, 6, 7, 8};
245 const uint8_t golden_output_uint8[] = {1, 2, 3, 4, 5, 6, 7, 8};
246 const int golden_dims_len = 3;
247 const int golden_dims[] = {2, 2, 2};
248 tflite::testing::TestReshape(input_dims, input_float, shape_dims, shape_int32,
249 output_dims, output_data_float,
250 golden_output_float, golden_output_len,
251 golden_dims, golden_dims_len, false);
252 tflite::testing::TestReshapeQuantized(
253 input_dims, input_int8, shape_dims, shape_int32, output_dims,
254 output_data_int8, golden_output_int8, golden_output_len, golden_dims,
255 golden_dims_len, false);
256 tflite::testing::TestReshapeQuantized(
257 input_dims, input_uint8, shape_dims, shape_int32, output_dims,
258 output_data_uint8, golden_output_uint8, golden_output_len, golden_dims,
259 golden_dims_len, false);
260 }
261
262 // Stretch is not supported with TF Micro
TF_LITE_MICRO_TEST(ReshapeWithStretchDimensionShouldSucceed)263 TF_LITE_MICRO_TEST(ReshapeWithStretchDimensionShouldSucceed) {
264 float output_data_float[32];
265 int8_t output_data_int8[32];
266 uint8_t output_data_uint8[32];
267 const int input_dims[] = {4, 1, 2, 4, 1};
268 const float input_float[] = {1, 2, 3, 4, 5, 6, 7, 8};
269 const int8_t input_int8[] = {1, 2, 3, 4, 5, 6, 7, 8};
270 const uint8_t input_uint8[] = {1, 2, 3, 4, 5, 6, 7, 8};
271 const int shape_dims[] = {1, 3};
272 const int32_t shape_int32[] = {2, 1, -1};
273 int output_dims[] = {3, 2, 1, -1};
274 const int golden_output_len = 8;
275 const float golden_output_float[] = {1, 2, 3, 4, 5, 6, 7, 8};
276 const int8_t golden_output_int8[] = {1, 2, 3, 4, 5, 6, 7, 8};
277 const uint8_t golden_output_uint8[] = {1, 2, 3, 4, 5, 6, 7, 8};
278 const int golden_dims_len = 3;
279 const int golden_dims[] = {2, 1, 4};
280 tflite::testing::TestReshape(input_dims, input_float, shape_dims, shape_int32,
281 output_dims, output_data_float,
282 golden_output_float, golden_output_len,
283 golden_dims, golden_dims_len, false);
284 tflite::testing::TestReshapeQuantized(
285 input_dims, input_int8, shape_dims, shape_int32, output_dims,
286 output_data_int8, golden_output_int8, golden_output_len, golden_dims,
287 golden_dims_len, false);
288 tflite::testing::TestReshapeQuantized(
289 input_dims, input_uint8, shape_dims, shape_int32, output_dims,
290 output_data_uint8, golden_output_uint8, golden_output_len, golden_dims,
291 golden_dims_len, false);
292 }
293
294 // Empty shape indicates scalar output.
TF_LITE_MICRO_TEST(ReshapeWithScalarOutputShouldSucceed)295 TF_LITE_MICRO_TEST(ReshapeWithScalarOutputShouldSucceed) {
296 float output_data_float[4];
297 int8_t output_data_int8[4];
298 uint8_t output_data_uint8[4];
299 const int input_dims[] = {1, 1};
300 const float input_float[] = {3};
301 const int8_t input_int8[] = {3};
302 const uint8_t input_uint8[] = {3};
303 const int shape_dims[] = {0};
304 const int32_t shape_int32[] = {};
305 int output_dims[] = {0};
306 const int golden_output_len = 1;
307 const float golden_output_float[] = {3};
308 const int8_t golden_output_int8[] = {3};
309 const uint8_t golden_output_uint8[] = {3};
310 const int golden_dims_len = 0;
311 const int golden_dims[] = {};
312 tflite::testing::TestReshape(input_dims, input_float, shape_dims, shape_int32,
313 output_dims, output_data_float,
314 golden_output_float, golden_output_len,
315 golden_dims, golden_dims_len, false);
316 tflite::testing::TestReshapeQuantized(
317 input_dims, input_int8, shape_dims, shape_int32, output_dims,
318 output_data_int8, golden_output_int8, golden_output_len, golden_dims,
319 golden_dims_len, false);
320 tflite::testing::TestReshapeQuantized(
321 input_dims, input_uint8, shape_dims, shape_int32, output_dims,
322 output_data_uint8, golden_output_uint8, golden_output_len, golden_dims,
323 golden_dims_len, false);
324 }
325
326 // Some old models specify '[0]' as the new shape, indicating that both input
327 // and output are scalars.
TF_LITE_MICRO_TEST(ReshapeWithLegacyScalarOutputShouldSucceed)328 TF_LITE_MICRO_TEST(ReshapeWithLegacyScalarOutputShouldSucceed) {
329 using tflite::testing::CreateTensor;
330 using tflite::testing::IntArrayFromInts;
331
332 int input_dims_data[] = {1, 1};
333 TfLiteIntArray* input_dims = IntArrayFromInts(input_dims_data);
334 const float input_data[] = {3.0f};
335 auto input_tensor = CreateTensor(input_data, input_dims);
336
337 float output_data[1];
338 int output_dims_data[2] = {1, 0};
339 TfLiteIntArray* output_dims = IntArrayFromInts(output_dims_data);
340 auto output_tensor = CreateTensor(output_data, output_dims);
341
342 int shape_dims_data[] = {1, 0};
343 TfLiteIntArray* shape_dims = IntArrayFromInts(shape_dims_data);
344
345 const int32_t shape_data[] = {0};
346 auto shape_tensor = tflite::testing::CreateTensor(shape_data, shape_dims);
347 const float expected_output_with_shape[] = {};
348 const int expected_output_with_shape_len = 0;
349 const float expected_output_no_shape[] = {3};
350 const int expected_output_no_shape_len = 1;
351 const int expected_dims[] = {};
352 const int expected_dims_len = 0;
353 tflite::testing::TestReshapeWithShape<float>(
354 &input_tensor, &shape_tensor, &output_tensor, expected_output_with_shape,
355 expected_output_with_shape_len, expected_dims, expected_dims_len, true);
356
357 tflite::testing::TestReshapeWithoutShape<float>(
358 &input_tensor, &output_tensor, expected_output_no_shape,
359 expected_output_no_shape_len, expected_dims, expected_dims_len, false);
360 }
361
362 TF_LITE_MICRO_TESTS_END
363