blob: 849ef7bf50f18e3f47b148935596629a52b7d056 [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
David Gross6174f002018-05-14 12:23:04 -070019#include <android/hardware/neuralnetworks/1.1/types.h>
20#include <android/hardware/neuralnetworks/1.3/types.h>
Lev Proleev13fdfcd2019-08-30 11:35:34 +010021#include "1.0/Utils.h"
Xusong Wangcc47dff2019-10-23 10:35:07 -070022#include "1.3/Callbacks.h"
Michael Butler79a41d72019-12-11 19:08:08 -080023#include "1.3/Utils.h"
Lev Proleev13fdfcd2019-08-30 11:35:34 +010024#include "GeneratedTestHarness.h"
25#include "VtsHalNeuralnetworks.h"
26
David Gross6174f002018-05-14 12:23:04 -070027#include <optional>
28#include <type_traits>
29#include <utility>
30
Lev Proleev26d1bc82019-08-30 11:57:18 +010031namespace android::hardware::neuralnetworks::V1_3::vts::functional {
Lev Proleev13fdfcd2019-08-30 11:35:34 +010032
Xusong Wangcc47dff2019-10-23 10:35:07 -070033using implementation::PreparedModelCallback;
David Gross6174f002018-05-14 12:23:04 -070034using V1_0::DataLocation;
Lev Proleev13fdfcd2019-08-30 11:35:34 +010035using V1_1::ExecutionPreference;
Lev Proleev26d1bc82019-08-30 11:57:18 +010036using V1_2::SymmPerChannelQuantParams;
Lev Proleev26d1bc82019-08-30 11:57:18 +010037using HidlToken =
38 hidl_array<uint8_t, static_cast<uint32_t>(V1_2::Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
Lev Proleev13fdfcd2019-08-30 11:35:34 +010039
Michael Butler68a6de72020-03-11 18:45:45 -070040using PrepareModelMutation = std::function<void(Model*, ExecutionPreference*, Priority*)>;
41
Lev Proleev13fdfcd2019-08-30 11:35:34 +010042///////////////////////// UTILITY FUNCTIONS /////////////////////////
43
44static void validateGetSupportedOperations(const sp<IDevice>& device, const std::string& message,
45 const Model& model) {
Lev Proleev26d1bc82019-08-30 11:57:18 +010046 SCOPED_TRACE(message + " [getSupportedOperations_1_3]");
Lev Proleev13fdfcd2019-08-30 11:35:34 +010047
Lev Proleev26d1bc82019-08-30 11:57:18 +010048 Return<void> ret = device->getSupportedOperations_1_3(
Lev Proleev13fdfcd2019-08-30 11:35:34 +010049 model, [&](ErrorStatus status, const hidl_vec<bool>&) {
50 EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, status);
51 });
52 EXPECT_TRUE(ret.isOk());
53}
54
55static void validatePrepareModel(const sp<IDevice>& device, const std::string& message,
Michael Butler68a6de72020-03-11 18:45:45 -070056 const Model& model, ExecutionPreference preference,
57 Priority priority) {
Lev Proleev26d1bc82019-08-30 11:57:18 +010058 SCOPED_TRACE(message + " [prepareModel_1_3]");
Lev Proleev13fdfcd2019-08-30 11:35:34 +010059
60 sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
Michael Butler68a6de72020-03-11 18:45:45 -070061 Return<ErrorStatus> prepareLaunchStatus =
62 device->prepareModel_1_3(model, preference, priority, {}, hidl_vec<hidl_handle>(),
63 hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
Lev Proleev13fdfcd2019-08-30 11:35:34 +010064 ASSERT_TRUE(prepareLaunchStatus.isOk());
65 ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(prepareLaunchStatus));
66
67 preparedModelCallback->wait();
68 ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
69 ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, prepareReturnStatus);
Xusong Wang1b3f4262019-10-25 12:07:17 -070070 sp<IPreparedModel> preparedModel = getPreparedModel_1_3(preparedModelCallback);
Lev Proleev13fdfcd2019-08-30 11:35:34 +010071 ASSERT_EQ(nullptr, preparedModel.get());
72}
73
74static bool validExecutionPreference(ExecutionPreference preference) {
75 return preference == ExecutionPreference::LOW_POWER ||
76 preference == ExecutionPreference::FAST_SINGLE_ANSWER ||
77 preference == ExecutionPreference::SUSTAINED_SPEED;
78}
79
Michael Butler68a6de72020-03-11 18:45:45 -070080static bool validExecutionPriority(Priority priority) {
81 return priority == Priority::LOW || priority == Priority::MEDIUM || priority == Priority::HIGH;
82}
83
Lev Proleev13fdfcd2019-08-30 11:35:34 +010084// Primary validation function. This function will take a valid model, apply a
Michael Butler68a6de72020-03-11 18:45:45 -070085// mutation to invalidate the model, the execution preference, or the priority,
86// then pass these to supportedOperations and/or prepareModel if that method is
87// called with an invalid argument.
88static void validate(const sp<IDevice>& device, const std::string& message,
89 const Model& originalModel, const PrepareModelMutation& mutate) {
90 Model model = originalModel;
91 ExecutionPreference preference = ExecutionPreference::FAST_SINGLE_ANSWER;
92 Priority priority = kDefaultPriority;
93 mutate(&model, &preference, &priority);
94
95 if (validExecutionPreference(preference) && validExecutionPriority(priority)) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +010096 validateGetSupportedOperations(device, message, model);
97 }
Michael Butler68a6de72020-03-11 18:45:45 -070098
99 validatePrepareModel(device, message, model, preference, priority);
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100100}
101
102static uint32_t addOperand(Model* model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000103 return hidl_vec_push_back(&model->main.operands,
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100104 {
105 .type = OperandType::INT32,
106 .dimensions = {},
107 .numberOfConsumers = 0,
108 .scale = 0.0f,
109 .zeroPoint = 0,
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000110 .lifetime = OperandLifeTime::SUBGRAPH_INPUT,
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100111 .location = {.poolIndex = 0, .offset = 0, .length = 0},
112 });
113}
114
115static uint32_t addOperand(Model* model, OperandLifeTime lifetime) {
116 uint32_t index = addOperand(model);
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000117 model->main.operands[index].numberOfConsumers = 1;
118 model->main.operands[index].lifetime = lifetime;
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100119 return index;
120}
121
David Gross6174f002018-05-14 12:23:04 -0700122// If we introduce a CONSTANT_COPY for an operand of size operandSize,
123// how much will this increase the size of the model? This assumes
124// that we can (re)use all of model.operandValues for the operand
125// value.
126static size_t constantCopyExtraSize(const Model& model, size_t operandSize) {
127 const size_t operandValuesSize = model.operandValues.size();
128 return (operandValuesSize < operandSize) ? (operandSize - operandValuesSize) : 0;
129}
130
131// Highly specialized utility routine for converting an operand to
132// CONSTANT_COPY lifetime.
133//
134// Expects that:
135// - operand has a known size
136// - operand->lifetime has already been set to CONSTANT_COPY
137// - operand->location has been zeroed out
138//
139// Does the following:
140// - initializes operand->location to point to the beginning of model->operandValues
141// - resizes model->operandValues (if necessary) to be large enough for the operand
142// value, padding it with zeroes on the end
143//
144// Potential problem:
145// By changing the operand to CONSTANT_COPY lifetime, this function is effectively initializing the
146// operand with unspecified (but deterministic) data. This means that the model may be invalidated
147// in two ways: not only is the lifetime of CONSTANT_COPY invalid, but the operand's value in the
148// graph may also be invalid (e.g., if the operand is used as an activation code and has an invalid
149// value). For now, this should be fine because it just means we're not testing what we think we're
150// testing in certain cases; but we can handwave this and assume we're probabilistically likely to
151// exercise the validation code over the span of the entire test set and operand space.
152//
153// Aborts if the specified operand type is an extension type or OEM type.
154static void becomeConstantCopy(Model* model, Operand* operand) {
155 // sizeOfData will abort if the specified type is an extension type or OEM type.
156 const size_t sizeOfOperand = sizeOfData(*operand);
157 EXPECT_NE(sizeOfOperand, size_t(0));
158 operand->location.poolIndex = 0;
159 operand->location.offset = 0;
160 operand->location.length = sizeOfOperand;
161 if (model->operandValues.size() < sizeOfOperand) {
162 model->operandValues.resize(sizeOfOperand);
163 }
164}
165
166// The sizeForBinder() functions estimate the size of the
167// representation of a value when sent to binder. It's probably a bit
168// of an under-estimate, because we don't know the size of the
169// metadata in the binder format (e.g., representation of the size of
170// a vector); but at least it adds up "big" things like vector
171// contents. However, it doesn't treat inter-field or end-of-struct
172// padding in a methodical way -- there's no attempt to be consistent
173// in whether or not padding in the native (C++) representation
174// contributes to the estimated size for the binder representation;
175// and there's no attempt to understand what padding (if any) is
176// needed in the binder representation.
177//
178// This assumes that non-metadata uses a fixed length encoding (e.g.,
179// a uint32_t is always encoded in sizeof(uint32_t) bytes, rather than
180// using an encoding whose length is related to the magnitude of the
181// encoded value).
182
183template <typename Type>
184static size_t sizeForBinder(const Type& val) {
185 static_assert(std::is_trivially_copyable_v<std::remove_reference_t<Type>>,
186 "expected a trivially copyable type");
187 return sizeof(val);
188}
189
190template <typename Type>
191static size_t sizeForBinder(const hidl_vec<Type>& vec) {
192 return std::accumulate(vec.begin(), vec.end(), 0,
193 [](size_t acc, const Type& x) { return acc + sizeForBinder(x); });
194}
195
196template <>
197size_t sizeForBinder(const SymmPerChannelQuantParams& symmPerChannelQuantParams) {
198 size_t size = 0;
199
200 size += sizeForBinder(symmPerChannelQuantParams.scales);
201 size += sizeForBinder(symmPerChannelQuantParams.channelDim);
202
203 return size;
204}
205
206template <>
207size_t sizeForBinder(const V1_2::Operand::ExtraParams& extraParams) {
208 using Discriminator = V1_2::Operand::ExtraParams::hidl_discriminator;
209 switch (extraParams.getDiscriminator()) {
210 case Discriminator::none:
211 return 0;
212 case Discriminator::channelQuant:
213 return sizeForBinder(extraParams.channelQuant());
214 case Discriminator::extension:
215 return sizeForBinder(extraParams.extension());
216 }
217 LOG(FATAL) << "Unrecognized extraParams enum: "
218 << static_cast<int>(extraParams.getDiscriminator());
219 return 0;
220}
221
222template <>
223size_t sizeForBinder(const Operand& operand) {
224 size_t size = 0;
225
226 size += sizeForBinder(operand.type);
227 size += sizeForBinder(operand.dimensions);
228 size += sizeForBinder(operand.numberOfConsumers);
229 size += sizeForBinder(operand.scale);
230 size += sizeForBinder(operand.zeroPoint);
231 size += sizeForBinder(operand.lifetime);
232 size += sizeForBinder(operand.location);
233 size += sizeForBinder(operand.extraParams);
234
235 return size;
236}
237
238template <>
239size_t sizeForBinder(const Operation& operation) {
240 size_t size = 0;
241
242 size += sizeForBinder(operation.type);
243 size += sizeForBinder(operation.inputs);
244 size += sizeForBinder(operation.outputs);
245
246 return size;
247}
248
249template <>
250size_t sizeForBinder(const hidl_string& name) {
251 return name.size();
252}
253
254template <>
255size_t sizeForBinder(const hidl_memory& memory) {
256 // This is just a guess.
257
258 size_t size = 0;
259
260 if (const native_handle_t* handle = memory.handle()) {
261 size += sizeof(*handle);
262 size += sizeof(handle->data[0] * (handle->numFds + handle->numInts));
263 }
264 size += sizeForBinder(memory.name());
265
266 return size;
267}
268
269template <>
270size_t sizeForBinder(const Subgraph& subgraph) {
271 size_t size = 0;
272
273 size += sizeForBinder(subgraph.operands);
274 size += sizeForBinder(subgraph.operations);
275 size += sizeForBinder(subgraph.inputIndexes);
276 size += sizeForBinder(subgraph.outputIndexes);
277
278 return size;
279}
280
281template <>
282size_t sizeForBinder(const V1_2::Model::ExtensionNameAndPrefix& extensionNameToPrefix) {
283 size_t size = 0;
284
285 size += sizeForBinder(extensionNameToPrefix.name);
286 size += sizeForBinder(extensionNameToPrefix.prefix);
287
288 return size;
289}
290
291template <>
292size_t sizeForBinder(const Model& model) {
293 size_t size = 0;
294
295 size += sizeForBinder(model.main);
296 size += sizeForBinder(model.referenced);
297 size += sizeForBinder(model.operandValues);
298 size += sizeForBinder(model.pools);
299 size += sizeForBinder(model.relaxComputationFloat32toFloat16);
300 size += sizeForBinder(model.extensionNameToPrefix);
301
302 return size;
303}
304
305// https://developer.android.com/reference/android/os/TransactionTooLargeException.html
306//
307// "The Binder transaction buffer has a limited fixed size,
308// currently 1Mb, which is shared by all transactions in progress
309// for the process."
310//
311// Will our representation fit under this limit? There are two complications:
312// - Our representation size is just approximate (see sizeForBinder()).
313// - This object may not be the only occupant of the Binder transaction buffer.
314// So we'll be very conservative: We want the representation size to be no
315// larger than half the transaction buffer size.
316//
317// If our representation grows large enough that it still fits within
318// the transaction buffer but combined with other transactions may
319// exceed the buffer size, then we may see intermittent HAL transport
320// errors.
321static bool exceedsBinderSizeLimit(size_t representationSize) {
322 // Instead of using this fixed buffer size, we might instead be able to use
323 // ProcessState::self()->getMmapSize(). However, this has a potential
324 // problem: The binder/mmap size of the current process does not necessarily
325 // indicate the binder/mmap size of the service (i.e., the other process).
326 // The only way it would be a good indication is if both the current process
327 // and the service use the default size.
328 static const size_t kHalfBufferSize = 1024 * 1024 / 2;
329
330 return representationSize > kHalfBufferSize;
331}
332
333///////////////////////// VALIDATE EXECUTION ORDER ////////////////////////////
334
335static void mutateExecutionOrderTest(const sp<IDevice>& device, const Model& model) {
336 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
337 const Operation& operationObj = model.main.operations[operation];
338 for (uint32_t input : operationObj.inputs) {
339 if (model.main.operands[input].lifetime == OperandLifeTime::TEMPORARY_VARIABLE ||
340 model.main.operands[input].lifetime == OperandLifeTime::SUBGRAPH_OUTPUT) {
341 // This operation reads an operand written by some
342 // other operation. Move this operation to the
343 // beginning of the sequence, ensuring that it reads
344 // the operand before that operand is written, thereby
345 // violating execution order rules.
346 const std::string message = "mutateExecutionOrderTest: operation " +
347 std::to_string(operation) + " is a reader";
348 validate(device, message, model,
349 [operation](Model* model, ExecutionPreference*, Priority*) {
350 auto& operations = model->main.operations;
351 std::rotate(operations.begin(), operations.begin() + operation,
352 operations.begin() + operation + 1);
353 });
354 break; // only need to do this once per operation
355 }
356 }
357 for (uint32_t output : operationObj.outputs) {
358 if (model.main.operands[output].numberOfConsumers > 0) {
359 // This operation writes an operand read by some other
360 // operation. Move this operation to the end of the
361 // sequence, ensuring that it writes the operand after
362 // that operand is read, thereby violating execution
363 // order rules.
364 const std::string message = "mutateExecutionOrderTest: operation " +
365 std::to_string(operation) + " is a writer";
366 validate(device, message, model,
367 [operation](Model* model, ExecutionPreference*, Priority*) {
368 auto& operations = model->main.operations;
369 std::rotate(operations.begin() + operation,
370 operations.begin() + operation + 1, operations.end());
371 });
372 break; // only need to do this once per operation
373 }
374 }
375 }
376}
377
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100378///////////////////////// VALIDATE MODEL OPERAND TYPE /////////////////////////
379
380static const uint32_t invalidOperandTypes[] = {
381 static_cast<uint32_t>(OperandTypeRange::FUNDAMENTAL_MIN) - 1,
382 static_cast<uint32_t>(OperandTypeRange::FUNDAMENTAL_MAX) + 1,
383 static_cast<uint32_t>(OperandTypeRange::OEM_MIN) - 1,
384 static_cast<uint32_t>(OperandTypeRange::OEM_MAX) + 1,
385};
386
387static void mutateOperandTypeTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000388 for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100389 for (uint32_t invalidOperandType : invalidOperandTypes) {
390 const std::string message = "mutateOperandTypeTest: operand " +
391 std::to_string(operand) + " set to value " +
392 std::to_string(invalidOperandType);
Michael Butler68a6de72020-03-11 18:45:45 -0700393 validate(device, message, model,
394 [operand, invalidOperandType](Model* model, ExecutionPreference*, Priority*) {
395 model->main.operands[operand].type =
396 static_cast<OperandType>(invalidOperandType);
397 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100398 }
399 }
400}
401
402///////////////////////// VALIDATE OPERAND RANK /////////////////////////
403
404static uint32_t getInvalidRank(OperandType type) {
405 switch (type) {
406 case OperandType::FLOAT16:
407 case OperandType::FLOAT32:
408 case OperandType::INT32:
409 case OperandType::UINT32:
410 case OperandType::BOOL:
411 return 1;
412 case OperandType::TENSOR_BOOL8:
413 case OperandType::TENSOR_FLOAT16:
414 case OperandType::TENSOR_FLOAT32:
415 case OperandType::TENSOR_INT32:
416 case OperandType::TENSOR_QUANT8_ASYMM:
417 case OperandType::TENSOR_QUANT8_SYMM:
418 case OperandType::TENSOR_QUANT16_ASYMM:
419 case OperandType::TENSOR_QUANT16_SYMM:
420 case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
421 return 0;
422 default:
423 return 0;
424 }
425}
426
427static void mutateOperandRankTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000428 for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
429 const uint32_t invalidRank = getInvalidRank(model.main.operands[operand].type);
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100430 if (invalidRank == 0) {
431 continue;
432 }
433 const std::string message = "mutateOperandRankTest: operand " + std::to_string(operand) +
434 " has rank of " + std::to_string(invalidRank);
Michael Butler68a6de72020-03-11 18:45:45 -0700435 validate(device, message, model,
436 [operand, invalidRank](Model* model, ExecutionPreference*, Priority*) {
437 model->main.operands[operand].dimensions =
438 std::vector<uint32_t>(invalidRank, 0);
439 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100440 }
441}
442
443///////////////////////// VALIDATE OPERAND SCALE /////////////////////////
444
445static float getInvalidScale(OperandType type) {
446 switch (type) {
447 case OperandType::FLOAT16:
448 case OperandType::FLOAT32:
449 case OperandType::INT32:
450 case OperandType::UINT32:
451 case OperandType::BOOL:
452 case OperandType::TENSOR_BOOL8:
453 case OperandType::TENSOR_FLOAT16:
454 case OperandType::TENSOR_FLOAT32:
455 case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
Slava Shklyaev0fff59b2020-01-31 15:14:24 +0000456 case OperandType::SUBGRAPH:
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100457 return 1.0f;
458 case OperandType::TENSOR_INT32:
459 return -1.0f;
460 case OperandType::TENSOR_QUANT8_SYMM:
461 case OperandType::TENSOR_QUANT8_ASYMM:
462 case OperandType::TENSOR_QUANT16_ASYMM:
463 case OperandType::TENSOR_QUANT16_SYMM:
464 return 0.0f;
465 default:
466 return 0.0f;
467 }
468}
469
470static void mutateOperandScaleTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000471 for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
472 const float invalidScale = getInvalidScale(model.main.operands[operand].type);
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100473 const std::string message = "mutateOperandScaleTest: operand " + std::to_string(operand) +
474 " has scale of " + std::to_string(invalidScale);
Michael Butler68a6de72020-03-11 18:45:45 -0700475 validate(device, message, model,
476 [operand, invalidScale](Model* model, ExecutionPreference*, Priority*) {
477 model->main.operands[operand].scale = invalidScale;
478 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100479 }
480}
481
482///////////////////////// VALIDATE OPERAND ZERO POINT /////////////////////////
483
484static std::vector<int32_t> getInvalidZeroPoints(OperandType type) {
485 switch (type) {
486 case OperandType::FLOAT16:
487 case OperandType::FLOAT32:
488 case OperandType::INT32:
489 case OperandType::UINT32:
490 case OperandType::BOOL:
491 case OperandType::TENSOR_BOOL8:
492 case OperandType::TENSOR_FLOAT16:
493 case OperandType::TENSOR_FLOAT32:
494 case OperandType::TENSOR_INT32:
495 case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
Slava Shklyaev0fff59b2020-01-31 15:14:24 +0000496 case OperandType::SUBGRAPH:
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100497 return {1};
498 case OperandType::TENSOR_QUANT8_ASYMM:
499 return {-1, 256};
500 case OperandType::TENSOR_QUANT8_SYMM:
501 return {-129, -1, 1, 128};
502 case OperandType::TENSOR_QUANT16_ASYMM:
503 return {-1, 65536};
504 case OperandType::TENSOR_QUANT16_SYMM:
505 return {-32769, -1, 1, 32768};
506 default:
507 return {};
508 }
509}
510
511static void mutateOperandZeroPointTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000512 for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100513 const std::vector<int32_t> invalidZeroPoints =
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000514 getInvalidZeroPoints(model.main.operands[operand].type);
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100515 for (int32_t invalidZeroPoint : invalidZeroPoints) {
516 const std::string message = "mutateOperandZeroPointTest: operand " +
517 std::to_string(operand) + " has zero point of " +
518 std::to_string(invalidZeroPoint);
Michael Butler68a6de72020-03-11 18:45:45 -0700519 validate(device, message, model,
520 [operand, invalidZeroPoint](Model* model, ExecutionPreference*, Priority*) {
521 model->main.operands[operand].zeroPoint = invalidZeroPoint;
522 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100523 }
524 }
525}
526
David Gross6174f002018-05-14 12:23:04 -0700527///////////////////////// VALIDATE OPERAND LIFETIME /////////////////////////////////////////////
528
529static std::vector<OperandLifeTime> getInvalidLifeTimes(const Model& model, size_t modelSize,
530 const Operand& operand) {
531 // TODO: Support OperandLifeTime::CONSTANT_REFERENCE as an invalid lifetime
532 // TODO: Support OperandLifeTime::NO_VALUE as an invalid lifetime
533
534 // Ways to get an invalid lifetime:
535 // - change whether a lifetime means an operand should have a writer
536 std::vector<OperandLifeTime> ret;
537 switch (operand.lifetime) {
538 case OperandLifeTime::SUBGRAPH_OUTPUT:
539 case OperandLifeTime::TEMPORARY_VARIABLE:
540 ret = {
541 OperandLifeTime::SUBGRAPH_INPUT,
542 OperandLifeTime::CONSTANT_COPY,
543 };
544 break;
545 case OperandLifeTime::CONSTANT_COPY:
546 case OperandLifeTime::CONSTANT_REFERENCE:
547 case OperandLifeTime::SUBGRAPH_INPUT:
548 ret = {
549 OperandLifeTime::TEMPORARY_VARIABLE,
550 OperandLifeTime::SUBGRAPH_OUTPUT,
551 };
552 break;
553 case OperandLifeTime::NO_VALUE:
554 // Not enough information to know whether
555 // TEMPORARY_VARIABLE or CONSTANT_COPY would be invalid --
556 // is this operand written (then CONSTANT_COPY would be
557 // invalid) or not (then TEMPORARY_VARIABLE would be
558 // invalid)?
559 break;
560 case OperandLifeTime::SUBGRAPH:
561 break;
562 default:
563 ADD_FAILURE();
564 break;
565 }
566
567 const size_t operandSize = sizeOfData(operand); // will be zero if shape is unknown
568 if (!operandSize ||
569 exceedsBinderSizeLimit(modelSize + constantCopyExtraSize(model, operandSize))) {
570 // Unknown size or too-large size
571 ret.erase(std::remove(ret.begin(), ret.end(), OperandLifeTime::CONSTANT_COPY), ret.end());
572 }
573
574 return ret;
575}
576
577static void mutateOperandLifeTimeTest(const sp<IDevice>& device, const Model& model) {
578 const size_t modelSize = sizeForBinder(model);
579 for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
580 const std::vector<OperandLifeTime> invalidLifeTimes =
581 getInvalidLifeTimes(model, modelSize, model.main.operands[operand]);
582 for (OperandLifeTime invalidLifeTime : invalidLifeTimes) {
583 const std::string message = "mutateOperandLifetimeTest: operand " +
584 std::to_string(operand) + " has lifetime " +
585 toString(invalidLifeTime) + " instead of lifetime " +
586 toString(model.main.operands[operand].lifetime);
587 validate(device, message, model,
588 [operand, invalidLifeTime](Model* model, ExecutionPreference*, Priority*) {
589 static const DataLocation kZeroDataLocation = {};
590 Operand& operandObj = model->main.operands[operand];
591 switch (operandObj.lifetime) {
592 case OperandLifeTime::SUBGRAPH_INPUT: {
593 hidl_vec_remove(&model->main.inputIndexes, uint32_t(operand));
594 break;
595 }
596 case OperandLifeTime::SUBGRAPH_OUTPUT: {
597 hidl_vec_remove(&model->main.outputIndexes, uint32_t(operand));
598 break;
599 }
600 default:
601 break;
602 }
603 operandObj.lifetime = invalidLifeTime;
604 operandObj.location = kZeroDataLocation;
605 switch (invalidLifeTime) {
606 case OperandLifeTime::CONSTANT_COPY: {
607 becomeConstantCopy(model, &operandObj);
608 break;
609 }
610 case OperandLifeTime::SUBGRAPH_INPUT:
611 hidl_vec_push_back(&model->main.inputIndexes, uint32_t(operand));
612 break;
613 case OperandLifeTime::SUBGRAPH_OUTPUT:
614 hidl_vec_push_back(&model->main.outputIndexes, uint32_t(operand));
615 break;
616 default:
617 break;
618 }
619 });
620 }
621 }
622}
623
624///////////////////////// VALIDATE OPERAND INPUT-or-OUTPUT //////////////////////////////////////
625
626static std::optional<OperandLifeTime> getInputOutputLifeTime(const Model& model, size_t modelSize,
627 const Operand& operand) {
628 // Ways to get an invalid lifetime (with respect to model inputIndexes and outputIndexes):
629 // - change whether a lifetime means an operand is a model input, a model output, or neither
630 // - preserve whether or not a lifetime means an operand should have a writer
631 switch (operand.lifetime) {
632 case OperandLifeTime::CONSTANT_COPY:
633 case OperandLifeTime::CONSTANT_REFERENCE:
634 return OperandLifeTime::SUBGRAPH_INPUT;
635 case OperandLifeTime::SUBGRAPH_INPUT: {
636 const size_t operandSize = sizeOfData(operand); // will be zero if shape is unknown
637 if (!operandSize ||
638 exceedsBinderSizeLimit(modelSize + constantCopyExtraSize(model, operandSize))) {
639 // Unknown size or too-large size
640 break;
641 }
642 return OperandLifeTime::CONSTANT_COPY;
643 }
644 case OperandLifeTime::SUBGRAPH_OUTPUT:
645 return OperandLifeTime::TEMPORARY_VARIABLE;
646 case OperandLifeTime::TEMPORARY_VARIABLE:
647 return OperandLifeTime::SUBGRAPH_OUTPUT;
648 case OperandLifeTime::NO_VALUE:
649 // Not enough information to know whether
650 // TEMPORARY_VARIABLE or CONSTANT_COPY would be an
651 // appropriate choice -- is this operand written (then
652 // TEMPORARY_VARIABLE would be appropriate) or not (then
653 // CONSTANT_COPY would be appropriate)?
654 break;
655 case OperandLifeTime::SUBGRAPH:
656 break;
657 default:
658 ADD_FAILURE();
659 break;
660 }
661
662 return std::nullopt;
663}
664
665static void mutateOperandInputOutputTest(const sp<IDevice>& device, const Model& model) {
666 const size_t modelSize = sizeForBinder(model);
667 for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
668 const std::optional<OperandLifeTime> changedLifeTime =
669 getInputOutputLifeTime(model, modelSize, model.main.operands[operand]);
670 if (changedLifeTime) {
671 const std::string message = "mutateOperandInputOutputTest: operand " +
672 std::to_string(operand) + " has lifetime " +
673 toString(*changedLifeTime) + " instead of lifetime " +
674 toString(model.main.operands[operand].lifetime);
675 validate(device, message, model,
676 [operand, changedLifeTime](Model* model, ExecutionPreference*, Priority*) {
677 static const DataLocation kZeroDataLocation = {};
678 Operand& operandObj = model->main.operands[operand];
679 operandObj.lifetime = *changedLifeTime;
680 operandObj.location = kZeroDataLocation;
681 if (*changedLifeTime == OperandLifeTime::CONSTANT_COPY) {
682 becomeConstantCopy(model, &operandObj);
683 }
684 });
685 }
686 }
687}
688
689///////////////////////// VALIDATE OPERAND NUMBER OF CONSUMERS //////////////////////////////////
690
691static std::vector<uint32_t> getInvalidNumberOfConsumers(uint32_t numberOfConsumers) {
692 if (numberOfConsumers == 0) {
693 return {1};
694 } else {
695 return {numberOfConsumers - 1, numberOfConsumers + 1};
696 }
697}
698
699static void mutateOperandNumberOfConsumersTest(const sp<IDevice>& device, const Model& model) {
700 for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
701 const std::vector<uint32_t> invalidNumberOfConsumersVec =
702 getInvalidNumberOfConsumers(model.main.operands[operand].numberOfConsumers);
703 for (uint32_t invalidNumberOfConsumers : invalidNumberOfConsumersVec) {
704 const std::string message =
705 "mutateOperandNumberOfConsumersTest: operand " + std::to_string(operand) +
706 " numberOfConsumers = " + std::to_string(invalidNumberOfConsumers);
707 validate(device, message, model,
708 [operand, invalidNumberOfConsumers](Model* model, ExecutionPreference*,
709 Priority*) {
710 model->main.operands[operand].numberOfConsumers = invalidNumberOfConsumers;
711 });
712 }
713 }
714}
715
716///////////////////////// VALIDATE OPERAND NUMBER OF WRITERS ////////////////////////////////////
717
718static void mutateOperandAddWriterTest(const sp<IDevice>& device, const Model& model) {
719 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
720 for (size_t badOutputNum = 0;
721 badOutputNum < model.main.operations[operation].outputs.size(); ++badOutputNum) {
722 const uint32_t outputOperandIndex =
723 model.main.operations[operation].outputs[badOutputNum];
724 const std::string message = "mutateOperandAddWriterTest: operation " +
725 std::to_string(operation) + " writes to " +
726 std::to_string(outputOperandIndex);
727 // We'll insert a copy of the operation, all of whose
728 // OTHER output operands are newly-created -- i.e.,
729 // there'll only be a duplicate write of ONE of that
730 // operation's output operands.
731 validate(device, message, model,
732 [operation, badOutputNum](Model* model, ExecutionPreference*, Priority*) {
733 Operation newOperation = model->main.operations[operation];
734 for (uint32_t input : newOperation.inputs) {
735 ++model->main.operands[input].numberOfConsumers;
736 }
737 for (size_t outputNum = 0; outputNum < newOperation.outputs.size();
738 ++outputNum) {
739 if (outputNum == badOutputNum) continue;
740
741 Operand operandValue =
742 model->main.operands[newOperation.outputs[outputNum]];
743 operandValue.numberOfConsumers = 0;
744 if (operandValue.lifetime == OperandLifeTime::SUBGRAPH_OUTPUT) {
745 operandValue.lifetime = OperandLifeTime::TEMPORARY_VARIABLE;
746 } else {
747 ASSERT_EQ(operandValue.lifetime,
748 OperandLifeTime::TEMPORARY_VARIABLE);
749 }
750 newOperation.outputs[outputNum] =
751 hidl_vec_push_back(&model->main.operands, operandValue);
752 }
753 // Where do we insert the extra writer (a new
754 // operation)? It has to be later than all the
755 // writers of its inputs. The easiest thing to do
756 // is to insert it at the end of the operation
757 // sequence.
758 hidl_vec_push_back(&model->main.operations, newOperation);
759 });
760 }
761 }
762}
763
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100764///////////////////////// VALIDATE EXTRA ??? /////////////////////////
765
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100766// TODO: Operand::location
767
768///////////////////////// VALIDATE OPERATION OPERAND TYPE /////////////////////////
769
770static void mutateOperand(Operand* operand, OperandType type) {
771 Operand newOperand = *operand;
772 newOperand.type = type;
773 switch (type) {
774 case OperandType::FLOAT16:
775 case OperandType::FLOAT32:
776 case OperandType::INT32:
777 case OperandType::UINT32:
778 case OperandType::BOOL:
779 newOperand.dimensions = hidl_vec<uint32_t>();
780 newOperand.scale = 0.0f;
781 newOperand.zeroPoint = 0;
782 break;
783 case OperandType::TENSOR_BOOL8:
784 case OperandType::TENSOR_FLOAT16:
785 case OperandType::TENSOR_FLOAT32:
786 newOperand.dimensions =
787 operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec<uint32_t>({1});
788 newOperand.scale = 0.0f;
789 newOperand.zeroPoint = 0;
790 break;
791 case OperandType::TENSOR_INT32:
792 newOperand.dimensions =
793 operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec<uint32_t>({1});
794 newOperand.zeroPoint = 0;
795 break;
796 case OperandType::TENSOR_QUANT8_ASYMM:
797 case OperandType::TENSOR_QUANT8_SYMM:
798 case OperandType::TENSOR_QUANT16_ASYMM:
799 case OperandType::TENSOR_QUANT16_SYMM:
800 newOperand.dimensions =
801 operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec<uint32_t>({1});
802 newOperand.scale = operand->scale != 0.0f ? operand->scale : 1.0f;
803 break;
804 case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL: {
805 newOperand.dimensions =
806 operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec<uint32_t>({1});
807 newOperand.scale = 0.0f;
808 newOperand.zeroPoint = 0;
809
810 SymmPerChannelQuantParams channelQuant;
811 channelQuant.channelDim = 0;
812 channelQuant.scales = hidl_vec<float>(
813 operand->dimensions.size() > 0 ? static_cast<size_t>(operand->dimensions[0])
814 : 0);
815 for (size_t i = 0; i < channelQuant.scales.size(); ++i) {
816 channelQuant.scales[i] = 1.0f;
817 }
818 newOperand.extraParams.channelQuant(std::move(channelQuant));
819 } break;
820 case OperandType::OEM:
821 case OperandType::TENSOR_OEM_BYTE:
822 default:
823 break;
824 }
825 *operand = newOperand;
826}
827
828static bool mutateOperationOperandTypeSkip(size_t operand, OperandType type, const Model& model) {
829 // Do not test OEM types
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000830 if (type == model.main.operands[operand].type || type == OperandType::OEM ||
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100831 type == OperandType::TENSOR_OEM_BYTE) {
832 return true;
833 }
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000834 for (const Operation& operation : model.main.operations) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100835 // Skip mutateOperationOperandTypeTest for the following operations.
836 // - LSH_PROJECTION's second argument is allowed to have any type.
837 // - ARGMIN and ARGMAX's first argument can be any of
838 // TENSOR_(FLOAT16|FLOAT32|INT32|QUANT8_ASYMM).
839 // - CAST's argument can be any of TENSOR_(FLOAT16|FLOAT32|INT32|QUANT8_ASYMM).
840 // - RANDOM_MULTINOMIAL's argument can be either TENSOR_FLOAT16 or TENSOR_FLOAT32.
841 // - DEQUANTIZE input can be any of
Lev Proleevae643ae2019-12-05 16:57:30 +0000842 // TENSOR_(QUANT8_ASYMM|QUANT8_ASYMM_SIGNED|QUANT8_SYMM|QUANT8_SYMM_PER_CHANNEL),
843 // output can be of either TENSOR_FLOAT16 or TENSOR_FLOAT32.
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100844 // - QUANTIZE input can be either TENSOR_FLOAT16 or TENSOR_FLOAT32
845 // - CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
846 // - DEPTHWISE_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
847 // - GROUPED_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
848 // - TRANSPOSE_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
Lev Proleevda779f32020-01-02 17:49:03 +0000849 // - AXIS_ALIGNED_BBOX_TRANSFORM bounding boxes (arg 1) can be of
850 // TENSOR_QUANT8_ASYMM or TENSOR_QUANT8_ASYMM_SIGNED.
Lev Proleev53a51cb2020-01-20 18:54:46 +0000851 // - RANK's input can have any TENSOR_* type.
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100852 switch (operation.type) {
853 case OperationType::LSH_PROJECTION: {
854 if (operand == operation.inputs[1]) {
855 return true;
856 }
857 } break;
858 case OperationType::CAST:
859 case OperationType::ARGMAX:
860 case OperationType::ARGMIN: {
861 if (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32 ||
Przemyslaw Szczepaniak2326dd12019-11-29 09:49:17 +0000862 type == OperandType::TENSOR_INT32 || type == OperandType::TENSOR_QUANT8_ASYMM ||
863 type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100864 return true;
865 }
866 } break;
Przemyslaw Szczepaniak90fc2cc2019-11-25 11:04:19 +0000867 case OperationType::QUANTIZE: {
868 if (operand == operation.inputs[0] &&
869 (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32)) {
870 return true;
871 }
872 if (operand == operation.outputs[0] &&
873 (type == OperandType::TENSOR_QUANT8_ASYMM ||
874 type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED)) {
875 return true;
876 }
877 } break;
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100878 case OperationType::RANDOM_MULTINOMIAL: {
879 if (operand == operation.inputs[0] &&
880 (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32)) {
881 return true;
882 }
883 } break;
884 case OperationType::DEQUANTIZE: {
885 if (operand == operation.inputs[0] &&
886 (type == OperandType::TENSOR_QUANT8_ASYMM ||
Lev Proleevae643ae2019-12-05 16:57:30 +0000887 type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED ||
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100888 type == OperandType::TENSOR_QUANT8_SYMM ||
889 type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL)) {
890 return true;
891 }
892 if (operand == operation.outputs[0] &&
893 (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32)) {
894 return true;
895 }
896 } break;
897 case OperationType::TRANSPOSE_CONV_2D:
898 case OperationType::GROUPED_CONV_2D:
899 case OperationType::DEPTHWISE_CONV_2D:
900 case OperationType::CONV_2D: {
901 if (operand == operation.inputs[1] &&
902 (type == OperandType::TENSOR_QUANT8_ASYMM ||
903 type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL)) {
904 return true;
905 }
906 } break;
Lev Proleevda779f32020-01-02 17:49:03 +0000907 case OperationType::AXIS_ALIGNED_BBOX_TRANSFORM: {
908 if (operand == operation.inputs[1] &&
909 (type == OperandType::TENSOR_QUANT8_ASYMM ||
910 type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED)) {
911 return true;
912 }
913 } break;
Lev Proleev53a51cb2020-01-20 18:54:46 +0000914 case OperationType::RANK: {
915 if (operand == operation.inputs[0] &&
916 (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32 ||
917 type == OperandType::TENSOR_INT32 ||
918 type == OperandType::TENSOR_QUANT8_ASYMM ||
919 type == OperandType::TENSOR_QUANT16_SYMM ||
920 type == OperandType::TENSOR_BOOL8 ||
921 type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL ||
922 type == OperandType::TENSOR_QUANT16_ASYMM ||
923 type == OperandType::TENSOR_QUANT8_SYMM ||
924 type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED)) {
925 return true;
926 }
927 } break;
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100928 default:
929 break;
930 }
931 }
932 return false;
933}
934
935static void mutateOperationOperandTypeTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000936 for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100937 for (OperandType invalidOperandType : hidl_enum_range<OperandType>{}) {
938 if (mutateOperationOperandTypeSkip(operand, invalidOperandType, model)) {
939 continue;
940 }
941 const std::string message = "mutateOperationOperandTypeTest: operand " +
942 std::to_string(operand) + " set to type " +
943 toString(invalidOperandType);
Michael Butler68a6de72020-03-11 18:45:45 -0700944 validate(device, message, model,
945 [operand, invalidOperandType](Model* model, ExecutionPreference*, Priority*) {
946 mutateOperand(&model->main.operands[operand], invalidOperandType);
947 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100948 }
949 }
950}
951
952///////////////////////// VALIDATE MODEL OPERATION TYPE /////////////////////////
953
954static const uint32_t invalidOperationTypes[] = {
955 static_cast<uint32_t>(OperationTypeRange::FUNDAMENTAL_MAX) + 1,
956 static_cast<uint32_t>(OperationTypeRange::OEM_MIN) - 1,
957 static_cast<uint32_t>(OperationTypeRange::OEM_MAX) + 1,
958};
959
960static void mutateOperationTypeTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000961 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100962 for (uint32_t invalidOperationType : invalidOperationTypes) {
963 const std::string message = "mutateOperationTypeTest: operation " +
964 std::to_string(operation) + " set to value " +
965 std::to_string(invalidOperationType);
Michael Butler68a6de72020-03-11 18:45:45 -0700966 validate(device, message, model,
967 [operation, invalidOperationType](Model* model, ExecutionPreference*,
968 Priority*) {
969 model->main.operations[operation].type =
970 static_cast<OperationType>(invalidOperationType);
971 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100972 }
973 }
974}
975
976///////////////////////// VALIDATE MODEL OPERATION INPUT OPERAND INDEX /////////////////////////
977
978static void mutateOperationInputOperandIndexTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000979 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
980 const uint32_t invalidOperand = model.main.operands.size();
981 for (size_t input = 0; input < model.main.operations[operation].inputs.size(); ++input) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100982 const std::string message = "mutateOperationInputOperandIndexTest: operation " +
983 std::to_string(operation) + " input " +
984 std::to_string(input);
Michael Butler68a6de72020-03-11 18:45:45 -0700985 validate(device, message, model,
986 [operation, input, invalidOperand](Model* model, ExecutionPreference*,
987 Priority*) {
988 model->main.operations[operation].inputs[input] = invalidOperand;
989 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +0100990 }
991 }
992}
993
994///////////////////////// VALIDATE MODEL OPERATION OUTPUT OPERAND INDEX /////////////////////////
995
996static void mutateOperationOutputOperandIndexTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +0000997 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
998 const uint32_t invalidOperand = model.main.operands.size();
999 for (size_t output = 0; output < model.main.operations[operation].outputs.size();
1000 ++output) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001001 const std::string message = "mutateOperationOutputOperandIndexTest: operation " +
1002 std::to_string(operation) + " output " +
1003 std::to_string(output);
Michael Butler68a6de72020-03-11 18:45:45 -07001004 validate(device, message, model,
1005 [operation, output, invalidOperand](Model* model, ExecutionPreference*,
1006 Priority*) {
1007 model->main.operations[operation].outputs[output] = invalidOperand;
1008 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001009 }
1010 }
1011}
1012
David Gross6174f002018-05-14 12:23:04 -07001013///////////////////////// VALIDATE MODEL OPERANDS WRITTEN ///////////////////////////////////////
1014
1015static void mutateOperationRemoveWriteTest(const sp<IDevice>& device, const Model& model) {
1016 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
1017 for (size_t outputNum = 0; outputNum < model.main.operations[operation].outputs.size();
1018 ++outputNum) {
1019 const uint32_t outputOperandIndex = model.main.operations[operation].outputs[outputNum];
1020 if (model.main.operands[outputOperandIndex].numberOfConsumers > 0) {
1021 const std::string message = "mutateOperationRemoveWriteTest: operation " +
1022 std::to_string(operation) + " writes to " +
1023 std::to_string(outputOperandIndex);
1024 validate(device, message, model,
1025 [operation, outputNum](Model* model, ExecutionPreference*, Priority*) {
1026 uint32_t& outputOperandIndex =
1027 model->main.operations[operation].outputs[outputNum];
1028 Operand operandValue = model->main.operands[outputOperandIndex];
1029 operandValue.numberOfConsumers = 0;
1030 if (operandValue.lifetime == OperandLifeTime::SUBGRAPH_OUTPUT) {
1031 operandValue.lifetime = OperandLifeTime::TEMPORARY_VARIABLE;
1032 } else {
1033 ASSERT_EQ(operandValue.lifetime,
1034 OperandLifeTime::TEMPORARY_VARIABLE);
1035 }
1036 outputOperandIndex =
1037 hidl_vec_push_back(&model->main.operands, operandValue);
1038 });
1039 }
1040 }
1041 }
1042}
1043
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001044///////////////////////// REMOVE OPERAND FROM EVERYTHING /////////////////////////
1045
1046static void removeValueAndDecrementGreaterValues(hidl_vec<uint32_t>* vec, uint32_t value) {
1047 if (vec) {
1048 // remove elements matching "value"
1049 auto last = std::remove(vec->begin(), vec->end(), value);
1050 vec->resize(std::distance(vec->begin(), last));
1051
1052 // decrement elements exceeding "value"
1053 std::transform(vec->begin(), vec->end(), vec->begin(),
1054 [value](uint32_t v) { return v > value ? v-- : v; });
1055 }
1056}
1057
1058static void removeOperand(Model* model, uint32_t index) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +00001059 hidl_vec_removeAt(&model->main.operands, index);
1060 for (Operation& operation : model->main.operations) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001061 removeValueAndDecrementGreaterValues(&operation.inputs, index);
1062 removeValueAndDecrementGreaterValues(&operation.outputs, index);
1063 }
Slava Shklyaevf8124a82019-12-13 12:24:35 +00001064 removeValueAndDecrementGreaterValues(&model->main.inputIndexes, index);
1065 removeValueAndDecrementGreaterValues(&model->main.outputIndexes, index);
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001066}
1067
Slava Shklyaevaf1c77d2020-04-16 14:58:55 +01001068static bool removeOperandSkip(size_t operandIndex, const Model& model) {
1069 const Operand& operand = model.main.operands[operandIndex];
1070 if (operand.numberOfConsumers == 0) {
1071 // Removing an unused operand has no effect.
1072 return true;
1073 }
Slava Shklyaevf8124a82019-12-13 12:24:35 +00001074 for (const Operation& operation : model.main.operations) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001075 // Skip removeOperandTest for the following operations.
1076 // - SPLIT's outputs are not checked during prepareModel.
1077 if (operation.type == OperationType::SPLIT) {
Slava Shklyaevaf1c77d2020-04-16 14:58:55 +01001078 for (const size_t index : operation.outputs) {
1079 if (index == operandIndex) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001080 return true;
1081 }
1082 }
1083 }
Lev Proleev17689aa2020-01-30 17:40:13 +00001084 // BIDIRECTIONAL_SEQUENCE_LSTM and BIDIRECTIONAL_SEQUENCE_RNN can have
1085 // either one, two, three or four outputs depending on their
1086 // mergeOutputs parameter and if state outputs are provided.
1087 // UNIDIRECTIONAL_SEQUENCE_LSTM and UNIDIRECTIONAL_SEQUENCE_RNN can have
1088 // either one or three outputs depending on whether state outputs are
1089 // provided.
1090 if (operation.type == OperationType::UNIDIRECTIONAL_SEQUENCE_LSTM ||
1091 operation.type == OperationType::UNIDIRECTIONAL_SEQUENCE_RNN ||
1092 operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_LSTM ||
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001093 operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_RNN) {
Slava Shklyaevaf1c77d2020-04-16 14:58:55 +01001094 for (const size_t index : operation.outputs) {
1095 if (index == operandIndex) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001096 return true;
1097 }
1098 }
1099 }
1100 }
1101 return false;
1102}
1103
1104static void removeOperandTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +00001105 for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001106 if (removeOperandSkip(operand, model)) {
1107 continue;
1108 }
1109 const std::string message = "removeOperandTest: operand " + std::to_string(operand);
Michael Butler68a6de72020-03-11 18:45:45 -07001110 validate(device, message, model, [operand](Model* model, ExecutionPreference*, Priority*) {
1111 removeOperand(model, operand);
1112 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001113 }
1114}
1115
1116///////////////////////// REMOVE OPERATION /////////////////////////
1117
1118static void removeOperation(Model* model, uint32_t index) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +00001119 for (uint32_t operand : model->main.operations[index].inputs) {
1120 model->main.operands[operand].numberOfConsumers--;
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001121 }
Slava Shklyaevf8124a82019-12-13 12:24:35 +00001122 hidl_vec_removeAt(&model->main.operations, index);
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001123}
1124
1125static void removeOperationTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +00001126 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001127 const std::string message = "removeOperationTest: operation " + std::to_string(operation);
1128 validate(device, message, model,
Michael Butler68a6de72020-03-11 18:45:45 -07001129 [operation](Model* model, ExecutionPreference*, Priority*) {
1130 removeOperation(model, operation);
1131 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001132 }
1133}
1134
1135///////////////////////// REMOVE OPERATION INPUT /////////////////////////
1136
1137static bool removeOperationInputSkip(const Operation& op, size_t input) {
1138 // Skip removeOperationInputTest for the following operations.
1139 // - CONCATENATION has at least 2 inputs, with the last element being INT32.
1140 // - CONV_2D, DEPTHWISE_CONV_2D, MAX_POOL_2D, AVERAGE_POOL_2D, L2_POOL_2D, RESIZE_BILINEAR,
1141 // SPACE_TO_DEPTH, SPACE_TO_DEPTH, SPACE_TO_BATCH_ND, BATCH_TO_SPACE_ND can have an optional
1142 // layout parameter.
Lev Proleeve3080db2020-02-18 12:53:00 +00001143 // RESIZE_BILINEAR and RESIZE_NEAREST_NEIGHBOR can have optional
1144 // align_corners and half_pixel_centers parameters.
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001145 // - L2_NORMALIZATION, LOCAL_RESPONSE_NORMALIZATION, SOFTMAX can have an optional axis
1146 // parameter.
1147 switch (op.type) {
1148 case OperationType::CONCATENATION: {
1149 if (op.inputs.size() > 2 && input != op.inputs.size() - 1) {
1150 return true;
1151 }
1152 } break;
1153 case OperationType::DEPTHWISE_CONV_2D: {
1154 if ((op.inputs.size() == 12 && input == 11) || (op.inputs.size() == 9 && input == 8)) {
1155 return true;
1156 }
1157 } break;
1158 case OperationType::CONV_2D:
1159 case OperationType::AVERAGE_POOL_2D:
1160 case OperationType::MAX_POOL_2D:
1161 case OperationType::L2_POOL_2D: {
1162 if ((op.inputs.size() == 11 && input == 10) || (op.inputs.size() == 8 && input == 7)) {
1163 return true;
1164 }
1165 } break;
1166 case OperationType::RESIZE_BILINEAR: {
Lev Proleeve3080db2020-02-18 12:53:00 +00001167 if (op.inputs.size() >= 4 && input >= 3) {
1168 return true;
1169 }
1170 } break;
1171 case OperationType::RESIZE_NEAREST_NEIGHBOR: {
1172 if (op.inputs.size() >= 5 && input >= 3) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001173 return true;
1174 }
1175 } break;
1176 case OperationType::SPACE_TO_DEPTH:
1177 case OperationType::DEPTH_TO_SPACE:
1178 case OperationType::BATCH_TO_SPACE_ND: {
1179 if (op.inputs.size() == 3 && input == 2) {
1180 return true;
1181 }
1182 } break;
1183 case OperationType::SPACE_TO_BATCH_ND: {
1184 if (op.inputs.size() == 4 && input == 3) {
1185 return true;
1186 }
1187 } break;
1188 case OperationType::L2_NORMALIZATION: {
1189 if (op.inputs.size() == 2 && input == 1) {
1190 return true;
1191 }
1192 } break;
1193 case OperationType::LOCAL_RESPONSE_NORMALIZATION: {
1194 if (op.inputs.size() == 6 && input == 5) {
1195 return true;
1196 }
1197 } break;
1198 case OperationType::SOFTMAX: {
1199 if (op.inputs.size() == 3 && input == 2) {
1200 return true;
1201 }
1202 } break;
1203 default:
1204 break;
1205 }
1206 return false;
1207}
1208
1209static void removeOperationInputTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +00001210 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
1211 for (size_t input = 0; input < model.main.operations[operation].inputs.size(); ++input) {
1212 const Operation& op = model.main.operations[operation];
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001213 if (removeOperationInputSkip(op, input)) {
1214 continue;
1215 }
1216 const std::string message = "removeOperationInputTest: operation " +
1217 std::to_string(operation) + ", input " +
1218 std::to_string(input);
Michael Butler68a6de72020-03-11 18:45:45 -07001219 validate(device, message, model,
1220 [operation, input](Model* model, ExecutionPreference*, Priority*) {
1221 uint32_t operand = model->main.operations[operation].inputs[input];
1222 model->main.operands[operand].numberOfConsumers--;
1223 hidl_vec_removeAt(&model->main.operations[operation].inputs, input);
1224 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001225 }
1226 }
1227}
1228
1229///////////////////////// REMOVE OPERATION OUTPUT /////////////////////////
1230
1231static void removeOperationOutputTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +00001232 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
1233 for (size_t output = 0; output < model.main.operations[operation].outputs.size();
1234 ++output) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001235 const std::string message = "removeOperationOutputTest: operation " +
1236 std::to_string(operation) + ", output " +
1237 std::to_string(output);
Michael Butler68a6de72020-03-11 18:45:45 -07001238 validate(device, message, model,
1239 [operation, output](Model* model, ExecutionPreference*, Priority*) {
1240 hidl_vec_removeAt(&model->main.operations[operation].outputs, output);
1241 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001242 }
1243 }
1244}
1245
1246///////////////////////// MODEL VALIDATION /////////////////////////
1247
1248// TODO: remove model input
1249// TODO: remove model output
1250// TODO: add unused operation
1251
1252///////////////////////// ADD OPERATION INPUT /////////////////////////
1253
1254static bool addOperationInputSkip(const Operation& op) {
1255 // Skip addOperationInputTest for the following operations.
1256 // - L2_NORMALIZATION, LOCAL_RESPONSE_NORMALIZATION, SOFTMAX can have an optional INT32 axis
1257 // parameter.
1258 if ((op.type == OperationType::L2_NORMALIZATION && op.inputs.size() == 1) ||
1259 (op.type == OperationType::LOCAL_RESPONSE_NORMALIZATION && op.inputs.size() == 5) ||
Lev Proleeve3080db2020-02-18 12:53:00 +00001260 (op.type == OperationType::SOFTMAX && op.inputs.size() == 2) ||
1261 (op.type == OperationType::RESIZE_BILINEAR && op.inputs.size() < 6) ||
1262 (op.type == OperationType::RESIZE_NEAREST_NEIGHBOR && op.inputs.size() < 6)) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001263 return true;
1264 }
1265 return false;
1266}
1267
1268static void addOperationInputTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +00001269 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
1270 if (addOperationInputSkip(model.main.operations[operation])) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001271 continue;
1272 }
1273 const std::string message = "addOperationInputTest: operation " + std::to_string(operation);
Michael Butler68a6de72020-03-11 18:45:45 -07001274 validate(device, message, model,
1275 [operation](Model* model, ExecutionPreference*, Priority*) {
1276 uint32_t index = addOperand(model, OperandLifeTime::SUBGRAPH_INPUT);
1277 hidl_vec_push_back(&model->main.operations[operation].inputs, index);
1278 hidl_vec_push_back(&model->main.inputIndexes, index);
1279 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001280 }
1281}
1282
1283///////////////////////// ADD OPERATION OUTPUT /////////////////////////
1284
1285static void addOperationOutputTest(const sp<IDevice>& device, const Model& model) {
Slava Shklyaevf8124a82019-12-13 12:24:35 +00001286 for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001287 const std::string message =
1288 "addOperationOutputTest: operation " + std::to_string(operation);
Michael Butler68a6de72020-03-11 18:45:45 -07001289 validate(device, message, model,
1290 [operation](Model* model, ExecutionPreference*, Priority*) {
1291 uint32_t index = addOperand(model, OperandLifeTime::SUBGRAPH_OUTPUT);
1292 hidl_vec_push_back(&model->main.operations[operation].outputs, index);
1293 hidl_vec_push_back(&model->main.outputIndexes, index);
1294 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001295 }
1296}
1297
1298///////////////////////// VALIDATE EXECUTION PREFERENCE /////////////////////////
1299
1300static const int32_t invalidExecutionPreferences[] = {
1301 static_cast<int32_t>(ExecutionPreference::LOW_POWER) - 1, // lower bound
1302 static_cast<int32_t>(ExecutionPreference::SUSTAINED_SPEED) + 1, // upper bound
1303};
1304
1305static void mutateExecutionPreferenceTest(const sp<IDevice>& device, const Model& model) {
Michael Butler68a6de72020-03-11 18:45:45 -07001306 for (int32_t invalidPreference : invalidExecutionPreferences) {
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001307 const std::string message =
Michael Butler68a6de72020-03-11 18:45:45 -07001308 "mutateExecutionPreferenceTest: preference " + std::to_string(invalidPreference);
1309 validate(device, message, model,
1310 [invalidPreference](Model*, ExecutionPreference* preference, Priority*) {
1311 *preference = static_cast<ExecutionPreference>(invalidPreference);
1312 });
1313 }
1314}
1315
1316///////////////////////// VALIDATE PRIORITY /////////////////////////
1317
1318static const int32_t invalidPriorities[] = {
1319 static_cast<int32_t>(Priority::LOW) - 1, // lower bound
1320 static_cast<int32_t>(Priority::HIGH) + 1, // upper bound
1321};
1322
1323static void mutateExecutionPriorityTest(const sp<IDevice>& device, const Model& model) {
1324 for (int32_t invalidPriority : invalidPriorities) {
1325 const std::string message =
1326 "mutatePriorityTest: priority " + std::to_string(invalidPriority);
1327 validate(device, message, model,
1328 [invalidPriority](Model*, ExecutionPreference*, Priority* priority) {
1329 *priority = static_cast<Priority>(invalidPriority);
1330 });
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001331 }
1332}
1333
1334////////////////////////// ENTRY POINT //////////////////////////////
1335
Michael Butlerff7d6c52020-02-13 16:37:22 -08001336void validateModel(const sp<IDevice>& device, const Model& model) {
David Gross6174f002018-05-14 12:23:04 -07001337 mutateExecutionOrderTest(device, model);
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001338 mutateOperandTypeTest(device, model);
1339 mutateOperandRankTest(device, model);
1340 mutateOperandScaleTest(device, model);
1341 mutateOperandZeroPointTest(device, model);
David Gross6174f002018-05-14 12:23:04 -07001342 mutateOperandLifeTimeTest(device, model);
1343 mutateOperandInputOutputTest(device, model);
1344 mutateOperandNumberOfConsumersTest(device, model);
1345 mutateOperandAddWriterTest(device, model);
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001346 mutateOperationOperandTypeTest(device, model);
1347 mutateOperationTypeTest(device, model);
1348 mutateOperationInputOperandIndexTest(device, model);
1349 mutateOperationOutputOperandIndexTest(device, model);
David Gross6174f002018-05-14 12:23:04 -07001350 mutateOperationRemoveWriteTest(device, model);
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001351 removeOperandTest(device, model);
1352 removeOperationTest(device, model);
1353 removeOperationInputTest(device, model);
1354 removeOperationOutputTest(device, model);
1355 addOperationInputTest(device, model);
1356 addOperationOutputTest(device, model);
1357 mutateExecutionPreferenceTest(device, model);
Michael Butler68a6de72020-03-11 18:45:45 -07001358 mutateExecutionPriorityTest(device, model);
Lev Proleev13fdfcd2019-08-30 11:35:34 +01001359}
1360
Lev Proleev26d1bc82019-08-30 11:57:18 +01001361} // namespace android::hardware::neuralnetworks::V1_3::vts::functional