IVGCVSW-6124 ConstTensorsAsInput: Conv2d - FrontEnd

 * Update Front-end and Tools.
 * Updated Serializer, Deserializer and unit tests to reflect this.
 * Updated TfLiteDelegate, TfLiteParser and OnnxParser.
 * Updated Ref.
 * Fixed resulting Neon / CL tests
 * Unified optimizers for conv2d ops
 * Optimizer Fix - Fp32ToBf16
 * Partial implementation for ACL backends to fix VTS failures

!android-nn-driver:7477

Signed-off-by: Keith Davis <keith.davis@arm.com>
Change-Id: I5fb18877f7ee32643e15a9818945356274bb401b
diff --git a/src/backends/aclCommon/ArmComputeSubgraphUtils.hpp b/src/backends/aclCommon/ArmComputeSubgraphUtils.hpp
index de3a34e..a26442c 100644
--- a/src/backends/aclCommon/ArmComputeSubgraphUtils.hpp
+++ b/src/backends/aclCommon/ArmComputeSubgraphUtils.hpp
@@ -225,28 +225,14 @@
                                   ActivationDescriptor& activationDesc,
                                   std::string name)
 {
-    std::shared_ptr<ConstTensorHandle> weightHandle = baseLayer->m_Weight;
-    TensorInfo weightInfo = weightHandle->GetTensorInfo();
+    IConnectableLayer* replacement = optimizationViews.GetINetwork()
+                                                      ->AddConvolution2dLayer(baseLayer->GetParameters(), name.c_str());
 
-    std::shared_ptr<ConstTensorHandle> biasHandle = baseLayer->m_Bias;
-    ConstTensor biasTensor;
-    if (!biasHandle)
-    {
-        biasTensor = ConstTensor();
-    }
-    else
-    {
-        biasTensor = ConstTensor(biasHandle->GetTensorInfo(), biasHandle->Map(true));
-    }
-
-    IConnectableLayer* replacement =
-        optimizationViews.GetINetwork()->
-            AddConvolution2dLayer(baseLayer->GetParameters(),
-                                  ConstTensor(weightInfo, weightHandle->Map(true)),
-                                  Optional<ConstTensor>(biasTensor),
-                                  name.c_str());
     LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
 
+    replacementLayer->m_Weight = std::move(baseLayer->m_Weight);
+    replacementLayer->m_Bias = std::move(baseLayer->m_Bias);
+
     FuseLayer(optimizationViews,
               baseLayer,
               replacementLayer,
@@ -263,8 +249,9 @@
                                            ActivationDescriptor& activationDesc,
                                            std::string name)
 {
-    IConnectableLayer* replacement = optimizationViews.GetINetwork()->
-        AddDepthwiseConvolution2dLayer(baseLayer->GetParameters(), name.c_str());
+    IConnectableLayer* replacement =
+        optimizationViews.GetINetwork()->AddDepthwiseConvolution2dLayer(baseLayer->GetParameters(), name.c_str());
+
     LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
 
     replacementLayer->m_Weight = std::move(baseLayer->m_Weight);
diff --git a/src/backends/backendsCommon/WorkloadData.cpp b/src/backends/backendsCommon/WorkloadData.cpp
index 289f780..37fda3e 100644
--- a/src/backends/backendsCommon/WorkloadData.cpp
+++ b/src/backends/backendsCommon/WorkloadData.cpp
@@ -1246,7 +1246,13 @@
 {
     const std::string descriptorName{"Convolution2dQueueDescriptor"};
 
-    ValidateNumInputs(workloadInfo,  descriptorName, 1);
+    uint32_t numInputs = 2;
+    if (m_Parameters.m_BiasEnabled)
+    {
+        numInputs = 3;
+    }
+
+    ValidateNumInputs(workloadInfo,  descriptorName, numInputs);
     ValidateNumOutputs(workloadInfo, descriptorName, 1);
 
     const TensorInfo& inputTensorInfo  = workloadInfo.m_InputTensorInfos[0];
@@ -1255,9 +1261,8 @@
     ValidateTensorNumDimensions(inputTensorInfo,  descriptorName, 4, "input");
     ValidateTensorNumDimensions(outputTensorInfo, descriptorName, 4, "output");
 
-    ValidatePointer(m_Weight, descriptorName, "weight");
+    const TensorInfo& weightTensorInfo = workloadInfo.m_InputTensorInfos[1];
 
-    const TensorInfo& weightTensorInfo = m_Weight->GetTensorInfo();
     ValidateTensorNumDimensions(weightTensorInfo, descriptorName, 4, "weight");
 
     ValidateWeightDataType(inputTensorInfo, weightTensorInfo, descriptorName);
@@ -1265,9 +1270,7 @@
     Optional<TensorInfo> optionalBiasTensorInfo;
     if (m_Parameters.m_BiasEnabled)
     {
-        ValidatePointer(m_Bias, descriptorName, "bias");
-
-        optionalBiasTensorInfo = MakeOptional<TensorInfo>(m_Bias->GetTensorInfo());
+        optionalBiasTensorInfo = MakeOptional<TensorInfo>(workloadInfo.m_InputTensorInfos[2]);
         const TensorInfo& biasTensorInfo = optionalBiasTensorInfo.value();
 
         ValidateTensorDataType(biasTensorInfo, GetBiasDataType(inputTensorInfo.GetDataType()), descriptorName, "bias");
diff --git a/src/backends/backendsCommon/WorkloadFactory.cpp b/src/backends/backendsCommon/WorkloadFactory.cpp
index f624ee6..3660e6e 100644
--- a/src/backends/backendsCommon/WorkloadFactory.cpp
+++ b/src/backends/backendsCommon/WorkloadFactory.cpp
@@ -246,7 +246,10 @@
             const TensorInfo input  = OverrideDataType(layer.GetInputSlot(0).GetConnection()->GetTensorInfo(),
                                                        dataType);
             const TensorInfo output = OverrideDataType(layer.GetOutputSlot(0).GetTensorInfo(), dataType);
-            ARMNN_ASSERT(cLayer->m_Weight.get() != nullptr);
+            ARMNN_ASSERT_MSG(layer.GetInputSlot(1).GetConnection(),
+                             "Convolution2dLayer: Weights should be connected as a Constant Layer.");
+            const TensorInfo weights = OverrideDataType(layer.GetInputSlot(1).GetConnection()->GetTensorInfo(),
+                                                        dataType);
 
             const Convolution2dDescriptor& descriptor = cLayer->GetParameters();
 
@@ -254,14 +257,17 @@
             Optional<TensorInfo> biases;
             if (descriptor.m_BiasEnabled)
             {
-                biases = OverrideDataType(cLayer->m_Bias->GetTensorInfo(), GetBiasTypeFromWeightsType(dataType));
+                ARMNN_ASSERT_MSG(layer.GetInputSlot(2).GetConnection(),
+                                 "Convolution2dLayer: Bias should be connected as a Constant Layer.");
+                biases = OverrideDataType(layer.GetInputSlot(2).GetConnection()->GetTensorInfo(),
+                                          GetBiasTypeFromWeightsType(dataType));
             }
 
             result = layerSupportObject.IsConvolution2dSupported(
                                               input,
                                               output,
                                               descriptor,
-                                              OverrideDataType(cLayer->m_Weight->GetTensorInfo(), dataType),
+                                              weights,
                                               biases,
                                               reason);
             break;
diff --git a/src/backends/backendsCommon/test/DynamicBackendTests.hpp b/src/backends/backendsCommon/test/DynamicBackendTests.hpp
index f53bd83..0d98804 100644
--- a/src/backends/backendsCommon/test/DynamicBackendTests.hpp
+++ b/src/backends/backendsCommon/test/DynamicBackendTests.hpp
@@ -1465,7 +1465,7 @@
     Convolution2dQueueDescriptor convolution2dQueueDescriptor;
     WorkloadInfo workloadInfo
     {
-        { inputInfo },
+        { inputInfo, weightInfo },
         { outputInfo }
     };
     convolution2dQueueDescriptor.m_Inputs.push_back(nullptr);
diff --git a/src/backends/backendsCommon/test/FullyConnectedEndToEndTestImpl.hpp b/src/backends/backendsCommon/test/FullyConnectedEndToEndTestImpl.hpp
index 1076aa6..0d2d2cb 100644
--- a/src/backends/backendsCommon/test/FullyConnectedEndToEndTestImpl.hpp
+++ b/src/backends/backendsCommon/test/FullyConnectedEndToEndTestImpl.hpp
@@ -110,8 +110,11 @@
 {
     armnn::INetworkPtr network(armnn::INetwork::Create());
 
+
+    ConstTensor biases;
+
     armnn::IConnectableLayer* inputLayer  = network->AddInputLayer(0, "Input");
-    armnn::IConnectableLayer* biasLayer   = network->AddInputLayer(2, "Bias_Input");
+    armnn::IConnectableLayer* biasLayer   = network->AddConstantLayer(biases, "Bias_Input");
     armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
     armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
 
@@ -402,16 +405,7 @@
             IRuntime::CreationOptions options;
             IRuntimePtr               runtime(IRuntime::Create(options));
 
-            try
-            {
-                Optimize(*network, backends, runtime->GetDeviceSpec());
-                FAIL("LayerValidationException should have been thrown");
-            }
-            catch (const LayerValidationException& exc)
-            {
-                CHECK(strcmp(exc.what(), "Fully_Connected layer weights not set: Input slot(s) 1 not connected "
-                                         "to an output slot on FullyConnected layer \"Fully_Connected\"") == 0);
-            }
+            CHECK_THROWS_AS(Optimize(*network, backends, runtime->GetDeviceSpec()), LayerValidationException);
         }
         else if (!connectedBias)
         {
@@ -429,16 +423,7 @@
             IRuntime::CreationOptions options;
             IRuntimePtr               runtime(IRuntime::Create(options));
 
-            try
-            {
-                Optimize(*network, backends, runtime->GetDeviceSpec());
-                FAIL("LayerValidationException should have been thrown");
-            }
-            catch (const LayerValidationException& exc)
-            {
-                CHECK(strcmp(exc.what(), "Fully_Connected layer bias not set: Input slot(s) 2 not connected "
-                                         "to an output slot on FullyConnected layer \"Fully_Connected\"") == 0);
-            }
+            CHECK_THROWS_AS(Optimize(*network, backends, runtime->GetDeviceSpec()), LayerValidationException);
         }
     }
     else if(!connectedWeights && !connectedBias)
@@ -452,17 +437,7 @@
         IRuntime::CreationOptions options;
         IRuntimePtr               runtime(IRuntime::Create(options));
 
-        try
-        {
-            Optimize(*network, backends, runtime->GetDeviceSpec());
-            FAIL("LayerValidationException should have been thrown");
-        }
-        catch (const LayerValidationException& exc)
-        {
-            CHECK(strcmp(exc.what(), "Fully_Connected layer weights and bias not set: Input slot(s) 1 & 2 not "
-                                     "connected to an output slot on FullyConnected layer \"Fully_Connected\"") == 0);
-        }
-
+        CHECK_THROWS_AS(Optimize(*network, backends, runtime->GetDeviceSpec()), LayerValidationException);
     }
     else if(!tensorInfoSet)
     {
diff --git a/src/backends/backendsCommon/test/LayerReleaseConstantDataTest.cpp b/src/backends/backendsCommon/test/LayerReleaseConstantDataTest.cpp
index 56f15a5..5ceb8ae 100644
--- a/src/backends/backendsCommon/test/LayerReleaseConstantDataTest.cpp
+++ b/src/backends/backendsCommon/test/LayerReleaseConstantDataTest.cpp
@@ -70,49 +70,68 @@
 
  }
 
+TEST_CASE("ReleaseConvolution2dLayerConstantDataTest")
+{
+    Graph graph;
 
- TEST_CASE("ReleaseConvolution2dLayerConstantDataTest")
- {
-     Graph graph;
+    // create the layer we're testing
+    Convolution2dDescriptor layerDesc;
+    layerDesc.m_PadLeft = 3;
+    layerDesc.m_PadRight = 3;
+    layerDesc.m_PadTop = 1;
+    layerDesc.m_PadBottom = 1;
+    layerDesc.m_StrideX = 2;
+    layerDesc.m_StrideY = 4;
+    layerDesc.m_BiasEnabled = true;
 
-     // create the layer we're testing
-     Convolution2dDescriptor layerDesc;
-     layerDesc.m_PadLeft = 3;
-     layerDesc.m_PadRight = 3;
-     layerDesc.m_PadTop = 1;
-     layerDesc.m_PadBottom = 1;
-     layerDesc.m_StrideX = 2;
-     layerDesc.m_StrideY = 4;
-     layerDesc.m_BiasEnabled = true;
+    Convolution2dLayer* const layer = graph.AddLayer<Convolution2dLayer>(layerDesc, "layer");
 
-     Convolution2dLayer* const layer = graph.AddLayer<Convolution2dLayer>(layerDesc, "layer");
+    layer->m_Weight = std::make_unique<ScopedTensorHandle>(TensorInfo({ 2, 3, 5, 3 },
+                                                                      armnn::DataType::Float32));
+    layer->m_Bias = std::make_unique<ScopedTensorHandle>
+        (TensorInfo({ 2 }, GetBiasDataType(armnn::DataType::Float32)));
 
-     layer->m_Weight = std::make_unique<ScopedTensorHandle>(TensorInfo({2, 3, 5, 3},
-                                                                          armnn::DataType::Float32));
-     layer->m_Bias   = std::make_unique<ScopedTensorHandle>
-             (TensorInfo({2}, GetBiasDataType(armnn::DataType::Float32)));
+    layer->m_Weight->Allocate();
+    layer->m_Bias->Allocate();
 
-     layer->m_Weight->Allocate();
-     layer->m_Bias->Allocate();
+    ConstantLayer* weightsLayer = graph.AddLayer<ConstantLayer>("Weights");
+    ConstantLayer* biasLayer = graph.AddLayer<ConstantLayer>("Bias");
 
-     // create extra layers
-     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
-     Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
+    weightsLayer->m_LayerOutput = std::make_shared<ScopedTensorHandle>(TensorInfo({ 2, 3, 5, 3 },
+                                                                      armnn::DataType::Float32));
 
-     // connect up
-     Connect(input, layer, TensorInfo({2, 3, 8, 16}, armnn::DataType::Float32));
-     Connect(layer, output, TensorInfo({2, 2, 2, 10}, armnn::DataType::Float32));
+    biasLayer->m_LayerOutput = std::make_shared<ScopedTensorHandle>(
+        TensorInfo({2}, GetBiasDataType(armnn::DataType::Float32)));
 
-     // check the constants that they are not NULL
-     CHECK(layer->m_Weight != nullptr);
-     CHECK(layer->m_Bias != nullptr);
+    TensorInfo weightsInfo = weightsLayer->m_LayerOutput->GetTensorInfo();
+    weightsInfo.SetConstant();
+    TensorInfo biasInfo = biasLayer->m_LayerOutput->GetTensorInfo();
+    biasInfo.SetConstant();
 
-     // free up the constants..
-     layer->ReleaseConstantData();
 
-     // check the constants that they are NULL now
-     CHECK(layer->m_Weight == nullptr);
-     CHECK(layer->m_Bias == nullptr);
+    weightsLayer->GetOutputSlot(0).SetTensorInfo(weightsInfo);
+    biasLayer->GetOutputSlot(0).SetTensorInfo(biasInfo);
+
+    // create extra layers
+    Layer* const input = graph.AddLayer<InputLayer>(0, "input");
+    Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
+
+    // connect up
+    Connect(input, layer, TensorInfo({ 2, 3, 8, 16 }, armnn::DataType::Float32));
+    weightsLayer->GetOutputSlot().Connect(layer->GetInputSlot(1));
+    biasLayer->GetOutputSlot().Connect(layer->GetInputSlot(2));
+    Connect(layer, output, TensorInfo({ 2, 2, 2, 10 }, armnn::DataType::Float32));
+
+    // check the constants that they are not NULL
+    CHECK(layer->m_Weight != nullptr);
+    CHECK(layer->m_Bias != nullptr);
+
+    // free up the constants..
+    layer->ReleaseConstantData();
+
+    // check the constants that they are NULL now
+    CHECK(layer->m_Weight == nullptr);
+    CHECK(layer->m_Bias == nullptr);
 }
 
 TEST_CASE("ReleaseDepthwiseConvolution2dLayerConstantDataTest")
@@ -131,8 +150,10 @@
 
     DepthwiseConvolution2dLayer* const layer = graph.AddLayer<DepthwiseConvolution2dLayer>(layerDesc, "layer");
 
-    layer->m_Weight = std::make_unique<ScopedTensorHandle>(TensorInfo({3, 3, 5, 3}, DataType::Float32));
-    layer->m_Bias   = std::make_unique<ScopedTensorHandle>(TensorInfo({9}, DataType::Float32));
+    layer->m_Weight = std::make_unique<ScopedTensorHandle>(
+        TensorInfo({3, 3, 5, 3}, DataType::Float32));
+    layer->m_Bias = std::make_unique<ScopedTensorHandle>(
+        TensorInfo({9}, DataType::Float32));
     layer->m_Weight->Allocate();
     layer->m_Bias->Allocate();
 
@@ -170,10 +191,10 @@
     float inputsQScale = 1.0f;
     float outputQScale = 2.0f;
 
-    layer->m_Weight = std::make_unique<ScopedTensorHandle>(TensorInfo({7, 20},
-                                                          DataType::QAsymmU8, inputsQScale, 0));
-    layer->m_Bias   = std::make_unique<ScopedTensorHandle>(TensorInfo({7},
-                                                          GetBiasDataType(DataType::QAsymmU8), inputsQScale));
+    layer->m_Weight = std::make_unique<ScopedTensorHandle>(
+        TensorInfo({7, 20}, DataType::QAsymmU8, inputsQScale, 0));
+    layer->m_Bias = std::make_unique<ScopedTensorHandle>(
+        TensorInfo({7}, GetBiasDataType(DataType::QAsymmU8), inputsQScale));
     layer->m_Weight->Allocate();
     layer->m_Bias->Allocate();
 
diff --git a/src/backends/backendsCommon/test/OptimizationViewsTests.cpp b/src/backends/backendsCommon/test/OptimizationViewsTests.cpp
index 1219ac5..9b86784 100644
--- a/src/backends/backendsCommon/test/OptimizationViewsTests.cpp
+++ b/src/backends/backendsCommon/test/OptimizationViewsTests.cpp
@@ -61,31 +61,35 @@
     Layer* const inputLayer = baseGraph.AddLayer<InputLayer>(0, "input");
 
     Convolution2dDescriptor convDescriptor;
-    PreCompiledDescriptor substitutionLayerDescriptor(1, 1);
+    PreCompiledDescriptor substitutionLayerDescriptor(2, 1);
     Layer* const convLayer1 = baseGraph.AddLayer<Convolution2dLayer>(convDescriptor, "conv1");
     Layer* const convLayer2 = baseGraph.AddLayer<Convolution2dLayer>(convDescriptor, "conv2");
+    Layer* const weightsLayer1 = baseGraph.AddLayer<ConstantLayer>("weights1");
+    Layer* const weightsLayer2 = baseGraph.AddLayer<ConstantLayer>("weights2");
     Layer* const substitutableCompiledLayer =
             baseGraph.AddLayer<PreCompiledLayer>(substitutionLayerDescriptor, "pre-compiled");
 
     Layer* const outputLayer = baseGraph.AddLayer<OutputLayer>(0, "output");
 
     inputLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0));
+    weightsLayer1->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(1));
     convLayer1->GetOutputSlot(0).Connect(convLayer2->GetInputSlot(0));
