1/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "NeuralNetworksWrapper.h"
18#include "NeuralNetworksOEM.h"
19
20#include <gtest/gtest.h>
21
22using namespace android::nn::wrapper;
23
24namespace {
25
26static const int32_t kAvailableOperandCodes[] = {
27    ANEURALNETWORKS_FLOAT32,
28    ANEURALNETWORKS_INT32,
29    ANEURALNETWORKS_UINT32,
30    ANEURALNETWORKS_TENSOR_FLOAT32,
31    ANEURALNETWORKS_TENSOR_INT32,
32    ANEURALNETWORKS_TENSOR_QUANT8_ASYMM,
33    ANEURALNETWORKS_TENSOR_OEM_BYTE
34};
35
36class OperationTestBase {
37public:
38    OperationTestBase(ANeuralNetworksOperationType opCode,
39                      std::vector<ANeuralNetworksOperandType> validInputs,
40                      std::vector<ANeuralNetworksOperandType> validOutputs)
41        : mOpCode(opCode),
42          mValidInputs(std::move(validInputs)),
43          mValidOutputs(std::move(validOutputs)) {}
44
45    // Add each operand separately and add the operation using these operands.
46    // This function does not cover the cases that a operand used mutiple times.
47    int32_t addOperation(const std::vector<ANeuralNetworksOperandType>& inputs,
48                         const std::vector<ANeuralNetworksOperandType>& outputs) {
49        ANeuralNetworksModel* model = nullptr;
50        ANeuralNetworksModel_create(&model);
51
52        uint32_t opIdx = 0;
53        std::vector<uint32_t> inputIds;
54        std::vector<uint32_t> outputIds;
55        for (uint32_t i = 0; i < inputs.size(); i++) {
56            ANeuralNetworksModel_addOperand(model, &inputs[i]);
57            inputIds.push_back(opIdx++);
58        }
59        for (uint32_t i = 0; i < outputs.size(); i++) {
60            ANeuralNetworksModel_addOperand(model, &outputs[i]);
61            outputIds.push_back(opIdx++);
62        }
63
64        int32_t result = ANeuralNetworksModel_addOperation(model, mOpCode,
65                                                 static_cast<uint32_t>(inputIds.size()),
66                                                 inputIds.data(),
67                                                 static_cast<uint32_t>(outputIds.size()),
68                                                 outputIds.data());
69        ANeuralNetworksModel_free(model);
70        return result;
71    }
72
73    bool testMutatingInputOperandCode() {
74        for (uint32_t i = 0; i < mValidInputs.size(); i++) {
75            // LSH_PROJECTION's second argument is allowed to have any type.
76            // This is the only operation that currently has a type that can be
77            // anything independent from any other type. Changing the operand
78            // type to any other type will result in a valid model for
79            // LSH_PROJECTION. If this is the case, skip the test.
80            if (mOpCode == ANEURALNETWORKS_LSH_PROJECTION && i == 1) {
81                continue;
82            }
83            ANeuralNetworksOperandType newType = mValidInputs[i];
84            int32_t originalOperandCode = mValidInputs[i].type;
85            for (int32_t newOperandCode : kAvailableOperandCodes) {
86                if (newOperandCode == originalOperandCode) {
87                    continue;
88                }
89                newType.type = newOperandCode;
90                std::vector<ANeuralNetworksOperandType> inputs = mValidInputs;
91                inputs[i] = newType;
92                int32_t result = addOperation(inputs, mValidOutputs);
93                if (ANEURALNETWORKS_NO_ERROR == result) {
94                    return false;
95                }
96            }
97        }
98        return true;
99    }
100
101    bool testMutatingOutputOperandCode() {
102        for (uint32_t i = 0; i < mValidOutputs.size(); i++) {
103            // LSH_PROJECTION's second argument is allowed to have any type.
104            // This is the only operation that currently has a type that can be
105            // anything independent from any other type. Changing the operand
106            // type to any other type will result in a valid model for
107            // LSH_PROJECTION. If this is the case, skip the test.
108            if (mOpCode == ANEURALNETWORKS_LSH_PROJECTION && i == 1) {
109                continue;
110            }
111            ANeuralNetworksOperandType newType = mValidOutputs[i];
112            int32_t originalOperandCode = mValidOutputs[i].type;
113            for (int32_t newOperandCode : kAvailableOperandCodes) {
114                if (newOperandCode == originalOperandCode) {
115                    continue;
116                }
117                newType.type = newOperandCode;
118                std::vector<ANeuralNetworksOperandType> outputs = mValidOutputs;
119                outputs[i] = newType;
120                int32_t result = addOperation(mValidInputs, outputs);
121                if (ANEURALNETWORKS_NO_ERROR == result) {
122                    return false;
123                }
124            }
125        }
126        return true;
127    }
128
129    bool testMutatingInputOperandCounts() {
130        std::vector<ANeuralNetworksOperandType> inputs = mValidInputs;
131        for (uint32_t i = 0; i < 5; i++) {
132            inputs.push_back(inputs[0]);
133            if (ANEURALNETWORKS_NO_ERROR == addOperation(inputs, mValidOutputs)) {
134                return false;
135            }
136        }
137        return true;
138    }
139
140    bool testMutatingOutputOperandCounts() {
141        std::vector<ANeuralNetworksOperandType> outputs = mValidOutputs;
142        for (int i = 0; i < 5; i++) {
143            outputs.push_back(outputs[0]);
144            if (ANEURALNETWORKS_NO_ERROR == addOperation(mValidInputs, outputs)) {
145                return false;
146            }
147        }
148        return true;
149    }
150
151private:
152    ANeuralNetworksOperationType mOpCode;
153    // The dimensions in the ANeuralNetworksOperandType must outlive the test object.
154    std::vector<ANeuralNetworksOperandType> mValidInputs;
155    std::vector<ANeuralNetworksOperandType> mValidOutputs;
156};
157
158TEST(OperationValidationTest, DEQUANTIZE_float32) {
159    uint32_t inputDimensions[4] = {2, 2, 2, 2};
160    ANeuralNetworksOperandType input = {.type = ANEURALNETWORKS_TENSOR_QUANT8_ASYMM,
161                                        .dimensionCount = 4,
162                                        .dimensions = inputDimensions,
163                                        .scale = 1.0f,
164                                        .zeroPoint = 0};
165    ANeuralNetworksOperandType output = {.type = ANEURALNETWORKS_TENSOR_FLOAT32,
166                                         .dimensionCount = 4,
167                                         .dimensions = inputDimensions,
168                                         .scale = 0.0f,
169                                         .zeroPoint = 0};
170    OperationTestBase dequantizeTest(ANEURALNETWORKS_DEQUANTIZE, {input}, {output});
171
172    EXPECT_TRUE(dequantizeTest.testMutatingInputOperandCode());
173    EXPECT_TRUE(dequantizeTest.testMutatingInputOperandCounts());
174    EXPECT_TRUE(dequantizeTest.testMutatingOutputOperandCode());
175    EXPECT_TRUE(dequantizeTest.testMutatingOutputOperandCounts());
176}
177
178void simpleMathOpTest(ANeuralNetworksOperationType operationCode, int32_t operandCode) {
179    uint32_t inputDimensions[4] = {2, 2, 2, 2};
180    ANeuralNetworksOperandType input1 = {.type = operandCode,
181                                         .dimensionCount = 4,
182                                         .dimensions = inputDimensions,
183                                         .scale = 0.0f,
184                                         .zeroPoint = 0};
185    if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
186        input1.scale = 0.5f;
187    }
188
189    ANeuralNetworksOperandType input2 = input1;
190    ANeuralNetworksOperandType output = input1;
191    ANeuralNetworksOperandType activation = {.type = ANEURALNETWORKS_INT32,
192                                             .dimensionCount = 0,
193                                             .dimensions = nullptr,
194                                             .scale = 0.0f,
195                                             .zeroPoint = 0};
196
197    OperationTestBase simpleMathTest(operationCode, {input1, input2, activation}, {output});
198
199    EXPECT_TRUE(simpleMathTest.testMutatingInputOperandCode());
200    EXPECT_TRUE(simpleMathTest.testMutatingInputOperandCounts());
201    EXPECT_TRUE(simpleMathTest.testMutatingOutputOperandCode());
202    EXPECT_TRUE(simpleMathTest.testMutatingOutputOperandCounts());
203}
204
205TEST(OperationValidationTest, ADD_float32) {
206    simpleMathOpTest(ANEURALNETWORKS_ADD, ANEURALNETWORKS_TENSOR_FLOAT32);
207}
208
209TEST(OperationValidationTest, MUL_float32) {
210    simpleMathOpTest(ANEURALNETWORKS_MUL, ANEURALNETWORKS_TENSOR_FLOAT32);
211}
212
213TEST(OperationValidationTest, SUB_float32) {
214    simpleMathOpTest(ANEURALNETWORKS_SUB, ANEURALNETWORKS_TENSOR_FLOAT32);
215}
216
217TEST(OperationValidationTest, DIV_float32) {
218    simpleMathOpTest(ANEURALNETWORKS_DIV, ANEURALNETWORKS_TENSOR_FLOAT32);
219}
220
221TEST(OperationValidationTest, ADD_quant8) {
222    simpleMathOpTest(ANEURALNETWORKS_ADD, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
223}
224
225TEST(OperationValidationTest, MUL_quant8) {
226    simpleMathOpTest(ANEURALNETWORKS_MUL, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
227}
228
229void activationOpTest(ANeuralNetworksOperationType operationCode, int32_t operandCode) {
230    uint32_t inputDimensions[4] = {2, 2, 2, 2};
231    ANeuralNetworksOperandType input = {.type = operandCode,
232                                        .dimensionCount = 4,
233                                        .dimensions = inputDimensions,
234                                        .scale = 0.0f,
235                                        .zeroPoint = 0};
236    if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
237        input.scale = 1.f / 256;
238    }
239
240    ANeuralNetworksOperandType output = input;
241    OperationTestBase activationTest(operationCode, {input}, {output});
242
243    EXPECT_TRUE(activationTest.testMutatingInputOperandCode());
244    EXPECT_TRUE(activationTest.testMutatingInputOperandCounts());
245    EXPECT_TRUE(activationTest.testMutatingOutputOperandCode());
246    EXPECT_TRUE(activationTest.testMutatingOutputOperandCounts());
247}
248
249TEST(OperationValidationTest, L2_NORMALIZATION_float32) {
250    activationOpTest(ANEURALNETWORKS_L2_NORMALIZATION, ANEURALNETWORKS_TENSOR_FLOAT32);
251}
252
253TEST(OperationValidationTest, FLOOR_float32) {
254    activationOpTest(ANEURALNETWORKS_FLOOR, ANEURALNETWORKS_TENSOR_FLOAT32);
255}
256
257TEST(OperationValidationTest, TANH_float32) {
258    activationOpTest(ANEURALNETWORKS_TANH, ANEURALNETWORKS_TENSOR_FLOAT32);
259}
260
261TEST(OperationValidationTest, RELU_float32) {
262    activationOpTest(ANEURALNETWORKS_RELU, ANEURALNETWORKS_TENSOR_FLOAT32);
263}
264
265TEST(OperationValidationTest, RELU1_float32) {
266    activationOpTest(ANEURALNETWORKS_RELU, ANEURALNETWORKS_TENSOR_FLOAT32);
267}
268
269TEST(OperationValidationTest, RELU6_float32) {
270    activationOpTest(ANEURALNETWORKS_RELU, ANEURALNETWORKS_TENSOR_FLOAT32);
271}
272
273TEST(OperationValidationTest, LOGISTIC_float32) {
274    activationOpTest(ANEURALNETWORKS_LOGISTIC, ANEURALNETWORKS_TENSOR_FLOAT32);
275}
276
277TEST(OperationValidationTest, RELU_quant8) {
278    activationOpTest(ANEURALNETWORKS_RELU, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
279}
280
281TEST(OperationValidationTest, RELU1_quant8) {
282    activationOpTest(ANEURALNETWORKS_RELU, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
283}
284
285TEST(OperationValidationTest, RELU6_quant8) {
286    activationOpTest(ANEURALNETWORKS_RELU, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
287}
288
289TEST(OperationValidationTest, LOGISTIC_quant8) {
290    activationOpTest(ANEURALNETWORKS_LOGISTIC, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
291}
292
293void softmaxOpTest(int32_t operandCode) {
294    uint32_t inputDimensions[4] = {2, 2, 2, 2};
295    ANeuralNetworksOperandType input = {.type = operandCode,
296                                         .dimensionCount = 4,
297                                         .dimensions = inputDimensions,
298                                         .scale = 0.0f,
299                                         .zeroPoint = 0};
300    if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
301        input.scale = 1.f / 256;
302    }
303
304    ANeuralNetworksOperandType output = input;
305    ANeuralNetworksOperandType beta = {.type = ANEURALNETWORKS_FLOAT32,
306                                       .dimensionCount = 0,
307                                       .dimensions = nullptr,
308                                       .scale = 0.0f,
309                                       .zeroPoint = 0};
310
311    OperationTestBase softmaxTest(ANEURALNETWORKS_SOFTMAX, {input, beta}, {output});
312
313    EXPECT_TRUE(softmaxTest.testMutatingInputOperandCode());
314    EXPECT_TRUE(softmaxTest.testMutatingInputOperandCounts());
315    EXPECT_TRUE(softmaxTest.testMutatingOutputOperandCode());
316    EXPECT_TRUE(softmaxTest.testMutatingOutputOperandCounts());
317}
318
319TEST(OperationValidationTest, SOFTMAX_float32) {
320    softmaxOpTest(ANEURALNETWORKS_TENSOR_FLOAT32);
321}
322
323TEST(OperationValidationTest, SOFTMAX_quant8) {
324    softmaxOpTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
325}
326
327void poolingOpTest(ANeuralNetworksOperationType operationCode, int32_t operandCode) {
328    uint32_t inputDimensions[4] = {2, 4, 4, 2};
329    ANeuralNetworksOperandType input = {.type = operandCode,
330                                        .dimensionCount = 4,
331                                        .dimensions = inputDimensions,
332                                        .scale = 0.0f,
333                                        .zeroPoint = 0};
334    if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
335        input.scale = 1.f / 256;
336    }
337    ANeuralNetworksOperandType output = input;
338
339    ANeuralNetworksOperandType scalar = {.type = ANEURALNETWORKS_INT32,
340                                         .dimensionCount = 0,
341                                         .dimensions = nullptr,
342                                         .scale = 0.0f,
343                                         .zeroPoint = 0};
344    ANeuralNetworksOperandType padLeft = scalar;
345    ANeuralNetworksOperandType padRight = scalar;
346    ANeuralNetworksOperandType padTop = scalar;
347    ANeuralNetworksOperandType padBottom = scalar;
348    ANeuralNetworksOperandType strideWidth = scalar;
349    ANeuralNetworksOperandType strideHeight = scalar;
350    ANeuralNetworksOperandType filterWidth = scalar;
351    ANeuralNetworksOperandType filterHeight = scalar;
352    ANeuralNetworksOperandType activation = scalar;
353
354    OperationTestBase explicitPoolingTest(operationCode,
355                                          {input,
356                                           padLeft, padRight, padTop, padBottom,
357                                           strideWidth, strideHeight,
358                                           filterWidth, filterHeight,
359                                           activation},
360                                          {output});
361
362    EXPECT_TRUE(explicitPoolingTest.testMutatingInputOperandCode());
363    EXPECT_TRUE(explicitPoolingTest.testMutatingInputOperandCounts());
364    EXPECT_TRUE(explicitPoolingTest.testMutatingOutputOperandCode());
365    EXPECT_TRUE(explicitPoolingTest.testMutatingOutputOperandCounts());
366
367    ANeuralNetworksOperandType padImplicit = scalar;
368    OperationTestBase implicitPoolingTest(operationCode,
369                                          {input,
370                                           padImplicit,
371                                           strideWidth, strideHeight,
372                                           filterWidth, filterHeight,
373                                           activation},
374                                          {output});
375
376    EXPECT_TRUE(implicitPoolingTest.testMutatingInputOperandCode());
377    EXPECT_TRUE(implicitPoolingTest.testMutatingInputOperandCounts());
378    EXPECT_TRUE(implicitPoolingTest.testMutatingOutputOperandCode());
379    EXPECT_TRUE(implicitPoolingTest.testMutatingOutputOperandCounts());
380}
381
382TEST(OperationValidationTest, AVERAGE_POOL_2D_float32) {
383    poolingOpTest(ANEURALNETWORKS_AVERAGE_POOL_2D, ANEURALNETWORKS_TENSOR_FLOAT32);
384}
385
386TEST(OperationValidationTest, MAX_POOL_2D_float32) {
387    poolingOpTest(ANEURALNETWORKS_MAX_POOL_2D, ANEURALNETWORKS_TENSOR_FLOAT32);
388}
389
390TEST(OperationValidationTest, L2_POOL_2D_float32) {
391    poolingOpTest(ANEURALNETWORKS_L2_POOL_2D, ANEURALNETWORKS_TENSOR_FLOAT32);
392}
393
394TEST(OperationValidationTest, AVERAGE_POOL_2D_quant8) {
395    poolingOpTest(ANEURALNETWORKS_AVERAGE_POOL_2D, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
396}
397
398TEST(OperationValidationTest, MAX_POOL_2D_quant8) {
399    poolingOpTest(ANEURALNETWORKS_MAX_POOL_2D, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
400}
401
402void spaceDepthOpTest(ANeuralNetworksOperationType operationCode, int32_t operandCode) {
403    uint32_t inputDimensions[4] = {2, 2, 2, 2};
404    ANeuralNetworksOperandType input = {.type = operandCode,
405                                        .dimensionCount = 4,
406                                        .dimensions = inputDimensions,
407                                        .scale = 0.0f,
408                                        .zeroPoint = 0};
409    if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
410        input.scale = 1.f / 256;
411    }
412
413    ANeuralNetworksOperandType block_size = {.type = ANEURALNETWORKS_INT32,
414                                             .dimensionCount = 0,
415                                             .dimensions = nullptr,
416                                             .scale = 0.0f,
417                                             .zeroPoint = 0};
418
419    ANeuralNetworksOperandType output = input;
420    OperationTestBase spaceDepthTest(operationCode, {input, block_size}, {output});
421
422    EXPECT_TRUE(spaceDepthTest.testMutatingInputOperandCode());
423    EXPECT_TRUE(spaceDepthTest.testMutatingInputOperandCounts());
424    EXPECT_TRUE(spaceDepthTest.testMutatingOutputOperandCode());
425    EXPECT_TRUE(spaceDepthTest.testMutatingOutputOperandCounts());
426}
427
428TEST(OperationValidationTest, SPACE_TO_DEPTH_float32) {
429    spaceDepthOpTest(ANEURALNETWORKS_SPACE_TO_DEPTH, ANEURALNETWORKS_TENSOR_FLOAT32);
430}
431
432TEST(OperationValidationTest, DEPTH_TO_SPACE_float32) {
433    spaceDepthOpTest(ANEURALNETWORKS_DEPTH_TO_SPACE, ANEURALNETWORKS_TENSOR_FLOAT32);
434}
435
436TEST(OperationValidationTest, SPACE_TO_DEPTH_quant8) {
437    spaceDepthOpTest(ANEURALNETWORKS_SPACE_TO_DEPTH, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
438}
439
440TEST(OperationValidationTest, DEPTH_TO_SPACE_quant8) {
441    spaceDepthOpTest(ANEURALNETWORKS_DEPTH_TO_SPACE, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
442}
443
444void spaceBatchOpTest(ANeuralNetworksOperationType operationCode, int32_t operandCode) {
445    uint32_t inputDimensions[4] = {2, 2, 2, 2};
446    ANeuralNetworksOperandType input = {.type = operandCode,
447                                        .dimensionCount = 4,
448                                        .dimensions = inputDimensions,
449                                        .scale = 0.0f,
450                                        .zeroPoint = 0};
451    if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
452        input.scale = 1.f / 256;
453    }
454
455    uint32_t blockDimensions[1] = {2};
456    ANeuralNetworksOperandType blockShape = {.type = ANEURALNETWORKS_TENSOR_INT32,
457                                             .dimensionCount = 1,
458                                             .dimensions = blockDimensions,
459                                             .scale = 0.0f,
460                                             .zeroPoint = 0};
461
462    ANeuralNetworksOperandType padding = blockShape;
463    ANeuralNetworksOperandType output = input;
464    if (operationCode == ANEURALNETWORKS_SPACE_TO_BATCH_ND) {
465        OperationTestBase spaceBatchTest(operationCode, {input, blockShape, padding}, {output});
466
467        EXPECT_TRUE(spaceBatchTest.testMutatingInputOperandCode());
468        EXPECT_TRUE(spaceBatchTest.testMutatingInputOperandCounts());
469        EXPECT_TRUE(spaceBatchTest.testMutatingOutputOperandCode());
470        EXPECT_TRUE(spaceBatchTest.testMutatingOutputOperandCounts());
471    } else {
472        OperationTestBase spaceBatchTest(operationCode, {input, blockShape}, {output});
473
474        EXPECT_TRUE(spaceBatchTest.testMutatingInputOperandCode());
475        EXPECT_TRUE(spaceBatchTest.testMutatingInputOperandCounts());
476        EXPECT_TRUE(spaceBatchTest.testMutatingOutputOperandCode());
477        EXPECT_TRUE(spaceBatchTest.testMutatingOutputOperandCounts());
478    }
479}
480
481TEST(OperationValidationTest, SPACE_TO_BATCH_ND_float32) {
482    spaceBatchOpTest(ANEURALNETWORKS_SPACE_TO_BATCH_ND, ANEURALNETWORKS_TENSOR_FLOAT32);
483}
484
485TEST(OperationValidationTest, BATCH_TO_SPACE_ND_float32) {
486    spaceBatchOpTest(ANEURALNETWORKS_BATCH_TO_SPACE_ND, ANEURALNETWORKS_TENSOR_FLOAT32);
487}
488
489TEST(OperationValidationTest, SPACE_TO_BATCH_ND_quant8) {
490    spaceBatchOpTest(ANEURALNETWORKS_SPACE_TO_BATCH_ND, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
491}
492
493TEST(OperationValidationTest, BATCH_TO_SPACE_ND_quant8) {
494    spaceBatchOpTest(ANEURALNETWORKS_BATCH_TO_SPACE_ND, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
495}
496
497void transposeAndSqueezeOpTest(ANeuralNetworksOperationType operationCode, int32_t operandCode) {
498    uint32_t inputDimensions[4] = {2, 2, 2, 2};
499    ANeuralNetworksOperandType input = {.type = operandCode,
500                                        .dimensionCount = 4,
501                                        .dimensions = inputDimensions,
502                                        .scale = 0.0f,
503                                        .zeroPoint = 0};
504    if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
505        input.scale = 1.f / 256;
506    }
507
508    uint32_t blockDimensions[1] = {4};
509    ANeuralNetworksOperandType dims = {.type = ANEURALNETWORKS_TENSOR_INT32,
510                                       .dimensionCount = 1,
511                                       .dimensions = blockDimensions,
512                                       .scale = 0.0f,
513                                       .zeroPoint = 0};
514
515    ANeuralNetworksOperandType output = input;
516    OperationTestBase transposeAndSqueezeTest(operationCode, {input, dims}, {output});
517
518    EXPECT_TRUE(transposeAndSqueezeTest.testMutatingInputOperandCode());
519    EXPECT_TRUE(transposeAndSqueezeTest.testMutatingInputOperandCounts());
520    EXPECT_TRUE(transposeAndSqueezeTest.testMutatingOutputOperandCode());
521    EXPECT_TRUE(transposeAndSqueezeTest.testMutatingOutputOperandCounts());
522}
523
524TEST(OperationValidationTest, TRANSPOSE_float32) {
525    transposeAndSqueezeOpTest(ANEURALNETWORKS_TRANSPOSE, ANEURALNETWORKS_TENSOR_FLOAT32);
526}
527
528TEST(OperationValidationTest, SQUEEZE_float32) {
529    transposeAndSqueezeOpTest(ANEURALNETWORKS_SQUEEZE, ANEURALNETWORKS_TENSOR_FLOAT32);
530}
531
532TEST(OperationValidationTest, TRANSPOSE_quant8) {
533    transposeAndSqueezeOpTest(ANEURALNETWORKS_TRANSPOSE, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
534}
535
536TEST(OperationValidationTest, SQUEEZE_quant8) {
537    transposeAndSqueezeOpTest(ANEURALNETWORKS_SQUEEZE, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
538}
539
540void convOpTest(int32_t operandCode) {
541    uint32_t inputDimensions[4] = {2, 4, 4, 2};
542    ANeuralNetworksOperandType input = {.type = operandCode,
543                                        .dimensionCount = 4,
544                                        .dimensions = inputDimensions,
545                                        .scale = 0.0f,
546                                        .zeroPoint = 0};
547    if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
548        input.scale = 0.5f;
549    }
550
551    ANeuralNetworksOperandType filter = input;
552    ANeuralNetworksOperandType output = input;
553
554    uint32_t biasDimensions[1] = {2};
555    ANeuralNetworksOperandType bias = {.type = operandCode,
556                                       .dimensionCount = 1,
557                                       .dimensions = biasDimensions,
558                                       .scale = 0.0f,
559                                       .zeroPoint = 0};
560    if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
561        bias.type = ANEURALNETWORKS_TENSOR_INT32;
562        bias.scale = 0.25f;
563    }
564
565    ANeuralNetworksOperandType scalar = {.type = ANEURALNETWORKS_INT32,
566                                         .dimensionCount = 0,
567                                         .dimensions = nullptr,
568                                         .scale = 0.0f,
569                                         .zeroPoint = 0};
570    ANeuralNetworksOperandType padLeft = scalar;
571    ANeuralNetworksOperandType padRight = scalar;
572    ANeuralNetworksOperandType padTop = scalar;
573    ANeuralNetworksOperandType padBottom = scalar;
574    ANeuralNetworksOperandType strideWidth = scalar;
575    ANeuralNetworksOperandType strideHeight = scalar;
576    ANeuralNetworksOperandType activation = scalar;
577
578    OperationTestBase explicitConvTest(ANEURALNETWORKS_CONV_2D,
579                                       {input, filter, bias,
580                                        padLeft, padRight, padTop, padBottom,
581                                        strideWidth, strideHeight,
582                                        activation},
583                                       {output});
584
585    EXPECT_TRUE(explicitConvTest.testMutatingInputOperandCode());
586    EXPECT_TRUE(explicitConvTest.testMutatingInputOperandCounts());
587    EXPECT_TRUE(explicitConvTest.testMutatingOutputOperandCode());
588    EXPECT_TRUE(explicitConvTest.testMutatingOutputOperandCounts());
589
590    ANeuralNetworksOperandType padImplicit = scalar;
591    OperationTestBase implicitConvTest(ANEURALNETWORKS_CONV_2D,
592                                       {input, filter, bias,
593                                        padImplicit,
594                                        strideWidth, strideHeight,
595                                        activation},
596                                       {output});
597
598    EXPECT_TRUE(implicitConvTest.testMutatingInputOperandCode());
599    EXPECT_TRUE(implicitConvTest.testMutatingInputOperandCounts());
600    EXPECT_TRUE(implicitConvTest.testMutatingOutputOperandCode());
601    EXPECT_TRUE(implicitConvTest.testMutatingOutputOperandCounts());
602}
603
604TEST(OperationValidationTest, CONV_2D_float32) {
605    convOpTest(ANEURALNETWORKS_TENSOR_FLOAT32);
606}
607
608TEST(OperationValidationTest, CONV_2D_quant8) {
609    convOpTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
610}
611
612void depthwiseConvOpTest(int32_t operandCode) {
613    uint32_t inputDimensions[4] = {1, 2, 2, 2};
614    ANeuralNetworksOperandType input = {.type = operandCode,
615                                        .dimensionCount = 4,
616                                        .dimensions = inputDimensions,
617                                        .scale = 0.0f,
618                                        .zeroPoint = 0};
619    if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
620        input.scale = 0.5f;
621    }
622
623    ANeuralNetworksOperandType filter = input;
624    ANeuralNetworksOperandType output = input;
625
626    uint32_t biasDimensions[1] = {2};
627    ANeuralNetworksOperandType bias = {.type = operandCode,
628                                       .dimensionCount = 1,
629                                       .dimensions = biasDimensions,
630                                       .scale = 0.0f,
631                                       .zeroPoint = 0};
632    if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
633        bias.type = ANEURALNETWORKS_TENSOR_INT32;
634        bias.scale = 0.25f;
635    }
636
637    ANeuralNetworksOperandType scalar = {.type = ANEURALNETWORKS_INT32,
638                                         .dimensionCount = 0,
639                                         .dimensions = nullptr,
640                                         .scale = 0.0f,
641                                         .zeroPoint = 0};
642    ANeuralNetworksOperandType padLeft = scalar;
643    ANeuralNetworksOperandType padRight = scalar;
644    ANeuralNetworksOperandType padTop = scalar;
645    ANeuralNetworksOperandType padBottom = scalar;
646    ANeuralNetworksOperandType strideWidth = scalar;
647    ANeuralNetworksOperandType strideHeight = scalar;
648    ANeuralNetworksOperandType multiplier = scalar;
649    ANeuralNetworksOperandType activation = scalar;
650
651    OperationTestBase explicitDepthwiseConvTest(ANEURALNETWORKS_DEPTHWISE_CONV_2D,
652                                                {input, filter, bias,
653                                                 padLeft, padRight, padTop, padBottom,
654                                                 strideWidth, strideHeight,
655                                                 multiplier, activation},
656                                                {output});
657
658    EXPECT_TRUE(explicitDepthwiseConvTest.testMutatingInputOperandCode());
659    EXPECT_TRUE(explicitDepthwiseConvTest.testMutatingInputOperandCounts());
660    EXPECT_TRUE(explicitDepthwiseConvTest.testMutatingOutputOperandCode());
661    EXPECT_TRUE(explicitDepthwiseConvTest.testMutatingOutputOperandCounts());
662
663    ANeuralNetworksOperandType padImplicit = scalar;
664    OperationTestBase implicitDepthwiseConvTest(ANEURALNETWORKS_DEPTHWISE_CONV_2D,
665                                                {input, filter, bias,
666                                                 padImplicit,
667                                                 strideWidth, strideHeight,
668                                                 multiplier, activation},
669                                                {output});
670
671    EXPECT_TRUE(implicitDepthwiseConvTest.testMutatingInputOperandCode());
672    EXPECT_TRUE(implicitDepthwiseConvTest.testMutatingInputOperandCounts());
673    EXPECT_TRUE(implicitDepthwiseConvTest.testMutatingOutputOperandCode());
674    EXPECT_TRUE(implicitDepthwiseConvTest.testMutatingOutputOperandCounts());
675}
676
677TEST(OperationValidationTest, DEPTHWISE_CONV_2D_float32) {
678    depthwiseConvOpTest(ANEURALNETWORKS_TENSOR_FLOAT32);
679}
680
681TEST(OperationValidationTest, DEPTHWISE_CONV_2D_quant8) {
682    depthwiseConvOpTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
683}
684
685void fullyConnectedOpTest(int32_t operandCode) {
686    uint32_t inputDimensions[2] = {5, 5};
687    ANeuralNetworksOperandType input = {.type = operandCode,
688                                        .dimensionCount = 2,
689                                        .dimensions = inputDimensions,
690                                        .scale = 0.0f,
691                                        .zeroPoint = 0};
692    if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
693        input.scale = 0.5f;
694    }
695
696    ANeuralNetworksOperandType weights = input;
697    ANeuralNetworksOperandType output = input;
698
699    uint32_t biasDimensions[1] = {5};
700    ANeuralNetworksOperandType bias = {.type = operandCode,
701                                       .dimensionCount = 1,
702                                       .dimensions = biasDimensions,
703                                       .scale = 0.0f,
704                                       .zeroPoint = 0};
705    if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
706        bias.type = ANEURALNETWORKS_TENSOR_INT32;
707        bias.scale = 0.25f;
708    }
709
710    ANeuralNetworksOperandType activation = {.type = ANEURALNETWORKS_INT32,
711                                             .dimensionCount = 0,
712                                             .dimensions = nullptr,
713                                             .scale = 0.0f,
714                                             .zeroPoint = 0};
715
716    OperationTestBase fullyConnectedTest(ANEURALNETWORKS_FULLY_CONNECTED,
717                                         {input, weights, bias, activation},
718                                         {output});
719
720    EXPECT_TRUE(fullyConnectedTest.testMutatingInputOperandCode());
721    EXPECT_TRUE(fullyConnectedTest.testMutatingInputOperandCounts());
722    EXPECT_TRUE(fullyConnectedTest.testMutatingOutputOperandCode());
723    EXPECT_TRUE(fullyConnectedTest.testMutatingOutputOperandCounts());
724}
725
726TEST(OperationValidationTest, FULLY_CONNECTED_float32) {
727    fullyConnectedOpTest(ANEURALNETWORKS_TENSOR_FLOAT32);
728}
729
730TEST(OperationValidationTest, FULLY_CONNECTED_quant8) {
731    fullyConnectedOpTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
732}
733
734void concatenationTest(int32_t operandCode) {
735    uint32_t inputDimensions[2] = {5, 5};
736    ANeuralNetworksOperandType input1 = {.type = operandCode,
737                                        .dimensionCount = 2,
738                                        .dimensions = inputDimensions,
739                                        .scale = 0.0f,
740                                        .zeroPoint = 0};
741    if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
742        input1.scale = 0.5f;
743    }
744    ANeuralNetworksOperandType input2 = input1;
745    ANeuralNetworksOperandType output = input1;
746
747    ANeuralNetworksOperandType activation = {.type = ANEURALNETWORKS_INT32,
748                                             .dimensionCount = 0,
749                                             .dimensions = nullptr,
750                                             .scale = 0.0f,
751                                             .zeroPoint = 0};
752
753    OperationTestBase concat2Test(ANEURALNETWORKS_CONCATENATION,
754                                  {input1, input2, activation}, {output});
755
756    EXPECT_TRUE(concat2Test.testMutatingInputOperandCode());
757    EXPECT_TRUE(concat2Test.testMutatingOutputOperandCode());
758    EXPECT_TRUE(concat2Test.testMutatingOutputOperandCounts());
759
760    OperationTestBase concat1Test(ANEURALNETWORKS_CONCATENATION,
761                                  {input1, activation}, {output});
762
763    EXPECT_TRUE(concat1Test.testMutatingInputOperandCode());
764    EXPECT_TRUE(concat1Test.testMutatingOutputOperandCode());
765    EXPECT_TRUE(concat1Test.testMutatingOutputOperandCounts());
766}
767
768TEST(OperationValidationTest, CONCATENATION_float32) {
769    concatenationTest(ANEURALNETWORKS_TENSOR_FLOAT32);
770}
771
772TEST(OperationValidationTest, CONCATENATION_quant8) {
773    concatenationTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
774}
775
776TEST(OperationValidationTest, RESIZE_BILINEAR_float32) {
777    uint32_t inputDimensions[4] = {2, 2, 2, 2};
778    ANeuralNetworksOperandType input = {.type = ANEURALNETWORKS_TENSOR_FLOAT32,
779                                        .dimensionCount = 4,
780                                        .dimensions = inputDimensions,
781                                        .scale = 0.0f,
782                                        .zeroPoint = 0};
783    ANeuralNetworksOperandType height = {.type = ANEURALNETWORKS_INT32,
784                                         .dimensionCount = 0,
785                                         .dimensions = nullptr,
786                                         .scale = 0.0f,
787                                         .zeroPoint = 0};
788    ANeuralNetworksOperandType width = height;
789    ANeuralNetworksOperandType output = input;
790    OperationTestBase resizeTest(ANEURALNETWORKS_RESIZE_BILINEAR,
791                                 {input, height, width}, {output});
792
793    EXPECT_TRUE(resizeTest.testMutatingInputOperandCode());
794    EXPECT_TRUE(resizeTest.testMutatingInputOperandCounts());
795    EXPECT_TRUE(resizeTest.testMutatingOutputOperandCode());
796    EXPECT_TRUE(resizeTest.testMutatingOutputOperandCounts());
797}
798
799void embeddingLookupTest(int32_t operandCode) {
800    uint32_t lookupDimensions[1] = {5};
801    ANeuralNetworksOperandType lookup = {.type = ANEURALNETWORKS_TENSOR_INT32,
802                                         .dimensionCount = 1,
803                                         .dimensions = lookupDimensions,
804                                         .scale = 0.0f,
805                                         .zeroPoint = 0};
806
807    uint32_t inputDimensions[2] = {5, 5};
808    ANeuralNetworksOperandType input = {.type = operandCode,
809                                        .dimensionCount = 2,
810                                        .dimensions = inputDimensions,
811                                        .scale = 0.0f,
812                                        .zeroPoint = 0};
813    if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
814        input.scale = 0.5f;
815    }
816    ANeuralNetworksOperandType output = input;
817
818    OperationTestBase embedLookupTest(ANEURALNETWORKS_EMBEDDING_LOOKUP,
819                                      {lookup, input}, {output});
820
821    EXPECT_TRUE(embedLookupTest.testMutatingInputOperandCode());
822    EXPECT_TRUE(embedLookupTest.testMutatingInputOperandCounts());
823    EXPECT_TRUE(embedLookupTest.testMutatingOutputOperandCode());
824    EXPECT_TRUE(embedLookupTest.testMutatingOutputOperandCounts());
825}
826
827TEST(OperationValidationTest, EMBEDDING_LOOKUP_float32) {
828    embeddingLookupTest(ANEURALNETWORKS_TENSOR_FLOAT32);
829}
830
831TEST(OperationValidationTest, EMBEDDING_LOOKUP_quant8) {
832    embeddingLookupTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
833}
834
835void hashtableLookupTest(int32_t operandCode) {
836    uint32_t lookupDimensions[1] = {5};
837    ANeuralNetworksOperandType lookup = {.type = ANEURALNETWORKS_TENSOR_INT32,
838                                         .dimensionCount = 1,
839                                         .dimensions = lookupDimensions,
840                                         .scale = 0.0f,
841                                         .zeroPoint = 0};
842    ANeuralNetworksOperandType keys = lookup;
843
844    uint32_t valuesDimensions[2] = {5, 5};
845    ANeuralNetworksOperandType values = {.type = operandCode,
846                                         .dimensionCount = 2,
847                                         .dimensions = valuesDimensions,
848                                         .scale = 0.0f,
849                                         .zeroPoint = 0};
850    if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
851        values.scale = 0.5f;
852    }
853    ANeuralNetworksOperandType output = values;
854
855    ANeuralNetworksOperandType hits = lookup;
856    hits.type = ANEURALNETWORKS_TENSOR_QUANT8_ASYMM;
857    hits.scale = 1.0f;
858
859    OperationTestBase hashLookupTest(ANEURALNETWORKS_HASHTABLE_LOOKUP,
860                                      {lookup, keys, values}, {output, hits});
861
862    EXPECT_TRUE(hashLookupTest.testMutatingInputOperandCode());
863    EXPECT_TRUE(hashLookupTest.testMutatingInputOperandCounts());
864    EXPECT_TRUE(hashLookupTest.testMutatingOutputOperandCode());
865    EXPECT_TRUE(hashLookupTest.testMutatingOutputOperandCounts());
866}
867
868TEST(OperationValidationTest, HASHTABLE_LOOKUP_float32) {
869    hashtableLookupTest(ANEURALNETWORKS_TENSOR_FLOAT32);
870}
871
872TEST(OperationValidationTest, HASHTABLE_LOOKUP_quant8) {
873    hashtableLookupTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
874}
875
876void lshProjectionTest(int32_t operandCode) {
877    uint32_t inputDimensions[2] = {5, 5};
878    ANeuralNetworksOperandType hash = {.type = ANEURALNETWORKS_TENSOR_FLOAT32,
879                                       .dimensionCount = 2,
880                                       .dimensions = inputDimensions,
881                                       .scale = 0.0f,
882                                       .zeroPoint = 0};
883
884    ANeuralNetworksOperandType input = hash;
885    input.type = operandCode;
886    if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
887        input.scale = 0.5f;
888    }
889
890    uint32_t weightDimensions[1] = {5};
891    ANeuralNetworksOperandType weight = {.type = ANEURALNETWORKS_TENSOR_FLOAT32,
892                                         .dimensionCount = 1,
893                                         .dimensions = weightDimensions,
894                                         .scale = 0.0f,
895                                         .zeroPoint = 0};
896
897    ANeuralNetworksOperandType type = {.type = ANEURALNETWORKS_INT32,
898                                       .dimensionCount = 0,
899                                       .dimensions = nullptr,
900                                       .scale = 0.0f,
901                                       .zeroPoint = 0};
902
903    ANeuralNetworksOperandType output = weight;
904    output.type = ANEURALNETWORKS_TENSOR_INT32;
905
906    OperationTestBase lshProjTest(ANEURALNETWORKS_LSH_PROJECTION,
907                                  {hash, input, weight, type}, {output});
908
909    EXPECT_TRUE(lshProjTest.testMutatingInputOperandCode());
910    EXPECT_TRUE(lshProjTest.testMutatingInputOperandCounts());
911    EXPECT_TRUE(lshProjTest.testMutatingOutputOperandCode());
912    EXPECT_TRUE(lshProjTest.testMutatingOutputOperandCounts());
913}
914
915TEST(OperationValidationTest, LSH_PROJECTION_float32) {
916    lshProjectionTest(ANEURALNETWORKS_TENSOR_FLOAT32);
917}
918
919TEST(OperationValidationTest, LSH_PROJECTION_quant8) {
920    lshProjectionTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
921}
922
923TEST(OperationValidationTest, LSTM_float32) {
924    uint32_t oneDimensional[1] = {5};
925    uint32_t twoDimensional[2] = {5, 5};
926    ANeuralNetworksOperandType floatTensor1D = {.type = ANEURALNETWORKS_TENSOR_FLOAT32,
927                                                .dimensionCount = 1,
928                                                .dimensions = oneDimensional,
929                                                .scale = 0.0f,
930                                                .zeroPoint = 0};
931    ANeuralNetworksOperandType floatTensor2D = {.type = ANEURALNETWORKS_TENSOR_FLOAT32,
932                                                .dimensionCount = 2,
933                                                .dimensions = twoDimensional,
934                                                .scale = 0.0f,
935                                                .zeroPoint = 0};
936    ANeuralNetworksOperandType intScalar = {.type = ANEURALNETWORKS_INT32,
937                                            .dimensionCount = 0,
938                                            .dimensions = nullptr,
939                                            .scale = 0.0f,
940                                            .zeroPoint = 0};
941    ANeuralNetworksOperandType floatScalar = {.type = ANEURALNETWORKS_FLOAT32,
942                                              .dimensionCount = 0,
943                                              .dimensions = nullptr,
944                                              .scale = 0.0f,
945                                              .zeroPoint = 0};
946
947    ANeuralNetworksOperandType input = floatTensor2D;
948    ANeuralNetworksOperandType inputToInput = floatTensor2D;
949    ANeuralNetworksOperandType inputToForget = floatTensor2D;
950    ANeuralNetworksOperandType inputToCell = floatTensor2D;
951    ANeuralNetworksOperandType inputToOutput = floatTensor2D;
952    ANeuralNetworksOperandType recurrentToInput = floatTensor2D;
953    ANeuralNetworksOperandType recurrentToForget = floatTensor2D;
954    ANeuralNetworksOperandType recurrentToCell = floatTensor2D;
955    ANeuralNetworksOperandType recurrentToOutput = floatTensor2D;
956    ANeuralNetworksOperandType cellToInput = floatTensor1D;
957    ANeuralNetworksOperandType cellToForget = floatTensor1D;
958    ANeuralNetworksOperandType cellToOutput = floatTensor1D;
959    ANeuralNetworksOperandType inputGateBias = floatTensor1D;
960    ANeuralNetworksOperandType forgetGateBias = floatTensor1D;
961    ANeuralNetworksOperandType cellBias = floatTensor1D;
962    ANeuralNetworksOperandType outputGateBias = floatTensor1D;
963    ANeuralNetworksOperandType projWeights = floatTensor2D;
964    ANeuralNetworksOperandType projBias = floatTensor1D;
965    ANeuralNetworksOperandType outputStateIn = floatTensor2D;
966    ANeuralNetworksOperandType cellStateIn = floatTensor2D;
967    ANeuralNetworksOperandType activation = intScalar;
968    ANeuralNetworksOperandType clipCellState = floatScalar;
969    ANeuralNetworksOperandType clipProjLayer = floatScalar;
970
971    ANeuralNetworksOperandType scratch = floatTensor2D;
972    ANeuralNetworksOperandType outputStateOut = floatTensor2D;
973    ANeuralNetworksOperandType cellStateOut = floatTensor2D;
974    ANeuralNetworksOperandType output = floatTensor2D;
975
976    OperationTestBase lstmTest(ANEURALNETWORKS_LSTM,
977        {input, inputToInput, inputToForget, inputToCell, inputToOutput, recurrentToInput,
978         recurrentToForget, recurrentToCell, recurrentToOutput, cellToInput, cellToForget,
979         cellToOutput, inputGateBias, forgetGateBias, cellBias, outputGateBias, projWeights,
980         projBias, outputStateIn, cellStateIn, activation, clipCellState, clipProjLayer},
981        {scratch, outputStateOut, cellStateOut, output});
982
983    EXPECT_TRUE(lstmTest.testMutatingInputOperandCode());
984    EXPECT_TRUE(lstmTest.testMutatingInputOperandCounts());
985    EXPECT_TRUE(lstmTest.testMutatingOutputOperandCode());
986    EXPECT_TRUE(lstmTest.testMutatingOutputOperandCounts());
987}
988
989TEST(OperationValidationTest, RNN_float32) {
990    uint32_t oneDimensional[1] = {5};
991    uint32_t twoDimensional[2] = {5, 5};
992    ANeuralNetworksOperandType floatTensor1D = {.type = ANEURALNETWORKS_TENSOR_FLOAT32,
993                                                .dimensionCount = 1,
994                                                .dimensions = oneDimensional,
995                                                .scale = 0.0f,
996                                                .zeroPoint = 0};
997    ANeuralNetworksOperandType floatTensor2D = {.type = ANEURALNETWORKS_TENSOR_FLOAT32,
998                                                .dimensionCount = 2,
999                                                .dimensions = twoDimensional,
1000                                                .scale = 0.0f,
1001                                                .zeroPoint = 0};
1002    ANeuralNetworksOperandType intScalar = {.type = ANEURALNETWORKS_INT32,
1003                                            .dimensionCount = 0,
1004                                            .dimensions = nullptr,
1005                                            .scale = 0.0f,
1006                                            .zeroPoint = 0};
1007
1008    ANeuralNetworksOperandType input = floatTensor2D;
1009    ANeuralNetworksOperandType weights = floatTensor2D;
1010    ANeuralNetworksOperandType recurrentWeights = floatTensor2D;
1011    ANeuralNetworksOperandType bias = floatTensor1D;
1012    ANeuralNetworksOperandType hiddenStateIn = floatTensor2D;
1013    ANeuralNetworksOperandType activation = intScalar;
1014
1015    ANeuralNetworksOperandType hiddenStateOut = floatTensor2D;
1016    ANeuralNetworksOperandType output = floatTensor2D;
1017
1018    OperationTestBase rnnTest(ANEURALNETWORKS_RNN,
1019                              {input, weights, recurrentWeights, bias, hiddenStateIn, activation},
1020                              {hiddenStateOut, output});
1021
1022    EXPECT_TRUE(rnnTest.testMutatingInputOperandCode());
1023    EXPECT_TRUE(rnnTest.testMutatingInputOperandCounts());
1024    EXPECT_TRUE(rnnTest.testMutatingOutputOperandCode());
1025    EXPECT_TRUE(rnnTest.testMutatingOutputOperandCounts());
1026}
1027
1028TEST(OperationValidationTest, SVDF_float32) {
1029    uint32_t oneDimensional[1] = {5};
1030    uint32_t twoDimensional[2] = {5, 5};
1031    ANeuralNetworksOperandType floatTensor1D = {.type = ANEURALNETWORKS_TENSOR_FLOAT32,
1032                                                .dimensionCount = 1,
1033                                                .dimensions = oneDimensional,
1034                                                .scale = 0.0f,
1035                                                .zeroPoint = 0};
1036    ANeuralNetworksOperandType floatTensor2D = {.type = ANEURALNETWORKS_TENSOR_FLOAT32,
1037                                                .dimensionCount = 2,
1038                                                .dimensions = twoDimensional,
1039                                                .scale = 0.0f,
1040                                                .zeroPoint = 0};
1041    ANeuralNetworksOperandType intScalar = {.type = ANEURALNETWORKS_INT32,
1042                                            .dimensionCount = 0,
1043                                            .dimensions = nullptr,
1044                                            .scale = 0.0f,
1045                                            .zeroPoint = 0};
1046
1047    ANeuralNetworksOperandType input = floatTensor2D;
1048    ANeuralNetworksOperandType weightsFeature = floatTensor2D;
1049    ANeuralNetworksOperandType weightsTime = floatTensor2D;
1050    ANeuralNetworksOperandType bias = floatTensor1D;
1051    ANeuralNetworksOperandType stateIn = floatTensor2D;
1052    ANeuralNetworksOperandType rank = intScalar;
1053    ANeuralNetworksOperandType activation = intScalar;
1054
1055    ANeuralNetworksOperandType stateOut = floatTensor2D;
1056    ANeuralNetworksOperandType output = floatTensor2D;
1057
1058    OperationTestBase svdfTest(ANEURALNETWORKS_SVDF,
1059        {input, weightsFeature, weightsTime, bias, stateIn, rank, activation},
1060        {stateOut, output});
1061
1062    EXPECT_TRUE(svdfTest.testMutatingInputOperandCode());
1063    EXPECT_TRUE(svdfTest.testMutatingInputOperandCounts());
1064    EXPECT_TRUE(svdfTest.testMutatingOutputOperandCode());
1065    EXPECT_TRUE(svdfTest.testMutatingOutputOperandCounts());
1066}
1067
1068void stridedSliceOpTest(int32_t operandCode) {
1069    uint32_t inputDimensions[2] = {5, 5};
1070    ANeuralNetworksOperandType input = {.type = operandCode,
1071                                        .dimensionCount = 2,
1072                                        .dimensions = inputDimensions,
1073                                        .scale = 0.0f,
1074                                        .zeroPoint = 0};
1075    if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
1076        input.scale = 0.5f;
1077    }
1078    ANeuralNetworksOperandType output = input;
1079
1080    uint32_t beginsDimensions[1] = {2};
1081    ANeuralNetworksOperandType begins = {.type = ANEURALNETWORKS_TENSOR_INT32,
1082                                         .dimensionCount = 1,
1083                                         .dimensions = beginsDimensions,
1084                                         .scale = 0.0f,
1085                                         .zeroPoint = 0};
1086
1087    ANeuralNetworksOperandType ends = begins;
1088    ANeuralNetworksOperandType strides = begins;
1089
1090    ANeuralNetworksOperandType beginMask = {.type = ANEURALNETWORKS_INT32,
1091                                            .dimensionCount = 0,
1092                                            .dimensions = nullptr,
1093                                            .scale = 0.0f,
1094                                            .zeroPoint = 0};
1095    ANeuralNetworksOperandType endMask = beginMask;
1096    ANeuralNetworksOperandType shrinkAxisMask = beginMask;
1097
1098    OperationTestBase stridedSliceTest(ANEURALNETWORKS_STRIDED_SLICE,
1099                                       {input, begins, ends, strides,
1100                                        beginMask, endMask, shrinkAxisMask},
1101                                       {output});
1102
1103    EXPECT_TRUE(stridedSliceTest.testMutatingInputOperandCode());
1104    EXPECT_TRUE(stridedSliceTest.testMutatingInputOperandCounts());
1105    EXPECT_TRUE(stridedSliceTest.testMutatingOutputOperandCode());
1106    EXPECT_TRUE(stridedSliceTest.testMutatingOutputOperandCounts());
1107}
1108
1109TEST(OperationValidationTest, STRIDED_SLICE_float32) {
1110    stridedSliceOpTest(ANEURALNETWORKS_TENSOR_FLOAT32);
1111}
1112
1113TEST(OperationValidationTest, STRIDED_SLICE_quant8) {
1114    stridedSliceOpTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
1115}
1116
1117}  // end namespace
1118