blob: e590fdad2d76d998f64932d112c41d6b44bc6ba0 [file] [log] [blame]
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001/*
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#define LOG_TAG "neuralnetworks_hidl_hal_test"
18
19#include "1.0/Utils.h"
Xusong Wangcc47dff2019-10-23 10:35:07 -070020#include "1.3/Callbacks.h"
Michael Butler79a41d72019-12-11 19:08:08 -080021#include "1.3/Utils.h"
Lev Proleev13fdfcd2019-08-30 11:35:34 +010022#include "GeneratedTestHarness.h"
23#include "VtsHalNeuralnetworks.h"
24
Lev Proleev26d1bc82019-08-30 11:57:18 +010025namespace android::hardware::neuralnetworks::V1_3::vts::functional {
Lev Proleev13fdfcd2019-08-30 11:35:34 +010026
Xusong Wangcc47dff2019-10-23 10:35:07 -070027using implementation::PreparedModelCallback;
Lev Proleev13fdfcd2019-08-30 11:35:34 +010028using V1_1::ExecutionPreference;
Lev Proleev26d1bc82019-08-30 11:57:18 +010029using V1_2::SymmPerChannelQuantParams;
Lev Proleev26d1bc82019-08-30 11:57:18 +010030using HidlToken =
31 hidl_array<uint8_t, static_cast<uint32_t>(V1_2::Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
Lev Proleev13fdfcd2019-08-30 11:35:34 +010032
Michael Butler68a6de72020-03-11 18:45:45 -070033using PrepareModelMutation = std::function<void(Model*, ExecutionPreference*, Priority*)>;
34
Lev Proleev13fdfcd2019-08-30 11:35:34 +010035///////////////////////// UTILITY FUNCTIONS /////////////////////////
36
37static void validateGetSupportedOperations(const sp<IDevice>& device, const std::string& message,
38 const Model& model) {
Lev Proleev26d1bc82019-08-30 11:57:18 +010039 SCOPED_TRACE(message + " [getSupportedOperations_1_3]");
Lev Proleev13fdfcd2019-08-30 11:35:34 +010040
Lev Proleev26d1bc82019-08-30 11:57:18 +010041 Return<void> ret = device->getSupportedOperations_1_3(
Lev Proleev13fdfcd2019-08-30 11:35:34 +010042 model, [&](ErrorStatus status, const hidl_vec<bool>&) {
43 EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, status);
44 });
45 EXPECT_TRUE(ret.isOk());
46}
47
48static void validatePrepareModel(const sp<IDevice>& device, const std::string& message,
Michael Butler68a6de72020-03-11 18:45:45 -070049 const Model& model, ExecutionPreference preference,
50 Priority priority) {
Lev Proleev26d1bc82019-08-30 11:57:18 +010051 SCOPED_TRACE(message + " [prepareModel_1_3]");
Lev Proleev13fdfcd2019-08-30 11:35:34 +010052
53 sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
Michael Butler68a6de72020-03-11 18:45:45 -070054 Return<ErrorStatus> prepareLaunchStatus =
55 device->prepareModel_1_3(model, preference, priority, {}, hidl_vec<hidl_handle>(),
56 hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
Lev Proleev13fdfcd2019-08-30 11:35:34 +010057 ASSERT_TRUE(prepareLaunchStatus.isOk());
58 ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(prepareLaunchStatus));
59
60 preparedModelCallback->wait();
61 ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
62 ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, prepareReturnStatus);
Xusong Wang1b3f4262019-10-25 12:07:17 -070063 sp<IPreparedModel> preparedModel = getPreparedModel_1_3(preparedModelCallback);
Lev Proleev13fdfcd2019-08-30 11:35:34 +010064 ASSERT_EQ(nullptr, preparedModel.get());
65}
66
67static bool validExecutionPreference(ExecutionPreference preference) {
68 return preference == ExecutionPreference::LOW_POWER ||
69 preference == ExecutionPreference::FAST_SINGLE_ANSWER ||
70 preference == ExecutionPreference::SUSTAINED_SPEED;
71}
72
Michael Butler68a6de72020-03-11 18:45:45 -070073static bool validExecutionPriority(Priority priority) {
74 return priority == Priority::LOW || priority == Priority::MEDIUM || priority == Priority::HIGH;
75}
76
Lev Proleev13fdfcd2019-08-30 11:35:34 +010077// Primary validation function. This function will take a valid model, apply a
Michael Butler68a6de72020-03-11 18:45:45 -070078// mutation to invalidate the model, the execution preference, or the priority,
79// then pass these to supportedOperations and/or prepareModel if that method is
80// called with an invalid argument.
81static void validate(const sp<IDevice>& device, const std::string& message,
82 const Model& originalModel, const PrepareModelMutation& mutate) {
83 Model model = originalModel;
84 ExecutionPreference preference = ExecutionPreference::FAST_SINGLE_ANSWER;
85 Priority priority = kDefaultPriority;
86 mutate(&model, &preference, &priority);
87
88 if (validExecutionPreference(preference) && validExecutionPriority(priority)) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +010089 validateGetSupportedOperations(device, message, model);
90 }
Michael Butler68a6de72020-03-11 18:45:45 -070091
92 validatePrepareModel(device, message, model, preference, priority);
Lev Proleev13fdfcd2019-08-30 11:35:34 +010093}
94
95static uint32_t addOperand(Model* model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +000096 return hidl_vec_push_back(&model->main.operands,
Lev Proleev13fdfcd2019-08-30 11:35:34 +010097 {
98 .type = OperandType::INT32,
99 .dimensions = {},
100 .numberOfConsumers = 0,
101 .scale = 0.0f,
102 .zeroPoint = 0,
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000103 .lifetime = OperandLifeTime::SUBGRAPH_INPUT,
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100104 .location = {.poolIndex = 0, .offset = 0, .length = 0},
105 });
106}
107
108static uint32_t addOperand(Model* model, OperandLifeTime lifetime) {
109 uint32_t index = addOperand(model);
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000110 model->main.operands[index].numberOfConsumers = 1;
111 model->main.operands[index].lifetime = lifetime;
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100112 return index;
113}
114
115///////////////////////// VALIDATE MODEL OPERAND TYPE /////////////////////////
116
117static const uint32_t invalidOperandTypes[] = {
118 static_cast<uint32_t>(OperandTypeRange::FUNDAMENTAL_MIN) - 1,
119 static_cast<uint32_t>(OperandTypeRange::FUNDAMENTAL_MAX) + 1,
120 static_cast<uint32_t>(OperandTypeRange::OEM_MIN) - 1,
121 static_cast<uint32_t>(OperandTypeRange::OEM_MAX) + 1,
122};
123
124static void mutateOperandTypeTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000125 for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100126 for (uint32_t invalidOperandType : invalidOperandTypes) {
127 const std::string message = "mutateOperandTypeTest: operand " +
128 std::to_string(operand) + " set to value " +
129 std::to_string(invalidOperandType);
Michael Butler68a6de72020-03-11 18:45:45 -0700130 validate(device, message, model,
131 [operand, invalidOperandType](Model* model, ExecutionPreference*, Priority*) {
132 model->main.operands[operand].type =
133 static_cast<OperandType>(invalidOperandType);
134 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100135 }
136 }
137}
138
139///////////////////////// VALIDATE OPERAND RANK /////////////////////////
140
141static uint32_t getInvalidRank(OperandType type) {
142 switch (type) {
143 case OperandType::FLOAT16:
144 case OperandType::FLOAT32:
145 case OperandType::INT32:
146 case OperandType::UINT32:
147 case OperandType::BOOL:
148 return 1;
149 case OperandType::TENSOR_BOOL8:
150 case OperandType::TENSOR_FLOAT16:
151 case OperandType::TENSOR_FLOAT32:
152 case OperandType::TENSOR_INT32:
153 case OperandType::TENSOR_QUANT8_ASYMM:
154 case OperandType::TENSOR_QUANT8_SYMM:
155 case OperandType::TENSOR_QUANT16_ASYMM:
156 case OperandType::TENSOR_QUANT16_SYMM:
157 case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
158 return 0;
159 default:
160 return 0;
161 }
162}
163
164static void mutateOperandRankTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000165 for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
166 const uint32_t invalidRank = getInvalidRank(model.main.operands[operand].type);
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100167 if (invalidRank == 0) {
168 continue;
169 }
170 const std::string message = "mutateOperandRankTest: operand " + std::to_string(operand) +
171 " has rank of " + std::to_string(invalidRank);
Michael Butler68a6de72020-03-11 18:45:45 -0700172 validate(device, message, model,
173 [operand, invalidRank](Model* model, ExecutionPreference*, Priority*) {
174 model->main.operands[operand].dimensions =
175 std::vector<uint32_t>(invalidRank, 0);
176 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100177 }
178}
179
180///////////////////////// VALIDATE OPERAND SCALE /////////////////////////
181
182static float getInvalidScale(OperandType type) {
183 switch (type) {
184 case OperandType::FLOAT16:
185 case OperandType::FLOAT32:
186 case OperandType::INT32:
187 case OperandType::UINT32:
188 case OperandType::BOOL:
189 case OperandType::TENSOR_BOOL8:
190 case OperandType::TENSOR_FLOAT16:
191 case OperandType::TENSOR_FLOAT32:
192 case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
Slava Shklyaev0fff59b2020-01-31 15:14:24 +0000193 case OperandType::SUBGRAPH:
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100194 return 1.0f;
195 case OperandType::TENSOR_INT32:
196 return -1.0f;
197 case OperandType::TENSOR_QUANT8_SYMM:
198 case OperandType::TENSOR_QUANT8_ASYMM:
199 case OperandType::TENSOR_QUANT16_ASYMM:
200 case OperandType::TENSOR_QUANT16_SYMM:
201 return 0.0f;
202 default:
203 return 0.0f;
204 }
205}
206
207static void mutateOperandScaleTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000208 for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
209 const float invalidScale = getInvalidScale(model.main.operands[operand].type);
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100210 const std::string message = "mutateOperandScaleTest: operand " + std::to_string(operand) +
211 " has scale of " + std::to_string(invalidScale);
Michael Butler68a6de72020-03-11 18:45:45 -0700212 validate(device, message, model,
213 [operand, invalidScale](Model* model, ExecutionPreference*, Priority*) {
214 model->main.operands[operand].scale = invalidScale;
215 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100216 }
217}
218
219///////////////////////// VALIDATE OPERAND ZERO POINT /////////////////////////
220
221static std::vector<int32_t> getInvalidZeroPoints(OperandType type) {
222 switch (type) {
223 case OperandType::FLOAT16:
224 case OperandType::FLOAT32:
225 case OperandType::INT32:
226 case OperandType::UINT32:
227 case OperandType::BOOL:
228 case OperandType::TENSOR_BOOL8:
229 case OperandType::TENSOR_FLOAT16:
230 case OperandType::TENSOR_FLOAT32:
231 case OperandType::TENSOR_INT32:
232 case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
Slava Shklyaev0fff59b2020-01-31 15:14:24 +0000233 case OperandType::SUBGRAPH:
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100234 return {1};
235 case OperandType::TENSOR_QUANT8_ASYMM:
236 return {-1, 256};
237 case OperandType::TENSOR_QUANT8_SYMM:
238 return {-129, -1, 1, 128};
239 case OperandType::TENSOR_QUANT16_ASYMM:
240 return {-1, 65536};
241 case OperandType::TENSOR_QUANT16_SYMM:
242 return {-32769, -1, 1, 32768};
243 default:
244 return {};
245 }
246}
247
248static void mutateOperandZeroPointTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000249 for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100250 const std::vector<int32_t> invalidZeroPoints =
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000251 getInvalidZeroPoints(model.main.operands[operand].type);
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100252 for (int32_t invalidZeroPoint : invalidZeroPoints) {
253 const std::string message = "mutateOperandZeroPointTest: operand " +
254 std::to_string(operand) + " has zero point of " +
255 std::to_string(invalidZeroPoint);
Michael Butler68a6de72020-03-11 18:45:45 -0700256 validate(device, message, model,
257 [operand, invalidZeroPoint](Model* model, ExecutionPreference*, Priority*) {
258 model->main.operands[operand].zeroPoint = invalidZeroPoint;
259 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100260 }
261 }
262}
263
264///////////////////////// VALIDATE EXTRA ??? /////////////////////////
265
266// TODO: Operand::lifetime
267// TODO: Operand::location
268
269///////////////////////// VALIDATE OPERATION OPERAND TYPE /////////////////////////
270
271static void mutateOperand(Operand* operand, OperandType type) {
272 Operand newOperand = *operand;
273 newOperand.type = type;
274 switch (type) {
275 case OperandType::FLOAT16:
276 case OperandType::FLOAT32:
277 case OperandType::INT32:
278 case OperandType::UINT32:
279 case OperandType::BOOL:
280 newOperand.dimensions = hidl_vec<uint32_t>();
281 newOperand.scale = 0.0f;
282 newOperand.zeroPoint = 0;
283 break;
284 case OperandType::TENSOR_BOOL8:
285 case OperandType::TENSOR_FLOAT16:
286 case OperandType::TENSOR_FLOAT32:
287 newOperand.dimensions =
288 operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec<uint32_t>({1});
289 newOperand.scale = 0.0f;
290 newOperand.zeroPoint = 0;
291 break;
292 case OperandType::TENSOR_INT32:
293 newOperand.dimensions =
294 operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec<uint32_t>({1});
295 newOperand.zeroPoint = 0;
296 break;
297 case OperandType::TENSOR_QUANT8_ASYMM:
298 case OperandType::TENSOR_QUANT8_SYMM:
299 case OperandType::TENSOR_QUANT16_ASYMM:
300 case OperandType::TENSOR_QUANT16_SYMM:
301 newOperand.dimensions =
302 operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec<uint32_t>({1});
303 newOperand.scale = operand->scale != 0.0f ? operand->scale : 1.0f;
304 break;
305 case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL: {
306 newOperand.dimensions =
307 operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec<uint32_t>({1});
308 newOperand.scale = 0.0f;
309 newOperand.zeroPoint = 0;
310
311 SymmPerChannelQuantParams channelQuant;
312 channelQuant.channelDim = 0;
313 channelQuant.scales = hidl_vec<float>(
314 operand->dimensions.size() > 0 ? static_cast<size_t>(operand->dimensions[0])
315 : 0);
316 for (size_t i = 0; i < channelQuant.scales.size(); ++i) {
317 channelQuant.scales[i] = 1.0f;
318 }
319 newOperand.extraParams.channelQuant(std::move(channelQuant));
320 } break;
321 case OperandType::OEM:
322 case OperandType::TENSOR_OEM_BYTE:
323 default:
324 break;
325 }
326 *operand = newOperand;
327}
328
329static bool mutateOperationOperandTypeSkip(size_t operand, OperandType type, const Model& model) {
330 // Do not test OEM types
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000331 if (type == model.main.operands[operand].type || type == OperandType::OEM ||
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100332 type == OperandType::TENSOR_OEM_BYTE) {
333 return true;
334 }
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000335 for (const Operation& operation : model.main.operations) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100336 // Skip mutateOperationOperandTypeTest for the following operations.
337 // - LSH_PROJECTION's second argument is allowed to have any type.
338 // - ARGMIN and ARGMAX's first argument can be any of
339 // TENSOR_(FLOAT16|FLOAT32|INT32|QUANT8_ASYMM).
340 // - CAST's argument can be any of TENSOR_(FLOAT16|FLOAT32|INT32|QUANT8_ASYMM).
341 // - RANDOM_MULTINOMIAL's argument can be either TENSOR_FLOAT16 or TENSOR_FLOAT32.
342 // - DEQUANTIZE input can be any of
Lev Proleevae643ae2019-12-05 16:57:30 +0000343 // TENSOR_(QUANT8_ASYMM|QUANT8_ASYMM_SIGNED|QUANT8_SYMM|QUANT8_SYMM_PER_CHANNEL),
344 // output can be of either TENSOR_FLOAT16 or TENSOR_FLOAT32.
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100345 // - QUANTIZE input can be either TENSOR_FLOAT16 or TENSOR_FLOAT32
346 // - CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
347 // - DEPTHWISE_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
348 // - GROUPED_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
349 // - TRANSPOSE_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
Lev Proleevda779f32020-01-02 17:49:03 +0000350 // - AXIS_ALIGNED_BBOX_TRANSFORM bounding boxes (arg 1) can be of
351 // TENSOR_QUANT8_ASYMM or TENSOR_QUANT8_ASYMM_SIGNED.
Lev Proleev53a51cb2020-01-20 18:54:46 +0000352 // - RANK's input can have any TENSOR_* type.
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100353 switch (operation.type) {
354 case OperationType::LSH_PROJECTION: {
355 if (operand == operation.inputs[1]) {
356 return true;
357 }
358 } break;
359 case OperationType::CAST:
360 case OperationType::ARGMAX:
361 case OperationType::ARGMIN: {
362 if (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32 ||
Przemyslaw Szczepaniak2326dd12019-11-29 09:49:17 +0000363 type == OperandType::TENSOR_INT32 || type == OperandType::TENSOR_QUANT8_ASYMM ||
364 type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100365 return true;
366 }
367 } break;
Przemyslaw Szczepaniak90fc2cc2019-11-25 11:04:19 +0000368 case OperationType::QUANTIZE: {
369 if (operand == operation.inputs[0] &&
370 (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32)) {
371 return true;
372 }
373 if (operand == operation.outputs[0] &&
374 (type == OperandType::TENSOR_QUANT8_ASYMM ||
375 type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED)) {
376 return true;
377 }
378 } break;
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100379 case OperationType::RANDOM_MULTINOMIAL: {
380 if (operand == operation.inputs[0] &&
381 (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32)) {
382 return true;
383 }
384 } break;
385 case OperationType::DEQUANTIZE: {
386 if (operand == operation.inputs[0] &&
387 (type == OperandType::TENSOR_QUANT8_ASYMM ||
Lev Proleevae643ae2019-12-05 16:57:30 +0000388 type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED ||
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100389 type == OperandType::TENSOR_QUANT8_SYMM ||
390 type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL)) {
391 return true;
392 }
393 if (operand == operation.outputs[0] &&
394 (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32)) {
395 return true;
396 }
397 } break;
398 case OperationType::TRANSPOSE_CONV_2D:
399 case OperationType::GROUPED_CONV_2D:
400 case OperationType::DEPTHWISE_CONV_2D:
401 case OperationType::CONV_2D: {
402 if (operand == operation.inputs[1] &&
403 (type == OperandType::TENSOR_QUANT8_ASYMM ||
404 type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL)) {
405 return true;
406 }
407 } break;
Lev Proleevda779f32020-01-02 17:49:03 +0000408 case OperationType::AXIS_ALIGNED_BBOX_TRANSFORM: {
409 if (operand == operation.inputs[1] &&
410 (type == OperandType::TENSOR_QUANT8_ASYMM ||
411 type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED)) {
412 return true;
413 }
414 } break;
Lev Proleev53a51cb2020-01-20 18:54:46 +0000415 case OperationType::RANK: {
416 if (operand == operation.inputs[0] &&
417 (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32 ||
418 type == OperandType::TENSOR_INT32 ||
419 type == OperandType::TENSOR_QUANT8_ASYMM ||
420 type == OperandType::TENSOR_QUANT16_SYMM ||
421 type == OperandType::TENSOR_BOOL8 ||
422 type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL ||
423 type == OperandType::TENSOR_QUANT16_ASYMM ||
424 type == OperandType::TENSOR_QUANT8_SYMM ||
425 type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED)) {
426 return true;
427 }
428 } break;
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100429 default:
430 break;
431 }
432 }
433 return false;
434}
435
436static void mutateOperationOperandTypeTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000437 for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100438 for (OperandType invalidOperandType : hidl_enum_range<OperandType>{}) {
439 if (mutateOperationOperandTypeSkip(operand, invalidOperandType, model)) {
440 continue;
441 }
442 const std::string message = "mutateOperationOperandTypeTest: operand " +
443 std::to_string(operand) + " set to type " +
444 toString(invalidOperandType);
Michael Butler68a6de72020-03-11 18:45:45 -0700445 validate(device, message, model,
446 [operand, invalidOperandType](Model* model, ExecutionPreference*, Priority*) {
447 mutateOperand(&model->main.operands[operand], invalidOperandType);
448 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100449 }
450 }
451}
452
453///////////////////////// VALIDATE MODEL OPERATION TYPE /////////////////////////
454
455static const uint32_t invalidOperationTypes[] = {
456 static_cast<uint32_t>(OperationTypeRange::FUNDAMENTAL_MAX) + 1,
457 static_cast<uint32_t>(OperationTypeRange::OEM_MIN) - 1,
458 static_cast<uint32_t>(OperationTypeRange::OEM_MAX) + 1,
459};
460
461static void mutateOperationTypeTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000462 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100463 for (uint32_t invalidOperationType : invalidOperationTypes) {
464 const std::string message = "mutateOperationTypeTest: operation " +
465 std::to_string(operation) + " set to value " +
466 std::to_string(invalidOperationType);
Michael Butler68a6de72020-03-11 18:45:45 -0700467 validate(device, message, model,
468 [operation, invalidOperationType](Model* model, ExecutionPreference*,
469 Priority*) {
470 model->main.operations[operation].type =
471 static_cast<OperationType>(invalidOperationType);
472 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100473 }
474 }
475}
476
477///////////////////////// VALIDATE MODEL OPERATION INPUT OPERAND INDEX /////////////////////////
478
479static void mutateOperationInputOperandIndexTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000480 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
481 const uint32_t invalidOperand = model.main.operands.size();
482 for (size_t input = 0; input < model.main.operations[operation].inputs.size(); ++input) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100483 const std::string message = "mutateOperationInputOperandIndexTest: operation " +
484 std::to_string(operation) + " input " +
485 std::to_string(input);
Michael Butler68a6de72020-03-11 18:45:45 -0700486 validate(device, message, model,
487 [operation, input, invalidOperand](Model* model, ExecutionPreference*,
488 Priority*) {
489 model->main.operations[operation].inputs[input] = invalidOperand;
490 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100491 }
492 }
493}
494
495///////////////////////// VALIDATE MODEL OPERATION OUTPUT OPERAND INDEX /////////////////////////
496
497static void mutateOperationOutputOperandIndexTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000498 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
499 const uint32_t invalidOperand = model.main.operands.size();
500 for (size_t output = 0; output < model.main.operations[operation].outputs.size();
501 ++output) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100502 const std::string message = "mutateOperationOutputOperandIndexTest: operation " +
503 std::to_string(operation) + " output " +
504 std::to_string(output);
Michael Butler68a6de72020-03-11 18:45:45 -0700505 validate(device, message, model,
506 [operation, output, invalidOperand](Model* model, ExecutionPreference*,
507 Priority*) {
508 model->main.operations[operation].outputs[output] = invalidOperand;
509 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100510 }
511 }
512}
513
514///////////////////////// REMOVE OPERAND FROM EVERYTHING /////////////////////////
515
516static void removeValueAndDecrementGreaterValues(hidl_vec<uint32_t>* vec, uint32_t value) {
517 if (vec) {
518 // remove elements matching "value"
519 auto last = std::remove(vec->begin(), vec->end(), value);
520 vec->resize(std::distance(vec->begin(), last));
521
522 // decrement elements exceeding "value"
523 std::transform(vec->begin(), vec->end(), vec->begin(),
524 [value](uint32_t v) { return v > value ? v-- : v; });
525 }
526}
527
528static void removeOperand(Model* model, uint32_t index) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000529 hidl_vec_removeAt(&model->main.operands, index);
530 for (Operation& operation : model->main.operations) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100531 removeValueAndDecrementGreaterValues(&operation.inputs, index);
532 removeValueAndDecrementGreaterValues(&operation.outputs, index);
533 }
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000534 removeValueAndDecrementGreaterValues(&model->main.inputIndexes, index);
535 removeValueAndDecrementGreaterValues(&model->main.outputIndexes, index);
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100536}
537
Slava Shklyaevaf1c77d2020-04-16 14:58:55 +0100538static bool removeOperandSkip(size_t operandIndex, const Model& model) {
539 const Operand& operand = model.main.operands[operandIndex];
540 if (operand.numberOfConsumers == 0) {
541 // Removing an unused operand has no effect.
542 return true;
543 }
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000544 for (const Operation& operation : model.main.operations) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100545 // Skip removeOperandTest for the following operations.
546 // - SPLIT's outputs are not checked during prepareModel.
547 if (operation.type == OperationType::SPLIT) {
Slava Shklyaevaf1c77d2020-04-16 14:58:55 +0100548 for (const size_t index : operation.outputs) {
549 if (index == operandIndex) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100550 return true;
551 }
552 }
553 }
Lev Proleev17689aa2020-01-30 17:40:13 +0000554 // BIDIRECTIONAL_SEQUENCE_LSTM and BIDIRECTIONAL_SEQUENCE_RNN can have
555 // either one, two, three or four outputs depending on their
556 // mergeOutputs parameter and if state outputs are provided.
557 // UNIDIRECTIONAL_SEQUENCE_LSTM and UNIDIRECTIONAL_SEQUENCE_RNN can have
558 // either one or three outputs depending on whether state outputs are
559 // provided.
560 if (operation.type == OperationType::UNIDIRECTIONAL_SEQUENCE_LSTM ||
561 operation.type == OperationType::UNIDIRECTIONAL_SEQUENCE_RNN ||
562 operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_LSTM ||
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100563 operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_RNN) {
Slava Shklyaevaf1c77d2020-04-16 14:58:55 +0100564 for (const size_t index : operation.outputs) {
565 if (index == operandIndex) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100566 return true;
567 }
568 }
569 }
570 }
571 return false;
572}
573
574static void removeOperandTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000575 for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100576 if (removeOperandSkip(operand, model)) {
577 continue;
578 }
579 const std::string message = "removeOperandTest: operand " + std::to_string(operand);
Michael Butler68a6de72020-03-11 18:45:45 -0700580 validate(device, message, model, [operand](Model* model, ExecutionPreference*, Priority*) {
581 removeOperand(model, operand);
582 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100583 }
584}
585
586///////////////////////// REMOVE OPERATION /////////////////////////
587
588static void removeOperation(Model* model, uint32_t index) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000589 for (uint32_t operand : model->main.operations[index].inputs) {
590 model->main.operands[operand].numberOfConsumers--;
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100591 }
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000592 hidl_vec_removeAt(&model->main.operations, index);
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100593}
594
595static void removeOperationTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000596 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100597 const std::string message = "removeOperationTest: operation " + std::to_string(operation);
598 validate(device, message, model,
Michael Butler68a6de72020-03-11 18:45:45 -0700599 [operation](Model* model, ExecutionPreference*, Priority*) {
600 removeOperation(model, operation);
601 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100602 }
603}
604
605///////////////////////// REMOVE OPERATION INPUT /////////////////////////
606
607static bool removeOperationInputSkip(const Operation& op, size_t input) {
608 // Skip removeOperationInputTest for the following operations.
609 // - CONCATENATION has at least 2 inputs, with the last element being INT32.
610 // - CONV_2D, DEPTHWISE_CONV_2D, MAX_POOL_2D, AVERAGE_POOL_2D, L2_POOL_2D, RESIZE_BILINEAR,
611 // SPACE_TO_DEPTH, SPACE_TO_DEPTH, SPACE_TO_BATCH_ND, BATCH_TO_SPACE_ND can have an optional
612 // layout parameter.
Lev Proleeve3080db2020-02-18 12:53:00 +0000613 // RESIZE_BILINEAR and RESIZE_NEAREST_NEIGHBOR can have optional
614 // align_corners and half_pixel_centers parameters.
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100615 // - L2_NORMALIZATION, LOCAL_RESPONSE_NORMALIZATION, SOFTMAX can have an optional axis
616 // parameter.
617 switch (op.type) {
618 case OperationType::CONCATENATION: {
619 if (op.inputs.size() > 2 && input != op.inputs.size() - 1) {
620 return true;
621 }
622 } break;
623 case OperationType::DEPTHWISE_CONV_2D: {
624 if ((op.inputs.size() == 12 && input == 11) || (op.inputs.size() == 9 && input == 8)) {
625 return true;
626 }
627 } break;
628 case OperationType::CONV_2D:
629 case OperationType::AVERAGE_POOL_2D:
630 case OperationType::MAX_POOL_2D:
631 case OperationType::L2_POOL_2D: {
632 if ((op.inputs.size() == 11 && input == 10) || (op.inputs.size() == 8 && input == 7)) {
633 return true;
634 }
635 } break;
636 case OperationType::RESIZE_BILINEAR: {
Lev Proleeve3080db2020-02-18 12:53:00 +0000637 if (op.inputs.size() >= 4 && input >= 3) {
638 return true;
639 }
640 } break;
641 case OperationType::RESIZE_NEAREST_NEIGHBOR: {
642 if (op.inputs.size() >= 5 && input >= 3) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100643 return true;
644 }
645 } break;
646 case OperationType::SPACE_TO_DEPTH:
647 case OperationType::DEPTH_TO_SPACE:
648 case OperationType::BATCH_TO_SPACE_ND: {
649 if (op.inputs.size() == 3 && input == 2) {
650 return true;
651 }
652 } break;
653 case OperationType::SPACE_TO_BATCH_ND: {
654 if (op.inputs.size() == 4 && input == 3) {
655 return true;
656 }
657 } break;
658 case OperationType::L2_NORMALIZATION: {
659 if (op.inputs.size() == 2 && input == 1) {
660 return true;
661 }
662 } break;
663 case OperationType::LOCAL_RESPONSE_NORMALIZATION: {
664 if (op.inputs.size() == 6 && input == 5) {
665 return true;
666 }
667 } break;
668 case OperationType::SOFTMAX: {
669 if (op.inputs.size() == 3 && input == 2) {
670 return true;
671 }
672 } break;
673 default:
674 break;
675 }
676 return false;
677}
678
679static void removeOperationInputTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000680 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
681 for (size_t input = 0; input < model.main.operations[operation].inputs.size(); ++input) {
682 const Operation& op = model.main.operations[operation];
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100683 if (removeOperationInputSkip(op, input)) {
684 continue;
685 }
686 const std::string message = "removeOperationInputTest: operation " +
687 std::to_string(operation) + ", input " +
688 std::to_string(input);
Michael Butler68a6de72020-03-11 18:45:45 -0700689 validate(device, message, model,
690 [operation, input](Model* model, ExecutionPreference*, Priority*) {
691 uint32_t operand = model->main.operations[operation].inputs[input];
692 model->main.operands[operand].numberOfConsumers--;
693 hidl_vec_removeAt(&model->main.operations[operation].inputs, input);
694 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100695 }
696 }
697}
698
699///////////////////////// REMOVE OPERATION OUTPUT /////////////////////////
700
701static void removeOperationOutputTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000702 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
703 for (size_t output = 0; output < model.main.operations[operation].outputs.size();
704 ++output) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100705 const std::string message = "removeOperationOutputTest: operation " +
706 std::to_string(operation) + ", output " +
707 std::to_string(output);
Michael Butler68a6de72020-03-11 18:45:45 -0700708 validate(device, message, model,
709 [operation, output](Model* model, ExecutionPreference*, Priority*) {
710 hidl_vec_removeAt(&model->main.operations[operation].outputs, output);
711 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100712 }
713 }
714}
715
716///////////////////////// MODEL VALIDATION /////////////////////////
717
718// TODO: remove model input
719// TODO: remove model output
720// TODO: add unused operation
721
722///////////////////////// ADD OPERATION INPUT /////////////////////////
723
724static bool addOperationInputSkip(const Operation& op) {
725 // Skip addOperationInputTest for the following operations.
726 // - L2_NORMALIZATION, LOCAL_RESPONSE_NORMALIZATION, SOFTMAX can have an optional INT32 axis
727 // parameter.
728 if ((op.type == OperationType::L2_NORMALIZATION && op.inputs.size() == 1) ||
729 (op.type == OperationType::LOCAL_RESPONSE_NORMALIZATION && op.inputs.size() == 5) ||
Lev Proleeve3080db2020-02-18 12:53:00 +0000730 (op.type == OperationType::SOFTMAX && op.inputs.size() == 2) ||
731 (op.type == OperationType::RESIZE_BILINEAR && op.inputs.size() < 6) ||
732 (op.type == OperationType::RESIZE_NEAREST_NEIGHBOR && op.inputs.size() < 6)) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100733 return true;
734 }
735 return false;
736}
737
738static void addOperationInputTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000739 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
740 if (addOperationInputSkip(model.main.operations[operation])) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100741 continue;
742 }
743 const std::string message = "addOperationInputTest: operation " + std::to_string(operation);
Michael Butler68a6de72020-03-11 18:45:45 -0700744 validate(device, message, model,
745 [operation](Model* model, ExecutionPreference*, Priority*) {
746 uint32_t index = addOperand(model, OperandLifeTime::SUBGRAPH_INPUT);
747 hidl_vec_push_back(&model->main.operations[operation].inputs, index);
748 hidl_vec_push_back(&model->main.inputIndexes, index);
749 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100750 }
751}
752
753///////////////////////// ADD OPERATION OUTPUT /////////////////////////
754
755static void addOperationOutputTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000756 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100757 const std::string message =
758 "addOperationOutputTest: operation " + std::to_string(operation);
Michael Butler68a6de72020-03-11 18:45:45 -0700759 validate(device, message, model,
760 [operation](Model* model, ExecutionPreference*, Priority*) {
761 uint32_t index = addOperand(model, OperandLifeTime::SUBGRAPH_OUTPUT);
762 hidl_vec_push_back(&model->main.operations[operation].outputs, index);
763 hidl_vec_push_back(&model->main.outputIndexes, index);
764 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100765 }
766}
767
768///////////////////////// VALIDATE EXECUTION PREFERENCE /////////////////////////
769
770static const int32_t invalidExecutionPreferences[] = {
771 static_cast<int32_t>(ExecutionPreference::LOW_POWER) - 1, // lower bound
772 static_cast<int32_t>(ExecutionPreference::SUSTAINED_SPEED) + 1, // upper bound
773};
774
775static void mutateExecutionPreferenceTest(const sp<IDevice>& device, const Model& model) {
Michael Butler68a6de72020-03-11 18:45:45 -0700776 for (int32_t invalidPreference : invalidExecutionPreferences) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100777 const std::string message =
Michael Butler68a6de72020-03-11 18:45:45 -0700778 "mutateExecutionPreferenceTest: preference " + std::to_string(invalidPreference);
779 validate(device, message, model,
780 [invalidPreference](Model*, ExecutionPreference* preference, Priority*) {
781 *preference = static_cast<ExecutionPreference>(invalidPreference);
782 });
783 }
784}
785
786///////////////////////// VALIDATE PRIORITY /////////////////////////
787
788static const int32_t invalidPriorities[] = {
789 static_cast<int32_t>(Priority::LOW) - 1, // lower bound
790 static_cast<int32_t>(Priority::HIGH) + 1, // upper bound
791};
792
793static void mutateExecutionPriorityTest(const sp<IDevice>& device, const Model& model) {
794 for (int32_t invalidPriority : invalidPriorities) {
795 const std::string message =
796 "mutatePriorityTest: priority " + std::to_string(invalidPriority);
797 validate(device, message, model,
798 [invalidPriority](Model*, ExecutionPreference*, Priority* priority) {
799 *priority = static_cast<Priority>(invalidPriority);
800 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100801 }
802}
803
804////////////////////////// ENTRY POINT //////////////////////////////
805
Michael Butlerff7d6c52020-02-13 16:37:22 -0800806void validateModel(const sp<IDevice>& device, const Model& model) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100807 mutateOperandTypeTest(device, model);
808 mutateOperandRankTest(device, model);
809 mutateOperandScaleTest(device, model);
810 mutateOperandZeroPointTest(device, model);
811 mutateOperationOperandTypeTest(device, model);
812 mutateOperationTypeTest(device, model);
813 mutateOperationInputOperandIndexTest(device, model);
814 mutateOperationOutputOperandIndexTest(device, model);
815 removeOperandTest(device, model);
816 removeOperationTest(device, model);
817 removeOperationInputTest(device, model);
818 removeOperationOutputTest(device, model);
819 addOperationInputTest(device, model);
820 addOperationOutputTest(device, model);
821 mutateExecutionPreferenceTest(device, model);
Michael Butler68a6de72020-03-11 18:45:45 -0700822 mutateExecutionPriorityTest(device, model);
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100823}
824
Lev Proleev26d1bc82019-08-30 11:57:18 +0100825} // namespace android::hardware::neuralnetworks::V1_3::vts::functional