+    weightsLayer2->GetOutputSlot(0).Connect(convLayer2->GetInputSlot(1));
     convLayer2->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
 
     // Subgraph for a failed layer
     SubgraphViewSelector::SubgraphViewPtr failedSubgraph =
-        CreateSubgraphViewFrom(CreateInputsFrom({convLayer1}),
+        CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
                                CreateOutputsFrom({convLayer1}),
                                {convLayer1});
     // Subgraph for an untouched layer
     SubgraphViewSelector::SubgraphViewPtr untouchedSubgraph =
-            CreateSubgraphViewFrom(CreateInputsFrom({convLayer2}),
+            CreateSubgraphViewFrom(CreateInputsFrom(convLayer2),
                                    CreateOutputsFrom({convLayer2}),
                                    {convLayer2});
     // Subgraph for a substitutable layer
     SubgraphViewSelector::SubgraphViewPtr substitutableSubgraph =
-            CreateSubgraphViewFrom(CreateInputsFrom({convLayer1}),
+            CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
                                    CreateOutputsFrom({convLayer2}),
                                    {substitutableCompiledLayer});
     // Create a Graph containing a layer to substitute in
@@ -95,7 +99,7 @@
 
     // Subgraph for a substitution layer
     SubgraphViewSelector::SubgraphViewPtr substitutionSubgraph =
-            CreateSubgraphViewFrom(CreateInputsFrom({substitutionpreCompiledLayer}),
+            CreateSubgraphViewFrom(CreateInputsFrom(substitutionpreCompiledLayer),
                                    CreateOutputsFrom({substitutionpreCompiledLayer}),
                                    {substitutionpreCompiledLayer});
 
@@ -106,14 +110,14 @@
     view.AddUntouchedSubgraph(SubgraphView(*untouchedSubgraph));
 
     SubgraphViewSelector::SubgraphViewPtr baseSubgraph =
-            CreateSubgraphViewFrom(CreateInputsFrom({convLayer1}),
+            CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
                                    CreateOutputsFrom({convLayer2}),
                                    {substitutionpreCompiledLayer});
     view.AddSubstitution({*baseSubgraph, *substitutionSubgraph});
 
     // Construct original subgraph to compare against
     SubgraphViewSelector::SubgraphViewPtr originalSubgraph =
-            CreateSubgraphViewFrom(CreateInputsFrom({convLayer1}),
+            CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
             CreateOutputsFrom({convLayer2}),
             {convLayer1, convLayer2, substitutionpreCompiledLayer});
 
@@ -147,11 +151,11 @@
     convLayer2->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
 
     // Subgraph for a failed layer
-    SubgraphViewSelector::SubgraphViewPtr failedSubgraph = CreateSubgraphViewFrom(CreateInputsFrom({convLayer1}),
+    SubgraphViewSelector::SubgraphViewPtr failedSubgraph = CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
                                                                                   CreateOutputsFrom({convLayer1}),
                                                                                   {convLayer1});
     // Subgraph for an untouched layer
-    SubgraphViewSelector::SubgraphViewPtr untouchedSubgraph = CreateSubgraphViewFrom(CreateInputsFrom({convLayer2}),
+    SubgraphViewSelector::SubgraphViewPtr untouchedSubgraph = CreateSubgraphViewFrom(CreateInputsFrom(convLayer2),
                                                                                      CreateOutputsFrom({convLayer2}),
                                                                                      {convLayer2});
 
@@ -162,21 +166,21 @@
 
     // Subgraph for a substitution layer
     SubgraphViewSelector::SubgraphViewPtr substitutionSubgraph =
-        CreateSubgraphViewFrom(CreateInputsFrom({substitutionpreCompiledLayer}),
+        CreateSubgraphViewFrom(CreateInputsFrom(substitutionpreCompiledLayer),
                                                 CreateOutputsFrom({substitutionpreCompiledLayer}),
                                                 {substitutionpreCompiledLayer});
 
     view.AddFailedSubgraph(SubgraphView(*failedSubgraph));
     view.AddUntouchedSubgraph(SubgraphView(*untouchedSubgraph));
 
-    SubgraphViewSelector::SubgraphViewPtr baseSubgraph = CreateSubgraphViewFrom(CreateInputsFrom({convLayer1}),
+    SubgraphViewSelector::SubgraphViewPtr baseSubgraph = CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
                                                                                 CreateOutputsFrom({convLayer2}),
                                                                                 {substitutionpreCompiledLayer});
     view.AddSubstitution({*baseSubgraph, *substitutionSubgraph});
 
     // Construct original subgraph to compare against
     SubgraphViewSelector::SubgraphViewPtr originalSubgraph =
-        CreateSubgraphViewFrom(CreateInputsFrom({convLayer1}),
+        CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
                                                 CreateOutputsFrom({convLayer2}),
                                                 {convLayer1, convLayer2, substitutionpreCompiledLayer});
 
@@ -192,26 +196,31 @@
     Layer* const inputLayer = baseGraph.AddLayer<InputLayer>(0, "input");
 
     Convolution2dDescriptor convDescriptor;
-    PreCompiledDescriptor substitutionLayerDescriptor(1, 1);
+    PreCompiledDescriptor substitutionLayerDescriptor(2, 1);
     Layer* const convLayer1 = baseGraph.AddLayer<Convolution2dLayer>(convDescriptor, "conv1");
     Layer* const convLayer2 = baseGraph.AddLayer<Convolution2dLayer>(convDescriptor, "conv2");
+    Layer* const weightsLayer1 = baseGraph.AddLayer<ConstantLayer>("weights1");
+    Layer* const weightsLayer2 = baseGraph.AddLayer<ConstantLayer>("weights2");
     Layer* const substitutableCompiledLayer =
             baseGraph.AddLayer<PreCompiledLayer>(substitutionLayerDescriptor, "pre-compiled");
 
     Layer* const outputLayer = baseGraph.AddLayer<OutputLayer>(0, "output");
 
+
     inputLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0));
+    weightsLayer1->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(1));
     convLayer1->GetOutputSlot(0).Connect(convLayer2->GetInputSlot(0));
+    weightsLayer2->GetOutputSlot(0).Connect(convLayer2->GetInputSlot(1));
     convLayer2->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
 
     // Subgraph for an untouched layer
     SubgraphViewSelector::SubgraphViewPtr untouchedSubgraph =
-            CreateSubgraphViewFrom(CreateInputsFrom({convLayer2}),
+            CreateSubgraphViewFrom(CreateInputsFrom(convLayer2),
                                    CreateOutputsFrom({convLayer2}),
                                    {convLayer2});
     // Subgraph for a substitutable layer
     SubgraphViewSelector::SubgraphViewPtr substitutableSubgraph =
-            CreateSubgraphViewFrom(CreateInputsFrom({convLayer1}),
+            CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
                                    CreateOutputsFrom({convLayer2}),
                                    {substitutableCompiledLayer});
     // Create a Graph containing a layer to substitute in
@@ -221,7 +230,7 @@
 
     // Subgraph for a substitution layer
     SubgraphViewSelector::SubgraphViewPtr substitutionSubgraph =
-            CreateSubgraphViewFrom(CreateInputsFrom({substitutionpreCompiledLayer}),
+            CreateSubgraphViewFrom(CreateInputsFrom(substitutionpreCompiledLayer),
                                    CreateOutputsFrom({substitutionpreCompiledLayer}),
                                    {substitutionpreCompiledLayer});
 
@@ -231,14 +240,14 @@
     view.AddUntouchedSubgraph(SubgraphView(*untouchedSubgraph));
 
     SubgraphViewSelector::SubgraphViewPtr baseSubgraph =
-            CreateSubgraphViewFrom(CreateInputsFrom({convLayer1}),
+            CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
                                    CreateOutputsFrom({convLayer2}),
                                    {substitutionpreCompiledLayer});
     view.AddSubstitution({*baseSubgraph, *substitutionSubgraph});
 
     // Construct original subgraph to compare against
     SubgraphViewSelector::SubgraphViewPtr originalSubgraph =
-            CreateSubgraphViewFrom(CreateInputsFrom({convLayer1}),
+            CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
                                    CreateOutputsFrom({convLayer2}),
                                    {convLayer1, convLayer2, substitutionpreCompiledLayer});
 
diff --git a/src/backends/backendsCommon/test/OptimizeSubgraphViewTests.cpp b/src/backends/backendsCommon/test/OptimizeSubgraphViewTests.cpp
index ad59704..45fcf19 100644
--- a/src/backends/backendsCommon/test/OptimizeSubgraphViewTests.cpp
+++ b/src/backends/backendsCommon/test/OptimizeSubgraphViewTests.cpp
@@ -106,6 +106,21 @@
     return convLayer;
 }
 
+// Convenience function to add a constant layer to a graph
+ConstantLayer* AddConstantLayer(Graph& graph,
+                                LayerNameToLayerMap& layersInGraph,
+                                const std::string& layerName,
+                                const ConstTensor& constTensor,
+                                const TensorInfo& outputInfo)
+{
+    ConstantLayer* const constantLayer = graph.AddLayer<ConstantLayer>(layerName.c_str());
+    CHECK(constantLayer);
+    constantLayer->m_LayerOutput = std::make_shared<ScopedTensorHandle>(constTensor);
+    constantLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
+    layersInGraph.insert(std::make_pair(constantLayer->GetName(), constantLayer));
+    return constantLayer;
+}
+
 // Convenience function to add a pooling layer to a graph
 Pooling2dLayer* AddPoolingLayer(Graph& graph,
                                 LayerNameToLayerMap& layersInGraph,
@@ -246,7 +261,7 @@
     poolingLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
 
     // Create the subgraph view for the whole network
-    return CreateSubgraphViewFrom(CreateInputsFrom({poolingLayer}),
+    return CreateSubgraphViewFrom(CreateInputsFrom(poolingLayer),
                                   CreateOutputsFrom({poolingLayer}),
                                   {poolingLayer});
 }
@@ -287,7 +302,7 @@
     pooling3Layer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
 
     // Create the subgraph view for the whole network
-    return CreateSubgraphViewFrom(CreateInputsFrom({pooling1Layer}),
+    return CreateSubgraphViewFrom(CreateInputsFrom(pooling1Layer),
                                   CreateOutputsFrom({pooling3Layer}),
                                   {pooling1Layer,
                                    pooling2Layer,
@@ -299,8 +314,11 @@
 {
     const TensorInfo inputInfo ({  1, 16, 16, 16 }, DataType::QAsymmU8, 1.0f, 0);
     const TensorInfo outputInfo({  1, 16, 16, 16 }, DataType::QAsymmU8, 1.0f, 0);
-    const TensorInfo weightInfo({ 16,  1,  1, 16 }, DataType::QAsymmU8, 0.9f, 0);
-    const TensorInfo biasInfo  ({  1,  1,  1, 16 }, DataType::Signed32,        0.9f, 0);
+    TensorInfo weightInfo({ 16,  1,  1, 16 }, DataType::QAsymmU8, 0.9f, 0);
+    TensorInfo biasInfo  ({  1,  1,  1, 16 }, DataType::Signed32,        0.9f, 0);
+
+    weightInfo.SetConstant(true);
+    biasInfo.SetConstant(true);
 
     Convolution2dDescriptor convolutionDescriptor;
     convolutionDescriptor.m_StrideX     = 1;
@@ -308,20 +326,34 @@
     convolutionDescriptor.m_BiasEnabled = true;
     convolutionDescriptor.m_DataLayout  = DataLayout::NHWC;
 
+    std::vector<float> weightsVector(64);
+    ConstTensor constWeightsTensor(weightInfo, weightsVector);
+
+    std::vector<float> biasVector(16);
+    ConstTensor constBiasTensor(biasInfo, biasVector);
+
     // Construct the graph
     Layer* const inputLayer = AddInputLayer(graph, "input layer", inputInfo);
     Convolution2dLayer* const convLayer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
                                                               "conv layer", weightInfo, biasInfo, outputInfo);
+
+  ConstantLayer* const weightsLayer =
+      AddConstantLayer(graph, layersInGraph, "Weights Layer", constWeightsTensor, outputInfo);
+  ConstantLayer* const biasLayer = AddConstantLayer(graph, layersInGraph, "Bias Layer", constBiasTensor, outputInfo);
+
     Layer* const outputLayer = AddOutputLayer(graph, "output layer");
 
     // Connect the network
     inputLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(0));
+    weightsLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(1));
+    biasLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(2));
     convLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
 
+    std::vector<unsigned int> ignoreSlots = {1, 2};
     // Create the subgraph view for the whole network
-    return CreateSubgraphViewFrom(CreateInputsFrom({convLayer}),
+    return CreateSubgraphViewFrom(CreateInputsFrom(convLayer, ignoreSlots),
                                   CreateOutputsFrom({convLayer}),
-                                  {convLayer});
+                                  {convLayer, weightsLayer, biasLayer});
 }
 
 // Creates a subgraph with five convolutions layers, all supported by the mock backend
@@ -329,8 +361,18 @@
 {
     const TensorInfo inputInfo ({  1, 16, 16, 16 }, DataType::QAsymmU8, 1.0f, 0);
     const TensorInfo outputInfo({  1, 16, 16, 16 }, DataType::QAsymmU8, 1.0f, 0);
-    const TensorInfo weightInfo({ 16,  1,  1, 16 }, DataType::QAsymmU8, 0.9f, 0);
-    const TensorInfo biasInfo  ({  1,  1,  1, 16 }, DataType::Signed32,        0.9f, 0);
+    TensorInfo weightInfo({ 16,  1,  1, 16 }, DataType::QAsymmU8, 0.9f, 0);
+    TensorInfo biasInfo  ({  1,  1,  1, 16 }, DataType::Signed32,        0.9f, 0);
+
+    weightInfo.SetConstant(true);
+    biasInfo.SetConstant(true);
+
+    std::vector<float> weightsVector(64);
+    ConstTensor constWeightsTensor(weightInfo, weightsVector);
+
+    std::vector<float> biasVector(16);
+    ConstTensor constBiasTensor(biasInfo, biasVector);
+
 
     Convolution2dDescriptor convolutionDescriptor;
     convolutionDescriptor.m_StrideX     = 1;
@@ -342,32 +384,84 @@
     Layer* const inputLayer = AddInputLayer(graph, "input layer", inputInfo);
     Convolution2dLayer* const conv1Layer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
                                                                "conv1 layer", weightInfo, biasInfo, outputInfo);
+    ConstantLayer* const weightsLayer1 =
+        AddConstantLayer(graph, layersInGraph, "Weights Layer 1", constWeightsTensor, outputInfo);
+    ConstantLayer* const biasLayer1 =
+        AddConstantLayer(graph, layersInGraph, "Bias Layer 1", constBiasTensor, outputInfo);
+
     Convolution2dLayer* const conv2Layer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
                                                                "conv2 layer", weightInfo, biasInfo, outputInfo);
+    ConstantLayer* const weightsLayer2 =
+        AddConstantLayer(graph, layersInGraph, "Weights Layer 2", constWeightsTensor, outputInfo);
+    ConstantLayer* const biasLayer2 =
+        AddConstantLayer(graph, layersInGraph, "Bias Layer 2", constBiasTensor, outputInfo);
+
+
     Convolution2dLayer* const conv3Layer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
                                                                "conv3 layer", weightInfo, biasInfo, outputInfo);
