blob: 3375602d27c105202dbb817639b05afc4948f355 [file] [log] [blame]
Slava Shklyaev871be942018-09-12 14:52:02 +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 Gross12ab6c52018-05-14 12:23:04 -070019#include <android/hardware/neuralnetworks/1.1/types.h>
Slava Shklyaev1d6b4652019-05-14 14:15:14 +010020#include "1.0/Utils.h"
21#include "1.2/Callbacks.h"
David Gross12ab6c52018-05-14 12:23:04 -070022#include "1.2/Utils.h"
Xusong Wang9e2b97b2019-08-23 16:10:54 -070023#include "GeneratedTestHarness.h"
Slava Shklyaev871be942018-09-12 14:52:02 +010024#include "VtsHalNeuralnetworks.h"
25
David Gross12ab6c52018-05-14 12:23:04 -070026#include <optional>
27#include <type_traits>
28#include <utility>
29
Michael Butlerbbe5dad2019-08-26 23:55:47 -070030namespace android::hardware::neuralnetworks::V1_2::vts::functional {
Slava Shklyaev871be942018-09-12 14:52:02 +010031
Michael Butlerbbe5dad2019-08-26 23:55:47 -070032using implementation::PreparedModelCallback;
David Gross12ab6c52018-05-14 12:23:04 -070033using V1_0::DataLocation;
Michael Butlerbbe5dad2019-08-26 23:55:47 -070034using V1_0::ErrorStatus;
Slava Shklyaev871be942018-09-12 14:52:02 +010035using V1_0::OperandLifeTime;
Slava Shklyaev871be942018-09-12 14:52:02 +010036using V1_1::ExecutionPreference;
Xusong Wangb61ba1e2019-02-25 16:58:58 -080037using HidlToken = hidl_array<uint8_t, static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
Slava Shklyaev871be942018-09-12 14:52:02 +010038
Michael Butlerda1a6922020-03-11 18:45:45 -070039using PrepareModelMutation = std::function<void(Model*, ExecutionPreference*)>;
40
Slava Shklyaev871be942018-09-12 14:52:02 +010041///////////////////////// UTILITY FUNCTIONS /////////////////////////
42
43static void validateGetSupportedOperations(const sp<IDevice>& device, const std::string& message,
44 const Model& model) {
45 SCOPED_TRACE(message + " [getSupportedOperations_1_2]");
46
Slava Shklyaev1d6b4652019-05-14 14:15:14 +010047 Return<void> ret = device->getSupportedOperations_1_2(
48 model, [&](ErrorStatus status, const hidl_vec<bool>&) {
49 EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, status);
50 });
Slava Shklyaev871be942018-09-12 14:52:02 +010051 EXPECT_TRUE(ret.isOk());
52}
53
54static void validatePrepareModel(const sp<IDevice>& device, const std::string& message,
55 const Model& model, ExecutionPreference preference) {
56 SCOPED_TRACE(message + " [prepareModel_1_2]");
57
58 sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
Slava Shklyaev871be942018-09-12 14:52:02 +010059 Return<ErrorStatus> prepareLaunchStatus =
Xusong Wangb61ba1e2019-02-25 16:58:58 -080060 device->prepareModel_1_2(model, preference, hidl_vec<hidl_handle>(),
61 hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
Slava Shklyaev871be942018-09-12 14:52:02 +010062 ASSERT_TRUE(prepareLaunchStatus.isOk());
63 ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(prepareLaunchStatus));
64
65 preparedModelCallback->wait();
66 ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
67 ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, prepareReturnStatus);
Xusong Wangb5cb8f72018-10-31 08:43:12 -070068 sp<IPreparedModel> preparedModel = getPreparedModel_1_2(preparedModelCallback);
Slava Shklyaev871be942018-09-12 14:52:02 +010069 ASSERT_EQ(nullptr, preparedModel.get());
70}
71
72static bool validExecutionPreference(ExecutionPreference preference) {
73 return preference == ExecutionPreference::LOW_POWER ||
74 preference == ExecutionPreference::FAST_SINGLE_ANSWER ||
75 preference == ExecutionPreference::SUSTAINED_SPEED;
76}
77
78// Primary validation function. This function will take a valid model, apply a
Michael Butlerda1a6922020-03-11 18:45:45 -070079// mutation to invalidate either the model or the execution preference, then
80// pass these to supportedOperations and/or prepareModel if that method is
81// called with an invalid argument.
82static void validate(const sp<IDevice>& device, const std::string& message,
83 const Model& originalModel, const PrepareModelMutation& mutate) {
84 Model model = originalModel;
85 ExecutionPreference preference = ExecutionPreference::FAST_SINGLE_ANSWER;
86 mutate(&model, &preference);
87
Slava Shklyaev871be942018-09-12 14:52:02 +010088 if (validExecutionPreference(preference)) {
89 validateGetSupportedOperations(device, message, model);
90 }
Michael Butlerda1a6922020-03-11 18:45:45 -070091
Slava Shklyaev871be942018-09-12 14:52:02 +010092 validatePrepareModel(device, message, model, preference);
93}
94
Slava Shklyaev871be942018-09-12 14:52:02 +010095static uint32_t addOperand(Model* model) {
96 return hidl_vec_push_back(&model->operands,
97 {
Slava Shklyaev1d6b4652019-05-14 14:15:14 +010098 .type = OperandType::INT32,
99 .dimensions = {},
100 .numberOfConsumers = 0,
101 .scale = 0.0f,
102 .zeroPoint = 0,
103 .lifetime = OperandLifeTime::MODEL_INPUT,
104 .location = {.poolIndex = 0, .offset = 0, .length = 0},
Slava Shklyaev871be942018-09-12 14:52:02 +0100105 });
106}
107
108static uint32_t addOperand(Model* model, OperandLifeTime lifetime) {
109 uint32_t index = addOperand(model);
110 model->operands[index].numberOfConsumers = 1;
111 model->operands[index].lifetime = lifetime;
112 return index;
113}
114
David Gross12ab6c52018-05-14 12:23:04 -0700115// If we introduce a CONSTANT_COPY for an operand of size operandSize,
116// how much will this increase the size of the model? This assumes
117// that we can (re)use all of model.operandValues for the operand
118// value.
119static size_t constantCopyExtraSize(const Model& model, size_t operandSize) {
120 const size_t operandValuesSize = model.operandValues.size();
121 return (operandValuesSize < operandSize) ? (operandSize - operandValuesSize) : 0;
122}
123
124// Highly specialized utility routine for converting an operand to
125// CONSTANT_COPY lifetime.
126//
127// Expects that:
128// - operand has a known size
129// - operand->lifetime has already been set to CONSTANT_COPY
130// - operand->location has been zeroed out
131//
132// Does the following:
133// - initializes operand->location to point to the beginning of model->operandValues
134// - resizes model->operandValues (if necessary) to be large enough for the operand
135// value, padding it with zeroes on the end
136//
137// Potential problem:
138// By changing the operand to CONSTANT_COPY lifetime, this function is effectively initializing the
139// operand with unspecified (but deterministic) data. This means that the model may be invalidated
140// in two ways: not only is the lifetime of CONSTANT_COPY invalid, but the operand's value in the
141// graph may also be invalid (e.g., if the operand is used as an activation code and has an invalid
142// value). For now, this should be fine because it just means we're not testing what we think we're
143// testing in certain cases; but we can handwave this and assume we're probabilistically likely to
144// exercise the validation code over the span of the entire test set and operand space.
145//
146// Aborts if the specified operand type is an extension type or OEM type.
147static void becomeConstantCopy(Model* model, Operand* operand) {
148 // sizeOfData will abort if the specified type is an extension type or OEM type.
149 const size_t sizeOfOperand = sizeOfData(*operand);
150 EXPECT_NE(sizeOfOperand, size_t(0));
151 operand->location.poolIndex = 0;
152 operand->location.offset = 0;
153 operand->location.length = sizeOfOperand;
154 if (model->operandValues.size() < sizeOfOperand) {
155 model->operandValues.resize(sizeOfOperand);
156 }
157}
158
159// The sizeForBinder() functions estimate the size of the
160// representation of a value when sent to binder. It's probably a bit
161// of an under-estimate, because we don't know the size of the
162// metadata in the binder format (e.g., representation of the size of
163// a vector); but at least it adds up "big" things like vector
164// contents. However, it doesn't treat inter-field or end-of-struct
165// padding in a methodical way -- there's no attempt to be consistent
166// in whether or not padding in the native (C++) representation
167// contributes to the estimated size for the binder representation;
168// and there's no attempt to understand what padding (if any) is
169// needed in the binder representation.
170//
171// This assumes that non-metadata uses a fixed length encoding (e.g.,
172// a uint32_t is always encoded in sizeof(uint32_t) bytes, rather than
173// using an encoding whose length is related to the magnitude of the
174// encoded value).
175
176template <typename Type>
177static size_t sizeForBinder(const Type& val) {
178 static_assert(std::is_trivially_copyable_v<std::remove_reference_t<Type>>,
179 "expected a trivially copyable type");
180 return sizeof(val);
181}
182
183template <typename Type>
184static size_t sizeForBinder(const hidl_vec<Type>& vec) {
185 return std::accumulate(vec.begin(), vec.end(), 0,
186 [](size_t acc, const Type& x) { return acc + sizeForBinder(x); });
187}
188
189template <>
190size_t sizeForBinder(const SymmPerChannelQuantParams& symmPerChannelQuantParams) {
191 size_t size = 0;
192
193 size += sizeForBinder(symmPerChannelQuantParams.scales);
194 size += sizeForBinder(symmPerChannelQuantParams.channelDim);
195
196 return size;
197}
198
199template <>
200size_t sizeForBinder(const Operand::ExtraParams& extraParams) {
201 using Discriminator = Operand::ExtraParams::hidl_discriminator;
202 switch (extraParams.getDiscriminator()) {
203 case Discriminator::none:
204 return 0;
205 case Discriminator::channelQuant:
206 return sizeForBinder(extraParams.channelQuant());
207 case Discriminator::extension:
208 return sizeForBinder(extraParams.extension());
209 }
210 LOG(FATAL) << "Unrecognized extraParams enum: "
211 << static_cast<int>(extraParams.getDiscriminator());
212 return 0;
213}
214
215template <>
216size_t sizeForBinder(const Operand& operand) {
217 size_t size = 0;
218
219 size += sizeForBinder(operand.type);
220 size += sizeForBinder(operand.dimensions);
221 size += sizeForBinder(operand.numberOfConsumers);
222 size += sizeForBinder(operand.scale);
223 size += sizeForBinder(operand.zeroPoint);
224 size += sizeForBinder(operand.lifetime);
225 size += sizeForBinder(operand.location);
226 size += sizeForBinder(operand.extraParams);
227
228 return size;
229}
230
231template <>
232size_t sizeForBinder(const Operation& operation) {
233 size_t size = 0;
234
235 size += sizeForBinder(operation.type);
236 size += sizeForBinder(operation.inputs);
237 size += sizeForBinder(operation.outputs);
238
239 return size;
240}
241
242template <>
243size_t sizeForBinder(const hidl_string& name) {
244 return name.size();
245}
246
247template <>
248size_t sizeForBinder(const hidl_memory& memory) {
249 // This is just a guess.
250
251 size_t size = 0;
252
253 if (const native_handle_t* handle = memory.handle()) {
254 size += sizeof(*handle);
255 size += sizeof(handle->data[0] * (handle->numFds + handle->numInts));
256 }
257 size += sizeForBinder(memory.name());
258
259 return size;
260}
261
262template <>
263size_t sizeForBinder(const Model::ExtensionNameAndPrefix& extensionNameToPrefix) {
264 size_t size = 0;
265
266 size += sizeForBinder(extensionNameToPrefix.name);
267 size += sizeForBinder(extensionNameToPrefix.prefix);
268
269 return size;
270}
271
272template <>
273size_t sizeForBinder(const Model& model) {
274 size_t size = 0;
275
276 size += sizeForBinder(model.operands);
277 size += sizeForBinder(model.operations);
278 size += sizeForBinder(model.inputIndexes);
279 size += sizeForBinder(model.outputIndexes);
280 size += sizeForBinder(model.operandValues);
281 size += sizeForBinder(model.pools);
282 size += sizeForBinder(model.relaxComputationFloat32toFloat16);
283 size += sizeForBinder(model.extensionNameToPrefix);
284
285 return size;
286}
287
288// https://developer.android.com/reference/android/os/TransactionTooLargeException.html
289//
290// "The Binder transaction buffer has a limited fixed size,
291// currently 1Mb, which is shared by all transactions in progress
292// for the process."
293//
294// Will our representation fit under this limit? There are two complications:
295// - Our representation size is just approximate (see sizeForBinder()).
296// - This object may not be the only occupant of the Binder transaction buffer.
297// So we'll be very conservative: We want the representation size to be no
298// larger than half the transaction buffer size.
299//
300// If our representation grows large enough that it still fits within
301// the transaction buffer but combined with other transactions may
302// exceed the buffer size, then we may see intermittent HAL transport
303// errors.
304static bool exceedsBinderSizeLimit(size_t representationSize) {
305 // Instead of using this fixed buffer size, we might instead be able to use
306 // ProcessState::self()->getMmapSize(). However, this has a potential
307 // problem: The binder/mmap size of the current process does not necessarily
308 // indicate the binder/mmap size of the service (i.e., the other process).
309 // The only way it would be a good indication is if both the current process
310 // and the service use the default size.
311 static const size_t kHalfBufferSize = 1024 * 1024 / 2;
312
313 return representationSize > kHalfBufferSize;
314}
315
316///////////////////////// VALIDATE EXECUTION ORDER ////////////////////////////
317
318static void mutateExecutionOrderTest(const sp<IDevice>& device, const Model& model) {
319 for (size_t operation = 0; operation < model.operations.size(); ++operation) {
320 const Operation& operationObj = model.operations[operation];
321 for (uint32_t input : operationObj.inputs) {
322 if (model.operands[input].lifetime == OperandLifeTime::TEMPORARY_VARIABLE ||
323 model.operands[input].lifetime == OperandLifeTime::MODEL_OUTPUT) {
324 // This operation reads an operand written by some
325 // other operation. Move this operation to the
326 // beginning of the sequence, ensuring that it reads
327 // the operand before that operand is written, thereby
328 // violating execution order rules.
329 const std::string message = "mutateExecutionOrderTest: operation " +
330 std::to_string(operation) + " is a reader";
331 validate(device, message, model, [operation](Model* model, ExecutionPreference*) {
332 auto& operations = model->operations;
333 std::rotate(operations.begin(), operations.begin() + operation,
334 operations.begin() + operation + 1);
335 });
336 break; // only need to do this once per operation
337 }
338 }
339 for (uint32_t output : operationObj.outputs) {
340 if (model.operands[output].numberOfConsumers > 0) {
341 // This operation writes an operand read by some other
342 // operation. Move this operation to the end of the
343 // sequence, ensuring that it writes the operand after
344 // that operand is read, thereby violating execution
345 // order rules.
346 const std::string message = "mutateExecutionOrderTest: operation " +
347 std::to_string(operation) + " is a writer";
348 validate(device, message, model, [operation](Model* model, ExecutionPreference*) {
349 auto& operations = model->operations;
350 std::rotate(operations.begin() + operation, operations.begin() + operation + 1,
351 operations.end());
352 });
353 break; // only need to do this once per operation
354 }
355 }
356 }
357}
358
Slava Shklyaev871be942018-09-12 14:52:02 +0100359///////////////////////// VALIDATE MODEL OPERAND TYPE /////////////////////////
360
Michael K. Sandersc785d462018-10-30 15:16:54 +0000361static const uint32_t invalidOperandTypes[] = {
Slava Shklyaev794703d2019-01-17 15:37:05 +0000362 static_cast<uint32_t>(OperandTypeRange::FUNDAMENTAL_MIN) - 1,
363 static_cast<uint32_t>(OperandTypeRange::FUNDAMENTAL_MAX) + 1,
364 static_cast<uint32_t>(OperandTypeRange::OEM_MIN) - 1,
365 static_cast<uint32_t>(OperandTypeRange::OEM_MAX) + 1,
Slava Shklyaev871be942018-09-12 14:52:02 +0100366};
367
368static void mutateOperandTypeTest(const sp<IDevice>& device, const Model& model) {
369 for (size_t operand = 0; operand < model.operands.size(); ++operand) {
Michael K. Sandersc785d462018-10-30 15:16:54 +0000370 for (uint32_t invalidOperandType : invalidOperandTypes) {
Slava Shklyaev871be942018-09-12 14:52:02 +0100371 const std::string message = "mutateOperandTypeTest: operand " +
372 std::to_string(operand) + " set to value " +
373 std::to_string(invalidOperandType);
Michael Butlerda1a6922020-03-11 18:45:45 -0700374 validate(device, message, model,
375 [operand, invalidOperandType](Model* model, ExecutionPreference*) {
376 model->operands[operand].type =
377 static_cast<OperandType>(invalidOperandType);
378 });
Slava Shklyaev871be942018-09-12 14:52:02 +0100379 }
380 }
381}
382
383///////////////////////// VALIDATE OPERAND RANK /////////////////////////
384
385static uint32_t getInvalidRank(OperandType type) {
386 switch (type) {
Xusong Wang7bca34b2018-12-05 14:21:51 -0800387 case OperandType::FLOAT16:
Slava Shklyaev871be942018-09-12 14:52:02 +0100388 case OperandType::FLOAT32:
389 case OperandType::INT32:
390 case OperandType::UINT32:
Lev Proleevabad9ea2018-10-01 11:18:31 +0100391 case OperandType::BOOL:
Slava Shklyaev871be942018-09-12 14:52:02 +0100392 return 1;
Lev Proleev923b8c52019-01-30 17:14:40 +0000393 case OperandType::TENSOR_BOOL8:
Michael K. Sanders19d63452018-10-12 09:10:15 +0100394 case OperandType::TENSOR_FLOAT16:
Slava Shklyaev871be942018-09-12 14:52:02 +0100395 case OperandType::TENSOR_FLOAT32:
396 case OperandType::TENSOR_INT32:
397 case OperandType::TENSOR_QUANT8_ASYMM:
Hervé Guihotbae91692019-01-23 19:18:59 -0800398 case OperandType::TENSOR_QUANT8_SYMM:
Xusong Wangd49f6652019-01-16 18:32:24 -0800399 case OperandType::TENSOR_QUANT16_ASYMM:
Lev Proleev48c88202018-11-13 15:42:36 +0000400 case OperandType::TENSOR_QUANT16_SYMM:
Przemyslaw Szczepaniakfaa59b82018-11-08 15:22:17 +0000401 case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
Slava Shklyaev871be942018-09-12 14:52:02 +0100402 return 0;
403 default:
404 return 0;
405 }
406}
407
408static void mutateOperandRankTest(const sp<IDevice>& device, const Model& model) {
409 for (size_t operand = 0; operand < model.operands.size(); ++operand) {
410 const uint32_t invalidRank = getInvalidRank(model.operands[operand].type);
Xusong Wanga3165812018-11-19 18:26:08 -0800411 if (invalidRank == 0) {
412 continue;
413 }
Slava Shklyaev871be942018-09-12 14:52:02 +0100414 const std::string message = "mutateOperandRankTest: operand " + std::to_string(operand) +
415 " has rank of " + std::to_string(invalidRank);
Michael Butlerda1a6922020-03-11 18:45:45 -0700416 validate(device, message, model,
417 [operand, invalidRank](Model* model, ExecutionPreference*) {
418 model->operands[operand].dimensions = std::vector<uint32_t>(invalidRank, 0);
419 });
Slava Shklyaev871be942018-09-12 14:52:02 +0100420 }
421}
422
423///////////////////////// VALIDATE OPERAND SCALE /////////////////////////
424
425static float getInvalidScale(OperandType type) {
426 switch (type) {
Xusong Wang7bca34b2018-12-05 14:21:51 -0800427 case OperandType::FLOAT16:
Slava Shklyaev871be942018-09-12 14:52:02 +0100428 case OperandType::FLOAT32:
429 case OperandType::INT32:
430 case OperandType::UINT32:
Lev Proleevabad9ea2018-10-01 11:18:31 +0100431 case OperandType::BOOL:
Lev Proleev923b8c52019-01-30 17:14:40 +0000432 case OperandType::TENSOR_BOOL8:
Michael K. Sanders19d63452018-10-12 09:10:15 +0100433 case OperandType::TENSOR_FLOAT16:
Slava Shklyaev871be942018-09-12 14:52:02 +0100434 case OperandType::TENSOR_FLOAT32:
Przemyslaw Szczepaniakfaa59b82018-11-08 15:22:17 +0000435 case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
Slava Shklyaev871be942018-09-12 14:52:02 +0100436 return 1.0f;
437 case OperandType::TENSOR_INT32:
438 return -1.0f;
Hervé Guihotbae91692019-01-23 19:18:59 -0800439 case OperandType::TENSOR_QUANT8_SYMM:
Slava Shklyaev871be942018-09-12 14:52:02 +0100440 case OperandType::TENSOR_QUANT8_ASYMM:
Xusong Wangd49f6652019-01-16 18:32:24 -0800441 case OperandType::TENSOR_QUANT16_ASYMM:
Lev Proleev48c88202018-11-13 15:42:36 +0000442 case OperandType::TENSOR_QUANT16_SYMM:
Slava Shklyaev871be942018-09-12 14:52:02 +0100443 return 0.0f;
444 default:
445 return 0.0f;
446 }
447}
448
449static void mutateOperandScaleTest(const sp<IDevice>& device, const Model& model) {
450 for (size_t operand = 0; operand < model.operands.size(); ++operand) {
451 const float invalidScale = getInvalidScale(model.operands[operand].type);
452 const std::string message = "mutateOperandScaleTest: operand " + std::to_string(operand) +
453 " has scale of " + std::to_string(invalidScale);
Michael Butlerda1a6922020-03-11 18:45:45 -0700454 validate(device, message, model,
455 [operand, invalidScale](Model* model, ExecutionPreference*) {
456 model->operands[operand].scale = invalidScale;
457 });
Slava Shklyaev871be942018-09-12 14:52:02 +0100458 }
459}
460
461///////////////////////// VALIDATE OPERAND ZERO POINT /////////////////////////
462
463static std::vector<int32_t> getInvalidZeroPoints(OperandType type) {
464 switch (type) {
Xusong Wang7bca34b2018-12-05 14:21:51 -0800465 case OperandType::FLOAT16:
Slava Shklyaev871be942018-09-12 14:52:02 +0100466 case OperandType::FLOAT32:
467 case OperandType::INT32:
468 case OperandType::UINT32:
Lev Proleevabad9ea2018-10-01 11:18:31 +0100469 case OperandType::BOOL:
Lev Proleev923b8c52019-01-30 17:14:40 +0000470 case OperandType::TENSOR_BOOL8:
Michael K. Sanders19d63452018-10-12 09:10:15 +0100471 case OperandType::TENSOR_FLOAT16:
Slava Shklyaev871be942018-09-12 14:52:02 +0100472 case OperandType::TENSOR_FLOAT32:
473 case OperandType::TENSOR_INT32:
Przemyslaw Szczepaniakfaa59b82018-11-08 15:22:17 +0000474 case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
Slava Shklyaev871be942018-09-12 14:52:02 +0100475 return {1};
476 case OperandType::TENSOR_QUANT8_ASYMM:
477 return {-1, 256};
Hervé Guihotbae91692019-01-23 19:18:59 -0800478 case OperandType::TENSOR_QUANT8_SYMM:
Slava Shklyaev1d6b4652019-05-14 14:15:14 +0100479 return {-129, -1, 1, 128};
Xusong Wangd49f6652019-01-16 18:32:24 -0800480 case OperandType::TENSOR_QUANT16_ASYMM:
481 return {-1, 65536};
Lev Proleev48c88202018-11-13 15:42:36 +0000482 case OperandType::TENSOR_QUANT16_SYMM:
483 return {-32769, -1, 1, 32768};
Slava Shklyaev871be942018-09-12 14:52:02 +0100484 default:
485 return {};
486 }
487}
488
489static void mutateOperandZeroPointTest(const sp<IDevice>& device, const Model& model) {
490 for (size_t operand = 0; operand < model.operands.size(); ++operand) {
491 const std::vector<int32_t> invalidZeroPoints =
Slava Shklyaev1d6b4652019-05-14 14:15:14 +0100492 getInvalidZeroPoints(model.operands[operand].type);
Slava Shklyaev871be942018-09-12 14:52:02 +0100493 for (int32_t invalidZeroPoint : invalidZeroPoints) {
494 const std::string message = "mutateOperandZeroPointTest: operand " +
495 std::to_string(operand) + " has zero point of " +
496 std::to_string(invalidZeroPoint);
Michael Butlerda1a6922020-03-11 18:45:45 -0700497 validate(device, message, model,
498 [operand, invalidZeroPoint](Model* model, ExecutionPreference*) {
499 model->operands[operand].zeroPoint = invalidZeroPoint;
500 });
Slava Shklyaev871be942018-09-12 14:52:02 +0100501 }
502 }
503}
504
David Gross12ab6c52018-05-14 12:23:04 -0700505///////////////////////// VALIDATE OPERAND LIFETIME /////////////////////////////////////////////
506
507static std::vector<OperandLifeTime> getInvalidLifeTimes(const Model& model, size_t modelSize,
508 const Operand& operand) {
509 // TODO: Support OperandLifeTime::CONSTANT_REFERENCE as an invalid lifetime
510 // TODO: Support OperandLifeTime::NO_VALUE as an invalid lifetime
511
512 // Ways to get an invalid lifetime:
513 // - change whether a lifetime means an operand should have a writer
514 std::vector<OperandLifeTime> ret;
515 switch (operand.lifetime) {
516 case OperandLifeTime::MODEL_OUTPUT:
517 case OperandLifeTime::TEMPORARY_VARIABLE:
518 ret = {
519 OperandLifeTime::MODEL_INPUT,
520 OperandLifeTime::CONSTANT_COPY,
521 };
522 break;
523 case OperandLifeTime::CONSTANT_COPY:
524 case OperandLifeTime::CONSTANT_REFERENCE:
525 case OperandLifeTime::MODEL_INPUT:
526 ret = {
527 OperandLifeTime::TEMPORARY_VARIABLE,
528 OperandLifeTime::MODEL_OUTPUT,
529 };
530 break;
531 case OperandLifeTime::NO_VALUE:
532 // Not enough information to know whether
533 // TEMPORARY_VARIABLE or CONSTANT_COPY would be invalid --
534 // is this operand written (then CONSTANT_COPY would be
535 // invalid) or not (then TEMPORARY_VARIABLE would be
536 // invalid)?
537 break;
538 default:
539 ADD_FAILURE();
540 break;
541 }
542
543 const size_t operandSize = sizeOfData(operand); // will be zero if shape is unknown
544 if (!operandSize ||
545 exceedsBinderSizeLimit(modelSize + constantCopyExtraSize(model, operandSize))) {
546 // Unknown size or too-large size
547 ret.erase(std::remove(ret.begin(), ret.end(), OperandLifeTime::CONSTANT_COPY), ret.end());
548 }
549
550 return ret;
551}
552
553static void mutateOperandLifeTimeTest(const sp<IDevice>& device, const Model& model) {
554 const size_t modelSize = sizeForBinder(model);
555 for (size_t operand = 0; operand < model.operands.size(); ++operand) {
556 const std::vector<OperandLifeTime> invalidLifeTimes =
557 getInvalidLifeTimes(model, modelSize, model.operands[operand]);
558 for (OperandLifeTime invalidLifeTime : invalidLifeTimes) {
559 const std::string message = "mutateOperandLifetimeTest: operand " +
560 std::to_string(operand) + " has lifetime " +
561 toString(invalidLifeTime) + " instead of lifetime " +
562 toString(model.operands[operand].lifetime);
563 validate(device, message, model,
564 [operand, invalidLifeTime](Model* model, ExecutionPreference*) {
565 static const DataLocation kZeroDataLocation = {};
566 Operand& operandObj = model->operands[operand];
567 switch (operandObj.lifetime) {
568 case OperandLifeTime::MODEL_INPUT: {
569 hidl_vec_remove(&model->inputIndexes, uint32_t(operand));
570 break;
571 }
572 case OperandLifeTime::MODEL_OUTPUT: {
573 hidl_vec_remove(&model->outputIndexes, uint32_t(operand));
574 break;
575 }
576 default:
577 break;
578 }
579 operandObj.lifetime = invalidLifeTime;
580 operandObj.location = kZeroDataLocation;
581 switch (invalidLifeTime) {
582 case OperandLifeTime::CONSTANT_COPY: {
583 becomeConstantCopy(model, &operandObj);
584 break;
585 }
586 case OperandLifeTime::MODEL_INPUT:
587 hidl_vec_push_back(&model->inputIndexes, uint32_t(operand));
588 break;
589 case OperandLifeTime::MODEL_OUTPUT:
590 hidl_vec_push_back(&model->outputIndexes, uint32_t(operand));
591 break;
592 default:
593 break;
594 }
595 });
596 }
597 }
598}
599
600///////////////////////// VALIDATE OPERAND INPUT-or-OUTPUT //////////////////////////////////////
601
602static std::optional<OperandLifeTime> getInputOutputLifeTime(const Model& model, size_t modelSize,
603 const Operand& operand) {
604 // Ways to get an invalid lifetime (with respect to model inputIndexes and outputIndexes):
605 // - change whether a lifetime means an operand is a model input, a model output, or neither
606 // - preserve whether or not a lifetime means an operand should have a writer
607 switch (operand.lifetime) {
608 case OperandLifeTime::CONSTANT_COPY:
609 case OperandLifeTime::CONSTANT_REFERENCE:
610 return OperandLifeTime::MODEL_INPUT;
611 case OperandLifeTime::MODEL_INPUT: {
612 const size_t operandSize = sizeOfData(operand); // will be zero if shape is unknown
613 if (!operandSize ||
614 exceedsBinderSizeLimit(modelSize + constantCopyExtraSize(model, operandSize))) {
615 // Unknown size or too-large size
616 break;
617 }
618 return OperandLifeTime::CONSTANT_COPY;
619 }
620 case OperandLifeTime::MODEL_OUTPUT:
621 return OperandLifeTime::TEMPORARY_VARIABLE;
622 case OperandLifeTime::TEMPORARY_VARIABLE:
623 return OperandLifeTime::MODEL_OUTPUT;
624 case OperandLifeTime::NO_VALUE:
625 // Not enough information to know whether
626 // TEMPORARY_VARIABLE or CONSTANT_COPY would be an
627 // appropriate choice -- is this operand written (then
628 // TEMPORARY_VARIABLE would be appropriate) or not (then
629 // CONSTANT_COPY would be appropriate)?
630 break;
631 default:
632 ADD_FAILURE();
633 break;
634 }
635
636 return std::nullopt;
637}
638
639static void mutateOperandInputOutputTest(const sp<IDevice>& device, const Model& model) {
640 const size_t modelSize = sizeForBinder(model);
641 for (size_t operand = 0; operand < model.operands.size(); ++operand) {
642 const std::optional<OperandLifeTime> changedLifeTime =
643 getInputOutputLifeTime(model, modelSize, model.operands[operand]);
644 if (changedLifeTime) {
645 const std::string message = "mutateOperandInputOutputTest: operand " +
646 std::to_string(operand) + " has lifetime " +
647 toString(*changedLifeTime) + " instead of lifetime " +
648 toString(model.operands[operand].lifetime);
649 validate(device, message, model,
650 [operand, changedLifeTime](Model* model, ExecutionPreference*) {
651 static const DataLocation kZeroDataLocation = {};
652 Operand& operandObj = model->operands[operand];
653 operandObj.lifetime = *changedLifeTime;
654 operandObj.location = kZeroDataLocation;
655 if (*changedLifeTime == OperandLifeTime::CONSTANT_COPY) {
656 becomeConstantCopy(model, &operandObj);
657 }
658 });
659 }
660 }
661}
662
663///////////////////////// VALIDATE OPERAND NUMBER OF CONSUMERS //////////////////////////////////
664
665static std::vector<uint32_t> getInvalidNumberOfConsumers(uint32_t numberOfConsumers) {
666 if (numberOfConsumers == 0) {
667 return {1};
668 } else {
669 return {numberOfConsumers - 1, numberOfConsumers + 1};
670 }
671}
672
673static void mutateOperandNumberOfConsumersTest(const sp<IDevice>& device, const Model& model) {
674 for (size_t operand = 0; operand < model.operands.size(); ++operand) {
675 const std::vector<uint32_t> invalidNumberOfConsumersVec =
676 getInvalidNumberOfConsumers(model.operands[operand].numberOfConsumers);
677 for (uint32_t invalidNumberOfConsumers : invalidNumberOfConsumersVec) {
678 const std::string message =
679 "mutateOperandNumberOfConsumersTest: operand " + std::to_string(operand) +
680 " numberOfConsumers = " + std::to_string(invalidNumberOfConsumers);
681 validate(device, message, model,
682 [operand, invalidNumberOfConsumers](Model* model, ExecutionPreference*) {
683 model->operands[operand].numberOfConsumers = invalidNumberOfConsumers;
684 });
685 }
686 }
687}
688
689///////////////////////// VALIDATE OPERAND NUMBER OF WRITERS ////////////////////////////////////
690
691static void mutateOperandAddWriterTest(const sp<IDevice>& device, const Model& model) {
692 for (size_t operation = 0; operation < model.operations.size(); ++operation) {
693 for (size_t badOutputNum = 0; badOutputNum < model.operations[operation].outputs.size();
694 ++badOutputNum) {
695 const uint32_t outputOperandIndex = model.operations[operation].outputs[badOutputNum];
696 const std::string message = "mutateOperandAddWriterTest: operation " +
697 std::to_string(operation) + " writes to " +
698 std::to_string(outputOperandIndex);
699 // We'll insert a copy of the operation, all of whose
700 // OTHER output operands are newly-created -- i.e.,
701 // there'll only be a duplicate write of ONE of that
702 // operation's output operands.
703 validate(device, message, model,
704 [operation, badOutputNum](Model* model, ExecutionPreference*) {
705 Operation newOperation = model->operations[operation];
706 for (uint32_t input : newOperation.inputs) {
707 ++model->operands[input].numberOfConsumers;
708 }
709 for (size_t outputNum = 0; outputNum < newOperation.outputs.size();
710 ++outputNum) {
711 if (outputNum == badOutputNum) continue;
712
713 Operand operandValue =
714 model->operands[newOperation.outputs[outputNum]];
715 operandValue.numberOfConsumers = 0;
716 if (operandValue.lifetime == OperandLifeTime::MODEL_OUTPUT) {
717 operandValue.lifetime = OperandLifeTime::TEMPORARY_VARIABLE;
718 } else {
719 ASSERT_EQ(operandValue.lifetime,
720 OperandLifeTime::TEMPORARY_VARIABLE);
721 }
722 newOperation.outputs[outputNum] =
723 hidl_vec_push_back(&model->operands, operandValue);
724 }
725 // Where do we insert the extra writer (a new
726 // operation)? It has to be later than all the
727 // writers of its inputs. The easiest thing to do
728 // is to insert it at the end of the operation
729 // sequence.
730 hidl_vec_push_back(&model->operations, newOperation);
731 });
732 }
733 }
734}
735
Slava Shklyaev871be942018-09-12 14:52:02 +0100736///////////////////////// VALIDATE EXTRA ??? /////////////////////////
737
Slava Shklyaev871be942018-09-12 14:52:02 +0100738// TODO: Operand::location
739
740///////////////////////// VALIDATE OPERATION OPERAND TYPE /////////////////////////
741
742static void mutateOperand(Operand* operand, OperandType type) {
743 Operand newOperand = *operand;
744 newOperand.type = type;
745 switch (type) {
Xusong Wang7bca34b2018-12-05 14:21:51 -0800746 case OperandType::FLOAT16:
Slava Shklyaev871be942018-09-12 14:52:02 +0100747 case OperandType::FLOAT32:
748 case OperandType::INT32:
749 case OperandType::UINT32:
Lev Proleevabad9ea2018-10-01 11:18:31 +0100750 case OperandType::BOOL:
Slava Shklyaev871be942018-09-12 14:52:02 +0100751 newOperand.dimensions = hidl_vec<uint32_t>();
752 newOperand.scale = 0.0f;
753 newOperand.zeroPoint = 0;
754 break;
Lev Proleev923b8c52019-01-30 17:14:40 +0000755 case OperandType::TENSOR_BOOL8:
Michael K. Sanders19d63452018-10-12 09:10:15 +0100756 case OperandType::TENSOR_FLOAT16:
Slava Shklyaev871be942018-09-12 14:52:02 +0100757 case OperandType::TENSOR_FLOAT32:
758 newOperand.dimensions =
Slava Shklyaev1d6b4652019-05-14 14:15:14 +0100759 operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec<uint32_t>({1});
Slava Shklyaev871be942018-09-12 14:52:02 +0100760 newOperand.scale = 0.0f;
761 newOperand.zeroPoint = 0;
762 break;
763 case OperandType::TENSOR_INT32:
764 newOperand.dimensions =
Slava Shklyaev1d6b4652019-05-14 14:15:14 +0100765 operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec<uint32_t>({1});
Slava Shklyaev871be942018-09-12 14:52:02 +0100766 newOperand.zeroPoint = 0;
767 break;
768 case OperandType::TENSOR_QUANT8_ASYMM:
Hervé Guihotbae91692019-01-23 19:18:59 -0800769 case OperandType::TENSOR_QUANT8_SYMM:
Xusong Wangd49f6652019-01-16 18:32:24 -0800770 case OperandType::TENSOR_QUANT16_ASYMM:
Lev Proleev48c88202018-11-13 15:42:36 +0000771 case OperandType::TENSOR_QUANT16_SYMM:
Slava Shklyaev871be942018-09-12 14:52:02 +0100772 newOperand.dimensions =
Slava Shklyaev1d6b4652019-05-14 14:15:14 +0100773 operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec<uint32_t>({1});
Slava Shklyaev871be942018-09-12 14:52:02 +0100774 newOperand.scale = operand->scale != 0.0f ? operand->scale : 1.0f;
775 break;
Przemyslaw Szczepaniakfaa59b82018-11-08 15:22:17 +0000776 case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL: {
777 newOperand.dimensions =
Slava Shklyaev1d6b4652019-05-14 14:15:14 +0100778 operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec<uint32_t>({1});
Przemyslaw Szczepaniakfaa59b82018-11-08 15:22:17 +0000779 newOperand.scale = 0.0f;
780 newOperand.zeroPoint = 0;
781
782 SymmPerChannelQuantParams channelQuant;
783 channelQuant.channelDim = 0;
784 channelQuant.scales = hidl_vec<float>(
Slava Shklyaev1d6b4652019-05-14 14:15:14 +0100785 operand->dimensions.size() > 0 ? static_cast<size_t>(operand->dimensions[0])
786 : 0);
Przemyslaw Szczepaniakfaa59b82018-11-08 15:22:17 +0000787 for (size_t i = 0; i < channelQuant.scales.size(); ++i) {
788 channelQuant.scales[i] = 1.0f;
789 }
790 newOperand.extraParams.channelQuant(std::move(channelQuant));
791 } break;
Slava Shklyaev871be942018-09-12 14:52:02 +0100792 case OperandType::OEM:
793 case OperandType::TENSOR_OEM_BYTE:
794 default:
795 break;
796 }
797 *operand = newOperand;
798}
799
Xusong Wang5b747ae2018-10-05 11:49:13 -0700800static bool mutateOperationOperandTypeSkip(size_t operand, OperandType type, const Model& model) {
801 // Do not test OEM types
802 if (type == model.operands[operand].type || type == OperandType::OEM ||
803 type == OperandType::TENSOR_OEM_BYTE) {
804 return true;
805 }
Slava Shklyaev871be942018-09-12 14:52:02 +0100806 for (const Operation& operation : model.operations) {
Xusong Wang5b747ae2018-10-05 11:49:13 -0700807 // Skip mutateOperationOperandTypeTest for the following operations.
808 // - LSH_PROJECTION's second argument is allowed to have any type.
Michael K. Sandersbbdab2f2018-11-28 10:35:08 +0000809 // - ARGMIN and ARGMAX's first argument can be any of
810 // TENSOR_(FLOAT16|FLOAT32|INT32|QUANT8_ASYMM).
811 // - CAST's argument can be any of TENSOR_(FLOAT16|FLOAT32|INT32|QUANT8_ASYMM).
Michael K. Sanders5b2615b2018-12-06 12:34:07 +0000812 // - RANDOM_MULTINOMIAL's argument can be either TENSOR_FLOAT16 or TENSOR_FLOAT32.
Lev Proleev923b8c52019-01-30 17:14:40 +0000813 // - DEQUANTIZE input can be any of
814 // TENSOR_(QUANT8_ASYMM|QUANT8_SYMM|QUANT8_SYMM_PER_CHANNEL), output can
815 // be of either TENSOR_FLOAT16 or TENSOR_FLOAT32.
816 // - QUANTIZE input can be either TENSOR_FLOAT16 or TENSOR_FLOAT32
Przemyslaw Szczepaniakf54f1262018-11-26 14:10:06 +0000817 // - CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
Przemyslaw Szczepaniak47b91412018-12-11 13:42:27 +0000818 // - DEPTHWISE_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
Lev Proleevb0762cc2019-01-15 17:53:46 +0000819 // - GROUPED_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
Lev Proleev1509a262019-01-15 17:49:24 +0000820 // - TRANSPOSE_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
Xusong Wang5b747ae2018-10-05 11:49:13 -0700821 switch (operation.type) {
822 case OperationType::LSH_PROJECTION: {
823 if (operand == operation.inputs[1]) {
824 return true;
825 }
826 } break;
827 case OperationType::CAST:
828 case OperationType::ARGMAX:
829 case OperationType::ARGMIN: {
Michael K. Sandersbbdab2f2018-11-28 10:35:08 +0000830 if (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32 ||
831 type == OperandType::TENSOR_INT32 || type == OperandType::TENSOR_QUANT8_ASYMM) {
Xusong Wang5b747ae2018-10-05 11:49:13 -0700832 return true;
833 }
834 } break;
Lev Proleev923b8c52019-01-30 17:14:40 +0000835 case OperationType::QUANTIZE:
Michael K. Sanders5b2615b2018-12-06 12:34:07 +0000836 case OperationType::RANDOM_MULTINOMIAL: {
Lev Proleev923b8c52019-01-30 17:14:40 +0000837 if (operand == operation.inputs[0] &&
838 (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32)) {
839 return true;
840 }
841 } break;
842 case OperationType::DEQUANTIZE: {
843 if (operand == operation.inputs[0] &&
844 (type == OperandType::TENSOR_QUANT8_ASYMM ||
845 type == OperandType::TENSOR_QUANT8_SYMM ||
846 type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL)) {
847 return true;
848 }
849 if (operand == operation.outputs[0] &&
850 (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32)) {
Michael K. Sanders5b2615b2018-12-06 12:34:07 +0000851 return true;
852 }
853 } break;
Lev Proleev1509a262019-01-15 17:49:24 +0000854 case OperationType::TRANSPOSE_CONV_2D:
Lev Proleevb0762cc2019-01-15 17:53:46 +0000855 case OperationType::GROUPED_CONV_2D:
Przemyslaw Szczepaniak47b91412018-12-11 13:42:27 +0000856 case OperationType::DEPTHWISE_CONV_2D:
Przemyslaw Szczepaniakf54f1262018-11-26 14:10:06 +0000857 case OperationType::CONV_2D: {
Xusong Wang88044232019-03-13 16:24:34 -0700858 if (operand == operation.inputs[1] &&
859 (type == OperandType::TENSOR_QUANT8_ASYMM ||
860 type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL)) {
Przemyslaw Szczepaniakf54f1262018-11-26 14:10:06 +0000861 return true;
862 }
863 } break;
Xusong Wang5b747ae2018-10-05 11:49:13 -0700864 default:
865 break;
Slava Shklyaev871be942018-09-12 14:52:02 +0100866 }
867 }
868 return false;
869}
870
871static void mutateOperationOperandTypeTest(const sp<IDevice>& device, const Model& model) {
872 for (size_t operand = 0; operand < model.operands.size(); ++operand) {
Slava Shklyaev871be942018-09-12 14:52:02 +0100873 for (OperandType invalidOperandType : hidl_enum_range<OperandType>{}) {
Xusong Wang5b747ae2018-10-05 11:49:13 -0700874 if (mutateOperationOperandTypeSkip(operand, invalidOperandType, model)) {
Slava Shklyaev871be942018-09-12 14:52:02 +0100875 continue;
876 }
877 const std::string message = "mutateOperationOperandTypeTest: operand " +
878 std::to_string(operand) + " set to type " +
879 toString(invalidOperandType);
Michael Butlerda1a6922020-03-11 18:45:45 -0700880 validate(device, message, model,
881 [operand, invalidOperandType](Model* model, ExecutionPreference*) {
882 mutateOperand(&model->operands[operand], invalidOperandType);
883 });
Slava Shklyaev871be942018-09-12 14:52:02 +0100884 }
885 }
886}
887
888///////////////////////// VALIDATE MODEL OPERATION TYPE /////////////////////////
889
Michael K. Sandersc785d462018-10-30 15:16:54 +0000890static const uint32_t invalidOperationTypes[] = {
Slava Shklyaev794703d2019-01-17 15:37:05 +0000891 static_cast<uint32_t>(OperationTypeRange::FUNDAMENTAL_MAX) + 1,
892 static_cast<uint32_t>(OperationTypeRange::OEM_MIN) - 1,
893 static_cast<uint32_t>(OperationTypeRange::OEM_MAX) + 1,
Slava Shklyaev871be942018-09-12 14:52:02 +0100894};
895
896static void mutateOperationTypeTest(const sp<IDevice>& device, const Model& model) {
897 for (size_t operation = 0; operation < model.operations.size(); ++operation) {
Michael K. Sandersc785d462018-10-30 15:16:54 +0000898 for (uint32_t invalidOperationType : invalidOperationTypes) {
Slava Shklyaev871be942018-09-12 14:52:02 +0100899 const std::string message = "mutateOperationTypeTest: operation " +
900 std::to_string(operation) + " set to value " +
901 std::to_string(invalidOperationType);
Michael Butlerda1a6922020-03-11 18:45:45 -0700902 validate(device, message, model,
903 [operation, invalidOperationType](Model* model, ExecutionPreference*) {
904 model->operations[operation].type =
905 static_cast<OperationType>(invalidOperationType);
906 });
Slava Shklyaev871be942018-09-12 14:52:02 +0100907 }
908 }
909}
910
911///////////////////////// VALIDATE MODEL OPERATION INPUT OPERAND INDEX /////////////////////////
912
913static void mutateOperationInputOperandIndexTest(const sp<IDevice>& device, const Model& model) {
914 for (size_t operation = 0; operation < model.operations.size(); ++operation) {
915 const uint32_t invalidOperand = model.operands.size();
916 for (size_t input = 0; input < model.operations[operation].inputs.size(); ++input) {
917 const std::string message = "mutateOperationInputOperandIndexTest: operation " +
918 std::to_string(operation) + " input " +
919 std::to_string(input);
Michael Butlerda1a6922020-03-11 18:45:45 -0700920 validate(device, message, model,
921 [operation, input, invalidOperand](Model* model, ExecutionPreference*) {
922 model->operations[operation].inputs[input] = invalidOperand;
923 });
Slava Shklyaev871be942018-09-12 14:52:02 +0100924 }
925 }
926}
927
928///////////////////////// VALIDATE MODEL OPERATION OUTPUT OPERAND INDEX /////////////////////////
929
930static void mutateOperationOutputOperandIndexTest(const sp<IDevice>& device, const Model& model) {
931 for (size_t operation = 0; operation < model.operations.size(); ++operation) {
932 const uint32_t invalidOperand = model.operands.size();
933 for (size_t output = 0; output < model.operations[operation].outputs.size(); ++output) {
934 const std::string message = "mutateOperationOutputOperandIndexTest: operation " +
935 std::to_string(operation) + " output " +
936 std::to_string(output);
Michael Butlerda1a6922020-03-11 18:45:45 -0700937 validate(device, message, model,
938 [operation, output, invalidOperand](Model* model, ExecutionPreference*) {
939 model->operations[operation].outputs[output] = invalidOperand;
940 });
Slava Shklyaev871be942018-09-12 14:52:02 +0100941 }
942 }
943}
944
David Gross12ab6c52018-05-14 12:23:04 -0700945///////////////////////// VALIDATE MODEL OPERANDS WRITTEN ///////////////////////////////////////
946
947static void mutateOperationRemoveWriteTest(const sp<IDevice>& device, const Model& model) {
948 for (size_t operation = 0; operation < model.operations.size(); ++operation) {
949 for (size_t outputNum = 0; outputNum < model.operations[operation].outputs.size();
950 ++outputNum) {
951 const uint32_t outputOperandIndex = model.operations[operation].outputs[outputNum];
952 if (model.operands[outputOperandIndex].numberOfConsumers > 0) {
953 const std::string message = "mutateOperationRemoveWriteTest: operation " +
954 std::to_string(operation) + " writes to " +
955 std::to_string(outputOperandIndex);
956 validate(device, message, model,
957 [operation, outputNum](Model* model, ExecutionPreference*) {
958 uint32_t& outputOperandIndex =
959 model->operations[operation].outputs[outputNum];
960 Operand operandValue = model->operands[outputOperandIndex];
961 operandValue.numberOfConsumers = 0;
962 if (operandValue.lifetime == OperandLifeTime::MODEL_OUTPUT) {
963 operandValue.lifetime = OperandLifeTime::TEMPORARY_VARIABLE;
964 } else {
965 ASSERT_EQ(operandValue.lifetime,
966 OperandLifeTime::TEMPORARY_VARIABLE);
967 }
968 outputOperandIndex =
969 hidl_vec_push_back(&model->operands, operandValue);
970 });
971 }
972 }
973 }
974}
975
Slava Shklyaev871be942018-09-12 14:52:02 +0100976///////////////////////// REMOVE OPERAND FROM EVERYTHING /////////////////////////
977
978static void removeValueAndDecrementGreaterValues(hidl_vec<uint32_t>* vec, uint32_t value) {
979 if (vec) {
980 // remove elements matching "value"
981 auto last = std::remove(vec->begin(), vec->end(), value);
982 vec->resize(std::distance(vec->begin(), last));
983
984 // decrement elements exceeding "value"
985 std::transform(vec->begin(), vec->end(), vec->begin(),
986 [value](uint32_t v) { return v > value ? v-- : v; });
987 }
988}
989
990static void removeOperand(Model* model, uint32_t index) {
991 hidl_vec_removeAt(&model->operands, index);
992 for (Operation& operation : model->operations) {
993 removeValueAndDecrementGreaterValues(&operation.inputs, index);
994 removeValueAndDecrementGreaterValues(&operation.outputs, index);
995 }
996 removeValueAndDecrementGreaterValues(&model->inputIndexes, index);
997 removeValueAndDecrementGreaterValues(&model->outputIndexes, index);
998}
999
Xusong Wang5b747ae2018-10-05 11:49:13 -07001000static bool removeOperandSkip(size_t operand, const Model& model) {
1001 for (const Operation& operation : model.operations) {
1002 // Skip removeOperandTest for the following operations.
1003 // - SPLIT's outputs are not checked during prepareModel.
1004 if (operation.type == OperationType::SPLIT) {
1005 for (const size_t outOprand : operation.outputs) {
1006 if (operand == outOprand) {
1007 return true;
1008 }
1009 }
1010 }
Viet Danga8f33f72019-03-28 17:22:56 +00001011 // BIDIRECTIONAL_SEQUENCE_LSTM and BIDIRECTIONAL_SEQUENCE_RNN can have either one or two
1012 // outputs depending on their mergeOutputs parameter.
1013 if (operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_LSTM ||
1014 operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_RNN) {
Lev Proleev923b8c52019-01-30 17:14:40 +00001015 for (const size_t outOprand : operation.outputs) {
1016 if (operand == outOprand) {
1017 return true;
1018 }
1019 }
1020 }
Xusong Wang5b747ae2018-10-05 11:49:13 -07001021 }
1022 return false;
1023}
1024
Slava Shklyaev871be942018-09-12 14:52:02 +01001025static void removeOperandTest(const sp<IDevice>& device, const Model& model) {
1026 for (size_t operand = 0; operand < model.operands.size(); ++operand) {
Xusong Wang5b747ae2018-10-05 11:49:13 -07001027 if (removeOperandSkip(operand, model)) {
1028 continue;
1029 }
Slava Shklyaev871be942018-09-12 14:52:02 +01001030 const std::string message = "removeOperandTest: operand " + std::to_string(operand);
1031 validate(device, message, model,
Michael Butlerda1a6922020-03-11 18:45:45 -07001032 [operand](Model* model, ExecutionPreference*) { removeOperand(model, operand); });
Slava Shklyaev871be942018-09-12 14:52:02 +01001033 }
1034}
1035
1036///////////////////////// REMOVE OPERATION /////////////////////////
1037
1038static void removeOperation(Model* model, uint32_t index) {
1039 for (uint32_t operand : model->operations[index].inputs) {
1040 model->operands[operand].numberOfConsumers--;
1041 }
1042 hidl_vec_removeAt(&model->operations, index);
1043}
1044
1045static void removeOperationTest(const sp<IDevice>& device, const Model& model) {
1046 for (size_t operation = 0; operation < model.operations.size(); ++operation) {
1047 const std::string message = "removeOperationTest: operation " + std::to_string(operation);
Michael Butlerda1a6922020-03-11 18:45:45 -07001048 validate(device, message, model, [operation](Model* model, ExecutionPreference*) {
1049 removeOperation(model, operation);
1050 });
Slava Shklyaev871be942018-09-12 14:52:02 +01001051 }
1052}
1053
1054///////////////////////// REMOVE OPERATION INPUT /////////////////////////
1055
Xusong Wang5b747ae2018-10-05 11:49:13 -07001056static bool removeOperationInputSkip(const Operation& op, size_t input) {
1057 // Skip removeOperationInputTest for the following operations.
1058 // - CONCATENATION has at least 2 inputs, with the last element being INT32.
1059 // - CONV_2D, DEPTHWISE_CONV_2D, MAX_POOL_2D, AVERAGE_POOL_2D, L2_POOL_2D, RESIZE_BILINEAR,
1060 // SPACE_TO_DEPTH, SPACE_TO_DEPTH, SPACE_TO_BATCH_ND, BATCH_TO_SPACE_ND can have an optional
1061 // layout parameter.
1062 // - L2_NORMALIZATION, LOCAL_RESPONSE_NORMALIZATION, SOFTMAX can have an optional axis
1063 // parameter.
1064 switch (op.type) {
1065 case OperationType::CONCATENATION: {
1066 if (op.inputs.size() > 2 && input != op.inputs.size() - 1) {
1067 return true;
1068 }
1069 } break;
1070 case OperationType::DEPTHWISE_CONV_2D: {
1071 if ((op.inputs.size() == 12 && input == 11) || (op.inputs.size() == 9 && input == 8)) {
1072 return true;
1073 }
1074 } break;
1075 case OperationType::CONV_2D:
1076 case OperationType::AVERAGE_POOL_2D:
1077 case OperationType::MAX_POOL_2D:
1078 case OperationType::L2_POOL_2D: {
1079 if ((op.inputs.size() == 11 && input == 10) || (op.inputs.size() == 8 && input == 7)) {
1080 return true;
1081 }
1082 } break;
1083 case OperationType::RESIZE_BILINEAR: {
1084 if (op.inputs.size() == 4 && input == 3) {
1085 return true;
1086 }
1087 } break;
1088 case OperationType::SPACE_TO_DEPTH:
1089 case OperationType::DEPTH_TO_SPACE:
1090 case OperationType::BATCH_TO_SPACE_ND: {
1091 if (op.inputs.size() == 3 && input == 2) {
1092 return true;
1093 }
1094 } break;
1095 case OperationType::SPACE_TO_BATCH_ND: {
1096 if (op.inputs.size() == 4 && input == 3) {
1097 return true;
1098 }
1099 } break;
1100 case OperationType::L2_NORMALIZATION: {
1101 if (op.inputs.size() == 2 && input == 1) {
1102 return true;
1103 }
1104 } break;
1105 case OperationType::LOCAL_RESPONSE_NORMALIZATION: {
1106 if (op.inputs.size() == 6 && input == 5) {
1107 return true;
1108 }
1109 } break;
1110 case OperationType::SOFTMAX: {
1111 if (op.inputs.size() == 3 && input == 2) {
1112 return true;
1113 }
1114 } break;
1115 default:
1116 break;
1117 }
1118 return false;
1119}
1120
Slava Shklyaev871be942018-09-12 14:52:02 +01001121static void removeOperationInputTest(const sp<IDevice>& device, const Model& model) {
1122 for (size_t operation = 0; operation < model.operations.size(); ++operation) {
1123 for (size_t input = 0; input < model.operations[operation].inputs.size(); ++input) {
1124 const Operation& op = model.operations[operation];
Xusong Wang5b747ae2018-10-05 11:49:13 -07001125 if (removeOperationInputSkip(op, input)) {
Slava Shklyaev871be942018-09-12 14:52:02 +01001126 continue;
1127 }
1128 const std::string message = "removeOperationInputTest: operation " +
1129 std::to_string(operation) + ", input " +
1130 std::to_string(input);
Michael Butlerda1a6922020-03-11 18:45:45 -07001131 validate(device, message, model,
1132 [operation, input](Model* model, ExecutionPreference*) {
1133 uint32_t operand = model->operations[operation].inputs[input];
1134 model->operands[operand].numberOfConsumers--;
1135 hidl_vec_removeAt(&model->operations[operation].inputs, input);
1136 });
Slava Shklyaev871be942018-09-12 14:52:02 +01001137 }
1138 }
1139}
1140
1141///////////////////////// REMOVE OPERATION OUTPUT /////////////////////////
1142
1143static void removeOperationOutputTest(const sp<IDevice>& device, const Model& model) {
1144 for (size_t operation = 0; operation < model.operations.size(); ++operation) {
1145 for (size_t output = 0; output < model.operations[operation].outputs.size(); ++output) {
1146 const std::string message = "removeOperationOutputTest: operation " +
1147 std::to_string(operation) + ", output " +
1148 std::to_string(output);
Michael Butlerda1a6922020-03-11 18:45:45 -07001149 validate(device, message, model,
1150 [operation, output](Model* model, ExecutionPreference*) {
1151 hidl_vec_removeAt(&model->operations[operation].outputs, output);
1152 });
Slava Shklyaev871be942018-09-12 14:52:02 +01001153 }
1154 }
1155}
1156
1157///////////////////////// MODEL VALIDATION /////////////////////////
1158
1159// TODO: remove model input
1160// TODO: remove model output
1161// TODO: add unused operation
1162
1163///////////////////////// ADD OPERATION INPUT /////////////////////////
1164
Xusong Wang5b747ae2018-10-05 11:49:13 -07001165static bool addOperationInputSkip(const Operation& op) {
Xusong Wang64337282018-10-22 13:49:00 -07001166 // Skip addOperationInputTest for the following operations.
Xusong Wang5b747ae2018-10-05 11:49:13 -07001167 // - L2_NORMALIZATION, LOCAL_RESPONSE_NORMALIZATION, SOFTMAX can have an optional INT32 axis
1168 // parameter.
1169 if ((op.type == OperationType::L2_NORMALIZATION && op.inputs.size() == 1) ||
1170 (op.type == OperationType::LOCAL_RESPONSE_NORMALIZATION && op.inputs.size() == 5) ||
1171 (op.type == OperationType::SOFTMAX && op.inputs.size() == 2)) {
Xusong Wang64337282018-10-22 13:49:00 -07001172 return true;
1173 }
1174 return false;
1175}
1176
Slava Shklyaev871be942018-09-12 14:52:02 +01001177static void addOperationInputTest(const sp<IDevice>& device, const Model& model) {
1178 for (size_t operation = 0; operation < model.operations.size(); ++operation) {
Xusong Wang64337282018-10-22 13:49:00 -07001179 if (addOperationInputSkip(model.operations[operation])) {
1180 continue;
1181 }
Slava Shklyaev871be942018-09-12 14:52:02 +01001182 const std::string message = "addOperationInputTest: operation " + std::to_string(operation);
Michael Butlerda1a6922020-03-11 18:45:45 -07001183 validate(device, message, model, [operation](Model* model, ExecutionPreference*) {
Slava Shklyaev871be942018-09-12 14:52:02 +01001184 uint32_t index = addOperand(model, OperandLifeTime::MODEL_INPUT);
1185 hidl_vec_push_back(&model->operations[operation].inputs, index);
1186 hidl_vec_push_back(&model->inputIndexes, index);
1187 });
1188 }
1189}
1190
1191///////////////////////// ADD OPERATION OUTPUT /////////////////////////
1192
1193static void addOperationOutputTest(const sp<IDevice>& device, const Model& model) {
1194 for (size_t operation = 0; operation < model.operations.size(); ++operation) {
1195 const std::string message =
Slava Shklyaev1d6b4652019-05-14 14:15:14 +01001196 "addOperationOutputTest: operation " + std::to_string(operation);
Michael Butlerda1a6922020-03-11 18:45:45 -07001197 validate(device, message, model, [operation](Model* model, ExecutionPreference*) {
Slava Shklyaev871be942018-09-12 14:52:02 +01001198 uint32_t index = addOperand(model, OperandLifeTime::MODEL_OUTPUT);
1199 hidl_vec_push_back(&model->operations[operation].outputs, index);
1200 hidl_vec_push_back(&model->outputIndexes, index);
1201 });
1202 }
1203}
1204
1205///////////////////////// VALIDATE EXECUTION PREFERENCE /////////////////////////
1206
1207static const int32_t invalidExecutionPreferences[] = {
Slava Shklyaev1d6b4652019-05-14 14:15:14 +01001208 static_cast<int32_t>(ExecutionPreference::LOW_POWER) - 1, // lower bound
1209 static_cast<int32_t>(ExecutionPreference::SUSTAINED_SPEED) + 1, // upper bound
Slava Shklyaev871be942018-09-12 14:52:02 +01001210};
1211
1212static void mutateExecutionPreferenceTest(const sp<IDevice>& device, const Model& model) {
Michael Butlerda1a6922020-03-11 18:45:45 -07001213 for (int32_t invalidPreference : invalidExecutionPreferences) {
Slava Shklyaev871be942018-09-12 14:52:02 +01001214 const std::string message =
Michael Butlerda1a6922020-03-11 18:45:45 -07001215 "mutateExecutionPreferenceTest: preference " + std::to_string(invalidPreference);
1216 validate(device, message, model,
1217 [invalidPreference](Model*, ExecutionPreference* preference) {
1218 *preference = static_cast<ExecutionPreference>(invalidPreference);
1219 });
Slava Shklyaev871be942018-09-12 14:52:02 +01001220 }
1221}
1222
1223////////////////////////// ENTRY POINT //////////////////////////////
1224
Michael Butlere16af0a2019-08-29 22:17:24 -07001225void validateModel(const sp<IDevice>& device, const Model& model) {
David Gross12ab6c52018-05-14 12:23:04 -07001226 mutateExecutionOrderTest(device, model);
Slava Shklyaev871be942018-09-12 14:52:02 +01001227 mutateOperandTypeTest(device, model);
1228 mutateOperandRankTest(device, model);
1229 mutateOperandScaleTest(device, model);
1230 mutateOperandZeroPointTest(device, model);
David Gross12ab6c52018-05-14 12:23:04 -07001231 mutateOperandLifeTimeTest(device, model);
1232 mutateOperandInputOutputTest(device, model);
1233 mutateOperandNumberOfConsumersTest(device, model);
1234 mutateOperandAddWriterTest(device, model);
Slava Shklyaev871be942018-09-12 14:52:02 +01001235 mutateOperationOperandTypeTest(device, model);
1236 mutateOperationTypeTest(device, model);
1237 mutateOperationInputOperandIndexTest(device, model);
1238 mutateOperationOutputOperandIndexTest(device, model);
David Gross12ab6c52018-05-14 12:23:04 -07001239 mutateOperationRemoveWriteTest(device, model);
Slava Shklyaev871be942018-09-12 14:52:02 +01001240 removeOperandTest(device, model);
1241 removeOperationTest(device, model);
1242 removeOperationInputTest(device, model);
1243 removeOperationOutputTest(device, model);
1244 addOperationInputTest(device, model);
1245 addOperationOutputTest(device, model);
1246 mutateExecutionPreferenceTest(device, model);
1247}
1248
Michael Butlerbbe5dad2019-08-26 23:55:47 -07001249} // namespace android::hardware::neuralnetworks::V1_2::vts::functional