aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSadik Armagan <sadik.armagan@arm.com>2021-03-25 07:46:55 +0000
committerSadik Armagan <sadik.armagan@arm.com>2021-03-25 07:46:55 +0000
commitf0a6dec75832604d5ab18242dc216852821a8279 (patch)
treeff25e64c62c63975a54abd16a8bff744be70d7c0
parent16fb1a2d9c1d3d80c0f0b6ab549919fbabd2a0b9 (diff)
downloadarmnn-f0a6dec75832604d5ab18242dc216852821a8279.tar.gz
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 <sadik.armagan@arm.com> Change-Id: Iffa5b9aa9297aca4c02d923cce4636c88ac21faa
-rw-r--r--delegate/include/Version.hpp2
-rw-r--r--delegate/src/FullyConnected.hpp109
-rw-r--r--delegate/src/test/FullyConnectedTest.cpp33
-rw-r--r--delegate/src/test/FullyConnectedTestHelper.hpp101
-rw-r--r--include/armnn/BackendHelper.hpp10
-rw-r--r--include/armnn/Descriptors.hpp10
-rw-r--r--include/armnn/INetwork.hpp11
-rw-r--r--include/armnn/Types.hpp10
-rw-r--r--include/armnn/Version.hpp2
-rw-r--r--include/armnn/backends/IBackendInternal.hpp3
-rw-r--r--include/armnnCaffeParser/Version.hpp2
-rw-r--r--include/armnnOnnxParser/Version.hpp2
-rw-r--r--include/armnnTfLiteParser/Version.hpp2
-rw-r--r--include/armnnTfParser/Version.hpp2
-rw-r--r--python/pyarmnn/README.md14
-rw-r--r--python/pyarmnn/examples/image_classification/README.md2
-rw-r--r--python/pyarmnn/examples/object_detection/README.md2
-rw-r--r--python/pyarmnn/examples/speech_recognition/README.md2
-rw-r--r--python/pyarmnn/src/pyarmnn/_version.py4
-rw-r--r--python/pyarmnn/test/test_setup.py8
-rw-r--r--python/pyarmnn/test/test_version.py4
-rw-r--r--samples/ObjectDetection/Readme.md6
-rw-r--r--src/armnn/BackendHelper.cpp26
-rw-r--r--src/armnn/Descriptors.cpp17
-rw-r--r--src/armnn/Network.cpp69
-rw-r--r--src/armnn/Network.hpp7
-rw-r--r--src/armnn/layers/FullyConnectedLayer.cpp82
-rw-r--r--src/armnn/test/OptimizerTests.cpp8
-rw-r--r--src/armnn/test/UtilsTests.cpp12
-rw-r--r--src/armnnDeserializer/Deserializer.cpp37
-rw-r--r--src/armnnSerializer/ArmnnSchema.fbs1
-rw-r--r--src/armnnSerializer/ArmnnSchema_generated.h14
-rw-r--r--src/armnnSerializer/Serializer.cpp24
-rw-r--r--src/armnnSerializer/test/SerializerTests.cpp48
-rw-r--r--src/backends/backendsCommon/WorkloadData.cpp38
-rw-r--r--src/backends/backendsCommon/WorkloadFactory.cpp408
-rw-r--r--src/backends/backendsCommon/test/CMakeLists.txt1
-rw-r--r--src/backends/backendsCommon/test/CompatibilityTests.cpp16
-rw-r--r--src/backends/backendsCommon/test/FullyConnectedEndToEndTestImpl.hpp97
-rw-r--r--src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.cpp105
-rw-r--r--src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.hpp3
-rw-r--r--src/backends/cl/test/ClLayerTests.cpp4
-rw-r--r--src/backends/neon/test/NeonLayerTests.cpp4
-rw-r--r--src/backends/reference/RefBackend.cpp10
-rw-r--r--src/backends/reference/RefBackend.hpp6
-rw-r--r--src/backends/reference/test/RefEndToEndTests.cpp6
-rw-r--r--src/backends/reference/test/RefLayerTests.cpp14
-rw-r--r--src/backends/reference/workloads/RefFullyConnectedWorkload.cpp45
48 files changed, 994 insertions, 449 deletions
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<int32_t>(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::PermutationVector&>());
-
- armnn::IConnectableLayer* layer = nullptr;
- if (biasEnabled)
+ armnn::Optional<armnn::ConstTensor> optionalWeights = armnn::EmptyOptional();
+ armnn::Optional<armnn::ConstTensor> optionalBiases = armnn::EmptyOptional();
+ if(descriptor.m_ConstantWeights)
{
- const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
- auto biasTensor = CreateConstTensor(&tfLiteBiasTensor,
- biasTensorInfo,
- armnn::Optional<armnn::PermutationVector&>());
- layer = delegateData.m_Network->AddFullyConnectedLayer(descriptor,
- weightsTensor,
- armnn::Optional<armnn::ConstTensor>(biasTensor));
- }
- else
- {
- layer = delegateData.m_Network->AddFullyConnectedLayer(descriptor,
- weightsTensor,
- armnn::EmptyOptional());
+ auto weightsTensor = CreateConstTensor(&tfLiteWeightsTensor,
+ weightsTensorInfo,
+ armnn::Optional<armnn::PermutationVector&>());
+ optionalWeights = armnn::Optional<armnn::ConstTensor>(weightsTensor);
+
+ if (biasEnabled)
+ {
+ const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
+ auto biasTensor = CreateConstTensor(&tfLiteBiasTensor,
+ biasTensorInfo,
+ armnn::Optional<armnn::PermutationVector&>());
+ optionalBiases = armnn::Optional<armnn::ConstTensor>(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<armnn::BackendId>& backends)
+void FullyConnectedFp32Test(std::vector<armnn::BackendId>& backends, bool constantWeights = true)
{
std::vector<int32_t> inputTensorShape { 1, 4, 1, 1 };
std::vector<int32_t> weightsTensorShape { 1, 4 };
@@ -30,10 +30,11 @@ void FullyConnectedFp32Test(std::vector<armnn::BackendId>& backends)
outputTensorShape,
inputValues,
expectedOutputValues,
- weightsData);
+ weightsData,
+ constantWeights);
}
-void FullyConnectedActicationTest(std::vector<armnn::BackendId>& backends)
+void FullyConnectedActicationTest(std::vector<armnn::BackendId>& backends, bool constantWeights = true)
{
std::vector<int32_t> inputTensorShape { 1, 4, 1, 1 };
std::vector<int32_t> weightsTensorShape { 1, 4 };
@@ -55,10 +56,11 @@ void FullyConnectedActicationTest(std::vector<armnn::BackendId>& backends)
outputTensorShape,
inputValues,
expectedOutputValues,
- weightsData);
+ weightsData,
+ constantWeights);
}
-void FullyConnectedInt8Test(std::vector<armnn::BackendId>& backends)
+void FullyConnectedInt8Test(std::vector<armnn::BackendId>& backends, bool constantWeights = true)
{
std::vector<int32_t> inputTensorShape { 1, 4, 2, 1 };
std::vector<int32_t> weightsTensorShape { 1, 4 };
@@ -82,7 +84,8 @@ void FullyConnectedInt8Test(std::vector<armnn::BackendId>& 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<armnn::BackendId> backends = { armnn::Compute::CpuRef };
+ FullyConnectedFp32Test(backends, false);
+}
+
+TEST_CASE ("FullyConnected_Weights_As_Inputs_Int8_CpuRef_Test")
+{
+ std::vector<armnn::BackendId> backends = { armnn::Compute::CpuRef };
+ FullyConnectedInt8Test(backends, false);
+}
+
+TEST_CASE ("FullyConnected_Weights_As_Inputs_Activation_CpuRef_Test")
+{
+ std::vector<armnn::BackendId> 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 <armnn_delegate.hpp>
#include <flatbuffers/flatbuffers.h>
@@ -25,8 +27,9 @@ std::vector<char> CreateFullyConnectedTfLiteModel(tflite::TensorType tensorType,
const std::vector <int32_t>& inputTensorShape,
const std::vector <int32_t>& weightsTensorShape,
const std::vector <int32_t>& biasTensorShape,
- const std::vector <int32_t>& outputTensorShape,
- const std::vector <T>& weightsData,
+ std::vector <int32_t>& outputTensorShape,
+ std::vector <T>& weightsData,
+ bool constantWeights = true,
float quantScale = 1.0f,
int quantOffset = 0,
float outputQuantScale = 2.0f,
@@ -36,26 +39,38 @@ std::vector<char> CreateFullyConnectedTfLiteModel(tflite::TensorType tensorType,
flatbuffers::FlatBufferBuilder flatBufferBuilder;
std::array<flatbuffers::Offset<tflite::Buffer>, 3> buffers;
buffers[0] = CreateBuffer(flatBufferBuilder, flatBufferBuilder.CreateVector({}));
- buffers[1] = CreateBuffer(flatBufferBuilder,
- flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(weightsData.data()),
- sizeof(T) * weightsData.size()));
auto biasTensorType = ::tflite::TensorType_FLOAT32;
if (tensorType == ::tflite::TensorType_INT8)
{
biasTensorType = ::tflite::TensorType_INT32;
- std::vector<int32_t> biasData = { 10 };
- buffers[2] = CreateBuffer(flatBufferBuilder,
- flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(biasData.data()),
- sizeof(int32_t) * biasData.size()));
+ }
+ if (constantWeights)
+ {
+ buffers[1] = CreateBuffer(flatBufferBuilder,
+ flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(weightsData.data()),
+ sizeof(T) * weightsData.size()));
+ if (tensorType == ::tflite::TensorType_INT8)
+ {
+ std::vector<int32_t> biasData = { 10 };
+ buffers[2] = CreateBuffer(flatBufferBuilder,
+ flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(biasData.data()),
+ sizeof(int32_t) * biasData.size()));
+
+ }
+ else
+ {
+ std::vector<float> biasData = { 10 };
+ buffers[2] = CreateBuffer(flatBufferBuilder,
+ flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(biasData.data()),
+ sizeof(float) * biasData.size()));
+ }
}
else
{
- std::vector<float> biasData = { 10 };
- buffers[2] = CreateBuffer(flatBufferBuilder,
- flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(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<armnn::BackendId>& backends,
const std::vector <int32_t>& inputTensorShape,
const std::vector <int32_t>& weightsTensorShape,
const std::vector <int32_t>& biasTensorShape,
- const std::vector <int32_t>& outputTensorShape,
- const std::vector <T>& inputValues,
- const std::vector <T>& expectedOutputValues,
- const std::vector <T>& weightsData,
+ std::vector <int32_t>& outputTensorShape,
+ std::vector <T>& inputValues,
+ std::vector <T>& expectedOutputValues,
+ std::vector <T>& weightsData,
+ bool constantWeights = true,
float quantScale = 1.0f,
int quantOffset = 0)
{
@@ -171,10 +187,11 @@ void FullyConnectedTest(std::vector<armnn::BackendId>& backends,
biasTensorShape,
outputTensorShape,
weightsData,
+ constantWeights,
quantScale,
quantOffset);
-
const Model* tfLiteModel = GetModel(modelBuffer.data());
+
// Create TfLite Interpreters
std::unique_ptr<Interpreter> armnnDelegateInterpreter;
CHECK(InterpreterBuilder(tfLiteModel, ::tflite::ops::builtin::BuiltinOpResolver())
@@ -191,25 +208,34 @@ void FullyConnectedTest(std::vector<armnn::BackendId>& backends,
// Create the ArmNN Delegate
armnnDelegate::DelegateOptions delegateOptions(backends);
std::unique_ptr<TfLiteDelegate, decltype(&armnnDelegate::TfLiteArmnnDelegateDelete)>
- 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<T>(tfLiteDelegateInputId);
- for (unsigned int i = 0; i < inputValues.size(); ++i)
- {
- tfLiteDelageInputData[i] = inputValues[i];
- }
+ armnnDelegate::FillInput<T>(tfLiteInterpreter, 0, inputValues);
+ armnnDelegate::FillInput<T>(armnnDelegateInterpreter, 0, inputValues);
- auto armnnDelegateInputId = armnnDelegateInterpreter->inputs()[0];
- auto armnnDelegateInputData = armnnDelegateInterpreter->typed_tensor<T>(armnnDelegateInputId);
- for (unsigned int i = 0; i < inputValues.size(); ++i)
+ if (!constantWeights)
{
- armnnDelegateInputData[i] = inputValues[i];
+ armnnDelegate::FillInput<T>(tfLiteInterpreter, 1, weightsData);
+ armnnDelegate::FillInput<T>(armnnDelegateInterpreter, 1, weightsData);
+
+ if (tensorType == ::tflite::TensorType_INT8)
+ {
+ std::vector <int32_t> biasData = {10};
+ armnnDelegate::FillInput<int32_t>(tfLiteInterpreter, 2, biasData);
+ armnnDelegate::FillInput<int32_t>(armnnDelegateInterpreter, 2, biasData);
+ }
+ else
+ {
+ std::vector<float> biasData = {10};
+ armnnDelegate::FillInput<float>(tfLiteInterpreter, 2, biasData);
+ armnnDelegate::FillInput<float>(armnnDelegateInterpreter, 2, biasData);
+ }
}
// Run EnqueWorkload
@@ -217,16 +243,11 @@ void FullyConnectedTest(std::vector<armnn::BackendId>& backends,
CHECK(armnnDelegateInterpreter->Invoke() == kTfLiteOk);
// Compare output data
- auto tfLiteDelegateOutputId = tfLiteInterpreter->outputs()[0];
- auto tfLiteDelageOutputData = tfLiteInterpreter->typed_tensor<T>(tfLiteDelegateOutputId);
- auto armnnDelegateOutputId = armnnDelegateInterpreter->outputs()[0];
- auto armnnDelegateOutputData = armnnDelegateInterpreter->typed_tensor<T>(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<T>(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 <armnn/BackendId.hpp>
#include <armnn/ILayerSupport.hpp>
+#include <armnn/Types.hpp>
namespace armnn
{
@@ -19,7 +20,10 @@ class LayerSupportHandle
{
public:
explicit LayerSupportHandle(std::shared_ptr<ILayerSupport> layerSupport)
- : m_LayerSupport(std::move(layerSupport)) {};
+ : m_LayerSupport(std::move(layerSupport)), m_BackendId(Compute::Undefined) {};
+
+ explicit LayerSupportHandle(std::shared_ptr<ILayerSupport> layerSupport, const BackendId& backendId)
+ : m_LayerSupport(std::move(layerSupport)), m_BackendId(backendId) {};
bool IsBackendRegistered() const;
@@ -422,9 +426,13 @@ public:
private:
std::shared_ptr<ILayerSupport> 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
@@ -299,6 +299,17 @@ public:
/// 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<ConstTensor>& weights,
+ const Optional<ConstTensor>& 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.
/// @param biases - Optional tensor for the bias data.
/// @param name - Optional name for the layer.
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<IBackend>;
using IBackendUniquePtr = std::unique_ptr<IBackend, void(*)(IBackend* backend)>;
+/// 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<IBackendInternal>;
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<std::string&> 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<ConstTensor>& biases,
const char* name)
{
- return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor, weights, biases, name);
+ return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor,
+ armnn::Optional<ConstTensor>(weights),
+ biases,
+ name);
}
IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
const ConstTensor& weights,
const char* name)
{
- Optional<ConstTensor> biases;
- return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor, weights, biases, name);
+ armnn::Optional<ConstTensor> biases;
+ return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor,
+ armnn::Optional<ConstTensor>(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<ConstTensor>(biases), name);
+ return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor,
+ armnn::Optional<ConstTensor>(weights),
+ armnn::Optional<ConstTensor>(biases),
+ name);
+}
+
+IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
+ const Optional<ConstTensor>& weights,
+ const Optional<ConstTensor>& 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<ConstTensor>& biases,
- const char* name)
+ const Optional<ConstTensor>& weights,
+ const Optional<ConstTensor>& 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<FullyConnectedLayer>(fullyConnectedDescriptor, name);
- layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
-
- if (fullyConnectedDescriptor.m_BiasEnabled)
+ if (fullyConnectedDescriptor.m_ConstantWeights)
{
- layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
+ layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights.value());
+ if (fullyConnectedDescriptor.m_BiasEnabled)
+ {
+ layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
+ }
}
return layer;
}
IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
+ const Optional<ConstTensor>& weights,
+ const Optional<ConstTensor>& biases,
+ const char* name)
+{
+ return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
+}
+
+IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
const ConstTensor& weights,
const Optional<ConstTensor>& biases,
const char* name)
{
- return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
+ Optional<ConstTensor> optionalWeights(weights);
+ return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, optionalWeights, biases, name);
}
IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
const ConstTensor& weights,
const char* name)
{
+ Optional<ConstTensor> optionalWeights(weights);
Optional<ConstTensor> 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<ConstTensor> optionalWeights(weights);
Optional<ConstTensor> 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
@@ -104,6 +104,11 @@ public:
const char* name = nullptr);
IConnectableLayer* AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
+ const Optional<ConstTensor>& weights,
+ const Optional<ConstTensor>& biases,
+ const char* name = nullptr);
+
+ IConnectableLayer* AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
const ConstTensor& weights,
const Optional<ConstTensor>& biases,
const char* name = nullptr);
@@ -265,7 +270,7 @@ public:
private:
IConnectableLayer* AddFullyConnectedLayerImpl(const FullyConnectedDescriptor& fullyConnectedDescriptor,
- const ConstTensor& weights,
+ const Optional<ConstTensor>& weights,
const Optional<ConstTensor>& 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<IWorkload> 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<IWorkload> FullyConnectedLayer::CreateWorkload(const IWorkloadFa
FullyConnectedLayer* FullyConnectedLayer::Clone(Graph& graph) const
{
auto layer = CloneBase<FullyConnectedLayer>(graph, m_Param, GetName());
-
- layer->m_Weight = m_Weight ? std::make_unique<ScopedCpuTensorHandle>(*m_Weight) : nullptr;
- if (layer->m_Param.m_BiasEnabled)
+ if (m_Param.m_ConstantWeights)
{
- layer->m_Bias = m_Bias ? std::make_unique<ScopedCpuTensorHandle>(*m_Bias) : nullptr;
- }
+ layer->m_Weight = m_Weight ? std::make_unique<ScopedCpuTensorHandle>(*m_Weight) : nullptr;
+ if (layer->m_Param.m_BiasEnabled)
+ {
+ layer->m_Bias = m_Bias ? std::make_unique<ScopedCpuTensorHandle>(*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<TensorShape> 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<ConstTensor> optionalWeightsTensor = EmptyOptional();
Optional<ConstTensor> optionalBiasTensor = EmptyOptional();
-
- if (GetParameters().m_BiasEnabled)
+ if(GetParameters().m_ConstantWeights)
{
- ConstTensor biasTensor(m_Bias->GetTensorInfo(), m_Bias->GetConstTensor<void>());
- optionalBiasTensor = Optional<ConstTensor>(biasTensor);
+ ConstTensor weightsTensor(m_Weight->GetTensorInfo(), m_Weight->GetConstTensor<void>());
+ optionalWeightsTensor = Optional<ConstTensor>(weightsTensor);
+
+ if (GetParameters().m_BiasEnabled)
+ {
+ ConstTensor biasTensor(m_Bias->GetTensorInfo(), m_Bias->GetConstTensor<void>());
+ optionalBiasTensor = Optional<ConstTensor>(biasTensor);
+ }
}
-
- visitor.VisitFullyConnectedLayer(this, GetParameters(), weightsTensor, optionalBiasTensor, GetName());
+ visitor.VisitFullyConnectedLayer(this,
+ GetParameters(),
+ optionalWeightsTensor.value(),
+ optionalBiasTensor,
+ GetName());
}
void FullyConnectedLayer::ExecuteStrategy(IStrategy& strategy) const
{
- std::vector<armnn::ConstTensor> constTensors { {m_Weight->GetTensorInfo(), m_Weight->Map(true)} };
-
- if (GetParameters().m_BiasEnabled)
+ std::vector <armnn::ConstTensor> 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 <Network.hpp>
#include <Optimizer.hpp>
+#include <armnn/BackendHelper.hpp>
#include <armnn/BackendRegistry.hpp>
#include <armnn/INetwork.hpp>
#include <armnn/LayerVisitorBase.hpp>
@@ -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<VisitorNoThrowPolicy>
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 <armnn::ConstTensor> optionalWeights = armnn::EmptyOptional();
armnn::Optional<armnn::ConstTensor> optionalBiases = armnn::EmptyOptional();
- if (flatBufferDescriptor->biasEnabled())
+ if (fullyConnectedDescriptor.m_ConstantWeights)
{
- armnn::ConstTensor biasTensorData = ToConstTensor(flatBufferLayer->biases());
- optionalBiases = armnn::Optional<armnn::ConstTensor>(biasTensorData);
+ armnn::ConstTensor weightsTensorData = ToConstTensor(flatBufferLayer->weights());
+ optionalWeights = armnn::Optional<armnn::ConstTensor>(weightsTensorData);
+
+ if (flatBufferDescriptor->biasEnabled())
+ {
+ armnn::ConstTensor biasTensorData = ToConstTensor(flatBufferLayer->biases());
+ optionalBiases = armnn::Optional<armnn::ConstTensor>(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<uint8_t>(VT_BIASENABLED, 0) != 0;
@@ -3512,10 +3513,14 @@ struct FullyConnectedDescriptor FLATBUFFERS_FINAL_CLASS : private flatbuffers::T
bool transposeWeightsMatrix() const {
return GetField<uint8_t>(VT_TRANSPOSEWEIGHTSMATRIX, 0) != 0;
}
+ bool constantWeights() const {
+ return GetField<uint8_t>(VT_CONSTANTWEIGHTS, 1) != 0;
+ }
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<uint8_t>(verifier, VT_BIASENABLED) &&
VerifyField<uint8_t>(verifier, VT_TRANSPOSEWEIGHTSMATRIX) &&
+ VerifyField<uint8_t>(verifier, VT_CONSTANTWEIGHTS) &&
verifier.EndTable();
}
};
@@ -3530,6 +3535,9 @@ struct FullyConnectedDescriptorBuilder {
void add_transposeWeightsMatrix(bool transposeWeightsMatrix) {
fbb_.AddElement<uint8_t>(FullyConnectedDescriptor::VT_TRANSPOSEWEIGHTSMATRIX, static_cast<uint8_t>(transposeWeightsMatrix), 0);
}
+ void add_constantWeights(bool constantWeights) {
+ fbb_.AddElement<uint8_t>(FullyConnectedDescriptor::VT_CONSTANTWEIGHTS, static_cast<uint8_t>(constantWeights), 1);
+ }
explicit FullyConnectedDescriptorBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
@@ -3545,8 +3553,10 @@ struct FullyConnectedDescriptorBuilder {
inline flatbuffers::Offset<FullyConnectedDescriptor> 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<armnn::ConstTensor>& 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<serializer::ConstTensor> flatBufferWeights;
// Create FlatBuffer bias data
flatbuffers::Offset<serializer::ConstTensor> 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<armnn::ConstTensor> weights = armnn::EmptyOptional();
+ armnn::Optional<armnn::ConstTensor> 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<armnn::ConstTensor> constants {};
+ LayerVerifierBaseWithDescriptorAndConstants<armnn::FullyConnectedDescriptor> 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 <armnn/Types.hpp>
#include <armnn/LayerSupport.hpp>
#include <armnn/ILayerSupport.hpp>
+#include <armnn/BackendHelper.hpp>
#include <armnn/BackendRegistry.hpp>
#include <armnn/utility/PolymorphicDowncast.hpp>
#include <armnn/utility/TransformIterator.hpp>
@@ -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<const ActivationLayer*>(&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<const BatchToSpaceNdLayer*>(&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<const FakeQuantizationLayer*>(&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<const FullyConnectedLayer*>(&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<const GatherLayer*>(&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<const NormalizationLayer*>(&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<const PermuteLayer*>(&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<const PadLayer*>(&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<const Pooling2dLayer*>(&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<const PreCompiledLayer*>(&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<const ReshapeLayer*>(&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<const ResizeLayer*>(&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<const SoftmaxLayer*>(&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<const SpaceToBatchNdLayer*>(&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<std::reference_wrapper<TensorInfo>> 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<const TensorInfo*> 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<const StridedSliceLayer*>(&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<const MeanLayer*>(&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<const TransposeLayer*>(&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 <cl/ClBackend.hpp>
#include <neon/NeonBackend.hpp>
+#include <reference/RefBackend.hpp>
#include <Network.hpp>
@@ -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<NeonBackend>();
+ auto clBackend = std::make_unique<ClBackend>();
+ auto refBackend = std::make_unique<RefBackend>();
+
+ 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 <ResolveType.hpp>
+
+#include <armnn/INetwork.hpp>
+
+#include <armnn/utility/NumericCast.hpp>
+
+#include <boost/test/unit_test.hpp>
+
+#include <vector>
+
+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<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
+void FullyConnectedWithDynamicWeightsEndToEnd(const std::vector<armnn::BackendId>& 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<T> inputData {
+ -1.2f, 6.1f, -3.5f,
+ 18.8f, -5.5f, 2.9f
+ };
+
+ std::vector<T> 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<T> floatExpectedOutputData {
+ -107.04f, 110.f
+ };
+ std::vector<T> expectedOutputData = armnnUtils::QuantizedVector<T>(floatExpectedOutputData);
+
+ armnn::INetworkPtr network = CreateFullyConnectedNetworkNonConstWeights(inputTensorInfo,
+ outputTensorInfo,
+ weightsTensorInfo,
+ descriptor);
+
+ BOOST_TEST_CHECKPOINT("create a network");
+
+ std::map<int, std::vector<T>> inputTensorData = {{ 0, inputData }, {1, weightsData}};
+ std::map<int, std::vector<T>> expectedOutputTensorData = {{ 0, expectedOutputData }};
+
+ EndToEndLayerTestImpl<ArmnnType, ArmnnType>(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<T, 2> SimpleFullyConnectedTestImpl(
return result;
}
+template<typename T, typename B>
+LayerTestResult<T, 2> 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<T, 2>& weights,
+ boost::multi_array<B, 1>& bias,
+ boost::multi_array<T, 4>& input,
+ bool biasEnabled,
+ bool transposeWeights)
+{
+ std::unique_ptr<armnn::ITensorHandle> input0Handle = tensorHandleFactory.CreateTensorHandle(inputTensorInfo);
+ std::unique_ptr<armnn::ITensorHandle> input1Handle = tensorHandleFactory.CreateTensorHandle(weightsTensorInfo);
+ std::unique_ptr<armnn::ITensorHandle> 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<armnn::ITensorHandle> input2Handle = nullptr;
+ if (biasEnabled)
+ {
+ input2Handle = tensorHandleFactory.CreateTensorHandle(biasesTensorInfo);
+ AddInputToWorkload(data, info, biasesTensorInfo, input2Handle.get());
+ }
+
+ std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateFullyConnected(data, info);
+ LayerTestResult<T, 2> 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<armnn::DataType ArmnnType, typename T>
LayerTestResult<T, 2> 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<T, 2> FullyConnectedTest(
auto bias = MakeTensor<int32_t, 1>(biasesDesc, std::vector<int32_t>{9250, 67500});
- result = SimpleFullyConnectedTestImpl<T>(
- workloadFactory,
- memoryManager,
- tensorHandleFactory,
- inputTensorInfo, outputTensorInfo,
- weightsDesc, biasesDesc,
- weights, bias, input,
- biasEnabled, true
- );
+ if (constantWeights)
+ {
+ result = SimpleFullyConnectedTestImpl<T>(workloadFactory,
+ memoryManager,
+ tensorHandleFactory,
+ inputTensorInfo,
+ outputTensorInfo,
+ weightsDesc,
+ biasesDesc,
+ weights,
+ bias,
+ input,
+ biasEnabled,
+ true);
+ }
+ else
+ {
+ result = SimpleFullyConnectedTestWeightsAsInputsImpl<T>(workloadFactory,
+ memoryManager,
+ tensorHandleFactory,
+ inputTensorInfo,
+ outputTensorInfo,
+ weightsDesc,
+ biasesDesc,
+ weights,
+ bias,
+ input,
+ biasEnabled,
+ true);
+ }
if (biasEnabled)
{
@@ -237,14 +316,16 @@ FullyConnectedTest<armnn::DataType::QAsymmU8>(
armnn::IWorkloadFactory& workloadFactory,
const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
const armnn::ITensorHandleFactory& tensorHandleFactory,
- bool biasEnabled);
+ bool biasEnabled,
+ bool constWeights);
template LayerTestResult<armnn::ResolveType<armnn::DataType::QSymmS16>, 2>
FullyConnectedTest<armnn::DataType::QSymmS16>(
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<T, 2> FullyConnectedTest(
armnn::IWorkloadFactory& workloadFactory,
const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
const armnn::ITensorHandleFactory& tensorHandleFactory,
- bool biasEnabled);
+ bool biasEnabled,
+ bool constantWeights);
LayerTestResult<float, 2> 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<D
ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleFullyConnected, FullyConnectedFloat32Test, false, false)
ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleFullyConnectedWithBias, FullyConnectedFloat32Test, true, false)
ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleFullyConnectedWithTranspose, FullyConnectedFloat32Test, false, true)
-ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedUint8, FullyConnectedTest<DataType::QAsymmU8>, false)
-ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedUint8, FullyConnectedTest<DataType::QAsymmU8>, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedUint8, FullyConnectedTest<DataType::QAsymmU8>, false, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedUint8, FullyConnectedTest<DataType::QAsymmU8>, 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<DataType::QAsymmU8>, false)
-ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedUint8, FullyConnectedTest<DataType::QAsymmU8>, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedUint8, FullyConnectedTest<DataType::QAsymmU8>, false, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedUint8, FullyConnectedTest<DataType::QAsymmU8>, 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<armnn::BackendCapability> backendCapabilities {
+ armnn::BackendCapability::NonConstWeights,
+};
+
class RefBackend : public IBackendInternal
{
public:
@@ -39,6 +43,8 @@ public:
std::vector<ITensorHandleFactory::FactoryId> 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 <backendsCommon/test/DetectionPostProcessEndToEndTestImpl.hpp>
#include <backendsCommon/test/ElementwiseUnaryEndToEndTestImpl.hpp>
#include <backendsCommon/test/FillEndToEndTestImpl.hpp>
+#include <backendsCommon/test/FullyConnectedEndToEndTestImpl.hpp>
#include <backendsCommon/test/GatherEndToEndTestImpl.hpp>
#include <backendsCommon/test/InstanceNormalizationEndToEndTestImpl.hpp>
#include <backendsCommon/test/LogSoftmaxEndToEndTestImpl.hpp>
@@ -599,6 +600,11 @@ BOOST_AUTO_TEST_CASE(RefFillEndToEndTestInt32)
FillEndToEnd<armnn::DataType::Signed32>(defaultBackends);
}
+BOOST_AUTO_TEST_CASE(RefFullyConnectedEndToEndTestInt32)
+{
+ FullyConnectedWithDynamicWeightsEndToEnd<armnn::DataType::Float32>(defaultBackends);
+}
+
BOOST_AUTO_TEST_CASE(RefGatherFloatTest)
{
GatherEndToEnd<armnn::DataType::Float32>(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<DataType::QAsymmU8>, false)
-ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedQSymm16, FullyConnectedTest<DataType::QSymmS16>, false)
+ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedUint8, FullyConnectedTest<DataType::QAsymmU8>, false, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedQSymm16, FullyConnectedTest<DataType::QSymmS16>, false, true)
ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleFullyConnectedWithBias, FullyConnectedFloat32Test, true, false)
-ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedUint8, FullyConnectedTest<DataType::QAsymmU8>, true)
-ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedQSymm16, FullyConnectedTest<DataType::QSymmS16>, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedUint8, FullyConnectedTest<DataType::QAsymmU8>, true, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(FullyConnectedBiasedQSymm16, FullyConnectedTest<DataType::QSymmS16>, 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<DataType::QAsymmU8>,
+ 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<FullyConnectedQueueDescriptor>(descriptor, info),
- m_Weight(std::make_unique<ScopedCpuTensorHandle>(*(descriptor.m_Weight)))
+ : BaseWorkload<FullyConnectedQueueDescriptor>(descriptor, info)
{
- const TensorInfo& rWeightInfo = m_Weight->GetTensorInfo();
- m_WeightShape = rWeightInfo.GetShape();
- m_WeightDecoder = MakeDecoder<float>(rWeightInfo, m_Weight->Map(true));
-
- if (descriptor.m_Parameters.m_BiasEnabled)
+ if (descriptor.m_Parameters.m_ConstantWeights)
{
- m_Bias = std::make_unique<ScopedCpuTensorHandle>(*(descriptor.m_Bias));
- const TensorInfo& biasInfo = m_Bias->GetTensorInfo();
- m_BiasDecoder = MakeDecoder<float>(biasInfo, m_Bias->Map(true));
+ m_Weight = std::make_unique<ScopedCpuTensorHandle>(*(descriptor.m_Weight));
+ const TensorInfo& rWeightInfo = m_Weight->GetTensorInfo();
+ m_WeightShape = rWeightInfo.GetShape();
+ m_WeightDecoder = MakeDecoder<float>(rWeightInfo, m_Weight->Map(true));
+
+ if (descriptor.m_Parameters.m_BiasEnabled)
+ {
+ m_Bias = std::make_unique<ScopedCpuTensorHandle>(*(descriptor.m_Bias));
+ const TensorInfo& biasInfo = m_Bias->GetTensorInfo();
+ m_BiasDecoder = MakeDecoder<float>(biasInfo, m_Bias->Map(true));
+ }
}
}
@@ -36,6 +39,20 @@ void RefFullyConnectedWorkload::PostAllocationConfigure()
m_InputShape = inputInfo.GetShape();
m_InputDecoder = MakeDecoder<float>(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<float>(rWeightInfo);
+
+ if (m_Data.m_Parameters.m_BiasEnabled)
+ {
+ const TensorInfo& biasInfo = GetTensorInfo(m_Data.m_Inputs[2]);
+ m_BiasDecoder = MakeDecoder<float>(biasInfo);
+ }
+ }
+
const TensorInfo& outputInfo = GetTensorInfo(m_Data.m_Outputs[0]);
m_OutputShape = outputInfo.GetShape();
m_OutputEncoder = MakeEncoder<float>(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,