+    ConstantLayer* const weightsLayer3 =
+        AddConstantLayer(graph, layersInGraph, "Weights Layer 3", constWeightsTensor, outputInfo);
+    ConstantLayer* const biasLayer3 =
+        AddConstantLayer(graph, layersInGraph, "Bias Layer 3", constBiasTensor, outputInfo);
+
     Convolution2dLayer* const conv4Layer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
                                                                "conv4 layer", weightInfo, biasInfo, outputInfo);
+    ConstantLayer* const weightsLayer4 =
+        AddConstantLayer(graph, layersInGraph, "Weights Layer 4", constWeightsTensor, outputInfo);
+    ConstantLayer* const biasLayer4 =
+        AddConstantLayer(graph, layersInGraph, "Bias Layer 4", constBiasTensor, outputInfo);
+
     Convolution2dLayer* const conv5Layer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
                                                                "conv5 layer", weightInfo, biasInfo, outputInfo);
+    ConstantLayer* const weightsLayer5 =
+        AddConstantLayer(graph, layersInGraph, "Weights Layer 5", constWeightsTensor, outputInfo);
+    ConstantLayer* const biasLayer5 =
+        AddConstantLayer(graph, layersInGraph, "Bias Layer 5", constBiasTensor, outputInfo);
+
+
     Layer* const outputLayer = AddOutputLayer(graph, "output layer");
 
     // Connect the network
     inputLayer->GetOutputSlot(0).Connect(conv1Layer->GetInputSlot(0));
-    conv1Layer->GetOutputSlot(0).Connect(conv2Layer->GetInputSlot(0));
-    conv2Layer->GetOutputSlot(0).Connect(conv3Layer->GetInputSlot(0));
-    conv3Layer->GetOutputSlot(0).Connect(conv4Layer->GetInputSlot(0));
-    conv4Layer->GetOutputSlot(0).Connect(conv5Layer->GetInputSlot(0));
-    conv5Layer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
+    weightsLayer1->GetOutputSlot(0).Connect(conv1Layer->GetInputSlot(1));
+    biasLayer1->GetOutputSlot(0).Connect(conv1Layer->GetInputSlot(2));
 
+    conv1Layer->GetOutputSlot(0).Connect(conv2Layer->GetInputSlot(0));
+    weightsLayer2->GetOutputSlot(0).Connect(conv2Layer->GetInputSlot(1));
+    biasLayer2->GetOutputSlot(0).Connect(conv2Layer->GetInputSlot(2));
+
+    conv2Layer->GetOutputSlot(0).Connect(conv3Layer->GetInputSlot(0));
+    weightsLayer3->GetOutputSlot(0).Connect(conv3Layer->GetInputSlot(1));
+    biasLayer3->GetOutputSlot(0).Connect(conv3Layer->GetInputSlot(2));
+
+    conv3Layer->GetOutputSlot(0).Connect(conv4Layer->GetInputSlot(0));
+    weightsLayer4->GetOutputSlot(0).Connect(conv4Layer->GetInputSlot(1));
+    biasLayer4->GetOutputSlot(0).Connect(conv4Layer->GetInputSlot(2));
+
+    conv4Layer->GetOutputSlot(0).Connect(conv5Layer->GetInputSlot(0));
+    weightsLayer5->GetOutputSlot(0).Connect(conv5Layer->GetInputSlot(1));
+    biasLayer5->GetOutputSlot(0).Connect(conv5Layer->GetInputSlot(2));
+
+    conv5Layer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
+    std::vector<unsigned int> ignoreSlots = {1, 2};
     // Create the subgraph view for the whole network
-    return CreateSubgraphViewFrom(CreateInputsFrom({conv1Layer}),
-                                  CreateOutputsFrom({conv5Layer}),
-                                  {conv1Layer,
-                                   conv2Layer,
-                                   conv3Layer,
-                                   conv4Layer,
-                                   conv5Layer});
+    return CreateSubgraphViewFrom(CreateInputsFrom(conv1Layer, ignoreSlots),
+                                  CreateOutputsFrom({ conv5Layer }),
+                                  { weightsLayer1,
+                                    biasLayer1,
+                                    conv1Layer,
+                                    weightsLayer2,
+                                    biasLayer2,
+                                    conv2Layer,
+                                    weightsLayer3,
+                                    biasLayer3,
+                                    conv3Layer,
+                                    weightsLayer4,
+                                    biasLayer4,
+                                    conv4Layer,
+                                    weightsLayer5,
+                                    biasLayer5,
+                                    conv5Layer });
 }
 
 // Creates a subgraph with both supported and unsupported layers
@@ -376,8 +470,17 @@
 {
     const TensorInfo inputInfo ({  1, 16, 16, 16 }, DataType::QAsymmU8, 1.0f, 0);
     const TensorInfo outputInfo({  1, 16, 16, 16 }, DataType::QAsymmU8, 1.0f, 0);
-    const TensorInfo weightInfo({ 16,  1,  1, 16 }, DataType::QAsymmU8, 0.9f, 0);
-    const TensorInfo biasInfo  ({  1,  1,  1, 16 }, DataType::Signed32,        0.9f, 0);
+    TensorInfo weightInfo({ 16,  1,  1, 16 }, DataType::QAsymmU8, 0.9f, 0);
+    TensorInfo biasInfo  ({  1,  1,  1, 16 }, DataType::Signed32,        0.9f, 0);
+
+    weightInfo.SetConstant(true);
+    biasInfo.SetConstant(true);
+
+    std::vector<float> weightsVector(64);
+    ConstTensor constWeightsTensor(weightInfo, weightsVector);
+
+    std::vector<float> biasVector(16);
+    ConstTensor constBiasTensor(biasInfo, biasVector);
 
     Convolution2dDescriptor convolutionDescriptor;
     convolutionDescriptor.m_StrideX     = 1;
@@ -400,12 +503,25 @@
 
     // Construct the graph
     Layer* const inputLayer = AddInputLayer(graph, "input layer", inputInfo);
+    ConstantLayer* const weightsLayer1 =
+        AddConstantLayer(graph, layersInGraph, "Weights Layer 1", constWeightsTensor, outputInfo);
+
+    ConstantLayer* const biasLayer1 =
+        AddConstantLayer(graph, layersInGraph, "Bias Layer 1", constBiasTensor, outputInfo);
+
     Convolution2dLayer* const conv1Layer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
                                                                "conv1 layer", weightInfo, biasInfo, outputInfo);
     Pooling2dLayer* const pooling1Layer = AddPoolingLayer(graph, layersInGraph, poolingDescriptor,
                                                           "pooling1 layer", outputInfo);
     Pooling2dLayer* const pooling2Layer = AddPoolingLayer(graph, layersInGraph, poolingDescriptor,
                                                           "pooling2 layer", outputInfo);
+
+    ConstantLayer* const weightsLayer2 =
+        AddConstantLayer(graph, layersInGraph, "Weights Layer 2", constWeightsTensor, outputInfo);
+
+    ConstantLayer* const biasLayer2 =
+        AddConstantLayer(graph, layersInGraph, "Bias Layer 2", constBiasTensor, outputInfo);
+
     Convolution2dLayer* const conv2Layer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
                                                                "conv2 layer", weightInfo, biasInfo, outputInfo);
     Pooling2dLayer* const pooling3Layer = AddPoolingLayer(graph, layersInGraph, poolingDescriptor,
@@ -414,18 +530,27 @@
 
     // Connect the network
     inputLayer->GetOutputSlot(0).Connect(conv1Layer->GetInputSlot(0));
+    weightsLayer1->GetOutputSlot(0).Connect(conv1Layer->GetInputSlot(1));
+    biasLayer1->GetOutputSlot(0).Connect(conv1Layer->GetInputSlot(2));
     conv1Layer->GetOutputSlot(0).Connect(pooling1Layer->GetInputSlot(0));
     pooling1Layer->GetOutputSlot(0).Connect(pooling2Layer->GetInputSlot(0));
     pooling2Layer->GetOutputSlot(0).Connect(conv2Layer->GetInputSlot(0));
+    weightsLayer2->GetOutputSlot(0).Connect(conv2Layer->GetInputSlot(1));
+    biasLayer2->GetOutputSlot(0).Connect(conv2Layer->GetInputSlot(2));
     conv2Layer->GetOutputSlot(0).Connect(pooling3Layer->GetInputSlot(0));
     pooling3Layer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
 
+    std::vector<unsigned int> ignoreSlots = {1, 2};
     // Create the subgraph view for the whole network
-    return CreateSubgraphViewFrom(CreateInputsFrom({conv1Layer}),
+    return CreateSubgraphViewFrom(CreateInputsFrom(conv1Layer, ignoreSlots),
                                   CreateOutputsFrom({pooling3Layer}),
-                                  {conv1Layer,
+                                  {weightsLayer1,
+                                   biasLayer1,
+                                   conv1Layer,
                                    pooling1Layer,
                                    pooling2Layer,
+                                   weightsLayer2,
+                                   biasLayer2,
                                    conv2Layer,
                                    pooling3Layer});
 }
@@ -435,9 +560,17 @@
 {
     const TensorInfo inputInfo ({  1, 16, 16, 16 }, DataType::QAsymmU8, 1.0f, 0);
     const TensorInfo outputInfo({  1, 16, 16, 16 }, DataType::QAsymmU8, 1.0f, 0);
-    const TensorInfo weightInfo({ 16,  1,  1, 16 }, DataType::QAsymmU8, 0.9f, 0);
-    const TensorInfo biasInfo  ({  1,  1,  1, 16 }, DataType::Signed32,        0.9f, 0);
+    TensorInfo weightInfo({ 16,  1,  1, 16 }, DataType::QAsymmU8, 0.9f, 0);
+    TensorInfo biasInfo  ({  1,  1,  1, 16 }, DataType::Signed32,        0.9f, 0);
 
+    weightInfo.SetConstant(true);
+    biasInfo.SetConstant(true);
+
+    std::vector<float> weightsVector(64);
+    ConstTensor constWeightsTensor(weightInfo, weightsVector);
+
+    std::vector<float> biasVector(16);
+    ConstTensor constBiasTensor(biasInfo, biasVector);
     Convolution2dDescriptor convolutionDescriptor;
     convolutionDescriptor.m_StrideX     = 1;
     convolutionDescriptor.m_StrideY     = 1;
@@ -446,6 +579,13 @@
 
     // Construct the graph
     Layer* const inputLayer = AddInputLayer(graph, "input layer", inputInfo);
+
+    ConstantLayer* const weightsLayer =
+        AddConstantLayer(graph, layersInGraph, "Weights Layer unoptimizable", constWeightsTensor, outputInfo);
+
+    ConstantLayer* const biasLayer =
+        AddConstantLayer(graph, layersInGraph, "Bias Layer unoptimizable", constBiasTensor, outputInfo);
+
     Convolution2dLayer* const convLayer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
                                                                "conv layer unoptimizable", weightInfo, biasInfo,
                                                                outputInfo);
@@ -453,12 +593,15 @@
 
     // Connect the network
     inputLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(0));
+    weightsLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(1));
+    biasLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(2));
     convLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
 
