From f29a2c55af463141fac7c92042bfdb9f00ba4ccd Mon Sep 17 00:00:00 2001 From: Kevin May Date: Thu, 14 Mar 2019 11:56:32 +0000 Subject: MLCE-91 LSTM doesn't support optional input * Add fix for optional NO_VALUE operands in ConversionUtils.hpp * Remove fail message for optional NO_VALUE in ConversionUtils.hpp * Add to existing tests and test helper to cover optional NO_VALUE Signed-off-by: Kevin May Change-Id: Icf36af1fc00d3fb33cdd77ff6d6618cc4700d3fd --- ConversionUtils.hpp | 19 ++++++++++++++++--- test/DriverTestHelpers.hpp | 14 ++++++++++---- test/Lstm.cpp | 35 +++++++++++++++++++++++++++-------- 3 files changed, 53 insertions(+), 15 deletions(-) diff --git a/ConversionUtils.hpp b/ConversionUtils.hpp index c86ad93c..ca1f0aea 100644 --- a/ConversionUtils.hpp +++ b/ConversionUtils.hpp @@ -488,13 +488,16 @@ ConstTensorPin ConvertOperandToConstTensorPin(const Operand& operand, return ConstTensorPin(); } - if (operand.lifetime != OperandLifeTime::CONSTANT_COPY && operand.lifetime != OperandLifeTime::CONSTANT_REFERENCE) + if (!optional && + operand.lifetime != OperandLifeTime::CONSTANT_COPY && + operand.lifetime != OperandLifeTime::CONSTANT_REFERENCE && + operand.lifetime != OperandLifeTime::NO_VALUE) { Fail("%s: invalid operand lifetime: %s", __func__, toString(operand.lifetime).c_str()); return ConstTensorPin(); } - const void* const valueStart = GetOperandValueReadOnlyAddress(operand, model, data); + const void* const valueStart = GetOperandValueReadOnlyAddress(operand, model, data, optional); if (!valueStart) { if (optional) @@ -539,7 +542,8 @@ ConstTensorPin ConvertOperationInputToConstTensorPin(const HalOperation& operati } template -const void* GetOperandValueReadOnlyAddress(const Operand& operand, const HalModel& model, const ConversionData& data) +const void* GetOperandValueReadOnlyAddress(const Operand& operand, const HalModel& model, const ConversionData& data, + bool optional = false) { const void* valueStart = nullptr; @@ -557,6 +561,15 @@ const void* GetOperandValueReadOnlyAddress(const Operand& operand, const HalMode valueStart = GetMemoryFromPool(operand.location, data.m_MemPools); break; } + case OperandLifeTime::NO_VALUE: + { + // An optional input tensor with no values is not an error so should not register as a fail + if (optional) + { + valueStart = nullptr; + break; + } + } default: { // Unsupported/invalid (e.g. can't get value of an input to the model) diff --git a/test/DriverTestHelpers.hpp b/test/DriverTestHelpers.hpp index 4d91ae22..394720ed 100644 --- a/test/DriverTestHelpers.hpp +++ b/test/DriverTestHelpers.hpp @@ -112,7 +112,8 @@ template void AddTensorOperand(HalModel& model, const hidl_vec& dimensions, const T* values, - OperandType operandType = OperandType::TENSOR_FLOAT32) + OperandType operandType = OperandType::TENSOR_FLOAT32, + OperandLifeTime operandLifeTime = OperandLifeTime::CONSTANT_COPY) { uint32_t totalElements = 1; for (uint32_t dim : dimensions) @@ -121,9 +122,13 @@ void AddTensorOperand(HalModel& model, } DataLocation location = {}; - location.offset = model.operandValues.size(); location.length = totalElements * sizeof(T); + if(operandLifeTime == OperandLifeTime::CONSTANT_COPY) + { + location.offset = model.operandValues.size(); + } + Operand op = {}; op.type = operandType; op.dimensions = dimensions; @@ -143,9 +148,10 @@ template void AddTensorOperand(HalModel& model, const hidl_vec& dimensions, const std::vector& values, - OperandType operandType = OperandType::TENSOR_FLOAT32) + OperandType operandType = OperandType::TENSOR_FLOAT32, + OperandLifeTime operandLifeTime = OperandLifeTime::CONSTANT_COPY) { - AddTensorOperand(model, dimensions, values.data(), operandType); + AddTensorOperand(model, dimensions, values.data(), operandType, operandLifeTime); } template diff --git a/test/Lstm.cpp b/test/Lstm.cpp index ea0405c6..66f2cf02 100644 --- a/test/Lstm.cpp +++ b/test/Lstm.cpp @@ -56,6 +56,17 @@ bool TolerantCompareEqual(float a, float b, float tolerance = 0.00001f) return rd < tolerance; } +// Helper function to create an OperandLifeTime::NO_VALUE for testing. +// To be used on optional input operands that have no values - these are valid and should be tested. +OperandLifeTime CreateNoValueLifeTime(const hidl_vec& dimensions) +{ + // Only create a NO_VALUE for optional operands that have no elements + if (dimensions.size() == 0 || dimensions[0] == 0) + { + return OperandLifeTime::NO_VALUE; + } + return OperandLifeTime::CONSTANT_COPY; +} } // anonymous namespace // Add our own tests here since we fail the lstm tests which Google supplies (because of non-const weights) @@ -126,7 +137,8 @@ void LstmTestImpl(const hidl_vec& inputDimensions, // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape // [num_units, input_size], where “num_units” corresponds to the number of cell units. - AddTensorOperand(model, inputToInputWeightsDimensions, inputToInputWeightsValue); + AddTensorOperand(model, inputToInputWeightsDimensions, inputToInputWeightsValue, OperandType::TENSOR_FLOAT32, + CreateNoValueLifeTime(inputToInputWeightsDimensions)); // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape // [num_units, input_size]. AddTensorOperand(model, inputToForgetWeightsDimensions, inputToForgetWeightsValue); @@ -138,7 +150,8 @@ void LstmTestImpl(const hidl_vec& inputDimensions, // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e., // “num_units”), or the second dimension of the “projection_weights”, if defined. - AddTensorOperand(model, recurrentToInputWeightsDimensions, recurrentToInputWeightsValue); + AddTensorOperand(model, recurrentToInputWeightsDimensions, recurrentToInputWeightsValue, + OperandType::TENSOR_FLOAT32, CreateNoValueLifeTime(recurrentToInputWeightsDimensions)); // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape // [num_units, output_size]. AddTensorOperand(model, recurrentToForgetWeightsDimensions, recurrentToForgetWeightsValue); @@ -149,13 +162,17 @@ void LstmTestImpl(const hidl_vec& inputDimensions, // [num_units, output_size]. AddTensorOperand(model, recurrentToOutputWeightsDimensions, recurrentToOutputWeightsValue); // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units]. - AddTensorOperand(model, cellToInputWeightsDimensions, cellToInputWeightsValue); + AddTensorOperand(model, cellToInputWeightsDimensions, cellToInputWeightsValue, + OperandType::TENSOR_FLOAT32, CreateNoValueLifeTime(cellToInputWeightsDimensions)); // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units]. - AddTensorOperand(model, cellToForgetWeightsDimensions, cellToForgetWeightsValue); + AddTensorOperand(model, cellToForgetWeightsDimensions, cellToForgetWeightsValue, + OperandType::TENSOR_FLOAT32, CreateNoValueLifeTime(cellToForgetWeightsDimensions)); // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units]. - AddTensorOperand(model, cellToOutputWeightsDimensions, cellToOutputWeightsValue); + AddTensorOperand(model, cellToOutputWeightsDimensions, cellToOutputWeightsValue, + OperandType::TENSOR_FLOAT32, CreateNoValueLifeTime(cellToOutputWeightsDimensions)); // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units]. - AddTensorOperand(model, inputGateBiasDimensions, inputGateBiasValue); + AddTensorOperand(model, inputGateBiasDimensions, inputGateBiasValue, + OperandType::TENSOR_FLOAT32, CreateNoValueLifeTime(inputGateBiasDimensions)); // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units]. AddTensorOperand(model, forgetGateBiasDimensions, forgetGateBiasValue); // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units]. @@ -164,9 +181,11 @@ void LstmTestImpl(const hidl_vec& inputDimensions, AddTensorOperand(model, outputGateBiasDimensions, outputGateBiasValue); // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape // [output_size, num_units]. - AddTensorOperand(model, projectionWeightsDimensions, projectionWeightsValue); + AddTensorOperand(model, projectionWeightsDimensions, projectionWeightsValue, + OperandType::TENSOR_FLOAT32, CreateNoValueLifeTime(projectionWeightsDimensions)); // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size]. - AddTensorOperand(model, projectionBiasDimensions, projectionBiasValue); + AddTensorOperand(model, projectionBiasDimensions, projectionBiasValue, + OperandType::TENSOR_FLOAT32, CreateNoValueLifeTime(projectionBiasDimensions)); // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. AddInputOperand(model, outputStateInDimensions); -- cgit v1.2.1