// // Copyright © 2017 Arm Ltd. All rights reserved. // SPDX-License-Identifier: MIT // #include "WorkloadData.hpp" #include "CpuTensorHandle.hpp" #include #include #include #include #include #include #include using namespace armnnUtils; namespace armnn { //--------------------------------------------------------------- DataType GetBiasDataType(DataType inputDataType) { switch (inputDataType) { case DataType::Float16: return DataType::Float16; case DataType::Float32: return DataType::Float32; case DataType::QuantisedAsymm8: return DataType::Signed32; case DataType::QuantisedSymm16: return DataType::Signed32; default: BOOST_ASSERT_MSG(false, "Invalid input data type"); return DataType::Float32; } } namespace { //--------------------------------------------------------------- //android ndk does not support std::to_string function. template std::string to_string(T value) { std::ostringstream os; os << value; return os.str(); } //--------------------------------------------------------------- void ValidatePointer(const void* ptr, std::string const& descName, std::string const& paramName) { if (!ptr) { throw InvalidArgumentException(descName + ": Invalid null pointer. The " + paramName + " parameter must be set."); } } //--------------------------------------------------------------- void ValidateTensorShapesMatch(const TensorInfo& first, const TensorInfo& second, std::string const& descName, std::string const& firstName, std::string const& secondName) { if (first.GetShape() != second.GetShape()) { throw InvalidArgumentException(descName + ": " + firstName + " & " + secondName + " must have identical shapes"); } } //--------------------------------------------------------------- void ValidateNumInputs(const WorkloadInfo& workloadInfo, std::string const& descName, const unsigned int expectedSize) { if (workloadInfo.m_InputTensorInfos.size() != expectedSize) { throw InvalidArgumentException(descName + ": Requires exactly " + to_string(expectedSize) + "input(s). " + to_string(workloadInfo.m_InputTensorInfos.size()) + " have been provided."); } } //--------------------------------------------------------------- void ValidateNumOutputs(const WorkloadInfo& workloadInfo, std::string const& descName, const unsigned int expectedSize) { if (workloadInfo.m_OutputTensorInfos.size() != expectedSize) { throw InvalidArgumentException(descName + ": Requires exactly " + to_string(expectedSize) + " output(s). " + to_string(workloadInfo.m_OutputTensorInfos.size()) + " has been provided."); } } //--------------------------------------------------------------- void ValidateTensorNumDimensions(const TensorInfo& tensor, std::string const& descName, unsigned int numDimensions, std::string const& tensorName) { if (tensor.GetNumDimensions() != numDimensions) { throw InvalidArgumentException(descName + ": Expected " + to_string(numDimensions) + " but got " + to_string(tensor.GetNumDimensions()) + " dimensions for " + tensorName + " tensor."); } } //--------------------------------------------------------------- void ValidateTensorDataType(const TensorInfo& tensor, DataType dataType, const std::string& descName, std::string const& tensorName) { if (tensor.GetDataType() != dataType) { throw InvalidArgumentException(descName + ": Expected data type " + GetDataTypeName(dataType) + " but got " + GetDataTypeName(tensor.GetDataType()) + " for " + tensorName + " tensor."); } } //--------------------------------------------------------------- void ValidateTensorQuantizationSpace(const TensorInfo& first, const TensorInfo& second, const std::string& descName, std::string const& firstName, std::string const& secondName) { if (!first.IsQuantized() || !second.IsQuantized()) { // Not a quantized type, ignore the validation return; } DataType firstDataType = first.GetDataType(); DataType secondDataType = second.GetDataType(); if (firstDataType != secondDataType) { throw InvalidArgumentException(descName + ": " + firstName + " and " + secondName + " must be of the same quantized type, " + firstName + " is " + GetDataTypeName(firstDataType) + ", " + secondName + " is " + GetDataTypeName(secondDataType)); } if (!first.IsTypeSpaceMatch(second)) { throw InvalidArgumentException(descName + ": " + firstName + " and " + secondName + " must have the same quantization space, " + firstName + " has offset " + to_string(first.GetQuantizationOffset()) + " and scale " + to_string(first.GetQuantizationScale()) + ", " + secondName + " has offset " + to_string(second.GetQuantizationOffset()) + " and scale " + to_string(second.GetQuantizationScale())); } } //--------------------------------------------------------------- void ValidateBiasTensorQuantization(const TensorInfo& biasTensor, const TensorInfo& inputTensorInfo, const TensorInfo& weightsTensorInfo, const std::string& descName) { if (biasTensor.GetQuantizationOffset() != 0) { throw InvalidArgumentException(descName + ": Expected zero quantization offset for bias tensor but got " + to_string(biasTensor.GetQuantizationOffset())); } const float expectedScale = inputTensorInfo.GetQuantizationScale() * weightsTensorInfo.GetQuantizationScale(); if (std::abs(biasTensor.GetQuantizationScale() - expectedScale) > 0.00000001f) { // Print the float values with extra precision to see very small differences std::stringstream msg; msg << std::setprecision(10) << descName << ": Expected " << expectedScale << " quantization scale for bias tensor (the product of the input and weight scales), but got " << biasTensor.GetQuantizationScale(); throw InvalidArgumentException(msg.str()); } } //--------------------------------------------------------------- void ValidateTensors(const std::vector& vec, unsigned int numExpected, const std::string& descName, const std::string& varName) { if (vec.empty() && numExpected > 0) { throw InvalidArgumentException(descName + ": Invalid empty " + varName + " array."); } for (unsigned int i = 0; i < numExpected; ++i) { if (!vec[i]) { throw InvalidArgumentException(descName + ": Invalid NULL for " + varName + to_string(i)); } } } //--------------------------------------------------------------- void ValidateBroadcastTensorShapesMatch(const TensorInfo& first, const TensorInfo& second, const TensorInfo& output, std::string const& descName, std::string const& firstName, std::string const& secondName) { // Tensors must have the same number of dimensions in order to be explicit about which dimensions will get // broadcasted. if (first.GetNumDimensions() != second.GetNumDimensions()) { throw InvalidArgumentException(descName + ": Tensors " + firstName + " & " + secondName + " must have the same number of dimensions in order to be broadcasted"); } uint32_t numDims = first.GetNumDimensions(); std::vector outputDims(numDims, 0u); for (uint32_t i = 0; i < numDims; i++) { const bool dimsNotEqual = first.GetShape()[i] != second.GetShape()[i]; const bool dimsNotOne = (first.GetShape()[i] != 1) && (second.GetShape()[i] != 1); if (dimsNotEqual && dimsNotOne) { throw InvalidArgumentException("Broadcasting is not possible for incompatible shapes"); } outputDims[i] = std::max(first.GetShape()[i], second.GetShape()[i]); } TensorShape broadcastShape = TensorShape(boost::numeric_cast(outputDims.size()), outputDims.data()); if (broadcastShape != output.GetShape()) { throw InvalidArgumentException(descName + ": The tensor shape resulting from adding " + firstName + " & " + secondName + " does not match the output shape"); } } //--------------------------------------------------------------- /// Validates that the output tensor's quantization scale is greater than the product /// of the two input tensors' quantization scales. This is a requirement of the implementation of /// the quantized multiplication. void ValidateTensorQuantizationMultiplier(const TensorInfo& inputTensor1, const TensorInfo& inputTensor2, const TensorInfo& outputTensorInfo, std::string const& descName, const std::string& inputTensor1Name, const std::string& inputTensor2Name, const std::string& outputTensorName) { if (outputTensorInfo.GetDataType() == DataType::QuantisedAsymm8) { if (outputTensorInfo.GetQuantizationScale() <= inputTensor1.GetQuantizationScale() * inputTensor2.GetQuantizationScale()) { std::stringstream msg; msg << descName << ": Quantization scale of " << outputTensorName << " is not greater than " << "the product of the " << inputTensor1Name << " and " << inputTensor2Name << " tensors"; throw InvalidArgumentException(msg.str()); } } } //--------------------------------------------------------------- void ValidateDataTypes(const TensorInfo& info, const std::vector& supportedTypes, std::string const& descName) { auto iterator = std::find(supportedTypes.begin(), supportedTypes.end(), info.GetDataType()); if (iterator == supportedTypes.end()) { throw InvalidArgumentException(descName + ": " + " Tensor type is not supported."); } } } //namespace void QueueDescriptor::ValidateInputsOutputs(const std::string& descName, unsigned int numExpectedIn, unsigned int numExpectedOut) const { ValidateTensors(m_Inputs, numExpectedIn, descName, "input"); ValidateTensors(m_Outputs, numExpectedOut, descName, "output"); } //--------------------------------------------------------------- void MemCopyQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "MemCopyQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "MemCopyQueueDescriptor" , 1); if (workloadInfo.m_InputTensorInfos.size() != workloadInfo.m_OutputTensorInfos.size()) { throw InvalidArgumentException(boost::str( boost::format("Number of input infos (%1%) does not match the number of output infos (%2%)") % workloadInfo.m_InputTensorInfos.size() % workloadInfo.m_OutputTensorInfos.size())); } for (std::size_t i = 0; i < workloadInfo.m_InputTensorInfos.size(); ++i) { if (workloadInfo.m_InputTensorInfos[i].GetNumElements() != workloadInfo.m_OutputTensorInfos[i].GetNumElements()) { throw InvalidArgumentException(boost::str( boost::format("Number of elements for tensor input and output %1% does not match") % i )); } } if (m_Inputs.size() != m_Outputs.size()) { throw InvalidArgumentException(boost::str( boost::format("Number of inputs (%1%) does not match the number of outputs (%2%)") % m_Inputs.size() % m_Outputs.size())); } for (unsigned int i = 0; i < m_Inputs.size(); ++i) { if (!m_Inputs[i]) { throw InvalidArgumentException(boost::str(boost::format("Invalid null input %1%") % i)); } if (!m_Outputs[i]) { throw InvalidArgumentException(boost::str(boost::format("Invalid null output %1%") % i)); } } } //--------------------------------------------------------------- void ActivationQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "ActivationQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "ActivationQueueDescriptor", 1); ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_OutputTensorInfos[0], "ActivationQueueDescriptor", "input", "output"); std::vector supportedTypes = { DataType::Float32, DataType::Float16, DataType::QuantisedAsymm8, DataType::QuantisedSymm16 }; ValidateDataTypes(workloadInfo.m_InputTensorInfos[0], supportedTypes, "ActivationQueueDescriptor"); ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], {workloadInfo.m_InputTensorInfos[0].GetDataType()}, "ActivationQueueDescriptor"); } //--------------------------------------------------------------- void SoftmaxQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "SoftmaxQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "SoftmaxQueueDescriptor", 1); ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_OutputTensorInfos[0], "SoftmaxQueueDescriptor", "input", "output"); std::vector supportedTypes = { DataType::Float16, DataType::Float32, DataType::QuantisedAsymm8, DataType::QuantisedSymm16 }; ValidateDataTypes(workloadInfo.m_InputTensorInfos[0], supportedTypes, "SoftmaxQueueDescriptor"); ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], {workloadInfo.m_InputTensorInfos[0].GetDataType()}, "SoftmaxQueueDescriptor"); } //--------------------------------------------------------------- void SplitterQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "SplitterQueueDescriptor", 1); // Check the supported data types std::vector supportedTypes = { DataType::Float32, DataType::Float16, DataType::Boolean, DataType::Signed32, DataType::QuantisedAsymm8, DataType::QuantisedSymm16 }; for (unsigned long i = 0; i < workloadInfo.m_OutputTensorInfos.size(); ++i) { ValidateDataTypes(workloadInfo.m_OutputTensorInfos[i], supportedTypes, "SplitterQueueDescriptor"); } ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], {workloadInfo.m_InputTensorInfos[0].GetDataType()}, "SplitterQueueDescriptor"); if (workloadInfo.m_OutputTensorInfos.size() <= 0) { throw InvalidArgumentException("SplitterQueueDescriptor: At least one output needs to be provided."); } if (workloadInfo.m_OutputTensorInfos.size() != m_ViewOrigins.size()) { throw InvalidArgumentException( "SplitterQueueDescriptor: Number of split windows " "has to match number of workloadInfo.m_OutputTensorInfos. " "Number of windows: " + to_string(m_ViewOrigins.size()) + ". Number of workloadInfo.m_OutputTensorInfos: " + to_string(workloadInfo.m_OutputTensorInfos.size())); } //The dimensionality of all the windows has to match the dimensionality (not shape) of the input. std::size_t inputDims = workloadInfo.m_InputTensorInfos[0].GetNumDimensions(); for(unsigned int w = 0; w < m_ViewOrigins.size(); ++w ) { //Checks that the dimensionality of input is same as the split windows. ViewOrigin const& e = m_ViewOrigins[w]; if (e.m_Origin.size() != inputDims) { throw InvalidArgumentException("SplitterQueueDescriptor: Window origin have to " "have the same dimensionality as the input tensor. " "Window origin (index: " + to_string(w) + ") has " + to_string(e.m_Origin.size()) + " dimensions, the input " "tensor has " + to_string(inputDims) + " dimensions."); } for (unsigned int i = 0; i < e.m_Origin.size(); ++i) { if (e.m_Origin[i] + workloadInfo.m_OutputTensorInfos[w].GetShape()[i] > workloadInfo.m_InputTensorInfos[0].GetShape()[i]) { throw InvalidArgumentException("SplitterQueueDescriptor: Window extent coordinates have to " "be smaller or equal than the size of the input in that coord."); } } } } //--------------------------------------------------------------- void ConcatQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumOutputs(workloadInfo, "ConcatQueueDescriptor", 1); if (m_Inputs.size() <= 0) { throw InvalidArgumentException("ConcatQueueDescriptor: At least one input needs to be provided."); } if (m_Outputs.size() <= 0) { throw InvalidArgumentException("ConcatQueueDescriptor: At least one output needs to be provided."); } if (workloadInfo.m_InputTensorInfos.size() <= 0) { throw InvalidArgumentException("ConcatQueueDescriptor: At least one TensorInfo input needs to be provided."); } if (workloadInfo.m_OutputTensorInfos.size() <= 0) { throw InvalidArgumentException("ConcatQueueDescriptor: At least one TensorInfo output needs to be provided."); } if(m_Parameters.GetConcatAxis() > workloadInfo.m_InputTensorInfos[0].GetShape().GetNumDimensions()) { throw InvalidArgumentException("Invalid Concatenation Axis provided"); } if (workloadInfo.m_InputTensorInfos[0].GetShape().GetNumDimensions() - m_Parameters.GetConcatAxis() == 1) { return; } if (workloadInfo.m_InputTensorInfos.size() != m_ViewOrigins.size()) { throw InvalidArgumentException( "ConcatQueueDescriptor: Number of split windows " "has to match number of workloadInfo.m_InputTensorInfos. " "Number of windows: " + to_string(m_ViewOrigins.size()) + ". Number of workloadInfo.m_InputTensorInfos: " + to_string(workloadInfo.m_InputTensorInfos.size())); } //The dimensionality of all the windows has to match the dimensionality (not shape) of the output. std::size_t outputDims = workloadInfo.m_OutputTensorInfos[0].GetNumDimensions(); for(unsigned int w = 0; w < m_ViewOrigins.size(); ++w ) { //Checks that the dimensionality of output is same as the split windows. ViewOrigin const& e = m_ViewOrigins[w]; if (e.m_Origin.size() != outputDims) { throw InvalidArgumentException("ConcatQueueDescriptor: Window origin have to " "have the same dimensionality as the output tensor. " "Window origin (index: " + to_string(w) + ") has " + to_string(e.m_Origin.size()) + " dimensions, the output " "tensor has " + to_string(outputDims) + " dimensions."); } //Checks that the merge windows are within the output tensor. for (unsigned int i = 0; i < e.m_Origin.size(); ++i) { if (e.m_Origin[i] + workloadInfo.m_InputTensorInfos[w].GetShape()[i] > workloadInfo.m_OutputTensorInfos[0].GetShape()[i]) { throw InvalidArgumentException("ConcatQueueDescriptor: Window extent coordinates have to " "be smaller or equal than the size of the output in that coord."); } } } // Check the supported data types std::vector supportedTypes = { DataType::Float32, DataType::Float16, DataType::Boolean, DataType::Signed32, DataType::QuantisedAsymm8, DataType::QuantisedSymm16 }; for (unsigned long i = 0; i < workloadInfo.m_InputTensorInfos.size(); ++i) { ValidateDataTypes(workloadInfo.m_InputTensorInfos[i], supportedTypes, "ConcatQueueDescriptor"); } ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], {workloadInfo.m_InputTensorInfos[0].GetDataType()}, "ConcatQueueDescriptor"); } //--------------------------------------------------------------- void FullyConnectedQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "FullyConnectedQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "FullyConnectedQueueDescriptor", 1); ValidateTensorNumDimensions(workloadInfo.m_OutputTensorInfos[0], "FullyConnectedQueueDescriptor", 2, "output"); if (!(workloadInfo.m_InputTensorInfos[0].GetNumDimensions() == 2 || workloadInfo.m_InputTensorInfos[0].GetNumDimensions() == 4)) { throw InvalidArgumentException("FullyConnectedQueueDescriptor: Input tensor must have 2 or 4 dimensions."); } if (m_Weight == nullptr) { throw InvalidArgumentException("FullyConnectedQueueDescriptor: Weight tensor descriptor is missing."); } ValidateTensorNumDimensions(m_Weight->GetTensorInfo(), "FullyConnectedQueueDescriptor", 2, "weight"); if (m_Parameters.m_BiasEnabled) { if (m_Bias == nullptr) { throw InvalidArgumentException("FullyConnectedQueueDescriptor: Bias is enabled but " "bias value tensor descriptor is missing."); } // Validates type and quantization values. ValidateBiasTensorQuantization(m_Bias->GetTensorInfo(), workloadInfo.m_InputTensorInfos[0], m_Weight->GetTensorInfo(), "FullyConnectedQueueDescriptor"); ValidateTensorDataType(m_Bias->GetTensorInfo(), GetBiasDataType(workloadInfo.m_InputTensorInfos[0].GetDataType()), "FullyConnectedQueueDescriptor", "bias"); ValidateTensorNumDimensions(m_Bias->GetTensorInfo(), "FullyConnectedQueueDescriptor", 1, "bias"); } ValidateTensorQuantizationMultiplier(workloadInfo.m_InputTensorInfos[0], m_Weight->GetTensorInfo(), workloadInfo.m_OutputTensorInfos[0], "FullyConnectedQueueDescriptor", "input", "weights", "output"); // Check the supported data types std::vector supportedTypes = { DataType::Float32, DataType::Float16, DataType::QuantisedAsymm8, DataType::QuantisedSymm16 }; ValidateDataTypes(workloadInfo.m_InputTensorInfos[0], supportedTypes, "FullyConnectedQueueDescriptor"); ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], {workloadInfo.m_InputTensorInfos[0].GetDataType()}, "FullyConnectedQueueDescriptor"); } //--------------------------------------------------------------- void NormalizationQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "NormalizationQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "NormalizationQueueDescriptor", 1); ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_OutputTensorInfos[0], "NormalizationQueueDescriptor", "input", "output"); } void AdditionQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "AdditionQueueDescriptor", 2); ValidateNumOutputs(workloadInfo, "AdditionQueueDescriptor", 1); std::vector supportedTypes = { DataType::Float32, DataType::QuantisedAsymm8, DataType::QuantisedSymm16, DataType::Float16 }; ValidateDataTypes(workloadInfo.m_InputTensorInfos[0], supportedTypes, "AdditionQueueDescriptor"); ValidateDataTypes(workloadInfo.m_InputTensorInfos[1], supportedTypes, "AdditionQueueDescriptor"); ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], supportedTypes, "AdditionQueueDescriptor"); ValidateBroadcastTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_InputTensorInfos[1], workloadInfo.m_OutputTensorInfos[0], "AdditionQueueDescriptor", "first input", "second input"); } //--------------------------------------------------------------- void MultiplicationQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "MultiplicationQueueDescriptor", 2); ValidateNumOutputs(workloadInfo, "MultiplicationQueueDescriptor", 1); std::vector supportedTypes = { DataType::Float32, DataType::QuantisedAsymm8, DataType::QuantisedSymm16, DataType::Float16 }; ValidateDataTypes(workloadInfo.m_InputTensorInfos[0], supportedTypes, "MultiplicationQueueDescriptor"); ValidateDataTypes(workloadInfo.m_InputTensorInfos[1], supportedTypes, "MultiplicationQueueDescriptor"); ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], supportedTypes, "MultiplicationQueueDescriptor"); ValidateBroadcastTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_InputTensorInfos[1], workloadInfo.m_OutputTensorInfos[0], "MultiplicationQueueDescriptor", "first input", "second input"); } void BatchNormalizationQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "BatchNormalizationQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "BatchNormalizationQueueDescriptor", 1); const TensorInfo& input = workloadInfo.m_InputTensorInfos[0]; const TensorInfo& output = workloadInfo.m_OutputTensorInfos[0]; std::vector supportedTypes = { DataType::Float16, DataType::Float32, DataType::QuantisedAsymm8, DataType::QuantisedSymm16 }; ValidateDataTypes(input, supportedTypes, "BatchNormalizationQueueDescriptor"); ValidateDataTypes(output, supportedTypes, "BatchNormalizationQueueDescriptor"); ValidateDataTypes(output, { input.GetDataType() }, "BatchNormalizationQueueDescriptor"); ValidateTensorQuantizationSpace(input, output, "BatchNormalizationQueueDescriptor", "input", "output"); ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_OutputTensorInfos[0], "BatchNormalizationQueueDescriptor", "input", "output"); ValidatePointer(m_Mean, "BatchNormalizationQueueDescriptor", "mean"); ValidatePointer(m_Variance, "BatchNormalizationQueueDescriptor", "variance"); ValidatePointer(m_Beta, "BatchNormalizationQueueDescriptor", "beta"); ValidatePointer(m_Gamma, "BatchNormalizationQueueDescriptor", "gamma"); const TensorInfo& mean = m_Mean->GetTensorInfo(); const TensorInfo& variance = m_Variance->GetTensorInfo(); const TensorInfo& beta = m_Beta->GetTensorInfo(); const TensorInfo& gamma = m_Gamma->GetTensorInfo(); ValidateTensorNumDimensions(mean, "BatchNormalizationQueueDescriptor", 1, "mean"); ValidateTensorNumDimensions(variance, "BatchNormalizationQueueDescriptor", 1, "variance"); ValidateTensorNumDimensions(beta, "BatchNormalizationQueueDescriptor", 1, "beta"); ValidateTensorNumDimensions(gamma, "BatchNormalizationQueueDescriptor", 1, "gamma"); ValidateTensorShapesMatch(mean, variance, "BatchNormalizationQueueDescriptor", "mean", "variance"); ValidateTensorShapesMatch(mean, beta, "BatchNormalizationQueueDescriptor", "mean", "beta"); ValidateTensorShapesMatch(mean, gamma, "BatchNormalizationQueueDescriptor", "mean", "gamma"); } void Convolution2dQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "Convolution2dQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "Convolution2dQueueDescriptor", 1); ValidateTensorNumDimensions(workloadInfo.m_InputTensorInfos[0], "Convolution2dQueueDescriptor", 4, "input"); ValidateTensorNumDimensions(workloadInfo.m_OutputTensorInfos[0], "Convolution2dQueueDescriptor", 4, "output"); ValidatePointer(m_Weight, "Convolution2dQueueDescriptor", "weight"); ValidateTensorNumDimensions(m_Weight->GetTensorInfo(), "Convolution2dQueueDescriptor", 4, "weight"); ValidateTensorDataType(m_Weight->GetTensorInfo(), workloadInfo.m_InputTensorInfos[0].GetDataType(), "Convolution2dQueueDescriptor", "weight"); if (m_Parameters.m_BiasEnabled) { ValidateTensorNumDimensions(m_Bias->GetTensorInfo(), "Convolution2dQueueDescriptor", 1, "bias"); ValidateTensorDataType(m_Bias->GetTensorInfo(), GetBiasDataType(workloadInfo.m_InputTensorInfos[0].GetDataType()), "Convolution2dQueueDescriptor", "bias"); ValidateBiasTensorQuantization(m_Bias->GetTensorInfo(), workloadInfo.m_InputTensorInfos[0], m_Weight->GetTensorInfo(), "Convolution2dQueueDescriptor"); } ValidateTensorQuantizationMultiplier(workloadInfo.m_InputTensorInfos[0], m_Weight->GetTensorInfo(), workloadInfo.m_OutputTensorInfos[0], "Convolution2dQueueDescriptor", "input", "weights", "output"); } void DepthwiseConvolution2dQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "DepthwiseConvolution2dQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "DepthwiseConvolution2dQueueDescriptor", 1); ValidateTensorNumDimensions( workloadInfo.m_InputTensorInfos[0], "DepthwiseConvolution2dQueueDescriptor", 4, "input"); ValidateTensorNumDimensions( workloadInfo.m_OutputTensorInfos[0], "DepthwiseConvolution2dQueueDescriptor", 4, "output"); ValidatePointer(m_Weight, "DepthwiseConvolution2dQueueDescriptor", "weight"); ValidateTensorNumDimensions(m_Weight->GetTensorInfo(), "DepthwiseConvolution2dQueueDescriptor", 4, "weight"); if (m_Parameters.m_DilationX < 1 || m_Parameters.m_DilationY < 1 ) { throw InvalidArgumentException( boost::str(boost::format("DepthwiseConvolution2dQueueDescriptor: dilationX (provided %1%) " "and dilationY (provided %2%) cannot be smaller than 1.") % m_Parameters.m_DilationX % m_Parameters.m_DilationX)); } const unsigned int channelIndex = (m_Parameters.m_DataLayout == DataLayout::NCHW) ? 1 : 3; // Expected weight shape: [ M, I, H, W ] - This shape does NOT depend on the data layout // inputChannels * channelMultiplier should be equal to outputChannels. const unsigned int numWeightChannelMultiplier = m_Weight->GetTensorInfo().GetShape()[0]; const unsigned int numWeightInputChannels = m_Weight->GetTensorInfo().GetShape()[1]; const unsigned int numWeightOutputChannels = workloadInfo.m_OutputTensorInfos[0].GetShape()[channelIndex]; if (numWeightChannelMultiplier * numWeightInputChannels != numWeightOutputChannels) { throw InvalidArgumentException( boost::str(boost::format("DepthwiseConvolution2dQueueDescriptor: output_channels (provided %1%) should be " "equal to input_channels (provided %2%) multiplied by channel_multiplier " "(provided %3%).") % numWeightOutputChannels % numWeightInputChannels % numWeightChannelMultiplier)); } if (m_Parameters.m_BiasEnabled) { ValidatePointer(m_Bias, "DepthwiseConvolution2dQueueDescriptor", "bias"); ValidateTensorNumDimensions(m_Bias->GetTensorInfo(), "DepthwiseConvolution2dQueueDescriptor", 1, "bias"); ValidateBiasTensorQuantization(m_Bias->GetTensorInfo(), workloadInfo.m_InputTensorInfos[0], m_Weight->GetTensorInfo(), "DepthwiseConvolution2dQueueDescriptor"); ValidateTensorDataType(m_Bias->GetTensorInfo(), GetBiasDataType(workloadInfo.m_InputTensorInfos[0].GetDataType()), "DepthwiseConvolution2dQueueDescriptor", "bias"); } ValidateTensorQuantizationMultiplier(workloadInfo.m_InputTensorInfos[0], m_Weight->GetTensorInfo(), workloadInfo.m_OutputTensorInfos[0], "DepthwiseConvolution2dQueueDescriptor", "input", "weights", "output"); // Check the supported data types std::vector supportedTypes = { DataType::Float32, DataType::QuantisedAsymm8, DataType::QuantisedSymm16, DataType::Float16 }; ValidateDataTypes(workloadInfo.m_InputTensorInfos[0], supportedTypes, "DepthwiseConvolution2dQueueDescriptor"); ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], {workloadInfo.m_InputTensorInfos[0].GetDataType()}, "DepthwiseConvolution2dQueueDescriptor"); } void PermuteQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "PermuteQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "PermuteQueueDescriptor", 1); const PermutationVector& mapping = m_Parameters.m_DimMappings; const TensorInfo& input = workloadInfo.m_InputTensorInfos[0]; const TensorInfo& output = workloadInfo.m_OutputTensorInfos[0]; ValidateTensorNumDimensions(input, "PermuteQueueDescriptor", mapping.GetSize(), "input"); ValidateTensorNumDimensions(output, "PermuteQueueDescriptor", mapping.GetSize(), "output"); for (unsigned int i = 0; i < mapping.GetSize(); ++i) { if (input.GetShape()[i] != output.GetShape()[mapping[i]]) { throw InvalidArgumentException("PermuteQueueDescriptor: src dimension " + to_string(i) + " (=" + to_string(input.GetShape()[i]) + ") " + "must match dst dimension " + to_string(mapping[i]) + " (=" + to_string(output.GetShape()[mapping[i]]) + ")"); } } } void Pooling2dQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "Pooling2dQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "Pooling2dQueueDescriptor", 1); ValidateTensorNumDimensions(workloadInfo.m_InputTensorInfos[0], "Pooling2dQueueDescriptor", 4, "input"); ValidateTensorNumDimensions(workloadInfo.m_OutputTensorInfos[0], "Pooling2dQueueDescriptor", 4, "output"); } void ResizeBilinearQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "ResizeBilinearQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "ResizeBilinearQueueDescriptor", 1); ValidateTensorNumDimensions(workloadInfo.m_InputTensorInfos[0], "ResizeBilinearQueueDescriptor", 4, "input"); ValidateTensorNumDimensions(workloadInfo.m_OutputTensorInfos[0], "ResizeBilinearQueueDescriptor", 4, "output"); // Resizes bilinear only changes width and height: batch and channel count must match. { const unsigned int inputBatchSize = workloadInfo.m_InputTensorInfos[0].GetShape()[0]; const unsigned int outputBatchSize = workloadInfo.m_OutputTensorInfos[0].GetShape()[0]; if (inputBatchSize != outputBatchSize) { throw InvalidArgumentException( boost::str(boost::format("ResizeBilinearQueueDescriptor: Input batch size (%1%) " "does not match output batch size (%2%)") % inputBatchSize % outputBatchSize)); } } { DataLayoutIndexed dimensionIndices(m_Parameters.m_DataLayout); const unsigned int inputChannelCount = workloadInfo.m_InputTensorInfos[0].GetShape()[dimensionIndices.GetChannelsIndex()]; const unsigned int outputChannelCount = workloadInfo.m_OutputTensorInfos[0].GetShape()[dimensionIndices.GetChannelsIndex()]; if (inputChannelCount != outputChannelCount) { throw InvalidArgumentException( boost::str(boost::format("ResizeBilinearQueueDescriptor: Input channel count (%1%) " "does not match output channel count (%2%)") % inputChannelCount % outputChannelCount)); } } } void FakeQuantizationQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "FakeQuantizationQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "FakeQuantizationQueueDescriptor", 1); ValidateTensorNumDimensions(workloadInfo.m_InputTensorInfos[0], "FakeQuantizationQueueDescriptor", 2, "input"); ValidateTensorNumDimensions(workloadInfo.m_OutputTensorInfos[0], "FakeQuantizationQueueDescriptor", 2, "output"); ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_OutputTensorInfos[0], "FakeQuantizationQueueDescriptor", "input", "output"); if (m_Parameters.m_Min > m_Parameters.m_Max) { throw InvalidArgumentException("FakeQuantizationQueueDescriptor: min cannot be greater than max"); } } void L2NormalizationQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "L2NormalizationQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "L2NormalizationQueueDescriptor", 1); ValidateTensorNumDimensions(workloadInfo.m_InputTensorInfos[0], "L2NormalizationQueueDescriptor", 4, "input"); ValidateTensorNumDimensions(workloadInfo.m_OutputTensorInfos[0], "L2NormalizationQueueDescriptor", 4, "output"); ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_OutputTensorInfos[0], "L2NormalizationQueueDescriptor", "input", "output"); } void ConstantQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "ConstantQueueDescriptor", 0); ValidateNumOutputs(workloadInfo, "ConstantQueueDescriptor", 1); if (!m_LayerOutput) { throw InvalidArgumentException("ConstantQueueDescriptor: No const input specified"); } ValidateTensorShapesMatch(m_LayerOutput->GetTensorInfo(), workloadInfo.m_OutputTensorInfos[0], "ConstantQueueDescriptor", "constant", "output"); // Check the supported data types std::vector supportedTypes = { DataType::Float32, DataType::Float16, DataType::Signed32, DataType::QuantisedAsymm8, DataType::QuantisedSymm16 }; ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], supportedTypes, "ConstantQueueDescriptor"); } void ReshapeQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "ReshapeQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "ReshapeQueueDescriptor", 1); if (workloadInfo.m_InputTensorInfos[0].GetNumElements() != workloadInfo.m_OutputTensorInfos[0].GetNumElements()) { throw InvalidArgumentException("ReshapeQueueDescriptor: Input tensor has " + to_string(workloadInfo.m_InputTensorInfos[0].GetNumElements()) + " but output tensor has " + to_string(workloadInfo.m_OutputTensorInfos[0].GetNumElements()) + " elements."); } // Check the supported data types std::vector supportedTypes = { DataType::Float32, DataType::Float16, DataType::QuantisedAsymm8, DataType::QuantisedSymm16 }; ValidateDataTypes(workloadInfo.m_InputTensorInfos[0], supportedTypes, "ReshapeQueueDescriptor"); ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], supportedTypes, "ReshapeQueueDescriptor"); } void SpaceToBatchNdQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "SpaceToBatchNdQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "SpaceToBatchNdQueueDescriptor", 1); ValidateTensorNumDimensions(workloadInfo.m_InputTensorInfos[0], "SpaceToBatchNdQueueDescriptor", 4, "input"); ValidateTensorNumDimensions(workloadInfo.m_OutputTensorInfos[0], "SpaceToBatchNdQueueDescriptor", 4, "output"); if (m_Parameters.m_BlockShape.size() != 2) { throw InvalidArgumentException("Block Shape must contain 2 spatial dimensions"); } if (m_Parameters.m_BlockShape.size() != m_Parameters.m_PadList.size()) { throw InvalidArgumentException("Pad List must contain the same number of dimensions as Block Shape."); } const TensorShape inputShape = workloadInfo.m_InputTensorInfos[0].GetShape(); std::pair heightPad = m_Parameters.m_PadList[0]; std::pair widthPad = m_Parameters.m_PadList[1]; DataLayoutIndexed dimensionIndices(m_Parameters.m_DataLayout); unsigned int inputHeight = inputShape[dimensionIndices.GetHeightIndex()] + heightPad.first + heightPad.second; unsigned int inputWidth = inputShape[dimensionIndices.GetWidthIndex()] + widthPad.first + widthPad.second; unsigned int numInputElements = inputShape[0] * inputHeight * inputWidth * inputShape[dimensionIndices.GetChannelsIndex()]; if (workloadInfo.m_OutputTensorInfos[0].GetNumElements() != numInputElements) { throw InvalidArgumentException("SpaceToBatchNdQueueDescriptor: Input tensor has " + to_string(numInputElements) + " after padding but output tensor has " + to_string(workloadInfo.m_OutputTensorInfos[0].GetNumElements()) + " elements."); } if (inputHeight % m_Parameters.m_BlockShape[0] != 0 || inputWidth % m_Parameters.m_BlockShape[1] != 0) { throw InvalidArgumentException( "Input shape after padding must be divisible by Block Shape in all spatial dimensions"); } std::vector supportedTypes = { DataType::Float16, DataType::Float32, DataType::QuantisedAsymm8, DataType::QuantisedSymm16 }; ValidateDataTypes(workloadInfo.m_InputTensorInfos[0], supportedTypes, "SpaceToBatchNdQueueDescriptor"); ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], {workloadInfo.m_InputTensorInfos[0].GetDataType()}, "SpaceToBatchNdQueueDescriptor"); } void FloorQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { const std::string floorQueueDescString = "FloorQueueDescriptor"; ValidateNumInputs(workloadInfo, floorQueueDescString, 1); ValidateNumOutputs(workloadInfo, floorQueueDescString, 1); std::vector supportedTypes = { DataType::Float32, DataType::QuantisedSymm16 }; ValidateDataTypes(workloadInfo.m_InputTensorInfos[0], supportedTypes, floorQueueDescString); ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], supportedTypes, floorQueueDescString); if (workloadInfo.m_InputTensorInfos[0] != workloadInfo.m_OutputTensorInfos[0]) { throw InvalidArgumentException(floorQueueDescString + ": Input and output tensor infos do not match."); } } void LstmQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateTensorNumDimensions(workloadInfo.m_InputTensorInfos[0], "LstmQueueDescriptor", 2, "input"); ValidateTensorNumDimensions(workloadInfo.m_OutputTensorInfos[0], "LstmQueueDescriptor", 2, "output"); std::vector supportedTypes = { DataType::Float16, DataType::Float32, DataType::QuantisedSymm16 }; ValidateDataTypes(workloadInfo.m_InputTensorInfos[0], supportedTypes, "LstmQueueDescriptor"); ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], supportedTypes, "LstmQueueDescriptor"); } void ConvertFp32ToFp16QueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "ConvertFp32ToFp16QueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "ConvertFp32ToFp16QueueDescriptor", 1); if (workloadInfo.m_InputTensorInfos[0].GetDataType() != DataType::Float32) { throw InvalidArgumentException("ConvertFp32ToFp16QueueDescriptor: Input tensor type must be Float32."); } if (workloadInfo.m_OutputTensorInfos[0].GetDataType() != DataType::Float16) { throw InvalidArgumentException("ConvertFp32ToFp16QueueDescriptor: Output tensor type must be Float16."); } ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_OutputTensorInfos[0], "ConvertFp32ToFp16QueueDescriptor", "input", "output"); } void ConvertFp16ToFp32QueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "ConvertFp16ToFp32QueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "ConvertFp16ToFp32QueueDescriptor", 1); if (workloadInfo.m_InputTensorInfos[0].GetDataType() != DataType::Float16) { throw InvalidArgumentException("ConvertFp16ToFp32QueueDescriptor: Input tensor type must be Float16."); } if (workloadInfo.m_OutputTensorInfos[0].GetDataType() != DataType::Float32) { throw InvalidArgumentException("ConvertFp16ToFp32QueueDescriptor: Output tensor type must be Float32."); } ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_OutputTensorInfos[0], "ConvertFp16ToFp32QueueDescriptor", "input", "output"); } void DivisionQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "DivisionQueueDescriptor", 2); ValidateNumOutputs(workloadInfo, "DivisionQueueDescriptor", 1); std::vector supportedTypes = { DataType::Float32, DataType::QuantisedAsymm8, DataType::QuantisedSymm16, DataType::Float16 }; ValidateDataTypes(workloadInfo.m_InputTensorInfos[0], supportedTypes, "DivisionQueueDescriptor"); ValidateDataTypes(workloadInfo.m_InputTensorInfos[1], supportedTypes, "DivisionQueueDescriptor"); ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], supportedTypes, "DivisionQueueDescriptor"); ValidateBroadcastTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_InputTensorInfos[1], workloadInfo.m_OutputTensorInfos[0], "DivisionQueueDescriptor", "first input", "second input"); } void SubtractionQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "SubtractionQueueDescriptor", 2); ValidateNumOutputs(workloadInfo, "SubtractionQueueDescriptor", 1); std::vector supportedTypes = { DataType::Float32, DataType::QuantisedAsymm8, DataType::QuantisedSymm16, DataType::Float16 }; ValidateDataTypes(workloadInfo.m_InputTensorInfos[0], supportedTypes, "SubtractionQueueDescriptor"); ValidateDataTypes(workloadInfo.m_InputTensorInfos[1], supportedTypes, "SubtractionQueueDescriptor"); ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], supportedTypes, "SubtractionQueueDescriptor"); ValidateBroadcastTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_InputTensorInfos[1], workloadInfo.m_OutputTensorInfos[0], "SubtractionQueueDescriptor", "first input", "second input"); } void MaximumQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "MaximumQueueDescriptor", 2); ValidateNumOutputs(workloadInfo, "MaximumQueueDescriptor", 1); std::vector supportedTypes = { DataType::Float32, DataType::QuantisedAsymm8, DataType::QuantisedSymm16 }; ValidateDataTypes(workloadInfo.m_InputTensorInfos[0], supportedTypes, "MaximumQueueDescriptor"); ValidateDataTypes(workloadInfo.m_InputTensorInfos[1], supportedTypes, "MaximumQueueDescriptor"); ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], supportedTypes, "MaximumQueueDescriptor"); ValidateBroadcastTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_InputTensorInfos[1], workloadInfo.m_OutputTensorInfos[0], "MaximumQueueDescriptor", "first input", "second input"); } void MeanQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "MeanQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "MeanQueueDescriptor", 1); const TensorInfo& input = workloadInfo.m_InputTensorInfos[0]; const TensorInfo& output = workloadInfo.m_OutputTensorInfos[0]; if (m_Parameters.m_KeepDims) { ValidateTensorNumDimensions(output, "MeanQueueDescriptor", input.GetNumDimensions(), "output"); } else if (m_Parameters.m_Axis.empty()) { ValidateTensorNumDimensions(output, "MeanQueueDescriptor", 1, "output"); } else { auto outputDim = input.GetNumDimensions() - boost::numeric_cast(m_Parameters.m_Axis.size()); ValidateTensorNumDimensions(output, "MeanQueueDescriptor", outputDim > 0 ? outputDim : 1, "output"); } } void PadQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "PadQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "PadQueueDescriptor", 1); const TensorInfo& input = workloadInfo.m_InputTensorInfos[0]; const TensorInfo& output = workloadInfo.m_OutputTensorInfos[0]; // input and output should have the same number of dimensions ValidateTensorNumDimensions(output, "PadQueueDescriptor", input.GetNumDimensions(), "output"); // there should be entry in the pad list for each dimension in the input tensor if (m_Parameters.m_PadList.size() != input.GetNumDimensions()) { throw InvalidArgumentException("Pad List should contain the same number of entries as there" " are dimensions in the input tensor that is " + to_string(input.GetNumDimensions()) + " entries " + " not " + to_string(m_Parameters.m_PadList.size()) + " entries."); } } void QuantizeQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "QuantizeQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "QuantizeQueueDescriptor", 1); if (workloadInfo.m_InputTensorInfos[0].GetDataType() != DataType::Float32) { throw InvalidArgumentException("Quantize only accepts Float32 inputs."); } if (workloadInfo.m_OutputTensorInfos[0].GetDataType() != DataType::QuantisedAsymm8 && workloadInfo.m_OutputTensorInfos[0].GetDataType() != DataType::QuantisedSymm16) { throw InvalidArgumentException("Output of quantized layer must be quantized type."); } } void BatchToSpaceNdQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "BatchToSpaceNdQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "BatchToSpaceNdQueueDescriptor", 1); } void StridedSliceQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "StridedSliceQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "StridedSliceQueueDescriptor", 1); const TensorInfo& input = workloadInfo.m_InputTensorInfos[0]; const TensorInfo& output = workloadInfo.m_OutputTensorInfos[0]; std::vector supportedTypes = { DataType::Float16, DataType::Float32, DataType::QuantisedAsymm8, DataType::QuantisedSymm16 }; ValidateDataTypes(input, supportedTypes, "StridedSliceQueueDescriptor"); ValidateDataTypes(output, supportedTypes, "StridedSliceQueueDescriptor"); ValidateDataTypes(output, { input.GetDataType() }, "StridedSliceQueueDescriptor"); ValidateTensorQuantizationSpace(input, output, "StridedSliceQueueDescriptor", "input", "output"); const uint32_t rank = input.GetNumDimensions(); if (rank > 4) { throw InvalidArgumentException( "StridedSliceLayer: Input tensors with rank greater than 4 are not supported"); } // Begin, End & Stride length must be of rank(input0) if (m_Parameters.m_Begin.size() != rank) { throw InvalidArgumentException("StridedSliceLayer: Begin length must be of rank input0(" + to_string(rank) + ")"); } if (m_Parameters.m_End.size() != rank) { throw InvalidArgumentException("StridedSliceLayer: End length must be of rank input0(" + to_string(rank) + ")"); } if (m_Parameters.m_Stride.size() != rank) { throw InvalidArgumentException("StridedSliceLayer: Stride length must be of rank input0(" + to_string(rank) + ")"); } // Stride entries must be non-zero for (auto& stride : m_Parameters.m_Stride) { if (stride == 0) { throw InvalidArgumentException("StridedSliceLayer: Stride entries must be non-zero"); } } } void MinimumQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "MinimumQueueDescriptor", 2); ValidateNumOutputs(workloadInfo, "MinimumQueueDescriptor", 1); std::vector supportedTypes = { DataType::Float32, DataType::QuantisedAsymm8, DataType::QuantisedSymm16 }; ValidateDataTypes(workloadInfo.m_InputTensorInfos[0], supportedTypes, "MinimumQueueDescriptor"); ValidateDataTypes(workloadInfo.m_InputTensorInfos[1], supportedTypes, "MinimumQueueDescriptor"); ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], supportedTypes, "MinimumQueueDescriptor"); ValidateBroadcastTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_InputTensorInfos[1], workloadInfo.m_OutputTensorInfos[0], "MinimumQueueDescriptor", "first input", "second input"); } void DebugQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "DebugQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "DebugQueueDescriptor", 1); } void EqualQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "EqualQueueDescriptor", 2); ValidateNumOutputs(workloadInfo, "EqualQueueDescriptor", 1); ValidateBroadcastTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_InputTensorInfos[1], workloadInfo.m_OutputTensorInfos[0], "EqualQueueDescriptor", "first input", "second input"); if (workloadInfo.m_OutputTensorInfos[0].GetDataType() != DataType::Boolean) { throw InvalidArgumentException("EqualQueueDescriptor: Output tensor type must be Boolean."); } } void GreaterQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "GreaterQueueDescriptor", 2); ValidateNumOutputs(workloadInfo, "GreaterQueueDescriptor", 1); ValidateBroadcastTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_InputTensorInfos[1], workloadInfo.m_OutputTensorInfos[0], "GreaterQueueDescriptor", "first input", "second input"); if (workloadInfo.m_OutputTensorInfos[0].GetDataType() != DataType::Boolean) { throw InvalidArgumentException("GreaterQueueDescriptor: Output tensor type must be Boolean."); } } void RsqrtQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "RsqrtQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "RsqrtQueueDescriptor", 1); ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_OutputTensorInfos[0], "RsqrtQueueDescriptor", "input", "output"); } void GatherQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "GatherQueueDescriptor", 2); ValidateNumOutputs(workloadInfo, "GatherQueueDescriptor", 1); const TensorInfo& indices = workloadInfo.m_InputTensorInfos[1]; if (indices.GetDataType() != DataType::Signed32) { throw InvalidArgumentException("GatherQueueDescriptor: Indices tensor type must be int32."); } const TensorInfo& params = workloadInfo.m_InputTensorInfos[0]; const TensorInfo& output = workloadInfo.m_OutputTensorInfos[0]; unsigned int paramsDim = params.GetNumDimensions(); unsigned int indicesDim = indices.GetNumDimensions(); unsigned int outputDim = paramsDim - 1 + indicesDim; ValidateTensorNumDimensions(output, "GatherQueueDescriptor", outputDim, "output"); } void DetectionPostProcessQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { const std::string& descriptorName = " DetectionPostProcessQueueDescriptor"; ValidateNumInputs(workloadInfo, descriptorName, 2); if (workloadInfo.m_OutputTensorInfos.size() != 4) { throw InvalidArgumentException(descriptorName + ": Requires exactly four outputs. " + to_string(workloadInfo.m_OutputTensorInfos.size()) + " has been provided."); } if (m_Anchors == nullptr) { throw InvalidArgumentException(descriptorName + ": Anchors tensor descriptor is missing."); } const TensorInfo& boxEncodingsInfo = workloadInfo.m_InputTensorInfos[0]; const TensorInfo& scoresInfo = workloadInfo.m_InputTensorInfos[1]; const TensorInfo& anchorsInfo = m_Anchors->GetTensorInfo(); const TensorInfo& detectionBoxesInfo = workloadInfo.m_OutputTensorInfos[0]; const TensorInfo& detectionClassesInfo = workloadInfo.m_OutputTensorInfos[1]; const TensorInfo& detectionScoresInfo = workloadInfo.m_OutputTensorInfos[2]; const TensorInfo& numDetectionsInfo = workloadInfo.m_OutputTensorInfos[3]; ValidateTensorNumDimensions(boxEncodingsInfo, descriptorName, 3, "box encodings"); ValidateTensorNumDimensions(scoresInfo, descriptorName, 3, "scores"); ValidateTensorNumDimensions(anchorsInfo, descriptorName, 2, "anchors"); const std::vector supportedInputTypes = { DataType::Float32, DataType::QuantisedAsymm8, DataType::QuantisedSymm16 }; ValidateDataTypes(boxEncodingsInfo, supportedInputTypes, descriptorName); ValidateDataTypes(scoresInfo, supportedInputTypes, descriptorName); ValidateDataTypes(anchorsInfo, supportedInputTypes, descriptorName); ValidateTensorNumDimensions(detectionBoxesInfo, descriptorName, 3, "detection boxes"); ValidateTensorNumDimensions(detectionScoresInfo, descriptorName, 2, "detection scores"); ValidateTensorNumDimensions(detectionClassesInfo, descriptorName, 2, "detection classes"); ValidateTensorNumDimensions(numDetectionsInfo, descriptorName, 1, "num detections"); // NOTE: Output is always Float32 regardless of input type ValidateTensorDataType(detectionBoxesInfo, DataType::Float32, descriptorName, "detection boxes"); ValidateTensorDataType(detectionScoresInfo, DataType::Float32, descriptorName, "detection scores"); ValidateTensorDataType(detectionClassesInfo, DataType::Float32, descriptorName, "detection classes"); ValidateTensorDataType(numDetectionsInfo, DataType::Float32, descriptorName, "num detections"); if (m_Parameters.m_NmsIouThreshold <= 0.0f || m_Parameters.m_NmsIouThreshold > 1.0f) { throw InvalidArgumentException(descriptorName + ": Intersection over union threshold " "must be positive and less than or equal to 1."); } if (scoresInfo.GetShape()[2] != m_Parameters.m_NumClasses + 1) { throw InvalidArgumentException(descriptorName + ": Number of classes with background " "should be equal to number of classes + 1."); } } void DequantizeQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "DequantizeQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "DequantizeQueueDescriptor", 1); if (workloadInfo.m_InputTensorInfos[0].GetDataType() != DataType::QuantisedAsymm8 && workloadInfo.m_InputTensorInfos[0].GetDataType() != DataType::QuantisedSymm16) { throw InvalidArgumentException("Input to dequantize layer must be quantized type."); } if (workloadInfo.m_OutputTensorInfos[0].GetDataType() != DataType::Float32) { throw InvalidArgumentException("Output of dequantize layer must be Float32 type."); } } void MergeQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "MergeQueueDescriptor", 2); ValidateNumOutputs(workloadInfo, "MergeQueueDescriptor", 1); ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_InputTensorInfos[1], "MergeQueueDescriptor", "input0", "input1"); ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_OutputTensorInfos[0], "MergeQueueDescriptor", "input0", "output"); const DataType dataType = workloadInfo.m_InputTensorInfos[0].GetDataType(); ValidateTensorDataType(workloadInfo.m_InputTensorInfos[1], dataType, "MergeQueueDescriptor", "input1"); ValidateTensorDataType(workloadInfo.m_OutputTensorInfos[0], dataType, "MergeQueueDescriptor", "output"); } void SwitchQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { ValidateNumInputs(workloadInfo, "SwitchQueueDescriptor", 2); ValidateNumOutputs(workloadInfo, "SwitchQueueDescriptor", 2); std::vector supportedTypes = { DataType::Float32, DataType::QuantisedAsymm8, DataType::QuantisedSymm16 }; ValidateDataTypes(workloadInfo.m_InputTensorInfos[0], supportedTypes, "SwitchQueueDescriptor"); ValidateDataTypes(workloadInfo.m_InputTensorInfos[1], supportedTypes, "SwitchQueueDescriptor"); ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], supportedTypes, "SwitchQueueDescriptor"); ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_OutputTensorInfos[0], "SwitchQueueDescriptor", "input0", "output0"); ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_OutputTensorInfos[1], "SwitchQueueDescriptor", "input0", "output1"); } void PreCompiledQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { // This is internally generated so it should not need validation. } } //namespace armnn