+    std::vector<unsigned int> ignoreSlots = {1, 2};
     // Create the subgraph view for the whole network
-    return CreateSubgraphViewFrom(CreateInputsFrom({convLayer}),
+    return CreateSubgraphViewFrom(CreateInputsFrom(convLayer, ignoreSlots),
                                   CreateOutputsFrom({convLayer}),
-                                  {convLayer});
+                                  {convLayer, weightsLayer, biasLayer});
 }
 
 // Creates a subgraph with some unoptimizable layers ("unoptimizable" is added to the layer's name)
@@ -466,8 +609,17 @@
 {
     const TensorInfo inputInfo ({  1, 16, 16, 16 }, DataType::QAsymmU8, 1.0f, 0);
     const TensorInfo outputInfo({  1, 16, 16, 16 }, DataType::QAsymmU8, 1.0f, 0);
-    const TensorInfo weightInfo({ 16,  1,  1, 16 }, DataType::QAsymmU8, 0.9f, 0);
-    const TensorInfo biasInfo  ({  1,  1,  1, 16 }, DataType::Signed32,        0.9f, 0);
+    TensorInfo weightInfo({ 16,  1,  1, 16 }, DataType::QAsymmU8, 0.9f, 0);
+    TensorInfo biasInfo  ({  1,  1,  1, 16 }, DataType::Signed32,        0.9f, 0);
+
+    weightInfo.SetConstant(true);
+    biasInfo.SetConstant(true);
+
+    std::vector<float> weightsVector(64);
+    ConstTensor constWeightsTensor(weightInfo, weightsVector);
+
+    std::vector<float> biasVector(16);
+    ConstTensor constBiasTensor(biasInfo, biasVector);
 
     Convolution2dDescriptor convolutionDescriptor;
     convolutionDescriptor.m_StrideX     = 1;
@@ -477,36 +629,93 @@
 
     // Construct the graph
     Layer* const inputLayer = AddInputLayer(graph, "input layer", inputInfo);
-    Convolution2dLayer* const conv1Layer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
-                                                               "conv1 layer", weightInfo, biasInfo, outputInfo);
-    Convolution2dLayer* const conv2Layer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
-                                                               "conv2 layer unoptimizable", weightInfo, biasInfo,
-                                                               outputInfo);
-    Convolution2dLayer* const conv3Layer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
-                                                               "conv3 layer", weightInfo, biasInfo, outputInfo);
-    Convolution2dLayer* const conv4Layer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
-                                                               "conv4 layer unoptimizable", weightInfo, biasInfo,
-                                                               outputInfo);
-    Convolution2dLayer* const conv5Layer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
-                                                               "conv5 layer", weightInfo, biasInfo, outputInfo);
-    Layer* const outputLayer = AddOutputLayer(graph, "output layer");
+
+    ConstantLayer* const weightsLayer1 =
+        AddConstantLayer(graph, layersInGraph, "Weights Layer 1", constWeightsTensor, outputInfo);
+    ConstantLayer* const biasLayer1 =
+        AddConstantLayer(graph, layersInGraph, "Bias Layer 1", constBiasTensor, outputInfo);
+    ConstantLayer* const weightsLayer2 =
+        AddConstantLayer(graph, layersInGraph, "Weights Layer 2 unoptimizable", constWeightsTensor, outputInfo);
+    ConstantLayer* const biasLayer2 =
+        AddConstantLayer(graph, layersInGraph, "Bias Layer 2 unoptimizable", constBiasTensor, outputInfo);
+    ConstantLayer* const weightsLayer3 =
+        AddConstantLayer(graph, layersInGraph, "Weights Layer 3", constWeightsTensor, outputInfo);
+    ConstantLayer* const biasLayer3 =
+        AddConstantLayer(graph, layersInGraph, "Bias Layer 3", constBiasTensor, outputInfo);
+    ConstantLayer* const weightsLayer4 =
+        AddConstantLayer(graph, layersInGraph, "Weights Layer 4 unoptimizable", constWeightsTensor, outputInfo);
+    ConstantLayer* const biasLayer4 =
+        AddConstantLayer(graph, layersInGraph, "Bias Layer 4 unoptimizable", constBiasTensor, outputInfo);
+    ConstantLayer* const weightsLayer5 =
+        AddConstantLayer(graph, layersInGraph, "Weights Layer 5", constWeightsTensor, outputInfo);
+    ConstantLayer* const biasLayer5 =
+        AddConstantLayer(graph, layersInGraph, "Bias Layer 5", constBiasTensor, outputInfo);
+
+  Convolution2dLayer* const conv1Layer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
+                                                             "conv1 layer", weightInfo, biasInfo, outputInfo);
+  Convolution2dLayer* const conv2Layer = AddConvolutionLayer(graph,
+                                                             layersInGraph,
+                                                             convolutionDescriptor,
+                                                             "conv2 layer unoptimizable",
+                                                             weightInfo,
+                                                             biasInfo,
+                                                             outputInfo);
+  Convolution2dLayer* const conv3Layer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
+                                                             "conv3 layer", weightInfo, biasInfo, outputInfo);
+  Convolution2dLayer* const conv4Layer = AddConvolutionLayer(graph,
+                                                             layersInGraph,
+                                                             convolutionDescriptor,
+                                                             "conv4 layer unoptimizable",
+                                                             weightInfo,
+                                                             biasInfo,
+                                                             outputInfo);
+  Convolution2dLayer* const conv5Layer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
+                                                             "conv5 layer", weightInfo, biasInfo, outputInfo);
+
+  Layer* const outputLayer = AddOutputLayer(graph, "output layer");
 
     // Connect the network
     inputLayer->GetOutputSlot(0).Connect(conv1Layer->GetInputSlot(0));
+    weightsLayer1->GetOutputSlot(0).Connect(conv1Layer->GetInputSlot(1));
+    biasLayer1->GetOutputSlot(0).Connect(conv1Layer->GetInputSlot(2));
+
     conv1Layer->GetOutputSlot(0).Connect(conv2Layer->GetInputSlot(0));
+    weightsLayer2->GetOutputSlot(0).Connect(conv2Layer->GetInputSlot(1));
+    biasLayer2->GetOutputSlot(0).Connect(conv2Layer->GetInputSlot(2));
+
     conv2Layer->GetOutputSlot(0).Connect(conv3Layer->GetInputSlot(0));
+    weightsLayer3->GetOutputSlot(0).Connect(conv3Layer->GetInputSlot(1));
+    biasLayer3->GetOutputSlot(0).Connect(conv3Layer->GetInputSlot(2));
+
     conv3Layer->GetOutputSlot(0).Connect(conv4Layer->GetInputSlot(0));
+    weightsLayer4->GetOutputSlot(0).Connect(conv4Layer->GetInputSlot(1));
+    biasLayer4->GetOutputSlot(0).Connect(conv4Layer->GetInputSlot(2));
+
     conv4Layer->GetOutputSlot(0).Connect(conv5Layer->GetInputSlot(0));
+    weightsLayer5->GetOutputSlot(0).Connect(conv5Layer->GetInputSlot(1));
+    biasLayer5->GetOutputSlot(0).Connect(conv5Layer->GetInputSlot(2));
+
     conv5Layer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
 
+    std::vector<unsigned int> ignoreSlots = {1, 2};
     // Create the subgraph view for the whole network
-    return CreateSubgraphViewFrom(CreateInputsFrom({conv1Layer}),
+    return CreateSubgraphViewFrom(CreateInputsFrom(conv1Layer, ignoreSlots),
                                   CreateOutputsFrom({conv5Layer}),
-                                  {conv1Layer,
-                                   conv2Layer,
-                                   conv3Layer,
-                                   conv4Layer,
-                                   conv5Layer});
+                                  {weightsLayer1,
+                                    biasLayer1,
+                                    conv1Layer,
+                                    weightsLayer2,
+                                    biasLayer2,
+                                    conv2Layer,
+                                    weightsLayer3,
+                                    biasLayer3,
+                                    conv3Layer,
+                                    weightsLayer4,
+                                    biasLayer4,
+                                    conv4Layer,
+                                    weightsLayer5,
+                                    biasLayer5,
+                                    conv5Layer});
 }
 
 // Creates a subgraph with some input unoptimizable layers ("unoptimizable" is added to the layer's name),
@@ -515,8 +724,17 @@
 {
     const TensorInfo inputInfo ({  1, 16, 16, 16 }, DataType::QAsymmU8, 1.0f, 0);
     const TensorInfo outputInfo({  1, 16, 16, 16 }, DataType::QAsymmU8, 1.0f, 0);
-    const TensorInfo weightInfo({ 16,  1,  1, 16 }, DataType::QAsymmU8, 0.9f, 0);
-    const TensorInfo biasInfo  ({  1,  1,  1, 16 }, DataType::Signed32,        0.9f, 0);
+    TensorInfo weightInfo({ 16,  1,  1, 16 }, DataType::QAsymmU8, 0.9f, 0);
+    TensorInfo biasInfo  ({  1,  1,  1, 16 }, DataType::Signed32,        0.9f, 0);
+
+    weightInfo.SetConstant(true);
+    biasInfo.SetConstant(true);
+
+    std::vector<float> weightsVector(64);
+    ConstTensor constWeightsTensor(weightInfo, weightsVector);
+
+    std::vector<float> biasVector(16);
+    ConstTensor constBiasTensor(biasInfo, biasVector);
 
     Convolution2dDescriptor convolutionDescriptor;
     convolutionDescriptor.m_StrideX     = 1;
@@ -527,6 +745,20 @@
     // Construct the graph
     Layer* const input1Layer = AddInputLayer(graph, "input1 layer", inputInfo, 0);
     Layer* const input2Layer = AddInputLayer(graph, "input2 layer", inputInfo, 1);
+
+    ConstantLayer* const weightsLayer1 =
+        AddConstantLayer(graph, layersInGraph, "Weights Layer 1", constWeightsTensor, outputInfo);
+    ConstantLayer* const biasLayer1 =
+        AddConstantLayer(graph, layersInGraph, "Bias Layer 1", constBiasTensor, outputInfo);
+    ConstantLayer* const weightsLayer2 =
+        AddConstantLayer(graph, layersInGraph, "Weights Layer 2 unoptimizable", constWeightsTensor, outputInfo);
+    ConstantLayer* const biasLayer2 =
+        AddConstantLayer(graph, layersInGraph, "Bias Layer 2 unoptimizable", constBiasTensor, outputInfo);
+    ConstantLayer* const weightsLayer3 =
+        AddConstantLayer(graph, layersInGraph, "Weights Layer 3", constWeightsTensor, outputInfo);
+    ConstantLayer* const biasLayer3 =
+        AddConstantLayer(graph, layersInGraph, "Bias Layer 3", constBiasTensor, outputInfo);
+
     Convolution2dLayer* const conv1Layer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
                                                                "conv1 layer", weightInfo, biasInfo, outputInfo);
     Convolution2dLayer* const conv2Layer = AddConvolutionLayer(graph, layersInGraph, convolutionDescriptor,
@@ -539,20 +771,35 @@
 
     // Connect the network
     input1Layer->GetOutputSlot(0).Connect(conv1Layer->GetInputSlot(0));
-    input2Layer->GetOutputSlot(0).Connect(conv2Layer->GetInputSlot(0));
+    weightsLayer1->GetOutputSlot(0).Connect(conv1Layer->GetInputSlot(1));
+    biasLayer1->GetOutputSlot(0).Connect(conv1Layer->GetInputSlot(2));
     conv1Layer->GetOutputSlot(0).Connect(addLayer->GetInputSlot(0));
+
+    input2Layer->GetOutputSlot(0).Connect(conv2Layer->GetInputSlot(0));
+    weightsLayer2->GetOutputSlot(0).Connect(conv2Layer->GetInputSlot(1));
+    biasLayer2->GetOutputSlot(0).Connect(conv2Layer->GetInputSlot(2));
     conv2Layer->GetOutputSlot(0).Connect(conv3Layer->GetInputSlot(0));
+    weightsLayer3->GetOutputSlot(0).Connect(conv3Layer->GetInputSlot(1));
+    biasLayer3->GetOutputSlot(0).Connect(conv3Layer->GetInputSlot(2));
     conv3Layer->GetOutputSlot(0).Connect(addLayer->GetInputSlot(1));
+
     addLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
 
     // Create the subgraph view for the whole network
+    std::vector<unsigned int> ignoreSlots = {1, 2};
     return CreateSubgraphViewFrom(CreateInputsFrom({conv1Layer,
-                                                    conv2Layer}),
+                                                    conv2Layer}, ignoreSlots),
                                   CreateOutputsFrom({addLayer}),
-                                  {conv1Layer,
-                                   conv2Layer,
-                                   conv3Layer,
-                                   addLayer});
+                                  { weightsLayer1,
+                                    biasLayer1,
+                                    weightsLayer2,
+                                    biasLayer2,
+                                    weightsLayer3,
+                                    biasLayer3,
+                                    conv1Layer,
+                                    conv2Layer,
+                                    conv3Layer,
+                                    addLayer });
 }
 
 // The input subgraph contains only a single unsupported layer (only convolutions are unsupported by the mock backend)
