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