diff --git a/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.h index 0050e52d25..a64268f46e 100644 --- a/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.h +++ b/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.h @@ -34,9 +34,11 @@ namespace hardware { namespace neuralnetworks { namespace V1_1 { -using V1_0::Request; using V1_0::DeviceStatus; using V1_0::ErrorStatus; +using V1_0::Operand; +using V1_0::OperandType; +using V1_0::Request; namespace vts { namespace functional { diff --git a/neuralnetworks/1.2/Android.bp b/neuralnetworks/1.2/Android.bp index d5ef49d4b4..5a661e06a3 100644 --- a/neuralnetworks/1.2/Android.bp +++ b/neuralnetworks/1.2/Android.bp @@ -17,6 +17,8 @@ hidl_interface { ], types: [ "Model", + "Operand", + "OperandType", "Operation", "OperationType", ], diff --git a/neuralnetworks/1.2/IDevice.hal b/neuralnetworks/1.2/IDevice.hal index 9cc23a26f5..aff4cf30f4 100644 --- a/neuralnetworks/1.2/IDevice.hal +++ b/neuralnetworks/1.2/IDevice.hal @@ -25,6 +25,36 @@ import @1.1::IDevice; * This interface represents a device driver. */ interface IDevice extends @1.1::IDevice { + /** + * Get the version string of the driver implementation. + * + * The version string must be a unique token among the set of version strings of + * drivers of a specific device. The token identifies the device driver's + * implementation. The token must not be confused with the feature level which is solely + * defined by the interface version. This API is opaque to the Android framework, but the + * Android framework may use the information for debugging or to pass on to NNAPI applications. + * + * Application developers sometimes have specific requirements to ensure good user experiences, + * and they need more information to make intelligent decisions when the Android framework cannot. + * For example, combined with the device name and other information, the token can help + * NNAPI applications filter devices based on their needs: + * - An application demands a certain level of performance, but a specific version of + * the driver cannot meet that requirement because of a performance regression. + * The application can blacklist the driver based on the version provided. + * - An application has a minimum precision requirement, but certain versions of + * the driver cannot meet that requirement because of bugs or certain optimizations. + * The application can filter out versions of these drivers. + * + * @return status Error status returned from querying the version string. Must be: + * - NONE if the query was successful + * - DEVICE_UNAVAILABLE if driver is offline or busy + * - GENERAL_FAILURE if the query resulted in an + * unspecified error + * @return version The version string of the device implementation. + * Must have nonzero length + */ + getVersionString() generates (ErrorStatus status, string version); + /** * Gets the supported operations in a model. * diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal index bed1d5cad9..4a1e7a8a40 100644 --- a/neuralnetworks/1.2/types.hal +++ b/neuralnetworks/1.2/types.hal @@ -16,10 +16,36 @@ package android.hardware.neuralnetworks@1.2; -import @1.0::Operand; +import @1.0::DataLocation; +import @1.0::OperandLifeTime; +import @1.0::OperandType; import @1.0::PerformanceInfo; import @1.1::OperationType; +enum OperandType : @1.0::OperandType { + /** + * An 8 bit boolean scalar value. + * + * Values of this operand type are either true or false. A zero value + * represents false; any other value represents true. + */ + BOOL = 6, + /** + * A tensor of 16 bit signed integers that represent real numbers. + * + * Attached to this tensor are two numbers that are used to convert the 16 + * bit integer to the real value and vice versa. These two numbers are: + * - scale: a 32 bit floating point value greater than zero. + * - zeroPoint: a 32 bit integer, in range [-32768, 32767]. + * + * The formula is: + * realValue = (integerValue - zeroPoint) * scale. + */ + TENSOR_QUANT16_ASYMM = 7, + /** A tensor of 16 bit floating point values. */ + TENSOR_FLOAT16 = 8, +}; + /** * Operation types. * @@ -101,6 +127,101 @@ struct Operation { vec outputs; }; +/** + * Describes one operand of the model's graph. + */ +struct Operand { + /** + * Data type of the operand. + */ + OperandType type; + + /** + * Dimensions of the operand. + * + * For a scalar operand, dimensions.size() must be 0. + * + * For a tensor operand, dimensions.size() must be at least 1; + * however, any of the dimensions may be unspecified. + * + * A tensor operand with all dimensions specified has "fully + * specified" dimensions. Whenever possible (i.e., whenever the + * dimensions are known at model construction time), a tensor + * operand should have (but is not required to have) fully + * specified dimensions, in order to enable the best possible + * performance. + * + * If a tensor operand's dimensions are not fully specified, the + * dimensions of the operand are deduced from the operand + * dimensions and values of the operation for which that operand + * is an output. + * + * In the following situations, a tensor operand's dimensions must + * be fully specified: + * + * . The operand has lifetime CONSTANT_COPY or + * CONSTANT_REFERENCE. + * + * . The operand has lifetime MODEL_INPUT or MODEL_OUTPUT. Fully + * specified dimensions must either be present in the + * Operand or they must be provided in the corresponding + * RequestArgument. + * EXCEPTION: If the input or output is optional and omitted + * (by setting the hasNoValue field of the corresponding + * RequestArgument to true) then it need not have fully + * specified dimensions. + * + * A tensor operand with some number of unspecified dimensions is + * represented by setting each unspecified dimension to 0. + */ + vec dimensions; + + /** + * The number of times this operand appears as an operation input. + * + * (For example, if this operand appears once in one operation's + * input list, and three times in another operation's input list, + * then numberOfConsumers = 4.) + */ + uint32_t numberOfConsumers; + + /** + * Quantized scale of the operand. + * + * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM or + * TENSOR_INT32. + */ + float scale; + + /** + * Quantized zero-point offset of the operand. + * + * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM. + */ + int32_t zeroPoint; + + /** + * How the operand is used. + */ + OperandLifeTime lifetime; + + /** + * Where to find the data for this operand. + * If the lifetime is TEMPORARY_VARIABLE, MODEL_INPUT, MODEL_OUTPUT, or + * NO_VALUE: + * - All the fields must be 0. + * If the lifetime is CONSTANT_COPY: + * - location.poolIndex is 0. + * - location.offset is the offset in bytes into Model.operandValues. + * - location.length is set. + * If the lifetime is CONSTANT_REFERENCE: + * - location.poolIndex is set. + * - location.offset is the offset in bytes into the specified pool. + * - location.length is set. + */ + DataLocation location; +}; + /** * A Neural Network Model. * diff --git a/neuralnetworks/1.2/vts/functional/BasicTests.cpp b/neuralnetworks/1.2/vts/functional/BasicTests.cpp index d2dea1dc75..eb3ebd326b 100644 --- a/neuralnetworks/1.2/vts/functional/BasicTests.cpp +++ b/neuralnetworks/1.2/vts/functional/BasicTests.cpp @@ -37,6 +37,14 @@ TEST_F(NeuralnetworksHidlTest, StatusTest) { EXPECT_EQ(DeviceStatus::AVAILABLE, static_cast(status)); } +// device version test +TEST_F(NeuralnetworksHidlTest, GetDeviceVersionStringTest) { + Return ret = device->getVersionString([](ErrorStatus status, const hidl_string& version) { + EXPECT_EQ(ErrorStatus::NONE, status); + EXPECT_LT(0, version.size()); + }); + EXPECT_TRUE(ret.isOk()); +} } // namespace functional } // namespace vts } // namespace V1_2 diff --git a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp index 7ec6ff183e..b840199d43 100644 --- a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp +++ b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp @@ -26,9 +26,7 @@ namespace neuralnetworks { namespace V1_2 { using V1_0::IPreparedModel; -using V1_0::Operand; using V1_0::OperandLifeTime; -using V1_0::OperandType; using V1_1::ExecutionPreference; namespace vts { @@ -131,10 +129,10 @@ static uint32_t addOperand(Model* model, OperandLifeTime lifetime) { ///////////////////////// VALIDATE MODEL OPERAND TYPE ///////////////////////// static const int32_t invalidOperandTypes[] = { - static_cast(OperandType::FLOAT32) - 1, // lower bound fundamental - static_cast(OperandType::TENSOR_QUANT8_ASYMM) + 1, // upper bound fundamental - static_cast(OperandType::OEM) - 1, // lower bound OEM - static_cast(OperandType::TENSOR_OEM_BYTE) + 1, // upper bound OEM + static_cast(OperandType::FLOAT32) - 1, // lower bound fundamental + static_cast(OperandType::TENSOR_QUANT16_ASYMM) + 1, // upper bound fundamental + static_cast(OperandType::OEM) - 1, // lower bound OEM + static_cast(OperandType::TENSOR_OEM_BYTE) + 1, // upper bound OEM }; static void mutateOperandTypeTest(const sp& device, const Model& model) { @@ -157,10 +155,13 @@ static uint32_t getInvalidRank(OperandType type) { case OperandType::FLOAT32: case OperandType::INT32: case OperandType::UINT32: + case OperandType::BOOL: return 1; + case OperandType::TENSOR_FLOAT16: case OperandType::TENSOR_FLOAT32: case OperandType::TENSOR_INT32: case OperandType::TENSOR_QUANT8_ASYMM: + case OperandType::TENSOR_QUANT16_ASYMM: return 0; default: return 0; @@ -185,11 +186,14 @@ static float getInvalidScale(OperandType type) { case OperandType::FLOAT32: case OperandType::INT32: case OperandType::UINT32: + case OperandType::BOOL: + case OperandType::TENSOR_FLOAT16: case OperandType::TENSOR_FLOAT32: return 1.0f; case OperandType::TENSOR_INT32: return -1.0f; case OperandType::TENSOR_QUANT8_ASYMM: + case OperandType::TENSOR_QUANT16_ASYMM: return 0.0f; default: return 0.0f; @@ -214,10 +218,13 @@ static std::vector getInvalidZeroPoints(OperandType type) { case OperandType::FLOAT32: case OperandType::INT32: case OperandType::UINT32: + case OperandType::BOOL: + case OperandType::TENSOR_FLOAT16: case OperandType::TENSOR_FLOAT32: case OperandType::TENSOR_INT32: return {1}; case OperandType::TENSOR_QUANT8_ASYMM: + case OperandType::TENSOR_QUANT16_ASYMM: return {-1, 256}; default: return {}; @@ -253,10 +260,12 @@ static void mutateOperand(Operand* operand, OperandType type) { case OperandType::FLOAT32: case OperandType::INT32: case OperandType::UINT32: + case OperandType::BOOL: newOperand.dimensions = hidl_vec(); newOperand.scale = 0.0f; newOperand.zeroPoint = 0; break; + case OperandType::TENSOR_FLOAT16: case OperandType::TENSOR_FLOAT32: newOperand.dimensions = operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec({1}); @@ -269,6 +278,7 @@ static void mutateOperand(Operand* operand, OperandType type) { newOperand.zeroPoint = 0; break; case OperandType::TENSOR_QUANT8_ASYMM: + case OperandType::TENSOR_QUANT16_ASYMM: newOperand.dimensions = operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec({1}); newOperand.scale = operand->scale != 0.0f ? operand->scale : 1.0f;