@@ -713,9 +960,11 @@
 
     CHECK(subgraphInputSlots.size()  == 1);
     CHECK(subgraphOutputSlots.size() == 1);
-    CHECK(subgraphLayers.size()      == 1);
+    CHECK(subgraphLayers.size()      == 3);
 
     CHECK(Contains(layersInGraph, "conv layer"));
+    CHECK(Contains(layersInGraph, "Weights Layer"));
+    CHECK(Contains(layersInGraph, "Bias Layer"));
 
     // Create a mock backend object
     MockBackendInitialiser initialiser; // Register the Mock Backend
@@ -776,15 +1025,25 @@
     const SubgraphView::IOutputSlots& subgraphOutputSlots = subgraphPtr->GetIOutputSlots();
     const SubgraphView::IConnectableLayers& subgraphLayers = subgraphPtr->GetIConnectableLayers();
 
-    CHECK(subgraphPtr->GetIInputSlots().size()  == 1);
-    CHECK(subgraphPtr->GetIOutputSlots().size() == 1);
-    CHECK(subgraphPtr->GetIConnectableLayers().size() == 5);
+    CHECK(subgraphInputSlots.size()  == 1);
+    CHECK(subgraphOutputSlots.size() == 1);
+    CHECK(subgraphPtr->GetIConnectableLayers().size() == 15);
 
     CHECK(Contains(layersInGraph, "conv1 layer"));
     CHECK(Contains(layersInGraph, "conv2 layer"));
     CHECK(Contains(layersInGraph, "conv3 layer"));
     CHECK(Contains(layersInGraph, "conv4 layer"));
     CHECK(Contains(layersInGraph, "conv5 layer"));
+    CHECK(Contains(layersInGraph, "Weights Layer 1"));
+    CHECK(Contains(layersInGraph, "Weights Layer 2"));
+    CHECK(Contains(layersInGraph, "Weights Layer 3"));
+    CHECK(Contains(layersInGraph, "Weights Layer 4"));
+    CHECK(Contains(layersInGraph, "Weights Layer 5"));
+    CHECK(Contains(layersInGraph, "Bias Layer 1"));
+    CHECK(Contains(layersInGraph, "Bias Layer 2"));
+    CHECK(Contains(layersInGraph, "Bias Layer 3"));
+    CHECK(Contains(layersInGraph, "Bias Layer 4"));
+    CHECK(Contains(layersInGraph, "Bias Layer 5"));
 
     // Create a mock backend object
     MockBackendInitialiser initialiser; // Register the Mock Backend
@@ -811,20 +1070,31 @@
     const OptimizationViews::Substitutions& substitutions = optimizationViews.GetSubstitutions();
     CHECK(substitutions.size() == 1);
 
-    std::list<IConnectableLayer*> expectedSubstitutableLayers{ layersInGraph.at("conv1 layer"),
+    std::list<IConnectableLayer*> expectedSubstitutableLayers{
+                                                   layersInGraph.at("Weights Layer 1"),
+                                                   layersInGraph.at("Weights Layer 2"),
+                                                   layersInGraph.at("Weights Layer 3"),
+                                                   layersInGraph.at("Weights Layer 4"),
+                                                   layersInGraph.at("Weights Layer 5"),
+                                                   layersInGraph.at("Bias Layer 1"),
+                                                   layersInGraph.at("Bias Layer 2"),
+                                                   layersInGraph.at("Bias Layer 3"),
+                                                   layersInGraph.at("Bias Layer 4"),
+                                                   layersInGraph.at("Bias Layer 5"),
+                                                   layersInGraph.at("conv1 layer"),
                                                    layersInGraph.at("conv2 layer"),
                                                    layersInGraph.at("conv3 layer"),
                                                    layersInGraph.at("conv4 layer"),
-                                                   layersInGraph.at("conv5 layer") };
+                                                   layersInGraph.at("conv5 layer")};
 
     const OptimizationViews::SubstitutionPair& substitution = substitutions.at(0);
 
-    CheckSubstitution(substitution,
-                      { subgraphInputSlots.size(), subgraphOutputSlots.size(), subgraphLayers.size() },
-                      { subgraphInputSlots.size(), subgraphOutputSlots.size(), 1 },
-                      subgraphInputSlots,
-                      subgraphOutputSlots,
-                      expectedSubstitutableLayers);
+    CheckSubstitution(
+        substitution,
+        {subgraphInputSlots.size(), subgraphOutputSlots.size(),
+         subgraphLayers.size()},
+        {subgraphInputSlots.size(), subgraphOutputSlots.size(), 1},
+        subgraphInputSlots, subgraphOutputSlots, expectedSubstitutableLayers);
 
     const SubgraphView::IConnectableLayers& substitutableSubgraphLayers =
             substitution.m_SubstitutableSubgraph.GetIConnectableLayers();
@@ -865,11 +1135,15 @@
 
     CHECK(subgraphInputSlots.size()  == 1);
     CHECK(subgraphOutputSlots.size() == 1);
-    CHECK(subgraphLayers.size()      == 5);
+    CHECK(subgraphLayers.size()      == 9);
 
+    CHECK(Contains(layersInGraph, "Weights Layer 1"));
+    CHECK(Contains(layersInGraph, "Bias Layer 1"));
     CHECK(Contains(layersInGraph, "conv1 layer"));
     CHECK(Contains(layersInGraph, "pooling1 layer"));
     CHECK(Contains(layersInGraph, "pooling2 layer"));
+    CHECK(Contains(layersInGraph, "Weights Layer 2"));
+    CHECK(Contains(layersInGraph, "Bias Layer 2"));
     CHECK(Contains(layersInGraph, "conv2 layer"));
     CHECK(Contains(layersInGraph, "pooling3 layer"));
 
@@ -903,16 +1177,16 @@
                       s2.m_SubstitutableSubgraph.GetIConnectableLayers().front()->GetName()) < 0;
     });
 
-    std::vector<ExpectedSubgraphSize> expectedSubstitutableSubgraphSizes{ { 1, 1, 1 },
-                                                                          { 1, 1, 1 } };
+    std::vector<ExpectedSubgraphSize> expectedSubstitutableSubgraphSizes{ { 1, 1, 3 },
+                                                                          { 1, 1, 3 } };
     std::vector<ExpectedSubgraphSize> expectedReplacementSubgraphSizes{ { 1, 1, 1 },
                                                                         { 1, 1, 1 } };
     std::vector<SubgraphView::IInputSlots> expectedSubstitutableInputSlots
     {
             ConvertSlotsToISlots<InputSlot, IInputSlot>(
-        ConvertReferenceTypeToPointerType(layersInGraph.at("conv1 layer")->GetInputSlots())),
+                {ConvertReferenceTypeToPointerType(layersInGraph.at("conv1 layer")->GetInputSlot(0))}),
             ConvertSlotsToISlots<InputSlot, IInputSlot>(
-        ConvertReferenceTypeToPointerType(layersInGraph.at("conv2 layer")->GetInputSlots()))
+                {ConvertReferenceTypeToPointerType(layersInGraph.at("conv2 layer")->GetInputSlot(0))})
     };
 
     std::vector<SubgraphView::IOutputSlots> expectedSubstitutableOutputSlots
@@ -924,8 +1198,8 @@
     };
     std::vector<SubgraphView::IConnectableLayers> expectedSubstitutableLayers
     {
-        { layersInGraph.at("conv1 layer") },
-        { layersInGraph.at("conv2 layer") }
+        { layersInGraph.at("Weights Layer 1"), layersInGraph.at("Bias Layer 1"), layersInGraph.at("conv1 layer") },
+        { layersInGraph.at("Weights Layer 2"), layersInGraph.at("Bias Layer 2"), layersInGraph.at("conv2 layer") }
     };
 
     for (size_t substitutionIndex = 0; substitutionIndex < substitutions.size(); substitutionIndex++)
@@ -1005,7 +1279,7 @@
 
     CHECK(subgraphInputSlots.size()  == 1);
     CHECK(subgraphOutputSlots.size() == 1);
-    CHECK(subgraphLayers.size()      == 1);
+    CHECK(subgraphLayers.size()      == 3);
 
     CHECK(Contains(layersInGraph, "conv layer unoptimizable"));
 
@@ -1047,9 +1321,9 @@
     CHECK(untouchedSubgraphs.size() == 1);
 
     CheckUntouchedSubgraph(untouchedSubgraphs.at(0),
-                           { subgraphInputSlots.size(), subgraphOutputSlots.size(), subgraphLayers.size() },
-                           subgraphInputSlots,
-                           subgraphOutputSlots,
+                           {subgraphInputSlots.size(),
+                            subgraphOutputSlots.size(), subgraphLayers.size()},
+                           subgraphInputSlots, subgraphOutputSlots,
                            subgraphLayers);
 }
 
@@ -1069,7 +1343,7 @@
 
     CHECK(subgraphInputSlots.size()  == 1);
     CHECK(subgraphOutputSlots.size() == 1);
-    CHECK(subgraphLayers.size()      == 5);
+    CHECK(subgraphLayers.size()      == 15);
 
     CHECK(Contains(layersInGraph, "conv1 layer"));
     CHECK(Contains(layersInGraph, "conv2 layer unoptimizable"));
@@ -1107,20 +1381,20 @@
         { return strcmp(s1.m_SubstitutableSubgraph.GetIConnectableLayers().front()->GetName(),
                         s2.m_SubstitutableSubgraph.GetIConnectableLayers().front()->GetName()) < 0; });
 
-    std::vector<ExpectedSubgraphSize> expectedSubstitutableSubgraphSizes{ { 1, 1, 1 },
-                                                                          { 1, 1, 1 },
-                                                                          { 1, 1, 1 } };
+    std::vector<ExpectedSubgraphSize> expectedSubstitutableSubgraphSizes{ { 1, 1, 3 },
+                                                                          { 1, 1, 3 },
+                                                                          { 1, 1, 3 } };
     std::vector<ExpectedSubgraphSize> expectedReplacementSubgraphSizes{ { 1, 1, 1 },
                                                                         { 1, 1, 1 },
                                                                         { 1, 1, 1 } };
     std::vector<SubgraphView::IInputSlots> expectedSubstitutableInputSlots
     {
         ConvertSlotsToISlots<InputSlot, IInputSlot>(
-        ConvertReferenceTypeToPointerType(layersInGraph.at("conv1 layer")->GetInputSlots())),
+            {ConvertReferenceTypeToPointerType(layersInGraph.at("conv1 layer")->GetInputSlot(0))}),
         ConvertSlotsToISlots<InputSlot, IInputSlot>(
-        ConvertReferenceTypeToPointerType(layersInGraph.at("conv3 layer")->GetInputSlots())),
+        {ConvertReferenceTypeToPointerType(layersInGraph.at("conv3 layer")->GetInputSlot(0))}),
         ConvertSlotsToISlots<InputSlot, IInputSlot>(
-        ConvertReferenceTypeToPointerType(layersInGraph.at("conv5 layer")->GetInputSlots()))
+        {ConvertReferenceTypeToPointerType(layersInGraph.at("conv5 layer")->GetInputSlot(0))})
     };
     std::vector<SubgraphView::IOutputSlots> expectedSubstitutableOutputSlots
     {
@@ -1133,9 +1407,9 @@
     };
     std::vector<SubgraphView::IConnectableLayers> expectedSubstitutableLayers
     {
-        { layersInGraph.at("conv1 layer") },
-        { layersInGraph.at("conv3 layer") },
-        { layersInGraph.at("conv5 layer") }
+        { layersInGraph.at("Weights Layer 1"), layersInGraph.at("Bias Layer 1"), layersInGraph.at("conv1 layer") },
+        { layersInGraph.at("Weights Layer 3"), layersInGraph.at("Bias Layer 3"), layersInGraph.at("conv3 layer") },
+        { layersInGraph.at("Weights Layer 5"), layersInGraph.at("Bias Layer 5"), layersInGraph.at("conv5 layer") }
     };
 
     for (size_t substitutionIndex = 0; substitutionIndex < substitutions.size(); substitutionIndex++)
@@ -1166,27 +1440,33 @@
                       s2.GetIConnectableLayers().front()->GetName()) < 0;
     });
 
-    std::vector<ExpectedSubgraphSize> expectedUntouchedSubgraphSizes{ { 1, 1, 1 },
-                                                                      { 1, 1, 1 } };
-    std::vector<SubgraphView::IInputSlots> expectedUntouchedInputSlots
-    {
-            ConvertSlotsToISlots<InputSlot, IInputSlot>(
-        ConvertReferenceTypeToPointerType(layersInGraph.at("conv2 layer unoptimizable")->GetInputSlots())),
-            ConvertSlotsToISlots<InputSlot, IInputSlot>(
-        ConvertReferenceTypeToPointerType(layersInGraph.at("conv4 layer unoptimizable")->GetInputSlots()))
-    };
+    std::vector<ExpectedSubgraphSize> expectedUntouchedSubgraphSizes{ { 1, 1, 3 },
+                                                                      { 1, 1, 3 } };
+    std::vector<SubgraphView::IInputSlots> expectedUntouchedInputSlots{
+        ConvertSlotsToISlots<InputSlot,
+                             IInputSlot>({ConvertReferenceTypeToPointerType(
+            layersInGraph.at("conv2 layer unoptimizable")->GetInputSlot(0))}),
+        ConvertSlotsToISlots<InputSlot,
+                             IInputSlot>({ConvertReferenceTypeToPointerType(
+            layersInGraph.at("conv4 layer unoptimizable")->GetInputSlot(0))})};
+
     std::vector<SubgraphView::IOutputSlots> expectedUntouchedOutputSlots
-    {
+        {
             ConvertSlotsToISlots<OutputSlot, IOutputSlot>(
-        ConvertReferenceTypeToPointerType(layersInGraph.at("conv2 layer unoptimizable")->GetOutputSlots())),
+                ConvertReferenceTypeToPointerType(layersInGraph.at("conv2 layer unoptimizable")->GetOutputSlots())),
             ConvertSlotsToISlots<OutputSlot, IOutputSlot>(
-        ConvertReferenceTypeToPointerType(layersInGraph.at("conv4 layer unoptimizable")->GetOutputSlots()))
-    };
+                ConvertReferenceTypeToPointerType(layersInGraph.at("conv4 layer unoptimizable")->GetOutputSlots()))
+        };
+
     std::vector<SubgraphView::IConnectableLayers> expectedUntouchedLayers
