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 #define LOG_TAG "neuralnetworks_hidl_hal_test" 18 19 #include "VtsHalNeuralnetworks.h" 20 21 #include "Callbacks.h" 22 23 namespace android { 24 namespace hardware { 25 namespace neuralnetworks { 26 namespace V1_2 { 27 28 using V1_0::OperandLifeTime; 29 using V1_1::ExecutionPreference; 30 31 namespace vts { 32 namespace functional { 33 34 using ::android::hardware::neuralnetworks::V1_2::implementation::ExecutionCallback; 35 using ::android::hardware::neuralnetworks::V1_2::implementation::PreparedModelCallback; 36 using HidlToken = hidl_array<uint8_t, static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)>; 37 38 ///////////////////////// UTILITY FUNCTIONS ///////////////////////// 39 40 static void validateGetSupportedOperations(const sp<IDevice>& device, const std::string& message, 41 const Model& model) { 42 SCOPED_TRACE(message + " [getSupportedOperations_1_2]"); 43 44 Return<void> ret = 45 device->getSupportedOperations_1_2(model, [&](ErrorStatus status, const hidl_vec<bool>&) { 46 EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, status); 47 }); 48 EXPECT_TRUE(ret.isOk()); 49 } 50 51 static void validatePrepareModel(const sp<IDevice>& device, const std::string& message, 52 const Model& model, ExecutionPreference preference) { 53 SCOPED_TRACE(message + " [prepareModel_1_2]"); 54 55 sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback(); 56 ASSERT_NE(nullptr, preparedModelCallback.get()); 57 Return<ErrorStatus> prepareLaunchStatus = 58 device->prepareModel_1_2(model, preference, hidl_vec<hidl_handle>(), 59 hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback); 60 ASSERT_TRUE(prepareLaunchStatus.isOk()); 61 ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(prepareLaunchStatus)); 62 63 preparedModelCallback->wait(); 64 ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus(); 65 ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, prepareReturnStatus); 66 sp<IPreparedModel> preparedModel = getPreparedModel_1_2(preparedModelCallback); 67 ASSERT_EQ(nullptr, preparedModel.get()); 68 } 69 70 static bool validExecutionPreference(ExecutionPreference preference) { 71 return preference == ExecutionPreference::LOW_POWER || 72 preference == ExecutionPreference::FAST_SINGLE_ANSWER || 73 preference == ExecutionPreference::SUSTAINED_SPEED; 74 } 75 76 // Primary validation function. This function will take a valid model, apply a 77 // mutation to it to invalidate the model, then pass it to interface calls that 78 // use the model. Note that the model here is passed by value, and any mutation 79 // to the model does not leave this function. 80 static void validate(const sp<IDevice>& device, const std::string& message, Model model, 81 const std::function<void(Model*)>& mutation, 82 ExecutionPreference preference = ExecutionPreference::FAST_SINGLE_ANSWER) { 83 mutation(&model); 84 if (validExecutionPreference(preference)) { 85 validateGetSupportedOperations(device, message, model); 86 } 87 validatePrepareModel(device, message, model, preference); 88 } 89 90 // Delete element from hidl_vec. hidl_vec doesn't support a "remove" operation, 91 // so this is efficiently accomplished by moving the element to the end and 92 // resizing the hidl_vec to one less. 93 template <typename Type> 94 static void hidl_vec_removeAt(hidl_vec<Type>* vec, uint32_t index) { 95 if (vec) { 96 std::rotate(vec->begin() + index, vec->begin() + index + 1, vec->end()); 97 vec->resize(vec->size() - 1); 98 } 99 } 100 101 template <typename Type> 102 static uint32_t hidl_vec_push_back(hidl_vec<Type>* vec, const Type& value) { 103 // assume vec is valid 104 const uint32_t index = vec->size(); 105 vec->resize(index + 1); 106 (*vec)[index] = value; 107 return index; 108 } 109 110 static uint32_t addOperand(Model* model) { 111 return hidl_vec_push_back(&model->operands, 112 { 113 .type = OperandType::INT32, 114 .dimensions = {}, 115 .numberOfConsumers = 0, 116 .scale = 0.0f, 117 .zeroPoint = 0, 118 .lifetime = OperandLifeTime::MODEL_INPUT, 119 .location = {.poolIndex = 0, .offset = 0, .length = 0}, 120 }); 121 } 122 123 static uint32_t addOperand(Model* model, OperandLifeTime lifetime) { 124 uint32_t index = addOperand(model); 125 model->operands[index].numberOfConsumers = 1; 126 model->operands[index].lifetime = lifetime; 127 return index; 128 } 129 130 ///////////////////////// VALIDATE MODEL OPERAND TYPE ///////////////////////// 131 132 static const uint32_t invalidOperandTypes[] = { 133 static_cast<uint32_t>(OperandTypeRange::FUNDAMENTAL_MIN) - 1, 134 static_cast<uint32_t>(OperandTypeRange::FUNDAMENTAL_MAX) + 1, 135 static_cast<uint32_t>(OperandTypeRange::OEM_MIN) - 1, 136 static_cast<uint32_t>(OperandTypeRange::OEM_MAX) + 1, 137 }; 138 139 static void mutateOperandTypeTest(const sp<IDevice>& device, const Model& model) { 140 for (size_t operand = 0; operand < model.operands.size(); ++operand) { 141 for (uint32_t invalidOperandType : invalidOperandTypes) { 142 const std::string message = "mutateOperandTypeTest: operand " + 143 std::to_string(operand) + " set to value " + 144 std::to_string(invalidOperandType); 145 validate(device, message, model, [operand, invalidOperandType](Model* model) { 146 model->operands[operand].type = static_cast<OperandType>(invalidOperandType); 147 }); 148 } 149 } 150 } 151 152 ///////////////////////// VALIDATE OPERAND RANK ///////////////////////// 153 154 static uint32_t getInvalidRank(OperandType type) { 155 switch (type) { 156 case OperandType::FLOAT16: 157 case OperandType::FLOAT32: 158 case OperandType::INT32: 159 case OperandType::UINT32: 160 case OperandType::BOOL: 161 return 1; 162 case OperandType::TENSOR_BOOL8: 163 case OperandType::TENSOR_FLOAT16: 164 case OperandType::TENSOR_FLOAT32: 165 case OperandType::TENSOR_INT32: 166 case OperandType::TENSOR_QUANT8_ASYMM: 167 case OperandType::TENSOR_QUANT8_SYMM: 168 case OperandType::TENSOR_QUANT16_ASYMM: 169 case OperandType::TENSOR_QUANT16_SYMM: 170 case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL: 171 return 0; 172 default: 173 return 0; 174 } 175 } 176 177 static void mutateOperandRankTest(const sp<IDevice>& device, const Model& model) { 178 for (size_t operand = 0; operand < model.operands.size(); ++operand) { 179 const uint32_t invalidRank = getInvalidRank(model.operands[operand].type); 180 if (invalidRank == 0) { 181 continue; 182 } 183 const std::string message = "mutateOperandRankTest: operand " + std::to_string(operand) + 184 " has rank of " + std::to_string(invalidRank); 185 validate(device, message, model, [operand, invalidRank](Model* model) { 186 model->operands[operand].dimensions = std::vector<uint32_t>(invalidRank, 0); 187 }); 188 } 189 } 190 191 ///////////////////////// VALIDATE OPERAND SCALE ///////////////////////// 192 193 static float getInvalidScale(OperandType type) { 194 switch (type) { 195 case OperandType::FLOAT16: 196 case OperandType::FLOAT32: 197 case OperandType::INT32: 198 case OperandType::UINT32: 199 case OperandType::BOOL: 200 case OperandType::TENSOR_BOOL8: 201 case OperandType::TENSOR_FLOAT16: 202 case OperandType::TENSOR_FLOAT32: 203 case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL: 204 return 1.0f; 205 case OperandType::TENSOR_INT32: 206 return -1.0f; 207 case OperandType::TENSOR_QUANT8_SYMM: 208 case OperandType::TENSOR_QUANT8_ASYMM: 209 case OperandType::TENSOR_QUANT16_ASYMM: 210 case OperandType::TENSOR_QUANT16_SYMM: 211 return 0.0f; 212 default: 213 return 0.0f; 214 } 215 } 216 217 static void mutateOperandScaleTest(const sp<IDevice>& device, const Model& model) { 218 for (size_t operand = 0; operand < model.operands.size(); ++operand) { 219 const float invalidScale = getInvalidScale(model.operands[operand].type); 220 const std::string message = "mutateOperandScaleTest: operand " + std::to_string(operand) + 221 " has scale of " + std::to_string(invalidScale); 222 validate(device, message, model, [operand, invalidScale](Model* model) { 223 model->operands[operand].scale = invalidScale; 224 }); 225 } 226 } 227 228 ///////////////////////// VALIDATE OPERAND ZERO POINT ///////////////////////// 229 230 static std::vector<int32_t> getInvalidZeroPoints(OperandType type) { 231 switch (type) { 232 case OperandType::FLOAT16: 233 case OperandType::FLOAT32: 234 case OperandType::INT32: 235 case OperandType::UINT32: 236 case OperandType::BOOL: 237 case OperandType::TENSOR_BOOL8: 238 case OperandType::TENSOR_FLOAT16: 239 case OperandType::TENSOR_FLOAT32: 240 case OperandType::TENSOR_INT32: 241 case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL: 242 return {1}; 243 case OperandType::TENSOR_QUANT8_ASYMM: 244 return {-1, 256}; 245 case OperandType::TENSOR_QUANT8_SYMM: 246 return {-129, -1, 1, 128}; 247 case OperandType::TENSOR_QUANT16_ASYMM: 248 return {-1, 65536}; 249 case OperandType::TENSOR_QUANT16_SYMM: 250 return {-32769, -1, 1, 32768}; 251 default: 252 return {}; 253 } 254 } 255 256 static void mutateOperandZeroPointTest(const sp<IDevice>& device, const Model& model) { 257 for (size_t operand = 0; operand < model.operands.size(); ++operand) { 258 const std::vector<int32_t> invalidZeroPoints = 259 getInvalidZeroPoints(model.operands[operand].type); 260 for (int32_t invalidZeroPoint : invalidZeroPoints) { 261 const std::string message = "mutateOperandZeroPointTest: operand " + 262 std::to_string(operand) + " has zero point of " + 263 std::to_string(invalidZeroPoint); 264 validate(device, message, model, [operand, invalidZeroPoint](Model* model) { 265 model->operands[operand].zeroPoint = invalidZeroPoint; 266 }); 267 } 268 } 269 } 270 271 ///////////////////////// VALIDATE EXTRA ??? ///////////////////////// 272 273 // TODO: Operand::lifetime 274 // TODO: Operand::location 275 276 ///////////////////////// VALIDATE OPERATION OPERAND TYPE ///////////////////////// 277 278 static void mutateOperand(Operand* operand, OperandType type) { 279 Operand newOperand = *operand; 280 newOperand.type = type; 281 switch (type) { 282 case OperandType::FLOAT16: 283 case OperandType::FLOAT32: 284 case OperandType::INT32: 285 case OperandType::UINT32: 286 case OperandType::BOOL: 287 newOperand.dimensions = hidl_vec<uint32_t>(); 288 newOperand.scale = 0.0f; 289 newOperand.zeroPoint = 0; 290 break; 291 case OperandType::TENSOR_BOOL8: 292 case OperandType::TENSOR_FLOAT16: 293 case OperandType::TENSOR_FLOAT32: 294 newOperand.dimensions = 295 operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec<uint32_t>({1}); 296 newOperand.scale = 0.0f; 297 newOperand.zeroPoint = 0; 298 break; 299 case OperandType::TENSOR_INT32: 300 newOperand.dimensions = 301 operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec<uint32_t>({1}); 302 newOperand.zeroPoint = 0; 303 break; 304 case OperandType::TENSOR_QUANT8_ASYMM: 305 case OperandType::TENSOR_QUANT8_SYMM: 306 case OperandType::TENSOR_QUANT16_ASYMM: 307 case OperandType::TENSOR_QUANT16_SYMM: 308 newOperand.dimensions = 309 operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec<uint32_t>({1}); 310 newOperand.scale = operand->scale != 0.0f ? operand->scale : 1.0f; 311 break; 312 case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL: { 313 newOperand.dimensions = 314 operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec<uint32_t>({1}); 315 newOperand.scale = 0.0f; 316 newOperand.zeroPoint = 0; 317 318 SymmPerChannelQuantParams channelQuant; 319 channelQuant.channelDim = 0; 320 channelQuant.scales = hidl_vec<float>( 321 operand->dimensions.size() > 0 ? static_cast<size_t>(operand->dimensions[0]) : 0); 322 for (size_t i = 0; i < channelQuant.scales.size(); ++i) { 323 channelQuant.scales[i] = 1.0f; 324 } 325 newOperand.extraParams.channelQuant(std::move(channelQuant)); 326 } break; 327 case OperandType::OEM: 328 case OperandType::TENSOR_OEM_BYTE: 329 default: 330 break; 331 } 332 *operand = newOperand; 333 } 334 335 static bool mutateOperationOperandTypeSkip(size_t operand, OperandType type, const Model& model) { 336 // Do not test OEM types 337 if (type == model.operands[operand].type || type == OperandType::OEM || 338 type == OperandType::TENSOR_OEM_BYTE) { 339 return true; 340 } 341 for (const Operation& operation : model.operations) { 342 // Skip mutateOperationOperandTypeTest for the following operations. 343 // - LSH_PROJECTION's second argument is allowed to have any type. 344 // - ARGMIN and ARGMAX's first argument can be any of 345 // TENSOR_(FLOAT16|FLOAT32|INT32|QUANT8_ASYMM). 346 // - CAST's argument can be any of TENSOR_(FLOAT16|FLOAT32|INT32|QUANT8_ASYMM). 347 // - RANDOM_MULTINOMIAL's argument can be either TENSOR_FLOAT16 or TENSOR_FLOAT32. 348 // - DEQUANTIZE input can be any of 349 // TENSOR_(QUANT8_ASYMM|QUANT8_SYMM|QUANT8_SYMM_PER_CHANNEL), output can 350 // be of either TENSOR_FLOAT16 or TENSOR_FLOAT32. 351 // - QUANTIZE input can be either TENSOR_FLOAT16 or TENSOR_FLOAT32 352 // - CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL 353 // - DEPTHWISE_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL 354 // - GROUPED_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL 355 // - TRANSPOSE_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL 356 switch (operation.type) { 357 case OperationType::LSH_PROJECTION: { 358 if (operand == operation.inputs[1]) { 359 return true; 360 } 361 } break; 362 case OperationType::CAST: 363 case OperationType::ARGMAX: 364 case OperationType::ARGMIN: { 365 if (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32 || 366 type == OperandType::TENSOR_INT32 || type == OperandType::TENSOR_QUANT8_ASYMM) { 367 return true; 368 } 369 } break; 370 case OperationType::QUANTIZE: 371 case OperationType::RANDOM_MULTINOMIAL: { 372 if (operand == operation.inputs[0] && 373 (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32)) { 374 return true; 375 } 376 } break; 377 case OperationType::DEQUANTIZE: { 378 if (operand == operation.inputs[0] && 379 (type == OperandType::TENSOR_QUANT8_ASYMM || 380 type == OperandType::TENSOR_QUANT8_SYMM || 381 type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL)) { 382 return true; 383 } 384 if (operand == operation.outputs[0] && 385 (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32)) { 386 return true; 387 } 388 } break; 389 case OperationType::TRANSPOSE_CONV_2D: 390 case OperationType::GROUPED_CONV_2D: 391 case OperationType::DEPTHWISE_CONV_2D: 392 case OperationType::CONV_2D: { 393 if (operand == operation.inputs[1] && 394 (type == OperandType::TENSOR_QUANT8_ASYMM || 395 type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL)) { 396 return true; 397 } 398 } break; 399 default: 400 break; 401 } 402 } 403 return false; 404 } 405 406 static void mutateOperationOperandTypeTest(const sp<IDevice>& device, const Model& model) { 407 for (size_t operand = 0; operand < model.operands.size(); ++operand) { 408 for (OperandType invalidOperandType : hidl_enum_range<OperandType>{}) { 409 if (mutateOperationOperandTypeSkip(operand, invalidOperandType, model)) { 410 continue; 411 } 412 const std::string message = "mutateOperationOperandTypeTest: operand " + 413 std::to_string(operand) + " set to type " + 414 toString(invalidOperandType); 415 validate(device, message, model, [operand, invalidOperandType](Model* model) { 416 mutateOperand(&model->operands[operand], invalidOperandType); 417 }); 418 } 419 } 420 } 421 422 ///////////////////////// VALIDATE MODEL OPERATION TYPE ///////////////////////// 423 424 static const uint32_t invalidOperationTypes[] = { 425 static_cast<uint32_t>(OperationTypeRange::FUNDAMENTAL_MAX) + 1, 426 static_cast<uint32_t>(OperationTypeRange::OEM_MIN) - 1, 427 static_cast<uint32_t>(OperationTypeRange::OEM_MAX) + 1, 428 }; 429 430 static void mutateOperationTypeTest(const sp<IDevice>& device, const Model& model) { 431 for (size_t operation = 0; operation < model.operations.size(); ++operation) { 432 for (uint32_t invalidOperationType : invalidOperationTypes) { 433 const std::string message = "mutateOperationTypeTest: operation " + 434 std::to_string(operation) + " set to value " + 435 std::to_string(invalidOperationType); 436 validate(device, message, model, [operation, invalidOperationType](Model* model) { 437 model->operations[operation].type = 438 static_cast<OperationType>(invalidOperationType); 439 }); 440 } 441 } 442 } 443 444 ///////////////////////// VALIDATE MODEL OPERATION INPUT OPERAND INDEX ///////////////////////// 445 446 static void mutateOperationInputOperandIndexTest(const sp<IDevice>& device, const Model& model) { 447 for (size_t operation = 0; operation < model.operations.size(); ++operation) { 448 const uint32_t invalidOperand = model.operands.size(); 449 for (size_t input = 0; input < model.operations[operation].inputs.size(); ++input) { 450 const std::string message = "mutateOperationInputOperandIndexTest: operation " + 451 std::to_string(operation) + " input " + 452 std::to_string(input); 453 validate(device, message, model, [operation, input, invalidOperand](Model* model) { 454 model->operations[operation].inputs[input] = invalidOperand; 455 }); 456 } 457 } 458 } 459 460 ///////////////////////// VALIDATE MODEL OPERATION OUTPUT OPERAND INDEX ///////////////////////// 461 462 static void mutateOperationOutputOperandIndexTest(const sp<IDevice>& device, const Model& model) { 463 for (size_t operation = 0; operation < model.operations.size(); ++operation) { 464 const uint32_t invalidOperand = model.operands.size(); 465 for (size_t output = 0; output < model.operations[operation].outputs.size(); ++output) { 466 const std::string message = "mutateOperationOutputOperandIndexTest: operation " + 467 std::to_string(operation) + " output " + 468 std::to_string(output); 469 validate(device, message, model, [operation, output, invalidOperand](Model* model) { 470 model->operations[operation].outputs[output] = invalidOperand; 471 }); 472 } 473 } 474 } 475 476 ///////////////////////// REMOVE OPERAND FROM EVERYTHING ///////////////////////// 477 478 static void removeValueAndDecrementGreaterValues(hidl_vec<uint32_t>* vec, uint32_t value) { 479 if (vec) { 480 // remove elements matching "value" 481 auto last = std::remove(vec->begin(), vec->end(), value); 482 vec->resize(std::distance(vec->begin(), last)); 483 484 // decrement elements exceeding "value" 485 std::transform(vec->begin(), vec->end(), vec->begin(), 486 [value](uint32_t v) { return v > value ? v-- : v; }); 487 } 488 } 489 490 static void removeOperand(Model* model, uint32_t index) { 491 hidl_vec_removeAt(&model->operands, index); 492 for (Operation& operation : model->operations) { 493 removeValueAndDecrementGreaterValues(&operation.inputs, index); 494 removeValueAndDecrementGreaterValues(&operation.outputs, index); 495 } 496 removeValueAndDecrementGreaterValues(&model->inputIndexes, index); 497 removeValueAndDecrementGreaterValues(&model->outputIndexes, index); 498 } 499 500 static bool removeOperandSkip(size_t operand, const Model& model) { 501 for (const Operation& operation : model.operations) { 502 // Skip removeOperandTest for the following operations. 503 // - SPLIT's outputs are not checked during prepareModel. 504 if (operation.type == OperationType::SPLIT) { 505 for (const size_t outOprand : operation.outputs) { 506 if (operand == outOprand) { 507 return true; 508 } 509 } 510 } 511 // BIDIRECTIONAL_SEQUENCE_LSTM and BIDIRECTIONAL_SEQUENCE_RNN can have either one or two 512 // outputs depending on their mergeOutputs parameter. 513 if (operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_LSTM || 514 operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_RNN) { 515 for (const size_t outOprand : operation.outputs) { 516 if (operand == outOprand) { 517 return true; 518 } 519 } 520 } 521 } 522 return false; 523 } 524 525 static void removeOperandTest(const sp<IDevice>& device, const Model& model) { 526 for (size_t operand = 0; operand < model.operands.size(); ++operand) { 527 if (removeOperandSkip(operand, model)) { 528 continue; 529 } 530 const std::string message = "removeOperandTest: operand " + std::to_string(operand); 531 validate(device, message, model, 532 [operand](Model* model) { removeOperand(model, operand); }); 533 } 534 } 535 536 ///////////////////////// REMOVE OPERATION ///////////////////////// 537 538 static void removeOperation(Model* model, uint32_t index) { 539 for (uint32_t operand : model->operations[index].inputs) { 540 model->operands[operand].numberOfConsumers--; 541 } 542 hidl_vec_removeAt(&model->operations, index); 543 } 544 545 static void removeOperationTest(const sp<IDevice>& device, const Model& model) { 546 for (size_t operation = 0; operation < model.operations.size(); ++operation) { 547 const std::string message = "removeOperationTest: operation " + std::to_string(operation); 548 validate(device, message, model, 549 [operation](Model* model) { removeOperation(model, operation); }); 550 } 551 } 552 553 ///////////////////////// REMOVE OPERATION INPUT ///////////////////////// 554 555 static bool removeOperationInputSkip(const Operation& op, size_t input) { 556 // Skip removeOperationInputTest for the following operations. 557 // - CONCATENATION has at least 2 inputs, with the last element being INT32. 558 // - CONV_2D, DEPTHWISE_CONV_2D, MAX_POOL_2D, AVERAGE_POOL_2D, L2_POOL_2D, RESIZE_BILINEAR, 559 // SPACE_TO_DEPTH, SPACE_TO_DEPTH, SPACE_TO_BATCH_ND, BATCH_TO_SPACE_ND can have an optional 560 // layout parameter. 561 // - L2_NORMALIZATION, LOCAL_RESPONSE_NORMALIZATION, SOFTMAX can have an optional axis 562 // parameter. 563 switch (op.type) { 564 case OperationType::CONCATENATION: { 565 if (op.inputs.size() > 2 && input != op.inputs.size() - 1) { 566 return true; 567 } 568 } break; 569 case OperationType::DEPTHWISE_CONV_2D: { 570 if ((op.inputs.size() == 12 && input == 11) || (op.inputs.size() == 9 && input == 8)) { 571 return true; 572 } 573 } break; 574 case OperationType::CONV_2D: 575 case OperationType::AVERAGE_POOL_2D: 576 case OperationType::MAX_POOL_2D: 577 case OperationType::L2_POOL_2D: { 578 if ((op.inputs.size() == 11 && input == 10) || (op.inputs.size() == 8 && input == 7)) { 579 return true; 580 } 581 } break; 582 case OperationType::RESIZE_BILINEAR: { 583 if (op.inputs.size() == 4 && input == 3) { 584 return true; 585 } 586 } break; 587 case OperationType::SPACE_TO_DEPTH: 588 case OperationType::DEPTH_TO_SPACE: 589 case OperationType::BATCH_TO_SPACE_ND: { 590 if (op.inputs.size() == 3 && input == 2) { 591 return true; 592 } 593 } break; 594 case OperationType::SPACE_TO_BATCH_ND: { 595 if (op.inputs.size() == 4 && input == 3) { 596 return true; 597 } 598 } break; 599 case OperationType::L2_NORMALIZATION: { 600 if (op.inputs.size() == 2 && input == 1) { 601 return true; 602 } 603 } break; 604 case OperationType::LOCAL_RESPONSE_NORMALIZATION: { 605 if (op.inputs.size() == 6 && input == 5) { 606 return true; 607 } 608 } break; 609 case OperationType::SOFTMAX: { 610 if (op.inputs.size() == 3 && input == 2) { 611 return true; 612 } 613 } break; 614 default: 615 break; 616 } 617 return false; 618 } 619 620 static void removeOperationInputTest(const sp<IDevice>& device, const Model& model) { 621 for (size_t operation = 0; operation < model.operations.size(); ++operation) { 622 for (size_t input = 0; input < model.operations[operation].inputs.size(); ++input) { 623 const Operation& op = model.operations[operation]; 624 if (removeOperationInputSkip(op, input)) { 625 continue; 626 } 627 const std::string message = "removeOperationInputTest: operation " + 628 std::to_string(operation) + ", input " + 629 std::to_string(input); 630 validate(device, message, model, [operation, input](Model* model) { 631 uint32_t operand = model->operations[operation].inputs[input]; 632 model->operands[operand].numberOfConsumers--; 633 hidl_vec_removeAt(&model->operations[operation].inputs, input); 634 }); 635 } 636 } 637 } 638 639 ///////////////////////// REMOVE OPERATION OUTPUT ///////////////////////// 640 641 static void removeOperationOutputTest(const sp<IDevice>& device, const Model& model) { 642 for (size_t operation = 0; operation < model.operations.size(); ++operation) { 643 for (size_t output = 0; output < model.operations[operation].outputs.size(); ++output) { 644 const std::string message = "removeOperationOutputTest: operation " + 645 std::to_string(operation) + ", output " + 646 std::to_string(output); 647 validate(device, message, model, [operation, output](Model* model) { 648 hidl_vec_removeAt(&model->operations[operation].outputs, output); 649 }); 650 } 651 } 652 } 653 654 ///////////////////////// MODEL VALIDATION ///////////////////////// 655 656 // TODO: remove model input 657 // TODO: remove model output 658 // TODO: add unused operation 659 660 ///////////////////////// ADD OPERATION INPUT ///////////////////////// 661 662 static bool addOperationInputSkip(const Operation& op) { 663 // Skip addOperationInputTest for the following operations. 664 // - L2_NORMALIZATION, LOCAL_RESPONSE_NORMALIZATION, SOFTMAX can have an optional INT32 axis 665 // parameter. 666 if ((op.type == OperationType::L2_NORMALIZATION && op.inputs.size() == 1) || 667 (op.type == OperationType::LOCAL_RESPONSE_NORMALIZATION && op.inputs.size() == 5) || 668 (op.type == OperationType::SOFTMAX && op.inputs.size() == 2)) { 669 return true; 670 } 671 return false; 672 } 673 674 static void addOperationInputTest(const sp<IDevice>& device, const Model& model) { 675 for (size_t operation = 0; operation < model.operations.size(); ++operation) { 676 if (addOperationInputSkip(model.operations[operation])) { 677 continue; 678 } 679 const std::string message = "addOperationInputTest: operation " + std::to_string(operation); 680 validate(device, message, model, [operation](Model* model) { 681 uint32_t index = addOperand(model, OperandLifeTime::MODEL_INPUT); 682 hidl_vec_push_back(&model->operations[operation].inputs, index); 683 hidl_vec_push_back(&model->inputIndexes, index); 684 }); 685 } 686 } 687 688 ///////////////////////// ADD OPERATION OUTPUT ///////////////////////// 689 690 static void addOperationOutputTest(const sp<IDevice>& device, const Model& model) { 691 for (size_t operation = 0; operation < model.operations.size(); ++operation) { 692 const std::string message = 693 "addOperationOutputTest: operation " + std::to_string(operation); 694 validate(device, message, model, [operation](Model* model) { 695 uint32_t index = addOperand(model, OperandLifeTime::MODEL_OUTPUT); 696 hidl_vec_push_back(&model->operations[operation].outputs, index); 697 hidl_vec_push_back(&model->outputIndexes, index); 698 }); 699 } 700 } 701 702 ///////////////////////// VALIDATE EXECUTION PREFERENCE ///////////////////////// 703 704 static const int32_t invalidExecutionPreferences[] = { 705 static_cast<int32_t>(ExecutionPreference::LOW_POWER) - 1, // lower bound 706 static_cast<int32_t>(ExecutionPreference::SUSTAINED_SPEED) + 1, // upper bound 707 }; 708 709 static void mutateExecutionPreferenceTest(const sp<IDevice>& device, const Model& model) { 710 for (int32_t preference : invalidExecutionPreferences) { 711 const std::string message = 712 "mutateExecutionPreferenceTest: preference " + std::to_string(preference); 713 validate(device, message, model, [](Model*) {}, 714 static_cast<ExecutionPreference>(preference)); 715 } 716 } 717 718 ////////////////////////// ENTRY POINT ////////////////////////////// 719 720 void ValidationTest::validateModel(const Model& model) { 721 mutateOperandTypeTest(device, model); 722 mutateOperandRankTest(device, model); 723 mutateOperandScaleTest(device, model); 724 mutateOperandZeroPointTest(device, model); 725 mutateOperationOperandTypeTest(device, model); 726 mutateOperationTypeTest(device, model); 727 mutateOperationInputOperandIndexTest(device, model); 728 mutateOperationOutputOperandIndexTest(device, model); 729 removeOperandTest(device, model); 730 removeOperationTest(device, model); 731 removeOperationInputTest(device, model); 732 removeOperationOutputTest(device, model); 733 addOperationInputTest(device, model); 734 addOperationOutputTest(device, model); 735 mutateExecutionPreferenceTest(device, model); 736 } 737 738 } // namespace functional 739 } // namespace vts 740 } // namespace V1_2 741 } // namespace neuralnetworks 742 } // namespace hardware 743 } // namespace android 744