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 "NeuralNetworks.h"
18 #include "NeuralNetworksOEM.h"
19 #include "NeuralNetworksWrapper.h"
20 #ifndef NNTEST_ONLY_PUBLIC_API
21 #include "Utils.h"
22 #endif
23 
24 #include <gtest/gtest.h>
25 
26 namespace {
27 
28 using namespace android::nn::wrapper;
29 
30 class OperandExtraParamsTest : public ::testing::Test {
31    protected:
SetUp()32     virtual void SetUp() {
33         ::testing::Test::SetUp();
34         ASSERT_EQ(ANeuralNetworksModel_create(&mModel), ANEURALNETWORKS_NO_ERROR);
35         nextOperandIndex = 0;
36     }
TearDown()37     virtual void TearDown() {
38         ANeuralNetworksModel_free(mModel);
39         ::testing::Test::TearDown();
40     }
41 
42     static const uint32_t CHANNEL_DIM_SIZE = 4;
43 
createOperand(int32_t dataType)44     ANeuralNetworksOperandType createOperand(int32_t dataType) {
45         static uint32_t dims[4] = {1, 2, 3, CHANNEL_DIM_SIZE};
46         switch (dataType) {
47             case ANEURALNETWORKS_FLOAT32:
48             case ANEURALNETWORKS_FLOAT16:
49             case ANEURALNETWORKS_INT32:
50             case ANEURALNETWORKS_UINT32:
51             case ANEURALNETWORKS_BOOL:
52             case ANEURALNETWORKS_MODEL:
53             case ANEURALNETWORKS_OEM_SCALAR:
54                 return {.type = dataType,
55                         .dimensionCount = 0,
56                         .dimensions = nullptr,
57                         .scale = 0.0f,
58                         .zeroPoint = 0};
59             case ANEURALNETWORKS_TENSOR_OEM_BYTE:
60             case ANEURALNETWORKS_TENSOR_FLOAT32:
61             case ANEURALNETWORKS_TENSOR_FLOAT16:
62             case ANEURALNETWORKS_TENSOR_INT32:
63             case ANEURALNETWORKS_TENSOR_BOOL8:
64             case ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL:
65                 return {.type = dataType,
66                         .dimensionCount = 4,
67                         .dimensions = dims,
68                         .scale = 0.0f,
69                         .zeroPoint = 0};
70             case ANEURALNETWORKS_TENSOR_QUANT8_ASYMM:
71                 return {.type = dataType,
72                         .dimensionCount = 4,
73                         .dimensions = dims,
74                         .scale = 1.0,
75                         .zeroPoint = 128};
76             case ANEURALNETWORKS_TENSOR_QUANT8_SYMM:
77                 return {.type = dataType,
78                         .dimensionCount = 4,
79                         .dimensions = dims,
80                         .scale = 1.0,
81                         .zeroPoint = 0};
82             case ANEURALNETWORKS_TENSOR_QUANT16_SYMM:
83                 return {.type = dataType,
84                         .dimensionCount = 4,
85                         .dimensions = dims,
86                         .scale = 1.0,
87                         .zeroPoint = 0};
88             case ANEURALNETWORKS_TENSOR_QUANT16_ASYMM:
89                 return {.type = dataType,
90                         .dimensionCount = 4,
91                         .dimensions = dims,
92                         .scale = 1.0,
93                         .zeroPoint = 32768};
94             case ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED:
95                 return {.type = dataType,
96                         .dimensionCount = 4,
97                         .dimensions = dims,
98                         .scale = 1.0,
99                         .zeroPoint = 1};
100             default:
101                 ADD_FAILURE();
102                 return {};
103         }
104     }
105 
createSymmPerChannelQuantParams()106     ANeuralNetworksSymmPerChannelQuantParams createSymmPerChannelQuantParams() {
107         static float scales[CHANNEL_DIM_SIZE] = {1.0, 2.0, 3.0, 4.0};
108         return {
109                 .channelDim = 3,
110                 .scaleCount = CHANNEL_DIM_SIZE,
111                 .scales = scales,
112         };
113     }
114 
testAddingWithSymmPerChannelQuantParams(int32_t dataType,ANeuralNetworksSymmPerChannelQuantParams params,bool expectExtraParamsSuccess)115     void testAddingWithSymmPerChannelQuantParams(int32_t dataType,
116                                                  ANeuralNetworksSymmPerChannelQuantParams params,
117                                                  bool expectExtraParamsSuccess) {
118         ANeuralNetworksOperandType operandType = createOperand(dataType);
119         EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &operandType), ANEURALNETWORKS_NO_ERROR);
120         int operandIndex = nextOperandIndex++;
121         EXPECT_EQ(ANeuralNetworksModel_setOperandSymmPerChannelQuantParams(mModel, operandIndex,
122                                                                            &params),
123                   expectExtraParamsSuccess ? ANEURALNETWORKS_NO_ERROR : ANEURALNETWORKS_BAD_DATA);
124     }
125 
126     ANeuralNetworksModel* mModel = nullptr;
127     int nextOperandIndex = 0;
128 };
129 
130 const uint32_t kOperandCodeNoExtraParams[]{
131         ANEURALNETWORKS_FLOAT32,
132         ANEURALNETWORKS_FLOAT16,
133         ANEURALNETWORKS_INT32,
134         ANEURALNETWORKS_UINT32,
135         ANEURALNETWORKS_BOOL,
136         ANEURALNETWORKS_OEM_SCALAR,
137         ANEURALNETWORKS_TENSOR_OEM_BYTE,
138         ANEURALNETWORKS_TENSOR_FLOAT32,
139         ANEURALNETWORKS_TENSOR_INT32,
140         ANEURALNETWORKS_TENSOR_QUANT8_ASYMM,
141         ANEURALNETWORKS_TENSOR_QUANT16_ASYMM,
142         ANEURALNETWORKS_TENSOR_QUANT16_SYMM,
143         ANEURALNETWORKS_TENSOR_FLOAT16,
144         ANEURALNETWORKS_TENSOR_BOOL8,
145         ANEURALNETWORKS_TENSOR_QUANT8_SYMM,
146         ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED,
147         ANEURALNETWORKS_MODEL,
148 };
149 
150 #ifndef NNTEST_ONLY_PUBLIC_API
151 // android::nn::k* consts are defined in private headers
152 static_assert(sizeof(kOperandCodeNoExtraParams) / sizeof(kOperandCodeNoExtraParams[0]) ==
153                       android::nn::kNumberOfDataTypes + android::nn::kNumberOfDataTypesOEM - 1,
154               "New type added, OperandExtraParamsTest needs an update");
155 #endif
156 
157 const uint32_t kOperandCodeChannelQuant[]{
158         ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL,
159 };
160 
TEST_F(OperandExtraParamsTest,TestNoExtraParams)161 TEST_F(OperandExtraParamsTest, TestNoExtraParams) {
162     // Test for operands that are expected to not have additonal parameters
163     for (uint32_t dataType : kOperandCodeNoExtraParams) {
164         testAddingWithSymmPerChannelQuantParams(dataType, createSymmPerChannelQuantParams(),
165                                                 /*expectExtraParamsSuccess=*/false);
166     }
167 }
168 
TEST_F(OperandExtraParamsTest,TestChannelQuant)169 TEST_F(OperandExtraParamsTest, TestChannelQuant) {
170     // Test for operands that are expected to have SymmPerChannelQuantParams value associated
171     for (uint32_t dataType : kOperandCodeChannelQuant) {
172         testAddingWithSymmPerChannelQuantParams(dataType, createSymmPerChannelQuantParams(),
173                                                 /*expectExtraParamsSuccess=*/true);
174     }
175 }
176 
TEST_F(OperandExtraParamsTest,TestChannelQuantValuesBadDim)177 TEST_F(OperandExtraParamsTest, TestChannelQuantValuesBadDim) {
178     // Bad .channelDim value
179     static float scales[4] = {1.0, 2.0, 3.0, 4.0};
180     ANeuralNetworksSymmPerChannelQuantParams ext = {
181             .channelDim = 7,
182             .scaleCount = 4,
183             .scales = scales,
184     };
185     for (uint32_t dataType : kOperandCodeChannelQuant) {
186         testAddingWithSymmPerChannelQuantParams(dataType, ext, /*expectExtraParamsSuccess=*/false);
187     }
188 }
189 
TEST_F(OperandExtraParamsTest,TestChannelQuantValuesBadScalesCount)190 TEST_F(OperandExtraParamsTest, TestChannelQuantValuesBadScalesCount) {
191     // Bad .scaleCount value
192     static float scales[4] = {1.0, 2.0, 3.0, 4.0};
193     ANeuralNetworksSymmPerChannelQuantParams lowScaleCountExt = {
194             .channelDim = 3,
195             .scaleCount = 3,
196             .scales = scales,
197     };
198     ANeuralNetworksSymmPerChannelQuantParams highScaleCountExt = {
199             .channelDim = 3,
200             .scaleCount = 10,
201             .scales = scales,
202     };
203 
204     for (uint32_t dataType : kOperandCodeChannelQuant) {
205         testAddingWithSymmPerChannelQuantParams(dataType, lowScaleCountExt,
206                                                 /*expectExtraParamsSuccess=*/false);
207         testAddingWithSymmPerChannelQuantParams(dataType, highScaleCountExt,
208                                                 /*expectExtraParamsSuccess=*/false);
209     }
210 }
211 
TEST_F(OperandExtraParamsTest,TestChannelQuantValuesBadScalesNegative)212 TEST_F(OperandExtraParamsTest, TestChannelQuantValuesBadScalesNegative) {
213     // Bad .scales value
214     static float scales[4] = {1.0, 2.0, -3.0, 4.0};
215     ANeuralNetworksSymmPerChannelQuantParams ext = {
216             .channelDim = 3,
217             .scaleCount = 4,
218             .scales = scales,
219     };
220     for (uint32_t dataType : kOperandCodeChannelQuant) {
221         testAddingWithSymmPerChannelQuantParams(dataType, ext, /*expectExtraParamsSuccess=*/false);
222     }
223 }
224 
TEST_F(OperandExtraParamsTest,TestChannelQuantValuesNullScales)225 TEST_F(OperandExtraParamsTest, TestChannelQuantValuesNullScales) {
226     // .scales == nullptr value
227     ANeuralNetworksSymmPerChannelQuantParams ext = {
228             .channelDim = 3,
229             .scaleCount = 4,
230             .scales = nullptr,
231     };
232     for (uint32_t dataType : kOperandCodeChannelQuant) {
233         testAddingWithSymmPerChannelQuantParams(dataType, ext, /*expectExtraParamsSuccess=*/false);
234     }
235 }
236 
TEST_F(OperandExtraParamsTest,TestChannelQuantValuesOperandScale)237 TEST_F(OperandExtraParamsTest, TestChannelQuantValuesOperandScale) {
238     for (uint32_t dataType : kOperandCodeChannelQuant) {
239         ANeuralNetworksOperandType operandType = createOperand(dataType);
240         operandType.scale = 1.0f;
241         EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &operandType), ANEURALNETWORKS_BAD_DATA);
242     }
243 }
244 
TEST_F(OperandExtraParamsTest,TestChannelQuantValuesOperandZeroPoint)245 TEST_F(OperandExtraParamsTest, TestChannelQuantValuesOperandZeroPoint) {
246     for (uint32_t dataType : kOperandCodeChannelQuant) {
247         ANeuralNetworksOperandType operandType = createOperand(dataType);
248         operandType.zeroPoint = 1;
249         EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &operandType), ANEURALNETWORKS_BAD_DATA);
250     }
251 }
252 
253 }  // namespace
254