-    {
-        { layersInGraph.at("conv2 layer unoptimizable") },
-        { layersInGraph.at("conv4 layer unoptimizable") }
-    };
+        {
+            { layersInGraph.at("Weights Layer 2 unoptimizable"),
+              layersInGraph.at("Bias Layer 2 unoptimizable"),
+              layersInGraph.at("conv2 layer unoptimizable") },
+            { layersInGraph.at("Weights Layer 4 unoptimizable"),
+              layersInGraph.at("Bias Layer 4 unoptimizable"),
+              layersInGraph.at("conv4 layer unoptimizable") }
+        };
 
     for (size_t untouchedIndex = 0; untouchedIndex < untouchedSubgraphs.size(); untouchedIndex++)
     {
@@ -1215,7 +1495,7 @@
 
     CHECK(subgraphInputSlots.size()  == 2);
     CHECK(subgraphOutputSlots.size() == 1);
-    CHECK(subgraphLayers.size()      == 4);
+    CHECK(subgraphLayers.size()      == 10);
 
     CHECK(Contains(layersInGraph, "conv1 layer"));
     CHECK(Contains(layersInGraph, "conv2 layer unoptimizable"));
@@ -1247,7 +1527,7 @@
     const OptimizationViews::Substitutions& substitutions = optimizationViews.GetSubstitutions();
     CHECK(substitutions.size() == 1);
 
-    ExpectedSubgraphSize expectedSubstitutableSubgraphSizes{ 2, 1, 3 };
+    ExpectedSubgraphSize expectedSubstitutableSubgraphSizes{ 2, 1, 7 };
     ExpectedSubgraphSize expectedReplacementSubgraphSizes{ 2, 1, 1 };
 
     SubgraphView::IInputSlots expectedSubstitutableInputSlots
@@ -1266,6 +1546,10 @@
 
     SubgraphView::IConnectableLayers expectedSubstitutableLayers
     {
+        layersInGraph.at("Weights Layer 1"),
+        layersInGraph.at("Weights Layer 3"),
+        layersInGraph.at("Bias Layer 1"),
+        layersInGraph.at("Bias Layer 3"),
         layersInGraph.at("conv1 layer"),
         layersInGraph.at("conv3 layer"),
         layersInGraph.at("add layer")
@@ -1291,12 +1575,12 @@
     const OptimizationViews::Subgraphs& untouchedSubgraphs = optimizationViews.GetUntouchedSubgraphs();
     CHECK(untouchedSubgraphs.size() == 1);
 
-    std::vector<ExpectedSubgraphSize> expectedUntouchedSubgraphSizes{ { 1, 1, 1 } };
+    std::vector<ExpectedSubgraphSize> expectedUntouchedSubgraphSizes{ { 1, 1, 3 } };
     std::vector<SubgraphView::IInputSlots> expectedUntouchedInputSlots
     {
-            ConvertSlotsToISlots<InputSlot, IInputSlot>(
-        ConvertReferenceTypeToPointerType(layersInGraph.at("conv2 layer unoptimizable")->GetInputSlots()))
-    };
+        ConvertSlotsToISlots<InputSlot,
+                             IInputSlot>({ConvertReferenceTypeToPointerType(
+            layersInGraph.at("conv2 layer unoptimizable")->GetInputSlot(0))})};
     std::vector<SubgraphView::IOutputSlots> expectedUntouchedOutputSlots
     {
             ConvertSlotsToISlots<OutputSlot, IOutputSlot>(
@@ -1304,7 +1588,8 @@
     };
     std::vector<SubgraphView::IConnectableLayers> expectedUntouchedLayers
     {
-        { layersInGraph.at("conv2 layer unoptimizable") }
+        { layersInGraph.at("conv2 layer unoptimizable"), layersInGraph.at("Weights Layer 2 unoptimizable"),
+        layersInGraph.at("Bias Layer 2 unoptimizable") }
     };
 
     for (size_t untouchedIndex = 0; untouchedIndex < untouchedSubgraphs.size(); untouchedIndex++)
diff --git a/src/backends/backendsCommon/test/OptimizedNetworkTests.cpp b/src/backends/backendsCommon/test/OptimizedNetworkTests.cpp
index cc79741..8e3b275 100644
--- a/src/backends/backendsCommon/test/OptimizedNetworkTests.cpp
+++ b/src/backends/backendsCommon/test/OptimizedNetworkTests.cpp
@@ -401,11 +401,14 @@
 
     armnn::INetworkPtr network = armnn::INetwork::Create();
     armnn::IConnectableLayer* const inputLayer  = network->AddInputLayer(0);
+
+    ARMNN_NO_DEPRECATE_WARN_BEGIN
     armnn::IConnectableLayer* const convLayer   =
             network->AddConvolution2dLayer(descriptor,
                                            weights,
                                            armnn::Optional<armnn::ConstTensor>(biases),
                                            layerName.c_str());
+    ARMNN_NO_DEPRECATE_WARN_END
     armnn::IConnectableLayer* const outputLayer = network->AddOutputLayer(0);
 
     inputLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(0));
diff --git a/src/backends/backendsCommon/test/WorkloadDataValidation.cpp b/src/backends/backendsCommon/test/WorkloadDataValidation.cpp
index c715d28..fed21eb 100644
--- a/src/backends/backendsCommon/test/WorkloadDataValidation.cpp
+++ b/src/backends/backendsCommon/test/WorkloadDataValidation.cpp
@@ -642,7 +642,7 @@
     CHECK_NOTHROW(data.Validate(info));
 }
 
-TEST_CASE("BiasPerAxisQuantization_Validate")
+TEST_CASE("BiasPerAxisQuantization_ValidateCorrectValues")
 {
     constexpr unsigned int nInput  = 1u;
     constexpr unsigned int cInput  = 3u;
@@ -675,6 +675,7 @@
 
     WorkloadInfo workloadInfo;
     AddInputToWorkload(queueDescriptor, workloadInfo, inputInfo, nullptr);
+    AddInputToWorkload(queueDescriptor, workloadInfo, weightInfo, nullptr);
     AddOutputToWorkload(queueDescriptor, workloadInfo, outputInfo, nullptr);
 
     ScopedTensorHandle weightTensor(weightInfo);
@@ -687,17 +688,102 @@
     ScopedTensorHandle biasHandle1(biasInfo1);
     queueDescriptor.m_Bias = &biasHandle1;
 
-    CHECK_NOTHROW(queueDescriptor.Validate(workloadInfo));
+    AddInputToWorkload(queueDescriptor, workloadInfo, biasInfo1, nullptr);
 
-    // Test 2: wrong per-axis quantization values
+    CHECK_NOTHROW(queueDescriptor.Validate(workloadInfo));
+}
+
+TEST_CASE("BiasPerAxisQuantization_ValidateIncorrectValues")
+{
+    constexpr unsigned int nInput  = 1u;
+    constexpr unsigned int cInput  = 3u;
+    constexpr unsigned int hInput  = 3u;
+    constexpr unsigned int wInput  = 3u;
+
+    constexpr unsigned int nOutput = nInput;
+    constexpr unsigned int cOutput = cInput;
+    constexpr unsigned int hOutput = 1u;
+    constexpr unsigned int wOutput = 1u;
+
+    const TensorShape inputShape { nInput,  cInput,  hInput,  wInput  };
+    const TensorShape outputShape{ nOutput, cOutput, hOutput, wOutput };
+    const TensorShape weightShape{ cOutput, cInput,  hInput,  wInput  };
+    const TensorShape biasShape  { cOutput                            };
+
+    constexpr DataType inputType  = DataType::QAsymmU8;
+    constexpr DataType weightType = DataType::QSymmS8;
+    constexpr DataType biasType   = DataType::Signed32;
+
+    constexpr float perTensorScale = 1.5f;
+    const TensorInfo inputInfo (inputShape,  inputType, perTensorScale);
+    const TensorInfo outputInfo(outputShape, inputType, perTensorScale);
+
+    const std::vector<float> weightPerAxisScales = { 2.50f, 3.50f };
+    const TensorInfo weightInfo(weightShape, weightType, weightPerAxisScales, 0);
+
+    Convolution2dQueueDescriptor queueDescriptor;
+    queueDescriptor.m_Parameters.m_BiasEnabled = true;
+
+    WorkloadInfo workloadInfo;
+    AddInputToWorkload(queueDescriptor, workloadInfo, inputInfo, nullptr);
+    AddInputToWorkload(queueDescriptor, workloadInfo, weightInfo, nullptr);
+    AddOutputToWorkload(queueDescriptor, workloadInfo, outputInfo, nullptr);
+
+    ScopedTensorHandle weightTensor(weightInfo);
+    queueDescriptor.m_Weight = &weightTensor;
+
+   // Test 2: wrong per-axis quantization values
     const std::vector<float> biasPerAxisScales2 = { 4.00f, 5.00f };
     const TensorInfo biasInfo2(biasShape, biasType, biasPerAxisScales2, 0);
 
     ScopedTensorHandle biasHandle2(biasInfo2);
     queueDescriptor.m_Bias = &biasHandle2;
 
+    AddInputToWorkload(queueDescriptor, workloadInfo, biasInfo2, nullptr);
+
     CHECK_NOTHROW(queueDescriptor.Validate(workloadInfo));
 
+}
+
+TEST_CASE("BiasPerAxisQuantization_ValidateInvalidArgumentException")
+{
+    constexpr unsigned int nInput  = 1u;
+    constexpr unsigned int cInput  = 3u;
+    constexpr unsigned int hInput  = 3u;
+    constexpr unsigned int wInput  = 3u;
+
+    constexpr unsigned int nOutput = nInput;
+    constexpr unsigned int cOutput = cInput;
+    constexpr unsigned int hOutput = 1u;
+    constexpr unsigned int wOutput = 1u;
+
+    const TensorShape inputShape { nInput,  cInput,  hInput,  wInput  };
+    const TensorShape outputShape{ nOutput, cOutput, hOutput, wOutput };
+    const TensorShape weightShape{ cOutput, cInput,  hInput,  wInput  };
+    const TensorShape biasShape  { cOutput                            };
+
+    constexpr DataType inputType  = DataType::QAsymmU8;
+    constexpr DataType weightType = DataType::QSymmS8;
+    constexpr DataType biasType   = DataType::Signed32;
+
+    constexpr float perTensorScale = 1.5f;
+    const TensorInfo inputInfo (inputShape,  inputType, perTensorScale);
+    const TensorInfo outputInfo(outputShape, inputType, perTensorScale);
+
+    const std::vector<float> weightPerAxisScales = { 2.50f, 3.50f };
+    const TensorInfo weightInfo(weightShape, weightType, weightPerAxisScales, 0);
+
+    Convolution2dQueueDescriptor queueDescriptor;
+    queueDescriptor.m_Parameters.m_BiasEnabled = true;
+
+    WorkloadInfo workloadInfo;
+    AddInputToWorkload(queueDescriptor, workloadInfo, inputInfo, nullptr);
+    AddInputToWorkload(queueDescriptor, workloadInfo, weightInfo, nullptr);
+    AddOutputToWorkload(queueDescriptor, workloadInfo, outputInfo, nullptr);
+
+    ScopedTensorHandle weightTensor(weightInfo);
+    queueDescriptor.m_Weight = &weightTensor;
+
     // Test 3: mismatched number of quantization scales
     const std::vector<float> biasPerAxisScales3 = { 3.75f, 5.25f, 5.25f };
     const TensorInfo biasInfo3(biasShape, biasType, biasPerAxisScales3, 0);
@@ -705,7 +791,10 @@
     ScopedTensorHandle biasHandle3(biasInfo3);
     queueDescriptor.m_Bias = &biasHandle3;
 
+    AddInputToWorkload(queueDescriptor, workloadInfo, biasInfo3, nullptr);
+
     CHECK_THROWS_AS(queueDescriptor.Validate(workloadInfo), InvalidArgumentException);
 }
 
+
 }
diff --git a/src/backends/backendsCommon/test/layerTests/Conv2dTestImpl.cpp b/src/backends/backendsCommon/test/layerTests/Conv2dTestImpl.cpp
index 74c65e2..1e0adc1 100644
--- a/src/backends/backendsCommon/test/layerTests/Conv2dTestImpl.cpp
+++ b/src/backends/backendsCommon/test/layerTests/Conv2dTestImpl.cpp
@@ -309,6 +309,7 @@
 
     std::unique_ptr<armnn::ITensorHandle> inputHandle = tensorHandleFactory.CreateTensorHandle(inputTensorInfo);
     std::unique_ptr<armnn::ITensorHandle> outputHandle = tensorHandleFactory.CreateTensorHandle(outputTensorInfo);
+    std::unique_ptr<armnn::ITensorHandle> weightsHandle = tensorHandleFactory.CreateTensorHandle(kernelDesc);
 
     armnn::Convolution2dQueueDescriptor data;
     armnn::WorkloadInfo info;
@@ -329,8 +330,15 @@
     }
 
     AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
+    AddInputToWorkload(data, info, kernelDesc, weightsHandle.get());
     AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
 
+    std::unique_ptr<armnn::ITensorHandle> biasHandle = nullptr;
+    if (biasEnabled)
+    {
+        biasHandle = tensorHandleFactory.CreateTensorHandle(biasDesc);
+        AddInputToWorkload(data, info, biasDesc, biasHandle.get());
+    }
     data.m_Weight = &weightsTensor;
     data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - can be a source of bugs.
     data.m_Parameters.m_StrideX = strideX;
@@ -349,8 +357,16 @@
                                                                                 info);
     inputHandle->Allocate();
     outputHandle->Allocate();
+    weightsHandle->Allocate();
+
+    if (biasEnabled)
+    {
+        biasHandle->Allocate();
+        CopyDataToITensorHandle(biasHandle.get(), bias.data());
+    }
 
     CopyDataToITensorHandle(inputHandle.get(), inputData.data());
+    CopyDataToITensorHandle(weightsHandle.get(), kernel.data());
 
     ExecuteWorkload(*workload, memoryManager);
 
@@ -423,6 +439,8 @@
 
     std::unique_ptr<armnn::ITensorHandle> inputHandle = tensorHandleFactory.CreateTensorHandle(inputTensorInfo);
     std::unique_ptr<armnn::ITensorHandle> outputHandle = tensorHandleFactory.CreateTensorHandle(outputTensorInfo);
+    std::unique_ptr<armnn::ITensorHandle> weightsHandle = tensorHandleFactory.CreateTensorHandle(kernelDesc);
+    std::unique_ptr<armnn::ITensorHandle> biasHandle = nullptr;
 
     armnn::ScopedTensorHandle weightsTensor(kernelDesc);
     AllocateAndCopyDataToITensorHandle(&weightsTensor, kernel.data());
@@ -444,15 +462,30 @@
 
     armnn::WorkloadInfo info;
     AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
+    AddInputToWorkload(data, info, kernelDesc, weightsHandle.get());
     AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
 
