From 618028b888dadc61152e0e0afc623a5de268df8a Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Thu, 20 Sep 2018 11:35:42 -0700 Subject: [PATCH 1/4] Add HAL entry to allow querying device impl version. Bug: 111425781 Bug: 112661050 Test: mm Test: NeuralNetworksTest_static Change-Id: I32527fa09e45459bc9759f5b679646073cf96785 Merged-In: I32527fa09e45459bc9759f5b679646073cf96785 (cherry picked from commit 44b029b181b554931367c670f8bd026ceacc0536) --- neuralnetworks/1.2/IDevice.hal | 30 +++++++++++++++++++ .../1.2/vts/functional/BasicTests.cpp | 8 +++++ 2 files changed, 38 insertions(+) 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/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 From 08662c6f66fa724a46b0ce1ae13a3c9358a658b4 Mon Sep 17 00:00:00 2001 From: Lev Proleev Date: Mon, 1 Oct 2018 11:18:31 +0100 Subject: [PATCH 2/4] Add new OperandType BOOL. - Add new enum OperandType::BOOL. - Add v1.2 Operand, OperandType. - Add VTS validation tests for BOOL. Bug: 117423393 Test: NeuralNetworksTest_static Test: VtsHalNeuralnetworksV1_2TargetTest Change-Id: I420e2afeb09b881a499eee6b138c1f26e9874f5a Merged-In: I420e2afeb09b881a499eee6b138c1f26e9874f5a (cherry picked from commit abad9eac448cc61582a9a658a231010358051b97) --- .../1.1/vts/functional/VtsHalNeuralnetworks.h | 4 +- neuralnetworks/1.2/Android.bp | 2 + neuralnetworks/1.2/types.hal | 109 +++++++++++++++++- .../1.2/vts/functional/ValidateModel.cpp | 14 ++- 4 files changed, 121 insertions(+), 8 deletions(-) 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/types.hal b/neuralnetworks/1.2/types.hal index bed1d5cad9..95e97c4b6b 100644 --- a/neuralnetworks/1.2/types.hal +++ b/neuralnetworks/1.2/types.hal @@ -16,10 +16,22 @@ 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, +}; + /** * Operation types. * @@ -101,6 +113,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/ValidateModel.cpp b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp index 7ec6ff183e..5a8b8c59c0 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::BOOL) + 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,6 +155,7 @@ static uint32_t getInvalidRank(OperandType type) { case OperandType::FLOAT32: case OperandType::INT32: case OperandType::UINT32: + case OperandType::BOOL: return 1; case OperandType::TENSOR_FLOAT32: case OperandType::TENSOR_INT32: @@ -185,6 +184,7 @@ static float getInvalidScale(OperandType type) { case OperandType::FLOAT32: case OperandType::INT32: case OperandType::UINT32: + case OperandType::BOOL: case OperandType::TENSOR_FLOAT32: return 1.0f; case OperandType::TENSOR_INT32: @@ -214,6 +214,7 @@ static std::vector getInvalidZeroPoints(OperandType type) { case OperandType::FLOAT32: case OperandType::INT32: case OperandType::UINT32: + case OperandType::BOOL: case OperandType::TENSOR_FLOAT32: case OperandType::TENSOR_INT32: return {1}; @@ -253,6 +254,7 @@ 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; From 68c8c174678f0881e4e031711397b6451479e36d Mon Sep 17 00:00:00 2001 From: Lev Proleev Date: Mon, 1 Oct 2018 11:18:31 +0100 Subject: [PATCH 3/4] Add TENSOR_QUANT16_ASYMM to operand types Add new OperandType::TENSOR_QUANT16_ASYMM. Add VTS validation for the new type. Bug: 113561892 Test: NeuralNetworksTest_static Test: VtsHalNeuralnetworksV1_0TargetTest Test: VtsHalNeuralnetworksV1_1TargetTest Test: VtsHalNeuralnetworksV1_2TargetTest Change-Id: I4f9ed6a33d5d3ec227e9f335df71954c73edf344 Merged-In: I4f9ed6a33d5d3ec227e9f335df71954c73edf344 (cherry picked from commit 5d7c99527e7bad07d6ab5413bcfd14cec5df5f31) --- neuralnetworks/1.2/types.hal | 12 ++++++++++++ neuralnetworks/1.2/vts/functional/ValidateModel.cpp | 12 ++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal index 95e97c4b6b..0aa7cc204b 100644 --- a/neuralnetworks/1.2/types.hal +++ b/neuralnetworks/1.2/types.hal @@ -30,6 +30,18 @@ enum OperandType : @1.0::OperandType { * 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, }; /** diff --git a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp index 5a8b8c59c0..9af6258917 100644 --- a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp +++ b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp @@ -129,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::BOOL) + 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) { @@ -160,6 +160,7 @@ static uint32_t getInvalidRank(OperandType type) { case OperandType::TENSOR_FLOAT32: case OperandType::TENSOR_INT32: case OperandType::TENSOR_QUANT8_ASYMM: + case OperandType::TENSOR_QUANT16_ASYMM: return 0; default: return 0; @@ -190,6 +191,7 @@ static float getInvalidScale(OperandType type) { case OperandType::TENSOR_INT32: return -1.0f; case OperandType::TENSOR_QUANT8_ASYMM: + case OperandType::TENSOR_QUANT16_ASYMM: return 0.0f; default: return 0.0f; @@ -219,6 +221,7 @@ static std::vector getInvalidZeroPoints(OperandType type) { case OperandType::TENSOR_INT32: return {1}; case OperandType::TENSOR_QUANT8_ASYMM: + case OperandType::TENSOR_QUANT16_ASYMM: return {-1, 256}; default: return {}; @@ -271,6 +274,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; From 5dd841253d7c490f71209e401e8fae64328ca305 Mon Sep 17 00:00:00 2001 From: "Michael K. Sanders" Date: Fri, 12 Oct 2018 09:10:15 +0100 Subject: [PATCH 4/4] Adds TENSOR_FLOAT16 operand type. Bug: 113563458 Test: VtsHalNeuralnetworksV1_2TargetTest --hal_service_instance=android.hardware.neuralnetworks@1.2::IDevice/sample-all Change-Id: If12ceff428e1b1a90ef99b7353f0df60d4ef8010 Merged-In: If12ceff428e1b1a90ef99b7353f0df60d4ef8010 (cherry picked from commit 19d63453d4c2723c3fad4ce7f852f548d761278e) --- neuralnetworks/1.2/types.hal | 2 ++ neuralnetworks/1.2/vts/functional/ValidateModel.cpp | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal index 0aa7cc204b..4a1e7a8a40 100644 --- a/neuralnetworks/1.2/types.hal +++ b/neuralnetworks/1.2/types.hal @@ -42,6 +42,8 @@ enum OperandType : @1.0::OperandType { * realValue = (integerValue - zeroPoint) * scale. */ TENSOR_QUANT16_ASYMM = 7, + /** A tensor of 16 bit floating point values. */ + TENSOR_FLOAT16 = 8, }; /** diff --git a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp index 9af6258917..b840199d43 100644 --- a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp +++ b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp @@ -157,6 +157,7 @@ static uint32_t getInvalidRank(OperandType type) { 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: @@ -186,6 +187,7 @@ static float getInvalidScale(OperandType type) { case OperandType::INT32: case OperandType::UINT32: case OperandType::BOOL: + case OperandType::TENSOR_FLOAT16: case OperandType::TENSOR_FLOAT32: return 1.0f; case OperandType::TENSOR_INT32: @@ -217,6 +219,7 @@ static std::vector getInvalidZeroPoints(OperandType type) { case OperandType::INT32: case OperandType::UINT32: case OperandType::BOOL: + case OperandType::TENSOR_FLOAT16: case OperandType::TENSOR_FLOAT32: case OperandType::TENSOR_INT32: return {1}; @@ -262,6 +265,7 @@ static void mutateOperand(Operand* operand, OperandType type) { 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});