From f0a6dec75832604d5ab18242dc216852821a8279 Mon Sep 17 00:00:00 2001 From: Sadik Armagan Date: Thu, 25 Mar 2021 07:46:55 +0000 Subject: IVGCVSW-5736 and IVGCVSW-5743 'NonConstWeights: Update front-end and TfLiteDelegate support for FullyConnected Operator' * Added front-end support for non-const weights for FULLY_CONNECTED operator * Added FULLY_CONNECTED end-to-end test * Updated FULLY_CONNECTED operator support in TfLite Arm NN Delegate for non-const weights * Updated the version numbers Signed-off-by: Sadik Armagan Change-Id: Iffa5b9aa9297aca4c02d923cce4636c88ac21faa --- delegate/include/Version.hpp | 2 +- delegate/src/FullyConnected.hpp | 109 +++--- delegate/src/test/FullyConnectedTest.cpp | 33 +- delegate/src/test/FullyConnectedTestHelper.hpp | 101 +++-- include/armnn/BackendHelper.hpp | 10 +- include/armnn/Descriptors.hpp | 10 +- include/armnn/INetwork.hpp | 11 + include/armnn/Types.hpp | 10 + include/armnn/Version.hpp | 2 +- include/armnn/backends/IBackendInternal.hpp | 3 + include/armnnCaffeParser/Version.hpp | 2 +- include/armnnOnnxParser/Version.hpp | 2 +- include/armnnTfLiteParser/Version.hpp | 2 +- include/armnnTfParser/Version.hpp | 2 +- python/pyarmnn/README.md | 14 +- .../examples/image_classification/README.md | 2 +- python/pyarmnn/examples/object_detection/README.md | 2 +- .../pyarmnn/examples/speech_recognition/README.md | 2 +- python/pyarmnn/src/pyarmnn/_version.py | 4 +- python/pyarmnn/test/test_setup.py | 8 +- python/pyarmnn/test/test_version.py | 4 +- samples/ObjectDetection/Readme.md | 6 +- src/armnn/BackendHelper.cpp | 26 +- src/armnn/Descriptors.cpp | 17 + src/armnn/Network.cpp | 69 +++- src/armnn/Network.hpp | 7 +- src/armnn/layers/FullyConnectedLayer.cpp | 82 +++-- src/armnn/test/OptimizerTests.cpp | 8 + src/armnn/test/UtilsTests.cpp | 12 + src/armnnDeserializer/Deserializer.cpp | 37 +- src/armnnSerializer/ArmnnSchema.fbs | 1 + src/armnnSerializer/ArmnnSchema_generated.h | 14 +- src/armnnSerializer/Serializer.cpp | 24 +- src/armnnSerializer/test/SerializerTests.cpp | 48 +++ src/backends/backendsCommon/WorkloadData.cpp | 38 +- src/backends/backendsCommon/WorkloadFactory.cpp | 408 +++++++++++---------- src/backends/backendsCommon/test/CMakeLists.txt | 1 + .../backendsCommon/test/CompatibilityTests.cpp | 16 + .../test/FullyConnectedEndToEndTestImpl.hpp | 97 +++++ .../test/layerTests/FullyConnectedTestImpl.cpp | 105 +++++- .../test/layerTests/FullyConnectedTestImpl.hpp | 3 +- src/backends/cl/test/ClLayerTests.cpp | 4 +- src/backends/neon/test/NeonLayerTests.cpp | 4 +- src/backends/reference/RefBackend.cpp | 10 + src/backends/reference/RefBackend.hpp | 6 + src/backends/reference/test/RefEndToEndTests.cpp | 6 + src/backends/reference/test/RefLayerTests.cpp | 14 +- .../workloads/RefFullyConnectedWorkload.cpp | 45 ++- 48 files changed, 994 insertions(+), 449 deletions(-) create mode 100644 src/backends/backendsCommon/test/FullyConnectedEndToEndTestImpl.hpp diff --git a/delegate/include/Version.hpp b/delegate/include/Version.hpp index 8b831f92cf..99ee2ad60f 100644 --- a/delegate/include/Version.hpp +++ b/delegate/include/Version.hpp @@ -14,7 +14,7 @@ namespace armnnDelegate // ArmNN Delegate version components #define DELEGATE_MAJOR_VERSION 24 -#define DELEGATE_MINOR_VERSION 0 +#define DELEGATE_MINOR_VERSION 1 #define DELEGATE_PATCH_VERSION 0 /// DELEGATE_VERSION: "X.Y.Z" diff --git a/delegate/src/FullyConnected.hpp b/delegate/src/FullyConnected.hpp index 0a82286479..2b45c48a89 100644 --- a/delegate/src/FullyConnected.hpp +++ b/delegate/src/FullyConnected.hpp @@ -35,62 +35,27 @@ TfLiteStatus VisitFullyConnectedOperator(DelegateData& delegateData, const TfLiteTensor* tfLiteTensors = tfLiteContext->tensors; const TfLiteTensor& tfLiteInputTensor = tfLiteTensors[tfLiteNode->inputs->data[0]]; - if(!IsValid(&tfLiteTensors[tfLiteNode->inputs->data[0]])) + if (!IsValid(tfLiteContext, tfLiteInputTensor, operatorCode, nodeIndex)) { - TF_LITE_MAYBE_KERNEL_LOG( - tfLiteContext, - "TfLiteArmnnDelegate: Invalid input tensor in operator #%d node #%d: ", - operatorCode, nodeIndex); - return kTfLiteError; - } - if (IsDynamicTensor(tfLiteInputTensor)) - { - TF_LITE_MAYBE_KERNEL_LOG( - tfLiteContext, - "TfLiteArmnnDelegate: Dynamic input tensors are not supported in node #%d: ", - nodeIndex); return kTfLiteError; } + const TfLiteTensor& tfLiteOutputTensor = tfLiteTensors[tfLiteNode->outputs->data[0]]; - if(!IsValid(&tfLiteOutputTensor)) - { - TF_LITE_MAYBE_KERNEL_LOG( - tfLiteContext, - "TfLiteArmnnDelegate: Invalid output tensor in operator #%d node #%d: ", - operatorCode, nodeIndex); - return kTfLiteError; - } - if (IsDynamicTensor(tfLiteOutputTensor)) + if (!IsValid(tfLiteContext, tfLiteOutputTensor, operatorCode, nodeIndex)) { - TF_LITE_MAYBE_KERNEL_LOG( - tfLiteContext, - "TfLiteArmnnDelegate: Dynamic output tensors are not supported in node #%d: ", - nodeIndex); return kTfLiteError; } const TfLiteTensor& tfLiteWeightsTensor = tfLiteTensors[tfLiteNode->inputs->data[1]]; - if(!IsValid(&tfLiteWeightsTensor)) - { - TF_LITE_MAYBE_KERNEL_LOG( - tfLiteContext, - "TfLiteArmnnDelegate: Invalid weights tensor in operator #%d node #%d: ", - operatorCode, nodeIndex); - return kTfLiteError; - } - if (IsDynamicTensor(tfLiteWeightsTensor)) + if (!IsValid(tfLiteContext, tfLiteWeightsTensor, operatorCode, nodeIndex)) { - TF_LITE_MAYBE_KERNEL_LOG( - tfLiteContext, - "TfLiteArmnnDelegate: Dynamic weight tensors are not supported in node #%d: ", - nodeIndex); return kTfLiteError; } const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteInputTensor); + armnn::TensorInfo weightsTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteWeightsTensor); const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteOutputTensor); - armnn::TensorInfo weightsTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteWeightsTensor); // Fully Connected Layer accepts two dimensional weights input int32_t weightsDimension = static_cast(weightsTensorInfo.GetNumDimensions()); if (weightsDimension != 2) @@ -102,24 +67,23 @@ TfLiteStatus VisitFullyConnectedOperator(DelegateData& delegateData, return kTfLiteError; } + bool isConstantWeights = tflite::IsConstantTensor(&tfLiteWeightsTensor); + armnn::TensorInfo biasTensorInfo; if (biasEnabled) { const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]]; - if(!IsValid(&tfLiteBiasTensor)) + if (!IsValid(tfLiteContext, tfLiteBiasTensor, operatorCode, nodeIndex)) { - TF_LITE_MAYBE_KERNEL_LOG( - tfLiteContext, - "TfLiteArmnnDelegate: Invalid bias tensor in operator #%d node #%d: ", - operatorCode, nodeIndex); return kTfLiteError; } - if (IsDynamicTensor(tfLiteBiasTensor)) + if ((isConstantWeights && !tflite::IsConstantTensor(&tfLiteBiasTensor)) + || (!isConstantWeights && tflite::IsConstantTensor(&tfLiteBiasTensor))) { TF_LITE_MAYBE_KERNEL_LOG( tfLiteContext, - "TfLiteArmnnDelegate: Dynamic bias tensors are not supported in node #%d: ", - nodeIndex); + "TfLiteArmnnDelegate: Weights and bias are not compatible" + " in operator #%d node #%d: ", operatorCode, nodeIndex); return kTfLiteError; } biasTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteBiasTensor); @@ -130,7 +94,6 @@ TfLiteStatus VisitFullyConnectedOperator(DelegateData& delegateData, } armnn::TensorInfo reshapedTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteInputTensor); - if (inputTensorInfo.GetNumDimensions() > 2) { // Calculate reshape to flatten to 2D [batch_size, input_size] @@ -153,6 +116,7 @@ TfLiteStatus VisitFullyConnectedOperator(DelegateData& delegateData, armnn::FullyConnectedDescriptor descriptor; descriptor.m_TransposeWeightMatrix = true; descriptor.m_BiasEnabled = biasEnabled; + descriptor.m_ConstantWeights = isConstantWeights; bool isSupported = false; auto validateFunc = [&](const armnn::TensorInfo& outputTensorInfo, bool& isSupported) @@ -175,27 +139,28 @@ TfLiteStatus VisitFullyConnectedOperator(DelegateData& delegateData, return isSupported ? kTfLiteOk : kTfLiteError; } - auto weightsTensor = CreateConstTensor(&tfLiteWeightsTensor, - weightsTensorInfo, - armnn::Optional()); - - armnn::IConnectableLayer* layer = nullptr; - if (biasEnabled) + armnn::Optional optionalWeights = armnn::EmptyOptional(); + armnn::Optional optionalBiases = armnn::EmptyOptional(); + if(descriptor.m_ConstantWeights) { - const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]]; - auto biasTensor = CreateConstTensor(&tfLiteBiasTensor, - biasTensorInfo, - armnn::Optional()); - layer = delegateData.m_Network->AddFullyConnectedLayer(descriptor, - weightsTensor, - armnn::Optional(biasTensor)); - } - else - { - layer = delegateData.m_Network->AddFullyConnectedLayer(descriptor, - weightsTensor, - armnn::EmptyOptional()); + auto weightsTensor = CreateConstTensor(&tfLiteWeightsTensor, + weightsTensorInfo, + armnn::Optional()); + optionalWeights = armnn::Optional(weightsTensor); + + if (biasEnabled) + { + const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]]; + auto biasTensor = CreateConstTensor(&tfLiteBiasTensor, + biasTensorInfo, + armnn::Optional()); + optionalBiases = armnn::Optional(biasTensor); + } } + + armnn::IConnectableLayer* layer = delegateData.m_Network->AddFullyConnectedLayer(descriptor, + optionalWeights, + optionalBiases); ARMNN_ASSERT(layer != nullptr); armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0); @@ -215,6 +180,14 @@ TfLiteStatus VisitFullyConnectedOperator(DelegateData& delegateData, // Connect delegateData.m_OutputSlotForNode[tfLiteNode->inputs->data[0]]->Connect(reshapeLayer->GetInputSlot(0)); reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0)); + if (!descriptor.m_ConstantWeights) + { + delegateData.m_OutputSlotForNode[tfLiteNode->inputs->data[1]]->Connect(layer->GetInputSlot(1)); + if (biasEnabled) + { + delegateData.m_OutputSlotForNode[tfLiteNode->inputs->data[2]]->Connect(layer->GetInputSlot(2)); + } + } delegateData.m_OutputSlotForNode[tfLiteNode->outputs->data[0]] = &outputSlot; } diff --git a/delegate/src/test/FullyConnectedTest.cpp b/delegate/src/test/FullyConnectedTest.cpp index 018f7f5190..3bea250988 100644 --- a/delegate/src/test/FullyConnectedTest.cpp +++ b/delegate/src/test/FullyConnectedTest.cpp @@ -8,7 +8,7 @@ namespace { -void FullyConnectedFp32Test(std::vector& backends) +void FullyConnectedFp32Test(std::vector& backends, bool constantWeights = true) { std::vector inputTensorShape { 1, 4, 1, 1 }; std::vector weightsTensorShape { 1, 4 }; @@ -30,10 +30,11 @@ void FullyConnectedFp32Test(std::vector& backends) outputTensorShape, inputValues, expectedOutputValues, - weightsData); + weightsData, + constantWeights); } -void FullyConnectedActicationTest(std::vector& backends) +void FullyConnectedActicationTest(std::vector& backends, bool constantWeights = true) { std::vector inputTensorShape { 1, 4, 1, 1 }; std::vector weightsTensorShape { 1, 4 }; @@ -55,10 +56,11 @@ void FullyConnectedActicationTest(std::vector& backends) outputTensorShape, inputValues, expectedOutputValues, - weightsData); + weightsData, + constantWeights); } -void FullyConnectedInt8Test(std::vector& backends) +void FullyConnectedInt8Test(std::vector& backends, bool constantWeights = true) { std::vector inputTensorShape { 1, 4, 2, 1 }; std::vector weightsTensorShape { 1, 4 }; @@ -82,7 +84,8 @@ void FullyConnectedInt8Test(std::vector& backends) outputTensorShape, inputValues, expectedOutputValues, - weightsData); + weightsData, + constantWeights); } TEST_SUITE("FullyConnected_GpuAccTests") @@ -152,6 +155,24 @@ TEST_CASE ("FullyConnected_Activation_CpuRef_Test") FullyConnectedActicationTest(backends); } +TEST_CASE ("FullyConnected_Weights_As_Inputs_FP32_CpuRef_Test") +{ + std::vector backends = { armnn::Compute::CpuRef }; + FullyConnectedFp32Test(backends, false); +} + +TEST_CASE ("FullyConnected_Weights_As_Inputs_Int8_CpuRef_Test") +{ + std::vector backends = { armnn::Compute::CpuRef }; + FullyConnectedInt8Test(backends, false); +} + +TEST_CASE ("FullyConnected_Weights_As_Inputs_Activation_CpuRef_Test") +{ + std::vector backends = { armnn::Compute::CpuRef }; + FullyConnectedActicationTest(backends, false); +} + } // End of TEST_SUITE("FullyConnected_CpuRefTests") } // anonymous namespace \ No newline at end of file diff --git a/delegate/src/test/FullyConnectedTestHelper.hpp b/delegate/src/test/FullyConnectedTestHelper.hpp index 1b6ca941b8..37062c3400 100644 --- a/delegate/src/test/FullyConnectedTestHelper.hpp +++ b/delegate/src/test/FullyConnectedTestHelper.hpp @@ -5,6 +5,8 @@ #pragma once +#include "TestUtils.hpp" + #include #include @@ -25,8 +27,9 @@ std::vector CreateFullyConnectedTfLiteModel(tflite::TensorType tensorType, const std::vector & inputTensorShape, const std::vector & weightsTensorShape, const std::vector & biasTensorShape, - const std::vector & outputTensorShape, - const std::vector & weightsData, + std::vector & outputTensorShape, + std::vector & weightsData, + bool constantWeights = true, float quantScale = 1.0f, int quantOffset = 0, float outputQuantScale = 2.0f, @@ -36,26 +39,38 @@ std::vector CreateFullyConnectedTfLiteModel(tflite::TensorType tensorType, flatbuffers::FlatBufferBuilder flatBufferBuilder; std::array, 3> buffers; buffers[0] = CreateBuffer(flatBufferBuilder, flatBufferBuilder.CreateVector({})); - buffers[1] = CreateBuffer(flatBufferBuilder, - flatBufferBuilder.CreateVector(reinterpret_cast(weightsData.data()), - sizeof(T) * weightsData.size())); auto biasTensorType = ::tflite::TensorType_FLOAT32; if (tensorType == ::tflite::TensorType_INT8) { biasTensorType = ::tflite::TensorType_INT32; - std::vector biasData = { 10 }; - buffers[2] = CreateBuffer(flatBufferBuilder, - flatBufferBuilder.CreateVector(reinterpret_cast(biasData.data()), - sizeof(int32_t) * biasData.size())); + } + if (constantWeights) + { + buffers[1] = CreateBuffer(flatBufferBuilder, + flatBufferBuilder.CreateVector(reinterpret_cast(weightsData.data()), + sizeof(T) * weightsData.size())); + if (tensorType == ::tflite::TensorType_INT8) + { + std::vector biasData = { 10 }; + buffers[2] = CreateBuffer(flatBufferBuilder, + flatBufferBuilder.CreateVector(reinterpret_cast(biasData.data()), + sizeof(int32_t) * biasData.size())); + + } + else + { + std::vector biasData = { 10 }; + buffers[2] = CreateBuffer(flatBufferBuilder, + flatBufferBuilder.CreateVector(reinterpret_cast(biasData.data()), + sizeof(float) * biasData.size())); + } } else { - std::vector biasData = { 10 }; - buffers[2] = CreateBuffer(flatBufferBuilder, - flatBufferBuilder.CreateVector(reinterpret_cast(biasData.data()), - sizeof(float) * biasData.size())); + buffers[1] = CreateBuffer(flatBufferBuilder, flatBufferBuilder.CreateVector({})); + buffers[2] = CreateBuffer(flatBufferBuilder, flatBufferBuilder.CreateVector({})); } auto quantizationParameters = @@ -155,10 +170,11 @@ void FullyConnectedTest(std::vector& backends, const std::vector & inputTensorShape, const std::vector & weightsTensorShape, const std::vector & biasTensorShape, - const std::vector & outputTensorShape, - const std::vector & inputValues, - const std::vector & expectedOutputValues, - const std::vector & weightsData, + std::vector & outputTensorShape, + std::vector & inputValues, + std::vector & expectedOutputValues, + std::vector & weightsData, + bool constantWeights = true, float quantScale = 1.0f, int quantOffset = 0) { @@ -171,10 +187,11 @@ void FullyConnectedTest(std::vector& backends, biasTensorShape, outputTensorShape, weightsData, + constantWeights, quantScale, quantOffset); - const Model* tfLiteModel = GetModel(modelBuffer.data()); + // Create TfLite Interpreters std::unique_ptr armnnDelegateInterpreter; CHECK(InterpreterBuilder(tfLiteModel, ::tflite::ops::builtin::BuiltinOpResolver()) @@ -191,25 +208,34 @@ void FullyConnectedTest(std::vector& backends, // Create the ArmNN Delegate armnnDelegate::DelegateOptions delegateOptions(backends); std::unique_ptr - theArmnnDelegate(armnnDelegate::TfLiteArmnnDelegateCreate(delegateOptions), - armnnDelegate::TfLiteArmnnDelegateDelete); + theArmnnDelegate(armnnDelegate::TfLiteArmnnDelegateCreate(delegateOptions), + armnnDelegate::TfLiteArmnnDelegateDelete); CHECK(theArmnnDelegate != nullptr); + // Modify armnnDelegateInterpreter to use armnnDelegate CHECK(armnnDelegateInterpreter->ModifyGraphWithDelegate(theArmnnDelegate.get()) == kTfLiteOk); // Set input data - auto tfLiteDelegateInputId = tfLiteInterpreter->inputs()[0]; - auto tfLiteDelageInputData = tfLiteInterpreter->typed_tensor(tfLiteDelegateInputId); - for (unsigned int i = 0; i < inputValues.size(); ++i) - { - tfLiteDelageInputData[i] = inputValues[i]; - } + armnnDelegate::FillInput(tfLiteInterpreter, 0, inputValues); + armnnDelegate::FillInput(armnnDelegateInterpreter, 0, inputValues); - auto armnnDelegateInputId = armnnDelegateInterpreter->inputs()[0]; - auto armnnDelegateInputData = armnnDelegateInterpreter->typed_tensor(armnnDelegateInputId); - for (unsigned int i = 0; i < inputValues.size(); ++i) + if (!constantWeights) { - armnnDelegateInputData[i] = inputValues[i]; + armnnDelegate::FillInput(tfLiteInterpreter, 1, weightsData); + armnnDelegate::FillInput(armnnDelegateInterpreter, 1, weightsData); + + if (tensorType == ::tflite::TensorType_INT8) + { + std::vector biasData = {10}; + armnnDelegate::FillInput(tfLiteInterpreter, 2, biasData); + armnnDelegate::FillInput(armnnDelegateInterpreter, 2, biasData); + } + else + { + std::vector biasData = {10}; + armnnDelegate::FillInput(tfLiteInterpreter, 2, biasData); + armnnDelegate::FillInput(armnnDelegateInterpreter, 2, biasData); + } } // Run EnqueWorkload @@ -217,16 +243,11 @@ void FullyConnectedTest(std::vector& backends, CHECK(armnnDelegateInterpreter->Invoke() == kTfLiteOk); // Compare output data - auto tfLiteDelegateOutputId = tfLiteInterpreter->outputs()[0]; - auto tfLiteDelageOutputData = tfLiteInterpreter->typed_tensor(tfLiteDelegateOutputId); - auto armnnDelegateOutputId = armnnDelegateInterpreter->outputs()[0]; - auto armnnDelegateOutputData = armnnDelegateInterpreter->typed_tensor(armnnDelegateOutputId); - for (size_t i = 0; i < expectedOutputValues.size(); i++) - { - CHECK(expectedOutputValues[i] == tfLiteDelageOutputData[i]); - CHECK(expectedOutputValues[i] == armnnDelegateOutputData[i]); - CHECK(tfLiteDelageOutputData[i] == armnnDelegateOutputData[i]); - } + armnnDelegate::CompareOutputData(tfLiteInterpreter, + armnnDelegateInterpreter, + outputTensorShape, + expectedOutputValues); + armnnDelegateInterpreter.reset(nullptr); } } // anonymous namespace \ No newline at end of file diff --git a/include/armnn/BackendHelper.hpp b/include/armnn/BackendHelper.hpp index a562f60c23..41bb5f9c3a 100644 --- a/include/armnn/BackendHelper.hpp +++ b/include/armnn/BackendHelper.hpp @@ -7,6 +7,7 @@ #include #include +#include namespace armnn { @@ -19,7 +20,10 @@ class LayerSupportHandle { public: explicit LayerSupportHandle(std::shared_ptr layerSupport) - : m_LayerSupport(std::move(layerSupport)) {}; + : m_LayerSupport(std::move(layerSupport)), m_BackendId(Compute::Undefined) {}; + + explicit LayerSupportHandle(std::shared_ptr layerSupport, const BackendId& backendId) + : m_LayerSupport(std::move(layerSupport)), m_BackendId(backendId) {}; bool IsBackendRegistered() const; @@ -422,9 +426,13 @@ public: private: std::shared_ptr m_LayerSupport; + const BackendId m_BackendId; }; /// Convenience function to retrieve the ILayerSupportHandle for a backend LayerSupportHandle GetILayerSupportByBackendId(const armnn::BackendId& backend); +/// Convenience function to check a capability on a backend +bool IsCapabilitySupported(const armnn::BackendId& backend, armnn::BackendCapability capability); + } diff --git a/include/armnn/Descriptors.hpp b/include/armnn/Descriptors.hpp index 20511ab00f..278c61f7d4 100644 --- a/include/armnn/Descriptors.hpp +++ b/include/armnn/Descriptors.hpp @@ -391,17 +391,25 @@ struct FullyConnectedDescriptor : BaseDescriptor FullyConnectedDescriptor() : m_BiasEnabled(false) , m_TransposeWeightMatrix(false) + , m_ConstantWeights(true) {} bool operator ==(const FullyConnectedDescriptor& rhs) const { - return m_BiasEnabled == rhs.m_BiasEnabled && m_TransposeWeightMatrix == rhs.m_TransposeWeightMatrix; + return m_BiasEnabled == rhs.m_BiasEnabled + && m_TransposeWeightMatrix == rhs.m_TransposeWeightMatrix + && m_ConstantWeights == rhs.m_ConstantWeights; } + /// Get the number of views/inputs. + uint32_t GetNumViews() const; + /// Enable/disable bias. bool m_BiasEnabled; /// Enable/disable transpose weight matrix. bool m_TransposeWeightMatrix; + /// Enable/disable constant weights and biases. + bool m_ConstantWeights; }; /// A Convolution2dDescriptor for the Convolution2dLayer. diff --git a/include/armnn/INetwork.hpp b/include/armnn/INetwork.hpp index d1d4744a42..bceb07405a 100644 --- a/include/armnn/INetwork.hpp +++ b/include/armnn/INetwork.hpp @@ -297,6 +297,17 @@ public: IConnectableLayer* AddFillLayer(const FillDescriptor& fillDescriptor, const char* name = nullptr); + /// Adds a fully connected layer to the network. + /// @param fullyConnectedDescriptor - Description of the fully connected layer. + /// @param weights -Optional Tensor for the weights data. + /// @param biases - Optional tensor for the bias data. + /// @param name - Optional name for the layer. + /// @return - Interface for configuring the layer. + IConnectableLayer* AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, + const Optional& weights, + const Optional& biases, + const char* name = nullptr); + /// Adds a fully connected layer to the network. /// @param fullyConnectedDescriptor - Description of the fully connected layer. /// @param weights - Tensor for the weights data. diff --git a/include/armnn/Types.hpp b/include/armnn/Types.hpp index e1ff46b023..576e67ea18 100644 --- a/include/armnn/Types.hpp +++ b/include/armnn/Types.hpp @@ -196,6 +196,16 @@ public: using IBackendSharedPtr = std::shared_ptr; using IBackendUniquePtr = std::unique_ptr; +/// BackendCapability class +enum class BackendCapability : uint32_t +{ + /// Constant weights can be accessed through the descriptors, + /// On the other hand, non-const weights can be accessed through inputs. + NonConstWeights, + + // add new enum values here +}; + /// Device specific knowledge to be passed to the optimizer. class IDeviceSpec { diff --git a/include/armnn/Version.hpp b/include/armnn/Version.hpp index d8c14ab262..2139637b5b 100644 --- a/include/armnn/Version.hpp +++ b/include/armnn/Version.hpp @@ -10,7 +10,7 @@ #define STRINGIFY_MACRO(s) #s // ArmNN version components -#define ARMNN_MAJOR_VERSION 24 +#define ARMNN_MAJOR_VERSION 25 #define ARMNN_MINOR_VERSION 0 #define ARMNN_PATCH_VERSION 0 diff --git a/include/armnn/backends/IBackendInternal.hpp b/include/armnn/backends/IBackendInternal.hpp index c7ed8efa78..8035cff456 100644 --- a/include/armnn/backends/IBackendInternal.hpp +++ b/include/armnn/backends/IBackendInternal.hpp @@ -164,6 +164,9 @@ public: /// Returns the version of the Backend API static constexpr BackendVersion GetApiVersion() { return BackendVersion(1, 0); } + + /// Returns true if backend support the capability false otherwise + virtual bool HasCapability(BackendCapability /*capabilityClass*/) const { return false; } }; using IBackendInternalUniquePtr = std::unique_ptr; diff --git a/include/armnnCaffeParser/Version.hpp b/include/armnnCaffeParser/Version.hpp index d7135bf158..6e7ce5a539 100644 --- a/include/armnnCaffeParser/Version.hpp +++ b/include/armnnCaffeParser/Version.hpp @@ -14,7 +14,7 @@ namespace armnnCaffeParser // CaffeParser version components #define CAFFE_PARSER_MAJOR_VERSION 24 -#define CAFFE_PARSER_MINOR_VERSION 0 +#define CAFFE_PARSER_MINOR_VERSION 1 #define CAFFE_PARSER_PATCH_VERSION 0 /// CAFFE_PARSER_VERSION: "X.Y.Z" diff --git a/include/armnnOnnxParser/Version.hpp b/include/armnnOnnxParser/Version.hpp index e42adf711d..d6308b376a 100644 --- a/include/armnnOnnxParser/Version.hpp +++ b/include/armnnOnnxParser/Version.hpp @@ -14,7 +14,7 @@ namespace armnnOnnxParser // OnnxParser version components #define ONNX_PARSER_MAJOR_VERSION 24 -#define ONNX_PARSER_MINOR_VERSION 0 +#define ONNX_PARSER_MINOR_VERSION 1 #define ONNX_PARSER_PATCH_VERSION 0 /// ONNX_PARSER_VERSION: "X.Y.Z" diff --git a/include/armnnTfLiteParser/Version.hpp b/include/armnnTfLiteParser/Version.hpp index 7d239bba38..99237f325d 100644 --- a/include/armnnTfLiteParser/Version.hpp +++ b/include/armnnTfLiteParser/Version.hpp @@ -14,7 +14,7 @@ namespace armnnTfLiteParser // TfLiteParser version components #define TFLITE_PARSER_MAJOR_VERSION 24 -#define TFLITE_PARSER_MINOR_VERSION 0 +#define TFLITE_PARSER_MINOR_VERSION 1 #define TFLITE_PARSER_PATCH_VERSION 0 /// TFLITE_PARSER_VERSION: "X.Y.Z" diff --git a/include/armnnTfParser/Version.hpp b/include/armnnTfParser/Version.hpp index 6f6aac9b38..25449f3180 100644 --- a/include/armnnTfParser/Version.hpp +++ b/include/armnnTfParser/Version.hpp @@ -14,7 +14,7 @@ namespace armnnTfParser // tfParser version components #define TF_PARSER_MAJOR_VERSION 24 -#define TF_PARSER_MINOR_VERSION 0 +#define TF_PARSER_MINOR_VERSION 1 #define TF_PARSER_PATCH_VERSION 0 /// TF_PARSER_VERSION: "X.Y.Z" diff --git a/python/pyarmnn/README.md b/python/pyarmnn/README.md index 0c4a313d09..00c0dfa91b 100644 --- a/python/pyarmnn/README.md +++ b/python/pyarmnn/README.md @@ -91,14 +91,14 @@ This step will put all generated files under `./src/pyarmnn/_generated` folder a ```bash $ python setup.py sdist ``` -As the result you will get `./dist/pyarmnn-24.0.0.tar.gz` file. As you can see it is platform independent. +As the result you will get `./dist/pyarmnn-25.0.0.tar.gz` file. As you can see it is platform independent. ##### 5. Build the binary package ```bash $ python setup.py bdist_wheel ``` -As the result you will get something like `./dist/pyarmnn-24.0.0-cp36-cp36m-linux_x86_64.whl` file. As you can see it +As the result you will get something like `./dist/pyarmnn-25.0.0-cp36-cp36m-linux_x86_64.whl` file. As you can see it is platform dependent. # PyArmNN installation @@ -107,8 +107,8 @@ PyArmNN can be distributed as a source package or a binary package (wheel). Binary package is platform dependent, the name of the package will indicate the platform it was built for, e.g.: -* Linux x86 64bit machine: pyarmnn-24.0.0-cp36-cp36m-*linux_x86_64*.whl -* Linux Aarch 64 bit machine: pyarmnn-24.0.0-cp36-cp36m-*linux_aarch64*.whl +* Linux x86 64bit machine: pyarmnn-25.0.0-cp36-cp36m-*linux_x86_64*.whl +* Linux Aarch 64 bit machine: pyarmnn-25.0.0-cp36-cp36m-*linux_aarch64*.whl The source package is platform independent but installation involves compilation of Arm NN python extension. You will need to have g++ compatible with C++ 14 standard and a python development library installed on the build machine. @@ -126,7 +126,7 @@ $ gcc --print-search-dirs ``` Install PyArmNN from binary by pointing to the wheel file: ```bash -$ pip install /path/to/pyarmnn-24.0.0-cp36-cp36m-linux_aarch64.whl +$ pip install /path/to/pyarmnn-25.0.0-cp36-cp36m-linux_aarch64.whl ``` ## Installing from source package @@ -143,7 +143,7 @@ $ export ARMNN_INCLUDE=/path/to/headers Install PyArmNN as follows: ```bash -$ pip install /path/to/pyarmnn-24.0.0.tar.gz +$ pip install /path/to/pyarmnn-25.0.0.tar.gz ``` If PyArmNN installation script fails to find Arm NN libraries it will raise an error like this @@ -157,7 +157,7 @@ $ pip show pyarmnn You can also verify it by running the following and getting output similar to below: ```bash $ python -c "import pyarmnn as ann;print(ann.GetVersion())" -'24.0.0' +'25.0.0' ``` # PyArmNN API overview diff --git a/python/pyarmnn/examples/image_classification/README.md b/python/pyarmnn/examples/image_classification/README.md index 3d2cbabca8..27ed4cad8b 100644 --- a/python/pyarmnn/examples/image_classification/README.md +++ b/python/pyarmnn/examples/image_classification/README.md @@ -20,7 +20,7 @@ $ pip show pyarmnn You can also verify it by running the following and getting output similar to below: ```bash $ python -c "import pyarmnn as ann;print(ann.GetVersion())" -'24.0.0' +'25.0.0' ``` ##### Dependencies diff --git a/python/pyarmnn/examples/object_detection/README.md b/python/pyarmnn/examples/object_detection/README.md index 38f0630fdf..267d2e9ece 100644 --- a/python/pyarmnn/examples/object_detection/README.md +++ b/python/pyarmnn/examples/object_detection/README.md @@ -23,7 +23,7 @@ $ pip show pyarmnn You can also verify it by running the following and getting output similar to below: ```bash $ python -c "import pyarmnn as ann;print(ann.GetVersion())" -'24.0.0' +'25.0.0' ``` ##### Dependencies diff --git a/python/pyarmnn/examples/speech_recognition/README.md b/python/pyarmnn/examples/speech_recognition/README.md index 5ccf003c77..f92bae6c42 100644 --- a/python/pyarmnn/examples/speech_recognition/README.md +++ b/python/pyarmnn/examples/speech_recognition/README.md @@ -18,7 +18,7 @@ You can also verify it by running the following and getting output similar to be ```bash $ python -c "import pyarmnn as ann;print(ann.GetVersion())" -'24.0.0' +'25.0.0' ``` ### Dependencies diff --git a/python/pyarmnn/src/pyarmnn/_version.py b/python/pyarmnn/src/pyarmnn/_version.py index 58e3954ea5..f7fc8c9a9c 100644 --- a/python/pyarmnn/src/pyarmnn/_version.py +++ b/python/pyarmnn/src/pyarmnn/_version.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: MIT import os -version_info = (24, 0, 0) +version_info = (25, 0, 0) __dev_version_env = os.getenv("PYARMNN_DEV_VER", "") @@ -24,7 +24,7 @@ def check_armnn_version(installed_armnn_version: str, expected_armnn_version: st """Compares expected Arm NN version and Arm NN version used to build the package. Args: - installed_armnn_version (str): Arm NN version used to generate the package (e.g. 24.0.0) + installed_armnn_version (str): Arm NN version used to generate the package (e.g. 25.0.0) expected_armnn_version (str): Expected Arm NN version Returns: diff --git a/python/pyarmnn/test/test_setup.py b/python/pyarmnn/test/test_setup.py index 19fd5f7c15..f47addb1f5 100644 --- a/python/pyarmnn/test/test_setup.py +++ b/python/pyarmnn/test/test_setup.py @@ -87,15 +87,15 @@ def test_gcc_serch_path(): def test_armnn_version(): - check_armnn_version('24.0.0', '24.0.0') + check_armnn_version('25.0.0', '25.0.0') def test_incorrect_armnn_version(): with pytest.raises(AssertionError) as err: - check_armnn_version('24.0.0', '24.1.0') + check_armnn_version('25.0.0', '25.1.0') - assert 'Expected ArmNN version is 24.1.0 but installed ArmNN version is 24.0.0' in str(err.value) + assert 'Expected ArmNN version is 25.1.0 but installed ArmNN version is 25.0.0' in str(err.value) def test_armnn_version_patch_does_not_matter(): - check_armnn_version('24.0.0', '24.0.1') + check_armnn_version('25.0.0', '25.0.1') diff --git a/python/pyarmnn/test/test_version.py b/python/pyarmnn/test/test_version.py index cfd8c88908..ddb4abe0dc 100644 --- a/python/pyarmnn/test/test_version.py +++ b/python/pyarmnn/test/test_version.py @@ -18,7 +18,7 @@ def test_dev_version(): importlib.reload(v) - assert "24.0.0.dev1" == v.__version__ + assert "25.0.0.dev1" == v.__version__ del os.environ["PYARMNN_DEV_VER"] del v @@ -30,7 +30,7 @@ def test_arm_version_not_affected(): importlib.reload(v) - assert "24.0.0" == v.__arm_ml_version__ + assert "25.0.0" == v.__arm_ml_version__ del os.environ["PYARMNN_DEV_VER"] del v diff --git a/samples/ObjectDetection/Readme.md b/samples/ObjectDetection/Readme.md index 59b4594130..bceaa4b250 100644 --- a/samples/ObjectDetection/Readme.md +++ b/samples/ObjectDetection/Readme.md @@ -168,10 +168,10 @@ From the build directory, copy the following to the host platform: The full list of libs after cross-compilation to copy on your board: ``` libarmnn.so -libarmnn.so.24 -libarmnn.so.24.0 +libarmnn.so.25 +libarmnn.so.25.0 libarmnnTfLiteParser.so -libarmnnTfLiteParser.so.24.0 +libarmnnTfLiteParser.so.24.1 libavcodec.so libavcodec.so.58 libavcodec.so.58.54.100 diff --git a/src/armnn/BackendHelper.cpp b/src/armnn/BackendHelper.cpp index 1467366323..1c926f4d30 100644 --- a/src/armnn/BackendHelper.cpp +++ b/src/armnn/BackendHelper.cpp @@ -23,7 +23,21 @@ LayerSupportHandle GetILayerSupportByBackendId(const armnn::BackendId& backend) auto factoryFunc = backendRegistry.GetFactory(backend); auto backendObject = factoryFunc(); - return LayerSupportHandle(backendObject->GetLayerSupport()); + return LayerSupportHandle(backendObject->GetLayerSupport(), backend); +} + +/// Convenience function to check a capability on a backend +bool IsCapabilitySupported(const armnn::BackendId& backend, armnn::BackendCapability capability) +{ + bool hasCapability = false; + auto const& backendRegistry = armnn::BackendRegistryInstance(); + if (backendRegistry.IsBackendRegistered(backend)) + { + auto factoryFunc = backendRegistry.GetFactory(backend); + auto backendObject = factoryFunc(); + hasCapability = backendObject->HasCapability(capability); + } + return hasCapability; } bool LayerSupportHandle::IsBackendRegistered() const @@ -293,6 +307,16 @@ bool LayerSupportHandle::IsFullyConnectedSupported(const TensorInfo& input, const FullyConnectedDescriptor& descriptor, Optional reasonIfUnsupported) { + if(!descriptor.m_ConstantWeights && !m_BackendId.IsUndefined()) + { + bool result = false; + result = IsCapabilitySupported(m_BackendId, BackendCapability::NonConstWeights); + if (!result) + { + return result; + } + } + return m_LayerSupport->IsFullyConnectedSupported(input, output, weights, diff --git a/src/armnn/Descriptors.cpp b/src/armnn/Descriptors.cpp index 881023e968..706992ccb0 100644 --- a/src/armnn/Descriptors.cpp +++ b/src/armnn/Descriptors.cpp @@ -425,4 +425,21 @@ int StridedSliceDescriptor::GetStopForAxis(const TensorShape& inputShape, } +uint32_t FullyConnectedDescriptor::GetNumViews() const +{ + // Return 1 with constant weights, otherwise check if bias is enabled + uint32_t numInputs = 1; + if (!m_ConstantWeights) + { + // non-const weights + numInputs = 2; + if (m_BiasEnabled) + { + // non-const bias + numInputs = 3; + } + } + return numInputs; +} + } diff --git a/src/armnn/Network.cpp b/src/armnn/Network.cpp index 9373a6ac15..18a4d02fca 100644 --- a/src/armnn/Network.cpp +++ b/src/armnn/Network.cpp @@ -171,21 +171,26 @@ IConnectableLayer* INetwork::AddFillLayer(const FillDescriptor& fillDescriptor, return pNetworkImpl->AddFillLayer(fillDescriptor, name); } - IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, const ConstTensor& weights, const Optional& biases, const char* name) { - return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor, weights, biases, name); + return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor, + armnn::Optional(weights), + biases, + name); } IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, const ConstTensor& weights, const char* name) { - Optional biases; - return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor, weights, biases, name); + armnn::Optional biases; + return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor, + armnn::Optional(weights), + biases, + name); } IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, @@ -193,8 +198,18 @@ IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescript const ConstTensor& biases, const char* name) { - return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor, weights, - armnn::Optional(biases), name); + return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor, + armnn::Optional(weights), + armnn::Optional(biases), + name); +} + +IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, + const Optional& weights, + const Optional& biases, + const char* name) +{ + return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor, weights, biases, name); } IConnectableLayer* INetwork::AddPermuteLayer(const PermuteDescriptor& permuteDescriptor, @@ -1709,41 +1724,58 @@ IConnectableLayer* NetworkImpl::AddFillLayer(const FillDescriptor& fillDescripto } IConnectableLayer* NetworkImpl::AddFullyConnectedLayerImpl(const FullyConnectedDescriptor& fullyConnectedDescriptor, - const ConstTensor& weights, - const Optional& biases, - const char* name) + const Optional& weights, + const Optional& biases, + const char* name) { - if (fullyConnectedDescriptor.m_BiasEnabled && !biases.has_value()) + if (fullyConnectedDescriptor.m_ConstantWeights && !weights.has_value()) { - throw InvalidArgumentException("AddFullyConnectedLayer: biases cannot be empty"); + throw InvalidArgumentException("AddFullyConnectedLayer: weights cannot be empty"); + + if (fullyConnectedDescriptor.m_BiasEnabled && !biases.has_value()) + { + throw InvalidArgumentException("AddFullyConnectedLayer: biases cannot be empty"); + } } const auto layer = m_Graph->AddLayer(fullyConnectedDescriptor, name); - layer->m_Weight = std::make_unique(weights); - - if (fullyConnectedDescriptor.m_BiasEnabled) + if (fullyConnectedDescriptor.m_ConstantWeights) { - layer->m_Bias = std::make_unique(biases.value()); + layer->m_Weight = std::make_unique(weights.value()); + if (fullyConnectedDescriptor.m_BiasEnabled) + { + layer->m_Bias = std::make_unique(biases.value()); + } } return layer; } +IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, + const Optional& weights, + const Optional& biases, + const char* name) +{ + return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name); +} + IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, const ConstTensor& weights, const Optional& biases, const char* name) { - return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name); + Optional optionalWeights(weights); + return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, optionalWeights, biases, name); } IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, const ConstTensor& weights, const char* name) { + Optional optionalWeights(weights); Optional biases; - return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name); + return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, optionalWeights, biases, name); } IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, @@ -1751,8 +1783,9 @@ IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescr const ConstTensor& biases, const char* name) { + Optional optionalWeights(weights); Optional optionalBiases(biases); - return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, optionalBiases, name); + return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, optionalWeights, optionalBiases, name); } IConnectableLayer* NetworkImpl::AddConcatLayer(const ConcatDescriptor& concatDescriptor, diff --git a/src/armnn/Network.hpp b/src/armnn/Network.hpp index 8f16be1684..30941ca9e4 100644 --- a/src/armnn/Network.hpp +++ b/src/armnn/Network.hpp @@ -103,6 +103,11 @@ public: IConnectableLayer* AddFillLayer(const FillDescriptor& fillDescriptor, const char* name = nullptr); + IConnectableLayer* AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, + const Optional& weights, + const Optional& biases, + const char* name = nullptr); + IConnectableLayer* AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, const ConstTensor& weights, const Optional& biases, @@ -265,7 +270,7 @@ public: private: IConnectableLayer* AddFullyConnectedLayerImpl(const FullyConnectedDescriptor& fullyConnectedDescriptor, - const ConstTensor& weights, + const Optional& weights, const Optional& biases, const char* name); diff --git a/src/armnn/layers/FullyConnectedLayer.cpp b/src/armnn/layers/FullyConnectedLayer.cpp index 0e5e5942de..6d0b57a84c 100644 --- a/src/armnn/layers/FullyConnectedLayer.cpp +++ b/src/armnn/layers/FullyConnectedLayer.cpp @@ -15,24 +15,25 @@ namespace armnn { FullyConnectedLayer::FullyConnectedLayer(const FullyConnectedDescriptor& param, const char* name) - : LayerWithParameters(1, 1, LayerType::FullyConnected, param, name) + : LayerWithParameters(param.GetNumViews(), 1, LayerType::FullyConnected, param, name) { } std::unique_ptr FullyConnectedLayer::CreateWorkload(const IWorkloadFactory& factory) const { // on this level constant data should not be released.. - ARMNN_ASSERT_MSG(m_Weight != nullptr, "FullyConnectedLayer: Weights data should not be null."); - FullyConnectedQueueDescriptor descriptor; - - descriptor.m_Weight = m_Weight.get(); - if (m_Param.m_BiasEnabled) + if (m_Param.m_ConstantWeights) { - ARMNN_ASSERT_MSG(m_Bias != nullptr, "FullyConnectedLayer: Bias data should not be null."); - descriptor.m_Bias = m_Bias.get(); + ARMNN_ASSERT_MSG(m_Weight != nullptr, "FullyConnectedLayer: Weights data should not be null."); + descriptor.m_Weight = m_Weight.get(); + + if (m_Param.m_BiasEnabled) + { + ARMNN_ASSERT_MSG(m_Bias != nullptr, "FullyConnectedLayer: Bias data should not be null."); + descriptor.m_Bias = m_Bias.get(); + } } - SetAdditionalInfo(descriptor); return factory.CreateFullyConnected(descriptor, PrepInfoAndDesc(descriptor)); @@ -41,13 +42,15 @@ std::unique_ptr FullyConnectedLayer::CreateWorkload(const IWorkloadFa FullyConnectedLayer* FullyConnectedLayer::Clone(Graph& graph) const { auto layer = CloneBase(graph, m_Param, GetName()); - - layer->m_Weight = m_Weight ? std::make_unique(*m_Weight) : nullptr; - if (layer->m_Param.m_BiasEnabled) + if (m_Param.m_ConstantWeights) { - layer->m_Bias = m_Bias ? std::make_unique(*m_Bias) : nullptr; - } + layer->m_Weight = m_Weight ? std::make_unique(*m_Weight) : nullptr; + if (layer->m_Param.m_BiasEnabled) + { + layer->m_Bias = m_Bias ? std::make_unique(*m_Bias) : nullptr; + } + } return std::move(layer); } @@ -70,11 +73,20 @@ void FullyConnectedLayer::ValidateTensorShapesFromInputs() VerifyShapeInferenceType(outputShape, m_ShapeInferenceMethod); - // check if we m_Weight data is not nullptr - ARMNN_ASSERT_MSG(m_Weight != nullptr, "FullyConnectedLayer: Weights data should not be null."); + std::vector inferredShapes; + if (m_Param.m_ConstantWeights) + { + // check if m_Weight data is not nullptr + ARMNN_ASSERT_MSG(m_Weight != nullptr, "FullyConnectedLayer: Weights data should not be null."); - auto inferredShapes = InferOutputShapes({GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(), - m_Weight->GetTensorInfo().GetShape() }); + inferredShapes = InferOutputShapes({GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(), + m_Weight->GetTensorInfo().GetShape()}); + } + else + { + inferredShapes = InferOutputShapes({GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(), + GetInputSlot(1).GetConnection()->GetTensorInfo().GetShape()}); + } ARMNN_ASSERT(inferredShapes.size() == 1); ARMNN_ASSERT(inferredShapes[0].GetDimensionality() == Dimensionality::Specified); @@ -89,27 +101,37 @@ Layer::ConstantTensors FullyConnectedLayer::GetConstantTensorsByRef() void FullyConnectedLayer::Accept(ILayerVisitor& visitor) const { - ConstTensor weightsTensor(m_Weight->GetTensorInfo(), m_Weight->Map(true)); + Optional optionalWeightsTensor = EmptyOptional(); Optional optionalBiasTensor = EmptyOptional(); - - if (GetParameters().m_BiasEnabled) + if(GetParameters().m_ConstantWeights) { - ConstTensor biasTensor(m_Bias->GetTensorInfo(), m_Bias->GetConstTensor()); - optionalBiasTensor = Optional(biasTensor); + ConstTensor weightsTensor(m_Weight->GetTensorInfo(), m_Weight->GetConstTensor()); + optionalWeightsTensor = Optional(weightsTensor); + + if (GetParameters().m_BiasEnabled) + { + ConstTensor biasTensor(m_Bias->GetTensorInfo(), m_Bias->GetConstTensor()); + optionalBiasTensor = Optional(biasTensor); + } } - - visitor.VisitFullyConnectedLayer(this, GetParameters(), weightsTensor, optionalBiasTensor, GetName()); + visitor.VisitFullyConnectedLayer(this, + GetParameters(), + optionalWeightsTensor.value(), + optionalBiasTensor, + GetName()); } void FullyConnectedLayer::ExecuteStrategy(IStrategy& strategy) const { - std::vector constTensors { {m_Weight->GetTensorInfo(), m_Weight->Map(true)} }; - - if (GetParameters().m_BiasEnabled) + std::vector constTensors; + if(GetParameters().m_ConstantWeights) { - constTensors.emplace_back(ConstTensor(m_Bias->GetTensorInfo(), m_Bias->Map(true))); + constTensors.emplace_back(ConstTensor(m_Weight->GetTensorInfo(), m_Weight->Map(true))); + if (GetParameters().m_BiasEnabled) + { + constTensors.emplace_back(ConstTensor(m_Bias->GetTensorInfo(), m_Bias->Map(true))); + } } - strategy.ExecuteStrategy(this, GetParameters(), constTensors, GetName()); } diff --git a/src/armnn/test/OptimizerTests.cpp b/src/armnn/test/OptimizerTests.cpp index fa860abb64..896fdfd68c 100644 --- a/src/armnn/test/OptimizerTests.cpp +++ b/src/armnn/test/OptimizerTests.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -679,6 +680,13 @@ public: }; }; +BOOST_AUTO_TEST_CASE(BackendCapabilityTest) +{ + BackendId backendId ="MockBackend"; + // MockBackend does not support the NonConstWeights capability + BOOST_CHECK(!armnn::IsCapabilitySupported(backendId, armnn::BackendCapability::NonConstWeights)); +} + BOOST_AUTO_TEST_CASE(BackendHintTest) { class TestBackendAssignment : public LayerVisitorBase diff --git a/src/armnn/test/UtilsTests.cpp b/src/armnn/test/UtilsTests.cpp index 7776a2d3cf..f0198cb9d4 100644 --- a/src/armnn/test/UtilsTests.cpp +++ b/src/armnn/test/UtilsTests.cpp @@ -278,6 +278,18 @@ BOOST_AUTO_TEST_CASE(LayerSupportHandle) BOOST_CHECK(layerSupportObject.IsBackendRegistered()); } + +BOOST_AUTO_TEST_CASE(IsCapabilitySupported_CpuRef) +{ + BOOST_CHECK(armnn::IsCapabilitySupported(armnn::Compute::CpuRef, armnn::BackendCapability::NonConstWeights)); +} +#endif + +#if defined(ARMCOMPUTENEON_ENABLED) +BOOST_AUTO_TEST_CASE(IsCapabilitySupported_CpuAcc) +{ + BOOST_CHECK(!armnn::IsCapabilitySupported(armnn::Compute::CpuAcc, armnn::BackendCapability::NonConstWeights)); +} #endif BOOST_AUTO_TEST_SUITE_END() diff --git a/src/armnnDeserializer/Deserializer.cpp b/src/armnnDeserializer/Deserializer.cpp index c34797725f..633c272f00 100644 --- a/src/armnnDeserializer/Deserializer.cpp +++ b/src/armnnDeserializer/Deserializer.cpp @@ -1841,7 +1841,6 @@ void IDeserializer::DeserializerImpl::ParseFullyConnected(GraphPtr graph, unsign CHECK_LAYERS(graph, 0, layerIndex); auto inputs = GetInputs(graph, layerIndex); CHECK_LOCATION(); - CHECK_VALID_SIZE(inputs.size(), 1); auto outputs = GetOutputs(graph, layerIndex); CHECK_VALID_SIZE(outputs.size(), 1); @@ -1853,20 +1852,36 @@ void IDeserializer::DeserializerImpl::ParseFullyConnected(GraphPtr graph, unsign armnn::FullyConnectedDescriptor fullyConnectedDescriptor; fullyConnectedDescriptor.m_BiasEnabled = flatBufferDescriptor->biasEnabled(); fullyConnectedDescriptor.m_TransposeWeightMatrix = flatBufferDescriptor->transposeWeightsMatrix(); + fullyConnectedDescriptor.m_ConstantWeights = flatBufferDescriptor->constantWeights(); + uint32_t numInputs = 1; + if (!fullyConnectedDescriptor.m_ConstantWeights) + { + numInputs = 2; + if (fullyConnectedDescriptor.m_BiasEnabled) + { + numInputs = 3; + } + } + CHECK_VALID_SIZE(inputs.size(), numInputs); - armnn::ConstTensor weightsTensor = ToConstTensor(flatBufferLayer->weights()); - - armnn::IConnectableLayer* layer; + armnn::Optional optionalWeights = armnn::EmptyOptional(); armnn::Optional optionalBiases = armnn::EmptyOptional(); - if (flatBufferDescriptor->biasEnabled()) + if (fullyConnectedDescriptor.m_ConstantWeights) { - armnn::ConstTensor biasTensorData = ToConstTensor(flatBufferLayer->biases()); - optionalBiases = armnn::Optional(biasTensorData); + armnn::ConstTensor weightsTensorData = ToConstTensor(flatBufferLayer->weights()); + optionalWeights = armnn::Optional(weightsTensorData); + + if (flatBufferDescriptor->biasEnabled()) + { + armnn::ConstTensor biasTensorData = ToConstTensor(flatBufferLayer->biases()); + optionalBiases = armnn::Optional(biasTensorData); + } } - layer = m_Network->AddFullyConnectedLayer(fullyConnectedDescriptor, - weightsTensor, - optionalBiases, - layerName.c_str()); + + armnn::IConnectableLayer* layer = m_Network->AddFullyConnectedLayer(fullyConnectedDescriptor, + optionalWeights, + optionalBiases, + layerName.c_str()); armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]); layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo); diff --git a/src/armnnSerializer/ArmnnSchema.fbs b/src/armnnSerializer/ArmnnSchema.fbs index e2b3a3c288..88d66f76f5 100644 --- a/src/armnnSerializer/ArmnnSchema.fbs +++ b/src/armnnSerializer/ArmnnSchema.fbs @@ -321,6 +321,7 @@ table FullyConnectedLayer { table FullyConnectedDescriptor { biasEnabled:bool = false; transposeWeightsMatrix:bool = false; + constantWeights:bool = true; } table GatherLayer { diff --git a/src/armnnSerializer/ArmnnSchema_generated.h b/src/armnnSerializer/ArmnnSchema_generated.h index 524ffb0182..99ab0dc78a 100644 --- a/src/armnnSerializer/ArmnnSchema_generated.h +++ b/src/armnnSerializer/ArmnnSchema_generated.h @@ -3504,7 +3504,8 @@ struct FullyConnectedDescriptor FLATBUFFERS_FINAL_CLASS : private flatbuffers::T typedef FullyConnectedDescriptorBuilder Builder; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_BIASENABLED = 4, - VT_TRANSPOSEWEIGHTSMATRIX = 6 + VT_TRANSPOSEWEIGHTSMATRIX = 6, + VT_CONSTANTWEIGHTS = 8 }; bool biasEnabled() const { return GetField(VT_BIASENABLED, 0) != 0; @@ -3512,10 +3513,14 @@ struct FullyConnectedDescriptor FLATBUFFERS_FINAL_CLASS : private flatbuffers::T bool transposeWeightsMatrix() const { return GetField(VT_TRANSPOSEWEIGHTSMATRIX, 0) != 0; } + bool constantWeights() const { + return GetField(VT_CONSTANTWEIGHTS, 1) != 0; + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField(verifier, VT_BIASENABLED) && VerifyField(verifier, VT_TRANSPOSEWEIGHTSMATRIX) && + VerifyField(verifier, VT_CONSTANTWEIGHTS) && verifier.EndTable(); } }; @@ -3530,6 +3535,9 @@ struct FullyConnectedDescriptorBuilder { void add_transposeWeightsMatrix(bool transposeWeightsMatrix) { fbb_.AddElement(FullyConnectedDescriptor::VT_TRANSPOSEWEIGHTSMATRIX, static_cast(transposeWeightsMatrix), 0); } + void add_constantWeights(bool constantWeights) { + fbb_.AddElement(FullyConnectedDescriptor::VT_CONSTANTWEIGHTS, static_cast(constantWeights), 1); + } explicit FullyConnectedDescriptorBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); @@ -3545,8 +3553,10 @@ struct FullyConnectedDescriptorBuilder { inline flatbuffers::Offset CreateFullyConnectedDescriptor( flatbuffers::FlatBufferBuilder &_fbb, bool biasEnabled = false, - bool transposeWeightsMatrix = false) { + bool transposeWeightsMatrix = false, + bool constantWeights = true) { FullyConnectedDescriptorBuilder builder_(_fbb); + builder_.add_constantWeights(constantWeights); builder_.add_transposeWeightsMatrix(transposeWeightsMatrix); builder_.add_biasEnabled(biasEnabled); return builder_.Finish(); diff --git a/src/armnnSerializer/Serializer.cpp b/src/armnnSerializer/Serializer.cpp index 0586700ada..ae9ddf29f2 100644 --- a/src/armnnSerializer/Serializer.cpp +++ b/src/armnnSerializer/Serializer.cpp @@ -1118,12 +1118,8 @@ void SerializerStrategy::SerializeQuantizeLayer(const armnn::IConnectableLayer * void SerializerStrategy::SerializeFullyConnectedLayer(const armnn::IConnectableLayer* layer, const armnn::FullyConnectedDescriptor& fullyConnectedDescriptor, const std::vector& constants, - const char* name) + const char*) { - IgnoreUnused(name); - - const armnn::ConstTensor& weights = constants.at(0); - // Create FlatBuffer BaseLayer auto flatBufferBaseLayer = CreateLayerBase(layer, serializer::LayerType::LayerType_FullyConnected); @@ -1131,17 +1127,23 @@ void SerializerStrategy::SerializeFullyConnectedLayer(const armnn::IConnectableL auto flatBufferDescriptor = serializer::CreateFullyConnectedDescriptor(m_flatBufferBuilder, fullyConnectedDescriptor.m_BiasEnabled, - fullyConnectedDescriptor.m_TransposeWeightMatrix); + fullyConnectedDescriptor.m_TransposeWeightMatrix, + fullyConnectedDescriptor.m_ConstantWeights); // Create FlatBuffer weights data - auto flatBufferWeights = CreateConstTensorInfo(weights); - + flatbuffers::Offset flatBufferWeights; // Create FlatBuffer bias data flatbuffers::Offset flatBufferBiases; - if (fullyConnectedDescriptor.m_BiasEnabled) + if (fullyConnectedDescriptor.m_ConstantWeights && !constants.empty()) { - armnn::ConstTensor biases = constants.at(1); - flatBufferBiases = CreateConstTensorInfo(biases); + armnn::ConstTensor weights = constants.at(0); + flatBufferWeights = CreateConstTensorInfo(weights); + + if (fullyConnectedDescriptor.m_BiasEnabled) + { + armnn::ConstTensor biases = constants.at(1); + flatBufferBiases = CreateConstTensorInfo(biases); + } } // Create FlatBuffer FullyConnectedLayer diff --git a/src/armnnSerializer/test/SerializerTests.cpp b/src/armnnSerializer/test/SerializerTests.cpp index f261731a75..d7c10cb599 100644 --- a/src/armnnSerializer/test/SerializerTests.cpp +++ b/src/armnnSerializer/test/SerializerTests.cpp @@ -748,6 +748,7 @@ BOOST_AUTO_TEST_CASE(SerializeFullyConnected) armnn::FullyConnectedDescriptor descriptor; descriptor.m_BiasEnabled = true; descriptor.m_TransposeWeightMatrix = false; + descriptor.m_ConstantWeights = true; armnn::INetworkPtr network = armnn::INetwork::Create(); armnn::IConnectableLayer* const inputLayer = network->AddInputLayer(0); @@ -773,6 +774,53 @@ BOOST_AUTO_TEST_CASE(SerializeFullyConnected) deserializedNetwork->ExecuteStrategy(verifier); } +BOOST_AUTO_TEST_CASE(SerializeFullyConnectedWeightsAsInputs) +{ + const std::string layerName("fullyConnected_weights_as_inputs"); + const armnn::TensorInfo inputInfo ({ 2, 5, 1, 1 }, armnn::DataType::Float32); + const armnn::TensorInfo outputInfo({ 2, 3 }, armnn::DataType::Float32); + + const armnn::TensorInfo weightsInfo({ 5, 3 }, armnn::DataType::Float32); + const armnn::TensorInfo biasesInfo ({ 3 }, armnn::DataType::Float32); + + armnn::Optional weights = armnn::EmptyOptional(); + armnn::Optional bias = armnn::EmptyOptional(); + + armnn::FullyConnectedDescriptor descriptor; + descriptor.m_BiasEnabled = true; + descriptor.m_TransposeWeightMatrix = false; + descriptor.m_ConstantWeights = false; + + armnn::INetworkPtr network = armnn::INetwork::Create(); + armnn::IConnectableLayer* const inputLayer = network->AddInputLayer(0); + armnn::IConnectableLayer* const weightsInputLayer = network->AddInputLayer(1); + armnn::IConnectableLayer* const biasInputLayer = network->AddInputLayer(2); + armnn::IConnectableLayer* const fullyConnectedLayer = + network->AddFullyConnectedLayer(descriptor, + weights, + bias, + layerName.c_str()); + armnn::IConnectableLayer* const outputLayer = network->AddOutputLayer(0); + + inputLayer->GetOutputSlot(0).Connect(fullyConnectedLayer->GetInputSlot(0)); + weightsInputLayer->GetOutputSlot(0).Connect(fullyConnectedLayer->GetInputSlot(1)); + biasInputLayer->GetOutputSlot(0).Connect(fullyConnectedLayer->GetInputSlot(2)); + fullyConnectedLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); + + inputLayer->GetOutputSlot(0).SetTensorInfo(inputInfo); + weightsInputLayer->GetOutputSlot(0).SetTensorInfo(weightsInfo); + biasInputLayer->GetOutputSlot(0).SetTensorInfo(biasesInfo); + fullyConnectedLayer->GetOutputSlot(0).SetTensorInfo(outputInfo); + + armnn::INetworkPtr deserializedNetwork = DeserializeNetwork(SerializeNetwork(*network)); + BOOST_CHECK(deserializedNetwork); + + const std::vector constants {}; + LayerVerifierBaseWithDescriptorAndConstants verifier( + layerName, {inputInfo, weightsInfo, biasesInfo}, {outputInfo}, descriptor, constants); + deserializedNetwork->ExecuteStrategy(verifier); +} + BOOST_AUTO_TEST_CASE(SerializeGather) { using GatherDescriptor = armnn::GatherDescriptor; diff --git a/src/backends/backendsCommon/WorkloadData.cpp b/src/backends/backendsCommon/WorkloadData.cpp index 90db57f953..2c5303c019 100644 --- a/src/backends/backendsCommon/WorkloadData.cpp +++ b/src/backends/backendsCommon/WorkloadData.cpp @@ -1022,7 +1022,16 @@ void FullyConnectedQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) c { const std::string descriptorName{"FullyConnectedQueueDescriptor"}; - ValidateNumInputs(workloadInfo, descriptorName, 1); + uint32_t numInputs = 1; + if (!m_Parameters.m_ConstantWeights) + { + numInputs = 2; + if (m_Parameters.m_BiasEnabled) + { + numInputs = 3; + } + } + ValidateNumInputs(workloadInfo, descriptorName, numInputs); ValidateNumOutputs(workloadInfo, descriptorName, 1); const TensorInfo& inputTensorInfo = workloadInfo.m_InputTensorInfos[0]; @@ -1035,19 +1044,32 @@ void FullyConnectedQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) c throw InvalidArgumentException(descriptorName + ": Input tensor must have 2 or 4 dimensions."); } - ValidatePointer(m_Weight, descriptorName, "weight"); - - const TensorInfo& weightTensorInfo = m_Weight->GetTensorInfo(); + TensorInfo weightTensorInfo; + if (m_Parameters.m_ConstantWeights) + { + ValidatePointer(m_Weight, descriptorName, "weight"); + weightTensorInfo = m_Weight->GetTensorInfo(); + } + else + { + weightTensorInfo = workloadInfo.m_InputTensorInfos[1]; + } ValidateTensorNumDimensions(weightTensorInfo, descriptorName, 2, "weight"); if (m_Parameters.m_BiasEnabled) { - ValidatePointer(m_Bias, descriptorName, "bias"); - + TensorInfo biasTensorInfo; + if (m_Parameters.m_ConstantWeights) + { + ValidatePointer(m_Bias, descriptorName, "bias"); + biasTensorInfo = m_Bias->GetTensorInfo(); + } + else + { + biasTensorInfo = workloadInfo.m_InputTensorInfos[2]; + } // Validates type and quantization values. - const TensorInfo& biasTensorInfo = m_Bias->GetTensorInfo(); ValidateBiasTensorQuantization(biasTensorInfo, inputTensorInfo, weightTensorInfo, descriptorName); - ValidateTensorDataType(biasTensorInfo, GetBiasDataType(inputTensorInfo.GetDataType()), descriptorName, "bias"); ValidateTensorNumDimensions(biasTensorInfo, descriptorName, 1, "bias"); } diff --git a/src/backends/backendsCommon/WorkloadFactory.cpp b/src/backends/backendsCommon/WorkloadFactory.cpp index 19281a82e9..20d7134c3a 100644 --- a/src/backends/backendsCommon/WorkloadFactory.cpp +++ b/src/backends/backendsCommon/WorkloadFactory.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -63,7 +64,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, auto backendFactory = backendRegistry.GetFactory(backendId); auto backendObject = backendFactory(); - auto layerSupportObject = backendObject->GetLayerSupport(modelOptions); + auto layerSupportObject = LayerSupportHandle(backendObject->GetLayerSupport(modelOptions), backendId); switch(layer.GetType()) { @@ -72,7 +73,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, auto cLayer = PolymorphicDowncast(&layer); const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsActivationSupported( + result = layerSupportObject.IsActivationSupported( OverrideDataType(input, dataType), OverrideDataType(output, dataType), cLayer->GetParameters(), @@ -84,7 +85,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input0 = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsAdditionSupported( + result = layerSupportObject.IsAdditionSupported( OverrideDataType(input0, dataType), OverrideDataType(input1, dataType), OverrideDataType(output, dataType), @@ -98,7 +99,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsArgMinMaxSupported( + result = layerSupportObject.IsArgMinMaxSupported( OverrideDataType(input, dataType), OverrideDataType(output, DataType::Signed32), descriptor, @@ -114,7 +115,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& var = cLayer->m_Variance->GetTensorInfo(); const TensorInfo& beta = cLayer->m_Beta->GetTensorInfo(); const TensorInfo& gamma = cLayer->m_Gamma->GetTensorInfo(); - result = layerSupportObject->IsBatchNormalizationSupported( + result = layerSupportObject.IsBatchNormalizationSupported( OverrideDataType(input, dataType), OverrideDataType(output, dataType), OverrideDataType(mean, dataType), @@ -131,10 +132,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); auto cLayer = PolymorphicDowncast(&layer); - result = layerSupportObject->IsBatchToSpaceNdSupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsBatchToSpaceNdSupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + cLayer->GetParameters(), + reason); break; } case LayerType::Comparison: @@ -145,45 +146,45 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsComparisonSupported(OverrideDataType(input0, dataType), - OverrideDataType(input1, dataType), - OverrideDataType(output, DataType::Boolean), - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsComparisonSupported(OverrideDataType(input0, dataType), + OverrideDataType(input1, dataType), + OverrideDataType(output, DataType::Boolean), + cLayer->GetParameters(), + reason); break; } case LayerType::Constant: { const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsConstantSupported(OverrideDataType(output, dataType), reason); + result = layerSupportObject.IsConstantSupported(OverrideDataType(output, dataType), reason); break; } case LayerType::ConvertBf16ToFp32: { const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsConvertBf16ToFp32Supported(input, output, reason); + result = layerSupportObject.IsConvertBf16ToFp32Supported(input, output, reason); break; } case LayerType::ConvertFp16ToFp32: { const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsConvertFp16ToFp32Supported(input, output, reason); + result = layerSupportObject.IsConvertFp16ToFp32Supported(input, output, reason); break; } case LayerType::ConvertFp32ToBf16: { const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsConvertFp32ToBf16Supported(input, output, reason); + result = layerSupportObject.IsConvertFp32ToBf16Supported(input, output, reason); break; } case LayerType::ConvertFp32ToFp16: { const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsConvertFp32ToFp16Supported(input, output, reason); + result = layerSupportObject.IsConvertFp32ToFp16Supported(input, output, reason); break; } case LayerType::Convolution2d: @@ -205,7 +206,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, OverrideDataType(cLayer->m_Bias->GetTensorInfo(), GetBiasTypeFromWeightsType(dataType)); } - result = layerSupportObject->IsConvolution2dSupported( + result = layerSupportObject.IsConvolution2dSupported( input, output, descriptor, @@ -219,7 +220,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsDebugSupported(OverrideDataType(input, dataType), + result = layerSupportObject.IsDebugSupported(OverrideDataType(input, dataType), OverrideDataType(output, dataType), reason); break; @@ -231,7 +232,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsDepthToSpaceSupported(OverrideDataType(input, dataType), + result = layerSupportObject.IsDepthToSpaceSupported(OverrideDataType(input, dataType), OverrideDataType(output, dataType), cLayer->GetParameters(), reason); @@ -255,7 +256,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, OverrideDataType(cLayer->m_Bias->GetTensorInfo(), GetBiasTypeFromWeightsType(dataType)); } - result = layerSupportObject->IsDepthwiseConvolutionSupported( + result = layerSupportObject.IsDepthwiseConvolutionSupported( input, output, descriptor, @@ -269,9 +270,9 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsDequantizeSupported(input, - OverrideDataType(output, dataType), - reason); + result = layerSupportObject.IsDequantizeSupported(input, + OverrideDataType(output, dataType), + reason); break; } case LayerType::DetectionPostProcess: @@ -287,15 +288,15 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& numDetections = layer.GetOutputSlot(3).GetTensorInfo(); const DetectionPostProcessDescriptor& descriptor = cLayer->GetParameters(); - result = layerSupportObject->IsDetectionPostProcessSupported(boxEncodings, - scores, - anchors, - detectionBoxes, - detectionClasses, - detectionScores, - numDetections, - descriptor, - reason); + result = layerSupportObject.IsDetectionPostProcessSupported(boxEncodings, + scores, + anchors, + detectionBoxes, + detectionClasses, + detectionScores, + numDetections, + descriptor, + reason); break; } case LayerType::ElementwiseUnary: @@ -305,10 +306,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsElementwiseUnarySupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsElementwiseUnarySupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + cLayer->GetParameters(), + reason); break; } case LayerType::Fill: @@ -318,7 +319,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); const FillDescriptor& descriptor = cLayer->GetParameters(); - result = layerSupportObject->IsFillSupported( + result = layerSupportObject.IsFillSupported( OverrideDataType(input, dataType), OverrideDataType(output, dataType), descriptor, @@ -329,18 +330,18 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, { auto cLayer = PolymorphicDowncast(&layer); const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); - result = layerSupportObject->IsFakeQuantizationSupported(OverrideDataType(input, dataType), - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsFakeQuantizationSupported(OverrideDataType(input, dataType), + cLayer->GetParameters(), + reason); break; } case LayerType::Floor: { const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsFloorSupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - reason); + result = layerSupportObject.IsFloorSupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + reason); break; } case LayerType::FullyConnected: @@ -348,21 +349,43 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, auto cLayer = PolymorphicDowncast(&layer); const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - ARMNN_ASSERT(cLayer->m_Weight.get() != nullptr); + + const FullyConnectedDescriptor& descriptor = cLayer->GetParameters(); + TensorInfo weightsInfo; + const TensorInfo* weightsInfoPtr = nullptr; + + if (descriptor.m_ConstantWeights) + { + ARMNN_ASSERT(cLayer->m_Weight.get() != nullptr); + weightsInfo = OverrideDataType(cLayer->m_Weight->GetTensorInfo(), dataType); + } + else + { + weightsInfo = OverrideDataType(layer.GetInputSlot(1).GetConnection()->GetTensorInfo(), dataType); + + } + weightsInfoPtr = &weightsInfo; TensorInfo biasInfo; - const TensorInfo * biasInfoPtr = nullptr; + const TensorInfo* biasInfoPtr = nullptr; static const TensorInfo dummyBFloat16Bias(TensorShape({1,1,1,1}), DataType::BFloat16); static const TensorInfo dummyFloat16Bias(TensorShape({1,1,1,1}), DataType::Float16); static const TensorInfo dummyFloat32Bias(TensorShape({1,1,1,1}), DataType::Float32); static const TensorInfo dummyQA8Bias(TensorShape({1,1,1,1}), DataType::Signed32); - const FullyConnectedDescriptor& descriptor = cLayer->GetParameters(); if (descriptor.m_BiasEnabled) { - ARMNN_ASSERT(cLayer->m_Bias.get() != nullptr); - biasInfo = OverrideDataType(cLayer->m_Bias->GetTensorInfo(), GetBiasTypeFromWeightsType(dataType)); - biasInfoPtr = &biasInfo; + if(descriptor.m_ConstantWeights) + { + ARMNN_ASSERT(cLayer->m_Bias.get() != nullptr); + biasInfo = OverrideDataType(cLayer->m_Bias->GetTensorInfo(), GetBiasTypeFromWeightsType(dataType)); + biasInfoPtr = &biasInfo; + } + else + { + biasInfo = OverrideDataType(layer.GetInputSlot(2).GetConnection()->GetTensorInfo(), dataType); + biasInfoPtr = &biasInfo; + } } else { @@ -398,11 +421,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, } } } - - result = layerSupportObject->IsFullyConnectedSupported( + result = layerSupportObject.IsFullyConnectedSupported( OverrideDataType(input, dataType), OverrideDataType(output, dataType), - OverrideDataType(cLayer->m_Weight->GetTensorInfo(), dataType), + *weightsInfoPtr, *biasInfoPtr, descriptor, reason); @@ -415,17 +437,17 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); auto cLayer = PolymorphicDowncast(&layer); const GatherDescriptor& descriptor = cLayer->GetParameters(); - result = layerSupportObject->IsGatherSupported(OverrideDataType(input0, dataType), - input1, - OverrideDataType(output, dataType), - descriptor, - reason); + result = layerSupportObject.IsGatherSupported(OverrideDataType(input0, dataType), + input1, + OverrideDataType(output, dataType), + descriptor, + reason); break; } case LayerType::Input: { const TensorInfo& input = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsInputSupported(OverrideDataType(input, dataType), reason); + result = layerSupportObject.IsInputSupported(OverrideDataType(input, dataType), reason); break; } case LayerType::InstanceNormalization: @@ -436,7 +458,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsInstanceNormalizationSupported( + result = layerSupportObject.IsInstanceNormalizationSupported( OverrideDataType(input, dataType), OverrideDataType(output, dataType), descriptor, @@ -451,7 +473,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsL2NormalizationSupported( + result = layerSupportObject.IsL2NormalizationSupported( OverrideDataType(input, dataType), OverrideDataType(output, dataType), descriptor, @@ -466,11 +488,11 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsLogicalBinarySupported(input0, - input1, - output, - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsLogicalBinarySupported(input0, + input1, + output, + cLayer->GetParameters(), + reason); break; } case LayerType::LogSoftmax: @@ -480,10 +502,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsLogSoftmaxSupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsLogSoftmaxSupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + cLayer->GetParameters(), + reason); break; } case LayerType::Lstm: @@ -617,7 +639,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, paramsInfo.m_OutputLayerNormWeights = &optOutputLayerNormWeights; } - result = layerSupportObject->IsLstmSupported( + result = layerSupportObject.IsLstmSupported( input, outputStateIn, cellStateIn, @@ -636,10 +658,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsMaximumSupported(OverrideDataType(input0, dataType), - OverrideDataType(input1, dataType), - OverrideDataType(output, dataType), - reason); + result = layerSupportObject.IsMaximumSupported(OverrideDataType(input0, dataType), + OverrideDataType(input1, dataType), + OverrideDataType(output, dataType), + reason); break; } case LayerType::MemCopy: @@ -647,9 +669,9 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsMemCopySupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - reason); + result = layerSupportObject.IsMemCopySupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + reason); break; } case LayerType::MemImport: @@ -657,9 +679,9 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsMemImportSupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - reason); + result = layerSupportObject.IsMemImportSupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + reason); break; } case LayerType::Merge: @@ -668,10 +690,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsMergeSupported(OverrideDataType(input0, dataType), - OverrideDataType(input1, dataType), - OverrideDataType(output, dataType), - reason); + result = layerSupportObject.IsMergeSupported(OverrideDataType(input0, dataType), + OverrideDataType(input1, dataType), + OverrideDataType(output, dataType), + reason); break; } case LayerType::Concat: @@ -699,7 +721,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsConcatSupported(inputPtrs, output, cLayer->GetParameters(), reason); + result = layerSupportObject.IsConcatSupported(inputPtrs, output, cLayer->GetParameters(), reason); break; @@ -709,7 +731,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input0 = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsMultiplicationSupported( + result = layerSupportObject.IsMultiplicationSupported( OverrideDataType(input0, dataType), OverrideDataType(input1, dataType), OverrideDataType(output, dataType), @@ -721,16 +743,16 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, auto cLayer = PolymorphicDowncast(&layer); const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsNormalizationSupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsNormalizationSupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + cLayer->GetParameters(), + reason); break; } case LayerType::Output: { const TensorInfo& output = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); - result = layerSupportObject->IsOutputSupported(OverrideDataType(output, dataType), reason); + result = layerSupportObject.IsOutputSupported(OverrideDataType(output, dataType), reason); break; } case LayerType::Permute: @@ -738,10 +760,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, auto cLayer = PolymorphicDowncast(&layer); const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsPermuteSupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsPermuteSupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + cLayer->GetParameters(), + reason); break; } case LayerType::Pad: @@ -749,7 +771,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, auto cLayer = PolymorphicDowncast(&layer); const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsPadSupported( + result = layerSupportObject.IsPadSupported( OverrideDataType(input, dataType), OverrideDataType(output, dataType), cLayer->GetParameters(), @@ -761,26 +783,26 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, auto cLayer = PolymorphicDowncast(&layer); const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsPooling2dSupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsPooling2dSupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + cLayer->GetParameters(), + reason); break; } case LayerType::PreCompiled: { auto cLayer = PolymorphicDowncast(&layer); const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); - result = layerSupportObject->IsPreCompiledSupported(OverrideDataType(input, dataType), - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsPreCompiledSupported(OverrideDataType(input, dataType), + cLayer->GetParameters(), + reason); break; } case LayerType::Quantize: { const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsQuantizeSupported(input, output, reason); + result = layerSupportObject.IsQuantizeSupported(input, output, reason); break; } case LayerType::QLstm: @@ -865,15 +887,15 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, &cLayer->m_LayerNormParameters.m_OutputLayerNormWeights->GetTensorInfo(); } - result = layerSupportObject->IsQLstmSupported(input, - previousOutputIn, - previousCellStateIn, - outputStateOut, - cellStateOut, - output, - descriptor, - paramsInfo, - reason); + result = layerSupportObject.IsQLstmSupported(input, + previousOutputIn, + previousCellStateIn, + outputStateOut, + cellStateOut, + output, + descriptor, + paramsInfo, + reason); break; } case LayerType::QuantizedLstm: @@ -919,13 +941,13 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, paramsInfo.m_OutputGateBias = &cLayer->m_QuantizedLstmParameters.m_OutputGateBias->GetTensorInfo();; - result = layerSupportObject->IsQuantizedLstmSupported(input, - previousCellStateIn, - previousOutputIn, - cellStateOut, - output, - paramsInfo, - reason); + result = layerSupportObject.IsQuantizedLstmSupported(input, + previousCellStateIn, + previousOutputIn, + cellStateOut, + output, + paramsInfo, + reason); break; } case LayerType::Division: @@ -933,7 +955,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input0 = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsDivisionSupported( + result = layerSupportObject.IsDivisionSupported( OverrideDataType(input0, dataType), OverrideDataType(input1, dataType), OverrideDataType(output, dataType), @@ -944,9 +966,9 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, { const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsRankSupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - reason); + result = layerSupportObject.IsRankSupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + reason); break; } case LayerType::Reshape: @@ -954,10 +976,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, auto cLayer = PolymorphicDowncast(&layer); const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsReshapeSupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsReshapeSupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + cLayer->GetParameters(), + reason); break; } case LayerType::Resize: @@ -965,10 +987,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, auto cLayer = PolymorphicDowncast(&layer); const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsResizeSupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsResizeSupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + cLayer->GetParameters(), + reason); break; } case LayerType::Slice: @@ -978,10 +1000,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsSliceSupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsSliceSupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + cLayer->GetParameters(), + reason); break; } case LayerType::Softmax: @@ -989,10 +1011,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, auto cLayer = PolymorphicDowncast(&layer); const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsSoftmaxSupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsSoftmaxSupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + cLayer->GetParameters(), + reason); break; } case LayerType::SpaceToBatchNd: @@ -1000,10 +1022,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, auto cLayer = PolymorphicDowncast(&layer); const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsSpaceToBatchNdSupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsSpaceToBatchNdSupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + cLayer->GetParameters(), + reason); break; } case LayerType::SpaceToDepth: @@ -1013,10 +1035,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsSpaceToDepthSupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsSpaceToDepthSupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + cLayer->GetParameters(), + reason); break; } case LayerType::Splitter: @@ -1035,10 +1057,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const std::vector> outputPtrs(outputs.begin(), outputs.end()); - result = layerSupportObject->IsSplitterSupported(OverrideDataType(input, dataType), - outputPtrs, - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsSplitterSupported(OverrideDataType(input, dataType), + outputPtrs, + cLayer->GetParameters(), + reason); break; } case LayerType::Stack: @@ -1064,7 +1086,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsStackSupported(inputPtrs, output, cLayer->GetParameters(), reason); + result = layerSupportObject.IsStackSupported(inputPtrs, output, cLayer->GetParameters(), reason); break; } @@ -1103,10 +1125,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, std::vector outputPtrs(beginPtrO, endPtrO); - result = layerSupportObject->IsStandInSupported(inputPtrs, - outputPtrs, - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsStandInSupported(inputPtrs, + outputPtrs, + cLayer->GetParameters(), + reason); break; } case LayerType::StridedSlice: @@ -1114,10 +1136,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, auto cLayer = PolymorphicDowncast(&layer); const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsStridedSliceSupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsStridedSliceSupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + cLayer->GetParameters(), + reason); break; } case LayerType::Subtraction: @@ -1125,7 +1147,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input0 = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsSubtractionSupported( + result = layerSupportObject.IsSubtractionSupported( OverrideDataType(input0, dataType), OverrideDataType(input1, dataType), OverrideDataType(output, dataType), @@ -1138,11 +1160,11 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo(); const TensorInfo& output0 = layer.GetOutputSlot(0).GetTensorInfo(); const TensorInfo& output1 = layer.GetOutputSlot(1).GetTensorInfo(); - result = layerSupportObject->IsSwitchSupported(OverrideDataType(input0, dataType), - OverrideDataType(input1, dataType), - OverrideDataType(output0, dataType), - OverrideDataType(output1, dataType), - reason); + result = layerSupportObject.IsSwitchSupported(OverrideDataType(input0, dataType), + OverrideDataType(input1, dataType), + OverrideDataType(output0, dataType), + OverrideDataType(output1, dataType), + reason); break; } case LayerType::Mean: @@ -1150,7 +1172,7 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, auto cLayer = PolymorphicDowncast(&layer); const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsMeanSupported( + result = layerSupportObject.IsMeanSupported( OverrideDataType(input, dataType), OverrideDataType(output, dataType), cLayer->GetParameters(), @@ -1162,10 +1184,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input0 = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsMinimumSupported(OverrideDataType(input0, dataType), - OverrideDataType(input1, dataType), - OverrideDataType(output, dataType), - reason); + result = layerSupportObject.IsMinimumSupported(OverrideDataType(input0, dataType), + OverrideDataType(input1, dataType), + OverrideDataType(output, dataType), + reason); break; } case LayerType::Prelu: @@ -1173,10 +1195,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& alpha = layer.GetInputSlot(1).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsPreluSupported(OverrideDataType(input, dataType), - OverrideDataType(alpha, dataType), - OverrideDataType(output, dataType), - reason); + result = layerSupportObject.IsPreluSupported(OverrideDataType(input, dataType), + OverrideDataType(alpha, dataType), + OverrideDataType(output, dataType), + reason); break; } case LayerType::Transpose: @@ -1184,10 +1206,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, auto cLayer = PolymorphicDowncast(&layer); const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsTransposeSupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsTransposeSupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + cLayer->GetParameters(), + reason); break; } case LayerType::TransposeConvolution2d: @@ -1211,12 +1233,12 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, ARMNN_ASSERT(cLayer->m_Weight.get() != nullptr); const TensorInfo weights = OverrideDataType(cLayer->m_Weight->GetTensorInfo(), dataType); - result = layerSupportObject->IsTransposeConvolution2dSupported(input, - output, - descriptor, - weights, - biases, - reason); + result = layerSupportObject.IsTransposeConvolution2dSupported(input, + output, + descriptor, + weights, + biases, + reason); break; } @@ -1226,10 +1248,10 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); - result = layerSupportObject->IsReduceSupported(OverrideDataType(input, dataType), - OverrideDataType(output, dataType), - cLayer->GetParameters(), - reason); + result = layerSupportObject.IsReduceSupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + cLayer->GetParameters(), + reason); break; } default: diff --git a/src/backends/backendsCommon/test/CMakeLists.txt b/src/backends/backendsCommon/test/CMakeLists.txt index f92e0745d3..d3857b8357 100644 --- a/src/backends/backendsCommon/test/CMakeLists.txt +++ b/src/backends/backendsCommon/test/CMakeLists.txt @@ -23,6 +23,7 @@ list(APPEND armnnBackendsCommonUnitTests_sources ElementwiseUnaryEndToEndTestImpl.hpp EndToEndTestImpl.hpp FillEndToEndTestImpl.hpp + FullyConnectedEndToEndTestImpl.hpp GatherEndToEndTestImpl.hpp InstanceNormalizationEndToEndTestImpl.cpp InstanceNormalizationEndToEndTestImpl.hpp diff --git a/src/backends/backendsCommon/test/CompatibilityTests.cpp b/src/backends/backendsCommon/test/CompatibilityTests.cpp index b69e11253d..1c4ff709fa 100644 --- a/src/backends/backendsCommon/test/CompatibilityTests.cpp +++ b/src/backends/backendsCommon/test/CompatibilityTests.cpp @@ -7,6 +7,7 @@ #include #include +#include #include @@ -115,3 +116,18 @@ BOOST_AUTO_TEST_CASE(Neon_Cl_DirectCompatibility_Test) } BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(BackendCapability) + +BOOST_AUTO_TEST_CASE(Backends_Capability_Test) +{ + auto neonBackend = std::make_unique(); + auto clBackend = std::make_unique(); + auto refBackend = std::make_unique(); + + BOOST_CHECK(!neonBackend->HasCapability(armnn::BackendCapability::NonConstWeights)); + BOOST_CHECK(!clBackend->HasCapability(armnn::BackendCapability::NonConstWeights)); + BOOST_CHECK(refBackend->HasCapability(armnn::BackendCapability::NonConstWeights)); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/backends/backendsCommon/test/FullyConnectedEndToEndTestImpl.hpp b/src/backends/backendsCommon/test/FullyConnectedEndToEndTestImpl.hpp new file mode 100644 index 0000000000..5a618c32e1 --- /dev/null +++ b/src/backends/backendsCommon/test/FullyConnectedEndToEndTestImpl.hpp @@ -0,0 +1,97 @@ +// +// Copyright © 2021 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// +#pragma once + +#include "CommonTestUtils.hpp" + +#include + +#include + +#include + +#include + +#include + +namespace +{ + +armnn::INetworkPtr CreateFullyConnectedNetworkNonConstWeights(const armnn::TensorInfo& inputTensorInfo, + const armnn::TensorInfo& outputTensorInfo, + const armnn::TensorInfo& weightsTensorInfo, + armnn::FullyConnectedDescriptor descriptor) +{ + armnn::INetworkPtr network(armnn::INetwork::Create()); + + armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input"); + armnn::IConnectableLayer* weightsInputLayer = network->AddInputLayer(1, "Weights_Input"); + armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, + armnn::EmptyOptional(), + armnn::EmptyOptional(), + "Fully_Connected"); + armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output"); + + Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0); + Connect(weightsInputLayer, fullyConnectedLayer, weightsTensorInfo, 0, 1); + Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0); + + return network; +} + +template> +void FullyConnectedWithDynamicWeightsEndToEnd(const std::vector& backends) +{ + using namespace armnn; + + armnn::TensorInfo inputTensorInfo({ 1, 1, 2, 3 }, ArmnnType); + inputTensorInfo.SetQuantizationScale(0.1f); + inputTensorInfo.SetQuantizationOffset(63); + + armnn::TensorInfo outputTensorInfo({ 1, 2 }, ArmnnType); + outputTensorInfo.SetQuantizationScale(5.f); + outputTensorInfo.SetQuantizationOffset(10); + + armnn::TensorInfo weightsTensorInfo({ 2, 6 }, ArmnnType); + weightsTensorInfo.SetQuantizationScale(0.2f); + weightsTensorInfo.SetQuantizationOffset(93); + + FullyConnectedDescriptor descriptor; + descriptor.m_ConstantWeights = false; + descriptor.m_BiasEnabled = false; + descriptor.m_TransposeWeightMatrix = true; + + std::vector inputData { + -1.2f, 6.1f, -3.5f, + 18.8f, -5.5f, 2.9f + }; + + std::vector weightsData { + -8.4f, 20.0f, -10.4f, -8, 16.4f, -11.8f, + 23.4f, 10.4f, -14.0f, -3.8f, -11.8f, 11.4f + }; + + std::vector floatExpectedOutputData { + -107.04f, 110.f + }; + std::vector expectedOutputData = armnnUtils::QuantizedVector(floatExpectedOutputData); + + armnn::INetworkPtr network = CreateFullyConnectedNetworkNonConstWeights(inputTensorInfo, + outputTensorInfo, + weightsTensorInfo, + descriptor); + + BOOST_TEST_CHECKPOINT("create a network"); + + std::map> inputTensorData = {{ 0, inputData }, {1, weightsData}}; + std::map> expectedOutputTensorData = {{ 0, expectedOutputData }}; + + EndToEndLayerTestImpl(move(network), + inputTensorData, + expectedOutputTensorData, + backends, + 1.0f); +} +} // anonymous namespace diff --git a/src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.cpp b/src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.cpp index c9e2e1602d..9176094eb2 100644 --- a/src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.cpp +++ b/src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.cpp @@ -67,12 +67,70 @@ LayerTestResult SimpleFullyConnectedTestImpl( return result; } +template +LayerTestResult SimpleFullyConnectedTestWeightsAsInputsImpl( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + armnn::TensorInfo inputTensorInfo, + armnn::TensorInfo outputTensorInfo, + armnn::TensorInfo weightsTensorInfo, + armnn::TensorInfo biasesTensorInfo, + boost::multi_array& weights, + boost::multi_array& bias, + boost::multi_array& input, + bool biasEnabled, + bool transposeWeights) +{ + std::unique_ptr input0Handle = tensorHandleFactory.CreateTensorHandle(inputTensorInfo); + std::unique_ptr input1Handle = tensorHandleFactory.CreateTensorHandle(weightsTensorInfo); + std::unique_ptr outputHandle = tensorHandleFactory.CreateTensorHandle(outputTensorInfo); + + armnn::FullyConnectedQueueDescriptor data; + armnn::WorkloadInfo info; + + AddInputToWorkload(data, info, inputTensorInfo, input0Handle.get()); + AddInputToWorkload(data, info, weightsTensorInfo, input1Handle.get()); + AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get()); + data.m_Parameters.m_BiasEnabled = biasEnabled; + data.m_Parameters.m_TransposeWeightMatrix = transposeWeights; + data.m_Parameters.m_ConstantWeights = false; + + std::unique_ptr input2Handle = nullptr; + if (biasEnabled) + { + input2Handle = tensorHandleFactory.CreateTensorHandle(biasesTensorInfo); + AddInputToWorkload(data, info, biasesTensorInfo, input2Handle.get()); + } + + std::unique_ptr workload = workloadFactory.CreateFullyConnected(data, info); + LayerTestResult result(outputTensorInfo); + + input0Handle->Allocate(); + input1Handle->Allocate(); + outputHandle->Allocate(); + CopyDataToITensorHandle(input0Handle.get(), &input[0][0][0][0]); + CopyDataToITensorHandle(input1Handle.get(), &weights[0][0]); + if (biasEnabled) + { + input2Handle->Allocate(); + CopyDataToITensorHandle(input2Handle.get(), &bias[0]); + } + + ExecuteWorkload(*workload, memoryManager); + + CopyDataFromITensorHandle(&result.output[0][0], outputHandle.get()); + + return result; +} + template LayerTestResult FullyConnectedTest( armnn::IWorkloadFactory& workloadFactory, const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, const armnn::ITensorHandleFactory& tensorHandleFactory, - bool biasEnabled) + bool biasEnabled, + bool constantWeights) { constexpr static unsigned int inputWidth = 3u; constexpr static unsigned int inputHeight = 2u; @@ -116,15 +174,36 @@ LayerTestResult FullyConnectedTest( auto bias = MakeTensor(biasesDesc, std::vector{9250, 67500}); - result = SimpleFullyConnectedTestImpl( - workloadFactory, - memoryManager, - tensorHandleFactory, - inputTensorInfo, outputTensorInfo, - weightsDesc, biasesDesc, - weights, bias, input, - biasEnabled, true - ); + if (constantWeights) + { + result = SimpleFullyConnectedTestImpl(workloadFactory, + memoryManager, + tensorHandleFactory, + inputTensorInfo, + outputTensorInfo, + weightsDesc, + biasesDesc, + weights, + bias, + input, + biasEnabled, + true); + } + else + { + result = SimpleFullyConnectedTestWeightsAsInputsImpl(workloadFactory, + memoryManager, + tensorHandleFactory, + inputTensorInfo, + outputTensorInfo, + weightsDesc, + biasesDesc, + weights, + bias, + input, + biasEnabled, + true); + } if (biasEnabled) { @@ -237,14 +316,16 @@ FullyConnectedTest( armnn::IWorkloadFactory& workloadFactory, const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, const armnn::ITensorHandleFactory& tensorHandleFactory, - bool biasEnabled); + bool biasEnabled, + bool constWeights); template LayerTestResult, 2> FullyConnectedTest( armnn::IWorkloadFactory& workloadFactory, const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, const armnn::ITensorHandleFactory& tensorHandleFactory, - bool biasEnabled); + bool biasEnabled, + bool constWeights); // // Implementation functions diff --git a/src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.hpp b/src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.hpp index c2d53a5178..ec921f7dd5 100644 --- a/src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.hpp +++ b/src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.hpp @@ -17,7 +17,8 @@ LayerTestResult FullyConnectedTest( armnn::IWorkloadFactory& workloadFactory, const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, const armnn::ITensorHandleFactory& tensorHandleFactory, - bool biasEnabled); + bool biasEnabled, + bool constantWeights); LayerTestResult FullyConnectedFloat32Test( armnn::IWorkloadFactory& workloadFactory, diff --git a/src/backends/cl/test/ClLayerTests.cpp b/src/backends/cl/test/ClLayerTests.cpp index 013965c445..2c6ebf9dc0 100644 --- a/src/backends/cl/test/ClLayerTests.cpp +++ b/src/backends/cl/test/ClLayerTests.cpp @@ -101,8 +101,8 @@ ARMNN_AUTO_TEST_CASE_WITH_THF(BatchToSpaceNdNchwUint3, BatchToSpaceNdNchwTest3, false) -ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedUint8, FullyConnectedTest, true) +ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedUint8, FullyConnectedTest, false, true) +ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedUint8, FullyConnectedTest, true, true) ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedLarge, FullyConnectedLargeTest, false) ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedLargeTransposed, FullyConnectedLargeTest, true) diff --git a/src/backends/neon/test/NeonLayerTests.cpp b/src/backends/neon/test/NeonLayerTests.cpp index 8434a67082..8cccf6f780 100644 --- a/src/backends/neon/test/NeonLayerTests.cpp +++ b/src/backends/neon/test/NeonLayerTests.cpp @@ -574,8 +574,8 @@ ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleFullyConnectedWithBias, FullyConnectedFloat3 ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleFullyConnectedWithTranspose, FullyConnectedFloat32Test, false, true) ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedLarge, FullyConnectedLargeTest, false) ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedLargeTransposed, FullyConnectedLargeTest, true) -ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedUint8, FullyConnectedTest, false) -ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedUint8, FullyConnectedTest, true) +ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedUint8, FullyConnectedTest, false, true) +ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedUint8, FullyConnectedTest, true, true) // Add ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleAdd, AdditionTest) diff --git a/src/backends/reference/RefBackend.cpp b/src/backends/reference/RefBackend.cpp index e93b317dce..53c55ab26a 100644 --- a/src/backends/reference/RefBackend.cpp +++ b/src/backends/reference/RefBackend.cpp @@ -69,6 +69,16 @@ IBackendInternal::ILayerSupportSharedPtr RefBackend::GetLayerSupport() const return layerSupport; } +bool RefBackend::HasCapability(BackendCapability capabilityClass) const +{ + auto search = backendCapabilities.find(capabilityClass); + if (search != backendCapabilities.end()) + { + return true; + } + return false; +} + OptimizationViews RefBackend::OptimizeSubgraphView(const SubgraphView& subgraph) const { OptimizationViews optimizationViews; diff --git a/src/backends/reference/RefBackend.hpp b/src/backends/reference/RefBackend.hpp index 92d392dde6..c92936ca0c 100644 --- a/src/backends/reference/RefBackend.hpp +++ b/src/backends/reference/RefBackend.hpp @@ -9,6 +9,10 @@ namespace armnn { +const std::set backendCapabilities { + armnn::BackendCapability::NonConstWeights, +}; + class RefBackend : public IBackendInternal { public: @@ -39,6 +43,8 @@ public: std::vector GetHandleFactoryPreferences() const override; void RegisterTensorHandleFactories(class TensorHandleFactoryRegistry& registry) override; + + bool HasCapability(BackendCapability capabilityClass) const override; }; } // namespace armnn diff --git a/src/backends/reference/test/RefEndToEndTests.cpp b/src/backends/reference/test/RefEndToEndTests.cpp index 4598568070..b6974811ef 100644 --- a/src/backends/reference/test/RefEndToEndTests.cpp +++ b/src/backends/reference/test/RefEndToEndTests.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -599,6 +600,11 @@ BOOST_AUTO_TEST_CASE(RefFillEndToEndTestInt32) FillEndToEnd(defaultBackends); } +BOOST_AUTO_TEST_CASE(RefFullyConnectedEndToEndTestInt32) +{ + FullyConnectedWithDynamicWeightsEndToEnd(defaultBackends); +} + BOOST_AUTO_TEST_CASE(RefGatherFloatTest) { GatherEndToEnd(defaultBackends); diff --git a/src/backends/reference/test/RefLayerTests.cpp b/src/backends/reference/test/RefLayerTests.cpp index 161476ed98..7371692d0e 100644 --- a/src/backends/reference/test/RefLayerTests.cpp +++ b/src/backends/reference/test/RefLayerTests.cpp @@ -579,16 +579,22 @@ ARMNN_AUTO_TEST_CASE_WITH_THF(HardSwishInt16, HardSwishInt16Test) // Fully Connected ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleFullyConnected, FullyConnectedFloat32Test, false, false) -ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedUint8, FullyConnectedTest, false) -ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedQSymm16, FullyConnectedTest, false) +ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedUint8, FullyConnectedTest, false, true) +ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedQSymm16, FullyConnectedTest, false, true) ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleFullyConnectedWithBias, FullyConnectedFloat32Test, true, false) -ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedUint8, FullyConnectedTest, true) -ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedQSymm16, FullyConnectedTest, true) +ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedUint8, FullyConnectedTest, true, true) +ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedQSymm16, FullyConnectedTest, true, true) ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleFullyConnectedWithTranspose, FullyConnectedFloat32Test, false, true) ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedLarge, FullyConnectedLargeTest, false) ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedLargeTransposed, FullyConnectedLargeTest, true) + +ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedWeightsAsInputsUint8, + FullyConnectedTest, + false, + false) + // Splitter ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleSplitterFloat32, SplitterFloat32Test) ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleSplitterFloat16, SplitterFloat16Test) diff --git a/src/backends/reference/workloads/RefFullyConnectedWorkload.cpp b/src/backends/reference/workloads/RefFullyConnectedWorkload.cpp index 9acca219b5..49e105f206 100644 --- a/src/backends/reference/workloads/RefFullyConnectedWorkload.cpp +++ b/src/backends/reference/workloads/RefFullyConnectedWorkload.cpp @@ -14,18 +14,21 @@ namespace armnn { RefFullyConnectedWorkload::RefFullyConnectedWorkload( const FullyConnectedQueueDescriptor& descriptor, const WorkloadInfo& info) - : BaseWorkload(descriptor, info), - m_Weight(std::make_unique(*(descriptor.m_Weight))) + : BaseWorkload(descriptor, info) { - const TensorInfo& rWeightInfo = m_Weight->GetTensorInfo(); - m_WeightShape = rWeightInfo.GetShape(); - m_WeightDecoder = MakeDecoder(rWeightInfo, m_Weight->Map(true)); - - if (descriptor.m_Parameters.m_BiasEnabled) + if (descriptor.m_Parameters.m_ConstantWeights) { - m_Bias = std::make_unique(*(descriptor.m_Bias)); - const TensorInfo& biasInfo = m_Bias->GetTensorInfo(); - m_BiasDecoder = MakeDecoder(biasInfo, m_Bias->Map(true)); + m_Weight = std::make_unique(*(descriptor.m_Weight)); + const TensorInfo& rWeightInfo = m_Weight->GetTensorInfo(); + m_WeightShape = rWeightInfo.GetShape(); + m_WeightDecoder = MakeDecoder(rWeightInfo, m_Weight->Map(true)); + + if (descriptor.m_Parameters.m_BiasEnabled) + { + m_Bias = std::make_unique(*(descriptor.m_Bias)); + const TensorInfo& biasInfo = m_Bias->GetTensorInfo(); + m_BiasDecoder = MakeDecoder(biasInfo, m_Bias->Map(true)); + } } } @@ -36,6 +39,20 @@ void RefFullyConnectedWorkload::PostAllocationConfigure() m_InputShape = inputInfo.GetShape(); m_InputDecoder = MakeDecoder(inputInfo); + if (!m_Data.m_Parameters.m_ConstantWeights) + { + const TensorInfo& rWeightInfo = GetTensorInfo(m_Data.m_Inputs[1]); + ARMNN_ASSERT(inputInfo.GetNumDimensions() > 1); + m_WeightShape = rWeightInfo.GetShape(); + m_WeightDecoder = MakeDecoder(rWeightInfo); + + if (m_Data.m_Parameters.m_BiasEnabled) + { + const TensorInfo& biasInfo = GetTensorInfo(m_Data.m_Inputs[2]); + m_BiasDecoder = MakeDecoder(biasInfo); + } + } + const TensorInfo& outputInfo = GetTensorInfo(m_Data.m_Outputs[0]); m_OutputShape = outputInfo.GetShape(); m_OutputEncoder = MakeEncoder(outputInfo); @@ -52,6 +69,14 @@ void RefFullyConnectedWorkload::Execute() const ARMNN_SCOPED_PROFILING_EVENT(Compute::CpuRef, "RefFullyConnectedWorkload_Execute"); m_InputDecoder->Reset(m_Data.m_Inputs[0]->Map()); + if (!m_Data.m_Parameters.m_ConstantWeights) + { + m_WeightDecoder->Reset(m_Data.m_Inputs[1]->Map()); + if (m_Data.m_Parameters.m_BiasEnabled) + { + m_BiasDecoder->Reset(m_Data.m_Inputs[2]->Map()); + } + } m_OutputEncoder->Reset(m_Data.m_Outputs[0]->Map()); FullyConnected(m_InputShape, -- cgit v1.2.1