+    if (biasEnabled)
+    {
+        biasHandle = tensorHandleFactory.CreateTensorHandle(biasDesc);
+        AddInputToWorkload(data, info, biasDesc, biasHandle.get());
+    }
+
     std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateWorkload(armnn::LayerType::Convolution2d,
                                                                                 data,
                                                                                 info);
     inputHandle->Allocate();
     outputHandle->Allocate();
+    weightsHandle->Allocate();
+
+    if (biasEnabled)
+    {
+        biasHandle->Allocate();
+        CopyDataToITensorHandle(biasHandle.get(), bias.data());
+    }
 
     CopyDataToITensorHandle(inputHandle.get(), inputData.data());
+    CopyDataToITensorHandle(weightsHandle.get(), kernel.data());
 
     ExecuteWorkload(*workload, memoryManager);
 
@@ -552,35 +585,52 @@
 
     std::unique_ptr<armnn::ITensorHandle> inputHandle  = tensorHandleFactory.CreateTensorHandle(inputInfo);
     std::unique_ptr<armnn::ITensorHandle> outputHandle = tensorHandleFactory.CreateTensorHandle(outputInfo);
+    std::unique_ptr<armnn::ITensorHandle> weightsHandle = tensorHandleFactory.CreateTensorHandle(kernelInfo);
+    std::unique_ptr<armnn::ITensorHandle> biasHandle = nullptr;
 
     armnn::Convolution2dQueueDescriptor data;
     armnn::WorkloadInfo info;
-    armnn::ScopedTensorHandle         weightsTensor(kernelInfo);
-    armnn::ScopedTensorHandle         biasTensor(biasInfo);
+    armnn::ScopedTensorHandle weightsTensor(kernelInfo);
+    armnn::ScopedTensorHandle biasTensor(biasInfo);
 
     AllocateAndCopyDataToITensorHandle(&weightsTensor, kernelData.data());
     AllocateAndCopyDataToITensorHandle(&biasTensor, biasData.data());
 
     AddInputToWorkload(data, info, inputInfo, inputHandle.get());
+    AddInputToWorkload(data, info, kernelInfo, weightsHandle.get());
     AddOutputToWorkload(data, info, outputInfo, outputHandle.get());
 
-    data.m_Weight         = &weightsTensor;
-    data.m_Bias           = &biasTensor;
-    data.m_Parameters.m_StrideX        = 1;
-    data.m_Parameters.m_StrideY        = stride;
-    data.m_Parameters.m_PadLeft        = 0;
-    data.m_Parameters.m_PadRight       = 0;
-    data.m_Parameters.m_PadTop         = padSize;
-    data.m_Parameters.m_PadBottom      = padSize;
-    data.m_Parameters.m_BiasEnabled    = biasEnabled;
+    data.m_Weight = &weightsTensor;
+    data.m_Bias = &biasTensor;
+    data.m_Parameters.m_StrideX = 1;
+    data.m_Parameters.m_StrideY = stride;
+    data.m_Parameters.m_PadLeft = 0;
+    data.m_Parameters.m_PadRight = 0;
+    data.m_Parameters.m_PadTop = padSize;
+    data.m_Parameters.m_PadBottom = padSize;
+    data.m_Parameters.m_BiasEnabled = biasEnabled;
+
+    if (biasEnabled)
+    {
+        biasHandle = tensorHandleFactory.CreateTensorHandle(biasInfo);
+        AddInputToWorkload(data, info, biasInfo, biasHandle.get());
+    }
 
     std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateWorkload(armnn::LayerType::Convolution2d,
                                                                                 data,
                                                                                 info);
     inputHandle->Allocate();
     outputHandle->Allocate();
+    weightsHandle->Allocate();
+
+    if (biasEnabled)
+    {
+        biasHandle->Allocate();
+        CopyDataToITensorHandle(biasHandle.get(), biasData.data());
+    }
 
     CopyDataToITensorHandle(inputHandle.get(), inputData.data());
+    CopyDataToITensorHandle(weightsHandle.get(), kernelData.data());
 
     ExecuteWorkload(*workload, memoryManager);
 
@@ -1364,18 +1414,30 @@
     std::vector<T> expectedOutput(outputTensorInfo.GetNumElements());
 
     std::unique_ptr<armnn::ITensorHandle> inputHandle = tensorHandleFactory.CreateTensorHandle(inputTensorInfo);
+    std::unique_ptr<armnn::ITensorHandle> biasHandle = tensorHandleFactory.CreateTensorHandle(biasDesc);
+    std::unique_ptr<armnn::ITensorHandle> weightsHandle = tensorHandleFactory.CreateTensorHandle(kernelDesc);
     std::unique_ptr<armnn::ITensorHandle> outputHandle = tensorHandleFactory.CreateTensorHandle(outputTensorInfo);
 
     armnn::Convolution2dQueueDescriptor data;
     armnn::WorkloadInfo info;
+
     armnn::ScopedTensorHandle weightsTensor(kernelDesc);
     armnn::ScopedTensorHandle biasTensor(biasDesc);
 
+    AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
+    AddInputToWorkload(data, info, kernelDesc, weightsHandle.get());
+    AddInputToWorkload(data, info, biasDesc, biasHandle.get());
+    AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
+
+    // AllocateAndCopyDataToITensorHandle() is required twice for the weights AND biases:
+    // See comment in DepthwiseConvolution2dAsymmetricTestImpl() for reasons.
+    // 1) ScopedTensorHandle (weightsTensor) required for QueueDescriptor (data.m_Weight).
+    // 2) ITensorHandle (converts to Backend TensorHandle) required in RefWorkload for GetTensorInfo() method.
+    AllocateAndCopyDataToITensorHandle(weightsHandle.get(), kernel.data());
     AllocateAndCopyDataToITensorHandle(&weightsTensor, kernel.data());
+    AllocateAndCopyDataToITensorHandle(biasHandle.get(), bias.data());
     AllocateAndCopyDataToITensorHandle(&biasTensor, bias.data());
 
-    AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
-    AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
     data.m_Weight = &weightsTensor;
     data.m_Bias = &biasTensor;
     data.m_Parameters.m_StrideX = strideX;
@@ -1387,11 +1449,15 @@
     data.m_Parameters.m_BiasEnabled = true;
 
     std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refTensorHandleFactory.CreateTensorHandle(outputTensorInfo);
+    std::unique_ptr<armnn::ITensorHandle> weightsHandleRef = refTensorHandleFactory.CreateTensorHandle(kernelDesc);
+    std::unique_ptr<armnn::ITensorHandle> biasHandleRef = refTensorHandleFactory.CreateTensorHandle(biasDesc);
     std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refTensorHandleFactory.CreateTensorHandle(inputTensorInfo);
 
     armnn::Convolution2dQueueDescriptor refData = data;
     armnn::WorkloadInfo                 refInfo = info;
     SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
+    SetWorkloadInput(refData, refInfo, 1, kernelDesc, weightsHandleRef.get());
+    SetWorkloadInput(refData, refInfo, 2, biasDesc, biasHandleRef.get());
     SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
 
     std::unique_ptr<armnn::IWorkload> workload
@@ -1401,12 +1467,16 @@
 
     outputHandleRef->Allocate();
     inputHandleRef->Allocate();
+    weightsHandleRef->Allocate();
+    biasHandleRef->Allocate();
 
     inputHandle->Allocate();
     outputHandle->Allocate();
 
     CopyDataToITensorHandle(inputHandle.get(), input.data());
     CopyDataToITensorHandle(inputHandleRef.get(), input.data());
+    CopyDataToITensorHandle(weightsHandleRef.get(), kernel.data());
+    CopyDataToITensorHandle(biasHandleRef.get(), bias.data());
 
     ExecuteWorkload(*workload, memoryManager);
 
@@ -3622,6 +3692,8 @@
 
     std::unique_ptr<ITensorHandle> inputHandle  = tensorHandleFactory.CreateTensorHandle(inputInfo);
     std::unique_ptr<ITensorHandle> outputHandle = tensorHandleFactory.CreateTensorHandle(outputInfo);
+    std::unique_ptr<armnn::ITensorHandle> weightsHandle = tensorHandleFactory.CreateTensorHandle(kernelInfo);
+    std::unique_ptr<armnn::ITensorHandle> biasHandle = nullptr;
 
     WorkloadInfo workloadInfo;
     ScopedTensorHandle weightTensor(kernelInfo);
@@ -3636,6 +3708,14 @@
     queueDescriptor.m_Bias       = &biasTensor;
 
     AddInputToWorkload(queueDescriptor, workloadInfo, inputInfo, inputHandle.get());
+    AddInputToWorkload(queueDescriptor, workloadInfo, kernelInfo, weightsHandle.get());
+
+    if (descriptor.m_BiasEnabled)
+    {
+        biasHandle = tensorHandleFactory.CreateTensorHandle(biasInfo);
+        AddInputToWorkload(queueDescriptor, workloadInfo, biasInfo, biasHandle.get());
+    }
+
     AddOutputToWorkload(queueDescriptor, workloadInfo, outputInfo, outputHandle.get());
 
     std::unique_ptr<IWorkload> workload= workloadFactory.CreateWorkload(armnn::LayerType::Convolution2d,
@@ -3643,8 +3723,16 @@
                                                                         workloadInfo);
     inputHandle->Allocate();
     outputHandle->Allocate();
+    weightsHandle->Allocate();
 
+    if (descriptor.m_BiasEnabled)
+    {
+        biasHandle->Allocate();
+        CopyDataToITensorHandle(biasHandle.get(), biasData.data());
+    }
     CopyDataToITensorHandle(inputHandle.get(), inputData.data());
+    CopyDataToITensorHandle(weightsHandle.get(), kernelData.data());
+
 
     ExecuteWorkload(*workload, memoryManager);
 
diff --git a/src/backends/cl/ClBackend.cpp b/src/backends/cl/ClBackend.cpp
index 47990d8..bd1b94e 100644
--- a/src/backends/cl/ClBackend.cpp
+++ b/src/backends/cl/ClBackend.cpp
@@ -341,14 +341,14 @@
 
                                 if (baseLayer->GetParameters().m_BiasEnabled)
                                 {
-                                    biases = baseLayer->m_Bias->GetTensorInfo();
+                                    biases = baseLayer->GetInputSlot(2).GetConnectedOutputSlot()->GetTensorInfo();
                                 }
 
                                 arm_compute::Status status = ClConvolution2dWorkloadValidate(
                                         baseLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo(),
                                         activationLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo(),
                                         baseLayer->GetParameters(),
-                                        baseLayer->m_Weight->GetTensorInfo(),
+                                        baseLayer->GetInputSlot(1).GetConnectedOutputSlot()->GetTensorInfo(),
                                         biases,
                                         isFastMathEnabled,
                                         &activationDesc);
diff --git a/src/backends/cl/test/ClCreateWorkloadTests.cpp b/src/backends/cl/test/ClCreateWorkloadTests.cpp
index 4f53b92..3a757f8 100644
--- a/src/backends/cl/test/ClCreateWorkloadTests.cpp
+++ b/src/backends/cl/test/ClCreateWorkloadTests.cpp
@@ -471,6 +471,8 @@
     auto tensorHandleFactory = ClWorkloadFactoryHelper::GetTensorHandleFactory(memoryManager);
 
     std::unique_ptr<ITensorHandle> inputHandle  = tensorHandleFactory.CreateTensorHandle(inputInfo);
+    std::unique_ptr<armnn::ITensorHandle> weightsHandle = tensorHandleFactory.CreateTensorHandle(kernelInfo);
+    std::unique_ptr<armnn::ITensorHandle> biasHandle = tensorHandleFactory.CreateTensorHandle(biasInfo);
     std::unique_ptr<ITensorHandle> outputHandle = tensorHandleFactory.CreateTensorHandle(outputInfo);
 
 
@@ -487,6 +489,8 @@
     queueDescriptor.m_Bias       = &biasTensor;
 
     AddInputToWorkload(queueDescriptor, workloadInfo, inputInfo, inputHandle.get());
+    AddInputToWorkload(queueDescriptor, workloadInfo, kernelInfo, weightsHandle.get());
+    AddInputToWorkload(queueDescriptor, workloadInfo, biasInfo, biasHandle.get());
     AddOutputToWorkload(queueDescriptor, workloadInfo, outputInfo, outputHandle.get());
 
     // Initialize our m_CLCompileContext using default device and context
diff --git a/src/backends/cl/test/ClImportTensorHandleTests.cpp b/src/backends/cl/test/ClImportTensorHandleTests.cpp
index e10e81a..9bfd1fb 100644
--- a/src/backends/cl/test/ClImportTensorHandleTests.cpp
+++ b/src/backends/cl/test/ClImportTensorHandleTests.cpp
@@ -11,7 +11,6 @@
 
 #include <doctest/doctest.h>
 
-
 #include <armnn/IRuntime.hpp>
 #include <armnn/INetwork.hpp>
 #include "Network.hpp"
@@ -320,10 +319,13 @@
     convDesc2d.m_PadTop = 1;
     convDesc2d.m_PadBottom = 1;
     convDesc2d.m_DataLayout = DataLayout::NHWC;
+
+    ARMNN_NO_DEPRECATE_WARN_BEGIN
     armnn::IConnectableLayer* const convLayer = network->AddConvolution2dLayer(convDesc2d,
                                                                           weights,
                                                                           armnn::EmptyOptional(),
                                                                           "conv");
+    ARMNN_NO_DEPRECATE_WARN_END
     ARMNN_ASSERT(convLayer);
 
     inputLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(0));
@@ -876,10 +878,12 @@
     convDesc2d.m_PadTop = 1;
     convDesc2d.m_PadBottom = 1;
     convDesc2d.m_DataLayout = DataLayout::NHWC;
+    ARMNN_NO_DEPRECATE_WARN_BEGIN
     armnn::IConnectableLayer* const convLayer = network->AddConvolution2dLayer(convDesc2d,
                                                                           weights,
                                                                           armnn::EmptyOptional(),
                                                                           "conv");
+    ARMNN_NO_DEPRECATE_WARN_END
     ARMNN_ASSERT(convLayer);
 
     inputLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(0));
@@ -1094,10 +1098,12 @@
     convDesc2d.m_PadTop = 1;
     convDesc2d.m_PadBottom = 1;
     convDesc2d.m_DataLayout = DataLayout::NHWC;
+    ARMNN_NO_DEPRECATE_WARN_BEGIN
     armnn::IConnectableLayer* const convLayer = network->AddConvolution2dLayer(convDesc2d,
                                                                           weights,
                                                                           armnn::EmptyOptional(),
                                                                           "conv");
+    ARMNN_NO_DEPRECATE_WARN_END
     ARMNN_ASSERT(convLayer);
 
     inputLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(0));
diff --git a/src/backends/cl/workloads/ClConvolution2dWorkload.cpp b/src/backends/cl/workloads/ClConvolution2dWorkload.cpp
index bf82fbf..e3d679a 100644
--- a/src/backends/cl/workloads/ClConvolution2dWorkload.cpp
+++ b/src/backends/cl/workloads/ClConvolution2dWorkload.cpp
@@ -28,6 +28,15 @@
                                                     bool isFastMathEnabled,
                                                     const ActivationDescriptor* activationDescriptor)
 {
+    // The implemented workload does support both const and non const
+    // weights. However, in the case of non const weights we'd have to call
+    // prepare or configure for each inference which we're not setup to do just yet.
+    if (!weights.IsConstant())
+    {
+        return arm_compute::Status{arm_compute::ErrorCode::RUNTIME_ERROR,
+                                   "ArmNN ClConvolution2dWorkload does not support non constant weights."};
+    }
+
     const arm_compute::TensorInfo aclInputInfo = BuildArmComputeTensorInfo(input, descriptor.m_DataLayout);
     const arm_compute::TensorInfo aclOutputInfo = BuildArmComputeTensorInfo(output, descriptor.m_DataLayout);
     const arm_compute::TensorInfo aclWeightsInfo = BuildArmComputeTensorInfo(weights, descriptor.m_DataLayout);
@@ -41,7 +50,12 @@
     if (descriptor.m_BiasEnabled)
     {
         ARMNN_ASSERT(biases.has_value());
-
+        // Same for bias as weights. We don't currently support non const.
+        if (!biases.value().IsConstant())
+        {
+            return arm_compute::Status{arm_compute::ErrorCode::RUNTIME_ERROR,
+                                       "ArmNN ClConvolution2dWorkload does not support non constant bias."};
+        }
         aclBiasesInfo = BuildArmComputeTensorInfo(biases.value(), descriptor.m_DataLayout);
         optionalAclBiasesInfo = &aclBiasesInfo;
     }
@@ -72,6 +86,7 @@
 {
     ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "ClConvolution2dWorkload");
     const TensorInfo& weightInfo = m_Data.m_Weight->GetTensorInfo();
+    m_Data.ValidateInputsOutputs("ClConvolution2dWorkload", 1, 1);
 
     m_KernelTensor = std::make_unique<arm_compute::CLTensor>();
     BuildArmComputeTensor(*m_KernelTensor, weightInfo, m_Data.m_Parameters.m_DataLayout);
@@ -85,8 +100,6 @@
         BuildArmComputeTensor(*m_BiasTensor, m_Data.m_Bias->GetTensorInfo(), m_Data.m_Parameters.m_DataLayout);
     }
 
-    m_Data.ValidateInputsOutputs("ClConvolution2dWorkload", 1, 1);
-
     arm_compute::ICLTensor& input  = static_cast<IClTensorHandle*>(m_Data.m_Inputs[0])->GetTensor();
     arm_compute::ICLTensor& output = static_cast<IClTensorHandle*>(m_Data.m_Outputs[0])->GetTensor();
 
diff --git a/src/backends/neon/NeonBackend.cpp b/src/backends/neon/NeonBackend.cpp
index 39ad4b9..2433642 100644
--- a/src/backends/neon/NeonBackend.cpp
+++ b/src/backends/neon/NeonBackend.cpp
@@ -193,14 +193,14 @@
 
                                 if (baseLayer->GetParameters().m_BiasEnabled)
                                 {
-                                    biases = baseLayer->m_Bias->GetTensorInfo();
+                                    biases = baseLayer->GetInputSlot(2).GetConnectedOutputSlot()->GetTensorInfo();
                                 }
 
                                 arm_compute::Status status = NeonConvolution2dWorkloadValidate(
                                         baseLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo(),
                                         activationLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo(),
                                         baseLayer->GetParameters(),
-                                        baseLayer->m_Weight->GetTensorInfo(),
+                                        baseLayer->GetInputSlot(1).GetConnectedOutputSlot()->GetTensorInfo(),
                                         biases,
                                         false,
                                         &activationDesc);
diff --git a/src/backends/neon/workloads/NeonConvolution2dWorkload.cpp b/src/backends/neon/workloads/NeonConvolution2dWorkload.cpp
index fce57e6..d5716c8 100644
--- a/src/backends/neon/workloads/NeonConvolution2dWorkload.cpp
+++ b/src/backends/neon/workloads/NeonConvolution2dWorkload.cpp
@@ -29,6 +29,15 @@
                                                       bool isFastMathEnabled,
                                                       const ActivationDescriptor* activationDescriptor)
 {
+    // The implemented workload does support both const and non const
+    // weights. However, in the case of non const weights we'd have to call
+    // prepare or configure for each inference which we're not setup to do just yet.
+    if (!weights.IsConstant())
+    {
+        return arm_compute::Status{arm_compute::ErrorCode::RUNTIME_ERROR,
+                                   "ArmNN NeonConvolution2dWorkload does not support non constant weights."};
+    }
+
     const arm_compute::TensorInfo aclInputInfo = BuildArmComputeTensorInfo(input, descriptor.m_DataLayout);
     const arm_compute::TensorInfo aclOutputInfo = BuildArmComputeTensorInfo(output, descriptor.m_DataLayout);
     const arm_compute::TensorInfo aclWeightsInfo = BuildArmComputeTensorInfo(weights, descriptor.m_DataLayout);
@@ -42,7 +51,12 @@
     if (descriptor.m_BiasEnabled)
     {
         ARMNN_ASSERT(biases.has_value());
-
+        // Same for bias as weights. We don't currently support non const.
+        if (!biases.value().IsConstant())
+        {
+            return arm_compute::Status{arm_compute::ErrorCode::RUNTIME_ERROR,
+                                       "ArmNN NeonConvolution2dWorkload does not support non constant bias."};
+        }
         aclBiasesInfo = BuildArmComputeTensorInfo(biases.value(), descriptor.m_DataLayout);
         optionalAclBiasesInfo = &aclBiasesInfo;
     }
diff --git a/src/backends/reference/RefLayerSupport.cpp b/src/backends/reference/RefLayerSupport.cpp
index f921383..8051dcf 100644
--- a/src/backends/reference/RefLayerSupport.cpp
+++ b/src/backends/reference/RefLayerSupport.cpp
@@ -1246,11 +1246,12 @@
 {
    bool supported = true;
 
-    std::array<DataType,4> supportedInputTypes = {
+    std::array<DataType,5> supportedInputTypes = {
         DataType::QAsymmS8,
         DataType::QAsymmU8,
         DataType::QSymmS8,
-        DataType::QSymmS16
+        DataType::QSymmS16,
+        DataType::Float16
     };
 
     supported &= CheckSupportRule(TypeAnyOf(input, supportedInputTypes), reasonIfUnsupported,
diff --git a/src/backends/reference/workloads/RefConvolution2dWorkload.cpp b/src/backends/reference/workloads/RefConvolution2dWorkload.cpp
index d57040e..fe97cb1 100644
--- a/src/backends/reference/workloads/RefConvolution2dWorkload.cpp
+++ b/src/backends/reference/workloads/RefConvolution2dWorkload.cpp
@@ -12,37 +12,46 @@
 
 namespace armnn
 {
-RefConvolution2dWorkload::RefConvolution2dWorkload(
-    const Convolution2dQueueDescriptor& descriptor, const WorkloadInfo& info)
+RefConvolution2dWorkload::RefConvolution2dWorkload(const Convolution2dQueueDescriptor& descriptor,
+                                                   const WorkloadInfo& info)
     : RefBaseWorkload<Convolution2dQueueDescriptor>(descriptor, info)
 {
     WorkloadInfo detailsInfo;
     detailsInfo.m_InputTensorInfos = info.m_InputTensorInfos;
     detailsInfo.m_OutputTensorInfos = info.m_OutputTensorInfos;
-    detailsInfo.m_WeightsTensorInfo = armnn::Optional<armnn::TensorInfo>(descriptor.m_Weight->GetTensorInfo());
-    if (descriptor.m_Parameters.m_BiasEnabled)
-    {
-        detailsInfo.m_BiasTensorInfo = armnn::Optional<armnn::TensorInfo>(descriptor.m_Bias->GetTensorInfo());
-    }
 
     // Report Profiling Details
     ARMNN_REPORT_PROFILING_WORKLOAD_DESC("RefConvolution2dWorkload_Construct",
                                          descriptor.m_Parameters,
                                          detailsInfo,
                                          this->GetGuid());
+}
 
-    m_Weight = std::make_unique<ScopedTensorHandle>(*( descriptor.m_Weight ));
-    const TensorInfo& rFilterInfo = m_Weight->GetTensorInfo();
+void RefConvolution2dWorkload::PostAllocationConfigure()
+{
+    PostAllocationConfigure(m_Data.m_Inputs, m_Data.m_Outputs);
+}
 
+void RefConvolution2dWorkload::PostAllocationConfigure(std::vector<ITensorHandle*> inputs,
+                                                        std::vector<ITensorHandle*> outputs)
+{
+    const TensorInfo& inputInfo = GetTensorInfo(inputs[0]);
+    ARMNN_ASSERT(inputInfo.GetNumDimensions() > 1);
+    m_InputShape = inputInfo.GetShape();
+
+    const TensorInfo& rFilterInfo = GetTensorInfo(inputs[1]);
+    ARMNN_ASSERT(inputInfo.GetNumDimensions() > 1);
     m_FilterShape = rFilterInfo.GetShape();
-    m_FilterDecoder = MakeDecoder<float>(rFilterInfo, m_Weight.get()->Map(true));
+    m_FilterDecoder = MakeDecoder<float>(rFilterInfo);
 
-    if ( descriptor.m_Parameters.m_BiasEnabled )
+    if (m_Data.m_Parameters.m_BiasEnabled)
     {
-        m_Bias = std::make_unique<ScopedTensorHandle>(*( descriptor.m_Bias ));
-        const TensorInfo& biasInfo = m_Bias->GetTensorInfo();
-        m_BiasDecoder = MakeDecoder<float>(biasInfo, m_Bias->Map(true));
+        const TensorInfo& biasInfo = GetTensorInfo(inputs[2]);
+        m_BiasDecoder = MakeDecoder<float>(biasInfo);
     }
+
+    const TensorInfo& outputInfo = GetTensorInfo(outputs[0]);
+    m_OutputShape = outputInfo.GetShape();
 }
 
 void RefConvolution2dWorkload::Execute() const
@@ -52,6 +61,8 @@
 
 void RefConvolution2dWorkload::ExecuteAsync(WorkingMemDescriptor& workingMemDescriptor)
 {
+    PostAllocationConfigure(workingMemDescriptor.m_Inputs, workingMemDescriptor.m_Outputs);
+
     Execute(workingMemDescriptor.m_Inputs, workingMemDescriptor.m_Outputs);
 }
 
@@ -62,14 +73,17 @@
     std::unique_ptr<Decoder<float>> inputDecoder = MakeDecoder<float>(GetTensorInfo(inputs[0]), inputs[0]->Map());
     std::unique_ptr<Encoder<float>> outputEncoder = MakeEncoder<float>(GetTensorInfo(outputs[0]), outputs[0]->Map());
 
-    const TensorShape& inputShape = GetTensorInfo(inputs[0]).GetShape();
-    const TensorShape& outputShape = GetTensorInfo(outputs[0]).GetShape();
+    m_FilterDecoder->Reset(inputs[1]->Map());
+    if (m_Data.m_Parameters.m_BiasEnabled)
+    {
+        m_BiasDecoder->Reset(inputs[2]->Map());
+    }
 
-    Convolve(inputShape, *inputDecoder, outputShape, *outputEncoder, m_FilterShape,
+    Convolve(m_InputShape, *inputDecoder, m_OutputShape, *outputEncoder, m_FilterShape,
              *m_FilterDecoder, m_Data.m_Parameters.m_BiasEnabled, m_BiasDecoder.get(),
              m_Data.m_Parameters.m_DataLayout, m_Data.m_Parameters.m_PadTop, m_Data.m_Parameters.m_PadLeft,
              m_Data.m_Parameters.m_StrideX, m_Data.m_Parameters.m_StrideY,
              m_Data.m_Parameters.m_DilationX, m_Data.m_Parameters.m_DilationY);
 }
 
-} //namespace armnn
+} //namespace armnn
\ No newline at end of file
diff --git a/src/backends/reference/workloads/RefConvolution2dWorkload.hpp b/src/backends/reference/workloads/RefConvolution2dWorkload.hpp
index 3335782..1cb30b6 100644
--- a/src/backends/reference/workloads/RefConvolution2dWorkload.hpp
+++ b/src/backends/reference/workloads/RefConvolution2dWorkload.hpp
@@ -19,20 +19,21 @@
     explicit RefConvolution2dWorkload(const Convolution2dQueueDescriptor& descriptor,
                                       const WorkloadInfo& info);
 
+    void PostAllocationConfigure() override;
 
     void Execute() const override;
     void ExecuteAsync(WorkingMemDescriptor& workingMemDescriptor)  override;
 
 private:
+    void PostAllocationConfigure(std::vector<ITensorHandle*> inputs, std::vector<ITensorHandle*> outputs);
     void Execute(std::vector<ITensorHandle*> inputs, std::vector<ITensorHandle*> outputs) const;
-    std::unique_ptr<ScopedTensorHandle> m_Weight;
-    std::unique_ptr<ScopedTensorHandle> m_Bias;
 
     std::unique_ptr<Decoder<float>> m_FilterDecoder;
     std::unique_ptr<Decoder<float>> m_BiasDecoder;
 
+    TensorShape m_InputShape;
+    TensorShape m_OutputShape;
     TensorShape m_FilterShape;
 };
 
-} //namespace armnn
-
+} //namespace armnn
\ No newline at end of file
diff --git a/src/backends/reference/workloads/RefDepthwiseConvolution2dWorkload.hpp b/src/backends/reference/workloads/RefDepthwiseConvolution2dWorkload.hpp
index ef0b16d..30ee6d8 100644
--- a/src/backends/reference/workloads/RefDepthwiseConvolution2dWorkload.hpp
+++ b/src/backends/reference/workloads/RefDepthwiseConvolution2dWorkload.hpp
@@ -22,13 +22,6 @@
 private:
     void Execute(std::vector<ITensorHandle*> inputs, std::vector<ITensorHandle*> outputs) const;
 
-    std::unique_ptr <ScopedTensorHandle> m_Weight;
-    std::unique_ptr <ScopedTensorHandle> m_Bias;
-
-    std::unique_ptr <Decoder<float>> m_FilterDecoder;
-    std::unique_ptr <Decoder<float>> m_BiasDecoder;
-
-    TensorShape m_FilterShape;
 };
 
 } //namespace armnn